Skip to content

Commit

Permalink
fix(levm): sstore refund logic (#1559)
Browse files Browse the repository at this point in the history
**Motivation**

There was a discrepancies in gas usage and refunds. The issue was
related to an improper management of gas refunds in `sstore`.

**Description**

There was an error when calculating the gas refund. Adjust the
conditional logic to subtract refunds when reverting storage changes and
adding refunds appropriately. Also, reordered refund and cost logic.
  • Loading branch information
damiramirez authored Dec 23, 2024
1 parent a2c65eb commit 3b440a8
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 39 deletions.
10 changes: 0 additions & 10 deletions crates/vm/levm/src/gas_cost.rs
Original file line number Diff line number Diff line change
Expand Up @@ -367,17 +367,7 @@ pub fn sstore(
storage_slot: &StorageSlot,
new_value: U256,
storage_slot_was_cold: bool,
current_call_frame: &CallFrame,
) -> Result<u64, VMError> {
// EIP-2200
let gas_left = current_call_frame
.gas_limit
.checked_sub(current_call_frame.gas_used)
.ok_or(OutOfGasError::ConsumedGasOverflow)?;
if gas_left <= SSTORE_STIPEND {
return Err(VMError::OutOfGas(OutOfGasError::MaxGasLimitExceeded));
}

let static_gas = SSTORE_STATIC;

let mut base_dynamic_gas = if new_value == storage_slot.current_value {
Expand Down
61 changes: 32 additions & 29 deletions crates/vm/levm/src/opcode_handlers/stack_memory_storage_flow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::{
call_frame::CallFrame,
constants::{WORD_SIZE, WORD_SIZE_IN_BYTES_USIZE},
errors::{OpcodeSuccess, OutOfGasError, VMError},
gas_cost,
gas_cost::{self, SSTORE_STIPEND},
memory::{self, calculate_memory_size},
vm::VM,
};
Expand Down Expand Up @@ -164,6 +164,15 @@ impl VM {
let storage_slot_key = current_call_frame.stack.pop()?;
let new_storage_slot_value = current_call_frame.stack.pop()?;

// EIP-2200
let gas_left = current_call_frame
.gas_limit
.checked_sub(current_call_frame.gas_used)
.ok_or(OutOfGasError::ConsumedGasOverflow)?;
if gas_left <= SSTORE_STIPEND {
return Err(VMError::OutOfGas(OutOfGasError::MaxGasLimitExceeded));
}

// Convert key from U256 to H256
let mut bytes = [0u8; 32];
storage_slot_key.to_big_endian(&mut bytes);
Expand All @@ -172,38 +181,27 @@ impl VM {
let (storage_slot, storage_slot_was_cold) =
self.access_storage_slot(current_call_frame.to, key)?;

self.increase_consumed_gas(
current_call_frame,
gas_cost::sstore(
&storage_slot,
new_storage_slot_value,
storage_slot_was_cold,
current_call_frame,
)?,
)?;

// Gas Refunds
// Sync gas refund with global env, ensuring consistency accross contexts.
let mut gas_refunds = self.env.refunded_gas;

if new_storage_slot_value != storage_slot.current_value {
if storage_slot.current_value == storage_slot.original_value {
if !storage_slot.original_value.is_zero() && new_storage_slot_value.is_zero() {
gas_refunds = gas_refunds
.checked_add(4800)
.ok_or(VMError::GasRefundsOverflow)?;
}
} else if !storage_slot.original_value.is_zero() {
if storage_slot.current_value.is_zero() {
gas_refunds = gas_refunds
.checked_sub(4800)
.ok_or(VMError::GasRefundsUnderflow)?;
} else if new_storage_slot_value.is_zero() {
gas_refunds = gas_refunds
.checked_add(4800)
.ok_or(VMError::GasRefundsOverflow)?;
}
} else if new_storage_slot_value == storage_slot.original_value {
if !storage_slot.original_value.is_zero()
&& !storage_slot.current_value.is_zero()
&& new_storage_slot_value.is_zero()
{
gas_refunds = gas_refunds
.checked_add(4800)
.ok_or(VMError::GasRefundsOverflow)?;
}

if !storage_slot.original_value.is_zero() && storage_slot.current_value.is_zero() {
gas_refunds = gas_refunds
.checked_sub(4800)
.ok_or(VMError::GasRefundsUnderflow)?;
}

if new_storage_slot_value == storage_slot.original_value {
if storage_slot.original_value.is_zero() {
gas_refunds = gas_refunds
.checked_add(19900)
Expand All @@ -214,10 +212,15 @@ impl VM {
.ok_or(VMError::GasRefundsOverflow)?;
}
}
};
}

self.env.refunded_gas = gas_refunds;

self.increase_consumed_gas(
current_call_frame,
gas_cost::sstore(&storage_slot, new_storage_slot_value, storage_slot_was_cold)?,
)?;

self.update_account_storage(current_call_frame.to, key, new_storage_slot_value)?;
Ok(OpcodeSuccess::Continue)
}
Expand Down

0 comments on commit 3b440a8

Please sign in to comment.