Skip to content

Commit 0869cd5

Browse files
starknet_os: migrate test_unpack_felts (#11140)
1 parent 99b7df2 commit 0869cd5

File tree

1 file changed

+109
-0
lines changed
  • crates/starknet_os/src/hints/hint_implementation/stateless_compression

1 file changed

+109
-0
lines changed

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

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,81 @@ use crate::test_utils::cairo_runner::{
4545

4646
const COMPRESSION_MODULE_PATH: &str = "starkware.starknet.core.os.data_availability.compression";
4747

48+
/// Runs the OS unpack_felts function and returns the unpacked felts + resources used.
49+
fn cairo_unpack_felts(
50+
compressed: &[Felt],
51+
elm_bound: u128,
52+
n_elms: usize,
53+
) -> (Vec<Felt>, ExecutionResources) {
54+
let runner_config = EntryPointRunnerConfig {
55+
layout: LayoutName::starknet,
56+
add_main_prefix_to_entrypoint: false,
57+
..Default::default()
58+
};
59+
let mut implicit_args = vec![ImplicitArg::Builtin(BuiltinName::range_check)];
60+
let (mut runner, program, entrypoint) = initialize_cairo_runner(
61+
&runner_config,
62+
OS_PROGRAM_BYTES,
63+
&format!("{COMPRESSION_MODULE_PATH}.unpack_felts"),
64+
&implicit_args,
65+
HashMap::new(),
66+
)
67+
.unwrap();
68+
69+
// Function accepts start pointer of the compressed data, and destination pointer of the
70+
// unpacked felts, as implicit arguments.
71+
let compressed_start = runner
72+
.vm
73+
.gen_arg(&compressed.iter().map(|x| MaybeRelocatable::Int(*x)).collect::<Vec<_>>())
74+
.unwrap()
75+
.get_relocatable()
76+
.unwrap();
77+
let decompressed_start = runner.vm.add_memory_segment();
78+
implicit_args.extend([
79+
ImplicitArg::NonBuiltin(EndpointArg::Value(ValueArg::Single(compressed_start.into()))),
80+
ImplicitArg::NonBuiltin(EndpointArg::Value(ValueArg::Single(decompressed_start.into()))),
81+
]);
82+
83+
let explicit_args = vec![
84+
EndpointArg::Value(ValueArg::Single(n_elms.into())),
85+
EndpointArg::Value(ValueArg::Single(Felt::from(elm_bound).into())),
86+
EndpointArg::Value(ValueArg::Single(Felt::from(get_n_elms_per_felt(elm_bound)).into())),
87+
];
88+
89+
// Run the entrypoint.
90+
// The unpacked data is stored in the segment starting at `decompressed_start`, the returned
91+
// implicit value is the end of the unpacked data.
92+
let state_reader = None;
93+
let expected_explicit_return_values = vec![];
94+
let (implicit_return_values, _explicit_return_values, _hint_processor) =
95+
run_cairo_0_entrypoint(
96+
entrypoint,
97+
&explicit_args,
98+
&implicit_args,
99+
state_reader,
100+
&mut runner,
101+
&program,
102+
&runner_config,
103+
&expected_explicit_return_values,
104+
)
105+
.unwrap();
106+
107+
// The implicit return values are [range_check_ptr, compressed_start, decompressed_end].
108+
assert_eq!(implicit_return_values.len(), 3);
109+
let EndpointArg::Value(ValueArg::Single(MaybeRelocatable::RelocatableValue(decompressed_end))) =
110+
implicit_return_values[2]
111+
else {
112+
panic!("Unexpected implicit return values, got: {implicit_return_values:?}");
113+
};
114+
115+
// Read the compressed data from the segment and return.
116+
let unpacked_data = runner
117+
.vm
118+
.get_integer_range(decompressed_start, (decompressed_end - decompressed_start).unwrap())
119+
.unwrap();
120+
(unpacked_data.into_iter().map(|f| *f).collect(), runner.get_execution_resources().unwrap())
121+
}
122+
48123
/// Runs the OS compression function and returns the compressed data, plus the execution resources
49124
/// used to compress the data.
50125
fn cairo_compress(data: &[Felt]) -> (Vec<Felt>, ExecutionResources) {
@@ -244,6 +319,40 @@ fn test_usize_pack_and_unpack() {
244319
assert_eq!(nums, unpacked);
245320
}
246321

322+
#[rstest]
323+
#[case::trivial(vec![], 0, None)]
324+
#[case::powers_of_two_plus_one((0..125).map(|i| 2_u128.pow(i) + 1).collect(), 63, Some(23))]
325+
#[case::small_number_range((0..2u128.pow(15)).collect(), 2usize.pow(11), Some(15))]
326+
#[case::large_number_range(
327+
(0..2u128.pow(10)).map(|i| 2u128.pow(30) + i).collect(), 2usize.pow(7), Some(16)
328+
)]
329+
fn test_unpack_felts(
330+
#[case] elms: Vec<u128>,
331+
#[case] expected_compression_length: usize,
332+
#[case] expected_n_steps_per_elm: Option<usize>,
333+
) {
334+
let elm_bound = elms.iter().max().unwrap_or(&0) + 1;
335+
let n_elms = elms.len();
336+
let compressed = pack_in_felts(&elms, elm_bound);
337+
let felt_bound = Felt::TWO.pow(251u8);
338+
assert!(compressed.iter().all(|f| *f < felt_bound));
339+
assert_eq!(compressed.len(), expected_compression_length);
340+
341+
let elm_felts: Vec<_> = elms.into_iter().map(Felt::from).collect();
342+
assert_eq!(
343+
elm_felts,
344+
unpack_felts_to::<u128>(&compressed, n_elms, elm_bound)
345+
.into_iter()
346+
.map(Felt::from)
347+
.collect::<Vec<Felt>>()
348+
);
349+
let (cairo_unpacked, resources) = cairo_unpack_felts(&compressed, elm_bound, n_elms);
350+
assert_eq!(elm_felts, cairo_unpacked);
351+
if n_elms > 0 {
352+
assert_eq!(resources.n_steps / n_elms, expected_n_steps_per_elm.unwrap());
353+
}
354+
}
355+
247356
#[test]
248357
fn test_get_bucket_offsets() {
249358
let lengths = vec![2, 3, 5];

0 commit comments

Comments
 (0)