Skip to content

Commit fa018bf

Browse files
Add_support_for_pies_with_simulated_builtins
1 parent 9c44e1c commit fa018bf

File tree

6 files changed

+160
-17
lines changed

6 files changed

+160
-17
lines changed

vm/src/cairo_run.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,8 @@ pub fn cairo_run_pie(
177177

178178
let end = cairo_runner.initialize(allow_missing_builtins)?;
179179
cairo_runner.vm.finalize_segments_by_cairo_pie(pie);
180+
// Load simulated builtin runners according to the pie's metadata.
181+
cairo_runner.load_simulated_builtin_runners(&pie.metadata.simulated_builtins)?;
180182
// Load builtin additional data
181183
for (name, data) in pie.additional_data.0.iter() {
182184
// Data is not trusted in secure_run, therefore we skip extending the hash builtin's data

vm/src/tests/cairo_pie_test.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@ use crate::{
33
types::{builtin_name::BuiltinName, layout_name::LayoutName},
44
};
55

6+
#[cfg(feature = "mod_builtin")]
7+
use crate::{
8+
cairo_run,
9+
vm::runners::{cairo_pie::CairoPie, cairo_runner::RunResources},
10+
};
11+
612
#[cfg(target_arch = "wasm32")]
713
use wasm_bindgen_test::*;
814

@@ -282,3 +288,28 @@ fn run_pie_validity_checks_integration() {
282288
let cairo_pie = runner.get_cairo_pie().expect("Failed to get pie");
283289
assert!(cairo_pie.run_validity_checks().is_ok())
284290
}
291+
292+
#[test]
293+
#[cfg(feature = "std")]
294+
#[cfg(feature = "mod_builtin")]
295+
fn get_cairo_pie_with_simulated_builtins() {
296+
// Load a Cairo PIE with simulated builtins (this PIE is of a run of the simple_bootloader
297+
// program, with a subtask that uses all available builtins, simulating the ones not present in
298+
// the `all_cairo_stwo` layout).
299+
let pie =
300+
CairoPie::from_bytes(include_bytes!("cairo_pie_with_simulated_builtins.zip")).unwrap();
301+
let mut hint_processor = BuiltinHintProcessor::new(
302+
Default::default(),
303+
RunResources::new(pie.execution_resources.n_steps),
304+
);
305+
let cairo_run_config = CairoRunConfig {
306+
layout: LayoutName::all_cairo_stwo,
307+
secure_run: Some(true),
308+
allow_missing_builtins: Some(true),
309+
..Default::default()
310+
};
311+
let result = cairo_run::cairo_run_pie(&pie, &cairo_run_config, &mut hint_processor);
312+
let runner = result.unwrap();
313+
let result = runner.get_cairo_pie();
314+
assert!(result.is_ok());
315+
}
863 KB
Binary file not shown.

vm/src/vm/errors/cairo_run_errors.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use thiserror::Error;
33
use super::cairo_pie_errors::CairoPieValidationError;
44
use super::memory_errors::MemoryError;
55
use super::vm_exception::VmException;
6+
use crate::types::builtin_name::BuiltinName;
67
use crate::types::errors::program_errors::ProgramError;
78
use crate::vm::errors::{
89
runner_errors::RunnerError, trace_errors::TraceError, vm_errors::VirtualMachineError,
@@ -26,4 +27,6 @@ pub enum CairoRunError {
2627
VmException(#[from] VmException),
2728
#[error(transparent)]
2829
CairoPieValidation(#[from] CairoPieValidationError),
30+
#[error("Unsupported simulated builtin in Cairo PIE: {0:?}")]
31+
UnsupportedSimulatedBuiltin(BuiltinName),
2932
}

vm/src/vm/runners/cairo_pie.rs

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,11 @@ pub struct CairoPieMetadata {
147147
#[serde(serialize_with = "serde_impl::serialize_builtin_segments")]
148148
pub builtin_segments: HashMap<BuiltinName, SegmentInfo>,
149149
pub extra_segments: Vec<SegmentInfo>,
150+
// `simulated_builtins` are builtins that are being verified in the executed cairo code
151+
// rather than being outputted as their own segment and proven later (see example implementation
152+
// of this mechanism in `simple_bootloader.cairo`).
153+
#[serde(default, skip_serializing_if = "Vec::is_empty")]
154+
pub simulated_builtins: Vec<BuiltinName>,
150155
}
151156

152157
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
@@ -175,12 +180,11 @@ impl CairoPieMetadata {
175180
if self.program.data.len() != self.program_segment.size {
176181
return Err(CairoPieValidationError::ProgramLenVsSegmentSizeMismatch);
177182
}
178-
if self.builtin_segments.len() != self.program.builtins.len()
179-
|| !self
180-
.program
181-
.builtins
182-
.iter()
183-
.all(|b| self.builtin_segments.contains_key(b))
183+
if self.builtin_segments.len() + self.simulated_builtins.len()
184+
!= self.program.builtins.len()
185+
|| !self.program.builtins.iter().all(|b| {
186+
self.builtin_segments.contains_key(b) || self.simulated_builtins.contains(b)
187+
})
184188
{
185189
return Err(CairoPieValidationError::BuiltinListVsSegmentsMismatch);
186190
}
@@ -200,23 +204,32 @@ impl CairoPieMetadata {
200204
if !self.execution_segment.index.is_one() {
201205
return Err(CairoPieValidationError::InvalidExecutionSegmentIndex);
202206
}
203-
for (i, builtin_name) in self.program.builtins.iter().enumerate() {
204-
// We can safely index as run_validity_checks already ensures that the keys match
205-
if self.builtin_segments[builtin_name].index != 2 + i as isize {
207+
let mut non_simulated_index = 0;
208+
for builtin_name in self.program.builtins.iter() {
209+
// We can safely index as run_validity_checks already ensures that the keys match,
210+
// to either a builtin_segments key or a simulated_builtins key.
211+
// If the builtin is simulated, skip processing without incrementing non_simulated_index.
212+
if self.simulated_builtins.contains(builtin_name) {
213+
continue;
214+
}
215+
216+
if self.builtin_segments[builtin_name].index != 2 + non_simulated_index as isize {
206217
return Err(CairoPieValidationError::InvalidBuiltinSegmentIndex(
207218
*builtin_name,
208219
));
209220
}
221+
non_simulated_index += 1;
210222
}
211-
let n_builtins = self.program.builtins.len() as isize;
212-
if self.ret_fp_segment.index != n_builtins + 2 {
223+
let n_builtin_segments =
224+
(self.program.builtins.len() - self.simulated_builtins.len()) as isize;
225+
if self.ret_fp_segment.index != n_builtin_segments + 2 {
213226
return Err(CairoPieValidationError::InvalidRetFpSegmentIndex);
214227
}
215-
if self.ret_pc_segment.index != n_builtins + 3 {
228+
if self.ret_pc_segment.index != n_builtin_segments + 3 {
216229
return Err(CairoPieValidationError::InvalidRetPcSegmentIndex);
217230
}
218231
for (i, segment) in self.extra_segments.iter().enumerate() {
219-
if segment.index != 4 + n_builtins + i as isize {
232+
if segment.index != 4 + n_builtin_segments + i as isize {
220233
return Err(CairoPieValidationError::InvalidExtraSegmentIndex);
221234
}
222235
}
@@ -230,11 +243,13 @@ impl CairoPie {
230243
self.metadata.run_validity_checks()?;
231244
self.run_memory_validity_checks()?;
232245
if self.execution_resources.builtin_instance_counter.len()
246+
+ self.metadata.simulated_builtins.len()
233247
!= self.metadata.program.builtins.len()
234248
|| !self.metadata.program.builtins.iter().all(|b| {
235249
self.execution_resources
236250
.builtin_instance_counter
237251
.contains_key(b)
252+
|| self.metadata.simulated_builtins.contains(b)
238253
})
239254
{
240255
return Err(CairoPieValidationError::BuiltinListVsSegmentsMismatch);

vm/src/vm/runners/cairo_runner.rs

Lines changed: 96 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1405,18 +1405,36 @@ impl CairoRunner {
14051405
}
14061406
}
14071407

1408+
let stripped_program = self
1409+
.get_program()
1410+
.get_stripped_program()
1411+
.map_err(|_| RunnerError::StrippedProgramNoMain)?;
1412+
let mut simulated_builtins = self
1413+
.vm
1414+
.simulated_builtin_runners
1415+
.iter()
1416+
.map(|builtin| builtin.name())
1417+
.collect::<Vec<_>>();
1418+
// Sort the simulated builtins by their order in the program, so their order is aligned
1419+
// when used (for example, in the simple bootloader when setting up the pre execution
1420+
// builtin pointers of a CairoPIE subtask).
1421+
simulated_builtins.sort_by_key(|builtin| {
1422+
stripped_program
1423+
.builtins
1424+
.iter()
1425+
.position(|b| b == builtin)
1426+
.unwrap()
1427+
});
14081428
let execution_size = (self.vm.get_ap() - execution_base)?;
14091429
let metadata = CairoPieMetadata {
1410-
program: self
1411-
.get_program()
1412-
.get_stripped_program()
1413-
.map_err(|_| RunnerError::StrippedProgramNoMain)?,
1430+
program: stripped_program,
14141431
program_segment: (program_base.segment_index, self.program.data_len()).into(),
14151432
execution_segment: (execution_base.segment_index, execution_size).into(),
14161433
ret_fp_segment: (return_fp.segment_index, 0).into(),
14171434
ret_pc_segment: (return_pc.segment_index, 0).into(),
14181435
builtin_segments,
14191436
extra_segments,
1437+
simulated_builtins,
14201438
};
14211439

14221440
Ok(CairoPie {
@@ -1543,6 +1561,35 @@ impl CairoRunner {
15431561
builtins_segments,
15441562
})
15451563
}
1564+
1565+
/// Loads builtin runners for simulated builtins in the pie's metadata, so the resulted pie is
1566+
/// compatible with the one received.
1567+
pub fn load_simulated_builtin_runners(
1568+
&mut self,
1569+
simulated_builtins: &[BuiltinName],
1570+
) -> Result<(), CairoRunError> {
1571+
for builtin_name in simulated_builtins.iter() {
1572+
match builtin_name {
1573+
BuiltinName::ecdsa => {
1574+
self.vm
1575+
.simulated_builtin_runners
1576+
.push(SignatureBuiltinRunner::new(Some(1), false).into());
1577+
}
1578+
BuiltinName::keccak => {
1579+
self.vm
1580+
.simulated_builtin_runners
1581+
.push(KeccakBuiltinRunner::new(Some(1), false).into());
1582+
}
1583+
BuiltinName::ec_op => {
1584+
self.vm
1585+
.simulated_builtin_runners
1586+
.push(EcOpBuiltinRunner::new(Some(1), false).into());
1587+
}
1588+
_ => return Err(CairoRunError::UnsupportedSimulatedBuiltin(*builtin_name)),
1589+
}
1590+
}
1591+
Ok(())
1592+
}
15461593
}
15471594

15481595
// TODO(Stav): move to specified file.
@@ -5729,4 +5776,49 @@ mod tests {
57295776
deserialized_prover_input_info.public_memory_offsets
57305777
);
57315778
}
5779+
5780+
#[test]
5781+
fn test_load_simulated_builtin_runners_success() {
5782+
let program = Program::from_bytes(
5783+
include_bytes!("../../../../cairo_programs/fibonacci.json"),
5784+
Some("main"),
5785+
)
5786+
.unwrap();
5787+
let mut runner =
5788+
CairoRunner::new(&program, LayoutName::plain, None, false, false, false).unwrap();
5789+
runner.vm.simulated_builtin_runners.clear();
5790+
5791+
// Pass a slice with all supported simulated builtins.
5792+
runner
5793+
.load_simulated_builtin_runners(&[
5794+
BuiltinName::ecdsa,
5795+
BuiltinName::keccak,
5796+
BuiltinName::ec_op,
5797+
])
5798+
.unwrap();
5799+
5800+
assert_eq!(runner.vm.simulated_builtin_runners.len(), 3);
5801+
}
5802+
5803+
#[test]
5804+
fn test_load_simulated_builtin_runners_failure() {
5805+
// Use a valid Cairo program to create a runner.
5806+
let program = Program::from_bytes(
5807+
include_bytes!("../../../../cairo_programs/fibonacci.json"),
5808+
Some("main"),
5809+
)
5810+
.unwrap();
5811+
let mut runner =
5812+
CairoRunner::new(&program, LayoutName::plain, None, false, false, false).unwrap();
5813+
runner.vm.simulated_builtin_runners.clear();
5814+
5815+
// Test with an unsupported simulated builtin.
5816+
let result = runner.load_simulated_builtin_runners(&[BuiltinName::pedersen]);
5817+
match result {
5818+
Err(CairoRunError::UnsupportedSimulatedBuiltin(builtin)) => {
5819+
assert_eq!(builtin, BuiltinName::pedersen);
5820+
}
5821+
_ => panic!("Expected unsupported simulated builtin error."),
5822+
}
5823+
}
57325824
}

0 commit comments

Comments
 (0)