diff --git a/.cargo/config.toml b/.cargo/config.toml index b1a93816cbe..d277b56a032 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,7 +1,7 @@ [alias] stacks-node = "run --package stacks-node --" fmt-stacks = "fmt -- --config group_imports=StdExternalCrate,imports_granularity=Module" -clippy-stacks = "clippy -p stx-genesis -p libstackerdb -p stacks-signer -p pox-locking -p clarity-serialization -p clarity -p libsigner -p stacks-common --no-deps --tests --all-features -- -D warnings" +clippy-stacks = "clippy -p stx-genesis -p libstackerdb -p stacks-signer -p pox-locking -p clarity-types -p clarity -p libsigner -p stacks-common --no-deps --tests --all-features -- -D warnings" clippy-stackslib = "clippy -p stackslib --no-deps -- -Aclippy::all -Wclippy::indexing_slicing -Wclippy::nonminimal_bool -Wclippy::clone_on_copy" # Uncomment to improve performance slightly, at the cost of portability diff --git a/.github/workflows/cargo-hack-check.yml b/.github/workflows/cargo-hack-check.yml index f833f94dc2f..31bfd4ac979 100644 --- a/.github/workflows/cargo-hack-check.yml +++ b/.github/workflows/cargo-hack-check.yml @@ -92,7 +92,7 @@ jobs: run: | cargo hack check \ -p clarity \ - -p clarity-serialization \ + -p clarity-types \ -p stacks-common \ --each-feature \ --no-dev-deps \ @@ -102,7 +102,7 @@ jobs: - name: Run cargo hack check (WASM Deterministic) run: | cargo hack check \ - -p clarity-serialization \ + -p clarity-types \ -p stacks-common \ --each-feature \ --no-dev-deps \ diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 5a038b92673..ded23c7c6e3 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -42,3 +42,7 @@ jobs: - name: Clippy id: clippy run: cargo clippy-stacks + + - name: Clippy Stackslib + id: clippy-stackslib + run: cargo clippy-stackslib diff --git a/CHANGELOG.md b/CHANGELOG.md index f065726d77e..da077c5051d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,12 +9,17 @@ and this project adheres to the versioning scheme outlined in the [README.md](RE ### Added +- Renamed `clarity-serialization` to `clarity-types`. - Add `stackerdb_timeout_secs` to miner config for limiting duration of StackerDB HTTP requests. - When determining a global transaction replay set, the state evaluator now uses a longest-common-prefix algorithm to find a replay set in the case where a single replay set has less than 70% of signer weight. -- New endpoint /v3/tenures/blocks/ allowing retrieving the list of stacks blocks from a burn block +- New endpoints /v3/tenures/blocks/, /v3/tenures/blocks/hash, /v3/tenures/blocks/height allowing retrieving the list of stacks blocks from a burn block - Creates epoch 3.3 and costs-4 in preparation for a hardfork to activate Clarity 4 - Adds support for new Clarity 4 builtins (not activated until epoch 3.3): - `contract-hash?` + - `current-contract` + - `block-time` + - `to-ascii?` +- Added `contract_cost_limit_percentage` to the miner config file — sets the percentage of a block’s execution cost at which, if a large non-boot contract call would cause a BlockTooBigError, the miner will stop adding further non-boot contract calls and only include STX transfers and boot contract calls for the remainder of the block. ### Changed @@ -22,6 +27,11 @@ and this project adheres to the versioning scheme outlined in the [README.md](RE expressive (#6337) - Removed affirmation maps logic throughout, upgrading chainstate DB schema to 11 and burnchain DB schema to 3 (#6314) +### Fixed + +- When running `stacks-inspect decode-tx`, print the correct version of the address (mainnet or testnet) based on the transaction passed in +- When a contract deploy is analyzed, it will no longer throw a `CostError` when the contract contains an undefined top-level variable. Instead, it will throw a `UndefinedVariable` error. + ## [3.2.0.0.1] ### Added diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 86291f170c5..613e8c973ee 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -56,7 +56,7 @@ For an example of this process, see PRs test, module, function, etc.), each should be documented according to our [coding guidelines](#Coding-Guidelines). -> [*] The Changelog focuses on product changes. A "major change" refers to updates that have a direct impact on the end user, such as introducing new features, modifying existing functionality, or optimizing runtime performance. +> [*] The Changelog focuses on product changes. A "major change" refers to updates that have a direct impact on the end user, such as introducing new features, modifying existing functionality, or optimizing runtime performance. On the other hand, changes that do not need to be reflected in the Changelog include code refactoring, writing tests, or automating processes, as these do not directly affect the user experience. ## Git Commit Messages @@ -374,10 +374,10 @@ A test should be marked `#[ignore]` if: - **Integration tests need to be properly tagged** using [pinny-rs](https://github.com/BitcoinL2-Labs/pinny-rs/) crate. Tagging requires two fundamental steps: 1. Define allowed tags in the package `Cargo.toml` file (if needed). 2. Apply relevant tags to the tests, picking from the allowed set. - + Then it will be possible to run tests with filtering based on the tags using `cargo test` and `cargo nextest` runner. > For more information and examples on how tagging works, refer to the [pinny-rs](https://github.com/BitcoinL2-Labs/pinny-rs/) readme. - + Below the tag set currently defined with related purpose: | Tag | Description | @@ -406,16 +406,26 @@ cargo fmt-stacks ## Clippy Warnings -PRs will be checked against `clippy` and will _fail_ if any clippy warnings are generated. -Unfortunately, not all existing clippy warnings have been addressed throughout stacks-core, so arguments must be passed via the command line. -Therefore, we handle `clippy` configurations using a Cargo alias: `cargo clippy-stacks` +All PRs are checked with `clippy`, and the CI will **fail** if any warnings are raised. +Because not all existing clippy warnings in `stacks-core` have been addressed, we use Cargo aliases to standardize how clippy is run across different parts of the codebase. + +Two commands are available: -You can check what warnings need to be addressed locally via: +- `cargo clippy-stacks` + Runs clippy across the core packages of the repository (e.g. `stx-genesis`, `clarity`, `stacks-signer`, etc.). + +- `cargo clippy-stackslib` + Runs clippy specifically on the `stackslib` package, with a different configuration. + +To check warnings locally, run: ```bash cargo clippy-stacks +cargo clippy-stackslib ``` +Make sure both commands pass before opening a pull request. + ## Comments Comments are very important for the readability and correctness of the codebase. The purpose of comments is: @@ -491,7 +501,7 @@ impl<'a, 'b> ReadOnlyChecker<'a, 'b> { /// - Returns CheckErrors::WriteAttemptedInReadOnly if there is a read-only /// violation, i.e. if some function marked read-only attempts to modify /// the chainstate. - pub fn run(&mut self, contract_analysis: &ContractAnalysis) -> CheckResult<()> + pub fn run(&mut self, contract_analysis: &ContractAnalysis) -> Result<(), CheckError> ``` This comment is considered positive because it explains the contract of the function in pseudo-code. Someone who understands the constructs mentioned could, e.g., write a test for this method from this description. diff --git a/Cargo.lock b/Cargo.lock index 52c8b791082..6c012a98d8d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -407,6 +407,12 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "base64ct" version = "1.6.0" @@ -578,7 +584,7 @@ name = "clarity" version = "0.0.1" dependencies = [ "assert-json-diff 1.1.0", - "clarity-serialization", + "clarity-types", "integer-sqrt", "lazy_static", "mutants", @@ -619,7 +625,7 @@ dependencies = [ ] [[package]] -name = "clarity-serialization" +name = "clarity-types" version = "0.0.1" dependencies = [ "lazy_static", @@ -1668,14 +1674,15 @@ dependencies = [ [[package]] name = "libsecp256k1" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" +checksum = "e79019718125edc905a079a70cfa5f3820bc76139fc91d6f9abc27ea2a887139" dependencies = [ "arrayref", - "base64 0.13.1", + "base64 0.22.1", "digest 0.9.0", "hmac-drbg", + "lazy_static", "libsecp256k1-core", "libsecp256k1-gen-ecmult", "libsecp256k1-gen-genmult", diff --git a/Cargo.toml b/Cargo.toml index 9a10754af75..9d68c25f29a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ members = [ "stackslib", "stacks-common", "pox-locking", - "clarity-serialization", + "clarity-types", "clarity", "stx-genesis", "libstackerdb", diff --git a/clarity-serialization/Cargo.toml b/clarity-types/Cargo.toml similarity index 77% rename from clarity-serialization/Cargo.toml rename to clarity-types/Cargo.toml index f7518e3a18c..6e2b8287f1b 100644 --- a/clarity-serialization/Cargo.toml +++ b/clarity-types/Cargo.toml @@ -1,8 +1,8 @@ [package] -name = "clarity-serialization" +name = "clarity-types" version = "0.0.1" edition = "2024" -description = "Serialization and deserialization for Stacks Clarity smart contract language types." +description = "Canonical Rust representations for Clarity's core data types, error definitions, and serialization format." license = "GPLv3" homepage = "https://github.com/stacks-network/stacks-core" repository = "https://github.com/stacks-network/stacks-core" @@ -14,7 +14,7 @@ lazy_static = { workspace = true } regex = { version = "1", default-features = false } rusqlite = { workspace = true, optional = true } serde = { workspace = true } -serde_json = { version = "1.0", default-features = false } +serde_json = { version = "1.0", default-features = false, optional = true } serde_derive = { workspace = true } slog = { workspace = true } stacks_common = { package = "stacks-common", path = "../stacks-common", default-features = false } @@ -28,7 +28,7 @@ default = [] testing = [] developer-mode = ["stacks_common/developer-mode"] slog_json = ["stacks_common/slog_json"] -rusqlite = ["stacks_common/rusqlite", "dep:rusqlite"] +rusqlite = ["stacks_common/rusqlite", "dep:rusqlite", "dep:serde_json"] # Wasm-specific features for easier configuration wasm-web = ["stacks_common/wasm-web"] diff --git a/clarity-serialization/README.md b/clarity-types/README.md similarity index 94% rename from clarity-serialization/README.md rename to clarity-types/README.md index 46e25648eec..7c423b58ff6 100644 --- a/clarity-serialization/README.md +++ b/clarity-types/README.md @@ -1,8 +1,8 @@ -# Clarity Serialization (`clarity-serialization`) +# Clarity Types (`clarity-types`) [![License: GPLv3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) -A Rust crate for representing, serializing, and deserializing data types from the Stacks Clarity smart contract language. +A Rust crate for representing all core data types, errors, and serializable structures of the Stacks Clarity smart contract language. ## Overview @@ -13,6 +13,7 @@ This crate provides the core components for working with Clarity data structures * **Canonical Data Structures**: Rust representations for all Clarity types, including `int`, `uint`, `bool`, `principal`, `optional`, `response`, `tuple`, `list`, `buffer`, and strings. * **Consensus-Compatible Binary Codec**: Implements the binary serialization and deserialization format required by the Stacks blockchain. * **Type Safety**: Includes type-checking logic (`admits`, `least_supertype`) for validating values against type signatures. +* **Canonical Errors**: The definitive enums for all static analysis, runtime, and internal errors that can occur during Clarity execution. ## Quick Start: Usage Examples @@ -21,7 +22,7 @@ This crate provides the core components for working with Clarity data structures This example demonstrates how to construct a complex Clarity `(tuple)` and serialize it to its hexadecimal string representation, which is suitable for use as a transaction argument. ```rust -use clarity_serialization::types::{PrincipalData, TupleData, Value}; +use clarity_types::types::{PrincipalData, TupleData, Value}; fn main() -> Result<(), Box> { // 1. Construct the individual values that will go into our tuple. @@ -64,7 +65,7 @@ fn main() -> Result<(), Box> { This example shows the reverse process: taking a hex string and deserializing it into a structured `Value` object, while validating it against an expected type. ```rust -use clarity_serialization::types::{TypeSignature, Value}; +use clarity_types::types::{TypeSignature, Value}; fn main() -> Result<(), Box> { let hex_string = "0c000000030269640100000000000000000000000000000065086d657461646174610a0200000004deadbeef056f776e65720514a46ff88886c2ef9762d970b4d2c63678835bd39d"; diff --git a/clarity-serialization/src/diagnostic.rs b/clarity-types/src/diagnostic.rs similarity index 100% rename from clarity-serialization/src/diagnostic.rs rename to clarity-types/src/diagnostic.rs diff --git a/clarity-serialization/src/errors/analysis.rs b/clarity-types/src/errors/analysis.rs similarity index 92% rename from clarity-serialization/src/errors/analysis.rs rename to clarity-types/src/errors/analysis.rs index 061fb538a6f..09e4e5f056d 100644 --- a/clarity-serialization/src/errors/analysis.rs +++ b/clarity-types/src/errors/analysis.rs @@ -21,8 +21,6 @@ use crate::execution_cost::ExecutionCost; use crate::representations::SymbolicExpression; use crate::types::{TraitIdentifier, TupleTypeSignature, TypeSignature, Value}; -pub type CheckResult = Result; - /// What kind of syntax binding was found to be in error? #[derive(Debug, PartialEq, Clone, Copy)] pub enum SyntaxBindingErrorType { @@ -164,50 +162,44 @@ pub enum CheckErrors { // match errors BadMatchOptionSyntax(Box), BadMatchResponseSyntax(Box), - BadMatchInput(TypeSignature), + BadMatchInput(Box), // list typing errors - UnknownListConstructionFailure, ListTypesMustMatch, ConstructedListTooLarge, // simple type expectation mismatch - TypeError(TypeSignature, TypeSignature), - TypeLiteralError(TypeSignature, TypeSignature), - TypeValueError(TypeSignature, Value), + TypeError(Box, Box), + TypeValueError(Box, Box), - NoSuperType(TypeSignature, TypeSignature), InvalidTypeDescription, UnknownTypeName(String), // union type mismatch - UnionTypeError(Vec, TypeSignature), - UnionTypeValueError(Vec, Value), - - ExpectedLiteral, - ExpectedOptionalType(TypeSignature), - ExpectedResponseType(TypeSignature), - ExpectedOptionalOrResponseType(TypeSignature), - ExpectedOptionalValue(Value), - ExpectedResponseValue(Value), - ExpectedOptionalOrResponseValue(Value), + UnionTypeError(Vec, Box), + UnionTypeValueError(Vec, Box), + + ExpectedOptionalType(Box), + ExpectedResponseType(Box), + ExpectedOptionalOrResponseType(Box), + ExpectedOptionalValue(Box), + ExpectedResponseValue(Box), + ExpectedOptionalOrResponseValue(Box), CouldNotDetermineResponseOkType, CouldNotDetermineResponseErrType, CouldNotDetermineSerializationType, UncheckedIntermediaryResponses, - ExpectedContractPrincipalValue(Value), + ExpectedContractPrincipalValue(Box), CouldNotDetermineMatchTypes, CouldNotDetermineType, // Checker runtime failures TypeAlreadyAnnotatedFailure, - TypeAnnotationExpectedFailure, CheckerImplementationFailure, // Assets BadTokenName, - DefineFTBadSignature, DefineNFTBadSignature, NoSuchNFT(String), NoSuchFT(String), @@ -220,7 +212,7 @@ pub enum CheckErrors { // tuples BadTupleFieldName, - ExpectedTuple(TypeSignature), + ExpectedTuple(Box), NoSuchTupleField(String, TupleTypeSignature), EmptyTuplesNotAllowed, BadTupleConstruction(String), @@ -236,9 +228,9 @@ pub enum CheckErrors { DefineFunctionBadSignature, BadFunctionName, BadMapTypeDefinition, - PublicFunctionMustReturnResponse(TypeSignature), + PublicFunctionMustReturnResponse(Box), DefineVariableBadSignature, - ReturnTypesMustMatch(TypeSignature, TypeSignature), + ReturnTypesMustMatch(Box, Box), CircularReference(Vec), @@ -248,7 +240,7 @@ pub enum CheckErrors { PublicFunctionNotReadOnly(String, String), ContractAlreadyExists(String), ContractCallExpectName, - ExpectedCallableType(TypeSignature), + ExpectedCallableType(Box), // get-block-info? errors NoSuchBlockInfoProperty(String), @@ -266,7 +258,7 @@ pub enum CheckErrors { // expect a function, or applying a function to a list NonFunctionApplication, ExpectedListApplication, - ExpectedSequence(TypeSignature), + ExpectedSequence(Box), MaxLengthOverflow, // let syntax @@ -283,10 +275,9 @@ pub enum CheckErrors { RequiresAtLeastArguments(usize, usize), RequiresAtMostArguments(usize, usize), IncorrectArgumentCount(usize, usize), - IfArmsMustMatch(TypeSignature, TypeSignature), - MatchArmsMustMatch(TypeSignature, TypeSignature), - DefaultTypesMustMatch(TypeSignature, TypeSignature), - TooManyExpressions, + IfArmsMustMatch(Box, Box), + MatchArmsMustMatch(Box, Box), + DefaultTypesMustMatch(Box, Box), IllegalOrUnknownFunctionApplication(String), UnknownFunction(String), @@ -295,7 +286,6 @@ pub enum CheckErrors { TraitReferenceUnknown(String), TraitMethodUnknown(String, String), ExpectedTraitIdentifier, - ImportTraitBadSignature, TraitReferenceNotAllowed, BadTraitImplementation(String, String), DefineTraitBadSignature, @@ -303,7 +293,7 @@ pub enum CheckErrors { UnexpectedTraitOrFieldReference, TraitBasedContractCallInReadOnly, ContractOfExpectsTrait, - IncompatibleTrait(TraitIdentifier, TraitIdentifier), + IncompatibleTrait(Box, Box), // strings InvalidCharactersDetected, @@ -321,7 +311,7 @@ pub enum CheckErrors { #[derive(Debug, PartialEq)] pub struct CheckError { - pub err: CheckErrors, + pub err: Box, pub expressions: Option>, pub diagnostic: Diagnostic, } @@ -341,7 +331,7 @@ impl CheckError { pub fn new(err: CheckErrors) -> CheckError { let diagnostic = Diagnostic::err(&err); CheckError { - err, + err: Box::new(err), expressions: None, diagnostic, } @@ -456,7 +446,6 @@ impl From for String { } } -#[allow(clippy::result_large_err)] pub fn check_argument_count(expected: usize, args: &[T]) -> Result<(), CheckErrors> { if args.len() != expected { Err(CheckErrors::IncorrectArgumentCount(expected, args.len())) @@ -465,7 +454,6 @@ pub fn check_argument_count(expected: usize, args: &[T]) -> Result<(), CheckE } } -#[allow(clippy::result_large_err)] pub fn check_arguments_at_least(expected: usize, args: &[T]) -> Result<(), CheckErrors> { if args.len() < expected { Err(CheckErrors::RequiresAtLeastArguments(expected, args.len())) @@ -474,7 +462,6 @@ pub fn check_arguments_at_least(expected: usize, args: &[T]) -> Result<(), Ch } } -#[allow(clippy::result_large_err)] pub fn check_arguments_at_most(expected: usize, args: &[T]) -> Result<(), CheckErrors> { if args.len() > expected { Err(CheckErrors::RequiresAtMostArguments(expected, args.len())) @@ -501,7 +488,6 @@ fn formatted_expected_types(expected_types: &[TypeSignature]) -> String { impl DiagnosableError for CheckErrors { fn message(&self) -> String { match &self { - CheckErrors::ExpectedLiteral => "expected a literal argument".into(), CheckErrors::SupertypeTooLarge => "supertype of two types is too large".into(), CheckErrors::Expects(s) => format!("unexpected interpreter behavior: {s}"), CheckErrors::BadMatchOptionSyntax(source) => @@ -512,7 +498,6 @@ impl DiagnosableError for CheckErrors { source.message()), CheckErrors::BadMatchInput(t) => format!("match requires an input of either a response or optional, found input: '{t}'"), - CheckErrors::TypeAnnotationExpectedFailure => "analysis expected type to already be annotated for expression".into(), CheckErrors::CostOverflow => "contract execution cost overflowed cost counter".into(), CheckErrors::CostBalanceExceeded(a, b) => format!("contract execution cost exceeded budget: {a:?} > {b:?}"), CheckErrors::MemoryBalanceExceeded(a, b) => format!("contract execution cost exceeded memory budget: {a:?} > {b:?}"), @@ -523,12 +508,9 @@ impl DiagnosableError for CheckErrors { CheckErrors::ValueOutOfBounds => "created a type which value size was out of defined bounds".into(), CheckErrors::TypeSignatureTooDeep => "created a type which was deeper than maximum allowed type depth".into(), CheckErrors::ExpectedName => "expected a name argument to this function".into(), - CheckErrors::NoSuperType(a, b) => format!("unable to create a supertype for the two types: '{a}' and '{b}'"), - CheckErrors::UnknownListConstructionFailure => "invalid syntax for list definition".into(), CheckErrors::ListTypesMustMatch => "expecting elements of same type in a list".into(), CheckErrors::ConstructedListTooLarge => "reached limit of elements in a sequence".into(), CheckErrors::TypeError(expected_type, found_type) => format!("expecting expression of type '{expected_type}', found '{found_type}'"), - CheckErrors::TypeLiteralError(expected_type, found_type) => format!("expecting a literal of type '{expected_type}', found '{found_type}'"), CheckErrors::TypeValueError(expected_type, found_value) => format!("expecting expression of type '{expected_type}', found '{found_value}'"), CheckErrors::UnionTypeError(expected_types, found_type) => format!("expecting expression of type {}, found '{}'", formatted_expected_types(expected_types), found_type), CheckErrors::UnionTypeValueError(expected_types, found_type) => format!("expecting expression of type {}, found '{}'", formatted_expected_types(expected_types), found_type), @@ -593,21 +575,18 @@ impl DiagnosableError for CheckErrors { CheckErrors::IfArmsMustMatch(type_1, type_2) => format!("expression types returned by the arms of 'if' must match (got '{type_1}' and '{type_2}')"), CheckErrors::MatchArmsMustMatch(type_1, type_2) => format!("expression types returned by the arms of 'match' must match (got '{type_1}' and '{type_2}')"), CheckErrors::DefaultTypesMustMatch(type_1, type_2) => format!("expression types passed in 'default-to' must match (got '{type_1}' and '{type_2}')"), - CheckErrors::TooManyExpressions => "reached limit of expressions".into(), CheckErrors::IllegalOrUnknownFunctionApplication(function_name) => format!("use of illegal / unresolved function '{function_name}"), CheckErrors::UnknownFunction(function_name) => format!("use of unresolved function '{function_name}'"), CheckErrors::TraitBasedContractCallInReadOnly => "use of trait based contract calls are not allowed in read-only context".into(), CheckErrors::WriteAttemptedInReadOnly => "expecting read-only statements, detected a writing operation".into(), CheckErrors::AtBlockClosureMustBeReadOnly => "(at-block ...) closures expect read-only statements, but detected a writing operation".into(), CheckErrors::BadTokenName => "expecting an token name as an argument".into(), - CheckErrors::DefineFTBadSignature => "(define-token ...) expects a token name as an argument".into(), CheckErrors::DefineNFTBadSignature => "(define-asset ...) expects an asset name and an asset identifier type signature as arguments".into(), CheckErrors::NoSuchNFT(asset_name) => format!("tried to use asset function with a undefined asset ('{asset_name}')"), CheckErrors::NoSuchFT(asset_name) => format!("tried to use token function with a undefined token ('{asset_name}')"), CheckErrors::NoSuchTrait(contract_name, trait_name) => format!("use of unresolved trait {contract_name}.{trait_name}"), CheckErrors::TraitReferenceUnknown(trait_name) => format!("use of undeclared trait <{trait_name}>"), CheckErrors::TraitMethodUnknown(trait_name, func_name) => format!("method '{func_name}' unspecified in trait <{trait_name}>"), - CheckErrors::ImportTraitBadSignature => "(use-trait ...) expects a trait name and a trait identifier".into(), CheckErrors::BadTraitImplementation(trait_name, func_name) => format!("invalid signature for method '{func_name}' regarding trait's specification <{trait_name}>"), CheckErrors::ExpectedTraitIdentifier => "expecting expression of type trait identifier".into(), CheckErrors::UnexpectedTraitOrFieldReference => "unexpected use of trait reference or field".into(), diff --git a/clarity-serialization/src/errors/ast.rs b/clarity-types/src/errors/ast.rs similarity index 97% rename from clarity-serialization/src/errors/ast.rs rename to clarity-types/src/errors/ast.rs index 35ee8fca2d8..a18e6242aa5 100644 --- a/clarity-serialization/src/errors/ast.rs +++ b/clarity-types/src/errors/ast.rs @@ -26,21 +26,31 @@ pub type ParseResult = Result; #[derive(Debug, PartialEq)] pub enum ParseErrors { + // Cost errors CostOverflow, CostBalanceExceeded(ExecutionCost, ExecutionCost), MemoryBalanceExceeded(u64, u64), + CostComputationFailed(String), + ExecutionTimeExpired, + TooManyExpressions, ExpressionStackDepthTooDeep, VaryExpressionStackDepthTooDeep, + FailedParsingIntValue(String), + CircularReference(Vec), + NameAlreadyUsed(String), + TraitReferenceNotAllowed, + ImportTraitBadSignature, + DefineTraitBadSignature, + ImplTraitBadSignature, + TraitReferenceUnknown(String), + + // V1 errors FailedCapturingInput, SeparatorExpected(String), SeparatorExpectedAfterColon(String), ProgramTooLarge, IllegalVariableName(String), - IllegalContractName(String), - UnknownQuotedValue(String), - FailedParsingIntValue(String), - FailedParsingUIntValue(String), FailedParsingBuffer(String), FailedParsingHexValue(String, String), FailedParsingPrincipal(String), @@ -50,54 +60,45 @@ pub enum ParseErrors { ClosingParenthesisExpected, ClosingTupleLiteralUnexpected, ClosingTupleLiteralExpected, - CircularReference(Vec), TupleColonExpected(usize), TupleCommaExpected(usize), TupleItemExpected(usize), - NameAlreadyUsed(String), - TraitReferenceNotAllowed, - ImportTraitBadSignature, - DefineTraitBadSignature, - ImplTraitBadSignature, - TraitReferenceUnknown(String), CommaSeparatorUnexpected, ColonSeparatorUnexpected, InvalidCharactersDetected, InvalidEscaping, - CostComputationFailed(String), // V2 Errors Lexer(LexerError), ContractNameTooLong(String), + ExpectedClosing(Token), ExpectedContractIdentifier, ExpectedTraitIdentifier, + ExpectedWhitespace, + FailedParsingUIntValue(String), IllegalTraitName(String), InvalidPrincipalLiteral, InvalidBuffer, NameTooLong(String), UnexpectedToken(Token), - ExpectedClosing(Token), TupleColonExpectedv2, TupleCommaExpectedv2, TupleValueExpected, IllegalClarityName(String), IllegalASCIIString(String), - IllegalUtf8String(String), - ExpectedWhitespace, + IllegalContractName(String), // Notes NoteToMatchThis(Token), - /// Should be an unreachable error UnexpectedParserFailure, + /// Should be an unreachable failure which invalidates the transaction InterpreterFailure, - - ExecutionTimeExpired, } #[derive(Debug, PartialEq)] pub struct ParseError { - pub err: ParseErrors, + pub err: Box, pub pre_expressions: Option>, pub diagnostic: Diagnostic, } @@ -106,14 +107,14 @@ impl ParseError { pub fn new(err: ParseErrors) -> ParseError { let diagnostic = Diagnostic::err(&err); ParseError { - err, + err: Box::new(err), pre_expressions: None, diagnostic, } } pub fn rejectable(&self) -> bool { - matches!(self.err, ParseErrors::InterpreterFailure) + matches!(*self.err, ParseErrors::InterpreterFailure) } pub fn has_pre_expression(&self) -> bool { @@ -204,7 +205,6 @@ impl DiagnosableError for ParseErrors { ParseErrors::IllegalVariableName(var_name) => { format!("Illegal variable name: '{var_name}'") } - ParseErrors::UnknownQuotedValue(value) => format!("Unknown 'quoted value '{value}'"), ParseErrors::FailedParsingIntValue(value) => { format!("Failed to parse int literal '{value}'") } @@ -291,7 +291,6 @@ impl DiagnosableError for ParseErrors { ParseErrors::TupleValueExpected => "expected value expression for tuple".into(), ParseErrors::IllegalClarityName(name) => format!("illegal clarity name, '{name}'"), ParseErrors::IllegalASCIIString(s) => format!("illegal ascii string \"{s}\""), - ParseErrors::IllegalUtf8String(s) => format!("illegal UTF8 string \"{s}\""), ParseErrors::ExpectedWhitespace => "expected whitespace before expression".into(), ParseErrors::NoteToMatchThis(token) => format!("to match this '{token}'"), ParseErrors::UnexpectedParserFailure => "unexpected failure while parsing".to_string(), diff --git a/clarity-serialization/src/errors/cost.rs b/clarity-types/src/errors/cost.rs similarity index 100% rename from clarity-serialization/src/errors/cost.rs rename to clarity-types/src/errors/cost.rs diff --git a/clarity-serialization/src/errors/lexer.rs b/clarity-types/src/errors/lexer.rs similarity index 100% rename from clarity-serialization/src/errors/lexer.rs rename to clarity-types/src/errors/lexer.rs diff --git a/clarity-serialization/src/errors/mod.rs b/clarity-types/src/errors/mod.rs similarity index 88% rename from clarity-serialization/src/errors/mod.rs rename to clarity-types/src/errors/mod.rs index 67baf1bcb3e..58b42b8a99d 100644 --- a/clarity-serialization/src/errors/mod.rs +++ b/clarity-types/src/errors/mod.rs @@ -20,13 +20,12 @@ pub mod lexer; use std::{error, fmt}; -pub use analysis::{CheckError, CheckErrors, CheckResult}; +pub use analysis::{CheckError, CheckErrors}; pub use ast::{ParseError, ParseErrors, ParseResult}; pub use cost::CostErrors; pub use lexer::LexerError; #[cfg(feature = "rusqlite")] use rusqlite::Error as SqliteError; -use serde_json::Error as SerdeJSONErr; use stacks_common::types::chainstate::BlockHeaderHash; use crate::representations::SymbolicExpression; @@ -54,10 +53,8 @@ pub enum Error { /// Test executions may trigger these errors. #[derive(Debug, PartialEq)] pub enum InterpreterError { - BadSender(Value), BadSymbolicRepresentation(String), InterpreterError(String), - UninitializedPersistedVariable, FailedToConstructAssetTable, FailedToConstructEventBatch, #[cfg(feature = "rusqlite")] @@ -68,7 +65,6 @@ pub enum InterpreterError { FailureConstructingTupleWithType, FailureConstructingListWithType, InsufficientBalance, - CostContractLoadFailure, DBError(String), Expect(String), } @@ -86,34 +82,31 @@ pub enum RuntimeErrorType { // error in parsing types ParseError(String), // error in parsing the AST - ASTError(ParseError), + ASTError(Box), MaxStackDepthReached, MaxContextDepthReached, - ListDimensionTooHigh, BadTypeConstruction, - ValueTooLarge, BadBlockHeight(String), - TransferNonPositiveAmount, NoSuchToken, NotImplemented, NoCallerInContext, NoSenderInContext, - NonPositiveTokenSupply, - JSONParseError(IncomparableError), - AttemptToFetchInTransientContext, BadNameValue(&'static str, String), UnknownBlockHeaderHash(BlockHeaderHash), BadBlockHash(Vec), UnwrapFailure, + MetadataAlreadySet, + // pox-locking errors DefunctPoxContract, PoxAlreadyLocked, - MetadataAlreadySet, + + BlockTimeNotAvailable, } #[derive(Debug, PartialEq)] pub enum ShortReturnType { - ExpectedValue(Value), - AssertionFailed(Value), + ExpectedValue(Box), + AssertionFailed(Box), } pub type InterpreterResult = Result; @@ -174,11 +167,11 @@ impl error::Error for RuntimeErrorType { impl From for Error { fn from(err: ParseError) -> Self { - match &err.err { + match *err.err { ParseErrors::InterpreterFailure => Error::from(InterpreterError::Expect( "Unexpected interpreter failure during parsing".into(), )), - _ => Error::from(RuntimeErrorType::ASTError(err)), + _ => Error::from(RuntimeErrorType::ASTError(Box::new(err))), } } } @@ -235,8 +228,8 @@ impl From for () { impl From for Value { fn from(val: ShortReturnType) -> Self { match val { - ShortReturnType::ExpectedValue(v) => v, - ShortReturnType::AssertionFailed(v) => v, + ShortReturnType::ExpectedValue(v) => *v, + ShortReturnType::AssertionFailed(v) => *v, } } } @@ -248,15 +241,15 @@ mod test { #[test] fn equality() { assert_eq!( - Error::ShortReturn(ShortReturnType::ExpectedValue(Value::Bool(true))), - Error::ShortReturn(ShortReturnType::ExpectedValue(Value::Bool(true))) + Error::ShortReturn(ShortReturnType::ExpectedValue(Box::new(Value::Bool(true)))), + Error::ShortReturn(ShortReturnType::ExpectedValue(Box::new(Value::Bool(true)))) ); assert_eq!( Error::Interpreter(InterpreterError::InterpreterError("".to_string())), Error::Interpreter(InterpreterError::InterpreterError("".to_string())) ); assert!( - Error::ShortReturn(ShortReturnType::ExpectedValue(Value::Bool(true))) + Error::ShortReturn(ShortReturnType::ExpectedValue(Box::new(Value::Bool(true)))) != Error::Interpreter(InterpreterError::InterpreterError("".to_string())) ); } diff --git a/clarity-serialization/src/execution_cost.rs b/clarity-types/src/execution_cost.rs similarity index 100% rename from clarity-serialization/src/execution_cost.rs rename to clarity-types/src/execution_cost.rs diff --git a/clarity-serialization/src/lib.rs b/clarity-types/src/lib.rs similarity index 100% rename from clarity-serialization/src/lib.rs rename to clarity-types/src/lib.rs diff --git a/clarity-serialization/src/representations.rs b/clarity-types/src/representations.rs similarity index 100% rename from clarity-serialization/src/representations.rs rename to clarity-types/src/representations.rs diff --git a/clarity-serialization/src/tests/mod.rs b/clarity-types/src/tests/mod.rs similarity index 100% rename from clarity-serialization/src/tests/mod.rs rename to clarity-types/src/tests/mod.rs diff --git a/clarity-serialization/src/tests/representations.rs b/clarity-types/src/tests/representations.rs similarity index 100% rename from clarity-serialization/src/tests/representations.rs rename to clarity-types/src/tests/representations.rs diff --git a/clarity-serialization/src/tests/types/mod.rs b/clarity-types/src/tests/types/mod.rs similarity index 100% rename from clarity-serialization/src/tests/types/mod.rs rename to clarity-types/src/tests/types/mod.rs diff --git a/clarity-serialization/src/tests/types/serialization.rs b/clarity-types/src/tests/types/serialization.rs similarity index 100% rename from clarity-serialization/src/tests/types/serialization.rs rename to clarity-types/src/tests/types/serialization.rs diff --git a/clarity-serialization/src/tests/types/signatures.rs b/clarity-types/src/tests/types/signatures.rs similarity index 100% rename from clarity-serialization/src/tests/types/signatures.rs rename to clarity-types/src/tests/types/signatures.rs diff --git a/clarity-serialization/src/token.rs b/clarity-types/src/token.rs similarity index 100% rename from clarity-serialization/src/token.rs rename to clarity-types/src/token.rs diff --git a/clarity-serialization/src/types/mod.rs b/clarity-types/src/types/mod.rs similarity index 98% rename from clarity-serialization/src/types/mod.rs rename to clarity-types/src/types/mod.rs index cc892a517ac..c28dc0c2753 100644 --- a/clarity-serialization/src/types/mod.rs +++ b/clarity-types/src/types/mod.rs @@ -12,7 +12,6 @@ // // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#![allow(clippy::result_large_err)] pub mod serialization; pub mod signatures; @@ -455,7 +454,11 @@ impl SequenceData { Ok(None) } } else { - Err(CheckErrors::TypeValueError(TypeSignature::min_buffer()?, to_find).into()) + Err(CheckErrors::TypeValueError( + Box::new(TypeSignature::min_buffer()?), + Box::new(to_find), + ) + .into()) } } SequenceData::List(data) => { @@ -480,10 +483,11 @@ impl SequenceData { Ok(None) } } else { - Err( - CheckErrors::TypeValueError(TypeSignature::min_string_ascii()?, to_find) - .into(), + Err(CheckErrors::TypeValueError( + Box::new(TypeSignature::min_string_ascii()?), + Box::new(to_find), ) + .into()) } } SequenceData::String(CharType::UTF8(data)) => { @@ -500,10 +504,11 @@ impl SequenceData { Ok(None) } } else { - Err( - CheckErrors::TypeValueError(TypeSignature::min_string_utf8()?, to_find) - .into(), + Err(CheckErrors::TypeValueError( + Box::new(TypeSignature::min_string_utf8()?), + Box::new(to_find), ) + .into()) } } } diff --git a/clarity-serialization/src/types/serialization.rs b/clarity-types/src/types/serialization.rs similarity index 100% rename from clarity-serialization/src/types/serialization.rs rename to clarity-types/src/types/serialization.rs diff --git a/clarity-serialization/src/types/signatures.rs b/clarity-types/src/types/signatures.rs similarity index 87% rename from clarity-serialization/src/types/signatures.rs rename to clarity-types/src/types/signatures.rs index 14db6760451..a8e49b92f9d 100644 --- a/clarity-serialization/src/types/signatures.rs +++ b/clarity-types/src/types/signatures.rs @@ -31,8 +31,6 @@ use crate::types::{ WRAPPER_VALUE_SIZE, }; -type Result = std::result::Result; - #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Serialize, Deserialize, Hash)] pub struct AssetIdentifier { pub contract_identifier: QualifiedContractIdentifier, @@ -152,7 +150,7 @@ pub enum SequenceSubtype { } impl SequenceSubtype { - pub fn unit_type(&self) -> Result { + pub fn unit_type(&self) -> Result { match &self { SequenceSubtype::ListType(list_data) => Ok(list_data.clone().destruct().0), SequenceSubtype::BufferType(_) => TypeSignature::min_buffer(), @@ -185,6 +183,14 @@ use self::TypeSignature::{ ResponseType, SequenceType, TraitReferenceType, TupleType, UIntType, }; +/// Maximum string length returned from `to-ascii?`. +/// 5 bytes reserved for embedding in response. +const MAX_TO_ASCII_RESULT_LEN: u32 = MAX_VALUE_SIZE - 5; + +/// Maximum buffer length returned from `to-ascii?`. +/// 2 bytes reserved for "0x" prefix and 2 characters per byte. +pub const MAX_TO_ASCII_BUFFER_LEN: u32 = (MAX_TO_ASCII_RESULT_LEN - 2) / 2; + lazy_static! { pub static ref BUFF_64: TypeSignature = { #[allow(clippy::expect_used)] @@ -234,6 +240,22 @@ lazy_static! { BufferLength::try_from(16u32).expect("BUG: Legal Clarity buffer length marked invalid"), )) }; + /// Maximum-sized buffer allowed for `to-ascii?` call. + pub static ref TO_ASCII_MAX_BUFF: TypeSignature = { + #[allow(clippy::expect_used)] + SequenceType(SequenceSubtype::BufferType( + BufferLength::try_from(MAX_TO_ASCII_BUFFER_LEN) + .expect("BUG: Legal Clarity buffer length marked invalid"), + )) + }; + /// Maximum-length string returned from `to-ascii?` + pub static ref TO_ASCII_RESPONSE_STRING: TypeSignature = { + #[allow(clippy::expect_used)] + SequenceType(SequenceSubtype::StringType( + StringSubtype::ASCII(BufferLength::try_from(MAX_TO_ASCII_RESULT_LEN) + .expect("BUG: Legal Clarity buffer length marked invalid")), + )) + }; } pub const ASCII_40: TypeSignature = SequenceType(SequenceSubtype::StringType( @@ -275,7 +297,7 @@ impl From for u32 { impl TryFrom for BufferLength { type Error = CheckErrors; - fn try_from(data: u32) -> Result { + fn try_from(data: u32) -> Result { if data > MAX_VALUE_SIZE { Err(CheckErrors::ValueTooLarge) } else { @@ -286,7 +308,7 @@ impl TryFrom for BufferLength { impl TryFrom for BufferLength { type Error = CheckErrors; - fn try_from(data: usize) -> Result { + fn try_from(data: usize) -> Result { if data > (MAX_VALUE_SIZE as usize) { Err(CheckErrors::ValueTooLarge) } else { @@ -297,7 +319,7 @@ impl TryFrom for BufferLength { impl TryFrom for BufferLength { type Error = CheckErrors; - fn try_from(data: i128) -> Result { + fn try_from(data: i128) -> Result { if data > (MAX_VALUE_SIZE as i128) { Err(CheckErrors::ValueTooLarge) } else if data < 0 { @@ -322,7 +344,7 @@ impl From for u32 { impl TryFrom for StringUTF8Length { type Error = CheckErrors; - fn try_from(data: u32) -> Result { + fn try_from(data: u32) -> Result { let len = data .checked_mul(4) .ok_or_else(|| CheckErrors::ValueTooLarge)?; @@ -336,7 +358,7 @@ impl TryFrom for StringUTF8Length { impl TryFrom for StringUTF8Length { type Error = CheckErrors; - fn try_from(data: usize) -> Result { + fn try_from(data: usize) -> Result { let len = data .checked_mul(4) .ok_or_else(|| CheckErrors::ValueTooLarge)?; @@ -350,7 +372,7 @@ impl TryFrom for StringUTF8Length { impl TryFrom for StringUTF8Length { type Error = CheckErrors; - fn try_from(data: i128) -> Result { + fn try_from(data: i128) -> Result { let len = data .checked_mul(4) .ok_or_else(|| CheckErrors::ValueTooLarge)?; @@ -365,7 +387,7 @@ impl TryFrom for StringUTF8Length { } impl ListTypeData { - pub fn new_list(entry_type: TypeSignature, max_len: u32) -> Result { + pub fn new_list(entry_type: TypeSignature, max_len: u32) -> Result { let would_be_depth = 1 + entry_type.depth(); if would_be_depth > MAX_TYPE_DEPTH { return Err(CheckErrors::TypeSignatureTooDeep); @@ -407,7 +429,7 @@ 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 { @@ -419,7 +441,10 @@ impl TypeSignature { } } - pub fn new_response(ok_type: TypeSignature, err_type: TypeSignature) -> Result { + pub fn new_response( + ok_type: TypeSignature, + err_type: TypeSignature, + ) -> 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()); @@ -432,14 +457,14 @@ impl TypeSignature { } } - pub fn new_string_ascii(len: usize) -> Result { + pub fn new_string_ascii(len: usize) -> Result { let len = BufferLength::try_from(len)?; Ok(TypeSignature::SequenceType(SequenceSubtype::StringType( StringSubtype::ASCII(len), ))) } - pub fn new_string_utf8(len: usize) -> Result { + pub fn new_string_utf8(len: usize) -> Result { let len = StringUTF8Length::try_from(len)?; Ok(TypeSignature::SequenceType(SequenceSubtype::StringType( StringSubtype::UTF8(len), @@ -454,12 +479,16 @@ 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) } - pub fn admits_type(&self, epoch: &StacksEpochId, other: &TypeSignature) -> Result { + pub fn admits_type( + &self, + epoch: &StacksEpochId, + other: &TypeSignature, + ) -> Result { match epoch { StacksEpochId::Epoch20 | StacksEpochId::Epoch2_05 => self.admits_type_v2_0(other), StacksEpochId::Epoch21 @@ -475,7 +504,7 @@ impl TypeSignature { } } - 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 { @@ -568,7 +597,7 @@ impl TypeSignature { } } - 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(_) => { @@ -714,7 +743,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; @@ -724,8 +753,8 @@ impl TypeSignature { CallableSubtype::Principal(_) => { if is_trait.is_some() { return Err(CheckErrors::TypeError( - TypeSignature::PrincipalType, - TypeSignature::CallableType(partial.clone()), + Box::new(TypeSignature::CallableType(partial.clone())), + Box::new(TypeSignature::PrincipalType), )); } else { is_principal = true; @@ -734,8 +763,8 @@ impl TypeSignature { CallableSubtype::Trait(t) => { if is_principal { return Err(CheckErrors::TypeError( - TypeSignature::PrincipalType, - TypeSignature::CallableType(partial.clone()), + Box::new(TypeSignature::PrincipalType), + Box::new(TypeSignature::CallableType(partial.clone())), )); } else { is_trait = Some(t.clone()); @@ -757,7 +786,9 @@ impl TypeSignature { impl TryFrom> for TupleTypeSignature { type Error = CheckErrors; - fn try_from(type_data: Vec<(ClarityName, TypeSignature)>) -> Result { + fn try_from( + type_data: Vec<(ClarityName, TypeSignature)>, + ) -> Result { if type_data.is_empty() { return Err(CheckErrors::EmptyTuplesNotAllowed); } @@ -776,7 +807,9 @@ impl TryFrom> for TupleTypeSignature { impl TryFrom> for TupleTypeSignature { type Error = CheckErrors; - fn try_from(type_map: BTreeMap) -> Result { + fn try_from( + type_map: BTreeMap, + ) -> Result { if type_map.is_empty() { return Err(CheckErrors::EmptyTuplesNotAllowed); } @@ -817,7 +850,11 @@ impl TupleTypeSignature { &self.type_map } - pub fn admits(&self, epoch: &StacksEpochId, other: &TupleTypeSignature) -> Result { + pub fn admits( + &self, + epoch: &StacksEpochId, + other: &TupleTypeSignature, + ) -> Result { if self.type_map.len() != other.type_map.len() { return Ok(false); } @@ -841,7 +878,7 @@ impl TupleTypeSignature { } impl TypeSignature { - pub fn empty_buffer() -> Result { + pub fn empty_buffer() -> Result { Ok(SequenceType(SequenceSubtype::BufferType( 0_u32.try_into().map_err(|_| { CheckErrors::Expects("FAIL: Empty clarity value size is not realizable".into()) @@ -849,7 +886,7 @@ impl TypeSignature { ))) } - pub fn min_buffer() -> Result { + pub fn min_buffer() -> Result { Ok(SequenceType(SequenceSubtype::BufferType( 1_u32.try_into().map_err(|_| { CheckErrors::Expects("FAIL: Min clarity value size is not realizable".into()) @@ -857,7 +894,7 @@ impl TypeSignature { ))) } - pub fn min_string_ascii() -> Result { + pub fn min_string_ascii() -> Result { Ok(SequenceType(SequenceSubtype::StringType( StringSubtype::ASCII(1_u32.try_into().map_err(|_| { CheckErrors::Expects("FAIL: Min clarity value size is not realizable".into()) @@ -865,7 +902,7 @@ impl TypeSignature { ))) } - pub fn min_string_utf8() -> Result { + pub fn min_string_utf8() -> Result { Ok(SequenceType(SequenceSubtype::StringType( StringSubtype::UTF8(1_u32.try_into().map_err(|_| { CheckErrors::Expects("FAIL: Min clarity value size is not realizable".into()) @@ -873,7 +910,7 @@ impl TypeSignature { ))) } - pub fn max_string_ascii() -> Result { + pub fn max_string_ascii() -> Result { Ok(SequenceType(SequenceSubtype::StringType( StringSubtype::ASCII(BufferLength::try_from(MAX_VALUE_SIZE).map_err(|_| { CheckErrors::Expects( @@ -883,7 +920,7 @@ impl TypeSignature { ))) } - pub fn max_string_utf8() -> Result { + pub fn max_string_utf8() -> Result { Ok(SequenceType(SequenceSubtype::StringType( StringSubtype::UTF8(StringUTF8Length::try_from(MAX_VALUE_SIZE / 4).map_err(|_| { CheckErrors::Expects( @@ -893,7 +930,7 @@ impl TypeSignature { ))) } - pub fn max_buffer() -> Result { + pub fn max_buffer() -> Result { Ok(SequenceType(SequenceSubtype::BufferType( BufferLength::try_from(MAX_VALUE_SIZE).map_err(|_| { CheckErrors::Expects( @@ -903,13 +940,13 @@ impl TypeSignature { ))) } - pub fn contract_name_string_ascii_type() -> Result { + pub fn contract_name_string_ascii_type() -> Result { TypeSignature::bound_string_ascii_type(CONTRACT_MAX_NAME_LENGTH.try_into().map_err( |_| CheckErrors::Expects("FAIL: contract name max length exceeds u32 space".into()), )?) } - pub fn bound_string_ascii_type(max_len: u32) -> Result { + pub fn bound_string_ascii_type(max_len: u32) -> Result { Ok(SequenceType(SequenceSubtype::StringType( StringSubtype::ASCII(BufferLength::try_from(max_len).map_err(|_| { CheckErrors::Expects( @@ -924,7 +961,7 @@ impl TypeSignature { epoch: &StacksEpochId, a: &TypeSignature, b: &TypeSignature, - ) -> Result { + ) -> Result { if a.is_no_type() { Ok(b.clone()) } else if b.is_no_type() { @@ -934,31 +971,48 @@ impl TypeSignature { } } - /// This function returns the most-restrictive type that admits _both_ A and B (something like a least common supertype), - /// or Errors if no such type exists. On error, it throws NoSuperType(A,B), unless a constructor error'ed -- in which case, - /// it throws the constructor's error. + /// Returns the most-restrictive type that admits _both_ A and B (something like a least common supertype), + /// or Errors if no such type exists. On error, it throws TypeError(A,B), unless a constructor error'ed, + /// in which case, it throws SupertypeTooLarge. /// - /// For two Tuples: - /// least_supertype(A, B) := (tuple \for_each(key k) least_supertype(type_a_k, type_b_k)) - /// For two Lists: - /// least_supertype(A, B) := (list max_len: max(max_len A, max_len B), entry: least_supertype(entry_a, entry_b)) - /// if max_len A | max_len B is 0: entry := Non-empty list entry - /// For two responses: - /// least_supertype(A, B) := (response least_supertype(ok_a, ok_b), least_supertype(err_a, err_b)) - /// if any entries are NoType, use the other type's entry - /// For two options: - /// least_supertype(A, B) := (option least_supertype(some_a, some_b)) - /// if some_a | some_b is NoType, use the other type's entry. - /// For buffers: - /// least_supertype(A, B) := (buff len: max(len A, len B)) - /// For ints, uints, principals, bools: - /// least_supertype(A, B) := if A != B, error, else A + /// The behavior varies by epoch: + /// - Epoch 2.0/2.05: Uses [`TypeSignature::least_supertype_v2_0`] + /// - Epoch 2.1+: Uses [`TypeSignature::least_supertype_v2_1`], Adds support for CallableTypes and ListUnionTypes /// + /// For two Tuples: + /// least_supertype(A, B) := (tuple \for_each(key k) least_supertype(type_a_k, type_b_k)) + /// Note: `A`'s keys must be a subset of `B`'s. + /// For two Lists: + /// least_supertype(A, B) := (list max_len: max(max_len A, max_len B), entry: least_supertype(entry_a, entry_b)) + /// If max_len A | max_len B is 0: entry := Non-empty list entry + /// For two Responses: + /// least_supertype(A, B) := (response least_supertype(ok_a, ok_b), least_supertype(err_a, err_b)) + /// If any entries are NoType, use the other type's entry + /// For two Options: + /// least_supertype(A, B) := (option least_supertype(some_a, some_b)) + /// If some_a | some_b is NoType, use the other type's entry + /// For Buffers: + /// least_supertype(A, B) := (buff len: max(len A, len B)) + /// For ASCII/UTF8 Strings: + /// least_supertype(A, B) := (string len: max(len A, len B)) + /// Note: ASCII and UTF8 strings cannot be unified + /// For NoType: + /// least_supertype(NoType, X) := X + /// For CallableTypes (v2.1+ only): + /// - Two identical CallableTypes unify to themselves + /// - Two different CallableTypes unify to a ListUnionType containing both + /// - CallableType and ListUnionType unify by adding the callable to the union + /// - Principal type unifies with CallableSubtype::Principal(_) to PrincipalType + /// - Principal type unifies with ListUnionType if all members are principals + /// For ListUnionTypes (v2.1+ only): + /// least_supertype(A, B) := ListUnionType with the union of both sets + /// For other types (ints, uints, principals, bools): + /// least_supertype(A, B) := if A != B, error, else A pub fn least_supertype( epoch: &StacksEpochId, a: &TypeSignature, b: &TypeSignature, - ) -> Result { + ) -> Result { match epoch { StacksEpochId::Epoch20 | StacksEpochId::Epoch2_05 => Self::least_supertype_v2_0(a, b), StacksEpochId::Epoch21 @@ -974,7 +1028,10 @@ impl TypeSignature { } } - pub fn least_supertype_v2_0(a: &TypeSignature, b: &TypeSignature) -> Result { + pub fn least_supertype_v2_0( + a: &TypeSignature, + b: &TypeSignature, + ) -> Result { match (a, b) { ( TupleType(TupleTypeSignature { type_map: types_a }), @@ -982,9 +1039,10 @@ 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(CheckErrors::TypeError(a.clone(), b.clone()))?; + let entry_b = types_b.get(name).ok_or(CheckErrors::TypeError( + Box::new(a.clone()), + Box::new(b.clone()), + ))?; let entry_out = Self::least_supertype_v2_0(entry_a, entry_b)?; type_map_out.insert(name.clone(), entry_out); } @@ -1070,13 +1128,19 @@ impl TypeSignature { if x == y { Ok(x.clone()) } else { - Err(CheckErrors::TypeError(a.clone(), b.clone())) + Err(CheckErrors::TypeError( + Box::new(a.clone()), + Box::new(b.clone()), + )) } } } } - pub fn least_supertype_v2_1(a: &TypeSignature, b: &TypeSignature) -> Result { + pub fn least_supertype_v2_1( + a: &TypeSignature, + b: &TypeSignature, + ) -> Result { match (a, b) { ( TupleType(TupleTypeSignature { type_map: types_a }), @@ -1084,9 +1148,10 @@ 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(CheckErrors::TypeError(a.clone(), b.clone()))?; + let entry_b = types_b.get(name).ok_or(CheckErrors::TypeError( + Box::new(a.clone()), + Box::new(b.clone()), + ))?; let entry_out = Self::least_supertype_v2_1(entry_a, entry_b)?; type_map_out.insert(name.clone(), entry_out); } @@ -1194,7 +1259,10 @@ impl TypeSignature { if all_principals { Ok(PrincipalType) } else { - Err(CheckErrors::TypeError(a.clone(), b.clone())) + Err(CheckErrors::TypeError( + Box::new(a.clone()), + Box::new(b.clone()), + )) } } (ListUnionType(l1), ListUnionType(l2)) => { @@ -1204,13 +1272,16 @@ impl TypeSignature { if x == y { Ok(x.clone()) } else { - Err(CheckErrors::TypeError(a.clone(), b.clone())) + Err(CheckErrors::TypeError( + Box::new(a.clone()), + Box::new(b.clone()), + )) } } } } - pub fn list_of(item_type: TypeSignature, max_len: u32) -> Result { + pub fn list_of(item_type: TypeSignature, max_len: u32) -> Result { ListTypeData::new_list(item_type, max_len).map(|x| x.into()) } @@ -1221,7 +1292,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, @@ -1250,7 +1321,7 @@ 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()), @@ -1260,14 +1331,13 @@ impl TypeSignature { } // 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(); + pub fn construct_parent_list_type(args: &[Value]) -> Result { + let children_types: Result, CheckErrors> = + args.iter().map(TypeSignature::type_of).collect(); TypeSignature::parent_list_type(&children_types?) } - pub fn parent_list_type( - children: &[TypeSignature], - ) -> std::result::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() { @@ -1313,7 +1383,7 @@ impl TypeSignature { } } - pub fn size(&self) -> Result { + pub fn size(&self) -> Result { self.inner_size()?.ok_or_else(|| { CheckErrors::Expects( "FAIL: .size() overflowed on too large of a type. construction should have failed!" @@ -1322,7 +1392,7 @@ impl TypeSignature { }) } - fn inner_size(&self) -> Result> { + fn inner_size(&self) -> Result, CheckErrors> { 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). @@ -1355,7 +1425,7 @@ impl TypeSignature { Ok(out) } - pub fn type_size(&self) -> Result { + pub fn type_size(&self) -> Result { self.inner_type_size() .ok_or_else(|| CheckErrors::ValueTooLarge) } @@ -1387,7 +1457,7 @@ impl TypeSignature { impl ListTypeData { /// List Size: type_signature_size + max_len * entry_type.size() - fn inner_size(&self) -> Result> { + fn inner_size(&self) -> Result, CheckErrors> { let total_size = self .entry_type .size()? @@ -1436,7 +1506,7 @@ impl TupleTypeSignature { } } - pub fn size(&self) -> Result { + pub fn size(&self) -> Result { self.inner_size()? .ok_or_else(|| CheckErrors::Expects("size() overflowed on a constructed type.".into())) } @@ -1452,7 +1522,7 @@ impl TupleTypeSignature { /// Tuple Size: /// size( btreemap ) + type_size /// size( btreemap ) = 2*map.len() + sum(names) + sum(values) - fn inner_size(&self) -> Result> { + fn inner_size(&self) -> Result, CheckErrors> { let Some(mut total_size) = u32::try_from(self.type_map.len()) .ok() .and_then(|x| x.checked_mul(2)) diff --git a/clarity/Cargo.toml b/clarity/Cargo.toml index dd6555ec7fe..cba9468313f 100644 --- a/clarity/Cargo.toml +++ b/clarity/Cargo.toml @@ -18,7 +18,7 @@ name = "clarity" path = "./src/libclarity.rs" [dependencies] -clarity-serialization = { package = "clarity-serialization", path = "../clarity-serialization", default-features = false } +clarity-types = { package = "clarity-types", path = "../clarity-types", default-features = false } serde = { workspace = true } serde_derive = { workspace = true } serde_json = { workspace = true } @@ -33,7 +33,7 @@ rusqlite = { workspace = true, optional = true } [dev-dependencies] assert-json-diff = "1.0.0" -clarity-serialization = { package = "clarity-serialization", path = "../clarity-serialization", default-features = false, features = ["testing"] } +clarity-types = { package = "clarity-types", path = "../clarity-types", default-features = false, features = ["testing"] } mutants = "0.0.3" proptest = { version = "1.6.0", default-features = false, features = ["std"] } rstest = { version = "0.17.0" } @@ -48,11 +48,11 @@ serde_stacker = "0.1" [features] default = ["rusqlite"] -developer-mode = ["stacks_common/developer-mode", "clarity-serialization/developer-mode"] +developer-mode = ["stacks_common/developer-mode", "clarity-types/developer-mode"] slog_json = ["stacks_common/slog_json"] -rusqlite = ["stacks_common/rusqlite", "clarity-serialization/rusqlite", "dep:rusqlite"] -testing = ["rusqlite", "rstest", "rstest_reuse", "clarity-serialization/testing"] +rusqlite = ["stacks_common/rusqlite", "clarity-types/rusqlite", "dep:rusqlite"] +testing = ["rusqlite", "rstest", "rstest_reuse", "clarity-types/testing"] devtools = [] rollback_value_check = [] disable-costs = [] -wasm-web = ["stacks_common/wasm-web", "clarity-serialization/wasm-web"] +wasm-web = ["stacks_common/wasm-web", "clarity-types/wasm-web"] diff --git a/clarity/fuzz/Cargo.lock b/clarity/fuzz/Cargo.lock index 105f727c674..c8c5e852896 100644 --- a/clarity/fuzz/Cargo.lock +++ b/clarity/fuzz/Cargo.lock @@ -137,7 +137,7 @@ dependencies = [ name = "clarity" version = "0.0.1" dependencies = [ - "clarity-serialization", + "clarity-types", "integer-sqrt", "lazy_static", "regex", @@ -162,7 +162,7 @@ dependencies = [ ] [[package]] -name = "clarity-serialization" +name = "clarity-types" version = "0.0.1" dependencies = [ "lazy_static", @@ -649,6 +649,7 @@ dependencies = [ "base64", "digest 0.9.0", "hmac-drbg", + "lazy_static", "libsecp256k1-core", "libsecp256k1-gen-ecmult", "libsecp256k1-gen-genmult", diff --git a/clarity/src/vm/analysis/analysis_db.rs b/clarity/src/vm/analysis/analysis_db.rs index 2510006f543..390d3e9dbe0 100644 --- a/clarity/src/vm/analysis/analysis_db.rs +++ b/clarity/src/vm/analysis/analysis_db.rs @@ -16,11 +16,11 @@ use std::collections::{BTreeMap, BTreeSet}; -use clarity_serialization::representations::ClarityName; -use clarity_serialization::types::{QualifiedContractIdentifier, TraitIdentifier}; +use clarity_types::representations::ClarityName; +use clarity_types::types::{QualifiedContractIdentifier, TraitIdentifier}; use stacks_common::types::StacksEpochId; -use crate::vm::analysis::errors::{CheckErrors, CheckResult}; +use crate::vm::analysis::errors::{CheckError, CheckErrors}; use crate::vm::analysis::type_checker::ContractAnalysis; use crate::vm::database::{ ClarityBackingStore, ClarityDeserializable, ClaritySerializable, RollbackWrapper, @@ -63,13 +63,13 @@ impl<'a> AnalysisDatabase<'a> { self.store.nest(); } - pub fn commit(&mut self) -> CheckResult<()> { + pub fn commit(&mut self) -> Result<(), CheckError> { self.store .commit() .map_err(|e| CheckErrors::Expects(format!("{e:?}")).into()) } - pub fn roll_back(&mut self) -> CheckResult<()> { + pub fn roll_back(&mut self) -> Result<(), CheckError> { self.store .rollback() .map_err(|e| CheckErrors::Expects(format!("{e:?}")).into()) @@ -99,7 +99,7 @@ impl<'a> AnalysisDatabase<'a> { pub fn load_contract_non_canonical( &mut self, contract_identifier: &QualifiedContractIdentifier, - ) -> CheckResult> { + ) -> Result, CheckError> { self.store .get_metadata(contract_identifier, AnalysisDatabase::storage_key()) // treat NoSuchContract error thrown by get_metadata as an Option::None -- @@ -118,7 +118,7 @@ impl<'a> AnalysisDatabase<'a> { &mut self, contract_identifier: &QualifiedContractIdentifier, epoch: &StacksEpochId, - ) -> CheckResult> { + ) -> Result, CheckError> { Ok(self .store .get_metadata(contract_identifier, AnalysisDatabase::storage_key()) @@ -141,7 +141,7 @@ impl<'a> AnalysisDatabase<'a> { &mut self, contract_identifier: &QualifiedContractIdentifier, contract: &ContractAnalysis, - ) -> CheckResult<()> { + ) -> Result<(), CheckError> { let key = AnalysisDatabase::storage_key(); if self.store.has_metadata_entry(contract_identifier, key) { return Err(CheckErrors::ContractAlreadyExists(contract_identifier.to_string()).into()); @@ -156,7 +156,7 @@ impl<'a> AnalysisDatabase<'a> { pub fn get_clarity_version( &mut self, contract_identifier: &QualifiedContractIdentifier, - ) -> CheckResult { + ) -> Result { // TODO: this function loads the whole contract to obtain the function type. // but it doesn't need to -- rather this information can just be // stored as its own entry. the analysis cost tracking currently only @@ -172,7 +172,7 @@ impl<'a> AnalysisDatabase<'a> { contract_identifier: &QualifiedContractIdentifier, function_name: &str, epoch: &StacksEpochId, - ) -> CheckResult> { + ) -> Result, CheckError> { // TODO: this function loads the whole contract to obtain the function type. // but it doesn't need to -- rather this information can just be // stored as its own entry. the analysis cost tracking currently only @@ -190,7 +190,7 @@ impl<'a> AnalysisDatabase<'a> { contract_identifier: &QualifiedContractIdentifier, function_name: &str, epoch: &StacksEpochId, - ) -> CheckResult> { + ) -> Result, CheckError> { // TODO: this function loads the whole contract to obtain the function type. // but it doesn't need to -- rather this information can just be // stored as its own entry. the analysis cost tracking currently only @@ -208,7 +208,7 @@ impl<'a> AnalysisDatabase<'a> { contract_identifier: &QualifiedContractIdentifier, trait_name: &str, epoch: &StacksEpochId, - ) -> CheckResult>> { + ) -> Result>, CheckError> { // TODO: this function loads the whole contract to obtain the function type. // but it doesn't need to -- rather this information can just be // stored as its own entry. the analysis cost tracking currently only @@ -227,7 +227,7 @@ impl<'a> AnalysisDatabase<'a> { pub fn get_implemented_traits( &mut self, contract_identifier: &QualifiedContractIdentifier, - ) -> CheckResult> { + ) -> Result, CheckError> { let contract = self .load_contract_non_canonical(contract_identifier)? .ok_or(CheckErrors::NoSuchContract(contract_identifier.to_string()))?; diff --git a/clarity/src/vm/analysis/arithmetic_checker/mod.rs b/clarity/src/vm/analysis/arithmetic_checker/mod.rs index 3ad4c17db8d..83be68ab230 100644 --- a/clarity/src/vm/analysis/arithmetic_checker/mod.rs +++ b/clarity/src/vm/analysis/arithmetic_checker/mod.rs @@ -14,11 +14,9 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use clarity_serialization::representations::ClarityName; +use clarity_types::representations::ClarityName; -pub use super::errors::{ - check_argument_count, check_arguments_at_least, CheckError, CheckErrors, CheckResult, -}; +pub use super::errors::{check_argument_count, check_arguments_at_least, CheckError, CheckErrors}; use crate::vm::analysis::types::ContractAnalysis; use crate::vm::functions::define::{DefineFunctions, DefineFunctionsParsed}; use crate::vm::functions::NativeFunctions; @@ -144,9 +142,8 @@ impl ArithmeticOnlyChecker<'_> { { match native_var { ContractCaller | TxSender | TotalLiquidMicroSTX | BlockHeight | BurnBlockHeight - | Regtest | TxSponsor | Mainnet | ChainId | StacksBlockHeight | TenureHeight => { - Err(Error::VariableForbidden(native_var)) - } + | Regtest | TxSponsor | Mainnet | ChainId | StacksBlockHeight | TenureHeight + | BlockTime | CurrentContract => Err(Error::VariableForbidden(native_var)), NativeNone | NativeTrue | NativeFalse => Ok(()), } } else { @@ -185,7 +182,7 @@ impl ArithmeticOnlyChecker<'_> { IsStandard | PrincipalDestruct | PrincipalConstruct => { Err(Error::FunctionNotPermitted(function)) } - IntToAscii | IntToUtf8 | StringToInt | StringToUInt => { + IntToAscii | IntToUtf8 | StringToInt | StringToUInt | ToAscii => { Err(Error::FunctionNotPermitted(function)) } Sha512 | Sha512Trunc256 | Secp256k1Recover | Secp256k1Verify | Hash160 | Sha256 diff --git a/clarity/src/vm/analysis/contract_interface_builder/mod.rs b/clarity/src/vm/analysis/contract_interface_builder/mod.rs index 4e0aa9a0cbe..26c71f695b9 100644 --- a/clarity/src/vm/analysis/contract_interface_builder/mod.rs +++ b/clarity/src/vm/analysis/contract_interface_builder/mod.rs @@ -19,7 +19,7 @@ use std::collections::{BTreeMap, BTreeSet}; use stacks_common::types::StacksEpochId; use crate::vm::analysis::types::ContractAnalysis; -use crate::vm::analysis::CheckResult; +use crate::vm::analysis::CheckError; use crate::vm::types::signatures::CallableSubtype; use crate::vm::types::{ FixedFunction, FunctionArg, FunctionType, TupleTypeSignature, TypeSignature, @@ -28,7 +28,7 @@ use crate::vm::{CheckErrors, ClarityName, ClarityVersion}; pub fn build_contract_interface( contract_analysis: &ContractAnalysis, -) -> CheckResult { +) -> Result { let mut contract_interface = ContractInterface::new(contract_analysis.epoch, contract_analysis.clarity_version); @@ -267,7 +267,7 @@ impl ContractInterfaceFunction { fn from_map( map: &BTreeMap, access: ContractInterfaceFunctionAccess, - ) -> CheckResult> { + ) -> Result, CheckError> { map.iter() .map(|(name, function_type)| { Ok(ContractInterfaceFunction { @@ -400,7 +400,7 @@ impl ContractInterface { } } - pub fn serialize(&self) -> CheckResult { + pub fn serialize(&self) -> Result { serde_json::to_string(self).map_err(|_| { CheckErrors::Expects("Failed to serialize contract interface".into()).into() }) diff --git a/clarity/src/vm/analysis/errors.rs b/clarity/src/vm/analysis/errors.rs index 022cc91de57..f7ce7072f38 100644 --- a/clarity/src/vm/analysis/errors.rs +++ b/clarity/src/vm/analysis/errors.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -pub use clarity_serialization::errors::analysis::{ +pub use clarity_types::errors::analysis::{ check_argument_count, check_arguments_at_least, check_arguments_at_most, CheckError, - CheckErrors, CheckResult, SyntaxBindingError, SyntaxBindingErrorType, + CheckErrors, SyntaxBindingError, SyntaxBindingErrorType, }; diff --git a/clarity/src/vm/analysis/mod.rs b/clarity/src/vm/analysis/mod.rs index 90c61ca78fd..404f468609c 100644 --- a/clarity/src/vm/analysis/mod.rs +++ b/clarity/src/vm/analysis/mod.rs @@ -28,7 +28,7 @@ use stacks_common::types::StacksEpochId; pub use self::analysis_db::AnalysisDatabase; use self::arithmetic_checker::ArithmeticOnlyChecker; use self::contract_interface_builder::build_contract_interface; -pub use self::errors::{CheckError, CheckErrors, CheckResult}; +pub use self::errors::{CheckError, CheckErrors}; use self::read_only_checker::ReadOnlyChecker; use self::trait_checker::TraitChecker; use self::type_checker::v2_05::TypeChecker as TypeChecker2_05; @@ -52,7 +52,7 @@ pub fn mem_type_check( snippet: &str, version: ClarityVersion, epoch: StacksEpochId, -) -> CheckResult<(Option, ContractAnalysis)> { +) -> Result<(Option, ContractAnalysis), CheckError> { let contract_identifier = QualifiedContractIdentifier::transient(); let contract = build_ast_with_rules( &contract_identifier, @@ -92,7 +92,7 @@ pub fn mem_type_check( .cloned(); Ok((first_type, x)) } - Err((e, _)) => Err(e), + Err(e) => Err(e.0), } } @@ -106,7 +106,7 @@ pub fn type_check( insert_contract: bool, epoch: &StacksEpochId, version: &ClarityVersion, -) -> CheckResult { +) -> Result { run_analysis( contract_identifier, expressions, @@ -119,7 +119,7 @@ pub fn type_check( *version, true, ) - .map_err(|(e, _cost_tracker)| e) + .map_err(|e| e.0) } #[allow(clippy::too_many_arguments)] @@ -132,7 +132,7 @@ pub fn run_analysis( epoch: StacksEpochId, version: ClarityVersion, build_type_map: bool, -) -> Result { +) -> Result> { let mut contract_analysis = ContractAnalysis::new( contract_identifier.clone(), expressions.to_vec(), @@ -178,7 +178,10 @@ pub fn run_analysis( }); match result { Ok(_) => Ok(contract_analysis), - Err(e) => Err((e, contract_analysis.take_contract_cost_tracker())), + Err(e) => Err(Box::new(( + e, + contract_analysis.take_contract_cost_tracker(), + ))), } } diff --git a/clarity/src/vm/analysis/read_only_checker/mod.rs b/clarity/src/vm/analysis/read_only_checker/mod.rs index 0b1c0d0a015..21b2bbd8f7b 100644 --- a/clarity/src/vm/analysis/read_only_checker/mod.rs +++ b/clarity/src/vm/analysis/read_only_checker/mod.rs @@ -16,13 +16,12 @@ use std::collections::HashMap; -use clarity_serialization::representations::ClarityName; -use clarity_serialization::types::{PrincipalData, Value}; +use clarity_types::representations::ClarityName; +use clarity_types::types::{PrincipalData, Value}; use stacks_common::types::StacksEpochId; pub use super::errors::{ - check_argument_count, check_arguments_at_least, CheckError, CheckErrors, CheckResult, - SyntaxBindingError, + check_argument_count, check_arguments_at_least, CheckError, CheckErrors, SyntaxBindingError, }; use super::AnalysisDatabase; use crate::vm::analysis::types::{AnalysisPass, ContractAnalysis}; @@ -55,7 +54,7 @@ impl AnalysisPass for ReadOnlyChecker<'_, '_> { epoch: &StacksEpochId, contract_analysis: &mut ContractAnalysis, analysis_db: &mut AnalysisDatabase, - ) -> CheckResult<()> { + ) -> Result<(), CheckError> { let mut command = ReadOnlyChecker::new(analysis_db, epoch, &contract_analysis.clarity_version); command.run(contract_analysis)?; @@ -84,7 +83,7 @@ impl<'a, 'b> ReadOnlyChecker<'a, 'b> { /// # Errors /// - `CheckErrors::WriteAttemptedInReadOnly` /// - Contract parsing errors - pub fn run(&mut self, contract_analysis: &ContractAnalysis) -> CheckResult<()> { + pub fn run(&mut self, contract_analysis: &ContractAnalysis) -> Result<(), CheckError> { // Iterate over all the top-level statements in a contract. for exp in contract_analysis.expressions.iter() { let mut result = self.check_top_level_expression(exp); @@ -108,7 +107,10 @@ impl<'a, 'b> ReadOnlyChecker<'a, 'b> { /// # Errors /// - CheckErrors::WriteAttemptedInReadOnly /// - Contract parsing errors - fn check_top_level_expression(&mut self, expression: &SymbolicExpression) -> CheckResult<()> { + fn check_top_level_expression( + &mut self, + expression: &SymbolicExpression, + ) -> Result<(), CheckError> { use crate::vm::functions::define::DefineFunctionsParsed::*; if let Some(define_type) = DefineFunctionsParsed::try_parse(expression)? { match define_type { @@ -166,7 +168,7 @@ impl<'a, 'b> ReadOnlyChecker<'a, 'b> { &mut self, signature: &[SymbolicExpression], body: &SymbolicExpression, - ) -> CheckResult<(ClarityName, bool)> { + ) -> Result<(ClarityName, bool), CheckError> { let function_name = signature .first() .ok_or(CheckErrors::DefineFunctionBadSignature)? @@ -178,7 +180,7 @@ impl<'a, 'b> ReadOnlyChecker<'a, 'b> { Ok((function_name.clone(), is_read_only)) } - fn check_reads_only_valid(&mut self, expr: &SymbolicExpression) -> CheckResult<()> { + fn check_reads_only_valid(&mut self, expr: &SymbolicExpression) -> Result<(), CheckError> { use crate::vm::functions::define::DefineFunctionsParsed::*; if let Some(define_type) = DefineFunctionsParsed::try_parse(expr)? { match define_type { @@ -223,7 +225,7 @@ impl<'a, 'b> ReadOnlyChecker<'a, 'b> { /// (1) for whether or not they are valid with respect to read-only requirements. /// (2) if valid, returns whether or not they are read only. /// Note that because of (1), this function _cannot_ short-circuit on read-only. - fn check_read_only(&mut self, expr: &SymbolicExpression) -> CheckResult { + fn check_read_only(&mut self, expr: &SymbolicExpression) -> Result { match expr.expr { AtomValue(_) | LiteralValue(_) | Atom(_) | TraitReference(_, _) | Field(_) => Ok(true), List(ref expression) => self.check_expression_application_is_read_only(expression), @@ -240,7 +242,7 @@ impl<'a, 'b> ReadOnlyChecker<'a, 'b> { fn check_each_expression_is_read_only( &mut self, expressions: &[SymbolicExpression], - ) -> CheckResult { + ) -> Result { let mut result = true; for expression in expressions.iter() { let expr_read_only = self.check_read_only(expression)?; @@ -263,7 +265,7 @@ impl<'a, 'b> ReadOnlyChecker<'a, 'b> { &mut self, function: &str, args: &[SymbolicExpression], - ) -> Option> { + ) -> Option> { NativeFunctions::lookup_by_name_at_version(function, &self.clarity_version) .map(|function| self.check_native_function_is_read_only(&function, args)) } @@ -276,7 +278,7 @@ impl<'a, 'b> ReadOnlyChecker<'a, 'b> { &mut self, function: &NativeFunctions, args: &[SymbolicExpression], - ) -> CheckResult { + ) -> Result { use crate::vm::functions::NativeFunctions::*; match function { @@ -293,7 +295,7 @@ impl<'a, 'b> ReadOnlyChecker<'a, 'b> { | AsContract | Begin | FetchVar | GetStxBalance | StxGetAccount | GetTokenBalance | GetAssetOwner | GetTokenSupply | ElementAt | IndexOf | Slice | ReplaceAt | BitwiseAnd | BitwiseOr | BitwiseNot | BitwiseLShift | BitwiseRShift | BitwiseXor2 - | ElementAtAlias | IndexOfAlias | ContractHash => { + | ElementAtAlias | IndexOfAlias | ContractHash | ToAscii => { // Check all arguments. self.check_each_expression_is_read_only(args) } @@ -441,7 +443,7 @@ impl<'a, 'b> ReadOnlyChecker<'a, 'b> { fn check_expression_application_is_read_only( &mut self, expressions: &[SymbolicExpression], - ) -> CheckResult { + ) -> Result { let (function_name, args) = expressions .split_first() .ok_or(CheckErrors::NonFunctionApplication)?; diff --git a/clarity/src/vm/analysis/read_only_checker/tests.rs b/clarity/src/vm/analysis/read_only_checker/tests.rs index 56eb0c47c62..585bea62cdc 100644 --- a/clarity/src/vm/analysis/read_only_checker/tests.rs +++ b/clarity/src/vm/analysis/read_only_checker/tests.rs @@ -44,7 +44,7 @@ fn test_argument_count_violations() { for (contract, expected) in examples.iter() { let err = mem_type_check(contract).unwrap_err(); - assert_eq!(&err.err, expected) + assert_eq!(*err.err, *expected) } } @@ -72,7 +72,7 @@ fn test_at_block_violations() { for contract in examples.iter() { let err = mem_type_check(contract).unwrap_err(); eprintln!("{err}"); - assert_eq!(err.err, CheckErrors::AtBlockClosureMustBeReadOnly) + assert_eq!(*err.err, CheckErrors::AtBlockClosureMustBeReadOnly) } } @@ -163,26 +163,24 @@ fn test_simple_read_only_violations() { for contract in bad_contracts.iter() { let err = mem_type_check(contract).unwrap_err(); - assert_eq!(err.err, CheckErrors::WriteAttemptedInReadOnly) + assert_eq!(*err.err, CheckErrors::WriteAttemptedInReadOnly) } } #[test] fn test_nested_writing_closure() { - let bad_contracts = [ - "(define-data-var cursor int 0) + let bad_contracts = ["(define-data-var cursor int 0) (define-public (bad-at-block-function) (begin (var-set cursor (at-block 0x0101010101010101010101010101010101010101010101010101010101010101 - ;; should be a read only error, caught in analysis, but it isn't + ;; should be a read only error, caught in analysis, but it isn't (begin (var-set cursor 1) 2))) - (ok 1)))" - ]; + (ok 1)))"]; for contract in bad_contracts.iter() { let err = mem_type_check(contract).unwrap_err(); - assert_eq!(err.err, CheckErrors::AtBlockClosureMustBeReadOnly) + assert_eq!(*err.err, CheckErrors::AtBlockClosureMustBeReadOnly) } } @@ -233,7 +231,7 @@ fn test_contract_call_read_only_violations( ) }) .unwrap_err(); - assert_eq!(err.err, CheckErrors::WriteAttemptedInReadOnly); + assert_eq!(*err.err, CheckErrors::WriteAttemptedInReadOnly); db.execute(|db| { type_check( diff --git a/clarity/src/vm/analysis/trait_checker/mod.rs b/clarity/src/vm/analysis/trait_checker/mod.rs index 87a31a9867c..491fe81c24c 100644 --- a/clarity/src/vm/analysis/trait_checker/mod.rs +++ b/clarity/src/vm/analysis/trait_checker/mod.rs @@ -13,10 +13,9 @@ // // You should have received a copy of the GNU General Public License // along with this program. If not, see . - use stacks_common::types::StacksEpochId; -use crate::vm::analysis::errors::{CheckErrors, CheckResult}; +use crate::vm::analysis::errors::{CheckError, CheckErrors}; use crate::vm::analysis::types::{AnalysisPass, ContractAnalysis}; use crate::vm::analysis::AnalysisDatabase; @@ -29,7 +28,7 @@ impl AnalysisPass for TraitChecker { epoch: &StacksEpochId, contract_analysis: &mut ContractAnalysis, analysis_db: &mut AnalysisDatabase, - ) -> CheckResult<()> { + ) -> Result<(), CheckError> { let mut command = TraitChecker::new(epoch); command.run(contract_analysis, analysis_db)?; Ok(()) @@ -45,7 +44,7 @@ impl TraitChecker { &mut self, contract_analysis: &ContractAnalysis, analysis_db: &mut AnalysisDatabase, - ) -> CheckResult<()> { + ) -> Result<(), CheckError> { for trait_identifier in &contract_analysis.implemented_traits { let trait_name = trait_identifier.name.to_string(); let contract_defining_trait = analysis_db diff --git a/clarity/src/vm/analysis/trait_checker/tests.rs b/clarity/src/vm/analysis/trait_checker/tests.rs index 2361cd1726a..305781f9593 100644 --- a/clarity/src/vm/analysis/trait_checker/tests.rs +++ b/clarity/src/vm/analysis/trait_checker/tests.rs @@ -97,7 +97,7 @@ fn test_incomplete_impl_trait_1(#[case] version: ClarityVersion, #[case] epoch: type_check(&impl_contract_id, &mut c3, db, true, &epoch, &version) }) .unwrap_err(); - match err.err { + match *err.err { CheckErrors::BadTraitImplementation(_, _) => {} _ => panic!("{err:?}"), } @@ -124,7 +124,7 @@ fn test_incomplete_impl_trait_2(#[case] version: ClarityVersion, #[case] epoch: type_check(&impl_contract_id, &mut c3, db, true, &epoch, &version) }) .unwrap_err(); - match err.err { + match *err.err { CheckErrors::BadTraitImplementation(_, _) => {} _ => panic!("{err:?}"), } @@ -148,7 +148,7 @@ fn test_impl_trait_arg_admission_1(#[case] version: ClarityVersion, #[case] epoc type_check(&impl_contract_id, &mut c3, db, true, &epoch, &version) }) .unwrap_err(); - match err.err { + match *err.err { CheckErrors::BadTraitImplementation(_, _) => {} _ => panic!("{err:?}"), } @@ -288,7 +288,7 @@ fn test_get_trait_reference_from_tuple( ) }) .unwrap_err(); - match err.err { + match *err.err { CheckErrors::ContractCallExpectName => {} _ => panic!("{err:?}"), } @@ -331,7 +331,7 @@ fn test_dynamic_dispatch_by_defining_and_impl_trait( ) }) .unwrap_err(); - match err.err { + match *err.err { CheckErrors::TraitReferenceUnknown(_) => {} _ => panic!("{err:?}"), } @@ -358,7 +358,7 @@ fn test_define_map_storing_trait_references( ) .unwrap_err(); - match err.err { + match *err.err { ParseErrors::TraitReferenceNotAllowed => {} _ => panic!("{err:?}"), } @@ -382,7 +382,7 @@ fn test_cycle_in_traits_1_contract(#[case] version: ClarityVersion, #[case] epoc epoch, ) .unwrap_err(); - match err.err { + match *err.err { ParseErrors::CircularReference(_) => {} _ => panic!("{err:?}"), } @@ -433,7 +433,7 @@ fn test_cycle_in_traits_2_contracts(#[case] version: ClarityVersion, #[case] epo ) }) .unwrap_err(); - match err.err { + match *err.err { CheckErrors::NoSuchContract(_) => {} _ => panic!("{err:?}"), } @@ -486,7 +486,7 @@ fn test_dynamic_dispatch_unknown_method( ) }) .unwrap_err(); - match err.err { + match *err.err { CheckErrors::TraitMethodUnknown(_, _) => {} _ => panic!("{err:?}"), } @@ -670,7 +670,7 @@ fn test_dynamic_dispatch_collision_trait( epoch, ) .unwrap_err(); - match err.err { + match *err.err { ParseErrors::NameAlreadyUsed(_) => {} _ => panic!("{err:?}"), } @@ -699,7 +699,7 @@ fn test_dynamic_dispatch_collision_defined_trait( epoch, ) .unwrap_err(); - match err.err { + match *err.err { ParseErrors::NameAlreadyUsed(_) => {} _ => panic!("{err:?}"), } @@ -739,7 +739,7 @@ fn test_dynamic_dispatch_collision_imported_trait( epoch, ) .unwrap_err(); - match err.err { + match *err.err { ParseErrors::NameAlreadyUsed(_) => {} _ => panic!("{err:?}"), } @@ -811,7 +811,7 @@ fn test_dynamic_dispatch_importing_non_existant_trait( ) }) .unwrap_err(); - match err.err { + match *err.err { CheckErrors::TraitReferenceUnknown(_) => {} _ => panic!("{err:?}"), } @@ -1098,13 +1098,21 @@ fn test_dynamic_dispatch_including_wrong_nested_trait( }) .unwrap_err(); - match err.err { - CheckErrors::TypeError( - TypeSignature::TraitReferenceType(_), - TypeSignature::TraitReferenceType(_), - ) if epoch < StacksEpochId::Epoch21 => {} - CheckErrors::TypeError(TypeSignature::CallableType(_), TypeSignature::CallableType(_)) - if epoch >= StacksEpochId::Epoch21 && version < ClarityVersion::Clarity2 => {} + match *err.err { + CheckErrors::TypeError(expected, actual) if epoch < StacksEpochId::Epoch21 => { + match (&*expected, &*actual) { + (TypeSignature::TraitReferenceType(_), TypeSignature::TraitReferenceType(_)) => {} + _ => panic!("unexpected TypeSignature: {expected:?} {actual:?}"), + } + } + CheckErrors::TypeError(expected, actual) + if epoch >= StacksEpochId::Epoch21 && version < ClarityVersion::Clarity2 => + { + match (&*expected, &*actual) { + (TypeSignature::CallableType(_), TypeSignature::CallableType(_)) => {} + _ => panic!("unexpected TypeSignature: {expected:?} {actual:?}"), + } + } CheckErrors::TraitReferenceUnknown(name) => assert_eq!(name.as_str(), "trait-a"), _ => panic!("{err:?}"), } @@ -1158,7 +1166,7 @@ fn test_dynamic_dispatch_mismatched_args( ) }) .unwrap_err(); - match err.err { + match *err.err { CheckErrors::TypeError(_, _) => {} _ => panic!("{err:?}"), } @@ -1212,7 +1220,7 @@ fn test_dynamic_dispatch_mismatched_returns( ) }) .unwrap_err(); - match err.err { + match *err.err { CheckErrors::BadTraitImplementation(_, _) => {} _ => panic!("{err:?}"), } @@ -1248,7 +1256,7 @@ fn test_bad_call_with_trait(#[case] version: ClarityVersion, #[case] epoch: Stac type_check(&call_contract_id, &mut c4, db, true, &epoch, &version) }) .unwrap_err(); - match err.err { + match *err.err { CheckErrors::TypeError(_, _) => {} _ => panic!("{err:?}"), } @@ -1457,7 +1465,7 @@ fn test_dynamic_dispatch_pass_bound_principal_as_trait_in_user_defined_functions }); match result { Err(err) if version == ClarityVersion::Clarity1 => { - match err.err { + match *err.err { CheckErrors::TypeError(_, _) => {} _ => panic!("{err:?}"), }; @@ -1551,7 +1559,7 @@ fn test_contract_of_wrong_type(#[case] version: ClarityVersion, #[case] epoch: S ) }) .unwrap_err(); - match err_principal.err { + match *err_principal.err { CheckErrors::TraitReferenceUnknown(_) => {} _ => panic!("{err_principal:?}"), } @@ -1561,7 +1569,7 @@ fn test_contract_of_wrong_type(#[case] version: ClarityVersion, #[case] epoch: S type_check(&disp_contract_id, &mut c_int, db, true, &epoch, &version) }) .unwrap_err(); - match err_int.err { + match *err_int.err { CheckErrors::TraitReferenceUnknown(_) => {} _ => panic!("{err_int:?}"), } @@ -1571,7 +1579,7 @@ fn test_contract_of_wrong_type(#[case] version: ClarityVersion, #[case] epoch: S type_check(&disp_contract_id, &mut c_int, db, true, &epoch, &version) }) .unwrap_err(); - match err_uint.err { + match *err_uint.err { CheckErrors::TraitReferenceUnknown(_) => {} _ => panic!("{err_uint:?}"), } @@ -1581,7 +1589,7 @@ fn test_contract_of_wrong_type(#[case] version: ClarityVersion, #[case] epoch: S type_check(&disp_contract_id, &mut c_int, db, true, &epoch, &version) }) .unwrap_err(); - match err_bool.err { + match *err_bool.err { CheckErrors::TraitReferenceUnknown(_) => {} _ => panic!("{err_bool:?}"), } @@ -1591,7 +1599,7 @@ fn test_contract_of_wrong_type(#[case] version: ClarityVersion, #[case] epoch: S type_check(&disp_contract_id, &mut c_int, db, true, &epoch, &version) }) .unwrap_err(); - match err_list.err { + match *err_list.err { CheckErrors::TraitReferenceUnknown(_) => {} _ => panic!("{err_list:?}"), } @@ -1601,7 +1609,7 @@ fn test_contract_of_wrong_type(#[case] version: ClarityVersion, #[case] epoch: S type_check(&disp_contract_id, &mut c_int, db, true, &epoch, &version) }) .unwrap_err(); - match err_buff.err { + match *err_buff.err { CheckErrors::TraitReferenceUnknown(_) => {} _ => panic!("{err_buff:?}"), } @@ -1611,7 +1619,7 @@ fn test_contract_of_wrong_type(#[case] version: ClarityVersion, #[case] epoch: S type_check(&disp_contract_id, &mut c_int, db, true, &epoch, &version) }) .unwrap_err(); - match err_tuple.err { + match *err_tuple.err { CheckErrors::TraitReferenceUnknown(_) => {} _ => panic!("{err_tuple:?}"), } @@ -1810,11 +1818,12 @@ fn test_trait_contract_not_found(#[case] version: ClarityVersion, #[case] epoch: &version, ) }) { - Err(CheckError { - err: CheckErrors::NoSuchContract(contract), - expressions: _, - diagnostic: _, - }) if version < ClarityVersion::Clarity2 => assert!(contract.ends_with(".trait-contract")), + Err(CheckError { err, .. }) if version < ClarityVersion::Clarity2 => match *err { + CheckErrors::NoSuchContract(contract) => { + assert!(contract.ends_with(".trait-contract")) + } + _ => panic!("{version}: unexpected NoSuchContract error"), + }, Ok(_) if version >= ClarityVersion::Clarity2 => (), res => panic!("{version}: {res:?}"), } diff --git a/clarity/src/vm/analysis/type_checker/contexts.rs b/clarity/src/vm/analysis/type_checker/contexts.rs index 7637f79a91a..ddc34d8b8b5 100644 --- a/clarity/src/vm/analysis/type_checker/contexts.rs +++ b/clarity/src/vm/analysis/type_checker/contexts.rs @@ -18,7 +18,7 @@ use std::collections::{HashMap, HashSet}; use stacks_common::types::StacksEpochId; -use crate::vm::analysis::errors::{CheckError, CheckErrors, CheckResult}; +use crate::vm::analysis::errors::{CheckError, CheckErrors}; use crate::vm::types::signatures::CallableSubtype; use crate::vm::types::{TraitIdentifier, TypeSignature}; use crate::vm::{ClarityName, ClarityVersion, SymbolicExpression, MAX_CONTEXT_DEPTH}; @@ -64,7 +64,7 @@ impl TypeMap { &mut self, expr: &SymbolicExpression, type_sig: TypeSignature, - ) -> CheckResult<()> { + ) -> Result<(), CheckError> { match self.map { TypeMapDataType::Map(ref mut map) => { if map.insert(expr.id, type_sig).is_some() { @@ -103,7 +103,7 @@ impl TypingContext<'_> { } } - pub fn extend(&self) -> CheckResult> { + pub fn extend(&self) -> Result, CheckError> { if self.depth >= MAX_CONTEXT_DEPTH { Err(CheckError::new(CheckErrors::MaxContextDepthReached)) } else { diff --git a/clarity/src/vm/analysis/type_checker/mod.rs b/clarity/src/vm/analysis/type_checker/mod.rs index 33e7035f5f9..d33c7c87e67 100644 --- a/clarity/src/vm/analysis/type_checker/mod.rs +++ b/clarity/src/vm/analysis/type_checker/mod.rs @@ -20,7 +20,7 @@ pub mod v2_1; use stacks_common::types::StacksEpochId; -use super::errors::{CheckErrors, CheckResult}; +use super::errors::{CheckError, CheckErrors}; pub use super::types::{AnalysisPass, ContractAnalysis}; use super::AnalysisDatabase; use crate::vm::costs::CostTracker; @@ -34,7 +34,7 @@ impl FunctionType { args: &[TypeSignature], epoch: StacksEpochId, clarity_version: ClarityVersion, - ) -> CheckResult { + ) -> Result { match epoch { StacksEpochId::Epoch20 | StacksEpochId::Epoch2_05 => { self.check_args_2_05(accounting, args) @@ -60,7 +60,7 @@ impl FunctionType { func_args: &[Value], epoch: StacksEpochId, clarity_version: ClarityVersion, - ) -> CheckResult { + ) -> Result { match epoch { StacksEpochId::Epoch20 | StacksEpochId::Epoch2_05 => { self.check_args_by_allowing_trait_cast_2_05(db, func_args) diff --git a/clarity/src/vm/analysis/type_checker/v2_05/contexts.rs b/clarity/src/vm/analysis/type_checker/v2_05/contexts.rs index d59ff4c2a8c..d89baac36e4 100644 --- a/clarity/src/vm/analysis/type_checker/v2_05/contexts.rs +++ b/clarity/src/vm/analysis/type_checker/v2_05/contexts.rs @@ -16,7 +16,7 @@ use std::collections::{BTreeMap, HashMap, HashSet}; -use crate::vm::analysis::errors::{CheckError, CheckErrors, CheckResult}; +use crate::vm::analysis::errors::{CheckError, CheckErrors}; use crate::vm::analysis::types::ContractAnalysis; use crate::vm::representations::ClarityName; use crate::vm::types::signatures::FunctionSignature; @@ -57,7 +57,7 @@ impl ContractContext { } } - pub fn check_name_used(&self, name: &str) -> CheckResult<()> { + pub fn check_name_used(&self, name: &str) -> Result<(), CheckError> { if self.variable_types.contains_key(name) || self.persisted_variable_types.contains_key(name) || self.private_function_types.contains_key(name) @@ -75,7 +75,7 @@ impl ContractContext { } } - fn check_function_type(&mut self, f_name: &str) -> CheckResult<()> { + fn check_function_type(&mut self, f_name: &str) -> Result<(), CheckError> { self.check_name_used(f_name)?; Ok(()) } @@ -92,7 +92,7 @@ impl ContractContext { &mut self, name: ClarityName, func_type: FunctionType, - ) -> CheckResult<()> { + ) -> Result<(), CheckError> { self.check_function_type(&name)?; self.public_function_types.insert(name, func_type); Ok(()) @@ -102,7 +102,7 @@ impl ContractContext { &mut self, name: ClarityName, func_type: FunctionType, - ) -> CheckResult<()> { + ) -> Result<(), CheckError> { self.check_function_type(&name)?; self.read_only_function_types.insert(name, func_type); Ok(()) @@ -112,7 +112,7 @@ impl ContractContext { &mut self, name: ClarityName, func_type: FunctionType, - ) -> CheckResult<()> { + ) -> Result<(), CheckError> { self.check_function_type(&name)?; self.private_function_types.insert(name, func_type); Ok(()) @@ -122,7 +122,7 @@ impl ContractContext { &mut self, map_name: ClarityName, map_type: (TypeSignature, TypeSignature), - ) -> CheckResult<()> { + ) -> Result<(), CheckError> { self.check_name_used(&map_name)?; self.map_types.insert(map_name, map_type); Ok(()) @@ -132,7 +132,7 @@ impl ContractContext { &mut self, const_name: ClarityName, var_type: TypeSignature, - ) -> CheckResult<()> { + ) -> Result<(), CheckError> { self.check_name_used(&const_name)?; self.variable_types.insert(const_name, var_type); Ok(()) @@ -142,13 +142,13 @@ impl ContractContext { &mut self, var_name: ClarityName, var_type: TypeSignature, - ) -> CheckResult<()> { + ) -> Result<(), CheckError> { self.check_name_used(&var_name)?; self.persisted_variable_types.insert(var_name, var_type); Ok(()) } - pub fn add_ft(&mut self, token_name: ClarityName) -> CheckResult<()> { + pub fn add_ft(&mut self, token_name: ClarityName) -> Result<(), CheckError> { self.check_name_used(&token_name)?; self.fungible_tokens.insert(token_name); Ok(()) @@ -158,7 +158,7 @@ impl ContractContext { &mut self, token_name: ClarityName, token_type: TypeSignature, - ) -> CheckResult<()> { + ) -> Result<(), CheckError> { self.check_name_used(&token_name)?; self.non_fungible_tokens.insert(token_name, token_type); Ok(()) @@ -168,12 +168,15 @@ impl ContractContext { &mut self, trait_name: ClarityName, trait_signature: BTreeMap, - ) -> CheckResult<()> { + ) -> Result<(), CheckError> { self.traits.insert(trait_name, trait_signature); Ok(()) } - pub fn add_implemented_trait(&mut self, trait_identifier: TraitIdentifier) -> CheckResult<()> { + pub fn add_implemented_trait( + &mut self, + trait_identifier: TraitIdentifier, + ) -> Result<(), CheckError> { self.implemented_traits.insert(trait_identifier); Ok(()) } 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 8a29b3f9e27..31cf5140dda 100644 --- a/clarity/src/vm/analysis/type_checker/v2_05/mod.rs +++ b/clarity/src/vm/analysis/type_checker/v2_05/mod.rs @@ -27,8 +27,7 @@ pub use self::natives::{SimpleNativeFunction, TypedNativeFunction}; use super::contexts::{TypeMap, TypingContext}; use super::ContractAnalysis; pub use crate::vm::analysis::errors::{ - check_argument_count, check_arguments_at_least, CheckError, CheckErrors, CheckResult, - SyntaxBindingErrorType, + check_argument_count, check_arguments_at_least, CheckError, CheckErrors, SyntaxBindingErrorType, }; use crate::vm::analysis::AnalysisDatabase; use crate::vm::costs::cost_functions::ClarityCostFunction; @@ -115,7 +114,7 @@ impl TypeChecker<'_, '_> { contract_analysis: &mut ContractAnalysis, analysis_db: &mut AnalysisDatabase, build_type_map: bool, - ) -> CheckResult<()> { + ) -> Result<(), CheckError> { let cost_track = contract_analysis.take_contract_cost_tracker(); let mut command = TypeChecker::new(analysis_db, cost_track, build_type_map); // run the analysis, and replace the cost tracker whether or not the @@ -135,14 +134,12 @@ impl TypeChecker<'_, '_> { } } -pub type TypeResult = CheckResult; - impl FunctionType { pub fn check_args_2_05( &self, accounting: &mut T, args: &[TypeSignature], - ) -> CheckResult { + ) -> Result { match self { FunctionType::Variadic(expected_type, return_type) => { check_arguments_at_least(1, args)?; @@ -150,8 +147,8 @@ impl FunctionType { analysis_typecheck_cost(accounting, expected_type, found_type)?; if !expected_type.admits_type(&StacksEpochId::Epoch2_05, found_type)? { return Err(CheckErrors::TypeError( - expected_type.clone(), - found_type.clone(), + Box::new(expected_type.clone()), + Box::new(found_type.clone()), ) .into()); } @@ -168,8 +165,8 @@ impl FunctionType { analysis_typecheck_cost(accounting, expected_type, found_type)?; if !expected_type.admits_type(&StacksEpochId::Epoch2_05, found_type)? { return Err(CheckErrors::TypeError( - expected_type.clone(), - found_type.clone(), + Box::new(expected_type.clone()), + Box::new(found_type.clone()), ) .into()); } @@ -185,7 +182,10 @@ impl FunctionType { return Ok(return_type.clone()); } } - Err(CheckErrors::UnionTypeError(arg_types.clone(), found_type.clone()).into()) + Err( + CheckErrors::UnionTypeError(arg_types.clone(), Box::new(found_type.clone())) + .into(), + ) } FunctionType::ArithmeticVariadic | FunctionType::ArithmeticBinary @@ -205,13 +205,17 @@ impl FunctionType { TypeSignature::UIntType => Ok(TypeSignature::UIntType), _ => Err(CheckErrors::UnionTypeError( vec![TypeSignature::IntType, TypeSignature::UIntType], - first.clone(), + Box::new(first.clone()), )), }?; for found_type in rest.iter() { analysis_typecheck_cost(accounting, &TypeSignature::IntType, found_type)?; if found_type != &return_type { - return Err(CheckErrors::TypeError(return_type, found_type.clone()).into()); + return Err(CheckErrors::TypeError( + Box::new(return_type), + Box::new(found_type.clone()), + ) + .into()); } } Ok(return_type) @@ -225,13 +229,17 @@ impl FunctionType { if first != &TypeSignature::IntType && first != &TypeSignature::UIntType { return Err(CheckErrors::UnionTypeError( vec![TypeSignature::IntType, TypeSignature::UIntType], - first.clone(), + Box::new(first.clone()), ) .into()); } if first != second { - return Err(CheckErrors::TypeError(first.clone(), second.clone()).into()); + return Err(CheckErrors::TypeError( + Box::new(first.clone()), + Box::new(second.clone()), + ) + .into()); } Ok(TypeSignature::BoolType) @@ -246,7 +254,7 @@ impl FunctionType { &self, db: &mut AnalysisDatabase, func_args: &[Value], - ) -> CheckResult { + ) -> Result { let (expected_args, returns) = match self { FunctionType::Fixed(FixedFunction { args, returns }) => (args, returns), _ => return Err(CheckErrors::Expects("Unexpected function type".into()).into()), @@ -283,9 +291,11 @@ impl FunctionType { (expected_type, value) => { if !expected_type.admits(&StacksEpochId::Epoch2_05, value)? { let actual_type = TypeSignature::type_of(value)?; - return Err( - CheckErrors::TypeError(expected_type.clone(), actual_type).into() - ); + return Err(CheckErrors::TypeError( + Box::new(expected_type.clone()), + Box::new(actual_type), + ) + .into()); } } } @@ -294,7 +304,9 @@ impl FunctionType { } } -fn trait_type_size(trait_sig: &BTreeMap) -> CheckResult { +fn trait_type_size( + trait_sig: &BTreeMap, +) -> Result { let mut total_size = 0; for (_func_name, value) in trait_sig.iter() { total_size = total_size.cost_overflow_add(value.total_type_size()?)?; @@ -302,7 +314,7 @@ fn trait_type_size(trait_sig: &BTreeMap) -> Chec Ok(total_size) } -fn type_reserved_variable(variable_name: &str) -> CheckResult> { +fn type_reserved_variable(variable_name: &str) -> Result, CheckError> { if let Some(variable) = NativeVariables::lookup_by_name_at_version(variable_name, &ClarityVersion::Clarity1) { @@ -318,9 +330,9 @@ fn type_reserved_variable(variable_name: &str) -> CheckResult TypeSignature::BoolType, TotalLiquidMicroSTX => TypeSignature::UIntType, Regtest => TypeSignature::BoolType, - TxSponsor | Mainnet | ChainId | StacksBlockHeight | TenureHeight => { + TxSponsor | Mainnet | ChainId | StacksBlockHeight | TenureHeight | BlockTime | CurrentContract => { return Err(CheckErrors::Expects( - "tx-sponsor, mainnet, chain-id, stacks-block-height, and tenure-height should not reach here in 2.05".into(), + "tx-sponsor, mainnet, chain-id, stacks-block-height, tenure-height, block-time, and current-contract should not reach here in 2.05".into(), ) .into()) } @@ -360,7 +372,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { self.cost_track } - pub fn track_return_type(&mut self, return_type: TypeSignature) -> CheckResult<()> { + pub fn track_return_type(&mut self, return_type: TypeSignature) -> Result<(), CheckError> { runtime_cost( ClarityCostFunction::AnalysisTypeCheck, self, @@ -375,7 +387,12 @@ impl<'a, 'b> TypeChecker<'a, 'b> { &expected_type, &return_type, ) - .map_err(|_| CheckErrors::ReturnTypesMustMatch(expected_type, return_type))?, + .map_err(|_| { + CheckErrors::ReturnTypesMustMatch( + Box::new(expected_type), + Box::new(return_type), + ) + })?, None => return_type, }; @@ -390,7 +407,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { } } - pub fn run(&mut self, contract_analysis: &ContractAnalysis) -> CheckResult<()> { + pub fn run(&mut self, contract_analysis: &ContractAnalysis) -> Result<(), CheckError> { // charge for the eventual storage cost of the analysis -- // it is linear in the size of the AST. let mut size: u64 = 0; @@ -432,7 +449,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { expr: &SymbolicExpression, context: &TypingContext, expected_type: &TypeSignature, - ) -> TypeResult { + ) -> Result { if let ( LiteralValue(Value::Principal(PrincipalData::Contract(ref contract_identifier))), TypeSignature::TraitReferenceType(trait_identifier), @@ -473,7 +490,8 @@ impl<'a, 'b> TypeChecker<'a, 'b> { if !expected_type.admits_type(&StacksEpochId::Epoch2_05, &actual_type)? { let mut err: CheckError = - CheckErrors::TypeError(expected_type.clone(), actual_type).into(); + CheckErrors::TypeError(Box::new(expected_type.clone()), Box::new(actual_type)) + .into(); err.set_expression(expr); Err(err) } else { @@ -482,7 +500,11 @@ impl<'a, 'b> TypeChecker<'a, 'b> { } // Type checks an expression, recursively type checking its subexpressions - pub fn type_check(&mut self, expr: &SymbolicExpression, context: &TypingContext) -> TypeResult { + pub fn type_check( + &mut self, + expr: &SymbolicExpression, + context: &TypingContext, + ) -> Result { runtime_cost(ClarityCostFunction::AnalysisVisit, self, 0)?; let mut result = self.inner_type_check(expr, context); @@ -500,7 +522,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { &mut self, args: &[SymbolicExpression], context: &TypingContext, - ) -> TypeResult { + ) -> Result { let mut types_returned = self.type_check_all(args, context)?; let last_return = types_returned @@ -519,7 +541,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { &mut self, args: &[SymbolicExpression], context: &TypingContext, - ) -> CheckResult> { + ) -> Result, CheckError> { let mut result = Vec::with_capacity(args.len()); for arg in args.iter() { // don't use map here, since type_check has side-effects. @@ -533,7 +555,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { func_type: &FunctionType, args: &[SymbolicExpression], context: &TypingContext, - ) -> TypeResult { + ) -> Result { let typed_args = self.type_check_all(args, context)?; func_type.check_args(self, &typed_args, context.epoch, context.clarity_version) } @@ -549,7 +571,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { signature: &[SymbolicExpression], body: &SymbolicExpression, context: &TypingContext, - ) -> CheckResult<(ClarityName, FixedFunction)> { + ) -> Result<(ClarityName, FixedFunction), CheckError> { let (function_name, args) = signature .split_first() .ok_or(CheckErrors::RequiresAtLeastArguments(1, 0))?; @@ -606,7 +628,10 @@ impl<'a, 'b> TypeChecker<'a, 'b> { &return_type, ) .map_err(|_| { - CheckErrors::ReturnTypesMustMatch(expected.clone(), return_type) + CheckErrors::ReturnTypesMustMatch( + Box::new(expected.clone()), + Box::new(return_type), + ) })? } else { return_type @@ -636,7 +661,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { map_name: &ClarityName, key_type: &SymbolicExpression, value_type: &SymbolicExpression, - ) -> CheckResult<(ClarityName, (TypeSignature, TypeSignature))> { + ) -> Result<(ClarityName, (TypeSignature, TypeSignature)), CheckError> { self.type_map.set_type(key_type, no_type())?; self.type_map.set_type(value_type, no_type())?; // should we set the type of the subexpressions of the signature to no-type as well? @@ -656,7 +681,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { function: &str, args: &[SymbolicExpression], context: &TypingContext, - ) -> Option { + ) -> Option> { if let Some(ref native_function) = NativeFunctions::lookup_by_name_at_version(function, &ClarityVersion::Clarity1) { @@ -674,7 +699,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { &mut self, expression: &[SymbolicExpression], context: &TypingContext, - ) -> TypeResult { + ) -> Result { let (function_name, args) = expression .split_first() .ok_or(CheckErrors::NonFunctionApplication)?; @@ -701,7 +726,11 @@ impl<'a, 'b> TypeChecker<'a, 'b> { } } - fn lookup_variable(&mut self, name: &str, context: &TypingContext) -> TypeResult { + fn lookup_variable( + &mut self, + name: &str, + context: &TypingContext, + ) -> Result { runtime_cost(ClarityCostFunction::AnalysisLookupVariableConst, self, 0)?; if let Some(type_result) = type_reserved_variable(name)? { @@ -711,6 +740,13 @@ impl<'a, 'b> TypeChecker<'a, 'b> { } else if let Some(type_result) = context.lookup_trait_reference_type(name) { Ok(TypeSignature::TraitReferenceType(type_result.clone())) } else { + // Special case where a top-level is being looked up, which must + // be undefined. This early error prevents a cost function error + // due to `context.depth` being 0. + if context.depth == 0 { + return Err(CheckErrors::UndefinedVariable(name.to_string()).into()); + } + runtime_cost( ClarityCostFunction::AnalysisLookupVariableDepth, self, @@ -729,7 +765,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { &mut self, expr: &SymbolicExpression, context: &TypingContext, - ) -> TypeResult { + ) -> Result { let type_sig = match expr.expr { AtomValue(ref value) | LiteralValue(ref value) => TypeSignature::type_of(value)?, Atom(ref name) => self.lookup_variable(name, context)?, @@ -753,7 +789,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { var_name: &ClarityName, var_type: &SymbolicExpression, context: &mut TypingContext, - ) -> CheckResult<(ClarityName, TypeSignature)> { + ) -> Result<(ClarityName, TypeSignature), CheckError> { let var_type = self.type_check(var_type, context)?; Ok((var_name.clone(), var_type)) } @@ -764,7 +800,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { var_type: &SymbolicExpression, initial: &SymbolicExpression, context: &mut TypingContext, - ) -> CheckResult<(ClarityName, TypeSignature)> { + ) -> Result<(ClarityName, TypeSignature), CheckError> { let expected_type = TypeSignature::parse_type_repr::<()>(StacksEpochId::Epoch2_05, var_type, &mut ()) .map_err(|_e| CheckErrors::DefineVariableBadSignature)?; @@ -779,7 +815,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { token_name: &ClarityName, bound: Option<&SymbolicExpression>, context: &mut TypingContext, - ) -> CheckResult { + ) -> Result { if let Some(bound) = bound { self.type_check_expects(bound, context, &TypeSignature::UIntType)?; } @@ -792,7 +828,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { asset_name: &ClarityName, nft_type: &SymbolicExpression, _context: &mut TypingContext, - ) -> CheckResult<(ClarityName, TypeSignature)> { + ) -> Result<(ClarityName, TypeSignature), CheckError> { let asset_type = TypeSignature::parse_type_repr::<()>(StacksEpochId::Epoch2_05, nft_type, &mut ()) .map_err(|_| CheckErrors::DefineNFTBadSignature)?; @@ -805,7 +841,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { trait_name: &ClarityName, function_types: &[SymbolicExpression], _context: &mut TypingContext, - ) -> CheckResult<(ClarityName, BTreeMap)> { + ) -> Result<(ClarityName, BTreeMap), CheckError> { let trait_signature = TypeSignature::parse_trait_type_repr( function_types, &mut (), @@ -821,7 +857,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { &mut self, expression: &SymbolicExpression, context: &mut TypingContext, - ) -> CheckResult> { + ) -> Result, CheckError> { if let Some(define_type) = DefineFunctionsParsed::try_parse(expression)? { match define_type { DefineFunctionsParsed::Constant { name, value } => { @@ -859,9 +895,10 @@ impl<'a, 'b> TypeChecker<'a, 'b> { .add_public_function_type(f_name, FunctionType::Fixed(f_type))?; return Ok(Some(())); } else { - return Err( - CheckErrors::PublicFunctionMustReturnResponse(f_type.returns).into(), - ); + return Err(CheckErrors::PublicFunctionMustReturnResponse(Box::new( + f_type.returns, + )) + .into()); } } DefineFunctionsParsed::ReadOnlyFunction { signature, body } => { 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 ad066938ce8..13f967e6af3 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 @@ -14,8 +14,8 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use super::{TypeChecker, TypeResult, TypingContext}; -use crate::vm::analysis::errors::{check_argument_count, CheckErrors}; +use super::{TypeChecker, TypingContext}; +use crate::vm::analysis::errors::{check_argument_count, CheckError, CheckErrors}; use crate::vm::costs::cost_functions::ClarityCostFunction; use crate::vm::costs::runtime_cost; use crate::vm::representations::SymbolicExpression; @@ -25,7 +25,7 @@ pub fn check_special_get_owner( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(2, args)?; let asset_name = args[0].match_atom().ok_or(CheckErrors::BadTokenName)?; @@ -53,7 +53,7 @@ pub fn check_special_get_balance( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(2, args)?; let asset_name = args[0].match_atom().ok_or(CheckErrors::BadTokenName)?; @@ -74,7 +74,7 @@ pub fn check_special_mint_asset( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(3, args)?; let asset_name = args[0].match_atom().ok_or(CheckErrors::BadTokenName)?; @@ -105,7 +105,7 @@ pub fn check_special_mint_token( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(3, args)?; let asset_name = args[0].match_atom().ok_or(CheckErrors::BadTokenName)?; @@ -132,7 +132,7 @@ pub fn check_special_transfer_asset( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(4, args)?; let token_name = args[0].match_atom().ok_or(CheckErrors::BadTokenName)?; @@ -164,7 +164,7 @@ pub fn check_special_transfer_token( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(4, args)?; let token_name = args[0].match_atom().ok_or(CheckErrors::BadTokenName)?; @@ -192,7 +192,7 @@ pub fn check_special_get_token_supply( checker: &mut TypeChecker, args: &[SymbolicExpression], _context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(1, args)?; let asset_name = args[0].match_atom().ok_or(CheckErrors::BadTokenName)?; @@ -210,7 +210,7 @@ pub fn check_special_burn_asset( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(3, args)?; let asset_name = args[0].match_atom().ok_or(CheckErrors::BadTokenName)?; @@ -241,7 +241,7 @@ pub fn check_special_burn_token( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(3, args)?; let asset_name = args[0].match_atom().ok_or(CheckErrors::BadTokenName)?; 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 b8d36b2f825..ccbf880e179 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 @@ -17,7 +17,7 @@ use stacks_common::types::StacksEpochId; use crate::vm::analysis::type_checker::v2_05::{ - check_arguments_at_least, CheckError, CheckErrors, TypeChecker, TypeResult, TypingContext, + check_arguments_at_least, CheckError, CheckErrors, TypeChecker, TypingContext, }; use crate::vm::costs::cost_functions::ClarityCostFunction; use crate::vm::costs::{analysis_typecheck_cost, runtime_cost}; @@ -28,7 +28,7 @@ pub fn check_special_fetch_entry( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_arguments_at_least(2, args)?; let map_name = args[0].match_atom().ok_or(CheckErrors::BadMapName)?; @@ -56,8 +56,8 @@ pub fn check_special_fetch_entry( if !expected_key_type.admits_type(&StacksEpochId::Epoch2_05, &key_type)? { Err(CheckError::new(CheckErrors::TypeError( - expected_key_type.clone(), - key_type, + Box::new(expected_key_type.clone()), + Box::new(key_type), ))) } else { Ok(option_type) @@ -68,7 +68,7 @@ pub fn check_special_delete_entry( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_arguments_at_least(2, args)?; let map_name = args[0].match_atom().ok_or(CheckErrors::BadMapName)?; @@ -89,8 +89,8 @@ pub fn check_special_delete_entry( if !expected_key_type.admits_type(&StacksEpochId::Epoch2_05, &key_type)? { Err(CheckError::new(CheckErrors::TypeError( - expected_key_type.clone(), - key_type, + Box::new(expected_key_type.clone()), + Box::new(key_type), ))) } else { Ok(TypeSignature::BoolType) @@ -101,7 +101,7 @@ fn check_set_or_insert_entry( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_arguments_at_least(3, args)?; let map_name = args[0].match_atom().ok_or(CheckErrors::BadMapName)?; @@ -130,13 +130,13 @@ fn check_set_or_insert_entry( if !expected_key_type.admits_type(&StacksEpochId::Epoch2_05, &key_type)? { Err(CheckError::new(CheckErrors::TypeError( - expected_key_type.clone(), - key_type, + Box::new(expected_key_type.clone()), + Box::new(key_type), ))) } else if !expected_value_type.admits_type(&StacksEpochId::Epoch2_05, &value_type)? { Err(CheckError::new(CheckErrors::TypeError( - expected_value_type.clone(), - value_type, + Box::new(expected_value_type.clone()), + Box::new(value_type), ))) } else { Ok(TypeSignature::BoolType) @@ -147,7 +147,7 @@ pub fn check_special_set_entry( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_set_or_insert_entry(checker, args, context) } @@ -155,6 +155,6 @@ pub fn check_special_insert_entry( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_set_or_insert_entry(checker, args, context) } 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 f481dbe6e44..08297f7440f 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 @@ -16,9 +16,7 @@ use stacks_common::types::StacksEpochId; -use super::{ - check_argument_count, check_arguments_at_least, no_type, TypeChecker, TypeResult, TypingContext, -}; +use super::{check_argument_count, check_arguments_at_least, no_type, TypeChecker, TypingContext}; use crate::vm::analysis::errors::{CheckError, CheckErrors, SyntaxBindingErrorType}; use crate::vm::costs::cost_functions::ClarityCostFunction; use crate::vm::costs::{analysis_typecheck_cost, runtime_cost}; @@ -41,8 +39,13 @@ pub enum TypedNativeFunction { Simple(SimpleNativeFunction), } +#[allow(clippy::type_complexity)] pub struct SpecialNativeFunction( - &'static dyn Fn(&mut TypeChecker, &[SymbolicExpression], &TypingContext) -> TypeResult, + &'static dyn Fn( + &mut TypeChecker, + &[SymbolicExpression], + &TypingContext, + ) -> Result, ); pub struct SimpleNativeFunction(pub FunctionType); @@ -50,7 +53,7 @@ fn check_special_list_cons( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { let typed_args = checker.type_check_all(args, context)?; for type_arg in typed_args.iter() { runtime_cost( @@ -67,7 +70,7 @@ fn check_special_print( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(1, args)?; checker.type_check(&args[0], context) } @@ -76,7 +79,7 @@ fn check_special_as_contract( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(1, args)?; checker.type_check(&args[0], context) } @@ -85,7 +88,7 @@ fn check_special_at_block( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(2, args)?; checker.type_check_expects(&args[0], context, &BUFF_32)?; checker.type_check(&args[1], context) @@ -95,7 +98,7 @@ fn check_special_begin( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_arguments_at_least(1, args)?; checker.type_check_consecutive_statements(args, context) @@ -105,7 +108,7 @@ fn inner_handle_tuple_get( tuple_type_sig: &TupleTypeSignature, field_to_get: &str, checker: &mut TypeChecker, -) -> TypeResult { +) -> Result { runtime_cost( ClarityCostFunction::AnalysisCheckTupleGet, checker, @@ -126,7 +129,7 @@ fn check_special_get( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(2, args)?; let field_to_get = args[0].match_atom().ok_or(CheckErrors::BadTupleFieldName)?; @@ -141,10 +144,10 @@ fn check_special_get( let option_type = TypeSignature::new_option(inner_type)?; Ok(option_type) } else { - Err(CheckErrors::ExpectedTuple(*value_type_sig).into()) + Err(CheckErrors::ExpectedTuple(value_type_sig).into()) } } else { - Err(CheckErrors::ExpectedTuple(argument_type).into()) + Err(CheckErrors::ExpectedTuple(Box::new(argument_type)).into()) } } @@ -152,19 +155,19 @@ fn check_special_merge( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(2, args)?; let res = checker.type_check(&args[0], context)?; let mut base = match res { TypeSignature::TupleType(tuple_sig) => Ok(tuple_sig), - _ => Err(CheckErrors::ExpectedTuple(res.clone())), + _ => Err(CheckErrors::ExpectedTuple(Box::new(res.clone()))), }?; let res = checker.type_check(&args[1], context)?; let mut update = match res { TypeSignature::TupleType(tuple_sig) => Ok(tuple_sig), - _ => Err(CheckErrors::ExpectedTuple(res.clone())), + _ => Err(CheckErrors::ExpectedTuple(Box::new(res.clone()))), }?; runtime_cost( ClarityCostFunction::AnalysisCheckTupleMerge, @@ -180,7 +183,7 @@ pub fn check_special_tuple_cons( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_arguments_at_least(1, args)?; let len = args.len(); @@ -214,7 +217,7 @@ fn check_special_let( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_arguments_at_least(2, args)?; let binding_list = args[0] @@ -257,7 +260,7 @@ fn check_special_fetch_var( checker: &mut TypeChecker, args: &[SymbolicExpression], _context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(1, args)?; let var_name = args[0] @@ -284,7 +287,7 @@ fn check_special_set_var( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_arguments_at_least(2, args)?; let var_name = args[0].match_atom().ok_or(CheckErrors::BadMapName)?; @@ -305,8 +308,8 @@ fn check_special_set_var( if !expected_value_type.admits_type(&StacksEpochId::Epoch2_05, &value_type)? { Err(CheckError::new(CheckErrors::TypeError( - expected_value_type.clone(), - value_type, + Box::new(expected_value_type.clone()), + Box::new(value_type), ))) } else { Ok(TypeSignature::BoolType) @@ -317,7 +320,7 @@ fn check_special_equals( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_arguments_at_least(1, args)?; let arg_types = checker.type_check_all(args, context)?; @@ -326,7 +329,7 @@ fn check_special_equals( for x_type in arg_types.into_iter() { analysis_typecheck_cost(checker, &x_type, &arg_type)?; arg_type = TypeSignature::least_supertype(&StacksEpochId::Epoch2_05, &x_type, &arg_type) - .map_err(|_| CheckErrors::TypeError(x_type, arg_type))?; + .map_err(|_| CheckErrors::TypeError(Box::new(x_type), Box::new(arg_type)))?; } Ok(TypeSignature::BoolType) @@ -336,7 +339,7 @@ fn check_special_if( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(3, args)?; checker.type_check_expects(&args[0], context, &TypeSignature::BoolType)?; @@ -348,15 +351,16 @@ fn check_special_if( analysis_typecheck_cost(checker, expr1, expr2)?; - TypeSignature::least_supertype(&StacksEpochId::Epoch2_05, expr1, expr2) - .map_err(|_| CheckErrors::IfArmsMustMatch(expr1.clone(), expr2.clone()).into()) + TypeSignature::least_supertype(&StacksEpochId::Epoch2_05, expr1, expr2).map_err(|_| { + CheckErrors::IfArmsMustMatch(Box::new(expr1.clone()), Box::new(expr2.clone())).into() + }) } fn check_contract_call( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_arguments_at_least(2, args)?; let func_name = args[1] @@ -449,7 +453,7 @@ fn check_contract_of( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(1, args)?; let trait_instance = match &args[0].expr { @@ -476,7 +480,7 @@ fn check_principal_of( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(1, args)?; checker.type_check_expects(&args[0], context, &BUFF_33)?; Ok( @@ -489,7 +493,7 @@ fn check_secp256k1_recover( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(2, args)?; checker.type_check_expects(&args[0], context, &BUFF_32)?; checker.type_check_expects(&args[1], context, &BUFF_65)?; @@ -503,7 +507,7 @@ fn check_secp256k1_verify( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(3, args)?; checker.type_check_expects(&args[0], context, &BUFF_32)?; checker.type_check_expects(&args[1], context, &BUFF_65)?; @@ -515,7 +519,7 @@ fn check_get_block_info( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_arguments_at_least(2, args)?; let block_info_prop_str = args[0] @@ -541,7 +545,7 @@ impl TypedNativeFunction { checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, - ) -> TypeResult { + ) -> Result { use self::TypedNativeFunction::{Simple, Special}; match self { Special(SpecialNativeFunction(check)) => check(checker, args, context), @@ -778,7 +782,7 @@ impl TypedNativeFunction { | StringToUInt | IntToAscii | IntToUtf8 | GetBurnBlockInfo | StxTransferMemo | StxGetAccount | BitwiseAnd | BitwiseOr | BitwiseNot | BitwiseLShift | BitwiseRShift | BitwiseXor2 | Slice | ToConsensusBuff | FromConsensusBuff - | ReplaceAt | GetStacksBlockInfo | GetTenureInfo | ContractHash => { + | ReplaceAt | GetStacksBlockInfo | GetTenureInfo | ContractHash | ToAscii => { return Err(CheckErrors::Expects( "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 5644ece9169..1cf9b301a7d 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 @@ -14,12 +14,12 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use clarity_serialization::representations::ClarityName; -use clarity_serialization::types::TypeSignature; +use clarity_types::representations::ClarityName; +use clarity_types::types::TypeSignature; use stacks_common::types::StacksEpochId; use crate::vm::analysis::type_checker::v2_05::{ - check_argument_count, check_arguments_at_least, no_type, CheckErrors, TypeChecker, TypeResult, + check_argument_count, check_arguments_at_least, no_type, CheckError, CheckErrors, TypeChecker, TypingContext, }; use crate::vm::costs::cost_functions::ClarityCostFunction; @@ -30,7 +30,7 @@ pub fn check_special_okay( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(1, args)?; runtime_cost(ClarityCostFunction::AnalysisOptionCons, checker, 0)?; @@ -44,7 +44,7 @@ pub fn check_special_some( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(1, args)?; runtime_cost(ClarityCostFunction::AnalysisOptionCons, checker, 0)?; @@ -58,7 +58,7 @@ pub fn check_special_error( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(1, args)?; runtime_cost(ClarityCostFunction::AnalysisOptionCons, checker, 0)?; @@ -72,7 +72,7 @@ pub fn check_special_is_response( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(1, args)?; let input = checker.type_check(&args[0], context)?; @@ -82,7 +82,7 @@ pub fn check_special_is_response( if let TypeSignature::ResponseType(_types) = input { Ok(TypeSignature::BoolType) } else { - Err(CheckErrors::ExpectedResponseType(input.clone()).into()) + Err(CheckErrors::ExpectedResponseType(Box::new(input)).into()) } } @@ -90,7 +90,7 @@ pub fn check_special_is_optional( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(1, args)?; let input = checker.type_check(&args[0], context)?; @@ -100,7 +100,7 @@ pub fn check_special_is_optional( if let TypeSignature::OptionalType(_type) = input { Ok(TypeSignature::BoolType) } else { - Err(CheckErrors::ExpectedOptionalType(input.clone()).into()) + Err(CheckErrors::ExpectedOptionalType(Box::new(input)).into()) } } @@ -108,7 +108,7 @@ pub fn check_special_default_to( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(2, args)?; let default = checker.type_check(&args[0], context)?; @@ -119,9 +119,12 @@ pub fn check_special_default_to( if let TypeSignature::OptionalType(input_type) = input { let contained_type = *input_type; TypeSignature::least_supertype(&StacksEpochId::Epoch2_05, &default, &contained_type) - .map_err(|_| CheckErrors::DefaultTypesMustMatch(default, contained_type).into()) + .map_err(|_| { + CheckErrors::DefaultTypesMustMatch(Box::new(default), Box::new(contained_type)) + .into() + }) } else { - Err(CheckErrors::ExpectedOptionalType(input).into()) + Err(CheckErrors::ExpectedOptionalType(Box::new(input)).into()) } } @@ -129,7 +132,7 @@ pub fn check_special_asserts( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(2, args)?; checker.type_check_expects(&args[0], context, &TypeSignature::BoolType)?; @@ -140,7 +143,10 @@ pub fn check_special_asserts( Ok(TypeSignature::BoolType) } -fn inner_unwrap(input: TypeSignature, checker: &mut TypeChecker) -> TypeResult { +fn inner_unwrap( + input: TypeSignature, + checker: &mut TypeChecker, +) -> Result { runtime_cost(ClarityCostFunction::AnalysisOptionCheck, checker, 0)?; match input { @@ -159,11 +165,14 @@ fn inner_unwrap(input: TypeSignature, checker: &mut TypeChecker) -> TypeResult { Ok(ok_type) } } - _ => Err(CheckErrors::ExpectedOptionalOrResponseType(input).into()), + _ => Err(CheckErrors::ExpectedOptionalOrResponseType(Box::new(input)).into()), } } -fn inner_unwrap_err(input: TypeSignature, checker: &mut TypeChecker) -> TypeResult { +fn inner_unwrap_err( + input: TypeSignature, + checker: &mut TypeChecker, +) -> Result { runtime_cost(ClarityCostFunction::AnalysisOptionCheck, checker, 0)?; if let TypeSignature::ResponseType(response_type) = input { @@ -174,7 +183,7 @@ fn inner_unwrap_err(input: TypeSignature, checker: &mut TypeChecker) -> TypeResu Ok(err_type) } } else { - Err(CheckErrors::ExpectedResponseType(input).into()) + Err(CheckErrors::ExpectedResponseType(Box::new(input)).into()) } } @@ -182,7 +191,7 @@ pub fn check_special_unwrap_or_ret( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(2, args)?; let input = checker.type_check(&args[0], context)?; @@ -197,7 +206,7 @@ pub fn check_special_unwrap_err_or_ret( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(2, args)?; let input = checker.type_check(&args[0], context)?; @@ -212,7 +221,7 @@ pub fn check_special_try_ret( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(1, args)?; let input = checker.type_check(&args[0], context)?; @@ -242,7 +251,7 @@ pub fn check_special_try_ret( Ok(ok_type) } } - _ => Err(CheckErrors::ExpectedOptionalOrResponseType(input).into()), + _ => Err(CheckErrors::ExpectedOptionalOrResponseType(Box::new(input)).into()), } } @@ -250,7 +259,7 @@ pub fn check_special_unwrap( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(1, args)?; let input = checker.type_check(&args[0], context)?; @@ -262,7 +271,7 @@ pub fn check_special_unwrap_err( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(1, args)?; let input = checker.type_check(&args[0], context)?; @@ -276,7 +285,7 @@ fn eval_with_new_binding( bind_type: TypeSignature, checker: &mut TypeChecker, context: &TypingContext, -) -> TypeResult { +) -> Result { let mut inner_context = context.extend()?; runtime_cost( @@ -301,7 +310,7 @@ fn check_special_match_opt( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { if args.len() != 3 { Err(CheckErrors::BadMatchOptionSyntax(Box::new( CheckErrors::IncorrectArgumentCount(4, args.len() + 1), @@ -330,7 +339,10 @@ fn check_special_match_opt( &some_branch_type, &none_branch_type, ) - .map_err(|_| CheckErrors::MatchArmsMustMatch(some_branch_type, none_branch_type).into()) + .map_err(|_| { + CheckErrors::MatchArmsMustMatch(Box::new(some_branch_type), Box::new(none_branch_type)) + .into() + }) } fn check_special_match_resp( @@ -338,7 +350,7 @@ fn check_special_match_resp( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { if args.len() != 4 { Err(CheckErrors::BadMatchResponseSyntax(Box::new( CheckErrors::IncorrectArgumentCount(5, args.len() + 1), @@ -369,14 +381,17 @@ fn check_special_match_resp( analysis_typecheck_cost(checker, &ok_branch_type, &err_branch_type)?; TypeSignature::least_supertype(&StacksEpochId::Epoch2_05, &ok_branch_type, &err_branch_type) - .map_err(|_| CheckErrors::MatchArmsMustMatch(ok_branch_type, err_branch_type).into()) + .map_err(|_| { + CheckErrors::MatchArmsMustMatch(Box::new(ok_branch_type), Box::new(err_branch_type)) + .into() + }) } pub fn check_special_match( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_arguments_at_least(1, args)?; let input = checker.type_check(&args[0], context)?; @@ -388,6 +403,6 @@ pub fn check_special_match( TypeSignature::ResponseType(resp_type) => { check_special_match_resp(*resp_type, checker, &args[1..], context) } - _ => Err(CheckErrors::BadMatchInput(input).into()), + _ => Err(CheckErrors::BadMatchInput(Box::new(input)).into()), } } 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 e1bdb8cbbd1..00904bfe493 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 @@ -18,8 +18,8 @@ use stacks_common::types::StacksEpochId; use super::{SimpleNativeFunction, TypedNativeFunction}; use crate::vm::analysis::type_checker::v2_05::{ - check_argument_count, check_arguments_at_least, CheckErrors, CheckResult, TypeChecker, - TypeResult, TypingContext, + check_argument_count, check_arguments_at_least, CheckError, CheckErrors, TypeChecker, + TypingContext, }; use crate::vm::costs::cost_functions::ClarityCostFunction; use crate::vm::costs::{analysis_typecheck_cost, runtime_cost}; @@ -34,7 +34,7 @@ use crate::vm::ClarityVersion; fn get_simple_native_or_user_define( function_name: &str, checker: &mut TypeChecker, -) -> CheckResult { +) -> Result { runtime_cost(ClarityCostFunction::AnalysisLookupFunction, checker, 0)?; if let Some(ref native_function) = NativeFunctions::lookup_by_name_at_version(function_name, &ClarityVersion::Clarity1) @@ -57,7 +57,7 @@ pub fn check_special_map( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_arguments_at_least(2, args)?; let function_name = args[0] @@ -98,7 +98,7 @@ pub fn check_special_map( // However that could lead to confusions when combining certain types: // ex: (map concat (list "hello " "hi ") "world") would fail, because // strings are handled as sequences. - return Err(CheckErrors::ExpectedSequence(argument_type).into()); + return Err(CheckErrors::ExpectedSequence(Box::new(argument_type)).into()); } }; func_args.push(entry_type); @@ -114,7 +114,7 @@ pub fn check_special_filter( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(2, args)?; let function_name = args[0] @@ -130,7 +130,9 @@ pub fn check_special_filter( { let input_type = match argument_type { TypeSignature::SequenceType(ref sequence_type) => Ok(sequence_type.unit_type()?), - _ => Err(CheckErrors::ExpectedSequence(argument_type.clone())), + _ => Err(CheckErrors::ExpectedSequence(Box::new( + argument_type.clone(), + ))), }?; let filter_type = function_type.check_args( @@ -141,7 +143,11 @@ pub fn check_special_filter( )?; if TypeSignature::BoolType != filter_type { - return Err(CheckErrors::TypeError(TypeSignature::BoolType, filter_type).into()); + return Err(CheckErrors::TypeError( + Box::new(TypeSignature::BoolType), + Box::new(filter_type), + ) + .into()); } } @@ -152,7 +158,7 @@ pub fn check_special_fold( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(3, args)?; let function_name = args[0] @@ -167,7 +173,7 @@ pub fn check_special_fold( let input_type = match argument_type { TypeSignature::SequenceType(sequence_type) => Ok(sequence_type.unit_type()?), - _ => Err(CheckErrors::ExpectedSequence(argument_type)), + _ => Err(CheckErrors::ExpectedSequence(Box::new(argument_type))), }?; let initial_value_type = checker.type_check(&args[2], context)?; @@ -199,7 +205,7 @@ pub fn check_special_concat( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(2, args)?; let lhs_type = checker.type_check(&args[0], context)?; @@ -247,11 +253,15 @@ pub fn check_special_concat( TypeSignature::SequenceType(StringType(UTF8(size.try_into()?))) } (_, _) => { - return Err(CheckErrors::TypeError(lhs_type.clone(), rhs_type.clone()).into()) + return Err(CheckErrors::TypeError( + Box::new(lhs_type.clone()), + Box::new(rhs_type.clone()), + ) + .into()) } } } - _ => return Err(CheckErrors::ExpectedSequence(lhs_type.clone()).into()), + _ => return Err(CheckErrors::ExpectedSequence(Box::new(lhs_type.clone())).into()), }; Ok(res) } @@ -260,7 +270,7 @@ pub fn check_special_append( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(2, args)?; runtime_cost(ClarityCostFunction::AnalysisIterableFunc, checker, 0)?; @@ -292,14 +302,18 @@ pub fn check_special_as_max_len( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(2, args)?; let expected_len = match args[1].expr { SymbolicExpressionType::LiteralValue(Value::UInt(expected_len)) => expected_len, _ => { let expected_len_type = checker.type_check(&args[1], context)?; - return Err(CheckErrors::TypeError(TypeSignature::UIntType, expected_len_type).into()); + return Err(CheckErrors::TypeError( + Box::new(TypeSignature::UIntType), + Box::new(expected_len_type), + ) + .into()); } }; runtime_cost( @@ -337,7 +351,7 @@ pub fn check_special_as_max_len( StringUTF8Length::try_from(expected_len)?, )))), )), - _ => Err(CheckErrors::ExpectedSequence(sequence).into()), + _ => Err(CheckErrors::ExpectedSequence(Box::new(sequence)).into()), } } @@ -345,7 +359,7 @@ pub fn check_special_len( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(1, args)?; let collection_type = checker.type_check(&args[0], context)?; @@ -353,7 +367,7 @@ pub fn check_special_len( match collection_type { TypeSignature::SequenceType(_) => Ok(()), - _ => Err(CheckErrors::ExpectedSequence(collection_type)), + _ => Err(CheckErrors::ExpectedSequence(Box::new(collection_type))), }?; Ok(TypeSignature::UIntType) @@ -363,7 +377,7 @@ pub fn check_special_element_at( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(2, args)?; let _index_type = checker.type_check_expects(&args[1], context, &TypeSignature::UIntType)?; @@ -391,7 +405,7 @@ pub fn check_special_element_at( .map_err(|_| CheckErrors::Expects("Bad constructor".into()))?, )))), )), - _ => Err(CheckErrors::ExpectedSequence(collection_type).into()), + _ => Err(CheckErrors::ExpectedSequence(Box::new(collection_type)).into()), } } @@ -399,7 +413,7 @@ pub fn check_special_index_of( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(2, args)?; runtime_cost(ClarityCostFunction::AnalysisIterableFunc, checker, 0)?; @@ -407,7 +421,7 @@ pub fn check_special_index_of( let expected_input_type = match list_type { TypeSignature::SequenceType(ref sequence_type) => Ok(sequence_type.unit_type()?), - _ => Err(CheckErrors::ExpectedSequence(list_type)), + _ => Err(CheckErrors::ExpectedSequence(Box::new(list_type))), }?; checker.type_check_expects(&args[1], context, &expected_input_type)?; diff --git a/clarity/src/vm/analysis/type_checker/v2_05/tests/assets.rs b/clarity/src/vm/analysis/type_checker/v2_05/tests/assets.rs index 3d1e8ad9d1f..75dbc4928f5 100644 --- a/clarity/src/vm/analysis/type_checker/v2_05/tests/assets.rs +++ b/clarity/src/vm/analysis/type_checker/v2_05/tests/assets.rs @@ -196,42 +196,108 @@ fn test_bad_asset_usage() { CheckErrors::NoSuchFT("stackoos".to_string()), CheckErrors::BadTokenName, CheckErrors::BadTokenName, - CheckErrors::TypeError(TypeSignature::PrincipalType, TypeSignature::UIntType), - CheckErrors::TypeError(TypeSignature::PrincipalType, TypeSignature::IntType), + CheckErrors::TypeError( + Box::new(TypeSignature::PrincipalType), + Box::new(TypeSignature::UIntType), + ), + CheckErrors::TypeError( + Box::new(TypeSignature::PrincipalType), + Box::new(TypeSignature::IntType), + ), CheckErrors::BadTokenName, CheckErrors::NoSuchNFT("stackoos".to_string()), - CheckErrors::TypeError(string_ascii_type(10), TypeSignature::UIntType), - CheckErrors::TypeError(string_ascii_type(10), string_ascii_type(15)), + CheckErrors::TypeError( + Box::new(string_ascii_type(10)), + Box::new(TypeSignature::UIntType), + ), + CheckErrors::TypeError( + Box::new(string_ascii_type(10)), + Box::new(string_ascii_type(15)), + ), CheckErrors::BadTokenName, CheckErrors::NoSuchNFT("stackoos".to_string()), - CheckErrors::TypeError(string_ascii_type(10), TypeSignature::UIntType), - CheckErrors::TypeError(string_ascii_type(10), string_ascii_type(15)), - CheckErrors::TypeError(TypeSignature::PrincipalType, TypeSignature::UIntType), + CheckErrors::TypeError( + Box::new(string_ascii_type(10)), + Box::new(TypeSignature::UIntType), + ), + CheckErrors::TypeError( + Box::new(string_ascii_type(10)), + Box::new(string_ascii_type(15)), + ), + CheckErrors::TypeError( + Box::new(TypeSignature::PrincipalType), + Box::new(TypeSignature::UIntType), + ), CheckErrors::NoSuchFT("stackoos".to_string()), CheckErrors::BadTokenName, - CheckErrors::TypeError(TypeSignature::PrincipalType, TypeSignature::UIntType), - CheckErrors::TypeError(TypeSignature::UIntType, TypeSignature::BoolType), + CheckErrors::TypeError( + Box::new(TypeSignature::PrincipalType), + Box::new(TypeSignature::UIntType), + ), + CheckErrors::TypeError( + Box::new(TypeSignature::UIntType), + Box::new(TypeSignature::BoolType), + ), CheckErrors::BadTokenName, CheckErrors::NoSuchNFT("stackoos".to_string()), - CheckErrors::TypeError(TypeSignature::PrincipalType, TypeSignature::UIntType), - CheckErrors::TypeError(TypeSignature::PrincipalType, TypeSignature::UIntType), - CheckErrors::TypeError(string_ascii_type(10), TypeSignature::UIntType), + CheckErrors::TypeError( + Box::new(TypeSignature::PrincipalType), + Box::new(TypeSignature::UIntType), + ), + CheckErrors::TypeError( + Box::new(TypeSignature::PrincipalType), + Box::new(TypeSignature::UIntType), + ), + CheckErrors::TypeError( + Box::new(string_ascii_type(10)), + Box::new(TypeSignature::UIntType), + ), CheckErrors::BadTokenName, - CheckErrors::TypeError(string_ascii_type(10), TypeSignature::UIntType), - CheckErrors::TypeError(TypeSignature::PrincipalType, TypeSignature::UIntType), + CheckErrors::TypeError( + Box::new(string_ascii_type(10)), + Box::new(TypeSignature::UIntType), + ), + CheckErrors::TypeError( + Box::new(TypeSignature::PrincipalType), + Box::new(TypeSignature::UIntType), + ), CheckErrors::NoSuchFT("stackoos".to_string()), CheckErrors::BadTokenName, - CheckErrors::TypeError(TypeSignature::PrincipalType, TypeSignature::UIntType), - CheckErrors::TypeError(TypeSignature::UIntType, TypeSignature::BoolType), - CheckErrors::TypeError(TypeSignature::PrincipalType, TypeSignature::UIntType), - CheckErrors::TypeError(TypeSignature::UIntType, TypeSignature::BoolType), + CheckErrors::TypeError( + Box::new(TypeSignature::PrincipalType), + Box::new(TypeSignature::UIntType), + ), + CheckErrors::TypeError( + Box::new(TypeSignature::UIntType), + Box::new(TypeSignature::BoolType), + ), + CheckErrors::TypeError( + Box::new(TypeSignature::PrincipalType), + Box::new(TypeSignature::UIntType), + ), + CheckErrors::TypeError( + Box::new(TypeSignature::UIntType), + Box::new(TypeSignature::BoolType), + ), CheckErrors::DefineNFTBadSignature, - CheckErrors::TypeError(TypeSignature::UIntType, TypeSignature::IntType), - CheckErrors::TypeError(TypeSignature::UIntType, TypeSignature::IntType), + CheckErrors::TypeError( + Box::new(TypeSignature::UIntType), + Box::new(TypeSignature::IntType), + ), + CheckErrors::TypeError( + Box::new(TypeSignature::UIntType), + Box::new(TypeSignature::IntType), + ), CheckErrors::NoSuchFT("stackoos".to_string()), CheckErrors::NoSuchFT("stackoos".to_string()), - CheckErrors::TypeError(TypeSignature::UIntType, TypeSignature::IntType), - CheckErrors::TypeError(TypeSignature::PrincipalType, TypeSignature::IntType), + CheckErrors::TypeError( + Box::new(TypeSignature::UIntType), + Box::new(TypeSignature::IntType), + ), + CheckErrors::TypeError( + Box::new(TypeSignature::PrincipalType), + Box::new(TypeSignature::IntType), + ), ]; for (script, expected_err) in bad_scripts.iter().zip(expected.iter()) { @@ -243,6 +309,6 @@ fn test_bad_asset_usage() { ) .unwrap_err(); println!("{script}"); - assert_eq!(&actual_err.err, expected_err); + assert_eq!(*actual_err.err, *expected_err); } } diff --git a/clarity/src/vm/analysis/type_checker/v2_05/tests/contracts.rs b/clarity/src/vm/analysis/type_checker/v2_05/tests/contracts.rs index ed5a53d7bf4..6d37d068376 100644 --- a/clarity/src/vm/analysis/type_checker/v2_05/tests/contracts.rs +++ b/clarity/src/vm/analysis/type_checker/v2_05/tests/contracts.rs @@ -54,8 +54,8 @@ const SIMPLE_TOKENS: &str = "(define-map tokens { account: principal } { balance const SIMPLE_NAMES: &str = "(define-constant burn-address 'SP000000000000000000002Q6VF78) (define-private (price-function (name uint)) (if (< name u100000) u1000 u100)) - - (define-map name-map + + (define-map name-map { name: uint } { owner: principal }) (define-map preorder-map { name-hash: (buff 20) } @@ -64,7 +64,7 @@ const SIMPLE_NAMES: &str = "(define-constant burn-address 'SP0000000000000000000 (define-private (check-balance) (contract-call? .tokens my-get-token-balance tx-sender)) - (define-public (preorder + (define-public (preorder (name-hash (buff 20)) (name-price uint)) (let ((xfer-result (contract-call? .tokens token-transfer @@ -88,13 +88,13 @@ const SIMPLE_NAMES: &str = "(define-constant burn-address 'SP0000000000000000000 ;; preorder entry must exist! (unwrap! (map-get? preorder-map (tuple (name-hash (hash160 (xor name salt))))) (err 2))) - (name-entry + (name-entry (map-get? name-map (tuple (name name))))) (if (and ;; name shouldn't *already* exist (is-none name-entry) ;; preorder must have paid enough - (<= (price-function name) + (<= (price-function name) (get paid preorder-entry)) ;; preorder must have been the current principal (is-eq tx-sender @@ -231,7 +231,7 @@ fn test_names_tokens_contracts_interface() { { "name": "tn1", "type": "bool" }, { "name": "tn2", "type": "int128" }, { "name": "tn3", "type": { "buffer": { "length": 1 } }} - ] } } + ] } } }, { "name": "f11", "access": "private", @@ -364,7 +364,7 @@ fn test_names_tokens_contracts_interface() { "name": "n2", "type": "bool" } - ] + ] } }] } @@ -492,7 +492,7 @@ fn test_names_tokens_contracts_bad() { ) }) .unwrap_err(); - assert!(matches!(err.err, CheckErrors::TypeError(_, _))); + assert!(matches!(*err.err, CheckErrors::TypeError(_, _))); } #[test] @@ -534,11 +534,11 @@ fn test_bad_map_usage() { for contract in tests.iter() { let err = mem_type_check(contract, ClarityVersion::Clarity1, StacksEpochId::Epoch2_05) .unwrap_err(); - assert!(matches!(err.err, CheckErrors::TypeError(_, _))); + assert!(matches!(*err.err, CheckErrors::TypeError(_, _))); } assert!(matches!( - mem_type_check( + *mem_type_check( unhandled_option, ClarityVersion::Clarity1, StacksEpochId::Epoch2_05 @@ -663,7 +663,7 @@ fn test_expects() { ) .unwrap_err(); eprintln!("unmatched_return_types returned check error: {err}"); - assert!(matches!(err.err, CheckErrors::ReturnTypesMustMatch(_, _))); + assert!(matches!(*err.err, CheckErrors::ReturnTypesMustMatch(_, _))); } let err = mem_type_check( @@ -673,7 +673,7 @@ fn test_expects() { ) .unwrap_err(); eprintln!("bad_default_types returned check error: {err}"); - assert!(matches!(err.err, CheckErrors::DefaultTypesMustMatch(_, _))); + assert!(matches!(*err.err, CheckErrors::DefaultTypesMustMatch(_, _))); let err = mem_type_check( notype_response_type, @@ -683,7 +683,7 @@ fn test_expects() { .unwrap_err(); eprintln!("notype_response_type returned check error: {err}"); assert!(matches!( - err.err, + *err.err, CheckErrors::CouldNotDetermineResponseErrType )); @@ -695,7 +695,7 @@ fn test_expects() { .unwrap_err(); eprintln!("notype_response_type_2 returned check error: {err}"); assert!(matches!( - err.err, + *err.err, CheckErrors::CouldNotDetermineResponseOkType )); } diff --git a/clarity/src/vm/analysis/type_checker/v2_05/tests/mod.rs b/clarity/src/vm/analysis/type_checker/v2_05/tests/mod.rs index c9c52b379f8..99fd0191cea 100644 --- a/clarity/src/vm/analysis/type_checker/v2_05/tests/mod.rs +++ b/clarity/src/vm/analysis/type_checker/v2_05/tests/mod.rs @@ -16,9 +16,8 @@ use stacks_common::types::StacksEpochId; -use crate::vm::analysis::errors::{CheckErrors, SyntaxBindingError}; +use crate::vm::analysis::errors::{CheckError, CheckErrors, SyntaxBindingError}; use crate::vm::analysis::mem_type_check; -use crate::vm::analysis::type_checker::v2_05::TypeResult; use crate::vm::ast::build_ast; use crate::vm::ast::errors::ParseErrors; use crate::vm::types::SequenceSubtype::*; @@ -32,7 +31,7 @@ use crate::vm::ClarityVersion; mod assets; mod contracts; -fn type_check_helper(exp: &str) -> TypeResult { +fn type_check_helper(exp: &str) -> Result { mem_type_check(exp, ClarityVersion::Clarity1, StacksEpochId::Epoch2_05) .map(|(type_sig_opt, _)| type_sig_opt.unwrap()) } @@ -72,8 +71,8 @@ fn test_get_block_info() { ]; let bad_expected = [ CheckErrors::NoSuchBlockInfoProperty("none".to_string()), - CheckErrors::TypeError(UIntType, BoolType), - CheckErrors::TypeError(UIntType, IntType), + CheckErrors::TypeError(Box::new(UIntType), Box::new(BoolType)), + CheckErrors::TypeError(Box::new(UIntType), Box::new(IntType)), CheckErrors::RequiresAtLeastArguments(2, 1), ]; @@ -85,7 +84,7 @@ fn test_get_block_info() { } for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { - assert_eq!(expected, &type_check_helper(bad_test).unwrap_err().err); + assert_eq!(*expected, *type_check_helper(bad_test).unwrap_err().err); } } @@ -122,7 +121,7 @@ fn test_define_trait() { ]; for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { - assert_eq!(expected, &type_check_helper(bad_test).unwrap_err().err); + assert_eq!(*expected, *type_check_helper(bad_test).unwrap_err().err); } let bad = ["(define-trait trait-1)", "(define-trait)"]; @@ -141,7 +140,7 @@ fn test_define_trait() { StacksEpochId::Epoch2_05, ) .unwrap_err(); - assert_eq!(expected, &res.err); + assert_eq!(*expected, *res.err); } } @@ -170,7 +169,7 @@ fn test_use_trait() { StacksEpochId::Epoch2_05, ) .unwrap_err(); - assert_eq!(expected, &res.err); + assert_eq!(*expected, *res.err); } } @@ -192,7 +191,7 @@ fn test_impl_trait() { StacksEpochId::Epoch2_05, ) .unwrap_err(); - assert_eq!(expected, &res.err); + assert_eq!(*expected, *res.err); } } @@ -219,14 +218,14 @@ fn test_stx_ops() { ]; let bad_expected = [ CheckErrors::IncorrectArgumentCount(3, 2), - CheckErrors::TypeError(UIntType, IntType), - CheckErrors::TypeError(PrincipalType, UIntType), - CheckErrors::TypeError(PrincipalType, BoolType), + CheckErrors::TypeError(Box::new(UIntType), Box::new(IntType)), + CheckErrors::TypeError(Box::new(PrincipalType), Box::new(UIntType)), + CheckErrors::TypeError(Box::new(PrincipalType), Box::new(BoolType)), CheckErrors::IncorrectArgumentCount(2, 1), - CheckErrors::TypeError(UIntType, IntType), - CheckErrors::TypeError(PrincipalType, BoolType), + CheckErrors::TypeError(Box::new(UIntType), Box::new(IntType)), + CheckErrors::TypeError(Box::new(PrincipalType), Box::new(BoolType)), CheckErrors::IncorrectArgumentCount(2, 3), - CheckErrors::TypeError(PrincipalType, BoolType), + CheckErrors::TypeError(Box::new(PrincipalType), Box::new(BoolType)), CheckErrors::IncorrectArgumentCount(1, 2), ]; @@ -238,7 +237,7 @@ fn test_stx_ops() { } for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { - assert_eq!(expected, &type_check_helper(bad_test).unwrap_err().err); + assert_eq!(*expected, *type_check_helper(bad_test).unwrap_err().err); } } @@ -293,11 +292,11 @@ fn test_destructuring_opts() { let bad = [ ( "(unwrap-err! (some 2) 2)", - CheckErrors::ExpectedResponseType(TypeSignature::from_string( + CheckErrors::ExpectedResponseType(Box::new(TypeSignature::from_string( "(optional int)", ClarityVersion::Clarity1, StacksEpochId::Epoch2_05, - )), + ))), ), ( "(unwrap! (err 3) 2)", @@ -334,11 +333,17 @@ fn test_destructuring_opts() { ( "(define-private (foo) (if (> 1 0) (ok 1) (err u8))) (match (foo) ok-val (+ 1 ok-val) err-val (/ err-val u0))", - CheckErrors::MatchArmsMustMatch(TypeSignature::IntType, TypeSignature::UIntType), + CheckErrors::MatchArmsMustMatch( + Box::new(TypeSignature::IntType), + Box::new(TypeSignature::UIntType), + ), ), ( "(match (some 1) inner-value (+ 1 inner-value) (> 1 28))", - CheckErrors::MatchArmsMustMatch(TypeSignature::IntType, TypeSignature::BoolType), + CheckErrors::MatchArmsMustMatch( + Box::new(TypeSignature::IntType), + Box::new(TypeSignature::BoolType), + ), ), ( "(match (some 1) inner-value (+ 1 inner-value))", @@ -365,15 +370,11 @@ fn test_destructuring_opts() { ("(match)", CheckErrors::RequiresAtLeastArguments(1, 0)), ( "(match 1 ok-val (/ ok-val 0) err-val (+ err-val 7))", - CheckErrors::BadMatchInput(TypeSignature::from_string( - "int", - ClarityVersion::Clarity1, - StacksEpochId::Epoch2_05, - )), + CheckErrors::BadMatchInput(Box::new(TypeSignature::IntType)), ), ( "(default-to 3 5)", - CheckErrors::ExpectedOptionalType(TypeSignature::IntType), + CheckErrors::ExpectedOptionalType(Box::new(TypeSignature::IntType)), ), ( "(define-private (foo (x int)) @@ -389,10 +390,14 @@ fn test_destructuring_opts() { (err u3) (ok (+ u2 (try! (t1 x))))))", CheckErrors::ReturnTypesMustMatch( - TypeSignature::new_response(TypeSignature::NoType, TypeSignature::BoolType) - .unwrap(), - TypeSignature::new_response(TypeSignature::UIntType, TypeSignature::UIntType) - .unwrap(), + Box::new( + TypeSignature::new_response(TypeSignature::NoType, TypeSignature::BoolType) + .unwrap(), + ), + Box::new( + TypeSignature::new_response(TypeSignature::UIntType, TypeSignature::UIntType) + .unwrap(), + ), ), ), ( @@ -400,9 +405,11 @@ fn test_destructuring_opts() { (define-private (t2 (x uint)) (> u2 (try! (t1 x))))", CheckErrors::ReturnTypesMustMatch( - TypeSignature::new_response(TypeSignature::NoType, TypeSignature::BoolType) - .unwrap(), - TypeSignature::BoolType, + Box::new( + TypeSignature::new_response(TypeSignature::NoType, TypeSignature::BoolType) + .unwrap(), + ), + Box::new(TypeSignature::BoolType), ), ), ( @@ -416,7 +423,7 @@ fn test_destructuring_opts() { ), ( "(try! 3)", - CheckErrors::ExpectedOptionalOrResponseType(TypeSignature::IntType), + CheckErrors::ExpectedOptionalOrResponseType(Box::new(TypeSignature::IntType)), ), ("(try! (ok 3) 4)", CheckErrors::IncorrectArgumentCount(1, 2)), ]; @@ -430,8 +437,8 @@ fn test_destructuring_opts() { for (bad_test, expected) in bad.iter() { assert_eq!( - expected, - &mem_type_check(bad_test, ClarityVersion::Clarity1, StacksEpochId::Epoch2_05) + *expected, + *mem_type_check(bad_test, ClarityVersion::Clarity1, StacksEpochId::Epoch2_05) .unwrap_err() .err ); @@ -445,7 +452,7 @@ fn test_at_block() { let bad = [ ( "(at-block (sha512 u0) u1)", - CheckErrors::TypeError(BUFF_32.clone(), BUFF_64.clone()), + CheckErrors::TypeError(Box::new(BUFF_32.clone()), Box::new(BUFF_64.clone())), ), ( "(at-block (sha256 u0) u1 u2)", @@ -461,7 +468,7 @@ fn test_at_block() { } for (bad_test, expected) in bad.iter() { - assert_eq!(expected, &type_check_helper(bad_test).unwrap_err().err); + assert_eq!(*expected, *type_check_helper(bad_test).unwrap_err().err); } } @@ -482,7 +489,7 @@ fn test_trait_reference_unknown() { StacksEpochId::Epoch2_05, ) .unwrap_err(); - assert_eq!(expected, &res.err); + assert_eq!(*expected, *res.err); } } @@ -494,7 +501,7 @@ fn test_unexpected_use_of_field_or_trait_reference() { )]; for (bad_test, expected) in bad.iter() { - assert_eq!(expected, &type_check_helper(bad_test).unwrap_err().err); + assert_eq!(*expected, *type_check_helper(bad_test).unwrap_err().err); } } @@ -515,12 +522,12 @@ fn test_simple_arithmetic_checks() { "(and (or true false) (+ 1 2 3))", ]; let bad_expected = [ - CheckErrors::TypeError(IntType, BoolType), + CheckErrors::TypeError(Box::new(IntType), Box::new(BoolType)), CheckErrors::RequiresAtLeastArguments(1, 0), CheckErrors::IncorrectArgumentCount(2, 1), CheckErrors::UndefinedVariable("x".to_string()), - CheckErrors::TypeError(IntType, BoolType), - CheckErrors::TypeError(BoolType, IntType), + CheckErrors::TypeError(Box::new(IntType), Box::new(BoolType)), + CheckErrors::TypeError(Box::new(BoolType), Box::new(IntType)), ]; for (good_test, expected) in good.iter().zip(expected.iter()) { @@ -531,7 +538,7 @@ fn test_simple_arithmetic_checks() { } for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { - assert_eq!(expected, &type_check_helper(bad_test).unwrap_err().err); + assert_eq!(*expected, *type_check_helper(bad_test).unwrap_err().err); } } @@ -580,14 +587,14 @@ fn test_simple_hash_checks() { for bad_test in bad_types.iter() { assert!(matches!( - type_check_helper(bad_test).unwrap_err().err, + *type_check_helper(bad_test).unwrap_err().err, CheckErrors::UnionTypeError(_, _) )); } for bad_test in invalid_args.iter() { assert!(matches!( - type_check_helper(bad_test).unwrap_err().err, + *type_check_helper(bad_test).unwrap_err().err, CheckErrors::IncorrectArgumentCount(_, _) )); } @@ -611,10 +618,10 @@ fn test_simple_ifs() { ]; let bad_expected = [ - CheckErrors::IfArmsMustMatch(BoolType, IntType), - CheckErrors::IfArmsMustMatch(ascii_type(1), BoolType), + CheckErrors::IfArmsMustMatch(Box::new(BoolType), Box::new(IntType)), + CheckErrors::IfArmsMustMatch(Box::new(ascii_type(1)), Box::new(BoolType)), CheckErrors::IncorrectArgumentCount(3, 0), - CheckErrors::TypeError(BoolType, IntType), + CheckErrors::TypeError(Box::new(BoolType), Box::new(IntType)), ]; for (good_test, expected) in good.iter().zip(expected.iter()) { @@ -625,7 +632,7 @@ fn test_simple_ifs() { } for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { - assert_eq!(expected, &type_check_helper(bad_test).unwrap_err().err); + assert_eq!(*expected, *type_check_helper(bad_test).unwrap_err().err); } } @@ -649,7 +656,10 @@ fn test_simple_lets() { let bad_expected = [ CheckErrors::BadSyntaxBinding(SyntaxBindingError::let_binding_invalid_length(0)), CheckErrors::BadSyntaxBinding(SyntaxBindingError::let_binding_not_atom(0)), - CheckErrors::TypeError(TypeSignature::IntType, TypeSignature::UIntType), + CheckErrors::TypeError( + Box::new(TypeSignature::IntType), + Box::new(TypeSignature::UIntType), + ), ]; for (good_test, expected) in good.iter().zip(expected.iter()) { @@ -660,7 +670,7 @@ fn test_simple_lets() { } for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { - assert_eq!(expected, &type_check_helper(bad_test).unwrap_err().err); + assert_eq!(*expected, *type_check_helper(bad_test).unwrap_err().err); } } @@ -701,19 +711,22 @@ fn test_index_of() { ]; let bad_expected = [ - CheckErrors::ExpectedSequence(TypeSignature::IntType), - CheckErrors::TypeError(TypeSignature::IntType, TypeSignature::UIntType), + CheckErrors::ExpectedSequence(Box::new(TypeSignature::IntType)), CheckErrors::TypeError( - TypeSignature::min_buffer().unwrap(), - TypeSignature::min_string_ascii().unwrap(), + Box::new(TypeSignature::IntType), + Box::new(TypeSignature::UIntType), ), CheckErrors::TypeError( - TypeSignature::min_string_utf8().unwrap(), - TypeSignature::min_string_ascii().unwrap(), + Box::new(TypeSignature::min_buffer().unwrap()), + Box::new(TypeSignature::min_string_ascii().unwrap()), ), CheckErrors::TypeError( - TypeSignature::min_string_ascii().unwrap(), - TypeSignature::min_string_utf8().unwrap(), + Box::new(TypeSignature::min_string_utf8().unwrap()), + Box::new(TypeSignature::min_string_ascii().unwrap()), + ), + CheckErrors::TypeError( + Box::new(TypeSignature::min_string_ascii().unwrap()), + Box::new(TypeSignature::min_string_utf8().unwrap()), ), CheckErrors::CouldNotDetermineType, CheckErrors::CouldNotDetermineType, @@ -721,7 +734,7 @@ fn test_index_of() { ]; for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { - assert_eq!(expected, &type_check_helper(bad_test).unwrap_err().err); + assert_eq!(*expected, *type_check_helper(bad_test).unwrap_err().err); } } @@ -746,8 +759,11 @@ fn test_element_at() { let bad = ["(element-at (list 1 2 3 4 5) 100)", "(element-at 3 u100)"]; let bad_expected = [ - CheckErrors::TypeError(TypeSignature::UIntType, TypeSignature::IntType), - CheckErrors::ExpectedSequence(TypeSignature::IntType), + CheckErrors::TypeError( + Box::new(TypeSignature::UIntType), + Box::new(TypeSignature::IntType), + ), + CheckErrors::ExpectedSequence(Box::new(TypeSignature::IntType)), ]; for (good_test, expected) in good.iter().zip(expected.iter()) { @@ -758,7 +774,7 @@ fn test_element_at() { } for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { - assert_eq!(expected, &type_check_helper(bad_test).unwrap_err().err); + assert_eq!(*expected, *type_check_helper(bad_test).unwrap_err().err); } } @@ -779,19 +795,22 @@ fn test_eqs() { ]; let bad_expected = [ - CheckErrors::TypeError(BoolType, IntType), - CheckErrors::TypeError(TypeSignature::list_of(IntType, 1).unwrap(), IntType), + CheckErrors::TypeError(Box::new(BoolType), Box::new(IntType)), + CheckErrors::TypeError( + Box::new(TypeSignature::list_of(IntType, 1).unwrap()), + Box::new(IntType), + ), CheckErrors::TypeError( - TypeSignature::from_string( + Box::new(TypeSignature::from_string( "(optional bool)", ClarityVersion::Clarity1, StacksEpochId::Epoch2_05, - ), - TypeSignature::from_string( + )), + Box::new(TypeSignature::from_string( "(optional int)", ClarityVersion::Clarity1, StacksEpochId::Epoch2_05, - ), + )), ), ]; @@ -803,7 +822,7 @@ fn test_eqs() { } for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { - assert_eq!(expected, &type_check_helper(bad_test).unwrap_err().err); + assert_eq!(*expected, *type_check_helper(bad_test).unwrap_err().err); } } @@ -824,7 +843,7 @@ fn test_asserts() { let bad_expected = [ CheckErrors::IncorrectArgumentCount(2, 1), - CheckErrors::TypeError(BoolType, IntType), + CheckErrors::TypeError(Box::new(BoolType), Box::new(IntType)), CheckErrors::IncorrectArgumentCount(2, 3), ]; @@ -836,7 +855,7 @@ fn test_asserts() { } for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { - assert_eq!(expected, &type_check_helper(bad_test).unwrap_err().err); + assert_eq!(*expected, *type_check_helper(bad_test).unwrap_err().err); } } @@ -889,23 +908,23 @@ fn test_lists() { "(map + (list 1 2 3 4 5) (list true true true true true))", ]; let bad_expected = [ - CheckErrors::TypeError(BoolType, IntType), + CheckErrors::TypeError(Box::new(BoolType), Box::new(IntType)), CheckErrors::IncorrectArgumentCount(1, 2), CheckErrors::IncorrectArgumentCount(1, 2), - CheckErrors::TypeError(IntType, BoolType), - CheckErrors::TypeError(IntType, BoolType), - CheckErrors::TypeError(IntType, BoolType), - CheckErrors::TypeError(BoolType, buff_type(20)), - CheckErrors::TypeError(BoolType, buff_type(20)), - CheckErrors::TypeError(BoolType, IntType), + CheckErrors::TypeError(Box::new(IntType), Box::new(BoolType)), + CheckErrors::TypeError(Box::new(IntType), Box::new(BoolType)), + CheckErrors::TypeError(Box::new(IntType), Box::new(BoolType)), + CheckErrors::TypeError(Box::new(BoolType), Box::new(buff_type(20))), + CheckErrors::TypeError(Box::new(BoolType), Box::new(buff_type(20))), + CheckErrors::TypeError(Box::new(BoolType), Box::new(IntType)), CheckErrors::IncorrectArgumentCount(2, 3), CheckErrors::UnknownFunction("ynot".to_string()), CheckErrors::IllegalOrUnknownFunctionApplication("if".to_string()), CheckErrors::IncorrectArgumentCount(2, 1), - CheckErrors::UnionTypeError(vec![IntType, UIntType], BoolType), - CheckErrors::ExpectedSequence(UIntType), - CheckErrors::ExpectedSequence(IntType), - CheckErrors::TypeError(IntType, BoolType), + CheckErrors::UnionTypeError(vec![IntType, UIntType], Box::new(BoolType)), + CheckErrors::ExpectedSequence(Box::new(UIntType)), + CheckErrors::ExpectedSequence(Box::new(IntType)), + CheckErrors::TypeError(Box::new(IntType), Box::new(BoolType)), ]; for (good_test, expected) in good.iter().zip(expected.iter()) { @@ -916,7 +935,7 @@ fn test_lists() { } for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { - assert_eq!(expected, &type_check_helper(bad_test).unwrap_err().err); + assert_eq!(*expected, *type_check_helper(bad_test).unwrap_err().err); } } @@ -946,20 +965,20 @@ fn test_buff() { "(len 1)", ]; let bad_expected = [ - CheckErrors::TypeError(BoolType, IntType), + CheckErrors::TypeError(Box::new(BoolType), Box::new(IntType)), CheckErrors::IncorrectArgumentCount(1, 2), - CheckErrors::TypeError(IntType, BoolType), - CheckErrors::TypeError(IntType, BoolType), - CheckErrors::TypeError(IntType, BoolType), - CheckErrors::TypeError(BoolType, buff_type(20)), - CheckErrors::TypeError(BoolType, IntType), + CheckErrors::TypeError(Box::new(IntType), Box::new(BoolType)), + CheckErrors::TypeError(Box::new(IntType), Box::new(BoolType)), + CheckErrors::TypeError(Box::new(IntType), Box::new(BoolType)), + CheckErrors::TypeError(Box::new(BoolType), Box::new(buff_type(20))), + CheckErrors::TypeError(Box::new(BoolType), Box::new(IntType)), CheckErrors::IncorrectArgumentCount(2, 3), CheckErrors::UnknownFunction("ynot".to_string()), CheckErrors::IllegalOrUnknownFunctionApplication("if".to_string()), CheckErrors::IncorrectArgumentCount(2, 1), - CheckErrors::UnionTypeError(vec![IntType, UIntType], BoolType), - CheckErrors::ExpectedSequence(UIntType), - CheckErrors::ExpectedSequence(IntType), + CheckErrors::UnionTypeError(vec![IntType, UIntType], Box::new(BoolType)), + CheckErrors::ExpectedSequence(Box::new(UIntType)), + CheckErrors::ExpectedSequence(Box::new(IntType)), ]; for (good_test, expected) in good.iter().zip(expected.iter()) { @@ -970,7 +989,7 @@ fn test_buff() { } for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { - assert_eq!(expected, &type_check_helper(bad_test).unwrap_err().err); + assert_eq!(*expected, *type_check_helper(bad_test).unwrap_err().err); } } @@ -1039,7 +1058,7 @@ fn test_native_as_max_len() { CheckErrors::ValueTooLarge, ]; for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { - assert_eq!(expected, &type_check_helper(bad_test).unwrap_err().err); + assert_eq!(*expected, *type_check_helper(bad_test).unwrap_err().err); } } @@ -1080,12 +1099,12 @@ fn test_native_append() { ]; let bad_expected = [ - CheckErrors::TypeError(IntType, UIntType), - CheckErrors::TypeError(UIntType, IntType), + CheckErrors::TypeError(Box::new(IntType), Box::new(UIntType)), + CheckErrors::TypeError(Box::new(UIntType), Box::new(IntType)), CheckErrors::IncorrectArgumentCount(2, 1), ]; for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { - assert_eq!(expected, &type_check_helper(bad_test).unwrap_err().err); + assert_eq!(*expected, *type_check_helper(bad_test).unwrap_err().err); } } @@ -1108,12 +1127,12 @@ fn test_native_concat() { ]; let bad_expected = [ - CheckErrors::TypeError(IntType, UIntType), - CheckErrors::TypeError(UIntType, IntType), + CheckErrors::TypeError(Box::new(IntType), Box::new(UIntType)), + CheckErrors::TypeError(Box::new(UIntType), Box::new(IntType)), CheckErrors::IncorrectArgumentCount(2, 1), ]; for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { - assert_eq!(expected, &type_check_helper(bad_test).unwrap_err().err); + assert_eq!(*expected, *type_check_helper(bad_test).unwrap_err().err); } } @@ -1206,8 +1225,8 @@ fn test_tuples() { ]; let bad_expected = [ - CheckErrors::TypeError(IntType, BoolType), - CheckErrors::TypeError(BoolType, IntType), + CheckErrors::TypeError(Box::new(IntType), Box::new(BoolType)), + CheckErrors::TypeError(Box::new(BoolType), Box::new(IntType)), ]; for (good_test, expected) in good.iter().zip(expected.iter()) { @@ -1218,7 +1237,7 @@ fn test_tuples() { } for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { - assert_eq!(expected, &type_check_helper(bad_test).unwrap_err().err); + assert_eq!(*expected, *type_check_helper(bad_test).unwrap_err().err); } } @@ -1230,7 +1249,7 @@ fn test_empty_tuple_should_fail() { "#; assert_eq!( - mem_type_check( + *mem_type_check( contract_src, ClarityVersion::Clarity1, StacksEpochId::Epoch2_05 @@ -1333,9 +1352,9 @@ fn test_simple_uints() { let bad = ["(> u1 1)", "(to-uint true)", "(to-int false)"]; let bad_expected = [ - CheckErrors::TypeError(UIntType, IntType), - CheckErrors::TypeError(IntType, BoolType), - CheckErrors::TypeError(UIntType, BoolType), + CheckErrors::TypeError(Box::new(UIntType), Box::new(IntType)), + CheckErrors::TypeError(Box::new(IntType), Box::new(BoolType)), + CheckErrors::TypeError(Box::new(UIntType), Box::new(BoolType)), ]; for (good_test, expected) in good.iter().zip(expected.iter()) { @@ -1352,10 +1371,10 @@ fn test_simple_uints() { for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { assert_eq!( - &mem_type_check(bad_test, ClarityVersion::Clarity1, StacksEpochId::Epoch2_05) + *expected, + *mem_type_check(bad_test, ClarityVersion::Clarity1, StacksEpochId::Epoch2_05) .unwrap_err() - .err, - expected + .err ); } } @@ -1397,14 +1416,14 @@ fn test_response_inference() { let bad_expected = [ CheckErrors::TypeError( - TypeSignature::from_string( + Box::new(TypeSignature::from_string( "(response bool int)", ClarityVersion::Clarity1, StacksEpochId::Epoch2_05, - ), - BoolType, + )), + Box::new(BoolType), ), - CheckErrors::ReturnTypesMustMatch(IntType, BoolType), + CheckErrors::ReturnTypesMustMatch(Box::new(IntType), Box::new(BoolType)), CheckErrors::CouldNotDetermineResponseOkType, ]; @@ -1422,10 +1441,10 @@ fn test_response_inference() { for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { assert_eq!( - &mem_type_check(bad_test, ClarityVersion::Clarity1, StacksEpochId::Epoch2_05) + *expected, + *mem_type_check(bad_test, ClarityVersion::Clarity1, StacksEpochId::Epoch2_05) .unwrap_err() - .err, - expected + .err ); } } @@ -1539,16 +1558,16 @@ fn test_options() { "; assert!( - match mem_type_check(contract, ClarityVersion::Clarity1, StacksEpochId::Epoch2_05) + match *mem_type_check(contract, ClarityVersion::Clarity1, StacksEpochId::Epoch2_05) .unwrap_err() .err { CheckErrors::TypeError(t1, t2) => { - t1 == TypeSignature::from_string( + *t1 == TypeSignature::from_string( "(optional bool)", ClarityVersion::Clarity1, StacksEpochId::Epoch2_05, - ) && t2 + ) && *t2 == TypeSignature::from_string( "(optional int)", ClarityVersion::Clarity1, @@ -1693,7 +1712,10 @@ fn test_missing_value_on_declaration_should_fail() { StacksEpochId::Epoch2_05, ) .unwrap_err(); - assert!(matches!(res.err, CheckErrors::IncorrectArgumentCount(_, _))); + assert!(matches!( + *res.err, + CheckErrors::IncorrectArgumentCount(_, _) + )); } #[test] @@ -1708,7 +1730,7 @@ fn test_mismatching_type_on_declaration_should_fail() { StacksEpochId::Epoch2_05, ) .unwrap_err(); - assert!(matches!(res.err, CheckErrors::TypeError(_, _))); + assert!(matches!(*res.err, CheckErrors::TypeError(_, _))); } #[test] @@ -1729,7 +1751,7 @@ fn test_mismatching_type_on_update_should_fail() { StacksEpochId::Epoch2_05, ) .unwrap_err(); - assert!(matches!(res.err, CheckErrors::TypeError(_, _))); + assert!(matches!(*res.err, CheckErrors::TypeError(_, _))); } #[test] @@ -1746,7 +1768,7 @@ fn test_direct_access_to_persisted_var_should_fail() { StacksEpochId::Epoch2_05, ) .unwrap_err(); - assert!(matches!(res.err, CheckErrors::UndefinedVariable(_))); + assert!(matches!(*res.err, CheckErrors::UndefinedVariable(_))); } #[test] @@ -1766,7 +1788,7 @@ fn test_data_var_shadowed_by_let_should_fail() { StacksEpochId::Epoch2_05, ) .unwrap_err(); - assert!(matches!(res.err, CheckErrors::NameAlreadyUsed(_))); + assert!(matches!(*res.err, CheckErrors::NameAlreadyUsed(_))); } #[test] @@ -1784,7 +1806,7 @@ fn test_mutating_unknown_data_var_should_fail() { StacksEpochId::Epoch2_05, ) .unwrap_err(); - assert!(matches!(res.err, CheckErrors::NoSuchDataVariable(_))); + assert!(matches!(*res.err, CheckErrors::NoSuchDataVariable(_))); } #[test] @@ -1800,7 +1822,7 @@ fn test_accessing_unknown_data_var_should_fail() { StacksEpochId::Epoch2_05, ) .unwrap_err(); - assert!(matches!(res.err, CheckErrors::NoSuchDataVariable(_))); + assert!(matches!(*res.err, CheckErrors::NoSuchDataVariable(_))); } #[test] @@ -1816,7 +1838,7 @@ fn test_let_shadowed_by_let_should_fail() { StacksEpochId::Epoch2_05, ) .unwrap_err(); - assert!(matches!(res.err, CheckErrors::NameAlreadyUsed(_))); + assert!(matches!(*res.err, CheckErrors::NameAlreadyUsed(_))); } #[test] @@ -1833,7 +1855,7 @@ fn test_let_shadowed_by_nested_let_should_fail() { StacksEpochId::Epoch2_05, ) .unwrap_err(); - assert!(matches!(res.err, CheckErrors::NameAlreadyUsed(_))); + assert!(matches!(*res.err, CheckErrors::NameAlreadyUsed(_))); } #[test] @@ -1851,7 +1873,7 @@ fn test_define_constant_shadowed_by_let_should_fail() { StacksEpochId::Epoch2_05, ) .unwrap_err(); - assert!(matches!(res.err, CheckErrors::NameAlreadyUsed(_))); + assert!(matches!(*res.err, CheckErrors::NameAlreadyUsed(_))); } #[test] @@ -1868,7 +1890,7 @@ fn test_define_constant_shadowed_by_argument_should_fail() { StacksEpochId::Epoch2_05, ) .unwrap_err(); - assert!(matches!(res.err, CheckErrors::NameAlreadyUsed(_))); + assert!(matches!(*res.err, CheckErrors::NameAlreadyUsed(_))); } #[test] @@ -2090,7 +2112,7 @@ fn test_fetch_entry_mismatching_type_signatures() { StacksEpochId::Epoch2_05, ) .unwrap_err(); - assert!(matches!(res.err, CheckErrors::TypeError(_, _))); + assert!(matches!(*res.err, CheckErrors::TypeError(_, _))); } } @@ -2110,7 +2132,7 @@ fn test_fetch_entry_unbound_variables() { StacksEpochId::Epoch2_05, ) .unwrap_err(); - assert!(matches!(res.err, CheckErrors::UndefinedVariable(_))); + assert!(matches!(*res.err, CheckErrors::UndefinedVariable(_))); } } @@ -2162,7 +2184,7 @@ fn test_insert_entry_mismatching_type_signatures() { StacksEpochId::Epoch2_05, ) .unwrap_err(); - assert!(matches!(res.err, CheckErrors::TypeError(_, _))); + assert!(matches!(*res.err, CheckErrors::TypeError(_, _))); } } @@ -2185,7 +2207,7 @@ fn test_insert_entry_unbound_variables() { StacksEpochId::Epoch2_05, ) .unwrap_err(); - assert!(matches!(res.err, CheckErrors::UndefinedVariable(_))); + assert!(matches!(*res.err, CheckErrors::UndefinedVariable(_))); } } @@ -2235,7 +2257,7 @@ fn test_delete_entry_mismatching_type_signatures() { StacksEpochId::Epoch2_05, ) .unwrap_err(); - assert!(matches!(res.err, CheckErrors::TypeError(_, _))); + assert!(matches!(*res.err, CheckErrors::TypeError(_, _))); } } @@ -2255,7 +2277,7 @@ fn test_delete_entry_unbound_variables() { StacksEpochId::Epoch2_05, ) .unwrap_err(); - assert!(matches!(res.err, CheckErrors::UndefinedVariable(_))); + assert!(matches!(*res.err, CheckErrors::UndefinedVariable(_))); } } @@ -2309,7 +2331,7 @@ fn test_set_entry_mismatching_type_signatures() { StacksEpochId::Epoch2_05, ) .unwrap_err(); - assert!(matches!(res.err, CheckErrors::TypeError(_, _))); + assert!(matches!(*res.err, CheckErrors::TypeError(_, _))); } } @@ -2332,7 +2354,7 @@ fn test_set_entry_unbound_variables() { StacksEpochId::Epoch2_05, ) .unwrap_err(); - assert!(matches!(res.err, CheckErrors::UndefinedVariable(_))); + assert!(matches!(*res.err, CheckErrors::UndefinedVariable(_))); } } @@ -2467,7 +2489,7 @@ fn test_buff_negative_len() { StacksEpochId::Epoch2_05, ) .unwrap_err(); - assert_eq!(res.err, CheckErrors::ValueOutOfBounds); + assert_eq!(*res.err, CheckErrors::ValueOutOfBounds); } #[test] @@ -2481,7 +2503,7 @@ fn test_string_ascii_negative_len() { StacksEpochId::Epoch2_05, ) .unwrap_err(); - assert_eq!(res.err, CheckErrors::ValueOutOfBounds); + assert_eq!(*res.err, CheckErrors::ValueOutOfBounds); } #[test] @@ -2495,5 +2517,5 @@ fn test_string_utf8_negative_len() { StacksEpochId::Epoch2_05, ) .unwrap_err(); - assert_eq!(res.err, CheckErrors::ValueOutOfBounds); + assert_eq!(*res.err, CheckErrors::ValueOutOfBounds); } diff --git a/clarity/src/vm/analysis/type_checker/v2_1/contexts.rs b/clarity/src/vm/analysis/type_checker/v2_1/contexts.rs index cd6812261ef..2d068554c3c 100644 --- a/clarity/src/vm/analysis/type_checker/v2_1/contexts.rs +++ b/clarity/src/vm/analysis/type_checker/v2_1/contexts.rs @@ -16,7 +16,7 @@ use std::collections::{BTreeMap, HashMap, HashSet}; -use crate::vm::analysis::errors::{CheckError, CheckErrors, CheckResult}; +use crate::vm::analysis::errors::{CheckError, CheckErrors}; use crate::vm::analysis::type_checker::is_reserved_word; use crate::vm::analysis::types::ContractAnalysis; use crate::vm::representations::ClarityName; @@ -61,7 +61,7 @@ impl TraitContext { contract_identifier: QualifiedContractIdentifier, trait_name: ClarityName, trait_signature: BTreeMap, - ) -> CheckResult<()> { + ) -> Result<(), CheckError> { match self { Self::Clarity1(map) => { map.insert(trait_name, trait_signature); @@ -85,7 +85,7 @@ impl TraitContext { alias: ClarityName, trait_id: TraitIdentifier, trait_signature: BTreeMap, - ) -> CheckResult<()> { + ) -> Result<(), CheckError> { match self { Self::Clarity1(map) => { map.insert(trait_id.name, trait_signature); @@ -169,7 +169,7 @@ impl ContractContext { &self.contract_identifier == other } - pub fn check_name_used(&self, name: &str) -> CheckResult<()> { + pub fn check_name_used(&self, name: &str) -> Result<(), CheckError> { if is_reserved_word(name, self.clarity_version) { return Err(CheckError::new(CheckErrors::ReservedWord(name.to_string()))); } @@ -191,7 +191,7 @@ impl ContractContext { } } - fn check_function_type(&mut self, f_name: &str) -> CheckResult<()> { + fn check_function_type(&mut self, f_name: &str) -> Result<(), CheckError> { self.check_name_used(f_name)?; Ok(()) } @@ -208,7 +208,7 @@ impl ContractContext { &mut self, name: ClarityName, func_type: FunctionType, - ) -> CheckResult<()> { + ) -> Result<(), CheckError> { self.check_function_type(&name)?; self.public_function_types.insert(name, func_type); Ok(()) @@ -218,7 +218,7 @@ impl ContractContext { &mut self, name: ClarityName, func_type: FunctionType, - ) -> CheckResult<()> { + ) -> Result<(), CheckError> { self.check_function_type(&name)?; self.read_only_function_types.insert(name, func_type); Ok(()) @@ -228,7 +228,7 @@ impl ContractContext { &mut self, name: ClarityName, func_type: FunctionType, - ) -> CheckResult<()> { + ) -> Result<(), CheckError> { self.check_function_type(&name)?; self.private_function_types.insert(name, func_type); Ok(()) @@ -238,7 +238,7 @@ impl ContractContext { &mut self, map_name: ClarityName, map_type: (TypeSignature, TypeSignature), - ) -> CheckResult<()> { + ) -> Result<(), CheckError> { self.check_name_used(&map_name)?; self.map_types.insert(map_name, map_type); Ok(()) @@ -248,7 +248,7 @@ impl ContractContext { &mut self, const_name: ClarityName, var_type: TypeSignature, - ) -> CheckResult<()> { + ) -> Result<(), CheckError> { self.check_name_used(&const_name)?; self.variable_types.insert(const_name, var_type); Ok(()) @@ -258,13 +258,13 @@ impl ContractContext { &mut self, var_name: ClarityName, var_type: TypeSignature, - ) -> CheckResult<()> { + ) -> Result<(), CheckError> { self.check_name_used(&var_name)?; self.persisted_variable_types.insert(var_name, var_type); Ok(()) } - pub fn add_ft(&mut self, token_name: ClarityName) -> CheckResult<()> { + pub fn add_ft(&mut self, token_name: ClarityName) -> Result<(), CheckError> { self.check_name_used(&token_name)?; self.fungible_tokens.insert(token_name); Ok(()) @@ -274,7 +274,7 @@ impl ContractContext { &mut self, token_name: ClarityName, token_type: TypeSignature, - ) -> CheckResult<()> { + ) -> Result<(), CheckError> { self.check_name_used(&token_name)?; self.non_fungible_tokens.insert(token_name, token_type); Ok(()) @@ -284,7 +284,7 @@ impl ContractContext { &mut self, trait_name: ClarityName, trait_signature: BTreeMap, - ) -> CheckResult<()> { + ) -> Result<(), CheckError> { if self.clarity_version >= ClarityVersion::Clarity3 { self.check_name_used(&trait_name)?; } @@ -301,7 +301,7 @@ impl ContractContext { alias: ClarityName, trait_id: TraitIdentifier, trait_signature: BTreeMap, - ) -> CheckResult<()> { + ) -> Result<(), CheckError> { if self.clarity_version >= ClarityVersion::Clarity3 { self.check_name_used(&alias)?; } @@ -309,7 +309,10 @@ impl ContractContext { self.traits.add_used_trait(alias, trait_id, trait_signature) } - pub fn add_implemented_trait(&mut self, trait_identifier: TraitIdentifier) -> CheckResult<()> { + pub fn add_implemented_trait( + &mut self, + trait_identifier: TraitIdentifier, + ) -> Result<(), CheckError> { self.implemented_traits.insert(trait_identifier); Ok(()) } 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 094b39c26a4..131186958c0 100644 --- a/clarity/src/vm/analysis/type_checker/v2_1/mod.rs +++ b/clarity/src/vm/analysis/type_checker/v2_1/mod.rs @@ -27,7 +27,7 @@ use super::contexts::{TypeMap, TypingContext}; use super::ContractAnalysis; pub use crate::vm::analysis::errors::{ check_argument_count, check_arguments_at_least, check_arguments_at_most, CheckError, - CheckErrors, CheckResult, SyntaxBindingErrorType, + CheckErrors, SyntaxBindingErrorType, }; use crate::vm::analysis::AnalysisDatabase; use crate::vm::costs::cost_functions::ClarityCostFunction; @@ -121,7 +121,7 @@ impl TypeChecker<'_, '_> { contract_analysis: &mut ContractAnalysis, analysis_db: &mut AnalysisDatabase, build_type_map: bool, - ) -> CheckResult<()> { + ) -> Result<(), CheckError> { let cost_track = contract_analysis.take_contract_cost_tracker(); let mut command = TypeChecker::new( epoch, @@ -148,8 +148,6 @@ impl TypeChecker<'_, '_> { } } -pub type TypeResult = CheckResult; - pub fn compute_typecheck_cost( track: &mut T, t1: &TypeSignature, @@ -172,6 +170,7 @@ pub fn check_argument_len(expected: usize, args_len: usize) -> Result<(), CheckE } impl FunctionType { + #[allow(clippy::type_complexity)] pub fn check_args_visitor_2_1( &self, accounting: &mut T, @@ -180,7 +179,7 @@ impl FunctionType { accumulated_type: Option<&TypeSignature>, ) -> ( Option>, - CheckResult>, + Result, CheckError>, ) { match self { // variadic stops checking cost at the first error... @@ -193,7 +192,11 @@ impl FunctionType { if !admitted { return ( cost, - Err(CheckErrors::TypeError(expected_type.clone(), arg_type.clone()).into()), + Err(CheckErrors::TypeError( + Box::new(expected_type.clone()), + Box::new(arg_type.clone()), + ) + .into()), ); } (cost, Ok(None)) @@ -210,7 +213,7 @@ impl FunctionType { TypeSignature::UIntType => Ok(Some(TypeSignature::UIntType)), _ => Err(CheckErrors::UnionTypeError( vec![TypeSignature::IntType, TypeSignature::UIntType], - arg_type.clone(), + Box::new(arg_type.clone()), ) .into()), }; @@ -220,10 +223,11 @@ impl FunctionType { .ok_or_else(|| CheckErrors::Expects("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( - CheckErrors::TypeError(return_type.clone(), arg_type.clone()) - .into(), + Err(CheckErrors::TypeError( + Box::new(return_type.clone()), + Box::new(arg_type.clone()), ) + .into()) } else { Ok(None) } @@ -279,7 +283,7 @@ impl FunctionType { accounting: &mut T, args: &[TypeSignature], clarity_version: ClarityVersion, - ) -> CheckResult { + ) -> Result { match self { FunctionType::Variadic(expected_type, return_type) => { check_arguments_at_least(1, args)?; @@ -287,8 +291,8 @@ impl FunctionType { analysis_typecheck_cost(accounting, expected_type, found_type)?; if !expected_type.admits_type(&StacksEpochId::Epoch21, found_type)? { return Err(CheckErrors::TypeError( - expected_type.clone(), - found_type.clone(), + Box::new(expected_type.clone()), + Box::new(found_type.clone()), ) .into()); } @@ -305,8 +309,8 @@ impl FunctionType { analysis_typecheck_cost(accounting, expected_type, found_type)?; if !expected_type.admits_type(&StacksEpochId::Epoch21, found_type)? { return Err(CheckErrors::TypeError( - expected_type.clone(), - found_type.clone(), + Box::new(expected_type.clone()), + Box::new(found_type.clone()), ) .into()); } @@ -322,7 +326,10 @@ impl FunctionType { return Ok(return_type.clone()); } } - Err(CheckErrors::UnionTypeError(arg_types.clone(), found_type.clone()).into()) + Err( + CheckErrors::UnionTypeError(arg_types.clone(), Box::new(found_type.clone())) + .into(), + ) } FunctionType::Binary(left_arg_sig, right_arg_sig, return_sig) => { check_argument_count(2, args)?; @@ -356,13 +363,17 @@ impl FunctionType { TypeSignature::UIntType => Ok(TypeSignature::UIntType), _ => Err(CheckErrors::UnionTypeError( vec![TypeSignature::IntType, TypeSignature::UIntType], - first.clone(), + Box::new(first.clone()), )), }?; for found_type in rest.iter() { analysis_typecheck_cost(accounting, &TypeSignature::IntType, found_type)?; if found_type != &return_type { - return Err(CheckErrors::TypeError(return_type, found_type.clone()).into()); + return Err(CheckErrors::TypeError( + Box::new(return_type.clone()), + Box::new(found_type.clone()), + ) + .into()); } } Ok(return_type) @@ -400,7 +411,7 @@ impl FunctionType { TypeSignature::max_string_utf8()?, TypeSignature::max_buffer()?, ], - first.clone(), + Box::new(first.clone()), ) .into()); } @@ -434,7 +445,11 @@ impl FunctionType { }; if !pair_of_types_matches { - return Err(CheckErrors::TypeError(first.clone(), second.clone()).into()); + return Err(CheckErrors::TypeError( + Box::new(first.clone()), + Box::new(second.clone()), + ) + .into()); } Ok(TypeSignature::BoolType) @@ -451,7 +466,7 @@ impl FunctionType { value: &Value, depth: u8, clarity_version: ClarityVersion, - ) -> TypeResult { + ) -> Result { if clarity_version >= ClarityVersion::Clarity2 { // In Clarity2, we recurse into complex data types self.clarity2_principal_to_callable_type(value, depth) @@ -469,7 +484,11 @@ impl FunctionType { } #[allow(clippy::only_used_in_recursion)] - fn clarity2_principal_to_callable_type(&self, value: &Value, depth: u8) -> TypeResult { + fn clarity2_principal_to_callable_type( + &self, + value: &Value, + depth: u8, + ) -> Result { if depth > MAX_TYPE_DEPTH { return Err(CheckErrors::TypeSignatureTooDeep.into()); } @@ -538,7 +557,7 @@ impl FunctionType { db: &mut AnalysisDatabase, clarity_version: ClarityVersion, func_args: &[Value], - ) -> CheckResult { + ) -> Result { let (expected_args, returns) = match self { FunctionType::Fixed(FixedFunction { args, returns }) => (args, returns), _ => return Err(CheckErrors::Expects("Unexpected function type".into()).into()), @@ -576,9 +595,11 @@ impl FunctionType { (expected_type, value) => { if !expected_type.admits(&StacksEpochId::Epoch21, value)? { let actual_type = TypeSignature::type_of(value)?; - return Err( - CheckErrors::TypeError(expected_type.clone(), actual_type).into() - ); + return Err(CheckErrors::TypeError( + Box::new(expected_type.clone()), + Box::new(actual_type.clone()), + ) + .into()); } } } @@ -608,14 +629,16 @@ fn check_function_arg_signature( cost_tracker: &mut T, expected_sig: &FunctionArgSignature, actual_type: &TypeSignature, -) -> CheckResult<()> { +) -> Result<(), CheckError> { 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)? { - return Err( - CheckErrors::TypeError(expected_type.clone(), actual_type.clone()).into(), - ); + return Err(CheckErrors::TypeError( + Box::new(expected_type.clone()), + Box::new(actual_type.clone()), + ) + .into()); } } FunctionArgSignature::Union(expected_types) => { @@ -630,7 +653,7 @@ fn check_function_arg_signature( if !admitted { return Err(CheckErrors::UnionTypeError( expected_types.clone(), - actual_type.clone(), + Box::new(actual_type.clone()), ) .into()); } @@ -694,7 +717,7 @@ pub fn clarity2_trait_check_trait_compliance( expected_trait_identifier: &TraitIdentifier, expected_trait: &BTreeMap, tracker: &mut T, -) -> CheckResult<()> { +) -> Result<(), CheckError> { // Shortcut for the simple case when the two traits are the same. if actual_trait_identifier == expected_trait_identifier { return Ok(()); @@ -710,15 +733,15 @@ pub fn clarity2_trait_check_trait_compliance( tracker, ) { return Err(CheckErrors::IncompatibleTrait( - expected_trait_identifier.clone(), - actual_trait_identifier.clone(), + Box::new(expected_trait_identifier.clone()), + Box::new(actual_trait_identifier.clone()), ) .into()); } } else { return Err(CheckErrors::IncompatibleTrait( - expected_trait_identifier.clone(), - actual_trait_identifier.clone(), + Box::new(expected_trait_identifier.clone()), + Box::new(actual_trait_identifier.clone()), ) .into()); } @@ -735,7 +758,7 @@ fn clarity2_inner_type_check_type( expected_type: &TypeSignature, depth: u8, tracker: &mut T, -) -> TypeResult { +) -> Result { if depth > MAX_TYPE_DEPTH { return Err(CheckErrors::TypeSignatureTooDeep.into()); } @@ -790,9 +813,11 @@ fn clarity2_inner_type_check_type( tracker, )?; } else { - return Err( - CheckErrors::TypeError(expected_type.clone(), actual_type.clone()).into(), - ); + return Err(CheckErrors::TypeError( + Box::new(expected_type.clone()), + Box::new(actual_type.clone()), + ) + .into()); } } ( @@ -800,9 +825,11 @@ fn clarity2_inner_type_check_type( TypeSignature::TupleType(expected_tuple_type), ) => { if expected_tuple_type.get_type_map().len() != atom_tuple_type.get_type_map().len() { - return Err( - CheckErrors::TypeError(expected_type.clone(), actual_type.clone()).into(), - ); + return Err(CheckErrors::TypeError( + Box::new(expected_type.clone()), + Box::new(actual_type.clone()), + ) + .into()); } for (name, expected_field_type) in expected_tuple_type.get_type_map() { @@ -819,8 +846,8 @@ fn clarity2_inner_type_check_type( } None => { return Err(CheckErrors::TypeError( - expected_type.clone(), - actual_type.clone(), + Box::new(expected_type.clone()), + Box::new(actual_type.clone()), ) .into()); } @@ -894,9 +921,11 @@ fn clarity2_inner_type_check_type( (TypeSignature::NoType, _) => (), (_, _) => { if !expected_type.admits_type(&StacksEpochId::Epoch21, actual_type)? { - return Err( - CheckErrors::TypeError(expected_type.clone(), actual_type.clone()).into(), - ); + return Err(CheckErrors::TypeError( + Box::new(expected_type.clone()), + Box::new(actual_type.clone()), + ) + .into()); } } } @@ -908,7 +937,7 @@ fn clarity2_lookup_trait( contract_context: Option<&ContractContext>, trait_id: &TraitIdentifier, tracker: &mut T, -) -> CheckResult> { +) -> Result, CheckError> { if let Some(contract_context) = contract_context { // If the trait is from this contract, then it must be in the context or it doesn't exist. if contract_context.is_contract(&trait_id.contract_identifier) { @@ -954,7 +983,9 @@ fn clarity2_lookup_trait( } } -fn trait_type_size(trait_sig: &BTreeMap) -> CheckResult { +fn trait_type_size( + trait_sig: &BTreeMap, +) -> Result { let mut total_size = 0; for (_func_name, value) in trait_sig.iter() { total_size = total_size.cost_overflow_add(value.total_type_size()?)?; @@ -962,7 +993,7 @@ fn trait_type_size(trait_sig: &BTreeMap) -> Chec Ok(total_size) } -fn contract_analysis_size(contract: &ContractAnalysis) -> CheckResult { +fn contract_analysis_size(contract: &ContractAnalysis) -> Result { let mut total_size = contract.public_function_types.len() as u64; total_size = total_size.cost_overflow_add(contract.read_only_function_types.len() as u64)?; Ok(total_size) @@ -971,7 +1002,7 @@ fn contract_analysis_size(contract: &ContractAnalysis) -> CheckResult { fn type_reserved_variable( variable_name: &str, version: &ClarityVersion, -) -> CheckResult> { +) -> Result, CheckError> { if let Some(variable) = NativeVariables::lookup_by_name_at_version(variable_name, version) { use crate::vm::variables::NativeVariables::*; let var_type = match variable { @@ -991,6 +1022,8 @@ fn type_reserved_variable( Regtest => TypeSignature::BoolType, Mainnet => TypeSignature::BoolType, ChainId => TypeSignature::UIntType, + CurrentContract => TypeSignature::PrincipalType, + BlockTime => TypeSignature::UIntType, }; Ok(Some(var_type)) } else { @@ -1032,7 +1065,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { self.cost_track } - pub fn track_return_type(&mut self, return_type: TypeSignature) -> CheckResult<()> { + pub fn track_return_type(&mut self, return_type: TypeSignature) -> Result<(), CheckError> { runtime_cost( ClarityCostFunction::AnalysisTypeCheck, self, @@ -1047,7 +1080,12 @@ impl<'a, 'b> TypeChecker<'a, 'b> { &expected_type, &return_type, ) - .map_err(|_| CheckErrors::ReturnTypesMustMatch(expected_type, return_type))?, + .map_err(|_| { + CheckErrors::ReturnTypesMustMatch( + Box::new(expected_type), + Box::new(return_type), + ) + })?, None => return_type, }; @@ -1062,7 +1100,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { } } - pub fn run(&mut self, contract_analysis: &ContractAnalysis) -> CheckResult<()> { + pub fn run(&mut self, contract_analysis: &ContractAnalysis) -> Result<(), CheckError> { // charge for the eventual storage cost of the analysis -- // it is linear in the size of the AST. let mut size: u64 = 0; @@ -1103,7 +1141,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { expr: &SymbolicExpression, context: &TypingContext, expected_type: &TypeSignature, - ) -> TypeResult { + ) -> Result { // Clarity 2 allows traits embedded in compound types and allows // implicit casts between compatible traits, while Clarity 1 does not. if self.clarity_version >= ClarityVersion::Clarity2 { @@ -1120,7 +1158,11 @@ impl<'a, 'b> TypeChecker<'a, 'b> { } // Type checks an expression, recursively type checking its subexpressions - pub fn type_check(&mut self, expr: &SymbolicExpression, context: &TypingContext) -> TypeResult { + pub fn type_check( + &mut self, + expr: &SymbolicExpression, + context: &TypingContext, + ) -> Result { runtime_cost(ClarityCostFunction::AnalysisVisit, self, 0)?; let mut result = self.inner_type_check(expr, context); @@ -1138,7 +1180,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { &mut self, args: &[SymbolicExpression], context: &TypingContext, - ) -> TypeResult { + ) -> Result { let mut last_return = None; let mut return_failure = Ok(()); for ix in 0..args.len() { @@ -1163,7 +1205,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { &mut self, args: &[SymbolicExpression], context: &TypingContext, - ) -> CheckResult> { + ) -> Result, CheckError> { let mut result = Vec::with_capacity(args.len()); for arg in args.iter() { // don't use map here, since type_check has side-effects. @@ -1179,7 +1221,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { context: &TypingContext, epoch: StacksEpochId, clarity_version: ClarityVersion, - ) -> TypeResult { + ) -> Result { if epoch <= StacksEpochId::Epoch2_05 { let typed_args = self.type_check_all(args, context)?; return func_type.check_args(self, &typed_args, epoch, clarity_version); @@ -1216,9 +1258,10 @@ impl<'a, 'b> TypeChecker<'a, 'b> { } } if let Err(mut check_error) = check_result { - if let CheckErrors::IncorrectArgumentCount(expected, _actual) = check_error.err { - check_error.err = CheckErrors::IncorrectArgumentCount(expected, args.len()); - check_error.diagnostic = Diagnostic::err(&check_error.err) + if let CheckErrors::IncorrectArgumentCount(expected, _actual) = *check_error.err { + check_error.err = + Box::new(CheckErrors::IncorrectArgumentCount(expected, args.len())); + check_error.diagnostic = Diagnostic::err(check_error.err.as_ref()); } // accumulate the checking costs // the reason we do this now (instead of within the loop) is for backwards compatibility @@ -1244,7 +1287,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { signature: &[SymbolicExpression], body: &SymbolicExpression, context: &TypingContext, - ) -> CheckResult<(ClarityName, FixedFunction)> { + ) -> Result<(ClarityName, FixedFunction), CheckError> { let (function_name, args) = signature .split_first() .ok_or(CheckErrors::RequiresAtLeastArguments(1, 0))?; @@ -1315,7 +1358,10 @@ impl<'a, 'b> TypeChecker<'a, 'b> { &return_type, ) .map_err(|_| { - CheckErrors::ReturnTypesMustMatch(expected.clone(), return_type) + CheckErrors::ReturnTypesMustMatch( + Box::new(expected.clone()), + Box::new(return_type), + ) })? } else { return_type @@ -1346,7 +1392,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { map_name: &ClarityName, key_type: &SymbolicExpression, value_type: &SymbolicExpression, - ) -> CheckResult<(ClarityName, (TypeSignature, TypeSignature))> { + ) -> Result<(ClarityName, (TypeSignature, TypeSignature)), CheckError> { self.type_map.set_type(key_type, no_type())?; self.type_map.set_type(value_type, no_type())?; // should we set the type of the subexpressions of the signature to no-type as well? @@ -1366,7 +1412,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { function: &str, args: &[SymbolicExpression], context: &TypingContext, - ) -> Option { + ) -> Option> { if let Some(ref native_function) = NativeFunctions::lookup_by_name_at_version(function, &self.clarity_version) { @@ -1384,7 +1430,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { &mut self, expression: &[SymbolicExpression], context: &TypingContext, - ) -> TypeResult { + ) -> Result { let (function_name, args) = expression .split_first() .ok_or(CheckErrors::NonFunctionApplication)?; @@ -1411,7 +1457,11 @@ impl<'a, 'b> TypeChecker<'a, 'b> { } } - fn lookup_variable(&mut self, name: &str, context: &TypingContext) -> TypeResult { + fn lookup_variable( + &mut self, + name: &str, + context: &TypingContext, + ) -> Result { runtime_cost(ClarityCostFunction::AnalysisLookupVariableConst, self, 0)?; if let Some(type_result) = type_reserved_variable(name, &self.clarity_version)? { @@ -1423,6 +1473,13 @@ impl<'a, 'b> TypeChecker<'a, 'b> { type_result.clone(), ))) } else { + // Special case where a top-level is being looked up, which must + // be undefined. This early error prevents a cost function error + // due to `context.depth` being 0. + if context.depth == 0 { + return Err(CheckErrors::UndefinedVariable(name.to_string()).into()); + } + runtime_cost( ClarityCostFunction::AnalysisLookupVariableDepth, self, @@ -1442,7 +1499,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { expr: &SymbolicExpression, context: &TypingContext, expected_type: &TypeSignature, - ) -> TypeResult { + ) -> Result { if let ( LiteralValue(Value::Principal(PrincipalData::Contract(ref contract_identifier))), TypeSignature::CallableType(CallableSubtype::Trait(trait_identifier)), @@ -1482,8 +1539,11 @@ impl<'a, 'b> TypeChecker<'a, 'b> { analysis_typecheck_cost(self, expected_type, &actual_type)?; if !expected_type.admits_type(&StacksEpochId::Epoch21, &actual_type)? { - let mut err: CheckError = - CheckErrors::TypeError(expected_type.clone(), actual_type).into(); + let mut err: CheckError = CheckErrors::TypeError( + Box::new(expected_type.clone()), + Box::new(actual_type.clone()), + ) + .into(); err.set_expression(expr); Err(err) } else { @@ -1496,7 +1556,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { expr: &SymbolicExpression, context: &TypingContext, expected_type: &TypeSignature, - ) -> TypeResult { + ) -> Result { let mut expr_type = match expr.expr { AtomValue(ref value) => TypeSignature::type_of(value)?, LiteralValue(ref value) => TypeSignature::literal_type_of(value)?, @@ -1534,7 +1594,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { &mut self, expr: &SymbolicExpression, context: &TypingContext, - ) -> TypeResult { + ) -> Result { let expr_type = match expr.expr { AtomValue(ref value) => TypeSignature::type_of(value)?, LiteralValue(ref value) => TypeSignature::literal_type_of(value)?, @@ -1559,7 +1619,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { var_name: &ClarityName, var_type: &SymbolicExpression, context: &mut TypingContext, - ) -> CheckResult<(ClarityName, TypeSignature)> { + ) -> Result<(ClarityName, TypeSignature), CheckError> { let var_type = self.type_check(var_type, context)?; Ok((var_name.clone(), var_type)) } @@ -1570,7 +1630,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { var_type: &SymbolicExpression, initial: &SymbolicExpression, context: &mut TypingContext, - ) -> CheckResult<(ClarityName, TypeSignature)> { + ) -> Result<(ClarityName, TypeSignature), CheckError> { let expected_type = TypeSignature::parse_type_repr::<()>(StacksEpochId::Epoch21, var_type, &mut ()) .map_err(|_e| CheckErrors::DefineVariableBadSignature)?; @@ -1585,7 +1645,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { token_name: &ClarityName, bound: Option<&SymbolicExpression>, context: &mut TypingContext, - ) -> CheckResult { + ) -> Result { if let Some(bound) = bound { self.type_check_expects(bound, context, &TypeSignature::UIntType)?; } @@ -1598,7 +1658,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { asset_name: &ClarityName, nft_type: &SymbolicExpression, _context: &mut TypingContext, - ) -> CheckResult<(ClarityName, TypeSignature)> { + ) -> Result<(ClarityName, TypeSignature), CheckError> { let asset_type = TypeSignature::parse_type_repr::<()>(StacksEpochId::Epoch21, nft_type, &mut ()) .map_err(|_| CheckErrors::DefineNFTBadSignature)?; @@ -1611,7 +1671,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { trait_name: &ClarityName, function_types: &[SymbolicExpression], _context: &mut TypingContext, - ) -> CheckResult<(ClarityName, BTreeMap)> { + ) -> Result<(ClarityName, BTreeMap), CheckError> { let trait_signature = TypeSignature::parse_trait_type_repr( function_types, &mut (), @@ -1627,7 +1687,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> { &mut self, expression: &SymbolicExpression, context: &mut TypingContext, - ) -> CheckResult> { + ) -> Result, CheckError> { if let Some(define_type) = DefineFunctionsParsed::try_parse(expression)? { match define_type { DefineFunctionsParsed::Constant { name, value } => { @@ -1676,9 +1736,10 @@ impl<'a, 'b> TypeChecker<'a, 'b> { .add_public_function_type(f_name, FunctionType::Fixed(f_type))?; return Ok(Some(())); } else { - return Err( - CheckErrors::PublicFunctionMustReturnResponse(f_type.returns).into(), - ); + return Err(CheckErrors::PublicFunctionMustReturnResponse(Box::new( + f_type.returns, + )) + .into()); } } DefineFunctionsParsed::ReadOnlyFunction { signature, body } => { 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 f91c64e1c09..fbd2e251c49 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 @@ -16,8 +16,8 @@ use stacks_common::consts::TOKEN_TRANSFER_MEMO_LENGTH; -use super::{TypeChecker, TypeResult, TypingContext}; -use crate::vm::analysis::errors::{check_argument_count, CheckErrors}; +use super::{TypeChecker, TypingContext}; +use crate::vm::analysis::errors::{check_argument_count, CheckError, CheckErrors}; use crate::vm::costs::cost_functions::ClarityCostFunction; use crate::vm::costs::runtime_cost; use crate::vm::representations::SymbolicExpression; @@ -27,7 +27,7 @@ pub fn check_special_get_owner( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(2, args)?; let asset_name = args[0].match_atom().ok_or(CheckErrors::BadTokenName)?; @@ -55,7 +55,7 @@ pub fn check_special_get_balance( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(2, args)?; let asset_name = args[0].match_atom().ok_or(CheckErrors::BadTokenName)?; @@ -76,7 +76,7 @@ pub fn check_special_mint_asset( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(3, args)?; let asset_name = args[0].match_atom().ok_or(CheckErrors::BadTokenName)?; @@ -107,7 +107,7 @@ pub fn check_special_mint_token( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(3, args)?; let asset_name = args[0].match_atom().ok_or(CheckErrors::BadTokenName)?; @@ -134,7 +134,7 @@ pub fn check_special_transfer_asset( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(4, args)?; let token_name = args[0].match_atom().ok_or(CheckErrors::BadTokenName)?; @@ -166,7 +166,7 @@ pub fn check_special_transfer_token( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(4, args)?; let token_name = args[0].match_atom().ok_or(CheckErrors::BadTokenName)?; @@ -194,7 +194,7 @@ pub fn check_special_stx_transfer( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(3, args)?; let amount_type: TypeSignature = TypeSignature::UIntType; @@ -217,7 +217,7 @@ pub fn check_special_stx_transfer_memo( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(4, args)?; let amount_type: TypeSignature = TypeSignature::UIntType; @@ -245,7 +245,7 @@ pub fn check_special_get_token_supply( checker: &mut TypeChecker, args: &[SymbolicExpression], _context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(1, args)?; let asset_name = args[0].match_atom().ok_or(CheckErrors::BadTokenName)?; @@ -263,7 +263,7 @@ pub fn check_special_burn_asset( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(3, args)?; let asset_name = args[0].match_atom().ok_or(CheckErrors::BadTokenName)?; @@ -294,7 +294,7 @@ pub fn check_special_burn_token( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(3, args)?; let asset_name = args[0].match_atom().ok_or(CheckErrors::BadTokenName)?; 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 89866b5a91b..88f5470d530 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 @@ -1,6 +1,6 @@ use stacks_common::types::StacksEpochId; -use super::{TypeChecker, TypeResult}; +use super::TypeChecker; use crate::vm::analysis::read_only_checker::check_argument_count; use crate::vm::analysis::type_checker::contexts::TypingContext; use crate::vm::analysis::CheckError; @@ -16,7 +16,7 @@ pub fn check_special_to_consensus_buff( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> 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()?)?; @@ -35,7 +35,7 @@ pub fn check_special_from_consensus_buff( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { 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::max_buffer()?)?; 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 676badd14f1..2dbc2407983 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 @@ -17,7 +17,7 @@ use stacks_common::types::StacksEpochId; use crate::vm::analysis::type_checker::v2_1::{ - check_arguments_at_least, CheckError, CheckErrors, TypeChecker, TypeResult, TypingContext, + check_arguments_at_least, CheckError, CheckErrors, TypeChecker, TypingContext, }; use crate::vm::costs::cost_functions::ClarityCostFunction; use crate::vm::costs::{analysis_typecheck_cost, runtime_cost}; @@ -28,7 +28,7 @@ pub fn check_special_fetch_entry( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_arguments_at_least(2, args)?; let map_name = args[0].match_atom().ok_or(CheckErrors::BadMapName)?; @@ -56,8 +56,8 @@ pub fn check_special_fetch_entry( if !expected_key_type.admits_type(&StacksEpochId::Epoch21, &key_type)? { Err(CheckError::new(CheckErrors::TypeError( - expected_key_type.clone(), - key_type, + Box::new(expected_key_type.clone()), + Box::new(key_type), ))) } else { Ok(option_type) @@ -68,7 +68,7 @@ pub fn check_special_delete_entry( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_arguments_at_least(2, args)?; let map_name = args[0].match_atom().ok_or(CheckErrors::BadMapName)?; @@ -89,8 +89,8 @@ pub fn check_special_delete_entry( if !expected_key_type.admits_type(&StacksEpochId::Epoch21, &key_type)? { Err(CheckError::new(CheckErrors::TypeError( - expected_key_type.clone(), - key_type, + Box::new(expected_key_type.clone()), + Box::new(key_type), ))) } else { Ok(TypeSignature::BoolType) @@ -101,7 +101,7 @@ fn check_set_or_insert_entry( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_arguments_at_least(3, args)?; let map_name = args[0].match_atom().ok_or(CheckErrors::BadMapName)?; @@ -130,13 +130,13 @@ fn check_set_or_insert_entry( if !expected_key_type.admits_type(&StacksEpochId::Epoch21, &key_type)? { Err(CheckError::new(CheckErrors::TypeError( - expected_key_type.clone(), - key_type, + Box::new(expected_key_type.clone()), + Box::new(key_type), ))) } else if !expected_value_type.admits_type(&StacksEpochId::Epoch21, &value_type)? { Err(CheckError::new(CheckErrors::TypeError( - expected_value_type.clone(), - value_type, + Box::new(expected_value_type.clone()), + Box::new(value_type), ))) } else { Ok(TypeSignature::BoolType) @@ -147,7 +147,7 @@ pub fn check_special_set_entry( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_set_or_insert_entry(checker, args, context) } @@ -155,6 +155,6 @@ pub fn check_special_insert_entry( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_set_or_insert_entry(checker, args, context) } 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 30dfc8280cd..161e6a5689d 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 @@ -18,7 +18,7 @@ use stacks_common::types::StacksEpochId; use super::{ check_argument_count, check_arguments_at_least, check_arguments_at_most, - compute_typecheck_cost, no_type, TypeChecker, TypeResult, TypingContext, + compute_typecheck_cost, no_type, TypeChecker, TypingContext, }; use crate::vm::analysis::errors::{CheckError, CheckErrors, SyntaxBindingErrorType}; use crate::vm::costs::cost_functions::ClarityCostFunction; @@ -27,7 +27,7 @@ use crate::vm::diagnostic::DiagnosableError; use crate::vm::functions::{handle_binding_list, NativeFunctions}; use crate::vm::types::signatures::{ CallableSubtype, FunctionArgSignature, FunctionReturnsSignature, SequenceSubtype, ASCII_40, - UTF8_40, + TO_ASCII_MAX_BUFF, TO_ASCII_RESPONSE_STRING, UTF8_40, }; use crate::vm::types::{ BlockInfoProperty, BufferLength, BurnBlockInfoProperty, FixedFunction, FunctionArg, @@ -49,8 +49,13 @@ pub enum TypedNativeFunction { Simple(SimpleNativeFunction), } +#[allow(clippy::type_complexity)] pub struct SpecialNativeFunction( - &'static dyn Fn(&mut TypeChecker, &[SymbolicExpression], &TypingContext) -> TypeResult, + &'static dyn Fn( + &mut TypeChecker, + &[SymbolicExpression], + &TypingContext, + ) -> Result, ); pub struct SimpleNativeFunction(pub FunctionType); @@ -58,7 +63,7 @@ fn check_special_list_cons( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { let mut result = Vec::with_capacity(args.len()); let mut entries_size: Option = Some(0); let mut costs = Vec::with_capacity(args.len()); @@ -105,7 +110,7 @@ fn check_special_print( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(1, args)?; checker.type_check(&args[0], context) } @@ -114,7 +119,7 @@ fn check_special_as_contract( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(1, args)?; checker.type_check(&args[0], context) } @@ -123,7 +128,7 @@ fn check_special_at_block( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(2, args)?; checker.type_check_expects(&args[0], context, &BUFF_32)?; checker.type_check(&args[1], context) @@ -133,7 +138,7 @@ fn check_special_begin( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_arguments_at_least(1, args)?; checker.type_check_consecutive_statements(args, context) @@ -143,7 +148,7 @@ fn inner_handle_tuple_get( tuple_type_sig: &TupleTypeSignature, field_to_get: &str, checker: &mut TypeChecker, -) -> TypeResult { +) -> Result { runtime_cost( ClarityCostFunction::AnalysisCheckTupleGet, checker, @@ -164,7 +169,7 @@ fn check_special_get( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(2, args)?; let field_to_get = args[0].match_atom().ok_or(CheckErrors::BadTupleFieldName)?; @@ -179,10 +184,10 @@ fn check_special_get( let option_type = TypeSignature::new_option(inner_type)?; Ok(option_type) } else { - Err(CheckErrors::ExpectedTuple(*value_type_sig).into()) + Err(CheckErrors::ExpectedTuple(value_type_sig).into()) } } else { - Err(CheckErrors::ExpectedTuple(argument_type).into()) + Err(CheckErrors::ExpectedTuple(Box::new(argument_type)).into()) } } @@ -190,19 +195,19 @@ fn check_special_merge( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(2, args)?; let res = checker.type_check(&args[0], context)?; let mut base = match res { TypeSignature::TupleType(tuple_sig) => Ok(tuple_sig), - _ => Err(CheckErrors::ExpectedTuple(res.clone())), + _ => Err(CheckErrors::ExpectedTuple(Box::new(res.clone()))), }?; let res = checker.type_check(&args[1], context)?; let mut update = match res { TypeSignature::TupleType(tuple_sig) => Ok(tuple_sig), - _ => Err(CheckErrors::ExpectedTuple(res.clone())), + _ => Err(CheckErrors::ExpectedTuple(Box::new(res.clone()))), }?; runtime_cost( ClarityCostFunction::AnalysisCheckTupleMerge, @@ -218,7 +223,7 @@ pub fn check_special_tuple_cons( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_arguments_at_least(1, args)?; let mut tuple_type_data = Vec::with_capacity(args.len()); @@ -271,7 +276,7 @@ fn check_special_let( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_arguments_at_least(2, args)?; let binding_list = args[0] @@ -326,7 +331,7 @@ fn check_special_fetch_var( checker: &mut TypeChecker, args: &[SymbolicExpression], _context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(1, args)?; let var_name = args[0] @@ -353,7 +358,7 @@ fn check_special_set_var( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_arguments_at_least(2, args)?; let var_name = args[0].match_atom().ok_or(CheckErrors::BadMapName)?; @@ -374,8 +379,8 @@ fn check_special_set_var( if !expected_value_type.admits_type(&StacksEpochId::Epoch21, &value_type)? { Err(CheckError::new(CheckErrors::TypeError( - expected_value_type.clone(), - value_type, + Box::new(expected_value_type.clone()), + Box::new(value_type), ))) } else { Ok(TypeSignature::BoolType) @@ -386,7 +391,7 @@ fn check_special_equals( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_arguments_at_least(1, args)?; let mut arg_type = None; @@ -402,7 +407,7 @@ fn check_special_equals( costs.push(cost); arg_type = Some( TypeSignature::least_supertype(&StacksEpochId::Epoch21, &x_type, &cur_type) - .map_err(|_| CheckErrors::TypeError(x_type, cur_type)), + .map_err(|_| CheckErrors::TypeError(Box::new(x_type), Box::new(cur_type))), ); } } @@ -423,7 +428,7 @@ fn check_special_if( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(3, args)?; checker.type_check_expects(&args[0], context, &TypeSignature::BoolType)?; @@ -437,14 +442,16 @@ fn check_special_if( TypeSignature::least_supertype(&StacksEpochId::Epoch21, expr1, expr2) .and_then(|t| t.concretize()) - .map_err(|_| CheckErrors::IfArmsMustMatch(expr1.clone(), expr2.clone()).into()) + .map_err(|_| { + CheckErrors::IfArmsMustMatch(Box::new(expr1.clone()), Box::new(expr2.clone())).into() + }) } fn check_contract_call( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_arguments_at_least(2, args)?; let func_name = args[1] @@ -566,7 +573,9 @@ fn check_contract_call( } Some(var_type) => { // Any other typed constant is an error - return Err(CheckErrors::ExpectedCallableType(var_type.clone()).into()); + return Err( + CheckErrors::ExpectedCallableType(Box::new(var_type.clone())).into(), + ); } _ => { // Dynamic dispatch @@ -618,7 +627,7 @@ fn check_contract_of( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(1, args)?; let trait_instance = match &args[0].expr { @@ -645,7 +654,7 @@ fn check_principal_of( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(1, args)?; checker.type_check_expects(&args[0], context, &BUFF_33)?; Ok( @@ -664,7 +673,7 @@ fn check_principal_construct( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_arguments_at_least(2, args)?; check_arguments_at_most(3, args)?; checker.type_check_expects(&args[0], context, &BUFF_1)?; @@ -696,7 +705,7 @@ fn check_secp256k1_recover( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(2, args)?; checker.type_check_expects(&args[0], context, &BUFF_32)?; checker.type_check_expects(&args[1], context, &BUFF_65)?; @@ -710,7 +719,7 @@ fn check_secp256k1_verify( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(3, args)?; checker.type_check_expects(&args[0], context, &BUFF_32)?; checker.type_check_expects(&args[1], context, &BUFF_65)?; @@ -722,7 +731,7 @@ fn check_get_block_info( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_arguments_at_least(2, args)?; let block_info_prop_str = args[0] @@ -747,7 +756,7 @@ fn check_get_burn_block_info( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(2, args)?; let block_info_prop_str = args[0].match_atom().ok_or(CheckError::new( @@ -772,7 +781,7 @@ fn check_get_stacks_block_info( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(2, args)?; let block_info_prop_str = args[0].match_atom().ok_or(CheckError::new( @@ -793,7 +802,7 @@ fn check_get_tenure_info( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(2, args)?; let block_info_prop_str = args[0].match_atom().ok_or(CheckError::new( @@ -816,7 +825,7 @@ impl TypedNativeFunction { checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, - ) -> TypeResult { + ) -> Result { use self::TypedNativeFunction::{Simple, Special}; match self { Special(SpecialNativeFunction(check)) => check(checker, args, context), @@ -1183,6 +1192,23 @@ impl TypedNativeFunction { returns: TypeSignature::new_response(BUFF_32.clone(), TypeSignature::UIntType) .map_err(|_| CheckErrors::Expects("Bad constructor".into()))?, }))), + ToAscii => Simple(SimpleNativeFunction(FunctionType::UnionArgs( + vec![ + TypeSignature::IntType, + TypeSignature::UIntType, + TypeSignature::BoolType, + TypeSignature::PrincipalType, + TO_ASCII_MAX_BUFF.clone(), + TypeSignature::max_string_utf8()?, + ], + TypeSignature::new_response( + TO_ASCII_RESPONSE_STRING.clone(), + TypeSignature::UIntType, + ) + .map_err(|_| { + CheckErrors::Expects("FATAL: Legal Clarity response type marked invalid".into()) + })?, + ))), }; Ok(out) 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 6b9469f970b..56d145298a6 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 @@ -14,12 +14,12 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use clarity_serialization::representations::ClarityName; -use clarity_serialization::types::TypeSignature; +use clarity_types::representations::ClarityName; +use clarity_types::types::TypeSignature; use stacks_common::types::StacksEpochId; use super::{ - check_argument_count, check_arguments_at_least, no_type, CheckErrors, TypeChecker, TypeResult, + check_argument_count, check_arguments_at_least, no_type, CheckError, CheckErrors, TypeChecker, }; use crate::vm::analysis::type_checker::contexts::TypingContext; use crate::vm::costs::cost_functions::ClarityCostFunction; @@ -30,7 +30,7 @@ pub fn check_special_okay( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(1, args)?; runtime_cost(ClarityCostFunction::AnalysisOptionCons, checker, 0)?; @@ -44,7 +44,7 @@ pub fn check_special_some( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(1, args)?; runtime_cost(ClarityCostFunction::AnalysisOptionCons, checker, 0)?; @@ -58,7 +58,7 @@ pub fn check_special_error( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(1, args)?; runtime_cost(ClarityCostFunction::AnalysisOptionCons, checker, 0)?; @@ -72,7 +72,7 @@ pub fn check_special_is_response( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(1, args)?; let input = checker.type_check(&args[0], context)?; @@ -82,7 +82,7 @@ pub fn check_special_is_response( if let TypeSignature::ResponseType(_types) = input { Ok(TypeSignature::BoolType) } else { - Err(CheckErrors::ExpectedResponseType(input.clone()).into()) + Err(CheckErrors::ExpectedResponseType(Box::new(input.clone())).into()) } } @@ -90,7 +90,7 @@ pub fn check_special_is_optional( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(1, args)?; let input = checker.type_check(&args[0], context)?; @@ -100,7 +100,7 @@ pub fn check_special_is_optional( if let TypeSignature::OptionalType(_type) = input { Ok(TypeSignature::BoolType) } else { - Err(CheckErrors::ExpectedOptionalType(input.clone()).into()) + Err(CheckErrors::ExpectedOptionalType(Box::new(input.clone())).into()) } } @@ -108,7 +108,7 @@ pub fn check_special_default_to( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(2, args)?; let default = checker.type_check(&args[0], context)?; @@ -118,10 +118,14 @@ pub fn check_special_default_to( if let TypeSignature::OptionalType(input_type) = input { let contained_type = *input_type; - TypeSignature::least_supertype(&StacksEpochId::Epoch21, &default, &contained_type) - .map_err(|_| CheckErrors::DefaultTypesMustMatch(default, contained_type).into()) + TypeSignature::least_supertype(&StacksEpochId::Epoch21, &default, &contained_type).map_err( + |_| { + CheckErrors::DefaultTypesMustMatch(Box::new(default), Box::new(contained_type)) + .into() + }, + ) } else { - Err(CheckErrors::ExpectedOptionalType(input).into()) + Err(CheckErrors::ExpectedOptionalType(Box::new(input)).into()) } } @@ -129,7 +133,7 @@ pub fn check_special_asserts( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(2, args)?; checker.type_check_expects(&args[0], context, &TypeSignature::BoolType)?; @@ -140,7 +144,10 @@ pub fn check_special_asserts( Ok(TypeSignature::BoolType) } -fn inner_unwrap(input: TypeSignature, checker: &mut TypeChecker) -> TypeResult { +fn inner_unwrap( + input: TypeSignature, + checker: &mut TypeChecker, +) -> Result { runtime_cost(ClarityCostFunction::AnalysisOptionCheck, checker, 0)?; match input { @@ -159,11 +166,14 @@ fn inner_unwrap(input: TypeSignature, checker: &mut TypeChecker) -> TypeResult { Ok(ok_type) } } - _ => Err(CheckErrors::ExpectedOptionalOrResponseType(input).into()), + _ => Err(CheckErrors::ExpectedOptionalOrResponseType(Box::new(input)).into()), } } -fn inner_unwrap_err(input: TypeSignature, checker: &mut TypeChecker) -> TypeResult { +fn inner_unwrap_err( + input: TypeSignature, + checker: &mut TypeChecker, +) -> Result { runtime_cost(ClarityCostFunction::AnalysisOptionCheck, checker, 0)?; if let TypeSignature::ResponseType(response_type) = input { @@ -174,7 +184,7 @@ fn inner_unwrap_err(input: TypeSignature, checker: &mut TypeChecker) -> TypeResu Ok(err_type) } } else { - Err(CheckErrors::ExpectedResponseType(input).into()) + Err(CheckErrors::ExpectedResponseType(Box::new(input)).into()) } } @@ -182,7 +192,7 @@ pub fn check_special_unwrap_or_ret( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(2, args)?; let input = checker.type_check(&args[0], context)?; @@ -197,7 +207,7 @@ pub fn check_special_unwrap_err_or_ret( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(2, args)?; let input = checker.type_check(&args[0], context)?; @@ -212,7 +222,7 @@ pub fn check_special_try_ret( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(1, args)?; let input = checker.type_check(&args[0], context)?; @@ -242,7 +252,7 @@ pub fn check_special_try_ret( Ok(ok_type) } } - _ => Err(CheckErrors::ExpectedOptionalOrResponseType(input).into()), + _ => Err(CheckErrors::ExpectedOptionalOrResponseType(Box::new(input)).into()), } } @@ -250,7 +260,7 @@ pub fn check_special_unwrap( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(1, args)?; let input = checker.type_check(&args[0], context)?; @@ -262,7 +272,7 @@ pub fn check_special_unwrap_err( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(1, args)?; let input = checker.type_check(&args[0], context)?; @@ -277,7 +287,7 @@ fn eval_with_new_binding( bind_type: TypeSignature, checker: &mut TypeChecker, context: &TypingContext, -) -> TypeResult { +) -> Result { let mut inner_context = context.extend()?; runtime_cost( @@ -312,7 +322,7 @@ fn check_special_match_opt( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { if args.len() != 3 { Err(CheckErrors::BadMatchOptionSyntax(Box::new( CheckErrors::IncorrectArgumentCount(4, args.len() + 1), @@ -341,7 +351,10 @@ fn check_special_match_opt( &some_branch_type, &none_branch_type, ) - .map_err(|_| CheckErrors::MatchArmsMustMatch(some_branch_type, none_branch_type).into()) + .map_err(|_| { + CheckErrors::MatchArmsMustMatch(Box::new(some_branch_type), Box::new(none_branch_type)) + .into() + }) } fn check_special_match_resp( @@ -349,7 +362,7 @@ fn check_special_match_resp( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { if args.len() != 4 { Err(CheckErrors::BadMatchResponseSyntax(Box::new( CheckErrors::IncorrectArgumentCount(5, args.len() + 1), @@ -380,14 +393,17 @@ fn check_special_match_resp( analysis_typecheck_cost(checker, &ok_branch_type, &err_branch_type)?; TypeSignature::least_supertype(&StacksEpochId::Epoch21, &ok_branch_type, &err_branch_type) - .map_err(|_| CheckErrors::MatchArmsMustMatch(ok_branch_type, err_branch_type).into()) + .map_err(|_| { + CheckErrors::MatchArmsMustMatch(Box::new(ok_branch_type), Box::new(err_branch_type)) + .into() + }) } pub fn check_special_match( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_arguments_at_least(1, args)?; let input = checker.type_check(&args[0], context)?; @@ -399,6 +415,6 @@ pub fn check_special_match( TypeSignature::ResponseType(resp_type) => { check_special_match_resp(*resp_type, checker, &args[1..], context) } - _ => Err(CheckErrors::BadMatchInput(input).into()), + _ => Err(CheckErrors::BadMatchInput(Box::new(input)).into()), } } 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 0207fe49d86..92143b80b0f 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 @@ -18,8 +18,8 @@ use stacks_common::types::StacksEpochId; use super::{SimpleNativeFunction, TypedNativeFunction}; use crate::vm::analysis::type_checker::v2_1::{ - check_argument_count, check_arguments_at_least, CheckErrors, CheckResult, TypeChecker, - TypeResult, TypingContext, + check_argument_count, check_arguments_at_least, CheckError, CheckErrors, TypeChecker, + TypingContext, }; use crate::vm::costs::cost_functions::ClarityCostFunction; use crate::vm::costs::{analysis_typecheck_cost, runtime_cost, CostTracker}; @@ -34,7 +34,7 @@ use crate::vm::types::{FunctionType, TypeSignature, Value}; fn get_simple_native_or_user_define( function_name: &str, checker: &mut TypeChecker, -) -> CheckResult { +) -> Result { runtime_cost(ClarityCostFunction::AnalysisLookupFunction, checker, 0)?; if let Some(ref native_function) = NativeFunctions::lookup_by_name_at_version(function_name, &checker.clarity_version) @@ -57,7 +57,7 @@ pub fn check_special_map( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_arguments_at_least(2, args)?; let function_name = args[0] @@ -104,7 +104,7 @@ pub fn check_special_map( // However that could lead to confusions when combining certain types: // ex: (map concat (list "hello " "hi ") "world") would fail, because // strings are handled as sequences. - return Err(CheckErrors::ExpectedSequence(argument_type).into()); + return Err(CheckErrors::ExpectedSequence(Box::new(argument_type)).into()); } }; @@ -134,10 +134,12 @@ pub fn check_special_map( } if let Err(mut check_error) = check_result { - if let CheckErrors::IncorrectArgumentCount(expected, _actual) = check_error.err { - check_error.err = - CheckErrors::IncorrectArgumentCount(expected, args.len().saturating_sub(1)); - check_error.diagnostic = Diagnostic::err(&check_error.err) + if let CheckErrors::IncorrectArgumentCount(expected, _actual) = *check_error.err { + check_error.err = Box::new(CheckErrors::IncorrectArgumentCount( + expected, + args.len().saturating_sub(1), + )); + check_error.diagnostic = Diagnostic::err(check_error.err.as_ref()); } // accumulate the checking costs for cost in total_costs.into_iter() { @@ -161,7 +163,7 @@ pub fn check_special_filter( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(2, args)?; let function_name = args[0] @@ -177,7 +179,9 @@ pub fn check_special_filter( { let input_type = match argument_type { TypeSignature::SequenceType(ref sequence_type) => Ok(sequence_type.unit_type()?), - _ => Err(CheckErrors::ExpectedSequence(argument_type.clone())), + _ => Err(CheckErrors::ExpectedSequence(Box::new( + argument_type.clone(), + ))), }?; let filter_type = function_type.check_args( @@ -188,7 +192,11 @@ pub fn check_special_filter( )?; if TypeSignature::BoolType != filter_type { - return Err(CheckErrors::TypeError(TypeSignature::BoolType, filter_type).into()); + return Err(CheckErrors::TypeError( + Box::new(TypeSignature::BoolType), + Box::new(filter_type), + ) + .into()); } } @@ -199,7 +207,7 @@ pub fn check_special_fold( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(3, args)?; let function_name = args[0] @@ -214,7 +222,7 @@ pub fn check_special_fold( let input_type = match argument_type { TypeSignature::SequenceType(sequence_type) => Ok(sequence_type.unit_type()?), - _ => Err(CheckErrors::ExpectedSequence(argument_type)), + _ => Err(CheckErrors::ExpectedSequence(Box::new(argument_type))), }?; let initial_value_type = checker.type_check(&args[2], context)?; @@ -246,7 +254,7 @@ pub fn check_special_concat( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(2, args)?; let lhs_type = checker.type_check(&args[0], context)?; @@ -294,11 +302,15 @@ pub fn check_special_concat( TypeSignature::SequenceType(StringType(UTF8(size.try_into()?))) } (_, _) => { - return Err(CheckErrors::TypeError(lhs_type.clone(), rhs_type.clone()).into()) + return Err(CheckErrors::TypeError( + Box::new(lhs_type.clone()), + Box::new(rhs_type.clone()), + ) + .into()) } } } - _ => return Err(CheckErrors::ExpectedSequence(lhs_type.clone()).into()), + _ => return Err(CheckErrors::ExpectedSequence(Box::new(lhs_type.clone())).into()), }; Ok(res) } @@ -307,7 +319,7 @@ pub fn check_special_append( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(2, args)?; runtime_cost(ClarityCostFunction::AnalysisIterableFunc, checker, 0)?; @@ -339,14 +351,18 @@ pub fn check_special_as_max_len( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(2, args)?; let expected_len = match args[1].expr { SymbolicExpressionType::LiteralValue(Value::UInt(expected_len)) => expected_len, _ => { let expected_len_type = checker.type_check(&args[1], context)?; - return Err(CheckErrors::TypeError(TypeSignature::UIntType, expected_len_type).into()); + return Err(CheckErrors::TypeError( + Box::new(TypeSignature::UIntType), + Box::new(expected_len_type), + ) + .into()); } }; runtime_cost( @@ -384,7 +400,7 @@ pub fn check_special_as_max_len( StringUTF8Length::try_from(expected_len)?, )))), )), - _ => Err(CheckErrors::ExpectedSequence(sequence).into()), + _ => Err(CheckErrors::ExpectedSequence(Box::new(sequence)).into()), } } @@ -392,7 +408,7 @@ pub fn check_special_len( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(1, args)?; let collection_type = checker.type_check(&args[0], context)?; @@ -400,7 +416,7 @@ pub fn check_special_len( match collection_type { TypeSignature::SequenceType(_) => Ok(()), - _ => Err(CheckErrors::ExpectedSequence(collection_type)), + _ => Err(CheckErrors::ExpectedSequence(Box::new(collection_type))), }?; Ok(TypeSignature::UIntType) @@ -410,7 +426,7 @@ pub fn check_special_element_at( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(2, args)?; let _index_type = checker.type_check_expects(&args[1], context, &TypeSignature::UIntType)?; @@ -438,7 +454,7 @@ pub fn check_special_element_at( .map_err(|_| CheckErrors::Expects("Bad constructor".into()))?, )))), )), - _ => Err(CheckErrors::ExpectedSequence(collection_type).into()), + _ => Err(CheckErrors::ExpectedSequence(Box::new(collection_type)).into()), } } @@ -446,7 +462,7 @@ pub fn check_special_index_of( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(2, args)?; runtime_cost(ClarityCostFunction::AnalysisIterableFunc, checker, 0)?; @@ -454,7 +470,7 @@ pub fn check_special_index_of( let expected_input_type = match list_type { TypeSignature::SequenceType(ref sequence_type) => Ok(sequence_type.unit_type()?), - _ => Err(CheckErrors::ExpectedSequence(list_type)), + _ => Err(CheckErrors::ExpectedSequence(Box::new(list_type))), }?; checker.type_check_expects(&args[1], context, &expected_input_type)?; @@ -467,7 +483,7 @@ pub fn check_special_slice( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(3, args)?; runtime_cost(ClarityCostFunction::AnalysisIterableFunc, checker, 0)?; @@ -477,7 +493,7 @@ pub fn check_special_slice( TypeSignature::SequenceType(seq) => { TypeSignature::new_option(TypeSignature::SequenceType(seq))? } - _ => return Err(CheckErrors::ExpectedSequence(seq_type).into()), + _ => return Err(CheckErrors::ExpectedSequence(Box::new(seq_type)).into()), }; // Check left position argument @@ -493,7 +509,7 @@ pub fn check_special_replace_at( checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext, -) -> TypeResult { +) -> Result { check_argument_count(3, args)?; runtime_cost(ClarityCostFunction::AnalysisIterableFunc, checker, 0)?; @@ -501,7 +517,7 @@ pub fn check_special_replace_at( let input_type = checker.type_check(&args[0], context)?; let seq_type = match &input_type { TypeSignature::SequenceType(seq) => seq, - _ => return Err(CheckErrors::ExpectedSequence(input_type).into()), + _ => return Err(CheckErrors::ExpectedSequence(Box::new(input_type)).into()), }; let unit_seq = seq_type.unit_type()?; // Check index argument diff --git a/clarity/src/vm/analysis/type_checker/v2_1/tests/assets.rs b/clarity/src/vm/analysis/type_checker/v2_1/tests/assets.rs index 8f766f87123..a25a43a3cc5 100644 --- a/clarity/src/vm/analysis/type_checker/v2_1/tests/assets.rs +++ b/clarity/src/vm/analysis/type_checker/v2_1/tests/assets.rs @@ -176,42 +176,108 @@ fn test_bad_asset_usage() { CheckErrors::NoSuchFT("stackoos".to_string()), CheckErrors::BadTokenName, CheckErrors::BadTokenName, - CheckErrors::TypeError(TypeSignature::PrincipalType, TypeSignature::UIntType), - CheckErrors::TypeError(TypeSignature::PrincipalType, TypeSignature::IntType), + CheckErrors::TypeError( + Box::new(TypeSignature::PrincipalType), + Box::new(TypeSignature::UIntType), + ), + CheckErrors::TypeError( + Box::new(TypeSignature::PrincipalType), + Box::new(TypeSignature::IntType), + ), CheckErrors::BadTokenName, CheckErrors::NoSuchNFT("stackoos".to_string()), - CheckErrors::TypeError(string_ascii_type(10), TypeSignature::UIntType), - CheckErrors::TypeError(string_ascii_type(10), string_ascii_type(15)), + CheckErrors::TypeError( + Box::new(string_ascii_type(10)), + Box::new(TypeSignature::UIntType), + ), + CheckErrors::TypeError( + Box::new(string_ascii_type(10)), + Box::new(string_ascii_type(15)), + ), CheckErrors::BadTokenName, CheckErrors::NoSuchNFT("stackoos".to_string()), - CheckErrors::TypeError(string_ascii_type(10), TypeSignature::UIntType), - CheckErrors::TypeError(string_ascii_type(10), string_ascii_type(15)), - CheckErrors::TypeError(TypeSignature::PrincipalType, TypeSignature::UIntType), + CheckErrors::TypeError( + Box::new(string_ascii_type(10)), + Box::new(TypeSignature::UIntType), + ), + CheckErrors::TypeError( + Box::new(string_ascii_type(10)), + Box::new(string_ascii_type(15)), + ), + CheckErrors::TypeError( + Box::new(TypeSignature::PrincipalType), + Box::new(TypeSignature::UIntType), + ), CheckErrors::NoSuchFT("stackoos".to_string()), CheckErrors::BadTokenName, - CheckErrors::TypeError(TypeSignature::PrincipalType, TypeSignature::UIntType), - CheckErrors::TypeError(TypeSignature::UIntType, TypeSignature::BoolType), + CheckErrors::TypeError( + Box::new(TypeSignature::PrincipalType), + Box::new(TypeSignature::UIntType), + ), + CheckErrors::TypeError( + Box::new(TypeSignature::UIntType), + Box::new(TypeSignature::BoolType), + ), CheckErrors::BadTokenName, CheckErrors::NoSuchNFT("stackoos".to_string()), - CheckErrors::TypeError(TypeSignature::PrincipalType, TypeSignature::UIntType), - CheckErrors::TypeError(TypeSignature::PrincipalType, TypeSignature::UIntType), - CheckErrors::TypeError(string_ascii_type(10), TypeSignature::UIntType), + CheckErrors::TypeError( + Box::new(TypeSignature::PrincipalType), + Box::new(TypeSignature::UIntType), + ), + CheckErrors::TypeError( + Box::new(TypeSignature::PrincipalType), + Box::new(TypeSignature::UIntType), + ), + CheckErrors::TypeError( + Box::new(string_ascii_type(10)), + Box::new(TypeSignature::UIntType), + ), CheckErrors::BadTokenName, - CheckErrors::TypeError(string_ascii_type(10), TypeSignature::UIntType), - CheckErrors::TypeError(TypeSignature::PrincipalType, TypeSignature::UIntType), + CheckErrors::TypeError( + Box::new(string_ascii_type(10)), + Box::new(TypeSignature::UIntType), + ), + CheckErrors::TypeError( + Box::new(TypeSignature::PrincipalType), + Box::new(TypeSignature::UIntType), + ), CheckErrors::NoSuchFT("stackoos".to_string()), CheckErrors::BadTokenName, - CheckErrors::TypeError(TypeSignature::PrincipalType, TypeSignature::UIntType), - CheckErrors::TypeError(TypeSignature::UIntType, TypeSignature::BoolType), - CheckErrors::TypeError(TypeSignature::PrincipalType, TypeSignature::UIntType), - CheckErrors::TypeError(TypeSignature::UIntType, TypeSignature::BoolType), + CheckErrors::TypeError( + Box::new(TypeSignature::PrincipalType), + Box::new(TypeSignature::UIntType), + ), + CheckErrors::TypeError( + Box::new(TypeSignature::UIntType), + Box::new(TypeSignature::BoolType), + ), + CheckErrors::TypeError( + Box::new(TypeSignature::PrincipalType), + Box::new(TypeSignature::UIntType), + ), + CheckErrors::TypeError( + Box::new(TypeSignature::UIntType), + Box::new(TypeSignature::BoolType), + ), CheckErrors::DefineNFTBadSignature, - CheckErrors::TypeError(TypeSignature::UIntType, TypeSignature::IntType), - CheckErrors::TypeError(TypeSignature::UIntType, TypeSignature::IntType), + CheckErrors::TypeError( + Box::new(TypeSignature::UIntType), + Box::new(TypeSignature::IntType), + ), + CheckErrors::TypeError( + Box::new(TypeSignature::UIntType), + Box::new(TypeSignature::IntType), + ), CheckErrors::NoSuchFT("stackoos".to_string()), CheckErrors::NoSuchFT("stackoos".to_string()), - CheckErrors::TypeError(TypeSignature::UIntType, TypeSignature::IntType), - CheckErrors::TypeError(TypeSignature::PrincipalType, TypeSignature::IntType), + CheckErrors::TypeError( + Box::new(TypeSignature::UIntType), + Box::new(TypeSignature::IntType), + ), + CheckErrors::TypeError( + Box::new(TypeSignature::PrincipalType), + Box::new(TypeSignature::IntType), + ), ]; for (script, expected_err) in bad_scripts.iter().zip(expected.iter()) { @@ -223,6 +289,6 @@ fn test_bad_asset_usage() { ) .unwrap_err(); println!("{script}"); - assert_eq!(&actual_err.err, expected_err); + assert_eq!(*actual_err.err, *expected_err); } } diff --git a/clarity/src/vm/analysis/type_checker/v2_1/tests/contracts.rs b/clarity/src/vm/analysis/type_checker/v2_1/tests/contracts.rs index 14d47bde1f4..82dd67ce0ea 100644 --- a/clarity/src/vm/analysis/type_checker/v2_1/tests/contracts.rs +++ b/clarity/src/vm/analysis/type_checker/v2_1/tests/contracts.rs @@ -24,21 +24,23 @@ use crate::vm::analysis::contract_interface_builder::build_contract_interface; use crate::vm::analysis::errors::CheckErrors; use crate::vm::analysis::type_checker::v2_1::tests::mem_type_check; use crate::vm::analysis::{ - mem_type_check as mem_run_analysis, run_analysis, AnalysisDatabase, CheckError, CheckResult, + mem_type_check as mem_run_analysis, run_analysis, AnalysisDatabase, CheckError, ContractAnalysis, }; use crate::vm::ast::parse; use crate::vm::costs::LimitedCostTracker; use crate::vm::database::MemoryBackingStore; use crate::vm::tests::test_clarity_versions; -use crate::vm::types::signatures::CallableSubtype; +use crate::vm::types::signatures::{CallableSubtype, TO_ASCII_MAX_BUFF, TO_ASCII_RESPONSE_STRING}; use crate::vm::types::{ BufferLength, ListTypeData, QualifiedContractIdentifier, SequenceSubtype, StringSubtype, StringUTF8Length, TypeSignature, }; use crate::vm::{ClarityName, ClarityVersion, SymbolicExpression}; -fn mem_type_check_v1(snippet: &str) -> CheckResult<(Option, ContractAnalysis)> { +fn mem_type_check_v1( + snippet: &str, +) -> Result<(Option, ContractAnalysis), CheckError> { mem_run_analysis(snippet, ClarityVersion::Clarity1, StacksEpochId::latest()) } @@ -83,7 +85,7 @@ pub fn type_check_version( version, false, ) - .map_err(|(e, _)| e) + .map_err(|e| e.0) } const SIMPLE_TOKENS: &str = "(define-map tokens { account: principal } { balance: uint }) @@ -283,7 +285,7 @@ fn test_names_tokens_contracts_interface() { {{ "name": "tn1", "type": "bool" }}, {{ "name": "tn2", "type": "int128" }}, {{ "name": "tn3", "type": {{ "buffer": {{ "length": 1 }} }}}} - ] }} }} + ] }} }} }}, {{ "name": "f11", "access": "private", @@ -416,7 +418,7 @@ fn test_names_tokens_contracts_interface() { "name": "n2", "type": "bool" }} - ] + ] }} }}] }} @@ -492,7 +494,7 @@ fn test_names_tokens_contracts_bad(#[case] version: ClarityVersion, #[case] epoc let err = db .execute(|db| type_check(&names_contract_id, &mut names_contract, db, true)) .unwrap_err(); - assert!(matches!(err.err, CheckErrors::TypeError(_, _))); + assert!(matches!(*err.err, CheckErrors::TypeError(_, _))); } #[test] @@ -533,11 +535,11 @@ fn test_bad_map_usage() { for contract in tests.iter() { let err = mem_type_check(contract).unwrap_err(); - assert!(matches!(err.err, CheckErrors::TypeError(_, _))); + assert!(matches!(*err.err, CheckErrors::TypeError(_, _))); } assert!(matches!( - mem_type_check(unhandled_option).unwrap_err().err, + *mem_type_check(unhandled_option).unwrap_err().err, CheckErrors::UnionTypeError(_, _) )); } @@ -625,24 +627,24 @@ fn test_expects() { for unmatched_return_types in bad_return_types_tests.iter() { let err = mem_type_check(unmatched_return_types).unwrap_err(); eprintln!("unmatched_return_types returned check error: {err}"); - assert!(matches!(err.err, CheckErrors::ReturnTypesMustMatch(_, _))); + assert!(matches!(*err.err, CheckErrors::ReturnTypesMustMatch(_, _))); } let err = mem_type_check(bad_default_type).unwrap_err(); eprintln!("bad_default_types returned check error: {err}"); - assert!(matches!(err.err, CheckErrors::DefaultTypesMustMatch(_, _))); + assert!(matches!(*err.err, CheckErrors::DefaultTypesMustMatch(_, _))); let err = mem_type_check(notype_response_type).unwrap_err(); eprintln!("notype_response_type returned check error: {err}"); assert!(matches!( - err.err, + *err.err, CheckErrors::CouldNotDetermineResponseErrType )); let err = mem_type_check(notype_response_type_2).unwrap_err(); eprintln!("notype_response_type_2 returned check error: {err}"); assert!(matches!( - err.err, + *err.err, CheckErrors::CouldNotDetermineResponseOkType )); } @@ -678,26 +680,19 @@ fn test_trait_to_compatible_trait() { mem_type_check(trait_to_compatible_trait).unwrap(); let err = mem_type_check_v1(trait_to_compatible_trait).unwrap_err(); - assert!(match err { - CheckError { - err: CheckErrors::TypeError(expected, found), - expressions: _, - diagnostic: _, - } => { - match (expected, found) { - ( - TypeSignature::CallableType(CallableSubtype::Trait(expected_trait)), - TypeSignature::CallableType(CallableSubtype::Trait(found_trait)), - ) => { - assert_eq!(expected_trait.name.as_str(), "trait-2"); - assert_eq!(found_trait.name.as_str(), "trait-1"); - true - } - _ => false, + match *err.err { + CheckErrors::TypeError(expected, found) => match (&*expected, &*found) { + ( + TypeSignature::CallableType(CallableSubtype::Trait(expected_trait)), + TypeSignature::CallableType(CallableSubtype::Trait(found_trait)), + ) => { + assert_eq!(expected_trait.name.as_str(), "trait-2"); + assert_eq!(found_trait.name.as_str(), "trait-1"); } - } - _ => false, - }); + _ => panic!("Unexpected type signatures: {expected:?} {found:?}"), + }, + _ => panic!("Unexpected error: {err:?}"), + }; } /// Pass a principal to a trait parameter @@ -712,45 +707,31 @@ fn test_bad_principal_to_trait() { (contract-call? contract get-1 u1))"; let err = mem_type_check(bad_principal_to_trait).unwrap_err(); - assert!(match err { - CheckError { - err: CheckErrors::TypeError(expected, found), - expressions: _, - diagnostic: _, - } => { - match (expected, found) { - ( - TypeSignature::CallableType(CallableSubtype::Trait(expected_trait)), - TypeSignature::PrincipalType, - ) => { - assert_eq!(expected_trait.name.as_str(), "trait-1"); - true - } - _ => false, + match *err.err { + CheckErrors::TypeError(expected, found) => match (&*expected, &*found) { + ( + TypeSignature::CallableType(CallableSubtype::Trait(expected_trait)), + TypeSignature::PrincipalType, + ) => { + assert_eq!(expected_trait.name.as_str(), "trait-1"); } - } - _ => false, - }); + _ => panic!("Unexpected type signatures: {expected:?} {found:?}"), + }, + _ => panic!("Unexpected error: {err:?}"), + }; let err = mem_type_check_v1(bad_principal_to_trait).unwrap_err(); - assert!(match err { - CheckError { - err: CheckErrors::TypeError(expected, found), - expressions: _, - diagnostic: _, - } => { - match (expected, found) { - ( - TypeSignature::CallableType(CallableSubtype::Trait(expected_trait)), - TypeSignature::PrincipalType, - ) => { - assert_eq!(expected_trait.name.as_str(), "trait-1"); - true - } - _ => false, + match *err.err { + CheckErrors::TypeError(expected, found) => match (&*expected, &*found) { + ( + TypeSignature::CallableType(CallableSubtype::Trait(expected_trait)), + TypeSignature::PrincipalType, + ) => { + assert_eq!(expected_trait.name.as_str(), "trait-1"); } - } - _ => false, - }); + _ => panic!("Unexpected type signatures: {expected:?} {found:?}"), + }, + _ => panic!("Unexpected error: {err:?}"), + }; } /// Pass a trait to a trait parameter which is not compatible @@ -768,39 +749,27 @@ fn test_bad_other_trait() { (contract-call? contract get-2 u1))"; let err = mem_type_check(bad_other_trait).unwrap_err(); - assert!(match err { - CheckError { - err: CheckErrors::IncompatibleTrait(expected, actual), - expressions: _, - diagnostic: _, - } => { + match *err.err { + CheckErrors::IncompatibleTrait(expected, actual) => { assert_eq!(expected.name.as_str(), "trait-2"); assert_eq!(actual.name.as_str(), "trait-1"); - true } - _ => false, - }); + _ => panic!("Unexpected error: {err:?}"), + }; let err = mem_type_check_v1(bad_other_trait).unwrap_err(); - assert!(match err { - CheckError { - err: CheckErrors::TypeError(expected, found), - expressions: _, - diagnostic: _, - } => { - match (expected, found) { - ( - TypeSignature::CallableType(CallableSubtype::Trait(expected_trait)), - TypeSignature::CallableType(CallableSubtype::Trait(found_trait)), - ) => { - assert_eq!(expected_trait.name.as_str(), "trait-2"); - assert_eq!(found_trait.name.as_str(), "trait-1"); - true - } - _ => false, + match *err.err { + CheckErrors::TypeError(expected, actual) => match (&*expected, &*actual) { + ( + TypeSignature::CallableType(CallableSubtype::Trait(expected_trait)), + TypeSignature::CallableType(CallableSubtype::Trait(found_trait)), + ) => { + assert_eq!(expected_trait.name.as_str(), "trait-2"); + assert_eq!(found_trait.name.as_str(), "trait-1"); } - } - _ => false, - }); + _ => panic!("Unexpected type signatures: {expected:?} {actual:?}"), + }, + _ => panic!("Unexpected error: {err:?}"), + }; } /// Pass a trait embedded in a compound type @@ -821,17 +790,12 @@ fn test_embedded_trait() { mem_type_check(embedded_trait).unwrap(); let err = mem_type_check_v1(embedded_trait).unwrap_err(); - assert!(match err { - CheckError { - err: CheckErrors::TraitReferenceUnknown(name), - expressions: _, - diagnostic: _, - } => { + match *err.err { + CheckErrors::TraitReferenceUnknown(name) => { assert_eq!(name.as_str(), "contract"); - true } - _ => false, - }); + _ => panic!("Unexpected error: {err:?}"), + }; } /// Pass a trait embedded in a compound type to a parameter with a compatible @@ -856,17 +820,12 @@ fn test_embedded_trait_compatible() { mem_type_check(embedded_trait_compatible).unwrap(); let err = mem_type_check_v1(embedded_trait_compatible).unwrap_err(); - assert!(match err { - CheckError { - err: CheckErrors::TraitReferenceUnknown(name), - expressions: _, - diagnostic: _, - } => { + match *err.err { + CheckErrors::TraitReferenceUnknown(name) => { assert_eq!(name.as_str(), "contract"); - true } - _ => false, - }); + _ => panic!("Unexpected error: {err:?}"), + }; } /// Pass a trait embedded in a compound type to a parameter with an @@ -894,30 +853,20 @@ fn test_bad_embedded_trait() { )"; let err = mem_type_check(bad_embedded_trait).unwrap_err(); - assert!(match err { - CheckError { - err: CheckErrors::IncompatibleTrait(expected, actual), - expressions: _, - diagnostic: _, - } => { + match *err.err { + CheckErrors::IncompatibleTrait(expected, actual) => { assert_eq!(expected.name.as_str(), "trait-12"); assert_eq!(actual.name.as_str(), "trait-1"); - true } - _ => false, - }); + _ => panic!("Unexpected error: {err:?}"), + }; let err = mem_type_check_v1(bad_embedded_trait).unwrap_err(); - assert!(match err { - CheckError { - err: CheckErrors::TraitReferenceUnknown(name), - expressions: _, - diagnostic: _, - } => { + match *err.err { + CheckErrors::TraitReferenceUnknown(name) => { assert_eq!(name.as_str(), "contract"); - true } - _ => false, - }); + _ => panic!("Unexpected error: {err:?}"), + }; } /// Bind a trait in a let expression @@ -934,17 +883,12 @@ fn test_let_trait() { mem_type_check(let_trait).unwrap(); let err = mem_type_check_v1(let_trait).unwrap_err(); - assert!(match err { - CheckError { - err: CheckErrors::TraitReferenceUnknown(name), - expressions: _, - diagnostic: _, - } => { + match *err.err { + CheckErrors::TraitReferenceUnknown(name) => { assert_eq!(name.as_str(), "t1"); - true } - _ => false, - }); + _ => panic!("Unexpected error: {err:?}"), + }; } /// Bind a trait in transitively in multiple let expressions @@ -965,17 +909,12 @@ fn test_let3_trait() { mem_type_check(let3_trait).unwrap(); let err = mem_type_check_v1(let3_trait).unwrap_err(); - assert!(match err { - CheckError { - err: CheckErrors::TraitReferenceUnknown(name), - expressions: _, - diagnostic: _, - } => { + match *err.err { + CheckErrors::TraitReferenceUnknown(name) => { assert_eq!(name.as_str(), "t3"); - true } - _ => false, - }); + _ => panic!("Unexpected error: {err:?}"), + }; } /// Bind a trait transitively in multiple let expressions with compound types @@ -1025,17 +964,12 @@ fn test_let3_compound_trait_call() { mem_type_check(let3_compound_trait_call).unwrap(); let err = mem_type_check_v1(let3_compound_trait_call).unwrap_err(); - assert!(match err { - CheckError { - err: CheckErrors::TraitReferenceUnknown(name), - expressions: _, - diagnostic: _, - } => { + match *err.err { + CheckErrors::TraitReferenceUnknown(name) => { assert_eq!(name.as_str(), "t4"); - true } - _ => false, - }); + _ => panic!("Unexpected error: {err:?}"), + }; } /// Check for compatibility between traits where the function parameter type @@ -1054,39 +988,27 @@ fn test_trait_args_differ() { (ok true))"; let err = mem_type_check(trait_args_differ).unwrap_err(); - assert!(match err { - CheckError { - err: CheckErrors::IncompatibleTrait(expected, actual), - expressions: _, - diagnostic: _, - } => { + match *err.err { + CheckErrors::IncompatibleTrait(expected, actual) => { assert_eq!(expected.name.as_str(), "trait-2"); assert_eq!(actual.name.as_str(), "trait-1"); - true } - _ => false, - }); + _ => panic!("Unexpected error: {err:?}"), + }; let err = mem_type_check_v1(trait_args_differ).unwrap_err(); - assert!(match err { - CheckError { - err: CheckErrors::TypeError(expected, found), - expressions: _, - diagnostic: _, - } => { - match (expected, found) { - ( - TypeSignature::CallableType(CallableSubtype::Trait(expected_trait)), - TypeSignature::CallableType(CallableSubtype::Trait(found_trait)), - ) => { - assert_eq!(expected_trait.name.as_str(), "trait-2"); - assert_eq!(found_trait.name.as_str(), "trait-1"); - true - } - _ => false, + match *err.err { + CheckErrors::TypeError(expected, found) => match (&*expected, &*found) { + ( + TypeSignature::CallableType(CallableSubtype::Trait(expected_trait)), + TypeSignature::CallableType(CallableSubtype::Trait(found_trait)), + ) => { + assert_eq!(expected_trait.name.as_str(), "trait-2"); + assert_eq!(found_trait.name.as_str(), "trait-1"); } - } - _ => false, - }); + _ => panic!("Unexpected type signatures: {expected:?} {found:?}"), + }, + _ => panic!("Unexpected error: {err:?}"), + }; } /// Pass a trait to a trait parameter with an compatible trait type @@ -1104,39 +1026,27 @@ fn test_trait_arg_counts_differ1() { (ok true))"; let err = mem_type_check(trait_to_compatible_trait).unwrap_err(); - assert!(match err { - CheckError { - err: CheckErrors::IncompatibleTrait(expected, found), - expressions: _, - diagnostic: _, - } => { + match *err.err { + CheckErrors::IncompatibleTrait(expected, found) => { assert_eq!(expected.name.as_str(), "trait-2"); assert_eq!(found.name.as_str(), "trait-1"); - true } - _ => false, - }); + _ => panic!("Unexpected error: {err:?}"), + }; let err = mem_type_check_v1(trait_to_compatible_trait).unwrap_err(); - assert!(match err { - CheckError { - err: CheckErrors::TypeError(expected, found), - expressions: _, - diagnostic: _, - } => { - match (expected, found) { - ( - TypeSignature::CallableType(CallableSubtype::Trait(expected_trait)), - TypeSignature::CallableType(CallableSubtype::Trait(found_trait)), - ) => { - assert_eq!(expected_trait.name.as_str(), "trait-2"); - assert_eq!(found_trait.name.as_str(), "trait-1"); - true - } - _ => false, + match *err.err { + CheckErrors::TypeError(expected, found) => match (&*expected, &*found) { + ( + TypeSignature::CallableType(CallableSubtype::Trait(expected_trait)), + TypeSignature::CallableType(CallableSubtype::Trait(found_trait)), + ) => { + assert_eq!(expected_trait.name.as_str(), "trait-2"); + assert_eq!(found_trait.name.as_str(), "trait-1"); } - } - _ => false, - }); + _ => panic!("Unexpected type signatures: {expected:?} {found:?}"), + }, + _ => panic!("Unexpected error: {err:?}"), + }; } /// Pass a trait to a trait parameter with an compatible trait type @@ -1154,39 +1064,27 @@ fn test_trait_arg_counts_differ2() { (ok true))"; let err = mem_type_check(trait_to_compatible_trait).unwrap_err(); - assert!(match err { - CheckError { - err: CheckErrors::IncompatibleTrait(expected, found), - expressions: _, - diagnostic: _, - } => { + match *err.err { + CheckErrors::IncompatibleTrait(expected, found) => { assert_eq!(expected.name.as_str(), "trait-2"); assert_eq!(found.name.as_str(), "trait-1"); - true } - _ => false, - }); + _ => panic!("Unexpected error: {err:?}"), + }; let err = mem_type_check_v1(trait_to_compatible_trait).unwrap_err(); - assert!(match err { - CheckError { - err: CheckErrors::TypeError(expected, found), - expressions: _, - diagnostic: _, - } => { - match (expected, found) { - ( - TypeSignature::CallableType(CallableSubtype::Trait(expected_trait)), - TypeSignature::CallableType(CallableSubtype::Trait(found_trait)), - ) => { - assert_eq!(expected_trait.name.as_str(), "trait-2"); - assert_eq!(found_trait.name.as_str(), "trait-1"); - true - } - _ => false, + match *err.err { + CheckErrors::TypeError(expected, found) => match (&*expected, &*found) { + ( + TypeSignature::CallableType(CallableSubtype::Trait(expected_trait)), + TypeSignature::CallableType(CallableSubtype::Trait(found_trait)), + ) => { + assert_eq!(expected_trait.name.as_str(), "trait-2"); + assert_eq!(found_trait.name.as_str(), "trait-1"); } - } - _ => false, - }); + _ => panic!("Unexpected type signatures: {expected:?} {found:?}"), + }, + _ => panic!("Unexpected error: {err:?}"), + }; } /// Check for compatibility between traits where the response types differ @@ -1204,39 +1102,27 @@ fn test_trait_ret_ty_differ() { (contract-call? contract echo u1))"; let err = mem_type_check(trait_ret_ty_differ).unwrap_err(); - assert!(match err { - CheckError { - err: CheckErrors::IncompatibleTrait(expected, actual), - expressions: _, - diagnostic: _, - } => { + match *err.err { + CheckErrors::IncompatibleTrait(expected, actual) => { assert_eq!(expected.name.as_str(), "trait-2"); assert_eq!(actual.name.as_str(), "trait-1"); - true } - _ => false, - }); + _ => panic!("Unexpected error: {err:?}"), + }; let err = mem_type_check_v1(trait_ret_ty_differ).unwrap_err(); - assert!(match err { - CheckError { - err: CheckErrors::TypeError(expected, found), - expressions: _, - diagnostic: _, - } => { - match (expected, found) { - ( - TypeSignature::CallableType(CallableSubtype::Trait(expected_trait)), - TypeSignature::CallableType(CallableSubtype::Trait(found_trait)), - ) => { - assert_eq!(expected_trait.name.as_str(), "trait-2"); - assert_eq!(found_trait.name.as_str(), "trait-1"); - true - } - _ => false, + match *err.err { + CheckErrors::TypeError(expected, found) => match (&*expected, &*found) { + ( + TypeSignature::CallableType(CallableSubtype::Trait(expected_trait)), + TypeSignature::CallableType(CallableSubtype::Trait(found_trait)), + ) => { + assert_eq!(expected_trait.name.as_str(), "trait-2"); + assert_eq!(found_trait.name.as_str(), "trait-1"); } - } - _ => false, - }); + _ => panic!("Unexpected type signatures: {expected:?} {found:?}"), + }, + _ => panic!("Unexpected error: {err:?}"), + }; } /// Check for compatibility of traits where a function parameter has a @@ -1262,26 +1148,19 @@ fn test_trait_with_compatible_trait_arg() { mem_type_check(trait_with_compatible_trait_arg).unwrap(); let err = mem_type_check_v1(trait_with_compatible_trait_arg).unwrap_err(); - assert!(match err { - CheckError { - err: CheckErrors::TypeError(expected, found), - expressions: _, - diagnostic: _, - } => { - match (expected, found) { - ( - TypeSignature::CallableType(CallableSubtype::Trait(expected_trait)), - TypeSignature::CallableType(CallableSubtype::Trait(found_trait)), - ) => { - assert_eq!(expected_trait.name.as_str(), "trait-b"); - assert_eq!(found_trait.name.as_str(), "trait-a"); - true - } - _ => false, + match *err.err { + CheckErrors::TypeError(expected, found) => match (&*expected, &*found) { + ( + TypeSignature::CallableType(CallableSubtype::Trait(expected_trait)), + TypeSignature::CallableType(CallableSubtype::Trait(found_trait)), + ) => { + assert_eq!(expected_trait.name.as_str(), "trait-b"); + assert_eq!(found_trait.name.as_str(), "trait-a"); } - } - _ => false, - }); + _ => panic!("Unexpected type signatures: {expected:?} {found:?}"), + }, + _ => panic!("Unexpected error: {err:?}"), + }; } /// Check for compatibility of traits where a function parameter has an @@ -1306,39 +1185,27 @@ fn test_trait_with_bad_trait_arg() { (contract-call? contract echo callee))"; let err = mem_type_check(trait_with_bad_trait_arg).unwrap_err(); - assert!(match err { - CheckError { - err: CheckErrors::IncompatibleTrait(expected, actual), - expressions: _, - diagnostic: _, - } => { + match *err.err { + CheckErrors::IncompatibleTrait(expected, actual) => { assert_eq!(expected.name.as_str(), "trait-b"); assert_eq!(actual.name.as_str(), "trait-a"); - true } - _ => false, - }); + _ => panic!("Unexpected error: {err:?}"), + }; let err = mem_type_check_v1(trait_with_bad_trait_arg).unwrap_err(); - assert!(match err { - CheckError { - err: CheckErrors::TypeError(expected, found), - expressions: _, - diagnostic: _, - } => { - match (expected, found) { - ( - TypeSignature::CallableType(CallableSubtype::Trait(expected_trait)), - TypeSignature::CallableType(CallableSubtype::Trait(found_trait)), - ) => { - assert_eq!(expected_trait.name.as_str(), "trait-b"); - assert_eq!(found_trait.name.as_str(), "trait-a"); - true - } - _ => false, + match *err.err { + CheckErrors::TypeError(expected, found) => match (&*expected, &*found) { + ( + TypeSignature::CallableType(CallableSubtype::Trait(expected_trait)), + TypeSignature::CallableType(CallableSubtype::Trait(found_trait)), + ) => { + assert_eq!(expected_trait.name.as_str(), "trait-b"); + assert_eq!(found_trait.name.as_str(), "trait-a"); } - } - _ => false, - }); + _ => panic!("Unexpected type signatures: {expected:?} {found:?}"), + }, + _ => panic!("Unexpected error: {err:?}"), + }; } /// Check for compatibility of traits where a function parameter from one trait @@ -1364,39 +1231,29 @@ fn test_trait_with_superset_trait_arg() { (contract-call? contract echo callee))"; let err = mem_type_check(trait_with_superset_trait_arg).unwrap_err(); - assert!(match err { - CheckError { - err: CheckErrors::IncompatibleTrait(expected, actual), - expressions: _, - diagnostic: _, - } => { + match *err.err { + CheckErrors::IncompatibleTrait(expected, actual) => { assert_eq!(expected.name.as_str(), "trait-b"); assert_eq!(actual.name.as_str(), "trait-a"); - true } - _ => false, - }); + _ => panic!("Unexpected error: {err:?}"), + }; + let err = mem_type_check_v1(trait_with_superset_trait_arg).unwrap_err(); - assert!(match err { - CheckError { - err: CheckErrors::TypeError(expected, found), - expressions: _, - diagnostic: _, - } => { - match (expected, found) { - ( - TypeSignature::CallableType(CallableSubtype::Trait(expected_trait)), - TypeSignature::CallableType(CallableSubtype::Trait(found_trait)), - ) => { - assert_eq!(expected_trait.name.as_str(), "trait-b"); - assert_eq!(found_trait.name.as_str(), "trait-a"); - true - } - _ => false, + + match *err.err { + CheckErrors::TypeError(expected, found) => match (&*expected, &*found) { + ( + TypeSignature::CallableType(CallableSubtype::Trait(expected_trait)), + TypeSignature::CallableType(CallableSubtype::Trait(found_trait)), + ) => { + assert_eq!(expected_trait.name.as_str(), "trait-b"); + assert_eq!(found_trait.name.as_str(), "trait-a"); } - } - _ => false, - }); + _ => panic!("Unexpected TypeSignatures: {expected:?} {found:?}"), + }, + _ => panic!("Unexpected error: {err:?}"), + } } /// Check for compatibility of traits where a function parameter from one trait @@ -1423,26 +1280,19 @@ fn test_trait_with_subset_trait_arg() { mem_type_check(trait_with_subset_trait_arg).unwrap(); let err = mem_type_check_v1(trait_with_subset_trait_arg).unwrap_err(); - assert!(match err { - CheckError { - err: CheckErrors::TypeError(expected, found), - expressions: _, - diagnostic: _, - } => { - match (expected, found) { - ( - TypeSignature::CallableType(CallableSubtype::Trait(expected_trait)), - TypeSignature::CallableType(CallableSubtype::Trait(found_trait)), - ) => { - assert_eq!(expected_trait.name.as_str(), "trait-a"); - assert_eq!(found_trait.name.as_str(), "trait-b"); - true - } - _ => false, + match *err.err { + CheckErrors::TypeError(expected, found) => match (&*expected, &*found) { + ( + TypeSignature::CallableType(CallableSubtype::Trait(expected_trait)), + TypeSignature::CallableType(CallableSubtype::Trait(found_trait)), + ) => { + assert_eq!(expected_trait.name.as_str(), "trait-a"); + assert_eq!(found_trait.name.as_str(), "trait-b"); } - } - _ => false, - }); + _ => panic!("Unexpected type signatures: {expected:?} {found:?}"), + }, + _ => panic!("Unexpected error: {err:?}"), + }; } /// Define a trait with a duplicated method name @@ -1454,17 +1304,12 @@ fn test_trait_with_duplicate_method() { ))"; let err = mem_type_check(trait_with_duplicate_method).unwrap_err(); - assert!(match err { - CheckError { - err: CheckErrors::DefineTraitDuplicateMethod(method_name), - expressions: _, - diagnostic: _, - } => { + match *err.err { + CheckErrors::DefineTraitDuplicateMethod(method_name) => { assert_eq!(method_name.as_str(), "foo"); - true } - _ => false, - }); + _ => panic!("Unexpected error: {err:?}"), + }; mem_type_check_v1(trait_with_duplicate_method).unwrap(); } @@ -1488,39 +1333,27 @@ fn test_trait_to_subtrait_and_back() { true)"; let err = mem_type_check(trait_to_subtrait_and_back).unwrap_err(); - assert!(match err { - CheckError { - err: CheckErrors::IncompatibleTrait(expected, actual), - expressions: _, - diagnostic: _, - } => { + match *err.err { + CheckErrors::IncompatibleTrait(expected, actual) => { assert_eq!(expected.name.as_str(), "trait-2"); assert_eq!(actual.name.as_str(), "trait-1"); - true } - _ => false, - }); + _ => panic!("Unexpected error: {err:?}"), + }; let err = mem_type_check_v1(trait_to_subtrait_and_back).unwrap_err(); - assert!(match err { - CheckError { - err: CheckErrors::TypeError(expected, found), - expressions: _, - diagnostic: _, - } => { - match (expected, found) { - ( - TypeSignature::CallableType(CallableSubtype::Trait(expected_trait)), - TypeSignature::CallableType(CallableSubtype::Trait(found_trait)), - ) => { - assert_eq!(expected_trait.name.as_str(), "trait-2"); - assert_eq!(found_trait.name.as_str(), "trait-1"); - true - } - _ => false, + match *err.err { + CheckErrors::TypeError(expected, found) => match (&*expected, &*found) { + ( + TypeSignature::CallableType(CallableSubtype::Trait(expected_trait)), + TypeSignature::CallableType(CallableSubtype::Trait(found_trait)), + ) => { + assert_eq!(expected_trait.name.as_str(), "trait-2"); + assert_eq!(found_trait.name.as_str(), "trait-1"); } - } - _ => false, - }); + _ => panic!("Unexpected type signatures: {expected:?} {found:?}"), + }, + _ => panic!("Unexpected error: {err:?}"), + }; } /// Use `map` on a list of traits @@ -1555,47 +1388,33 @@ fn test_if_branches_with_incompatible_trait_types() { ) )"; let err = mem_type_check(if_branches_with_incompatible_trait_types).unwrap_err(); - assert!(match err { - CheckError { - err: CheckErrors::IfArmsMustMatch(type1, type2), - expressions: _, - diagnostic: _, - } => { - match (type1, type2) { - ( - TypeSignature::CallableType(CallableSubtype::Trait(trait1)), - TypeSignature::CallableType(CallableSubtype::Trait(trait2)), - ) => { - assert_eq!(trait1.name.as_str(), "trait-1"); - assert_eq!(trait2.name.as_str(), "trait-2"); - true - } - _ => false, + match *err.err { + CheckErrors::IfArmsMustMatch(type1, type2) => match (&*type1, &*type2) { + ( + TypeSignature::CallableType(CallableSubtype::Trait(trait1)), + TypeSignature::CallableType(CallableSubtype::Trait(trait2)), + ) => { + assert_eq!(trait1.name.as_str(), "trait-1"); + assert_eq!(trait2.name.as_str(), "trait-2"); } - } - _ => false, - }); + _ => panic!("Unexpected type signatures: {type1:?} {type2:?}"), + }, + _ => panic!("Unexpected error: {err:?}"), + }; let err = mem_type_check_v1(if_branches_with_incompatible_trait_types).unwrap_err(); - assert!(match err { - CheckError { - err: CheckErrors::IfArmsMustMatch(type1, type2), - expressions: _, - diagnostic: _, - } => { - match (type1, type2) { - ( - TypeSignature::CallableType(CallableSubtype::Trait(trait1)), - TypeSignature::CallableType(CallableSubtype::Trait(trait2)), - ) => { - assert_eq!(trait1.name.as_str(), "trait-1"); - assert_eq!(trait2.name.as_str(), "trait-2"); - true - } - _ => false, + match *err.err { + CheckErrors::IfArmsMustMatch(type1, type2) => match (&*type1, &*type2) { + ( + TypeSignature::CallableType(CallableSubtype::Trait(trait1)), + TypeSignature::CallableType(CallableSubtype::Trait(trait2)), + ) => { + assert_eq!(trait1.name.as_str(), "trait-1"); + assert_eq!(trait2.name.as_str(), "trait-2"); } - } - _ => false, - }); + _ => panic!("Unexpected type signatures: {type1:?} {type2:?}"), + }, + _ => panic!("Unexpected error: {err:?}"), + }; } /// If branches with compatible trait types @@ -1615,47 +1434,33 @@ fn test_if_branches_with_compatible_trait_types() { )"; let err = mem_type_check(if_branches_with_compatible_trait_types).unwrap_err(); - assert!(match err { - CheckError { - err: CheckErrors::IfArmsMustMatch(type1, type2), - expressions: _, - diagnostic: _, - } => { - match (type1, type2) { - ( - TypeSignature::CallableType(CallableSubtype::Trait(trait1)), - TypeSignature::CallableType(CallableSubtype::Trait(trait2)), - ) => { - assert_eq!(trait1.name.as_str(), "trait-1"); - assert_eq!(trait2.name.as_str(), "trait-2"); - true - } - _ => false, + match *err.err { + CheckErrors::IfArmsMustMatch(type1, type2) => match (&*type1, &*type2) { + ( + TypeSignature::CallableType(CallableSubtype::Trait(trait1)), + TypeSignature::CallableType(CallableSubtype::Trait(trait2)), + ) => { + assert_eq!(trait1.name.as_str(), "trait-1"); + assert_eq!(trait2.name.as_str(), "trait-2"); } - } - _ => false, - }); + _ => panic!("Unexpected type signatures: {type1:?} {type2:?}"), + }, + _ => panic!("Unexpected error: {err:?}"), + }; let err = mem_type_check_v1(if_branches_with_compatible_trait_types).unwrap_err(); - assert!(match err { - CheckError { - err: CheckErrors::IfArmsMustMatch(type1, type2), - expressions: _, - diagnostic: _, - } => { - match (type1, type2) { - ( - TypeSignature::CallableType(CallableSubtype::Trait(trait1)), - TypeSignature::CallableType(CallableSubtype::Trait(trait2)), - ) => { - assert_eq!(trait1.name.as_str(), "trait-1"); - assert_eq!(trait2.name.as_str(), "trait-2"); - true - } - _ => false, + match *err.err { + CheckErrors::IfArmsMustMatch(type1, type2) => match (&*type1, &*type2) { + ( + TypeSignature::CallableType(CallableSubtype::Trait(trait1)), + TypeSignature::CallableType(CallableSubtype::Trait(trait2)), + ) => { + assert_eq!(trait1.name.as_str(), "trait-1"); + assert_eq!(trait2.name.as_str(), "trait-2"); } - } - _ => false, - }); + _ => panic!("Unexpected type signatures: {type1:?} {type2:?}"), + }, + _ => panic!("Unexpected error: {err:?}"), + }; } /// Based on issue #3215 from sskeirik @@ -1682,7 +1487,7 @@ fn test_traits_multi_contract(#[case] version: ClarityVersion) { let mut marf = MemoryBackingStore::new(); let mut db = marf.as_analysis_db(); - match db.execute(|db| { + let result = db.execute(|db| { type_check_version( &trait_contract_id, &mut trait_contract, @@ -1699,17 +1504,17 @@ fn test_traits_multi_contract(#[case] version: ClarityVersion) { StacksEpochId::Epoch21, version, ) - }) { - Err(CheckError { - err: CheckErrors::TraitMethodUnknown(trait_name, function), - expressions: _, - diagnostic: _, - }) if version < ClarityVersion::Clarity2 => { - assert_eq!(trait_name.as_str(), "a"); - assert_eq!(function.as_str(), "do-it"); - } + }); + match result { Ok(_) if version >= ClarityVersion::Clarity2 => (), - res => panic!("{res:?}"), + Err(CheckError { err, .. }) if version < ClarityVersion::Clarity2 => match *err { + CheckErrors::TraitMethodUnknown(trait_name, function) => { + assert_eq!(trait_name.as_str(), "a"); + assert_eq!(function.as_str(), "do-it"); + } + _ => panic!("Unexpected error: {err:?}"), + }, + _ => panic!("Unexpected result: {result:?}"), } } @@ -3537,6 +3342,19 @@ fn clarity_trait_experiments_cross_epochs( }; } +#[apply(test_clarity_versions)] +fn clarity_trait_experiments_undefined_top_variable( + #[case] version: ClarityVersion, + #[case] epoch: StacksEpochId, +) { + let mut marf = MemoryBackingStore::new(); + let mut db = marf.as_analysis_db(); + let err = db + .execute(|db| load_versioned(db, "undefined-top-variable", version, epoch)) + .unwrap_err(); + assert!(err.starts_with("UndefinedVariable(\"foo\")")); +} + /// Pass various types to `contract-hash?` #[apply(test_clarity_versions)] fn test_contract_hash(#[case] version: ClarityVersion, #[case] epoch: StacksEpochId) { @@ -3571,43 +3389,43 @@ fn test_contract_hash(#[case] version: ClarityVersion, #[case] epoch: StacksEpoc "(contract-hash? 123)", "int type", Err(CheckErrors::TypeError( - TypeSignature::PrincipalType, - TypeSignature::IntType, + Box::new(TypeSignature::PrincipalType), + Box::new(TypeSignature::IntType), )), ), ( "(contract-hash? u123)", "uint type", Err(CheckErrors::TypeError( - TypeSignature::PrincipalType, - TypeSignature::UIntType, + Box::new(TypeSignature::PrincipalType), + Box::new(TypeSignature::UIntType), )), ), ( "(contract-hash? true)", "bool type", Err(CheckErrors::TypeError( - TypeSignature::PrincipalType, - TypeSignature::BoolType, + Box::new(TypeSignature::PrincipalType), + Box::new(TypeSignature::BoolType), )), ), ( "(contract-hash? 0x1234)", "buffer type", Err(CheckErrors::TypeError( - TypeSignature::PrincipalType, - TypeSignature::SequenceType(SequenceSubtype::BufferType( + Box::new(TypeSignature::PrincipalType), + Box::new(TypeSignature::SequenceType(SequenceSubtype::BufferType( BufferLength::try_from(2u32).unwrap(), - )), + ))), )), ), ( "(contract-hash? \"60 percent of the time, it works every time\")", "ascii string", Err(CheckErrors::TypeError( - TypeSignature::PrincipalType, - TypeSignature::SequenceType(SequenceSubtype::StringType(StringSubtype::ASCII( - BufferLength::try_from(43u32).unwrap(), + Box::new(TypeSignature::PrincipalType), + Box::new(TypeSignature::SequenceType(SequenceSubtype::StringType( + StringSubtype::ASCII(BufferLength::try_from(43u32).unwrap()), ))), )), ), @@ -3615,9 +3433,9 @@ fn test_contract_hash(#[case] version: ClarityVersion, #[case] epoch: StacksEpoc "(contract-hash? u\"I am serious, and don't call me Shirley.\")", "utf8 string", Err(CheckErrors::TypeError( - TypeSignature::PrincipalType, - TypeSignature::SequenceType(SequenceSubtype::StringType(StringSubtype::UTF8( - StringUTF8Length::try_from(40u32).unwrap(), + Box::new(TypeSignature::PrincipalType), + Box::new(TypeSignature::SequenceType(SequenceSubtype::StringType( + StringSubtype::UTF8(StringUTF8Length::try_from(40u32).unwrap()), ))), )), ), @@ -3625,49 +3443,51 @@ fn test_contract_hash(#[case] version: ClarityVersion, #[case] epoch: StacksEpoc "(contract-hash? (list 1 2 3))", "list type", Err(CheckErrors::TypeError( - TypeSignature::PrincipalType, - TypeSignature::SequenceType(SequenceSubtype::ListType( + Box::new(TypeSignature::PrincipalType), + Box::new(TypeSignature::SequenceType(SequenceSubtype::ListType( ListTypeData::new_list(TypeSignature::IntType, 3).unwrap(), - )), + ))), )), ), ( "(contract-hash? { a: 1, b: u2 })", "tuple type", Err(CheckErrors::TypeError( - TypeSignature::PrincipalType, - TypeSignature::TupleType( + Box::new(TypeSignature::PrincipalType), + Box::new(TypeSignature::TupleType( vec![ (ClarityName::from("a"), TypeSignature::IntType), (ClarityName::from("b"), TypeSignature::UIntType), ] .try_into() .unwrap(), - ), + )), )), ), ( "(contract-hash? (some u789))", "optional type", Err(CheckErrors::TypeError( - TypeSignature::PrincipalType, - TypeSignature::new_option(TypeSignature::UIntType).unwrap(), + Box::new(TypeSignature::PrincipalType), + Box::new(TypeSignature::new_option(TypeSignature::UIntType).unwrap()), )), ), ( "(contract-hash? (ok true))", "response type", Err(CheckErrors::TypeError( - TypeSignature::PrincipalType, - TypeSignature::new_response(TypeSignature::BoolType, TypeSignature::NoType) - .unwrap(), + Box::new(TypeSignature::PrincipalType), + Box::new( + TypeSignature::new_response(TypeSignature::BoolType, TypeSignature::NoType) + .unwrap(), + ), )), ), ]; for (source, description, clarity4_expected) in test_cases.iter() { let result = mem_run_analysis(source, version, epoch); - let actual = result.map(|(type_sig, _)| type_sig).map_err(|e| e.err); + let actual = result.map(|(type_sig, _)| type_sig).map_err(|e| *e.err); let expected = if version >= ClarityVersion::Clarity4 { clarity4_expected @@ -3678,3 +3498,134 @@ fn test_contract_hash(#[case] version: ClarityVersion, #[case] epoch: StacksEpoc assert_eq!(&actual, expected, "Failed for test case: {description}"); } } + +/// Pass various types to `to-ascii?` +#[apply(test_clarity_versions)] +fn test_to_ascii(#[case] version: ClarityVersion, #[case] epoch: StacksEpochId) { + let to_ascii_response_type = Some( + TypeSignature::new_response(TO_ASCII_RESPONSE_STRING.clone(), TypeSignature::UIntType) + .unwrap(), + ); + let to_ascii_expected_types = vec![ + TypeSignature::IntType, + TypeSignature::UIntType, + TypeSignature::BoolType, + TypeSignature::PrincipalType, + TO_ASCII_MAX_BUFF.clone(), + TypeSignature::max_string_utf8().unwrap(), + ]; + let test_cases = [ + ( + "(to-ascii? 123)", + "int type", + Ok(to_ascii_response_type.clone()), + ), + ( + "(to-ascii? u123)", + "uint type", + Ok(to_ascii_response_type.clone()), + ), + ( + "(to-ascii? true)", + "bool type", + Ok(to_ascii_response_type.clone()), + ), + ( + "(to-ascii? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM)", + "standard principal", + Ok(to_ascii_response_type.clone()), + ), + ( + "(to-ascii? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.foo)", + "contract principal", + Ok(to_ascii_response_type.clone()), + ), + ( + "(to-ascii? 0x1234)", + "buffer type", + Ok(to_ascii_response_type.clone()), + ), + ( + &format!("(to-ascii? 0x{})", "ff".repeat(524285)), + "oversized buffer type", + Err(CheckErrors::UnionTypeError( + to_ascii_expected_types.clone(), + Box::new(TypeSignature::SequenceType(SequenceSubtype::BufferType( + BufferLength::try_from(524285u32).unwrap(), + ))), + )), + ), + ( + "(to-ascii? u\"I am serious, and don't call me Shirley.\")", + "utf8 string", + Ok(to_ascii_response_type), + ), + ( + "(to-ascii? \"60 percent of the time, it works every time\")", + "ascii string", + Err(CheckErrors::UnionTypeError( + to_ascii_expected_types.clone(), + Box::new(TypeSignature::SequenceType(SequenceSubtype::StringType( + StringSubtype::ASCII(BufferLength::try_from(43u32).unwrap()), + ))), + )), + ), + ( + "(to-ascii? (list 1 2 3))", + "list type", + Err(CheckErrors::UnionTypeError( + to_ascii_expected_types.clone(), + Box::new(TypeSignature::SequenceType(SequenceSubtype::ListType( + ListTypeData::new_list(TypeSignature::IntType, 3).unwrap(), + ))), + )), + ), + ( + "(to-ascii? { a: 1, b: u2 })", + "tuple type", + Err(CheckErrors::UnionTypeError( + to_ascii_expected_types.clone(), + Box::new(TypeSignature::TupleType( + vec![ + (ClarityName::from("a"), TypeSignature::IntType), + (ClarityName::from("b"), TypeSignature::UIntType), + ] + .try_into() + .unwrap(), + )), + )), + ), + ( + "(to-ascii? (some u789))", + "optional type", + Err(CheckErrors::UnionTypeError( + to_ascii_expected_types.clone(), + Box::new(TypeSignature::new_option(TypeSignature::UIntType).unwrap()), + )), + ), + ( + "(to-ascii? (ok true))", + "response type", + Err(CheckErrors::UnionTypeError( + to_ascii_expected_types.clone(), + Box::new( + TypeSignature::new_response(TypeSignature::BoolType, TypeSignature::NoType) + .unwrap(), + ), + )), + ), + ]; + + for (source, description, clarity4_expected) in test_cases.iter() { + let result = mem_run_analysis(source, version, epoch); + let actual = result.map(|(type_sig, _)| type_sig).map_err(|e| *e.err); + + let expected = if version >= ClarityVersion::Clarity4 { + clarity4_expected + } else { + &Err(CheckErrors::UnknownFunction("to-ascii?".to_string())) + }; + + assert_eq!(&actual, expected, "Failed for test case: {description}"); + } +} diff --git a/clarity/src/vm/analysis/type_checker/v2_1/tests/contracts/undefined-top-variable.clar b/clarity/src/vm/analysis/type_checker/v2_1/tests/contracts/undefined-top-variable.clar new file mode 100644 index 00000000000..19102815663 --- /dev/null +++ b/clarity/src/vm/analysis/type_checker/v2_1/tests/contracts/undefined-top-variable.clar @@ -0,0 +1 @@ +foo \ No newline at end of file diff --git a/clarity/src/vm/analysis/type_checker/v2_1/tests/mod.rs b/clarity/src/vm/analysis/type_checker/v2_1/tests/mod.rs index b451ec74c86..ac978b277b6 100644 --- a/clarity/src/vm/analysis/type_checker/v2_1/tests/mod.rs +++ b/clarity/src/vm/analysis/type_checker/v2_1/tests/mod.rs @@ -20,10 +20,8 @@ use rstest::rstest; use rstest_reuse::{self, *}; use stacks_common::types::StacksEpochId; -use super::CheckResult; -use crate::vm::analysis::errors::{CheckErrors, SyntaxBindingError}; +use crate::vm::analysis::errors::{CheckError, CheckErrors, SyntaxBindingError}; use crate::vm::analysis::mem_type_check as mem_run_analysis; -use crate::vm::analysis::type_checker::v2_1::TypeResult; use crate::vm::analysis::types::ContractAnalysis; use crate::vm::ast::build_ast; use crate::vm::ast::errors::ParseErrors; @@ -43,7 +41,7 @@ mod assets; pub mod contracts; /// Backwards-compatibility shim for type_checker tests. Runs at latest Clarity version. -pub fn mem_type_check(exp: &str) -> CheckResult<(Option, ContractAnalysis)> { +pub fn mem_type_check(exp: &str) -> Result<(Option, ContractAnalysis), CheckError> { mem_run_analysis( exp, crate::vm::ClarityVersion::latest(), @@ -52,16 +50,19 @@ pub fn mem_type_check(exp: &str) -> CheckResult<(Option, Contract } /// NOTE: runs at latest Clarity version -fn type_check_helper(exp: &str) -> TypeResult { +fn type_check_helper(exp: &str) -> Result { mem_type_check(exp).map(|(type_sig_opt, _)| type_sig_opt.unwrap()) } -fn type_check_helper_version(exp: &str, version: ClarityVersion) -> TypeResult { +fn type_check_helper_version( + exp: &str, + version: ClarityVersion, +) -> Result { mem_run_analysis(exp, version, StacksEpochId::latest()) .map(|(type_sig_opt, _)| type_sig_opt.unwrap()) } -fn type_check_helper_v1(exp: &str) -> TypeResult { +fn type_check_helper_v1(exp: &str) -> Result { type_check_helper_version(exp, ClarityVersion::Clarity1) } @@ -99,8 +100,8 @@ fn test_from_consensus_buff() { ( "(from-consensus-buff? int u6)", CheckErrors::TypeError( - TypeSignature::max_buffer().unwrap(), - TypeSignature::UIntType, + Box::new(TypeSignature::max_buffer().unwrap()), + Box::new(TypeSignature::UIntType), ), ), ( @@ -125,7 +126,7 @@ fn test_from_consensus_buff() { } for (bad_test, expected) in bad.iter() { - assert_eq!(expected, &type_check_helper(bad_test).unwrap_err().err); + assert_eq!(*expected, *type_check_helper(bad_test).unwrap_err().err); } } @@ -221,7 +222,7 @@ fn test_to_consensus_buff() { } for (bad_test, expected) in bad.iter() { - assert_eq!(expected, &type_check_helper(bad_test).unwrap_err().err); + assert_eq!(*expected, *type_check_helper(bad_test).unwrap_err().err); } } @@ -261,8 +262,8 @@ fn test_get_block_info() { ]; let bad_expected = [ CheckErrors::NoSuchBlockInfoProperty("none".to_string()), - CheckErrors::TypeError(UIntType, BoolType), - CheckErrors::TypeError(UIntType, IntType), + CheckErrors::TypeError(Box::new(UIntType), Box::new(BoolType)), + CheckErrors::TypeError(Box::new(UIntType), Box::new(IntType)), CheckErrors::RequiresAtLeastArguments(2, 1), ]; @@ -287,8 +288,8 @@ fn test_get_block_info() { for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { assert_eq!( - expected, - &type_check_helper_version(bad_test, ClarityVersion::Clarity2) + *expected, + *type_check_helper_version(bad_test, ClarityVersion::Clarity2) .unwrap_err() .err ); @@ -296,7 +297,7 @@ fn test_get_block_info() { for good_test in good_v210.iter() { if let CheckErrors::NoSuchBlockInfoProperty(_) = - type_check_helper_v1(good_test).unwrap_err().err + *type_check_helper_v1(good_test).unwrap_err().err { } else { panic!("Failed to get a typecheck error when using a v2 property in a v1 context"); @@ -320,10 +321,10 @@ fn test_get_burn_block_info() { CheckErrors::IncorrectArgumentCount(2, 0), CheckErrors::IncorrectArgumentCount(2, 1), CheckErrors::TypeError( - UIntType, - SequenceType(StringType(ASCII( + Box::new(UIntType), + Box::new(SequenceType(StringType(ASCII( BufferLength::try_from(1u32).expect("BufferLength::try_from failed"), - ))), + )))), ), ]; @@ -335,7 +336,7 @@ fn test_get_burn_block_info() { } for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { - assert_eq!(expected, &type_check_helper(bad_test).unwrap_err().err); + assert_eq!(*expected, *type_check_helper(bad_test).unwrap_err().err); } } @@ -367,7 +368,7 @@ fn test_define_trait(#[case] version: ClarityVersion, #[case] epoch: StacksEpoch ]; for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { - assert_eq!(expected, &type_check_helper(bad_test).unwrap_err().err); + assert_eq!(*expected, *type_check_helper(bad_test).unwrap_err().err); } let bad = ["(define-trait trait-1)", "(define-trait)"]; @@ -379,7 +380,7 @@ fn test_define_trait(#[case] version: ClarityVersion, #[case] epoch: StacksEpoch let contract_identifier = QualifiedContractIdentifier::transient(); for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { let res = build_ast(&contract_identifier, bad_test, &mut (), version, epoch).unwrap_err(); - assert_eq!(expected, &res.err); + assert_eq!(*expected, *res.err); } } @@ -401,7 +402,7 @@ fn test_use_trait(#[case] version: ClarityVersion, #[case] epoch: StacksEpochId) let contract_identifier = QualifiedContractIdentifier::transient(); for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { let res = build_ast(&contract_identifier, bad_test, &mut (), version, epoch).unwrap_err(); - assert_eq!(expected, &res.err); + assert_eq!(*expected, *res.err); } } @@ -416,7 +417,7 @@ fn test_impl_trait(#[case] version: ClarityVersion, #[case] epoch: StacksEpochId let contract_identifier = QualifiedContractIdentifier::transient(); for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { let res = build_ast(&contract_identifier, bad_test, &mut (), version, epoch).unwrap_err(); - assert_eq!(expected, &res.err); + assert_eq!(*expected, *res.err); } } @@ -459,30 +460,40 @@ fn test_stx_ops() { ]; let bad_expected = [ CheckErrors::TypeError( - PrincipalType, - SequenceType(BufferType(BufferLength::try_from(2_u32).unwrap())), + Box::new(PrincipalType), + Box::new(SequenceType(BufferType( + BufferLength::try_from(2_u32).unwrap(), + ))), ), - CheckErrors::TypeError(UIntType, IntType), + CheckErrors::TypeError(Box::new(UIntType), Box::new(IntType)), CheckErrors::IncorrectArgumentCount(3, 5), - CheckErrors::TypeError(PrincipalType, UIntType), - CheckErrors::TypeError(PrincipalType, BoolType), - CheckErrors::TypeError(PrincipalType, OptionalType(Box::from(PrincipalType))), + CheckErrors::TypeError(Box::new(PrincipalType), Box::new(UIntType)), + CheckErrors::TypeError(Box::new(PrincipalType), Box::new(BoolType)), + CheckErrors::TypeError( + Box::new(PrincipalType), + Box::new(OptionalType(Box::from(PrincipalType))), + ), CheckErrors::IncorrectArgumentCount(3, 4), CheckErrors::TypeError( - PrincipalType, - SequenceType(BufferType(BufferLength::try_from(2_u32).unwrap())), + Box::new(PrincipalType), + Box::new(SequenceType(BufferType( + BufferLength::try_from(2_u32).unwrap(), + ))), ), - CheckErrors::TypeError(UIntType, IntType), + CheckErrors::TypeError(Box::new(UIntType), Box::new(IntType)), CheckErrors::IncorrectArgumentCount(4, 5), - CheckErrors::TypeError(PrincipalType, UIntType), - CheckErrors::TypeError(PrincipalType, BoolType), - CheckErrors::TypeError(PrincipalType, OptionalType(Box::from(PrincipalType))), + CheckErrors::TypeError(Box::new(PrincipalType), Box::new(UIntType)), + CheckErrors::TypeError(Box::new(PrincipalType), Box::new(BoolType)), + CheckErrors::TypeError( + Box::new(PrincipalType), + Box::new(OptionalType(Box::from(PrincipalType))), + ), CheckErrors::IncorrectArgumentCount(4, 3), CheckErrors::IncorrectArgumentCount(2, 1), - CheckErrors::TypeError(UIntType, IntType), - CheckErrors::TypeError(PrincipalType, BoolType), + CheckErrors::TypeError(Box::new(UIntType), Box::new(IntType)), + CheckErrors::TypeError(Box::new(PrincipalType), Box::new(BoolType)), CheckErrors::IncorrectArgumentCount(2, 3), - CheckErrors::TypeError(PrincipalType, BoolType), + CheckErrors::TypeError(Box::new(PrincipalType), Box::new(BoolType)), CheckErrors::IncorrectArgumentCount(1, 2), ]; @@ -494,7 +505,7 @@ fn test_stx_ops() { } for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { - assert_eq!(expected, &type_check_helper(bad_test).unwrap_err().err); + assert_eq!(*expected, *type_check_helper(bad_test).unwrap_err().err); } } @@ -513,8 +524,8 @@ fn test_tx_sponsor() { let bad = ["(stx-transfer? u10 tx-sponsor? 'SM2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQVX8X0G)"]; let bad_expected = [CheckErrors::TypeError( - PrincipalType, - OptionalType(Box::from(PrincipalType)), + Box::new(PrincipalType), + Box::new(OptionalType(Box::from(PrincipalType))), )]; for (good_test, expected) in good.iter().zip(expected.iter()) { @@ -525,7 +536,7 @@ fn test_tx_sponsor() { } for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { - assert_eq!(expected, &type_check_helper(bad_test).unwrap_err().err); + assert_eq!(*expected, *type_check_helper(bad_test).unwrap_err().err); } } @@ -580,11 +591,11 @@ fn test_destructuring_opts(#[case] version: ClarityVersion, #[case] epoch: Stack let bad = [ ( "(unwrap-err! (some 2) 2)", - CheckErrors::ExpectedResponseType(TypeSignature::from_string( + CheckErrors::ExpectedResponseType(Box::new(TypeSignature::from_string( "(optional int)", version, epoch, - )), + ))), ), ( "(unwrap! (err 3) 2)", @@ -621,11 +632,17 @@ fn test_destructuring_opts(#[case] version: ClarityVersion, #[case] epoch: Stack ( "(define-private (foo) (if (> 1 0) (ok 1) (err u8))) (match (foo) ok-val (+ 1 ok-val) err-val (/ err-val u0))", - CheckErrors::MatchArmsMustMatch(TypeSignature::IntType, TypeSignature::UIntType), + CheckErrors::MatchArmsMustMatch( + Box::new(TypeSignature::IntType), + Box::new(TypeSignature::UIntType), + ), ), ( "(match (some 1) inner-value (+ 1 inner-value) (> 1 28))", - CheckErrors::MatchArmsMustMatch(TypeSignature::IntType, TypeSignature::BoolType), + CheckErrors::MatchArmsMustMatch( + Box::new(TypeSignature::IntType), + Box::new(TypeSignature::BoolType), + ), ), ( "(match (some 1) inner-value (+ 1 inner-value))", @@ -652,11 +669,11 @@ fn test_destructuring_opts(#[case] version: ClarityVersion, #[case] epoch: Stack ("(match)", CheckErrors::RequiresAtLeastArguments(1, 0)), ( "(match 1 ok-val (/ ok-val 0) err-val (+ err-val 7))", - CheckErrors::BadMatchInput(TypeSignature::from_string("int", version, epoch)), + CheckErrors::BadMatchInput(Box::new(TypeSignature::from_string("int", version, epoch))), ), ( "(default-to 3 5)", - CheckErrors::ExpectedOptionalType(TypeSignature::IntType), + CheckErrors::ExpectedOptionalType(Box::new(TypeSignature::IntType)), ), ( "(define-private (foo (x int)) @@ -672,10 +689,14 @@ fn test_destructuring_opts(#[case] version: ClarityVersion, #[case] epoch: Stack (err u3) (ok (+ u2 (try! (t1 x))))))", CheckErrors::ReturnTypesMustMatch( - TypeSignature::new_response(TypeSignature::NoType, TypeSignature::BoolType) - .unwrap(), - TypeSignature::new_response(TypeSignature::UIntType, TypeSignature::UIntType) - .unwrap(), + Box::new( + TypeSignature::new_response(TypeSignature::NoType, TypeSignature::BoolType) + .unwrap(), + ), + Box::new( + TypeSignature::new_response(TypeSignature::UIntType, TypeSignature::UIntType) + .unwrap(), + ), ), ), ( @@ -683,9 +704,11 @@ fn test_destructuring_opts(#[case] version: ClarityVersion, #[case] epoch: Stack (define-private (t2 (x uint)) (> u2 (try! (t1 x))))", CheckErrors::ReturnTypesMustMatch( - TypeSignature::new_response(TypeSignature::NoType, TypeSignature::BoolType) - .unwrap(), - TypeSignature::BoolType, + Box::new( + TypeSignature::new_response(TypeSignature::NoType, TypeSignature::BoolType) + .unwrap(), + ), + Box::new(TypeSignature::BoolType), ), ), ( @@ -699,7 +722,7 @@ fn test_destructuring_opts(#[case] version: ClarityVersion, #[case] epoch: Stack ), ( "(try! 3)", - CheckErrors::ExpectedOptionalOrResponseType(TypeSignature::IntType), + CheckErrors::ExpectedOptionalOrResponseType(Box::new(TypeSignature::IntType)), ), ("(try! (ok 3) 4)", CheckErrors::IncorrectArgumentCount(1, 2)), ]; @@ -712,7 +735,7 @@ fn test_destructuring_opts(#[case] version: ClarityVersion, #[case] epoch: Stack } for (bad_test, expected) in bad.iter() { - assert_eq!(expected, &mem_type_check(bad_test).unwrap_err().err); + assert_eq!(*expected, *mem_type_check(bad_test).unwrap_err().err); } } @@ -723,7 +746,7 @@ fn test_at_block() { let bad = [ ( "(at-block (sha512 u0) u1)", - CheckErrors::TypeError(BUFF_32.clone(), BUFF_64.clone()), + CheckErrors::TypeError(Box::new(BUFF_32.clone()), Box::new(BUFF_64.clone())), ), ( "(at-block (sha256 u0) u1 u2)", @@ -739,7 +762,7 @@ fn test_at_block() { } for (bad_test, expected) in bad.iter() { - assert_eq!(expected, &type_check_helper(bad_test).unwrap_err().err); + assert_eq!(*expected, *type_check_helper(bad_test).unwrap_err().err); } } @@ -753,7 +776,7 @@ fn test_trait_reference_unknown(#[case] version: ClarityVersion, #[case] epoch: let contract_identifier = QualifiedContractIdentifier::transient(); for (bad_test, expected) in bad.iter() { let res = build_ast(&contract_identifier, bad_test, &mut (), version, epoch).unwrap_err(); - assert_eq!(expected, &res.err); + assert_eq!(*expected, *res.err); } } @@ -765,7 +788,7 @@ fn test_unexpected_use_of_field_or_trait_reference() { )]; for (bad_test, expected) in bad.iter() { - assert_eq!(expected, &type_check_helper(bad_test).unwrap_err().err); + assert_eq!(*expected, *type_check_helper(bad_test).unwrap_err().err); } } @@ -809,24 +832,26 @@ fn test_bitwise_bad_checks() { ]; let bad_expected = [ CheckErrors::IncorrectArgumentCount(2, 1), - CheckErrors::TypeError(IntType, UIntType), - CheckErrors::TypeError(UIntType, IntType), + CheckErrors::TypeError(Box::new(IntType), Box::new(UIntType)), + CheckErrors::TypeError(Box::new(UIntType), Box::new(IntType)), CheckErrors::UnionTypeError( vec![IntType, UIntType], - SequenceType(StringType(ASCII(BufferLength::try_from(5u32).unwrap()))), + Box::new(SequenceType(StringType(ASCII( + BufferLength::try_from(5u32).unwrap(), + )))), ), CheckErrors::IncorrectArgumentCount(1, 2), - CheckErrors::TypeError(IntType, UIntType), + CheckErrors::TypeError(Box::new(IntType), Box::new(UIntType)), CheckErrors::IncorrectArgumentCount(2, 1), CheckErrors::IncorrectArgumentCount(2, 1), - CheckErrors::UnionTypeError(vec![IntType, UIntType], BoolType), - CheckErrors::TypeError(UIntType, IntType), - CheckErrors::TypeError(UIntType, IntType), - CheckErrors::TypeError(IntType, UIntType), + CheckErrors::UnionTypeError(vec![IntType, UIntType], Box::new(BoolType)), + CheckErrors::TypeError(Box::new(UIntType), Box::new(IntType)), + CheckErrors::TypeError(Box::new(UIntType), Box::new(IntType)), + CheckErrors::TypeError(Box::new(IntType), Box::new(UIntType)), ]; for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { - assert_eq!(expected, &type_check_helper(bad_test).unwrap_err().err); + assert_eq!(*expected, *type_check_helper(bad_test).unwrap_err().err); } } @@ -847,12 +872,12 @@ fn test_simple_arithmetic_checks() { "(and (or true false) (+ 1 2 3))", ]; let bad_expected = [ - CheckErrors::TypeError(IntType, BoolType), + CheckErrors::TypeError(Box::new(IntType), Box::new(BoolType)), CheckErrors::RequiresAtLeastArguments(1, 0), CheckErrors::IncorrectArgumentCount(2, 1), CheckErrors::UndefinedVariable("x".to_string()), - CheckErrors::TypeError(IntType, BoolType), - CheckErrors::TypeError(BoolType, IntType), + CheckErrors::TypeError(Box::new(IntType), Box::new(BoolType)), + CheckErrors::TypeError(Box::new(BoolType), Box::new(IntType)), ]; for (good_test, expected) in good.iter().zip(expected.iter()) { @@ -863,7 +888,7 @@ fn test_simple_arithmetic_checks() { } for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { - assert_eq!(expected, &type_check_helper(bad_test).unwrap_err().err); + assert_eq!(*expected, *type_check_helper(bad_test).unwrap_err().err); } } @@ -912,14 +937,14 @@ fn test_simple_hash_checks() { for bad_test in bad_types.iter() { assert!(matches!( - type_check_helper(bad_test).unwrap_err().err, + *type_check_helper(bad_test).unwrap_err().err, CheckErrors::UnionTypeError(_, _) )); } for bad_test in invalid_args.iter() { assert!(matches!( - type_check_helper(bad_test).unwrap_err().err, + *type_check_helper(bad_test).unwrap_err().err, CheckErrors::IncorrectArgumentCount(_, _) )); } @@ -943,10 +968,10 @@ fn test_simple_ifs() { ]; let bad_expected = [ - CheckErrors::IfArmsMustMatch(BoolType, IntType), - CheckErrors::IfArmsMustMatch(ascii_type(1), BoolType), + CheckErrors::IfArmsMustMatch(Box::new(BoolType), Box::new(IntType)), + CheckErrors::IfArmsMustMatch(Box::new(ascii_type(1)), Box::new(BoolType)), CheckErrors::IncorrectArgumentCount(3, 0), - CheckErrors::TypeError(BoolType, IntType), + CheckErrors::TypeError(Box::new(BoolType), Box::new(IntType)), ]; for (good_test, expected) in good.iter().zip(expected.iter()) { @@ -957,7 +982,7 @@ fn test_simple_ifs() { } for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { - assert_eq!(expected, &type_check_helper(bad_test).unwrap_err().err); + assert_eq!(*expected, *type_check_helper(bad_test).unwrap_err().err); } } @@ -981,7 +1006,10 @@ fn test_simple_lets() { let bad_expected = [ CheckErrors::BadSyntaxBinding(SyntaxBindingError::let_binding_invalid_length(0)), CheckErrors::BadSyntaxBinding(SyntaxBindingError::let_binding_not_atom(0)), - CheckErrors::TypeError(TypeSignature::IntType, TypeSignature::UIntType), + CheckErrors::TypeError( + Box::new(TypeSignature::IntType), + Box::new(TypeSignature::UIntType), + ), ]; for (good_test, expected) in good.iter().zip(expected.iter()) { @@ -992,7 +1020,7 @@ fn test_simple_lets() { } for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { - assert_eq!(expected, &type_check_helper(bad_test).unwrap_err().err); + assert_eq!(*expected, *type_check_helper(bad_test).unwrap_err().err); } } @@ -1051,37 +1079,43 @@ fn test_index_of() { ]; let bad_expected = [ - CheckErrors::ExpectedSequence(TypeSignature::IntType), - CheckErrors::TypeError(TypeSignature::IntType, TypeSignature::UIntType), + CheckErrors::ExpectedSequence(Box::new(TypeSignature::IntType)), CheckErrors::TypeError( - TypeSignature::min_buffer().unwrap(), - TypeSignature::min_string_ascii().unwrap(), + Box::new(TypeSignature::IntType), + Box::new(TypeSignature::UIntType), ), CheckErrors::TypeError( - TypeSignature::min_string_utf8().unwrap(), - TypeSignature::min_string_ascii().unwrap(), + Box::new(TypeSignature::min_buffer().unwrap()), + Box::new(TypeSignature::min_string_ascii().unwrap()), ), CheckErrors::TypeError( - TypeSignature::min_string_ascii().unwrap(), - TypeSignature::min_string_utf8().unwrap(), + Box::new(TypeSignature::min_string_utf8().unwrap()), + Box::new(TypeSignature::min_string_ascii().unwrap()), ), CheckErrors::TypeError( - TypeSignature::list_of(TypeSignature::IntType, 1).unwrap(), - TypeSignature::list_of(TypeSignature::IntType, 2).unwrap(), + Box::new(TypeSignature::min_string_ascii().unwrap()), + Box::new(TypeSignature::min_string_utf8().unwrap()), ), - CheckErrors::ExpectedSequence(TypeSignature::IntType), - CheckErrors::TypeError(TypeSignature::IntType, TypeSignature::UIntType), CheckErrors::TypeError( - TypeSignature::min_buffer().unwrap(), - TypeSignature::min_string_ascii().unwrap(), + Box::new(TypeSignature::list_of(TypeSignature::IntType, 1).unwrap()), + Box::new(TypeSignature::list_of(TypeSignature::IntType, 2).unwrap()), ), + CheckErrors::ExpectedSequence(Box::new(TypeSignature::IntType)), CheckErrors::TypeError( - TypeSignature::min_string_utf8().unwrap(), - TypeSignature::min_string_ascii().unwrap(), + Box::new(TypeSignature::IntType), + Box::new(TypeSignature::UIntType), ), CheckErrors::TypeError( - TypeSignature::min_string_ascii().unwrap(), - TypeSignature::min_string_utf8().unwrap(), + Box::new(TypeSignature::min_buffer().unwrap()), + Box::new(TypeSignature::min_string_ascii().unwrap()), + ), + CheckErrors::TypeError( + Box::new(TypeSignature::min_string_utf8().unwrap()), + Box::new(TypeSignature::min_string_ascii().unwrap()), + ), + CheckErrors::TypeError( + Box::new(TypeSignature::min_string_ascii().unwrap()), + Box::new(TypeSignature::min_string_utf8().unwrap()), ), CheckErrors::CouldNotDetermineType, CheckErrors::CouldNotDetermineType, @@ -1089,7 +1123,7 @@ fn test_index_of() { ]; for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { - assert_eq!(expected, &type_check_helper(bad_test).unwrap_err().err); + assert_eq!(*expected, *type_check_helper(bad_test).unwrap_err().err); } } @@ -1129,10 +1163,16 @@ fn test_element_at() { ]; let bad_expected = [ - CheckErrors::TypeError(TypeSignature::UIntType, TypeSignature::IntType), - CheckErrors::ExpectedSequence(TypeSignature::IntType), - CheckErrors::TypeError(TypeSignature::UIntType, TypeSignature::IntType), - CheckErrors::ExpectedSequence(TypeSignature::IntType), + CheckErrors::TypeError( + Box::new(TypeSignature::UIntType), + Box::new(TypeSignature::IntType), + ), + CheckErrors::ExpectedSequence(Box::new(TypeSignature::IntType)), + CheckErrors::TypeError( + Box::new(TypeSignature::UIntType), + Box::new(TypeSignature::IntType), + ), + CheckErrors::ExpectedSequence(Box::new(TypeSignature::IntType)), ]; for (good_test, expected) in good.iter().zip(expected.iter()) { @@ -1143,7 +1183,7 @@ fn test_element_at() { } for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { - assert_eq!(expected, &type_check_helper(bad_test).unwrap_err().err); + assert_eq!(*expected, *type_check_helper(bad_test).unwrap_err().err); } } @@ -1164,11 +1204,18 @@ fn test_eqs(#[case] version: ClarityVersion, #[case] epoch: StacksEpochId) { ]; let bad_expected = [ - CheckErrors::TypeError(BoolType, IntType), - CheckErrors::TypeError(TypeSignature::list_of(IntType, 1).unwrap(), IntType), + CheckErrors::TypeError(Box::new(BoolType), Box::new(IntType)), CheckErrors::TypeError( - TypeSignature::from_string("(optional bool)", version, epoch), - TypeSignature::from_string("(optional int)", version, epoch), + Box::new(TypeSignature::list_of(IntType, 1).unwrap()), + Box::new(IntType), + ), + CheckErrors::TypeError( + Box::new(TypeSignature::from_string( + "(optional bool)", + version, + epoch, + )), + Box::new(TypeSignature::from_string("(optional int)", version, epoch)), ), ]; @@ -1180,7 +1227,7 @@ fn test_eqs(#[case] version: ClarityVersion, #[case] epoch: StacksEpochId) { } for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { - assert_eq!(expected, &type_check_helper(bad_test).unwrap_err().err); + assert_eq!(*expected, *type_check_helper(bad_test).unwrap_err().err); } } @@ -1201,7 +1248,7 @@ fn test_asserts() { let bad_expected = [ CheckErrors::IncorrectArgumentCount(2, 1), - CheckErrors::TypeError(BoolType, IntType), + CheckErrors::TypeError(Box::new(BoolType), Box::new(IntType)), CheckErrors::IncorrectArgumentCount(2, 3), ]; @@ -1213,7 +1260,7 @@ fn test_asserts() { } for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { - assert_eq!(expected, &type_check_helper(bad_test).unwrap_err().err); + assert_eq!(*expected, *type_check_helper(bad_test).unwrap_err().err); } } @@ -1266,23 +1313,23 @@ fn test_lists() { "(map + (list 1 2 3 4 5) (list true true true true true))", ]; let bad_expected = [ - CheckErrors::TypeError(BoolType, IntType), + CheckErrors::TypeError(Box::new(BoolType), Box::new(IntType)), CheckErrors::IncorrectArgumentCount(1, 2), CheckErrors::IncorrectArgumentCount(1, 2), - CheckErrors::TypeError(IntType, BoolType), - CheckErrors::TypeError(IntType, BoolType), - CheckErrors::TypeError(IntType, BoolType), - CheckErrors::TypeError(BoolType, buff_type(20)), - CheckErrors::TypeError(BoolType, buff_type(20)), - CheckErrors::TypeError(BoolType, IntType), + CheckErrors::TypeError(Box::new(IntType), Box::new(BoolType)), + CheckErrors::TypeError(Box::new(IntType), Box::new(BoolType)), + CheckErrors::TypeError(Box::new(IntType), Box::new(BoolType)), + CheckErrors::TypeError(Box::new(BoolType), Box::new(buff_type(20))), + CheckErrors::TypeError(Box::new(BoolType), Box::new(buff_type(20))), + CheckErrors::TypeError(Box::new(BoolType), Box::new(IntType)), CheckErrors::IncorrectArgumentCount(2, 3), CheckErrors::UnknownFunction("ynot".to_string()), CheckErrors::IllegalOrUnknownFunctionApplication("if".to_string()), CheckErrors::IncorrectArgumentCount(2, 1), - CheckErrors::UnionTypeError(vec![IntType, UIntType], BoolType), - CheckErrors::ExpectedSequence(UIntType), - CheckErrors::ExpectedSequence(IntType), - CheckErrors::TypeError(IntType, BoolType), + CheckErrors::UnionTypeError(vec![IntType, UIntType], Box::new(BoolType)), + CheckErrors::ExpectedSequence(Box::new(UIntType)), + CheckErrors::ExpectedSequence(Box::new(IntType)), + CheckErrors::TypeError(Box::new(IntType), Box::new(BoolType)), ]; for (good_test, expected) in good.iter().zip(expected.iter()) { @@ -1293,7 +1340,7 @@ fn test_lists() { } for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { - assert_eq!(expected, &type_check_helper(bad_test).unwrap_err().err); + assert_eq!(*expected, *type_check_helper(bad_test).unwrap_err().err); } } @@ -1323,20 +1370,20 @@ fn test_buff() { "(len 1)", ]; let bad_expected = [ - CheckErrors::TypeError(BoolType, IntType), + CheckErrors::TypeError(Box::new(BoolType), Box::new(IntType)), CheckErrors::IncorrectArgumentCount(1, 2), - CheckErrors::TypeError(IntType, BoolType), - CheckErrors::TypeError(IntType, BoolType), - CheckErrors::TypeError(IntType, BoolType), - CheckErrors::TypeError(BoolType, buff_type(20)), - CheckErrors::TypeError(BoolType, IntType), + CheckErrors::TypeError(Box::new(IntType), Box::new(BoolType)), + CheckErrors::TypeError(Box::new(IntType), Box::new(BoolType)), + CheckErrors::TypeError(Box::new(IntType), Box::new(BoolType)), + CheckErrors::TypeError(Box::new(BoolType), Box::new(buff_type(20))), + CheckErrors::TypeError(Box::new(BoolType), Box::new(IntType)), CheckErrors::IncorrectArgumentCount(2, 3), CheckErrors::UnknownFunction("ynot".to_string()), CheckErrors::IllegalOrUnknownFunctionApplication("if".to_string()), CheckErrors::IncorrectArgumentCount(2, 1), - CheckErrors::UnionTypeError(vec![IntType, UIntType], BoolType), - CheckErrors::ExpectedSequence(UIntType), - CheckErrors::ExpectedSequence(IntType), + CheckErrors::UnionTypeError(vec![IntType, UIntType], Box::new(BoolType)), + CheckErrors::ExpectedSequence(Box::new(UIntType)), + CheckErrors::ExpectedSequence(Box::new(IntType)), ]; for (good_test, expected) in good.iter().zip(expected.iter()) { @@ -1347,7 +1394,7 @@ fn test_buff() { } for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { - assert_eq!(expected, &type_check_helper(bad_test).unwrap_err().err); + assert_eq!(*expected, *type_check_helper(bad_test).unwrap_err().err); } } @@ -1409,7 +1456,7 @@ fn test_native_as_max_len() { CheckErrors::ValueTooLarge, ]; for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { - assert_eq!(expected, &type_check_helper(bad_test).unwrap_err().err); + assert_eq!(*expected, *type_check_helper(bad_test).unwrap_err().err); } } @@ -1450,12 +1497,12 @@ fn test_native_append() { ]; let bad_expected = [ - CheckErrors::TypeError(IntType, UIntType), - CheckErrors::TypeError(UIntType, IntType), + CheckErrors::TypeError(Box::new(IntType), Box::new(UIntType)), + CheckErrors::TypeError(Box::new(UIntType), Box::new(IntType)), CheckErrors::IncorrectArgumentCount(2, 1), ]; for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { - assert_eq!(expected, &type_check_helper(bad_test).unwrap_err().err); + assert_eq!(*expected, *type_check_helper(bad_test).unwrap_err().err); } } @@ -1490,12 +1537,12 @@ fn test_slice_list() { ]; let bad_expected = [ - CheckErrors::TypeError(UIntType, IntType), - CheckErrors::TypeError(UIntType, IntType), + CheckErrors::TypeError(Box::new(UIntType), Box::new(IntType)), + CheckErrors::TypeError(Box::new(UIntType), Box::new(IntType)), CheckErrors::IncorrectArgumentCount(3, 2), ]; for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { - assert_eq!(expected, &type_check_helper(bad_test).unwrap_err().err); + assert_eq!(*expected, *type_check_helper(bad_test).unwrap_err().err); } } @@ -1521,12 +1568,12 @@ fn test_slice_buff() { ]; let bad_expected = [ - CheckErrors::TypeError(UIntType, IntType), - CheckErrors::TypeError(UIntType, IntType), + CheckErrors::TypeError(Box::new(UIntType), Box::new(IntType)), + CheckErrors::TypeError(Box::new(UIntType), Box::new(IntType)), CheckErrors::IncorrectArgumentCount(3, 2), ]; for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { - assert_eq!(expected, &type_check_helper(bad_test).unwrap_err().err); + assert_eq!(*expected, *type_check_helper(bad_test).unwrap_err().err); } } @@ -1555,12 +1602,12 @@ fn test_slice_ascii() { ]; let bad_expected = [ - CheckErrors::TypeError(UIntType, IntType), - CheckErrors::TypeError(UIntType, IntType), + CheckErrors::TypeError(Box::new(UIntType), Box::new(IntType)), + CheckErrors::TypeError(Box::new(UIntType), Box::new(IntType)), CheckErrors::IncorrectArgumentCount(3, 2), ]; for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { - assert_eq!(expected, &type_check_helper(bad_test).unwrap_err().err); + assert_eq!(*expected, *type_check_helper(bad_test).unwrap_err().err); } } @@ -1586,12 +1633,12 @@ fn test_slice_utf8() { ]; let bad_expected = [ - CheckErrors::TypeError(UIntType, IntType), - CheckErrors::TypeError(UIntType, IntType), + CheckErrors::TypeError(Box::new(UIntType), Box::new(IntType)), + CheckErrors::TypeError(Box::new(UIntType), Box::new(IntType)), CheckErrors::IncorrectArgumentCount(3, 2), ]; for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { - assert_eq!(expected, &type_check_helper(bad_test).unwrap_err().err); + assert_eq!(*expected, *type_check_helper(bad_test).unwrap_err().err); } } @@ -1635,20 +1682,26 @@ fn test_replace_at_list() { let bad_expected = [ CheckErrors::TypeError( - IntType, - SequenceType(ListType(ListTypeData::new_list(IntType, 1).unwrap())), + Box::new(IntType), + Box::new(SequenceType(ListType( + ListTypeData::new_list(IntType, 1).unwrap(), + ))), ), - CheckErrors::TypeError(IntType, BoolType), - CheckErrors::TypeError(UIntType, IntType), + CheckErrors::TypeError(Box::new(IntType), Box::new(BoolType)), + CheckErrors::TypeError(Box::new(UIntType), Box::new(IntType)), CheckErrors::IncorrectArgumentCount(3, 4), CheckErrors::IncorrectArgumentCount(3, 2), CheckErrors::TypeError( - SequenceType(ListType(ListTypeData::new_list(IntType, 1).unwrap())), - SequenceType(ListType(ListTypeData::new_list(IntType, 2).unwrap())), + Box::new(SequenceType(ListType( + ListTypeData::new_list(IntType, 1).unwrap(), + ))), + Box::new(SequenceType(ListType( + ListTypeData::new_list(IntType, 2).unwrap(), + ))), ), ]; for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { - assert_eq!(expected, &type_check_helper(bad_test).unwrap_err().err); + assert_eq!(*expected, *type_check_helper(bad_test).unwrap_err().err); } } @@ -1687,23 +1740,25 @@ fn test_replace_at_buff() { let buff_len_two = BufferLength::try_from(2u32).unwrap(); let bad_expected = [ CheckErrors::TypeError( - SequenceType(BufferType(buff_len.clone())), - SequenceType(ListType(ListTypeData::new_list(IntType, 1).unwrap())), + Box::new(SequenceType(BufferType(buff_len.clone()))), + Box::new(SequenceType(ListType( + ListTypeData::new_list(IntType, 1).unwrap(), + ))), ), CheckErrors::TypeError( - SequenceType(BufferType(buff_len.clone())), - SequenceType(StringType(ASCII(buff_len.clone()))), + Box::new(SequenceType(BufferType(buff_len.clone()))), + Box::new(SequenceType(StringType(ASCII(buff_len.clone())))), ), - CheckErrors::TypeError(UIntType, IntType), + CheckErrors::TypeError(Box::new(UIntType), Box::new(IntType)), CheckErrors::IncorrectArgumentCount(3, 4), CheckErrors::IncorrectArgumentCount(3, 2), CheckErrors::TypeError( - SequenceType(BufferType(buff_len)), - SequenceType(BufferType(buff_len_two)), + Box::new(SequenceType(BufferType(buff_len))), + Box::new(SequenceType(BufferType(buff_len_two))), ), ]; for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { - assert_eq!(expected, &type_check_helper(bad_test).unwrap_err().err); + assert_eq!(*expected, *type_check_helper(bad_test).unwrap_err().err); } } @@ -1743,23 +1798,25 @@ fn test_replace_at_ascii() { let buff_len_two = BufferLength::try_from(2u32).unwrap(); let bad_expected = [ CheckErrors::TypeError( - SequenceType(StringType(ASCII(buff_len.clone()))), - SequenceType(ListType(ListTypeData::new_list(IntType, 1).unwrap())), + Box::new(SequenceType(StringType(ASCII(buff_len.clone())))), + Box::new(SequenceType(ListType( + ListTypeData::new_list(IntType, 1).unwrap(), + ))), ), CheckErrors::TypeError( - SequenceType(StringType(ASCII(buff_len.clone()))), - SequenceType(BufferType(buff_len.clone())), + Box::new(SequenceType(StringType(ASCII(buff_len.clone())))), + Box::new(SequenceType(BufferType(buff_len.clone()))), ), - CheckErrors::TypeError(UIntType, IntType), + CheckErrors::TypeError(Box::new(UIntType), Box::new(IntType)), CheckErrors::IncorrectArgumentCount(3, 4), CheckErrors::IncorrectArgumentCount(3, 2), CheckErrors::TypeError( - SequenceType(StringType(ASCII(buff_len))), - SequenceType(StringType(ASCII(buff_len_two))), + Box::new(SequenceType(StringType(ASCII(buff_len)))), + Box::new(SequenceType(StringType(ASCII(buff_len_two)))), ), ]; for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { - assert_eq!(expected, &type_check_helper(bad_test).unwrap_err().err); + assert_eq!(*expected, *type_check_helper(bad_test).unwrap_err().err); } } @@ -1799,23 +1856,25 @@ fn test_replace_at_utf8() { let str_len_two = StringUTF8Length::try_from(2u32).unwrap(); let bad_expected = [ CheckErrors::TypeError( - SequenceType(StringType(UTF8(str_len.clone()))), - SequenceType(ListType(ListTypeData::new_list(IntType, 1).unwrap())), + Box::new(SequenceType(StringType(UTF8(str_len.clone())))), + Box::new(SequenceType(ListType( + ListTypeData::new_list(IntType, 1).unwrap(), + ))), ), CheckErrors::TypeError( - SequenceType(StringType(UTF8(str_len.clone()))), - SequenceType(BufferType(buff_len)), + Box::new(SequenceType(StringType(UTF8(str_len.clone())))), + Box::new(SequenceType(BufferType(buff_len))), ), - CheckErrors::TypeError(UIntType, IntType), + CheckErrors::TypeError(Box::new(UIntType), Box::new(IntType)), CheckErrors::IncorrectArgumentCount(3, 4), CheckErrors::IncorrectArgumentCount(3, 2), CheckErrors::TypeError( - SequenceType(StringType(UTF8(str_len))), - SequenceType(StringType(UTF8(str_len_two))), + Box::new(SequenceType(StringType(UTF8(str_len)))), + Box::new(SequenceType(StringType(UTF8(str_len_two)))), ), ]; for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { - assert_eq!(expected, &type_check_helper(bad_test).unwrap_err().err); + assert_eq!(*expected, *type_check_helper(bad_test).unwrap_err().err); } } @@ -1838,12 +1897,12 @@ fn test_native_concat() { ]; let bad_expected = [ - CheckErrors::TypeError(IntType, UIntType), - CheckErrors::TypeError(UIntType, IntType), + CheckErrors::TypeError(Box::new(IntType), Box::new(UIntType)), + CheckErrors::TypeError(Box::new(UIntType), Box::new(IntType)), CheckErrors::IncorrectArgumentCount(2, 1), ]; for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { - assert_eq!(expected, &type_check_helper(bad_test).unwrap_err().err); + assert_eq!(*expected, *type_check_helper(bad_test).unwrap_err().err); } } @@ -1923,8 +1982,8 @@ fn test_tuples() { ]; let bad_expected = [ - CheckErrors::TypeError(IntType, BoolType), - CheckErrors::TypeError(BoolType, IntType), + CheckErrors::TypeError(Box::new(IntType), Box::new(BoolType)), + CheckErrors::TypeError(Box::new(BoolType), Box::new(IntType)), ]; for (good_test, expected) in good.iter().zip(expected.iter()) { @@ -1935,7 +1994,7 @@ fn test_tuples() { } for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { - assert_eq!(expected, &type_check_helper(bad_test).unwrap_err().err); + assert_eq!(*expected, *type_check_helper(bad_test).unwrap_err().err); } } @@ -1947,7 +2006,7 @@ fn test_empty_tuple_should_fail() { "#; assert_eq!( - mem_type_check(contract_src).unwrap_err().err, + *mem_type_check(contract_src).unwrap_err().err, CheckErrors::EmptyTuplesNotAllowed, ); } @@ -2028,9 +2087,9 @@ fn test_simple_uints() { let bad = ["(> u1 1)", "(to-uint true)", "(to-int false)"]; let bad_expected = [ - CheckErrors::TypeError(UIntType, IntType), - CheckErrors::TypeError(IntType, BoolType), - CheckErrors::TypeError(UIntType, BoolType), + CheckErrors::TypeError(Box::new(UIntType), Box::new(IntType)), + CheckErrors::TypeError(Box::new(IntType), Box::new(BoolType)), + CheckErrors::TypeError(Box::new(UIntType), Box::new(BoolType)), ]; for (good_test, expected) in good.iter().zip(expected.iter()) { @@ -2039,7 +2098,7 @@ fn test_simple_uints() { } for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { - assert_eq!(&mem_type_check(bad_test).unwrap_err().err, expected); + assert_eq!(*mem_type_check(bad_test).unwrap_err().err, *expected); } } @@ -2065,12 +2124,20 @@ fn test_buffer_to_ints() { CheckErrors::IncorrectArgumentCount(1, 2), CheckErrors::IncorrectArgumentCount(1, 0), CheckErrors::TypeError( - SequenceType(BufferType(BufferLength::try_from(16_u32).unwrap())), - SequenceType(BufferType(BufferLength::try_from(17_u32).unwrap())), + Box::new(SequenceType(BufferType( + BufferLength::try_from(16_u32).unwrap(), + ))), + Box::new(SequenceType(BufferType( + BufferLength::try_from(17_u32).unwrap(), + ))), ), CheckErrors::TypeError( - SequenceType(BufferType(BufferLength::try_from(16_u32).unwrap())), - SequenceType(StringType(ASCII(BufferLength::try_from(1_u32).unwrap()))), + Box::new(SequenceType(BufferType( + BufferLength::try_from(16_u32).unwrap(), + ))), + Box::new(SequenceType(StringType(ASCII( + BufferLength::try_from(1_u32).unwrap(), + )))), ), ]; @@ -2080,7 +2147,7 @@ fn test_buffer_to_ints() { } for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { - assert_eq!(&mem_type_check(bad_test).unwrap_err().err, expected); + assert_eq!(*mem_type_check(bad_test).unwrap_err().err, *expected); } } @@ -2132,21 +2199,29 @@ fn test_string_to_ints() { CheckErrors::IncorrectArgumentCount(1, 0), CheckErrors::UnionTypeError( vec![IntType, UIntType], - SequenceType(BufferType(BufferLength::try_from(17_u32).unwrap())), + Box::new(SequenceType(BufferType( + BufferLength::try_from(17_u32).unwrap(), + ))), ), CheckErrors::UnionTypeError( vec![IntType, UIntType], - SequenceType(StringType(ASCII(BufferLength::try_from(1_u32).unwrap()))), + Box::new(SequenceType(StringType(ASCII( + BufferLength::try_from(1_u32).unwrap(), + )))), ), CheckErrors::IncorrectArgumentCount(1, 2), CheckErrors::IncorrectArgumentCount(1, 0), CheckErrors::UnionTypeError( vec![IntType, UIntType], - SequenceType(BufferType(BufferLength::try_from(17_u32).unwrap())), + Box::new(SequenceType(BufferType( + BufferLength::try_from(17_u32).unwrap(), + ))), ), CheckErrors::UnionTypeError( vec![IntType, UIntType], - SequenceType(StringType(ASCII(BufferLength::try_from(1_u32).unwrap()))), + Box::new(SequenceType(StringType(ASCII( + BufferLength::try_from(1_u32).unwrap(), + )))), ), CheckErrors::IncorrectArgumentCount(1, 2), CheckErrors::IncorrectArgumentCount(1, 0), @@ -2155,14 +2230,16 @@ fn test_string_to_ints() { TypeSignature::max_string_ascii().unwrap(), TypeSignature::max_string_utf8().unwrap(), ], - SequenceType(BufferType(BufferLength::try_from(17_u32).unwrap())), + Box::new(SequenceType(BufferType( + BufferLength::try_from(17_u32).unwrap(), + ))), ), CheckErrors::UnionTypeError( vec![ TypeSignature::max_string_ascii().unwrap(), TypeSignature::max_string_utf8().unwrap(), ], - IntType, + Box::new(IntType), ), CheckErrors::IncorrectArgumentCount(1, 2), CheckErrors::IncorrectArgumentCount(1, 0), @@ -2171,14 +2248,16 @@ fn test_string_to_ints() { TypeSignature::max_string_ascii().unwrap(), TypeSignature::max_string_utf8().unwrap(), ], - SequenceType(BufferType(BufferLength::try_from(17_u32).unwrap())), + Box::new(SequenceType(BufferType( + BufferLength::try_from(17_u32).unwrap(), + ))), ), CheckErrors::UnionTypeError( vec![ TypeSignature::max_string_ascii().unwrap(), TypeSignature::max_string_utf8().unwrap(), ], - IntType, + Box::new(IntType), ), ]; @@ -2188,7 +2267,7 @@ fn test_string_to_ints() { } for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { - assert_eq!(&mem_type_check(bad_test).unwrap_err().err, expected); + assert_eq!(*mem_type_check(bad_test).unwrap_err().err, *expected); } } @@ -2229,10 +2308,14 @@ fn test_response_inference(#[case] version: ClarityVersion, #[case] epoch: Stack let bad_expected = [ CheckErrors::TypeError( - TypeSignature::from_string("(response bool int)", version, epoch), - BoolType, + Box::new(TypeSignature::from_string( + "(response bool int)", + version, + epoch, + )), + Box::new(BoolType), ), - CheckErrors::ReturnTypesMustMatch(IntType, BoolType), + CheckErrors::ReturnTypesMustMatch(Box::new(IntType), Box::new(BoolType)), CheckErrors::CouldNotDetermineResponseOkType, ]; @@ -2242,7 +2325,7 @@ fn test_response_inference(#[case] version: ClarityVersion, #[case] epoch: Stack } for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { - assert_eq!(&mem_type_check(bad_test).unwrap_err().err, expected); + assert_eq!(*mem_type_check(bad_test).unwrap_err().err, *expected); } } @@ -2350,20 +2433,20 @@ fn test_options(#[case] version: ClarityVersion, #[case] epoch: StacksEpochId) { if version < ClarityVersion::Clarity2 { assert!( - match mem_run_analysis(contract, version, epoch).unwrap_err().err { + match *mem_run_analysis(contract, version, epoch).unwrap_err().err { CheckErrors::TypeError(t1, t2) => { - t1 == TypeSignature::from_string("(optional bool)", version, epoch) - && t2 == TypeSignature::from_string("(optional int)", version, epoch) + *t1 == TypeSignature::from_string("(optional bool)", version, epoch) + && *t2 == TypeSignature::from_string("(optional int)", version, epoch) } _ => false, } ); } else { assert!( - match mem_run_analysis(contract, version, epoch).unwrap_err().err { + match *mem_run_analysis(contract, version, epoch).unwrap_err().err { CheckErrors::TypeError(t1, t2) => { - t1 == TypeSignature::from_string("bool", version, epoch) - && t2 == TypeSignature::from_string("int", version, epoch) + *t1 == TypeSignature::from_string("bool", version, epoch) + && *t2 == TypeSignature::from_string("int", version, epoch) } _ => false, } @@ -2378,7 +2461,7 @@ fn test_list_nones() { (let ((a (list none none none))) (print a)))"; assert_eq!( "(list 3 (optional UnknownType))", - &format!("{}", mem_type_check(contract).unwrap().0.unwrap()) + &format!("{}", mem_type_check(contract).unwrap().0.unwrap()), ); } @@ -2468,7 +2551,10 @@ fn test_missing_value_on_declaration_should_fail() { "#; let res = mem_type_check(contract_src).unwrap_err(); - assert!(matches!(res.err, CheckErrors::IncorrectArgumentCount(_, _))); + assert!(matches!( + *res.err, + CheckErrors::IncorrectArgumentCount(_, _) + )); } #[test] @@ -2478,7 +2564,7 @@ fn test_mismatching_type_on_declaration_should_fail() { "#; let res = mem_type_check(contract_src).unwrap_err(); - assert!(matches!(res.err, CheckErrors::TypeError(_, _))); + assert!(matches!(*res.err, CheckErrors::TypeError(_, _))); } #[test] @@ -2494,7 +2580,7 @@ fn test_mismatching_type_on_update_should_fail() { "#; let res = mem_type_check(contract_src).unwrap_err(); - assert!(matches!(res.err, CheckErrors::TypeError(_, _))); + assert!(matches!(*res.err, CheckErrors::TypeError(_, _))); } #[test] @@ -2506,7 +2592,7 @@ fn test_direct_access_to_persisted_var_should_fail() { "#; let res = mem_type_check(contract_src).unwrap_err(); - assert!(matches!(res.err, CheckErrors::UndefinedVariable(_))); + assert!(matches!(*res.err, CheckErrors::UndefinedVariable(_))); } #[test] @@ -2521,7 +2607,7 @@ fn test_data_var_shadowed_by_let_should_fail() { "#; let res = mem_type_check(contract_src).unwrap_err(); - assert!(matches!(res.err, CheckErrors::NameAlreadyUsed(_))); + assert!(matches!(*res.err, CheckErrors::NameAlreadyUsed(_))); } #[test] @@ -2534,7 +2620,7 @@ fn test_mutating_unknown_data_var_should_fail() { "#; let res = mem_type_check(contract_src).unwrap_err(); - assert!(matches!(res.err, CheckErrors::NoSuchDataVariable(_))); + assert!(matches!(*res.err, CheckErrors::NoSuchDataVariable(_))); } #[test] @@ -2545,7 +2631,7 @@ fn test_accessing_unknown_data_var_should_fail() { "#; let res = mem_type_check(contract_src).unwrap_err(); - assert!(matches!(res.err, CheckErrors::NoSuchDataVariable(_))); + assert!(matches!(*res.err, CheckErrors::NoSuchDataVariable(_))); } #[test] @@ -2556,7 +2642,7 @@ fn test_let_shadowed_by_let_should_fail() { "#; let res = mem_type_check(contract_src).unwrap_err(); - assert!(matches!(res.err, CheckErrors::NameAlreadyUsed(_))); + assert!(matches!(*res.err, CheckErrors::NameAlreadyUsed(_))); } #[test] @@ -2568,7 +2654,7 @@ fn test_let_shadowed_by_nested_let_should_fail() { "#; let res = mem_type_check(contract_src).unwrap_err(); - assert!(matches!(res.err, CheckErrors::NameAlreadyUsed(_))); + assert!(matches!(*res.err, CheckErrors::NameAlreadyUsed(_))); } #[test] @@ -2581,7 +2667,7 @@ fn test_define_constant_shadowed_by_let_should_fail() { "#; let res = mem_type_check(contract_src).unwrap_err(); - assert!(matches!(res.err, CheckErrors::NameAlreadyUsed(_))); + assert!(matches!(*res.err, CheckErrors::NameAlreadyUsed(_))); } #[test] @@ -2593,7 +2679,7 @@ fn test_define_constant_shadowed_by_argument_should_fail() { "#; let res = mem_type_check(contract_src).unwrap_err(); - assert!(matches!(res.err, CheckErrors::NameAlreadyUsed(_))); + assert!(matches!(*res.err, CheckErrors::NameAlreadyUsed(_))); } #[test] @@ -2793,7 +2879,7 @@ fn test_fetch_entry_mismatching_type_signatures() { ({case}))" ); let res = mem_type_check(&contract_src).unwrap_err(); - assert!(matches!(res.err, CheckErrors::TypeError(_, _))); + assert!(matches!(*res.err, CheckErrors::TypeError(_, _))); } } @@ -2808,7 +2894,7 @@ fn test_fetch_entry_unbound_variables() { ({case}))" ); let res = mem_type_check(&contract_src).unwrap_err(); - assert!(matches!(res.err, CheckErrors::UndefinedVariable(_))); + assert!(matches!(*res.err, CheckErrors::UndefinedVariable(_))); } } @@ -2850,7 +2936,7 @@ fn test_insert_entry_mismatching_type_signatures() { ({case}))" ); let res = mem_type_check(&contract_src).unwrap_err(); - assert!(matches!(res.err, CheckErrors::TypeError(_, _))); + assert!(matches!(*res.err, CheckErrors::TypeError(_, _))); } } @@ -2868,7 +2954,7 @@ fn test_insert_entry_unbound_variables() { ({case}))" ); let res = mem_type_check(&contract_src).unwrap_err(); - assert!(matches!(res.err, CheckErrors::UndefinedVariable(_))); + assert!(matches!(*res.err, CheckErrors::UndefinedVariable(_))); } } @@ -2908,7 +2994,7 @@ fn test_delete_entry_mismatching_type_signatures() { ({case}))" ); let res = mem_type_check(&contract_src).unwrap_err(); - assert!(matches!(res.err, CheckErrors::TypeError(_, _))); + assert!(matches!(*res.err, CheckErrors::TypeError(_, _))); } } @@ -2923,7 +3009,7 @@ fn test_delete_entry_unbound_variables() { ({case}))" ); let res = mem_type_check(&contract_src).unwrap_err(); - assert!(matches!(res.err, CheckErrors::UndefinedVariable(_))); + assert!(matches!(*res.err, CheckErrors::UndefinedVariable(_))); } } @@ -2967,7 +3053,7 @@ fn test_set_entry_mismatching_type_signatures() { ({case}))" ); let res = mem_type_check(&contract_src).unwrap_err(); - assert!(matches!(res.err, CheckErrors::TypeError(_, _))); + assert!(matches!(*res.err, CheckErrors::TypeError(_, _))); } } @@ -2985,7 +3071,7 @@ fn test_set_entry_unbound_variables() { ({case}))" ); let res = mem_type_check(&contract_src).unwrap_err(); - assert!(matches!(res.err, CheckErrors::UndefinedVariable(_))); + assert!(matches!(*res.err, CheckErrors::UndefinedVariable(_))); } } @@ -3101,7 +3187,7 @@ fn test_buff_negative_len() { (func 0x00)"; let res = mem_type_check(contract_src).unwrap_err(); - assert_eq!(res.err, CheckErrors::ValueOutOfBounds); + assert_eq!(*res.err, CheckErrors::ValueOutOfBounds); } #[test] @@ -3110,7 +3196,7 @@ fn test_string_ascii_negative_len() { (func \"\")"; let res = mem_type_check(contract_src).unwrap_err(); - assert_eq!(res.err, CheckErrors::ValueOutOfBounds); + assert_eq!(*res.err, CheckErrors::ValueOutOfBounds); } #[test] @@ -3119,7 +3205,7 @@ fn test_string_utf8_negative_len() { (func u\"\")"; let res = mem_type_check(contract_src).unwrap_err(); - assert_eq!(res.err, CheckErrors::ValueOutOfBounds); + assert_eq!(*res.err, CheckErrors::ValueOutOfBounds); } #[test] @@ -3175,7 +3261,7 @@ fn test_comparison_types() { ))), SequenceType(BufferType(BufferLength::try_from(1048576_u32).unwrap())), ], - PrincipalType, + Box::new(PrincipalType), ), CheckErrors::UnionTypeError( vec![ @@ -3189,23 +3275,41 @@ fn test_comparison_types() { ))), SequenceType(BufferType(BufferLength::try_from(1048576_u32).unwrap())), ], - SequenceType(ListType(ListTypeData::new_list(IntType, 3).unwrap())), + Box::new(SequenceType(ListType( + ListTypeData::new_list(IntType, 3).unwrap(), + ))), ), CheckErrors::TypeError( - SequenceType(StringType(UTF8(StringUTF8Length::try_from(3u32).unwrap()))), - SequenceType(StringType(ASCII(BufferLength::try_from(2_u32).unwrap()))), + Box::new(SequenceType(StringType(UTF8( + StringUTF8Length::try_from(3u32).unwrap(), + )))), + Box::new(SequenceType(StringType(ASCII( + BufferLength::try_from(2_u32).unwrap(), + )))), ), CheckErrors::TypeError( - SequenceType(StringType(ASCII(BufferLength::try_from(3_u32).unwrap()))), - SequenceType(BufferType(BufferLength::try_from(2_u32).unwrap())), + Box::new(SequenceType(StringType(ASCII( + BufferLength::try_from(3_u32).unwrap(), + )))), + Box::new(SequenceType(BufferType( + BufferLength::try_from(2_u32).unwrap(), + ))), ), CheckErrors::TypeError( - SequenceType(BufferType(BufferLength::try_from(2_u32).unwrap())), - SequenceType(StringType(UTF8(StringUTF8Length::try_from(3u32).unwrap()))), + Box::new(SequenceType(BufferType( + BufferLength::try_from(2_u32).unwrap(), + ))), + Box::new(SequenceType(StringType(UTF8( + StringUTF8Length::try_from(3u32).unwrap(), + )))), ), CheckErrors::TypeError( - SequenceType(BufferType(BufferLength::try_from(2_u32).unwrap())), - SequenceType(StringType(ASCII(BufferLength::try_from(3_u32).unwrap()))), + Box::new(SequenceType(BufferType( + BufferLength::try_from(2_u32).unwrap(), + ))), + Box::new(SequenceType(StringType(ASCII( + BufferLength::try_from(3_u32).unwrap(), + )))), ), CheckErrors::IncorrectArgumentCount(2, 0), CheckErrors::IncorrectArgumentCount(2, 1), @@ -3213,7 +3317,7 @@ fn test_comparison_types() { ]; for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { - assert_eq!(expected, &type_check_helper(bad_test).unwrap_err().err); + assert_eq!(*expected, *type_check_helper(bad_test).unwrap_err().err); } } @@ -3240,7 +3344,10 @@ fn test_principal_destruct() { let bad_expected = [ CheckErrors::IncorrectArgumentCount(1, 2), CheckErrors::IncorrectArgumentCount(1, 0), - CheckErrors::TypeError(TypeSignature::PrincipalType, BUFF_1.clone()), + CheckErrors::TypeError( + Box::new(TypeSignature::PrincipalType), + Box::new(BUFF_1.clone()), + ), ]; for (good_test, expected) in good.iter().zip(expected.iter()) { @@ -3251,7 +3358,7 @@ fn test_principal_destruct() { } for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { - assert_eq!(expected, &type_check_helper(bad_test).unwrap_err().err); + assert_eq!(*expected, *type_check_helper(bad_test).unwrap_err().err); } } @@ -3301,32 +3408,32 @@ fn test_principal_construct() { // The first buffer is too long, should be `(buff 1)`. ( r#"(principal-construct? 0xfa6bf38ed557fe417333710d6033e9419391a320 0xfa6bf38ed557fe417333710d6033e9419391a320)"#, - CheckErrors::TypeError(BUFF_1.clone(), BUFF_20.clone()), + CheckErrors::TypeError(Box::new(BUFF_1.clone()), Box::new(BUFF_20.clone())), ), // The second buffer is too long, should be `(buff 20)`. ( r#"(principal-construct? 0x22 0xfa6bf38ed557fe417333710d6033e9419391a32009)"#, - CheckErrors::TypeError(BUFF_20.clone(), BUFF_21.clone()), + CheckErrors::TypeError(Box::new(BUFF_20.clone()), Box::new(BUFF_21.clone())), ), // `int` argument instead of `(buff 1)` for version. ( r#"(principal-construct? 22 0xfa6bf38ed557fe417333710d6033e9419391a320)"#, - CheckErrors::TypeError(BUFF_1.clone(), IntType), + CheckErrors::TypeError(Box::new(BUFF_1.clone()), Box::new(IntType.clone())), ), // `name` argument is too long ( r#"(principal-construct? 0x22 0xfa6bf38ed557fe417333710d6033e9419391a320 "foooooooooooooooooooooooooooooooooooooooo")"#, CheckErrors::TypeError( - TypeSignature::contract_name_string_ascii_type().unwrap(), - TypeSignature::bound_string_ascii_type(41).unwrap(), + Box::new(TypeSignature::contract_name_string_ascii_type().unwrap()), + Box::new(TypeSignature::bound_string_ascii_type(41).unwrap()), ), ), // bad argument type for `name` ( r#"(principal-construct? 0x22 0xfa6bf38ed557fe417333710d6033e9419391a320 u123)"#, CheckErrors::TypeError( - TypeSignature::contract_name_string_ascii_type().unwrap(), - UIntType, + Box::new(TypeSignature::contract_name_string_ascii_type().unwrap()), + Box::new(UIntType), ), ), // too many arguments @@ -3337,7 +3444,7 @@ fn test_principal_construct() { ]; for (bad_test, expected) in bad_pairs.iter() { - assert_eq!(expected, &type_check_helper(bad_test).unwrap_err().err); + assert_eq!(*expected, *type_check_helper(bad_test).unwrap_err().err); } } @@ -3387,14 +3494,14 @@ fn test_trait_args() { let contract_identifier = QualifiedContractIdentifier::transient(); let bad_expected = [CheckErrors::IncompatibleTrait( - TraitIdentifier { + Box::new(TraitIdentifier { name: ClarityName::from("trait-foo"), contract_identifier: contract_identifier.clone(), - }, - TraitIdentifier { + }), + Box::new(TraitIdentifier { name: ClarityName::from("trait-bar"), contract_identifier, - }, + }), )]; for good_test in good.iter() { @@ -3402,7 +3509,7 @@ fn test_trait_args() { } for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { - assert_eq!(expected, &mem_type_check(bad_test).unwrap_err().err); + assert_eq!(*expected, *mem_type_check(bad_test).unwrap_err().err); } } @@ -3553,28 +3660,36 @@ fn test_list_arg(#[case] version: ClarityVersion, #[case] epoch: StacksEpochId) ]; let bad_expected = [ CheckErrors::TypeError( - TypeSignature::list_of(TypeSignature::IntType, 3).unwrap(), - TypeSignature::list_of(TypeSignature::IntType, 4).unwrap(), + Box::new(TypeSignature::list_of(TypeSignature::IntType, 3).unwrap()), + Box::new(TypeSignature::list_of(TypeSignature::IntType, 4).unwrap()), ), CheckErrors::TypeError( - TypeSignature::list_of(TypeSignature::IntType, 3).unwrap(), - TypeSignature::list_of(TypeSignature::UIntType, 1).unwrap(), + Box::new(TypeSignature::list_of(TypeSignature::IntType, 3).unwrap()), + Box::new(TypeSignature::list_of(TypeSignature::UIntType, 1).unwrap()), ), CheckErrors::TypeError( - TypeSignature::list_of(TypeSignature::IntType, 3).unwrap(), - TypeSignature::list_of(TypeSignature::list_of(TypeSignature::NoType, 0).unwrap(), 1) + Box::new(TypeSignature::list_of(TypeSignature::IntType, 3).unwrap()), + Box::new( + TypeSignature::list_of( + TypeSignature::list_of(TypeSignature::NoType, 0).unwrap(), + 1, + ) .unwrap(), + ), ), ]; let bad_expected2 = [ CheckErrors::TypeError( - TypeSignature::list_of(TypeSignature::IntType, 3).unwrap(), - TypeSignature::list_of(TypeSignature::IntType, 4).unwrap(), + Box::new(TypeSignature::list_of(TypeSignature::IntType, 3).unwrap()), + Box::new(TypeSignature::list_of(TypeSignature::IntType, 4).unwrap()), ), - CheckErrors::TypeError(TypeSignature::IntType, TypeSignature::UIntType), CheckErrors::TypeError( - TypeSignature::IntType, - TypeSignature::list_of(TypeSignature::NoType, 0).unwrap(), + Box::new(TypeSignature::IntType), + Box::new(TypeSignature::UIntType), + ), + CheckErrors::TypeError( + Box::new(TypeSignature::IntType), + Box::new(TypeSignature::list_of(TypeSignature::NoType, 0).unwrap()), ), ]; @@ -3587,8 +3702,8 @@ fn test_list_arg(#[case] version: ClarityVersion, #[case] epoch: StacksEpochId) .iter(), ) { assert_eq!( - expected, - &mem_run_analysis(bad_test, version, epoch).unwrap_err().err + *expected, + *mem_run_analysis(bad_test, version, epoch).unwrap_err().err ); } } @@ -3699,7 +3814,7 @@ fn test_simple_bad_syntax_bindings() { for (bad_code, expected_err) in bad.iter().zip(expected.iter()) { debug!("test simple bad syntax binding: '{}'", bad_code); - assert_eq!(expected_err, &type_check_helper(bad_code).unwrap_err().err); + assert_eq!(*expected_err, *type_check_helper(bad_code).unwrap_err().err); } } @@ -3725,6 +3840,6 @@ fn test_nested_bad_type_signature_syntax_bindings() { for (bad_code, expected_err) in bad.iter().zip(expected.iter()) { debug!("test nested bad syntax binding: '{}'", bad_code); - assert_eq!(expected_err, &type_check_helper(bad_code).unwrap_err().err); + assert_eq!(*expected_err, *type_check_helper(bad_code).unwrap_err().err); } } diff --git a/clarity/src/vm/analysis/types.rs b/clarity/src/vm/analysis/types.rs index 4e7f2bcb952..145ce59bd3c 100644 --- a/clarity/src/vm/analysis/types.rs +++ b/clarity/src/vm/analysis/types.rs @@ -16,13 +16,13 @@ use std::collections::{BTreeMap, BTreeSet}; -use clarity_serialization::representations::ClarityName; -use clarity_serialization::types::{QualifiedContractIdentifier, TraitIdentifier, TypeSignature}; +use clarity_types::representations::ClarityName; +use clarity_types::types::{QualifiedContractIdentifier, TraitIdentifier, TypeSignature}; use stacks_common::types::StacksEpochId; use crate::vm::analysis::analysis_db::AnalysisDatabase; use crate::vm::analysis::contract_interface_builder::ContractInterface; -use crate::vm::analysis::errors::{CheckErrors, CheckResult}; +use crate::vm::analysis::errors::{CheckError, CheckErrors}; use crate::vm::analysis::type_checker::contexts::TypeMap; use crate::vm::costs::LimitedCostTracker; use crate::vm::types::signatures::FunctionSignature; @@ -39,7 +39,7 @@ pub trait AnalysisPass { epoch: &StacksEpochId, contract_analysis: &mut ContractAnalysis, analysis_db: &mut AnalysisDatabase, - ) -> CheckResult<()>; + ) -> Result<(), CheckError>; } #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] @@ -230,7 +230,7 @@ impl ContractAnalysis { epoch: &StacksEpochId, trait_identifier: &TraitIdentifier, trait_definition: &BTreeMap, - ) -> CheckResult<()> { + ) -> Result<(), CheckError> { let trait_name = trait_identifier.name.to_string(); for (func_name, expected_sig) in trait_definition.iter() { diff --git a/clarity/src/vm/ast/definition_sorter/mod.rs b/clarity/src/vm/ast/definition_sorter/mod.rs index f883d055c6f..e740514e552 100644 --- a/clarity/src/vm/ast/definition_sorter/mod.rs +++ b/clarity/src/vm/ast/definition_sorter/mod.rs @@ -16,7 +16,7 @@ use std::collections::{HashMap, HashSet}; -use clarity_serialization::representations::ClarityName; +use clarity_types::representations::ClarityName; use crate::vm::ast::errors::{ParseError, ParseErrors, ParseResult}; use crate::vm::ast::types::ContractAST; diff --git a/clarity/src/vm/ast/definition_sorter/tests.rs b/clarity/src/vm/ast/definition_sorter/tests.rs index 0142052c507..9bbb3211781 100644 --- a/clarity/src/vm/ast/definition_sorter/tests.rs +++ b/clarity/src/vm/ast/definition_sorter/tests.rs @@ -63,7 +63,7 @@ fn should_succeed_sorting_contract_case_1(#[case] version: ClarityVersion) { (define-private (wrapped-kv-del (key int)) (kv-del key)) (define-private (kv-del (key int)) - (begin + (begin (map-delete kv-store { key: key }) key)) (define-map kv-store { key: int } { value: int }) @@ -96,7 +96,7 @@ fn should_raise_dependency_cycle_case_1(#[case] version: ClarityVersion) { "#; let err = run_scoped_parsing_helper(contract, version).unwrap_err(); - assert!(matches!(err.err, ParseErrors::CircularReference(_))); + assert!(matches!(*err.err, ParseErrors::CircularReference(_))); } #[apply(test_clarity_versions_definition_sorter)] @@ -109,14 +109,14 @@ fn should_raise_dependency_cycle_case_2(#[case] version: ClarityVersion) { "#; let err = run_scoped_parsing_helper(contract, version).unwrap_err(); - assert!(matches!(err.err, ParseErrors::CircularReference(_))); + assert!(matches!(*err.err, ParseErrors::CircularReference(_))); } #[apply(test_clarity_versions_definition_sorter)] fn should_not_raise_dependency_cycle_case_let(#[case] version: ClarityVersion) { let contract = r#" (define-private (foo (x int)) (begin (bar 1) 1)) - (define-private (bar (x int)) (let ((foo 1)) (+ 1 x))) + (define-private (bar (x int)) (let ((foo 1)) (+ 1 x))) "#; run_scoped_parsing_helper(contract, version).unwrap(); @@ -127,11 +127,11 @@ fn should_not_raise_dependency_cycle_case_let(#[case] version: ClarityVersion) { fn should_raise_dependency_cycle_case_let(#[case] version: ClarityVersion) { let contract = r#" (define-private (foo (x int)) (begin (bar 1) 1)) - (define-private (bar (x int)) (let ((baz (foo 1))) (+ 1 x))) + (define-private (bar (x int)) (let ((baz (foo 1))) (+ 1 x))) "#; let err = run_scoped_parsing_helper(contract, version).unwrap_err(); - assert!(matches!(err.err, ParseErrors::CircularReference(_))); + assert!(matches!(*err.err, ParseErrors::CircularReference(_))); } #[apply(test_clarity_versions_definition_sorter)] @@ -149,18 +149,18 @@ fn should_not_raise_dependency_cycle_case_get(#[case] version: ClarityVersion) { fn should_raise_dependency_cycle_case_get(#[case] version: ClarityVersion) { let contract = r#" (define-private (foo (x int)) (begin (bar 1) 1)) - (define-private (bar (x int)) (let ((res (foo 1))) (+ 1 x))) + (define-private (bar (x int)) (let ((res (foo 1))) (+ 1 x))) "#; let err = run_scoped_parsing_helper(contract, version).unwrap_err(); - assert!(matches!(err.err, ParseErrors::CircularReference(_))); + assert!(matches!(*err.err, ParseErrors::CircularReference(_))); } #[apply(test_clarity_versions_definition_sorter)] fn should_not_raise_dependency_cycle_case_fetch_entry(#[case] version: ClarityVersion) { let contract = r#" (define-private (foo (x int)) (begin (bar 1) 1)) - (define-private (bar (x int)) (map-get? kv-store { foo: 1 })) + (define-private (bar (x int)) (map-get? kv-store { foo: 1 })) (define-map kv-store { foo: int } { bar: int }) "#; @@ -172,19 +172,19 @@ fn should_not_raise_dependency_cycle_case_fetch_entry(#[case] version: ClarityVe fn should_raise_dependency_cycle_case_fetch_entry(#[case] version: ClarityVersion) { let contract = r#" (define-private (foo (x int)) (+ (bar x) x)) - (define-private (bar (x int)) (map-get? kv-store { foo: (foo 1) })) + (define-private (bar (x int)) (map-get? kv-store { foo: (foo 1) })) (define-map kv-store { foo: int } { bar: int }) "#; let err = run_scoped_parsing_helper(contract, version).unwrap_err(); - assert!(matches!(err.err, ParseErrors::CircularReference(_))); + assert!(matches!(*err.err, ParseErrors::CircularReference(_))); } #[apply(test_clarity_versions_definition_sorter)] fn should_not_raise_dependency_cycle_case_delete_entry(#[case] version: ClarityVersion) { let contract = r#" (define-private (foo (x int)) (begin (bar 1) 1)) - (define-private (bar (x int)) (map-delete kv-store (tuple (foo 1)))) + (define-private (bar (x int)) (map-delete kv-store (tuple (foo 1)))) (define-map kv-store { foo: int } { bar: int }) "#; @@ -196,19 +196,19 @@ fn should_not_raise_dependency_cycle_case_delete_entry(#[case] version: ClarityV fn should_raise_dependency_cycle_case_delete_entry(#[case] version: ClarityVersion) { let contract = r#" (define-private (foo (x int)) (+ (bar x) x)) - (define-private (bar (x int)) (map-delete kv-store (tuple (foo (foo 1))))) + (define-private (bar (x int)) (map-delete kv-store (tuple (foo (foo 1))))) (define-map kv-store { foo: int } { bar: int }) "#; let err = run_scoped_parsing_helper(contract, version).unwrap_err(); - assert!(matches!(err.err, ParseErrors::CircularReference(_))); + assert!(matches!(*err.err, ParseErrors::CircularReference(_))); } #[apply(test_clarity_versions_definition_sorter)] fn should_not_raise_dependency_cycle_case_set_entry(#[case] version: ClarityVersion) { let contract = r#" (define-private (foo (x int)) (begin (bar 1) 1)) - (define-private (bar (x int)) (map-set kv-store { foo: 1 } { bar: 3 })) + (define-private (bar (x int)) (map-set kv-store { foo: 1 } { bar: 3 })) (define-map kv-store { foo: int } { bar: int }) "#; @@ -220,19 +220,19 @@ fn should_not_raise_dependency_cycle_case_set_entry(#[case] version: ClarityVers fn should_raise_dependency_cycle_case_set_entry(#[case] version: ClarityVersion) { let contract = r#" (define-private (foo (x int)) (+ (bar x) x)) - (define-private (bar (x int)) (map-set kv-store { foo: 1 } { bar: (foo 1) })) + (define-private (bar (x int)) (map-set kv-store { foo: 1 } { bar: (foo 1) })) (define-map kv-store { foo: int } { bar: int }) "#; let err = run_scoped_parsing_helper(contract, version).unwrap_err(); - assert!(matches!(err.err, ParseErrors::CircularReference(_))); + assert!(matches!(*err.err, ParseErrors::CircularReference(_))); } #[apply(test_clarity_versions_definition_sorter)] fn should_not_raise_dependency_cycle_case_insert_entry(#[case] version: ClarityVersion) { let contract = r#" (define-private (foo (x int)) (begin (bar 1) 1)) - (define-private (bar (x int)) (map-insert kv-store { foo: 1 } { bar: 3 })) + (define-private (bar (x int)) (map-insert kv-store { foo: 1 } { bar: 3 })) (define-map kv-store { foo: int } { bar: int }) "#; @@ -249,18 +249,18 @@ fn should_raise_dependency_cycle_case_insert_entry(#[case] version: ClarityVersi "#; let err = run_scoped_parsing_helper(contract, version).unwrap_err(); - assert!(matches!(err.err, ParseErrors::CircularReference(_))); + assert!(matches!(*err.err, ParseErrors::CircularReference(_))); } #[apply(test_clarity_versions_definition_sorter)] fn should_raise_dependency_cycle_case_fetch_contract_entry(#[case] version: ClarityVersion) { let contract = r#" (define-private (foo (x int)) (+ (bar x) x)) - (define-private (bar (x int)) (map-get? kv-store { foo: (foo 1) })) + (define-private (bar (x int)) (map-get? kv-store { foo: (foo 1) })) "#; let err = run_scoped_parsing_helper(contract, version).unwrap_err(); - assert!(matches!(err.err, ParseErrors::CircularReference(_))); + assert!(matches!(*err.err, ParseErrors::CircularReference(_))); } #[apply(test_clarity_versions_definition_sorter)] diff --git a/clarity/src/vm/ast/errors.rs b/clarity/src/vm/ast/errors.rs index 6d734c475d5..2b9b5beb3c5 100644 --- a/clarity/src/vm/ast/errors.rs +++ b/clarity/src/vm/ast/errors.rs @@ -14,4 +14,4 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -pub use clarity_serialization::errors::ast::{ParseError, ParseErrors, ParseResult, PlacedError}; +pub use clarity_types::errors::ast::{ParseError, ParseErrors, ParseResult, PlacedError}; diff --git a/clarity/src/vm/ast/mod.rs b/clarity/src/vm/ast/mod.rs index d269403d776..ccb78536411 100644 --- a/clarity/src/vm/ast/mod.rs +++ b/clarity/src/vm/ast/mod.rs @@ -416,7 +416,7 @@ mod test { cost_addition_count: 1, }; - assert_eq!(&expected_err, &err.err); + assert_eq!(expected_err, *err.err); assert_eq!(expected_list_cost_state, cost_track); // with new rules, this is now VaryExpressionStackDepthTooDeep @@ -438,7 +438,7 @@ mod test { cost_addition_count: 1, }; - assert_eq!(&expected_err, &err.err); + assert_eq!(expected_err, *err.err); assert_eq!(expected_list_cost_state, cost_track); // you cannot do the same for tuples! @@ -474,7 +474,7 @@ mod test { cost_addition_count: 1, }; - assert_eq!(&expected_err, &err.err); + assert_eq!(expected_err, *err.err); assert_eq!(expected_list_cost_state, cost_track); } @@ -518,7 +518,7 @@ mod test { cost_addition_count: 1, }; - assert_eq!(&expected_err, &err.err); + assert_eq!(expected_err, *err.err); assert_eq!(expected_list_cost_state, cost_track); // in 2.1, this is still ExpressionStackDepthTooDeep @@ -540,7 +540,7 @@ mod test { cost_addition_count: 1, }; - assert_eq!(&expected_err, &err.err); + assert_eq!(expected_err, *err.err); assert_eq!(expected_list_cost_state, cost_track); // in 2.1, ASTRules::Typical is ignored -- this still fails to parse @@ -562,7 +562,7 @@ mod test { cost_addition_count: 1, }; - assert_eq!(&expected_err, &err.err); + assert_eq!(expected_err, *err.err); assert_eq!(expected_list_cost_state, cost_track); // in 2.1, ASTRules::PrecheckSize is still ignored -- this still fails to parse @@ -584,7 +584,7 @@ mod test { cost_addition_count: 1, }; - assert_eq!(&expected_err, &err.err); + assert_eq!(expected_err, *err.err); assert_eq!(expected_list_cost_state, cost_track); } } diff --git a/clarity/src/vm/ast/parser/v1.rs b/clarity/src/vm/ast/parser/v1.rs index d767e5c0923..feb59798956 100644 --- a/clarity/src/vm/ast/parser/v1.rs +++ b/clarity/src/vm/ast/parser/v1.rs @@ -1056,56 +1056,56 @@ mod test { ); assert!(matches!( - ast::parser::v1::parse(split_tokens).unwrap_err().err, + *ast::parser::v1::parse(split_tokens).unwrap_err().err, ParseErrors::SeparatorExpected(_) )); assert!(matches!( - ast::parser::v1::parse(too_much_closure).unwrap_err().err, + *ast::parser::v1::parse(too_much_closure).unwrap_err().err, ParseErrors::ClosingParenthesisUnexpected )); assert!(matches!( - ast::parser::v1::parse(not_enough_closure).unwrap_err().err, + *ast::parser::v1::parse(not_enough_closure).unwrap_err().err, ParseErrors::ClosingParenthesisExpected )); assert!(matches!( - ast::parser::v1::parse(middle_hash).unwrap_err().err, + *ast::parser::v1::parse(middle_hash).unwrap_err().err, ParseErrors::FailedParsingRemainder(_) )); assert!(matches!( - ast::parser::v1::parse(unicode).unwrap_err().err, + *ast::parser::v1::parse(unicode).unwrap_err().err, ParseErrors::FailedParsingRemainder(_) )); assert!(matches!( - ast::parser::v1::parse(name_with_dot).unwrap_err().err, + *ast::parser::v1::parse(name_with_dot).unwrap_err().err, ParseErrors::SeparatorExpected(_) )); assert!(matches!( - ast::parser::v1::parse(wrong_tuple_literal_close) + *ast::parser::v1::parse(wrong_tuple_literal_close) .unwrap_err() .err, ParseErrors::ClosingTupleLiteralExpected )); assert!(matches!( - ast::parser::v1::parse(wrong_list_close).unwrap_err().err, + *ast::parser::v1::parse(wrong_list_close).unwrap_err().err, ParseErrors::ClosingParenthesisExpected )); assert!(matches!( - ast::parser::v1::parse(extra_tuple_literal_close) + *ast::parser::v1::parse(extra_tuple_literal_close) .unwrap_err() .err, ParseErrors::ClosingTupleLiteralUnexpected )); assert!(matches!( - ast::parser::v1::parse(unexpected_comma).unwrap_err().err, + *ast::parser::v1::parse(unexpected_comma).unwrap_err().err, ParseErrors::CommaSeparatorUnexpected )); @@ -1113,91 +1113,91 @@ mod test { ast::parser::v1::parse(tuple_comma_no_space).unwrap(); assert!(matches!( - ast::parser::v1::parse(tuple_colon_no_space) + *ast::parser::v1::parse(tuple_colon_no_space) .unwrap_err() .err, ParseErrors::SeparatorExpectedAfterColon(_) )); assert!(matches!( - ast::parser::v1::parse(shorthand_tuple).unwrap_err().err, + *ast::parser::v1::parse(shorthand_tuple).unwrap_err().err, ParseErrors::TupleColonExpected(_) )); assert!(matches!( - ast::parser::v1::parse(shorthand_tuple_dangling_comma) + *ast::parser::v1::parse(shorthand_tuple_dangling_comma) .unwrap_err() .err, ParseErrors::TupleItemExpected(_) )); assert!(matches!( - ast::parser::v1::parse(decorative_colon_on_value) + *ast::parser::v1::parse(decorative_colon_on_value) .unwrap_err() .err, ParseErrors::TupleCommaExpected(_) )); assert!(matches!( - ast::parser::v1::parse(tuple_literal_colon_after_comma) + *ast::parser::v1::parse(tuple_literal_colon_after_comma) .unwrap_err() .err, ParseErrors::TupleItemExpected(_) )); assert!(matches!( - ast::parser::v1::parse(empty_tuple_literal_comma) + *ast::parser::v1::parse(empty_tuple_literal_comma) .unwrap_err() .err, ParseErrors::TupleItemExpected(_) )); assert!(matches!( - ast::parser::v1::parse(empty_tuple_literal_colon) + *ast::parser::v1::parse(empty_tuple_literal_colon) .unwrap_err() .err, ParseErrors::TupleItemExpected(_) )); assert!(matches!( - ast::parser::v1::parse(legacy_boolean_literals) + *ast::parser::v1::parse(legacy_boolean_literals) .unwrap_err() .err, ParseErrors::FailedParsingRemainder(_) )); assert!(matches!( - ast::parser::v1::parse(function_with_CR).unwrap_err().err, + *ast::parser::v1::parse(function_with_CR).unwrap_err().err, ParseErrors::FailedParsingRemainder(_) )); assert!(matches!( - ast::parser::v1::parse(function_with_CRLF).unwrap_err().err, + *ast::parser::v1::parse(function_with_CRLF).unwrap_err().err, ParseErrors::FailedParsingRemainder(_) )); assert!(matches!( - ast::parser::v1::parse(function_with_NEL).unwrap_err().err, + *ast::parser::v1::parse(function_with_NEL).unwrap_err().err, ParseErrors::FailedParsingRemainder(_) )); assert!(matches!( - ast::parser::v1::parse(function_with_LS).unwrap_err().err, + *ast::parser::v1::parse(function_with_LS).unwrap_err().err, ParseErrors::FailedParsingRemainder(_) )); assert!(matches!( - ast::parser::v1::parse(function_with_PS).unwrap_err().err, + *ast::parser::v1::parse(function_with_PS).unwrap_err().err, ParseErrors::FailedParsingRemainder(_) )); ast::parser::v1::parse(function_with_LF).unwrap(); assert!(matches!( - ast::parser::v1::parse(string_with_invalid_escape) + *ast::parser::v1::parse(string_with_invalid_escape) .unwrap_err() .err, ParseErrors::InvalidEscaping )); assert!(matches!( - ast::parser::v1::parse(ascii_string_with_unicode_escape) + *ast::parser::v1::parse(ascii_string_with_unicode_escape) .unwrap_err() .err, ParseErrors::InvalidEscaping @@ -1224,14 +1224,14 @@ mod test { ); assert!(matches!( - ast::parser::v1::parse(&exceeds_stack_depth_tuple) + *ast::parser::v1::parse(&exceeds_stack_depth_tuple) .unwrap_err() .err, ParseErrors::VaryExpressionStackDepthTooDeep )); assert!(matches!( - ast::parser::v1::parse(&exceeds_stack_depth_list) + *ast::parser::v1::parse(&exceeds_stack_depth_list) .unwrap_err() .err, ParseErrors::VaryExpressionStackDepthTooDeep @@ -1242,7 +1242,7 @@ mod test { fn test_long_contract_name() { let long_contract_name = "(define-private (transfer (id uint) (receiver principal)) (contract-call? 'SP3D6PV2ACBPEKYJTCMH7HEN02KP87QSP8KTEH335.megapont-robot-expansion-nftSPNWZ5V2TPWGQGVDR6T7B6RQ4XMGZ4PXTEE0VQ0S.guests-hosted-stacks-parrots transfer id tx-sender receiver))"; assert!(matches!( - ast::parser::v1::parse(long_contract_name).unwrap_err().err, + *ast::parser::v1::parse(long_contract_name).unwrap_err().err, ParseErrors::SeparatorExpected(_) )); } diff --git a/clarity/src/vm/ast/parser/v2/lexer/error.rs b/clarity/src/vm/ast/parser/v2/lexer/error.rs index 11bfb0e4d05..b883533b94e 100644 --- a/clarity/src/vm/ast/parser/v2/lexer/error.rs +++ b/clarity/src/vm/ast/parser/v2/lexer/error.rs @@ -13,4 +13,4 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -pub use clarity_serialization::errors::lexer::{LexerError, PlacedError}; +pub use clarity_types::errors::lexer::{LexerError, PlacedError}; diff --git a/clarity/src/vm/ast/parser/v2/lexer/token.rs b/clarity/src/vm/ast/parser/v2/lexer/token.rs index ce4768798f7..57d40e65af0 100644 --- a/clarity/src/vm/ast/parser/v2/lexer/token.rs +++ b/clarity/src/vm/ast/parser/v2/lexer/token.rs @@ -1 +1 @@ -pub use clarity_serialization::token::{PlacedToken, Token}; +pub use clarity_types::token::{PlacedToken, Token}; diff --git a/clarity/src/vm/ast/parser/v2/mod.rs b/clarity/src/vm/ast/parser/v2/mod.rs index 03a62f9560c..61821d196c3 100644 --- a/clarity/src/vm/ast/parser/v2/mod.rs +++ b/clarity/src/vm/ast/parser/v2/mod.rs @@ -1,7 +1,7 @@ pub mod lexer; -use clarity_serialization::representations::{ClarityName, ContractName}; -use clarity_serialization::types::{ +use clarity_types::representations::{ClarityName, ContractName}; +use clarity_types::types::{ CharType, PrincipalData, QualifiedContractIdentifier, SequenceData, TraitIdentifier, UTF8Data, Value, }; @@ -3533,7 +3533,7 @@ mod tests { fn test_parse_fail_fast() { match parse("42g !ok") { Ok(_) => panic!("fail_fast mode should have returned an error"), - Err(e) => assert_eq!(e.err, ParseErrors::Lexer(LexerError::InvalidCharInt('g'))), + Err(e) => assert_eq!(*e.err, ParseErrors::Lexer(LexerError::InvalidCharInt('g'))), } } @@ -3560,7 +3560,7 @@ mod tests { ")".repeat(stack_limit + 1) ); - assert!(match parse(&exceeds_stack_depth_list).unwrap_err().err { + assert!(match *(parse(&exceeds_stack_depth_list).unwrap_err().err) { ParseErrors::ExpressionStackDepthTooDeep => true, x => panic!("expected a stack depth too deep error, got {x:?}"), }); @@ -3583,7 +3583,7 @@ mod tests { } ); - assert!(match parse(&exceeds_stack_depth_tuple).unwrap_err().err { + assert!(match *parse(&exceeds_stack_depth_tuple).unwrap_err().err { ParseErrors::ExpressionStackDepthTooDeep => true, x => panic!("expected a stack depth too deep error, got {x:?}"), }); diff --git a/clarity/src/vm/ast/sugar_expander/mod.rs b/clarity/src/vm/ast/sugar_expander/mod.rs index 6f9cac5bd52..b7b4965f50c 100644 --- a/clarity/src/vm/ast/sugar_expander/mod.rs +++ b/clarity/src/vm/ast/sugar_expander/mod.rs @@ -16,8 +16,8 @@ use std::collections::{HashMap, HashSet}; -use clarity_serialization::representations::ClarityName; -use clarity_serialization::types::{ +use clarity_types::representations::ClarityName; +use clarity_types::types::{ PrincipalData, QualifiedContractIdentifier, StandardPrincipalData, TraitIdentifier, Value, }; diff --git a/clarity/src/vm/ast/traits_resolver/mod.rs b/clarity/src/vm/ast/traits_resolver/mod.rs index 27e451b752a..90bf989ade5 100644 --- a/clarity/src/vm/ast/traits_resolver/mod.rs +++ b/clarity/src/vm/ast/traits_resolver/mod.rs @@ -16,8 +16,8 @@ use std::collections::HashMap; -use clarity_serialization::representations::ClarityName; -use clarity_serialization::types::{QualifiedContractIdentifier, TraitIdentifier}; +use clarity_types::representations::ClarityName; +use clarity_types::types::{QualifiedContractIdentifier, TraitIdentifier}; use crate::vm::ast::errors::{ParseError, ParseErrors, ParseResult}; use crate::vm::ast::types::{BuildASTPass, ContractAST}; diff --git a/clarity/src/vm/callables.rs b/clarity/src/vm/callables.rs index e7f9aa6a0ec..b697b3a0959 100644 --- a/clarity/src/vm/callables.rs +++ b/clarity/src/vm/callables.rs @@ -16,8 +16,8 @@ use std::collections::BTreeMap; -use clarity_serialization::representations::ClarityName; -pub use clarity_serialization::types::FunctionIdentifier; +use clarity_types::representations::ClarityName; +pub use clarity_types::types::FunctionIdentifier; use stacks_common::types::StacksEpochId; use super::costs::{CostErrors, CostOverflowingMath}; @@ -228,8 +228,8 @@ impl DefinedFunction { _ => { if !type_sig.admits(env.epoch(), value)? { return Err(CheckErrors::TypeValueError( - type_sig.clone(), - value.clone(), + Box::new(type_sig.clone()), + Box::new(value.clone()), ) .into()); } @@ -272,9 +272,11 @@ impl DefinedFunction { } _ => { if !type_sig.admits(env.epoch(), &cast_value)? { - return Err( - CheckErrors::TypeValueError(type_sig.clone(), cast_value).into() - ); + return Err(CheckErrors::TypeValueError( + Box::new(type_sig.clone()), + Box::new(cast_value), + ) + .into()); } } } @@ -447,9 +449,11 @@ fn clarity2_implicit_cast(type_sig: &TypeSignature, value: &Value) -> Result ty, None => { // This should be unreachable if the type-checker has already run successfully - return Err( - CheckErrors::TypeValueError(type_sig.clone(), value.clone()).into() - ); + return Err(CheckErrors::TypeValueError( + Box::new(type_sig.clone()), + Box::new(value.clone()), + ) + .into()); } }; cast_data_map.insert(name.clone(), clarity2_implicit_cast(to_type, field_value)?); diff --git a/clarity/src/vm/clarity.rs b/clarity/src/vm/clarity.rs index 0cec4993fa2..979d815268c 100644 --- a/clarity/src/vm/clarity.rs +++ b/clarity/src/vm/clarity.rs @@ -23,9 +23,9 @@ pub enum Error { AbortedByCallback { /// What the output value of the transaction would have been. /// This will be a Some for contract-calls, and None for contract initialization txs. - output: Option, + output: Option>, /// The asset map which was evaluated by the abort callback - assets_modified: AssetMap, + assets_modified: Box, /// The events from the transaction processing tx_events: Vec, /// A human-readable explanation for aborting the transaction @@ -65,7 +65,7 @@ impl std::error::Error for Error { impl From for Error { fn from(e: CheckError) -> Self { - match e.err { + match *e.err { CheckErrors::CostOverflow => { Error::CostError(ExecutionCost::max_value(), ExecutionCost::max_value()) } @@ -100,7 +100,7 @@ impl From for Error { impl From for Error { fn from(e: ParseError) -> Self { - match e.err { + match *e.err { ParseErrors::CostOverflow => { Error::CostError(ExecutionCost::max_value(), ExecutionCost::max_value()) } @@ -244,7 +244,7 @@ pub trait TransactionConnection: ClarityConnection { let cost_track = contract_analysis.take_contract_cost_tracker(); (cost_track, Ok((contract_ast, contract_analysis))) } - Err((e, cost_track)) => (cost_track, Err(e.into())), + Err(e) => (e.1, Err(e.0.into())), } }) } @@ -347,8 +347,8 @@ pub trait TransactionConnection: ClarityConnection { .and_then(|(value, assets_modified, tx_events, reason)| { if let Some(reason) = reason { Err(Error::AbortedByCallback { - output: Some(value), - assets_modified, + output: Some(Box::new(value)), + assets_modified: Box::new(assets_modified), tx_events, reason, }) @@ -399,7 +399,7 @@ pub trait TransactionConnection: ClarityConnection { if let Some(reason) = reason { Err(Error::AbortedByCallback { output: None, - assets_modified, + assets_modified: Box::new(assets_modified), tx_events, reason, }) diff --git a/clarity/src/vm/contexts.rs b/clarity/src/vm/contexts.rs index b316fcb1870..355a0e9d537 100644 --- a/clarity/src/vm/contexts.rs +++ b/clarity/src/vm/contexts.rs @@ -19,8 +19,8 @@ use std::fmt; use std::mem::replace; use std::time::{Duration, Instant}; -pub use clarity_serialization::errors::StackTrace; -use clarity_serialization::representations::ClarityName; +pub use clarity_types::errors::StackTrace; +use clarity_types::representations::ClarityName; use serde::Serialize; use serde_json::json; use stacks_common::types::chainstate::StacksBlockId; @@ -1134,7 +1134,11 @@ impl<'a, 'b, 'hooks> Environment<'a, 'b, 'hooks> { self.epoch(), &expected_type, value.clone(), - ).ok_or_else(|| CheckErrors::TypeValueError(expected_type, value.clone()))?; + ).ok_or_else(|| CheckErrors::TypeValueError( + Box::new(expected_type), + Box::new(value.clone()), + ) + )?; Ok(sanitized_value) }) @@ -1762,10 +1766,10 @@ impl<'a, 'hooks> GlobalContext<'a, 'hooks> { self.commit()?; Ok(result) } else { - Err( - CheckErrors::PublicFunctionMustReturnResponse(TypeSignature::type_of(&result)?) - .into(), - ) + Err(CheckErrors::PublicFunctionMustReturnResponse(Box::new( + TypeSignature::type_of(&result)?, + )) + .into()) } } else { self.roll_back()?; diff --git a/clarity/src/vm/costs/cost_functions.rs b/clarity/src/vm/costs/cost_functions.rs index bfcc62e2304..6abbaec5556 100644 --- a/clarity/src/vm/costs/cost_functions.rs +++ b/clarity/src/vm/costs/cost_functions.rs @@ -158,6 +158,7 @@ define_named_enum!(ClarityCostFunction { BitwiseLShift("cost_bitwise_left_shift"), BitwiseRShift("cost_bitwise_right_shift"), ContractHash("cost_contract_hash"), + ToAscii("cost_to_ascii"), Unimplemented("cost_unimplemented"), }); @@ -328,6 +329,7 @@ pub trait CostValues { fn cost_bitwise_left_shift(n: u64) -> InterpreterResult; fn cost_bitwise_right_shift(n: u64) -> InterpreterResult; fn cost_contract_hash(n: u64) -> InterpreterResult; + fn cost_to_ascii(n: u64) -> InterpreterResult; } impl ClarityCostFunction { @@ -481,6 +483,7 @@ impl ClarityCostFunction { ClarityCostFunction::BitwiseLShift => C::cost_bitwise_left_shift(n), ClarityCostFunction::BitwiseRShift => C::cost_bitwise_right_shift(n), ClarityCostFunction::ContractHash => C::cost_contract_hash(n), + ClarityCostFunction::ToAscii => C::cost_to_ascii(n), ClarityCostFunction::Unimplemented => Err(RuntimeErrorType::NotImplemented.into()), } } diff --git a/clarity/src/vm/costs/costs_1.rs b/clarity/src/vm/costs/costs_1.rs index 1e4bf02372f..1e400f56bd5 100644 --- a/clarity/src/vm/costs/costs_1.rs +++ b/clarity/src/vm/costs/costs_1.rs @@ -749,4 +749,8 @@ impl CostValues for Costs1 { fn cost_contract_hash(n: u64) -> InterpreterResult { Err(RuntimeErrorType::NotImplemented.into()) } + + fn cost_to_ascii(n: u64) -> InterpreterResult { + Err(RuntimeErrorType::NotImplemented.into()) + } } diff --git a/clarity/src/vm/costs/costs_2.rs b/clarity/src/vm/costs/costs_2.rs index b8e1290c963..451008bd1b9 100644 --- a/clarity/src/vm/costs/costs_2.rs +++ b/clarity/src/vm/costs/costs_2.rs @@ -749,4 +749,8 @@ impl CostValues for Costs2 { fn cost_contract_hash(n: u64) -> InterpreterResult { Err(RuntimeErrorType::NotImplemented.into()) } + + fn cost_to_ascii(n: u64) -> InterpreterResult { + Err(RuntimeErrorType::NotImplemented.into()) + } } diff --git a/clarity/src/vm/costs/costs_2_testnet.rs b/clarity/src/vm/costs/costs_2_testnet.rs index e35f38e0374..647bafedb96 100644 --- a/clarity/src/vm/costs/costs_2_testnet.rs +++ b/clarity/src/vm/costs/costs_2_testnet.rs @@ -749,4 +749,8 @@ impl CostValues for Costs2Testnet { fn cost_contract_hash(n: u64) -> InterpreterResult { Err(RuntimeErrorType::NotImplemented.into()) } + + fn cost_to_ascii(n: u64) -> InterpreterResult { + Err(RuntimeErrorType::NotImplemented.into()) + } } diff --git a/clarity/src/vm/costs/costs_3.rs b/clarity/src/vm/costs/costs_3.rs index b8c1838a753..b195303510c 100644 --- a/clarity/src/vm/costs/costs_3.rs +++ b/clarity/src/vm/costs/costs_3.rs @@ -767,4 +767,8 @@ impl CostValues for Costs3 { fn cost_contract_hash(n: u64) -> InterpreterResult { Err(RuntimeErrorType::NotImplemented.into()) } + + fn cost_to_ascii(n: u64) -> InterpreterResult { + Err(RuntimeErrorType::NotImplemented.into()) + } } diff --git a/clarity/src/vm/costs/costs_4.rs b/clarity/src/vm/costs/costs_4.rs index 6a3b5c6eb13..d1c92732d98 100644 --- a/clarity/src/vm/costs/costs_4.rs +++ b/clarity/src/vm/costs/costs_4.rs @@ -21,6 +21,7 @@ use super::cost_functions::CostValues; use super::costs_3::Costs3; use super::ExecutionCost; +use crate::vm::costs::cost_functions::linear; use crate::vm::errors::InterpreterResult; pub struct Costs4; @@ -455,4 +456,9 @@ impl CostValues for Costs4 { read_length: 32, }) } + + fn cost_to_ascii(n: u64) -> InterpreterResult { + // TODO: needs criterion benchmark + Ok(ExecutionCost::runtime(linear(n, 1, 100))) + } } diff --git a/clarity/src/vm/costs/mod.rs b/clarity/src/vm/costs/mod.rs index cb3ecd27f9f..669f6f483dc 100644 --- a/clarity/src/vm/costs/mod.rs +++ b/clarity/src/vm/costs/mod.rs @@ -17,8 +17,8 @@ use std::collections::HashMap; use std::{cmp, fmt}; -pub use clarity_serialization::errors::CostErrors; -pub use clarity_serialization::execution_cost::ExecutionCost; +pub use clarity_types::errors::CostErrors; +pub use clarity_types::execution_cost::{CostOverflowingMath, ExecutionCost}; use costs_1::Costs1; use costs_2::Costs2; use costs_2_testnet::Costs2Testnet; @@ -55,8 +55,6 @@ pub mod costs_3; #[allow(unused_variables)] pub mod costs_4; -type Result = std::result::Result; - pub const CLARITY_MEMORY_LIMIT: u64 = 100 * 1000 * 1000; // TODO: factor out into a boot lib? @@ -85,7 +83,7 @@ pub fn runtime_cost, C: CostTracker>( cost_function: ClarityCostFunction, tracker: &mut C, input: T, -) -> Result<()> { +) -> Result<(), CostErrors> { let size: u64 = input.try_into().map_err(|_| CostErrors::CostOverflow)?; let cost = tracker.compute_cost(cost_function, &[size])?; @@ -104,7 +102,7 @@ pub fn analysis_typecheck_cost( track: &mut T, t1: &TypeSignature, t2: &TypeSignature, -) -> Result<()> { +) -> Result<(), CostErrors> { let t1_size = t1.type_size().map_err(|_| CostErrors::CostOverflow)?; let t2_size = t2.type_size().map_err(|_| CostErrors::CostOverflow)?; let cost = track.compute_cost( @@ -115,11 +113,11 @@ pub fn analysis_typecheck_cost( } pub trait MemoryConsumer { - fn get_memory_use(&self) -> Result; + fn get_memory_use(&self) -> Result; } impl MemoryConsumer for Value { - fn get_memory_use(&self) -> Result { + fn get_memory_use(&self) -> Result { Ok(self .size() .map_err(|_| CostErrors::InterpreterFailure)? @@ -132,10 +130,10 @@ pub trait CostTracker { &mut self, cost_function: ClarityCostFunction, input: &[u64], - ) -> Result; - fn add_cost(&mut self, cost: ExecutionCost) -> Result<()>; - fn add_memory(&mut self, memory: u64) -> Result<()>; - fn drop_memory(&mut self, memory: u64) -> Result<()>; + ) -> Result; + fn add_cost(&mut self, cost: ExecutionCost) -> Result<(), CostErrors>; + fn add_memory(&mut self, memory: u64) -> Result<(), CostErrors>; + fn drop_memory(&mut self, memory: u64) -> Result<(), CostErrors>; fn reset_memory(&mut self); /// Check if the given contract-call should be short-circuited. /// If so: this charges the cost to the CostTracker, and return true @@ -145,7 +143,7 @@ pub trait CostTracker { contract: &QualifiedContractIdentifier, function: &ClarityName, input: &[u64], - ) -> Result; + ) -> Result; } // Don't track! @@ -154,16 +152,16 @@ impl CostTracker for () { &mut self, _cost_function: ClarityCostFunction, _input: &[u64], - ) -> std::result::Result { + ) -> Result { Ok(ExecutionCost::ZERO) } - fn add_cost(&mut self, _cost: ExecutionCost) -> std::result::Result<(), CostErrors> { + fn add_cost(&mut self, _cost: ExecutionCost) -> Result<(), CostErrors> { Ok(()) } - fn add_memory(&mut self, _memory: u64) -> std::result::Result<(), CostErrors> { + fn add_memory(&mut self, _memory: u64) -> Result<(), CostErrors> { Ok(()) } - fn drop_memory(&mut self, _memory: u64) -> Result<()> { + fn drop_memory(&mut self, _memory: u64) -> Result<(), CostErrors> { Ok(()) } fn reset_memory(&mut self) {} @@ -172,7 +170,7 @@ impl CostTracker for () { _contract: &QualifiedContractIdentifier, _function: &ClarityName, _input: &[u64], - ) -> Result { + ) -> Result { Ok(false) } } @@ -204,7 +202,7 @@ impl DefaultVersion { cost_function_ref: &ClarityCostFunctionReference, f: &ClarityCostFunction, input: &[u64], - ) -> Result { + ) -> Result { let n = input.first().ok_or_else(|| { CostErrors::Expect("Default cost function supplied with 0 args".into()) })?; @@ -231,10 +229,7 @@ impl DefaultVersion { } impl DefaultVersion { - pub fn try_from( - mainnet: bool, - value: &QualifiedContractIdentifier, - ) -> std::result::Result { + pub fn try_from(mainnet: bool, value: &QualifiedContractIdentifier) -> Result { if !value.is_boot() { return Err("Not a boot contract".into()); } @@ -414,7 +409,10 @@ impl PartialEq for LimitedCostTracker { } } -fn load_state_summary(mainnet: bool, clarity_db: &mut ClarityDatabase) -> Result { +fn load_state_summary( + mainnet: bool, + clarity_db: &mut ClarityDatabase, +) -> Result { let cost_voting_contract = boot_code_id("cost-voting", mainnet); let clarity_epoch = clarity_db @@ -455,7 +453,7 @@ fn store_state_summary( mainnet: bool, clarity_db: &mut ClarityDatabase, to_store: &CostStateSummary, -) -> Result<()> { +) -> Result<(), CostErrors> { let block_height = clarity_db.get_current_block_height(); let cost_voting_contract = boot_code_id("cost-voting", mainnet); let epoch = clarity_db @@ -496,7 +494,7 @@ fn load_cost_functions( mainnet: bool, clarity_db: &mut ClarityDatabase, apply_updates: bool, -) -> Result { +) -> Result { let clarity_epoch = clarity_db .get_clarity_epoch_version() .map_err(|e| CostErrors::CostComputationFailed(e.to_string()))?; @@ -772,7 +770,7 @@ impl LimitedCostTracker { limit: ExecutionCost, clarity_db: &mut ClarityDatabase, epoch: StacksEpochId, - ) -> Result { + ) -> Result { let mut cost_tracker = TrackerData { cost_function_references: HashMap::new(), cost_contracts: HashMap::new(), @@ -796,7 +794,7 @@ impl LimitedCostTracker { limit: ExecutionCost, clarity_db: &mut ClarityDatabase, epoch: StacksEpochId, - ) -> Result { + ) -> Result { let mut cost_tracker = TrackerData { cost_function_references: HashMap::new(), cost_contracts: HashMap::new(), @@ -818,7 +816,7 @@ impl LimitedCostTracker { clarity_db: &mut ClarityDatabase, epoch: StacksEpochId, use_mainnet: bool, - ) -> Result { + ) -> Result { use crate::vm::tests::test_only_mainnet_to_chain_id; let chain_id = test_only_mainnet_to_chain_id(use_mainnet); assert!(clarity_db.is_stack_empty()); @@ -835,7 +833,7 @@ impl LimitedCostTracker { Self::Free } - pub fn default_cost_contract_for_epoch(epoch_id: StacksEpochId) -> Result { + pub fn default_cost_contract_for_epoch(epoch_id: StacksEpochId) -> Result { let result = match epoch_id { StacksEpochId::Epoch10 => { return Err(CostErrors::Expect("Attempted to get default cost functions for Epoch 1.0 where Clarity does not exist".into())); @@ -862,7 +860,11 @@ impl TrackerData { /// `apply_updates` - tells this function to look for any changes in the cost voting contract /// which would need to be applied. if `false`, just load the last computed cost state in this /// fork. - fn load_costs(&mut self, clarity_db: &mut ClarityDatabase, apply_updates: bool) -> Result<()> { + fn load_costs( + &mut self, + clarity_db: &mut ClarityDatabase, + apply_updates: bool, + ) -> Result<(), CostErrors> { clarity_db.begin(); let epoch_id = clarity_db .get_clarity_epoch_version() @@ -1003,7 +1005,7 @@ impl LimitedCostTracker { pub fn parse_cost( cost_function_name: &str, eval_result: InterpreterResult>, -) -> Result { +) -> Result { match eval_result { Ok(Some(Value::Tuple(data))) => { let results = ( @@ -1052,7 +1054,7 @@ pub fn compute_cost( cost_function_reference: ClarityCostFunctionReference, input_sizes: &[u64], eval_in_epoch: StacksEpochId, -) -> Result { +) -> Result { let mainnet = cost_tracker.mainnet; let chain_id = cost_tracker.chain_id; let mut null_store = NullBackingStore::new(); @@ -1103,7 +1105,7 @@ pub fn compute_cost( parse_cost(&cost_function_reference.to_string(), eval_result) } -fn add_cost(s: &mut TrackerData, cost: ExecutionCost) -> std::result::Result<(), CostErrors> { +fn add_cost(s: &mut TrackerData, cost: ExecutionCost) -> Result<(), CostErrors> { s.total.add(&cost)?; if cfg!(feature = "disable-costs") { // Disable check for exceeding the cost limit to allow mining large blocks for profiling purposes. @@ -1119,7 +1121,7 @@ fn add_cost(s: &mut TrackerData, cost: ExecutionCost) -> std::result::Result<(), } } -fn add_memory(s: &mut TrackerData, memory: u64) -> std::result::Result<(), CostErrors> { +fn add_memory(s: &mut TrackerData, memory: u64) -> Result<(), CostErrors> { s.memory = s.memory.cost_overflow_add(memory)?; if s.memory > s.memory_limit { Err(CostErrors::MemoryBalanceExceeded(s.memory, s.memory_limit)) @@ -1128,7 +1130,7 @@ fn add_memory(s: &mut TrackerData, memory: u64) -> std::result::Result<(), CostE } } -fn drop_memory(s: &mut TrackerData, memory: u64) -> Result<()> { +fn drop_memory(s: &mut TrackerData, memory: u64) -> Result<(), CostErrors> { s.memory = s .memory .checked_sub(memory) @@ -1141,7 +1143,7 @@ impl CostTracker for LimitedCostTracker { &mut self, cost_function: ClarityCostFunction, input: &[u64], - ) -> std::result::Result { + ) -> Result { match self { Self::Free => { // tracker is free, return zero! @@ -1172,19 +1174,19 @@ impl CostTracker for LimitedCostTracker { } } } - fn add_cost(&mut self, cost: ExecutionCost) -> std::result::Result<(), CostErrors> { + fn add_cost(&mut self, cost: ExecutionCost) -> Result<(), CostErrors> { match self { Self::Free => Ok(()), Self::Limited(ref mut data) => add_cost(data, cost), } } - fn add_memory(&mut self, memory: u64) -> std::result::Result<(), CostErrors> { + fn add_memory(&mut self, memory: u64) -> Result<(), CostErrors> { match self { Self::Free => Ok(()), Self::Limited(ref mut data) => add_memory(data, memory), } } - fn drop_memory(&mut self, memory: u64) -> Result<()> { + fn drop_memory(&mut self, memory: u64) -> Result<(), CostErrors> { match self { Self::Free => Ok(()), Self::Limited(ref mut data) => drop_memory(data, memory), @@ -1203,7 +1205,7 @@ impl CostTracker for LimitedCostTracker { contract: &QualifiedContractIdentifier, function: &ClarityName, input: &[u64], - ) -> Result { + ) -> Result { match self { Self::Free => { // if we're already free, no need to worry about short circuiting contract-calls @@ -1228,16 +1230,16 @@ impl CostTracker for &mut LimitedCostTracker { &mut self, cost_function: ClarityCostFunction, input: &[u64], - ) -> std::result::Result { + ) -> Result { LimitedCostTracker::compute_cost(self, cost_function, input) } - fn add_cost(&mut self, cost: ExecutionCost) -> std::result::Result<(), CostErrors> { + fn add_cost(&mut self, cost: ExecutionCost) -> Result<(), CostErrors> { LimitedCostTracker::add_cost(self, cost) } - fn add_memory(&mut self, memory: u64) -> std::result::Result<(), CostErrors> { + fn add_memory(&mut self, memory: u64) -> Result<(), CostErrors> { LimitedCostTracker::add_memory(self, memory) } - fn drop_memory(&mut self, memory: u64) -> std::result::Result<(), CostErrors> { + fn drop_memory(&mut self, memory: u64) -> Result<(), CostErrors> { LimitedCostTracker::drop_memory(self, memory) } fn reset_memory(&mut self) { @@ -1248,33 +1250,11 @@ impl CostTracker for &mut LimitedCostTracker { contract: &QualifiedContractIdentifier, function: &ClarityName, input: &[u64], - ) -> Result { + ) -> Result { LimitedCostTracker::short_circuit_contract_call(self, contract, function, input) } } -pub trait CostOverflowingMath { - fn cost_overflow_mul(self, other: T) -> Result; - fn cost_overflow_add(self, other: T) -> Result; - fn cost_overflow_sub(self, other: T) -> Result; - fn cost_overflow_div(self, other: T) -> Result; -} - -impl CostOverflowingMath for u64 { - fn cost_overflow_mul(self, other: u64) -> Result { - self.checked_mul(other).ok_or(CostErrors::CostOverflow) - } - fn cost_overflow_add(self, other: u64) -> Result { - self.checked_add(other).ok_or(CostErrors::CostOverflow) - } - fn cost_overflow_sub(self, other: u64) -> Result { - self.checked_sub(other).ok_or(CostErrors::CostOverflow) - } - fn cost_overflow_div(self, other: u64) -> Result { - self.checked_div(other).ok_or(CostErrors::CostOverflow) - } -} - // ONLY WORKS IF INPUT IS u64 fn int_log2(input: u64) -> Option { 63_u32.checked_sub(input.leading_zeros()).map(|floor_log| { diff --git a/clarity/src/vm/database/clarity_db.rs b/clarity/src/vm/database/clarity_db.rs index d17180ded7a..a895b00ddb3 100644 --- a/clarity/src/vm/database/clarity_db.rs +++ b/clarity/src/vm/database/clarity_db.rs @@ -49,6 +49,7 @@ use crate::vm::types::{ pub const STORE_CONTRACT_SRC_INTERFACE: bool = true; const TENURE_HEIGHT_KEY: &str = "_stx-data::tenure_height"; +const CLARITY_STORAGE_BLOCK_TIME_KEY: &str = "_stx-data::clarity_storage::block_time"; pub type StacksEpoch = GenericStacksEpoch; @@ -885,6 +886,28 @@ impl<'a> ClarityDatabase<'a> { self.put_data(Self::clarity_state_epoch_key(), &(epoch as u32)) } + /// Setup block metadata at the beginning of a block + /// This stores block-specific data that can be accessed during Clarity execution + pub fn setup_block_metadata(&mut self, block_time: Option) -> Result<()> { + let epoch = self.get_clarity_epoch_version()?; + if epoch.uses_marfed_block_time() { + let block_time = block_time.ok_or_else(|| { + InterpreterError::Expect( + "FATAL: Marfed block time not provided to Clarity DB setup".into(), + ) + })?; + self.put_data(CLARITY_STORAGE_BLOCK_TIME_KEY, &block_time)?; + } + Ok(()) + } + + pub fn get_current_block_time(&mut self) -> Result { + match self.get_data(CLARITY_STORAGE_BLOCK_TIME_KEY)? { + Some(value) => Ok(value), + None => Err(RuntimeErrorType::BlockTimeNotAvailable.into()), + } + } + /// Returns the _current_ total liquid ustx pub fn get_total_liquid_ustx(&mut self) -> Result { let epoch = self.get_clarity_epoch_version()?; @@ -1524,9 +1547,11 @@ impl ClarityDatabase<'_> { .value_type .admits(&self.get_clarity_epoch_version()?, &value)? { - return Err( - CheckErrors::TypeValueError(variable_descriptor.value_type.clone(), value).into(), - ); + return Err(CheckErrors::TypeValueError( + Box::new(variable_descriptor.value_type.clone()), + Box::new(value), + ) + .into()); } let key = ClarityDatabase::make_key_for_trip( @@ -1682,8 +1707,8 @@ impl ClarityDatabase<'_> { .admits(&self.get_clarity_epoch_version()?, key_value)? { return Err(CheckErrors::TypeValueError( - map_descriptor.key_type.clone(), - (*key_value).clone(), + Box::new(map_descriptor.key_type.clone()), + Box::new(key_value.clone()), ) .into()); } @@ -1713,8 +1738,8 @@ impl ClarityDatabase<'_> { .admits(&self.get_clarity_epoch_version()?, key_value)? { return Err(CheckErrors::TypeValueError( - map_descriptor.key_type.clone(), - (*key_value).clone(), + Box::new(map_descriptor.key_type.clone()), + Box::new(key_value.clone()), ) .into()); } @@ -1855,17 +1880,21 @@ impl ClarityDatabase<'_> { .key_type .admits(&self.get_clarity_epoch_version()?, &key_value)? { - return Err( - CheckErrors::TypeValueError(map_descriptor.key_type.clone(), key_value).into(), - ); + return Err(CheckErrors::TypeValueError( + Box::new(map_descriptor.key_type.clone()), + Box::new(key_value), + ) + .into()); } if !map_descriptor .value_type .admits(&self.get_clarity_epoch_version()?, &value)? { - return Err( - CheckErrors::TypeValueError(map_descriptor.value_type.clone(), value).into(), - ); + return Err(CheckErrors::TypeValueError( + Box::new(map_descriptor.value_type.clone()), + Box::new(value), + ) + .into()); } let key_serialized = key_value.serialize_to_hex()?; @@ -1911,8 +1940,8 @@ impl ClarityDatabase<'_> { .admits(&self.get_clarity_epoch_version()?, key_value)? { return Err(CheckErrors::TypeValueError( - map_descriptor.key_type.clone(), - (*key_value).clone(), + Box::new(map_descriptor.key_type.clone()), + Box::new(key_value.clone()), ) .into()); } @@ -2128,7 +2157,11 @@ impl ClarityDatabase<'_> { key_type: &TypeSignature, ) -> Result { if !key_type.admits(&self.get_clarity_epoch_version()?, asset)? { - return Err(CheckErrors::TypeValueError(key_type.clone(), (*asset).clone()).into()); + return Err(CheckErrors::TypeValueError( + Box::new(key_type.clone()), + Box::new(asset.clone()), + ) + .into()); } let key = ClarityDatabase::make_key_for_quad( @@ -2177,7 +2210,11 @@ impl ClarityDatabase<'_> { epoch: &StacksEpochId, ) -> Result<()> { if !key_type.admits(&self.get_clarity_epoch_version()?, asset)? { - return Err(CheckErrors::TypeValueError(key_type.clone(), (*asset).clone()).into()); + return Err(CheckErrors::TypeValueError( + Box::new(key_type.clone()), + Box::new(asset.clone()), + ) + .into()); } let key = ClarityDatabase::make_key_for_quad( @@ -2202,7 +2239,11 @@ impl ClarityDatabase<'_> { epoch: &StacksEpochId, ) -> Result<()> { if !key_type.admits(&self.get_clarity_epoch_version()?, asset)? { - return Err(CheckErrors::TypeValueError(key_type.clone(), (*asset).clone()).into()); + return Err(CheckErrors::TypeValueError( + Box::new(key_type.clone()), + Box::new(asset.clone()), + ) + .into()); } let key = ClarityDatabase::make_key_for_quad( diff --git a/clarity/src/vm/database/structures.rs b/clarity/src/vm/database/structures.rs index 20f1aa0d04b..c4fec9f382e 100644 --- a/clarity/src/vm/database/structures.rs +++ b/clarity/src/vm/database/structures.rs @@ -30,7 +30,7 @@ pub trait ClaritySerializable { } pub trait ClarityDeserializable { - fn deserialize(json: &str) -> Result; + fn deserialize(json: &str) -> Result; } impl ClaritySerializable for String { @@ -40,7 +40,7 @@ impl ClaritySerializable for String { } impl ClarityDeserializable for String { - fn deserialize(serialized: &str) -> Result { + fn deserialize(serialized: &str) -> Result { Ok(serialized.into()) } } @@ -54,7 +54,7 @@ macro_rules! clarity_serializable { } impl ClarityDeserializable<$Name> for $Name { #[cfg(not(target_family = "wasm"))] - fn deserialize(json: &str) -> Result { + fn deserialize(json: &str) -> Result { let mut deserializer = serde_json::Deserializer::from_str(&json); // serde's default 128 depth limit can be exhausted // by a 64-stack-depth AST, so disable the recursion limit @@ -67,7 +67,7 @@ macro_rules! clarity_serializable { }) } #[cfg(target_family = "wasm")] - fn deserialize(json: &str) -> Result { + fn deserialize(json: &str) -> Result { serde_json::from_str(json).map_err(|_| { InterpreterError::Expect("Failed to deserialize vm.Value".into()).into() }) @@ -166,8 +166,6 @@ pub struct STXBalanceSnapshot<'db, 'conn> { db_ref: &'conn mut ClarityDatabase<'db>, } -type Result = std::result::Result; - impl ClaritySerializable for STXBalance { #[allow(clippy::expect_used)] fn serialize(&self) -> String { @@ -259,7 +257,7 @@ impl ClaritySerializable for STXBalance { } impl ClarityDeserializable for STXBalance { - fn deserialize(input: &str) -> Result { + fn deserialize(input: &str) -> Result { let bytes = hex_bytes(input).map_err(|_| { InterpreterError::Expect("STXBalance deserialization: failed decoding bytes.".into()) })?; @@ -376,12 +374,12 @@ impl<'db, 'conn> STXBalanceSnapshot<'db, 'conn> { &self.balance } - pub fn save(self) -> Result<()> { + pub fn save(self) -> Result<(), Error> { let key = ClarityDatabase::make_key_for_account_balance(&self.principal); self.db_ref.put_data(&key, &self.balance) } - pub fn transfer_to(mut self, recipient: &PrincipalData, amount: u128) -> Result<()> { + pub fn transfer_to(mut self, recipient: &PrincipalData, amount: u128) -> Result<(), Error> { if !self.can_transfer(amount)? { return Err(InterpreterError::InsufficientBalance.into()); } @@ -402,7 +400,7 @@ impl<'db, 'conn> STXBalanceSnapshot<'db, 'conn> { Ok(()) } - pub fn get_available_balance(&mut self) -> Result { + pub fn get_available_balance(&mut self) -> Result { let v1_unlock_height = self.db_ref.get_v1_unlock_height(); let v2_unlock_height = self.db_ref.get_v2_unlock_height()?; let v3_unlock_height = self.db_ref.get_v3_unlock_height()?; @@ -414,7 +412,7 @@ impl<'db, 'conn> STXBalanceSnapshot<'db, 'conn> { ) } - pub fn canonical_balance_repr(&mut self) -> Result { + pub fn canonical_balance_repr(&mut self) -> Result { let v1_unlock_height = self.db_ref.get_v1_unlock_height(); let v2_unlock_height = self.db_ref.get_v2_unlock_height()?; let v3_unlock_height = self.db_ref.get_v3_unlock_height()?; @@ -429,7 +427,7 @@ impl<'db, 'conn> STXBalanceSnapshot<'db, 'conn> { .0) } - pub fn has_locked_tokens(&mut self) -> Result { + pub fn has_locked_tokens(&mut self) -> Result { let v1_unlock_height = self.db_ref.get_v1_unlock_height(); let v2_unlock_height = self.db_ref.get_v2_unlock_height()?; let v3_unlock_height = self.db_ref.get_v3_unlock_height()?; @@ -441,7 +439,7 @@ impl<'db, 'conn> STXBalanceSnapshot<'db, 'conn> { )) } - pub fn has_unlockable_tokens(&mut self) -> Result { + pub fn has_unlockable_tokens(&mut self) -> Result { let v1_unlock_height = self.db_ref.get_v1_unlock_height(); let v2_unlock_height = self.db_ref.get_v2_unlock_height()?; let v3_unlock_height = self.db_ref.get_v3_unlock_height()?; @@ -453,11 +451,11 @@ impl<'db, 'conn> STXBalanceSnapshot<'db, 'conn> { )) } - pub fn can_transfer(&mut self, amount: u128) -> Result { + pub fn can_transfer(&mut self, amount: u128) -> Result { Ok(self.get_available_balance()? >= amount) } - pub fn debit(&mut self, amount: u128) -> Result<()> { + pub fn debit(&mut self, amount: u128) -> Result<(), Error> { let unlocked = self.unlock_available_tokens_if_any()?; if unlocked > 0 { debug!("Consolidated after account-debit"); @@ -466,7 +464,7 @@ impl<'db, 'conn> STXBalanceSnapshot<'db, 'conn> { self.balance.debit_unlocked_amount(amount) } - pub fn credit(&mut self, amount: u128) -> Result<()> { + pub fn credit(&mut self, amount: u128) -> Result<(), Error> { let unlocked = self.unlock_available_tokens_if_any()?; if unlocked > 0 { debug!("Consolidated after account-credit"); @@ -482,7 +480,11 @@ impl<'db, 'conn> STXBalanceSnapshot<'db, 'conn> { self.balance = balance; } - pub fn lock_tokens_v1(&mut self, amount_to_lock: u128, unlock_burn_height: u64) -> Result<()> { + pub fn lock_tokens_v1( + &mut self, + amount_to_lock: u128, + unlock_burn_height: u64, + ) -> Result<(), Error> { let unlocked = self.unlock_available_tokens_if_any()?; if unlocked > 0 { debug!("Consolidated after account-token-lock"); @@ -528,7 +530,7 @@ impl<'db, 'conn> STXBalanceSnapshot<'db, 'conn> { /// Return true iff `self` represents a snapshot that has a lock /// created by PoX v2. - pub fn is_v2_locked(&mut self) -> Result { + pub fn is_v2_locked(&mut self) -> Result { match self.canonical_balance_repr()? { STXBalance::LockedPoxTwo { .. } => Ok(true), _ => Ok(false), @@ -537,7 +539,7 @@ impl<'db, 'conn> STXBalanceSnapshot<'db, 'conn> { /// Increase the account's current lock to `new_total_locked`. /// Panics if `self` was not locked by V2 PoX. - pub fn increase_lock_v2(&mut self, new_total_locked: u128) -> Result<()> { + pub fn increase_lock_v2(&mut self, new_total_locked: u128) -> Result<(), Error> { let unlocked = self.unlock_available_tokens_if_any()?; if unlocked > 0 { debug!("Consolidated after extend-token-lock"); @@ -586,7 +588,7 @@ impl<'db, 'conn> STXBalanceSnapshot<'db, 'conn> { /// Extend this account's current lock to `unlock_burn_height`. /// After calling, this method will set the balance to a "LockedPoxTwo" balance, /// because this method is only invoked as a result of PoX2 interactions - pub fn extend_lock_v2(&mut self, unlock_burn_height: u64) -> Result<()> { + pub fn extend_lock_v2(&mut self, unlock_burn_height: u64) -> Result<(), Error> { let unlocked = self.unlock_available_tokens_if_any()?; if unlocked > 0 { debug!("Consolidated after extend-token-lock"); @@ -619,7 +621,11 @@ impl<'db, 'conn> STXBalanceSnapshot<'db, 'conn> { /// Lock `amount_to_lock` tokens on this account until `unlock_burn_height`. /// After calling, this method will set the balance to a "LockedPoxTwo" balance, /// because this method is only invoked as a result of PoX2 interactions - pub fn lock_tokens_v2(&mut self, amount_to_lock: u128, unlock_burn_height: u64) -> Result<()> { + pub fn lock_tokens_v2( + &mut self, + amount_to_lock: u128, + unlock_burn_height: u64, + ) -> Result<(), Error> { let unlocked = self.unlock_available_tokens_if_any()?; if unlocked > 0 { debug!("Consolidated after account-token-lock"); @@ -668,7 +674,11 @@ impl<'db, 'conn> STXBalanceSnapshot<'db, 'conn> { /// Lock `amount_to_lock` tokens on this account until `unlock_burn_height`. /// After calling, this method will set the balance to a "LockedPoxThree" balance, /// because this method is only invoked as a result of PoX3 interactions - pub fn lock_tokens_v3(&mut self, amount_to_lock: u128, unlock_burn_height: u64) -> Result<()> { + pub fn lock_tokens_v3( + &mut self, + amount_to_lock: u128, + unlock_burn_height: u64, + ) -> Result<(), Error> { let unlocked = self.unlock_available_tokens_if_any()?; if unlocked > 0 { debug!("Consolidated after account-token-lock"); @@ -718,7 +728,7 @@ impl<'db, 'conn> STXBalanceSnapshot<'db, 'conn> { /// Extend this account's current lock to `unlock_burn_height`. /// After calling, this method will set the balance to a "LockedPoxThree" balance, /// because this method is only invoked as a result of PoX3 interactions - pub fn extend_lock_v3(&mut self, unlock_burn_height: u64) -> Result<()> { + pub fn extend_lock_v3(&mut self, unlock_burn_height: u64) -> Result<(), Error> { let unlocked = self.unlock_available_tokens_if_any()?; if unlocked > 0 { debug!("Consolidated after extend-token-lock"); @@ -750,7 +760,7 @@ impl<'db, 'conn> STXBalanceSnapshot<'db, 'conn> { /// Increase the account's current lock to `new_total_locked`. /// Panics if `self` was not locked by V3 PoX. - pub fn increase_lock_v3(&mut self, new_total_locked: u128) -> Result<()> { + pub fn increase_lock_v3(&mut self, new_total_locked: u128) -> Result<(), Error> { let unlocked = self.unlock_available_tokens_if_any()?; if unlocked > 0 { debug!("Consolidated after extend-token-lock"); @@ -795,7 +805,7 @@ impl<'db, 'conn> STXBalanceSnapshot<'db, 'conn> { /// Return true iff `self` represents a snapshot that has a lock /// created by PoX v3. - pub fn is_v3_locked(&mut self) -> Result { + pub fn is_v3_locked(&mut self) -> Result { match self.canonical_balance_repr()? { STXBalance::LockedPoxThree { .. } => Ok(true), _ => Ok(false), @@ -807,7 +817,11 @@ impl<'db, 'conn> STXBalanceSnapshot<'db, 'conn> { /// Lock `amount_to_lock` tokens on this account until `unlock_burn_height`. /// After calling, this method will set the balance to a "LockedPoxFour" balance, /// because this method is only invoked as a result of PoX4 interactions - pub fn lock_tokens_v4(&mut self, amount_to_lock: u128, unlock_burn_height: u64) -> Result<()> { + pub fn lock_tokens_v4( + &mut self, + amount_to_lock: u128, + unlock_burn_height: u64, + ) -> Result<(), Error> { let unlocked = self.unlock_available_tokens_if_any()?; if unlocked > 0 { debug!("Consolidated after account-token-lock"); @@ -846,7 +860,7 @@ impl<'db, 'conn> STXBalanceSnapshot<'db, 'conn> { /// Extend this account's current lock to `unlock_burn_height`. /// After calling, this method will set the balance to a "LockedPoxFour" balance, /// because this method is only invoked as a result of PoX3 interactions - pub fn extend_lock_v4(&mut self, unlock_burn_height: u64) -> Result<()> { + pub fn extend_lock_v4(&mut self, unlock_burn_height: u64) -> Result<(), Error> { let unlocked = self.unlock_available_tokens_if_any()?; if unlocked > 0 { debug!("Consolidated after extend-token-lock"); @@ -872,7 +886,7 @@ impl<'db, 'conn> STXBalanceSnapshot<'db, 'conn> { /// Increase the account's current lock to `new_total_locked`. /// Panics if `self` was not locked by V3 PoX. - pub fn increase_lock_v4(&mut self, new_total_locked: u128) -> Result<()> { + pub fn increase_lock_v4(&mut self, new_total_locked: u128) -> Result<(), Error> { let unlocked = self.unlock_available_tokens_if_any()?; if unlocked > 0 { debug!("Consolidated after extend-token-lock"); @@ -912,7 +926,7 @@ impl<'db, 'conn> STXBalanceSnapshot<'db, 'conn> { /// Return true iff `self` represents a snapshot that has a lock /// created by PoX v3. - pub fn is_v4_locked(&mut self) -> Result { + pub fn is_v4_locked(&mut self) -> Result { match self.canonical_balance_repr()? { STXBalance::LockedPoxFour { .. } => Ok(true), _ => Ok(false), @@ -923,7 +937,7 @@ impl<'db, 'conn> STXBalanceSnapshot<'db, 'conn> { /// If this snapshot is locked, then alter the lock height to be /// the next burn block (i.e., `self.burn_block_height + 1`) - pub fn accelerate_unlock(&mut self) -> Result<()> { + pub fn accelerate_unlock(&mut self) -> Result<(), Error> { let unlocked = self.unlock_available_tokens_if_any()?; if unlocked > 0 { debug!("Consolidated after account-token-lock"); @@ -971,7 +985,7 @@ impl<'db, 'conn> STXBalanceSnapshot<'db, 'conn> { /// Unlock any tokens that are unlockable at the current /// burn block height, and return the amount newly unlocked - fn unlock_available_tokens_if_any(&mut self) -> Result { + fn unlock_available_tokens_if_any(&mut self) -> Result { let (new_balance, unlocked) = self.balance.canonical_repr_at_block( self.burn_block_height, self.db_ref.get_v1_unlock_height(), @@ -1088,7 +1102,7 @@ impl STXBalance { } } - fn debit_unlocked_amount(&mut self, delta: u128) -> Result<()> { + fn debit_unlocked_amount(&mut self, delta: u128) -> Result<(), Error> { match self { STXBalance::Unlocked { amount: amount_unlocked, @@ -1150,7 +1164,7 @@ impl STXBalance { v1_unlock_height: u32, v2_unlock_height: u32, v3_unlock_height: u32, - ) -> Result<(STXBalance, u128)> { + ) -> Result<(STXBalance, u128), Error> { if self.has_unlockable_tokens_at_burn_block( burn_block_height, v1_unlock_height, @@ -1174,7 +1188,7 @@ impl STXBalance { v1_unlock_height: u32, v2_unlock_height: u32, v3_unlock_height: u32, - ) -> Result { + ) -> Result { if self.has_unlockable_tokens_at_burn_block( burn_block_height, v1_unlock_height, @@ -1243,7 +1257,7 @@ impl STXBalance { } } - pub fn get_total_balance(&self) -> Result { + pub fn get_total_balance(&self) -> Result { let (unlocked, locked) = match self { STXBalance::Unlocked { amount } => (*amount, 0), STXBalance::LockedPoxOne { @@ -1448,7 +1462,7 @@ impl STXBalance { v1_unlock_height: u32, v2_unlock_height: u32, v3_unlock_height: u32, - ) -> Result { + ) -> Result { Ok(self.get_available_balance_at_burn_block( burn_block_height, v1_unlock_height, diff --git a/clarity/src/vm/diagnostic.rs b/clarity/src/vm/diagnostic.rs index 559dd912133..968cbff5a48 100644 --- a/clarity/src/vm/diagnostic.rs +++ b/clarity/src/vm/diagnostic.rs @@ -14,4 +14,4 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -pub use clarity_serialization::diagnostic::{DiagnosableError, Diagnostic, Level}; +pub use clarity_types::diagnostic::{DiagnosableError, Diagnostic, Level}; diff --git a/clarity/src/vm/docs/mod.rs b/clarity/src/vm/docs/mod.rs index a53fef1850b..68e995bd7d2 100644 --- a/clarity/src/vm/docs/mod.rs +++ b/clarity/src/vm/docs/mod.rs @@ -125,6 +125,15 @@ to the same contract principal.", example: "(print contract-caller) ;; Will print out a Stacks address of the transaction sender", }; +const CURRENT_CONTRACT_KEYWORD: SimpleKeywordAPI = SimpleKeywordAPI { + name: "current-contract", + snippet: "current-contract", + output_type: "principal", + description: "Returns the principal of the current contract.", + example: + "(print current-contract) ;; Will print out the Stacks address of the current contract", +}; + const STACKS_BLOCK_HEIGHT_KEYWORD: SimpleKeywordAPI = SimpleKeywordAPI { name: "stacks-block-height", snippet: "stacks-block-height", @@ -144,6 +153,16 @@ At the start of epoch 3.0, `tenure-height` will return the same value as `block- "(< tenure-height u140000) ;; returns true if the current tenure-height has passed 140,000 blocks.", }; +const BLOCK_TIME_KEYWORD: SimpleKeywordAPI = SimpleKeywordAPI { + name: "block-time", + snippet: "block-time", + output_type: "uint", + description: "Returns the Unix timestamp (in seconds) of the current Stacks block. Introduced +in Clarity 4. Provides access to the timestamp of the current block, which is +not available with `get-stacks-block-info?`.", + example: "(>= block-time u1755820800) ;; returns true if current block timestamp is at or after 2025-07-22.", +}; + const TX_SENDER_KEYWORD: SimpleKeywordAPI = SimpleKeywordAPI { name: "tx-sender", snippet: "tx-sender", @@ -2528,6 +2547,25 @@ is not a contract or the specified contract does not exist. Returns: "#, }; +const TO_ASCII: SpecialAPI = SpecialAPI { + input_type: "int|uint|bool|principal|(buff 524284)|(string-utf8 1048571)", + snippet: "to-ascii? ${1:value}", + output_type: "(response (string-ascii 1048571) uint)", + signature: "(to-ascii? value)", + description: "The `to-ascii?` function converts the input value to its ASCII representation. +If the input is a `string-utf8`, it will fail with `(err u1)` if the string contains non-ASCII +characters.", + example: r#" +(to-ascii? 123) ;; Returns (ok "123") +(to-ascii? u456) ;; Returns (ok "u456") +(to-ascii? false) ;; Returns (ok "false") +(to-ascii? 'SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4) ;; Returns (ok "SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4") +(to-ascii? 0x00112233) ;; Returns (ok "0x00112233") +(to-ascii? u"An ASCII smiley face: :)") ;; Returns (ok "An ASCII smiley face: :)") +(to-ascii? u"A smiley face emoji: \u{1F600}") ;; Returns (err u1) +"#, +}; + pub fn make_api_reference(function: &NativeFunctions) -> FunctionAPI { use crate::vm::functions::NativeFunctions::*; let name = function.get_name(); @@ -2641,6 +2679,7 @@ pub fn make_api_reference(function: &NativeFunctions) -> FunctionAPI { BitwiseLShift => make_for_simple_native(&BITWISE_LEFT_SHIFT_API, function, name), BitwiseRShift => make_for_simple_native(&BITWISE_RIGHT_SHIFT_API, function, name), ContractHash => make_for_simple_native(&CONTRACT_HASH, function, name), + ToAscii => make_for_special(&TO_ASCII, function), } } @@ -2660,6 +2699,8 @@ pub fn make_keyword_reference(variable: &NativeVariables) -> Option NativeVariables::Mainnet => MAINNET_KEYWORD.clone(), NativeVariables::ChainId => CHAINID_KEYWORD.clone(), NativeVariables::TxSponsor => TX_SPONSOR_KEYWORD.clone(), + NativeVariables::CurrentContract => CURRENT_CONTRACT_KEYWORD.clone(), + NativeVariables::BlockTime => BLOCK_TIME_KEYWORD.clone(), }; Some(KeywordAPI { name: keyword.name, diff --git a/clarity/src/vm/errors.rs b/clarity/src/vm/errors.rs index 66cf6989232..764ac466e5c 100644 --- a/clarity/src/vm/errors.rs +++ b/clarity/src/vm/errors.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -pub use clarity_serialization::errors::{ +pub use clarity_types::errors::{ Error, IncomparableError, InterpreterError, InterpreterResult, RuntimeErrorType, ShortReturnType, }; diff --git a/clarity/src/vm/functions/arithmetic.rs b/clarity/src/vm/functions/arithmetic.rs index a04e813786a..2d5c2aff3ae 100644 --- a/clarity/src/vm/functions/arithmetic.rs +++ b/clarity/src/vm/functions/arithmetic.rs @@ -78,7 +78,7 @@ macro_rules! type_force_binary_arithmetic { (Value::UInt(x), Value::UInt(y)) => U128Ops::$function(x, y), (x, _) => Err(CheckErrors::UnionTypeValueError( vec![TypeSignature::IntType, TypeSignature::UIntType], - x, + Box::new(x), ) .into()), } @@ -93,7 +93,7 @@ macro_rules! type_force_binary_comparison_v1 { (Value::UInt(x), Value::UInt(y)) => U128Ops::$function(x, y), (x, _) => Err(CheckErrors::UnionTypeValueError( vec![TypeSignature::IntType, TypeSignature::UIntType], - x, + Box::new(x), ) .into()), } @@ -127,7 +127,7 @@ macro_rules! type_force_binary_comparison_v2 { TypeSignature::max_string_utf8()?, TypeSignature::max_buffer()?, ], - x, + Box::new(x), ) .into()), } @@ -141,7 +141,7 @@ macro_rules! type_force_unary_arithmetic { Value::UInt(x) => U128Ops::$function(x), x => Err(CheckErrors::UnionTypeValueError( vec![TypeSignature::IntType, TypeSignature::UIntType], - x, + Box::new(x), ) .into()), } @@ -163,8 +163,8 @@ macro_rules! type_force_variadic_arithmetic { .map(|x| match x { Value::Int(value) => Ok(value), _ => Err(CheckErrors::TypeValueError( - TypeSignature::IntType, - x.clone(), + Box::new(TypeSignature::IntType), + Box::new(x.clone()), )), }) .collect(); @@ -177,8 +177,8 @@ macro_rules! type_force_variadic_arithmetic { .map(|x| match x { Value::UInt(value) => Ok(value), _ => Err(CheckErrors::TypeValueError( - TypeSignature::UIntType, - x.clone(), + Box::new(TypeSignature::UIntType), + Box::new(x.clone()), )), }) .collect(); @@ -187,7 +187,7 @@ macro_rules! type_force_variadic_arithmetic { } _ => Err(CheckErrors::UnionTypeValueError( vec![TypeSignature::IntType, TypeSignature::UIntType], - first.clone(), + Box::new(first.clone()), ) .into()), } @@ -600,12 +600,12 @@ pub fn native_bitwise_left_shift(input: Value, pos: Value) -> InterpreterResult< } _ => Err(CheckErrors::UnionTypeError( vec![TypeSignature::IntType, TypeSignature::UIntType], - TypeSignature::type_of(&input)?, + Box::new(TypeSignature::type_of(&input)?), ) .into()), } } else { - Err(CheckErrors::TypeValueError(TypeSignature::UIntType, pos).into()) + Err(CheckErrors::TypeValueError(Box::new(TypeSignature::UIntType), Box::new(pos)).into()) } } @@ -626,12 +626,12 @@ pub fn native_bitwise_right_shift(input: Value, pos: Value) -> InterpreterResult } _ => Err(CheckErrors::UnionTypeError( vec![TypeSignature::IntType, TypeSignature::UIntType], - TypeSignature::type_of(&input)?, + Box::new(TypeSignature::type_of(&input)?), ) .into()), } } else { - Err(CheckErrors::TypeValueError(TypeSignature::UIntType, pos).into()) + Err(CheckErrors::TypeValueError(Box::new(TypeSignature::UIntType), Box::new(pos)).into()) } } @@ -641,7 +641,7 @@ pub fn native_to_uint(input: Value) -> InterpreterResult { u128::try_from(int_val).map_err(|_| RuntimeErrorType::ArithmeticUnderflow)?; Ok(Value::UInt(uint_val)) } else { - Err(CheckErrors::TypeValueError(TypeSignature::IntType, input).into()) + Err(CheckErrors::TypeValueError(Box::new(TypeSignature::IntType), Box::new(input)).into()) } } @@ -650,6 +650,6 @@ pub fn native_to_int(input: Value) -> InterpreterResult { let int_val = i128::try_from(uint_val).map_err(|_| RuntimeErrorType::ArithmeticOverflow)?; Ok(Value::Int(int_val)) } else { - Err(CheckErrors::TypeValueError(TypeSignature::UIntType, input).into()) + Err(CheckErrors::TypeValueError(Box::new(TypeSignature::UIntType), Box::new(input)).into()) } } diff --git a/clarity/src/vm/functions/assets.rs b/clarity/src/vm/functions/assets.rs index 1d60bc7a75a..62763fa3fc0 100644 --- a/clarity/src/vm/functions/assets.rs +++ b/clarity/src/vm/functions/assets.rs @@ -108,7 +108,10 @@ pub fn special_stx_balance( }; Ok(Value::UInt(balance)) } else { - Err(CheckErrors::TypeValueError(TypeSignature::PrincipalType, owner).into()) + Err( + CheckErrors::TypeValueError(Box::new(TypeSignature::PrincipalType), Box::new(owner)) + .into(), + ) } } @@ -222,7 +225,11 @@ pub fn special_stx_account( let principal = if let Value::Principal(p) = owner { p } else { - return Err(CheckErrors::TypeValueError(TypeSignature::PrincipalType, owner).into()); + return Err(CheckErrors::TypeValueError( + Box::new(TypeSignature::PrincipalType), + Box::new(owner), + ) + .into()); }; let stx_balance = env @@ -397,7 +404,11 @@ pub fn special_mint_asset_v200( )?; if !expected_asset_type.admits(env.epoch(), &asset)? { - return Err(CheckErrors::TypeValueError(expected_asset_type.clone(), asset).into()); + return Err(CheckErrors::TypeValueError( + Box::new(expected_asset_type.clone()), + Box::new(asset), + ) + .into()); } if let Value::Principal(ref to_principal) = to { @@ -433,7 +444,10 @@ pub fn special_mint_asset_v200( Ok(Value::okay_true()) } else { - Err(CheckErrors::TypeValueError(TypeSignature::PrincipalType, to).into()) + Err( + CheckErrors::TypeValueError(Box::new(TypeSignature::PrincipalType), Box::new(to)) + .into(), + ) } } @@ -464,7 +478,11 @@ pub fn special_mint_asset_v205( runtime_cost(ClarityCostFunction::NftMint, env, asset_size)?; if !expected_asset_type.admits(env.epoch(), &asset)? { - return Err(CheckErrors::TypeValueError(expected_asset_type.clone(), asset).into()); + return Err(CheckErrors::TypeValueError( + Box::new(expected_asset_type.clone()), + Box::new(asset), + ) + .into()); } if let Value::Principal(ref to_principal) = to { @@ -500,7 +518,10 @@ pub fn special_mint_asset_v205( Ok(Value::okay_true()) } else { - Err(CheckErrors::TypeValueError(TypeSignature::PrincipalType, to).into()) + Err( + CheckErrors::TypeValueError(Box::new(TypeSignature::PrincipalType), Box::new(to)) + .into(), + ) } } @@ -531,7 +552,11 @@ pub fn special_transfer_asset_v200( )?; if !expected_asset_type.admits(env.epoch(), &asset)? { - return Err(CheckErrors::TypeValueError(expected_asset_type.clone(), asset).into()); + return Err(CheckErrors::TypeValueError( + Box::new(expected_asset_type.clone()), + Box::new(asset), + ) + .into()); } if let (Value::Principal(ref from_principal), Value::Principal(ref to_principal)) = (from, to) { @@ -621,7 +646,11 @@ pub fn special_transfer_asset_v205( runtime_cost(ClarityCostFunction::NftTransfer, env, asset_size)?; if !expected_asset_type.admits(env.epoch(), &asset)? { - return Err(CheckErrors::TypeValueError(expected_asset_type.clone(), asset).into()); + return Err(CheckErrors::TypeValueError( + Box::new(expected_asset_type.clone()), + Box::new(asset), + ) + .into()); } if let (Value::Principal(ref from_principal), Value::Principal(ref to_principal)) = (from, to) { @@ -812,7 +841,10 @@ pub fn special_get_balance( )?; Ok(Value::UInt(balance)) } else { - Err(CheckErrors::TypeValueError(TypeSignature::PrincipalType, owner).into()) + Err( + CheckErrors::TypeValueError(Box::new(TypeSignature::PrincipalType), Box::new(owner)) + .into(), + ) } } @@ -841,7 +873,11 @@ pub fn special_get_owner_v200( )?; if !expected_asset_type.admits(env.epoch(), &asset)? { - return Err(CheckErrors::TypeValueError(expected_asset_type.clone(), asset).into()); + return Err(CheckErrors::TypeValueError( + Box::new(expected_asset_type.clone()), + Box::new(asset), + ) + .into()); } match env.global_context.database.get_nft_owner( @@ -884,7 +920,11 @@ pub fn special_get_owner_v205( runtime_cost(ClarityCostFunction::NftOwner, env, asset_size)?; if !expected_asset_type.admits(env.epoch(), &asset)? { - return Err(CheckErrors::TypeValueError(expected_asset_type.clone(), asset).into()); + return Err(CheckErrors::TypeValueError( + Box::new(expected_asset_type.clone()), + Box::new(asset), + ) + .into()); } match env.global_context.database.get_nft_owner( @@ -1014,7 +1054,11 @@ pub fn special_burn_asset_v200( )?; if !expected_asset_type.admits(env.epoch(), &asset)? { - return Err(CheckErrors::TypeValueError(expected_asset_type.clone(), asset).into()); + return Err(CheckErrors::TypeValueError( + Box::new(expected_asset_type.clone()), + Box::new(asset), + ) + .into()); } if let Value::Principal(ref sender_principal) = sender { @@ -1062,7 +1106,10 @@ pub fn special_burn_asset_v200( Ok(Value::okay_true()) } else { - Err(CheckErrors::TypeValueError(TypeSignature::PrincipalType, sender).into()) + Err( + CheckErrors::TypeValueError(Box::new(TypeSignature::PrincipalType), Box::new(sender)) + .into(), + ) } } @@ -1095,7 +1142,11 @@ pub fn special_burn_asset_v205( runtime_cost(ClarityCostFunction::NftBurn, env, asset_size)?; if !expected_asset_type.admits(env.epoch(), &asset)? { - return Err(CheckErrors::TypeValueError(expected_asset_type.clone(), asset).into()); + return Err(CheckErrors::TypeValueError( + Box::new(expected_asset_type.clone()), + Box::new(asset), + ) + .into()); } if let Value::Principal(ref sender_principal) = sender { @@ -1143,6 +1194,9 @@ pub fn special_burn_asset_v205( Ok(Value::okay_true()) } else { - Err(CheckErrors::TypeValueError(TypeSignature::PrincipalType, sender).into()) + Err( + CheckErrors::TypeValueError(Box::new(TypeSignature::PrincipalType), Box::new(sender)) + .into(), + ) } } diff --git a/clarity/src/vm/functions/boolean.rs b/clarity/src/vm/functions/boolean.rs index 08716cfe64c..e692b4ad71c 100644 --- a/clarity/src/vm/functions/boolean.rs +++ b/clarity/src/vm/functions/boolean.rs @@ -25,7 +25,11 @@ use crate::vm::types::{TypeSignature, Value}; fn type_force_bool(value: &Value) -> Result { match *value { Value::Bool(boolean) => Ok(boolean), - _ => Err(CheckErrors::TypeValueError(TypeSignature::BoolType, value.clone()).into()), + _ => Err(CheckErrors::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 b922f13fc08..bd7834a5521 100644 --- a/clarity/src/vm/functions/conversions.rs +++ b/clarity/src/vm/functions/conversions.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use clarity_serialization::types::serialization::SerializationError; +use clarity_types::types::serialization::SerializationError; use crate::vm::costs::cost_functions::ClarityCostFunction; use crate::vm::costs::runtime_cost; @@ -22,6 +22,7 @@ use crate::vm::errors::{ check_argument_count, CheckErrors, InterpreterError, InterpreterResult as Result, }; use crate::vm::representations::SymbolicExpression; +use crate::vm::types::signatures::TO_ASCII_MAX_BUFF; use crate::vm::types::SequenceSubtype::BufferType; use crate::vm::types::TypeSignature::SequenceType; use crate::vm::types::{ @@ -57,10 +58,11 @@ pub fn buff_to_int_generic( .map_err(|_| InterpreterError::Expect("Failed to construct".into()))? { Err(CheckErrors::TypeValueError( - SequenceType(BufferType(BufferLength::try_from(16_u32).map_err( - |_| InterpreterError::Expect("Failed to construct".into()), - )?)), - value, + Box::new(SequenceType(BufferType( + BufferLength::try_from(16_u32) + .map_err(|_| InterpreterError::Expect("Failed to construct".into()))?, + ))), + Box::new(value), ) .into()) } else { @@ -82,10 +84,11 @@ pub fn buff_to_int_generic( } } _ => Err(CheckErrors::TypeValueError( - SequenceType(BufferType(BufferLength::try_from(16_u32).map_err( - |_| InterpreterError::Expect("Failed to construct".into()), - )?)), - value, + Box::new(SequenceType(BufferType( + BufferLength::try_from(16_u32) + .map_err(|_| InterpreterError::Expect("Failed to construct".into()))?, + ))), + Box::new(value), ) .into()), } @@ -151,7 +154,7 @@ pub fn native_string_to_int_generic( TypeSignature::max_string_ascii()?, TypeSignature::max_string_utf8()?, ], - value, + Box::new(value), ) .into()), } @@ -204,22 +207,85 @@ pub fn native_int_to_string_generic( } _ => Err(CheckErrors::UnionTypeValueError( vec![TypeSignature::IntType, TypeSignature::UIntType], - value, + Box::new(value), ) .into()), } } pub fn native_int_to_ascii(value: Value) -> Result { - // Given a string representing an integer, convert this to Clarity ASCII value. + // Given an integer, convert this to Clarity ASCII value. native_int_to_string_generic(value, Value::string_ascii_from_bytes) } pub fn native_int_to_utf8(value: Value) -> Result { - // Given a string representing an integer, convert this to Clarity UTF8 value. + // Given an integer, convert this to Clarity UTF8 value. native_int_to_string_generic(value, Value::string_utf8_from_bytes) } +/// Helper function to convert a string to ASCII and wrap in Ok response +/// This should only fail due to system errors, not conversion failures +fn convert_string_to_ascii_ok(s: String) -> Result { + let ascii_value = Value::string_ascii_from_bytes(s.into_bytes()).map_err(|_| { + InterpreterError::Expect("Unexpected error converting string to ASCII".into()) + })?; + 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), + Err(_) => Ok(Value::err_uint(1)), // Non-ASCII characters in UTF8 + } +} + +pub fn special_to_ascii( + args: &[SymbolicExpression], + env: &mut Environment, + context: &LocalContext, +) -> Result { + check_argument_count(1, args)?; + + let value = eval(&args[0], env, context)?; + + runtime_cost(ClarityCostFunction::ToAscii, env, value.size()?)?; + + match value { + Value::Int(num) => convert_string_to_ascii_ok(num.to_string()), + Value::UInt(num) => convert_string_to_ascii_ok(format!("u{num}")), + Value::Bool(b) => convert_string_to_ascii_ok(if b { + "true".to_string() + } else { + "false".to_string() + }), + Value::Principal(principal_data) => convert_string_to_ascii_ok(principal_data.to_string()), + Value::Sequence(SequenceData::Buffer(buffer_data)) => { + convert_string_to_ascii_ok(format!("0x{buffer_data}")) + } + Value::Sequence(SequenceData::String(CharType::UTF8(UTF8Data { data }))) => { + // 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), + Err(_) => Ok(Value::err_uint(1)), // Invalid UTF8 + } + } + _ => Err(CheckErrors::UnionTypeValueError( + vec![ + TypeSignature::IntType, + TypeSignature::UIntType, + TypeSignature::BoolType, + TypeSignature::PrincipalType, + TO_ASCII_MAX_BUFF.clone(), + TypeSignature::max_string_utf8()?, + ], + Box::new(value), + ) + .into()), + } +} + /// Returns `value` consensus serialized into a `(optional buff)` object. /// If the value cannot fit as serialized into the maximum buffer size, /// this returns `none`, otherwise, it will be `(some consensus-serialized-buffer)` @@ -259,8 +325,8 @@ pub fn from_consensus_buff( Ok(buff_data.data) } else { Err(CheckErrors::TypeValueError( - TypeSignature::max_buffer()?, - value, + Box::new(TypeSignature::max_buffer()?), + Box::new(value), )) }?; @@ -280,7 +346,7 @@ pub fn from_consensus_buff( ) { Ok(value) => value, Err(SerializationError::UnexpectedSerialization) => { - return Err(CheckErrors::Expects("UnexpectedSerialization".into()).into()) + return Err(CheckErrors::Expects("UnexpectedSerialization".into()).into()); } Err(_) => return Ok(Value::none()), }; diff --git a/clarity/src/vm/functions/crypto.rs b/clarity/src/vm/functions/crypto.rs index 1dd92a8f8fb..3b5223bd721 100644 --- a/clarity/src/vm/functions/crypto.rs +++ b/clarity/src/vm/functions/crypto.rs @@ -43,7 +43,7 @@ macro_rules! native_hash_func { TypeSignature::UIntType, TypeSignature::max_buffer()?, ], - input, + Box::new(input), )), }?; let hash = <$module>::from_data(&bytes); @@ -102,11 +102,19 @@ pub fn special_principal_of( let pub_key = match param0 { Value::Sequence(SequenceData::Buffer(BuffData { ref data })) => { if data.len() != 33 { - return Err(CheckErrors::TypeValueError(BUFF_33.clone(), param0).into()); + return Err(CheckErrors::TypeValueError( + Box::new(BUFF_33.clone()), + Box::new(param0), + ) + .into()); } data } - _ => return Err(CheckErrors::TypeValueError(BUFF_33.clone(), param0).into()), + _ => { + return Err( + CheckErrors::TypeValueError(Box::new(BUFF_33.clone()), Box::new(param0)).into(), + ) + } }; if let Ok(pub_key) = Secp256k1PublicKey::from_slice(pub_key) { @@ -140,25 +148,41 @@ pub fn special_secp256k1_recover( let message = match param0 { Value::Sequence(SequenceData::Buffer(BuffData { ref data })) => { if data.len() != 32 { - return Err(CheckErrors::TypeValueError(BUFF_32.clone(), param0).into()); + return Err(CheckErrors::TypeValueError( + Box::new(BUFF_32.clone()), + Box::new(param0), + ) + .into()); } data } - _ => return Err(CheckErrors::TypeValueError(BUFF_32.clone(), param0).into()), + _ => { + return Err( + CheckErrors::TypeValueError(Box::new(BUFF_32.clone()), Box::new(param0)).into(), + ) + } }; let param1 = eval(&args[1], env, context)?; let signature = match param1 { Value::Sequence(SequenceData::Buffer(BuffData { ref data })) => { if data.len() > 65 { - return Err(CheckErrors::TypeValueError(BUFF_65.clone(), param1).into()); + return Err(CheckErrors::TypeValueError( + Box::new(BUFF_65.clone()), + Box::new(param1), + ) + .into()); } if data.len() < 65 || data[64] > 3 { return Ok(Value::err_uint(2)); } data } - _ => return Err(CheckErrors::TypeValueError(BUFF_65.clone(), param1).into()), + _ => { + return Err( + CheckErrors::TypeValueError(Box::new(BUFF_65.clone()), Box::new(param1)).into(), + ) + } }; match secp256k1_recover(message, signature).map_err(|_| CheckErrors::InvalidSecp65k1Signature) { @@ -186,18 +210,30 @@ pub fn special_secp256k1_verify( let message = match param0 { Value::Sequence(SequenceData::Buffer(BuffData { ref data })) => { if data.len() != 32 { - return Err(CheckErrors::TypeValueError(BUFF_32.clone(), param0).into()); + return Err(CheckErrors::TypeValueError( + Box::new(BUFF_32.clone()), + Box::new(param0), + ) + .into()); } data } - _ => return Err(CheckErrors::TypeValueError(BUFF_32.clone(), param0).into()), + _ => { + return Err( + CheckErrors::TypeValueError(Box::new(BUFF_32.clone()), Box::new(param0)).into(), + ) + } }; let param1 = eval(&args[1], env, context)?; let signature = match param1 { Value::Sequence(SequenceData::Buffer(BuffData { ref data })) => { if data.len() > 65 { - return Err(CheckErrors::TypeValueError(BUFF_65.clone(), param1).into()); + return Err(CheckErrors::TypeValueError( + Box::new(BUFF_65.clone()), + Box::new(param1), + ) + .into()); } if data.len() < 64 { return Ok(Value::Bool(false)); @@ -207,18 +243,30 @@ pub fn special_secp256k1_verify( } data } - _ => return Err(CheckErrors::TypeValueError(BUFF_65.clone(), param1).into()), + _ => { + return Err( + CheckErrors::TypeValueError(Box::new(BUFF_65.clone()), Box::new(param1)).into(), + ) + } }; let param2 = eval(&args[2], env, context)?; let pubkey = match param2 { Value::Sequence(SequenceData::Buffer(BuffData { ref data })) => { if data.len() != 33 { - return Err(CheckErrors::TypeValueError(BUFF_33.clone(), param2).into()); + return Err(CheckErrors::TypeValueError( + Box::new(BUFF_33.clone()), + Box::new(param2), + ) + .into()); } data } - _ => return Err(CheckErrors::TypeValueError(BUFF_33.clone(), param2).into()), + _ => { + return Err( + CheckErrors::TypeValueError(Box::new(BUFF_33.clone()), Box::new(param2)).into(), + ) + } }; Ok(Value::Bool( diff --git a/clarity/src/vm/functions/database.rs b/clarity/src/vm/functions/database.rs index 11276c31deb..93fbef0258e 100644 --- a/clarity/src/vm/functions/database.rs +++ b/clarity/src/vm/functions/database.rs @@ -210,9 +210,11 @@ pub fn special_contract_call( 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)? { - return Err( - CheckErrors::ReturnTypesMustMatch(returns_type_signature, actual_returns).into(), - ); + return Err(CheckErrors::ReturnTypesMustMatch( + Box::new(returns_type_signature), + Box::new(actual_returns), + ) + .into()); } } @@ -449,7 +451,9 @@ pub fn special_at_block( StacksBlockId::from(data.as_slice()) } } - x => return Err(CheckErrors::TypeValueError(BUFF_32.clone(), x).into()), + x => { + return Err(CheckErrors::TypeValueError(Box::new(BUFF_32.clone()), Box::new(x)).into()) + } }; env.add_memory(cost_constants::AT_BLOCK_MEMORY)?; @@ -755,7 +759,10 @@ pub fn special_get_block_info( let height_eval = eval(&args[1], env, context)?; let height_value = match height_eval { Value::UInt(result) => Ok(result), - x => Err(CheckErrors::TypeValueError(TypeSignature::UIntType, x)), + x => Err(CheckErrors::TypeValueError( + Box::new(TypeSignature::UIntType), + Box::new(x), + )), }?; let height_value = match u32::try_from(height_value) { @@ -903,7 +910,11 @@ pub fn special_get_burn_block_info( let height_value = match height_eval { Value::UInt(result) => result, x => { - return Err(CheckErrors::TypeValueError(TypeSignature::UIntType, x).into()); + return Err(CheckErrors::TypeValueError( + Box::new(TypeSignature::UIntType), + Box::new(x), + ) + .into()); } }; @@ -1000,7 +1011,10 @@ pub fn special_get_stacks_block_info( let height_eval = eval(&args[1], env, context)?; let height_value = match height_eval { Value::UInt(result) => Ok(result), - x => Err(CheckErrors::TypeValueError(TypeSignature::UIntType, x)), + x => Err(CheckErrors::TypeValueError( + Box::new(TypeSignature::UIntType), + Box::new(x), + )), }?; let Ok(height_value) = u32::try_from(height_value) else { @@ -1078,7 +1092,10 @@ pub fn special_get_tenure_info( let height_eval = eval(&args[1], env, context)?; let height_value = match height_eval { Value::UInt(result) => Ok(result), - x => Err(CheckErrors::TypeValueError(TypeSignature::UIntType, x)), + x => Err(CheckErrors::TypeValueError( + Box::new(TypeSignature::UIntType), + Box::new(x), + )), }?; let Ok(height_value) = u32::try_from(height_value) else { @@ -1169,7 +1186,9 @@ pub fn special_contract_hash( Value::Principal(PrincipalData::Contract(contract_identifier)) => contract_identifier, _ => { // If the value is not a principal, we return a check error. - return Err(CheckErrors::ExpectedContractPrincipalValue(contract_value).into()); + return Err( + CheckErrors::ExpectedContractPrincipalValue(Box::new(contract_value)).into(), + ); } }; diff --git a/clarity/src/vm/functions/define.rs b/clarity/src/vm/functions/define.rs index 071d7dfafac..9cef5640743 100644 --- a/clarity/src/vm/functions/define.rs +++ b/clarity/src/vm/functions/define.rs @@ -217,7 +217,11 @@ fn handle_define_fungible_token( Some(total_supply_int), )) } else { - Err(CheckErrors::TypeValueError(TypeSignature::UIntType, total_supply_value).into()) + Err(CheckErrors::TypeValueError( + Box::new(TypeSignature::UIntType), + Box::new(total_supply_value), + ) + .into()) } } else { Ok(DefineResult::FungibleToken(asset_name.clone(), None)) diff --git a/clarity/src/vm/functions/mod.rs b/clarity/src/vm/functions/mod.rs index 9cdabcc34a3..f458c282e6c 100644 --- a/clarity/src/vm/functions/mod.rs +++ b/clarity/src/vm/functions/mod.rs @@ -192,6 +192,7 @@ define_versioned_named_enum_with_max!(NativeFunctions(ClarityVersion) { GetStacksBlockInfo("get-stacks-block-info?", ClarityVersion::Clarity3, None), GetTenureInfo("get-tenure-info?", ClarityVersion::Clarity3, None), ContractHash("contract-hash?", ClarityVersion::Clarity4, None), + ToAscii("to-ascii?", ClarityVersion::Clarity4, None), }); /// @@ -563,6 +564,7 @@ pub fn lookup_reserved_functions(name: &str, version: &ClarityVersion) -> Option ContractHash => { SpecialFunction("special_contract_hash", &database::special_contract_hash) } + ToAscii => SpecialFunction("special_to_ascii", &conversions::special_to_ascii), }; Some(callable) } else { @@ -640,7 +642,11 @@ fn special_if( eval(&args[2], env, context) } } - _ => Err(CheckErrors::TypeValueError(TypeSignature::BoolType, conditional).into()), + _ => Err(CheckErrors::TypeValueError( + Box::new(TypeSignature::BoolType), + Box::new(conditional), + ) + .into()), } } @@ -661,10 +667,14 @@ fn special_asserts( Ok(conditional) } else { let thrown = eval(&args[1], env, context)?; - Err(ShortReturnType::AssertionFailed(thrown).into()) + Err(ShortReturnType::AssertionFailed(Box::new(thrown)).into()) } } - _ => Err(CheckErrors::TypeValueError(TypeSignature::BoolType, conditional).into()), + _ => Err(CheckErrors::TypeValueError( + Box::new(TypeSignature::BoolType), + Box::new(conditional), + ) + .into()), } } diff --git a/clarity/src/vm/functions/options.rs b/clarity/src/vm/functions/options.rs index edbd2d99081..c47dae91ccd 100644 --- a/clarity/src/vm/functions/options.rs +++ b/clarity/src/vm/functions/options.rs @@ -35,7 +35,7 @@ fn inner_unwrap(to_unwrap: Value) -> Result> { None } } - _ => return Err(CheckErrors::ExpectedOptionalOrResponseValue(to_unwrap).into()), + _ => return Err(CheckErrors::ExpectedOptionalOrResponseValue(Box::new(to_unwrap)).into()), }; Ok(result) @@ -50,7 +50,7 @@ fn inner_unwrap_err(to_unwrap: Value) -> Result> { None } } - _ => return Err(CheckErrors::ExpectedResponseValue(to_unwrap).into()), + _ => return Err(CheckErrors::ExpectedResponseValue(Box::new(to_unwrap)).into()), }; Ok(result) @@ -66,7 +66,7 @@ pub fn native_unwrap(input: Value) -> Result { pub fn native_unwrap_or_ret(input: Value, thrown: Value) -> Result { inner_unwrap(input).and_then(|opt_value| match opt_value { Some(v) => Ok(v), - None => Err(ShortReturnType::ExpectedValue(thrown).into()), + None => Err(ShortReturnType::ExpectedValue(Box::new(thrown)).into()), }) } @@ -80,7 +80,7 @@ pub fn native_unwrap_err(input: Value) -> Result { pub fn native_unwrap_err_or_ret(input: Value, thrown: Value) -> Result { inner_unwrap_err(input).and_then(|opt_value| match opt_value { Some(v) => Ok(v), - None => Err(ShortReturnType::ExpectedValue(thrown).into()), + None => Err(ShortReturnType::ExpectedValue(Box::new(thrown)).into()), }) } @@ -88,7 +88,7 @@ pub fn native_try_ret(input: Value) -> Result { match input { Value::Optional(data) => match data.data { Some(data) => Ok(*data), - None => Err(ShortReturnType::ExpectedValue(Value::none()).into()), + None => Err(ShortReturnType::ExpectedValue(Box::new(Value::none())).into()), }, Value::Response(data) => { if data.committed { @@ -99,10 +99,10 @@ pub fn native_try_ret(input: Value) -> Result { "BUG: Failed to construct new response type from old response type".into(), ) })?; - Err(ShortReturnType::ExpectedValue(short_return_val).into()) + Err(ShortReturnType::ExpectedValue(Box::new(short_return_val)).into()) } } - _ => Err(CheckErrors::ExpectedOptionalOrResponseValue(input).into()), + _ => Err(CheckErrors::ExpectedOptionalOrResponseValue(Box::new(input)).into()), } } @@ -212,7 +212,7 @@ pub fn special_match( match input { Value::Response(data) => special_match_resp(data, &args[1..], env, context), Value::Optional(data) => special_match_opt(data, &args[1..], env, context), - _ => Err(CheckErrors::BadMatchInput(TypeSignature::type_of(&input)?).into()), + _ => Err(CheckErrors::BadMatchInput(Box::new(TypeSignature::type_of(&input)?)).into()), } } @@ -223,14 +223,14 @@ pub fn native_some(input: Value) -> Result { fn is_some(input: Value) -> Result { match input { Value::Optional(ref data) => Ok(data.data.is_some()), - _ => Err(CheckErrors::ExpectedOptionalValue(input).into()), + _ => Err(CheckErrors::ExpectedOptionalValue(Box::new(input)).into()), } } fn is_okay(input: Value) -> Result { match input { Value::Response(data) => Ok(data.committed), - _ => Err(CheckErrors::ExpectedResponseValue(input).into()), + _ => Err(CheckErrors::ExpectedResponseValue(Box::new(input)).into()), } } @@ -264,6 +264,6 @@ pub fn native_default_to(default: Value, input: Value) -> Result { Some(data) => Ok(*data), None => Ok(default), }, - _ => Err(CheckErrors::ExpectedOptionalValue(input).into()), + _ => Err(CheckErrors::ExpectedOptionalValue(Box::new(input)).into()), } } diff --git a/clarity/src/vm/functions/principals.rs b/clarity/src/vm/functions/principals.rs index c3600e6654a..51fc33136f2 100644 --- a/clarity/src/vm/functions/principals.rs +++ b/clarity/src/vm/functions/principals.rs @@ -61,7 +61,11 @@ pub fn special_is_standard( let version = if let Value::Principal(ref p) = owner { p.version() } else { - return Err(CheckErrors::TypeValueError(TypeSignature::PrincipalType, owner).into()); + return Err(CheckErrors::TypeValueError( + Box::new(TypeSignature::PrincipalType), + Box::new(owner), + ) + .into()); }; Ok(Value::Bool(version_matches_current_network( @@ -165,7 +169,11 @@ pub fn special_principal_destruct( (issuer.0, issuer.1, Some(name)) } _ => { - return Err(CheckErrors::TypeValueError(TypeSignature::PrincipalType, principal).into()) + return Err(CheckErrors::TypeValueError( + Box::new(TypeSignature::PrincipalType), + Box::new(principal), + ) + .into()); } }; @@ -203,14 +211,16 @@ pub fn special_principal_construct( _ => { return { // This is an aborting error because this should have been caught in analysis pass. - Err(CheckErrors::TypeValueError(BUFF_1.clone(), version).into()) + Err(CheckErrors::TypeValueError(Box::new(BUFF_1.clone()), Box::new(version)).into()) }; } }; let version_byte = if verified_version.len() > 1 { // should have been caught by the type-checker - return Err(CheckErrors::TypeValueError(BUFF_1.clone(), version).into()); + return Err( + CheckErrors::TypeValueError(Box::new(BUFF_1.clone()), Box::new(version)).into(), + ); } else if verified_version.is_empty() { // the type checker does not check the actual length of the buffer, but a 0-length buffer // will type-check to (buff 1) @@ -233,13 +243,21 @@ pub fn special_principal_construct( // This is an aborting error because this should have been caught in analysis pass. let verified_hash_bytes = match hash_bytes { Value::Sequence(SequenceData::Buffer(BuffData { ref data })) => data, - _ => return Err(CheckErrors::TypeValueError(BUFF_20.clone(), hash_bytes).into()), + _ => { + return Err(CheckErrors::TypeValueError( + Box::new(BUFF_20.clone()), + Box::new(hash_bytes), + ) + .into()) + } }; // This must have been a (buff 20). // This is an aborting error because this should have been caught in analysis pass. if verified_hash_bytes.len() > 20 { - return Err(CheckErrors::TypeValueError(BUFF_20.clone(), hash_bytes).into()); + return Err( + CheckErrors::TypeValueError(Box::new(BUFF_20.clone()), Box::new(hash_bytes)).into(), + ); } // If the hash-bytes buffer has less than 20 bytes, this is a runtime error, because it @@ -260,8 +278,8 @@ pub fn special_principal_construct( Value::Sequence(SequenceData::String(CharType::ASCII(ascii_data))) => ascii_data, _ => { return Err(CheckErrors::TypeValueError( - TypeSignature::contract_name_string_ascii_type()?, - name, + Box::new(TypeSignature::contract_name_string_ascii_type()?), + Box::new(name), ) .into()) } @@ -277,8 +295,8 @@ pub fn special_principal_construct( // if it's too long, then this should have been caught by the type-checker if name_bytes.data.len() > CONTRACT_MAX_NAME_LENGTH { return Err(CheckErrors::TypeValueError( - TypeSignature::contract_name_string_ascii_type()?, - Value::from(name_bytes), + Box::new(TypeSignature::contract_name_string_ascii_type()?), + Box::new(Value::from(name_bytes)), ) .into()); } diff --git a/clarity/src/vm/functions/sequences.rs b/clarity/src/vm/functions/sequences.rs index 8bc89e7373f..cafc795d362 100644 --- a/clarity/src/vm/functions/sequences.rs +++ b/clarity/src/vm/functions/sequences.rs @@ -70,11 +70,18 @@ pub fn special_filter( if let Value::Bool(include) = filter_eval { Ok(include) } else { - Err(CheckErrors::TypeValueError(BoolType, filter_eval).into()) + Err( + CheckErrors::TypeValueError(Box::new(BoolType), Box::new(filter_eval)) + .into(), + ) } })?; } - _ => return Err(CheckErrors::ExpectedSequence(TypeSignature::type_of(&sequence)?).into()), + _ => { + return Err( + CheckErrors::ExpectedSequence(Box::new(TypeSignature::type_of(&sequence)?)).into(), + ) + } }; Ok(sequence) } @@ -106,7 +113,9 @@ pub fn special_fold( context, ) }), - _ => Err(CheckErrors::ExpectedSequence(TypeSignature::type_of(&sequence)?).into()), + _ => { + Err(CheckErrors::ExpectedSequence(Box::new(TypeSignature::type_of(&sequence)?)).into()) + } } } @@ -145,7 +154,8 @@ pub fn special_map( } _ => { return Err( - CheckErrors::ExpectedSequence(TypeSignature::type_of(&sequence)?).into(), + CheckErrors::ExpectedSequence(Box::new(TypeSignature::type_of(&sequence)?)) + .into(), ) } } @@ -209,7 +219,7 @@ pub fn special_append( data, }))) } else { - Err(CheckErrors::TypeValueError(entry_type, element).into()) + Err(CheckErrors::TypeValueError(Box::new(entry_type), Box::new(element)).into()) } } _ => Err(CheckErrors::ExpectedListApplication.into()), @@ -289,7 +299,8 @@ pub fn special_as_max_len( Value::Sequence(ref sequence_data) => sequence_data.len() as u128, _ => { return Err( - CheckErrors::ExpectedSequence(TypeSignature::type_of(&sequence)?).into(), + CheckErrors::ExpectedSequence(Box::new(TypeSignature::type_of(&sequence)?)) + .into(), ) } }; @@ -304,8 +315,8 @@ pub fn special_as_max_len( } else { let actual_len = eval(&args[1], env, context)?; Err(CheckErrors::TypeError( - TypeSignature::UIntType, - TypeSignature::type_of(&actual_len)?, + Box::new(TypeSignature::UIntType), + Box::new(TypeSignature::type_of(&actual_len)?), ) .into()) } @@ -314,7 +325,9 @@ 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(CheckErrors::ExpectedSequence(TypeSignature::type_of(&sequence)?).into()), + _ => { + Err(CheckErrors::ExpectedSequence(Box::new(TypeSignature::type_of(&sequence)?)).into()) + } } } @@ -325,7 +338,7 @@ pub fn native_index_of(sequence: Value, to_find: Value) -> Result { None => Ok(Value::none()), } } else { - Err(CheckErrors::ExpectedSequence(TypeSignature::type_of(&sequence)?).into()) + Err(CheckErrors::ExpectedSequence(Box::new(TypeSignature::type_of(&sequence)?)).into()) } } @@ -333,7 +346,9 @@ pub fn native_element_at(sequence: Value, index: Value) -> Result { let sequence_data = if let Value::Sequence(sequence_data) = sequence { sequence_data } else { - return Err(CheckErrors::ExpectedSequence(TypeSignature::type_of(&sequence)?).into()); + return Err( + CheckErrors::ExpectedSequence(Box::new(TypeSignature::type_of(&sequence)?)).into(), + ); }; let index = if let Value::UInt(index_u128) = index { @@ -343,7 +358,11 @@ pub fn native_element_at(sequence: Value, index: Value) -> Result { return Ok(Value::none()); } } else { - return Err(CheckErrors::TypeValueError(TypeSignature::UIntType, index).into()); + return Err(CheckErrors::TypeValueError( + Box::new(TypeSignature::UIntType), + Box::new(index), + ) + .into()); }; if let Some(result) = sequence_data.element_at(index)? { @@ -420,7 +439,7 @@ pub fn special_replace_at( let expected_elem_type = if let TypeSignature::SequenceType(seq_subtype) = &seq_type { seq_subtype.unit_type()? } else { - return Err(CheckErrors::ExpectedSequence(seq_type).into()); + return Err(CheckErrors::ExpectedSequence(Box::new(seq_type)).into()); }; let index_val = eval(&args[1], env, context)?; let new_element = eval(&args[2], env, context)?; @@ -428,7 +447,11 @@ pub fn special_replace_at( if expected_elem_type != TypeSignature::NoType && !expected_elem_type.admits(env.epoch(), &new_element)? { - return Err(CheckErrors::TypeValueError(expected_elem_type, new_element).into()); + return Err(CheckErrors::TypeValueError( + Box::new(expected_elem_type), + Box::new(new_element), + ) + .into()); } let index = if let Value::UInt(index_u128) = index_val { @@ -438,7 +461,11 @@ pub fn special_replace_at( return Ok(Value::none()); } } else { - return Err(CheckErrors::TypeValueError(TypeSignature::UIntType, index_val).into()); + return Err(CheckErrors::TypeValueError( + Box::new(TypeSignature::UIntType), + Box::new(index_val), + ) + .into()); }; if let Value::Sequence(data) = seq { @@ -448,6 +475,6 @@ pub fn special_replace_at( } data.replace_at(env.epoch(), index, new_element) } else { - Err(CheckErrors::ExpectedSequence(seq_type).into()) + Err(CheckErrors::ExpectedSequence(Box::new(seq_type)).into()) } } diff --git a/clarity/src/vm/functions/tuples.rs b/clarity/src/vm/functions/tuples.rs index 5aefe70e608..7db8f13959a 100644 --- a/clarity/src/vm/functions/tuples.rs +++ b/clarity/src/vm/functions/tuples.rs @@ -65,7 +65,10 @@ pub fn tuple_get( ) })?) } else { - Err(CheckErrors::ExpectedTuple(TypeSignature::type_of(&data)?).into()) + Err( + CheckErrors::ExpectedTuple(Box::new(TypeSignature::type_of(&data)?)) + .into(), + ) } } None => Ok(Value::none()), // just pass through none-types. @@ -75,19 +78,23 @@ pub fn tuple_get( runtime_cost(ClarityCostFunction::TupleGet, env, tuple_data.len())?; tuple_data.get_owned(arg_name) } - _ => Err(CheckErrors::ExpectedTuple(TypeSignature::type_of(&value)?).into()), + _ => Err(CheckErrors::ExpectedTuple(Box::new(TypeSignature::type_of(&value)?)).into()), } } pub fn tuple_merge(base: Value, update: Value) -> Result { let initial_values = match base { Value::Tuple(initial_values) => Ok(initial_values), - _ => Err(CheckErrors::ExpectedTuple(TypeSignature::type_of(&base)?)), + _ => Err(CheckErrors::ExpectedTuple(Box::new( + TypeSignature::type_of(&base)?, + ))), }?; let new_values = match update { Value::Tuple(new_values) => Ok(new_values), - _ => Err(CheckErrors::ExpectedTuple(TypeSignature::type_of(&update)?)), + _ => Err(CheckErrors::ExpectedTuple(Box::new( + TypeSignature::type_of(&update)?, + ))), }?; let combined = TupleData::shallow_merge(initial_values, new_values)?; diff --git a/clarity/src/vm/mod.rs b/clarity/src/vm/mod.rs index a716f7799e3..781c7d4d7ba 100644 --- a/clarity/src/vm/mod.rs +++ b/clarity/src/vm/mod.rs @@ -13,8 +13,6 @@ // // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#![allow(clippy::result_large_err)] - pub mod diagnostic; pub mod errors; @@ -55,7 +53,7 @@ pub mod clarity; use std::collections::BTreeMap; -pub use clarity_serialization::MAX_CALL_STACK_DEPTH; +pub use clarity_types::MAX_CALL_STACK_DEPTH; use costs::CostErrors; use stacks_common::types::StacksEpochId; diff --git a/clarity/src/vm/representations.rs b/clarity/src/vm/representations.rs index 94d87718c1b..9cc95ec5a8e 100644 --- a/clarity/src/vm/representations.rs +++ b/clarity/src/vm/representations.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -pub use clarity_serialization::representations::{ +pub use clarity_types::representations::{ depth_traverse, ClarityName, ContractName, PreSymbolicExpression, PreSymbolicExpressionType, Span, SymbolicExpression, SymbolicExpressionCommon, SymbolicExpressionType, TraitDefinition, CLARITY_NAME_REGEX, CLARITY_NAME_REGEX_STRING, CONTRACT_MAX_NAME_LENGTH, diff --git a/clarity/src/vm/tests/contracts.rs b/clarity/src/vm/tests/contracts.rs index 5af6bc14056..da9d4f37318 100644 --- a/clarity/src/vm/tests/contracts.rs +++ b/clarity/src/vm/tests/contracts.rs @@ -114,8 +114,16 @@ fn test_get_block_info_eval( Ok(Value::none()), Ok(Value::none()), Ok(Value::none()), - Err(CheckErrors::TypeValueError(TypeSignature::UIntType, Value::Int(-1)).into()), - Err(CheckErrors::TypeValueError(TypeSignature::UIntType, Value::Bool(true)).into()), + Err(CheckErrors::TypeValueError( + Box::new(TypeSignature::UIntType), + Box::new(Value::Int(-1)), + ) + .into()), + Err(CheckErrors::TypeValueError( + Box::new(TypeSignature::UIntType), + Box::new(Value::Bool(true)), + ) + .into()), Ok(Value::none()), Ok(Value::none()), Ok(Value::none()), @@ -1043,7 +1051,10 @@ fn test_ast_stack_depth() { "; assert_eq!( vm_execute(program).unwrap_err(), - RuntimeErrorType::ASTError(ParseErrors::VaryExpressionStackDepthTooDeep.into()).into() + RuntimeErrorType::ASTError(Box::new( + ParseErrors::VaryExpressionStackDepthTooDeep.into(), + )) + .into() ); } @@ -1371,8 +1382,8 @@ fn test_contract_hash_type_check( .unwrap_err(); assert_eq!( err, - Error::Unchecked(CheckErrors::ExpectedContractPrincipalValue(Value::UInt( - 123 + Error::Unchecked(CheckErrors::ExpectedContractPrincipalValue(Box::new( + Value::UInt(123) ))) ); } diff --git a/clarity/src/vm/tests/conversions.rs b/clarity/src/vm/tests/conversions.rs index dbe45eb724c..c6835efaa44 100644 --- a/clarity/src/vm/tests/conversions.rs +++ b/clarity/src/vm/tests/conversions.rs @@ -14,14 +14,17 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -pub use crate::vm::analysis::errors::{CheckError, CheckErrors}; -use crate::vm::types::SequenceSubtype::{BufferType, StringType}; -use crate::vm::types::StringSubtype::ASCII; +use stacks_common::types::StacksEpochId; + +pub use crate::vm::analysis::errors::CheckErrors; +use crate::vm::tests::test_clarity_versions; +use crate::vm::types::signatures::MAX_TO_ASCII_BUFFER_LEN; +use crate::vm::types::SequenceSubtype::BufferType; use crate::vm::types::TypeSignature::SequenceType; use crate::vm::types::{ ASCIIData, BuffData, BufferLength, CharType, SequenceData, TypeSignature, UTF8Data, Value, }; -use crate::vm::{execute_v2, ClarityVersion}; +use crate::vm::{execute_v2, execute_with_parameters, ClarityVersion}; #[test] fn test_simple_buff_to_int_le() { @@ -53,10 +56,14 @@ fn test_simple_buff_to_int_le() { assert_eq!( execute_v2(bad_wrong_type_test).unwrap_err(), CheckErrors::TypeValueError( - SequenceType(BufferType(BufferLength::try_from(16_u32).unwrap())), - Value::Sequence(SequenceData::String(CharType::ASCII(ASCIIData { - data: "wrong-type".as_bytes().to_vec() - }))) + Box::new(SequenceType(BufferType( + BufferLength::try_from(16_u32).unwrap() + ))), + Box::new(Value::Sequence(SequenceData::String(CharType::ASCII( + ASCIIData { + data: "wrong-type".as_bytes().to_vec() + } + )))) ) .into() ); @@ -66,10 +73,12 @@ fn test_simple_buff_to_int_le() { assert_eq!( execute_v2(bad_too_large_test).unwrap_err(), CheckErrors::TypeValueError( - SequenceType(BufferType(BufferLength::try_from(16_u32).unwrap())), - Value::Sequence(SequenceData::Buffer(BuffData { - data: vec![00, 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12, 13, 14, 15, 00] - })) + Box::new(SequenceType(BufferType( + BufferLength::try_from(16_u32).unwrap() + ))), + Box::new(Value::Sequence(SequenceData::Buffer(BuffData { + data: vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0] + }))) ) .into() ); @@ -105,10 +114,14 @@ fn test_simple_buff_to_uint_le() { assert_eq!( execute_v2(bad_wrong_type_test).unwrap_err(), CheckErrors::TypeValueError( - SequenceType(BufferType(BufferLength::try_from(16_u32).unwrap())), - Value::Sequence(SequenceData::String(CharType::ASCII(ASCIIData { - data: "wrong-type".as_bytes().to_vec() - }))) + Box::new(SequenceType(BufferType( + BufferLength::try_from(16_u32).unwrap() + ))), + Box::new(Value::Sequence(SequenceData::String(CharType::ASCII( + ASCIIData { + data: "wrong-type".as_bytes().to_vec() + } + )))) ) .into() ); @@ -118,10 +131,12 @@ fn test_simple_buff_to_uint_le() { assert_eq!( execute_v2(bad_too_large_test).unwrap_err(), CheckErrors::TypeValueError( - SequenceType(BufferType(BufferLength::try_from(16_u32).unwrap())), - Value::Sequence(SequenceData::Buffer(BuffData { - data: vec![00, 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12, 13, 14, 15, 00] - })) + Box::new(SequenceType(BufferType( + BufferLength::try_from(16_u32).unwrap() + ))), + Box::new(Value::Sequence(SequenceData::Buffer(BuffData { + data: vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0] + }))) ) .into() ); @@ -157,10 +172,14 @@ fn test_simple_buff_to_int_be() { assert_eq!( execute_v2(bad_wrong_type_test).unwrap_err(), CheckErrors::TypeValueError( - SequenceType(BufferType(BufferLength::try_from(16 as u32).unwrap())), - Value::Sequence(SequenceData::String(CharType::ASCII(ASCIIData { - data: "wrong-type".as_bytes().to_vec() - }))) + Box::new(SequenceType(BufferType( + BufferLength::try_from(16_u32).unwrap() + ))), + Box::new(Value::Sequence(SequenceData::String(CharType::ASCII( + ASCIIData { + data: "wrong-type".as_bytes().to_vec() + } + )))) ) .into() ); @@ -170,10 +189,12 @@ fn test_simple_buff_to_int_be() { assert_eq!( execute_v2(bad_too_large_test).unwrap_err(), CheckErrors::TypeValueError( - SequenceType(BufferType(BufferLength::try_from(16_u32).unwrap())), - Value::Sequence(SequenceData::Buffer(BuffData { - data: vec![00, 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12, 13, 14, 15, 00] - })) + Box::new(SequenceType(BufferType( + BufferLength::try_from(16_u32).unwrap() + ))), + Box::new(Value::Sequence(SequenceData::Buffer(BuffData { + data: vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0] + }))) ) .into() ); @@ -209,10 +230,14 @@ fn test_simple_buff_to_uint_be() { assert_eq!( execute_v2(bad_wrong_type_test).unwrap_err(), CheckErrors::TypeValueError( - SequenceType(BufferType(BufferLength::try_from(16_u32).unwrap())), - Value::Sequence(SequenceData::String(CharType::ASCII(ASCIIData { - data: "wrong-type".as_bytes().to_vec() - }))) + Box::new(SequenceType(BufferType( + BufferLength::try_from(16_u32).unwrap() + ))), + Box::new(Value::Sequence(SequenceData::String(CharType::ASCII( + ASCIIData { + data: "wrong-type".as_bytes().to_vec() + } + )))) ) .into() ); @@ -222,10 +247,12 @@ fn test_simple_buff_to_uint_be() { assert_eq!( execute_v2(bad_too_large_test).unwrap_err(), CheckErrors::TypeValueError( - SequenceType(BufferType(BufferLength::try_from(16_u32).unwrap())), - Value::Sequence(SequenceData::Buffer(BuffData { - data: vec![00, 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12, 13, 14, 15, 00] - })) + Box::new(SequenceType(BufferType( + BufferLength::try_from(16_u32).unwrap() + ))), + Box::new(Value::Sequence(SequenceData::Buffer(BuffData { + data: vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0] + }))) ) .into() ); @@ -287,10 +314,10 @@ fn test_simple_string_to_int() { execute_v2(wrong_type_error_test).unwrap_err(), CheckErrors::UnionTypeValueError( vec![ - TypeSignature::max_string_ascii(), - TypeSignature::max_string_utf8(), + TypeSignature::max_string_ascii().unwrap(), + TypeSignature::max_string_utf8().unwrap(), ], - Value::Int(1) + Box::new(Value::Int(1)) ) .into() ); @@ -352,10 +379,10 @@ fn test_simple_string_to_uint() { execute_v2(wrong_type_error_test).unwrap_err(), CheckErrors::UnionTypeValueError( vec![ - TypeSignature::max_string_ascii(), - TypeSignature::max_string_utf8(), + TypeSignature::max_string_ascii().unwrap(), + TypeSignature::max_string_utf8().unwrap(), ], - Value::Int(1) + Box::new(Value::Int(1)) ) .into() ); @@ -386,9 +413,11 @@ fn test_simple_int_to_ascii() { execute_v2(wrong_type_error_test).unwrap_err(), CheckErrors::UnionTypeValueError( vec![TypeSignature::IntType, TypeSignature::UIntType], - Value::Sequence(SequenceData::String(CharType::ASCII(ASCIIData { - data: "1".as_bytes().to_vec() - }))) + Box::new(Value::Sequence(SequenceData::String(CharType::ASCII( + ASCIIData { + data: "1".as_bytes().to_vec() + } + )))) ) .into() ); @@ -419,10 +448,234 @@ fn test_simple_int_to_utf8() { execute_v2(wrong_type_error_test).unwrap_err(), CheckErrors::UnionTypeValueError( vec![TypeSignature::IntType, TypeSignature::UIntType], - Value::Sequence(SequenceData::String(CharType::ASCII(ASCIIData { - data: "1".as_bytes().to_vec() - }))) + Box::new(Value::Sequence(SequenceData::String(CharType::ASCII( + ASCIIData { + data: "1".as_bytes().to_vec() + } + )))) ) .into() ); } + +#[apply(test_clarity_versions)] +fn test_to_ascii(version: ClarityVersion, epoch: StacksEpochId) { + // to-ascii? is available in Clarity 4 + if version < ClarityVersion::Clarity4 { + return; + } + + // Test successful conversions + let int_to_ascii = "(to-ascii? 9876)"; + assert_eq!( + execute_with_parameters( + int_to_ascii, + version, + epoch, + crate::vm::ast::ASTRules::PrecheckSize, + false, + ), + Ok(Some( + Value::okay(Value::string_ascii_from_bytes(b"9876".to_vec()).unwrap()).unwrap() + )) + ); + + let uint_to_ascii = "(to-ascii? u12345678)"; + assert_eq!( + execute_with_parameters( + uint_to_ascii, + version, + epoch, + crate::vm::ast::ASTRules::PrecheckSize, + false, + ), + Ok(Some( + Value::okay(Value::string_ascii_from_bytes(b"u12345678".to_vec()).unwrap()).unwrap() + )) + ); + + let bool_true_to_ascii = "(to-ascii? true)"; + assert_eq!( + execute_with_parameters( + bool_true_to_ascii, + version, + epoch, + crate::vm::ast::ASTRules::PrecheckSize, + false, + ), + Ok(Some( + Value::okay(Value::string_ascii_from_bytes(b"true".to_vec()).unwrap()).unwrap() + )) + ); + + let bool_false_to_ascii = "(to-ascii? false)"; + assert_eq!( + execute_with_parameters( + bool_false_to_ascii, + version, + epoch, + crate::vm::ast::ASTRules::PrecheckSize, + false, + ), + Ok(Some( + Value::okay(Value::string_ascii_from_bytes(b"false".to_vec()).unwrap()).unwrap() + )) + ); + + let standard_principal_to_ascii = "(to-ascii? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM)"; + assert_eq!( + execute_with_parameters( + standard_principal_to_ascii, + version, + epoch, + crate::vm::ast::ASTRules::PrecheckSize, + false, + ), + Ok(Some( + Value::okay( + Value::string_ascii_from_bytes( + b"ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM".to_vec() + ) + .unwrap() + ) + .unwrap() + )) + ); + + let contract_principal_to_ascii = "(to-ascii? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.foo)"; + assert_eq!( + execute_with_parameters( + contract_principal_to_ascii, + version, + epoch, + crate::vm::ast::ASTRules::PrecheckSize, + false, + ), + Ok(Some( + Value::okay( + Value::string_ascii_from_bytes( + b"ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.foo".to_vec() + ) + .unwrap() + ) + .unwrap() + )) + ); + + let buffer_to_ascii = "(to-ascii? 0x1234)"; + assert_eq!( + execute_with_parameters( + buffer_to_ascii, + version, + epoch, + crate::vm::ast::ASTRules::PrecheckSize, + false, + ), + Ok(Some( + Value::okay(Value::string_ascii_from_bytes(b"0x1234".to_vec()).unwrap()).unwrap() + )) + ); + + let utf8_string_to_ascii = "(to-ascii? u\"I am serious, and don't call me Shirley.\")"; + assert_eq!( + execute_with_parameters( + utf8_string_to_ascii, + version, + epoch, + crate::vm::ast::ASTRules::PrecheckSize, + false, + ), + Ok(Some( + Value::okay( + Value::string_ascii_from_bytes( + b"I am serious, and don't call me Shirley.".to_vec() + ) + .unwrap() + ) + .unwrap() + )) + ); + + // This should error since the UTF-8 string contains a non-ASCII character + let utf8_string_with_non_ascii = "(to-ascii? u\"A smiley face emoji: \\u{1F600}\")"; + assert_eq!( + execute_with_parameters( + utf8_string_with_non_ascii, + version, + epoch, + crate::vm::ast::ASTRules::PrecheckSize, + false, + ), + Ok(Some(Value::err_uint(1))) // Should return error for non-ASCII UTF8 + ); + + // Test error cases - these should fail at analysis time with type errors + let ascii_string_to_ascii = "(to-ascii? \"60 percent of the time, it works every time\")"; + let result = execute_with_parameters( + ascii_string_to_ascii, + version, + epoch, + crate::vm::ast::ASTRules::PrecheckSize, + false, + ); + // This should fail at analysis time since ASCII strings are not allowed + assert!(result.is_err()); + + let list_to_ascii = "(to-ascii? (list 1 2 3))"; + let result = execute_with_parameters( + list_to_ascii, + version, + epoch, + crate::vm::ast::ASTRules::PrecheckSize, + false, + ); + // This should fail at analysis time since lists are not allowed + assert!(result.is_err()); + + let tuple_to_ascii = "(to-ascii? { a: 1, b: u2 })"; + let result = execute_with_parameters( + tuple_to_ascii, + version, + epoch, + crate::vm::ast::ASTRules::PrecheckSize, + false, + ); + // This should fail at analysis time since tuples are not allowed + assert!(result.is_err()); + + let optional_to_ascii = "(to-ascii? (some u789))"; + let result = execute_with_parameters( + optional_to_ascii, + version, + epoch, + crate::vm::ast::ASTRules::PrecheckSize, + false, + ); + // This should fail at analysis time since optionals are not allowed + assert!(result.is_err()); + + let response_to_ascii = "(to-ascii? (ok true))"; + let result = execute_with_parameters( + response_to_ascii, + version, + epoch, + crate::vm::ast::ASTRules::PrecheckSize, + false, + ); + // This should fail at analysis time since responses are not allowed + assert!(result.is_err()); + + let oversized_buffer_to_ascii = format!( + "(to-ascii? 0x{})", + "ff".repeat(MAX_TO_ASCII_BUFFER_LEN as usize + 1) + ); + let result = execute_with_parameters( + response_to_ascii, + version, + epoch, + crate::vm::ast::ASTRules::PrecheckSize, + false, + ); + // This should fail at analysis time since the value is too big + assert!(result.is_err()); +} diff --git a/clarity/src/vm/tests/datamaps.rs b/clarity/src/vm/tests/datamaps.rs index 4e748dd98df..83fef9ab764 100644 --- a/clarity/src/vm/tests/datamaps.rs +++ b/clarity/src/vm/tests/datamaps.rs @@ -296,7 +296,7 @@ fn test_set_response_variable() { "#; let contract_src = contract_src.to_string(); assert_eq!( - Err(ShortReturnType::ExpectedValue(Value::Int(5)).into()), + Err(ShortReturnType::ExpectedValue(Box::new(Value::Int(5))).into()), execute(&contract_src) ); } diff --git a/clarity/src/vm/tests/defines.rs b/clarity/src/vm/tests/defines.rs index 49bf702584d..2cd874f2c31 100644 --- a/clarity/src/vm/tests/defines.rs +++ b/clarity/src/vm/tests/defines.rs @@ -26,10 +26,7 @@ use crate::vm::tests::test_clarity_versions; #[cfg(test)] use crate::vm::{ analysis::errors::SyntaxBindingError, - ast::{ - build_ast, - errors::{ParseError, ParseErrors}, - }, + ast::{build_ast, errors::ParseErrors}, errors::RuntimeErrorType, types::{QualifiedContractIdentifier, TypeSignature, TypeSignatureExt as _, Value}, {execute, ClarityVersion}, @@ -74,8 +71,8 @@ fn test_accept_options(#[case] version: ClarityVersion, #[case] epoch: StacksEpo Ok(Some(Value::Int(0))), Ok(Some(Value::Int(10))), Err(CheckErrors::TypeValueError( - TypeSignature::from_string("(optional int)", version, epoch), - Value::some(Value::Bool(true)).unwrap(), + Box::new(TypeSignature::from_string("(optional int)", version, epoch)), + Box::new(Value::some(Value::Bool(true)).unwrap()), ) .into()), ]; @@ -133,11 +130,11 @@ fn test_unwrap_ret() { execute(test1).unwrap_err(), ); assert_eq_err( - CheckErrors::ExpectedOptionalOrResponseValue(Value::Int(1)), + CheckErrors::ExpectedOptionalOrResponseValue(Box::new(Value::Int(1))), execute(test2).unwrap_err(), ); assert_eq_err( - CheckErrors::ExpectedResponseValue(Value::Int(1)), + CheckErrors::ExpectedResponseValue(Box::new(Value::Int(1))), execute(test3).unwrap_err(), ); assert_eq!(Ok(Some(Value::Int(1))), execute(test4)); @@ -210,7 +207,7 @@ fn test_recursive_panic(#[case] version: ClarityVersion, #[case] epoch: StacksEp epoch, ) .unwrap_err(); - assert!(matches!(err.err, ParseErrors::CircularReference(_))); + assert!(matches!(*err.err, ParseErrors::CircularReference(_))); } #[test] @@ -477,37 +474,19 @@ fn test_define_trait_arg_count() { // These errors are hit in the trait resolver, before reaching the type-checker match execute(test0).unwrap_err() { - Error::Runtime( - RuntimeErrorType::ASTError(ParseError { - err: ParseErrors::DefineTraitBadSignature, - pre_expressions: _, - diagnostic: _, - }), - _, - ) => (), + Error::Runtime(RuntimeErrorType::ASTError(parse_err), _) + if *parse_err.err == ParseErrors::DefineTraitBadSignature => {} e => panic!("{e:?}"), }; match execute(test1).unwrap_err() { - Error::Runtime( - RuntimeErrorType::ASTError(ParseError { - err: ParseErrors::DefineTraitBadSignature, - pre_expressions: _, - diagnostic: _, - }), - _, - ) => (), + Error::Runtime(RuntimeErrorType::ASTError(parse_err), _) + if *parse_err.err == ParseErrors::DefineTraitBadSignature => {} e => panic!("{e}"), }; execute(test2).unwrap(); match execute(test3).unwrap_err() { - Error::Runtime( - RuntimeErrorType::ASTError(ParseError { - err: ParseErrors::DefineTraitBadSignature, - pre_expressions: _, - diagnostic: _, - }), - _, - ) => (), + Error::Runtime(RuntimeErrorType::ASTError(parse_err), _) + if *parse_err.err == ParseErrors::DefineTraitBadSignature => {} e => panic!("{e}"), }; } @@ -521,37 +500,19 @@ fn test_use_trait_arg_count() { // These errors are hit in the trait resolver, before reaching the type-checker match execute(test0).unwrap_err() { - Error::Runtime( - RuntimeErrorType::ASTError(ParseError { - err: ParseErrors::ImportTraitBadSignature, - pre_expressions: _, - diagnostic: _, - }), - _, - ) => (), + Error::Runtime(RuntimeErrorType::ASTError(parse_err), _) + if *parse_err.err == ParseErrors::ImportTraitBadSignature => {} e => panic!("{e:?}"), }; match execute(test1).unwrap_err() { - Error::Runtime( - RuntimeErrorType::ASTError(ParseError { - err: ParseErrors::ImportTraitBadSignature, - pre_expressions: _, - diagnostic: _, - }), - _, - ) => (), + Error::Runtime(RuntimeErrorType::ASTError(parse_err), _) + if *parse_err.err == ParseErrors::ImportTraitBadSignature => {} e => panic!("{e}"), }; execute(test2).unwrap(); match execute(test3).unwrap_err() { - Error::Runtime( - RuntimeErrorType::ASTError(ParseError { - err: ParseErrors::ImportTraitBadSignature, - pre_expressions: _, - diagnostic: _, - }), - _, - ) => (), + Error::Runtime(RuntimeErrorType::ASTError(parse_err), _) + if *parse_err.err == ParseErrors::ImportTraitBadSignature => {} e => panic!("{e}"), }; } @@ -564,26 +525,14 @@ fn test_impl_trait_arg_count() { // These errors are hit in the trait resolver, before reaching the type-checker match execute(test0).unwrap_err() { - Error::Runtime( - RuntimeErrorType::ASTError(ParseError { - err: ParseErrors::ImplTraitBadSignature, - pre_expressions: _, - diagnostic: _, - }), - _, - ) => (), + Error::Runtime(RuntimeErrorType::ASTError(parse_err), _) + if *parse_err.err == ParseErrors::ImplTraitBadSignature => {} e => panic!("{e:?}"), }; execute(test1).unwrap(); match execute(test2).unwrap_err() { - Error::Runtime( - RuntimeErrorType::ASTError(ParseError { - err: ParseErrors::ImplTraitBadSignature, - pre_expressions: _, - diagnostic: _, - }), - _, - ) => (), + Error::Runtime(RuntimeErrorType::ASTError(parse_err), _) + if *parse_err.err == ParseErrors::ImplTraitBadSignature => {} e => panic!("{e}"), }; } diff --git a/clarity/src/vm/tests/mod.rs b/clarity/src/vm/tests/mod.rs index 94a4f4312fa..a10aa7b1282 100644 --- a/clarity/src/vm/tests/mod.rs +++ b/clarity/src/vm/tests/mod.rs @@ -26,6 +26,8 @@ use crate::vm::database::MemoryBackingStore; mod assets; mod contracts; +#[cfg(test)] +mod conversions; mod datamaps; mod defines; mod principals; @@ -47,6 +49,15 @@ impl OwnedEnvironment<'_, '_> { .unwrap(); self.context.database.commit().unwrap(); } + + pub fn setup_block_metadata(&mut self, block_time: u64) { + self.context.database.begin(); + self.context + .database + .setup_block_metadata(Some(block_time)) + .unwrap(); + self.context.database.commit().unwrap(); + } } macro_rules! epochs_template { @@ -74,12 +85,12 @@ macro_rules! epochs_template { } macro_rules! clarity_template { - ($(($epoch:ident, $clarity:ident),)*) => { + ($($case_name:ident: ($epoch:ident, $clarity:ident),)*) => { #[template] #[export] #[rstest] $( - #[case::$epoch(ClarityVersion::$clarity, StacksEpochId::$epoch)] + #[case::$case_name(ClarityVersion::$clarity, StacksEpochId::$epoch)] )* pub fn test_clarity_versions(#[case] version: ClarityVersion, #[case] epoch: StacksEpochId) {} @@ -140,31 +151,31 @@ epochs_template! { } clarity_template! { - (Epoch20, Clarity1), - (Epoch2_05, Clarity1), - (Epoch21, Clarity1), - (Epoch21, Clarity2), - (Epoch22, Clarity1), - (Epoch22, Clarity2), - (Epoch23, Clarity1), - (Epoch23, Clarity2), - (Epoch24, Clarity1), - (Epoch24, Clarity2), - (Epoch25, Clarity1), - (Epoch25, Clarity2), - (Epoch30, Clarity1), - (Epoch30, Clarity2), - (Epoch30, Clarity3), - (Epoch31, Clarity1), - (Epoch31, Clarity2), - (Epoch31, Clarity3), - (Epoch32, Clarity1), - (Epoch32, Clarity2), - (Epoch32, Clarity3), - (Epoch33, Clarity1), - (Epoch33, Clarity2), - (Epoch33, Clarity3), - (Epoch33, Clarity4), + Epoch20_Clarity1: (Epoch20, Clarity1), + Epoch2_05_Clarity1: (Epoch2_05, Clarity1), + Epoch21_Clarity1: (Epoch21, Clarity1), + Epoch21_Clarity2: (Epoch21, Clarity2), + Epoch22_Clarity1: (Epoch22, Clarity1), + Epoch22_Clarity2: (Epoch22, Clarity2), + Epoch23_Clarity1: (Epoch23, Clarity1), + Epoch23_Clarity2: (Epoch23, Clarity2), + Epoch24_Clarity1: (Epoch24, Clarity1), + Epoch24_Clarity2: (Epoch24, Clarity2), + Epoch25_Clarity1: (Epoch25, Clarity1), + Epoch25_Clarity2: (Epoch25, Clarity2), + Epoch30_Clarity1: (Epoch30, Clarity1), + Epoch30_Clarity2: (Epoch30, Clarity2), + Epoch30_Clarity3: (Epoch30, Clarity3), + Epoch31_Clarity1: (Epoch31, Clarity1), + Epoch31_Clarity2: (Epoch31, Clarity2), + Epoch31_Clarity3: (Epoch31, Clarity3), + Epoch32_Clarity1: (Epoch32, Clarity1), + Epoch32_Clarity2: (Epoch32, Clarity2), + Epoch32_Clarity3: (Epoch32, Clarity3), + Epoch33_Clarity1: (Epoch33, Clarity1), + Epoch33_Clarity2: (Epoch33, Clarity2), + Epoch33_Clarity3: (Epoch33, Clarity3), + Epoch33_Clarity4: (Epoch33, Clarity4), } #[fixture] @@ -189,6 +200,11 @@ impl MemoryEnvironmentGenerator { db.set_tenure_height(1).unwrap(); db.commit().unwrap(); } + if epoch.uses_marfed_block_time() { + db.begin(); + db.setup_block_metadata(Some(1)).unwrap(); + db.commit().unwrap(); + } let mut owned_env = OwnedEnvironment::new(db, epoch); // start an initial transaction. owned_env.begin(); @@ -208,6 +224,11 @@ impl TopLevelMemoryEnvironmentGenerator { db.set_tenure_height(1).unwrap(); db.commit().unwrap(); } + if epoch.uses_marfed_block_time() { + db.begin(); + db.setup_block_metadata(Some(1)).unwrap(); + db.commit().unwrap(); + } OwnedEnvironment::new(db, epoch) } } diff --git a/clarity/src/vm/tests/principals.rs b/clarity/src/vm/tests/principals.rs index aec00b3173d..aefbd2cc7a3 100644 --- a/clarity/src/vm/tests/principals.rs +++ b/clarity/src/vm/tests/principals.rs @@ -27,7 +27,7 @@ fn test_simple_is_standard_check_inputs() { true ) .unwrap_err(), - CheckErrors::TypeValueError(PrincipalType, Value::UInt(10)).into() + CheckErrors::TypeValueError(Box::new(PrincipalType), Box::new(Value::UInt(10)),).into() ); } @@ -952,10 +952,10 @@ fn test_principal_construct_check_errors() { let input = r#"(principal-construct? 0x590493 0x0102030405060708091011121314151617181920)"#; assert_eq!( Err(CheckErrors::TypeValueError( - BUFF_1.clone(), - Value::Sequence(SequenceData::Buffer(BuffData { + Box::new(BUFF_1.clone()), + Box::new(Value::Sequence(SequenceData::Buffer(BuffData { data: hex_bytes("590493").unwrap() - })) + }))), ) .into()), execute_with_parameters( @@ -971,7 +971,10 @@ fn test_principal_construct_check_errors() { // `CheckErrors`. let input = r#"(principal-construct? u22 0x0102030405060708091011121314151617181920)"#; assert_eq!( - Err(CheckErrors::TypeValueError(BUFF_1.clone(), Value::UInt(22)).into()), + Err( + CheckErrors::TypeValueError(Box::new(BUFF_1.clone()), Box::new(Value::UInt(22)),) + .into() + ), execute_with_parameters( input, ClarityVersion::Clarity2, @@ -994,10 +997,10 @@ fn test_principal_construct_check_errors() { ) .unwrap_err(), CheckErrors::TypeValueError( - BUFF_20.clone(), - Value::Sequence(SequenceData::Buffer(BuffData { + Box::new(BUFF_20.clone()), + Box::new(Value::Sequence(SequenceData::Buffer(BuffData { data: hex_bytes("010203040506070809101112131415161718192021").unwrap() - })) + }))), ) .into() ); @@ -1006,12 +1009,14 @@ fn test_principal_construct_check_errors() { let input = r#"(principal-construct? 0x16 0x0102030405060708091011121314151617181920 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")"#; assert_eq!( Err(CheckErrors::TypeValueError( - TypeSignature::contract_name_string_ascii_type().unwrap(), - Value::Sequence(SequenceData::String(CharType::ASCII(ASCIIData { - data: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" - .as_bytes() - .to_vec() - }))) + Box::new(TypeSignature::contract_name_string_ascii_type().unwrap()), + Box::new(Value::Sequence(SequenceData::String(CharType::ASCII( + ASCIIData { + data: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + .as_bytes() + .to_vec() + } + )))) ) .into()), execute_with_parameters( diff --git a/clarity/src/vm/tests/sequences.rs b/clarity/src/vm/tests/sequences.rs index d89a2460b23..3ea779b7d67 100644 --- a/clarity/src/vm/tests/sequences.rs +++ b/clarity/src/vm/tests/sequences.rs @@ -112,18 +112,18 @@ fn test_index_of() { ]; let bad_expected = [ - CheckErrors::ExpectedSequence(TypeSignature::IntType), + CheckErrors::ExpectedSequence(Box::new(TypeSignature::IntType)), CheckErrors::TypeValueError( - TypeSignature::min_buffer().unwrap(), - execute("\"a\"").unwrap().unwrap(), + Box::new(TypeSignature::min_buffer().unwrap()), + Box::new(execute("\"a\"").unwrap().unwrap()), ), CheckErrors::TypeValueError( - TypeSignature::min_string_utf8().unwrap(), - execute("\"a\"").unwrap().unwrap(), + Box::new(TypeSignature::min_string_utf8().unwrap()), + Box::new(execute("\"a\"").unwrap().unwrap()), ), CheckErrors::TypeValueError( - TypeSignature::min_string_ascii().unwrap(), - execute("u\"a\"").unwrap().unwrap(), + Box::new(TypeSignature::min_string_ascii().unwrap()), + Box::new(execute("u\"a\"").unwrap().unwrap()), ), ]; @@ -173,8 +173,8 @@ fn test_element_at() { let bad = ["(element-at 3 u1)", "(element-at (list 1 2 3) 1)"]; let bad_expected = [ - CheckErrors::ExpectedSequence(TypeSignature::IntType), - CheckErrors::TypeValueError(TypeSignature::UIntType, Value::Int(1)), + CheckErrors::ExpectedSequence(Box::new(TypeSignature::IntType)), + CheckErrors::TypeValueError(Box::new(TypeSignature::UIntType), Box::new(Value::Int(1))), ]; for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { @@ -451,7 +451,7 @@ fn test_simple_map_append() { assert_eq!( execute("(append (append (list) 1) u2)").unwrap_err(), - CheckErrors::TypeValueError(IntType, Value::UInt(2)).into() + CheckErrors::TypeValueError(Box::new(IntType), Box::new(Value::UInt(2))).into() ); } @@ -587,7 +587,7 @@ fn test_simple_list_concat() { assert_eq!( execute("(concat (list 1) (list u4 u8))").unwrap_err(), - CheckErrors::TypeError(IntType, UIntType).into() + CheckErrors::TypeError(Box::new(IntType), Box::new(UIntType)).into() ); assert_eq!( @@ -710,25 +710,25 @@ fn test_simple_list_replace_at() { // The sequence input has the wrong type assert_eq!( execute_v2("(replace-at? 0 u0 (list 0))").unwrap_err(), - CheckErrors::ExpectedSequence(IntType).into() + CheckErrors::ExpectedSequence(Box::new(IntType)).into() ); // The type of the index should be uint. assert_eq!( execute_v2("(replace-at? (list 1) 0 0)").unwrap_err(), - CheckErrors::TypeValueError(UIntType, Value::Int(0)).into() + CheckErrors::TypeValueError(Box::new(UIntType), Box::new(Value::Int(0))).into() ); // The element input has the wrong type assert_eq!( execute_v2("(replace-at? (list 2 3) u0 true)").unwrap_err(), - CheckErrors::TypeValueError(IntType, Value::Bool(true)).into() + CheckErrors::TypeValueError(Box::new(IntType), Box::new(Value::Bool(true))).into() ); // The element input has the wrong type assert_eq!( execute_v2("(replace-at? (list 2 3) u0 0x00)").unwrap_err(), - CheckErrors::TypeValueError(IntType, Value::buff_from_byte(0)).into() + CheckErrors::TypeValueError(Box::new(IntType), Box::new(Value::buff_from_byte(0))).into() ); } @@ -768,29 +768,32 @@ fn test_simple_buff_replace_at() { // The sequence input has the wrong type assert_eq!( execute_v2("(replace-at? 33 u0 0x00)").unwrap_err(), - CheckErrors::ExpectedSequence(IntType).into() + CheckErrors::ExpectedSequence(Box::new(IntType)).into() ); // The type of the index should be uint. assert_eq!( execute_v2("(replace-at? 0x002244 0 0x99)").unwrap_err(), - CheckErrors::TypeValueError(UIntType, Value::Int(0)).into() + CheckErrors::TypeValueError(Box::new(UIntType), Box::new(Value::Int(0))).into() ); // The element input has the wrong type let buff_len = BufferLength::try_from(1u32).unwrap(); assert_eq!( execute_v2("(replace-at? 0x445522 u0 55)").unwrap_err(), - CheckErrors::TypeValueError(SequenceType(BufferType(buff_len.clone())), Value::Int(55)) - .into() + CheckErrors::TypeValueError( + Box::new(SequenceType(BufferType(buff_len.clone()))), + Box::new(Value::Int(55)) + ) + .into() ); // The element input has the wrong type assert_eq!( execute_v2("(replace-at? 0x445522 u0 (list 5))").unwrap_err(), CheckErrors::TypeValueError( - SequenceType(BufferType(buff_len.clone())), - Value::list_from(vec![Value::Int(5)]).unwrap() + Box::new(SequenceType(BufferType(buff_len.clone()))), + Box::new(Value::list_from(vec![Value::Int(5)]).unwrap()) ) .into() ); @@ -799,8 +802,8 @@ fn test_simple_buff_replace_at() { assert_eq!( execute_v2("(replace-at? 0x445522 u0 0x0044)").unwrap_err(), CheckErrors::TypeValueError( - SequenceType(BufferType(buff_len)), - Value::buff_from(vec![0, 68]).unwrap() + Box::new(SequenceType(BufferType(buff_len))), + Box::new(Value::buff_from(vec![0, 68]).unwrap()) ) .into() ); @@ -842,13 +845,13 @@ fn test_simple_string_ascii_replace_at() { // The sequence input has the wrong type assert_eq!( execute_v2("(replace-at? 33 u0 \"c\")").unwrap_err(), - CheckErrors::ExpectedSequence(IntType).into() + CheckErrors::ExpectedSequence(Box::new(IntType)).into() ); // The type of the index should be uint. assert_eq!( execute_v2("(replace-at? \"abc\" 0 \"c\")").unwrap_err(), - CheckErrors::TypeValueError(UIntType, Value::Int(0)).into() + CheckErrors::TypeValueError(Box::new(UIntType), Box::new(Value::Int(0))).into() ); // The element input has the wrong type @@ -856,8 +859,8 @@ fn test_simple_string_ascii_replace_at() { assert_eq!( execute_v2("(replace-at? \"abc\" u0 55)").unwrap_err(), CheckErrors::TypeValueError( - SequenceType(StringType(ASCII(buff_len.clone()))), - Value::Int(55) + Box::new(SequenceType(StringType(ASCII(buff_len.clone())))), + Box::new(Value::Int(55)) ) .into() ); @@ -866,8 +869,8 @@ fn test_simple_string_ascii_replace_at() { assert_eq!( execute_v2("(replace-at? \"abc\" u0 0x00)").unwrap_err(), CheckErrors::TypeValueError( - SequenceType(StringType(ASCII(buff_len.clone()))), - Value::buff_from_byte(0) + Box::new(SequenceType(StringType(ASCII(buff_len.clone())))), + Box::new(Value::buff_from_byte(0)) ) .into() ); @@ -876,8 +879,8 @@ fn test_simple_string_ascii_replace_at() { assert_eq!( execute_v2("(replace-at? \"abc\" u0 \"de\")").unwrap_err(), CheckErrors::TypeValueError( - SequenceType(StringType(ASCII(buff_len))), - Value::string_ascii_from_bytes("de".into()).unwrap() + Box::new(SequenceType(StringType(ASCII(buff_len)))), + Box::new(Value::string_ascii_from_bytes("de".into()).unwrap()) ) .into() ); @@ -923,13 +926,13 @@ fn test_simple_string_utf8_replace_at() { // The sequence input has the wrong type assert_eq!( execute_v2("(replace-at? 33 u0 u\"c\")").unwrap_err(), - CheckErrors::ExpectedSequence(IntType).into() + CheckErrors::ExpectedSequence(Box::new(IntType)).into() ); // The type of the index should be uint. assert_eq!( execute_v2("(replace-at? u\"abc\" 0 u\"c\")").unwrap_err(), - CheckErrors::TypeValueError(UIntType, Value::Int(0)).into() + CheckErrors::TypeValueError(Box::new(UIntType), Box::new(Value::Int(0))).into() ); // The element input has the wrong type @@ -937,8 +940,10 @@ fn test_simple_string_utf8_replace_at() { assert_eq!( execute_v2("(replace-at? u\"abc\" u0 55)").unwrap_err(), CheckErrors::TypeValueError( - TypeSignature::SequenceType(StringType(StringSubtype::UTF8(str_len.clone()))), - Value::Int(55) + Box::new(TypeSignature::SequenceType(StringType( + StringSubtype::UTF8(str_len.clone()) + ))), + Box::new(Value::Int(55)) ) .into() ); @@ -947,8 +952,10 @@ fn test_simple_string_utf8_replace_at() { assert_eq!( execute_v2("(replace-at? u\"abc\" u0 0x00)").unwrap_err(), CheckErrors::TypeValueError( - TypeSignature::SequenceType(StringType(StringSubtype::UTF8(str_len.clone()))), - Value::buff_from_byte(0) + Box::new(TypeSignature::SequenceType(StringType( + StringSubtype::UTF8(str_len.clone()) + ))), + Box::new(Value::buff_from_byte(0)) ) .into() ); @@ -957,8 +964,10 @@ fn test_simple_string_utf8_replace_at() { assert_eq!( execute_v2("(replace-at? u\"abc\" u0 u\"de\")").unwrap_err(), CheckErrors::TypeValueError( - TypeSignature::SequenceType(StringType(StringSubtype::UTF8(str_len))), - Value::string_utf8_from_string_utf8_literal("de".to_string()).unwrap() + Box::new(TypeSignature::SequenceType(StringType( + StringSubtype::UTF8(str_len) + ))), + Box::new(Value::string_utf8_from_string_utf8_literal("de".to_string()).unwrap()) ) .into() ); @@ -989,19 +998,21 @@ fn test_simple_buff_assert_max_len() { assert_eq!( execute("(as-max-len? 0x313233 3)").unwrap_err(), - CheckErrors::TypeError(UIntType, IntType).into() + CheckErrors::TypeError(Box::new(UIntType), Box::new(IntType)).into() ); assert_eq!( execute("(as-max-len? 1 u3)").unwrap_err(), - CheckErrors::ExpectedSequence(IntType).into() + CheckErrors::ExpectedSequence(Box::new(IntType)).into() ); assert_eq!( execute("(as-max-len? 0x313233 0x31)").unwrap_err(), CheckErrors::TypeError( - UIntType, - SequenceType(SequenceSubtype::BufferType(1_u32.try_into().unwrap())) + Box::new(UIntType), + Box::new(SequenceType(SequenceSubtype::BufferType( + 1_u32.try_into().unwrap() + ))) ) .into() ); @@ -1164,14 +1175,14 @@ fn test_construct_bad_list(#[case] version: ClarityVersion, #[case] epoch: Stack let test1 = "(list 1 2 3 true)"; assert_eq!( execute(test1).unwrap_err(), - CheckErrors::TypeError(IntType, BoolType).into() + CheckErrors::TypeError(Box::new(IntType), Box::new(BoolType)).into() ); let test2 = "(define-private (bad-function (x int)) (if (is-eq x 1) true x)) (map bad-function (list 0 1 2 3))"; assert_eq!( execute(test2).unwrap_err(), - CheckErrors::TypeError(IntType, BoolType).into() + CheckErrors::TypeError(Box::new(IntType), Box::new(BoolType)).into() ); let bad_2d_list = "(list (list 1 2 3) (list true false true))"; @@ -1179,13 +1190,13 @@ fn test_construct_bad_list(#[case] version: ClarityVersion, #[case] epoch: Stack assert_eq!( execute(bad_2d_list).unwrap_err(), - CheckErrors::TypeError(IntType, BoolType).into() + CheckErrors::TypeError(Box::new(IntType), Box::new(BoolType)).into() ); assert_eq!( execute(bad_high_order_list).unwrap_err(), CheckErrors::TypeError( - IntType, - TypeSignature::from_string("(list 3 int)", version, epoch) + Box::new(IntType), + Box::new(TypeSignature::from_string("(list 3 int)", version, epoch)) ) .into() ); @@ -1211,6 +1222,6 @@ fn test_eval_func_arg_panic() { assert_eq!(e, execute(test4).unwrap_err()); let test5 = "(map + (list 1 2 3 4) 2)"; - let e: Error = CheckErrors::ExpectedSequence(IntType).into(); + let e: Error = CheckErrors::ExpectedSequence(Box::new(IntType)).into(); assert_eq!(e, execute(test5).unwrap_err()); } diff --git a/clarity/src/vm/tests/simple_apply_eval.rs b/clarity/src/vm/tests/simple_apply_eval.rs index c4ca5781198..53558e3548f 100644 --- a/clarity/src/vm/tests/simple_apply_eval.rs +++ b/clarity/src/vm/tests/simple_apply_eval.rs @@ -581,14 +581,14 @@ fn test_secp256k1_errors() { ]; let expectations: &[Error] = &[ - CheckErrors::TypeValueError(BUFF_32.clone(), Value::Sequence(SequenceData::Buffer(BuffData { data: hex_bytes("de5b9eb9e7c5592930eb2e30a01369c36586d872082ed8181ee83d2a0ec20f").unwrap() }))).into(), - CheckErrors::TypeValueError(BUFF_65.clone(), Value::Sequence(SequenceData::Buffer(BuffData { data: hex_bytes("8738487ebe69b93d8e51583be8eee50bb4213fc49c767d329632730cc193b873554428fc936ca3569afc15f1c9365f6591d6251a89fee9c9ac661116824d3a130100").unwrap() }))).into(), + CheckErrors::TypeValueError(Box::new(BUFF_32.clone()), Box::new(Value::Sequence(SequenceData::Buffer(BuffData { data: hex_bytes("de5b9eb9e7c5592930eb2e30a01369c36586d872082ed8181ee83d2a0ec20f").unwrap() })))).into(), + CheckErrors::TypeValueError(Box::new(BUFF_65.clone()), Box::new(Value::Sequence(SequenceData::Buffer(BuffData { data: hex_bytes("8738487ebe69b93d8e51583be8eee50bb4213fc49c767d329632730cc193b873554428fc936ca3569afc15f1c9365f6591d6251a89fee9c9ac661116824d3a130100").unwrap() })))).into(), CheckErrors::IncorrectArgumentCount(2, 1).into(), CheckErrors::IncorrectArgumentCount(2, 3).into(), - CheckErrors::TypeValueError(BUFF_32.clone(), Value::Sequence(SequenceData::Buffer(BuffData { data: hex_bytes("de5b9eb9e7c5592930eb2e30a01369c36586d872082ed8181ee83d2a0ec20f").unwrap() }))).into(), - CheckErrors::TypeValueError(BUFF_65.clone(), Value::Sequence(SequenceData::Buffer(BuffData { data: hex_bytes("8738487ebe69b93d8e51583be8eee50bb4213fc49c767d329632730cc193b873554428fc936ca3569afc15f1c9365f6591d6251a89fee9c9ac661116824d3a130111").unwrap() }))).into(), - CheckErrors::TypeValueError(BUFF_33.clone(), Value::Sequence(SequenceData::Buffer(BuffData { data: hex_bytes("03adb8de4bfb65db2cfd6120d55c6526ae9c52e675db7e47308636534ba7").unwrap() }))).into(), + CheckErrors::TypeValueError(Box::new(BUFF_32.clone()), Box::new(Value::Sequence(SequenceData::Buffer(BuffData { data: hex_bytes("de5b9eb9e7c5592930eb2e30a01369c36586d872082ed8181ee83d2a0ec20f").unwrap() })))).into(), + CheckErrors::TypeValueError(Box::new(BUFF_65.clone()), Box::new(Value::Sequence(SequenceData::Buffer(BuffData { data: hex_bytes("8738487ebe69b93d8e51583be8eee50bb4213fc49c767d329632730cc193b873554428fc936ca3569afc15f1c9365f6591d6251a89fee9c9ac661116824d3a130111").unwrap() })))).into(), + CheckErrors::TypeValueError(Box::new(BUFF_33.clone()), Box::new(Value::Sequence(SequenceData::Buffer(BuffData { data: hex_bytes("03adb8de4bfb65db2cfd6120d55c6526ae9c52e675db7e47308636534ba7").unwrap() })))).into(), CheckErrors::IncorrectArgumentCount(3, 2).into(), CheckErrors::IncorrectArgumentCount(1, 2).into(), @@ -880,30 +880,38 @@ fn test_sequence_comparisons_clarity1() { let error_expectations: &[Error] = &[ CheckErrors::UnionTypeValueError( vec![TypeSignature::IntType, TypeSignature::UIntType], - Value::Sequence(SequenceData::String(CharType::ASCII(ASCIIData { - data: "baa".as_bytes().to_vec(), - }))), + Box::new(Value::Sequence(SequenceData::String(CharType::ASCII( + ASCIIData { + data: "baa".as_bytes().to_vec(), + }, + )))), ) .into(), CheckErrors::UnionTypeValueError( vec![TypeSignature::IntType, TypeSignature::UIntType], - Value::Sequence(SequenceData::String(CharType::ASCII(ASCIIData { - data: "baa".as_bytes().to_vec(), - }))), + Box::new(Value::Sequence(SequenceData::String(CharType::ASCII( + ASCIIData { + data: "baa".as_bytes().to_vec(), + }, + )))), ) .into(), CheckErrors::UnionTypeValueError( vec![TypeSignature::IntType, TypeSignature::UIntType], - Value::Sequence(SequenceData::String(CharType::ASCII(ASCIIData { - data: "baa".as_bytes().to_vec(), - }))), + Box::new(Value::Sequence(SequenceData::String(CharType::ASCII( + ASCIIData { + data: "baa".as_bytes().to_vec(), + }, + )))), ) .into(), CheckErrors::UnionTypeValueError( vec![TypeSignature::IntType, TypeSignature::UIntType], - Value::Sequence(SequenceData::String(CharType::ASCII(ASCIIData { - data: "baa".as_bytes().to_vec(), - }))), + Box::new(Value::Sequence(SequenceData::String(CharType::ASCII( + ASCIIData { + data: "baa".as_bytes().to_vec(), + }, + )))), ) .into(), ]; @@ -983,12 +991,12 @@ fn test_sequence_comparisons_mismatched_types() { let v1_error_expectations: &[Error] = &[ CheckErrors::UnionTypeValueError( vec![TypeSignature::IntType, TypeSignature::UIntType], - Value::Int(0), + Box::new(Value::Int(0)), ) .into(), CheckErrors::UnionTypeValueError( vec![TypeSignature::IntType, TypeSignature::UIntType], - Value::Int(0), + Box::new(Value::Int(0)), ) .into(), ]; @@ -1010,7 +1018,7 @@ fn test_sequence_comparisons_mismatched_types() { TypeSignature::max_string_utf8().unwrap(), TypeSignature::max_buffer().unwrap(), ], - Value::Int(0), + Box::new(Value::Int(0)), ) .into(), CheckErrors::UnionTypeValueError( @@ -1021,7 +1029,7 @@ fn test_sequence_comparisons_mismatched_types() { TypeSignature::max_string_utf8().unwrap(), TypeSignature::max_buffer().unwrap(), ], - Value::Int(0), + Box::new(Value::Int(0)), ) .into(), ]; @@ -1044,9 +1052,11 @@ fn test_sequence_comparisons_mismatched_types() { TypeSignature::max_string_utf8().unwrap(), TypeSignature::max_buffer().unwrap(), ], - Value::Sequence(SequenceData::String(CharType::ASCII(ASCIIData { - data: "baa".as_bytes().to_vec(), - }))), + Box::new(Value::Sequence(SequenceData::String(CharType::ASCII( + ASCIIData { + data: "baa".as_bytes().to_vec(), + }, + )))), ) .into(), CheckErrors::UnionTypeValueError( @@ -1057,9 +1067,11 @@ fn test_sequence_comparisons_mismatched_types() { TypeSignature::max_string_utf8().unwrap(), TypeSignature::max_buffer().unwrap(), ], - Value::Sequence(SequenceData::String(CharType::ASCII(ASCIIData { - data: "baa".as_bytes().to_vec(), - }))), + Box::new(Value::Sequence(SequenceData::String(CharType::ASCII( + ASCIIData { + data: "baa".as_bytes().to_vec(), + }, + )))), ) .into(), ]; @@ -1102,7 +1114,11 @@ fn test_simple_arithmetic_errors(#[case] version: ClarityVersion, #[case] epoch: let expectations: &[Error] = &[ CheckErrors::IncorrectArgumentCount(2, 1).into(), - CheckErrors::TypeValueError(TypeSignature::IntType, Value::Bool(true)).into(), + CheckErrors::TypeValueError( + Box::new(TypeSignature::IntType), + Box::new(Value::Bool(true)), + ) + .into(), RuntimeErrorType::DivisionByZero.into(), RuntimeErrorType::DivisionByZero.into(), RuntimeErrorType::ArithmeticOverflow.into(), @@ -1129,8 +1145,8 @@ fn test_simple_arithmetic_errors(#[case] version: ClarityVersion, #[case] epoch: ) .into(), CheckErrors::TypeError( - TypeSignature::from_string("bool", version, epoch), - TypeSignature::from_string("int", version, epoch), + Box::new(TypeSignature::from_string("bool", version, epoch)), + Box::new(TypeSignature::from_string("int", version, epoch)), ) .into(), ]; @@ -1156,10 +1172,11 @@ fn test_unsigned_arithmetic() { RuntimeErrorType::ArithmeticUnderflow.into(), CheckErrors::UnionTypeValueError( vec![TypeSignature::IntType, TypeSignature::UIntType], - Value::UInt(10), + Box::new(Value::UInt(10)), ) .into(), - CheckErrors::TypeValueError(TypeSignature::UIntType, Value::Int(80)).into(), + CheckErrors::TypeValueError(Box::new(TypeSignature::UIntType), Box::new(Value::Int(80))) + .into(), RuntimeErrorType::ArithmeticUnderflow.into(), RuntimeErrorType::ArithmeticOverflow.into(), ]; @@ -1191,20 +1208,20 @@ fn test_options_errors() { let expectations: &[Error] = &[ CheckErrors::IncorrectArgumentCount(1, 2).into(), - CheckErrors::ExpectedOptionalValue(Value::Bool(true)).into(), + CheckErrors::ExpectedOptionalValue(Box::new(Value::Bool(true))).into(), CheckErrors::IncorrectArgumentCount(1, 2).into(), - CheckErrors::ExpectedResponseValue(Value::Bool(true)).into(), + CheckErrors::ExpectedResponseValue(Box::new(Value::Bool(true))).into(), CheckErrors::IncorrectArgumentCount(1, 2).into(), - CheckErrors::ExpectedResponseValue(Value::Bool(true)).into(), + CheckErrors::ExpectedResponseValue(Box::new(Value::Bool(true))).into(), CheckErrors::IncorrectArgumentCount(1, 2).into(), - CheckErrors::ExpectedOptionalValue(Value::Bool(true)).into(), + CheckErrors::ExpectedOptionalValue(Box::new(Value::Bool(true))).into(), CheckErrors::IncorrectArgumentCount(1, 2).into(), CheckErrors::IncorrectArgumentCount(1, 2).into(), CheckErrors::IncorrectArgumentCount(1, 2).into(), CheckErrors::IncorrectArgumentCount(2, 3).into(), - CheckErrors::ExpectedOptionalValue(Value::Bool(true)).into(), - CheckErrors::ExpectedTuple(TypeSignature::IntType).into(), - CheckErrors::ExpectedTuple(TypeSignature::IntType).into(), + CheckErrors::ExpectedOptionalValue(Box::new(Value::Bool(true))).into(), + CheckErrors::ExpectedTuple(Box::new(TypeSignature::IntType)).into(), + CheckErrors::ExpectedTuple(Box::new(TypeSignature::IntType)).into(), ]; for (program, expectation) in tests.iter().zip(expectations.iter()) { @@ -1409,9 +1426,12 @@ fn test_option_destructs() { let expectations: &[Result] = &[ Ok(Value::Int(1)), Ok(Value::Int(1)), - Err(CheckErrors::ExpectedResponseValue(Value::some(Value::Int(2)).unwrap()).into()), + Err( + CheckErrors::ExpectedResponseValue(Box::new(Value::some(Value::Int(2)).unwrap())) + .into(), + ), Ok(Value::Int(3)), - Err(ShortReturnType::ExpectedValue(Value::Int(2)).into()), + Err(ShortReturnType::ExpectedValue(Box::new(Value::Int(2))).into()), Ok(Value::Int(3)), Ok(Value::Int(3)), Ok(Value::Int(3)), @@ -1422,14 +1442,14 @@ fn test_option_destructs() { Ok(Value::Int(9)), Ok(Value::Int(2)), Ok(Value::Int(8)), - Err(CheckErrors::BadMatchInput(TypeSignature::IntType).into()), - Err(CheckErrors::BadMatchInput(TypeSignature::IntType).into()), - Err(ShortReturnType::ExpectedValue(Value::error(Value::UInt(1)).unwrap()).into()), + Err(CheckErrors::BadMatchInput(Box::new(TypeSignature::IntType)).into()), + Err(CheckErrors::BadMatchInput(Box::new(TypeSignature::IntType)).into()), + Err(ShortReturnType::ExpectedValue(Box::new(Value::error(Value::UInt(1)).unwrap())).into()), Ok(Value::Int(3)), - Err(ShortReturnType::ExpectedValue(Value::none()).into()), + Err(ShortReturnType::ExpectedValue(Box::new(Value::none())).into()), Ok(Value::Bool(true)), Err(CheckErrors::IncorrectArgumentCount(1, 2).into()), - Err(CheckErrors::ExpectedOptionalOrResponseValue(Value::Int(1)).into()), + Err(CheckErrors::ExpectedOptionalOrResponseValue(Box::new(Value::Int(1))).into()), ]; for (program, expectation) in tests.iter().zip(expectations.iter()) { @@ -1462,7 +1482,7 @@ fn test_hash_errors() { TypeSignature::UIntType, TypeSignature::max_buffer().unwrap(), ], - Value::Bool(true), + Box::new(Value::Bool(true)), ) .into(), CheckErrors::UnionTypeValueError( @@ -1471,7 +1491,7 @@ fn test_hash_errors() { TypeSignature::UIntType, TypeSignature::max_buffer().unwrap(), ], - Value::Bool(true), + Box::new(Value::Bool(true)), ) .into(), CheckErrors::UnionTypeValueError( @@ -1480,7 +1500,7 @@ fn test_hash_errors() { TypeSignature::UIntType, TypeSignature::max_buffer().unwrap(), ], - Value::Bool(true), + Box::new(Value::Bool(true)), ) .into(), CheckErrors::UnionTypeValueError( @@ -1489,7 +1509,7 @@ fn test_hash_errors() { TypeSignature::UIntType, TypeSignature::max_buffer().unwrap(), ], - Value::Bool(true), + Box::new(Value::Bool(true)), ) .into(), CheckErrors::IncorrectArgumentCount(1, 2).into(), @@ -1499,7 +1519,7 @@ fn test_hash_errors() { TypeSignature::UIntType, TypeSignature::max_buffer().unwrap(), ], - Value::Bool(true), + Box::new(Value::Bool(true)), ) .into(), CheckErrors::IncorrectArgumentCount(1, 2).into(), @@ -1655,12 +1675,12 @@ fn test_asserts_short_circuit() { ]; let expectations: &[Error] = &[ - Error::ShortReturn(ShortReturnType::AssertionFailed( + Error::ShortReturn(ShortReturnType::AssertionFailed(Box::new( Value::error(Value::Int(0)).unwrap(), - )), - Error::ShortReturn(ShortReturnType::AssertionFailed( + ))), + Error::ShortReturn(ShortReturnType::AssertionFailed(Box::new( Value::error(Value::Int(1)).unwrap(), - )), + ))), ]; tests diff --git a/clarity/src/vm/tests/variables.rs b/clarity/src/vm/tests/variables.rs index c3652d97f0c..f1a0f16e130 100644 --- a/clarity/src/vm/tests/variables.rs +++ b/clarity/src/vm/tests/variables.rs @@ -27,7 +27,7 @@ use crate::vm::{ database::MemoryBackingStore, errors::{CheckErrors, Error}, tests::{tl_env_factory, TopLevelMemoryEnvironmentGenerator}, - types::{QualifiedContractIdentifier, Value}, + types::{PrincipalData, QualifiedContractIdentifier, Value}, ClarityVersion, ContractContext, }; @@ -55,7 +55,7 @@ fn test_block_height( let err = analysis.unwrap_err(); assert_eq!( CheckErrors::UndefinedVariable("block-height".to_string()), - err.err + *err.err ); } else { assert!(analysis.is_ok()); @@ -113,7 +113,7 @@ fn test_stacks_block_height( let err = analysis.unwrap_err(); assert_eq!( CheckErrors::UndefinedVariable("stacks-block-height".to_string()), - err.err + *err.err ); } else { assert!(analysis.is_ok()); @@ -173,7 +173,7 @@ fn test_tenure_height( let err = analysis.unwrap_err(); assert_eq!( CheckErrors::UndefinedVariable("tenure-height".to_string()), - err.err + *err.err ); } else { assert!(analysis.is_ok()); @@ -246,7 +246,7 @@ fn expect_contract_error( for (when, err_condition, expected_error) in expected_errors { if *when == WhenError::Analysis && err_condition(version, epoch) { let err = analysis.unwrap_err(); - assert_eq!(*expected_error, err.err); + assert_eq!(*expected_error, *err.err); // Do not continue with the test if the analysis failed. return; @@ -849,8 +849,10 @@ fn reuse_stacks_block_height( ); } -#[apply(test_clarity_versions)] -fn reuse_tenure_height( +#[cfg(test)] +fn reuse_builtin_name( + name: &str, + version_check: fn(ClarityVersion, StacksEpochId) -> bool, version: ClarityVersion, epoch: StacksEpochId, mut tl_env_factory: TopLevelMemoryEnvironmentGenerator, @@ -861,16 +863,18 @@ fn reuse_tenure_height( epoch, &mut tl_env_factory, "data-var", - r#" - (define-data-var tenure-height uint u1234) + &format!( + r#" + (define-data-var {name} uint u1234) (define-read-only (test-func) - (var-get tenure-height) + (var-get {name}) ) - "#, + "# + ), &[( WhenError::Initialization, - |version, _| version >= ClarityVersion::Clarity3, - CheckErrors::NameAlreadyUsed("tenure-height".to_string()), + version_check, + CheckErrors::NameAlreadyUsed(name.to_string()), )], Value::UInt(1234), ); @@ -881,16 +885,18 @@ fn reuse_tenure_height( epoch, &mut tl_env_factory, "map", - r#" - (define-map tenure-height uint uint) + &format!( + r#" + (define-map {name} uint uint) (define-private (test-func) - (map-insert tenure-height u1 u2) + (map-insert {name} u1 u2) ) - "#, + "# + ), &[( WhenError::Initialization, - |version, _| version >= ClarityVersion::Clarity3, - CheckErrors::NameAlreadyUsed("tenure-height".to_string()), + version_check, + CheckErrors::NameAlreadyUsed(name.to_string()), )], Value::Bool(true), ); @@ -901,17 +907,19 @@ fn reuse_tenure_height( epoch, &mut tl_env_factory, "let", - r#" + &format!( + r#" (define-private (test-func) - (let ((tenure-height 32)) - tenure-height + (let (({name} 32)) + {name} ) ) - "#, + "# + ), &[( WhenError::Runtime, - |version, _| version >= ClarityVersion::Clarity3, - CheckErrors::NameAlreadyUsed("tenure-height".to_string()), + version_check, + CheckErrors::NameAlreadyUsed(name.to_string()), )], Value::Int(32), ); @@ -922,20 +930,22 @@ fn reuse_tenure_height( epoch, &mut tl_env_factory, "match-binding", - r#" + &format!( + r#" (define-read-only (test-func) (let ((x (if true (ok u5) (err u7)))) (match x - tenure-height 3 + {name} 3 e 4 ) ) ) - "#, + "# + ), &[( WhenError::Runtime, - |version, _| version >= ClarityVersion::Clarity3, - CheckErrors::NameAlreadyUsed("tenure-height".to_string()), + version_check, + CheckErrors::NameAlreadyUsed(name.to_string()), )], Value::Int(3), ); @@ -946,14 +956,16 @@ fn reuse_tenure_height( epoch, &mut tl_env_factory, "function", - r#" - (define-private (tenure-height) true) - (define-private (test-func) (tenure-height)) - "#, + &format!( + r#" + (define-private ({name}) true) + (define-private (test-func) ({name})) + "# + ), &[( WhenError::Initialization, - |version, _| version >= ClarityVersion::Clarity3, - CheckErrors::NameAlreadyUsed("tenure-height".to_string()), + version_check, + CheckErrors::NameAlreadyUsed(name.to_string()), )], Value::Bool(true), ); @@ -964,14 +976,16 @@ fn reuse_tenure_height( epoch, &mut tl_env_factory, "constant", - r#" - (define-constant tenure-height u1234) - (define-read-only (test-func) tenure-height) - "#, + &format!( + r#" + (define-constant {name} u1234) + (define-read-only (test-func) {name}) + "# + ), &[( WhenError::Initialization, - |version, _| version >= ClarityVersion::Clarity3, - CheckErrors::NameAlreadyUsed("tenure-height".to_string()), + version_check, + CheckErrors::NameAlreadyUsed(name.to_string()), )], Value::UInt(1234), ); @@ -982,14 +996,16 @@ fn reuse_tenure_height( epoch, &mut tl_env_factory, "trait", - r#" - (define-trait tenure-height ()) + &format!( + r#" + (define-trait {name} ()) (define-read-only (test-func) false) - "#, + "# + ), &[( WhenError::Initialization, - |version, _| version >= ClarityVersion::Clarity3, - CheckErrors::NameAlreadyUsed("tenure-height".to_string()), + version_check, + CheckErrors::NameAlreadyUsed(name.to_string()), )], Value::Bool(false), ); @@ -1000,11 +1016,13 @@ fn reuse_tenure_height( epoch, &mut tl_env_factory, "tuple", - r#" + &format!( + r#" (define-read-only (test-func) - (get tenure-height { tenure-height: 1234 }) + (get {name} {{ {name}: 1234 }}) ) - "#, + "# + ), &[], Value::Int(1234), ); @@ -1015,14 +1033,16 @@ fn reuse_tenure_height( epoch, &mut tl_env_factory, "trait", - r#" - (define-fungible-token tenure-height) + &format!( + r#" + (define-fungible-token {name}) (define-read-only (test-func) false) - "#, + "# + ), &[( WhenError::Initialization, - |version, _| version >= ClarityVersion::Clarity3, - CheckErrors::NameAlreadyUsed("tenure-height".to_string()), + version_check, + CheckErrors::NameAlreadyUsed(name.to_string()), )], Value::Bool(false), ); @@ -1033,14 +1053,16 @@ fn reuse_tenure_height( epoch, &mut tl_env_factory, "trait", - r#" - (define-non-fungible-token tenure-height uint) + &format!( + r#" + (define-non-fungible-token {name} uint) (define-read-only (test-func) false) - "#, + "# + ), &[( WhenError::Initialization, - |version, _| version >= ClarityVersion::Clarity3, - CheckErrors::NameAlreadyUsed("tenure-height".to_string()), + version_check, + CheckErrors::NameAlreadyUsed(name.to_string()), )], Value::Bool(false), ); @@ -1051,14 +1073,16 @@ fn reuse_tenure_height( epoch, &mut tl_env_factory, "function", - r#" - (define-public (tenure-height) (ok true)) - (define-private (test-func) (unwrap-panic (tenure-height))) - "#, + &format!( + r#" + (define-public ({name}) (ok true)) + (define-private (test-func) (unwrap-panic ({name}))) + "# + ), &[( WhenError::Initialization, - |version, _| version >= ClarityVersion::Clarity3, - CheckErrors::NameAlreadyUsed("tenure-height".to_string()), + version_check, + CheckErrors::NameAlreadyUsed(name.to_string()), )], Value::Bool(true), ); @@ -1069,15 +1093,230 @@ fn reuse_tenure_height( epoch, &mut tl_env_factory, "function", - r#" - (define-read-only (tenure-height) true) - (define-private (test-func) (tenure-height)) - "#, + &format!( + r#" + (define-read-only ({name}) true) + (define-private (test-func) ({name})) + "# + ), &[( WhenError::Initialization, - |version, _| version >= ClarityVersion::Clarity3, - CheckErrors::NameAlreadyUsed("tenure-height".to_string()), + version_check, + CheckErrors::NameAlreadyUsed(name.to_string()), )], Value::Bool(true), ); } + +#[apply(test_clarity_versions)] +fn test_block_time( + version: ClarityVersion, + epoch: StacksEpochId, + mut tl_env_factory: TopLevelMemoryEnvironmentGenerator, +) { + let contract = "(define-read-only (test-func) block-time)"; + + let placeholder_context = + ContractContext::new(QualifiedContractIdentifier::transient(), version); + + let mut owned_env = tl_env_factory.get_env(epoch); + let contract_identifier = QualifiedContractIdentifier::local("test-contract").unwrap(); + + let mut exprs = parse(&contract_identifier, contract, version, epoch).unwrap(); + let mut marf = MemoryBackingStore::new(); + let mut db = marf.as_analysis_db(); + let analysis = db.execute(|db| { + type_check_version(&contract_identifier, &mut exprs, db, true, epoch, version) + }); + + // block-time should only be available in Clarity 4 + if version < ClarityVersion::Clarity4 { + let err = analysis.unwrap_err(); + assert_eq!( + CheckErrors::UndefinedVariable("block-time".to_string()), + *err.err + ); + } else { + assert!(analysis.is_ok()); + } + + // Initialize the contract + // Note that we're ignoring the analysis failure here so that we can test + // the runtime behavior. In earlier versions, if this case somehow gets past the + // analysis, it should fail at runtime. + let result = owned_env.initialize_versioned_contract( + contract_identifier.clone(), + version, + contract, + None, + ASTRules::PrecheckSize, + ); + + let mut env = owned_env.get_exec_environment(None, None, &placeholder_context); + + // Call the function + let eval_result = env.eval_read_only(&contract_identifier, "(test-func)"); + + // In versions before Clarity 4, this should trigger a runtime error + if version < ClarityVersion::Clarity4 { + let err = eval_result.unwrap_err(); + assert_eq!( + Error::Unchecked(CheckErrors::UndefinedVariable("block-time".to_string(),)), + err + ); + } else { + // Always 1 in the testing environment + assert_eq!(Ok(Value::UInt(1)), eval_result); + } +} + +#[test] +fn test_block_time_in_expressions() { + let version = ClarityVersion::Clarity4; + let epoch = StacksEpochId::Epoch33; + let mut tl_env_factory = tl_env_factory(); + + let contract = r#" + (define-read-only (time-comparison (threshold uint)) + (>= block-time threshold)) + (define-read-only (time-arithmetic) + (+ block-time u100)) + (define-read-only (time-in-response) + (ok block-time)) + "#; + + let placeholder_context = + ContractContext::new(QualifiedContractIdentifier::transient(), version); + + let mut owned_env = tl_env_factory.get_env(epoch); + let contract_identifier = QualifiedContractIdentifier::local("test-contract").unwrap(); + + // Initialize the contract + let result = owned_env.initialize_versioned_contract( + contract_identifier.clone(), + version, + contract, + None, + ASTRules::PrecheckSize, + ); + assert!(result.is_ok()); + + let mut env = owned_env.get_exec_environment(None, None, &placeholder_context); + + // Test comparison: 1 >= 0 should be true + let eval_result = env.eval_read_only(&contract_identifier, "(time-comparison u0)"); + info!("time-comparison result: {:?}", eval_result); + assert_eq!(Ok(Value::Bool(true)), eval_result); + + // Test arithmetic: 1 + 100 = 101 + let eval_result = env.eval_read_only(&contract_identifier, "(time-arithmetic)"); + info!("time-arithmetic result: {:?}", eval_result); + assert_eq!(Ok(Value::UInt(101)), eval_result); + + // Test in response: (ok 1) + let eval_result = env.eval_read_only(&contract_identifier, "(time-in-response)"); + info!("time-in-response result: {:?}", eval_result); + assert_eq!(Ok(Value::okay(Value::UInt(1)).unwrap()), eval_result); +} + +#[apply(test_clarity_versions)] +fn reuse_tenure_height( + version: ClarityVersion, + epoch: StacksEpochId, + tl_env_factory: TopLevelMemoryEnvironmentGenerator, +) { + fn version_check(version: ClarityVersion, _epoch: StacksEpochId) -> bool { + version >= ClarityVersion::Clarity3 + } + reuse_builtin_name( + "tenure-height", + version_check, + version, + epoch, + tl_env_factory, + ); +} + +#[apply(test_clarity_versions)] +fn test_current_contract( + version: ClarityVersion, + epoch: StacksEpochId, + mut tl_env_factory: TopLevelMemoryEnvironmentGenerator, +) { + let contract = "(define-read-only (test-func) current-contract)"; + + let placeholder_context = + ContractContext::new(QualifiedContractIdentifier::transient(), version); + + let mut owned_env = tl_env_factory.get_env(epoch); + let contract_identifier = QualifiedContractIdentifier::local("test-contract").unwrap(); + + let mut exprs = parse(&contract_identifier, contract, version, epoch).unwrap(); + let mut marf = MemoryBackingStore::new(); + let mut db = marf.as_analysis_db(); + let analysis = db.execute(|db| { + type_check_version(&contract_identifier, &mut exprs, db, true, epoch, version) + }); + if version < ClarityVersion::Clarity4 { + let err = analysis.unwrap_err(); + assert_eq!( + CheckErrors::UndefinedVariable("current-contract".to_string()), + *err.err + ); + } else { + assert!(analysis.is_ok()); + } + + // Initialize the contract + // Note that we're ignoring the analysis failure here so that we can test + // the runtime behavior. In Clarity 3, if this case somehow gets past the + // analysis, it should fail at runtime. + let result = owned_env.initialize_versioned_contract( + contract_identifier.clone(), + version, + contract, + None, + ASTRules::PrecheckSize, + ); + + let mut env = owned_env.get_exec_environment(None, None, &placeholder_context); + + // Call the function + let eval_result = env.eval_read_only(&contract_identifier, "(test-func)"); + // In Clarity 3, this should trigger a runtime error + if version < ClarityVersion::Clarity4 { + let err = eval_result.unwrap_err(); + assert_eq!( + Error::Unchecked(CheckErrors::UndefinedVariable( + "current-contract".to_string(), + )), + err + ); + } else { + assert_eq!( + Ok(Value::Principal(PrincipalData::Contract( + contract_identifier + ))), + eval_result + ); + } +} + +/// Test the checks on reuse of the `current-contract` name +#[apply(test_clarity_versions)] +fn reuse_current_contract( + version: ClarityVersion, + epoch: StacksEpochId, + tl_env_factory: TopLevelMemoryEnvironmentGenerator, +) { + fn version_check(version: ClarityVersion, _epoch: StacksEpochId) -> bool { + version >= ClarityVersion::Clarity4 + } + reuse_builtin_name( + "current-contract", + version_check, + version, + epoch, + tl_env_factory, + ); +} diff --git a/clarity/src/vm/tooling/mod.rs b/clarity/src/vm/tooling/mod.rs index 0713d4576f0..5b0a7a01e1c 100644 --- a/clarity/src/vm/tooling/mod.rs +++ b/clarity/src/vm/tooling/mod.rs @@ -3,7 +3,7 @@ use stacks_common::types::StacksEpochId; use super::analysis::ContractAnalysis; use super::types::TypeSignature; use super::ClarityVersion; -use crate::vm::analysis::{run_analysis, CheckResult}; +use crate::vm::analysis::{run_analysis, CheckError}; use crate::vm::ast::{build_ast_with_rules, ASTRules}; use crate::vm::costs::LimitedCostTracker; use crate::vm::database::MemoryBackingStore; @@ -14,7 +14,7 @@ pub fn mem_type_check( snippet: &str, version: ClarityVersion, epoch: StacksEpochId, -) -> CheckResult<(Option, ContractAnalysis)> { +) -> Result<(Option, ContractAnalysis), CheckError> { let contract_identifier = QualifiedContractIdentifier::transient(); let contract = build_ast_with_rules( &contract_identifier, @@ -50,6 +50,6 @@ pub fn mem_type_check( .cloned(); Ok((first_type, x)) } - Err((e, _)) => Err(e), + Err(e) => Err(e.0), } } diff --git a/clarity/src/vm/types/mod.rs b/clarity/src/vm/types/mod.rs index daa1ebc7168..5d18da01fa5 100644 --- a/clarity/src/vm/types/mod.rs +++ b/clarity/src/vm/types/mod.rs @@ -19,7 +19,7 @@ pub mod signatures; use std::str; -pub use clarity_serialization::types::{ +pub use clarity_types::types::{ byte_len_of_serialization, ASCIIData, BuffData, CallableData, CharType, ContractIdentifier, ListData, OptionalData, PrincipalData, QualifiedContractIdentifier, ResponseData, SequenceData, SequencedValue, StacksAddressExtensions, TraitIdentifier, TupleData, UTF8Data, Value, @@ -38,7 +38,7 @@ pub use crate::vm::types::signatures::{ use crate::vm::ClarityVersion; mod std_principals { - pub use clarity_serialization::types::StandardPrincipalData; + pub use clarity_types::types::StandardPrincipalData; } // Properties for "get-block-info". diff --git a/clarity/src/vm/types/serialization.rs b/clarity/src/vm/types/serialization.rs index 5989dca9df6..16e62f24099 100644 --- a/clarity/src/vm/types/serialization.rs +++ b/clarity/src/vm/types/serialization.rs @@ -16,7 +16,7 @@ use std::str; -pub use clarity_serialization::types::serialization::{ +pub use clarity_types::types::serialization::{ SerializationError, TypePrefix, NONE_SERIALIZATION_LEN, }; use stacks_common::util::hash::{hex_bytes, to_hex}; diff --git a/clarity/src/vm/types/signatures.rs b/clarity/src/vm/types/signatures.rs index f44758f7d9a..09e0c74be43 100644 --- a/clarity/src/vm/types/signatures.rs +++ b/clarity/src/vm/types/signatures.rs @@ -17,24 +17,22 @@ use std::collections::BTreeMap; use std::fmt; -pub use clarity_serialization::types::signatures::{ +pub use clarity_types::types::signatures::{ AssetIdentifier, BufferLength, CallableSubtype, ListTypeData, SequenceSubtype, StringSubtype, StringUTF8Length, TupleTypeSignature, TypeSignature, ASCII_40, BUFF_1, BUFF_16, BUFF_20, - BUFF_21, BUFF_32, BUFF_33, BUFF_64, BUFF_65, UTF8_40, + BUFF_21, BUFF_32, BUFF_33, BUFF_64, BUFF_65, MAX_TO_ASCII_BUFFER_LEN, TO_ASCII_MAX_BUFF, + TO_ASCII_RESPONSE_STRING, UTF8_40, }; -pub use clarity_serialization::types::Value; +pub use clarity_types::types::Value; use stacks_common::types::StacksEpochId; +use self::TypeSignature::SequenceType; use crate::vm::costs::{runtime_cost, CostOverflowingMath}; use crate::vm::errors::{CheckErrors, SyntaxBindingError, SyntaxBindingErrorType}; use crate::vm::representations::{ ClarityName, SymbolicExpression, SymbolicExpressionType, TraitDefinition, }; -type Result = std::result::Result; - -use self::TypeSignature::SequenceType; - #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct FunctionSignature { pub args: Vec, @@ -163,50 +161,55 @@ impl From for FunctionSignature { } /// This trait is used to parse type signatures from Clarity expressions. -/// This is not included in clarity-serialization because it requires the +/// This is not included in clarity-types because it requires the /// [`CostTracker`] trait. pub trait TypeSignatureExt { - fn parse_atom_type(typename: &str) -> Result; + fn parse_atom_type(typename: &str) -> Result; fn parse_list_type_repr( epoch: StacksEpochId, type_args: &[SymbolicExpression], accounting: &mut A, - ) -> Result; + ) -> Result; fn parse_tuple_type_repr( epoch: StacksEpochId, type_args: &[SymbolicExpression], accounting: &mut A, - ) -> Result; - fn parse_buff_type_repr(type_args: &[SymbolicExpression]) -> Result; - fn parse_string_utf8_type_repr(type_args: &[SymbolicExpression]) -> Result; - fn parse_string_ascii_type_repr(type_args: &[SymbolicExpression]) -> Result; + ) -> Result; + fn parse_buff_type_repr(type_args: &[SymbolicExpression]) + -> Result; + fn parse_string_utf8_type_repr( + type_args: &[SymbolicExpression], + ) -> Result; + fn parse_string_ascii_type_repr( + type_args: &[SymbolicExpression], + ) -> Result; fn parse_optional_type_repr( epoch: StacksEpochId, type_args: &[SymbolicExpression], accounting: &mut A, - ) -> Result; + ) -> Result; fn parse_response_type_repr( epoch: StacksEpochId, type_args: &[SymbolicExpression], accounting: &mut A, - ) -> Result; + ) -> Result; fn parse_type_repr( epoch: StacksEpochId, x: &SymbolicExpression, accounting: &mut A, - ) -> Result; + ) -> Result; fn parse_trait_type_repr( type_args: &[SymbolicExpression], accounting: &mut A, epoch: StacksEpochId, clarity_version: ClarityVersion, - ) -> Result>; + ) -> Result, CheckErrors>; #[cfg(test)] fn from_string(val: &str, version: ClarityVersion, epoch: StacksEpochId) -> Self; } impl TypeSignatureExt for TypeSignature { - fn parse_atom_type(typename: &str) -> Result { + fn parse_atom_type(typename: &str) -> Result { match typename { "int" => Ok(TypeSignature::IntType), "uint" => Ok(TypeSignature::UIntType), @@ -222,7 +225,7 @@ impl TypeSignatureExt for TypeSignature { epoch: StacksEpochId, type_args: &[SymbolicExpression], accounting: &mut A, - ) -> Result { + ) -> Result { if type_args.len() != 2 { return Err(CheckErrors::InvalidTypeDescription); } @@ -243,7 +246,7 @@ impl TypeSignatureExt for TypeSignature { epoch: StacksEpochId, type_args: &[SymbolicExpression], accounting: &mut A, - ) -> Result { + ) -> Result { let mapped_key_types = parse_name_type_pairs::<_, CheckErrors>( epoch, type_args, @@ -256,7 +259,9 @@ impl TypeSignatureExt for TypeSignature { // Parses type signatures of the form: // (buff 10) - fn parse_buff_type_repr(type_args: &[SymbolicExpression]) -> Result { + fn parse_buff_type_repr( + type_args: &[SymbolicExpression], + ) -> Result { if type_args.len() != 1 { return Err(CheckErrors::InvalidTypeDescription); } @@ -270,7 +275,9 @@ impl TypeSignatureExt for TypeSignature { // Parses type signatures of the form: // (string-utf8 10) - fn parse_string_utf8_type_repr(type_args: &[SymbolicExpression]) -> Result { + fn parse_string_utf8_type_repr( + type_args: &[SymbolicExpression], + ) -> Result { if type_args.len() != 1 { return Err(CheckErrors::InvalidTypeDescription); } @@ -285,7 +292,9 @@ impl TypeSignatureExt for TypeSignature { // Parses type signatures of the form: // (string-ascii 10) - fn parse_string_ascii_type_repr(type_args: &[SymbolicExpression]) -> Result { + fn parse_string_ascii_type_repr( + type_args: &[SymbolicExpression], + ) -> Result { if type_args.len() != 1 { return Err(CheckErrors::InvalidTypeDescription); } @@ -302,7 +311,7 @@ impl TypeSignatureExt for TypeSignature { epoch: StacksEpochId, type_args: &[SymbolicExpression], accounting: &mut A, - ) -> Result { + ) -> Result { if type_args.len() != 1 { return Err(CheckErrors::InvalidTypeDescription); } @@ -315,7 +324,7 @@ impl TypeSignatureExt for TypeSignature { epoch: StacksEpochId, type_args: &[SymbolicExpression], accounting: &mut A, - ) -> Result { + ) -> Result { if type_args.len() != 2 { return Err(CheckErrors::InvalidTypeDescription); } @@ -328,7 +337,7 @@ impl TypeSignatureExt for TypeSignature { epoch: StacksEpochId, x: &SymbolicExpression, accounting: &mut A, - ) -> Result { + ) -> Result { runtime_cost(ClarityCostFunction::TypeParseStep, accounting, 0)?; match x.expr { @@ -390,7 +399,7 @@ impl TypeSignatureExt for TypeSignature { accounting: &mut A, epoch: StacksEpochId, clarity_version: ClarityVersion, - ) -> Result> { + ) -> Result, CheckErrors> { let mut trait_signature: BTreeMap = BTreeMap::new(); let functions_types = type_args .first() @@ -418,7 +427,7 @@ impl TypeSignatureExt for TypeSignature { let fn_args = fn_args_exprs .iter() .map(|arg_type| TypeSignature::parse_type_repr(epoch, arg_type, accounting)) - .collect::>()?; + .collect::>()?; // Extract function's type return - must be a response let fn_return = match TypeSignature::parse_type_repr(epoch, &args[2], accounting) { @@ -448,7 +457,7 @@ impl TypeSignatureExt for TypeSignature { #[cfg(test)] fn from_string(val: &str, version: ClarityVersion, epoch: StacksEpochId) -> Self { - use clarity_serialization::types::QualifiedContractIdentifier; + use clarity_types::types::QualifiedContractIdentifier; use crate::vm::ast::parse; let expr = &parse( @@ -463,7 +472,7 @@ impl TypeSignatureExt for TypeSignature { } impl FixedFunction { - pub fn total_type_size(&self) -> Result { + pub fn total_type_size(&self) -> Result { let mut function_type_size = u64::from(self.returns.type_size()?); for arg in self.args.iter() { function_type_size = @@ -474,7 +483,7 @@ impl FixedFunction { } impl FunctionSignature { - pub fn total_type_size(&self) -> Result { + pub fn total_type_size(&self) -> Result { let mut function_type_size = u64::from(self.returns.type_size()?); for arg in self.args.iter() { function_type_size = @@ -487,7 +496,7 @@ impl FunctionSignature { &self, epoch: &StacksEpochId, args: Vec, - ) -> Result { + ) -> Result { if args.len() != self.args.len() { return Ok(false); } @@ -534,7 +543,7 @@ pub fn parse_name_type_pairs( name_type_pairs: &[SymbolicExpression], binding_error_type: SyntaxBindingErrorType, accounting: &mut A, -) -> std::result::Result, E> +) -> Result, E> where E: for<'a> From<(CheckErrors, &'a SymbolicExpression)>, { @@ -545,7 +554,7 @@ where use crate::vm::representations::SymbolicExpressionType::List; // step 1: parse it into a vec of symbolicexpression pairs. - let as_pairs: std::result::Result, (CheckErrors, &SymbolicExpression)> = name_type_pairs + let as_pairs: Result, (CheckErrors, &SymbolicExpression)> = name_type_pairs .iter() .enumerate() .map(|(i, key_type_pair)| { @@ -571,7 +580,7 @@ where .collect(); // step 2: turn into a vec of (name, typesignature) pairs. - let key_types: std::result::Result, (CheckErrors, &SymbolicExpression)> = (as_pairs?) + let key_types: Result, (CheckErrors, &SymbolicExpression)> = (as_pairs?) .iter() .enumerate() .map(|(i, (name_symbol, type_symbol))| { diff --git a/clarity/src/vm/variables.rs b/clarity/src/vm/variables.rs index 0846e14140f..cb84336dc99 100644 --- a/clarity/src/vm/variables.rs +++ b/clarity/src/vm/variables.rs @@ -14,6 +14,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +use clarity_types::types::PrincipalData; use stacks_common::types::StacksEpochId; use super::errors::InterpreterError; @@ -39,6 +40,8 @@ define_versioned_named_enum_with_max!(NativeVariables(ClarityVersion) { ChainId("chain-id", ClarityVersion::Clarity2, None), StacksBlockHeight("stacks-block-height", ClarityVersion::Clarity3, None), TenureHeight("tenure-height", ClarityVersion::Clarity3, None), + BlockTime("block-time", ClarityVersion::Clarity4, None), + CurrentContract("current-contract", ClarityVersion::Clarity4, None) }); pub fn is_reserved_name(name: &str, version: &ClarityVersion) -> bool { @@ -133,6 +136,15 @@ pub fn lookup_reserved_variable( let tenure_height = env.global_context.database.get_tenure_height()?; Ok(Some(Value::UInt(tenure_height as u128))) } + NativeVariables::CurrentContract => { + let contract = env.contract_context.contract_identifier.clone(); + Ok(Some(Value::Principal(PrincipalData::Contract(contract)))) + } + NativeVariables::BlockTime => { + runtime_cost(ClarityCostFunction::FetchVar, env, 1)?; + let block_time = env.global_context.database.get_current_block_time()?; + Ok(Some(Value::UInt(u128::from(block_time)))) + } } } else { Ok(None) diff --git a/contrib/nix/flake.nix b/contrib/nix/flake.nix index e038fa40587..d2505fee968 100644 --- a/contrib/nix/flake.nix +++ b/contrib/nix/flake.nix @@ -85,8 +85,8 @@ (lib.fileset.fileFilter (file: file.hasExt "clar") ../..) # (craneLib.fileset.commonCargoSources ../../clarity) - (craneLib.fileset.commonCargoSources ../../clarity-serialization) - ../../clarity-serialization/README.md + (craneLib.fileset.commonCargoSources ../../clarity-types) + ../../clarity-types/README.md (craneLib.fileset.commonCargoSources ../../libsigner) (craneLib.fileset.commonCargoSources ../../libstackerdb) (craneLib.fileset.commonCargoSources ../../pox-locking) diff --git a/docs/rpc/openapi.yaml b/docs/rpc/openapi.yaml index f33d22f6598..d1a4abcc852 100644 --- a/docs/rpc/openapi.yaml +++ b/docs/rpc/openapi.yaml @@ -1093,6 +1093,73 @@ paths: "500": $ref: "#/components/responses/InternalServerError" + /v3/tenures/blocks/hash/{burnchain_block_hash}: + get: + summary: Get the list of Nakamoto Stacks blocks in a tenure given Bitcoin block hash + tags: + - Blocks + security: [] + operationId: getTenureBlocksByHash + description: | + Get the list of Nakamoto blocks in a tenure given the Bitcoin block hash. The blocks will be + shown in order from highest to lowest. This is only for Nakamoto blocks, Epoch2 ones will not be shown. + parameters: + - name: burnchain_block_hash + in: path + description: The hex-encoded Bitcoin block hash of the tenure to query (64 hexadecimal characters, without 0x prefix) + required: true + schema: + type: string + pattern: "^[0-9a-f]{64}$" + responses: + "200": + description: List of Stacks blocks in the tenure + content: + application/json: + schema: + $ref: "#/components/schemas/TenureBlocks" + example: + $ref: "./components/examples/tenure-blocks.example.json" + "400": + $ref: "#/components/responses/BadRequest" + "404": + $ref: "#/components/responses/NotFound" + "500": + $ref: "#/components/responses/InternalServerError" + + /v3/tenures/blocks/height/{burnchain_block_height}: + get: + summary: Get the list of Nakamoto Stacks blocks in a tenure given Bitcoin block height + tags: + - Blocks + security: [] + operationId: getTenureBlocksByHeight + description: | + Get the list of Nakamoto blocks in a tenure given the Bitcoin block height. The blocks will be + shown in order from highest to lowest. This is only for Nakamoto blocks, Epoch2 ones will not be shown. + parameters: + - name: burnchain_block_height + in: path + description: The Bitcoin block height of the tenure to query + required: true + schema: + type: integer + responses: + "200": + description: List of Stacks blocks in the tenure + content: + application/json: + schema: + $ref: "#/components/schemas/TenureBlocks" + example: + $ref: "./components/examples/tenure-blocks.example.json" + "400": + $ref: "#/components/responses/BadRequest" + "404": + $ref: "#/components/responses/NotFound" + "500": + $ref: "#/components/responses/InternalServerError" + /v3/sortitions: get: summary: Get latest sortition information diff --git a/pox-locking/src/lib.rs b/pox-locking/src/lib.rs index 8336ee64b66..16c11c8af26 100644 --- a/pox-locking/src/lib.rs +++ b/pox-locking/src/lib.rs @@ -24,8 +24,6 @@ //! checks if the function called requires applying or updating the //! `STXBalance` struct's locks, and if the function was successfully //! invoked. If so, it updates the PoX lock. -#![allow(clippy::result_large_err)] - use clarity::boot_util::boot_code_id; use clarity::vm::contexts::GlobalContext; use clarity::vm::errors::{Error as ClarityError, RuntimeErrorType}; diff --git a/stacks-common/Cargo.toml b/stacks-common/Cargo.toml index 4b2c96d3bb0..8adaa41eeed 100644 --- a/stacks-common/Cargo.toml +++ b/stacks-common/Cargo.toml @@ -64,7 +64,7 @@ secp256k1 = { version = "0.24.3", default-features = false, features = ["std","s rusqlite = { workspace = true, optional = true } [target.'cfg(target_family = "wasm")'.dependencies] -libsecp256k1 = { version = "0.7.0", default-features = false, features = ["hmac"] } +libsecp256k1 = { version = "0.7.2", default-features = false, features = ["hmac", "lazy-static-context"] } [target.'cfg(all(any(target_arch = "x86_64", target_arch = "x86", target_arch = "aarch64"), not(any(target_os="windows"))))'.dependencies] sha2 = { version = "0.10", features = ["asm"] } diff --git a/stacks-common/src/types/mod.rs b/stacks-common/src/types/mod.rs index 0a7eeb0ae55..ffe1477d514 100644 --- a/stacks-common/src/types/mod.rs +++ b/stacks-common/src/types/mod.rs @@ -69,6 +69,12 @@ pub trait PublicKey: Clone + fmt::Debug + serde::Serialize + serde::de::Deserial pub trait PrivateKey: Clone + fmt::Debug + serde::Serialize + serde::de::DeserializeOwned { fn to_bytes(&self) -> Vec; fn sign(&self, data_hash: &[u8]) -> Result; + #[cfg(any(test, feature = "testing"))] + fn sign_with_noncedata( + &self, + data_hash: &[u8], + noncedata: &[u8; 32], + ) -> Result; } pub trait Address: Clone + fmt::Debug + fmt::Display { @@ -759,6 +765,23 @@ impl StacksEpochId { StacksEpochId::Epoch32 | StacksEpochId::Epoch33 => true, } } + + pub fn uses_marfed_block_time(&self) -> bool { + match self { + StacksEpochId::Epoch10 + | StacksEpochId::Epoch20 + | StacksEpochId::Epoch2_05 + | StacksEpochId::Epoch21 + | StacksEpochId::Epoch22 + | StacksEpochId::Epoch23 + | StacksEpochId::Epoch24 + | StacksEpochId::Epoch25 + | StacksEpochId::Epoch30 + | StacksEpochId::Epoch31 + | StacksEpochId::Epoch32 => false, + StacksEpochId::Epoch33 => true, + } + } } impl std::fmt::Display for StacksEpochId { diff --git a/stacks-common/src/util/secp256k1/native.rs b/stacks-common/src/util/secp256k1/native.rs index 75adb12d211..e6b7452061d 100644 --- a/stacks-common/src/util/secp256k1/native.rs +++ b/stacks-common/src/util/secp256k1/native.rs @@ -354,6 +354,22 @@ impl PrivateKey for Secp256k1PrivateKey { Ok(MessageSignature::from_secp256k1_recoverable(&sig)) }) } + + #[cfg(any(test, feature = "testing"))] + fn sign_with_noncedata( + &self, + data_hash: &[u8], + noncedata: &[u8; 32], + ) -> Result { + _secp256k1.with(|ctx| { + let msg = LibSecp256k1Message::from_slice(data_hash).map_err(|_e| { + "Invalid message: failed to decode data hash: must be a 32-byte hash" + })?; + + let sig = ctx.sign_ecdsa_recoverable_with_noncedata(&msg, &self.key, noncedata); + Ok(MessageSignature::from_secp256k1_recoverable(&sig)) + }) + } } fn secp256k1_pubkey_serialize( diff --git a/stacks-common/src/util/secp256k1/wasm.rs b/stacks-common/src/util/secp256k1/wasm.rs index c9c79a352e5..55b5266bfd5 100644 --- a/stacks-common/src/util/secp256k1/wasm.rs +++ b/stacks-common/src/util/secp256k1/wasm.rs @@ -15,12 +15,13 @@ // along with this program. If not, see . use ::libsecp256k1; +use ::libsecp256k1::curve::Scalar; pub use ::libsecp256k1::Error; #[cfg(not(feature = "wasm-deterministic"))] use ::libsecp256k1::{Error as LibSecp256k1Error, Message as LibSecp256k1Message}; use ::libsecp256k1::{ PublicKey as LibSecp256k1PublicKey, RecoveryId as LibSecp256k1RecoveryId, - SecretKey as LibSecp256k1PrivateKey, Signature as LibSecp256k1Signature, + SecretKey as LibSecp256k1PrivateKey, Signature as LibSecp256k1Signature, ECMULT_GEN_CONTEXT, }; use serde::de::{Deserialize, Error as de_Error}; use serde::Serialize; @@ -104,7 +105,8 @@ impl Secp256k1PublicKey { #[cfg(not(feature = "wasm-deterministic"))] pub fn from_private(privk: &Secp256k1PrivateKey) -> Secp256k1PublicKey { - let key = LibSecp256k1PublicKey::from_secret_key(&privk.key); + let key = + LibSecp256k1PublicKey::from_secret_key_with_context(&privk.key, &ECMULT_GEN_CONTEXT); Secp256k1PublicKey { key, compressed: privk.compress_public, @@ -339,4 +341,43 @@ impl PrivateKey for Secp256k1PrivateKey { let rec_sig = MessageSignature::from_secp256k1_recoverable(&sig, recid); Ok(rec_sig) } + + #[cfg(all(feature = "wasm-deterministic", any(test, feature = "testing")))] + fn sign_with_noncedata( + &self, + data_hash: &[u8], + noncedata: &[u8; 32], + ) -> Result { + Err("Not implemented for wasm-deterministic") + } + + #[cfg(all(any(test, feature = "testing"), not(feature = "wasm-deterministic")))] + fn sign_with_noncedata( + &self, + data_hash: &[u8], + noncedata: &[u8; 32], + ) -> Result { + let message = LibSecp256k1Message::parse_slice(data_hash) + .map_err(|_e| "Invalid message: failed to decode data hash: must be a 32-byte hash")?; + let mut nonce = Scalar::default(); + let _ = nonce.set_b32(&noncedata); + + // we need this as the key raw data are private + let mut key = Scalar::default(); + let _ = key.set_b32(&self.key.serialize()); + + let (sigr, sigs, recid) = match ECMULT_GEN_CONTEXT.sign_raw(&key, &message.0, &nonce) { + Ok(result) => result, + Err(_) => return Err("unable to sign message"), + }; + + let recid = match LibSecp256k1RecoveryId::parse(recid) { + Ok(recid) => recid, + Err(_) => return Err("invalid recovery id"), + }; + + let (sig, recid) = (LibSecp256k1Signature { r: sigr, s: sigs }, recid); + let rec_sig = MessageSignature::from_secp256k1_recoverable(&sig, recid); + Ok(rec_sig) + } } diff --git a/stacks-node/src/burnchains/bitcoin_regtest_controller.rs b/stacks-node/src/burnchains/bitcoin_regtest_controller.rs index 04bd2337fc9..5a54d6121c0 100644 --- a/stacks-node/src/burnchains/bitcoin_regtest_controller.rs +++ b/stacks-node/src/burnchains/bitcoin_regtest_controller.rs @@ -14,16 +14,12 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use std::io::Cursor; +use std::cmp; +use std::collections::HashSet; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; -use std::time::{Duration, Instant}; -use std::{cmp, io}; +use std::time::Instant; -use base64::encode; -use serde::Serialize; -use serde_json::json; -use serde_json::value::RawValue; use stacks::burnchains::bitcoin::address::{ BitcoinAddress, LegacyBitcoinAddress, LegacyBitcoinAddressType, SegwitBitcoinAddress, }; @@ -56,30 +52,25 @@ use stacks::config::{ }; use stacks::core::{EpochList, StacksEpochId}; use stacks::monitoring::{increment_btc_blocks_received_counter, increment_btc_ops_sent_counter}; -use stacks::net::http::{HttpRequestContents, HttpResponsePayload}; -use stacks::net::httpcore::{send_http_request, StacksHttpRequest}; -use stacks::net::Error as NetError; use stacks_common::codec::StacksMessageCodec; use stacks_common::deps_common::bitcoin::blockdata::opcodes; use stacks_common::deps_common::bitcoin::blockdata::script::{Builder, Script}; use stacks_common::deps_common::bitcoin::blockdata::transaction::{ OutPoint, Transaction, TxIn, TxOut, }; -use stacks_common::deps_common::bitcoin::network::encodable::ConsensusEncodable; -use stacks_common::deps_common::bitcoin::network::serialize::RawEncoder; +use stacks_common::deps_common::bitcoin::network::serialize::{serialize, serialize_hex}; use stacks_common::deps_common::bitcoin::util::hash::Sha256dHash; use stacks_common::types::chainstate::BurnchainHeaderHash; -use stacks_common::types::net::PeerHost; use stacks_common::util::hash::{hex_bytes, Hash160}; use stacks_common::util::secp256k1::Secp256k1PublicKey; use stacks_common::util::sleep_ms; -use url::Url; use super::super::operations::BurnchainOpSigner; use super::super::Config; use super::{BurnchainController, BurnchainTip, Error as BurnchainControllerError}; use crate::burnchains::rpc::bitcoin_rpc_client::{ - BitcoinRpcClient, BitcoinRpcClientError, ImportDescriptorsRequest, Timestamp, + BitcoinRpcClient, BitcoinRpcClientError, BitcoinRpcClientResult, ImportDescriptorsRequest, + Timestamp, }; /// The number of bitcoin blocks that can have @@ -665,97 +656,32 @@ impl BitcoinRegtestController { } } + /// Retrieves all UTXOs associated with the given public key. + /// + /// The address to query is computed from the public key, + /// disregard the epoch we're in and currently set to [`StacksEpochId::Epoch21`]. + /// + /// Automatically imports descriptors into the wallet for the public_key #[cfg(test)] pub fn get_all_utxos(&self, public_key: &Secp256k1PublicKey) -> Vec { - // Configure UTXO filter, disregard what epoch we're in - let address = self.get_miner_address(StacksEpochId::Epoch21, public_key); - let filter_addresses = vec![address.to_string()]; - - let pubk = if self.config.miner.segwit { - let mut p = public_key.clone(); - p.set_compressed(true); - p - } else { - public_key.clone() - }; - - test_debug!("Import public key '{}'", &pubk.to_hex()); - let result = self.import_public_key(&pubk); - if let Err(error) = result { - warn!("Import public key '{}' failed: {error:?}", &pubk.to_hex()); - } + const EPOCH: StacksEpochId = StacksEpochId::Epoch21; + let address = self.get_miner_address(EPOCH, public_key); + let pub_key_rev = self.to_epoch_aware_pubkey(EPOCH, public_key); + + test_debug!("Import public key '{}'", &pub_key_rev.to_hex()); + self.import_public_key(&pub_key_rev) + .unwrap_or_else(|error| { + panic!( + "Import public key '{}' failed: {error:?}", + pub_key_rev.to_hex() + ) + }); sleep_ms(1000); - let min_conf = 0i64; - let max_conf = 9999999i64; - let minimum_amount = ParsedUTXO::sat_to_serialized_btc(1); - - test_debug!("List unspent for '{address}' ('{}')", pubk.to_hex()); - let payload = BitcoinRPCRequest { - method: "listunspent".to_string(), - params: vec![ - min_conf.into(), - max_conf.into(), - filter_addresses.into(), - true.into(), - json!({ "minimumAmount": minimum_amount, "maximumCount": self.config.burnchain.max_unspent_utxos }), - ], - id: "stacks".to_string(), - jsonrpc: "2.0".to_string(), - }; - - let mut res = BitcoinRPCRequest::send(&self.config, payload).unwrap(); - let mut result_vec = vec![]; - - if let Some(ref mut object) = res.as_object_mut() { - match object.get_mut("result") { - Some(serde_json::Value::Array(entries)) => { - while let Some(entry) = entries.pop() { - let parsed_utxo: ParsedUTXO = match serde_json::from_value(entry) { - Ok(utxo) => utxo, - Err(err) => { - warn!("Failed parsing UTXO: {err}"); - continue; - } - }; - let amount = match parsed_utxo.get_sat_amount() { - Some(amount) => amount, - None => continue, - }; - - if amount < 1 { - continue; - } - - let script_pub_key = match parsed_utxo.get_script_pub_key() { - Some(script_pub_key) => script_pub_key, - None => { - continue; - } - }; - - let txid = match parsed_utxo.get_txid() { - Some(amount) => amount, - None => continue, - }; - - result_vec.push(UTXO { - txid, - vout: parsed_utxo.vout, - script_pub_key, - amount, - confirmations: parsed_utxo.confirmations, - }); - } - } - _ => { - warn!("Failed to get UTXOs"); - } - } - } - - result_vec + self.retrieve_utxo_set(&address, true, 1, &None, 0) + .unwrap_or_log_panic("retrieve all utxos") + .utxos } /// Retrieve all loaded wallets. @@ -782,23 +708,15 @@ impl BitcoinRegtestController { utxos_to_exclude: Option, block_height: u64, ) -> Option { - let pubk = if self.config.miner.segwit && epoch_id >= StacksEpochId::Epoch21 { - let mut p = public_key.clone(); - p.set_compressed(true); - p - } else { - public_key.clone() - }; + let pub_key_rev = self.to_epoch_aware_pubkey(epoch_id, public_key); // Configure UTXO filter - let address = self.get_miner_address(epoch_id, &pubk); - test_debug!("Get UTXOs for {} ({address})", pubk.to_hex()); - let filter_addresses = vec![address.to_string()]; + let address = self.get_miner_address(epoch_id, &pub_key_rev); + test_debug!("Get UTXOs for {} ({address})", pub_key_rev.to_hex()); let mut utxos = loop { - let result = BitcoinRPCRequest::list_unspent( - &self.config, - filter_addresses.clone(), + let result = self.retrieve_utxo_set( + &address, false, total_required, &utxos_to_exclude, @@ -826,16 +744,18 @@ impl BitcoinRegtestController { // Assuming that miners are in charge of correctly operating their bitcoind nodes sounds // reasonable to me. // $ bitcoin-cli importaddress mxVFsFW5N4mu1HPkxPttorvocvzeZ7KZyk - let result = self.import_public_key(&pubk); + let result = self.import_public_key(&pub_key_rev); if let Err(error) = result { - warn!("Import public key '{}' failed: {error:?}", &pubk.to_hex()); + warn!( + "Import public key '{}' failed: {error:?}", + &pub_key_rev.to_hex() + ); } sleep_ms(1000); } - let result = BitcoinRPCRequest::list_unspent( - &self.config, - filter_addresses.clone(), + let result = self.retrieve_utxo_set( + &address, false, total_required, &utxos_to_exclude, @@ -851,7 +771,7 @@ impl BitcoinRegtestController { } }; - test_debug!("Unspent for {filter_addresses:?}: {utxos:?}"); + test_debug!("Unspent for {address:?}: {utxos:?}"); if utxos.is_empty() { return None; @@ -860,7 +780,7 @@ impl BitcoinRegtestController { } } } else { - debug!("Got {} UTXOs for {filter_addresses:?}", utxos.utxos.len(),); + debug!("Got {} UTXOs for {address:?}", utxos.utxos.len(),); utxos }; @@ -868,7 +788,7 @@ impl BitcoinRegtestController { if total_unspent < total_required { warn!( "Total unspent {total_unspent} < {total_required} for {:?}", - &pubk.to_hex() + &pub_key_rev.to_hex() ); return None; } @@ -983,10 +903,7 @@ impl BitcoinRegtestController { self.build_transfer_stacks_tx(epoch_id, payload, op_signer, utxo) } }?; - - let ser_transaction = SerializedTx::new(transaction.clone()); - - self.send_transaction(ser_transaction).map(|_| transaction) + self.send_transaction(&transaction).map(|_| transaction) } #[cfg(test)] @@ -1511,16 +1428,13 @@ impl BitcoinRegtestController { signer, true, // block commit op requires change output to exist ); + debug!("Transaction relying on UTXOs: {utxos:?}"); - let serialized_tx = SerializedTx::new(tx.clone()); - - let tx_size = serialized_tx.bytes.len() as u64; + let serialized_tx = serialize(&tx).expect("BUG: failed to serialize to a vec"); + let tx_size = serialized_tx.len() as u64; estimated_fees.register_replacement(tx_size); - let mut txid = tx.txid().as_bytes().to_vec(); - txid.reverse(); - debug!("Transaction relying on UTXOs: {utxos:?}"); - let txid = Txid::from_bytes(&txid[..]).unwrap(); + let txid = Txid::from_bitcoin_tx_hash(&tx.txid()); let mut txids = previous_txids.to_vec(); txids.push(txid.clone()); let ongoing_block_commit = OngoingBlockCommit { @@ -1787,8 +1701,8 @@ impl BitcoinRegtestController { signer, force_change_output, ); - let serialized_tx = SerializedTx::new(tx_cloned); - cmp::max(min_tx_size, serialized_tx.bytes.len() as u64) + let serialized_tx = serialize(&tx_cloned).expect("BUG: failed to serialize to a vec"); + cmp::max(min_tx_size, serialized_tx.len() as u64) }; let rbf_fee = if spent_in_rbf == 0 { @@ -1928,18 +1842,30 @@ impl BitcoinRegtestController { true } - /// Send a serialized tx to the Bitcoin node. Return Some(txid) on successful send; None on - /// failure. - pub fn send_transaction( - &self, - transaction: SerializedTx, - ) -> Result { - debug!("Sending raw transaction: {}", transaction.to_hex()); + /// Broadcast a signed raw [`Transaction`] to the underlying Bitcoin node. + /// + /// The transaction is submitted with following parameters: + /// - `max_fee_rate = 0.0` (uncapped, accept any fee rate), + /// - `max_burn_amount = 1_000_000` (in sats). + /// + /// # Arguments + /// * `transaction` - A fully signed raw [`Transaction`] to broadcast. + /// + /// # Returns + /// On success, returns the [`Txid`] of the broadcasted transaction. + pub fn send_transaction(&self, tx: &Transaction) -> Result { + debug!( + "Sending raw transaction: {}", + serialize_hex(tx).unwrap_or("SERIALIZATION FAILED".to_string()) + ); - BitcoinRPCRequest::send_raw_transaction(&self.config, transaction.to_hex()) - .map(|_| { - debug!("Transaction {} sent successfully", &transaction.txid()); - transaction.txid() + const UNCAPPED_FEE: f64 = 0.0; + const MAX_BURN_AMOUNT: u64 = 1_000_000; + self.rpc_client + .send_raw_transaction(tx, Some(UNCAPPED_FEE), Some(MAX_BURN_AMOUNT)) + .map(|txid| { + debug!("Transaction {txid} sent successfully"); + txid }) .map_err(|e| { error!("Bitcoin RPC error: transaction submission failed - {e:?}"); @@ -2086,8 +2012,8 @@ impl BitcoinRegtestController { epoch_id: StacksEpochId, operation: BlockstackOperationType, op_signer: &mut BurnchainOpSigner, - ) -> Result { - let transaction = match operation { + ) -> Result { + match operation { BlockstackOperationType::LeaderBlockCommit(payload) => { self.build_leader_block_commit_tx(epoch_id, payload, op_signer) } @@ -2109,9 +2035,7 @@ impl BitcoinRegtestController { BlockstackOperationType::VoteForAggregateKey(payload) => { self.build_vote_for_aggregate_key_tx(epoch_id, payload, op_signer, None) } - }; - - transaction.map(SerializedTx::new) + } } /// Retrieves a raw [`Transaction`] by its [`Txid`] @@ -2247,6 +2171,97 @@ impl BitcoinRegtestController { } Ok(()) } + + /// Returns a copy of the given public key adjusted to the current epoch rules. + /// + /// In particular: + /// - For epochs **before** [`StacksEpochId::Epoch21`], the public key is returned + /// unchanged. + /// - Starting with [`StacksEpochId::Epoch21`], if **SegWit** is enabled in the miner + /// configuration, the key is forced into compressed form. + /// + /// # Arguments + /// * `epoch_id` — The epoch identifier to check against protocol upgrade rules. + /// * `public_key` — The original public key to adjust. + /// + /// # Returns + /// A [`Secp256k1PublicKey`] that is either the same as the input or compressed, + /// depending on the epoch and miner configuration. + fn to_epoch_aware_pubkey( + &self, + epoch_id: StacksEpochId, + public_key: &Secp256k1PublicKey, + ) -> Secp256k1PublicKey { + let mut reviewed = public_key.clone(); + if self.config.miner.segwit && epoch_id >= StacksEpochId::Epoch21 { + reviewed.set_compressed(true); + } + return reviewed; + } + + /// Retrieves the set of UTXOs for a given address at a specific block height. + /// + /// This method queries all unspent outputs belonging to the provided address: + /// 1. Using a confirmation window of `0..=9_999_999` for the RPC call. + /// 2. Filtering out UTXOs that: + /// - Are present in the optional exclusion set (matched by transaction ID). + /// - Have an amount below the specified `minimum_sum_amount`. + /// + /// Note: The `block_height` is only used to retrieve the corresponding block hash + /// and does not affect which UTXOs are included in the result. + /// + /// # Arguments + /// - `address`: The Bitcoin address whose UTXOs should be retrieved. + /// - `include_unsafe`: Whether to include unsafe UTXOs. + /// - `minimum_sum_amount`: Minimum amount (in satoshis) that a UTXO must have to be included in the final set. + /// - `utxos_to_exclude`: Optional set of UTXOs to exclude from the final result. + /// - `block_height`: The block height at which to resolve the block hash used in the result. + /// + /// # Returns + /// A [`UTXOSet`] containing the filtered UTXOs and the block hash corresponding to `block_height`. + fn retrieve_utxo_set( + &self, + address: &BitcoinAddress, + include_unsafe: bool, + minimum_sum_amount: u64, + utxos_to_exclude: &Option, + block_height: u64, + ) -> BitcoinRpcClientResult { + let bhh = self.rpc_client.get_block_hash(block_height)?; + + const MIN_CONFIRMATIONS: u64 = 0; + const MAX_CONFIRMATIONS: u64 = 9_999_999; + let unspents = self.rpc_client.list_unspent( + &self.get_wallet_name(), + Some(MIN_CONFIRMATIONS), + Some(MAX_CONFIRMATIONS), + Some(&[address]), + Some(include_unsafe), + Some(minimum_sum_amount), + self.config.burnchain.max_unspent_utxos.clone(), + )?; + + let txids_to_exclude = utxos_to_exclude.as_ref().map_or_else(HashSet::new, |set| { + set.utxos + .iter() + .map(|utxo| Txid::from_bitcoin_tx_hash(&utxo.txid)) + .collect() + }); + + let utxos = unspents + .into_iter() + .filter(|each| !txids_to_exclude.contains(&each.txid)) + .filter(|each| each.amount >= minimum_sum_amount) + .map(|each| UTXO { + txid: Txid::to_bitcoin_tx_hash(&each.txid), + vout: each.vout, + script_pub_key: each.script_pub_key, + amount: each.amount, + confirmations: each.confirmations, + }) + .collect::>(); + Ok(UTXOSet { bhh, utxos }) + } } impl BurnchainController for BitcoinRegtestController { @@ -2350,7 +2365,7 @@ impl BurnchainController for BitcoinRegtestController { op_signer: &mut BurnchainOpSigner, ) -> Result { let transaction = self.make_operation_tx(epoch_id, operation, op_signer)?; - self.send_transaction(transaction) + self.send_transaction(&transaction) } #[cfg(test)] @@ -2391,44 +2406,6 @@ impl UTXOSet { } } -#[derive(Debug, Clone)] -pub struct SerializedTx { - pub bytes: Vec, - pub txid: Txid, -} - -impl SerializedTx { - pub fn new(tx: Transaction) -> SerializedTx { - let txid = Txid::from_vec_be(tx.txid().as_bytes()).unwrap(); - let mut encoder = RawEncoder::new(Cursor::new(vec![])); - tx.consensus_encode(&mut encoder) - .expect("BUG: failed to serialize to a vec"); - let bytes: Vec = encoder.into_inner().into_inner(); - - SerializedTx { txid, bytes } - } - - pub fn txid(&self) -> Txid { - self.txid.clone() - } - - pub fn to_hex(&self) -> String { - let formatted_bytes: Vec = self.bytes.iter().map(|b| format!("{b:02x}")).collect(); - formatted_bytes.join("") - } -} - -#[derive(Debug, Clone, Deserialize)] -#[serde(rename_all = "camelCase")] -#[allow(dead_code)] -pub struct ParsedUTXO { - txid: String, - vout: u32, - script_pub_key: String, - amount: Box, - confirmations: u32, -} - #[derive(Clone, Debug, PartialEq)] pub struct UTXO { pub txid: Sha256dHash, @@ -2438,300 +2415,6 @@ pub struct UTXO { pub confirmations: u32, } -impl ParsedUTXO { - pub fn get_txid(&self) -> Option { - match hex_bytes(&self.txid) { - Ok(ref mut txid) => { - txid.reverse(); - Some(Sha256dHash::from(&txid[..])) - } - Err(err) => { - warn!("Unable to get txid from UTXO {err}"); - None - } - } - } - - pub fn get_sat_amount(&self) -> Option { - ParsedUTXO::serialized_btc_to_sat(self.amount.get()) - } - - pub fn serialized_btc_to_sat(amount: &str) -> Option { - let comps: Vec<&str> = amount.split('.').collect(); - match comps[..] { - [lhs, rhs] => { - if rhs.len() > 8 { - warn!("Unexpected amount of decimals"); - return None; - } - - match (lhs.parse::(), rhs.parse::()) { - (Ok(btc), Ok(frac_part)) => { - let base: u64 = 10; - let btc_to_sat = base.pow(8); - let mut amount = btc * btc_to_sat; - let sat = frac_part * base.pow(8 - rhs.len() as u32); - amount += sat; - Some(amount) - } - (lhs, rhs) => { - warn!("Error while converting BTC to sat {lhs:?} - {rhs:?}"); - None - } - } - } - _ => None, - } - } - - pub fn sat_to_serialized_btc(amount: u64) -> String { - let base: u64 = 10; - let int_part = amount / base.pow(8); - let frac_part = amount % base.pow(8); - let amount = format!("{int_part}.{frac_part:08}"); - amount - } - - pub fn get_script_pub_key(&self) -> Option