Skip to content

Commit 7ed89aa

Browse files
feat: Integrate verify_proof calls in mock protocol circuits (#9253)
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 <l.edwards.d@gmail.com>
1 parent c2c8cc6 commit 7ed89aa

File tree

19 files changed

+244
-18
lines changed

19 files changed

+244
-18
lines changed

barretenberg/cpp/src/barretenberg/bb/api.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ class API {
88
struct Flags {
99
std::optional<std::string> output_type; // bytes, fields, bytes_and_fields, fields_msgpack
1010
std::optional<std::string> input_type; // compiletime_stack, runtime_stack
11+
bool no_auto_verify; // TODO(https://github.com/AztecProtocol/barretenberg/issues/1101): remove
1112
};
1213

1314
virtual void prove(const Flags& flags,

barretenberg/cpp/src/barretenberg/bb/api_client_ivc.hpp

+55-3
Original file line numberDiff line numberDiff line change
@@ -141,8 +141,7 @@ class ClientIVCAPI : public API {
141141
ClientIVC ivc{ { E2E_FULL_TEST_STRUCTURE }, /*auto_verify_mode=*/true };
142142

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

165+
static ClientIVC _accumulate_without_auto_verify(std::vector<acir_format::AcirProgram>& folding_stack)
166+
{
167+
using Builder = MegaCircuitBuilder;
168+
using Program = acir_format::AcirProgram;
169+
170+
using namespace acir_format;
171+
172+
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1163) set these dynamically
173+
init_bn254_crs(1 << 20);
174+
init_grumpkin_crs(1 << 15);
175+
176+
// TODO(#7371) dedupe this with the rest of the similar code
177+
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1101): remove use of auto_verify_mode
178+
ClientIVC ivc{ { E2E_FULL_TEST_STRUCTURE }, /*auto_verify_mode=*/false };
179+
180+
// Accumulate the entire program stack into the IVC
181+
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1116): remove manual setting of is_kernel once
182+
// databus has been integrated into noir kernel programs
183+
bool is_kernel = false;
184+
for (Program& program : folding_stack) {
185+
186+
Builder circuit;
187+
188+
is_kernel = !program.constraints.ivc_recursion_constraints.empty();
189+
if (is_kernel) {
190+
vinfo("Accumulating KERNEL.");
191+
circuit = create_kernel_circuit(program.constraints, ivc, program.witness);
192+
} else {
193+
vinfo("Accumulating APP.");
194+
circuit = create_circuit<Builder>(
195+
program.constraints, /*recursive=*/false, 0, program.witness, false, ivc.goblin.op_queue);
196+
}
197+
198+
// Do one step of ivc accumulator or, if there is only one circuit in the stack, prove that circuit. In this
199+
// case, no work is added to the Goblin opqueue, but VM proofs for trivial inputs are produced.
200+
ivc.accumulate(circuit, /*one_circuit=*/folding_stack.size() == 1);
201+
}
202+
203+
return ivc;
204+
};
205+
166206
public:
167207
void prove(const API::Flags& flags,
168208
const std::filesystem::path& bytecode_path,
@@ -179,7 +219,19 @@ class ClientIVCAPI : public API {
179219

180220
std::vector<acir_format::AcirProgram> folding_stack =
181221
_build_folding_stack(*flags.input_type, bytecode_path, witness_path);
182-
ClientIVC ivc = _accumulate(folding_stack);
222+
223+
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1163) set these dynamically
224+
init_bn254_crs(1 << 20);
225+
init_grumpkin_crs(1 << 15);
226+
227+
ClientIVC ivc;
228+
if (flags.no_auto_verify) {
229+
vinfo("performing accumulation WITHOUT auto-verify");
230+
ivc = _accumulate_without_auto_verify(folding_stack);
231+
} else {
232+
vinfo("performing accumulation with auto-verify");
233+
ivc = _accumulate(folding_stack);
234+
}
183235
ClientIVC::Proof proof = ivc.prove();
184236

185237
// Write the proof and verification keys into the working directory in 'binary' format (in practice it seems

barretenberg/cpp/src/barretenberg/bb/main.cpp

+62-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "barretenberg/constants.hpp"
1010
#include "barretenberg/dsl/acir_format/acir_format.hpp"
1111
#include "barretenberg/dsl/acir_format/acir_to_constraint_buf.hpp"
12+
#include "barretenberg/dsl/acir_format/ivc_recursion_constraint.hpp"
1213
#include "barretenberg/dsl/acir_format/proof_surgeon.hpp"
1314
#include "barretenberg/dsl/acir_proofs/acir_composer.hpp"
1415
#include "barretenberg/dsl/acir_proofs/honk_contract.hpp"
@@ -833,6 +834,62 @@ void write_vk_honk(const std::string& bytecodePath, const std::string& outputPat
833834
}
834835
}
835836

837+
/**
838+
* @brief Compute and write to file a MegaHonk VK for a circuit to be accumulated in the IVC
839+
* @note This method differes from write_vk_honk<MegaFlavor> in that it handles kernel circuits which require special
840+
* treatment (i.e. construction of mock IVC state to correctly complete the kernel logic).
841+
*
842+
* @param bytecodePath
843+
* @param witnessPath
844+
*/
845+
void write_vk_for_ivc(const std::string& bytecodePath, const std::string& outputPath)
846+
{
847+
using Builder = ClientIVC::ClientCircuit;
848+
using Prover = ClientIVC::MegaProver;
849+
using DeciderProvingKey = ClientIVC::DeciderProvingKey;
850+
using VerificationKey = ClientIVC::MegaVerificationKey;
851+
852+
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1163) set these dynamically
853+
init_bn254_crs(1 << 20);
854+
init_grumpkin_crs(1 << 15);
855+
856+
auto constraints = get_constraint_system(bytecodePath, /*honk_recursion=*/false);
857+
acir_format::WitnessVector witness = {};
858+
859+
TraceSettings trace_settings{ E2E_FULL_TEST_STRUCTURE };
860+
861+
// The presence of ivc recursion constraints determines whether or not the program is a kernel
862+
bool is_kernel = !constraints.ivc_recursion_constraints.empty();
863+
864+
Builder builder;
865+
if (is_kernel) {
866+
// Create a mock IVC instance based on the IVC recursion constraints in the kernel program
867+
ClientIVC mock_ivc = create_mock_ivc_from_constraints(constraints.ivc_recursion_constraints, trace_settings);
868+
builder = acir_format::create_kernel_circuit(constraints, mock_ivc, witness);
869+
} else {
870+
builder = acir_format::create_circuit<Builder>(
871+
constraints, /*recursive=*/false, 0, witness, /*honk_recursion=*/false);
872+
}
873+
// Add public inputs corresponding to pairing point accumulator
874+
builder.add_pairing_point_accumulator(stdlib::recursion::init_default_agg_obj_indices<Builder>(builder));
875+
876+
// Construct the verification key via the prover-constructed proving key with the proper trace settings
877+
auto proving_key = std::make_shared<DeciderProvingKey>(builder, trace_settings);
878+
Prover prover{ proving_key };
879+
init_bn254_crs(prover.proving_key->proving_key.circuit_size);
880+
VerificationKey vk(prover.proving_key->proving_key);
881+
882+
// Write the VK to file as a buffer
883+
auto serialized_vk = to_buffer(vk);
884+
if (outputPath == "-") {
885+
writeRawBytesToStdout(serialized_vk);
886+
vinfo("vk written to stdout");
887+
} else {
888+
write_file(outputPath, serialized_vk);
889+
vinfo("vk written to: ", outputPath);
890+
}
891+
}
892+
836893
/**
837894
* @brief Write a toml file containing recursive verifier inputs for a given program + witness
838895
*
@@ -1073,7 +1130,8 @@ int main(int argc, char* argv[])
10731130

10741131
const API::Flags flags = [&args]() {
10751132
return API::Flags{ .output_type = get_option(args, "--output_type", "fields_msgpack"),
1076-
.input_type = get_option(args, "--input_type", "compiletime_stack") };
1133+
.input_type = get_option(args, "--input_type", "compiletime_stack"),
1134+
.no_auto_verify = flag_present(args, "--no_auto_verify") };
10771135
}();
10781136

10791137
const std::string command = args[0];
@@ -1227,6 +1285,9 @@ int main(int argc, char* argv[])
12271285
} else if (command == "write_vk_mega_honk") {
12281286
std::string output_path = get_option(args, "-o", "./target/vk");
12291287
write_vk_honk<MegaFlavor>(bytecode_path, output_path, recursive);
1288+
} else if (command == "write_vk_for_ivc") {
1289+
std::string output_path = get_option(args, "-o", "./target/vk");
1290+
write_vk_for_ivc(bytecode_path, output_path);
12301291
} else if (command == "proof_as_fields_honk") {
12311292
std::string output_path = get_option(args, "-o", proof_path + "_fields.json");
12321293
proof_as_fields_honk(proof_path, output_path);

barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -656,6 +656,11 @@ void handle_blackbox_func_call(Program::Opcode::BlackBoxFuncCall const& arg,
656656
af.honk_recursion_constraints.push_back(c);
657657
af.original_opcode_indices.honk_recursion_constraints.push_back(opcode_index);
658658
break;
659+
case OINK:
660+
case PG:
661+
af.ivc_recursion_constraints.push_back(c);
662+
af.original_opcode_indices.ivc_recursion_constraints.push_back(opcode_index);
663+
break;
659664
case AVM:
660665
af.avm_recursion_constraints.push_back(c);
661666
af.original_opcode_indices.avm_recursion_constraints.push_back(opcode_index);

barretenberg/cpp/src/barretenberg/dsl/acir_format/block_constraint.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ void create_block_constraints(MegaCircuitBuilder& builder,
8686
process_call_data_operations(builder, constraint, has_valid_witness_assignments, init);
8787
// The presence of calldata is used to indicate that the present circuit is a kernel. This is needed in the
8888
// databus consistency checks to indicate that the corresponding return data belongs to a kernel (else an app).
89+
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1165): is_kernel must be known prior to this stage
90+
// since we must determine whether to use create_circuit or create_kernel_circuit. Resolve.
8991
builder.databus_propagation_data.is_kernel = true;
9092
} break;
9193
case BlockType::ReturnData: {

noir-projects/Earthfile

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ source:
2020
RUN yarn
2121

2222
COPY mega_honk_circuits.json .
23+
COPY ivc_integration_circuits.json .
2324
COPY --dir aztec-nr noir-contracts noir-protocol-circuits mock-protocol-circuits scripts .
2425

2526
build-contracts:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[
2+
"mock_private_kernel_init",
3+
"mock_private_kernel_inner",
4+
"mock_private_kernel_reset.*",
5+
"mock_private_kernel_tail.*",
6+
"app_creator",
7+
"app_reader"
8+
]

noir-projects/mega_honk_circuits.json

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[
2-
"private_kernel_init",
3-
"private_kernel_inner",
4-
"private_kernel_reset.*",
5-
"private_kernel_tail.*"
2+
"^private_kernel_init",
3+
"^private_kernel_inner",
4+
"^private_kernel_reset.*",
5+
"^private_kernel_tail.*"
66
]

noir-projects/mock-protocol-circuits/crates/mock-private-kernel-init/src/main.nr

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
use dep::mock_types::{
2-
AppPublicInputs, PrivateKernelPublicInputs, PrivateKernelPublicInputsBuilder, TxRequest,
2+
AppPublicInputs, CLIENT_IVC_VERIFICATION_KEY_LENGTH_IN_FIELDS, PrivateKernelPublicInputs,
3+
PrivateKernelPublicInputsBuilder, PROOF_TYPE_OINK, TxRequest,
34
};
45

56
fn main(
67
tx: TxRequest,
78
app_inputs: call_data(1) AppPublicInputs,
9+
app_vk: [Field; CLIENT_IVC_VERIFICATION_KEY_LENGTH_IN_FIELDS],
810
) -> return_data PrivateKernelPublicInputs {
11+
std::verify_proof_with_type(app_vk, [], [], 0, PROOF_TYPE_OINK);
12+
913
let mut private_kernel_inputs = PrivateKernelPublicInputsBuilder::from_tx(tx);
1014
private_kernel_inputs.ingest_app_inputs(app_inputs);
1115
private_kernel_inputs.finish()

noir-projects/mock-protocol-circuits/crates/mock-private-kernel-inner/src/main.nr

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
1-
use dep::mock_types::{AppPublicInputs, PrivateKernelPublicInputs, PrivateKernelPublicInputsBuilder};
1+
use dep::mock_types::{
2+
AppPublicInputs, CLIENT_IVC_VERIFICATION_KEY_LENGTH_IN_FIELDS, PrivateKernelPublicInputs,
3+
PrivateKernelPublicInputsBuilder, PROOF_TYPE_PG,
4+
};
25

36
fn main(
47
prev_kernel_public_inputs: call_data(0) PrivateKernelPublicInputs,
8+
kernel_vk: [Field; CLIENT_IVC_VERIFICATION_KEY_LENGTH_IN_FIELDS],
59
app_inputs: call_data(1) AppPublicInputs,
10+
app_vk: [Field; CLIENT_IVC_VERIFICATION_KEY_LENGTH_IN_FIELDS],
611
) -> return_data PrivateKernelPublicInputs {
12+
std::verify_proof_with_type(kernel_vk, [], [], 0, PROOF_TYPE_PG);
13+
std::verify_proof_with_type(app_vk, [], [], 0, PROOF_TYPE_PG);
14+
715
let mut private_kernel_inputs =
816
PrivateKernelPublicInputsBuilder::from_previous_kernel(prev_kernel_public_inputs);
917
private_kernel_inputs.ingest_app_inputs(app_inputs);

noir-projects/mock-protocol-circuits/crates/mock-private-kernel-reset/src/main.nr

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
use dep::mock_types::{
2-
MAX_COMMITMENT_READ_REQUESTS_PER_TX, MAX_COMMITMENTS_PER_TX, PrivateKernelPublicInputs,
2+
CLIENT_IVC_VERIFICATION_KEY_LENGTH_IN_FIELDS, MAX_COMMITMENT_READ_REQUESTS_PER_TX,
3+
MAX_COMMITMENTS_PER_TX, PrivateKernelPublicInputs, PROOF_TYPE_PG,
34
};
45

56
// Mock reset kernel that reset read requests.
67
// It needs hints to locate the commitment that matches the read requests.
78
fn main(
89
mut prev_kernel_public_inputs: call_data(0) PrivateKernelPublicInputs,
10+
kernel_vk: [Field; CLIENT_IVC_VERIFICATION_KEY_LENGTH_IN_FIELDS],
911
commitment_read_hints: [u32; MAX_COMMITMENT_READ_REQUESTS_PER_TX],
1012
) -> return_data PrivateKernelPublicInputs {
13+
std::verify_proof_with_type(kernel_vk, [], [], 0, PROOF_TYPE_PG);
14+
1115
for i in 0..MAX_COMMITMENT_READ_REQUESTS_PER_TX {
1216
if commitment_read_hints[i] != MAX_COMMITMENTS_PER_TX {
1317
assert_eq(

noir-projects/mock-protocol-circuits/crates/mock-private-kernel-tail/src/main.nr

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
use dep::mock_types::{
2-
KernelPublicInputs, MAX_COMMITMENT_READ_REQUESTS_PER_TX, PrivateKernelPublicInputs,
2+
CLIENT_IVC_VERIFICATION_KEY_LENGTH_IN_FIELDS, KernelPublicInputs,
3+
MAX_COMMITMENT_READ_REQUESTS_PER_TX, PrivateKernelPublicInputs, PROOF_TYPE_PG,
34
};
45

56
// The tail kernel finishes the client IVC chain exposing the final public inputs with no remaining calls or unfulfilled read requests.
67
fn main(
78
prev_kernel_public_inputs: call_data(0) PrivateKernelPublicInputs,
9+
kernel_vk: [Field; CLIENT_IVC_VERIFICATION_KEY_LENGTH_IN_FIELDS],
810
) -> pub KernelPublicInputs {
11+
std::verify_proof_with_type(kernel_vk, [], [], 0, PROOF_TYPE_PG);
12+
913
assert_eq(prev_kernel_public_inputs.remaining_calls, 0);
1014
for i in 0..MAX_COMMITMENT_READ_REQUESTS_PER_TX {
1115
assert_eq(prev_kernel_public_inputs.read_requests[i], 0);

noir-projects/mock-protocol-circuits/crates/mock-types/Nargo.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ type = "lib"
44
authors = [""]
55
compiler_version = ">=0.32.0"
66

7-
[dependencies]
7+
[dependencies]
8+
protocol_types = { path = "../../../noir-protocol-circuits/crates/types" }

noir-projects/mock-protocol-circuits/crates/mock-types/src/lib.nr

+4
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ global MAX_COMMITMENTS_PER_TX: u32 = 4;
33
global MAX_COMMITMENT_READ_REQUESTS_PER_CALL: u32 = 2;
44
global MAX_COMMITMENT_READ_REQUESTS_PER_TX: u32 = 4;
55

6+
pub use protocol_types::constants::{
7+
CLIENT_IVC_VERIFICATION_KEY_LENGTH_IN_FIELDS, PROOF_TYPE_OINK, PROOF_TYPE_PG,
8+
};
9+
610
struct TxRequest {
711
number_of_calls: u32,
812
}

0 commit comments

Comments
 (0)