1use std::{
10 any::TypeId,
11 borrow::Borrow,
12 collections::{HashMap, VecDeque},
13 marker::PhantomData,
14 sync::Arc,
15};
16
17use getset::{Getters, MutGetters, Setters, WithSetters};
18use itertools::{zip_eq, Itertools};
19use openvm_circuit::system::program::trace::compute_exe_commit;
20use openvm_instructions::{
21 exe::{SparseMemoryImage, VmExe},
22 program::Program,
23};
24use openvm_stark_backend::{
25 config::{Com, StarkGenericConfig, Val},
26 engine::StarkEngine,
27 keygen::types::{MultiStarkProvingKey, MultiStarkVerifyingKey},
28 p3_field::{FieldAlgebra, FieldExtensionAlgebra, PrimeField32, TwoAdicField},
29 p3_util::{log2_ceil_usize, log2_strict_usize},
30 proof::Proof,
31 prover::{
32 hal::{DeviceDataTransporter, MatrixDimensions, TraceCommitter},
33 types::{CommittedTraceData, DeviceMultiStarkProvingKey, ProvingContext},
34 },
35 verifier::VerificationError,
36};
37use p3_baby_bear::BabyBear;
38use serde::{Deserialize, Serialize};
39use thiserror::Error;
40use tracing::{info_span, instrument};
41
42#[cfg(feature = "aot")]
43use super::aot::AotInstance;
44use super::{
45 execution_mode::{ExecutionCtx, MeteredCostCtx, MeteredCtx, PreflightCtx, Segment},
46 hasher::poseidon2::vm_poseidon2_hasher,
47 interpreter::InterpretedInstance,
48 interpreter_preflight::PreflightInterpretedInstance,
49 AirInventoryError, ChipInventoryError, ExecutionError, ExecutionState, Executor,
50 ExecutorInventory, ExecutorInventoryError, MemoryConfig, MeteredExecutor, PreflightExecutor,
51 StaticProgramError, SystemConfig, VmBuilder, VmChipComplex, VmCircuitConfig, VmExecState,
52 VmExecutionConfig, VmState, CONNECTOR_AIR_ID, MERKLE_AIR_ID, PROGRAM_AIR_ID,
53 PROGRAM_CACHED_TRACE_INDEX, PUBLIC_VALUES_AIR_ID,
54};
55use crate::{
56 arch::DEFAULT_RNG_SEED,
57 execute_spanned,
58 system::{
59 connector::{VmConnectorPvs, DEFAULT_SUSPEND_EXIT_CODE},
60 memory::{
61 adapter::records,
62 merkle::{
63 public_values::{UserPublicValuesProof, UserPublicValuesProofError},
64 MemoryMerklePvs,
65 },
66 online::{GuestMemory, TracingMemory},
67 AddressMap, CHUNK,
68 },
69 program::trace::{generate_cached_trace, VmCommittedExe},
70 SystemChipComplex, SystemRecords, SystemWithFixedTraceHeights,
71 },
72};
73
74#[derive(Error, Debug)]
75pub enum GenerationError {
76 #[error("unexpected number of arenas: {actual} (expected num_airs={expected})")]
77 UnexpectedNumArenas { actual: usize, expected: usize },
78 #[error("trace height for air_idx={air_idx} must be fixed to {expected}, actual={actual}")]
79 ForceTraceHeightIncorrect {
80 air_idx: usize,
81 actual: usize,
82 expected: usize,
83 },
84 #[error("trace height of air {air_idx} has height {height} greater than maximum {max_height}")]
85 TraceHeightsLimitExceeded {
86 air_idx: usize,
87 height: usize,
88 max_height: usize,
89 },
90 #[error("trace heights violate linear constraint {constraint_idx} ({value} >= {threshold})")]
91 LinearTraceHeightConstraintExceeded {
92 constraint_idx: usize,
93 value: u64,
94 threshold: u32,
95 },
96}
97
98pub trait KvStore: Send + Sync {
100 fn get(&self, key: &[u8]) -> Option<&[u8]>;
101}
102
103impl KvStore for HashMap<Vec<u8>, Vec<u8>> {
104 fn get(&self, key: &[u8]) -> Option<&[u8]> {
105 self.get(key).map(|v| v.as_slice())
106 }
107}
108
109#[derive(Clone)]
110pub struct Streams<F> {
111 pub input_stream: VecDeque<Vec<F>>,
112 pub hint_stream: VecDeque<F>,
113 pub hint_space: Vec<Vec<F>>,
114 pub kv_store: Arc<dyn KvStore>,
117}
118
119impl<F> Streams<F> {
120 pub fn new(input_stream: impl Into<VecDeque<Vec<F>>>) -> Self {
121 Self {
122 input_stream: input_stream.into(),
123 hint_stream: VecDeque::default(),
124 hint_space: Vec::default(),
125 kv_store: Arc::new(HashMap::new()),
126 }
127 }
128}
129
130impl<F> Default for Streams<F> {
131 fn default() -> Self {
132 Self::new(VecDeque::default())
133 }
134}
135
136impl<F> From<VecDeque<Vec<F>>> for Streams<F> {
137 fn from(value: VecDeque<Vec<F>>) -> Self {
138 Streams::new(value)
139 }
140}
141
142impl<F> From<Vec<Vec<F>>> for Streams<F> {
143 fn from(value: Vec<Vec<F>>) -> Self {
144 Streams::new(value)
145 }
146}
147
148type PreflightInterpretedInstance2<F, VC> =
150 PreflightInterpretedInstance<F, <VC as VmExecutionConfig<F>>::Executor>;
151
152#[derive(Clone)]
158pub struct VmExecutor<F, VC>
159where
160 VC: VmExecutionConfig<F>,
161{
162 pub config: VC,
163 inventory: Arc<ExecutorInventory<VC::Executor>>,
164 phantom: PhantomData<F>,
165}
166
167#[repr(i32)]
168pub enum ExitCode {
169 Success = 0,
170 Error = 1,
171 Suspended = -1, }
173
174pub struct PreflightExecutionOutput<F, RA> {
175 pub system_records: SystemRecords<F>,
176 pub record_arenas: Vec<RA>,
177 pub to_state: VmState<F, GuestMemory>,
178}
179
180impl<F, VC> VmExecutor<F, VC>
181where
182 VC: VmExecutionConfig<F>,
183{
184 pub fn new(config: VC) -> Result<Self, ExecutorInventoryError> {
188 let inventory = config.create_executors()?;
189 Ok(Self {
190 config,
191 inventory: Arc::new(inventory),
192 phantom: PhantomData,
193 })
194 }
195}
196
197impl<F, VC> VmExecutor<F, VC>
198where
199 VC: VmExecutionConfig<F> + AsRef<SystemConfig>,
200{
201 pub fn build_metered_ctx(
202 &self,
203 constant_trace_heights: &[Option<usize>],
204 air_names: &[String],
205 widths: &[usize],
206 interactions: &[usize],
207 ) -> MeteredCtx {
208 MeteredCtx::new(
209 constant_trace_heights.to_vec(),
210 air_names.to_vec(),
211 widths.to_vec(),
212 interactions.to_vec(),
213 self.config.as_ref(),
214 )
215 }
216
217 pub fn build_metered_cost_ctx(&self, widths: &[usize]) -> MeteredCostCtx {
218 MeteredCostCtx::new(widths.to_vec(), self.config.as_ref())
219 }
220}
221
222impl<F, VC> VmExecutor<F, VC>
223where
224 F: PrimeField32,
225 VC: VmExecutionConfig<F>,
226 VC::Executor: Executor<F>,
227{
228 #[cfg(not(feature = "aot"))]
233 pub fn instance(
234 &self,
235 exe: &VmExe<F>,
236 ) -> Result<InterpretedInstance<F, ExecutionCtx>, StaticProgramError> {
237 InterpretedInstance::new(&self.inventory, exe)
238 }
239
240 #[cfg(feature = "aot")]
241 pub fn interpreter_instance(
242 &self,
243 exe: &VmExe<F>,
244 ) -> Result<InterpretedInstance<F, ExecutionCtx>, StaticProgramError> {
245 InterpretedInstance::new(&self.inventory, exe)
246 }
247
248 #[cfg(feature = "aot")]
249 pub fn instance(
250 &self,
251 exe: &VmExe<F>,
252 ) -> Result<AotInstance<F, ExecutionCtx>, StaticProgramError> {
253 Self::aot_instance(self, exe)
254 }
255}
256#[cfg(feature = "aot")]
257impl<F, VC> VmExecutor<F, VC>
258where
259 F: PrimeField32,
260 VC: VmExecutionConfig<F>,
261 VC::Executor: Executor<F>,
262{
263 pub fn aot_instance(
264 &self,
265 exe: &VmExe<F>,
266 ) -> Result<AotInstance<F, ExecutionCtx>, StaticProgramError> {
267 AotInstance::new(&self.inventory, exe)
268 }
269}
270
271impl<F, VC> VmExecutor<F, VC>
272where
273 F: PrimeField32,
274 VC: VmExecutionConfig<F>,
275 VC::Executor: MeteredExecutor<F>,
276{
277 #[cfg(not(feature = "aot"))]
279 pub fn metered_instance(
280 &self,
281 exe: &VmExe<F>,
282 executor_idx_to_air_idx: &[usize],
283 ) -> Result<InterpretedInstance<F, MeteredCtx>, StaticProgramError> {
284 InterpretedInstance::new_metered(&self.inventory, exe, executor_idx_to_air_idx)
285 }
286
287 #[cfg(feature = "aot")]
288 pub fn metered_interpreter_instance(
289 &self,
290 exe: &VmExe<F>,
291 executor_idx_to_air_idx: &[usize],
292 ) -> Result<InterpretedInstance<F, MeteredCtx>, StaticProgramError> {
293 InterpretedInstance::new_metered(&self.inventory, exe, executor_idx_to_air_idx)
294 }
295
296 #[cfg(feature = "aot")]
297 pub fn metered_instance(
298 &self,
299 exe: &VmExe<F>,
300 executor_idx_to_air_idx: &[usize],
301 ) -> Result<AotInstance<F, MeteredCtx>, StaticProgramError> {
302 Self::metered_aot_instance(self, exe, executor_idx_to_air_idx)
303 }
304
305 #[cfg(feature = "aot")]
307 pub fn metered_aot_instance(
308 &self,
309 exe: &VmExe<F>,
310 executor_idx_to_air_idx: &[usize],
311 ) -> Result<AotInstance<F, MeteredCtx>, StaticProgramError> {
312 AotInstance::new_metered(&self.inventory, exe, executor_idx_to_air_idx)
313 }
314
315 pub fn metered_cost_instance(
318 &self,
319 exe: &VmExe<F>,
320 executor_idx_to_air_idx: &[usize],
321 ) -> Result<InterpretedInstance<F, MeteredCostCtx>, StaticProgramError> {
322 InterpretedInstance::new_metered(&self.inventory, exe, executor_idx_to_air_idx)
323 }
324}
325
326#[derive(Error, Debug)]
327pub enum VmVerificationError {
328 #[error("no proof is provided")]
329 ProofNotFound,
330
331 #[error("program commit mismatch (index of mismatch proof: {index}")]
332 ProgramCommitMismatch { index: usize },
333
334 #[error("exe commit mismatch (expected: {expected:?}, actual: {actual:?})")]
335 ExeCommitMismatch {
336 expected: [u32; CHUNK],
337 actual: [u32; CHUNK],
338 },
339
340 #[error("initial pc mismatch (initial: {initial}, prev_final: {prev_final})")]
341 InitialPcMismatch { initial: u32, prev_final: u32 },
342
343 #[error("initial memory root mismatch")]
344 InitialMemoryRootMismatch,
345
346 #[error("is terminate mismatch (expected: {expected}, actual: {actual})")]
347 IsTerminateMismatch { expected: bool, actual: bool },
348
349 #[error("exit code mismatch")]
350 ExitCodeMismatch { expected: u32, actual: u32 },
351
352 #[error("AIR has unexpected public values (expected: {expected}, actual: {actual})")]
353 UnexpectedPvs { expected: usize, actual: usize },
354
355 #[error("Invalid number of AIRs: expected at least 3, got {0}")]
356 NotEnoughAirs(usize),
357
358 #[error("missing system AIR with ID {air_id}")]
359 SystemAirMissing { air_id: usize },
360
361 #[error("stark verification error: {0}")]
362 StarkError(#[from] VerificationError),
363
364 #[error("user public values proof error: {0}")]
365 UserPublicValuesError(#[from] UserPublicValuesProofError),
366}
367
368#[derive(Error, Debug)]
369pub enum VirtualMachineError {
370 #[error("executor inventory error: {0}")]
371 ExecutorInventory(#[from] ExecutorInventoryError),
372 #[error("air inventory error: {0}")]
373 AirInventory(#[from] AirInventoryError),
374 #[error("chip inventory error: {0}")]
375 ChipInventory(#[from] ChipInventoryError),
376 #[error("static program error: {0}")]
377 StaticProgram(#[from] StaticProgramError),
378 #[error("execution error: {0}")]
379 Execution(#[from] ExecutionError),
380 #[error("trace generation error: {0}")]
381 Generation(#[from] GenerationError),
382 #[error("program committed trade data not loaded")]
383 ProgramIsNotCommitted,
384 #[error("verification error: {0}")]
385 Verification(#[from] VmVerificationError),
386}
387
388#[derive(Getters, MutGetters, Setters, WithSetters)]
396pub struct VirtualMachine<E, VB>
397where
398 E: StarkEngine,
399 VB: VmBuilder<E>,
400{
401 pub engine: E,
403 #[getset(get = "pub")]
405 executor: VmExecutor<Val<E::SC>, VB::VmConfig>,
406 #[getset(get = "pub", get_mut = "pub")]
407 pk: DeviceMultiStarkProvingKey<E::PB>,
408 chip_complex: VmChipComplex<E::SC, VB::RecordArena, E::PB, VB::SystemChipInventory>,
409 #[cfg(feature = "stark-debug")]
410 pub h_pk: Option<MultiStarkProvingKey<E::SC>>,
411}
412
413impl<E, VB> VirtualMachine<E, VB>
414where
415 E: StarkEngine,
416 VB: VmBuilder<E>,
417{
418 pub fn new(
419 engine: E,
420 builder: VB,
421 config: VB::VmConfig,
422 d_pk: DeviceMultiStarkProvingKey<E::PB>,
423 ) -> Result<Self, VirtualMachineError> {
424 let circuit = config.create_airs()?;
425 let chip_complex = builder.create_chip_complex(&config, circuit)?;
426 let executor = VmExecutor::<Val<E::SC>, _>::new(config)?;
427 Ok(Self {
428 engine,
429 executor,
430 pk: d_pk,
431 chip_complex,
432 #[cfg(feature = "stark-debug")]
433 h_pk: None,
434 })
435 }
436
437 pub fn new_with_keygen(
438 engine: E,
439 builder: VB,
440 config: VB::VmConfig,
441 ) -> Result<(Self, MultiStarkProvingKey<E::SC>), VirtualMachineError> {
442 let circuit = config.create_airs()?;
443 let pk = circuit.keygen(&engine);
444 let d_pk = engine.device().transport_pk_to_device(&pk);
445 let vm = Self::new(engine, builder, config, d_pk)?;
446 Ok((vm, pk))
447 }
448
449 pub fn config(&self) -> &VB::VmConfig {
450 &self.executor.config
451 }
452
453 #[cfg(not(feature = "aot"))]
455 pub fn interpreter(
456 &self,
457 exe: &VmExe<Val<E::SC>>,
458 ) -> Result<InterpretedInstance<Val<E::SC>, ExecutionCtx>, StaticProgramError>
459 where
460 Val<E::SC>: PrimeField32,
461 <VB::VmConfig as VmExecutionConfig<Val<E::SC>>>::Executor: Executor<Val<E::SC>>,
462 {
463 self.executor().instance(exe)
464 }
465
466 #[cfg(feature = "aot")]
468 pub fn naive_interpreter(
469 &self,
470 exe: &VmExe<Val<E::SC>>,
471 ) -> Result<InterpretedInstance<Val<E::SC>, ExecutionCtx>, StaticProgramError>
472 where
473 Val<E::SC>: PrimeField32,
474 <VB::VmConfig as VmExecutionConfig<Val<E::SC>>>::Executor: Executor<Val<E::SC>>,
475 {
476 self.executor().interpreter_instance(exe)
477 }
478
479 #[cfg(feature = "aot")]
481 pub fn interpreter(
482 &self,
483 exe: &VmExe<Val<E::SC>>,
484 ) -> Result<AotInstance<Val<E::SC>, ExecutionCtx>, StaticProgramError>
485 where
486 Val<E::SC>: PrimeField32,
487 <VB::VmConfig as VmExecutionConfig<Val<E::SC>>>::Executor: Executor<Val<E::SC>>,
488 {
489 Self::get_aot_instance(self, exe)
490 }
491
492 #[cfg(feature = "aot")]
493 pub fn get_aot_instance(
494 &self,
495 exe: &VmExe<Val<E::SC>>,
496 ) -> Result<AotInstance<Val<E::SC>, ExecutionCtx>, StaticProgramError>
497 where
498 Val<E::SC>: PrimeField32,
499 <VB::VmConfig as VmExecutionConfig<Val<E::SC>>>::Executor: Executor<Val<E::SC>>,
500 {
501 self.executor().aot_instance(exe)
502 }
503
504 #[cfg(not(feature = "aot"))]
505 pub fn metered_interpreter(
506 &self,
507 exe: &VmExe<Val<E::SC>>,
508 ) -> Result<InterpretedInstance<Val<E::SC>, MeteredCtx>, StaticProgramError>
509 where
510 Val<E::SC>: PrimeField32,
511 <VB::VmConfig as VmExecutionConfig<Val<E::SC>>>::Executor: MeteredExecutor<Val<E::SC>>,
512 {
513 let executor_idx_to_air_idx = self.executor_idx_to_air_idx();
514 self.executor()
515 .metered_instance(exe, &executor_idx_to_air_idx)
516 }
517
518 #[cfg(feature = "aot")]
519 pub fn metered_interpreter(
520 &self,
521 exe: &VmExe<Val<E::SC>>,
522 ) -> Result<AotInstance<Val<E::SC>, MeteredCtx>, StaticProgramError>
523 where
524 Val<E::SC>: PrimeField32,
525 <VB::VmConfig as VmExecutionConfig<Val<E::SC>>>::Executor: MeteredExecutor<Val<E::SC>>,
526 {
527 let executor_idx_to_air_idx = self.executor_idx_to_air_idx();
528 self.executor()
529 .metered_instance(exe, &executor_idx_to_air_idx)
530 }
531
532 #[cfg(feature = "aot")]
534 pub fn get_metered_aot_instance(
535 &self,
536 exe: &VmExe<Val<E::SC>>,
537 ) -> Result<AotInstance<Val<E::SC>, MeteredCtx>, StaticProgramError>
538 where
539 Val<E::SC>: PrimeField32,
540 <VB::VmConfig as VmExecutionConfig<Val<E::SC>>>::Executor: MeteredExecutor<Val<E::SC>>,
541 {
542 let executor_idx_to_air_idx = self.executor_idx_to_air_idx();
543 self.executor()
544 .metered_aot_instance(exe, &executor_idx_to_air_idx)
545 }
546
547 #[cfg(feature = "aot")]
548 pub fn naive_metered_interpreter(
549 &self,
550 exe: &VmExe<Val<E::SC>>,
551 ) -> Result<InterpretedInstance<Val<E::SC>, MeteredCtx>, StaticProgramError>
552 where
553 Val<E::SC>: PrimeField32,
554 <VB::VmConfig as VmExecutionConfig<Val<E::SC>>>::Executor: MeteredExecutor<Val<E::SC>>,
555 {
556 let executor_idx_to_air_idx = self.executor_idx_to_air_idx();
557 self.executor()
558 .metered_interpreter_instance(exe, &executor_idx_to_air_idx)
559 }
560
561 pub fn metered_cost_interpreter(
562 &self,
563 exe: &VmExe<Val<E::SC>>,
564 ) -> Result<InterpretedInstance<Val<E::SC>, MeteredCostCtx>, StaticProgramError>
565 where
566 Val<E::SC>: PrimeField32,
567 <VB::VmConfig as VmExecutionConfig<Val<E::SC>>>::Executor: MeteredExecutor<Val<E::SC>>,
568 {
569 let executor_idx_to_air_idx = self.executor_idx_to_air_idx();
570 self.executor()
571 .metered_cost_instance(exe, &executor_idx_to_air_idx)
572 }
573
574 pub fn preflight_interpreter(
575 &self,
576 exe: &VmExe<Val<E::SC>>,
577 ) -> Result<PreflightInterpretedInstance2<Val<E::SC>, VB::VmConfig>, StaticProgramError> {
578 PreflightInterpretedInstance::new(
579 &exe.program,
580 self.executor.inventory.clone(),
581 self.executor_idx_to_air_idx(),
582 )
583 }
584
585 #[instrument(name = "execute_preflight", skip_all)]
593 pub fn execute_preflight(
594 &self,
595 interpreter: &mut PreflightInterpretedInstance2<Val<E::SC>, VB::VmConfig>,
596 state: VmState<Val<E::SC>, GuestMemory>,
597 num_insns: Option<u64>,
598 trace_heights: &[u32],
599 ) -> Result<PreflightExecutionOutput<Val<E::SC>, VB::RecordArena>, ExecutionError>
600 where
601 Val<E::SC>: PrimeField32,
602 <VB::VmConfig as VmExecutionConfig<Val<E::SC>>>::Executor:
603 PreflightExecutor<Val<E::SC>, VB::RecordArena>,
604 {
605 debug_assert!(interpreter
606 .executor_idx_to_air_idx
607 .iter()
608 .all(|&air_idx| air_idx < trace_heights.len()));
609
610 let main_widths = self
612 .pk
613 .per_air
614 .iter()
615 .map(|pk| pk.vk.params.width.main_width())
616 .collect_vec();
617 let capacities = zip_eq(trace_heights, main_widths)
618 .map(|(&h, w)| (h as usize, w))
619 .collect::<Vec<_>>();
620 let ctx = PreflightCtx::new_with_capacity(&capacities, num_insns);
621
622 let system_config: &SystemConfig = self.config().as_ref();
623 let adapter_offset = system_config.access_adapter_air_id_offset();
624 let num_adapters = log2_strict_usize(system_config.memory_config.max_access_adapter_n);
626 assert_eq!(adapter_offset + num_adapters, system_config.num_airs());
627 let access_adapter_arena_size_bound = records::arena_size_bound(
628 &trace_heights[adapter_offset..adapter_offset + num_adapters],
629 );
630 let pc = state.pc();
631 let memory = TracingMemory::from_image(
632 state.memory,
633 system_config.initial_block_size(),
634 access_adapter_arena_size_bound,
635 );
636 let from_state = ExecutionState::new(pc, memory.timestamp());
637 let vm_state = VmState::new(
638 pc,
639 memory,
640 state.streams,
641 state.rng,
642 state.custom_pvs,
643 #[cfg(feature = "metrics")]
644 state.metrics,
645 );
646 let mut exec_state = VmExecState::new(vm_state, ctx);
647 interpreter.reset_execution_frequencies();
648 execute_spanned!("execute_preflight", interpreter, &mut exec_state)?;
649 let filtered_exec_frequencies = interpreter.filtered_execution_frequencies();
650 let touched_memory = exec_state
651 .vm_state
652 .memory
653 .finalize::<Val<E::SC>>(system_config.continuation_enabled);
654 #[cfg(feature = "perf-metrics")]
655 crate::metrics::end_segment_metrics(&mut exec_state);
656
657 let pc = exec_state.vm_state.pc();
658 let memory = exec_state.vm_state.memory;
659 let to_state = ExecutionState::new(pc, memory.timestamp());
660 let public_values = exec_state
661 .vm_state
662 .custom_pvs
663 .iter()
664 .map(|&x| x.unwrap_or(Val::<E::SC>::ZERO))
665 .collect();
666 let exit_code = exec_state.exit_code?;
667 let system_records = SystemRecords {
668 from_state,
669 to_state,
670 exit_code,
671 filtered_exec_frequencies,
672 access_adapter_records: memory.access_adapter_records,
673 touched_memory,
674 public_values,
675 };
676 let record_arenas = exec_state.ctx.arenas;
677 let to_state = VmState::new(
678 pc,
679 memory.data,
680 exec_state.vm_state.streams,
681 exec_state.vm_state.rng,
682 exec_state.vm_state.custom_pvs,
683 #[cfg(feature = "metrics")]
684 exec_state.vm_state.metrics,
685 );
686 Ok(PreflightExecutionOutput {
687 system_records,
688 record_arenas,
689 to_state,
690 })
691 }
692
693 #[instrument(name = "vm.create_initial_state", level = "debug", skip_all)]
696 pub fn create_initial_state(
697 &self,
698 exe: &VmExe<Val<E::SC>>,
699 inputs: impl Into<Streams<Val<E::SC>>>,
700 ) -> VmState<Val<E::SC>, GuestMemory> {
701 #[allow(unused_mut)]
702 let mut state = VmState::initial(
703 self.config().as_ref(),
704 &exe.init_memory,
705 exe.pc_start,
706 inputs,
707 );
708 #[cfg(all(feature = "metrics", any(feature = "perf-metrics", debug_assertions)))]
712 {
713 state.metrics.fn_bounds = exe.fn_bounds.clone();
714 state.metrics.debug_infos = exe.program.debug_infos();
715 }
716 #[cfg(feature = "perf-metrics")]
717 {
718 state.metrics.set_pk_info(&self.pk);
719 state.metrics.num_sys_airs = self.config().as_ref().num_airs();
720 state.metrics.access_adapter_offset =
721 self.config().as_ref().access_adapter_air_id_offset();
722 }
723 state
724 }
725
726 #[instrument(name = "trace_gen", skip_all)]
732 pub fn generate_proving_ctx(
733 &mut self,
734 system_records: SystemRecords<Val<E::SC>>,
735 record_arenas: Vec<VB::RecordArena>,
736 ) -> Result<ProvingContext<E::PB>, GenerationError> {
737 #[cfg(feature = "metrics")]
738 let mut current_trace_heights =
739 self.get_trace_heights_from_arenas(&system_records, &record_arenas);
740 let ctx = self
742 .chip_complex
743 .generate_proving_ctx(system_records, record_arenas)?;
744
745 let idx_trace_heights = ctx
747 .per_air
748 .iter()
749 .map(|(air_idx, ctx)| (*air_idx, ctx.main_trace_height()))
750 .collect_vec();
751 let max_trace_height = if TypeId::of::<Val<E::SC>>() == TypeId::of::<BabyBear>() {
753 let min_log_blowup = log2_ceil_usize(self.config().as_ref().max_constraint_degree - 1);
754 1 << (BabyBear::TWO_ADICITY - min_log_blowup)
755 } else {
756 tracing::warn!(
757 "constructing VirtualMachine for unrecognized field; using max_trace_height=2^30"
758 );
759 1 << 30
760 };
761 if let Some(&(air_idx, height)) = idx_trace_heights
762 .iter()
763 .find(|(_, height)| *height > max_trace_height)
764 {
765 return Err(GenerationError::TraceHeightsLimitExceeded {
766 air_idx,
767 height,
768 max_height: max_trace_height,
769 });
770 }
771 let trace_height_constraints = &self.pk.trace_height_constraints;
773 if trace_height_constraints.is_empty() {
774 tracing::warn!("generating proving context without trace height constraints");
775 }
776 for (i, constraint) in trace_height_constraints.iter().enumerate() {
777 let value = idx_trace_heights
778 .iter()
779 .map(|&(air_idx, h)| constraint.coefficients[air_idx] as u64 * h as u64)
780 .sum::<u64>();
781
782 if value >= constraint.threshold as u64 {
783 tracing::info!(
784 "trace heights {:?} violate linear constraint {} ({} >= {})",
785 idx_trace_heights,
786 i,
787 value,
788 constraint.threshold
789 );
790 return Err(GenerationError::LinearTraceHeightConstraintExceeded {
791 constraint_idx: i,
792 value,
793 threshold: constraint.threshold,
794 });
795 }
796 }
797 #[cfg(feature = "metrics")]
798 self.finalize_metrics(&mut current_trace_heights);
799 #[cfg(feature = "stark-debug")]
800 self.debug_proving_ctx(&ctx);
801
802 Ok(ctx)
803 }
804
805 pub fn prove(
816 &mut self,
817 interpreter: &mut PreflightInterpretedInstance2<Val<E::SC>, VB::VmConfig>,
818 state: VmState<Val<E::SC>, GuestMemory>,
819 num_insns: Option<u64>,
820 trace_heights: &[u32],
821 ) -> Result<(Proof<E::SC>, Option<GuestMemory>), VirtualMachineError>
822 where
823 Val<E::SC>: PrimeField32,
824 <VB::VmConfig as VmExecutionConfig<Val<E::SC>>>::Executor:
825 PreflightExecutor<Val<E::SC>, VB::RecordArena>,
826 {
827 self.transport_init_memory_to_device(&state.memory);
828
829 let PreflightExecutionOutput {
830 system_records,
831 record_arenas,
832 to_state,
833 } = self.execute_preflight(interpreter, state, num_insns, trace_heights)?;
834 let final_memory =
836 (system_records.exit_code == Some(ExitCode::Success as u32)).then_some(to_state.memory);
837 let ctx = self.generate_proving_ctx(system_records, record_arenas)?;
838 let proof = self.engine.prove(&self.pk, ctx);
839
840 Ok((proof, final_memory))
841 }
842
843 pub fn verify(
848 &self,
849 vk: &MultiStarkVerifyingKey<E::SC>,
850 proofs: &[Proof<E::SC>],
851 ) -> Result<(), VmVerificationError>
852 where
853 Com<E::SC>: AsRef<[Val<E::SC>; CHUNK]> + From<[Val<E::SC>; CHUNK]>,
854 Val<E::SC>: PrimeField32,
855 {
856 if self.config().as_ref().continuation_enabled {
857 verify_segments(&self.engine, vk, proofs).map(|_| ())
858 } else {
859 assert_eq!(proofs.len(), 1);
860 verify_single(&self.engine, vk, &proofs[0]).map_err(VmVerificationError::StarkError)
861 }
862 }
863
864 pub fn commit_program_on_device(
871 &self,
872 program: &Program<Val<E::SC>>,
873 ) -> CommittedTraceData<E::PB> {
874 let trace = generate_cached_trace(program);
875 let d_trace = self
876 .engine
877 .device()
878 .transport_matrix_to_device(&Arc::new(trace));
879 let (commitment, data) = self.engine.device().commit(std::slice::from_ref(&d_trace));
880 CommittedTraceData {
881 commitment,
882 trace: d_trace,
883 data,
884 }
885 }
886
887 pub fn transport_committed_exe_to_device(
893 &self,
894 committed_exe: &VmCommittedExe<E::SC>,
895 ) -> CommittedTraceData<E::PB> {
896 let commitment = committed_exe.get_program_commit();
897 let trace = &committed_exe.trace;
898 let prover_data = &committed_exe.prover_data;
899 self.engine
900 .device()
901 .transport_committed_trace_to_device(commitment, trace, prover_data)
902 }
903
904 pub fn load_program(&mut self, cached_program_trace: CommittedTraceData<E::PB>) {
906 self.chip_complex.system.load_program(cached_program_trace);
907 }
908
909 pub fn transport_init_memory_to_device(&mut self, memory: &GuestMemory) {
910 self.chip_complex
911 .system
912 .transport_init_memory_to_device(memory);
913 }
914
915 pub fn memory_top_tree(&self) -> Option<&[[Val<E::SC>; CHUNK]]> {
917 self.chip_complex.system.memory_top_tree()
918 }
919
920 pub fn executor_idx_to_air_idx(&self) -> Vec<usize> {
921 let ret = self.chip_complex.inventory.executor_idx_to_air_idx();
922 tracing::debug!("executor_idx_to_air_idx: {:?}", ret);
923 assert_eq!(self.executor().inventory.executors().len(), ret.len());
924 ret
925 }
926
927 pub fn build_metered_ctx(&self, exe: &VmExe<Val<E::SC>>) -> MeteredCtx {
929 let config = self.config().as_ref();
930 let program_len = exe.program.num_defined_instructions();
931
932 let (mut constant_trace_heights, air_names, widths, interactions): (
933 Vec<_>,
934 Vec<_>,
935 Vec<_>,
936 Vec<_>,
937 ) = self
938 .pk
939 .per_air
940 .iter()
941 .map(|pk| {
942 let constant_trace_height =
943 pk.preprocessed_data.as_ref().map(|pd| pd.trace.height());
944 let air_names = pk.air_name.clone();
945 let width = pk
946 .vk
947 .params
948 .width
949 .total_width(<<E::SC as StarkGenericConfig>::Challenge>::D);
950 let num_interactions = pk.vk.symbolic_constraints.interactions.len();
951 (constant_trace_height, air_names, width, num_interactions)
952 })
953 .multiunzip();
954
955 constant_trace_heights[PROGRAM_AIR_ID] = Some(program_len);
957 if config.has_public_values_chip() {
958 constant_trace_heights[PUBLIC_VALUES_AIR_ID] = Some(config.num_public_values);
960 }
961
962 self.executor().build_metered_ctx(
963 &constant_trace_heights,
964 &air_names,
965 &widths,
966 &interactions,
967 )
968 }
969
970 pub fn build_metered_cost_ctx(&self) -> MeteredCostCtx {
972 let widths: Vec<_> = self
973 .pk
974 .per_air
975 .iter()
976 .map(|pk| {
977 pk.vk
978 .params
979 .width
980 .total_width(<<E::SC as StarkGenericConfig>::Challenge>::D)
981 })
982 .collect();
983
984 self.executor().build_metered_cost_ctx(&widths)
985 }
986
987 pub fn num_airs(&self) -> usize {
988 let num_airs = self.pk.per_air.len();
989 debug_assert_eq!(num_airs, self.chip_complex.inventory.airs().num_airs());
990 num_airs
991 }
992
993 pub fn air_names(&self) -> impl Iterator<Item = &'_ str> {
994 self.pk.per_air.iter().map(|pk| pk.air_name.as_str())
995 }
996
997 #[cfg(feature = "stark-debug")]
999 pub fn debug_proving_ctx(&mut self, ctx: &ProvingContext<E::PB>) {
1000 if self.h_pk.is_none() {
1001 let air_inv = self.config().create_airs().unwrap();
1002 self.h_pk = Some(air_inv.keygen(&self.engine));
1003 }
1004 let pk = self.h_pk.as_ref().unwrap();
1005 debug_proving_ctx(self, pk, ctx);
1006 }
1007}
1008
1009#[derive(Serialize, Deserialize)]
1010#[serde(bound(
1011 serialize = "Com<SC>: Serialize",
1012 deserialize = "Com<SC>: Deserialize<'de>"
1013))]
1014pub struct ContinuationVmProof<SC: StarkGenericConfig> {
1015 pub per_segment: Vec<Proof<SC>>,
1016 pub user_public_values: UserPublicValuesProof<{ CHUNK }, Val<SC>>,
1017}
1018
1019pub trait ContinuationVmProver<SC: StarkGenericConfig> {
1021 fn prove(
1022 &mut self,
1023 input: impl Into<Streams<Val<SC>>>,
1024 ) -> Result<ContinuationVmProof<SC>, VirtualMachineError>;
1025}
1026
1027pub trait SingleSegmentVmProver<SC: StarkGenericConfig> {
1033 fn prove(
1034 &mut self,
1035 input: impl Into<Streams<Val<SC>>>,
1036 trace_heights: &[u32],
1037 ) -> Result<Proof<SC>, VirtualMachineError>;
1038}
1039
1040#[derive(Getters, MutGetters)]
1046pub struct VmInstance<E, VB>
1047where
1048 E: StarkEngine,
1049 VB: VmBuilder<E>,
1050{
1051 pub vm: VirtualMachine<E, VB>,
1052 pub interpreter: PreflightInterpretedInstance2<Val<E::SC>, VB::VmConfig>,
1053 #[getset(get = "pub")]
1054 program_commitment: Com<E::SC>,
1055 #[getset(get = "pub")]
1056 exe: Arc<VmExe<Val<E::SC>>>,
1057 #[getset(get = "pub", get_mut = "pub")]
1058 state: Option<VmState<Val<E::SC>, GuestMemory>>,
1059}
1060
1061impl<E, VB> VmInstance<E, VB>
1062where
1063 E: StarkEngine,
1064 VB: VmBuilder<E>,
1065{
1066 pub fn new(
1067 mut vm: VirtualMachine<E, VB>,
1068 exe: Arc<VmExe<Val<E::SC>>>,
1069 cached_program_trace: CommittedTraceData<E::PB>,
1070 ) -> Result<Self, StaticProgramError> {
1071 let program_commitment = cached_program_trace.commitment.clone();
1072 vm.load_program(cached_program_trace);
1073 let interpreter = vm.preflight_interpreter(&exe)?;
1074 let state = vm.create_initial_state(&exe, vec![]);
1075 Ok(Self {
1076 vm,
1077 interpreter,
1078 program_commitment,
1079 exe,
1080 state: Some(state),
1081 })
1082 }
1083
1084 #[instrument(name = "vm.reset_state", level = "debug", skip_all)]
1085 pub fn reset_state(&mut self, inputs: impl Into<Streams<Val<E::SC>>>) {
1086 self.state
1087 .as_mut()
1088 .unwrap()
1089 .reset(&self.exe.init_memory, self.exe.pc_start, inputs);
1090 }
1091}
1092
1093impl<E, VB> ContinuationVmProver<E::SC> for VmInstance<E, VB>
1094where
1095 E: StarkEngine,
1096 Val<E::SC>: PrimeField32,
1097 VB: VmBuilder<E>,
1098 <VB::VmConfig as VmExecutionConfig<Val<E::SC>>>::Executor: Executor<Val<E::SC>>
1099 + MeteredExecutor<Val<E::SC>>
1100 + PreflightExecutor<Val<E::SC>, VB::RecordArena>,
1101{
1102 fn prove(
1106 &mut self,
1107 input: impl Into<Streams<Val<E::SC>>>,
1108 ) -> Result<ContinuationVmProof<E::SC>, VirtualMachineError> {
1109 self.prove_continuations(input, |_, _| {})
1110 }
1111}
1112
1113impl<E, VB> VmInstance<E, VB>
1114where
1115 E: StarkEngine,
1116 Val<E::SC>: PrimeField32,
1117 VB: VmBuilder<E>,
1118 <VB::VmConfig as VmExecutionConfig<Val<E::SC>>>::Executor: Executor<Val<E::SC>>
1119 + MeteredExecutor<Val<E::SC>>
1120 + PreflightExecutor<Val<E::SC>, VB::RecordArena>,
1121{
1122 pub fn prove_continuations(
1126 &mut self,
1127 input: impl Into<Streams<Val<E::SC>>>,
1128 mut modify_ctx: impl FnMut(usize, &mut ProvingContext<E::PB>),
1129 ) -> Result<ContinuationVmProof<E::SC>, VirtualMachineError> {
1130 let input = input.into();
1131 self.reset_state(input.clone());
1132 let vm = &mut self.vm;
1133 let metered_ctx = vm.build_metered_ctx(&self.exe);
1134 let metered_interpreter = vm.metered_interpreter(&self.exe)?;
1135 let (segments, _) = metered_interpreter.execute_metered(input, metered_ctx)?;
1136 let mut proofs = Vec::with_capacity(segments.len());
1137 let mut state = self.state.take();
1138 for (seg_idx, segment) in segments.into_iter().enumerate() {
1139 let _segment_span = info_span!("prove_segment", segment = seg_idx).entered();
1140 let _prove_span = info_span!("total_proof").entered();
1142 let Segment {
1143 num_insns,
1144 trace_heights,
1145 ..
1146 } = segment;
1147 let from_state = Option::take(&mut state).unwrap();
1148 vm.transport_init_memory_to_device(&from_state.memory);
1149 let PreflightExecutionOutput {
1150 system_records,
1151 record_arenas,
1152 to_state,
1153 } = vm.execute_preflight(
1154 &mut self.interpreter,
1155 from_state,
1156 Some(num_insns),
1157 &trace_heights,
1158 )?;
1159 state = Some(to_state);
1160
1161 let mut ctx = vm.generate_proving_ctx(system_records, record_arenas)?;
1162 modify_ctx(seg_idx, &mut ctx);
1163 let proof = vm.engine.prove(vm.pk(), ctx);
1164 proofs.push(proof);
1165 }
1166 let to_state = state.unwrap();
1167 let final_memory = &to_state.memory.memory;
1168 let final_memory_top_tree = vm.memory_top_tree().expect("memory top tree should exist");
1169 let user_public_values = UserPublicValuesProof::compute(
1170 vm.config().as_ref().memory_config.memory_dimensions(),
1171 vm.config().as_ref().num_public_values,
1172 &vm_poseidon2_hasher(),
1173 final_memory,
1174 final_memory_top_tree,
1175 );
1176 self.state = Some(to_state);
1177 Ok(ContinuationVmProof {
1178 per_segment: proofs,
1179 user_public_values,
1180 })
1181 }
1182}
1183
1184impl<E, VB> SingleSegmentVmProver<E::SC> for VmInstance<E, VB>
1185where
1186 E: StarkEngine,
1187 Val<E::SC>: PrimeField32,
1188 VB: VmBuilder<E>,
1189 <VB::VmConfig as VmExecutionConfig<Val<E::SC>>>::Executor:
1190 PreflightExecutor<Val<E::SC>, VB::RecordArena>,
1191{
1192 #[instrument(name = "total_proof", skip_all)]
1193 fn prove(
1194 &mut self,
1195 input: impl Into<Streams<Val<E::SC>>>,
1196 trace_heights: &[u32],
1197 ) -> Result<Proof<E::SC>, VirtualMachineError> {
1198 self.reset_state(input);
1199 let vm = &mut self.vm;
1200 let exe = &self.exe;
1201 assert!(!vm.config().as_ref().continuation_enabled);
1202 let mut trace_heights = trace_heights.to_vec();
1203 trace_heights[PUBLIC_VALUES_AIR_ID] = vm.config().as_ref().num_public_values as u32;
1204 let state = self.state.take().expect("State should always be present");
1205 let num_custom_pvs = state.custom_pvs.len();
1206 let (proof, final_memory) = vm.prove(&mut self.interpreter, state, None, &trace_heights)?;
1207 let final_memory = final_memory.ok_or(ExecutionError::DidNotTerminate)?;
1208 self.state = Some(VmState::new_with_defaults(
1210 exe.pc_start,
1211 final_memory,
1212 vec![],
1213 DEFAULT_RNG_SEED,
1214 num_custom_pvs,
1215 ));
1216 Ok(proof)
1217 }
1218}
1219
1220pub fn verify_single<E>(
1226 engine: &E,
1227 vk: &MultiStarkVerifyingKey<E::SC>,
1228 proof: &Proof<E::SC>,
1229) -> Result<(), VerificationError>
1230where
1231 E: StarkEngine,
1232{
1233 engine.verify(vk, proof)
1234}
1235
1236pub struct VerifiedExecutionPayload<F> {
1238 pub exe_commit: [F; CHUNK],
1246 pub final_memory_root: [F; CHUNK],
1248}
1249
1250pub fn verify_segments<E>(
1267 engine: &E,
1268 vk: &MultiStarkVerifyingKey<E::SC>,
1269 proofs: &[Proof<E::SC>],
1270) -> Result<VerifiedExecutionPayload<Val<E::SC>>, VmVerificationError>
1271where
1272 E: StarkEngine,
1273 Val<E::SC>: PrimeField32,
1274 Com<E::SC>: AsRef<[Val<E::SC>; CHUNK]>,
1275{
1276 if proofs.is_empty() {
1277 return Err(VmVerificationError::ProofNotFound);
1278 }
1279 let mut prev_final_memory_root = None;
1280 let mut prev_final_pc = None;
1281 let mut start_pc = None;
1282 let mut initial_memory_root = None;
1283 let mut program_commit = None;
1284
1285 for (i, proof) in proofs.iter().enumerate() {
1286 let res = engine.verify(vk, proof);
1287 match res {
1288 Ok(_) => (),
1289 Err(e) => return Err(VmVerificationError::StarkError(e)),
1290 };
1291
1292 let mut program_air_present = false;
1293 let mut connector_air_present = false;
1294 let mut merkle_air_present = false;
1295
1296 for air_proof_data in proof.per_air.iter() {
1298 let pvs = &air_proof_data.public_values;
1299 let air_vk = &vk.inner.per_air[air_proof_data.air_id];
1300 if air_proof_data.air_id == PROGRAM_AIR_ID {
1301 program_air_present = true;
1302 if i == 0 {
1303 program_commit =
1304 Some(proof.commitments.main_trace[PROGRAM_CACHED_TRACE_INDEX].as_ref());
1305 } else if program_commit.unwrap()
1306 != proof.commitments.main_trace[PROGRAM_CACHED_TRACE_INDEX].as_ref()
1307 {
1308 return Err(VmVerificationError::ProgramCommitMismatch { index: i });
1309 }
1310 } else if air_proof_data.air_id == CONNECTOR_AIR_ID {
1311 connector_air_present = true;
1312 let pvs: &VmConnectorPvs<_> = pvs.as_slice().borrow();
1313
1314 if i != 0 {
1315 if pvs.initial_pc != prev_final_pc.unwrap() {
1317 return Err(VmVerificationError::InitialPcMismatch {
1318 initial: pvs.initial_pc.as_canonical_u32(),
1319 prev_final: prev_final_pc.unwrap().as_canonical_u32(),
1320 });
1321 }
1322 } else {
1323 start_pc = Some(pvs.initial_pc);
1324 }
1325 prev_final_pc = Some(pvs.final_pc);
1326
1327 let expected_is_terminate = i == proofs.len() - 1;
1328 if pvs.is_terminate != FieldAlgebra::from_bool(expected_is_terminate) {
1329 return Err(VmVerificationError::IsTerminateMismatch {
1330 expected: expected_is_terminate,
1331 actual: pvs.is_terminate.as_canonical_u32() != 0,
1332 });
1333 }
1334
1335 let expected_exit_code = if expected_is_terminate {
1336 ExitCode::Success as u32
1337 } else {
1338 DEFAULT_SUSPEND_EXIT_CODE
1339 };
1340 if pvs.exit_code != FieldAlgebra::from_canonical_u32(expected_exit_code) {
1341 return Err(VmVerificationError::ExitCodeMismatch {
1342 expected: expected_exit_code,
1343 actual: pvs.exit_code.as_canonical_u32(),
1344 });
1345 }
1346 } else if air_proof_data.air_id == MERKLE_AIR_ID {
1347 merkle_air_present = true;
1348 let pvs: &MemoryMerklePvs<_, CHUNK> = pvs.as_slice().borrow();
1349
1350 if i != 0 {
1352 if pvs.initial_root != prev_final_memory_root.unwrap() {
1353 return Err(VmVerificationError::InitialMemoryRootMismatch);
1354 }
1355 } else {
1356 initial_memory_root = Some(pvs.initial_root);
1357 }
1358 prev_final_memory_root = Some(pvs.final_root);
1359 } else {
1360 if !pvs.is_empty() {
1361 return Err(VmVerificationError::UnexpectedPvs {
1362 expected: 0,
1363 actual: pvs.len(),
1364 });
1365 }
1366 debug_assert_eq!(air_vk.params.num_public_values, 0);
1368 }
1369 }
1370 if !program_air_present {
1371 return Err(VmVerificationError::SystemAirMissing {
1372 air_id: PROGRAM_AIR_ID,
1373 });
1374 }
1375 if !connector_air_present {
1376 return Err(VmVerificationError::SystemAirMissing {
1377 air_id: CONNECTOR_AIR_ID,
1378 });
1379 }
1380 if !merkle_air_present {
1381 return Err(VmVerificationError::SystemAirMissing {
1382 air_id: MERKLE_AIR_ID,
1383 });
1384 }
1385 }
1386 let exe_commit = compute_exe_commit(
1387 &vm_poseidon2_hasher(),
1388 program_commit.unwrap(),
1389 initial_memory_root.as_ref().unwrap(),
1390 start_pc.unwrap(),
1391 );
1392 Ok(VerifiedExecutionPayload {
1393 exe_commit,
1394 final_memory_root: prev_final_memory_root.unwrap(),
1395 })
1396}
1397
1398impl<SC: StarkGenericConfig> Clone for ContinuationVmProof<SC>
1399where
1400 Com<SC>: Clone,
1401{
1402 fn clone(&self) -> Self {
1403 Self {
1404 per_segment: self.per_segment.clone(),
1405 user_public_values: self.user_public_values.clone(),
1406 }
1407 }
1408}
1409
1410pub(super) fn create_memory_image(
1411 memory_config: &MemoryConfig,
1412 init_memory: &SparseMemoryImage,
1413) -> GuestMemory {
1414 let mut inner = AddressMap::new(memory_config.addr_spaces.clone());
1415 inner.set_from_sparse(init_memory);
1416 GuestMemory::new(inner)
1417}
1418
1419impl<E, VC> VirtualMachine<E, VC>
1420where
1421 E: StarkEngine,
1422 VC: VmBuilder<E>,
1423 VC::SystemChipInventory: SystemWithFixedTraceHeights,
1424{
1425 pub fn override_system_trace_heights(&mut self, heights: &[u32]) {
1427 let num_sys_airs = self.config().as_ref().num_airs();
1428 assert!(heights.len() >= num_sys_airs);
1429 self.chip_complex
1430 .system
1431 .override_trace_heights(&heights[..num_sys_airs]);
1432 }
1433}
1434
1435#[cfg(any(debug_assertions, feature = "test-utils", feature = "stark-debug"))]
1445#[tracing::instrument(level = "debug", skip_all)]
1446pub fn debug_proving_ctx<E, VB>(
1447 vm: &VirtualMachine<E, VB>,
1448 pk: &MultiStarkProvingKey<E::SC>,
1449 ctx: &ProvingContext<E::PB>,
1450) where
1451 E: StarkEngine,
1452 VB: VmBuilder<E>,
1453{
1454 use itertools::multiunzip;
1455 use openvm_stark_backend::prover::types::AirProofRawInput;
1456
1457 let device = vm.engine.device();
1458 let air_inv = vm.config().create_airs().unwrap();
1459 let global_airs = air_inv.into_airs().collect_vec();
1460 let (airs, pks, proof_inputs): (Vec<_>, Vec<_>, Vec<_>) =
1461 multiunzip(ctx.per_air.iter().map(|(air_id, air_ctx)| {
1462 let cached_mains = air_ctx
1464 .cached_mains
1465 .iter()
1466 .map(|pre| device.transport_matrix_from_device_to_host(&pre.trace))
1467 .collect_vec();
1468 let common_main = air_ctx
1469 .common_main
1470 .as_ref()
1471 .map(|m| device.transport_matrix_from_device_to_host(m));
1472 let public_values = air_ctx.public_values.clone();
1473 let raw = AirProofRawInput {
1474 cached_mains,
1475 common_main,
1476 public_values,
1477 };
1478 (
1479 global_airs[*air_id].clone(),
1480 pk.per_air[*air_id].clone(),
1481 raw,
1482 )
1483 }));
1484 vm.engine.debug(&airs, &pks, &proof_inputs);
1485}
1486
1487#[cfg(feature = "metrics")]
1488mod vm_metrics {
1489 use std::iter::zip;
1490
1491 use metrics::counter;
1492
1493 use super::*;
1494 use crate::arch::Arena;
1495
1496 impl<E, VB> VirtualMachine<E, VB>
1497 where
1498 E: StarkEngine,
1499 VB: VmBuilder<E>,
1500 {
1501 pub(crate) fn get_trace_heights_from_arenas(
1509 &self,
1510 system_records: &SystemRecords<Val<E::SC>>,
1511 record_arenas: &[VB::RecordArena],
1512 ) -> Vec<usize> {
1513 let num_airs = self.num_airs();
1514 assert_eq!(num_airs, record_arenas.len());
1515 let mut heights: Vec<usize> = record_arenas
1516 .iter()
1517 .map(|arena| arena.current_trace_height())
1518 .collect();
1519 for (pk, height) in zip(&self.pk.per_air, &mut heights) {
1521 if let Some(constant_height) =
1522 pk.preprocessed_data.as_ref().map(|pd| pd.trace.height())
1523 {
1524 *height = constant_height;
1525 }
1526 }
1527 heights[PROGRAM_AIR_ID] = system_records.filtered_exec_frequencies.len();
1529
1530 heights
1531 }
1532
1533 pub(crate) fn finalize_metrics(&self, heights: &mut [usize]) {
1536 self.chip_complex.system.finalize_trace_heights(heights);
1537 let mut main_cells_used = 0usize;
1538 let mut total_cells_used = 0usize;
1539 for (pk, height) in zip(&self.pk.per_air, heights.iter()) {
1540 let width = &pk.vk.params.width;
1541 main_cells_used += width.main_width() * *height;
1542 total_cells_used +=
1543 width.total_width(<E::SC as StarkGenericConfig>::Challenge::D) * *height;
1544 }
1545 tracing::debug!(?heights);
1546 tracing::info!(main_cells_used, total_cells_used);
1547 counter!("main_cells_used").absolute(main_cells_used as u64);
1548 counter!("total_cells_used").absolute(total_cells_used as u64);
1549
1550 #[cfg(feature = "perf-metrics")]
1551 {
1552 for (name, value) in zip(self.air_names(), heights) {
1553 let labels = [("air_name", name.to_string())];
1554 counter!("rows_used", &labels).absolute(*value as u64);
1555 }
1556 }
1557 }
1558 }
1559}