Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
context.test.cpp
Go to the documentation of this file.
1#include <gmock/gmock.h>
2#include <gtest/gtest.h>
3
4#include <cstdint>
5
15
16namespace bb::avm2::constraining {
17namespace {
18
19using tracegen::TestTraceContainer;
21using C = Column;
23
24TEST(ContextConstrainingTest, EmptyRow)
25{
26 check_relation<context>(testing::empty_trace());
27}
28
29// This test currently does a lot, consider splitting up the various exit call conditions
30TEST(ContextConstrainingTest, ContextSwitchingCallReturn)
31{
32 constexpr uint32_t top_bytecode_id = 0x12345678;
33 constexpr uint32_t nested_bytecode_id = 0x456789ab;
34
35 TestTraceContainer trace(
36 { {
37 { C::execution_next_context_id, 0 },
38 { C::precomputed_first_row, 1 },
39 // Context Stack Rows
40 { C::context_stack_sel, 1 },
41 { C::context_stack_entered_context_id, 2 },
42 { C::context_stack_context_id, 1 },
43 { C::context_stack_parent_id, 0 },
44 { C::context_stack_next_pc, 2 },
45 { C::context_stack_msg_sender, 0 },
46 { C::context_stack_contract_address, 0 },
47 { C::context_stack_bytecode_id, top_bytecode_id },
48 { C::context_stack_is_static, 0 },
49 { C::context_stack_parent_calldata_addr, 0 },
50 { C::context_stack_parent_calldata_size, 0 },
51 { C::context_stack_parent_l2_gas_limit, 2000 },
52 { C::context_stack_parent_da_gas_limit, 4000 },
53 { C::context_stack_parent_l2_gas_used, 500 },
54 { C::context_stack_parent_da_gas_used, 1500 },
55 },
56 // First Row of execution
57 {
58 { C::execution_sel, 1 },
59 { C::execution_pc, 0 },
60 { C::execution_next_pc, 1 },
61 { C::execution_context_id, 1 },
62 { C::execution_next_context_id, 2 },
63 { C::execution_bytecode_id, top_bytecode_id },
64 { C::execution_is_static, 0 }, // Non-static context
65 { C::execution_parent_l2_gas_limit, 2000 },
66 { C::execution_parent_da_gas_limit, 4000 },
67 { C::execution_parent_l2_gas_used, 500 },
68 { C::execution_parent_da_gas_used, 1500 },
69 { C::execution_enqueued_call_start, 1 },
70 },
71 // CALL
72 {
73 { C::execution_sel, 1 },
74 { C::execution_pc, 1 },
75 { C::execution_next_pc, 2 },
76 { C::execution_sel_execute_call, 1 },
77 { C::execution_sel_execute_static_call, 0 }, // Regular CALL, not STATICCALL
78 { C::execution_sel_enter_call, 1 },
79 { C::execution_context_id, 1 },
80 { C::execution_next_context_id, 2 },
81 { C::execution_bytecode_id, top_bytecode_id }, // Same as previous row (propagated)
82 { C::execution_is_static, 0 }, // Still non-static
83 { C::execution_rop_4_, /*cd offset=*/10 },
84 { C::execution_register_2_, /*contract address=*/0xdeadbeef },
85 { C::execution_register_3_, /*cd size=*/1 },
86 { C::execution_parent_l2_gas_limit, 2000 },
87 { C::execution_parent_da_gas_limit, 4000 },
88 { C::execution_parent_l2_gas_used, 500 },
89 { C::execution_parent_da_gas_used, 1500 },
90 },
91 // First Row in new context
92 {
93 { C::execution_sel, 1 },
94 { C::execution_sel_first_row_in_context, 1 },
95 { C::execution_internal_call_id, 1 },
96 { C::execution_internal_call_return_id, 0 },
97 { C::execution_next_internal_call_id, 2 },
98 { C::execution_pc, 0 }, // pc=0 because it is after a CALL
99 { C::execution_next_pc, 20 },
100 { C::execution_context_id, 2 }, // Previous row next_context_id
101 { C::execution_next_context_id, 3 }, // Incremented due to previous call
102 { C::execution_parent_id, 1 }, // Previous row context id
103 { C::execution_is_parent_id_inv, 1 },
104 { C::execution_has_parent_ctx, 1 },
105 { C::execution_contract_address, 0xdeadbeef },
106 { C::execution_bytecode_id, nested_bytecode_id }, // New bytecode_id on entering new context
107 { C::execution_is_static, 0 }, // Remains non-static after regular CALL
108 { C::execution_parent_calldata_addr, 10 },
109 { C::execution_parent_calldata_size, 1 },
110 },
111 // Return Row
112 {
113 { C::execution_sel, 1 },
114 { C::execution_internal_call_id, 1 },
115 { C::execution_internal_call_return_id, 0 },
116 { C::execution_next_internal_call_id, 2 },
117 { C::execution_pc, 20 },
118 { C::execution_next_pc, 30 },
119 { C::execution_sel_execute_return, 1 },
120 { C::execution_rop_0_, 500 }, // Return data size offset
121 { C::execution_rop_1_, 600 }, // Return data offset
122 { C::execution_register_0_, 200 }, // Return data size
123 { C::execution_sel_exit_call, 1 },
124 { C::execution_nested_return, 1 },
125 { C::execution_context_id, 2 },
126 { C::execution_next_context_id, 3 },
127 { C::execution_parent_id, 1 },
128 { C::execution_is_parent_id_inv, 1 },
129 { C::execution_has_parent_ctx, 1 },
130 { C::execution_contract_address, 0xdeadbeef },
131 { C::execution_bytecode_id, nested_bytecode_id }, // Propagated within same context
132 { C::execution_parent_calldata_addr, 10 },
133 { C::execution_parent_calldata_size, 1 },
134 },
135 {
136 { C::execution_sel, 1 },
137 { C::execution_next_context_id, 3 },
138 { C::execution_context_id, 1 },
139 { C::execution_parent_id, 0 },
140 { C::execution_last_child_id, 2 }, // Previous context id
141 { C::execution_pc, 2 }, // Based on next_pc of CALL step
142 { C::execution_msg_sender, 0 },
143 { C::execution_contract_address, 0 },
144 { C::execution_bytecode_id, top_bytecode_id }, // Restored from context stack
145 { C::execution_is_static, 0 },
146 { C::execution_parent_calldata_addr, 0 },
147 { C::execution_parent_calldata_size, 0 },
148 { C::execution_last_child_returndata_size, 200 }, // Return data size
149 { C::execution_last_child_returndata_addr, 600 }, // Return data offset
150 { C::execution_last_child_success, 1 }, // Success because return was successful
151 { C::execution_parent_l2_gas_limit, 2000 },
152 { C::execution_parent_da_gas_limit, 4000 },
153 { C::execution_parent_l2_gas_used, 500 },
154 { C::execution_parent_da_gas_used, 1500 },
155 // End of enqueued call (last active row)
156 { C::execution_sel_exit_call, 1 },
157 { C::execution_sel_execute_return, 1 },
158 { C::execution_enqueued_call_end, 1 },
159 },
160 {
161 { C::execution_sel, 0 },
162 } });
163
164 check_relation<context>(trace);
165
166 check_interaction<tracegen::ExecutionTraceBuilder,
170}
171
172TEST(ContextConstrainingTest, ContextSwitchingExceptionalHalt)
173{
174 constexpr uint32_t top_bytecode_id = 0x12345678;
175 constexpr uint32_t nested_bytecode_id = 0x456789ab;
176
177 TestTraceContainer trace(
178 { {
179 { C::execution_next_context_id, 0 },
180 { C::precomputed_first_row, 1 },
181 // Context Stack Rows
182 { C::context_stack_sel, 1 },
183 { C::context_stack_entered_context_id, 2 },
184 { C::context_stack_context_id, 1 },
185 { C::context_stack_parent_id, 0 },
186 { C::context_stack_next_pc, 2 },
187 { C::context_stack_msg_sender, 0 },
188 { C::context_stack_contract_address, 0 },
189 { C::context_stack_bytecode_id, top_bytecode_id },
190 { C::context_stack_is_static, 0 },
191 { C::context_stack_parent_calldata_addr, 0 },
192 { C::context_stack_parent_calldata_size, 0 },
193 { C::context_stack_parent_l2_gas_limit, 2000 },
194 { C::context_stack_parent_da_gas_limit, 4000 },
195 { C::context_stack_parent_l2_gas_used, 500 },
196 { C::context_stack_parent_da_gas_used, 1500 },
197 },
198 // First Row of execution
199 {
200 { C::execution_sel, 1 },
201 { C::execution_pc, 0 },
202 { C::execution_next_pc, 1 },
203 { C::execution_context_id, 1 },
204 { C::execution_next_context_id, 2 },
205 { C::execution_bytecode_id, top_bytecode_id },
206 { C::execution_parent_l2_gas_limit, 2000 },
207 { C::execution_parent_da_gas_limit, 4000 },
208 { C::execution_parent_l2_gas_used, 500 },
209 { C::execution_parent_da_gas_used, 1500 },
210 { C::execution_enqueued_call_start, 1 },
211 },
212 // CALL
213 {
214 { C::execution_sel, 1 },
215 { C::execution_pc, 1 },
216 { C::execution_next_pc, 2 },
217 { C::execution_sel_execute_call, 1 },
218 { C::execution_sel_enter_call, 1 },
219 { C::execution_context_id, 1 },
220 { C::execution_next_context_id, 2 },
221 { C::execution_bytecode_id, top_bytecode_id }, // Same as previous row (propagated)
222 { C::execution_rop_4_, /*cd offset=*/10 },
223 { C::execution_register_2_, /*contract address=*/0xdeadbeef },
224 { C::execution_register_3_, /*cd size=*/1 },
225 { C::execution_parent_l2_gas_limit, 2000 },
226 { C::execution_parent_da_gas_limit, 4000 },
227 { C::execution_parent_l2_gas_used, 500 },
228 { C::execution_parent_da_gas_used, 1500 },
229 },
230 // First Row in new context
231 {
232 { C::execution_sel, 1 },
233 { C::execution_sel_first_row_in_context, 1 },
234 { C::execution_internal_call_id, 1 },
235 { C::execution_internal_call_return_id, 0 },
236 { C::execution_next_internal_call_id, 2 },
237 { C::execution_pc, 0 }, // pc=0 because it is after a CALL
238 { C::execution_next_pc, 20 },
239 { C::execution_context_id, 2 }, // Previous row next_context_id
240 { C::execution_next_context_id, 3 }, // Incremented due to previous call
241 { C::execution_parent_id, 1 }, // Previous row context id
242 { C::execution_is_parent_id_inv, 1 },
243 { C::execution_has_parent_ctx, 1 },
244 { C::execution_contract_address, 0xdeadbeef },
245 { C::execution_bytecode_id, nested_bytecode_id }, // New bytecode_id on entering new context
246 { C::execution_parent_calldata_addr, 10 },
247 { C::execution_parent_calldata_size, 1 },
248 },
249 // Exceptional Halt Row
250 {
251 { C::execution_sel, 1 },
252 { C::execution_pc, 20 },
253 { C::execution_internal_call_id, 1 },
254 { C::execution_internal_call_return_id, 0 },
255 { C::execution_next_internal_call_id, 2 },
256 { C::execution_next_pc, 30 },
257 // Note that `sel_execute_return` is 0 because this is an exceptional halt.
258 { C::execution_rop_0_, 500 }, // Return data size offset
259 { C::execution_rop_1_, 600 }, // Return data offset
260 { C::execution_register_0_, 200 }, // Return data size
261 { C::execution_sel_exit_call, 1 },
262 { C::execution_sel_error, 1 }, // Exceptional Halt
263 { C::execution_sel_failure, 1 },
264 { C::execution_nested_failure, 1 },
265 { C::execution_context_id, 2 },
266 { C::execution_next_context_id, 3 },
267 { C::execution_parent_id, 1 },
268 { C::execution_is_parent_id_inv, 1 },
269 { C::execution_has_parent_ctx, 1 },
270 { C::execution_contract_address, 0xdeadbeef },
271 { C::execution_bytecode_id, nested_bytecode_id }, // Propagated within same context
272 { C::execution_parent_calldata_addr, 10 },
273 { C::execution_parent_calldata_size, 1 },
274 },
275 {
276 { C::execution_sel, 1 },
277 { C::execution_next_context_id, 3 },
278 { C::execution_context_id, 1 },
279 { C::execution_parent_id, 0 },
280 { C::execution_last_child_id, 2 }, // Previous context id
281 { C::execution_pc, 2 }, // Based on next_pc of CALL step
282 { C::execution_next_pc, 3 },
283 { C::execution_msg_sender, 0 },
284 { C::execution_contract_address, 0 },
285 { C::execution_bytecode_id, top_bytecode_id }, // Restored from context stack
286 { C::execution_is_static, 0 },
287 { C::execution_parent_calldata_addr, 0 },
288 { C::execution_parent_calldata_size, 0 },
289 { C::execution_last_child_returndata_size, 0 }, // Return data size reset
290 { C::execution_last_child_returndata_addr, 0 }, // Return data offset reset
291 { C::execution_parent_l2_gas_limit, 2000 },
292 { C::execution_parent_da_gas_limit, 4000 },
293 { C::execution_parent_l2_gas_used, 500 },
294 { C::execution_parent_da_gas_used, 1500 },
295 // End of enqueued call (last active row)
296 { C::execution_sel_exit_call, 1 },
297 { C::execution_sel_execute_return, 1 },
298 { C::execution_enqueued_call_end, 1 },
299 },
300 {
301 { C::execution_sel, 0 },
302 } });
303
304 check_relation<context>(trace);
305
306 check_interaction<tracegen::ExecutionTraceBuilder,
310}
311
312TEST(ContextConstrainingTest, GasNextRow)
313{
314 TestTraceContainer trace({ { { C::precomputed_first_row, 1 } },
315 {
316 // First Row of execution
317 { C::execution_sel, 1 },
318 { C::execution_l2_gas_limit, 1000 },
319 { C::execution_da_gas_limit, 2000 },
320 { C::execution_parent_l2_gas_limit, 2000 },
321 { C::execution_parent_da_gas_limit, 4000 },
322 { C::execution_parent_l2_gas_used, 500 },
323 { C::execution_parent_da_gas_used, 1500 },
324 },
325 {
326 // CALL
327 { C::execution_sel, 1 },
328 { C::execution_sel_enter_call, 1 },
329 { C::execution_l2_gas_used, 200 },
330 { C::execution_da_gas_used, 300 },
331 { C::execution_l2_gas_limit, 1000 },
332 { C::execution_da_gas_limit, 2000 },
333 { C::execution_parent_l2_gas_limit, 2000 },
334 { C::execution_parent_da_gas_limit, 4000 },
335 { C::execution_parent_l2_gas_used, 500 },
336 { C::execution_parent_da_gas_used, 1500 },
337 },
338 {
339 // Return
340 { C::execution_sel, 1 },
341 { C::execution_sel_exit_call, 1 },
342 { C::execution_nested_return, 1 },
343 { C::execution_parent_l2_gas_limit, 1000 },
344 { C::execution_parent_da_gas_limit, 2000 },
345 { C::execution_parent_l2_gas_used, 200 },
346 { C::execution_parent_da_gas_used, 300 },
347 },
348 {
349 // After return
350 { C::execution_sel, 1 },
351 { C::execution_l2_gas_limit, 1000 },
352 { C::execution_da_gas_limit, 2000 },
353 { C::execution_parent_l2_gas_limit, 2000 },
354 { C::execution_parent_da_gas_limit, 4000 },
355 // End of enqueued call (last active row)
356 { C::execution_sel_exit_call, 1 },
357 { C::execution_sel_execute_return, 1 },
358 { C::execution_enqueued_call_end, 1 },
359 },
360 {
361 { C::execution_sel, 0 },
362 } });
363
364 check_relation<context>(trace,
377
378 // Negative test: after return, restore wrong limits
379 trace.set(C::execution_l2_gas_limit, 4, 1001);
381 "L2_GAS_LIMIT_RESTORE_ON_EXIT");
382 trace.set(C::execution_da_gas_limit, 4, 2001);
384 "DA_GAS_LIMIT_RESTORE_ON_EXIT");
385
386 // Negative test: inside a nested call, store wrong parent limit and used
387 trace.set(C::execution_parent_l2_gas_limit, 3, 2001);
389 "PARENT_L2_GAS_LIMIT_STORE_ON_ENTER");
390 trace.set(C::execution_parent_da_gas_limit, 3, 4001);
392 "PARENT_DA_GAS_LIMIT_STORE_ON_ENTER");
393 trace.set(C::execution_parent_l2_gas_used, 3, 201);
395 "PARENT_L2_GAS_USED_STORE_ON_ENTER");
396 trace.set(C::execution_parent_da_gas_used, 3, 301);
398 "PARENT_DA_GAS_USED_STORE_ON_ENTER");
399
400 // Negative test: when no calls have been made, limits, parent limits, and parent used shouldn't change
401 trace.set(C::execution_l2_gas_limit, 2, 1001);
403 "L2_GAS_LIMIT_NEXT_ROW_DEFAULT");
404 trace.set(C::execution_da_gas_limit, 2, 2001);
406 "DA_GAS_LIMIT_NEXT_ROW_DEFAULT");
407
408 trace.set(C::execution_parent_l2_gas_limit, 2, 2001);
410 "PARENT_L2_GAS_LIMIT_NEXT_ROW_DEFAULT");
411 trace.set(C::execution_parent_da_gas_limit, 2, 4001);
413 "PARENT_DA_GAS_LIMIT_NEXT_ROW_DEFAULT");
414
415 trace.set(C::execution_parent_l2_gas_used, 2, 501);
417 "PARENT_L2_GAS_USED_NEXT_ROW");
418 trace.set(C::execution_parent_da_gas_used, 2, 1501);
420 "PARENT_DA_GAS_USED_NEXT_ROW");
421}
422
423TEST(ContextConstrainingTest, GasUsedContinuity)
424{
425 TestTraceContainer trace({ { { C::precomputed_first_row, 1 } },
426 {
427 // First Row of execution
428 { C::execution_sel, 1 },
429 { C::execution_l2_gas_used, 100 },
430 { C::execution_da_gas_used, 200 },
431 },
432 {
433 // CALL
434 { C::execution_sel, 1 },
435 { C::execution_sel_enter_call, 1 },
436 { C::execution_l2_gas_used, 110 },
437 { C::execution_da_gas_used, 200 },
438 { C::execution_prev_l2_gas_used, 100 },
439 { C::execution_prev_da_gas_used, 200 },
440 },
441 {
442 // Return
443 { C::execution_sel, 1 },
444 { C::execution_sel_exit_call, 1 },
445 { C::execution_nested_return, 1 },
446 { C::execution_l2_gas_used, 50 },
447 { C::execution_da_gas_used, 60 },
448 { C::execution_parent_l2_gas_used, 110 },
449 { C::execution_parent_da_gas_used, 200 },
450 { C::execution_prev_l2_gas_used, 0 },
451 { C::execution_prev_da_gas_used, 0 },
452 },
453 {
454 // After return
455 { C::execution_sel, 1 },
456 { C::execution_l2_gas_used, 170 },
457 { C::execution_da_gas_used, 260 },
458 { C::execution_prev_l2_gas_used, 160 }, // 110 + 50
459 { C::execution_prev_da_gas_used, 260 }, // 200 + 60
460 // End of enqueued call (last active row)
461 { C::execution_sel_exit_call, 1 },
462 { C::execution_sel_execute_return, 1 },
463 { C::execution_enqueued_call_end, 1 },
464 },
465 {
466 { C::execution_sel, 0 },
467 } });
468
469 check_relation<context>(trace,
476
477 // Negative test: after return, ingest a wrong value
478 trace.set(C::execution_prev_l2_gas_used, 4, 110);
479
481 "L2_GAS_USED_INGEST_AFTER_EXIT");
482
483 trace.set(C::execution_prev_da_gas_used, 4, 60);
485 "DA_GAS_USED_INGEST_AFTER_EXIT");
486
487 // Negative test: inside a nested call, start with non-zero gas used
488 trace.set(C::execution_prev_l2_gas_used, 3, 110);
490 "L2_GAS_USED_ZERO_AFTER_CALL");
491
492 trace.set(C::execution_prev_da_gas_used, 3, 200);
494 "DA_GAS_USED_ZERO_AFTER_CALL");
495
496 // Negative test: when no calls are made, prev gas used should be gas used of the previous row
497 trace.set(C::execution_prev_l2_gas_used, 2, 0);
499 "L2_GAS_USED_DEFAULT_ROW");
500
501 trace.set(C::execution_prev_da_gas_used, 2, 0);
503 "DA_GAS_USED_DEFAULT_ROW");
504}
505
506TEST(ContextConstrainingTest, TreeStateContinuity)
507{
508 TestTraceContainer trace({ { { C::precomputed_first_row, 1 } },
509 {
510 // First Row of execution
511 { C::execution_sel, 1 },
512 { C::execution_note_hash_tree_root, 10 },
513 { C::execution_note_hash_tree_size, 9 },
514 { C::execution_num_note_hashes_emitted, 8 },
515 { C::execution_nullifier_tree_root, 7 },
516 { C::execution_nullifier_tree_size, 6 },
517 { C::execution_num_nullifiers_emitted, 5 },
518 { C::execution_public_data_tree_root, 4 },
519 { C::execution_public_data_tree_size, 3 },
520 { C::execution_written_public_data_slots_tree_root, 2 },
521 { C::execution_written_public_data_slots_tree_size, 1 },
522 { C::execution_l1_l2_tree_root, 27 },
523 { C::execution_retrieved_bytecodes_tree_root, 26 },
524 { C::execution_retrieved_bytecodes_tree_size, 25 },
525 },
526 {
527 // Second row of execution
528 { C::execution_sel, 1 },
529 { C::execution_prev_note_hash_tree_root, 10 },
530 { C::execution_prev_note_hash_tree_size, 9 },
531 { C::execution_prev_num_note_hashes_emitted, 8 },
532 { C::execution_prev_nullifier_tree_root, 7 },
533 { C::execution_prev_nullifier_tree_size, 6 },
534 { C::execution_prev_num_nullifiers_emitted, 5 },
535 { C::execution_prev_public_data_tree_root, 4 },
536 { C::execution_prev_public_data_tree_size, 3 },
537 { C::execution_prev_written_public_data_slots_tree_root, 2 },
538 { C::execution_prev_written_public_data_slots_tree_size, 1 },
539 { C::execution_l1_l2_tree_root, 27 },
540 { C::execution_prev_retrieved_bytecodes_tree_root, 26 },
541 { C::execution_prev_retrieved_bytecodes_tree_size, 25 },
542 { C::execution_enqueued_call_end, 1 },
543 { C::execution_sel_exit_call, 1 },
544 },
545 {
546 // Third row of execution
547 { C::execution_sel, 1 },
548 { C::execution_prev_note_hash_tree_root, 100 },
549 { C::execution_prev_note_hash_tree_size, 90 },
550 { C::execution_prev_num_note_hashes_emitted, 80 },
551 { C::execution_prev_nullifier_tree_root, 70 },
552 { C::execution_prev_nullifier_tree_size, 60 },
553 { C::execution_prev_num_nullifiers_emitted, 50 },
554 { C::execution_prev_public_data_tree_root, 40 },
555 { C::execution_prev_public_data_tree_size, 30 },
556 { C::execution_prev_written_public_data_slots_tree_root, 20 },
557 { C::execution_prev_written_public_data_slots_tree_size, 10 },
558 { C::execution_l1_l2_tree_root, 27 },
559 { C::execution_prev_retrieved_bytecodes_tree_root, 260 },
560 { C::execution_prev_retrieved_bytecodes_tree_size, 250 },
561 // End of enqueued call (last active row)
562 { C::execution_sel_exit_call, 1 },
563 { C::execution_sel_execute_return, 1 },
564 { C::execution_enqueued_call_end, 1 },
565 } });
566
567 check_relation<context>(trace,
581
582 // Negative test: change note hash tree root
583 trace.set(C::execution_prev_note_hash_tree_root, 2, 100);
585 "NOTE_HASH_TREE_ROOT_CONTINUITY");
586
587 // Negative test: change note hash tree size
588 trace.set(C::execution_prev_note_hash_tree_size, 2, 100);
590 "NOTE_HASH_TREE_SIZE_CONTINUITY");
591
592 // Negative test: change num note hashes emitted
593 trace.set(C::execution_prev_num_note_hashes_emitted, 2, 10);
595 "NUM_NOTE_HASHES_EMITTED_CONTINUITY");
596
597 // Negative test: change nullifier tree root
598 trace.set(C::execution_prev_nullifier_tree_root, 2, 100);
600 "NULLIFIER_TREE_ROOT_CONTINUITY");
601
602 // Negative test: change nullifier tree size
603 trace.set(C::execution_prev_nullifier_tree_size, 2, 100);
605 "NULLIFIER_TREE_SIZE_CONTINUITY");
606
607 // Negative test: change num nullifiers emitted
608 trace.set(C::execution_prev_num_nullifiers_emitted, 2, 100);
610 "NUM_NULLIFIERS_EMITTED_CONTINUITY");
611
612 // Negative test: change public data tree root
613 trace.set(C::execution_prev_public_data_tree_root, 2, 100);
615 "PUBLIC_DATA_TREE_ROOT_CONTINUITY");
616
617 // Negative test: change public data tree size
618 trace.set(C::execution_prev_public_data_tree_size, 2, 100);
620 "PUBLIC_DATA_TREE_SIZE_CONTINUITY");
621
622 // Negative test: change written public data slots tree root
623 trace.set(C::execution_prev_written_public_data_slots_tree_root, 2, 100);
626 "WRITTEN_PUBLIC_DATA_SLOTS_TREE_ROOT_CONTINUITY");
627
628 // Negative test: change written public data slots tree size
629 trace.set(C::execution_prev_written_public_data_slots_tree_size, 2, 100);
632 "WRITTEN_PUBLIC_DATA_SLOTS_TREE_SIZE_CONTINUITY");
633
634 // Negative test: change l1 l2 tree root
635 trace.set(C::execution_l1_l2_tree_root, 2, 100);
637 "L1_L2_TREE_ROOT_CONTINUITY");
638
639 // Negative test: change retrieved bytecodes tree root
640 trace.set(C::execution_prev_retrieved_bytecodes_tree_root, 2, 100);
642 "RETRIEVED_BYTECODES_TREE_ROOT_CONTINUITY");
643
644 // Negative test: change retrieved bytecodes tree size
645 trace.set(C::execution_prev_retrieved_bytecodes_tree_size, 2, 100);
647 "RETRIEVED_BYTECODES_TREE_SIZE_CONTINUITY");
648}
649
650TEST(ContextConstrainingTest, SideEffectStateContinuity)
651{
652 TestTraceContainer trace({
653 { { C::precomputed_first_row, 1 } },
654 {
655 // First Row of execution
656 { C::execution_sel, 1 },
657 { C::execution_num_unencrypted_log_fields, 10 },
658 { C::execution_num_l2_to_l1_messages, 11 },
659 },
660 {
661 // Second row of execution
662 { C::execution_sel, 1 },
663 { C::execution_prev_num_unencrypted_log_fields, 10 },
664 { C::execution_prev_num_l2_to_l1_messages, 11 },
665 },
666 });
667
668 check_relation<context>(
670
671 // Negative test: change num unencrypted logs
672 trace.set(C::execution_prev_num_unencrypted_log_fields, 2, 100);
674 "NUM_UNENCRYPTED_LOGS_CONTINUITY");
675
676 // Negative test: change num l2 to l1 messages
677 trace.set(C::execution_prev_num_l2_to_l1_messages, 2, 100);
679 "NUM_L2_TO_L1_MESSAGES_CONTINUITY");
680}
681
682TEST(ContextConstrainingTest, BytecodeIdPropagation)
683{
684 TestTraceContainer trace({ // First row - setup
685 {
686 { C::precomputed_first_row, 1 },
687 { C::execution_sel, 1 },
688 { C::execution_context_id, 1 },
689 { C::execution_next_context_id, 1 },
690 { C::execution_bytecode_id, 42 }, // Initial bytecode_id
691 },
692 // Second row - should propagate bytecode_id
693 {
694 { C::execution_sel, 1 },
695 { C::execution_context_id, 1 },
696 { C::execution_next_context_id, 1 },
697 { C::execution_bytecode_id, 42 }, // Same bytecode_id (propagated)
698
699 // End of enqueued call (last active row)
700 { C::execution_sel_exit_call, 1 },
701 { C::execution_sel_execute_return, 1 },
702 { C::execution_enqueued_call_end, 1 },
703 } });
704
705 check_relation<context>(trace);
706 // mutate the bytecode_id and confirm that it is a violation
707 trace.set(C::execution_bytecode_id, 1, 99);
708 EXPECT_THROW_WITH_MESSAGE(check_relation<context>(trace, context::SR_BYTECODE_ID_NEXT_ROW),
709 "BYTECODE_ID_NEXT_ROW"); // Should fail constraint
710}
711
712TEST(ContextConstrainingTest, IsStaticRegularCallFromNonStaticContext)
713{
714 // Non-static context making a regular CALL - should remain non-static
715 TestTraceContainer trace({
716 { { C::precomputed_first_row, 1 } },
717 {
718 { C::execution_sel, 1 },
719 { C::execution_context_id, 1 },
720 { C::execution_next_context_id, 2 },
721 { C::execution_is_static, 0 }, // Non-static context
722 { C::execution_sel_enter_call, 1 },
723 { C::execution_sel_execute_call, 1 }, // Regular CALL
724 { C::execution_sel_execute_static_call, 0 },
725 },
726 {
727 { C::execution_sel, 1 },
728 { C::execution_context_id, 2 },
729 { C::execution_next_context_id, 3 },
730 { C::execution_is_static, 0 }, // Should remain non-static
731 },
732 });
733 check_relation<context>(
735
736 // Negative test: change is_static
737 // regular call from non-static context cannot become static
738 trace.set(C::execution_is_static, 2, 1);
740 "IS_STATIC_IF_STATIC_CALL");
741
742 // reset is_static
743 trace.set(C::execution_is_static, 2, 0);
744}
745
746TEST(ContextConstrainingTest, IsStaticStaticCallFromNonStaticContext)
747{
748 // Non-static context making a STATICCALL - should become static
749 TestTraceContainer trace({
750 { { C::precomputed_first_row, 1 } },
751 {
752 { C::execution_sel, 1 },
753 { C::execution_context_id, 1 },
754 { C::execution_next_context_id, 2 },
755 { C::execution_is_static, 0 }, // Non-static context
756 { C::execution_sel_enter_call, 1 },
757 { C::execution_sel_execute_call, 0 },
758 { C::execution_sel_execute_static_call, 1 }, // STATICCALL
759 },
760 {
761 { C::execution_sel, 1 },
762 { C::execution_context_id, 2 },
763 { C::execution_next_context_id, 3 },
764 { C::execution_is_static, 1 }, // Should become static
765 },
766 });
767 check_relation<context>(
769
770 // Negative test: change is_static
771 // static call from non-static context MUST become static
772 trace.set(C::execution_is_static, 2, 0);
774 "IS_STATIC_IF_STATIC_CALL");
775
776 // reset is_static
777 trace.set(C::execution_is_static, 2, 1);
778}
779
780TEST(ContextConstrainingTest, IsStaticCallFromStaticContext)
781{
782 // Static context making any call - must remain static
783 TestTraceContainer trace({
784 { { C::precomputed_first_row, 1 } },
785 {
786 { C::execution_sel, 1 },
787 { C::execution_context_id, 1 },
788 { C::execution_next_context_id, 2 },
789 { C::execution_is_static, 1 }, // Static context
790 { C::execution_sel_enter_call, 1 },
791 { C::execution_sel_execute_call, 1 }, // Regular CALL
792 { C::execution_sel_execute_static_call, 0 },
793 },
794 {
795 { C::execution_sel, 1 },
796 { C::execution_context_id, 2 },
797 { C::execution_next_context_id, 3 },
798 { C::execution_is_static, 1 }, // Must remain static
799 },
800 });
801 check_relation<context>(
803
804 // Negative test: change is_static
805 // static call from static context MUST remain static
806 trace.set(C::execution_is_static, 2, 0);
808 "IS_STATIC_IF_CALL_FROM_STATIC_CONTEXT");
809
810 // reset is_static
811 trace.set(C::execution_is_static, 2, 1);
812}
813
814TEST(ContextConstrainingTest, IsStaticPropagationWithoutCalls)
815{
816 // is_static propagation without calls
817 TestTraceContainer trace({
818 { { C::precomputed_first_row, 1 } },
819 {
820 { C::execution_sel, 1 },
821 { C::execution_context_id, 1 },
822 { C::execution_next_context_id, 1 },
823 { C::execution_is_static, 1 }, // Static context
824 },
825 {
826 { C::execution_sel, 1 },
827 { C::execution_context_id, 1 },
828 { C::execution_next_context_id, 1 },
829 { C::execution_is_static, 1 }, // Should propagate
830
831 // End of enqueued call (last active row)
832 { C::execution_sel_exit_call, 1 },
833 { C::execution_sel_execute_return, 1 },
834 { C::execution_enqueued_call_end, 1 },
835 },
836 });
837 check_relation<context>(trace, context::SR_IS_STATIC_NEXT_ROW_DEFAULT);
838
839 // Negative test: change is_static
840 // staticness must propagate without calls
841 trace.set(C::execution_is_static, 2, 0);
843 "IS_STATIC_NEXT_ROW_DEFAULT");
844
845 // reset is_static
846 trace.set(C::execution_is_static, 2, 1);
847}
848
849// =================================================================================================
850// Test: Tree state must be inherited when entering a nested call (sel_enter_call=1)
851// =================================================================================================
852// Previously this was a vulnerability - tree state was unconstrained on enter_call.
853// Now fixed with *_ON_ENTER_CALL constraints that enforce continuity.
854// =================================================================================================
855TEST(ContextConstrainingTest, NegativeTreeStateOnEnterCall)
856{
857 // Setup: Row 1 (CALL instruction) has tree state, Row 2 (nested context) has DIFFERENT tree state
858 // The new *_ON_ENTER_CALL constraints should catch this manipulation
859 TestTraceContainer trace({
860 { { C::precomputed_first_row, 1 } },
861 {
862 // CALL instruction - has current tree state
863 { C::execution_sel, 1 },
864 { C::execution_context_id, 1 },
865 { C::execution_next_context_id, 2 },
866 { C::execution_sel_enter_call, 1 },
867 { C::execution_sel_execute_call, 1 },
868 // Current tree state at end of this instruction
869 { C::execution_note_hash_tree_root, 100 },
870 { C::execution_note_hash_tree_size, 50 },
871 { C::execution_num_note_hashes_emitted, 10 },
872 { C::execution_nullifier_tree_root, 200 },
873 { C::execution_nullifier_tree_size, 60 },
874 { C::execution_num_nullifiers_emitted, 20 },
875 { C::execution_public_data_tree_root, 300 },
876 { C::execution_public_data_tree_size, 70 },
877 { C::execution_written_public_data_slots_tree_root, 400 },
878 { C::execution_written_public_data_slots_tree_size, 80 },
879 { C::execution_num_unencrypted_log_fields, 5 },
880 { C::execution_num_l2_to_l1_messages, 3 },
881 },
882 {
883 // First row of nested context - MALICIOUS: completely different tree state!
884 // A malicious prover tries to set arbitrary values here
885 { C::execution_sel, 1 },
886 { C::execution_context_id, 2 },
887 { C::execution_next_context_id, 3 },
888 { C::execution_parent_id, 1 },
889 { C::execution_has_parent_ctx, 1 },
890 { C::execution_is_parent_id_inv, 1 },
891 // ATTACK ATTEMPT: These should equal the previous row's values
892 { C::execution_prev_note_hash_tree_root, 999999 }, // Should be 100
893 { C::execution_prev_note_hash_tree_size, 888888 }, // Should be 50
894 { C::execution_prev_num_note_hashes_emitted, 777777 }, // Should be 10
895 { C::execution_prev_nullifier_tree_root, 666666 }, // Should be 200
896 { C::execution_prev_nullifier_tree_size, 555555 }, // Should be 60
897 { C::execution_prev_num_nullifiers_emitted, 444444 }, // Should be 20
898 { C::execution_prev_public_data_tree_root, 333333 }, // Should be 300
899 { C::execution_prev_public_data_tree_size, 222222 }, // Should be 70
900 { C::execution_prev_written_public_data_slots_tree_root, 111111 }, // Should be 400
901 { C::execution_prev_written_public_data_slots_tree_size, 99999 }, // Should be 80
902 { C::execution_prev_num_unencrypted_log_fields, 88888 }, // Should be 5
903 { C::execution_prev_num_l2_to_l1_messages, 77777 }, // Should be 3
904 },
905 {
906 { C::execution_sel, 0 },
907 },
908 });
909
910 // Each of the new *_ON_ENTER_CALL constraints should catch the manipulation
912 "NOTE_HASH_TREE_ROOT_CONTINUITY");
914 "NOTE_HASH_TREE_SIZE_CONTINUITY");
916 "NUM_NOTE_HASHES_EMITTED_CONTINUITY");
918 "NULLIFIER_TREE_ROOT_CONTINUITY");
920 "NULLIFIER_TREE_SIZE_CONTINUITY");
922 "NUM_NULLIFIERS_EMITTED_CONTINUITY");
924 "PUBLIC_DATA_TREE_ROOT_CONTINUITY");
926 "PUBLIC_DATA_TREE_SIZE_CONTINUITY");
929 "WRITTEN_PUBLIC_DATA_SLOTS_TREE_ROOT_CONTINUITY");
932 "WRITTEN_PUBLIC_DATA_SLOTS_TREE_SIZE_CONTINUITY");
934 "NUM_UNENCRYPTED_LOGS_CONTINUITY");
936 "NUM_L2_TO_L1_MESSAGES_CONTINUITY");
937}
938
939// Positive test: Correct tree state inheritance on enter call
940TEST(ContextConstrainingTest, TreeStateOnEnterCallCorrect)
941{
942 // Tree state is correctly inherited (prev values on row 2 match current values on row 1)
943 TestTraceContainer trace({
944 { { C::precomputed_first_row, 1 } },
945 {
946 // CALL instruction
947 { C::execution_sel, 1 },
948 { C::execution_context_id, 1 },
949 { C::execution_next_context_id, 2 },
950 { C::execution_sel_enter_call, 1 },
951 { C::execution_sel_execute_call, 1 },
952 // Current tree state
953 { C::execution_note_hash_tree_root, 100 },
954 { C::execution_note_hash_tree_size, 50 },
955 { C::execution_num_note_hashes_emitted, 10 },
956 { C::execution_nullifier_tree_root, 200 },
957 { C::execution_nullifier_tree_size, 60 },
958 { C::execution_num_nullifiers_emitted, 20 },
959 { C::execution_public_data_tree_root, 300 },
960 { C::execution_public_data_tree_size, 70 },
961 { C::execution_written_public_data_slots_tree_root, 400 },
962 { C::execution_written_public_data_slots_tree_size, 80 },
963 { C::execution_num_unencrypted_log_fields, 5 },
964 { C::execution_num_l2_to_l1_messages, 3 },
965 },
966 {
967 // First row of nested context - CORRECT: inherits parent's tree state
968 { C::execution_sel, 1 },
969 { C::execution_context_id, 2 },
970 { C::execution_next_context_id, 3 },
971 { C::execution_parent_id, 1 },
972 { C::execution_has_parent_ctx, 1 },
973 { C::execution_is_parent_id_inv, 1 },
974 // Correctly inherited from previous row
975 { C::execution_prev_note_hash_tree_root, 100 },
976 { C::execution_prev_note_hash_tree_size, 50 },
977 { C::execution_prev_num_note_hashes_emitted, 10 },
978 { C::execution_prev_nullifier_tree_root, 200 },
979 { C::execution_prev_nullifier_tree_size, 60 },
980 { C::execution_prev_num_nullifiers_emitted, 20 },
981 { C::execution_prev_public_data_tree_root, 300 },
982 { C::execution_prev_public_data_tree_size, 70 },
983 { C::execution_prev_written_public_data_slots_tree_root, 400 },
984 { C::execution_prev_written_public_data_slots_tree_size, 80 },
985 { C::execution_prev_num_unencrypted_log_fields, 5 },
986 { C::execution_prev_num_l2_to_l1_messages, 3 },
987 },
988 {
989 { C::execution_sel, 0 },
990 },
991 });
992
993 // All constraints should pass with correct values
994 check_relation<context>(trace,
1007}
1008
1009TEST(ContextConstrainingTest, ContextIdPropagation)
1010{
1011 TestTraceContainer trace({
1012 {
1013 { C::precomputed_first_row, 1 },
1014 },
1015 {
1016 { C::execution_sel, 1 },
1017 { C::execution_enqueued_call_start, 1 },
1018 { C::execution_sel_first_row_in_context, 1 },
1019 { C::execution_context_id, 1 },
1020 { C::execution_next_context_id, 2 },
1021 { C::execution_sel_enter_call, 1 },
1022 },
1023 {
1024 { C::execution_sel, 1 },
1025 { C::execution_sel_first_row_in_context, 1 },
1026 { C::execution_context_id, 2 },
1027 { C::execution_next_context_id, 3 },
1028 { C::execution_sel_exit_call, 1 },
1029 { C::execution_nested_return, 1 },
1030 { C::execution_parent_id, 1 },
1031 },
1032 {
1033 { C::execution_sel, 1 },
1034 { C::execution_context_id, 1 },
1035 { C::execution_next_context_id, 3 },
1036 },
1037 {
1038 { C::execution_sel, 1 },
1039 { C::execution_context_id, 1 },
1040 { C::execution_next_context_id, 3 },
1041 // End of enqueued call (last active row)
1042 { C::execution_sel_exit_call, 1 },
1043 { C::execution_sel_execute_return, 1 },
1044 { C::execution_enqueued_call_end, 1 },
1045 },
1046 });
1047 check_relation<context>(trace,
1053
1054 // Negative test: next context id should be context id + 1 on enqueued call start
1055 trace.set(C::execution_next_context_id, 1, 3);
1057 "ENQUEUED_CALL_START_NEXT_CTX_ID");
1058 trace.set(C::execution_next_context_id, 1, 2);
1059
1060 // Negative test: next context id should increase on external call
1061 trace.set(C::execution_next_context_id, 2, 2);
1062 EXPECT_THROW_WITH_MESSAGE(check_relation<context>(trace, context::SR_INCR_NEXT_CONTEXT_ID), "INCR_NEXT_CONTEXT_ID");
1063 trace.set(C::execution_next_context_id, 2, 3);
1064
1065 // Negative test: next context id should be propagated
1066 trace.set(C::execution_next_context_id, 4, 4);
1067 EXPECT_THROW_WITH_MESSAGE(check_relation<context>(trace, context::SR_INCR_NEXT_CONTEXT_ID), "INCR_NEXT_CONTEXT_ID");
1068 trace.set(C::execution_next_context_id, 4, 3);
1069
1070 // Negative test: context id should be propagated
1071 trace.set(C::execution_context_id, 4, 2);
1073 "CONTEXT_ID_NEXT_DEFAULT_ROW");
1074 trace.set(C::execution_context_id, 4, 1);
1075
1076 // Negative test: context id should be next context id when entering call
1077 trace.set(C::execution_context_id, 2, 1);
1078 EXPECT_THROW_WITH_MESSAGE(check_relation<context>(trace, context::SR_CONTEXT_ID_EXT_CALL), "CONTEXT_ID_EXT_CALL");
1079 trace.set(C::execution_context_id, 2, 2);
1080
1081 // Negative test: context id should be restored on exit
1082 trace.set(C::execution_context_id, 3, 2);
1083 EXPECT_THROW_WITH_MESSAGE(check_relation<context>(trace, context::SR_CONTEXT_ID_NESTED_EXIT),
1084 "CONTEXT_ID_NESTED_EXIT");
1085 trace.set(C::execution_context_id, 3, 1);
1086}
1087
1088} // namespace
1089} // namespace bb::avm2::constraining
#define EXPECT_THROW_WITH_MESSAGE(code, expectedMessageRegex)
Definition assert.hpp:193
static constexpr size_t SR_INCR_NEXT_CONTEXT_ID
Definition context.hpp:43
static constexpr size_t SR_L1_L2_TREE_ROOT_CONTINUITY
Definition context.hpp:120
static constexpr size_t SR_DA_GAS_USED_INGEST_AFTER_EXIT
Definition context.hpp:106
static constexpr size_t SR_L2_GAS_USED_ZERO_AFTER_CALL
Definition context.hpp:102
static constexpr size_t SR_DA_GAS_USED_DEFAULT_ROW
Definition context.hpp:107
static constexpr size_t SR_PARENT_L2_GAS_LIMIT_STORE_ON_ENTER
Definition context.hpp:86
static constexpr size_t SR_PARENT_L2_GAS_USED_STORE_ON_ENTER
Definition context.hpp:90
static constexpr size_t SR_NOTE_HASH_TREE_SIZE_CONTINUITY
Definition context.hpp:109
static constexpr size_t SR_IS_STATIC_IF_CALL_FROM_STATIC_CONTEXT
Definition context.hpp:60
static constexpr size_t SR_RETRIEVED_BYTECODES_TREE_ROOT_CONTINUITY
Definition context.hpp:100
static constexpr size_t SR_CONTEXT_ID_NEXT_DEFAULT_ROW
Definition context.hpp:46
static constexpr size_t SR_IS_STATIC_NEXT_ROW_DEFAULT
Definition context.hpp:61
static constexpr size_t SR_DA_GAS_LIMIT_NEXT_ROW_DEFAULT
Definition context.hpp:85
static constexpr size_t SR_WRITTEN_PUBLIC_DATA_SLOTS_TREE_ROOT_CONTINUITY
Definition context.hpp:116
static constexpr size_t SR_L2_GAS_LIMIT_RESTORE_ON_EXIT
Definition context.hpp:82
static constexpr size_t SR_L2_GAS_USED_INGEST_AFTER_EXIT
Definition context.hpp:103
static constexpr size_t SR_PUBLIC_DATA_TREE_SIZE_CONTINUITY
Definition context.hpp:115
static constexpr size_t SR_PARENT_DA_GAS_USED_STORE_ON_ENTER
Definition context.hpp:92
static constexpr size_t SR_NUM_NOTE_HASHES_EMITTED_CONTINUITY
Definition context.hpp:110
static constexpr size_t SR_NULLIFIER_TREE_SIZE_CONTINUITY
Definition context.hpp:112
static constexpr size_t SR_CONTEXT_ID_EXT_CALL
Definition context.hpp:44
static constexpr size_t SR_DA_GAS_USED_ZERO_AFTER_CALL
Definition context.hpp:105
static constexpr size_t SR_NUM_L2_TO_L1_MESSAGES_CONTINUITY
Definition context.hpp:119
static constexpr size_t SR_ENQUEUED_CALL_START_NEXT_CTX_ID
Definition context.hpp:42
static constexpr size_t SR_PARENT_L2_GAS_LIMIT_NEXT_ROW_DEFAULT
Definition context.hpp:87
static constexpr size_t SR_L2_GAS_USED_DEFAULT_ROW
Definition context.hpp:104
static constexpr size_t SR_PARENT_DA_GAS_LIMIT_STORE_ON_ENTER
Definition context.hpp:88
static constexpr size_t SR_NOTE_HASH_TREE_ROOT_CONTINUITY
Definition context.hpp:108
static constexpr size_t SR_CONTEXT_ID_NESTED_EXIT
Definition context.hpp:45
static constexpr size_t SR_WRITTEN_PUBLIC_DATA_SLOTS_TREE_SIZE_CONTINUITY
Definition context.hpp:117
static constexpr size_t SR_BYTECODE_ID_NEXT_ROW
Definition context.hpp:57
static constexpr size_t SR_PARENT_DA_GAS_LIMIT_NEXT_ROW_DEFAULT
Definition context.hpp:89
static constexpr size_t SR_RETRIEVED_BYTECODES_TREE_SIZE_CONTINUITY
Definition context.hpp:101
static constexpr size_t SR_PUBLIC_DATA_TREE_ROOT_CONTINUITY
Definition context.hpp:114
static constexpr size_t SR_NUM_NULLIFIERS_EMITTED_CONTINUITY
Definition context.hpp:113
static constexpr size_t SR_NUM_UNENCRYPTED_LOGS_CONTINUITY
Definition context.hpp:118
static constexpr size_t SR_DA_GAS_LIMIT_RESTORE_ON_EXIT
Definition context.hpp:84
static constexpr size_t SR_IS_STATIC_IF_STATIC_CALL
Definition context.hpp:59
static constexpr size_t SR_NULLIFIER_TREE_ROOT_CONTINUITY
Definition context.hpp:111
static constexpr size_t SR_PARENT_L2_GAS_USED_NEXT_ROW_DEFAULT
Definition context.hpp:91
static constexpr size_t SR_L2_GAS_LIMIT_NEXT_ROW_DEFAULT
Definition context.hpp:83
static constexpr size_t SR_PARENT_DA_GAS_USED_NEXT_ROW_DEFAULT
Definition context.hpp:93
void set(Column col, uint32_t row, const FF &value)
TestTraceContainer trace
StrictMock< MockContext > context
void check_interaction(tracegen::TestTraceContainer &trace)
TEST(AvmFixedVKTests, FixedVKCommitments)
Test that the fixed VK commitments agree with the ones computed from precomputed columns.
TestTraceContainer empty_trace()
Definition fixtures.cpp:153
lookup_settings< lookup_context_ctx_stack_rollback_settings_ > lookup_context_ctx_stack_rollback_settings
lookup_settings< lookup_context_ctx_stack_return_settings_ > lookup_context_ctx_stack_return_settings
permutation_settings< perm_context_ctx_stack_call_settings_ > perm_context_ctx_stack_call_settings