diff --git a/gtest/src/manager/journal.rs b/gtest/src/manager/journal.rs index 2cee4bd2d21..39c757dfbf5 100644 --- a/gtest/src/manager/journal.rs +++ b/gtest/src/manager/journal.rs @@ -18,15 +18,18 @@ //! Implementation of the `JournalHandler` trait for the `ExtManager`. +use std::collections::BTreeMap; + use super::{ExtManager, Gas, GenuineProgram, Program, TestActor}; use crate::{ + manager::hold_bound::HoldBoundBuilder, state::{accounts::Accounts, actors::Actors}, Value, EXISTENTIAL_DEPOSIT, }; use core_processor::common::{DispatchOutcome, JournalHandler}; use gear_common::{ event::{MessageWaitedRuntimeReason, RuntimeReason}, - scheduler::ScheduledTask, + scheduler::{ScheduledTask, StorageType, TaskHandler}, Origin, }; use gear_core::{ @@ -43,7 +46,6 @@ use gear_core::{ }; use gear_core_errors::SignalCode; use gear_wasm_instrument::gas_metering::Schedule; -use std::collections::BTreeMap; impl JournalHandler for ExtManager { fn message_dispatched( @@ -327,7 +329,7 @@ impl JournalHandler for ExtManager { &mut self, message_id: MessageId, reservation_id: ReservationId, - _program_id: ProgramId, + program_id: ProgramId, amount: u64, duration: u32, ) { @@ -339,9 +341,57 @@ impl JournalHandler for ExtManager { duration ); + let hold = HoldBoundBuilder::new(StorageType::Reservation).duration(self, duration); + + if hold.expected_duration(self).is_zero() { + let err_msg = format!( + "JournalHandler::reserve_gas: reservation got zero duration hold bound for storing. \ + Duration - {duration}, block cost - {cost}, program - {program_id}.", + cost = Self::cost_by_storage_type(StorageType::Reservation) + ); + + unreachable!("{err_msg}"); + } + + let total_amount = amount.saturating_add(hold.lock_amount(self)); + self.gas_tree - .reserve_gas(message_id, reservation_id, amount) + .reserve_gas(message_id, reservation_id, total_amount) .unwrap_or_else(|e| unreachable!("GasTree corrupted: {:?}", e)); + + let lock_id = hold.lock_id().unwrap_or_else(|| { + // Reservation storage is guaranteed to have an associated lock id + let err_msg = + "JournalHandler::reserve_gas: No associated lock id for the reservation storage"; + + unreachable!("{err_msg}"); + }); + + self.gas_tree + .lock(reservation_id, lock_id, hold.lock_amount(self)) + .unwrap_or_else(|e| { + let err_msg = format!( + "JournalHandler::reserve_gas: failed locking gas for the reservation hold. \ + Reseravation - {reservation_id}, lock amount - {lock}. Got error - {e:?}", + lock = hold.lock_amount(self) + ); + + unreachable!("{err_msg}"); + }); + + self.task_pool.add( + hold.expected(), + ScheduledTask::RemoveGasReservation(program_id, reservation_id) + ).unwrap_or_else(|e| { + let err_msg = format!( + "JournalHandler::reserve_gas: failed adding task for gas reservation removal. \ + Expected bn - {bn:?}, program id - {program_id}, reservation id - {reservation_id}. Got error - {e:?}", + bn = hold.expected() + ); + + + unreachable!("{err_msg}"); + }); } fn unreserve_gas( @@ -350,12 +400,7 @@ impl JournalHandler for ExtManager { program_id: ProgramId, expiration: u32, ) { - let has_removed_reservation = self - .remove_reservation(program_id, reservation_id) - .expect("failed to find genuine_program"); - if !has_removed_reservation { - unreachable!("Failed to remove reservation {reservation_id} from {program_id}"); - } + >::remove_gas_reservation(self, program_id, reservation_id); let _ = self.task_pool.delete( expiration, diff --git a/gtest/src/manager/reservations.rs b/gtest/src/manager/reservations.rs index c54cb9c7bb6..80915d86705 100644 --- a/gtest/src/manager/reservations.rs +++ b/gtest/src/manager/reservations.rs @@ -27,29 +27,6 @@ use gear_common::{ use gear_core::reservation::GasReservationSlot; impl ExtManager { - pub(crate) fn remove_reservation( - &mut self, - id: ProgramId, - reservation: ReservationId, - ) -> Option { - let was_in_map = self.update_genuine_program(id, |genuine_program| { - genuine_program - .gas_reservation_map - .remove(&reservation) - .is_some() - })?; - - if was_in_map { - self.gas_tree - .consume(reservation) - .unwrap_or_else(|e| unreachable!("GasTree corrupted! {:?}", e)); - } else { - log::error!("Tried to remove unexistent reservation {reservation} for program {id}."); - } - - Some(was_in_map) - } - pub(crate) fn remove_gas_reservation_impl( &mut self, program_id: ProgramId, diff --git a/gtest/src/manager/task.rs b/gtest/src/manager/task.rs index 08d27cd328a..f403c1e9133 100644 --- a/gtest/src/manager/task.rs +++ b/gtest/src/manager/task.rs @@ -173,10 +173,11 @@ impl TaskHandler for ExtManager { fn remove_gas_reservation( &mut self, - _program_id: ProgramId, - _reservation_id: ReservationId, + program_id: ProgramId, + reservation_id: ReservationId, ) -> GearCommonGas { - todo!() + let _slot = self.remove_gas_reservation_impl(program_id, reservation_id); + GearCommonGas::MIN } fn remove_resume_session(&mut self, _session_id: u32) -> GearCommonGas { diff --git a/gtest/src/program.rs b/gtest/src/program.rs index 9aecac7a2cb..d770d79fe5e 100644 --- a/gtest/src/program.rs +++ b/gtest/src/program.rs @@ -1354,6 +1354,50 @@ mod tests { assert!(!sys.0.borrow().gas_tree.exists(reservation_id)); } + #[test] + fn test_delete_expired_reservation() { + use demo_constructor::{Calls, WASM_BINARY}; + + let sys = System::new(); + sys.init_logger(); + + let user_id = DEFAULT_USER_ALICE; + let prog = Program::from_binary_with_id(&sys, 4242, WASM_BINARY); + + // Initialize program + let msg_id = prog.send(user_id, Scheme::empty()); + let res = sys.run_next_block(); + assert!(res.succeed.contains(&msg_id)); + + // Reserve gas handle + let handle = Calls::builder().reserve_gas(1_000_000, 1); + let msg_id = prog.send(user_id, handle); + let res = sys.run_next_block(); + assert!(res.succeed.contains(&msg_id)); + + // Get reservation id from program + let reservation_id = sys + .0 + .borrow_mut() + .update_genuine_program(prog.id(), |genuine_prog| { + assert_eq!(genuine_prog.gas_reservation_map.len(), 1); + genuine_prog + .gas_reservation_map + .iter() + .next() + .map(|(&id, _)| id) + .expect("reservation exists, checked upper; qed.") + }) + .expect("internal error: existing prog not found"); + + // Check reservation exists in the tree + assert!(sys.0.borrow().gas_tree.exists(reservation_id)); + + sys.run_next_block(); + + assert!(!sys.0.borrow().gas_tree.exists(reservation_id)); + } + #[test] fn test_reservation_send() { use demo_constructor::{Calls, WASM_BINARY}; diff --git a/gtest/src/state/gas_tree.rs b/gtest/src/state/gas_tree.rs index e9d3a83c8ee..ef2d1a9e34e 100644 --- a/gtest/src/state/gas_tree.rs +++ b/gtest/src/state/gas_tree.rs @@ -198,12 +198,8 @@ impl GasTreeManager { GasTree::system_reserve(GasNodeId::from(message_id.cast::()), amount) } - pub fn lock(&self, message_id: MessageId, id: LockId, amount: Gas) -> Result<(), GasTreeError> { - GasTree::lock( - GasNodeId::from(message_id.cast::()), - id, - amount, - ) + pub fn lock(&self, node_id: impl Origin, id: LockId, amount: Gas) -> Result<(), GasTreeError> { + GasTree::lock(GasNodeId::from(node_id.cast::()), id, amount) } pub(crate) fn unlock_all(