From b7766d68727c92f92abc91131a4332db25d805dd Mon Sep 17 00:00:00 2001 From: Jean M <132435771+jeanmon@users.noreply.github.com> Date: Fri, 5 Jan 2024 11:34:35 +0100 Subject: [PATCH] refactor(avm): avm memory trace building (#3835) Resolves #3834 Moving all AVM memory related trace building functions into a dedicated class. Furthermore, all AVM related trace files were moved into a dedicated folder in vm. The following files: - AvmMini_common.hpp - AvmMini_helper.cpp - AvmMini_helper.hpp - AvmMini_mem_trace.cpp - AvmMini_mem_trace.hpp - AvmMini_trace.cpp - AvmMini_trace.hpp were moved from barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/ to barretenberg/cpp/src/barretenberg/vm/avm_trace/ Finally, the namespace for these files were migrating from proof_system to avm_trace. --- .../circuit_builder/AvmMini_trace.cpp | 743 ------------------ .../circuit_builder/AvmMini_trace.hpp | 114 --- .../avm_trace/AvmMini_common.hpp} | 14 +- .../avm_trace}/AvmMini_helper.cpp | 11 +- .../vm/avm_trace/AvmMini_helper.hpp | 9 + .../vm/avm_trace/AvmMini_mem_trace.cpp | 244 ++++++ .../vm/avm_trace/AvmMini_mem_trace.hpp | 68 ++ .../vm/avm_trace/AvmMini_trace.cpp | 597 ++++++++++++++ .../vm/avm_trace/AvmMini_trace.hpp | 71 ++ .../vm/tests/AvmMini_arithmetic.test.cpp | 118 ++- .../vm/tests/AvmMini_control_flow.test.cpp | 17 +- .../vm/tests/AvmMini_memory.test.cpp | 58 +- .../barretenberg/vm/tests/helpers.test.cpp | 22 +- .../barretenberg/vm/tests/helpers.test.hpp | 6 +- 14 files changed, 1100 insertions(+), 992 deletions(-) delete mode 100644 barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/AvmMini_trace.cpp delete mode 100644 barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/AvmMini_trace.hpp rename barretenberg/cpp/src/barretenberg/{proof_system/circuit_builder/AvmMini_helper.hpp => vm/avm_trace/AvmMini_common.hpp} (54%) rename barretenberg/cpp/src/barretenberg/{proof_system/circuit_builder => vm/avm_trace}/AvmMini_helper.cpp (91%) create mode 100644 barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_helper.hpp create mode 100644 barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_mem_trace.cpp create mode 100644 barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_mem_trace.hpp create mode 100644 barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_trace.cpp create mode 100644 barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_trace.hpp diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/AvmMini_trace.cpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/AvmMini_trace.cpp deleted file mode 100644 index 308724f2e16..00000000000 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/AvmMini_trace.cpp +++ /dev/null @@ -1,743 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "./AvmMini_trace.hpp" -#include "./generated/AvmMini_circuit_builder.hpp" - -namespace proof_system { - -/** - * @brief Constructor of a trace builder of AVM. Only serves to set the capacity of the - * underlying traces. - */ -AvmMiniTraceBuilder::AvmMiniTraceBuilder() -{ - mainTrace.reserve(N); - memTrace.reserve(N); -} - -/** - * @brief Resetting the internal state so that a new trace can be rebuilt using the same object. - * - */ -void AvmMiniTraceBuilder::reset() -{ - mainTrace.clear(); - memTrace.clear(); - memory.fill(FF(0)); -} - -/** - * @brief A comparator on MemoryTraceEntry to be used by sorting algorithm. - * - */ -bool AvmMiniTraceBuilder::compareMemEntries(const MemoryTraceEntry& left, const MemoryTraceEntry& right) -{ - if (left.m_addr < right.m_addr) { - return true; - } - - if (left.m_addr > right.m_addr) { - return false; - } - - if (left.m_clk < right.m_clk) { - return true; - } - - if (left.m_clk > right.m_clk) { - return false; - } - - // No safeguard in case they are equal. The caller should ensure this property. - // Otherwise, relation will not be satisfied. - return left.m_sub_clk < right.m_sub_clk; -} - -/** - * @brief A method to insert a row/entry in the memory trace. - * - * @param m_clk Main clock - * @param m_sub_clk Sub-clock used to order load/store sub operations - * @param m_addr Address pertaining to the memory operation - * @param m_val Value (FF) pertaining to the memory operation - * @param m_in_tag Memory tag pertaining to the instruction - * @param m_rw Boolean telling whether it is a load (false) or store operation (true). - */ -void AvmMiniTraceBuilder::insertInMemTrace( - uint32_t m_clk, uint32_t m_sub_clk, uint32_t m_addr, FF m_val, AvmMemoryTag m_in_tag, bool m_rw) -{ - memTrace.emplace_back(MemoryTraceEntry{ - .m_clk = m_clk, - .m_sub_clk = m_sub_clk, - .m_addr = m_addr, - .m_val = m_val, - .m_tag = m_in_tag, - .m_in_tag = m_in_tag, - .m_rw = m_rw, - }); -} - -void AvmMiniTraceBuilder::loadMismatchTagInMemTrace( - uint32_t m_clk, uint32_t m_sub_clk, uint32_t m_addr, FF m_val, AvmMemoryTag m_in_tag, AvmMemoryTag m_tag) -{ - FF one_min_inv = FF(1) - (FF(static_cast(m_in_tag)) - FF(static_cast(m_tag))).invert(); - memTrace.emplace_back(MemoryTraceEntry{ .m_clk = m_clk, - .m_sub_clk = m_sub_clk, - .m_addr = m_addr, - .m_val = m_val, - .m_tag = m_tag, - .m_in_tag = m_in_tag, - .m_tag_err = true, - .m_one_min_inv = one_min_inv }); -} - -// Memory operations need to be performed before the addition of the corresponding row in -// MainTrace, otherwise the m_clk value will be wrong. This applies to loadInMemTrace and -// storeInMemTrace. - -/** - * @brief Add a memory trace entry corresponding to a memory load into the intermediate - * passed register. - * - * @param intermReg The intermediate register - * @param addr The memory address - * @param val The value to be loaded - * @param m_in_tag The memory tag of the instruction - */ -bool AvmMiniTraceBuilder::loadInMemTrace(IntermRegister intermReg, uint32_t addr, FF val, AvmMemoryTag m_in_tag) -{ - uint32_t sub_clk = 0; - switch (intermReg) { - case IntermRegister::ia: - sub_clk = SUB_CLK_LOAD_A; - break; - case IntermRegister::ib: - sub_clk = SUB_CLK_LOAD_B; - break; - case IntermRegister::ic: - sub_clk = SUB_CLK_LOAD_C; - break; - } - - auto m_tag = memoryTag.at(addr); - if (m_tag == AvmMemoryTag::u0 || m_tag == m_in_tag) { - insertInMemTrace(static_cast(mainTrace.size()), sub_clk, addr, val, m_in_tag, false); - return true; - } - - // Handle memory tag inconsistency - loadMismatchTagInMemTrace(static_cast(mainTrace.size()), sub_clk, addr, val, m_in_tag, m_tag); - return false; -} - -/** - * @brief Add a memory trace entry corresponding to a memory store from the intermediate - * register. - * - * @param intermReg The intermediate register - * @param addr The memory address - * @param val The value to be stored - * @param m_in_tag The memory tag of the instruction - */ -void AvmMiniTraceBuilder::storeInMemTrace(IntermRegister intermReg, uint32_t addr, FF val, AvmMemoryTag m_in_tag) -{ - uint32_t sub_clk = 0; - switch (intermReg) { - case IntermRegister::ia: - sub_clk = SUB_CLK_STORE_A; - break; - case IntermRegister::ib: - sub_clk = SUB_CLK_STORE_B; - break; - case IntermRegister::ic: - sub_clk = SUB_CLK_STORE_C; - break; - } - - insertInMemTrace(static_cast(mainTrace.size()), sub_clk, addr, val, m_in_tag, true); -} - -/** TODO: Implement for non finite field types - * @brief Addition with direct memory access. - * - * @param aOffset An index in memory pointing to the first operand of the addition. - * @param bOffset An index in memory pointing to the second operand of the addition. - * @param dstOffset An index in memory pointing to the output of the addition. - * @param inTag The instruction memory tag of the operands. - */ -void AvmMiniTraceBuilder::add(uint32_t aOffset, uint32_t bOffset, uint32_t dstOffset, AvmMemoryTag inTag) -{ - // a + b = c - FF a = memory.at(aOffset); - FF b = memory.at(bOffset); - FF c = a + b; - memory.at(dstOffset) = c; - memoryTag.at(dstOffset) = inTag; - - // Loading into Ia, Ib and storing into Ic - bool tagMatch = loadInMemTrace(IntermRegister::ia, aOffset, a, inTag); - tagMatch = loadInMemTrace(IntermRegister::ib, bOffset, b, inTag) && tagMatch; - storeInMemTrace(IntermRegister::ic, dstOffset, c, inTag); - - auto clk = mainTrace.size(); - - mainTrace.push_back(Row{ - .avmMini_clk = clk, - .avmMini_pc = FF(pc++), - .avmMini_internal_return_ptr = FF(internal_return_ptr), - .avmMini_sel_op_add = FF(1), - .avmMini_in_tag = FF(static_cast(inTag)), - .avmMini_tag_err = FF(static_cast(!tagMatch)), - .avmMini_ia = tagMatch ? a : FF(0), - .avmMini_ib = tagMatch ? b : FF(0), - .avmMini_ic = tagMatch ? c : FF(0), - .avmMini_mem_op_a = FF(1), - .avmMini_mem_op_b = FF(1), - .avmMini_mem_op_c = FF(1), - .avmMini_rwc = FF(1), - .avmMini_mem_idx_a = FF(aOffset), - .avmMini_mem_idx_b = FF(bOffset), - .avmMini_mem_idx_c = FF(dstOffset), - }); -}; - -/** TODO: Implement for non finite field types - * @brief Subtraction with direct memory access. - * - * @param aOffset An index in memory pointing to the first operand of the subtraction. - * @param bOffset An index in memory pointing to the second operand of the subtraction. - * @param dstOffset An index in memory pointing to the output of the subtraction. - * @param inTag The instruction memory tag of the operands. - */ -void AvmMiniTraceBuilder::sub(uint32_t aOffset, uint32_t bOffset, uint32_t dstOffset, AvmMemoryTag inTag) -{ - // a - b = c - FF a = memory.at(aOffset); - FF b = memory.at(bOffset); - FF c = a - b; - memory.at(dstOffset) = c; - memoryTag.at(dstOffset) = inTag; - - // Loading into Ia, Ib and storing into Ic - bool tagMatch = loadInMemTrace(IntermRegister::ia, aOffset, a, inTag); - tagMatch = loadInMemTrace(IntermRegister::ib, bOffset, b, inTag) && tagMatch; - storeInMemTrace(IntermRegister::ic, dstOffset, c, inTag); - - auto clk = mainTrace.size(); - - mainTrace.push_back(Row{ - .avmMini_clk = clk, - .avmMini_pc = FF(pc++), - .avmMini_internal_return_ptr = FF(internal_return_ptr), - .avmMini_sel_op_sub = FF(1), - .avmMini_in_tag = FF(static_cast(inTag)), - .avmMini_tag_err = FF(static_cast(!tagMatch)), - .avmMini_ia = tagMatch ? a : FF(0), - .avmMini_ib = tagMatch ? b : FF(0), - .avmMini_ic = tagMatch ? c : FF(0), - .avmMini_mem_op_a = FF(1), - .avmMini_mem_op_b = FF(1), - .avmMini_mem_op_c = FF(1), - .avmMini_rwc = FF(1), - .avmMini_mem_idx_a = FF(aOffset), - .avmMini_mem_idx_b = FF(bOffset), - .avmMini_mem_idx_c = FF(dstOffset), - }); -}; - -/** TODO: Implement for non finite field types - * @brief Multiplication with direct memory access. - * - * @param aOffset An index in memory pointing to the first operand of the multiplication. - * @param bOffset An index in memory pointing to the second operand of the multiplication. - * @param dstOffset An index in memory pointing to the output of the multiplication. - * @param inTag The instruction memory tag of the operands. - */ -void AvmMiniTraceBuilder::mul(uint32_t aOffset, uint32_t bOffset, uint32_t dstOffset, AvmMemoryTag inTag) -{ - // a * b = c - FF a = memory.at(aOffset); - FF b = memory.at(bOffset); - FF c = a * b; - memory.at(dstOffset) = c; - memoryTag.at(dstOffset) = inTag; - - // Loading into Ia, Ib and storing into Ic - bool tagMatch = loadInMemTrace(IntermRegister::ia, aOffset, a, inTag); - tagMatch = loadInMemTrace(IntermRegister::ib, bOffset, b, inTag) && tagMatch; - storeInMemTrace(IntermRegister::ic, dstOffset, c, inTag); - - auto clk = mainTrace.size(); - - mainTrace.push_back(Row{ - .avmMini_clk = clk, - .avmMini_pc = FF(pc++), - .avmMini_internal_return_ptr = FF(internal_return_ptr), - .avmMini_sel_op_mul = FF(1), - .avmMini_in_tag = FF(static_cast(inTag)), - .avmMini_tag_err = FF(static_cast(!tagMatch)), - .avmMini_ia = tagMatch ? a : FF(0), - .avmMini_ib = tagMatch ? b : FF(0), - .avmMini_ic = tagMatch ? c : FF(0), - .avmMini_mem_op_a = FF(1), - .avmMini_mem_op_b = FF(1), - .avmMini_mem_op_c = FF(1), - .avmMini_rwc = FF(1), - .avmMini_mem_idx_a = FF(aOffset), - .avmMini_mem_idx_b = FF(bOffset), - .avmMini_mem_idx_c = FF(dstOffset), - }); -} - -/** TODO: Implement for non finite field types - * @brief Division with direct memory access. - * - * @param aOffset An index in memory pointing to the first operand of the division. - * @param bOffset An index in memory pointing to the second operand of the division. - * @param dstOffset An index in memory pointing to the output of the division. - * @param inTag The instruction memory tag of the operands. - */ -void AvmMiniTraceBuilder::div(uint32_t aOffset, uint32_t bOffset, uint32_t dstOffset, AvmMemoryTag inTag) -{ - // a * b^(-1) = c - FF a = memory.at(aOffset); - FF b = memory.at(bOffset); - FF c; - FF inv; - FF error; - - if (!b.is_zero()) { - - inv = b.invert(); - c = a * inv; - error = 0; - } else { - inv = 1; - c = 0; - error = 1; - } - - memory.at(dstOffset) = c; - memoryTag.at(dstOffset) = inTag; - - // Loading into Ia, Ib and storing into Ic - bool tagMatch = loadInMemTrace(IntermRegister::ia, aOffset, a, inTag); - tagMatch = loadInMemTrace(IntermRegister::ib, bOffset, b, inTag) && tagMatch; - storeInMemTrace(IntermRegister::ic, dstOffset, c, inTag); - - auto clk = mainTrace.size(); - - mainTrace.push_back(Row{ - .avmMini_clk = clk, - .avmMini_pc = FF(pc++), - .avmMini_internal_return_ptr = FF(internal_return_ptr), - .avmMini_sel_op_div = FF(1), - .avmMini_in_tag = FF(static_cast(inTag)), - .avmMini_op_err = tagMatch ? error : FF(1), - .avmMini_tag_err = FF(static_cast(!tagMatch)), - .avmMini_inv = tagMatch ? inv : FF(1), - .avmMini_ia = tagMatch ? a : FF(0), - .avmMini_ib = tagMatch ? b : FF(0), - .avmMini_ic = tagMatch ? c : FF(0), - .avmMini_mem_op_a = FF(1), - .avmMini_mem_op_b = FF(1), - .avmMini_mem_op_c = FF(1), - .avmMini_rwc = FF(1), - .avmMini_mem_idx_a = FF(aOffset), - .avmMini_mem_idx_b = FF(bOffset), - .avmMini_mem_idx_c = FF(dstOffset), - }); -} - -/** - * @brief CALLDATACOPY opcode with direct memory access, i.e., - * M[dstOffset:dstOffset+copySize] = calldata[cdOffset:cdOffset+copySize] - * Simplified version with exclusively memory store operations and - * values from M_calldata passed by an array and loaded into - * intermediate registers. - * Assume that caller passes callDataMem which is large enough so that - * no out-of-bound memory issues occur. - * TODO: Implement the indirect memory version (maybe not required) - * TODO: taking care of intermediate register values consistency and propagating their - * values to the next row when not overwritten. - * - * @param cdOffset The starting index of the region in calldata to be copied. - * @param copySize The number of finite field elements to be copied into memory. - * @param dstOffset The starting index of memory where calldata will be copied to. - * @param callDataMem The vector containing calldata. - */ -void AvmMiniTraceBuilder::callDataCopy(uint32_t cdOffset, - uint32_t copySize, - uint32_t dstOffset, - std::vector const& callDataMem) -{ - // We parallelize storing memory operations in chunk of 3, i.e., 1 per intermediate register. - // The variable pos is an index pointing to the first storing operation (pertaining to intermediate - // register Ia) relative to cdOffset: - // cdOffset + pos: Ia memory store operation - // cdOffset + pos + 1: Ib memory store operation - // cdOffset + pos + 2: Ic memory store operation - - uint32_t pos = 0; - - while (pos < copySize) { - FF ib(0); - FF ic(0); - uint32_t mem_op_b(0); - uint32_t mem_op_c(0); - uint32_t mem_idx_b(0); - uint32_t mem_idx_c(0); - uint32_t rwb(0); - uint32_t rwc(0); - auto clk = mainTrace.size(); - - FF ia = callDataMem.at(cdOffset + pos); - uint32_t mem_op_a(1); - uint32_t mem_idx_a = dstOffset + pos; - uint32_t rwa = 1; - - // Storing from Ia - memory.at(mem_idx_a) = ia; - memoryTag.at(mem_idx_a) = AvmMemoryTag::ff; - storeInMemTrace(IntermRegister::ia, mem_idx_a, ia, AvmMemoryTag::ff); - - if (copySize - pos > 1) { - ib = callDataMem.at(cdOffset + pos + 1); - mem_op_b = 1; - mem_idx_b = dstOffset + pos + 1; - rwb = 1; - - // Storing from Ib - memory.at(mem_idx_b) = ib; - memoryTag.at(mem_idx_b) = AvmMemoryTag::ff; - storeInMemTrace(IntermRegister::ib, mem_idx_b, ib, AvmMemoryTag::ff); - } - - if (copySize - pos > 2) { - ic = callDataMem.at(cdOffset + pos + 2); - mem_op_c = 1; - mem_idx_c = dstOffset + pos + 2; - rwc = 1; - - // Storing from Ic - memory.at(mem_idx_c) = ic; - memoryTag.at(mem_idx_c) = AvmMemoryTag::ff; - storeInMemTrace(IntermRegister::ic, mem_idx_c, ic, AvmMemoryTag::ff); - } - - mainTrace.push_back(Row{ - .avmMini_clk = clk, - .avmMini_pc = FF(pc++), - .avmMini_internal_return_ptr = FF(internal_return_ptr), - .avmMini_in_tag = FF(static_cast(AvmMemoryTag::ff)), - .avmMini_ia = ia, - .avmMini_ib = ib, - .avmMini_ic = ic, - .avmMini_mem_op_a = FF(mem_op_a), - .avmMini_mem_op_b = FF(mem_op_b), - .avmMini_mem_op_c = FF(mem_op_c), - .avmMini_rwa = FF(rwa), - .avmMini_rwb = FF(rwb), - .avmMini_rwc = FF(rwc), - .avmMini_mem_idx_a = FF(mem_idx_a), - .avmMini_mem_idx_b = FF(mem_idx_b), - .avmMini_mem_idx_c = FF(mem_idx_c), - }); - - if (copySize - pos > 2) { // Guard to prevent overflow if copySize is close to uint32_t maximum value. - pos += 3; - } else { - pos = copySize; - } - } -} - -/** - * @brief RETURN opcode with direct memory access, i.e., - * return(M[retOffset:retOffset+retSize]) - * Simplified version with exclusively memory load operations into - * intermediate registers and then values are copied to the returned vector. - * TODO: Implement the indirect memory version (maybe not required) - * TODO: taking care of flagging this row as the last one? Special STOP flag? - * - * @param retOffset The starting index of the memory region to be returned. - * @param retSize The number of elements to be returned. - * @return The returned memory region as a std::vector. - */ - -std::vector AvmMiniTraceBuilder::returnOP(uint32_t retOffset, uint32_t retSize) -{ - // We parallelize loading memory operations in chunk of 3, i.e., 1 per intermediate register. - // The variable pos is an index pointing to the first storing operation (pertaining to intermediate - // register Ia) relative to retOffset: - // retOffset + pos: Ia memory load operation - // retOffset + pos + 1: Ib memory load operation - // retOffset + pos + 2: Ic memory load operation - - uint32_t pos = 0; - std::vector returnMem; - - while (pos < retSize) { - FF ib(0); - FF ic(0); - uint32_t mem_op_b(0); - uint32_t mem_op_c(0); - uint32_t mem_idx_b(0); - uint32_t mem_idx_c(0); - auto clk = mainTrace.size(); - - uint32_t mem_op_a(1); - uint32_t mem_idx_a = retOffset + pos; - FF ia = memory.at(mem_idx_a); - - // Loading from Ia - returnMem.push_back(ia); - loadInMemTrace(IntermRegister::ia, mem_idx_a, ia, AvmMemoryTag::ff); - - if (retSize - pos > 1) { - mem_op_b = 1; - mem_idx_b = retOffset + pos + 1; - ib = memory.at(mem_idx_b); - - // Loading from Ib - returnMem.push_back(ib); - loadInMemTrace(IntermRegister::ib, mem_idx_b, ib, AvmMemoryTag::ff); - } - - if (retSize - pos > 2) { - mem_op_c = 1; - mem_idx_c = retOffset + pos + 2; - ic = memory.at(mem_idx_c); - - // Loading from Ic - returnMem.push_back(ic); - loadInMemTrace(IntermRegister::ic, mem_idx_c, ic, AvmMemoryTag::ff); - } - - mainTrace.push_back(Row{ - .avmMini_clk = clk, - .avmMini_pc = FF(pc), - .avmMini_internal_return_ptr = FF(internal_return_ptr), - .avmMini_sel_halt = FF(1), - .avmMini_in_tag = FF(static_cast(AvmMemoryTag::ff)), - .avmMini_ia = ia, - .avmMini_ib = ib, - .avmMini_ic = ic, - .avmMini_mem_op_a = FF(mem_op_a), - .avmMini_mem_op_b = FF(mem_op_b), - .avmMini_mem_op_c = FF(mem_op_c), - .avmMini_mem_idx_a = FF(mem_idx_a), - .avmMini_mem_idx_b = FF(mem_idx_b), - .avmMini_mem_idx_c = FF(mem_idx_c), - }); - - if (retSize - pos > 2) { // Guard to prevent overflow if retSize is close to uint32_t maximum value. - pos += 3; - } else { - pos = retSize; - } - } - return returnMem; -} - -/** - * @brief HALT opcode - * This opcode effectively stops program execution, and is used in the relation that - * ensures the program counter increments on each opcode. - * i.e.ythe program counter should freeze and the halt flag is set to 1. - */ -void AvmMiniTraceBuilder::halt() -{ - auto clk = mainTrace.size(); - - mainTrace.push_back(Row{ - .avmMini_clk = clk, - .avmMini_pc = FF(pc), - .avmMini_internal_return_ptr = FF(internal_return_ptr), - .avmMini_sel_halt = FF(1), - }); -} - -/** - * @brief JUMP OPCODE - * Jumps to a new `jmpDest` - * This function must: - * - Set the next program counter to the provided `jmpDest`. - * - * @param jmpDest - The destination to jump to - */ -void AvmMiniTraceBuilder::jump(uint32_t jmpDest) -{ - auto clk = mainTrace.size(); - - mainTrace.push_back(Row{ - .avmMini_clk = clk, - .avmMini_pc = FF(pc), - .avmMini_internal_return_ptr = FF(internal_return_ptr), - .avmMini_sel_jump = FF(1), - .avmMini_ia = FF(jmpDest), - }); - - // Adjust parameters for the next row - pc = jmpDest; -} - -/** - * @brief INTERNAL_CALL OPCODE - * This opcode effectively jumps to a new `jmpDest` and stores the return program counter - * (current program counter + 1) onto a call stack. - * This function must: - * - Set the next program counter to the provided `jmpDest`. - * - Store the current `pc` + 1 onto the call stack (emulated in memory) - * - Increment the return stack pointer (a pointer to where the call stack is in memory) - * - * Note: We use intermediate register to perform memory storage operations. - * - * @param jmpDest - The destination to jump to - */ -void AvmMiniTraceBuilder::internal_call(uint32_t jmpDest) -{ - auto clk = mainTrace.size(); - - // We store the next instruction as the return location - uint32_t stored_pc = pc + 1; - internal_call_stack.push(stored_pc); - - // Add the return location to the memory trace - storeInMemTrace(IntermRegister::ib, internal_return_ptr, FF(stored_pc), AvmMemoryTag::ff); - memory.at(internal_return_ptr) = stored_pc; - - mainTrace.push_back(Row{ - .avmMini_clk = clk, - .avmMini_pc = FF(pc), - .avmMini_internal_return_ptr = FF(internal_return_ptr), - .avmMini_sel_internal_call = FF(1), - .avmMini_ia = FF(jmpDest), - .avmMini_ib = stored_pc, - .avmMini_mem_op_b = FF(1), - .avmMini_rwb = FF(1), - .avmMini_mem_idx_b = FF(internal_return_ptr), - }); - - // Adjust parameters for the next row - pc = jmpDest; - internal_return_ptr++; -} - -/** - * @brief INTERNAL_RETURN OPCODE - * The opcode returns from an internal call. - * This function must: - * - Read the return location from the internal_return_ptr - * - Set the next program counter to the return location - * - Decrement the return stack pointer - * - * TODO(https://github.com/AztecProtocol/aztec-packages/issues/3740): This function MUST come after a call instruction. - */ -void AvmMiniTraceBuilder::internal_return() -{ - auto clk = mainTrace.size(); - - // Internal return pointer is decremented - FF a = memory.at(internal_return_ptr - 1); - - // We want to load the value pointed by the internal pointer - loadInMemTrace(IntermRegister::ia, internal_return_ptr - 1, FF(a), AvmMemoryTag::ff); - - mainTrace.push_back(Row{ - .avmMini_clk = clk, - .avmMini_pc = pc, - .avmMini_internal_return_ptr = FF(internal_return_ptr), - .avmMini_sel_internal_return = FF(1), - .avmMini_ia = a, - .avmMini_mem_op_a = FF(1), - .avmMini_rwa = FF(0), - .avmMini_mem_idx_a = FF(internal_return_ptr - 1), - }); - - // We want the next row to be the one pointed by jmpDest - // The next pc should be from the top of the internal call stack + 1 - pc = internal_call_stack.top(); - internal_call_stack.pop(); - internal_return_ptr--; -} - -/** - * @brief Helper to initialize ffMemory. (Testing purpose mostly.) - * - */ -void AvmMiniTraceBuilder::setFFMem(size_t idx, FF el, AvmMemoryTag tag) -{ - memory.at(idx) = el; - memoryTag.at(idx) = tag; -}; - -/** - * @brief Finalisation of the memory trace and incorporating it to the main trace. - * In particular, sorting the memory trace, setting .m_lastAccess and - * adding shifted values (first row). The main trace is moved at the end of - * this call. - * - * @return The main trace - */ -std::vector AvmMiniTraceBuilder::finalize() -{ - size_t memTraceSize = memTrace.size(); - size_t mainTraceSize = mainTrace.size(); - - // TODO: We will have to handle this through error handling and not an assertion - // Smaller than N because we have to add an extra initial row to support shifted - // elements - assert(memTraceSize < N); - assert(mainTraceSize < N); - - // Sort memTrace - std::sort(memTrace.begin(), memTrace.end(), compareMemEntries); - - // Fill the rest with zeros. - size_t zeroRowsNum = N - mainTraceSize - 1; - while (zeroRowsNum-- > 0) { - mainTrace.push_back(Row{}); - } - - mainTrace.at(mainTraceSize - 1).avmMini_last = FF(1); - - for (size_t i = 0; i < memTraceSize; i++) { - auto const& src = memTrace.at(i); - auto& dest = mainTrace.at(i); - - dest.memTrace_m_clk = FF(src.m_clk); - dest.memTrace_m_sub_clk = FF(src.m_sub_clk); - dest.memTrace_m_addr = FF(src.m_addr); - dest.memTrace_m_val = src.m_val; - dest.memTrace_m_rw = FF(static_cast(src.m_rw)); - dest.memTrace_m_in_tag = FF(static_cast(src.m_in_tag)); - dest.memTrace_m_tag = FF(static_cast(src.m_tag)); - dest.memTrace_m_tag_err = FF(static_cast(src.m_tag_err)); - dest.memTrace_m_one_min_inv = src.m_one_min_inv; - - if (i + 1 < memTraceSize) { - auto const& next = memTrace.at(i + 1); - dest.memTrace_m_lastAccess = FF(static_cast(src.m_addr != next.m_addr)); - } else { - dest.memTrace_m_lastAccess = FF(1); - dest.memTrace_m_last = FF(1); - } - } - - // Adding extra row for the shifted values at the top of the execution trace. - Row first_row = Row{ .avmMini_first = FF(1), .memTrace_m_lastAccess = FF(1) }; - mainTrace.insert(mainTrace.begin(), first_row); - - return std::move(mainTrace); -} - -} // namespace proof_system \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/AvmMini_trace.hpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/AvmMini_trace.hpp deleted file mode 100644 index 0185d76cf97..00000000000 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/AvmMini_trace.hpp +++ /dev/null @@ -1,114 +0,0 @@ -#pragma once - -#include - -#include "barretenberg/common/throw_or_abort.hpp" -#include "barretenberg/ecc/curves/bn254/fr.hpp" -#include "barretenberg/proof_system/circuit_builder/circuit_builder_base.hpp" -#include "barretenberg/proof_system/circuit_builder/generated/AvmMini_circuit_builder.hpp" - -#include "barretenberg/flavor/generated/AvmMini_flavor.hpp" - -#include "barretenberg/relations/generated/AvmMini/avm_mini.hpp" - -using Flavor = proof_system::honk::flavor::AvmMiniFlavor; -using FF = Flavor::FF; -using Row = proof_system::AvmMiniFullRow; - -namespace proof_system { - -enum class IntermRegister : uint32_t { ia = 0, ib = 1, ic = 2 }; -enum class AvmMemoryTag : uint32_t { u0 = 0, u8 = 1, u16 = 2, u32 = 3, u64 = 4, u128 = 5, ff = 6 }; - -// 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. -// At the end of circuit building, mainTrace can be moved to AvmMiniCircuitBuilder by calling -// AvmMiniCircuitBuilder::set_trace(rows). -class AvmMiniTraceBuilder { - - public: - // Number of rows - static const size_t N = 256; - static const size_t MEM_SIZE = 1024; - static const size_t CALLSTACK_OFFSET = 896; // TODO(md): Temporary reserved area 896 - 1024 - - static const uint32_t SUB_CLK_LOAD_A = 0; - static const uint32_t SUB_CLK_LOAD_B = 1; - static const uint32_t SUB_CLK_LOAD_C = 2; - static const uint32_t SUB_CLK_STORE_A = 3; - static const uint32_t SUB_CLK_STORE_B = 4; - static const uint32_t SUB_CLK_STORE_C = 5; - - AvmMiniTraceBuilder(); - - // Temporary helper to initialize memory. - void setFFMem(size_t idx, FF el, AvmMemoryTag tag); - - std::vector finalize(); - void reset(); - - // Addition with direct memory access. - void add(uint32_t aOffset, uint32_t bOffset, uint32_t dstOffset, AvmMemoryTag inTag); - - // Subtraction with direct memory access. - void sub(uint32_t aOffset, uint32_t bOffset, uint32_t dstOffset, AvmMemoryTag inTag); - - // Multiplication with direct memory access. - void mul(uint32_t aOffset, uint32_t bOffset, uint32_t dstOffset, AvmMemoryTag inTag); - - // Division with direct memory access. - void div(uint32_t aOffset, uint32_t bOffset, uint32_t dstOffset, AvmMemoryTag inTag); - - // Jump to a given program counter. - void jump(uint32_t jmpDest); - - // Jump to a given program counter; storing the return location on a call stack. - // TODO(md): this program counter MUST be an operand to the OPCODE. - void internal_call(uint32_t jmpDest); - - // Return from a jump. - void internal_return(); - - // Halt -> stop program execution. - void halt(); - - // CALLDATACOPY opcode with direct memory access, i.e., - // M[dstOffset:dstOffset+copySize] = calldata[cdOffset:cdOffset+copySize] - void callDataCopy(uint32_t cdOffset, uint32_t copySize, uint32_t dstOffset, std::vector const& callDataMem); - - // RETURN opcode with direct memory access, i.e., - // return(M[retOffset:retOffset+retSize]) - std::vector returnOP(uint32_t retOffset, uint32_t retSize); - - private: - struct MemoryTraceEntry { - uint32_t m_clk; - uint32_t m_sub_clk; - uint32_t m_addr; - FF m_val{}; - AvmMemoryTag m_tag; - AvmMemoryTag m_in_tag; - bool m_rw = false; - bool m_tag_err = false; - FF m_one_min_inv{}; - }; - - std::vector mainTrace; - std::vector memTrace; // Entries will be sorted by m_clk, m_sub_clk after finalize(). - std::array memory{}; // Memory table (used for simulation) - std::array memoryTag{}; // The tag of the corresponding memory - // entry (aligned with the memory array). - - uint32_t pc = 0; - uint32_t internal_return_ptr = CALLSTACK_OFFSET; - std::stack internal_call_stack = {}; - - static bool compareMemEntries(const MemoryTraceEntry& left, const MemoryTraceEntry& right); - void insertInMemTrace( - uint32_t m_clk, uint32_t m_sub_clk, uint32_t m_addr, FF m_val, AvmMemoryTag m_in_tag, bool m_rw); - void loadMismatchTagInMemTrace( - uint32_t m_clk, uint32_t m_sub_clk, uint32_t m_addr, FF m_val, AvmMemoryTag m_in_tag, AvmMemoryTag m_tag); - bool loadInMemTrace(IntermRegister intermReg, uint32_t addr, FF val, AvmMemoryTag m_in_tag); - void storeInMemTrace(IntermRegister intermReg, uint32_t addr, FF val, AvmMemoryTag m_in_tag); -}; -} // namespace proof_system diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/AvmMini_helper.hpp b/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_common.hpp similarity index 54% rename from barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/AvmMini_helper.hpp rename to barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_common.hpp index 491d597fa26..b925d5e01c2 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/AvmMini_helper.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_common.hpp @@ -1,17 +1,17 @@ #pragma once -#include "barretenberg/ecc/curves/bn254/fr.hpp" #include "barretenberg/proof_system/circuit_builder/circuit_builder_base.hpp" - -#include "barretenberg/flavor/generated/AvmMini_flavor.hpp" #include "barretenberg/proof_system/circuit_builder/generated/AvmMini_circuit_builder.hpp" -namespace proof_system { - using Flavor = proof_system::honk::flavor::AvmMiniFlavor; using FF = Flavor::FF; using Row = proof_system::AvmMiniFullRow; -void log_avmMini_trace(std::vector const& trace, size_t beg, size_t end); +namespace avm_trace { + +// Number of rows +static const size_t AVM_TRACE_SIZE = 256; +enum class IntermRegister : uint32_t { ia = 0, ib = 1, ic = 2 }; +enum class AvmMemoryTag : uint32_t { u0 = 0, u8 = 1, u16 = 2, u32 = 3, u64 = 4, u128 = 5, ff = 6 }; -} // namespace proof_system \ No newline at end of file +} // namespace avm_trace \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/AvmMini_helper.cpp b/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_helper.cpp similarity index 91% rename from barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/AvmMini_helper.cpp rename to barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_helper.cpp index 60d92a168cc..207485ac63d 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/AvmMini_helper.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_helper.cpp @@ -1,11 +1,6 @@ -#include "barretenberg/ecc/curves/bn254/fr.hpp" -#include "barretenberg/proof_system/circuit_builder/circuit_builder_base.hpp" +#include "AvmMini_helper.hpp" -#include "./AvmMini_helper.hpp" -#include "barretenberg/flavor/generated/AvmMini_flavor.hpp" -#include "barretenberg/relations/generated/AvmMini/avm_mini.hpp" - -namespace proof_system { +namespace avm_trace { /** * @brief Routine to log some slice of a trace of the AVM. Used to debug or in some unit tests. @@ -70,4 +65,4 @@ void log_avmMini_trace(std::vector const& trace, size_t beg, size_t end) } } -} // namespace proof_system \ No newline at end of file +} // namespace avm_trace \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_helper.hpp b/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_helper.hpp new file mode 100644 index 00000000000..eda2b5c89a6 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_helper.hpp @@ -0,0 +1,9 @@ +#pragma once + +#include "AvmMini_common.hpp" + +namespace avm_trace { + +void log_avmMini_trace(std::vector const& trace, size_t beg, size_t end); + +} // namespace avm_trace \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_mem_trace.cpp b/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_mem_trace.cpp new file mode 100644 index 00000000000..f55d7e9d8b6 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_mem_trace.cpp @@ -0,0 +1,244 @@ +#include "AvmMini_mem_trace.hpp" + +namespace avm_trace { + +/** + * @brief Constructor of a memory trace builder of AVM. Only serves to set the capacity of the + * underlying traces. + */ +AvmMiniMemTraceBuilder::AvmMiniMemTraceBuilder() +{ + mem_trace.reserve(AVM_TRACE_SIZE); +} + +/** + * @brief Resetting the internal state so that a new memory trace can be rebuilt using the same object. + * + */ +void AvmMiniMemTraceBuilder::reset() +{ + mem_trace.clear(); + memory.fill(FF(0)); +} + +/** + * @brief A comparator on MemoryTraceEntry to be used by sorting algorithm. We sort first by + * ascending address (m_addr), then by clock (m_clk) and finally sub-clock (m_sub_clk). + * + * @param left The left hand side memory trace entry + * @param right The right hand side memory trace entry + * + * @return A boolean indicating whether left member is smaller than right member. + */ +bool AvmMiniMemTraceBuilder::compare_mem_entries(const MemoryTraceEntry& left, const MemoryTraceEntry& right) +{ + if (left.m_addr < right.m_addr) { + return true; + } + + if (left.m_addr > right.m_addr) { + return false; + } + + if (left.m_clk < right.m_clk) { + return true; + } + + if (left.m_clk > right.m_clk) { + return false; + } + + // No safeguard in case they are equal. The caller should ensure this property. + // Otherwise, relation will not be satisfied. + return left.m_sub_clk < right.m_sub_clk; +} + +/** + * @brief Prepare the memory trace to be incorporated into the main trace. + * + * @return The memory trace (which is moved). + */ +std::vector AvmMiniMemTraceBuilder::finalize() +{ + // Sort memTrace + std::sort(mem_trace.begin(), mem_trace.end(), compare_mem_entries); + return std::move(mem_trace); +} + +/** + * @brief A method to insert a row/entry in the memory trace. + * + * @param m_clk Main clock + * @param m_sub_clk Sub-clock used to order load/store sub operations + * @param m_addr Address pertaining to the memory operation + * @param m_val Value (FF) pertaining to the memory operation + * @param m_in_tag Memory tag pertaining to the instruction + * @param m_rw Boolean telling whether it is a load (false) or store operation (true). + */ +void AvmMiniMemTraceBuilder::insert_in_mem_trace(uint32_t const m_clk, + uint32_t const m_sub_clk, + uint32_t const m_addr, + FF const& m_val, + AvmMemoryTag const m_in_tag, + bool const m_rw) +{ + mem_trace.emplace_back(MemoryTraceEntry{ + .m_clk = m_clk, + .m_sub_clk = m_sub_clk, + .m_addr = m_addr, + .m_val = m_val, + .m_tag = m_in_tag, + .m_in_tag = m_in_tag, + .m_rw = m_rw, + }); +} + +// Memory operations need to be performed before the addition of the corresponding row in +// MainTrace, otherwise the m_clk value will be wrong. This applies to loadInMemTrace and +// storeInMemTrace. + +/** + * @brief Add a memory trace entry for a load with a memory tag mismatching the instruction + * memory tag. + * + * @param m_clk Main clock + * @param m_sub_clk Sub-clock used to order load/store sub operations + * @param m_addr Address pertaining to the memory operation + * @param m_val Value (FF) pertaining to the memory operation + * @param m_in_tag Memory tag pertaining to the instruction + * @param m_tag Memory tag pertaining to the address + */ +void AvmMiniMemTraceBuilder::load_mismatch_tag_in_mem_trace(uint32_t const m_clk, + uint32_t const m_sub_clk, + uint32_t const m_addr, + FF const& m_val, + AvmMemoryTag const m_in_tag, + AvmMemoryTag const m_tag) +{ + FF one_min_inv = FF(1) - (FF(static_cast(m_in_tag)) - FF(static_cast(m_tag))).invert(); + mem_trace.emplace_back(MemoryTraceEntry{ .m_clk = m_clk, + .m_sub_clk = m_sub_clk, + .m_addr = m_addr, + .m_val = m_val, + .m_tag = m_tag, + .m_in_tag = m_in_tag, + .m_tag_err = true, + .m_one_min_inv = one_min_inv }); +} + +/** + * @brief Add a memory trace entry corresponding to a memory load into the intermediate + * passed register. + * + * @param clk The main clock + * @param interm_reg The intermediate register + * @param addr The memory address + * @param val The value to be loaded + * @param m_in_tag The memory tag of the instruction + * + * @return A boolean indicating that memory tag matches (resp. does not match) the + * instruction tag. Set to false in case of a mismatch. + */ +bool AvmMiniMemTraceBuilder::load_in_mem_trace( + uint32_t clk, IntermRegister interm_reg, uint32_t addr, FF const& val, AvmMemoryTag m_in_tag) +{ + uint32_t sub_clk = 0; + switch (interm_reg) { + case IntermRegister::ia: + sub_clk = SUB_CLK_LOAD_A; + break; + case IntermRegister::ib: + sub_clk = SUB_CLK_LOAD_B; + break; + case IntermRegister::ic: + sub_clk = SUB_CLK_LOAD_C; + break; + } + + auto m_tag = memory_tag.at(addr); + if (m_tag == AvmMemoryTag::u0 || m_tag == m_in_tag) { + insert_in_mem_trace(clk, sub_clk, addr, val, m_in_tag, false); + return true; + } + + // Handle memory tag inconsistency + load_mismatch_tag_in_mem_trace(clk, sub_clk, addr, val, m_in_tag, m_tag); + return false; +} + +/** + * @brief Add a memory trace entry corresponding to a memory store from the intermediate + * register. + * + * @param clk The main clock + * @param interm_reg The intermediate register + * @param addr The memory address + * @param val The value to be stored + * @param m_in_tag The memory tag of the instruction + */ +void AvmMiniMemTraceBuilder::store_in_mem_trace( + uint32_t clk, IntermRegister interm_reg, uint32_t addr, FF const& val, AvmMemoryTag m_in_tag) +{ + uint32_t sub_clk = 0; + switch (interm_reg) { + case IntermRegister::ia: + sub_clk = SUB_CLK_STORE_A; + break; + case IntermRegister::ib: + sub_clk = SUB_CLK_STORE_B; + break; + case IntermRegister::ic: + sub_clk = SUB_CLK_STORE_C; + break; + } + + insert_in_mem_trace(clk, sub_clk, addr, val, m_in_tag, true); +} + +/** + * @brief Handle a read memory operation and load the corresponding value to the + * supplied intermediate register. A memory trace entry for the load operation + * is added. + * + * @param clk Main clock + * @param interm_reg Intermediate register where we load the value + * @param addr Memory address to be read and loaded + * @param m_in_tag Memory instruction tag + * + * @return Result of the read operation containing the value and a boolean telling + * potential mismatch between instruction tag and memory tag of the address. + */ +AvmMiniMemTraceBuilder::MemRead AvmMiniMemTraceBuilder::read_and_load_from_memory(uint32_t const clk, + IntermRegister const interm_reg, + uint32_t const addr, + AvmMemoryTag const m_in_tag) +{ + FF val = memory.at(addr); + bool tagMatch = load_in_mem_trace(clk, interm_reg, addr, val, m_in_tag); + + return MemRead{ + .tag_match = tagMatch, + .val = val, + }; +} + +/** + * @brief Handle a write memory operation and store the supplied value into memory + * at the supplied address. A memory trace entry for the write operation + * is added. + * + * @param clk Main clock + * @param interm_reg Intermediate register where we write the value + * @param addr Memory address to be written to + * @param val Value to be written into memory + * @param m_in_tag Memory instruction tag + */ +void AvmMiniMemTraceBuilder::write_into_memory( + uint32_t const clk, IntermRegister interm_reg, uint32_t addr, FF const& val, AvmMemoryTag m_in_tag) +{ + memory.at(addr) = val; + memory_tag.at(addr) = m_in_tag; + store_in_mem_trace(clk, interm_reg, addr, val, m_in_tag); +} + +} // namespace avm_trace \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_mem_trace.hpp b/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_mem_trace.hpp new file mode 100644 index 00000000000..6a06dcee1fb --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_mem_trace.hpp @@ -0,0 +1,68 @@ +#pragma once + +#include "AvmMini_common.hpp" + +namespace avm_trace { + +class AvmMiniMemTraceBuilder { + + public: + static const size_t MEM_SIZE = 1024; + static const uint32_t SUB_CLK_LOAD_A = 0; + static const uint32_t SUB_CLK_LOAD_B = 1; + static const uint32_t SUB_CLK_LOAD_C = 2; + static const uint32_t SUB_CLK_STORE_A = 3; + static const uint32_t SUB_CLK_STORE_B = 4; + static const uint32_t SUB_CLK_STORE_C = 5; + + struct MemoryTraceEntry { + uint32_t m_clk; + uint32_t m_sub_clk; + uint32_t m_addr; + FF m_val{}; + AvmMemoryTag m_tag; + AvmMemoryTag m_in_tag; + bool m_rw = false; + bool m_tag_err = false; + FF m_one_min_inv{}; + }; + + // Structure to return value and tag matching boolean after a memory read. + struct MemRead { + bool tag_match; + FF val; + }; + + AvmMiniMemTraceBuilder(); + + void reset(); + + std::vector finalize(); + + MemRead read_and_load_from_memory(uint32_t clk, IntermRegister interm_reg, uint32_t addr, AvmMemoryTag m_in_tag); + void write_into_memory( + uint32_t clk, IntermRegister interm_reg, uint32_t addr, FF const& val, AvmMemoryTag m_in_tag); + + private: + std::vector mem_trace; // Entries will be sorted by m_clk, m_sub_clk after finalize(). + std::array memory{}; // Memory table (used for simulation) + std::array memory_tag{}; // The tag of the corresponding memory + // entry (aligned with the memory array). + + static bool compare_mem_entries(const MemoryTraceEntry& left, const MemoryTraceEntry& right); + + void insert_in_mem_trace( + uint32_t m_clk, uint32_t m_sub_clk, uint32_t m_addr, FF const& m_val, AvmMemoryTag m_in_tag, bool m_rw); + void load_mismatch_tag_in_mem_trace(uint32_t m_clk, + uint32_t m_sub_clk, + uint32_t m_addr, + FF const& m_val, + AvmMemoryTag m_in_tag, + AvmMemoryTag m_tag); + + bool load_in_mem_trace( + uint32_t clk, IntermRegister interm_reg, uint32_t addr, FF const& val, AvmMemoryTag m_in_tag); + void store_in_mem_trace( + uint32_t clk, IntermRegister interm_reg, uint32_t addr, FF const& val, AvmMemoryTag m_in_tag); +}; +} // namespace avm_trace \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_trace.cpp b/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_trace.cpp new file mode 100644 index 00000000000..c38a615560f --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_trace.cpp @@ -0,0 +1,597 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "AvmMini_trace.hpp" + +namespace avm_trace { + +/** + * @brief Constructor of a trace builder of AVM. Only serves to set the capacity of the + * underlying traces. + */ +AvmMiniTraceBuilder::AvmMiniTraceBuilder() +{ + main_trace.reserve(AVM_TRACE_SIZE); +} + +/** + * @brief Resetting the internal state so that a new trace can be rebuilt using the same object. + * + */ +void AvmMiniTraceBuilder::reset() +{ + main_trace.clear(); + mem_trace_builder.reset(); +} + +/** TODO: Implement for non finite field types + * @brief Addition with direct memory access. + * + * @param a_offset An index in memory pointing to the first operand of the addition. + * @param b_offset An index in memory pointing to the second operand of the addition. + * @param dst_offset An index in memory pointing to the output of the addition. + * @param in_tag The instruction memory tag of the operands. + */ +void AvmMiniTraceBuilder::add(uint32_t a_offset, uint32_t b_offset, uint32_t dst_offset, AvmMemoryTag in_tag) +{ + auto clk = static_cast(main_trace.size()); + + // Reading from memory and loading into ia resp. ib. + auto read_a = mem_trace_builder.read_and_load_from_memory(clk, IntermRegister::ia, a_offset, in_tag); + auto read_b = mem_trace_builder.read_and_load_from_memory(clk, IntermRegister::ib, b_offset, in_tag); + bool tag_match = read_a.tag_match && read_b.tag_match; + + // a + b = c + FF a = read_a.val; + FF b = read_b.val; + FF c = a + b; + + // Write into memory value c from intermediate register ic. + mem_trace_builder.write_into_memory(clk, IntermRegister::ic, dst_offset, c, in_tag); + + main_trace.push_back(Row{ + .avmMini_clk = clk, + .avmMini_pc = FF(pc++), + .avmMini_internal_return_ptr = FF(internal_return_ptr), + .avmMini_sel_op_add = FF(1), + .avmMini_in_tag = FF(static_cast(in_tag)), + .avmMini_tag_err = FF(static_cast(!tag_match)), + .avmMini_ia = tag_match ? a : FF(0), + .avmMini_ib = tag_match ? b : FF(0), + .avmMini_ic = tag_match ? c : FF(0), + .avmMini_mem_op_a = FF(1), + .avmMini_mem_op_b = FF(1), + .avmMini_mem_op_c = FF(1), + .avmMini_rwc = FF(1), + .avmMini_mem_idx_a = FF(a_offset), + .avmMini_mem_idx_b = FF(b_offset), + .avmMini_mem_idx_c = FF(dst_offset), + }); +}; + +/** TODO: Implement for non finite field types + * @brief Subtraction with direct memory access. + * + * @param a_offset An index in memory pointing to the first operand of the subtraction. + * @param b_offset An index in memory pointing to the second operand of the subtraction. + * @param dst_offset An index in memory pointing to the output of the subtraction. + * @param in_tag The instruction memory tag of the operands. + */ +void AvmMiniTraceBuilder::sub(uint32_t a_offset, uint32_t b_offset, uint32_t dst_offset, AvmMemoryTag in_tag) +{ + auto clk = static_cast(main_trace.size()); + + // Reading from memory and loading into ia resp. ib. + auto read_a = mem_trace_builder.read_and_load_from_memory(clk, IntermRegister::ia, a_offset, in_tag); + auto read_b = mem_trace_builder.read_and_load_from_memory(clk, IntermRegister::ib, b_offset, in_tag); + bool tag_match = read_a.tag_match && read_b.tag_match; + + // a - b = c + FF a = read_a.val; + FF b = read_b.val; + FF c = a - b; + + // Write into memory value c from intermediate register ic. + mem_trace_builder.write_into_memory(clk, IntermRegister::ic, dst_offset, c, in_tag); + + main_trace.push_back(Row{ + .avmMini_clk = clk, + .avmMini_pc = FF(pc++), + .avmMini_internal_return_ptr = FF(internal_return_ptr), + .avmMini_sel_op_sub = FF(1), + .avmMini_in_tag = FF(static_cast(in_tag)), + .avmMini_tag_err = FF(static_cast(!tag_match)), + .avmMini_ia = tag_match ? a : FF(0), + .avmMini_ib = tag_match ? b : FF(0), + .avmMini_ic = tag_match ? c : FF(0), + .avmMini_mem_op_a = FF(1), + .avmMini_mem_op_b = FF(1), + .avmMini_mem_op_c = FF(1), + .avmMini_rwc = FF(1), + .avmMini_mem_idx_a = FF(a_offset), + .avmMini_mem_idx_b = FF(b_offset), + .avmMini_mem_idx_c = FF(dst_offset), + }); +}; + +/** TODO: Implement for non finite field types + * @brief Multiplication with direct memory access. + * + * @param a_offset An index in memory pointing to the first operand of the multiplication. + * @param b_offset An index in memory pointing to the second operand of the multiplication. + * @param dst_offset An index in memory pointing to the output of the multiplication. + * @param in_tag The instruction memory tag of the operands. + */ +void AvmMiniTraceBuilder::mul(uint32_t a_offset, uint32_t b_offset, uint32_t dst_offset, AvmMemoryTag in_tag) +{ + auto clk = static_cast(main_trace.size()); + + // Reading from memory and loading into ia resp. ib. + auto read_a = mem_trace_builder.read_and_load_from_memory(clk, IntermRegister::ia, a_offset, in_tag); + auto read_b = mem_trace_builder.read_and_load_from_memory(clk, IntermRegister::ib, b_offset, in_tag); + bool tag_match = read_a.tag_match && read_b.tag_match; + + // a * b = c + FF a = read_a.val; + FF b = read_b.val; + FF c = a * b; + + // Write into memory value c from intermediate register ic. + mem_trace_builder.write_into_memory(clk, IntermRegister::ic, dst_offset, c, in_tag); + + main_trace.push_back(Row{ + .avmMini_clk = clk, + .avmMini_pc = FF(pc++), + .avmMini_internal_return_ptr = FF(internal_return_ptr), + .avmMini_sel_op_mul = FF(1), + .avmMini_in_tag = FF(static_cast(in_tag)), + .avmMini_tag_err = FF(static_cast(!tag_match)), + .avmMini_ia = tag_match ? a : FF(0), + .avmMini_ib = tag_match ? b : FF(0), + .avmMini_ic = tag_match ? c : FF(0), + .avmMini_mem_op_a = FF(1), + .avmMini_mem_op_b = FF(1), + .avmMini_mem_op_c = FF(1), + .avmMini_rwc = FF(1), + .avmMini_mem_idx_a = FF(a_offset), + .avmMini_mem_idx_b = FF(b_offset), + .avmMini_mem_idx_c = FF(dst_offset), + }); +} + +/** TODO: Implement for non finite field types + * @brief Division with direct memory access. + * + * @param a_offset An index in memory pointing to the first operand of the division. + * @param b_offset An index in memory pointing to the second operand of the division. + * @param dst_offset An index in memory pointing to the output of the division. + * @param in_tag The instruction memory tag of the operands. + */ +void AvmMiniTraceBuilder::div(uint32_t a_offset, uint32_t b_offset, uint32_t dst_offset, AvmMemoryTag in_tag) +{ + auto clk = static_cast(main_trace.size()); + + // Reading from memory and loading into ia resp. ib. + auto read_a = mem_trace_builder.read_and_load_from_memory(clk, IntermRegister::ia, a_offset, in_tag); + auto read_b = mem_trace_builder.read_and_load_from_memory(clk, IntermRegister::ib, b_offset, in_tag); + bool tag_match = read_a.tag_match && read_b.tag_match; + + // a * b^(-1) = c + FF a = read_a.val; + FF b = read_b.val; + FF c; + FF inv; + FF error; + + if (!b.is_zero()) { + + inv = b.invert(); + c = a * inv; + error = 0; + } else { + inv = 1; + c = 0; + error = 1; + } + + // Write into memory value c from intermediate register ic. + mem_trace_builder.write_into_memory(clk, IntermRegister::ic, dst_offset, c, in_tag); + + main_trace.push_back(Row{ + .avmMini_clk = clk, + .avmMini_pc = FF(pc++), + .avmMini_internal_return_ptr = FF(internal_return_ptr), + .avmMini_sel_op_div = FF(1), + .avmMini_in_tag = FF(static_cast(in_tag)), + .avmMini_op_err = tag_match ? error : FF(1), + .avmMini_tag_err = FF(static_cast(!tag_match)), + .avmMini_inv = tag_match ? inv : FF(1), + .avmMini_ia = tag_match ? a : FF(0), + .avmMini_ib = tag_match ? b : FF(0), + .avmMini_ic = tag_match ? c : FF(0), + .avmMini_mem_op_a = FF(1), + .avmMini_mem_op_b = FF(1), + .avmMini_mem_op_c = FF(1), + .avmMini_rwc = FF(1), + .avmMini_mem_idx_a = FF(a_offset), + .avmMini_mem_idx_b = FF(b_offset), + .avmMini_mem_idx_c = FF(dst_offset), + }); +} + +/** + * @brief CALLDATACOPY opcode with direct memory access, i.e., + * M[dst_offset:dst_offset+copy_size] = calldata[cd_offset:cd_offset+copy_size] + * Simplified version with exclusively memory store operations and + * values from M_calldata passed by an array and loaded into + * intermediate registers. + * Assume that caller passes call_data_mem which is large enough so that + * no out-of-bound memory issues occur. + * TODO: Implement the indirect memory version (maybe not required) + * TODO: taking care of intermediate register values consistency and propagating their + * values to the next row when not overwritten. + * + * @param cd_offset The starting index of the region in calldata to be copied. + * @param copy_size The number of finite field elements to be copied into memory. + * @param dst_offset The starting index of memory where calldata will be copied to. + * @param call_data_mem The vector containing calldata. + */ +void AvmMiniTraceBuilder::call_data_copy(uint32_t cd_offset, + uint32_t copy_size, + uint32_t dst_offset, + std::vector const& call_data_mem) +{ + // We parallelize storing memory operations in chunk of 3, i.e., 1 per intermediate register. + // The variable pos is an index pointing to the first storing operation (pertaining to intermediate + // register Ia) relative to cd_offset: + // cd_offset + pos: Ia memory store operation + // cd_offset + pos + 1: Ib memory store operation + // cd_offset + pos + 2: Ic memory store operation + + uint32_t pos = 0; + + while (pos < copy_size) { + FF ib(0); + FF ic(0); + uint32_t mem_op_b(0); + uint32_t mem_op_c(0); + uint32_t mem_idx_b(0); + uint32_t mem_idx_c(0); + uint32_t rwb(0); + uint32_t rwc(0); + auto clk = static_cast(main_trace.size()); + + FF ia = call_data_mem.at(cd_offset + pos); + uint32_t mem_op_a(1); + uint32_t mem_idx_a = dst_offset + pos; + uint32_t rwa = 1; + + // Storing from Ia + mem_trace_builder.write_into_memory(clk, IntermRegister::ia, mem_idx_a, ia, AvmMemoryTag::ff); + + if (copy_size - pos > 1) { + ib = call_data_mem.at(cd_offset + pos + 1); + mem_op_b = 1; + mem_idx_b = dst_offset + pos + 1; + rwb = 1; + + // Storing from Ib + mem_trace_builder.write_into_memory(clk, IntermRegister::ib, mem_idx_b, ib, AvmMemoryTag::ff); + } + + if (copy_size - pos > 2) { + ic = call_data_mem.at(cd_offset + pos + 2); + mem_op_c = 1; + mem_idx_c = dst_offset + pos + 2; + rwc = 1; + + // Storing from Ic + mem_trace_builder.write_into_memory(clk, IntermRegister::ic, mem_idx_c, ic, AvmMemoryTag::ff); + } + + main_trace.push_back(Row{ + .avmMini_clk = clk, + .avmMini_pc = FF(pc++), + .avmMini_internal_return_ptr = FF(internal_return_ptr), + .avmMini_in_tag = FF(static_cast(AvmMemoryTag::ff)), + .avmMini_ia = ia, + .avmMini_ib = ib, + .avmMini_ic = ic, + .avmMini_mem_op_a = FF(mem_op_a), + .avmMini_mem_op_b = FF(mem_op_b), + .avmMini_mem_op_c = FF(mem_op_c), + .avmMini_rwa = FF(rwa), + .avmMini_rwb = FF(rwb), + .avmMini_rwc = FF(rwc), + .avmMini_mem_idx_a = FF(mem_idx_a), + .avmMini_mem_idx_b = FF(mem_idx_b), + .avmMini_mem_idx_c = FF(mem_idx_c), + }); + + if (copy_size - pos > 2) { // Guard to prevent overflow if copy_size is close to uint32_t maximum value. + pos += 3; + } else { + pos = copy_size; + } + } +} + +/** + * @brief RETURN opcode with direct memory access, i.e., + * return(M[ret_offset:ret_offset+ret_size]) + * Simplified version with exclusively memory load operations into + * intermediate registers and then values are copied to the returned vector. + * TODO: Implement the indirect memory version (maybe not required) + * TODO: taking care of flagging this row as the last one? Special STOP flag? + * + * @param ret_offset The starting index of the memory region to be returned. + * @param ret_size The number of elements to be returned. + * @return The returned memory region as a std::vector. + */ + +std::vector AvmMiniTraceBuilder::return_op(uint32_t ret_offset, uint32_t ret_size) +{ + // We parallelize loading memory operations in chunk of 3, i.e., 1 per intermediate register. + // The variable pos is an index pointing to the first storing operation (pertaining to intermediate + // register Ia) relative to ret_offset: + // ret_offset + pos: Ia memory load operation + // ret_offset + pos + 1: Ib memory load operation + // ret_offset + pos + 2: Ic memory load operation + + uint32_t pos = 0; + std::vector returnMem; + + while (pos < ret_size) { + FF ib(0); + FF ic(0); + uint32_t mem_op_b(0); + uint32_t mem_op_c(0); + uint32_t mem_idx_b(0); + uint32_t mem_idx_c(0); + auto clk = static_cast(main_trace.size()); + + uint32_t mem_op_a(1); + uint32_t mem_idx_a = ret_offset + pos; + + // Reading and loading to Ia + auto read_a = mem_trace_builder.read_and_load_from_memory(clk, IntermRegister::ia, mem_idx_a, AvmMemoryTag::ff); + FF ia = read_a.val; + returnMem.push_back(ia); + + if (ret_size - pos > 1) { + mem_op_b = 1; + mem_idx_b = ret_offset + pos + 1; + + // Reading and loading to Ib + auto read_b = + mem_trace_builder.read_and_load_from_memory(clk, IntermRegister::ib, mem_idx_b, AvmMemoryTag::ff); + FF ib = read_b.val; + returnMem.push_back(ib); + } + + if (ret_size - pos > 2) { + mem_op_c = 1; + mem_idx_c = ret_offset + pos + 2; + + // Reading and loading to Ic + auto read_c = + mem_trace_builder.read_and_load_from_memory(clk, IntermRegister::ic, mem_idx_c, AvmMemoryTag::ff); + FF ic = read_c.val; + returnMem.push_back(ic); + } + + main_trace.push_back(Row{ + .avmMini_clk = clk, + .avmMini_pc = FF(pc), + .avmMini_internal_return_ptr = FF(internal_return_ptr), + .avmMini_sel_halt = FF(1), + .avmMini_in_tag = FF(static_cast(AvmMemoryTag::ff)), + .avmMini_ia = ia, + .avmMini_ib = ib, + .avmMini_ic = ic, + .avmMini_mem_op_a = FF(mem_op_a), + .avmMini_mem_op_b = FF(mem_op_b), + .avmMini_mem_op_c = FF(mem_op_c), + .avmMini_mem_idx_a = FF(mem_idx_a), + .avmMini_mem_idx_b = FF(mem_idx_b), + .avmMini_mem_idx_c = FF(mem_idx_c), + }); + + if (ret_size - pos > 2) { // Guard to prevent overflow if ret_size is close to uint32_t maximum value. + pos += 3; + } else { + pos = ret_size; + } + } + return returnMem; +} + +/** + * @brief HALT opcode + * This opcode effectively stops program execution, and is used in the relation that + * ensures the program counter increments on each opcode. + * i.e.ythe program counter should freeze and the halt flag is set to 1. + */ +void AvmMiniTraceBuilder::halt() +{ + auto clk = main_trace.size(); + + main_trace.push_back(Row{ + .avmMini_clk = clk, + .avmMini_pc = FF(pc), + .avmMini_internal_return_ptr = FF(internal_return_ptr), + .avmMini_sel_halt = FF(1), + }); +} + +/** + * @brief JUMP OPCODE + * Jumps to a new `jmp_dest` + * This function must: + * - Set the next program counter to the provided `jmp_dest`. + * + * @param jmp_dest - The destination to jump to + */ +void AvmMiniTraceBuilder::jump(uint32_t jmp_dest) +{ + auto clk = main_trace.size(); + + main_trace.push_back(Row{ + .avmMini_clk = clk, + .avmMini_pc = FF(pc), + .avmMini_internal_return_ptr = FF(internal_return_ptr), + .avmMini_sel_jump = FF(1), + .avmMini_ia = FF(jmp_dest), + }); + + // Adjust parameters for the next row + pc = jmp_dest; +} + +/** + * @brief INTERNAL_CALL OPCODE + * This opcode effectively jumps to a new `jmp_dest` and stores the return program counter + * (current program counter + 1) onto a call stack. + * This function must: + * - Set the next program counter to the provided `jmp_dest`. + * - Store the current `pc` + 1 onto the call stack (emulated in memory) + * - Increment the return stack pointer (a pointer to where the call stack is in memory) + * + * Note: We use intermediate register to perform memory storage operations. + * + * @param jmp_dest - The destination to jump to + */ +void AvmMiniTraceBuilder::internal_call(uint32_t jmp_dest) +{ + auto clk = static_cast(main_trace.size()); + + // We store the next instruction as the return location + uint32_t stored_pc = pc + 1; + internal_call_stack.push(stored_pc); + + // Add the return location to the memory trace + mem_trace_builder.write_into_memory(clk, IntermRegister::ib, internal_return_ptr, FF(stored_pc), AvmMemoryTag::ff); + + main_trace.push_back(Row{ + .avmMini_clk = clk, + .avmMini_pc = FF(pc), + .avmMini_internal_return_ptr = FF(internal_return_ptr), + .avmMini_sel_internal_call = FF(1), + .avmMini_ia = FF(jmp_dest), + .avmMini_ib = stored_pc, + .avmMini_mem_op_b = FF(1), + .avmMini_rwb = FF(1), + .avmMini_mem_idx_b = FF(internal_return_ptr), + }); + + // Adjust parameters for the next row + pc = jmp_dest; + internal_return_ptr++; +} + +/** + * @brief INTERNAL_RETURN OPCODE + * The opcode returns from an internal call. + * This function must: + * - Read the return location from the internal_return_ptr + * - Set the next program counter to the return location + * - Decrement the return stack pointer + * + * TODO(https://github.com/AztecProtocol/aztec-packages/issues/3740): This function MUST come after a call instruction. + */ +void AvmMiniTraceBuilder::internal_return() +{ + auto clk = static_cast(main_trace.size()); + + // Internal return pointer is decremented + // We want to load the value pointed by the internal pointer + auto read_a = + mem_trace_builder.read_and_load_from_memory(clk, IntermRegister::ia, internal_return_ptr - 1, AvmMemoryTag::ff); + + main_trace.push_back(Row{ + .avmMini_clk = clk, + .avmMini_pc = pc, + .avmMini_internal_return_ptr = FF(internal_return_ptr), + .avmMini_sel_internal_return = FF(1), + .avmMini_ia = read_a.val, + .avmMini_mem_op_a = FF(1), + .avmMini_rwa = FF(0), + .avmMini_mem_idx_a = FF(internal_return_ptr - 1), + }); + + // We want the next row to be the one pointed by jmp_dest + // The next pc should be from the top of the internal call stack + 1 + pc = internal_call_stack.top(); + internal_call_stack.pop(); + internal_return_ptr--; +} + +/** + * @brief Finalisation of the memory trace and incorporating it to the main trace. + * In particular, sorting the memory trace, setting .m_lastAccess and + * adding shifted values (first row). The main trace is moved at the end of + * this call. + * + * @return The main trace + */ +std::vector AvmMiniTraceBuilder::finalize() +{ + auto mem_trace = mem_trace_builder.finalize(); + size_t mem_trace_size = mem_trace.size(); + size_t main_trace_size = main_trace.size(); + + // TODO: We will have to handle this through error handling and not an assertion + // Smaller than N because we have to add an extra initial row to support shifted + // elements + assert(mem_trace_size < AVM_TRACE_SIZE); + assert(main_trace_size < AVM_TRACE_SIZE); + + // Fill the rest with zeros. + size_t zero_rows_num = AVM_TRACE_SIZE - main_trace_size - 1; + while (zero_rows_num-- > 0) { + main_trace.push_back(Row{}); + } + + main_trace.at(main_trace_size - 1).avmMini_last = FF(1); + + for (size_t i = 0; i < mem_trace_size; i++) { + auto const& src = mem_trace.at(i); + auto& dest = main_trace.at(i); + + dest.memTrace_m_clk = FF(src.m_clk); + dest.memTrace_m_sub_clk = FF(src.m_sub_clk); + dest.memTrace_m_addr = FF(src.m_addr); + dest.memTrace_m_val = src.m_val; + dest.memTrace_m_rw = FF(static_cast(src.m_rw)); + dest.memTrace_m_in_tag = FF(static_cast(src.m_in_tag)); + dest.memTrace_m_tag = FF(static_cast(src.m_tag)); + dest.memTrace_m_tag_err = FF(static_cast(src.m_tag_err)); + dest.memTrace_m_one_min_inv = src.m_one_min_inv; + + if (i + 1 < mem_trace_size) { + auto const& next = mem_trace.at(i + 1); + dest.memTrace_m_lastAccess = FF(static_cast(src.m_addr != next.m_addr)); + } else { + dest.memTrace_m_lastAccess = FF(1); + dest.memTrace_m_last = FF(1); + } + } + + // Adding extra row for the shifted values at the top of the execution trace. + Row first_row = Row{ .avmMini_first = FF(1), .memTrace_m_lastAccess = FF(1) }; + main_trace.insert(main_trace.begin(), first_row); + + auto trace = std::move(main_trace); + reset(); + + return trace; +} + +} // namespace avm_trace \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_trace.hpp b/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_trace.hpp new file mode 100644 index 00000000000..ef8e51b65bf --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_trace.hpp @@ -0,0 +1,71 @@ +#pragma once + +#include + +#include "AvmMini_common.hpp" +#include "AvmMini_mem_trace.hpp" +#include "barretenberg/common/throw_or_abort.hpp" + +#include "barretenberg/relations/generated/AvmMini/avm_mini.hpp" + +namespace avm_trace { + +// 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. +// At the end of circuit building, mainTrace can be moved to AvmMiniCircuitBuilder by calling +// AvmMiniCircuitBuilder::set_trace(rows). +class AvmMiniTraceBuilder { + + public: + static const size_t CALLSTACK_OFFSET = 896; // TODO(md): Temporary reserved area 896 - 1024 + + AvmMiniTraceBuilder(); + + std::vector finalize(); + void reset(); + + // Addition with direct memory access. + void add(uint32_t a_offset, uint32_t b_offset, uint32_t dst_offset, AvmMemoryTag in_tag); + + // Subtraction with direct memory access. + void sub(uint32_t a_offset, uint32_t b_offset, uint32_t dst_offset, AvmMemoryTag in_tag); + + // Multiplication with direct memory access. + void mul(uint32_t a_offset, uint32_t b_offset, uint32_t dst_offset, AvmMemoryTag in_tag); + + // Division with direct memory access. + void div(uint32_t a_offset, uint32_t b_offset, uint32_t dst_offset, AvmMemoryTag in_tag); + + // Jump to a given program counter. + void jump(uint32_t jmp_dest); + + // Jump to a given program counter; storing the return location on a call stack. + // TODO(md): this program counter MUST be an operand to the OPCODE. + void internal_call(uint32_t jmp_dest); + + // Return from a jump. + void internal_return(); + + // Halt -> stop program execution. + void halt(); + + // CALLDATACOPY opcode with direct memory access, i.e., + // M[dst_offset:dst_offset+copy_size] = calldata[cd_offset:cd_offset+copy_size] + void call_data_copy(uint32_t cd_offset, + uint32_t copy_size, + uint32_t dst_offset, + std::vector const& call_data_mem); + + // RETURN opcode with direct memory access, i.e., + // return(M[ret_offset:ret_offset+ret_size]) + std::vector return_op(uint32_t ret_offset, uint32_t ret_size); + + private: + std::vector main_trace; + AvmMiniMemTraceBuilder mem_trace_builder; + + uint32_t pc = 0; + uint32_t internal_return_ptr = CALLSTACK_OFFSET; + std::stack internal_call_stack = {}; +}; +} // namespace avm_trace diff --git a/barretenberg/cpp/src/barretenberg/vm/tests/AvmMini_arithmetic.test.cpp b/barretenberg/cpp/src/barretenberg/vm/tests/AvmMini_arithmetic.test.cpp index a75e798292c..c8f61b2dc59 100644 --- a/barretenberg/cpp/src/barretenberg/vm/tests/AvmMini_arithmetic.test.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/tests/AvmMini_arithmetic.test.cpp @@ -1,9 +1,4 @@ -#include "barretenberg/ecc/curves/bn254/fr.hpp" -#include "barretenberg/flavor/generated/AvmMini_flavor.hpp" -#include "barretenberg/numeric/uint256/uint256.hpp" -#include "barretenberg/proof_system/circuit_builder/AvmMini_helper.hpp" -#include "barretenberg/proof_system/circuit_builder/AvmMini_trace.hpp" -#include "barretenberg/sumcheck/sumcheck_round.hpp" +#include "barretenberg/vm/avm_trace/AvmMini_helper.hpp" #include "barretenberg/vm/generated/AvmMini_composer.hpp" #include "barretenberg/vm/generated/AvmMini_prover.hpp" #include "barretenberg/vm/generated/AvmMini_verifier.hpp" @@ -15,9 +10,8 @@ #include #include -using namespace proof_system; - namespace tests_avm { +using namespace avm_trace; class AvmMiniArithmeticTests : public ::testing::Test { public: @@ -62,11 +56,11 @@ class AvmMiniArithmeticNegativeTests : public AvmMiniArithmeticTests {}; TEST_F(AvmMiniArithmeticTests, additionFF) { // trace_builder - trace_builder.callDataCopy(0, 3, 0, std::vector{ 37, 4, 11 }); + trace_builder.call_data_copy(0, 3, 0, std::vector{ 37, 4, 11 }); // Memory layout: [37,4,11,0,0,0,....] trace_builder.add(0, 1, 4, AvmMemoryTag::ff); // [37,4,11,0,41,0,....] - trace_builder.returnOP(0, 5); + trace_builder.return_op(0, 5); auto trace = trace_builder.finalize(); // Find the first row enabling the addition selector @@ -79,17 +73,17 @@ TEST_F(AvmMiniArithmeticTests, additionFF) EXPECT_EQ(row->avmMini_mem_op_c, FF(1)); EXPECT_EQ(row->avmMini_rwc, FF(1)); - validateTraceProof(std::move(trace)); + validate_trace_proof(std::move(trace)); } // Test on basic subtraction over finite field type. TEST_F(AvmMiniArithmeticTests, subtractionFF) { - trace_builder.callDataCopy(0, 3, 0, std::vector{ 8, 4, 17 }); + trace_builder.call_data_copy(0, 3, 0, std::vector{ 8, 4, 17 }); // Memory layout: [8,4,17,0,0,0,....] trace_builder.sub(2, 0, 1, AvmMemoryTag::ff); // [8,9,17,0,0,0....] - trace_builder.returnOP(0, 3); + trace_builder.return_op(0, 3); auto trace = trace_builder.finalize(); // Find the first row enabling the subtraction selector @@ -102,17 +96,17 @@ TEST_F(AvmMiniArithmeticTests, subtractionFF) EXPECT_EQ(row->avmMini_mem_op_c, FF(1)); EXPECT_EQ(row->avmMini_rwc, FF(1)); - validateTraceProof(std::move(trace)); + validate_trace_proof(std::move(trace)); } // Test on basic multiplication over finite field type. TEST_F(AvmMiniArithmeticTests, multiplicationFF) { - trace_builder.callDataCopy(0, 3, 0, std::vector{ 5, 0, 20 }); + trace_builder.call_data_copy(0, 3, 0, std::vector{ 5, 0, 20 }); // Memory layout: [5,0,20,0,0,0,....] trace_builder.mul(2, 0, 1, AvmMemoryTag::ff); // [5,100,20,0,0,0....] - trace_builder.returnOP(0, 3); + trace_builder.return_op(0, 3); auto trace = trace_builder.finalize(); // Find the first row enabling the multiplication selector @@ -125,17 +119,17 @@ TEST_F(AvmMiniArithmeticTests, multiplicationFF) EXPECT_EQ(row->avmMini_mem_op_c, FF(1)); EXPECT_EQ(row->avmMini_rwc, FF(1)); - validateTraceProof(std::move(trace)); + validate_trace_proof(std::move(trace)); } // Test on multiplication by zero over finite field type. TEST_F(AvmMiniArithmeticTests, multiplicationByZeroFF) { - trace_builder.callDataCopy(0, 1, 0, std::vector{ 127 }); + trace_builder.call_data_copy(0, 1, 0, std::vector{ 127 }); // Memory layout: [127,0,0,0,0,0,....] trace_builder.mul(0, 1, 2, AvmMemoryTag::ff); // [127,0,0,0,0,0....] - trace_builder.returnOP(0, 3); + trace_builder.return_op(0, 3); auto trace = trace_builder.finalize(); // Find the first row enabling the multiplication selector @@ -148,17 +142,17 @@ TEST_F(AvmMiniArithmeticTests, multiplicationByZeroFF) EXPECT_EQ(row->avmMini_mem_op_c, FF(1)); EXPECT_EQ(row->avmMini_rwc, FF(1)); - validateTraceProof(std::move(trace)); + validate_trace_proof(std::move(trace)); } // Test on basic division over finite field type. TEST_F(AvmMiniArithmeticTests, divisionFF) { - trace_builder.callDataCopy(0, 2, 0, std::vector{ 15, 315 }); + trace_builder.call_data_copy(0, 2, 0, std::vector{ 15, 315 }); // Memory layout: [15,315,0,0,0,0,....] trace_builder.div(1, 0, 2, AvmMemoryTag::ff); // [15,315,21,0,0,0....] - trace_builder.returnOP(0, 3); + trace_builder.return_op(0, 3); auto trace = trace_builder.finalize(); // Find the first row enabling the division selector @@ -171,17 +165,17 @@ TEST_F(AvmMiniArithmeticTests, divisionFF) EXPECT_EQ(row->avmMini_mem_op_c, FF(1)); EXPECT_EQ(row->avmMini_rwc, FF(1)); - validateTraceProof(std::move(trace)); + validate_trace_proof(std::move(trace)); } // Test on division with zero numerator over finite field type. TEST_F(AvmMiniArithmeticTests, divisionNumeratorZeroFF) { - trace_builder.callDataCopy(0, 1, 0, std::vector{ 15 }); + trace_builder.call_data_copy(0, 1, 0, std::vector{ 15 }); // Memory layout: [15,0,0,0,0,0,....] trace_builder.div(1, 0, 0, AvmMemoryTag::ff); // [0,0,0,0,0,0....] - trace_builder.returnOP(0, 3); + trace_builder.return_op(0, 3); auto trace = trace_builder.finalize(); // Find the first row enabling the division selector @@ -194,14 +188,14 @@ TEST_F(AvmMiniArithmeticTests, divisionNumeratorZeroFF) EXPECT_EQ(row->avmMini_mem_op_c, FF(1)); EXPECT_EQ(row->avmMini_rwc, FF(1)); - validateTraceProof(std::move(trace)); + validate_trace_proof(std::move(trace)); } // Test on division by zero over finite field type. // We check that the operator error flag is raised. TEST_F(AvmMiniArithmeticTests, divisionByZeroErrorFF) { - trace_builder.callDataCopy(0, 1, 0, std::vector{ 15 }); + trace_builder.call_data_copy(0, 1, 0, std::vector{ 15 }); // Memory layout: [15,0,0,0,0,0,....] trace_builder.div(0, 1, 2, AvmMemoryTag::ff); // [15,0,0,0,0,0....] @@ -219,7 +213,7 @@ TEST_F(AvmMiniArithmeticTests, divisionByZeroErrorFF) EXPECT_EQ(row->avmMini_rwc, FF(1)); EXPECT_EQ(row->avmMini_op_err, FF(1)); - validateTraceProof(std::move(trace)); + validate_trace_proof(std::move(trace)); } // Test on division of zero by zero over finite field type. @@ -242,7 +236,7 @@ TEST_F(AvmMiniArithmeticTests, divisionZeroByZeroErrorFF) EXPECT_EQ(row->avmMini_rwc, FF(1)); EXPECT_EQ(row->avmMini_op_err, FF(1)); - validateTraceProof(std::move(trace)); + validate_trace_proof(std::move(trace)); } // Testing an execution of the different arithmetic opcodes over finite field @@ -251,7 +245,7 @@ TEST_F(AvmMiniArithmeticTests, divisionZeroByZeroErrorFF) // No check on the evaluation is performed here. TEST_F(AvmMiniArithmeticTests, arithmeticFFWithError) { - trace_builder.callDataCopy(0, 3, 2, std::vector{ 45, 23, 12 }); + trace_builder.call_data_copy(0, 3, 2, std::vector{ 45, 23, 12 }); // Memory layout: [0,0,45,23,12,0,0,0,....] trace_builder.add(2, 3, 4, AvmMemoryTag::ff); // [0,0,45,23,68,0,0,0,....] @@ -267,7 +261,7 @@ TEST_F(AvmMiniArithmeticTests, arithmeticFFWithError) trace_builder.halt(); auto trace = trace_builder.finalize(); - validateTraceProof(std::move(trace)); + validate_trace_proof(std::move(trace)); } /****************************************************************************** @@ -294,68 +288,68 @@ TEST_F(AvmMiniArithmeticTests, arithmeticFFWithError) // Test on basic incorrect addition over finite field type. TEST_F(AvmMiniArithmeticNegativeTests, additionFF) { - trace_builder.callDataCopy(0, 3, 0, std::vector{ 37, 4, 11 }); + trace_builder.call_data_copy(0, 3, 0, std::vector{ 37, 4, 11 }); // Memory layout: [37,4,11,0,0,0,....] trace_builder.add(0, 1, 4, AvmMemoryTag::ff); // [37,4,11,0,41,0,....] auto trace = trace_builder.finalize(); - auto selectRow = [](Row r) { return r.avmMini_sel_op_add == FF(1); }; - mutateIcInTrace(trace, std::move(selectRow), FF(40)); + auto select_row = [](Row r) { return r.avmMini_sel_op_add == FF(1); }; + mutate_ic_in_trace(trace, std::move(select_row), FF(40)); - EXPECT_THROW_WITH_MESSAGE(validateTraceProof(std::move(trace)), "SUBOP_ADDITION_FF"); + EXPECT_THROW_WITH_MESSAGE(validate_trace_proof(std::move(trace)), "SUBOP_ADDITION_FF"); } // Test on basic incorrect subtraction over finite field type. TEST_F(AvmMiniArithmeticNegativeTests, subtractionFF) { - trace_builder.callDataCopy(0, 3, 0, std::vector{ 8, 4, 17 }); + trace_builder.call_data_copy(0, 3, 0, std::vector{ 8, 4, 17 }); // Memory layout: [8,4,17,0,0,0,....] trace_builder.sub(2, 0, 1, AvmMemoryTag::ff); // [8,9,17,0,0,0....] auto trace = trace_builder.finalize(); - auto selectRow = [](Row r) { return r.avmMini_sel_op_sub == FF(1); }; - mutateIcInTrace(trace, std::move(selectRow), FF(-9)); + auto select_row = [](Row r) { return r.avmMini_sel_op_sub == FF(1); }; + mutate_ic_in_trace(trace, std::move(select_row), FF(-9)); - EXPECT_THROW_WITH_MESSAGE(validateTraceProof(std::move(trace)), "SUBOP_SUBTRACTION_FF"); + EXPECT_THROW_WITH_MESSAGE(validate_trace_proof(std::move(trace)), "SUBOP_SUBTRACTION_FF"); } // Test on basic incorrect multiplication over finite field type. TEST_F(AvmMiniArithmeticNegativeTests, multiplicationFF) { - trace_builder.callDataCopy(0, 3, 0, std::vector{ 5, 0, 20 }); + trace_builder.call_data_copy(0, 3, 0, std::vector{ 5, 0, 20 }); // Memory layout: [5,0,20,0,0,0,....] trace_builder.mul(2, 0, 1, AvmMemoryTag::ff); // [5,100,20,0,0,0....] auto trace = trace_builder.finalize(); - auto selectRow = [](Row r) { return r.avmMini_sel_op_mul == FF(1); }; - mutateIcInTrace(trace, std::move(selectRow), FF(1000)); + auto select_row = [](Row r) { return r.avmMini_sel_op_mul == FF(1); }; + mutate_ic_in_trace(trace, std::move(select_row), FF(1000)); - EXPECT_THROW_WITH_MESSAGE(validateTraceProof(std::move(trace)), "SUBOP_MULTIPLICATION_FF"); + EXPECT_THROW_WITH_MESSAGE(validate_trace_proof(std::move(trace)), "SUBOP_MULTIPLICATION_FF"); } // Test on basic incorrect division over finite field type. TEST_F(AvmMiniArithmeticNegativeTests, divisionFF) { - trace_builder.callDataCopy(0, 2, 0, std::vector{ 15, 315 }); + trace_builder.call_data_copy(0, 2, 0, std::vector{ 15, 315 }); // Memory layout: [15,315,0,0,0,0,....] trace_builder.div(1, 0, 2, AvmMemoryTag::ff); // [15,315,21,0,0,0....] auto trace = trace_builder.finalize(); - auto selectRow = [](Row r) { return r.avmMini_sel_op_div == FF(1); }; - mutateIcInTrace(trace, std::move(selectRow), FF(0)); + auto select_row = [](Row r) { return r.avmMini_sel_op_div == FF(1); }; + mutate_ic_in_trace(trace, std::move(select_row), FF(0)); - EXPECT_THROW_WITH_MESSAGE(validateTraceProof(std::move(trace)), "SUBOP_DIVISION_FF"); + EXPECT_THROW_WITH_MESSAGE(validate_trace_proof(std::move(trace)), "SUBOP_DIVISION_FF"); } // Test where division is not by zero but an operation error is wrongly raised // in the trace. TEST_F(AvmMiniArithmeticNegativeTests, divisionNoZeroButErrorFF) { - trace_builder.callDataCopy(0, 2, 0, std::vector{ 15, 315 }); + trace_builder.call_data_copy(0, 2, 0, std::vector{ 15, 315 }); // Memory layout: [15,315,0,0,0,0,....] trace_builder.div(1, 0, 2, AvmMemoryTag::ff); // [15,315,21,0,0,0....] @@ -370,17 +364,17 @@ TEST_F(AvmMiniArithmeticNegativeTests, divisionNoZeroButErrorFF) trace[index].avmMini_op_err = FF(1); auto trace2 = trace; - EXPECT_THROW_WITH_MESSAGE(validateTraceProof(std::move(trace)), "SUBOP_DIVISION_ZERO_ERR1"); + EXPECT_THROW_WITH_MESSAGE(validate_trace_proof(std::move(trace)), "SUBOP_DIVISION_ZERO_ERR1"); // Even more malicious, one makes the first relation passes by setting the inverse to zero. trace2[index].avmMini_inv = FF(0); - EXPECT_THROW_WITH_MESSAGE(validateTraceProof(std::move(trace2)), "SUBOP_DIVISION_ZERO_ERR2"); + EXPECT_THROW_WITH_MESSAGE(validate_trace_proof(std::move(trace2)), "SUBOP_DIVISION_ZERO_ERR2"); } // Test with division by zero occurs and no error is raised (remove error flag) TEST_F(AvmMiniArithmeticNegativeTests, divisionByZeroNoErrorFF) { - trace_builder.callDataCopy(0, 1, 0, std::vector{ 15 }); + trace_builder.call_data_copy(0, 1, 0, std::vector{ 15 }); // Memory layout: [15,0,0,0,0,0,....] trace_builder.div(0, 1, 2, AvmMemoryTag::ff); // [15,0,0,0,0,0....] @@ -393,7 +387,7 @@ TEST_F(AvmMiniArithmeticNegativeTests, divisionByZeroNoErrorFF) // Remove the operator error flag row->avmMini_op_err = FF(0); - EXPECT_THROW_WITH_MESSAGE(validateTraceProof(std::move(trace)), "SUBOP_DIVISION_FF"); + EXPECT_THROW_WITH_MESSAGE(validate_trace_proof(std::move(trace)), "SUBOP_DIVISION_FF"); } // Test with division of zero by zero occurs and no error is raised (remove error flag) @@ -409,18 +403,18 @@ TEST_F(AvmMiniArithmeticNegativeTests, divisionZeroByZeroNoErrorFF) // Remove the operator error flag row->avmMini_op_err = FF(0); - EXPECT_THROW_WITH_MESSAGE(validateTraceProof(std::move(trace)), "SUBOP_DIVISION_ZERO_ERR1"); + EXPECT_THROW_WITH_MESSAGE(validate_trace_proof(std::move(trace)), "SUBOP_DIVISION_ZERO_ERR1"); } // Test that error flag cannot be raised for a non-relevant operation such as // the addition, subtraction, multiplication. TEST_F(AvmMiniArithmeticNegativeTests, operationWithErrorFlagFF) { - trace_builder.callDataCopy(0, 3, 0, std::vector{ 37, 4, 11 }); + trace_builder.call_data_copy(0, 3, 0, std::vector{ 37, 4, 11 }); // Memory layout: [37,4,11,0,0,0,....] trace_builder.add(0, 1, 4, AvmMemoryTag::ff); // [37,4,11,0,41,0,....] - trace_builder.returnOP(0, 5); + trace_builder.return_op(0, 5); auto trace = trace_builder.finalize(); // Find the first row enabling the addition selector @@ -429,15 +423,15 @@ TEST_F(AvmMiniArithmeticNegativeTests, operationWithErrorFlagFF) // Activate the operator error row->avmMini_op_err = FF(1); - EXPECT_THROW_WITH_MESSAGE(validateTraceProof(std::move(trace)), "SUBOP_ERROR_RELEVANT_OP"); + EXPECT_THROW_WITH_MESSAGE(validate_trace_proof(std::move(trace)), "SUBOP_ERROR_RELEVANT_OP"); trace_builder.reset(); - trace_builder.callDataCopy(0, 3, 0, std::vector{ 8, 4, 17 }); + trace_builder.call_data_copy(0, 3, 0, std::vector{ 8, 4, 17 }); // Memory layout: [8,4,17,0,0,0,....] trace_builder.sub(2, 0, 1, AvmMemoryTag::ff); // [8,9,17,0,0,0....] - trace_builder.returnOP(0, 3); + trace_builder.return_op(0, 3); trace = trace_builder.finalize(); // Find the first row enabling the subtraction selector @@ -446,15 +440,15 @@ TEST_F(AvmMiniArithmeticNegativeTests, operationWithErrorFlagFF) // Activate the operator error row->avmMini_op_err = FF(1); - EXPECT_THROW_WITH_MESSAGE(validateTraceProof(std::move(trace)), "SUBOP_ERROR_RELEVANT_OP"); + EXPECT_THROW_WITH_MESSAGE(validate_trace_proof(std::move(trace)), "SUBOP_ERROR_RELEVANT_OP"); trace_builder.reset(); - trace_builder.callDataCopy(0, 3, 0, std::vector{ 5, 0, 20 }); + trace_builder.call_data_copy(0, 3, 0, std::vector{ 5, 0, 20 }); // Memory layout: [5,0,20,0,0,0,....] trace_builder.mul(2, 0, 1, AvmMemoryTag::ff); // [5,100,20,0,0,0....] - trace_builder.returnOP(0, 3); + trace_builder.return_op(0, 3); trace = trace_builder.finalize(); // Find the first row enabling the multiplication selector @@ -463,7 +457,7 @@ TEST_F(AvmMiniArithmeticNegativeTests, operationWithErrorFlagFF) // Activate the operator error row->avmMini_op_err = FF(1); - EXPECT_THROW_WITH_MESSAGE(validateTraceProof(std::move(trace)), "SUBOP_ERROR_RELEVANT_OP"); + EXPECT_THROW_WITH_MESSAGE(validate_trace_proof(std::move(trace)), "SUBOP_ERROR_RELEVANT_OP"); } } // namespace tests_avm \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/vm/tests/AvmMini_control_flow.test.cpp b/barretenberg/cpp/src/barretenberg/vm/tests/AvmMini_control_flow.test.cpp index c771ad6eaf6..36673853057 100644 --- a/barretenberg/cpp/src/barretenberg/vm/tests/AvmMini_control_flow.test.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/tests/AvmMini_control_flow.test.cpp @@ -1,8 +1,4 @@ -#include "barretenberg/ecc/curves/bn254/fr.hpp" -#include "barretenberg/flavor/generated/AvmMini_flavor.hpp" -#include "barretenberg/numeric/uint256/uint256.hpp" -#include "barretenberg/proof_system/circuit_builder/AvmMini_helper.hpp" -#include "barretenberg/proof_system/circuit_builder/AvmMini_trace.hpp" +#include "barretenberg/vm/avm_trace/AvmMini_helper.hpp" #include "barretenberg/vm/generated/AvmMini_composer.hpp" #include "barretenberg/vm/generated/AvmMini_prover.hpp" #include "barretenberg/vm/generated/AvmMini_verifier.hpp" @@ -14,9 +10,8 @@ #include #include -using namespace proof_system; - namespace tests_avm { +using namespace avm_trace; class AvmMiniControlFlowTests : public ::testing::Test { public: @@ -73,7 +68,7 @@ TEST_F(AvmMiniControlFlowTests, simpleCall) EXPECT_EQ(halt_row->avmMini_pc, FF(CALL_ADDRESS)); EXPECT_EQ(halt_row->avmMini_internal_return_ptr, FF(AvmMiniTraceBuilder::CALLSTACK_OFFSET + 1)); } - validateTraceProof(std::move(trace)); + validate_trace_proof(std::move(trace)); } TEST_F(AvmMiniControlFlowTests, simpleJump) @@ -106,7 +101,7 @@ TEST_F(AvmMiniControlFlowTests, simpleJump) EXPECT_TRUE(halt_row != trace.end()); EXPECT_EQ(halt_row->avmMini_pc, FF(JUMP_ADDRESS)); } - validateTraceProof(std::move(trace)); + validate_trace_proof(std::move(trace)); } TEST_F(AvmMiniControlFlowTests, simpleCallAndReturn) @@ -156,7 +151,7 @@ TEST_F(AvmMiniControlFlowTests, simpleCallAndReturn) EXPECT_EQ(halt_row->avmMini_pc, FF(RETURN_ADDRESS)); } - validateTraceProof(std::move(trace)); + validate_trace_proof(std::move(trace)); } TEST_F(AvmMiniControlFlowTests, multipleCallsAndReturns) @@ -298,6 +293,6 @@ TEST_F(AvmMiniControlFlowTests, multipleCallsAndReturns) EXPECT_TRUE(halt_row != trace.end()); EXPECT_EQ(halt_row->avmMini_pc, FF(1)); - validateTraceProof(std::move(trace)); + validate_trace_proof(std::move(trace)); } } // namespace tests_avm \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/vm/tests/AvmMini_memory.test.cpp b/barretenberg/cpp/src/barretenberg/vm/tests/AvmMini_memory.test.cpp index 893216fb71e..e3798357e15 100644 --- a/barretenberg/cpp/src/barretenberg/vm/tests/AvmMini_memory.test.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/tests/AvmMini_memory.test.cpp @@ -1,9 +1,4 @@ -#include "barretenberg/ecc/curves/bn254/fr.hpp" -#include "barretenberg/flavor/generated/AvmMini_flavor.hpp" -#include "barretenberg/numeric/uint256/uint256.hpp" -#include "barretenberg/proof_system/circuit_builder/AvmMini_helper.hpp" -#include "barretenberg/proof_system/circuit_builder/AvmMini_trace.hpp" -#include "barretenberg/sumcheck/sumcheck_round.hpp" +#include "barretenberg/vm/avm_trace/AvmMini_helper.hpp" #include "barretenberg/vm/generated/AvmMini_composer.hpp" #include "barretenberg/vm/generated/AvmMini_prover.hpp" #include "barretenberg/vm/generated/AvmMini_verifier.hpp" @@ -15,9 +10,8 @@ #include #include -using namespace proof_system; - namespace tests_avm { +using namespace avm_trace; class AvmMiniMemoryTests : public ::testing::Test { public: @@ -49,7 +43,7 @@ class AvmMiniMemoryTests : public ::testing::Test { // The proof must pass and we check that the AVM error is raised. TEST_F(AvmMiniMemoryTests, mismatchedTag) { - trace_builder.callDataCopy(0, 2, 0, std::vector{ 98, 12 }); + trace_builder.call_data_copy(0, 2, 0, std::vector{ 98, 12 }); trace_builder.add(0, 1, 4, AvmMemoryTag::u8); trace_builder.halt(); @@ -69,7 +63,7 @@ TEST_F(AvmMiniMemoryTests, mismatchedTag) // Find the memory trace position corresponding to the add sub-operation of register ia. row = std::ranges::find_if(trace.begin(), trace.end(), [clk](Row r) { - return r.memTrace_m_clk == clk && r.memTrace_m_sub_clk == AvmMiniTraceBuilder::SUB_CLK_LOAD_A; + return r.memTrace_m_clk == clk && r.memTrace_m_sub_clk == AvmMiniMemTraceBuilder::SUB_CLK_LOAD_A; }); EXPECT_TRUE(row != trace.end()); @@ -80,7 +74,7 @@ TEST_F(AvmMiniMemoryTests, mismatchedTag) // Find the memory trace position corresponding to the add sub-operation of register ib. row = std::ranges::find_if(trace.begin(), trace.end(), [clk](Row r) { - return r.memTrace_m_clk == clk && r.memTrace_m_sub_clk == AvmMiniTraceBuilder::SUB_CLK_LOAD_B; + return r.memTrace_m_clk == clk && r.memTrace_m_sub_clk == AvmMiniMemTraceBuilder::SUB_CLK_LOAD_B; }); EXPECT_TRUE(row != trace.end()); @@ -89,14 +83,14 @@ TEST_F(AvmMiniMemoryTests, mismatchedTag) EXPECT_EQ(row->memTrace_m_in_tag, FF(static_cast(AvmMemoryTag::u8))); EXPECT_EQ(row->memTrace_m_tag, FF(static_cast(AvmMemoryTag::ff))); - validateTraceProof(std::move(trace)); + validate_trace_proof(std::move(trace)); } // Testing violation that m_lastAccess is a delimiter for two different addresses // in the memory trace TEST_F(AvmMiniMemoryTests, mLastAccessViolation) { - trace_builder.callDataCopy(0, 2, 0, std::vector{ 4, 9 }); + trace_builder.call_data_copy(0, 2, 0, std::vector{ 4, 9 }); // Memory layout: [4,9,0,0,0,0,....] trace_builder.sub(1, 0, 2, AvmMemoryTag::u8); // [4,9,5,0,0,0.....] @@ -112,25 +106,25 @@ TEST_F(AvmMiniMemoryTests, mLastAccessViolation) // Find the row for memory trace with last memory entry for address 1 (read for subtraction) row = std::ranges::find_if(trace.begin(), trace.end(), [clk](Row r) { return r.memTrace_m_clk == clk && r.memTrace_m_addr == FF(1) && - r.memTrace_m_sub_clk == AvmMiniTraceBuilder::SUB_CLK_LOAD_A; + r.memTrace_m_sub_clk == AvmMiniMemTraceBuilder::SUB_CLK_LOAD_A; }); EXPECT_TRUE(row != trace.end()); row->memTrace_m_lastAccess = FF(0); - EXPECT_THROW_WITH_MESSAGE(validateTraceProof(std::move(trace)), "MEM_LAST_ACCESS_DELIMITER"); + EXPECT_THROW_WITH_MESSAGE(validate_trace_proof(std::move(trace)), "MEM_LAST_ACCESS_DELIMITER"); } // Testing violation that a memory read operation must read the same value which was // written into memory TEST_F(AvmMiniMemoryTests, readWriteConsistencyValViolation) { - trace_builder.callDataCopy(0, 2, 0, std::vector{ 4, 9 }); + trace_builder.call_data_copy(0, 2, 0, std::vector{ 4, 9 }); // Memory layout: [4,9,0,0,0,0,....] trace_builder.mul(1, 0, 2, AvmMemoryTag::u8); // [4,9,36,0,0,0.....] - trace_builder.returnOP(2, 1); // Return single memory word at position 2 (36) + trace_builder.return_op(2, 1); // Return single memory word at position 2 (36) auto trace = trace_builder.finalize(); // Find the row with multiplication operation @@ -142,25 +136,25 @@ TEST_F(AvmMiniMemoryTests, readWriteConsistencyValViolation) // Find the row for memory trace with last memory entry for address 2 (read for multiplication) row = std::ranges::find_if(trace.begin(), trace.end(), [clk](Row r) { return r.memTrace_m_clk == clk && r.memTrace_m_addr == FF(2) && - r.memTrace_m_sub_clk == AvmMiniTraceBuilder::SUB_CLK_LOAD_A; + r.memTrace_m_sub_clk == AvmMiniMemTraceBuilder::SUB_CLK_LOAD_A; }); EXPECT_TRUE(row != trace.end()); row->memTrace_m_val = FF(35); - EXPECT_THROW_WITH_MESSAGE(validateTraceProof(std::move(trace)), "MEM_READ_WRITE_VAL_CONSISTENCY"); + EXPECT_THROW_WITH_MESSAGE(validate_trace_proof(std::move(trace)), "MEM_READ_WRITE_VAL_CONSISTENCY"); } // Testing violation that memory read operation must read the same tag which was // written into memory TEST_F(AvmMiniMemoryTests, readWriteConsistencyTagViolation) { - trace_builder.callDataCopy(0, 2, 0, std::vector{ 4, 9 }); + trace_builder.call_data_copy(0, 2, 0, std::vector{ 4, 9 }); // Memory layout: [4,9,0,0,0,0,....] trace_builder.mul(1, 0, 2, AvmMemoryTag::u8); // [4,9,36,0,0,0.....] - trace_builder.returnOP(2, 1); // Return single memory word at position 2 (36) + trace_builder.return_op(2, 1); // Return single memory word at position 2 (36) auto trace = trace_builder.finalize(); // Find the row with multiplication operation @@ -172,32 +166,32 @@ TEST_F(AvmMiniMemoryTests, readWriteConsistencyTagViolation) // Find the row for memory trace with last memory entry for address 2 (read for multiplication) row = std::ranges::find_if(trace.begin(), trace.end(), [clk](Row r) { return r.memTrace_m_clk == clk && r.memTrace_m_addr == FF(2) && - r.memTrace_m_sub_clk == AvmMiniTraceBuilder::SUB_CLK_LOAD_A; + r.memTrace_m_sub_clk == AvmMiniMemTraceBuilder::SUB_CLK_LOAD_A; }); EXPECT_TRUE(row != trace.end()); row->memTrace_m_tag = static_cast(AvmMemoryTag::u16); - EXPECT_THROW_WITH_MESSAGE(validateTraceProof(std::move(trace)), "MEM_READ_WRITE_TAG_CONSISTENCY"); + EXPECT_THROW_WITH_MESSAGE(validate_trace_proof(std::move(trace)), "MEM_READ_WRITE_TAG_CONSISTENCY"); } // Testing violation that a memory read at uninitialized location must have value 0. TEST_F(AvmMiniMemoryTests, readUninitializedMemoryViolation) { - trace_builder.returnOP(1, 1); // Return single memory word at position 1 + trace_builder.return_op(1, 1); // Return single memory word at position 1 auto trace = trace_builder.finalize(); trace[1].memTrace_m_val = 9; - EXPECT_THROW_WITH_MESSAGE(validateTraceProof(std::move(trace)), "MEM_ZERO_INIT"); + EXPECT_THROW_WITH_MESSAGE(validate_trace_proof(std::move(trace)), "MEM_ZERO_INIT"); } // Testing violation that an operation with a mismatched memory tag // must raise a VM error. TEST_F(AvmMiniMemoryTests, mismatchedTagErrorViolation) { - trace_builder.callDataCopy(0, 2, 0, std::vector{ 98, 12 }); + trace_builder.call_data_copy(0, 2, 0, std::vector{ 98, 12 }); trace_builder.sub(0, 1, 4, AvmMemoryTag::u8); trace_builder.halt(); @@ -212,26 +206,26 @@ TEST_F(AvmMiniMemoryTests, mismatchedTagErrorViolation) // Find the memory trace position corresponding to the subtraction sub-operation of register ia. row = std::ranges::find_if(trace.begin(), trace.end(), [clk](Row r) { - return r.memTrace_m_clk == clk && r.memTrace_m_sub_clk == AvmMiniTraceBuilder::SUB_CLK_LOAD_A; + return r.memTrace_m_clk == clk && r.memTrace_m_sub_clk == AvmMiniMemTraceBuilder::SUB_CLK_LOAD_A; }); row->memTrace_m_tag_err = FF(0); auto index = static_cast(row - trace.begin()); auto trace2 = trace; - EXPECT_THROW_WITH_MESSAGE(validateTraceProof(std::move(trace)), "MEM_IN_TAG_CONSISTENCY_1"); + EXPECT_THROW_WITH_MESSAGE(validate_trace_proof(std::move(trace)), "MEM_IN_TAG_CONSISTENCY_1"); // More sophisticated attempt by adapting witness "on_min_inv" to make pass the above constraint trace2[index].memTrace_m_one_min_inv = FF(1); - EXPECT_THROW_WITH_MESSAGE(validateTraceProof(std::move(trace2)), "MEM_IN_TAG_CONSISTENCY_2"); + EXPECT_THROW_WITH_MESSAGE(validate_trace_proof(std::move(trace2)), "MEM_IN_TAG_CONSISTENCY_2"); } // Testing violation that an operation with a consistent memory tag // must not set a VM error. TEST_F(AvmMiniMemoryTests, consistentTagNoErrorViolation) { - trace_builder.callDataCopy(0, 2, 0, std::vector{ 84, 7 }); + trace_builder.call_data_copy(0, 2, 0, std::vector{ 84, 7 }); trace_builder.div(0, 1, 4, AvmMemoryTag::ff); trace_builder.halt(); @@ -246,11 +240,11 @@ TEST_F(AvmMiniMemoryTests, consistentTagNoErrorViolation) // Find the memory trace position corresponding to the div sub-operation of register ia. row = std::ranges::find_if(trace.begin(), trace.end(), [clk](Row r) { - return r.memTrace_m_clk == clk && r.memTrace_m_sub_clk == AvmMiniTraceBuilder::SUB_CLK_LOAD_A; + return r.memTrace_m_clk == clk && r.memTrace_m_sub_clk == AvmMiniMemTraceBuilder::SUB_CLK_LOAD_A; }); row->memTrace_m_tag_err = FF(1); - EXPECT_THROW_WITH_MESSAGE(validateTraceProof(std::move(trace)), "MEM_IN_TAG_CONSISTENCY_1"); + EXPECT_THROW_WITH_MESSAGE(validate_trace_proof(std::move(trace)), "MEM_IN_TAG_CONSISTENCY_1"); } } // namespace tests_avm \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/vm/tests/helpers.test.cpp b/barretenberg/cpp/src/barretenberg/vm/tests/helpers.test.cpp index 9cfb6fe7a17..2a761b69a47 100644 --- a/barretenberg/cpp/src/barretenberg/vm/tests/helpers.test.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/tests/helpers.test.cpp @@ -1,28 +1,26 @@ -#include "barretenberg/vm/tests/helpers.test.hpp" -#include "barretenberg/flavor/generated/AvmMini_flavor.hpp" -#include "barretenberg/proof_system/circuit_builder/AvmMini_helper.hpp" -#include "barretenberg/proof_system/circuit_builder/AvmMini_trace.hpp" +#include "helpers.test.hpp" +#include "barretenberg/vm/avm_trace/AvmMini_helper.hpp" #include "barretenberg/vm/generated/AvmMini_composer.hpp" #include "barretenberg/vm/generated/AvmMini_prover.hpp" #include "barretenberg/vm/generated/AvmMini_verifier.hpp" #include namespace tests_avm { -using namespace proof_system; +using namespace avm_trace; /** * @brief Helper routine proving and verifying a proof based on the supplied trace * * @param trace The execution trace */ -void validateTraceProof(std::vector&& trace) +void validate_trace_proof(std::vector&& trace) { - auto circuit_builder = AvmMiniCircuitBuilder(); + auto circuit_builder = proof_system::AvmMiniCircuitBuilder(); circuit_builder.set_trace(std::move(trace)); EXPECT_TRUE(circuit_builder.check_circuit()); - auto composer = honk::AvmMiniComposer(); + auto composer = proof_system::honk::AvmMiniComposer(); auto prover = composer.create_prover(circuit_builder); auto proof = prover.construct_proof(); @@ -42,7 +40,7 @@ void validateTraceProof(std::vector&& trace) * @param selectRow Lambda serving to select the row in trace * @param newValue The value that will be written in intermediate register Ic at the selected row. */ -void mutateIcInTrace(std::vector& trace, std::function&& selectRow, FF const& newValue) +void mutate_ic_in_trace(std::vector& trace, std::function&& selectRow, FF const& newValue) { // Find the first row matching the criteria defined by selectRow auto row = std::ranges::find_if(trace.begin(), trace.end(), selectRow); @@ -58,12 +56,12 @@ void mutateIcInTrace(std::vector& trace, std::function&& selectR auto const addr = row->avmMini_mem_idx_c; // Find the relevant memory trace entry. - auto memRow = std::ranges::find_if(trace.begin(), trace.end(), [clk, addr](Row r) { + auto mem_row = std::ranges::find_if(trace.begin(), trace.end(), [clk, addr](Row r) { return r.memTrace_m_clk == clk && r.memTrace_m_addr == addr; }); - EXPECT_TRUE(memRow != trace.end()); - memRow->memTrace_m_val = newValue; + EXPECT_TRUE(mem_row != trace.end()); + mem_row->memTrace_m_val = newValue; }; } // namespace tests_avm \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/vm/tests/helpers.test.hpp b/barretenberg/cpp/src/barretenberg/vm/tests/helpers.test.hpp index 36732908f89..145eb171457 100644 --- a/barretenberg/cpp/src/barretenberg/vm/tests/helpers.test.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/tests/helpers.test.hpp @@ -1,6 +1,6 @@ #pragma once -#include "barretenberg/proof_system/circuit_builder/AvmMini_trace.hpp" +#include "barretenberg/vm/avm_trace/AvmMini_trace.hpp" #define EXPECT_THROW_WITH_MESSAGE(code, expectedMessage) \ try { \ @@ -13,7 +13,7 @@ namespace tests_avm { -void validateTraceProof(std::vector&& trace); -void mutateIcInTrace(std::vector& trace, std::function&& selectRow, FF const& newValue); +void validate_trace_proof(std::vector&& trace); +void mutate_ic_in_trace(std::vector& trace, std::function&& selectRow, FF const& newValue); } // namespace tests_avm \ No newline at end of file