Skip to content

Commit

Permalink
feat(gtest): Proper runtime-like reservations for gtest (#4196)
Browse files Browse the repository at this point in the history
  • Loading branch information
playX18 authored Aug 28, 2024
1 parent 7d45a41 commit 3bf4156
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 42 deletions.
65 changes: 55 additions & 10 deletions gtest/src/manager/journal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand All @@ -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(
Expand Down Expand Up @@ -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,
) {
Expand All @@ -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(
Expand All @@ -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}");
}
<Self as TaskHandler<ProgramId>>::remove_gas_reservation(self, program_id, reservation_id);

let _ = self.task_pool.delete(
expiration,
Expand Down
23 changes: 0 additions & 23 deletions gtest/src/manager/reservations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<bool> {
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,
Expand Down
7 changes: 4 additions & 3 deletions gtest/src/manager/task.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,10 +173,11 @@ impl TaskHandler<ProgramId> 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 {
Expand Down
44 changes: 44 additions & 0 deletions gtest/src/program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down
8 changes: 2 additions & 6 deletions gtest/src/state/gas_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,12 +198,8 @@ impl GasTreeManager {
GasTree::system_reserve(GasNodeId::from(message_id.cast::<PlainNodeId>()), amount)
}

pub fn lock(&self, message_id: MessageId, id: LockId, amount: Gas) -> Result<(), GasTreeError> {
GasTree::lock(
GasNodeId::from(message_id.cast::<PlainNodeId>()),
id,
amount,
)
pub fn lock(&self, node_id: impl Origin, id: LockId, amount: Gas) -> Result<(), GasTreeError> {
GasTree::lock(GasNodeId::from(node_id.cast::<PlainNodeId>()), id, amount)
}

pub(crate) fn unlock_all(
Expand Down

0 comments on commit 3bf4156

Please sign in to comment.