Skip to content

Commit

Permalink
Merge pull request #6 from lambdaclass/upd
Browse files Browse the repository at this point in the history
Add more libfuncs and update to 2.8.0
  • Loading branch information
edg-l authored Sep 3, 2024
2 parents 5707a7b + 14a2c18 commit b5c08e3
Show file tree
Hide file tree
Showing 22 changed files with 1,006 additions and 53 deletions.
15 changes: 9 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ version = "0.1.0"
edition = "2021"

[dependencies]
cairo-lang-sierra = "=2.7.1"
cairo-lang-utils = "=2.7.1"
cairo-lang-sierra = "2.8.0"
cairo-lang-sierra-ap-change = "2.8.0"
cairo-lang-sierra-gas = "2.8.0"
cairo-lang-utils = "2.8.0"
clap = { version = "4.5.16", features = ["derive"] }
k256 = "0.13.3"
keccak = "0.1.5"
Expand All @@ -14,19 +16,20 @@ num-traits = "0.2.19"
p256 = "0.13.2"
rand = "0.8.5"
sec1 = { version = "0.7.3", features = ["std"] }
serde = { version = "1.0.208", features = ["derive"] }
serde_json = "1.0.125"
serde = { version = "1.0.209", features = ["derive"] }
serde_json = "1.0.127"
sha2 = { version = "0.10.8", features = ["compress"] }
smallvec = "1.13.2"
starknet-crypto = "0.7.1"
starknet-curve = "0.5.0"
starknet-types-core = "0.1.2"
thiserror = "1.0.63"
tracing = "0.1.40"
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }

[dev-dependencies]
cairo-lang-compiler = "=2.7.0"
cairo-lang-starknet = "=2.7.0"
cairo-lang-compiler = "2.8.0"
cairo-lang-starknet = "2.8.0"

# On dev optimize dependencies a bit so it's not as slow.
[profile.dev.package."*"]
Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

UNAME := $(shell uname)

CAIRO_2_VERSION=2.7.0
SCARB_VERSION = 2.7.0
CAIRO_2_VERSION=2.8.0
SCARB_VERSION = 2.8.0

needs-cairo2:
ifeq ($(wildcard ./cairo2/.),)
Expand Down
2 changes: 1 addition & 1 deletion rust-toolchain.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[toolchain]
channel = "1.80.0"
channel = "1.80.1"
components = ["rustfmt", "clippy"]
profile = "minimal"
411 changes: 411 additions & 0 deletions src/debug.rs

Large diffs are not rendered by default.

215 changes: 215 additions & 0 deletions src/gas.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
use cairo_lang_sierra::{
extensions::gas::CostTokenType,
ids::FunctionId,
program::{Program, StatementIdx},
};
use cairo_lang_sierra_ap_change::{
ap_change_info::ApChangeInfo, calc_ap_changes,
compute::calc_ap_changes as linear_calc_ap_changes, ApChangeError,
};
use cairo_lang_sierra_gas::{
compute_postcost_info, compute_precost_info, gas_info::GasInfo, CostError,
};
use cairo_lang_utils::{casts::IntoOrPanic, ordered_hash_map::OrderedHashMap};

/// Holds global gas info.
#[derive(Debug, Default, PartialEq, Eq)]
pub struct GasMetadata {
pub ap_change_info: ApChangeInfo,
pub gas_info: GasInfo,
}

/// Configuration for metadata computation.
#[derive(Debug, Clone)]
pub struct MetadataComputationConfig {
pub function_set_costs: OrderedHashMap<FunctionId, OrderedHashMap<CostTokenType, i32>>,
// ignored, its always used
pub linear_gas_solver: bool,
pub linear_ap_change_solver: bool,
}

impl Default for MetadataComputationConfig {
fn default() -> Self {
Self {
function_set_costs: Default::default(),
linear_gas_solver: true,
linear_ap_change_solver: true,
}
}
}

/// Error for metadata calculations.
#[derive(Debug, thiserror::Error, Eq, PartialEq)]
pub enum GasMetadataError {
#[error(transparent)]
ApChangeError(#[from] ApChangeError),
#[error(transparent)]
CostError(#[from] CostError),
#[error("Not enough gas to run the operation. Required: {:?}, Available: {:?}.", gas.0, gas.1)]
NotEnoughGas { gas: Box<(u128, u128)> },
}

impl GasMetadata {
pub fn new(
sierra_program: &Program,
config: Option<MetadataComputationConfig>,
) -> Result<GasMetadata, GasMetadataError> {
if let Some(metadata_config) = config {
calc_metadata(sierra_program, metadata_config)
} else {
calc_metadata_ap_change_only(sierra_program)
}
}

/// Returns the initial value for the gas counter.
/// If `available_gas` is None returns 0.
pub fn get_initial_available_gas(
&self,
func: &FunctionId,
available_gas: Option<u128>,
) -> Result<u128, GasMetadataError> {
let Some(available_gas) = available_gas else {
return Ok(0);
};

// In case we don't have any costs - it means no gas equations were solved (and we are in
// the case of no gas checking enabled) - so the gas builtin is irrelevant, and we
// can return any value.
let Some(required_gas) = self.initial_required_gas(func) else {
return Ok(0);
};

available_gas
.checked_sub(required_gas)
.ok_or(GasMetadataError::NotEnoughGas {
gas: Box::new((required_gas, available_gas)),
})
}

pub fn initial_required_gas(&self, func: &FunctionId) -> Option<u128> {
if self.gas_info.function_costs.is_empty() {
return None;
}
Some(
self.gas_info.function_costs[func]
.iter()
.map(|(token_type, val)| val.into_or_panic::<usize>() * token_gas_cost(*token_type))
.sum::<usize>() as u128,
)
}

pub fn get_gas_cost_for_statement(&self, idx: StatementIdx) -> Option<u128> {
let mut cost = None;
for cost_type in CostTokenType::iter_casm_tokens() {
if let Some(amount) =
self.get_gas_cost_for_statement_and_cost_token_type(idx, *cost_type)
{
*cost.get_or_insert(0) += amount * token_gas_cost(*cost_type) as u128;
}
}
cost
}

pub fn get_gas_cost_for_statement_and_cost_token_type(
&self,
idx: StatementIdx,
cost_type: CostTokenType,
) -> Option<u128> {
self.gas_info
.variable_values
.get(&(idx, cost_type))
.copied()
.map(|x| {
x.try_into()
.expect("gas cost couldn't be converted to u128, should never happen")
})
}
}

impl Clone for GasMetadata {
fn clone(&self) -> Self {
Self {
ap_change_info: ApChangeInfo {
variable_values: self.ap_change_info.variable_values.clone(),
function_ap_change: self.ap_change_info.function_ap_change.clone(),
},
gas_info: GasInfo {
variable_values: self.gas_info.variable_values.clone(),
function_costs: self.gas_info.function_costs.clone(),
},
}
}
}

/// Methods from https://github.com/starkware-libs/cairo/blob/fbdbbe4c42a6808eccbff8436078f73d0710c772/crates/cairo-lang-sierra-to-casm/src/metadata.rs#L71

/// Calculates the metadata for a Sierra program, with ap change info only.
fn calc_metadata_ap_change_only(program: &Program) -> Result<GasMetadata, GasMetadataError> {
Ok(GasMetadata {
ap_change_info: calc_ap_changes(program, |_, _| 0)?,
gas_info: GasInfo {
variable_values: Default::default(),
function_costs: Default::default(),
},
})
}

/// Calculates the metadata for a Sierra program.
///
/// `no_eq_solver` uses a linear-time algorithm for calculating the gas, instead of solving
/// equations.
fn calc_metadata(
program: &Program,
config: MetadataComputationConfig,
) -> Result<GasMetadata, GasMetadataError> {
let pre_gas_info = compute_precost_info(program)?;

let ap_change_info = if config.linear_ap_change_solver {
linear_calc_ap_changes
} else {
calc_ap_changes
}(program, |idx, token_type| {
pre_gas_info.variable_values[&(idx, token_type)] as usize
})?;

let enforced_function_costs: OrderedHashMap<FunctionId, i32> = config
.function_set_costs
.iter()
.map(|(func, costs)| (func.clone(), costs[&CostTokenType::Const]))
.collect();
let post_gas_info = compute_postcost_info(
program,
&|idx| {
ap_change_info
.variable_values
.get(idx)
.copied()
.unwrap_or_default()
},
&pre_gas_info,
&enforced_function_costs,
)?;

Ok(GasMetadata {
ap_change_info,
gas_info: pre_gas_info.combine(post_gas_info),
})
}

pub fn token_gas_cost(token_type: CostTokenType) -> usize {
match token_type {
CostTokenType::Const => 1,
CostTokenType::Step
| CostTokenType::Hole
| CostTokenType::RangeCheck
| CostTokenType::RangeCheck96 => {
panic!("Token type {:?} has no gas cost.", token_type)
}
CostTokenType::Pedersen => 4130,
CostTokenType::Poseidon => 500,
CostTokenType::Bitwise => 594,
CostTokenType::EcOp => 4166,
CostTokenType::AddMod => 234,
CostTokenType::MulMod => 616,
}
}
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ use cairo_lang_sierra::program::{GenFunction, Program, StatementIdx};

pub use self::{dump::*, value::*, vm::VirtualMachine};

mod debug;
mod dump;
mod gas;
pub mod starknet;
mod value;
mod vm;
Expand Down
10 changes: 6 additions & 4 deletions src/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,13 @@ impl Value {
type_id: &ConcreteTypeId,
) -> Self {
match registry.get_type(type_id).unwrap() {
CoreTypeConcrete::Uint8(_) => Value::U8(0),
CoreTypeConcrete::Uint32(_) => Value::U32(0),
_ => panic!("type {type_id} has no default value implementation"),
CoreTypeConcrete::Uint64(_) => Value::U64(0),
CoreTypeConcrete::Uint16(_) => Value::U16(0),
CoreTypeConcrete::Uint128(_) => Value::U128(0),
CoreTypeConcrete::Felt252(_) => Value::Felt(0.into()),
x => panic!("type {:?} has no default value implementation", x.info()),
}
}

Expand Down Expand Up @@ -147,14 +152,12 @@ impl Value {
CoreTypeConcrete::BuiltinCosts(_) => matches!(self, Self::Unit),
CoreTypeConcrete::Uint16(_) => matches!(self, Self::U16(_)),
CoreTypeConcrete::Uint64(_) => matches!(self, Self::U64(_)),
CoreTypeConcrete::Uint128(_) => matches!(self, Self::U128(_)),
CoreTypeConcrete::Uint128MulGuarantee(_) => matches!(self, Self::Unit),
CoreTypeConcrete::Sint16(_) => todo!(),
CoreTypeConcrete::Sint32(_) => todo!(),
CoreTypeConcrete::Sint64(_) => todo!(),
CoreTypeConcrete::Sint128(_) => todo!(),
CoreTypeConcrete::Nullable(info) => self.is(registry, &info.ty),
CoreTypeConcrete::RangeCheck96(_) => matches!(self, Self::Unit),
CoreTypeConcrete::Uninitialized(_) => todo!(),
CoreTypeConcrete::Felt252DictEntry(_) => todo!(),
CoreTypeConcrete::SquashedFelt252Dict(_) => todo!(),
Expand All @@ -170,7 +173,6 @@ impl Value {
StarkNetTypeConcrete::Secp256Point(_) => todo!(),
StarkNetTypeConcrete::Sha256StateHandle(_) => todo!(),
},
CoreTypeConcrete::BoundedInt(_) => matches!(self, Self::BoundedInt { .. }),
};

if !res {
Expand Down
Loading

0 comments on commit b5c08e3

Please sign in to comment.