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}