use crate::{policy::ResourceControlPolicy, system::SystemExecutionError, ExecutionError};
use custom_debug_derive::Debug;
use linera_base::data_types::Amount;
#[derive(Copy, Debug, Clone)]
pub struct RuntimeLimits {
pub max_budget_num_reads: u64,
pub max_budget_bytes_read: u64,
pub max_budget_bytes_written: u64,
pub maximum_bytes_left_to_read: u64,
pub maximum_bytes_left_to_write: u64,
}
#[derive(Copy, Debug, Clone)]
pub struct ResourceTracker {
pub used_fuel: u64,
pub num_reads: u64,
pub bytes_read: u64,
pub bytes_written: u64,
pub maximum_bytes_left_to_read: u64,
pub maximum_bytes_left_to_write: u64,
}
#[cfg(any(test, feature = "test"))]
impl Default for ResourceTracker {
fn default() -> Self {
ResourceTracker {
used_fuel: 0,
num_reads: 0,
bytes_read: 0,
bytes_written: 0,
maximum_bytes_left_to_read: u64::MAX / 2,
maximum_bytes_left_to_write: u64::MAX / 2,
}
}
}
impl Default for RuntimeLimits {
fn default() -> Self {
RuntimeLimits {
max_budget_num_reads: u64::MAX / 2,
max_budget_bytes_read: u64::MAX / 2,
max_budget_bytes_written: u64::MAX / 2,
maximum_bytes_left_to_read: u64::MAX / 2,
maximum_bytes_left_to_write: u64::MAX / 2,
}
}
}
impl ResourceTracker {
fn sub_assign_fees(balance: &mut Amount, fees: Amount) -> Result<(), SystemExecutionError> {
balance
.try_sub_assign(fees)
.map_err(|_| SystemExecutionError::InsufficientFunding {
current_balance: *balance,
})
}
pub fn update_limits(
&mut self,
balance: &mut Amount,
policy: &ResourceControlPolicy,
runtime_counts: RuntimeCounts,
) -> Result<(), ExecutionError> {
let initial_fuel = policy.remaining_fuel(*balance);
let used_fuel = initial_fuel.saturating_sub(runtime_counts.remaining_fuel);
self.used_fuel += used_fuel;
Self::sub_assign_fees(balance, policy.fuel_price(used_fuel)?)?;
Self::sub_assign_fees(
balance,
policy.storage_num_reads_price(&runtime_counts.num_reads)?,
)?;
self.num_reads += runtime_counts.num_reads;
let bytes_read = runtime_counts.bytes_read;
self.maximum_bytes_left_to_read -= bytes_read;
self.bytes_read += runtime_counts.bytes_read;
Self::sub_assign_fees(balance, policy.storage_bytes_read_price(&bytes_read)?)?;
let bytes_written = runtime_counts.bytes_written;
self.maximum_bytes_left_to_write -= bytes_written;
self.bytes_written += bytes_written;
Self::sub_assign_fees(balance, policy.storage_bytes_written_price(&bytes_written)?)?;
Ok(())
}
pub fn limits(&self, policy: &ResourceControlPolicy, balance: &Amount) -> RuntimeLimits {
let max_budget_num_reads =
u64::try_from(balance.saturating_div(policy.storage_num_reads)).unwrap_or(u64::MAX);
let max_budget_bytes_read =
u64::try_from(balance.saturating_div(policy.storage_bytes_read)).unwrap_or(u64::MAX);
let max_budget_bytes_written =
u64::try_from(balance.saturating_div(policy.storage_bytes_read)).unwrap_or(u64::MAX);
RuntimeLimits {
max_budget_num_reads,
max_budget_bytes_read,
max_budget_bytes_written,
maximum_bytes_left_to_read: self.maximum_bytes_left_to_read,
maximum_bytes_left_to_write: self.maximum_bytes_left_to_write,
}
}
}
#[derive(Copy, Debug, Clone)]
pub struct RuntimeCounts {
pub remaining_fuel: u64,
pub num_reads: u64,
pub bytes_read: u64,
pub bytes_written: u64,
}