use crate::{
manager::ExtManager, weights::WeightInfo, Config, DispatchStashOf, Event, Pallet, QueueOf,
};
use alloc::{format, string::ToString};
use common::{
event::{
MessageWokenRuntimeReason, MessageWokenSystemReason, RuntimeReason, SystemReason,
UserMessageReadSystemReason,
},
scheduler::*,
storage::*,
Gas, Origin,
};
use core::cmp;
use gear_core::{
ids::{CodeId, MessageId, ProgramId, ReservationId},
message::{DispatchKind, Payload, ReplyMessage},
tasks::{ScheduledTask, TaskHandler, VaraScheduledTask},
};
use gear_core_errors::{ErrorReplyReason, SignalCode};
pub fn get_maximum_task_gas<T: Config>(task: &VaraScheduledTask<T::AccountId>) -> Gas {
use ScheduledTask::*;
match task {
PauseProgram(_) => 0,
RemoveCode(_) => todo!("#646"),
RemoveFromMailbox(_, _) => {
<T as Config>::WeightInfo::tasks_remove_from_mailbox().ref_time()
}
RemoveFromWaitlist(_, _) => {
<T as Config>::WeightInfo::tasks_remove_from_waitlist().ref_time()
}
RemovePausedProgram(_) => todo!("#646"),
WakeMessage(_, _) => cmp::max(
<T as Config>::WeightInfo::tasks_wake_message().ref_time(),
<T as Config>::WeightInfo::tasks_wake_message_no_wake().ref_time(),
),
SendDispatch(_) => <T as Config>::WeightInfo::tasks_send_dispatch().ref_time(),
SendUserMessage { .. } => cmp::max(
<T as Config>::WeightInfo::tasks_send_user_message_to_mailbox().ref_time(),
<T as Config>::WeightInfo::tasks_send_user_message().ref_time(),
),
RemoveGasReservation(_, _) => {
<T as Config>::WeightInfo::tasks_remove_gas_reservation().ref_time()
}
#[allow(deprecated)]
RemoveResumeSession(_) => 0,
}
}
impl<T: Config> TaskHandler<T::AccountId, MessageId, bool> for ExtManager<T>
where
T::AccountId: Origin,
{
fn pause_program(&mut self, _program_id: ProgramId) -> Gas {
log::debug!("Program rent logic is disabled.");
0
}
fn remove_code(&mut self, _code_id: CodeId) -> Gas {
todo!("#646")
}
fn remove_from_mailbox(&mut self, user_id: T::AccountId, message_id: MessageId) -> Gas {
let reason = UserMessageReadSystemReason::OutOfRent.into_reason();
let message = ReplyMessage::auto(message_id);
Pallet::<T>::create(user_id.clone(), message.id(), 0, true);
let mailboxed = Pallet::<T>::read_message(user_id.clone(), message_id, reason.clone())
.unwrap_or_else(|e| {
let err_msg = format!(
"TaskHandler::remove_from_mailbox: failed reading message from mailbox. \
User - {user_id:?}, message - {message_id}, reason - {reason:?}. Got error: {e:?}."
);
log::error!("{err_msg}");
unreachable!("{err_msg}");
});
let dispatch = message.into_stored_dispatch(
mailboxed.destination(),
mailboxed.source(),
mailboxed.id(),
);
QueueOf::<T>::queue(dispatch).unwrap_or_else(|e| {
let err_msg = format!(
"TaskHandler::remove_from_mailbox: failed queuing message. \
Got error: {e:?}"
);
log::error!("{err_msg}");
unreachable!("{err_msg}");
});
let gas = <T as Config>::WeightInfo::tasks_remove_from_mailbox().ref_time();
log::trace!("Task gas: tasks_remove_from_mailbox = {gas}");
gas
}
fn remove_from_waitlist(&mut self, program_id: ProgramId, message_id: MessageId) -> Gas {
let reason = MessageWokenSystemReason::OutOfRent.into_reason();
let waitlisted = Pallet::<T>::wake_dispatch(program_id, message_id, reason.clone())
.unwrap_or_else(|e| {
let err_msg = format!(
"TaskHandler::remove_from_waitlist: failed waking dispatch. \
Program id - {program_id}, waking message - {message_id}, reason - {reason:?} \
Got error - {e:?}."
);
log::error!("{err_msg}");
unreachable!("{err_msg}");
});
self.send_signal(
message_id,
waitlisted.destination(),
SignalCode::RemovedFromWaitlist,
);
if !waitlisted.is_reply() && waitlisted.kind() != DispatchKind::Signal {
let err = ErrorReplyReason::RemovedFromWaitlist;
let err_payload = err
.to_string()
.into_bytes()
.try_into()
.unwrap_or_else(|_| {
let error_reply = err.to_string().into_bytes();
let err_msg = format!(
"TaskHandler::remove_from_waitlist: failed conversion of error reply into `Payload`. \
Error reply bytes len - {len}, max payload len - {max_len}",
len = error_reply.len(),
max_len = Payload::max_len(),
);
log::error!("{err_msg}");
unreachable!("{err_msg}");
});
let trap_reply = ReplyMessage::system(message_id, err_payload, err);
if self.check_program_id(&waitlisted.source()) {
let trap_dispatch =
trap_reply.into_stored_dispatch(program_id, waitlisted.source(), message_id);
Pallet::<T>::split(
waitlisted.id(),
trap_dispatch.id(),
trap_dispatch.is_reply(),
);
QueueOf::<T>::queue(trap_dispatch).unwrap_or_else(|e| {
let err_msg = format!(
"TaskHandler::remove_from_waitlist: failed queuing message. \
Got error - {e:?}"
);
log::error!("{err_msg}");
unreachable!("{err_msg}");
});
} else {
let trap_reply =
trap_reply.into_stored(program_id, waitlisted.source(), message_id);
let trap_reply = trap_reply
.try_into()
.unwrap_or_else(|_| {
let err_msg = format!(
"TaskHandler::remove_from_waitlist: failed conversion from stored into user message. \
Message id - {message_id}, program id - {program_id}, destination - {dest}",
dest = waitlisted.source()
);
log::error!("{err_msg}");
unreachable!("{err_msg}")
});
Pallet::<T>::deposit_event(Event::UserMessageSent {
message: trap_reply,
expiration: None,
});
}
}
Pallet::<T>::consume_and_retrieve(waitlisted.id());
if waitlisted.kind() == DispatchKind::Init {
let origin = waitlisted.source();
Self::process_failed_init(program_id, origin);
}
let gas = <T as Config>::WeightInfo::tasks_remove_from_waitlist().ref_time();
log::trace!("Task gas: tasks_remove_from_waitlist = {gas}");
gas
}
fn remove_paused_program(&mut self, _program_id: ProgramId) -> Gas {
todo!("#646")
}
fn wake_message(&mut self, program_id: ProgramId, message_id: MessageId) -> Gas {
match Pallet::<T>::wake_dispatch(
program_id,
message_id,
MessageWokenRuntimeReason::WakeCalled.into_reason(),
)
.ok()
{
Some(dispatch) => {
QueueOf::<T>::queue(dispatch).unwrap_or_else(|e| {
let err_msg = format!(
"TaskHandler::wake_message: failed queuing message. \
Got error - {e:?}"
);
log::error!("{err_msg}");
unreachable!("{err_msg}");
});
let gas = <T as Config>::WeightInfo::tasks_wake_message().ref_time();
log::trace!("Task gas: tasks_wake_message = {gas}");
gas
}
None => {
let gas = <T as Config>::WeightInfo::tasks_wake_message_no_wake().ref_time();
log::trace!("Task gas: tasks_wake_message_no_wake = {gas}");
gas
}
}
}
fn send_dispatch(&mut self, stashed_message_id: MessageId) -> Gas {
let (dispatch, hold_interval) = DispatchStashOf::<T>::take(stashed_message_id)
.unwrap_or_else(|| {
let err_msg = format!(
"TaskHandler::send_dispatch: failed taking message from stash. Message id - {stashed_message_id}."
);
log::error!("{err_msg}");
unreachable!("{err_msg}");
});
Pallet::<T>::charge_for_hold(dispatch.id(), hold_interval, StorageType::DispatchStash);
QueueOf::<T>::queue(dispatch.into()).unwrap_or_else(|e| {
let err_msg = format!(
"TaskHandler::send_dispatch: failed queuing message. \
Got error - {e:?}"
);
log::error!("{err_msg}");
unreachable!("{err_msg}");
});
let gas = <T as Config>::WeightInfo::tasks_send_dispatch().ref_time();
log::trace!("Task gas: tasks_send_dispatch = {gas}");
gas
}
fn send_user_message(&mut self, stashed_message_id: MessageId, to_mailbox: bool) -> Gas {
let (message, hold_interval) = DispatchStashOf::<T>::take(stashed_message_id)
.map(|(dispatch, interval)| (dispatch.into_parts().1, interval))
.unwrap_or_else(|| {
let err_msg = format!(
"TaskHandler::send_user_message: failed taking message from stash. Message id - {stashed_message_id}."
);
log::error!("{err_msg}");
unreachable!("{err_msg}");
});
Pallet::<T>::charge_for_hold(message.id(), hold_interval, StorageType::DispatchStash);
let message_id = message.id();
let program_id = message.source();
let message = message.try_into().unwrap_or_else(|_| {
let err_msg = format!(
"TaskHandler::send_user_message: failed conversion from stored into user message. \
Message id - {message_id}, program id - {program_id}.",
);
log::error!("{err_msg}");
unreachable!("{err_msg}");
});
Pallet::<T>::send_user_message_after_delay(message, to_mailbox);
if to_mailbox {
let gas = <T as Config>::WeightInfo::tasks_send_user_message_to_mailbox().ref_time();
log::trace!("Task gas: tasks_send_user_message_to_mailbox = {gas}");
gas
} else {
let gas = <T as Config>::WeightInfo::tasks_send_user_message().ref_time();
log::trace!("Task gas: tasks_send_user_message = {gas}");
gas
}
}
fn remove_gas_reservation(
&mut self,
program_id: ProgramId,
reservation_id: ReservationId,
) -> Gas {
let _slot = Self::remove_gas_reservation_impl(program_id, reservation_id);
let gas = <T as Config>::WeightInfo::tasks_remove_gas_reservation().ref_time();
log::trace!("Task gas: tasks_remove_gas_reservation = {gas}");
gas
}
fn remove_resume_session(&mut self, _session_id: u32) -> Gas {
log::debug!("Program rent logic is disabled");
0
}
}