sui_adapter_v3/static_programmable_transactions/metering/
translation_meter.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::gas_charger::GasCharger;
5use sui_protocol_config::ProtocolConfig;
6use sui_types::error::ExecutionError;
7use sui_types::execution_status::ExecutionErrorKind;
8
9/// The [`TranslationMeter`] is responsible for metering gas usage for various operations
10/// during the translation of a transaction. It interacts with and exposes interfaces to the
11/// [`GasCharger`] it holds in order to deduct gas based on the operations performed.
12///
13/// It holds a reference to the `ProtocolConfig` to access protocol-specific configuration
14/// parameters that may influence gas costs and limits.
15pub struct TranslationMeter<'pc, 'gas> {
16    protocol_config: &'pc ProtocolConfig,
17    charger: &'gas mut GasCharger,
18}
19
20impl<'pc, 'gas> TranslationMeter<'pc, 'gas> {
21    pub fn new(
22        protocol_config: &'pc ProtocolConfig,
23        gas_charger: &'gas mut GasCharger,
24    ) -> TranslationMeter<'pc, 'gas> {
25        TranslationMeter {
26            protocol_config,
27            charger: gas_charger,
28        }
29    }
30
31    pub fn charge_base_inputs(&mut self, num_inputs: usize) -> Result<(), ExecutionError> {
32        let amount = (num_inputs as u64)
33            .max(1)
34            .saturating_mul(self.protocol_config.translation_per_input_base_charge());
35        self.charge(amount)
36    }
37
38    pub fn charge_pure_input_bytes(&mut self, num_bytes: usize) -> Result<(), ExecutionError> {
39        let amount = (num_bytes as u64).max(1).saturating_mul(
40            self.protocol_config
41                .translation_pure_input_per_byte_charge(),
42        );
43        self.charge(amount)
44    }
45
46    pub fn charge_base_command(&mut self, num_args: usize) -> Result<(), ExecutionError> {
47        let amount = (num_args as u64)
48            .max(1)
49            .saturating_mul(self.protocol_config.translation_per_command_base_charge());
50        self.charge(amount)
51    }
52
53    /// Charge gas for loading types based on the number of type nodes loaded.
54    /// The cost is calculated as `num_type_nodes * TYPE_LOAD_PER_NODE_MULTIPLIER`.
55    /// This function assumes that `num_type_nodes` is non-zero.
56    pub fn charge_num_type_nodes(&mut self, num_type_nodes: u64) -> Result<(), ExecutionError> {
57        let amount = num_type_nodes
58            .max(1)
59            .saturating_mul(self.protocol_config.translation_per_type_node_charge());
60        self.charge(amount)
61    }
62
63    pub fn charge_num_type_references(
64        &mut self,
65        num_type_references: u64,
66    ) -> Result<(), ExecutionError> {
67        let amount = self.reference_cost_formula(num_type_references.max(1))?;
68        let amount =
69            amount.saturating_mul(self.protocol_config.translation_per_reference_node_charge());
70        self.charge(amount)
71    }
72
73    pub fn charge_num_linkage_entries(
74        &mut self,
75        num_linkage_entries: usize,
76    ) -> Result<(), ExecutionError> {
77        let amount = (num_linkage_entries as u64)
78            .saturating_mul(self.protocol_config.translation_per_linkage_entry_charge())
79            .max(1);
80        self.charge(amount)
81    }
82
83    // We use a non-linear cost function for type references to account for the increased
84    // complexity they introduce. The cost is calculated as:
85    // cost = (num_type_references * (num_type_references + 1)) / 2
86    //
87    // Take &self to access protocol config if needed in the future.
88    fn reference_cost_formula(&self, n: u64) -> Result<u64, ExecutionError> {
89        let Some(n_succ) = n.checked_add(1) else {
90            invariant_violation!("u64 overflow when calculating type reference cost")
91        };
92        Ok(n.saturating_mul(n_succ) / 2)
93    }
94
95    // Charge gas using a point charge mechanism based on the cumulative number of units charged so
96    // far.
97    fn charge(&mut self, amount: u64) -> Result<(), ExecutionError> {
98        debug_assert!(amount > 0);
99        self.charger
100            .move_gas_status_mut()
101            .deduct_gas(amount.into())
102            .map_err(Self::gas_error)
103    }
104
105    fn gas_error<E>(e: E) -> ExecutionError
106    where
107        E: Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
108    {
109        ExecutionError::new_with_source(ExecutionErrorKind::InsufficientGas, e)
110    }
111}