Skip to content

Commit 9a75478

Browse files
committed
feat: add syntax and type-checking for as-contract? and allowances
1 parent 504c115 commit 9a75478

File tree

22 files changed

+1645
-146
lines changed

22 files changed

+1645
-146
lines changed

clarity-types/src/errors/analysis.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,10 @@ pub enum CheckErrors {
309309
ExecutionTimeExpired,
310310

311311
// contract post-conditions
312-
RestrictAssetsExpectedListOfAllowances,
312+
ExpectedListOfAllowances(String, i32),
313+
AllowanceExprNotAllowed,
314+
ExpectedAllowanceExpr(String),
315+
WithAllAllowanceNotAllowed,
313316
}
314317

315318
#[derive(Debug, PartialEq)]
@@ -608,7 +611,10 @@ impl DiagnosableError for CheckErrors {
608611
CheckErrors::CostComputationFailed(s) => format!("contract cost computation failed: {s}"),
609612
CheckErrors::CouldNotDetermineSerializationType => "could not determine the input type for the serialization function".into(),
610613
CheckErrors::ExecutionTimeExpired => "execution time expired".into(),
611-
CheckErrors::RestrictAssetsExpectedListOfAllowances => "restrict-assets? expects a list of asset allowances as its second argument".into(),
614+
CheckErrors::ExpectedListOfAllowances(fn_name, arg_num) => format!("{fn_name} expects a list of asset allowances as argument {arg_num}"),
615+
CheckErrors::AllowanceExprNotAllowed => "allowance expressions are only allowed in the context of a `restrict-assets?` or `as-contract?`".into(),
616+
CheckErrors::ExpectedAllowanceExpr(got_name) => format!("expected an allowance expression, got: {got_name}"),
617+
CheckErrors::WithAllAllowanceNotAllowed => "with-all-assets-unsafe is not allowed here, only in the allowance list for `as-contract?`".into(),
612618
}
613619
}
614620

clarity-types/src/types/signatures.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,9 @@ lazy_static! {
261261
pub const ASCII_40: TypeSignature = SequenceType(SequenceSubtype::StringType(
262262
StringSubtype::ASCII(BufferLength(40)),
263263
));
264+
pub const ASCII_128: TypeSignature = SequenceType(SequenceSubtype::StringType(
265+
StringSubtype::ASCII(BufferLength(128)),
266+
));
264267
pub const UTF8_40: TypeSignature = SequenceType(SequenceSubtype::StringType(StringSubtype::UTF8(
265268
StringUTF8Length(40),
266269
)));

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

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -173,11 +173,31 @@ impl ArithmeticOnlyChecker<'_> {
173173
| ContractCall | StxTransfer | StxTransferMemo | StxBurn | AtBlock | GetStxBalance
174174
| GetTokenSupply | BurnToken | FromConsensusBuff | ToConsensusBuff | BurnAsset
175175
| StxGetAccount => Err(Error::FunctionNotPermitted(function)),
176-
Append | Concat | AsMaxLen | ContractOf | PrincipalOf | ListCons | Print
177-
| AsContract | ElementAt | ElementAtAlias | IndexOf | IndexOfAlias | Map | Filter
178-
| Fold | Slice | ReplaceAt | ContractHash | RestrictAssets => {
179-
Err(Error::FunctionNotPermitted(function))
180-
}
176+
Append
177+
| Concat
178+
| AsMaxLen
179+
| ContractOf
180+
| PrincipalOf
181+
| ListCons
182+
| Print
183+
| AsContract
184+
| ElementAt
185+
| ElementAtAlias
186+
| IndexOf
187+
| IndexOfAlias
188+
| Map
189+
| Filter
190+
| Fold
191+
| Slice
192+
| ReplaceAt
193+
| ContractHash
194+
| RestrictAssets
195+
| AsContractSafe
196+
| AllowanceWithStx
197+
| AllowanceWithFt
198+
| AllowanceWithNft
199+
| AllowanceWithStacking
200+
| AllowanceAll => Err(Error::FunctionNotPermitted(function)),
181201
BuffToIntLe | BuffToUIntLe | BuffToIntBe | BuffToUIntBe => {
182202
Err(Error::FunctionNotPermitted(function))
183203
}

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

Lines changed: 120 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -282,20 +282,101 @@ impl<'a, 'b> ReadOnlyChecker<'a, 'b> {
282282
use crate::vm::functions::NativeFunctions::*;
283283

284284
match function {
285-
Add | Subtract | Divide | Multiply | CmpGeq | CmpLeq | CmpLess | CmpGreater
286-
| Modulo | Power | Sqrti | Log2 | BitwiseXor | And | Or | Not | Hash160 | Sha256
287-
| Keccak256 | Equals | If | Sha512 | Sha512Trunc256 | Secp256k1Recover
288-
| Secp256k1Verify | ConsSome | ConsOkay | ConsError | DefaultTo | UnwrapRet
289-
| UnwrapErrRet | IsOkay | IsNone | Asserts | Unwrap | UnwrapErr | Match | IsErr
290-
| IsSome | TryRet | ToUInt | ToInt | BuffToIntLe | BuffToUIntLe | BuffToIntBe
291-
| BuffToUIntBe | IntToAscii | IntToUtf8 | StringToInt | StringToUInt | IsStandard
292-
| ToConsensusBuff | PrincipalDestruct | PrincipalConstruct | Append | Concat
293-
| AsMaxLen | ContractOf | PrincipalOf | ListCons | GetBlockInfo | GetBurnBlockInfo
294-
| GetStacksBlockInfo | GetTenureInfo | TupleGet | TupleMerge | Len | Print
295-
| AsContract | Begin | FetchVar | GetStxBalance | StxGetAccount | GetTokenBalance
296-
| GetAssetOwner | GetTokenSupply | ElementAt | IndexOf | Slice | ReplaceAt
297-
| BitwiseAnd | BitwiseOr | BitwiseNot | BitwiseLShift | BitwiseRShift | BitwiseXor2
298-
| ElementAtAlias | IndexOfAlias | ContractHash | ToAscii => {
285+
Add
286+
| Subtract
287+
| Divide
288+
| Multiply
289+
| CmpGeq
290+
| CmpLeq
291+
| CmpLess
292+
| CmpGreater
293+
| Modulo
294+
| Power
295+
| Sqrti
296+
| Log2
297+
| BitwiseXor
298+
| And
299+
| Or
300+
| Not
301+
| Hash160
302+
| Sha256
303+
| Keccak256
304+
| Equals
305+
| If
306+
| Sha512
307+
| Sha512Trunc256
308+
| Secp256k1Recover
309+
| Secp256k1Verify
310+
| ConsSome
311+
| ConsOkay
312+
| ConsError
313+
| DefaultTo
314+
| UnwrapRet
315+
| UnwrapErrRet
316+
| IsOkay
317+
| IsNone
318+
| Asserts
319+
| Unwrap
320+
| UnwrapErr
321+
| Match
322+
| IsErr
323+
| IsSome
324+
| TryRet
325+
| ToUInt
326+
| ToInt
327+
| BuffToIntLe
328+
| BuffToUIntLe
329+
| BuffToIntBe
330+
| BuffToUIntBe
331+
| IntToAscii
332+
| IntToUtf8
333+
| StringToInt
334+
| StringToUInt
335+
| IsStandard
336+
| ToConsensusBuff
337+
| PrincipalDestruct
338+
| PrincipalConstruct
339+
| Append
340+
| Concat
341+
| AsMaxLen
342+
| ContractOf
343+
| PrincipalOf
344+
| ListCons
345+
| GetBlockInfo
346+
| GetBurnBlockInfo
347+
| GetStacksBlockInfo
348+
| GetTenureInfo
349+
| TupleGet
350+
| TupleMerge
351+
| Len
352+
| Print
353+
| AsContract
354+
| Begin
355+
| FetchVar
356+
| GetStxBalance
357+
| StxGetAccount
358+
| GetTokenBalance
359+
| GetAssetOwner
360+
| GetTokenSupply
361+
| ElementAt
362+
| IndexOf
363+
| Slice
364+
| ReplaceAt
365+
| BitwiseAnd
366+
| BitwiseOr
367+
| BitwiseNot
368+
| BitwiseLShift
369+
| BitwiseRShift
370+
| BitwiseXor2
371+
| ElementAtAlias
372+
| IndexOfAlias
373+
| ContractHash
374+
| ToAscii
375+
| AllowanceWithStx
376+
| AllowanceWithFt
377+
| AllowanceWithNft
378+
| AllowanceWithStacking
379+
| AllowanceAll => {
299380
// Check all arguments.
300381
self.check_each_expression_is_read_only(args)
301382
}
@@ -434,16 +515,38 @@ impl<'a, 'b> ReadOnlyChecker<'a, 'b> {
434515
let asset_owner_read_only = self.check_read_only(&args[0])?;
435516

436517
// Check the allowances argument.
437-
let allowances = args[1]
438-
.match_list()
439-
.ok_or(CheckErrors::RestrictAssetsExpectedListOfAllowances)?;
518+
let allowances =
519+
args[1]
520+
.match_list()
521+
.ok_or(CheckErrors::ExpectedListOfAllowances(
522+
"restrict-assets?".into(),
523+
2,
524+
))?;
440525
let allowances_read_only = self.check_each_expression_is_read_only(allowances)?;
441526

442527
// Check the body expressions.
443528
let body_read_only = self.check_each_expression_is_read_only(&args[2..])?;
444529

445530
Ok(asset_owner_read_only && allowances_read_only && body_read_only)
446531
}
532+
AsContractSafe => {
533+
check_arguments_at_least(2, args)?;
534+
535+
// Check the allowances argument.
536+
let allowances =
537+
args[0]
538+
.match_list()
539+
.ok_or(CheckErrors::ExpectedListOfAllowances(
540+
"as-contract?".into(),
541+
1,
542+
))?;
543+
let allowances_read_only = self.check_each_expression_is_read_only(allowances)?;
544+
545+
// Check the body expressions.
546+
let body_read_only = self.check_each_expression_is_read_only(&args[1..])?;
547+
548+
Ok(allowances_read_only && body_read_only)
549+
}
447550
}
448551
}
449552

clarity/src/vm/analysis/type_checker/v2_05/natives/mod.rs

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,15 @@
1616

1717
use stacks_common::types::StacksEpochId;
1818

19-
use super::{TypeChecker, TypingContext, check_argument_count, check_arguments_at_least, no_type};
19+
use super::{check_argument_count, check_arguments_at_least, no_type, TypeChecker, TypingContext};
2020
use crate::vm::analysis::errors::{CheckError, CheckErrors, SyntaxBindingErrorType};
2121
use crate::vm::costs::cost_functions::ClarityCostFunction;
2222
use crate::vm::costs::{analysis_typecheck_cost, runtime_cost};
2323
use crate::vm::diagnostic::DiagnosableError;
24-
use crate::vm::functions::{NativeFunctions, handle_binding_list};
24+
use crate::vm::functions::{handle_binding_list, NativeFunctions};
2525
use crate::vm::types::{
26-
BUFF_20, BUFF_32, BUFF_33, BUFF_64, BUFF_65, BlockInfoProperty, FixedFunction, FunctionArg,
27-
FunctionSignature, FunctionType, PrincipalData, TupleTypeSignature, TypeSignature, Value,
26+
BlockInfoProperty, FixedFunction, FunctionArg, FunctionSignature, FunctionType, PrincipalData,
27+
TupleTypeSignature, TypeSignature, Value, BUFF_20, BUFF_32, BUFF_33, BUFF_64, BUFF_65,
2828
};
2929
use crate::vm::{ClarityName, ClarityVersion, SymbolicExpression, SymbolicExpressionType};
3030

@@ -777,13 +777,43 @@ impl TypedNativeFunction {
777777
IsNone => Special(SpecialNativeFunction(&options::check_special_is_optional)),
778778
IsSome => Special(SpecialNativeFunction(&options::check_special_is_optional)),
779779
AtBlock => Special(SpecialNativeFunction(&check_special_at_block)),
780-
ElementAtAlias | IndexOfAlias | BuffToIntLe | BuffToUIntLe | BuffToIntBe
781-
| BuffToUIntBe | IsStandard | PrincipalDestruct | PrincipalConstruct | StringToInt
782-
| StringToUInt | IntToAscii | IntToUtf8 | GetBurnBlockInfo | StxTransferMemo
783-
| StxGetAccount | BitwiseAnd | BitwiseOr | BitwiseNot | BitwiseLShift
784-
| BitwiseRShift | BitwiseXor2 | Slice | ToConsensusBuff | FromConsensusBuff
785-
| ReplaceAt | GetStacksBlockInfo | GetTenureInfo | ContractHash | ToAscii
786-
| RestrictAssets => {
780+
ElementAtAlias
781+
| IndexOfAlias
782+
| BuffToIntLe
783+
| BuffToUIntLe
784+
| BuffToIntBe
785+
| BuffToUIntBe
786+
| IsStandard
787+
| PrincipalDestruct
788+
| PrincipalConstruct
789+
| StringToInt
790+
| StringToUInt
791+
| IntToAscii
792+
| IntToUtf8
793+
| GetBurnBlockInfo
794+
| StxTransferMemo
795+
| StxGetAccount
796+
| BitwiseAnd
797+
| BitwiseOr
798+
| BitwiseNot
799+
| BitwiseLShift
800+
| BitwiseRShift
801+
| BitwiseXor2
802+
| Slice
803+
| ToConsensusBuff
804+
| FromConsensusBuff
805+
| ReplaceAt
806+
| GetStacksBlockInfo
807+
| GetTenureInfo
808+
| ContractHash
809+
| ToAscii
810+
| RestrictAssets
811+
| AsContractSafe
812+
| AllowanceWithStx
813+
| AllowanceWithFt
814+
| AllowanceWithNft
815+
| AllowanceWithStacking
816+
| AllowanceAll => {
787817
return Err(CheckErrors::Expects(
788818
"Clarity 2+ keywords should not show up in 2.05".into(),
789819
));

0 commit comments

Comments
 (0)