Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions lib/eevm.ex
Original file line number Diff line number Diff line change
Expand Up @@ -79,17 +79,17 @@ defmodule EEVM do
"""
@spec stack_values(EEVM.Interpreter.MachineState.t()) :: [non_neg_integer()]
def stack_values(state) do
Stack.to_list(state.stack)
Stack.to_list(state.frame.stack)
end

@doc "Returns the list of logs emitted during execution."
@spec logs(MachineState.t()) :: [map()]
def logs(%MachineState{} = state), do: state.logs
def logs(%MachineState{} = state), do: state.substate.logs

@doc "Computes the 256-byte logs bloom filter for the executed transaction."
@spec logs_bloom(MachineState.t()) :: Bloom.t()
def logs_bloom(%MachineState{} = state) do
Bloom.from_logs(state.logs)
Bloom.from_logs(state.substate.logs)
end

@doc """
Expand Down
25 changes: 15 additions & 10 deletions lib/eevm/gas/access.ex
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,14 @@ defmodule EEVM.Gas.Access do
@spec address_access_cost(MachineState.t(), non_neg_integer()) ::
{non_neg_integer(), MachineState.t()}
def address_access_cost(state, address) do
if HardforkConfig.enabled?(state.config.hardfork, :eip_2929) do
if MapSet.member?(state.accessed_addresses, address) do
if HardforkConfig.enabled?(state.env.config.hardfork, :eip_2929) do
sub = state.substate

if MapSet.member?(sub.accessed_addresses, address) do
{@warm_storage_read_cost, state}
else
new_state = %{state | accessed_addresses: MapSet.put(state.accessed_addresses, address)}
{@cold_account_access_cost, new_state}
new_sub = %{sub | accessed_addresses: MapSet.put(sub.accessed_addresses, address)}
{@cold_account_access_cost, %{state | substate: new_sub}}
end
else
# Pre-EIP-2929 (pre-Berlin): flat warm cost — the static cost table already
Expand All @@ -53,14 +55,15 @@ defmodule EEVM.Gas.Access do
@spec storage_access_cost(MachineState.t(), non_neg_integer(), non_neg_integer()) ::
{non_neg_integer(), MachineState.t()}
def storage_access_cost(state, address, slot) do
if HardforkConfig.enabled?(state.config.hardfork, :eip_2929) do
if HardforkConfig.enabled?(state.env.config.hardfork, :eip_2929) do
key = {address, slot}
sub = state.substate

if MapSet.member?(state.accessed_storage_keys, key) do
if MapSet.member?(sub.accessed_storage_keys, key) do
{@warm_storage_read_cost, state}
else
new_state = %{state | accessed_storage_keys: MapSet.put(state.accessed_storage_keys, key)}
{@cold_sload_cost, new_state}
new_sub = %{sub | accessed_storage_keys: MapSet.put(sub.accessed_storage_keys, key)}
{@cold_sload_cost, %{state | substate: new_sub}}
end
else
# Pre-EIP-2929: flat warm cost (pre-Berlin SLOAD was 800, but our static costs
Expand All @@ -70,10 +73,12 @@ defmodule EEVM.Gas.Access do
end

@spec warm_address?(MachineState.t(), non_neg_integer()) :: boolean()
def warm_address?(state, address), do: MapSet.member?(state.accessed_addresses, address)
def warm_address?(state, address),
do: MapSet.member?(state.substate.accessed_addresses, address)

@spec mark_address_warm(MachineState.t(), non_neg_integer()) :: MachineState.t()
def mark_address_warm(state, address) do
%{state | accessed_addresses: MapSet.put(state.accessed_addresses, address)}
sub = state.substate
%{state | substate: %{sub | accessed_addresses: MapSet.put(sub.accessed_addresses, address)}}
end
end
21 changes: 14 additions & 7 deletions lib/eevm/handler/execution.ex
Original file line number Diff line number Diff line change
Expand Up @@ -89,33 +89,40 @@ defmodule EEVM.Handler.Execution do
end

defp deploy_runtime_code(%MachineState{status: :stopped} = state, new_address) do
runtime_code = state.return_data
runtime_code = state.frame.return_data
size = byte_size(runtime_code)
deposit_cost = size * @code_deposit_gas_per_byte

cond do
size > @max_code_size ->
{:error, %{state | gas: 0, status: :reverted}}
{:error, zero_gas_revert(state)}

state.gas < deposit_cost ->
{:error, %{state | gas: 0, status: :reverted}}
state.frame.gas < deposit_cost ->
{:error, zero_gas_revert(state)}

size > 0 and :binary.first(runtime_code) == 0xEF and
HardforkConfig.enabled?(state.config.hardfork, :eip_3541) ->
{:error, %{state | gas: 0, status: :reverted}}
HardforkConfig.enabled?(state.env.config.hardfork, :eip_3541) ->
{:error, zero_gas_revert(state)}

true ->
updated_db =
state.db
|> Database.put_code(new_address, runtime_code)
|> Database.set_nonce(new_address, 1)

{:ok, %{state | db: updated_db, gas: state.gas - deposit_cost}}
deducted = MachineState.update_frame(state, &%{&1 | gas: &1.gas - deposit_cost})
{:ok, %{deducted | db: updated_db}}
end
end

defp deploy_runtime_code(state, _new_address), do: {:error, state}

defp zero_gas_revert(state) do
state
|> MachineState.update_frame(&%{&1 | gas: 0})
|> Map.put(:status, :reverted)
end

defp transfer_value(%Database{} = db, _from, _to, 0), do: db

defp transfer_value(%Database{} = db, from, to, value) do
Expand Down
8 changes: 4 additions & 4 deletions lib/eevm/handler/post_execution.ex
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,15 @@ defmodule EEVM.Handler.PostExecution do
%Database{} = db_nonce_bumped,
%Block{} = block
) do
gas_used = tx.gas_limit - final_state.gas
gas_used = tx.gas_limit - final_state.frame.gas
effective_price = PreExecution.effective_gas_price(tx, block)

settled_db = settle_state(final_state, db_nonce_bumped, tx, gas_used, effective_price)
coinbase_credited_db = credit_coinbase(settled_db, block, tx, gas_used, effective_price)

status = result_status(final_state.status)

logs = if status == :success, do: final_state.logs, else: []
logs = if status == :success, do: final_state.substate.logs, else: []
logs_bloom = Bloom.from_logs(logs)
receipt_status = if status == :success, do: 1, else: 0

Expand All @@ -62,13 +62,13 @@ defmodule EEVM.Handler.PostExecution do
%TransactionResult{
status: status,
gas_used: gas_used,
gas_refunded: max(final_state.gas, 0),
gas_refunded: max(final_state.frame.gas, 0),
sender: Validation.decode_address(sender_bytes),
logs: logs,
logs_bloom: logs_bloom,
receipt: receipt,
post_state_db: coinbase_credited_db,
return_data: if(status == :success, do: final_state.return_data, else: <<>>),
return_data: if(status == :success, do: final_state.frame.return_data, else: <<>>),
contract_address: if(status == :success, do: contract_address, else: nil)
}}
end
Expand Down
44 changes: 23 additions & 21 deletions lib/eevm/interpreter.ex
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ defmodule EEVM.Interpreter do
MachineState.halt(state, :stopped)

opcode ->
static_cost = if opcode == 0xFE, do: state.gas, else: Static.static_cost(opcode)
static_cost = if opcode == 0xFE, do: state.frame.gas, else: Static.static_cost(opcode)
traced_state = trace_opcode(state, opcode, static_cost)

case MachineState.consume_gas(traced_state, static_cost) do
Expand All @@ -121,7 +121,9 @@ defmodule EEVM.Interpreter do
end

def run_loop(%MachineState{status: :reverted, call_stack: [parent | _]} = state) do
state_with_restored_refund = %{state | refund: parent.refund}
state_with_restored_refund =
MachineState.update_frame(state, &%{&1 | refund: parent.refund})

{:ok, resumed_state} = MachineState.pop_frame(state_with_restored_refund)
run_loop(resumed_state)
end
Expand All @@ -136,36 +138,36 @@ defmodule EEVM.Interpreter do

defp apply_refund(%MachineState{status: status} = state, _initial_gas)
when status in [:reverted, :out_of_gas, :invalid] do
%{state | refund: 0}
MachineState.update_frame(state, &%{&1 | refund: 0})
end

defp apply_refund(%MachineState{} = state, initial_gas) do
gas_used = initial_gas - state.gas
gas_used = initial_gas - state.frame.gas
# EIP-3529 (London+): cap refunds at 1/5 of gas used.
# Pre-London: cap was 1/2 of gas used.
refund_cap_divisor =
if HardforkConfig.enabled?(state.config.hardfork, :eip_3529), do: 5, else: 2
if HardforkConfig.enabled?(state.env.config.hardfork, :eip_3529), do: 5, else: 2

effective_refund = min(state.refund, div(gas_used, refund_cap_divisor))
effective_refund = min(state.frame.refund, div(gas_used, refund_cap_divisor))

refunded_gas = state.gas + effective_refund
refunded_gas = state.frame.gas + effective_refund

calldata_floor_gas =
IntrinsicGas.calldata_floor_gas_cost(state.tx, state.config.hardfork)
IntrinsicGas.calldata_floor_gas_cost(state.env.tx, state.env.config.hardfork)

gas_after_floor =
if calldata_floor_gas > 0 and state.tx.gas_limit > 0 do
min(refunded_gas, max(state.tx.gas_limit - calldata_floor_gas, 0))
if calldata_floor_gas > 0 and state.env.tx.gas_limit > 0 do
min(refunded_gas, max(state.env.tx.gas_limit - calldata_floor_gas, 0))
else
refunded_gas
end

%{state | gas: gas_after_floor, refund: 0}
MachineState.update_frame(state, &%{&1 | gas: gas_after_floor, refund: 0})
end

defp cleanup_touched_empty_accounts(%MachineState{status: :stopped} = state) do
cleaned_db =
Enum.reduce(state.touched_addresses, state.db, fn address, db_acc ->
Enum.reduce(state.substate.touched_addresses, state.db, fn address, db_acc ->
if Database.account_empty?(db_acc, address) do
Database.delete_account(db_acc, address)
else
Expand All @@ -188,7 +190,7 @@ defmodule EEVM.Interpreter do

defp execute_opcode(opcode, state) do
case Registry.info(opcode) do
{:ok, %{state_mutating: true}} when state.is_static ->
{:ok, %{state_mutating: true}} when state.frame.is_static ->
{:ok, MachineState.halt(state, :reverted)}

{:ok, %{module: module}} ->
Expand All @@ -204,18 +206,18 @@ defmodule EEVM.Interpreter do

defp trace_opcode(%MachineState{tracer: nil} = state, _opcode, _static_cost), do: state

defp trace_opcode(%MachineState{tracer: tracer} = state, opcode, static_cost) do
defp trace_opcode(%MachineState{tracer: tracer, frame: frame} = state, opcode, static_cost) do
step = %TraceStep{
pc: state.pc,
pc: frame.pc,
op: Tracer.op_name(opcode),
op_byte: opcode,
gas_remaining: state.gas,
gas_remaining: frame.gas,
gas_cost: static_cost,
stack: Stack.to_list(state.stack),
memory_size: Memory.size(state.memory),
depth: state.depth,
refund: state.refund,
return_data: state.return_data,
stack: Stack.to_list(frame.stack),
memory_size: Memory.size(frame.memory),
depth: frame.depth,
refund: frame.refund,
return_data: frame.return_data,
error: nil
}

Expand Down
73 changes: 0 additions & 73 deletions lib/eevm/interpreter/call_frame.ex

This file was deleted.

Loading
Loading