Skip to content

Commit 6910505

Browse files
committed
feat!: a static context propagates staticness to nested calls, even those that are not static calls.
1 parent 6fa94fa commit 6910505

File tree

6 files changed

+527
-201
lines changed

6 files changed

+527
-201
lines changed

barretenberg/cpp/pil/vm2/context.pil

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,12 @@ namespace execution;
193193
// otherwise = 0 ==> is_static' = is_static
194194
#[IS_STATIC_NEXT_ROW]
195195
NOT_LAST_EXEC * DEFAULT_CTX_ROW * (is_static' - is_static) = 0;
196-
NOT_LAST_EXEC * sel_enter_call * (is_static' - sel_execute_static_call) = 0;
196+
// An external call from a non-static context only creates a nested static context if the opcode is STATICCALL.
197+
#[IS_STATIC_IF_STATIC_CALL]
198+
NOT_LAST_EXEC * sel_enter_call * (1 - is_static) * (is_static' - sel_execute_static_call) = 0;
199+
// An external call from a static context always creates a nested static context.
200+
#[IS_STATIC_IF_CALL_FROM_STATIC_CONTEXT]
201+
NOT_LAST_EXEC * sel_enter_call * is_static * (is_static' - 1) = 0;
197202

198203
// nested_exit_call = 1 ==> constraints come from lookup
199204
// sel_enter_call = 1 ==> parent_calldata_addr' = rop[4] (resolved operand 5 from execution trace)

barretenberg/cpp/src/barretenberg/vm2/constraining/relations/context.test.cpp

Lines changed: 307 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
636943
TEST(ContextConstrainingTest, ContextIdPropagation)
637944
{
638945
TestTraceContainer trace({

0 commit comments

Comments
 (0)