Skip to content

Commit

Permalink
fix: set root kwargs for parity trace at end of builder-logic (#29)
Browse files Browse the repository at this point in the history
  • Loading branch information
antazoey authored Nov 30, 2022
1 parent da997e0 commit b042c1f
Show file tree
Hide file tree
Showing 6 changed files with 58 additions and 51 deletions.
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):
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)
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

0 comments on commit b042c1f

Please sign in to comment.