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: Verify ClientIVC proofs through Bb binary #7407

Merged
merged 12 commits into from
Jul 11, 2024
2 changes: 2 additions & 0 deletions barretenberg/Earthfile
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ barretenberg-acir-tests-bb:
RUN FLOW=prove_and_verify_mega_honk_program ./run_acir_tests.sh
# Fold and verify an ACIR program stack using ClientIvc
RUN FLOW=fold_and_verify_program ./run_acir_tests.sh fold_basic
# Fold and verify an ACIR program stack using ClientIvc, then natively verify the ClientIVC proof.
RUN FLOW=prove_then_verify_client_ivc ./run_acir_tests.sh fold_basic
# Fold and verify an ACIR program stack using ClientIvc, recursively verify as part of the Tube circuit and produce and verify a Honk proof
RUN FLOW=prove_then_verify_tube ./run_acir_tests.sh fold_basic
# Construct and separately verify a UltraHonk proof for a single program that recursively verifies a Honk proof
Expand Down
9 changes: 9 additions & 0 deletions barretenberg/acir_tests/flows/prove_then_verify_client_ivc.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/sh
set -eu

VFLAG=${VERBOSE:+-v}
BFLAG="-b ./target/program.json"
FLAGS="-c $CRS_PATH $VFLAG"

$BIN client_ivc_prove_output_all $FLAGS $BFLAG
$BIN verify_client_ivc $FLAGS
80 changes: 67 additions & 13 deletions barretenberg/cpp/src/barretenberg/bb/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ std::vector<uint8_t> decompressedBuffer(uint8_t* bytes, size_t size)

void client_ivc_prove_output_all_msgpack(const std::string& bytecodePath,
const std::string& witnessPath,
const std::string& outputPath)
const std::string& outputDir)
{
using Flavor = MegaFlavor; // This is the only option
using Builder = Flavor::CircuitBuilder;
Expand Down Expand Up @@ -360,11 +360,11 @@ void client_ivc_prove_output_all_msgpack(const std::string& bytecodePath,

// Write the proof and verification keys into the working directory in 'binary' format (in practice it seems this
// directory is passed by bb.js)
std::string vkPath = outputPath + "/inst_vk"; // the vk of the last instance
std::string accPath = outputPath + "/pg_acc";
std::string proofPath = outputPath + "/client_ivc_proof";
std::string translatorVkPath = outputPath + "/translator_vk";
std::string eccVkPath = outputPath + "/ecc_vk";
std::string vkPath = outputDir + "/inst_vk"; // the vk of the last instance
std::string accPath = outputDir + "/pg_acc";
std::string proofPath = outputDir + "/client_ivc_proof";
std::string translatorVkPath = outputDir + "/translator_vk";
std::string eccVkPath = outputDir + "/ecc_vk";

auto proof = ivc.prove();
auto eccvm_vk = std::make_shared<ECCVMVK>(ivc.goblin.get_eccvm_proving_key());
Expand All @@ -381,6 +381,48 @@ void client_ivc_prove_output_all_msgpack(const std::string& bytecodePath,
write_file(eccVkPath, to_buffer(eccvm_vk));
}

template <typename T> std::shared_ptr<T> read_to_shared_ptr(const std::filesystem::path& path)
{
return std::make_shared<T>(from_buffer<T>(read_file(path)));
};

/**
* @brief Verifies a client ivc proof and writes the result to stdout
*
* Communication:
* - 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 vk_path Path to the file containing the serialized verification key of the final mega honk instance
* @param accumualtor_path Path to the file containing the serialized protogalaxy accumulator
* @return true (resp., false) if the proof is valid (resp., invalid).
*/
bool verify_client_ivc(const std::filesystem::path& proof_path,
const std::filesystem::path& accumulator_path,
const std::filesystem::path& final_vk_path,
const std::filesystem::path& eccvm_vk_path,
const std::filesystem::path& translator_vk_path)
{
init_bn254_crs(1 << 24);
init_grumpkin_crs(1 << 14);

const auto proof = from_buffer<ClientIVC::Proof>(read_file(proof_path));
const auto accumulator = read_to_shared_ptr<ClientIVC::VerifierInstance>(accumulator_path);
accumulator->verification_key->pcs_verification_key = std::make_shared<VerifierCommitmentKey<curve::BN254>>();
const auto final_vk = read_to_shared_ptr<ClientIVC::VerificationKey>(final_vk_path);
const auto eccvm_vk = read_to_shared_ptr<ECCVMFlavor::VerificationKey>(eccvm_vk_path);
eccvm_vk->pcs_verification_key =
std::make_shared<VerifierCommitmentKey<curve::Grumpkin>>(eccvm_vk->circuit_size + 1);
const auto translator_vk = read_to_shared_ptr<TranslatorFlavor::VerificationKey>(translator_vk_path);
translator_vk->pcs_verification_key = std::make_shared<VerifierCommitmentKey<curve::BN254>>();

const bool verified = ClientIVC::verify(
proof, accumulator, std::make_shared<ClientIVC::VerifierInstance>(final_vk), eccvm_vk, translator_vk);
vinfo("verified: ", verified);
return verified;
}

bool foldAndVerifyProgram(const std::string& bytecodePath, const std::string& witnessPath)
{
using Flavor = MegaFlavor; // This is the only option
Expand Down Expand Up @@ -533,8 +575,7 @@ void prove_tube(const std::string& output_path)
ClientIVC verifier{ builder, input };

verifier.verify(proof);
info("num gates: ", builder->get_num_gates());
info("generating proof");
info("num gates in tube circuit: ", builder->get_num_gates());
using Prover = UltraProver_<UltraFlavor>;
using Verifier = UltraVerifier_<UltraFlavor>;
Prover tube_prover{ *builder };
Expand Down Expand Up @@ -1268,10 +1309,23 @@ int main(int argc, char* argv[])
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1050) we need a verify_client_ivc bb cli command
// TODO(#7371): remove this
if (command == "client_ivc_prove_output_all_msgpack") {
std::string output_path = get_option(args, "-o", "./proofs/proof");
client_ivc_prove_output_all_msgpack(bytecode_path, witness_path, output_path);
std::filesystem::path output_dir = get_option(args, "-o", "./target");
client_ivc_prove_output_all_msgpack(bytecode_path, witness_path, output_dir);
return 0;
}
if (command == "verify_client_ivc") {
std::filesystem::path output_dir = get_option(args, "-o", "./target");
std::filesystem::path client_ivc_proof_path = output_dir / "client_ivc_proof";
std::filesystem::path accumulator_path = output_dir / "pg_acc";
std::filesystem::path final_vk_path = output_dir / "inst_vk";
std::filesystem::path eccvm_vk_path = output_dir / "ecc_vk";
std::filesystem::path translator_vk_path = output_dir / "translator_vk";

return verify_client_ivc(
client_ivc_proof_path, accumulator_path, final_vk_path, eccvm_vk_path, translator_vk_path)
? 0
: 1;
}
if (command == "fold_and_verify_program") {
return foldAndVerifyProgram(bytecode_path, witness_path) ? 0 : 1;
}
Expand All @@ -1289,13 +1343,13 @@ int main(int argc, char* argv[])
std::string output_path = get_option(args, "-o", "./proofs");
prove_honk_output_all<MegaFlavor>(bytecode_path, witness_path, output_path);
} else if (command == "client_ivc_prove_output_all") {
std::string output_path = get_option(args, "-o", "./proofs");
std::string output_path = get_option(args, "-o", "./target");
client_ivc_prove_output_all(bytecode_path, witness_path, output_path);
} else if (command == "prove_tube") {
std::string output_path = get_option(args, "-o", "./proofs");
std::string output_path = get_option(args, "-o", "./target");
prove_tube(output_path);
} else if (command == "verify_tube") {
std::string output_path = get_option(args, "-o", "./proofs");
std::string output_path = get_option(args, "-o", "./target");
auto tube_proof_path = output_path + "/proof";
auto tube_vk_path = output_path + "/vk";
return verify_honk<UltraFlavor>(tube_proof_path, tube_vk_path) ? 0 : 1;
Expand Down
33 changes: 21 additions & 12 deletions barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,31 +58,40 @@ ClientIVC::Proof ClientIVC::prove()
{
max_block_sizes.print(); // print minimum structured sizes for each block
return { fold_output.proof, decider_prove(), goblin.prove() };
}
};

/**
* @brief Verify a full proof of the IVC
*
* @param proof
* @return bool
*/
bool ClientIVC::verify(Proof& proof, const std::vector<std::shared_ptr<VerifierInstance>>& verifier_instances)
bool ClientIVC::verify(const Proof& proof,
const std::shared_ptr<VerifierInstance>& accumulator,
const std::shared_ptr<VerifierInstance>& final_verifier_instance,
const std::shared_ptr<ClientIVC::ECCVMVerificationKey>& eccvm_vk,
const std::shared_ptr<ClientIVC::TranslatorVerificationKey>& translator_vk)
{
// Goblin verification (merge, eccvm, translator)
auto eccvm_vkey = std::make_shared<ECCVMVerificationKey>(goblin.get_eccvm_proving_key());
auto translator_vkey = std::make_shared<TranslatorVerificationKey>(goblin.get_translator_proving_key());
GoblinVerifier goblin_verifier{ eccvm_vkey, translator_vkey };
GoblinVerifier goblin_verifier{ eccvm_vk, translator_vk };
bool goblin_verified = goblin_verifier.verify(proof.goblin_proof);

// Decider verification
ClientIVC::FoldingVerifier folding_verifier({ verifier_instances[0], verifier_instances[1] });
ClientIVC::FoldingVerifier folding_verifier({ accumulator, final_verifier_instance });
auto verifier_accumulator = folding_verifier.verify_folding_proof(proof.folding_proof);

ClientIVC::DeciderVerifier decider_verifier(verifier_accumulator);
bool decision = decider_verifier.verify_proof(proof.decider_proof);
return goblin_verified && decision;
}

/**
* @brief Verify a full proof of the IVC
*
* @param proof
* @return bool
*/
bool ClientIVC::verify(Proof& proof, const std::vector<std::shared_ptr<VerifierInstance>>& verifier_instances)
{
auto eccvm_vk = std::make_shared<ECCVMVerificationKey>(goblin.get_eccvm_proving_key());
auto translator_vk = std::make_shared<TranslatorVerificationKey>(goblin.get_translator_proving_key());
return verify(proof, verifier_instances[0], verifier_instances[1], eccvm_vk, translator_vk);
}

/**
* @brief Internal method for constructing a decider proof
*
Expand Down
6 changes: 6 additions & 0 deletions barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,12 @@ class ClientIVC {

Proof prove();

static bool verify(const Proof& proof,
const std::shared_ptr<VerifierInstance>& accumulator,
const std::shared_ptr<VerifierInstance>& final_verifier_instance,
const std::shared_ptr<ClientIVC::ECCVMVerificationKey>& eccvm_vk,
const std::shared_ptr<ClientIVC::TranslatorVerificationKey>& translator_vk);

bool verify(Proof& proof, const std::vector<std::shared_ptr<VerifierInstance>>& verifier_instances);

bool prove_and_verify();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -691,8 +691,23 @@ template <typename Curve> class ZeroMorphVerifier_ {
* @brief Utility for native batch multiplication of group elements
* @note This is used only for native verification and is not optimized for efficiency
*/
static Commitment batch_mul_native(const std::vector<Commitment>& points, const std::vector<FF>& scalars)
static Commitment batch_mul_native(const std::vector<Commitment>& _points, const std::vector<FF>& _scalars)
{
std::vector<Commitment> points;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why underscore? Underscore signifies unused variable elsewhere in codebase

Copy link
Contributor Author

Choose a reason for hiding this comment

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

err, here I meant that the variable is 'hidden' / about to be replaces, not sure better notation for that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I would rather just use a filter but I'm trying to get this in

std::vector<FF> scalars;
for (auto [point, scalar] : zip_view(_points, _scalars)) {
// TODO(https://github.com/AztecProtocol/barretenberg/issues/866) Special handling of point at infinity here
// due to incorrect serialization.
if (!scalar.is_zero() && !point.is_point_at_infinity() && !point.y.is_zero()) {
points.emplace_back(point);
scalars.emplace_back(scalar);
}
}

if (points.empty()) {
return Commitment::infinity();
}

auto result = points[0] * scalars[0];
for (size_t idx = 1; idx < scalars.size(); ++idx) {
result = result + points[idx] * scalars[idx];
Expand Down
Loading