builtins_common/
lib.rs

1// This file is part of Gear.
2
3// Copyright (C) 2025 Gear Technologies Inc.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19//! Common utilities for gear protocol built-ins
20
21#![no_std]
22
23extern crate alloc;
24
25/// Common utilities for gear protocol BLS12-381 built-in.
26#[cfg(any(feature = "bls12-381", feature = "bls12-381-std"))]
27pub mod bls12_381;
28
29/// Common utilities for gear protocol eth-bridge built-in.
30#[cfg(any(feature = "eth-bridge", feature = "eth-bridge-std"))]
31pub mod eth_bridge;
32
33use gear_core::{
34    gas::{ChargeResult, GasAllowanceCounter, GasAmount, GasCounter},
35    limited::LimitedStr,
36};
37use parity_scale_codec::{Decode, Encode};
38
39/// A builtin actor execution context. Primarily used to track gas usage.
40#[derive(Debug)]
41pub struct BuiltinContext {
42    pub(crate) gas_counter: GasCounter,
43    pub(crate) gas_allowance_counter: GasAllowanceCounter,
44}
45
46impl BuiltinContext {
47    /// Creates a new `BuiltinContext` with the specified initial gas and gas allowance.
48    pub fn new(counter_initial: u64, allowance_initial: u64) -> Self {
49        Self {
50            gas_counter: GasCounter::new(counter_initial),
51            gas_allowance_counter: GasAllowanceCounter::new(allowance_initial),
52        }
53    }
54
55    /// Tries to charge the gas amount from the gas counters.
56    pub fn try_charge_gas(&mut self, amount: u64) -> Result<(), BuiltinActorError> {
57        if self.gas_counter.charge_if_enough(amount) == ChargeResult::NotEnough {
58            return Err(BuiltinActorError::InsufficientGas);
59        }
60
61        if self.gas_allowance_counter.charge_if_enough(amount) == ChargeResult::NotEnough {
62            return Err(BuiltinActorError::GasAllowanceExceeded);
63        }
64
65        Ok(())
66    }
67
68    /// Checks if an amount of gas can be charged without actually modifying the inner counters.
69    pub fn can_charge_gas(&self, amount: u64) -> Result<(), BuiltinActorError> {
70        if self.gas_counter.left() < amount {
71            return Err(BuiltinActorError::InsufficientGas);
72        }
73
74        if self.gas_allowance_counter.left() < amount {
75            return Err(BuiltinActorError::GasAllowanceExceeded);
76        }
77
78        Ok(())
79    }
80
81    /// Converts the current gas counter to a `GasAmount`.
82    pub fn to_gas_amount(&self) -> GasAmount {
83        self.gas_counter.to_amount()
84    }
85}
86
87/// Built-in actor error type
88#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, derive_more::Display)]
89pub enum BuiltinActorError {
90    /// Occurs if the underlying call has the weight greater than the `gas_limit`.
91    #[display("Not enough gas supplied")]
92    InsufficientGas,
93    /// Occurs if the dispatch's value is less than the minimum required value.
94    #[display("Not enough value supplied")]
95    InsufficientValue,
96    /// Occurs if the dispatch's message can't be decoded into a known type.
97    #[display("Failure to decode message")]
98    DecodingError,
99    /// Actor's inner error encoded as a String.
100    #[display("Builtin execution resulted in error: {_0}")]
101    Custom(LimitedStr<'static>),
102    /// Occurs if a builtin actor execution does not fit in the current block.
103    #[display("Block gas allowance exceeded")]
104    GasAllowanceExceeded,
105    /// Occurs when aggregation of G1 points is requested with an empty list.
106    #[display("Empty G1 points list")]
107    EmptyG1PointsList,
108    /// Occurs when creating a `MapToCurveBasedHasher` for mapping a message
109    /// to G2Affine-point fails.
110    #[display("Failed to create `MapToCurveBasedHasher`")]
111    MapperCreationError,
112    /// Occurs when mapping a message to a G2-point fails.
113    #[display("Failed to map a message to a G2-point")]
114    MessageMappingError,
115}
116
117impl BuiltinActorError {
118    /// Converts a `BuiltinActorError` to a u32 error code.
119    ///
120    /// Gear runtime-interface (RI) defines bls12-381 calls (aggregate g1 and map to g2), which return
121    /// the `BuiltinActorError`. So in order to pass the error from RI to the runtime, we convert
122    /// the error to a u32 code.
123    pub fn as_u32(&self) -> u32 {
124        match self {
125            BuiltinActorError::InsufficientGas => 0,
126            BuiltinActorError::InsufficientValue => 1,
127            BuiltinActorError::DecodingError => 2,
128            BuiltinActorError::Custom(_) => 3,
129            BuiltinActorError::GasAllowanceExceeded => 4,
130            BuiltinActorError::EmptyG1PointsList => 5,
131            BuiltinActorError::MapperCreationError => 6,
132            BuiltinActorError::MessageMappingError => 7,
133        }
134    }
135
136    /// Converts a u32 error code back to a `BuiltinActorError`.
137    ///
138    /// This function does the opposite conversion in comparison to `as_u32`.
139    pub fn from_u32(code: u32, custom_err_message: Option<&'static str>) -> Self {
140        match code {
141            0 => BuiltinActorError::InsufficientGas,
142            1 => BuiltinActorError::InsufficientValue,
143            2 => BuiltinActorError::DecodingError,
144            3 => BuiltinActorError::Custom(LimitedStr::from_small_str(
145                custom_err_message.unwrap_or("Unrecognized builtin actor error from RI call"),
146            )),
147            4 => BuiltinActorError::GasAllowanceExceeded,
148            5 => BuiltinActorError::EmptyG1PointsList,
149            6 => BuiltinActorError::MapperCreationError,
150            7 => BuiltinActorError::MessageMappingError,
151            _ => panic!("Invalid builtin-actor error code"),
152        }
153    }
154}