Skip to content

Commit

Permalink
Implement SyscallHint storage_read (#68)
Browse files Browse the repository at this point in the history
* Implemnet struct ContractStorageState

* Add stuct StorageReadRequest

* Add stuct StorageReadResponse

* Implemet SyscallHandler::storage_read

* Add STORAGE_READ to execute_syscall_hint

* Add unfinished unit test

* Add unit test test_os_storage_read_hint_ok

* Add CachedState unit tests

* Add RcRefCell CarriedState.parent_state: Option<Rc<RefCell<CarriedState<T>>>>

* Modify State::get_contract_class

* Add get_storage test

* Add deploy_contract unit test

* Modify contract_hash type from vec<u8> to  [u8; 32]

* Modify SyscallHandler._storage_read return type to Result<Felt,..>

* Add State to BusinessLogicSyscallHandler

* Remove StateComplete

* Remove Storage generics in  InMemoryStateReader

* Implement Address.to_32_bytes()

* Add test

* Rename BusinessLogicSyscallHandler.starknet_storage to starknet_storage_state

* Check inserts in unit test

Co-authored-by: Pedro Fontana <pedro.fontana@lamdaclass.com>
  • Loading branch information
pefontana and Pedro Fontana authored Jan 26, 2023
1 parent 89a458a commit c70edaa
Show file tree
Hide file tree
Showing 12 changed files with 333 additions and 47 deletions.
2 changes: 1 addition & 1 deletion src/business_logic/execution/objects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ pub struct CallInfo {
pub(crate) execution_resources: ExecutionResources,
pub(crate) events: VecDeque<OrderedEvent>,
pub(crate) l2_to_l1_messages: VecDeque<OrderedL2ToL1Message>,
pub(crate) storage_read_values: VecDeque<u64>,
pub(crate) storage_read_values: VecDeque<Felt>,
pub(crate) accesed_storage_keys: VecDeque<[u8; 32]>,
pub(crate) internal_calls: Vec<CallInfo>,
}
Expand Down
14 changes: 7 additions & 7 deletions src/business_logic/fact_state/in_memory_state_reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,18 @@ use crate::{
use super::contract_state::{self, ContractState};

#[derive(Clone, Debug)]
pub(crate) struct InMemoryStateReader<S1: Storage, S2: Storage> {
pub(crate) struct InMemoryStateReader {
pub(crate) global_state_root: HashMap<Address, [u8; 32]>,
pub(crate) ffc: S1,
pub(crate) ffc: DictStorage,
pub(crate) contract_states: HashMap<Address, ContractState>,
pub(crate) contract_class_storage: S2,
pub(crate) contract_class_storage: DictStorage,
}

impl<S1: Storage, S2: Storage> InMemoryStateReader<S1, S2> {
impl InMemoryStateReader {
pub(crate) fn new(
global_state_root: HashMap<Address, [u8; 32]>,
ffc: S1,
contract_class_storage: S2,
ffc: DictStorage,
contract_class_storage: DictStorage,
) -> Self {
Self {
global_state_root,
Expand Down Expand Up @@ -57,7 +57,7 @@ impl<S1: Storage, S2: Storage> InMemoryStateReader<S1, S2> {
}
}

impl<S1: Storage, S2: Storage> StateReader for InMemoryStateReader<S1, S2> {
impl StateReader for InMemoryStateReader {
fn get_contract_class(&mut self, class_hash: &[u8; 32]) -> Result<ContractClass, StateError> {
let contract_class = self.contract_class_storage.get_contract_class(class_hash)?;
contract_class.validate()?;
Expand Down
43 changes: 43 additions & 0 deletions src/business_logic/state/contract_storage_state.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use std::collections::HashSet;

use felt::Felt;
use num_bigint::BigInt;

use crate::{core::errors::state_errors::StateError, utils::Address};

use super::state_api::{State, StateReader};

pub(crate) struct ContractStorageState<T: State + StateReader> {
pub(crate) state: T,
pub(crate) contract_address: Address,
/// Maintain all read request values in chronological order
pub(crate) read_values: Vec<Felt>,
pub(crate) accessed_keys: HashSet<[u8; 32]>,
}

impl<T: State + StateReader> ContractStorageState<T> {
pub(crate) fn new(state: T, contract_address: Address) -> Self {
Self {
state,
contract_address,
read_values: Vec::new(),
accessed_keys: HashSet::new(),
}
}

pub(crate) fn read(&mut self, address: &[u8; 32]) -> Result<&Felt, StateError> {
self.accessed_keys.insert(*address);
let value = self
.state
.get_storage_at(&(self.contract_address.clone(), *address))?;

self.read_values.push(value.clone());
Ok(value)
}

pub(crate) fn write(&mut self, address: &[u8; 32], value: Felt) {
self.accessed_keys.insert(*address);
self.state
.set_storage_at(&(self.contract_address.clone(), *address), value);
}
}
1 change: 1 addition & 0 deletions src/business_logic/state/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub(crate) mod cached_state;
pub(crate) mod contract_storage_state;
pub mod state_api;
pub mod state_api_objects;
pub(crate) mod state_cache;
Expand Down
6 changes: 6 additions & 0 deletions src/core/errors/syscall_handler_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ use cairo_rs::vm::errors::vm_errors::VirtualMachineError;
use felt::Felt;
use thiserror::Error;

use super::state_errors::StateError;

#[derive(Debug, PartialEq, Error)]
pub enum SyscallHandlerError {
#[error("Missing Member")]
Expand Down Expand Up @@ -34,6 +36,8 @@ pub enum SyscallHandlerError {
ExpectedSendMessageToL1,
#[error("Expected GetBlockTimestampRequest")]
ExpectedGetBlockTimestampRequest,
#[error("Expected StorageReadRequest")]
ExpectedStorageReadRequest,
#[error("The deploy_from_zero field in the deploy system call must be 0 or 1, found: {0}")]
DeployFromZero(usize),
#[error("Hint not implemented")]
Expand Down Expand Up @@ -78,4 +82,6 @@ pub enum SyscallHandlerError {
ExpectedGetTxSignatureRequest,
#[error("Expected a ptr but received invalid data")]
InvalidTxInfoPtr,
#[error(transparent)]
StateError(#[from] StateError),
}
45 changes: 33 additions & 12 deletions src/core/syscalls/business_logic_syscall_handler.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
use std::cell::RefCell;
use std::collections::HashMap;
use std::ops::Deref;
use std::rc::Rc;

use super::syscall_handler::SyscallHandler;
use super::syscall_request::*;
use crate::business_logic::execution::objects::*;
use crate::business_logic::fact_state::in_memory_state_reader::InMemoryStateReader;
use crate::business_logic::fact_state::state::ExecutionResourcesManager;
use crate::business_logic::state::cached_state::CachedState;
use crate::business_logic::state::contract_storage_state::ContractStorageState;
use crate::business_logic::state::state_api::{State, StateReader};
use crate::business_logic::state::state_api_objects::BlockInfo;
use crate::core::errors::syscall_handler_errors::SyscallHandlerError;
use crate::definitions::general_config::StarknetGeneralConfig;
use crate::hash_utils::calculate_contract_address_from_hash;
use crate::services::api::contract_class::EntryPointType;
use crate::starknet_storage::dict_storage::DictStorage;
use crate::utils::*;
use cairo_rs::types::relocatable::{MaybeRelocatable, Relocatable};
use cairo_rs::vm::runners::cairo_runner::ExecutionResources;
Expand All @@ -22,7 +28,7 @@ use num_traits::{One, ToPrimitive, Zero};
//* BusinessLogicHandler implementation
//* -----------------------------------

pub struct BusinessLogicSyscallHandler {
pub struct BusinessLogicSyscallHandler<T: State + StateReader> {
pub(crate) tx_execution_context: TransactionExecutionContext,
/// Events emitted by the current contract call.
pub(crate) events: Vec<OrderedEvent>,
Expand All @@ -35,10 +41,12 @@ pub struct BusinessLogicSyscallHandler {
pub(crate) general_config: StarknetGeneralConfig,
pub(crate) tx_info_ptr: Option<MaybeRelocatable>,
pub(crate) block_info: BlockInfo,
pub(crate) state: T,
pub(crate) starknet_storage_state: ContractStorageState<T>,
}

impl BusinessLogicSyscallHandler {
pub fn new(block_info: BlockInfo) -> Self {
impl<T: State + StateReader + Clone> BusinessLogicSyscallHandler<T> {
pub fn new(block_info: BlockInfo, contract_address: Address, state: T) -> Self {
let syscalls = Vec::from([
"emit_event".to_string(),
"deploy".to_string(),
Expand Down Expand Up @@ -66,6 +74,8 @@ impl BusinessLogicSyscallHandler {
let l2_to_l1_messages = Vec::new();
let general_config = StarknetGeneralConfig::default();
let tx_info_ptr = None;
let starknet_storage_state =
ContractStorageState::new(state.clone(), contract_address.clone());

BusinessLogicSyscallHandler {
tx_execution_context,
Expand All @@ -78,6 +88,8 @@ impl BusinessLogicSyscallHandler {
general_config,
tx_info_ptr,
block_info,
state,
starknet_storage_state,
}
}

Expand All @@ -88,7 +100,7 @@ impl BusinessLogicSyscallHandler {
}
}

impl SyscallHandler for BusinessLogicSyscallHandler {
impl<T: State + StateReader + Clone> SyscallHandler for BusinessLogicSyscallHandler<T> {
fn emit_event(
&mut self,
vm: &VirtualMachine,
Expand Down Expand Up @@ -352,8 +364,11 @@ impl SyscallHandler for BusinessLogicSyscallHandler {
Ok(())
}

fn _storage_read(&mut self, _address: Address) -> Result<u64, SyscallHandlerError> {
todo!()
fn _storage_read(&mut self, address: Address) -> Result<Felt, SyscallHandlerError> {
Ok(self
.starknet_storage_state
.read(&address.to_32_bytes()?)?
.clone())
}
fn _storage_write(&mut self, _address: Address, _value: u64) {
todo!()
Expand All @@ -370,9 +385,15 @@ impl SyscallHandler for BusinessLogicSyscallHandler {
}
}

impl Default for BusinessLogicSyscallHandler {
impl Default for BusinessLogicSyscallHandler<CachedState<InMemoryStateReader>> {
fn default() -> Self {
Self::new(BlockInfo::default())
let cached_state = CachedState::new(
BlockInfo::default(),
InMemoryStateReader::new(HashMap::new(), DictStorage::new(), DictStorage::new()),
None,
);

Self::new(BlockInfo::default(), Address(0.into()), cached_state)
}
}

Expand Down Expand Up @@ -438,7 +459,7 @@ mod tests {
}

fn deploy_from_zero_error() {
let mut syscall = BusinessLogicSyscallHandler::new(BlockInfo::default());
let mut syscall = BusinessLogicSyscallHandler::default();
let mut vm = vm!();

add_segments!(vm, 2);
Expand All @@ -463,7 +484,7 @@ mod tests {

#[test]
fn can_allocate_segment() {
let mut syscall_handler = BusinessLogicSyscallHandler::new(BlockInfo::default());
let mut syscall_handler = BusinessLogicSyscallHandler::default();
let mut vm = vm!();
let data = vec![MaybeRelocatable::Int(7.into())];

Expand All @@ -477,7 +498,7 @@ mod tests {
}
#[test]
fn test_get_block_number() {
let mut syscall = BusinessLogicSyscallHandler::new(BlockInfo::default());
let mut syscall = BusinessLogicSyscallHandler::default();
let mut vm = vm!();

add_segments!(vm, 2);
Expand All @@ -496,7 +517,7 @@ mod tests {

#[test]
fn test_get_contract_address_ok() {
let mut syscall = BusinessLogicSyscallHandler::new(BlockInfo::default());
let mut syscall = BusinessLogicSyscallHandler::default();
let mut vm = vm!();

add_segments!(vm, 2);
Expand Down
1 change: 0 additions & 1 deletion src/core/syscalls/hint_code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ pub(crate) const GET_SEQUENCER_ADDRESS: &str =
pub(crate) const STORAGE_WRITE: &str =
"syscall_handler.storage_write(segments=segments, syscall_ptr=ids.syscall_ptr)";

#[allow(unused)] // TODO: remove after using.
pub(crate) const STORAGE_READ: &str =
"syscall_handler.storage_read(segments=segments, syscall_ptr=ids.syscall_ptr)";

Expand Down
22 changes: 12 additions & 10 deletions src/core/syscalls/os_syscall_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::utils::Address;
use cairo_rs::types::relocatable::{MaybeRelocatable, Relocatable};
use cairo_rs::vm::vm_core::VirtualMachine;
use cairo_rs::vm::vm_memory::memory_segments::MemorySegmentManager;
use felt::Felt;
use std::any::Any;
use std::collections::{HashMap, VecDeque};

Expand Down Expand Up @@ -41,7 +42,7 @@ pub(crate) struct OsSyscallHandler {
retdata_iterator: VecDeque<VecDeque<u64>>, //VEC<u64>
// An iterator to the read_values array which is consumed when the transaction
// code is executed.
execute_code_read_iterator: VecDeque<u64>, //u64
execute_code_read_iterator: VecDeque<Felt>,
// StarkNet storage members.
starknet_storage_by_address: HashMap<u64, OsSingleStarknetStorage>,
// A pointer to the Cairo TxInfo struct.
Expand Down Expand Up @@ -153,7 +154,7 @@ impl SyscallHandler for OsSyscallHandler {
}
}

fn _storage_read(&mut self, address: Address) -> Result<u64, SyscallHandlerError> {
fn _storage_read(&mut self, address: Address) -> Result<Felt, SyscallHandlerError> {
self.execute_code_read_iterator
.pop_front()
.ok_or(SyscallHandlerError::IteratorEmpty)
Expand Down Expand Up @@ -191,7 +192,7 @@ impl OsSyscallHandler {
call_stack: VecDeque<CallInfo>,
deployed_contracts_iterator: VecDeque<Address>,
retdata_iterator: VecDeque<VecDeque<u64>>,
execute_code_read_iterator: VecDeque<u64>,
execute_code_read_iterator: VecDeque<Felt>,
starknet_storage_by_address: HashMap<u64, OsSingleStarknetStorage>,
tx_info_ptr: Option<Relocatable>,
tx_execution_info: Option<TransactionExecutionInfo>,
Expand Down Expand Up @@ -353,6 +354,7 @@ mod tests {
use cairo_rs::vm::errors::vm_errors::VirtualMachineError;
use cairo_rs::vm::runners::cairo_runner::ExecutionResources;
use cairo_rs::vm::vm_core::VirtualMachine;
use felt::{Felt, NewFelt};
use std::any::Any;
use std::collections::{HashMap, VecDeque};

Expand Down Expand Up @@ -429,7 +431,7 @@ mod tests {
#[test]
fn end_tx_err_execute_code_read_iterator() {
let mut execute_code_read_iterator = VecDeque::new();
execute_code_read_iterator.push_back(12);
execute_code_read_iterator.push_back(Felt::new(12));
let mut handler = OsSyscallHandler {
execute_code_read_iterator,
..Default::default()
Expand Down Expand Up @@ -678,16 +680,16 @@ mod tests {
#[test]
fn storage_read() {
let mut execute_code_read_iterator = VecDeque::new();
execute_code_read_iterator.push_back(12);
execute_code_read_iterator.push_back(1444);
execute_code_read_iterator.push_back(Felt::new(12));
execute_code_read_iterator.push_back(Felt::new(1444));
let mut handler = OsSyscallHandler {
execute_code_read_iterator,
..Default::default()
};

let addr = Address(0.into());
assert_eq!(handler._storage_read(addr.clone()), Ok(12));
assert_eq!(handler._storage_read(addr.clone()), Ok(1444));
assert_eq!(handler._storage_read(addr.clone()), Ok(Felt::new(12)));
assert_eq!(handler._storage_read(addr.clone()), Ok(Felt::new(1444)));
assert_eq!(
handler._storage_read(addr),
Err(SyscallHandlerError::IteratorEmpty)
Expand All @@ -697,7 +699,7 @@ mod tests {
#[test]
fn storage_write() {
let mut execute_code_read_iterator = VecDeque::new();
execute_code_read_iterator.push_back(12);
execute_code_read_iterator.push_back(Felt::new(12));
let mut handler = OsSyscallHandler {
execute_code_read_iterator,
..Default::default()
Expand Down Expand Up @@ -743,7 +745,7 @@ mod tests {
#[test]
fn assert_iterators_exhausted_err_execute() {
let mut execute_code_read_iterator = VecDeque::new();
execute_code_read_iterator.push_back(12);
execute_code_read_iterator.push_back(Felt::new(12));
let handler = OsSyscallHandler {
execute_code_read_iterator,
..Default::default()
Expand Down
Loading

0 comments on commit c70edaa

Please sign in to comment.