@@ -8,7 +8,10 @@ use crate::{
88 errors:: {
99 cairo_run_errors:: CairoRunError , runner_errors:: RunnerError , vm_exception:: VmException ,
1010 } ,
11- runners:: { cairo_pie:: CairoPie , cairo_runner:: CairoRunner } ,
11+ runners:: {
12+ cairo_pie:: CairoPie ,
13+ cairo_runner:: { CairoRunner , RunnerMode } ,
14+ } ,
1215 security:: verify_secure_runner,
1316 trace:: trace_entry:: RelocatedTraceEntry ,
1417 } ,
@@ -67,6 +70,82 @@ impl Default for CairoRunConfig<'_> {
6770 }
6871}
6972
73+ pub struct StwoCairoRunConfig {
74+ pub trace_enabled : bool ,
75+ pub relocate_mem : bool ,
76+ pub relocate_trace : bool ,
77+ pub fill_holes : bool ,
78+ pub secure_run : bool ,
79+ pub disable_trace_padding : bool ,
80+ }
81+
82+ impl Default for StwoCairoRunConfig {
83+ fn default ( ) -> Self {
84+ StwoCairoRunConfig {
85+ trace_enabled : true ,
86+ relocate_mem : false ,
87+ relocate_trace : true ,
88+ fill_holes : false ,
89+ secure_run : true ,
90+ disable_trace_padding : true ,
91+ }
92+ }
93+ }
94+
95+ #[ allow( clippy:: result_large_err) ]
96+ pub fn cairo_run_stwo (
97+ program : & Program ,
98+ runner_mode : RunnerMode ,
99+ allowed_builtins : & [ BuiltinName ] ,
100+ hint_processor : & mut dyn HintProcessor ,
101+ exec_scopes : ExecutionScopes ,
102+ cairo_run_config : & StwoCairoRunConfig ,
103+ ) -> Result < CairoRunner , CairoRunError > {
104+ let _span = span ! ( Level :: INFO , "cairo run stwo" ) . entered ( ) ;
105+
106+ let proof_mode = runner_mode != RunnerMode :: ExecutionMode ;
107+ let mut cairo_runner = CairoRunner :: new_stwo (
108+ program,
109+ runner_mode,
110+ cairo_run_config. trace_enabled ,
111+ cairo_run_config. disable_trace_padding ,
112+ ) ?;
113+ cairo_runner. exec_scopes = exec_scopes;
114+
115+ let end = cairo_runner. initialize_stwo ( allowed_builtins) ?;
116+
117+ cairo_runner
118+ . run_until_pc ( end, hint_processor)
119+ . map_err ( |err| VmException :: from_vm_error ( & cairo_runner, err) ) ?;
120+
121+ if proof_mode {
122+ cairo_runner. run_for_steps ( 1 , hint_processor) ?;
123+ }
124+
125+ cairo_runner. end_run (
126+ cairo_run_config. disable_trace_padding ,
127+ false ,
128+ hint_processor,
129+ cairo_run_config. fill_holes ,
130+ ) ?;
131+
132+ cairo_runner. read_return_values ( false ) ?;
133+ if proof_mode {
134+ cairo_runner. finalize_segments ( ) ?;
135+ }
136+
137+ if cairo_run_config. secure_run {
138+ verify_secure_runner ( & cairo_runner, true , None ) ?;
139+ }
140+
141+ cairo_runner. relocate (
142+ cairo_run_config. relocate_mem ,
143+ cairo_run_config. relocate_trace ,
144+ ) ?;
145+
146+ Ok ( cairo_runner)
147+ }
148+
70149#[ allow( clippy:: result_large_err) ]
71150/// Runs a program with a customized execution scope.
72151pub fn cairo_run_program_with_initial_scope (
@@ -251,6 +330,86 @@ pub fn cairo_run_pie(
251330 Ok ( cairo_runner)
252331}
253332
333+ /// Runs a Cairo PIE using the Stwo runtime API.
334+ /// Same as `cairo_run_pie` but uses `new_stwo` + `initialize_stwo` instead of layouts.
335+ /// Note: Cairo PIEs cannot be run in proof mode.
336+ /// WARNING: As the RunResources are part of the HintProcessor trait, the caller should make sure that
337+ /// the number of steps in the `RunResources` matches that of the `ExecutionResources` in the `CairoPie`.
338+ /// An error will be returned if this doesn't hold.
339+ #[ allow( clippy:: result_large_err) ]
340+ pub fn cairo_run_pie_stwo (
341+ pie : & CairoPie ,
342+ allowed_builtins : & [ BuiltinName ] ,
343+ hint_processor : & mut dyn HintProcessor ,
344+ cairo_run_config : & StwoCairoRunConfig ,
345+ ) -> Result < CairoRunner , CairoRunError > {
346+ if hint_processor
347+ . get_n_steps ( )
348+ . is_none_or ( |steps| steps != pie. execution_resources . n_steps )
349+ {
350+ return Err ( RunnerError :: PieNStepsVsRunResourcesNStepsMismatch . into ( ) ) ;
351+ }
352+ pie. run_validity_checks ( ) ?;
353+
354+ let program = Program :: from_stripped_program ( & pie. metadata . program ) ;
355+ let mut cairo_runner = CairoRunner :: new_stwo (
356+ & program,
357+ RunnerMode :: ExecutionMode ,
358+ cairo_run_config. trace_enabled ,
359+ cairo_run_config. disable_trace_padding ,
360+ ) ?;
361+
362+ let end = cairo_runner. initialize_stwo ( allowed_builtins) ?;
363+ cairo_runner. vm . finalize_segments_by_cairo_pie ( pie) ;
364+ // Load builtin additional data
365+ for ( name, data) in pie. additional_data . 0 . iter ( ) {
366+ // Data is not trusted in secure_run, therefore we skip extending the hash builtin's data
367+ if matches ! ( name, BuiltinName :: pedersen) && cairo_run_config. secure_run {
368+ continue ;
369+ }
370+ if let Some ( builtin) = cairo_runner
371+ . vm
372+ . builtin_runners
373+ . iter_mut ( )
374+ . find ( |b| b. name ( ) == * name)
375+ {
376+ builtin. extend_additional_data ( data) ?;
377+ }
378+ }
379+ // Load previous execution memory
380+ let has_zero_segment = cairo_runner. vm . segments . has_zero_segment ( ) as usize ;
381+ let n_extra_segments = pie. metadata . extra_segments . len ( ) - has_zero_segment;
382+ cairo_runner
383+ . vm
384+ . segments
385+ . load_pie_memory ( & pie. memory , n_extra_segments) ?;
386+
387+ cairo_runner
388+ . run_until_pc ( end, hint_processor)
389+ . map_err ( |err| VmException :: from_vm_error ( & cairo_runner, err) ) ?;
390+
391+ cairo_runner. end_run (
392+ cairo_run_config. disable_trace_padding ,
393+ false ,
394+ hint_processor,
395+ cairo_run_config. fill_holes ,
396+ ) ?;
397+
398+ cairo_runner. read_return_values ( false ) ?;
399+
400+ if cairo_run_config. secure_run {
401+ verify_secure_runner ( & cairo_runner, true , None ) ?;
402+ // Check that the Cairo PIE produced by this run is compatible with the Cairo PIE received
403+ cairo_runner. get_cairo_pie ( ) ?. check_pie_compatibility ( pie) ?;
404+ }
405+ cairo_runner. relocate (
406+ cairo_run_config. relocate_mem ,
407+ cairo_run_config. relocate_trace ,
408+ ) ?;
409+
410+ Ok ( cairo_runner)
411+ }
412+
254413#[ cfg( feature = "test_utils" ) ]
255414#[ allow( clippy:: result_large_err) ]
256415pub fn cairo_run_fuzzed_program (
@@ -553,6 +712,184 @@ mod tests {
553712 ) ) ) ;
554713 }
555714
715+ fn make_cairo_pie ( program_content : & [ u8 ] ) -> CairoPie {
716+ let runner = cairo_run (
717+ program_content,
718+ & CairoRunConfig {
719+ layout : LayoutName :: all_cairo_stwo,
720+ ..Default :: default ( )
721+ } ,
722+ & mut BuiltinHintProcessor :: new_empty ( ) ,
723+ )
724+ . unwrap ( ) ;
725+ runner. get_cairo_pie ( ) . unwrap ( )
726+ }
727+
728+ fn stwo_allowed_builtins ( ) -> Vec < BuiltinName > {
729+ let mut allowed = vec ! [
730+ BuiltinName :: output,
731+ BuiltinName :: pedersen,
732+ BuiltinName :: range_check,
733+ BuiltinName :: bitwise,
734+ BuiltinName :: ec_op,
735+ BuiltinName :: poseidon,
736+ BuiltinName :: range_check96,
737+ ] ;
738+ if cfg ! ( feature = "mod_builtin" ) {
739+ allowed. push ( BuiltinName :: add_mod) ;
740+ allowed. push ( BuiltinName :: mul_mod) ;
741+ }
742+ allowed
743+ }
744+
745+ fn make_cairo_pie_stwo ( program_content : & [ u8 ] ) -> CairoPie {
746+ let program = Program :: from_bytes ( program_content, Some ( "main" ) ) . unwrap ( ) ;
747+ let runner = cairo_run_stwo (
748+ & program,
749+ RunnerMode :: ExecutionMode ,
750+ & stwo_allowed_builtins ( ) ,
751+ & mut BuiltinHintProcessor :: new_empty ( ) ,
752+ ExecutionScopes :: new ( ) ,
753+ & StwoCairoRunConfig {
754+ disable_trace_padding : false ,
755+ ..Default :: default ( )
756+ } ,
757+ )
758+ . unwrap ( ) ;
759+ runner. get_cairo_pie ( ) . unwrap ( )
760+ }
761+
762+ fn stwo_pie_config ( ) -> StwoCairoRunConfig {
763+ StwoCairoRunConfig {
764+ disable_trace_padding : false ,
765+ ..Default :: default ( )
766+ }
767+ }
768+
769+ #[ rstest]
770+ #[ case( include_bytes!( "../../cairo_programs/fibonacci.json" ) ) ]
771+ #[ case( include_bytes!( "../../cairo_programs/integration.json" ) ) ]
772+ #[ case( include_bytes!( "../../cairo_programs/relocate_segments.json" ) ) ]
773+ #[ case( include_bytes!( "../../cairo_programs/ec_op.json" ) ) ]
774+ #[ case( include_bytes!( "../../cairo_programs/bitwise_output.json" ) ) ]
775+ fn get_and_run_cairo_pie_stwo ( #[ case] program_content : & [ u8 ] ) {
776+ let cairo_pie = make_cairo_pie_stwo ( program_content) ;
777+ let allowed: Vec < BuiltinName > = cairo_pie
778+ . metadata
779+ . builtin_segments
780+ . keys ( )
781+ . copied ( )
782+ . collect ( ) ;
783+ let mut hint_processor = BuiltinHintProcessor :: new (
784+ Default :: default ( ) ,
785+ RunResources :: new ( cairo_pie. execution_resources . n_steps ) ,
786+ ) ;
787+ let config = stwo_pie_config ( ) ;
788+ let runner =
789+ cairo_run_pie_stwo ( & cairo_pie, & allowed, & mut hint_processor, & config) . unwrap ( ) ;
790+ assert ! ( runner. relocated_trace. is_some( ) ) ;
791+ }
792+
793+ #[ rstest]
794+ #[ case( include_bytes!( "../../cairo_programs/fibonacci.json" ) ) ]
795+ #[ case( include_bytes!( "../../cairo_programs/bitwise_output.json" ) ) ]
796+ fn cairo_run_pie_stwo_matches_legacy ( #[ case] program_content : & [ u8 ] ) {
797+ let cairo_pie = make_cairo_pie ( program_content) ;
798+ let allowed: Vec < BuiltinName > = cairo_pie
799+ . metadata
800+ . builtin_segments
801+ . keys ( )
802+ . copied ( )
803+ . collect ( ) ;
804+ let legacy_runner = cairo_run_pie (
805+ & cairo_pie,
806+ & CairoRunConfig {
807+ layout : LayoutName :: all_cairo_stwo,
808+ trace_enabled : true ,
809+ relocate_mem : true ,
810+ ..Default :: default ( )
811+ } ,
812+ & mut BuiltinHintProcessor :: new (
813+ Default :: default ( ) ,
814+ RunResources :: new ( cairo_pie. execution_resources . n_steps ) ,
815+ ) ,
816+ )
817+ . unwrap ( ) ;
818+ let stwo_config = StwoCairoRunConfig {
819+ relocate_mem : true ,
820+ ..stwo_pie_config ( )
821+ } ;
822+ let stwo_runner = cairo_run_pie_stwo (
823+ & cairo_pie,
824+ & allowed,
825+ & mut BuiltinHintProcessor :: new (
826+ Default :: default ( ) ,
827+ RunResources :: new ( cairo_pie. execution_resources . n_steps ) ,
828+ ) ,
829+ & stwo_config,
830+ )
831+ . unwrap ( ) ;
832+ assert_eq ! ( legacy_runner. relocated_memory, stwo_runner. relocated_memory) ;
833+ assert_eq ! ( legacy_runner. relocated_trace, stwo_runner. relocated_trace) ;
834+ }
835+
836+ #[ test]
837+ fn cairo_run_pie_stwo_n_steps_not_set ( ) {
838+ let cairo_pie = make_cairo_pie ( include_bytes ! ( "../../cairo_programs/fibonacci.json" ) ) ;
839+ let res = cairo_run_pie_stwo (
840+ & cairo_pie,
841+ & [ ] ,
842+ & mut BuiltinHintProcessor :: new_empty ( ) ,
843+ & stwo_pie_config ( ) ,
844+ ) ;
845+ assert ! ( res. is_err_and( |err| matches!(
846+ err,
847+ CairoRunError :: Runner ( RunnerError :: PieNStepsVsRunResourcesNStepsMismatch )
848+ ) ) ) ;
849+ }
850+
851+ #[ test]
852+ fn cairo_run_pie_stwo_n_steps_mismatch ( ) {
853+ let cairo_pie = make_cairo_pie ( include_bytes ! ( "../../cairo_programs/fibonacci.json" ) ) ;
854+ let wrong_steps = cairo_pie. execution_resources . n_steps + 1 ;
855+ let res = cairo_run_pie_stwo (
856+ & cairo_pie,
857+ & [ ] ,
858+ & mut BuiltinHintProcessor :: new ( Default :: default ( ) , RunResources :: new ( wrong_steps) ) ,
859+ & stwo_pie_config ( ) ,
860+ ) ;
861+ assert ! ( res. is_err_and( |err| matches!(
862+ err,
863+ CairoRunError :: Runner ( RunnerError :: PieNStepsVsRunResourcesNStepsMismatch )
864+ ) ) ) ;
865+ }
866+
867+ #[ test]
868+ fn cairo_run_pie_stwo_without_secure_run ( ) {
869+ let cairo_pie = make_cairo_pie ( include_bytes ! ( "../../cairo_programs/fibonacci.json" ) ) ;
870+ let allowed: Vec < BuiltinName > = cairo_pie
871+ . metadata
872+ . builtin_segments
873+ . keys ( )
874+ . copied ( )
875+ . collect ( ) ;
876+ let config = StwoCairoRunConfig {
877+ secure_run : false ,
878+ ..stwo_pie_config ( )
879+ } ;
880+ let runner = cairo_run_pie_stwo (
881+ & cairo_pie,
882+ & allowed,
883+ & mut BuiltinHintProcessor :: new (
884+ Default :: default ( ) ,
885+ RunResources :: new ( cairo_pie. execution_resources . n_steps ) ,
886+ ) ,
887+ & config,
888+ )
889+ . unwrap ( ) ;
890+ assert ! ( runner. relocated_trace. is_some( ) ) ;
891+ }
892+
556893 /// A simple slice writer for testing BinaryWrite in no_std-like conditions.
557894 struct SliceWriter < ' a > {
558895 buf : & ' a mut [ u8 ] ,
0 commit comments