Skip to content

Commit a9fc6fe

Browse files
committed
Merge branch 'develop' into feat/improve-consensus-test-api
2 parents ca09506 + 0efc005 commit a9fc6fe

37 files changed

+6418
-127
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@ and this project adheres to the versioning scheme outlined in the [README.md](RE
2626
- `current-contract`
2727
- `block-time`
2828
- `to-ascii?`
29+
- `restrict-assets?`
30+
- `as-contract?`
31+
- Special allowance expressions:
32+
- `with-stx`
33+
- `with-ft`
34+
- `with-nft`
35+
- `with-stacking`
36+
- `with-all-assets-unsafe`
2937
- 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.
3038
- Fixed a bug caused by a miner winning a sortition with a block commit that pointed to a previous tip, which would cause the miner to try and reorg itself. [#6481](https://github.com/stacks-network/stacks-core/issues/6481)
3139

clarity-types/src/errors/analysis.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,16 @@ pub enum CheckErrors {
307307

308308
// time checker errors
309309
ExecutionTimeExpired,
310+
311+
// contract post-conditions
312+
ExpectedListOfAllowances(String, i32),
313+
AllowanceExprNotAllowed,
314+
ExpectedAllowanceExpr(String),
315+
WithAllAllowanceNotAllowed,
316+
WithAllAllowanceNotAlone,
317+
WithNftExpectedListOfIdentifiers,
318+
MaxIdentifierLengthExceeded(u32, u32),
319+
TooManyAllowances(usize, usize),
310320
}
311321

312322
#[derive(Debug, PartialEq)]
@@ -605,6 +615,14 @@ impl DiagnosableError for CheckErrors {
605615
CheckErrors::CostComputationFailed(s) => format!("contract cost computation failed: {s}"),
606616
CheckErrors::CouldNotDetermineSerializationType => "could not determine the input type for the serialization function".into(),
607617
CheckErrors::ExecutionTimeExpired => "execution time expired".into(),
618+
CheckErrors::ExpectedListOfAllowances(fn_name, arg_num) => format!("{fn_name} expects a list of asset allowances as argument {arg_num}"),
619+
CheckErrors::AllowanceExprNotAllowed => "allowance expressions are only allowed in the context of a `restrict-assets?` or `as-contract?`".into(),
620+
CheckErrors::ExpectedAllowanceExpr(got_name) => format!("expected an allowance expression, got: {got_name}"),
621+
CheckErrors::WithAllAllowanceNotAllowed => "with-all-assets-unsafe is not allowed here, only in the allowance list for `as-contract?`".into(),
622+
CheckErrors::WithAllAllowanceNotAlone => "with-all-assets-unsafe must not be used along with other allowances".into(),
623+
CheckErrors::WithNftExpectedListOfIdentifiers => "with-nft allowance must include a list of asset identifiers".into(),
624+
CheckErrors::MaxIdentifierLengthExceeded(max_len, len) => format!("with-nft allowance identifiers list must not exceed {max_len} elements, got {len}"),
625+
CheckErrors::TooManyAllowances(max_allowed, found) => format!("too many allowances specified, the maximum is {max_allowed}, found {found}"),
608626
}
609627
}
610628

clarity-types/src/errors/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ pub enum RuntimeErrorType {
101101
PoxAlreadyLocked,
102102

103103
BlockTimeNotAvailable,
104+
Unreachable,
104105
}
105106

106107
#[derive(Debug, PartialEq)]

clarity-types/src/types/mod.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1235,6 +1235,16 @@ impl Value {
12351235
Err(InterpreterError::Expect("Expected response".into()).into())
12361236
}
12371237
}
1238+
1239+
pub fn expect_string_ascii(self) -> Result<String> {
1240+
if let Value::Sequence(SequenceData::String(CharType::ASCII(ASCIIData { data }))) = self {
1241+
Ok(String::from_utf8(data)
1242+
.map_err(|_| InterpreterError::Expect("Non UTF-8 data in string".into()))?)
1243+
} else {
1244+
error!("Value '{self:?}' is not an ASCII string");
1245+
Err(InterpreterError::Expect("Expected ASCII string".into()).into())
1246+
}
1247+
}
12381248
}
12391249

12401250
impl BuffData {

clarity-types/src/types/signatures.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -855,6 +855,8 @@ impl TypeSignature {
855855
pub const STRING_ASCII_MAX: TypeSignature = Self::type_ascii_const(MAX_VALUE_SIZE);
856856
/// String ASCII type with length 40.
857857
pub const STRING_ASCII_40: TypeSignature = Self::type_ascii_const(40);
858+
/// String ASCII type with length 128.
859+
pub const STRING_ASCII_128: TypeSignature = Self::type_ascii_const(128);
858860

859861
/// String UTF8 type with minimum length (`1`).
860862
pub const STRING_UTF8_MIN: TypeSignature = Self::type_string_utf8(1);
@@ -908,7 +910,7 @@ impl TypeSignature {
908910

909911
/// Creates a string ASCII type with the specified length.
910912
/// It may panic if the provided length is invalid.
911-
#[cfg(test)]
913+
#[cfg(any(test, feature = "testing"))]
912914
pub const fn new_ascii_type_checked(len: u32) -> Self {
913915
Self::type_ascii_const(len)
914916
}

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

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -173,9 +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 => Err(Error::FunctionNotPermitted(function)),
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)),
179201
BuffToIntLe | BuffToUIntLe | BuffToIntBe | BuffToUIntBe => {
180202
Err(Error::FunctionNotPermitted(function))
181203
}

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

Lines changed: 134 additions & 14 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
}
@@ -427,6 +508,45 @@ impl<'a, 'b> ReadOnlyChecker<'a, 'b> {
427508
self.check_each_expression_is_read_only(&args[2..])
428509
.map(|args_read_only| args_read_only && is_function_read_only)
429510
}
511+
RestrictAssets => {
512+
check_arguments_at_least(3, args)?;
513+
514+
// Check the asset owner argument.
515+
let asset_owner_read_only = self.check_read_only(&args[0])?;
516+
517+
// Check the allowances argument.
518+
let allowances =
519+
args[1]
520+
.match_list()
521+
.ok_or(CheckErrors::ExpectedListOfAllowances(
522+
"restrict-assets?".into(),
523+
2,
524+
))?;
525+
let allowances_read_only = self.check_each_expression_is_read_only(allowances)?;
526+
527+
// Check the body expressions.
528+
let body_read_only = self.check_each_expression_is_read_only(&args[2..])?;
529+
530+
Ok(asset_owner_read_only && allowances_read_only && body_read_only)
531+
}
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+
}
430550
}
431551
}
432552

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

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -777,12 +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 => {
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 => {
786817
return Err(CheckErrors::Expects(
787818
"Clarity 2+ keywords should not show up in 2.05".into(),
788819
));

clarity/src/vm/analysis/type_checker/v2_1/mod.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1135,7 +1135,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> {
11351135
Ok(())
11361136
}
11371137

1138-
// Type check an expression, with an expected_type that should _admit_ the expression.
1138+
/// Type check an expression, with an expected_type that should _admit_ the expression.
11391139
pub fn type_check_expects(
11401140
&mut self,
11411141
expr: &SymbolicExpression,
@@ -1157,7 +1157,7 @@ impl<'a, 'b> TypeChecker<'a, 'b> {
11571157
}
11581158
}
11591159

1160-
// Type checks an expression, recursively type checking its subexpressions
1160+
/// Type checks an expression, recursively type checking its subexpressions
11611161
pub fn type_check(
11621162
&mut self,
11631163
expr: &SymbolicExpression,
@@ -1176,6 +1176,8 @@ impl<'a, 'b> TypeChecker<'a, 'b> {
11761176
result
11771177
}
11781178

1179+
/// Type checks a list of statements, ensuring that each statement is valid
1180+
/// and any responses before the last statement are handled.
11791181
fn type_check_consecutive_statements(
11801182
&mut self,
11811183
args: &[SymbolicExpression],

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ mod assets;
3939
mod conversions;
4040
mod maps;
4141
mod options;
42+
pub(crate) mod post_conditions;
4243
mod sequences;
4344

4445
#[allow(clippy::large_enum_variant)]
@@ -1210,6 +1211,15 @@ impl TypedNativeFunction {
12101211
CheckErrors::Expects("FATAL: Legal Clarity response type marked invalid".into())
12111212
})?,
12121213
))),
1214+
RestrictAssets => Special(SpecialNativeFunction(
1215+
&post_conditions::check_restrict_assets,
1216+
)),
1217+
AsContractSafe => Special(SpecialNativeFunction(&post_conditions::check_as_contract)),
1218+
AllowanceWithStx
1219+
| AllowanceWithFt
1220+
| AllowanceWithNft
1221+
| AllowanceWithStacking
1222+
| AllowanceAll => Special(SpecialNativeFunction(&post_conditions::check_allowance_err)),
12131223
};
12141224

12151225
Ok(out)

0 commit comments

Comments
 (0)