use crate::{
types::{exec_scope::ExecutionScopes, relocatable::MaybeRelocatable},
vm::{errors::hint_errors::HintError, vm_core::VirtualMachine},
};
use std::{any::Any, cell::RefCell, collections::HashMap, rc::Rc};
use crate::{
any_box,
hint_processor::{
builtin_hint_processor::hint_utils::{
get_ptr_from_var_name, insert_value_from_var_name, insert_value_into_ap,
},
hint_processor_definition::HintReference,
},
serde::deserialize_program::ApTracking,
};
use super::{dict_manager::DictManager, hint_utils::get_maybe_relocatable_from_var_name};
pub const DICT_ACCESS_SIZE: usize = 3;
fn copy_initial_dict(
exec_scopes: &mut ExecutionScopes,
) -> Option<HashMap<MaybeRelocatable, MaybeRelocatable>> {
let mut initial_dict: Option<HashMap<MaybeRelocatable, MaybeRelocatable>> = None;
if let Some(variable) = exec_scopes.get_local_variables().ok()?.get("initial_dict") {
if let Some(dict) = variable.downcast_ref::<HashMap<MaybeRelocatable, MaybeRelocatable>>() {
initial_dict = Some(dict.clone());
}
}
initial_dict
}
pub fn dict_new(
vm: &mut VirtualMachine,
exec_scopes: &mut ExecutionScopes,
) -> Result<(), HintError> {
let initial_dict = copy_initial_dict(exec_scopes).ok_or(HintError::NoInitialDict)?;
let base = if let Ok(dict_manager) = exec_scopes.get_dict_manager() {
dict_manager.borrow_mut().new_dict(vm, initial_dict)?
} else {
let mut dict_manager = DictManager::new();
let base = dict_manager.new_dict(vm, initial_dict)?;
exec_scopes.insert_value("dict_manager", Rc::new(RefCell::new(dict_manager)));
base
};
insert_value_into_ap(vm, base)
}
pub fn default_dict_new(
vm: &mut VirtualMachine,
exec_scopes: &mut ExecutionScopes,
ids_data: &HashMap<String, HintReference>,
ap_tracking: &ApTracking,
) -> Result<(), HintError> {
let default_value =
get_maybe_relocatable_from_var_name("default_value", vm, ids_data, ap_tracking)?;
let initial_dict = copy_initial_dict(exec_scopes);
let base = if let Ok(dict_manager) = exec_scopes.get_dict_manager() {
dict_manager
.borrow_mut()
.new_default_dict(vm, &default_value, initial_dict)?
} else {
let mut dict_manager = DictManager::new();
let base = dict_manager.new_default_dict(vm, &default_value, initial_dict)?;
exec_scopes.insert_value("dict_manager", Rc::new(RefCell::new(dict_manager)));
base
};
insert_value_into_ap(vm, base)
}
pub fn dict_read(
vm: &mut VirtualMachine,
exec_scopes: &mut ExecutionScopes,
ids_data: &HashMap<String, HintReference>,
ap_tracking: &ApTracking,
) -> Result<(), HintError> {
let key = get_maybe_relocatable_from_var_name("key", vm, ids_data, ap_tracking)?;
let dict_ptr = get_ptr_from_var_name("dict_ptr", vm, ids_data, ap_tracking)?;
let dict_manager_ref = exec_scopes.get_dict_manager()?;
let mut dict = dict_manager_ref.borrow_mut();
let tracker = dict.get_tracker_mut(&dict_ptr)?;
tracker.current_ptr.offset += DICT_ACCESS_SIZE;
let value = tracker.get_value(&key)?;
insert_value_from_var_name("value", value.clone(), vm, ids_data, ap_tracking)
}
pub fn dict_write(
vm: &mut VirtualMachine,
exec_scopes: &mut ExecutionScopes,
ids_data: &HashMap<String, HintReference>,
ap_tracking: &ApTracking,
) -> Result<(), HintError> {
let key = get_maybe_relocatable_from_var_name("key", vm, ids_data, ap_tracking)?;
let new_value = get_maybe_relocatable_from_var_name("new_value", vm, ids_data, ap_tracking)?;
let dict_ptr = get_ptr_from_var_name("dict_ptr", vm, ids_data, ap_tracking)?;
let dict_manager_ref = exec_scopes.get_dict_manager()?;
let mut dict = dict_manager_ref.borrow_mut();
let tracker = dict.get_tracker_mut(&dict_ptr)?;
let dict_ptr_prev_value = dict_ptr + 1_i32;
tracker.current_ptr.offset += DICT_ACCESS_SIZE;
let prev_value = tracker.get_value(&key)?.clone();
tracker.insert_value(&key, &new_value);
vm.insert_value(&dict_ptr_prev_value, prev_value)?;
Ok(())
}
pub fn dict_update(
vm: &mut VirtualMachine,
exec_scopes: &mut ExecutionScopes,
ids_data: &HashMap<String, HintReference>,
ap_tracking: &ApTracking,
) -> Result<(), HintError> {
let key = get_maybe_relocatable_from_var_name("key", vm, ids_data, ap_tracking)?;
let prev_value = get_maybe_relocatable_from_var_name("prev_value", vm, ids_data, ap_tracking)?;
let new_value = get_maybe_relocatable_from_var_name("new_value", vm, ids_data, ap_tracking)?;
let dict_ptr = get_ptr_from_var_name("dict_ptr", vm, ids_data, ap_tracking)?;
let dict_manager_ref = exec_scopes.get_dict_manager()?;
let mut dict = dict_manager_ref.borrow_mut();
let tracker = dict.get_tracker_mut(&dict_ptr)?;
let current_value = tracker.get_value(&key)?;
if current_value != &prev_value {
return Err(HintError::WrongPrevValue(
prev_value,
current_value.into(),
key,
));
}
tracker.insert_value(&key, &new_value);
tracker.current_ptr.offset += DICT_ACCESS_SIZE;
Ok(())
}
pub fn dict_squash_copy_dict(
vm: &mut VirtualMachine,
exec_scopes: &mut ExecutionScopes,
ids_data: &HashMap<String, HintReference>,
ap_tracking: &ApTracking,
) -> Result<(), HintError> {
let dict_accesses_end = get_ptr_from_var_name("dict_accesses_end", vm, ids_data, ap_tracking)?;
let dict_manager_ref = exec_scopes.get_dict_manager()?;
let dict_manager = dict_manager_ref.borrow();
let dict_copy: Box<dyn Any> = Box::new(
dict_manager
.get_tracker(&dict_accesses_end)?
.get_dictionary_copy(),
);
exec_scopes.enter_scope(HashMap::from([
(
String::from("dict_manager"),
any_box!(exec_scopes.get_dict_manager()?),
),
(String::from("initial_dict"), dict_copy),
]));
Ok(())
}
pub fn dict_squash_update_ptr(
vm: &mut VirtualMachine,
exec_scopes: &mut ExecutionScopes,
ids_data: &HashMap<String, HintReference>,
ap_tracking: &ApTracking,
) -> Result<(), HintError> {
let squashed_dict_start =
get_ptr_from_var_name("squashed_dict_start", vm, ids_data, ap_tracking)?;
let squashed_dict_end = get_ptr_from_var_name("squashed_dict_end", vm, ids_data, ap_tracking)?;
exec_scopes
.get_dict_manager()?
.borrow_mut()
.get_tracker_mut(&squashed_dict_start)?
.current_ptr = squashed_dict_end;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::any_box;
use crate::hint_processor::builtin_hint_processor::builtin_hint_processor_definition::BuiltinHintProcessor;
use crate::hint_processor::builtin_hint_processor::builtin_hint_processor_definition::HintProcessorData;
use crate::hint_processor::builtin_hint_processor::dict_manager::Dictionary;
use crate::hint_processor::builtin_hint_processor::hint_code;
use crate::hint_processor::hint_processor_definition::HintProcessor;
use crate::types::exec_scope::ExecutionScopes;
use crate::vm::errors::vm_errors::VirtualMachineError;
use crate::vm::vm_memory::memory::Memory;
use crate::{
hint_processor::builtin_hint_processor::dict_manager::{DictManager, DictTracker},
relocatable,
types::relocatable::{MaybeRelocatable, Relocatable},
utils::test_utils::*,
vm::{errors::memory_errors::MemoryError, vm_core::VirtualMachine},
};
use std::collections::HashMap;
#[test]
fn run_dict_new_with_initial_dict_empty() {
let hint_code = "if '__dict_manager' not in globals():\n from starkware.cairo.common.dict import DictManager\n __dict_manager = DictManager()\n\nmemory[ap] = __dict_manager.new_dict(segments, initial_dict)\ndel initial_dict";
let mut vm = vm!();
add_segments!(vm, 1);
let mut exec_scopes = scope![(
"initial_dict",
HashMap::<MaybeRelocatable, MaybeRelocatable>::new()
)];
run_hint!(vm, HashMap::new(), hint_code, &mut exec_scopes)
.expect("Error while executing hint");
assert_eq!(vm.segments.num_segments, 2);
check_memory![vm.memory, ((1, 0), (1, 0))];
assert_eq!(
exec_scopes
.get_dict_manager()
.unwrap()
.borrow()
.trackers
.get(&1),
Some(&DictTracker::new_empty(&relocatable!(1, 0)))
);
}
#[test]
fn run_dict_new_with_no_initial_dict() {
let hint_code = "if '__dict_manager' not in globals():\n from starkware.cairo.common.dict import DictManager\n __dict_manager = DictManager()\n\nmemory[ap] = __dict_manager.new_dict(segments, initial_dict)\ndel initial_dict";
let mut vm = vm!();
assert_eq!(
run_hint!(vm, HashMap::new(), hint_code),
Err(HintError::NoInitialDict)
);
}
#[test]
fn run_dict_new_ap_is_taken() {
let hint_code = "if '__dict_manager' not in globals():\n from starkware.cairo.common.dict import DictManager\n __dict_manager = DictManager()\n\nmemory[ap] = __dict_manager.new_dict(segments, initial_dict)\ndel initial_dict";
let mut vm = vm!();
let mut exec_scopes = scope![(
"initial_dict",
HashMap::<MaybeRelocatable, MaybeRelocatable>::new()
)];
vm.memory = memory![((1, 0), 1)];
assert_eq!(
run_hint!(vm, HashMap::new(), hint_code, &mut exec_scopes),
Err(HintError::Internal(VirtualMachineError::MemoryError(
MemoryError::InconsistentMemory(
MaybeRelocatable::from((1, 0)),
MaybeRelocatable::from(1),
MaybeRelocatable::from((0, 0))
)
)))
);
}
#[test]
fn run_dict_read_valid() {
let hint_code = "dict_tracker = __dict_manager.get_tracker(ids.dict_ptr)\ndict_tracker.current_ptr += ids.DictAccess.SIZE\nids.value = dict_tracker.data[ids.key]";
let mut vm = vm!();
vm.run_context.fp = 3;
vm.memory = memory![((1, 0), 5), ((1, 2), (2, 0))];
let ids_data = ids_data!["key", "value", "dict_ptr"];
add_segments!(vm, 1);
let mut exec_scopes = ExecutionScopes::new();
dict_manager!(&mut exec_scopes, 2, (5, 12));
assert_eq!(run_hint!(vm, ids_data, hint_code, &mut exec_scopes), Ok(()));
assert_eq!(
vm.memory
.get(&MaybeRelocatable::from((1, 1)))
.unwrap()
.unwrap()
.as_ref(),
&MaybeRelocatable::from(12)
);
check_dict_ptr!(&exec_scopes, 2, (2, 3));
}
#[test]
fn run_dict_read_invalid_key() {
let hint_code = "dict_tracker = __dict_manager.get_tracker(ids.dict_ptr)\ndict_tracker.current_ptr += ids.DictAccess.SIZE\nids.value = dict_tracker.data[ids.key]";
let mut vm = vm!();
vm.run_context.fp = 3;
vm.memory = memory![((1, 0), 6), ((1, 2), (2, 0))];
let ids_data = ids_data!["key", "value", "dict_ptr"];
let mut exec_scopes = ExecutionScopes::new();
dict_manager!(&mut exec_scopes, 2, (5, 12));
assert_eq!(
run_hint!(vm, ids_data, hint_code, &mut exec_scopes),
Err(HintError::NoValueForKey(MaybeRelocatable::from(6)))
);
}
#[test]
fn run_dict_read_no_tracker() {
let hint_code = "dict_tracker = __dict_manager.get_tracker(ids.dict_ptr)\ndict_tracker.current_ptr += ids.DictAccess.SIZE\nids.value = dict_tracker.data[ids.key]";
let mut vm = vm!();
vm.run_context.fp = 3;
let mut exec_scopes = scope![("dict_manager", Rc::new(RefCell::new(DictManager::new())))];
vm.memory = memory![((1, 0), 6), ((1, 2), (2, 0))];
add_segments!(vm, 1);
let ids_data = ids_data!["key", "value", "dict_ptr"];
assert_eq!(
run_hint!(vm, ids_data, hint_code, &mut exec_scopes),
Err(HintError::NoDictTracker(2))
);
}
#[test]
fn run_default_dict_new_valid() {
let hint_code = "if '__dict_manager' not in globals():\n from starkware.cairo.common.dict import DictManager\n __dict_manager = DictManager()\n\nmemory[ap] = __dict_manager.new_default_dict(segments, ids.default_value)";
let mut vm = vm!();
run_context!(vm, 0, 1, 1);
vm.memory = memory![((1, 0), 17)];
let ids_data = ids_data!["default_value"];
let mut exec_scopes = ExecutionScopes::new();
run_hint!(vm, ids_data, hint_code, &mut exec_scopes).expect("Error while executing hint");
assert_eq!(vm.memory.data.len(), 3);
check_memory![vm.memory, ((1, 1), (0, 0))];
assert_eq!(
exec_scopes
.get_dict_manager()
.unwrap()
.borrow()
.trackers
.get(&0),
Some(&DictTracker::new_default_dict(
&relocatable!(0, 0),
&MaybeRelocatable::from(17),
None
))
);
}
#[test]
fn run_default_dict_new_no_default_value() {
let hint_code = "if '__dict_manager' not in globals():\n from starkware.cairo.common.dict import DictManager\n __dict_manager = DictManager()\n\nmemory[ap] = __dict_manager.new_default_dict(segments, ids.default_value)";
let mut vm = vm!();
vm.run_context.fp = 1;
let ids_data = ids_data!["default_value"];
assert_eq!(
run_hint!(vm, ids_data, hint_code),
Err(HintError::FailedToGetIds)
);
}
#[test]
fn run_dict_write_default_valid_empty_dict() {
let hint_code = "dict_tracker = __dict_manager.get_tracker(ids.dict_ptr)\ndict_tracker.current_ptr += ids.DictAccess.SIZE\nids.dict_ptr.prev_value = dict_tracker.data[ids.key]\ndict_tracker.data[ids.key] = ids.new_value";
let mut vm = vm!();
vm.run_context.fp = 3;
let mut exec_scopes = ExecutionScopes::new();
dict_manager_default!(&mut exec_scopes, 2, 2);
vm.memory = memory![((1, 0), 5), ((1, 1), 17), ((1, 2), (2, 0))];
add_segments!(vm, 1);
let ids_data = ids_data!["key", "new_value", "dict_ptr"];
assert_eq!(run_hint!(vm, ids_data, hint_code, &mut exec_scopes), Ok(()));
check_dictionary![exec_scopes, 2, (5, 17)];
check_dict_ptr!(exec_scopes, 2, (2, 3));
check_memory![vm.memory, ((2, 1), 2)];
}
#[test]
fn run_dict_write_default_valid_overwrite_value() {
let hint_code = "dict_tracker = __dict_manager.get_tracker(ids.dict_ptr)\ndict_tracker.current_ptr += ids.DictAccess.SIZE\nids.dict_ptr.prev_value = dict_tracker.data[ids.key]\ndict_tracker.data[ids.key] = ids.new_value";
let mut vm = vm!();
vm.run_context.fp = 3;
let mut exec_scopes = ExecutionScopes::new();
dict_manager_default!(exec_scopes, 2, 2, (5, 10));
vm.memory = memory![((1, 0), 5), ((1, 1), 17), ((1, 2), (2, 0))];
add_segments!(vm, 1);
let ids_data = ids_data!["key", "new_value", "dict_ptr"];
assert_eq!(run_hint!(vm, ids_data, hint_code, &mut exec_scopes), Ok(()));
check_dictionary![exec_scopes, 2, (5, 17)];
check_dict_ptr!(exec_scopes, 2, (2, 3));
check_memory![vm.memory, ((2, 1), 10)];
}
#[test]
fn run_dict_write_simple_valid_overwrite_value() {
let hint_code = "dict_tracker = __dict_manager.get_tracker(ids.dict_ptr)\ndict_tracker.current_ptr += ids.DictAccess.SIZE\nids.dict_ptr.prev_value = dict_tracker.data[ids.key]\ndict_tracker.data[ids.key] = ids.new_value";
let mut vm = vm!();
vm.run_context.fp = 3;
let mut exec_scopes = ExecutionScopes::new();
dict_manager!(exec_scopes, 2, (5, 10));
vm.memory = memory![((1, 0), 5), ((1, 1), 17), ((1, 2), (2, 0))];
add_segments!(vm, 1);
let ids_data = ids_data!["key", "new_value", "dict_ptr"];
assert_eq!(run_hint!(vm, ids_data, hint_code, &mut exec_scopes), Ok(()));
check_dictionary![exec_scopes, 2, (5, 17)];
check_dict_ptr!(exec_scopes, 2, (2, 3));
check_dict_ptr!(exec_scopes, 2, (2, 3));
check_memory![vm.memory, ((2, 1), 10)];
}
#[test]
fn run_dict_write_simple_valid_cant_write_new_key() {
let hint_code = "dict_tracker = __dict_manager.get_tracker(ids.dict_ptr)\ndict_tracker.current_ptr += ids.DictAccess.SIZE\nids.dict_ptr.prev_value = dict_tracker.data[ids.key]\ndict_tracker.data[ids.key] = ids.new_value";
let mut vm = vm!();
vm.run_context.fp = 3;
let mut exec_scopes = ExecutionScopes::new();
dict_manager!(exec_scopes, 2);
vm.memory = memory![((1, 0), 5), ((1, 1), 17), ((1, 2), (2, 0))];
add_segments!(vm, 1);
let ids_data = ids_data!["key", "new_value", "dict_ptr"];
assert_eq!(
run_hint!(vm, ids_data, hint_code, &mut exec_scopes),
Err(HintError::NoValueForKey(MaybeRelocatable::from(5)))
);
}
#[test]
fn run_dict_update_simple_valid() {
let hint_code = "# Verify dict pointer and prev value.\ndict_tracker = __dict_manager.get_tracker(ids.dict_ptr)\ncurrent_value = dict_tracker.data[ids.key]\nassert current_value == ids.prev_value, \\\n f'Wrong previous value in dict. Got {ids.prev_value}, expected {current_value}.'\n\n# Update value.\ndict_tracker.data[ids.key] = ids.new_value\ndict_tracker.current_ptr += ids.DictAccess.SIZE";
let mut vm = vm!();
vm.run_context.fp = 4;
let mut exec_scopes = ExecutionScopes::new();
dict_manager!(exec_scopes, 2, (5, 10));
vm.memory = memory![((1, 0), 5), ((1, 1), 10), ((1, 2), 20), ((1, 3), (2, 0))];
add_segments!(vm, 1);
let ids_data = ids_data!["key", "prev_value", "new_value", "dict_ptr"];
assert_eq!(run_hint!(vm, ids_data, hint_code, &mut exec_scopes), Ok(()));
check_dictionary![exec_scopes, 2, (5, 20)];
check_dict_ptr!(exec_scopes, 2, (2, 3));
}
#[test]
fn run_dict_update_simple_valid_no_change() {
let hint_code = "# Verify dict pointer and prev value.\ndict_tracker = __dict_manager.get_tracker(ids.dict_ptr)\ncurrent_value = dict_tracker.data[ids.key]\nassert current_value == ids.prev_value, \\\n f'Wrong previous value in dict. Got {ids.prev_value}, expected {current_value}.'\n\n# Update value.\ndict_tracker.data[ids.key] = ids.new_value\ndict_tracker.current_ptr += ids.DictAccess.SIZE";
let mut vm = vm!();
vm.run_context.fp = 4;
let mut exec_scopes = ExecutionScopes::new();
dict_manager!(exec_scopes, 2, (5, 10));
vm.memory = memory![((1, 0), 5), ((1, 1), 10), ((1, 2), 10), ((1, 3), (2, 0))];
add_segments!(vm, 1);
let ids_data = ids_data!["key", "prev_value", "new_value", "dict_ptr"];
assert_eq!(run_hint!(vm, ids_data, hint_code, &mut exec_scopes), Ok(()));
check_dictionary![exec_scopes, 2, (5, 10)];
check_dict_ptr!(exec_scopes, 2, (2, 3));
}
#[test]
fn run_dict_update_simple_invalid_wrong_prev_key() {
let hint_code = "# Verify dict pointer and prev value.\ndict_tracker = __dict_manager.get_tracker(ids.dict_ptr)\ncurrent_value = dict_tracker.data[ids.key]\nassert current_value == ids.prev_value, \\\n f'Wrong previous value in dict. Got {ids.prev_value}, expected {current_value}.'\n\n# Update value.\ndict_tracker.data[ids.key] = ids.new_value\ndict_tracker.current_ptr += ids.DictAccess.SIZE";
let mut vm = vm!();
vm.run_context.fp = 4;
let mut exec_scopes = ExecutionScopes::new();
dict_manager!(exec_scopes, 2, (5, 10));
vm.memory = memory![((1, 0), 5), ((1, 1), 11), ((1, 2), 20), ((1, 3), (2, 0))];
add_segments!(vm, 1);
let ids_data = ids_data!["key", "prev_value", "new_value", "dict_ptr"];
assert_eq!(
run_hint!(vm, ids_data, hint_code, &mut exec_scopes),
Err(HintError::WrongPrevValue(
MaybeRelocatable::from(11),
MaybeRelocatable::from(10),
MaybeRelocatable::from(5)
))
);
}
#[test]
fn run_dict_update_simple_invalid_wrong_key() {
let hint_code = "# Verify dict pointer and prev value.\ndict_tracker = __dict_manager.get_tracker(ids.dict_ptr)\ncurrent_value = dict_tracker.data[ids.key]\nassert current_value == ids.prev_value, \\\n f'Wrong previous value in dict. Got {ids.prev_value}, expected {current_value}.'\n\n# Update value.\ndict_tracker.data[ids.key] = ids.new_value\ndict_tracker.current_ptr += ids.DictAccess.SIZE";
let mut vm = vm!();
vm.run_context.fp = 4;
let mut exec_scopes = ExecutionScopes::new();
dict_manager!(exec_scopes, 2, (5, 10));
vm.memory = memory![((1, 0), 6), ((1, 1), 10), ((1, 2), 10), ((1, 3), (2, 0))];
add_segments!(vm, 1);
let ids_data = ids_data!["key", "prev_value", "new_value", "dict_ptr"];
assert_eq!(
run_hint!(vm, ids_data, hint_code, &mut exec_scopes),
Err(HintError::NoValueForKey(MaybeRelocatable::from(6)))
);
}
#[test]
fn run_dict_update_default_valid() {
let hint_code = "# Verify dict pointer and prev value.\ndict_tracker = __dict_manager.get_tracker(ids.dict_ptr)\ncurrent_value = dict_tracker.data[ids.key]\nassert current_value == ids.prev_value, \\\n f'Wrong previous value in dict. Got {ids.prev_value}, expected {current_value}.'\n\n# Update value.\ndict_tracker.data[ids.key] = ids.new_value\ndict_tracker.current_ptr += ids.DictAccess.SIZE";
let mut vm = vm!();
vm.run_context.fp = 4;
let mut exec_scopes = ExecutionScopes::new();
dict_manager!(exec_scopes, 2, (5, 10));
vm.memory = memory![((1, 0), 5), ((1, 1), 10), ((1, 2), 20), ((1, 3), (2, 0))];
add_segments!(vm, 1);
let ids_data = ids_data!["key", "prev_value", "new_value", "dict_ptr"];
assert_eq!(run_hint!(vm, ids_data, hint_code, &mut exec_scopes), Ok(()));
check_dictionary![exec_scopes, 2, (5, 20)];
check_dict_ptr!(exec_scopes, 2, (2, 3));
}
#[test]
fn run_dict_update_default_valid_no_change() {
let hint_code = "# Verify dict pointer and prev value.\ndict_tracker = __dict_manager.get_tracker(ids.dict_ptr)\ncurrent_value = dict_tracker.data[ids.key]\nassert current_value == ids.prev_value, \\\n f'Wrong previous value in dict. Got {ids.prev_value}, expected {current_value}.'\n\n# Update value.\ndict_tracker.data[ids.key] = ids.new_value\ndict_tracker.current_ptr += ids.DictAccess.SIZE";
let mut vm = vm!();
vm.run_context.fp = 4;
let mut exec_scopes = ExecutionScopes::new();
dict_manager!(exec_scopes, 2, (5, 10));
vm.memory = memory![((1, 0), 5), ((1, 1), 10), ((1, 2), 10), ((1, 3), (2, 0))];
add_segments!(vm, 1);
let ids_data = ids_data!["key", "prev_value", "new_value", "dict_ptr"];
assert_eq!(run_hint!(vm, ids_data, hint_code, &mut exec_scopes), Ok(()));
check_dictionary![exec_scopes, 2, (5, 10)];
check_dict_ptr!(exec_scopes, 2, (2, 3));
}
#[test]
fn run_dict_update_default_invalid_wrong_prev_key() {
let hint_code = "# Verify dict pointer and prev value.\ndict_tracker = __dict_manager.get_tracker(ids.dict_ptr)\ncurrent_value = dict_tracker.data[ids.key]\nassert current_value == ids.prev_value, \\\n f'Wrong previous value in dict. Got {ids.prev_value}, expected {current_value}.'\n\n# Update value.\ndict_tracker.data[ids.key] = ids.new_value\ndict_tracker.current_ptr += ids.DictAccess.SIZE";
let mut vm = vm!();
vm.run_context.fp = 4;
let mut exec_scopes = ExecutionScopes::new();
dict_manager!(exec_scopes, 2, (5, 10));
vm.memory = memory![((1, 0), 5), ((1, 1), 11), ((1, 2), 10), ((1, 3), (2, 0))];
add_segments!(vm, 1);
let ids_data = ids_data!["key", "prev_value", "new_value", "dict_ptr"];
assert_eq!(
run_hint!(vm, ids_data, hint_code, &mut exec_scopes),
Err(HintError::WrongPrevValue(
MaybeRelocatable::from(11),
MaybeRelocatable::from(10),
MaybeRelocatable::from(5)
))
);
}
#[test]
fn run_dict_update_default_invalid_wrong_key() {
let hint_code = "# Verify dict pointer and prev value.\ndict_tracker = __dict_manager.get_tracker(ids.dict_ptr)\ncurrent_value = dict_tracker.data[ids.key]\nassert current_value == ids.prev_value, \\\n f'Wrong previous value in dict. Got {ids.prev_value}, expected {current_value}.'\n\n# Update value.\ndict_tracker.data[ids.key] = ids.new_value\ndict_tracker.current_ptr += ids.DictAccess.SIZE";
let mut vm = vm!();
vm.run_context.fp = 4;
let mut exec_scopes = ExecutionScopes::new();
dict_manager_default!(exec_scopes, 2, 17, (5, 10));
vm.memory = memory![((1, 0), 6), ((1, 1), 10), ((1, 2), 10), ((1, 3), (2, 0))];
add_segments!(vm, 1);
let ids_data = ids_data!["key", "prev_value", "new_value", "dict_ptr"];
assert_eq!(
run_hint!(vm, ids_data, hint_code, &mut exec_scopes),
Err(HintError::WrongPrevValue(
MaybeRelocatable::from(10),
MaybeRelocatable::from(17),
MaybeRelocatable::from(6)
))
);
}
#[test]
fn run_dict_update_default_valid_no_key_prev_value_equals_default() {
let hint_code = "# Verify dict pointer and prev value.\ndict_tracker = __dict_manager.get_tracker(ids.dict_ptr)\ncurrent_value = dict_tracker.data[ids.key]\nassert current_value == ids.prev_value, \\\n f'Wrong previous value in dict. Got {ids.prev_value}, expected {current_value}.'\n\n# Update value.\ndict_tracker.data[ids.key] = ids.new_value\ndict_tracker.current_ptr += ids.DictAccess.SIZE";
let mut vm = vm!();
vm.run_context.fp = 4;
let mut exec_scopes = ExecutionScopes::new();
dict_manager_default!(exec_scopes, 2, 17);
vm.memory = memory![((1, 0), 5), ((1, 1), 17), ((1, 2), 20), ((1, 3), (2, 0))];
add_segments!(vm, 1);
let ids_data = ids_data!["key", "prev_value", "new_value", "dict_ptr"];
assert_eq!(run_hint!(vm, ids_data, hint_code, &mut exec_scopes), Ok(()));
check_dictionary![exec_scopes, 2, (5, 20)];
check_dict_ptr!(exec_scopes, 2, (2, 3));
}
#[test]
fn run_dict_squash_copy_dict_valid_empty_dict() {
let hint_code = "# Prepare arguments for dict_new. In particular, the same dictionary values should be copied\n# to the new (squashed) dictionary.\nvm_enter_scope({\n # Make __dict_manager accessible.\n '__dict_manager': __dict_manager,\n # Create a copy of the dict, in case it changes in the future.\n 'initial_dict': dict(__dict_manager.get_dict(ids.dict_accesses_end)),\n})";
let mut vm = vm!();
vm.run_context.fp = 1;
vm.memory = memory![((1, 0), (2, 0))];
add_segments!(vm, 1);
let ids_data = ids_data!["dict_accesses_end"];
let mut exec_scopes = ExecutionScopes::new();
dict_manager!(exec_scopes, 2);
assert_eq!(run_hint!(vm, ids_data, hint_code, &mut exec_scopes), Ok(()));
assert_eq!(exec_scopes.data.len(), 2);
let variables = exec_scopes.get_local_variables().unwrap();
assert_eq!(variables.len(), 2); assert_eq!(
variables
.get("initial_dict")
.unwrap()
.downcast_ref::<HashMap<MaybeRelocatable, MaybeRelocatable>>(),
Some(&HashMap::<MaybeRelocatable, MaybeRelocatable>::new())
);
}
#[test]
fn run_dict_squash_copy_dict_valid_non_empty_dict() {
let hint_code = "# Prepare arguments for dict_new. In particular, the same dictionary values should be copied\n# to the new (squashed) dictionary.\nvm_enter_scope({\n # Make __dict_manager accessible.\n '__dict_manager': __dict_manager,\n # Create a copy of the dict, in case it changes in the future.\n 'initial_dict': dict(__dict_manager.get_dict(ids.dict_accesses_end)),\n})";
let mut vm = vm!();
vm.run_context.fp = 1;
vm.memory = memory![((1, 0), (2, 0))];
add_segments!(vm, 1);
let ids_data = ids_data!["dict_accesses_end"];
let mut exec_scopes = ExecutionScopes::new();
dict_manager!(exec_scopes, 2, (1, 2), (3, 4), (5, 6));
assert_eq!(run_hint!(vm, ids_data, hint_code, &mut exec_scopes), Ok(()));
assert_eq!(exec_scopes.data.len(), 2);
let variables = exec_scopes.get_local_variables().unwrap();
assert_eq!(variables.len(), 2); assert_eq!(
variables
.get("initial_dict")
.unwrap()
.downcast_ref::<HashMap<MaybeRelocatable, MaybeRelocatable>>(),
Some(&HashMap::from([
(MaybeRelocatable::from(1), MaybeRelocatable::from(2)),
(MaybeRelocatable::from(3), MaybeRelocatable::from(4)),
(MaybeRelocatable::from(5), MaybeRelocatable::from(6))
]))
);
}
#[test]
fn run_dict_squash_copy_dict_invalid_no_dict() {
let hint_code = "# Prepare arguments for dict_new. In particular, the same dictionary values should be copied\n# to the new (squashed) dictionary.\nvm_enter_scope({\n # Make __dict_manager accessible.\n '__dict_manager': __dict_manager,\n # Create a copy of the dict, in case it changes in the future.\n 'initial_dict': dict(__dict_manager.get_dict(ids.dict_accesses_end)),\n})";
let mut vm = vm!();
vm.run_context.fp = 1;
let dict_manager = DictManager::new();
let mut exec_scopes = scope![("dict_manager", Rc::new(RefCell::new(dict_manager)))];
vm.memory = memory![((1, 0), (2, 0))];
add_segments!(vm, 1);
let ids_data = ids_data!["dict_accesses_end"];
assert_eq!(
run_hint!(vm, ids_data, hint_code, &mut exec_scopes),
Err(HintError::NoDictTracker(2))
);
}
#[test]
fn run_dict_squash_update_ptr_no_tracker() {
let hint_code = "# Update the DictTracker's current_ptr to point to the end of the squashed dict.\n__dict_manager.get_tracker(ids.squashed_dict_start).current_ptr = \\\n ids.squashed_dict_end.address_";
let mut vm = vm!();
vm.run_context.fp = 2;
let dict_manager = DictManager::new();
let mut exec_scopes = scope![("dict_manager", Rc::new(RefCell::new(dict_manager)))];
vm.memory = memory![((1, 0), (2, 0)), ((1, 1), (2, 3))];
add_segments!(vm, 1);
let ids_data = ids_data!["squashed_dict_start", "squashed_dict_end"];
assert_eq!(
run_hint!(vm, ids_data, hint_code, &mut exec_scopes),
Err(HintError::NoDictTracker(2))
);
}
#[test]
fn run_dict_squash_update_ptr_valid() {
let hint_code = "# Update the DictTracker's current_ptr to point to the end of the squashed dict.\n__dict_manager.get_tracker(ids.squashed_dict_start).current_ptr = \\\n ids.squashed_dict_end.address_";
let mut vm = vm!();
vm.run_context.fp = 2;
let mut exec_scopes = ExecutionScopes::new();
dict_manager![exec_scopes, 2, (1, 2)];
vm.memory = memory![((1, 0), (2, 0)), ((1, 1), (2, 3))];
add_segments!(vm, 1);
let ids_data = ids_data!["squashed_dict_start", "squashed_dict_end"];
assert_eq!(run_hint!(vm, ids_data, hint_code, &mut exec_scopes), Ok(()));
check_dict_ptr!(exec_scopes, 2, (2, 3));
}
#[test]
fn run_dict_squash_update_ptr_mismatched_dict_ptr() {
let hint_code = "# Update the DictTracker's current_ptr to point to the end of the squashed dict.\n__dict_manager.get_tracker(ids.squashed_dict_start).current_ptr = \\\n ids.squashed_dict_end.address_";
let mut vm = vm!();
vm.run_context.fp = 2;
let mut exec_scopes = ExecutionScopes::new();
dict_manager!(exec_scopes, 2, (1, 2));
vm.memory = memory![((1, 0), (2, 3)), ((1, 1), (2, 6))];
add_segments!(vm, 1);
let ids_data = ids_data!["squashed_dict_start", "squashed_dict_end"];
assert_eq!(
run_hint!(vm, ids_data, hint_code, &mut exec_scopes),
Err(HintError::MismatchedDictPtr(
relocatable!(2, 0),
relocatable!(2, 3)
))
);
}
#[test]
fn run_dict_write_valid_relocatable_new_value() {
let mut vm = vm!();
vm.run_context.fp = 3;
let mut exec_scopes = ExecutionScopes::new();
dict_manager_default!(&mut exec_scopes, 2, 2);
vm.memory = memory![((1, 0), 5), ((1, 1), (1, 7)), ((1, 2), (2, 0))];
add_segments!(vm, 1);
let ids_data = ids_data!["key", "new_value", "dict_ptr"];
assert_eq!(
run_hint!(vm, ids_data, hint_code::DICT_WRITE, &mut exec_scopes),
Ok(())
);
let expected_dict = Dictionary::DefaultDictionary {
dict: HashMap::from([(MaybeRelocatable::from(5), MaybeRelocatable::from((1, 7)))]),
default_value: MaybeRelocatable::from(2),
};
let expeced_dict_tracker = DictTracker {
data: expected_dict,
current_ptr: Relocatable::from((2, 3)),
};
assert_eq!(
exec_scopes
.get_dict_manager()
.unwrap()
.borrow()
.trackers
.get(&2),
Some(&expeced_dict_tracker)
);
}
}