Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add other evm trace options #845

Merged
merged 2 commits into from
Oct 2, 2023
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
8 changes: 7 additions & 1 deletion src/ethereum/trace.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,13 @@ class GasAndRefund:
]


def evm_trace(evm: object, event: TraceEvent) -> None:
def evm_trace(
evm: object,
event: TraceEvent,
trace_memory: bool = False,
trace_stack: bool = True,
trace_return_data: bool = False,
) -> None:
"""
Create a trace of the event.
"""
Expand Down
14 changes: 11 additions & 3 deletions src/ethereum_spec_tools/evm_tools/t8n/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import json
import os
import sys
from functools import partial
from typing import Any

from ethereum import rlp, trace
Expand Down Expand Up @@ -62,12 +63,11 @@ def t8n_arguments(subparsers: argparse._SubParsersAction) -> None:
t8n_parser.add_argument(
"--state.reward", dest="state_reward", type=int, default=0
)
# TODO: Add support for the following trace options
t8n_parser.add_argument("--trace", action="store_true")
t8n_parser.add_argument("--trace.memory", action="store_true")
t8n_parser.add_argument("--trace.nomemory", action="store_true")
t8n_parser.add_argument("--trace.noreturndata", action="store_true")
t8n_parser.add_argument("--trace.nostack ", action="store_true")
t8n_parser.add_argument("--trace.nostack", action="store_true")
t8n_parser.add_argument("--trace.returndata", action="store_true")


Expand All @@ -92,7 +92,15 @@ def __init__(self, options: Any) -> None:
)

if self.options.trace:
trace.evm_trace = evm_trace
trace_memory = getattr(self.options, "trace.memory", False)
trace_stack = not getattr(self.options, "trace.nostack", False)
trace_return_data = getattr(self.options, "trace.returndata")
trace.evm_trace = partial(
evm_trace,
trace_memory=trace_memory,
trace_stack=trace_stack,
trace_return_data=trace_return_data,
)
self.logger = get_stream_logger("T8N")

super().__init__(
Expand Down
64 changes: 48 additions & 16 deletions src/ethereum_spec_tools/evm_tools/t8n/evm_trace.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,10 @@ class Trace:
op: str
gas: str
gasCost: str
memory: Optional[str]
memSize: int
stack: List[str]
stack: Optional[List[str]]
returnData: Optional[str]
depth: int
refund: int
opName: str
Expand Down Expand Up @@ -95,21 +97,45 @@ class Evm(Protocol):
refund_counter: int
running: bool
message: Message
return_data: Bytes


def evm_trace(evm: object, event: TraceEvent) -> None:
def evm_trace(
evm: object,
event: TraceEvent,
trace_memory: bool = False,
trace_stack: bool = True,
trace_return_data: bool = False,
) -> None:
"""
Create a trace of the event.
"""
assert isinstance(evm, Evm)
last_trace: Optional[Union[Trace, FinalTrace]]

last_trace = None
if evm.env.traces:
last_trace = evm.env.traces[-1]

refund_counter = evm.refund_counter
parent_evm = evm.message.parent_evm
while parent_evm is not None:
refund_counter += parent_evm.refund_counter
parent_evm = parent_evm.message.parent_evm

len_memory = len(evm.memory)

return_data = None
if trace_return_data and evm.return_data:
return_data = "0x" + evm.return_data.hex()

memory = None
if trace_memory and len_memory > 0:
memory = "0x" + evm.memory.hex()

stack = None
if trace_stack:
stack = [hex(i) for i in evm.stack]

if isinstance(event, TransactionStart):
pass
elif isinstance(event, TransactionEnd):
Expand All @@ -121,8 +147,10 @@ def evm_trace(evm: object, event: TraceEvent) -> None:
op="0x" + event.address.hex().lstrip("0"),
gas=hex(evm.gas_left),
gasCost="0x0",
memSize=len(evm.memory),
stack=[hex(i) for i in evm.stack],
memory=memory,
memSize=len_memory,
stack=stack,
returnData=return_data,
depth=evm.message.depth + 1,
refund=refund_counter,
opName="0x" + event.address.hex().lstrip("0"),
Expand All @@ -131,7 +159,6 @@ def evm_trace(evm: object, event: TraceEvent) -> None:

evm.env.traces.append(new_trace)
elif isinstance(event, PrecompileEnd):
last_trace = evm.env.traces[-1]
assert isinstance(last_trace, Trace)

last_trace.gasCostTraced = True
Expand All @@ -142,24 +169,22 @@ def evm_trace(evm: object, event: TraceEvent) -> None:
op=event.op.value,
gas=hex(evm.gas_left),
gasCost="0x0",
memSize=len(evm.memory),
stack=[hex(i) for i in evm.stack],
memory=memory,
memSize=len_memory,
stack=stack,
returnData=return_data,
depth=evm.message.depth + 1,
refund=refund_counter,
opName=str(event.op).split(".")[-1],
)

evm.env.traces.append(new_trace)
elif isinstance(event, OpEnd):
last_trace = evm.env.traces[-1]
assert isinstance(last_trace, Trace)

last_trace.gasCostTraced = True
last_trace.errorTraced = True
elif isinstance(event, OpException):
last_trace = None
if evm.env.traces:
last_trace = evm.env.traces[-1]
if last_trace is not None:
assert isinstance(last_trace, Trace)
if (
Expand All @@ -180,8 +205,10 @@ def evm_trace(evm: object, event: TraceEvent) -> None:
op="InvalidOpcode",
gas=hex(evm.gas_left),
gasCost="0x0",
memSize=len(evm.memory),
stack=[hex(i) for i in evm.stack],
memory=memory,
memSize=len_memory,
stack=stack,
returnData=return_data,
depth=evm.message.depth + 1,
refund=refund_counter,
opName="InvalidOpcode",
Expand All @@ -202,13 +229,18 @@ def evm_trace(evm: object, event: TraceEvent) -> None:
elif len(evm.code) == 0:
return
else:
evm_trace(evm, OpStart(event.op))
evm_trace(
evm,
OpStart(event.op),
trace_memory,
trace_stack,
trace_return_data,
)
elif isinstance(event, GasAndRefund):
if not evm.env.traces:
# In contract creation transactions, there may not be any traces
return

last_trace = evm.env.traces[-1]
assert isinstance(last_trace, Trace)

if not last_trace.gasCostTraced:
Expand Down
Loading