From cd8b2e0c3df2912dbd6aaf49559f4ee894276a5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juli=C3=A1n=20Gonz=C3=A1lez=20Calder=C3=B3n?= Date: Tue, 12 Aug 2025 14:53:28 -0300 Subject: [PATCH 1/6] Add get_simulated_builtin_base hint for testing purposes --- .../builtin_hint_processor_definition.rs | 10 +++++ .../builtin_hint_processor/mod.rs | 3 ++ .../simulated_builtins.rs | 40 +++++++++++++++++++ 3 files changed, 53 insertions(+) create mode 100644 vm/src/hint_processor/builtin_hint_processor/simulated_builtins.rs diff --git a/vm/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs b/vm/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs index ea4cfc272e..def35751ef 100644 --- a/vm/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs +++ b/vm/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs @@ -1017,6 +1017,16 @@ impl HintProcessorLogic for BuiltinHintProcessor { &hint_data.ap_tracking, constants, ), + #[cfg(feature = "test_utils")] + super::simulated_builtins::GET_SIMULATED_BUILTIN_BASE => { + super::simulated_builtins::get_simulated_builtin_base( + vm, + exec_scopes, + &hint_data.ids_data, + &hint_data.ap_tracking, + constants, + ) + } code => Err(HintError::UnknownHint(code.to_string().into_boxed_str())), } diff --git a/vm/src/hint_processor/builtin_hint_processor/mod.rs b/vm/src/hint_processor/builtin_hint_processor/mod.rs index 497c556913..1ba1a76024 100644 --- a/vm/src/hint_processor/builtin_hint_processor/mod.rs +++ b/vm/src/hint_processor/builtin_hint_processor/mod.rs @@ -30,6 +30,9 @@ pub mod sha256_utils; pub mod signature; #[cfg(feature = "test_utils")] #[cfg_attr(docsrs, doc(cfg(feature = "test_utils")))] +pub mod simulated_builtins; +#[cfg(feature = "test_utils")] +#[cfg_attr(docsrs, doc(cfg(feature = "test_utils")))] pub mod skip_next_instruction; pub mod squash_dict_utils; pub mod uint256_utils; diff --git a/vm/src/hint_processor/builtin_hint_processor/simulated_builtins.rs b/vm/src/hint_processor/builtin_hint_processor/simulated_builtins.rs new file mode 100644 index 0000000000..8208381018 --- /dev/null +++ b/vm/src/hint_processor/builtin_hint_processor/simulated_builtins.rs @@ -0,0 +1,40 @@ +use crate::hint_processor::hint_processor_definition::HintReference; +use crate::stdlib::collections::HashMap; +use crate::types::relocatable::Relocatable; +use crate::Felt252; +use crate::{ + serde::deserialize_program::ApTracking, + types::exec_scope::ExecutionScopes, + vm::{errors::hint_errors::HintError, vm_core::VirtualMachine}, +}; +use num_traits::ToPrimitive; + +use super::hint_utils::{get_integer_from_var_name, insert_value_from_var_name}; + +pub const GET_SIMULATED_BUILTIN_BASE: &str = + "ids.new_ptr = get_simulated_builtin_base(ids.builtin_idx)"; + +pub fn get_simulated_builtin_base( + vm: &mut VirtualMachine, + _exec_scopes: &mut ExecutionScopes, + ids_data: &HashMap, + ap_tracking: &ApTracking, + _constants: &HashMap, +) -> Result<(), HintError> { + let builtin_idx = get_integer_from_var_name("builtin_idx", vm, ids_data, ap_tracking)? + .to_usize() + .ok_or(HintError::BigintToUsizeFail)?; + + let builtin_runner = &vm.simulated_builtin_runners[builtin_idx]; + + insert_value_from_var_name( + "new_ptr", + Relocatable { + segment_index: builtin_runner.base() as isize, + offset: 0, + }, + vm, + ids_data, + ap_tracking, + ) +} From 7ed8e2694900e3d365c564859212d67d5d10f496 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juli=C3=A1n=20Gonz=C3=A1lez=20Calder=C3=B3n?= Date: Tue, 12 Aug 2025 14:53:39 -0300 Subject: [PATCH 2/6] Add simulated_builtins.cairo example --- cairo_programs/simulated_builtins.cairo | 37 +++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 cairo_programs/simulated_builtins.cairo diff --git a/cairo_programs/simulated_builtins.cairo b/cairo_programs/simulated_builtins.cairo new file mode 100644 index 0000000000..8e5e2423ac --- /dev/null +++ b/cairo_programs/simulated_builtins.cairo @@ -0,0 +1,37 @@ +%builtins ec_op + +from starkware.cairo.common.cairo_builtins import EcOpBuiltin +from starkware.cairo.common.ec_point import EcPoint +from starkware.cairo.common.ec import ec_op + +func main{ec_op_ptr: EcOpBuiltin*}() { + alloc_locals; + let (local ec_op_ptr) = init_ec_op(ec_op_ptr=ec_op_ptr); + + let p = EcPoint( + 0x6a4beaef5a93425b973179cdba0c9d42f30e01a5f1e2db73da0884b8d6756fc, + 0x72565ec81bc09ff53fbfad99324a92aa5b39fb58267e395e8abe36290ebf24f, + ); + let m = 34; + let q = EcPoint( + 0x654fd7e67a123dd13868093b3b7777f1ffef596c2e324f25ceaf9146698482c, + 0x4fad269cbf860980e38768fe9cb6b0b9ab03ee3fe84cfde2eccce597c874fd8, + ); + let (r) = ec_op(p, m, q); + assert r.x = 108925483682366235368969256555281508851459278989259552980345066351008608800; + assert r.y = 1592365885972480102953613056006596671718206128324372995731808913669237079419; + return (); +} + +func init_ec_op(ec_op_ptr: EcOpBuiltin*) -> (ec_op_ptr: EcOpBuiltin*) { + if (ec_op_ptr != 0) { + return (ec_op_ptr=ec_op_ptr); + } + + alloc_locals; + local builtin_idx = 0; + local new_ptr; + %{ ids.new_ptr = get_simulated_builtin_base(ids.builtin_idx) %} + + return (ec_op_ptr=cast(new_ptr, EcOpBuiltin*)); +} From 3a937af5d45efb22f71743866b90d66c85d239a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juli=C3=A1n=20Gonz=C3=A1lez=20Calder=C3=B3n?= Date: Tue, 12 Aug 2025 14:53:55 -0300 Subject: [PATCH 3/6] Document simulated builtins field --- vm/src/vm/vm_core.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/vm/src/vm/vm_core.rs b/vm/src/vm/vm_core.rs index fcd8f9dac9..335943b74d 100644 --- a/vm/src/vm/vm_core.rs +++ b/vm/src/vm/vm_core.rs @@ -89,6 +89,16 @@ impl DeducedOperands { pub struct VirtualMachine { pub(crate) run_context: RunContext, pub builtin_runners: Vec, + /// A simulated builtin is being verified in the executed Cairo + /// code (so proving them only involves proving cairo steps), rather + /// than being outputted as their own segment and proven later using + /// dedicated AIRs. + /// + /// These builtins won't be included in the layout, hence the builtin + /// segment pointer won't be passed as argument to the program. The program + /// needs a mechanism for obtaining the segment pointer. See example + /// implementation of this mechanism in `simulated_builtins.cairo`, or + /// cairo-lang's `simple_bootloader.cairo`. pub simulated_builtin_runners: Vec, pub segments: MemorySegmentManager, pub(crate) trace: Option>, From c6c5e3e052800d474de04887ba1ee64ca996bc33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juli=C3=A1n=20Gonz=C3=A1lez=20Calder=C3=B3n?= Date: Tue, 12 Aug 2025 14:54:05 -0300 Subject: [PATCH 4/6] Add a test for simulated builtins --- vm/src/vm/runners/cairo_runner.rs | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/vm/src/vm/runners/cairo_runner.rs b/vm/src/vm/runners/cairo_runner.rs index b9ee5701a4..f70856373f 100644 --- a/vm/src/vm/runners/cairo_runner.rs +++ b/vm/src/vm/runners/cairo_runner.rs @@ -5726,4 +5726,33 @@ mod tests { let builtin_segments = cairo_runner.get_builtin_segments(); assert!(builtin_segments.get(&9) == Some(&BuiltinName::poseidon)); } + + #[test] + #[cfg(feature = "test_utils")] + fn test_simulated_builtins() { + let program_bytes = include_bytes!("../../../../cairo_programs/simulated_builtins.json"); + let program = + Program::from_bytes(program_bytes, Some("main")).expect("failed to read program"); + + let program: &Program = &program; + let mut cairo_runner = + CairoRunner::new(program, LayoutName::plain, None, false, false, false) + .expect("failed to create runner"); + + let end = cairo_runner + .initialize(true) + .expect("failed to initialize builtins"); + + let mut builtin_runner = BuiltinRunner::EcOp(EcOpBuiltinRunner::new(None, true)); + builtin_runner.initialize_segments(&mut cairo_runner.vm.segments); + cairo_runner + .vm + .simulated_builtin_runners + .push(builtin_runner); + + let hint_processor: &mut dyn HintProcessor = &mut BuiltinHintProcessor::new_empty(); + cairo_runner + .run_until_pc(end, hint_processor) + .expect("failed to run program"); + } } From 184b798f502b0e4dac263dc96546443ff6282bc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juli=C3=A1n=20Gonz=C3=A1lez=20Calder=C3=B3n?= Date: Tue, 12 Aug 2025 16:28:25 -0300 Subject: [PATCH 5/6] Add documentation --- cairo_programs/simulated_builtins.cairo | 10 ++++++++++ .../builtin_hint_processor/simulated_builtins.rs | 8 +++++++- vm/src/vm/runners/cairo_runner.rs | 2 ++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/cairo_programs/simulated_builtins.cairo b/cairo_programs/simulated_builtins.cairo index 8e5e2423ac..8627025af0 100644 --- a/cairo_programs/simulated_builtins.cairo +++ b/cairo_programs/simulated_builtins.cairo @@ -23,6 +23,13 @@ func main{ec_op_ptr: EcOpBuiltin*}() { return (); } +// Initializes the ec_op builtin pointer if not initialized. +// +// If the builtin is included in the layout, the ec_op_ptr will be valid, and +// this function will do nothing. If the builtin is not included in the layout, +// then it will obtain the pointer of the ec_op simulated builtina and return +// it. For this to work properly, the runner must have the ec_op simualted +// builtin runner at index 0. func init_ec_op(ec_op_ptr: EcOpBuiltin*) -> (ec_op_ptr: EcOpBuiltin*) { if (ec_op_ptr != 0) { return (ec_op_ptr=ec_op_ptr); @@ -31,6 +38,9 @@ func init_ec_op(ec_op_ptr: EcOpBuiltin*) -> (ec_op_ptr: EcOpBuiltin*) { alloc_locals; local builtin_idx = 0; local new_ptr; + + // This hint is not defined used in the original VM, + // and its declared for testing purposes only. %{ ids.new_ptr = get_simulated_builtin_base(ids.builtin_idx) %} return (ec_op_ptr=cast(new_ptr, EcOpBuiltin*)); diff --git a/vm/src/hint_processor/builtin_hint_processor/simulated_builtins.rs b/vm/src/hint_processor/builtin_hint_processor/simulated_builtins.rs index 8208381018..b4cc5259ee 100644 --- a/vm/src/hint_processor/builtin_hint_processor/simulated_builtins.rs +++ b/vm/src/hint_processor/builtin_hint_processor/simulated_builtins.rs @@ -14,6 +14,11 @@ use super::hint_utils::{get_integer_from_var_name, insert_value_from_var_name}; pub const GET_SIMULATED_BUILTIN_BASE: &str = "ids.new_ptr = get_simulated_builtin_base(ids.builtin_idx)"; +/// Obtains the simulated builtin runner base, at the given index. The simulated +/// builtin runner must be initialized before the execution. +/// +/// This hint is not defined in the original VM, and its declared for testing +/// purposes only. pub fn get_simulated_builtin_base( vm: &mut VirtualMachine, _exec_scopes: &mut ExecutionScopes, @@ -21,12 +26,13 @@ pub fn get_simulated_builtin_base( ap_tracking: &ApTracking, _constants: &HashMap, ) -> Result<(), HintError> { + // Obtain the simulated builtin runner from the builtin_idx variable. let builtin_idx = get_integer_from_var_name("builtin_idx", vm, ids_data, ap_tracking)? .to_usize() .ok_or(HintError::BigintToUsizeFail)?; - let builtin_runner = &vm.simulated_builtin_runners[builtin_idx]; + // Set new_ptr with the value of the builtin runner base. insert_value_from_var_name( "new_ptr", Relocatable { diff --git a/vm/src/vm/runners/cairo_runner.rs b/vm/src/vm/runners/cairo_runner.rs index f70856373f..a540d59c8b 100644 --- a/vm/src/vm/runners/cairo_runner.rs +++ b/vm/src/vm/runners/cairo_runner.rs @@ -5739,10 +5739,12 @@ mod tests { CairoRunner::new(program, LayoutName::plain, None, false, false, false) .expect("failed to create runner"); + // We allow missing builtins, as we will simulate them later. let end = cairo_runner .initialize(true) .expect("failed to initialize builtins"); + // Initialize the ec_op simulated builtin runner. let mut builtin_runner = BuiltinRunner::EcOp(EcOpBuiltinRunner::new(None, true)); builtin_runner.initialize_segments(&mut cairo_runner.vm.segments); cairo_runner From c23fdb0169bbd6e3f204aedf15adf212670760bf Mon Sep 17 00:00:00 2001 From: Julian Gonzalez Calderon Date: Thu, 14 Aug 2025 15:41:06 -0300 Subject: [PATCH 6/6] Fix typos Co-authored-by: DiegoC <90105443+DiegoCivi@users.noreply.github.com> --- cairo_programs/simulated_builtins.cairo | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cairo_programs/simulated_builtins.cairo b/cairo_programs/simulated_builtins.cairo index 8627025af0..82e7ffa901 100644 --- a/cairo_programs/simulated_builtins.cairo +++ b/cairo_programs/simulated_builtins.cairo @@ -27,8 +27,8 @@ func main{ec_op_ptr: EcOpBuiltin*}() { // // If the builtin is included in the layout, the ec_op_ptr will be valid, and // this function will do nothing. If the builtin is not included in the layout, -// then it will obtain the pointer of the ec_op simulated builtina and return -// it. For this to work properly, the runner must have the ec_op simualted +// then it will obtain the pointer of the ec_op simulated builtin and return +// it. For this to work properly, the runner must have the ec_op simulated // builtin runner at index 0. func init_ec_op(ec_op_ptr: EcOpBuiltin*) -> (ec_op_ptr: EcOpBuiltin*) { if (ec_op_ptr != 0) { @@ -39,7 +39,7 @@ func init_ec_op(ec_op_ptr: EcOpBuiltin*) -> (ec_op_ptr: EcOpBuiltin*) { local builtin_idx = 0; local new_ptr; - // This hint is not defined used in the original VM, + // This hint is not defined in the original VM, // and its declared for testing purposes only. %{ ids.new_ptr = get_simulated_builtin_base(ids.builtin_idx) %}