1use itertools::multiunzip;
2use openvm_instructions::{exe::VmExe, program::Program};
3use openvm_stark_backend::{
4 config::{StarkGenericConfig, Val},
5 p3_field::PrimeField32,
6 verifier::VerificationError,
7 Chip,
8};
9use openvm_stark_sdk::{
10 config::{
11 baby_bear_poseidon2::{BabyBearPoseidon2Config, BabyBearPoseidon2Engine},
12 setup_tracing, FriParameters,
13 },
14 engine::{StarkEngine, StarkFriEngine, VerificationDataWithFriParams},
15 p3_baby_bear::BabyBear,
16 utils::ProofInputForTest,
17};
18
19use crate::arch::{
20 vm::{VirtualMachine, VmExecutor},
21 Streams, VmConfig, VmMemoryState,
22};
23
24pub fn air_test<VC>(config: VC, exe: impl Into<VmExe<BabyBear>>)
25where
26 VC: VmConfig<BabyBear>,
27 VC::Executor: Chip<BabyBearPoseidon2Config>,
28 VC::Periphery: Chip<BabyBearPoseidon2Config>,
29{
30 air_test_with_min_segments(config, exe, Streams::default(), 1);
31}
32
33pub fn air_test_with_min_segments<VC>(
35 config: VC,
36 exe: impl Into<VmExe<BabyBear>>,
37 input: impl Into<Streams<BabyBear>>,
38 min_segments: usize,
39) -> Option<VmMemoryState<BabyBear>>
40where
41 VC: VmConfig<BabyBear>,
42 VC::Executor: Chip<BabyBearPoseidon2Config>,
43 VC::Periphery: Chip<BabyBearPoseidon2Config>,
44{
45 air_test_impl(config, exe, input, min_segments, true)
46}
47
48pub fn air_test_impl<VC>(
51 config: VC,
52 exe: impl Into<VmExe<BabyBear>>,
53 input: impl Into<Streams<BabyBear>>,
54 min_segments: usize,
55 debug: bool,
56) -> Option<VmMemoryState<BabyBear>>
57where
58 VC: VmConfig<BabyBear>,
59 VC::Executor: Chip<BabyBearPoseidon2Config>,
60 VC::Periphery: Chip<BabyBearPoseidon2Config>,
61{
62 setup_tracing();
63 let mut log_blowup = 1;
64 while config.system().max_constraint_degree > (1 << log_blowup) + 1 {
65 log_blowup += 1;
66 }
67 let engine = BabyBearPoseidon2Engine::new(FriParameters::new_for_testing(log_blowup));
68 let vm = VirtualMachine::new(engine, config);
69 let pk = vm.keygen();
70 let mut result = vm.execute_and_generate(exe, input).unwrap();
71 let final_memory = Option::take(&mut result.final_memory);
72 let global_airs = vm.config().create_chip_complex().unwrap().airs();
73 if debug {
74 for proof_input in &result.per_segment {
75 let (airs, pks, air_proof_inputs): (Vec<_>, Vec<_>, Vec<_>) =
76 multiunzip(proof_input.per_air.iter().map(|(air_id, air_proof_input)| {
77 (
78 global_airs[*air_id].clone(),
79 pk.per_air[*air_id].clone(),
80 air_proof_input.clone(),
81 )
82 }));
83 vm.engine.debug(&airs, &pks, &air_proof_inputs);
84 }
85 }
86 let proofs = vm.prove(&pk, result);
87
88 assert!(proofs.len() >= min_segments);
89 vm.verify(&pk.get_vk(), proofs)
90 .expect("segment proofs should verify");
91 final_memory
92}
93
94pub fn gen_vm_program_test_proof_input<SC: StarkGenericConfig, VC>(
99 program: Program<Val<SC>>,
100 input_stream: impl Into<Streams<Val<SC>>> + Clone,
101 #[allow(unused_mut)] mut config: VC,
102) -> ProofInputForTest<SC>
103where
104 Val<SC>: PrimeField32,
105 VC: VmConfig<Val<SC>> + Clone,
106 VC::Executor: Chip<SC>,
107 VC::Periphery: Chip<SC>,
108{
109 cfg_if::cfg_if! {
110 if #[cfg(feature = "bench-metrics")] {
111 config.system_mut().profiling = true;
113 {
114 let executor = VmExecutor::<Val<SC>, VC>::new(config.clone());
115 executor.execute(program.clone(), input_stream.clone()).unwrap();
116 }
117 config.system_mut().profiling = false;
119 let start = std::time::Instant::now();
120 }
121 }
122
123 let airs = config.create_chip_complex().unwrap().airs();
124 let executor = VmExecutor::<Val<SC>, VC>::new(config);
125
126 let mut result = executor
127 .execute_and_generate(program, input_stream)
128 .unwrap();
129 assert_eq!(
130 result.per_segment.len(),
131 1,
132 "only proving one segment for now"
133 );
134
135 let result = result.per_segment.pop().unwrap();
136 #[cfg(feature = "bench-metrics")]
137 metrics::gauge!("execute_and_trace_gen_time_ms").set(start.elapsed().as_millis() as f64);
138 let (used_airs, per_air) = result
140 .per_air
141 .into_iter()
142 .map(|(air_id, x)| (airs[air_id].clone(), x))
143 .unzip();
144 ProofInputForTest {
145 airs: used_airs,
146 per_air,
147 }
148}
149
150type ExecuteAndProveResult<SC> = Result<VerificationDataWithFriParams<SC>, VerificationError>;
151
152pub fn execute_and_prove_program<SC: StarkGenericConfig, E: StarkFriEngine<SC>, VC>(
154 program: Program<Val<SC>>,
155 input_stream: impl Into<Streams<Val<SC>>> + Clone,
156 config: VC,
157 engine: &E,
158) -> ExecuteAndProveResult<SC>
159where
160 Val<SC>: PrimeField32,
161 VC: VmConfig<Val<SC>> + Clone,
162 VC::Executor: Chip<SC>,
163 VC::Periphery: Chip<SC>,
164{
165 let span = tracing::info_span!("execute_and_prove_program").entered();
166 let test_proof_input = gen_vm_program_test_proof_input(program, input_stream, config);
167 let vparams = test_proof_input.run_test(engine)?;
168 span.exit();
169 Ok(vparams)
170}