1use crate::{
2 hint_processor::hint_processor_definition::HintProcessor,
3 types::{
4 builtin_name::BuiltinName, layout::CairoLayoutParams, layout_name::LayoutName,
5 program::Program,
6 },
7 vm::{
8 errors::{
9 cairo_run_errors::CairoRunError, runner_errors::RunnerError, vm_exception::VmException,
10 },
11 runners::{cairo_pie::CairoPie, cairo_runner::CairoRunner},
12 security::verify_secure_runner,
13 },
14};
15
16use crate::Felt252;
17use bincode::enc::write::Writer;
18
19use thiserror_no_std::Error;
20
21use crate::types::exec_scope::ExecutionScopes;
22#[cfg(feature = "test_utils")]
23use arbitrary::{self, Arbitrary};
24
25#[cfg_attr(feature = "test_utils", derive(Arbitrary))]
26pub struct CairoRunConfig<'a> {
27 #[cfg_attr(feature = "test_utils", arbitrary(value = "main"))]
28 pub entrypoint: &'a str,
29 pub trace_enabled: bool,
30 pub relocate_mem: bool,
31 pub layout: LayoutName,
32 pub dynamic_layout_params: Option<CairoLayoutParams>,
35 pub proof_mode: bool,
36 pub secure_run: Option<bool>,
37 pub disable_trace_padding: bool,
45 pub allow_missing_builtins: Option<bool>,
46}
47
48impl<'a> Default for CairoRunConfig<'a> {
49 fn default() -> Self {
50 CairoRunConfig {
51 entrypoint: "main",
52 trace_enabled: false,
53 relocate_mem: false,
54 layout: LayoutName::plain,
55 proof_mode: false,
56 secure_run: None,
57 disable_trace_padding: false,
58 allow_missing_builtins: None,
59 dynamic_layout_params: None,
60 }
61 }
62}
63
64pub fn cairo_run_program_with_initial_scope(
66 program: &Program,
67 cairo_run_config: &CairoRunConfig,
68 hint_processor: &mut dyn HintProcessor,
69 exec_scopes: ExecutionScopes,
70) -> Result<CairoRunner, CairoRunError> {
71 let secure_run = cairo_run_config
72 .secure_run
73 .unwrap_or(!cairo_run_config.proof_mode);
74
75 let allow_missing_builtins = cairo_run_config
76 .allow_missing_builtins
77 .unwrap_or(cairo_run_config.proof_mode);
78
79 let mut cairo_runner = CairoRunner::new(
80 program,
81 cairo_run_config.layout,
82 cairo_run_config.dynamic_layout_params.clone(),
83 cairo_run_config.proof_mode,
84 cairo_run_config.trace_enabled,
85 cairo_run_config.disable_trace_padding,
86 )?;
87
88 cairo_runner.exec_scopes = exec_scopes;
89
90 let end = cairo_runner.initialize(allow_missing_builtins)?;
91 cairo_runner
94 .run_until_pc(end, hint_processor)
95 .map_err(|err| VmException::from_vm_error(&cairo_runner, err))?;
96
97 if cairo_run_config.proof_mode {
98 cairo_runner.run_for_steps(1, hint_processor)?;
99 }
100 cairo_runner.end_run(
101 cairo_run_config.disable_trace_padding,
102 false,
103 hint_processor,
104 )?;
105
106 cairo_runner.vm.verify_auto_deductions()?;
107 cairo_runner.read_return_values(allow_missing_builtins)?;
108 if cairo_run_config.proof_mode {
109 cairo_runner.finalize_segments()?;
110 }
111 if secure_run {
112 verify_secure_runner(&cairo_runner, true, None)?;
113 }
114 cairo_runner.relocate(cairo_run_config.relocate_mem)?;
115
116 Ok(cairo_runner)
117}
118
119pub fn cairo_run_program(
120 program: &Program,
121 cairo_run_config: &CairoRunConfig,
122 hint_processor: &mut dyn HintProcessor,
123) -> Result<CairoRunner, CairoRunError> {
124 cairo_run_program_with_initial_scope(
125 program,
126 cairo_run_config,
127 hint_processor,
128 ExecutionScopes::new(),
129 )
130}
131
132pub fn cairo_run(
133 program_content: &[u8],
134 cairo_run_config: &CairoRunConfig,
135 hint_processor: &mut dyn HintProcessor,
136) -> Result<CairoRunner, CairoRunError> {
137 let program = Program::from_bytes(program_content, Some(cairo_run_config.entrypoint))?;
138
139 cairo_run_program(&program, cairo_run_config, hint_processor)
140}
141pub fn cairo_run_pie(
148 pie: &CairoPie,
149 cairo_run_config: &CairoRunConfig,
150 hint_processor: &mut dyn HintProcessor,
151) -> Result<CairoRunner, CairoRunError> {
152 if cairo_run_config.proof_mode {
153 return Err(RunnerError::CairoPieProofMode.into());
154 }
155 if !hint_processor
156 .get_n_steps()
157 .is_some_and(|steps| steps == pie.execution_resources.n_steps)
158 {
159 return Err(RunnerError::PieNStepsVsRunResourcesNStepsMismatch.into());
160 }
161 pie.run_validity_checks()?;
162 let secure_run = cairo_run_config.secure_run.unwrap_or(true);
163
164 let allow_missing_builtins = cairo_run_config.allow_missing_builtins.unwrap_or_default();
165
166 let program = Program::from_stripped_program(&pie.metadata.program);
167 let mut cairo_runner = CairoRunner::new(
168 &program,
169 cairo_run_config.layout,
170 cairo_run_config.dynamic_layout_params.clone(),
171 false,
172 cairo_run_config.trace_enabled,
173 cairo_run_config.disable_trace_padding,
174 )?;
175
176 let end = cairo_runner.initialize(allow_missing_builtins)?;
177 cairo_runner.vm.finalize_segments_by_cairo_pie(pie);
178 for (name, data) in pie.additional_data.0.iter() {
180 if matches!(name, BuiltinName::pedersen) && secure_run {
182 continue;
183 }
184 if let Some(builtin) = cairo_runner
185 .vm
186 .builtin_runners
187 .iter_mut()
188 .find(|b| b.name() == *name)
189 {
190 builtin.extend_additional_data(data)?;
191 }
192 }
193 let has_zero_segment = cairo_runner.vm.segments.has_zero_segment() as usize;
195 let n_extra_segments = pie.metadata.extra_segments.len() - has_zero_segment;
196 cairo_runner
197 .vm
198 .segments
199 .load_pie_memory(&pie.memory, n_extra_segments)?;
200
201 cairo_runner
202 .run_until_pc(end, hint_processor)
203 .map_err(|err| VmException::from_vm_error(&cairo_runner, err))?;
204
205 cairo_runner.end_run(
206 cairo_run_config.disable_trace_padding,
207 false,
208 hint_processor,
209 )?;
210
211 cairo_runner.vm.verify_auto_deductions()?;
212 cairo_runner.read_return_values(allow_missing_builtins)?;
213
214 if secure_run {
215 verify_secure_runner(&cairo_runner, true, None)?;
216 cairo_runner.get_cairo_pie()?.check_pie_compatibility(pie)?;
218 }
219 cairo_runner.relocate(cairo_run_config.relocate_mem)?;
220
221 Ok(cairo_runner)
222}
223
224#[cfg(feature = "test_utils")]
225pub fn cairo_run_fuzzed_program(
226 program: Program,
227 cairo_run_config: &CairoRunConfig,
228 hint_processor: &mut dyn HintProcessor,
229 steps_limit: usize,
230) -> Result<CairoRunner, CairoRunError> {
231 use crate::vm::errors::vm_errors::VirtualMachineError;
232
233 let secure_run = cairo_run_config
234 .secure_run
235 .unwrap_or(!cairo_run_config.proof_mode);
236
237 let allow_missing_builtins = cairo_run_config
238 .allow_missing_builtins
239 .unwrap_or(cairo_run_config.proof_mode);
240
241 let mut cairo_runner = CairoRunner::new(
242 &program,
243 cairo_run_config.layout,
244 cairo_run_config.dynamic_layout_params.clone(),
245 cairo_run_config.proof_mode,
246 cairo_run_config.trace_enabled,
247 cairo_run_config.disable_trace_padding,
248 )?;
249
250 let _end = cairo_runner.initialize(allow_missing_builtins)?;
251
252 let res = match cairo_runner.run_until_steps(steps_limit, hint_processor) {
253 Err(VirtualMachineError::EndOfProgram(_remaining)) => Ok(()), res => res,
255 };
256
257 res.map_err(|err| VmException::from_vm_error(&cairo_runner, err))?;
258
259 cairo_runner.end_run(false, false, hint_processor)?;
260
261 cairo_runner.vm.verify_auto_deductions()?;
262 cairo_runner.read_return_values(allow_missing_builtins)?;
263 if cairo_run_config.proof_mode {
264 cairo_runner.finalize_segments()?;
265 }
266 if secure_run {
267 verify_secure_runner(&cairo_runner, true, None)?;
268 }
269 cairo_runner.relocate(cairo_run_config.relocate_mem)?;
270
271 Ok(cairo_runner)
272}
273
274#[derive(Debug, Error)]
275#[error("Failed to encode trace at position {0}, serialize error: {1}")]
276pub struct EncodeTraceError(usize, bincode::error::EncodeError);
277
278pub fn write_encoded_trace(
283 relocated_trace: &[crate::vm::trace::trace_entry::RelocatedTraceEntry],
284 dest: &mut impl Writer,
285) -> Result<(), EncodeTraceError> {
286 for (i, entry) in relocated_trace.iter().enumerate() {
287 dest.write(&((entry.ap as u64).to_le_bytes()))
288 .map_err(|e| EncodeTraceError(i, e))?;
289 dest.write(&((entry.fp as u64).to_le_bytes()))
290 .map_err(|e| EncodeTraceError(i, e))?;
291 dest.write(&((entry.pc as u64).to_le_bytes()))
292 .map_err(|e| EncodeTraceError(i, e))?;
293 }
294
295 Ok(())
296}
297
298pub fn write_encoded_memory(
304 relocated_memory: &[Option<Felt252>],
305 dest: &mut impl Writer,
306) -> Result<(), EncodeTraceError> {
307 for (i, memory_cell) in relocated_memory.iter().enumerate() {
308 match memory_cell {
309 None => continue,
310 Some(unwrapped_memory_cell) => {
311 dest.write(&(i as u64).to_le_bytes())
312 .map_err(|e| EncodeTraceError(i, e))?;
313 dest.write(&unwrapped_memory_cell.to_bytes_le())
314 .map_err(|e| EncodeTraceError(i, e))?;
315 }
316 }
317 }
318
319 Ok(())
320}
321
322#[cfg(test)]
323mod tests {
324 use super::*;
325 use crate::stdlib::prelude::*;
326 use crate::vm::runners::cairo_runner::RunResources;
327 use crate::Felt252;
328 use crate::{
329 hint_processor::{
330 builtin_hint_processor::builtin_hint_processor_definition::BuiltinHintProcessor,
331 hint_processor_definition::HintProcessor,
332 },
333 utils::test_utils::*,
334 };
335 use bincode::enc::write::SliceWriter;
336
337 use rstest::rstest;
338 #[cfg(target_arch = "wasm32")]
339 use wasm_bindgen_test::*;
340
341 fn run_test_program(
342 program_content: &[u8],
343 hint_processor: &mut dyn HintProcessor,
344 ) -> Result<CairoRunner, CairoRunError> {
345 let program = Program::from_bytes(program_content, Some("main")).unwrap();
346 let mut cairo_runner = cairo_runner!(program, LayoutName::all_cairo, false, true);
347 let end = cairo_runner
348 .initialize(false)
349 .map_err(CairoRunError::Runner)?;
350
351 assert!(cairo_runner.run_until_pc(end, hint_processor).is_ok());
352
353 Ok(cairo_runner)
354 }
355
356 #[test]
357 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
358 fn cairo_run_custom_entry_point() {
359 let program = Program::from_bytes(
360 include_bytes!("../../cairo_programs/not_main.json"),
361 Some("not_main"),
362 )
363 .unwrap();
364 let mut hint_processor = BuiltinHintProcessor::new_empty();
365 let mut cairo_runner = cairo_runner!(program);
366
367 let end = cairo_runner.initialize(false).unwrap();
368 assert!(cairo_runner.run_until_pc(end, &mut hint_processor).is_ok());
369 assert!(cairo_runner.relocate(true).is_ok());
370 assert_eq!(cairo_runner.relocated_memory[2], Some(Felt252::from(123)));
373 }
374
375 #[test]
376 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
377 fn cairo_run_with_no_data_program() {
378 let mut hint_processor = BuiltinHintProcessor::new_empty();
381 let no_data_program_path =
382 include_bytes!("../../cairo_programs/manually_compiled/no_data_program.json");
383 let cairo_run_config = CairoRunConfig::default();
384 assert!(cairo_run(no_data_program_path, &cairo_run_config, &mut hint_processor,).is_err());
385 }
386
387 #[test]
388 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
389 fn cairo_run_with_no_main_program() {
390 let mut hint_processor = BuiltinHintProcessor::new_empty();
393 let no_main_program =
394 include_bytes!("../../cairo_programs/manually_compiled/no_main_program.json");
395 let cairo_run_config = CairoRunConfig::default();
396 assert!(cairo_run(no_main_program, &cairo_run_config, &mut hint_processor,).is_err());
397 }
398
399 #[test]
400 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
401 fn cairo_run_with_invalid_memory() {
402 let mut hint_processor = BuiltinHintProcessor::new_empty();
405 let invalid_memory =
406 include_bytes!("../../cairo_programs/manually_compiled/invalid_memory.json");
407 let cairo_run_config = CairoRunConfig::default();
408 assert!(cairo_run(invalid_memory, &cairo_run_config, &mut hint_processor,).is_err());
409 }
410
411 #[test]
412 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
413 fn write_output_program() {
414 let program_content = include_bytes!("../../cairo_programs/bitwise_output.json");
415 let mut hint_processor = BuiltinHintProcessor::new_empty();
416 let mut runner = run_test_program(program_content, &mut hint_processor)
417 .expect("Couldn't initialize cairo runner");
418
419 let mut output_buffer = String::new();
420 runner.vm.write_output(&mut output_buffer).unwrap();
421 assert_eq!(&output_buffer, "0\n");
422 }
423
424 #[test]
425 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
426 fn write_binary_trace_file() {
427 let program_content = include_bytes!("../../cairo_programs/struct.json");
428 let expected_encoded_trace =
429 include_bytes!("../../cairo_programs/trace_memory/cairo_trace_struct");
430
431 let mut hint_processor = BuiltinHintProcessor::new_empty();
433 let mut cairo_runner = run_test_program(program_content, &mut hint_processor).unwrap();
434
435 assert!(cairo_runner.relocate(false).is_ok());
436
437 let trace_entries = cairo_runner.relocated_trace.unwrap();
438 let mut buffer = [0; 24];
439 let mut buff_writer = SliceWriter::new(&mut buffer);
440 write_encoded_trace(&trace_entries, &mut buff_writer).unwrap();
442
443 assert_eq!(buffer, *expected_encoded_trace);
445 }
446
447 #[test]
448 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
449 fn write_binary_memory_file() {
450 let program_content = include_bytes!("../../cairo_programs/struct.json");
451 let expected_encoded_memory =
452 include_bytes!("../../cairo_programs/trace_memory/cairo_memory_struct");
453
454 let mut hint_processor = BuiltinHintProcessor::new_empty();
456 let mut cairo_runner = run_test_program(program_content, &mut hint_processor).unwrap();
457
458 assert!(cairo_runner.relocate(true).is_ok());
460
461 let mut buffer = [0; 120];
462 let mut buff_writer = SliceWriter::new(&mut buffer);
463 write_encoded_memory(&cairo_runner.relocated_memory, &mut buff_writer).unwrap();
465
466 assert_eq!(*expected_encoded_memory, buffer);
468 }
469
470 #[test]
471 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
472 fn run_with_no_trace() {
473 let program = Program::from_bytes(
474 include_bytes!("../../cairo_programs/struct.json"),
475 Some("main"),
476 )
477 .unwrap();
478
479 let mut hint_processor = BuiltinHintProcessor::new_empty();
480 let mut cairo_runner = cairo_runner!(program);
481 let end = cairo_runner.initialize(false).unwrap();
482 assert!(cairo_runner.run_until_pc(end, &mut hint_processor).is_ok());
483 assert!(cairo_runner.relocate(false).is_ok());
484 assert!(cairo_runner.relocated_trace.is_none());
485 }
486
487 #[rstest]
488 #[case(include_bytes!("../../cairo_programs/fibonacci.json"))]
489 #[case(include_bytes!("../../cairo_programs/integration.json"))]
490 #[case(include_bytes!("../../cairo_programs/common_signature.json"))]
491 #[case(include_bytes!("../../cairo_programs/relocate_segments.json"))]
492 #[case(include_bytes!("../../cairo_programs/ec_op.json"))]
493 #[case(include_bytes!("../../cairo_programs/bitwise_output.json"))]
494 #[case(include_bytes!("../../cairo_programs/value_beyond_segment.json"))]
495 fn get_and_run_cairo_pie(#[case] program_content: &[u8]) {
496 let cairo_run_config = CairoRunConfig {
497 layout: LayoutName::starknet_with_keccak,
498 ..Default::default()
499 };
500 let cairo_pie = {
502 let runner = cairo_run(
503 program_content,
504 &cairo_run_config,
505 &mut BuiltinHintProcessor::new_empty(),
506 )
507 .unwrap();
508 runner.get_cairo_pie().unwrap()
509 };
510 let mut hint_processor = BuiltinHintProcessor::new(
511 Default::default(),
512 RunResources::new(cairo_pie.execution_resources.n_steps),
513 );
514 assert!(cairo_run_pie(&cairo_pie, &cairo_run_config, &mut hint_processor).is_ok());
516 }
517
518 #[test]
519 fn cairo_run_pie_n_steps_not_set() {
520 let cairo_pie = {
522 let runner = cairo_run(
523 include_bytes!("../../cairo_programs/fibonacci.json"),
524 &CairoRunConfig::default(),
525 &mut BuiltinHintProcessor::new_empty(),
526 )
527 .unwrap();
528 runner.get_cairo_pie().unwrap()
529 };
530 let res = cairo_run_pie(
532 &cairo_pie,
533 &CairoRunConfig::default(),
534 &mut BuiltinHintProcessor::new_empty(),
535 );
536 assert!(res.is_err_and(|err| matches!(
537 err,
538 CairoRunError::Runner(RunnerError::PieNStepsVsRunResourcesNStepsMismatch)
539 )));
540 }
541}