use revm::{handler::FrameResult, interpreter::interpreter_action::FrameInit};
use super::{
frame_limit::{FrameLimitTracker, TxRuntimeLimit},
LimitCheck, LimitKind,
};
use crate::{JournalInspectTr, MegaSpecId};
#[derive(Debug, Clone)]
pub(crate) struct ComputeGasTracker {
rex1_enabled: bool,
rex4_enabled: bool,
detained_limit: u64,
frame_tracker: FrameLimitTracker<()>,
}
impl ComputeGasTracker {
pub(crate) fn new(spec: MegaSpecId, tx_limit: u64) -> Self {
Self {
detained_limit: tx_limit,
frame_tracker: FrameLimitTracker::new(tx_limit),
rex1_enabled: spec.is_enabled(MegaSpecId::REX1),
rex4_enabled: spec.is_enabled(MegaSpecId::REX4),
}
}
fn push_frame(&mut self) {
if self.rex4_enabled {
self.frame_tracker.push_frame(());
} else {
self.frame_tracker.push_frame_with_limit(u64::MAX, ());
}
}
pub(crate) fn set_detained_limit(&mut self, cap: u64) {
let new_limit = if self.rex4_enabled {
self.tx_usage().saturating_add(cap)
} else {
cap
};
self.detained_limit = self.detained_limit.min(new_limit);
}
pub(crate) fn current_call_remaining(&self) -> u64 {
let tx_remaining = self.tx_limit().saturating_sub(self.tx_usage());
if self.rex4_enabled {
self.frame_tracker.current_frame_remaining().min(tx_remaining)
} else {
tx_remaining
}
}
pub(crate) fn detained_limit(&self) -> u64 {
self.detained_limit
}
pub(crate) fn is_detained_exceed(&self) -> bool {
let used = self.tx_usage();
used > self.detained_limit && self.detained_limit < self.frame_tracker.tx_limit()
}
pub(crate) fn cap_current_frame_limit(&mut self, cap: u64) {
if let Some(frame) = self.frame_tracker.frame_mut() {
frame.limit = frame.limit.min(cap);
}
}
pub(crate) fn record_gas_used(&mut self, gas: u64) {
if let Some(entry) = self.frame_tracker.frame_mut() {
entry.persistent_usage += gas;
} else {
self.frame_tracker.tx_mut().persistent_usage += gas;
}
}
}
impl TxRuntimeLimit for ComputeGasTracker {
#[inline]
fn tx_limit(&self) -> u64 {
self.frame_tracker.tx_limit().min(self.detained_limit)
}
#[inline]
fn tx_usage(&self) -> u64 {
self.frame_tracker.net_usage()
}
#[inline]
fn reset(&mut self) {
self.frame_tracker.reset();
if self.rex1_enabled {
self.detained_limit = self.frame_tracker.tx_limit();
}
}
#[inline]
fn check_limit(&self) -> LimitCheck {
if self.rex4_enabled {
let frame_check = self.frame_tracker.exceeds_current_frame_limit(LimitKind::ComputeGas);
if frame_check.exceeded_limit() {
return frame_check;
}
}
let limit = self.tx_limit();
let used = self.tx_usage();
if used > limit {
LimitCheck::ExceedsLimit {
kind: LimitKind::ComputeGas,
frame_local: false,
limit,
used,
}
} else {
LimitCheck::WithinLimit
}
}
#[inline]
fn push_empty_frame(&mut self) {
self.push_frame();
}
#[inline]
fn before_frame_init<JOURNAL: JournalInspectTr<DBError: core::fmt::Debug>>(
&mut self,
_frame_init: &FrameInit,
_journal: &mut JOURNAL,
) -> Result<(), JOURNAL::DBError> {
self.push_frame();
Ok(())
}
#[inline]
fn before_frame_return_result<const LAST_FRAME: bool>(&mut self, result: &FrameResult) {
assert!(LAST_FRAME || self.frame_tracker.has_active_frame(), "frame stack is empty");
self.frame_tracker.pop_frame(result.instruction_result().is_ok());
}
}