Skip to content

Commit c022280

Browse files
adding builtin usage tests
1 parent 1a731f2 commit c022280

File tree

2 files changed

+333
-1
lines changed

2 files changed

+333
-1
lines changed

crates/cairo-program-runner-lib/src/hints/builtin_usage_hints.rs

Lines changed: 332 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,3 +214,335 @@ pub fn flexible_builtin_usage_from_input(
214214
ap_tracking,
215215
)
216216
}
217+
#[cfg(test)]
218+
mod tests {
219+
use super::*;
220+
use crate::test_utils::prepare_ids_data_for_test;
221+
use cairo_vm::serde::deserialize_program::OffsetValue;
222+
use cairo_vm::types::relocatable::MaybeRelocatable;
223+
use cairo_vm::types::relocatable::Relocatable;
224+
use cairo_vm::vm::runners::builtin_runner::{
225+
BuiltinRunner, OutputBuiltinRunner, OutputBuiltinState, SignatureBuiltinRunner,
226+
};
227+
use cairo_vm::vm::runners::cairo_pie::PublicMemoryPage;
228+
use cairo_vm::vm::vm_core::VirtualMachine;
229+
use rstest::rstest;
230+
use std::borrow::Cow;
231+
232+
fn prepare_vm_for_flexible_builtin_usage_test(
233+
input: &FlexibleBuiltinUsageInput,
234+
) -> (
235+
VirtualMachine,
236+
ExecutionScopes,
237+
HashMap<String, HintReference>,
238+
ApTracking,
239+
) {
240+
// Prepare a VirtualMachine instance, ids_data, exec_scopes, and ap_tracking for testing.
241+
// Should mimic the vm's state after running the following Cairo0 code:
242+
// alloc_locals;
243+
// local n_output;
244+
// local n_pedersen;
245+
// local n_range_check;
246+
// local n_ecdsa;
247+
// local n_bitwise;
248+
// local n_ec_op;
249+
// local n_keccak;
250+
// local n_poseidon;
251+
// local n_range_check96;
252+
// local n_add_mod;
253+
// local n_mul_mod;
254+
// local n_memory_holes;
255+
// local n_blake2s;
256+
let mut vm = VirtualMachine::new(false, false);
257+
let ids_data = prepare_ids_data_for_test(&[
258+
"n_output",
259+
"n_pedersen",
260+
"n_range_check",
261+
"n_ecdsa",
262+
"n_bitwise",
263+
"n_ec_op",
264+
"n_keccak",
265+
"n_poseidon",
266+
"n_range_check96",
267+
"n_add_mod",
268+
"n_mul_mod",
269+
"n_memory_holes",
270+
"n_blake2s",
271+
]);
272+
let mut exec_scopes = ExecutionScopes::new();
273+
exec_scopes.insert_value(PROGRAM_INPUT, serde_json::to_string(input).unwrap());
274+
let ap_tracking = ApTracking::default();
275+
vm.set_fp(13);
276+
vm.set_ap(13);
277+
vm.segments.add();
278+
vm.segments.add();
279+
(vm, exec_scopes, ids_data, ap_tracking)
280+
}
281+
282+
/// Test the builtin_usage_add_other_segment function with finalize set to true and false.
283+
/// Checks that other_segment is added correctly and segment is finalized based on the flag.
284+
#[rstest(finalize, case(true), case(false))]
285+
fn test_builtin_usage_add_other_segment(finalize: bool) {
286+
let mut vm = VirtualMachine::new(false, false);
287+
let ids_data = prepare_ids_data_for_test(&["other_segment"]);
288+
vm.segments.add();
289+
vm.segments.add();
290+
vm.set_fp(1);
291+
let ap_tracking = ApTracking::default();
292+
let result = builtin_usage_add_other_segment(&mut vm, &ids_data, &ap_tracking, finalize);
293+
assert!(result.is_ok());
294+
295+
// Check that other_segment was added correctly.
296+
let other_segment = vm
297+
.segments
298+
.memory
299+
.get_relocatable(Relocatable::from((1, 0)))
300+
.expect("Failed to get other_segment");
301+
assert_eq!(other_segment, Relocatable::from((2, 0)));
302+
303+
// Check that the segment is finalized based on the finalize flag.
304+
if finalize {
305+
assert_eq!(vm.segments.get_segment_size(2), Some(1));
306+
} else {
307+
assert_eq!(vm.segments.get_segment_size(2), None);
308+
}
309+
}
310+
311+
/// Test the builtin_usage_add_signature hint with a sample signature.
312+
/// Checks that the signature is added correctly to the VM's state.
313+
#[test]
314+
fn test_builtin_usage_add_signature() {
315+
let mut vm = VirtualMachine::new(false, false);
316+
let ids_data = prepare_ids_data_for_test(&["ecdsa_ptr"]);
317+
let ap_tracking = ApTracking::default();
318+
vm.segments.add();
319+
vm.segments.add();
320+
vm.set_fp(1);
321+
vm.set_ap(1);
322+
323+
// Initialize the SignatureBuiltinRunner with a base of 2.
324+
let mut ecdsa_builtin = SignatureBuiltinRunner::new(Some(512), true);
325+
ecdsa_builtin.initialize_segments(&mut vm.segments);
326+
assert_eq!(ecdsa_builtin.base, 2);
327+
vm.builtin_runners = vec![ecdsa_builtin.into()];
328+
329+
// Load the ecdsa_ptr (2,0) into the VM's memory
330+
let _ = vm
331+
.load_data(
332+
Relocatable::from((1, 0)),
333+
&[MaybeRelocatable::from(Relocatable::from((2, 0)))],
334+
)
335+
.unwrap();
336+
337+
builtin_usage_add_signature(&mut vm, &ids_data, &ap_tracking)
338+
.expect("Failed to add signature");
339+
340+
// Check that the signature was added correctly.
341+
if let BuiltinRunner::Signature(sig_runner) = &vm.builtin_runners[0] {
342+
let signatures = sig_runner.signatures.try_borrow_mut().unwrap();
343+
let signature = signatures
344+
.get(&Relocatable::from((2, 0)))
345+
.expect("Signature not found at ecdsa_ptr");
346+
let expected_signature = (
347+
Felt252::from_dec_str(
348+
"3086480810278599376317923499561306189851900463386393948998357832163236918254",
349+
)
350+
.unwrap(),
351+
Felt252::from_dec_str(
352+
"598673427589502599949712887611119751108407514580626464031881322743364689811",
353+
)
354+
.unwrap(),
355+
);
356+
assert_eq!(signature.r, expected_signature.0);
357+
assert_eq!(signature.s, expected_signature.1);
358+
} else {
359+
panic!("Not a SignatureBuiltinRunner");
360+
}
361+
}
362+
363+
/// Test the builtin_usage_5_to_ap function - checks that the value 5 is inserted into the AP.
364+
/// Checks that Ap indeed has the value 5 and not 6.
365+
#[test]
366+
fn test_builtin_usage_5_to_ap() {
367+
let mut vm = VirtualMachine::new(false, false);
368+
vm.segments.add();
369+
vm.segments.add();
370+
vm.set_fp(1);
371+
vm.set_ap(1);
372+
let result = builtin_usage_5_to_ap(&mut vm);
373+
assert!(result.is_ok());
374+
// Check that the value 5 was inserted into the AP.
375+
let ap = vm.get_ap();
376+
let value = vm
377+
.segments
378+
.memory
379+
.get_integer(ap)
380+
.expect("Failed to get AP value");
381+
assert_eq!(value, Cow::Borrowed(&Felt252::from(5)));
382+
383+
//Check that 6 is not in ap.
384+
assert_ne!(value, Cow::Borrowed(&Felt252::from(6)));
385+
386+
//Also check that 5 is still in ap.
387+
assert_eq!(value, Cow::Borrowed(&Felt252::from(5)));
388+
}
389+
390+
/// Test the builtin_usage_set_pages_and_fact_topology function.
391+
/// Checks the assertion of the pedersen hash and the addition of pages and fact topology to the
392+
/// output builtin.
393+
#[rstest(
394+
left_pedersen_hash,
395+
case::hash_matches(123_usize),
396+
case::hash_mismatch(122_usize)
397+
)]
398+
fn test_builtin_usage_set_pages_and_fact_topology(left_pedersen_hash: usize) {
399+
let mut vm = VirtualMachine::new(false, false);
400+
let ids_data = prepare_ids_data_for_test(&["output_ptr"]);
401+
let ap_tracking = ApTracking::default();
402+
vm.segments.add();
403+
vm.segments.add();
404+
vm.set_fp(1);
405+
vm.set_ap(1);
406+
// Load the output_ptr (2,0) into the VM's memory
407+
let _ = vm
408+
.load_data(
409+
Relocatable::from((1, 0)),
410+
&[MaybeRelocatable::from(Relocatable::from((2, 0)))],
411+
)
412+
.unwrap();
413+
414+
// Initialize the OutputBuiltinRunner with a base of 2.
415+
let output_segment = vm.add_memory_segment();
416+
let mut output_builtin_runner = OutputBuiltinRunner::new(true);
417+
let temp_output_builtin_state = OutputBuiltinState {
418+
base: output_segment.segment_index as usize,
419+
base_offset: 0,
420+
pages: Default::default(),
421+
attributes: Default::default(),
422+
};
423+
output_builtin_runner.set_state(temp_output_builtin_state);
424+
vm.builtin_runners = vec![output_builtin_runner.into()];
425+
426+
// Load pedersen hash into output builtin_ptr
427+
let ped_hash = pedersen_hash(
428+
&FieldElement::from(left_pedersen_hash),
429+
&FieldElement::from(456_usize),
430+
);
431+
let ped_hash_felt = field_element_to_felt(ped_hash);
432+
let _ = vm
433+
.load_data(
434+
Relocatable::from((2, 0)),
435+
&[MaybeRelocatable::from(ped_hash_felt)],
436+
)
437+
.unwrap();
438+
439+
let result = builtin_usage_set_pages_and_fact_topology(&mut vm, &ids_data, &ap_tracking);
440+
441+
// Hint should succeed if the pedersen hash matches the expected value.
442+
if left_pedersen_hash == 123_usize {
443+
assert!(result.is_ok());
444+
} else {
445+
assert!(result.is_err());
446+
return;
447+
}
448+
449+
// Assert that OutputBuiltinRunner has the expected pages and attributes.
450+
if let BuiltinRunner::Output(output_runner) = &mut vm.builtin_runners[0] {
451+
let output_builtin_state = output_runner.get_state();
452+
let expected_output_builtin_state = OutputBuiltinState {
453+
base: 2,
454+
base_offset: 0,
455+
pages: HashMap::from([
456+
(1, PublicMemoryPage { start: 1, size: 2 }),
457+
(2, PublicMemoryPage { start: 3, size: 2 }),
458+
]),
459+
attributes: HashMap::from([(GPS_FACT_TOPOLOGY.into(), vec![3, 2, 0, 1, 0, 2])]),
460+
};
461+
assert_eq!(output_builtin_state, expected_output_builtin_state);
462+
} else {
463+
panic!("Not an OutputBuiltinRunner");
464+
}
465+
}
466+
467+
/// Test the flexible_builtin_usage_from_input function with a sample input.
468+
/// Asserts that the hint puts all values into the VM's locals as expected.
469+
#[test]
470+
fn test_flexible_builtin_usage_from_input() {
471+
let input = FlexibleBuiltinUsageInput {
472+
n_output: 1,
473+
n_pedersen: 2,
474+
n_range_check: 3,
475+
n_ecdsa: 4,
476+
n_bitwise: 5,
477+
n_ec_op: 6,
478+
n_keccak: 7,
479+
n_poseidon: 8,
480+
n_range_check96: 9,
481+
n_add_mod: 10,
482+
n_mul_mod: 11,
483+
n_memory_holes: 12,
484+
n_blake2s: 13,
485+
};
486+
let (mut vm, mut exec_scopes, ids_data, ap_tracking) =
487+
prepare_vm_for_flexible_builtin_usage_test(&input);
488+
489+
let result =
490+
flexible_builtin_usage_from_input(&mut vm, &mut exec_scopes, &ids_data, &ap_tracking);
491+
assert!(result.is_ok());
492+
493+
// Check that the VM state was modified as expected.
494+
let local_names = [
495+
"n_output",
496+
"n_pedersen",
497+
"n_range_check",
498+
"n_ecdsa",
499+
"n_bitwise",
500+
"n_ec_op",
501+
"n_keccak",
502+
"n_poseidon",
503+
"n_range_check96",
504+
"n_add_mod",
505+
"n_mul_mod",
506+
"n_memory_holes",
507+
"n_blake2s",
508+
];
509+
let expected_values = [
510+
input.n_output,
511+
input.n_pedersen,
512+
input.n_range_check,
513+
input.n_ecdsa,
514+
input.n_bitwise,
515+
input.n_ec_op,
516+
input.n_keccak,
517+
input.n_poseidon,
518+
input.n_range_check96,
519+
input.n_add_mod,
520+
input.n_mul_mod,
521+
input.n_memory_holes,
522+
input.n_blake2s,
523+
];
524+
525+
// Check that the VM's locals match the expected values.
526+
// The locals are stored at FP + offset2, where offset2 is the offset in the HintReference.
527+
// The offset2 is always a Value, so we can safely use it
528+
// to calculate the address of the local variable.
529+
for (i, (&name, &expected)) in local_names.iter().zip(expected_values.iter()).enumerate() {
530+
let hint_ref = ids_data.get(name).expect("Missing HintReference");
531+
// Calculate address: FP + offset2
532+
let offset: i32 = match hint_ref.offset1 {
533+
OffsetValue::Immediate(_) => panic!("Unexpected Immediate in offset2 for {name}"),
534+
OffsetValue::Value(_) => panic!("Unexpected Value in offset2 for {name}"),
535+
OffsetValue::Reference(_, offset, _, _) => offset,
536+
};
537+
let fp = vm.get_fp();
538+
let addr = Relocatable::from((fp.segment_index, (fp.offset as i32 + offset) as usize));
539+
let value_owned = vm.segments.memory.get_integer(addr).unwrap();
540+
let value = value_owned.as_ref();
541+
assert_eq!(
542+
value,
543+
&Felt252::from(expected),
544+
"Mismatch for {name} at local {i}"
545+
);
546+
}
547+
}
548+
}

crates/cairo-program-runner-lib/src/hints/types.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -532,7 +532,7 @@ pub struct MockCairoVerifierInput {
532532
pub program_output: Vec<Felt252>,
533533
}
534534

535-
#[derive(Debug, Clone, Deserialize)]
535+
#[derive(Debug, Clone, Deserialize, Serialize)]
536536
pub struct FlexibleBuiltinUsageInput {
537537
#[serde(default)]
538538
pub n_output: usize,

0 commit comments

Comments
 (0)