1use aztec_core::error::Error;
8use aztec_core::types::{AztecAddress, Fr};
9use aztec_node_client::AztecNode;
10
11use crate::stores::{ContractStore, KeyStore};
12
13use super::oracle::PrivateKernelOracle;
14use super::prover::{
15 ChonkProofWithPublicInputs, PrivateExecutionStep, PrivateKernelProver,
16 PrivateKernelSimulateOutput, ProvingTimings,
17};
18
19#[derive(Debug, Clone)]
21pub struct KernelExecutionConfig {
22 pub simulate: bool,
24 pub skip_fee_enforcement: bool,
26 pub profile_mode: String,
28}
29
30impl Default for KernelExecutionConfig {
31 fn default() -> Self {
32 Self {
33 simulate: false,
34 skip_fee_enforcement: false,
35 profile_mode: "none".to_owned(),
36 }
37 }
38}
39
40#[derive(Debug)]
42pub struct KernelProvingResult {
43 pub public_inputs: serde_json::Value,
45 pub chonk_proof: Option<ChonkProofWithPublicInputs>,
47 pub execution_steps: Vec<PrivateExecutionStep>,
49 pub timings: ProvingTimings,
51}
52
53pub struct PrivateKernelExecutionProver<'a, N: AztecNode> {
63 oracle: PrivateKernelOracle<'a, N>,
64 prover: &'a dyn PrivateKernelProver,
65 config: KernelExecutionConfig,
66}
67
68impl<'a, N: AztecNode> PrivateKernelExecutionProver<'a, N> {
69 pub fn new(
70 oracle: PrivateKernelOracle<'a, N>,
71 prover: &'a dyn PrivateKernelProver,
72 config: KernelExecutionConfig,
73 ) -> Self {
74 Self {
75 oracle,
76 prover,
77 config,
78 }
79 }
80
81 pub fn from_stores(
83 node: &'a N,
84 contract_store: &'a ContractStore,
85 key_store: &'a KeyStore,
86 prover: &'a dyn PrivateKernelProver,
87 block_hash: Fr,
88 config: KernelExecutionConfig,
89 ) -> Self {
90 let oracle = PrivateKernelOracle::new(node, contract_store, key_store, block_hash);
91 Self::new(oracle, prover, config)
92 }
93
94 pub async fn prove_with_kernels(
99 &self,
100 execution_results: &[PrivateCallExecution],
101 ) -> Result<KernelProvingResult, Error> {
102 let start = std::time::Instant::now();
103 let mut timings = ProvingTimings::default();
104 let mut execution_steps = Vec::new();
105
106 if execution_results.is_empty() {
107 return Err(Error::InvalidData("no execution results to prove".into()));
108 }
109
110 let first = &execution_results[0];
112 let init_inputs = self.build_init_inputs(first).await?;
113
114 let init_output = if self.config.simulate {
115 self.prover.simulate_init(&init_inputs).await?
116 } else {
117 let output = self.prover.generate_init_output(&init_inputs).await?;
118 execution_steps.push(self.output_to_step(&output, "private_kernel_init"));
119 output
120 };
121
122 timings
123 .circuits
124 .push(("init".to_owned(), start.elapsed().as_millis() as u64));
125 let mut current_output = init_output;
126
127 for (i, call) in execution_results.iter().enumerate().skip(1) {
129 let inner_start = std::time::Instant::now();
130 let inner_inputs = self.build_inner_inputs(call, ¤t_output).await?;
131
132 current_output = if self.config.simulate {
133 self.prover.simulate_inner(&inner_inputs).await?
134 } else {
135 let output = self.prover.generate_inner_output(&inner_inputs).await?;
136 execution_steps
137 .push(self.output_to_step(&output, &format!("private_kernel_inner_{i}")));
138 output
139 };
140
141 timings.circuits.push((
142 format!("inner_{i}"),
143 inner_start.elapsed().as_millis() as u64,
144 ));
145
146 if self.needs_reset(¤t_output) {
148 let reset_start = std::time::Instant::now();
149 let reset_inputs = self.build_reset_inputs(¤t_output).await?;
150
151 current_output = if self.config.simulate {
152 self.prover.simulate_reset(&reset_inputs).await?
153 } else {
154 let output = self.prover.generate_reset_output(&reset_inputs).await?;
155 execution_steps
156 .push(self.output_to_step(&output, &format!("private_kernel_reset_{i}")));
157 output
158 };
159
160 timings.circuits.push((
161 format!("reset_{i}"),
162 reset_start.elapsed().as_millis() as u64,
163 ));
164 }
165 }
166
167 let final_reset_start = std::time::Instant::now();
169 let final_reset_inputs = self.build_final_reset_inputs(¤t_output).await?;
170
171 current_output = if self.config.simulate {
172 self.prover.simulate_reset(&final_reset_inputs).await?
173 } else {
174 let output = self
175 .prover
176 .generate_reset_output(&final_reset_inputs)
177 .await?;
178 execution_steps.push(self.output_to_step(&output, "private_kernel_reset_final"));
179 output
180 };
181
182 timings.circuits.push((
183 "reset_final".to_owned(),
184 final_reset_start.elapsed().as_millis() as u64,
185 ));
186
187 let tail_start = std::time::Instant::now();
189 let tail_inputs = self.build_tail_inputs(¤t_output).await?;
190 let is_for_public = self.is_for_public(¤t_output);
191
192 let tail_output = if self.config.simulate {
193 self.prover.simulate_tail(&tail_inputs).await?
194 } else {
195 let output = self.prover.generate_tail_output(&tail_inputs).await?;
196 let name = if is_for_public {
197 "private_kernel_tail_to_public"
198 } else {
199 "private_kernel_tail"
200 };
201 execution_steps.push(self.output_to_step(&output, name));
202 output
203 };
204
205 timings
206 .circuits
207 .push(("tail".to_owned(), tail_start.elapsed().as_millis() as u64));
208
209 if !self.config.simulate {
211 let hiding_start = std::time::Instant::now();
212 let hiding_inputs = self
213 .build_hiding_inputs(&tail_output, is_for_public)
214 .await?;
215
216 let hiding_output = if is_for_public {
217 let output = self
218 .prover
219 .generate_hiding_to_public_output(&hiding_inputs)
220 .await?;
221 execution_steps.push(self.output_to_step(&output, "hiding_kernel_to_public"));
222 output
223 } else {
224 let output = self
225 .prover
226 .generate_hiding_to_rollup_output(&hiding_inputs)
227 .await?;
228 execution_steps.push(self.output_to_step(&output, "hiding_kernel_to_rollup"));
229 output
230 };
231
232 timings.circuits.push((
233 "hiding".to_owned(),
234 hiding_start.elapsed().as_millis() as u64,
235 ));
236
237 let _ = hiding_output;
239 }
240
241 let chonk_proof = if !self.config.simulate && !execution_steps.is_empty() {
243 let chonk_start = std::time::Instant::now();
244 let proof = self.prover.create_chonk_proof(&execution_steps).await?;
245 timings
246 .circuits
247 .push(("chonk".to_owned(), chonk_start.elapsed().as_millis() as u64));
248 Some(proof)
249 } else {
250 None
251 };
252
253 timings.total_ms = start.elapsed().as_millis() as u64;
254
255 Ok(KernelProvingResult {
256 public_inputs: tail_output.public_inputs,
257 chonk_proof,
258 execution_steps,
259 timings,
260 })
261 }
262
263 async fn build_init_inputs(
267 &self,
268 call: &PrivateCallExecution,
269 ) -> Result<serde_json::Value, Error> {
270 let contract_preimage = self
271 .oracle
272 .get_contract_address_preimage(&call.contract_address)
273 .await?;
274
275 Ok(serde_json::json!({
276 "txRequest": call.tx_request,
277 "privateCall": {
278 "callStackItem": call.call_stack_item,
279 "executionResult": call.execution_result_json,
280 },
281 "contractInstance": contract_preimage,
282 "vkMembershipWitness": self.oracle.get_vk_membership_witness(&Fr::zero()).await?,
283 }))
284 }
285
286 async fn build_inner_inputs(
288 &self,
289 call: &PrivateCallExecution,
290 previous_output: &PrivateKernelSimulateOutput,
291 ) -> Result<serde_json::Value, Error> {
292 let contract_preimage = self
293 .oracle
294 .get_contract_address_preimage(&call.contract_address)
295 .await?;
296
297 Ok(serde_json::json!({
298 "previousKernelData": {
299 "publicInputs": previous_output.public_inputs,
300 },
301 "privateCall": {
302 "callStackItem": call.call_stack_item,
303 "executionResult": call.execution_result_json,
304 },
305 "contractInstance": contract_preimage,
306 "vkMembershipWitness": self.oracle.get_vk_membership_witness(&Fr::zero()).await?,
307 }))
308 }
309
310 async fn build_reset_inputs(
312 &self,
313 current_output: &PrivateKernelSimulateOutput,
314 ) -> Result<serde_json::Value, Error> {
315 Ok(serde_json::json!({
316 "previousKernelData": {
317 "publicInputs": current_output.public_inputs,
318 },
319 "silo": false,
320 }))
321 }
322
323 async fn build_final_reset_inputs(
325 &self,
326 current_output: &PrivateKernelSimulateOutput,
327 ) -> Result<serde_json::Value, Error> {
328 Ok(serde_json::json!({
329 "previousKernelData": {
330 "publicInputs": current_output.public_inputs,
331 },
332 "silo": true,
333 }))
334 }
335
336 async fn build_tail_inputs(
338 &self,
339 current_output: &PrivateKernelSimulateOutput,
340 ) -> Result<serde_json::Value, Error> {
341 Ok(serde_json::json!({
342 "previousKernelData": {
343 "publicInputs": current_output.public_inputs,
344 },
345 }))
346 }
347
348 async fn build_hiding_inputs(
350 &self,
351 tail_output: &PrivateKernelSimulateOutput,
352 _is_for_public: bool,
353 ) -> Result<serde_json::Value, Error> {
354 Ok(serde_json::json!({
355 "previousKernelData": {
356 "publicInputs": tail_output.public_inputs,
357 },
358 }))
359 }
360
361 fn needs_reset(&self, _output: &PrivateKernelSimulateOutput) -> bool {
363 false
368 }
369
370 fn is_for_public(&self, output: &PrivateKernelSimulateOutput) -> bool {
372 output
373 .public_inputs
374 .pointer("/end/publicCallRequests")
375 .and_then(|v| v.as_array())
376 .map(|arr| !arr.is_empty())
377 .unwrap_or(false)
378 }
379
380 fn output_to_step(
382 &self,
383 output: &PrivateKernelSimulateOutput,
384 function_name: &str,
385 ) -> PrivateExecutionStep {
386 PrivateExecutionStep {
387 function_name: function_name.to_owned(),
388 bytecode: output.bytecode.clone(),
389 witness: output.output_witness.clone(),
390 vk: output.verification_key.clone(),
391 timings: Default::default(),
392 }
393 }
394}
395
396#[derive(Debug, Clone)]
398pub struct PrivateCallExecution {
399 pub contract_address: AztecAddress,
401 pub tx_request: serde_json::Value,
403 pub call_stack_item: serde_json::Value,
405 pub execution_result_json: serde_json::Value,
407 pub execution_result: crate::execution::execution_result::PrivateCallExecutionResult,
409}