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: ProgramId,
263        /// Value amount
264        value: u128,
265        /// If to send locked value.
266        locked: bool,
267    },
268    /// Store programs requested by user to be initialized later
269    StoreNewPrograms {
270        /// Current program id.
271        program_id: ProgramId,
272        /// Code hash used to create new programs with ids in `candidates` field
273        code_id: CodeId,
274        /// Collection of program candidate ids and their init message ids.
275        candidates: Vec<(MessageId, ProgramId)>,
276    },
277    /// Stop processing queue.
278    StopProcessing {
279        /// Pushes StoredDispatch back to the top of the queue.
280        dispatch: StoredDispatch,
281        /// Decreases gas allowance by that amount, burned for processing try.
282        gas_burned: u64,
283    },
284    /// Reserve gas.
285    ReserveGas {
286        /// Message from which gas is reserved.
287        message_id: MessageId,
288        /// Reservation ID
289        reservation_id: ReservationId,
290        /// Program which contains reservation.
291        program_id: ProgramId,
292        /// Amount of reserved gas.
293        amount: u64,
294        /// How many blocks reservation will live.
295        duration: u32,
296    },
297    /// Unreserve gas.
298    UnreserveGas {
299        /// Reservation ID
300        reservation_id: ReservationId,
301        /// Program which contains reservation.
302        program_id: ProgramId,
303        /// Block number until reservation will live.
304        expiration: u32,
305    },
306    /// Update gas reservation map in program.
307    UpdateGasReservations {
308        /// Program whose map will be updated.
309        program_id: ProgramId,
310        /// Map with reservations.
311        reserver: GasReserver,
312    },
313    /// Do system reservation.
314    SystemReserveGas {
315        /// Message ID which system reservation will be made from.
316        message_id: MessageId,
317        /// Amount of reserved gas.
318        amount: u64,
319    },
320    /// Do system unreservation in case it is created but not used.
321    SystemUnreserveGas {
322        /// Message ID which system reservation was made from.
323        message_id: MessageId,
324    },
325    /// Send signal.
326    SendSignal {
327        /// Message ID which system reservation was made from.
328        message_id: MessageId,
329        /// Program ID which signal will be sent to.
330        destination: ProgramId,
331        /// Simple signal error.
332        code: SignalCode,
333    },
334    /// Create deposit for future reply.
335    ReplyDeposit {
336        /// Message id of the message that generated this message.
337        message_id: MessageId,
338        /// Future reply id to be sponsored.
339        future_reply_id: MessageId,
340        /// Amount of gas for reply.
341        amount: u64,
342    },
343}
344
345/// Journal handler.
346///
347/// Something that can update state.
348pub trait JournalHandler {
349    /// Process message dispatch.
350    fn message_dispatched(
351        &mut self,
352        message_id: MessageId,
353        source: ProgramId,
354        outcome: DispatchOutcome,
355    );
356    /// Process gas burned.
357    fn gas_burned(&mut self, message_id: MessageId, amount: u64);
358    /// Process exit dispatch.
359    fn exit_dispatch(&mut self, id_exited: ProgramId, value_destination: ProgramId);
360    /// Process message consumed.
361    fn message_consumed(&mut self, message_id: MessageId);
362    /// Process send dispatch.
363    fn send_dispatch(
364        &mut self,
365        message_id: MessageId,
366        dispatch: Dispatch,
367        delay: u32,
368        reservation: Option<ReservationId>,
369    );
370    /// Process send message.
371    fn wait_dispatch(
372        &mut self,
373        dispatch: StoredDispatch,
374        duration: Option<u32>,
375        waited_type: MessageWaitedType,
376    );
377    /// Process send message.
378    fn wake_message(
379        &mut self,
380        message_id: MessageId,
381        program_id: ProgramId,
382        awakening_id: MessageId,
383        delay: u32,
384    );
385    /// Process page update.
386    fn update_pages_data(&mut self, program_id: ProgramId, pages_data: BTreeMap<GearPage, PageBuf>);
387    /// Process [JournalNote::UpdateAllocations].
388    fn update_allocations(&mut self, program_id: ProgramId, allocations: IntervalsTree<WasmPage>);
389    /// Send value.
390    fn send_value(&mut self, from: ProgramId, to: ProgramId, value: u128, locked: bool);
391    /// Store new programs in storage.
392    ///
393    /// Program ids are ids of _potential_ (planned to be initialized) programs.
394    fn store_new_programs(
395        &mut self,
396        program_id: ProgramId,
397        code_id: CodeId,
398        candidates: Vec<(MessageId, ProgramId)>,
399    );
400    /// Stop processing queue.
401    ///
402    /// Pushes StoredDispatch back to the top of the queue and decreases gas allowance.
403    fn stop_processing(&mut self, dispatch: StoredDispatch, gas_burned: u64);
404    /// Reserve gas.
405    fn reserve_gas(
406        &mut self,
407        message_id: MessageId,
408        reservation_id: ReservationId,
409        program_id: ProgramId,
410        amount: u64,
411        bn: u32,
412    );
413    /// Unreserve gas.
414    fn unreserve_gas(
415        &mut self,
416        reservation_id: ReservationId,
417        program_id: ProgramId,
418        expiration: u32,
419    );
420    /// Update gas reservations.
421    fn update_gas_reservation(&mut self, program_id: ProgramId, reserver: GasReserver);
422    /// Do system reservation.
423    fn system_reserve_gas(&mut self, message_id: MessageId, amount: u64);
424    /// Do system unreservation.
425    fn system_unreserve_gas(&mut self, message_id: MessageId);
426    /// Send system signal.
427    fn send_signal(&mut self, message_id: MessageId, destination: ProgramId, code: SignalCode);
428    /// Create deposit for future reply.
429    fn reply_deposit(&mut self, message_id: MessageId, future_reply_id: MessageId, amount: u64);
430}
431
432actor_system_error! {
433    /// Execution error.
434    pub type ExecutionError = ActorSystemError<ActorExecutionError, SystemExecutionError>;
435}
436
437/// Actor execution error.
438#[derive(Debug, derive_more::Display)]
439#[display("{reason}")]
440pub struct ActorExecutionError {
441    /// Gas amount of the execution.
442    pub gas_amount: GasAmount,
443    /// Error text.
444    pub reason: ActorExecutionErrorReplyReason,
445}
446
447/// Reason of execution error
448#[derive(Debug, PartialEq, Eq, derive_more::Display)]
449pub enum ActorExecutionErrorReplyReason {
450    /// Not enough gas to perform an operation during precharge.
451    #[display("Not enough gas to {_0}")]
452    PreChargeGasLimitExceeded(PreChargeGasOperation),
453    /// Backend error
454    #[display("Environment error: <host error stripped>")]
455    Environment,
456    /// Trap explanation
457    Trap(TrapExplanation),
458}
459
460impl ActorExecutionErrorReplyReason {
461    /// Convert self into [`gear_core_errors::SimpleExecutionError`].
462    pub fn as_simple(&self) -> SimpleExecutionError {
463        match self {
464            Self::PreChargeGasLimitExceeded(_) => SimpleExecutionError::RanOutOfGas,
465            Self::Trap(expl) => match expl {
466                TrapExplanation::GasLimitExceeded => SimpleExecutionError::RanOutOfGas,
467                TrapExplanation::ForbiddenFunction | TrapExplanation::UnrecoverableExt(_) => {
468                    SimpleExecutionError::BackendError
469                }
470                TrapExplanation::ProgramAllocOutOfBounds => SimpleExecutionError::MemoryOverflow,
471                TrapExplanation::Panic(_) => SimpleExecutionError::UserspacePanic,
472                TrapExplanation::StackLimitExceeded => SimpleExecutionError::StackLimitExceeded,
473                TrapExplanation::Unknown => SimpleExecutionError::UnreachableInstruction,
474            },
475            Self::Environment => SimpleExecutionError::Unsupported,
476        }
477    }
478}
479
480/// System execution error
481#[derive(Debug, derive_more::Display, derive_more::From)]
482pub enum SystemExecutionError {
483    /// Incorrect memory parameters
484    #[display("Memory parameters error: {_0}")]
485    MemoryParams(MemorySetupError),
486    /// Environment error
487    #[display("Backend error: {_0}")]
488    Environment(SystemEnvironmentError),
489    /// Termination reason
490    #[display("Syscall function error: {_0}")]
491    UndefinedTerminationReason(SystemTerminationReason),
492    /// Error during `into_ext_info()` call
493    #[display("`into_ext_info()` error: {_0}")]
494    IntoExtInfo(MemoryError),
495    // TODO: uncomment when #3751
496    // /// Incoming dispatch store has too many outgoing messages total bytes.
497    // #[display("Incoming dispatch store has too many outgoing messages total bytes")]
498    // MessageStoreOutgoingBytesOverflow,
499}
500
501/// Actor.
502#[derive(Clone, Debug)]
503pub struct Actor {
504    /// Program value balance.
505    pub balance: u128,
506    /// Destination program.
507    pub destination_program: ProgramId,
508    /// Executable actor data
509    pub executable_data: ExecutableActorData,
510}
511
512/// Executable actor data.
513#[derive(Clone, Debug)]
514pub struct ExecutableActorData {
515    /// Set of wasm pages, which are allocated by the program.
516    pub allocations: IntervalsTree<WasmPage>,
517    /// The infix of memory pages in a storage.
518    pub memory_infix: MemoryInfix,
519    /// Id of the program code.
520    pub code_id: CodeId,
521    /// Exported functions by the program code.
522    pub code_exports: BTreeSet<DispatchKind>,
523    /// Count of static memory pages.
524    pub static_pages: WasmPagesAmount,
525    /// Gas reservation map.
526    pub gas_reservation_map: GasReservationMap,
527}
528
529/// Program.
530#[derive(Clone, Debug)]
531pub(crate) struct Program {
532    /// Program id.
533    pub id: ProgramId,
534    /// Memory infix.
535    pub memory_infix: MemoryInfix,
536    /// Instrumented code.
537    pub code: InstrumentedCode,
538    /// Allocations.
539    pub allocations: IntervalsTree<WasmPage>,
540}
541
542/// Execution context.
543#[derive(Debug)]
544pub(crate) struct WasmExecutionContext {
545    /// A counter for gas.
546    pub gas_counter: GasCounter,
547    /// A counter for gas allowance.
548    pub gas_allowance_counter: GasAllowanceCounter,
549    /// Gas reserver.
550    pub gas_reserver: GasReserver,
551    /// Program to be executed.
552    pub program: Program,
553    /// Size of the memory block.
554    pub memory_size: WasmPagesAmount,
555}