Skip to content

Commit

Permalink
feat(gtest): proper charging of gas allowance in gtest (#4216)
Browse files Browse the repository at this point in the history
  • Loading branch information
playX18 authored Sep 4, 2024
1 parent d8e0e5b commit 86843d3
Show file tree
Hide file tree
Showing 14 changed files with 342 additions and 75 deletions.
67 changes: 67 additions & 0 deletions core/src/gas_metering/schedule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ pub struct Schedule {
pub rent_weights: RentWeights,
#[doc = " The weights for database access."]
pub db_weights: DbWeights,
#[doc = " The weights for executing tasks."]
pub task_weights: TaskWeights,
#[doc = " The weights for instantiation of the module."]
pub instantiation_weights: InstantiationWeights,
#[doc = " WASM code instrumentation base cost."]
Expand All @@ -58,6 +60,7 @@ impl Default for Schedule {
memory_weights: MemoryWeights::default(),
rent_weights: RentWeights::default(),
db_weights: DbWeights::default(),
task_weights: TaskWeights::default(),
instantiation_weights: InstantiationWeights::default(),
code_instrumentation_cost: Weight {
ref_time: 306821000,
Expand Down Expand Up @@ -961,6 +964,10 @@ pub struct RentWeights {
pub dispatch_stash: Weight,
#[doc = " Holding reservation weight."]
pub reservation: Weight,
#[doc = " Holding message in mailbox weight."]
pub mailbox: Weight,
#[doc = " The minimal gas amount for message to be inserted in mailbox."]
pub mailbox_threshold: Weight,
}

impl Default for RentWeights {
Expand All @@ -978,6 +985,14 @@ impl Default for RentWeights {
ref_time: 100,
proof_size: 0,
},
mailbox: Weight {
ref_time: 100,
proof_size: 0,
},
mailbox_threshold: Weight {
ref_time: 3000,
proof_size: 0,
},
}
}
}
Expand Down Expand Up @@ -1014,6 +1029,58 @@ impl Default for DbWeights {
}
}

#[derive(Debug, Clone)]
#[doc = " Describes weights for running tasks."]
pub struct TaskWeights {
pub remove_gas_reservation: Weight,
pub send_user_message_to_mailbox: Weight,
pub send_user_message: Weight,
pub send_dispatch: Weight,
pub wake_message: Weight,
pub wake_message_no_wake: Weight,
pub remove_from_waitlist: Weight,
pub remove_from_mailbox: Weight,
}

impl Default for TaskWeights {
fn default() -> Self {
Self {
remove_gas_reservation: Weight {
ref_time: 904369000,
proof_size: 6196,
},
send_user_message_to_mailbox: Weight {
ref_time: 694016000,
proof_size: 4323,
},
send_user_message: Weight {
ref_time: 1414815000,
proof_size: 6196,
},
send_dispatch: Weight {
ref_time: 806277000,
proof_size: 4159,
},
wake_message: Weight {
ref_time: 843879000,
proof_size: 4402,
},
wake_message_no_wake: Weight {
ref_time: 30115000,
proof_size: 3545,
},
remove_from_waitlist: Weight {
ref_time: 1846716000,
proof_size: 7609,
},
remove_from_mailbox: Weight {
ref_time: 1806798000,
proof_size: 7338,
},
}
}
}

#[doc = r" Represents the computational time and storage space required for an operation."]
#[derive(Debug, Clone, Copy)]
pub struct Weight {
Expand Down
14 changes: 14 additions & 0 deletions gsdk/src/metadata/generated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2565,6 +2565,8 @@ pub mod runtime_types {
pub waitlist: runtime_types::sp_weights::weight_v2::Weight,
pub dispatch_stash: runtime_types::sp_weights::weight_v2::Weight,
pub reservation: runtime_types::sp_weights::weight_v2::Weight,
pub mailbox: runtime_types::sp_weights::weight_v2::Weight,
pub mailbox_threshold: runtime_types::sp_weights::weight_v2::Weight,
}
#[derive(Debug, crate::gp::Decode, crate::gp::DecodeAsType, crate::gp::Encode)]
pub struct Schedule {
Expand All @@ -2575,6 +2577,7 @@ pub mod runtime_types {
pub memory_weights: runtime_types::pallet_gear::schedule::MemoryWeights,
pub rent_weights: runtime_types::pallet_gear::schedule::RentWeights,
pub db_weights: runtime_types::pallet_gear::schedule::DbWeights,
pub task_weights: runtime_types::pallet_gear::schedule::TaskWeights,
pub instantiation_weights:
runtime_types::pallet_gear::schedule::InstantiationWeights,
pub code_instrumentation_cost: runtime_types::sp_weights::weight_v2::Weight,
Expand Down Expand Up @@ -2659,6 +2662,17 @@ pub mod runtime_types {
pub gr_create_program_wgas_salt_per_byte:
runtime_types::sp_weights::weight_v2::Weight,
}
#[derive(Debug, crate::gp::Decode, crate::gp::DecodeAsType, crate::gp::Encode)]
pub struct TaskWeights {
pub remove_gas_reservation: runtime_types::sp_weights::weight_v2::Weight,
pub send_user_message_to_mailbox: runtime_types::sp_weights::weight_v2::Weight,
pub send_user_message: runtime_types::sp_weights::weight_v2::Weight,
pub send_dispatch: runtime_types::sp_weights::weight_v2::Weight,
pub wake_message: runtime_types::sp_weights::weight_v2::Weight,
pub wake_message_no_wake: runtime_types::sp_weights::weight_v2::Weight,
pub remove_from_waitlist: runtime_types::sp_weights::weight_v2::Weight,
pub remove_from_mailbox: runtime_types::sp_weights::weight_v2::Weight,
}
}
}
pub mod pallet_gear_bank {
Expand Down
8 changes: 0 additions & 8 deletions gtest/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -551,17 +551,9 @@ pub mod constants {
pub const EPOCH_DURATION_IN_BLOCKS: Block = 600;

/* Storage-related constants */

/// Minimal amount of gas required to be inserted into Mailbox.
pub const MAILBOX_THRESHOLD: Gas = 3_000;
/// Extra amount of blocks must be reserved for storing in storage.
pub const RESERVE_FOR: Block = 1;

/* Rent-related constants */

/// Cost of storing message in mailbox
pub const MAILBOX_COST: Gas = 100;

/* Execution-related constants */

/// Maximal amount of reservations program may have.
Expand Down
25 changes: 17 additions & 8 deletions gtest/src/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,7 @@ use crate::{
waitlist::WaitlistManager,
},
Result, TestError, EPOCH_DURATION_IN_BLOCKS, EXISTENTIAL_DEPOSIT, GAS_ALLOWANCE,
GAS_MULTIPLIER, INITIAL_RANDOM_SEED, MAILBOX_COST, MAILBOX_THRESHOLD, MAX_RESERVATIONS,
RESERVE_FOR, VALUE_PER_GAS,
GAS_MULTIPLIER, INITIAL_RANDOM_SEED, MAX_RESERVATIONS, RESERVE_FOR, VALUE_PER_GAS,
};
use core_processor::{
common::*,
Expand All @@ -61,7 +60,7 @@ use gear_common::{
};
use gear_core::{
code::{Code, CodeAndId, InstrumentedCode, InstrumentedCodeAndId, TryNewCodeConfig},
gas_metering::{RentWeights, Schedule},
gas_metering::{DbWeights, RentWeights, Schedule},
ids::{prelude::*, CodeId, MessageId, ProgramId, ReservationId},
memory::PageBuf,
message::{
Expand Down Expand Up @@ -105,7 +104,7 @@ pub(crate) struct ExtManager {
pub(crate) gas_allowance: Gas,
pub(crate) dispatches_stash: HashMap<MessageId, (StoredDelayedDispatch, Interval<BlockNumber>)>,
pub(crate) messages_processing_enabled: bool,

pub(crate) first_incomplete_tasks_block: Option<u32>,
// Last block execution info
pub(crate) succeed: BTreeSet<MessageId>,
pub(crate) failed: BTreeSet<MessageId>,
Expand Down Expand Up @@ -231,6 +230,11 @@ impl ExtManager {
Accounts::override_balance(id, balance);
}

pub(crate) fn on_task_pool_change(&mut self) {
let write = DbWeights::default().write.ref_time;
self.gas_allowance = self.gas_allowance.saturating_sub(Gas(write));
}

#[track_caller]
fn init_success(&mut self, program_id: ProgramId) {
Actors::modify(program_id, |actor| {
Expand Down Expand Up @@ -280,10 +284,15 @@ impl ExtManager {

self.bank.transfer_value(from, user_id, message.value());

let _ = self.task_pool.delete(
expected,
ScheduledTask::RemoveFromMailbox(user_id, message.id()),
);
let _ = self
.task_pool
.delete(
expected,
ScheduledTask::RemoveFromMailbox(user_id, message.id()),
)
.map(|_| {
self.on_task_pool_change();
});

Ok(message)
}
Expand Down
109 changes: 104 additions & 5 deletions gtest/src/manager/block_exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

use core_processor::SuccessfulDispatchResultKind;
use gear_core::{gas::GasCounter, str::LimitedStr};
use task::get_maximum_task_gas;

use super::*;

Expand Down Expand Up @@ -140,10 +141,108 @@ impl ExtManager {
}

#[track_caller]
pub(crate) fn process_tasks(&mut self, bn: u32) {
for task in self.task_pool.drain_prefix_keys(bn) {
log::debug!("⚙️ Processing task {task:?} at the block {bn}");
task.process_with(self);
pub(crate) fn process_tasks(&mut self, current_bn: u32) {
let db_weights = DbWeights::default();

let (first_incomplete_block, were_empty) = self
.first_incomplete_tasks_block
.take()
.map(|block| {
self.gas_allowance = self
.gas_allowance
.saturating_sub(Gas(db_weights.write.ref_time));
(block, false)
})
.unwrap_or_else(|| {
self.gas_allowance = self
.gas_allowance
.saturating_sub(Gas(db_weights.read.ref_time));
(current_bn, true)
});

// When we had to stop processing due to insufficient gas allowance.
let mut stopped_at = None;

let missing_blocks = first_incomplete_block..=current_bn;
for bn in missing_blocks {
if self.gas_allowance.0 <= db_weights.write.ref_time.saturating_mul(2) {
stopped_at = Some(bn);
log::debug!(
"Stopped processing tasks at: {stopped_at:?} due to insufficient allowance"
);
break;
}

let mut last_task = None;
for task in self.task_pool.drain_prefix_keys(bn) {
// decreasing allowance due to DB deletion
self.on_task_pool_change();

let max_task_gas = get_maximum_task_gas(&task);
log::debug!(
"⚙️ Processing task {task:?} at the block {bn}, max gas = {max_task_gas}"
);

if self.gas_allowance.saturating_sub(max_task_gas) <= Gas(db_weights.write.ref_time)
{
// Since the task is not processed write DB cost should be refunded.
// In the same time gas allowance should be charged for read DB cost.
self.gas_allowance = self
.gas_allowance
.saturating_add(Gas(db_weights.write.ref_time))
.saturating_sub(Gas(db_weights.read.ref_time));

last_task = Some(task);

log::debug!("Not enough gas to process task at {bn:?}");

break;
}

let task_gas = task.process_with(self);

self.gas_allowance = self.gas_allowance.saturating_sub(Gas(task_gas));

if self.gas_allowance <= Gas(db_weights.write.ref_time + db_weights.read.ref_time) {
stopped_at = Some(bn);
log::debug!("Stopping processing tasks at (read next): {stopped_at:?}");
break;
}
}

if let Some(task) = last_task {
stopped_at = Some(bn);

self.gas_allowance = self
.gas_allowance
.saturating_add(Gas(db_weights.write.ref_time));

self.task_pool.add(bn, task.clone()).unwrap_or_else(|e| {
let err_msg = format!(
"process_tasks: failed adding not processed last task to task pool. \
Bn - {bn:?}, task - {task:?}. Got error - {e:?}"
);

unreachable!("{err_msg}");
});
self.on_task_pool_change();
}

if stopped_at.is_some() {
break;
}
}

if let Some(stopped_at) = stopped_at {
if were_empty {
// Charging for inserting into storage of the first block of incomplete tasks,
// if we were reading it only (they were empty).
self.gas_allowance = self
.gas_allowance
.saturating_sub(Gas(db_weights.write.ref_time));
}

self.first_incomplete_tasks_block = Some(stopped_at);
}
}

Expand Down Expand Up @@ -446,7 +545,7 @@ impl ExtManager {
gas_multiplier: gsys::GasMultiplier::from_value_per_gas(VALUE_PER_GAS),
costs: schedule.process_costs(),
existential_deposit: EXISTENTIAL_DEPOSIT,
mailbox_threshold: MAILBOX_THRESHOLD,
mailbox_threshold: schedule.rent_weights.mailbox_threshold.ref_time,
max_reservations: MAX_RESERVATIONS,
max_pages: TESTS_MAX_PAGES_NUMBER.into(),
outgoing_limit: OUTGOING_LIMIT,
Expand Down
12 changes: 11 additions & 1 deletion gtest/src/manager/expend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,18 +47,28 @@ impl ExtManager {
self.bank.spend_gas(external.cast(), amount, multiplier)
}

pub(crate) fn spend_burned(&mut self, id: MessageId, amount: u64) {
self.gas_burned
.entry(id)
.and_modify(|v| *v = v.saturating_sub(Gas(amount)))
.or_insert(Gas(amount));
self.spend_gas(id, amount);
}

pub(crate) fn cost_by_storage_type(storage_type: StorageType) -> u64 {
// Cost per block based on the storage used for holding
let schedule = Schedule::default();
let RentWeights {
waitlist,
dispatch_stash,
reservation,
mailbox,
..
} = schedule.rent_weights;
match storage_type {
StorageType::Code => todo!("#646"),
StorageType::Waitlist => waitlist.ref_time,
StorageType::Mailbox => MAILBOX_COST,
StorageType::Mailbox => mailbox.ref_time,
StorageType::DispatchStash => dispatch_stash.ref_time,
StorageType::Program => todo!("#646"),
StorageType::Reservation => reservation.ref_time,
Expand Down
Loading

0 comments on commit 86843d3

Please sign in to comment.