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: compile sierra to casm in declare tx #234

Merged
merged 1 commit into from
Jun 25, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 4 additions & 0 deletions Cargo.lock

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

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ bincode = "1.3.3"
cairo-lang-sierra = "2.6.0"
cairo-lang-starknet-classes = "2.6.0"
cairo-lang-utils = "2.6.0"
cairo-vm = "0.9.2"
clap = "4.3.10"
colored = "2.1.0"
const_format = "0.2.30"
Expand All @@ -51,6 +52,7 @@ futures = "0.3.30"
hyper = { version = "0.14", features = ["client", "http1", "http2"] }
indexmap = "2.1.0"
itertools = "0.13.0"
num-bigint = { version = "0.4.5", default-features = false }
# TODO(YaelD, 28/5/2024): The special Papyrus version is needed in order to be aligned with the
# starknet-api version. This should be removed once we have a mono-repo.
papyrus_common = { git = "https://github.com/starkware-libs/papyrus.git", rev = "050e470f" }
Expand Down
2 changes: 2 additions & 0 deletions crates/gateway/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ testing = []
axum.workspace = true
blockifier.workspace = true
cairo-lang-starknet-classes.workspace = true
cairo-vm.workspace = true
hyper.workspace = true
papyrus_config.workspace = true
reqwest.workspace = true
serde.workspace = true
serde_json.workspace = true
starknet_api.workspace = true
starknet_mempool_types = { path = "../mempool_types", version = "0.0" }
starknet_sierra_compile = { path = "../starknet_sierra_compile", version = "0.0" }
thiserror.workspace = true
tokio.workspace = true
validator.workspace = true
Expand Down
8 changes: 8 additions & 0 deletions crates/gateway/src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use axum::http::StatusCode;
use axum::response::{IntoResponse, Response};
use blockifier::blockifier::stateful_validator::StatefulValidatorError;
use blockifier::execution::errors::ContractClassError;
use blockifier::state::errors::StateError;
use blockifier::transaction::errors::TransactionExecutionError;
use cairo_vm::types::errors::program_errors::ProgramError;
use starknet_api::block::BlockNumber;
use starknet_api::transaction::{Resource, ResourceBounds};
use starknet_api::StarknetApiError;
Expand All @@ -14,6 +16,12 @@ use crate::compiler_version::VersionIdError;
/// Errors directed towards the end-user, as a result of gateway requests.
#[derive(Debug, Error)]
pub enum GatewayError {
#[error(transparent)]
CompilationError(#[from] starknet_sierra_compile::compile::CompilationUtilError),
#[error(transparent)]
DeclaredContractClassError(#[from] ContractClassError),
#[error(transparent)]
DeclaredContractProgramError(#[from] ProgramError),
#[error("Internal server error: {0}")]
InternalServerError(#[from] JoinError),
#[error("Error sending message: {0}")]
Expand Down
49 changes: 46 additions & 3 deletions crates/gateway/src/gateway.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
use std::clone::Clone;
use std::net::SocketAddr;
use std::panic;
use std::sync::Arc;

use axum::extract::State;
use axum::routing::{get, post};
use axum::{Json, Router};
use starknet_api::rpc_transaction::RPCTransaction;
use blockifier::execution::contract_class::{ClassInfo, ContractClass, ContractClassV1};
use starknet_api::rpc_transaction::{RPCDeclareTransaction, RPCTransaction};
use starknet_api::transaction::TransactionHash;
use starknet_mempool_types::communication::SharedMempoolClient;
use starknet_mempool_types::mempool_types::{Account, MempoolInput};
use starknet_sierra_compile::compile::{compile_sierra_to_casm, CompilationUtilError};
use starknet_sierra_compile::utils::into_contract_class_for_compilation;

use crate::config::{GatewayConfig, GatewayNetworkConfig};
use crate::errors::{GatewayError, GatewayRunError};
Expand Down Expand Up @@ -116,11 +120,50 @@ fn process_tx(
// Perform stateless validations.
stateless_tx_validator.validate(&tx)?;

// TODO(Yael, 19/5/2024): pass the relevant class_info and deploy_account_hash.
let tx_hash = stateful_tx_validator.run_validate(state_reader_factory, &tx, None, None)?;
// Compile Sierra to Casm.
let optional_class_info = match &tx {
RPCTransaction::Declare(declare_tx) => Some(compile_contract_class(declare_tx)?),
_ => None,
};

// TODO(Yael, 19/5/2024): pass the relevant deploy_account_hash.
let tx_hash =
stateful_tx_validator.run_validate(state_reader_factory, &tx, optional_class_info, None)?;

// TODO(Arni): Add the Sierra and the Casm to the mempool input.
Ok(MempoolInput {
tx: external_tx_to_thin_tx(&tx, tx_hash),
account: Account { sender_address: get_sender_address(&tx), ..Default::default() },
})
}

/// Formats the contract class for compilation, compiles it, and returns the compiled contract class
/// wrapped in a [`ClassInfo`].
/// Assumes the contract class is of a Sierra program which is compiled to Casm.
fn compile_contract_class(declare_tx: &RPCDeclareTransaction) -> GatewayResult<ClassInfo> {
let RPCDeclareTransaction::V3(tx) = declare_tx;
let starknet_api_contract_class = &tx.contract_class;
let cairo_lang_contract_class =
into_contract_class_for_compilation(starknet_api_contract_class);

// Compile Sierra to Casm.
let catch_unwind_result =
panic::catch_unwind(|| compile_sierra_to_casm(cairo_lang_contract_class));
let casm_contract_class = match catch_unwind_result {
Ok(compilation_result) => compilation_result?,
Err(_) => {
// TODO(Arni): Log the panic.
return Err(GatewayError::CompilationError(CompilationUtilError::CompilationPanic));
}
};

// Convert Casm contract class to Starknet contract class directly.
let blockifier_contract_class =
ContractClass::V1(ContractClassV1::try_from(casm_contract_class)?);
let class_info = ClassInfo::new(
&blockifier_contract_class,
starknet_api_contract_class.sierra_program.len(),
starknet_api_contract_class.abi.len(),
)?;
Ok(class_info)
}
2 changes: 2 additions & 0 deletions crates/starknet_sierra_compile/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ workspace = true
cairo-lang-sierra.workspace = true
cairo-lang-starknet-classes.workspace = true
cairo-lang-utils.workspace = true
num-bigint.workspace = true
serde_json.workspace = true
serde.workspace = true
starknet_api.workspace = true
thiserror.workspace = true

[dev-dependencies]
Expand Down
2 changes: 2 additions & 0 deletions crates/starknet_sierra_compile/src/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ pub enum CompilationUtilError {
AllowedLibfuncsError(#[from] AllowedLibfuncsError),
#[error(transparent)]
StarknetSierraCompilationError(#[from] StarknetSierraCompilationError),
#[error("Compilation panicked")]
CompilationPanic,
}

/// This function may panic.
Expand Down
1 change: 1 addition & 0 deletions crates/starknet_sierra_compile/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! A lib for compiling Sierra into Casm.

pub mod compile;
pub mod utils;

#[cfg(any(feature = "testing", test))]
pub mod test_utils;
69 changes: 69 additions & 0 deletions crates/starknet_sierra_compile/src/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
use std::clone::Clone;

use cairo_lang_starknet_classes::contract_class::{
ContractClass as CairoLangContractClass, ContractEntryPoint as CairoLangContractEntryPoint,
ContractEntryPoints as CairoLangContractEntryPoints,
};
use cairo_lang_utils::bigint::BigUintAsHex;
use num_bigint::BigUint;
use starknet_api::hash::StarkFelt;
use starknet_api::rpc_transaction::{
ContractClass as StarknetApiContractClass, EntryPointByType as StarknetApiEntryPointByType,
};
use starknet_api::state::EntryPoint as StarknetApiEntryPoint;

/// Retruns a [`CairoLangContractClass`] struct ready for Sierra to Casm compilation. Note the `abi`
/// field is None as it is not relevant for the compilation.
pub fn into_contract_class_for_compilation(
starknet_api_contract_class: &StarknetApiContractClass,
) -> CairoLangContractClass {
let sierra_program = starknet_api_contract_class
.sierra_program
.iter()
.map(stark_felt_to_big_uint_as_hex)
.collect();
let entry_points_by_type =
into_cairo_lang_contract_entry_points(&starknet_api_contract_class.entry_points_by_type);

CairoLangContractClass {
sierra_program,
sierra_program_debug_info: None,
contract_class_version: starknet_api_contract_class.contract_class_version.clone(),
entry_points_by_type,
abi: None,
}
}

fn into_cairo_lang_contract_entry_points(
entry_points_by_type: &StarknetApiEntryPointByType,
) -> CairoLangContractEntryPoints {
let StarknetApiEntryPointByType { constructor, external, l1handler } = entry_points_by_type;
CairoLangContractEntryPoints {
external: into_cairo_lang_contract_entry_points_vec(external),
l1_handler: into_cairo_lang_contract_entry_points_vec(l1handler),
constructor: into_cairo_lang_contract_entry_points_vec(constructor),
}
}

fn into_cairo_lang_contract_entry_points_vec(
entry_points: &[StarknetApiEntryPoint],
) -> Vec<CairoLangContractEntryPoint> {
entry_points.iter().map(into_cairo_lang_contract_entry_point).collect()
}

fn into_cairo_lang_contract_entry_point(
entry_point: &StarknetApiEntryPoint,
) -> CairoLangContractEntryPoint {
CairoLangContractEntryPoint {
selector: stark_felt_to_big_uint(&entry_point.selector.0),
function_idx: entry_point.function_idx.0,
}
}

fn stark_felt_to_big_uint_as_hex(stark_felt: &StarkFelt) -> BigUintAsHex {
BigUintAsHex { value: stark_felt_to_big_uint(stark_felt) }
}

fn stark_felt_to_big_uint(stark_felt: &StarkFelt) -> BigUint {
BigUint::from_bytes_be(stark_felt.bytes())
}
Loading