generated from ApeWorX/project-template
-
-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #7 from banteg/feat/parity-trace
- Loading branch information
Showing
17 changed files
with
2,679 additions
and
59 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,12 @@ | ||
from .base import CallTreeNode, CallType, TraceFrame, get_calltree_from_trace | ||
from .base import CallTreeNode, CallType, TraceFrame, get_calltree_from_geth_trace | ||
from .parity import ParityTrace, ParityTraceList, get_calltree_from_parity_trace | ||
|
||
__all__ = ["CallTreeNode", "CallType", "get_calltree_from_trace", "TraceFrame"] | ||
__all__ = [ | ||
"CallTreeNode", | ||
"CallType", | ||
"get_calltree_from_geth_trace", | ||
"get_calltree_from_parity_trace", | ||
"ParityTrace", | ||
"ParityTraceList", | ||
"TraceFrame", | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
from typing import List, Optional, Type, Union | ||
|
||
from pydantic import BaseModel, Field, validator | ||
|
||
from evm_trace.base import CallTreeNode, CallType | ||
from evm_trace.display import DisplayableCallTreeNode | ||
|
||
|
||
class CallAction(BaseModel): | ||
gas: int | ||
input: Optional[str] = None | ||
receiver: Optional[str] = Field(alias="to", default=None) | ||
sender: str = Field(alias="from") | ||
value: int | ||
# only used to recover the specific call type | ||
call_type: str = Field(alias="callType", repr=False) | ||
|
||
@validator("value", "gas", pre=True) | ||
def convert_integer(cls, v): | ||
return int(v, 16) | ||
|
||
|
||
class CreateAction(BaseModel): | ||
gas: int | ||
init: str | ||
value: int | ||
|
||
@validator("value", "gas", pre=True) | ||
def convert_integer(cls, v): | ||
return int(v, 16) | ||
|
||
|
||
class SelfDestructAction(BaseModel): | ||
address: str | ||
balance: int | ||
|
||
@validator("balance", pre=True) | ||
def convert_integer(cls, v): | ||
return int(v, 16) | ||
|
||
|
||
class ActionResult(BaseModel): | ||
gas_used: str = Field(alias="gasUsed") | ||
|
||
@validator("gas_used", pre=True) | ||
def convert_integer(cls, v): | ||
return int(v, 16) | ||
|
||
|
||
class CallResult(ActionResult): | ||
output: str | ||
|
||
|
||
class CreateResult(ActionResult): | ||
address: str | ||
code: str | ||
|
||
|
||
ParityTraceAction = Union[CreateAction, CallAction, SelfDestructAction] | ||
ParityTraceResult = Union[CallResult, CreateResult] | ||
|
||
|
||
class ParityTrace(BaseModel): | ||
error: Optional[str] = None | ||
action: ParityTraceAction | ||
block_hash: str = Field(alias="blockHash") | ||
call_type: CallType = Field(alias="type") | ||
result: Optional[ParityTraceResult] = None | ||
subtraces: int | ||
trace_address: List[int] = Field(alias="traceAddress") | ||
transaction_hash: str = Field(alias="transactionHash") | ||
|
||
@validator("call_type", pre=True) | ||
def convert_call_type(cls, v, values) -> CallType: | ||
if isinstance(values["action"], CallAction): | ||
v = values["action"].call_type | ||
value = v.upper() | ||
if value == "SUICIDE": | ||
value = "SELFDESTRUCT" | ||
|
||
return CallType(value) | ||
|
||
|
||
class ParityTraceList(BaseModel): | ||
__root__: List[ParityTrace] | ||
|
||
# pydantic models with custom root don't have this by default | ||
def __iter__(self): | ||
return iter(self.__root__) | ||
|
||
def __getitem__(self, item): | ||
return self.__root__[item] | ||
|
||
|
||
def get_calltree_from_parity_trace( | ||
traces: ParityTraceList, | ||
root: Optional[ParityTrace] = None, | ||
display_cls: Type[DisplayableCallTreeNode] = DisplayableCallTreeNode, | ||
) -> CallTreeNode: | ||
""" | ||
Create a :class:`~evm_trace.base.CallTreeNode` from output models using the Parity approach | ||
(e.g. from the ``trace_transaction`` RPC). | ||
Args: | ||
traces (List[: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. | ||
display_cls (Type[DisplayableCallTreeNode]]: A custom class to use for representing | ||
the call tree. Defaults to :class:`~evm_trace.display.DisplayableCallTreeNode`. | ||
Returns: | ||
:class:`~evm_trace.base.CallTreeNode` | ||
""" | ||
if root is None: | ||
root = traces[0] | ||
|
||
node_kwargs = dict( | ||
call_type=root.call_type, | ||
error=root.error is not None, | ||
display_cls=display_cls, | ||
) | ||
|
||
if root.call_type == CallType.CREATE: | ||
create_action: CreateAction = root.action # type: ignore | ||
create_result: CreateResult = root.result # type: ignore | ||
|
||
node_kwargs.update( | ||
value=create_action.value, | ||
gas_limit=create_action.gas, | ||
) | ||
if create_result: | ||
node_kwargs.update(gas_used=create_result.gas_used, address=create_result.address) | ||
|
||
elif root.call_type in ( | ||
CallType.CALL, | ||
CallType.DELEGATECALL, | ||
CallType.STATICCALL, | ||
CallType.CALLCODE, | ||
): | ||
call_action: CallAction = root.action # type: ignore | ||
call_result: CallResult = root.result # type: ignore | ||
|
||
node_kwargs.update( | ||
address=call_action.receiver, | ||
value=call_action.value, | ||
gas_limit=call_action.gas, | ||
calldata=call_action.input, | ||
) | ||
# no result if the call has an error | ||
if call_result: | ||
node_kwargs.update( | ||
gas_cost=call_result.gas_used, | ||
returndata=call_result.output, | ||
) | ||
|
||
elif root.call_type == CallType.SELFDESTRUCT: | ||
selfdestruct_action: SelfDestructAction = root.action # type: ignore | ||
node_kwargs.update( | ||
address=selfdestruct_action.address, | ||
) | ||
|
||
subtraces = [ | ||
sub | ||
for sub in traces | ||
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 = CallTreeNode.parse_obj(node_kwargs) | ||
return node |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.