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

Handle erigon traces and other failing transactions #1245

Merged
merged 5 commits into from
Sep 11, 2021
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
4 changes: 2 additions & 2 deletions brownie/network/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from brownie._config import CONFIG, _get_data_folder
from brownie._singleton import _Singleton
from brownie.convert import Wei
from brownie.exceptions import BrownieEnvironmentError
from brownie.exceptions import BrownieEnvironmentError, CompilerError
from brownie.network import rpc
from brownie.project.build import DEPLOYMENT_KEYS
from brownie.utils.sql import Cursor
Expand Down Expand Up @@ -555,7 +555,7 @@ def _find_contract(address: Any) -> Any:
from brownie.network.contract import Contract

return Contract(address)
except ValueError:
except (ValueError, CompilerError):
pass


Expand Down
39 changes: 33 additions & 6 deletions brownie/network/transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from hashlib import sha1
from pathlib import Path
from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union
from warnings import warn

import black
import requests
Expand Down Expand Up @@ -657,12 +658,32 @@ def _get_trace(self) -> None:
self._modified_state = False
return

if isinstance(trace[0]["gas"], str):
# handle traces where numeric values are returned as hex (Nethermind)
# different nodes return slightly different formats. its really fun to handle
# geth/nethermind returns unprefixed and with 0-padding for stack and memory
# erigon returns 0x-prefixed and without padding (but their memory values are like geth)
fix_stack = False
for step in trace:
if not step["stack"]:
continue
check = step["stack"][0]
if not isinstance(check, str):
break
if check.startswith("0x"):
fix_stack = True
break

fix_gas = isinstance(trace[0]["gas"], str)

if fix_stack or fix_gas:
for step in trace:
step["gas"] = int(step["gas"], 16)
step["gasCost"] = int.from_bytes(HexBytes(step["gasCost"]), "big", signed=True)
step["pc"] = int(step["pc"], 16)
if fix_stack:
# for stack values, we need 32 bytes (64 chars) without the 0x prefix
step["stack"] = [HexBytes(s).hex()[2:].zfill(64) for s in step["stack"]]
if fix_gas:
# handle traces where numeric values are returned as hex (Nethermind)
step["gas"] = int(step["gas"], 16)
step["gasCost"] = int.from_bytes(HexBytes(step["gasCost"]), "big", signed=True)
step["pc"] = int(step["pc"], 16)

if self.status:
self._confirmed_trace(trace)
Expand All @@ -678,6 +699,9 @@ def _confirmed_trace(self, trace: Sequence) -> None:
if contract:
data = _get_memory(trace[-1], -1)
fn = contract.get_method_object(self.input)
if not fn:
warn(f"Unable to find function on {contract} for input {self.input}")
return
self._return_value = fn.decode_output(data)

def _reverted_trace(self, trace: Sequence) -> None:
Expand Down Expand Up @@ -978,8 +1002,11 @@ def _expand_trace(self) -> None:
)

def _add_internal_xfer(self, from_: str, to: str, value: str) -> None:
if not value.startswith("0x"):
value = f"0x{value}"

self._internal_transfers.append( # type: ignore
{"from": EthAddress(from_), "to": EthAddress(to), "value": Wei(f"0x{value}")}
{"from": EthAddress(from_), "to": EthAddress(to), "value": Wei(value)}
)

def _full_name(self) -> str:
Expand Down