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}