core_processor/
common.rs

1// This file is part of Gear.
2
3// Copyright (C) 2021-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 structures for processing.
20
21use crate::{context::SystemReservationContext, precharge::PreChargeGasOperation};
22use actor_system_error::actor_system_error;
23use alloc::{collections::BTreeMap, string::String, vec::Vec};
24use gear_core::{
25    code::{CodeMetadata, InstrumentedCode},
26    env::MessageWaitedType,
27    gas::{GasAllowanceCounter, GasAmount, GasCounter},
28    ids::{ActorId, CodeId, MessageId, ReservationId},
29    memory::{MemoryError, MemorySetupError, PageBuf},
30    message::{ContextStore, Dispatch, IncomingDispatch, StoredDispatch},
31    pages::{GearPage, WasmPage, WasmPagesAmount, numerated::tree::IntervalsTree},
32    program::MemoryInfix,
33    reservation::{GasReservationMap, GasReserver},
34};
35pub use gear_core_backend::error::TrapExplanation;
36use gear_core_backend::{env::SystemEnvironmentError, error::SystemTerminationReason};
37use gear_core_errors::{SignalCode, SimpleExecutionError};
38use parity_scale_codec::{Decode, Encode};
39
40/// Kind of the dispatch result.
41#[derive(Clone)]
42pub enum DispatchResultKind {
43    /// Successful dispatch
44    Success,
45    /// Trap dispatch.
46    Trap(TrapExplanation),
47    /// Wait dispatch.
48    Wait(Option<u32>, MessageWaitedType),
49    /// Exit dispatch.
50    Exit(ActorId),
51    /// Gas allowance exceed.
52    GasAllowanceExceed,
53}
54
55/// Result of the specific dispatch.
56pub struct DispatchResult {
57    /// Kind of the dispatch.
58    pub kind: DispatchResultKind,
59    /// Program id of actor which was executed.
60    pub program_id: ActorId,
61    /// Context store after execution.
62    pub context_store: ContextStore,
63    /// List of generated messages.
64    pub generated_dispatches: Vec<(Dispatch, u32, Option<ReservationId>)>,
65    /// List of messages that should be woken.
66    pub awakening: Vec<(MessageId, u32)>,
67    /// List of reply deposits to be provided.
68    pub reply_deposits: Vec<(MessageId, u64)>,
69    /// New programs to be created with additional data (corresponding code hash and init message id).
70    pub program_candidates: BTreeMap<CodeId, Vec<(MessageId, ActorId)>>,
71    /// Gas amount after execution.
72    pub gas_amount: GasAmount,
73    /// Gas amount programs reserved.
74    pub gas_reserver: Option<GasReserver>,
75    /// System reservation context.
76    pub system_reservation_context: SystemReservationContext,
77    /// Page updates.
78    pub page_update: BTreeMap<GearPage, PageBuf>,
79    /// New allocations set for program if it has been changed.
80    pub allocations: Option<IntervalsTree<WasmPage>>,
81    /// Whether this execution sent out a reply.
82    pub reply_sent: bool,
83}
84
85impl DispatchResult {
86    /// Create partially initialized instance with the kind
87    /// representing Success.
88    pub fn success(
89        dispatch: &IncomingDispatch,
90        program_id: ActorId,
91        gas_amount: GasAmount,
92    ) -> Self {
93        let system_reservation_context = SystemReservationContext::from_dispatch(dispatch);
94
95        Self {
96            kind: DispatchResultKind::Success,
97            program_id,
98            context_store: Default::default(),
99            generated_dispatches: Default::default(),
100            awakening: Default::default(),
101            reply_deposits: Default::default(),
102            program_candidates: Default::default(),
103            gas_amount,
104            gas_reserver: None,
105            system_reservation_context,
106            page_update: Default::default(),
107            allocations: Default::default(),
108            // This function is only used to generate a dispatch result if nothing is executed,
109            // therefore reply_sent will always be false
110            reply_sent: false,
111        }
112    }
113}
114
115/// Possible variants of the [`DispatchResult`] if the latter contains value.
116#[derive(Debug)]
117pub enum SuccessfulDispatchResultKind {
118    /// Process dispatch as exit
119    Exit(ActorId),
120    /// Process dispatch as wait
121    Wait(Option<u32>, MessageWaitedType),
122    /// Process dispatch as success
123    Success,
124}
125
126/// Dispatch outcome of the specific message.
127#[derive(Clone, Debug, Encode, Decode)]
128pub enum DispatchOutcome {
129    /// Message was a exit.
130    Exit {
131        /// Id of the program that was successfully exited.
132        program_id: ActorId,
133    },
134    /// Message was an initialization success.
135    InitSuccess {
136        /// Id of the program that was successfully initialized.
137        program_id: ActorId,
138    },
139    /// Message was an initialization failure.
140    InitFailure {
141        /// Program that was failed initializing.
142        program_id: ActorId,
143        /// Source of the init message. Funds inheritor.
144        origin: ActorId,
145        /// Reason of the fail.
146        reason: String,
147    },
148    /// Message was a trap.
149    MessageTrap {
150        /// Program that was failed.
151        program_id: ActorId,
152        /// Reason of the fail.
153        trap: String,
154    },
155    /// Message was a success.
156    Success,
157    /// Message was processed, but not executed
158    NoExecution,
159}
160
161/// Journal record for the state update.
162#[derive(Clone, Debug, Encode, Decode)]
163pub enum JournalNote {
164    /// Message was successfully dispatched.
165    MessageDispatched {
166        /// Message id of dispatched message.
167        message_id: MessageId,
168        /// Source of the dispatched message.
169        source: ActorId,
170        /// Outcome of the processing.
171        outcome: DispatchOutcome,
172    },
173    /// Some gas was burned.
174    GasBurned {
175        /// Message id in which gas was burned.
176        message_id: MessageId,
177        /// Amount of gas burned.
178        amount: u64,
179        /// Is execution panicked (`gr_panic` syscall called).
180        is_panic: bool,
181    },
182    /// Exit the program.
183    ExitDispatch {
184        /// Id of the program called `exit`.
185        id_exited: ActorId,
186        /// Address where all remaining value of the program should
187        /// be transferred to.
188        value_destination: ActorId,
189    },
190    /// Message was handled and no longer exists.
191    ///
192    /// This should be the last update involving this message id.
193    MessageConsumed(MessageId),
194    /// Message was generated.
195    SendDispatch {
196        /// Message id of the message that generated this message.
197        message_id: MessageId,
198        /// New message with entry point that was generated.
199        dispatch: Dispatch,
200        /// Amount of blocks to wait before sending.
201        delay: u32,
202        /// Whether use supply from reservation or current message.
203        reservation: Option<ReservationId>,
204    },
205    /// Put this dispatch in the wait list.
206    WaitDispatch {
207        /// Stored dispatch to be inserted into Waitlist.
208        dispatch: StoredDispatch,
209        /// Expected duration of holding.
210        duration: Option<u32>,
211        /// If this message is waiting for its reincarnation.
212        waited_type: MessageWaitedType,
213    },
214    /// Wake particular message.
215    WakeMessage {
216        /// Message which has initiated wake.
217        message_id: MessageId,
218        /// Program which has initiated wake.
219        program_id: ActorId,
220        /// Message that should be woken.
221        awakening_id: MessageId,
222        /// Amount of blocks to wait before waking.
223        delay: u32,
224    },
225    /// Update page.
226    UpdatePage {
227        /// Program that owns the page.
228        program_id: ActorId,
229        /// Number of the page.
230        page_number: GearPage,
231        /// New data of the page.
232        data: PageBuf,
233    },
234    /// Update allocations set note.
235    /// And also removes data for pages which is not in allocations set now.
236    UpdateAllocations {
237        /// Program id.
238        program_id: ActorId,
239        /// New allocations set for the program.
240        allocations: IntervalsTree<WasmPage>,
241    },
242    /// Send value
243    SendValue {
244        /// Value sender
245        from: ActorId,
246        /// Value beneficiary,
247        to: ActorId,
248        /// Value amount
249        value: u128,
250        /// If to send locked value.
251        locked: bool,
252    },
253    /// Store programs requested by user to be initialized later
254    StoreNewPrograms {
255        /// Current program id.
256        program_id: ActorId,
257        /// Code hash used to create new programs with ids in `candidates` field
258        code_id: CodeId,
259        /// Collection of program candidate ids and their init message ids.
260        candidates: Vec<(MessageId, ActorId)>,
261    },
262    /// Stop processing queue.
263    StopProcessing {
264        /// Pushes StoredDispatch back to the top of the queue.
265        dispatch: StoredDispatch,
266        /// Decreases gas allowance by that amount, burned for processing try.
267        gas_burned: u64,
268    },
269    /// Reserve gas.
270    ReserveGas {
271        /// Message from which gas is reserved.
272        message_id: MessageId,
273        /// Reservation ID
274        reservation_id: ReservationId,
275        /// Program which contains reservation.
276        program_id: ActorId,
277        /// Amount of reserved gas.
278        amount: u64,
279        /// How many blocks reservation will live.
280        duration: u32,
281    },
282    /// Unreserve gas.
283    UnreserveGas {
284        /// Reservation ID
285        reservation_id: ReservationId,
286        /// Program which contains reservation.
287        program_id: ActorId,
288        /// Block number until reservation will live.
289        expiration: u32,
290    },
291    /// Update gas reservation map in program.
292    UpdateGasReservations {
293        /// Program whose map will be updated.
294        program_id: ActorId,
295        /// Map with reservations.
296        reserver: GasReserver,
297    },
298    /// Do system reservation.
299    SystemReserveGas {
300        /// Message ID which system reservation will be made from.
301        message_id: MessageId,
302        /// Amount of reserved gas.
303        amount: u64,
304    },
305    /// Do system unreservation in case it is created but not used.
306    SystemUnreserveGas {
307        /// Message ID which system reservation was made from.
308        message_id: MessageId,
309    },
310    /// Send signal.
311    SendSignal {
312        /// Message ID which system reservation was made from.
313        message_id: MessageId,
314        /// Program ID which signal will be sent to.
315        destination: ActorId,
316        /// Simple signal error.
317        code: SignalCode,
318    },
319    /// Create deposit for future reply.
320    ReplyDeposit {
321        /// Message id of the message that generated this message.
322        message_id: MessageId,
323        /// Future reply id to be sponsored.
324        future_reply_id: MessageId,
325        /// Amount of gas for reply.
326        amount: u64,
327    },
328}
329
330/// Journal handler.
331///
332/// Something that can update state.
333pub trait JournalHandler {
334    /// Process message dispatch.
335    fn message_dispatched(
336        &mut self,
337        message_id: MessageId,
338        source: ActorId,
339        outcome: DispatchOutcome,
340    );
341    /// Process gas burned.
342    fn gas_burned(&mut self, message_id: MessageId, amount: u64);
343    /// Process exit dispatch.
344    fn exit_dispatch(&mut self, id_exited: ActorId, value_destination: ActorId);
345    /// Process message consumed.
346    fn message_consumed(&mut self, message_id: MessageId);
347    /// Process send dispatch.
348    fn send_dispatch(
349        &mut self,
350        message_id: MessageId,
351        dispatch: Dispatch,
352        delay: u32,
353        reservation: Option<ReservationId>,
354    );
355    /// Process send message.
356    fn wait_dispatch(
357        &mut self,
358        dispatch: StoredDispatch,
359        duration: Option<u32>,
360        waited_type: MessageWaitedType,
361    );
362    /// Process send message.
363    fn wake_message(
364        &mut self,
365        message_id: MessageId,
366        program_id: ActorId,
367        awakening_id: MessageId,
368        delay: u32,
369    );
370    /// Process page update.
371    fn update_pages_data(&mut self, program_id: ActorId, pages_data: BTreeMap<GearPage, PageBuf>);
372    /// Process [JournalNote::UpdateAllocations].
373    fn update_allocations(&mut self, program_id: ActorId, allocations: IntervalsTree<WasmPage>);
374    /// Send value.
375    fn send_value(&mut self, from: ActorId, to: ActorId, value: u128, locked: bool);
376    /// Store new programs in storage.
377    ///
378    /// Program ids are ids of _potential_ (planned to be initialized) programs.
379    fn store_new_programs(
380        &mut self,
381        program_id: ActorId,
382        code_id: CodeId,
383        candidates: Vec<(MessageId, ActorId)>,
384    );
385    /// Stop processing queue.
386    ///
387    /// Pushes StoredDispatch back to the top of the queue and decreases gas allowance.
388    fn stop_processing(&mut self, dispatch: StoredDispatch, gas_burned: u64);
389    /// Reserve gas.
390    fn reserve_gas(
391        &mut self,
392        message_id: MessageId,
393        reservation_id: ReservationId,
394        program_id: ActorId,
395        amount: u64,
396        bn: u32,
397    );
398    /// Unreserve gas.
399    fn unreserve_gas(
400        &mut self,
401        reservation_id: ReservationId,
402        program_id: ActorId,
403        expiration: u32,
404    );
405    /// Update gas reservations.
406    fn update_gas_reservation(&mut self, program_id: ActorId, reserver: GasReserver);
407    /// Do system reservation.
408    fn system_reserve_gas(&mut self, message_id: MessageId, amount: u64);
409    /// Do system unreservation.
410    fn system_unreserve_gas(&mut self, message_id: MessageId);
411    /// Send system signal.
412    fn send_signal(&mut self, message_id: MessageId, destination: ActorId, code: SignalCode);
413    /// Create deposit for future reply.
414    fn reply_deposit(&mut self, message_id: MessageId, future_reply_id: MessageId, amount: u64);
415}
416
417actor_system_error! {
418    /// Execution error.
419    pub type ExecutionError = ActorSystemError<ActorExecutionError, SystemExecutionError>;
420}
421
422/// Actor execution error.
423#[derive(Debug, derive_more::Display)]
424#[display("{reason}")]
425pub struct ActorExecutionError {
426    /// Gas amount of the execution.
427    pub gas_amount: GasAmount,
428    /// Error text.
429    pub reason: ActorExecutionErrorReplyReason,
430}
431
432/// Reason of execution error
433#[derive(Debug, PartialEq, Eq, derive_more::Display)]
434pub enum ActorExecutionErrorReplyReason {
435    /// Not enough gas to perform an operation during precharge.
436    #[display("Not enough gas to {_0}")]
437    PreChargeGasLimitExceeded(PreChargeGasOperation),
438    /// Backend error
439    #[display("Environment error: <host error stripped>")]
440    Environment,
441    /// Trap explanation
442    Trap(TrapExplanation),
443}
444
445impl ActorExecutionErrorReplyReason {
446    /// Convert self into [`gear_core_errors::SimpleExecutionError`].
447    pub fn as_simple(&self) -> SimpleExecutionError {
448        match self {
449            Self::PreChargeGasLimitExceeded(_) => SimpleExecutionError::RanOutOfGas,
450            Self::Trap(expl) => match expl {
451                TrapExplanation::GasLimitExceeded => SimpleExecutionError::RanOutOfGas,
452                TrapExplanation::ForbiddenFunction | TrapExplanation::UnrecoverableExt(_) => {
453                    SimpleExecutionError::BackendError
454                }
455                TrapExplanation::ProgramAllocOutOfBounds => SimpleExecutionError::MemoryOverflow,
456                TrapExplanation::Panic(_) => SimpleExecutionError::UserspacePanic,
457                TrapExplanation::StackLimitExceeded => SimpleExecutionError::StackLimitExceeded,
458                TrapExplanation::Unknown => SimpleExecutionError::UnreachableInstruction,
459            },
460            Self::Environment => SimpleExecutionError::Unsupported,
461        }
462    }
463}
464
465/// System execution error
466#[derive(Debug, derive_more::Display, derive_more::From)]
467pub enum SystemExecutionError {
468    /// Incorrect memory parameters
469    #[display("Memory parameters error: {_0}")]
470    MemoryParams(MemorySetupError),
471    /// Environment error
472    #[display("Backend error: {_0}")]
473    Environment(SystemEnvironmentError),
474    /// Termination reason
475    #[display("Syscall function error: {_0}")]
476    UndefinedTerminationReason(SystemTerminationReason),
477    /// Error during `into_ext_info()` call
478    #[display("`into_ext_info()` error: {_0}")]
479    IntoExtInfo(MemoryError),
480    // TODO: uncomment when #3751
481    // /// Incoming dispatch store has too many outgoing messages total bytes.
482    // #[display("Incoming dispatch store has too many outgoing messages total bytes")]
483    // MessageStoreOutgoingBytesOverflow,
484}
485
486/// Actor.
487#[derive(Clone, Debug)]
488pub struct Actor {
489    /// Program value balance.
490    pub balance: u128,
491    /// Destination program.
492    pub destination_program: ActorId,
493    /// Executable actor data
494    pub executable_data: ExecutableActorData,
495}
496
497/// Executable actor data.
498#[derive(Clone, Debug)]
499pub struct ExecutableActorData {
500    /// Set of wasm pages, which are allocated by the program.
501    pub allocations: IntervalsTree<WasmPage>,
502    /// The infix of memory pages in a storage.
503    pub memory_infix: MemoryInfix,
504    /// Gas reservation map.
505    pub gas_reservation_map: GasReservationMap,
506}
507
508/// Executable allocations data.
509#[derive(Clone, Debug)]
510pub struct ReservationsAndMemorySize {
511    /// Amount of reservations can exist for 1 program.
512    pub max_reservations: u64,
513    /// Size of wasm memory buffer which must be created in execution environment
514    pub memory_size: WasmPagesAmount,
515}
516
517/// Program.
518#[derive(Clone, Debug)]
519pub(crate) struct Program {
520    /// Program id.
521    pub id: ActorId,
522    /// Memory infix.
523    pub memory_infix: MemoryInfix,
524    /// Instrumented code.
525    pub instrumented_code: InstrumentedCode,
526    /// Code metadata.
527    pub code_metadata: CodeMetadata,
528    /// Allocations.
529    pub allocations: IntervalsTree<WasmPage>,
530}
531
532/// Execution context.
533#[derive(Debug)]
534pub(crate) struct WasmExecutionContext {
535    /// A counter for gas.
536    pub gas_counter: GasCounter,
537    /// A counter for gas allowance.
538    pub gas_allowance_counter: GasAllowanceCounter,
539    /// Gas reserver.
540    pub gas_reserver: GasReserver,
541    /// Program to be executed.
542    pub program: Program,
543    /// Size of the memory block.
544    pub memory_size: WasmPagesAmount,
545}