1use crate::{
20 common::{
21 ActorExecutionErrorReplyReason, DispatchOutcome, DispatchResult, DispatchResultKind,
22 ExecutionError, JournalNote, SystemExecutionError, WasmExecutionContext,
23 },
24 configs::{BlockConfig, ExecutionSettings},
25 context::*,
26 executor,
27 ext::ProcessorExternalities,
28 precharge::SuccessfulDispatchResultKind,
29};
30use alloc::{collections::BTreeMap, string::ToString, vec::Vec};
31use gear_backend_common::{
32 BackendExternalities, BackendSyscallError, Environment, SystemReservationContext,
33};
34use gear_core::{
35 env::Externalities,
36 ids::{MessageId, ProgramId},
37 memory::PageBuf,
38 message::{ContextSettings, DispatchKind, IncomingDispatch, ReplyMessage, StoredDispatch},
39 pages::GearPage,
40 reservation::GasReservationState,
41};
42use gear_core_errors::{ErrorReplyReason, SignalCode};
43
44pub fn process<E>(
46 block_config: &BlockConfig,
47 execution_context: ProcessExecutionContext,
48 random_data: (Vec<u8>, u32),
49 memory_pages: BTreeMap<GearPage, PageBuf>,
50) -> Result<Vec<JournalNote>, SystemExecutionError>
51where
52 E: Environment,
53 E::Ext: ProcessorExternalities + BackendExternalities + 'static,
54 <E::Ext as Externalities>::UnrecoverableError: BackendSyscallError,
55{
56 use crate::precharge::SuccessfulDispatchResultKind::*;
57
58 let BlockConfig {
59 block_info,
60 max_pages,
61 page_costs,
62 existential_deposit,
63 outgoing_limit,
64 host_fn_weights,
65 forbidden_funcs,
66 mailbox_threshold,
67 waitlist_cost,
68 dispatch_hold_cost,
69 reserve_for,
70 reservation,
71 write_cost,
72 rent_cost,
73 ..
74 } = block_config.clone();
75
76 let execution_settings = ExecutionSettings {
77 block_info,
78 existential_deposit,
79 max_pages,
80 page_costs,
81 host_fn_weights,
82 forbidden_funcs,
83 mailbox_threshold,
84 waitlist_cost,
85 dispatch_hold_cost,
86 reserve_for,
87 reservation,
88 random_data,
89 rent_cost,
90 };
91
92 let dispatch = execution_context.dispatch;
93 let balance = execution_context.balance;
94 let program_id = execution_context.program.id();
95 let execution_context = WasmExecutionContext {
96 gas_counter: execution_context.gas_counter,
97 gas_allowance_counter: execution_context.gas_allowance_counter,
98 gas_reserver: execution_context.gas_reserver,
99 program: execution_context.program,
100 pages_initial_data: memory_pages,
101 memory_size: execution_context.memory_size,
102 };
103
104 let msg_ctx_settings = ContextSettings::new(
116 write_cost.saturating_mul(2),
117 write_cost.saturating_mul(4),
118 write_cost.saturating_mul(3),
119 write_cost.saturating_mul(2),
120 write_cost.saturating_mul(2),
121 outgoing_limit,
122 );
123
124 let exec_result = executor::execute_wasm::<E>(
125 balance,
126 dispatch.clone(),
127 execution_context,
128 execution_settings,
129 msg_ctx_settings,
130 )
131 .map_err(|err| {
132 log::debug!("Wasm execution error: {}", err);
133 err
134 });
135
136 match exec_result {
137 Ok(res) => Ok(match res.kind {
138 DispatchResultKind::Trap(reason) => process_error(
139 res.dispatch,
140 program_id,
141 res.gas_amount.burned(),
142 res.system_reservation_context,
143 ActorExecutionErrorReplyReason::Trap(reason),
144 true,
145 ),
146 DispatchResultKind::Success => process_success(Success, res),
147 DispatchResultKind::Wait(duration, ref waited_type) => {
148 process_success(Wait(duration, waited_type.clone()), res)
149 }
150 DispatchResultKind::Exit(value_destination) => {
151 process_success(Exit(value_destination), res)
152 }
153 DispatchResultKind::GasAllowanceExceed => {
154 process_allowance_exceed(dispatch, program_id, res.gas_amount.burned())
155 }
156 }),
157 Err(ExecutionError::Actor(e)) => Ok(process_error(
158 dispatch,
159 program_id,
160 e.gas_amount.burned(),
161 SystemReservationContext::default(),
162 e.reason,
163 true,
164 )),
165 Err(ExecutionError::System(e)) => Err(e),
166 }
167}
168
169pub fn process_error(
171 dispatch: IncomingDispatch,
172 program_id: ProgramId,
173 gas_burned: u64,
174 system_reservation_ctx: SystemReservationContext,
175 err: ActorExecutionErrorReplyReason,
176 executed: bool,
177) -> Vec<JournalNote> {
178 let mut journal = Vec::new();
179
180 let message_id = dispatch.id();
181 let origin = dispatch.source();
182 let value = dispatch.value();
183
184 journal.push(JournalNote::GasBurned {
185 message_id,
186 amount: gas_burned,
187 });
188
189 if dispatch.context().is_none() && value != 0 {
197 journal.push(JournalNote::SendValue {
199 from: origin,
200 to: None,
201 value,
202 });
203 }
204
205 if let Some(amount) = system_reservation_ctx.current_reservation {
206 journal.push(JournalNote::SystemReserveGas { message_id, amount });
207 }
208
209 if system_reservation_ctx.has_any() {
210 if !dispatch.is_error_reply()
211 && !matches!(dispatch.kind(), DispatchKind::Signal | DispatchKind::Init)
212 {
213 journal.push(JournalNote::SendSignal {
214 message_id,
215 destination: program_id,
216 code: SignalCode::Execution(err.as_simple()),
217 });
218 }
219
220 journal.push(JournalNote::SystemUnreserveGas { message_id });
221 }
222
223 if !dispatch.is_reply() && dispatch.kind() != DispatchKind::Signal {
224 let err_payload = err
226 .to_string()
227 .into_bytes()
228 .try_into()
229 .unwrap_or_else(|_| unreachable!("Error message is too large"));
230 let err = err.as_simple();
231
232 let dispatch = ReplyMessage::system(dispatch.id(), err_payload, err).into_dispatch(
239 program_id,
240 dispatch.source(),
241 dispatch.id(),
242 );
243
244 journal.push(JournalNote::SendDispatch {
245 message_id,
246 dispatch,
247 delay: 0,
248 reservation: None,
249 });
250 }
251
252 let outcome = match dispatch.kind() {
253 DispatchKind::Init => DispatchOutcome::InitFailure {
254 program_id,
255 origin,
256 reason: err.to_string(),
257 executed,
258 },
259 _ => DispatchOutcome::MessageTrap {
260 program_id,
261 trap: err.to_string(),
262 },
263 };
264
265 journal.push(JournalNote::MessageDispatched {
266 message_id,
267 source: origin,
268 outcome,
269 });
270 journal.push(JournalNote::MessageConsumed(message_id));
271
272 journal
273}
274
275pub fn process_success(
277 kind: SuccessfulDispatchResultKind,
278 dispatch_result: DispatchResult,
279) -> Vec<JournalNote> {
280 use crate::precharge::SuccessfulDispatchResultKind::*;
281
282 let DispatchResult {
283 dispatch,
284 generated_dispatches,
285 awakening,
286 program_candidates,
287 program_rents,
288 gas_amount,
289 gas_reserver,
290 system_reservation_context,
291 page_update,
292 program_id,
293 context_store,
294 allocations,
295 reply_deposits,
296 ..
297 } = dispatch_result;
298
299 let mut journal = Vec::new();
300
301 let message_id = dispatch.id();
302 let origin = dispatch.source();
303 let value = dispatch.value();
304
305 journal.push(JournalNote::GasBurned {
306 message_id,
307 amount: gas_amount.burned(),
308 });
309
310 if let Some(gas_reserver) = gas_reserver {
311 journal.extend(gas_reserver.states().iter().flat_map(
312 |(&reservation_id, &state)| match state {
313 GasReservationState::Exists { .. } => None,
314 GasReservationState::Created {
315 amount, duration, ..
316 } => Some(JournalNote::ReserveGas {
317 message_id,
318 reservation_id,
319 program_id,
320 amount,
321 duration,
322 }),
323 GasReservationState::Removed { expiration } => Some(JournalNote::UnreserveGas {
324 reservation_id,
325 program_id,
326 expiration,
327 }),
328 },
329 ));
330
331 journal.push(JournalNote::UpdateGasReservations {
332 program_id,
333 reserver: gas_reserver,
334 });
335 }
336
337 if let Some(amount) = system_reservation_context.current_reservation {
338 journal.push(JournalNote::SystemReserveGas { message_id, amount });
339 }
340
341 if dispatch.context().is_none() && value != 0 {
349 journal.push(JournalNote::SendValue {
351 from: origin,
352 to: Some(program_id),
353 value,
354 });
355 }
356
357 for (code_id, candidates) in program_candidates {
359 journal.push(JournalNote::StoreNewPrograms {
360 code_id,
361 candidates,
362 });
363 }
364
365 if matches!(kind, SuccessfulDispatchResultKind::Success)
367 && !context_store.reply_sent()
368 && !dispatch.is_reply()
369 && dispatch.kind() != DispatchKind::Signal
370 {
371 let auto_reply = ReplyMessage::auto(dispatch.id()).into_dispatch(
372 program_id,
373 dispatch.source(),
374 dispatch.id(),
375 );
376
377 journal.push(JournalNote::SendDispatch {
378 message_id,
379 dispatch: auto_reply,
380 delay: 0,
381 reservation: None,
382 });
383 }
384
385 let payer = program_id;
387 for (program_id, block_count) in program_rents {
388 journal.push(JournalNote::PayProgramRent {
389 payer,
390 program_id,
391 block_count,
392 });
393 }
394
395 for (message_id_sent, amount) in reply_deposits {
396 journal.push(JournalNote::ReplyDeposit {
397 message_id,
398 future_reply_id: MessageId::generate_reply(message_id_sent),
399 amount,
400 });
401 }
402
403 for (dispatch, delay, reservation) in generated_dispatches {
404 journal.push(JournalNote::SendDispatch {
405 message_id,
406 dispatch,
407 delay,
408 reservation,
409 });
410 }
411
412 for (awakening_id, delay) in awakening {
413 journal.push(JournalNote::WakeMessage {
414 message_id,
415 program_id,
416 awakening_id,
417 delay,
418 });
419 }
420
421 for (page_number, data) in page_update {
422 journal.push(JournalNote::UpdatePage {
423 program_id,
424 page_number,
425 data,
426 })
427 }
428
429 if !allocations.is_empty() {
430 journal.push(JournalNote::UpdateAllocations {
431 program_id,
432 allocations,
433 });
434 }
435
436 let outcome = match kind {
437 Wait(duration, waited_type) => {
438 journal.push(JournalNote::WaitDispatch {
439 dispatch: dispatch.into_stored(program_id, context_store),
440 duration,
441 waited_type,
442 });
443
444 return journal;
445 }
446 Success => match dispatch.kind() {
447 DispatchKind::Init => DispatchOutcome::InitSuccess { program_id },
448 _ => DispatchOutcome::Success,
449 },
450 Exit(value_destination) => {
451 journal.push(JournalNote::ExitDispatch {
452 id_exited: program_id,
453 value_destination,
454 });
455
456 DispatchOutcome::Exit { program_id }
457 }
458 };
459
460 if system_reservation_context.has_any() {
461 journal.push(JournalNote::SystemUnreserveGas { message_id });
462 }
463
464 journal.push(JournalNote::MessageDispatched {
465 message_id,
466 source: origin,
467 outcome,
468 });
469 journal.push(JournalNote::MessageConsumed(message_id));
470 journal
471}
472
473pub fn process_allowance_exceed(
474 dispatch: IncomingDispatch,
475 program_id: ProgramId,
476 gas_burned: u64,
477) -> Vec<JournalNote> {
478 let mut journal = Vec::with_capacity(1);
479
480 let (kind, message, opt_context) = dispatch.into_parts();
481
482 let dispatch = StoredDispatch::new(kind, message.into_stored(program_id), opt_context);
483
484 journal.push(JournalNote::StopProcessing {
485 dispatch,
486 gas_burned,
487 });
488
489 journal
490}
491
492pub fn process_non_executable(
494 dispatch: IncomingDispatch,
495 program_id: ProgramId,
496 system_reservation_ctx: SystemReservationContext,
497) -> Vec<JournalNote> {
498 let mut journal = Vec::with_capacity(4);
500
501 let message_id = dispatch.id();
502 let source = dispatch.source();
503 let value = dispatch.value();
504
505 if value != 0 {
506 journal.push(JournalNote::SendValue {
508 from: dispatch.source(),
509 to: None,
510 value,
511 });
512 }
513
514 if !dispatch.is_reply() && dispatch.kind() != DispatchKind::Signal {
516 let err = ErrorReplyReason::InactiveProgram;
517 let err_payload = err
518 .to_string()
519 .into_bytes()
520 .try_into()
521 .unwrap_or_else(|_| unreachable!("Error message is too large"));
522
523 let dispatch = ReplyMessage::system(dispatch.id(), err_payload, err).into_dispatch(
524 program_id,
525 dispatch.source(),
526 dispatch.id(),
527 );
528
529 journal.push(JournalNote::SendDispatch {
530 message_id,
531 dispatch,
532 delay: 0,
533 reservation: None,
534 });
535 }
536
537 if system_reservation_ctx.has_any() {
538 journal.push(JournalNote::SystemUnreserveGas { message_id });
539 }
540
541 journal.push(JournalNote::MessageDispatched {
542 message_id,
543 source,
544 outcome: DispatchOutcome::NoExecution,
545 });
546
547 journal.push(JournalNote::MessageConsumed(message_id));
548
549 journal
550}