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

Add more libfuncs and update to 2.8.0 #6

Merged
merged 16 commits into from
Sep 3, 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
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