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 5c1ac86c7c37..d685ef9a8de4 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_trace.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_trace.cpp @@ -64,50 +64,95 @@ void AvmTraceBuilder::reset() external_call_counter = 0; } - -AvmTraceBuilder::IndirectThreeResolution AvmTraceBuilder::resolve_ind_three( - uint8_t space_id, uint32_t clk, uint8_t indirect, uint32_t a_offset, uint32_t b_offset, uint32_t c_offset) +/** + * @brief Returns an array of mem_offsets and tags them with their given Addressing Mode (direct/indirect) based on the + * given indirect byte. + * @tparam N The number of memory offsets to resolve. + */ +template std::array resolve_indirects(uint8_t indirect, std::array mem_offsets) { - bool indirect_flag_a = is_operand_indirect(indirect, 0); - bool indirect_flag_b = is_operand_indirect(indirect, 1); - bool indirect_flag_c = is_operand_indirect(indirect, 2); - - uint32_t direct_a_offset = a_offset; - uint32_t direct_b_offset = b_offset; - uint32_t direct_c_offset = c_offset; - - bool tag_match = true; - - if (indirect_flag_a) { - auto read_ind_a = - mem_trace_builder.indirect_read_and_load_from_memory(space_id, clk, IndirectRegister::IND_A, a_offset); - direct_a_offset = uint32_t(read_ind_a.val); - tag_match = tag_match && read_ind_a.tag_match; + std::array resolved_addresses; + ASSERT(indirect < 256); + + for (size_t i = 0; i < N; i++) { + // No need to type this as a bool as is implied by the (& 1). + uint8_t indirect_bit = (indirect >> i) & 1; + // Cast straight to AddressingMode, saves having to have a branching statement here. + auto addr = static_cast(indirect_bit); + resolved_addresses[i] = std::make_pair(addr, mem_offsets[i]); } + return resolved_addresses; +} - if (indirect_flag_b) { - auto read_ind_b = - mem_trace_builder.indirect_read_and_load_from_memory(space_id, clk, IndirectRegister::IND_B, b_offset); - direct_b_offset = uint32_t(read_ind_b.val); - tag_match = tag_match && read_ind_b.tag_match; +/** + * @brief Loads a value from memory into a given intermediate register at a specified clock cycle. + * Handles both direct and indirect memory access. + * @tparam reg The intermediate register to load the value into. + */ +template +AvmTraceBuilder::MemOp AvmTraceBuilder::constrained_read_from_memory( + uint8_t space_id, uint32_t clk, ResolvedAddr addr, AvmMemoryTag read_tag, AvmMemoryTag write_tag) +{ + // Get the same matching indirect register for the given intermediate register. + auto indirect_reg = static_cast(reg); + // Set up direct and indirect offsets that may be overwritten + uint32_t direct_offset = addr.second; + uint32_t indirect_offset = 0; + bool tag_match = true; + bool is_indirect = false; + if (addr.first == AddressingMode::INDIRECT) { + is_indirect = true; + indirect_offset = direct_offset; + auto read_ind = + mem_trace_builder.indirect_read_and_load_from_memory(space_id, clk, indirect_reg, indirect_offset); + if (!read_ind.tag_match) { + tag_match = false; + } + direct_offset = uint32_t(read_ind.val); } + auto read_dir = mem_trace_builder.read_and_load_from_memory(space_id, clk, reg, direct_offset, read_tag, write_tag); + + return MemOp{ + .is_indirect = is_indirect, + .indirect_address = indirect_offset, + .direct_address = direct_offset, + .tag = read_tag, + .tag_match = tag_match && read_dir.tag_match, + .val = read_dir.val, + }; +} - if (indirect_flag_c) { - auto read_ind_c = - mem_trace_builder.indirect_read_and_load_from_memory(space_id, clk, IndirectRegister::IND_C, c_offset); - direct_c_offset = uint32_t(read_ind_c.val); - tag_match = tag_match && read_ind_c.tag_match; +/** + * @brief Writes a value to memory from a given intermediate register at a specified clock cycle. + * Handles both direct and indirect memory access. + * @tparam reg The intermediate register to write the value from. + */ +template +AvmTraceBuilder::MemOp AvmTraceBuilder::constrained_write_to_memory( + uint8_t space_id, uint32_t clk, ResolvedAddr addr, FF value, AvmMemoryTag read_tag, AvmMemoryTag write_tag) +{ + auto indirect_reg = static_cast(reg); + uint32_t direct_offset = addr.second; + uint32_t indirect_offset = 0; + bool tag_match = true; + bool is_indirect = false; + if (addr.first == AddressingMode::INDIRECT) { + is_indirect = true; + indirect_offset = direct_offset; + auto read_ind = + mem_trace_builder.indirect_read_and_load_from_memory(space_id, clk, indirect_reg, indirect_offset); + if (!read_ind.tag_match) { + tag_match = false; + } + direct_offset = uint32_t(read_ind.val); } - - return IndirectThreeResolution{ - .tag_match = tag_match, - .direct_a_offset = direct_a_offset, - .direct_b_offset = direct_b_offset, - .direct_c_offset = direct_c_offset, - .indirect_flag_a = indirect_flag_a, - .indirect_flag_b = indirect_flag_b, - .indirect_flag_c = indirect_flag_c, - }; + mem_trace_builder.write_into_memory(space_id, clk, reg, direct_offset, value, read_tag, write_tag); + return MemOp{ .is_indirect = is_indirect, + .indirect_address = indirect_offset, + .direct_address = direct_offset, + .tag = write_tag, + .tag_match = tag_match, + .val = value }; } /** @@ -124,15 +169,14 @@ void AvmTraceBuilder::op_add( { auto clk = static_cast(main_trace.size()) + 1; - auto const res = resolve_ind_three(call_ptr, clk, indirect, a_offset, b_offset, dst_offset); - bool tag_match = res.tag_match; + // Resolve any potential indirects in the order they are encoded in the indirect byte. + auto [resolved_a, resolved_b, resolved_c] = resolve_indirects<3>(indirect, { a_offset, b_offset, dst_offset }); // Reading from memory and loading into ia resp. ib. - auto read_a = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IA, res.direct_a_offset, in_tag, in_tag); - auto read_b = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IB, res.direct_b_offset, in_tag, in_tag); - tag_match = read_a.tag_match && read_b.tag_match; + auto read_a = constrained_read_from_memory(call_ptr, clk, resolved_a, in_tag, in_tag); + auto read_b = constrained_read_from_memory(call_ptr, clk, resolved_b, in_tag, in_tag); + + bool tag_match = read_a.tag_match && read_b.tag_match; // a + b = c FF a = read_a.val; @@ -144,7 +188,7 @@ void AvmTraceBuilder::op_add( FF c = tag_match ? alu_trace_builder.op_add(a, b, in_tag, clk) : FF(0); // Write into memory value c from intermediate register ic. - mem_trace_builder.write_into_memory(call_ptr, clk, IntermRegister::IC, res.direct_c_offset, c, in_tag, in_tag); + auto write_c = constrained_write_to_memory(call_ptr, clk, resolved_c, c, in_tag, in_tag); // Constrain gas cost gas_trace_builder.constrain_gas_lookup(clk, OpCode::ADD); @@ -153,19 +197,19 @@ void AvmTraceBuilder::op_add( .avm_main_clk = clk, .avm_main_alu_in_tag = FF(static_cast(in_tag)), .avm_main_call_ptr = call_ptr, - .avm_main_ia = a, - .avm_main_ib = b, - .avm_main_ic = c, - .avm_main_ind_a = res.indirect_flag_a ? FF(a_offset) : FF(0), - .avm_main_ind_b = res.indirect_flag_b ? FF(b_offset) : FF(0), - .avm_main_ind_c = res.indirect_flag_c ? FF(dst_offset) : FF(0), - .avm_main_ind_op_a = FF(static_cast(res.indirect_flag_a)), - .avm_main_ind_op_b = FF(static_cast(res.indirect_flag_b)), - .avm_main_ind_op_c = FF(static_cast(res.indirect_flag_c)), + .avm_main_ia = read_a.val, + .avm_main_ib = read_b.val, + .avm_main_ic = write_c.val, + .avm_main_ind_a = FF(read_a.indirect_address), + .avm_main_ind_b = FF(read_b.indirect_address), + .avm_main_ind_c = FF(write_c.indirect_address), + .avm_main_ind_op_a = FF(static_cast(read_a.is_indirect)), + .avm_main_ind_op_b = FF(static_cast(read_b.is_indirect)), + .avm_main_ind_op_c = FF(static_cast(write_c.is_indirect)), .avm_main_internal_return_ptr = FF(internal_return_ptr), - .avm_main_mem_idx_a = FF(res.direct_a_offset), - .avm_main_mem_idx_b = FF(res.direct_b_offset), - .avm_main_mem_idx_c = FF(res.direct_c_offset), + .avm_main_mem_idx_a = FF(read_a.direct_address), + .avm_main_mem_idx_b = FF(read_b.direct_address), + .avm_main_mem_idx_c = FF(write_c.direct_address), .avm_main_mem_op_a = FF(1), .avm_main_mem_op_b = FF(1), .avm_main_mem_op_c = FF(1), @@ -192,15 +236,14 @@ void AvmTraceBuilder::op_sub( { auto clk = static_cast(main_trace.size()) + 1; - auto const res = resolve_ind_three(call_ptr, clk, indirect, a_offset, b_offset, dst_offset); - bool tag_match = res.tag_match; + // Resolve any potential indirects in the order they are encoded in the indirect byte. + auto [resolved_a, resolved_b, resolved_c] = resolve_indirects<3>(indirect, { a_offset, b_offset, dst_offset }); // Reading from memory and loading into ia resp. ib. - auto read_a = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IA, res.direct_a_offset, in_tag, in_tag); - auto read_b = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IB, res.direct_b_offset, in_tag, in_tag); - tag_match = read_a.tag_match && read_b.tag_match; + auto read_a = constrained_read_from_memory(call_ptr, clk, resolved_a, in_tag, in_tag); + auto read_b = constrained_read_from_memory(call_ptr, clk, resolved_b, in_tag, in_tag); + + bool tag_match = read_a.tag_match && read_b.tag_match; // a - b = c FF a = read_a.val; @@ -212,7 +255,7 @@ void AvmTraceBuilder::op_sub( FF c = tag_match ? alu_trace_builder.op_sub(a, b, in_tag, clk) : FF(0); // Write into memory value c from intermediate register ic. - mem_trace_builder.write_into_memory(call_ptr, clk, IntermRegister::IC, res.direct_c_offset, c, in_tag, in_tag); + auto write_c = constrained_write_to_memory(call_ptr, clk, resolved_c, c, in_tag, in_tag); // Constrain gas cost gas_trace_builder.constrain_gas_lookup(clk, OpCode::SUB); @@ -221,19 +264,19 @@ void AvmTraceBuilder::op_sub( .avm_main_clk = clk, .avm_main_alu_in_tag = FF(static_cast(in_tag)), .avm_main_call_ptr = call_ptr, - .avm_main_ia = a, - .avm_main_ib = b, - .avm_main_ic = c, - .avm_main_ind_a = res.indirect_flag_a ? FF(a_offset) : FF(0), - .avm_main_ind_b = res.indirect_flag_b ? FF(b_offset) : FF(0), - .avm_main_ind_c = res.indirect_flag_c ? FF(dst_offset) : FF(0), - .avm_main_ind_op_a = FF(static_cast(res.indirect_flag_a)), - .avm_main_ind_op_b = FF(static_cast(res.indirect_flag_b)), - .avm_main_ind_op_c = FF(static_cast(res.indirect_flag_c)), + .avm_main_ia = read_a.val, + .avm_main_ib = read_b.val, + .avm_main_ic = write_c.val, + .avm_main_ind_a = FF(read_a.indirect_address), + .avm_main_ind_b = FF(read_b.indirect_address), + .avm_main_ind_c = FF(write_c.indirect_address), + .avm_main_ind_op_a = FF(static_cast(read_a.is_indirect)), + .avm_main_ind_op_b = FF(static_cast(read_b.is_indirect)), + .avm_main_ind_op_c = FF(static_cast(write_c.is_indirect)), .avm_main_internal_return_ptr = FF(internal_return_ptr), - .avm_main_mem_idx_a = FF(res.direct_a_offset), - .avm_main_mem_idx_b = FF(res.direct_b_offset), - .avm_main_mem_idx_c = FF(res.direct_c_offset), + .avm_main_mem_idx_a = FF(read_a.direct_address), + .avm_main_mem_idx_b = FF(read_b.direct_address), + .avm_main_mem_idx_c = FF(write_c.direct_address), .avm_main_mem_op_a = FF(1), .avm_main_mem_op_b = FF(1), .avm_main_mem_op_c = FF(1), @@ -260,15 +303,14 @@ void AvmTraceBuilder::op_mul( { auto clk = static_cast(main_trace.size()) + 1; - auto const res = resolve_ind_three(call_ptr, clk, indirect, a_offset, b_offset, dst_offset); - bool tag_match = res.tag_match; + // Resolve any potential indirects in the order they are encoded in the indirect byte. + auto [resolved_a, resolved_b, resolved_c] = resolve_indirects<3>(indirect, { a_offset, b_offset, dst_offset }); // Reading from memory and loading into ia resp. ib. - auto read_a = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IA, res.direct_a_offset, in_tag, in_tag); - auto read_b = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IB, res.direct_b_offset, in_tag, in_tag); - tag_match = read_a.tag_match && read_b.tag_match; + auto read_a = constrained_read_from_memory(call_ptr, clk, resolved_a, in_tag, in_tag); + auto read_b = constrained_read_from_memory(call_ptr, clk, resolved_b, in_tag, in_tag); + + bool tag_match = read_a.tag_match && read_b.tag_match; // a * b = c FF a = read_a.val; @@ -280,7 +322,7 @@ void AvmTraceBuilder::op_mul( FF c = tag_match ? alu_trace_builder.op_mul(a, b, in_tag, clk) : FF(0); // Write into memory value c from intermediate register ic. - mem_trace_builder.write_into_memory(call_ptr, clk, IntermRegister::IC, res.direct_c_offset, c, in_tag, in_tag); + auto write_c = constrained_write_to_memory(call_ptr, clk, resolved_c, c, in_tag, in_tag); // Constrain gas cost gas_trace_builder.constrain_gas_lookup(clk, OpCode::MUL); @@ -289,19 +331,19 @@ void AvmTraceBuilder::op_mul( .avm_main_clk = clk, .avm_main_alu_in_tag = FF(static_cast(in_tag)), .avm_main_call_ptr = call_ptr, - .avm_main_ia = a, - .avm_main_ib = b, - .avm_main_ic = c, - .avm_main_ind_a = res.indirect_flag_a ? FF(a_offset) : FF(0), - .avm_main_ind_b = res.indirect_flag_b ? FF(b_offset) : FF(0), - .avm_main_ind_c = res.indirect_flag_c ? FF(dst_offset) : FF(0), - .avm_main_ind_op_a = FF(static_cast(res.indirect_flag_a)), - .avm_main_ind_op_b = FF(static_cast(res.indirect_flag_b)), - .avm_main_ind_op_c = FF(static_cast(res.indirect_flag_c)), + .avm_main_ia = read_a.val, + .avm_main_ib = read_b.val, + .avm_main_ic = write_c.val, + .avm_main_ind_a = FF(read_a.indirect_address), + .avm_main_ind_b = FF(read_b.indirect_address), + .avm_main_ind_c = FF(write_c.indirect_address), + .avm_main_ind_op_a = FF(static_cast(read_a.is_indirect)), + .avm_main_ind_op_b = FF(static_cast(read_b.is_indirect)), + .avm_main_ind_op_c = FF(static_cast(write_c.is_indirect)), .avm_main_internal_return_ptr = FF(internal_return_ptr), - .avm_main_mem_idx_a = FF(res.direct_a_offset), - .avm_main_mem_idx_b = FF(res.direct_b_offset), - .avm_main_mem_idx_c = FF(res.direct_c_offset), + .avm_main_mem_idx_a = FF(read_a.direct_address), + .avm_main_mem_idx_b = FF(read_b.direct_address), + .avm_main_mem_idx_c = FF(write_c.direct_address), .avm_main_mem_op_a = FF(1), .avm_main_mem_op_b = FF(1), .avm_main_mem_op_c = FF(1), @@ -327,15 +369,16 @@ void AvmTraceBuilder::op_fdiv(uint8_t indirect, uint32_t a_offset, uint32_t b_of { auto clk = static_cast(main_trace.size()) + 1; - auto const res = resolve_ind_three(call_ptr, clk, indirect, a_offset, b_offset, dst_offset); - bool tag_match = res.tag_match; + // Resolve any potential indirects in the order they are encoded in the indirect byte. + auto [resolved_a, resolved_b, resolved_c] = resolve_indirects<3>(indirect, { a_offset, b_offset, dst_offset }); // Reading from memory and loading into ia resp. ib. - auto read_a = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IA, res.direct_a_offset, AvmMemoryTag::FF, AvmMemoryTag::FF); - auto read_b = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IB, res.direct_b_offset, AvmMemoryTag::FF, AvmMemoryTag::FF); - tag_match = read_a.tag_match && read_b.tag_match; + auto read_a = + constrained_read_from_memory(call_ptr, clk, resolved_a, AvmMemoryTag::FF, AvmMemoryTag::FF); + auto read_b = + constrained_read_from_memory(call_ptr, clk, resolved_b, AvmMemoryTag::FF, AvmMemoryTag::FF); + + bool tag_match = read_a.tag_match && read_b.tag_match; // a * b^(-1) = c FF a = read_a.val; @@ -356,8 +399,8 @@ void AvmTraceBuilder::op_fdiv(uint8_t indirect, uint32_t a_offset, uint32_t b_of } // Write into memory value c from intermediate register ic. - mem_trace_builder.write_into_memory( - call_ptr, clk, IntermRegister::IC, res.direct_c_offset, c, AvmMemoryTag::FF, AvmMemoryTag::FF); + auto write_c = constrained_write_to_memory( + call_ptr, clk, resolved_c, c, AvmMemoryTag::FF, AvmMemoryTag::FF); // Constrain gas cost gas_trace_builder.constrain_gas_lookup(clk, OpCode::FDIV); @@ -365,20 +408,20 @@ void AvmTraceBuilder::op_fdiv(uint8_t indirect, uint32_t a_offset, uint32_t b_of main_trace.push_back(Row{ .avm_main_clk = clk, .avm_main_call_ptr = call_ptr, - .avm_main_ia = tag_match ? a : FF(0), - .avm_main_ib = tag_match ? b : FF(0), - .avm_main_ic = tag_match ? c : FF(0), - .avm_main_ind_a = res.indirect_flag_a ? FF(a_offset) : FF(0), - .avm_main_ind_b = res.indirect_flag_b ? FF(b_offset) : FF(0), - .avm_main_ind_c = res.indirect_flag_c ? FF(dst_offset) : FF(0), - .avm_main_ind_op_a = FF(static_cast(res.indirect_flag_a)), - .avm_main_ind_op_b = FF(static_cast(res.indirect_flag_b)), - .avm_main_ind_op_c = FF(static_cast(res.indirect_flag_c)), + .avm_main_ia = tag_match ? read_a.val : FF(0), + .avm_main_ib = tag_match ? read_b.val : FF(0), + .avm_main_ic = tag_match ? write_c.val : FF(0), + .avm_main_ind_a = FF(read_a.indirect_address), + .avm_main_ind_b = FF(read_b.indirect_address), + .avm_main_ind_c = FF(write_c.indirect_address), + .avm_main_ind_op_a = FF(static_cast(read_a.is_indirect)), + .avm_main_ind_op_b = FF(static_cast(read_b.is_indirect)), + .avm_main_ind_op_c = FF(static_cast(write_c.is_indirect)), .avm_main_internal_return_ptr = FF(internal_return_ptr), .avm_main_inv = tag_match ? inv : FF(1), - .avm_main_mem_idx_a = FF(res.direct_a_offset), - .avm_main_mem_idx_b = FF(res.direct_b_offset), - .avm_main_mem_idx_c = FF(res.direct_c_offset), + .avm_main_mem_idx_a = FF(read_a.direct_address), + .avm_main_mem_idx_b = FF(read_b.direct_address), + .avm_main_mem_idx_c = FF(write_c.direct_address), .avm_main_mem_op_a = FF(1), .avm_main_mem_op_b = FF(1), .avm_main_mem_op_c = FF(1), @@ -403,31 +446,14 @@ void AvmTraceBuilder::op_fdiv(uint8_t indirect, uint32_t a_offset, uint32_t b_of void AvmTraceBuilder::op_not(uint8_t indirect, uint32_t a_offset, uint32_t dst_offset, AvmMemoryTag in_tag) { auto clk = static_cast(main_trace.size()) + 1; - bool tag_match = true; - uint32_t direct_a_offset = a_offset; - uint32_t direct_dst_offset = dst_offset; - - bool indirect_a_flag = is_operand_indirect(indirect, 0); - bool indirect_c_flag = is_operand_indirect(indirect, 1); - if (indirect_a_flag) { - auto read_ind_a = - mem_trace_builder.indirect_read_and_load_from_memory(call_ptr, clk, IndirectRegister::IND_A, a_offset); - tag_match = read_ind_a.tag_match; - direct_a_offset = uint32_t(read_ind_a.val); - } + // Resolve any potential indirects in the order they are encoded in the indirect byte. + auto [resolved_a, resolved_c] = resolve_indirects<2>(indirect, { a_offset, dst_offset }); - if (indirect_c_flag) { - auto read_ind_c = - mem_trace_builder.indirect_read_and_load_from_memory(call_ptr, clk, IndirectRegister::IND_C, dst_offset); - tag_match = tag_match && read_ind_c.tag_match; - direct_dst_offset = uint32_t(read_ind_c.val); - } + // Reading from memory and loading into ia + auto read_a = constrained_read_from_memory(call_ptr, clk, resolved_a, in_tag, in_tag); - // Reading from memory and loading into ia. - auto read_a = - mem_trace_builder.read_and_load_from_memory(call_ptr, clk, IntermRegister::IA, direct_a_offset, in_tag, in_tag); - tag_match = read_a.tag_match && tag_match; + bool tag_match = read_a.tag_match; // ~a = c FF a = read_a.val; @@ -437,7 +463,7 @@ void AvmTraceBuilder::op_not(uint8_t indirect, uint32_t a_offset, uint32_t dst_o FF c = tag_match ? alu_trace_builder.op_not(a, in_tag, clk) : FF(0); // Write into memory value c from intermediate register ic. - mem_trace_builder.write_into_memory(call_ptr, clk, IntermRegister::IC, direct_dst_offset, c, in_tag, in_tag); + auto write_c = constrained_write_to_memory(call_ptr, clk, resolved_c, c, in_tag, in_tag); // Constrain gas cost gas_trace_builder.constrain_gas_lookup(clk, OpCode::NOT); @@ -446,15 +472,15 @@ void AvmTraceBuilder::op_not(uint8_t indirect, uint32_t a_offset, uint32_t dst_o .avm_main_clk = clk, .avm_main_alu_in_tag = FF(static_cast(in_tag)), .avm_main_call_ptr = call_ptr, - .avm_main_ia = a, - .avm_main_ic = c, - .avm_main_ind_a = indirect_a_flag ? FF(a_offset) : FF(0), - .avm_main_ind_c = indirect_c_flag ? FF(dst_offset) : FF(0), - .avm_main_ind_op_a = FF(static_cast(indirect_a_flag)), - .avm_main_ind_op_c = FF(static_cast(indirect_c_flag)), + .avm_main_ia = read_a.val, + .avm_main_ic = write_c.val, + .avm_main_ind_a = FF(read_a.indirect_address), + .avm_main_ind_c = FF(write_c.indirect_address), + .avm_main_ind_op_a = FF(static_cast(read_a.is_indirect)), + .avm_main_ind_op_c = FF(static_cast(write_c.is_indirect)), .avm_main_internal_return_ptr = FF(internal_return_ptr), - .avm_main_mem_idx_a = FF(direct_a_offset), - .avm_main_mem_idx_c = FF(direct_dst_offset), + .avm_main_mem_idx_a = FF(read_a.direct_address), + .avm_main_mem_idx_c = FF(write_c.direct_address), .avm_main_mem_op_a = FF(1), .avm_main_mem_op_c = FF(1), .avm_main_pc = FF(pc++), @@ -480,15 +506,12 @@ void AvmTraceBuilder::op_eq( { auto clk = static_cast(main_trace.size()) + 1; - auto const res = resolve_ind_three(call_ptr, clk, indirect, a_offset, b_offset, dst_offset); - bool tag_match = res.tag_match; + auto [resolved_a, resolved_b, resolved_c] = resolve_indirects<3>(indirect, { a_offset, b_offset, dst_offset }); // Reading from memory and loading into ia resp. ib. - auto read_a = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IA, res.direct_a_offset, in_tag, AvmMemoryTag::U8); - auto read_b = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IB, res.direct_b_offset, in_tag, AvmMemoryTag::U8); - tag_match = read_a.tag_match && read_b.tag_match; + auto read_a = constrained_read_from_memory(call_ptr, clk, resolved_a, in_tag, AvmMemoryTag::U8); + auto read_b = constrained_read_from_memory(call_ptr, clk, resolved_b, in_tag, AvmMemoryTag::U8); + bool tag_match = read_a.tag_match && read_b.tag_match; FF a = read_a.val; FF b = read_b.val; @@ -499,8 +522,8 @@ void AvmTraceBuilder::op_eq( FF c = tag_match ? alu_trace_builder.op_eq(a, b, in_tag, clk) : FF(0); // Write into memory value c from intermediate register ic. - mem_trace_builder.write_into_memory( - call_ptr, clk, IntermRegister::IC, res.direct_c_offset, c, in_tag, AvmMemoryTag::U8); + auto write_c = + constrained_write_to_memory(call_ptr, clk, resolved_c, c, in_tag, AvmMemoryTag::U8); // Constrain gas cost gas_trace_builder.constrain_gas_lookup(clk, OpCode::EQ); @@ -509,19 +532,19 @@ void AvmTraceBuilder::op_eq( .avm_main_clk = clk, .avm_main_alu_in_tag = FF(static_cast(in_tag)), .avm_main_call_ptr = call_ptr, - .avm_main_ia = a, - .avm_main_ib = b, - .avm_main_ic = c, - .avm_main_ind_a = res.indirect_flag_a ? FF(a_offset) : FF(0), - .avm_main_ind_b = res.indirect_flag_b ? FF(b_offset) : FF(0), - .avm_main_ind_c = res.indirect_flag_c ? FF(dst_offset) : FF(0), - .avm_main_ind_op_a = FF(static_cast(res.indirect_flag_a)), - .avm_main_ind_op_b = FF(static_cast(res.indirect_flag_b)), - .avm_main_ind_op_c = FF(static_cast(res.indirect_flag_c)), + .avm_main_ia = read_a.val, + .avm_main_ib = read_b.val, + .avm_main_ic = write_c.val, + .avm_main_ind_a = FF(read_a.indirect_address), + .avm_main_ind_b = FF(read_b.indirect_address), + .avm_main_ind_c = FF(write_c.indirect_address), + .avm_main_ind_op_a = FF(static_cast(read_a.is_indirect)), + .avm_main_ind_op_b = FF(static_cast(read_b.is_indirect)), + .avm_main_ind_op_c = FF(static_cast(write_c.is_indirect)), .avm_main_internal_return_ptr = FF(internal_return_ptr), - .avm_main_mem_idx_a = FF(res.direct_a_offset), - .avm_main_mem_idx_b = FF(res.direct_b_offset), - .avm_main_mem_idx_c = FF(res.direct_c_offset), + .avm_main_mem_idx_a = FF(read_a.direct_address), + .avm_main_mem_idx_b = FF(read_b.direct_address), + .avm_main_mem_idx_c = FF(write_c.direct_address), .avm_main_mem_op_a = FF(1), .avm_main_mem_op_b = FF(1), .avm_main_mem_op_c = FF(1), @@ -539,15 +562,12 @@ void AvmTraceBuilder::op_and( { auto clk = static_cast(main_trace.size()) + 1; - auto const res = resolve_ind_three(call_ptr, clk, indirect, a_offset, b_offset, dst_offset); - bool tag_match = res.tag_match; + auto [resolved_a, resolved_b, resolved_c] = resolve_indirects<3>(indirect, { a_offset, b_offset, dst_offset }); // Reading from memory and loading into ia resp. ib. - auto read_a = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IA, res.direct_a_offset, in_tag, in_tag); - auto read_b = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IB, res.direct_b_offset, in_tag, in_tag); - tag_match = read_a.tag_match && read_b.tag_match; + auto read_a = constrained_read_from_memory(call_ptr, clk, resolved_a, in_tag, in_tag); + auto read_b = constrained_read_from_memory(call_ptr, clk, resolved_b, in_tag, in_tag); + bool tag_match = read_a.tag_match && read_b.tag_match; FF a = tag_match ? read_a.val : FF(0); FF b = tag_match ? read_b.val : FF(0); @@ -555,7 +575,7 @@ void AvmTraceBuilder::op_and( FF c = tag_match ? bin_trace_builder.op_and(a, b, in_tag, clk) : FF(0); // Write into memory value c from intermediate register ic. - mem_trace_builder.write_into_memory(call_ptr, clk, IntermRegister::IC, res.direct_c_offset, c, in_tag, in_tag); + auto write_c = constrained_write_to_memory(call_ptr, clk, resolved_c, c, in_tag, in_tag); // Constrain gas cost gas_trace_builder.constrain_gas_lookup(clk, OpCode::AND); @@ -565,19 +585,19 @@ void AvmTraceBuilder::op_and( .avm_main_bin_op_id = FF(0), .avm_main_bin_sel = FF(1), .avm_main_call_ptr = call_ptr, - .avm_main_ia = a, - .avm_main_ib = b, - .avm_main_ic = c, - .avm_main_ind_a = res.indirect_flag_a ? FF(a_offset) : FF(0), - .avm_main_ind_b = res.indirect_flag_b ? FF(b_offset) : FF(0), - .avm_main_ind_c = res.indirect_flag_c ? FF(dst_offset) : FF(0), - .avm_main_ind_op_a = FF(static_cast(res.indirect_flag_a)), - .avm_main_ind_op_b = FF(static_cast(res.indirect_flag_b)), - .avm_main_ind_op_c = FF(static_cast(res.indirect_flag_c)), + .avm_main_ia = read_a.val, + .avm_main_ib = read_b.val, + .avm_main_ic = write_c.val, + .avm_main_ind_a = FF(read_a.indirect_address), + .avm_main_ind_b = FF(read_b.indirect_address), + .avm_main_ind_c = FF(write_c.indirect_address), + .avm_main_ind_op_a = FF(static_cast(read_a.is_indirect)), + .avm_main_ind_op_b = FF(static_cast(read_b.is_indirect)), + .avm_main_ind_op_c = FF(static_cast(write_c.is_indirect)), .avm_main_internal_return_ptr = FF(internal_return_ptr), - .avm_main_mem_idx_a = FF(res.direct_a_offset), - .avm_main_mem_idx_b = FF(res.direct_b_offset), - .avm_main_mem_idx_c = FF(res.direct_c_offset), + .avm_main_mem_idx_a = FF(read_a.direct_address), + .avm_main_mem_idx_b = FF(read_b.direct_address), + .avm_main_mem_idx_c = FF(write_c.direct_address), .avm_main_mem_op_a = FF(1), .avm_main_mem_op_b = FF(1), .avm_main_mem_op_c = FF(1), @@ -594,16 +614,12 @@ void AvmTraceBuilder::op_or( uint8_t indirect, uint32_t a_offset, uint32_t b_offset, uint32_t dst_offset, AvmMemoryTag in_tag) { auto clk = static_cast(main_trace.size()) + 1; - - auto const res = resolve_ind_three(call_ptr, clk, indirect, a_offset, b_offset, dst_offset); - bool tag_match = res.tag_match; + auto [resolved_a, resolved_b, resolved_c] = resolve_indirects<3>(indirect, { a_offset, b_offset, dst_offset }); // Reading from memory and loading into ia resp. ib. - auto read_a = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IA, res.direct_a_offset, in_tag, in_tag); - auto read_b = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IB, res.direct_b_offset, in_tag, in_tag); - tag_match = read_a.tag_match && read_b.tag_match; + auto read_a = constrained_read_from_memory(call_ptr, clk, resolved_a, in_tag, in_tag); + auto read_b = constrained_read_from_memory(call_ptr, clk, resolved_b, in_tag, in_tag); + bool tag_match = read_a.tag_match && read_b.tag_match; FF a = tag_match ? read_a.val : FF(0); FF b = tag_match ? read_b.val : FF(0); @@ -611,7 +627,7 @@ void AvmTraceBuilder::op_or( FF c = tag_match ? bin_trace_builder.op_or(a, b, in_tag, clk) : FF(0); // Write into memory value c from intermediate register ic. - mem_trace_builder.write_into_memory(call_ptr, clk, IntermRegister::IC, res.direct_c_offset, c, in_tag, in_tag); + auto write_c = constrained_write_to_memory(call_ptr, clk, resolved_c, c, in_tag, in_tag); // Constrain gas cost gas_trace_builder.constrain_gas_lookup(clk, OpCode::OR); @@ -621,19 +637,19 @@ void AvmTraceBuilder::op_or( .avm_main_bin_op_id = FF(1), .avm_main_bin_sel = FF(1), .avm_main_call_ptr = call_ptr, - .avm_main_ia = a, - .avm_main_ib = b, - .avm_main_ic = c, - .avm_main_ind_a = res.indirect_flag_a ? FF(a_offset) : FF(0), - .avm_main_ind_b = res.indirect_flag_b ? FF(b_offset) : FF(0), - .avm_main_ind_c = res.indirect_flag_c ? FF(dst_offset) : FF(0), - .avm_main_ind_op_a = FF(static_cast(res.indirect_flag_a)), - .avm_main_ind_op_b = FF(static_cast(res.indirect_flag_b)), - .avm_main_ind_op_c = FF(static_cast(res.indirect_flag_c)), + .avm_main_ia = read_a.val, + .avm_main_ib = read_b.val, + .avm_main_ic = write_c.val, + .avm_main_ind_a = FF(read_a.indirect_address), + .avm_main_ind_b = FF(read_b.indirect_address), + .avm_main_ind_c = FF(write_c.indirect_address), + .avm_main_ind_op_a = FF(static_cast(read_a.is_indirect)), + .avm_main_ind_op_b = FF(static_cast(read_b.is_indirect)), + .avm_main_ind_op_c = FF(static_cast(write_c.is_indirect)), .avm_main_internal_return_ptr = FF(internal_return_ptr), - .avm_main_mem_idx_a = FF(res.direct_a_offset), - .avm_main_mem_idx_b = FF(res.direct_b_offset), - .avm_main_mem_idx_c = FF(res.direct_c_offset), + .avm_main_mem_idx_a = FF(read_a.direct_address), + .avm_main_mem_idx_b = FF(read_b.direct_address), + .avm_main_mem_idx_c = FF(write_c.direct_address), .avm_main_mem_op_a = FF(1), .avm_main_mem_op_b = FF(1), .avm_main_mem_op_c = FF(1), @@ -651,15 +667,12 @@ void AvmTraceBuilder::op_xor( { auto clk = static_cast(main_trace.size()) + 1; - auto const res = resolve_ind_three(call_ptr, clk, indirect, a_offset, b_offset, dst_offset); - bool tag_match = res.tag_match; + auto [resolved_a, resolved_b, resolved_c] = resolve_indirects<3>(indirect, { a_offset, b_offset, dst_offset }); // Reading from memory and loading into ia resp. ib. - auto read_a = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IA, res.direct_a_offset, in_tag, in_tag); - auto read_b = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IB, res.direct_b_offset, in_tag, in_tag); - tag_match = read_a.tag_match && read_b.tag_match; + auto read_a = constrained_read_from_memory(call_ptr, clk, resolved_a, in_tag, in_tag); + auto read_b = constrained_read_from_memory(call_ptr, clk, resolved_b, in_tag, in_tag); + bool tag_match = read_a.tag_match && read_b.tag_match; FF a = tag_match ? read_a.val : FF(0); FF b = tag_match ? read_b.val : FF(0); @@ -667,7 +680,7 @@ void AvmTraceBuilder::op_xor( FF c = tag_match ? bin_trace_builder.op_xor(a, b, in_tag, clk) : FF(0); // Write into memory value c from intermediate register ic. - mem_trace_builder.write_into_memory(call_ptr, clk, IntermRegister::IC, res.direct_c_offset, c, in_tag, in_tag); + auto write_c = constrained_write_to_memory(call_ptr, clk, resolved_c, c, in_tag, in_tag); // Constrain gas cost gas_trace_builder.constrain_gas_lookup(clk, OpCode::XOR); @@ -677,19 +690,19 @@ void AvmTraceBuilder::op_xor( .avm_main_bin_op_id = FF(2), .avm_main_bin_sel = FF(1), .avm_main_call_ptr = call_ptr, - .avm_main_ia = a, - .avm_main_ib = b, - .avm_main_ic = c, - .avm_main_ind_a = res.indirect_flag_a ? FF(a_offset) : FF(0), - .avm_main_ind_b = res.indirect_flag_b ? FF(b_offset) : FF(0), - .avm_main_ind_c = res.indirect_flag_c ? FF(dst_offset) : FF(0), - .avm_main_ind_op_a = FF(static_cast(res.indirect_flag_a)), - .avm_main_ind_op_b = FF(static_cast(res.indirect_flag_b)), - .avm_main_ind_op_c = FF(static_cast(res.indirect_flag_c)), + .avm_main_ia = read_a.val, + .avm_main_ib = read_b.val, + .avm_main_ic = write_c.val, + .avm_main_ind_a = FF(read_a.indirect_address), + .avm_main_ind_b = FF(read_b.indirect_address), + .avm_main_ind_c = FF(write_c.indirect_address), + .avm_main_ind_op_a = FF(static_cast(read_a.is_indirect)), + .avm_main_ind_op_b = FF(static_cast(read_b.is_indirect)), + .avm_main_ind_op_c = FF(static_cast(write_c.is_indirect)), .avm_main_internal_return_ptr = FF(internal_return_ptr), - .avm_main_mem_idx_a = FF(res.direct_a_offset), - .avm_main_mem_idx_b = FF(res.direct_b_offset), - .avm_main_mem_idx_c = FF(res.direct_c_offset), + .avm_main_mem_idx_a = FF(read_a.direct_address), + .avm_main_mem_idx_b = FF(read_b.direct_address), + .avm_main_mem_idx_c = FF(write_c.direct_address), .avm_main_mem_op_a = FF(1), .avm_main_mem_op_b = FF(1), .avm_main_mem_op_c = FF(1), @@ -707,15 +720,11 @@ void AvmTraceBuilder::op_lt( { auto clk = static_cast(main_trace.size()) + 1; - auto const res = resolve_ind_three(call_ptr, clk, indirect, a_offset, b_offset, dst_offset); - bool tag_match = res.tag_match; + auto [resolved_a, resolved_b, resolved_c] = resolve_indirects<3>(indirect, { a_offset, b_offset, dst_offset }); - // Reading from memory and loading into ia resp. ib. - auto read_a = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IA, res.direct_a_offset, in_tag, AvmMemoryTag::U8); - auto read_b = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IB, res.direct_b_offset, in_tag, AvmMemoryTag::U8); - tag_match = read_a.tag_match && read_b.tag_match; + auto read_a = constrained_read_from_memory(call_ptr, clk, resolved_a, in_tag, AvmMemoryTag::U8); + auto read_b = constrained_read_from_memory(call_ptr, clk, resolved_b, in_tag, AvmMemoryTag::U8); + bool tag_match = read_a.tag_match && read_b.tag_match; FF a = tag_match ? read_a.val : FF(0); FF b = tag_match ? read_b.val : FF(0); @@ -723,8 +732,8 @@ void AvmTraceBuilder::op_lt( FF c = tag_match ? alu_trace_builder.op_lt(a, b, in_tag, clk) : FF(0); // Write into memory value c from intermediate register ic. - mem_trace_builder.write_into_memory( - call_ptr, clk, IntermRegister::IC, res.direct_c_offset, c, in_tag, AvmMemoryTag::U8); + auto write_c = + constrained_write_to_memory(call_ptr, clk, resolved_c, c, in_tag, AvmMemoryTag::U8); // Constrain gas cost gas_trace_builder.constrain_gas_lookup(clk, OpCode::LT); @@ -733,19 +742,19 @@ void AvmTraceBuilder::op_lt( .avm_main_clk = clk, .avm_main_alu_in_tag = FF(static_cast(in_tag)), .avm_main_call_ptr = call_ptr, - .avm_main_ia = a, - .avm_main_ib = b, - .avm_main_ic = c, - .avm_main_ind_a = res.indirect_flag_a ? FF(a_offset) : FF(0), - .avm_main_ind_b = res.indirect_flag_b ? FF(b_offset) : FF(0), - .avm_main_ind_c = res.indirect_flag_c ? FF(dst_offset) : FF(0), - .avm_main_ind_op_a = FF(static_cast(res.indirect_flag_a)), - .avm_main_ind_op_b = FF(static_cast(res.indirect_flag_b)), - .avm_main_ind_op_c = FF(static_cast(res.indirect_flag_c)), + .avm_main_ia = read_a.val, + .avm_main_ib = read_b.val, + .avm_main_ic = write_c.val, + .avm_main_ind_a = FF(read_a.indirect_address), + .avm_main_ind_b = FF(read_b.indirect_address), + .avm_main_ind_c = FF(write_c.indirect_address), + .avm_main_ind_op_a = FF(static_cast(read_a.is_indirect)), + .avm_main_ind_op_b = FF(static_cast(read_b.is_indirect)), + .avm_main_ind_op_c = FF(static_cast(write_c.is_indirect)), .avm_main_internal_return_ptr = FF(internal_return_ptr), - .avm_main_mem_idx_a = FF(res.direct_a_offset), - .avm_main_mem_idx_b = FF(res.direct_b_offset), - .avm_main_mem_idx_c = FF(res.direct_c_offset), + .avm_main_mem_idx_a = FF(read_a.direct_address), + .avm_main_mem_idx_b = FF(read_b.direct_address), + .avm_main_mem_idx_c = FF(write_c.direct_address), .avm_main_mem_op_a = FF(1), .avm_main_mem_op_b = FF(1), .avm_main_mem_op_c = FF(1), @@ -763,15 +772,12 @@ void AvmTraceBuilder::op_lte( { auto clk = static_cast(main_trace.size()) + 1; - auto const res = resolve_ind_three(call_ptr, clk, indirect, a_offset, b_offset, dst_offset); - bool tag_match = res.tag_match; + auto [resolved_a, resolved_b, resolved_c] = resolve_indirects<3>(indirect, { a_offset, b_offset, dst_offset }); // Reading from memory and loading into ia resp. ib. - auto read_a = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IA, res.direct_a_offset, in_tag, AvmMemoryTag::U8); - auto read_b = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IB, res.direct_b_offset, in_tag, AvmMemoryTag::U8); - tag_match = read_a.tag_match && read_b.tag_match; + auto read_a = constrained_read_from_memory(call_ptr, clk, resolved_a, in_tag, AvmMemoryTag::U8); + auto read_b = constrained_read_from_memory(call_ptr, clk, resolved_b, in_tag, AvmMemoryTag::U8); + bool tag_match = read_a.tag_match && read_b.tag_match; FF a = tag_match ? read_a.val : FF(0); FF b = tag_match ? read_b.val : FF(0); @@ -779,8 +785,8 @@ void AvmTraceBuilder::op_lte( FF c = tag_match ? alu_trace_builder.op_lte(a, b, in_tag, clk) : FF(0); // Write into memory value c from intermediate register ic. - mem_trace_builder.write_into_memory( - call_ptr, clk, IntermRegister::IC, res.direct_c_offset, c, in_tag, AvmMemoryTag::U8); + auto write_c = + constrained_write_to_memory(call_ptr, clk, resolved_c, c, in_tag, AvmMemoryTag::U8); // Constrain gas cost gas_trace_builder.constrain_gas_lookup(clk, OpCode::LTE); @@ -789,19 +795,19 @@ void AvmTraceBuilder::op_lte( .avm_main_clk = clk, .avm_main_alu_in_tag = FF(static_cast(in_tag)), .avm_main_call_ptr = call_ptr, - .avm_main_ia = a, - .avm_main_ib = b, - .avm_main_ic = c, - .avm_main_ind_a = res.indirect_flag_a ? FF(a_offset) : FF(0), - .avm_main_ind_b = res.indirect_flag_b ? FF(b_offset) : FF(0), - .avm_main_ind_c = res.indirect_flag_c ? FF(dst_offset) : FF(0), - .avm_main_ind_op_a = FF(static_cast(res.indirect_flag_a)), - .avm_main_ind_op_b = FF(static_cast(res.indirect_flag_b)), - .avm_main_ind_op_c = FF(static_cast(res.indirect_flag_c)), + .avm_main_ia = read_a.val, + .avm_main_ib = read_b.val, + .avm_main_ic = write_c.val, + .avm_main_ind_a = FF(read_a.indirect_address), + .avm_main_ind_b = FF(read_b.indirect_address), + .avm_main_ind_c = FF(write_c.indirect_address), + .avm_main_ind_op_a = FF(static_cast(read_a.is_indirect)), + .avm_main_ind_op_b = FF(static_cast(read_b.is_indirect)), + .avm_main_ind_op_c = FF(static_cast(write_c.is_indirect)), .avm_main_internal_return_ptr = FF(internal_return_ptr), - .avm_main_mem_idx_a = FF(res.direct_a_offset), - .avm_main_mem_idx_b = FF(res.direct_b_offset), - .avm_main_mem_idx_c = FF(res.direct_c_offset), + .avm_main_mem_idx_a = FF(read_a.direct_address), + .avm_main_mem_idx_b = FF(read_b.direct_address), + .avm_main_mem_idx_c = FF(write_c.direct_address), .avm_main_mem_op_a = FF(1), .avm_main_mem_op_b = FF(1), .avm_main_mem_op_c = FF(1), @@ -820,15 +826,12 @@ void AvmTraceBuilder::op_shr( auto clk = static_cast(main_trace.size()) + 1; - auto const res = resolve_ind_three(call_ptr, clk, indirect, a_offset, b_offset, dst_offset); - bool tag_match = res.tag_match; + auto [resolved_a, resolved_b, resolved_c] = resolve_indirects<3>(indirect, { a_offset, b_offset, dst_offset }); // Reading from memory and loading into ia resp. ib. - auto read_a = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IA, res.direct_a_offset, in_tag, in_tag); - auto read_b = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IB, res.direct_b_offset, in_tag, in_tag); - tag_match = read_a.tag_match && read_b.tag_match; + auto read_a = constrained_read_from_memory(call_ptr, clk, resolved_a, in_tag, in_tag); + auto read_b = constrained_read_from_memory(call_ptr, clk, resolved_b, in_tag, in_tag); + bool tag_match = read_a.tag_match && read_b.tag_match; FF a = tag_match ? read_a.val : FF(0); FF b = tag_match ? read_b.val : FF(0); @@ -836,8 +839,7 @@ void AvmTraceBuilder::op_shr( FF c = tag_match ? alu_trace_builder.op_shr(a, b, in_tag, clk) : FF(0); // Write into memory value c from intermediate register ic. - mem_trace_builder.write_into_memory(call_ptr, clk, IntermRegister::IC, res.direct_c_offset, c, in_tag, in_tag); - + auto write_c = constrained_write_to_memory(call_ptr, clk, resolved_c, c, in_tag, in_tag); // Constrain gas cost gas_trace_builder.constrain_gas_lookup(clk, OpCode::SHR); @@ -845,19 +847,19 @@ void AvmTraceBuilder::op_shr( .avm_main_clk = clk, .avm_main_alu_in_tag = FF(static_cast(in_tag)), .avm_main_call_ptr = call_ptr, - .avm_main_ia = a, - .avm_main_ib = b, - .avm_main_ic = c, - .avm_main_ind_a = res.indirect_flag_a ? FF(a_offset) : FF(0), - .avm_main_ind_b = res.indirect_flag_b ? FF(b_offset) : FF(0), - .avm_main_ind_c = res.indirect_flag_c ? FF(dst_offset) : FF(0), - .avm_main_ind_op_a = FF(static_cast(res.indirect_flag_a)), - .avm_main_ind_op_b = FF(static_cast(res.indirect_flag_b)), - .avm_main_ind_op_c = FF(static_cast(res.indirect_flag_c)), + .avm_main_ia = read_a.val, + .avm_main_ib = read_b.val, + .avm_main_ic = write_c.val, + .avm_main_ind_a = FF(read_a.indirect_address), + .avm_main_ind_b = FF(read_b.indirect_address), + .avm_main_ind_c = FF(write_c.indirect_address), + .avm_main_ind_op_a = FF(static_cast(read_a.is_indirect)), + .avm_main_ind_op_b = FF(static_cast(read_b.is_indirect)), + .avm_main_ind_op_c = FF(static_cast(write_c.is_indirect)), .avm_main_internal_return_ptr = FF(internal_return_ptr), - .avm_main_mem_idx_a = FF(res.direct_a_offset), - .avm_main_mem_idx_b = FF(res.direct_b_offset), - .avm_main_mem_idx_c = FF(res.direct_c_offset), + .avm_main_mem_idx_a = FF(read_a.direct_address), + .avm_main_mem_idx_b = FF(read_b.direct_address), + .avm_main_mem_idx_c = FF(write_c.direct_address), .avm_main_mem_op_a = FF(1), .avm_main_mem_op_b = FF(1), .avm_main_mem_op_c = FF(1), @@ -875,15 +877,12 @@ void AvmTraceBuilder::op_shl( { auto clk = static_cast(main_trace.size()) + 1; - auto const res = resolve_ind_three(call_ptr, clk, indirect, a_offset, b_offset, dst_offset); - bool tag_match = res.tag_match; + auto [resolved_a, resolved_b, resolved_c] = resolve_indirects<3>(indirect, { a_offset, b_offset, dst_offset }); // Reading from memory and loading into ia resp. ib. - auto read_a = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IA, res.direct_a_offset, in_tag, in_tag); - auto read_b = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IB, res.direct_b_offset, in_tag, in_tag); - tag_match = read_a.tag_match && read_b.tag_match; + auto read_a = constrained_read_from_memory(call_ptr, clk, resolved_a, in_tag, in_tag); + auto read_b = constrained_read_from_memory(call_ptr, clk, resolved_b, in_tag, in_tag); + bool tag_match = read_a.tag_match && read_b.tag_match; FF a = tag_match ? read_a.val : FF(0); FF b = tag_match ? read_b.val : FF(0); @@ -891,8 +890,7 @@ void AvmTraceBuilder::op_shl( FF c = tag_match ? alu_trace_builder.op_shl(a, b, in_tag, clk) : FF(0); // Write into memory value c from intermediate register ic. - mem_trace_builder.write_into_memory(call_ptr, clk, IntermRegister::IC, res.direct_c_offset, c, in_tag, in_tag); - + auto write_c = constrained_write_to_memory(call_ptr, clk, resolved_c, c, in_tag, in_tag); // Constrain gas cost gas_trace_builder.constrain_gas_lookup(clk, OpCode::SHL); @@ -900,19 +898,19 @@ void AvmTraceBuilder::op_shl( .avm_main_clk = clk, .avm_main_alu_in_tag = FF(static_cast(in_tag)), .avm_main_call_ptr = call_ptr, - .avm_main_ia = a, - .avm_main_ib = b, - .avm_main_ic = c, - .avm_main_ind_a = res.indirect_flag_a ? FF(a_offset) : FF(0), - .avm_main_ind_b = res.indirect_flag_b ? FF(b_offset) : FF(0), - .avm_main_ind_c = res.indirect_flag_c ? FF(dst_offset) : FF(0), - .avm_main_ind_op_a = FF(static_cast(res.indirect_flag_a)), - .avm_main_ind_op_b = FF(static_cast(res.indirect_flag_b)), - .avm_main_ind_op_c = FF(static_cast(res.indirect_flag_c)), + .avm_main_ia = read_a.val, + .avm_main_ib = read_b.val, + .avm_main_ic = write_c.val, + .avm_main_ind_a = FF(read_a.indirect_address), + .avm_main_ind_b = FF(read_b.indirect_address), + .avm_main_ind_c = FF(write_c.indirect_address), + .avm_main_ind_op_a = FF(static_cast(read_a.is_indirect)), + .avm_main_ind_op_b = FF(static_cast(read_b.is_indirect)), + .avm_main_ind_op_c = FF(static_cast(write_c.is_indirect)), .avm_main_internal_return_ptr = FF(internal_return_ptr), - .avm_main_mem_idx_a = FF(res.direct_a_offset), - .avm_main_mem_idx_b = FF(res.direct_b_offset), - .avm_main_mem_idx_c = FF(res.direct_c_offset), + .avm_main_mem_idx_a = FF(read_a.direct_address), + .avm_main_mem_idx_b = FF(read_b.direct_address), + .avm_main_mem_idx_c = FF(write_c.direct_address), .avm_main_mem_op_a = FF(1), .avm_main_mem_op_b = FF(1), .avm_main_mem_op_c = FF(1), @@ -943,19 +941,10 @@ void AvmTraceBuilder::op_set(uint8_t indirect, uint128_t val, uint32_t dst_offse { auto const clk = static_cast(main_trace.size()) + 1; auto const val_ff = FF{ uint256_t::from_uint128(val) }; - uint32_t direct_dst_offset = dst_offset; // Overriden in indirect mode - bool indirect_dst_flag = is_operand_indirect(indirect, 0); - bool tag_match = true; - - if (indirect_dst_flag) { - auto read_ind_c = - mem_trace_builder.indirect_read_and_load_from_memory(call_ptr, clk, IndirectRegister::IND_C, dst_offset); - tag_match = read_ind_c.tag_match; - direct_dst_offset = uint32_t(read_ind_c.val); - } + auto [resolved_c] = resolve_indirects<1>(indirect, { dst_offset }); - mem_trace_builder.write_into_memory( - call_ptr, clk, IntermRegister::IC, direct_dst_offset, val_ff, AvmMemoryTag::U0, in_tag); + auto write_c = + constrained_write_to_memory(call_ptr, clk, resolved_c, val_ff, AvmMemoryTag::U0, in_tag); // Constrain gas cost gas_trace_builder.constrain_gas_lookup(clk, OpCode::SET); @@ -963,16 +952,16 @@ void AvmTraceBuilder::op_set(uint8_t indirect, uint128_t val, uint32_t dst_offse main_trace.push_back(Row{ .avm_main_clk = clk, .avm_main_call_ptr = call_ptr, - .avm_main_ic = val_ff, - .avm_main_ind_c = indirect_dst_flag ? dst_offset : 0, - .avm_main_ind_op_c = static_cast(indirect_dst_flag), + .avm_main_ic = write_c.val, + .avm_main_ind_c = FF(write_c.indirect_address), + .avm_main_ind_op_c = FF(static_cast(write_c.is_indirect)), .avm_main_internal_return_ptr = internal_return_ptr, - .avm_main_mem_idx_c = direct_dst_offset, + .avm_main_mem_idx_c = FF(write_c.direct_address), .avm_main_mem_op_activate_gas = 1, // TODO: remove in the long term .avm_main_mem_op_c = 1, .avm_main_pc = pc++, .avm_main_rwc = 1, - .avm_main_tag_err = static_cast(!tag_match), + .avm_main_tag_err = static_cast(!write_c.tag_match), .avm_main_w_in_tag = static_cast(in_tag), }); } @@ -1158,35 +1147,28 @@ 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) + uint8_t indirect, uint32_t dst_offset, uint32_t selector, FF value, AvmMemoryTag w_tag) { auto const clk = static_cast(main_trace.size()) + 1; - bool tag_match = true; - uint32_t direct_dst_offset = dst_offset; - if (indirect) { - auto read_ind_dst = - mem_trace_builder.indirect_read_and_load_from_memory(call_ptr, clk, IndirectRegister::IND_A, dst_offset); - direct_dst_offset = uint32_t(read_ind_dst.val); - tag_match = tag_match && read_ind_dst.tag_match; - } - - AvmMemoryTag r_tag = AvmMemoryTag::U0; - mem_trace_builder.write_into_memory(call_ptr, clk, IntermRegister::IA, direct_dst_offset, value, r_tag, w_tag); + auto [resolved_dst] = resolve_indirects<1>(indirect, { dst_offset }); + auto write_dst = + constrained_write_to_memory(call_ptr, clk, resolved_dst, value, AvmMemoryTag::U0, w_tag); return Row{ .avm_main_clk = clk, .avm_kernel_kernel_in_offset = selector, .avm_main_call_ptr = call_ptr, .avm_main_ia = value, - .avm_main_ind_a = indirect ? FF(dst_offset) : FF(0), - .avm_main_ind_op_a = FF(static_cast(indirect)), + .avm_main_ind_a = FF(write_dst.indirect_address), + .avm_main_ind_op_a = FF(static_cast(write_dst.is_indirect)), .avm_main_internal_return_ptr = internal_return_ptr, - .avm_main_mem_idx_a = direct_dst_offset, + .avm_main_mem_idx_a = FF(write_dst.direct_address), .avm_main_mem_op_a = 1, .avm_main_pc = pc++, .avm_main_q_kernel_lookup = 1, .avm_main_rwa = 1, + .avm_main_tag_err = FF(static_cast(!write_dst.tag_match)), .avm_main_w_in_tag = static_cast(w_tag), }; } @@ -1194,10 +1176,7 @@ Row AvmTraceBuilder::create_kernel_lookup_opcode( void AvmTraceBuilder::op_storage_address(uint8_t indirect, uint32_t dst_offset) { FF ia_value = kernel_trace_builder.op_storage_address(); - - bool indirect_dst_flag = is_operand_indirect(indirect, 0); - Row row = create_kernel_lookup_opcode( - indirect_dst_flag, dst_offset, STORAGE_ADDRESS_SELECTOR, ia_value, AvmMemoryTag::FF); + Row row = create_kernel_lookup_opcode(indirect, dst_offset, STORAGE_ADDRESS_SELECTOR, ia_value, AvmMemoryTag::FF); row.avm_main_sel_op_storage_address = FF(1); // Constrain gas cost @@ -1209,9 +1188,7 @@ void AvmTraceBuilder::op_storage_address(uint8_t indirect, uint32_t dst_offset) void AvmTraceBuilder::op_sender(uint8_t indirect, uint32_t dst_offset) { FF ia_value = kernel_trace_builder.op_sender(); - - bool indirect_dst_flag = is_operand_indirect(indirect, 0); - Row row = create_kernel_lookup_opcode(indirect_dst_flag, dst_offset, SENDER_SELECTOR, ia_value, AvmMemoryTag::FF); + Row row = create_kernel_lookup_opcode(indirect, dst_offset, SENDER_SELECTOR, ia_value, AvmMemoryTag::FF); row.avm_main_sel_op_sender = FF(1); // Constrain gas cost @@ -1223,9 +1200,7 @@ void AvmTraceBuilder::op_sender(uint8_t indirect, uint32_t dst_offset) void AvmTraceBuilder::op_address(uint8_t indirect, uint32_t dst_offset) { FF ia_value = kernel_trace_builder.op_address(); - - bool indirect_dst_flag = is_operand_indirect(indirect, 0); - Row row = create_kernel_lookup_opcode(indirect_dst_flag, dst_offset, ADDRESS_SELECTOR, ia_value, AvmMemoryTag::FF); + Row row = create_kernel_lookup_opcode(indirect, dst_offset, ADDRESS_SELECTOR, ia_value, AvmMemoryTag::FF); row.avm_main_sel_op_address = FF(1); // Constrain gas cost @@ -1237,10 +1212,7 @@ void AvmTraceBuilder::op_address(uint8_t indirect, uint32_t dst_offset) void AvmTraceBuilder::op_fee_per_da_gas(uint8_t indirect, uint32_t dst_offset) { FF ia_value = kernel_trace_builder.op_fee_per_da_gas(); - - bool indirect_dst_flag = is_operand_indirect(indirect, 0); - Row row = - create_kernel_lookup_opcode(indirect_dst_flag, dst_offset, FEE_PER_DA_GAS_SELECTOR, ia_value, AvmMemoryTag::FF); + Row row = create_kernel_lookup_opcode(indirect, dst_offset, FEE_PER_DA_GAS_SELECTOR, ia_value, AvmMemoryTag::FF); row.avm_main_sel_op_fee_per_da_gas = FF(1); // Constrain gas cost @@ -1252,10 +1224,7 @@ void AvmTraceBuilder::op_fee_per_da_gas(uint8_t indirect, uint32_t dst_offset) void AvmTraceBuilder::op_fee_per_l2_gas(uint8_t indirect, uint32_t dst_offset) { FF ia_value = kernel_trace_builder.op_fee_per_l2_gas(); - - bool indirect_dst_flag = is_operand_indirect(indirect, 0); - Row row = - create_kernel_lookup_opcode(indirect_dst_flag, dst_offset, FEE_PER_L2_GAS_SELECTOR, ia_value, AvmMemoryTag::FF); + Row row = create_kernel_lookup_opcode(indirect, dst_offset, FEE_PER_L2_GAS_SELECTOR, ia_value, AvmMemoryTag::FF); row.avm_main_sel_op_fee_per_l2_gas = FF(1); // Constrain gas cost @@ -1267,10 +1236,7 @@ void AvmTraceBuilder::op_fee_per_l2_gas(uint8_t indirect, uint32_t dst_offset) void AvmTraceBuilder::op_transaction_fee(uint8_t indirect, uint32_t dst_offset) { FF ia_value = kernel_trace_builder.op_transaction_fee(); - - bool indirect_dst_flag = is_operand_indirect(indirect, 0); - Row row = create_kernel_lookup_opcode( - indirect_dst_flag, dst_offset, TRANSACTION_FEE_SELECTOR, ia_value, AvmMemoryTag::FF); + Row row = create_kernel_lookup_opcode(indirect, dst_offset, TRANSACTION_FEE_SELECTOR, ia_value, AvmMemoryTag::FF); row.avm_main_sel_op_transaction_fee = FF(1); // Constrain gas cost @@ -1282,9 +1248,7 @@ void AvmTraceBuilder::op_transaction_fee(uint8_t indirect, uint32_t dst_offset) void AvmTraceBuilder::op_chain_id(uint8_t indirect, uint32_t dst_offset) { FF ia_value = kernel_trace_builder.op_chain_id(); - - bool indirect_dst_flag = is_operand_indirect(indirect, 0); - Row row = create_kernel_lookup_opcode(indirect_dst_flag, dst_offset, CHAIN_ID_SELECTOR, ia_value, AvmMemoryTag::FF); + Row row = create_kernel_lookup_opcode(indirect, dst_offset, CHAIN_ID_SELECTOR, ia_value, AvmMemoryTag::FF); row.avm_main_sel_op_chain_id = FF(1); // Constrain gas cost @@ -1296,9 +1260,7 @@ void AvmTraceBuilder::op_chain_id(uint8_t indirect, uint32_t dst_offset) void AvmTraceBuilder::op_version(uint8_t indirect, uint32_t dst_offset) { FF ia_value = kernel_trace_builder.op_version(); - - bool indirect_dst_flag = is_operand_indirect(indirect, 0); - Row row = create_kernel_lookup_opcode(indirect_dst_flag, dst_offset, VERSION_SELECTOR, ia_value, AvmMemoryTag::FF); + Row row = create_kernel_lookup_opcode(indirect, dst_offset, VERSION_SELECTOR, ia_value, AvmMemoryTag::FF); row.avm_main_sel_op_version = FF(1); // Constrain gas cost @@ -1310,10 +1272,7 @@ void AvmTraceBuilder::op_version(uint8_t indirect, uint32_t dst_offset) void AvmTraceBuilder::op_block_number(uint8_t indirect, uint32_t dst_offset) { FF ia_value = kernel_trace_builder.op_block_number(); - - bool indirect_dst_flag = is_operand_indirect(indirect, 0); - Row row = - create_kernel_lookup_opcode(indirect_dst_flag, dst_offset, BLOCK_NUMBER_SELECTOR, ia_value, AvmMemoryTag::FF); + Row row = create_kernel_lookup_opcode(indirect, dst_offset, BLOCK_NUMBER_SELECTOR, ia_value, AvmMemoryTag::FF); row.avm_main_sel_op_block_number = FF(1); // Constrain gas cost @@ -1325,9 +1284,7 @@ void AvmTraceBuilder::op_block_number(uint8_t indirect, uint32_t dst_offset) void AvmTraceBuilder::op_coinbase(uint8_t indirect, uint32_t dst_offset) { FF ia_value = kernel_trace_builder.op_coinbase(); - - bool indirect_dst_flag = is_operand_indirect(indirect, 0); - Row row = create_kernel_lookup_opcode(indirect_dst_flag, dst_offset, COINBASE_SELECTOR, ia_value, AvmMemoryTag::FF); + Row row = create_kernel_lookup_opcode(indirect, dst_offset, COINBASE_SELECTOR, ia_value, AvmMemoryTag::FF); row.avm_main_sel_op_coinbase = FF(1); // Constrain gas cost @@ -1339,10 +1296,7 @@ void AvmTraceBuilder::op_coinbase(uint8_t indirect, uint32_t dst_offset) void AvmTraceBuilder::op_timestamp(uint8_t indirect, uint32_t dst_offset) { FF ia_value = kernel_trace_builder.op_timestamp(); - - bool indirect_dst_flag = is_operand_indirect(indirect, 0); - Row row = - create_kernel_lookup_opcode(indirect_dst_flag, dst_offset, TIMESTAMP_SELECTOR, ia_value, AvmMemoryTag::U64); + Row row = create_kernel_lookup_opcode(indirect, dst_offset, TIMESTAMP_SELECTOR, ia_value, AvmMemoryTag::U64); row.avm_main_sel_op_timestamp = FF(1); // Constrain gas cost @@ -1354,32 +1308,23 @@ 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(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, direct_data_offset, AvmMemoryTag::FF, AvmMemoryTag::U0); + auto [resolved_data] = resolve_indirects<1>(indirect, { data_offset }); + auto read_a = constrained_read_from_memory( + call_ptr, clk, resolved_data, AvmMemoryTag::FF, AvmMemoryTag::U0); + bool tag_match = read_a.tag_match; return Row{ .avm_main_clk = clk, .avm_main_ia = read_a.val, - .avm_main_ind_a = indirect_data_flag ? FF(data_offset) : FF(0), - .avm_main_ind_op_a = FF(static_cast(indirect)), + .avm_main_ind_a = FF(read_a.indirect_address), + .avm_main_ind_op_a = FF(static_cast(read_a.is_indirect)), .avm_main_internal_return_ptr = internal_return_ptr, - .avm_main_mem_idx_a = direct_data_offset, + .avm_main_mem_idx_a = FF(read_a.direct_address), .avm_main_mem_op_a = 1, .avm_main_pc = pc++, .avm_main_q_kernel_output_lookup = 1, .avm_main_r_in_tag = static_cast(AvmMemoryTag::FF), - .avm_main_rwa = 0, + .avm_main_tag_err = FF(static_cast(!tag_match)), }; } @@ -1390,52 +1335,31 @@ Row AvmTraceBuilder::create_kernel_output_opcode_with_metadata(uint8_t indirect, uint32_t metadata_offset, AvmMemoryTag metadata_r_tag) { + auto [resolved_data, resolved_metadata] = resolve_indirects<2>(indirect, { data_offset, metadata_offset }); - 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, 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, direct_metadata_offset, metadata_r_tag, AvmMemoryTag::U0); + auto read_a = + constrained_read_from_memory(call_ptr, clk, resolved_data, data_r_tag, AvmMemoryTag::U0); + auto read_b = constrained_read_from_memory( + call_ptr, clk, resolved_metadata, metadata_r_tag, AvmMemoryTag::U0); + bool tag_match = read_a.tag_match && read_b.tag_match; return Row{ .avm_main_clk = clk, .avm_main_ia = read_a.val, .avm_main_ib = read_b.val, - .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_ind_a = FF(read_a.indirect_address), + .avm_main_ind_b = FF(read_b.indirect_address), + .avm_main_ind_op_a = FF(static_cast(read_a.is_indirect)), + .avm_main_ind_op_b = FF(static_cast(read_b.is_indirect)), .avm_main_internal_return_ptr = internal_return_ptr, - .avm_main_mem_idx_a = direct_data_offset, - .avm_main_mem_idx_b = direct_metadata_offset, + .avm_main_mem_idx_a = FF(read_a.direct_address), + .avm_main_mem_idx_b = FF(read_b.direct_address), .avm_main_mem_op_a = 1, .avm_main_mem_op_b = 1, .avm_main_pc = pc++, .avm_main_q_kernel_output_lookup = 1, .avm_main_r_in_tag = static_cast(data_r_tag), - .avm_main_rwa = 0, - .avm_main_rwb = 0, + .avm_main_tag_err = FF(static_cast(!tag_match)), }; } @@ -1448,45 +1372,25 @@ Row AvmTraceBuilder::create_kernel_output_opcode_with_set_metadata_output_from_h 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); + auto [resolved_data, resolved_metadata] = resolve_indirects<2>(indirect, { data_offset, metadata_offset }); + auto read_a = constrained_read_from_memory( + call_ptr, clk, resolved_data, AvmMemoryTag::FF, AvmMemoryTag::U8); - 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, direct_metadata_offset, exists, AvmMemoryTag::FF, AvmMemoryTag::U8); + auto write_b = constrained_write_to_memory( + call_ptr, clk, resolved_metadata, exists, AvmMemoryTag::FF, AvmMemoryTag::U8); + bool tag_match = read_a.tag_match && write_b.tag_match; return Row{ .avm_main_clk = clk, .avm_main_ia = read_a.val, - .avm_main_ib = exists, - .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_ib = write_b.val, + .avm_main_ind_a = FF(read_a.indirect_address), + .avm_main_ind_b = FF(write_b.indirect_address), + .avm_main_ind_op_a = FF(static_cast(read_a.is_indirect)), + .avm_main_ind_op_b = FF(static_cast(write_b.is_indirect)), .avm_main_internal_return_ptr = internal_return_ptr, - .avm_main_mem_idx_a = direct_data_offset, - .avm_main_mem_idx_b = direct_metadata_offset, + .avm_main_mem_idx_a = FF(read_a.direct_address), + .avm_main_mem_idx_b = FF(write_b.direct_address), .avm_main_mem_op_a = 1, .avm_main_mem_op_b = 1, .avm_main_pc = pc++, @@ -1494,6 +1398,7 @@ Row AvmTraceBuilder::create_kernel_output_opcode_with_set_metadata_output_from_h .avm_main_r_in_tag = static_cast(AvmMemoryTag::FF), .avm_main_rwa = 0, .avm_main_rwb = 1, + .avm_main_tag_err = static_cast(!tag_match), .avm_main_w_in_tag = static_cast(AvmMemoryTag::U8), }; } @@ -1506,41 +1411,24 @@ Row AvmTraceBuilder::create_kernel_output_opcode_with_set_value_from_hint(uint8_ 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, 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, direct_metadata_offset, AvmMemoryTag::FF, AvmMemoryTag::FF); + auto [resolved_data, resolved_metadata] = resolve_indirects<2>(indirect, { data_offset, metadata_offset }); + auto write_a = constrained_write_to_memory( + call_ptr, clk, resolved_data, value, AvmMemoryTag::FF, AvmMemoryTag::FF); + auto read_b = constrained_read_from_memory( + call_ptr, clk, resolved_metadata, AvmMemoryTag::FF, AvmMemoryTag::FF); + bool tag_match = write_a.tag_match && read_b.tag_match; return Row{ .avm_main_clk = clk, - .avm_main_ia = value, + .avm_main_ia = write_a.val, .avm_main_ib = read_b.val, - .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_ind_a = FF(write_a.indirect_address), + .avm_main_ind_b = FF(read_b.indirect_address), + .avm_main_ind_op_a = FF(static_cast(write_a.is_indirect)), + .avm_main_ind_op_b = FF(static_cast(read_b.is_indirect)), .avm_main_internal_return_ptr = internal_return_ptr, - .avm_main_mem_idx_a = direct_data_offset, - .avm_main_mem_idx_b = direct_metadata_offset, + .avm_main_mem_idx_a = FF(write_a.direct_address), + .avm_main_mem_idx_b = FF(read_b.direct_address), .avm_main_mem_op_a = 1, .avm_main_mem_op_b = 1, .avm_main_pc = pc, // No PC increment here since we do it in the specific ops @@ -1548,6 +1436,7 @@ Row AvmTraceBuilder::create_kernel_output_opcode_with_set_value_from_hint(uint8_ .avm_main_r_in_tag = static_cast(AvmMemoryTag::FF), .avm_main_rwa = 1, .avm_main_rwb = 0, + .avm_main_tag_err = static_cast(!tag_match), .avm_main_w_in_tag = static_cast(AvmMemoryTag::FF), }; } @@ -1668,35 +1557,30 @@ void AvmTraceBuilder::op_sload(uint8_t indirect, uint32_t slot_offset, uint32_t { auto clk = static_cast(main_trace.size()) + 1; - // TODO: align usage of indirect with simulator - // TODO: support indirect slot offset - bool dest_offset_is_indirect = is_operand_indirect(indirect, 1); - - auto direct_dest_offset = dest_offset; - if (dest_offset_is_indirect) { - auto read_ind_dest_offset = - mem_trace_builder.indirect_read_and_load_from_memory(call_ptr, clk, IndirectRegister::IND_A, dest_offset); - direct_dest_offset = uint32_t(read_ind_dest_offset.val); - } - auto read_dest_value = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IA, direct_dest_offset, AvmMemoryTag::FF, AvmMemoryTag::FF); - - AvmMemTraceBuilder::MemRead read_slot = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IB, slot_offset, AvmMemoryTag::FF, AvmMemoryTag::FF); + auto [resolved_slot, resolved_dest] = resolve_indirects<2>(indirect, { slot_offset, dest_offset }); + auto read_dest_value = constrained_read_from_memory( + call_ptr, clk, resolved_dest, AvmMemoryTag::FF, AvmMemoryTag::FF); + auto read_slot = constrained_read_from_memory( + call_ptr, clk, resolved_slot, AvmMemoryTag::FF, AvmMemoryTag::FF); + // TODO: Reenable tag_match + // bool tag_match = read_dest_value.tag_match && read_slot.tag_match; main_trace.push_back(Row{ .avm_main_clk = clk, .avm_main_ia = read_dest_value.val, .avm_main_ib = read_slot.val, - .avm_main_ind_a = dest_offset_is_indirect ? dest_offset : 0, - .avm_main_ind_op_a = FF(static_cast(dest_offset_is_indirect)), + .avm_main_ind_a = FF(read_dest_value.indirect_address), + .avm_main_ind_b = FF(read_slot.indirect_address), + .avm_main_ind_op_a = FF(static_cast(read_dest_value.is_indirect)), + .avm_main_ind_op_b = FF(static_cast(read_slot.is_indirect)), .avm_main_internal_return_ptr = FF(internal_return_ptr), - .avm_main_mem_idx_a = FF(direct_dest_offset), - .avm_main_mem_idx_b = FF(slot_offset), + .avm_main_mem_idx_a = FF(read_dest_value.direct_address), + .avm_main_mem_idx_b = FF(read_slot.direct_address), .avm_main_mem_op_a = FF(1), .avm_main_mem_op_b = FF(1), .avm_main_pc = pc, // No PC increment here since this is the same opcode as the rows created below .avm_main_r_in_tag = FF(static_cast(AvmMemoryTag::FF)), + // .avm_main_tag_err = FF(static_cast(tag_match)), .avm_main_w_in_tag = FF(static_cast(AvmMemoryTag::FF)), }); clk++; @@ -1704,15 +1588,20 @@ void AvmTraceBuilder::op_sload(uint8_t indirect, uint32_t slot_offset, uint32_t for (uint32_t i = 0; i < size; i++) { FF value = execution_hints.get_side_effect_hints().at(side_effect_counter); - mem_trace_builder.write_into_memory( - call_ptr, clk, IntermRegister::IA, direct_dest_offset + i, value, AvmMemoryTag::FF, AvmMemoryTag::FF); + mem_trace_builder.write_into_memory(call_ptr, + clk, + IntermRegister::IA, + read_dest_value.direct_address + i, + value, + AvmMemoryTag::FF, + AvmMemoryTag::FF); auto row = Row{ .avm_main_clk = clk, .avm_main_ia = value, .avm_main_ib = read_slot.val + i, // slot increments each time .avm_main_internal_return_ptr = internal_return_ptr, - .avm_main_mem_idx_a = direct_dest_offset + i, + .avm_main_mem_idx_a = read_dest_value.direct_address + i, .avm_main_mem_op_a = 1, .avm_main_pc = pc, // No PC increment here since this is the same opcode for all loop iterations .avm_main_q_kernel_output_lookup = 1, @@ -1739,54 +1628,51 @@ void AvmTraceBuilder::op_sstore(uint8_t indirect, uint32_t src_offset, uint32_t { auto clk = static_cast(main_trace.size()) + 1; - // TODO: align usage of indirect with simulator - // TODO: support indirect slot offset - bool src_offset_is_indirect = is_operand_indirect(indirect, 0); + auto [resolved_src, resolved_slot] = resolve_indirects<2>(indirect, { src_offset, slot_offset }); - // Resolve loads and indirect - auto direct_src_offset = src_offset; - if (src_offset_is_indirect) { - auto read_ind_src_offset = - mem_trace_builder.indirect_read_and_load_from_memory(call_ptr, clk, IndirectRegister::IND_A, src_offset); - direct_src_offset = uint32_t(read_ind_src_offset.val); - } - auto read_src_value = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IA, direct_src_offset, AvmMemoryTag::FF, AvmMemoryTag::FF); - - auto read_slot = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IB, slot_offset, AvmMemoryTag::FF, AvmMemoryTag::FF); + auto read_src_value = constrained_read_from_memory( + call_ptr, clk, resolved_src, AvmMemoryTag::FF, AvmMemoryTag::FF); + auto read_slot = constrained_read_from_memory( + call_ptr, clk, resolved_slot, AvmMemoryTag::FF, AvmMemoryTag::FF); + // TODO: Reenable tag_match + // bool tag_match = read_src_value.tag_match && read_slot.tag_match; main_trace.push_back(Row{ .avm_main_clk = clk, .avm_main_ia = read_src_value.val, .avm_main_ib = read_slot.val, - .avm_main_ind_a = src_offset_is_indirect ? src_offset : 0, - .avm_main_ind_op_a = FF(static_cast(src_offset_is_indirect)), + .avm_main_ind_a = FF(read_src_value.indirect_address), + .avm_main_ind_b = FF(read_slot.indirect_address), + .avm_main_ind_op_a = FF(static_cast(read_src_value.is_indirect)), + .avm_main_ind_op_b = FF(static_cast(read_slot.is_indirect)), .avm_main_internal_return_ptr = FF(internal_return_ptr), - .avm_main_mem_idx_a = FF(direct_src_offset), - .avm_main_mem_idx_b = FF(slot_offset), + .avm_main_mem_idx_a = FF(read_src_value.direct_address), + .avm_main_mem_idx_b = FF(read_slot.direct_address), .avm_main_mem_op_a = FF(1), .avm_main_mem_op_b = FF(1), .avm_main_pc = pc, // No PC increment here since this is the same opcode as the rows created below .avm_main_r_in_tag = FF(static_cast(AvmMemoryTag::FF)), + // TODO: Reenable when tag_match is wokring sstore + // .avm_main_tag_err = FF(static_cast(tag_match)), .avm_main_w_in_tag = FF(static_cast(AvmMemoryTag::FF)), }); clk++; for (uint32_t i = 0; i < size; i++) { auto read_a = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IA, direct_src_offset + i, AvmMemoryTag::FF, AvmMemoryTag::U0); + call_ptr, clk, IntermRegister::IA, read_src_value.direct_address + i, AvmMemoryTag::FF, AvmMemoryTag::U0); Row row = Row{ .avm_main_clk = clk, .avm_main_ia = read_a.val, .avm_main_ib = read_slot.val + i, // slot increments each time .avm_main_internal_return_ptr = internal_return_ptr, - .avm_main_mem_idx_a = direct_src_offset + i, + .avm_main_mem_idx_a = read_src_value.direct_address + i, .avm_main_mem_op_a = 1, .avm_main_pc = pc, .avm_main_q_kernel_output_lookup = 1, .avm_main_r_in_tag = static_cast(AvmMemoryTag::FF), + .avm_main_tag_err = FF(static_cast(!read_a.tag_match)), }; row.avm_main_sel_op_sstore = FF(1); kernel_trace_builder.op_sstore(clk, side_effect_counter, row.avm_main_ib, row.avm_main_ia); @@ -1886,15 +1772,12 @@ void AvmTraceBuilder::op_div( { auto clk = static_cast(main_trace.size()) + 1; - auto const res = resolve_ind_three(call_ptr, clk, indirect, a_offset, b_offset, dst_offset); - bool tag_match = res.tag_match; + auto [resolved_a, resolved_b, resolved_dst] = resolve_indirects<3>(indirect, { a_offset, b_offset, dst_offset }); // Reading from memory and loading into ia resp. ib. - auto read_a = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IA, res.direct_a_offset, in_tag, in_tag); - auto read_b = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IB, res.direct_b_offset, in_tag, in_tag); - tag_match = read_a.tag_match && read_b.tag_match; + auto read_a = constrained_read_from_memory(call_ptr, clk, resolved_a, in_tag, in_tag); + auto read_b = constrained_read_from_memory(call_ptr, clk, resolved_b, in_tag, in_tag); + bool tag_match = read_a.tag_match && read_b.tag_match; // a / b = c FF a = read_a.val; @@ -1919,7 +1802,7 @@ void AvmTraceBuilder::op_div( } // Write into memory value c from intermediate register ic. - mem_trace_builder.write_into_memory(call_ptr, clk, IntermRegister::IC, res.direct_c_offset, c, in_tag, in_tag); + auto write_dst = constrained_write_to_memory(call_ptr, clk, resolved_dst, c, in_tag, in_tag); // Constrain gas cost gas_trace_builder.constrain_gas_lookup(clk, OpCode::DIV); @@ -1928,20 +1811,20 @@ void AvmTraceBuilder::op_div( .avm_main_clk = clk, .avm_main_alu_in_tag = FF(static_cast(in_tag)), .avm_main_call_ptr = call_ptr, - .avm_main_ia = a, - .avm_main_ib = b, + .avm_main_ia = read_a.val, + .avm_main_ib = read_b.val, .avm_main_ic = c, - .avm_main_ind_a = res.indirect_flag_a ? FF(a_offset) : FF(0), - .avm_main_ind_b = res.indirect_flag_b ? FF(b_offset) : FF(0), - .avm_main_ind_c = res.indirect_flag_c ? FF(dst_offset) : FF(0), - .avm_main_ind_op_a = FF(static_cast(res.indirect_flag_a)), - .avm_main_ind_op_b = FF(static_cast(res.indirect_flag_b)), - .avm_main_ind_op_c = FF(static_cast(res.indirect_flag_c)), + .avm_main_ind_a = FF(read_a.indirect_address), + .avm_main_ind_b = FF(read_b.indirect_address), + .avm_main_ind_c = FF(write_dst.indirect_address), + .avm_main_ind_op_a = FF(static_cast(read_a.is_indirect)), + .avm_main_ind_op_b = FF(static_cast(read_b.is_indirect)), + .avm_main_ind_op_c = FF(static_cast(write_dst.is_indirect)), .avm_main_internal_return_ptr = FF(internal_return_ptr), .avm_main_inv = tag_match ? inv : FF(1), - .avm_main_mem_idx_a = FF(res.direct_a_offset), - .avm_main_mem_idx_b = FF(res.direct_b_offset), - .avm_main_mem_idx_c = FF(res.direct_c_offset), + .avm_main_mem_idx_a = FF(read_a.direct_address), + .avm_main_mem_idx_b = FF(read_b.direct_address), + .avm_main_mem_idx_c = FF(write_dst.direct_address), .avm_main_mem_op_a = FF(1), .avm_main_mem_op_b = FF(1), .avm_main_mem_op_c = FF(1), @@ -2241,18 +2124,8 @@ void AvmTraceBuilder::execute_gasleft(OpCode opcode, uint8_t indirect, uint32_t assert(opcode == OpCode::L2GASLEFT || opcode == OpCode::DAGASLEFT); auto clk = static_cast(main_trace.size()) + 1; - bool tag_match = true; - - uint32_t direct_dst_offset = dst_offset; - bool indirect_dst_flag = is_operand_indirect(indirect, 0); - - if (indirect_dst_flag) { - auto read_ind_dst = - mem_trace_builder.indirect_read_and_load_from_memory(call_ptr, clk, IndirectRegister::IND_A, dst_offset); - direct_dst_offset = uint32_t(read_ind_dst.val); - tag_match = tag_match && read_ind_dst.tag_match; - } + auto [resolved_dst] = resolve_indirects<1>(indirect, { dst_offset }); // Constrain gas cost gas_trace_builder.constrain_gas_lookup(clk, opcode); @@ -2266,29 +2139,25 @@ void AvmTraceBuilder::execute_gasleft(OpCode opcode, uint8_t indirect, uint32_t } // Write into memory from intermediate register ia. - mem_trace_builder.write_into_memory(call_ptr, - clk, - IntermRegister::IA, - direct_dst_offset, - gas_remaining, - AvmMemoryTag::U0, - AvmMemoryTag::FF); // TODO: probably will be U32 in final version + // TODO: probably will be U32 in final version + auto write_dst = constrained_write_to_memory( + call_ptr, clk, resolved_dst, gas_remaining, AvmMemoryTag::U0, AvmMemoryTag::FF); main_trace.push_back(Row{ .avm_main_clk = clk, .avm_main_call_ptr = call_ptr, .avm_main_ia = gas_remaining, - .avm_main_ind_a = indirect_dst_flag ? FF(dst_offset) : FF(0), - .avm_main_ind_op_a = FF(static_cast(indirect_dst_flag)), + .avm_main_ind_a = FF(write_dst.indirect_address), + .avm_main_ind_op_a = FF(static_cast(is_operand_indirect(indirect, 0))), .avm_main_internal_return_ptr = FF(internal_return_ptr), - .avm_main_mem_idx_a = FF(direct_dst_offset), + .avm_main_mem_idx_a = FF(write_dst.direct_address), .avm_main_mem_op_a = FF(1), .avm_main_pc = FF(pc++), .avm_main_r_in_tag = FF(static_cast(AvmMemoryTag::U0)), .avm_main_rwa = FF(1), .avm_main_sel_op_dagasleft = (opcode == OpCode::DAGASLEFT) ? FF(1) : FF(0), .avm_main_sel_op_l2gasleft = (opcode == OpCode::L2GASLEFT) ? FF(1) : FF(0), - .avm_main_tag_err = FF(static_cast(!tag_match)), + .avm_main_tag_err = FF(static_cast(!write_dst.tag_match)), .avm_main_w_in_tag = FF(static_cast(AvmMemoryTag::FF)), // TODO: probably will be U32 in final version // Should the circuit (pil) constrain U32? }); @@ -2483,12 +2352,14 @@ void AvmTraceBuilder::internal_return() // TODO(ilyas: #6383): Temporary way to bulk write slices void AvmTraceBuilder::write_slice_to_memory(uint8_t space_id, uint32_t clk, - uint32_t dst_offset, + ResolvedAddr addr, AvmMemoryTag r_tag, AvmMemoryTag w_tag, FF internal_return_ptr, std::vector const& slice) { + bool is_indirect = addr.first == AddressingMode::INDIRECT; + auto dst_offset = addr.second; // We have 4 registers that we are able to use to write to memory within a single main trace row auto register_order = std::array{ IntermRegister::IA, IntermRegister::IB, IntermRegister::IC, IntermRegister::ID }; // If the slice size isnt a multiple of 4, we still need an extra row to write the remainder @@ -2509,27 +2380,46 @@ void AvmTraceBuilder::write_slice_to_memory(uint8_t space_id, if (offset >= slice.size()) { break; } - mem_trace_builder.write_into_memory( - space_id, clk + i, register_order[j], dst_offset + offset, slice.at(offset), r_tag, w_tag); + MemOp mem_write; + if (is_indirect) { + mem_write = constrained_write_to_memory( + space_id, clk + i, addr, slice.at(offset), r_tag, w_tag); + // Ensure futures calls are direct + is_indirect = false; + dst_offset = mem_write.direct_address; + } else { + mem_trace_builder.write_into_memory( + space_id, clk + i, register_order[j], dst_offset + offset, slice.at(offset), r_tag, w_tag); + mem_write = MemOp{ + .is_indirect = false, + .indirect_address = 0, + .direct_address = dst_offset + offset, + .tag = w_tag, + .tag_match = true, + .val = slice.at(offset), + }; + } // This looks a bit gross, but it is fine for now. if (j == 0) { main_row.avm_main_ia = slice.at(offset); - main_row.avm_main_mem_idx_a = FF(dst_offset + offset); + main_row.avm_main_ind_a = FF(mem_write.indirect_address); + main_row.avm_main_ind_op_a = FF(static_cast(mem_write.is_indirect)); + main_row.avm_main_mem_idx_a = FF(mem_write.direct_address); main_row.avm_main_mem_op_a = FF(1); main_row.avm_main_rwa = FF(1); } else if (j == 1) { main_row.avm_main_ib = slice.at(offset); - main_row.avm_main_mem_idx_b = FF(dst_offset + offset); + main_row.avm_main_mem_idx_b = FF(mem_write.direct_address); main_row.avm_main_mem_op_b = FF(1); main_row.avm_main_rwb = FF(1); } else if (j == 2) { main_row.avm_main_ic = slice.at(offset); - main_row.avm_main_mem_idx_c = FF(dst_offset + offset); + main_row.avm_main_mem_idx_c = FF(mem_write.direct_address); main_row.avm_main_mem_op_c = FF(1); main_row.avm_main_rwc = FF(1); } else { main_row.avm_main_id = slice.at(offset); - main_row.avm_main_mem_idx_d = FF(dst_offset + offset); + main_row.avm_main_mem_idx_d = FF(mem_write.direct_address); main_row.avm_main_mem_op_d = FF(1); main_row.avm_main_rwd = FF(1); } @@ -2551,13 +2441,16 @@ template std::array vec_to_arr(std::vector template uint32_t AvmTraceBuilder::read_slice_to_memory(uint8_t space_id, uint32_t clk, - uint32_t src_offset, + ResolvedAddr addr, AvmMemoryTag r_tag, AvmMemoryTag w_tag, FF internal_return_ptr, size_t slice_len, std::vector& slice) { + // If the mem_op is indirect, it goes into register A + bool is_indirect = addr.first == AddressingMode::INDIRECT; + auto src_offset = addr.second; // We have 4 registers that we are able to use to read from memory within a single main trace row auto register_order = std::array{ IntermRegister::IA, IntermRegister::IB, IntermRegister::IC, IntermRegister::ID }; // If the slice size isnt a multiple of 4, we still need an extra row to write the remainder @@ -2577,28 +2470,47 @@ uint32_t AvmTraceBuilder::read_slice_to_memory(uint8_t space_id, if (offset >= slice_len) { break; } - auto mem_read = mem_trace_builder.read_and_load_from_memory( - space_id, clk + i, register_order[j], src_offset + offset, r_tag, w_tag); + MemOp mem_read; + if (is_indirect) { + // If the first address is indirect we read it into register A, this can only happen once per slice read + mem_read = constrained_read_from_memory(space_id, clk + i, addr, r_tag, w_tag); + // Set this to false for the rest of the reads + is_indirect = false; + src_offset = mem_read.direct_address; + } else { + auto mem_load = mem_trace_builder.read_and_load_from_memory( + space_id, clk + i, register_order[j], src_offset + offset, r_tag, w_tag); + mem_read = MemOp{ + .is_indirect = false, + .indirect_address = 0, + .direct_address = src_offset + offset, + .tag = r_tag, + .tag_match = mem_load.tag_match, + .val = MEM(mem_load.val), + }; + } slice.emplace_back(MEM(mem_read.val)); // This looks a bit gross, but it is fine for now. if (j == 0) { main_row.avm_main_ia = slice.at(offset); - main_row.avm_main_mem_idx_a = FF(src_offset + offset); + main_row.avm_main_ind_a = FF(mem_read.indirect_address); + main_row.avm_main_ind_op_a = FF(static_cast(mem_read.is_indirect)); + main_row.avm_main_mem_idx_a = FF(mem_read.direct_address); main_row.avm_main_mem_op_a = FF(1); main_row.avm_main_tag_err = FF(static_cast(!mem_read.tag_match)); } else if (j == 1) { main_row.avm_main_ib = slice.at(offset); - main_row.avm_main_mem_idx_b = FF(src_offset + offset); + main_row.avm_main_mem_idx_b = FF(mem_read.direct_address); main_row.avm_main_mem_op_b = FF(1); main_row.avm_main_tag_err = FF(static_cast(!mem_read.tag_match)); } else if (j == 2) { main_row.avm_main_ic = slice.at(offset); - main_row.avm_main_mem_idx_c = FF(src_offset + offset); + main_row.avm_main_mem_idx_c = FF(mem_read.direct_address); main_row.avm_main_mem_op_c = FF(1); main_row.avm_main_tag_err = FF(static_cast(!mem_read.tag_match)); } else { main_row.avm_main_id = slice.at(offset); - main_row.avm_main_mem_idx_d = FF(src_offset + offset); + main_row.avm_main_mem_idx_d = FF(mem_read.direct_address); main_row.avm_main_mem_op_d = FF(1); main_row.avm_main_tag_err = FF(static_cast(!mem_read.tag_match)); } @@ -2619,8 +2531,8 @@ uint32_t AvmTraceBuilder::read_slice_to_memory(uint8_t space_id, * @param addr_offset An index in memory pointing to the target contract address * @param args_offset An index in memory pointing to the first value of the input array for the external call * @param args_size The number of values in the input array for the external call - * @param ret_offset An index in memory pointing to where the first value of the external calls return value should be - * stored. + * @param ret_offset An index in memory pointing to where the first value of the external calls return value should + * be stored. * @param ret_size The number of values in the return array * @param success_offset An index in memory pointing to where the success flag (U8) of the external call should be * stored @@ -2636,76 +2548,73 @@ void AvmTraceBuilder::op_call([[maybe_unused]] uint8_t indirect, [[maybe_unused]] uint32_t success_offset, [[maybe_unused]] uint32_t function_selector_offset) { - // pc++; auto clk = static_cast(main_trace.size()) + 1; const ExternalCallHint& hint = execution_hints.externalcall_hints.at(external_call_counter); - // We can load up to 4 things per row - auto register_order = std::array{ IntermRegister::IA, IntermRegister::IB, IntermRegister::IC, IntermRegister::ID }; - // Constrain gas cost + gas_trace_builder.constrain_gas_for_external_call( clk, static_cast(hint.l2_gas_used), static_cast(hint.da_gas_used)); - // Indirect is ZEROTH, SECOND and FOURTH bit COME BACK TO MAKING THIS ALL SUPPORTED - auto read_ind_gas_offset = - mem_trace_builder.indirect_read_and_load_from_memory(call_ptr, clk, IndirectRegister::IND_A, gas_offset); - auto read_ind_args_offset = - mem_trace_builder.indirect_read_and_load_from_memory(call_ptr, clk, IndirectRegister::IND_C, args_offset); - - std::vector first_row_load = { - uint32_t(read_ind_gas_offset.val), - addr_offset, - uint32_t(read_ind_args_offset.val), - }; - std::vector first_row_values = {}; - for (uint32_t j = 0; j < first_row_load.size(); j++) { - // We just read and load to set up the constraints, we dont actually use these values for now. - // info("Register order ", register_order[j]); - auto mem_read = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, register_order[j], first_row_load[j], AvmMemoryTag::FF, AvmMemoryTag::U0); - first_row_values.emplace_back(mem_read.val); - } + // We can load up to 4 things per row + auto [resolved_gas_offset, + resolved_addr_offset, + resolved_args_offset, + resolved_args_size, + resolved_ret_offset, + resolved_success_offset] = + resolve_indirects<6>(indirect, { gas_offset, addr_offset, args_offset, args_size, ret_offset, success_offset }); + + // Should read the address next to read_gas as well (tuple of gas values) + auto read_gas = constrained_read_from_memory( + call_ptr, clk, resolved_gas_offset, AvmMemoryTag::FF, AvmMemoryTag::U0); + auto read_addr = constrained_read_from_memory( + call_ptr, clk, resolved_addr_offset, AvmMemoryTag::FF, AvmMemoryTag::U0); + auto read_args = constrained_read_from_memory( + call_ptr, clk, resolved_args_offset, AvmMemoryTag::FF, AvmMemoryTag::U0); + // auto read_args_size = constrained_read_from_memory( + // call_ptr, clk, resolved_args_size, AvmMemoryTag::U32, AvmMemoryTag::U0); + bool tag_match = read_gas.tag_match && read_addr.tag_match && read_args.tag_match; // We read the input and output addresses in one row as they should contain FF elements main_trace.push_back(Row{ .avm_main_clk = clk, - .avm_main_ia = first_row_values[0], /* gas_offset */ - .avm_main_ib = first_row_values[1], /* addr_offset */ - .avm_main_ic = first_row_values[2], /* args_offset */ - .avm_main_ind_a = gas_offset, - .avm_main_ind_c = args_offset, - .avm_main_ind_op_a = FF(1), - .avm_main_ind_op_c = FF(1), + .avm_main_ia = read_gas.val, /* gas_offset */ + .avm_main_ic = read_addr.val, /* addr_offset */ + .avm_main_id = read_args.val, /* args_offset */ + .avm_main_ind_a = FF(read_gas.indirect_address), + .avm_main_ind_c = FF(read_addr.indirect_address), + .avm_main_ind_d = FF(read_args.indirect_address), + .avm_main_ind_op_a = FF(static_cast(read_gas.is_indirect)), + .avm_main_ind_op_c = FF(static_cast(read_addr.is_indirect)), + .avm_main_ind_op_d = FF(static_cast(read_args.is_indirect)), .avm_main_internal_return_ptr = FF(internal_return_ptr), - .avm_main_mem_idx_a = read_ind_gas_offset.val, - .avm_main_mem_idx_b = addr_offset, - .avm_main_mem_idx_c = read_ind_args_offset.val, + .avm_main_mem_idx_a = FF(read_gas.direct_address), + .avm_main_mem_idx_c = FF(read_addr.direct_address), + .avm_main_mem_idx_d = FF(read_args.direct_address), .avm_main_mem_op_a = FF(1), - .avm_main_mem_op_b = FF(1), .avm_main_mem_op_c = FF(1), + .avm_main_mem_op_d = FF(1), .avm_main_pc = FF(pc++), .avm_main_r_in_tag = FF(static_cast(AvmMemoryTag::FF)), .avm_main_sel_external_call = FF(1), + .avm_main_tag_err = FF(static_cast(!tag_match)), }); clk++; // Read the rest on a separate line, remember that the 4th operand is indirect - auto read_ind_ret_offset = - mem_trace_builder.indirect_read_and_load_from_memory(call_ptr, clk, IndirectRegister::IND_A, ret_offset); - // We just read and load to set up the constraints, we dont actually use these values for now. - auto mem_read_ret = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IA, uint32_t(read_ind_ret_offset.val), AvmMemoryTag::FF, AvmMemoryTag::U0); + auto read_ret = constrained_read_from_memory( + call_ptr, clk, resolved_ret_offset, AvmMemoryTag::FF, AvmMemoryTag::U0); main_trace.push_back(Row{ .avm_main_clk = clk, - .avm_main_ia = mem_read_ret.val, /* ret_offset */ - .avm_main_ind_a = ret_offset, - .avm_main_ind_op_a = FF(1), + .avm_main_ia = read_ret.val, /* ret_offset */ + .avm_main_ind_a = FF(read_ret.indirect_address), + .avm_main_ind_op_a = FF(static_cast(read_ret.is_indirect)), .avm_main_internal_return_ptr = FF(internal_return_ptr), - .avm_main_mem_idx_a = read_ind_ret_offset.val, + .avm_main_mem_idx_a = FF(read_ret.direct_address), .avm_main_mem_op_a = FF(1), .avm_main_pc = FF(pc), .avm_main_r_in_tag = FF(static_cast(AvmMemoryTag::FF)), }); clk++; - auto mem_read_success = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IA, success_offset, AvmMemoryTag::U32, AvmMemoryTag::U0); + auto mem_read_success = constrained_read_from_memory( + call_ptr, clk, resolved_success_offset, AvmMemoryTag::U32, AvmMemoryTag::U0); main_trace.push_back(Row{ .avm_main_clk = clk, .avm_main_ia = mem_read_success.val, /* success_offset */ @@ -2716,47 +2625,28 @@ void AvmTraceBuilder::op_call([[maybe_unused]] uint8_t indirect, .avm_main_r_in_tag = FF(static_cast(AvmMemoryTag::U32)), }); clk++; + write_slice_to_memory( + call_ptr, clk, resolved_ret_offset, AvmMemoryTag::U0, AvmMemoryTag::FF, internal_return_ptr, hint.return_data); + clk++; write_slice_to_memory(call_ptr, clk, - uint32_t(read_ind_ret_offset.val), + resolved_success_offset, AvmMemoryTag::U0, - AvmMemoryTag::FF, + AvmMemoryTag::U8, internal_return_ptr, - hint.return_data); - clk++; - write_slice_to_memory( - call_ptr, clk, success_offset, AvmMemoryTag::U0, AvmMemoryTag::U8, internal_return_ptr, { hint.success }); + { hint.success }); external_call_counter++; } void AvmTraceBuilder::op_get_contract_instance(uint8_t indirect, uint32_t address_offset, uint32_t dst_offset) { auto clk = static_cast(main_trace.size()) + 1; - bool tag_match = true; - uint32_t direct_address_offset = address_offset; - uint32_t direct_dst_offset = dst_offset; - - bool indirect_address_flag = is_operand_indirect(indirect, 0); - bool indirect_dst_flag = is_operand_indirect(indirect, 1); - - if (indirect_address_flag) { - auto read_ind_address = mem_trace_builder.indirect_read_and_load_from_memory( - call_ptr, clk, IndirectRegister::IND_A, address_offset); - direct_address_offset = uint32_t(read_ind_address.val); - tag_match = tag_match && read_ind_address.tag_match; - } - - if (indirect_dst_flag) { - auto read_ind_dst = - mem_trace_builder.indirect_read_and_load_from_memory(call_ptr, clk, IndirectRegister::IND_B, dst_offset); - direct_dst_offset = uint32_t(read_ind_dst.val); - tag_match = tag_match && read_ind_dst.tag_match; - } - auto read_address = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IA, direct_address_offset, AvmMemoryTag::FF, AvmMemoryTag::U0); - auto read_dst = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IB, direct_dst_offset, AvmMemoryTag::FF, AvmMemoryTag::U0); + auto [resolved_address_offset, resolved_dst_offset] = + resolve_indirects<2>(indirect, { address_offset, dst_offset }); + auto read_address = constrained_read_from_memory( + call_ptr, clk, resolved_address_offset, AvmMemoryTag::FF, AvmMemoryTag::U0); + bool tag_match = read_address.tag_match; // Constrain gas cost gas_trace_builder.constrain_gas_lookup(clk, OpCode::GETCONTRACTINSTANCE); @@ -2764,20 +2654,16 @@ void AvmTraceBuilder::op_get_contract_instance(uint8_t indirect, uint32_t addres main_trace.push_back(Row{ .avm_main_clk = clk, .avm_main_ia = read_address.val, - .avm_main_ib = read_dst.val, - .avm_main_ind_a = indirect_address_flag ? address_offset : 0, - .avm_main_ind_b = indirect_dst_flag ? dst_offset : 0, - .avm_main_ind_op_a = FF(static_cast(indirect_address_flag)), - .avm_main_ind_op_b = FF(static_cast(indirect_dst_flag)), + .avm_main_ind_a = FF(read_address.indirect_address), + .avm_main_ind_op_a = FF(static_cast(read_address.is_indirect)), .avm_main_internal_return_ptr = FF(internal_return_ptr), - .avm_main_mem_idx_a = FF(direct_address_offset), - .avm_main_mem_idx_b = FF(direct_dst_offset), + .avm_main_mem_idx_a = FF(read_address.direct_address), .avm_main_mem_op_a = FF(1), .avm_main_mem_op_activate_gas = FF(1), // TODO: remove in the long term - .avm_main_mem_op_b = FF(1), .avm_main_pc = FF(pc++), .avm_main_r_in_tag = FF(static_cast(AvmMemoryTag::FF)), .avm_main_sel_op_get_contract_instance = FF(1), + .avm_main_tag_err = FF(static_cast(!tag_match)), }); clk++; // Read the contract instance @@ -2792,7 +2678,7 @@ void AvmTraceBuilder::op_get_contract_instance(uint8_t indirect, uint32_t addres contract_instance.public_key_hash }; write_slice_to_memory(call_ptr, clk, - direct_dst_offset, + resolved_dst_offset, AvmMemoryTag::U0, AvmMemoryTag::FF, internal_return_ptr, @@ -2812,41 +2698,21 @@ void AvmTraceBuilder::op_to_radix_le( uint8_t indirect, uint32_t src_offset, uint32_t dst_offset, uint32_t radix, uint32_t num_limbs) { auto clk = static_cast(main_trace.size()) + 1; - bool tag_match = true; - uint32_t direct_src_offset = src_offset; - uint32_t direct_dst_offset = dst_offset; + auto [resolved_src_offset, resolved_dst_offset] = resolve_indirects<2>(indirect, { src_offset, dst_offset }); - bool indirect_src_flag = is_operand_indirect(indirect, 0); - bool indirect_dst_flag = is_operand_indirect(indirect, 1); + auto read_src = constrained_read_from_memory( + call_ptr, clk, resolved_src_offset, AvmMemoryTag::FF, AvmMemoryTag::U8); - if (indirect_src_flag) { - auto read_ind_src = - mem_trace_builder.indirect_read_and_load_from_memory(call_ptr, clk, IndirectRegister::IND_A, src_offset); - direct_src_offset = uint32_t(read_ind_src.val); - tag_match = tag_match && read_ind_src.tag_match; - } - - if (indirect_dst_flag) { - auto read_ind_dst = - mem_trace_builder.indirect_read_and_load_from_memory(call_ptr, clk, IndirectRegister::IND_B, dst_offset); - direct_dst_offset = uint32_t(read_ind_dst.val); - tag_match = tag_match && read_ind_dst.tag_match; - } - - auto read_src = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IA, direct_src_offset, AvmMemoryTag::FF, AvmMemoryTag::U8); - // Read in the memory address of where the first limb should be stored (the read_tag must be U32 and write tag - // U8) - auto read_dst = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IB, direct_dst_offset, AvmMemoryTag::FF, AvmMemoryTag::U8); + auto read_dst = constrained_read_from_memory( + call_ptr, clk, resolved_dst_offset, AvmMemoryTag::FF, AvmMemoryTag::U8); FF input = read_src.val; - FF dst_addr = read_dst.val; // In case of a memory tag error, we do not perform the computation. // Therefore, we do not create any entry in gadget table and return a vector of 0 - std::vector res = tag_match ? conversion_trace_builder.op_to_radix_le(input, radix, num_limbs, clk) - : std::vector(num_limbs, 0); + std::vector res = read_src.tag_match + ? conversion_trace_builder.op_to_radix_le(input, radix, num_limbs, clk) + : std::vector(num_limbs, 0); // Constrain gas cost gas_trace_builder.constrain_gas_lookup(clk, OpCode::TORADIXLE); @@ -2857,16 +2723,16 @@ void AvmTraceBuilder::op_to_radix_le( .avm_main_clk = clk, .avm_main_call_ptr = call_ptr, .avm_main_ia = input, - .avm_main_ib = dst_addr, + .avm_main_ib = read_dst.val, .avm_main_ic = radix, .avm_main_id = num_limbs, - .avm_main_ind_a = indirect_src_flag ? src_offset : 0, - .avm_main_ind_b = indirect_dst_flag ? dst_offset : 0, - .avm_main_ind_op_a = FF(static_cast(indirect_src_flag)), - .avm_main_ind_op_b = FF(static_cast(indirect_dst_flag)), + .avm_main_ind_a = read_src.indirect_address, + .avm_main_ind_b = read_dst.indirect_address, + .avm_main_ind_op_a = FF(static_cast(read_src.is_indirect)), + .avm_main_ind_op_b = FF(static_cast(read_dst.is_indirect)), .avm_main_internal_return_ptr = FF(internal_return_ptr), - .avm_main_mem_idx_a = FF(direct_src_offset), - .avm_main_mem_idx_b = FF(direct_dst_offset), + .avm_main_mem_idx_a = read_src.direct_address, + .avm_main_mem_idx_b = read_dst.direct_address, .avm_main_mem_op_a = FF(1), .avm_main_mem_op_b = FF(1), .avm_main_pc = FF(pc++), @@ -2885,7 +2751,7 @@ void AvmTraceBuilder::op_to_radix_le( ff_res.emplace_back(limb); } write_slice_to_memory( - call_ptr, clk, direct_dst_offset, AvmMemoryTag::FF, AvmMemoryTag::U8, FF(internal_return_ptr), ff_res); + call_ptr, clk, resolved_dst_offset, AvmMemoryTag::FF, AvmMemoryTag::U8, FF(internal_return_ptr), ff_res); } /** @@ -2896,7 +2762,8 @@ void AvmTraceBuilder::op_to_radix_le( * instance of sha256 compression. * @param input_offset An index in memory pointing to the first U32 value of the input array to be used in the next * instance of sha256 compression. - * @param output_offset An index in memory pointing to where the first U32 value of the output array should be stored. + * @param output_offset An index in memory pointing to where the first U32 value of the output array should be + * stored. */ void AvmTraceBuilder::op_sha256_compression(uint8_t indirect, uint32_t output_offset, @@ -2908,15 +2775,16 @@ void AvmTraceBuilder::op_sha256_compression(uint8_t indirect, // Resolve the indirect flags, the results of this function are used to determine the memory offsets // that point to the starting memory addresses for the input and output values. - // Note::This function will add memory reads at clk in the mem_trace_builder - auto const res = resolve_ind_three(call_ptr, clk, indirect, h_init_offset, input_offset, output_offset); + auto [resolved_h_init_offset, resolved_input_offset, resolved_output_offset] = + resolve_indirects<3>(indirect, { h_init_offset, input_offset, output_offset }); - auto read_a = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IA, res.direct_a_offset, AvmMemoryTag::U32, AvmMemoryTag::U32); - auto read_b = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IB, res.direct_b_offset, AvmMemoryTag::U32, AvmMemoryTag::U32); - auto read_c = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IC, res.direct_c_offset, AvmMemoryTag::U32, AvmMemoryTag::U32); + auto read_a = constrained_read_from_memory( + call_ptr, clk, resolved_h_init_offset, AvmMemoryTag::U32, AvmMemoryTag::U0); + auto read_b = constrained_read_from_memory( + call_ptr, clk, resolved_input_offset, AvmMemoryTag::U32, AvmMemoryTag::U0); + auto read_c = constrained_read_from_memory( + call_ptr, clk, resolved_output_offset, AvmMemoryTag::U32, AvmMemoryTag::U0); + bool tag_match = read_a.tag_match && read_b.tag_match && read_c.tag_match; // Constrain gas cost gas_trace_builder.constrain_gas_lookup(clk, OpCode::SHA256COMPRESSION); @@ -2931,26 +2799,26 @@ void AvmTraceBuilder::op_sha256_compression(uint8_t indirect, // did not lay down constraints), but this is a simplification main_trace.push_back(Row{ .avm_main_clk = clk, - .avm_main_ia = read_a.val, // First element of output (trivially 0) - .avm_main_ib = read_b.val, // First element of state - .avm_main_ic = read_c.val, // First element of input - .avm_main_ind_a = res.indirect_flag_a ? FF(h_init_offset) : FF(0), - .avm_main_ind_b = res.indirect_flag_b ? FF(input_offset) : FF(0), - .avm_main_ind_c = res.indirect_flag_a ? FF(output_offset) : FF(0), - .avm_main_ind_op_a = FF(static_cast(res.indirect_flag_a)), - .avm_main_ind_op_b = FF(static_cast(res.indirect_flag_b)), - .avm_main_ind_op_c = FF(static_cast(res.indirect_flag_c)), + .avm_main_ia = read_a.val, // First element of state + .avm_main_ib = read_b.val, // First element of input + .avm_main_ic = read_c.val, // First element of output (trivially 0) + .avm_main_ind_a = FF(read_a.indirect_address), + .avm_main_ind_b = FF(read_b.indirect_address), + .avm_main_ind_c = FF(read_c.indirect_address), + .avm_main_ind_op_a = FF(static_cast(read_a.is_indirect)), + .avm_main_ind_op_b = FF(static_cast(read_b.is_indirect)), + .avm_main_ind_op_c = FF(static_cast(read_c.is_indirect)), .avm_main_internal_return_ptr = FF(internal_return_ptr), - .avm_main_mem_idx_a = FF(res.direct_a_offset), - .avm_main_mem_idx_b = FF(res.direct_b_offset), - .avm_main_mem_idx_c = FF(res.direct_c_offset), + .avm_main_mem_idx_a = FF(read_a.direct_address), + .avm_main_mem_idx_b = FF(read_b.direct_address), + .avm_main_mem_idx_c = FF(read_c.direct_address), .avm_main_mem_op_a = FF(1), .avm_main_mem_op_b = FF(1), .avm_main_mem_op_c = FF(1), .avm_main_pc = FF(pc++), .avm_main_r_in_tag = FF(static_cast(AvmMemoryTag::U32)), .avm_main_sel_op_sha256 = FF(1), - .avm_main_w_in_tag = FF(static_cast(AvmMemoryTag::U32)), + .avm_main_tag_err = FF(static_cast(!tag_match)), }); // We store the current clk this main trace row occurred so that we can line up the sha256 gadget operation at // the same clk later. @@ -2964,7 +2832,7 @@ void AvmTraceBuilder::op_sha256_compression(uint8_t indirect, // Read results are written to h_init array. read_slice_to_memory(call_ptr, clk, - res.direct_a_offset, + resolved_h_init_offset, AvmMemoryTag::U32, AvmMemoryTag::U32, FF(internal_return_ptr), @@ -2976,7 +2844,7 @@ void AvmTraceBuilder::op_sha256_compression(uint8_t indirect, // Read results are written to input array read_slice_to_memory(call_ptr, clk, - res.direct_b_offset, + resolved_input_offset, AvmMemoryTag::U32, AvmMemoryTag::U32, FF(internal_return_ptr), @@ -2999,15 +2867,21 @@ void AvmTraceBuilder::op_sha256_compression(uint8_t indirect, } // Write the result to memory after - write_slice_to_memory( - call_ptr, clk, res.direct_c_offset, AvmMemoryTag::U32, AvmMemoryTag::U32, FF(internal_return_ptr), ff_result); + write_slice_to_memory(call_ptr, + clk, + resolved_output_offset, + AvmMemoryTag::U32, + AvmMemoryTag::U32, + FF(internal_return_ptr), + ff_result); } /** * @brief SHA256 Hash with direct or indirect memory access. * This function is temporary until we have transitioned to sha256Compression * @param indirect byte encoding information about indirect/direct memory access. - * @param output_offset An index in memory pointing to where the first U32 value of the output array should be stored. + * @param output_offset An index in memory pointing to where the first U32 value of the output array should be + * stored. * @param input_offset An index in memory pointing to the first U8 value of the state array to be used in the next * instance of sha256. * @param input_size_offset An index in memory pointing to the U32 value of the input size. @@ -3018,144 +2892,61 @@ void AvmTraceBuilder::op_sha256(uint8_t indirect, uint32_t input_size_offset) { auto clk = static_cast(main_trace.size()) + 1; - bool tag_match = true; - uint32_t direct_src_offset = input_offset; - uint32_t direct_dst_offset = output_offset; - - bool indirect_src_flag = is_operand_indirect(indirect, 1); - bool indirect_dst_flag = is_operand_indirect(indirect, 0); - - if (indirect_src_flag) { - auto read_ind_src = - mem_trace_builder.indirect_read_and_load_from_memory(call_ptr, clk, IndirectRegister::IND_A, input_offset); - direct_src_offset = uint32_t(read_ind_src.val); - tag_match = tag_match && read_ind_src.tag_match; - } + auto [resolved_output_offset, resolved_input_offset, resolved_input_size_offset] = + resolve_indirects<3>(indirect, { output_offset, input_offset, input_size_offset }); - if (indirect_dst_flag) { - auto read_ind_dst = - mem_trace_builder.indirect_read_and_load_from_memory(call_ptr, clk, IndirectRegister::IND_C, output_offset); - direct_dst_offset = uint32_t(read_ind_dst.val); - tag_match = tag_match && read_ind_dst.tag_match; - } - // Note we load the input and output onto one line in the main trace and the length on the next line - // We do this so we can load two different AvmMemoryTags (u8 for the I/O and u32 for the length) - auto input_read = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IA, direct_src_offset, AvmMemoryTag::U8, AvmMemoryTag::U8); - auto output_read = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IC, direct_dst_offset, AvmMemoryTag::U8, AvmMemoryTag::U8); - - // Constrain gas cost gas_trace_builder.constrain_gas_lookup(clk, OpCode::SHA256); - // Store the clock time that we will use to line up the gadget later - auto sha256_op_clk = clk; - main_trace.push_back(Row{ - .avm_main_clk = clk, - .avm_main_ia = input_read.val, // First element of input - .avm_main_ic = output_read.val, // First element of output - .avm_main_ind_a = indirect_src_flag ? FF(input_offset) : FF(0), - .avm_main_ind_c = indirect_dst_flag ? FF(output_offset) : FF(0), - .avm_main_ind_op_a = FF(static_cast(indirect_src_flag)), - .avm_main_ind_op_c = FF(static_cast(indirect_dst_flag)), - .avm_main_internal_return_ptr = FF(internal_return_ptr), - .avm_main_mem_idx_a = FF(direct_src_offset), // input - .avm_main_mem_idx_c = FF(direct_dst_offset), // output - .avm_main_mem_op_a = FF(1), - .avm_main_mem_op_c = FF(1), - .avm_main_pc = FF(pc++), - .avm_main_r_in_tag = FF(static_cast(AvmMemoryTag::U8)), - .avm_main_sel_op_sha256 = FF(1), - .avm_main_w_in_tag = FF(static_cast(AvmMemoryTag::U8)), - }); - clk++; - auto input_length_read = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IB, input_size_offset, AvmMemoryTag::U32, AvmMemoryTag::U32); + auto input_length_read = constrained_read_from_memory( + call_ptr, clk, resolved_input_size_offset, AvmMemoryTag::U32, AvmMemoryTag::U0); main_trace.push_back(Row{ .avm_main_clk = clk, .avm_main_ib = input_length_read.val, // Message Length + .avm_main_ind_b = FF(input_length_read.indirect_address), + .avm_main_ind_op_b = FF(static_cast(input_length_read.is_indirect)), .avm_main_internal_return_ptr = FF(internal_return_ptr), - .avm_main_mem_idx_b = FF(input_size_offset), // length + .avm_main_mem_idx_b = FF(input_length_read.direct_address), .avm_main_mem_op_b = FF(1), - .avm_main_pc = FF(pc), + .avm_main_pc = FF(pc++), .avm_main_r_in_tag = FF(static_cast(AvmMemoryTag::U32)), - .avm_main_w_in_tag = FF(static_cast(AvmMemoryTag::U32)), + .avm_main_sel_op_sha256 = FF(1), + .avm_main_tag_err = FF(static_cast(!input_length_read.tag_match)), }); clk++; + auto sha256_op_clk = clk; std::vector input; input.reserve(uint32_t(input_length_read.val)); - // We unroll this loop because the function typically expects arrays and for this temporary sha256 function we - // have a dynamic amount of input so we will use a vector. - auto register_order = std::array{ IntermRegister::IA, IntermRegister::IB, IntermRegister::IC, IntermRegister::ID }; - // If the slice size isnt a multiple of 4, we still need an extra row to write the remainder - uint32_t const num_main_rows = static_cast(input_length_read.val) / 4 + - static_cast(uint32_t(input_length_read.val) % 4 != 0); - for (uint32_t i = 0; i < num_main_rows; i++) { - Row main_row{ - .avm_main_clk = clk + i, - .avm_main_internal_return_ptr = FF(internal_return_ptr), - .avm_main_pc = FF(pc), - .avm_main_r_in_tag = FF(static_cast(AvmMemoryTag::U8)), - .avm_main_w_in_tag = FF(static_cast(AvmMemoryTag::U8)), - }; - // Write 4 values to memory in each_row - for (uint32_t j = 0; j < 4; j++) { - auto offset = i * 4 + j; - // If we exceed the slice size, we break - if (offset >= uint32_t(input_length_read.val)) { - break; - } - auto mem_read = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk + i, register_order[j], direct_src_offset + offset, AvmMemoryTag::U8, AvmMemoryTag::U8); - input.emplace_back(uint8_t(mem_read.val)); - // This looks a bit gross, but it is fine for now. - if (j == 0) { - main_row.avm_main_ia = input.at(offset); - main_row.avm_main_mem_idx_a = FF(direct_src_offset + offset); - main_row.avm_main_mem_op_a = FF(1); - main_row.avm_main_tag_err = FF(static_cast(!mem_read.tag_match)); - } else if (j == 1) { - main_row.avm_main_ib = input.at(offset); - main_row.avm_main_mem_idx_b = FF(direct_src_offset + offset); - main_row.avm_main_mem_op_b = FF(1); - main_row.avm_main_tag_err = FF(static_cast(!mem_read.tag_match)); - } else if (j == 2) { - main_row.avm_main_ic = input.at(offset); - main_row.avm_main_mem_idx_c = FF(direct_src_offset + offset); - main_row.avm_main_mem_op_c = FF(1); - main_row.avm_main_tag_err = FF(static_cast(!mem_read.tag_match)); - } else { - main_row.avm_main_id = input.at(offset); - main_row.avm_main_mem_idx_d = FF(direct_src_offset + offset); - main_row.avm_main_mem_op_d = FF(1); - main_row.avm_main_tag_err = FF(static_cast(!mem_read.tag_match)); - } - } - main_trace.emplace_back(main_row); - } - + uint32_t num_main_rows = read_slice_to_memory(call_ptr, + clk, + resolved_input_offset, + AvmMemoryTag::U8, + AvmMemoryTag::U0, + FF(internal_return_ptr), + uint32_t(input_length_read.val), + input); clk += num_main_rows; std::array result = sha256_trace_builder.sha256(input, sha256_op_clk); - // We convert the results to field elements here + std::vector ff_result; for (uint32_t i = 0; i < 32; i++) { ff_result.emplace_back(result[i]); } // Write the result to memory after write_slice_to_memory( - call_ptr, clk, direct_dst_offset, AvmMemoryTag::U8, AvmMemoryTag::U8, FF(internal_return_ptr), ff_result); + call_ptr, clk, resolved_output_offset, AvmMemoryTag::U0, AvmMemoryTag::U8, FF(internal_return_ptr), ff_result); } /** * @brief Poseidon2 Permutation with direct or indirect memory access. * * @param indirect byte encoding information about indirect/direct memory access. - * @param input_offset An index in memory pointing to the first Field value of the input array to be used in the next - * instance of poseidon2 permutation. - * @param output_offset An index in memory pointing to where the first Field value of the output array should be stored. + * @param input_offset An index in memory pointing to the first Field value of the input array to be used in the + * next instance of poseidon2 permutation. + * @param output_offset An index in memory pointing to where the first Field value of the output array should be + * stored. */ void AvmTraceBuilder::op_poseidon2_permutation(uint8_t indirect, uint32_t input_offset, uint32_t output_offset) { @@ -3164,32 +2955,14 @@ void AvmTraceBuilder::op_poseidon2_permutation(uint8_t indirect, uint32_t input_ // Resolve the indirect flags, the results of this function are used to determine the memory offsets // that point to the starting memory addresses for the input, output and h_init values // Note::This function will add memory reads at clk in the mem_trace_builder - bool tag_match = true; - uint32_t direct_src_offset = input_offset; - uint32_t direct_dst_offset = output_offset; + auto [resolved_input_offset, resolved_output_offset] = + resolve_indirects<2>(indirect, { input_offset, output_offset }); - bool indirect_src_flag = is_operand_indirect(indirect, 0); - bool indirect_dst_flag = is_operand_indirect(indirect, 1); - - if (indirect_src_flag) { - auto read_ind_src = - mem_trace_builder.indirect_read_and_load_from_memory(call_ptr, clk, IndirectRegister::IND_A, input_offset); - direct_src_offset = uint32_t(read_ind_src.val); - tag_match = tag_match && read_ind_src.tag_match; - } - - if (indirect_dst_flag) { - auto read_ind_dst = - mem_trace_builder.indirect_read_and_load_from_memory(call_ptr, clk, IndirectRegister::IND_B, output_offset); - direct_dst_offset = uint32_t(read_ind_dst.val); - tag_match = tag_match && read_ind_dst.tag_match; - } - - auto read_a = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IA, direct_src_offset, AvmMemoryTag::FF, AvmMemoryTag::FF); - // Read in the memory address of where the first limb should be stored - auto read_b = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IB, direct_dst_offset, AvmMemoryTag::FF, AvmMemoryTag::FF); + auto read_a = constrained_read_from_memory( + call_ptr, clk, resolved_input_offset, AvmMemoryTag::FF, AvmMemoryTag::U0); + auto read_b = constrained_read_from_memory( + call_ptr, clk, resolved_output_offset, AvmMemoryTag::FF, AvmMemoryTag::U0); + bool tag_match = read_a.tag_match && read_b.tag_match; // Constrain gas cost gas_trace_builder.constrain_gas_lookup(clk, OpCode::POSEIDON2); @@ -3198,19 +2971,19 @@ void AvmTraceBuilder::op_poseidon2_permutation(uint8_t indirect, uint32_t input_ .avm_main_clk = clk, .avm_main_ia = read_a.val, // First element of input .avm_main_ib = read_b.val, // First element of output (trivially zero) - .avm_main_ind_a = indirect_src_flag ? FF(input_offset) : FF(0), - .avm_main_ind_b = indirect_dst_flag ? FF(output_offset) : FF(0), - .avm_main_ind_op_a = FF(static_cast(indirect_src_flag)), - .avm_main_ind_op_b = FF(static_cast(indirect_dst_flag)), + .avm_main_ind_a = FF(read_a.indirect_address), + .avm_main_ind_b = FF(read_b.indirect_address), + .avm_main_ind_op_a = FF(static_cast(read_a.is_indirect)), + .avm_main_ind_op_b = FF(static_cast(read_b.is_indirect)), .avm_main_internal_return_ptr = FF(internal_return_ptr), - .avm_main_mem_idx_a = FF(direct_src_offset), - .avm_main_mem_idx_b = FF(direct_dst_offset), + .avm_main_mem_idx_a = FF(read_a.direct_address), + .avm_main_mem_idx_b = FF(read_b.direct_address), .avm_main_mem_op_a = FF(1), .avm_main_mem_op_b = FF(1), .avm_main_pc = FF(pc++), .avm_main_r_in_tag = FF(static_cast(AvmMemoryTag::FF)), .avm_main_sel_op_poseidon2 = FF(1), - .avm_main_w_in_tag = FF(static_cast(AvmMemoryTag::FF)), + .avm_main_tag_err = FF(static_cast(!tag_match)), }); // We store the current clk this main trace row occurred so that we can line up the poseidon2 gadget operation // at the same clk later. @@ -3220,8 +2993,14 @@ void AvmTraceBuilder::op_poseidon2_permutation(uint8_t indirect, uint32_t input_ clk++; // Read results are written to input array. std::vector input_vec; - read_slice_to_memory( - call_ptr, clk, direct_src_offset, AvmMemoryTag::FF, AvmMemoryTag::FF, FF(internal_return_ptr), 4, input_vec); + read_slice_to_memory(call_ptr, + clk, + resolved_input_offset, + AvmMemoryTag::FF, + AvmMemoryTag::U0, + FF(internal_return_ptr), + 4, + input_vec); // Increment the clock by 1 since (4 reads / 4 reads per row = 1) clk += 1; @@ -3233,19 +3012,21 @@ void AvmTraceBuilder::op_poseidon2_permutation(uint8_t indirect, uint32_t input_ } // // Write the result to memory after write_slice_to_memory( - call_ptr, clk, direct_dst_offset, AvmMemoryTag::FF, AvmMemoryTag::FF, FF(internal_return_ptr), ff_result); + call_ptr, clk, resolved_output_offset, AvmMemoryTag::U0, AvmMemoryTag::FF, FF(internal_return_ptr), ff_result); } /** * @brief Keccakf1600 with direct or indirect memory access. - * This function temporarily has the same interface as the kecccak opcode for compatibility, when the keccak migration - * is complete (to keccakf1600) We will update this function call as we will not likely need input_size_offset + * This function temporarily has the same interface as the kecccak opcode for compatibility, when the keccak + * migration is complete (to keccakf1600) We will update this function call as we will not likely need + * input_size_offset * @param indirect byte encoding information about indirect/direct memory access. - * @param output_offset An index in memory pointing to where the first u64 value of the output array should be stored. + * @param output_offset An index in memory pointing to where the first u64 value of the output array should be + * stored. * @param input_offset An index in memory pointing to the first u64 value of the input array to be used in the next * instance of poseidon2 permutation. - * @param input_size offset An index in memory pointing to the size of the input array. Temporary while we maintain the - * same interface as keccak (this is fixed to 25) + * @param input_size offset An index in memory pointing to the size of the input array. Temporary while we maintain + * the same interface as keccak (this is fixed to 25) */ void AvmTraceBuilder::op_keccakf1600(uint8_t indirect, uint32_t output_offset, @@ -3254,32 +3035,13 @@ void AvmTraceBuilder::op_keccakf1600(uint8_t indirect, { // What happens if the input_size_offset is > 25 when the state is more that that? auto clk = static_cast(main_trace.size()) + 1; - // bool tag_match = res.tag_match; - bool tag_match = true; - uint32_t direct_src_offset = input_offset; - uint32_t direct_dst_offset = output_offset; - - bool indirect_src_flag = is_operand_indirect(indirect, 1); - bool indirect_dst_flag = is_operand_indirect(indirect, 0); - - if (indirect_src_flag) { - auto read_ind_src = - mem_trace_builder.indirect_read_and_load_from_memory(call_ptr, clk, IndirectRegister::IND_A, input_offset); - direct_src_offset = uint32_t(read_ind_src.val); - tag_match = tag_match && read_ind_src.tag_match; - } - - if (indirect_dst_flag) { - auto read_ind_dst = - mem_trace_builder.indirect_read_and_load_from_memory(call_ptr, clk, IndirectRegister::IND_C, output_offset); - direct_dst_offset = uint32_t(read_ind_dst.val); - tag_match = tag_match && read_ind_dst.tag_match; - } - - auto input_read = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IA, direct_src_offset, AvmMemoryTag::U64, AvmMemoryTag::U64); - auto output_read = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IC, direct_dst_offset, AvmMemoryTag::U64, AvmMemoryTag::U64); + auto [resolved_output_offset, resolved_input_offset] = + resolve_indirects<2>(indirect, { output_offset, input_offset }); + auto input_read = constrained_read_from_memory( + call_ptr, clk, resolved_input_offset, AvmMemoryTag::U64, AvmMemoryTag::U0); + auto output_read = constrained_read_from_memory( + call_ptr, clk, resolved_output_offset, AvmMemoryTag::U64, AvmMemoryTag::U0); + bool tag_match = input_read.tag_match && output_read.tag_match; // Constrain gas cost gas_trace_builder.constrain_gas_lookup(clk, OpCode::KECCAKF1600); @@ -3288,19 +3050,19 @@ void AvmTraceBuilder::op_keccakf1600(uint8_t indirect, .avm_main_clk = clk, .avm_main_ia = input_read.val, // First element of input .avm_main_ic = output_read.val, // First element of output - .avm_main_ind_a = indirect_src_flag ? FF(input_offset) : FF(0), - .avm_main_ind_c = indirect_dst_flag ? FF(output_offset) : FF(0), - .avm_main_ind_op_a = FF(static_cast(indirect_src_flag)), - .avm_main_ind_op_c = FF(static_cast(indirect_dst_flag)), + .avm_main_ind_a = FF(input_read.indirect_address), + .avm_main_ind_c = FF(output_read.indirect_address), + .avm_main_ind_op_a = FF(static_cast(input_read.is_indirect)), + .avm_main_ind_op_c = FF(static_cast(output_read.is_indirect)), .avm_main_internal_return_ptr = FF(internal_return_ptr), - .avm_main_mem_idx_a = FF(direct_src_offset), // input - .avm_main_mem_idx_c = FF(direct_dst_offset), // output + .avm_main_mem_idx_a = FF(input_read.direct_address), + .avm_main_mem_idx_c = FF(output_read.direct_address), .avm_main_mem_op_a = FF(1), .avm_main_mem_op_c = FF(1), .avm_main_pc = FF(pc++), .avm_main_r_in_tag = FF(static_cast(AvmMemoryTag::U64)), .avm_main_sel_op_keccak = FF(1), - .avm_main_w_in_tag = FF(static_cast(AvmMemoryTag::U64)), + .avm_main_tag_err = FF(static_cast(!tag_match)), }); // We store the current clk this main trace row occurred so that we can line up the keccak gadget operation // at the same clk later. @@ -3308,7 +3070,7 @@ void AvmTraceBuilder::op_keccakf1600(uint8_t indirect, // We need to increment the clk clk++; auto input_length_read = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IB, input_size_offset, AvmMemoryTag::U32, AvmMemoryTag::U32); + call_ptr, clk, IntermRegister::IB, input_size_offset, AvmMemoryTag::U32, AvmMemoryTag::U0); main_trace.push_back(Row{ .avm_main_clk = clk, .avm_main_ib = input_length_read.val, // Message Length @@ -3317,18 +3079,24 @@ void AvmTraceBuilder::op_keccakf1600(uint8_t indirect, .avm_main_mem_op_b = FF(1), .avm_main_pc = FF(pc), .avm_main_r_in_tag = FF(static_cast(AvmMemoryTag::U32)), - .avm_main_w_in_tag = FF(static_cast(AvmMemoryTag::U32)), + .avm_main_tag_err = FF(static_cast(!input_length_read.tag_match)), }); clk++; // Array input is fixed to 1600 bits std::vector input_vec; // Read results are written to input array - read_slice_to_memory( - call_ptr, clk, direct_src_offset, AvmMemoryTag::U64, AvmMemoryTag::U64, FF(internal_return_ptr), 25, input_vec); + uint32_t num_main_rows = read_slice_to_memory(call_ptr, + clk, + resolved_input_offset, + AvmMemoryTag::U64, + AvmMemoryTag::U0, + FF(internal_return_ptr), + 25, + input_vec); std::array input = vec_to_arr(input_vec); // Increment the clock by 7 since (25 reads / 4 reads per row = 7) - clk += 7; + clk += num_main_rows; // Now that we have read all the values, we can perform the operation to get the resulting witness. // Note: We use the keccak_op_clk to ensure that the keccakf1600 operation is performed at the same clock cycle @@ -3342,14 +3110,15 @@ void AvmTraceBuilder::op_keccakf1600(uint8_t indirect, // Write the result to memory after write_slice_to_memory( - call_ptr, clk, direct_dst_offset, AvmMemoryTag::U64, AvmMemoryTag::U64, FF(internal_return_ptr), ff_result); + call_ptr, clk, resolved_output_offset, AvmMemoryTag::U0, AvmMemoryTag::U64, FF(internal_return_ptr), ff_result); } /** * @brief Keccak with direct or indirect memory access. * Keccak is TEMPORARY while we wait for the transition to keccakf1600, so we do the minimal to store the result * @param indirect byte encoding information about indirect/direct memory access. - * @param output_offset An index in memory pointing to where the first u8 value of the output array should be stored. + * @param output_offset An index in memory pointing to where the first u8 value of the output array should be + * stored. * @param input_offset An index in memory pointing to the first u8 value of the input array to be used in the next * instance of poseidon2 permutation. * @param input_size offset An index in memory pointing to the size of the input array. @@ -3360,76 +3129,42 @@ void AvmTraceBuilder::op_keccak(uint8_t indirect, uint32_t input_size_offset) { auto clk = static_cast(main_trace.size()) + 1; - bool tag_match = true; - uint32_t direct_src_offset = input_offset; - uint32_t direct_dst_offset = output_offset; - - bool indirect_src_flag = is_operand_indirect(indirect, 1); - bool indirect_dst_flag = is_operand_indirect(indirect, 0); - - if (indirect_src_flag) { - auto read_ind_src = - mem_trace_builder.indirect_read_and_load_from_memory(call_ptr, clk, IndirectRegister::IND_A, input_offset); - direct_src_offset = uint32_t(read_ind_src.val); - tag_match = tag_match && read_ind_src.tag_match; - } - - if (indirect_dst_flag) { - auto read_ind_dst = - mem_trace_builder.indirect_read_and_load_from_memory(call_ptr, clk, IndirectRegister::IND_C, output_offset); - direct_dst_offset = uint32_t(read_ind_dst.val); - tag_match = tag_match && read_ind_dst.tag_match; - } - // Note we load the input and output onto one line in the main trace and the length on the next line - // We do this so we can load two different AvmMemoryTags (u8 for the I/O and u32 for the length) - auto input_read = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IA, direct_src_offset, AvmMemoryTag::U8, AvmMemoryTag::U8); - auto output_read = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IC, direct_dst_offset, AvmMemoryTag::U8, AvmMemoryTag::U8); + auto [resolved_output_offset, resolved_input_offset, resolved_input_size_offset] = + resolve_indirects<3>(indirect, { output_offset, input_offset, input_size_offset }); // Constrain gas cost gas_trace_builder.constrain_gas_lookup(clk, OpCode::KECCAK); - // Store the clock time that we will use to line up the gadget later - auto keccak_op_clk = clk; - main_trace.push_back(Row{ - .avm_main_clk = clk, - .avm_main_ia = input_read.val, // First element of input - .avm_main_ic = output_read.val, // First element of output - .avm_main_ind_a = indirect_src_flag ? FF(input_offset) : FF(0), - .avm_main_ind_c = indirect_dst_flag ? FF(output_offset) : FF(0), - .avm_main_ind_op_a = FF(static_cast(indirect_src_flag)), - .avm_main_ind_op_c = FF(static_cast(indirect_dst_flag)), - .avm_main_internal_return_ptr = FF(internal_return_ptr), - .avm_main_mem_idx_a = FF(direct_src_offset), // input - .avm_main_mem_idx_c = FF(direct_dst_offset), // output - .avm_main_mem_op_a = FF(1), - .avm_main_mem_op_c = FF(1), - .avm_main_pc = FF(pc++), - .avm_main_r_in_tag = FF(static_cast(AvmMemoryTag::U8)), - .avm_main_sel_op_keccak = FF(1), - .avm_main_w_in_tag = FF(static_cast(AvmMemoryTag::U8)), - }); - clk++; - auto input_length_read = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IB, input_size_offset, AvmMemoryTag::U32, AvmMemoryTag::U32); + // Read the input length first + auto input_length_read = constrained_read_from_memory( + call_ptr, clk, resolved_input_size_offset, AvmMemoryTag::U32, AvmMemoryTag::U0); + [[maybe_unused]] auto keccak_op_clk = clk; main_trace.push_back(Row{ .avm_main_clk = clk, .avm_main_ib = input_length_read.val, // Message Length + .avm_main_ind_b = FF(input_length_read.indirect_address), + .avm_main_ind_op_b = FF(static_cast(input_length_read.is_indirect)), .avm_main_internal_return_ptr = FF(internal_return_ptr), - .avm_main_mem_idx_b = FF(input_size_offset), // length + .avm_main_mem_idx_b = FF(input_length_read.direct_address), // length .avm_main_mem_op_b = FF(1), - .avm_main_pc = FF(pc), + .avm_main_pc = FF(pc++), .avm_main_r_in_tag = FF(static_cast(AvmMemoryTag::U32)), - .avm_main_w_in_tag = FF(static_cast(AvmMemoryTag::U32)), + .avm_main_sel_op_keccak = FF(1), + .avm_main_tag_err = FF(static_cast(!input_length_read.tag_match)), }); clk++; std::vector input; input.reserve(uint32_t(input_length_read.val)); - - uint32_t num_main_rows = read_slice_to_memory( - call_ptr, clk, direct_src_offset, AvmMemoryTag::U8, AvmMemoryTag::U8, FF(internal_return_ptr), 4, input); + // Read the slice length from memory + uint32_t num_main_rows = read_slice_to_memory(call_ptr, + clk, + resolved_input_offset, + AvmMemoryTag::U8, + AvmMemoryTag::U8, + FF(internal_return_ptr), + uint32_t(input_length_read.val), + input); clk += num_main_rows; @@ -3441,7 +3176,7 @@ void AvmTraceBuilder::op_keccak(uint8_t indirect, } // Write the result to memory after write_slice_to_memory( - call_ptr, clk, direct_dst_offset, AvmMemoryTag::U8, AvmMemoryTag::U8, FF(internal_return_ptr), ff_result); + call_ptr, clk, resolved_output_offset, AvmMemoryTag::U8, AvmMemoryTag::U8, FF(internal_return_ptr), ff_result); } /** @@ -3458,19 +3193,10 @@ void AvmTraceBuilder::op_pedersen_hash(uint8_t indirect, uint32_t input_size_offset) { auto clk = static_cast(main_trace.size()) + 1; - bool tag_match = true; - uint32_t direct_src_offset = input_offset; - bool indirect_src_flag = is_operand_indirect(indirect, 2); - - if (indirect_src_flag) { - auto read_ind_src = - mem_trace_builder.indirect_read_and_load_from_memory(call_ptr, clk, IndirectRegister::IND_A, input_offset); - direct_src_offset = uint32_t(read_ind_src.val); - tag_match = tag_match && read_ind_src.tag_match; - } - - auto input_read = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IA, direct_src_offset, AvmMemoryTag::FF, AvmMemoryTag::FF); + auto [resolved_gen_ctx_offset, resolved_output_offset, resolved_input_offset, resolved_input_size_offset] = + resolve_indirects<4>(indirect, { gen_ctx_offset, output_offset, input_offset, input_size_offset }); + auto input_read = constrained_read_from_memory( + call_ptr, clk, resolved_input_offset, AvmMemoryTag::FF, AvmMemoryTag::U0); // Constrain gas cost gas_trace_builder.constrain_gas_lookup(clk, OpCode::PEDERSEN); @@ -3480,41 +3206,44 @@ void AvmTraceBuilder::op_pedersen_hash(uint8_t indirect, main_trace.push_back(Row{ .avm_main_clk = clk, .avm_main_ia = input_read.val, // First element of input - .avm_main_ind_a = indirect_src_flag ? FF(input_offset) : FF(0), - .avm_main_ind_op_a = FF(static_cast(indirect_src_flag)), + .avm_main_ind_a = FF(input_read.indirect_address), + .avm_main_ind_op_a = FF(static_cast(input_read.is_indirect)), .avm_main_internal_return_ptr = FF(internal_return_ptr), - .avm_main_mem_idx_a = FF(direct_src_offset), // input + .avm_main_mem_idx_a = FF(input_read.direct_address), .avm_main_mem_op_a = FF(1), .avm_main_pc = FF(pc++), .avm_main_r_in_tag = FF(static_cast(AvmMemoryTag::FF)), .avm_main_sel_op_pedersen = FF(1), - .avm_main_w_in_tag = FF(static_cast(AvmMemoryTag::FF)), + .avm_main_tag_err = FF(static_cast(!input_read.tag_match)), }); clk++; // We read the input size and gen_ctx addresses in one row as they should contain U32 elements - auto input_size_read = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IA, input_size_offset, AvmMemoryTag::U32, AvmMemoryTag::U32); - auto gen_ctx_read = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IB, gen_ctx_offset, AvmMemoryTag::U32, AvmMemoryTag::U32); + auto input_size_read = constrained_read_from_memory( + call_ptr, clk, resolved_input_size_offset, AvmMemoryTag::U32, AvmMemoryTag::U0); + auto gen_ctx_read = constrained_read_from_memory( + call_ptr, clk, resolved_gen_ctx_offset, AvmMemoryTag::U32, AvmMemoryTag::U0); main_trace.push_back(Row{ .avm_main_clk = clk, .avm_main_ia = input_size_read.val, .avm_main_ib = gen_ctx_read.val, + .avm_main_ind_a = FF(input_size_read.indirect_address), + .avm_main_ind_b = FF(gen_ctx_read.indirect_address), + .avm_main_ind_op_a = FF(static_cast(input_size_read.is_indirect)), + .avm_main_ind_op_b = FF(static_cast(gen_ctx_read.is_indirect)), .avm_main_internal_return_ptr = FF(internal_return_ptr), - .avm_main_mem_idx_a = FF(input_size_offset), - .avm_main_mem_idx_b = FF(gen_ctx_offset), + .avm_main_mem_idx_a = FF(input_size_read.direct_address), + .avm_main_mem_idx_b = FF(gen_ctx_read.direct_address), .avm_main_mem_op_a = FF(1), .avm_main_mem_op_b = FF(1), .avm_main_pc = FF(pc), .avm_main_r_in_tag = FF(static_cast(AvmMemoryTag::U32)), - .avm_main_w_in_tag = FF(static_cast(AvmMemoryTag::U32)), }); clk++; std::vector inputs; uint32_t num_main_rows = read_slice_to_memory(call_ptr, clk, - direct_src_offset, + resolved_input_offset, AvmMemoryTag::FF, AvmMemoryTag::FF, FF(internal_return_ptr), @@ -3523,7 +3252,7 @@ void AvmTraceBuilder::op_pedersen_hash(uint8_t indirect, clk += num_main_rows; FF output = pedersen_trace_builder.pedersen_hash(inputs, uint32_t(gen_ctx_read.val), pedersen_clk); write_slice_to_memory( - call_ptr, clk, output_offset, AvmMemoryTag::FF, AvmMemoryTag::FF, FF(internal_return_ptr), { output }); + call_ptr, clk, resolved_output_offset, AvmMemoryTag::FF, AvmMemoryTag::FF, FF(internal_return_ptr), { output }); } void AvmTraceBuilder::op_ec_add(uint8_t indirect, @@ -3536,16 +3265,31 @@ void AvmTraceBuilder::op_ec_add(uint8_t indirect, uint32_t output_offset) { auto clk = static_cast(main_trace.size()) + 1; + auto [resolved_lhs_x_offset, + resolved_lhs_y_offset, + resolved_lhs_is_inf_offset, + resolved_rhs_x_offset, + resolved_rhs_y_offset, + resolved_rhs_is_inf_offset, + resolved_output_offset] = resolve_indirects<7>(indirect, + { lhs_x_offset, + lhs_y_offset, + lhs_is_inf_offset, + rhs_x_offset, + rhs_y_offset, + rhs_is_inf_offset, + output_offset }); // Load lhs point - auto lhs_x_read = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IA, lhs_x_offset, AvmMemoryTag::FF, AvmMemoryTag::U0); - auto lhs_y_read = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IB, lhs_y_offset, AvmMemoryTag::FF, AvmMemoryTag::U0); + auto lhs_x_read = constrained_read_from_memory( + call_ptr, clk, resolved_lhs_x_offset, AvmMemoryTag::FF, AvmMemoryTag::U0); + auto lhs_y_read = constrained_read_from_memory( + call_ptr, clk, resolved_lhs_y_offset, AvmMemoryTag::FF, AvmMemoryTag::U0); // Load rhs point - auto rhs_x_read = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IC, rhs_x_offset, AvmMemoryTag::FF, AvmMemoryTag::U0); - auto rhs_y_read = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::ID, rhs_y_offset, AvmMemoryTag::FF, AvmMemoryTag::U0); + auto rhs_x_read = constrained_read_from_memory( + call_ptr, clk, resolved_rhs_x_offset, AvmMemoryTag::FF, AvmMemoryTag::U0); + auto rhs_y_read = constrained_read_from_memory( + call_ptr, clk, resolved_rhs_y_offset, AvmMemoryTag::FF, AvmMemoryTag::U0); + bool tag_match = lhs_x_read.tag_match && lhs_y_read.tag_match && rhs_x_read.tag_match && rhs_y_read.tag_match; // Save this clk time to line up with the gadget op. auto ecc_clk = clk; @@ -3555,24 +3299,34 @@ void AvmTraceBuilder::op_ec_add(uint8_t indirect, .avm_main_ib = lhs_y_read.val, .avm_main_ic = rhs_x_read.val, .avm_main_id = rhs_y_read.val, + .avm_main_ind_a = FF(lhs_x_read.indirect_address), + .avm_main_ind_b = FF(lhs_y_read.indirect_address), + .avm_main_ind_c = FF(rhs_x_read.indirect_address), + .avm_main_ind_d = FF(rhs_y_read.indirect_address), + .avm_main_ind_op_a = FF(static_cast(lhs_x_read.is_indirect)), + .avm_main_ind_op_b = FF(static_cast(lhs_y_read.is_indirect)), + .avm_main_ind_op_c = FF(static_cast(rhs_x_read.is_indirect)), + .avm_main_ind_op_d = FF(static_cast(rhs_y_read.is_indirect)), .avm_main_internal_return_ptr = FF(internal_return_ptr), - .avm_main_mem_idx_a = FF(lhs_x_offset), - .avm_main_mem_idx_b = FF(lhs_y_offset), - .avm_main_mem_idx_c = FF(rhs_x_offset), - .avm_main_mem_idx_d = FF(rhs_y_offset), + .avm_main_mem_idx_a = FF(lhs_x_read.direct_address), + .avm_main_mem_idx_b = FF(lhs_y_read.direct_address), + .avm_main_mem_idx_c = FF(rhs_x_read.direct_address), + .avm_main_mem_idx_d = FF(rhs_y_read.direct_address), .avm_main_mem_op_a = FF(1), .avm_main_mem_op_b = FF(1), .avm_main_mem_op_c = FF(1), .avm_main_mem_op_d = FF(1), .avm_main_pc = FF(pc++), .avm_main_r_in_tag = FF(static_cast(AvmMemoryTag::FF)), + .avm_main_tag_err = FF(static_cast(!tag_match)), }); clk++; // Load the infinite bools separately since they have a different memory tag - auto lhs_is_inf_read = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IA, lhs_is_inf_offset, AvmMemoryTag::U8, AvmMemoryTag::U0); - auto rhs_is_inf_read = mem_trace_builder.read_and_load_from_memory( - call_ptr, clk, IntermRegister::IB, rhs_is_inf_offset, AvmMemoryTag::U8, AvmMemoryTag::U0); + auto lhs_is_inf_read = constrained_read_from_memory( + call_ptr, clk, resolved_lhs_is_inf_offset, AvmMemoryTag::U8, AvmMemoryTag::U0); + auto rhs_is_inf_read = constrained_read_from_memory( + call_ptr, clk, resolved_rhs_is_inf_offset, AvmMemoryTag::U8, AvmMemoryTag::U0); + bool tag_match_inf = lhs_is_inf_read.tag_match && rhs_is_inf_read.tag_match; main_trace.push_back(Row{ .avm_main_clk = clk, @@ -3585,6 +3339,7 @@ void AvmTraceBuilder::op_ec_add(uint8_t indirect, .avm_main_mem_op_b = FF(1), .avm_main_pc = FF(pc), .avm_main_r_in_tag = FF(static_cast(AvmMemoryTag::U8)), + .avm_main_tag_err = FF(static_cast(!tag_match_inf)), }); clk++; grumpkin::g1::affine_element lhs = uint8_t(lhs_is_inf_read.val) == 1 @@ -3593,44 +3348,49 @@ void AvmTraceBuilder::op_ec_add(uint8_t indirect, grumpkin::g1::affine_element rhs = uint8_t(rhs_is_inf_read.val) == 1 ? grumpkin::g1::affine_element::infinity() : grumpkin::g1::affine_element{ rhs_x_read.val, rhs_y_read.val }; - auto result = ecc_trace_builder.embedded_curve_add(lhs, rhs, ecc_clk); - // Write across two lines since we have different mem_tags - uint32_t direct_output_offset = output_offset; - bool indirect_flag_output = is_operand_indirect(indirect, 6); - if (indirect_flag_output) { - auto read_ind_output = - mem_trace_builder.indirect_read_and_load_from_memory(call_ptr, clk, IndirectRegister::IND_A, output_offset); - direct_output_offset = uint32_t(read_ind_output.val); - } + [[maybe_unused]] auto result = ecc_trace_builder.embedded_curve_add(lhs, rhs, ecc_clk); + // Write point coordinates + auto write_x = constrained_write_to_memory( + call_ptr, clk, resolved_output_offset, result.x, AvmMemoryTag::U0, AvmMemoryTag::FF); mem_trace_builder.write_into_memory( - call_ptr, clk, IntermRegister::IA, direct_output_offset, result.x, AvmMemoryTag::U0, AvmMemoryTag::FF); - mem_trace_builder.write_into_memory( - call_ptr, clk, IntermRegister::IB, direct_output_offset + 1, result.y, AvmMemoryTag::U0, AvmMemoryTag::FF); + call_ptr, clk, IntermRegister::IB, write_x.direct_address + 1, result.y, AvmMemoryTag::U0, AvmMemoryTag::FF); main_trace.push_back(Row{ .avm_main_clk = clk, .avm_main_ia = result.x, .avm_main_ib = result.y, - .avm_main_ind_a = indirect_flag_output ? FF(output_offset) : FF(0), - .avm_main_ind_op_a = FF(static_cast(indirect_flag_output)), + .avm_main_ind_a = FF(write_x.indirect_address), + .avm_main_ind_op_a = FF(static_cast(write_x.is_indirect)), .avm_main_internal_return_ptr = FF(internal_return_ptr), - .avm_main_mem_idx_a = FF(direct_output_offset), - .avm_main_mem_idx_b = FF(direct_output_offset + 1), + .avm_main_mem_idx_a = FF(write_x.direct_address), + .avm_main_mem_idx_b = FF(write_x.direct_address + 1), .avm_main_mem_op_a = FF(1), .avm_main_mem_op_b = FF(1), .avm_main_pc = FF(pc), .avm_main_rwa = FF(1), .avm_main_rwb = FF(1), .avm_main_w_in_tag = FF(static_cast(AvmMemoryTag::FF)), + }); clk++; - write_slice_to_memory(call_ptr, - clk, - direct_output_offset + 2, - AvmMemoryTag::U8, - AvmMemoryTag::U8, - FF(internal_return_ptr), - { result.is_point_at_infinity() }); + mem_trace_builder.write_into_memory(call_ptr, + clk, + IntermRegister::IA, + write_x.direct_address + 2, + result.is_point_at_infinity(), + AvmMemoryTag::U0, + AvmMemoryTag::U8); + + main_trace.push_back(Row{ + .avm_main_clk = clk, + .avm_main_ia = result.is_point_at_infinity(), + .avm_main_internal_return_ptr = FF(internal_return_ptr), + .avm_main_mem_idx_a = FF(write_x.direct_address + 2), + .avm_main_mem_op_a = FF(1), + .avm_main_pc = FF(pc), + .avm_main_rwa = FF(1), + .avm_main_w_in_tag = FF(static_cast(AvmMemoryTag::U8)), + }); } // Finalise Lookup Counts // 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 aa2ff3c9900a..cde9eea97fff 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_trace.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_trace.hpp @@ -21,6 +21,11 @@ namespace bb::avm_trace { using Row = bb::AvmFullRow; +enum class AddressingMode { + DIRECT, + INDIRECT, +}; +using ResolvedAddr = std::pair; // This is the internal context that we keep along the lifecycle of bytecode execution // to iteratively build the whole trace. This is effectively performing witness generation. @@ -205,19 +210,16 @@ class AvmTraceBuilder { uint32_t rhs_is_inf_offset, uint32_t output_offset); - private: - // Used for the standard indirect address resolution of three operands opcode. - struct IndirectThreeResolution { - bool tag_match = false; - uint32_t direct_a_offset; - uint32_t direct_b_offset; - uint32_t direct_c_offset; - - bool indirect_flag_a = false; - bool indirect_flag_b = false; - bool indirect_flag_c = false; + struct MemOp { + bool is_indirect; + uint32_t indirect_address; + uint32_t direct_address; + AvmMemoryTag tag; + bool tag_match; + FF val; }; + private: std::vector main_trace; AvmMemTraceBuilder mem_trace_builder; AvmAluTraceBuilder alu_trace_builder; @@ -244,7 +246,7 @@ class AvmTraceBuilder { * @return Row */ Row create_kernel_lookup_opcode( - bool indirect, uint32_t dst_offset, uint32_t selector, FF value, AvmMemoryTag w_tag); + uint8_t indirect, uint32_t dst_offset, uint32_t selector, FF value, AvmMemoryTag w_tag); /** * @brief Create a kernel output opcode object @@ -316,9 +318,6 @@ class AvmTraceBuilder { void finalise_mem_trace_lookup_counts(); - IndirectThreeResolution resolve_ind_three( - uint8_t space_id, uint32_t clk, uint8_t indirect, uint32_t a_offset, uint32_t b_offset, uint32_t c_offset); - uint32_t pc = 0; uint32_t internal_return_ptr = 0; // After a nested call, it should be initialized with MAX_SIZE_INTERNAL_STACK * call_ptr @@ -334,11 +333,18 @@ class AvmTraceBuilder { // Mapping of side effect counter -> value ExecutionHints execution_hints; + template + MemOp constrained_read_from_memory( + uint8_t space_id, uint32_t clk, ResolvedAddr addr, AvmMemoryTag read_tag, AvmMemoryTag write_tag); + template + MemOp constrained_write_to_memory( + uint8_t space_id, uint32_t clk, ResolvedAddr addr, FF value, AvmMemoryTag read_tag, AvmMemoryTag write_tag); + // TODO(ilyas: #6383): Temporary way to bulk read slices template uint32_t read_slice_to_memory(uint8_t space_id, uint32_t clk, - uint32_t src_offset, + ResolvedAddr addr, AvmMemoryTag r_tag, AvmMemoryTag w_tag, FF internal_return_ptr, @@ -346,7 +352,7 @@ class AvmTraceBuilder { std::vector& slice); void write_slice_to_memory(uint8_t space_id, uint32_t clk, - uint32_t dst_offset, + ResolvedAddr addr, AvmMemoryTag r_tag, AvmMemoryTag w_tag, FF internal_return_ptr, diff --git a/barretenberg/cpp/src/barretenberg/vm/tests/avm_execution.test.cpp b/barretenberg/cpp/src/barretenberg/vm/tests/avm_execution.test.cpp index bfa5339d3621..41042046490a 100644 --- a/barretenberg/cpp/src/barretenberg/vm/tests/avm_execution.test.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/tests/avm_execution.test.cpp @@ -785,34 +785,12 @@ TEST_F(AvmExecutionTests, toRadixLeOpcode) auto bytecode = hex_to_bytes(bytecode_hex); auto instructions = Deserialization::parse(bytecode); - ASSERT_THAT(instructions, SizeIs(5)); - - // TORADIXLE - EXPECT_THAT(instructions.at(3), - AllOf(Field(&Instruction::op_code, OpCode::TORADIXLE), - Field(&Instruction::operands, - ElementsAre(VariantWith(3), - VariantWith(17), - VariantWith(21), - VariantWith(2), - VariantWith(256))))); - // Assign a vector that we will mutate internally in gen_trace to store the return values; std::vector returndata = std::vector(); auto trace = Execution::gen_trace(instructions, returndata, std::vector{ FF::modulus - FF(1) }, public_inputs_vec); // Find the first row enabling the TORADIXLE selector - auto row = std::ranges::find_if(trace.begin(), trace.end(), [](Row r) { return r.avm_main_sel_op_radix_le == 1; }); - EXPECT_EQ(row->avm_main_ind_a, 17); - EXPECT_EQ(row->avm_main_ind_b, 21); - EXPECT_EQ(row->avm_main_mem_idx_a, 1); // Indirect(17) -> 1 - EXPECT_EQ(row->avm_main_mem_idx_b, 5); // Indirect(21) -> 5 - EXPECT_EQ(row->avm_main_ia, FF(FF::modulus - FF(1))); // Indirect(17) -> Direct(1) -> FF::modulus - FF(1) - EXPECT_EQ(row->avm_main_ib, 0); // Indirect(21) -> 5 -> Unintialized memory - EXPECT_EQ(row->avm_main_ic, 2); - EXPECT_EQ(row->avm_main_id, 256); - // Expected output is bitwise decomposition of MODULUS - 1..could hardcode the result but it's a bit long std::vector expected_output; // Extract each bit. @@ -878,18 +856,6 @@ TEST_F(AvmExecutionTests, sha256CompressionOpcode) auto bytecode = hex_to_bytes(bytecode_hex); auto instructions = Deserialization::parse(bytecode); - // 8 SET for state + 16 SET for input + 3 SET for setting up indirects + 1 SHA256COMPRESSION + 1 RETURN - ASSERT_THAT(instructions, SizeIs(29)); - - // SHA256COMPRESSION - EXPECT_THAT(instructions.at(27), - AllOf(Field(&Instruction::op_code, OpCode::SHA256COMPRESSION), - Field(&Instruction::operands, - ElementsAre(VariantWith(7), - VariantWith(36), - VariantWith(34), - VariantWith(35))))); - // Assign a vector that we will mutate internally in gen_trace to store the return values; std::vector calldata = std::vector(); std::vector returndata = std::vector(); @@ -898,21 +864,8 @@ TEST_F(AvmExecutionTests, sha256CompressionOpcode) // 4091010797,3974542186]), std::vector expected_output = { 1862536192, 526086805, 2067405084, 593147560, 726610467, 813867028, 4091010797ULL, 3974542186ULL }; - auto trace = Execution::gen_trace(instructions, returndata, calldata, public_inputs_vec); - // Find the first row enabling the Sha256Compression selector - auto row = std::ranges::find_if(trace.begin(), trace.end(), [](Row r) { return r.avm_main_sel_op_sha256 == 1; }); - EXPECT_EQ(row->avm_main_ind_a, 34); - EXPECT_EQ(row->avm_main_ind_b, 35); - EXPECT_EQ(row->avm_main_ind_c, 36); - EXPECT_EQ(row->avm_main_mem_idx_a, 1); // Indirect(34) -> 9 - EXPECT_EQ(row->avm_main_mem_idx_b, 9); // Indirect(35) -> 9 - EXPECT_EQ(row->avm_main_mem_idx_c, 256); // Indirect(36) -> 256 - EXPECT_EQ(row->avm_main_ia, 1); // Trivially contains 0. (See avm_trace for explanation why) - EXPECT_EQ(row->avm_main_ib, 1); // Contains first element of the state - EXPECT_EQ(row->avm_main_ic, 0); // Contains first element of the input - EXPECT_EQ(returndata, expected_output); validate_trace(std::move(trace), public_inputs); @@ -976,36 +929,11 @@ TEST_F(AvmExecutionTests, sha256Opcode) auto bytecode = hex_to_bytes(bytecode_hex); auto instructions = Deserialization::parse(bytecode); - ASSERT_THAT(instructions, SizeIs(8)); - // - // SHA256 - EXPECT_THAT(instructions.at(6), - AllOf(Field(&Instruction::op_code, OpCode::SHA256), - Field(&Instruction::operands, - ElementsAre(VariantWith(3), - VariantWith(35), - VariantWith(36), - VariantWith(37))))); - // Assign a vector that we will mutate internally in gen_trace to store the return values; std::vector returndata = std::vector(); std::vector calldata = std::vector(); auto trace = Execution::gen_trace(instructions, returndata, calldata, public_inputs_vec); - // Find the first row enabling the sha256 selector - auto row = std::ranges::find_if(trace.begin(), trace.end(), [](Row r) { return r.avm_main_sel_op_sha256 == 1; }); - EXPECT_EQ(row->avm_main_ind_a, 36); // Register A is indirect - EXPECT_EQ(row->avm_main_ind_c, 35); // Register C is indirect - EXPECT_EQ(row->avm_main_mem_idx_a, 1); // Indirect(36) -> 1 - EXPECT_EQ(row->avm_main_mem_idx_c, 256); // Indirect(35) -> 256 - EXPECT_EQ(row->avm_main_ia, 97); - EXPECT_EQ(row->avm_main_ic, 0); - // Register b checks are done in the next row due to the difference in the memory tag - std::advance(row, 1); - EXPECT_EQ(row->avm_main_ind_b, 0); // Register B is not - EXPECT_EQ(row->avm_main_mem_idx_b, 37); // Load(37) -> input length - EXPECT_EQ(row->avm_main_ib, 3); // Input length - EXPECT_EQ(returndata, expected_output); validate_trace(std::move(trace), public_inputs); @@ -1047,16 +975,6 @@ TEST_F(AvmExecutionTests, poseidon2PermutationOpCode) auto bytecode = hex_to_bytes(bytecode_hex); auto instructions = Deserialization::parse(bytecode); - // 1 CALLDATACOPY for input + 2 SET for setting up indirects + 1 POSEIDON2 + 1 RETURN - ASSERT_THAT(instructions, SizeIs(5)); - - // POSEIDON2_PERM - EXPECT_THAT( - instructions.at(3), - AllOf(Field(&Instruction::op_code, OpCode::POSEIDON2), - Field(&Instruction::operands, - ElementsAre(VariantWith(3), VariantWith(36), VariantWith(35))))); - // Assign a vector that we will mutate internally in gen_trace to store the return values; std::vector returndata = std::vector(); std::vector expected_output = { @@ -1065,18 +983,8 @@ TEST_F(AvmExecutionTests, poseidon2PermutationOpCode) FF(std::string("0x018555a8eb50cf07f64b019ebaf3af3c925c93e631f3ecd455db07bbb52bbdd3")), FF(std::string("0x0cbea457c91c22c6c31fd89afd2541efc2edf31736b9f721e823b2165c90fd41")) }; - auto trace = Execution::gen_trace(instructions, returndata, calldata, public_inputs_vec); - // Find the first row enabling the poseidon2 selector - auto row = std::ranges::find_if(trace.begin(), trace.end(), [](Row r) { return r.avm_main_sel_op_poseidon2 == 1; }); - EXPECT_EQ(row->avm_main_ind_a, 36); - EXPECT_EQ(row->avm_main_ind_b, 35); - EXPECT_EQ(row->avm_main_mem_idx_a, 1); // Indirect(36) -> 1 - EXPECT_EQ(row->avm_main_mem_idx_b, 9); // Indirect(34) -> 9 - EXPECT_EQ(row->avm_main_ia, FF(std::string("9a807b615c4d3e2fa0b1c2d3e4f56789fedcba9876543210abcdef0123456789"))); - EXPECT_EQ(row->avm_main_ib, 0); // Contains first element of the output (trivially 0) - EXPECT_EQ(returndata, expected_output); validate_trace(std::move(trace), public_inputs); @@ -1146,36 +1054,11 @@ TEST_F(AvmExecutionTests, keccakf1600OpCode) auto bytecode = hex_to_bytes(bytecode_hex); auto instructions = Deserialization::parse(bytecode); - // 25 SET for input + 2 SET for setting up indirects + 1 KECCAK + 1 RETURN - ASSERT_THAT(instructions, SizeIs(30)); - // - // KECCAKF1600 - EXPECT_THAT(instructions.at(28), - AllOf(Field(&Instruction::op_code, OpCode::KECCAKF1600), - Field(&Instruction::operands, - ElementsAre(VariantWith(3), - VariantWith(35), - VariantWith(36), - VariantWith(37))))); - // // Assign a vector that we will mutate internally in gen_trace to store the return values; std::vector calldata = std::vector(); std::vector returndata = std::vector(); auto trace = Execution::gen_trace(instructions, returndata, calldata, public_inputs_vec); - // Find the first row enabling the keccak selector - auto row = std::ranges::find_if(trace.begin(), trace.end(), [](Row r) { return r.avm_main_sel_op_keccak == 1; }); - EXPECT_EQ(row->avm_main_ind_a, 36); // Register A is indirect - EXPECT_EQ(row->avm_main_ind_c, 35); // Register C is indirect - EXPECT_EQ(row->avm_main_mem_idx_a, 1); // Indirect(36) -> 1 - EXPECT_EQ(row->avm_main_mem_idx_c, 256); // Indirect(35) -> 256 - EXPECT_EQ(row->avm_main_ia, (0xF1258F7940E1DDE7LLU)); - EXPECT_EQ(row->avm_main_ic, 0); - - std::advance(row, 1); - EXPECT_EQ(row->avm_main_ind_b, 0); // Register B is not - EXPECT_EQ(row->avm_main_mem_idx_b, 37); // Load(37) -> input length - EXPECT_EQ(row->avm_main_ib, 25); // Input length EXPECT_EQ(returndata, expected_output); validate_trace(std::move(trace), public_inputs); @@ -1229,36 +1112,11 @@ TEST_F(AvmExecutionTests, keccakOpCode) auto bytecode = hex_to_bytes(bytecode_hex); auto instructions = Deserialization::parse(bytecode); - ASSERT_THAT(instructions, SizeIs(6)); - // - // KECCAK - EXPECT_THAT(instructions.at(4), - AllOf(Field(&Instruction::op_code, OpCode::KECCAK), - Field(&Instruction::operands, - ElementsAre(VariantWith(3), - VariantWith(35), - VariantWith(36), - VariantWith(37))))); - // Assign a vector that we will mutate internally in gen_trace to store the return values; std::vector calldata = std::vector(); std::vector returndata = std::vector(); auto trace = Execution::gen_trace(instructions, returndata, calldata, public_inputs_vec); - // Find the first row enabling the keccak selector - auto row = std::ranges::find_if(trace.begin(), trace.end(), [](Row r) { return r.avm_main_sel_op_keccak == 1; }); - EXPECT_EQ(row->avm_main_ind_a, 36); // Register A is indirect - EXPECT_EQ(row->avm_main_ind_c, 35); // Register C is indirect - EXPECT_EQ(row->avm_main_mem_idx_a, 1); // Indirect(36) -> 1 - EXPECT_EQ(row->avm_main_mem_idx_c, 256); // Indirect(35) -> 256 - EXPECT_EQ(row->avm_main_ia, 189); - EXPECT_EQ(row->avm_main_ic, 0); - // Register b checks are done in the next row due to the difference in the memory tag - std::advance(row, 1); - EXPECT_EQ(row->avm_main_ind_b, 0); // Register B is not - EXPECT_EQ(row->avm_main_mem_idx_b, 37); // Load(37) -> input length - EXPECT_EQ(row->avm_main_ib, 1); // Input length - EXPECT_EQ(returndata, expected_output); validate_trace(std::move(trace), public_inputs); @@ -1307,32 +1165,11 @@ TEST_F(AvmExecutionTests, pedersenHashOpCode) auto bytecode = hex_to_bytes(bytecode_hex); auto instructions = Deserialization::parse(bytecode); - ASSERT_THAT(instructions, SizeIs(6)); - // Pedersen - EXPECT_THAT(instructions.at(4), - AllOf(Field(&Instruction::op_code, OpCode::PEDERSEN), - Field(&Instruction::operands, - ElementsAre(VariantWith(4), - VariantWith(2), - VariantWith(3), - VariantWith(4), - VariantWith(5))))); - // Assign a vector that we will mutate internally in gen_trace to store the return values; std::vector returndata = std::vector(); std::vector calldata = { FF(1), FF(1) }; auto trace = Execution::gen_trace(instructions, returndata, calldata, public_inputs_vec); - // Find the first row enabling the pedersen selector - auto row = std::ranges::find_if(trace.begin(), trace.end(), [](Row r) { return r.avm_main_sel_op_pedersen == 1; }); - EXPECT_EQ(row->avm_main_ind_a, 4); // Register A is indirect - EXPECT_EQ(row->avm_main_mem_idx_a, 0); // Indirect(4) -> 1 - EXPECT_EQ(row->avm_main_ia, 1); // The first input - // The second row loads the U32 values - std::advance(row, 1); - EXPECT_EQ(row->avm_main_ia, 2); // Input length is 2 - EXPECT_EQ(row->avm_main_ib, 5); // Hash offset is 5 - EXPECT_EQ(returndata[0], expected_output); validate_trace(std::move(trace), public_inputs);