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: more robust recursion input generator #8634

Merged
merged 11 commits into from
Sep 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ jobs:
- barretenberg/cpp/src/barretenberg/vm/**
- barretenberg/cpp/src/barretenberg/**/generated/*
- barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.{hpp,cpp}
- barretenberg/cpp/src/barretenberg/bb/main.cpp
Copy link
Contributor Author

Choose a reason for hiding this comment

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

seems obvious to run e2e tests if there is a change to main.cpp...

Copy link
Contributor

Choose a reason for hiding this comment

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

you can add the e2e-all label now to your PR to have it run in CI

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeah but don't you agree it should run automatically if we change the single interface to bb?

Copy link
Contributor

Choose a reason for hiding this comment

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

oh whoops, didn't read this change carefully. Yes I agree with this change

non-docs:
- '!(docs/**)'
non-misc-ci:
Expand Down
41 changes: 15 additions & 26 deletions barretenberg/acir_tests/regenerate_verify_honk_proof_inputs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ set -eu
BIN=${BIN:-../cpp/build/bin/bb}
CRS_PATH=~/.bb-crs
BRANCH=master
VERBOSE=${VERBOSE:-}
RECURSIVE=true
PROOF_NAME="proof_a"
VERBOSE=${VERBOSE:+-v}

if [ -f $BIN ]; then
BIN=$(realpath $BIN)
Expand All @@ -18,31 +16,22 @@ fi

export BRANCH

./reset_acir_tests.sh --rebuild-nargo --programs assert_statement_recursive
# the program for which a proof will be recursively verified
PROGRAM=assert_statement_recursive
# the program containing the recursive verifier
RECURSIVE_PROGRAM=verify_honk_proof

cd acir_tests/assert_statement_recursive
./reset_acir_tests.sh --rebuild-nargo --programs "$PROGRAM"
cd "acir_tests/$PROGRAM"

PROOF_DIR=$PWD/proofs
PROOF_PATH=$PROOF_DIR/$PROOF_NAME
VFLAG=${VERBOSE:+-v}
RFLAG=${RECURSIVE:+-r}

echo "Write VK to file for assert_statement..."
$BIN write_vk_ultra_honk $VFLAG -c $CRS_PATH -o ./target/honk_vk

echo "Write VK as fields for recursion..."
$BIN vk_as_fields_ultra_honk $VFLAG -c $CRS_PATH -k ./target/honk_vk -o ./target/honk_vk_fields.json

echo "Generate proof to file..."
[ -d "$PROOF_DIR" ] || mkdir $PWD/proofs
[ -e "$PROOF_PATH" ] || touch $PROOF_PATH
$BIN prove_ultra_honk $VFLAG -c $CRS_PATH -b ./target/program.json -o "./proofs/honk_$PROOF_NAME"
TOML_DIR=../../../../noir/noir-repo/test_programs/execution_success/"$RECURSIVE_PROGRAM"
if [ ! -d "$TOML_DIR" ]; then
echo "Error: Directory $TOML_DIR does not exist."
exit 1
fi

echo "Write proof as fields for recursion..."
$BIN proof_as_fields_honk $VFLAG -c $CRS_PATH -p "./proofs/honk_$PROOF_NAME" -o "./proofs/honk_${PROOF_NAME}_fields.json"
echo "Generating recursion inputs and writing to directory $TOML_DIR"
$BIN write_recursion_inputs_honk $VERBOSE -c $CRS_PATH -b ./target/program.json -o "$TOML_DIR"

# cd back to barretenberg/acir_tests
cd ../..
python3 update_verify_honk_proof_inputs.py

./reset_acir_tests.sh --programs verify_honk_proof
./reset_acir_tests.sh --programs "$RECURSIVE_PROGRAM"
45 changes: 0 additions & 45 deletions barretenberg/acir_tests/update_verify_honk_proof_inputs.py

This file was deleted.

92 changes: 70 additions & 22 deletions barretenberg/cpp/src/barretenberg/bb/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include "barretenberg/client_ivc/client_ivc.hpp"
#include "barretenberg/common/map.hpp"
#include "barretenberg/common/serialize.hpp"
#include "barretenberg/constants.hpp"
#include "barretenberg/dsl/acir_format/acir_format.hpp"
#include "barretenberg/dsl/acir_format/proof_surgeon.hpp"
#include "barretenberg/dsl/acir_proofs/honk_contract.hpp"
Expand Down Expand Up @@ -133,6 +134,7 @@ std::string vk_to_json(std::vector<bb::fr> const& data)
return format("[", join(map(rotated, [](auto fr) { return format("\"", fr, "\""); })), "]");
}

// WORKTODO: delete?
std::string honk_vk_to_json(std::vector<bb::fr>& data)
{
return format("[", join(map(data, [](auto fr) { return format("\"", fr, "\""); })), "]");
Expand Down Expand Up @@ -340,26 +342,26 @@ void client_ivc_prove_output_all_msgpack(const std::string& bytecodePath,
using TranslatorVK = TranslatorFlavor::VerificationKey;
using DeciderVerificationKey = ClientIVC::DeciderVerificationKey;

using namespace acir_format;

init_bn254_crs(1 << 24);
init_grumpkin_crs(1 << 15);

auto gzippedBincodes = unpack_from_file<std::vector<std::string>>(bytecodePath);
auto witnessMaps = unpack_from_file<std::vector<std::string>>(witnessPath);
auto gzipped_bincodes = unpack_from_file<std::vector<std::string>>(bytecodePath);
auto witness_data = unpack_from_file<std::vector<std::string>>(witnessPath);
std::vector<Program> folding_stack;
for (size_t i = 0; i < gzippedBincodes.size(); i++) {
for (auto [bincode, wit] : zip_view(gzipped_bincodes, witness_data)) {
// TODO(#7371) there is a lot of copying going on in bincode, we should make sure this writes as a buffer in
// the future
std::vector<uint8_t> buffer =
decompressedBuffer(reinterpret_cast<uint8_t*>(&gzippedBincodes[i][0]), gzippedBincodes[i].size()); // NOLINT

std::vector<acir_format::AcirFormat> constraint_systems =
acir_format::program_buf_to_acir_format(buffer,
/*honk_recursion=*/false);
std::vector<uint8_t> witnessBuffer =
decompressedBuffer(reinterpret_cast<uint8_t*>(&witnessMaps[i][0]), witnessMaps[i].size()); // NOLINT
acir_format::WitnessVectorStack witness_stack = acir_format::witness_buf_to_witness_stack(witnessBuffer);
acir_format::AcirProgramStack program_stack{ constraint_systems, witness_stack };
folding_stack.push_back(program_stack.back());
std::vector<uint8_t> constraint_buf =
decompressedBuffer(reinterpret_cast<uint8_t*>(bincode.data()), bincode.size()); // NOLINT
std::vector<uint8_t> witness_buf =
decompressedBuffer(reinterpret_cast<uint8_t*>(wit.data()), wit.size()); // NOLINT

AcirFormat constraints = circuit_buf_to_acir_format(constraint_buf, /*honk_recursion=*/false);
WitnessVector witness = witness_buf_to_witness_data(witness_buf);

folding_stack.push_back(Program{ constraints, witness });
}
// TODO(#7371) dedupe this with the rest of the similar code
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1101): remove use of auto_verify_mode
Expand All @@ -370,8 +372,7 @@ void client_ivc_prove_output_all_msgpack(const std::string& bytecodePath,
// Accumulate the entire program stack into the IVC
for (Program& program : folding_stack) {
// Construct a bberg circuit from the acir representation then accumulate it into the IVC
auto circuit =
acir_format::create_circuit<Builder>(program.constraints, 0, program.witness, false, ivc.goblin.op_queue);
auto circuit = create_circuit<Builder>(program.constraints, 0, program.witness, false, ivc.goblin.op_queue);
ivc.accumulate(circuit);
}

Expand Down Expand Up @@ -1075,8 +1076,7 @@ UltraProver_<Flavor> compute_valid_prover(const std::string& bytecodePath, const
size_t srs_size = builder.get_circuit_subgroup_size(builder.get_total_circuit_size() + num_extra_gates);
init_bn254_crs(srs_size);

Prover prover{ builder };
return prover;
return Prover{ builder };
}

/**
Expand Down Expand Up @@ -1155,13 +1155,11 @@ template <IsUltraFlavor Flavor> bool verify_honk(const std::string& proof_path,
template <IsUltraFlavor Flavor> void write_vk_honk(const std::string& bytecodePath, const std::string& outputPath)
{
using Prover = UltraProver_<Flavor>;
using DeciderProvingKey = DeciderProvingKey_<Flavor>;
using VerificationKey = Flavor::VerificationKey;

// Construct a verification key from a partial form of the proving key which only has precomputed entities
Prover prover = compute_valid_prover<Flavor>(bytecodePath, "");
DeciderProvingKey& prover_inst = *prover.proving_key;
VerificationKey vk(
prover_inst.proving_key); // uses a partial form of the proving key which only has precomputed entities
VerificationKey vk(prover.proving_key->proving_key);

auto serialized_vk = to_buffer(vk);
if (outputPath == "-") {
Expand All @@ -1173,6 +1171,53 @@ template <IsUltraFlavor Flavor> void write_vk_honk(const std::string& bytecodePa
}
}

/**
* @brief Write a toml file containing recursive verifier inputs for a given program + witness
*
* @tparam Flavor
* @param bytecodePath Path to the file containing the serialized circuit
* @param witnessPath Path to the file containing the serialized witness
* @param outputPath Path to write toml file
*/
template <IsUltraFlavor Flavor>
void write_recursion_inputs_honk(const std::string& bytecodePath,
const std::string& witnessPath,
const std::string& outputPath)
{
using Builder = Flavor::CircuitBuilder;
using Prover = UltraProver_<Flavor>;
using VerificationKey = Flavor::VerificationKey;
using FF = Flavor::FF;

bool honk_recursion = true;
auto constraints = get_constraint_system(bytecodePath, /*honk_recursion=*/true);
auto witness = get_witness(witnessPath);
auto builder = acir_format::create_circuit<Builder>(constraints, 0, witness, honk_recursion);

auto num_extra_gates = builder.get_num_gates_added_to_ensure_nonzero_polynomials();
size_t srs_size = builder.get_circuit_subgroup_size(builder.get_total_circuit_size() + num_extra_gates);
init_bn254_crs(srs_size);

// Construct Honk proof and verification key
Prover prover{ builder };
std::vector<FF> proof = prover.construct_proof();
VerificationKey verification_key(prover.proving_key->proving_key);

// Construct a string with the content of the toml file (vk hash, proof, public inputs, vk)
std::string toml_content = acir_format::ProofSurgeon::construct_recursion_inputs_toml_data(proof, verification_key);

// Write all components to the TOML file
std::string toml_path = outputPath + "/Prover.toml";
write_file(toml_path, { toml_content.begin(), toml_content.end() });

// Write to additional dir for noir-sync purposes
std::string part_to_remove = "/noir-repo/test_programs/execution_success";
size_t pos = toml_path.find(part_to_remove);
std::string toml_path_2 = toml_path; // define path here
toml_path_2.erase(pos, part_to_remove.length());
write_file(toml_path_2, { toml_content.begin(), toml_content.end() });
}

/**
* @brief Outputs proof as vector of field elements in readable format.
*
Expand Down Expand Up @@ -1470,6 +1515,9 @@ int main(int argc, char* argv[])
} else if (command == "vk_as_fields") {
std::string output_path = get_option(args, "-o", vk_path + "_fields.json");
vk_as_fields(vk_path, output_path);
} else if (command == "write_recursion_inputs_honk") {
std::string output_path = get_option(args, "-o", "./target");
write_recursion_inputs_honk<UltraFlavor>(bytecode_path, witness_path, output_path);
#ifndef DISABLE_AZTEC_VM
} else if (command == "avm_prove") {
std::filesystem::path avm_bytecode_path = get_option(args, "--avm-bytecode", "./target/avm_bytecode.bin");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
#pragma once
#include "barretenberg/common/map.hpp"
#include "barretenberg/common/serialize.hpp"
#include "barretenberg/ecc/curves/bn254/fr.hpp"
#include "barretenberg/plonk_honk_shared/types/aggregation_object_type.hpp"
#include "barretenberg/serialize/msgpack.hpp"
#include <barretenberg/common/container.hpp>
#include <cstdint>

namespace acir_format {
Expand All @@ -11,7 +13,47 @@ namespace acir_format {
static constexpr size_t HONK_RECURSION_PUBLIC_INPUT_OFFSET = 3;

class ProofSurgeon {
using FF = bb::fr;

// construct a string of the form "[<fr_0 hex>, <fr_1 hex>, ...]"
static std::string to_json(const std::vector<bb::fr>& data)
{
return format("[", bb::join(map(data, [](auto fr) { return format("\"", fr, "\""); }), ", "), "]");
}

public:
/**
* @brief Write a toml file containing the inputs to a noir verify_proof call
*
* @param proof A complete bberg style proof (i.e. contains the public inputs)
* @param verification_key
* @param toml_path
*/
static std::string construct_recursion_inputs_toml_data(std::vector<FF>& proof, const auto& verification_key)
{
// Convert verification key to fields
std::vector<FF> vkey_fields = verification_key.to_field_elements();

// Get public inputs by cutting them out of the proof
const size_t num_public_inputs_to_extract = verification_key.num_public_inputs - bb::AGGREGATION_OBJECT_SIZE;
std::vector<FF> public_inputs =
acir_format::ProofSurgeon::cut_public_inputs_from_proof(proof, num_public_inputs_to_extract);

// Construct json-style output for each component
// FF key_hash{ 0 }; // not used for Honk
std::string proof_json = to_json(proof);
std::string pub_inputs_json = to_json(public_inputs);
std::string vk_json = to_json(vkey_fields);

// Format with labels for noir recursion input
std::string toml_content = "key_hash = " + format("\"", FF(0), "\"") + "\n";
toml_content += "proof = " + proof_json + "\n";
toml_content += "public_inputs = " + pub_inputs_json + "\n";
toml_content += "verification_key = " + vk_json + "\n";

return toml_content;
}

/**
* @brief Reconstruct a bberg style proof from a acir style proof + public inputs
* @details Insert the public inputs in the middle the proof fields after 'inner_public_input_offset' because this
Expand Down
4 changes: 2 additions & 2 deletions barretenberg/cpp/src/barretenberg/flavor/flavor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ class VerificationKey_ : public PrecomputedCommitments {
*
* @return std::vector<FF>
*/
std::vector<FF> to_field_elements()
std::vector<FF> to_field_elements() const
{
using namespace bb::field_conversion;

Expand All @@ -189,7 +189,7 @@ class VerificationKey_ : public PrecomputedCommitments {
serialize_to_field_buffer(this->contains_recursive_proof, elements);
serialize_to_field_buffer(this->recursive_proof_public_input_indices, elements);

for (Commitment& commitment : this->get_all()) {
for (const Commitment& commitment : this->get_all()) {
serialize_to_field_buffer(commitment, elements);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -562,7 +562,7 @@ class MegaFlavor {
/**
* @brief Serialize verification key to field elements
*/
std::vector<FF> to_field_elements()
std::vector<FF> to_field_elements() const
{
using namespace bb::field_conversion;

Expand All @@ -585,7 +585,7 @@ class MegaFlavor {
serialize_to_field_buffer(this->databus_propagation_data.kernel_return_data_public_input_idx, elements);
serialize_to_field_buffer(this->databus_propagation_data.is_kernel, elements);

for (Commitment& commitment : this->get_all()) {
for (const Commitment& commitment : this->get_all()) {
serialize_to_field_buffer(commitment, elements);
}

Expand Down
Loading