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