From e78e4177fad5494f62fa5ff3f60f61d6383e101b Mon Sep 17 00:00:00 2001 From: Jacinta Ferrant Date: Wed, 3 Dec 2025 10:31:16 -0800 Subject: [PATCH 1/3] Update functions that should return CheckErrorKind instead of VmExecutioError Signed-off-by: Jacinta Ferrant --- clarity-types/src/types/mod.rs | 34 ++++++++++----------- clarity/src/vm/functions/assets.rs | 4 +-- clarity/src/vm/functions/boolean.rs | 5 ++- clarity/src/vm/functions/conversions.rs | 20 ++++++------ clarity/src/vm/functions/crypto.rs | 3 +- clarity/src/vm/functions/database.rs | 18 ++++++----- clarity/src/vm/functions/define.rs | 4 +-- clarity/src/vm/functions/options.rs | 22 ++++++------- clarity/src/vm/functions/post_conditions.rs | 8 ++--- clarity/src/vm/functions/sequences.rs | 14 +++++---- clarity/src/vm/functions/tuples.rs | 4 +-- 11 files changed, 70 insertions(+), 66 deletions(-) diff --git a/clarity-types/src/types/mod.rs b/clarity-types/src/types/mod.rs index 78b7567563..74555dcc35 100644 --- a/clarity-types/src/types/mod.rs +++ b/clarity-types/src/types/mod.rs @@ -452,7 +452,7 @@ impl SequenceData { _ => return Err(CheckErrorKind::ListTypesMustMatch.into()), }; - Value::some(Value::Sequence(new_seq_data)) + Ok(Value::some(Value::Sequence(new_seq_data))?) } pub fn contains(&self, to_find: Value) -> Result, VmExecutionError> { @@ -867,7 +867,7 @@ impl PartialEq for TupleData { pub const NONE: Value = Value::Optional(OptionalData { data: None }); impl Value { - pub fn some(data: Value) -> Result { + pub fn some(data: Value) -> Result { if data.size()? + WRAPPER_VALUE_SIZE > MAX_VALUE_SIZE { Err(CheckErrorKind::ValueTooLarge.into()) } else if data.depth()? + 1 > MAX_TYPE_DEPTH { @@ -904,7 +904,7 @@ impl Value { }) } - pub fn okay(data: Value) -> Result { + pub fn okay(data: Value) -> Result { if data.size()? + WRAPPER_VALUE_SIZE > MAX_VALUE_SIZE { Err(CheckErrorKind::ValueTooLarge.into()) } else if data.depth()? + 1 > MAX_TYPE_DEPTH { @@ -917,7 +917,7 @@ impl Value { } } - pub fn error(data: Value) -> Result { + pub fn error(data: Value) -> Result { if data.size()? + WRAPPER_VALUE_SIZE > MAX_VALUE_SIZE { Err(CheckErrorKind::ValueTooLarge.into()) } else if data.depth()? + 1 > MAX_TYPE_DEPTH { @@ -930,11 +930,11 @@ impl Value { } } - pub fn size(&self) -> Result { + pub fn size(&self) -> Result { Ok(TypeSignature::type_of(self)?.size()?) } - pub fn depth(&self) -> Result { + pub fn depth(&self) -> Result { Ok(TypeSignature::type_of(self)?.depth()) } @@ -985,7 +985,7 @@ impl Value { pub fn cons_list( list_data: Vec, epoch: &StacksEpochId, - ) -> Result { + ) -> Result { // Constructors for TypeSignature ensure that the size of the Value cannot // be greater than MAX_VALUE_SIZE (they error on such constructions) // Aaron: at this point, we've _already_ allocated memory for this type. @@ -1009,7 +1009,7 @@ impl Value { /// # Errors /// - CheckErrorKind::ValueTooLarge if `buff_data` is too large. - pub fn buff_from(buff_data: Vec) -> Result { + pub fn buff_from(buff_data: Vec) -> Result { // check the buffer size BufferLength::try_from(buff_data.len())?; // construct the buffer @@ -1022,13 +1022,13 @@ impl Value { Value::Sequence(SequenceData::Buffer(BuffData { data: vec![byte] })) } - pub fn string_ascii_from_bytes(bytes: Vec) -> Result { + pub fn string_ascii_from_bytes(bytes: Vec) -> Result { // check the string size BufferLength::try_from(bytes.len())?; for b in bytes.iter() { if !b.is_ascii_alphanumeric() && !b.is_ascii_punctuation() && !b.is_ascii_whitespace() { - return Err(CheckErrorKind::InvalidCharactersDetected.into()); + return Err(CheckErrorKind::InvalidCharactersDetected); } } // construct the string @@ -1078,10 +1078,10 @@ impl Value { )))) } - pub fn string_utf8_from_bytes(bytes: Vec) -> Result { + pub fn string_utf8_from_bytes(bytes: Vec) -> Result { let validated_utf8_str = match str::from_utf8(&bytes) { Ok(string) => string, - _ => return Err(CheckErrorKind::InvalidCharactersDetected.into()), + _ => return Err(CheckErrorKind::InvalidCharactersDetected), }; let data = validated_utf8_str .chars() @@ -1588,7 +1588,7 @@ impl TupleData { // TODO: add tests from mutation testing results #4833 #[cfg_attr(test, mutants::skip)] - pub fn from_data(data: Vec<(ClarityName, Value)>) -> Result { + pub fn from_data(data: Vec<(ClarityName, Value)>) -> Result { let mut type_map = BTreeMap::new(); let mut data_map = BTreeMap::new(); for (name, value) in data.into_iter() { @@ -1626,15 +1626,15 @@ impl TupleData { Ok(Self::new(expected.clone(), data_map)) } - pub fn get(&self, name: &str) -> Result<&Value, VmExecutionError> { + pub fn get(&self, name: &str) -> Result<&Value, CheckErrorKind> { self.data_map.get(name).ok_or_else(|| { - CheckErrorKind::NoSuchTupleField(name.to_string(), self.type_signature.clone()).into() + CheckErrorKind::NoSuchTupleField(name.to_string(), self.type_signature.clone()) }) } - pub fn get_owned(mut self, name: &str) -> Result { + pub fn get_owned(mut self, name: &str) -> Result { self.data_map.remove(name).ok_or_else(|| { - CheckErrorKind::NoSuchTupleField(name.to_string(), self.type_signature.clone()).into() + CheckErrorKind::NoSuchTupleField(name.to_string(), self.type_signature.clone()) }) } diff --git a/clarity/src/vm/functions/assets.rs b/clarity/src/vm/functions/assets.rs index debbee8ec5..8a1624f7c9 100644 --- a/clarity/src/vm/functions/assets.rs +++ b/clarity/src/vm/functions/assets.rs @@ -240,7 +240,7 @@ pub fn special_stx_account( let v2_unlock_ht = env.global_context.database.get_v2_unlock_height()?; let v3_unlock_ht = env.global_context.database.get_v3_unlock_height()?; - TupleData::from_data(vec![ + Ok(TupleData::from_data(vec![ ( "unlocked" .try_into() @@ -264,7 +264,7 @@ pub fn special_stx_account( ))), ), ]) - .map(Value::Tuple) + .map(Value::Tuple)?) } pub fn special_stx_burn( diff --git a/clarity/src/vm/functions/boolean.rs b/clarity/src/vm/functions/boolean.rs index afcd1b88cd..098d0ff575 100644 --- a/clarity/src/vm/functions/boolean.rs +++ b/clarity/src/vm/functions/boolean.rs @@ -22,14 +22,13 @@ use crate::vm::eval; use crate::vm::representations::SymbolicExpression; use crate::vm::types::{TypeSignature, Value}; -fn type_force_bool(value: &Value) -> Result { +fn type_force_bool(value: &Value) -> Result { match *value { Value::Bool(boolean) => Ok(boolean), _ => Err(CheckErrorKind::TypeValueError( Box::new(TypeSignature::BoolType), Box::new(value.clone()), - ) - .into()), + )), } } diff --git a/clarity/src/vm/functions/conversions.rs b/clarity/src/vm/functions/conversions.rs index 4b5438717e..5a2ec4b4ed 100644 --- a/clarity/src/vm/functions/conversions.rs +++ b/clarity/src/vm/functions/conversions.rs @@ -130,19 +130,19 @@ pub fn native_buff_to_uint_be(value: Value) -> Result { // either a Int or UInt, depending on the desired result. pub fn native_string_to_int_generic( value: Value, - string_to_value_fn: fn(String) -> Result, + string_to_value_fn: fn(String) -> Result, ) -> Result { match value { Value::Sequence(SequenceData::String(CharType::ASCII(ASCIIData { data }))) => { match String::from_utf8(data) { - Ok(as_string) => string_to_value_fn(as_string), + Ok(as_string) => Ok(string_to_value_fn(as_string)?), Err(_error) => Ok(Value::none()), } } Value::Sequence(SequenceData::String(CharType::UTF8(UTF8Data { data }))) => { let flattened_bytes = data.into_iter().flatten().collect(); match String::from_utf8(flattened_bytes) { - Ok(as_string) => string_to_value_fn(as_string), + Ok(as_string) => Ok(string_to_value_fn(as_string)?), Err(_error) => Ok(Value::none()), } } @@ -157,7 +157,7 @@ pub fn native_string_to_int_generic( } } -fn safe_convert_string_to_int(raw_string: String) -> Result { +fn safe_convert_string_to_int(raw_string: String) -> Result { let possible_int = raw_string.parse::(); match possible_int { Ok(val) => Value::some(Value::Int(val)), @@ -169,7 +169,7 @@ pub fn native_string_to_int(value: Value) -> Result { native_string_to_int_generic(value, safe_convert_string_to_int) } -fn safe_convert_string_to_uint(raw_string: String) -> Result { +fn safe_convert_string_to_uint(raw_string: String) -> Result { let possible_int = raw_string.parse::(); match possible_int { Ok(val) => Value::some(Value::UInt(val)), @@ -187,7 +187,7 @@ pub fn native_string_to_uint(value: Value) -> Result { // either an ASCII or UTF8 string, depending on the desired result. pub fn native_int_to_string_generic( value: Value, - bytes_to_value_fn: fn(bytes: Vec) -> Result, + bytes_to_value_fn: fn(bytes: Vec) -> Result, ) -> Result { match value { Value::Int(ref int_value) => { @@ -226,13 +226,13 @@ fn convert_string_to_ascii_ok(s: String) -> Result { let ascii_value = Value::string_ascii_from_bytes(s.into_bytes()).map_err(|_| { VmInternalError::Expect("Unexpected error converting string to ASCII".into()) })?; - Value::okay(ascii_value) + Ok(Value::okay(ascii_value)?) } /// Helper function for UTF8 conversion that can return err u1 for non-ASCII characters fn convert_utf8_to_ascii(s: String) -> Result { match Value::string_ascii_from_bytes(s.into_bytes()) { - Ok(ascii_value) => Value::okay(ascii_value), + Ok(ascii_value) => Ok(Value::okay(ascii_value)?), Err(_) => Ok(Value::err_uint(1)), // Non-ASCII characters in UTF8 } } @@ -264,7 +264,7 @@ pub fn special_to_ascii( // Convert UTF8 to string first, then to ASCII let flattened_bytes: Vec = data.into_iter().flatten().collect(); match String::from_utf8(flattened_bytes) { - Ok(utf8_string) => convert_utf8_to_ascii(utf8_string), + Ok(utf8_string) => Ok(convert_utf8_to_ascii(utf8_string)?), Err(_) => Ok(Value::err_uint(1)), // Invalid UTF8 } } @@ -351,5 +351,5 @@ pub fn from_consensus_buff( return Ok(Value::none()); } - Value::some(result) + Ok(Value::some(result)?) } diff --git a/clarity/src/vm/functions/crypto.rs b/clarity/src/vm/functions/crypto.rs index d3a6384e82..88e4c8eff7 100644 --- a/clarity/src/vm/functions/crypto.rs +++ b/clarity/src/vm/functions/crypto.rs @@ -46,7 +46,8 @@ macro_rules! native_hash_func { )), }?; let hash = <$module>::from_data(&bytes); - Value::buff_from(hash.as_bytes().to_vec()) + let value = Value::buff_from(hash.as_bytes().to_vec())?; + Ok(value) } }; } diff --git a/clarity/src/vm/functions/database.rs b/clarity/src/vm/functions/database.rs index e32624ef82..f17c577c5a 100644 --- a/clarity/src/vm/functions/database.rs +++ b/clarity/src/vm/functions/database.rs @@ -882,7 +882,7 @@ pub fn special_get_block_info( } }; - Value::some(result) + Ok(Value::some(result)?) } /// Handles the `get-burn-block-info?` special function. @@ -941,11 +941,11 @@ pub fn special_get_burn_block_info( .get_burnchain_block_header_hash_for_burnchain_height(height_value)?; match burnchain_header_hash_opt { - Some(burnchain_header_hash) => { - Value::some(Value::Sequence(SequenceData::Buffer(BuffData { + Some(burnchain_header_hash) => Ok(Value::some(Value::Sequence( + SequenceData::Buffer(BuffData { data: burnchain_header_hash.as_bytes().to_vec(), - }))) - } + }), + ))?), None => Ok(Value::none()), } } @@ -1060,7 +1060,7 @@ pub fn special_get_stacks_block_info( } }; - Value::some(result) + Ok(Value::some(result)?) } /// Handles the function `get-tenure-info?` special function. @@ -1173,7 +1173,7 @@ pub fn special_get_tenure_info( } }; - Value::some(result) + Ok(Value::some(result)?) } /// Handles the function `contract-hash?` @@ -1212,5 +1212,7 @@ pub fn special_contract_hash( return Ok(Value::err_uint(2)); }; - Value::okay(Value::buff_from(contract_hash.as_bytes().to_vec())?) + Ok(Value::okay(Value::buff_from( + contract_hash.as_bytes().to_vec(), + )?)?) } diff --git a/clarity/src/vm/functions/define.rs b/clarity/src/vm/functions/define.rs index b692d58421..5bb97ae983 100644 --- a/clarity/src/vm/functions/define.rs +++ b/clarity/src/vm/functions/define.rs @@ -111,9 +111,9 @@ pub enum DefineResult { fn check_legal_define( name: &str, contract_context: &ContractContext, -) -> Result<(), VmExecutionError> { +) -> Result<(), CheckErrorKind> { if contract_context.is_name_used(name) { - Err(CheckErrorKind::NameAlreadyUsed(name.to_string()).into()) + Err(CheckErrorKind::NameAlreadyUsed(name.to_string())) } else { Ok(()) } diff --git a/clarity/src/vm/functions/options.rs b/clarity/src/vm/functions/options.rs index d8fe8959b0..83722667bd 100644 --- a/clarity/src/vm/functions/options.rs +++ b/clarity/src/vm/functions/options.rs @@ -225,45 +225,45 @@ pub fn special_match( } pub fn native_some(input: Value) -> Result { - Value::some(input) + Ok(Value::some(input)?) } -fn is_some(input: Value) -> Result { +fn is_some(input: Value) -> Result { match input { Value::Optional(ref data) => Ok(data.data.is_some()), - _ => Err(CheckErrorKind::ExpectedOptionalValue(Box::new(input)).into()), + _ => Err(CheckErrorKind::ExpectedOptionalValue(Box::new(input))), } } -fn is_okay(input: Value) -> Result { +fn is_okay(input: Value) -> Result { match input { Value::Response(data) => Ok(data.committed), - _ => Err(CheckErrorKind::ExpectedResponseValue(Box::new(input)).into()), + _ => Err(CheckErrorKind::ExpectedResponseValue(Box::new(input))), } } pub fn native_is_some(input: Value) -> Result { - is_some(input).map(Value::Bool) + Ok(is_some(input).map(Value::Bool)?) } pub fn native_is_none(input: Value) -> Result { - is_some(input).map(|is_some| Value::Bool(!is_some)) + Ok(is_some(input).map(|is_some| Value::Bool(!is_some))?) } pub fn native_is_okay(input: Value) -> Result { - is_okay(input).map(Value::Bool) + Ok(is_okay(input).map(Value::Bool)?) } pub fn native_is_err(input: Value) -> Result { - is_okay(input).map(|is_ok| Value::Bool(!is_ok)) + Ok(is_okay(input).map(|is_ok| Value::Bool(!is_ok))?) } pub fn native_okay(input: Value) -> Result { - Value::okay(input) + Ok(Value::okay(input)?) } pub fn native_error(input: Value) -> Result { - Value::error(input) + Ok(Value::error(input)?) } pub fn native_default_to(default: Value, input: Value) -> Result { diff --git a/clarity/src/vm/functions/post_conditions.rs b/clarity/src/vm/functions/post_conditions.rs index d50638f818..94ea734548 100644 --- a/clarity/src/vm/functions/post_conditions.rs +++ b/clarity/src/vm/functions/post_conditions.rs @@ -258,7 +258,7 @@ pub fn special_restrict_assets( Ok(None) => {} Ok(Some(violation_index)) => { env.global_context.roll_back()?; - return Value::error(Value::UInt(violation_index)); + return Ok(Value::error(Value::UInt(violation_index))?); } Err(e) => { env.global_context.roll_back()?; @@ -272,7 +272,7 @@ pub fn special_restrict_assets( match eval_result { Ok(Some(last)) => { // body completed successfully — commit and return ok(last) - Value::okay(last) + Ok(Value::okay(last)?) } Ok(None) => { // Body had no expressions (shouldn't happen due to argument checks) @@ -352,7 +352,7 @@ pub fn special_as_contract( Ok(None) => {} Ok(Some(violation_index)) => { nested_env.global_context.roll_back()?; - return Value::error(Value::UInt(violation_index)); + return Ok(Value::error(Value::UInt(violation_index))?); } Err(e) => { nested_env.global_context.roll_back()?; @@ -366,7 +366,7 @@ pub fn special_as_contract( match eval_result { Ok(Some(last)) => { // body completed successfully — commit and return ok(last) - Value::okay(last) + Ok(Value::okay(last)?) } Ok(None) => { // Body had no expressions (shouldn't happen due to argument checks) diff --git a/clarity/src/vm/functions/sequences.rs b/clarity/src/vm/functions/sequences.rs index a30ce53a41..f9e044e463 100644 --- a/clarity/src/vm/functions/sequences.rs +++ b/clarity/src/vm/functions/sequences.rs @@ -45,7 +45,8 @@ pub fn list_cons( runtime_cost(ClarityCostFunction::ListCons, env, arg_size)?; - Value::cons_list(args, env.epoch()) + let value = Value::cons_list(args, env.epoch())?; + Ok(value) } pub fn special_filter( @@ -178,7 +179,8 @@ pub fn special_map( mapped_results.push(res); } - Value::cons_list(mapped_results, env.epoch()) + let value = Value::cons_list(mapped_results, env.epoch())?; + Ok(value) } pub fn special_append( @@ -205,7 +207,7 @@ pub fn special_append( )?; if entry_type.is_no_type() { assert_eq!(size, 0); - return Value::cons_list(vec![element], env.epoch()); + return Ok(Value::cons_list(vec![element], env.epoch())?); } if let Ok(next_entry_type) = TypeSignature::least_supertype(env.epoch(), &entry_type, &element_type) @@ -335,7 +337,7 @@ pub fn native_len(sequence: Value) -> Result { pub fn native_index_of(sequence: Value, to_find: Value) -> Result { if let Value::Sequence(sequence_data) = sequence { match sequence_data.contains(to_find)? { - Some(index) => Value::some(Value::UInt(index as u128)), + Some(index) => Ok(Value::some(Value::UInt(index as u128))?), None => Ok(Value::none()), } } else { @@ -367,7 +369,7 @@ pub fn native_element_at(sequence: Value, index: Value) -> Result Err(RuntimeError::BadTypeConstruction.into()), } diff --git a/clarity/src/vm/functions/tuples.rs b/clarity/src/vm/functions/tuples.rs index 87b8eb7281..4a177424fc 100644 --- a/clarity/src/vm/functions/tuples.rs +++ b/clarity/src/vm/functions/tuples.rs @@ -37,7 +37,7 @@ pub fn tuple_cons( let bindings = parse_eval_bindings(args, SyntaxBindingErrorType::TupleCons, env, context)?; runtime_cost(ClarityCostFunction::TupleCons, env, bindings.len())?; - TupleData::from_data(bindings).map(Value::from) + Ok(TupleData::from_data(bindings).map(Value::from)?) } pub fn tuple_get( @@ -76,7 +76,7 @@ pub fn tuple_get( } Value::Tuple(tuple_data) => { runtime_cost(ClarityCostFunction::TupleGet, env, tuple_data.len())?; - tuple_data.get_owned(arg_name) + Ok(tuple_data.get_owned(arg_name)?) } _ => Err(CheckErrorKind::ExpectedTuple(Box::new(TypeSignature::type_of(&value)?)).into()), } From b8ca7dd01e2fccd3357e4f4f24fd8bece0df82ce Mon Sep 17 00:00:00 2001 From: Jacinta Ferrant Date: Wed, 10 Dec 2025 09:52:44 -0800 Subject: [PATCH 2/3] First pass: add ClarityTypeError and cleanup RuntimeError a bit Signed-off-by: Jacinta Ferrant --- Cargo.lock | 1 + clarity-types/src/errors/analysis.rs | 218 +++++- clarity-types/src/errors/mod.rs | 5 - clarity-types/src/lib.rs | 2 +- clarity-types/src/representations.rs | 13 +- clarity-types/src/tests/representations.rs | 10 +- clarity-types/src/tests/types/mod.rs | 372 +++++----- .../src/tests/types/serialization.rs | 35 +- clarity-types/src/tests/types/signatures.rs | 28 +- clarity-types/src/types/mod.rs | 661 ++++++++++++------ clarity-types/src/types/serialization.rs | 94 ++- clarity-types/src/types/signatures.rs | 199 +++--- clarity/fuzz/fuzz_targets/fuzz_sanitize.rs | 7 +- clarity/src/vm/analysis/analysis_db.rs | 15 +- .../contract_interface_builder/mod.rs | 7 +- clarity/src/vm/analysis/mod.rs | 22 +- clarity/src/vm/analysis/type_checker/mod.rs | 14 +- .../src/vm/analysis/type_checker/v2_05/mod.rs | 91 ++- .../type_checker/v2_05/natives/assets.rs | 16 +- .../type_checker/v2_05/natives/maps.rs | 43 +- .../type_checker/v2_05/natives/mod.rs | 66 +- .../type_checker/v2_05/natives/options.rs | 26 +- .../type_checker/v2_05/natives/sequences.rs | 60 +- .../src/vm/analysis/type_checker/v2_1/mod.rs | 213 ++++-- .../type_checker/v2_1/natives/assets.rs | 18 +- .../type_checker/v2_1/natives/conversions.rs | 19 +- .../type_checker/v2_1/natives/maps.rs | 43 +- .../analysis/type_checker/v2_1/natives/mod.rs | 127 ++-- .../type_checker/v2_1/natives/options.rs | 32 +- .../v2_1/natives/post_conditions.rs | 13 +- .../type_checker/v2_1/natives/sequences.rs | 71 +- clarity/src/vm/analysis/types.rs | 6 +- clarity/src/vm/callables.rs | 24 +- clarity/src/vm/clarity.rs | 12 +- clarity/src/vm/contexts.rs | 11 +- clarity/src/vm/database/clarity_db.rs | 122 +++- clarity/src/vm/database/key_value_wrapper.rs | 6 +- clarity/src/vm/errors.rs | 1 + clarity/src/vm/functions/arithmetic.rs | 38 +- clarity/src/vm/functions/assets.rs | 205 +++++- clarity/src/vm/functions/conversions.rs | 34 +- clarity/src/vm/functions/crypto.rs | 3 +- clarity/src/vm/functions/database.rs | 139 +++- clarity/src/vm/functions/mod.rs | 16 +- clarity/src/vm/functions/options.rs | 11 +- clarity/src/vm/functions/post_conditions.rs | 51 +- clarity/src/vm/functions/principals.rs | 3 +- clarity/src/vm/functions/sequences.rs | 240 +++++-- clarity/src/vm/functions/tuples.rs | 33 +- clarity/src/vm/mod.rs | 45 +- clarity/src/vm/tests/datamaps.rs | 8 +- clarity/src/vm/tests/representations.rs | 14 +- clarity/src/vm/tests/sequences.rs | 58 +- clarity/src/vm/types/mod.rs | 8 +- clarity/src/vm/types/signatures.rs | 68 +- contrib/stacks-cli/Cargo.toml | 1 + contrib/stacks-cli/src/main.rs | 50 +- stacks-common/src/util/macros.rs | 8 +- .../src/tests/signer/commands/block_wait.rs | 1 - stacks-signer/src/client/mod.rs | 8 +- .../chainstate/nakamoto/coordinator/mod.rs | 87 +-- .../src/chainstate/nakamoto/signer_set.rs | 194 +++-- stackslib/src/chainstate/stacks/boot/mod.rs | 15 +- stackslib/src/chainstate/stacks/db/blocks.rs | 82 +-- .../src/chainstate/stacks/db/transactions.rs | 8 +- stackslib/src/chainstate/stacks/mod.rs | 18 +- .../tests/runtime_analysis_tests.rs | 3 +- .../src/chainstate/tests/runtime_tests.rs | 10 - .../chainstate/tests/static_analysis_tests.rs | 3 +- stackslib/src/net/api/getpoxinfo.rs | 54 +- stackslib/src/net/mod.rs | 13 +- stackslib/src/util_lib/strings.rs | 7 +- 72 files changed, 2806 insertions(+), 1453 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 482b228034..67a0ce3bf2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3326,6 +3326,7 @@ dependencies = [ "stacks-common 0.0.1", "stackslib 0.0.1", "tempfile", + "thiserror", ] [[package]] diff --git a/clarity-types/src/errors/analysis.rs b/clarity-types/src/errors/analysis.rs index 29bdb3bcb2..8faa7e159b 100644 --- a/clarity-types/src/errors/analysis.rs +++ b/clarity-types/src/errors/analysis.rs @@ -19,7 +19,7 @@ use crate::diagnostic::{DiagnosableError, Diagnostic}; use crate::errors::CostErrors; use crate::execution_cost::ExecutionCost; use crate::representations::SymbolicExpression; -use crate::types::{TraitIdentifier, TupleTypeSignature, TypeSignature, Value}; +use crate::types::{ClarityTypeError, TraitIdentifier, TupleTypeSignature, TypeSignature, Value}; /// What kind of syntax binding was found to be in error? #[derive(Debug, PartialEq, Clone, Copy)] @@ -200,7 +200,11 @@ pub enum CommonCheckErrorKind { // Unexpected interpreter behavior /// Unexpected condition or failure in the type-checker, indicating a bug or invalid state. /// This error indicates a transaction would invalidate a block if included. - Expects(String), + ExpectsRejectable(String), + // Unexpected interpreter behavior + /// Unexpected condition or failure in the type-checker, indicating a bug or invalid state. + /// This error does NOT indicate a transaction would invalidate a block if included. + ExpectsAcceptable(String), // Type mismatch errors /// Expected type does not match the actual type during analysis. @@ -256,6 +260,61 @@ pub enum CommonCheckErrorKind { TraitTooManyMethods(usize, usize), } +impl CommonCheckErrorKind { + pub fn from_clarity_type_error(err: ClarityTypeError) -> Self { + match err { + ClarityTypeError::ValueTooLarge => CommonCheckErrorKind::ValueTooLarge, + ClarityTypeError::TypeSignatureTooDeep => CommonCheckErrorKind::TypeSignatureTooDeep, + ClarityTypeError::ValueOutOfBounds => CommonCheckErrorKind::ValueOutOfBounds, + ClarityTypeError::DuplicateTupleField(name) => { + CommonCheckErrorKind::NameAlreadyUsed(name) + } + ClarityTypeError::TypeMismatch(expected, found) => { + CommonCheckErrorKind::TypeError(expected, found) + } + ClarityTypeError::EmptyTuplesNotAllowed => CommonCheckErrorKind::EmptyTuplesNotAllowed, + ClarityTypeError::SupertypeTooLarge => CommonCheckErrorKind::SupertypeTooLarge, + ClarityTypeError::InvalidTypeDescription => { + CommonCheckErrorKind::InvalidTypeDescription + } + ClarityTypeError::CouldNotDetermineType => CommonCheckErrorKind::CouldNotDetermineType, + ClarityTypeError::ListTypeMismatch + | ClarityTypeError::SequenceElementArityMismatch { .. } + | ClarityTypeError::ExpectedSequenceValue + | ClarityTypeError::InvalidAsciiCharacter(_) + | ClarityTypeError::InvalidUtf8Encoding + | ClarityTypeError::NoSuchTupleField(_, _) + | ClarityTypeError::TypeMismatchValue(_, _) + | ClarityTypeError::CouldNotDetermineSerializationType + | ClarityTypeError::InvalidUrlString(_) + | ClarityTypeError::InvalidClarityName(_) + | ClarityTypeError::InvalidContractName(_) + | ClarityTypeError::QualifiedContractEmptyIssuer + | ClarityTypeError::QualifiedContractMissingDot + | ClarityTypeError::InvalidPrincipalEncoding(_) + | ClarityTypeError::InvalidPrincipalLength(_) + | ClarityTypeError::ResponseTypeMismatch { .. } => { + CommonCheckErrorKind::ExpectsAcceptable(format!( + "Unexpected error type during analysis: {err}" + )) + } + ClarityTypeError::InvariantViolation(_) + | ClarityTypeError::InvalidPrincipalVersion(_) => { + CommonCheckErrorKind::ExpectsRejectable(format!( + "Unexpected error type during analysis: {err}" + )) + } + ClarityTypeError::UnsupportedTypeInEpoch(ty, epoch) => { + CommonCheckErrorKind::ExpectsRejectable(format!( + "{ty} should not be used in {epoch}" + )) + } + ClarityTypeError::UnsupportedEpoch(epoch) => { + CommonCheckErrorKind::ExpectsRejectable(format!("{epoch} is not supported")) + } + } + } +} /// An error detected during the static analysis of a smart contract at deployment time. /// /// These checks are performed once, before any contract execution occurs, to find issues @@ -293,7 +352,11 @@ pub enum StaticCheckErrorKind { // Unexpected interpreter behavior /// Unexpected condition or failure in the type-checker, indicating a bug or invalid state. /// This error indicates a transaction would invalidate a block if included. - Expects(String), + ExpectsRejectable(String), + // Unexpected interpreter behavior + /// Unexpected condition or failure in the type-checker, indicating a bug or invalid state. + /// This error does NOT indicate a transaction would invalidate a block if included. + ExpectsAcceptable(String), // Match expression errors /// Invalid syntax in an `option` match expression. @@ -606,7 +669,11 @@ pub enum CheckErrorKind { // Unexpected interpreter behavior /// Unexpected condition or failure in the type-checker, indicating a bug or invalid state. /// This error indicates a transaction would invalidate a block if included. - Expects(String), + ExpectsRejectable(String), + // Unexpected interpreter behavior + /// Unexpected condition or failure in the type-checker, indicating a bug or invalid state. + /// This error does NOT indicate a transaction would invalidate a block if included. + ExpectsAcceptable(String), // Match expression errors /// Invalid syntax in an `option` match expression. @@ -760,7 +827,7 @@ pub enum CheckErrorKind { /// Expected a list application but found a different expression. ExpectedListApplication, /// Expected a sequence type (e.g., list, buffer) but found a different type. - /// The `Box` wraps the actual type provided. + /// The `Box` wraps the actual type provided if we know it. ExpectedSequence(Box), // Let syntax @@ -863,9 +930,57 @@ impl CheckErrorKind { pub fn rejectable(&self) -> bool { matches!( self, - CheckErrorKind::SupertypeTooLarge | CheckErrorKind::Expects(_) + CheckErrorKind::SupertypeTooLarge | CheckErrorKind::ExpectsRejectable(_) ) } + + pub fn from_clarity_type_error(err: ClarityTypeError) -> Self { + match err { + ClarityTypeError::ValueTooLarge => CheckErrorKind::ValueTooLarge, + ClarityTypeError::TypeSignatureTooDeep => CheckErrorKind::TypeSignatureTooDeep, + ClarityTypeError::ValueOutOfBounds => CheckErrorKind::ValueOutOfBounds, + ClarityTypeError::DuplicateTupleField(name) => CheckErrorKind::NameAlreadyUsed(name), + ClarityTypeError::NoSuchTupleField(field, tuple_sig) => { + CheckErrorKind::NoSuchTupleField(field, tuple_sig) + } + ClarityTypeError::TypeMismatchValue(ty, value) => { + CheckErrorKind::TypeValueError(ty, value) + } + ClarityTypeError::TypeMismatch(expected, found) => { + CheckErrorKind::TypeError(expected, found) + } + ClarityTypeError::EmptyTuplesNotAllowed => CheckErrorKind::EmptyTuplesNotAllowed, + ClarityTypeError::SupertypeTooLarge => CheckErrorKind::SupertypeTooLarge, + ClarityTypeError::InvalidTypeDescription => CheckErrorKind::InvalidTypeDescription, + ClarityTypeError::ListTypeMismatch => CheckErrorKind::ListTypesMustMatch, + ClarityTypeError::InvalidAsciiCharacter(_) => CheckErrorKind::InvalidCharactersDetected, + ClarityTypeError::InvalidUtf8Encoding => CheckErrorKind::InvalidUTF8Encoding, + ClarityTypeError::ExpectedSequenceValue + | ClarityTypeError::SequenceElementArityMismatch { .. } + | ClarityTypeError::CouldNotDetermineSerializationType + | ClarityTypeError::InvalidUrlString(_) + | ClarityTypeError::InvalidClarityName(_) + | ClarityTypeError::InvalidContractName(_) + | ClarityTypeError::QualifiedContractEmptyIssuer + | ClarityTypeError::QualifiedContractMissingDot + | ClarityTypeError::InvalidPrincipalEncoding(_) + | ClarityTypeError::InvalidPrincipalLength(_) + | ClarityTypeError::ResponseTypeMismatch { .. } => CheckErrorKind::ExpectsAcceptable( + format!("Unexpected error type during runtime analysis: {err}"), + ), + ClarityTypeError::InvariantViolation(_) + | ClarityTypeError::InvalidPrincipalVersion(_) => CheckErrorKind::ExpectsRejectable( + format!("Unexpected error type during runtime analysis: {err}"), + ), + ClarityTypeError::CouldNotDetermineType => CheckErrorKind::CouldNotDetermineType, + ClarityTypeError::UnsupportedTypeInEpoch(ty, epoch) => { + CheckErrorKind::ExpectsRejectable(format!("{ty} should not be used in {epoch}")) + } + ClarityTypeError::UnsupportedEpoch(epoch) => { + CheckErrorKind::ExpectsRejectable(format!("{epoch} is not supported")) + } + } + } } impl StaticCheckErrorKind { @@ -873,9 +988,65 @@ impl StaticCheckErrorKind { pub fn rejectable(&self) -> bool { matches!( self, - StaticCheckErrorKind::SupertypeTooLarge | StaticCheckErrorKind::Expects(_) + StaticCheckErrorKind::SupertypeTooLarge | StaticCheckErrorKind::ExpectsRejectable(_) ) } + + pub fn from_clarity_type_error(err: ClarityTypeError) -> Self { + match err { + ClarityTypeError::ValueTooLarge => StaticCheckErrorKind::ValueTooLarge, + ClarityTypeError::TypeSignatureTooDeep => StaticCheckErrorKind::TypeSignatureTooDeep, + ClarityTypeError::ValueOutOfBounds => StaticCheckErrorKind::ValueOutOfBounds, + ClarityTypeError::DuplicateTupleField(name) => { + StaticCheckErrorKind::NameAlreadyUsed(name) + } + ClarityTypeError::NoSuchTupleField(field, tuple_sig) => { + StaticCheckErrorKind::NoSuchTupleField(field, tuple_sig) + } + ClarityTypeError::TypeMismatch(expected, found) => { + StaticCheckErrorKind::TypeError(expected, found) + } + ClarityTypeError::EmptyTuplesNotAllowed => StaticCheckErrorKind::EmptyTuplesNotAllowed, + ClarityTypeError::SupertypeTooLarge => StaticCheckErrorKind::SupertypeTooLarge, + ClarityTypeError::InvalidTypeDescription => { + StaticCheckErrorKind::InvalidTypeDescription + } + ClarityTypeError::InvalidUrlString(_) + | ClarityTypeError::InvalidClarityName(_) + | ClarityTypeError::InvalidContractName(_) + | ClarityTypeError::QualifiedContractEmptyIssuer + | ClarityTypeError::QualifiedContractMissingDot + | ClarityTypeError::InvalidPrincipalEncoding(_) + | ClarityTypeError::InvalidPrincipalLength(_) + | ClarityTypeError::ListTypeMismatch + | ClarityTypeError::SequenceElementArityMismatch { .. } + | ClarityTypeError::ExpectedSequenceValue + | ClarityTypeError::TypeMismatchValue(_, _) + | ClarityTypeError::ResponseTypeMismatch { .. } + | ClarityTypeError::InvalidAsciiCharacter(_) + | ClarityTypeError::InvalidUtf8Encoding => StaticCheckErrorKind::ExpectsAcceptable( + format!("Unexpected error type during static analysis: {err}"), + ), + ClarityTypeError::InvariantViolation(_) + | ClarityTypeError::InvalidPrincipalVersion(_) => { + StaticCheckErrorKind::ExpectsRejectable(format!( + "Unexpected error type during static analysis: {err}" + )) + } + ClarityTypeError::CouldNotDetermineSerializationType => { + StaticCheckErrorKind::CouldNotDetermineSerializationType + } + ClarityTypeError::CouldNotDetermineType => StaticCheckErrorKind::CouldNotDetermineType, + ClarityTypeError::UnsupportedTypeInEpoch(ty, epoch) => { + StaticCheckErrorKind::ExpectsRejectable(format!( + "{ty} should not be used in {epoch}" + )) + } + ClarityTypeError::UnsupportedEpoch(epoch) => { + StaticCheckErrorKind::ExpectsRejectable(format!("{epoch} is not supported")) + } + } + } } impl StaticCheckError { @@ -907,6 +1078,10 @@ impl StaticCheckError { r.set_expression(expr); r } + + pub fn from_clarity_type_error(err: ClarityTypeError) -> Self { + StaticCheckErrorKind::from_clarity_type_error(err).into() + } } impl From<(CommonCheckErrorKind, &SymbolicExpression)> for StaticCheckError { @@ -983,10 +1158,10 @@ impl From for StaticCheckErrorKind { CostErrors::CostContractLoadFailure => { StaticCheckErrorKind::CostComputationFailed("Failed to load cost contract".into()) } - CostErrors::InterpreterFailure => StaticCheckErrorKind::Expects( + CostErrors::InterpreterFailure => StaticCheckErrorKind::ExpectsRejectable( "Unexpected interpreter failure in cost computation".into(), ), - CostErrors::Expect(s) => StaticCheckErrorKind::Expects(s), + CostErrors::Expect(s) => StaticCheckErrorKind::ExpectsRejectable(s), CostErrors::ExecutionTimeExpired => StaticCheckErrorKind::ExecutionTimeExpired, } } @@ -1002,10 +1177,10 @@ impl From for CheckErrorKind { CostErrors::CostContractLoadFailure => { CheckErrorKind::CostComputationFailed("Failed to load cost contract".into()) } - CostErrors::InterpreterFailure => { - CheckErrorKind::Expects("Unexpected interpreter failure in cost computation".into()) - } - CostErrors::Expect(s) => CheckErrorKind::Expects(s), + CostErrors::InterpreterFailure => CheckErrorKind::ExpectsRejectable( + "Unexpected interpreter failure in cost computation".into(), + ), + CostErrors::Expect(s) => CheckErrorKind::ExpectsRejectable(s), CostErrors::ExecutionTimeExpired => CheckErrorKind::ExecutionTimeExpired, } } @@ -1025,10 +1200,10 @@ impl From for CommonCheckErrorKind { CostErrors::CostContractLoadFailure => { CommonCheckErrorKind::CostComputationFailed("Failed to load cost contract".into()) } - CostErrors::InterpreterFailure => CommonCheckErrorKind::Expects( + CostErrors::InterpreterFailure => CommonCheckErrorKind::ExpectsRejectable( "Unexpected interpreter failure in cost computation".into(), ), - CostErrors::Expect(s) => CommonCheckErrorKind::Expects(s), + CostErrors::Expect(s) => CommonCheckErrorKind::ExpectsRejectable(s), CostErrors::ExecutionTimeExpired => CommonCheckErrorKind::ExecutionTimeExpired, } } @@ -1097,7 +1272,8 @@ impl From for CheckErrorKind { CommonCheckErrorKind::ExpectedTraitIdentifier => { CheckErrorKind::ExpectedTraitIdentifier } - CommonCheckErrorKind::Expects(s) => CheckErrorKind::Expects(s), + CommonCheckErrorKind::ExpectsRejectable(s) => CheckErrorKind::ExpectsRejectable(s), + CommonCheckErrorKind::ExpectsAcceptable(s) => CheckErrorKind::ExpectsAcceptable(s), CommonCheckErrorKind::CouldNotDetermineType => CheckErrorKind::CouldNotDetermineType, CommonCheckErrorKind::ValueTooLarge => CheckErrorKind::ValueTooLarge, CommonCheckErrorKind::TypeSignatureTooDeep => CheckErrorKind::TypeSignatureTooDeep, @@ -1157,7 +1333,12 @@ impl From for StaticCheckErrorKind { CommonCheckErrorKind::ExpectedTraitIdentifier => { StaticCheckErrorKind::ExpectedTraitIdentifier } - CommonCheckErrorKind::Expects(s) => StaticCheckErrorKind::Expects(s), + CommonCheckErrorKind::ExpectsRejectable(s) => { + StaticCheckErrorKind::ExpectsRejectable(s) + } + CommonCheckErrorKind::ExpectsAcceptable(s) => { + StaticCheckErrorKind::ExpectsAcceptable(s) + } CommonCheckErrorKind::CouldNotDetermineType => { StaticCheckErrorKind::CouldNotDetermineType } @@ -1258,7 +1439,8 @@ impl DiagnosableError for StaticCheckErrorKind { fn message(&self) -> String { match &self { StaticCheckErrorKind::SupertypeTooLarge => "supertype of two types is too large".into(), - StaticCheckErrorKind::Expects(s) => format!("unexpected interpreter behavior: {s}"), + StaticCheckErrorKind::ExpectsRejectable(s) => format!("unexpected interpreter behavior: {s}"), + StaticCheckErrorKind::ExpectsAcceptable(s) => s.clone(), StaticCheckErrorKind::BadMatchOptionSyntax(source) => format!("match on a optional type uses the following syntax: (match input some-name if-some-expression if-none-expression). Caused by: {}", source.message()), diff --git a/clarity-types/src/errors/mod.rs b/clarity-types/src/errors/mod.rs index 4db200c312..d661c63717 100644 --- a/clarity-types/src/errors/mod.rs +++ b/clarity-types/src/errors/mod.rs @@ -160,8 +160,6 @@ pub enum RuntimeError { MaxStackDepthReached, /// The execution context depth exceeded the virtual machine's limit. MaxContextDepthReached, - /// Attempt to construct an invalid or unsupported type at runtime (e.g., malformed data structure). - BadTypeConstruction, /// Reference to an invalid or out-of-bounds block height. /// The `String` represents the string representation of the queried block height that was invalid. BadBlockHeight(String), @@ -173,9 +171,6 @@ pub enum RuntimeError { NoCallerInContext, /// No sender principal available in the current execution context. NoSenderInContext, - /// Invalid name-value pair in contract data (e.g., map keys). - /// The `&'static str` represents the name of the invalid pair, and the `String` represents the offending value. - BadNameValue(&'static str, String), /// Reference to a non-existent block header hash. /// The `BlockHeaderHash` represents the unknown block header hash. UnknownBlockHeaderHash(BlockHeaderHash), diff --git a/clarity-types/src/lib.rs b/clarity-types/src/lib.rs index 68cc269b47..88618db39c 100644 --- a/clarity-types/src/lib.rs +++ b/clarity-types/src/lib.rs @@ -33,7 +33,7 @@ pub mod types; pub use errors::VmExecutionError; pub use representations::{ClarityName, ContractName}; -pub use types::Value; +pub use types::{ClarityTypeError, Value}; pub const MAX_CALL_STACK_DEPTH: usize = 64; diff --git a/clarity-types/src/representations.rs b/clarity-types/src/representations.rs index 86275e25a4..dfda3225fb 100644 --- a/clarity-types/src/representations.rs +++ b/clarity-types/src/representations.rs @@ -23,8 +23,7 @@ use regex::Regex; use stacks_common::codec::{Error as codec_error, StacksMessageCodec, read_next, write_next}; use crate::Value; -use crate::errors::RuntimeError; -use crate::types::TraitIdentifier; +use crate::types::{ClarityTypeError, TraitIdentifier}; pub const CONTRACT_MIN_NAME_LENGTH: usize = 1; pub const CONTRACT_MAX_NAME_LENGTH: usize = 40; @@ -63,20 +62,18 @@ lazy_static! { guarded_string!( ClarityName, - "ClarityName", CLARITY_NAME_REGEX, MAX_STRING_LEN, - RuntimeError, - RuntimeError::BadNameValue + ClarityTypeError, + ClarityTypeError::InvalidClarityName ); guarded_string!( ContractName, - "ContractName", CONTRACT_NAME_REGEX, MAX_STRING_LEN, - RuntimeError, - RuntimeError::BadNameValue + ClarityTypeError, + ClarityTypeError::InvalidContractName ); impl StacksMessageCodec for ClarityName { diff --git a/clarity-types/src/tests/representations.rs b/clarity-types/src/tests/representations.rs index 0d00f7bc87..166dc8e6ab 100644 --- a/clarity-types/src/tests/representations.rs +++ b/clarity-types/src/tests/representations.rs @@ -15,11 +15,11 @@ use rstest::rstest; -use crate::errors::RuntimeError; use crate::representations::{ CONTRACT_MAX_NAME_LENGTH, CONTRACT_MIN_NAME_LENGTH, ClarityName, ContractName, MAX_STRING_LEN, }; use crate::stacks_common::codec::StacksMessageCodec; +use crate::types::ClarityTypeError; #[rstest] #[case::valid_name("hello")] @@ -73,7 +73,7 @@ fn test_clarity_name_invalid(#[case] name: &str) { assert!(result.is_err()); assert!(matches!( result.unwrap_err(), - RuntimeError::BadNameValue(_, _) + ClarityTypeError::InvalidClarityName(_) )); } @@ -99,7 +99,7 @@ fn test_clarity_name_serialization(#[case] name: &str) { // the first byte is the length of the buffer. #[rstest] #[case::invalid_utf8(vec![4, 0xFF, 0xFE, 0xFD, 0xFC], "Failed to parse Clarity name: could not contruct from utf8")] -#[case::invalid_name(vec![2, b'2', b'i'], "Failed to parse Clarity name: BadNameValue(\"ClarityName\", \"2i\")")] // starts with number +#[case::invalid_name(vec![2, b'2', b'i'], "Failed to parse Clarity name: InvalidClarityName(\"2i\")")] // starts with number #[case::too_long(vec![MAX_STRING_LEN + 1], "Failed to deserialize clarity name: too long")] #[case::wrong_length(vec![3, b'a'], "failed to fill whole buffer")] fn test_clarity_name_deserialization_errors(#[case] buffer: Vec, #[case] error_message: &str) { @@ -157,7 +157,7 @@ fn test_contract_name_invalid(#[case] name: &str) { assert!(result.is_err()); assert!(matches!( result.unwrap_err(), - RuntimeError::BadNameValue(_, _) + ClarityTypeError::InvalidContractName(_) )); } @@ -201,7 +201,7 @@ fn test_contract_name_serialization_too_long() { // the first byte is the length of the buffer. #[rstest] #[case::invalid_utf8(vec![4, 0xFF, 0xFE, 0xFD, 0xFC], "Failed to parse Contract name: could not construct from utf8")] -#[case::invalid_name(vec![2, b'2', b'i'], "Failed to parse Contract name: BadNameValue(\"ContractName\", \"2i\")")] // starts with number +#[case::invalid_name(vec![2, b'2', b'i'], "Failed to parse Contract name: InvalidContractName(\"2i\")")] // starts with number #[case::too_long(vec![MAX_STRING_LEN + 1], &format!("Failed to deserialize contract name: too short or too long: {}", MAX_STRING_LEN + 1))] #[case::wrong_length(vec![3, b'a'], "failed to fill whole buffer")] fn test_contract_name_deserialization_errors(#[case] buffer: Vec, #[case] error_message: &str) { diff --git a/clarity-types/src/tests/types/mod.rs b/clarity-types/src/tests/types/mod.rs index 1028d443a2..d977afa684 100644 --- a/clarity-types/src/tests/types/mod.rs +++ b/clarity-types/src/tests/types/mod.rs @@ -18,13 +18,11 @@ mod signatures; 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, - QualifiedContractIdentifier, SequenceData, SequencedValue as _, StandardPrincipalData, - TraitIdentifier, TupleData, TupleTypeSignature, TypeSignature, UTF8Data, Value, + ASCIIData, BuffData, CharType, ClarityTypeError, ListTypeData, MAX_VALUE_SIZE, PrincipalData, + QualifiedContractIdentifier, SequenceData, SequenceSubtype, SequencedValue as _, + StandardPrincipalData, TraitIdentifier, TupleData, TupleTypeSignature, TypeSignature, UTF8Data, + Value, }; #[test] @@ -35,16 +33,16 @@ fn test_constructors() { vec![Value::Int(5), Value::Int(2)], ListTypeData::new_list(TypeSignature::BoolType, 3).unwrap() ), - Err(VmInternalError::FailureConstructingListWithType.into()) + Err(ClarityTypeError::ListTypeMismatch) ); assert_eq!( ListTypeData::new_list(TypeSignature::IntType, MAX_VALUE_SIZE), - Err(CommonCheckErrorKind::ValueTooLarge) + Err(ClarityTypeError::ValueTooLarge) ); assert_eq!( Value::buff_from(vec![0; (MAX_VALUE_SIZE + 1) as usize]), - Err(CheckErrorKind::ValueTooLarge.into()) + Err(ClarityTypeError::ValueTooLarge) ); // Test that wrappers (okay, error, some) @@ -53,17 +51,17 @@ fn test_constructors() { // isn't causing the error). assert_eq!( Value::okay(Value::buff_from(vec![0; (MAX_VALUE_SIZE) as usize]).unwrap()), - Err(CheckErrorKind::ValueTooLarge.into()) + Err(ClarityTypeError::ValueTooLarge) ); assert_eq!( Value::error(Value::buff_from(vec![0; (MAX_VALUE_SIZE) as usize]).unwrap()), - Err(CheckErrorKind::ValueTooLarge.into()) + Err(ClarityTypeError::ValueTooLarge) ); assert_eq!( Value::some(Value::buff_from(vec![0; (MAX_VALUE_SIZE) as usize]).unwrap()), - Err(CheckErrorKind::ValueTooLarge.into()) + Err(ClarityTypeError::ValueTooLarge) ); // Test that the depth limit is correctly enforced: @@ -87,24 +85,24 @@ fn test_constructors() { let inner_value = cons().unwrap(); assert_eq!( TupleData::from_data(vec![("a".into(), inner_value.clone())]), - Err(CheckErrorKind::TypeSignatureTooDeep.into()) + Err(ClarityTypeError::TypeSignatureTooDeep) ); assert_eq!( Value::list_from(vec![inner_value.clone()]), - Err(CheckErrorKind::TypeSignatureTooDeep.into()) + Err(ClarityTypeError::TypeSignatureTooDeep) ); assert_eq!( Value::okay(inner_value.clone()), - Err(CheckErrorKind::TypeSignatureTooDeep.into()) + Err(ClarityTypeError::TypeSignatureTooDeep) ); assert_eq!( Value::error(inner_value.clone()), - Err(CheckErrorKind::TypeSignatureTooDeep.into()) + Err(ClarityTypeError::TypeSignatureTooDeep) ); assert_eq!( Value::some(inner_value), - Err(CheckErrorKind::TypeSignatureTooDeep.into()) + Err(ClarityTypeError::TypeSignatureTooDeep) ); if std::env::var("CIRCLE_TESTING") == Ok("1".to_string()) { @@ -116,7 +114,7 @@ fn test_constructors() { if (u32::MAX as usize) < usize::MAX { assert_eq!( Value::buff_from(vec![0; (u32::MAX as usize) + 10]), - Err(CheckErrorKind::ValueTooLarge.into()) + Err(ClarityTypeError::ValueTooLarge) ); } } @@ -256,65 +254,60 @@ fn test_qualified_contract_identifier_local_returns_runtime_error() { let err = QualifiedContractIdentifier::local("1nvalid-name") .expect_err("Unexpected qualified contract identifier"); assert_eq!( - VmExecutionError::from(RuntimeError::BadNameValue( - "ContractName", - "1nvalid-name".into() - )), + ClarityTypeError::InvalidContractName("1nvalid-name".into()), err, ); } #[rstest] -#[case::too_short("S162RK3CHJPCSSK6BM757FW", RuntimeError::TypeParseFailure( - "Invalid principal literal: Expected 20 data bytes.".to_string(), +#[case::too_short("S162RK3CHJPCSSK6BM757FW", ClarityTypeError::InvalidPrincipalLength(9))] +#[case::too_long( + "S1C5H66S35CSKK6CK1C9HP8SB6CWSK4RB2CDJK8HY4", + ClarityTypeError::InvalidPrincipalLength(21) +)] +#[case::invalid_c32("II2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQVX8X0G", ClarityTypeError::InvalidPrincipalEncoding( + "base58ck checksum 0x1074d4f7 does not match expected 0xae29c6e0".into(), ))] -#[case::too_long("S1C5H66S35CSKK6CK1C9HP8SB6CWSK4RB2CDJK8HY4", RuntimeError::TypeParseFailure( - "Invalid principal literal: Expected 20 data bytes.".to_string(), -))] -#[case::invalid_c32("II2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQVX8X0G", RuntimeError::TypeParseFailure( - "Invalid principal literal: base58ck checksum 0x1074d4f7 does not match expected 0xae29c6e0".to_string(), -))] -fn test_principal_data_parse_standard_principal_returns_runtime_error( +fn test_principal_data_parse_standard_principal_returns_clarity_type_error( #[case] input: &str, - #[case] expected_err: RuntimeError, + #[case] expected_err: ClarityTypeError, ) { - let err = - PrincipalData::parse_standard_principal(input).expect_err("Unexpected principal data"); - assert_eq!(VmExecutionError::from(expected_err), err); + let err = PrincipalData::parse_standard_principal(input) + .expect_err("Unexpected valid principal data"); + assert_eq!(expected_err, err); } #[rstest] -#[case::no_dot("SM2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQVX8X0Gcontract-name", RuntimeError::TypeParseFailure( - "Invalid principal literal: expected a `.` in a qualified contract name" - .to_string(), -))] -#[case::invalid_contract_name("SM2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQVX8X0G.1nvalid-name", RuntimeError::BadNameValue("ContractName", "1nvalid-name".into()))] +#[case::no_dot( + "SM2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQVX8X0Gcontract-name", + ClarityTypeError::QualifiedContractMissingDot +)] +#[case::invalid_contract_name("SM2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQVX8X0G.1nvalid-name", ClarityTypeError::InvalidContractName("1nvalid-name".into()))] -fn test_qualified_contract_identifier_parse_returns_vm_internal_error( +fn test_qualified_contract_identifier_parse_returns_clarity_type_error( #[case] input: &str, - #[case] expected_err: RuntimeError, + #[case] expected_err: ClarityTypeError, ) { let err = QualifiedContractIdentifier::parse(input) .expect_err("Unexpected qualified contract identifier"); - assert_eq!(VmExecutionError::from(expected_err), err); + assert_eq!(expected_err, err); } #[rstest] -#[case::no_dot("SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9.nft-traitnft-trait", RuntimeError::TypeParseFailure( - "Invalid principal literal: expected a `.` in a qualified contract name" - .to_string(), -))] -#[case::invalid_contract_name("SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9.1nvalid-contract.valid-trait", RuntimeError::BadNameValue("ContractName", "1nvalid-contract".into()))] -#[case::invalid_trait_name("SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9.valid-contract.1nvalid-trait", RuntimeError::BadNameValue("ClarityName", "1nvalid-trait".into()))] -#[case::invalid_standard_principal("S162RK3CHJPCSSK6BM757FW.valid-contract.valid-trait", RuntimeError::TypeParseFailure( - "Invalid principal literal: Expected 20 data bytes.".to_string(), -))] -fn test_trait_identifier_parse_returns_runtime_error( +#[case::no_dot( + "SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9.nft-traitnft-trait", + ClarityTypeError::QualifiedContractMissingDot +)] +#[case::invalid_contract_name("SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9.1nvalid-contract.valid-trait", ClarityTypeError::InvalidContractName("1nvalid-contract".into()))] +#[case::invalid_trait_name("SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9.valid-contract.1nvalid-trait", ClarityTypeError::InvalidClarityName("1nvalid-trait".into()))] +#[case::invalid_standard_principal( + "S162RK3CHJPCSSK6BM757FW.valid-contract.valid-trait", + ClarityTypeError::InvalidPrincipalLength(9) +)] +fn test_trait_identifier_parse_returns_clarity_type_error( #[case] input: &str, - #[case] expected_err: RuntimeError, + #[case] expected_err: ClarityTypeError, ) { - let expected_err = VmExecutionError::from(expected_err); - let err = TraitIdentifier::parse(input).expect_err("Unexpected trait identifier"); assert_eq!(expected_err, err); @@ -324,71 +317,65 @@ fn test_trait_identifier_parse_returns_runtime_error( } #[rstest] -#[case::bad_type_construction(".valid-contract.valid-trait", RuntimeError::BadTypeConstruction)] -#[case::forwards_parse_errors("S162RK3CHJPCSSK6BM757FW.valid-contract.valid-trait", RuntimeError::TypeParseFailure( - "Invalid principal literal: Expected 20 data bytes.".to_string(), -))] -fn test_trait_identifier_parse_fully_qualified_returns_runtime_error( +#[case::bad_type_construction( + ".valid-contract.valid-trait", + ClarityTypeError::QualifiedContractEmptyIssuer +)] +#[case::forwards_parse_errors( + "S162RK3CHJPCSSK6BM757FW.valid-contract.valid-trait", + ClarityTypeError::InvalidPrincipalLength(9) +)] +fn test_trait_identifier_parse_fully_qualified_returns_clarity_type_error( #[case] input: &str, - #[case] expected_err: RuntimeError, + #[case] expected_err: ClarityTypeError, ) { let err = TraitIdentifier::parse_fully_qualified(input).expect_err("Unexpected trait identifier"); - assert_eq!(VmExecutionError::from(expected_err), err); + assert_eq!(expected_err, err); } -/// The returned VMInternalError is consensus-critical. +// TODO: remove this comment. Is this truly consensus critical? i.e. if it just returns an error, does it matter if it +// maintains that its a rejectable block +/// The returned ClarityTypeError::InvalidPrincipalVersion is consensus-critical. #[test] -fn test_standard_principal_data_new_returns_vm_internal_error_consensus_critical() { +fn test_standard_principal_data_new_returns_clarity_type_error_invalid_principal_version_error_consensus_critical() + { let result = StandardPrincipalData::new(32, [0; 20]); - let err = result.expect_err("Unexpected principal data"); + let err = result.expect_err("Unexpected valid principal data"); - assert_eq!( - VmExecutionError::from(VmInternalError::Expect("Unexpected principal data".into())), - err.into(), - ); + assert_eq!(ClarityTypeError::InvalidPrincipalVersion(32), err,); } -/// The returned VMInternalError is consensus-critical. +// TODO: remove this comment. Is this truly consensus critical? i.e. if it just returns an error, does it matter if it +// maintains that its a rejectable block? Currently all calls to elemant_at are converted to an VmInternal::Expects #[test] -fn test_sequence_data_element_at_returns_vm_internal_error_consensus_critical() { +fn test_sequence_data_element_at_returns_clarity_type_error_consensus_critical() { let buff = SequenceData::String(CharType::ASCII(ASCIIData { data: vec![1] })); let err = buff.element_at(0).unwrap_err(); - assert_eq!( - VmExecutionError::from(VmInternalError::Expect( - "BUG: failed to initialize single-byte ASCII buffer".into() - )), - err - ); + assert_eq!(ClarityTypeError::InvalidAsciiCharacter(1), err); } -/// The returned VMInternalError is consensus-critical. +// TODO: remove this comment. Is this truly consensus critical? i.e. if it just returns an error, does it matter if it +// maintains that its a rejectable block #[test] -fn test_ascii_data_to_value_returns_vm_internal_error_consensus_critical() { +fn test_ascii_data_to_value_returns_clarity_type_error() { let err = ASCIIData::to_value(&1).unwrap_err(); - assert_eq!( - VmExecutionError::from(VmInternalError::Expect( - "ERROR: Invalid ASCII string successfully constructed".into() - )), - err - ); + assert_eq!(ClarityTypeError::InvalidAsciiCharacter(1), err); } -/// The returned VMInternalError is consensus-critical. +// TODO: remove this comment. Is this truly consensus critical? i.e. if it just returns an error, does it matter if it +// maintains that its a rejectable block #[test] -fn test_utf8_data_to_value_returns_vm_internal_error_consensus_critical() { +fn test_utf8_data_to_value_returns_clarity_types_error_invalid_utf8_encoding_consensus_critical() { let err = UTF8Data::to_value(&vec![0xED, 0xA0, 0x80]).unwrap_err(); - assert_eq!( - VmExecutionError::from(VmInternalError::Expect( - "ERROR: Invalid UTF8 string successfully constructed".into() - )), - err - ); + assert_eq!(ClarityTypeError::InvalidUtf8Encoding, err); } -/// The returned VMInternalError is consensus-critical. +// TODO: remove this comment. Is this truly consensus critical? i.e. if it just returns an error, does it matter if it +// maintains that its a rejectable block? Currently even without my own changes, calls to from_data_typed are already +// immediately remapped #[test] -fn test_tuple_data_from_data_typed_returns_vm_internal_error_consensus_critical() { +fn test_tuple_data_from_data_typed_returns_clarity_type_error() { let tuple_type = TupleTypeSignature::try_from(vec![("a".into(), TypeSignature::IntType)]).unwrap(); let err = TupleData::from_data_typed( @@ -398,193 +385,237 @@ fn test_tuple_data_from_data_typed_returns_vm_internal_error_consensus_critical( ) .unwrap_err(); assert_eq!( - VmExecutionError::from(VmInternalError::FailureConstructingTupleWithType), + ClarityTypeError::TypeMismatchValue( + Box::new(TypeSignature::IntType), + Box::new(Value::UInt(1)), + ), err ); } #[rstest] -#[case::not_a_string(Value::none(), VmInternalError::Expect("Expected ASCII string".to_string()))] -#[case::invalid_utf8(Value::Sequence(SequenceData::String(CharType::ASCII(ASCIIData { data: vec![0xED, 0xA0, 0x80] }))), VmInternalError::Expect("Non UTF-8 data in string".to_string()))] -fn test_value_expect_ascii_returns_vm_internal_error( +#[case::not_a_string( + Value::none(), + ClarityTypeError::TypeMismatchValue( + Box::new(TypeSignature::STRING_ASCII_MIN), + Box::new(Value::none()) + ) +)] +#[case::invalid_utf8(Value::Sequence(SequenceData::String(CharType::ASCII(ASCIIData { data: vec![0xED, 0xA0, 0x80] }))), ClarityTypeError::InvalidUtf8Encoding)] +fn test_value_expect_ascii_returns_clarity_type_error( #[case] value: Value, - #[case] expected_err: VmInternalError, + #[case] expected_err: ClarityTypeError, ) { let err = value.expect_ascii().unwrap_err(); - assert_eq!(VmExecutionError::from(expected_err), err); + assert_eq!(expected_err, err); } -/// The returned VMInternalError is consensus-critical. +// TODO: remove this comment. Is this truly consensus critical? i.e. if it just returns an error, does it matter if it +// maintains that its a rejectable block? I think its up to the caller to determine if its consensus critical issue #[test] -fn test_value_expect_u128_returns_vm_internal_error_consensus_critical() { +fn test_value_expect_u128_returns_clarity_type_error() { let err = Value::none().expect_u128().unwrap_err(); assert_eq!( - VmExecutionError::from(VmInternalError::Expect("Expected u128".to_string())), + ClarityTypeError::TypeMismatchValue( + Box::new(TypeSignature::UIntType), + Box::new(Value::none()) + ), err ); } #[test] -fn test_value_expect_i128_returns_vm_internal_error() { +fn test_value_expect_i128_returns_clarity_type_error() { let err = Value::none().expect_i128().unwrap_err(); assert_eq!( - VmExecutionError::from(VmInternalError::Expect("Expected i128".to_string())), + ClarityTypeError::TypeMismatchValue( + Box::new(TypeSignature::IntType), + Box::new(Value::none()) + ), err ); } #[rstest] -#[case::not_a_buffer(Value::none(), VmInternalError::Expect("Expected buff".to_string()))] -#[case::too_small(Value::buff_from(vec![1, 2, 3, 4]).unwrap(), VmInternalError::Expect("Unexpected buff length".to_string()))] -fn test_value_expect_buff_returns_vm_internal_error( +#[case::not_a_buffer( + Value::none(), + ClarityTypeError::TypeMismatchValue( + Box::new(TypeSignature::BUFFER_MIN), + Box::new(Value::none()) + ) +)] +#[case::too_small(Value::buff_from(vec![1, 2, 3, 4]).unwrap(), ClarityTypeError::ValueOutOfBounds)] +fn test_value_expect_buff_returns_clarity_type_error( #[case] value: Value, - #[case] expected_err: VmInternalError, + #[case] expected_err: ClarityTypeError, ) { let err = value.expect_buff(1).unwrap_err(); - assert_eq!(VmExecutionError::from(expected_err), err); + assert_eq!(expected_err, err); } #[test] -fn test_value_expect_tuple_returns_vm_internal_error() { +fn test_value_expect_tuple_returns_clarity_type_error() { let err = Value::none().expect_tuple().unwrap_err(); assert_eq!( - VmExecutionError::from(VmInternalError::Expect("Expected tuple".to_string())), + ClarityTypeError::TypeMismatchValue( + // Unfortunately cannot construct an empty Tuple type + // And to add it now would be intrusive. + Box::new(TypeSignature::NoType), + Box::new(Value::none()), + ), err ); } #[test] -fn test_value_expect_list_returns_vm_internal_error() { +fn test_value_expect_list_returns_clarity_type_error() { let err = Value::none().expect_list().unwrap_err(); assert_eq!( - VmExecutionError::from(VmInternalError::Expect("Expected list".to_string())), + ClarityTypeError::TypeMismatchValue( + Box::new(TypeSignature::SequenceType(SequenceSubtype::ListType( + TypeSignature::empty_list(), + ))), + Box::new(Value::none()), + ), err ); } #[test] -fn test_value_expect_buff_padded_returns_vm_internal_error() { +fn test_value_expect_buff_padded_returns_clarity_type_error() { let err = Value::none().expect_buff_padded(10, 0).unwrap_err(); assert_eq!( - VmExecutionError::from(VmInternalError::Expect("Expected buff".to_string())), + ClarityTypeError::TypeMismatchValue( + Box::new(TypeSignature::BUFFER_MIN), + Box::new(Value::none()), + ), err ); } #[test] -fn test_value_expect_bool_returns_vm_internal_error() { +fn test_value_expect_bool_returns_clarity_type_error() { let err = Value::none().expect_bool().unwrap_err(); assert_eq!( - VmExecutionError::from(VmInternalError::Expect("Expected bool".to_string())), + ClarityTypeError::TypeMismatchValue( + Box::new(TypeSignature::BoolType), + Box::new(Value::none()), + ), err ); } -/// The returned VMInternalError is consensus-critical. +/// TODO: remove this comment. Is this really consensus critical? +/// I think its up to the caller to determine if its consensus critical issue #[test] -fn test_value_expect_optional_returns_vm_internal_error_consensus_critical() { +fn test_value_expect_optional_returns_clarity_type_error() { let err = Value::okay_true().expect_optional().unwrap_err(); assert_eq!( - VmExecutionError::from(VmInternalError::Expect("Expected optional".to_string())), + ClarityTypeError::TypeMismatchValue( + Box::new(TypeSignature::OptionalType(Box::new(TypeSignature::NoType))), + Box::new(Value::okay_true()), + ), err ); } -/// The returned VMInternalError is consensus-critical. +/// TODO: remove this comment. Is this really consensus critical? +/// I think its up to the caller to determine if its consensus critical issue #[test] -fn test_value_expect_principal_returns_vm_internal_error_consensus_critical() { +fn test_value_expect_principal_returns_clarity_type_error() { let err = Value::none().expect_principal().unwrap_err(); assert_eq!( - VmExecutionError::from(VmInternalError::Expect("Expected principal".to_string())), + ClarityTypeError::TypeMismatchValue( + Box::new(TypeSignature::PrincipalType), + Box::new(Value::none()), + ), err ); } -/// The returned VMInternalError is consensus-critical. +/// TODO: remove this comment. Is this really consensus critical? +/// I think its up to the caller to determine if its consensus critical issue #[test] -fn test_value_expect_callable_returns_vm_internal_error_consensus_critical() { +fn test_value_expect_callable_returns_clarity_type_error() { let err = Value::none().expect_callable().unwrap_err(); + // Unfortunately cannot construct an empty Callable type + // And to add it now would be intrusive. assert_eq!( - VmExecutionError::from(VmInternalError::Expect("Expected callable".to_string())), + ClarityTypeError::TypeMismatchValue( + Box::new(TypeSignature::NoType), + Box::new(Value::none()), + ), err ); } #[test] -fn test_value_expect_result_returns_vm_internal_error() { +fn test_value_expect_result_returns_clarity_type_error() { let err = Value::none().expect_result().unwrap_err(); assert_eq!( - VmExecutionError::from(VmInternalError::Expect("Expected response".to_string())), + ClarityTypeError::TypeMismatchValue( + Box::new(TypeSignature::ResponseType(Box::new(( + TypeSignature::NoType, + TypeSignature::NoType + )))), + Box::new(Value::none()), + ), err ); } #[rstest] -#[case::not_a_response(Value::none(), VmInternalError::Expect("Expected response".to_string()))] -#[case::not_an_ok_response(Value::error(Value::Int(1)).unwrap(), VmInternalError::Expect("Expected ok response".to_string()))] -fn test_value_expect_result_ok_returns_vm_internal_error( +#[case::not_a_response(Value::none(), ClarityTypeError::TypeMismatchValue(Box::new(TypeSignature::ResponseType(Box::new((TypeSignature::NoType, TypeSignature::NoType)))), Box::new(Value::none())))] +#[case::not_an_ok_response(Value::error(Value::Int(1)).unwrap(), ClarityTypeError::ResponseTypeMismatch { expected_ok: true, data_committed: false })] +fn test_value_expect_result_ok_returns_clarity_type_error( #[case] value: Value, - #[case] expected_err: VmInternalError, + #[case] expected_err: ClarityTypeError, ) { let err = value.expect_result_ok().unwrap_err(); - assert_eq!(VmExecutionError::from(expected_err), err); + assert_eq!(expected_err, err); } #[rstest] -#[case::not_a_response(Value::none(), VmInternalError::Expect("Expected response".to_string()))] -#[case::not_an_err_response(Value::okay_true(), VmInternalError::Expect("Expected err response".to_string()))] -fn test_value_expect_result_err_returns_vm_internal_error( +#[case::not_a_response(Value::none(), ClarityTypeError::TypeMismatchValue(Box::new(TypeSignature::ResponseType(Box::new((TypeSignature::NoType, TypeSignature::NoType)))), Box::new(Value::none())))] +#[case::not_an_err_response(Value::okay_true(), ClarityTypeError::ResponseTypeMismatch { expected_ok: false, data_committed: true })] +fn test_value_expect_result_err_returns_clarity_type_error( #[case] value: Value, - #[case] expected_err: VmInternalError, + #[case] expected_err: ClarityTypeError, ) { let err = value.expect_result_err().unwrap_err(); - assert_eq!(VmExecutionError::from(expected_err), err); + assert_eq!(expected_err, err); } -/// The returned VMInternalError is consensus-critical. +// TODO: remove this comment. Is this truly consensus critical? i.e. if it just returns an error, does it matter if it +// maintains that its a rejectable block #[test] -fn test_buff_data_len_returns_vm_internal_error_consensus_critical() { +fn test_buff_data_len_returns_clarity_type_error() { let err = BuffData { data: vec![1; MAX_VALUE_SIZE as usize + 1], } .len() .unwrap_err(); - assert_eq!( - VmExecutionError::from(VmInternalError::Expect( - "Data length should be valid".into() - )), - err - ); + assert_eq!(ClarityTypeError::ValueTooLarge, err); } #[test] -fn test_ascii_data_len_returns_vm_internal_error() { +fn test_ascii_data_len_returns_clarity_type_error() { let err = ASCIIData { data: vec![1; MAX_VALUE_SIZE as usize + 1], } .len() .unwrap_err(); - assert_eq!( - VmExecutionError::from(VmInternalError::Expect( - "Data length should be valid".into() - )), - err - ); + assert_eq!(ClarityTypeError::ValueTooLarge, err); } #[test] -fn test_utf8_data_len_returns_vm_internal_error() { +fn test_utf8_data_len_returns_clarity_type_error() { let err = UTF8Data { data: vec![vec![]; MAX_VALUE_SIZE as usize + 1], } .len() .unwrap_err(); - assert_eq!( - VmExecutionError::from(VmInternalError::Expect( - "Data length should be valid".into() - )), - err - ); + assert_eq!(ClarityTypeError::ValueTooLarge, err); } #[test] @@ -595,10 +626,7 @@ fn invalid_utf8_encoding_from_oob_unicode_escape() { let bad_utf8_literal = "\\u{110000}".to_string(); let err = Value::string_utf8_from_string_utf8_literal(bad_utf8_literal).unwrap_err(); - assert!(matches!( - err, - VmExecutionError::Unchecked(CheckErrorKind::InvalidUTF8Encoding) - )); + assert!(matches!(err, ClarityTypeError::InvalidUtf8Encoding)); } #[test] @@ -611,10 +639,7 @@ fn invalid_string_ascii_from_bytes() { let err = Value::string_ascii_from_bytes(bad_bytes).unwrap_err(); - assert!(matches!( - err, - VmExecutionError::Unchecked(CheckErrorKind::InvalidCharactersDetected) - )); + assert!(matches!(err, ClarityTypeError::InvalidAsciiCharacter(_))); } #[test] @@ -624,8 +649,5 @@ fn invalid_utf8_string_from_bytes() { let err = Value::string_utf8_from_bytes(bad_bytes).unwrap_err(); - assert!(matches!( - err, - VmExecutionError::Unchecked(CheckErrorKind::InvalidCharactersDetected) - )); + assert!(matches!(err, ClarityTypeError::InvalidUtf8Encoding)); } diff --git a/clarity-types/src/tests/types/serialization.rs b/clarity-types/src/tests/types/serialization.rs index 9878bf148c..05df8194cc 100644 --- a/clarity-types/src/tests/types/serialization.rs +++ b/clarity-types/src/tests/types/serialization.rs @@ -14,12 +14,11 @@ // along with this program. If not, see . use std::io::Write; -use crate::VmExecutionError; -use crate::errors::{CheckErrorKind, VmInternalError}; use crate::types::serialization::SerializationError; use crate::types::{ - ASCIIData, CharType, MAX_VALUE_SIZE, PrincipalData, QualifiedContractIdentifier, SequenceData, - StandardPrincipalData, TupleData, TypeSignature, Value, + ASCIIData, CharType, ClarityTypeError, MAX_VALUE_SIZE, PrincipalData, + QualifiedContractIdentifier, SequenceData, StandardPrincipalData, TupleData, TypeSignature, + Value, }; fn test_deser_ser(v: Value) { @@ -374,7 +373,7 @@ fn try_deser_large_list() { assert_eq!( Value::try_deserialize_bytes_untyped(&buff).unwrap_err(), - SerializationError::DeserializationError("Illegal list type".to_string()) + SerializationError::DeserializationFailure("Illegal list type".to_string()) ); } @@ -386,7 +385,7 @@ fn try_deser_large_tuple() { assert_eq!( Value::try_deserialize_bytes_untyped(&buff).unwrap_err(), - SerializationError::DeserializationError("Illegal tuple type".to_string()) + SerializationError::DeserializationFailure("Illegal tuple type".to_string()) ); } @@ -394,7 +393,7 @@ fn try_deser_large_tuple() { fn try_overflow_stack() { let input = "08080808080808080808070707080807080808080808080708080808080708080707080707080807080808080808080708080808080708080707080708070807080808080808080708080808080708080708080808080808080807070807080808080808070808070707080807070808070808080808070808070708070807080808080808080707080708070807080708080808080808070808080808070808070808080808080808080707080708080808080807080807070708080707080807080808080807080807070807080708080808080808070708070808080808080708080707070808070708080807080807070708"; assert_eq!( - Err(CheckErrorKind::TypeSignatureTooDeep.into()), + Err(ClarityTypeError::TypeSignatureTooDeep.into()), Value::try_deserialize_hex_untyped(input) ); } @@ -416,32 +415,30 @@ fn test_principals() { test_bad_expectation(standard_p, TypeSignature::BoolType); } -/// The returned VmInternalError is consensus-critical. +/// TODO: remove this comment: I have made it so that any serialize_to_vec that fails is mapped to an Expect. +/// Not sure that is sufficient... #[test] -fn test_serialize_to_vec_returns_vm_internal_error_consensus_critical() { +fn test_serialize_to_vec_returns_serialization_failure() { let value = Value::Sequence(SequenceData::String(CharType::ASCII(ASCIIData { data: vec![0; MAX_VALUE_SIZE as usize + 1], }))); let err = value.serialize_to_vec().unwrap_err(); assert_eq!( - VmExecutionError::from(VmInternalError::Expect( - "IOError filling byte buffer.".into() - )), - err.into() + SerializationError::SerializationFailure(ClarityTypeError::ValueTooLarge.to_string()), + err ); } -/// The returned VmInternalError is consensus-critical. +/// TODO: remove this comment: I have made it so that any serialize_to_vec that fails is mapped to an Expect. +/// Not sure that is sufficient... #[test] -fn test_serialize_to_hex_returns_vm_internal_error_consensus_critical() { +fn test_serialize_to_hex_returns_serialization_failure() { let value = Value::Sequence(SequenceData::String(CharType::ASCII(ASCIIData { data: vec![0; MAX_VALUE_SIZE as usize + 1], }))); let err = value.serialize_to_hex().unwrap_err(); assert_eq!( - VmExecutionError::from(VmInternalError::Expect( - "IOError filling byte buffer.".into() - )), - err.into() + SerializationError::SerializationFailure(ClarityTypeError::ValueTooLarge.to_string()), + err ); } diff --git a/clarity-types/src/tests/types/signatures.rs b/clarity-types/src/tests/types/signatures.rs index a41dbf6ed3..1636220e9f 100644 --- a/clarity-types/src/tests/types/signatures.rs +++ b/clarity-types/src/tests/types/signatures.rs @@ -14,14 +14,14 @@ // along with this program. If not, see . use std::collections::HashSet; -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}; use crate::types::{ - BufferLength, MAX_TO_ASCII_BUFFER_LEN, MAX_TO_ASCII_RESULT_LEN, MAX_TYPE_DEPTH, - MAX_UTF8_VALUE_SIZE, MAX_VALUE_SIZE, QualifiedContractIdentifier, SequenceSubtype, - StringSubtype, StringUTF8Length, TraitIdentifier, TupleTypeSignature, WRAPPER_VALUE_SIZE, + BufferLength, ClarityTypeError, MAX_TO_ASCII_BUFFER_LEN, MAX_TO_ASCII_RESULT_LEN, + MAX_TYPE_DEPTH, MAX_UTF8_VALUE_SIZE, MAX_VALUE_SIZE, QualifiedContractIdentifier, + SequenceSubtype, StringSubtype, StringUTF8Length, TraitIdentifier, TupleTypeSignature, + WRAPPER_VALUE_SIZE, }; #[test] @@ -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!(CommonCheckErrorKind::ValueTooLarge, err); + assert_eq!(ClarityTypeError::ValueTooLarge, err); } #[test] @@ -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!(CommonCheckErrorKind::ValueTooLarge, err); + assert_eq!(ClarityTypeError::ValueTooLarge, err); } #[test] @@ -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!(CommonCheckErrorKind::ValueTooLarge, err); + assert_eq!(ClarityTypeError::ValueTooLarge, err); let err = BufferLength::try_from(-1_i128).unwrap_err(); - assert_eq!(CommonCheckErrorKind::ValueOutOfBounds, err); + assert_eq!(ClarityTypeError::ValueOutOfBounds, err); } #[test] @@ -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!(CommonCheckErrorKind::ValueTooLarge, err); + assert_eq!(ClarityTypeError::ValueTooLarge, err); } #[test] @@ -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!(CommonCheckErrorKind::ValueTooLarge, err); + assert_eq!(ClarityTypeError::ValueTooLarge, err); } #[test] @@ -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!(CommonCheckErrorKind::ValueTooLarge, err); + assert_eq!(ClarityTypeError::ValueTooLarge, err); let err = StringUTF8Length::try_from(-1_i128).unwrap_err(); - assert_eq!(CommonCheckErrorKind::ValueOutOfBounds, err); + assert_eq!(ClarityTypeError::ValueOutOfBounds, err); } #[test] @@ -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(), - CommonCheckErrorKind::TypeError(..) + ClarityTypeError::TypeMismatch(..) ); matches!( TypeSignature::least_supertype_v2_1(&pair.1, &pair.0).unwrap_err(), - CommonCheckErrorKind::TypeError(..) + ClarityTypeError::TypeMismatch(..) ); } } diff --git a/clarity-types/src/types/mod.rs b/clarity-types/src/types/mod.rs index 4bab14473c..ed6246c6ea 100644 --- a/clarity-types/src/types/mod.rs +++ b/clarity-types/src/types/mod.rs @@ -16,6 +16,7 @@ pub mod serialization; pub mod signatures; +use core::error; use std::collections::BTreeMap; use std::collections::btree_map::Entry; use std::{char, fmt, str}; @@ -36,8 +37,9 @@ pub use self::signatures::{ AssetIdentifier, BufferLength, ListTypeData, SequenceSubtype, StringSubtype, StringUTF8Length, TupleTypeSignature, TypeSignature, }; -use crate::errors::analysis::CommonCheckErrorKind; -use crate::errors::{CheckErrorKind, RuntimeError, VmExecutionError, VmInternalError}; +use crate::VmExecutionError; +use crate::diagnostic::DiagnosableError; +use crate::errors::CheckErrorKind; use crate::representations::{ClarityName, ContractName, SymbolicExpression}; /// Maximum size in bytes allowed for types. @@ -59,6 +61,170 @@ pub const MAX_TYPE_DEPTH: u8 = 32; /// this is the charged size for wrapped values, i.e., response or optionals pub const WRAPPER_VALUE_SIZE: u32 = 1; +/// Errors originating purely from the Clarity type system layer. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ClarityTypeError { + // Size & Depth Invariants + /// The constructed value exceeds the maximum allowed Clarity value size. + ValueTooLarge, + /// The constructed value exceeds the maximum allowed nesting depth. + TypeSignatureTooDeep, + + // String & Encoding Errors + /// A non-ASCII byte was found in an ASCII string. + InvalidAsciiCharacter(u8), + /// The provided bytes did not form valid UTF-8. + InvalidUtf8Encoding, + + // List, Tuple, & Structural Type Errors + /// A list operation failed because element types do not match. + ListTypeMismatch, + /// An index was out of bounds for a sequence. + ValueOutOfBounds, + /// A tuple was constructed with duplicate field names. + DuplicateTupleField(String), + /// Referenced tuple field does not exist in the tuple type. + /// The `String` wraps the requested field name, and the `TupleTypeSignature` wraps the tuple’s type. + NoSuchTupleField(String, TupleTypeSignature), + /// Value does not match the expected type. + /// The `Box` wraps the expected type, and the `Box` wraps the invalid value. + TypeMismatchValue(Box, Box), + /// Expected type does not match the actual type during analysis. + /// The first `Box` wraps the expected type, and the second wraps the actual type. + TypeMismatch(Box, Box), + /// Expected an different response type + ResponseTypeMismatch { + /// Whether the response type should be an `Ok` response + expected_ok: bool, + /// Whether the response data was committed or not + data_committed: bool, + }, + /// Invalid contract name. + /// The `String` represents the offending value. + InvalidContractName(String), + /// Invalid Clarity name. + /// The `String` represents the offending value. + InvalidClarityName(String), + /// Invalid URL. + /// The `String` represents the offending value. + InvalidUrlString(String), + /// Empty tuple is not allowed in Clarity. + EmptyTuplesNotAllowed, + /// Supertype (e.g., trait or union) exceeds the maximum allowed size or complexity. + /// This error indicates a transaction would invalidate a block if included. + SupertypeTooLarge, + /// Type description is invalid or malformed, preventing proper type-checking. + InvalidTypeDescription, + /// Sequence element length mismatch + SequenceElementArityMismatch { expected: usize, found: usize }, + /// Expected a sequence value + ExpectedSequenceValue, + + // Principal & Identifier Errors + /// An invalid version byte was used for a principal. + InvalidPrincipalVersion(u8), + /// An invalid principal byte length was supplied. + InvalidPrincipalLength(usize), + /// C32 decode failed + InvalidPrincipalEncoding(String), + /// An invalid qualified identifier was supplied with a missing '.' separator. + QualifiedContractMissingDot, + /// An invalid qualified identifier was supplied with a missing issuer. + QualifiedContractEmptyIssuer, + + // Type Resolution & Abstract Type Failures + /// The value has a valid abstract type, but it cannot be serialized + /// into a concrete consensus representation. + CouldNotDetermineSerializationType, + /// The type signature could not be determined. + CouldNotDetermineType, + + /// Type is unsupported in the given epoch + UnsupportedTypeInEpoch(Box, StacksEpochId), + /// Unsupported epoch + UnsupportedEpoch(StacksEpochId), + /// Something unexpected happened that should not be possible + InvariantViolation(String), +} + +impl fmt::Display for ClarityTypeError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{self:?}") + } +} + +impl DiagnosableError for ClarityTypeError { + fn message(&self) -> String { + match &self { + // Size & Depth + Self::ValueTooLarge => "value exceeds maximum Clarity size".into(), + Self::TypeSignatureTooDeep => "type signature exceeds maximum nesting depth".into(), + // Encoding + Self::InvalidAsciiCharacter(b) => format!("invalid ASCII character byte: 0x{b:02x}"), + Self::InvalidUtf8Encoding => "invalid UTF-8 encoding".into(), + // List / Tuple / Type Construction + Self::ListTypeMismatch => "list elements do not match required type".into(), + Self::ValueOutOfBounds => "value index is out of bounds".into(), + Self::DuplicateTupleField(name) => format!("duplicate tuple field '{name}'"), + Self::NoSuchTupleField(field_name, tuple_signature) => { + format!("cannot find field '{field_name}' in tuple '{tuple_signature}'") + } + Self::TypeMismatchValue(expected_type, found_value) => { + format!("expecting expression of type '{expected_type}', found '{found_value}'") + } + Self::TypeMismatch(expected_type, found_type) => { + format!("expecting expression of type '{expected_type}', found '{found_type}'") + } + Self::ResponseTypeMismatch { + expected_ok, + data_committed, + } => format!( + "expected ok response `{expected_ok}`, found data committed `{data_committed}`" + ), + Self::InvalidClarityName(value) => format!("invalid clarity name `{value}`"), + Self::InvalidContractName(value) => format!("invalid contract name `{value}`"), + Self::InvalidUrlString(value) => format!("invalid URL string `{value}`"), + Self::EmptyTuplesNotAllowed => "tuple types may not be empty".into(), + Self::SupertypeTooLarge => "supertype of two types is too large".into(), + Self::InvalidTypeDescription => "supplied type description is invalid".into(), + Self::SequenceElementArityMismatch { expected, found } => { + format!("sequence expected {expected} elements, but found {found} elements") + } + Self::ExpectedSequenceValue => "expected sequence value".into(), + // Principal + Self::InvalidPrincipalVersion(v) => format!("invalid principal version byte: {v}"), + Self::InvalidPrincipalLength(len) => { + format!("invalid principal byte length. Expected 20 bytes. Got: {len}") + } + Self::InvalidPrincipalEncoding(msg) => format!("invalid principal encoding: {msg}"), + Self::QualifiedContractMissingDot => { + "expected a `.` in a qualified contract name".into() + } + Self::QualifiedContractEmptyIssuer => "Expected an issuer, but found none".into(), + // Type resolution + Self::CouldNotDetermineSerializationType => { + "could not determine the input type for the serialization function".into() + } + Self::CouldNotDetermineType => "could not determine the input type".into(), + Self::UnsupportedTypeInEpoch(type_signature, epoch) => { + format!("{type_signature} is unsupported in {epoch}") + } + Self::UnsupportedEpoch(epoch) => format!("{epoch} is unsupported"), + Self::InvariantViolation(msg) => format!("Invariant violation: {msg}"), + } + } + + fn suggestion(&self) -> Option { + None + } +} + +impl error::Error for ClarityTypeError { + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + None + } +} + #[derive(Debug, Clone, Eq, Serialize, Deserialize)] pub struct TupleData { // todo: remove type_signature @@ -91,9 +257,9 @@ impl StandardPrincipalData { } impl StandardPrincipalData { - pub fn new(version: u8, bytes: [u8; 20]) -> std::result::Result { + pub fn new(version: u8, bytes: [u8; 20]) -> Result { if version >= 32 { - return Err(VmInternalError::Expect("Unexpected principal data".into())); + return Err(ClarityTypeError::InvalidPrincipalVersion(version)); } Ok(Self(version, bytes)) } @@ -175,7 +341,7 @@ impl QualifiedContractIdentifier { Self { issuer, name } } - pub fn local(name: &str) -> Result { + pub fn local(name: &str) -> Result { let name = name.to_string().try_into()?; Ok(Self::new(StandardPrincipalData::transient(), name)) } @@ -194,14 +360,10 @@ impl QualifiedContractIdentifier { self.issuer.1 == [0; 20] } - pub fn parse(literal: &str) -> Result { + pub fn parse(literal: &str) -> Result { let split: Vec<_> = literal.splitn(2, '.').collect(); if split.len() != 2 { - return Err(RuntimeError::TypeParseFailure( - "Invalid principal literal: expected a `.` in a qualified contract name" - .to_string(), - ) - .into()); + return Err(ClarityTypeError::QualifiedContractMissingDot); } let sender = PrincipalData::parse_standard_principal(split[0])?; let name = split[1].to_string().try_into()?; @@ -283,29 +445,25 @@ impl TraitIdentifier { } } - pub fn parse_fully_qualified(literal: &str) -> Result { + pub fn parse_fully_qualified(literal: &str) -> Result { let (issuer, contract_name, name) = Self::parse(literal)?; - let issuer = issuer.ok_or(RuntimeError::BadTypeConstruction)?; + let issuer = issuer.ok_or(ClarityTypeError::QualifiedContractEmptyIssuer)?; Ok(TraitIdentifier::new(issuer, contract_name, name)) } pub fn parse_sugared_syntax( literal: &str, - ) -> Result<(ContractName, ClarityName), VmExecutionError> { + ) -> Result<(ContractName, ClarityName), ClarityTypeError> { let (_, contract_name, name) = Self::parse(literal)?; Ok((contract_name, name)) } pub fn parse( literal: &str, - ) -> Result<(Option, ContractName, ClarityName), VmExecutionError> { + ) -> Result<(Option, ContractName, ClarityName), ClarityTypeError> { let split: Vec<_> = literal.splitn(3, '.').collect(); if split.len() != 3 { - return Err(RuntimeError::TypeParseFailure( - "Invalid principal literal: expected a `.` in a qualified contract name" - .to_string(), - ) - .into()); + return Err(ClarityTypeError::QualifiedContractMissingDot); } let issuer = match split[0].len() { @@ -343,7 +501,16 @@ pub enum SequenceData { } impl SequenceData { - pub fn atom_values(&mut self) -> Result, VmExecutionError> { + pub fn type_signature(&self) -> Result { + match self { + SequenceData::Buffer(b) => b.type_signature(), + SequenceData::List(l) => l.type_signature(), + SequenceData::String(CharType::ASCII(a)) => a.type_signature(), + SequenceData::String(CharType::UTF8(u)) => u.type_signature(), + } + } + + pub fn atom_values(&mut self) -> Result, ClarityTypeError> { match self { SequenceData::Buffer(data) => data.atom_values(), SequenceData::List(data) => data.atom_values(), @@ -352,7 +519,7 @@ impl SequenceData { } } - pub fn element_size(&self) -> Result { + pub fn element_size(&self) -> Result { let out = match self { SequenceData::Buffer(..) => TypeSignature::BUFFER_MIN.size(), SequenceData::List(data) => data.type_signature.get_list_item_type().size(), @@ -375,7 +542,7 @@ impl SequenceData { self.len() == 0 } - pub fn element_at(self, index: usize) -> Result, VmExecutionError> { + pub fn element_at(self, index: usize) -> Result, ClarityTypeError> { if self.len() <= index { return Ok(None); } @@ -383,11 +550,7 @@ impl SequenceData { SequenceData::Buffer(data) => Value::buff_from_byte(data.data[index]), SequenceData::List(mut data) => data.data.remove(index), SequenceData::String(CharType::ASCII(data)) => { - Value::string_ascii_from_bytes(vec![data.data[index]]).map_err(|_| { - VmInternalError::Expect( - "BUG: failed to initialize single-byte ASCII buffer".into(), - ) - })? + Value::string_ascii_from_bytes(vec![data.data[index]])? } SequenceData::String(CharType::UTF8(mut data)) => { Value::Sequence(SequenceData::String(CharType::UTF8(UTF8Data { @@ -404,7 +567,7 @@ impl SequenceData { epoch: &StacksEpochId, index: usize, element: Value, - ) -> Result { + ) -> Result { let seq_length = self.len(); // Check that the length of the provided element is 1. In the case that SequenceData @@ -413,14 +576,17 @@ impl SequenceData { if let Value::Sequence(data) = &element { let elem_length = data.len(); if elem_length != 1 { - return Err(RuntimeError::BadTypeConstruction.into()); + return Err(ClarityTypeError::SequenceElementArityMismatch { + expected: 1, + found: elem_length, + }); } } else { - return Err(RuntimeError::BadTypeConstruction.into()); + return Err(ClarityTypeError::ExpectedSequenceValue); } } if index >= seq_length { - return Err(CheckErrorKind::ValueOutOfBounds.into()); + return Err(ClarityTypeError::ValueOutOfBounds); } let new_seq_data = match (self, element) { @@ -431,7 +597,7 @@ impl SequenceData { (SequenceData::List(mut data), elem) => { let entry_type = data.type_signature.get_list_item_type(); if !entry_type.admits(epoch, &elem)? { - return Err(CheckErrorKind::ListTypesMustMatch.into()); + return Err(ClarityTypeError::ListTypeMismatch); } data.data[index] = elem; SequenceData::List(data) @@ -450,13 +616,18 @@ impl SequenceData { data.data[index] = elem.data.swap_remove(0); SequenceData::String(CharType::UTF8(data)) } - _ => return Err(CheckErrorKind::ListTypesMustMatch.into()), + (seq, element) => { + return Err(ClarityTypeError::TypeMismatchValue( + Box::new(seq.type_signature()?), + Box::new(element), + )); + } }; - Ok(Value::some(Value::Sequence(new_seq_data))?) + Value::some(Value::Sequence(new_seq_data)) } - pub fn contains(&self, to_find: Value) -> Result, VmExecutionError> { + pub fn contains(&self, to_find: Value) -> Result, ClarityTypeError> { match self { SequenceData::Buffer(data) => { if let Value::Sequence(SequenceData::Buffer(to_find_vec)) = to_find { @@ -471,11 +642,10 @@ impl SequenceData { Ok(None) } } else { - Err(CheckErrorKind::TypeValueError( + Err(ClarityTypeError::TypeMismatchValue( Box::new(TypeSignature::BUFFER_MIN), Box::new(to_find), - ) - .into()) + )) } } SequenceData::List(data) => { @@ -500,11 +670,10 @@ impl SequenceData { Ok(None) } } else { - Err(CheckErrorKind::TypeValueError( + Err(ClarityTypeError::TypeMismatchValue( Box::new(TypeSignature::STRING_ASCII_MIN), Box::new(to_find), - ) - .into()) + )) } } SequenceData::String(CharType::UTF8(data)) => { @@ -521,11 +690,10 @@ impl SequenceData { Ok(None) } } else { - Err(CheckErrorKind::TypeValueError( + Err(ClarityTypeError::TypeMismatchValue( Box::new(TypeSignature::STRING_UTF8_MIN), Box::new(to_find), - ) - .into()) + )) } } } @@ -542,8 +710,10 @@ impl SequenceData { ($data:expr, $seq_type:ident) => { let mut i = 0; while i != $data.data.len() { - let atom_value = - SymbolicExpression::atom_value($seq_type::to_value(&$data.data[i])?); + let atom_value = SymbolicExpression::atom_value( + $seq_type::to_value(&$data.data[i]) + .map_err(CheckErrorKind::from_clarity_type_error)?, + ); match filter(atom_value) { Ok(res) if res == false => { $data.data.remove(i); @@ -578,7 +748,7 @@ impl SequenceData { &mut self, epoch: &StacksEpochId, other_seq: SequenceData, - ) -> Result<(), VmExecutionError> { + ) -> Result<(), ClarityTypeError> { match (self, other_seq) { (SequenceData::List(inner_data), SequenceData::List(other_inner_data)) => { inner_data.append(epoch, other_inner_data)?; @@ -594,7 +764,12 @@ impl SequenceData { SequenceData::String(CharType::UTF8(inner_data)), SequenceData::String(CharType::UTF8(ref mut other_inner_data)), ) => inner_data.append(other_inner_data), - _ => return Err(RuntimeError::BadTypeConstruction.into()), + (seq, other_seq) => { + return Err(ClarityTypeError::TypeMismatch( + Box::new(seq.type_signature()?), + Box::new(other_seq.type_signature()?), + )); + } }; Ok(()) } @@ -604,7 +779,7 @@ impl SequenceData { epoch: &StacksEpochId, left_position: usize, right_position: usize, - ) -> Result { + ) -> Result { let empty_seq = left_position == right_position; let result = match self { @@ -712,15 +887,15 @@ impl fmt::Display for UTF8Data { } pub trait SequencedValue { - fn type_signature(&self) -> std::result::Result; + fn type_signature(&self) -> std::result::Result; fn items(&self) -> &Vec; fn drained_items(&mut self) -> Vec; - fn to_value(v: &T) -> Result; + fn to_value(v: &T) -> Result; - fn atom_values(&mut self) -> Result, VmExecutionError> { + fn atom_values(&mut self) -> Result, ClarityTypeError> { self.drained_items() .iter() .map(|item| Ok(SymbolicExpression::atom_value(Self::to_value(item)?))) @@ -737,13 +912,13 @@ impl SequencedValue for ListData { self.data.drain(..).collect() } - fn type_signature(&self) -> std::result::Result { + fn type_signature(&self) -> std::result::Result { Ok(TypeSignature::SequenceType(SequenceSubtype::ListType( self.type_signature.clone(), ))) } - fn to_value(v: &Value) -> Result { + fn to_value(v: &Value) -> Result { Ok(v.clone()) } } @@ -757,18 +932,14 @@ impl SequencedValue for BuffData { self.data.drain(..).collect() } - fn type_signature(&self) -> std::result::Result { - let buff_length = BufferLength::try_from(self.data.len()).map_err(|_| { - CommonCheckErrorKind::Expects( - "ERROR: Too large of a buffer successfully constructed.".into(), - ) - })?; + fn type_signature(&self) -> Result { + let buff_length = BufferLength::try_from(self.data.len())?; Ok(TypeSignature::SequenceType(SequenceSubtype::BufferType( buff_length, ))) } - fn to_value(v: &u8) -> Result { + fn to_value(v: &u8) -> Result { Ok(Value::buff_from_byte(*v)) } } @@ -782,22 +953,15 @@ impl SequencedValue for ASCIIData { self.data.drain(..).collect() } - fn type_signature(&self) -> std::result::Result { - let buff_length = BufferLength::try_from(self.data.len()).map_err(|_| { - CommonCheckErrorKind::Expects( - "ERROR: Too large of a buffer successfully constructed.".into(), - ) - })?; + fn type_signature(&self) -> std::result::Result { + let buff_length = BufferLength::try_from(self.data.len())?; Ok(TypeSignature::SequenceType(SequenceSubtype::StringType( StringSubtype::ASCII(buff_length), ))) } - fn to_value(v: &u8) -> Result { - Value::string_ascii_from_bytes(vec![*v]).map_err(|_| { - VmInternalError::Expect("ERROR: Invalid ASCII string successfully constructed".into()) - .into() - }) + fn to_value(v: &u8) -> Result { + Value::string_ascii_from_bytes(vec![*v]) } } @@ -810,40 +974,30 @@ impl SequencedValue> for UTF8Data { self.data.drain(..).collect() } - fn type_signature(&self) -> std::result::Result { - let str_len = StringUTF8Length::try_from(self.data.len()).map_err(|_| { - CommonCheckErrorKind::Expects( - "ERROR: Too large of a buffer successfully constructed.".into(), - ) - })?; + fn type_signature(&self) -> std::result::Result { + let str_len = StringUTF8Length::try_from(self.data.len())?; Ok(TypeSignature::SequenceType(SequenceSubtype::StringType( StringSubtype::UTF8(str_len), ))) } - fn to_value(v: &Vec) -> Result { - Value::string_utf8_from_bytes(v.clone()).map_err(|_| { - VmInternalError::Expect("ERROR: Invalid UTF8 string successfully constructed".into()) - .into() - }) + fn to_value(v: &Vec) -> Result { + Value::string_utf8_from_bytes(v.clone()) } } impl OptionalData { - pub fn type_signature(&self) -> std::result::Result { - let type_result = match self.data { + pub fn type_signature(&self) -> Result { + match self.data { Some(ref v) => TypeSignature::new_option(TypeSignature::type_of(v)?), None => TypeSignature::new_option(TypeSignature::NoType), - }; - type_result.map_err(|_| { - CommonCheckErrorKind::Expects("Should not have constructed too large of a type.".into()) - }) + } } } impl ResponseData { - pub fn type_signature(&self) -> std::result::Result { - let type_result = match self.committed { + pub fn type_signature(&self) -> Result { + match self.committed { true => TypeSignature::new_response( TypeSignature::type_of(&self.data)?, TypeSignature::NoType, @@ -852,10 +1006,7 @@ impl ResponseData { TypeSignature::NoType, TypeSignature::type_of(&self.data)?, ), - }; - type_result.map_err(|_| { - CommonCheckErrorKind::Expects("Should not have constructed too large of a type.".into()) - }) + } } } @@ -874,11 +1025,11 @@ impl PartialEq for TupleData { pub const NONE: Value = Value::Optional(OptionalData { data: None }); impl Value { - pub fn some(data: Value) -> Result { + pub fn some(data: Value) -> Result { if data.size()? + WRAPPER_VALUE_SIZE > MAX_VALUE_SIZE { - Err(CheckErrorKind::ValueTooLarge.into()) + Err(ClarityTypeError::ValueTooLarge) } else if data.depth()? + 1 > MAX_TYPE_DEPTH { - Err(CheckErrorKind::TypeSignatureTooDeep.into()) + Err(ClarityTypeError::TypeSignatureTooDeep) } else { Ok(Value::Optional(OptionalData { data: Some(Box::new(data)), @@ -911,11 +1062,11 @@ impl Value { }) } - pub fn okay(data: Value) -> Result { + pub fn okay(data: Value) -> Result { if data.size()? + WRAPPER_VALUE_SIZE > MAX_VALUE_SIZE { - Err(CheckErrorKind::ValueTooLarge.into()) + Err(ClarityTypeError::ValueTooLarge) } else if data.depth()? + 1 > MAX_TYPE_DEPTH { - Err(CheckErrorKind::TypeSignatureTooDeep.into()) + Err(ClarityTypeError::TypeSignatureTooDeep) } else { Ok(Value::Response(ResponseData { committed: true, @@ -924,11 +1075,11 @@ impl Value { } } - pub fn error(data: Value) -> Result { + pub fn error(data: Value) -> Result { if data.size()? + WRAPPER_VALUE_SIZE > MAX_VALUE_SIZE { - Err(CheckErrorKind::ValueTooLarge.into()) + Err(ClarityTypeError::ValueTooLarge) } else if data.depth()? + 1 > MAX_TYPE_DEPTH { - Err(CheckErrorKind::TypeSignatureTooDeep.into()) + Err(ClarityTypeError::TypeSignatureTooDeep) } else { Ok(Value::Response(ResponseData { committed: false, @@ -937,35 +1088,36 @@ impl Value { } } - pub fn size(&self) -> Result { - Ok(TypeSignature::type_of(self)?.size()?) + pub fn size(&self) -> Result { + TypeSignature::type_of(self)?.size() } - pub fn depth(&self) -> Result { + pub fn depth(&self) -> Result { Ok(TypeSignature::type_of(self)?.depth()) } - /// Invariant: the supplied Values have already been "checked", i.e., it's a valid Value object - /// this invariant is enforced through the Value constructors, each of which checks to ensure - /// that any typing data is correct. + // TODO: remove this comment. This is to help reviewers: list_with_type is only called in + // serialization.rs where its returned error is immediately ignored. Therefore changes to the error + // types in here are not consensus-breaking pub fn list_with_type( epoch: &StacksEpochId, list_data: Vec, expected_type: ListTypeData, - ) -> Result { - // Constructors for TypeSignature ensure that the size of the Value cannot - // be greater than MAX_VALUE_SIZE (they error on such constructions) - // so we do not need to perform that check here. + ) -> Result { if (expected_type.get_max_len() as usize) < list_data.len() { - return Err(VmInternalError::FailureConstructingListWithType.into()); + return Err(ClarityTypeError::ValueTooLarge); } { let expected_item_type = expected_type.get_list_item_type(); for item in &list_data { - if !expected_item_type.admits(epoch, item)? { - return Err(VmInternalError::FailureConstructingListWithType.into()); + let admits = expected_item_type + .admits(epoch, item) + .map_err(|_| ClarityTypeError::ListTypeMismatch)?; + + if !admits { + return Err(ClarityTypeError::ListTypeMismatch); } } } @@ -976,7 +1128,7 @@ impl Value { }))) } - pub fn cons_list_unsanitized(list_data: Vec) -> Result { + pub fn cons_list_unsanitized(list_data: Vec) -> Result { let type_sig = TypeSignature::construct_parent_list_type(&list_data)?; Ok(Value::Sequence(SequenceData::List(ListData { data: list_data, @@ -985,14 +1137,14 @@ impl Value { } #[cfg(any(test, feature = "testing"))] - pub fn list_from(list_data: Vec) -> Result { + pub fn list_from(list_data: Vec) -> Result { Value::cons_list_unsanitized(list_data) } pub fn cons_list( list_data: Vec, epoch: &StacksEpochId, - ) -> Result { + ) -> Result { // Constructors for TypeSignature ensure that the size of the Value cannot // be greater than MAX_VALUE_SIZE (they error on such constructions) // Aaron: at this point, we've _already_ allocated memory for this type. @@ -1007,7 +1159,7 @@ impl Value { .map(|(value, _did_sanitize)| value) }) .collect(); - let list_data = list_data_opt.ok_or_else(|| CheckErrorKind::ListTypesMustMatch)?; + let list_data = list_data_opt.ok_or_else(|| ClarityTypeError::ListTypeMismatch)?; Ok(Value::Sequence(SequenceData::List(ListData { data: list_data, type_signature: type_sig, @@ -1015,8 +1167,8 @@ impl Value { } /// # Errors - /// - CheckErrorKind::ValueTooLarge if `buff_data` is too large. - pub fn buff_from(buff_data: Vec) -> Result { + /// - ClarityTypeError::ValueTooLarge if `buff_data` is too large. + pub fn buff_from(buff_data: Vec) -> Result { // check the buffer size BufferLength::try_from(buff_data.len())?; // construct the buffer @@ -1029,13 +1181,13 @@ impl Value { Value::Sequence(SequenceData::Buffer(BuffData { data: vec![byte] })) } - pub fn string_ascii_from_bytes(bytes: Vec) -> Result { + pub fn string_ascii_from_bytes(bytes: Vec) -> Result { // check the string size BufferLength::try_from(bytes.len())?; for b in bytes.iter() { if !b.is_ascii_alphanumeric() && !b.is_ascii_punctuation() && !b.is_ascii_whitespace() { - return Err(CheckErrorKind::InvalidCharactersDetected); + return Err(ClarityTypeError::InvalidAsciiCharacter(*b)); } } // construct the string @@ -1044,26 +1196,28 @@ impl Value { )))) } + // This is parsing escaped clarity literals and is essentially part of the lexer pub fn string_utf8_from_string_utf8_literal( tokenized_str: String, - ) -> Result { + ) -> Result { let wrapped_codepoints_matcher = Regex::new("^\\\\u\\{(?P[[:xdigit:]]+)\\}") - .map_err(|_| VmInternalError::Expect("Bad regex".into()))?; + .map_err(|_| ClarityTypeError::InvariantViolation("Bad regex".into()))?; let mut window = tokenized_str.as_str(); let mut cursor = 0; let mut data: Vec> = vec![]; while !window.is_empty() { if let Some(captures) = wrapped_codepoints_matcher.captures(window) { - let matched = captures - .name("value") - .ok_or_else(|| VmInternalError::Expect("Expected capture".into()))?; + let matched = captures.name("value").ok_or_else(|| { + ClarityTypeError::InvariantViolation("Expected capture".into()) + })?; let scalar_value = window[matched.start()..matched.end()].to_string(); let unicode_char = { // This first InvalidUTF8Encoding is logically unreachable: the escape regex rejects non-hex digits, // so from_str_radix only sees valid hex and never errors here. let u = u32::from_str_radix(&scalar_value, 16) - .map_err(|_| CheckErrorKind::InvalidUTF8Encoding)?; - let c = char::from_u32(u).ok_or_else(|| CheckErrorKind::InvalidUTF8Encoding)?; + .map_err(|_| ClarityTypeError::InvalidUtf8Encoding)?; + let c = + char::from_u32(u).ok_or_else(|| ClarityTypeError::InvalidUtf8Encoding)?; let mut encoded_char: Vec = vec![0; c.len_utf8()]; c.encode_utf8(&mut encoded_char[..]); encoded_char @@ -1087,11 +1241,11 @@ impl Value { )))) } - pub fn string_utf8_from_bytes(bytes: Vec) -> Result { - let validated_utf8_str = match str::from_utf8(&bytes) { - Ok(string) => string, - _ => return Err(CheckErrorKind::InvalidCharactersDetected), - }; + pub fn string_utf8_from_bytes(bytes: Vec) -> Result { + // This used to return InvalidCharactersDetected, but its more accurate to label + // this as InvalidUtf8Encoding + let validated_utf8_str = + str::from_utf8(&bytes).map_err(|_| ClarityTypeError::InvalidUtf8Encoding)?; let data = validated_utf8_str .chars() .map(|char| { @@ -1108,35 +1262,47 @@ impl Value { )))) } - pub fn expect_ascii(self) -> Result { + /// TODO: remove this comment. For code reviewers. Expect ascii is only called in load_cost_functions and immediately + /// is mapped to a CostError + pub fn expect_ascii(self) -> Result { if let Value::Sequence(SequenceData::String(CharType::ASCII(ASCIIData { data }))) = self { - Ok(String::from_utf8(data) - .map_err(|_| VmInternalError::Expect("Non UTF-8 data in string".into()))?) + String::from_utf8(data).map_err(|_| ClarityTypeError::InvalidUtf8Encoding) } else { error!("Value '{self:?}' is not an ASCII string"); - Err(VmInternalError::Expect("Expected ASCII string".into()).into()) + Err(ClarityTypeError::TypeMismatchValue( + Box::new(TypeSignature::STRING_ASCII_MIN), + Box::new(self), + )) } } - pub fn expect_u128(self) -> Result { + pub fn expect_u128(self) -> Result { if let Value::UInt(inner) = self { Ok(inner) } else { error!("Value '{self:?}' is not a u128"); - Err(VmInternalError::Expect("Expected u128".into()).into()) + Err(ClarityTypeError::TypeMismatchValue( + Box::new(TypeSignature::UIntType), + Box::new(self), + )) } } - pub fn expect_i128(self) -> Result { + /// TODO: from this comment. For code reviewers. This is only called in tests and immediately unwrwapped. + /// Therefore, its returned value is not currently important. + pub fn expect_i128(self) -> Result { if let Value::Int(inner) = self { Ok(inner) } else { error!("Value '{self:?}' is not an i128"); - Err(VmInternalError::Expect("Expected i128".into()).into()) + Err(ClarityTypeError::TypeMismatchValue( + Box::new(TypeSignature::IntType), + Box::new(self), + )) } } - pub fn expect_buff(self, sz: usize) -> Result, VmExecutionError> { + pub fn expect_buff(self, sz: usize) -> Result, ClarityTypeError> { if let Value::Sequence(SequenceData::Buffer(buffdata)) = self { if buffdata.data.len() <= sz { Ok(buffdata.data) @@ -1145,24 +1311,32 @@ impl Value { "Value buffer has len {}, expected {sz}", buffdata.data.len() ); - Err(VmInternalError::Expect("Unexpected buff length".into()).into()) + Err(ClarityTypeError::ValueOutOfBounds) } } else { error!("Value '{self:?}' is not a buff"); - Err(VmInternalError::Expect("Expected buff".into()).into()) + Err(ClarityTypeError::TypeMismatchValue( + Box::new(TypeSignature::BUFFER_MIN), + Box::new(self), + )) } } - pub fn expect_list(self) -> Result, VmExecutionError> { + pub fn expect_list(self) -> Result, ClarityTypeError> { if let Value::Sequence(SequenceData::List(listdata)) = self { Ok(listdata.data) } else { error!("Value '{self:?}' is not a list"); - Err(VmInternalError::Expect("Expected list".into()).into()) + Err(ClarityTypeError::TypeMismatchValue( + Box::new(TypeSignature::SequenceType(SequenceSubtype::ListType( + TypeSignature::empty_list(), + ))), + Box::new(self), + )) } } - pub fn expect_buff_padded(self, sz: usize, pad: u8) -> Result, VmExecutionError> { + pub fn expect_buff_padded(self, sz: usize, pad: u8) -> Result, ClarityTypeError> { let mut data = self.expect_buff(sz)?; if sz > data.len() { for _ in data.len()..sz { @@ -1172,25 +1346,34 @@ impl Value { Ok(data) } - pub fn expect_bool(self) -> Result { + /// TODO: remove this comment. For code reviwers: this is only ever called in tests and immediately unwrapped + pub fn expect_bool(self) -> Result { if let Value::Bool(b) = self { Ok(b) } else { error!("Value '{self:?}' is not a bool"); - Err(VmInternalError::Expect("Expected bool".into()).into()) + Err(ClarityTypeError::TypeMismatchValue( + Box::new(TypeSignature::BoolType), + Box::new(self), + )) } } - pub fn expect_tuple(self) -> Result { + pub fn expect_tuple(self) -> Result { if let Value::Tuple(data) = self { Ok(data) } else { error!("Value '{self:?}' is not a tuple"); - Err(VmInternalError::Expect("Expected tuple".into()).into()) + Err(ClarityTypeError::TypeMismatchValue( + // Unfortunately cannot construct an empty Tuple type + // And to add it now would be intrusive. + Box::new(TypeSignature::NoType), + Box::new(self), + )) } } - pub fn expect_optional(self) -> Result, VmExecutionError> { + pub fn expect_optional(self) -> Result, ClarityTypeError> { if let Value::Optional(opt) = self { match opt.data { Some(boxed_value) => Ok(Some(*boxed_value)), @@ -1198,29 +1381,41 @@ impl Value { } } else { error!("Value '{self:?}' is not an optional"); - Err(VmInternalError::Expect("Expected optional".into()).into()) + Err(ClarityTypeError::TypeMismatchValue( + Box::new(TypeSignature::OptionalType(Box::new(TypeSignature::NoType))), + Box::new(self), + )) } } - pub fn expect_principal(self) -> Result { + pub fn expect_principal(self) -> Result { if let Value::Principal(p) = self { Ok(p) } else { error!("Value '{self:?}' is not a principal"); - Err(VmInternalError::Expect("Expected principal".into()).into()) + Err(ClarityTypeError::TypeMismatchValue( + Box::new(TypeSignature::PrincipalType), + Box::new(self), + )) } } - pub fn expect_callable(self) -> Result { + /// TODO: remove this comment. For reviwers: this is only called in tests and immediately unwrapped + pub fn expect_callable(self) -> Result { if let Value::CallableContract(t) = self { Ok(t) } else { error!("Value '{self:?}' is not a callable contract"); - Err(VmInternalError::Expect("Expected callable".into()).into()) + // Unfortunately cannot construct an empty Callable type + // And to add it now would be intrusive. + Err(ClarityTypeError::TypeMismatchValue( + Box::new(TypeSignature::NoType), + Box::new(self), + )) } } - pub fn expect_result(self) -> Result, VmExecutionError> { + pub fn expect_result(self) -> Result, ClarityTypeError> { if let Value::Response(res_data) = self { if res_data.committed { Ok(Ok(*res_data.data)) @@ -1229,62 +1424,89 @@ impl Value { } } else { error!("Value '{self:?}' is not a response"); - Err(VmInternalError::Expect("Expected response".into()).into()) + Err(ClarityTypeError::TypeMismatchValue( + Box::new(TypeSignature::ResponseType(Box::new(( + TypeSignature::NoType, + TypeSignature::NoType, + )))), + Box::new(self), + )) } } - pub fn expect_result_ok(self) -> Result { - if let Value::Response(res_data) = self { + pub fn expect_result_ok(self) -> Result { + if let Value::Response(res_data) = self.clone() { if res_data.committed { Ok(*res_data.data) } else { error!("Value is not a (ok ..)"); - Err(VmInternalError::Expect("Expected ok response".into()).into()) + Err(ClarityTypeError::ResponseTypeMismatch { + expected_ok: true, + data_committed: false, + }) } } else { error!("Value '{self:?}' is not a response"); - Err(VmInternalError::Expect("Expected response".into()).into()) + Err(ClarityTypeError::TypeMismatchValue( + Box::new(TypeSignature::ResponseType(Box::new(( + TypeSignature::NoType, + TypeSignature::NoType, + )))), + Box::new(self), + )) } } - pub fn expect_result_err(self) -> Result { - if let Value::Response(res_data) = self { + /// TODO: remove this comment. For reviewers: only ever called in tests and immediately unwrapped + pub fn expect_result_err(self) -> Result { + if let Value::Response(res_data) = self.clone() { if !res_data.committed { Ok(*res_data.data) } else { error!("Value is not a (err ..)"); - Err(VmInternalError::Expect("Expected err response".into()).into()) + Err(ClarityTypeError::ResponseTypeMismatch { + expected_ok: false, + data_committed: true, + }) } } else { error!("Value '{self:?}' is not a response"); - Err(VmInternalError::Expect("Expected response".into()).into()) + Err(ClarityTypeError::TypeMismatchValue( + Box::new(TypeSignature::ResponseType(Box::new(( + TypeSignature::NoType, + TypeSignature::NoType, + )))), + Box::new(self), + )) } } - pub fn expect_string_ascii(self) -> Result { + pub fn expect_string_ascii(self) -> Result { if let Value::Sequence(SequenceData::String(CharType::ASCII(ASCIIData { data }))) = self { - Ok(String::from_utf8(data) - .map_err(|_| VmInternalError::Expect("Non UTF-8 data in string".into()))?) + String::from_utf8(data).map_err(|_| ClarityTypeError::InvalidUtf8Encoding) } else { error!("Value '{self:?}' is not an ASCII string"); - Err(VmInternalError::Expect("Expected ASCII string".into()).into()) + Err(ClarityTypeError::TypeMismatchValue( + Box::new(TypeSignature::STRING_ASCII_MIN), + Box::new(self), + )) } } } impl BuffData { - pub fn len(&self) -> Result { + pub fn len(&self) -> Result { self.data .len() .try_into() - .map_err(|_| VmInternalError::Expect("Data length should be valid".into()).into()) + .map_err(|_| ClarityTypeError::ValueTooLarge) } pub fn as_slice(&self) -> &[u8] { self.data.as_slice() } - fn append(&mut self, other_seq: &mut BuffData) { + pub fn append(&mut self, other_seq: &mut BuffData) { self.data.append(&mut other_seq.data); } @@ -1294,29 +1516,29 @@ impl BuffData { } impl ListData { - pub fn len(&self) -> Result { + pub fn len(&self) -> Result { self.data .len() .try_into() - .map_err(|_| VmInternalError::Expect("Data length should be valid".into()).into()) + .map_err(|_| ClarityTypeError::ValueTooLarge) } pub fn is_empty(&self) -> bool { self.data.is_empty() } - fn append( + pub fn append( &mut self, epoch: &StacksEpochId, other_seq: ListData, - ) -> Result<(), VmExecutionError> { + ) -> Result<(), ClarityTypeError> { let entry_type_a = self.type_signature.get_list_item_type(); let entry_type_b = other_seq.type_signature.get_list_item_type(); let entry_type = TypeSignature::factor_out_no_type(epoch, entry_type_a, entry_type_b)?; let max_len = self.type_signature.get_max_len() + other_seq.type_signature.get_max_len(); for item in other_seq.data.into_iter() { let (item, _) = Value::sanitize_value(epoch, &entry_type, item) - .ok_or_else(|| CheckErrorKind::ListTypesMustMatch)?; + .ok_or_else(|| ClarityTypeError::ListTypeMismatch)?; self.data.push(item); } @@ -1330,11 +1552,11 @@ impl ASCIIData { self.data.append(&mut other_seq.data); } - pub fn len(&self) -> Result { + pub fn len(&self) -> Result { self.data .len() .try_into() - .map_err(|_| VmInternalError::Expect("Data length should be valid".into()).into()) + .map_err(|_| ClarityTypeError::ValueTooLarge) } } @@ -1343,11 +1565,11 @@ impl UTF8Data { self.data.append(&mut other_seq.data); } - pub fn len(&self) -> Result { + pub fn len(&self) -> Result { self.data .len() .try_into() - .map_err(|_| VmInternalError::Expect("Data length should be valid".into()).into()) + .map_err(|_| ClarityTypeError::ValueTooLarge) } } @@ -1432,7 +1654,7 @@ impl PrincipalData { self.version() < 32 } - pub fn parse(literal: &str) -> Result { + pub fn parse(literal: &str) -> Result { // be permissive about leading single-quote let literal = literal.strip_prefix('\'').unwrap_or(literal); @@ -1445,30 +1667,27 @@ impl PrincipalData { pub fn parse_qualified_contract_principal( literal: &str, - ) -> Result { + ) -> Result { let contract_id = QualifiedContractIdentifier::parse(literal)?; Ok(PrincipalData::Contract(contract_id)) } pub fn parse_standard_principal( literal: &str, - ) -> Result { + ) -> Result { let (version, data) = c32::c32_address_decode(literal).map_err(|x| { - // This `TypeParseFailure` is unreachable in normal Clarity execution. + // This `InvalidPrincipalLiteral` is unreachable in normal Clarity execution. // - All principal literals are validated by the Clarity lexer *before* reaching `parse_standard_principal`. // - The lexer rejects any literal containing characters outside the C32 alphabet. // Therefore, only malformed input fed directly into low-level VM entry points can cause this branch to execute. - RuntimeError::TypeParseFailure(format!("Invalid principal literal: {x}")) + ClarityTypeError::InvalidPrincipalEncoding(x.to_string()) })?; if data.len() != 20 { - return Err(RuntimeError::TypeParseFailure( - "Invalid principal literal: Expected 20 data bytes.".to_string(), - ) - .into()); + return Err(ClarityTypeError::InvalidPrincipalLength(data.len())); } let mut fixed_data = [0; 20]; fixed_data.copy_from_slice(&data[..20]); - Ok(StandardPrincipalData::new(version, fixed_data)?) + StandardPrincipalData::new(version, fixed_data) } } @@ -1501,6 +1720,8 @@ impl fmt::Display for TraitIdentifier { } } +/// TODO: Do we want to make these return errors? I know in theory its infallible, but there is a lot of +/// in theory infallible that return errors instead of straight expects. impl From for StandardPrincipalData { fn from(addr: StacksAddress) -> Self { let (version, bytes) = addr.destruct(); @@ -1518,6 +1739,8 @@ impl From for PrincipalData { } } +/// TODO: Do we want to make these return errors? I know in theory its infallible, but there is a lot of +/// in theory infallible that return errors instead of straight expects. impl From for StacksAddress { fn from(o: StandardPrincipalData) -> StacksAddress { // should be infallible because it's impossible to construct a StandardPrincipalData with @@ -1601,7 +1824,7 @@ impl TupleData { // TODO: add tests from mutation testing results #4833 #[cfg_attr(test, mutants::skip)] - pub fn from_data(data: Vec<(ClarityName, Value)>) -> Result { + pub fn from_data(data: Vec<(ClarityName, Value)>) -> Result { let mut type_map = BTreeMap::new(); let mut data_map = BTreeMap::new(); for (name, value) in data.into_iter() { @@ -1610,7 +1833,7 @@ impl TupleData { match entry { Entry::Vacant(e) => e.insert(type_info), Entry::Occupied(_) => { - return Err(CheckErrorKind::NameAlreadyUsed(name.into()).into()); + return Err(ClarityTypeError::DuplicateTupleField(name.into())); } }; data_map.insert(name, value); @@ -1620,34 +1843,52 @@ impl TupleData { } // TODO: add tests from mutation testing results #4834 + // TODO: remove this comment. This is to help reviewers: from_data_typed is only called in + // serialization.rs where its returned error is immediately ignored. Therefore changes to the error + // types in here are not consensus-breaking #[cfg_attr(test, mutants::skip)] pub fn from_data_typed( epoch: &StacksEpochId, data: Vec<(ClarityName, Value)>, expected: &TupleTypeSignature, - ) -> Result { + ) -> Result { let mut data_map = BTreeMap::new(); + for (name, value) in data.into_iter() { - let expected_type = expected - .field_type(&name) - .ok_or(VmInternalError::FailureConstructingTupleWithType)?; - if !expected_type.admits(epoch, &value)? { - return Err(VmInternalError::FailureConstructingTupleWithType.into()); + // User provided a field not declared in the expected tuple type + let expected_type = expected.field_type(&name).ok_or_else(|| { + ClarityTypeError::NoSuchTupleField(name.to_string(), expected.clone()) + })?; + + // User provided a value that does not match the declared field type + let admits = expected_type.admits(epoch, &value).map_err(|_| { + ClarityTypeError::TypeMismatchValue( + Box::new(expected_type.clone()), + Box::new(value.clone()), + ) + })?; + if !admits { + return Err(ClarityTypeError::TypeMismatchValue( + Box::new(expected_type.clone()), + Box::new(value), + )); } + data_map.insert(name, value); } + Ok(Self::new(expected.clone(), data_map)) } - pub fn get(&self, name: &str) -> Result<&Value, CheckErrorKind> { + pub fn get(&self, name: &str) -> Result<&Value, ClarityTypeError> { self.data_map.get(name).ok_or_else(|| { - CheckErrorKind::NoSuchTupleField(name.to_string(), self.type_signature.clone()) + ClarityTypeError::NoSuchTupleField(name.to_string(), self.type_signature.clone()) }) } - pub fn get_owned(mut self, name: &str) -> Result { + pub fn get_owned(mut self, name: &str) -> Result { self.data_map.remove(name).ok_or_else(|| { - CheckErrorKind::NoSuchTupleField(name.to_string(), self.type_signature.clone()) + ClarityTypeError::NoSuchTupleField(name.to_string(), self.type_signature.clone()) }) } diff --git a/clarity-types/src/types/serialization.rs b/clarity-types/src/types/serialization.rs index 9747aca13d..3252e913d7 100644 --- a/clarity-types/src/types/serialization.rs +++ b/clarity-types/src/types/serialization.rs @@ -23,29 +23,29 @@ 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::errors::IncomparableError; use crate::representations::{ClarityName, ContractName, MAX_STRING_LEN}; use crate::types::{ - BOUND_VALUE_SERIALIZATION_BYTES, BufferLength, CallableData, CharType, MAX_TYPE_DEPTH, - MAX_VALUE_SIZE, OptionalData, PrincipalData, QualifiedContractIdentifier, SequenceData, - SequenceSubtype, StandardPrincipalData, StringSubtype, TupleData, TypeSignature, Value, + BOUND_VALUE_SERIALIZATION_BYTES, BufferLength, CallableData, CharType, ClarityTypeError, + MAX_TYPE_DEPTH, MAX_VALUE_SIZE, OptionalData, PrincipalData, QualifiedContractIdentifier, + SequenceData, SequenceSubtype, StandardPrincipalData, StringSubtype, TupleData, TypeSignature, + Value, }; /// Errors that may occur in serialization or deserialization /// If deserialization failed because the described type is a bad type and -/// a CheckErrorKind is thrown, it gets wrapped in BadTypeError. +/// a ClarityTypeError is thrown, it gets wrapped in BadTypeError. /// Any IOErrrors from the supplied buffer will manifest as IOError variants, /// except for EOF -- if the deserialization code experiences an EOF, it is caught /// and rethrown as DeserializationError #[derive(Debug, PartialEq)] pub enum SerializationError { IOError(IncomparableError), - BadTypeError(CheckErrorKind), - DeserializationError(String), + BadTypeError(ClarityTypeError), DeserializeExpected(Box), LeftoverBytesInDeserialization, - SerializationError(String), + SerializationFailure(String), + DeserializationFailure(String), UnexpectedSerialization, } @@ -79,11 +79,11 @@ impl std::fmt::Display for SerializationError { SerializationError::BadTypeError(e) => { write!(f, "Deserialization error, bad type, caused by: {e}") } - SerializationError::DeserializationError(e) => { - write!(f, "Deserialization error: {e}") + SerializationError::DeserializationFailure(e) => { + write!(f, "Deserialization failure: {e}") } - SerializationError::SerializationError(e) => { - write!(f, "Serialization error: {e}") + SerializationError::SerializationFailure(e) => { + write!(f, "Serialization failure: {e}") } SerializationError::DeserializeExpected(e) => write!( f, @@ -119,12 +119,12 @@ impl From for SerializationError { impl From<&str> for SerializationError { fn from(e: &str) -> Self { - SerializationError::DeserializationError(e.into()) + SerializationError::DeserializationFailure(e.into()) } } -impl From for SerializationError { - fn from(e: CheckErrorKind) -> Self { +impl From for SerializationError { + fn from(e: ClarityTypeError) -> Self { SerializationError::BadTypeError(e) } } @@ -232,7 +232,7 @@ macro_rules! serialize_guarded_string { r.read_exact(&mut len)?; let len = u8::from_be_bytes(len); if len > MAX_STRING_LEN { - return Err(SerializationError::DeserializationError( + return Err(SerializationError::DeserializationFailure( "String too long".to_string(), )); } @@ -395,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 { + pub fn max_serialized_size(&self) -> Result { let type_prefix_size = 1; let max_output_size = match self { @@ -406,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(StaticCheckErrorKind::CouldNotDetermineSerializationType); + return Err(ClarityTypeError::CouldNotDetermineSerializationType); } TypeSignature::IntType => 16, TypeSignature::UIntType => 16, @@ -418,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(|| StaticCheckErrorKind::ValueTooLarge)? + .ok_or_else(|| ClarityTypeError::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(|| StaticCheckErrorKind::ValueTooLarge)? + .ok_or_else(|| ClarityTypeError::ValueTooLarge)? } TypeSignature::SequenceType(SequenceSubtype::StringType(StringSubtype::ASCII( length, @@ -435,7 +435,7 @@ impl TypeSignature { // ascii is 1-byte per character u32::from(length) .checked_add(str_length_encode) - .ok_or_else(|| StaticCheckErrorKind::ValueTooLarge)? + .ok_or_else(|| ClarityTypeError::ValueTooLarge)? } TypeSignature::SequenceType(SequenceSubtype::StringType(StringSubtype::UTF8( length, @@ -446,7 +446,7 @@ impl TypeSignature { u32::from(length) .checked_mul(4) .and_then(|x| x.checked_add(str_length_encode)) - .ok_or_else(|| StaticCheckErrorKind::ValueTooLarge)? + .ok_or_else(|| ClarityTypeError::ValueTooLarge)? } TypeSignature::PrincipalType | TypeSignature::CallableType(_) @@ -469,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(|| StaticCheckErrorKind::ValueTooLarge)?; + .ok_or_else(|| ClarityTypeError::ValueTooLarge)?; } total_size } @@ -478,7 +478,7 @@ impl TypeSignature { Ok(size) => size, // if NoType, then this is just serializing a none // value, which is only the type prefix - Err(StaticCheckErrorKind::CouldNotDetermineSerializationType) => 0, + Err(ClarityTypeError::CouldNotDetermineSerializationType) => 0, Err(e) => return Err(e), } } @@ -486,17 +486,17 @@ impl TypeSignature { 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(StaticCheckErrorKind::CouldNotDetermineSerializationType) => (0, true), + Err(ClarityTypeError::CouldNotDetermineSerializationType) => (0, true), Err(e) => return Err(e), }; let err_type_max_size = match err_type.max_serialized_size() { Ok(size) => size, - Err(StaticCheckErrorKind::CouldNotDetermineSerializationType) => { + Err(ClarityTypeError::CouldNotDetermineSerializationType) => { if no_ok_type { // if both the ok type and the error type are NoType, - // throw a StaticCheckErrorKind. This should not be possible, but the check + // throw a ClarityTypeError. This should not be possible, but the check // is done out of caution. - return Err(StaticCheckErrorKind::CouldNotDetermineSerializationType); + return Err(ClarityTypeError::CouldNotDetermineSerializationType); } else { 0 } @@ -506,13 +506,13 @@ impl TypeSignature { cmp::max(ok_type_max_size, err_type_max_size) } TypeSignature::ListUnionType(_) => { - return Err(StaticCheckErrorKind::CouldNotDetermineSerializationType); + return Err(ClarityTypeError::CouldNotDetermineSerializationType); } }; max_output_size .checked_add(type_prefix_size) - .ok_or_else(|| StaticCheckErrorKind::ValueTooLarge) + .ok_or_else(|| ClarityTypeError::ValueTooLarge) } } @@ -584,7 +584,7 @@ impl Value { UNSANITIZED_DEPTH_CHECK }; if stack.len() > depth_check { - return Err(CheckErrorKind::TypeSignatureTooDeep.into()); + return Err(ClarityTypeError::TypeSignatureTooDeep.into()); } #[allow(clippy::expect_used)] @@ -613,8 +613,7 @@ 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)) - .map_err(CheckErrorKind::from)?; + let buffer_len = BufferLength::try_from(u32::from_be_bytes(buffer_len))?; if let Some(x) = &expected_type { let passed_test = match x { TypeSignature::SequenceType(SequenceSubtype::BufferType( @@ -768,7 +767,7 @@ impl Value { let expected_len = u64::from(len); if len > MAX_VALUE_SIZE { - return Err(SerializationError::DeserializationError( + return Err(SerializationError::DeserializationFailure( "Illegal tuple type".to_string(), )); } @@ -845,8 +844,7 @@ 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)) - .map_err(CheckErrorKind::from)?; + let buffer_len = BufferLength::try_from(u32::from_be_bytes(buffer_len))?; if let Some(x) = &expected_type { let passed_test = match x { @@ -871,8 +869,7 @@ 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)) - .map_err(CheckErrorKind::from)?; + let total_len = BufferLength::try_from(u32::from_be_bytes(total_len))?; let mut data: Vec = vec![0; u32::from(total_len) as usize]; @@ -1034,7 +1031,7 @@ impl Value { } } - Err(SerializationError::DeserializationError( + Err(SerializationError::DeserializationFailure( "Invalid data: stack ran out before finishing parsing".into(), )) } @@ -1069,7 +1066,7 @@ impl Value { Sequence(List(data)) => { let len_bytes = data .len() - .map_err(|e| SerializationError::SerializationError(e.to_string()))? + .map_err(|e| SerializationError::SerializationFailure(e.to_string()))? .to_be_bytes(); w.write_all(&len_bytes)?; for item in data.data.iter() { @@ -1080,7 +1077,7 @@ impl Value { let len_bytes = u32::from( value .len() - .map_err(|e| SerializationError::SerializationError(e.to_string()))?, + .map_err(|e| SerializationError::SerializationFailure(e.to_string()))?, ) .to_be_bytes(); w.write_all(&len_bytes)?; @@ -1097,7 +1094,7 @@ impl Value { let len_bytes = u32::from( value .len() - .map_err(|e| SerializationError::SerializationError(e.to_string()))?, + .map_err(|e| SerializationError::SerializationFailure(e.to_string()))?, ) .to_be_bytes(); w.write_all(&len_bytes)?; @@ -1105,7 +1102,7 @@ impl Value { } Tuple(data) => { let len_bytes = u32::try_from(data.data_map.len()) - .map_err(|e| SerializationError::SerializationError(e.to_string()))? + .map_err(|e| SerializationError::SerializationFailure(e.to_string()))? .to_be_bytes(); w.write_all(&len_bytes)?; for (key, value) in data.data_map.iter() { @@ -1186,7 +1183,7 @@ impl Value { pub fn serialized_size(&self) -> Result { let mut counter = WriteCounter { count: 0 }; self.serialize_write(&mut counter).map_err(|_| { - SerializationError::DeserializationError( + SerializationError::DeserializationFailure( "Error: Failed to count serialization length of Clarity value".into(), ) })?; @@ -1218,15 +1215,14 @@ impl Write for WriteCounter { } impl Value { - pub fn serialize_to_vec(&self) -> Result, VmInternalError> { + pub fn serialize_to_vec(&self) -> Result, SerializationError> { let mut byte_serialization = Vec::new(); - self.serialize_write(&mut byte_serialization) - .map_err(|_| VmInternalError::Expect("IOError filling byte buffer.".into()))?; + self.serialize_write(&mut byte_serialization)?; Ok(byte_serialization) } /// This does *not* perform any data sanitization - pub fn serialize_to_hex(&self) -> Result { + pub fn serialize_to_hex(&self) -> Result { let byte_serialization = self.serialize_to_vec()?; Ok(to_hex(byte_serialization.as_slice())) } diff --git a/clarity-types/src/types/signatures.rs b/clarity-types/src/types/signatures.rs index 13ce2becc0..0680fd5171 100644 --- a/clarity-types/src/types/signatures.rs +++ b/clarity-types/src/types/signatures.rs @@ -22,11 +22,9 @@ use std::{cmp, fmt}; use serde::{Deserialize, Serialize}; use stacks_common::types::StacksEpochId; -use crate::errors::CheckErrorKind; -use crate::errors::analysis::{CommonCheckErrorKind, StaticCheckErrorKind}; use crate::representations::{CONTRACT_MAX_NAME_LENGTH, ClarityName, ContractName}; use crate::types::{ - CharType, MAX_TO_ASCII_BUFFER_LEN, MAX_TO_ASCII_RESULT_LEN, MAX_TYPE_DEPTH, + CharType, ClarityTypeError, MAX_TO_ASCII_BUFFER_LEN, MAX_TO_ASCII_RESULT_LEN, MAX_TYPE_DEPTH, MAX_UTF8_VALUE_SIZE, MAX_VALUE_SIZE, PrincipalData, QualifiedContractIdentifier, SequenceData, SequencedValue, StandardPrincipalData, TraitIdentifier, Value, WRAPPER_VALUE_SIZE, }; @@ -122,11 +120,11 @@ impl BufferLength { /// /// This function is primarily intended for internal runtime use, /// and serves as the central place for all integer validation logic. - fn try_from_i128(data: i128) -> Result { + fn try_from_i128(data: i128) -> Result { if data > (MAX_VALUE_SIZE as i128) { - Err(CommonCheckErrorKind::ValueTooLarge) + Err(ClarityTypeError::ValueTooLarge) } else if data < 0 { - Err(CommonCheckErrorKind::ValueOutOfBounds) + Err(ClarityTypeError::ValueOutOfBounds) } else { Ok(BufferLength(data as u32)) } @@ -162,22 +160,22 @@ impl From for u32 { } impl TryFrom for BufferLength { - type Error = CommonCheckErrorKind; - fn try_from(data: u32) -> Result { + type Error = ClarityTypeError; + fn try_from(data: u32) -> Result { Self::try_from(data as i128) } } impl TryFrom for BufferLength { - type Error = CommonCheckErrorKind; - fn try_from(data: usize) -> Result { + type Error = ClarityTypeError; + fn try_from(data: usize) -> Result { Self::try_from(data as i128) } } impl TryFrom for BufferLength { - type Error = CommonCheckErrorKind; - fn try_from(data: i128) -> Result { + type Error = ClarityTypeError; + fn try_from(data: i128) -> Result { Self::try_from_i128(data) } } @@ -203,11 +201,11 @@ impl StringUTF8Length { /// /// This function is primarily intended for internal runtime use, /// and serves as the central place for all integer validation logic. - fn try_from_i128(value: i128) -> Result { + fn try_from_i128(value: i128) -> Result { if value > MAX_UTF8_VALUE_SIZE as i128 { - Err(CommonCheckErrorKind::ValueTooLarge) + Err(ClarityTypeError::ValueTooLarge) } else if value < 0 { - Err(CommonCheckErrorKind::ValueOutOfBounds) + Err(ClarityTypeError::ValueOutOfBounds) } else { Ok(StringUTF8Length(value as u32)) } @@ -243,22 +241,22 @@ impl From for u32 { } impl TryFrom for StringUTF8Length { - type Error = CommonCheckErrorKind; - fn try_from(data: u32) -> Result { + type Error = ClarityTypeError; + fn try_from(data: u32) -> Result { Self::try_from(data as i128) } } impl TryFrom for StringUTF8Length { - type Error = CommonCheckErrorKind; - fn try_from(data: usize) -> Result { + type Error = ClarityTypeError; + fn try_from(data: usize) -> Result { Self::try_from(data as i128) } } impl TryFrom for StringUTF8Length { - type Error = CommonCheckErrorKind; - fn try_from(data: i128) -> Result { + type Error = ClarityTypeError; + fn try_from(data: i128) -> Result { Self::try_from_i128(data) } } @@ -359,10 +357,10 @@ impl ListTypeData { pub fn new_list( entry_type: TypeSignature, max_len: u32, - ) -> Result { + ) -> Result { let would_be_depth = 1 + entry_type.depth(); if would_be_depth > MAX_TYPE_DEPTH { - return Err(CommonCheckErrorKind::TypeSignatureTooDeep); + return Err(ClarityTypeError::TypeSignatureTooDeep); } let list_data = ListTypeData { @@ -371,9 +369,9 @@ impl ListTypeData { }; let would_be_size = list_data .inner_size()? - .ok_or_else(|| CommonCheckErrorKind::ValueTooLarge)?; + .ok_or_else(|| ClarityTypeError::ValueTooLarge)?; if would_be_size > MAX_VALUE_SIZE { - Err(CommonCheckErrorKind::ValueTooLarge) + Err(ClarityTypeError::ValueTooLarge) } else { Ok(list_data) } @@ -401,13 +399,13 @@ impl ListTypeData { } impl TypeSignature { - pub fn new_option(inner_type: TypeSignature) -> Result { + pub fn new_option(inner_type: TypeSignature) -> Result { let new_size = WRAPPER_VALUE_SIZE + inner_type.size()?; let new_depth = 1 + inner_type.depth(); if new_size > MAX_VALUE_SIZE { - Err(CommonCheckErrorKind::ValueTooLarge) + Err(ClarityTypeError::ValueTooLarge) } else if new_depth > MAX_TYPE_DEPTH { - Err(CommonCheckErrorKind::TypeSignatureTooDeep) + Err(ClarityTypeError::TypeSignatureTooDeep) } else { Ok(OptionalType(Box::new(inner_type))) } @@ -416,14 +414,14 @@ impl TypeSignature { pub fn new_response( ok_type: TypeSignature, err_type: TypeSignature, - ) -> Result { + ) -> Result { let new_size = WRAPPER_VALUE_SIZE + cmp::max(ok_type.size()?, err_type.size()?); let new_depth = 1 + cmp::max(ok_type.depth(), err_type.depth()); if new_size > MAX_VALUE_SIZE { - Err(CommonCheckErrorKind::ValueTooLarge) + Err(ClarityTypeError::ValueTooLarge) } else if new_depth > MAX_TYPE_DEPTH { - Err(CommonCheckErrorKind::TypeSignatureTooDeep) + Err(ClarityTypeError::TypeSignatureTooDeep) } else { Ok(ResponseType(Box::new((ok_type, err_type)))) } @@ -437,7 +435,7 @@ impl TypeSignature { &TypeSignature::NoType == self } - pub fn admits(&self, epoch: &StacksEpochId, x: &Value) -> Result { + pub fn admits(&self, epoch: &StacksEpochId, x: &Value) -> Result { let x_type = TypeSignature::type_of(x)?; self.admits_type(epoch, &x_type) } @@ -446,7 +444,7 @@ impl TypeSignature { &self, epoch: &StacksEpochId, other: &TypeSignature, - ) -> Result { + ) -> Result { match epoch { StacksEpochId::Epoch20 | StacksEpochId::Epoch2_05 => self.admits_type_v2_0(other), StacksEpochId::Epoch21 @@ -458,13 +456,11 @@ impl TypeSignature { | StacksEpochId::Epoch31 | StacksEpochId::Epoch32 | StacksEpochId::Epoch33 => self.admits_type_v2_1(other), - StacksEpochId::Epoch10 => Err(CommonCheckErrorKind::Expects( - "epoch 1.0 not supported".into(), - )), + StacksEpochId::Epoch10 => Err(ClarityTypeError::UnsupportedEpoch(*epoch)), } } - pub fn admits_type_v2_0(&self, other: &TypeSignature) -> Result { + pub fn admits_type_v2_0(&self, other: &TypeSignature) -> Result { match self { SequenceType(SequenceSubtype::ListType(my_list_type)) => { if let SequenceType(SequenceSubtype::ListType(other_list_type)) = other { @@ -546,18 +542,16 @@ impl TypeSignature { Ok(false) } } - NoType => Err(CommonCheckErrorKind::CouldNotDetermineType), - CallableType(_) => Err(CommonCheckErrorKind::Expects( - "CallableType should not be used in epoch v2.0".into(), - )), - ListUnionType(_) => Err(CommonCheckErrorKind::Expects( - "ListUnionType should not be used in epoch v2.0".into(), + NoType => Err(ClarityTypeError::CouldNotDetermineType), + CallableType(_) | ListUnionType(_) => Err(ClarityTypeError::UnsupportedTypeInEpoch( + Box::new(self.clone()), + StacksEpochId::Epoch20, )), _ => Ok(other == self), } } - fn admits_type_v2_1(&self, other: &TypeSignature) -> Result { + fn admits_type_v2_1(&self, other: &TypeSignature) -> Result { let other = match other.concretize() { Ok(other) => other, Err(_) => { @@ -646,7 +640,7 @@ impl TypeSignature { Ok(false) } } - NoType => Err(CommonCheckErrorKind::CouldNotDetermineType), + NoType => Err(ClarityTypeError::CouldNotDetermineType), _ => Ok(&other == self), } } @@ -703,7 +697,7 @@ impl TypeSignature { /// Concretize the type. The input to this method may include /// `ListUnionType` and the `CallableType` variant for a `principal. /// This method turns these "temporary" types into actual types. - pub fn concretize(&self) -> Result { + pub fn concretize(&self) -> Result { match self { ListUnionType(types) => { let mut is_trait = None; @@ -712,7 +706,7 @@ impl TypeSignature { match partial { CallableSubtype::Principal(_) => { if is_trait.is_some() { - return Err(StaticCheckErrorKind::TypeError( + return Err(ClarityTypeError::TypeMismatch( Box::new(TypeSignature::CallableType(partial.clone())), Box::new(TypeSignature::PrincipalType), )); @@ -722,7 +716,7 @@ impl TypeSignature { } CallableSubtype::Trait(t) => { if is_principal { - return Err(StaticCheckErrorKind::TypeError( + return Err(ClarityTypeError::TypeMismatch( Box::new(TypeSignature::PrincipalType), Box::new(TypeSignature::CallableType(partial.clone())), )); @@ -745,12 +739,12 @@ impl TypeSignature { } impl TryFrom> for TupleTypeSignature { - type Error = CommonCheckErrorKind; + type Error = ClarityTypeError; fn try_from( type_data: Vec<(ClarityName, TypeSignature)>, - ) -> Result { + ) -> Result { if type_data.is_empty() { - return Err(CommonCheckErrorKind::EmptyTuplesNotAllowed); + return Err(ClarityTypeError::EmptyTuplesNotAllowed); } let mut type_map = BTreeMap::new(); @@ -758,7 +752,7 @@ impl TryFrom> for TupleTypeSignature { if let Entry::Vacant(e) = type_map.entry(name.clone()) { e.insert(type_info); } else { - return Err(CommonCheckErrorKind::NameAlreadyUsed(name.into())); + return Err(ClarityTypeError::DuplicateTupleField(name.into())); } } TupleTypeSignature::try_from(type_map) @@ -766,25 +760,25 @@ impl TryFrom> for TupleTypeSignature { } impl TryFrom> for TupleTypeSignature { - type Error = CommonCheckErrorKind; + type Error = ClarityTypeError; fn try_from( type_map: BTreeMap, - ) -> Result { + ) -> Result { if type_map.is_empty() { - return Err(CommonCheckErrorKind::EmptyTuplesNotAllowed); + return Err(ClarityTypeError::EmptyTuplesNotAllowed); } for child_sig in type_map.values() { if (1 + child_sig.depth()) > MAX_TYPE_DEPTH { - return Err(CommonCheckErrorKind::TypeSignatureTooDeep); + return Err(ClarityTypeError::TypeSignatureTooDeep); } } let type_map = Arc::new(type_map.into_iter().collect()); let result = TupleTypeSignature { type_map }; let would_be_size = result .inner_size()? - .ok_or_else(|| CommonCheckErrorKind::ValueTooLarge)?; + .ok_or_else(|| ClarityTypeError::ValueTooLarge)?; if would_be_size > MAX_VALUE_SIZE { - Err(CommonCheckErrorKind::ValueTooLarge) + Err(ClarityTypeError::ValueTooLarge) } else { Ok(result) } @@ -814,7 +808,7 @@ impl TupleTypeSignature { &self, epoch: &StacksEpochId, other: &TupleTypeSignature, - ) -> Result { + ) -> Result { if self.type_map.len() != other.type_map.len() { return Ok(false); } @@ -935,18 +929,18 @@ impl TypeSignature { /// Creates a string ASCII type with the specified length. /// Returns an error if the provided length is invalid. - pub fn new_ascii_type(len: i128) -> Result { + pub fn new_ascii_type(len: i128) -> Result { Ok(SequenceType(SequenceSubtype::StringType( StringSubtype::ASCII(BufferLength::try_from_i128(len)?), ))) } /// If one of the types is a NoType, return Ok(the other type), otherwise return least_supertype(a, b) - pub(crate) fn factor_out_no_type( + pub fn factor_out_no_type( epoch: &StacksEpochId, a: &TypeSignature, b: &TypeSignature, - ) -> Result { + ) -> Result { if a.is_no_type() { Ok(b.clone()) } else if b.is_no_type() { @@ -997,7 +991,7 @@ impl TypeSignature { epoch: &StacksEpochId, a: &TypeSignature, b: &TypeSignature, - ) -> Result { + ) -> Result { match epoch { StacksEpochId::Epoch20 | StacksEpochId::Epoch2_05 => Self::least_supertype_v2_0(a, b), StacksEpochId::Epoch21 @@ -1009,16 +1003,14 @@ impl TypeSignature { | StacksEpochId::Epoch31 | StacksEpochId::Epoch32 | StacksEpochId::Epoch33 => Self::least_supertype_v2_1(a, b), - StacksEpochId::Epoch10 => Err(CommonCheckErrorKind::Expects( - "epoch 1.0 not supported".into(), - )), + StacksEpochId::Epoch10 => Err(ClarityTypeError::UnsupportedEpoch(*epoch)), } } - fn least_supertype_v2_0( + pub fn least_supertype_v2_0( a: &TypeSignature, b: &TypeSignature, - ) -> Result { + ) -> Result { match (a, b) { ( TupleType(TupleTypeSignature { type_map: types_a }), @@ -1026,7 +1018,7 @@ impl TypeSignature { ) => { let mut type_map_out = BTreeMap::new(); for (name, entry_a) in types_a.iter() { - let entry_b = types_b.get(name).ok_or(CommonCheckErrorKind::TypeError( + let entry_b = types_b.get(name).ok_or(ClarityTypeError::TypeMismatch( Box::new(a.clone()), Box::new(b.clone()), ))?; @@ -1035,7 +1027,7 @@ impl TypeSignature { } Ok(TupleTypeSignature::try_from(type_map_out) .map(|x| x.into()) - .map_err(|_| CommonCheckErrorKind::SupertypeTooLarge)?) + .map_err(|_| ClarityTypeError::SupertypeTooLarge)?) } ( SequenceType(SequenceSubtype::ListType(ListTypeData { @@ -1056,7 +1048,7 @@ impl TypeSignature { }; let max_len = cmp::max(len_a, len_b); Ok(Self::list_of(entry_type, *max_len) - .map_err(|_| CommonCheckErrorKind::SupertypeTooLarge)?) + .map_err(|_| ClarityTypeError::SupertypeTooLarge)?) } (ResponseType(resp_a), ResponseType(resp_b)) => { let ok_type = @@ -1115,7 +1107,7 @@ impl TypeSignature { if x == y { Ok(x.clone()) } else { - Err(CommonCheckErrorKind::TypeError( + Err(ClarityTypeError::TypeMismatch( Box::new(a.clone()), Box::new(b.clone()), )) @@ -1124,10 +1116,10 @@ impl TypeSignature { } } - pub(crate) fn least_supertype_v2_1( + pub fn least_supertype_v2_1( a: &TypeSignature, b: &TypeSignature, - ) -> Result { + ) -> Result { match (a, b) { ( TupleType(TupleTypeSignature { type_map: types_a }), @@ -1135,7 +1127,7 @@ impl TypeSignature { ) => { let mut type_map_out = BTreeMap::new(); for (name, entry_a) in types_a.iter() { - let entry_b = types_b.get(name).ok_or(CommonCheckErrorKind::TypeError( + let entry_b = types_b.get(name).ok_or(ClarityTypeError::TypeMismatch( Box::new(a.clone()), Box::new(b.clone()), ))?; @@ -1144,7 +1136,7 @@ impl TypeSignature { } Ok(TupleTypeSignature::try_from(type_map_out) .map(|x| x.into()) - .map_err(|_| CommonCheckErrorKind::SupertypeTooLarge)?) + .map_err(|_| ClarityTypeError::SupertypeTooLarge)?) } ( SequenceType(SequenceSubtype::ListType(ListTypeData { @@ -1165,7 +1157,7 @@ impl TypeSignature { }; let max_len = cmp::max(len_a, len_b); Ok(Self::list_of(entry_type, *max_len) - .map_err(|_| CommonCheckErrorKind::SupertypeTooLarge)?) + .map_err(|_| ClarityTypeError::SupertypeTooLarge)?) } (ResponseType(resp_a), ResponseType(resp_b)) => { let ok_type = @@ -1246,7 +1238,7 @@ impl TypeSignature { if all_principals { Ok(PrincipalType) } else { - Err(CommonCheckErrorKind::TypeError( + Err(ClarityTypeError::TypeMismatch( Box::new(a.clone()), Box::new(b.clone()), )) @@ -1259,7 +1251,7 @@ impl TypeSignature { if x == y { Ok(x.clone()) } else { - Err(CommonCheckErrorKind::TypeError( + Err(ClarityTypeError::TypeMismatch( Box::new(a.clone()), Box::new(b.clone()), )) @@ -1271,7 +1263,7 @@ impl TypeSignature { pub fn list_of( item_type: TypeSignature, max_len: u32, - ) -> Result { + ) -> Result { ListTypeData::new_list(item_type, max_len).map(|x| x.into()) } @@ -1282,7 +1274,7 @@ impl TypeSignature { } } - pub fn type_of(x: &Value) -> Result { + pub fn type_of(x: &Value) -> Result { let out = match x { Value::Principal(_) => PrincipalType, Value::Int(_v) => IntType, @@ -1311,31 +1303,29 @@ impl TypeSignature { Ok(out) } - pub fn literal_type_of(x: &Value) -> Result { + pub fn literal_type_of(x: &Value) -> Result { match x { Value::Principal(PrincipalData::Contract(contract_id)) => Ok(CallableType( CallableSubtype::Principal(contract_id.clone()), )), - _ => Self::type_of(x).map_err(StaticCheckErrorKind::from), + _ => Self::type_of(x), } } // Checks if resulting type signature is of valid size. - pub fn construct_parent_list_type(args: &[Value]) -> Result { - let children_types: Result, _> = args.iter().map(TypeSignature::type_of).collect(); - Ok(TypeSignature::parent_list_type(&children_types?)?) + pub fn construct_parent_list_type(args: &[Value]) -> Result { + let children_types: Result, ClarityTypeError> = + args.iter().map(TypeSignature::type_of).collect(); + TypeSignature::parent_list_type(&children_types?) } - pub fn parent_list_type( - children: &[TypeSignature], - ) -> Result { + pub fn parent_list_type(children: &[TypeSignature]) -> Result { if let Some((first, rest)) = children.split_first() { let mut current_entry_type = first.clone(); for next_entry in rest.iter() { current_entry_type = Self::least_supertype_v2_1(¤t_entry_type, next_entry)?; } - let len = - u32::try_from(children.len()).map_err(|_| CommonCheckErrorKind::ValueTooLarge)?; + let len = u32::try_from(children.len()).map_err(|_| ClarityTypeError::ValueTooLarge)?; ListTypeData::new_list(current_entry_type, len) } else { Ok(TypeSignature::empty_list()) @@ -1375,16 +1365,12 @@ impl TypeSignature { } } - pub fn size(&self) -> Result { - self.inner_size()?.ok_or_else(|| { - CommonCheckErrorKind::Expects( - "FAIL: .size() overflowed on too large of a type. construction should have failed!" - .into(), - ) - }) + pub fn size(&self) -> Result { + self.inner_size()? + .ok_or_else(|| ClarityTypeError::ValueTooLarge) } - fn inner_size(&self) -> Result, CommonCheckErrorKind> { + fn inner_size(&self) -> Result, ClarityTypeError> { let out = match self { // NoType's may be asked for their size at runtime -- // legal constructions like `(ok 1)` have NoType parts (if they have unknown error variant types). @@ -1417,9 +1403,9 @@ impl TypeSignature { Ok(out) } - pub fn type_size(&self) -> Result { + pub fn type_size(&self) -> Result { self.inner_type_size() - .ok_or_else(|| CommonCheckErrorKind::ValueTooLarge) + .ok_or_else(|| ClarityTypeError::ValueTooLarge) } /// Returns the size of the _type signature_ @@ -1449,7 +1435,7 @@ impl TypeSignature { impl ListTypeData { /// List Size: type_signature_size + max_len * entry_type.size() - fn inner_size(&self) -> Result, CommonCheckErrorKind> { + fn inner_size(&self) -> Result, ClarityTypeError> { let total_size = self .entry_type .size()? @@ -1498,10 +1484,9 @@ impl TupleTypeSignature { } } - pub fn size(&self) -> Result { - self.inner_size()?.ok_or_else(|| { - CheckErrorKind::Expects("size() overflowed on a constructed type.".into()) - }) + pub fn size(&self) -> Result { + self.inner_size()? + .ok_or_else(|| ClarityTypeError::ValueTooLarge) } fn max_depth(&self) -> u8 { @@ -1515,7 +1500,7 @@ impl TupleTypeSignature { /// Tuple Size: /// size( btreemap ) + type_size /// size( btreemap ) = 2*map.len() + sum(names) + sum(values) - fn inner_size(&self) -> Result, CommonCheckErrorKind> { + fn inner_size(&self) -> Result, ClarityTypeError> { let Some(mut total_size) = u32::try_from(self.type_map.len()) .ok() .and_then(|x| x.checked_mul(2)) diff --git a/clarity/fuzz/fuzz_targets/fuzz_sanitize.rs b/clarity/fuzz/fuzz_targets/fuzz_sanitize.rs index 298151e096..aa5b116eb5 100644 --- a/clarity/fuzz/fuzz_targets/fuzz_sanitize.rs +++ b/clarity/fuzz/fuzz_targets/fuzz_sanitize.rs @@ -16,6 +16,7 @@ #![no_main] use arbitrary::Arbitrary; +use clarity::vm::errors::ClarityTypeError; use clarity::vm::analysis::CheckErrorKind; use clarity::vm::representations::ContractName; use clarity::vm::types::serialization::SerializationError; @@ -277,7 +278,7 @@ fn fuzz_sanitize(input: ClarityValue) { deserialize_unsanitized.unwrap_err(); } else { let deser_value = match deserialize_unsanitized { - Err(SerializationError::BadTypeError(CheckErrorKind::TypeSignatureTooDeep)) => { + Err(SerializationError::BadTypeError(ClarityTypeError::TypeSignatureTooDeep)) => { // pre-2.4, deserializer could error on types deeper than a deserialization limit of 16. // with sanitization enabled (a 2.4-gated feature), these serializations are readable. ClarityValue::deserialize_read( @@ -298,7 +299,7 @@ fn fuzz_sanitize(input: ClarityValue) { true, ) { Ok(x) => x, - Err(SerializationError::BadTypeError(CheckErrorKind::TypeSignatureTooDeep)) => { + Err(SerializationError::BadTypeError(ClarityTypeError::TypeSignatureTooDeep)) => { assert!(!did_strict_admit, "Unsanitized inputs may fail to deserialize, but they must have needed sanitization"); // check that the sanitized value *is* readable let serialized = sanitized_value @@ -309,7 +310,7 @@ fn fuzz_sanitize(input: ClarityValue) { Some(&computed_type), false, ) { - Err(SerializationError::BadTypeError(CheckErrorKind::TypeSignatureTooDeep)) => { + Err(SerializationError::BadTypeError(ClarityTypeError::TypeSignatureTooDeep)) => { // pre-2.4, deserializer could error on legal types deeper than a deserialization limit of 16. // with sanitization enabled (a 2.4-gated feature), these serializations are readable. ClarityValue::deserialize_read( diff --git a/clarity/src/vm/analysis/analysis_db.rs b/clarity/src/vm/analysis/analysis_db.rs index ff7197249f..623fee4cb6 100644 --- a/clarity/src/vm/analysis/analysis_db.rs +++ b/clarity/src/vm/analysis/analysis_db.rs @@ -51,11 +51,11 @@ impl<'a> AnalysisDatabase<'a> { self.begin(); let result = f(self).or_else(|e| { self.roll_back() - .map_err(|e| StaticCheckErrorKind::Expects(format!("{e:?}")))?; + .map_err(|e| StaticCheckErrorKind::ExpectsRejectable(format!("{e:?}")))?; Err(e) })?; self.commit() - .map_err(|e| StaticCheckErrorKind::Expects(format!("{e:?}")))?; + .map_err(|e| StaticCheckErrorKind::ExpectsRejectable(format!("{e:?}")))?; Ok(result) } @@ -66,13 +66,13 @@ impl<'a> AnalysisDatabase<'a> { pub fn commit(&mut self) -> Result<(), StaticCheckError> { self.store .commit() - .map_err(|e| StaticCheckErrorKind::Expects(format!("{e:?}")).into()) + .map_err(|e| StaticCheckErrorKind::ExpectsRejectable(format!("{e:?}")).into()) } pub fn roll_back(&mut self) -> Result<(), StaticCheckError> { self.store .rollback() - .map_err(|e| StaticCheckErrorKind::Expects(format!("{e:?}")).into()) + .map_err(|e| StaticCheckErrorKind::ExpectsRejectable(format!("{e:?}")).into()) } pub fn storage_key() -> &'static str { @@ -108,7 +108,8 @@ impl<'a> AnalysisDatabase<'a> { .flatten() .map(|x| { ContractAnalysis::deserialize(&x).map_err(|_| { - StaticCheckErrorKind::Expects("Bad data deserialized from DB".into()).into() + StaticCheckErrorKind::ExpectsRejectable("Bad data deserialized from DB".into()) + .into() }) }) .transpose() @@ -128,7 +129,7 @@ impl<'a> AnalysisDatabase<'a> { .flatten() .map(|x| { ContractAnalysis::deserialize(&x).map_err(|_| { - StaticCheckErrorKind::Expects("Bad data deserialized from DB".into()) + StaticCheckErrorKind::ExpectsRejectable("Bad data deserialized from DB".into()) }) }) .transpose()? @@ -153,7 +154,7 @@ impl<'a> AnalysisDatabase<'a> { self.store .insert_metadata(contract_identifier, key, &contract.serialize()) - .map_err(|e| StaticCheckErrorKind::Expects(format!("{e:?}")))?; + .map_err(|e| StaticCheckErrorKind::ExpectsRejectable(format!("{e:?}")))?; Ok(()) } diff --git a/clarity/src/vm/analysis/contract_interface_builder/mod.rs b/clarity/src/vm/analysis/contract_interface_builder/mod.rs index 54f1a161cf..ed64dc7ef5 100644 --- a/clarity/src/vm/analysis/contract_interface_builder/mod.rs +++ b/clarity/src/vm/analysis/contract_interface_builder/mod.rs @@ -278,7 +278,7 @@ impl ContractInterfaceFunction { FunctionType::Fixed(FixedFunction { returns, .. }) => { ContractInterfaceAtomType::from_type_signature(returns) } - _ => return Err(StaticCheckErrorKind::Expects( + _ => return Err(StaticCheckErrorKind::ExpectsRejectable( "Contract functions should only have fixed function return types!" .into(), ) @@ -290,7 +290,7 @@ impl ContractInterfaceFunction { ContractInterfaceFunctionArg::from_function_args(args) } _ => { - return Err(StaticCheckErrorKind::Expects( + return Err(StaticCheckErrorKind::ExpectsRejectable( "Contract functions should only have fixed function arguments!" .into(), ) @@ -402,7 +402,8 @@ impl ContractInterface { pub fn serialize(&self) -> Result { serde_json::to_string(self).map_err(|_| { - StaticCheckErrorKind::Expects("Failed to serialize contract interface".into()).into() + StaticCheckErrorKind::ExpectsRejectable("Failed to serialize contract interface".into()) + .into() }) } } diff --git a/clarity/src/vm/analysis/mod.rs b/clarity/src/vm/analysis/mod.rs index fac0cce6e7..c3397d654c 100644 --- a/clarity/src/vm/analysis/mod.rs +++ b/clarity/src/vm/analysis/mod.rs @@ -57,7 +57,7 @@ pub fn mem_type_check( ) -> Result<(Option, ContractAnalysis), StaticCheckError> { let contract_identifier = QualifiedContractIdentifier::transient(); let contract = build_ast(&contract_identifier, snippet, &mut (), version, epoch) - .map_err(|e| StaticCheckErrorKind::Expects(format!("Failed to build AST: {e}")))? + .map_err(|e| StaticCheckErrorKind::ExpectsRejectable(format!("Failed to build AST: {e}")))? .expressions; let mut marf = MemoryBackingStore::new(); @@ -76,14 +76,16 @@ pub fn mem_type_check( Ok(x) => { // return the first type result of the type checker - let first_type = - x.type_map - .as_ref() - .ok_or_else(|| StaticCheckErrorKind::Expects("Should be non-empty".into()))? - .get_type_expected(x.expressions.last().ok_or_else(|| { - StaticCheckErrorKind::Expects("Should be non-empty".into()) - })?) - .cloned(); + let first_type = x + .type_map + .as_ref() + .ok_or_else(|| { + StaticCheckErrorKind::ExpectsRejectable("Should be non-empty".into()) + })? + .get_type_expected(x.expressions.last().ok_or_else(|| { + StaticCheckErrorKind::ExpectsRejectable("Should be non-empty".into()) + })?) + .cloned(); Ok((first_type, x)) } Err(e) => Err(e.0), @@ -152,7 +154,7 @@ pub fn run_analysis( TypeChecker2_1::run_pass(&epoch, &mut contract_analysis, db, build_type_map) } StacksEpochId::Epoch10 => { - return Err(StaticCheckErrorKind::Expects( + return Err(StaticCheckErrorKind::ExpectsRejectable( "Epoch 1.0 is not a valid epoch for analysis".into(), ) .into()) diff --git a/clarity/src/vm/analysis/type_checker/mod.rs b/clarity/src/vm/analysis/type_checker/mod.rs index 30db2e931f..eb294a2fcc 100644 --- a/clarity/src/vm/analysis/type_checker/mod.rs +++ b/clarity/src/vm/analysis/type_checker/mod.rs @@ -48,9 +48,10 @@ impl FunctionType { | StacksEpochId::Epoch31 | StacksEpochId::Epoch32 | StacksEpochId::Epoch33 => self.check_args_2_1(accounting, args, clarity_version), - StacksEpochId::Epoch10 => { - Err(StaticCheckErrorKind::Expects("Epoch10 is not supported".into()).into()) - } + StacksEpochId::Epoch10 => Err(StaticCheckErrorKind::ExpectsRejectable( + "Epoch10 is not supported".into(), + ) + .into()), } } @@ -76,9 +77,10 @@ impl FunctionType { | StacksEpochId::Epoch33 => { self.check_args_by_allowing_trait_cast_2_1(db, clarity_version, func_args) } - StacksEpochId::Epoch10 => { - Err(StaticCheckErrorKind::Expects("Epoch10 is not supported".into()).into()) - } + StacksEpochId::Epoch10 => Err(StaticCheckErrorKind::ExpectsRejectable( + "Epoch10 is not supported".into(), + ) + .into()), } } } 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 8ccd89410b..ddf6cbb984 100644 --- a/clarity/src/vm/analysis/type_checker/v2_05/mod.rs +++ b/clarity/src/vm/analysis/type_checker/v2_05/mod.rs @@ -146,7 +146,10 @@ impl FunctionType { check_arguments_at_least(1, args)?; for found_type in args.iter() { analysis_typecheck_cost(accounting, expected_type, found_type)?; - if !expected_type.admits_type(&StacksEpochId::Epoch2_05, found_type)? { + if !expected_type + .admits_type(&StacksEpochId::Epoch2_05, found_type) + .map_err(StaticCheckError::from_clarity_type_error)? + { return Err(StaticCheckErrorKind::TypeError( Box::new(expected_type.clone()), Box::new(found_type.clone()), @@ -164,7 +167,10 @@ impl FunctionType { for (expected_type, found_type) in arg_types.iter().map(|x| &x.signature).zip(args) { analysis_typecheck_cost(accounting, expected_type, found_type)?; - if !expected_type.admits_type(&StacksEpochId::Epoch2_05, found_type)? { + if !expected_type + .admits_type(&StacksEpochId::Epoch2_05, found_type) + .map_err(StaticCheckError::from_clarity_type_error)? + { return Err(StaticCheckErrorKind::TypeError( Box::new(expected_type.clone()), Box::new(found_type.clone()), @@ -179,7 +185,10 @@ impl FunctionType { let found_type = &args[0]; for expected_type in arg_types.iter() { analysis_typecheck_cost(accounting, expected_type, found_type)?; - if expected_type.admits_type(&StacksEpochId::Epoch2_05, found_type)? { + if expected_type + .admits_type(&StacksEpochId::Epoch2_05, found_type) + .map_err(StaticCheckError::from_clarity_type_error)? + { return Ok(return_type.clone()); } } @@ -249,7 +258,7 @@ impl FunctionType { Ok(TypeSignature::BoolType) } - FunctionType::Binary(_, _, _) => Err(StaticCheckErrorKind::Expects( + FunctionType::Binary(_, _, _) => Err(StaticCheckErrorKind::ExpectsRejectable( "Binary type should not be reached in 2.05".into(), ) .into()), @@ -264,7 +273,10 @@ impl FunctionType { let (expected_args, returns) = match self { FunctionType::Fixed(FixedFunction { args, returns }) => (args, returns), _ => { - return Err(StaticCheckErrorKind::Expects("Unexpected function type".into()).into()) + return Err(StaticCheckErrorKind::ExpectsRejectable( + "Unexpected function type".into(), + ) + .into()) } }; check_argument_count(expected_args.len(), func_args)?; @@ -301,8 +313,12 @@ impl FunctionType { )?; } (expected_type, value) => { - if !expected_type.admits(&StacksEpochId::Epoch2_05, value)? { - let actual_type = TypeSignature::type_of(value)?; + if !expected_type + .admits(&StacksEpochId::Epoch2_05, value) + .map_err(StaticCheckError::from_clarity_type_error)? + { + let actual_type = TypeSignature::type_of(value) + .map_err(StaticCheckError::from_clarity_type_error)?; return Err(StaticCheckErrorKind::TypeError( Box::new(expected_type.clone()), Box::new(actual_type), @@ -337,13 +353,13 @@ fn type_reserved_variable(variable_name: &str) -> Result, BlockHeight => TypeSignature::UIntType, BurnBlockHeight => TypeSignature::UIntType, NativeNone => TypeSignature::new_option(no_type()) - .map_err(|_| StaticCheckErrorKind::Expects("Bad constructor".into()))?, + .map_err(|_| StaticCheckErrorKind::ExpectsRejectable("Bad constructor".into()))?, NativeTrue => TypeSignature::BoolType, NativeFalse => TypeSignature::BoolType, TotalLiquidMicroSTX => TypeSignature::UIntType, Regtest => TypeSignature::BoolType, TxSponsor | Mainnet | ChainId | StacksBlockHeight | TenureHeight | StacksBlockTime | CurrentContract => { - return Err(StaticCheckErrorKind::Expects( + return Err(StaticCheckErrorKind::ExpectsRejectable( "tx-sponsor, mainnet, chain-id, stacks-block-height, tenure-height, stacks-block-time, and current-contract should not reach here in 2.05".into(), ) .into()) @@ -391,7 +407,9 @@ impl<'a, 'b> TypeChecker<'a, 'b> { runtime_cost( ClarityCostFunction::AnalysisTypeCheck, self, - return_type.type_size()?, + return_type + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, )?; match self.function_return_tracker { @@ -434,7 +452,9 @@ impl<'a, 'b> TypeChecker<'a, 'b> { } Err(e) => Err(e), })? - .ok_or_else(|| StaticCheckErrorKind::Expects("Expected a depth result".into()))?; + .ok_or_else(|| { + StaticCheckErrorKind::ExpectsRejectable("Expected a depth result".into()) + })?; } runtime_cost(ClarityCostFunction::AnalysisStorage, self, size)?; @@ -505,7 +525,10 @@ impl<'a, 'b> TypeChecker<'a, 'b> { let actual_type = self.type_check(expr, context)?; analysis_typecheck_cost(self, expected_type, &actual_type)?; - if !expected_type.admits_type(&StacksEpochId::Epoch2_05, &actual_type)? { + if !expected_type + .admits_type(&StacksEpochId::Epoch2_05, &actual_type) + .map_err(StaticCheckError::from_clarity_type_error)? + { let mut err: StaticCheckError = StaticCheckErrorKind::TypeError( Box::new(expected_type.clone()), Box::new(actual_type), @@ -605,7 +628,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { )?; if self.function_return_tracker.is_some() { - return Err(StaticCheckErrorKind::Expects( + return Err(StaticCheckErrorKind::ExpectsRejectable( "Interpreter error: Previous function define left dirty typecheck state.".into(), ) .into()); @@ -788,7 +811,9 @@ impl<'a, 'b> TypeChecker<'a, 'b> { context: &TypingContext, ) -> Result { let type_sig = match expr.expr { - AtomValue(ref value) | LiteralValue(ref value) => TypeSignature::type_of(value)?, + AtomValue(ref value) | LiteralValue(ref value) => { + TypeSignature::type_of(value).map_err(StaticCheckError::from_clarity_type_error)? + } Atom(ref name) => self.lookup_variable(name, context)?, List(ref expression) => self.type_check_function_application(expression, context)?, TraitReference(_, _) | Field(_) => { @@ -799,7 +824,9 @@ impl<'a, 'b> TypeChecker<'a, 'b> { runtime_cost( ClarityCostFunction::AnalysisTypeAnnotate, self, - type_sig.type_size()?, + type_sig + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, )?; self.type_map.set_type(expr, type_sig.clone())?; Ok(type_sig) @@ -886,7 +913,9 @@ impl<'a, 'b> TypeChecker<'a, 'b> { runtime_cost( ClarityCostFunction::AnalysisBindName, self, - v_type.type_size()?, + v_type + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, )?; self.contract_context.add_variable_type(v_name, v_type)?; } @@ -940,8 +969,18 @@ impl<'a, 'b> TypeChecker<'a, 'b> { } => { let (f_name, map_type) = self.type_check_define_map(name, key_type, value_type)?; - let total_type_size = u64::from(map_type.0.type_size()?) - .cost_overflow_add(u64::from(map_type.1.type_size()?))?; + let total_type_size = u64::from( + map_type + .0 + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, + ) + .cost_overflow_add(u64::from( + map_type + .1 + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, + ))?; runtime_cost(ClarityCostFunction::AnalysisBindName, self, total_type_size)?; self.contract_context.add_map_type(f_name, map_type)?; } @@ -955,7 +994,9 @@ impl<'a, 'b> TypeChecker<'a, 'b> { runtime_cost( ClarityCostFunction::AnalysisBindName, self, - v_type.type_size()?, + v_type + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, )?; self.contract_context .add_persisted_variable_type(v_name, v_type)?; @@ -965,7 +1006,9 @@ impl<'a, 'b> TypeChecker<'a, 'b> { runtime_cost( ClarityCostFunction::AnalysisBindName, self, - TypeSignature::UIntType.type_size()?, + TypeSignature::UIntType + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, )?; self.contract_context.add_ft(token_name)?; } @@ -974,7 +1017,9 @@ impl<'a, 'b> TypeChecker<'a, 'b> { runtime_cost( ClarityCostFunction::AnalysisBindName, self, - TypeSignature::UIntType.type_size()?, + TypeSignature::UIntType + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, )?; self.contract_context.add_ft(token_name)?; } @@ -984,7 +1029,9 @@ impl<'a, 'b> TypeChecker<'a, 'b> { runtime_cost( ClarityCostFunction::AnalysisBindName, self, - token_type.type_size()?, + token_type + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, )?; self.contract_context.add_nft(token_name, token_type)?; } diff --git a/clarity/src/vm/analysis/type_checker/v2_05/natives/assets.rs b/clarity/src/vm/analysis/type_checker/v2_05/natives/assets.rs index d5a492d09a..e8a2d3f8b4 100644 --- a/clarity/src/vm/analysis/type_checker/v2_05/natives/assets.rs +++ b/clarity/src/vm/analysis/type_checker/v2_05/natives/assets.rs @@ -41,7 +41,9 @@ pub fn check_special_get_owner( runtime_cost( ClarityCostFunction::AnalysisTypeLookup, checker, - expected_asset_type.type_size()?, + expected_asset_type + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, )?; checker.type_check_expects(&args[1], context, &expected_asset_type)?; @@ -95,7 +97,9 @@ pub fn check_special_mint_asset( runtime_cost( ClarityCostFunction::AnalysisTypeLookup, checker, - expected_asset_type.type_size()?, + expected_asset_type + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, )?; checker.type_check_expects(&args[1], context, &expected_asset_type)?; @@ -157,7 +161,9 @@ pub fn check_special_transfer_asset( runtime_cost( ClarityCostFunction::AnalysisTypeLookup, checker, - expected_asset_type.type_size()?, + expected_asset_type + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, )?; checker.type_check_expects(&args[1], context, &expected_asset_type)?; @@ -241,7 +247,9 @@ pub fn check_special_burn_asset( runtime_cost( ClarityCostFunction::AnalysisTypeLookup, checker, - expected_asset_type.type_size()?, + expected_asset_type + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, )?; checker.type_check_expects(&args[1], context, &expected_asset_type)?; diff --git a/clarity/src/vm/analysis/type_checker/v2_05/natives/maps.rs b/clarity/src/vm/analysis/type_checker/v2_05/natives/maps.rs index b9cb0122af..c5560a43da 100644 --- a/clarity/src/vm/analysis/type_checker/v2_05/natives/maps.rs +++ b/clarity/src/vm/analysis/type_checker/v2_05/natives/maps.rs @@ -45,18 +45,26 @@ pub fn check_special_fetch_entry( runtime_cost( ClarityCostFunction::AnalysisTypeLookup, &mut checker.cost_track, - expected_key_type.type_size()?, + expected_key_type + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, )?; runtime_cost( ClarityCostFunction::AnalysisTypeLookup, &mut checker.cost_track, - value_type.type_size()?, + value_type + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, )?; analysis_typecheck_cost(&mut checker.cost_track, expected_key_type, &key_type)?; - let option_type = TypeSignature::new_option(value_type.clone())?; + let option_type = TypeSignature::new_option(value_type.clone()) + .map_err(StaticCheckError::from_clarity_type_error)?; - if !expected_key_type.admits_type(&StacksEpochId::Epoch2_05, &key_type)? { + if !expected_key_type + .admits_type(&StacksEpochId::Epoch2_05, &key_type) + .map_err(StaticCheckError::from_clarity_type_error)? + { Err(StaticCheckError::new(StaticCheckErrorKind::TypeError( Box::new(expected_key_type.clone()), Box::new(key_type), @@ -87,11 +95,16 @@ pub fn check_special_delete_entry( runtime_cost( ClarityCostFunction::AnalysisTypeLookup, &mut checker.cost_track, - expected_key_type.type_size()?, + expected_key_type + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, )?; analysis_typecheck_cost(&mut checker.cost_track, expected_key_type, &key_type)?; - if !expected_key_type.admits_type(&StacksEpochId::Epoch2_05, &key_type)? { + if !expected_key_type + .admits_type(&StacksEpochId::Epoch2_05, &key_type) + .map_err(StaticCheckError::from_clarity_type_error)? + { Err(StaticCheckError::new(StaticCheckErrorKind::TypeError( Box::new(expected_key_type.clone()), Box::new(key_type), @@ -123,23 +136,33 @@ fn check_set_or_insert_entry( runtime_cost( ClarityCostFunction::AnalysisTypeLookup, &mut checker.cost_track, - expected_key_type.type_size()?, + expected_key_type + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, )?; runtime_cost( ClarityCostFunction::AnalysisTypeLookup, &mut checker.cost_track, - value_type.type_size()?, + value_type + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, )?; analysis_typecheck_cost(&mut checker.cost_track, expected_key_type, &key_type)?; analysis_typecheck_cost(&mut checker.cost_track, expected_value_type, &value_type)?; - if !expected_key_type.admits_type(&StacksEpochId::Epoch2_05, &key_type)? { + if !expected_key_type + .admits_type(&StacksEpochId::Epoch2_05, &key_type) + .map_err(StaticCheckError::from_clarity_type_error)? + { Err(StaticCheckError::new(StaticCheckErrorKind::TypeError( Box::new(expected_key_type.clone()), Box::new(key_type), ))) - } else if !expected_value_type.admits_type(&StacksEpochId::Epoch2_05, &value_type)? { + } else if !expected_value_type + .admits_type(&StacksEpochId::Epoch2_05, &value_type) + .map_err(StaticCheckError::from_clarity_type_error)? + { Err(StaticCheckError::new(StaticCheckErrorKind::TypeError( Box::new(expected_value_type.clone()), Box::new(value_type), diff --git a/clarity/src/vm/analysis/type_checker/v2_05/natives/mod.rs b/clarity/src/vm/analysis/type_checker/v2_05/natives/mod.rs index 1cc33db455..321f245af9 100644 --- a/clarity/src/vm/analysis/type_checker/v2_05/natives/mod.rs +++ b/clarity/src/vm/analysis/type_checker/v2_05/natives/mod.rs @@ -59,10 +59,13 @@ fn check_special_list_cons( runtime_cost( ClarityCostFunction::AnalysisListItemsCheck, checker, - type_arg.type_size()?, + type_arg + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, )?; } - let list_type = TypeSignature::parent_list_type(&typed_args)?; + let list_type = TypeSignature::parent_list_type(&typed_args) + .map_err(StaticCheckError::from_clarity_type_error)?; Ok(TypeSignature::from(list_type)) } @@ -145,7 +148,8 @@ fn check_special_get( } else if let TypeSignature::OptionalType(value_type_sig) = argument_type { if let TypeSignature::TupleType(tuple_type_sig) = *value_type_sig { let inner_type = inner_handle_tuple_get(&tuple_type_sig, field_to_get, checker)?; - let option_type = TypeSignature::new_option(inner_type)?; + let option_type = TypeSignature::new_option(inner_type) + .map_err(StaticCheckError::from_clarity_type_error)?; Ok(option_type) } else { Err(StaticCheckErrorKind::ExpectedTuple(value_type_sig).into()) @@ -203,7 +207,9 @@ pub fn check_special_tuple_cons( runtime_cost( ClarityCostFunction::AnalysisTupleItemsCheck, checker, - var_type.type_size()?, + var_type + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, )?; tuple_type_data.push((var_name.clone(), var_type)); Ok(()) @@ -212,7 +218,9 @@ pub fn check_special_tuple_cons( )?; let tuple_signature = TupleTypeSignature::try_from(tuple_type_data).map_err(|e| { - StaticCheckErrorKind::BadTupleConstruction(StaticCheckErrorKind::from(e).message()) + StaticCheckErrorKind::BadTupleConstruction( + StaticCheckErrorKind::from_clarity_type_error(e).message(), + ) })?; Ok(TypeSignature::TupleType(tuple_signature)) @@ -249,7 +257,9 @@ fn check_special_let( runtime_cost( ClarityCostFunction::AnalysisBindName, checker, - typed_result.type_size()?, + typed_result + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, )?; out_context .variable_types @@ -282,7 +292,9 @@ fn check_special_fetch_var( runtime_cost( ClarityCostFunction::AnalysisTypeLookup, &mut checker.cost_track, - value_type.type_size()?, + value_type + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, )?; Ok(value_type.clone()) @@ -311,11 +323,16 @@ fn check_special_set_var( runtime_cost( ClarityCostFunction::AnalysisTypeLookup, &mut checker.cost_track, - expected_value_type.type_size()?, + expected_value_type + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, )?; analysis_typecheck_cost(&mut checker.cost_track, &value_type, expected_value_type)?; - if !expected_value_type.admits_type(&StacksEpochId::Epoch2_05, &value_type)? { + if !expected_value_type + .admits_type(&StacksEpochId::Epoch2_05, &value_type) + .map_err(StaticCheckError::from_clarity_type_error)? + { Err(StaticCheckError::new(StaticCheckErrorKind::TypeError( Box::new(expected_value_type.clone()), Box::new(value_type), @@ -510,7 +527,7 @@ fn check_principal_of( checker.type_check_expects(&args[0], context, &TypeSignature::BUFFER_33)?; Ok( TypeSignature::new_response(TypeSignature::PrincipalType, TypeSignature::UIntType) - .map_err(|_| StaticCheckErrorKind::Expects("Bad constructor".into()))?, + .map_err(|_| StaticCheckErrorKind::ExpectsRejectable("Bad constructor".into()))?, ) } @@ -524,7 +541,7 @@ fn check_secp256k1_recover( checker.type_check_expects(&args[1], context, &TypeSignature::BUFFER_65)?; Ok( TypeSignature::new_response(TypeSignature::BUFFER_33, TypeSignature::UIntType) - .map_err(|_| StaticCheckErrorKind::Expects("Bad constructor".into()))?, + .map_err(|_| StaticCheckErrorKind::ExpectsRejectable("Bad constructor".into()))?, ) } @@ -561,7 +578,8 @@ fn check_get_block_info( checker.type_check_expects(&args[1], context, &TypeSignature::UIntType)?; - Ok(TypeSignature::new_option(block_info_prop.type_result())?) + TypeSignature::new_option(block_info_prop.type_result()) + .map_err(StaticCheckError::from_clarity_type_error) } impl TypedNativeFunction { @@ -604,7 +622,7 @@ impl TypedNativeFunction { args: vec![FunctionArg::new( TypeSignature::IntType, ClarityName::try_from("value".to_owned()).map_err(|_| { - StaticCheckErrorKind::Expects( + StaticCheckErrorKind::ExpectsRejectable( "FAIL: ClarityName failed to accept default arg name".into(), ) })?, @@ -615,7 +633,7 @@ impl TypedNativeFunction { args: vec![FunctionArg::new( TypeSignature::UIntType, ClarityName::try_from("value".to_owned()).map_err(|_| { - StaticCheckErrorKind::Expects( + StaticCheckErrorKind::ExpectsRejectable( "FAIL: ClarityName failed to accept default arg name".into(), ) })?, @@ -626,7 +644,7 @@ impl TypedNativeFunction { args: vec![FunctionArg::new( TypeSignature::BoolType, ClarityName::try_from("value".to_owned()).map_err(|_| { - StaticCheckErrorKind::Expects( + StaticCheckErrorKind::ExpectsRejectable( "FAIL: ClarityName failed to accept default arg name".into(), ) })?, @@ -679,7 +697,7 @@ impl TypedNativeFunction { args: vec![FunctionArg::new( TypeSignature::PrincipalType, ClarityName::try_from("owner".to_owned()).map_err(|_| { - StaticCheckErrorKind::Expects( + StaticCheckErrorKind::ExpectsRejectable( "FAIL: ClarityName failed to accept default arg name".into(), ) })?, @@ -691,7 +709,7 @@ impl TypedNativeFunction { FunctionArg::new( TypeSignature::UIntType, ClarityName::try_from("amount".to_owned()).map_err(|_| { - StaticCheckErrorKind::Expects( + StaticCheckErrorKind::ExpectsRejectable( "FAIL: ClarityName failed to accept default arg name".into(), ) })?, @@ -699,7 +717,7 @@ impl TypedNativeFunction { FunctionArg::new( TypeSignature::PrincipalType, ClarityName::try_from("sender".to_owned()).map_err(|_| { - StaticCheckErrorKind::Expects( + StaticCheckErrorKind::ExpectsRejectable( "FAIL: ClarityName failed to accept default arg name".into(), ) })?, @@ -707,7 +725,7 @@ impl TypedNativeFunction { FunctionArg::new( TypeSignature::PrincipalType, ClarityName::try_from("recipient".to_owned()).map_err(|_| { - StaticCheckErrorKind::Expects( + StaticCheckErrorKind::ExpectsRejectable( "FAIL: ClarityName failed to accept default arg name".into(), ) })?, @@ -717,14 +735,14 @@ impl TypedNativeFunction { TypeSignature::BoolType, TypeSignature::UIntType, ) - .map_err(|_| StaticCheckErrorKind::Expects("Bad constructor".into()))?, + .map_err(|_| StaticCheckErrorKind::ExpectsRejectable("Bad constructor".into()))?, }))), StxBurn => Simple(SimpleNativeFunction(FunctionType::Fixed(FixedFunction { args: vec![ FunctionArg::new( TypeSignature::UIntType, ClarityName::try_from("amount".to_owned()).map_err(|_| { - StaticCheckErrorKind::Expects( + StaticCheckErrorKind::ExpectsRejectable( "FAIL: ClarityName failed to accept default arg name".into(), ) })?, @@ -732,7 +750,7 @@ impl TypedNativeFunction { FunctionArg::new( TypeSignature::PrincipalType, ClarityName::try_from("sender".to_owned()).map_err(|_| { - StaticCheckErrorKind::Expects( + StaticCheckErrorKind::ExpectsRejectable( "FAIL: ClarityName failed to accept default arg name".into(), ) })?, @@ -742,7 +760,7 @@ impl TypedNativeFunction { TypeSignature::BoolType, TypeSignature::UIntType, ) - .map_err(|_| StaticCheckErrorKind::Expects("Bad constructor".into()))?, + .map_err(|_| StaticCheckErrorKind::ExpectsRejectable("Bad constructor".into()))?, }))), GetTokenBalance => Special(SpecialNativeFunction(&assets::check_special_get_balance)), GetAssetOwner => Special(SpecialNativeFunction(&assets::check_special_get_owner)), @@ -840,7 +858,7 @@ impl TypedNativeFunction { | AllowanceWithStacking | AllowanceAll | Secp256r1Verify => { - return Err(StaticCheckErrorKind::Expects( + return Err(StaticCheckErrorKind::ExpectsRejectable( "Clarity 2+ keywords should not show up in 2.05".into(), )); } diff --git a/clarity/src/vm/analysis/type_checker/v2_05/natives/options.rs b/clarity/src/vm/analysis/type_checker/v2_05/natives/options.rs index 75c931428c..8c29d7aa42 100644 --- a/clarity/src/vm/analysis/type_checker/v2_05/natives/options.rs +++ b/clarity/src/vm/analysis/type_checker/v2_05/natives/options.rs @@ -36,7 +36,8 @@ pub fn check_special_okay( runtime_cost(ClarityCostFunction::AnalysisOptionCons, checker, 0)?; let inner_type = checker.type_check(&args[0], context)?; - let resp_type = TypeSignature::new_response(inner_type, no_type())?; + let resp_type = TypeSignature::new_response(inner_type, no_type()) + .map_err(StaticCheckError::from_clarity_type_error)?; Ok(resp_type) } @@ -50,7 +51,8 @@ pub fn check_special_some( runtime_cost(ClarityCostFunction::AnalysisOptionCons, checker, 0)?; let inner_type = checker.type_check(&args[0], context)?; - let resp_type = TypeSignature::new_option(inner_type)?; + let resp_type = + TypeSignature::new_option(inner_type).map_err(StaticCheckError::from_clarity_type_error)?; Ok(resp_type) } @@ -64,7 +66,8 @@ pub fn check_special_error( runtime_cost(ClarityCostFunction::AnalysisOptionCons, checker, 0)?; let inner_type = checker.type_check(&args[0], context)?; - let resp_type = TypeSignature::new_response(no_type(), inner_type)?; + let resp_type = TypeSignature::new_response(no_type(), inner_type) + .map_err(StaticCheckError::from_clarity_type_error)?; Ok(resp_type) } @@ -236,7 +239,10 @@ pub fn check_special_try_ret( if input_type.is_no_type() { Err(StaticCheckErrorKind::CouldNotDetermineResponseOkType.into()) } else { - checker.track_return_type(TypeSignature::new_option(TypeSignature::NoType)?)?; + checker.track_return_type( + TypeSignature::new_option(TypeSignature::NoType) + .map_err(StaticCheckError::from_clarity_type_error)?, + )?; Ok(*input_type) } } @@ -247,10 +253,10 @@ pub fn check_special_try_ret( } else if err_type.is_no_type() { Err(StaticCheckErrorKind::CouldNotDetermineResponseErrType.into()) } else { - checker.track_return_type(TypeSignature::new_response( - TypeSignature::NoType, - err_type, - )?)?; + checker.track_return_type( + TypeSignature::new_response(TypeSignature::NoType, err_type) + .map_err(StaticCheckError::from_clarity_type_error)?, + )?; Ok(ok_type) } } @@ -294,7 +300,9 @@ fn eval_with_new_binding( runtime_cost( ClarityCostFunction::AnalysisBindName, checker, - bind_type.type_size()?, + bind_type + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, )?; checker.contract_context.check_name_used(&bind_name)?; diff --git a/clarity/src/vm/analysis/type_checker/v2_05/natives/sequences.rs b/clarity/src/vm/analysis/type_checker/v2_05/natives/sequences.rs index 37f3d83b83..6f7522dbfd 100644 --- a/clarity/src/vm/analysis/type_checker/v2_05/natives/sequences.rs +++ b/clarity/src/vm/analysis/type_checker/v2_05/natives/sequences.rs @@ -234,29 +234,40 @@ pub fn check_special_concat( &StacksEpochId::Epoch2_05, lhs_entry_type, rhs_entry_type, - )?; + ) + .map_err(StaticCheckError::from_clarity_type_error)?; let new_len = lhs_max_len .checked_add(rhs_max_len) .ok_or(StaticCheckErrorKind::MaxLengthOverflow)?; - TypeSignature::list_of(list_entry_type, new_len)? + TypeSignature::list_of(list_entry_type, new_len) + .map_err(StaticCheckError::from_clarity_type_error)? } (BufferType(lhs_len), BufferType(rhs_len)) => { let size: u32 = u32::from(lhs_len) .checked_add(u32::from(rhs_len)) .ok_or(StaticCheckErrorKind::MaxLengthOverflow)?; - TypeSignature::SequenceType(BufferType(size.try_into()?)) + TypeSignature::SequenceType(BufferType( + size.try_into() + .map_err(StaticCheckError::from_clarity_type_error)?, + )) } (StringType(ASCII(lhs_len)), StringType(ASCII(rhs_len))) => { let size: u32 = u32::from(lhs_len) .checked_add(u32::from(rhs_len)) .ok_or(StaticCheckErrorKind::MaxLengthOverflow)?; - TypeSignature::SequenceType(StringType(ASCII(size.try_into()?))) + TypeSignature::SequenceType(StringType(ASCII( + size.try_into() + .map_err(StaticCheckError::from_clarity_type_error)?, + ))) } (StringType(UTF8(lhs_len)), StringType(UTF8(rhs_len))) => { let size: u32 = u32::from(lhs_len) .checked_add(u32::from(rhs_len)) .ok_or(StaticCheckErrorKind::MaxLengthOverflow)?; - TypeSignature::SequenceType(StringType(UTF8(size.try_into()?))) + TypeSignature::SequenceType(StringType(UTF8( + size.try_into() + .map_err(StaticCheckError::from_clarity_type_error)?, + ))) } (_, _) => { return Err(StaticCheckErrorKind::TypeError( @@ -293,11 +304,13 @@ pub fn check_special_append( &StacksEpochId::Epoch2_05, &lhs_entry_type, &rhs_type, - )?; + ) + .map_err(StaticCheckError::from_clarity_type_error)?; let new_len = lhs_max_len .checked_add(1) .ok_or(StaticCheckErrorKind::MaxLengthOverflow)?; - let return_type = TypeSignature::list_of(list_entry_type, new_len)?; + let return_type = TypeSignature::list_of(list_entry_type, new_len) + .map_err(StaticCheckError::from_clarity_type_error)?; Ok(return_type) } _ => Err(StaticCheckErrorKind::ExpectedListApplication.into()), @@ -325,7 +338,9 @@ pub fn check_special_as_max_len( runtime_cost( ClarityCostFunction::AnalysisTypeAnnotate, checker, - TypeSignature::UIntType.type_size()?, + TypeSignature::UIntType + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, )?; checker .type_map @@ -340,22 +355,28 @@ pub fn check_special_as_max_len( match sequence { TypeSignature::SequenceType(ListType(list)) => { let (lhs_entry_type, _) = list.destruct(); - let resized_list = ListTypeData::new_list(lhs_entry_type, expected_len)?; + let resized_list = ListTypeData::new_list(lhs_entry_type, expected_len) + .map_err(StaticCheckError::from_clarity_type_error)?; Ok(TypeSignature::OptionalType(Box::new( TypeSignature::SequenceType(ListType(resized_list)), ))) } TypeSignature::SequenceType(BufferType(_)) => Ok(TypeSignature::OptionalType(Box::new( - TypeSignature::SequenceType(BufferType(BufferLength::try_from(expected_len)?)), + TypeSignature::SequenceType(BufferType( + BufferLength::try_from(expected_len) + .map_err(StaticCheckError::from_clarity_type_error)?, + )), ))), TypeSignature::SequenceType(StringType(ASCII(_))) => Ok(TypeSignature::OptionalType( Box::new(TypeSignature::SequenceType(StringType(ASCII( - BufferLength::try_from(expected_len)?, + BufferLength::try_from(expected_len) + .map_err(StaticCheckError::from_clarity_type_error)?, )))), )), TypeSignature::SequenceType(StringType(UTF8(_))) => Ok(TypeSignature::OptionalType( Box::new(TypeSignature::SequenceType(StringType(UTF8( - StringUTF8Length::try_from(expected_len)?, + StringUTF8Length::try_from(expected_len) + .map_err(StaticCheckError::from_clarity_type_error)?, )))), )), _ => Err(StaticCheckErrorKind::ExpectedSequence(Box::new(sequence)).into()), @@ -397,21 +418,23 @@ pub fn check_special_element_at( match collection_type { TypeSignature::SequenceType(ListType(list)) => { let (entry_type, _) = list.destruct(); - TypeSignature::new_option(entry_type).map_err(|e| e.into()) + TypeSignature::new_option(entry_type).map_err(StaticCheckError::from_clarity_type_error) } TypeSignature::SequenceType(BufferType(_)) => Ok(TypeSignature::OptionalType(Box::new( TypeSignature::BUFFER_1, ))), TypeSignature::SequenceType(StringType(ASCII(_))) => Ok(TypeSignature::OptionalType( Box::new(TypeSignature::SequenceType(StringType(ASCII( - BufferLength::try_from(1u32) - .map_err(|_| StaticCheckErrorKind::Expects("Bad constructor".into()))?, + BufferLength::try_from(1u32).map_err(|_| { + StaticCheckErrorKind::ExpectsRejectable("Bad constructor".into()) + })?, )))), )), TypeSignature::SequenceType(StringType(UTF8(_))) => Ok(TypeSignature::OptionalType( Box::new(TypeSignature::SequenceType(StringType(UTF8( - StringUTF8Length::try_from(1u32) - .map_err(|_| StaticCheckErrorKind::Expects("Bad constructor".into()))?, + StringUTF8Length::try_from(1u32).map_err(|_| { + StaticCheckErrorKind::ExpectsRejectable("Bad constructor".into()) + })?, )))), )), _ => Err(StaticCheckErrorKind::ExpectedSequence(Box::new(collection_type)).into()), @@ -435,5 +458,6 @@ pub fn check_special_index_of( checker.type_check_expects(&args[1], context, &expected_input_type)?; - TypeSignature::new_option(TypeSignature::UIntType).map_err(|e| e.into()) + TypeSignature::new_option(TypeSignature::UIntType) + .map_err(StaticCheckError::from_clarity_type_error) } 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 fbbbc8fb57..26007511dc 100644 --- a/clarity/src/vm/analysis/type_checker/v2_1/mod.rs +++ b/clarity/src/vm/analysis/type_checker/v2_1/mod.rs @@ -186,7 +186,7 @@ impl FunctionType { let cost = Some(compute_typecheck_cost(accounting, expected_type, arg_type)); let admitted = match expected_type.admits_type(&StacksEpochId::Epoch21, arg_type) { Ok(admitted) => admitted, - Err(e) => return (cost, Err(e.into())), + Err(e) => return (cost, Err(StaticCheckError::from_clarity_type_error(e))), }; if !admitted { return ( @@ -219,7 +219,7 @@ impl FunctionType { (cost, return_type) } else { let return_type = accumulated_type - .ok_or_else(|| StaticCheckErrorKind::Expects("Failed to set accumulated type for arg indices >= 1 in variadic arithmetic".into()).into()); + .ok_or_else(|| StaticCheckErrorKind::ExpectsRejectable("Failed to set accumulated type for arg indices >= 1 in variadic arithmetic".into()).into()); let check_result = return_type.and_then(|return_type| { if arg_type != return_type { Err(StaticCheckErrorKind::TypeError( @@ -292,7 +292,10 @@ impl FunctionType { check_arguments_at_least(1, args)?; for found_type in args.iter() { analysis_typecheck_cost(accounting, expected_type, found_type)?; - if !expected_type.admits_type(&StacksEpochId::Epoch21, found_type)? { + if !expected_type + .admits_type(&StacksEpochId::Epoch21, found_type) + .map_err(StaticCheckError::from_clarity_type_error)? + { return Err(StaticCheckErrorKind::TypeError( Box::new(expected_type.clone()), Box::new(found_type.clone()), @@ -310,7 +313,10 @@ impl FunctionType { for (expected_type, found_type) in arg_types.iter().map(|x| &x.signature).zip(args) { analysis_typecheck_cost(accounting, expected_type, found_type)?; - if !expected_type.admits_type(&StacksEpochId::Epoch21, found_type)? { + if !expected_type + .admits_type(&StacksEpochId::Epoch21, found_type) + .map_err(StaticCheckError::from_clarity_type_error)? + { return Err(StaticCheckErrorKind::TypeError( Box::new(expected_type.clone()), Box::new(found_type.clone()), @@ -325,7 +331,10 @@ impl FunctionType { let found_type = &args[0]; for expected_type in arg_types.iter() { analysis_typecheck_cost(accounting, expected_type, found_type)?; - if expected_type.admits_type(&StacksEpochId::Epoch21, found_type)? { + if expected_type + .admits_type(&StacksEpochId::Epoch21, found_type) + .map_err(StaticCheckError::from_clarity_type_error)? + { return Ok(return_type.clone()); } } @@ -485,7 +494,8 @@ impl FunctionType { contract_identifier.clone(), )) } - _ => TypeSignature::type_of(value)?, + _ => TypeSignature::type_of(value) + .map_err(StaticCheckError::from_clarity_type_error)?, }) } } @@ -508,7 +518,8 @@ impl FunctionType { data: Some(inner_value), }) => TypeSignature::new_option( self.clarity2_principal_to_callable_type(inner_value, depth + 1)?, - )?, + ) + .map_err(StaticCheckError::from_clarity_type_error)?, Value::Response(ResponseData { committed, data }) => { let (ok_type, err_type) = if *committed { ( @@ -521,7 +532,8 @@ impl FunctionType { self.clarity2_principal_to_callable_type(data, depth + 1)?, ) }; - TypeSignature::new_response(ok_type, err_type)? + TypeSignature::new_response(ok_type, err_type) + .map_err(StaticCheckError::from_clarity_type_error)? } Value::Sequence(SequenceData::List(ListData { data, @@ -533,10 +545,10 @@ impl FunctionType { } None => TypeSignature::NoType, }; - TypeSignature::SequenceType(SequenceSubtype::ListType(ListTypeData::new_list( - inner_type, - data.len() as u32, - )?)) + TypeSignature::SequenceType(SequenceSubtype::ListType( + ListTypeData::new_list(inner_type, data.len() as u32) + .map_err(StaticCheckError::from_clarity_type_error)?, + )) } Value::Tuple(TupleData { type_signature: _, @@ -549,9 +561,14 @@ impl FunctionType { self.clarity2_principal_to_callable_type(field_value, depth + 1)?, ); } - TypeSignature::TupleType(TupleTypeSignature::try_from(type_map)?) + TypeSignature::TupleType( + TupleTypeSignature::try_from(type_map) + .map_err(StaticCheckError::from_clarity_type_error)?, + ) + } + _ => { + TypeSignature::type_of(value).map_err(StaticCheckError::from_clarity_type_error)? } - _ => TypeSignature::type_of(value)?, }) } @@ -568,7 +585,10 @@ impl FunctionType { let (expected_args, returns) = match self { FunctionType::Fixed(FixedFunction { args, returns }) => (args, returns), _ => { - return Err(StaticCheckErrorKind::Expects("Unexpected function type".into()).into()) + return Err(StaticCheckErrorKind::ExpectsRejectable( + "Unexpected function type".into(), + ) + .into()) } }; check_argument_count(expected_args.len(), func_args)?; @@ -592,7 +612,9 @@ impl FunctionType { &StacksEpochId::Epoch21, ) .map_err(|_| { - StaticCheckErrorKind::Expects("Failed to get trait".into()) + StaticCheckErrorKind::ExpectsRejectable( + "Failed to get trait".into(), + ) })? .ok_or(StaticCheckErrorKind::NoSuchContract( trait_id.contract_identifier.to_string(), @@ -604,8 +626,12 @@ impl FunctionType { )?; } (expected_type, value) => { - if !expected_type.admits(&StacksEpochId::Epoch21, value)? { - let actual_type = TypeSignature::type_of(value)?; + if !expected_type + .admits(&StacksEpochId::Epoch21, value) + .map_err(StaticCheckError::from_clarity_type_error)? + { + let actual_type = TypeSignature::type_of(value) + .map_err(StaticCheckError::from_clarity_type_error)?; return Err(StaticCheckErrorKind::TypeError( Box::new(expected_type.clone()), Box::new(actual_type.clone()), @@ -644,7 +670,10 @@ fn check_function_arg_signature( match expected_sig { FunctionArgSignature::Single(expected_type) => { analysis_typecheck_cost(cost_tracker, expected_type, actual_type)?; - if !expected_type.admits_type(&StacksEpochId::Epoch21, actual_type)? { + if !expected_type + .admits_type(&StacksEpochId::Epoch21, actual_type) + .map_err(StaticCheckError::from_clarity_type_error)? + { return Err(StaticCheckErrorKind::TypeError( Box::new(expected_type.clone()), Box::new(actual_type.clone()), @@ -656,7 +685,10 @@ fn check_function_arg_signature( let mut admitted = false; for expected_type in expected_types.iter() { analysis_typecheck_cost(cost_tracker, expected_type, actual_type)?; - if expected_type.admits_type(&StacksEpochId::Epoch21, actual_type)? { + if expected_type + .admits_type(&StacksEpochId::Epoch21, actual_type) + .map_err(StaticCheckError::from_clarity_type_error)? + { admitted = true; break; } @@ -933,7 +965,10 @@ fn clarity2_inner_type_check_type( } (TypeSignature::NoType, _) => (), (_, _) => { - if !expected_type.admits_type(&StacksEpochId::Epoch21, actual_type)? { + if !expected_type + .admits_type(&StacksEpochId::Epoch21, actual_type) + .map_err(StaticCheckError::from_clarity_type_error)? + { return Err(StaticCheckErrorKind::TypeError( Box::new(expected_type.clone()), Box::new(actual_type.clone()), @@ -1021,14 +1056,14 @@ fn type_reserved_variable( let var_type = match variable { TxSender => TypeSignature::PrincipalType, TxSponsor => TypeSignature::new_option(TypeSignature::PrincipalType) - .map_err(|_| StaticCheckErrorKind::Expects("Bad construction".into()))?, + .map_err(|_| StaticCheckErrorKind::ExpectsRejectable("Bad construction".into()))?, ContractCaller => TypeSignature::PrincipalType, BlockHeight => TypeSignature::UIntType, StacksBlockHeight => TypeSignature::UIntType, TenureHeight => TypeSignature::UIntType, BurnBlockHeight => TypeSignature::UIntType, NativeNone => TypeSignature::new_option(no_type()) - .map_err(|_| StaticCheckErrorKind::Expects("Bad construction".into()))?, + .map_err(|_| StaticCheckErrorKind::ExpectsRejectable("Bad construction".into()))?, NativeTrue => TypeSignature::BoolType, NativeFalse => TypeSignature::BoolType, TotalLiquidMicroSTX => TypeSignature::UIntType, @@ -1085,7 +1120,9 @@ impl<'a, 'b> TypeChecker<'a, 'b> { runtime_cost( ClarityCostFunction::AnalysisTypeCheck, self, - return_type.type_size()?, + return_type + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, )?; match self.function_return_tracker { @@ -1128,7 +1165,9 @@ impl<'a, 'b> TypeChecker<'a, 'b> { } Err(e) => Err(e), })? - .ok_or_else(|| StaticCheckErrorKind::Expects("Expected a depth result".into()))?; + .ok_or_else(|| { + StaticCheckErrorKind::ExpectsRejectable("Expected a depth result".into()) + })?; } runtime_cost(ClarityCostFunction::AnalysisStorage, self, size)?; @@ -1334,7 +1373,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { )?; if self.function_return_tracker.is_some() { - return Err(StaticCheckErrorKind::Expects( + return Err(StaticCheckErrorKind::ExpectsRejectable( "Interpreter error: Previous function define left dirty typecheck state.".into(), ) .into()); @@ -1347,7 +1386,12 @@ impl<'a, 'b> TypeChecker<'a, 'b> { if self.epoch.analysis_memory() { let added_memory = u64::from(arg_name.len()) - .checked_add(arg_type.type_size()?.into()) + .checked_add( + arg_type + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)? + .into(), + ) .ok_or_else(|| CostErrors::CostOverflow)?; self.add_memory(added_memory)?; tracked_mem = tracked_mem @@ -1398,7 +1442,8 @@ impl<'a, 'b> TypeChecker<'a, 'b> { } else { return_type } - .concretize()? + .concretize() + .map_err(StaticCheckError::from_clarity_type_error)? }; self.function_return_tracker = None; @@ -1574,7 +1619,10 @@ impl<'a, 'b> TypeChecker<'a, 'b> { let actual_type = self.type_check(expr, context)?; analysis_typecheck_cost(self, expected_type, &actual_type)?; - if !expected_type.admits_type(&StacksEpochId::Epoch21, &actual_type)? { + if !expected_type + .admits_type(&StacksEpochId::Epoch21, &actual_type) + .map_err(StaticCheckError::from_clarity_type_error)? + { let mut err: StaticCheckError = StaticCheckErrorKind::TypeError( Box::new(expected_type.clone()), Box::new(actual_type.clone()), @@ -1594,8 +1642,11 @@ impl<'a, 'b> TypeChecker<'a, 'b> { expected_type: &TypeSignature, ) -> Result { let mut expr_type = match expr.expr { - AtomValue(ref value) => TypeSignature::type_of(value)?, - LiteralValue(ref value) => TypeSignature::literal_type_of(value)?, + AtomValue(ref value) => { + TypeSignature::type_of(value).map_err(StaticCheckError::from_clarity_type_error)? + } + LiteralValue(ref value) => TypeSignature::literal_type_of(value) + .map_err(StaticCheckError::from_clarity_type_error)?, Atom(ref name) => self.lookup_variable(name, context)?, List(ref expression) => self.type_check_function_application(expression, context)?, TraitReference(_, _) | Field(_) => { @@ -1620,7 +1671,9 @@ impl<'a, 'b> TypeChecker<'a, 'b> { runtime_cost( ClarityCostFunction::AnalysisTypeAnnotate, self, - expr_type.type_size()?, + expr_type + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, )?; self.type_map.set_type(expr, expr_type.clone())?; Ok(expr_type) @@ -1632,8 +1685,11 @@ impl<'a, 'b> TypeChecker<'a, 'b> { context: &TypingContext, ) -> Result { let expr_type = match expr.expr { - AtomValue(ref value) => TypeSignature::type_of(value)?, - LiteralValue(ref value) => TypeSignature::literal_type_of(value)?, + AtomValue(ref value) => { + TypeSignature::type_of(value).map_err(StaticCheckError::from_clarity_type_error)? + } + LiteralValue(ref value) => TypeSignature::literal_type_of(value) + .map_err(StaticCheckError::from_clarity_type_error)?, Atom(ref name) => self.lookup_variable(name, context)?, List(ref expression) => self.type_check_function_application(expression, context)?, TraitReference(_, _) | Field(_) => { @@ -1644,7 +1700,9 @@ impl<'a, 'b> TypeChecker<'a, 'b> { runtime_cost( ClarityCostFunction::AnalysisTypeAnnotate, self, - expr_type.type_size()?, + expr_type + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, )?; self.type_map.set_type(expr, expr_type.clone())?; Ok(expr_type) @@ -1731,11 +1789,18 @@ impl<'a, 'b> TypeChecker<'a, 'b> { runtime_cost( ClarityCostFunction::AnalysisBindName, self, - v_type.type_size()?, + v_type + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, )?; if self.epoch.analysis_memory() { self.add_memory(v_name.len().into())?; - self.add_memory(v_type.type_size()?.into())?; + self.add_memory( + v_type + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)? + .into(), + )?; } self.contract_context.add_variable_type(v_name, v_type)?; } @@ -1800,13 +1865,35 @@ impl<'a, 'b> TypeChecker<'a, 'b> { } => { let (f_name, map_type) = self.type_check_define_map(name, key_type, value_type)?; - let total_type_size = u64::from(map_type.0.type_size()?) - .cost_overflow_add(u64::from(map_type.1.type_size()?))?; + let total_type_size = u64::from( + map_type + .0 + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, + ) + .cost_overflow_add(u64::from( + map_type + .1 + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, + ))?; runtime_cost(ClarityCostFunction::AnalysisBindName, self, total_type_size)?; if self.epoch.analysis_memory() { self.add_memory(f_name.len().into())?; - self.add_memory(map_type.0.type_size()?.into())?; - self.add_memory(map_type.1.type_size()?.into())?; + self.add_memory( + map_type + .0 + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)? + .into(), + )?; + self.add_memory( + map_type + .1 + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)? + .into(), + )?; } self.contract_context.add_map_type(f_name, map_type)?; } @@ -1820,11 +1907,18 @@ impl<'a, 'b> TypeChecker<'a, 'b> { runtime_cost( ClarityCostFunction::AnalysisBindName, self, - v_type.type_size()?, + v_type + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, )?; if self.epoch.analysis_memory() { self.add_memory(v_name.len().into())?; - self.add_memory(v_type.type_size()?.into())?; + self.add_memory( + v_type + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)? + .into(), + )?; } self.contract_context .add_persisted_variable_type(v_name, v_type)?; @@ -1834,11 +1928,18 @@ impl<'a, 'b> TypeChecker<'a, 'b> { runtime_cost( ClarityCostFunction::AnalysisBindName, self, - TypeSignature::UIntType.type_size()?, + TypeSignature::UIntType + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, )?; if self.epoch.analysis_memory() { self.add_memory(token_name.len().into())?; - self.add_memory(TypeSignature::UIntType.type_size()?.into())?; + self.add_memory( + TypeSignature::UIntType + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)? + .into(), + )?; } self.contract_context.add_ft(token_name)?; } @@ -1847,11 +1948,18 @@ impl<'a, 'b> TypeChecker<'a, 'b> { runtime_cost( ClarityCostFunction::AnalysisBindName, self, - TypeSignature::UIntType.type_size()?, + TypeSignature::UIntType + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, )?; if self.epoch.analysis_memory() { self.add_memory(token_name.len().into())?; - self.add_memory(TypeSignature::UIntType.type_size()?.into())?; + self.add_memory( + TypeSignature::UIntType + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)? + .into(), + )?; } self.contract_context.add_ft(token_name)?; } @@ -1861,11 +1969,18 @@ impl<'a, 'b> TypeChecker<'a, 'b> { runtime_cost( ClarityCostFunction::AnalysisBindName, self, - token_type.type_size()?, + token_type + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, )?; if self.epoch.analysis_memory() { self.add_memory(token_name.len().into())?; - self.add_memory(token_type.type_size()?.into())?; + self.add_memory( + token_type + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)? + .into(), + )?; } self.contract_context.add_nft(token_name, token_type)?; } diff --git a/clarity/src/vm/analysis/type_checker/v2_1/natives/assets.rs b/clarity/src/vm/analysis/type_checker/v2_1/natives/assets.rs index c00c7aaeea..e4adcaccd2 100644 --- a/clarity/src/vm/analysis/type_checker/v2_1/natives/assets.rs +++ b/clarity/src/vm/analysis/type_checker/v2_1/natives/assets.rs @@ -43,7 +43,9 @@ pub fn check_special_get_owner( runtime_cost( ClarityCostFunction::AnalysisTypeLookup, checker, - expected_asset_type.type_size()?, + expected_asset_type + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, )?; checker.type_check_expects(&args[1], context, &expected_asset_type)?; @@ -97,7 +99,9 @@ pub fn check_special_mint_asset( runtime_cost( ClarityCostFunction::AnalysisTypeLookup, checker, - expected_asset_type.type_size()?, + expected_asset_type + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, )?; checker.type_check_expects(&args[1], context, &expected_asset_type)?; @@ -159,7 +163,9 @@ pub fn check_special_transfer_asset( runtime_cost( ClarityCostFunction::AnalysisTypeLookup, checker, - expected_asset_type.type_size()?, + expected_asset_type + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, )?; checker.type_check_expects(&args[1], context, &expected_asset_type)?; @@ -237,7 +243,7 @@ pub fn check_special_stx_transfer_memo( let to_type: TypeSignature = TypeSignature::PrincipalType; let memo_type: TypeSignature = TypeSignature::SequenceType(SequenceSubtype::BufferType( BufferLength::try_from(TOKEN_TRANSFER_MEMO_LENGTH as u32) - .map_err(|_| StaticCheckErrorKind::Expects("Bad constructor".into()))?, + .map_err(|_| StaticCheckErrorKind::ExpectsRejectable("Bad constructor".into()))?, )); runtime_cost(ClarityCostFunction::AnalysisTypeLookup, checker, 0)?; @@ -294,7 +300,9 @@ pub fn check_special_burn_asset( runtime_cost( ClarityCostFunction::AnalysisTypeLookup, checker, - expected_asset_type.type_size()?, + expected_asset_type + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, )?; checker.type_check_expects(&args[1], context, &expected_asset_type)?; diff --git a/clarity/src/vm/analysis/type_checker/v2_1/natives/conversions.rs b/clarity/src/vm/analysis/type_checker/v2_1/natives/conversions.rs index 3ad5fc72b5..2d7fc62849 100644 --- a/clarity/src/vm/analysis/type_checker/v2_1/natives/conversions.rs +++ b/clarity/src/vm/analysis/type_checker/v2_1/natives/conversions.rs @@ -21,11 +21,16 @@ pub fn check_special_to_consensus_buff( ) -> Result { check_argument_count(1, args)?; let input_type = checker.type_check(&args[0], context)?; - let buffer_max_len = BufferLength::try_from(input_type.max_serialized_size()?)?; + let buffer_max_len = BufferLength::try_from( + input_type + .max_serialized_size() + .map_err(StaticCheckError::from_clarity_type_error)?, + ) + .map_err(StaticCheckError::from_clarity_type_error)?; TypeSignature::new_option(TypeSignature::SequenceType(SequenceSubtype::BufferType( buffer_max_len, ))) - .map_err(StaticCheckError::from) + .map_err(StaticCheckError::from_clarity_type_error) } /// `from-consensus-buff?` admits exactly two arguments: @@ -41,7 +46,7 @@ pub fn check_special_from_consensus_buff( check_argument_count(2, args)?; let result_type = TypeSignature::parse_type_repr(StacksEpochId::Epoch21, &args[0], checker)?; checker.type_check_expects(&args[1], context, &TypeSignature::BUFFER_MAX)?; - TypeSignature::new_option(result_type).map_err(StaticCheckError::from) + TypeSignature::new_option(result_type).map_err(StaticCheckError::from_clarity_type_error) } /// `to-ascii?` admits exactly one argument, a value to convert to a @@ -77,11 +82,13 @@ pub fn check_special_to_ascii( if u32::from(len.clone()) <= MAX_TO_ASCII_BUFFER_LEN => { // Each byte in the buffer becomes two ASCII characters, plus "0x" prefix - TypeSignature::new_ascii_type((u32::from(len) * 2 + 2).into())? + TypeSignature::new_ascii_type((u32::from(len) * 2 + 2).into()) + .map_err(StaticCheckError::from_clarity_type_error)? } TypeSignature::SequenceType(SequenceSubtype::StringType(StringSubtype::UTF8(len))) => { // Each UTF-8 character is exactly one ASCII character - TypeSignature::new_ascii_type(u32::from(len).into())? + TypeSignature::new_ascii_type(u32::from(len).into()) + .map_err(StaticCheckError::from_clarity_type_error)? } _ => { let types = vec![ @@ -97,7 +104,7 @@ pub fn check_special_to_ascii( }; Ok( TypeSignature::new_response(result_type, TypeSignature::UIntType).map_err(|_| { - StaticCheckErrorKind::Expects( + StaticCheckErrorKind::ExpectsRejectable( "FATAL: Legal Clarity response type marked invalid".into(), ) })?, diff --git a/clarity/src/vm/analysis/type_checker/v2_1/natives/maps.rs b/clarity/src/vm/analysis/type_checker/v2_1/natives/maps.rs index 26e44b9aea..27fe4ab13e 100644 --- a/clarity/src/vm/analysis/type_checker/v2_1/natives/maps.rs +++ b/clarity/src/vm/analysis/type_checker/v2_1/natives/maps.rs @@ -45,18 +45,26 @@ pub fn check_special_fetch_entry( runtime_cost( ClarityCostFunction::AnalysisTypeLookup, &mut checker.cost_track, - expected_key_type.type_size()?, + expected_key_type + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, )?; runtime_cost( ClarityCostFunction::AnalysisTypeLookup, &mut checker.cost_track, - value_type.type_size()?, + value_type + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, )?; analysis_typecheck_cost(&mut checker.cost_track, expected_key_type, &key_type)?; - let option_type = TypeSignature::new_option(value_type.clone())?; + let option_type = TypeSignature::new_option(value_type.clone()) + .map_err(StaticCheckError::from_clarity_type_error)?; - if !expected_key_type.admits_type(&StacksEpochId::Epoch21, &key_type)? { + if !expected_key_type + .admits_type(&StacksEpochId::Epoch21, &key_type) + .map_err(StaticCheckError::from_clarity_type_error)? + { Err(StaticCheckError::new(StaticCheckErrorKind::TypeError( Box::new(expected_key_type.clone()), Box::new(key_type), @@ -87,11 +95,16 @@ pub fn check_special_delete_entry( runtime_cost( ClarityCostFunction::AnalysisTypeLookup, &mut checker.cost_track, - expected_key_type.type_size()?, + expected_key_type + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, )?; analysis_typecheck_cost(&mut checker.cost_track, expected_key_type, &key_type)?; - if !expected_key_type.admits_type(&StacksEpochId::Epoch21, &key_type)? { + if !expected_key_type + .admits_type(&StacksEpochId::Epoch21, &key_type) + .map_err(StaticCheckError::from_clarity_type_error)? + { Err(StaticCheckError::new(StaticCheckErrorKind::TypeError( Box::new(expected_key_type.clone()), Box::new(key_type), @@ -123,23 +136,33 @@ fn check_set_or_insert_entry( runtime_cost( ClarityCostFunction::AnalysisTypeLookup, &mut checker.cost_track, - expected_key_type.type_size()?, + expected_key_type + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, )?; runtime_cost( ClarityCostFunction::AnalysisTypeLookup, &mut checker.cost_track, - value_type.type_size()?, + value_type + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, )?; analysis_typecheck_cost(&mut checker.cost_track, expected_key_type, &key_type)?; analysis_typecheck_cost(&mut checker.cost_track, expected_value_type, &value_type)?; - if !expected_key_type.admits_type(&StacksEpochId::Epoch21, &key_type)? { + if !expected_key_type + .admits_type(&StacksEpochId::Epoch21, &key_type) + .map_err(StaticCheckError::from_clarity_type_error)? + { Err(StaticCheckError::new(StaticCheckErrorKind::TypeError( Box::new(expected_key_type.clone()), Box::new(key_type), ))) - } else if !expected_value_type.admits_type(&StacksEpochId::Epoch21, &value_type)? { + } else if !expected_value_type + .admits_type(&StacksEpochId::Epoch21, &value_type) + .map_err(StaticCheckError::from_clarity_type_error)? + { Err(StaticCheckError::new(StaticCheckErrorKind::TypeError( Box::new(expected_value_type.clone()), Box::new(value_type), diff --git a/clarity/src/vm/analysis/type_checker/v2_1/natives/mod.rs b/clarity/src/vm/analysis/type_checker/v2_1/natives/mod.rs index 2496897dac..05ef31e93e 100644 --- a/clarity/src/vm/analysis/type_checker/v2_1/natives/mod.rs +++ b/clarity/src/vm/analysis/type_checker/v2_1/natives/mod.rs @@ -72,7 +72,7 @@ fn check_special_list_cons( let checked = checker.type_check(arg, context)?; let cost = checked .type_size() - .map_err(StaticCheckErrorKind::from) + .map_err(StaticCheckErrorKind::from_clarity_type_error) .and_then(|ty_size| { checker .compute_cost( @@ -84,7 +84,11 @@ fn check_special_list_cons( costs.push(cost); if let Some(cur_size) = entries_size { - entries_size = cur_size.checked_add(checked.size()?); + entries_size = cur_size.checked_add( + checked + .size() + .map_err(StaticCheckError::from_clarity_type_error)?, + ); } if let Some(cur_size) = entries_size { if cur_size > MAX_VALUE_SIZE { @@ -104,7 +108,7 @@ fn check_special_list_cons( } let typed_args = result; TypeSignature::parent_list_type(&typed_args) - .map_err(|x| x.into()) + .map_err(StaticCheckError::from_clarity_type_error) .map(TypeSignature::from) } @@ -187,7 +191,8 @@ fn check_special_get( } else if let TypeSignature::OptionalType(value_type_sig) = argument_type { if let TypeSignature::TupleType(tuple_type_sig) = *value_type_sig { let inner_type = inner_handle_tuple_get(&tuple_type_sig, field_to_get, checker)?; - let option_type = TypeSignature::new_option(inner_type)?; + let option_type = TypeSignature::new_option(inner_type) + .map_err(StaticCheckError::from_clarity_type_error)?; Ok(option_type) } else { Err(StaticCheckErrorKind::ExpectedTuple(value_type_sig).into()) @@ -251,14 +256,24 @@ pub fn check_special_tuple_cons( runtime_cost( ClarityCostFunction::AnalysisTupleItemsCheck, checker, - var_type.type_size()?, + var_type + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, )?; if type_size < MAX_VALUE_SIZE { type_size = type_size .saturating_add(var_name.len() as u32) .saturating_add(var_name.len() as u32) - .saturating_add(var_type.type_size()?) - .saturating_add(var_type.size()?); + .saturating_add( + var_type + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, + ) + .saturating_add( + var_type + .size() + .map_err(StaticCheckError::from_clarity_type_error)?, + ); tuple_type_data.push((var_name.clone(), var_type)); } else { cons_error = Err(StaticCheckErrorKind::BadTupleConstruction(format!( @@ -272,7 +287,9 @@ pub fn check_special_tuple_cons( cons_error?; let tuple_signature = TupleTypeSignature::try_from(tuple_type_data).map_err(|e| { - StaticCheckErrorKind::BadTupleConstruction(StaticCheckErrorKind::from(e).message()) + StaticCheckErrorKind::BadTupleConstruction( + StaticCheckErrorKind::from_clarity_type_error(e).message(), + ) })?; Ok(TypeSignature::TupleType(tuple_signature)) @@ -311,11 +328,17 @@ fn check_special_let( runtime_cost( ClarityCostFunction::AnalysisBindName, checker, - typed_result.type_size()?, + typed_result + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, )?; if checker.epoch.analysis_memory() { let memory_use = u64::from(var_name.len()) - .checked_add(u64::from(typed_result.type_size()?)) + .checked_add(u64::from( + typed_result + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, + )) .ok_or_else(|| CostErrors::CostOverflow)?; added_memory = added_memory .checked_add(memory_use) @@ -355,7 +378,9 @@ fn check_special_fetch_var( runtime_cost( ClarityCostFunction::AnalysisTypeLookup, &mut checker.cost_track, - value_type.type_size()?, + value_type + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, )?; Ok(value_type.clone()) @@ -384,11 +409,16 @@ fn check_special_set_var( runtime_cost( ClarityCostFunction::AnalysisTypeLookup, &mut checker.cost_track, - expected_value_type.type_size()?, + expected_value_type + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, )?; analysis_typecheck_cost(&mut checker.cost_track, &value_type, expected_value_type)?; - if !expected_value_type.admits_type(&StacksEpochId::Epoch21, &value_type)? { + if !expected_value_type + .admits_type(&StacksEpochId::Epoch21, &value_type) + .map_err(StaticCheckError::from_clarity_type_error)? + { Err(StaticCheckError::new(StaticCheckErrorKind::TypeError( Box::new(expected_value_type.clone()), Box::new(value_type), @@ -431,7 +461,7 @@ fn check_special_equals( // check if there was a least supertype failure. arg_type.ok_or_else(|| { - StaticCheckErrorKind::Expects( + StaticCheckErrorKind::ExpectsRejectable( "Arg type should be set because arguments checked for >= 1".into(), ) })??; @@ -456,7 +486,6 @@ fn check_special_if( analysis_typecheck_cost(checker, expr1, expr2)?; TypeSignature::least_supertype(&StacksEpochId::Epoch21, expr1, expr2) - .map_err(StaticCheckErrorKind::from) .and_then(|t| t.concretize()) .map_err(|_| { StaticCheckErrorKind::IfArmsMustMatch(Box::new(expr1.clone()), Box::new(expr2.clone())) @@ -693,7 +722,7 @@ fn check_principal_of( checker.type_check_expects(&args[0], context, &TypeSignature::BUFFER_33)?; Ok( TypeSignature::new_response(TypeSignature::PrincipalType, TypeSignature::UIntType) - .map_err(|_| StaticCheckErrorKind::Expects("Bad constructor".into()))?, + .map_err(|_| StaticCheckErrorKind::ExpectsRejectable("Bad constructor".into()))?, ) } @@ -725,13 +754,13 @@ fn check_principal_construct( ("error_code".into(), TypeSignature::UIntType), ( "value".into(), - TypeSignature::new_option(TypeSignature::PrincipalType).map_err(|_| StaticCheckErrorKind::Expects("FATAL: failed to create (optional principal) type signature".into()))?, + TypeSignature::new_option(TypeSignature::PrincipalType).map_err(|_| StaticCheckErrorKind::ExpectsRejectable("FATAL: failed to create (optional principal) type signature".into()))?, ), ]) - .map_err(|_| StaticCheckErrorKind::Expects("FAIL: PrincipalConstruct failed to initialize type signature".into()))? + .map_err(|_| StaticCheckErrorKind::ExpectsRejectable("FAIL: PrincipalConstruct failed to initialize type signature".into()))? .into() ) - .map_err(|_| StaticCheckErrorKind::Expects("FATAL: failed to create `(response principal { error_code: uint, principal: (optional principal) })` type signature".into()))? + .map_err(|_| StaticCheckErrorKind::ExpectsRejectable("FATAL: failed to create `(response principal { error_code: uint, principal: (optional principal) })` type signature".into()))? ) } @@ -745,7 +774,7 @@ fn check_secp256k1_recover( checker.type_check_expects(&args[1], context, &TypeSignature::BUFFER_65)?; Ok( TypeSignature::new_response(TypeSignature::BUFFER_33, TypeSignature::UIntType) - .map_err(|_| StaticCheckErrorKind::Expects("Bad constructor".into()))?, + .map_err(|_| StaticCheckErrorKind::ExpectsRejectable("Bad constructor".into()))?, ) } @@ -792,7 +821,8 @@ fn check_get_block_info( checker.type_check_expects(&args[1], context, &TypeSignature::UIntType)?; - Ok(TypeSignature::new_option(block_info_prop.type_result())?) + TypeSignature::new_option(block_info_prop.type_result()) + .map_err(StaticCheckError::from_clarity_type_error) } // # Errors @@ -816,11 +846,10 @@ fn check_get_burn_block_info( checker.type_check_expects(&args[1], context, &TypeSignature::UIntType)?; - Ok(TypeSignature::new_option( - block_info_prop.type_result().map_err(|_| { - StaticCheckErrorKind::Expects("FAILED to type valid burn info property".into()) - })?, - )?) + TypeSignature::new_option(block_info_prop.type_result().map_err(|_| { + StaticCheckErrorKind::ExpectsRejectable("FAILED to type valid burn info property".into()) + })?) + .map_err(StaticCheckError::from_clarity_type_error) } fn check_get_stacks_block_info( @@ -842,7 +871,8 @@ fn check_get_stacks_block_info( checker.type_check_expects(&args[1], context, &TypeSignature::UIntType)?; - Ok(TypeSignature::new_option(block_info_prop.type_result())?) + TypeSignature::new_option(block_info_prop.type_result()) + .map_err(StaticCheckError::from_clarity_type_error) } fn check_get_tenure_info( @@ -863,7 +893,8 @@ fn check_get_tenure_info( checker.type_check_expects(&args[1], context, &TypeSignature::UIntType)?; - Ok(TypeSignature::new_option(block_info_prop.type_result())?) + TypeSignature::new_option(block_info_prop.type_result()) + .map_err(StaticCheckError::from_clarity_type_error) } impl TypedNativeFunction { @@ -917,7 +948,7 @@ impl TypedNativeFunction { args: vec![FunctionArg::new( TypeSignature::IntType, ClarityName::try_from("value".to_owned()).map_err(|_| { - StaticCheckErrorKind::Expects( + StaticCheckErrorKind::ExpectsRejectable( "FAIL: ClarityName failed to accept default arg name".into(), ) })?, @@ -928,7 +959,7 @@ impl TypedNativeFunction { args: vec![FunctionArg::new( TypeSignature::UIntType, ClarityName::try_from("value".to_owned()).map_err(|_| { - StaticCheckErrorKind::Expects( + StaticCheckErrorKind::ExpectsRejectable( "FAIL: ClarityName failed to accept default arg name".into(), ) })?, @@ -939,7 +970,7 @@ impl TypedNativeFunction { args: vec![FunctionArg::new( TypeSignature::PrincipalType, ClarityName::try_from("value".to_owned()).map_err(|_| { - StaticCheckErrorKind::Expects( + StaticCheckErrorKind::ExpectsRejectable( "FAIL: ClarityName failed to accept default arg name".into(), ) })?, @@ -951,11 +982,11 @@ impl TypedNativeFunction { args: vec![FunctionArg::new( TypeSignature::SequenceType(SequenceSubtype::BufferType( BufferLength::try_from(16_u32).map_err(|_| { - StaticCheckErrorKind::Expects("Bad constructor".into()) + StaticCheckErrorKind::ExpectsRejectable("Bad constructor".into()) })?, )), ClarityName::try_from("value".to_owned()).map_err(|_| { - StaticCheckErrorKind::Expects( + StaticCheckErrorKind::ExpectsRejectable( "FAIL: ClarityName failed to accept default arg name".into(), ) })?, @@ -968,11 +999,11 @@ impl TypedNativeFunction { args: vec![FunctionArg::new( TypeSignature::SequenceType(SequenceSubtype::BufferType( BufferLength::try_from(16_u32).map_err(|_| { - StaticCheckErrorKind::Expects("Bad constructor".into()) + StaticCheckErrorKind::ExpectsRejectable("Bad constructor".into()) })?, )), ClarityName::try_from("value".to_owned()).map_err(|_| { - StaticCheckErrorKind::Expects( + StaticCheckErrorKind::ExpectsRejectable( "FAIL: ClarityName failed to accept default arg name".into(), ) })?, @@ -1008,7 +1039,7 @@ impl TypedNativeFunction { args: vec![FunctionArg::new( TypeSignature::BoolType, ClarityName::try_from("value".to_owned()).map_err(|_| { - StaticCheckErrorKind::Expects( + StaticCheckErrorKind::ExpectsRejectable( "FAIL: ClarityName failed to accept default arg name".into(), ) })?, @@ -1061,7 +1092,7 @@ impl TypedNativeFunction { args: vec![FunctionArg::new( TypeSignature::PrincipalType, ClarityName::try_from("owner".to_owned()).map_err(|_| { - StaticCheckErrorKind::Expects( + StaticCheckErrorKind::ExpectsRejectable( "FAIL: ClarityName failed to accept default arg name".into(), ) })?, @@ -1073,7 +1104,7 @@ impl TypedNativeFunction { args: vec![FunctionArg::new( TypeSignature::PrincipalType, ClarityName::try_from("principal".to_owned()).map_err(|_| { - StaticCheckErrorKind::Expects( + StaticCheckErrorKind::ExpectsRejectable( "FAIL: ClarityName failed to accept default arg name".into(), ) })?, @@ -1092,12 +1123,14 @@ impl TypedNativeFunction { TypeSignature::CONTRACT_NAME_STRING_ASCII_MAX, ) .map_err(|_| { - StaticCheckErrorKind::Expects("Bad constructor".into()) + StaticCheckErrorKind::ExpectsRejectable( + "Bad constructor".into(), + ) })?, ), ]) .map_err(|_| { - StaticCheckErrorKind::Expects( + StaticCheckErrorKind::ExpectsRejectable( "FAIL: PrincipalDestruct failed to initialize type signature" .into(), ) @@ -1113,7 +1146,7 @@ impl TypedNativeFunction { args: vec![FunctionArg::new( TypeSignature::PrincipalType, ClarityName::try_from("owner".to_owned()).map_err(|_| { - StaticCheckErrorKind::Expects( + StaticCheckErrorKind::ExpectsRejectable( "FAIL: ClarityName failed to accept default arg name".into(), ) })?, @@ -1124,7 +1157,7 @@ impl TypedNativeFunction { ("unlock-height".into(), TypeSignature::UIntType), ]) .map_err(|_| { - StaticCheckErrorKind::Expects( + StaticCheckErrorKind::ExpectsRejectable( "FAIL: StxGetAccount failed to initialize type signature".into(), ) })? @@ -1135,7 +1168,7 @@ impl TypedNativeFunction { FunctionArg::new( TypeSignature::UIntType, ClarityName::try_from("amount".to_owned()).map_err(|_| { - StaticCheckErrorKind::Expects( + StaticCheckErrorKind::ExpectsRejectable( "FAIL: ClarityName failed to accept default arg name".into(), ) })?, @@ -1143,7 +1176,7 @@ impl TypedNativeFunction { FunctionArg::new( TypeSignature::PrincipalType, ClarityName::try_from("sender".to_owned()).map_err(|_| { - StaticCheckErrorKind::Expects( + StaticCheckErrorKind::ExpectsRejectable( "FAIL: ClarityName failed to accept default arg name".into(), ) })?, @@ -1153,7 +1186,7 @@ impl TypedNativeFunction { TypeSignature::BoolType, TypeSignature::UIntType, ) - .map_err(|_| StaticCheckErrorKind::Expects("Bad constructor".into()))?, + .map_err(|_| StaticCheckErrorKind::ExpectsRejectable("Bad constructor".into()))?, }))), StxTransfer => Special(SpecialNativeFunction(&assets::check_special_stx_transfer)), StxTransferMemo => Special(SpecialNativeFunction( @@ -1236,7 +1269,7 @@ impl TypedNativeFunction { args: vec![FunctionArg::new( TypeSignature::PrincipalType, ClarityName::try_from("contract".to_owned()).map_err(|_| { - StaticCheckErrorKind::Expects( + StaticCheckErrorKind::ExpectsRejectable( "FAIL: ClarityName failed to accept default arg name".into(), ) })?, @@ -1245,7 +1278,7 @@ impl TypedNativeFunction { TypeSignature::BUFFER_32, TypeSignature::UIntType, ) - .map_err(|_| StaticCheckErrorKind::Expects("Bad constructor".into()))?, + .map_err(|_| StaticCheckErrorKind::ExpectsRejectable("Bad constructor".into()))?, }))), ToAscii => Special(SpecialNativeFunction(&conversions::check_special_to_ascii)), RestrictAssets => Special(SpecialNativeFunction( diff --git a/clarity/src/vm/analysis/type_checker/v2_1/natives/options.rs b/clarity/src/vm/analysis/type_checker/v2_1/natives/options.rs index 57406396d7..e232ccbf4a 100644 --- a/clarity/src/vm/analysis/type_checker/v2_1/natives/options.rs +++ b/clarity/src/vm/analysis/type_checker/v2_1/natives/options.rs @@ -37,7 +37,8 @@ pub fn check_special_okay( runtime_cost(ClarityCostFunction::AnalysisOptionCons, checker, 0)?; let inner_type = checker.type_check(&args[0], context)?; - let resp_type = TypeSignature::new_response(inner_type, no_type())?; + let resp_type = TypeSignature::new_response(inner_type, no_type()) + .map_err(StaticCheckError::from_clarity_type_error)?; Ok(resp_type) } @@ -51,7 +52,8 @@ pub fn check_special_some( runtime_cost(ClarityCostFunction::AnalysisOptionCons, checker, 0)?; let inner_type = checker.type_check(&args[0], context)?; - let resp_type = TypeSignature::new_option(inner_type)?; + let resp_type = + TypeSignature::new_option(inner_type).map_err(StaticCheckError::from_clarity_type_error)?; Ok(resp_type) } @@ -65,7 +67,8 @@ pub fn check_special_error( runtime_cost(ClarityCostFunction::AnalysisOptionCons, checker, 0)?; let inner_type = checker.type_check(&args[0], context)?; - let resp_type = TypeSignature::new_response(no_type(), inner_type)?; + let resp_type = TypeSignature::new_response(no_type(), inner_type) + .map_err(StaticCheckError::from_clarity_type_error)?; Ok(resp_type) } @@ -238,7 +241,10 @@ pub fn check_special_try_ret( if input_type.is_no_type() { Err(StaticCheckErrorKind::CouldNotDetermineResponseOkType.into()) } else { - checker.track_return_type(TypeSignature::new_option(TypeSignature::NoType)?)?; + checker.track_return_type( + TypeSignature::new_option(TypeSignature::NoType) + .map_err(StaticCheckError::from_clarity_type_error)?, + )?; Ok(*input_type) } } @@ -249,10 +255,10 @@ pub fn check_special_try_ret( } else if err_type.is_no_type() { Err(StaticCheckErrorKind::CouldNotDetermineResponseErrType.into()) } else { - checker.track_return_type(TypeSignature::new_response( - TypeSignature::NoType, - err_type, - )?)?; + checker.track_return_type( + TypeSignature::new_response(TypeSignature::NoType, err_type) + .map_err(StaticCheckError::from_clarity_type_error)?, + )?; Ok(ok_type) } } @@ -297,12 +303,18 @@ fn eval_with_new_binding( runtime_cost( ClarityCostFunction::AnalysisBindName, checker, - bind_type.type_size()?, + bind_type + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, )?; let mut memory_use = 0; if checker.epoch.analysis_memory() { memory_use = u64::from(bind_name.len()) - .checked_add(u64::from(bind_type.type_size()?)) + .checked_add(u64::from( + bind_type + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, + )) .ok_or_else(|| CostErrors::CostOverflow)?; checker.add_memory(memory_use)?; } diff --git a/clarity/src/vm/analysis/type_checker/v2_1/natives/post_conditions.rs b/clarity/src/vm/analysis/type_checker/v2_1/natives/post_conditions.rs index 4288820fd6..1bf9364d47 100644 --- a/clarity/src/vm/analysis/type_checker/v2_1/natives/post_conditions.rs +++ b/clarity/src/vm/analysis/type_checker/v2_1/natives/post_conditions.rs @@ -86,10 +86,9 @@ pub fn check_restrict_assets( } let ok_type = last_return.ok_or_else(|| StaticCheckErrorKind::CheckerImplementationFailure)?; - Ok(TypeSignature::new_response( - ok_type, - TypeSignature::UIntType, - )?) + + TypeSignature::new_response(ok_type, TypeSignature::UIntType) + .map_err(StaticCheckError::from_clarity_type_error) } pub fn check_as_contract( @@ -140,10 +139,8 @@ pub fn check_as_contract( } let ok_type = last_return.ok_or_else(|| StaticCheckErrorKind::CheckerImplementationFailure)?; - Ok(TypeSignature::new_response( - ok_type, - TypeSignature::UIntType, - )?) + TypeSignature::new_response(ok_type, TypeSignature::UIntType) + .map_err(StaticCheckError::from_clarity_type_error) } /// Type-checking for allowance expressions. These are only allowed within the diff --git a/clarity/src/vm/analysis/type_checker/v2_1/natives/sequences.rs b/clarity/src/vm/analysis/type_checker/v2_1/natives/sequences.rs index ab81b4a5d2..6d31dbeb7b 100644 --- a/clarity/src/vm/analysis/type_checker/v2_1/natives/sequences.rs +++ b/clarity/src/vm/analysis/type_checker/v2_1/natives/sequences.rs @@ -283,29 +283,40 @@ pub fn check_special_concat( &StacksEpochId::Epoch21, lhs_entry_type, rhs_entry_type, - )?; + ) + .map_err(StaticCheckError::from_clarity_type_error)?; let new_len = lhs_max_len .checked_add(rhs_max_len) .ok_or(StaticCheckErrorKind::MaxLengthOverflow)?; - TypeSignature::list_of(list_entry_type, new_len)? + TypeSignature::list_of(list_entry_type, new_len) + .map_err(StaticCheckError::from_clarity_type_error)? } (BufferType(lhs_len), BufferType(rhs_len)) => { let size: u32 = u32::from(lhs_len) .checked_add(u32::from(rhs_len)) .ok_or(StaticCheckErrorKind::MaxLengthOverflow)?; - TypeSignature::SequenceType(BufferType(size.try_into()?)) + TypeSignature::SequenceType(BufferType( + size.try_into() + .map_err(StaticCheckError::from_clarity_type_error)?, + )) } (StringType(ASCII(lhs_len)), StringType(ASCII(rhs_len))) => { let size: u32 = u32::from(lhs_len) .checked_add(u32::from(rhs_len)) .ok_or(StaticCheckErrorKind::MaxLengthOverflow)?; - TypeSignature::SequenceType(StringType(ASCII(size.try_into()?))) + TypeSignature::SequenceType(StringType(ASCII( + size.try_into() + .map_err(StaticCheckError::from_clarity_type_error)?, + ))) } (StringType(UTF8(lhs_len)), StringType(UTF8(rhs_len))) => { let size: u32 = u32::from(lhs_len) .checked_add(u32::from(rhs_len)) .ok_or(StaticCheckErrorKind::MaxLengthOverflow)?; - TypeSignature::SequenceType(StringType(UTF8(size.try_into()?))) + TypeSignature::SequenceType(StringType(UTF8( + size.try_into() + .map_err(StaticCheckError::from_clarity_type_error)?, + ))) } (_, _) => { return Err(StaticCheckErrorKind::TypeError( @@ -338,15 +349,14 @@ pub fn check_special_append( analysis_typecheck_cost(checker, &lhs_entry_type, &rhs_type)?; - let list_entry_type = TypeSignature::least_supertype( - &StacksEpochId::Epoch21, - &lhs_entry_type, - &rhs_type, - )?; + let list_entry_type = + TypeSignature::least_supertype(&StacksEpochId::Epoch21, &lhs_entry_type, &rhs_type) + .map_err(StaticCheckError::from_clarity_type_error)?; let new_len = lhs_max_len .checked_add(1) .ok_or(StaticCheckErrorKind::MaxLengthOverflow)?; - let return_type = TypeSignature::list_of(list_entry_type, new_len)?; + let return_type = TypeSignature::list_of(list_entry_type, new_len) + .map_err(StaticCheckError::from_clarity_type_error)?; Ok(return_type) } _ => Err(StaticCheckErrorKind::ExpectedListApplication.into()), @@ -374,7 +384,9 @@ pub fn check_special_as_max_len( runtime_cost( ClarityCostFunction::AnalysisTypeAnnotate, checker, - TypeSignature::UIntType.type_size()?, + TypeSignature::UIntType + .type_size() + .map_err(StaticCheckError::from_clarity_type_error)?, )?; checker .type_map @@ -389,22 +401,28 @@ pub fn check_special_as_max_len( match sequence { TypeSignature::SequenceType(ListType(list)) => { let (lhs_entry_type, _) = list.destruct(); - let resized_list = ListTypeData::new_list(lhs_entry_type, expected_len)?; + let resized_list = ListTypeData::new_list(lhs_entry_type, expected_len) + .map_err(StaticCheckError::from_clarity_type_error)?; Ok(TypeSignature::OptionalType(Box::new( TypeSignature::SequenceType(ListType(resized_list)), ))) } TypeSignature::SequenceType(BufferType(_)) => Ok(TypeSignature::OptionalType(Box::new( - TypeSignature::SequenceType(BufferType(BufferLength::try_from(expected_len)?)), + TypeSignature::SequenceType(BufferType( + BufferLength::try_from(expected_len) + .map_err(StaticCheckError::from_clarity_type_error)?, + )), ))), TypeSignature::SequenceType(StringType(ASCII(_))) => Ok(TypeSignature::OptionalType( Box::new(TypeSignature::SequenceType(StringType(ASCII( - BufferLength::try_from(expected_len)?, + BufferLength::try_from(expected_len) + .map_err(StaticCheckError::from_clarity_type_error)?, )))), )), TypeSignature::SequenceType(StringType(UTF8(_))) => Ok(TypeSignature::OptionalType( Box::new(TypeSignature::SequenceType(StringType(UTF8( - StringUTF8Length::try_from(expected_len)?, + StringUTF8Length::try_from(expected_len) + .map_err(StaticCheckError::from_clarity_type_error)?, )))), )), _ => Err(StaticCheckErrorKind::ExpectedSequence(Box::new(sequence)).into()), @@ -446,21 +464,23 @@ pub fn check_special_element_at( match collection_type { TypeSignature::SequenceType(ListType(list)) => { let (entry_type, _) = list.destruct(); - TypeSignature::new_option(entry_type).map_err(|e| e.into()) + TypeSignature::new_option(entry_type).map_err(StaticCheckError::from_clarity_type_error) } TypeSignature::SequenceType(BufferType(_)) => Ok(TypeSignature::OptionalType(Box::new( TypeSignature::BUFFER_1, ))), TypeSignature::SequenceType(StringType(ASCII(_))) => Ok(TypeSignature::OptionalType( Box::new(TypeSignature::SequenceType(StringType(ASCII( - BufferLength::try_from(1u32) - .map_err(|_| StaticCheckErrorKind::Expects("Bad constructor".into()))?, + BufferLength::try_from(1u32).map_err(|_| { + StaticCheckErrorKind::ExpectsRejectable("Bad constructor".into()) + })?, )))), )), TypeSignature::SequenceType(StringType(UTF8(_))) => Ok(TypeSignature::OptionalType( Box::new(TypeSignature::SequenceType(StringType(UTF8( - StringUTF8Length::try_from(1u32) - .map_err(|_| StaticCheckErrorKind::Expects("Bad constructor".into()))?, + StringUTF8Length::try_from(1u32).map_err(|_| { + StaticCheckErrorKind::ExpectsRejectable("Bad constructor".into()) + })?, )))), )), _ => Err(StaticCheckErrorKind::ExpectedSequence(Box::new(collection_type)).into()), @@ -484,7 +504,8 @@ pub fn check_special_index_of( checker.type_check_expects(&args[1], context, &expected_input_type)?; - TypeSignature::new_option(TypeSignature::UIntType).map_err(|e| e.into()) + TypeSignature::new_option(TypeSignature::UIntType) + .map_err(StaticCheckError::from_clarity_type_error) } /// This function type checks the Clarity2 function `slice?`. @@ -500,7 +521,8 @@ pub fn check_special_slice( let seq_type = checker.type_check(&args[0], context)?; let seq = match seq_type { TypeSignature::SequenceType(seq) => { - TypeSignature::new_option(TypeSignature::SequenceType(seq))? + TypeSignature::new_option(TypeSignature::SequenceType(seq)) + .map_err(StaticCheckError::from_clarity_type_error)? } _ => return Err(StaticCheckErrorKind::ExpectedSequence(Box::new(seq_type)).into()), }; @@ -534,6 +556,7 @@ pub fn check_special_replace_at( // Check element argument checker.type_check_expects(&args[2], context, &unit_seq)?; - let final_type = TypeSignature::new_option(input_type)?; + let final_type = + TypeSignature::new_option(input_type).map_err(StaticCheckError::from_clarity_type_error)?; Ok(final_type) } diff --git a/clarity/src/vm/analysis/types.rs b/clarity/src/vm/analysis/types.rs index 58f08e8de9..5f6e341b50 100644 --- a/clarity/src/vm/analysis/types.rs +++ b/clarity/src/vm/analysis/types.rs @@ -249,7 +249,11 @@ impl ContractAnalysis { .into()); } - if !expected_sig.returns.admits_type(epoch, &func.returns)? { + if !expected_sig + .returns + .admits_type(epoch, &func.returns) + .map_err(StaticCheckError::from_clarity_type_error)? + { return Err(StaticCheckErrorKind::BadTraitImplementation( trait_name, func_name.to_string(), diff --git a/clarity/src/vm/callables.rs b/clarity/src/vm/callables.rs index 052c7d6dc0..d3f4775a2c 100644 --- a/clarity/src/vm/callables.rs +++ b/clarity/src/vm/callables.rs @@ -161,14 +161,21 @@ impl DefinedFunction { if env.epoch().uses_arg_size_for_cost() { for arg in args.iter() { - runtime_cost(ClarityCostFunction::InnerTypeCheckCost, env, arg.size()?)?; + runtime_cost( + ClarityCostFunction::InnerTypeCheckCost, + env, + arg.size() + .map_err(CheckErrorKind::from_clarity_type_error)?, + )?; } } else { for arg_type in self.arg_types.iter() { runtime_cost( ClarityCostFunction::InnerTypeCheckCost, env, - arg_type.size()?, + arg_type + .size() + .map_err(CheckErrorKind::from_clarity_type_error)?, )?; } } @@ -245,7 +252,10 @@ impl DefinedFunction { ); } _ => { - if !type_sig.admits(env.epoch(), value)? { + if !type_sig + .admits(env.epoch(), value) + .map_err(CheckErrorKind::from_clarity_type_error)? + { return Err(CheckErrorKind::TypeValueError( Box::new(type_sig.clone()), Box::new(value.clone()), @@ -290,7 +300,10 @@ impl DefinedFunction { ); } _ => { - if !type_sig.admits(env.epoch(), &cast_value)? { + if !type_sig + .admits(env.epoch(), &cast_value) + .map_err(CheckErrorKind::from_clarity_type_error)? + { return Err(CheckErrorKind::TypeValueError( Box::new(type_sig.clone()), Box::new(cast_value), @@ -454,7 +467,8 @@ fn clarity2_implicit_cast( let cast_list_type_data = ListTypeData::new_list( list_type.get_list_item_type().clone(), type_signature.get_max_len(), - )?; + ) + .map_err(CheckErrorKind::from_clarity_type_error)?; Value::Sequence(SequenceData::List(ListData { data: values, type_signature: cast_list_type_data, diff --git a/clarity/src/vm/clarity.rs b/clarity/src/vm/clarity.rs index 7cdb1f2a01..faee73c6fb 100644 --- a/clarity/src/vm/clarity.rs +++ b/clarity/src/vm/clarity.rs @@ -297,15 +297,15 @@ pub trait TransactionConnection: ClarityConnection { let result = db.insert_contract(identifier, contract_analysis); match result { Ok(_) => { - let result = db - .commit() - .map_err(|e| StaticCheckErrorKind::Expects(format!("{e:?}")).into()); + let result = db.commit().map_err(|e| { + StaticCheckErrorKind::ExpectsRejectable(format!("{e:?}")).into() + }); (cost_tracker, result) } Err(e) => { - let result = db - .roll_back() - .map_err(|e| StaticCheckErrorKind::Expects(format!("{e:?}")).into()); + let result = db.roll_back().map_err(|e| { + StaticCheckErrorKind::ExpectsRejectable(format!("{e:?}")).into() + }); if result.is_err() { (cost_tracker, result) } else { diff --git a/clarity/src/vm/contexts.rs b/clarity/src/vm/contexts.rs index e385c2b99a..cc543455b1 100644 --- a/clarity/src/vm/contexts.rs +++ b/clarity/src/vm/contexts.rs @@ -1185,7 +1185,7 @@ impl<'a, 'b, 'hooks> Environment<'a, 'b, 'hooks> { .ok_or_else(|| VmInternalError::InvariantViolation(format!("Passed non-value expression to exec_tx on {tx_name}!")))?; // sanitize contract-call inputs in epochs >= 2.4 // testing todo: ensure sanitize_value() preserves trait callability! - let expected_type = TypeSignature::type_of(value)?; + let expected_type = TypeSignature::type_of(value).map_err(CheckErrorKind::from_clarity_type_error)?; let (sanitized_value, _) = Value::sanitize_value( self.epoch(), &expected_type, @@ -1402,7 +1402,11 @@ impl<'a, 'b, 'hooks> Environment<'a, 'b, 'hooks> { self.global_context.begin(); let result = stx_transfer_consolidated(self, from, to, amount, memo); match result { - Ok(value) => match value.clone().expect_result()? { + Ok(value) => match value + .clone() + .expect_result() + .map_err(|_| VmInternalError::Expect("Expected result".into()))? + { Ok(_) => { self.global_context.commit()?; Ok(value) @@ -1848,7 +1852,8 @@ impl<'a, 'hooks> GlobalContext<'a, 'hooks> { Ok(result) } else { Err(CheckErrorKind::PublicFunctionMustReturnResponse(Box::new( - TypeSignature::type_of(&result)?, + TypeSignature::type_of(&result) + .map_err(CheckErrorKind::from_clarity_type_error)?, )) .into()) } diff --git a/clarity/src/vm/database/clarity_db.rs b/clarity/src/vm/database/clarity_db.rs index 62e4ccd3f9..c2c6e34683 100644 --- a/clarity/src/vm/database/clarity_db.rs +++ b/clarity/src/vm/database/clarity_db.rs @@ -551,16 +551,23 @@ impl<'a> ClarityDatabase<'a> { .map_err(|e| VmInternalError::Expect(e.to_string()))? as u64; - let (sanitized_value, did_sanitize) = - Value::sanitize_value(epoch, &TypeSignature::type_of(&value)?, value) - .ok_or_else(|| CheckErrorKind::CouldNotDetermineType)?; + let (sanitized_value, did_sanitize) = Value::sanitize_value( + epoch, + &TypeSignature::type_of(&value).map_err(CheckErrorKind::from_clarity_type_error)?, + value, + ) + .ok_or_else(|| CheckErrorKind::CouldNotDetermineType)?; // if data needed to be sanitized *charge* for the unsanitized cost if did_sanitize { pre_sanitized_size = Some(value_size); } - sanitized_value.serialize_to_vec()? + sanitized_value + .serialize_to_vec() + .map_err(|_| VmInternalError::Expect("IOError filling byte buffer.".into()))? } else { - value.serialize_to_vec()? + value + .serialize_to_vec() + .map_err(|_| VmInternalError::Expect("IOError filling byte buffer.".into()))? }; let size = serialized.len() as u64; @@ -915,7 +922,11 @@ impl<'a> ClarityDatabase<'a> { "FATAL: failed to load ustx_liquid_supply Clarity key".into(), ) })? - .map(|v| v.value.expect_u128()) + .map(|v| { + v.value + .expect_u128() + .map_err(|_| VmInternalError::Expect("Expected u128".into())) + }) .transpose()? .unwrap_or(0)) } @@ -1452,7 +1463,9 @@ impl ClarityDatabase<'_> { "BUG: failed to decode serialized poison-microblock reporter".into(), ) })?; - let tuple_data = reporter_value.expect_tuple()?; + let tuple_data = reporter_value + .expect_tuple() + .map_err(|_| VmInternalError::Expect("Expected tuple".into()))?; let reporter_value = tuple_data .get("reporter") .map_err(|_| { @@ -1470,8 +1483,12 @@ impl ClarityDatabase<'_> { })? .to_owned(); - let reporter_principal = reporter_value.expect_principal()?; - let seq_u128 = seq_value.expect_u128()?; + let reporter_principal = reporter_value + .expect_principal() + .map_err(|_| VmInternalError::Expect("Expected principal".into()))?; + let seq_u128 = seq_value + .expect_u128() + .map_err(|_| VmInternalError::Expect("Expected u128".into()))?; let seq: u16 = seq_u128 .try_into() @@ -1556,7 +1573,8 @@ impl ClarityDatabase<'_> { ) -> Result { if !variable_descriptor .value_type - .admits(&self.get_clarity_epoch_version()?, &value)? + .admits(&self.get_clarity_epoch_version()?, &value) + .map_err(CheckErrorKind::from_clarity_type_error)? { return Err(CheckErrorKind::TypeValueError( Box::new(variable_descriptor.value_type.clone()), @@ -1676,7 +1694,9 @@ impl ClarityDatabase<'_> { Ok(ClarityDatabase::make_key_for_data_map_entry_serialized( contract_identifier, map_name, - &key_value.serialize_to_hex()?, + &key_value + .serialize_to_hex() + .map_err(|_| VmInternalError::Expect("IOError filling byte buffer.".into()))?, )) } @@ -1715,7 +1735,8 @@ impl ClarityDatabase<'_> { ) -> Result { if !map_descriptor .key_type - .admits(&self.get_clarity_epoch_version()?, key_value)? + .admits(&self.get_clarity_epoch_version()?, key_value) + .map_err(CheckErrorKind::from_clarity_type_error)? { return Err(CheckErrorKind::TypeValueError( Box::new(map_descriptor.key_type.clone()), @@ -1727,7 +1748,8 @@ impl ClarityDatabase<'_> { let key = ClarityDatabase::make_key_for_data_map_entry(contract_identifier, map_name, key_value)?; - let stored_type = TypeSignature::new_option(map_descriptor.value_type.clone())?; + let stored_type = TypeSignature::new_option(map_descriptor.value_type.clone()) + .map_err(CheckErrorKind::from_clarity_type_error)?; let result = self.get_value(&key, &stored_type, epoch)?; match result { @@ -1746,7 +1768,8 @@ impl ClarityDatabase<'_> { ) -> Result { if !map_descriptor .key_type - .admits(&self.get_clarity_epoch_version()?, key_value)? + .admits(&self.get_clarity_epoch_version()?, key_value) + .map_err(CheckErrorKind::from_clarity_type_error)? { return Err(CheckErrorKind::TypeValueError( Box::new(map_descriptor.key_type.clone()), @@ -1755,14 +1778,17 @@ impl ClarityDatabase<'_> { .into()); } - let key_serialized = key_value.serialize_to_hex()?; + let key_serialized = key_value + .serialize_to_hex() + .map_err(|_| VmInternalError::Expect("IOError filling byte buffer.".into()))?; let key = ClarityDatabase::make_key_for_data_map_entry_serialized( contract_identifier, map_name, &key_serialized, ); - let stored_type = TypeSignature::new_option(map_descriptor.value_type.clone())?; + let stored_type = TypeSignature::new_option(map_descriptor.value_type.clone()) + .map_err(CheckErrorKind::from_clarity_type_error)?; let result = self.get_value(&key, &stored_type, epoch)?; match result { @@ -1889,7 +1915,8 @@ impl ClarityDatabase<'_> { ) -> Result { if !map_descriptor .key_type - .admits(&self.get_clarity_epoch_version()?, &key_value)? + .admits(&self.get_clarity_epoch_version()?, &key_value) + .map_err(CheckErrorKind::from_clarity_type_error)? { return Err(CheckErrorKind::TypeValueError( Box::new(map_descriptor.key_type.clone()), @@ -1899,7 +1926,8 @@ impl ClarityDatabase<'_> { } if !map_descriptor .value_type - .admits(&self.get_clarity_epoch_version()?, &value)? + .admits(&self.get_clarity_epoch_version()?, &value) + .map_err(CheckErrorKind::from_clarity_type_error)? { return Err(CheckErrorKind::TypeValueError( Box::new(map_descriptor.value_type.clone()), @@ -1908,7 +1936,9 @@ impl ClarityDatabase<'_> { .into()); } - let key_serialized = key_value.serialize_to_hex()?; + let key_serialized = key_value + .serialize_to_hex() + .map_err(|_| VmInternalError::Expect("IOError filling byte buffer.".into()))?; let key_serialized_byte_len = byte_len_of_serialization(&key_serialized); let key = ClarityDatabase::make_key_for_quad( contract_identifier, @@ -1916,7 +1946,8 @@ impl ClarityDatabase<'_> { map_name, &key_serialized, ); - let stored_type = TypeSignature::new_option(map_descriptor.value_type.clone())?; + let stored_type = TypeSignature::new_option(map_descriptor.value_type.clone()) + .map_err(CheckErrorKind::from_clarity_type_error)?; if return_if_exists && self.data_map_entry_exists(&key, &stored_type, epoch)? { return Ok(ValueResult { @@ -1925,7 +1956,7 @@ impl ClarityDatabase<'_> { }); } - let placed_value = Value::some(value)?; + let placed_value = Value::some(value).map_err(CheckErrorKind::from_clarity_type_error)?; let placed_size = self.put_value_with_size(&key, placed_value, epoch)?; Ok(ValueResult { @@ -1948,7 +1979,8 @@ impl ClarityDatabase<'_> { ) -> Result { if !map_descriptor .key_type - .admits(&self.get_clarity_epoch_version()?, key_value)? + .admits(&self.get_clarity_epoch_version()?, key_value) + .map_err(CheckErrorKind::from_clarity_type_error)? { return Err(CheckErrorKind::TypeValueError( Box::new(map_descriptor.key_type.clone()), @@ -1957,7 +1989,9 @@ impl ClarityDatabase<'_> { .into()); } - let key_serialized = key_value.serialize_to_hex()?; + let key_serialized = key_value + .serialize_to_hex() + .map_err(|_| VmInternalError::Expect("IOError filling byte buffer.".into()))?; let key_serialized_byte_len = byte_len_of_serialization(&key_serialized); let key = ClarityDatabase::make_key_for_quad( contract_identifier, @@ -1965,7 +1999,8 @@ impl ClarityDatabase<'_> { map_name, &key_serialized, ); - let stored_type = TypeSignature::new_option(map_descriptor.value_type.clone())?; + let stored_type = TypeSignature::new_option(map_descriptor.value_type.clone()) + .map_err(CheckErrorKind::from_clarity_type_error)?; if !self.data_map_entry_exists(&key, &stored_type, epoch)? { return Ok(ValueResult { value: Value::Bool(false), @@ -2171,7 +2206,10 @@ impl ClarityDatabase<'_> { asset: &Value, key_type: &TypeSignature, ) -> Result { - if !key_type.admits(&self.get_clarity_epoch_version()?, asset)? { + if !key_type + .admits(&self.get_clarity_epoch_version()?, asset) + .map_err(CheckErrorKind::from_clarity_type_error)? + { return Err(CheckErrorKind::TypeValueError( Box::new(key_type.clone()), Box::new(asset.clone()), @@ -2183,7 +2221,9 @@ impl ClarityDatabase<'_> { contract_identifier, StoreType::NonFungibleToken, asset_name, - &asset.serialize_to_hex()?, + &asset + .serialize_to_hex() + .map_err(|_| VmInternalError::Expect("IOError filling byte buffer.".into()))?, ); let epoch = self.get_clarity_epoch_version()?; @@ -2194,12 +2234,17 @@ impl ClarityDatabase<'_> { &epoch, )?; let owner = match value { - Some(owner) => owner.value.expect_optional()?, + Some(owner) => owner + .value + .expect_optional() + .map_err(|_| VmInternalError::Expect("Expected an optional".into()))?, None => return Err(RuntimeError::NoSuchToken.into()), }; let principal = match owner { - Some(value) => value.expect_principal()?, + Some(value) => value + .expect_principal() + .map_err(|_| VmInternalError::Expect("Expected principal.".into()))?, None => return Err(RuntimeError::NoSuchToken.into()), }; @@ -2224,7 +2269,10 @@ impl ClarityDatabase<'_> { key_type: &TypeSignature, epoch: &StacksEpochId, ) -> Result<(), VmExecutionError> { - if !key_type.admits(&self.get_clarity_epoch_version()?, asset)? { + if !key_type + .admits(&self.get_clarity_epoch_version()?, asset) + .map_err(CheckErrorKind::from_clarity_type_error)? + { return Err(CheckErrorKind::TypeValueError( Box::new(key_type.clone()), Box::new(asset.clone()), @@ -2236,10 +2284,13 @@ impl ClarityDatabase<'_> { contract_identifier, StoreType::NonFungibleToken, asset_name, - &asset.serialize_to_hex()?, + &asset + .serialize_to_hex() + .map_err(|_| VmInternalError::Expect("IOError filling byte buffer.".into()))?, ); - let value = Value::some(Value::Principal(principal.clone()))?; + let value = Value::some(Value::Principal(principal.clone())) + .map_err(CheckErrorKind::from_clarity_type_error)?; self.put_value(&key, value, epoch)?; Ok(()) @@ -2253,7 +2304,10 @@ impl ClarityDatabase<'_> { key_type: &TypeSignature, epoch: &StacksEpochId, ) -> Result<(), VmExecutionError> { - if !key_type.admits(&self.get_clarity_epoch_version()?, asset)? { + if !key_type + .admits(&self.get_clarity_epoch_version()?, asset) + .map_err(CheckErrorKind::from_clarity_type_error)? + { return Err(CheckErrorKind::TypeValueError( Box::new(key_type.clone()), Box::new(asset.clone()), @@ -2265,7 +2319,9 @@ impl ClarityDatabase<'_> { contract_identifier, StoreType::NonFungibleToken, asset_name, - &asset.serialize_to_hex()?, + &asset + .serialize_to_hex() + .map_err(|_| VmInternalError::Expect("IOError filling byte buffer.".into()))?, ); self.put_value(&key, Value::none(), epoch)?; diff --git a/clarity/src/vm/database/key_value_wrapper.rs b/clarity/src/vm/database/key_value_wrapper.rs index 56b082c96f..6c4392aed4 100644 --- a/clarity/src/vm/database/key_value_wrapper.rs +++ b/clarity/src/vm/database/key_value_wrapper.rs @@ -445,7 +445,7 @@ impl RollbackWrapper<'_> { epoch: &StacksEpochId, ) -> Result, SerializationError> { self.stack.last().ok_or_else(|| { - SerializationError::DeserializationError( + SerializationError::DeserializationFailure( "ERROR: Clarity VM attempted GET on non-nested context.".into(), ) })?; @@ -456,7 +456,9 @@ impl RollbackWrapper<'_> { } } let stored_data = self.store.get_data(key).map_err(|_| { - SerializationError::DeserializationError("ERROR: Clarity backing store failure".into()) + SerializationError::DeserializationFailure( + "ERROR: Clarity backing store failure".into(), + ) })?; match stored_data { Some(x) => Ok(Some(Self::deserialize_value(&x, expected, epoch)?)), diff --git a/clarity/src/vm/errors.rs b/clarity/src/vm/errors.rs index 7dd07dcb4f..5d826d8408 100644 --- a/clarity/src/vm/errors.rs +++ b/clarity/src/vm/errors.rs @@ -17,6 +17,7 @@ pub use clarity_types::errors::{ EarlyReturnError, IncomparableError, RuntimeError, VmExecutionError, VmInternalError, }; +pub use clarity_types::ClarityTypeError; pub use crate::vm::analysis::errors::{ check_argument_count, check_arguments_at_least, check_arguments_at_most, CheckErrorKind, diff --git a/clarity/src/vm/functions/arithmetic.rs b/clarity/src/vm/functions/arithmetic.rs index cf629a85d8..02e0612917 100644 --- a/clarity/src/vm/functions/arithmetic.rs +++ b/clarity/src/vm/functions/arithmetic.rs @@ -408,7 +408,10 @@ fn special_geq_v2( runtime_cost( ClarityCostFunction::Geq, env, - cmp::min(a.size()?, b.size()?), + cmp::min( + a.size().map_err(CheckErrorKind::from_clarity_type_error)?, + b.size().map_err(CheckErrorKind::from_clarity_type_error)?, + ), )?; type_force_binary_comparison_v2!(geq, a, b) } @@ -455,7 +458,10 @@ fn special_leq_v2( runtime_cost( ClarityCostFunction::Leq, env, - cmp::min(a.size()?, b.size()?), + cmp::min( + a.size().map_err(CheckErrorKind::from_clarity_type_error)?, + b.size().map_err(CheckErrorKind::from_clarity_type_error)?, + ), )?; type_force_binary_comparison_v2!(leq, a, b) } @@ -498,7 +504,14 @@ fn special_greater_v2( check_argument_count(2, args)?; let a = eval(&args[0], env, context)?; let b = eval(&args[1], env, context)?; - runtime_cost(ClarityCostFunction::Ge, env, cmp::min(a.size()?, b.size()?))?; + runtime_cost( + ClarityCostFunction::Ge, + env, + cmp::min( + a.size().map_err(CheckErrorKind::from_clarity_type_error)?, + b.size().map_err(CheckErrorKind::from_clarity_type_error)?, + ), + )?; type_force_binary_comparison_v2!(greater, a, b) } @@ -540,7 +553,14 @@ fn special_less_v2( check_argument_count(2, args)?; let a = eval(&args[0], env, context)?; let b = eval(&args[1], env, context)?; - runtime_cost(ClarityCostFunction::Le, env, cmp::min(a.size()?, b.size()?))?; + runtime_cost( + ClarityCostFunction::Le, + env, + cmp::min( + a.size().map_err(CheckErrorKind::from_clarity_type_error)?, + b.size().map_err(CheckErrorKind::from_clarity_type_error)?, + ), + )?; type_force_binary_comparison_v2!(less, a, b) } @@ -600,7 +620,10 @@ pub fn native_bitwise_left_shift(input: Value, pos: Value) -> Result Err(CheckErrorKind::UnionTypeError( vec![TypeSignature::IntType, TypeSignature::UIntType], - Box::new(TypeSignature::type_of(&input)?), + Box::new( + TypeSignature::type_of(&input) + .map_err(CheckErrorKind::from_clarity_type_error)?, + ), ) .into()), } @@ -626,7 +649,10 @@ pub fn native_bitwise_right_shift(input: Value, pos: Value) -> Result Err(CheckErrorKind::UnionTypeError( vec![TypeSignature::IntType, TypeSignature::UIntType], - Box::new(TypeSignature::type_of(&input)?), + Box::new( + TypeSignature::type_of(&input) + .map_err(CheckErrorKind::from_clarity_type_error)?, + ), ) .into()), } diff --git a/clarity/src/vm/functions/assets.rs b/clarity/src/vm/functions/assets.rs index 4dde9fd28f..1fdd988fd4 100644 --- a/clarity/src/vm/functions/assets.rs +++ b/clarity/src/vm/functions/assets.rs @@ -137,8 +137,18 @@ pub fn stx_transfer_consolidated( } // loading from/to principals and balances - env.add_memory(TypeSignature::PrincipalType.size()? as u64)?; - env.add_memory(TypeSignature::PrincipalType.size()? as u64)?; + env.add_memory( + TypeSignature::PrincipalType + .size() + .map_err(CheckErrorKind::from_clarity_type_error)? + .into(), + )?; + env.add_memory( + TypeSignature::PrincipalType + .size() + .map_err(CheckErrorKind::from_clarity_type_error)? + .into(), + )?; // loading from's locked amount and height // TODO: this does not count the inner stacks block header load, but arguably, // this could be optimized away, so it shouldn't penalize the caller. @@ -264,7 +274,8 @@ pub fn special_stx_account( ))), ), ]) - .map(Value::Tuple)?) + .map(Value::Tuple) + .map_err(CheckErrorKind::from_clarity_type_error)?) } pub fn special_stx_burn( @@ -288,8 +299,17 @@ pub fn special_stx_burn( return clarity_ecode!(StxErrorCodes::SENDER_IS_NOT_TX_SENDER); } - env.add_memory(TypeSignature::PrincipalType.size()? as u64)?; - env.add_memory(STXBalance::unlocked_and_v1_size as u64)?; + env.add_memory( + TypeSignature::PrincipalType + .size() + .map_err(CheckErrorKind::from_clarity_type_error)? + .into(), + )?; + env.add_memory(STXBalance::unlocked_and_v1_size.try_into().map_err(|_| { + CheckErrorKind::ExpectsRejectable( + "BUG: STXBalance::unlocked_and_v1_size does not fit into a u64".into(), + ) + })?)?; let mut burner_snapshot = env.global_context.database.get_stx_balance_snapshot(from)?; if !burner_snapshot.can_transfer(amount)? { @@ -355,8 +375,18 @@ pub fn special_mint_token( .checked_add(amount) .ok_or_else(|| VmInternalError::Expect("STX overflow".into()))?; - env.add_memory(TypeSignature::PrincipalType.size()? as u64)?; - env.add_memory(TypeSignature::UIntType.size()? as u64)?; + env.add_memory( + TypeSignature::PrincipalType + .size() + .map_err(CheckErrorKind::from_clarity_type_error)? + .into(), + )?; + env.add_memory( + TypeSignature::UIntType + .size() + .map_err(CheckErrorKind::from_clarity_type_error)? + .into(), + )?; env.global_context.database.set_ft_balance( &env.contract_context.contract_identifier, @@ -399,10 +429,15 @@ pub fn special_mint_asset_v200( runtime_cost( ClarityCostFunction::NftMint, env, - expected_asset_type.size()?, + expected_asset_type + .size() + .map_err(CheckErrorKind::from_clarity_type_error)?, )?; - if !expected_asset_type.admits(env.epoch(), &asset)? { + if !expected_asset_type + .admits(env.epoch(), &asset) + .map_err(CheckErrorKind::from_clarity_type_error)? + { return Err(CheckErrorKind::TypeValueError( Box::new(expected_asset_type.clone()), Box::new(asset), @@ -422,8 +457,18 @@ pub fn special_mint_asset_v200( Err(e) => Err(e), }?; - env.add_memory(TypeSignature::PrincipalType.size()? as u64)?; - env.add_memory(expected_asset_type.size()? as u64)?; + env.add_memory( + TypeSignature::PrincipalType + .size() + .map_err(CheckErrorKind::from_clarity_type_error)? + .into(), + )?; + env.add_memory( + expected_asset_type + .size() + .map_err(CheckErrorKind::from_clarity_type_error)? + .into(), + )?; let epoch = *env.epoch(); env.global_context.database.set_nft_owner( @@ -476,7 +521,10 @@ pub fn special_mint_asset_v205( .map_err(|e| VmInternalError::Expect(e.to_string()))? as u64; runtime_cost(ClarityCostFunction::NftMint, env, asset_size)?; - if !expected_asset_type.admits(env.epoch(), &asset)? { + if !expected_asset_type + .admits(env.epoch(), &asset) + .map_err(CheckErrorKind::from_clarity_type_error)? + { return Err(CheckErrorKind::TypeValueError( Box::new(expected_asset_type.clone()), Box::new(asset), @@ -496,7 +544,12 @@ pub fn special_mint_asset_v205( Err(e) => Err(e), }?; - env.add_memory(TypeSignature::PrincipalType.size()? as u64)?; + env.add_memory( + TypeSignature::PrincipalType + .size() + .map_err(CheckErrorKind::from_clarity_type_error)? + .into(), + )?; env.add_memory(asset_size)?; let epoch = *env.epoch(); @@ -547,10 +600,15 @@ pub fn special_transfer_asset_v200( runtime_cost( ClarityCostFunction::NftTransfer, env, - expected_asset_type.size()?, + expected_asset_type + .size() + .map_err(CheckErrorKind::from_clarity_type_error)?, )?; - if !expected_asset_type.admits(env.epoch(), &asset)? { + if !expected_asset_type + .admits(env.epoch(), &asset) + .map_err(CheckErrorKind::from_clarity_type_error)? + { return Err(CheckErrorKind::TypeValueError( Box::new(expected_asset_type.clone()), Box::new(asset), @@ -580,8 +638,18 @@ pub fn special_transfer_asset_v200( return clarity_ecode!(TransferAssetErrorCodes::NOT_OWNED_BY); } - env.add_memory(TypeSignature::PrincipalType.size()? as u64)?; - env.add_memory(expected_asset_type.size()? as u64)?; + env.add_memory( + TypeSignature::PrincipalType + .size() + .map_err(CheckErrorKind::from_clarity_type_error)? + .into(), + )?; + env.add_memory( + expected_asset_type + .size() + .map_err(CheckErrorKind::from_clarity_type_error)? + .into(), + )?; let epoch = *env.epoch(); env.global_context.database.set_nft_owner( @@ -644,7 +712,10 @@ pub fn special_transfer_asset_v205( .map_err(|e| VmInternalError::Expect(e.to_string()))? as u64; runtime_cost(ClarityCostFunction::NftTransfer, env, asset_size)?; - if !expected_asset_type.admits(env.epoch(), &asset)? { + if !expected_asset_type + .admits(env.epoch(), &asset) + .map_err(CheckErrorKind::from_clarity_type_error)? + { return Err(CheckErrorKind::TypeValueError( Box::new(expected_asset_type.clone()), Box::new(asset), @@ -674,7 +745,12 @@ pub fn special_transfer_asset_v205( return clarity_ecode!(TransferAssetErrorCodes::NOT_OWNED_BY); } - env.add_memory(TypeSignature::PrincipalType.size()? as u64)?; + env.add_memory( + TypeSignature::PrincipalType + .size() + .map_err(CheckErrorKind::from_clarity_type_error)? + .into(), + )?; env.add_memory(asset_size)?; let epoch = *env.epoch(); @@ -772,10 +848,30 @@ pub fn special_transfer_token( .checked_add(amount) .ok_or(RuntimeError::ArithmeticOverflow)?; - env.add_memory(TypeSignature::PrincipalType.size()? as u64)?; - env.add_memory(TypeSignature::PrincipalType.size()? as u64)?; - env.add_memory(TypeSignature::UIntType.size()? as u64)?; - env.add_memory(TypeSignature::UIntType.size()? as u64)?; + env.add_memory( + TypeSignature::PrincipalType + .size() + .map_err(CheckErrorKind::from_clarity_type_error)? + .into(), + )?; + env.add_memory( + TypeSignature::PrincipalType + .size() + .map_err(CheckErrorKind::from_clarity_type_error)? + .into(), + )?; + env.add_memory( + TypeSignature::UIntType + .size() + .map_err(CheckErrorKind::from_clarity_type_error)? + .into(), + )?; + env.add_memory( + TypeSignature::UIntType + .size() + .map_err(CheckErrorKind::from_clarity_type_error)? + .into(), + )?; env.global_context.database.set_ft_balance( &env.contract_context.contract_identifier, @@ -870,10 +966,15 @@ pub fn special_get_owner_v200( runtime_cost( ClarityCostFunction::NftOwner, env, - expected_asset_type.size()?, + expected_asset_type + .size() + .map_err(CheckErrorKind::from_clarity_type_error)?, )?; - if !expected_asset_type.admits(env.epoch(), &asset)? { + if !expected_asset_type + .admits(env.epoch(), &asset) + .map_err(CheckErrorKind::from_clarity_type_error)? + { return Err(CheckErrorKind::TypeValueError( Box::new(expected_asset_type.clone()), Box::new(asset), @@ -920,7 +1021,10 @@ pub fn special_get_owner_v205( .map_err(|e| VmInternalError::Expect(e.to_string()))? as u64; runtime_cost(ClarityCostFunction::NftOwner, env, asset_size)?; - if !expected_asset_type.admits(env.epoch(), &asset)? { + if !expected_asset_type + .admits(env.epoch(), &asset) + .map_err(CheckErrorKind::from_clarity_type_error)? + { return Err(CheckErrorKind::TypeValueError( Box::new(expected_asset_type.clone()), Box::new(asset), @@ -1011,8 +1115,18 @@ pub fn special_burn_token( }; env.register_ft_burn_event(burner.clone(), amount, asset_identifier)?; - env.add_memory(TypeSignature::PrincipalType.size()? as u64)?; - env.add_memory(TypeSignature::UIntType.size()? as u64)?; + env.add_memory( + TypeSignature::PrincipalType + .size() + .map_err(CheckErrorKind::from_clarity_type_error)? + .into(), + )?; + env.add_memory( + TypeSignature::UIntType + .size() + .map_err(CheckErrorKind::from_clarity_type_error)? + .into(), + )?; env.global_context.log_token_transfer( burner, @@ -1051,10 +1165,15 @@ pub fn special_burn_asset_v200( runtime_cost( ClarityCostFunction::NftBurn, env, - expected_asset_type.size()?, + expected_asset_type + .size() + .map_err(CheckErrorKind::from_clarity_type_error)?, )?; - if !expected_asset_type.admits(env.epoch(), &asset)? { + if !expected_asset_type + .admits(env.epoch(), &asset) + .map_err(CheckErrorKind::from_clarity_type_error)? + { return Err(CheckErrorKind::TypeValueError( Box::new(expected_asset_type.clone()), Box::new(asset), @@ -1080,8 +1199,18 @@ pub fn special_burn_asset_v200( return clarity_ecode!(BurnAssetErrorCodes::NOT_OWNED_BY); } - env.add_memory(TypeSignature::PrincipalType.size()? as u64)?; - env.add_memory(expected_asset_type.size()? as u64)?; + env.add_memory( + TypeSignature::PrincipalType + .size() + .map_err(CheckErrorKind::from_clarity_type_error)? + .into(), + )?; + env.add_memory( + expected_asset_type + .size() + .map_err(CheckErrorKind::from_clarity_type_error)? + .into(), + )?; let epoch = *env.epoch(); env.global_context.database.burn_nft( @@ -1143,7 +1272,10 @@ pub fn special_burn_asset_v205( .map_err(|e| VmInternalError::Expect(e.to_string()))? as u64; runtime_cost(ClarityCostFunction::NftBurn, env, asset_size)?; - if !expected_asset_type.admits(env.epoch(), &asset)? { + if !expected_asset_type + .admits(env.epoch(), &asset) + .map_err(CheckErrorKind::from_clarity_type_error)? + { return Err(CheckErrorKind::TypeValueError( Box::new(expected_asset_type.clone()), Box::new(asset), @@ -1169,7 +1301,12 @@ pub fn special_burn_asset_v205( return clarity_ecode!(BurnAssetErrorCodes::NOT_OWNED_BY); } - env.add_memory(TypeSignature::PrincipalType.size()? as u64)?; + env.add_memory( + TypeSignature::PrincipalType + .size() + .map_err(CheckErrorKind::from_clarity_type_error)? + .into(), + )?; env.add_memory(asset_size)?; let epoch = *env.epoch(); diff --git a/clarity/src/vm/functions/conversions.rs b/clarity/src/vm/functions/conversions.rs index 5a2ec4b4ed..d893aee55d 100644 --- a/clarity/src/vm/functions/conversions.rs +++ b/clarity/src/vm/functions/conversions.rs @@ -15,6 +15,7 @@ // along with this program. If not, see . use clarity_types::types::serialization::SerializationError; +use clarity_types::types::ClarityTypeError; use crate::vm::costs::cost_functions::ClarityCostFunction; use crate::vm::costs::runtime_cost; @@ -50,7 +51,9 @@ pub fn buff_to_int_generic( ) -> Result { match value { Value::Sequence(SequenceData::Buffer(ref sequence_data)) => { - if sequence_data.len()? + if sequence_data + .len() + .map_err(CheckErrorKind::from_clarity_type_error)? > BufferLength::try_from(16_u32) .map_err(|_| VmInternalError::Expect("Failed to construct".into()))? { @@ -160,7 +163,7 @@ pub fn native_string_to_int_generic( fn safe_convert_string_to_int(raw_string: String) -> Result { let possible_int = raw_string.parse::(); match possible_int { - Ok(val) => Value::some(Value::Int(val)), + Ok(val) => Value::some(Value::Int(val)).map_err(CheckErrorKind::from_clarity_type_error), Err(_error) => Ok(Value::none()), } } @@ -172,7 +175,7 @@ pub fn native_string_to_int(value: Value) -> Result { fn safe_convert_string_to_uint(raw_string: String) -> Result { let possible_int = raw_string.parse::(); match possible_int { - Ok(val) => Value::some(Value::UInt(val)), + Ok(val) => Value::some(Value::UInt(val)).map_err(CheckErrorKind::from_clarity_type_error), Err(_error) => Ok(Value::none()), } } @@ -187,7 +190,7 @@ pub fn native_string_to_uint(value: Value) -> Result { // either an ASCII or UTF8 string, depending on the desired result. pub fn native_int_to_string_generic( value: Value, - bytes_to_value_fn: fn(bytes: Vec) -> Result, + bytes_to_value_fn: fn(bytes: Vec) -> Result, ) -> Result { match value { Value::Int(ref int_value) => { @@ -226,13 +229,15 @@ fn convert_string_to_ascii_ok(s: String) -> Result { let ascii_value = Value::string_ascii_from_bytes(s.into_bytes()).map_err(|_| { VmInternalError::Expect("Unexpected error converting string to ASCII".into()) })?; - Ok(Value::okay(ascii_value)?) + Ok(Value::okay(ascii_value).map_err(CheckErrorKind::from_clarity_type_error)?) } /// Helper function for UTF8 conversion that can return err u1 for non-ASCII characters fn convert_utf8_to_ascii(s: String) -> Result { match Value::string_ascii_from_bytes(s.into_bytes()) { - Ok(ascii_value) => Ok(Value::okay(ascii_value)?), + Ok(ascii_value) => { + Ok(Value::okay(ascii_value).map_err(CheckErrorKind::from_clarity_type_error)?) + } Err(_) => Ok(Value::err_uint(1)), // Non-ASCII characters in UTF8 } } @@ -246,7 +251,13 @@ pub fn special_to_ascii( let value = eval(&args[0], env, context)?; - runtime_cost(ClarityCostFunction::ToAscii, env, value.size()?)?; + runtime_cost( + ClarityCostFunction::ToAscii, + env, + value + .size() + .map_err(CheckErrorKind::from_clarity_type_error)?, + )?; match value { Value::Int(num) => convert_string_to_ascii_ok(num.to_string()), @@ -343,13 +354,16 @@ pub fn from_consensus_buff( ) { Ok(value) => value, Err(SerializationError::UnexpectedSerialization) => { - return Err(CheckErrorKind::Expects("UnexpectedSerialization".into()).into()); + return Err(CheckErrorKind::ExpectsRejectable("UnexpectedSerialization".into()).into()); } Err(_) => return Ok(Value::none()), }; - if !type_arg.admits(env.epoch(), &result)? { + if !type_arg + .admits(env.epoch(), &result) + .map_err(CheckErrorKind::from_clarity_type_error)? + { return Ok(Value::none()); } - Ok(Value::some(result)?) + Ok(Value::some(result).map_err(CheckErrorKind::from_clarity_type_error)?) } diff --git a/clarity/src/vm/functions/crypto.rs b/clarity/src/vm/functions/crypto.rs index 499bed5ba3..53e5d92d7c 100644 --- a/clarity/src/vm/functions/crypto.rs +++ b/clarity/src/vm/functions/crypto.rs @@ -46,7 +46,8 @@ macro_rules! native_hash_func { )), }?; let hash = <$module>::from_data(&bytes); - let value = Value::buff_from(hash.as_bytes().to_vec())?; + let value = Value::buff_from(hash.as_bytes().to_vec()) + .map_err(CheckErrorKind::from_clarity_type_error)?; Ok(value) } }; diff --git a/clarity/src/vm/functions/database.rs b/clarity/src/vm/functions/database.rs index f17c577c5a..85ea17a99d 100644 --- a/clarity/src/vm/functions/database.rs +++ b/clarity/src/vm/functions/database.rs @@ -76,7 +76,12 @@ pub fn special_contract_call( let mut rest_args_sizes = Vec::with_capacity(rest_args_len); for arg in rest_args_slice.iter() { let evaluated_arg = eval(arg, env, context)?; - rest_args_sizes.push(evaluated_arg.size()? as u64); + rest_args_sizes.push( + evaluated_arg + .size() + .map_err(CheckErrorKind::from_clarity_type_error)? + .into(), + ); rest_args.push(SymbolicExpression::atom_value(evaluated_arg)); } @@ -206,15 +211,20 @@ pub fn special_contract_call( }?; // sanitize contract-call outputs in epochs >= 2.4 - let result_type = TypeSignature::type_of(&result)?; + let result_type = + TypeSignature::type_of(&result).map_err(CheckErrorKind::from_clarity_type_error)?; let (result, _) = Value::sanitize_value(env.epoch(), &result_type, result) .ok_or_else(|| CheckErrorKind::CouldNotDetermineType)?; // Ensure that the expected type from the trait spec admits // the type of the value returned by the dynamic dispatch. if let Some(returns_type_signature) = type_returns_constraint { - let actual_returns = TypeSignature::type_of(&result)?; - if !returns_type_signature.admits_type(env.epoch(), &actual_returns)? { + let actual_returns = + TypeSignature::type_of(&result).map_err(CheckErrorKind::from_clarity_type_error)?; + if !returns_type_signature + .admits_type(env.epoch(), &actual_returns) + .map_err(CheckErrorKind::from_clarity_type_error)? + { return Err(CheckErrorKind::ReturnTypesMustMatch( Box::new(returns_type_signature), Box::new(actual_returns), @@ -246,7 +256,10 @@ pub fn special_fetch_variable_v200( runtime_cost( ClarityCostFunction::FetchVar, env, - data_types.value_type.size()?, + data_types + .value_type + .size() + .map_err(CheckErrorKind::from_clarity_type_error)?, )?; let epoch = *env.epoch(); @@ -282,7 +295,11 @@ pub fn special_fetch_variable_v205( let result_size = match &result { Ok(data) => data.serialized_byte_len, - Err(_e) => data_types.value_type.size()? as u64, + Err(_e) => data_types + .value_type + .size() + .map_err(CheckErrorKind::from_clarity_type_error)? + .into(), }; runtime_cost(ClarityCostFunction::FetchVar, env, result_size)?; @@ -316,7 +333,10 @@ pub fn special_set_variable_v200( runtime_cost( ClarityCostFunction::SetVar, env, - data_types.value_type.size()?, + data_types + .value_type + .size() + .map_err(CheckErrorKind::from_clarity_type_error)?, )?; env.add_memory(value.get_memory_use()?)?; @@ -361,7 +381,11 @@ pub fn special_set_variable_v205( let result_size = match &result { Ok(data) => data.serialized_byte_len, - Err(_e) => data_types.value_type.size()? as u64, + Err(_e) => data_types + .value_type + .size() + .map_err(CheckErrorKind::from_clarity_type_error)? + .into(), }; runtime_cost(ClarityCostFunction::SetVar, env, result_size)?; @@ -393,7 +417,14 @@ pub fn special_fetch_entry_v200( runtime_cost( ClarityCostFunction::FetchEntry, env, - data_types.value_type.size()? + data_types.key_type.size()?, + data_types + .value_type + .size() + .map_err(CheckErrorKind::from_clarity_type_error)? + + data_types + .key_type + .size() + .map_err(CheckErrorKind::from_clarity_type_error)?, )?; let epoch = *env.epoch(); @@ -431,7 +462,15 @@ pub fn special_fetch_entry_v205( let result_size = match &result { Ok(data) => data.serialized_byte_len, - Err(_e) => (data_types.value_type.size()? + data_types.key_type.size()?) as u64, + Err(_e) => (data_types + .value_type + .size() + .map_err(CheckErrorKind::from_clarity_type_error)? + + data_types + .key_type + .size() + .map_err(CheckErrorKind::from_clarity_type_error)?) + .into(), }; runtime_cost(ClarityCostFunction::FetchEntry, env, result_size)?; @@ -500,7 +539,14 @@ pub fn special_set_entry_v200( runtime_cost( ClarityCostFunction::SetEntry, env, - data_types.value_type.size()? + data_types.key_type.size()?, + data_types + .value_type + .size() + .map_err(CheckErrorKind::from_clarity_type_error)? + + data_types + .key_type + .size() + .map_err(CheckErrorKind::from_clarity_type_error)?, )?; env.add_memory(key.get_memory_use()?)?; @@ -548,7 +594,15 @@ pub fn special_set_entry_v205( let result_size = match &result { Ok(data) => data.serialized_byte_len, - Err(_e) => (data_types.value_type.size()? + data_types.key_type.size()?) as u64, + Err(_e) => (data_types + .value_type + .size() + .map_err(CheckErrorKind::from_clarity_type_error)? + + data_types + .key_type + .size() + .map_err(CheckErrorKind::from_clarity_type_error)?) + .into(), }; runtime_cost(ClarityCostFunction::SetEntry, env, result_size)?; @@ -586,7 +640,14 @@ pub fn special_insert_entry_v200( runtime_cost( ClarityCostFunction::SetEntry, env, - data_types.value_type.size()? + data_types.key_type.size()?, + data_types + .value_type + .size() + .map_err(CheckErrorKind::from_clarity_type_error)? + + data_types + .key_type + .size() + .map_err(CheckErrorKind::from_clarity_type_error)?, )?; env.add_memory(key.get_memory_use()?)?; @@ -635,7 +696,15 @@ pub fn special_insert_entry_v205( let result_size = match &result { Ok(data) => data.serialized_byte_len, - Err(_e) => (data_types.value_type.size()? + data_types.key_type.size()?) as u64, + Err(_e) => (data_types + .value_type + .size() + .map_err(CheckErrorKind::from_clarity_type_error)? + + data_types + .key_type + .size() + .map_err(CheckErrorKind::from_clarity_type_error)?) + .into(), }; runtime_cost(ClarityCostFunction::SetEntry, env, result_size)?; @@ -671,7 +740,10 @@ pub fn special_delete_entry_v200( runtime_cost( ClarityCostFunction::SetEntry, env, - data_types.key_type.size()?, + data_types + .key_type + .size() + .map_err(CheckErrorKind::from_clarity_type_error)?, )?; env.add_memory(key.get_memory_use()?)?; @@ -716,7 +788,11 @@ pub fn special_delete_entry_v205( let result_size = match &result { Ok(data) => data.serialized_byte_len, - Err(_e) => data_types.key_type.size()? as u64, + Err(_e) => data_types + .key_type + .size() + .map_err(CheckErrorKind::from_clarity_type_error)? + .into(), }; runtime_cost(ClarityCostFunction::SetEntry, env, result_size)?; @@ -876,13 +952,15 @@ pub fn special_get_block_info( // this is already an optional let block_reward_opt = env.global_context.database.get_block_reward(height_value)?; return Ok(match block_reward_opt { - Some(x) => Value::some(Value::UInt(x))?, + Some(x) => { + Value::some(Value::UInt(x)).map_err(CheckErrorKind::from_clarity_type_error)? + } None => Value::none(), }); } }; - Ok(Value::some(result)?) + Ok(Value::some(result).map_err(CheckErrorKind::from_clarity_type_error)?) } /// Handles the `get-burn-block-info?` special function. @@ -941,11 +1019,12 @@ pub fn special_get_burn_block_info( .get_burnchain_block_header_hash_for_burnchain_height(height_value)?; match burnchain_header_hash_opt { - Some(burnchain_header_hash) => Ok(Value::some(Value::Sequence( - SequenceData::Buffer(BuffData { + Some(burnchain_header_hash) => { + Ok(Value::some(Value::Sequence(SequenceData::Buffer(BuffData { data: burnchain_header_hash.as_bytes().to_vec(), - }), - ))?), + }))) + .map_err(CheckErrorKind::from_clarity_type_error)?) + } None => Ok(Value::none()), } } @@ -1060,7 +1139,7 @@ pub fn special_get_stacks_block_info( } }; - Ok(Value::some(result)?) + Ok(Value::some(result).map_err(CheckErrorKind::from_clarity_type_error)?) } /// Handles the function `get-tenure-info?` special function. @@ -1167,13 +1246,15 @@ pub fn special_get_tenure_info( // this is already an optional let block_reward_opt = env.global_context.database.get_block_reward(height_value)?; return Ok(match block_reward_opt { - Some(x) => Value::some(Value::UInt(x))?, + Some(x) => { + Value::some(Value::UInt(x)).map_err(CheckErrorKind::from_clarity_type_error)? + } None => Value::none(), }); } }; - Ok(Value::some(result)?) + Ok(Value::some(result).map_err(CheckErrorKind::from_clarity_type_error)?) } /// Handles the function `contract-hash?` @@ -1212,7 +1293,9 @@ pub fn special_contract_hash( return Ok(Value::err_uint(2)); }; - Ok(Value::okay(Value::buff_from( - contract_hash.as_bytes().to_vec(), - )?)?) + Ok(Value::okay( + Value::buff_from(contract_hash.as_bytes().to_vec()) + .map_err(CheckErrorKind::from_clarity_type_error)?, + ) + .map_err(CheckErrorKind::from_clarity_type_error)?) } diff --git a/clarity/src/vm/functions/mod.rs b/clarity/src/vm/functions/mod.rs index 71d405313e..49eab0ef25 100644 --- a/clarity/src/vm/functions/mod.rs +++ b/clarity/src/vm/functions/mod.rs @@ -609,13 +609,15 @@ fn native_eq(args: Vec, env: &mut Environment) -> Result special_match_resp(data, &args[1..], env, context), Value::Optional(data) => special_match_opt(data, &args[1..], env, context), - _ => Err(CheckErrorKind::BadMatchInput(Box::new(TypeSignature::type_of(&input)?)).into()), + _ => Err(CheckErrorKind::BadMatchInput(Box::new( + TypeSignature::type_of(&input).map_err(CheckErrorKind::from_clarity_type_error)?, + )) + .into()), } } pub fn native_some(input: Value) -> Result { - Ok(Value::some(input)?) + Ok(Value::some(input).map_err(CheckErrorKind::from_clarity_type_error)?) } fn is_some(input: Value) -> Result { @@ -259,11 +262,11 @@ pub fn native_is_err(input: Value) -> Result { } pub fn native_okay(input: Value) -> Result { - Ok(Value::okay(input)?) + Ok(Value::okay(input).map_err(CheckErrorKind::from_clarity_type_error)?) } pub fn native_error(input: Value) -> Result { - Ok(Value::error(input)?) + Ok(Value::error(input).map_err(CheckErrorKind::from_clarity_type_error)?) } pub fn native_default_to(default: Value, input: Value) -> Result { diff --git a/clarity/src/vm/functions/post_conditions.rs b/clarity/src/vm/functions/post_conditions.rs index 99ed59f457..e431f81d6d 100644 --- a/clarity/src/vm/functions/post_conditions.rs +++ b/clarity/src/vm/functions/post_conditions.rs @@ -114,7 +114,9 @@ fn eval_allowance( return Err(CheckErrorKind::IncorrectArgumentCount(1, rest.len()).into()); } let amount = eval(&rest[0], env, context)?; - let amount = amount.expect_u128()?; + let amount = amount + .expect_u128() + .map_err(|_| VmInternalError::Expect("Expected u128".into()))?; Ok(Allowance::Stx(StxAllowance { amount })) } NativeFunctions::AllowanceWithFt => { @@ -123,7 +125,10 @@ fn eval_allowance( } let contract_value = eval(&rest[0], env, context)?; - let contract = contract_value.clone().expect_principal()?; + let contract = contract_value + .clone() + .expect_principal() + .map_err(|_| VmInternalError::Expect("Expected principal".into()))?; let contract_identifier = match contract { PrincipalData::Standard(_) => { return Err(CheckErrorKind::ExpectedContractPrincipalValue( @@ -135,7 +140,11 @@ fn eval_allowance( }; let asset_name = eval(&rest[1], env, context)?; - let asset_name = asset_name.expect_string_ascii()?.as_str().into(); + let asset_name = asset_name + .expect_string_ascii() + .map_err(|_| VmInternalError::Expect("Expected ASCII String.".into()))? + .as_str() + .into(); let asset = AssetIdentifier { contract_identifier, @@ -143,7 +152,9 @@ fn eval_allowance( }; let amount = eval(&rest[2], env, context)?; - let amount = amount.expect_u128()?; + let amount = amount + .expect_u128() + .map_err(|_| VmInternalError::Expect("Expected u128".into()))?; Ok(Allowance::Ft(FtAllowance { asset, amount })) } @@ -153,7 +164,10 @@ fn eval_allowance( } let contract_value = eval(&rest[0], env, context)?; - let contract = contract_value.clone().expect_principal()?; + let contract = contract_value + .clone() + .expect_principal() + .map_err(|_| VmInternalError::Expect("Expected principal".into()))?; let contract_identifier = match contract { PrincipalData::Standard(_) => { return Err(CheckErrorKind::ExpectedContractPrincipalValue( @@ -165,7 +179,11 @@ fn eval_allowance( }; let asset_name = eval(&rest[1], env, context)?; - let asset_name = asset_name.expect_string_ascii()?.as_str().into(); + let asset_name = asset_name + .expect_string_ascii() + .map_err(|_| VmInternalError::Expect("Expected ASCII String.".into()))? + .as_str() + .into(); let asset = AssetIdentifier { contract_identifier, @@ -173,7 +191,9 @@ fn eval_allowance( }; let asset_id_list = eval(&rest[2], env, context)?; - let asset_ids = asset_id_list.expect_list()?; + let asset_ids = asset_id_list + .expect_list() + .map_err(|_| VmInternalError::Expect("Expected list".into()))?; Ok(Allowance::Nft(NftAllowance { asset, asset_ids })) } @@ -182,7 +202,9 @@ fn eval_allowance( return Err(CheckErrorKind::IncorrectArgumentCount(1, rest.len()).into()); } let amount = eval(&rest[0], env, context)?; - let amount = amount.expect_u128()?; + let amount = amount + .expect_u128() + .map_err(|_| VmInternalError::Expect("Expected u128".into()))?; Ok(Allowance::Stacking(StackingAllowance { amount })) } NativeFunctions::AllowanceAll => { @@ -217,7 +239,9 @@ pub fn special_restrict_assets( let body_exprs = &args[2..]; let asset_owner = eval(asset_owner_expr, env, context)?; - let asset_owner = asset_owner.expect_principal()?; + let asset_owner = asset_owner + .expect_principal() + .map_err(|_| VmInternalError::Expect("Expected principal".into()))?; runtime_cost( ClarityCostFunction::RestrictAssets, @@ -258,7 +282,8 @@ pub fn special_restrict_assets( Ok(None) => {} Ok(Some(violation_index)) => { env.global_context.roll_back()?; - return Ok(Value::error(Value::UInt(violation_index))?); + return Ok(Value::error(Value::UInt(violation_index)) + .map_err(CheckErrorKind::from_clarity_type_error)?); } Err(e) => { env.global_context.roll_back()?; @@ -272,7 +297,7 @@ pub fn special_restrict_assets( match eval_result { Ok(Some(last)) => { // body completed successfully — commit and return ok(last) - Ok(Value::okay(last)?) + Ok(Value::okay(last).map_err(CheckErrorKind::from_clarity_type_error)?) } Ok(None) => { // Body had no expressions (shouldn't happen due to argument checks) @@ -352,7 +377,7 @@ pub fn special_as_contract( Ok(None) => {} Ok(Some(violation_index)) => { nested_env.global_context.roll_back()?; - return Ok(Value::error(Value::UInt(violation_index))?); + return Ok(Value::error(Value::UInt(violation_index)).map_err(CheckErrorKind::from_clarity_type_error)?); } Err(e) => { nested_env.global_context.roll_back()?; @@ -366,7 +391,7 @@ pub fn special_as_contract( match eval_result { Ok(Some(last)) => { // body completed successfully — commit and return ok(last) - Ok(Value::okay(last)?) + Ok(Value::okay(last).map_err(CheckErrorKind::from_clarity_type_error)?) } Ok(None) => { // Body had no expressions (shouldn't happen due to argument checks) diff --git a/clarity/src/vm/functions/principals.rs b/clarity/src/vm/functions/principals.rs index b708ca62e8..252e3d5939 100644 --- a/clarity/src/vm/functions/principals.rs +++ b/clarity/src/vm/functions/principals.rs @@ -278,7 +278,8 @@ pub fn special_principal_construct( // Construct the principal. let mut transfer_buffer = [0u8; 20]; transfer_buffer.copy_from_slice(verified_hash_bytes); - let principal_data = StandardPrincipalData::new(version_byte, transfer_buffer)?; + let principal_data = StandardPrincipalData::new(version_byte, transfer_buffer) + .map_err(CheckErrorKind::from_clarity_type_error)?; let principal = if let Some(name) = name_opt { // requested a contract principal. Verify that the `name` is a valid ContractName. diff --git a/clarity/src/vm/functions/sequences.rs b/clarity/src/vm/functions/sequences.rs index f9e044e463..ac72c7871a 100644 --- a/clarity/src/vm/functions/sequences.rs +++ b/clarity/src/vm/functions/sequences.rs @@ -16,12 +16,13 @@ use std::cmp; +use clarity_types::errors::VmInternalError; use stacks_common::types::StacksEpochId; use crate::vm::costs::cost_functions::ClarityCostFunction; use crate::vm::costs::{runtime_cost, CostOverflowingMath}; use crate::vm::errors::{ - check_argument_count, check_arguments_at_least, CheckErrorKind, RuntimeError, VmExecutionError, + check_argument_count, check_arguments_at_least, CheckErrorKind, VmExecutionError, }; use crate::vm::representations::SymbolicExpression; use crate::vm::types::signatures::ListTypeData; @@ -40,12 +41,17 @@ pub fn list_cons( let mut arg_size = 0; for a in args.iter() { - arg_size = arg_size.cost_overflow_add(a.size()?.into())?; + arg_size = arg_size.cost_overflow_add( + a.size() + .map_err(CheckErrorKind::from_clarity_type_error)? + .into(), + )?; } runtime_cost(ClarityCostFunction::ListCons, env, arg_size)?; - let value = Value::cons_list(args, env.epoch())?; + let value = + Value::cons_list(args, env.epoch()).map_err(CheckErrorKind::from_clarity_type_error)?; Ok(value) } @@ -79,10 +85,11 @@ pub fn special_filter( })?; } _ => { - return Err( - CheckErrorKind::ExpectedSequence(Box::new(TypeSignature::type_of(&sequence)?)) - .into(), - ) + return Err(CheckErrorKind::ExpectedSequence(Box::new( + TypeSignature::type_of(&sequence) + .map_err(CheckErrorKind::from_clarity_type_error)?, + )) + .into()) } }; Ok(sequence) @@ -105,7 +112,8 @@ pub fn special_fold( match sequence { Value::Sequence(ref mut sequence_data) => sequence_data - .atom_values()? + .atom_values() + .map_err(CheckErrorKind::from_clarity_type_error)? .into_iter() .try_fold(initial, |acc, x| { apply( @@ -115,9 +123,10 @@ pub fn special_fold( context, ) }), - _ => Err( - CheckErrorKind::ExpectedSequence(Box::new(TypeSignature::type_of(&sequence)?)).into(), - ), + _ => Err(CheckErrorKind::ExpectedSequence(Box::new( + TypeSignature::type_of(&sequence).map_err(CheckErrorKind::from_clarity_type_error)?, + )) + .into()), } } @@ -143,7 +152,12 @@ pub fn special_map( match sequence { Value::Sequence(ref mut sequence_data) => { min_args_len = min_args_len.min(sequence_data.len()); - for (apply_index, value) in sequence_data.atom_values()?.into_iter().enumerate() { + for (apply_index, value) in sequence_data + .atom_values() + .map_err(CheckErrorKind::from_clarity_type_error)? + .into_iter() + .enumerate() + { if apply_index > min_args_len { break; } @@ -155,10 +169,11 @@ pub fn special_map( } } _ => { - return Err( - CheckErrorKind::ExpectedSequence(Box::new(TypeSignature::type_of(&sequence)?)) - .into(), - ) + return Err(CheckErrorKind::ExpectedSequence(Box::new( + TypeSignature::type_of(&sequence) + .map_err(CheckErrorKind::from_clarity_type_error)?, + )) + .into()) } } } @@ -179,7 +194,8 @@ pub fn special_map( mapped_results.push(res); } - let value = Value::cons_list(mapped_results, env.epoch())?; + let value = Value::cons_list(mapped_results, env.epoch()) + .map_err(CheckErrorKind::from_clarity_type_error)?; Ok(value) } @@ -199,15 +215,24 @@ pub fn special_append( type_signature, } = list; let (entry_type, size) = type_signature.destruct(); - let element_type = TypeSignature::type_of(&element)?; + let element_type = TypeSignature::type_of(&element) + .map_err(CheckErrorKind::from_clarity_type_error)?; runtime_cost( ClarityCostFunction::Append, env, - u64::from(cmp::max(entry_type.size()?, element_type.size()?)), + u64::from(cmp::max( + entry_type + .size() + .map_err(CheckErrorKind::from_clarity_type_error)?, + element_type + .size() + .map_err(CheckErrorKind::from_clarity_type_error)?, + )), )?; if entry_type.is_no_type() { assert_eq!(size, 0); - return Ok(Value::cons_list(vec![element], env.epoch())?); + return Ok(Value::cons_list(vec![element], env.epoch()) + .map_err(CheckErrorKind::from_clarity_type_error)?); } if let Ok(next_entry_type) = TypeSignature::least_supertype(env.epoch(), &entry_type, &element_type) @@ -215,7 +240,8 @@ pub fn special_append( let (element, _) = Value::sanitize_value(env.epoch(), &next_entry_type, element) .ok_or_else(|| CheckErrorKind::ListTypesMustMatch)?; - let next_type_signature = ListTypeData::new_list(next_entry_type, size + 1)?; + let next_type_signature = ListTypeData::new_list(next_entry_type, size + 1) + .map_err(CheckErrorKind::from_clarity_type_error)?; data.push(element); Ok(Value::Sequence(SequenceData::List(ListData { type_signature: next_type_signature, @@ -244,15 +270,35 @@ pub fn special_concat_v200( runtime_cost( ClarityCostFunction::Concat, env, - u64::from(wrapped_seq.size()?).cost_overflow_add(u64::from(other_wrapped_seq.size()?))?, + u64::from( + wrapped_seq + .size() + .map_err(CheckErrorKind::from_clarity_type_error)?, + ) + .cost_overflow_add(u64::from( + other_wrapped_seq + .size() + .map_err(CheckErrorKind::from_clarity_type_error)?, + ))?, )?; match (&mut wrapped_seq, other_wrapped_seq) { - (Value::Sequence(ref mut seq), Value::Sequence(other_seq)) => { - seq.concat(env.epoch(), other_seq) + (Value::Sequence(ref mut seq), Value::Sequence(other_seq)) => seq + .concat(env.epoch(), other_seq) + .map_err(CheckErrorKind::from_clarity_type_error)?, + (Value::Sequence(ref mut seq_data), other_value) => { + return Err(CheckErrorKind::TypeValueError( + Box::new( + seq_data + .type_signature() + .map_err(CheckErrorKind::from_clarity_type_error)?, + ), + Box::new(other_value), + ) + .into()) } - _ => Err(RuntimeError::BadTypeConstruction.into()), - }?; + _ => return Err(CheckErrorKind::ExpectedSequence(Box::new(TypeSignature::NoType)).into()), + }; Ok(wrapped_seq) } @@ -276,12 +322,25 @@ pub fn special_concat_v205( )?; seq.concat(env.epoch(), other_seq) + .map_err(CheckErrorKind::from_clarity_type_error)? + } + (Value::Sequence(ref mut seq_data), other_value) => { + runtime_cost(ClarityCostFunction::Concat, env, 1)?; + return Err(CheckErrorKind::TypeValueError( + Box::new( + seq_data + .type_signature() + .map_err(CheckErrorKind::from_clarity_type_error)?, + ), + Box::new(other_value), + ) + .into()); } _ => { runtime_cost(ClarityCostFunction::Concat, env, 1)?; - Err(RuntimeError::BadTypeConstruction.into()) + return Err(CheckErrorKind::ExpectedSequence(Box::new(TypeSignature::NoType)).into()); } - }?; + }; Ok(wrapped_seq) } @@ -301,10 +360,11 @@ pub fn special_as_max_len( let sequence_len = match sequence { Value::Sequence(ref sequence_data) => sequence_data.len() as u128, _ => { - return Err( - CheckErrorKind::ExpectedSequence(Box::new(TypeSignature::type_of(&sequence)?)) - .into(), - ) + return Err(CheckErrorKind::ExpectedSequence(Box::new( + TypeSignature::type_of(&sequence) + .map_err(CheckErrorKind::from_clarity_type_error)?, + )) + .into()) } }; if sequence_len > *expected_len { @@ -313,13 +373,16 @@ pub fn special_as_max_len( if let Value::Sequence(SequenceData::List(ref mut list)) = sequence { list.type_signature.reduce_max_len(*expected_len as u32); } - Ok(Value::some(sequence)?) + Ok(Value::some(sequence).map_err(CheckErrorKind::from_clarity_type_error)?) } } else { let actual_len = eval(&args[1], env, context)?; Err(CheckErrorKind::TypeError( Box::new(TypeSignature::UIntType), - Box::new(TypeSignature::type_of(&actual_len)?), + Box::new( + TypeSignature::type_of(&actual_len) + .map_err(CheckErrorKind::from_clarity_type_error)?, + ), ) .into()) } @@ -328,20 +391,28 @@ pub fn special_as_max_len( pub fn native_len(sequence: Value) -> Result { match sequence { Value::Sequence(sequence_data) => Ok(Value::UInt(sequence_data.len() as u128)), - _ => Err( - CheckErrorKind::ExpectedSequence(Box::new(TypeSignature::type_of(&sequence)?)).into(), - ), + _ => Err(CheckErrorKind::ExpectedSequence(Box::new( + TypeSignature::type_of(&sequence).map_err(CheckErrorKind::from_clarity_type_error)?, + )) + .into()), } } pub fn native_index_of(sequence: Value, to_find: Value) -> Result { if let Value::Sequence(sequence_data) = sequence { - match sequence_data.contains(to_find)? { - Some(index) => Ok(Value::some(Value::UInt(index as u128))?), + match sequence_data + .contains(to_find) + .map_err(CheckErrorKind::from_clarity_type_error)? + { + Some(index) => Ok(Value::some(Value::UInt(index as u128)) + .map_err(CheckErrorKind::from_clarity_type_error)?), None => Ok(Value::none()), } } else { - Err(CheckErrorKind::ExpectedSequence(Box::new(TypeSignature::type_of(&sequence)?)).into()) + Err(CheckErrorKind::ExpectedSequence(Box::new( + TypeSignature::type_of(&sequence).map_err(CheckErrorKind::from_clarity_type_error)?, + )) + .into()) } } @@ -349,9 +420,10 @@ pub fn native_element_at(sequence: Value, index: Value) -> Result Result { + // TODO: REMOVE THIS COMMENT: Is it better to keep RuntimeError::BadTypeConstruction? It just seems weird + // to have this error. if we are going to have an error for htings that shouldn't really hit, I feel like + // it should be more explicit like an ExpectAcceptable(String) error here instead. But handling explicitly + // for now until I get feedback. + + // These errors should not really ever occur at runtime. These should have already been caught + // during static analysis. However, handle them just in case. + + // seq must be a sequence + if !matches!(seq, Value::Sequence(_)) { + return Err(CheckErrorKind::ExpectedSequence(Box::new( + TypeSignature::NoType, + ))); + } + + // left must be uint + if !matches!(left_position, Value::UInt(_)) { + return Err(CheckErrorKind::TypeValueError( + Box::new(TypeSignature::UIntType), + Box::new(left_position), + )); + } + + // right must be uint + Err(CheckErrorKind::TypeValueError( + Box::new(TypeSignature::UIntType), + Box::new(right_position), + )) } - _ => Err(RuntimeError::BadTypeConstruction.into()), } })(); @@ -421,7 +528,7 @@ pub fn special_slice( Ok(sliced_seq) => Ok(sliced_seq), Err(e) => { runtime_cost(ClarityCostFunction::Slice, env, 0)?; - Err(e) + Err(e.into()) } } } @@ -434,10 +541,16 @@ pub fn special_replace_at( check_argument_count(3, args)?; let seq = eval(&args[0], env, context)?; - let seq_type = TypeSignature::type_of(&seq)?; + let seq_type = TypeSignature::type_of(&seq).map_err(CheckErrorKind::from_clarity_type_error)?; // runtime is the cost to copy over one element into its place - runtime_cost(ClarityCostFunction::ReplaceAt, env, seq_type.size()?)?; + runtime_cost( + ClarityCostFunction::ReplaceAt, + env, + seq_type + .size() + .map_err(CheckErrorKind::from_clarity_type_error)?, + )?; let expected_elem_type = if let TypeSignature::SequenceType(seq_subtype) = &seq_type { seq_subtype.unit_type() @@ -448,7 +561,9 @@ pub fn special_replace_at( let new_element = eval(&args[2], env, context)?; if expected_elem_type != TypeSignature::NoType - && !expected_elem_type.admits(env.epoch(), &new_element)? + && !expected_elem_type + .admits(env.epoch(), &new_element) + .map_err(CheckErrorKind::from_clarity_type_error)? { return Err(CheckErrorKind::TypeValueError( Box::new(expected_elem_type), @@ -471,13 +586,14 @@ pub fn special_replace_at( .into()); }; - if let Value::Sequence(data) = seq { - let seq_len = data.len(); - if index >= seq_len { - return Ok(Value::none()); - } - data.replace_at(env.epoch(), index, new_element) - } else { - Err(CheckErrorKind::ExpectedSequence(Box::new(seq_type)).into()) + let Value::Sequence(data) = seq else { + return Err(CheckErrorKind::ExpectedSequence(Box::new(seq_type)).into()); + }; + let seq_len = data.len(); + if index >= seq_len { + return Ok(Value::none()); } + Ok(data + .replace_at(env.epoch(), index, new_element) + .map_err(CheckErrorKind::from_clarity_type_error)?) } diff --git a/clarity/src/vm/functions/tuples.rs b/clarity/src/vm/functions/tuples.rs index 4a177424fc..04dcf7a7ee 100644 --- a/clarity/src/vm/functions/tuples.rs +++ b/clarity/src/vm/functions/tuples.rs @@ -37,7 +37,9 @@ pub fn tuple_cons( let bindings = parse_eval_bindings(args, SyntaxBindingErrorType::TupleCons, env, context)?; runtime_cost(ClarityCostFunction::TupleCons, env, bindings.len())?; - Ok(TupleData::from_data(bindings).map(Value::from)?) + Ok(TupleData::from_data(bindings) + .map(Value::from) + .map_err(CheckErrorKind::from_clarity_type_error)?) } pub fn tuple_get( @@ -59,16 +61,22 @@ pub fn tuple_get( Some(data) => { if let Value::Tuple(tuple_data) = *data { runtime_cost(ClarityCostFunction::TupleGet, env, tuple_data.len())?; - Ok(Value::some(tuple_data.get_owned(arg_name)?).map_err(|_| { + Ok(Value::some( + tuple_data + .get_owned(arg_name) + .map_err(CheckErrorKind::from_clarity_type_error)?, + ) + .map_err(|_| { VmInternalError::Expect( "Tuple contents should *always* fit in a some wrapper".into(), ) })?) } else { - Err( - CheckErrorKind::ExpectedTuple(Box::new(TypeSignature::type_of(&data)?)) - .into(), - ) + Err(CheckErrorKind::ExpectedTuple(Box::new( + TypeSignature::type_of(&data) + .map_err(CheckErrorKind::from_clarity_type_error)?, + )) + .into()) } } None => Ok(Value::none()), // just pass through none-types. @@ -76,9 +84,14 @@ pub fn tuple_get( } Value::Tuple(tuple_data) => { runtime_cost(ClarityCostFunction::TupleGet, env, tuple_data.len())?; - Ok(tuple_data.get_owned(arg_name)?) + Ok(tuple_data + .get_owned(arg_name) + .map_err(CheckErrorKind::from_clarity_type_error)?) } - _ => Err(CheckErrorKind::ExpectedTuple(Box::new(TypeSignature::type_of(&value)?)).into()), + _ => Err(CheckErrorKind::ExpectedTuple(Box::new( + TypeSignature::type_of(&value).map_err(CheckErrorKind::from_clarity_type_error)?, + )) + .into()), } } @@ -86,14 +99,14 @@ pub fn tuple_merge(base: Value, update: Value) -> Result Ok(initial_values), _ => Err(CheckErrorKind::ExpectedTuple(Box::new( - TypeSignature::type_of(&base)?, + TypeSignature::type_of(&base).map_err(CheckErrorKind::from_clarity_type_error)?, ))), }?; let new_values = match update { Value::Tuple(new_values) => Ok(new_values), _ => Err(CheckErrorKind::ExpectedTuple(Box::new( - TypeSignature::type_of(&update)?, + TypeSignature::type_of(&update).map_err(CheckErrorKind::from_clarity_type_error)?, ))), }?; diff --git a/clarity/src/vm/mod.rs b/clarity/src/vm/mod.rs index 9a3799f92f..2b02e6f53e 100644 --- a/clarity/src/vm/mod.rs +++ b/clarity/src/vm/mod.rs @@ -179,13 +179,28 @@ fn lookup_variable( context.depth(), )?; if let Some(value) = context.lookup_variable(name) { - runtime_cost(ClarityCostFunction::LookupVariableSize, env, value.size()?)?; + runtime_cost( + ClarityCostFunction::LookupVariableSize, + env, + value + .size() + .map_err(CheckErrorKind::from_clarity_type_error)?, + )?; Ok(value.clone()) } else if let Some(value) = env.contract_context.lookup_variable(name).cloned() { - runtime_cost(ClarityCostFunction::LookupVariableSize, env, value.size()?)?; - let (value, _) = - Value::sanitize_value(env.epoch(), &TypeSignature::type_of(&value)?, value) - .ok_or_else(|| CheckErrorKind::CouldNotDetermineType)?; + runtime_cost( + ClarityCostFunction::LookupVariableSize, + env, + value + .size() + .map_err(CheckErrorKind::from_clarity_type_error)?, + )?; + let (value, _) = Value::sanitize_value( + env.epoch(), + &TypeSignature::type_of(&value).map_err(CheckErrorKind::from_clarity_type_error)?, + value, + ) + .ok_or_else(|| CheckErrorKind::CouldNotDetermineType)?; Ok(value) } else if let Some(callable_data) = context.lookup_callable_contract(name) { if env.contract_context.get_clarity_version() < &ClarityVersion::Clarity2 { @@ -417,13 +432,13 @@ pub fn eval_all( contract_context.functions.insert(name, value); }, DefineResult::PersistedVariable(name, value_type, value) => { - runtime_cost(ClarityCostFunction::CreateVar, global_context, value_type.size()?)?; + runtime_cost(ClarityCostFunction::CreateVar, global_context, value_type.size().map_err(CheckErrorKind::from_clarity_type_error)?)?; contract_context.persisted_names.insert(name.clone()); global_context.add_memory(value_type.type_size() - .map_err(|_| VmInternalError::Expect("Type size should be realizable".into()))? as u64)?; + .map_err(|_| VmInternalError::Expect("Type size should be realizable".into()))?.into())?; - global_context.add_memory(value.size()? as u64)?; + global_context.add_memory(value.size().map_err(CheckErrorKind::from_clarity_type_error)?.into())?; let data_type = global_context.database.create_variable(&contract_context.contract_identifier, &name, value_type)?; global_context.database.set_variable(&contract_context.contract_identifier, &name, value, &data_type, &global_context.epoch_id)?; @@ -432,14 +447,14 @@ pub fn eval_all( }, DefineResult::Map(name, key_type, value_type) => { runtime_cost(ClarityCostFunction::CreateMap, global_context, - u64::from(key_type.size()?).cost_overflow_add( - u64::from(value_type.size()?))?)?; + u64::from(key_type.size().map_err(CheckErrorKind::from_clarity_type_error)?).cost_overflow_add( + u64::from(value_type.size().map_err(CheckErrorKind::from_clarity_type_error)?))?)?; contract_context.persisted_names.insert(name.clone()); global_context.add_memory(key_type.type_size() - .map_err(|_| VmInternalError::Expect("Type size should be realizable".into()))? as u64)?; + .map_err(|_| VmInternalError::Expect("Type size should be realizable".into()))?.into())?; global_context.add_memory(value_type.type_size() - .map_err(|_| VmInternalError::Expect("Type size should be realizable".into()))? as u64)?; + .map_err(|_| VmInternalError::Expect("Type size should be realizable".into()))?.into())?; let data_type = global_context.database.create_map(&contract_context.contract_identifier, &name, key_type, value_type)?; @@ -450,18 +465,18 @@ pub fn eval_all( contract_context.persisted_names.insert(name.clone()); global_context.add_memory(TypeSignature::UIntType.type_size() - .map_err(|_| VmInternalError::Expect("Type size should be realizable".into()))? as u64)?; + .map_err(|_| VmInternalError::Expect("Type size should be realizable".into()))?.into())?; let data_type = global_context.database.create_fungible_token(&contract_context.contract_identifier, &name, &total_supply)?; contract_context.meta_ft.insert(name, data_type); }, DefineResult::NonFungibleAsset(name, asset_type) => { - runtime_cost(ClarityCostFunction::CreateNft, global_context, asset_type.size()?)?; + runtime_cost(ClarityCostFunction::CreateNft, global_context, asset_type.size().map_err(CheckErrorKind::from_clarity_type_error)?)?; contract_context.persisted_names.insert(name.clone()); global_context.add_memory(asset_type.type_size() - .map_err(|_| VmInternalError::Expect("Type size should be realizable".into()))? as u64)?; + .map_err(|_| VmInternalError::Expect("Type size should be realizable".into()))?.into())?; let data_type = global_context.database.create_non_fungible_token(&contract_context.contract_identifier, &name, &asset_type)?; diff --git a/clarity/src/vm/tests/datamaps.rs b/clarity/src/vm/tests/datamaps.rs index ae71745a30..3de8e8f557 100644 --- a/clarity/src/vm/tests/datamaps.rs +++ b/clarity/src/vm/tests/datamaps.rs @@ -13,15 +13,19 @@ // // You should have received a copy of the GNU General Public License // along with this program. If not, see . +use clarity_types::types::ClarityTypeError; +#[cfg(test)] +use clarity_types::VmExecutionError; + use crate::vm::types::{TupleData, Value}; #[cfg(test)] use crate::vm::{ errors::{CheckErrorKind, EarlyReturnError, SyntaxBindingError}, types::{ListData, SequenceData, TupleTypeSignature, TypeSignature}, }; -use crate::vm::{execute, ClarityName, VmExecutionError}; +use crate::vm::{execute, ClarityName}; -fn assert_executes(expected: Result, input: &str) { +fn assert_executes(expected: Result, input: &str) { assert_eq!(expected.unwrap(), execute(input).unwrap().unwrap()); } diff --git a/clarity/src/vm/tests/representations.rs b/clarity/src/vm/tests/representations.rs index e96db9ba73..6bd017efa7 100644 --- a/clarity/src/vm/tests/representations.rs +++ b/clarity/src/vm/tests/representations.rs @@ -13,11 +13,11 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +use clarity_types::types::ClarityTypeError; use proptest::prelude::*; use proptest::string::string_regex; use stacks_common::codec::StacksMessageCodec; -use crate::vm::errors::RuntimeError; use crate::vm::representations::{ CLARITY_NAME_REGEX_STRING, CONTRACT_MAX_NAME_LENGTH, CONTRACT_MIN_NAME_LENGTH, CONTRACT_NAME_REGEX_STRING, MAX_STRING_LEN, @@ -161,11 +161,11 @@ fn any_invalid_clarity_name() -> impl Strategy { fn prop_clarity_name_invalid_patterns() { proptest!(|(name in any_invalid_clarity_name())| { let result = ClarityName::try_from(name.clone()); - prop_assert!(result.is_err(), "Expected invalid name '{}' to be rejected", name); + prop_assert!(result.is_err(), "Expected invalid name '{name}' to be rejected"); prop_assert!(matches!( result.unwrap_err(), - RuntimeError::BadNameValue(_, _) - ), "Expected BadNameValue error for invalid name '{}'", name); + ClarityTypeError::InvalidClarityName(_) + ), "Expected BadNameValue error for invalid name '{name}'"); }); } @@ -299,11 +299,11 @@ fn any_invalid_contract_name() -> impl Strategy { fn prop_contract_name_invalid_patterns() { proptest!(|(name in any_invalid_contract_name())| { let result = ContractName::try_from(name.clone()); - prop_assert!(result.is_err(), "Expected invalid contract name '{}' to be rejected", name); + prop_assert!(result.is_err(), "Expected invalid contract name '{name}' to be rejected"); prop_assert!(matches!( result.unwrap_err(), - RuntimeError::BadNameValue(_, _) - ), "Expected BadNameValue error for invalid contract name '{}'", name); + ClarityTypeError::InvalidContractName(_) + ), "Expected BadNameValue error for invalid contract name '{name}'"); }); } diff --git a/clarity/src/vm/tests/sequences.rs b/clarity/src/vm/tests/sequences.rs index 5796d258c9..4e26dadf57 100644 --- a/clarity/src/vm/tests/sequences.rs +++ b/clarity/src/vm/tests/sequences.rs @@ -22,14 +22,14 @@ use stacks_common::types::StacksEpochId; use crate::vm::tests::test_clarity_versions; #[cfg(test)] use crate::vm::{ - errors::{CheckErrorKind, RuntimeError, VmExecutionError}, + errors::{CheckErrorKind, VmExecutionError}, execute, execute_v2, types::{ signatures::{ SequenceSubtype::{self, BufferType, StringType}, StringSubtype::ASCII, }, - BufferLength, StringSubtype, StringUTF8Length, + BufferLength, ListTypeData, StringSubtype, StringUTF8Length, TypeSignature::{self, BoolType, IntType, SequenceType, UIntType}, Value, }, @@ -552,6 +552,24 @@ fn test_slice_utf8() { } } +#[test] +fn test_slice_type_errors() { + assert_eq!( + execute_v2("(slice? 3 u0 u1)").unwrap_err(), + CheckErrorKind::ExpectedSequence(Box::new(TypeSignature::NoType)).into() + ); + + assert_eq!( + execute_v2("(slice? (list 1 2 3) 0 u1)").unwrap_err(), + CheckErrorKind::TypeValueError(Box::new(UIntType), Box::new(Value::Int(0))).into() + ); + + assert_eq!( + execute_v2("(slice? (list 1 2 3) u0 1)").unwrap_err(), + CheckErrorKind::TypeValueError(Box::new(UIntType), Box::new(Value::Int(1))).into() + ); +} + #[test] fn test_simple_list_concat() { let tests = [ @@ -587,17 +605,33 @@ fn test_simple_list_concat() { assert_eq!( execute("(concat (list 1) (list u4 u8))").unwrap_err(), - CheckErrorKind::TypeError(Box::new(IntType), Box::new(UIntType)).into() + CheckErrorKind::TypeError( + Box::new(TypeSignature::IntType), + Box::new(TypeSignature::UIntType) + ) + .into() ); assert_eq!( execute("(concat (list 1) 3)").unwrap_err(), - RuntimeError::BadTypeConstruction.into() + CheckErrorKind::TypeValueError( + Box::new(SequenceType(SequenceSubtype::ListType( + ListTypeData::new_list(TypeSignature::IntType, 1).unwrap() + ))), + Box::new(Value::Int(3)) + ) + .into() ); assert_eq!( execute("(concat (list 1) \"1\")").unwrap_err(), - RuntimeError::BadTypeConstruction.into() + CheckErrorKind::TypeError( + Box::new(SequenceType(SequenceSubtype::ListType( + ListTypeData::new_list(TypeSignature::IntType, 1).unwrap() + ))), + Box::new(TypeSignature::STRING_ASCII_MIN) + ) + .into() ); } @@ -623,12 +657,22 @@ fn test_simple_buff_concat() { assert_eq!( execute("(concat 0x31 3)").unwrap_err(), - RuntimeError::BadTypeConstruction.into() + CheckErrorKind::TypeValueError( + Box::new(TypeSignature::BUFFER_MIN), + Box::new(Value::Int(3)) + ) + .into() ); assert_eq!( execute("(concat 0x31 (list 1))").unwrap_err(), - RuntimeError::BadTypeConstruction.into() + CheckErrorKind::TypeError( + Box::new(TypeSignature::BUFFER_MIN), + Box::new(TypeSignature::SequenceType(SequenceSubtype::ListType( + ListTypeData::new_list(TypeSignature::IntType, 1).unwrap() + ))) + ) + .into() ); } diff --git a/clarity/src/vm/types/mod.rs b/clarity/src/vm/types/mod.rs index 3ef4d04b73..f70d3e6143 100644 --- a/clarity/src/vm/types/mod.rs +++ b/clarity/src/vm/types/mod.rs @@ -103,7 +103,7 @@ impl BurnBlockInfoProperty { ("hashbytes".into(), TypeSignature::BUFFER_32), ]) .map_err(|_| { - CheckErrorKind::Expects( + CheckErrorKind::ExpectsRejectable( "FATAL: bad type signature for pox addr".into(), ) })?, @@ -111,12 +111,14 @@ impl BurnBlockInfoProperty { 2, ) .map_err(|_| { - CheckErrorKind::Expects("FATAL: bad list type signature".into()) + CheckErrorKind::ExpectsRejectable("FATAL: bad list type signature".into()) })?, ), ("payout".into(), TypeSignature::UIntType), ]) - .map_err(|_| CheckErrorKind::Expects("FATAL: bad type signature for pox addr".into()))? + .map_err(|_| { + CheckErrorKind::ExpectsRejectable("FATAL: bad type signature for pox addr".into()) + })? .into(), }; Ok(result) diff --git a/clarity/src/vm/types/signatures.rs b/clarity/src/vm/types/signatures.rs index 4f9dca8740..504d836aed 100644 --- a/clarity/src/vm/types/signatures.rs +++ b/clarity/src/vm/types/signatures.rs @@ -236,7 +236,9 @@ impl TypeSignatureExt for TypeSignature { let entry_type = TypeSignature::parse_type_repr(epoch, atomic_type_arg, accounting)?; let max_len = u32::try_from(*max_len).map_err(|_| CommonCheckErrorKind::ValueTooLarge)?; - ListTypeData::new_list(entry_type, max_len).map(|x| x.into()) + Ok(ListTypeData::new_list(entry_type, max_len) + .map_err(CommonCheckErrorKind::from_clarity_type_error)? + .into()) } else { Err(CommonCheckErrorKind::InvalidTypeDescription) } @@ -255,7 +257,8 @@ impl TypeSignatureExt for TypeSignature { SyntaxBindingErrorType::TupleCons, accounting, )?; - let tuple_type_signature = TupleTypeSignature::try_from(mapped_key_types)?; + let tuple_type_signature = TupleTypeSignature::try_from(mapped_key_types) + .map_err(CommonCheckErrorKind::from_clarity_type_error)?; Ok(TypeSignature::from(tuple_type_signature)) } @@ -268,8 +271,10 @@ impl TypeSignatureExt for TypeSignature { return Err(CommonCheckErrorKind::InvalidTypeDescription); } if let SymbolicExpressionType::LiteralValue(Value::Int(buff_len)) = &type_args[0].expr { - BufferLength::try_from(*buff_len) - .map(|buff_len| SequenceType(SequenceSubtype::BufferType(buff_len))) + Ok(SequenceType(SequenceSubtype::BufferType( + BufferLength::try_from(*buff_len) + .map_err(CommonCheckErrorKind::from_clarity_type_error)?, + ))) } else { Err(CommonCheckErrorKind::InvalidTypeDescription) } @@ -284,9 +289,12 @@ impl TypeSignatureExt for TypeSignature { return Err(CommonCheckErrorKind::InvalidTypeDescription); } if let SymbolicExpressionType::LiteralValue(Value::Int(utf8_len)) = &type_args[0].expr { - StringUTF8Length::try_from(*utf8_len).map(|utf8_len| { - SequenceType(SequenceSubtype::StringType(StringSubtype::UTF8(utf8_len))) - }) + Ok(SequenceType(SequenceSubtype::StringType( + StringSubtype::UTF8( + StringUTF8Length::try_from(*utf8_len) + .map_err(CommonCheckErrorKind::from_clarity_type_error)?, + ), + ))) } else { Err(CommonCheckErrorKind::InvalidTypeDescription) } @@ -301,9 +309,12 @@ impl TypeSignatureExt for TypeSignature { return Err(CommonCheckErrorKind::InvalidTypeDescription); } if let SymbolicExpressionType::LiteralValue(Value::Int(buff_len)) = &type_args[0].expr { - BufferLength::try_from(*buff_len).map(|buff_len| { - SequenceType(SequenceSubtype::StringType(StringSubtype::ASCII(buff_len))) - }) + Ok(SequenceType(SequenceSubtype::StringType( + StringSubtype::ASCII( + BufferLength::try_from(*buff_len) + .map_err(CommonCheckErrorKind::from_clarity_type_error)?, + ), + ))) } else { Err(CommonCheckErrorKind::InvalidTypeDescription) } @@ -319,7 +330,7 @@ impl TypeSignatureExt for TypeSignature { } let inner_type = TypeSignature::parse_type_repr(epoch, &type_args[0], accounting)?; - TypeSignature::new_option(inner_type) + TypeSignature::new_option(inner_type).map_err(CommonCheckErrorKind::from_clarity_type_error) } fn parse_response_type_repr( @@ -332,7 +343,8 @@ impl TypeSignatureExt for TypeSignature { } let ok_type = TypeSignature::parse_type_repr(epoch, &type_args[0], accounting)?; let err_type = TypeSignature::parse_type_repr(epoch, &type_args[1], accounting)?; - let response_type = TypeSignature::new_response(ok_type, err_type)?; + let response_type = TypeSignature::new_response(ok_type, err_type) + .map_err(CommonCheckErrorKind::from_clarity_type_error)?; Ok(response_type) } @@ -505,10 +517,17 @@ impl TypeSignatureExt for TypeSignature { impl FixedFunction { pub fn total_type_size(&self) -> Result { - let mut function_type_size = u64::from(self.returns.type_size()?); + let mut function_type_size = u64::from( + self.returns + .type_size() + .map_err(StaticCheckErrorKind::from_clarity_type_error)?, + ); for arg in self.args.iter() { - function_type_size = - function_type_size.cost_overflow_add(u64::from(arg.signature.type_size()?))?; + function_type_size = function_type_size.cost_overflow_add(u64::from( + arg.signature + .type_size() + .map_err(StaticCheckErrorKind::from_clarity_type_error)?, + ))?; } Ok(function_type_size) } @@ -516,10 +535,17 @@ impl FixedFunction { impl FunctionSignature { pub fn total_type_size(&self) -> Result { - let mut function_type_size = u64::from(self.returns.type_size()?); + let mut function_type_size = u64::from( + self.returns + .type_size() + .map_err(StaticCheckErrorKind::from_clarity_type_error)?, + ); for arg in self.args.iter() { function_type_size = function_type_size - .cost_overflow_add(u64::from(arg.type_size()?)) + .cost_overflow_add(u64::from( + arg.type_size() + .map_err(StaticCheckErrorKind::from_clarity_type_error)?, + )) .map_err(|_| StaticCheckErrorKind::CostOverflow)?; } Ok(function_type_size) @@ -535,7 +561,10 @@ impl FunctionSignature { } let args_iter = self.args.iter().zip(args.iter()); for (expected_arg, arg) in args_iter { - if !arg.admits_type(epoch, expected_arg)? { + if !arg + .admits_type(epoch, expected_arg) + .map_err(CommonCheckErrorKind::from_clarity_type_error)? + { return Ok(false); } } @@ -648,6 +677,7 @@ impl fmt::Display for FunctionArg { mod test { use clarity_types::errors::CheckErrorKind; use clarity_types::errors::CheckErrorKind::*; + use clarity_types::types::ClarityTypeError; #[cfg(test)] use rstest::rstest; #[cfg(test)] @@ -698,7 +728,7 @@ mod test { assert_eq!( TupleTypeSignature::try_from(keys).unwrap_err(), - CommonCheckErrorKind::ValueTooLarge + ClarityTypeError::ValueTooLarge ); } diff --git a/contrib/stacks-cli/Cargo.toml b/contrib/stacks-cli/Cargo.toml index a942aa50c3..93c0bfed19 100644 --- a/contrib/stacks-cli/Cargo.toml +++ b/contrib/stacks-cli/Cargo.toml @@ -9,6 +9,7 @@ clarity = { path = "../../clarity", default-features = false } clarity-cli = { path = "../clarity-cli", default-features = false } stacks-common = { path = "../../stacks-common", default-features = false } serde_json = { workspace = true } +thiserror = { workspace = true } [dev-dependencies] stacks-common = { path = "../../stacks-common", default-features = false, features = ["testing"] } diff --git a/contrib/stacks-cli/src/main.rs b/contrib/stacks-cli/src/main.rs index 2014be553f..7d10b7b5a5 100644 --- a/contrib/stacks-cli/src/main.rs +++ b/contrib/stacks-cli/src/main.rs @@ -22,7 +22,7 @@ use std::io::Read; use std::io::prelude::*; use std::{env, fs, io}; -use clarity::vm::errors::{RuntimeError, VmExecutionError}; +use clarity::vm::errors::{ClarityTypeError, VmExecutionError}; use clarity::vm::types::PrincipalData; use clarity::vm::{ClarityName, ClarityVersion, ContractName, Value}; use clarity_cli::vm_execute; @@ -182,55 +182,25 @@ raw binary microblocks will be read from stdin. N.B. Stacks microblocks are not stored as files in the Stacks chainstate -- they are stored in block's sqlite database."; -#[derive(Debug)] +#[derive(thiserror::Error, Debug)] enum CliError { - ClarityRuntimeError(RuntimeError), - ClarityGeneralError(VmExecutionError), + #[error("Clarity error: {0}")] + ClarityGeneralError(#[from] VmExecutionError), + #[error("Clarity error: {0}")] + ClarityTypeError(#[from] ClarityTypeError), + #[error("{0}")] Message(String), + #[error("{USAGE}")] Usage, + #[error("Invalid chain ID: {0}")] InvalidChainId(std::num::ParseIntError), } -impl std::error::Error for CliError { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - match self { - CliError::ClarityRuntimeError(e) => Some(e), - CliError::ClarityGeneralError(e) => Some(e), - _ => None, - } - } -} - -impl std::fmt::Display for CliError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - CliError::ClarityRuntimeError(e) => write!(f, "Clarity error: {e:?}"), - CliError::ClarityGeneralError(e) => write!(f, "Clarity error: {e}"), - CliError::Message(e) => write!(f, "{e}"), - CliError::Usage => write!(f, "{USAGE}"), - CliError::InvalidChainId(e) => write!(f, "Invalid chain ID: {e}"), - } - } -} - impl From<&str> for CliError { fn from(value: &str) -> Self { CliError::Message(value.into()) } } - -impl From for CliError { - fn from(value: RuntimeError) -> Self { - CliError::ClarityRuntimeError(value) - } -} - -impl From for CliError { - fn from(value: VmExecutionError) -> Self { - CliError::ClarityGeneralError(value) - } -} - impl From for CliError { fn from(value: NetError) -> Self { CliError::Message(format!("Stacks NetError: {value}")) @@ -1628,7 +1598,7 @@ mod test { let result = main_handler(to_string_vec(&cc_args)); assert!(result.is_err(), "Result should be err!"); - let expected_msg = "Failed to deserialize: Deserialization error: Bad hex string"; + let expected_msg = "Failed to deserialize: Deserialization failure: Bad hex string"; assert_eq!(expected_msg, result.unwrap_err().to_string()); } diff --git a/stacks-common/src/util/macros.rs b/stacks-common/src/util/macros.rs index cbf602cfa1..57b1fa5f8f 100644 --- a/stacks-common/src/util/macros.rs +++ b/stacks-common/src/util/macros.rs @@ -204,19 +204,19 @@ macro_rules! define_versioned_named_enum_internal { #[allow(clippy::crate_in_macro_def)] #[macro_export] macro_rules! guarded_string { - ($Name:ident, $Label:literal, $Regex:expr, $MaxStringLength:expr, $ErrorType:ty, $ErrorVariant:path) => { + ($Name:ident, $Regex:expr, $MaxStringLength:expr, $ErrorType:ty, $ErrorVariant:path) => { #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct $Name(String); impl TryFrom for $Name { type Error = $ErrorType; fn try_from(value: String) -> Result { if value.len() > ($MaxStringLength as usize) { - return Err($ErrorVariant($Label, value)); + return Err($ErrorVariant(value)); } if $Regex.is_match(&value) { Ok(Self(value)) } else { - Err($ErrorVariant($Label, value)) + Err($ErrorVariant(value)) } } } @@ -426,8 +426,8 @@ macro_rules! impl_array_newtype { } } + #[allow(clippy::non_canonical_clone_impl)] impl Clone for $thing { - #[allow(clippy::non_canonical_clone_impl)] fn clone(&self) -> Self { $thing(self.0.clone()) } diff --git a/stacks-node/src/tests/signer/commands/block_wait.rs b/stacks-node/src/tests/signer/commands/block_wait.rs index 04e9b9f2b7..7706055a43 100644 --- a/stacks-node/src/tests/signer/commands/block_wait.rs +++ b/stacks-node/src/tests/signer/commands/block_wait.rs @@ -4,7 +4,6 @@ use std::sync::Arc; use libsigner::v0::messages::RejectReason; use madhouse::{Command, CommandWrapper}; use proptest::prelude::{Just, Strategy}; -use proptest::prop_oneof; use stacks::chainstate::stacks::{TenureChangeCause, TenureChangePayload, TransactionPayload}; use super::context::{SignerTestContext, SignerTestState}; diff --git a/stacks-signer/src/client/mod.rs b/stacks-signer/src/client/mod.rs index 9c893237be..483b7ec7bd 100644 --- a/stacks-signer/src/client/mod.rs +++ b/stacks-signer/src/client/mod.rs @@ -21,7 +21,7 @@ pub(crate) mod stacks_client; use std::time::Duration; -use clarity::vm::errors::VmExecutionError; +use clarity::vm::errors::ClarityTypeError; use clarity::vm::types::serialization::SerializationError; use libsigner::RPCError; use libstackerdb::Error as StackerDBError; @@ -79,9 +79,9 @@ pub enum ClientError { /// Not connected #[error("Not connected")] NotConnected, - /// Clarity interpreter error - #[error("Clarity interpreter error: {0}")] - ClarityError(#[from] VmExecutionError), + /// Clarity type error + #[error("Clarity error: {0}")] + ClarityError(#[from] ClarityTypeError), /// Malformed reward set #[error("Malformed contract data: {0}")] MalformedContractData(String), diff --git a/stackslib/src/chainstate/nakamoto/coordinator/mod.rs b/stackslib/src/chainstate/nakamoto/coordinator/mod.rs index a154c33062..e7eb8a4548 100644 --- a/stackslib/src/chainstate/nakamoto/coordinator/mod.rs +++ b/stackslib/src/chainstate/nakamoto/coordinator/mod.rs @@ -109,16 +109,16 @@ impl OnChainRewardSetProvider<'_, T> { sortdb, block_id, SIGNERS_NAME, - &format!("(map-get? cycle-set-height u{})", cycle), + &format!("(map-get? cycle-set-height u{cycle})"), )? .expect_optional() - .map_err(|e| Error::ChainstateError(e.into()))? + .map_err(ChainstateError::from)? .map(|x| { let as_u128 = x.expect_u128()?; - Ok(u64::try_from(as_u128).expect("FATAL: block height exceeded u64")) + u64::try_from(as_u128) + .map_err(|_| ChainstateError::Expects("block height exceeded u64".into())) }) - .transpose() - .map_err(|e| Error::ChainstateError(ChainstateError::ClarityError(e)))? + .transpose()? else { err_or_debug!( debug_log, @@ -156,13 +156,13 @@ impl OnChainRewardSetProvider<'_, T> { ) .map_err(ChainstateError::ClarityError)? .expect_optional() - .map_err(|e| Error::ChainstateError(e.into()))? + .map_err(ChainstateError::from)? .map(|x| { let as_u128 = x.expect_u128()?; - Ok(u64::try_from(as_u128).expect("FATAL: block height exceeded u64")) + u64::try_from(as_u128) + .map_err(|_| ChainstateError::Expects("block height exceeded u64".into())) }) - .transpose() - .map_err(|e| Error::ChainstateError(ChainstateError::ClarityError(e)))? + .transpose()? else { error!( "The reward set was not written to .signers before it was needed by Nakamoto"; @@ -298,7 +298,9 @@ pub fn get_nakamoto_reward_cycle_info( let burn_height = burnchain.nakamoto_first_block_of_cycle(reward_cycle); let epoch_at_height = SortitionDB::get_stacks_epoch(sort_db.conn(), burn_height)? - .unwrap_or_else(|| panic!("FATAL: no epoch defined for burn height {}", burn_height)) + .ok_or_else(|| { + ChainstateError::Expects(format!("no epoch defined for burn height {burn_height}")) + })? .epoch_id; assert!( @@ -366,9 +368,13 @@ pub fn load_nakamoto_reward_set( let prepare_phase_start_height = cycle_start_height.saturating_sub(u64::from(burnchain.pox_constants.prepare_length)); let epoch_at_height = - SortitionDB::get_stacks_epoch(sort_db.conn(), prepare_phase_start_height)?.unwrap_or_else( - || panic!("FATAL: no epoch defined for burn height {prepare_phase_start_height}"), - ); + SortitionDB::get_stacks_epoch(sort_db.conn(), prepare_phase_start_height)?.ok_or_else( + || { + ChainstateError::Expects(format!( + "FATAL: no epoch defined for burn height {prepare_phase_start_height}" + )) + }, + )?; if epoch_at_height.epoch_id < StacksEpochId::Epoch30 { // in epoch 2.5, and in the first reward cycle of epoch 3.0, the reward set can *only* be found in the sortition DB. // The nakamoto chain-processing rules aren't active yet, so we can't look for the reward @@ -507,7 +513,7 @@ pub fn load_nakamoto_reward_set( sort_db.conn(), &anchor_block_header.consensus_hash, )? - .expect("FATAL: no snapshot for winning PoX anchor block"); + .ok_or_else(|| ChainstateError::Expects("no snapshot for winning PoX anchor block".into()))?; // make sure the `anchor_block` field is the same as whatever goes into the block-commit, // or PoX ancestry queries won't work. @@ -683,15 +689,12 @@ impl< debug!("Received new epoch 2.x Stacks block notice"); match self.handle_new_stacks_block() { Ok(missing_block_opt) => { - if missing_block_opt.is_some() { - debug!( - "Missing affirmed anchor block: {:?}", - &missing_block_opt.as_ref().expect("unreachable") - ); + if let Some(missing_block) = &missing_block_opt { + debug!("Missing affirmed anchor block: {missing_block:?}"); } } Err(e) => { - warn!("Error processing new stacks block: {:?}", e); + warn!("Error processing new stacks block: {e:?}"); } } } @@ -761,9 +764,11 @@ impl< /// DB. pub fn handle_new_nakamoto_stacks_block(&mut self) -> Result, Error> { debug!("Handle new Nakamoto block"); - let canonical_sortition_tip = self.canonical_sortition_tip.clone().expect( - "FAIL: processing a new Stacks block, but don't have a canonical sortition tip", - ); + let canonical_sortition_tip = self.canonical_sortition_tip.clone().ok_or_else(|| { + ChainstateError::Expects( + "processing a new Stacks block, but don't have a canonical sortition tip".into(), + ) + })?; loop { Self::fault_injection_pause_nakamoto_block_processing(); @@ -822,7 +827,11 @@ impl< .header .anchored_header .as_stacks_nakamoto() - .expect("FATAL: unreachable: processed a non-Nakamoto block"); + .ok_or_else(|| { + ChainstateError::Expects( + "unreachable: processed a non-Nakamoto block".into(), + ) + })?; ( nakamoto_header.block_id(), @@ -831,7 +840,7 @@ impl< ) }; - debug!("Bump blocks processed ({})", &canonical_stacks_block_id); + debug!("Bump blocks processed ({canonical_stacks_block_id})"); self.notifier.notify_stacks_block_processed(); increment_stx_blocks_processed_counter(); @@ -850,7 +859,7 @@ impl< self.sortition_db.conn(), &block_receipt.evaluated_epoch, )? - .expect("Could not find a stacks epoch."); + .ok_or_else(|| ChainstateError::Expects("Could not find a stacks epoch.".into()))?; estimator.notify_block( &block_receipt.tx_receipts, &stacks_epoch.block_limit, @@ -864,7 +873,7 @@ impl< self.sortition_db.conn(), &block_receipt.evaluated_epoch, )? - .expect("Could not find a stacks epoch."); + .ok_or_else(|| ChainstateError::Expects("Could not find a stacks epoch.".into()))?; if let Err(e) = estimator.notify_block(&block_receipt, &stacks_epoch.block_limit) { warn!("FeeEstimator failed to process block receipt"; "stacks_block_hash" => %block_hash, @@ -879,12 +888,7 @@ impl< self.sortition_db.conn(), &canonical_stacks_consensus_hash, )? - .unwrap_or_else(|| { - panic!( - "FATAL: unreachable: consensus hash {} has no snapshot", - &canonical_stacks_consensus_hash - ) - }); + .ok_or_else(|| ChainstateError::Expects(format!("FATAL: unreachable: consensus hash {canonical_stacks_consensus_hash} has no snapshot")))?; // are we in the prepare phase? // TODO: this should *not* include the 0 block! @@ -900,9 +904,9 @@ impl< let current_reward_cycle = self .burnchain .block_height_to_reward_cycle(stacks_sn.block_height) - .unwrap_or_else(|| { - panic!("FATAL: unreachable: burnchain block height has no reward cycle") - }); + .ok_or_else(|| { + ChainstateError::Expects(format!("burnchain block height has no reward cycle")) + })?; let last_processed_reward_cycle = { let canonical_sn = SortitionDB::get_block_snapshot( @@ -916,7 +920,9 @@ impl< let Some((rc_info, _)) = load_nakamoto_reward_set( self.burnchain .block_height_to_reward_cycle(canonical_sn.block_height) - .expect("FATAL: snapshot has no reward cycle"), + .ok_or_else(|| { + ChainstateError::Expects("snapshot has no reward cycle".into()) + })?, &canonical_sn.sortition_id, &self.burnchain, &mut self.chain_state_db, @@ -954,10 +960,9 @@ impl< stacks_tip: &StacksBlockId, reward_cycle: u64, ) -> Result, Error> { - let sortition_tip_id = self - .canonical_sortition_tip - .as_ref() - .expect("FATAL: Processing anchor block, but no known sortition tip"); + let sortition_tip_id = self.canonical_sortition_tip.as_ref().ok_or_else(|| { + ChainstateError::Expects("Processing anchor block, but no known sortition tip".into()) + })?; get_nakamoto_reward_cycle_info( sortition_tip_id, diff --git a/stackslib/src/chainstate/nakamoto/signer_set.rs b/stackslib/src/chainstate/nakamoto/signer_set.rs index eb0eb46bce..eb240995db 100644 --- a/stackslib/src/chainstate/nakamoto/signer_set.rs +++ b/stackslib/src/chainstate/nakamoto/signer_set.rs @@ -54,31 +54,45 @@ impl RawRewardSetEntry { pub fn from_pox_4_tuple(is_mainnet: bool, tuple: TupleData) -> Result { let mut tuple_data = tuple.data_map; - let pox_addr_tuple = tuple_data - .remove("pox-addr") - .expect("FATAL: no `pox-addr` in return value from (get-reward-set-pox-address)"); + let pox_addr_tuple = tuple_data.remove("pox-addr").ok_or_else(|| { + ChainstateError::Expects( + "no `pox-addr` in return value from (get-reward-set-pox-address)".into(), + ) + })?; let reward_address = PoxAddress::try_from_pox_tuple(is_mainnet, &pox_addr_tuple) - .unwrap_or_else(|| panic!("FATAL: not a valid PoX address: {pox_addr_tuple}")); + .ok_or_else(|| { + ChainstateError::Expects(format!("not a valid PoX address: {pox_addr_tuple}")) + })?; let total_ustx = tuple_data .remove("total-ustx") - .expect( - "FATAL: no 'total-ustx' in return value from (pox-4.get-reward-set-pox-address)", - ) - .expect_u128() - .expect("FATAL: total-ustx is not a u128"); + .ok_or_else(|| { + ChainstateError::Expects( + "no 'total-ustx' in return value from (pox-4.get-reward-set-pox-address)" + .into(), + ) + })? + .expect_u128()?; let stacker = tuple_data .remove("stacker") - .expect("FATAL: no 'stacker' in return value from (pox-4.get-reward-set-pox-address)") + .ok_or_else(|| { + ChainstateError::Expects( + "no 'stacker' in return value from (pox-4.get-reward-set-pox-address)".into(), + ) + })? .expect_optional()? .map(|value| value.expect_principal()) .transpose()?; let signer = tuple_data .remove("signer") - .expect("FATAL: no 'signer' in return value from (pox-4.get-reward-set-pox-address)") + .ok_or_else(|| { + ChainstateError::Expects( + "no 'signer' in return value from (pox-4.get-reward-set-pox-address)".into(), + ) + })? .expect_buff(SIGNERS_PK_LEN)?; // (buff 33) only enforces max size, not min size, so we need to do a len check @@ -145,12 +159,9 @@ impl NakamotoSigners { ], )? .expect_optional()? - .unwrap_or_else(|| { - panic!( - "FATAL: missing PoX address in slot {} out of {} in reward cycle {}", - index, list_length, reward_cycle - ) - }) + .ok_or_else(|| { + ChainstateError::Expects(format!("Missing PoX address in slot {index} out of {list_length} in reward cycle {reward_cycle}")) + })? .expect_tuple()?; let entry = RawRewardSetEntry::from_pox_4_tuple(is_mainnet, tuple)?; @@ -196,20 +207,16 @@ impl NakamotoSigners { .map(|signer| { let signer_hash = Hash160::from_data(&signer.signing_key); let signing_address = StacksAddress::p2pkh_from_hash(is_mainnet, signer_hash); - Value::Tuple( - TupleData::from_data(vec![ - ( - "signer".into(), - Value::Principal(PrincipalData::from(signing_address)), - ), - ("num-slots".into(), Value::UInt(1)) - ]) - .expect( - "BUG: Failed to construct `{ signer: principal, num-slots: u64 }` tuple", - ), - ) + let tuple_data = TupleData::from_data(vec![ + ( + "signer".into(), + Value::Principal(PrincipalData::from(signing_address)), + ), + ("num-slots".into(), Value::UInt(1)), + ])?; + Ok::(Value::Tuple(tuple_data)) }) - .collect() + .collect::, _>>()? }; let signers_list = if participation == 0 { @@ -223,66 +230,55 @@ impl NakamotoSigners { .map(|signer| { let signer_hash = Hash160::from_data(&signer.signing_key); let signing_address = StacksAddress::p2pkh_from_hash(is_mainnet, signer_hash); - Value::Tuple( - TupleData::from_data(vec![ - ( - "signer".into(), - Value::Principal(PrincipalData::from(signing_address)), - ), - ("weight".into(), Value::UInt(signer.weight.into())), - ]) - .expect( - "BUG: Failed to construct `{ signer: principal, weight: uint }` tuple", + let tuple = TupleData::from_data(vec![ + ( + "signer".into(), + Value::Principal(PrincipalData::from(signing_address)), ), - ) + ("weight".into(), Value::UInt(signer.weight.into())), + ])?; + Ok::(Value::Tuple(tuple)) }) - .collect() + .collect::, _>>()? }; if signers_list.len() > SIGNERS_MAX_LIST_SIZE { - panic!( - "FATAL: signers list returned by reward set calculations longer than maximum ({} > {})", - signers_list.len(), - SIGNERS_MAX_LIST_SIZE, - ); + return Err(ChainstateError::Expects(format!( + "signers list returned by reward set calculations longer than maximum ({} > {SIGNERS_MAX_LIST_SIZE})", + signers_list.len() + ))); } let set_stackerdb_args = [ - SymbolicExpression::atom_value(Value::cons_list_unsanitized(stackerdb_list).expect( - "BUG: Failed to construct `(list 4000 { signer: principal, num-slots: u64 })` list", - )), + SymbolicExpression::atom_value(Value::cons_list_unsanitized(stackerdb_list)?), SymbolicExpression::atom_value(Value::UInt(reward_cycle.into())), SymbolicExpression::atom_value(Value::UInt(coinbase_height.into())), ]; let set_signers_args = [ SymbolicExpression::atom_value(Value::UInt(reward_cycle.into())), - SymbolicExpression::atom_value(Value::cons_list_unsanitized(signers_list).expect( - "BUG: Failed to construct `(list 4000 { signer: principal, weight: uint })` list", - )), + SymbolicExpression::atom_value(Value::cons_list_unsanitized(signers_list)?), ]; - let (value, _, events, _) = clarity - .with_abort_callback( - |vm_env| { - vm_env.execute_in_env(sender_addr.clone(), None, None, |env| { - env.execute_contract_allow_private( - signers_contract, - "stackerdb-set-signer-slots", - &set_stackerdb_args, - false, - )?; - env.execute_contract_allow_private( - signers_contract, - "set-signers", - &set_signers_args, - false, - ) - }) - }, - |_, _| None, - ) - .expect("FATAL: failed to update signer stackerdb"); + let (value, _, events, _) = clarity.with_abort_callback( + |vm_env| { + vm_env.execute_in_env(sender_addr.clone(), None, None, |env| { + env.execute_contract_allow_private( + signers_contract, + "stackerdb-set-signer-slots", + &set_stackerdb_args, + false, + )?; + env.execute_contract_allow_private( + signers_contract, + "set-signers", + &set_signers_args, + false, + ) + }) + }, + |_, _| None, + )?; if let Value::Response(ref data) = value { if !data.committed { @@ -291,7 +287,9 @@ impl NakamotoSigners { "reward_cycle" => reward_cycle, "cc_response" => %value, ); - panic!(); + return Err(ChainstateError::Expects( + "Failed to update .signers contract".into(), + )); } } @@ -343,24 +341,23 @@ impl NakamotoSigners { let signers_contract = &boot_code_id(SIGNERS_NAME, clarity_tx.config.mainnet); // are we the first block in the prepare phase in our fork? - let needs_update: Result<_, ChainstateError> = clarity_tx.connection().with_clarity_db_readonly(|clarity_db| { - if !clarity_db.has_contract(signers_contract) { - // if there's no signers contract, no need to update anything. - return Ok(false) - } - let Ok(value) = clarity_db.lookup_variable_unknown_descriptor( - signers_contract, - SIGNERS_UPDATE_STATE, - ¤t_epoch, - ) else { - error!("FATAL: Failed to read `{SIGNERS_UPDATE_STATE}` variable from .signers contract"); - panic!(); - }; - let cycle_number = value.expect_u128()?; - // if the cycle_number is less than `cycle_of_prepare_phase`, we need to update - // the .signers state. - Ok(cycle_number < u128::from(cycle_of_prepare_phase)) - }); + let needs_update: Result<_, ChainstateError> = clarity_tx + .connection() + .with_clarity_db_readonly(|clarity_db| { + if !clarity_db.has_contract(signers_contract) { + // if there's no signers contract, no need to update anything. + return Ok(false); + } + let value = clarity_db.lookup_variable_unknown_descriptor( + signers_contract, + SIGNERS_UPDATE_STATE, + ¤t_epoch, + )?; + let cycle_number = value.expect_u128()?; + // if the cycle_number is less than `cycle_of_prepare_phase`, we need to update + // the .signers state. + Ok(cycle_number < u128::from(cycle_of_prepare_phase)) + }); if !needs_update? { debug!("Current cycle has already been setup in .signers or .signers is not initialized yet"); @@ -416,7 +413,7 @@ impl NakamotoSigners { sortdb, block_id, SIGNERS_NAME, - &format!("(get-signers u{})", reward_cycle), + &format!("(get-signers u{reward_cycle})"), )? .expect_optional()?; let mut signers = HashMap::new(); @@ -427,13 +424,12 @@ impl NakamotoSigners { let signer_address = if let PrincipalData::Standard(signer) = principal_data { signer.into() } else { - panic!( - "FATAL: Signer returned from get-signers is not a standard principal: {:?}", - principal_data - ); + return Err(ChainstateError::Expects(format!("Signer returned from get-signers is not a standard principal: {principal_data:?}"))); }; let weight = u64::try_from(signer_tuple.get("weight")?.to_owned().expect_u128()?) - .expect("FATAL: Signer weight greater than a u64::MAX"); + .map_err(|_| { + ChainstateError::Expects("Signer weight greater than a u64::MAX".into()) + })?; signers.insert(signer_address, weight); } } diff --git a/stackslib/src/chainstate/stacks/boot/mod.rs b/stackslib/src/chainstate/stacks/boot/mod.rs index 7464c49052..8353e4a076 100644 --- a/stackslib/src/chainstate/stacks/boot/mod.rs +++ b/stackslib/src/chainstate/stacks/boot/mod.rs @@ -1267,13 +1267,12 @@ impl StacksChainState { sortdb, block_id, POX_4_NAME, - &format!("(get-reward-set-size u{})", reward_cycle), + &format!("(get-reward-set-size u{reward_cycle})"), )? .expect_u128()?; debug!( - "At block {:?} (reward cycle {}): {} PoX reward addresses", - block_id, reward_cycle, num_addrs + "At block {block_id:?} (reward cycle {reward_cycle}): {num_addrs} PoX reward addresses" ); let mut ret = vec![]; @@ -1290,13 +1289,12 @@ impl StacksChainState { sortdb, block_id, POX_4_NAME, - &format!("(get-reward-set-pox-address u{} u{})", reward_cycle, i), + &format!("(get-reward-set-pox-address u{reward_cycle} u{i})"), )? .expect_optional()? .unwrap_or_else(|| { panic!( - "FATAL: missing PoX address in slot {} out of {} in reward cycle {}", - i, num_addrs, reward_cycle + "FATAL: missing PoX address in slot {i} out of {num_addrs} in reward cycle {reward_cycle}" ) }) .expect_tuple()?; @@ -1380,12 +1378,11 @@ impl StacksChainState { sortdb, block_id, SIGNERS_VOTING_NAME, - &format!("(get-approved-aggregate-key u{})", reward_cycle), + &format!("(get-approved-aggregate-key u{reward_cycle})"), )? .expect_optional()?; debug!( - "Aggregate public key for reward cycle {} is {:?}", - reward_cycle, aggregate_public_key_opt + "Aggregate public key for reward cycle {reward_cycle} is {aggregate_public_key_opt:?}" ); let aggregate_public_key = match aggregate_public_key_opt { diff --git a/stackslib/src/chainstate/stacks/db/blocks.rs b/stackslib/src/chainstate/stacks/db/blocks.rs index ef2c1457e3..a5573a66f7 100644 --- a/stackslib/src/chainstate/stacks/db/blocks.rs +++ b/stackslib/src/chainstate/stacks/db/blocks.rs @@ -4582,55 +4582,45 @@ impl StacksChainState { ) -> Result<(u128, Vec), Error> { let mainnet = clarity_tx.config.mainnet; let lockup_contract_id = boot_code_id("lockup", mainnet); - clarity_tx - .connection() - .as_transaction(|tx_connection| { - let epoch = tx_connection.get_epoch(); - let result = tx_connection.with_clarity_db(|db| { - let block_height = Value::UInt(db.get_current_block_height().into()); - let res = db.fetch_entry_unknown_descriptor( - &lockup_contract_id, - "lockups", - &block_height, - &epoch, - )?; - Ok(res) - })?; + clarity_tx.connection().as_transaction(|tx_connection| { + let epoch = tx_connection.get_epoch(); + let result = tx_connection.with_clarity_db(|db| { + let block_height = Value::UInt(db.get_current_block_height().into()); + let res = db.fetch_entry_unknown_descriptor( + &lockup_contract_id, + "lockups", + &block_height, + &epoch, + )?; + Ok(res) + })?; - let entries = match result { - Value::Optional(_) => match result.expect_optional()? { - Some(Value::Sequence(SequenceData::List(entries))) => entries.data, - _ => return Ok((0, vec![])), - }, + let entries = match result { + Value::Optional(_) => match result.expect_optional()? { + Some(Value::Sequence(SequenceData::List(entries))) => entries.data, _ => return Ok((0, vec![])), - }; + }, + _ => return Ok((0, vec![])), + }; - let mut total_minted = 0; - let mut events = vec![]; - for entry in entries.into_iter() { - let schedule: TupleData = entry.expect_tuple()?; - let amount = schedule - .get("amount") - .expect("Lockup malformed") - .to_owned() - .expect_u128()?; - let recipient = schedule - .get("recipient") - .expect("Lockup malformed") - .to_owned() - .expect_principal()?; - total_minted += amount; - StacksChainState::account_credit( - tx_connection, - &recipient, - u64::try_from(amount).expect("FATAL: transferred more STX than exist"), - ); - let event = STXEventType::STXMintEvent(STXMintEventData { recipient, amount }); - events.push(StacksTransactionEvent::STXEvent(event)); - } - Ok((total_minted, events)) - }) - .map_err(Error::ClarityError) + let mut total_minted = 0; + let mut events = vec![]; + for entry in entries.into_iter() { + let schedule: TupleData = entry.expect_tuple()?; + let amount = schedule.get("amount")?.to_owned().expect_u128()?; + let recipient = schedule.get("recipient")?.to_owned().expect_principal()?; + total_minted += amount; + StacksChainState::account_credit( + tx_connection, + &recipient, + u64::try_from(amount) + .map_err(|_| Error::Expects("transferred more STX than exists".into()))?, + ); + let event = STXEventType::STXMintEvent(STXMintEventData { recipient, amount }); + events.push(StacksTransactionEvent::STXEvent(event)); + } + Ok((total_minted, events)) + }) } /// Given the list of matured miners, find the miner reward schedule that produced the parent diff --git a/stackslib/src/chainstate/stacks/db/transactions.rs b/stackslib/src/chainstate/stacks/db/transactions.rs index 46dd54b041..63fbb0c39b 100644 --- a/stackslib/src/chainstate/stacks/db/transactions.rs +++ b/stackslib/src/chainstate/stacks/db/transactions.rs @@ -956,12 +956,8 @@ impl StacksChainState { .get_microblock_poison_report(mblock_pubk_height)? { // account for report loaded - env.add_memory(u64::from( - TypeSignature::PrincipalType - .size() - .map_err(VmExecutionError::from)?, - )) - .map_err(|e| Error::from_cost_error(e, cost_before.clone(), env.global_context))?; + env.add_memory(u64::from(TypeSignature::PrincipalType.size()?)) + .map_err(|e| Error::from_cost_error(e, cost_before.clone(), env.global_context))?; // u128 sequence env.add_memory(16) diff --git a/stackslib/src/chainstate/stacks/mod.rs b/stackslib/src/chainstate/stacks/mod.rs index 1d86ab3c26..e4144e544b 100644 --- a/stackslib/src/chainstate/stacks/mod.rs +++ b/stackslib/src/chainstate/stacks/mod.rs @@ -20,7 +20,7 @@ use std::{error, fmt, io}; use clarity::vm::contexts::GlobalContext; use clarity::vm::costs::{CostErrors, ExecutionCost}; -use clarity::vm::errors::VmExecutionError; +use clarity::vm::errors::{ClarityTypeError, StaticCheckError, VmExecutionError}; use clarity::vm::representations::{ClarityName, ContractName}; use clarity::vm::types::{ PrincipalData, QualifiedContractIdentifier, StandardPrincipalData, Value, @@ -122,6 +122,8 @@ pub enum Error { InvalidChildOfNakomotoBlock, NoRegisteredSigners(u64), TenureTooBigError, + /// This error indicates an internal state or condition that should never actually happen + Expects(String), } impl From for Error { @@ -225,6 +227,7 @@ impl fmt::Display for Error { write!(f, "The supplied block identifiers are not in the same fork") } Error::TenureTooBigError => write!(f, "Too much data in tenure"), + Error::Expects(ref msg) => write!(f, "Unexpected state: {msg}"), } } } @@ -272,6 +275,7 @@ impl error::Error for Error { Error::NoRegisteredSigners(_) => None, Error::NotInSameFork => None, Error::TenureTooBigError => None, + Error::Expects(ref _msg) => None, } } } @@ -319,6 +323,7 @@ impl Error { Error::NoRegisteredSigners(_) => "NoRegisteredSigners", Error::NotInSameFork => "NotInSameFork", Error::TenureTooBigError => "TenureTooBigError", + Error::Expects(_) => "Expects", } } @@ -353,6 +358,17 @@ impl From for Error { } } +/// TODO: remove this comment. Should this actually convert to a static check +/// or is it possible for this to be a runtime error...I don't think so because +/// if its a runtime issue, it would be really hitting VmExecutionError already +impl From for Error { + fn from(e: ClarityTypeError) -> Error { + Error::ClarityError(ClarityError::StaticCheck( + StaticCheckError::from_clarity_type_error(e), + )) + } +} + impl Error { pub fn from_cost_error( err: CostErrors, diff --git a/stackslib/src/chainstate/tests/runtime_analysis_tests.rs b/stackslib/src/chainstate/tests/runtime_analysis_tests.rs index 22a688127c..3238dbdaba 100644 --- a/stackslib/src/chainstate/tests/runtime_analysis_tests.rs +++ b/stackslib/src/chainstate/tests/runtime_analysis_tests.rs @@ -91,7 +91,8 @@ fn variant_coverage_report(variant: CheckErrorKind) { "least_supertype checks already run in analysis, and runtime values are sanitized to their declared signatures, so the VM never sees a pair of values whose unified type wasn't accepted earlier."), - Expects(_) => Unreachable_ExpectLike, + ExpectsAcceptable(_) => Unreachable_ExpectLike, + ExpectsRejectable(_) => Unreachable_ExpectLike, BadMatchOptionSyntax(_) => Unreachable_Functionally( "Both the analyzer and the runtime examine the exact same match AST slice. The static pass invokes check_special_match_opt, which enforces the 3 diff --git a/stackslib/src/chainstate/tests/runtime_tests.rs b/stackslib/src/chainstate/tests/runtime_tests.rs index b11db2a6cf..532d012a4d 100644 --- a/stackslib/src/chainstate/tests/runtime_tests.rs +++ b/stackslib/src/chainstate/tests/runtime_tests.rs @@ -127,11 +127,6 @@ fn variant_coverage_report(variant: RuntimeError) { are significantly lower and will trigger first. Only low-level Rust unit tests \ can construct a context deep enough to hit this error." ), - BadTypeConstruction => Unreachable_Functionally( - "BadTypeConstruction is rejected during static analysis at contract-publish time. \ - Any value construction that would produce an ill-formed type fails parsing or \ - type-checking before the contract is stored on-chain." - ), BadBlockHeight(_) => Unreachable_Functionally( "All block heights referenced via `at-block` or `get-block-info?` are guaranteed \ to exist in the node's historical database during normal execution. \ @@ -154,11 +149,6 @@ fn variant_coverage_report(variant: RuntimeError) { "Every on-chain transaction and contract-call has a well-defined sender. \ This error only occurs in malformed test harnesses." ), - BadNameValue(_, _) => Unreachable_Functionally( - "Contract, function, trait, and variable names are fully validated during static analysis at publish time. \ - The runtime only ever encounters already-validated names. \ - Only corrupted state or manual VM manipulation can produce this error." - ), UnknownBlockHeaderHash(_) => Tested(vec![unknown_block_header_hash_fork]), BadBlockHash(_) => Tested(vec![bad_block_hash]), UnwrapFailure => Tested(vec![ diff --git a/stackslib/src/chainstate/tests/static_analysis_tests.rs b/stackslib/src/chainstate/tests/static_analysis_tests.rs index 1b918fd294..4f402174a8 100644 --- a/stackslib/src/chainstate/tests/static_analysis_tests.rs +++ b/stackslib/src/chainstate/tests/static_analysis_tests.rs @@ -68,7 +68,8 @@ fn variant_coverage_report(variant: StaticCheckErrorKind) { TypeSignatureTooDeep => Tested(vec![static_check_error_type_signature_too_deep]), ExpectedName => Tested(vec![static_check_error_expected_name]), SupertypeTooLarge => Tested(vec![static_check_error_supertype_too_large]), - Expects(_) => Unreachable_ExpectLike, + ExpectsAcceptable(_) => Unreachable_ExpectLike, + ExpectsRejectable(_) => Unreachable_ExpectLike, BadMatchOptionSyntax(static_check_error_kind) => { Tested(vec![static_check_error_bad_match_option_syntax]) } diff --git a/stackslib/src/net/api/getpoxinfo.rs b/stackslib/src/net/api/getpoxinfo.rs index f00a3008f6..76ee16f87c 100644 --- a/stackslib/src/net/api/getpoxinfo.rs +++ b/stackslib/src/net/api/getpoxinfo.rs @@ -206,34 +206,39 @@ impl RPCPoxInfoData { }; let first_burnchain_block_height = res - .get("first-burnchain-block-height") - .unwrap_or_else(|_| panic!("FATAL: no 'first-burnchain-block-height'")) + .get("first-burnchain-block-height")? .to_owned() - .expect_u128()? as u64; + .expect_u128()? + .try_into() + .map_err(|_| NetError::DBError(DBError::Overflow))?; let min_stacking_increment_ustx = res - .get("min-amount-ustx") - .unwrap_or_else(|_| panic!("FATAL: no 'min-amount-ustx'")) + .get("min-amount-ustx")? .to_owned() - .expect_u128()? as u64; + .expect_u128()? + .try_into() + .map_err(|_| NetError::DBError(DBError::Overflow))?; let prepare_cycle_length = res - .get("prepare-cycle-length") - .unwrap_or_else(|_| panic!("FATAL: no 'prepare-cycle-length'")) + .get("prepare-cycle-length")? .to_owned() - .expect_u128()? as u64; + .expect_u128()? + .try_into() + .map_err(|_| NetError::DBError(DBError::Overflow))?; let reward_cycle_length = res - .get("reward-cycle-length") - .unwrap_or_else(|_| panic!("FATAL: no 'reward-cycle-length'")) + .get("reward-cycle-length")? .to_owned() - .expect_u128()? as u64; + .expect_u128()? + .try_into() + .map_err(|_| NetError::DBError(DBError::Overflow))?; let total_liquid_supply_ustx = res - .get("total-liquid-supply-ustx") - .unwrap_or_else(|_| panic!("FATAL: no 'total-liquid-supply-ustx'")) + .get("total-liquid-supply-ustx")? .to_owned() - .expect_u128()? as u64; + .expect_u128()? + .try_into() + .map_err(|_| NetError::DBError(DBError::Overflow))?; let has_rejection_data = pox_contract_name == POX_1_NAME || pox_contract_name == POX_2_NAME @@ -241,21 +246,24 @@ impl RPCPoxInfoData { let (rejection_fraction, rejection_votes_left_required) = if has_rejection_data { let rejection_fraction = res - .get("rejection-fraction") - .unwrap_or_else(|_| panic!("FATAL: no 'rejection-fraction'")) + .get("rejection-fraction")? .to_owned() - .expect_u128()? as u64; + .expect_u128()? + .try_into() + .map_err(|_| NetError::DBError(DBError::Overflow))?; let current_rejection_votes = res - .get("current-rejection-votes") - .unwrap_or_else(|_| panic!("FATAL: no 'current-rejection-votes'")) + .get("current-rejection-votes")? .to_owned() - .expect_u128()? as u64; + .expect_u128()? + .try_into() + .map_err(|_| NetError::DBError(DBError::Overflow))?; - let total_required = (total_liquid_supply_ustx as u128 / 100) + let total_required: u64 = (total_liquid_supply_ustx as u128 / 100) .checked_mul(rejection_fraction as u128) .ok_or_else(|| NetError::DBError(DBError::Overflow))? - as u64; + .try_into() + .map_err(|_| NetError::DBError(DBError::Overflow))?; let votes_left = total_required.saturating_sub(current_rejection_votes); (Some(rejection_fraction), Some(votes_left)) diff --git a/stackslib/src/net/mod.rs b/stackslib/src/net/mod.rs index 55e9e5de95..97ff1ad477 100644 --- a/stackslib/src/net/mod.rs +++ b/stackslib/src/net/mod.rs @@ -19,7 +19,7 @@ use std::io::{Read, Write}; use std::net::{IpAddr, SocketAddr}; use std::{error, fmt, io}; -use clarity::vm::errors::VmExecutionError; +use clarity::vm::errors::{ClarityTypeError, StaticCheckError, VmExecutionError}; use clarity::vm::types::{PrincipalData, QualifiedContractIdentifier}; use libstackerdb::{Error as libstackerdb_error, StackerDBChunkData}; use p2p::{DropReason, DropSource}; @@ -533,6 +533,17 @@ impl From for Error { } } +/// TODO: remove this comment. Should this actually convert to a static check +/// or is it possible for this to be a runtime error...I don't think so because +/// if its a runtime issue, it would be really hitting VmExecutionError already +impl From for Error { + fn from(e: ClarityTypeError) -> Self { + Error::ClarityError(ClarityError::StaticCheck( + StaticCheckError::from_clarity_type_error(e), + )) + } +} + impl From for Error { fn from(e: VmExecutionError) -> Self { Error::ClarityError(e.into()) diff --git a/stackslib/src/util_lib/strings.rs b/stackslib/src/util_lib/strings.rs index 86592ab5e3..e990df70c7 100644 --- a/stackslib/src/util_lib/strings.rs +++ b/stackslib/src/util_lib/strings.rs @@ -19,7 +19,7 @@ use std::fmt; use std::io::{Read, Write}; use std::ops::{Deref, DerefMut}; -use clarity::vm::errors::RuntimeError; +use clarity::vm::errors::ClarityTypeError; use clarity::vm::representations::{ ClarityName, ContractName, MAX_STRING_LEN as CLARITY_MAX_STRING_LENGTH, }; @@ -38,11 +38,10 @@ lazy_static! { guarded_string!( UrlString, - "UrlString", URL_STRING_REGEX, CLARITY_MAX_STRING_LENGTH, - RuntimeError, - RuntimeError::BadNameValue + ClarityTypeError, + ClarityTypeError::InvalidUrlString ); /// printable-ASCII-only string, but encodable. From 8f25aada0c8cb0b2baf6d288a67ac5350873745c Mon Sep 17 00:00:00 2001 From: Jacinta Ferrant Date: Wed, 10 Dec 2025 14:09:28 -0800 Subject: [PATCH 3/3] Add From implementations for ClarityErrorType Signed-off-by: Jacinta Ferrant --- clarity-types/src/errors/analysis.rs | 303 ++++++++---------- clarity-types/src/errors/mod.rs | 7 + clarity-types/src/types/mod.rs | 7 +- .../src/vm/analysis/type_checker/v2_05/mod.rs | 74 +---- .../type_checker/v2_05/natives/assets.rs | 16 +- .../type_checker/v2_05/natives/maps.rs | 43 +-- .../type_checker/v2_05/natives/mod.rs | 38 +-- .../type_checker/v2_05/natives/options.rs | 26 +- .../type_checker/v2_05/natives/sequences.rs | 50 +-- .../src/vm/analysis/type_checker/v2_1/mod.rs | 192 +++-------- .../type_checker/v2_1/natives/assets.rs | 16 +- .../type_checker/v2_1/natives/conversions.rs | 22 +- .../type_checker/v2_1/natives/maps.rs | 43 +-- .../analysis/type_checker/v2_1/natives/mod.rs | 76 ++--- .../type_checker/v2_1/natives/options.rs | 32 +- .../v2_1/natives/post_conditions.rs | 12 +- .../type_checker/v2_1/natives/sequences.rs | 61 ++-- clarity/src/vm/analysis/types.rs | 6 +- clarity/src/vm/callables.rs | 24 +- clarity/src/vm/contexts.rs | 5 +- clarity/src/vm/database/clarity_db.rs | 59 ++-- clarity/src/vm/functions/arithmetic.rs | 38 +-- clarity/src/vm/functions/assets.rs | 199 ++---------- clarity/src/vm/functions/conversions.rs | 29 +- clarity/src/vm/functions/crypto.rs | 3 +- clarity/src/vm/functions/database.rs | 139 ++------ clarity/src/vm/functions/mod.rs | 16 +- clarity/src/vm/functions/options.rs | 11 +- clarity/src/vm/functions/post_conditions.rs | 9 +- clarity/src/vm/functions/principals.rs | 3 +- clarity/src/vm/functions/sequences.rs | 172 +++------- clarity/src/vm/functions/tuples.rs | 33 +- clarity/src/vm/mod.rs | 35 +- clarity/src/vm/types/signatures.rs | 54 +--- stackslib/src/chainstate/stacks/mod.rs | 4 +- stackslib/src/net/mod.rs | 4 +- 36 files changed, 539 insertions(+), 1322 deletions(-) diff --git a/clarity-types/src/errors/analysis.rs b/clarity-types/src/errors/analysis.rs index 8faa7e159b..8ae5026751 100644 --- a/clarity-types/src/errors/analysis.rs +++ b/clarity-types/src/errors/analysis.rs @@ -260,61 +260,6 @@ pub enum CommonCheckErrorKind { TraitTooManyMethods(usize, usize), } -impl CommonCheckErrorKind { - pub fn from_clarity_type_error(err: ClarityTypeError) -> Self { - match err { - ClarityTypeError::ValueTooLarge => CommonCheckErrorKind::ValueTooLarge, - ClarityTypeError::TypeSignatureTooDeep => CommonCheckErrorKind::TypeSignatureTooDeep, - ClarityTypeError::ValueOutOfBounds => CommonCheckErrorKind::ValueOutOfBounds, - ClarityTypeError::DuplicateTupleField(name) => { - CommonCheckErrorKind::NameAlreadyUsed(name) - } - ClarityTypeError::TypeMismatch(expected, found) => { - CommonCheckErrorKind::TypeError(expected, found) - } - ClarityTypeError::EmptyTuplesNotAllowed => CommonCheckErrorKind::EmptyTuplesNotAllowed, - ClarityTypeError::SupertypeTooLarge => CommonCheckErrorKind::SupertypeTooLarge, - ClarityTypeError::InvalidTypeDescription => { - CommonCheckErrorKind::InvalidTypeDescription - } - ClarityTypeError::CouldNotDetermineType => CommonCheckErrorKind::CouldNotDetermineType, - ClarityTypeError::ListTypeMismatch - | ClarityTypeError::SequenceElementArityMismatch { .. } - | ClarityTypeError::ExpectedSequenceValue - | ClarityTypeError::InvalidAsciiCharacter(_) - | ClarityTypeError::InvalidUtf8Encoding - | ClarityTypeError::NoSuchTupleField(_, _) - | ClarityTypeError::TypeMismatchValue(_, _) - | ClarityTypeError::CouldNotDetermineSerializationType - | ClarityTypeError::InvalidUrlString(_) - | ClarityTypeError::InvalidClarityName(_) - | ClarityTypeError::InvalidContractName(_) - | ClarityTypeError::QualifiedContractEmptyIssuer - | ClarityTypeError::QualifiedContractMissingDot - | ClarityTypeError::InvalidPrincipalEncoding(_) - | ClarityTypeError::InvalidPrincipalLength(_) - | ClarityTypeError::ResponseTypeMismatch { .. } => { - CommonCheckErrorKind::ExpectsAcceptable(format!( - "Unexpected error type during analysis: {err}" - )) - } - ClarityTypeError::InvariantViolation(_) - | ClarityTypeError::InvalidPrincipalVersion(_) => { - CommonCheckErrorKind::ExpectsRejectable(format!( - "Unexpected error type during analysis: {err}" - )) - } - ClarityTypeError::UnsupportedTypeInEpoch(ty, epoch) => { - CommonCheckErrorKind::ExpectsRejectable(format!( - "{ty} should not be used in {epoch}" - )) - } - ClarityTypeError::UnsupportedEpoch(epoch) => { - CommonCheckErrorKind::ExpectsRejectable(format!("{epoch} is not supported")) - } - } - } -} /// An error detected during the static analysis of a smart contract at deployment time. /// /// These checks are performed once, before any contract execution occurs, to find issues @@ -933,54 +878,6 @@ impl CheckErrorKind { CheckErrorKind::SupertypeTooLarge | CheckErrorKind::ExpectsRejectable(_) ) } - - pub fn from_clarity_type_error(err: ClarityTypeError) -> Self { - match err { - ClarityTypeError::ValueTooLarge => CheckErrorKind::ValueTooLarge, - ClarityTypeError::TypeSignatureTooDeep => CheckErrorKind::TypeSignatureTooDeep, - ClarityTypeError::ValueOutOfBounds => CheckErrorKind::ValueOutOfBounds, - ClarityTypeError::DuplicateTupleField(name) => CheckErrorKind::NameAlreadyUsed(name), - ClarityTypeError::NoSuchTupleField(field, tuple_sig) => { - CheckErrorKind::NoSuchTupleField(field, tuple_sig) - } - ClarityTypeError::TypeMismatchValue(ty, value) => { - CheckErrorKind::TypeValueError(ty, value) - } - ClarityTypeError::TypeMismatch(expected, found) => { - CheckErrorKind::TypeError(expected, found) - } - ClarityTypeError::EmptyTuplesNotAllowed => CheckErrorKind::EmptyTuplesNotAllowed, - ClarityTypeError::SupertypeTooLarge => CheckErrorKind::SupertypeTooLarge, - ClarityTypeError::InvalidTypeDescription => CheckErrorKind::InvalidTypeDescription, - ClarityTypeError::ListTypeMismatch => CheckErrorKind::ListTypesMustMatch, - ClarityTypeError::InvalidAsciiCharacter(_) => CheckErrorKind::InvalidCharactersDetected, - ClarityTypeError::InvalidUtf8Encoding => CheckErrorKind::InvalidUTF8Encoding, - ClarityTypeError::ExpectedSequenceValue - | ClarityTypeError::SequenceElementArityMismatch { .. } - | ClarityTypeError::CouldNotDetermineSerializationType - | ClarityTypeError::InvalidUrlString(_) - | ClarityTypeError::InvalidClarityName(_) - | ClarityTypeError::InvalidContractName(_) - | ClarityTypeError::QualifiedContractEmptyIssuer - | ClarityTypeError::QualifiedContractMissingDot - | ClarityTypeError::InvalidPrincipalEncoding(_) - | ClarityTypeError::InvalidPrincipalLength(_) - | ClarityTypeError::ResponseTypeMismatch { .. } => CheckErrorKind::ExpectsAcceptable( - format!("Unexpected error type during runtime analysis: {err}"), - ), - ClarityTypeError::InvariantViolation(_) - | ClarityTypeError::InvalidPrincipalVersion(_) => CheckErrorKind::ExpectsRejectable( - format!("Unexpected error type during runtime analysis: {err}"), - ), - ClarityTypeError::CouldNotDetermineType => CheckErrorKind::CouldNotDetermineType, - ClarityTypeError::UnsupportedTypeInEpoch(ty, epoch) => { - CheckErrorKind::ExpectsRejectable(format!("{ty} should not be used in {epoch}")) - } - ClarityTypeError::UnsupportedEpoch(epoch) => { - CheckErrorKind::ExpectsRejectable(format!("{epoch} is not supported")) - } - } - } } impl StaticCheckErrorKind { @@ -991,62 +888,6 @@ impl StaticCheckErrorKind { StaticCheckErrorKind::SupertypeTooLarge | StaticCheckErrorKind::ExpectsRejectable(_) ) } - - pub fn from_clarity_type_error(err: ClarityTypeError) -> Self { - match err { - ClarityTypeError::ValueTooLarge => StaticCheckErrorKind::ValueTooLarge, - ClarityTypeError::TypeSignatureTooDeep => StaticCheckErrorKind::TypeSignatureTooDeep, - ClarityTypeError::ValueOutOfBounds => StaticCheckErrorKind::ValueOutOfBounds, - ClarityTypeError::DuplicateTupleField(name) => { - StaticCheckErrorKind::NameAlreadyUsed(name) - } - ClarityTypeError::NoSuchTupleField(field, tuple_sig) => { - StaticCheckErrorKind::NoSuchTupleField(field, tuple_sig) - } - ClarityTypeError::TypeMismatch(expected, found) => { - StaticCheckErrorKind::TypeError(expected, found) - } - ClarityTypeError::EmptyTuplesNotAllowed => StaticCheckErrorKind::EmptyTuplesNotAllowed, - ClarityTypeError::SupertypeTooLarge => StaticCheckErrorKind::SupertypeTooLarge, - ClarityTypeError::InvalidTypeDescription => { - StaticCheckErrorKind::InvalidTypeDescription - } - ClarityTypeError::InvalidUrlString(_) - | ClarityTypeError::InvalidClarityName(_) - | ClarityTypeError::InvalidContractName(_) - | ClarityTypeError::QualifiedContractEmptyIssuer - | ClarityTypeError::QualifiedContractMissingDot - | ClarityTypeError::InvalidPrincipalEncoding(_) - | ClarityTypeError::InvalidPrincipalLength(_) - | ClarityTypeError::ListTypeMismatch - | ClarityTypeError::SequenceElementArityMismatch { .. } - | ClarityTypeError::ExpectedSequenceValue - | ClarityTypeError::TypeMismatchValue(_, _) - | ClarityTypeError::ResponseTypeMismatch { .. } - | ClarityTypeError::InvalidAsciiCharacter(_) - | ClarityTypeError::InvalidUtf8Encoding => StaticCheckErrorKind::ExpectsAcceptable( - format!("Unexpected error type during static analysis: {err}"), - ), - ClarityTypeError::InvariantViolation(_) - | ClarityTypeError::InvalidPrincipalVersion(_) => { - StaticCheckErrorKind::ExpectsRejectable(format!( - "Unexpected error type during static analysis: {err}" - )) - } - ClarityTypeError::CouldNotDetermineSerializationType => { - StaticCheckErrorKind::CouldNotDetermineSerializationType - } - ClarityTypeError::CouldNotDetermineType => StaticCheckErrorKind::CouldNotDetermineType, - ClarityTypeError::UnsupportedTypeInEpoch(ty, epoch) => { - StaticCheckErrorKind::ExpectsRejectable(format!( - "{ty} should not be used in {epoch}" - )) - } - ClarityTypeError::UnsupportedEpoch(epoch) => { - StaticCheckErrorKind::ExpectsRejectable(format!("{epoch} is not supported")) - } - } - } } impl StaticCheckError { @@ -1078,9 +919,59 @@ impl StaticCheckError { r.set_expression(expr); r } +} + +impl From for StaticCheckErrorKind { + fn from(err: ClarityTypeError) -> Self { + match err { + ClarityTypeError::ValueTooLarge => Self::ValueTooLarge, + ClarityTypeError::TypeSignatureTooDeep => Self::TypeSignatureTooDeep, + ClarityTypeError::ValueOutOfBounds => Self::ValueOutOfBounds, + ClarityTypeError::DuplicateTupleField(name) => Self::NameAlreadyUsed(name), + ClarityTypeError::NoSuchTupleField(field, tuple_sig) => { + Self::NoSuchTupleField(field, tuple_sig) + } + ClarityTypeError::TypeMismatch(expected, found) => Self::TypeError(expected, found), + ClarityTypeError::EmptyTuplesNotAllowed => Self::EmptyTuplesNotAllowed, + ClarityTypeError::SupertypeTooLarge => Self::SupertypeTooLarge, + ClarityTypeError::InvalidTypeDescription => Self::InvalidTypeDescription, + ClarityTypeError::InvalidUrlString(_) + | ClarityTypeError::InvalidClarityName(_) + | ClarityTypeError::InvalidContractName(_) + | ClarityTypeError::QualifiedContractEmptyIssuer + | ClarityTypeError::QualifiedContractMissingDot + | ClarityTypeError::InvalidPrincipalEncoding(_) + | ClarityTypeError::InvalidPrincipalLength(_) + | ClarityTypeError::ListTypeMismatch + | ClarityTypeError::SequenceElementArityMismatch { .. } + | ClarityTypeError::ExpectedSequenceValue + | ClarityTypeError::TypeMismatchValue(_, _) + | ClarityTypeError::ResponseTypeMismatch { .. } + | ClarityTypeError::InvalidAsciiCharacter(_) + | ClarityTypeError::InvalidUtf8Encoding => Self::ExpectsAcceptable(format!( + "Unexpected error type during static analysis: {err}" + )), + ClarityTypeError::InvariantViolation(_) + | ClarityTypeError::InvalidPrincipalVersion(_) => Self::ExpectsRejectable(format!( + "Unexpected error type during static analysis: {err}" + )), + ClarityTypeError::CouldNotDetermineSerializationType => { + Self::CouldNotDetermineSerializationType + } + ClarityTypeError::CouldNotDetermineType => Self::CouldNotDetermineType, + ClarityTypeError::UnsupportedTypeInEpoch(ty, epoch) => { + Self::ExpectsRejectable(format!("{ty} should not be used in {epoch}")) + } + ClarityTypeError::UnsupportedEpoch(epoch) => { + Self::ExpectsRejectable(format!("{epoch} is not supported")) + } + } + } +} - pub fn from_clarity_type_error(err: ClarityTypeError) -> Self { - StaticCheckErrorKind::from_clarity_type_error(err).into() +impl From for StaticCheckError { + fn from(err: ClarityTypeError) -> Self { + StaticCheckErrorKind::from(err).into() } } @@ -1108,6 +999,96 @@ impl From<(CommonCheckErrorKind, &SymbolicExpression)> for CheckErrorKind { } } +impl From for CheckErrorKind { + fn from(err: ClarityTypeError) -> Self { + match err { + ClarityTypeError::ValueTooLarge => Self::ValueTooLarge, + ClarityTypeError::TypeSignatureTooDeep => Self::TypeSignatureTooDeep, + ClarityTypeError::ValueOutOfBounds => Self::ValueOutOfBounds, + ClarityTypeError::DuplicateTupleField(name) => Self::NameAlreadyUsed(name), + ClarityTypeError::NoSuchTupleField(field, tuple_sig) => { + Self::NoSuchTupleField(field, tuple_sig) + } + ClarityTypeError::TypeMismatchValue(ty, value) => Self::TypeValueError(ty, value), + ClarityTypeError::TypeMismatch(expected, found) => Self::TypeError(expected, found), + ClarityTypeError::EmptyTuplesNotAllowed => Self::EmptyTuplesNotAllowed, + ClarityTypeError::SupertypeTooLarge => Self::SupertypeTooLarge, + ClarityTypeError::InvalidTypeDescription => Self::InvalidTypeDescription, + ClarityTypeError::ListTypeMismatch => Self::ListTypesMustMatch, + ClarityTypeError::InvalidAsciiCharacter(_) => Self::InvalidCharactersDetected, + ClarityTypeError::InvalidUtf8Encoding => Self::InvalidUTF8Encoding, + ClarityTypeError::ExpectedSequenceValue + | ClarityTypeError::SequenceElementArityMismatch { .. } + | ClarityTypeError::CouldNotDetermineSerializationType + | ClarityTypeError::InvalidUrlString(_) + | ClarityTypeError::InvalidClarityName(_) + | ClarityTypeError::InvalidContractName(_) + | ClarityTypeError::QualifiedContractEmptyIssuer + | ClarityTypeError::QualifiedContractMissingDot + | ClarityTypeError::InvalidPrincipalEncoding(_) + | ClarityTypeError::InvalidPrincipalLength(_) + | ClarityTypeError::ResponseTypeMismatch { .. } => Self::ExpectsAcceptable(format!( + "Unexpected error type during runtime analysis: {err}" + )), + ClarityTypeError::InvariantViolation(_) + | ClarityTypeError::InvalidPrincipalVersion(_) => Self::ExpectsRejectable(format!( + "Unexpected error type during runtime analysis: {err}" + )), + ClarityTypeError::CouldNotDetermineType => Self::CouldNotDetermineType, + ClarityTypeError::UnsupportedTypeInEpoch(ty, epoch) => { + Self::ExpectsRejectable(format!("{ty} should not be used in {epoch}")) + } + ClarityTypeError::UnsupportedEpoch(epoch) => { + Self::ExpectsRejectable(format!("{epoch} is not supported")) + } + } + } +} + +impl From for CommonCheckErrorKind { + fn from(err: ClarityTypeError) -> Self { + match err { + ClarityTypeError::ValueTooLarge => Self::ValueTooLarge, + ClarityTypeError::TypeSignatureTooDeep => Self::TypeSignatureTooDeep, + ClarityTypeError::ValueOutOfBounds => Self::ValueOutOfBounds, + ClarityTypeError::DuplicateTupleField(name) => Self::NameAlreadyUsed(name), + ClarityTypeError::TypeMismatch(expected, found) => Self::TypeError(expected, found), + ClarityTypeError::EmptyTuplesNotAllowed => Self::EmptyTuplesNotAllowed, + ClarityTypeError::SupertypeTooLarge => Self::SupertypeTooLarge, + ClarityTypeError::InvalidTypeDescription => Self::InvalidTypeDescription, + ClarityTypeError::CouldNotDetermineType => Self::CouldNotDetermineType, + ClarityTypeError::ListTypeMismatch + | ClarityTypeError::SequenceElementArityMismatch { .. } + | ClarityTypeError::ExpectedSequenceValue + | ClarityTypeError::InvalidAsciiCharacter(_) + | ClarityTypeError::InvalidUtf8Encoding + | ClarityTypeError::NoSuchTupleField(_, _) + | ClarityTypeError::TypeMismatchValue(_, _) + | ClarityTypeError::CouldNotDetermineSerializationType + | ClarityTypeError::InvalidUrlString(_) + | ClarityTypeError::InvalidClarityName(_) + | ClarityTypeError::InvalidContractName(_) + | ClarityTypeError::QualifiedContractEmptyIssuer + | ClarityTypeError::QualifiedContractMissingDot + | ClarityTypeError::InvalidPrincipalEncoding(_) + | ClarityTypeError::InvalidPrincipalLength(_) + | ClarityTypeError::ResponseTypeMismatch { .. } => { + Self::ExpectsAcceptable(format!("Unexpected error type during analysis: {err}")) + } + ClarityTypeError::InvariantViolation(_) + | ClarityTypeError::InvalidPrincipalVersion(_) => { + Self::ExpectsRejectable(format!("Unexpected error type during analysis: {err}")) + } + ClarityTypeError::UnsupportedTypeInEpoch(ty, epoch) => { + Self::ExpectsRejectable(format!("{ty} should not be used in {epoch}")) + } + ClarityTypeError::UnsupportedEpoch(epoch) => { + Self::ExpectsRejectable(format!("{epoch} is not supported")) + } + } + } +} + impl fmt::Display for CommonCheckErrorKind { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{self:?}") diff --git a/clarity-types/src/errors/mod.rs b/clarity-types/src/errors/mod.rs index d661c63717..f38195eef5 100644 --- a/clarity-types/src/errors/mod.rs +++ b/clarity-types/src/errors/mod.rs @@ -28,6 +28,7 @@ pub use lexer::LexerError; use rusqlite::Error as SqliteError; use stacks_common::types::chainstate::BlockHeaderHash; +use crate::ClarityTypeError; use crate::representations::SymbolicExpression; use crate::types::{FunctionIdentifier, Value}; @@ -256,6 +257,12 @@ impl error::Error for RuntimeError { } } +impl From for VmExecutionError { + fn from(err: ClarityTypeError) -> Self { + Self::from(CheckErrorKind::from(err)) + } +} + impl From for VmExecutionError { fn from(err: ParseError) -> Self { match *err.err { diff --git a/clarity-types/src/types/mod.rs b/clarity-types/src/types/mod.rs index ed6246c6ea..3fc8ad50a5 100644 --- a/clarity-types/src/types/mod.rs +++ b/clarity-types/src/types/mod.rs @@ -39,7 +39,6 @@ pub use self::signatures::{ }; use crate::VmExecutionError; use crate::diagnostic::DiagnosableError; -use crate::errors::CheckErrorKind; use crate::representations::{ClarityName, ContractName, SymbolicExpression}; /// Maximum size in bytes allowed for types. @@ -710,10 +709,8 @@ impl SequenceData { ($data:expr, $seq_type:ident) => { let mut i = 0; while i != $data.data.len() { - let atom_value = SymbolicExpression::atom_value( - $seq_type::to_value(&$data.data[i]) - .map_err(CheckErrorKind::from_clarity_type_error)?, - ); + let atom_value = + SymbolicExpression::atom_value($seq_type::to_value(&$data.data[i])?); match filter(atom_value) { Ok(res) if res == false => { $data.data.remove(i); 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 ddf6cbb984..252d524868 100644 --- a/clarity/src/vm/analysis/type_checker/v2_05/mod.rs +++ b/clarity/src/vm/analysis/type_checker/v2_05/mod.rs @@ -146,10 +146,7 @@ impl FunctionType { check_arguments_at_least(1, args)?; for found_type in args.iter() { analysis_typecheck_cost(accounting, expected_type, found_type)?; - if !expected_type - .admits_type(&StacksEpochId::Epoch2_05, found_type) - .map_err(StaticCheckError::from_clarity_type_error)? - { + if !expected_type.admits_type(&StacksEpochId::Epoch2_05, found_type)? { return Err(StaticCheckErrorKind::TypeError( Box::new(expected_type.clone()), Box::new(found_type.clone()), @@ -167,10 +164,7 @@ impl FunctionType { for (expected_type, found_type) in arg_types.iter().map(|x| &x.signature).zip(args) { analysis_typecheck_cost(accounting, expected_type, found_type)?; - if !expected_type - .admits_type(&StacksEpochId::Epoch2_05, found_type) - .map_err(StaticCheckError::from_clarity_type_error)? - { + if !expected_type.admits_type(&StacksEpochId::Epoch2_05, found_type)? { return Err(StaticCheckErrorKind::TypeError( Box::new(expected_type.clone()), Box::new(found_type.clone()), @@ -185,10 +179,7 @@ impl FunctionType { let found_type = &args[0]; for expected_type in arg_types.iter() { analysis_typecheck_cost(accounting, expected_type, found_type)?; - if expected_type - .admits_type(&StacksEpochId::Epoch2_05, found_type) - .map_err(StaticCheckError::from_clarity_type_error)? - { + if expected_type.admits_type(&StacksEpochId::Epoch2_05, found_type)? { return Ok(return_type.clone()); } } @@ -313,12 +304,8 @@ impl FunctionType { )?; } (expected_type, value) => { - if !expected_type - .admits(&StacksEpochId::Epoch2_05, value) - .map_err(StaticCheckError::from_clarity_type_error)? - { - let actual_type = TypeSignature::type_of(value) - .map_err(StaticCheckError::from_clarity_type_error)?; + if !expected_type.admits(&StacksEpochId::Epoch2_05, value)? { + let actual_type = TypeSignature::type_of(value)?; return Err(StaticCheckErrorKind::TypeError( Box::new(expected_type.clone()), Box::new(actual_type), @@ -407,9 +394,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { runtime_cost( ClarityCostFunction::AnalysisTypeCheck, self, - return_type - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, + return_type.type_size()?, )?; match self.function_return_tracker { @@ -525,10 +510,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { let actual_type = self.type_check(expr, context)?; analysis_typecheck_cost(self, expected_type, &actual_type)?; - if !expected_type - .admits_type(&StacksEpochId::Epoch2_05, &actual_type) - .map_err(StaticCheckError::from_clarity_type_error)? - { + if !expected_type.admits_type(&StacksEpochId::Epoch2_05, &actual_type)? { let mut err: StaticCheckError = StaticCheckErrorKind::TypeError( Box::new(expected_type.clone()), Box::new(actual_type), @@ -811,9 +793,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { context: &TypingContext, ) -> Result { let type_sig = match expr.expr { - AtomValue(ref value) | LiteralValue(ref value) => { - TypeSignature::type_of(value).map_err(StaticCheckError::from_clarity_type_error)? - } + AtomValue(ref value) | LiteralValue(ref value) => TypeSignature::type_of(value)?, Atom(ref name) => self.lookup_variable(name, context)?, List(ref expression) => self.type_check_function_application(expression, context)?, TraitReference(_, _) | Field(_) => { @@ -824,9 +804,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { runtime_cost( ClarityCostFunction::AnalysisTypeAnnotate, self, - type_sig - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, + type_sig.type_size()?, )?; self.type_map.set_type(expr, type_sig.clone())?; Ok(type_sig) @@ -913,9 +891,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { runtime_cost( ClarityCostFunction::AnalysisBindName, self, - v_type - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, + v_type.type_size()?, )?; self.contract_context.add_variable_type(v_name, v_type)?; } @@ -969,18 +945,8 @@ impl<'a, 'b> TypeChecker<'a, 'b> { } => { let (f_name, map_type) = self.type_check_define_map(name, key_type, value_type)?; - let total_type_size = u64::from( - map_type - .0 - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, - ) - .cost_overflow_add(u64::from( - map_type - .1 - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, - ))?; + let total_type_size = u64::from(map_type.0.type_size()?) + .cost_overflow_add(u64::from(map_type.1.type_size()?))?; runtime_cost(ClarityCostFunction::AnalysisBindName, self, total_type_size)?; self.contract_context.add_map_type(f_name, map_type)?; } @@ -994,9 +960,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { runtime_cost( ClarityCostFunction::AnalysisBindName, self, - v_type - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, + v_type.type_size()?, )?; self.contract_context .add_persisted_variable_type(v_name, v_type)?; @@ -1006,9 +970,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { runtime_cost( ClarityCostFunction::AnalysisBindName, self, - TypeSignature::UIntType - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, + TypeSignature::UIntType.type_size()?, )?; self.contract_context.add_ft(token_name)?; } @@ -1017,9 +979,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { runtime_cost( ClarityCostFunction::AnalysisBindName, self, - TypeSignature::UIntType - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, + TypeSignature::UIntType.type_size()?, )?; self.contract_context.add_ft(token_name)?; } @@ -1029,9 +989,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { runtime_cost( ClarityCostFunction::AnalysisBindName, self, - token_type - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, + token_type.type_size()?, )?; self.contract_context.add_nft(token_name, token_type)?; } diff --git a/clarity/src/vm/analysis/type_checker/v2_05/natives/assets.rs b/clarity/src/vm/analysis/type_checker/v2_05/natives/assets.rs index e8a2d3f8b4..d5a492d09a 100644 --- a/clarity/src/vm/analysis/type_checker/v2_05/natives/assets.rs +++ b/clarity/src/vm/analysis/type_checker/v2_05/natives/assets.rs @@ -41,9 +41,7 @@ pub fn check_special_get_owner( runtime_cost( ClarityCostFunction::AnalysisTypeLookup, checker, - expected_asset_type - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, + expected_asset_type.type_size()?, )?; checker.type_check_expects(&args[1], context, &expected_asset_type)?; @@ -97,9 +95,7 @@ pub fn check_special_mint_asset( runtime_cost( ClarityCostFunction::AnalysisTypeLookup, checker, - expected_asset_type - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, + expected_asset_type.type_size()?, )?; checker.type_check_expects(&args[1], context, &expected_asset_type)?; @@ -161,9 +157,7 @@ pub fn check_special_transfer_asset( runtime_cost( ClarityCostFunction::AnalysisTypeLookup, checker, - expected_asset_type - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, + expected_asset_type.type_size()?, )?; checker.type_check_expects(&args[1], context, &expected_asset_type)?; @@ -247,9 +241,7 @@ pub fn check_special_burn_asset( runtime_cost( ClarityCostFunction::AnalysisTypeLookup, checker, - expected_asset_type - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, + expected_asset_type.type_size()?, )?; checker.type_check_expects(&args[1], context, &expected_asset_type)?; diff --git a/clarity/src/vm/analysis/type_checker/v2_05/natives/maps.rs b/clarity/src/vm/analysis/type_checker/v2_05/natives/maps.rs index c5560a43da..b9cb0122af 100644 --- a/clarity/src/vm/analysis/type_checker/v2_05/natives/maps.rs +++ b/clarity/src/vm/analysis/type_checker/v2_05/natives/maps.rs @@ -45,26 +45,18 @@ pub fn check_special_fetch_entry( runtime_cost( ClarityCostFunction::AnalysisTypeLookup, &mut checker.cost_track, - expected_key_type - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, + expected_key_type.type_size()?, )?; runtime_cost( ClarityCostFunction::AnalysisTypeLookup, &mut checker.cost_track, - value_type - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, + value_type.type_size()?, )?; analysis_typecheck_cost(&mut checker.cost_track, expected_key_type, &key_type)?; - let option_type = TypeSignature::new_option(value_type.clone()) - .map_err(StaticCheckError::from_clarity_type_error)?; + let option_type = TypeSignature::new_option(value_type.clone())?; - if !expected_key_type - .admits_type(&StacksEpochId::Epoch2_05, &key_type) - .map_err(StaticCheckError::from_clarity_type_error)? - { + if !expected_key_type.admits_type(&StacksEpochId::Epoch2_05, &key_type)? { Err(StaticCheckError::new(StaticCheckErrorKind::TypeError( Box::new(expected_key_type.clone()), Box::new(key_type), @@ -95,16 +87,11 @@ pub fn check_special_delete_entry( runtime_cost( ClarityCostFunction::AnalysisTypeLookup, &mut checker.cost_track, - expected_key_type - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, + expected_key_type.type_size()?, )?; analysis_typecheck_cost(&mut checker.cost_track, expected_key_type, &key_type)?; - if !expected_key_type - .admits_type(&StacksEpochId::Epoch2_05, &key_type) - .map_err(StaticCheckError::from_clarity_type_error)? - { + if !expected_key_type.admits_type(&StacksEpochId::Epoch2_05, &key_type)? { Err(StaticCheckError::new(StaticCheckErrorKind::TypeError( Box::new(expected_key_type.clone()), Box::new(key_type), @@ -136,33 +123,23 @@ fn check_set_or_insert_entry( runtime_cost( ClarityCostFunction::AnalysisTypeLookup, &mut checker.cost_track, - expected_key_type - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, + expected_key_type.type_size()?, )?; runtime_cost( ClarityCostFunction::AnalysisTypeLookup, &mut checker.cost_track, - value_type - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, + value_type.type_size()?, )?; analysis_typecheck_cost(&mut checker.cost_track, expected_key_type, &key_type)?; analysis_typecheck_cost(&mut checker.cost_track, expected_value_type, &value_type)?; - if !expected_key_type - .admits_type(&StacksEpochId::Epoch2_05, &key_type) - .map_err(StaticCheckError::from_clarity_type_error)? - { + if !expected_key_type.admits_type(&StacksEpochId::Epoch2_05, &key_type)? { Err(StaticCheckError::new(StaticCheckErrorKind::TypeError( Box::new(expected_key_type.clone()), Box::new(key_type), ))) - } else if !expected_value_type - .admits_type(&StacksEpochId::Epoch2_05, &value_type) - .map_err(StaticCheckError::from_clarity_type_error)? - { + } else if !expected_value_type.admits_type(&StacksEpochId::Epoch2_05, &value_type)? { Err(StaticCheckError::new(StaticCheckErrorKind::TypeError( Box::new(expected_value_type.clone()), Box::new(value_type), diff --git a/clarity/src/vm/analysis/type_checker/v2_05/natives/mod.rs b/clarity/src/vm/analysis/type_checker/v2_05/natives/mod.rs index 321f245af9..8eb62e0922 100644 --- a/clarity/src/vm/analysis/type_checker/v2_05/natives/mod.rs +++ b/clarity/src/vm/analysis/type_checker/v2_05/natives/mod.rs @@ -59,13 +59,10 @@ fn check_special_list_cons( runtime_cost( ClarityCostFunction::AnalysisListItemsCheck, checker, - type_arg - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, + type_arg.type_size()?, )?; } - let list_type = TypeSignature::parent_list_type(&typed_args) - .map_err(StaticCheckError::from_clarity_type_error)?; + let list_type = TypeSignature::parent_list_type(&typed_args)?; Ok(TypeSignature::from(list_type)) } @@ -148,8 +145,7 @@ fn check_special_get( } else if let TypeSignature::OptionalType(value_type_sig) = argument_type { if let TypeSignature::TupleType(tuple_type_sig) = *value_type_sig { let inner_type = inner_handle_tuple_get(&tuple_type_sig, field_to_get, checker)?; - let option_type = TypeSignature::new_option(inner_type) - .map_err(StaticCheckError::from_clarity_type_error)?; + let option_type = TypeSignature::new_option(inner_type)?; Ok(option_type) } else { Err(StaticCheckErrorKind::ExpectedTuple(value_type_sig).into()) @@ -207,9 +203,7 @@ pub fn check_special_tuple_cons( runtime_cost( ClarityCostFunction::AnalysisTupleItemsCheck, checker, - var_type - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, + var_type.type_size()?, )?; tuple_type_data.push((var_name.clone(), var_type)); Ok(()) @@ -218,9 +212,7 @@ pub fn check_special_tuple_cons( )?; let tuple_signature = TupleTypeSignature::try_from(tuple_type_data).map_err(|e| { - StaticCheckErrorKind::BadTupleConstruction( - StaticCheckErrorKind::from_clarity_type_error(e).message(), - ) + StaticCheckErrorKind::BadTupleConstruction(StaticCheckErrorKind::from(e).message()) })?; Ok(TypeSignature::TupleType(tuple_signature)) @@ -257,9 +249,7 @@ fn check_special_let( runtime_cost( ClarityCostFunction::AnalysisBindName, checker, - typed_result - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, + typed_result.type_size()?, )?; out_context .variable_types @@ -292,9 +282,7 @@ fn check_special_fetch_var( runtime_cost( ClarityCostFunction::AnalysisTypeLookup, &mut checker.cost_track, - value_type - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, + value_type.type_size()?, )?; Ok(value_type.clone()) @@ -323,16 +311,11 @@ fn check_special_set_var( runtime_cost( ClarityCostFunction::AnalysisTypeLookup, &mut checker.cost_track, - expected_value_type - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, + expected_value_type.type_size()?, )?; analysis_typecheck_cost(&mut checker.cost_track, &value_type, expected_value_type)?; - if !expected_value_type - .admits_type(&StacksEpochId::Epoch2_05, &value_type) - .map_err(StaticCheckError::from_clarity_type_error)? - { + if !expected_value_type.admits_type(&StacksEpochId::Epoch2_05, &value_type)? { Err(StaticCheckError::new(StaticCheckErrorKind::TypeError( Box::new(expected_value_type.clone()), Box::new(value_type), @@ -578,8 +561,7 @@ fn check_get_block_info( checker.type_check_expects(&args[1], context, &TypeSignature::UIntType)?; - TypeSignature::new_option(block_info_prop.type_result()) - .map_err(StaticCheckError::from_clarity_type_error) + Ok(TypeSignature::new_option(block_info_prop.type_result())?) } impl TypedNativeFunction { diff --git a/clarity/src/vm/analysis/type_checker/v2_05/natives/options.rs b/clarity/src/vm/analysis/type_checker/v2_05/natives/options.rs index 8c29d7aa42..75c931428c 100644 --- a/clarity/src/vm/analysis/type_checker/v2_05/natives/options.rs +++ b/clarity/src/vm/analysis/type_checker/v2_05/natives/options.rs @@ -36,8 +36,7 @@ pub fn check_special_okay( runtime_cost(ClarityCostFunction::AnalysisOptionCons, checker, 0)?; let inner_type = checker.type_check(&args[0], context)?; - let resp_type = TypeSignature::new_response(inner_type, no_type()) - .map_err(StaticCheckError::from_clarity_type_error)?; + let resp_type = TypeSignature::new_response(inner_type, no_type())?; Ok(resp_type) } @@ -51,8 +50,7 @@ pub fn check_special_some( runtime_cost(ClarityCostFunction::AnalysisOptionCons, checker, 0)?; let inner_type = checker.type_check(&args[0], context)?; - let resp_type = - TypeSignature::new_option(inner_type).map_err(StaticCheckError::from_clarity_type_error)?; + let resp_type = TypeSignature::new_option(inner_type)?; Ok(resp_type) } @@ -66,8 +64,7 @@ pub fn check_special_error( runtime_cost(ClarityCostFunction::AnalysisOptionCons, checker, 0)?; let inner_type = checker.type_check(&args[0], context)?; - let resp_type = TypeSignature::new_response(no_type(), inner_type) - .map_err(StaticCheckError::from_clarity_type_error)?; + let resp_type = TypeSignature::new_response(no_type(), inner_type)?; Ok(resp_type) } @@ -239,10 +236,7 @@ pub fn check_special_try_ret( if input_type.is_no_type() { Err(StaticCheckErrorKind::CouldNotDetermineResponseOkType.into()) } else { - checker.track_return_type( - TypeSignature::new_option(TypeSignature::NoType) - .map_err(StaticCheckError::from_clarity_type_error)?, - )?; + checker.track_return_type(TypeSignature::new_option(TypeSignature::NoType)?)?; Ok(*input_type) } } @@ -253,10 +247,10 @@ pub fn check_special_try_ret( } else if err_type.is_no_type() { Err(StaticCheckErrorKind::CouldNotDetermineResponseErrType.into()) } else { - checker.track_return_type( - TypeSignature::new_response(TypeSignature::NoType, err_type) - .map_err(StaticCheckError::from_clarity_type_error)?, - )?; + checker.track_return_type(TypeSignature::new_response( + TypeSignature::NoType, + err_type, + )?)?; Ok(ok_type) } } @@ -300,9 +294,7 @@ fn eval_with_new_binding( runtime_cost( ClarityCostFunction::AnalysisBindName, checker, - bind_type - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, + bind_type.type_size()?, )?; checker.contract_context.check_name_used(&bind_name)?; diff --git a/clarity/src/vm/analysis/type_checker/v2_05/natives/sequences.rs b/clarity/src/vm/analysis/type_checker/v2_05/natives/sequences.rs index 6f7522dbfd..09feeb0ff9 100644 --- a/clarity/src/vm/analysis/type_checker/v2_05/natives/sequences.rs +++ b/clarity/src/vm/analysis/type_checker/v2_05/natives/sequences.rs @@ -234,40 +234,29 @@ pub fn check_special_concat( &StacksEpochId::Epoch2_05, lhs_entry_type, rhs_entry_type, - ) - .map_err(StaticCheckError::from_clarity_type_error)?; + )?; let new_len = lhs_max_len .checked_add(rhs_max_len) .ok_or(StaticCheckErrorKind::MaxLengthOverflow)?; - TypeSignature::list_of(list_entry_type, new_len) - .map_err(StaticCheckError::from_clarity_type_error)? + TypeSignature::list_of(list_entry_type, new_len)? } (BufferType(lhs_len), BufferType(rhs_len)) => { let size: u32 = u32::from(lhs_len) .checked_add(u32::from(rhs_len)) .ok_or(StaticCheckErrorKind::MaxLengthOverflow)?; - TypeSignature::SequenceType(BufferType( - size.try_into() - .map_err(StaticCheckError::from_clarity_type_error)?, - )) + TypeSignature::SequenceType(BufferType(size.try_into()?)) } (StringType(ASCII(lhs_len)), StringType(ASCII(rhs_len))) => { let size: u32 = u32::from(lhs_len) .checked_add(u32::from(rhs_len)) .ok_or(StaticCheckErrorKind::MaxLengthOverflow)?; - TypeSignature::SequenceType(StringType(ASCII( - size.try_into() - .map_err(StaticCheckError::from_clarity_type_error)?, - ))) + TypeSignature::SequenceType(StringType(ASCII(size.try_into()?))) } (StringType(UTF8(lhs_len)), StringType(UTF8(rhs_len))) => { let size: u32 = u32::from(lhs_len) .checked_add(u32::from(rhs_len)) .ok_or(StaticCheckErrorKind::MaxLengthOverflow)?; - TypeSignature::SequenceType(StringType(UTF8( - size.try_into() - .map_err(StaticCheckError::from_clarity_type_error)?, - ))) + TypeSignature::SequenceType(StringType(UTF8(size.try_into()?))) } (_, _) => { return Err(StaticCheckErrorKind::TypeError( @@ -304,13 +293,11 @@ pub fn check_special_append( &StacksEpochId::Epoch2_05, &lhs_entry_type, &rhs_type, - ) - .map_err(StaticCheckError::from_clarity_type_error)?; + )?; let new_len = lhs_max_len .checked_add(1) .ok_or(StaticCheckErrorKind::MaxLengthOverflow)?; - let return_type = TypeSignature::list_of(list_entry_type, new_len) - .map_err(StaticCheckError::from_clarity_type_error)?; + let return_type = TypeSignature::list_of(list_entry_type, new_len)?; Ok(return_type) } _ => Err(StaticCheckErrorKind::ExpectedListApplication.into()), @@ -338,9 +325,7 @@ pub fn check_special_as_max_len( runtime_cost( ClarityCostFunction::AnalysisTypeAnnotate, checker, - TypeSignature::UIntType - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, + TypeSignature::UIntType.type_size()?, )?; checker .type_map @@ -355,28 +340,22 @@ pub fn check_special_as_max_len( match sequence { TypeSignature::SequenceType(ListType(list)) => { let (lhs_entry_type, _) = list.destruct(); - let resized_list = ListTypeData::new_list(lhs_entry_type, expected_len) - .map_err(StaticCheckError::from_clarity_type_error)?; + let resized_list = ListTypeData::new_list(lhs_entry_type, expected_len)?; Ok(TypeSignature::OptionalType(Box::new( TypeSignature::SequenceType(ListType(resized_list)), ))) } TypeSignature::SequenceType(BufferType(_)) => Ok(TypeSignature::OptionalType(Box::new( - TypeSignature::SequenceType(BufferType( - BufferLength::try_from(expected_len) - .map_err(StaticCheckError::from_clarity_type_error)?, - )), + TypeSignature::SequenceType(BufferType(BufferLength::try_from(expected_len)?)), ))), TypeSignature::SequenceType(StringType(ASCII(_))) => Ok(TypeSignature::OptionalType( Box::new(TypeSignature::SequenceType(StringType(ASCII( - BufferLength::try_from(expected_len) - .map_err(StaticCheckError::from_clarity_type_error)?, + BufferLength::try_from(expected_len)?, )))), )), TypeSignature::SequenceType(StringType(UTF8(_))) => Ok(TypeSignature::OptionalType( Box::new(TypeSignature::SequenceType(StringType(UTF8( - StringUTF8Length::try_from(expected_len) - .map_err(StaticCheckError::from_clarity_type_error)?, + StringUTF8Length::try_from(expected_len)?, )))), )), _ => Err(StaticCheckErrorKind::ExpectedSequence(Box::new(sequence)).into()), @@ -418,7 +397,7 @@ pub fn check_special_element_at( match collection_type { TypeSignature::SequenceType(ListType(list)) => { let (entry_type, _) = list.destruct(); - TypeSignature::new_option(entry_type).map_err(StaticCheckError::from_clarity_type_error) + Ok(TypeSignature::new_option(entry_type)?) } TypeSignature::SequenceType(BufferType(_)) => Ok(TypeSignature::OptionalType(Box::new( TypeSignature::BUFFER_1, @@ -458,6 +437,5 @@ pub fn check_special_index_of( checker.type_check_expects(&args[1], context, &expected_input_type)?; - TypeSignature::new_option(TypeSignature::UIntType) - .map_err(StaticCheckError::from_clarity_type_error) + Ok(TypeSignature::new_option(TypeSignature::UIntType)?) } 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 26007511dc..f090e2d175 100644 --- a/clarity/src/vm/analysis/type_checker/v2_1/mod.rs +++ b/clarity/src/vm/analysis/type_checker/v2_1/mod.rs @@ -186,7 +186,7 @@ impl FunctionType { let cost = Some(compute_typecheck_cost(accounting, expected_type, arg_type)); let admitted = match expected_type.admits_type(&StacksEpochId::Epoch21, arg_type) { Ok(admitted) => admitted, - Err(e) => return (cost, Err(StaticCheckError::from_clarity_type_error(e))), + Err(e) => return (cost, Err(StaticCheckError::from(e))), }; if !admitted { return ( @@ -292,10 +292,7 @@ impl FunctionType { check_arguments_at_least(1, args)?; for found_type in args.iter() { analysis_typecheck_cost(accounting, expected_type, found_type)?; - if !expected_type - .admits_type(&StacksEpochId::Epoch21, found_type) - .map_err(StaticCheckError::from_clarity_type_error)? - { + if !expected_type.admits_type(&StacksEpochId::Epoch21, found_type)? { return Err(StaticCheckErrorKind::TypeError( Box::new(expected_type.clone()), Box::new(found_type.clone()), @@ -313,10 +310,7 @@ impl FunctionType { for (expected_type, found_type) in arg_types.iter().map(|x| &x.signature).zip(args) { analysis_typecheck_cost(accounting, expected_type, found_type)?; - if !expected_type - .admits_type(&StacksEpochId::Epoch21, found_type) - .map_err(StaticCheckError::from_clarity_type_error)? - { + if !expected_type.admits_type(&StacksEpochId::Epoch21, found_type)? { return Err(StaticCheckErrorKind::TypeError( Box::new(expected_type.clone()), Box::new(found_type.clone()), @@ -331,10 +325,7 @@ impl FunctionType { let found_type = &args[0]; for expected_type in arg_types.iter() { analysis_typecheck_cost(accounting, expected_type, found_type)?; - if expected_type - .admits_type(&StacksEpochId::Epoch21, found_type) - .map_err(StaticCheckError::from_clarity_type_error)? - { + if expected_type.admits_type(&StacksEpochId::Epoch21, found_type)? { return Ok(return_type.clone()); } } @@ -494,8 +485,7 @@ impl FunctionType { contract_identifier.clone(), )) } - _ => TypeSignature::type_of(value) - .map_err(StaticCheckError::from_clarity_type_error)?, + _ => TypeSignature::type_of(value)?, }) } } @@ -518,8 +508,7 @@ impl FunctionType { data: Some(inner_value), }) => TypeSignature::new_option( self.clarity2_principal_to_callable_type(inner_value, depth + 1)?, - ) - .map_err(StaticCheckError::from_clarity_type_error)?, + )?, Value::Response(ResponseData { committed, data }) => { let (ok_type, err_type) = if *committed { ( @@ -532,8 +521,7 @@ impl FunctionType { self.clarity2_principal_to_callable_type(data, depth + 1)?, ) }; - TypeSignature::new_response(ok_type, err_type) - .map_err(StaticCheckError::from_clarity_type_error)? + TypeSignature::new_response(ok_type, err_type)? } Value::Sequence(SequenceData::List(ListData { data, @@ -545,10 +533,10 @@ impl FunctionType { } None => TypeSignature::NoType, }; - TypeSignature::SequenceType(SequenceSubtype::ListType( - ListTypeData::new_list(inner_type, data.len() as u32) - .map_err(StaticCheckError::from_clarity_type_error)?, - )) + TypeSignature::SequenceType(SequenceSubtype::ListType(ListTypeData::new_list( + inner_type, + data.len() as u32, + )?)) } Value::Tuple(TupleData { type_signature: _, @@ -561,14 +549,9 @@ impl FunctionType { self.clarity2_principal_to_callable_type(field_value, depth + 1)?, ); } - TypeSignature::TupleType( - TupleTypeSignature::try_from(type_map) - .map_err(StaticCheckError::from_clarity_type_error)?, - ) - } - _ => { - TypeSignature::type_of(value).map_err(StaticCheckError::from_clarity_type_error)? + TypeSignature::TupleType(TupleTypeSignature::try_from(type_map)?) } + _ => TypeSignature::type_of(value)?, }) } @@ -626,12 +609,8 @@ impl FunctionType { )?; } (expected_type, value) => { - if !expected_type - .admits(&StacksEpochId::Epoch21, value) - .map_err(StaticCheckError::from_clarity_type_error)? - { - let actual_type = TypeSignature::type_of(value) - .map_err(StaticCheckError::from_clarity_type_error)?; + if !expected_type.admits(&StacksEpochId::Epoch21, value)? { + let actual_type = TypeSignature::type_of(value)?; return Err(StaticCheckErrorKind::TypeError( Box::new(expected_type.clone()), Box::new(actual_type.clone()), @@ -670,10 +649,7 @@ fn check_function_arg_signature( match expected_sig { FunctionArgSignature::Single(expected_type) => { analysis_typecheck_cost(cost_tracker, expected_type, actual_type)?; - if !expected_type - .admits_type(&StacksEpochId::Epoch21, actual_type) - .map_err(StaticCheckError::from_clarity_type_error)? - { + if !expected_type.admits_type(&StacksEpochId::Epoch21, actual_type)? { return Err(StaticCheckErrorKind::TypeError( Box::new(expected_type.clone()), Box::new(actual_type.clone()), @@ -685,10 +661,7 @@ fn check_function_arg_signature( let mut admitted = false; for expected_type in expected_types.iter() { analysis_typecheck_cost(cost_tracker, expected_type, actual_type)?; - if expected_type - .admits_type(&StacksEpochId::Epoch21, actual_type) - .map_err(StaticCheckError::from_clarity_type_error)? - { + if expected_type.admits_type(&StacksEpochId::Epoch21, actual_type)? { admitted = true; break; } @@ -965,10 +938,7 @@ fn clarity2_inner_type_check_type( } (TypeSignature::NoType, _) => (), (_, _) => { - if !expected_type - .admits_type(&StacksEpochId::Epoch21, actual_type) - .map_err(StaticCheckError::from_clarity_type_error)? - { + if !expected_type.admits_type(&StacksEpochId::Epoch21, actual_type)? { return Err(StaticCheckErrorKind::TypeError( Box::new(expected_type.clone()), Box::new(actual_type.clone()), @@ -1120,9 +1090,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { runtime_cost( ClarityCostFunction::AnalysisTypeCheck, self, - return_type - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, + return_type.type_size()?, )?; match self.function_return_tracker { @@ -1386,12 +1354,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { if self.epoch.analysis_memory() { let added_memory = u64::from(arg_name.len()) - .checked_add( - arg_type - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)? - .into(), - ) + .checked_add(arg_type.type_size()?.into()) .ok_or_else(|| CostErrors::CostOverflow)?; self.add_memory(added_memory)?; tracked_mem = tracked_mem @@ -1442,8 +1405,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { } else { return_type } - .concretize() - .map_err(StaticCheckError::from_clarity_type_error)? + .concretize()? }; self.function_return_tracker = None; @@ -1619,10 +1581,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { let actual_type = self.type_check(expr, context)?; analysis_typecheck_cost(self, expected_type, &actual_type)?; - if !expected_type - .admits_type(&StacksEpochId::Epoch21, &actual_type) - .map_err(StaticCheckError::from_clarity_type_error)? - { + if !expected_type.admits_type(&StacksEpochId::Epoch21, &actual_type)? { let mut err: StaticCheckError = StaticCheckErrorKind::TypeError( Box::new(expected_type.clone()), Box::new(actual_type.clone()), @@ -1642,11 +1601,8 @@ impl<'a, 'b> TypeChecker<'a, 'b> { expected_type: &TypeSignature, ) -> Result { let mut expr_type = match expr.expr { - AtomValue(ref value) => { - TypeSignature::type_of(value).map_err(StaticCheckError::from_clarity_type_error)? - } - LiteralValue(ref value) => TypeSignature::literal_type_of(value) - .map_err(StaticCheckError::from_clarity_type_error)?, + AtomValue(ref value) => TypeSignature::type_of(value)?, + LiteralValue(ref value) => TypeSignature::literal_type_of(value)?, Atom(ref name) => self.lookup_variable(name, context)?, List(ref expression) => self.type_check_function_application(expression, context)?, TraitReference(_, _) | Field(_) => { @@ -1671,9 +1627,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { runtime_cost( ClarityCostFunction::AnalysisTypeAnnotate, self, - expr_type - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, + expr_type.type_size()?, )?; self.type_map.set_type(expr, expr_type.clone())?; Ok(expr_type) @@ -1685,11 +1639,8 @@ impl<'a, 'b> TypeChecker<'a, 'b> { context: &TypingContext, ) -> Result { let expr_type = match expr.expr { - AtomValue(ref value) => { - TypeSignature::type_of(value).map_err(StaticCheckError::from_clarity_type_error)? - } - LiteralValue(ref value) => TypeSignature::literal_type_of(value) - .map_err(StaticCheckError::from_clarity_type_error)?, + AtomValue(ref value) => TypeSignature::type_of(value)?, + LiteralValue(ref value) => TypeSignature::literal_type_of(value)?, Atom(ref name) => self.lookup_variable(name, context)?, List(ref expression) => self.type_check_function_application(expression, context)?, TraitReference(_, _) | Field(_) => { @@ -1700,9 +1651,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { runtime_cost( ClarityCostFunction::AnalysisTypeAnnotate, self, - expr_type - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, + expr_type.type_size()?, )?; self.type_map.set_type(expr, expr_type.clone())?; Ok(expr_type) @@ -1789,18 +1738,11 @@ impl<'a, 'b> TypeChecker<'a, 'b> { runtime_cost( ClarityCostFunction::AnalysisBindName, self, - v_type - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, + v_type.type_size()?, )?; if self.epoch.analysis_memory() { self.add_memory(v_name.len().into())?; - self.add_memory( - v_type - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)? - .into(), - )?; + self.add_memory(v_type.type_size()?.into())?; } self.contract_context.add_variable_type(v_name, v_type)?; } @@ -1865,35 +1807,13 @@ impl<'a, 'b> TypeChecker<'a, 'b> { } => { let (f_name, map_type) = self.type_check_define_map(name, key_type, value_type)?; - let total_type_size = u64::from( - map_type - .0 - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, - ) - .cost_overflow_add(u64::from( - map_type - .1 - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, - ))?; + let total_type_size = u64::from(map_type.0.type_size()?) + .cost_overflow_add(u64::from(map_type.1.type_size()?))?; runtime_cost(ClarityCostFunction::AnalysisBindName, self, total_type_size)?; if self.epoch.analysis_memory() { self.add_memory(f_name.len().into())?; - self.add_memory( - map_type - .0 - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)? - .into(), - )?; - self.add_memory( - map_type - .1 - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)? - .into(), - )?; + self.add_memory(map_type.0.type_size()?.into())?; + self.add_memory(map_type.1.type_size()?.into())?; } self.contract_context.add_map_type(f_name, map_type)?; } @@ -1907,18 +1827,11 @@ impl<'a, 'b> TypeChecker<'a, 'b> { runtime_cost( ClarityCostFunction::AnalysisBindName, self, - v_type - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, + v_type.type_size()?, )?; if self.epoch.analysis_memory() { self.add_memory(v_name.len().into())?; - self.add_memory( - v_type - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)? - .into(), - )?; + self.add_memory(v_type.type_size()?.into())?; } self.contract_context .add_persisted_variable_type(v_name, v_type)?; @@ -1928,18 +1841,11 @@ impl<'a, 'b> TypeChecker<'a, 'b> { runtime_cost( ClarityCostFunction::AnalysisBindName, self, - TypeSignature::UIntType - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, + TypeSignature::UIntType.type_size()?, )?; if self.epoch.analysis_memory() { self.add_memory(token_name.len().into())?; - self.add_memory( - TypeSignature::UIntType - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)? - .into(), - )?; + self.add_memory(TypeSignature::UIntType.type_size()?.into())?; } self.contract_context.add_ft(token_name)?; } @@ -1948,18 +1854,11 @@ impl<'a, 'b> TypeChecker<'a, 'b> { runtime_cost( ClarityCostFunction::AnalysisBindName, self, - TypeSignature::UIntType - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, + TypeSignature::UIntType.type_size()?, )?; if self.epoch.analysis_memory() { self.add_memory(token_name.len().into())?; - self.add_memory( - TypeSignature::UIntType - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)? - .into(), - )?; + self.add_memory(TypeSignature::UIntType.type_size()?.into())?; } self.contract_context.add_ft(token_name)?; } @@ -1969,18 +1868,11 @@ impl<'a, 'b> TypeChecker<'a, 'b> { runtime_cost( ClarityCostFunction::AnalysisBindName, self, - token_type - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, + token_type.type_size()?, )?; if self.epoch.analysis_memory() { self.add_memory(token_name.len().into())?; - self.add_memory( - token_type - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)? - .into(), - )?; + self.add_memory(token_type.type_size()?.into())?; } self.contract_context.add_nft(token_name, token_type)?; } diff --git a/clarity/src/vm/analysis/type_checker/v2_1/natives/assets.rs b/clarity/src/vm/analysis/type_checker/v2_1/natives/assets.rs index e4adcaccd2..b85d1cabff 100644 --- a/clarity/src/vm/analysis/type_checker/v2_1/natives/assets.rs +++ b/clarity/src/vm/analysis/type_checker/v2_1/natives/assets.rs @@ -43,9 +43,7 @@ pub fn check_special_get_owner( runtime_cost( ClarityCostFunction::AnalysisTypeLookup, checker, - expected_asset_type - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, + expected_asset_type.type_size()?, )?; checker.type_check_expects(&args[1], context, &expected_asset_type)?; @@ -99,9 +97,7 @@ pub fn check_special_mint_asset( runtime_cost( ClarityCostFunction::AnalysisTypeLookup, checker, - expected_asset_type - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, + expected_asset_type.type_size()?, )?; checker.type_check_expects(&args[1], context, &expected_asset_type)?; @@ -163,9 +159,7 @@ pub fn check_special_transfer_asset( runtime_cost( ClarityCostFunction::AnalysisTypeLookup, checker, - expected_asset_type - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, + expected_asset_type.type_size()?, )?; checker.type_check_expects(&args[1], context, &expected_asset_type)?; @@ -300,9 +294,7 @@ pub fn check_special_burn_asset( runtime_cost( ClarityCostFunction::AnalysisTypeLookup, checker, - expected_asset_type - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, + expected_asset_type.type_size()?, )?; checker.type_check_expects(&args[1], context, &expected_asset_type)?; diff --git a/clarity/src/vm/analysis/type_checker/v2_1/natives/conversions.rs b/clarity/src/vm/analysis/type_checker/v2_1/natives/conversions.rs index 2d7fc62849..a558c6baf5 100644 --- a/clarity/src/vm/analysis/type_checker/v2_1/natives/conversions.rs +++ b/clarity/src/vm/analysis/type_checker/v2_1/natives/conversions.rs @@ -21,16 +21,10 @@ pub fn check_special_to_consensus_buff( ) -> Result { check_argument_count(1, args)?; let input_type = checker.type_check(&args[0], context)?; - let buffer_max_len = BufferLength::try_from( - input_type - .max_serialized_size() - .map_err(StaticCheckError::from_clarity_type_error)?, - ) - .map_err(StaticCheckError::from_clarity_type_error)?; - TypeSignature::new_option(TypeSignature::SequenceType(SequenceSubtype::BufferType( - buffer_max_len, - ))) - .map_err(StaticCheckError::from_clarity_type_error) + let buffer_max_len = BufferLength::try_from(input_type.max_serialized_size()?)?; + Ok(TypeSignature::new_option(TypeSignature::SequenceType( + SequenceSubtype::BufferType(buffer_max_len), + ))?) } /// `from-consensus-buff?` admits exactly two arguments: @@ -46,7 +40,7 @@ pub fn check_special_from_consensus_buff( check_argument_count(2, args)?; let result_type = TypeSignature::parse_type_repr(StacksEpochId::Epoch21, &args[0], checker)?; checker.type_check_expects(&args[1], context, &TypeSignature::BUFFER_MAX)?; - TypeSignature::new_option(result_type).map_err(StaticCheckError::from_clarity_type_error) + Ok(TypeSignature::new_option(result_type)?) } /// `to-ascii?` admits exactly one argument, a value to convert to a @@ -82,13 +76,11 @@ pub fn check_special_to_ascii( if u32::from(len.clone()) <= MAX_TO_ASCII_BUFFER_LEN => { // Each byte in the buffer becomes two ASCII characters, plus "0x" prefix - TypeSignature::new_ascii_type((u32::from(len) * 2 + 2).into()) - .map_err(StaticCheckError::from_clarity_type_error)? + TypeSignature::new_ascii_type((u32::from(len) * 2 + 2).into())? } TypeSignature::SequenceType(SequenceSubtype::StringType(StringSubtype::UTF8(len))) => { // Each UTF-8 character is exactly one ASCII character - TypeSignature::new_ascii_type(u32::from(len).into()) - .map_err(StaticCheckError::from_clarity_type_error)? + TypeSignature::new_ascii_type(u32::from(len).into())? } _ => { let types = vec![ diff --git a/clarity/src/vm/analysis/type_checker/v2_1/natives/maps.rs b/clarity/src/vm/analysis/type_checker/v2_1/natives/maps.rs index 27fe4ab13e..26e44b9aea 100644 --- a/clarity/src/vm/analysis/type_checker/v2_1/natives/maps.rs +++ b/clarity/src/vm/analysis/type_checker/v2_1/natives/maps.rs @@ -45,26 +45,18 @@ pub fn check_special_fetch_entry( runtime_cost( ClarityCostFunction::AnalysisTypeLookup, &mut checker.cost_track, - expected_key_type - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, + expected_key_type.type_size()?, )?; runtime_cost( ClarityCostFunction::AnalysisTypeLookup, &mut checker.cost_track, - value_type - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, + value_type.type_size()?, )?; analysis_typecheck_cost(&mut checker.cost_track, expected_key_type, &key_type)?; - let option_type = TypeSignature::new_option(value_type.clone()) - .map_err(StaticCheckError::from_clarity_type_error)?; + let option_type = TypeSignature::new_option(value_type.clone())?; - if !expected_key_type - .admits_type(&StacksEpochId::Epoch21, &key_type) - .map_err(StaticCheckError::from_clarity_type_error)? - { + if !expected_key_type.admits_type(&StacksEpochId::Epoch21, &key_type)? { Err(StaticCheckError::new(StaticCheckErrorKind::TypeError( Box::new(expected_key_type.clone()), Box::new(key_type), @@ -95,16 +87,11 @@ pub fn check_special_delete_entry( runtime_cost( ClarityCostFunction::AnalysisTypeLookup, &mut checker.cost_track, - expected_key_type - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, + expected_key_type.type_size()?, )?; analysis_typecheck_cost(&mut checker.cost_track, expected_key_type, &key_type)?; - if !expected_key_type - .admits_type(&StacksEpochId::Epoch21, &key_type) - .map_err(StaticCheckError::from_clarity_type_error)? - { + if !expected_key_type.admits_type(&StacksEpochId::Epoch21, &key_type)? { Err(StaticCheckError::new(StaticCheckErrorKind::TypeError( Box::new(expected_key_type.clone()), Box::new(key_type), @@ -136,33 +123,23 @@ fn check_set_or_insert_entry( runtime_cost( ClarityCostFunction::AnalysisTypeLookup, &mut checker.cost_track, - expected_key_type - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, + expected_key_type.type_size()?, )?; runtime_cost( ClarityCostFunction::AnalysisTypeLookup, &mut checker.cost_track, - value_type - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, + value_type.type_size()?, )?; analysis_typecheck_cost(&mut checker.cost_track, expected_key_type, &key_type)?; analysis_typecheck_cost(&mut checker.cost_track, expected_value_type, &value_type)?; - if !expected_key_type - .admits_type(&StacksEpochId::Epoch21, &key_type) - .map_err(StaticCheckError::from_clarity_type_error)? - { + if !expected_key_type.admits_type(&StacksEpochId::Epoch21, &key_type)? { Err(StaticCheckError::new(StaticCheckErrorKind::TypeError( Box::new(expected_key_type.clone()), Box::new(key_type), ))) - } else if !expected_value_type - .admits_type(&StacksEpochId::Epoch21, &value_type) - .map_err(StaticCheckError::from_clarity_type_error)? - { + } else if !expected_value_type.admits_type(&StacksEpochId::Epoch21, &value_type)? { Err(StaticCheckError::new(StaticCheckErrorKind::TypeError( Box::new(expected_value_type.clone()), Box::new(value_type), diff --git a/clarity/src/vm/analysis/type_checker/v2_1/natives/mod.rs b/clarity/src/vm/analysis/type_checker/v2_1/natives/mod.rs index 05ef31e93e..44e6a0d2fe 100644 --- a/clarity/src/vm/analysis/type_checker/v2_1/natives/mod.rs +++ b/clarity/src/vm/analysis/type_checker/v2_1/natives/mod.rs @@ -72,7 +72,7 @@ fn check_special_list_cons( let checked = checker.type_check(arg, context)?; let cost = checked .type_size() - .map_err(StaticCheckErrorKind::from_clarity_type_error) + .map_err(StaticCheckErrorKind::from) .and_then(|ty_size| { checker .compute_cost( @@ -84,11 +84,7 @@ fn check_special_list_cons( costs.push(cost); if let Some(cur_size) = entries_size { - entries_size = cur_size.checked_add( - checked - .size() - .map_err(StaticCheckError::from_clarity_type_error)?, - ); + entries_size = cur_size.checked_add(checked.size()?); } if let Some(cur_size) = entries_size { if cur_size > MAX_VALUE_SIZE { @@ -108,7 +104,7 @@ fn check_special_list_cons( } let typed_args = result; TypeSignature::parent_list_type(&typed_args) - .map_err(StaticCheckError::from_clarity_type_error) + .map_err(StaticCheckError::from) .map(TypeSignature::from) } @@ -191,8 +187,7 @@ fn check_special_get( } else if let TypeSignature::OptionalType(value_type_sig) = argument_type { if let TypeSignature::TupleType(tuple_type_sig) = *value_type_sig { let inner_type = inner_handle_tuple_get(&tuple_type_sig, field_to_get, checker)?; - let option_type = TypeSignature::new_option(inner_type) - .map_err(StaticCheckError::from_clarity_type_error)?; + let option_type = TypeSignature::new_option(inner_type)?; Ok(option_type) } else { Err(StaticCheckErrorKind::ExpectedTuple(value_type_sig).into()) @@ -256,24 +251,14 @@ pub fn check_special_tuple_cons( runtime_cost( ClarityCostFunction::AnalysisTupleItemsCheck, checker, - var_type - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, + var_type.type_size()?, )?; if type_size < MAX_VALUE_SIZE { type_size = type_size .saturating_add(var_name.len() as u32) .saturating_add(var_name.len() as u32) - .saturating_add( - var_type - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, - ) - .saturating_add( - var_type - .size() - .map_err(StaticCheckError::from_clarity_type_error)?, - ); + .saturating_add(var_type.type_size()?) + .saturating_add(var_type.size()?); tuple_type_data.push((var_name.clone(), var_type)); } else { cons_error = Err(StaticCheckErrorKind::BadTupleConstruction(format!( @@ -287,9 +272,7 @@ pub fn check_special_tuple_cons( cons_error?; let tuple_signature = TupleTypeSignature::try_from(tuple_type_data).map_err(|e| { - StaticCheckErrorKind::BadTupleConstruction( - StaticCheckErrorKind::from_clarity_type_error(e).message(), - ) + StaticCheckErrorKind::BadTupleConstruction(StaticCheckErrorKind::from(e).message()) })?; Ok(TypeSignature::TupleType(tuple_signature)) @@ -328,17 +311,11 @@ fn check_special_let( runtime_cost( ClarityCostFunction::AnalysisBindName, checker, - typed_result - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, + typed_result.type_size()?, )?; if checker.epoch.analysis_memory() { let memory_use = u64::from(var_name.len()) - .checked_add(u64::from( - typed_result - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, - )) + .checked_add(u64::from(typed_result.type_size()?)) .ok_or_else(|| CostErrors::CostOverflow)?; added_memory = added_memory .checked_add(memory_use) @@ -378,9 +355,7 @@ fn check_special_fetch_var( runtime_cost( ClarityCostFunction::AnalysisTypeLookup, &mut checker.cost_track, - value_type - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, + value_type.type_size()?, )?; Ok(value_type.clone()) @@ -409,16 +384,11 @@ fn check_special_set_var( runtime_cost( ClarityCostFunction::AnalysisTypeLookup, &mut checker.cost_track, - expected_value_type - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, + expected_value_type.type_size()?, )?; analysis_typecheck_cost(&mut checker.cost_track, &value_type, expected_value_type)?; - if !expected_value_type - .admits_type(&StacksEpochId::Epoch21, &value_type) - .map_err(StaticCheckError::from_clarity_type_error)? - { + if !expected_value_type.admits_type(&StacksEpochId::Epoch21, &value_type)? { Err(StaticCheckError::new(StaticCheckErrorKind::TypeError( Box::new(expected_value_type.clone()), Box::new(value_type), @@ -821,8 +791,7 @@ fn check_get_block_info( checker.type_check_expects(&args[1], context, &TypeSignature::UIntType)?; - TypeSignature::new_option(block_info_prop.type_result()) - .map_err(StaticCheckError::from_clarity_type_error) + Ok(TypeSignature::new_option(block_info_prop.type_result())?) } // # Errors @@ -846,10 +815,13 @@ fn check_get_burn_block_info( checker.type_check_expects(&args[1], context, &TypeSignature::UIntType)?; - TypeSignature::new_option(block_info_prop.type_result().map_err(|_| { - StaticCheckErrorKind::ExpectsRejectable("FAILED to type valid burn info property".into()) - })?) - .map_err(StaticCheckError::from_clarity_type_error) + Ok(TypeSignature::new_option( + block_info_prop.type_result().map_err(|_| { + StaticCheckErrorKind::ExpectsRejectable( + "FAILED to type valid burn info property".into(), + ) + })?, + )?) } fn check_get_stacks_block_info( @@ -871,8 +843,7 @@ fn check_get_stacks_block_info( checker.type_check_expects(&args[1], context, &TypeSignature::UIntType)?; - TypeSignature::new_option(block_info_prop.type_result()) - .map_err(StaticCheckError::from_clarity_type_error) + Ok(TypeSignature::new_option(block_info_prop.type_result())?) } fn check_get_tenure_info( @@ -893,8 +864,7 @@ fn check_get_tenure_info( checker.type_check_expects(&args[1], context, &TypeSignature::UIntType)?; - TypeSignature::new_option(block_info_prop.type_result()) - .map_err(StaticCheckError::from_clarity_type_error) + Ok(TypeSignature::new_option(block_info_prop.type_result())?) } impl TypedNativeFunction { diff --git a/clarity/src/vm/analysis/type_checker/v2_1/natives/options.rs b/clarity/src/vm/analysis/type_checker/v2_1/natives/options.rs index e232ccbf4a..57406396d7 100644 --- a/clarity/src/vm/analysis/type_checker/v2_1/natives/options.rs +++ b/clarity/src/vm/analysis/type_checker/v2_1/natives/options.rs @@ -37,8 +37,7 @@ pub fn check_special_okay( runtime_cost(ClarityCostFunction::AnalysisOptionCons, checker, 0)?; let inner_type = checker.type_check(&args[0], context)?; - let resp_type = TypeSignature::new_response(inner_type, no_type()) - .map_err(StaticCheckError::from_clarity_type_error)?; + let resp_type = TypeSignature::new_response(inner_type, no_type())?; Ok(resp_type) } @@ -52,8 +51,7 @@ pub fn check_special_some( runtime_cost(ClarityCostFunction::AnalysisOptionCons, checker, 0)?; let inner_type = checker.type_check(&args[0], context)?; - let resp_type = - TypeSignature::new_option(inner_type).map_err(StaticCheckError::from_clarity_type_error)?; + let resp_type = TypeSignature::new_option(inner_type)?; Ok(resp_type) } @@ -67,8 +65,7 @@ pub fn check_special_error( runtime_cost(ClarityCostFunction::AnalysisOptionCons, checker, 0)?; let inner_type = checker.type_check(&args[0], context)?; - let resp_type = TypeSignature::new_response(no_type(), inner_type) - .map_err(StaticCheckError::from_clarity_type_error)?; + let resp_type = TypeSignature::new_response(no_type(), inner_type)?; Ok(resp_type) } @@ -241,10 +238,7 @@ pub fn check_special_try_ret( if input_type.is_no_type() { Err(StaticCheckErrorKind::CouldNotDetermineResponseOkType.into()) } else { - checker.track_return_type( - TypeSignature::new_option(TypeSignature::NoType) - .map_err(StaticCheckError::from_clarity_type_error)?, - )?; + checker.track_return_type(TypeSignature::new_option(TypeSignature::NoType)?)?; Ok(*input_type) } } @@ -255,10 +249,10 @@ pub fn check_special_try_ret( } else if err_type.is_no_type() { Err(StaticCheckErrorKind::CouldNotDetermineResponseErrType.into()) } else { - checker.track_return_type( - TypeSignature::new_response(TypeSignature::NoType, err_type) - .map_err(StaticCheckError::from_clarity_type_error)?, - )?; + checker.track_return_type(TypeSignature::new_response( + TypeSignature::NoType, + err_type, + )?)?; Ok(ok_type) } } @@ -303,18 +297,12 @@ fn eval_with_new_binding( runtime_cost( ClarityCostFunction::AnalysisBindName, checker, - bind_type - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, + bind_type.type_size()?, )?; let mut memory_use = 0; if checker.epoch.analysis_memory() { memory_use = u64::from(bind_name.len()) - .checked_add(u64::from( - bind_type - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, - )) + .checked_add(u64::from(bind_type.type_size()?)) .ok_or_else(|| CostErrors::CostOverflow)?; checker.add_memory(memory_use)?; } diff --git a/clarity/src/vm/analysis/type_checker/v2_1/natives/post_conditions.rs b/clarity/src/vm/analysis/type_checker/v2_1/natives/post_conditions.rs index 1bf9364d47..5b7547c31f 100644 --- a/clarity/src/vm/analysis/type_checker/v2_1/natives/post_conditions.rs +++ b/clarity/src/vm/analysis/type_checker/v2_1/natives/post_conditions.rs @@ -87,8 +87,10 @@ pub fn check_restrict_assets( let ok_type = last_return.ok_or_else(|| StaticCheckErrorKind::CheckerImplementationFailure)?; - TypeSignature::new_response(ok_type, TypeSignature::UIntType) - .map_err(StaticCheckError::from_clarity_type_error) + Ok(TypeSignature::new_response( + ok_type, + TypeSignature::UIntType, + )?) } pub fn check_as_contract( @@ -139,8 +141,10 @@ pub fn check_as_contract( } let ok_type = last_return.ok_or_else(|| StaticCheckErrorKind::CheckerImplementationFailure)?; - TypeSignature::new_response(ok_type, TypeSignature::UIntType) - .map_err(StaticCheckError::from_clarity_type_error) + Ok(TypeSignature::new_response( + ok_type, + TypeSignature::UIntType, + )?) } /// Type-checking for allowance expressions. These are only allowed within the diff --git a/clarity/src/vm/analysis/type_checker/v2_1/natives/sequences.rs b/clarity/src/vm/analysis/type_checker/v2_1/natives/sequences.rs index 6d31dbeb7b..b37ab0999e 100644 --- a/clarity/src/vm/analysis/type_checker/v2_1/natives/sequences.rs +++ b/clarity/src/vm/analysis/type_checker/v2_1/natives/sequences.rs @@ -283,40 +283,29 @@ pub fn check_special_concat( &StacksEpochId::Epoch21, lhs_entry_type, rhs_entry_type, - ) - .map_err(StaticCheckError::from_clarity_type_error)?; + )?; let new_len = lhs_max_len .checked_add(rhs_max_len) .ok_or(StaticCheckErrorKind::MaxLengthOverflow)?; - TypeSignature::list_of(list_entry_type, new_len) - .map_err(StaticCheckError::from_clarity_type_error)? + TypeSignature::list_of(list_entry_type, new_len)? } (BufferType(lhs_len), BufferType(rhs_len)) => { let size: u32 = u32::from(lhs_len) .checked_add(u32::from(rhs_len)) .ok_or(StaticCheckErrorKind::MaxLengthOverflow)?; - TypeSignature::SequenceType(BufferType( - size.try_into() - .map_err(StaticCheckError::from_clarity_type_error)?, - )) + TypeSignature::SequenceType(BufferType(size.try_into()?)) } (StringType(ASCII(lhs_len)), StringType(ASCII(rhs_len))) => { let size: u32 = u32::from(lhs_len) .checked_add(u32::from(rhs_len)) .ok_or(StaticCheckErrorKind::MaxLengthOverflow)?; - TypeSignature::SequenceType(StringType(ASCII( - size.try_into() - .map_err(StaticCheckError::from_clarity_type_error)?, - ))) + TypeSignature::SequenceType(StringType(ASCII(size.try_into()?))) } (StringType(UTF8(lhs_len)), StringType(UTF8(rhs_len))) => { let size: u32 = u32::from(lhs_len) .checked_add(u32::from(rhs_len)) .ok_or(StaticCheckErrorKind::MaxLengthOverflow)?; - TypeSignature::SequenceType(StringType(UTF8( - size.try_into() - .map_err(StaticCheckError::from_clarity_type_error)?, - ))) + TypeSignature::SequenceType(StringType(UTF8(size.try_into()?))) } (_, _) => { return Err(StaticCheckErrorKind::TypeError( @@ -349,14 +338,15 @@ pub fn check_special_append( analysis_typecheck_cost(checker, &lhs_entry_type, &rhs_type)?; - let list_entry_type = - TypeSignature::least_supertype(&StacksEpochId::Epoch21, &lhs_entry_type, &rhs_type) - .map_err(StaticCheckError::from_clarity_type_error)?; + let list_entry_type = TypeSignature::least_supertype( + &StacksEpochId::Epoch21, + &lhs_entry_type, + &rhs_type, + )?; let new_len = lhs_max_len .checked_add(1) .ok_or(StaticCheckErrorKind::MaxLengthOverflow)?; - let return_type = TypeSignature::list_of(list_entry_type, new_len) - .map_err(StaticCheckError::from_clarity_type_error)?; + let return_type = TypeSignature::list_of(list_entry_type, new_len)?; Ok(return_type) } _ => Err(StaticCheckErrorKind::ExpectedListApplication.into()), @@ -384,9 +374,7 @@ pub fn check_special_as_max_len( runtime_cost( ClarityCostFunction::AnalysisTypeAnnotate, checker, - TypeSignature::UIntType - .type_size() - .map_err(StaticCheckError::from_clarity_type_error)?, + TypeSignature::UIntType.type_size()?, )?; checker .type_map @@ -401,28 +389,22 @@ pub fn check_special_as_max_len( match sequence { TypeSignature::SequenceType(ListType(list)) => { let (lhs_entry_type, _) = list.destruct(); - let resized_list = ListTypeData::new_list(lhs_entry_type, expected_len) - .map_err(StaticCheckError::from_clarity_type_error)?; + let resized_list = ListTypeData::new_list(lhs_entry_type, expected_len)?; Ok(TypeSignature::OptionalType(Box::new( TypeSignature::SequenceType(ListType(resized_list)), ))) } TypeSignature::SequenceType(BufferType(_)) => Ok(TypeSignature::OptionalType(Box::new( - TypeSignature::SequenceType(BufferType( - BufferLength::try_from(expected_len) - .map_err(StaticCheckError::from_clarity_type_error)?, - )), + TypeSignature::SequenceType(BufferType(BufferLength::try_from(expected_len)?)), ))), TypeSignature::SequenceType(StringType(ASCII(_))) => Ok(TypeSignature::OptionalType( Box::new(TypeSignature::SequenceType(StringType(ASCII( - BufferLength::try_from(expected_len) - .map_err(StaticCheckError::from_clarity_type_error)?, + BufferLength::try_from(expected_len)?, )))), )), TypeSignature::SequenceType(StringType(UTF8(_))) => Ok(TypeSignature::OptionalType( Box::new(TypeSignature::SequenceType(StringType(UTF8( - StringUTF8Length::try_from(expected_len) - .map_err(StaticCheckError::from_clarity_type_error)?, + StringUTF8Length::try_from(expected_len)?, )))), )), _ => Err(StaticCheckErrorKind::ExpectedSequence(Box::new(sequence)).into()), @@ -464,7 +446,7 @@ pub fn check_special_element_at( match collection_type { TypeSignature::SequenceType(ListType(list)) => { let (entry_type, _) = list.destruct(); - TypeSignature::new_option(entry_type).map_err(StaticCheckError::from_clarity_type_error) + Ok(TypeSignature::new_option(entry_type)?) } TypeSignature::SequenceType(BufferType(_)) => Ok(TypeSignature::OptionalType(Box::new( TypeSignature::BUFFER_1, @@ -504,8 +486,7 @@ pub fn check_special_index_of( checker.type_check_expects(&args[1], context, &expected_input_type)?; - TypeSignature::new_option(TypeSignature::UIntType) - .map_err(StaticCheckError::from_clarity_type_error) + Ok(TypeSignature::new_option(TypeSignature::UIntType)?) } /// This function type checks the Clarity2 function `slice?`. @@ -521,8 +502,7 @@ pub fn check_special_slice( let seq_type = checker.type_check(&args[0], context)?; let seq = match seq_type { TypeSignature::SequenceType(seq) => { - TypeSignature::new_option(TypeSignature::SequenceType(seq)) - .map_err(StaticCheckError::from_clarity_type_error)? + TypeSignature::new_option(TypeSignature::SequenceType(seq))? } _ => return Err(StaticCheckErrorKind::ExpectedSequence(Box::new(seq_type)).into()), }; @@ -556,7 +536,6 @@ pub fn check_special_replace_at( // Check element argument checker.type_check_expects(&args[2], context, &unit_seq)?; - let final_type = - TypeSignature::new_option(input_type).map_err(StaticCheckError::from_clarity_type_error)?; + let final_type = TypeSignature::new_option(input_type)?; Ok(final_type) } diff --git a/clarity/src/vm/analysis/types.rs b/clarity/src/vm/analysis/types.rs index 5f6e341b50..58f08e8de9 100644 --- a/clarity/src/vm/analysis/types.rs +++ b/clarity/src/vm/analysis/types.rs @@ -249,11 +249,7 @@ impl ContractAnalysis { .into()); } - if !expected_sig - .returns - .admits_type(epoch, &func.returns) - .map_err(StaticCheckError::from_clarity_type_error)? - { + if !expected_sig.returns.admits_type(epoch, &func.returns)? { return Err(StaticCheckErrorKind::BadTraitImplementation( trait_name, func_name.to_string(), diff --git a/clarity/src/vm/callables.rs b/clarity/src/vm/callables.rs index d3f4775a2c..052c7d6dc0 100644 --- a/clarity/src/vm/callables.rs +++ b/clarity/src/vm/callables.rs @@ -161,21 +161,14 @@ impl DefinedFunction { if env.epoch().uses_arg_size_for_cost() { for arg in args.iter() { - runtime_cost( - ClarityCostFunction::InnerTypeCheckCost, - env, - arg.size() - .map_err(CheckErrorKind::from_clarity_type_error)?, - )?; + runtime_cost(ClarityCostFunction::InnerTypeCheckCost, env, arg.size()?)?; } } else { for arg_type in self.arg_types.iter() { runtime_cost( ClarityCostFunction::InnerTypeCheckCost, env, - arg_type - .size() - .map_err(CheckErrorKind::from_clarity_type_error)?, + arg_type.size()?, )?; } } @@ -252,10 +245,7 @@ impl DefinedFunction { ); } _ => { - if !type_sig - .admits(env.epoch(), value) - .map_err(CheckErrorKind::from_clarity_type_error)? - { + if !type_sig.admits(env.epoch(), value)? { return Err(CheckErrorKind::TypeValueError( Box::new(type_sig.clone()), Box::new(value.clone()), @@ -300,10 +290,7 @@ impl DefinedFunction { ); } _ => { - if !type_sig - .admits(env.epoch(), &cast_value) - .map_err(CheckErrorKind::from_clarity_type_error)? - { + if !type_sig.admits(env.epoch(), &cast_value)? { return Err(CheckErrorKind::TypeValueError( Box::new(type_sig.clone()), Box::new(cast_value), @@ -467,8 +454,7 @@ fn clarity2_implicit_cast( let cast_list_type_data = ListTypeData::new_list( list_type.get_list_item_type().clone(), type_signature.get_max_len(), - ) - .map_err(CheckErrorKind::from_clarity_type_error)?; + )?; Value::Sequence(SequenceData::List(ListData { data: values, type_signature: cast_list_type_data, diff --git a/clarity/src/vm/contexts.rs b/clarity/src/vm/contexts.rs index a0e7cc0948..b3cd19f7e3 100644 --- a/clarity/src/vm/contexts.rs +++ b/clarity/src/vm/contexts.rs @@ -1186,7 +1186,7 @@ impl<'a, 'b, 'hooks> Environment<'a, 'b, 'hooks> { .ok_or_else(|| VmInternalError::InvariantViolation(format!("Passed non-value expression to exec_tx on {tx_name}!")))?; // sanitize contract-call inputs in epochs >= 2.4 // testing todo: ensure sanitize_value() preserves trait callability! - let expected_type = TypeSignature::type_of(value).map_err(CheckErrorKind::from_clarity_type_error)?; + let expected_type = TypeSignature::type_of(value)?; let (sanitized_value, _) = Value::sanitize_value( self.epoch(), &expected_type, @@ -1882,8 +1882,7 @@ impl<'a, 'hooks> GlobalContext<'a, 'hooks> { Ok(result) } else { Err(CheckErrorKind::PublicFunctionMustReturnResponse(Box::new( - TypeSignature::type_of(&result) - .map_err(CheckErrorKind::from_clarity_type_error)?, + TypeSignature::type_of(&result)?, )) .into()) } diff --git a/clarity/src/vm/database/clarity_db.rs b/clarity/src/vm/database/clarity_db.rs index c2c6e34683..baab7df387 100644 --- a/clarity/src/vm/database/clarity_db.rs +++ b/clarity/src/vm/database/clarity_db.rs @@ -551,12 +551,9 @@ impl<'a> ClarityDatabase<'a> { .map_err(|e| VmInternalError::Expect(e.to_string()))? as u64; - let (sanitized_value, did_sanitize) = Value::sanitize_value( - epoch, - &TypeSignature::type_of(&value).map_err(CheckErrorKind::from_clarity_type_error)?, - value, - ) - .ok_or_else(|| CheckErrorKind::CouldNotDetermineType)?; + let (sanitized_value, did_sanitize) = + Value::sanitize_value(epoch, &TypeSignature::type_of(&value)?, value) + .ok_or_else(|| CheckErrorKind::CouldNotDetermineType)?; // if data needed to be sanitized *charge* for the unsanitized cost if did_sanitize { pre_sanitized_size = Some(value_size); @@ -1573,8 +1570,7 @@ impl ClarityDatabase<'_> { ) -> Result { if !variable_descriptor .value_type - .admits(&self.get_clarity_epoch_version()?, &value) - .map_err(CheckErrorKind::from_clarity_type_error)? + .admits(&self.get_clarity_epoch_version()?, &value)? { return Err(CheckErrorKind::TypeValueError( Box::new(variable_descriptor.value_type.clone()), @@ -1735,8 +1731,7 @@ impl ClarityDatabase<'_> { ) -> Result { if !map_descriptor .key_type - .admits(&self.get_clarity_epoch_version()?, key_value) - .map_err(CheckErrorKind::from_clarity_type_error)? + .admits(&self.get_clarity_epoch_version()?, key_value)? { return Err(CheckErrorKind::TypeValueError( Box::new(map_descriptor.key_type.clone()), @@ -1748,8 +1743,7 @@ impl ClarityDatabase<'_> { let key = ClarityDatabase::make_key_for_data_map_entry(contract_identifier, map_name, key_value)?; - let stored_type = TypeSignature::new_option(map_descriptor.value_type.clone()) - .map_err(CheckErrorKind::from_clarity_type_error)?; + let stored_type = TypeSignature::new_option(map_descriptor.value_type.clone())?; let result = self.get_value(&key, &stored_type, epoch)?; match result { @@ -1768,8 +1762,7 @@ impl ClarityDatabase<'_> { ) -> Result { if !map_descriptor .key_type - .admits(&self.get_clarity_epoch_version()?, key_value) - .map_err(CheckErrorKind::from_clarity_type_error)? + .admits(&self.get_clarity_epoch_version()?, key_value)? { return Err(CheckErrorKind::TypeValueError( Box::new(map_descriptor.key_type.clone()), @@ -1787,8 +1780,7 @@ impl ClarityDatabase<'_> { &key_serialized, ); - let stored_type = TypeSignature::new_option(map_descriptor.value_type.clone()) - .map_err(CheckErrorKind::from_clarity_type_error)?; + let stored_type = TypeSignature::new_option(map_descriptor.value_type.clone())?; let result = self.get_value(&key, &stored_type, epoch)?; match result { @@ -1915,8 +1907,7 @@ impl ClarityDatabase<'_> { ) -> Result { if !map_descriptor .key_type - .admits(&self.get_clarity_epoch_version()?, &key_value) - .map_err(CheckErrorKind::from_clarity_type_error)? + .admits(&self.get_clarity_epoch_version()?, &key_value)? { return Err(CheckErrorKind::TypeValueError( Box::new(map_descriptor.key_type.clone()), @@ -1926,8 +1917,7 @@ impl ClarityDatabase<'_> { } if !map_descriptor .value_type - .admits(&self.get_clarity_epoch_version()?, &value) - .map_err(CheckErrorKind::from_clarity_type_error)? + .admits(&self.get_clarity_epoch_version()?, &value)? { return Err(CheckErrorKind::TypeValueError( Box::new(map_descriptor.value_type.clone()), @@ -1946,8 +1936,7 @@ impl ClarityDatabase<'_> { map_name, &key_serialized, ); - let stored_type = TypeSignature::new_option(map_descriptor.value_type.clone()) - .map_err(CheckErrorKind::from_clarity_type_error)?; + let stored_type = TypeSignature::new_option(map_descriptor.value_type.clone())?; if return_if_exists && self.data_map_entry_exists(&key, &stored_type, epoch)? { return Ok(ValueResult { @@ -1956,7 +1945,7 @@ impl ClarityDatabase<'_> { }); } - let placed_value = Value::some(value).map_err(CheckErrorKind::from_clarity_type_error)?; + let placed_value = Value::some(value)?; let placed_size = self.put_value_with_size(&key, placed_value, epoch)?; Ok(ValueResult { @@ -1979,8 +1968,7 @@ impl ClarityDatabase<'_> { ) -> Result { if !map_descriptor .key_type - .admits(&self.get_clarity_epoch_version()?, key_value) - .map_err(CheckErrorKind::from_clarity_type_error)? + .admits(&self.get_clarity_epoch_version()?, key_value)? { return Err(CheckErrorKind::TypeValueError( Box::new(map_descriptor.key_type.clone()), @@ -1999,8 +1987,7 @@ impl ClarityDatabase<'_> { map_name, &key_serialized, ); - let stored_type = TypeSignature::new_option(map_descriptor.value_type.clone()) - .map_err(CheckErrorKind::from_clarity_type_error)?; + let stored_type = TypeSignature::new_option(map_descriptor.value_type.clone())?; if !self.data_map_entry_exists(&key, &stored_type, epoch)? { return Ok(ValueResult { value: Value::Bool(false), @@ -2206,10 +2193,7 @@ impl ClarityDatabase<'_> { asset: &Value, key_type: &TypeSignature, ) -> Result { - if !key_type - .admits(&self.get_clarity_epoch_version()?, asset) - .map_err(CheckErrorKind::from_clarity_type_error)? - { + if !key_type.admits(&self.get_clarity_epoch_version()?, asset)? { return Err(CheckErrorKind::TypeValueError( Box::new(key_type.clone()), Box::new(asset.clone()), @@ -2269,10 +2253,7 @@ impl ClarityDatabase<'_> { key_type: &TypeSignature, epoch: &StacksEpochId, ) -> Result<(), VmExecutionError> { - if !key_type - .admits(&self.get_clarity_epoch_version()?, asset) - .map_err(CheckErrorKind::from_clarity_type_error)? - { + if !key_type.admits(&self.get_clarity_epoch_version()?, asset)? { return Err(CheckErrorKind::TypeValueError( Box::new(key_type.clone()), Box::new(asset.clone()), @@ -2289,8 +2270,7 @@ impl ClarityDatabase<'_> { .map_err(|_| VmInternalError::Expect("IOError filling byte buffer.".into()))?, ); - let value = Value::some(Value::Principal(principal.clone())) - .map_err(CheckErrorKind::from_clarity_type_error)?; + let value = Value::some(Value::Principal(principal.clone()))?; self.put_value(&key, value, epoch)?; Ok(()) @@ -2304,10 +2284,7 @@ impl ClarityDatabase<'_> { key_type: &TypeSignature, epoch: &StacksEpochId, ) -> Result<(), VmExecutionError> { - if !key_type - .admits(&self.get_clarity_epoch_version()?, asset) - .map_err(CheckErrorKind::from_clarity_type_error)? - { + if !key_type.admits(&self.get_clarity_epoch_version()?, asset)? { return Err(CheckErrorKind::TypeValueError( Box::new(key_type.clone()), Box::new(asset.clone()), diff --git a/clarity/src/vm/functions/arithmetic.rs b/clarity/src/vm/functions/arithmetic.rs index 02e0612917..cf629a85d8 100644 --- a/clarity/src/vm/functions/arithmetic.rs +++ b/clarity/src/vm/functions/arithmetic.rs @@ -408,10 +408,7 @@ fn special_geq_v2( runtime_cost( ClarityCostFunction::Geq, env, - cmp::min( - a.size().map_err(CheckErrorKind::from_clarity_type_error)?, - b.size().map_err(CheckErrorKind::from_clarity_type_error)?, - ), + cmp::min(a.size()?, b.size()?), )?; type_force_binary_comparison_v2!(geq, a, b) } @@ -458,10 +455,7 @@ fn special_leq_v2( runtime_cost( ClarityCostFunction::Leq, env, - cmp::min( - a.size().map_err(CheckErrorKind::from_clarity_type_error)?, - b.size().map_err(CheckErrorKind::from_clarity_type_error)?, - ), + cmp::min(a.size()?, b.size()?), )?; type_force_binary_comparison_v2!(leq, a, b) } @@ -504,14 +498,7 @@ fn special_greater_v2( check_argument_count(2, args)?; let a = eval(&args[0], env, context)?; let b = eval(&args[1], env, context)?; - runtime_cost( - ClarityCostFunction::Ge, - env, - cmp::min( - a.size().map_err(CheckErrorKind::from_clarity_type_error)?, - b.size().map_err(CheckErrorKind::from_clarity_type_error)?, - ), - )?; + runtime_cost(ClarityCostFunction::Ge, env, cmp::min(a.size()?, b.size()?))?; type_force_binary_comparison_v2!(greater, a, b) } @@ -553,14 +540,7 @@ fn special_less_v2( check_argument_count(2, args)?; let a = eval(&args[0], env, context)?; let b = eval(&args[1], env, context)?; - runtime_cost( - ClarityCostFunction::Le, - env, - cmp::min( - a.size().map_err(CheckErrorKind::from_clarity_type_error)?, - b.size().map_err(CheckErrorKind::from_clarity_type_error)?, - ), - )?; + runtime_cost(ClarityCostFunction::Le, env, cmp::min(a.size()?, b.size()?))?; type_force_binary_comparison_v2!(less, a, b) } @@ -620,10 +600,7 @@ pub fn native_bitwise_left_shift(input: Value, pos: Value) -> Result Err(CheckErrorKind::UnionTypeError( vec![TypeSignature::IntType, TypeSignature::UIntType], - Box::new( - TypeSignature::type_of(&input) - .map_err(CheckErrorKind::from_clarity_type_error)?, - ), + Box::new(TypeSignature::type_of(&input)?), ) .into()), } @@ -649,10 +626,7 @@ pub fn native_bitwise_right_shift(input: Value, pos: Value) -> Result Err(CheckErrorKind::UnionTypeError( vec![TypeSignature::IntType, TypeSignature::UIntType], - Box::new( - TypeSignature::type_of(&input) - .map_err(CheckErrorKind::from_clarity_type_error)?, - ), + Box::new(TypeSignature::type_of(&input)?), ) .into()), } diff --git a/clarity/src/vm/functions/assets.rs b/clarity/src/vm/functions/assets.rs index 1fdd988fd4..f3f9344f0c 100644 --- a/clarity/src/vm/functions/assets.rs +++ b/clarity/src/vm/functions/assets.rs @@ -137,18 +137,8 @@ pub fn stx_transfer_consolidated( } // loading from/to principals and balances - env.add_memory( - TypeSignature::PrincipalType - .size() - .map_err(CheckErrorKind::from_clarity_type_error)? - .into(), - )?; - env.add_memory( - TypeSignature::PrincipalType - .size() - .map_err(CheckErrorKind::from_clarity_type_error)? - .into(), - )?; + env.add_memory(TypeSignature::PrincipalType.size()?.into())?; + env.add_memory(TypeSignature::PrincipalType.size()?.into())?; // loading from's locked amount and height // TODO: this does not count the inner stacks block header load, but arguably, // this could be optimized away, so it shouldn't penalize the caller. @@ -274,8 +264,7 @@ pub fn special_stx_account( ))), ), ]) - .map(Value::Tuple) - .map_err(CheckErrorKind::from_clarity_type_error)?) + .map(Value::Tuple)?) } pub fn special_stx_burn( @@ -299,12 +288,7 @@ pub fn special_stx_burn( return clarity_ecode!(StxErrorCodes::SENDER_IS_NOT_TX_SENDER); } - env.add_memory( - TypeSignature::PrincipalType - .size() - .map_err(CheckErrorKind::from_clarity_type_error)? - .into(), - )?; + env.add_memory(TypeSignature::PrincipalType.size()?.into())?; env.add_memory(STXBalance::unlocked_and_v1_size.try_into().map_err(|_| { CheckErrorKind::ExpectsRejectable( "BUG: STXBalance::unlocked_and_v1_size does not fit into a u64".into(), @@ -375,18 +359,8 @@ pub fn special_mint_token( .checked_add(amount) .ok_or_else(|| VmInternalError::Expect("STX overflow".into()))?; - env.add_memory( - TypeSignature::PrincipalType - .size() - .map_err(CheckErrorKind::from_clarity_type_error)? - .into(), - )?; - env.add_memory( - TypeSignature::UIntType - .size() - .map_err(CheckErrorKind::from_clarity_type_error)? - .into(), - )?; + env.add_memory(TypeSignature::PrincipalType.size()?.into())?; + env.add_memory(TypeSignature::UIntType.size()?.into())?; env.global_context.database.set_ft_balance( &env.contract_context.contract_identifier, @@ -429,15 +403,10 @@ pub fn special_mint_asset_v200( runtime_cost( ClarityCostFunction::NftMint, env, - expected_asset_type - .size() - .map_err(CheckErrorKind::from_clarity_type_error)?, + expected_asset_type.size()?, )?; - if !expected_asset_type - .admits(env.epoch(), &asset) - .map_err(CheckErrorKind::from_clarity_type_error)? - { + if !expected_asset_type.admits(env.epoch(), &asset)? { return Err(CheckErrorKind::TypeValueError( Box::new(expected_asset_type.clone()), Box::new(asset), @@ -457,18 +426,8 @@ pub fn special_mint_asset_v200( Err(e) => Err(e), }?; - env.add_memory( - TypeSignature::PrincipalType - .size() - .map_err(CheckErrorKind::from_clarity_type_error)? - .into(), - )?; - env.add_memory( - expected_asset_type - .size() - .map_err(CheckErrorKind::from_clarity_type_error)? - .into(), - )?; + env.add_memory(TypeSignature::PrincipalType.size()?.into())?; + env.add_memory(expected_asset_type.size()?.into())?; let epoch = *env.epoch(); env.global_context.database.set_nft_owner( @@ -521,10 +480,7 @@ pub fn special_mint_asset_v205( .map_err(|e| VmInternalError::Expect(e.to_string()))? as u64; runtime_cost(ClarityCostFunction::NftMint, env, asset_size)?; - if !expected_asset_type - .admits(env.epoch(), &asset) - .map_err(CheckErrorKind::from_clarity_type_error)? - { + if !expected_asset_type.admits(env.epoch(), &asset)? { return Err(CheckErrorKind::TypeValueError( Box::new(expected_asset_type.clone()), Box::new(asset), @@ -544,12 +500,7 @@ pub fn special_mint_asset_v205( Err(e) => Err(e), }?; - env.add_memory( - TypeSignature::PrincipalType - .size() - .map_err(CheckErrorKind::from_clarity_type_error)? - .into(), - )?; + env.add_memory(TypeSignature::PrincipalType.size()?.into())?; env.add_memory(asset_size)?; let epoch = *env.epoch(); @@ -600,15 +551,10 @@ pub fn special_transfer_asset_v200( runtime_cost( ClarityCostFunction::NftTransfer, env, - expected_asset_type - .size() - .map_err(CheckErrorKind::from_clarity_type_error)?, + expected_asset_type.size()?, )?; - if !expected_asset_type - .admits(env.epoch(), &asset) - .map_err(CheckErrorKind::from_clarity_type_error)? - { + if !expected_asset_type.admits(env.epoch(), &asset)? { return Err(CheckErrorKind::TypeValueError( Box::new(expected_asset_type.clone()), Box::new(asset), @@ -638,18 +584,8 @@ pub fn special_transfer_asset_v200( return clarity_ecode!(TransferAssetErrorCodes::NOT_OWNED_BY); } - env.add_memory( - TypeSignature::PrincipalType - .size() - .map_err(CheckErrorKind::from_clarity_type_error)? - .into(), - )?; - env.add_memory( - expected_asset_type - .size() - .map_err(CheckErrorKind::from_clarity_type_error)? - .into(), - )?; + env.add_memory(TypeSignature::PrincipalType.size()?.into())?; + env.add_memory(expected_asset_type.size()?.into())?; let epoch = *env.epoch(); env.global_context.database.set_nft_owner( @@ -712,10 +648,7 @@ pub fn special_transfer_asset_v205( .map_err(|e| VmInternalError::Expect(e.to_string()))? as u64; runtime_cost(ClarityCostFunction::NftTransfer, env, asset_size)?; - if !expected_asset_type - .admits(env.epoch(), &asset) - .map_err(CheckErrorKind::from_clarity_type_error)? - { + if !expected_asset_type.admits(env.epoch(), &asset)? { return Err(CheckErrorKind::TypeValueError( Box::new(expected_asset_type.clone()), Box::new(asset), @@ -745,12 +678,7 @@ pub fn special_transfer_asset_v205( return clarity_ecode!(TransferAssetErrorCodes::NOT_OWNED_BY); } - env.add_memory( - TypeSignature::PrincipalType - .size() - .map_err(CheckErrorKind::from_clarity_type_error)? - .into(), - )?; + env.add_memory(TypeSignature::PrincipalType.size()?.into())?; env.add_memory(asset_size)?; let epoch = *env.epoch(); @@ -848,30 +776,10 @@ pub fn special_transfer_token( .checked_add(amount) .ok_or(RuntimeError::ArithmeticOverflow)?; - env.add_memory( - TypeSignature::PrincipalType - .size() - .map_err(CheckErrorKind::from_clarity_type_error)? - .into(), - )?; - env.add_memory( - TypeSignature::PrincipalType - .size() - .map_err(CheckErrorKind::from_clarity_type_error)? - .into(), - )?; - env.add_memory( - TypeSignature::UIntType - .size() - .map_err(CheckErrorKind::from_clarity_type_error)? - .into(), - )?; - env.add_memory( - TypeSignature::UIntType - .size() - .map_err(CheckErrorKind::from_clarity_type_error)? - .into(), - )?; + env.add_memory(TypeSignature::PrincipalType.size()?.into())?; + env.add_memory(TypeSignature::PrincipalType.size()?.into())?; + env.add_memory(TypeSignature::UIntType.size()?.into())?; + env.add_memory(TypeSignature::UIntType.size()?.into())?; env.global_context.database.set_ft_balance( &env.contract_context.contract_identifier, @@ -966,15 +874,10 @@ pub fn special_get_owner_v200( runtime_cost( ClarityCostFunction::NftOwner, env, - expected_asset_type - .size() - .map_err(CheckErrorKind::from_clarity_type_error)?, + expected_asset_type.size()?, )?; - if !expected_asset_type - .admits(env.epoch(), &asset) - .map_err(CheckErrorKind::from_clarity_type_error)? - { + if !expected_asset_type.admits(env.epoch(), &asset)? { return Err(CheckErrorKind::TypeValueError( Box::new(expected_asset_type.clone()), Box::new(asset), @@ -1021,10 +924,7 @@ pub fn special_get_owner_v205( .map_err(|e| VmInternalError::Expect(e.to_string()))? as u64; runtime_cost(ClarityCostFunction::NftOwner, env, asset_size)?; - if !expected_asset_type - .admits(env.epoch(), &asset) - .map_err(CheckErrorKind::from_clarity_type_error)? - { + if !expected_asset_type.admits(env.epoch(), &asset)? { return Err(CheckErrorKind::TypeValueError( Box::new(expected_asset_type.clone()), Box::new(asset), @@ -1115,18 +1015,8 @@ pub fn special_burn_token( }; env.register_ft_burn_event(burner.clone(), amount, asset_identifier)?; - env.add_memory( - TypeSignature::PrincipalType - .size() - .map_err(CheckErrorKind::from_clarity_type_error)? - .into(), - )?; - env.add_memory( - TypeSignature::UIntType - .size() - .map_err(CheckErrorKind::from_clarity_type_error)? - .into(), - )?; + env.add_memory(TypeSignature::PrincipalType.size()?.into())?; + env.add_memory(TypeSignature::UIntType.size()?.into())?; env.global_context.log_token_transfer( burner, @@ -1165,15 +1055,10 @@ pub fn special_burn_asset_v200( runtime_cost( ClarityCostFunction::NftBurn, env, - expected_asset_type - .size() - .map_err(CheckErrorKind::from_clarity_type_error)?, + expected_asset_type.size()?, )?; - if !expected_asset_type - .admits(env.epoch(), &asset) - .map_err(CheckErrorKind::from_clarity_type_error)? - { + if !expected_asset_type.admits(env.epoch(), &asset)? { return Err(CheckErrorKind::TypeValueError( Box::new(expected_asset_type.clone()), Box::new(asset), @@ -1199,18 +1084,8 @@ pub fn special_burn_asset_v200( return clarity_ecode!(BurnAssetErrorCodes::NOT_OWNED_BY); } - env.add_memory( - TypeSignature::PrincipalType - .size() - .map_err(CheckErrorKind::from_clarity_type_error)? - .into(), - )?; - env.add_memory( - expected_asset_type - .size() - .map_err(CheckErrorKind::from_clarity_type_error)? - .into(), - )?; + env.add_memory(TypeSignature::PrincipalType.size()?.into())?; + env.add_memory(expected_asset_type.size()?.into())?; let epoch = *env.epoch(); env.global_context.database.burn_nft( @@ -1272,10 +1147,7 @@ pub fn special_burn_asset_v205( .map_err(|e| VmInternalError::Expect(e.to_string()))? as u64; runtime_cost(ClarityCostFunction::NftBurn, env, asset_size)?; - if !expected_asset_type - .admits(env.epoch(), &asset) - .map_err(CheckErrorKind::from_clarity_type_error)? - { + if !expected_asset_type.admits(env.epoch(), &asset)? { return Err(CheckErrorKind::TypeValueError( Box::new(expected_asset_type.clone()), Box::new(asset), @@ -1301,12 +1173,7 @@ pub fn special_burn_asset_v205( return clarity_ecode!(BurnAssetErrorCodes::NOT_OWNED_BY); } - env.add_memory( - TypeSignature::PrincipalType - .size() - .map_err(CheckErrorKind::from_clarity_type_error)? - .into(), - )?; + env.add_memory(TypeSignature::PrincipalType.size()?.into())?; env.add_memory(asset_size)?; let epoch = *env.epoch(); diff --git a/clarity/src/vm/functions/conversions.rs b/clarity/src/vm/functions/conversions.rs index d893aee55d..db109e77a8 100644 --- a/clarity/src/vm/functions/conversions.rs +++ b/clarity/src/vm/functions/conversions.rs @@ -51,9 +51,7 @@ pub fn buff_to_int_generic( ) -> Result { match value { Value::Sequence(SequenceData::Buffer(ref sequence_data)) => { - if sequence_data - .len() - .map_err(CheckErrorKind::from_clarity_type_error)? + if sequence_data.len()? > BufferLength::try_from(16_u32) .map_err(|_| VmInternalError::Expect("Failed to construct".into()))? { @@ -163,7 +161,7 @@ pub fn native_string_to_int_generic( fn safe_convert_string_to_int(raw_string: String) -> Result { let possible_int = raw_string.parse::(); match possible_int { - Ok(val) => Value::some(Value::Int(val)).map_err(CheckErrorKind::from_clarity_type_error), + Ok(val) => Ok(Value::some(Value::Int(val))?), Err(_error) => Ok(Value::none()), } } @@ -175,7 +173,7 @@ pub fn native_string_to_int(value: Value) -> Result { fn safe_convert_string_to_uint(raw_string: String) -> Result { let possible_int = raw_string.parse::(); match possible_int { - Ok(val) => Value::some(Value::UInt(val)).map_err(CheckErrorKind::from_clarity_type_error), + Ok(val) => Ok(Value::some(Value::UInt(val))?), Err(_error) => Ok(Value::none()), } } @@ -229,15 +227,13 @@ fn convert_string_to_ascii_ok(s: String) -> Result { let ascii_value = Value::string_ascii_from_bytes(s.into_bytes()).map_err(|_| { VmInternalError::Expect("Unexpected error converting string to ASCII".into()) })?; - Ok(Value::okay(ascii_value).map_err(CheckErrorKind::from_clarity_type_error)?) + Ok(Value::okay(ascii_value)?) } /// Helper function for UTF8 conversion that can return err u1 for non-ASCII characters fn convert_utf8_to_ascii(s: String) -> Result { match Value::string_ascii_from_bytes(s.into_bytes()) { - Ok(ascii_value) => { - Ok(Value::okay(ascii_value).map_err(CheckErrorKind::from_clarity_type_error)?) - } + Ok(ascii_value) => Ok(Value::okay(ascii_value)?), Err(_) => Ok(Value::err_uint(1)), // Non-ASCII characters in UTF8 } } @@ -251,13 +247,7 @@ pub fn special_to_ascii( let value = eval(&args[0], env, context)?; - runtime_cost( - ClarityCostFunction::ToAscii, - env, - value - .size() - .map_err(CheckErrorKind::from_clarity_type_error)?, - )?; + runtime_cost(ClarityCostFunction::ToAscii, env, value.size()?)?; match value { Value::Int(num) => convert_string_to_ascii_ok(num.to_string()), @@ -358,12 +348,9 @@ pub fn from_consensus_buff( } Err(_) => return Ok(Value::none()), }; - if !type_arg - .admits(env.epoch(), &result) - .map_err(CheckErrorKind::from_clarity_type_error)? - { + if !type_arg.admits(env.epoch(), &result)? { return Ok(Value::none()); } - Ok(Value::some(result).map_err(CheckErrorKind::from_clarity_type_error)?) + Ok(Value::some(result)?) } diff --git a/clarity/src/vm/functions/crypto.rs b/clarity/src/vm/functions/crypto.rs index 53e5d92d7c..499bed5ba3 100644 --- a/clarity/src/vm/functions/crypto.rs +++ b/clarity/src/vm/functions/crypto.rs @@ -46,8 +46,7 @@ macro_rules! native_hash_func { )), }?; let hash = <$module>::from_data(&bytes); - let value = Value::buff_from(hash.as_bytes().to_vec()) - .map_err(CheckErrorKind::from_clarity_type_error)?; + let value = Value::buff_from(hash.as_bytes().to_vec())?; Ok(value) } }; diff --git a/clarity/src/vm/functions/database.rs b/clarity/src/vm/functions/database.rs index 85ea17a99d..2d27491820 100644 --- a/clarity/src/vm/functions/database.rs +++ b/clarity/src/vm/functions/database.rs @@ -76,12 +76,7 @@ pub fn special_contract_call( let mut rest_args_sizes = Vec::with_capacity(rest_args_len); for arg in rest_args_slice.iter() { let evaluated_arg = eval(arg, env, context)?; - rest_args_sizes.push( - evaluated_arg - .size() - .map_err(CheckErrorKind::from_clarity_type_error)? - .into(), - ); + rest_args_sizes.push(evaluated_arg.size()?.into()); rest_args.push(SymbolicExpression::atom_value(evaluated_arg)); } @@ -211,20 +206,15 @@ pub fn special_contract_call( }?; // sanitize contract-call outputs in epochs >= 2.4 - let result_type = - TypeSignature::type_of(&result).map_err(CheckErrorKind::from_clarity_type_error)?; + let result_type = TypeSignature::type_of(&result)?; let (result, _) = Value::sanitize_value(env.epoch(), &result_type, result) .ok_or_else(|| CheckErrorKind::CouldNotDetermineType)?; // Ensure that the expected type from the trait spec admits // the type of the value returned by the dynamic dispatch. if let Some(returns_type_signature) = type_returns_constraint { - let actual_returns = - TypeSignature::type_of(&result).map_err(CheckErrorKind::from_clarity_type_error)?; - if !returns_type_signature - .admits_type(env.epoch(), &actual_returns) - .map_err(CheckErrorKind::from_clarity_type_error)? - { + let actual_returns = TypeSignature::type_of(&result)?; + if !returns_type_signature.admits_type(env.epoch(), &actual_returns)? { return Err(CheckErrorKind::ReturnTypesMustMatch( Box::new(returns_type_signature), Box::new(actual_returns), @@ -256,10 +246,7 @@ pub fn special_fetch_variable_v200( runtime_cost( ClarityCostFunction::FetchVar, env, - data_types - .value_type - .size() - .map_err(CheckErrorKind::from_clarity_type_error)?, + data_types.value_type.size()?, )?; let epoch = *env.epoch(); @@ -295,11 +282,7 @@ pub fn special_fetch_variable_v205( let result_size = match &result { Ok(data) => data.serialized_byte_len, - Err(_e) => data_types - .value_type - .size() - .map_err(CheckErrorKind::from_clarity_type_error)? - .into(), + Err(_e) => data_types.value_type.size()?.into(), }; runtime_cost(ClarityCostFunction::FetchVar, env, result_size)?; @@ -333,10 +316,7 @@ pub fn special_set_variable_v200( runtime_cost( ClarityCostFunction::SetVar, env, - data_types - .value_type - .size() - .map_err(CheckErrorKind::from_clarity_type_error)?, + data_types.value_type.size()?, )?; env.add_memory(value.get_memory_use()?)?; @@ -381,11 +361,7 @@ pub fn special_set_variable_v205( let result_size = match &result { Ok(data) => data.serialized_byte_len, - Err(_e) => data_types - .value_type - .size() - .map_err(CheckErrorKind::from_clarity_type_error)? - .into(), + Err(_e) => data_types.value_type.size()?.into(), }; runtime_cost(ClarityCostFunction::SetVar, env, result_size)?; @@ -417,14 +393,7 @@ pub fn special_fetch_entry_v200( runtime_cost( ClarityCostFunction::FetchEntry, env, - data_types - .value_type - .size() - .map_err(CheckErrorKind::from_clarity_type_error)? - + data_types - .key_type - .size() - .map_err(CheckErrorKind::from_clarity_type_error)?, + data_types.value_type.size()? + data_types.key_type.size()?, )?; let epoch = *env.epoch(); @@ -462,15 +431,7 @@ pub fn special_fetch_entry_v205( let result_size = match &result { Ok(data) => data.serialized_byte_len, - Err(_e) => (data_types - .value_type - .size() - .map_err(CheckErrorKind::from_clarity_type_error)? - + data_types - .key_type - .size() - .map_err(CheckErrorKind::from_clarity_type_error)?) - .into(), + Err(_e) => (data_types.value_type.size()? + data_types.key_type.size()?).into(), }; runtime_cost(ClarityCostFunction::FetchEntry, env, result_size)?; @@ -539,14 +500,7 @@ pub fn special_set_entry_v200( runtime_cost( ClarityCostFunction::SetEntry, env, - data_types - .value_type - .size() - .map_err(CheckErrorKind::from_clarity_type_error)? - + data_types - .key_type - .size() - .map_err(CheckErrorKind::from_clarity_type_error)?, + data_types.value_type.size()? + data_types.key_type.size()?, )?; env.add_memory(key.get_memory_use()?)?; @@ -594,15 +548,7 @@ pub fn special_set_entry_v205( let result_size = match &result { Ok(data) => data.serialized_byte_len, - Err(_e) => (data_types - .value_type - .size() - .map_err(CheckErrorKind::from_clarity_type_error)? - + data_types - .key_type - .size() - .map_err(CheckErrorKind::from_clarity_type_error)?) - .into(), + Err(_e) => (data_types.value_type.size()? + data_types.key_type.size()?).into(), }; runtime_cost(ClarityCostFunction::SetEntry, env, result_size)?; @@ -640,14 +586,7 @@ pub fn special_insert_entry_v200( runtime_cost( ClarityCostFunction::SetEntry, env, - data_types - .value_type - .size() - .map_err(CheckErrorKind::from_clarity_type_error)? - + data_types - .key_type - .size() - .map_err(CheckErrorKind::from_clarity_type_error)?, + data_types.value_type.size()? + data_types.key_type.size()?, )?; env.add_memory(key.get_memory_use()?)?; @@ -696,15 +635,7 @@ pub fn special_insert_entry_v205( let result_size = match &result { Ok(data) => data.serialized_byte_len, - Err(_e) => (data_types - .value_type - .size() - .map_err(CheckErrorKind::from_clarity_type_error)? - + data_types - .key_type - .size() - .map_err(CheckErrorKind::from_clarity_type_error)?) - .into(), + Err(_e) => (data_types.value_type.size()? + data_types.key_type.size()?).into(), }; runtime_cost(ClarityCostFunction::SetEntry, env, result_size)?; @@ -740,10 +671,7 @@ pub fn special_delete_entry_v200( runtime_cost( ClarityCostFunction::SetEntry, env, - data_types - .key_type - .size() - .map_err(CheckErrorKind::from_clarity_type_error)?, + data_types.key_type.size()?, )?; env.add_memory(key.get_memory_use()?)?; @@ -788,11 +716,7 @@ pub fn special_delete_entry_v205( let result_size = match &result { Ok(data) => data.serialized_byte_len, - Err(_e) => data_types - .key_type - .size() - .map_err(CheckErrorKind::from_clarity_type_error)? - .into(), + Err(_e) => data_types.key_type.size()?.into(), }; runtime_cost(ClarityCostFunction::SetEntry, env, result_size)?; @@ -952,15 +876,13 @@ pub fn special_get_block_info( // this is already an optional let block_reward_opt = env.global_context.database.get_block_reward(height_value)?; return Ok(match block_reward_opt { - Some(x) => { - Value::some(Value::UInt(x)).map_err(CheckErrorKind::from_clarity_type_error)? - } + Some(x) => Value::some(Value::UInt(x))?, None => Value::none(), }); } }; - Ok(Value::some(result).map_err(CheckErrorKind::from_clarity_type_error)?) + Ok(Value::some(result)?) } /// Handles the `get-burn-block-info?` special function. @@ -1019,12 +941,11 @@ pub fn special_get_burn_block_info( .get_burnchain_block_header_hash_for_burnchain_height(height_value)?; match burnchain_header_hash_opt { - Some(burnchain_header_hash) => { - Ok(Value::some(Value::Sequence(SequenceData::Buffer(BuffData { + Some(burnchain_header_hash) => Ok(Value::some(Value::Sequence( + SequenceData::Buffer(BuffData { data: burnchain_header_hash.as_bytes().to_vec(), - }))) - .map_err(CheckErrorKind::from_clarity_type_error)?) - } + }), + ))?), None => Ok(Value::none()), } } @@ -1139,7 +1060,7 @@ pub fn special_get_stacks_block_info( } }; - Ok(Value::some(result).map_err(CheckErrorKind::from_clarity_type_error)?) + Ok(Value::some(result)?) } /// Handles the function `get-tenure-info?` special function. @@ -1246,15 +1167,13 @@ pub fn special_get_tenure_info( // this is already an optional let block_reward_opt = env.global_context.database.get_block_reward(height_value)?; return Ok(match block_reward_opt { - Some(x) => { - Value::some(Value::UInt(x)).map_err(CheckErrorKind::from_clarity_type_error)? - } + Some(x) => Value::some(Value::UInt(x))?, None => Value::none(), }); } }; - Ok(Value::some(result).map_err(CheckErrorKind::from_clarity_type_error)?) + Ok(Value::some(result)?) } /// Handles the function `contract-hash?` @@ -1293,9 +1212,7 @@ pub fn special_contract_hash( return Ok(Value::err_uint(2)); }; - Ok(Value::okay( - Value::buff_from(contract_hash.as_bytes().to_vec()) - .map_err(CheckErrorKind::from_clarity_type_error)?, - ) - .map_err(CheckErrorKind::from_clarity_type_error)?) + Ok(Value::okay(Value::buff_from( + contract_hash.as_bytes().to_vec(), + )?)?) } diff --git a/clarity/src/vm/functions/mod.rs b/clarity/src/vm/functions/mod.rs index 49eab0ef25..71d405313e 100644 --- a/clarity/src/vm/functions/mod.rs +++ b/clarity/src/vm/functions/mod.rs @@ -609,15 +609,13 @@ fn native_eq(args: Vec, env: &mut Environment) -> Result special_match_resp(data, &args[1..], env, context), Value::Optional(data) => special_match_opt(data, &args[1..], env, context), - _ => Err(CheckErrorKind::BadMatchInput(Box::new( - TypeSignature::type_of(&input).map_err(CheckErrorKind::from_clarity_type_error)?, - )) - .into()), + _ => Err(CheckErrorKind::BadMatchInput(Box::new(TypeSignature::type_of(&input)?)).into()), } } pub fn native_some(input: Value) -> Result { - Ok(Value::some(input).map_err(CheckErrorKind::from_clarity_type_error)?) + Ok(Value::some(input)?) } fn is_some(input: Value) -> Result { @@ -262,11 +259,11 @@ pub fn native_is_err(input: Value) -> Result { } pub fn native_okay(input: Value) -> Result { - Ok(Value::okay(input).map_err(CheckErrorKind::from_clarity_type_error)?) + Ok(Value::okay(input)?) } pub fn native_error(input: Value) -> Result { - Ok(Value::error(input).map_err(CheckErrorKind::from_clarity_type_error)?) + Ok(Value::error(input)?) } pub fn native_default_to(default: Value, input: Value) -> Result { diff --git a/clarity/src/vm/functions/post_conditions.rs b/clarity/src/vm/functions/post_conditions.rs index e431f81d6d..823a25772e 100644 --- a/clarity/src/vm/functions/post_conditions.rs +++ b/clarity/src/vm/functions/post_conditions.rs @@ -282,8 +282,7 @@ pub fn special_restrict_assets( Ok(None) => {} Ok(Some(violation_index)) => { env.global_context.roll_back()?; - return Ok(Value::error(Value::UInt(violation_index)) - .map_err(CheckErrorKind::from_clarity_type_error)?); + return Ok(Value::error(Value::UInt(violation_index))?); } Err(e) => { env.global_context.roll_back()?; @@ -297,7 +296,7 @@ pub fn special_restrict_assets( match eval_result { Ok(Some(last)) => { // body completed successfully — commit and return ok(last) - Ok(Value::okay(last).map_err(CheckErrorKind::from_clarity_type_error)?) + Ok(Value::okay(last)?) } Ok(None) => { // Body had no expressions (shouldn't happen due to argument checks) @@ -377,7 +376,7 @@ pub fn special_as_contract( Ok(None) => {} Ok(Some(violation_index)) => { nested_env.global_context.roll_back()?; - return Ok(Value::error(Value::UInt(violation_index)).map_err(CheckErrorKind::from_clarity_type_error)?); + return Ok(Value::error(Value::UInt(violation_index))?); } Err(e) => { nested_env.global_context.roll_back()?; @@ -391,7 +390,7 @@ pub fn special_as_contract( match eval_result { Ok(Some(last)) => { // body completed successfully — commit and return ok(last) - Ok(Value::okay(last).map_err(CheckErrorKind::from_clarity_type_error)?) + Ok(Value::okay(last)?) } Ok(None) => { // Body had no expressions (shouldn't happen due to argument checks) diff --git a/clarity/src/vm/functions/principals.rs b/clarity/src/vm/functions/principals.rs index 252e3d5939..b708ca62e8 100644 --- a/clarity/src/vm/functions/principals.rs +++ b/clarity/src/vm/functions/principals.rs @@ -278,8 +278,7 @@ pub fn special_principal_construct( // Construct the principal. let mut transfer_buffer = [0u8; 20]; transfer_buffer.copy_from_slice(verified_hash_bytes); - let principal_data = StandardPrincipalData::new(version_byte, transfer_buffer) - .map_err(CheckErrorKind::from_clarity_type_error)?; + let principal_data = StandardPrincipalData::new(version_byte, transfer_buffer)?; let principal = if let Some(name) = name_opt { // requested a contract principal. Verify that the `name` is a valid ContractName. diff --git a/clarity/src/vm/functions/sequences.rs b/clarity/src/vm/functions/sequences.rs index ac72c7871a..66f0ed0eca 100644 --- a/clarity/src/vm/functions/sequences.rs +++ b/clarity/src/vm/functions/sequences.rs @@ -41,17 +41,12 @@ pub fn list_cons( let mut arg_size = 0; for a in args.iter() { - arg_size = arg_size.cost_overflow_add( - a.size() - .map_err(CheckErrorKind::from_clarity_type_error)? - .into(), - )?; + arg_size = arg_size.cost_overflow_add(a.size()?.into())?; } runtime_cost(ClarityCostFunction::ListCons, env, arg_size)?; - let value = - Value::cons_list(args, env.epoch()).map_err(CheckErrorKind::from_clarity_type_error)?; + let value = Value::cons_list(args, env.epoch())?; Ok(value) } @@ -85,11 +80,10 @@ pub fn special_filter( })?; } _ => { - return Err(CheckErrorKind::ExpectedSequence(Box::new( - TypeSignature::type_of(&sequence) - .map_err(CheckErrorKind::from_clarity_type_error)?, - )) - .into()) + return Err( + CheckErrorKind::ExpectedSequence(Box::new(TypeSignature::type_of(&sequence)?)) + .into(), + ) } }; Ok(sequence) @@ -112,8 +106,7 @@ pub fn special_fold( match sequence { Value::Sequence(ref mut sequence_data) => sequence_data - .atom_values() - .map_err(CheckErrorKind::from_clarity_type_error)? + .atom_values()? .into_iter() .try_fold(initial, |acc, x| { apply( @@ -123,10 +116,9 @@ pub fn special_fold( context, ) }), - _ => Err(CheckErrorKind::ExpectedSequence(Box::new( - TypeSignature::type_of(&sequence).map_err(CheckErrorKind::from_clarity_type_error)?, - )) - .into()), + _ => Err( + CheckErrorKind::ExpectedSequence(Box::new(TypeSignature::type_of(&sequence)?)).into(), + ), } } @@ -152,12 +144,7 @@ pub fn special_map( match sequence { Value::Sequence(ref mut sequence_data) => { min_args_len = min_args_len.min(sequence_data.len()); - for (apply_index, value) in sequence_data - .atom_values() - .map_err(CheckErrorKind::from_clarity_type_error)? - .into_iter() - .enumerate() - { + for (apply_index, value) in sequence_data.atom_values()?.into_iter().enumerate() { if apply_index > min_args_len { break; } @@ -169,11 +156,10 @@ pub fn special_map( } } _ => { - return Err(CheckErrorKind::ExpectedSequence(Box::new( - TypeSignature::type_of(&sequence) - .map_err(CheckErrorKind::from_clarity_type_error)?, - )) - .into()) + return Err( + CheckErrorKind::ExpectedSequence(Box::new(TypeSignature::type_of(&sequence)?)) + .into(), + ) } } } @@ -194,8 +180,7 @@ pub fn special_map( mapped_results.push(res); } - let value = Value::cons_list(mapped_results, env.epoch()) - .map_err(CheckErrorKind::from_clarity_type_error)?; + let value = Value::cons_list(mapped_results, env.epoch())?; Ok(value) } @@ -215,24 +200,15 @@ pub fn special_append( type_signature, } = list; let (entry_type, size) = type_signature.destruct(); - let element_type = TypeSignature::type_of(&element) - .map_err(CheckErrorKind::from_clarity_type_error)?; + let element_type = TypeSignature::type_of(&element)?; runtime_cost( ClarityCostFunction::Append, env, - u64::from(cmp::max( - entry_type - .size() - .map_err(CheckErrorKind::from_clarity_type_error)?, - element_type - .size() - .map_err(CheckErrorKind::from_clarity_type_error)?, - )), + u64::from(cmp::max(entry_type.size()?, element_type.size()?)), )?; if entry_type.is_no_type() { assert_eq!(size, 0); - return Ok(Value::cons_list(vec![element], env.epoch()) - .map_err(CheckErrorKind::from_clarity_type_error)?); + return Ok(Value::cons_list(vec![element], env.epoch())?); } if let Ok(next_entry_type) = TypeSignature::least_supertype(env.epoch(), &entry_type, &element_type) @@ -240,8 +216,7 @@ pub fn special_append( let (element, _) = Value::sanitize_value(env.epoch(), &next_entry_type, element) .ok_or_else(|| CheckErrorKind::ListTypesMustMatch)?; - let next_type_signature = ListTypeData::new_list(next_entry_type, size + 1) - .map_err(CheckErrorKind::from_clarity_type_error)?; + let next_type_signature = ListTypeData::new_list(next_entry_type, size + 1)?; data.push(element); Ok(Value::Sequence(SequenceData::List(ListData { type_signature: next_type_signature, @@ -270,29 +245,16 @@ pub fn special_concat_v200( runtime_cost( ClarityCostFunction::Concat, env, - u64::from( - wrapped_seq - .size() - .map_err(CheckErrorKind::from_clarity_type_error)?, - ) - .cost_overflow_add(u64::from( - other_wrapped_seq - .size() - .map_err(CheckErrorKind::from_clarity_type_error)?, - ))?, + u64::from(wrapped_seq.size()?).cost_overflow_add(u64::from(other_wrapped_seq.size()?))?, )?; match (&mut wrapped_seq, other_wrapped_seq) { - (Value::Sequence(ref mut seq), Value::Sequence(other_seq)) => seq - .concat(env.epoch(), other_seq) - .map_err(CheckErrorKind::from_clarity_type_error)?, + (Value::Sequence(ref mut seq), Value::Sequence(other_seq)) => { + seq.concat(env.epoch(), other_seq)? + } (Value::Sequence(ref mut seq_data), other_value) => { return Err(CheckErrorKind::TypeValueError( - Box::new( - seq_data - .type_signature() - .map_err(CheckErrorKind::from_clarity_type_error)?, - ), + Box::new(seq_data.type_signature()?), Box::new(other_value), ) .into()) @@ -321,17 +283,12 @@ pub fn special_concat_v205( (seq.len() as u64).cost_overflow_add(other_seq.len() as u64)?, )?; - seq.concat(env.epoch(), other_seq) - .map_err(CheckErrorKind::from_clarity_type_error)? + seq.concat(env.epoch(), other_seq)? } (Value::Sequence(ref mut seq_data), other_value) => { runtime_cost(ClarityCostFunction::Concat, env, 1)?; return Err(CheckErrorKind::TypeValueError( - Box::new( - seq_data - .type_signature() - .map_err(CheckErrorKind::from_clarity_type_error)?, - ), + Box::new(seq_data.type_signature()?), Box::new(other_value), ) .into()); @@ -360,11 +317,10 @@ pub fn special_as_max_len( let sequence_len = match sequence { Value::Sequence(ref sequence_data) => sequence_data.len() as u128, _ => { - return Err(CheckErrorKind::ExpectedSequence(Box::new( - TypeSignature::type_of(&sequence) - .map_err(CheckErrorKind::from_clarity_type_error)?, - )) - .into()) + return Err( + CheckErrorKind::ExpectedSequence(Box::new(TypeSignature::type_of(&sequence)?)) + .into(), + ) } }; if sequence_len > *expected_len { @@ -373,16 +329,13 @@ pub fn special_as_max_len( if let Value::Sequence(SequenceData::List(ref mut list)) = sequence { list.type_signature.reduce_max_len(*expected_len as u32); } - Ok(Value::some(sequence).map_err(CheckErrorKind::from_clarity_type_error)?) + Ok(Value::some(sequence)?) } } else { let actual_len = eval(&args[1], env, context)?; Err(CheckErrorKind::TypeError( Box::new(TypeSignature::UIntType), - Box::new( - TypeSignature::type_of(&actual_len) - .map_err(CheckErrorKind::from_clarity_type_error)?, - ), + Box::new(TypeSignature::type_of(&actual_len)?), ) .into()) } @@ -391,28 +344,20 @@ pub fn special_as_max_len( pub fn native_len(sequence: Value) -> Result { match sequence { Value::Sequence(sequence_data) => Ok(Value::UInt(sequence_data.len() as u128)), - _ => Err(CheckErrorKind::ExpectedSequence(Box::new( - TypeSignature::type_of(&sequence).map_err(CheckErrorKind::from_clarity_type_error)?, - )) - .into()), + _ => Err( + CheckErrorKind::ExpectedSequence(Box::new(TypeSignature::type_of(&sequence)?)).into(), + ), } } pub fn native_index_of(sequence: Value, to_find: Value) -> Result { if let Value::Sequence(sequence_data) = sequence { - match sequence_data - .contains(to_find) - .map_err(CheckErrorKind::from_clarity_type_error)? - { - Some(index) => Ok(Value::some(Value::UInt(index as u128)) - .map_err(CheckErrorKind::from_clarity_type_error)?), + match sequence_data.contains(to_find)? { + Some(index) => Ok(Value::some(Value::UInt(index as u128))?), None => Ok(Value::none()), } } else { - Err(CheckErrorKind::ExpectedSequence(Box::new( - TypeSignature::type_of(&sequence).map_err(CheckErrorKind::from_clarity_type_error)?, - )) - .into()) + Err(CheckErrorKind::ExpectedSequence(Box::new(TypeSignature::type_of(&sequence)?)).into()) } } @@ -420,10 +365,9 @@ pub fn native_element_at(sequence: Value, index: Value) -> Result Result { // TODO: REMOVE THIS COMMENT: Is it better to keep RuntimeError::BadTypeConstruction? It just seems weird @@ -541,16 +481,10 @@ pub fn special_replace_at( check_argument_count(3, args)?; let seq = eval(&args[0], env, context)?; - let seq_type = TypeSignature::type_of(&seq).map_err(CheckErrorKind::from_clarity_type_error)?; + let seq_type = TypeSignature::type_of(&seq)?; // runtime is the cost to copy over one element into its place - runtime_cost( - ClarityCostFunction::ReplaceAt, - env, - seq_type - .size() - .map_err(CheckErrorKind::from_clarity_type_error)?, - )?; + runtime_cost(ClarityCostFunction::ReplaceAt, env, seq_type.size()?)?; let expected_elem_type = if let TypeSignature::SequenceType(seq_subtype) = &seq_type { seq_subtype.unit_type() @@ -561,9 +495,7 @@ pub fn special_replace_at( let new_element = eval(&args[2], env, context)?; if expected_elem_type != TypeSignature::NoType - && !expected_elem_type - .admits(env.epoch(), &new_element) - .map_err(CheckErrorKind::from_clarity_type_error)? + && !expected_elem_type.admits(env.epoch(), &new_element)? { return Err(CheckErrorKind::TypeValueError( Box::new(expected_elem_type), @@ -593,7 +525,5 @@ pub fn special_replace_at( if index >= seq_len { return Ok(Value::none()); } - Ok(data - .replace_at(env.epoch(), index, new_element) - .map_err(CheckErrorKind::from_clarity_type_error)?) + Ok(data.replace_at(env.epoch(), index, new_element)?) } diff --git a/clarity/src/vm/functions/tuples.rs b/clarity/src/vm/functions/tuples.rs index 04dcf7a7ee..4a177424fc 100644 --- a/clarity/src/vm/functions/tuples.rs +++ b/clarity/src/vm/functions/tuples.rs @@ -37,9 +37,7 @@ pub fn tuple_cons( let bindings = parse_eval_bindings(args, SyntaxBindingErrorType::TupleCons, env, context)?; runtime_cost(ClarityCostFunction::TupleCons, env, bindings.len())?; - Ok(TupleData::from_data(bindings) - .map(Value::from) - .map_err(CheckErrorKind::from_clarity_type_error)?) + Ok(TupleData::from_data(bindings).map(Value::from)?) } pub fn tuple_get( @@ -61,22 +59,16 @@ pub fn tuple_get( Some(data) => { if let Value::Tuple(tuple_data) = *data { runtime_cost(ClarityCostFunction::TupleGet, env, tuple_data.len())?; - Ok(Value::some( - tuple_data - .get_owned(arg_name) - .map_err(CheckErrorKind::from_clarity_type_error)?, - ) - .map_err(|_| { + Ok(Value::some(tuple_data.get_owned(arg_name)?).map_err(|_| { VmInternalError::Expect( "Tuple contents should *always* fit in a some wrapper".into(), ) })?) } else { - Err(CheckErrorKind::ExpectedTuple(Box::new( - TypeSignature::type_of(&data) - .map_err(CheckErrorKind::from_clarity_type_error)?, - )) - .into()) + Err( + CheckErrorKind::ExpectedTuple(Box::new(TypeSignature::type_of(&data)?)) + .into(), + ) } } None => Ok(Value::none()), // just pass through none-types. @@ -84,14 +76,9 @@ pub fn tuple_get( } Value::Tuple(tuple_data) => { runtime_cost(ClarityCostFunction::TupleGet, env, tuple_data.len())?; - Ok(tuple_data - .get_owned(arg_name) - .map_err(CheckErrorKind::from_clarity_type_error)?) + Ok(tuple_data.get_owned(arg_name)?) } - _ => Err(CheckErrorKind::ExpectedTuple(Box::new( - TypeSignature::type_of(&value).map_err(CheckErrorKind::from_clarity_type_error)?, - )) - .into()), + _ => Err(CheckErrorKind::ExpectedTuple(Box::new(TypeSignature::type_of(&value)?)).into()), } } @@ -99,14 +86,14 @@ pub fn tuple_merge(base: Value, update: Value) -> Result Ok(initial_values), _ => Err(CheckErrorKind::ExpectedTuple(Box::new( - TypeSignature::type_of(&base).map_err(CheckErrorKind::from_clarity_type_error)?, + TypeSignature::type_of(&base)?, ))), }?; let new_values = match update { Value::Tuple(new_values) => Ok(new_values), _ => Err(CheckErrorKind::ExpectedTuple(Box::new( - TypeSignature::type_of(&update).map_err(CheckErrorKind::from_clarity_type_error)?, + TypeSignature::type_of(&update)?, ))), }?; diff --git a/clarity/src/vm/mod.rs b/clarity/src/vm/mod.rs index 2b02e6f53e..b749052ac9 100644 --- a/clarity/src/vm/mod.rs +++ b/clarity/src/vm/mod.rs @@ -179,28 +179,13 @@ fn lookup_variable( context.depth(), )?; if let Some(value) = context.lookup_variable(name) { - runtime_cost( - ClarityCostFunction::LookupVariableSize, - env, - value - .size() - .map_err(CheckErrorKind::from_clarity_type_error)?, - )?; + runtime_cost(ClarityCostFunction::LookupVariableSize, env, value.size()?)?; Ok(value.clone()) } else if let Some(value) = env.contract_context.lookup_variable(name).cloned() { - runtime_cost( - ClarityCostFunction::LookupVariableSize, - env, - value - .size() - .map_err(CheckErrorKind::from_clarity_type_error)?, - )?; - let (value, _) = Value::sanitize_value( - env.epoch(), - &TypeSignature::type_of(&value).map_err(CheckErrorKind::from_clarity_type_error)?, - value, - ) - .ok_or_else(|| CheckErrorKind::CouldNotDetermineType)?; + runtime_cost(ClarityCostFunction::LookupVariableSize, env, value.size()?)?; + let (value, _) = + Value::sanitize_value(env.epoch(), &TypeSignature::type_of(&value)?, value) + .ok_or_else(|| CheckErrorKind::CouldNotDetermineType)?; Ok(value) } else if let Some(callable_data) = context.lookup_callable_contract(name) { if env.contract_context.get_clarity_version() < &ClarityVersion::Clarity2 { @@ -432,13 +417,13 @@ pub fn eval_all( contract_context.functions.insert(name, value); }, DefineResult::PersistedVariable(name, value_type, value) => { - runtime_cost(ClarityCostFunction::CreateVar, global_context, value_type.size().map_err(CheckErrorKind::from_clarity_type_error)?)?; + runtime_cost(ClarityCostFunction::CreateVar, global_context, value_type.size()?)?; contract_context.persisted_names.insert(name.clone()); global_context.add_memory(value_type.type_size() .map_err(|_| VmInternalError::Expect("Type size should be realizable".into()))?.into())?; - global_context.add_memory(value.size().map_err(CheckErrorKind::from_clarity_type_error)?.into())?; + global_context.add_memory(value.size()?.into())?; let data_type = global_context.database.create_variable(&contract_context.contract_identifier, &name, value_type)?; global_context.database.set_variable(&contract_context.contract_identifier, &name, value, &data_type, &global_context.epoch_id)?; @@ -447,8 +432,8 @@ pub fn eval_all( }, DefineResult::Map(name, key_type, value_type) => { runtime_cost(ClarityCostFunction::CreateMap, global_context, - u64::from(key_type.size().map_err(CheckErrorKind::from_clarity_type_error)?).cost_overflow_add( - u64::from(value_type.size().map_err(CheckErrorKind::from_clarity_type_error)?))?)?; + u64::from(key_type.size()?).cost_overflow_add( + u64::from(value_type.size()?))?)?; contract_context.persisted_names.insert(name.clone()); global_context.add_memory(key_type.type_size() @@ -472,7 +457,7 @@ pub fn eval_all( contract_context.meta_ft.insert(name, data_type); }, DefineResult::NonFungibleAsset(name, asset_type) => { - runtime_cost(ClarityCostFunction::CreateNft, global_context, asset_type.size().map_err(CheckErrorKind::from_clarity_type_error)?)?; + runtime_cost(ClarityCostFunction::CreateNft, global_context, asset_type.size()?)?; contract_context.persisted_names.insert(name.clone()); global_context.add_memory(asset_type.type_size() diff --git a/clarity/src/vm/types/signatures.rs b/clarity/src/vm/types/signatures.rs index 504d836aed..3ba942537b 100644 --- a/clarity/src/vm/types/signatures.rs +++ b/clarity/src/vm/types/signatures.rs @@ -236,9 +236,7 @@ impl TypeSignatureExt for TypeSignature { let entry_type = TypeSignature::parse_type_repr(epoch, atomic_type_arg, accounting)?; let max_len = u32::try_from(*max_len).map_err(|_| CommonCheckErrorKind::ValueTooLarge)?; - Ok(ListTypeData::new_list(entry_type, max_len) - .map_err(CommonCheckErrorKind::from_clarity_type_error)? - .into()) + Ok(ListTypeData::new_list(entry_type, max_len)?.into()) } else { Err(CommonCheckErrorKind::InvalidTypeDescription) } @@ -257,8 +255,7 @@ impl TypeSignatureExt for TypeSignature { SyntaxBindingErrorType::TupleCons, accounting, )?; - let tuple_type_signature = TupleTypeSignature::try_from(mapped_key_types) - .map_err(CommonCheckErrorKind::from_clarity_type_error)?; + let tuple_type_signature = TupleTypeSignature::try_from(mapped_key_types)?; Ok(TypeSignature::from(tuple_type_signature)) } @@ -272,8 +269,7 @@ impl TypeSignatureExt for TypeSignature { } if let SymbolicExpressionType::LiteralValue(Value::Int(buff_len)) = &type_args[0].expr { Ok(SequenceType(SequenceSubtype::BufferType( - BufferLength::try_from(*buff_len) - .map_err(CommonCheckErrorKind::from_clarity_type_error)?, + BufferLength::try_from(*buff_len)?, ))) } else { Err(CommonCheckErrorKind::InvalidTypeDescription) @@ -290,10 +286,7 @@ impl TypeSignatureExt for TypeSignature { } if let SymbolicExpressionType::LiteralValue(Value::Int(utf8_len)) = &type_args[0].expr { Ok(SequenceType(SequenceSubtype::StringType( - StringSubtype::UTF8( - StringUTF8Length::try_from(*utf8_len) - .map_err(CommonCheckErrorKind::from_clarity_type_error)?, - ), + StringSubtype::UTF8(StringUTF8Length::try_from(*utf8_len)?), ))) } else { Err(CommonCheckErrorKind::InvalidTypeDescription) @@ -310,10 +303,7 @@ impl TypeSignatureExt for TypeSignature { } if let SymbolicExpressionType::LiteralValue(Value::Int(buff_len)) = &type_args[0].expr { Ok(SequenceType(SequenceSubtype::StringType( - StringSubtype::ASCII( - BufferLength::try_from(*buff_len) - .map_err(CommonCheckErrorKind::from_clarity_type_error)?, - ), + StringSubtype::ASCII(BufferLength::try_from(*buff_len)?), ))) } else { Err(CommonCheckErrorKind::InvalidTypeDescription) @@ -330,7 +320,7 @@ impl TypeSignatureExt for TypeSignature { } let inner_type = TypeSignature::parse_type_repr(epoch, &type_args[0], accounting)?; - TypeSignature::new_option(inner_type).map_err(CommonCheckErrorKind::from_clarity_type_error) + Ok(TypeSignature::new_option(inner_type)?) } fn parse_response_type_repr( @@ -343,8 +333,7 @@ impl TypeSignatureExt for TypeSignature { } let ok_type = TypeSignature::parse_type_repr(epoch, &type_args[0], accounting)?; let err_type = TypeSignature::parse_type_repr(epoch, &type_args[1], accounting)?; - let response_type = TypeSignature::new_response(ok_type, err_type) - .map_err(CommonCheckErrorKind::from_clarity_type_error)?; + let response_type = TypeSignature::new_response(ok_type, err_type)?; Ok(response_type) } @@ -517,17 +506,10 @@ impl TypeSignatureExt for TypeSignature { impl FixedFunction { pub fn total_type_size(&self) -> Result { - let mut function_type_size = u64::from( - self.returns - .type_size() - .map_err(StaticCheckErrorKind::from_clarity_type_error)?, - ); + let mut function_type_size = u64::from(self.returns.type_size()?); for arg in self.args.iter() { - function_type_size = function_type_size.cost_overflow_add(u64::from( - arg.signature - .type_size() - .map_err(StaticCheckErrorKind::from_clarity_type_error)?, - ))?; + function_type_size = + function_type_size.cost_overflow_add(u64::from(arg.signature.type_size()?))?; } Ok(function_type_size) } @@ -535,17 +517,10 @@ impl FixedFunction { impl FunctionSignature { pub fn total_type_size(&self) -> Result { - let mut function_type_size = u64::from( - self.returns - .type_size() - .map_err(StaticCheckErrorKind::from_clarity_type_error)?, - ); + let mut function_type_size = u64::from(self.returns.type_size()?); for arg in self.args.iter() { function_type_size = function_type_size - .cost_overflow_add(u64::from( - arg.type_size() - .map_err(StaticCheckErrorKind::from_clarity_type_error)?, - )) + .cost_overflow_add(u64::from(arg.type_size()?)) .map_err(|_| StaticCheckErrorKind::CostOverflow)?; } Ok(function_type_size) @@ -561,10 +536,7 @@ impl FunctionSignature { } let args_iter = self.args.iter().zip(args.iter()); for (expected_arg, arg) in args_iter { - if !arg - .admits_type(epoch, expected_arg) - .map_err(CommonCheckErrorKind::from_clarity_type_error)? - { + if !arg.admits_type(epoch, expected_arg)? { return Ok(false); } } diff --git a/stackslib/src/chainstate/stacks/mod.rs b/stackslib/src/chainstate/stacks/mod.rs index e4144e544b..3a19e8df0f 100644 --- a/stackslib/src/chainstate/stacks/mod.rs +++ b/stackslib/src/chainstate/stacks/mod.rs @@ -363,9 +363,7 @@ impl From for Error { /// if its a runtime issue, it would be really hitting VmExecutionError already impl From for Error { fn from(e: ClarityTypeError) -> Error { - Error::ClarityError(ClarityError::StaticCheck( - StaticCheckError::from_clarity_type_error(e), - )) + Error::ClarityError(ClarityError::StaticCheck(StaticCheckError::from(e))) } } diff --git a/stackslib/src/net/mod.rs b/stackslib/src/net/mod.rs index 97ff1ad477..ef366f95f9 100644 --- a/stackslib/src/net/mod.rs +++ b/stackslib/src/net/mod.rs @@ -538,9 +538,7 @@ impl From for Error { /// if its a runtime issue, it would be really hitting VmExecutionError already impl From for Error { fn from(e: ClarityTypeError) -> Self { - Error::ClarityError(ClarityError::StaticCheck( - StaticCheckError::from_clarity_type_error(e), - )) + Error::ClarityError(ClarityError::StaticCheck(StaticCheckError::from(e))) } }