From 95a1d8ac45e0db8ec21ba1cb2e3f47bb68909b71 Mon Sep 17 00:00:00 2001 From: ledwards2225 <98505400+ledwards2225@users.noreply.github.com> Date: Wed, 3 Apr 2024 14:55:29 -0700 Subject: [PATCH] feat: DataBus notion with calldata/return data (#5504) The DataBus will eventually contain 3 columns: calldata, return data, and a 3rd for storing "function" calldata/returndata. Prior to this PR, we only had calldata (implemented as a proof of concept). This PR introduced a more proper notion of the `DataBus` and adds return data to its functionality. (No 3rd column yet). Each column (`BusVector`) of the `DataBus` essentially behaves the same way so the bulk of the work in this PR is simply making that logic generic/shareable in the `GoblinUltraCircuitBuilder` and the `DataBusLookupRelation`. Note: In prior work I collected some of the generic log-derivative lookup logic (originally introduced by Zac for the ECCVM) in `logderivative_library.hpp`. I'm no longer using that shared logic for the Databus for a few reasons: 1) the library is now also used by the AVM so any changes would have touched many avm files, 2) the DataBus relation needs functionality for multiple "tables" (bus columns) whereas others only have one, and 3) the library methods were over generalized in a way not needed for the DataBus (multiple read/write terms) that obscured the simple structure of the Databus lookup relation. --- .../relations/databus_lookup_relation.hpp | 241 ++++++++++++------ .../protogalaxy_recursive_verifier.cpp | 10 +- .../verifier/ultra_recursive_verifier.cpp | 9 +- .../stdlib_circuit_builders/databus.hpp | 64 +++++ .../goblin_ultra_circuit_builder.cpp | 68 +++-- .../goblin_ultra_circuit_builder.hpp | 56 +++- .../goblin_ultra_flavor.hpp | 66 +++-- .../sumcheck/instance/prover_instance.cpp | 16 +- .../barretenberg/ultra_honk/databus.test.cpp | 166 ++++++++++++ .../ultra_honk/databus_composer.test.cpp | 78 ------ .../goblin_ultra_transcript.test.cpp | 5 +- .../barretenberg/ultra_honk/oink_prover.cpp | 17 +- .../barretenberg/ultra_honk/oink_verifier.cpp | 12 +- .../ultra_honk/relation_correctness.test.cpp | 5 +- 14 files changed, 590 insertions(+), 223 deletions(-) create mode 100644 barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/databus.hpp create mode 100644 barretenberg/cpp/src/barretenberg/ultra_honk/databus.test.cpp delete mode 100644 barretenberg/cpp/src/barretenberg/ultra_honk/databus_composer.test.cpp diff --git a/barretenberg/cpp/src/barretenberg/relations/databus_lookup_relation.hpp b/barretenberg/cpp/src/barretenberg/relations/databus_lookup_relation.hpp index eb79bb21efe..d919738a928 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 f1b50360f94..2e2d2454610 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 c400f1d4953..51ae5b06b0a 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 00000000000..b5fc9a7dcc4 --- /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 f6a4c2901a1..75d8f2e71c7 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 d0f77eee38a..0cb7520570e 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 a7a93833f43..677749016b5 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 0e936dddfff..3f0a6b18072 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 00000000000..46c418fe1fa --- /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 b7729c1099f..00000000000 --- 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 18e66bf9f09..0bccfa8084f 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 7d3a1251f3c..700ece5181f 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 f7e265b49eb..6f565be827c 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 2fb80d76c9f..ac3ca627ce5 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;