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