Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
mock_circuit_producer.hpp
Go to the documentation of this file.
1#pragma once
2
8
9using namespace bb;
10
11namespace {
12
19class MockDatabusProducer {
20 private:
21 using ClientCircuit = Chonk::ClientCircuit;
22 using Flavor = MegaFlavor;
23 using FF = Flavor::FF;
24 using BusDataArray = std::vector<FF>;
25
26 static constexpr size_t BUS_ARRAY_SIZE = 3; // arbitrary length of mock bus inputs
28 BusDataArray kernel_return_data;
29
30 FF dummy_return_val = 1; // use simple return val for easier test debugging
31
32 BusDataArray generate_random_bus_array()
33 {
34 BusDataArray result;
35 for (size_t i = 0; i < BUS_ARRAY_SIZE; ++i) {
36 result.emplace_back(dummy_return_val);
37 }
38 dummy_return_val += 1;
39 return result;
40 }
41
42 public:
46 void populate_app_databus(ClientCircuit& circuit)
47 {
48 for (auto& app_data : app_return_data) {
49 if (app_data.empty()) {
50 app_data = generate_random_bus_array();
51 for (auto& val : app_data) {
52 circuit.add_public_return_data(circuit.add_variable(val));
53 }
54 return;
55 }
56 }
57 };
58
63 void populate_kernel_databus(ClientCircuit& circuit)
64 {
65 // Populate kernel calldata from previous kernel return data (if it exists)
66 for (auto& val : kernel_return_data) {
67 circuit.add_public_calldata(BusId::KERNEL_CALLDATA, circuit.add_variable(val));
68 }
69 // Populate app calldata from app return data (if it exists), then clear the app return data
70 for (size_t idx = 0; idx < app_return_data.size(); ++idx) {
71 for (auto& val : app_return_data[idx]) {
72 circuit.add_public_calldata(static_cast<BusId>(idx + 1), circuit.add_variable(val));
73 }
74 app_return_data[idx].clear();
75 }
76
77 // Mock the return data for the present kernel circuit
78 kernel_return_data = generate_random_bus_array();
79 for (auto& val : kernel_return_data) {
80 circuit.add_public_return_data(circuit.add_variable(val));
81 }
82 };
83};
84
89struct TestSettings {
90 // number of public inputs to manually add to circuits, by default this would be 0 because we use the
91 // MockDatabusProducer to test public inputs handling
92 size_t num_public_inputs = 0;
93 // by default we will create more complex apps and kernel with various types of gates but in case we want to
94 // specifically test overflow behaviour or unstructured circuits we can manually construct simple circuits with a
95 // specified number of gates
96 size_t log2_num_gates = 0;
97};
98
107class PrivateFunctionExecutionMockCircuitProducer {
108 using ClientCircuit = Chonk::ClientCircuit;
109 using Flavor = MegaFlavor;
111
112 size_t circuit_counter = 0;
113 std::vector<bool> is_kernel_flags;
114
115 MockDatabusProducer mock_databus;
116 bool large_first_app = true;
117 constexpr static size_t NUM_TRAILING_KERNELS = 3; // reset, tail, hiding
118
119 public:
120 size_t total_num_circuits = 0;
121
122 PrivateFunctionExecutionMockCircuitProducer(size_t num_app_circuits, bool large_first_app = true)
123 : large_first_app(large_first_app)
124 {
125 for (size_t i = 0; i < num_app_circuits / MAX_APPS_PER_KERNEL; ++i) {
126 for (size_t idx = 0; idx < MAX_APPS_PER_KERNEL; ++idx) {
127 is_kernel_flags.emplace_back(false);
128 }
129 is_kernel_flags.emplace_back(true);
130 }
131 if (num_app_circuits % MAX_APPS_PER_KERNEL != 0) {
132 for (size_t idx = 0; idx < num_app_circuits % MAX_APPS_PER_KERNEL; ++idx) {
133 is_kernel_flags.emplace_back(false);
134 }
135 is_kernel_flags.emplace_back(true);
136 }
137 for (size_t i = 0; i < NUM_TRAILING_KERNELS; ++i) {
138 is_kernel_flags.emplace_back(true);
139 }
140 total_num_circuits = is_kernel_flags.size();
141 }
142
143 PrivateFunctionExecutionMockCircuitProducer(std::vector<bool> leading_is_kernel_flags, bool large_first_app = false)
144 : is_kernel_flags(std::move(leading_is_kernel_flags))
145 , large_first_app(large_first_app)
146 {
147 BB_ASSERT(!is_kernel_flags.empty(), "Mock circuit layout must contain at least one leading circuit");
148 BB_ASSERT_EQ(is_kernel_flags[0], false, "Mock circuit layout must start with an app circuit");
149 for (size_t i = 0; i < NUM_TRAILING_KERNELS; ++i) {
150 is_kernel_flags.emplace_back(true);
151 }
152 total_num_circuits = is_kernel_flags.size();
153 }
154
158 static std::shared_ptr<VerificationKey> get_verification_key(ClientCircuit& builder_in,
159 bool is_hiding_kernel = false)
160 {
161 // This is a workaround to ensure that the circuit is finalized before we create the verification key
162 // In practice, this should not be needed as the circuit will be finalized when it is accumulated into the IVC
163 // but this is a workaround for the test setup.
165
166 // Deepcopy the opqueue to avoid modifying the original one when finalising the circuit
168
169 if (is_hiding_kernel) {
171 return std::make_shared<VerificationKey>(prover_instance->get_precomputed());
172 }
173 auto prover_instance = std::make_shared<Chonk::ProverInstance>(builder);
174 return std::make_shared<VerificationKey>(prover_instance->get_precomputed());
175 }
176
183 ClientCircuit create_next_circuit(Chonk& ivc,
184 size_t log2_num_gates = 0,
185 size_t num_public_inputs = 0,
186 bool check_circuit_sizes = false)
187 {
188 const bool is_kernel = is_kernel_flags[circuit_counter++];
189 const bool use_large_circuit = large_first_app && (circuit_counter == 1); // first circuit is size 2^19
190 // Check if this is one of the trailing kernels (reset, tail, hiding)
191 const bool is_trailing_kernel = (ivc.num_circuits_accumulated >= ivc.get_num_circuits() - NUM_TRAILING_KERNELS);
192
193 ClientCircuit circuit{ ivc.goblin.op_queue };
194 // if the number of gates is specified we just add a number of arithmetic gates
195 if (log2_num_gates != 0) {
196 MockCircuits::construct_arithmetic_circuit(circuit, log2_num_gates, /* include_public_inputs= */ false);
197 // Add some public inputs
198 for (size_t i = 0; i < num_public_inputs; ++i) {
199 circuit.add_public_variable(typename Flavor::FF(13634816 + i)); // arbitrary number
200 }
201 } else {
202 // If the number of gates is not specified we create a structured mock circuit
203 if (is_kernel) {
204 // For trailing kernels (reset, tail, hiding), skip the expensive mock kernel logic to match real Noir
205 // flows. These kernels are simpler and mainly contain the completion logic added by Chonk.
206 if (!is_trailing_kernel) {
207 GoblinMockCircuits::construct_mock_folding_kernel(circuit); // construct mock base logic
208 }
209 mock_databus.populate_kernel_databus(circuit); // populate databus inputs/outputs
210 } else {
211 GoblinMockCircuits::construct_mock_app_circuit(circuit, use_large_circuit); // construct mock app
212 mock_databus.populate_app_databus(circuit); // populate databus outputs
213 }
214 }
215
216 if (is_kernel) {
218 } else {
220 }
221
222 if (check_circuit_sizes) {
223 auto prover_instance = std::make_shared<Chonk::ProverInstance>(circuit);
224 size_t log2_dyadic_size = prover_instance->log_dyadic_size();
225 if (log2_num_gates != 0) {
226 if (is_kernel) {
227 // There are various possibilities here, so we provide a bound
229 log2_dyadic_size,
230 19UL,
231 "Log number of gates in a kernel with fixed number of arithmetic gates has exceeded bound.");
232 vinfo("Log number of gates in a kernel with fixed number of arithmetic gates is: ",
233 log2_dyadic_size);
234 } else {
235 // The offset is due to the fact that finalization adds a certain number of gates
236 size_t LOG2_OFFSET = 2;
237 BB_ASSERT_LTE(log2_dyadic_size,
238 log2_num_gates + LOG2_OFFSET,
239 "Log number of arithemtic gates produced is different from the one requested.");
240 }
241 } else {
242 if (is_kernel) {
243 // Trailing kernels (reset, tail, hiding) are simpler than regular kernels
244 if (is_trailing_kernel) {
245 // Trailing kernels should be significantly smaller, with hiding kernel < 2^16
246 BB_ASSERT_LTE(log2_dyadic_size,
247 17UL,
248 "Trailing kernel circuit size has exceeded expected bound (should be <= 2^16).");
249 vinfo("Log number of gates in a trailing kernel circuit is: ", log2_dyadic_size);
250 } else {
251 const bool is_init_kernel = circuit_counter == 2;
252 const size_t expected_log2_dyadic_size = is_init_kernel ? 17UL : 18UL;
253 BB_ASSERT_EQ(log2_dyadic_size,
254 expected_log2_dyadic_size,
255 "There has been a change in the number of gates of a mock kernel circuit.");
256 }
257 } else {
258 BB_ASSERT_EQ(log2_dyadic_size,
259 use_large_circuit ? 19UL : 17UL,
260 "There has been a change in the of gates generated for a mock app circuit.");
261 }
262 }
263 }
264 return circuit;
265 }
266
271 Chonk& ivc, TestSettings settings = {}, bool check_circuit_size = false)
272 {
273 // If this is a mock hiding kernel, remove the settings and use a default (non-structured) trace
274 const bool is_hiding_kernel = ivc.num_circuits_accumulated == ivc.get_num_circuits() - 1;
275 if (is_hiding_kernel) {
276 settings = TestSettings{};
277 }
278 auto circuit =
279 create_next_circuit(ivc, settings.log2_num_gates, settings.num_public_inputs, check_circuit_size);
280 return { circuit, get_verification_key(circuit, is_hiding_kernel) };
281 }
282
283 void construct_and_accumulate_next_circuit(Chonk& ivc, TestSettings settings = {}, bool check_circuit_sizes = false)
284 {
285 auto [circuit, vk] = create_next_circuit_and_vk(ivc, settings, check_circuit_sizes);
286 ivc.accumulate(circuit, vk);
287 }
288};
289
290} // namespace
#define BB_ASSERT(expression,...)
Definition assert.hpp:70
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:83
#define BB_ASSERT_LTE(left, right,...)
Definition assert.hpp:158
The IVC scheme used by the aztec client for private function execution.
Definition chonk.hpp:39
void complete_kernel_circuit_logic(ClientCircuit &circuit)
Append logic to complete a kernel circuit.
Definition chonk.cpp:297
size_t num_circuits_accumulated
Definition chonk.hpp:157
size_t get_num_circuits() const
Definition chonk.hpp:187
void accumulate(ClientCircuit &circuit, const std::shared_ptr< MegaVerificationKey > &precomputed_vk) override
Perform prover work for accumulation (e.g. HN folding, merge proving)
Definition chonk.cpp:573
MegaCircuitBuilder ClientCircuit
Definition chonk.hpp:53
Goblin goblin
Definition chonk.hpp:181
typename Curve::ScalarField FF
std::shared_ptr< OpQueue > op_queue
Definition goblin.hpp:59
static void construct_mock_app_circuit(MegaBuilder &builder, bool large=false)
Populate a builder with some arbitrary but nontrivial constraints.
static void construct_mock_folding_kernel(MegaBuilder &builder)
Construct a mock kernel circuit.
std::shared_ptr< ECCOpQueue > op_queue
Curve::ScalarField FF
NativeVerificationKey_< PrecomputedEntities< Commitment >, Codec, HashFunction, CommitmentKey > VerificationKey
The verification key stores commitments to the precomputed (non-witness) polynomials used by the veri...
static void construct_arithmetic_circuit(Builder &builder, const size_t target_log2_dyadic_size=4, bool include_public_inputs=true)
Populate a builder with a specified number of arithmetic gates; includes a PI.
Base Native verification key class.
Definition flavor.hpp:135
static void add_default(Builder &builder)
Add default public inputs when they are not present.
#define vinfo(...)
Definition log.hpp:94
AluTraceBuilder builder
Definition alu.test.cpp:124
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
::testing::Types< BN254Settings, GrumpkinSettings > TestSettings
BusId
Definition databus.hpp:75
VerifierCommitmentKey< Curve > vk
STL namespace.
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13