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

Update native juno for sw/main rebase #2167

Open
wants to merge 13 commits into
base: native2.8.x-blockifier
Choose a base branch
from
Open
44 changes: 14 additions & 30 deletions vm/rust/Cargo.lock

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

8 changes: 4 additions & 4 deletions vm/rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ edition = "2021"
[dependencies]
serde = "1.0.171"
serde_json = { version = "1.0.96", features = ["raw_value"] }
blockifier = { git = "https://github.com/NethermindEth/sequencer", branch = "native2.8.x" }
starknet_api = { git = "https://github.com/NethermindEth/sequencer", branch = "native2.8.x" }
blockifier = { git = "https://github.com/NethermindEth/sequencer", rev = "de05be2f904cf23cfc25eeaa68bf89546298577d" }
starknet_api = { git = "https://github.com/NethermindEth/sequencer", rev = "de05be2f904cf23cfc25eeaa68bf89546298577d" }
cairo-lang-sierra = "2.8.0"
cairo-lang-starknet = "2.8.0"
cairo-lang-starknet-classes = "2.8.0"
Expand All @@ -18,7 +18,7 @@ starknet-types-core = { version = "0.1.5", features = [
"prime-bigint",
"serde",
] }
cairo-native = { git = "https://github.com/lambdaclass/cairo_native", rev = "4355357697e9ab57ab88ae3a4282aac61455619e" }
cairo-native = { git = "https://github.com/lambdaclass/cairo_native", rev = "a478e89b749bf0b596a7e63afd14e834c08a84e3" }
cairo-vm = "1.0.0"
indexmap = "2.1.0"
cached = "0.46.1"
Expand All @@ -30,7 +30,7 @@ libloading = "0.8.5"
thiserror = "1.0.63"
ciborium = "0.2.2"

# Trace block dependencies
# Trace block dependencies
starknet-core = "0.11.1"
starknet-providers = "0.11.0"
tokio = { version = "1.38.1", features = ["rt", "macros"] }
Expand Down
106 changes: 11 additions & 95 deletions vm/rust/src/juno_state_reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,15 @@ use blockifier::{
state::state_api::{StateReader, StateResult},
};
use cached::{Cached, SizedCache};
use cairo_lang_sierra::{program::Program, program_registry::ProgramRegistry};
use cairo_native::{
context::NativeContext, error::Error as NativeError, executor::AotNativeExecutor,
metadata::gas::GasMetadata, module::NativeModule,
};
use libloading::Library;
use cairo_native::executor::contract::ContractExecutor;
use cairo_native::OptLevel;
use once_cell::sync::Lazy;
use serde::Deserialize;
use starknet_api::core::{ClassHash, CompiledClassHash, ContractAddress, Nonce};
use starknet_api::state::StorageKey;
use starknet_types_core::felt::Felt;
use std::cell::RefCell;
use std::sync::Arc;
use std::{
ffi::{c_char, c_uchar, c_void, CStr},
fs,
Expand Down Expand Up @@ -185,6 +182,7 @@ impl StateReader for JunoStateReader {
/// Returns the contract class of the given class hash.
fn get_compiled_contract_class(&self, class_hash: ClassHash) -> StateResult<ContractClass> {
println!("Juno State Reader(Rust): calling `get_compiled_contract_class` with class hash: {class_hash}");

if let Some(cached_class) = CLASS_CACHE.lock().unwrap().cache_get(&class_hash) {
// skip the cache if it comes from a height higher than ours. Class might be undefined on the height
// that we are reading from right now.
Expand Down Expand Up @@ -311,68 +309,16 @@ fn native_try_from_json_string(
raw_contract_class: &str,
library_output_path: &PathBuf,
) -> Result<NativeContractClassV1, Box<dyn std::error::Error>> {
fn compile_and_load(
sierra_program: Program,
library_output_path: &PathBuf,
) -> Result<AotNativeExecutor, Box<dyn std::error::Error>> {
let native_context = NativeContext::new();
let native_module = native_context.compile(&sierra_program)?;

persist_from_native_module(native_module, &sierra_program, library_output_path)
}

let sierra_contract_class: cairo_lang_starknet_classes::contract_class::ContractClass =
serde_json::from_str(raw_contract_class)?;

// todo(rodro): we are having two instances of a sierra program, one it's object form
// and another in its felt encoded form. This can be avoided by either:
// 1. Having access to the encoding/decoding functions
// 2. Refactoring the code on the Cairo mono-repo

let sierra_program = sierra_contract_class.extract_sierra_program()?;

// todo(xrvdg) lift this match out of the function once we do not need sierra_program anymore
let executor = match load_compiled_contract(&sierra_program, library_output_path) {
Some(executor) => {
executor.or_else(|_err| compile_and_load(sierra_program, library_output_path))
}
None => compile_and_load(sierra_program, library_output_path),
}?;

Ok(NativeContractClassV1::new(executor, sierra_contract_class)?)
}

/// Load a contract that is already compiled.
///
/// Returns None if the contract does not exist at the output_path.
///
/// To compile and load a contract use [persist_from_native_module] instead.
fn load_compiled_contract(
sierra_program: &Program,
library_output_path: &PathBuf,
) -> Option<Result<AotNativeExecutor, Box<dyn std::error::Error>>> {
fn load(
sierra_program: &Program,
library_output_path: &PathBuf,
) -> Result<AotNativeExecutor, Box<dyn std::error::Error>> {
let has_gas_builtin = sierra_program
.type_declarations
.iter()
.any(|decl| decl.long_id.generic_id.0.as_str() == "GasBuiltin");
let config = has_gas_builtin.then_some(Default::default());
let gas_metadata = GasMetadata::new(sierra_program, config)?;
let program_registry = ProgramRegistry::new(sierra_program)?;
let library = unsafe { Library::new(library_output_path)? };
Ok(AotNativeExecutor::new(
library,
program_registry,
gas_metadata,
))
}

library_output_path
.is_file()
.then_some(load(sierra_program, library_output_path))
let executor = ContractExecutor::load(library_output_path).or_else(|_| {
let executor = ContractExecutor::new(&sierra_program, OptLevel::Default)?;
executor.save(library_output_path)?;
Ok::<ContractExecutor, Box<dyn std::error::Error>>(executor)
})?;
let contract_executor = NativeContractClassV1::new(Arc::new(executor), sierra_contract_class)?;
Ok(contract_executor)
}

// todo(xrvdg) once [class_info_from_json_str] is part of JunoStateReader
Expand Down Expand Up @@ -403,33 +349,3 @@ fn generate_library_path(class_hash: ClassHash) -> PathBuf {
path.push(class_hash.to_string().trim_start_matches("0x"));
path
}

/// Compiles and load contract
///
/// Modelled after [AotNativeExecutor::from_native_module].
/// Needs a sierra_program to workaround limitations of NativeModule
fn persist_from_native_module(
mut native_module: NativeModule,
sierra_program: &Program,
library_output_path: &PathBuf,
) -> Result<AotNativeExecutor, Box<dyn std::error::Error>> {
let object_data = cairo_native::module_to_object(native_module.module(), Default::default())
.map_err(|err| NativeError::LLVMCompileError(err.to_string()))?; // cairo native didn't include a from instance

cairo_native::object_to_shared_lib(&object_data, library_output_path)?;

let gas_metadata = native_module
.remove_metadata()
.expect("native_module should have set gas_metadata");

// Recreate the program registry as it can't be moved out of native module.
let program_registry = ProgramRegistry::new(sierra_program)?;

let library = unsafe { Library::new(library_output_path)? };

Ok(AotNativeExecutor::new(
library,
program_registry,
gas_metadata,
))
}
28 changes: 16 additions & 12 deletions vm/rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ pub extern "C" fn cairoVMCall(

let mut state = CachedState::new(reader);
let mut resources = ExecutionResources::default();
let context = EntryPointExecutionContext::new_invoke(
let mut context = EntryPointExecutionContext::new_invoke(
Arc::new(TransactionContext {
block_context: build_block_context(
&mut state,
Expand All @@ -171,12 +171,8 @@ pub extern "C" fn cairoVMCall(
}),
false,
);
if let Err(e) = context {
report_error(reader_handle, e.to_string().as_str(), -1);
return;
}

match entry_point.execute(&mut state, &mut resources, &mut context.unwrap()) {
match entry_point.execute(&mut state, &mut resources, &mut context) {
Err(e) => report_error(reader_handle, e.to_string().as_str(), -1),
Ok(t) => {
for data in t.execution.retdata.0 {
Expand Down Expand Up @@ -339,7 +335,7 @@ fn cairo_vm_execute(
};

if let Some(path) = JUNO_RECORD_DIR.clone() {
let mut args_path: PathBuf = path.into();
let mut args_path: PathBuf = path;
args_path.push(format!("{}.args.cbor", block_info.block_number));

let file_args = std::fs::File::create(args_path).unwrap();
Expand Down Expand Up @@ -401,7 +397,7 @@ fn cairo_vm_execute(
}

if let Some(path) = JUNO_RECORD_DIR.clone() {
let mut state_path: PathBuf = path.into();
let mut state_path: PathBuf = path;
state_path.push(format!("{}.state.cbor", block_info.block_number));

let state_file = File::create(state_path).unwrap();
Expand Down Expand Up @@ -461,9 +457,17 @@ pub fn execute_transaction<S: StateReader>(
let minimal_l1_gas_amount_vector: Option<GasVector>;
let res = match txn {
Transaction::AccountTransaction(t) => {
let tx_context = block_context.to_tx_context(&t);
fee_type = t.fee_type();
minimal_l1_gas_amount_vector =
Some(gas_usage::estimate_minimal_gas_vector(block_context, &t).unwrap());
minimal_l1_gas_amount_vector = Some(
gas_usage::estimate_minimal_gas_vector(
block_context,
&t,
&tx_context.get_gas_vector_computation_mode(),
)
.unwrap(),
);

t.execute(txn_state, block_context, charge_fee, validate)
}
Transaction::L1HandlerTransaction(t) => {
Expand Down Expand Up @@ -619,11 +623,11 @@ fn build_block_context(
gas_to_nzu128(block_info.data_gas_price_wei),
gas_to_nzu128(block_info.data_gas_price_fri),
VersionedConstants::latest_constants()
.l1_to_l2_gas_price_conversion(felt_to_u128(block_info.gas_price_wei))
.convert_l1_to_l2_gas(felt_to_u128(block_info.gas_price_wei))
.try_into()
.expect("gas price wei is zero :O"),
VersionedConstants::latest_constants()
.l1_to_l2_gas_price_conversion(felt_to_u128(block_info.gas_price_fri))
.convert_l1_to_l2_gas(felt_to_u128(block_info.gas_price_fri))
.try_into()
.expect("gas price fri is zero :O"),
),
Expand Down