From 6198140ef9d5b087f0d79cc18505678ebb46fd03 Mon Sep 17 00:00:00 2001 From: Ben Hauser Date: Sat, 21 Nov 2020 03:43:06 +0400 Subject: [PATCH 1/4] feat: supports_traces property method --- brownie/network/web3.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/brownie/network/web3.py b/brownie/network/web3.py index a76a76ac1..0a2d1a14c 100644 --- a/brownie/network/web3.py +++ b/brownie/network/web3.py @@ -33,6 +33,7 @@ def __init__(self) -> None: self._genesis_hash: Optional[str] = None self._chain_uri: Optional[str] = None self._custom_middleware: Set = set() + self._supports_traces = None def connect(self, uri: str, timeout: int = 30) -> None: """Connects to a provider""" @@ -89,12 +90,27 @@ def disconnect(self) -> None: self.provider = None self._genesis_hash = None self._chain_uri = None + self._supports_traces = None def isConnected(self) -> bool: if not self.provider: return False return super().isConnected() + @property + def supports_traces(self) -> bool: + if not self.provider: + return False + + # Send a malformed request to `debug_traceTransaction`. If the error code + # returned is -32601 "endpoint does not exist/is not available" we know + # traces are not possible. Any other error code means the endpoint is open. + if self._supports_traces is None: + response = self.provider.make_request("debug_traceTransaction", []) + self._supports_traces = bool(response["error"]["code"] != -32601) + + return self._supports_traces + @property def _mainnet(self) -> _Web3: # a web3 instance connected to the mainnet From eb1c763a60ec362a62cb2980e829af51df72d6da Mon Sep 17 00:00:00 2001 From: Ben Hauser Date: Sat, 21 Nov 2020 18:34:36 +0400 Subject: [PATCH 2/4] fix: do not attempt to query traces when they are not supported --- brownie/network/transaction.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/brownie/network/transaction.py b/brownie/network/transaction.py index 0d48a2ed9..e73df5e6a 100644 --- a/brownie/network/transaction.py +++ b/brownie/network/transaction.py @@ -38,6 +38,11 @@ def trace_property(fn: Callable) -> Any: def wrapper(self: "TransactionReceipt") -> Any: if self.status < 0: return None + if not web3.supports_traces: + raise RPCRequestError( + f"`TransactionReceipt.{fn.__name__}` requires the `debug_traceTransaction` RPC" + " endpoint, but the node client does not support it or has not made it available." + ) if self._trace_exc is not None: raise self._trace_exc return fn(self) @@ -323,6 +328,10 @@ def wait(self, required_confs: int) -> None: def _raise_if_reverted(self, exc: Any) -> None: if self.status or CONFIG.mode == "console": return + if not web3.supports_traces: + # if traces are not available, do not attempt to determine the revert reason + raise exc + if self._revert_msg is None: # no revert message and unable to check dev string - have to get trace self._expand_trace() @@ -479,7 +488,8 @@ def _set_from_receipt(self, receipt: Dict) -> None: def _confirm_output(self) -> str: status = "" if not self.status: - status = f"({color('bright red')}{self.revert_msg or 'reverted'}{color}) " + revert_msg = self.revert_msg if web3.supports_traces else None + status = f"({color('bright red')}{revert_msg or 'reverted'}{color}) " result = ( f" {self._full_name()} confirmed {status}- " f"Block: {color('bright blue')}{self.block_number}{color} " From cba09e764552b5326076f3e92250e833128b0194 Mon Sep 17 00:00:00 2001 From: Ben Hauser Date: Sat, 21 Nov 2020 19:26:36 +0400 Subject: [PATCH 3/4] test: supports_trace --- tests/network/test_web3.py | 23 +++++++++++++++++++++++ tests/network/transaction/test_trace.py | 14 ++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/tests/network/test_web3.py b/tests/network/test_web3.py index b4261c94f..84f325c2e 100755 --- a/tests/network/test_web3.py +++ b/tests/network/test_web3.py @@ -100,3 +100,26 @@ def test_rinkeby(web3, network): # this should work because we automatically add the POA middleware web3.eth.getBlock("latest") + + +def test_supports_traces_development(web3, devnetwork): + # development should return true + assert web3.supports_traces + + +def test_supports_traces_not_connected(web3, network): + # should return false when disconnected + assert not web3.supports_traces + + +def test_supports_traces_infura(web3, network): + # ropsten should return false (infura, geth) + network.connect("ropsten") + assert not web3.supports_traces + + +def test_supports_traces_kovan(web3, network): + # kovan should return false (infura, parity) + network.connect("kovan") + + assert not web3.supports_traces diff --git a/tests/network/transaction/test_trace.py b/tests/network/transaction/test_trace.py index 945bf5c6b..e4f1a3c75 100755 --- a/tests/network/transaction/test_trace.py +++ b/tests/network/transaction/test_trace.py @@ -6,6 +6,7 @@ import pytest from brownie import Contract +from brownie.exceptions import RPCRequestError from brownie.network.transaction import TransactionReceipt from brownie.project import build @@ -248,3 +249,16 @@ def test_contractabi(ExternalCallTester, accounts, tester, ext_tester): del ExternalCallTester[0] ext_tester = Contract.from_abi("ExternalTesterABI", ext_tester.address, ext_tester.abi) tx.call_trace() + + +def test_traces_not_supported(network, chain): + network.connect("ropsten") + + tx = chain.get_transaction("0xfd9f98a245d3cff68dd67546fa7e89009a291101f045f81eb9afd14abcbdc6aa") + + # the confirmation output should work even without traces + tx._confirm_output() + + # querying the revert message should raise + with pytest.raises(RPCRequestError): + tx.revert_msg From b9f146f48837501516a000d30690e7e4704e194e Mon Sep 17 00:00:00 2001 From: Ben Hauser Date: Sat, 21 Nov 2020 19:32:01 +0400 Subject: [PATCH 4/4] doc: web3.supports_traces --- docs/api-network.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/api-network.rst b/docs/api-network.rst index 5d82d8a41..693b8a516 100644 --- a/docs/api-network.rst +++ b/docs/api-network.rst @@ -2568,6 +2568,16 @@ Web3 Attributes >>> web3.genesis_hash '41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d' +.. py:classmethod:: Web3.supports_traces + + Boolean indicating if the currently connected node client supports the `debug_traceTransaction `_ RPC endpoint. + + .. code-block:: python + + >>> web3.supports_traces + True + + Web3 Internals **************