Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: DataBus notion with calldata/return data #5504

Merged
merged 26 commits into from
Apr 3, 2024
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
c19b183
clean up relation a bit
ledwards2225 Mar 28, 2024
10c8a27
generalize via BusVector and DataBus
ledwards2225 Mar 28, 2024
c19d36e
generic gate
ledwards2225 Mar 28, 2024
69dbfaa
calldata selected by q_busread q_1
ledwards2225 Mar 28, 2024
b3c847a
simplify databus relation and make it self contained
ledwards2225 Mar 28, 2024
28306d2
Merge branch 'master' into lde/db_generic
ledwards2225 Mar 28, 2024
5add60a
cleanup and comments
ledwards2225 Mar 29, 2024
2d3fd9f
Merge branch 'master' into lde/db_generic
ledwards2225 Mar 29, 2024
a58a8d1
introduce bus index
ledwards2225 Mar 29, 2024
83a112d
remove incorrect View
ledwards2225 Mar 29, 2024
897b0b8
cleanup and comment
ledwards2225 Mar 29, 2024
b59e085
busvector members are private
ledwards2225 Mar 31, 2024
ccbe3a4
databus in new file and basic builder methods for return data
ledwards2225 Mar 31, 2024
f98b583
fully added return data; tests seem to be passing
ledwards2225 Apr 1, 2024
7b264bf
update PG recursive verifier
ledwards2225 Apr 1, 2024
c5008aa
more tests and an assert on read counts
ledwards2225 Apr 1, 2024
70d6506
Merge branch 'master' into lde/db_generic
ledwards2225 Apr 1, 2024
b327241
remove some duplication in bus relation
ledwards2225 Apr 1, 2024
adcba13
refactor relation with BusData
ledwards2225 Apr 2, 2024
c3d290b
cleanup and commenting
ledwards2225 Apr 2, 2024
872672b
Merge branch 'master' into lde/db_generic
ledwards2225 Apr 2, 2024
8e5decf
big comment for databus relation
ledwards2225 Apr 2, 2024
3d410bd
woops
ledwards2225 Apr 2, 2024
7132823
typo
ledwards2225 Apr 2, 2024
a64d57a
comments in response to review
ledwards2225 Apr 3, 2024
cc3c0d8
Merge branch 'master' into lde/db_generic
ledwards2225 Apr 3, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
236 changes: 158 additions & 78 deletions barretenberg/cpp/src/barretenberg/relations/databus_lookup_relation.hpp

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ void ProtoGalaxyRecursiveVerifier_<VerifierInstances>::receive_and_finalise_inst
transcript->template receive_from_prover<Commitment>(domain_separator + "_" + labels.calldata);
witness_commitments.calldata_read_counts =
transcript->template receive_from_prover<Commitment>(domain_separator + "_" + labels.calldata_read_counts);
witness_commitments.return_data =
transcript->template receive_from_prover<Commitment>(domain_separator + "_" + labels.return_data);
witness_commitments.return_data_read_counts = transcript->template receive_from_prover<Commitment>(
domain_separator + "_" + labels.return_data_read_counts);
}

// Get challenge for sorted list batching and wire four memory records commitment
Expand All @@ -62,8 +66,10 @@ void ProtoGalaxyRecursiveVerifier_<VerifierInstances>::receive_and_finalise_inst

// If Goblin (i.e. using DataBus) receive commitments to log-deriv inverses polynomial
if constexpr (IsGoblinFlavor<Flavor>) {
witness_commitments.lookup_inverses = transcript->template receive_from_prover<Commitment>(
domain_separator + "_" + commitment_labels.lookup_inverses);
witness_commitments.calldata_inverses = transcript->template receive_from_prover<Commitment>(
domain_separator + "_" + commitment_labels.calldata_inverses);
witness_commitments.return_data_inverses = transcript->template receive_from_prover<Commitment>(
domain_separator + "_" + commitment_labels.return_data_inverses);
}

witness_commitments.z_perm =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ std::array<typename Flavor::GroupElement, 2> UltraRecursiveVerifier_<Flavor>::ve
commitments.calldata = transcript->template receive_from_prover<Commitment>(commitment_labels.calldata);
commitments.calldata_read_counts =
transcript->template receive_from_prover<Commitment>(commitment_labels.calldata_read_counts);
commitments.return_data = transcript->template receive_from_prover<Commitment>(commitment_labels.return_data);
commitments.return_data_read_counts =
transcript->template receive_from_prover<Commitment>(commitment_labels.return_data_read_counts);
}

// Get challenge for sorted list batching and wire four memory records
Expand All @@ -85,8 +88,10 @@ std::array<typename Flavor::GroupElement, 2> UltraRecursiveVerifier_<Flavor>::ve

// If Goblin (i.e. using DataBus) receive commitments to log-deriv inverses polynomial
if constexpr (IsGoblinFlavor<Flavor>) {
commitments.lookup_inverses =
transcript->template receive_from_prover<Commitment>(commitment_labels.lookup_inverses);
commitments.calldata_inverses =
transcript->template receive_from_prover<Commitment>(commitment_labels.calldata_inverses);
commitments.return_data_inverses =
transcript->template receive_from_prover<Commitment>(commitment_labels.return_data_inverses);
}

const FF public_input_delta = compute_public_input_delta<Flavor>(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#pragma once

#include <cstdint>
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<uint32_t> read_counts; // count of reads at each index into data
std::vector<uint32_t> 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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please add comments here explaining what calldata and return_data is?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, thanks. Basically calldata = public input to the circuit and return_data = public output of the circuit. I added some comments to this effect

BusVector return_data;
};

} // namespace bb
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,16 @@ template <typename FF> void GoblinUltraCircuitBuilder_<FF>::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<uint32_t>(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<uint32_t>(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);
Expand Down Expand Up @@ -219,40 +225,39 @@ template <typename FF> void GoblinUltraCircuitBuilder_<FF>::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 <typename FF> uint32_t GoblinUltraCircuitBuilder_<FF>::read_calldata(const uint32_t& read_idx_witness_idx)
template <typename FF>
uint32_t GoblinUltraCircuitBuilder_<FF>::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<uint32_t>(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
ASSERT(bus_vector.get_read_count(read_idx) < 1); // Reading more than once at the same index is not supported
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is reading more than once something we don't wanna support?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is done for efficiency reasons, I added an issue explaining this in detail. I marked it as NOTE rather than TODO since this may be an acceptable restriction indefinitely


// 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 <typename FF>
void GoblinUltraCircuitBuilder_<FF>::create_calldata_read_gate(const databus_lookup_gate_<FF>& in)
template <typename FF> void GoblinUltraCircuitBuilder_<FF>::create_databus_read_gate(const databus_lookup_gate_<FF>& in)
{
auto& block = this->blocks.busread;
block.populate_wires(in.value, in.index, this->zero_idx, this->zero_idx);
Expand All @@ -277,6 +282,36 @@ void GoblinUltraCircuitBuilder_<FF>::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 <typename FF>
void GoblinUltraCircuitBuilder_<FF>::create_calldata_read_gate(const databus_lookup_gate_<FF>& 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 <typename FF>
void GoblinUltraCircuitBuilder_<FF>::create_return_data_read_gate(const databus_lookup_gate_<FF>& 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
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -26,10 +27,8 @@ template <typename FF> class GoblinUltraCircuitBuilder_ : public UltraCircuitBui
uint32_t mul_accum_op_idx;
uint32_t equality_op_idx;

// DataBus call/return data arrays
std::vector<uint32_t> public_calldata;
std::vector<uint32_t> calldata_read_counts;
std::vector<uint32_t> 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);
Expand All @@ -40,7 +39,16 @@ template <typename FF> 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_<FF>& in);
void create_calldata_read_gate(const databus_lookup_gate_<FF>& in);
void create_return_data_read_gate(const databus_lookup_gate_<FF>& 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,
Expand Down Expand Up @@ -126,18 +134,40 @@ template <typename FF> 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_<FF>& in);
void create_poseidon2_internal_gate(const poseidon2_internal_gate_<FF>& in);
Expand Down
Loading
Loading