use crate::{gas::Token, pages::PageU32Size};
use core::{fmt::Debug, marker::PhantomData};
use paste::paste;
use scale_info::scale::{Decode, Encode};
#[derive(Clone, Copy, PartialEq, Eq, Encode, Decode)]
pub struct CostPerPage<P: PageU32Size> {
cost: u64,
_phantom: PhantomData<P>,
}
impl<P: PageU32Size> CostPerPage<P> {
pub const fn new(cost: u64) -> Self {
Self {
cost,
_phantom: PhantomData,
}
}
pub fn calc(&self, pages: P) -> u64 {
self.cost.saturating_mul(pages.raw() as u64)
}
pub fn one(&self) -> u64 {
self.cost
}
pub fn saturating_add(&self, other: Self) -> Self {
self.cost.saturating_add(other.cost).into()
}
}
impl<P: PageU32Size> Debug for CostPerPage<P> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_fmt(format_args!("{}", &self.cost))
}
}
impl<P: PageU32Size> From<u64> for CostPerPage<P> {
fn from(cost: u64) -> Self {
CostPerPage {
cost,
_phantom: PhantomData,
}
}
}
impl<P: PageU32Size> From<CostPerPage<P>> for u64 {
fn from(value: CostPerPage<P>) -> Self {
value.cost
}
}
impl<P: PageU32Size> Default for CostPerPage<P> {
fn default() -> Self {
Self {
cost: 0,
_phantom: PhantomData,
}
}
}
#[derive(Clone, Encode, Decode, PartialEq, Eq, Default)]
pub struct HostFnWeights {
pub alloc: u64,
pub alloc_per_page: u64,
pub free: u64,
pub gr_reserve_gas: u64,
pub gr_unreserve_gas: u64,
pub gr_system_reserve_gas: u64,
pub gr_gas_available: u64,
pub gr_message_id: u64,
pub gr_pay_program_rent: u64,
pub gr_program_id: u64,
pub gr_source: u64,
pub gr_value: u64,
pub gr_value_available: u64,
pub gr_size: u64,
pub gr_read: u64,
pub gr_read_per_byte: u64,
pub gr_block_height: u64,
pub gr_block_timestamp: u64,
pub gr_random: u64,
pub gr_reply_deposit: u64,
pub gr_send: u64,
pub gr_send_per_byte: u64,
pub gr_send_wgas: u64,
pub gr_send_wgas_per_byte: u64,
pub gr_send_init: u64,
pub gr_send_push: u64,
pub gr_send_push_per_byte: u64,
pub gr_send_commit: u64,
pub gr_send_commit_wgas: u64,
pub gr_reservation_send: u64,
pub gr_reservation_send_per_byte: u64,
pub gr_reservation_send_commit: u64,
pub gr_send_input: u64,
pub gr_send_input_wgas: u64,
pub gr_send_push_input: u64,
pub gr_send_push_input_per_byte: u64,
pub gr_reply: u64,
pub gr_reply_per_byte: u64,
pub gr_reply_wgas: u64,
pub gr_reply_wgas_per_byte: u64,
pub gr_reply_commit: u64,
pub gr_reply_commit_wgas: u64,
pub gr_reservation_reply: u64,
pub gr_reservation_reply_per_byte: u64,
pub gr_reservation_reply_commit: u64,
pub gr_reply_push: u64,
pub gr_reply_push_per_byte: u64,
pub gr_reply_input: u64,
pub gr_reply_input_wgas: u64,
pub gr_reply_push_input: u64,
pub gr_reply_push_input_per_byte: u64,
pub gr_reply_to: u64,
pub gr_signal_from: u64,
pub gr_debug: u64,
pub gr_debug_per_byte: u64,
pub gr_reply_code: u64,
pub gr_exit: u64,
pub gr_leave: u64,
pub gr_wait: u64,
pub gr_wait_for: u64,
pub gr_wait_up_to: u64,
pub gr_wake: u64,
pub gr_create_program: u64,
pub gr_create_program_payload_per_byte: u64,
pub gr_create_program_salt_per_byte: u64,
pub gr_create_program_wgas: u64,
pub gr_create_program_wgas_payload_per_byte: u64,
pub gr_create_program_wgas_salt_per_byte: u64,
}
#[derive(Copy, Clone)]
pub struct RuntimeToken {
weight: u64,
}
impl From<RuntimeToken> for u64 {
fn from(value: RuntimeToken) -> Self {
value.weight
}
}
impl Token for RuntimeToken {
fn weight(&self) -> u64 {
self.weight
}
}
#[derive(Debug, Copy, Clone)]
pub enum RuntimeCosts {
Null,
Alloc(u32),
Free,
ReserveGas,
UnreserveGas,
SystemReserveGas,
GasAvailable,
MsgId,
PayProgramRent,
ProgramId,
Source,
Value,
ValueAvailable,
Size,
Read,
ReadPerByte(u32),
BlockHeight,
BlockTimestamp,
Random,
ReplyDeposit,
Send(u32),
SendWGas(u32),
SendInit,
SendPush(u32),
SendCommit,
SendCommitWGas,
ReservationSend(u32),
ReservationSendCommit,
SendInput,
SendInputWGas,
SendPushInput,
SendPushInputPerByte(u32),
Reply(u32),
ReplyWGas(u32),
ReplyPush(u32),
ReplyCommit,
ReplyCommitWGas,
ReservationReply(u32),
ReservationReplyCommit,
ReplyInput,
ReplyInputWGas,
ReplyPushInput,
ReplyPushInputPerByte(u32),
ReplyTo,
SignalFrom,
Debug(u32),
ReplyCode,
Exit,
Leave,
Wait,
WaitFor,
WaitUpTo,
Wake,
CreateProgram(u32, u32),
CreateProgramWGas(u32, u32),
}
impl RuntimeCosts {
pub fn token(&self, s: &HostFnWeights) -> RuntimeToken {
use self::RuntimeCosts::*;
let cost_per_byte =
|weight_per_byte: u64, len: u32| weight_per_byte.saturating_mul(len.into());
let cost_with_two_weights_per_byte =
|weight_per_call: u64, weight1_per_byte, weight2_per_byte, len1, len2| {
weight_per_call
.saturating_add(cost_per_byte(weight1_per_byte, len1))
.saturating_add(cost_per_byte(weight2_per_byte, len2))
};
macro_rules! cost_with_weight_per_byte {
($name:ident, $len:expr) => {
paste! {
s.$name.saturating_add(cost_per_byte(s.[< $name _per_byte >], $len))
}
};
}
let cost_with_weight_per_page = |call_weight: u64, weight_per_page: u64, pages: u32| {
call_weight.saturating_add(weight_per_page.saturating_mul(pages as u64))
};
let weight = match *self {
Null => 0,
Alloc(pages) => cost_with_weight_per_page(s.alloc, s.alloc_per_page, pages),
Free => s.free,
ReserveGas => s.gr_reserve_gas,
UnreserveGas => s.gr_unreserve_gas,
SystemReserveGas => s.gr_system_reserve_gas,
GasAvailable => s.gr_gas_available,
MsgId => s.gr_message_id,
PayProgramRent => s.gr_pay_program_rent,
ProgramId => s.gr_program_id,
Source => s.gr_source,
Value => s.gr_value,
ValueAvailable => s.gr_value_available,
Size => s.gr_size,
Read => s.gr_read,
ReadPerByte(len) => cost_per_byte(s.gr_read_per_byte, len),
BlockHeight => s.gr_block_height,
BlockTimestamp => s.gr_block_timestamp,
Random => s.gr_random,
ReplyDeposit => s.gr_reply_deposit,
Send(len) => cost_with_weight_per_byte!(gr_send, len),
SendWGas(len) => cost_with_weight_per_byte!(gr_send_wgas, len),
SendInit => s.gr_send_init,
SendPush(len) => cost_with_weight_per_byte!(gr_send_push, len),
SendCommit => s.gr_send_commit,
SendCommitWGas => s.gr_send_commit_wgas,
ReservationSend(len) => cost_with_weight_per_byte!(gr_reservation_send, len),
ReservationSendCommit => s.gr_reservation_send_commit,
SendInput => s.gr_send_input,
SendInputWGas => s.gr_send_input_wgas,
SendPushInput => s.gr_send_push_input,
SendPushInputPerByte(len) => cost_per_byte(s.gr_send_push_input_per_byte, len),
Reply(len) => cost_with_weight_per_byte!(gr_reply, len),
ReplyWGas(len) => cost_with_weight_per_byte!(gr_reply_wgas, len),
ReplyPush(len) => cost_with_weight_per_byte!(gr_reply_push, len),
ReplyCommit => s.gr_reply_commit,
ReplyCommitWGas => s.gr_reply_commit_wgas,
ReservationReply(len) => cost_with_weight_per_byte!(gr_reservation_reply, len),
ReservationReplyCommit => s.gr_reservation_reply_commit,
ReplyInput => s.gr_reply_input,
ReplyInputWGas => s.gr_reply_input_wgas,
ReplyPushInput => s.gr_reply_push_input,
ReplyPushInputPerByte(len) => cost_per_byte(s.gr_reply_push_input_per_byte, len),
ReplyTo => s.gr_reply_to,
SignalFrom => s.gr_signal_from,
Debug(len) => cost_with_weight_per_byte!(gr_debug, len),
ReplyCode => s.gr_reply_code,
Exit => s.gr_exit,
Leave => s.gr_leave,
Wait => s.gr_wait,
WaitFor => s.gr_wait_for,
WaitUpTo => s.gr_wait_up_to,
Wake => s.gr_wake,
CreateProgram(payload_len, salt_len) => cost_with_two_weights_per_byte(
s.gr_create_program,
s.gr_create_program_payload_per_byte,
s.gr_create_program_salt_per_byte,
payload_len,
salt_len,
),
CreateProgramWGas(payload_len, salt_len) => cost_with_two_weights_per_byte(
s.gr_create_program_wgas,
s.gr_create_program_wgas_payload_per_byte,
s.gr_create_program_wgas_salt_per_byte,
payload_len,
salt_len,
),
};
RuntimeToken { weight }
}
}