Skip to content

Commit

Permalink
feat: compile sierra to casm in declare tx (#234)
Browse files Browse the repository at this point in the history
  • Loading branch information
ArniStarkware authored Jun 25, 2024
1 parent 8df3617 commit ccd75ea
Show file tree
Hide file tree
Showing 9 changed files with 136 additions and 3 deletions.
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())
}

0 comments on commit ccd75ea

Please sign in to comment.