-
Notifications
You must be signed in to change notification settings - Fork 327
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: stdlib databus #5598
Merged
Merged
feat: stdlib databus #5598
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
7c301ed
basic class in place with a test
ledwards2225 34bd510
use enum, maybe temporary
ledwards2225 1a877c6
builder databus is an array of bus vectors
ledwards2225 192e3e3
clean up and simplify methods
ledwards2225 04a63c5
move bus_vector into stdlib databus class
ledwards2225 14d901d
enum class
ledwards2225 48b308e
improved stdlib databus tests
ledwards2225 2cd8021
some comments and cleanup
ledwards2225 2efc3bc
comments
ledwards2225 850f74b
comments and some test updates
ledwards2225 a239fdd
comment
ledwards2225 9cc276d
Merge branch 'master' into lde/stdlib_databus
ledwards2225 f85141c
remove constant index path
ledwards2225 d74d125
Merge branch 'master' into lde/stdlib_databus
ledwards2225 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
54 changes: 54 additions & 0 deletions
54
barretenberg/cpp/src/barretenberg/stdlib/primitives/databus/databus.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
#include "databus.hpp" | ||
#include "../circuit_builders/circuit_builders.hpp" | ||
|
||
namespace bb::stdlib { | ||
|
||
template <typename Builder> void databus<Builder>::bus_vector::set_values(const std::vector<field_pt>& entries_in) | ||
{ | ||
// Set the context from the input entries | ||
for (const auto& entry : entries_in) { | ||
if (entry.get_context() != nullptr) { | ||
context = entry.get_context(); | ||
break; | ||
} | ||
} | ||
// Enforce that builder context is known at this stage. Otherwise first read will fail if the index is a constant. | ||
ASSERT(context != nullptr); | ||
|
||
// Initialize the bus vector entries from the input entries which are un-normalized and possibly constants | ||
for (const auto& entry : entries_in) { | ||
if (entry.is_constant()) { // create a constant witness from the constant | ||
auto const_var_idx = context->put_constant_variable(entry.get_value()); | ||
entries.emplace_back(field_pt::from_witness_index(context, const_var_idx)); | ||
} else { // normalize the raw entry | ||
entries.emplace_back(entry.normalize()); | ||
} | ||
// Add the entry to the bus vector data | ||
context->append_to_bus_vector(bus_idx, entries.back().get_witness_index()); | ||
} | ||
length = entries.size(); | ||
} | ||
|
||
template <typename Builder> field_t<Builder> databus<Builder>::bus_vector::operator[](const field_pt& index) const | ||
{ | ||
// Ensure the read is valid | ||
auto raw_index = static_cast<size_t>(uint256_t(index.get_value()).data[0]); | ||
if (raw_index >= length) { | ||
context->failure("bus_vector: access out of bounds"); | ||
} | ||
|
||
// The read index must be a witness; if constant, add it as a constant variable | ||
uint32_t index_witness_idx = 0; | ||
if (index.is_constant()) { | ||
index_witness_idx = context->put_constant_variable(index.get_value()); | ||
} else { | ||
index_witness_idx = index.normalize().get_witness_index(); | ||
} | ||
|
||
// Read from the bus vector at the specified index. Creates a single read gate | ||
uint32_t output_idx = context->read_bus_vector(bus_idx, index_witness_idx); | ||
return field_pt::from_witness_index(context, output_idx); | ||
} | ||
|
||
template class databus<bb::GoblinUltraCircuitBuilder>; | ||
} // namespace bb::stdlib |
54 changes: 54 additions & 0 deletions
54
barretenberg/cpp/src/barretenberg/stdlib/primitives/databus/databus.hpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
#pragma once | ||
#include "../circuit_builders/circuit_builders_fwd.hpp" | ||
#include "../field/field.hpp" | ||
#include "barretenberg/stdlib_circuit_builders/databus.hpp" | ||
|
||
namespace bb::stdlib { | ||
|
||
template <typename Builder> class databus { | ||
public: | ||
databus() = default; | ||
|
||
private: | ||
class bus_vector { | ||
private: | ||
using field_pt = field_t<Builder>; | ||
|
||
public: | ||
bus_vector(const BusId bus_idx) | ||
: bus_idx(bus_idx){}; | ||
|
||
/** | ||
* @brief Set the entries of the bus vector from possibly unnormalized or constant inputs | ||
* @note A builder/context is assumed to be known at this stage, otherwise the first read will fail if index is | ||
* constant | ||
* | ||
* @tparam Builder | ||
* @param entries_in | ||
*/ | ||
void set_values(const std::vector<field_pt>& entries_in); | ||
|
||
/** | ||
* @brief Read from the bus vector with a witness index value. Creates a read gate | ||
* | ||
* @param index | ||
* @return field_pt | ||
*/ | ||
field_pt operator[](const field_pt& index) const; | ||
|
||
size_t size() const { return length; } | ||
Builder* get_context() const { return context; } | ||
|
||
private: | ||
mutable std::vector<field_pt> entries; // bus vector entries | ||
size_t length = 0; | ||
BusId bus_idx; // Idx of column in bus | ||
mutable Builder* context = nullptr; | ||
}; | ||
|
||
public: | ||
// The columns of the DataBus | ||
bus_vector calldata{ BusId::CALLDATA }; | ||
bus_vector return_data{ BusId::RETURNDATA }; | ||
}; | ||
} // namespace bb::stdlib |
137 changes: 137 additions & 0 deletions
137
barretenberg/cpp/src/barretenberg/stdlib/primitives/databus/databus.test.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
|
||
#include <gtest/gtest.h> | ||
|
||
#include "barretenberg/circuit_checker/circuit_checker.hpp" | ||
#include "barretenberg/numeric/random/engine.hpp" | ||
#include "barretenberg/stdlib_circuit_builders/goblin_ultra_circuit_builder.hpp" | ||
#include "databus.hpp" | ||
|
||
using Builder = GoblinUltraCircuitBuilder; | ||
using field_ct = stdlib::field_t<Builder>; | ||
using witness_ct = stdlib::witness_t<Builder>; | ||
using databus_ct = stdlib::databus<Builder>; | ||
|
||
namespace { | ||
auto& engine = numeric::get_debug_randomness(); | ||
} | ||
|
||
/** | ||
* @brief An expository test demonstrating the functionality of the databus in a small but representative use case | ||
* | ||
*/ | ||
TEST(Databus, CallDataAndReturnData) | ||
{ | ||
Builder builder; | ||
databus_ct databus; | ||
|
||
// The databus is advantageous in situations where we want to pass large amounts of public inputs between circuits | ||
// in a chain (like private function execution in Aztec) but where we only need to use a small subset of those | ||
// values in any given circuit. As an example of this utility, consider the case where the output (return data) is | ||
// defined by simply taking the last two elements of the input (calldata) and summing them together. We can use the | ||
// databus mechanism to establish that the return data was indeed formed in this way. | ||
|
||
// Define some bus data that conform to the pattern described above | ||
std::array<bb::fr, 4> raw_calldata_values = { 4, 5, 6, 7 }; | ||
std::array<bb::fr, 3> raw_return_data_values = { 4, 5, 13 }; // 13 = 6 + 7 | ||
|
||
// Populate the calldata in the databus | ||
std::vector<field_ct> calldata_values; | ||
for (auto& value : raw_calldata_values) { | ||
calldata_values.emplace_back(witness_ct(&builder, value)); | ||
} | ||
databus.calldata.set_values(calldata_values); | ||
|
||
// Populate the return data in the databus | ||
std::vector<field_ct> return_data_values; | ||
for (auto& value : raw_return_data_values) { | ||
return_data_values.emplace_back(witness_ct(&builder, value)); | ||
} | ||
databus.return_data.set_values(return_data_values); | ||
|
||
// Establish that the first two outputs are simply copied over from the inputs. Each 'copy' requires two read gates. | ||
field_ct idx_0(witness_ct(&builder, 0)); | ||
field_ct idx_1(witness_ct(&builder, 1)); | ||
databus.calldata[idx_0].assert_equal(databus.return_data[idx_0]); | ||
databus.calldata[idx_1].assert_equal(databus.return_data[idx_1]); | ||
|
||
// Get the last two entries in calldata and compute their sum | ||
field_ct idx_2(witness_ct(&builder, 2)); | ||
field_ct idx_3(witness_ct(&builder, 3)); | ||
// This line creates an arithmetic gate and two calldata read gates (via operator[]). | ||
field_ct sum = databus.calldata[idx_2] + databus.calldata[idx_3]; | ||
|
||
// Read the last index of the return data. (Creates a return data read gate via operator[]). | ||
field_ct idx(witness_ct(&builder, 2)); | ||
field_ct read_result = databus.return_data[idx]; | ||
|
||
// By construction, the last return data value is equal to the sum of the last two calldata values | ||
EXPECT_EQ(sum.get_value(), read_result.get_value()); | ||
|
||
// Asserting that 'sum' is equal to the read result completes the process of establishing that the corresponding | ||
// return data entry was formed correctly; 'sum' is equal to the read result (enforced via copy constraint) and the | ||
// read result is connected to the value in the databus return data column via the read gate. 'sum' is connected to | ||
// the calldata values via an arithmetic gate and the two calldata read gates. | ||
sum.assert_equal(read_result); | ||
|
||
EXPECT_TRUE(CircuitChecker::check(builder)); | ||
} | ||
|
||
/** | ||
* @brief A failure test demonstrating that trying to prove (via a databus read) that an erroneous value is present in | ||
* the databus will result in an invalid witness. | ||
* | ||
*/ | ||
TEST(Databus, BadReadFailure) | ||
{ | ||
Builder builder; | ||
databus_ct databus; | ||
|
||
// Populate return data with a single arbitrary value | ||
bb::fr actual_value = 13; | ||
databus.return_data.set_values({ witness_ct(&builder, actual_value) }); | ||
|
||
// Read the value from the return data | ||
size_t raw_idx = 0; // read at 0th index | ||
field_ct idx(witness_ct(&builder, raw_idx)); | ||
field_ct read_result = databus.return_data[idx]; | ||
|
||
// The result of the read should be as expected | ||
EXPECT_EQ(actual_value, read_result.get_value()); | ||
|
||
// Since the read gate implicitly created by using operator[] on return data is valid, the witness is valid | ||
EXPECT_TRUE(CircuitChecker::check(builder)); | ||
|
||
// Now assert that the read result is equal to some erroneous value. This effectively updates the return data read | ||
// gate to attest to the erroneous value being present at index 0 in the return data. | ||
field_ct erroneous_value(witness_ct(&builder, actual_value - 1)); | ||
erroneous_value.assert_equal(read_result); | ||
|
||
// Since the read gate is no longer valid, the circuit checker will fail | ||
EXPECT_FALSE(CircuitChecker::check(builder)); | ||
} | ||
|
||
/** | ||
* @brief A failure test demonstrating that a bad input-output 'copy' will lead to an invalid witness | ||
* | ||
*/ | ||
TEST(Databus, BadCopyFailure) | ||
{ | ||
Builder builder; | ||
databus_ct databus; | ||
|
||
// Populate calldata with a single input | ||
bb::fr input = 13; | ||
databus.calldata.set_values({ witness_ct(&builder, input) }); | ||
|
||
// Populate return data with an output different from the input | ||
bb::fr output = input - 1; | ||
databus.return_data.set_values({ witness_ct(&builder, output) }); | ||
|
||
// Attempt to attest that the calldata has been copied into the return data | ||
size_t raw_idx = 0; // read at 0th index | ||
field_ct idx(witness_ct(&builder, raw_idx)); | ||
databus.calldata[idx].assert_equal(databus.return_data[idx]); | ||
|
||
// Since the output data is not a copy of the input, the checker should fail | ||
EXPECT_FALSE(CircuitChecker::check(builder)); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Let's discuss this constant code path. I based the design of this class after the ROM table class which is why it's here. Originally thought it made sense but I'm not sure there's a valid reason to be able to extract databus entries without creating a gate.
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.
Wouldn't the prover be able to replace the value in the witness, since there is nothing connecting the variable to databus column?