From f330bffa80b6da5f037cea3cf469ef1c7b6d9d03 Mon Sep 17 00:00:00 2001 From: Maddiaa <47148561+Maddiaa0@users.noreply.github.com> Date: Fri, 7 Jun 2024 13:33:40 +0100 Subject: [PATCH] feat(avm): indirect support for kernel output opcodes (#6962) Adds indirect support to kernel output opcodes, apart from sload and sstore, being separately handled by @IlyasRidhuan --- .../vm/avm_trace/avm_execution.cpp | 21 ++- .../barretenberg/vm/avm_trace/avm_trace.cpp | 176 +++++++++++++----- .../barretenberg/vm/avm_trace/avm_trace.hpp | 29 +-- .../barretenberg/vm/tests/avm_kernel.test.cpp | 164 ++++++++++++---- 4 files changed, 288 insertions(+), 102 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_execution.cpp b/barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_execution.cpp index 111d99dfaca..3ebe04cea52 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_execution.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_execution.cpp @@ -471,22 +471,26 @@ std::vector Execution::gen_trace(std::vector const& instructio trace_builder.op_timestamp(std::get(inst.operands.at(0)), std::get(inst.operands.at(1))); break; case OpCode::NOTEHASHEXISTS: - trace_builder.op_note_hash_exists(std::get(inst.operands.at(1)), + trace_builder.op_note_hash_exists(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), // TODO: leaf offset exists // std::get(inst.operands.at(2)) std::get(inst.operands.at(3))); break; case OpCode::EMITNOTEHASH: - trace_builder.op_emit_note_hash(std::get(inst.operands.at(1))); + trace_builder.op_emit_note_hash(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1))); break; case OpCode::NULLIFIEREXISTS: - trace_builder.op_nullifier_exists(std::get(inst.operands.at(1)), + trace_builder.op_nullifier_exists(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), // std::get(inst.operands.at(2)) /**TODO: Address offset for siloing */ std::get(inst.operands.at(3))); break; case OpCode::EMITNULLIFIER: - trace_builder.op_emit_nullifier(std::get(inst.operands.at(1))); + trace_builder.op_emit_nullifier(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1))); break; case OpCode::SLOAD: trace_builder.op_sload(std::get(inst.operands.at(1)), std::get(inst.operands.at(2))); @@ -495,7 +499,8 @@ std::vector Execution::gen_trace(std::vector const& instructio trace_builder.op_sstore(std::get(inst.operands.at(1)), std::get(inst.operands.at(2))); break; case OpCode::L1TOL2MSGEXISTS: - trace_builder.op_l1_to_l2_msg_exists(std::get(inst.operands.at(1)), + trace_builder.op_l1_to_l2_msg_exists(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), // TODO: leaf offset exists // std::get(inst.operands.at(2)) std::get(inst.operands.at(3))); @@ -506,10 +511,12 @@ std::vector Execution::gen_trace(std::vector const& instructio std::get(inst.operands.at(2))); break; case OpCode::EMITUNENCRYPTEDLOG: - trace_builder.op_emit_unencrypted_log(std::get(inst.operands.at(1))); + trace_builder.op_emit_unencrypted_log(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1))); break; case OpCode::SENDL2TOL1MSG: - trace_builder.op_emit_l2_to_l1_msg(std::get(inst.operands.at(1)), + trace_builder.op_emit_l2_to_l1_msg(std::get(inst.operands.at(0)), + std::get(inst.operands.at(1)), std::get(inst.operands.at(2))); break; // Machine State - Internal Control Flow diff --git a/barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_trace.cpp b/barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_trace.cpp index 3f3ec38df83..a19197602c5 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_trace.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_trace.cpp @@ -1149,6 +1149,7 @@ void AvmTraceBuilder::op_cmov( } // Helper function to add kernel lookup operations into the main trace +// TODO: add tag match to kernel_input_lookup opcodes to - it isnt written to - -ve test would catch Row AvmTraceBuilder::create_kernel_lookup_opcode( bool indirect, uint32_t dst_offset, uint32_t selector, FF value, AvmMemoryTag w_tag) { @@ -1344,17 +1345,29 @@ void AvmTraceBuilder::op_timestamp(uint8_t indirect, uint32_t dst_offset) } // Helper function to add kernel lookup operations into the main trace -Row AvmTraceBuilder::create_kernel_output_opcode(uint32_t clk, uint32_t data_offset) +Row AvmTraceBuilder::create_kernel_output_opcode(uint8_t indirect, uint32_t clk, uint32_t data_offset) { + bool indirect_data_flag = is_operand_indirect(indirect, 0); + + bool tag_match = true; + uint32_t direct_data_offset = data_offset; + if (indirect) { + auto read_ind_dst = + mem_trace_builder.indirect_read_and_load_from_memory(call_ptr, clk, IndirectRegister::IND_A, data_offset); + direct_data_offset = uint32_t(read_ind_dst.val); + tag_match = tag_match && read_ind_dst.tag_match; + } + AvmMemTraceBuilder::MemRead read_a = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IA, data_offset, AvmMemoryTag::FF, AvmMemoryTag::U0); + call_ptr, clk, IntermRegister::IA, direct_data_offset, AvmMemoryTag::FF, AvmMemoryTag::U0); return Row{ .avm_main_clk = clk, .avm_main_ia = read_a.val, - .avm_main_ind_a = 0, + .avm_main_ind_a = indirect_data_flag ? FF(data_offset) : FF(0), + .avm_main_ind_op_a = FF(static_cast(indirect)), .avm_main_internal_return_ptr = internal_return_ptr, - .avm_main_mem_idx_a = data_offset, + .avm_main_mem_idx_a = direct_data_offset, .avm_main_mem_op_a = 1, .avm_main_pc = pc++, .avm_main_q_kernel_output_lookup = 1, @@ -1363,24 +1376,52 @@ Row AvmTraceBuilder::create_kernel_output_opcode(uint32_t clk, uint32_t data_off }; } -Row AvmTraceBuilder::create_kernel_output_opcode_with_metadata( - uint32_t clk, uint32_t data_offset, AvmMemoryTag data_r_tag, uint32_t metadata_offset, AvmMemoryTag metadata_r_tag) +Row AvmTraceBuilder::create_kernel_output_opcode_with_metadata(uint8_t indirect, + uint32_t clk, + uint32_t data_offset, + AvmMemoryTag data_r_tag, + uint32_t metadata_offset, + AvmMemoryTag metadata_r_tag) { + + bool indirect_a_flag = is_operand_indirect(indirect, 0); + bool indirect_b_flag = is_operand_indirect(indirect, 1); + + bool tag_match = true; + uint32_t direct_data_offset = data_offset; + uint32_t direct_metadata_offset = metadata_offset; + if (indirect_a_flag) { + auto read_a_ind_dst = + mem_trace_builder.indirect_read_and_load_from_memory(call_ptr, clk, IndirectRegister::IND_A, data_offset); + direct_data_offset = static_cast(read_a_ind_dst.val); + + tag_match = tag_match && read_a_ind_dst.tag_match; + } + if (indirect_b_flag) { + auto read_b_ind_dst = mem_trace_builder.indirect_read_and_load_from_memory( + call_ptr, clk, IndirectRegister::IND_B, metadata_offset); + direct_metadata_offset = static_cast(read_b_ind_dst.val); + + tag_match = tag_match && read_b_ind_dst.tag_match; + } + AvmMemTraceBuilder::MemRead read_a = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IA, data_offset, data_r_tag, AvmMemoryTag::U0); + call_ptr, clk, IntermRegister::IA, direct_data_offset, data_r_tag, AvmMemoryTag::U0); AvmMemTraceBuilder::MemRead read_b = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IB, metadata_offset, metadata_r_tag, AvmMemoryTag::U0); + call_ptr, clk, IntermRegister::IB, direct_metadata_offset, metadata_r_tag, AvmMemoryTag::U0); return Row{ .avm_main_clk = clk, .avm_main_ia = read_a.val, .avm_main_ib = read_b.val, - .avm_main_ind_a = 0, - .avm_main_ind_b = 0, + .avm_main_ind_a = indirect_a_flag ? data_offset : FF(0), + .avm_main_ind_b = indirect_b_flag ? metadata_offset : FF(0), + .avm_main_ind_op_a = FF(static_cast(indirect_a_flag)), + .avm_main_ind_op_b = FF(static_cast(indirect_b_flag)), .avm_main_internal_return_ptr = internal_return_ptr, - .avm_main_mem_idx_a = data_offset, - .avm_main_mem_idx_b = metadata_offset, + .avm_main_mem_idx_a = direct_data_offset, + .avm_main_mem_idx_b = direct_metadata_offset, .avm_main_mem_op_a = 1, .avm_main_mem_op_b = 1, .avm_main_pc = pc++, @@ -1391,28 +1432,54 @@ Row AvmTraceBuilder::create_kernel_output_opcode_with_metadata( }; } -Row AvmTraceBuilder::create_kernel_output_opcode_with_set_metadata_output_from_hint(uint32_t clk, +Row AvmTraceBuilder::create_kernel_output_opcode_with_set_metadata_output_from_hint(uint8_t indirect, + uint32_t clk, uint32_t data_offset, uint32_t metadata_offset) { - AvmMemTraceBuilder::MemRead read_a = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IA, data_offset, AvmMemoryTag::FF, AvmMemoryTag::U8); FF exists = execution_hints.get_side_effect_hints().at(side_effect_counter); // TODO: throw error if incorrect + bool indirect_a_flag = is_operand_indirect(indirect, 0); + bool indirect_b_flag = is_operand_indirect(indirect, 1); + + bool tag_match = true; + uint32_t direct_data_offset = data_offset; + uint32_t direct_metadata_offset = metadata_offset; + if (indirect_a_flag) { + auto read_a_ind_dst = + mem_trace_builder.indirect_read_and_load_from_memory(call_ptr, clk, IndirectRegister::IND_A, data_offset); + direct_data_offset = uint32_t(read_a_ind_dst.val); + + tag_match = tag_match && read_a_ind_dst.tag_match; + } + + if (indirect_b_flag) { + auto read_b_ind_dst = mem_trace_builder.indirect_read_and_load_from_memory( + call_ptr, clk, IndirectRegister::IND_B, metadata_offset); + direct_metadata_offset = uint32_t(read_b_ind_dst.val); + + tag_match = tag_match && read_b_ind_dst.tag_match; + } + + AvmMemTraceBuilder::MemRead read_a = mem_trace_builder.read_and_load_from_memory( + call_ptr, clk, IntermRegister::IA, direct_data_offset, AvmMemoryTag::FF, AvmMemoryTag::U8); + mem_trace_builder.write_into_memory( - call_ptr, clk, IntermRegister::IB, metadata_offset, exists, AvmMemoryTag::FF, AvmMemoryTag::U8); + call_ptr, clk, IntermRegister::IB, direct_metadata_offset, exists, AvmMemoryTag::FF, AvmMemoryTag::U8); return Row{ .avm_main_clk = clk, .avm_main_ia = read_a.val, .avm_main_ib = exists, - .avm_main_ind_a = 0, - .avm_main_ind_b = 0, + .avm_main_ind_a = indirect_a_flag ? data_offset : FF(0), + .avm_main_ind_b = indirect_b_flag ? metadata_offset : FF(0), + .avm_main_ind_op_a = FF(static_cast(indirect_a_flag)), + .avm_main_ind_op_b = FF(static_cast(indirect_b_flag)), .avm_main_internal_return_ptr = internal_return_ptr, - .avm_main_mem_idx_a = data_offset, - .avm_main_mem_idx_b = metadata_offset, + .avm_main_mem_idx_a = direct_data_offset, + .avm_main_mem_idx_b = direct_metadata_offset, .avm_main_mem_op_a = 1, .avm_main_mem_op_b = 1, .avm_main_pc = pc++, @@ -1424,28 +1491,49 @@ Row AvmTraceBuilder::create_kernel_output_opcode_with_set_metadata_output_from_h }; } -Row AvmTraceBuilder::create_kernel_output_opcode_with_set_value_from_hint(uint32_t clk, +Row AvmTraceBuilder::create_kernel_output_opcode_with_set_value_from_hint(uint8_t indirect, + uint32_t clk, uint32_t data_offset, uint32_t metadata_offset) { FF value = execution_hints.get_side_effect_hints().at(side_effect_counter); // TODO: throw error if incorrect + bool indirect_a_flag = is_operand_indirect(indirect, 0); + bool indirect_b_flag = is_operand_indirect(indirect, 1); + + bool tag_match = true; + uint32_t direct_data_offset = data_offset; + uint32_t direct_metadata_offset = metadata_offset; + if (indirect) { + auto read_a_ind_dst = + mem_trace_builder.indirect_read_and_load_from_memory(call_ptr, clk, IndirectRegister::IND_A, data_offset); + auto read_b_ind_dst = mem_trace_builder.indirect_read_and_load_from_memory( + call_ptr, clk, IndirectRegister::IND_B, metadata_offset); + + direct_data_offset = uint32_t(read_a_ind_dst.val); + direct_metadata_offset = uint32_t(read_b_ind_dst.val); + + tag_match = tag_match && read_a_ind_dst.tag_match && read_b_ind_dst.tag_match; + } + mem_trace_builder.write_into_memory( - call_ptr, clk, IntermRegister::IA, data_offset, value, AvmMemoryTag::FF, AvmMemoryTag::FF); + call_ptr, clk, IntermRegister::IA, direct_data_offset, value, AvmMemoryTag::FF, AvmMemoryTag::FF); AvmMemTraceBuilder::MemRead read_b = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IB, metadata_offset, AvmMemoryTag::FF, AvmMemoryTag::FF); + call_ptr, clk, IntermRegister::IB, direct_metadata_offset, AvmMemoryTag::FF, AvmMemoryTag::FF); return Row{ .avm_main_clk = clk, .avm_main_ia = value, .avm_main_ib = read_b.val, - .avm_main_ind_a = 0, - .avm_main_ind_b = 0, + .avm_main_ind_a = indirect_a_flag ? data_offset : FF(0), + .avm_main_ind_b = indirect_b_flag ? metadata_offset : FF(0), + .avm_main_ind_op_a = FF(static_cast(indirect_a_flag)), + .avm_main_ind_op_b = FF(static_cast(indirect_b_flag)), .avm_main_internal_return_ptr = internal_return_ptr, - .avm_main_mem_idx_a = data_offset, - .avm_main_mem_idx_b = metadata_offset, + .avm_main_mem_idx_a = direct_data_offset, + .avm_main_mem_idx_b = direct_metadata_offset, .avm_main_mem_op_a = 1, .avm_main_mem_op_b = 1, .avm_main_pc = pc++, @@ -1457,11 +1545,11 @@ Row AvmTraceBuilder::create_kernel_output_opcode_with_set_value_from_hint(uint32 }; } -void AvmTraceBuilder::op_emit_note_hash(uint32_t note_hash_offset) +void AvmTraceBuilder::op_emit_note_hash(uint8_t indirect, uint32_t note_hash_offset) { auto const clk = static_cast(main_trace.size()) + 1; - Row row = create_kernel_output_opcode(clk, note_hash_offset); + Row row = create_kernel_output_opcode(indirect, clk, note_hash_offset); kernel_trace_builder.op_emit_note_hash(clk, side_effect_counter, row.avm_main_ia); row.avm_main_sel_op_emit_note_hash = FF(1); @@ -1472,11 +1560,11 @@ void AvmTraceBuilder::op_emit_note_hash(uint32_t note_hash_offset) side_effect_counter++; } -void AvmTraceBuilder::op_emit_nullifier(uint32_t nullifier_offset) +void AvmTraceBuilder::op_emit_nullifier(uint8_t indirect, uint32_t nullifier_offset) { auto const clk = static_cast(main_trace.size()) + 1; - Row row = create_kernel_output_opcode(clk, nullifier_offset); + Row row = create_kernel_output_opcode(indirect, clk, nullifier_offset); kernel_trace_builder.op_emit_nullifier(clk, side_effect_counter, row.avm_main_ia); row.avm_main_sel_op_emit_nullifier = FF(1); @@ -1487,13 +1575,13 @@ void AvmTraceBuilder::op_emit_nullifier(uint32_t nullifier_offset) side_effect_counter++; } -void AvmTraceBuilder::op_emit_l2_to_l1_msg(uint32_t recipient_offset, uint32_t msg_offset) +void AvmTraceBuilder::op_emit_l2_to_l1_msg(uint8_t indirect, uint32_t recipient_offset, uint32_t msg_offset) { auto const clk = static_cast(main_trace.size()) + 1; // Note: unorthadox order - as seen in L2ToL1Message struct in TS Row row = create_kernel_output_opcode_with_metadata( - clk, msg_offset, AvmMemoryTag::FF, recipient_offset, AvmMemoryTag::FF); + indirect, clk, msg_offset, AvmMemoryTag::FF, recipient_offset, AvmMemoryTag::FF); kernel_trace_builder.op_emit_l2_to_l1_msg(clk, side_effect_counter, row.avm_main_ia, row.avm_main_ib); row.avm_main_sel_op_emit_l2_to_l1_msg = FF(1); @@ -1504,11 +1592,11 @@ void AvmTraceBuilder::op_emit_l2_to_l1_msg(uint32_t recipient_offset, uint32_t m side_effect_counter++; } -void AvmTraceBuilder::op_emit_unencrypted_log(uint32_t log_offset) +void AvmTraceBuilder::op_emit_unencrypted_log(uint8_t indirect, uint32_t log_offset) { auto const clk = static_cast(main_trace.size()) + 1; - Row row = create_kernel_output_opcode(clk, log_offset); + Row row = create_kernel_output_opcode(indirect, clk, log_offset); kernel_trace_builder.op_emit_unencrypted_log(clk, side_effect_counter, row.avm_main_ia); row.avm_main_sel_op_emit_unencrypted_log = FF(1); @@ -1520,11 +1608,11 @@ void AvmTraceBuilder::op_emit_unencrypted_log(uint32_t log_offset) } // State output opcodes that include metadata -void AvmTraceBuilder::op_l1_to_l2_msg_exists(uint32_t log_offset, uint32_t dest_offset) +void AvmTraceBuilder::op_l1_to_l2_msg_exists(uint8_t indirect, uint32_t log_offset, uint32_t dest_offset) { auto const clk = static_cast(main_trace.size()) + 1; - Row row = create_kernel_output_opcode_with_set_metadata_output_from_hint(clk, log_offset, dest_offset); + Row row = create_kernel_output_opcode_with_set_metadata_output_from_hint(indirect, clk, log_offset, dest_offset); kernel_trace_builder.op_l1_to_l2_msg_exists( clk, side_effect_counter, row.avm_main_ia, /*safe*/ static_cast(row.avm_main_ib)); row.avm_main_sel_op_l1_to_l2_msg_exists = FF(1); @@ -1536,11 +1624,11 @@ void AvmTraceBuilder::op_l1_to_l2_msg_exists(uint32_t log_offset, uint32_t dest_ side_effect_counter++; } -void AvmTraceBuilder::op_note_hash_exists(uint32_t note_offset, uint32_t dest_offset) +void AvmTraceBuilder::op_note_hash_exists(uint8_t indirect, uint32_t note_offset, uint32_t dest_offset) { auto const clk = static_cast(main_trace.size()) + 1; - Row row = create_kernel_output_opcode_with_set_metadata_output_from_hint(clk, note_offset, dest_offset); + Row row = create_kernel_output_opcode_with_set_metadata_output_from_hint(indirect, clk, note_offset, dest_offset); kernel_trace_builder.op_note_hash_exists( clk, side_effect_counter, row.avm_main_ia, /*safe*/ static_cast(row.avm_main_ib)); row.avm_main_sel_op_note_hash_exists = FF(1); @@ -1552,11 +1640,11 @@ void AvmTraceBuilder::op_note_hash_exists(uint32_t note_offset, uint32_t dest_of side_effect_counter++; } -void AvmTraceBuilder::op_nullifier_exists(uint32_t note_offset, uint32_t dest_offset) +void AvmTraceBuilder::op_nullifier_exists(uint8_t indirect, uint32_t note_offset, uint32_t dest_offset) { auto const clk = static_cast(main_trace.size()) + 1; - Row row = create_kernel_output_opcode_with_set_metadata_output_from_hint(clk, note_offset, dest_offset); + Row row = create_kernel_output_opcode_with_set_metadata_output_from_hint(indirect, clk, note_offset, dest_offset); kernel_trace_builder.op_nullifier_exists( clk, side_effect_counter, row.avm_main_ia, /*safe*/ static_cast(row.avm_main_ib)); row.avm_main_sel_op_nullifier_exists = FF(1); @@ -1573,7 +1661,7 @@ void AvmTraceBuilder::op_sload(uint32_t slot_offset, uint32_t write_offset) auto const clk = static_cast(main_trace.size()) + 1; // Read the slot - Row row = create_kernel_output_opcode_with_set_value_from_hint(clk, write_offset, slot_offset); + Row row = create_kernel_output_opcode_with_set_value_from_hint(0, clk, write_offset, slot_offset); // Row row = create_sload(clk, write_offset, value, slot_read.val, slot_offset); kernel_trace_builder.op_sload(clk, side_effect_counter, row.avm_main_ib, row.avm_main_ia); @@ -1590,8 +1678,8 @@ void AvmTraceBuilder::op_sstore(uint32_t slot_offset, uint32_t value_offset) { auto const clk = static_cast(main_trace.size()) + 1; - Row row = - create_kernel_output_opcode_with_metadata(clk, value_offset, AvmMemoryTag::FF, slot_offset, AvmMemoryTag::FF); + Row row = create_kernel_output_opcode_with_metadata( + 0, clk, value_offset, AvmMemoryTag::FF, slot_offset, AvmMemoryTag::FF); kernel_trace_builder.op_sstore(clk, side_effect_counter, row.avm_main_ib, row.avm_main_ia); row.avm_main_sel_op_sstore = FF(1); diff --git a/barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_trace.hpp b/barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_trace.hpp index 8e9cbb233b2..42cab974b86 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_trace.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_trace.hpp @@ -103,16 +103,16 @@ class AvmTraceBuilder { // Outputs // With single output values - void op_emit_note_hash(uint32_t note_hash_offset); - void op_emit_nullifier(uint32_t nullifier_offset); - void op_emit_unencrypted_log(uint32_t log_offset); - void op_emit_l2_to_l1_msg(uint32_t msg_offset, uint32_t recipient_offset); + void op_emit_note_hash(uint8_t indirect, uint32_t note_hash_offset); + void op_emit_nullifier(uint8_t indirect, uint32_t nullifier_offset); + void op_emit_unencrypted_log(uint8_t indirect, uint32_t log_offset); + void op_emit_l2_to_l1_msg(uint8_t indirect, uint32_t msg_offset, uint32_t recipient_offset); void op_get_contract_instance(uint8_t indirect, uint32_t address_offset, uint32_t dst_offset); // With additional metadata output - void op_l1_to_l2_msg_exists(uint32_t msg_offset, uint32_t dest_offset); - void op_note_hash_exists(uint32_t note_hash_offset, uint32_t dest_offset); - void op_nullifier_exists(uint32_t nullifier_offset, uint32_t dest_offset); + void op_l1_to_l2_msg_exists(uint8_t indirect, uint32_t msg_offset, uint32_t dest_offset); + void op_note_hash_exists(uint8_t indirect, uint32_t note_hash_offset, uint32_t dest_offset); + void op_nullifier_exists(uint8_t indirect, uint32_t nullifier_offset, uint32_t dest_offset); void op_sload(uint32_t slot_offset, uint32_t value_offset); void op_sstore(uint32_t slot_offset, uint32_t value_offset); @@ -238,17 +238,19 @@ class AvmTraceBuilder { * * Used for writing to the kernel app outputs - {new_note_hash, new_nullifier, etc.} * + * @param indirect - Perform indirect memory resolution * @param clk - The trace clk * @param data_offset - The memory address to read the output from * @return Row */ - Row create_kernel_output_opcode(uint32_t clk, uint32_t data_offset); + Row create_kernel_output_opcode(uint8_t indirect, uint32_t clk, uint32_t data_offset); /** * @brief Create a kernel output opcode with metadata object * * Used for writing to the kernel app outputs with extra metadata - {sload, sstore} (value, slot) * + * @param indirect - Perform indirect memory resolution * @param clk - The trace clk * @param data_offset - The offset of the main value to output * @param data_r_tag - The data type of the value @@ -256,7 +258,8 @@ class AvmTraceBuilder { * @param metadata_r_tag - The data type of the metadata * @return Row */ - Row create_kernel_output_opcode_with_metadata(uint32_t clk, + Row create_kernel_output_opcode_with_metadata(uint8_t indirect, + uint32_t clk, uint32_t data_offset, AvmMemoryTag data_r_tag, uint32_t metadata_offset, @@ -268,12 +271,14 @@ class AvmTraceBuilder { * Used for writing output opcode where one metadata value is written and comes from a hint * {note_hash_exists, nullifier_exists, etc. } Where a boolean output if it exists must also be written * + * @param indirect - Perform indirect memory resolution * @param clk - The trace clk * @param data_offset - The offset of the main value to output * @param metadata_offset - The offset of the metadata (slot in the sload example) * @return Row */ - Row create_kernel_output_opcode_with_set_metadata_output_from_hint(uint32_t clk, + Row create_kernel_output_opcode_with_set_metadata_output_from_hint(uint8_t indirect, + uint32_t clk, uint32_t data_offset, uint32_t metadata_offset); @@ -283,12 +288,14 @@ class AvmTraceBuilder { * Used for writing output opcode where one value is written and comes from a hint * {sload} * + * @param indirect - Perform indirect memory resolution * @param clk - The trace clk * @param data_offset - The offset of the main value to output * @param metadata_offset - The offset of the metadata (slot in the sload example) * @return Row */ - Row create_kernel_output_opcode_with_set_value_from_hint(uint32_t clk, + Row create_kernel_output_opcode_with_set_value_from_hint(uint8_t indirect, + uint32_t clk, uint32_t data_offset, uint32_t metadata_offset); diff --git a/barretenberg/cpp/src/barretenberg/vm/tests/avm_kernel.test.cpp b/barretenberg/cpp/src/barretenberg/vm/tests/avm_kernel.test.cpp index 1cabd6f9f36..4731055db7a 100644 --- a/barretenberg/cpp/src/barretenberg/vm/tests/avm_kernel.test.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/tests/avm_kernel.test.cpp @@ -20,6 +20,7 @@ class AvmKernelPositiveTests : public ::testing::Test {}; class AvmKernelNegativeTests : public ::testing::Test {}; using KernelInputs = std::array; +const size_t INITIAL_GAS = 10000; VmPublicInputs get_public_inputs() { @@ -30,8 +31,12 @@ VmPublicInputs get_public_inputs() kernel_inputs[i] = FF(i + 1); } + // Set high initial gas + kernel_inputs[L2_GAS_LEFT_CONTEXT_INPUTS_OFFSET] = INITIAL_GAS; + kernel_inputs[DA_GAS_LEFT_CONTEXT_INPUTS_OFFSET] = INITIAL_GAS; + // Copy the kernel inputs into the public inputs object - std::get<0>(public_inputs) = kernel_inputs; + std::get(public_inputs) = kernel_inputs; return public_inputs; } @@ -82,6 +87,7 @@ void expect_output_table_row(std::vector::const_iterator row, FF selector, FF ia, FF mem_idx_a, + FF ind_a, AvmMemoryTag r_in_tag, uint32_t side_effect_counter, uint32_t rwa = 0) @@ -93,7 +99,8 @@ void expect_output_table_row(std::vector::const_iterator row, // Checks that are fixed for kernel inputs EXPECT_EQ(row->avm_main_rwa, FF(rwa)); - EXPECT_EQ(row->avm_main_ind_a, FF(0)); + EXPECT_EQ(row->avm_main_ind_a, ind_a); + EXPECT_EQ(row->avm_main_ind_op_a, FF(ind_a != 0)); EXPECT_EQ(row->avm_main_mem_op_a, FF(1)); EXPECT_EQ(row->avm_main_r_in_tag, static_cast(r_in_tag)); EXPECT_EQ(row->avm_main_q_kernel_output_lookup, FF(1)); @@ -105,20 +112,23 @@ void expect_output_table_row_with_metadata(std::vector::const_iterator row, FF selector, FF ia, FF mem_idx_a, + FF ind_a, FF ib, FF mem_idx_b, + FF ind_b, AvmMemoryTag r_in_tag, uint32_t side_effect_counter, uint32_t rwa = 0) { - expect_output_table_row(row, selector, ia, mem_idx_a, r_in_tag, side_effect_counter, rwa); + expect_output_table_row(row, selector, ia, mem_idx_a, ind_a, r_in_tag, side_effect_counter, rwa); EXPECT_EQ(row->avm_main_ib, ib); EXPECT_EQ(row->avm_main_mem_idx_b, mem_idx_b); // Checks that are fixed for kernel inputs EXPECT_EQ(row->avm_main_rwb, FF(0)); - EXPECT_EQ(row->avm_main_ind_b, FF(0)); + EXPECT_EQ(row->avm_main_ind_b, ind_b); + EXPECT_EQ(row->avm_main_ind_op_b, FF(ind_b != 0)); EXPECT_EQ(row->avm_main_mem_op_b, FF(1)); } @@ -126,19 +136,22 @@ void expect_output_table_row_with_exists_metadata(std::vector::const_iterat FF selector, FF ia, FF mem_idx_a, + FF ind_a, FF ib, FF mem_idx_b, + FF ind_b, AvmMemoryTag w_in_tag, uint32_t side_effect_counter) { - expect_output_table_row(row, selector, ia, mem_idx_a, w_in_tag, side_effect_counter); + expect_output_table_row(row, selector, ia, mem_idx_a, ind_a, w_in_tag, side_effect_counter); EXPECT_EQ(row->avm_main_ib, ib); EXPECT_EQ(row->avm_main_mem_idx_b, mem_idx_b); // Checks that are fixed for kernel inputs EXPECT_EQ(row->avm_main_rwb, FF(1)); - EXPECT_EQ(row->avm_main_ind_b, FF(0)); + EXPECT_EQ(row->avm_main_ind_b, ind_b); + EXPECT_EQ(row->avm_main_ind_op_b, FF(ind_b != 0)); EXPECT_EQ(row->avm_main_mem_op_b, FF(1)); } @@ -848,13 +861,21 @@ class AvmKernelOutputNegativeTests : public ::testing::Test {}; TEST_F(AvmKernelOutputPositiveTests, kernelEmitNoteHash) { - uint32_t offset = 42; + uint32_t direct_offset = 42; + uint32_t indirect_offset = 69; + // We write the note hash into memory - auto apply_opcodes = [=](AvmTraceBuilder& trace_builder) { - trace_builder.op_set(0, 1234, offset, AvmMemoryTag::FF); - trace_builder.op_emit_note_hash(offset); + auto direct_apply_opcodes = [=](AvmTraceBuilder& trace_builder) { + trace_builder.op_set(0, 1234, direct_offset, AvmMemoryTag::FF); + trace_builder.op_emit_note_hash(/*indirect=*/false, direct_offset); }; - auto checks = [=]([[maybe_unused]] bool indirect, const std::vector& trace) { + auto indirect_apply_opcodes = [=](AvmTraceBuilder& trace_builder) { + trace_builder.op_set(0, 1234, direct_offset, AvmMemoryTag::FF); + trace_builder.op_set(0, direct_offset, indirect_offset, AvmMemoryTag::U32); + trace_builder.op_emit_note_hash(/*indirect=*/true, indirect_offset); + }; + + auto checks = [=](bool indirect, const std::vector& trace) { std::vector::const_iterator row = std::ranges::find_if( trace.begin(), trace.end(), [](Row r) { return r.avm_main_sel_op_emit_note_hash == FF(1); }); EXPECT_TRUE(row != trace.end()); @@ -866,24 +887,35 @@ TEST_F(AvmKernelOutputPositiveTests, kernelEmitNoteHash) row, /*kernel_in_offset=*/output_offset, /*ia=*/1234, // Note the value generated above for public inputs is the same as the index read + 1 - /*mem_idx_a=*/offset, + /*mem_idx_a=*/direct_offset, + /*ind_a*/ indirect ? indirect_offset : 0, /*w_in_tag=*/AvmMemoryTag::FF, /*side_effect_counter=*/0); check_kernel_outputs(trace.at(output_offset), 1234, /*side_effect_counter=*/0, /*metadata=*/0); }; - test_kernel_lookup(false, apply_opcodes, checks); + test_kernel_lookup(false, direct_apply_opcodes, checks); + test_kernel_lookup(true, indirect_apply_opcodes, checks); } TEST_F(AvmKernelOutputPositiveTests, kernelEmitNullifier) { - uint32_t offset = 42; - auto apply_opcodes = [=](AvmTraceBuilder& trace_builder) { - trace_builder.op_set(0, 1234, offset, AvmMemoryTag::FF); - trace_builder.op_emit_nullifier(offset); + uint32_t direct_offset = 42; + uint32_t indirect_offset = 69; + + // We write the note hash into memory + auto direct_apply_opcodes = [=](AvmTraceBuilder& trace_builder) { + trace_builder.op_set(0, 1234, direct_offset, AvmMemoryTag::FF); + trace_builder.op_emit_nullifier(/*indirect=*/false, direct_offset); }; - auto checks = [=]([[maybe_unused]] bool indirect, const std::vector& trace) { + auto indirect_apply_opcodes = [=](AvmTraceBuilder& trace_builder) { + trace_builder.op_set(0, 1234, direct_offset, AvmMemoryTag::FF); + trace_builder.op_set(0, direct_offset, indirect_offset, AvmMemoryTag::U32); + trace_builder.op_emit_nullifier(/*indirect=*/true, indirect_offset); + }; + + auto checks = [=](bool indirect, const std::vector& trace) { std::vector::const_iterator row = std::ranges::find_if( trace.begin(), trace.end(), [](Row r) { return r.avm_main_sel_op_emit_nullifier == FF(1); }); EXPECT_TRUE(row != trace.end()); @@ -895,7 +927,8 @@ TEST_F(AvmKernelOutputPositiveTests, kernelEmitNullifier) row, /*kernel_in_offset=*/output_offset, /*ia=*/1234, // Note the value generated above for public inputs is the same as the index read + 1 - /*mem_idx_a=*/offset, + /*mem_idx_a=*/direct_offset, + /*ind_a*/ indirect ? indirect_offset : 0, /*w_in_tag=*/AvmMemoryTag::FF, /*side_effect_counter=*/0); @@ -904,19 +937,32 @@ TEST_F(AvmKernelOutputPositiveTests, kernelEmitNullifier) check_kernel_outputs(trace.at(output_offset), 1234, /*side_effect_counter=*/0, /*metadata=*/0); }; - test_kernel_lookup(false, apply_opcodes, checks); + test_kernel_lookup(false, direct_apply_opcodes, checks); + test_kernel_lookup(true, indirect_apply_opcodes, checks); } TEST_F(AvmKernelOutputPositiveTests, kernelEmitL2ToL1Msg) { uint32_t msg_offset = 42; + uint32_t indirect_msg_offset = 420; + uint32_t recipient_offset = 69; - auto apply_opcodes = [=](AvmTraceBuilder& trace_builder) { + uint32_t indirect_recipient_offset = 690; + + // auto direct_apply_opcodes = [=](AvmTraceBuilder& trace_builder) { + // trace_builder.op_set(0, 1234, msg_offset, AvmMemoryTag::FF); + // trace_builder.op_set(0, 420, recipient_offset, AvmMemoryTag::FF); + // trace_builder.op_emit_l2_to_l1_msg(false, recipient_offset, msg_offset); + // }; + auto indirect_apply_opcodes = [=](AvmTraceBuilder& trace_builder) { trace_builder.op_set(0, 1234, msg_offset, AvmMemoryTag::FF); + trace_builder.op_set(0, msg_offset, indirect_msg_offset, AvmMemoryTag::U32); trace_builder.op_set(0, 420, recipient_offset, AvmMemoryTag::FF); - trace_builder.op_emit_l2_to_l1_msg(recipient_offset, msg_offset); + trace_builder.op_set(0, recipient_offset, indirect_recipient_offset, AvmMemoryTag::U32); + trace_builder.op_emit_l2_to_l1_msg(3, indirect_recipient_offset, indirect_msg_offset); }; - auto checks = [=]([[maybe_unused]] bool indirect, const std::vector& trace) { + + auto checks = [=](bool indirect, const std::vector& trace) { std::vector::const_iterator row = std::ranges::find_if( trace.begin(), trace.end(), [](Row r) { return r.avm_main_sel_op_emit_l2_to_l1_msg == FF(1); }); EXPECT_TRUE(row != trace.end()); @@ -929,8 +975,10 @@ TEST_F(AvmKernelOutputPositiveTests, kernelEmitL2ToL1Msg) /*kernel_in_offset=*/output_offset, /*ia=*/1234, // Note the value generated above for public inputs is the same as the index read + 1 /*mem_idx_a=*/msg_offset, + /*ind_a*/ indirect ? indirect_msg_offset : 0, /*ib=*/420, /*mem_idx_b=*/recipient_offset, + /*ind_a*/ indirect ? indirect_recipient_offset : 0, /*w_in_tag=*/AvmMemoryTag::FF, /*side_effect_counter=*/0 @@ -939,17 +987,27 @@ TEST_F(AvmKernelOutputPositiveTests, kernelEmitL2ToL1Msg) check_kernel_outputs(trace.at(output_offset), 1234, /*side_effect_counter=*/0, /*metadata=*/420); }; - test_kernel_lookup(false, apply_opcodes, checks); + // test_kernel_lookup(false, direct_apply_opcodes, checks); + test_kernel_lookup(true, indirect_apply_opcodes, checks); } TEST_F(AvmKernelOutputPositiveTests, kernelEmitUnencryptedLog) { - uint32_t offset = 42; - auto apply_opcodes = [=](AvmTraceBuilder& trace_builder) { - trace_builder.op_set(0, 1234, offset, AvmMemoryTag::FF); - trace_builder.op_emit_unencrypted_log(offset); + uint32_t direct_offset = 42; + uint32_t indirect_offset = 69; + + // We write the note hash into memory + auto direct_apply_opcodes = [=](AvmTraceBuilder& trace_builder) { + trace_builder.op_set(0, 1234, direct_offset, AvmMemoryTag::FF); + trace_builder.op_emit_unencrypted_log(/*indirect=*/false, direct_offset); }; - auto checks = [=]([[maybe_unused]] bool indirect, const std::vector& trace) { + auto indirect_apply_opcodes = [=](AvmTraceBuilder& trace_builder) { + trace_builder.op_set(0, 1234, direct_offset, AvmMemoryTag::FF); + trace_builder.op_set(0, direct_offset, indirect_offset, AvmMemoryTag::U32); + trace_builder.op_emit_unencrypted_log(/*indirect=*/true, indirect_offset); + }; + + auto checks = [=](bool indirect, const std::vector& trace) { std::vector::const_iterator row = std::ranges::find_if( trace.begin(), trace.end(), [](Row r) { return r.avm_main_sel_op_emit_unencrypted_log == FF(1); }); EXPECT_TRUE(row != trace.end()); @@ -961,14 +1019,16 @@ TEST_F(AvmKernelOutputPositiveTests, kernelEmitUnencryptedLog) row, /*kernel_in_offset=*/output_offset, /*ia=*/1234, // Note the value generated above for public inputs is the same as the index read + 1 - /*mem_idx_a=*/offset, + /*mem_idx_a=*/direct_offset, + /*ind_a*/ indirect ? indirect_offset : 0, /*w_in_tag=*/AvmMemoryTag::FF, /*side_effect_counter=*/0); check_kernel_outputs(trace.at(output_offset), 1234, 0, 0); }; - test_kernel_lookup(false, apply_opcodes, checks); + test_kernel_lookup(false, direct_apply_opcodes, checks); + test_kernel_lookup(true, indirect_apply_opcodes, checks); } TEST_F(AvmKernelOutputPositiveTests, kernelSload) @@ -993,13 +1053,16 @@ TEST_F(AvmKernelOutputPositiveTests, kernelSload) // Check the outputs of the trace uint32_t output_offset = AvmKernelTraceBuilder::START_SLOAD_WRITE_OFFSET; + // TODO: temporarily hardcoded to direct, resolved by dbanks12 / ilyas pr - use your changes expect_output_table_row_with_metadata( row, /*kernel_in_offset=*/output_offset, /*ia=*/value, // Note the value generated above for public inputs is the same as the index read + 1 /*mem_idx_a=*/value_offset, + /*ind_a=*/false, /*ib=*/slot, /*mem_idx_b=*/slot_offset, + /*ind_b=*/false, /*w_in_tag=*/AvmMemoryTag::FF, /*side_effect_counter=*/0, /*rwa=*/1); @@ -1030,13 +1093,16 @@ TEST_F(AvmKernelOutputPositiveTests, kernelSstore) // Check the outputs of the trace uint32_t output_offset = AvmKernelTraceBuilder::START_SSTORE_WRITE_OFFSET; + // TODO: temporarily hardcoded to direct, resolved by dbanks12 / ilyas pr - use your changes expect_output_table_row_with_metadata( row, /*kernel_in_offset=*/output_offset, /*ia=*/value, // Note the value generated above for public inputs is the same as the index read + 1 /*mem_idx_a=*/value_offset, + /*ind_a*/ false, /*ib=*/slot, /*mem_idx_b=*/metadata_offset, + /*ind_b*/ false, /*w_in_tag=*/AvmMemoryTag::FF, /*side_effect_counter=*/0); @@ -1049,17 +1115,26 @@ TEST_F(AvmKernelOutputPositiveTests, kernelSstore) TEST_F(AvmKernelOutputPositiveTests, kernelNoteHashExists) { uint32_t value_offset = 42; + uint32_t indirect_value_offset = 69; auto value = 1234; uint32_t metadata_offset = 420; + uint32_t indirect_metadata_offset = 690; auto exists = 1; auto execution_hints = ExecutionHints().with_note_hash_exists_hints({ { 0, exists } }); - auto apply_opcodes = [=](AvmTraceBuilder& trace_builder) { + auto direct_apply_opcodes = [=](AvmTraceBuilder& trace_builder) { trace_builder.op_set(0, static_cast(value), value_offset, AvmMemoryTag::FF); - trace_builder.op_note_hash_exists(value_offset, metadata_offset); + trace_builder.op_note_hash_exists(/*indirect*/ false, value_offset, metadata_offset); }; - auto checks = [=]([[maybe_unused]] bool indirect, const std::vector& trace) { + // TODO: fix + auto indirect_apply_opcodes = [=](AvmTraceBuilder& trace_builder) { + trace_builder.op_set(0, static_cast(value), value_offset, AvmMemoryTag::FF); + trace_builder.op_set(0, value_offset, indirect_value_offset, AvmMemoryTag::U32); + trace_builder.op_set(0, metadata_offset, indirect_metadata_offset, AvmMemoryTag::U32); + trace_builder.op_note_hash_exists(/*indirect*/ 3, indirect_value_offset, indirect_metadata_offset); + }; + auto checks = [=](bool indirect, const std::vector& trace) { std::vector::const_iterator row = std::ranges::find_if( trace.begin(), trace.end(), [](Row r) { return r.avm_main_sel_op_note_hash_exists == FF(1); }); EXPECT_TRUE(row != trace.end()); @@ -1072,8 +1147,10 @@ TEST_F(AvmKernelOutputPositiveTests, kernelNoteHashExists) /*kernel_in_offset=*/output_offset, /*ia=*/value, // Note the value generated above for public inputs is the same as the index read + 1 /*mem_idx_a=*/value_offset, + /*ind_a*/ indirect ? FF(indirect_value_offset) : FF(0), /*ib=*/exists, /*mem_idx_b=*/metadata_offset, + /*ind_a*/ indirect ? FF(indirect_metadata_offset) : FF(0), /*w_in_tag=*/AvmMemoryTag::FF, /*side_effect_counter=*/0); @@ -1081,7 +1158,8 @@ TEST_F(AvmKernelOutputPositiveTests, kernelNoteHashExists) check_kernel_outputs(trace.at(output_offset), value, /*side_effect_counter=*/0, exists); }; - test_kernel_lookup(false, apply_opcodes, checks, execution_hints); + test_kernel_lookup(false, direct_apply_opcodes, checks, execution_hints); + test_kernel_lookup(true, indirect_apply_opcodes, checks, execution_hints); } TEST_F(AvmKernelOutputPositiveTests, kernelNullifierExists) @@ -1095,9 +1173,9 @@ TEST_F(AvmKernelOutputPositiveTests, kernelNullifierExists) auto apply_opcodes = [=](AvmTraceBuilder& trace_builder) { trace_builder.op_set(0, static_cast(value), value_offset, AvmMemoryTag::FF); - trace_builder.op_nullifier_exists(value_offset, metadata_offset); + trace_builder.op_nullifier_exists(/*indirect=*/false, value_offset, metadata_offset); }; - auto checks = [=]([[maybe_unused]] bool indirect, const std::vector& trace) { + auto checks = [=](bool indirect, const std::vector& trace) { std::vector::const_iterator row = std::ranges::find_if( trace.begin(), trace.end(), [](Row r) { return r.avm_main_sel_op_nullifier_exists == FF(1); }); EXPECT_TRUE(row != trace.end()); @@ -1110,8 +1188,10 @@ TEST_F(AvmKernelOutputPositiveTests, kernelNullifierExists) /*kernel_in_offset=*/output_offset, /*ia=*/value, // Note the value generated above for public inputs is the same as the index read + 1 /*mem_idx_a=*/value_offset, + /*ind_a*/ indirect, /*ib=*/exists, /*mem_idx_b=*/metadata_offset, + /*ind_b*/ indirect, /*w_in_tag=*/AvmMemoryTag::FF, /*side_effect_counter=*/0); @@ -1132,9 +1212,9 @@ TEST_F(AvmKernelOutputPositiveTests, kernelNullifierNonExists) auto apply_opcodes = [=](AvmTraceBuilder& trace_builder) { trace_builder.op_set(0, static_cast(value), value_offset, AvmMemoryTag::FF); - trace_builder.op_nullifier_exists(value_offset, metadata_offset); + trace_builder.op_nullifier_exists(/*indirect=*/false, value_offset, metadata_offset); }; - auto checks = [=]([[maybe_unused]] bool indirect, const std::vector& trace) { + auto checks = [=](bool indirect, const std::vector& trace) { std::vector::const_iterator row = std::ranges::find_if( trace.begin(), trace.end(), [](Row r) { return r.avm_main_sel_op_nullifier_exists == FF(1); }); EXPECT_TRUE(row != trace.end()); @@ -1147,8 +1227,10 @@ TEST_F(AvmKernelOutputPositiveTests, kernelNullifierNonExists) /*kernel_in_offset=*/output_offset, /*ia=*/value, // Note the value generated above for public inputs is the same as the index read + 1 /*mem_idx_a=*/value_offset, + /*ind_a*/ indirect, /*ib=*/exists, /*mem_idx_b=*/metadata_offset, + /*ind_b*/ indirect, /*w_in_tag=*/AvmMemoryTag::FF, /*side_effect_counter=*/0); @@ -1170,7 +1252,7 @@ TEST_F(AvmKernelOutputPositiveTests, kernelL1ToL2MsgExists) auto apply_opcodes = [=](AvmTraceBuilder& trace_builder) { trace_builder.op_set(0, static_cast(value), value_offset, AvmMemoryTag::FF); - trace_builder.op_l1_to_l2_msg_exists(value_offset, metadata_offset); + trace_builder.op_l1_to_l2_msg_exists(/*indirect*/ false, value_offset, metadata_offset); }; auto checks = [=]([[maybe_unused]] bool indirect, const std::vector& trace) { std::vector::const_iterator row = std::ranges::find_if( @@ -1185,8 +1267,10 @@ TEST_F(AvmKernelOutputPositiveTests, kernelL1ToL2MsgExists) /*kernel_in_offset=*/output_offset, /*ia=*/value, // Note the value generated above for public inputs is the same as the index read + 1 /*mem_idx_a=*/value_offset, + /*ind_a*/ indirect, /*ib=*/exists, /*mem_idx_b=*/metadata_offset, + /*ind_b*/ indirect, /*w_in_tag=*/AvmMemoryTag::FF, /*side_effect_counter=*/0);