Skip to content

Commit

Permalink
feat: Integrate verify_proof calls in mock protocol circuits (#9253)
Browse files Browse the repository at this point in the history
Integrates `verify_proof` calls into the mock kernels in the IVC
integration suite. VKs are computed using a new `write_vk_for_ivc` flow.

---------

Co-authored-by: ledwards2225 <[email protected]>
  • Loading branch information
sirasistant and ledwards2225 authored Dec 5, 2024
1 parent c2c8cc6 commit 7ed89aa
Show file tree
Hide file tree
Showing 19 changed files with 244 additions and 18 deletions.
1 change: 1 addition & 0 deletions barretenberg/cpp/src/barretenberg/bb/api.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ class API {
struct Flags {
std::optional<std::string> output_type; // bytes, fields, bytes_and_fields, fields_msgpack
std::optional<std::string> input_type; // compiletime_stack, runtime_stack
bool no_auto_verify; // TODO(https://github.com/AztecProtocol/barretenberg/issues/1101): remove
};

virtual void prove(const Flags& flags,
Expand Down
58 changes: 55 additions & 3 deletions barretenberg/cpp/src/barretenberg/bb/api_client_ivc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,7 @@ class ClientIVCAPI : public API {
ClientIVC ivc{ { E2E_FULL_TEST_STRUCTURE }, /*auto_verify_mode=*/true };

// Accumulate the entire program stack into the IVC
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1116): remove manual setting of is_kernel once
// databus has been integrated into noir kernel programs
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1116): remove manual setting of is_kernel
bool is_kernel = false;
for (Program& program : folding_stack) {
// Construct a bberg circuit from the acir representation then accumulate it into the IVC
Expand All @@ -163,6 +162,47 @@ class ClientIVCAPI : public API {
return ivc;
};

static ClientIVC _accumulate_without_auto_verify(std::vector<acir_format::AcirProgram>& folding_stack)
{
using Builder = MegaCircuitBuilder;
using Program = acir_format::AcirProgram;

using namespace acir_format;

// TODO(https://github.com/AztecProtocol/barretenberg/issues/1163) set these dynamically
init_bn254_crs(1 << 20);
init_grumpkin_crs(1 << 15);

// 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
ClientIVC ivc{ { E2E_FULL_TEST_STRUCTURE }, /*auto_verify_mode=*/false };

// Accumulate the entire program stack into the IVC
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1116): remove manual setting of is_kernel once
// databus has been integrated into noir kernel programs
bool is_kernel = false;
for (Program& program : folding_stack) {

Builder circuit;

is_kernel = !program.constraints.ivc_recursion_constraints.empty();
if (is_kernel) {
vinfo("Accumulating KERNEL.");
circuit = create_kernel_circuit(program.constraints, ivc, program.witness);
} else {
vinfo("Accumulating APP.");
circuit = create_circuit<Builder>(
program.constraints, /*recursive=*/false, 0, program.witness, false, ivc.goblin.op_queue);
}

// Do one step of ivc accumulator or, if there is only one circuit in the stack, prove that circuit. In this
// case, no work is added to the Goblin opqueue, but VM proofs for trivial inputs are produced.
ivc.accumulate(circuit, /*one_circuit=*/folding_stack.size() == 1);
}

return ivc;
};

public:
void prove(const API::Flags& flags,
const std::filesystem::path& bytecode_path,
Expand All @@ -179,7 +219,19 @@ class ClientIVCAPI : public API {

std::vector<acir_format::AcirProgram> folding_stack =
_build_folding_stack(*flags.input_type, bytecode_path, witness_path);
ClientIVC ivc = _accumulate(folding_stack);

// TODO(https://github.com/AztecProtocol/barretenberg/issues/1163) set these dynamically
init_bn254_crs(1 << 20);
init_grumpkin_crs(1 << 15);

ClientIVC ivc;
if (flags.no_auto_verify) {
vinfo("performing accumulation WITHOUT auto-verify");
ivc = _accumulate_without_auto_verify(folding_stack);
} else {
vinfo("performing accumulation with auto-verify");
ivc = _accumulate(folding_stack);
}
ClientIVC::Proof proof = ivc.prove();

// Write the proof and verification keys into the working directory in 'binary' format (in practice it seems
Expand Down
63 changes: 62 additions & 1 deletion barretenberg/cpp/src/barretenberg/bb/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "barretenberg/constants.hpp"
#include "barretenberg/dsl/acir_format/acir_format.hpp"
#include "barretenberg/dsl/acir_format/acir_to_constraint_buf.hpp"
#include "barretenberg/dsl/acir_format/ivc_recursion_constraint.hpp"
#include "barretenberg/dsl/acir_format/proof_surgeon.hpp"
#include "barretenberg/dsl/acir_proofs/acir_composer.hpp"
#include "barretenberg/dsl/acir_proofs/honk_contract.hpp"
Expand Down Expand Up @@ -833,6 +834,62 @@ void write_vk_honk(const std::string& bytecodePath, const std::string& outputPat
}
}

/**
* @brief Compute and write to file a MegaHonk VK for a circuit to be accumulated in the IVC
* @note This method differes from write_vk_honk<MegaFlavor> in that it handles kernel circuits which require special
* treatment (i.e. construction of mock IVC state to correctly complete the kernel logic).
*
* @param bytecodePath
* @param witnessPath
*/
void write_vk_for_ivc(const std::string& bytecodePath, const std::string& outputPath)
{
using Builder = ClientIVC::ClientCircuit;
using Prover = ClientIVC::MegaProver;
using DeciderProvingKey = ClientIVC::DeciderProvingKey;
using VerificationKey = ClientIVC::MegaVerificationKey;

// TODO(https://github.com/AztecProtocol/barretenberg/issues/1163) set these dynamically
init_bn254_crs(1 << 20);
init_grumpkin_crs(1 << 15);

auto constraints = get_constraint_system(bytecodePath, /*honk_recursion=*/false);
acir_format::WitnessVector witness = {};

TraceSettings trace_settings{ E2E_FULL_TEST_STRUCTURE };

// The presence of ivc recursion constraints determines whether or not the program is a kernel
bool is_kernel = !constraints.ivc_recursion_constraints.empty();

Builder builder;
if (is_kernel) {
// Create a mock IVC instance based on the IVC recursion constraints in the kernel program
ClientIVC mock_ivc = create_mock_ivc_from_constraints(constraints.ivc_recursion_constraints, trace_settings);
builder = acir_format::create_kernel_circuit(constraints, mock_ivc, witness);
} else {
builder = acir_format::create_circuit<Builder>(
constraints, /*recursive=*/false, 0, witness, /*honk_recursion=*/false);
}
// Add public inputs corresponding to pairing point accumulator
builder.add_pairing_point_accumulator(stdlib::recursion::init_default_agg_obj_indices<Builder>(builder));

// Construct the verification key via the prover-constructed proving key with the proper trace settings
auto proving_key = std::make_shared<DeciderProvingKey>(builder, trace_settings);
Prover prover{ proving_key };
init_bn254_crs(prover.proving_key->proving_key.circuit_size);
VerificationKey vk(prover.proving_key->proving_key);

// Write the VK to file as a buffer
auto serialized_vk = to_buffer(vk);
if (outputPath == "-") {
writeRawBytesToStdout(serialized_vk);
vinfo("vk written to stdout");
} else {
write_file(outputPath, serialized_vk);
vinfo("vk written to: ", outputPath);
}
}

/**
* @brief Write a toml file containing recursive verifier inputs for a given program + witness
*
Expand Down Expand Up @@ -1073,7 +1130,8 @@ int main(int argc, char* argv[])

const API::Flags flags = [&args]() {
return API::Flags{ .output_type = get_option(args, "--output_type", "fields_msgpack"),
.input_type = get_option(args, "--input_type", "compiletime_stack") };
.input_type = get_option(args, "--input_type", "compiletime_stack"),
.no_auto_verify = flag_present(args, "--no_auto_verify") };
}();

const std::string command = args[0];
Expand Down Expand Up @@ -1227,6 +1285,9 @@ int main(int argc, char* argv[])
} else if (command == "write_vk_mega_honk") {
std::string output_path = get_option(args, "-o", "./target/vk");
write_vk_honk<MegaFlavor>(bytecode_path, output_path, recursive);
} else if (command == "write_vk_for_ivc") {
std::string output_path = get_option(args, "-o", "./target/vk");
write_vk_for_ivc(bytecode_path, output_path);
} else if (command == "proof_as_fields_honk") {
std::string output_path = get_option(args, "-o", proof_path + "_fields.json");
proof_as_fields_honk(proof_path, output_path);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -656,6 +656,11 @@ void handle_blackbox_func_call(Program::Opcode::BlackBoxFuncCall const& arg,
af.honk_recursion_constraints.push_back(c);
af.original_opcode_indices.honk_recursion_constraints.push_back(opcode_index);
break;
case OINK:
case PG:
af.ivc_recursion_constraints.push_back(c);
af.original_opcode_indices.ivc_recursion_constraints.push_back(opcode_index);
break;
case AVM:
af.avm_recursion_constraints.push_back(c);
af.original_opcode_indices.avm_recursion_constraints.push_back(opcode_index);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ void create_block_constraints(MegaCircuitBuilder& builder,
process_call_data_operations(builder, constraint, has_valid_witness_assignments, init);
// The presence of calldata is used to indicate that the present circuit is a kernel. This is needed in the
// databus consistency checks to indicate that the corresponding return data belongs to a kernel (else an app).
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1165): is_kernel must be known prior to this stage
// since we must determine whether to use create_circuit or create_kernel_circuit. Resolve.
builder.databus_propagation_data.is_kernel = true;
} break;
case BlockType::ReturnData: {
Expand Down
1 change: 1 addition & 0 deletions noir-projects/Earthfile
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ source:
RUN yarn

COPY mega_honk_circuits.json .
COPY ivc_integration_circuits.json .
COPY --dir aztec-nr noir-contracts noir-protocol-circuits mock-protocol-circuits scripts .

build-contracts:
Expand Down
8 changes: 8 additions & 0 deletions noir-projects/ivc_integration_circuits.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[
"mock_private_kernel_init",
"mock_private_kernel_inner",
"mock_private_kernel_reset.*",
"mock_private_kernel_tail.*",
"app_creator",
"app_reader"
]
8 changes: 4 additions & 4 deletions noir-projects/mega_honk_circuits.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[
"private_kernel_init",
"private_kernel_inner",
"private_kernel_reset.*",
"private_kernel_tail.*"
"^private_kernel_init",
"^private_kernel_inner",
"^private_kernel_reset.*",
"^private_kernel_tail.*"
]
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
use dep::mock_types::{
AppPublicInputs, PrivateKernelPublicInputs, PrivateKernelPublicInputsBuilder, TxRequest,
AppPublicInputs, CLIENT_IVC_VERIFICATION_KEY_LENGTH_IN_FIELDS, PrivateKernelPublicInputs,
PrivateKernelPublicInputsBuilder, PROOF_TYPE_OINK, TxRequest,
};

fn main(
tx: TxRequest,
app_inputs: call_data(1) AppPublicInputs,
app_vk: [Field; CLIENT_IVC_VERIFICATION_KEY_LENGTH_IN_FIELDS],
) -> return_data PrivateKernelPublicInputs {
std::verify_proof_with_type(app_vk, [], [], 0, PROOF_TYPE_OINK);

let mut private_kernel_inputs = PrivateKernelPublicInputsBuilder::from_tx(tx);
private_kernel_inputs.ingest_app_inputs(app_inputs);
private_kernel_inputs.finish()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
use dep::mock_types::{AppPublicInputs, PrivateKernelPublicInputs, PrivateKernelPublicInputsBuilder};
use dep::mock_types::{
AppPublicInputs, CLIENT_IVC_VERIFICATION_KEY_LENGTH_IN_FIELDS, PrivateKernelPublicInputs,
PrivateKernelPublicInputsBuilder, PROOF_TYPE_PG,
};

fn main(
prev_kernel_public_inputs: call_data(0) PrivateKernelPublicInputs,
kernel_vk: [Field; CLIENT_IVC_VERIFICATION_KEY_LENGTH_IN_FIELDS],
app_inputs: call_data(1) AppPublicInputs,
app_vk: [Field; CLIENT_IVC_VERIFICATION_KEY_LENGTH_IN_FIELDS],
) -> return_data PrivateKernelPublicInputs {
std::verify_proof_with_type(kernel_vk, [], [], 0, PROOF_TYPE_PG);
std::verify_proof_with_type(app_vk, [], [], 0, PROOF_TYPE_PG);

let mut private_kernel_inputs =
PrivateKernelPublicInputsBuilder::from_previous_kernel(prev_kernel_public_inputs);
private_kernel_inputs.ingest_app_inputs(app_inputs);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
use dep::mock_types::{
MAX_COMMITMENT_READ_REQUESTS_PER_TX, MAX_COMMITMENTS_PER_TX, PrivateKernelPublicInputs,
CLIENT_IVC_VERIFICATION_KEY_LENGTH_IN_FIELDS, MAX_COMMITMENT_READ_REQUESTS_PER_TX,
MAX_COMMITMENTS_PER_TX, PrivateKernelPublicInputs, PROOF_TYPE_PG,
};

// Mock reset kernel that reset read requests.
// It needs hints to locate the commitment that matches the read requests.
fn main(
mut prev_kernel_public_inputs: call_data(0) PrivateKernelPublicInputs,
kernel_vk: [Field; CLIENT_IVC_VERIFICATION_KEY_LENGTH_IN_FIELDS],
commitment_read_hints: [u32; MAX_COMMITMENT_READ_REQUESTS_PER_TX],
) -> return_data PrivateKernelPublicInputs {
std::verify_proof_with_type(kernel_vk, [], [], 0, PROOF_TYPE_PG);

for i in 0..MAX_COMMITMENT_READ_REQUESTS_PER_TX {
if commitment_read_hints[i] != MAX_COMMITMENTS_PER_TX {
assert_eq(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
use dep::mock_types::{
KernelPublicInputs, MAX_COMMITMENT_READ_REQUESTS_PER_TX, PrivateKernelPublicInputs,
CLIENT_IVC_VERIFICATION_KEY_LENGTH_IN_FIELDS, KernelPublicInputs,
MAX_COMMITMENT_READ_REQUESTS_PER_TX, PrivateKernelPublicInputs, PROOF_TYPE_PG,
};

// The tail kernel finishes the client IVC chain exposing the final public inputs with no remaining calls or unfulfilled read requests.
fn main(
prev_kernel_public_inputs: call_data(0) PrivateKernelPublicInputs,
kernel_vk: [Field; CLIENT_IVC_VERIFICATION_KEY_LENGTH_IN_FIELDS],
) -> pub KernelPublicInputs {
std::verify_proof_with_type(kernel_vk, [], [], 0, PROOF_TYPE_PG);

assert_eq(prev_kernel_public_inputs.remaining_calls, 0);
for i in 0..MAX_COMMITMENT_READ_REQUESTS_PER_TX {
assert_eq(prev_kernel_public_inputs.read_requests[i], 0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ type = "lib"
authors = [""]
compiler_version = ">=0.32.0"

[dependencies]
[dependencies]
protocol_types = { path = "../../../noir-protocol-circuits/crates/types" }
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ global MAX_COMMITMENTS_PER_TX: u32 = 4;
global MAX_COMMITMENT_READ_REQUESTS_PER_CALL: u32 = 2;
global MAX_COMMITMENT_READ_REQUESTS_PER_TX: u32 = 4;

pub use protocol_types::constants::{
CLIENT_IVC_VERIFICATION_KEY_LENGTH_IN_FIELDS, PROOF_TYPE_OINK, PROOF_TYPE_PG,
};

struct TxRequest {
number_of_calls: u32,
}
Expand Down
Loading

0 comments on commit 7ed89aa

Please sign in to comment.