diff --git a/barretenberg/cpp/src/barretenberg/relations/databus_lookup_relation.hpp b/barretenberg/cpp/src/barretenberg/relations/databus_lookup_relation.hpp index eb79bb21efeb..d919738a928d 100644 --- a/barretenberg/cpp/src/barretenberg/relations/databus_lookup_relation.hpp +++ b/barretenberg/cpp/src/barretenberg/relations/databus_lookup_relation.hpp @@ -3,166 +3,250 @@ #include #include "barretenberg/common/constexpr_utils.hpp" -#include "barretenberg/honk/proof_system/logderivative_library.hpp" #include "barretenberg/polynomials/polynomial.hpp" #include "barretenberg/polynomials/univariate.hpp" #include "barretenberg/relations/relation_types.hpp" namespace bb { +/** + * @brief Log-derivative lookup argument relation for establishing DataBus reads + * @details Each column of the databus can be thought of as a table from which we can look up values. The log-derivative + * lookup argument seeks to prove lookups from a column by establishing the following sum: + * + * \sum_{i=0}^{n-1} q_{logderiv_lookup}_i * (1 / write_term_i) + read_count_i * (1 / read_term_i) = 0 + * + * where the read and write terms are both of the form value_i + idx_i*\beta + \gamma. This expression is motivated by + * taking the derivative of the log of a more conventional grand product style set equivalence argument (see e.g. + * https://eprint.iacr.org/2022/1530.pdf for details). For the write term, the (idx, value) pair comes from the "table" + * (bus column), and for the read term the (idx, value) pair comes from wires 1 and 2 which should contain a valid entry + * in the table. (Note: the meaning of "read" here is clear: the inputs are an (index, value) pair that we want to read + * from the table. Here "write" refers to data that is present in the "table", i.e. the bus column. There is no gate + * associated with a write, the data is simply populated in the corresponding column and committed to when constructing + * a proof). + * + * In practice, we must rephrase this expression in terms of polynomials, one of which is a polynomial I containing + * (indirectly) the rational functions in the above expression: I_i = 1/[(read_term_i) * (write_term_i)]. This leads to + * two subrelations. The first demonstrates that the inverse polynomial I is correctly formed. The second is the primary + * lookup identity, where the rational functions are replaced by the use of the inverse polynomial I. These two + * subrelations can be expressed as follows: + * + * (1) I_i * (read_term_i) * (write_term_i) - 1 = 0 + * + * (2) \sum_{i=0}^{n-1} [q_{logderiv_lookup} * I_i * write_term_i + read_count_i * I_i * read_term_i] = 0 + * + * Each column of the DataBus requires its own pair of subrelations. The column being read is selected via a unique + * product, i.e. a lookup from bus column j is selected via q_busread * q_j (j = 1,2,...). + * + * Note: that the latter subrelation is "linearly dependent" in the sense that it establishes that a sum across all + * rows of the exectution trace is zero, rather than that some expression holds independently at each row. Accordingly, + * this subrelation is not multiplied by a scaling factor at each accumulation step. + * + */ template class DatabusLookupRelationImpl { public: using FF = FF_; - static constexpr size_t READ_TERMS = 1; - static constexpr size_t WRITE_TERMS = 1; - // 1 + polynomial degree of this relation - static constexpr size_t LENGTH = READ_TERMS + WRITE_TERMS + 3; + static constexpr size_t LENGTH = 5; // 1 + polynomial degree of this relation + static constexpr size_t NUM_BUS_COLUMNS = 2; // calldata, return data - static constexpr std::array SUBRELATION_PARTIAL_LENGTHS{ + // Note: Inverse correctness subrelations are actually LENGTH-1; taking advantage would require additional work + static constexpr std::array SUBRELATION_PARTIAL_LENGTHS{ + LENGTH, // inverse polynomial correctness subrelation + LENGTH, // log-derivative lookup argument subrelation LENGTH, // inverse polynomial correctness subrelation LENGTH // log-derivative lookup argument subrelation }; - // The second subrelation is "linearly dependant" in the sense that it establishes the value of a sum across the + // The lookup subrelations are "linearly dependent" in the sense that they establish the value of a sum across the // entire execution trace rather than a per-row identity. - static constexpr std::array SUBRELATION_LINEARLY_INDEPENDENT = { true, false }; + static constexpr std::array SUBRELATION_LINEARLY_INDEPENDENT = { + true, false, true, false + }; + + // Interface for easy access of databus components by column (bus_idx) + template struct BusData; + + // Specialization for calldata (bus_idx = 0) + template struct BusData { + static auto& values(const AllEntities& in) { return in.calldata; } + static auto& selector(const AllEntities& in) { return in.q_l; } + static auto& inverses(AllEntities& in) { return in.calldata_inverses; } + static auto& inverses(const AllEntities& in) { return in.calldata_inverses; } // const version + static auto& read_counts(const AllEntities& in) { return in.calldata_read_counts; } + }; + + // Specialization for return data (bus_idx = 1) + template struct BusData { + static auto& values(const AllEntities& in) { return in.return_data; } + static auto& selector(const AllEntities& in) { return in.q_r; } + static auto& inverses(AllEntities& in) { return in.return_data_inverses; } + static auto& inverses(const AllEntities& in) { return in.return_data_inverses; } // const version + static auto& read_counts(const AllEntities& in) { return in.return_data_read_counts; } + }; /** - * @brief Determine whether the inverse I needs to be computed at a given row + * @brief Determine whether the inverse I needs to be computed at a given row for a given bus column * @details The value of the inverse polynomial I(X) only needs to be computed when the databus lookup gate is * "active". Otherwise it is set to 0. This method allows for determination of when the inverse should be computed. * * @tparam AllValues * @param row - * @return true - * @return false */ - template static bool operation_exists_at_row(const AllValues& row) + template static bool operation_exists_at_row(const AllValues& row) { - return (row.q_busread == 1 || row.calldata_read_counts > 0); + auto read_selector = get_read_selector(row); + auto read_counts = BusData::read_counts(row); + return (read_selector == 1 || read_counts > 0); } - /** - * @brief Get the lookup inverse polynomial - * - * @tparam AllEntities - * @param in - * @return auto& - */ - template static auto& get_inverse_polynomial(AllEntities& in) { return in.lookup_inverses; } /** * @brief Compute the Accumulator whose values indicate whether the inverse is computed or not * @details This is needed for efficiency since we don't need to compute the inverse unless the log derivative * lookup relation is active at a given row. + * @note read_counts is constructed such that read_count_i <= 1 and is thus treated as boolean. * */ - template + template static Accumulator compute_inverse_exists(const AllEntities& in) { using View = typename Accumulator::View; - // TODO(luke): row_has_read should really be a boolean object thats equal to 1 when counts > 0 and 0 otherwise. - // This current structure will lead to failure if call_data_read_counts > 1. - const auto row_has_write = View(in.q_busread); - const auto row_has_read = View(in.calldata_read_counts); - - return row_has_write + row_has_read - (row_has_write * row_has_read); - return Accumulator(View(in.q_busread) + View(in.calldata_read_counts)); - } - - template - static Accumulator lookup_read_counts(const AllEntities& in) - { - using View = typename Accumulator::View; + const auto is_read_gate = get_read_selector(in); + const auto read_counts = View(BusData::read_counts(in)); - if constexpr (index == 0) { - return Accumulator(View(in.calldata_read_counts)); - } - return Accumulator(1); + return is_read_gate + read_counts - (is_read_gate * read_counts); } /** * @brief Compute scalar for read term in log derivative lookup argument + * @details The selector indicating read from bus column j is given by q_busread * q_j, j = 1,2,3 * */ - template - static Accumulator compute_read_term_predicate([[maybe_unused]] const AllEntities& in) - + template + static Accumulator get_read_selector(const AllEntities& in) { using View = typename Accumulator::View; - if constexpr (read_index == 0) { - return Accumulator(View(in.q_busread)); - } - return Accumulator(1); - } + auto q_busread = View(in.q_busread); + auto column_selector = View(BusData::selector(in)); - /** - * @brief Compute scalar for write term in log derivative lookup argument - * - */ - template - static Accumulator compute_write_term_predicate(const AllEntities& /*unused*/) - { - return Accumulator(1); + return q_busread * column_selector; } /** * @brief Compute write term denominator in log derivative lookup argument * */ - template + template static Accumulator compute_write_term(const AllEntities& in, const Parameters& params) { using View = typename Accumulator::View; using ParameterView = GetParameterView; - static_assert(write_index < WRITE_TERMS); - - const auto& calldata = View(in.calldata); const auto& id = View(in.databus_id); - + const auto& value = View(BusData::values(in)); const auto& gamma = ParameterView(params.gamma); const auto& beta = ParameterView(params.beta); - // Construct b_i + idx_i*\beta + \gamma - if constexpr (write_index == 0) { - return calldata + gamma + id * beta; // degree 1 - } - - return Accumulator(1); + // Construct value_i + idx_i*\beta + \gamma + return value + gamma + id * beta; // degree 1 } /** * @brief Compute read term denominator in log derivative lookup argument + * @note No bus_idx required here since inputs to a read are of the same form regardless the bus column * */ - template + template static Accumulator compute_read_term(const AllEntities& in, const Parameters& params) { using View = typename Accumulator::View; using ParameterView = GetParameterView; - static_assert(read_index < READ_TERMS); - // Bus value stored in w_1, index into bus column stored in w_2 const auto& w_1 = View(in.w_l); const auto& w_2 = View(in.w_r); - const auto& gamma = ParameterView(params.gamma); const auto& beta = ParameterView(params.beta); // Construct value + index*\beta + \gamma - if constexpr (read_index == 0) { - return w_1 + gamma + w_2 * beta; + return w_1 + gamma + w_2 * beta; + } + + /** + * @brief Construct the polynomial I whose components are the inverse of the product of the read and write terms + * @details If the denominators of log derivative lookup relation are read_term and write_term, then I_i = + * (read_term_i*write_term_i)^{-1}. + * @note Importantly, I_i = 0 for rows i at which there is no read or write. + * + */ + template + static void compute_logderivative_inverse(Polynomials& polynomials, + auto& relation_parameters, + const size_t circuit_size) + { + auto& inverse_polynomial = BusData::inverses(polynomials); + // Compute the product of the read and write terms for each row + for (size_t i = 0; i < circuit_size; ++i) { + auto row = polynomials.get_row(i); + // We only compute the inverse if this row contains a read gate or data that has been read + if (operation_exists_at_row(row)) { + inverse_polynomial[i] = compute_read_term(row, relation_parameters) * + compute_write_term(row, relation_parameters); + } } + // Compute inverse polynomial I in place by inverting the product at each row + FF::batch_invert(inverse_polynomial); + }; + + /** + * @brief Accumulate the subrelation contributions for reads from a single databus column + * @details Two subrelations are required per bus column, one to establish correctness of the precomputed inverses + * and one to establish the validity of the read. + * + * @param accumulator + * @param in + * @param params + * @param scaling_factor + */ + template + static void accumulate_subrelation_contributions(ContainerOverSubrelations& accumulator, + const AllEntities& in, + const Parameters& params, + const FF& scaling_factor) + { + using Accumulator = typename std::tuple_element_t<0, ContainerOverSubrelations>; + using View = typename Accumulator::View; + + const auto inverses = View(BusData::inverses(in)); // Degree 1 + const auto read_counts = View(BusData::read_counts(in)); // Degree 1 + const auto read_term = compute_read_term(in, params); // Degree 1 + const auto write_term = compute_write_term(in, params); // Degree 1 + const auto inverse_exists = compute_inverse_exists(in); // Degree 1 + const auto read_selector = get_read_selector(in); // Degree 2 + const auto write_inverse = inverses * read_term; // Degree 2 + const auto read_inverse = inverses * write_term; // Degree 2 - return Accumulator(1); + // Determine which pair of subrelations to update based on which bus column is being read + constexpr size_t subrel_idx_1 = 2 * bus_idx; + constexpr size_t subrel_idx_2 = 2 * bus_idx + 1; + + // Establish the correctness of the polynomial of inverses I. Note: inverses is computed so that the value is 0 + // if !inverse_exists. Degree 3 + std::get(accumulator) += (read_term * write_term * inverses - inverse_exists) * scaling_factor; + + // Establish validity of the read. Note: no scaling factor here since this constraint is enforced across the + // entire trace, not on a per-row basis + std::get(accumulator) += read_selector * read_inverse - read_counts * write_inverse; // Degree 4 } /** - * @brief Accumulate the contribution from two surelations for the log derivative databus lookup argument - * @details See logderivative_library.hpp for details of the generic log-derivative lookup argument + * @brief Accumulate the log derivative databus lookup argument subrelation contributions for each databus column + * @details Each databus column requires two subrelations * * @param accumulator transformed to `evals + C(in(X)...)*scaling_factor` * @param in an std::array containing the fully extended Accumulator edges. @@ -175,9 +259,10 @@ template class DatabusLookupRelationImpl { const Parameters& params, const FF& scaling_factor) { - - accumulate_logderivative_lookup_subrelation_contributions>( - accumulator, in, params, scaling_factor); + // Accumulate the subrelation contributions for each column of the databus + bb::constexpr_for<0, NUM_BUS_COLUMNS, 1>([&]() { + accumulate_subrelation_contributions(accumulator, in, params, scaling_factor); + }); } }; diff --git a/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/protogalaxy_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/protogalaxy_recursive_verifier.cpp index f1b50360f945..2e2d2454610f 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/protogalaxy_recursive_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/protogalaxy_recursive_verifier.cpp @@ -47,6 +47,10 @@ void ProtoGalaxyRecursiveVerifier_::receive_and_finalise_inst transcript->template receive_from_prover(domain_separator + "_" + labels.calldata); witness_commitments.calldata_read_counts = transcript->template receive_from_prover(domain_separator + "_" + labels.calldata_read_counts); + witness_commitments.return_data = + transcript->template receive_from_prover(domain_separator + "_" + labels.return_data); + witness_commitments.return_data_read_counts = transcript->template receive_from_prover( + domain_separator + "_" + labels.return_data_read_counts); } // Get challenge for sorted list batching and wire four memory records commitment @@ -62,8 +66,10 @@ void ProtoGalaxyRecursiveVerifier_::receive_and_finalise_inst // If Goblin (i.e. using DataBus) receive commitments to log-deriv inverses polynomial if constexpr (IsGoblinFlavor) { - witness_commitments.lookup_inverses = transcript->template receive_from_prover( - domain_separator + "_" + commitment_labels.lookup_inverses); + witness_commitments.calldata_inverses = transcript->template receive_from_prover( + domain_separator + "_" + commitment_labels.calldata_inverses); + witness_commitments.return_data_inverses = transcript->template receive_from_prover( + domain_separator + "_" + commitment_labels.return_data_inverses); } witness_commitments.z_perm = diff --git a/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/ultra_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/ultra_recursive_verifier.cpp index c400f1d4953a..51ae5b06b0a6 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/ultra_recursive_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/ultra_recursive_verifier.cpp @@ -68,6 +68,9 @@ std::array UltraRecursiveVerifier_::ve commitments.calldata = transcript->template receive_from_prover(commitment_labels.calldata); commitments.calldata_read_counts = transcript->template receive_from_prover(commitment_labels.calldata_read_counts); + commitments.return_data = transcript->template receive_from_prover(commitment_labels.return_data); + commitments.return_data_read_counts = + transcript->template receive_from_prover(commitment_labels.return_data_read_counts); } // Get challenge for sorted list batching and wire four memory records @@ -85,8 +88,10 @@ std::array UltraRecursiveVerifier_::ve // If Goblin (i.e. using DataBus) receive commitments to log-deriv inverses polynomial if constexpr (IsGoblinFlavor) { - commitments.lookup_inverses = - transcript->template receive_from_prover(commitment_labels.lookup_inverses); + commitments.calldata_inverses = + transcript->template receive_from_prover(commitment_labels.calldata_inverses); + commitments.return_data_inverses = + transcript->template receive_from_prover(commitment_labels.return_data_inverses); } const FF public_input_delta = compute_public_input_delta( diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/databus.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/databus.hpp new file mode 100644 index 000000000000..b5fc9a7dcc45 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/databus.hpp @@ -0,0 +1,64 @@ +#pragma once + +#include +namespace bb { + +using namespace bb; + +/** + * @brief A DataBus column + * + */ +struct BusVector { + + /** + * @brief Add an element to the data defining this bus column + * + * @param idx Index of the element in the variables vector of a builder + */ + void append(const uint32_t& idx) + { + data.emplace_back(idx); + read_counts.emplace_back(0); + } + + size_t size() const { return data.size(); } + + const uint32_t& operator[](size_t idx) const + { + ASSERT(idx < size()); + return data[idx]; + } + + const uint32_t& get_read_count(size_t idx) const + { + ASSERT(idx < read_counts.size()); + return read_counts[idx]; + } + + void increment_read_count(size_t idx) + { + ASSERT(idx < read_counts.size()); + read_counts[idx]++; + } + + private: + std::vector read_counts; // count of reads at each index into data + std::vector data; // variable indices corresponding to data in this bus vector +}; + +/** + * @brief The DataBus; facilitates storage of public circuit input/output + * @details The DataBus is designed to facilitate efficient transfer of large amounts of public data between circuits. + * It is expected that only a small subset of the data being passed needs to be used in any single circuit, thus we + * provide a read mechanism (implemented through a Builder) that results in prover work proportional to only the data + * that is used. (The prover must still commit to all data in each bus vector but we do not need to hash all data + * in-circuit as we would with public inputs). + * + */ +struct DataBus { + BusVector calldata; // the public input to the circuit + BusVector return_data; // the public output of the circuit +}; + +} // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/goblin_ultra_circuit_builder.cpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/goblin_ultra_circuit_builder.cpp index f6a4c2901a1f..75d8f2e71c77 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/goblin_ultra_circuit_builder.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/goblin_ultra_circuit_builder.cpp @@ -33,10 +33,16 @@ template void GoblinUltraCircuitBuilder_::add_gates_to_ensure_ // Create an arbitrary calldata read gate add_public_calldata(FF(25)); // ensure there is at least one entry in calldata - uint32_t raw_read_idx = 0; // read first entry in calldata + auto raw_read_idx = static_cast(databus.calldata.size()) - 1; // read data that was just added auto read_idx = this->add_variable(raw_read_idx); read_calldata(read_idx); + // Create an arbitrary return data read gate + add_public_return_data(FF(17)); // ensure there is at least one entry in return data + raw_read_idx = static_cast(databus.return_data.size()) - 1; // read data that was just added + read_idx = this->add_variable(raw_read_idx); + read_return_data(read_idx); + // mock a poseidon external gate, with all zeros as input this->blocks.poseidon_external.populate_wires(this->zero_idx, this->zero_idx, this->zero_idx, this->zero_idx); this->blocks.poseidon_external.q_m().emplace_back(0); @@ -219,40 +225,40 @@ template void GoblinUltraCircuitBuilder_::set_goblin_ecc_op_co } /** - * @brief Read from calldata - * @details Creates a calldata lookup gate based on the read data + * @brief Read from a databus column + * @details Creates a databus lookup gate based on the input index and read result * * @tparam FF * @param read_idx_witness_idx Variable index of the read index * @return uint32_t Variable index of the result of the read */ -template uint32_t GoblinUltraCircuitBuilder_::read_calldata(const uint32_t& read_idx_witness_idx) +template +uint32_t GoblinUltraCircuitBuilder_::read_bus_vector(BusVector& bus_vector, const uint32_t& read_idx_witness_idx) { - // Get the raw index into the calldata + // Get the raw index into the databus column const uint32_t read_idx = static_cast(uint256_t(this->get_variable(read_idx_witness_idx))); - // Ensure that the read index is valid - ASSERT(read_idx < public_calldata.size()); + ASSERT(read_idx < bus_vector.size()); // Ensure that the read index is valid + // NOTE(https://github.com/AztecProtocol/barretenberg/issues/937): Multiple reads at same index is not supported. + ASSERT(bus_vector.get_read_count(read_idx) < 1); // Create a variable corresponding to the result of the read. Note that we do not in general connect reads from - // calldata via copy constraints (i.e. we create a unique variable for the result of each read) - FF calldata_value = this->get_variable(public_calldata[read_idx]); - uint32_t value_witness_idx = this->add_variable(calldata_value); + // databus via copy constraints (i.e. we create a unique variable for the result of each read) + FF value = this->get_variable(bus_vector[read_idx]); + uint32_t value_witness_idx = this->add_variable(value); - create_calldata_read_gate({ read_idx_witness_idx, value_witness_idx }); - calldata_read_counts[read_idx]++; + bus_vector.increment_read_count(read_idx); return value_witness_idx; } /** - * @brief Create a calldata lookup/read gate + * @brief Create a databus lookup/read gate * * @tparam FF - * @param databus_lookup_gate_ witness indices corresponding to: calldata index, calldata value + * @param databus_lookup_gate_ witness indices corresponding to: read index, result value */ -template -void GoblinUltraCircuitBuilder_::create_calldata_read_gate(const databus_lookup_gate_& in) +template void GoblinUltraCircuitBuilder_::create_databus_read_gate(const databus_lookup_gate_& in) { auto& block = this->blocks.busread; block.populate_wires(in.value, in.index, this->zero_idx, this->zero_idx); @@ -277,6 +283,36 @@ void GoblinUltraCircuitBuilder_::create_calldata_read_gate(const databus_loo ++this->num_gates; } +/** + * @brief Create a databus calldata lookup/read gate + * + * @tparam FF + * @param databus_lookup_gate_ witness indices corresponding to: calldata index, calldata value + */ +template +void GoblinUltraCircuitBuilder_::create_calldata_read_gate(const databus_lookup_gate_& in) +{ + // Create generic read gate then set q_1 = 1 to specify a calldata read + create_databus_read_gate(in); + auto& block = this->blocks.busread; + block.q_1()[block.size() - 1] = 1; +} + +/** + * @brief Create a databus return data lookup/read gate + * + * @tparam FF + * @param databus_lookup_gate_ witness indices corresponding to: read index, result value + */ +template +void GoblinUltraCircuitBuilder_::create_return_data_read_gate(const databus_lookup_gate_& in) +{ + // Create generic read gate then set q_2 = 1 to specify a return data read + create_databus_read_gate(in); + auto& block = this->blocks.busread; + block.q_2()[block.size() - 1] = 1; +} + /** * @brief Poseidon2 external round gate, activates the q_poseidon2_external selector and relation */ diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/goblin_ultra_circuit_builder.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/goblin_ultra_circuit_builder.hpp index d0f77eee38af..0cb7520570e1 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/goblin_ultra_circuit_builder.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/goblin_ultra_circuit_builder.hpp @@ -2,6 +2,7 @@ #include "barretenberg/execution_trace/execution_trace.hpp" #include "barretenberg/plonk_honk_shared/arithmetization/arithmetization.hpp" #include "barretenberg/stdlib_circuit_builders/op_queue/ecc_op_queue.hpp" +#include "databus.hpp" #include "ultra_circuit_builder.hpp" namespace bb { @@ -26,10 +27,8 @@ template class GoblinUltraCircuitBuilder_ : public UltraCircuitBui uint32_t mul_accum_op_idx; uint32_t equality_op_idx; - // DataBus call/return data arrays - std::vector public_calldata; - std::vector calldata_read_counts; - std::vector public_return_data; + // Container for public calldata/returndata + DataBus databus; // Functions for adding ECC op queue "gates" ecc_op_tuple queue_ecc_add_accum(const g1::affine_element& point); @@ -40,7 +39,16 @@ template class GoblinUltraCircuitBuilder_ : public UltraCircuitBui void populate_ecc_op_wires(const ecc_op_tuple& in); ecc_op_tuple decompose_ecc_operands(uint32_t op, const g1::affine_element& point, const FF& scalar = FF::zero()); void set_goblin_ecc_op_code_constant_variables(); + uint32_t read_bus_vector(BusVector& bus_vector, const uint32_t& read_idx_witness_idx); + void create_databus_read_gate(const databus_lookup_gate_& in); void create_calldata_read_gate(const databus_lookup_gate_& in); + void create_return_data_read_gate(const databus_lookup_gate_& in); + uint32_t append_to_bus_vector(BusVector& bus_vector, const FF& in) + { + const uint32_t index = this->add_variable(in); + bus_vector.append(index); + return index; + } public: GoblinUltraCircuitBuilder_(const size_t size_hint = 0, @@ -126,18 +134,40 @@ template class GoblinUltraCircuitBuilder_ : public UltraCircuitBui /** * @brief Add a witness variable to the public calldata. * - * @param in Value to be added to calldata. * */ - uint32_t add_public_calldata(const FF& in) + uint32_t add_public_calldata(const FF& in) { return append_to_bus_vector(databus.calldata, in); } + + /** + * @brief Add a witness variable to the public return_data. + * + * */ + uint32_t add_public_return_data(const FF& in) { return append_to_bus_vector(databus.return_data, in); } + + /** + * @brief Read from calldata and create a corresponding databus read gate + * + * @param read_idx_witness_idx Witness index for the calldata read index + * @return uint32_t Witness index for the result of the read + */ + uint32_t read_calldata(const uint32_t& read_idx_witness_idx) { - const uint32_t index = this->add_variable(in); - public_calldata.emplace_back(index); - // Note: this is a bit inefficent to do every time but for safety these need to be coupled - calldata_read_counts.resize(public_calldata.size()); - return index; - } + uint32_t value_witness_idx = read_bus_vector(databus.calldata, read_idx_witness_idx); + create_calldata_read_gate({ read_idx_witness_idx, value_witness_idx }); + return value_witness_idx; + }; - uint32_t read_calldata(const uint32_t& read_idx_witness_idx); + /** + * @brief Read from return_data and create a corresponding databus read gate + * + * @param read_idx_witness_idx Witness index for the return_data read index + * @return uint32_t Witness index for the result of the read + */ + uint32_t read_return_data(const uint32_t& read_idx_witness_idx) + { + uint32_t value_witness_idx = read_bus_vector(databus.return_data, read_idx_witness_idx); + create_return_data_read_gate({ read_idx_witness_idx, value_witness_idx }); + return value_witness_idx; + }; void create_poseidon2_external_gate(const poseidon2_external_gate_& in); void create_poseidon2_internal_gate(const poseidon2_internal_gate_& in); diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/goblin_ultra_flavor.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/goblin_ultra_flavor.hpp index a7a93833f435..677749016b5e 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/goblin_ultra_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/goblin_ultra_flavor.hpp @@ -40,12 +40,12 @@ class GoblinUltraFlavor { // The number of multivariate polynomials on which a sumcheck prover sumcheck operates (including shifts). We often // need containers of this size to hold related data, so we choose a name more agnostic than `NUM_POLYNOMIALS`. // Note: this number does not include the individual sorted list polynomials. - static constexpr size_t NUM_ALL_ENTITIES = 55; + static constexpr size_t NUM_ALL_ENTITIES = 58; // The number of polynomials precomputed to describe a circuit and to aid a prover in constructing a satisfying // assignment of witnesses. We again choose a neutral name. static constexpr size_t NUM_PRECOMPUTED_ENTITIES = 30; // The total number of witness entities not including shifts. - static constexpr size_t NUM_WITNESS_ENTITIES = 14; + static constexpr size_t NUM_WITNESS_ENTITIES = 17; // Total number of folded polynomials, which is just all polynomials except the shifts static constexpr size_t NUM_FOLDED_ENTITIES = NUM_PRECOMPUTED_ENTITIES + NUM_WITNESS_ENTITIES; @@ -173,16 +173,19 @@ class GoblinUltraFlavor { template class DerivedEntities { public: DEFINE_FLAVOR_MEMBERS(DataType, - sorted_accum, // column 4 - z_perm, // column 5 - z_lookup, // column 6 - ecc_op_wire_1, // column 7 - ecc_op_wire_2, // column 8 - ecc_op_wire_3, // column 9 - ecc_op_wire_4, // column 10 - calldata, // column 11 - calldata_read_counts, // column 12 - lookup_inverses); // column 13 + sorted_accum, // column 4 + z_perm, // column 5 + z_lookup, // column 6 + ecc_op_wire_1, // column 7 + ecc_op_wire_2, // column 8 + ecc_op_wire_3, // column 9 + ecc_op_wire_4, // column 10 + calldata, // column 11 + calldata_read_counts, // column 12 + calldata_inverses, // column 13 + return_data, // column 14 + return_data_read_counts, // column 15 + return_data_inverses); // column 16 }; /** @@ -349,7 +352,7 @@ class GoblinUltraFlavor { } /** - * @brief Compute the inverse polynomial used in the log derivative lookup argument + * @brief Compute the inverse polynomial used in the databus log derivative lookup argument * * @tparam Flavor * @param beta @@ -358,10 +361,16 @@ class GoblinUltraFlavor { void compute_logderivative_inverse(const RelationParameters& relation_parameters) { auto prover_polynomials = ProverPolynomials(*this); - // Compute permutation and lookup grand product polynomials - bb::compute_logderivative_inverse( + + // Compute inverses for calldata reads + DatabusLookupRelation::compute_logderivative_inverse( + prover_polynomials, relation_parameters, this->circuit_size); + this->calldata_inverses = prover_polynomials.calldata_inverses; + + // Compute inverses for return data reads + DatabusLookupRelation::compute_logderivative_inverse( prover_polynomials, relation_parameters, this->circuit_size); - this->lookup_inverses = prover_polynomials.lookup_inverses; + this->return_data_inverses = prover_polynomials.return_data_inverses; } /** @@ -519,7 +528,10 @@ class GoblinUltraFlavor { ecc_op_wire_4 = "ECC_OP_WIRE_4"; calldata = "CALLDATA"; calldata_read_counts = "CALLDATA_READ_COUNTS"; - lookup_inverses = "LOOKUP_INVERSES"; + calldata_inverses = "CALLDATA_INVERSES"; + return_data = "RETURN_DATA"; + return_data_read_counts = "RETURN_DATA_READ_COUNTS"; + return_data_inverses = "RETURN_DATA_INVERSES"; q_c = "Q_C"; q_l = "Q_L"; @@ -608,7 +620,10 @@ class GoblinUltraFlavor { this->ecc_op_wire_4 = commitments.ecc_op_wire_4; this->calldata = commitments.calldata; this->calldata_read_counts = commitments.calldata_read_counts; - this->lookup_inverses = commitments.lookup_inverses; + this->calldata_inverses = commitments.calldata_inverses; + this->return_data = commitments.return_data; + this->return_data_read_counts = commitments.return_data_read_counts; + this->return_data_inverses = commitments.return_data_inverses; } } }; @@ -635,7 +650,10 @@ class GoblinUltraFlavor { Commitment ecc_op_wire_4_comm; Commitment calldata_comm; Commitment calldata_read_counts_comm; - Commitment lookup_inverses_comm; + Commitment calldata_inverses_comm; + Commitment return_data_comm; + Commitment return_data_read_counts_comm; + Commitment return_data_inverses_comm; Commitment sorted_accum_comm; Commitment w_4_comm; Commitment z_perm_comm; @@ -688,7 +706,10 @@ class GoblinUltraFlavor { ecc_op_wire_4_comm = deserialize_from_buffer(proof_data, num_frs_read); calldata_comm = deserialize_from_buffer(proof_data, num_frs_read); calldata_read_counts_comm = deserialize_from_buffer(proof_data, num_frs_read); - lookup_inverses_comm = deserialize_from_buffer(proof_data, num_frs_read); + calldata_inverses_comm = deserialize_from_buffer(proof_data, num_frs_read); + return_data_comm = deserialize_from_buffer(proof_data, num_frs_read); + return_data_read_counts_comm = deserialize_from_buffer(proof_data, num_frs_read); + return_data_inverses_comm = deserialize_from_buffer(proof_data, num_frs_read); sorted_accum_comm = deserialize_from_buffer(proof_data, num_frs_read); w_4_comm = deserialize_from_buffer(proof_data, num_frs_read); z_perm_comm = deserialize_from_buffer(proof_data, num_frs_read); @@ -726,7 +747,10 @@ class GoblinUltraFlavor { serialize_to_buffer(ecc_op_wire_4_comm, proof_data); serialize_to_buffer(calldata_comm, proof_data); serialize_to_buffer(calldata_read_counts_comm, proof_data); - serialize_to_buffer(lookup_inverses_comm, proof_data); + serialize_to_buffer(calldata_inverses_comm, proof_data); + serialize_to_buffer(return_data_comm, proof_data); + serialize_to_buffer(return_data_read_counts_comm, proof_data); + serialize_to_buffer(return_data_inverses_comm, proof_data); serialize_to_buffer(sorted_accum_comm, proof_data); serialize_to_buffer(w_4_comm, proof_data); serialize_to_buffer(z_perm_comm, proof_data); diff --git a/barretenberg/cpp/src/barretenberg/sumcheck/instance/prover_instance.cpp b/barretenberg/cpp/src/barretenberg/sumcheck/instance/prover_instance.cpp index 0e936dddfffd..3f0a6b18072f 100644 --- a/barretenberg/cpp/src/barretenberg/sumcheck/instance/prover_instance.cpp +++ b/barretenberg/cpp/src/barretenberg/sumcheck/instance/prover_instance.cpp @@ -43,14 +43,20 @@ void ProverInstance_::construct_databus_polynomials(Circuit& circuit) { Polynomial public_calldata{ dyadic_circuit_size }; Polynomial calldata_read_counts{ dyadic_circuit_size }; - Polynomial databus_id{ dyadic_circuit_size }; + Polynomial public_return_data{ dyadic_circuit_size }; + Polynomial return_data_read_counts{ dyadic_circuit_size }; // Note: We do not utilize a zero row for databus columns - for (size_t idx = 0; idx < circuit.public_calldata.size(); ++idx) { - public_calldata[idx] = circuit.get_variable(circuit.public_calldata[idx]); - calldata_read_counts[idx] = circuit.calldata_read_counts[idx]; + for (size_t idx = 0; idx < circuit.databus.calldata.size(); ++idx) { + public_calldata[idx] = circuit.get_variable(circuit.databus.calldata[idx]); + calldata_read_counts[idx] = circuit.databus.calldata.get_read_count(idx); + } + for (size_t idx = 0; idx < circuit.databus.return_data.size(); ++idx) { + public_return_data[idx] = circuit.get_variable(circuit.databus.return_data[idx]); + return_data_read_counts[idx] = circuit.databus.return_data.get_read_count(idx); } + Polynomial databus_id{ dyadic_circuit_size }; // Compute a simple identity polynomial for use in the databus lookup argument for (size_t i = 0; i < databus_id.size(); ++i) { databus_id[i] = i; @@ -58,6 +64,8 @@ void ProverInstance_::construct_databus_polynomials(Circuit& circuit) proving_key.calldata = public_calldata.share(); proving_key.calldata_read_counts = calldata_read_counts.share(); + proving_key.return_data = public_return_data.share(); + proving_key.return_data_read_counts = return_data_read_counts.share(); proving_key.databus_id = databus_id.share(); } diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/databus.test.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/databus.test.cpp new file mode 100644 index 000000000000..46c418fe1fa0 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/databus.test.cpp @@ -0,0 +1,166 @@ +#include +#include +#include + +#include "barretenberg/common/log.hpp" +#include "barretenberg/goblin/mock_circuits.hpp" +#include "barretenberg/plonk_honk_shared/instance_inspector.hpp" +#include "barretenberg/stdlib_circuit_builders/goblin_ultra_circuit_builder.hpp" +#include "barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp" + +#include "barretenberg/ultra_honk/ultra_prover.hpp" +#include "barretenberg/ultra_honk/ultra_verifier.hpp" + +using namespace bb; + +namespace { +auto& engine = numeric::get_debug_randomness(); +} + +class DataBusTests : public ::testing::Test { + protected: + static void SetUpTestSuite() { bb::srs::init_crs_factory("../srs_db/ignition"); } + + using Curve = curve::BN254; + using FF = Curve::ScalarField; + + // Construct and verify a GUH proof for a given circuit + static bool construct_and_verify_proof(GoblinUltraCircuitBuilder& builder) + { + GoblinUltraProver prover{ builder }; + auto verification_key = std::make_shared(prover.instance->proving_key); + GoblinUltraVerifier verifier{ verification_key }; + auto proof = prover.construct_proof(); + return verifier.verify_proof(proof); + } + + // Construct a Goblin Ultra circuit with some arbitrary sample gates + static GoblinUltraCircuitBuilder construct_test_builder() + { + auto op_queue = std::make_shared(); + auto builder = GoblinUltraCircuitBuilder{ op_queue }; + GoblinMockCircuits::construct_simple_circuit(builder); + return builder; + } +}; + +/** + * @brief Test proof construction/verification for a circuit with calldata lookup gates + * gates + * + */ +TEST_F(DataBusTests, CallDataRead) +{ + // Construct a circuit and add some ecc op gates and arithmetic gates + auto builder = construct_test_builder(); + + // Add some values to calldata + std::vector calldata_values = { 7, 10, 3, 12, 1 }; + for (auto& val : calldata_values) { + builder.add_public_calldata(val); + } + + // Define some raw indices at which to read calldata + std::vector read_indices = { 1, 4 }; + + // Create some calldata read gates and store the variable indices of the result for later + std::vector result_witness_indices; + for (uint32_t& read_idx : read_indices) { + // Create a variable corresponding to the index at which we want to read into calldata + uint32_t read_idx_witness_idx = builder.add_variable(read_idx); + + auto value_witness_idx = builder.read_calldata(read_idx_witness_idx); + result_witness_indices.emplace_back(value_witness_idx); + } + + // Generally, we'll want to use the result of a read in some other operation. As an example, we construct a gate + // that shows the sum of the two values just read is equal to the expected sum. + FF expected_sum = 0; + for (uint32_t& read_idx : read_indices) { + expected_sum += calldata_values[read_idx]; + } + builder.create_add_gate( + { result_witness_indices[0], result_witness_indices[1], builder.zero_idx, 1, 1, 0, -expected_sum }); + + // Construct and verify Honk proof + bool result = construct_and_verify_proof(builder); + EXPECT_TRUE(result); +} + +/** + * @brief Test proof construction/verification for a circuit with return data lookup gates + * gates + * + */ +TEST_F(DataBusTests, ReturnDataRead) +{ + // Construct a circuit and add some ecc op gates and arithmetic gates + auto builder = construct_test_builder(); + + // Add some values to return_data + std::vector return_data_values = { 7, 10, 3, 12, 1 }; + for (auto& val : return_data_values) { + builder.add_public_return_data(val); + } + + // Define some raw indices at which to read return_data + std::vector read_indices = { 1, 4 }; + + // Create some return_data read gates and store the variable indices of the result for later + std::vector result_witness_indices; + for (uint32_t& read_idx : read_indices) { + // Create a variable corresponding to the index at which we want to read into return_data + uint32_t read_idx_witness_idx = builder.add_variable(read_idx); + + auto value_witness_idx = builder.read_return_data(read_idx_witness_idx); + result_witness_indices.emplace_back(value_witness_idx); + } + + // Generally, we'll want to use the result of a read in some other operation. As an example, we construct a gate + // that shows the sum of the two values just read is equal to the expected sum. + FF expected_sum = 0; + for (uint32_t& read_idx : read_indices) { + expected_sum += return_data_values[read_idx]; + } + builder.create_add_gate( + { result_witness_indices[0], result_witness_indices[1], builder.zero_idx, 1, 1, 0, -expected_sum }); + + // Construct and verify Honk proof + bool result = construct_and_verify_proof(builder); + EXPECT_TRUE(result); +} + +/** + * @brief Test reads from calldata and return data in the same circuit + * + */ +TEST_F(DataBusTests, CallDataAndReturnData) +{ + // Construct a circuit and add some ecc op gates and arithmetic gates + auto builder = construct_test_builder(); + + // Add some values to calldata + std::vector calldata_values = { 5, 27, 11 }; + for (auto& val : calldata_values) { + builder.add_public_calldata(val); + } + + // Add some values to return_data + std::vector return_data_values = { 7, 10 }; + for (auto& val : return_data_values) { + builder.add_public_return_data(val); + } + + // Make some aribitrary reads from calldata and return data + uint32_t read_idx = 2; + uint32_t read_idx_witness_idx = builder.add_variable(read_idx); + builder.read_calldata(read_idx_witness_idx); + + read_idx = 0; + read_idx_witness_idx = builder.add_variable(read_idx); + builder.read_return_data(read_idx_witness_idx); + + // Construct and verify Honk proof + bool result = construct_and_verify_proof(builder); + EXPECT_TRUE(result); +} diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/databus_composer.test.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/databus_composer.test.cpp deleted file mode 100644 index b7729c1099f6..000000000000 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/databus_composer.test.cpp +++ /dev/null @@ -1,78 +0,0 @@ -#include -#include -#include - -#include "barretenberg/common/log.hpp" -#include "barretenberg/goblin/mock_circuits.hpp" -#include "barretenberg/plonk_honk_shared/instance_inspector.hpp" -#include "barretenberg/stdlib_circuit_builders/goblin_ultra_circuit_builder.hpp" -#include "barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp" - -#include "barretenberg/ultra_honk/ultra_prover.hpp" -#include "barretenberg/ultra_honk/ultra_verifier.hpp" - -using namespace bb; - -namespace { -auto& engine = numeric::get_debug_randomness(); -} - -class DataBusComposerTests : public ::testing::Test { - protected: - static void SetUpTestSuite() { bb::srs::init_crs_factory("../srs_db/ignition"); } - - using Curve = curve::BN254; - using FF = Curve::ScalarField; -}; - -/** - * @brief Test proof construction/verification for a circuit with calldata lookup gates - * gates - * @note We simulate op queue interactions with a previous circuit so the actual circuit under test utilizes an op queue - * with non-empty 'previous' data. This avoid complications with zero-commitments etc. - * - */ -TEST_F(DataBusComposerTests, CallDataRead) -{ - auto op_queue = std::make_shared(); - - auto builder = GoblinUltraCircuitBuilder{ op_queue }; - - // Add some ecc op gates and arithmetic gates - GoblinMockCircuits::construct_simple_circuit(builder); - - // Add some values to calldata - std::vector calldata_values = { 7, 10, 3, 12, 1 }; - for (auto& val : calldata_values) { - builder.add_public_calldata(val); - } - - // Define some raw indices at which to read calldata (these will be ASSERTed to be valid) - std::vector read_indices = { 1, 4 }; - - // Create some calldata read gates. (Normally we'd use the result of the read. Example of that is below) - for (uint32_t& read_idx : read_indices) { - // Create a variable corresponding to the index at which we want to read into calldata - uint32_t read_idx_witness_idx = builder.add_variable(read_idx); - - builder.read_calldata(read_idx_witness_idx); - } - - // In general we'll want to use the result of a calldata read in another operation. Here's an example using - // an add gate to show that the result of the read is as expected: - uint32_t read_idx = 2; - FF expected_result = calldata_values[read_idx]; - uint32_t read_idx_witness_idx = builder.add_variable(read_idx); - uint32_t result_witness_idx = builder.read_calldata(read_idx_witness_idx); - builder.create_add_gate({ result_witness_idx, builder.zero_idx, builder.zero_idx, 1, 0, 0, -expected_result }); - - // Construct and verify Honk proof - auto instance = std::make_shared>(builder); - // For debugging, use "instance_inspector::print_databus_info(instance)" - GoblinUltraProver prover(instance); - auto verification_key = std::make_shared(instance->proving_key); - GoblinUltraVerifier verifier(verification_key); - auto proof = prover.construct_proof(); - bool verified = verifier.verify_proof(proof); - EXPECT_TRUE(verified); -} diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/goblin_ultra_transcript.test.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/goblin_ultra_transcript.test.cpp index 18e66bf9f095..0bccfa8084f9 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/goblin_ultra_transcript.test.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/goblin_ultra_transcript.test.cpp @@ -60,6 +60,8 @@ class GoblinUltraTranscriptTests : public ::testing::Test { manifest_expected.add_entry(round, "ECC_OP_WIRE_4", frs_per_G); manifest_expected.add_entry(round, "CALLDATA", frs_per_G); manifest_expected.add_entry(round, "CALLDATA_READ_COUNTS", frs_per_G); + manifest_expected.add_entry(round, "RETURN_DATA", frs_per_G); + manifest_expected.add_entry(round, "RETURN_DATA_READ_COUNTS", frs_per_G); manifest_expected.add_challenge(round, "eta", "eta_two", "eta_three"); round++; @@ -68,7 +70,8 @@ class GoblinUltraTranscriptTests : public ::testing::Test { manifest_expected.add_challenge(round, "beta", "gamma"); round++; - manifest_expected.add_entry(round, "LOOKUP_INVERSES", frs_per_G); + manifest_expected.add_entry(round, "CALLDATA_INVERSES", frs_per_G); + manifest_expected.add_entry(round, "RETURN_DATA_INVERSES", frs_per_G); manifest_expected.add_entry(round, "Z_PERM", frs_per_G); manifest_expected.add_entry(round, "Z_LOOKUP", frs_per_G); diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/oink_prover.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/oink_prover.cpp index 7d3a1251f3c3..700ece5181f2 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/oink_prover.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/oink_prover.cpp @@ -87,12 +87,18 @@ template void OinkProver::execute_wire_commitment for (size_t idx = 0; idx < Flavor::NUM_WIRES; ++idx) { transcript->send_to_verifier(domain_separator + labels[idx], op_wire_comms[idx]); } - // Commit to DataBus columns + + // Commit to DataBus columns and corresponding read counts witness_commitments.calldata = commitment_key->commit(proving_key.calldata); witness_commitments.calldata_read_counts = commitment_key->commit(proving_key.calldata_read_counts); transcript->send_to_verifier(domain_separator + commitment_labels.calldata, witness_commitments.calldata); transcript->send_to_verifier(domain_separator + commitment_labels.calldata_read_counts, witness_commitments.calldata_read_counts); + witness_commitments.return_data = commitment_key->commit(proving_key.return_data); + witness_commitments.return_data_read_counts = commitment_key->commit(proving_key.return_data_read_counts); + transcript->send_to_verifier(domain_separator + commitment_labels.return_data, witness_commitments.return_data); + transcript->send_to_verifier(domain_separator + commitment_labels.return_data_read_counts, + witness_commitments.return_data_read_counts); } } @@ -132,9 +138,12 @@ template void OinkProver::execute_log_derivative_ if constexpr (IsGoblinFlavor) { // Compute and commit to the logderivative inverse used in DataBus proving_key.compute_logderivative_inverse(relation_parameters); - witness_commitments.lookup_inverses = commitment_key->commit(proving_key.lookup_inverses); - transcript->send_to_verifier(domain_separator + commitment_labels.lookup_inverses, - witness_commitments.lookup_inverses); + witness_commitments.calldata_inverses = commitment_key->commit(proving_key.calldata_inverses); + witness_commitments.return_data_inverses = commitment_key->commit(proving_key.return_data_inverses); + transcript->send_to_verifier(domain_separator + commitment_labels.calldata_inverses, + witness_commitments.calldata_inverses); + transcript->send_to_verifier(domain_separator + commitment_labels.return_data_inverses, + witness_commitments.return_data_inverses); } } diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/oink_verifier.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/oink_verifier.cpp index f7e265b49eba..6f565be827c4 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/oink_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/oink_verifier.cpp @@ -74,6 +74,10 @@ template void OinkVerifier::execute_wire_commitme transcript->template receive_from_prover(domain_separator + comm_labels.calldata); witness_comms.calldata_read_counts = transcript->template receive_from_prover(domain_separator + comm_labels.calldata_read_counts); + witness_comms.return_data = + transcript->template receive_from_prover(domain_separator + comm_labels.return_data); + witness_comms.return_data_read_counts = transcript->template receive_from_prover( + domain_separator + comm_labels.return_data_read_counts); } } @@ -105,10 +109,12 @@ template void OinkVerifier::execute_log_derivativ auto [beta, gamma] = transcript->template get_challenges(domain_separator + "beta", domain_separator + "gamma"); relation_parameters.beta = beta; relation_parameters.gamma = gamma; - // If Goblin (i.e. using DataBus) receive commitments to log-deriv inverses polynomial + // If Goblin (i.e. using DataBus) receive commitments to log-deriv inverses polynomials if constexpr (IsGoblinFlavor) { - witness_comms.lookup_inverses = - transcript->template receive_from_prover(domain_separator + comm_labels.lookup_inverses); + witness_comms.calldata_inverses = + transcript->template receive_from_prover(domain_separator + comm_labels.calldata_inverses); + witness_comms.return_data_inverses = + transcript->template receive_from_prover(domain_separator + comm_labels.return_data_inverses); } } diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/relation_correctness.test.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/relation_correctness.test.cpp index 2fb80d76c9fa..ac3ca627ce51 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/relation_correctness.test.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/relation_correctness.test.cpp @@ -347,7 +347,10 @@ TEST_F(UltraRelationCorrectnessTests, GoblinUltra) ensure_non_zero(proving_key.calldata); ensure_non_zero(proving_key.calldata_read_counts); - ensure_non_zero(proving_key.lookup_inverses); + ensure_non_zero(proving_key.calldata_inverses); + ensure_non_zero(proving_key.return_data); + ensure_non_zero(proving_key.return_data_read_counts); + ensure_non_zero(proving_key.return_data_inverses); // Construct the round for applying sumcheck relations and results for storing computed results using Relations = typename Flavor::Relations;