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

fix: set root kwargs for parity trace at end of builder-logic #29

Merged
merged 2 commits into from
Nov 30, 2022
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: 4 additions & 4 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,17 @@ repos:
- id: black
name: black

- repo: https://gitlab.com/pycqa/flake8
- repo: https://github.com/pycqa/flake8
rev: 5.0.4
hooks:
- id: flake8

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v0.982
rev: v0.991
hooks:
- id: mypy
additional_dependencies: [types-PyYAML, types-requests]
additional_dependencies: [types-PyYAML, types-requests, types-setuptools]


default_language_version:
python: python3.9
python: python3
14 changes: 9 additions & 5 deletions evm_trace/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from eth_utils import to_int
from ethpm_types import BaseModel, HexBytes
from pydantic import Field
from pydantic import Field, validator

from evm_trace.display import get_tree_display
from evm_trace.enums import CallType
Expand All @@ -19,6 +19,10 @@ class TraceFrame(BaseModel):
memory: List[HexBytes] = []
storage: Dict[HexBytes, HexBytes] = {}

@validator("pc", "gas", "gas_cost", "depth", pre=True)
def validate_ints(cls, value):
This conversation was marked as resolved.
Show resolved Hide resolved
return int(value, 16) if isinstance(value, str) else value


class CallTreeNode(BaseModel):
call_type: CallType
Expand Down Expand Up @@ -51,19 +55,19 @@ def get_calltree_from_geth_trace(

Args:
trace (Iterator[TraceFrame]): Iterator of transaction trace frames.
show_internal (bool): Boolean whether to display internal calls. Defaulted to False.
show_internal (bool): Boolean whether to display internal calls.
Defaults to ``False``.
root_node_kwargs (dict): Keyword arguments passed to the root ``CallTreeNode``.

Returns:
:class:`~evm_trace.base.CallTreeNode`: Call tree of transaction trace.
"""

node = _create_node_from_call(
return _create_node_from_call(
trace=trace,
show_internal=show_internal,
**root_node_kwargs,
)
return node


def _extract_memory(offset: HexBytes, size: HexBytes, memory: List[HexBytes]) -> HexBytes:
Expand All @@ -82,7 +86,7 @@ def _extract_memory(offset: HexBytes, size: HexBytes, memory: List[HexBytes]) ->
size_int = to_int(size)

if size_int == 0:
return HexBytes(b"")
return HexBytes("")

offset_int = to_int(offset)

Expand Down
9 changes: 5 additions & 4 deletions evm_trace/display.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from typing import TYPE_CHECKING, Iterator, Optional
from typing import TYPE_CHECKING, Iterator, Optional, cast

from eth_typing import ChecksumAddress
from eth_utils import to_checksum_address

if TYPE_CHECKING:
Expand All @@ -10,7 +11,7 @@ def get_tree_display(call: "CallTreeNode") -> str:
return "\n".join([str(t) for t in TreeRepresentation.make_tree(call)])


class TreeRepresentation(object):
class TreeRepresentation:
FILE_MIDDLE_PREFIX = "├──"
FILE_LAST_PREFIX = "└──"
PARENT_PREFIX_MIDDLE = " "
Expand All @@ -37,9 +38,9 @@ def title(self) -> str:

try:
address = to_checksum_address(address_hex_str) if address_hex_str else None
except ImportError:
except (ImportError, ValueError):
# Ignore checksumming if user does not have eth-hash backend installed.
address = address_hex_str # type: ignore
address = cast(ChecksumAddress, address_hex_str)

cost = self.call.gas_cost
call_path = str(address) if address else ""
Expand Down
33 changes: 19 additions & 14 deletions evm_trace/parity.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Any, Dict, List, Optional, Union
from typing import Any, Dict, List, Optional, Union, cast

from pydantic import BaseModel, Field, validator

Expand Down Expand Up @@ -101,7 +101,7 @@ def get_calltree_from_parity_trace(
(e.g. from the ``trace_transaction`` RPC).

Args:
traces (List[:class:~evm_trace.parity.ParityTraceList]): The list of parity trace nodes,
traces (:class:~evm_trace.parity.ParityTraceList): The list of parity trace nodes,
likely loaded from the response data from the ``trace_transaction`` RPC response.
root (:class:`~evm_trace.parity.ParityTrace`): The root parity trace node. Optional, uses
the first item by default.
Expand All @@ -113,15 +113,16 @@ def get_calltree_from_parity_trace(
"""
root = root or traces[0]
failed = root.error is not None
node_kwargs: Dict[Any, Any] = dict(
call_type=root.call_type,
failed=failed,
**root_kwargs,
)
node_kwargs: Dict[Any, Any] = {
"call_type": root.call_type,
"failed": failed,
}

if root.call_type == CallType.CREATE:
create_action: CreateAction = root.action # type: ignore
create_result: CreateResult = root.result # type: ignore
create_action: CreateAction = cast(CreateAction, root.action)
create_result: Optional[CreateResult] = (
cast(CreateResult, root.result) if root.result is not None else None
)

node_kwargs.update(
value=create_action.value,
Expand All @@ -136,8 +137,10 @@ def get_calltree_from_parity_trace(
CallType.STATICCALL,
CallType.CALLCODE,
):
call_action: CallAction = root.action # type: ignore
call_result: CallResult = root.result # type: ignore
call_action: CallAction = cast(CallAction, root.action)
call_result: Optional[CallResult] = (
cast(CallResult, root.result) if root.result is not None else None
)

node_kwargs.update(
address=call_action.receiver,
Expand All @@ -153,17 +156,19 @@ def get_calltree_from_parity_trace(
)

elif root.call_type == CallType.SELFDESTRUCT:
selfdestruct_action: SelfDestructAction = root.action # type: ignore
selfdestruct_action: SelfDestructAction = cast(SelfDestructAction, root.action)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kept wanting to read this as self.destruct_action 😂

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dang overloaded self!

node_kwargs.update(
address=selfdestruct_action.address,
)

subtraces = [
trace_list: List[ParityTrace] = [x for x in traces]
subtraces: List[ParityTrace] = [
sub
for sub in traces
for sub in trace_list
if len(sub.trace_address) == len(root.trace_address) + 1
and sub.trace_address[:-1] == root.trace_address
]
node_kwargs["calls"] = [get_calltree_from_parity_trace(traces, root=sub) for sub in subtraces]
node_kwargs = {**node_kwargs, **root_kwargs}
node = CallTreeNode.parse_obj(node_kwargs)
return node
37 changes: 17 additions & 20 deletions evm_trace/vmtrace.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ class StorageDiff(Struct):

class VMTraceFrame(Struct):
"""
A synthetic trace frame represening the state at a step of execution.
A synthetic trace frame representing the state at a step of execution.
"""

address: str
Expand All @@ -106,24 +106,21 @@ def to_trace_frames(
Replays a VMTrace and yields trace frames at each step of the execution.
Can be used as a much faster drop-in replacement for Geth-style traces.

Arguments:
trace (VMTrace):
a decoded trace from a `trace_` rpc.
depth (int):
a depth of the call being processed. automatically populated.
address (str):
the address of the contract being executed. auto populated except the root call.
copy_memory (bool):
whether to copy memory when returning trace frames. disable for a speedup when dealing
with traces using a large amount of memory. when disabled, `VMTraceFrame.memory` becomes
`memoryview` instead of `bytes`, which works like a pointer at the memory `bytearray`.
this means you must process the frames immediately, otherwise you risk memory value
mutating further into execution.
Args:
trace (VMTrace): A decoded trace from a `trace_` rpc.
depth (int): A depth of the call being processed. automatically populated.
address (str): The address of the contract being executed. auto populated
except the root call.
copy_memory (bool): Whether to copy memory when returning trace frames.
Disable for a speedup when dealing with traces using a large amount of memory.
when disabled, `VMTraceFrame.memory` becomes `memoryview` instead of `bytes`, which
works like a pointer at the memory `bytearray`. this means you must process the
frames immediately, otherwise you risk memory value mutating further into execution.

Returns:
Iterator[VMTraceFrame]:
an iterator of synthetic traces which can be used as a drop-in replacement for
Geth-style traces. also contains the address of the current contract context.
Iterator[VMTraceFrame]: An iterator of synthetic traces which can be used as a drop-in
replacement for Geth-style traces. also contains the address of the current contract
context.
"""
memory = Memory()
stack = Stack()
Expand Down Expand Up @@ -182,7 +179,7 @@ class RPCTraceResult(Struct):
def dec_hook(type: Type, obj: Any) -> Any:
if type is uint256:
return uint256(obj, 16)
if type is HexBytes:
elif type is HexBytes:
return HexBytes(obj)


Expand All @@ -194,5 +191,5 @@ def from_rpc_response(buffer: bytes) -> Union[VMTrace, List[VMTrace]]:

if isinstance(resp.result, list):
return [i.vmTrace for i in resp.result]
else:
return resp.result.vmTrace

return resp.result.vmTrace
8 changes: 4 additions & 4 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from setuptools import find_packages, setup # type: ignore
from setuptools import find_packages, setup

extras_require = {
"test": [ # `test` GitHub Action jobs uses this
Expand All @@ -12,7 +11,8 @@
],
"lint": [
"black>=22.10.0", # auto-formatter and linter
"mypy>=0.982", # Static type analyzer
"mypy>=0.991", # Static type analyzer
"types-setuptools", # Needed due to mypy typeshed
"flake8>=5.0.4", # Style linter
"isort>=5.10.1", # Import sorting linter
],
Expand All @@ -23,7 +23,7 @@
],
"dev": [
"commitizen", # Manage commits and publishing releases
"pre-commit", # Ensure that linters are run prior to commiting
"pre-commit", # Ensure that linters are run prior to committing
"pytest-watch", # `ptw` test watcher/runner
"IPython", # Console for interacting
"ipdb", # Debugger (Must use `export PYTHONBREAKPOINT=ipdb.set_trace`)
Expand Down