use crate::{
executor::SystemPrepareMemoryError, precharge::PreChargeGasOperation, ActorPrepareMemoryError,
};
use actor_system_error::actor_system_error;
use alloc::{
collections::{BTreeMap, BTreeSet},
string::String,
vec::Vec,
};
use gear_backend_common::{SystemReservationContext, SystemTerminationReason, TrapExplanation};
use gear_core::{
gas::{GasAllowanceCounter, GasAmount, GasCounter},
ids::{CodeId, MessageId, ProgramId, ReservationId},
memory::{MemoryError, PageBuf},
message::{
ContextStore, Dispatch, DispatchKind, IncomingDispatch, MessageWaitedType, StoredDispatch,
},
pages::{GearPage, WasmPage},
program::Program,
reservation::{GasReservationMap, GasReserver},
};
use gear_core_errors::{SignalCode, SimpleExecutionError};
use scale_info::scale::{self, Decode, Encode};
#[derive(Clone)]
pub enum DispatchResultKind {
Success,
Trap(TrapExplanation),
Wait(Option<u32>, MessageWaitedType),
Exit(ProgramId),
GasAllowanceExceed,
}
pub struct DispatchResult {
pub kind: DispatchResultKind,
pub dispatch: IncomingDispatch,
pub program_id: ProgramId,
pub context_store: ContextStore,
pub generated_dispatches: Vec<(Dispatch, u32, Option<ReservationId>)>,
pub awakening: Vec<(MessageId, u32)>,
pub reply_deposits: Vec<(MessageId, u64)>,
pub program_candidates: BTreeMap<CodeId, Vec<(MessageId, ProgramId)>>,
pub program_rents: BTreeMap<ProgramId, u32>,
pub gas_amount: GasAmount,
pub gas_reserver: Option<GasReserver>,
pub system_reservation_context: SystemReservationContext,
pub page_update: BTreeMap<GearPage, PageBuf>,
pub allocations: BTreeSet<WasmPage>,
}
impl DispatchResult {
pub fn message_id(&self) -> MessageId {
self.dispatch.id()
}
pub fn program_id(&self) -> ProgramId {
self.program_id
}
pub fn message_source(&self) -> ProgramId {
self.dispatch.source()
}
pub fn message_value(&self) -> u128 {
self.dispatch.value()
}
pub fn success(
dispatch: IncomingDispatch,
program_id: ProgramId,
gas_amount: GasAmount,
) -> Self {
let system_reservation_context = SystemReservationContext::from_dispatch(&dispatch);
Self {
kind: DispatchResultKind::Success,
dispatch,
program_id,
context_store: Default::default(),
generated_dispatches: Default::default(),
awakening: Default::default(),
reply_deposits: Default::default(),
program_candidates: Default::default(),
program_rents: Default::default(),
gas_amount,
gas_reserver: None,
system_reservation_context,
page_update: Default::default(),
allocations: Default::default(),
}
}
}
#[derive(Clone, Debug)]
pub enum DispatchOutcome {
Exit {
program_id: ProgramId,
},
InitSuccess {
program_id: ProgramId,
},
InitFailure {
program_id: ProgramId,
origin: ProgramId,
reason: String,
executed: bool,
},
MessageTrap {
program_id: ProgramId,
trap: String,
},
Success,
NoExecution,
}
#[derive(Clone, Debug)]
pub enum JournalNote {
MessageDispatched {
message_id: MessageId,
source: ProgramId,
outcome: DispatchOutcome,
},
GasBurned {
message_id: MessageId,
amount: u64,
},
ExitDispatch {
id_exited: ProgramId,
value_destination: ProgramId,
},
MessageConsumed(MessageId),
SendDispatch {
message_id: MessageId,
dispatch: Dispatch,
delay: u32,
reservation: Option<ReservationId>,
},
WaitDispatch {
dispatch: StoredDispatch,
duration: Option<u32>,
waited_type: MessageWaitedType,
},
WakeMessage {
message_id: MessageId,
program_id: ProgramId,
awakening_id: MessageId,
delay: u32,
},
UpdatePage {
program_id: ProgramId,
page_number: GearPage,
data: PageBuf,
},
UpdateAllocations {
program_id: ProgramId,
allocations: BTreeSet<WasmPage>,
},
SendValue {
from: ProgramId,
to: Option<ProgramId>,
value: u128,
},
StoreNewPrograms {
code_id: CodeId,
candidates: Vec<(MessageId, ProgramId)>,
},
StopProcessing {
dispatch: StoredDispatch,
gas_burned: u64,
},
ReserveGas {
message_id: MessageId,
reservation_id: ReservationId,
program_id: ProgramId,
amount: u64,
duration: u32,
},
UnreserveGas {
reservation_id: ReservationId,
program_id: ProgramId,
expiration: u32,
},
UpdateGasReservations {
program_id: ProgramId,
reserver: GasReserver,
},
SystemReserveGas {
message_id: MessageId,
amount: u64,
},
SystemUnreserveGas {
message_id: MessageId,
},
SendSignal {
message_id: MessageId,
destination: ProgramId,
code: SignalCode,
},
PayProgramRent {
payer: ProgramId,
program_id: ProgramId,
block_count: u32,
},
ReplyDeposit {
message_id: MessageId,
future_reply_id: MessageId,
amount: u64,
},
}
pub trait JournalHandler {
fn message_dispatched(
&mut self,
message_id: MessageId,
source: ProgramId,
outcome: DispatchOutcome,
);
fn gas_burned(&mut self, message_id: MessageId, amount: u64);
fn exit_dispatch(&mut self, id_exited: ProgramId, value_destination: ProgramId);
fn message_consumed(&mut self, message_id: MessageId);
fn send_dispatch(
&mut self,
message_id: MessageId,
dispatch: Dispatch,
delay: u32,
reservation: Option<ReservationId>,
);
fn wait_dispatch(
&mut self,
dispatch: StoredDispatch,
duration: Option<u32>,
waited_type: MessageWaitedType,
);
fn wake_message(
&mut self,
message_id: MessageId,
program_id: ProgramId,
awakening_id: MessageId,
delay: u32,
);
fn update_pages_data(&mut self, program_id: ProgramId, pages_data: BTreeMap<GearPage, PageBuf>);
fn update_allocations(&mut self, program_id: ProgramId, allocations: BTreeSet<WasmPage>);
fn send_value(&mut self, from: ProgramId, to: Option<ProgramId>, value: u128);
fn store_new_programs(&mut self, code_id: CodeId, candidates: Vec<(MessageId, ProgramId)>);
fn stop_processing(&mut self, dispatch: StoredDispatch, gas_burned: u64);
fn reserve_gas(
&mut self,
message_id: MessageId,
reservation_id: ReservationId,
program_id: ProgramId,
amount: u64,
bn: u32,
);
fn unreserve_gas(
&mut self,
reservation_id: ReservationId,
program_id: ProgramId,
expiration: u32,
);
fn update_gas_reservation(&mut self, program_id: ProgramId, reserver: GasReserver);
fn system_reserve_gas(&mut self, message_id: MessageId, amount: u64);
fn system_unreserve_gas(&mut self, message_id: MessageId);
fn send_signal(&mut self, message_id: MessageId, destination: ProgramId, code: SignalCode);
fn pay_program_rent(&mut self, payer: ProgramId, program_id: ProgramId, block_count: u32);
fn reply_deposit(&mut self, message_id: MessageId, future_reply_id: MessageId, amount: u64);
}
actor_system_error! {
pub type ExecutionError = ActorSystemError<ActorExecutionError, SystemExecutionError>;
}
#[derive(Debug, derive_more::Display)]
#[display(fmt = "{reason}")]
pub struct ActorExecutionError {
pub gas_amount: GasAmount,
pub reason: ActorExecutionErrorReplyReason,
}
#[derive(Encode, Decode, Debug, PartialEq, Eq, PartialOrd, Ord, derive_more::Display)]
#[codec(crate = scale)]
pub enum ActorExecutionErrorReplyReason {
#[display(fmt = "Not enough gas to {_0}")]
PreChargeGasLimitExceeded(PreChargeGasOperation),
#[display(fmt = "{_0}")]
PrepareMemory(ActorPrepareMemoryError),
#[display(fmt = "Environment error: <host error stripped>")]
Environment,
#[display(fmt = "{_0}")]
Trap(TrapExplanation),
}
impl ActorExecutionErrorReplyReason {
pub fn as_simple(&self) -> SimpleExecutionError {
match self {
Self::PreChargeGasLimitExceeded(_) => SimpleExecutionError::RanOutOfGas,
Self::PrepareMemory(_) | Self::Environment => SimpleExecutionError::Unsupported,
Self::Trap(expl) => match expl {
TrapExplanation::GasLimitExceeded => SimpleExecutionError::RanOutOfGas,
TrapExplanation::ForbiddenFunction | TrapExplanation::UnrecoverableExt(_) => {
SimpleExecutionError::BackendError
}
TrapExplanation::ProgramAllocOutOfBounds => SimpleExecutionError::MemoryOverflow,
TrapExplanation::Panic(_) => SimpleExecutionError::UserspacePanic,
TrapExplanation::Unknown => SimpleExecutionError::UnreachableInstruction,
},
}
}
}
#[derive(Debug, derive_more::Display, derive_more::From)]
pub enum SystemExecutionError {
#[from]
#[display(fmt = "Prepare memory: {_0}")]
PrepareMemory(SystemPrepareMemoryError),
#[display(fmt = "Backend error: {_0}")]
Environment(String),
#[from]
#[display(fmt = "Syscall function error: {_0}")]
UndefinedTerminationReason(SystemTerminationReason),
#[display(fmt = "`into_ext_info()` error: {_0}")]
IntoExtInfo(MemoryError),
}
#[derive(Clone, Debug, Decode, Encode)]
#[codec(crate = scale)]
pub struct Actor {
pub balance: u128,
pub destination_program: ProgramId,
pub executable_data: Option<ExecutableActorData>,
}
#[derive(Clone, Debug, Decode, Encode)]
#[codec(crate = scale)]
pub struct ExecutableActorData {
pub allocations: BTreeSet<WasmPage>,
pub pages_with_data: BTreeSet<GearPage>,
pub code_id: CodeId,
pub code_exports: BTreeSet<DispatchKind>,
pub static_pages: WasmPage,
pub initialized: bool,
pub gas_reservation_map: GasReservationMap,
}
#[derive(Debug)]
pub struct WasmExecutionContext {
pub gas_counter: GasCounter,
pub gas_allowance_counter: GasAllowanceCounter,
pub gas_reserver: GasReserver,
pub program: Program,
pub pages_initial_data: BTreeMap<GearPage, PageBuf>,
pub memory_size: WasmPage,
}
#[derive(Debug)]
pub struct PrechargedDispatch {
gas: GasCounter,
allowance: GasAllowanceCounter,
dispatch: IncomingDispatch,
}
impl PrechargedDispatch {
pub fn into_dispatch_and_note(self) -> (IncomingDispatch, Vec<JournalNote>) {
let journal = alloc::vec![JournalNote::GasBurned {
message_id: self.dispatch.id(),
amount: self.gas.burned(),
}];
(self.dispatch, journal)
}
pub fn into_parts(self) -> (IncomingDispatch, GasCounter, GasAllowanceCounter) {
(self.dispatch, self.gas, self.allowance)
}
}
impl From<(IncomingDispatch, GasCounter, GasAllowanceCounter)> for PrechargedDispatch {
fn from(
(dispatch, gas, allowance): (IncomingDispatch, GasCounter, GasAllowanceCounter),
) -> Self {
Self {
gas,
allowance,
dispatch,
}
}
}