use crate::costs::RuntimeCosts;
use enum_iterator::Sequence;
use scale_info::scale::{Decode, Encode};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Sequence)]
#[repr(u8)]
pub enum LockId {
Mailbox,
Waitlist,
Reservation,
DispatchStash,
}
pub trait Token: Copy + Clone + Into<u64> {
fn weight(&self) -> u64;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ChargeResult {
Enough,
NotEnough,
}
#[derive(Debug)]
pub struct GasCounter {
left: u64,
burned: u64,
}
impl GasCounter {
pub fn new(initial_amount: u64) -> Self {
Self {
left: initial_amount,
burned: 0,
}
}
pub fn charge<T: Into<u64> + Copy>(&mut self, amount: T) -> ChargeResult {
if let Some(new_left) = self.left.checked_sub(amount.into()) {
self.left = new_left;
self.burned += amount.into();
ChargeResult::Enough
} else {
self.burned += self.left;
self.left = 0;
ChargeResult::NotEnough
}
}
pub fn charge_if_enough<T: Into<u64> + Copy>(&mut self, amount: T) -> ChargeResult {
match self.left.checked_sub(amount.into()) {
None => ChargeResult::NotEnough,
Some(new_left) => {
self.left = new_left;
self.burned += amount.into();
ChargeResult::Enough
}
}
}
pub fn reduce(&mut self, amount: u64) -> ChargeResult {
match self.left.checked_sub(amount) {
None => ChargeResult::NotEnough,
Some(new_left) => {
self.left = new_left;
ChargeResult::Enough
}
}
}
pub fn left(&self) -> u64 {
self.left
}
pub fn burned(&self) -> u64 {
self.burned
}
pub fn to_amount(&self) -> GasAmount {
GasAmount {
left: self.left,
burned: self.burned,
}
}
}
#[derive(Debug, Clone)]
pub struct GasAmount {
left: u64,
burned: u64,
}
impl GasAmount {
pub fn left(&self) -> u64 {
self.left
}
pub fn burned(&self) -> u64 {
self.burned
}
}
#[derive(Debug)]
pub struct ValueCounter(u128);
impl ValueCounter {
pub fn new(initial_amount: u128) -> Self {
Self(initial_amount)
}
pub fn reduce(&mut self, amount: u128) -> ChargeResult {
match self.0.checked_sub(amount) {
None => ChargeResult::NotEnough,
Some(new_left) => {
self.0 = new_left;
ChargeResult::Enough
}
}
}
pub fn left(&self) -> u128 {
self.0
}
}
#[derive(Clone, Debug)]
pub struct GasAllowanceCounter(u128);
impl GasAllowanceCounter {
pub fn new(initial_amount: u64) -> Self {
Self(initial_amount as u128)
}
pub fn left(&self) -> u64 {
self.0 as u64
}
pub fn charge<T: Into<u64>>(&mut self, amount: T) -> ChargeResult {
if let Some(new_left) = self.0.checked_sub(Into::<u64>::into(amount) as u128) {
self.0 = new_left;
ChargeResult::Enough
} else {
self.0 = 0;
ChargeResult::NotEnough
}
}
pub fn charge_if_enough<T: Into<u64>>(&mut self, amount: T) -> ChargeResult {
if let Some(new_left) = self.0.checked_sub(Into::<u64>::into(amount) as u128) {
self.0 = new_left;
ChargeResult::Enough
} else {
ChargeResult::NotEnough
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, derive_more::Display)]
pub enum ChargeError {
#[display(fmt = "Not enough gas to continue execution")]
GasLimitExceeded,
#[display(fmt = "Gas allowance exceeded")]
GasAllowanceExceeded,
}
pub trait CountersOwner {
fn charge_gas_runtime(&mut self, cost: RuntimeCosts) -> Result<(), ChargeError>;
fn charge_gas_runtime_if_enough(&mut self, cost: RuntimeCosts) -> Result<(), ChargeError>;
fn charge_gas_if_enough(&mut self, amount: u64) -> Result<(), ChargeError>;
fn gas_left(&self) -> GasLeft;
fn current_counter_type(&self) -> CounterType;
fn decrease_current_counter_to(&mut self, amount: u64);
fn define_current_counter(&mut self) -> u64;
fn current_counter_value(&self) -> u64 {
let GasLeft { gas, allowance } = self.gas_left();
match self.current_counter_type() {
CounterType::GasLimit => gas,
CounterType::GasAllowance => allowance,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)]
pub enum CounterType {
GasLimit,
GasAllowance,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)]
pub struct GasLeft {
pub gas: u64,
pub allowance: u64,
}
impl From<(u64, u64)> for GasLeft {
fn from((gas, allowance): (u64, u64)) -> Self {
Self { gas, allowance }
}
}
impl From<(i64, i64)> for GasLeft {
fn from((gas, allowance): (i64, i64)) -> Self {
(gas as u64, allowance as u64).into()
}
}
#[cfg(test)]
mod tests {
use super::{ChargeResult, GasCounter};
use crate::{
costs::{HostFnWeights, RuntimeCosts},
gas::GasAllowanceCounter,
};
#[test]
fn limited_gas_counter_charging() {
let mut counter = GasCounter::new(200);
let result = counter.charge_if_enough(100u64);
assert_eq!(result, ChargeResult::Enough);
assert_eq!(counter.left(), 100);
let result = counter.charge_if_enough(101u64);
assert_eq!(result, ChargeResult::NotEnough);
assert_eq!(counter.left(), 100);
}
#[test]
fn charge_fails() {
let mut counter = GasCounter::new(100);
assert_eq!(counter.charge_if_enough(200u64), ChargeResult::NotEnough);
}
#[test]
fn charge_token_fails() {
let token = RuntimeCosts::Alloc(0).token(&HostFnWeights {
alloc: 1_000,
..Default::default()
});
let mut counter = GasCounter::new(10);
assert_eq!(counter.charge(token), ChargeResult::NotEnough);
}
#[test]
fn charge_allowance_token_fails() {
let token = RuntimeCosts::Alloc(0).token(&HostFnWeights {
alloc: 1_000,
..Default::default()
});
let mut counter = GasAllowanceCounter::new(10);
assert_eq!(counter.charge(token), ChargeResult::NotEnough);
}
}