use crate::pages::{GearPagesAmount, WasmPagesAmount};
use core::{fmt::Debug, marker::PhantomData};
use paste::paste;
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct CostOf<T> {
cost: u64,
_phantom: PhantomData<T>,
}
impl<T> CostOf<T> {
pub const fn new(cost: u64) -> Self {
Self {
cost,
_phantom: PhantomData,
}
}
pub const fn cost_for_one(&self) -> u64 {
self.cost
}
}
impl<T: Into<u32>> CostOf<T> {
pub fn cost_for(&self, num: T) -> u64 {
self.cost.saturating_mul(Into::<u32>::into(num).into())
}
}
impl<T> Debug for CostOf<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_fmt(format_args!("{}", &self.cost))
}
}
impl<T> From<u64> for CostOf<T> {
fn from(cost: u64) -> Self {
CostOf::new(cost)
}
}
impl<T> From<CostOf<T>> for u64 {
fn from(value: CostOf<T>) -> Self {
value.cost
}
}
impl<T> Default for CostOf<T> {
fn default() -> Self {
CostOf::new(0)
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, derive_more::From, derive_more::Into)]
pub struct CallsAmount(u32);
impl CostOf<CallsAmount> {
pub fn with_bytes(&self, per_byte: CostOf<BytesAmount>, amount: BytesAmount) -> u64 {
self.cost_for_one()
.saturating_add(per_byte.cost_for(amount))
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, derive_more::From, derive_more::Into)]
pub struct BytesAmount(u32);
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, derive_more::From, derive_more::Into)]
pub struct BlocksAmount(u32);
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct SyscallCosts {
pub alloc: CostOf<CallsAmount>,
pub free: CostOf<CallsAmount>,
pub free_range: CostOf<CallsAmount>,
pub free_range_per_page: CostOf<WasmPagesAmount>,
pub gr_reserve_gas: CostOf<CallsAmount>,
pub gr_unreserve_gas: CostOf<CallsAmount>,
pub gr_system_reserve_gas: CostOf<CallsAmount>,
pub gr_gas_available: CostOf<CallsAmount>,
pub gr_message_id: CostOf<CallsAmount>,
pub gr_program_id: CostOf<CallsAmount>,
pub gr_source: CostOf<CallsAmount>,
pub gr_value: CostOf<CallsAmount>,
pub gr_value_available: CostOf<CallsAmount>,
pub gr_size: CostOf<CallsAmount>,
pub gr_read: CostOf<CallsAmount>,
pub gr_read_per_byte: CostOf<BytesAmount>,
pub gr_env_vars: CostOf<CallsAmount>,
pub gr_block_height: CostOf<CallsAmount>,
pub gr_block_timestamp: CostOf<CallsAmount>,
pub gr_random: CostOf<CallsAmount>,
pub gr_reply_deposit: CostOf<CallsAmount>,
pub gr_send: CostOf<CallsAmount>,
pub gr_send_per_byte: CostOf<BytesAmount>,
pub gr_send_wgas: CostOf<CallsAmount>,
pub gr_send_wgas_per_byte: CostOf<BytesAmount>,
pub gr_send_init: CostOf<CallsAmount>,
pub gr_send_push: CostOf<CallsAmount>,
pub gr_send_push_per_byte: CostOf<BytesAmount>,
pub gr_send_commit: CostOf<CallsAmount>,
pub gr_send_commit_wgas: CostOf<CallsAmount>,
pub gr_reservation_send: CostOf<CallsAmount>,
pub gr_reservation_send_per_byte: CostOf<BytesAmount>,
pub gr_reservation_send_commit: CostOf<CallsAmount>,
pub gr_send_input: CostOf<CallsAmount>,
pub gr_send_input_wgas: CostOf<CallsAmount>,
pub gr_send_push_input: CostOf<CallsAmount>,
pub gr_send_push_input_per_byte: CostOf<BytesAmount>,
pub gr_reply: CostOf<CallsAmount>,
pub gr_reply_per_byte: CostOf<BytesAmount>,
pub gr_reply_wgas: CostOf<CallsAmount>,
pub gr_reply_wgas_per_byte: CostOf<BytesAmount>,
pub gr_reply_commit: CostOf<CallsAmount>,
pub gr_reply_commit_wgas: CostOf<CallsAmount>,
pub gr_reservation_reply: CostOf<CallsAmount>,
pub gr_reservation_reply_per_byte: CostOf<BytesAmount>,
pub gr_reservation_reply_commit: CostOf<CallsAmount>,
pub gr_reply_push: CostOf<CallsAmount>,
pub gr_reply_push_per_byte: CostOf<BytesAmount>,
pub gr_reply_input: CostOf<CallsAmount>,
pub gr_reply_input_wgas: CostOf<CallsAmount>,
pub gr_reply_push_input: CostOf<CallsAmount>,
pub gr_reply_push_input_per_byte: CostOf<BytesAmount>,
pub gr_reply_to: CostOf<CallsAmount>,
pub gr_signal_code: CostOf<CallsAmount>,
pub gr_signal_from: CostOf<CallsAmount>,
pub gr_debug: CostOf<CallsAmount>,
pub gr_debug_per_byte: CostOf<BytesAmount>,
pub gr_reply_code: CostOf<CallsAmount>,
pub gr_exit: CostOf<CallsAmount>,
pub gr_leave: CostOf<CallsAmount>,
pub gr_wait: CostOf<CallsAmount>,
pub gr_wait_for: CostOf<CallsAmount>,
pub gr_wait_up_to: CostOf<CallsAmount>,
pub gr_wake: CostOf<CallsAmount>,
pub gr_create_program: CostOf<CallsAmount>,
pub gr_create_program_payload_per_byte: CostOf<BytesAmount>,
pub gr_create_program_salt_per_byte: CostOf<BytesAmount>,
pub gr_create_program_wgas: CostOf<CallsAmount>,
pub gr_create_program_wgas_payload_per_byte: CostOf<BytesAmount>,
pub gr_create_program_wgas_salt_per_byte: CostOf<BytesAmount>,
}
#[derive(Debug, Copy, Clone)]
pub enum CostToken {
Null,
Alloc,
Free,
FreeRange,
ReserveGas,
UnreserveGas,
SystemReserveGas,
GasAvailable,
MsgId,
ActorId,
Source,
Value,
ValueAvailable,
Size,
Read,
EnvVars,
BlockHeight,
BlockTimestamp,
Random,
ReplyDeposit,
Send(BytesAmount),
SendWGas(BytesAmount),
SendInit,
SendPush(BytesAmount),
SendCommit,
SendCommitWGas,
ReservationSend(BytesAmount),
ReservationSendCommit,
SendInput,
SendInputWGas,
SendPushInput,
Reply(BytesAmount),
ReplyWGas(BytesAmount),
ReplyPush(BytesAmount),
ReplyCommit,
ReplyCommitWGas,
ReservationReply(BytesAmount),
ReservationReplyCommit,
ReplyInput,
ReplyInputWGas,
ReplyPushInput,
ReplyTo,
SignalCode,
SignalFrom,
Debug(BytesAmount),
ReplyCode,
Exit,
Leave,
Wait,
WaitFor,
WaitUpTo,
Wake,
CreateProgram(BytesAmount, BytesAmount),
CreateProgramWGas(BytesAmount, BytesAmount),
}
impl SyscallCosts {
pub fn cost_for_token(&self, token: CostToken) -> u64 {
use CostToken::*;
macro_rules! cost_with_per_byte {
($name:ident, $len:expr) => {
paste! {
self.$name.with_bytes(self.[< $name _per_byte >], $len)
}
};
}
match token {
Null => 0,
Alloc => self.alloc.cost_for_one(),
Free => self.free.cost_for_one(),
FreeRange => self.free_range.cost_for_one(),
ReserveGas => self.gr_reserve_gas.cost_for_one(),
UnreserveGas => self.gr_unreserve_gas.cost_for_one(),
SystemReserveGas => self.gr_system_reserve_gas.cost_for_one(),
GasAvailable => self.gr_gas_available.cost_for_one(),
MsgId => self.gr_message_id.cost_for_one(),
ActorId => self.gr_program_id.cost_for_one(),
Source => self.gr_source.cost_for_one(),
Value => self.gr_value.cost_for_one(),
ValueAvailable => self.gr_value_available.cost_for_one(),
Size => self.gr_size.cost_for_one(),
Read => self.gr_read.cost_for_one(),
EnvVars => self.gr_env_vars.cost_for_one(),
BlockHeight => self.gr_block_height.cost_for_one(),
BlockTimestamp => self.gr_block_timestamp.cost_for_one(),
Random => self.gr_random.cost_for_one(),
ReplyDeposit => self.gr_reply_deposit.cost_for_one(),
Send(len) => cost_with_per_byte!(gr_send, len),
SendWGas(len) => cost_with_per_byte!(gr_send_wgas, len),
SendInit => self.gr_send_init.cost_for_one(),
SendPush(len) => cost_with_per_byte!(gr_send_push, len),
SendCommit => self.gr_send_commit.cost_for_one(),
SendCommitWGas => self.gr_send_commit_wgas.cost_for_one(),
ReservationSend(len) => cost_with_per_byte!(gr_reservation_send, len),
ReservationSendCommit => self.gr_reservation_send_commit.cost_for_one(),
SendInput => self.gr_send_input.cost_for_one(),
SendInputWGas => self.gr_send_input_wgas.cost_for_one(),
SendPushInput => self.gr_send_push_input.cost_for_one(),
Reply(len) => cost_with_per_byte!(gr_reply, len),
ReplyWGas(len) => cost_with_per_byte!(gr_reply_wgas, len),
ReplyPush(len) => cost_with_per_byte!(gr_reply_push, len),
ReplyCommit => self.gr_reply_commit.cost_for_one(),
ReplyCommitWGas => self.gr_reply_commit_wgas.cost_for_one(),
ReservationReply(len) => cost_with_per_byte!(gr_reservation_reply, len),
ReservationReplyCommit => self.gr_reservation_reply_commit.cost_for_one(),
ReplyInput => self.gr_reply_input.cost_for_one(),
ReplyInputWGas => self.gr_reply_input_wgas.cost_for_one(),
ReplyPushInput => self.gr_reply_push_input.cost_for_one(),
ReplyTo => self.gr_reply_to.cost_for_one(),
SignalCode => self.gr_signal_code.cost_for_one(),
SignalFrom => self.gr_signal_from.cost_for_one(),
Debug(len) => cost_with_per_byte!(gr_debug, len),
ReplyCode => self.gr_reply_code.cost_for_one(),
Exit => self.gr_exit.cost_for_one(),
Leave => self.gr_leave.cost_for_one(),
Wait => self.gr_wait.cost_for_one(),
WaitFor => self.gr_wait_for.cost_for_one(),
WaitUpTo => self.gr_wait_up_to.cost_for_one(),
Wake => self.gr_wake.cost_for_one(),
CreateProgram(payload, salt) => CostOf::from(
self.gr_create_program
.with_bytes(self.gr_create_program_payload_per_byte, payload),
)
.with_bytes(self.gr_create_program_salt_per_byte, salt),
CreateProgramWGas(payload, salt) => CostOf::from(
self.gr_create_program_wgas
.with_bytes(self.gr_create_program_wgas_payload_per_byte, payload),
)
.with_bytes(self.gr_create_program_wgas_salt_per_byte, salt),
}
}
}
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct PagesCosts {
pub load_page_data: CostOf<GearPagesAmount>,
pub upload_page_data: CostOf<GearPagesAmount>,
pub mem_grow: CostOf<GearPagesAmount>,
pub mem_grow_per_page: CostOf<GearPagesAmount>,
pub parachain_read_heuristic: CostOf<GearPagesAmount>,
}
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct LazyPagesCosts {
pub signal_read: CostOf<GearPagesAmount>,
pub signal_write: CostOf<GearPagesAmount>,
pub signal_write_after_read: CostOf<GearPagesAmount>,
pub host_func_read: CostOf<GearPagesAmount>,
pub host_func_write: CostOf<GearPagesAmount>,
pub host_func_write_after_read: CostOf<GearPagesAmount>,
pub load_page_storage_data: CostOf<GearPagesAmount>,
}
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct IoCosts {
pub common: PagesCosts,
pub lazy_pages: LazyPagesCosts,
}
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct RentCosts {
pub waitlist: CostOf<BlocksAmount>,
pub dispatch_stash: CostOf<BlocksAmount>,
pub reservation: CostOf<BlocksAmount>,
}
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct ExtCosts {
pub syscalls: SyscallCosts,
pub rent: RentCosts,
pub mem_grow: CostOf<CallsAmount>,
pub mem_grow_per_page: CostOf<WasmPagesAmount>,
}
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct InstantiationCosts {
pub code_section_per_byte: CostOf<BytesAmount>,
pub data_section_per_byte: CostOf<BytesAmount>,
pub global_section_per_byte: CostOf<BytesAmount>,
pub table_section_per_byte: CostOf<BytesAmount>,
pub element_section_per_byte: CostOf<BytesAmount>,
pub type_section_per_byte: CostOf<BytesAmount>,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct DbCosts {
pub read: CostOf<CallsAmount>,
pub read_per_byte: CostOf<BytesAmount>,
pub write: CostOf<CallsAmount>,
pub write_per_byte: CostOf<BytesAmount>,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct InstrumentationCosts {
pub base: CostOf<CallsAmount>,
pub per_byte: CostOf<BytesAmount>,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct ProcessCosts {
pub ext: ExtCosts,
pub lazy_pages: LazyPagesCosts,
pub instantiation: InstantiationCosts,
pub db: DbCosts,
pub instrumentation: InstrumentationCosts,
pub load_allocations_per_interval: CostOf<u32>,
}