1use std::{borrow::Borrow, collections::VecDeque, marker::PhantomData, mem, sync::Arc};
2
3use openvm_circuit::system::program::trace::compute_exe_commit;
4use openvm_instructions::exe::VmExe;
5use openvm_stark_backend::{
6 config::{Com, Domain, StarkGenericConfig, Val},
7 engine::StarkEngine,
8 keygen::types::{LinearConstraint, MultiStarkProvingKey, MultiStarkVerifyingKey},
9 p3_commit::PolynomialSpace,
10 p3_field::{FieldAlgebra, PrimeField32},
11 proof::Proof,
12 prover::types::{CommittedTraceData, ProofInput},
13 utils::metrics_span,
14 verifier::VerificationError,
15 Chip,
16};
17use serde::{Deserialize, Serialize};
18use thiserror::Error;
19use tracing::info_span;
20
21use super::{
22 ExecutionError, VmComplexTraceHeights, VmConfig, CONNECTOR_AIR_ID, MERKLE_AIR_ID,
23 PROGRAM_AIR_ID, PROGRAM_CACHED_TRACE_INDEX,
24};
25#[cfg(feature = "bench-metrics")]
26use crate::metrics::VmMetrics;
27use crate::{
28 arch::{hasher::poseidon2::vm_poseidon2_hasher, segment::ExecutionSegment},
29 system::{
30 connector::{VmConnectorPvs, DEFAULT_SUSPEND_EXIT_CODE},
31 memory::{
32 merkle::MemoryMerklePvs,
33 paged_vec::AddressMap,
34 tree::public_values::{UserPublicValuesProof, UserPublicValuesProofError},
35 MemoryImage, CHUNK,
36 },
37 program::trace::VmCommittedExe,
38 },
39};
40
41#[derive(Error, Debug)]
42pub enum GenerationError {
43 #[error("generated trace heights violate constraints")]
44 TraceHeightsLimitExceeded,
45 #[error(transparent)]
46 Execution(#[from] ExecutionError),
47}
48
49pub type VmMemoryState<F> = MemoryImage<F>;
51
52#[derive(Clone, Default, Debug)]
53pub struct Streams<F> {
54 pub input_stream: VecDeque<Vec<F>>,
55 pub hint_stream: VecDeque<F>,
56 pub hint_space: Vec<Vec<F>>,
57}
58
59impl<F> Streams<F> {
60 pub fn new(input_stream: impl Into<VecDeque<Vec<F>>>) -> Self {
61 Self {
62 input_stream: input_stream.into(),
63 hint_stream: VecDeque::default(),
64 hint_space: Vec::default(),
65 }
66 }
67}
68
69impl<F> From<VecDeque<Vec<F>>> for Streams<F> {
70 fn from(value: VecDeque<Vec<F>>) -> Self {
71 Streams::new(value)
72 }
73}
74
75impl<F> From<Vec<Vec<F>>> for Streams<F> {
76 fn from(value: Vec<Vec<F>>) -> Self {
77 Streams::new(value)
78 }
79}
80
81pub struct VmExecutor<F, VC> {
82 pub config: VC,
83 pub overridden_heights: Option<VmComplexTraceHeights>,
84 pub trace_height_constraints: Vec<LinearConstraint>,
85 _marker: PhantomData<F>,
86}
87
88#[repr(i32)]
89pub enum ExitCode {
90 Success = 0,
91 Error = 1,
92 Suspended = -1, }
94
95pub struct VmExecutorResult<SC: StarkGenericConfig> {
96 pub per_segment: Vec<ProofInput<SC>>,
97 pub final_memory: Option<VmMemoryState<Val<SC>>>,
99}
100
101pub struct VmExecutorNextSegmentState<F: PrimeField32> {
102 pub memory: MemoryImage<F>,
103 pub input: Streams<F>,
104 pub pc: u32,
105 #[cfg(feature = "bench-metrics")]
106 pub metrics: VmMetrics,
107}
108
109impl<F: PrimeField32> VmExecutorNextSegmentState<F> {
110 pub fn new(memory: MemoryImage<F>, input: impl Into<Streams<F>>, pc: u32) -> Self {
111 Self {
112 memory,
113 input: input.into(),
114 pc,
115 #[cfg(feature = "bench-metrics")]
116 metrics: VmMetrics::default(),
117 }
118 }
119}
120
121pub struct VmExecutorOneSegmentResult<F: PrimeField32, VC: VmConfig<F>> {
122 pub segment: ExecutionSegment<F, VC>,
123 pub next_state: Option<VmExecutorNextSegmentState<F>>,
124}
125
126impl<F, VC> VmExecutor<F, VC>
127where
128 F: PrimeField32,
129 VC: VmConfig<F>,
130{
131 pub fn new(config: VC) -> Self {
135 Self::new_with_overridden_trace_heights(config, None)
136 }
137
138 pub fn set_override_trace_heights(&mut self, overridden_heights: VmComplexTraceHeights) {
139 self.overridden_heights = Some(overridden_heights);
140 }
141
142 pub fn new_with_overridden_trace_heights(
143 config: VC,
144 overridden_heights: Option<VmComplexTraceHeights>,
145 ) -> Self {
146 Self {
147 config,
148 overridden_heights,
149 trace_height_constraints: vec![],
150 _marker: Default::default(),
151 }
152 }
153
154 pub fn continuation_enabled(&self) -> bool {
155 self.config.system().continuation_enabled
156 }
157
158 pub fn execute_and_then<R, E>(
164 &self,
165 exe: impl Into<VmExe<F>>,
166 input: impl Into<Streams<F>>,
167 mut f: impl FnMut(usize, ExecutionSegment<F, VC>) -> Result<R, E>,
168 map_err: impl Fn(ExecutionError) -> E,
169 ) -> Result<Vec<R>, E> {
170 let mem_config = self.config.system().memory_config;
171 let exe = exe.into();
172 let mut segment_results = vec![];
173 let memory = AddressMap::from_iter(
174 mem_config.as_offset,
175 1 << mem_config.as_height,
176 1 << mem_config.pointer_max_bits,
177 exe.init_memory.clone(),
178 );
179 let pc = exe.pc_start;
180 let mut state = VmExecutorNextSegmentState::new(memory, input, pc);
181
182 #[cfg(feature = "bench-metrics")]
183 {
184 state.metrics.fn_bounds = exe.fn_bounds.clone();
185 }
186
187 let mut segment_idx = 0;
188
189 loop {
190 let _span = info_span!("execute_segment", segment = segment_idx).entered();
191 let one_segment_result = self
192 .execute_until_segment(exe.clone(), state)
193 .map_err(&map_err)?;
194 segment_results.push(f(segment_idx, one_segment_result.segment)?);
195 if one_segment_result.next_state.is_none() {
196 break;
197 }
198 state = one_segment_result.next_state.unwrap();
199 segment_idx += 1;
200 }
201 tracing::debug!("Number of continuation segments: {}", segment_results.len());
202 #[cfg(feature = "bench-metrics")]
203 metrics::counter!("num_segments").absolute(segment_results.len() as u64);
204
205 Ok(segment_results)
206 }
207
208 pub fn execute_segments(
209 &self,
210 exe: impl Into<VmExe<F>>,
211 input: impl Into<Streams<F>>,
212 ) -> Result<Vec<ExecutionSegment<F, VC>>, ExecutionError> {
213 self.execute_and_then(exe, input, |_, seg| Ok(seg), |err| err)
214 }
215
216 pub fn execute_until_segment(
221 &self,
222 exe: impl Into<VmExe<F>>,
223 from_state: VmExecutorNextSegmentState<F>,
224 ) -> Result<VmExecutorOneSegmentResult<F, VC>, ExecutionError> {
225 let exe = exe.into();
226 let mut segment = ExecutionSegment::new(
227 &self.config,
228 exe.program.clone(),
229 from_state.input,
230 Some(from_state.memory),
231 self.trace_height_constraints.clone(),
232 exe.fn_bounds.clone(),
233 );
234 #[cfg(feature = "bench-metrics")]
235 {
236 segment.metrics = from_state.metrics;
237 }
238 if let Some(overridden_heights) = self.overridden_heights.as_ref() {
239 segment.set_override_trace_heights(overridden_heights.clone());
240 }
241 let state = metrics_span("execute_time_ms", || segment.execute_from_pc(from_state.pc))?;
242
243 if state.is_terminated {
244 return Ok(VmExecutorOneSegmentResult {
245 segment,
246 next_state: None,
247 });
248 }
249
250 assert!(
251 self.continuation_enabled(),
252 "multiple segments require to enable continuations"
253 );
254 assert_eq!(
255 state.pc,
256 segment.chip_complex.connector_chip().boundary_states[1]
257 .unwrap()
258 .pc
259 );
260 let final_memory = mem::take(&mut segment.final_memory)
261 .expect("final memory should be set in continuations segment");
262 let streams = segment.chip_complex.take_streams();
263 #[cfg(feature = "bench-metrics")]
264 let metrics = segment.metrics.partial_take();
265 Ok(VmExecutorOneSegmentResult {
266 segment,
267 next_state: Some(VmExecutorNextSegmentState {
268 memory: final_memory,
269 input: streams,
270 pc: state.pc,
271 #[cfg(feature = "bench-metrics")]
272 metrics,
273 }),
274 })
275 }
276
277 pub fn execute(
278 &self,
279 exe: impl Into<VmExe<F>>,
280 input: impl Into<Streams<F>>,
281 ) -> Result<Option<VmMemoryState<F>>, ExecutionError> {
282 let mut last = None;
283 self.execute_and_then(
284 exe,
285 input,
286 |_, seg| {
287 last = Some(seg);
288 Ok(())
289 },
290 |err| err,
291 )?;
292 let last = last.expect("at least one segment must be executed");
293 let final_memory = last.final_memory;
294 let end_state =
295 last.chip_complex.connector_chip().boundary_states[1].expect("end state must be set");
296 if end_state.is_terminate != 1 {
297 return Err(ExecutionError::DidNotTerminate);
298 }
299 if end_state.exit_code != ExitCode::Success as u32 {
300 return Err(ExecutionError::FailedWithExitCode(end_state.exit_code));
301 }
302 Ok(final_memory)
303 }
304
305 pub fn execute_and_generate<SC: StarkGenericConfig>(
306 &self,
307 exe: impl Into<VmExe<F>>,
308 input: impl Into<Streams<F>>,
309 ) -> Result<VmExecutorResult<SC>, GenerationError>
310 where
311 Domain<SC>: PolynomialSpace<Val = F>,
312 VC::Executor: Chip<SC>,
313 VC::Periphery: Chip<SC>,
314 {
315 self.execute_and_generate_impl(exe.into(), None, input)
316 }
317
318 pub fn execute_and_generate_with_cached_program<SC: StarkGenericConfig>(
319 &self,
320 committed_exe: Arc<VmCommittedExe<SC>>,
321 input: impl Into<Streams<F>>,
322 ) -> Result<VmExecutorResult<SC>, GenerationError>
323 where
324 Domain<SC>: PolynomialSpace<Val = F>,
325 VC::Executor: Chip<SC>,
326 VC::Periphery: Chip<SC>,
327 {
328 self.execute_and_generate_impl(
329 committed_exe.exe.clone(),
330 Some(committed_exe.committed_program.clone()),
331 input,
332 )
333 }
334
335 fn execute_and_generate_impl<SC: StarkGenericConfig>(
336 &self,
337 exe: VmExe<F>,
338 committed_program: Option<CommittedTraceData<SC>>,
339 input: impl Into<Streams<F>>,
340 ) -> Result<VmExecutorResult<SC>, GenerationError>
341 where
342 Domain<SC>: PolynomialSpace<Val = F>,
343 VC::Executor: Chip<SC>,
344 VC::Periphery: Chip<SC>,
345 {
346 let mut final_memory = None;
347 let per_segment = self.execute_and_then(
348 exe,
349 input,
350 |seg_idx, mut seg| {
351 final_memory = mem::take(&mut seg.final_memory);
354 tracing::info_span!("trace_gen", segment = seg_idx)
355 .in_scope(|| seg.generate_proof_input(committed_program.clone()))
356 },
357 GenerationError::Execution,
358 )?;
359
360 Ok(VmExecutorResult {
361 per_segment,
362 final_memory,
363 })
364 }
365
366 pub fn set_trace_height_constraints(&mut self, constraints: Vec<LinearConstraint>) {
367 self.trace_height_constraints = constraints;
368 }
369}
370
371pub struct SingleSegmentVmExecutor<F, VC> {
373 pub config: VC,
374 pub overridden_heights: Option<VmComplexTraceHeights>,
375 pub trace_height_constraints: Vec<LinearConstraint>,
376 _marker: PhantomData<F>,
377}
378
379pub struct SingleSegmentVmExecutionResult<F> {
381 pub public_values: Vec<Option<F>>,
383 pub air_heights: Vec<usize>,
385 pub vm_heights: VmComplexTraceHeights,
387}
388
389impl<F, VC> SingleSegmentVmExecutor<F, VC>
390where
391 F: PrimeField32,
392 VC: VmConfig<F>,
393{
394 pub fn new(config: VC) -> Self {
395 Self::new_with_overridden_trace_heights(config, None)
396 }
397
398 pub fn new_with_overridden_trace_heights(
399 config: VC,
400 overridden_heights: Option<VmComplexTraceHeights>,
401 ) -> Self {
402 assert!(
403 !config.system().continuation_enabled,
404 "Single segment VM doesn't support continuation mode"
405 );
406 Self {
407 config,
408 overridden_heights,
409 trace_height_constraints: vec![],
410 _marker: Default::default(),
411 }
412 }
413
414 pub fn set_override_trace_heights(&mut self, overridden_heights: VmComplexTraceHeights) {
415 self.overridden_heights = Some(overridden_heights);
416 }
417
418 pub fn set_trace_height_constraints(&mut self, constraints: Vec<LinearConstraint>) {
419 self.trace_height_constraints = constraints;
420 }
421
422 pub fn execute_and_compute_heights(
424 &self,
425 exe: impl Into<VmExe<F>>,
426 input: impl Into<Streams<F>>,
427 ) -> Result<SingleSegmentVmExecutionResult<F>, ExecutionError> {
428 let segment = {
429 let mut segment = self.execute_impl(exe.into(), input.into())?;
430 segment.chip_complex.finalize_memory();
431 segment
432 };
433 let air_heights = segment.chip_complex.current_trace_heights();
434 let vm_heights = segment.chip_complex.get_internal_trace_heights();
435 let public_values = if let Some(pv_chip) = segment.chip_complex.public_values_chip() {
436 pv_chip.core.get_custom_public_values()
437 } else {
438 vec![]
439 };
440 Ok(SingleSegmentVmExecutionResult {
441 public_values,
442 air_heights,
443 vm_heights,
444 })
445 }
446
447 pub fn execute_and_generate<SC: StarkGenericConfig>(
449 &self,
450 committed_exe: Arc<VmCommittedExe<SC>>,
451 input: impl Into<Streams<F>>,
452 ) -> Result<ProofInput<SC>, GenerationError>
453 where
454 Domain<SC>: PolynomialSpace<Val = F>,
455 VC::Executor: Chip<SC>,
456 VC::Periphery: Chip<SC>,
457 {
458 let segment = self.execute_impl(committed_exe.exe.clone(), input)?;
459 let proof_input = tracing::info_span!("trace_gen").in_scope(|| {
460 segment.generate_proof_input(Some(committed_exe.committed_program.clone()))
461 })?;
462 Ok(proof_input)
463 }
464
465 fn execute_impl(
466 &self,
467 exe: VmExe<F>,
468 input: impl Into<Streams<F>>,
469 ) -> Result<ExecutionSegment<F, VC>, ExecutionError> {
470 let pc_start = exe.pc_start;
471 let mut segment = ExecutionSegment::new(
472 &self.config,
473 exe.program.clone(),
474 input.into(),
475 None,
476 self.trace_height_constraints.clone(),
477 exe.fn_bounds.clone(),
478 );
479 if let Some(overridden_heights) = self.overridden_heights.as_ref() {
480 segment.set_override_trace_heights(overridden_heights.clone());
481 }
482 metrics_span("execute_time_ms", || segment.execute_from_pc(pc_start))?;
483 Ok(segment)
484 }
485}
486
487#[derive(Error, Debug)]
488pub enum VmVerificationError {
489 #[error("no proof is provided")]
490 ProofNotFound,
491
492 #[error("program commit mismatch (index of mismatch proof: {index}")]
493 ProgramCommitMismatch { index: usize },
494
495 #[error("initial pc mismatch (initial: {initial}, prev_final: {prev_final})")]
496 InitialPcMismatch { initial: u32, prev_final: u32 },
497
498 #[error("initial memory root mismatch")]
499 InitialMemoryRootMismatch,
500
501 #[error("is terminate mismatch (expected: {expected}, actual: {actual})")]
502 IsTerminateMismatch { expected: bool, actual: bool },
503
504 #[error("exit code mismatch")]
505 ExitCodeMismatch { expected: u32, actual: u32 },
506
507 #[error("AIR has unexpected public values (expected: {expected}, actual: {actual})")]
508 UnexpectedPvs { expected: usize, actual: usize },
509
510 #[error("missing system AIR with ID {air_id}")]
511 SystemAirMissing { air_id: usize },
512
513 #[error("stark verification error: {0}")]
514 StarkError(#[from] VerificationError),
515
516 #[error("user public values proof error: {0}")]
517 UserPublicValuesError(#[from] UserPublicValuesProofError),
518}
519
520pub struct VirtualMachine<SC: StarkGenericConfig, E, VC> {
521 pub engine: E,
523 pub executor: VmExecutor<Val<SC>, VC>,
525 _marker: PhantomData<SC>,
526}
527
528impl<F, SC, E, VC> VirtualMachine<SC, E, VC>
529where
530 F: PrimeField32,
531 SC: StarkGenericConfig,
532 E: StarkEngine<SC>,
533 Domain<SC>: PolynomialSpace<Val = F>,
534 VC: VmConfig<F>,
535 VC::Executor: Chip<SC>,
536 VC::Periphery: Chip<SC>,
537{
538 pub fn new(engine: E, config: VC) -> Self {
539 let executor = VmExecutor::new(config);
540 Self {
541 engine,
542 executor,
543 _marker: PhantomData,
544 }
545 }
546
547 pub fn new_with_overridden_trace_heights(
548 engine: E,
549 config: VC,
550 overridden_heights: Option<VmComplexTraceHeights>,
551 ) -> Self {
552 let executor = VmExecutor::new_with_overridden_trace_heights(config, overridden_heights);
553 Self {
554 engine,
555 executor,
556 _marker: PhantomData,
557 }
558 }
559
560 pub fn config(&self) -> &VC {
561 &self.executor.config
562 }
563
564 pub fn keygen(&self) -> MultiStarkProvingKey<SC> {
565 let mut keygen_builder = self.engine.keygen_builder();
566 let chip_complex = self.config().create_chip_complex().unwrap();
567 for air in chip_complex.airs() {
568 keygen_builder.add_air(air);
569 }
570 keygen_builder.generate_pk()
571 }
572
573 pub fn set_trace_height_constraints(
574 &mut self,
575 trace_height_constraints: Vec<LinearConstraint>,
576 ) {
577 self.executor
578 .set_trace_height_constraints(trace_height_constraints);
579 }
580
581 pub fn commit_exe(&self, exe: impl Into<VmExe<F>>) -> Arc<VmCommittedExe<SC>> {
582 let exe = exe.into();
583 Arc::new(VmCommittedExe::commit(exe, self.engine.config().pcs()))
584 }
585
586 pub fn execute(
587 &self,
588 exe: impl Into<VmExe<F>>,
589 input: impl Into<Streams<F>>,
590 ) -> Result<Option<VmMemoryState<F>>, ExecutionError> {
591 self.executor.execute(exe, input)
592 }
593
594 pub fn execute_and_generate(
595 &self,
596 exe: impl Into<VmExe<F>>,
597 input: impl Into<Streams<F>>,
598 ) -> Result<VmExecutorResult<SC>, GenerationError> {
599 self.executor.execute_and_generate(exe, input)
600 }
601
602 pub fn execute_and_generate_with_cached_program(
603 &self,
604 committed_exe: Arc<VmCommittedExe<SC>>,
605 input: impl Into<Streams<F>>,
606 ) -> Result<VmExecutorResult<SC>, GenerationError>
607 where
608 Domain<SC>: PolynomialSpace<Val = F>,
609 {
610 self.executor
611 .execute_and_generate_with_cached_program(committed_exe, input)
612 }
613
614 pub fn prove_single(
615 &self,
616 pk: &MultiStarkProvingKey<SC>,
617 proof_input: ProofInput<SC>,
618 ) -> Proof<SC> {
619 self.engine.prove(pk, proof_input)
620 }
621
622 pub fn prove(
623 &self,
624 pk: &MultiStarkProvingKey<SC>,
625 results: VmExecutorResult<SC>,
626 ) -> Vec<Proof<SC>> {
627 results
628 .per_segment
629 .into_iter()
630 .enumerate()
631 .map(|(seg_idx, proof_input)| {
632 tracing::info_span!("prove_segment", segment = seg_idx)
633 .in_scope(|| self.engine.prove(pk, proof_input))
634 })
635 .collect()
636 }
637
638 pub fn verify(
643 &self,
644 vk: &MultiStarkVerifyingKey<SC>,
645 proofs: Vec<Proof<SC>>,
646 ) -> Result<(), VmVerificationError>
647 where
648 Val<SC>: PrimeField32,
649 Com<SC>: AsRef<[Val<SC>; CHUNK]> + From<[Val<SC>; CHUNK]>,
650 {
651 if self.config().system().continuation_enabled {
652 verify_segments(&self.engine, vk, &proofs).map(|_| ())
653 } else {
654 assert_eq!(proofs.len(), 1);
655 verify_single(&self.engine, vk, &proofs.into_iter().next().unwrap())
656 .map_err(VmVerificationError::StarkError)
657 }
658 }
659}
660
661pub fn verify_single<SC, E>(
667 engine: &E,
668 vk: &MultiStarkVerifyingKey<SC>,
669 proof: &Proof<SC>,
670) -> Result<(), VerificationError>
671where
672 SC: StarkGenericConfig,
673 E: StarkEngine<SC>,
674{
675 engine.verify(vk, proof)
676}
677
678pub struct VerifiedExecutionPayload<F> {
680 pub exe_commit: [F; CHUNK],
688 pub final_memory_root: [F; CHUNK],
690}
691
692pub fn verify_segments<SC, E>(
709 engine: &E,
710 vk: &MultiStarkVerifyingKey<SC>,
711 proofs: &[Proof<SC>],
712) -> Result<VerifiedExecutionPayload<Val<SC>>, VmVerificationError>
713where
714 SC: StarkGenericConfig,
715 E: StarkEngine<SC>,
716 Val<SC>: PrimeField32,
717 Com<SC>: AsRef<[Val<SC>; CHUNK]>,
718{
719 if proofs.is_empty() {
720 return Err(VmVerificationError::ProofNotFound);
721 }
722 let mut prev_final_memory_root = None;
723 let mut prev_final_pc = None;
724 let mut start_pc = None;
725 let mut initial_memory_root = None;
726 let mut program_commit = None;
727
728 for (i, proof) in proofs.iter().enumerate() {
729 let res = engine.verify(vk, proof);
730 match res {
731 Ok(_) => (),
732 Err(e) => return Err(VmVerificationError::StarkError(e)),
733 };
734
735 let mut program_air_present = false;
736 let mut connector_air_present = false;
737 let mut merkle_air_present = false;
738
739 for air_proof_data in proof.per_air.iter() {
741 let pvs = &air_proof_data.public_values;
742 let air_vk = &vk.inner.per_air[air_proof_data.air_id];
743 if air_proof_data.air_id == PROGRAM_AIR_ID {
744 program_air_present = true;
745 if i == 0 {
746 program_commit =
747 Some(proof.commitments.main_trace[PROGRAM_CACHED_TRACE_INDEX].as_ref());
748 } else if program_commit.unwrap()
749 != proof.commitments.main_trace[PROGRAM_CACHED_TRACE_INDEX].as_ref()
750 {
751 return Err(VmVerificationError::ProgramCommitMismatch { index: i });
752 }
753 } else if air_proof_data.air_id == CONNECTOR_AIR_ID {
754 connector_air_present = true;
755 let pvs: &VmConnectorPvs<_> = pvs.as_slice().borrow();
756
757 if i != 0 {
758 if pvs.initial_pc != prev_final_pc.unwrap() {
760 return Err(VmVerificationError::InitialPcMismatch {
761 initial: pvs.initial_pc.as_canonical_u32(),
762 prev_final: prev_final_pc.unwrap().as_canonical_u32(),
763 });
764 }
765 } else {
766 start_pc = Some(pvs.initial_pc);
767 }
768 prev_final_pc = Some(pvs.final_pc);
769
770 let expected_is_terminate = i == proofs.len() - 1;
771 if pvs.is_terminate != FieldAlgebra::from_bool(expected_is_terminate) {
772 return Err(VmVerificationError::IsTerminateMismatch {
773 expected: expected_is_terminate,
774 actual: pvs.is_terminate.as_canonical_u32() != 0,
775 });
776 }
777
778 let expected_exit_code = if expected_is_terminate {
779 ExitCode::Success as u32
780 } else {
781 DEFAULT_SUSPEND_EXIT_CODE
782 };
783 if pvs.exit_code != FieldAlgebra::from_canonical_u32(expected_exit_code) {
784 return Err(VmVerificationError::ExitCodeMismatch {
785 expected: expected_exit_code,
786 actual: pvs.exit_code.as_canonical_u32(),
787 });
788 }
789 } else if air_proof_data.air_id == MERKLE_AIR_ID {
790 merkle_air_present = true;
791 let pvs: &MemoryMerklePvs<_, CHUNK> = pvs.as_slice().borrow();
792
793 if i != 0 {
795 if pvs.initial_root != prev_final_memory_root.unwrap() {
796 return Err(VmVerificationError::InitialMemoryRootMismatch);
797 }
798 } else {
799 initial_memory_root = Some(pvs.initial_root);
800 }
801 prev_final_memory_root = Some(pvs.final_root);
802 } else {
803 if !pvs.is_empty() {
804 return Err(VmVerificationError::UnexpectedPvs {
805 expected: 0,
806 actual: pvs.len(),
807 });
808 }
809 debug_assert_eq!(air_vk.params.num_public_values, 0);
811 }
812 }
813 if !program_air_present {
814 return Err(VmVerificationError::SystemAirMissing {
815 air_id: PROGRAM_AIR_ID,
816 });
817 }
818 if !connector_air_present {
819 return Err(VmVerificationError::SystemAirMissing {
820 air_id: CONNECTOR_AIR_ID,
821 });
822 }
823 if !merkle_air_present {
824 return Err(VmVerificationError::SystemAirMissing {
825 air_id: MERKLE_AIR_ID,
826 });
827 }
828 }
829 let exe_commit = compute_exe_commit(
830 &vm_poseidon2_hasher(),
831 program_commit.unwrap(),
832 initial_memory_root.as_ref().unwrap(),
833 start_pc.unwrap(),
834 );
835 Ok(VerifiedExecutionPayload {
836 exe_commit,
837 final_memory_root: prev_final_memory_root.unwrap(),
838 })
839}
840
841#[derive(Serialize, Deserialize)]
842#[serde(bound(
843 serialize = "Com<SC>: Serialize",
844 deserialize = "Com<SC>: Deserialize<'de>"
845))]
846pub struct ContinuationVmProof<SC: StarkGenericConfig> {
847 pub per_segment: Vec<Proof<SC>>,
848 pub user_public_values: UserPublicValuesProof<{ CHUNK }, Val<SC>>,
849}
850
851impl<SC: StarkGenericConfig> Clone for ContinuationVmProof<SC>
852where
853 Com<SC>: Clone,
854{
855 fn clone(&self) -> Self {
856 Self {
857 per_segment: self.per_segment.clone(),
858 user_public_values: self.user_public_values.clone(),
859 }
860 }
861}