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