use revm::primitives::{Bytes, ExecutionResult, HaltReason, Output};
use std::ops::Range;
pub(crate) struct SearchRange(Range<u64>);
impl From<Range<u64>> for SearchRange {
fn from(value: Range<u64>) -> Self {
Self(value)
}
}
impl From<SearchRange> for Range<u64> {
fn from(value: SearchRange) -> Self {
value.0
}
}
impl SearchRange {
pub(crate) const fn new(start: u64, end: u64) -> Self {
Self(start..end)
}
pub(crate) const fn midpoint(&self) -> u64 {
(self.0.end - self.0.start) / 2
}
pub(crate) const fn min(&self) -> u64 {
self.0.start
}
pub(crate) const fn set_min(&mut self, min: u64) {
self.0.start = min;
}
pub(crate) const fn maybe_raise_min(&mut self, candidate: u64) {
if candidate > self.min() {
self.set_min(candidate);
}
}
pub(crate) const fn max(&self) -> u64 {
self.0.end
}
pub(crate) const fn set_max(&mut self, max: u64) {
self.0.end = max;
}
pub(crate) const fn maybe_lower_max(&mut self, candidate: u64) {
if candidate < self.max() {
self.set_max(candidate);
}
}
pub(crate) const fn ratio(&self) -> f64 {
(self.max() - self.min()) as f64 / self.max() as f64
}
pub(crate) fn contains(&self, value: u64) -> bool {
self.0.contains(&value)
}
pub(crate) const fn size(&self) -> u64 {
self.0.end - self.0.start
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum EstimationResult {
Success {
estimation: u64,
refund: u64,
gas_used: u64,
output: Output,
},
Revert {
reason: Bytes,
gas_used: u64,
},
Halt {
reason: HaltReason,
gas_used: u64,
},
}
impl From<&ExecutionResult> for EstimationResult {
fn from(value: &ExecutionResult) -> Self {
match value {
ExecutionResult::Success { gas_used, output, gas_refunded, .. } => Self::Success {
estimation: *gas_used,
refund: *gas_refunded,
gas_used: *gas_used,
output: output.clone(),
},
ExecutionResult::Revert { output, gas_used } => {
Self::Revert { reason: output.clone(), gas_used: *gas_used }
}
ExecutionResult::Halt { reason, gas_used } => {
Self::Halt { reason: *reason, gas_used: *gas_used }
}
}
}
}
impl EstimationResult {
pub const fn basic_transfer_success(estimation: u64) -> Self {
Self::Success {
estimation,
refund: 0,
gas_used: estimation,
output: Output::Call(Bytes::new()),
}
}
pub const fn is_success(&self) -> bool {
matches!(self, Self::Success { .. })
}
pub const fn is_failure(&self) -> bool {
!self.is_success()
}
pub const fn gas_estimation(&self) -> Option<u64> {
match self {
Self::Success { estimation, .. } => Some(*estimation),
_ => None,
}
}
pub const fn gas_refunded(&self) -> Option<u64> {
match self {
Self::Success { refund, .. } => Some(*refund),
_ => None,
}
}
pub const fn output(&self) -> Option<&Output> {
match self {
Self::Success { output, .. } => Some(output),
_ => None,
}
}
pub const fn gas_used(&self) -> u64 {
match self {
Self::Success { gas_used, .. } => *gas_used,
Self::Revert { gas_used, .. } => *gas_used,
Self::Halt { gas_used, .. } => *gas_used,
}
}
pub const fn is_revert(&self) -> bool {
matches!(self, Self::Revert { .. })
}
pub const fn revert_reason(&self) -> Option<&Bytes> {
match self {
Self::Revert { reason, .. } => Some(reason),
_ => None,
}
}
pub const fn is_halt(&self) -> bool {
matches!(self, Self::Halt { .. })
}
pub const fn halt_reason(&self) -> Option<&HaltReason> {
match self {
Self::Halt { reason, .. } => Some(reason),
_ => None,
}
}
pub(crate) fn adjust_binary_search_range(
&self,
limit: u64,
range: &mut SearchRange,
) -> Result<(), Self> {
match self {
Self::Success { .. } => range.set_max(limit),
Self::Revert { .. } => range.set_min(limit),
Self::Halt { reason, gas_used } => {
if matches!(reason, HaltReason::OutOfGas(_) | HaltReason::InvalidFEOpcode) {
range.set_min(limit);
} else {
return Err(Self::Halt { reason: *reason, gas_used: *gas_used });
}
}
}
Ok(())
}
}