use crate::{
xdr::{ContractCostParamEntry, ScErrorCode, ScErrorType},
HostError,
};
use core::fmt::{Debug, Display};
pub trait HostCostModel {
fn evaluate(&self, input: Option<u64>) -> Result<u64, HostError>;
#[cfg(any(test, feature = "testutils", feature = "bench"))]
fn reset(&mut self);
}
pub const COST_MODEL_LIN_TERM_SCALE_BITS: u32 = 7;
#[derive(Clone, Default)]
pub(crate) struct ScaledU64(pub(crate) u64);
impl ScaledU64 {
pub const fn unscale(self) -> u64 {
self.0 >> COST_MODEL_LIN_TERM_SCALE_BITS
}
pub const fn from_unscaled_u64(u: u64) -> Self {
ScaledU64(u << COST_MODEL_LIN_TERM_SCALE_BITS)
}
pub const fn is_zero(&self) -> bool {
self.0 == 0
}
pub const fn saturating_mul(&self, rhs: u64) -> Self {
ScaledU64(self.0.saturating_mul(rhs))
}
pub const fn safe_div(&self, rhs: u64) -> Self {
ScaledU64(match self.0.checked_div(rhs) {
Some(v) => v,
None => 0,
})
}
}
impl Display for ScaledU64 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl Debug for ScaledU64 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Scaled({})", self.0)
}
}
#[derive(Clone, Debug, Default)]
pub(crate) struct MeteredCostComponent {
pub(crate) const_term: u64,
pub(crate) lin_term: ScaledU64,
}
impl TryFrom<&ContractCostParamEntry> for MeteredCostComponent {
type Error = HostError;
fn try_from(entry: &ContractCostParamEntry) -> Result<Self, Self::Error> {
if entry.const_term < 0 || entry.linear_term < 0 {
return Err((ScErrorType::Context, ScErrorCode::InvalidInput).into());
}
Ok(MeteredCostComponent {
const_term: entry.const_term as u64,
lin_term: ScaledU64(entry.linear_term as u64),
})
}
}
impl TryFrom<ContractCostParamEntry> for MeteredCostComponent {
type Error = HostError;
fn try_from(entry: ContractCostParamEntry) -> Result<Self, Self::Error> {
Self::try_from(&entry)
}
}
impl HostCostModel for MeteredCostComponent {
fn evaluate(&self, input: Option<u64>) -> Result<u64, HostError> {
let const_term = self.const_term;
match input {
Some(input) => {
let mut res = const_term;
if !self.lin_term.is_zero() {
let lin_cost = self.lin_term.saturating_mul(input).unscale();
res = res.saturating_add(lin_cost)
}
Ok(res)
}
None => Ok(const_term),
}
}
#[cfg(any(test, feature = "testutils", feature = "bench"))]
fn reset(&mut self) {
self.const_term = 0;
self.lin_term = ScaledU64(0);
}
}