Skip to content

Commit

Permalink
chore(cairo_native): add the compilation of native to the flow
Browse files Browse the repository at this point in the history
  • Loading branch information
meship-starkware committed Dec 24, 2024
1 parent 535775d commit e1a7869
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 24 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions crates/blockifier/src/state/contract_class_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,16 @@ impl ContractClassManager {
self.contract_caches.set_casm(class_hash, compiled_class);
}

#[cfg(feature = "cairo_native")]
pub fn run_cairo_native(&self) -> bool {
self.config.run_cairo_native
}

#[cfg(feature = "cairo_native")]
pub fn wait_on_native_compilation(&self) -> bool {
self.config.wait_on_native_compilation
}

/// Clear the contract caches.
pub fn clear(&mut self) {
self.contract_caches.clear();
Expand Down
7 changes: 7 additions & 0 deletions crates/blockifier/src/state/global_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ pub enum CachedCasm {
WithoutSierra(RunnableCompiledClass),
WithSierra(RunnableCompiledClass, Arc<SierraContractClass>),
}
impl CachedCasm {
pub fn to_runnable_casm(&self) -> RunnableCompiledClass {
match self {
CachedCasm::WithoutSierra(casm) | CachedCasm::WithSierra(casm, _) => casm.clone(),
}
}
}

#[cfg(feature = "cairo_native")]
#[derive(Debug, Clone)]
Expand Down
2 changes: 1 addition & 1 deletion crates/native_blockifier/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ license-file.workspace = true
description = "A Bridge between the Rust blockifier crate and Python."

[features]
cairo_native = ["blockifier/cairo_native"]
cairo_native = ["blockifier/cairo_native", "papyrus_state_reader/cairo_native"]
# Required for `cargo test` to work with Pyo3.
# On Python, make sure to compile this with the extension-module feature enabled.
# https://pyo3.rs/v0.19.1/faq#i-cant-run-cargo-test-or-i-cant-build-in-a-cargo-workspace-im-having-linker-issues-like-symbol-not-found-or-undefined-reference-to-_pyexc_systemerror
Expand Down
4 changes: 4 additions & 0 deletions crates/papyrus_state_reader/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@ edition.workspace = true
repository.workspace = true
license.workspace = true

[features]
cairo_native = ["blockifier/cairo_native"]

[lints]
workspace = true

[dependencies]
blockifier.workspace = true
log.workspace = true
papyrus_storage.workspace = true
starknet-types-core.workspace = true
starknet_api.workspace = true
Expand Down
149 changes: 126 additions & 23 deletions crates/papyrus_state_reader/src/papyrus_state.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
use std::sync::Arc;

use blockifier::execution::contract_class::{
CompiledClassV0,
CompiledClassV1,
RunnableCompiledClass,
};
use blockifier::state::contract_class_manager::ContractClassManager;
use blockifier::state::errors::{couple_casm_and_sierra, StateError};
#[cfg(feature = "cairo_native")]
use blockifier::state::global_cache::CachedCairoNative;
use blockifier::state::global_cache::CachedCasm;
use blockifier::state::state_api::{StateReader, StateResult};
use papyrus_storage::compiled_class::CasmStorageReader;
Expand All @@ -22,6 +26,7 @@ use starknet_types_core::felt::Felt;
mod test;

type RawPapyrusReader<'env> = papyrus_storage::StorageTxn<'env, RO>;

pub struct PapyrusReader {
storage_reader: StorageReader,
latest_block: BlockNumber,
Expand All @@ -43,12 +48,9 @@ impl PapyrusReader {
.map_err(|error| StateError::StateReadError(error.to_string()))
}

/// Returns a V1 contract if found, or a V0 contract if a V1 contract is not
/// found, or an `Error` otherwise.
fn get_compiled_class_inner(
&self,
class_hash: ClassHash,
) -> StateResult<RunnableCompiledClass> {
/// Returns a CachedCasm with Sierra if V1 contract is found, or a CachedCasm without Sierra if
/// a V1 contract is not found, or an `Error` otherwise.
fn get_compiled_class_inner(&self, class_hash: ClassHash) -> StateResult<CachedCasm> {
let state_number = StateNumber(self.latest_block);
let class_declaration_block_number = self
.reader()?
Expand All @@ -69,11 +71,11 @@ impl PapyrusReader {
database is inconsistent.",
);
let sierra_version = SierraVersion::extract_from_program(&sierra.sierra_program)?;
let runnable_compiled = RunnableCompiledClass::V1(CompiledClassV1::try_from((
let runnable_casm = RunnableCompiledClass::V1(CompiledClassV1::try_from((
casm_compiled_class,
sierra_version,
))?);
return Ok(runnable_compiled);
return Ok(CachedCasm::WithSierra(runnable_casm, Arc::new(sierra)));
}

let v0_compiled_class = self
Expand All @@ -83,12 +85,59 @@ impl PapyrusReader {
.map_err(|err| StateError::StateReadError(err.to_string()))?;

match v0_compiled_class {
Some(starknet_api_contract_class) => Ok(RunnableCompiledClass::V0(
CompiledClassV0::try_from(starknet_api_contract_class)?,
)),
Some(starknet_api_contract_class) => {
let runnable_casm = RunnableCompiledClass::V0(CompiledClassV0::try_from(
starknet_api_contract_class,
)?);
Ok(CachedCasm::WithoutSierra(runnable_casm))
}
None => Err(StateError::UndeclaredClassHash(class_hash)),
}
}

// Handles `get_compiled_class` when cairo native is turned off.
// Returns casm from cache if exists, otherwise fetches it from state.
fn get_compiled_class_non_native_flow(
&self,
class_hash: ClassHash,
) -> StateResult<RunnableCompiledClass> {
match self.contract_class_manager.get_casm(&class_hash) {
Some(contract_class) => Ok(contract_class.to_runnable_casm()),
None => {
let runnable_casm_from_db =
self.get_compiled_class_inner(class_hash)?.to_runnable_casm();
self.contract_class_manager
.set_casm(class_hash, CachedCasm::WithoutSierra(runnable_casm_from_db.clone()));
Ok(runnable_casm_from_db)
}
}
}

#[cfg(feature = "cairo_native")]
// Handles `get_compiled_class` under the assumption that native compilation has finished.
// Returns the native compiled class if the compilation succeeded and the runnable casm upon
// failure.
fn get_compiled_class_after_waiting_on_native_compilation(
&self,
class_hash: ClassHash,
casm: RunnableCompiledClass,
) -> RunnableCompiledClass {
assert!(
self.contract_class_manager.wait_on_native_compilation(),
"this function should only be called when the waiting on native compilation flag is \
on."
);
let cached_native = self
.contract_class_manager
.get_native(&class_hash)
.expect("Should have native in cache in sync compilation flow.");
match cached_native {
CachedCairoNative::Compiled(compiled_native) => {
RunnableCompiledClass::from(compiled_native)
}
CachedCairoNative::CompilationFailed => casm,
}
}
}

// Currently unused - will soon replace the same `impl` for `PapyrusStateReader`.
Expand Down Expand Up @@ -133,20 +182,74 @@ impl StateReader for PapyrusReader {

fn get_compiled_class(&self, class_hash: ClassHash) -> StateResult<RunnableCompiledClass> {
// Assumption: the global cache is cleared upon reverted blocks.
let cached_casm = self.contract_class_manager.get_casm(&class_hash);

match cached_casm {
None => {
let compiled_class_from_db = self.get_compiled_class_inner(class_hash)?;
self.contract_class_manager.set_casm(
class_hash,
CachedCasm::WithoutSierra(compiled_class_from_db.clone()),
);
Ok(compiled_class_from_db)
#[cfg(not(feature = "cairo_native"))]
return self.get_compiled_class_non_native_flow(class_hash);

#[cfg(feature = "cairo_native")]
{
if !self.contract_class_manager.run_cairo_native() {
// Cairo native is disabled - use the non-Cairo-native flow.
return self.get_compiled_class_non_native_flow(class_hash);
}
Some(CachedCasm::WithoutSierra(casm)) => Ok(casm),
Some(CachedCasm::WithSierra(_, _)) => {
todo!("Add this flow when Sierra to Native compilation is added to PapyrusReader.")

// Try fetching native from cache.
if let Some(cached_native) = self.contract_class_manager.get_native(&class_hash) {
match cached_native {
CachedCairoNative::Compiled(compiled_native) => {
return Ok(RunnableCompiledClass::from(compiled_native));
}
CachedCairoNative::CompilationFailed => {
// The compilation previously failed. Make no further compilation attempts.
// Use the non-Cairo-native flow.
return self.get_compiled_class_non_native_flow(class_hash);
}
}
};

// Native not found in cache. Get the `CachedCasm` - if not in cache, fetch it from
// state and cache it.
let cached_casm = match self.contract_class_manager.get_casm(&class_hash) {
Some(cached_casm) => cached_casm,
None => {
let cached_casm = self.get_compiled_class_inner(class_hash)?;
self.contract_class_manager.set_casm(class_hash, cached_casm.clone());
cached_casm
}
};

// If the fetched casm includes a Sierra, send a compilation request.
// Return the casm.
// NOTE: We assume that whenever the fetched casm does not include a Sierra, compilation
// to native is not required.
match cached_casm {
CachedCasm::WithSierra(runnable_casm, sierra) => {
if let RunnableCompiledClass::V1(casm_v1) = runnable_casm.clone() {
self.contract_class_manager.send_compilation_request((
class_hash,
sierra.clone(),
casm_v1.clone(),
));
if self.contract_class_manager.wait_on_native_compilation() {
// With this config, sending a compilation request blocks the sender
// until compilation completes. Retry fetching Native from cache.
return Ok(self
.get_compiled_class_after_waiting_on_native_compilation(
class_hash,
runnable_casm,
));
}
} else {
log::warn!(
"A Sierra file was saved in cache for a Cairo0 contract - class hash \
{class_hash}. This is probably a bug as no Sierra file exists for a \
Cairo0 contract."
);
}

Ok(runnable_casm)
}
CachedCasm::WithoutSierra(runnable_casm) => Ok(runnable_casm),
}
}
}
Expand Down

0 comments on commit e1a7869

Please sign in to comment.