iota_verifier_latest/
meter.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use move_binary_format::errors::{PartialVMError, PartialVMResult};
6use move_bytecode_verifier_meter::{Meter, Scope};
7use move_core_types::vm_status::StatusCode;
8use move_vm_config::verifier::MeterConfig;
9
10struct IotaVerifierMeterBounds {
11    name: String,
12    ticks: u128,
13    max_ticks: Option<u128>,
14}
15
16impl IotaVerifierMeterBounds {
17    fn add(&mut self, ticks: u128) -> PartialVMResult<()> {
18        let max_ticks = self.max_ticks.unwrap_or(u128::MAX);
19
20        let new_ticks = self.ticks.saturating_add(ticks);
21        if new_ticks >= max_ticks {
22            return Err(PartialVMError::new(StatusCode::PROGRAM_TOO_COMPLEX)
23                    .with_message(format!(
24                        "program too complex. Ticks exceeded `{}` will exceed limits: `{} current + {} new > {} max`)",
25                        self.name, self.ticks, ticks, max_ticks
26                    )));
27        }
28        self.ticks = new_ticks;
29        Ok(())
30    }
31}
32
33pub struct IotaVerifierMeter {
34    transaction_bounds: IotaVerifierMeterBounds,
35    package_bounds: IotaVerifierMeterBounds,
36    module_bounds: IotaVerifierMeterBounds,
37    function_bounds: IotaVerifierMeterBounds,
38}
39
40impl IotaVerifierMeter {
41    pub fn new(config: MeterConfig) -> Self {
42        Self {
43            transaction_bounds: IotaVerifierMeterBounds {
44                name: "<unknown>".to_string(),
45                ticks: 0,
46                max_ticks: None,
47            },
48            package_bounds: IotaVerifierMeterBounds {
49                name: "<unknown>".to_string(),
50                ticks: 0,
51                max_ticks: config.max_per_pkg_meter_units,
52            },
53            module_bounds: IotaVerifierMeterBounds {
54                name: "<unknown>".to_string(),
55                ticks: 0,
56                max_ticks: config.max_per_mod_meter_units,
57            },
58            function_bounds: IotaVerifierMeterBounds {
59                name: "<unknown>".to_string(),
60                ticks: 0,
61                max_ticks: config.max_per_fun_meter_units,
62            },
63        }
64    }
65
66    fn get_bounds_mut(&mut self, scope: Scope) -> &mut IotaVerifierMeterBounds {
67        match scope {
68            Scope::Transaction => &mut self.transaction_bounds,
69            Scope::Package => &mut self.package_bounds,
70            Scope::Module => &mut self.module_bounds,
71            Scope::Function => &mut self.function_bounds,
72        }
73    }
74
75    fn get_bounds(&self, scope: Scope) -> &IotaVerifierMeterBounds {
76        match scope {
77            Scope::Transaction => &self.transaction_bounds,
78            Scope::Package => &self.package_bounds,
79            Scope::Module => &self.module_bounds,
80            Scope::Function => &self.function_bounds,
81        }
82    }
83
84    pub fn get_usage(&self, scope: Scope) -> u128 {
85        self.get_bounds(scope).ticks
86    }
87
88    pub fn get_limit(&self, scope: Scope) -> Option<u128> {
89        self.get_bounds(scope).max_ticks
90    }
91}
92
93impl Meter for IotaVerifierMeter {
94    fn enter_scope(&mut self, name: &str, scope: Scope) {
95        let bounds = self.get_bounds_mut(scope);
96        bounds.name = name.into();
97        bounds.ticks = 0;
98    }
99
100    fn transfer(&mut self, from: Scope, to: Scope, factor: f32) -> PartialVMResult<()> {
101        let ticks = (self.get_bounds_mut(from).ticks as f32 * factor) as u128;
102        self.add(to, ticks)
103    }
104
105    fn add(&mut self, scope: Scope, ticks: u128) -> PartialVMResult<()> {
106        self.get_bounds_mut(scope).add(ticks)
107    }
108}