Skip to content

Commit a4dfe13

Browse files
authored
feat: unified create circuit from acir (#10440)
The main goal of this PR is to allow for all circuits (including kernels) to be created using a single `create_circuit()` method. (Prior to this PR things had diverged for kernels/ivc_recursion_constraints and a separate `create_kernel_circuit()` method was required). To facilitate this and as general cleanup, this PR introduces struct `ProgramMetadata` so that the create_circuit interface is reduced to `create_circuit(AcirProgram&, ProgramMetadata&)`. Note: `ProgramMetadata` simply contains all the stuff that used to be individual defaulted inputs to the create_circuit methods (plus a pointer to a ClientIVC instance). This is a better pattern but I haven't yet made an effort to address whether some of the parameters can be removed altogether. (It may be that they cannot).
1 parent 2c36088 commit a4dfe13

12 files changed

+406
-436
lines changed

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

+30-72
Original file line numberDiff line numberDiff line change
@@ -125,79 +125,38 @@ class ClientIVCAPI : public API {
125125
return folding_stack;
126126
};
127127

128-
static ClientIVC _accumulate(std::vector<acir_format::AcirProgram>& folding_stack)
128+
static std::shared_ptr<ClientIVC> _accumulate(std::vector<acir_format::AcirProgram>& folding_stack,
129+
bool auto_verify = false)
129130
{
130131
using Builder = MegaCircuitBuilder;
131132
using Program = acir_format::AcirProgram;
132-
133133
using namespace acir_format;
134134

135-
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1163) set these dynamically
136-
init_bn254_crs(1 << 20);
137-
init_grumpkin_crs(1 << 15);
135+
vinfo("performing accumulation with auto-verify = ", auto_verify);
136+
137+
TraceSettings trace_settings{ E2E_FULL_TEST_STRUCTURE };
138+
auto ivc = std::make_shared<ClientIVC>(trace_settings, auto_verify);
138139

139-
// TODO(#7371) dedupe this with the rest of the similar code
140-
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1101): remove use of auto_verify_mode
141-
ClientIVC ivc{ { E2E_FULL_TEST_STRUCTURE }, /*auto_verify_mode=*/true };
140+
const ProgramMetadata metadata{ ivc };
142141

143142
// Accumulate the entire program stack into the IVC
144143
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1116): remove manual setting of is_kernel
145144
bool is_kernel = false;
146145
for (Program& program : folding_stack) {
147146
// Construct a bberg circuit from the acir representation then accumulate it into the IVC
148-
Builder circuit = acir_format::create_circuit<Builder>(
149-
program.constraints, true, 0, program.witness, false, ivc.goblin.op_queue);
147+
Builder circuit = acir_format::create_circuit<Builder>(program, metadata);
150148

151149
// Set the internal is_kernel flag based on the local mechanism only if it has not already been set to true
152-
if (!circuit.databus_propagation_data.is_kernel) {
153-
circuit.databus_propagation_data.is_kernel = is_kernel;
150+
if (ivc->auto_verify_mode) {
151+
if (!circuit.databus_propagation_data.is_kernel) {
152+
circuit.databus_propagation_data.is_kernel = is_kernel;
153+
}
154+
is_kernel = !is_kernel;
154155
}
155-
is_kernel = !is_kernel;
156156

157157
// Do one step of ivc accumulator or, if there is only one circuit in the stack, prove that circuit. In this
158158
// case, no work is added to the Goblin opqueue, but VM proofs for trivials inputs are produced.
159-
ivc.accumulate(circuit, /*one_circuit=*/folding_stack.size() == 1);
160-
}
161-
162-
return ivc;
163-
};
164-
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);
159+
ivc->accumulate(circuit, /*one_circuit=*/folding_stack.size() == 1);
201160
}
202161

203162
return ivc;
@@ -217,33 +176,27 @@ class ClientIVCAPI : public API {
217176
throw_or_abort("No input_type or input_type not supported");
218177
}
219178

220-
std::vector<acir_format::AcirProgram> folding_stack =
221-
_build_folding_stack(*flags.input_type, bytecode_path, witness_path);
222-
223179
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1163) set these dynamically
224180
init_bn254_crs(1 << 20);
225181
init_grumpkin_crs(1 << 15);
226182

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-
}
235-
ClientIVC::Proof proof = ivc.prove();
183+
std::vector<acir_format::AcirProgram> folding_stack =
184+
_build_folding_stack(*flags.input_type, bytecode_path, witness_path);
185+
186+
bool auto_verify = !flags.no_auto_verify;
187+
std::shared_ptr<ClientIVC> ivc = _accumulate(folding_stack, auto_verify);
188+
ClientIVC::Proof proof = ivc->prove();
236189

237190
// Write the proof and verification keys into the working directory in 'binary' format (in practice it seems
238191
// this directory is passed by bb.js)
239192
vinfo("writing ClientIVC proof and vk...");
240193
write_file(output_dir / "client_ivc_proof", to_buffer(proof));
241194

242-
auto eccvm_vk = std::make_shared<ECCVMFlavor::VerificationKey>(ivc.goblin.get_eccvm_proving_key());
195+
auto eccvm_vk = std::make_shared<ECCVMFlavor::VerificationKey>(ivc->goblin.get_eccvm_proving_key());
243196
auto translator_vk =
244-
std::make_shared<TranslatorFlavor::VerificationKey>(ivc.goblin.get_translator_proving_key());
197+
std::make_shared<TranslatorFlavor::VerificationKey>(ivc->goblin.get_translator_proving_key());
245198
write_file(output_dir / "client_ivc_vk",
246-
to_buffer(ClientIVC::VerificationKey{ ivc.honk_vk, eccvm_vk, translator_vk }));
199+
to_buffer(ClientIVC::VerificationKey{ ivc->honk_vk, eccvm_vk, translator_vk }));
247200
};
248201

249202
/**
@@ -286,10 +239,15 @@ class ClientIVCAPI : public API {
286239
if (!flags.input_type || !(*flags.input_type == "compiletime_stack" || *flags.input_type == "runtime_stack")) {
287240
throw_or_abort("No input_type or input_type not supported");
288241
}
242+
243+
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1163) set these dynamically
244+
init_bn254_crs(1 << 20);
245+
init_grumpkin_crs(1 << 15);
246+
289247
std::vector<acir_format::AcirProgram> folding_stack =
290248
_build_folding_stack(*flags.input_type, bytecode_path, witness_path);
291-
ClientIVC ivc = _accumulate(folding_stack);
292-
const bool verified = ivc.prove_and_verify();
249+
std::shared_ptr<ClientIVC> ivc = _accumulate(folding_stack, /*auto_verify=*/true);
250+
const bool verified = ivc->prove_and_verify();
293251
return verified;
294252
};
295253

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

+52-61
Original file line numberDiff line numberDiff line change
@@ -110,21 +110,15 @@ bool proveAndVerify(const std::string& bytecodePath, const bool recursive, const
110110
}
111111

112112
template <IsUltraFlavor Flavor>
113-
bool proveAndVerifyHonkAcirFormat(acir_format::AcirFormat constraint_system,
114-
const bool recursive,
115-
acir_format::WitnessVector witness)
113+
bool proveAndVerifyHonkAcirFormat(acir_format::AcirProgram program, acir_format::ProgramMetadata metadata)
116114
{
117115
using Builder = Flavor::CircuitBuilder;
118116
using Prover = UltraProver_<Flavor>;
119117
using Verifier = UltraVerifier_<Flavor>;
120118
using VerificationKey = Flavor::VerificationKey;
121119

122-
bool honk_recursion = false;
123-
if constexpr (IsAnyOf<Flavor, UltraFlavor>) {
124-
honk_recursion = true;
125-
}
126120
// Construct a bberg circuit from the acir representation
127-
auto builder = acir_format::create_circuit<Builder>(constraint_system, recursive, 0, witness, honk_recursion);
121+
auto builder = acir_format::create_circuit<Builder>(program, metadata);
128122

129123
// Construct Honk proof
130124
Prover prover{ builder };
@@ -149,15 +143,15 @@ bool proveAndVerifyHonkAcirFormat(acir_format::AcirFormat constraint_system,
149143
template <IsUltraFlavor Flavor>
150144
bool proveAndVerifyHonk(const std::string& bytecodePath, const bool recursive, const std::string& witnessPath)
151145
{
152-
bool honk_recursion = false;
153-
if constexpr (IsAnyOf<Flavor, UltraFlavor>) {
154-
honk_recursion = true;
155-
}
146+
constexpr bool honk_recursion = IsAnyOf<Flavor, UltraFlavor>;
147+
const acir_format::ProgramMetadata metadata{ .recursive = recursive, .honk_recursion = honk_recursion };
148+
156149
// Populate the acir constraint system and witness from gzipped data
157-
auto constraint_system = get_constraint_system(bytecodePath, honk_recursion);
158-
auto witness = get_witness(witnessPath);
150+
acir_format::AcirProgram program;
151+
program.constraints = get_constraint_system(bytecodePath, metadata.honk_recursion);
152+
program.witness = get_witness(witnessPath);
159153

160-
return proveAndVerifyHonkAcirFormat<Flavor>(constraint_system, recursive, witness);
154+
return proveAndVerifyHonkAcirFormat<Flavor>(program, metadata);
161155
}
162156

163157
/**
@@ -171,14 +165,14 @@ bool proveAndVerifyHonk(const std::string& bytecodePath, const bool recursive, c
171165
template <IsUltraFlavor Flavor>
172166
bool proveAndVerifyHonkProgram(const std::string& bytecodePath, const bool recursive, const std::string& witnessPath)
173167
{
174-
bool honk_recursion = false;
175-
if constexpr (IsAnyOf<Flavor, UltraFlavor>) {
176-
honk_recursion = true;
177-
}
178-
auto program_stack = acir_format::get_acir_program_stack(bytecodePath, witnessPath, honk_recursion);
168+
constexpr bool honk_recursion = IsAnyOf<Flavor, UltraFlavor>;
169+
const acir_format::ProgramMetadata metadata{ .recursive = recursive, .honk_recursion = honk_recursion };
170+
171+
auto program_stack = acir_format::get_acir_program_stack(bytecodePath, witnessPath, metadata.honk_recursion);
172+
179173
while (!program_stack.empty()) {
180-
auto stack_item = program_stack.back();
181-
if (!proveAndVerifyHonkAcirFormat<Flavor>(stack_item.constraints, recursive, stack_item.witness)) {
174+
auto program = program_stack.back();
175+
if (!proveAndVerifyHonkAcirFormat<Flavor>(program, metadata)) {
182176
return false;
183177
}
184178
program_stack.pop_back();
@@ -329,25 +323,29 @@ void gateCount(const std::string& bytecodePath, bool recursive, bool honk_recurs
329323
// All circuit reports will be built into the string below
330324
std::string functions_string = "{\"functions\": [\n ";
331325
auto constraint_systems = get_constraint_systems(bytecodePath, honk_recursion);
326+
327+
const acir_format::ProgramMetadata metadata{ .recursive = recursive,
328+
.honk_recursion = honk_recursion,
329+
.collect_gates_per_opcode = true };
332330
size_t i = 0;
333-
for (auto constraint_system : constraint_systems) {
334-
auto builder = acir_format::create_circuit<Builder>(
335-
constraint_system, recursive, 0, {}, honk_recursion, std::make_shared<bb::ECCOpQueue>(), true);
331+
for (const auto& constraint_system : constraint_systems) {
332+
acir_format::AcirProgram program{ constraint_system };
333+
auto builder = acir_format::create_circuit<Builder>(program, metadata);
336334
builder.finalize_circuit(/*ensure_nonzero=*/true);
337335
size_t circuit_size = builder.num_gates;
338336
vinfo("Calculated circuit size in gateCount: ", circuit_size);
339337

340338
// Build individual circuit report
341339
std::string gates_per_opcode_str;
342-
for (size_t j = 0; j < constraint_system.gates_per_opcode.size(); j++) {
343-
gates_per_opcode_str += std::to_string(constraint_system.gates_per_opcode[j]);
344-
if (j != constraint_system.gates_per_opcode.size() - 1) {
340+
for (size_t j = 0; j < program.constraints.gates_per_opcode.size(); j++) {
341+
gates_per_opcode_str += std::to_string(program.constraints.gates_per_opcode[j]);
342+
if (j != program.constraints.gates_per_opcode.size() - 1) {
345343
gates_per_opcode_str += ",";
346344
}
347345
}
348346

349347
auto result_string = format("{\n \"acir_opcodes\": ",
350-
constraint_system.num_acir_opcodes,
348+
program.constraints.num_acir_opcodes,
351349
",\n \"circuit_size\": ",
352350
circuit_size,
353351
",\n \"gates_per_opcode\": [",
@@ -713,17 +711,15 @@ UltraProver_<Flavor> compute_valid_prover(const std::string& bytecodePath,
713711
using Builder = Flavor::CircuitBuilder;
714712
using Prover = UltraProver_<Flavor>;
715713

716-
bool honk_recursion = false;
717-
if constexpr (IsAnyOf<Flavor, UltraFlavor, UltraKeccakFlavor, UltraRollupFlavor>) {
718-
honk_recursion = true;
719-
}
720-
auto constraint_system = get_constraint_system(bytecodePath, honk_recursion);
721-
acir_format::WitnessVector witness = {};
714+
constexpr bool honk_recursion = IsAnyOf<Flavor, UltraFlavor, UltraKeccakFlavor, UltraRollupFlavor>;
715+
const acir_format::ProgramMetadata metadata{ .recursive = recursive, .honk_recursion = honk_recursion };
716+
717+
acir_format::AcirProgram program{ get_constraint_system(bytecodePath, metadata.honk_recursion) };
722718
if (!witnessPath.empty()) {
723-
witness = get_witness(witnessPath);
719+
program.witness = get_witness(witnessPath);
724720
}
725721

726-
auto builder = acir_format::create_circuit<Builder>(constraint_system, recursive, 0, witness, honk_recursion);
722+
auto builder = acir_format::create_circuit<Builder>(program, metadata);
727723
auto prover = Prover{ builder };
728724
init_bn254_crs(prover.proving_key->proving_key.circuit_size);
729725
return std::move(prover);
@@ -846,28 +842,23 @@ void write_vk_for_ivc(const std::string& bytecodePath, const std::string& output
846842
using Prover = ClientIVC::MegaProver;
847843
using DeciderProvingKey = ClientIVC::DeciderProvingKey;
848844
using VerificationKey = ClientIVC::MegaVerificationKey;
845+
using Program = acir_format::AcirProgram;
846+
using ProgramMetadata = acir_format::ProgramMetadata;
849847

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

854-
auto constraints = get_constraint_system(bytecodePath, /*honk_recursion=*/false);
855-
acir_format::WitnessVector witness = {};
852+
Program program{ get_constraint_system(bytecodePath, /*honk_recursion=*/false), /*witness=*/{} };
853+
auto& ivc_constraints = program.constraints.ivc_recursion_constraints;
856854

857855
TraceSettings trace_settings{ E2E_FULL_TEST_STRUCTURE };
858856

859-
// The presence of ivc recursion constraints determines whether or not the program is a kernel
860-
bool is_kernel = !constraints.ivc_recursion_constraints.empty();
857+
const ProgramMetadata metadata{ .ivc = ivc_constraints.empty()
858+
? nullptr
859+
: create_mock_ivc_from_constraints(ivc_constraints, trace_settings) };
860+
Builder builder = acir_format::create_circuit<Builder>(program, metadata);
861861

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

@@ -907,10 +898,12 @@ void write_recursion_inputs_honk(const std::string& bytecodePath,
907898
using VerificationKey = Flavor::VerificationKey;
908899
using FF = Flavor::FF;
909900

910-
bool honk_recursion = true;
911-
auto constraints = get_constraint_system(bytecodePath, honk_recursion);
912-
auto witness = get_witness(witnessPath);
913-
auto builder = acir_format::create_circuit<Builder>(constraints, recursive, 0, witness, honk_recursion);
901+
const acir_format::ProgramMetadata metadata{ .recursive = recursive, .honk_recursion = true };
902+
903+
acir_format::AcirProgram program;
904+
program.constraints = get_constraint_system(bytecodePath, metadata.honk_recursion);
905+
program.witness = get_witness(witnessPath);
906+
auto builder = acir_format::create_circuit<Builder>(program, metadata);
914907

915908
// Construct Honk proof and verification key
916909
Prover prover{ builder };
@@ -1058,15 +1051,13 @@ void prove_honk_output_all(const std::string& bytecodePath,
10581051
using Prover = UltraProver_<Flavor>;
10591052
using VerificationKey = Flavor::VerificationKey;
10601053

1061-
bool honk_recursion = false;
1062-
if constexpr (IsAnyOf<Flavor, UltraFlavor, UltraKeccakFlavor>) {
1063-
honk_recursion = true;
1064-
}
1054+
constexpr bool honk_recursion = IsAnyOf<Flavor, UltraFlavor, UltraKeccakFlavor>;
1055+
const acir_format::ProgramMetadata metadata{ .recursive = recursive, .honk_recursion = honk_recursion };
10651056

1066-
auto constraint_system = get_constraint_system(bytecodePath, honk_recursion);
1067-
auto witness = get_witness(witnessPath);
1057+
acir_format::AcirProgram program{ get_constraint_system(bytecodePath, metadata.honk_recursion),
1058+
get_witness(witnessPath) };
10681059

1069-
auto builder = acir_format::create_circuit<Builder>(constraint_system, recursive, 0, witness, honk_recursion);
1060+
auto builder = acir_format::create_circuit<Builder>(program, metadata);
10701061

10711062
// Construct Honk proof
10721063
Prover prover{ builder };

0 commit comments

Comments
 (0)