Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement blockifier State and StateReader traits #107

Merged
merged 9 commits into from
Sep 21, 2023
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
180 changes: 71 additions & 109 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,14 @@ chrono = { version = "0.4.26", features = ["serde"] }
ctor = "0.2.4"
dotenv = "0.15.0"
eyre = "0.6.8"
lazy_static = "1.4.0"
regex = "1.9.3"
reqwest = { version = "0.11.20", features = ["gzip"] }
rstest = "0.18.1"
thiserror = "1.0.47"
tokio = { version = "1.21.2", features = ["macros"] }
tracing = "0.1.37"
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
walkdir = "2.3.3"
zip = "0.6.6"

Expand Down
4 changes: 2 additions & 2 deletions crates/ef-testing/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,12 @@ regex = { workspace = true }
reqwest = { workspace = true, optional = true }
rstest = { workspace = true }
tokio = { workspace = true }
tracing = "0.1.37"
tracing = { workspace = true }
walkdir = { workspace = true }
zip = { workspace = true, optional = true }

[dev-dependencies]
tracing-subscriber = "0.3.17"
tracing-subscriber = { workspace = true }

[features]
ef-tests = []
Expand Down
6 changes: 5 additions & 1 deletion crates/sequencer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,12 @@ license.workspace = true

[dependencies]
# Starknet
blockifier = { git = "https://github.com/starkware-libs/blockifier.git", tag = "v0.3.0-rc0" }
blockifier = { package = "blockifier", git = "https://github.com/starkware-libs/blockifier.git", tag = "v0.3.0-rc0" }
greged93 marked this conversation as resolved.
Show resolved Hide resolved
starknet_api = { workspace = true }
starknet = { workspace = true }

# Other
rustc-hash = "1.1.0"

[dev-dependencies]
lazy_static = { workspace = true }
1 change: 0 additions & 1 deletion crates/sequencer/src/config.rs

This file was deleted.

29 changes: 29 additions & 0 deletions crates/sequencer/src/constants.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#[cfg(test)]
pub mod test_constants {
use starknet::core::types::FieldElement;
use starknet_api::{
block::{BlockNumber, BlockTimestamp},
core::{ClassHash, CompiledClassHash, ContractAddress, PatriciaKey},
hash::StarkFelt,
};

lazy_static::lazy_static! {
pub static ref TEST_CONTRACT_ADDRESS: ContractAddress = ContractAddress(*ONE_PATRICIA);
pub static ref TEST_CONTRACT_ACCOUNT: ContractAddress = ContractAddress(*TWO_PATRICIA);
pub static ref TEST_ADDRESS: StarkFelt = *ONE_FELT;
pub static ref TEST_CONTRACT: StarkFelt = *TWO_FELT;
pub static ref SENDER_ADDRESS: FieldElement = FieldElement::from(2u8);
pub static ref SEQUENCER_ADDRESS: ContractAddress = ContractAddress(TryInto::<PatriciaKey>::try_into(StarkFelt::from(1234u16)).unwrap());
pub static ref FEE_TOKEN_ADDRESS: ContractAddress = ContractAddress(TryInto::<PatriciaKey>::try_into(StarkFelt::from(12345u16)).unwrap());

pub static ref ONE_FELT: StarkFelt = StarkFelt::from(1u8);
pub static ref TWO_FELT: StarkFelt = StarkFelt::from(2u8);
pub static ref ONE_PATRICIA: PatriciaKey = TryInto::<PatriciaKey>::try_into(*ONE_FELT).unwrap();
pub static ref TWO_PATRICIA: PatriciaKey = TryInto::<PatriciaKey>::try_into(*TWO_FELT).unwrap();
pub static ref ONE_HASH: ClassHash = ClassHash(*ONE_FELT);
greged93 marked this conversation as resolved.
Show resolved Hide resolved
pub static ref TWO_HASH: ClassHash = ClassHash(*TWO_FELT);
greged93 marked this conversation as resolved.
Show resolved Hide resolved
pub static ref ONE_COMPILED_HASH: CompiledClassHash = CompiledClassHash(*ONE_FELT);
greged93 marked this conversation as resolved.
Show resolved Hide resolved
pub static ref ONE_BLOCK_NUMBER: BlockNumber = BlockNumber(1);
pub static ref ONE_BLOCK_TIMESTAMP: BlockTimestamp = BlockTimestamp(1);
}
}
2 changes: 1 addition & 1 deletion crates/sequencer/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
pub mod config;
pub mod constants;
pub mod sequencer;
pub mod state;
22 changes: 16 additions & 6 deletions crates/sequencer/src/sequencer.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,27 @@
use crate::config::SequencerConfig;
use blockifier::block_context::BlockContext;
use blockifier::state::state_api::{State, StateReader};
Eikix marked this conversation as resolved.
Show resolved Hide resolved

/// Sequencer is the main struct of the sequencer crate.
/// Using a trait bound for the state allows for better
/// speed, as the type of the state is known at compile time.
pub struct Sequencer<S: State + StateReader> {
pub config: SequencerConfig,
/// We bound S such that a mutable reference to S (&'a mut S)
/// must implement State and StateReader. The `for` keyword
/// indicates that the bound must hold for any lifetime 'a.
/// For more details, check out https://doc.rust-lang.org/nomicon/hrtb.html
pub struct Sequencer<S>
where
for<'a> &'a mut S: State + StateReader,
{
Eikix marked this conversation as resolved.
Show resolved Hide resolved
pub context: BlockContext,
pub state: S,
}

impl<S: State + StateReader> Sequencer<S> {
impl<S> Sequencer<S>
where
for<'a> &'a mut S: State + StateReader,
Eikix marked this conversation as resolved.
Show resolved Hide resolved
{
/// Creates a new Sequencer instance.
pub fn new(config: SequencerConfig, state: S) -> Self {
Self { config, state }
pub fn new(context: BlockContext, state: S) -> Self {
Self { context, state }
}
}
239 changes: 234 additions & 5 deletions crates/sequencer/src/state.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
use blockifier::state::cached_state::CommitmentStateDiff;
use blockifier::state::errors::StateError;
use blockifier::state::state_api::{
State as BlockifierState, StateReader as BlockifierStateReader, StateResult,
};
use blockifier::{
execution::contract_class::ContractClass, state::cached_state::ContractStorageKey,
};
use rustc_hash::FxHashMap;
use starknet_api::core::CompiledClassHash;
use starknet_api::state::StorageKey;
use starknet_api::{
core::{ClassHash, ContractAddress, Nonce},
hash::StarkFelt,
Expand All @@ -16,9 +22,232 @@ use starknet_api::{
/// See [rustc-hash](https://crates.io/crates/rustc-hash) for more information.
#[derive(Default)]
pub struct State {
pub classes: FxHashMap<ClassHash, ContractClass>,
pub compiled_classes: FxHashMap<ClassHash, CompiledClassHash>,
pub contracts: FxHashMap<ContractAddress, ClassHash>,
pub storage: FxHashMap<ContractStorageKey, StarkFelt>,
pub nonces: FxHashMap<ContractAddress, Nonce>,
classes: FxHashMap<ClassHash, ContractClass>,
compiled_class_hashes: FxHashMap<ClassHash, CompiledClassHash>,
contracts: FxHashMap<ContractAddress, ClassHash>,
storage: FxHashMap<ContractStorageKey, StarkFelt>,
nonces: FxHashMap<ContractAddress, Nonce>,
}

/// State implementation for the sequencer. We use a mutable reference to the state
/// because this is what will be available during the implementation of the execution.
impl BlockifierState for &mut State {
fn set_storage_at(
&mut self,
contract_address: ContractAddress,
key: StorageKey,
value: StarkFelt,
) {
self.storage.insert((contract_address, key), value);
}

fn increment_nonce(&mut self, contract_address: ContractAddress) -> StateResult<()> {
let current_nonce = self
.nonces
.get(&contract_address)
.cloned()
.unwrap_or_default();

let mut current_nonce: u64 = current_nonce.0.try_into()?;
current_nonce += 1;

self.nonces
.insert(contract_address, Nonce(StarkFelt::from(current_nonce)));

Ok(())
}

fn set_class_hash_at(
&mut self,
contract_address: ContractAddress,
class_hash: ClassHash,
) -> StateResult<()> {
self.contracts.insert(contract_address, class_hash);
Ok(())
}

fn set_contract_class(
&mut self,
class_hash: &ClassHash,
contract_class: ContractClass,
) -> StateResult<()> {
self.classes.insert(class_hash.to_owned(), contract_class);
Ok(())
}

fn set_compiled_class_hash(
&mut self,
class_hash: ClassHash,
compiled_class_hash: CompiledClassHash,
) -> StateResult<()> {
self.compiled_class_hashes
.insert(class_hash, compiled_class_hash);
Ok(())
}

fn to_state_diff(&self) -> CommitmentStateDiff {
unreachable!("to_state_diff should not be called in the sequencer")
}
}

impl BlockifierStateReader for &mut State {
fn get_storage_at(
Eikix marked this conversation as resolved.
Show resolved Hide resolved
&mut self,
contract_address: ContractAddress,
key: StorageKey,
) -> StateResult<StarkFelt> {
Ok(self
.storage
.get(&(contract_address, key))
.cloned()
.unwrap_or_default())
}

fn get_nonce_at(&mut self, contract_address: ContractAddress) -> StateResult<Nonce> {
Eikix marked this conversation as resolved.
Show resolved Hide resolved
Ok(self
.nonces
.get(&contract_address)
.cloned()
.unwrap_or_default())
}

fn get_class_hash_at(&mut self, contract_address: ContractAddress) -> StateResult<ClassHash> {
Ok(self
.contracts
.get(&contract_address)
.cloned()
.unwrap_or_default())
}

/// # Errors
///
/// If the compiled class is not declared.
fn get_compiled_contract_class(
&mut self,
class_hash: &ClassHash,
) -> StateResult<ContractClass> {
self.classes
.get(class_hash)
.cloned()
.ok_or_else(|| StateError::UndeclaredClassHash(class_hash.to_owned()))
}

/// # Errors
///
/// If the compiled class hash is not declared.
fn get_compiled_class_hash(&mut self, class_hash: ClassHash) -> StateResult<CompiledClassHash> {
self.compiled_class_hashes
.get(&class_hash)
.cloned()
.ok_or_else(|| StateError::UndeclaredClassHash(class_hash))
}
}

#[cfg(test)]
mod tests {
use blockifier::execution::contract_class::ContractClassV0;

use crate::constants::test_constants::{
ONE_COMPILED_HASH, ONE_FELT, ONE_HASH, ONE_PATRICIA, TEST_CONTRACT_ADDRESS,
};

use super::*;

#[test]
fn test_storage() {
// Given
let mut state = &mut State::default();

// When
state.set_storage_at(*TEST_CONTRACT_ADDRESS, StorageKey(*ONE_PATRICIA), *ONE_FELT);

// Then
let expected = *ONE_FELT;
let actual = state
.get_storage_at(*TEST_CONTRACT_ADDRESS, StorageKey(*ONE_PATRICIA))
.unwrap();
assert_eq!(expected, actual);
}

#[test]
fn test_nonce() {
// Given
let mut state = &mut State::default();

// When
state.increment_nonce(*TEST_CONTRACT_ADDRESS).unwrap();

// Then
let expected = Nonce(*ONE_FELT);
let actual = state.get_nonce_at(*TEST_CONTRACT_ADDRESS).unwrap();
assert_eq!(expected, actual);
}

#[test]
fn test_class_hash() {
// Given
let mut state = &mut State::default();

// When
state
.set_class_hash_at(*TEST_CONTRACT_ADDRESS, *ONE_HASH)
.unwrap();

// Then
let expected = *ONE_HASH;
let actual = state.get_class_hash_at(*TEST_CONTRACT_ADDRESS).unwrap();
assert_eq!(expected, actual);
}

#[test]
fn test_contract_class() {
// Given
let mut state = &mut State::default();

// When
state
.set_contract_class(&ONE_HASH, ContractClass::V0(ContractClassV0::default()))
.unwrap();

// Then
let expected = ContractClass::V0(ContractClassV0::default());
let actual = state.get_compiled_contract_class(&ONE_HASH).unwrap();
assert_eq!(expected, actual);
}

#[test]
#[should_panic(expected = "UndeclaredClassHash")]
fn test_uninitialized_contract_class() {
// Given
let mut state = &mut State::default();

// When
state.get_compiled_contract_class(&ONE_HASH).unwrap();
}

#[test]
fn test_compiled_class_hash() {
// Given
let mut state = &mut State::default();

// When
state
.set_compiled_class_hash(*ONE_HASH, *ONE_COMPILED_HASH)
.unwrap();

// Then
let expected = *ONE_COMPILED_HASH;
let actual = state.get_compiled_class_hash(*ONE_HASH).unwrap();
assert_eq!(expected, actual);
}

#[test]
#[should_panic(expected = "UndeclaredClassHash")]
fn test_uninitialized_compiled_class_hash() {
// Given
let mut state = &mut State::default();

// When
state.get_compiled_class_hash(*ONE_HASH).unwrap();
Eikix marked this conversation as resolved.
Show resolved Hide resolved
}
}
Loading