Skip to content

Commit 2c75c44

Browse files
starknet_os: allocate and replace aliases test
1 parent e3ebcf7 commit 2c75c44

File tree

2 files changed

+266
-11
lines changed

2 files changed

+266
-11
lines changed

crates/starknet_os/src/test_utils/utils.rs

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ use std::sync::LazyLock;
44

55
use cairo_vm::hint_processor::builtin_hint_processor::dict_hint_utils::DICT_ACCESS_SIZE;
66
use cairo_vm::types::layout_name::LayoutName;
7+
use cairo_vm::types::relocatable::{MaybeRelocatable, Relocatable};
8+
use cairo_vm::vm::vm_core::VirtualMachine;
79
use ethnum::U256;
810
use num_bigint::{BigInt, Sign};
911
use rand::rngs::StdRng;
@@ -17,8 +19,6 @@ use crate::test_utils::cairo_runner::{
1719
EndpointArg,
1820
EntryPointRunnerConfig,
1921
ImplicitArg,
20-
PointerArg,
21-
ValueArg,
2222
};
2323

2424
#[allow(clippy::too_many_arguments)]
@@ -50,21 +50,31 @@ pub fn run_cairo_function_and_check_result(
5050
}
5151

5252
pub fn create_squashed_cairo_dict(
53-
prev_values: &HashMap<Felt, EndpointArg>,
54-
new_values: &HashMap<Felt, EndpointArg>,
55-
) -> PointerArg {
56-
let mut squashed_dict: Vec<EndpointArg> = vec![];
53+
prev_values: &HashMap<Felt, MaybeRelocatable>,
54+
new_values: &HashMap<Felt, MaybeRelocatable>,
55+
vm: &mut VirtualMachine,
56+
) -> (Relocatable, Relocatable) {
57+
let squashed_dict = flatten_cairo_dict(prev_values, new_values);
58+
let dict_segment_start = vm.add_memory_segment();
59+
let dict_segment_end = vm.load_data(dict_segment_start, &squashed_dict).unwrap();
60+
(dict_segment_start, dict_segment_end)
61+
}
62+
63+
pub fn flatten_cairo_dict(
64+
prev_values: &HashMap<Felt, MaybeRelocatable>,
65+
new_values: &HashMap<Felt, MaybeRelocatable>,
66+
) -> Vec<MaybeRelocatable> {
67+
let mut squashed_dict = vec![];
5768
let mut sorted_new_values: Vec<_> = new_values.iter().collect();
5869
sorted_new_values.sort_by_key(|(key, _)| *key);
5970

6071
for (key, value) in sorted_new_values {
61-
let prev_value: &EndpointArg =
62-
prev_values.get(key).unwrap_or(&EndpointArg::Value(ValueArg::Single(Felt::ZERO)));
72+
let prev_value = prev_values.get(key).unwrap_or(&MaybeRelocatable::Int(Felt::ZERO));
6373
squashed_dict.push((*key).into());
6474
squashed_dict.push(prev_value.clone());
6575
squashed_dict.push(value.clone());
6676
}
67-
PointerArg::Composed(squashed_dict)
77+
squashed_dict
6878
}
6979

7080
pub fn parse_squashed_cairo_dict(squashed_dict: &[Felt]) -> HashMap<Felt, Felt> {

crates/starknet_os/src/tests/aliases.rs

Lines changed: 247 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
use std::collections::{HashMap, HashSet};
1+
use core::panic;
2+
use std::collections::{BTreeSet, HashMap, HashSet};
23

34
use apollo_starknet_os_program::test_programs::ALIASES_TEST_BYTES;
45
use blockifier::state::stateful_compression::{ALIAS_COUNTER_STORAGE_KEY, INITIAL_AVAILABLE_ALIAS};
@@ -15,20 +16,25 @@ use starknet_types_core::felt::Felt;
1516

1617
use crate::test_utils::cairo_runner::{
1718
initialize_and_run_cairo_0_entry_point,
19+
initialize_cairo_runner,
20+
run_cairo_0_entrypoint,
1821
EndpointArg,
1922
EntryPointRunnerConfig,
2023
ImplicitArg,
2124
PointerArg,
2225
ValueArg,
2326
};
2427
use crate::test_utils::utils::{
28+
create_squashed_cairo_dict,
29+
flatten_cairo_dict,
2530
get_entrypoint_runner_config,
2631
parse_squashed_cairo_dict,
2732
test_cairo_function,
2833
};
2934

3035
// TODO(Nimrod): Move this next to the stateful compression hints implementation.
31-
// TODO(Amos): This test is incomplete. Add the rest of the test cases and remove this todo.
36+
37+
const DEFAULT_CLASS_HASH: u128 = 7777;
3238

3339
#[test]
3440
fn test_constants() {
@@ -309,3 +315,242 @@ fn allocate_aliases_for_keys_and_replace(
309315
);
310316
}
311317
}
318+
319+
#[rstest]
320+
#[case::non_allocation_of_address_lt_16_from_empty_storage(
321+
HashMap::from([
322+
(
323+
15,
324+
HashMap::from([(5534, 1), (98435, 1), (99999, 1)])
325+
),
326+
(
327+
16,
328+
HashMap::from([(11, 1), (127, 1), (128, 1), (129, 1), (225, 1), (7659, 1)])
329+
),
330+
(
331+
7659,
332+
HashMap::from([(12, 0), (200, 1), (300, 1), (1111, 1)])
333+
),
334+
(
335+
99999,
336+
HashMap::from([(225, 1)])
337+
)
338+
]),
339+
HashMap::new(),
340+
HashMap::new(),
341+
HashMap::new(),
342+
HashMap::from([(0, 136), (128, 128), (129, 129), (200, 132), (225, 130), (300, 133), (1111, 134), (7659, 131), (99999, 135)])
343+
)]
344+
#[case::non_allocation_of_address_lt_16_from_non_empty_storage(
345+
HashMap::from([
346+
(
347+
9,
348+
HashMap::from([(5534, 1), (98435, 1), (99999, 1)])
349+
),
350+
(
351+
44,
352+
HashMap::from([(11, 1), (129, 1), (225, 1), (7659, 1)])
353+
),
354+
(
355+
400,
356+
HashMap::from([(225, 1), (400, 1), (700, 1), (701, 1), (1111, 1)])
357+
),
358+
]),
359+
HashMap::new(),
360+
HashMap::new(),
361+
HashMap::from([(0, 135), (129, 128), (225, 129), (7659, 130), (200, 131), (300, 132), (1111, 133), (99999, 134)]),
362+
HashMap::from([(0, 138), (129, 128), (225, 129), (400, 135), (700, 136), (701, 137), (1111, 133), (7659, 130)]
363+
)
364+
)]
365+
#[case::non_allocation_with_only_trivial_updates(
366+
HashMap::from([
367+
(
368+
11,
369+
HashMap::from([(5534, 1), (98435, 1), (99999, 1)])
370+
),
371+
(
372+
44,
373+
HashMap::from([(11, 0), (129, 1), (225, 0), (400, 1), (7659, 1)])
374+
),
375+
(
376+
400,
377+
HashMap::from([(225, 0), (406, 0), (700, 1), (701, 1), (1111, 1)])
378+
),
379+
(
380+
598,
381+
HashMap::from([(2255, 0), (7008, 0)]) // Trivial update.
382+
)
383+
]),
384+
HashMap::new(),
385+
HashMap::new(),
386+
HashMap::new(),
387+
HashMap::from([(0, 134), (129, 128), (400, 129), (700, 131), (701, 132), (1111, 133), (7659, 130)]
388+
)
389+
)]
390+
#[case::allocation_with_only_nonce_change(
391+
HashMap::new(),
392+
HashMap::from([
393+
(13, 1),
394+
(58, 1),
395+
(11111, DEFAULT_CLASS_HASH), // Gets a new nonce.
396+
(222222, 1),
397+
(3333333, 1),
398+
(3333336, DEFAULT_CLASS_HASH), // Nothing changed.
399+
]),
400+
HashMap::from([(11111, 1)]),
401+
HashMap::new(),
402+
HashMap::from([(0, 131), (11111, 128), (222222, 129), (3333333, 130)]
403+
)
404+
)]
405+
#[case::non_allocation_with_trivial_class_hash_update(
406+
HashMap::new(),
407+
HashMap::from([(24, 1), (5000, 1), (6666, 1), (9999, 1), (11111, DEFAULT_CLASS_HASH),
408+
]),
409+
HashMap::new(),
410+
HashMap::from([(0, 133), (5000, 128), (11111, 129), (222222, 130), (3333333, 131), (87777, 132)]),
411+
HashMap::from([(0, 135), (5000, 128), (6666, 133), (9999, 134)]
412+
)
413+
)]
414+
#[case::allocation_with_partially_trivial_updates(
415+
HashMap::from([
416+
(
417+
1,
418+
HashMap::from([(777, 1), (8888, 1), (9999, 1)])
419+
),
420+
(
421+
100,
422+
HashMap::from([(200, 1), (777, 1), (888, 0)])
423+
),
424+
(
425+
600,
426+
HashMap::from([(2000, 1), (3000, 1)])
427+
),
428+
(
429+
800,
430+
HashMap::from([(700, 1), (701, 1)])
431+
),
432+
(
433+
3000,
434+
HashMap::from([(600, 1), (2000, 1)])
435+
),
436+
(
437+
10000,
438+
HashMap::from([(34567, 0), (435, 0)])
439+
)
440+
]),
441+
HashMap::from([(200, 1), (500, 1), (700, 1), (800, DEFAULT_CLASS_HASH)]),
442+
HashMap::from([(700, 1), (10000, 0)]),
443+
HashMap::new(),
444+
HashMap::from([(0, 137), (200, 128), (500, 130), (600, 133), (700, 134), (701, 135), (777, 129), (800, 136), (2000, 131), (3000, 132)])
445+
)]
446+
fn test_allocate_addresses_for_state_diff_and_replace(
447+
#[case] storage_updates: HashMap<u128, HashMap<u128, u128>>,
448+
#[case] address_to_class_hash: HashMap<u128, u128>,
449+
#[case] address_to_nonce: HashMap<u128, u128>,
450+
#[case] initial_alias_storage: HashMap<u128, u128>,
451+
#[case] expected_alias_storage: HashMap<u128, u128>,
452+
) {
453+
let runner_config = get_entrypoint_runner_config();
454+
let entrypoint = "__main__.allocate_aliases_and_replace";
455+
let implicit_args = [ImplicitArg::Builtin(BuiltinName::range_check)];
456+
let modified_contracts: BTreeSet<_> = storage_updates
457+
.keys()
458+
.chain(address_to_class_hash.keys().chain(address_to_nonce.keys()))
459+
.collect();
460+
461+
// Initialize the runner to be able to allocate segments.
462+
let (mut cairo_runner, program, entrypoint) = initialize_cairo_runner(
463+
&runner_config,
464+
ALIASES_TEST_BYTES,
465+
entrypoint,
466+
&implicit_args,
467+
HashMap::new(),
468+
)
469+
.unwrap();
470+
471+
// Construct the contract state changes.
472+
let mut prev_state_entries = HashMap::new();
473+
let mut new_state_entries = HashMap::new();
474+
let n_contracts = modified_contracts.len();
475+
for address in modified_contracts {
476+
let inner_updates = storage_updates
477+
.get(address)
478+
.unwrap_or(&HashMap::new())
479+
.iter()
480+
.map(|(k, v)| ((*k).into(), Felt::from(*v).into()))
481+
.collect();
482+
let (new_nonce, prev_nonce) = (address_to_nonce.get(address).copied().unwrap_or(0), 0);
483+
let (new_class_hash, prev_class_hash) = (
484+
address_to_class_hash.get(address).copied().unwrap_or(DEFAULT_CLASS_HASH),
485+
DEFAULT_CLASS_HASH,
486+
);
487+
let (prev_storage_ptr, new_storage_ptr) =
488+
create_squashed_cairo_dict(&HashMap::new(), &inner_updates, &mut cairo_runner.vm);
489+
let new_state_entry: Vec<MaybeRelocatable> = vec![
490+
Felt::from(new_class_hash).into(),
491+
new_storage_ptr.into(),
492+
Felt::from(new_nonce).into(),
493+
];
494+
let prev_state_entry: Vec<MaybeRelocatable> = vec![
495+
Felt::from(prev_class_hash).into(),
496+
prev_storage_ptr.into(),
497+
Felt::from(prev_nonce).into(),
498+
];
499+
new_state_entries
500+
.insert((*address).into(), cairo_runner.vm.gen_arg(&new_state_entry).unwrap());
501+
prev_state_entries
502+
.insert((*address).into(), cairo_runner.vm.gen_arg(&prev_state_entry).unwrap());
503+
}
504+
let flat_contract_state_changes = flatten_cairo_dict(&prev_state_entries, &new_state_entries);
505+
let explicit_args = vec![
506+
EndpointArg::Value(ValueArg::Single(n_contracts.into())),
507+
EndpointArg::Pointer(PointerArg::Array(flat_contract_state_changes)),
508+
];
509+
let storage_view = initial_alias_storage
510+
.into_iter()
511+
.map(|(key, value)| ((*ALIAS_CONTRACT_ADDRESS, key.into()), value.into()))
512+
.collect();
513+
let state_reader = DictStateReader { storage_view, ..Default::default() };
514+
let expected_aliases_storage_flat_length = expected_alias_storage.len() * DICT_ACCESS_SIZE;
515+
let expected_explicit_return_values = vec![
516+
EndpointArg::Pointer(PointerArg::Array(vec![
517+
MaybeRelocatable::Int(Felt::ZERO);
518+
expected_aliases_storage_flat_length
519+
])),
520+
EndpointArg::Pointer(PointerArg::Array(vec![])),
521+
EndpointArg::Pointer(PointerArg::Array(vec![])),
522+
];
523+
524+
let (_, explicit_return_values) = run_cairo_0_entrypoint(
525+
entrypoint,
526+
&explicit_args,
527+
&implicit_args,
528+
Some(state_reader),
529+
&mut cairo_runner,
530+
&program,
531+
&runner_config,
532+
&expected_explicit_return_values,
533+
)
534+
.unwrap();
535+
536+
// TODO(Nimrod): Complete this test to also compare the other return values.
537+
if let [
538+
EndpointArg::Pointer(PointerArg::Array(aliases_storage_updates)),
539+
EndpointArg::Pointer(PointerArg::Array(_)),
540+
EndpointArg::Pointer(PointerArg::Array(_)),
541+
] = explicit_return_values.as_slice()
542+
{
543+
let aliases_storage_updates_as_felts: Vec<Felt> =
544+
aliases_storage_updates.iter().map(|f| f.get_int().unwrap()).collect();
545+
let actual_alias_storage = parse_squashed_cairo_dict(&aliases_storage_updates_as_felts);
546+
let expected_alias_storage: HashMap<Felt, Felt> = expected_alias_storage
547+
.into_iter()
548+
.map(|(key, value)| (key.into(), value.into()))
549+
.collect();
550+
assert_eq!(actual_alias_storage, expected_alias_storage);
551+
} else {
552+
panic!(
553+
"The return value doesn't match the given format.\n Got: {explicit_return_values:?}"
554+
);
555+
}
556+
}

0 commit comments

Comments
 (0)