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}