pub use context_interface::cfg::gas::*;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Gas {
limit: u64,
remaining: u64,
refunded: i64,
memory: MemoryGas,
}
impl Gas {
#[inline]
pub const fn new(limit: u64) -> Self {
Self {
limit,
remaining: limit,
refunded: 0,
memory: MemoryGas::new(),
}
}
#[inline]
pub const fn new_spent(limit: u64) -> Self {
Self {
limit,
remaining: 0,
refunded: 0,
memory: MemoryGas::new(),
}
}
#[inline]
pub const fn limit(&self) -> u64 {
self.limit
}
#[inline]
pub fn memory(&self) -> &MemoryGas {
&self.memory
}
#[inline]
pub fn memory_mut(&mut self) -> &mut MemoryGas {
&mut self.memory
}
#[inline]
pub const fn refunded(&self) -> i64 {
self.refunded
}
#[inline]
pub const fn spent(&self) -> u64 {
self.limit - self.remaining
}
#[inline]
pub const fn used(&self) -> u64 {
self.spent().saturating_sub(self.refunded() as u64)
}
#[inline]
pub const fn spent_sub_refunded(&self) -> u64 {
self.spent().saturating_sub(self.refunded as u64)
}
#[inline]
pub const fn remaining(&self) -> u64 {
self.remaining
}
#[inline]
pub fn erase_cost(&mut self, returned: u64) {
self.remaining += returned;
}
#[inline]
pub fn spend_all(&mut self) {
self.remaining = 0;
}
#[inline]
pub fn record_refund(&mut self, refund: i64) {
self.refunded += refund;
}
#[inline]
pub fn set_final_refund(&mut self, is_london: bool) {
let max_refund_quotient = if is_london { 5 } else { 2 };
self.refunded = (self.refunded() as u64).min(self.spent() / max_refund_quotient) as i64;
}
#[inline]
pub fn set_refund(&mut self, refund: i64) {
self.refunded = refund;
}
#[inline]
pub fn set_spent(&mut self, spent: u64) {
self.remaining = self.limit.saturating_sub(spent);
}
#[inline]
#[must_use = "prefer using `gas!` instead to return an out-of-gas error on failure"]
pub fn record_cost(&mut self, cost: u64) -> bool {
#[cfg(feature = "debug-print")]
{
let str = std::format!("- record_cost: cost={}, remaining={}", cost, self.remaining);
#[cfg(target_arch = "wasm32")]
{
#[link(wasm_import_module = "fluentbase_v1preview")]
extern "C" {
pub fn _debug_log(msg_ptr: *const u8, msg_len: u32);
}
unsafe { _debug_log(str.as_ptr(), str.len() as u32) };
}
#[cfg(feature = "std")]
println!("{}", str);
}
if let Some(new_remaining) = self.remaining.checked_sub(cost) {
self.remaining = new_remaining;
return true;
}
false
}
#[inline(always)]
#[must_use = "In case of not enough gas, the interpreter should halt with an out-of-gas error"]
pub fn record_cost_unsafe(&mut self, cost: u64) -> bool {
#[cfg(feature = "debug-print")]
{
let str = std::format!(
"- record_cost_unsafe: cost={}, remaining={}",
cost,
self.remaining
);
#[cfg(target_arch = "wasm32")]
{
#[link(wasm_import_module = "fluentbase_v1preview")]
extern "C" {
pub fn _debug_log(msg_ptr: *const u8, msg_len: u32);
}
unsafe { _debug_log(str.as_ptr(), str.len() as u32) };
}
#[cfg(feature = "std")]
println!("{}", str);
}
let oog = self.remaining < cost;
self.remaining = self.remaining.wrapping_sub(cost);
oog
}
}
#[derive(Debug)]
pub enum MemoryExtensionResult {
Extended,
Same,
OutOfGas,
}
#[derive(Clone, Copy, Default, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct MemoryGas {
pub words_num: usize,
pub expansion_cost: u64,
}
impl MemoryGas {
#[inline]
pub const fn new() -> Self {
Self {
words_num: 0,
expansion_cost: 0,
}
}
#[inline]
pub fn set_words_num(&mut self, words_num: usize, mut expansion_cost: u64) -> Option<u64> {
self.words_num = words_num;
core::mem::swap(&mut self.expansion_cost, &mut expansion_cost);
self.expansion_cost.checked_sub(expansion_cost)
}
#[inline]
pub fn record_new_len(
&mut self,
new_num: usize,
linear_cost: u64,
quadratic_cost: u64,
) -> Option<u64> {
if new_num <= self.words_num {
return None;
}
self.words_num = new_num;
let mut cost = memory_gas(new_num, linear_cost, quadratic_cost);
core::mem::swap(&mut self.expansion_cost, &mut cost);
Some(self.expansion_cost - cost)
}
}
#[inline]
pub const fn memory_gas(num_words: usize, linear_cost: u64, quadratic_cost: u64) -> u64 {
let num_words = num_words as u64;
linear_cost
.saturating_mul(num_words)
.saturating_add(num_words.saturating_mul(num_words) / quadratic_cost)
}