Skip to content
Open
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
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,10 @@ with-debug-utils = []
with-mem-tracing = []
with-libfunc-profiling = []
with-segfault-catcher = []
with-trace-dump = ["dep:sierra-emu"]
with-trace-dump = ["sierra-emu"]
# Enables the `ContractExecutor::Emu` variant for dispatching contract execution
# through the sierra-emu interpreter.
sierra-emu = ["dep:sierra-emu"]
testing = ["dep:cairo-lang-compiler", "dep:cairo-lang-filesystem"]

[dependencies]
Expand Down
26 changes: 26 additions & 0 deletions debug_utils/sierra-emu/src/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,32 @@ impl VirtualMachine {

ContractExecutionResult::from_state(&last?)
}

/// One-shot convenience over [`VirtualMachine::new_starknet`],
/// [`VirtualMachine::call_contract`] and [`VirtualMachine::run`]: build a
/// Starknet VM for `program`, call the entry point identified by `selector`
/// and run it to completion.
///
/// Returns `None` when execution never produced a final state.
#[allow(clippy::too_many_arguments)]
pub fn run_contract<I>(
program: Arc<Program>,
entry_points: &ContractEntryPoints,
sierra_version: VersionId,
selector: Felt,
initial_gas: u64,
calldata: I,
builtin_costs: Option<BuiltinCosts>,
syscall_handler: &mut impl StarknetSyscallHandler,
) -> Option<ContractExecutionResult>
where
I: IntoIterator<Item = Felt>,
I::IntoIter: ExactSizeIterator,
{
let mut vm = Self::new_starknet(program, entry_points, sierra_version);
vm.call_contract(selector, initial_gas, calldata, builtin_costs);
vm.run(syscall_handler)
}
}

#[derive(Clone, Debug)]
Expand Down
4 changes: 4 additions & 0 deletions src/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
//! This module provides methods to execute the programs, either via JIT or compiled ahead
//! of time. It also provides a cache to avoid recompiling previously compiled programs.

#[cfg(feature = "sierra-emu")]
pub use self::emu_contract_executor::EmuContractExecutor;
pub use self::{aot::AotNativeExecutor, contract::AotContractExecutor, jit::JitNativeExecutor};
use crate::{
arch::{AbiArgument, ValueWithInfoWrapper},
Expand Down Expand Up @@ -39,6 +41,8 @@ use std::{alloc::Layout, arch::global_asm, ptr::NonNull};

mod aot;
mod contract;
#[cfg(feature = "sierra-emu")]
mod emu_contract_executor;
mod jit;

#[cfg(target_arch = "aarch64")]
Expand Down
82 changes: 82 additions & 0 deletions src/executor/emu_contract_executor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
//! A sierra-emu-backed contract executor exposing the same `run` shape as
//! [`AotContractExecutor::run`](crate::executor::AotContractExecutor::run), so a caller
//! can swap the two behind a feature flag without changing call-site code.
//!
//! Both executors share the same `H: StarknetSyscallHandler` -- sierra-emu and
//! cairo-native re-export the trait from `cairo-native-syscalls`, so no adapter is
//! needed.

use cairo_lang_sierra::program::Program;
use cairo_lang_starknet_classes::compiler_version::VersionId;
use cairo_lang_starknet_classes::contract_class::ContractEntryPoints;
use starknet_types_core::felt::Felt;
use std::sync::Arc;

use crate::error::{Error, Result};
use crate::execution_result::ContractExecutionResult;
use crate::starknet::StarknetSyscallHandler;
use crate::utils::BuiltinCosts;

/// Runs contract entry points through the sierra-emu interpreter.
///
/// Holds the program + entry points + sierra version triple that
/// `sierra_emu::VirtualMachine::run_contract` requires; the `Arc<Program>` is shared
/// across invocations rather than cloned per call.
#[derive(Debug, Clone)]
pub struct EmuContractExecutor {
pub program: Arc<Program>,
pub entry_points: ContractEntryPoints,
pub sierra_version: VersionId,
}

impl EmuContractExecutor {
/// Run the contract entry point identified by `selector`.
///
/// Mirrors [`AotContractExecutor::run`](crate::executor::AotContractExecutor::run) so
/// the two executor types are interchangeable at the call site.
pub fn run(
&self,
selector: Felt,
args: &[Felt],
gas: u64,
builtin_costs: Option<BuiltinCosts>,
mut syscall_handler: impl StarknetSyscallHandler,
) -> Result<ContractExecutionResult> {
// `run_contract` returns `None` when the VM never produced a final state --
// propagate as an error rather than aborting the host.
let result = sierra_emu::VirtualMachine::run_contract(
Arc::clone(&self.program),
&self.entry_points,
self.sierra_version,
selector,
gas,
args.to_vec(),
builtin_costs.map(convert_builtin_costs),
&mut syscall_handler,
)
.ok_or_else(|| {
Error::UnexpectedValue("sierra-emu VM produced no final state".to_string())
})?;

Ok(ContractExecutionResult {
remaining_gas: result.remaining_gas,
failure_flag: result.failure_flag,
return_values: result.return_values,
error_msg: result.error_msg,
builtin_stats: Default::default(),
})
}
}

fn convert_builtin_costs(builtin_costs: BuiltinCosts) -> sierra_emu::BuiltinCosts {
sierra_emu::BuiltinCosts {
r#const: builtin_costs.r#const,
pedersen: builtin_costs.pedersen,
bitwise: builtin_costs.bitwise,
ecop: builtin_costs.ecop,
poseidon: builtin_costs.poseidon,
add_mod: builtin_costs.add_mod,
mul_mod: builtin_costs.mul_mod,
blake: builtin_costs.blake,
}
}
Loading