use std::collections::HashMap;
use apollo_sizeof::SizeOf;
use serde::{Deserialize, Serialize};
use starknet_types_core::felt::Felt;
use strum::EnumIter;
use crate::block::{GasPrice, GasPriceVector, NonzeroGasPrice};
use crate::transaction::fields::{Fee, Resource, Tip};
#[cfg_attr(
any(test, feature = "testing"),
derive(
derive_more::Sum,
derive_more::Div,
derive_more::SubAssign,
derive_more::Sub,
derive_more::Add,
derive_more::AddAssign,
)
)]
#[derive(
derive_more::Display,
Clone,
Copy,
Debug,
Default,
Eq,
PartialEq,
PartialOrd,
Ord,
Serialize,
Deserialize,
Hash,
SizeOf,
)]
pub struct GasAmount(pub u64);
impl From<GasAmount> for Felt {
fn from(gas_amount: GasAmount) -> Self {
Self::from(gas_amount.0)
}
}
macro_rules! impl_from_uint_for_gas_amount {
($($uint:ty),*) => {
$(
impl From<$uint> for GasAmount {
fn from(value: $uint) -> Self {
Self(u64::from(value))
}
}
)*
};
}
impl_from_uint_for_gas_amount!(u8, u16, u32, u64);
impl GasAmount {
pub const ZERO: Self = Self(0);
pub const MAX: Self = Self(u64::MAX);
pub fn checked_add(self, rhs: Self) -> Option<Self> {
self.0.checked_add(rhs.0).map(Self)
}
pub fn checked_add_panic_on_overflow(self, added_gas: GasAmount) -> GasAmount {
self.checked_add(added_gas).unwrap_or_else(|| {
panic!(
"Addition overflow while adding gas. current gas: {self}, try to add
gas: {added_gas}.",
)
})
}
pub fn checked_sub(self, rhs: Self) -> Option<Self> {
self.0.checked_sub(rhs.0).map(Self)
}
pub const fn nonzero_saturating_mul(self, rhs: NonzeroGasPrice) -> Fee {
rhs.saturating_mul(self)
}
pub const fn saturating_mul(self, rhs: GasPrice) -> Fee {
rhs.saturating_mul(self)
}
pub fn checked_mul(self, rhs: GasPrice) -> Option<Fee> {
rhs.checked_mul(self)
}
pub fn checked_factor_mul(self, factor: u64) -> Option<Self> {
self.0.checked_mul(factor).map(Self)
}
pub fn checked_factor_div(self, factor: u64) -> Option<Self> {
self.0.checked_div(factor).map(Self)
}
}
#[cfg_attr(
any(test, feature = "testing"),
derive(derive_more::Add, derive_more::Sum, derive_more::AddAssign)
)]
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
pub struct GasVector {
pub l1_gas: GasAmount,
pub l1_data_gas: GasAmount,
#[serde(default)]
pub l2_gas: GasAmount,
}
impl GasVector {
pub const ZERO: GasVector =
GasVector { l1_gas: GasAmount(0), l1_data_gas: GasAmount(0), l2_gas: GasAmount(0) };
pub fn from_l1_gas(l1_gas: GasAmount) -> Self {
Self { l1_gas, ..Default::default() }
}
pub fn from_l1_data_gas(l1_data_gas: GasAmount) -> Self {
Self { l1_data_gas, ..Default::default() }
}
pub fn from_l2_gas(l2_gas: GasAmount) -> Self {
Self { l2_gas, ..Default::default() }
}
pub fn checked_add(self, rhs: Self) -> Option<Self> {
match (
self.l1_gas.checked_add(rhs.l1_gas),
self.l1_data_gas.checked_add(rhs.l1_data_gas),
self.l2_gas.checked_add(rhs.l2_gas),
) {
(Some(l1_gas), Some(l1_data_gas), Some(l2_gas)) => {
Some(Self { l1_gas, l1_data_gas, l2_gas })
}
_ => None,
}
}
pub fn checked_scalar_mul(self, factor: u64) -> Option<Self> {
match (
self.l1_gas.checked_factor_mul(factor),
self.l1_data_gas.checked_factor_mul(factor),
self.l2_gas.checked_factor_mul(factor),
) {
(Some(l1_gas), Some(l1_data_gas), Some(l2_gas)) => {
Some(Self { l1_gas, l1_data_gas, l2_gas })
}
_ => None,
}
}
pub fn cost(&self, gas_prices: &GasPriceVector, tip: Tip) -> Fee {
let tipped_l2_gas_price =
gas_prices.l2_gas_price.checked_add(tip.into()).unwrap_or_else(|| {
panic!(
"Tip overflowed: addition of L2 gas price ({}) and tip ({}) resulted in \
overflow.",
gas_prices.l2_gas_price, tip
)
});
let mut sum = Fee(0);
for (gas, price, resource) in [
(self.l1_gas, gas_prices.l1_gas_price, Resource::L1Gas),
(self.l1_data_gas, gas_prices.l1_data_gas_price, Resource::L1DataGas),
(self.l2_gas, tipped_l2_gas_price, Resource::L2Gas),
] {
let cost = gas.checked_mul(price.get()).unwrap_or_else(|| {
panic!(
"{resource} cost overflowed: multiplication of gas amount ({gas}) by price \
per unit ({price}) resulted in overflow."
)
});
sum = sum.checked_add(cost).unwrap_or_else(|| {
panic!(
"Total cost overflowed: addition of current sum ({sum}) and cost of \
{resource} ({cost}) resulted in overflow."
)
});
}
sum
}
}
pub fn to_discounted_l1_gas(
l1_gas_price: NonzeroGasPrice,
l1_data_gas_price: GasPrice,
l1_gas: GasAmount,
l1_data_gas: GasAmount,
) -> GasAmount {
let l1_data_gas_fee = l1_data_gas.checked_mul(l1_data_gas_price).unwrap_or_else(|| {
panic!(
"Discounted L1 gas cost overflowed: multiplication of L1 data gas ({l1_data_gas}) by \
L1 data gas price ({l1_data_gas_price}) resulted in overflow."
);
});
let l1_data_gas_in_l1_gas_units =
l1_data_gas_fee.checked_div_ceil(l1_gas_price).unwrap_or_else(|| {
panic!(
"Discounted L1 gas cost overflowed: division of L1 data fee ({l1_data_gas_fee}) \
by regular L1 gas price ({l1_gas_price}) resulted in overflow."
);
});
l1_gas.checked_add(l1_data_gas_in_l1_gas_units).unwrap_or_else(|| {
panic!(
"Overflow while computing discounted L1 gas: L1 gas ({l1_gas}) + L1 data gas in L1 \
gas units ({l1_data_gas_in_l1_gas_units}) resulted in overflow."
)
})
}
#[derive(Debug, Default, Deserialize, Serialize, Clone, Eq, PartialEq)]
pub struct ExecutionResources {
pub steps: u64,
pub builtin_instance_counter: HashMap<Builtin, u64>,
pub memory_holes: u64,
pub da_gas_consumed: GasVector,
pub gas_consumed: GasVector,
}
#[derive(Clone, Debug, Deserialize, EnumIter, Eq, Hash, PartialEq, Serialize)]
pub enum Builtin {
#[serde(rename = "range_check_builtin_applications")]
RangeCheck,
#[serde(rename = "pedersen_builtin_applications")]
Pedersen,
#[serde(rename = "poseidon_builtin_applications")]
Poseidon,
#[serde(rename = "ec_op_builtin_applications")]
EcOp,
#[serde(rename = "ecdsa_builtin_applications")]
Ecdsa,
#[serde(rename = "bitwise_builtin_applications")]
Bitwise,
#[serde(rename = "keccak_builtin_applications")]
Keccak,
#[serde(rename = "segment_arena_builtin")]
SegmentArena,
#[serde(rename = "add_mod_builtin")]
AddMod,
#[serde(rename = "mul_mod_builtin")]
MulMod,
#[serde(rename = "range_check96_builtin")]
RangeCheck96,
}
const RANGE_CHACK_BUILTIN_NAME: &str = "range_check";
const PEDERSEN_BUILTIN_NAME: &str = "pedersen";
const POSEIDON_BUILTIN_NAME: &str = "poseidon";
const EC_OP_BUILTIN_NAME: &str = "ec_op";
const ECDSA_BUILTIN_NAME: &str = "ecdsa";
const BITWISE_BUILTIN_NAME: &str = "bitwise";
const KECCAK_BUILTIN_NAME: &str = "keccak";
const SEGMENT_ARENA_BUILTIN_NAME: &str = "segment_arena";
const ADD_MOD_BUILTIN_NAME: &str = "add_mod";
const MUL_MOD_BUILTIN_NAME: &str = "mul_mod";
const RANGE_CHECK96_BUILTIN_NAME: &str = "range_check96";
impl Builtin {
pub fn name(&self) -> &'static str {
match self {
Builtin::RangeCheck => RANGE_CHACK_BUILTIN_NAME,
Builtin::Pedersen => PEDERSEN_BUILTIN_NAME,
Builtin::Poseidon => POSEIDON_BUILTIN_NAME,
Builtin::EcOp => EC_OP_BUILTIN_NAME,
Builtin::Ecdsa => ECDSA_BUILTIN_NAME,
Builtin::Bitwise => BITWISE_BUILTIN_NAME,
Builtin::Keccak => KECCAK_BUILTIN_NAME,
Builtin::SegmentArena => SEGMENT_ARENA_BUILTIN_NAME,
Builtin::AddMod => ADD_MOD_BUILTIN_NAME,
Builtin::MulMod => MUL_MOD_BUILTIN_NAME,
Builtin::RangeCheck96 => RANGE_CHECK96_BUILTIN_NAME,
}
}
}