Skip to content

Commit ca1ea0f

Browse files
starknet_os: migrate test_compress (to test cairo compression) (#9917)
1 parent 0316394 commit ca1ea0f

File tree

1 file changed

+121
-1
lines changed
  • crates/starknet_os/src/hints/hint_implementation/stateless_compression

1 file changed

+121
-1
lines changed

crates/starknet_os/src/hints/hint_implementation/stateless_compression/tests.rs

Lines changed: 121 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
1-
use std::collections::HashSet;
1+
use std::collections::{HashMap, HashSet};
22

3+
use apollo_starknet_os_program::OS_PROGRAM_BYTES;
34
use assert_matches::assert_matches;
5+
use cairo_vm::types::builtin_name::BuiltinName;
6+
use cairo_vm::types::layout_name::LayoutName;
7+
use cairo_vm::types::relocatable::MaybeRelocatable;
8+
use cairo_vm::vm::runners::cairo_runner::ExecutionResources;
9+
use expect_test::{expect, Expect};
410
use num_bigint::BigUint;
511
use rstest::rstest;
612
use starknet_types_core::felt::Felt;
@@ -27,6 +33,87 @@ use crate::hints::hint_implementation::stateless_compression::utils::{
2733
unpack_felts,
2834
unpack_felts_to_usize,
2935
};
36+
use crate::test_utils::cairo_runner::{
37+
initialize_cairo_runner,
38+
run_cairo_0_entrypoint,
39+
EndpointArg,
40+
EntryPointRunnerConfig,
41+
ImplicitArg,
42+
ValueArg,
43+
};
44+
45+
/// Runs the OS compression function and returns the compressed data, plus the execution resources
46+
/// used to compress the data.
47+
fn cairo_compress(data: &[Felt]) -> (Vec<Felt>, ExecutionResources) {
48+
let range_check_arg = ImplicitArg::Builtin(BuiltinName::range_check);
49+
let runner_config = EntryPointRunnerConfig {
50+
layout: LayoutName::starknet,
51+
add_main_prefix_to_entrypoint: false,
52+
..Default::default()
53+
};
54+
let (mut runner, program, entrypoint) = initialize_cairo_runner(
55+
&runner_config,
56+
OS_PROGRAM_BYTES,
57+
"starkware.starknet.core.os.data_availability.compression.compress",
58+
std::slice::from_ref(&range_check_arg),
59+
HashMap::new(),
60+
)
61+
.unwrap();
62+
63+
// Function accepts start and end pointers explicitly, and the destination pointer is passed
64+
// as an implicit argument (along with the range check pointer).
65+
let data_start = runner
66+
.vm
67+
.gen_arg(&data.iter().map(|x| MaybeRelocatable::Int(*x)).collect::<Vec<_>>())
68+
.unwrap()
69+
.get_relocatable()
70+
.unwrap();
71+
let compressed_dst = runner.vm.add_memory_segment();
72+
let explicit_args = vec![
73+
EndpointArg::Value(ValueArg::Single(data_start.into())),
74+
EndpointArg::Value(ValueArg::Single((data_start + data.len()).unwrap().into())),
75+
];
76+
let implicit_args = vec![
77+
range_check_arg,
78+
ImplicitArg::NonBuiltin(EndpointArg::Value(ValueArg::Single(compressed_dst.into()))),
79+
];
80+
81+
// Run the entrypoint.
82+
// The compressed data is stored in the segment starting at `compressed_dst`, the returned
83+
// implicit value is the end of the compressed data.
84+
let state_reader = None;
85+
let expected_explicit_return_values = vec![];
86+
let (implicit_return_values, _explicit_return_values, _hint_processor) =
87+
run_cairo_0_entrypoint(
88+
entrypoint,
89+
&explicit_args,
90+
&implicit_args,
91+
state_reader,
92+
&mut runner,
93+
&program,
94+
&runner_config,
95+
&expected_explicit_return_values,
96+
)
97+
.unwrap();
98+
99+
// The implicit return values are [range_check_ptr, compressed_end].
100+
assert_eq!(implicit_return_values.len(), 2);
101+
let EndpointArg::Value(ValueArg::Single(MaybeRelocatable::RelocatableValue(compressed_end))) =
102+
implicit_return_values[1]
103+
else {
104+
panic!(
105+
"Unexpected implicit return value for compressed_end, got: {:?}",
106+
implicit_return_values[1]
107+
);
108+
};
109+
110+
// Read the compressed data from the segment and return.
111+
let compressed_data = runner
112+
.vm
113+
.get_integer_range(compressed_dst, (compressed_end - compressed_dst).unwrap())
114+
.unwrap();
115+
(compressed_data.into_iter().map(|f| *f).collect(), runner.get_execution_resources().unwrap())
116+
}
30117

31118
#[rstest]
32119
#[case::zero([false; 10], Felt::ZERO)]
@@ -274,3 +361,36 @@ fn test_compression_length(
274361
fn test_get_n_elms_per_felt(#[case] elm_bound: u32, #[case] expected_n_elems: usize) {
275362
assert_eq!(get_n_elms_per_felt(elm_bound), expected_n_elems);
276363
}
364+
365+
/// Test that Cairo and Rust implementations of compress are identical. Also verifies resources
366+
/// required for cairo compression.
367+
#[rstest]
368+
#[case::no_buckets(vec![], None)]
369+
#[case::single_bucket_one_value(vec![Felt::from(7777777)], Some(expect![[r#"
370+
1271
371+
"#]]))]
372+
#[case::large_duplicates(vec![Felt::from(BigUint::from(2_u8).pow(250)); 100], Some(expect![[r#"
373+
78
374+
"#]]))]
375+
#[case::small_values((0..(1 << 15)).map(Felt::from).collect(), Some(expect![[r#"
376+
60
377+
"#]]))]
378+
#[case::mixed_buckets(
379+
(0..252).map(|i| Felt::from(BigUint::from(2_u8).pow(i))).collect(),
380+
Some(expect![[r#"
381+
62
382+
"#]]))]
383+
fn test_cairo_compress(#[case] data: Vec<Felt>, #[case] expected_n_steps_per_elm: Option<Expect>) {
384+
let compressed = compress(&data);
385+
let (cairo_compressed, execution_resources) = cairo_compress(&data);
386+
assert_eq!(cairo_compressed, compressed);
387+
388+
if !data.is_empty() {
389+
expected_n_steps_per_elm
390+
.unwrap()
391+
.assert_debug_eq(&(execution_resources.n_steps / data.len()));
392+
}
393+
394+
assert_eq!(data, decompress(&mut compressed.into_iter()));
395+
// TODO(Dori): Check cairo decompression.
396+
}

0 commit comments

Comments
 (0)