@@ -19,10 +19,12 @@ use clarity::types::chainstate::{StacksAddress, StacksPrivateKey, StacksPublicKe
1919use clarity:: types:: StacksEpochId ;
2020use clarity:: util:: hash:: { MerkleTree , Sha512Trunc256Sum } ;
2121use clarity:: util:: secp256k1:: MessageSignature ;
22+ use clarity:: vm:: ast:: errors:: { ParseError , ParseErrors } ;
23+ use clarity:: vm:: ast:: stack_depth_checker:: AST_CALL_STACK_DEPTH_BUFFER ;
2224use clarity:: vm:: costs:: ExecutionCost ;
2325use clarity:: vm:: events:: StacksTransactionEvent ;
2426use clarity:: vm:: types:: { PrincipalData , ResponseData } ;
25- use clarity:: vm:: Value as ClarityValue ;
27+ use clarity:: vm:: { Value as ClarityValue , MAX_CALL_STACK_DEPTH } ;
2628use serde:: { Deserialize , Serialize } ;
2729use stacks_common:: bitvec:: BitVec ;
2830
@@ -32,10 +34,9 @@ use crate::chainstate::stacks::boot::{RewardSet, RewardSetData};
3234use crate :: chainstate:: stacks:: db:: StacksEpochReceipt ;
3335use crate :: chainstate:: stacks:: { Error as ChainstateError , StacksTransaction , TenureChangeCause } ;
3436use crate :: chainstate:: tests:: TestChainstate ;
35- use crate :: clarity_vm:: clarity:: PreCommitClarityBlock ;
36- use crate :: core:: test_util:: make_stacks_transfer_tx;
37+ use crate :: clarity_vm:: clarity:: { Error as ClarityError , PreCommitClarityBlock } ;
38+ use crate :: core:: test_util:: { make_contract_publish , make_stacks_transfer_tx} ;
3739use crate :: net:: tests:: NakamotoBootPlan ;
38-
3940pub const SK_1 : & str = "a1289f6438855da7decf9b61b852c882c398cff1446b2a0f823538aa2ebef92e01" ;
4041pub const SK_2 : & str = "4ce9a8f7539ea93753a36405b16e8b57e15a552430410709c2b6d65dca5c02e201" ;
4142pub const SK_3 : & str = "cb95ddd0fe18ec57f4f3533b95ae564b3f1ae063dbf75b46334bd86245aef78501" ;
@@ -207,6 +208,7 @@ impl ConsensusMismatch {
207208 ( Err ( actual_err) , ExpectedResult :: Failure ( expected_err) ) => {
208209 let actual_err_str = actual_err. to_string ( ) ;
209210 if actual_err_str != expected_err {
211+ panic ! ( "actual_err_str: {actual_err_str}" ) ;
210212 mismatches. error = Some ( ( actual_err_str, expected_err) ) ;
211213 }
212214 }
@@ -487,3 +489,61 @@ fn test_append_stx_transfers() {
487489 } ;
488490 ConsensusTest :: new ( function_name ! ( ) , test_vector) . run ( )
489491}
492+
493+ #[ test]
494+ fn test_append_chainstate_error_expression_stack_depth_too_deep ( ) {
495+ // something just over the limit of the expression depth
496+ let exceeds_repeat_factor = AST_CALL_STACK_DEPTH_BUFFER + ( MAX_CALL_STACK_DEPTH as u64 ) ;
497+ let tx_exceeds_body_start = "{ a : " . repeat ( exceeds_repeat_factor as usize ) ;
498+ let tx_exceeds_body_end = "} " . repeat ( exceeds_repeat_factor as usize ) ;
499+ let tx_exceeds_body = format ! ( "{tx_exceeds_body_start}u1 {tx_exceeds_body_end}" ) ;
500+
501+ let sender_privk = StacksPrivateKey :: from_hex ( SK_1 ) . unwrap ( ) ;
502+ let tx_fee = ( tx_exceeds_body. len ( ) * 100 ) as u64 ;
503+ let initial_balances = vec ! [ (
504+ StacksAddress :: p2pkh( false , & StacksPublicKey :: from_private( & sender_privk) ) . into( ) ,
505+ tx_fee,
506+ ) ] ;
507+ let tx_bytes = make_contract_publish (
508+ & sender_privk,
509+ 0 ,
510+ tx_fee,
511+ CHAIN_ID_TESTNET ,
512+ "test-exceeds" ,
513+ & tx_exceeds_body,
514+ ) ;
515+ let tx = StacksTransaction :: consensus_deserialize ( & mut & tx_bytes[ ..] ) . unwrap ( ) ;
516+ let transfer_result = ExpectedTransactionOutput {
517+ return_type : ClarityValue :: Response ( ResponseData {
518+ committed : true ,
519+ data : Box :: new ( ClarityValue :: Bool ( true ) ) ,
520+ } ) ,
521+ cost : ExecutionCost {
522+ write_length : 0 ,
523+ write_count : 0 ,
524+ read_length : 0 ,
525+ read_count : 0 ,
526+ runtime : 0 ,
527+ } ,
528+ } ;
529+ let outputs = ExpectedBlockOutput {
530+ transactions : vec ! [ transfer_result] ,
531+ total_block_cost : ExecutionCost :: ZERO ,
532+ } ;
533+ // TODO: should look into append_block. It does weird wrapping of ChainstateError variants inside ChainstateError::StacksInvalidBlock.
534+ let e = ChainstateError :: ClarityError ( ClarityError :: Parse ( ParseError :: new (
535+ ParseErrors :: ExpressionStackDepthTooDeep ,
536+ ) ) ) ;
537+ let msg = format ! ( "Invalid Stacks block 518dfea674b5c4874e025a31e01a522c8269005c0685d12658f0359757de6692: {e:?}" ) ;
538+ let test_vector = ConsensusTestVector {
539+ initial_balances,
540+ // Marf hash doesn't matter. It will fail with ExpressionStackDepthTooDeep
541+ marf_hash : "0000000000000000000000000000000000000000000000000000000000000000" . into ( ) ,
542+ epoch_id : StacksEpochId :: Epoch30 as u32 ,
543+ transactions : vec ! [ tx] ,
544+ expected_result : ExpectedResult :: Failure (
545+ ChainstateError :: InvalidStacksBlock ( msg) . to_string ( ) ,
546+ ) ,
547+ } ;
548+ ConsensusTest :: new ( function_name ! ( ) , test_vector) . run ( )
549+ }
0 commit comments