use crate::{context::SystemReservationContext, precharge::PreChargeGasOperation};
use actor_system_error::actor_system_error;
use alloc::{collections::BTreeMap, string::String, vec::Vec};
use gear_core::{
code::{CodeMetadata, InstrumentedCode},
env::MessageWaitedType,
gas::{GasAllowanceCounter, GasAmount, GasCounter},
ids::{ActorId, CodeId, MessageId, ReservationId},
memory::{MemoryError, MemorySetupError, PageBuf},
message::{ContextStore, Dispatch, IncomingDispatch, StoredDispatch},
pages::{GearPage, WasmPage, WasmPagesAmount, numerated::tree::IntervalsTree},
program::MemoryInfix,
reservation::{GasReservationMap, GasReserver},
};
pub use gear_core_backend::error::TrapExplanation;
use gear_core_backend::{env::SystemEnvironmentError, error::SystemTerminationReason};
use gear_core_errors::{SignalCode, SimpleExecutionError};
use parity_scale_codec::{Decode, Encode};
#[derive(Clone)]
pub enum DispatchResultKind {
Success,
Trap(TrapExplanation),
Wait(Option<u32>, MessageWaitedType),
Exit(ActorId),
GasAllowanceExceed,
}
pub struct DispatchResult {
pub kind: DispatchResultKind,
pub program_id: ActorId,
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, ActorId)>>,
pub gas_amount: GasAmount,
pub gas_reserver: Option<GasReserver>,
pub system_reservation_context: SystemReservationContext,
pub page_update: BTreeMap<GearPage, PageBuf>,
pub allocations: Option<IntervalsTree<WasmPage>>,
pub reply_sent: bool,
}
impl DispatchResult {
pub fn success(
dispatch: &IncomingDispatch,
program_id: ActorId,
gas_amount: GasAmount,
) -> Self {
let system_reservation_context = SystemReservationContext::from_dispatch(dispatch);
Self {
kind: DispatchResultKind::Success,
program_id,
context_store: Default::default(),
generated_dispatches: Default::default(),
awakening: Default::default(),
reply_deposits: Default::default(),
program_candidates: Default::default(),
gas_amount,
gas_reserver: None,
system_reservation_context,
page_update: Default::default(),
allocations: Default::default(),
reply_sent: false,
}
}
}
#[derive(Debug)]
pub enum SuccessfulDispatchResultKind {
Exit(ActorId),
Wait(Option<u32>, MessageWaitedType),
Success,
}
#[derive(Clone, Debug, Encode, Decode)]
pub enum DispatchOutcome {
Exit {
program_id: ActorId,
},
InitSuccess {
program_id: ActorId,
},
InitFailure {
program_id: ActorId,
origin: ActorId,
reason: String,
},
MessageTrap {
program_id: ActorId,
trap: String,
},
Success,
NoExecution,
}
#[derive(Clone, Debug, Encode, Decode)]
pub enum JournalNote {
MessageDispatched {
message_id: MessageId,
source: ActorId,
outcome: DispatchOutcome,
},
GasBurned {
message_id: MessageId,
amount: u64,
is_panic: bool,
},
ExitDispatch {
id_exited: ActorId,
value_destination: ActorId,
},
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: ActorId,
awakening_id: MessageId,
delay: u32,
},
UpdatePage {
program_id: ActorId,
page_number: GearPage,
data: PageBuf,
},
UpdateAllocations {
program_id: ActorId,
allocations: IntervalsTree<WasmPage>,
},
SendValue {
from: ActorId,
to: ActorId,
value: u128,
locked: bool,
},
StoreNewPrograms {
program_id: ActorId,
code_id: CodeId,
candidates: Vec<(MessageId, ActorId)>,
},
StopProcessing {
dispatch: StoredDispatch,
gas_burned: u64,
},
ReserveGas {
message_id: MessageId,
reservation_id: ReservationId,
program_id: ActorId,
amount: u64,
duration: u32,
},
UnreserveGas {
reservation_id: ReservationId,
program_id: ActorId,
expiration: u32,
},
UpdateGasReservations {
program_id: ActorId,
reserver: GasReserver,
},
SystemReserveGas {
message_id: MessageId,
amount: u64,
},
SystemUnreserveGas {
message_id: MessageId,
},
SendSignal {
message_id: MessageId,
destination: ActorId,
code: SignalCode,
},
ReplyDeposit {
message_id: MessageId,
future_reply_id: MessageId,
amount: u64,
},
}
pub trait JournalHandler {
fn message_dispatched(
&mut self,
message_id: MessageId,
source: ActorId,
outcome: DispatchOutcome,
);
fn gas_burned(&mut self, message_id: MessageId, amount: u64);
fn exit_dispatch(&mut self, id_exited: ActorId, value_destination: ActorId);
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: ActorId,
awakening_id: MessageId,
delay: u32,
);
fn update_pages_data(&mut self, program_id: ActorId, pages_data: BTreeMap<GearPage, PageBuf>);
fn update_allocations(&mut self, program_id: ActorId, allocations: IntervalsTree<WasmPage>);
fn send_value(&mut self, from: ActorId, to: ActorId, value: u128, locked: bool);
fn store_new_programs(
&mut self,
program_id: ActorId,
code_id: CodeId,
candidates: Vec<(MessageId, ActorId)>,
);
fn stop_processing(&mut self, dispatch: StoredDispatch, gas_burned: u64);
fn reserve_gas(
&mut self,
message_id: MessageId,
reservation_id: ReservationId,
program_id: ActorId,
amount: u64,
bn: u32,
);
fn unreserve_gas(
&mut self,
reservation_id: ReservationId,
program_id: ActorId,
expiration: u32,
);
fn update_gas_reservation(&mut self, program_id: ActorId, 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: ActorId, code: SignalCode);
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("{reason}")]
pub struct ActorExecutionError {
pub gas_amount: GasAmount,
pub reason: ActorExecutionErrorReplyReason,
}
#[derive(Debug, PartialEq, Eq, derive_more::Display)]
pub enum ActorExecutionErrorReplyReason {
#[display("Not enough gas to {_0}")]
PreChargeGasLimitExceeded(PreChargeGasOperation),
#[display("Environment error: <host error stripped>")]
Environment,
Trap(TrapExplanation),
}
impl ActorExecutionErrorReplyReason {
pub fn as_simple(&self) -> SimpleExecutionError {
match self {
Self::PreChargeGasLimitExceeded(_) => SimpleExecutionError::RanOutOfGas,
Self::Trap(expl) => match expl {
TrapExplanation::GasLimitExceeded => SimpleExecutionError::RanOutOfGas,
TrapExplanation::ForbiddenFunction | TrapExplanation::UnrecoverableExt(_) => {
SimpleExecutionError::BackendError
}
TrapExplanation::ProgramAllocOutOfBounds => SimpleExecutionError::MemoryOverflow,
TrapExplanation::Panic(_) => SimpleExecutionError::UserspacePanic,
TrapExplanation::StackLimitExceeded => SimpleExecutionError::StackLimitExceeded,
TrapExplanation::Unknown => SimpleExecutionError::UnreachableInstruction,
},
Self::Environment => SimpleExecutionError::Unsupported,
}
}
}
#[derive(Debug, derive_more::Display, derive_more::From)]
pub enum SystemExecutionError {
#[display("Memory parameters error: {_0}")]
MemoryParams(MemorySetupError),
#[display("Backend error: {_0}")]
Environment(SystemEnvironmentError),
#[display("Syscall function error: {_0}")]
UndefinedTerminationReason(SystemTerminationReason),
#[display("`into_ext_info()` error: {_0}")]
IntoExtInfo(MemoryError),
}
#[derive(Clone, Debug)]
pub struct Actor {
pub balance: u128,
pub destination_program: ActorId,
pub executable_data: ExecutableActorData,
}
#[derive(Clone, Debug)]
pub struct ExecutableActorData {
pub allocations: IntervalsTree<WasmPage>,
pub memory_infix: MemoryInfix,
pub gas_reservation_map: GasReservationMap,
}
#[derive(Clone, Debug)]
pub struct ReservationsAndMemorySize {
pub max_reservations: u64,
pub memory_size: WasmPagesAmount,
}
#[derive(Clone, Debug)]
pub(crate) struct Program {
pub id: ActorId,
pub memory_infix: MemoryInfix,
pub instrumented_code: InstrumentedCode,
pub code_metadata: CodeMetadata,
pub allocations: IntervalsTree<WasmPage>,
}
#[derive(Debug)]
pub(crate) struct WasmExecutionContext {
pub gas_counter: GasCounter,
pub gas_allowance_counter: GasAllowanceCounter,
pub gas_reserver: GasReserver,
pub program: Program,
pub memory_size: WasmPagesAmount,
}