arb_stylus/
meter.rs

1use crate::{config::PricingParams, error::Escape, ink::Ink};
2
3/// Names of the WASM globals used for ink metering.
4pub const STYLUS_INK_LEFT: &str = "stylus_ink_left";
5pub const STYLUS_INK_STATUS: &str = "stylus_ink_status";
6
7/// Names of the WASM globals used for stack depth checking.
8pub const STYLUS_STACK_LEFT: &str = "stylus_stack_left";
9
10/// The Stylus program entry point function name.
11pub const STYLUS_ENTRY_POINT: &str = "user_entrypoint";
12
13/// Default host I/O ink cost.
14pub const HOSTIO_INK: Ink = Ink(8400);
15
16/// State of the ink meter.
17#[derive(Clone, Copy, Debug, PartialEq, Eq)]
18pub enum MachineMeter {
19    Ready(Ink),
20    Exhausted,
21}
22
23impl MachineMeter {
24    pub fn ink(&self) -> Ink {
25        match self {
26            Self::Ready(ink) => *ink,
27            Self::Exhausted => Ink(0),
28        }
29    }
30
31    pub fn status(&self) -> u32 {
32        match self {
33            Self::Ready(_) => 0,
34            Self::Exhausted => 1,
35        }
36    }
37}
38
39/// Trait for machines that track ink consumption.
40pub trait MeteredMachine {
41    fn ink_left(&self) -> MachineMeter;
42    fn set_meter(&mut self, meter: MachineMeter);
43
44    fn ink_ready(&self) -> Result<Ink, Escape> {
45        match self.ink_left() {
46            MachineMeter::Ready(ink) => Ok(ink),
47            MachineMeter::Exhausted => Escape::out_of_ink(),
48        }
49    }
50
51    fn set_ink(&mut self, ink: Ink) {
52        self.set_meter(MachineMeter::Ready(ink));
53    }
54
55    fn buy_ink(&mut self, ink: Ink) -> Result<(), Escape> {
56        let current = self.ink_ready()?;
57        if current < ink {
58            self.set_meter(MachineMeter::Exhausted);
59            return Escape::out_of_ink();
60        }
61        self.set_meter(MachineMeter::Ready(current - ink));
62        Ok(())
63    }
64
65    fn require_ink(&mut self, ink: Ink) -> Result<(), Escape> {
66        let current = self.ink_ready()?;
67        if current < ink {
68            return Escape::out_of_ink();
69        }
70        Ok(())
71    }
72
73    fn pay_for_read(&mut self, bytes: u32) -> Result<(), Escape> {
74        self.buy_ink(crate::pricing::read_price(bytes))
75    }
76
77    fn pay_for_write(&mut self, bytes: u32) -> Result<(), Escape> {
78        self.buy_ink(crate::pricing::write_price(bytes))
79    }
80
81    fn pay_for_keccak(&mut self, bytes: u32) -> Result<(), Escape> {
82        self.buy_ink(crate::pricing::keccak_price(bytes))
83    }
84}
85
86/// Trait for machines that can convert between gas and ink.
87pub trait GasMeteredMachine: MeteredMachine {
88    fn pricing(&self) -> PricingParams;
89
90    fn buy_gas(&mut self, gas: u64) -> Result<(), Escape> {
91        let ink = self.pricing().gas_to_ink(crate::ink::Gas(gas));
92        self.buy_ink(ink)
93    }
94
95    fn require_gas(&mut self, gas: u64) -> Result<(), Escape> {
96        let ink = self.pricing().gas_to_ink(crate::ink::Gas(gas));
97        self.require_ink(ink)
98    }
99
100    fn pay_for_evm_log(&mut self, topics: u32, data_len: u32) -> Result<(), Escape> {
101        use crate::pricing::evm_gas;
102        let cost = (1 + topics as u64) * evm_gas::LOG_TOPIC_GAS;
103        let cost = cost.saturating_add(data_len as u64 * evm_gas::LOG_DATA_GAS);
104        self.buy_gas(cost)
105    }
106}
107
108/// Trait for machines that track stack depth.
109pub trait DepthCheckedMachine {
110    fn stack_left(&mut self) -> u32;
111    fn set_stack(&mut self, size: u32);
112}