gear_core_processor/
common.rs

1// This file is part of Gear.
2
3// Copyright (C) 2021-2023 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::{
22    executor::SystemPrepareMemoryError, precharge::PreChargeGasOperation, ActorPrepareMemoryError,
23};
24use actor_system_error::actor_system_error;
25use alloc::{
26    collections::{BTreeMap, BTreeSet},
27    string::String,
28    vec::Vec,
29};
30use gear_backend_common::{SystemReservationContext, SystemTerminationReason, TrapExplanation};
31use gear_core::{
32    gas::{GasAllowanceCounter, GasAmount, GasCounter},
33    ids::{CodeId, MessageId, ProgramId, ReservationId},
34    memory::{MemoryError, PageBuf},
35    message::{
36        ContextStore, Dispatch, DispatchKind, IncomingDispatch, MessageWaitedType, StoredDispatch,
37    },
38    pages::{GearPage, WasmPage},
39    program::Program,
40    reservation::{GasReservationMap, GasReserver},
41};
42use gear_core_errors::{SignalCode, SimpleExecutionError};
43use scale_info::scale::{self, 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    /// Map of program ids to paid blocks.
79    pub program_rents: BTreeMap<ProgramId, u32>,
80    /// Gas amount after execution.
81    pub gas_amount: GasAmount,
82    /// Gas amount programs reserved.
83    pub gas_reserver: Option<GasReserver>,
84    /// System reservation context.
85    pub system_reservation_context: SystemReservationContext,
86    /// Page updates.
87    pub page_update: BTreeMap<GearPage, PageBuf>,
88    /// New allocations set for program if it has been changed.
89    pub allocations: BTreeSet<WasmPage>,
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            program_rents: Default::default(),
132            gas_amount,
133            gas_reserver: None,
134            system_reservation_context,
135            page_update: Default::default(),
136            allocations: Default::default(),
137        }
138    }
139}
140
141/// Dispatch outcome of the specific message.
142#[derive(Clone, Debug)]
143pub enum DispatchOutcome {
144    /// Message was a exit.
145    Exit {
146        /// Id of the program that was successfully exited.
147        program_id: ProgramId,
148    },
149    /// Message was an initialization success.
150    InitSuccess {
151        /// Id of the program that was successfully initialized.
152        program_id: ProgramId,
153    },
154    /// Message was an initialization failure.
155    InitFailure {
156        /// Program that was failed initializing.
157        program_id: ProgramId,
158        /// Source of the init message. Funds inheritor.
159        origin: ProgramId,
160        /// Reason of the fail.
161        reason: String,
162        /// Flag defining was the program executed to fail its initialization.
163        executed: bool,
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)]
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: BTreeSet<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        /// Code hash used to create new programs with ids in `candidates` field
269        code_id: CodeId,
270        /// Collection of program candidate ids and their init message ids.
271        candidates: Vec<(MessageId, ProgramId)>,
272    },
273    /// Stop processing queue.
274    StopProcessing {
275        /// Pushes StoredDispatch back to the top of the queue.
276        dispatch: StoredDispatch,
277        /// Decreases gas allowance by that amount, burned for processing try.
278        gas_burned: u64,
279    },
280    /// Reserve gas.
281    ReserveGas {
282        /// Message from which gas is reserved.
283        message_id: MessageId,
284        /// Reservation ID
285        reservation_id: ReservationId,
286        /// Program which contains reservation.
287        program_id: ProgramId,
288        /// Amount of reserved gas.
289        amount: u64,
290        /// How many blocks reservation will live.
291        duration: u32,
292    },
293    /// Unreserve gas.
294    UnreserveGas {
295        /// Reservation ID
296        reservation_id: ReservationId,
297        /// Program which contains reservation.
298        program_id: ProgramId,
299        /// Block number until reservation will live.
300        expiration: u32,
301    },
302    /// Update gas reservation map in program.
303    UpdateGasReservations {
304        /// Program whose map will be updated.
305        program_id: ProgramId,
306        /// Map with reservations.
307        reserver: GasReserver,
308    },
309    /// Do system reservation.
310    SystemReserveGas {
311        /// Message ID which system reservation will be made from.
312        message_id: MessageId,
313        /// Amount of reserved gas.
314        amount: u64,
315    },
316    /// Do system unreservation in case it is created but not used.
317    SystemUnreserveGas {
318        /// Message ID which system reservation was made from.
319        message_id: MessageId,
320    },
321    /// Send signal.
322    SendSignal {
323        /// Message ID which system reservation was made from.
324        message_id: MessageId,
325        /// Program ID which signal will be sent to.
326        destination: ProgramId,
327        /// Simple signal error.
328        code: SignalCode,
329    },
330    /// Pay rent for the program.
331    PayProgramRent {
332        /// Rent payer.
333        payer: ProgramId,
334        /// Program whose rent will be paid.
335        program_id: ProgramId,
336        /// Amount of blocks to pay for.
337        block_count: u32,
338    },
339    /// Create deposit for future reply.
340    ReplyDeposit {
341        /// Message id of the message that generated this message.
342        message_id: MessageId,
343        /// Future reply id to be sponsored.
344        future_reply_id: MessageId,
345        /// Amount of gas for reply.
346        amount: u64,
347    },
348}
349
350/// Journal handler.
351///
352/// Something that can update state.
353pub trait JournalHandler {
354    /// Process message dispatch.
355    fn message_dispatched(
356        &mut self,
357        message_id: MessageId,
358        source: ProgramId,
359        outcome: DispatchOutcome,
360    );
361    /// Process gas burned.
362    fn gas_burned(&mut self, message_id: MessageId, amount: u64);
363    /// Process exit dispatch.
364    fn exit_dispatch(&mut self, id_exited: ProgramId, value_destination: ProgramId);
365    /// Process message consumed.
366    fn message_consumed(&mut self, message_id: MessageId);
367    /// Process send dispatch.
368    fn send_dispatch(
369        &mut self,
370        message_id: MessageId,
371        dispatch: Dispatch,
372        delay: u32,
373        reservation: Option<ReservationId>,
374    );
375    /// Process send message.
376    fn wait_dispatch(
377        &mut self,
378        dispatch: StoredDispatch,
379        duration: Option<u32>,
380        waited_type: MessageWaitedType,
381    );
382    /// Process send message.
383    fn wake_message(
384        &mut self,
385        message_id: MessageId,
386        program_id: ProgramId,
387        awakening_id: MessageId,
388        delay: u32,
389    );
390    /// Process page update.
391    fn update_pages_data(&mut self, program_id: ProgramId, pages_data: BTreeMap<GearPage, PageBuf>);
392    /// Process [JournalNote::UpdateAllocations].
393    fn update_allocations(&mut self, program_id: ProgramId, allocations: BTreeSet<WasmPage>);
394    /// Send value.
395    fn send_value(&mut self, from: ProgramId, to: Option<ProgramId>, value: u128);
396    /// Store new programs in storage.
397    ///
398    /// Program ids are ids of _potential_ (planned to be initialized) programs.
399    fn store_new_programs(&mut self, code_id: CodeId, candidates: Vec<(MessageId, ProgramId)>);
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    /// Pay rent for the program.
429    fn pay_program_rent(&mut self, payer: ProgramId, program_id: ProgramId, block_count: u32);
430    /// Create deposit for future reply.
431    fn reply_deposit(&mut self, message_id: MessageId, future_reply_id: MessageId, amount: u64);
432}
433
434actor_system_error! {
435    /// Execution error.
436    pub type ExecutionError = ActorSystemError<ActorExecutionError, SystemExecutionError>;
437}
438
439/// Actor execution error.
440#[derive(Debug, derive_more::Display)]
441#[display(fmt = "{reason}")]
442pub struct ActorExecutionError {
443    /// Gas amount of the execution.
444    pub gas_amount: GasAmount,
445    /// Error text.
446    pub reason: ActorExecutionErrorReplyReason,
447}
448
449/// Reason of execution error
450#[derive(Encode, Decode, Debug, PartialEq, Eq, PartialOrd, Ord, derive_more::Display)]
451#[codec(crate = scale)]
452pub enum ActorExecutionErrorReplyReason {
453    /// Not enough gas to perform an operation during precharge.
454    #[display(fmt = "Not enough gas to {_0}")]
455    PreChargeGasLimitExceeded(PreChargeGasOperation),
456    /// Prepare memory error
457    #[display(fmt = "{_0}")]
458    PrepareMemory(ActorPrepareMemoryError),
459    /// Backend error
460    #[display(fmt = "Environment error: <host error stripped>")]
461    Environment,
462    /// Trap explanation
463    #[display(fmt = "{_0}")]
464    Trap(TrapExplanation),
465}
466
467impl ActorExecutionErrorReplyReason {
468    /// Convert self into [`gear_core_errors::SimpleExecutionError`].
469    pub fn as_simple(&self) -> SimpleExecutionError {
470        match self {
471            Self::PreChargeGasLimitExceeded(_) => SimpleExecutionError::RanOutOfGas,
472            Self::PrepareMemory(_) | Self::Environment => SimpleExecutionError::Unsupported,
473            Self::Trap(expl) => match expl {
474                TrapExplanation::GasLimitExceeded => SimpleExecutionError::RanOutOfGas,
475                TrapExplanation::ForbiddenFunction | TrapExplanation::UnrecoverableExt(_) => {
476                    SimpleExecutionError::BackendError
477                }
478                TrapExplanation::ProgramAllocOutOfBounds => SimpleExecutionError::MemoryOverflow,
479                TrapExplanation::Panic(_) => SimpleExecutionError::UserspacePanic,
480                TrapExplanation::Unknown => SimpleExecutionError::UnreachableInstruction,
481            },
482        }
483    }
484}
485
486/// System execution error
487#[derive(Debug, derive_more::Display, derive_more::From)]
488pub enum SystemExecutionError {
489    /// Prepare memory error
490    #[from]
491    #[display(fmt = "Prepare memory: {_0}")]
492    PrepareMemory(SystemPrepareMemoryError),
493    /// Environment error
494    #[display(fmt = "Backend error: {_0}")]
495    Environment(String),
496    /// Termination reason
497    #[from]
498    #[display(fmt = "Syscall function error: {_0}")]
499    UndefinedTerminationReason(SystemTerminationReason),
500    /// Error during `into_ext_info()` call
501    #[display(fmt = "`into_ext_info()` error: {_0}")]
502    IntoExtInfo(MemoryError),
503}
504
505/// Actor.
506#[derive(Clone, Debug, Decode, Encode)]
507#[codec(crate = scale)]
508pub struct Actor {
509    /// Program value balance.
510    pub balance: u128,
511    /// Destination program.
512    pub destination_program: ProgramId,
513    /// Executable actor data
514    pub executable_data: Option<ExecutableActorData>,
515}
516
517/// Executable actor data.
518#[derive(Clone, Debug, Decode, Encode)]
519#[codec(crate = scale)]
520pub struct ExecutableActorData {
521    /// Set of dynamic wasm page numbers, which are allocated by the program.
522    pub allocations: BTreeSet<WasmPage>,
523    /// Set of gear pages numbers, which has data in storage.
524    pub pages_with_data: BTreeSet<GearPage>,
525    /// Id of the program code.
526    pub code_id: CodeId,
527    /// Exported functions by the program code.
528    pub code_exports: BTreeSet<DispatchKind>,
529    /// Count of static memory pages.
530    pub static_pages: WasmPage,
531    /// Flag indicates if the program is initialized.
532    pub initialized: bool,
533    /// Gas reservation map.
534    pub gas_reservation_map: GasReservationMap,
535}
536
537/// Execution context.
538#[derive(Debug)]
539pub struct WasmExecutionContext {
540    /// A counter for gas.
541    pub gas_counter: GasCounter,
542    /// A counter for gas allowance.
543    pub gas_allowance_counter: GasAllowanceCounter,
544    /// Gas reserver.
545    pub gas_reserver: GasReserver,
546    /// Program to be executed.
547    pub program: Program,
548    /// Memory pages with initial data.
549    pub pages_initial_data: BTreeMap<GearPage, PageBuf>,
550    /// Size of the memory block.
551    pub memory_size: WasmPage,
552}
553
554/// Struct with dispatch and counters charged for program data.
555#[derive(Debug)]
556pub struct PrechargedDispatch {
557    gas: GasCounter,
558    allowance: GasAllowanceCounter,
559    dispatch: IncomingDispatch,
560}
561
562impl PrechargedDispatch {
563    /// Decompose this instance into dispatch and journal.
564    pub fn into_dispatch_and_note(self) -> (IncomingDispatch, Vec<JournalNote>) {
565        let journal = alloc::vec![JournalNote::GasBurned {
566            message_id: self.dispatch.id(),
567            amount: self.gas.burned(),
568        }];
569
570        (self.dispatch, journal)
571    }
572
573    /// Decompose the instance into parts.
574    pub fn into_parts(self) -> (IncomingDispatch, GasCounter, GasAllowanceCounter) {
575        (self.dispatch, self.gas, self.allowance)
576    }
577}
578
579impl From<(IncomingDispatch, GasCounter, GasAllowanceCounter)> for PrechargedDispatch {
580    fn from(
581        (dispatch, gas, allowance): (IncomingDispatch, GasCounter, GasAllowanceCounter),
582    ) -> Self {
583        Self {
584            gas,
585            allowance,
586            dispatch,
587        }
588    }
589}