From 7ea31c46622885a256d86b8d7d18f8055d7b50fe Mon Sep 17 00:00:00 2001 From: unparalleled-js Date: Tue, 29 Nov 2022 17:35:52 -0600 Subject: [PATCH 1/2] chore: random stuff --- .pre-commit-config.yaml | 8 ++++---- evm_trace/base.py | 14 +++++++++----- evm_trace/display.py | 9 +++++---- evm_trace/parity.py | 23 ++++++++++++++--------- evm_trace/vmtrace.py | 37 +++++++++++++++++-------------------- setup.py | 8 ++++---- 6 files changed, 53 insertions(+), 46 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 24efb07..bee797b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -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 diff --git a/evm_trace/base.py b/evm_trace/base.py index 947f3b4..dddc150 100644 --- a/evm_trace/base.py +++ b/evm_trace/base.py @@ -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 @@ -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 @@ -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: @@ -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) diff --git a/evm_trace/display.py b/evm_trace/display.py index 02476f0..3826cb6 100644 --- a/evm_trace/display.py +++ b/evm_trace/display.py @@ -1,5 +1,6 @@ -from typing import TYPE_CHECKING, Iterator, Optional +from typing import TYPE_CHECKING, Iterator, Optional, cast +from ape.types import AddressType from eth_utils import to_checksum_address if TYPE_CHECKING: @@ -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 = " " @@ -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(AddressType, address_hex_str) cost = self.call.gas_cost call_path = str(address) if address else "" diff --git a/evm_trace/parity.py b/evm_trace/parity.py index 7861f39..6ca7d51 100644 --- a/evm_trace/parity.py +++ b/evm_trace/parity.py @@ -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 @@ -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. @@ -120,8 +120,10 @@ def get_calltree_from_parity_trace( ) 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, @@ -136,8 +138,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, @@ -153,14 +157,15 @@ 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 ] diff --git a/evm_trace/vmtrace.py b/evm_trace/vmtrace.py index 244bffa..8bb8c78 100644 --- a/evm_trace/vmtrace.py +++ b/evm_trace/vmtrace.py @@ -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 @@ -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() @@ -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) @@ -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 diff --git a/setup.py b/setup.py index 011757d..5261190 100644 --- a/setup.py +++ b/setup.py @@ -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 @@ -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 ], @@ -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`) From fd16212a5b629e62f799529fec11645ee5ca9eca Mon Sep 17 00:00:00 2001 From: unparalleled-js Date: Tue, 29 Nov 2022 20:00:54 -0600 Subject: [PATCH 2/2] fix: import --- evm_trace/display.py | 4 ++-- evm_trace/parity.py | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/evm_trace/display.py b/evm_trace/display.py index 3826cb6..2af1af3 100644 --- a/evm_trace/display.py +++ b/evm_trace/display.py @@ -1,6 +1,6 @@ from typing import TYPE_CHECKING, Iterator, Optional, cast -from ape.types import AddressType +from eth_typing import ChecksumAddress from eth_utils import to_checksum_address if TYPE_CHECKING: @@ -40,7 +40,7 @@ def title(self) -> str: address = to_checksum_address(address_hex_str) if address_hex_str else None except (ImportError, ValueError): # Ignore checksumming if user does not have eth-hash backend installed. - address = cast(AddressType, address_hex_str) + address = cast(ChecksumAddress, address_hex_str) cost = self.call.gas_cost call_path = str(address) if address else "" diff --git a/evm_trace/parity.py b/evm_trace/parity.py index 6ca7d51..a4f0ba3 100644 --- a/evm_trace/parity.py +++ b/evm_trace/parity.py @@ -113,11 +113,10 @@ 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 = cast(CreateAction, root.action) @@ -170,5 +169,6 @@ def get_calltree_from_parity_trace( 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