Skip to content

Commit

Permalink
Merge pull request #828 from gurukamath/evm-trace-final
Browse files Browse the repository at this point in the history
Implement Evm Trace
  • Loading branch information
petertdavies authored Sep 26, 2023
2 parents 3144038 + 753416e commit b2ddda3
Show file tree
Hide file tree
Showing 124 changed files with 1,177 additions and 349 deletions.
10 changes: 0 additions & 10 deletions src/ethereum/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
possible, to aid in defining the behavior of Ethereum clients.
"""
import sys
from typing import Any

__version__ = "0.1.0"

Expand All @@ -26,12 +25,3 @@
#
EVM_RECURSION_LIMIT = 1024 * 12
sys.setrecursionlimit(max(EVM_RECURSION_LIMIT, sys.getrecursionlimit()))


def evm_trace(evm: Any, op: Any) -> None:
"""
autoapi_noshow
Placeholder for an evm trace function. The spec does not trace evm by
default. EVM tracing will be injected if the user requests it.
"""
pass
1 change: 1 addition & 0 deletions src/ethereum/arrow_glacier/fork.py
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,7 @@ def apply_body(
difficulty=block_difficulty,
state=state,
chain_id=chain_id,
traces=[],
)

gas_used, logs, has_erred = process_transaction(env, tx)
Expand Down
1 change: 1 addition & 0 deletions src/ethereum/arrow_glacier/utils/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,4 +111,5 @@ def prepare_message(
is_static=is_static,
accessed_addresses=accessed_addresses,
accessed_storage_keys=set(preaccessed_storage_keys),
parent_evm=None,
)
2 changes: 2 additions & 0 deletions src/ethereum/arrow_glacier/vm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class Environment:
difficulty: Uint
state: State
chain_id: U64
traces: List[dict]


@dataclass
Expand All @@ -65,6 +66,7 @@ class Message:
is_static: bool
accessed_addresses: Set[Address]
accessed_storage_keys: Set[Tuple[Address, Bytes32]]
parent_evm: Optional["Evm"]


@dataclass
Expand Down
3 changes: 3 additions & 0 deletions src/ethereum/arrow_glacier/vm/gas.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from typing import List, Tuple

from ethereum.base_types import U256, Uint
from ethereum.trace import GasAndRefund, evm_trace
from ethereum.utils.numeric import ceil32

from . import Evm
Expand Down Expand Up @@ -106,6 +107,8 @@ def charge_gas(evm: Evm, amount: Uint) -> None:
The amount of gas the current operation requires.
"""
evm_trace(evm, GasAndRefund(amount))

if evm.gas_left < amount:
raise OutOfGasError
else:
Expand Down
4 changes: 2 additions & 2 deletions src/ethereum/arrow_glacier/vm/instructions/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,6 @@ def sstore(evm: Evm) -> None:
else:
gas_cost += GAS_WARM_ACCESS

charge_gas(evm, gas_cost)

# Refund Counter Calculation
if current_value != new_value:
if original_value != 0 and current_value != 0 and new_value == 0:
Expand All @@ -118,6 +116,8 @@ def sstore(evm: Evm) -> None:
GAS_STORAGE_UPDATE - GAS_COLD_SLOAD - GAS_WARM_ACCESS
)

charge_gas(evm, gas_cost)

# OPERATION
ensure(not evm.message.is_static, WriteInStaticContext)
set_storage(evm.env.state, evm.message.current_target, key, new_value)
Expand Down
11 changes: 7 additions & 4 deletions src/ethereum/arrow_glacier/vm/instructions/system.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ def generic_create(
is_static=False,
accessed_addresses=evm.accessed_addresses.copy(),
accessed_storage_keys=evm.accessed_storage_keys.copy(),
parent_evm=evm,
)
child_evm = process_create_message(child_message, evm.env)

Expand Down Expand Up @@ -283,6 +284,7 @@ def generic_call(
is_static=True if is_staticcall else evm.message.is_static,
accessed_addresses=evm.accessed_addresses.copy(),
accessed_storage_keys=evm.accessed_storage_keys.copy(),
parent_evm=evm,
)
child_evm = process_message(child_message, evm.env)

Expand Down Expand Up @@ -468,17 +470,18 @@ def selfdestruct(evm: Evm) -> None:
beneficiary = to_address(pop(evm.stack))

# GAS
gas_cost = GAS_SELF_DESTRUCT
if beneficiary not in evm.accessed_addresses:
evm.accessed_addresses.add(beneficiary)
charge_gas(evm, GAS_COLD_ACCOUNT_ACCESS)
gas_cost += GAS_COLD_ACCOUNT_ACCESS

if (
not is_account_alive(evm.env.state, beneficiary)
and get_account(evm.env.state, evm.message.current_target).balance != 0
):
charge_gas(evm, GAS_SELF_DESTRUCT + GAS_SELF_DESTRUCT_NEW_ACCOUNT)
else:
charge_gas(evm, GAS_SELF_DESTRUCT)
gas_cost += GAS_SELF_DESTRUCT_NEW_ACCOUNT

charge_gas(evm, gas_cost)

# OPERATION
ensure(not evm.message.is_static, WriteInStaticContext)
Expand Down
27 changes: 24 additions & 3 deletions src/ethereum/arrow_glacier/vm/interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,17 @@
from dataclasses import dataclass
from typing import Iterable, Set, Tuple

from ethereum import evm_trace
from ethereum.base_types import U256, Bytes0, Uint
from ethereum.trace import (
EvmStop,
OpEnd,
OpException,
OpStart,
PrecompileEnd,
PrecompileStart,
TransactionEnd,
evm_trace,
)
from ethereum.utils.ensure import ensure

from ..fork_types import Address, Log
Expand Down Expand Up @@ -120,6 +129,11 @@ def process_message_call(
touched_accounts = evm.touched_accounts
refund_counter = U256(evm.refund_counter)

tx_end = TransactionEnd(
message.gas - evm.gas_left, evm.output, evm.has_erred
)
evm_trace(evm, tx_end)

return MessageCallOutput(
gas_left=evm.gas_left,
refund_counter=refund_counter,
Expand Down Expand Up @@ -243,6 +257,7 @@ def execute_code(message: Message, env: Environment) -> Evm:
"""
code = message.code
valid_jump_destinations = get_valid_jump_destinations(code)

evm = Evm(
pc=Uint(0),
stack=[],
Expand All @@ -267,8 +282,9 @@ def execute_code(message: Message, env: Environment) -> Evm:
try:

if evm.message.code_address in PRE_COMPILED_CONTRACTS:
evm_trace(evm, evm.message.code_address)
evm_trace(evm, PrecompileStart(evm.message.code_address))
PRE_COMPILED_CONTRACTS[evm.message.code_address](evm)
evm_trace(evm, PrecompileEnd())
return evm

while evm.running and evm.pc < len(evm.code):
Expand All @@ -277,14 +293,19 @@ def execute_code(message: Message, env: Environment) -> Evm:
except ValueError:
raise InvalidOpcode(evm.code[evm.pc])

evm_trace(evm, op)
evm_trace(evm, OpStart(op))
op_implementation[op](evm)
evm_trace(evm, OpEnd())

evm_trace(evm, EvmStop(Ops.STOP))

except ExceptionalHalt:
evm_trace(evm, OpException())
evm.gas_left = Uint(0)
evm.output = b""
evm.has_erred = True
except Revert as e:
evm_trace(evm, OpException())
evm.error = e
evm.has_erred = True
return evm
1 change: 1 addition & 0 deletions src/ethereum/berlin/fork.py
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,7 @@ def apply_body(
difficulty=block_difficulty,
state=state,
chain_id=chain_id,
traces=[],
)

gas_used, logs, has_erred = process_transaction(env, tx)
Expand Down
1 change: 1 addition & 0 deletions src/ethereum/berlin/utils/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,4 +111,5 @@ def prepare_message(
is_static=is_static,
accessed_addresses=accessed_addresses,
accessed_storage_keys=set(preaccessed_storage_keys),
parent_evm=None,
)
2 changes: 2 additions & 0 deletions src/ethereum/berlin/vm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class Environment:
difficulty: Uint
state: State
chain_id: U64
traces: List[dict]


@dataclass
Expand All @@ -64,6 +65,7 @@ class Message:
is_static: bool
accessed_addresses: Set[Address]
accessed_storage_keys: Set[Tuple[Address, Bytes32]]
parent_evm: Optional["Evm"]


@dataclass
Expand Down
3 changes: 3 additions & 0 deletions src/ethereum/berlin/vm/gas.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from typing import List, Tuple

from ethereum.base_types import U256, Uint
from ethereum.trace import GasAndRefund, evm_trace
from ethereum.utils.numeric import ceil32

from . import Evm
Expand Down Expand Up @@ -107,6 +108,8 @@ def charge_gas(evm: Evm, amount: Uint) -> None:
The amount of gas the current operation requires.
"""
evm_trace(evm, GasAndRefund(amount))

if evm.gas_left < amount:
raise OutOfGasError
else:
Expand Down
4 changes: 2 additions & 2 deletions src/ethereum/berlin/vm/instructions/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,6 @@ def sstore(evm: Evm) -> None:
else:
gas_cost += GAS_WARM_ACCESS

charge_gas(evm, gas_cost)

# Refund Counter Calculation
if current_value != new_value:
if original_value != 0 and current_value != 0 and new_value == 0:
Expand All @@ -118,6 +116,8 @@ def sstore(evm: Evm) -> None:
GAS_STORAGE_UPDATE - GAS_COLD_SLOAD - GAS_WARM_ACCESS
)

charge_gas(evm, gas_cost)

# OPERATION
ensure(not evm.message.is_static, WriteInStaticContext)
set_storage(evm.env.state, evm.message.current_target, key, new_value)
Expand Down
24 changes: 19 additions & 5 deletions src/ethereum/berlin/vm/instructions/system.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
GAS_SELF_DESTRUCT_NEW_ACCOUNT,
GAS_WARM_ACCESS,
GAS_ZERO,
REFUND_SELF_DESTRUCT,
calculate_gas_extend_memory,
calculate_message_call_gas,
charge_gas,
Expand Down Expand Up @@ -114,6 +115,7 @@ def generic_create(
is_static=False,
accessed_addresses=evm.accessed_addresses.copy(),
accessed_storage_keys=evm.accessed_storage_keys.copy(),
parent_evm=evm,
)
child_evm = process_create_message(child_message, evm.env)

Expand Down Expand Up @@ -283,6 +285,7 @@ def generic_call(
is_static=True if is_staticcall else evm.message.is_static,
accessed_addresses=evm.accessed_addresses.copy(),
accessed_storage_keys=evm.accessed_storage_keys.copy(),
parent_evm=evm,
)
child_evm = process_message(child_message, evm.env)

Expand Down Expand Up @@ -468,22 +471,33 @@ def selfdestruct(evm: Evm) -> None:
beneficiary = to_address(pop(evm.stack))

# GAS
gas_cost = GAS_SELF_DESTRUCT
if beneficiary not in evm.accessed_addresses:
evm.accessed_addresses.add(beneficiary)
charge_gas(evm, GAS_COLD_ACCOUNT_ACCESS)
gas_cost += GAS_COLD_ACCOUNT_ACCESS

if (
not is_account_alive(evm.env.state, beneficiary)
and get_account(evm.env.state, evm.message.current_target).balance != 0
):
charge_gas(evm, GAS_SELF_DESTRUCT + GAS_SELF_DESTRUCT_NEW_ACCOUNT)
else:
charge_gas(evm, GAS_SELF_DESTRUCT)
gas_cost += GAS_SELF_DESTRUCT_NEW_ACCOUNT

originator = evm.message.current_target

refunded_accounts = evm.accounts_to_delete
parent_evm = evm.message.parent_evm
while parent_evm is not None:
refunded_accounts.update(parent_evm.accounts_to_delete)
parent_evm = parent_evm.message.parent_evm

if originator not in refunded_accounts:
evm.refund_counter += REFUND_SELF_DESTRUCT

charge_gas(evm, gas_cost)

# OPERATION
ensure(not evm.message.is_static, WriteInStaticContext)

originator = evm.message.current_target
beneficiary_balance = get_account(evm.env.state, beneficiary).balance
originator_balance = get_account(evm.env.state, originator).balance

Expand Down
Loading

0 comments on commit b2ddda3

Please sign in to comment.