@@ -61,21 +61,25 @@ TEST(ContextConstrainingTest, ContextSwitchingCallReturn)
6161 { C::execution_context_id, 1 },
6262 { C::execution_next_context_id, 2 },
6363 { C::execution_bytecode_id, top_bytecode_id },
64+ { C::execution_is_static, 0 }, // Non-static context
6465 { C::execution_parent_l2_gas_limit, 2000 },
6566 { C::execution_parent_da_gas_limit, 4000 },
6667 { C::execution_parent_l2_gas_used, 500 },
6768 { C::execution_parent_da_gas_used, 1500 },
69+ { C::execution_enqueued_call_start, 1 },
6870 },
6971 // CALL
7072 {
7173 { C::execution_sel, 1 },
7274 { C::execution_pc, 1 },
7375 { C::execution_next_pc, 2 },
7476 { C::execution_sel_execute_call, 1 },
77+ { C::execution_sel_execute_static_call, 0 }, // Regular CALL, not STATICCALL
7578 { C::execution_sel_enter_call, 1 },
7679 { C::execution_context_id, 1 },
7780 { C::execution_next_context_id, 2 },
7881 { C::execution_bytecode_id, top_bytecode_id }, // Same as previous row (propagated)
82+ { C::execution_is_static, 0 }, // Still non-static
7983 { C::execution_rop_4_, /* cd offset=*/ 10 },
8084 { C::execution_register_2_, /* contract address=*/ 0xdeadbeef },
8185 { C::execution_register_3_, /* cd size=*/ 1 },
@@ -96,6 +100,7 @@ TEST(ContextConstrainingTest, ContextSwitchingCallReturn)
96100 { C::execution_has_parent_ctx, 1 },
97101 { C::execution_contract_address, 0xdeadbeef },
98102 { C::execution_bytecode_id, nested_bytecode_id }, // New bytecode_id on entering new context
103+ { C::execution_is_static, 0 }, // Remains non-static after regular CALL
99104 { C::execution_parent_calldata_addr, 10 },
100105 { C::execution_parent_calldata_size, 1 },
101106 },
@@ -633,6 +638,308 @@ TEST(ContextConstrainingTest, BytecodeIdPropagation)
633638 " BYTECODE_ID_NEXT_ROW" ); // Should fail constraint
634639}
635640
641+ TEST (ContextConstrainingTest, IsStaticPropagation)
642+ {
643+ // Test 1: Non-static context making a regular CALL - should remain non-static
644+ TestTraceContainer trace1 ({
645+ { { C::precomputed_first_row, 1 } },
646+ {
647+ { C::execution_sel, 1 },
648+ { C::execution_context_id, 1 },
649+ { C::execution_next_context_id, 2 },
650+ { C::execution_is_static, 0 }, // Non-static context
651+ { C::execution_sel_enter_call, 1 },
652+ { C::execution_sel_execute_call, 1 }, // Regular CALL
653+ { C::execution_sel_execute_static_call, 0 },
654+ },
655+ {
656+ { C::execution_sel, 1 },
657+ { C::execution_context_id, 2 },
658+ { C::execution_next_context_id, 3 },
659+ { C::execution_is_static, 0 }, // Should remain non-static
660+ },
661+ });
662+ check_relation<context>(trace1, context::SR_IS_STATIC_IF_STATIC_CALL);
663+
664+ // Test 2: Non-static context making a STATICCALL - should become static
665+ TestTraceContainer trace2 ({
666+ { { C::precomputed_first_row, 1 } },
667+ {
668+ { C::execution_sel, 1 },
669+ { C::execution_context_id, 1 },
670+ { C::execution_next_context_id, 2 },
671+ { C::execution_is_static, 0 }, // Non-static context
672+ { C::execution_sel_enter_call, 1 },
673+ { C::execution_sel_execute_call, 1 },
674+ { C::execution_sel_execute_static_call, 1 }, // STATICCALL
675+ },
676+ {
677+ { C::execution_sel, 1 },
678+ { C::execution_context_id, 2 },
679+ { C::execution_next_context_id, 3 },
680+ { C::execution_is_static, 1 }, // Should become static
681+ },
682+ });
683+ check_relation<context>(trace2, context::SR_IS_STATIC_IF_STATIC_CALL);
684+
685+ // Test 3: Static context making any call - must remain static
686+ TestTraceContainer trace3 ({
687+ { { C::precomputed_first_row, 1 } },
688+ {
689+ { C::execution_sel, 1 },
690+ { C::execution_context_id, 1 },
691+ { C::execution_next_context_id, 2 },
692+ { C::execution_is_static, 1 }, // Static context
693+ { C::execution_sel_enter_call, 1 },
694+ { C::execution_sel_execute_call, 1 }, // Regular CALL
695+ { C::execution_sel_execute_static_call, 0 },
696+ },
697+ {
698+ { C::execution_sel, 1 },
699+ { C::execution_context_id, 2 },
700+ { C::execution_next_context_id, 3 },
701+ { C::execution_is_static, 1 }, // Must remain static
702+ },
703+ });
704+ check_relation<context>(trace3, context::SR_IS_STATIC_IF_CALL_FROM_STATIC_CONTEXT);
705+
706+ // Negative test 1: Non-static context with regular CALL incorrectly becomes static
707+ TestTraceContainer trace4 ({
708+ { { C::precomputed_first_row, 1 } },
709+ {
710+ { C::execution_sel, 1 },
711+ { C::execution_context_id, 1 },
712+ { C::execution_next_context_id, 2 },
713+ { C::execution_is_static, 0 },
714+ { C::execution_sel_enter_call, 1 },
715+ { C::execution_sel_execute_call, 1 },
716+ { C::execution_sel_execute_static_call, 0 }, // Regular CALL
717+ },
718+ {
719+ { C::execution_sel, 1 },
720+ { C::execution_context_id, 2 },
721+ { C::execution_next_context_id, 3 },
722+ { C::execution_is_static, 1 }, // Incorrectly becomes static
723+ },
724+ });
725+ EXPECT_THROW_WITH_MESSAGE (check_relation<context>(trace4, context::SR_IS_STATIC_IF_STATIC_CALL),
726+ " IS_STATIC_IF_STATIC_CALL" );
727+
728+ // Negative test 2: Static context making call but next context is non-static
729+ TestTraceContainer trace5 ({
730+ { { C::precomputed_first_row, 1 } },
731+ {
732+ { C::execution_sel, 1 },
733+ { C::execution_context_id, 1 },
734+ { C::execution_next_context_id, 2 },
735+ { C::execution_is_static, 1 }, // Static context
736+ { C::execution_sel_enter_call, 1 },
737+ { C::execution_sel_execute_call, 1 },
738+ },
739+ {
740+ { C::execution_sel, 1 },
741+ { C::execution_context_id, 2 },
742+ { C::execution_next_context_id, 3 },
743+ { C::execution_is_static, 0 }, // Incorrectly becomes non-static
744+ },
745+ });
746+ EXPECT_THROW_WITH_MESSAGE (check_relation<context>(trace5, context::SR_IS_STATIC_IF_CALL_FROM_STATIC_CONTEXT),
747+ " IS_STATIC_IF_CALL_FROM_STATIC_CONTEXT" );
748+
749+ // Test 4: is_static propagation without calls
750+ TestTraceContainer trace6 ({
751+ { { C::precomputed_first_row, 1 } },
752+ {
753+ { C::execution_sel, 1 },
754+ { C::execution_context_id, 1 },
755+ { C::execution_next_context_id, 1 },
756+ { C::execution_is_static, 1 }, // Static context
757+ },
758+ {
759+ { C::execution_sel, 1 },
760+ { C::execution_context_id, 1 },
761+ { C::execution_next_context_id, 1 },
762+ { C::execution_is_static, 1 }, // Should propagate
763+ },
764+ });
765+ check_relation<context>(trace6, context::SR_IS_STATIC_NEXT_ROW);
766+
767+ // Negative test 3: is_static not propagating correctly when no call
768+ TestTraceContainer trace7 ({
769+ { { C::precomputed_first_row, 1 } },
770+ {
771+ { C::execution_sel, 1 },
772+ { C::execution_context_id, 1 },
773+ { C::execution_next_context_id, 1 },
774+ { C::execution_is_static, 0 },
775+ },
776+ {
777+ { C::execution_sel, 1 },
778+ { C::execution_context_id, 1 },
779+ { C::execution_next_context_id, 1 },
780+ { C::execution_is_static, 1 }, // Incorrectly changes
781+ },
782+ });
783+ EXPECT_THROW_WITH_MESSAGE (check_relation<context>(trace7, context::SR_IS_STATIC_NEXT_ROW), " IS_STATIC_NEXT_ROW" );
784+ }
785+
786+ TEST (ContextConstrainingTest, ContextSwitchingStaticCall)
787+ {
788+ constexpr uint32_t top_bytecode_id = 0x12345678 ;
789+ constexpr uint32_t nested_bytecode_id = 0x456789ab ;
790+
791+ // Test STATICCALL from non-static context
792+ TestTraceContainer trace1 ({ {
793+ { C::execution_next_context_id, 0 },
794+ { C::precomputed_first_row, 1 },
795+ // Context Stack Rows
796+ { C::context_stack_sel, 1 },
797+ { C::context_stack_entered_context_id, 2 },
798+ { C::context_stack_context_id, 1 },
799+ { C::context_stack_parent_id, 0 },
800+ { C::context_stack_next_pc, 2 },
801+ { C::context_stack_msg_sender, 0 },
802+ { C::context_stack_contract_address, 0 },
803+ { C::context_stack_bytecode_id, top_bytecode_id },
804+ { C::context_stack_is_static, 0 }, // Non-static in context stack
805+ { C::context_stack_parent_calldata_addr, 0 },
806+ { C::context_stack_parent_calldata_size, 0 },
807+ { C::context_stack_parent_l2_gas_limit, 2000 },
808+ { C::context_stack_parent_da_gas_limit, 4000 },
809+ { C::context_stack_parent_l2_gas_used, 500 },
810+ { C::context_stack_parent_da_gas_used, 1500 },
811+ },
812+ // First Row of execution
813+ {
814+ { C::execution_sel, 1 },
815+ { C::execution_pc, 0 },
816+ { C::execution_next_pc, 1 },
817+ { C::execution_context_id, 1 },
818+ { C::execution_next_context_id, 2 },
819+ { C::execution_bytecode_id, top_bytecode_id },
820+ { C::execution_is_static, 0 }, // Non-static context
821+ { C::execution_parent_l2_gas_limit, 2000 },
822+ { C::execution_parent_da_gas_limit, 4000 },
823+ { C::execution_parent_l2_gas_used, 500 },
824+ { C::execution_parent_da_gas_used, 1500 },
825+ { C::execution_enqueued_call_start, 1 },
826+ },
827+ // STATICCALL
828+ {
829+ { C::execution_sel, 1 },
830+ { C::execution_pc, 1 },
831+ { C::execution_next_pc, 2 },
832+ { C::execution_sel_execute_call, 0 }, // This is not a regular call
833+ { C::execution_sel_execute_static_call, 1 }, // STATICCALL
834+ { C::execution_sel_enter_call, 1 },
835+ { C::execution_context_id, 1 },
836+ { C::execution_next_context_id, 2 },
837+ { C::execution_bytecode_id, top_bytecode_id },
838+ { C::execution_is_static, 0 }, // Current context is non-static
839+ { C::execution_rop_4_, /* cd offset=*/ 10 },
840+ { C::execution_register_2_, /* contract address=*/ 0xdeadbeef },
841+ { C::execution_register_3_, /* cd size=*/ 1 },
842+ { C::execution_parent_l2_gas_limit, 2000 },
843+ { C::execution_parent_da_gas_limit, 4000 },
844+ { C::execution_parent_l2_gas_used, 500 },
845+ { C::execution_parent_da_gas_used, 1500 },
846+ },
847+ // First Row in new static context
848+ {
849+ { C::execution_sel, 1 },
850+ { C::execution_pc, 0 },
851+ { C::execution_next_pc, 20 },
852+ { C::execution_context_id, 2 },
853+ { C::execution_next_context_id, 3 },
854+ { C::execution_parent_id, 1 },
855+ { C::execution_is_parent_id_inv, 1 },
856+ { C::execution_has_parent_ctx, 1 },
857+ { C::execution_contract_address, 0xdeadbeef },
858+ { C::execution_bytecode_id, nested_bytecode_id },
859+ { C::execution_is_static, 1 }, // Becomes static due to STATICCALL
860+ { C::execution_parent_calldata_addr, 10 },
861+ { C::execution_parent_calldata_size, 1 },
862+ } });
863+
864+ check_relation<context>(trace1);
865+
866+ // Test: Call from static context - must remain static
867+ TestTraceContainer trace2 (
868+ { {
869+ { C::execution_next_context_id, 0 },
870+ { C::precomputed_first_row, 1 },
871+ // Context Stack Rows
872+ { C::context_stack_sel, 1 },
873+ { C::context_stack_entered_context_id, 2 },
874+ { C::context_stack_context_id, 1 },
875+ { C::context_stack_parent_id, 0 },
876+ { C::context_stack_next_pc, 2 },
877+ { C::context_stack_msg_sender, 0 },
878+ { C::context_stack_contract_address, 0 },
879+ { C::context_stack_bytecode_id, top_bytecode_id },
880+ { C::context_stack_is_static, 1 }, // Static in context stack
881+ { C::context_stack_parent_calldata_addr, 0 },
882+ { C::context_stack_parent_calldata_size, 0 },
883+ { C::context_stack_parent_l2_gas_limit, 2000 },
884+ { C::context_stack_parent_da_gas_limit, 4000 },
885+ { C::context_stack_parent_l2_gas_used, 500 },
886+ { C::context_stack_parent_da_gas_used, 1500 },
887+ },
888+ // First Row of execution (in static context)
889+ {
890+ { C::execution_sel, 1 },
891+ { C::execution_pc, 0 },
892+ { C::execution_next_pc, 1 },
893+ { C::execution_context_id, 1 },
894+ { C::execution_next_context_id, 2 },
895+ { C::execution_bytecode_id, top_bytecode_id },
896+ { C::execution_is_static, 1 }, // Static context
897+ { C::execution_parent_l2_gas_limit, 2000 },
898+ { C::execution_parent_da_gas_limit, 4000 },
899+ { C::execution_parent_l2_gas_used, 500 },
900+ { C::execution_parent_da_gas_used, 1500 },
901+ { C::execution_enqueued_call_start, 1 },
902+ },
903+ // Regular CALL from static context
904+ {
905+ { C::execution_sel, 1 },
906+ { C::execution_pc, 1 },
907+ { C::execution_next_pc, 2 },
908+ { C::execution_sel_execute_call, 1 }, // Regular CALL
909+ { C::execution_sel_execute_static_call, 0 }, // Not STATICCALL
910+ { C::execution_sel_enter_call, 1 },
911+ { C::execution_context_id, 1 },
912+ { C::execution_next_context_id, 2 },
913+ { C::execution_bytecode_id, top_bytecode_id },
914+ { C::execution_is_static, 1 }, // Current context is static
915+ { C::execution_rop_4_, /* cd offset=*/ 10 },
916+ { C::execution_register_2_, /* contract address=*/ 0xdeadbeef },
917+ { C::execution_register_3_, /* cd size=*/ 1 },
918+ { C::execution_parent_l2_gas_limit, 2000 },
919+ { C::execution_parent_da_gas_limit, 4000 },
920+ { C::execution_parent_l2_gas_used, 500 },
921+ { C::execution_parent_da_gas_used, 1500 },
922+ },
923+ // First Row in new context - must be static
924+ {
925+ { C::execution_sel, 1 },
926+ { C::execution_pc, 0 },
927+ { C::execution_next_pc, 20 },
928+ { C::execution_context_id, 2 },
929+ { C::execution_next_context_id, 3 },
930+ { C::execution_parent_id, 1 },
931+ { C::execution_is_parent_id_inv, 1 },
932+ { C::execution_has_parent_ctx, 1 },
933+ { C::execution_contract_address, 0xdeadbeef },
934+ { C::execution_bytecode_id, nested_bytecode_id },
935+ { C::execution_is_static, 1 }, // Must remain static when calling from static context
936+ { C::execution_parent_calldata_addr, 10 },
937+ { C::execution_parent_calldata_size, 1 },
938+ } });
939+
940+ check_relation<context>(trace2);
941+ }
942+
636943TEST (ContextConstrainingTest, ContextIdPropagation)
637944{
638945 TestTraceContainer trace ({
0 commit comments