1use 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#[derive(Clone)]
47pub enum DispatchResultKind {
48 Success,
50 Trap(TrapExplanation),
52 Wait(Option<u32>, MessageWaitedType),
54 Exit(ProgramId),
56 GasAllowanceExceed,
58}
59
60pub struct DispatchResult {
62 pub kind: DispatchResultKind,
64 pub dispatch: IncomingDispatch,
66 pub program_id: ProgramId,
68 pub context_store: ContextStore,
70 pub generated_dispatches: Vec<(Dispatch, u32, Option<ReservationId>)>,
72 pub awakening: Vec<(MessageId, u32)>,
74 pub reply_deposits: Vec<(MessageId, u64)>,
76 pub program_candidates: BTreeMap<CodeId, Vec<(MessageId, ProgramId)>>,
78 pub program_rents: BTreeMap<ProgramId, u32>,
80 pub gas_amount: GasAmount,
82 pub gas_reserver: Option<GasReserver>,
84 pub system_reservation_context: SystemReservationContext,
86 pub page_update: BTreeMap<GearPage, PageBuf>,
88 pub allocations: BTreeSet<WasmPage>,
90}
91
92impl DispatchResult {
93 pub fn message_id(&self) -> MessageId {
95 self.dispatch.id()
96 }
97
98 pub fn program_id(&self) -> ProgramId {
100 self.program_id
101 }
102
103 pub fn message_source(&self) -> ProgramId {
105 self.dispatch.source()
106 }
107
108 pub fn message_value(&self) -> u128 {
110 self.dispatch.value()
111 }
112
113 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#[derive(Clone, Debug)]
143pub enum DispatchOutcome {
144 Exit {
146 program_id: ProgramId,
148 },
149 InitSuccess {
151 program_id: ProgramId,
153 },
154 InitFailure {
156 program_id: ProgramId,
158 origin: ProgramId,
160 reason: String,
162 executed: bool,
164 },
165 MessageTrap {
167 program_id: ProgramId,
169 trap: String,
171 },
172 Success,
174 NoExecution,
176}
177
178#[derive(Clone, Debug)]
180pub enum JournalNote {
181 MessageDispatched {
183 message_id: MessageId,
185 source: ProgramId,
187 outcome: DispatchOutcome,
189 },
190 GasBurned {
192 message_id: MessageId,
194 amount: u64,
196 },
197 ExitDispatch {
199 id_exited: ProgramId,
201 value_destination: ProgramId,
204 },
205 MessageConsumed(MessageId),
209 SendDispatch {
211 message_id: MessageId,
213 dispatch: Dispatch,
215 delay: u32,
217 reservation: Option<ReservationId>,
219 },
220 WaitDispatch {
222 dispatch: StoredDispatch,
224 duration: Option<u32>,
226 waited_type: MessageWaitedType,
228 },
229 WakeMessage {
231 message_id: MessageId,
233 program_id: ProgramId,
235 awakening_id: MessageId,
237 delay: u32,
239 },
240 UpdatePage {
242 program_id: ProgramId,
244 page_number: GearPage,
246 data: PageBuf,
248 },
249 UpdateAllocations {
252 program_id: ProgramId,
254 allocations: BTreeSet<WasmPage>,
256 },
257 SendValue {
259 from: ProgramId,
261 to: Option<ProgramId>,
263 value: u128,
265 },
266 StoreNewPrograms {
268 code_id: CodeId,
270 candidates: Vec<(MessageId, ProgramId)>,
272 },
273 StopProcessing {
275 dispatch: StoredDispatch,
277 gas_burned: u64,
279 },
280 ReserveGas {
282 message_id: MessageId,
284 reservation_id: ReservationId,
286 program_id: ProgramId,
288 amount: u64,
290 duration: u32,
292 },
293 UnreserveGas {
295 reservation_id: ReservationId,
297 program_id: ProgramId,
299 expiration: u32,
301 },
302 UpdateGasReservations {
304 program_id: ProgramId,
306 reserver: GasReserver,
308 },
309 SystemReserveGas {
311 message_id: MessageId,
313 amount: u64,
315 },
316 SystemUnreserveGas {
318 message_id: MessageId,
320 },
321 SendSignal {
323 message_id: MessageId,
325 destination: ProgramId,
327 code: SignalCode,
329 },
330 PayProgramRent {
332 payer: ProgramId,
334 program_id: ProgramId,
336 block_count: u32,
338 },
339 ReplyDeposit {
341 message_id: MessageId,
343 future_reply_id: MessageId,
345 amount: u64,
347 },
348}
349
350pub trait JournalHandler {
354 fn message_dispatched(
356 &mut self,
357 message_id: MessageId,
358 source: ProgramId,
359 outcome: DispatchOutcome,
360 );
361 fn gas_burned(&mut self, message_id: MessageId, amount: u64);
363 fn exit_dispatch(&mut self, id_exited: ProgramId, value_destination: ProgramId);
365 fn message_consumed(&mut self, message_id: MessageId);
367 fn send_dispatch(
369 &mut self,
370 message_id: MessageId,
371 dispatch: Dispatch,
372 delay: u32,
373 reservation: Option<ReservationId>,
374 );
375 fn wait_dispatch(
377 &mut self,
378 dispatch: StoredDispatch,
379 duration: Option<u32>,
380 waited_type: MessageWaitedType,
381 );
382 fn wake_message(
384 &mut self,
385 message_id: MessageId,
386 program_id: ProgramId,
387 awakening_id: MessageId,
388 delay: u32,
389 );
390 fn update_pages_data(&mut self, program_id: ProgramId, pages_data: BTreeMap<GearPage, PageBuf>);
392 fn update_allocations(&mut self, program_id: ProgramId, allocations: BTreeSet<WasmPage>);
394 fn send_value(&mut self, from: ProgramId, to: Option<ProgramId>, value: u128);
396 fn store_new_programs(&mut self, code_id: CodeId, candidates: Vec<(MessageId, ProgramId)>);
400 fn stop_processing(&mut self, dispatch: StoredDispatch, gas_burned: u64);
404 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 fn unreserve_gas(
415 &mut self,
416 reservation_id: ReservationId,
417 program_id: ProgramId,
418 expiration: u32,
419 );
420 fn update_gas_reservation(&mut self, program_id: ProgramId, reserver: GasReserver);
422 fn system_reserve_gas(&mut self, message_id: MessageId, amount: u64);
424 fn system_unreserve_gas(&mut self, message_id: MessageId);
426 fn send_signal(&mut self, message_id: MessageId, destination: ProgramId, code: SignalCode);
428 fn pay_program_rent(&mut self, payer: ProgramId, program_id: ProgramId, block_count: u32);
430 fn reply_deposit(&mut self, message_id: MessageId, future_reply_id: MessageId, amount: u64);
432}
433
434actor_system_error! {
435 pub type ExecutionError = ActorSystemError<ActorExecutionError, SystemExecutionError>;
437}
438
439#[derive(Debug, derive_more::Display)]
441#[display(fmt = "{reason}")]
442pub struct ActorExecutionError {
443 pub gas_amount: GasAmount,
445 pub reason: ActorExecutionErrorReplyReason,
447}
448
449#[derive(Encode, Decode, Debug, PartialEq, Eq, PartialOrd, Ord, derive_more::Display)]
451#[codec(crate = scale)]
452pub enum ActorExecutionErrorReplyReason {
453 #[display(fmt = "Not enough gas to {_0}")]
455 PreChargeGasLimitExceeded(PreChargeGasOperation),
456 #[display(fmt = "{_0}")]
458 PrepareMemory(ActorPrepareMemoryError),
459 #[display(fmt = "Environment error: <host error stripped>")]
461 Environment,
462 #[display(fmt = "{_0}")]
464 Trap(TrapExplanation),
465}
466
467impl ActorExecutionErrorReplyReason {
468 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#[derive(Debug, derive_more::Display, derive_more::From)]
488pub enum SystemExecutionError {
489 #[from]
491 #[display(fmt = "Prepare memory: {_0}")]
492 PrepareMemory(SystemPrepareMemoryError),
493 #[display(fmt = "Backend error: {_0}")]
495 Environment(String),
496 #[from]
498 #[display(fmt = "Syscall function error: {_0}")]
499 UndefinedTerminationReason(SystemTerminationReason),
500 #[display(fmt = "`into_ext_info()` error: {_0}")]
502 IntoExtInfo(MemoryError),
503}
504
505#[derive(Clone, Debug, Decode, Encode)]
507#[codec(crate = scale)]
508pub struct Actor {
509 pub balance: u128,
511 pub destination_program: ProgramId,
513 pub executable_data: Option<ExecutableActorData>,
515}
516
517#[derive(Clone, Debug, Decode, Encode)]
519#[codec(crate = scale)]
520pub struct ExecutableActorData {
521 pub allocations: BTreeSet<WasmPage>,
523 pub pages_with_data: BTreeSet<GearPage>,
525 pub code_id: CodeId,
527 pub code_exports: BTreeSet<DispatchKind>,
529 pub static_pages: WasmPage,
531 pub initialized: bool,
533 pub gas_reservation_map: GasReservationMap,
535}
536
537#[derive(Debug)]
539pub struct WasmExecutionContext {
540 pub gas_counter: GasCounter,
542 pub gas_allowance_counter: GasAllowanceCounter,
544 pub gas_reserver: GasReserver,
546 pub program: Program,
548 pub pages_initial_data: BTreeMap<GearPage, PageBuf>,
550 pub memory_size: WasmPage,
552}
553
554#[derive(Debug)]
556pub struct PrechargedDispatch {
557 gas: GasCounter,
558 allowance: GasAllowanceCounter,
559 dispatch: IncomingDispatch,
560}
561
562impl PrechargedDispatch {
563 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 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}