Skip to main content

iota_types/gas_model/
units_types.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use std::{
6    collections::BTreeMap,
7    ops::{Add, Bound},
8};
9
10use move_core_types::gas_algebra::{
11    GasQuantity, InternalGas, InternalGasUnit, ToUnit, ToUnitFractional,
12};
13use serde::{Deserialize, Serialize};
14
15pub enum GasUnit {}
16
17pub type Gas = GasQuantity<GasUnit>;
18
19impl ToUnit<InternalGasUnit> for GasUnit {
20    const MULTIPLIER: u64 = 1000;
21}
22
23impl ToUnitFractional<GasUnit> for InternalGasUnit {
24    const NOMINATOR: u64 = 1;
25    const DENOMINATOR: u64 = 1000;
26}
27
28pub const INSTRUCTION_TIER_DEFAULT: u64 = 1;
29
30pub const STACK_HEIGHT_TIER_DEFAULT: u64 = 1;
31pub const STACK_SIZE_TIER_DEFAULT: u64 = 1;
32
33// The cost table holds the tiers and curves for instruction costs.
34#[derive(Clone, Debug, Serialize, PartialEq, Eq, Deserialize)]
35pub struct CostTable {
36    pub instruction_tiers: BTreeMap<u64, u64>,
37    pub stack_height_tiers: BTreeMap<u64, u64>,
38    pub stack_size_tiers: BTreeMap<u64, u64>,
39}
40
41impl CostTable {
42    fn get_current_and_future_tier(
43        tiers: &BTreeMap<u64, u64>,
44        current: u64,
45        default: u64,
46    ) -> (u64, Option<u64>) {
47        let current_cost = tiers
48            .get(&current)
49            .or_else(|| tiers.range(..current).next_back().map(|(_, v)| v))
50            .unwrap_or(&default);
51        let next_tier_start = tiers
52            .range::<u64, _>((Bound::Excluded(current), Bound::Unbounded))
53            .next()
54            .map(|(next_tier_start, _)| *next_tier_start);
55        (*current_cost, next_tier_start)
56    }
57
58    pub fn instruction_tier(&self, instr_count: u64) -> (u64, Option<u64>) {
59        Self::get_current_and_future_tier(
60            &self.instruction_tiers,
61            instr_count,
62            INSTRUCTION_TIER_DEFAULT,
63        )
64    }
65
66    pub fn stack_height_tier(&self, stack_height: u64) -> (u64, Option<u64>) {
67        Self::get_current_and_future_tier(
68            &self.stack_height_tiers,
69            stack_height,
70            STACK_HEIGHT_TIER_DEFAULT,
71        )
72    }
73
74    pub fn stack_size_tier(&self, stack_size: u64) -> (u64, Option<u64>) {
75        Self::get_current_and_future_tier(
76            &self.stack_size_tiers,
77            stack_size,
78            STACK_SIZE_TIER_DEFAULT,
79        )
80    }
81}
82
83/// The  `GasCost` tracks:
84/// - instruction cost: how much time/computational power is needed to perform the instruction
85/// - memory cost: how much memory is required for the instruction, and storage overhead
86/// - stack height: how high is the stack growing (regardless of size in bytes)
87#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
88pub struct GasCost {
89    pub instruction_gas: u64,
90    pub memory_gas: u64,
91    pub stack_height_gas: u64,
92}
93
94impl GasCost {
95    pub fn new(instruction_gas: u64, memory_gas: u64, stack_height_gas: u64) -> Self {
96        Self {
97            instruction_gas,
98            memory_gas,
99            stack_height_gas,
100        }
101    }
102
103    /// Convert a GasCost to a total gas charge in `InternalGas`.
104    #[inline]
105    pub fn total(&self) -> u64 {
106        self.instruction_gas
107            .add(self.memory_gas)
108            .add(self.stack_height_gas)
109    }
110
111    #[inline]
112    pub fn total_internal(&self) -> InternalGas {
113        GasQuantity::new(
114            self.instruction_gas
115                .add(self.memory_gas)
116                .add(self.stack_height_gas),
117        )
118    }
119}