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!: Separate public inputs from proof in acir composer #2618

Closed
wants to merge 45 commits into from
Closed
Show file tree
Hide file tree
Changes from 36 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
5035024
temporary: add methods to split the proof and take in public inputs s…
kevaundray Oct 2, 2023
2b438db
modify c-binds to use those methods
kevaundray Oct 2, 2023
934a578
modify browser test app to account for separate public inputs
kevaundray Oct 2, 2023
75b9dc7
modify bb binary to account for separate public inputs
kevaundray Oct 2, 2023
65acca8
modify bb.js node binary to account for separate public inputs
kevaundray Oct 2, 2023
902520a
exports.json file was regenerated
kevaundray Oct 2, 2023
8dadcfa
regenerate api file -- unfortunately my linter changed lines in the f…
kevaundray Oct 2, 2023
6376452
formatter
kevaundray Oct 2, 2023
06e2ad7
add code to not read the public inputs file if there are no public in…
kevaundray Oct 2, 2023
ab683a1
temporarily switch args
kevaundray Oct 2, 2023
adc2357
put publicInputs in serializeProofIntoFields
kevaundray Oct 2, 2023
4a026aa
modify binary to account for new cbind api
kevaundray Oct 2, 2023
f6af120
always put public inputs vector first
kevaundray Oct 2, 2023
06ea909
regenerate exports.json
kevaundray Oct 2, 2023
93218e7
conditionally read the public inputs
kevaundray Oct 2, 2023
7aa3855
typo
kevaundray Oct 2, 2023
0c6ca7f
yarn
kevaundray Oct 2, 2023
0df922c
put in separate PR
kevaundray Oct 2, 2023
0e3bfa7
cleanup bb
kevaundray Oct 2, 2023
b463a8a
cleanup bb.js binary
kevaundray Oct 2, 2023
23d2067
remove _splitted methods
kevaundray Oct 2, 2023
4461139
modify bb binary; since we removed _splitted methods
kevaundray Oct 2, 2023
c2a6773
Update barretenberg/acir_tests/run_acir_tests_browser.sh
kevaundray Oct 2, 2023
b4233da
formatting fix
kevaundray Oct 2, 2023
362552a
change outwards facing API to not mention proofWithoutPublicInputs
kevaundray Oct 3, 2023
8576164
using proof instead of proof_without_public_inputs in cbinds
kevaundray Oct 3, 2023
51714c8
redo bindings
kevaundray Oct 3, 2023
a811d36
modify binaries
kevaundray Oct 3, 2023
2d07550
linter
kevaundray Oct 3, 2023
0b49345
multi:
kevaundray Oct 3, 2023
b669013
bb: -public_inputs -> _public_inputs
kevaundray Oct 3, 2023
aeea25e
lint
kevaundray Oct 3, 2023
dab3ec3
add get_file_size method
kevaundray Oct 3, 2023
2b60a6c
use get_file_size instead of passing number of public inputs
kevaundray Oct 3, 2023
24d0bc6
do not pass vk to proofAsFields
kevaundray Oct 3, 2023
7f539fe
use methods in container to refactor
kevaundray Oct 3, 2023
c62a6cd
change c-style cast and add back comment
kevaundray Oct 3, 2023
21e67bd
tellg returns 0 for empty file, if there was an error reading it, rea…
kevaundray Oct 3, 2023
4a369ab
inline splitVector and remove concatenate vector
kevaundray Oct 3, 2023
c7b5102
reduce diff
kevaundray Oct 3, 2023
00a19ba
Merge branch 'master' into kw/separate-public-inputs
kevaundray Oct 3, 2023
fc6aefd
better variable name
kevaundray Oct 3, 2023
0cafaa8
Merge remote-tracking branch 'origin/master' into kw/separate-public-…
kevaundray Oct 12, 2023
036d1f3
fix merge
kevaundray Oct 12, 2023
fe50733
deduplicate public witness indices
kevaundray Oct 12, 2023
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
4 changes: 2 additions & 2 deletions barretenberg/acir_tests/browser-test-app/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,14 @@ async function runTest(
);

const acirComposer = await api.acirNewAcirComposer(CIRCUIT_SIZE);
const proof = await api.acirCreateProof(
const [publicInputs, proof] = await api.acirCreateProof(
acirComposer,
bytecode,
witness,
true
);
debug(`verifying...`);
const verified = await api.acirVerifyProof(acirComposer, proof, true);
const verified = await api.acirVerifyProof(acirComposer, publicInputs, proof, true);
debug(`verified: ${verified}`);

await api.destroy();
Expand Down
2 changes: 1 addition & 1 deletion barretenberg/acir_tests/flows/all_cmds.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@ $BIN verify -k vk -p proof $FLAGS
# Grep to determine success.
$BIN contract -k vk $BFLAG -o - | grep "Verification Key Hash" > /dev/null
# Use jq to determine success.
$BIN proof_as_fields -k vk -p proof -o - | jq . > /dev/null
$BIN proof_as_fields -p proof -o - | jq . > /dev/null
$BIN vk_as_fields -k vk -o - > vk_as_fields | jq . > /dev/null
19 changes: 12 additions & 7 deletions barretenberg/cpp/src/barretenberg/bb/file_io.hpp
Original file line number Diff line number Diff line change
@@ -1,27 +1,32 @@
#pragma once
#include <barretenberg/common/log.hpp>
#include <cstdint>
#include <fstream>
#include <vector>

inline std::vector<uint8_t> read_file(const std::string& filename)
inline std::streamsize get_file_size(const std::string& filename)
{
// Open the file in binary mode and move to the end.
std::ifstream file(filename, std::ios::binary | std::ios::ate);
if (!file) {
throw std::runtime_error("Unable to open file: " + filename);
}

// Get the file size.
std::streamsize size = file.tellg();
return file.tellg();
}

inline std::vector<uint8_t> read_file(const std::string& filename)
{
std::streamsize size = get_file_size(filename);

if (size <= 0) {
throw std::runtime_error("File is empty or there's an error reading it: " + filename);
}

// Create a vector with enough space for the file data.
std::vector<uint8_t> fileData((size_t)size);

// Go back to the start of the file and read all its contents.
file.seekg(0, std::ios::beg);
// Since the file was closed after getting its size,
// we need to open it again.
std::ifstream file(filename, std::ios::binary);
file.read(reinterpret_cast<char*>(fileData.data()), size);

return fileData;
Expand Down
70 changes: 54 additions & 16 deletions barretenberg/cpp/src/barretenberg/bb/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <barretenberg/dsl/acir_format/acir_to_constraint_buf.hpp>
#include <barretenberg/dsl/acir_proofs/acir_composer.hpp>
#include <barretenberg/srs/global_crs.hpp>
#include <cstddef>
#include <iostream>
#include <stdexcept>
#include <string>
Expand Down Expand Up @@ -38,6 +39,29 @@ acir_format::acir_format get_constraint_system(std::string const& bytecode_path)
return acir_format::circuit_buf_to_acir_format(bytecode);
}

std::vector<uint8_t> read_public_inputs(std::string const& public_inputs_path)
{
// If the number of public inputs is 0, then read_file will trigger a failure
// because the file will be empty. We therefore check if the file is empty
// before trying to read it.
if (get_file_size(public_inputs_path) <= 0) {
return {};
}
return read_file(public_inputs_path);
}

// When given a proof path, we simply append "-public_inputs" to it.
// to derive a file path for the public inputs.
//
// The alternative is to have the user pass in a path for their proof
// and a path for their public inputs. This is more verbose and
// its likely that the user will want their public inputs to be
// in the same directory as their proof.
std::string public_inputs_path_from_proof_path(std::string const& proof_path)
{
return proof_path + "_public_inputs";
}

/**
* @brief Proves and Verifies an ACIR circuit
*
Expand All @@ -56,8 +80,10 @@ bool proveAndVerify(const std::string& bytecodePath, const std::string& witnessP
auto acir_composer = new acir_proofs::AcirComposer(MAX_CIRCUIT_SIZE, verbose);
auto constraint_system = get_constraint_system(bytecodePath);
auto witness = get_witness(witnessPath);
auto proof = acir_composer->create_proof(srs::get_crs_factory(), constraint_system, witness, recursive);
auto verified = acir_composer->verify_proof(proof, recursive);
auto [public_inputs, proof] =
acir_composer->create_proof(srs::get_crs_factory(), constraint_system, witness, recursive);

auto verified = acir_composer->verify_proof(public_inputs, proof, recursive);

vinfo("verified: ", verified);
return verified;
Expand All @@ -73,24 +99,29 @@ bool proveAndVerify(const std::string& bytecodePath, const std::string& witnessP
* @param bytecodePath Path to the file containing the serialized circuit
* @param witnessPath Path to the file containing the serialized witness
* @param recursive Whether to use recursive proof generation of non-recursive
* @param outputPath Path to write the proof to
* @param outputProofPath Path to write the proof to - The proof does not contain public inputs
*/
void prove(const std::string& bytecodePath,
const std::string& witnessPath,
bool recursive,
const std::string& outputPath)
const std::string& outputProofPath)
{
auto acir_composer = new acir_proofs::AcirComposer(MAX_CIRCUIT_SIZE, verbose);
auto constraint_system = get_constraint_system(bytecodePath);
auto witness = get_witness(witnessPath);
auto proof = acir_composer->create_proof(srs::get_crs_factory(), constraint_system, witness, recursive);
auto [public_inputs, proof] =
acir_composer->create_proof(srs::get_crs_factory(), constraint_system, witness, recursive);

if (outputPath == "-") {
if (outputProofPath == "-") {
writeRawBytesToStdout(public_inputs);
writeRawBytesToStdout(proof);
vinfo("proof written to stdout");
vinfo("proof and public inputs written to stdout");
} else {
write_file(outputPath, proof);
vinfo("proof written to: ", outputPath);
auto outputPublicInputsPath = public_inputs_path_from_proof_path(outputProofPath);
write_file(outputPublicInputsPath, public_inputs);
write_file(outputProofPath, proof);
vinfo("proof written to: ", outputProofPath);
vinfo("public inputs written to: ", outputPublicInputsPath);
}
}

Expand Down Expand Up @@ -123,7 +154,7 @@ void gateCount(const std::string& bytecodePath)
* - proc_exit: A boolean value is returned indicating whether the proof is valid.
* an exit code of 0 will be returned for success and 1 for failure.
*
* @param proof_path Path to the file containing the serialized proof
* @param proof_path Path to the file containing the serialized proof without public inputs preprended
* @param recursive Whether to use recursive proof generation of non-recursive
* @param vk_path Path to the file containing the serialized verification key
* @return true If the proof is valid
Expand All @@ -134,7 +165,11 @@ bool verify(const std::string& proof_path, bool recursive, const std::string& vk
auto acir_composer = new acir_proofs::AcirComposer(MAX_CIRCUIT_SIZE, verbose);
auto vk_data = from_buffer<plonk::verification_key_data>(read_file(vk_path));
acir_composer->load_verification_key(barretenberg::srs::get_crs_factory(), std::move(vk_data));
auto verified = acir_composer->verify_proof(read_file(proof_path), recursive);

auto public_inputs_path = public_inputs_path_from_proof_path(proof_path);
std::vector<uint8_t> public_inputs = read_public_inputs(public_inputs_path);
auto proof = read_file(proof_path);
auto verified = acir_composer->verify_proof(public_inputs, proof, recursive);

vinfo("verified: ", verified);

Expand Down Expand Up @@ -218,14 +253,17 @@ void contract(const std::string& output_path, const std::string& vk_path)
*
*
* @param proof_path Path to the file containing the serialized proof
* @param vk_path Path to the file containing the serialized verification key
* @param output_path Path to write the proof to
*/
void proofAsFields(const std::string& proof_path, std::string const& vk_path, const std::string& output_path)
void proofAsFields(const std::string& proof_path, const std::string& output_path)
kevaundray marked this conversation as resolved.
Show resolved Hide resolved
{

auto public_inputs_path = public_inputs_path_from_proof_path(proof_path);
auto proof = read_file(proof_path);
std::vector<uint8_t> public_inputs = read_public_inputs(public_inputs_path);

auto acir_composer = new acir_proofs::AcirComposer(MAX_CIRCUIT_SIZE, verbose);
auto vk_data = from_buffer<plonk::verification_key_data>(read_file(vk_path));
auto data = acir_composer->serialize_proof_into_fields(read_file(proof_path), vk_data.num_public_inputs);
auto data = acir_composer->serialize_proof_into_fields(public_inputs, proof);
auto json = format("[", join(map(data, [](auto fr) { return format("\"", fr, "\""); })), "]");

if (output_path == "-") {
Expand Down Expand Up @@ -363,7 +401,7 @@ int main(int argc, char* argv[])
writeVk(bytecode_path, output_path);
} else if (command == "proof_as_fields") {
std::string output_path = getOption(args, "-o", proof_path + "_fields.json");
proofAsFields(proof_path, vk_path, output_path);
proofAsFields(proof_path, output_path);
} else if (command == "vk_as_fields") {
std::string output_path = getOption(args, "-o", vk_path + "_fields.json");
vkAsFields(vk_path, output_path);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "acir_composer.hpp"
#include "barretenberg/common/container.hpp"
#include "barretenberg/common/serialize.hpp"
#include "barretenberg/common/throw_or_abort.hpp"
#include "barretenberg/dsl/acir_format/acir_format.hpp"
Expand All @@ -17,6 +18,56 @@ AcirComposer::AcirComposer(size_t size_hint, bool verbose)
, verbose_(verbose)
{}

/**
* @brief Splits a vector into two vectors,
* the first containing the first 32 * k elements, and the second containing
* the rest.
*
* @param original - The original vector to split
* @param k - The number of 32 bytes to remove
* @return std::pair<std::vector<uint8_t>, std::vector<uint8_t>>
*/
std::pair<std::vector<uint8_t>, std::vector<uint8_t>> splitVector(const std::vector<uint8_t>& original, uint32_t k)
{
uint32_t elementsToRemove = 32 * k;

if (original.size() < elementsToRemove) {
throw_or_abort("Not enough elements in the original vector");
}

std::vector<uint8_t> removed = slice(original, 0, elementsToRemove);
std::vector<uint8_t> rest = slice(original, elementsToRemove);

return {
rest,
removed,
};
}

/**
* @brief Splits the proof into two vectors.
*
* Barretenberg returns a proof that is concatenated with the public inputs.
* This function splits the proof into two vectors, one containing the public inputs,
* and the other containing the proof without the public inputs.
*
* @param proof - proof with the public inputs preprended
* @param num_public_inputs - the number of public inputs prepended to the proof
* @return std::pair<std::vector<uint8_t>, std::vector<uint8_t>>
*/
std::pair<std::vector<uint8_t>, std::vector<uint8_t>> split_proof(std::vector<uint8_t>& proof,
uint32_t num_public_inputs)
{
auto [proof_without_public_inputs, public_inputs] = splitVector(proof, num_public_inputs);
return { public_inputs, proof_without_public_inputs };
}

std::vector<uint8_t> concatenateVectors(const std::vector<uint8_t>& firstVector,
const std::vector<uint8_t>& secondVector)
{
return join({ firstVector, secondVector });
}

void AcirComposer::create_circuit(acir_format::acir_format& constraint_system)
{
builder_ = acir_format::create_circuit(constraint_system, size_hint_);
Expand Down Expand Up @@ -52,7 +103,7 @@ void AcirComposer::init_proving_key(
proving_key_ = composer_.compute_proving_key(builder_);
}

std::vector<uint8_t> AcirComposer::create_proof(
std::pair<std::vector<uint8_t>, std::vector<uint8_t>> AcirComposer::create_proof(
std::shared_ptr<barretenberg::srs::factories::CrsFactory<curve::BN254>> const& crs_factory,
acir_format::acir_format& constraint_system,
acir_format::WitnessVector& witness,
Expand Down Expand Up @@ -97,7 +148,9 @@ std::vector<uint8_t> AcirComposer::create_proof(
proof = prover.construct_proof().proof_data;
}
vinfo("done.");
return proof;

auto num_public_inputs = static_cast<uint32_t>(constraint_system.public_inputs.size());
return split_proof(proof, num_public_inputs);
}

std::shared_ptr<proof_system::plonk::verification_key> AcirComposer::init_verification_key()
Expand All @@ -117,8 +170,13 @@ void AcirComposer::load_verification_key(
composer_ = acir_format::Composer(proving_key_, verification_key_);
}

bool AcirComposer::verify_proof(std::vector<uint8_t> const& proof, bool is_recursive)
bool AcirComposer::verify_proof(std::vector<uint8_t> const& public_inputs,
std::vector<uint8_t> const& proof_without_public_inputs,
bool is_recursive)
{

auto proof = concatenateVectors(public_inputs, proof_without_public_inputs);

if (!verification_key_) {
vinfo("computing verification key...");
verification_key_ = composer_.compute_verification_key(builder_);
Expand Down Expand Up @@ -150,12 +208,13 @@ std::string AcirComposer::get_solidity_verifier()
* Use this method to get the witness values!
*
* @param proof
* @param num_inner_public_inputs - number of public inputs on the proof being serialized
* @param public_inputs - number of public inputs associated with the proof
*/
std::vector<barretenberg::fr> AcirComposer::serialize_proof_into_fields(std::vector<uint8_t> const& proof,
size_t num_inner_public_inputs)
std::vector<barretenberg::fr> AcirComposer::serialize_proof_into_fields(std::vector<uint8_t> const& public_inputs,
std::vector<uint8_t> const& proof)
{
transcript::StandardTranscript transcript(proof,
auto num_inner_public_inputs = public_inputs.size() / 32;
transcript::StandardTranscript transcript(concatenateVectors(public_inputs, proof),
acir_format::Composer::create_manifest(num_inner_public_inputs),
transcript::HashType::PlookupPedersenBlake3s,
16);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class AcirComposer {
void init_proving_key(std::shared_ptr<barretenberg::srs::factories::CrsFactory<curve::BN254>> const& crs_factory,
acir_format::acir_format& constraint_system);

std::vector<uint8_t> create_proof(
std::pair<std::vector<uint8_t>, std::vector<uint8_t>> create_proof(
std::shared_ptr<barretenberg::srs::factories::CrsFactory<curve::BN254>> const& crs_factory,
acir_format::acir_format& constraint_system,
acir_format::WitnessVector& witness,
Expand All @@ -29,14 +29,14 @@ class AcirComposer {

std::shared_ptr<proof_system::plonk::verification_key> init_verification_key();

bool verify_proof(std::vector<uint8_t> const& proof, bool is_recursive);
bool verify_proof(std::vector<uint8_t> const& public_inputs, std::vector<uint8_t> const& proof, bool is_recursive);

std::string get_solidity_verifier();
size_t get_exact_circuit_size() { return exact_circuit_size_; };
size_t get_total_circuit_size() { return total_circuit_size_; };

std::vector<barretenberg::fr> serialize_proof_into_fields(std::vector<uint8_t> const& proof,
size_t num_inner_public_inputs);
std::vector<barretenberg::fr> serialize_proof_into_fields(std::vector<uint8_t> const& public_inputs,
std::vector<uint8_t> const& proof);

std::vector<barretenberg::fr> serialize_verification_key_into_fields();

Expand Down
Loading