gear_core_backend/
error.rs

1// This file is part of Gear.
2
3// Copyright (C) 2023-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
19use actor_system_error::actor_system_error;
20use codec::{Decode, Encode};
21use gear_core::{
22    gas::{ChargeError, CounterType},
23    ids::ProgramId,
24    message::MessageWaitedType,
25    str::LimitedStr,
26};
27use gear_core_errors::ExtError as FallibleExtError;
28
29actor_system_error! {
30    pub type TerminationReason = ActorSystemError<ActorTerminationReason, SystemTerminationReason>;
31}
32
33#[derive(Debug, Clone, Eq, PartialEq, derive_more::From)]
34pub enum UndefinedTerminationReason {
35    Actor(ActorTerminationReason),
36    System(SystemTerminationReason),
37    /// Undefined reason because we need access to counters owner trait for RI.
38    ProcessAccessErrorResourcesExceed,
39}
40
41impl UndefinedTerminationReason {
42    pub fn define(self, current_counter: CounterType) -> TerminationReason {
43        match self {
44            Self::Actor(r) => r.into(),
45            Self::System(r) => r.into(),
46            Self::ProcessAccessErrorResourcesExceed => {
47                ActorTerminationReason::from(current_counter).into()
48            }
49        }
50    }
51}
52
53impl From<ChargeError> for UndefinedTerminationReason {
54    fn from(err: ChargeError) -> Self {
55        match err {
56            ChargeError::GasLimitExceeded => {
57                ActorTerminationReason::Trap(TrapExplanation::GasLimitExceeded).into()
58            }
59            ChargeError::GasAllowanceExceeded => {
60                ActorTerminationReason::GasAllowanceExceeded.into()
61            }
62        }
63    }
64}
65
66impl From<TrapExplanation> for UndefinedTerminationReason {
67    fn from(trap: TrapExplanation) -> Self {
68        ActorTerminationReason::Trap(trap).into()
69    }
70}
71
72impl<E: BackendSyscallError> From<E> for UndefinedTerminationReason {
73    fn from(err: E) -> Self {
74        err.into_termination_reason()
75    }
76}
77
78#[derive(Decode, Encode, Debug, PartialEq, Eq, PartialOrd, Ord, Clone, derive_more::From)]
79#[codec(crate = codec)]
80pub enum ActorTerminationReason {
81    Exit(ProgramId),
82    Leave,
83    Success,
84    Wait(Option<u32>, MessageWaitedType),
85    GasAllowanceExceeded,
86    #[from]
87    Trap(TrapExplanation),
88}
89
90impl From<CounterType> for ActorTerminationReason {
91    fn from(counter_type: CounterType) -> Self {
92        match counter_type {
93            CounterType::GasLimit => Self::Trap(TrapExplanation::GasLimitExceeded),
94            CounterType::GasAllowance => Self::GasAllowanceExceeded,
95        }
96    }
97}
98
99/// Non-actor related termination reason.
100///
101/// ### NOTICE:
102/// It's currently unused, but is left as a stub, until
103/// further massive errors refactoring is done.
104#[derive(Debug, Clone, Eq, PartialEq, derive_more::Display)]
105pub struct SystemTerminationReason;
106
107/// Execution error in infallible syscall.
108#[derive(
109    Decode,
110    Encode,
111    Debug,
112    Clone,
113    Eq,
114    PartialEq,
115    PartialOrd,
116    Ord,
117    derive_more::Display,
118    derive_more::From,
119)]
120#[codec(crate = codec)]
121pub enum UnrecoverableExecutionError {
122    #[display(fmt = "Invalid debug string passed in `gr_debug` syscall")]
123    InvalidDebugString,
124    #[display(fmt = "Not enough gas for operation")]
125    NotEnoughGas,
126    #[display(fmt = "Length is overflowed to read payload")]
127    TooBigReadLen,
128    #[display(fmt = "Cannot take data in payload range from message with size")]
129    ReadWrongRange,
130    #[display(fmt = "Unsupported version of environment variables encountered")]
131    UnsupportedEnvVarsVersion,
132}
133
134/// Memory error in infallible syscall.
135#[derive(
136    Decode,
137    Encode,
138    Debug,
139    Clone,
140    Eq,
141    PartialEq,
142    PartialOrd,
143    Ord,
144    derive_more::Display,
145    derive_more::From,
146)]
147#[codec(crate = codec)]
148pub enum UnrecoverableMemoryError {
149    /// The error occurs in attempt to access memory outside wasm program memory.
150    #[display(fmt = "Trying to access memory outside wasm program memory")]
151    AccessOutOfBounds,
152    /// The error occurs, when program tries to allocate in block-chain runtime more memory than allowed.
153    #[display(fmt = "Trying to allocate more memory in block-chain runtime than allowed")]
154    RuntimeAllocOutOfBounds,
155}
156
157/// Wait error in infallible syscall.
158#[derive(
159    Decode,
160    Encode,
161    Debug,
162    Clone,
163    Eq,
164    PartialEq,
165    PartialOrd,
166    Ord,
167    derive_more::Display,
168    derive_more::From,
169)]
170#[codec(crate = codec)]
171pub enum UnrecoverableWaitError {
172    /// An error occurs in attempt to wait for or wait up to zero blocks.
173    #[display(fmt = "Waiting duration cannot be zero")]
174    ZeroDuration,
175    /// An error occurs in attempt to wait after reply sent.
176    #[display(fmt = "`wait()` is not allowed after reply sent")]
177    WaitAfterReply,
178}
179
180#[derive(
181    Decode,
182    Encode,
183    Debug,
184    Clone,
185    Eq,
186    PartialEq,
187    PartialOrd,
188    Ord,
189    derive_more::Display,
190    derive_more::From,
191)]
192#[codec(crate = codec)]
193pub enum UnrecoverableExtError {
194    #[display(fmt = "Execution error: {_0}")]
195    Execution(UnrecoverableExecutionError),
196    #[display(fmt = "Memory error: {_0}")]
197    Memory(UnrecoverableMemoryError),
198    #[display(fmt = "Waiting error: {_0}")]
199    Wait(UnrecoverableWaitError),
200}
201
202#[derive(
203    Decode,
204    Encode,
205    Debug,
206    Clone,
207    PartialEq,
208    Eq,
209    PartialOrd,
210    Ord,
211    derive_more::Display,
212    derive_more::From,
213)]
214#[codec(crate = codec)]
215pub enum TrapExplanation {
216    /// An error occurs in attempt to charge more gas than available during execution.
217    #[display(fmt = "Not enough gas to continue execution")]
218    GasLimitExceeded,
219    /// An error occurs in attempt to call forbidden syscall.
220    #[display(fmt = "Unable to call a forbidden function")]
221    ForbiddenFunction,
222    /// The error occurs when a program tries to allocate more memory than
223    /// allowed.
224    #[display(fmt = "Trying to allocate more wasm program memory than allowed")]
225    ProgramAllocOutOfBounds,
226    #[display(fmt = "Syscall unrecoverable error: {_0}")]
227    UnrecoverableExt(UnrecoverableExtError),
228    #[display(fmt = "Panic occurred: {_0}")]
229    Panic(LimitedStr<'static>),
230    #[display(fmt = "Stack limit exceeded")]
231    StackLimitExceeded,
232    #[display(fmt = "Reason is unknown. Possibly `unreachable` instruction is occurred")]
233    Unknown,
234}
235
236/// Error returned by fallible syscall.
237#[derive(Debug, Clone)]
238pub enum RunFallibleError {
239    UndefinedTerminationReason(UndefinedTerminationReason),
240    FallibleExt(FallibleExtError),
241}
242
243impl<E> From<E> for RunFallibleError
244where
245    E: BackendSyscallError,
246{
247    fn from(err: E) -> Self {
248        err.into_run_fallible_error()
249    }
250}
251
252/// A trait for conversion of the externalities API error
253/// to `UndefinedTerminationReason` and `RunFallibleError`.
254pub trait BackendSyscallError: Sized {
255    fn into_termination_reason(self) -> UndefinedTerminationReason;
256
257    fn into_run_fallible_error(self) -> RunFallibleError;
258}
259
260// TODO: consider to remove this trait and use Result<Result<Page, AllocError>, GasError> instead #2571
261/// A trait for conversion of the externalities memory management error to api error.
262///
263/// If the conversion fails, then `Self` is returned in the `Err` variant.
264pub trait BackendAllocSyscallError: Sized {
265    type ExtError: BackendSyscallError;
266
267    fn into_backend_error(self) -> Result<Self::ExtError, Self>;
268}