-
Notifications
You must be signed in to change notification settings - Fork 268
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
Changes from 24 commits
c19b183
10c8a27
c19d36e
69dbfaa
b3c847a
28306d2
5add60a
2d3fd9f
a58a8d1
83a112d
897b0b8
b59e085
ccbe3a4
f98b583
7b264bf
c5008aa
70d6506
b327241
adcba13
c3d290b
872672b
8e5decf
3d410bd
7132823
a64d57a
cc3c0d8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
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; | ||
BusVector return_data; | ||
}; | ||
|
||
} // namespace bb |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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); | ||
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why is reading more than once something we don't wanna support? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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); | ||
|
@@ -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 | ||
*/ | ||
|
There was a problem hiding this comment.
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
andreturn_data
is?There was a problem hiding this comment.
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