use mirai_annotations::*;
use serde::{Deserialize, Serialize};
use std::{
ops::{Add, Div, Mul, Sub},
u64,
};
pub type GasCarrier = u64;
pub trait GasAlgebra<GasCarrier>: Sized
where
GasCarrier: Add<Output = GasCarrier>
+ Sub<Output = GasCarrier>
+ Div<Output = GasCarrier>
+ Mul<Output = GasCarrier>
+ Copy,
{
fn new(carrier: GasCarrier) -> Self;
fn get(&self) -> GasCarrier;
fn map<F: Fn(GasCarrier) -> GasCarrier>(self, f: F) -> Self {
Self::new(f(self.get()))
}
fn map2<F: Fn(GasCarrier, GasCarrier) -> GasCarrier>(
self,
other: impl GasAlgebra<GasCarrier>,
f: F,
) -> Self {
Self::new(f(self.get(), other.get()))
}
fn app<T, F: Fn(GasCarrier, GasCarrier) -> T>(
&self,
other: &impl GasAlgebra<GasCarrier>,
f: F,
) -> T {
f(self.get(), other.get())
}
fn unitary_cast<T: GasAlgebra<GasCarrier>>(self) -> T {
T::new(self.get())
}
fn add(self, right: impl GasAlgebra<GasCarrier>) -> Self {
self.map2(right, Add::add)
}
fn sub(self, right: impl GasAlgebra<GasCarrier>) -> Self {
self.map2(right, Sub::sub)
}
fn mul(self, right: impl GasAlgebra<GasCarrier>) -> Self {
self.map2(right, Mul::mul)
}
fn div(self, right: impl GasAlgebra<GasCarrier>) -> Self {
self.map2(right, Div::div)
}
}
macro_rules! define_gas_unit {
{
name: $name: ident,
carrier: $carrier: ty,
doc: $comment: literal
} => {
#[derive(Debug, Hash, Eq, PartialEq, Copy, Clone, Serialize, Deserialize)]
#[doc=$comment]
pub struct $name<GasCarrier>(GasCarrier);
impl GasAlgebra<$carrier> for $name<$carrier> {
fn new(c: GasCarrier) -> Self {
Self(c)
}
fn get(&self) -> GasCarrier {
self.0
}
}
}
}
define_gas_unit! {
name: AbstractMemorySize,
carrier: GasCarrier,
doc: "A newtype wrapper that represents the (abstract) memory size that the instruction will take up."
}
define_gas_unit! {
name: GasUnits,
carrier: GasCarrier,
doc: "Units of gas as seen by clients of the Move VM."
}
define_gas_unit! {
name: InternalGasUnits,
carrier: GasCarrier,
doc: "Units of gas used within the Move VM, scaled for fine-grained accounting."
}
define_gas_unit! {
name: GasPrice,
carrier: GasCarrier,
doc: "A newtype wrapper around the gas price for each unit of gas consumed."
}
pub const ONE_GAS_UNIT: InternalGasUnits<GasCarrier> = InternalGasUnits(1);
pub const MAX_ABSTRACT_MEMORY_SIZE: AbstractMemorySize<GasCarrier> =
AbstractMemorySize(std::u64::MAX);
pub const CONST_SIZE: AbstractMemorySize<GasCarrier> = AbstractMemorySize(16);
pub const REFERENCE_SIZE: AbstractMemorySize<GasCarrier> = AbstractMemorySize(8);
pub const STRUCT_SIZE: AbstractMemorySize<GasCarrier> = AbstractMemorySize(2);
pub const DEFAULT_ACCOUNT_SIZE: AbstractMemorySize<GasCarrier> = AbstractMemorySize(800);
pub const LARGE_TRANSACTION_CUTOFF: AbstractMemorySize<GasCarrier> = AbstractMemorySize(600);
pub const MIN_EXISTS_DATA_SIZE: AbstractMemorySize<GasCarrier> = AbstractMemorySize(100);
pub const MAX_TRANSACTION_SIZE_IN_BYTES: GasCarrier = 4096;
#[derive(Clone, Debug, Serialize, PartialEq, Deserialize)]
pub struct GasConstants {
pub global_memory_per_byte_cost: InternalGasUnits<GasCarrier>,
pub global_memory_per_byte_write_cost: InternalGasUnits<GasCarrier>,
pub min_transaction_gas_units: InternalGasUnits<GasCarrier>,
pub large_transaction_cutoff: AbstractMemorySize<GasCarrier>,
pub intrinsic_gas_per_byte: InternalGasUnits<GasCarrier>,
pub maximum_number_of_gas_units: GasUnits<GasCarrier>,
pub min_price_per_gas_unit: GasPrice<GasCarrier>,
pub max_price_per_gas_unit: GasPrice<GasCarrier>,
pub max_transaction_size_in_bytes: GasCarrier,
pub gas_unit_scaling_factor: GasCarrier,
pub default_account_size: AbstractMemorySize<GasCarrier>,
}
impl GasConstants {
pub fn to_internal_units(&self, units: GasUnits<GasCarrier>) -> InternalGasUnits<GasCarrier> {
InternalGasUnits::new(units.get() * self.gas_unit_scaling_factor)
}
pub fn to_external_units(&self, units: InternalGasUnits<GasCarrier>) -> GasUnits<GasCarrier> {
GasUnits::new(units.get() / self.gas_unit_scaling_factor)
}
}
impl Default for GasConstants {
fn default() -> Self {
Self {
global_memory_per_byte_cost: InternalGasUnits(4),
global_memory_per_byte_write_cost: InternalGasUnits(9),
min_transaction_gas_units: InternalGasUnits(600),
large_transaction_cutoff: LARGE_TRANSACTION_CUTOFF,
intrinsic_gas_per_byte: InternalGasUnits(8),
maximum_number_of_gas_units: GasUnits(4_000_000),
min_price_per_gas_unit: GasPrice(0),
max_price_per_gas_unit: GasPrice(10_000),
max_transaction_size_in_bytes: MAX_TRANSACTION_SIZE_IN_BYTES,
gas_unit_scaling_factor: 1000,
default_account_size: DEFAULT_ACCOUNT_SIZE,
}
}
}
#[derive(Clone, Debug, Serialize, PartialEq, Deserialize)]
pub struct CostTable {
pub instruction_table: Vec<GasCost>,
pub native_table: Vec<GasCost>,
pub gas_constants: GasConstants,
}
impl CostTable {
#[inline]
pub fn instruction_cost(&self, instr_index: u8) -> &GasCost {
precondition!(instr_index > 0 && instr_index <= (self.instruction_table.len() as u8));
&self.instruction_table[(instr_index - 1) as usize]
}
#[inline]
pub fn native_cost(&self, native_index: u8) -> &GasCost {
precondition!(native_index < (self.native_table.len() as u8));
&self.native_table[native_index as usize]
}
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct GasCost {
pub instruction_gas: InternalGasUnits<GasCarrier>,
pub memory_gas: InternalGasUnits<GasCarrier>,
}
impl GasCost {
pub fn new(instr_gas: GasCarrier, mem_gas: GasCarrier) -> Self {
Self {
instruction_gas: InternalGasUnits::new(instr_gas),
memory_gas: InternalGasUnits::new(mem_gas),
}
}
#[inline]
pub fn total(&self) -> InternalGasUnits<GasCarrier> {
self.instruction_gas.add(self.memory_gas)
}
}