From 699bf6c11032472c303b58c98ec2bca1289510d8 Mon Sep 17 00:00:00 2001 From: kclowes Date: Thu, 26 Jan 2023 11:15:07 -0700 Subject: [PATCH 1/3] Raise new MethodUnavailable error if the JSON-RPC method is not supported by the provider --- tests/core/manager/test_response_formatters.py | 16 ++++++++++++++++ web3/exceptions.py | 8 ++++++++ web3/manager.py | 3 +++ 3 files changed, 27 insertions(+) diff --git a/tests/core/manager/test_response_formatters.py b/tests/core/manager/test_response_formatters.py index 4b372f2349..e10d16e0bc 100644 --- a/tests/core/manager/test_response_formatters.py +++ b/tests/core/manager/test_response_formatters.py @@ -13,6 +13,7 @@ BadResponseFormat, BlockNotFound, ContractLogicError, + MethodUnavailable, TransactionNotFound, ) @@ -32,6 +33,14 @@ "message": "You cannot query logs for more than 10000 blocks at once.", "method": "eth_getLogs", } +METHOD_NOT_FOUND_RESP_FORMAT = { + "jsonrpc": "2.0", + "error": { + "code": -32601, + "message": "the method eth_getTransactionByHash does not exist/is not " + "available", + }, +} def raise_contract_logic_error(response): @@ -105,6 +114,13 @@ def raise_contract_logic_error(response): raise_transaction_not_found, TransactionNotFound, ), + ( + METHOD_NOT_FOUND_RESP_FORMAT, + (), + identity, + identity, + MethodUnavailable, + ), ], ) def test_formatted_response_raises_errors( diff --git a/web3/exceptions.py b/web3/exceptions.py index 8bd5288f86..235b9ee8ea 100644 --- a/web3/exceptions.py +++ b/web3/exceptions.py @@ -287,3 +287,11 @@ class BadResponseFormat(Web3Exception): """ pass + + +class MethodUnavailable(Web3Exception): + """ + Raised when the method is not available on the node + """ + + pass diff --git a/web3/manager.py b/web3/manager.py index ffab72d7d8..9080837379 100644 --- a/web3/manager.py +++ b/web3/manager.py @@ -34,6 +34,7 @@ ) from web3.exceptions import ( BadResponseFormat, + MethodUnavailable, ) from web3.middleware import ( abi_middleware, @@ -188,6 +189,8 @@ def formatted_response( ) -> Any: if "error" in response: apply_error_formatters(error_formatters, response) + if response["error"].get("code") == -32601: + raise MethodUnavailable(response["error"]) raise ValueError(response["error"]) # NULL_RESPONSES includes None, so return False here as the default # so we don't apply the null_result_formatters if there is no 'result' key From 4e471a157d231b655d1e7b1ea296f8c4070d17b3 Mon Sep 17 00:00:00 2001 From: kclowes Date: Thu, 26 Jan 2023 11:47:44 -0700 Subject: [PATCH 2/3] Add eth-tester MethodNotFound response format --- tests/core/manager/test_response_formatters.py | 10 ++++++++++ web3/manager.py | 9 +++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/tests/core/manager/test_response_formatters.py b/tests/core/manager/test_response_formatters.py index e10d16e0bc..505a146386 100644 --- a/tests/core/manager/test_response_formatters.py +++ b/tests/core/manager/test_response_formatters.py @@ -41,6 +41,9 @@ "available", }, } +ETH_TESTER_METHOD_NOT_FOUND_RESP_FORMAT = { + "error": "the method eth_getTransactionByHash does not exist/is not available", +} def raise_contract_logic_error(response): @@ -121,6 +124,13 @@ def raise_contract_logic_error(response): identity, MethodUnavailable, ), + ( + ETH_TESTER_METHOD_NOT_FOUND_RESP_FORMAT, + (), + identity, + identity, + ValueError, + ), ], ) def test_formatted_response_raises_errors( diff --git a/web3/manager.py b/web3/manager.py index 9080837379..fed4c1c786 100644 --- a/web3/manager.py +++ b/web3/manager.py @@ -189,8 +189,13 @@ def formatted_response( ) -> Any: if "error" in response: apply_error_formatters(error_formatters, response) - if response["error"].get("code") == -32601: - raise MethodUnavailable(response["error"]) + + # guard against eth-tester case - eth-tester returns a string + # with no code, so can't parse what the error is. + if isinstance(response["error"], dict): + resp_code = response["error"].get("code") + if resp_code == -32601: + raise MethodUnavailable(response["error"]) raise ValueError(response["error"]) # NULL_RESPONSES includes None, so return False here as the default # so we don't apply the null_result_formatters if there is no 'result' key From 0509be01ee48fd99348293265186d80137b56943 Mon Sep 17 00:00:00 2001 From: kclowes Date: Thu, 26 Jan 2023 15:41:27 -0700 Subject: [PATCH 3/3] Add newsfragment, add to migration guide --- docs/v6_migration.rst | 3 +++ newsfragments/2796.breaking.rst | 1 + 2 files changed, 4 insertions(+) create mode 100644 newsfragments/2796.breaking.rst diff --git a/docs/v6_migration.rst b/docs/v6_migration.rst index 74da70889e..97d80f7aba 100644 --- a/docs/v6_migration.rst +++ b/docs/v6_migration.rst @@ -76,6 +76,9 @@ Other Misc Changes - ``InfuraKeyNotFound`` exception has been changed to ``InfuraProjectIdNotFound`` - ``SolidityError`` has been removed in favor of ``ContractLogicError`` +- When a method is unavailable from a node provider (i.e. a response error + code of -32601 is returned), a ``MethodUnavailable`` error is + now raised instead of ``ValueError`` Removals ~~~~~~~~ diff --git a/newsfragments/2796.breaking.rst b/newsfragments/2796.breaking.rst new file mode 100644 index 0000000000..3494b00189 --- /dev/null +++ b/newsfragments/2796.breaking.rst @@ -0,0 +1 @@ +When a method is not supported by a node provider, raise a MethodUnavailable error instead of the generic ValueError.