web3 6.20.3__py3-none-any.whl → 7.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ens/__init__.py +13 -2
- ens/_normalization.py +2 -17
- ens/async_ens.py +33 -21
- ens/base_ens.py +3 -1
- ens/ens.py +16 -11
- ens/exceptions.py +16 -29
- ens/specs/nf.json +1 -1
- ens/specs/normalization_spec.json +1 -1
- ens/utils.py +52 -63
- web3/__init__.py +20 -24
- web3/_utils/abi.py +115 -271
- web3/_utils/async_transactions.py +7 -4
- web3/_utils/batching.py +217 -0
- web3/_utils/blocks.py +6 -2
- web3/_utils/caching.py +128 -5
- web3/_utils/compat/__init__.py +2 -3
- web3/_utils/contract_sources/compile_contracts.py +1 -1
- web3/_utils/contract_sources/contract_data/arrays_contract.py +3 -3
- web3/_utils/contract_sources/contract_data/bytes_contracts.py +5 -5
- web3/_utils/contract_sources/contract_data/constructor_contracts.py +7 -7
- web3/_utils/contract_sources/contract_data/contract_caller_tester.py +3 -3
- web3/_utils/contract_sources/contract_data/emitter_contract.py +3 -3
- web3/_utils/contract_sources/contract_data/event_contracts.py +5 -5
- web3/_utils/contract_sources/contract_data/extended_resolver.py +3 -3
- web3/_utils/contract_sources/contract_data/fallback_function_contract.py +3 -3
- web3/_utils/contract_sources/contract_data/function_name_tester_contract.py +3 -3
- web3/_utils/contract_sources/contract_data/math_contract.py +3 -3
- web3/_utils/contract_sources/contract_data/offchain_lookup.py +3 -3
- web3/_utils/contract_sources/contract_data/offchain_resolver.py +3 -3
- web3/_utils/contract_sources/contract_data/panic_errors_contract.py +3 -3
- web3/_utils/contract_sources/contract_data/payable_tester.py +3 -3
- web3/_utils/contract_sources/contract_data/receive_function_contracts.py +5 -5
- web3/_utils/contract_sources/contract_data/reflector_contracts.py +3 -3
- web3/_utils/contract_sources/contract_data/revert_contract.py +3 -3
- web3/_utils/contract_sources/contract_data/simple_resolver.py +3 -3
- web3/_utils/contract_sources/contract_data/storage_contract.py +3 -3
- web3/_utils/contract_sources/contract_data/string_contract.py +3 -3
- web3/_utils/contract_sources/contract_data/tuple_contracts.py +5 -5
- web3/_utils/contracts.py +130 -236
- web3/_utils/datatypes.py +5 -1
- web3/_utils/decorators.py +13 -23
- web3/_utils/empty.py +1 -1
- web3/_utils/encoding.py +16 -12
- web3/_utils/ens.py +2 -1
- web3/_utils/error_formatters_utils.py +5 -3
- web3/_utils/events.py +66 -69
- web3/_utils/fee_utils.py +1 -3
- web3/_utils/filters.py +24 -22
- web3/_utils/formatters.py +2 -2
- web3/_utils/http.py +5 -3
- web3/_utils/http_session_manager.py +303 -0
- web3/_utils/math.py +14 -15
- web3/_utils/method_formatters.py +34 -36
- web3/_utils/module.py +2 -1
- web3/_utils/module_testing/__init__.py +0 -3
- web3/_utils/module_testing/eth_module.py +695 -643
- web3/_utils/module_testing/module_testing_utils.py +61 -34
- web3/_utils/module_testing/persistent_connection_provider.py +56 -25
- web3/_utils/module_testing/utils.py +258 -0
- web3/_utils/module_testing/web3_module.py +438 -17
- web3/_utils/normalizers.py +13 -11
- web3/_utils/rpc_abi.py +5 -32
- web3/_utils/threads.py +8 -7
- web3/_utils/transactions.py +14 -12
- web3/_utils/type_conversion.py +5 -1
- web3/_utils/validation.py +17 -17
- web3/auto/gethdev.py +7 -2
- web3/beacon/__init__.py +6 -1
- web3/beacon/async_beacon.py +9 -5
- web3/beacon/{main.py → beacon.py} +7 -5
- web3/contract/__init__.py +7 -0
- web3/contract/async_contract.py +47 -46
- web3/contract/base_contract.py +183 -158
- web3/contract/contract.py +49 -43
- web3/contract/utils.py +203 -59
- web3/datastructures.py +79 -31
- web3/eth/__init__.py +7 -0
- web3/eth/async_eth.py +39 -51
- web3/eth/base_eth.py +17 -10
- web3/eth/eth.py +30 -68
- web3/exceptions.py +108 -82
- web3/gas_strategies/time_based.py +8 -6
- web3/geth.py +1 -254
- web3/main.py +75 -122
- web3/manager.py +316 -146
- web3/method.py +38 -31
- web3/middleware/__init__.py +67 -89
- web3/middleware/attrdict.py +36 -49
- web3/middleware/base.py +174 -0
- web3/middleware/buffered_gas_estimate.py +20 -21
- web3/middleware/filter.py +157 -117
- web3/middleware/formatting.py +124 -108
- web3/middleware/gas_price_strategy.py +20 -32
- web3/middleware/names.py +29 -26
- web3/middleware/proof_of_authority.py +68 -0
- web3/middleware/pythonic.py +2 -2
- web3/middleware/signing.py +74 -89
- web3/middleware/stalecheck.py +52 -79
- web3/middleware/validation.py +5 -13
- web3/module.py +54 -10
- web3/providers/__init__.py +10 -6
- web3/providers/async_base.py +117 -39
- web3/providers/auto.py +3 -3
- web3/providers/base.py +89 -33
- web3/providers/eth_tester/__init__.py +5 -0
- web3/providers/eth_tester/defaults.py +1 -64
- web3/providers/eth_tester/main.py +99 -31
- web3/providers/eth_tester/middleware.py +45 -73
- web3/providers/ipc.py +42 -46
- web3/providers/{websocket/websocket.py → legacy_websocket.py} +32 -7
- web3/providers/persistent/__init__.py +22 -0
- web3/providers/persistent/async_ipc.py +153 -0
- web3/providers/{persistent.py → persistent/persistent.py} +106 -25
- web3/providers/persistent/persistent_connection.py +84 -0
- web3/providers/{websocket → persistent}/request_processor.py +94 -32
- web3/providers/persistent/utils.py +43 -0
- web3/providers/{websocket/websocket_v2.py → persistent/websocket.py} +29 -28
- web3/providers/rpc/__init__.py +11 -0
- web3/providers/rpc/async_rpc.py +171 -0
- web3/providers/rpc/rpc.py +179 -0
- web3/providers/rpc/utils.py +92 -0
- web3/testing.py +4 -4
- web3/tools/benchmark/main.py +22 -22
- web3/tools/benchmark/node.py +2 -8
- web3/tools/benchmark/reporting.py +2 -2
- web3/tools/benchmark/utils.py +1 -1
- web3/tracing.py +9 -5
- web3/types.py +30 -107
- web3/utils/__init__.py +58 -5
- web3/utils/abi.py +575 -10
- web3/utils/async_exception_handling.py +19 -7
- web3/utils/caching.py +32 -13
- web3/utils/exception_handling.py +7 -5
- {web3-6.20.3.dist-info → web3-7.0.0.dist-info}/LICENSE +1 -1
- web3-7.0.0.dist-info/METADATA +112 -0
- web3-7.0.0.dist-info/RECORD +167 -0
- {web3-6.20.3.dist-info → web3-7.0.0.dist-info}/top_level.txt +0 -1
- ethpm/__init__.py +0 -20
- ethpm/_utils/__init__.py +0 -0
- ethpm/_utils/backend.py +0 -93
- ethpm/_utils/cache.py +0 -44
- ethpm/_utils/chains.py +0 -119
- ethpm/_utils/contract.py +0 -35
- ethpm/_utils/deployments.py +0 -145
- ethpm/_utils/ipfs.py +0 -116
- ethpm/_utils/protobuf/__init__.py +0 -0
- ethpm/_utils/protobuf/ipfs_file_pb2.py +0 -33
- ethpm/_utils/registry.py +0 -29
- ethpm/assets/__init__.py +0 -0
- ethpm/assets/ens/v3.json +0 -1
- ethpm/assets/escrow/with_bytecode_v3.json +0 -1
- ethpm/assets/ipfs_file.proto +0 -32
- ethpm/assets/owned/output_v3.json +0 -1
- ethpm/assets/owned/with_contract_type_v3.json +0 -1
- ethpm/assets/registry/contracts/Authority.sol +0 -156
- ethpm/assets/registry/contracts/IndexedOrderedSetLib.sol +0 -106
- ethpm/assets/registry/contracts/PackageDB.sol +0 -225
- ethpm/assets/registry/contracts/PackageRegistry.sol +0 -361
- ethpm/assets/registry/contracts/PackageRegistryInterface.sol +0 -97
- ethpm/assets/registry/contracts/ReleaseDB.sol +0 -309
- ethpm/assets/registry/contracts/ReleaseValidator.sol +0 -152
- ethpm/assets/registry/solc_input.json +0 -1
- ethpm/assets/registry/solc_output.json +0 -1
- ethpm/assets/registry/v3.json +0 -1
- ethpm/assets/safe-math-lib/v3-strict-no-deployments.json +0 -1
- ethpm/assets/simple-registry/contracts/Ownable.sol +0 -63
- ethpm/assets/simple-registry/contracts/PackageRegistry.sol +0 -373
- ethpm/assets/simple-registry/contracts/PackageRegistryInterface.sol +0 -96
- ethpm/assets/simple-registry/solc_input.json +0 -33
- ethpm/assets/simple-registry/solc_output.json +0 -1
- ethpm/assets/simple-registry/v3.json +0 -1
- ethpm/assets/standard-token/output_v3.json +0 -1
- ethpm/assets/standard-token/with_bytecode_v3.json +0 -1
- ethpm/assets/vyper_registry/0.1.0.json +0 -1
- ethpm/assets/vyper_registry/registry.vy +0 -216
- ethpm/assets/vyper_registry/registry_with_delete.vy +0 -244
- ethpm/backends/__init__.py +0 -0
- ethpm/backends/base.py +0 -43
- ethpm/backends/http.py +0 -108
- ethpm/backends/ipfs.py +0 -219
- ethpm/backends/registry.py +0 -154
- ethpm/constants.py +0 -17
- ethpm/contract.py +0 -187
- ethpm/dependencies.py +0 -58
- ethpm/deployments.py +0 -80
- ethpm/ethpm-spec/examples/escrow/1.0.0-pretty.json +0 -146
- ethpm/ethpm-spec/examples/escrow/1.0.0.json +0 -1
- ethpm/ethpm-spec/examples/escrow/contracts/Escrow.sol +0 -32
- ethpm/ethpm-spec/examples/escrow/contracts/SafeSendLib.sol +0 -20
- ethpm/ethpm-spec/examples/escrow/v3-pretty.json +0 -171
- ethpm/ethpm-spec/examples/escrow/v3.json +0 -1
- ethpm/ethpm-spec/examples/owned/1.0.0-pretty.json +0 -21
- ethpm/ethpm-spec/examples/owned/1.0.0.json +0 -1
- ethpm/ethpm-spec/examples/owned/contracts/Owned.sol +0 -12
- ethpm/ethpm-spec/examples/owned/v3-pretty.json +0 -27
- ethpm/ethpm-spec/examples/owned/v3.json +0 -1
- ethpm/ethpm-spec/examples/piper-coin/1.0.0-pretty.json +0 -31
- ethpm/ethpm-spec/examples/piper-coin/1.0.0.json +0 -1
- ethpm/ethpm-spec/examples/piper-coin/v3-pretty.json +0 -21
- ethpm/ethpm-spec/examples/piper-coin/v3.json +0 -1
- ethpm/ethpm-spec/examples/safe-math-lib/1.0.0-pretty.json +0 -85
- ethpm/ethpm-spec/examples/safe-math-lib/1.0.0.json +0 -1
- ethpm/ethpm-spec/examples/safe-math-lib/contracts/SafeMathLib.sol +0 -24
- ethpm/ethpm-spec/examples/safe-math-lib/v3-pretty.json +0 -117
- ethpm/ethpm-spec/examples/safe-math-lib/v3.json +0 -1
- ethpm/ethpm-spec/examples/standard-token/1.0.0-pretty.json +0 -55
- ethpm/ethpm-spec/examples/standard-token/1.0.0.json +0 -1
- ethpm/ethpm-spec/examples/standard-token/contracts/AbstractToken.sol +0 -20
- ethpm/ethpm-spec/examples/standard-token/contracts/StandardToken.sol +0 -84
- ethpm/ethpm-spec/examples/standard-token/v3-pretty.json +0 -460
- ethpm/ethpm-spec/examples/standard-token/v3.json +0 -1
- ethpm/ethpm-spec/examples/transferable/1.0.0-pretty.json +0 -21
- ethpm/ethpm-spec/examples/transferable/1.0.0.json +0 -1
- ethpm/ethpm-spec/examples/transferable/contracts/Transferable.sol +0 -14
- ethpm/ethpm-spec/examples/transferable/v3-pretty.json +0 -27
- ethpm/ethpm-spec/examples/transferable/v3.json +0 -1
- ethpm/ethpm-spec/examples/wallet/1.0.0-pretty.json +0 -120
- ethpm/ethpm-spec/examples/wallet/1.0.0.json +0 -1
- ethpm/ethpm-spec/examples/wallet/contracts/Wallet.sol +0 -41
- ethpm/ethpm-spec/examples/wallet/v3-pretty.json +0 -181
- ethpm/ethpm-spec/examples/wallet/v3.json +0 -1
- ethpm/ethpm-spec/examples/wallet-with-send/1.0.0-pretty.json +0 -135
- ethpm/ethpm-spec/examples/wallet-with-send/1.0.0.json +0 -1
- ethpm/ethpm-spec/examples/wallet-with-send/contracts/WalletWithSend.sol +0 -18
- ethpm/ethpm-spec/examples/wallet-with-send/v3-pretty.json +0 -207
- ethpm/ethpm-spec/examples/wallet-with-send/v3.json +0 -1
- ethpm/ethpm-spec/spec/package.spec.json +0 -379
- ethpm/ethpm-spec/spec/v3.spec.json +0 -483
- ethpm/exceptions.py +0 -68
- ethpm/package.py +0 -438
- ethpm/tools/__init__.py +0 -4
- ethpm/tools/builder.py +0 -930
- ethpm/tools/checker.py +0 -312
- ethpm/tools/get_manifest.py +0 -19
- ethpm/uri.py +0 -141
- ethpm/validation/__init__.py +0 -0
- ethpm/validation/manifest.py +0 -146
- ethpm/validation/misc.py +0 -39
- ethpm/validation/package.py +0 -80
- ethpm/validation/uri.py +0 -163
- web3/_utils/contract_sources/contract_data/address_reflector.py +0 -29
- web3/_utils/miner.py +0 -88
- web3/_utils/module_testing/go_ethereum_personal_module.py +0 -323
- web3/_utils/request.py +0 -265
- web3/middleware/abi.py +0 -11
- web3/middleware/async_cache.py +0 -99
- web3/middleware/cache.py +0 -374
- web3/middleware/exception_handling.py +0 -49
- web3/middleware/exception_retry_request.py +0 -188
- web3/middleware/fixture.py +0 -190
- web3/middleware/geth_poa.py +0 -81
- web3/middleware/normalize_request_parameters.py +0 -11
- web3/middleware/simulate_unmined_transaction.py +0 -43
- web3/pm.py +0 -602
- web3/providers/async_rpc.py +0 -99
- web3/providers/rpc.py +0 -98
- web3/providers/websocket/__init__.py +0 -11
- web3/providers/websocket/websocket_connection.py +0 -42
- web3/tools/__init__.py +0 -4
- web3/tools/pytest_ethereum/__init__.py +0 -0
- web3/tools/pytest_ethereum/_utils.py +0 -145
- web3/tools/pytest_ethereum/deployer.py +0 -48
- web3/tools/pytest_ethereum/exceptions.py +0 -22
- web3/tools/pytest_ethereum/linker.py +0 -128
- web3/tools/pytest_ethereum/plugins.py +0 -33
- web3-6.20.3.dist-info/METADATA +0 -104
- web3-6.20.3.dist-info/RECORD +0 -283
- web3-6.20.3.dist-info/entry_points.txt +0 -2
- /web3/_utils/{function_identifiers.py → abi_element_identifiers.py} +0 -0
- {web3-6.20.3.dist-info → web3-7.0.0.dist-info}/WHEEL +0 -0
web3/manager.py
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import asyncio
|
|
1
2
|
import logging
|
|
2
3
|
from typing import (
|
|
3
4
|
TYPE_CHECKING,
|
|
4
5
|
Any,
|
|
5
6
|
AsyncGenerator,
|
|
6
7
|
Callable,
|
|
8
|
+
Coroutine,
|
|
7
9
|
List,
|
|
8
10
|
Optional,
|
|
9
11
|
Sequence,
|
|
@@ -18,10 +20,10 @@ from eth_utils.toolz import (
|
|
|
18
20
|
from hexbytes import (
|
|
19
21
|
HexBytes,
|
|
20
22
|
)
|
|
21
|
-
from websockets.exceptions import (
|
|
22
|
-
ConnectionClosedOK,
|
|
23
|
-
)
|
|
24
23
|
|
|
24
|
+
from web3._utils.batching import (
|
|
25
|
+
RequestBatcher,
|
|
26
|
+
)
|
|
25
27
|
from web3._utils.caching import (
|
|
26
28
|
generate_cache_key,
|
|
27
29
|
)
|
|
@@ -35,33 +37,37 @@ from web3.exceptions import (
|
|
|
35
37
|
BadResponseFormat,
|
|
36
38
|
MethodUnavailable,
|
|
37
39
|
ProviderConnectionError,
|
|
40
|
+
RequestTimedOut,
|
|
38
41
|
TaskNotRunning,
|
|
42
|
+
Web3RPCError,
|
|
43
|
+
Web3TypeError,
|
|
44
|
+
)
|
|
45
|
+
from web3.method import (
|
|
46
|
+
Method,
|
|
39
47
|
)
|
|
40
48
|
from web3.middleware import (
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
name_to_address_middleware,
|
|
51
|
-
validation_middleware,
|
|
49
|
+
AttributeDictMiddleware,
|
|
50
|
+
BufferedGasEstimateMiddleware,
|
|
51
|
+
ENSNameToAddressMiddleware,
|
|
52
|
+
GasPriceStrategyMiddleware,
|
|
53
|
+
ValidationMiddleware,
|
|
54
|
+
)
|
|
55
|
+
from web3.middleware.base import (
|
|
56
|
+
Middleware,
|
|
57
|
+
MiddlewareOnion,
|
|
52
58
|
)
|
|
53
59
|
from web3.module import (
|
|
54
60
|
apply_result_formatters,
|
|
55
61
|
)
|
|
56
62
|
from web3.providers import (
|
|
57
63
|
AutoProvider,
|
|
64
|
+
JSONBaseProvider,
|
|
58
65
|
PersistentConnectionProvider,
|
|
59
66
|
)
|
|
67
|
+
from web3.providers.async_base import (
|
|
68
|
+
AsyncJSONBaseProvider,
|
|
69
|
+
)
|
|
60
70
|
from web3.types import (
|
|
61
|
-
AsyncMiddleware,
|
|
62
|
-
AsyncMiddlewareOnion,
|
|
63
|
-
Middleware,
|
|
64
|
-
MiddlewareOnion,
|
|
65
71
|
RPCEndpoint,
|
|
66
72
|
RPCResponse,
|
|
67
73
|
)
|
|
@@ -71,16 +77,26 @@ if TYPE_CHECKING:
|
|
|
71
77
|
AsyncWeb3,
|
|
72
78
|
Web3,
|
|
73
79
|
)
|
|
80
|
+
from web3.middleware.base import ( # noqa: F401
|
|
81
|
+
Web3Middleware,
|
|
82
|
+
)
|
|
74
83
|
from web3.providers import ( # noqa: F401
|
|
75
84
|
AsyncBaseProvider,
|
|
76
85
|
BaseProvider,
|
|
77
86
|
)
|
|
78
|
-
from web3.providers.
|
|
87
|
+
from web3.providers.persistent.request_processor import ( # noqa: F401
|
|
79
88
|
RequestProcessor,
|
|
80
89
|
)
|
|
81
90
|
|
|
82
91
|
|
|
83
92
|
NULL_RESPONSES = [None, HexBytes("0x"), "0x"]
|
|
93
|
+
KNOWN_REQUEST_TIMEOUT_MESSAGING = {
|
|
94
|
+
# Note: It's important to be very explicit here and not too broad. We don't want
|
|
95
|
+
# to accidentally catch a message that is not for a request timeout. In the worst
|
|
96
|
+
# case, we raise something more generic like `Web3RPCError`. JSON-RPC unfortunately
|
|
97
|
+
# has not standardized error codes for request timeouts.
|
|
98
|
+
"request timed out", # go-ethereum
|
|
99
|
+
}
|
|
84
100
|
METHOD_NOT_FOUND = -32601
|
|
85
101
|
|
|
86
102
|
|
|
@@ -89,6 +105,7 @@ def _raise_bad_response_format(response: RPCResponse, error: str = "") -> None:
|
|
|
89
105
|
raw_response = f"The raw response is: {response}"
|
|
90
106
|
|
|
91
107
|
if error is not None and error != "":
|
|
108
|
+
error = error[:-1] if error.endswith(".") else error
|
|
92
109
|
message = f"{message} {error}. {raw_response}"
|
|
93
110
|
else:
|
|
94
111
|
message = f"{message} {raw_response}"
|
|
@@ -119,22 +136,135 @@ def apply_null_result_formatters(
|
|
|
119
136
|
return response
|
|
120
137
|
|
|
121
138
|
|
|
139
|
+
def _validate_subscription_fields(response: RPCResponse) -> None:
|
|
140
|
+
params = response["params"]
|
|
141
|
+
subscription = params["subscription"]
|
|
142
|
+
if not isinstance(subscription, str) and not len(subscription) == 34:
|
|
143
|
+
_raise_bad_response_format(
|
|
144
|
+
response, "eth_subscription 'params' must include a 'subscription' field."
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def _validate_response(
|
|
149
|
+
response: RPCResponse,
|
|
150
|
+
error_formatters: Optional[Callable[..., Any]],
|
|
151
|
+
is_subscription_response: bool = False,
|
|
152
|
+
logger: Optional[logging.Logger] = None,
|
|
153
|
+
) -> None:
|
|
154
|
+
if "jsonrpc" not in response or response["jsonrpc"] != "2.0":
|
|
155
|
+
_raise_bad_response_format(
|
|
156
|
+
response, 'The "jsonrpc" field must be present with a value of "2.0".'
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
response_id = response.get("id")
|
|
160
|
+
if "id" in response:
|
|
161
|
+
int_error_msg = (
|
|
162
|
+
'"id" must be an integer or a string representation of an integer.'
|
|
163
|
+
)
|
|
164
|
+
if response_id is None and "error" in response:
|
|
165
|
+
# errors can sometimes have null `id`, according to the JSON-RPC spec
|
|
166
|
+
pass
|
|
167
|
+
elif not isinstance(response_id, (str, int)):
|
|
168
|
+
_raise_bad_response_format(response, int_error_msg)
|
|
169
|
+
elif isinstance(response_id, str):
|
|
170
|
+
try:
|
|
171
|
+
int(response_id)
|
|
172
|
+
except ValueError:
|
|
173
|
+
_raise_bad_response_format(response, int_error_msg)
|
|
174
|
+
elif is_subscription_response:
|
|
175
|
+
# if `id` is not present, this must be a subscription response
|
|
176
|
+
_validate_subscription_fields(response)
|
|
177
|
+
else:
|
|
178
|
+
_raise_bad_response_format(
|
|
179
|
+
response,
|
|
180
|
+
'Response must include an "id" field or be formatted as an '
|
|
181
|
+
"`eth_subscription` response.",
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
if all(key in response for key in {"error", "result"}):
|
|
185
|
+
_raise_bad_response_format(
|
|
186
|
+
response, 'Response cannot include both "error" and "result".'
|
|
187
|
+
)
|
|
188
|
+
elif (
|
|
189
|
+
not any(key in response for key in {"error", "result"})
|
|
190
|
+
and not is_subscription_response
|
|
191
|
+
):
|
|
192
|
+
_raise_bad_response_format(
|
|
193
|
+
response, 'Response must include either "error" or "result".'
|
|
194
|
+
)
|
|
195
|
+
elif "error" in response:
|
|
196
|
+
web3_rpc_error: Optional[Web3RPCError] = None
|
|
197
|
+
error = response["error"]
|
|
198
|
+
|
|
199
|
+
# raise the error when the value is a string
|
|
200
|
+
if error is None or not isinstance(error, dict):
|
|
201
|
+
_raise_bad_response_format(
|
|
202
|
+
response,
|
|
203
|
+
'response["error"] must be a valid object as defined by the '
|
|
204
|
+
"JSON-RPC 2.0 specification.",
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
# errors must include a message
|
|
208
|
+
error_message = error.get("message")
|
|
209
|
+
if not isinstance(error_message, str):
|
|
210
|
+
_raise_bad_response_format(
|
|
211
|
+
response, 'error["message"] is required and must be a string value.'
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
# errors must include an integer code
|
|
215
|
+
code = error.get("code")
|
|
216
|
+
if not isinstance(code, int):
|
|
217
|
+
_raise_bad_response_format(
|
|
218
|
+
response, 'error["code"] is required and must be an integer value.'
|
|
219
|
+
)
|
|
220
|
+
elif code == METHOD_NOT_FOUND:
|
|
221
|
+
web3_rpc_error = MethodUnavailable(
|
|
222
|
+
repr(error),
|
|
223
|
+
rpc_response=response,
|
|
224
|
+
user_message=(
|
|
225
|
+
"This method is not available. Check your node provider or your "
|
|
226
|
+
"client's API docs to see what methods are supported and / or "
|
|
227
|
+
"currently enabled."
|
|
228
|
+
),
|
|
229
|
+
)
|
|
230
|
+
elif any(
|
|
231
|
+
# parse specific timeout messages
|
|
232
|
+
timeout_str in error_message.lower()
|
|
233
|
+
for timeout_str in KNOWN_REQUEST_TIMEOUT_MESSAGING
|
|
234
|
+
):
|
|
235
|
+
web3_rpc_error = RequestTimedOut(
|
|
236
|
+
repr(error),
|
|
237
|
+
rpc_response=response,
|
|
238
|
+
user_message=(
|
|
239
|
+
"The request timed out. Check the connection to your node and "
|
|
240
|
+
"try again."
|
|
241
|
+
),
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
if web3_rpc_error is None:
|
|
245
|
+
# if no condition was met above, raise a more generic `Web3RPCError`
|
|
246
|
+
web3_rpc_error = Web3RPCError(repr(error), rpc_response=response)
|
|
247
|
+
|
|
248
|
+
response = apply_error_formatters(error_formatters, response)
|
|
249
|
+
|
|
250
|
+
logger.error(web3_rpc_error.user_message)
|
|
251
|
+
logger.debug(f"RPC error response: {response}")
|
|
252
|
+
raise web3_rpc_error
|
|
253
|
+
|
|
254
|
+
elif "result" not in response and not is_subscription_response:
|
|
255
|
+
_raise_bad_response_format(response)
|
|
256
|
+
|
|
257
|
+
|
|
122
258
|
class RequestManager:
|
|
123
|
-
logger = logging.getLogger("web3.RequestManager")
|
|
259
|
+
logger = logging.getLogger("web3.manager.RequestManager")
|
|
124
260
|
|
|
125
|
-
middleware_onion: Union[
|
|
126
|
-
MiddlewareOnion, AsyncMiddlewareOnion, NamedElementOnion[None, None]
|
|
127
|
-
]
|
|
261
|
+
middleware_onion: Union["MiddlewareOnion", NamedElementOnion[None, None]]
|
|
128
262
|
|
|
129
263
|
def __init__(
|
|
130
264
|
self,
|
|
131
265
|
w3: Union["AsyncWeb3", "Web3"],
|
|
132
266
|
provider: Optional[Union["BaseProvider", "AsyncBaseProvider"]] = None,
|
|
133
|
-
|
|
134
|
-
Union[
|
|
135
|
-
Sequence[Tuple[Middleware, str]], Sequence[Tuple[AsyncMiddleware, str]]
|
|
136
|
-
]
|
|
137
|
-
] = None,
|
|
267
|
+
middleware: Optional[Sequence[Tuple[Middleware, str]]] = None,
|
|
138
268
|
) -> None:
|
|
139
269
|
self.w3 = w3
|
|
140
270
|
|
|
@@ -143,14 +273,10 @@ class RequestManager:
|
|
|
143
273
|
else:
|
|
144
274
|
self.provider = provider
|
|
145
275
|
|
|
146
|
-
if
|
|
147
|
-
|
|
148
|
-
self.async_default_middlewares()
|
|
149
|
-
if self.provider.is_async
|
|
150
|
-
else self.default_middlewares(cast("Web3", w3))
|
|
151
|
-
)
|
|
276
|
+
if middleware is None:
|
|
277
|
+
middleware = self.get_default_middleware()
|
|
152
278
|
|
|
153
|
-
self.middleware_onion = NamedElementOnion(
|
|
279
|
+
self.middleware_onion = NamedElementOnion(middleware)
|
|
154
280
|
|
|
155
281
|
if isinstance(provider, PersistentConnectionProvider):
|
|
156
282
|
# set up the request processor to be able to properly process ordered
|
|
@@ -170,33 +296,17 @@ class RequestManager:
|
|
|
170
296
|
self._provider = provider
|
|
171
297
|
|
|
172
298
|
@staticmethod
|
|
173
|
-
def
|
|
174
|
-
"""
|
|
175
|
-
List the default middlewares for the request manager.
|
|
176
|
-
Leaving w3 unspecified will prevent the middleware from resolving names.
|
|
177
|
-
Documentation should remain in sync with these defaults.
|
|
178
|
-
"""
|
|
179
|
-
return [
|
|
180
|
-
(gas_price_strategy_middleware, "gas_price_strategy"),
|
|
181
|
-
(name_to_address_middleware(w3), "name_to_address"),
|
|
182
|
-
(attrdict_middleware, "attrdict"),
|
|
183
|
-
(validation_middleware, "validation"),
|
|
184
|
-
(abi_middleware, "abi"),
|
|
185
|
-
(buffered_gas_estimate_middleware, "gas_estimate"),
|
|
186
|
-
]
|
|
187
|
-
|
|
188
|
-
@staticmethod
|
|
189
|
-
def async_default_middlewares() -> List[Tuple[AsyncMiddleware, str]]:
|
|
299
|
+
def get_default_middleware() -> List[Tuple[Middleware, str]]:
|
|
190
300
|
"""
|
|
191
|
-
List the default
|
|
301
|
+
List the default middleware for the request manager.
|
|
192
302
|
Documentation should remain in sync with these defaults.
|
|
193
303
|
"""
|
|
194
304
|
return [
|
|
195
|
-
(
|
|
196
|
-
(
|
|
197
|
-
(
|
|
198
|
-
(
|
|
199
|
-
(
|
|
305
|
+
(GasPriceStrategyMiddleware, "gas_price_strategy"),
|
|
306
|
+
(ENSNameToAddressMiddleware, "ens_name_to_address"),
|
|
307
|
+
(AttributeDictMiddleware, "attrdict"),
|
|
308
|
+
(ValidationMiddleware, "validation"),
|
|
309
|
+
(BufferedGasEstimateMiddleware, "gas_estimate"),
|
|
200
310
|
]
|
|
201
311
|
|
|
202
312
|
#
|
|
@@ -207,7 +317,7 @@ class RequestManager:
|
|
|
207
317
|
) -> RPCResponse:
|
|
208
318
|
provider = cast("BaseProvider", self.provider)
|
|
209
319
|
request_func = provider.request_func(
|
|
210
|
-
cast("Web3", self.w3), cast(MiddlewareOnion, self.middleware_onion)
|
|
320
|
+
cast("Web3", self.w3), cast("MiddlewareOnion", self.middleware_onion)
|
|
211
321
|
)
|
|
212
322
|
self.logger.debug(f"Making request. Method: {method}")
|
|
213
323
|
return request_func(method, params)
|
|
@@ -217,8 +327,7 @@ class RequestManager:
|
|
|
217
327
|
) -> RPCResponse:
|
|
218
328
|
provider = cast("AsyncBaseProvider", self.provider)
|
|
219
329
|
request_func = await provider.request_func(
|
|
220
|
-
cast("AsyncWeb3", self.w3),
|
|
221
|
-
cast(AsyncMiddlewareOnion, self.middleware_onion),
|
|
330
|
+
cast("AsyncWeb3", self.w3), cast("MiddlewareOnion", self.middleware_onion)
|
|
222
331
|
)
|
|
223
332
|
self.logger.debug(f"Making request. Method: {method}")
|
|
224
333
|
return await request_func(method, params)
|
|
@@ -232,90 +341,42 @@ class RequestManager:
|
|
|
232
341
|
#
|
|
233
342
|
# See also: https://www.jsonrpc.org/specification
|
|
234
343
|
#
|
|
235
|
-
@staticmethod
|
|
236
344
|
def formatted_response(
|
|
345
|
+
self,
|
|
237
346
|
response: RPCResponse,
|
|
238
347
|
params: Any,
|
|
239
348
|
error_formatters: Optional[Callable[..., Any]] = None,
|
|
240
349
|
null_result_formatters: Optional[Callable[..., Any]] = None,
|
|
241
350
|
) -> Any:
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
)
|
|
247
|
-
|
|
248
|
-
# id is not enforced (as per the spec) but if present, it must be a
|
|
249
|
-
# string or integer
|
|
250
|
-
# TODO: v7 - enforce id per the spec
|
|
251
|
-
if "id" in response:
|
|
252
|
-
response_id = response["id"]
|
|
253
|
-
# id is always None for errors
|
|
254
|
-
if response_id is None and "error" not in response:
|
|
255
|
-
_raise_bad_response_format(
|
|
256
|
-
response, '"id" must be None when an error is present'
|
|
257
|
-
)
|
|
258
|
-
elif not isinstance(response_id, (str, int, type(None))):
|
|
259
|
-
_raise_bad_response_format(response, '"id" must be a string or integer')
|
|
260
|
-
|
|
261
|
-
# Response may not include both "error" and "result"
|
|
262
|
-
if "error" in response and "result" in response:
|
|
263
|
-
_raise_bad_response_format(
|
|
264
|
-
response, 'Response cannot include both "error" and "result"'
|
|
265
|
-
)
|
|
266
|
-
|
|
267
|
-
# Format and validate errors
|
|
268
|
-
elif "error" in response:
|
|
269
|
-
error = response.get("error")
|
|
270
|
-
# Raise the error when the value is a string
|
|
271
|
-
if error is None or isinstance(error, str):
|
|
272
|
-
raise ValueError(error)
|
|
273
|
-
|
|
274
|
-
# Errors must include an integer code
|
|
275
|
-
code = error.get("code")
|
|
276
|
-
if not isinstance(code, int):
|
|
277
|
-
_raise_bad_response_format(response, "error['code'] must be an integer")
|
|
278
|
-
elif code == METHOD_NOT_FOUND:
|
|
279
|
-
raise MethodUnavailable(
|
|
280
|
-
error,
|
|
281
|
-
user_message="Check your node provider's API docs to see what "
|
|
282
|
-
"methods are supported",
|
|
283
|
-
)
|
|
284
|
-
|
|
285
|
-
# Errors must include a message
|
|
286
|
-
if not isinstance(error.get("message"), str):
|
|
287
|
-
_raise_bad_response_format(
|
|
288
|
-
response, "error['message'] must be a string"
|
|
289
|
-
)
|
|
290
|
-
|
|
291
|
-
apply_error_formatters(error_formatters, response)
|
|
351
|
+
is_subscription_response = (
|
|
352
|
+
response.get("method") == "eth_subscription"
|
|
353
|
+
and response.get("params") is not None
|
|
354
|
+
and response["params"].get("subscription") is not None
|
|
355
|
+
and response["params"].get("result") is not None
|
|
356
|
+
)
|
|
292
357
|
|
|
293
|
-
|
|
358
|
+
_validate_response(
|
|
359
|
+
response,
|
|
360
|
+
error_formatters,
|
|
361
|
+
is_subscription_response=is_subscription_response,
|
|
362
|
+
logger=self.logger,
|
|
363
|
+
)
|
|
294
364
|
|
|
295
|
-
#
|
|
296
|
-
|
|
365
|
+
# format results
|
|
366
|
+
if "result" in response:
|
|
297
367
|
# Null values for result should apply null_result_formatters
|
|
298
368
|
# Skip when result not present in the response (fallback to False)
|
|
299
369
|
if response.get("result", False) in NULL_RESPONSES:
|
|
300
370
|
apply_null_result_formatters(null_result_formatters, response, params)
|
|
301
371
|
return response.get("result")
|
|
302
372
|
|
|
303
|
-
#
|
|
304
|
-
elif
|
|
305
|
-
response.get("method") == "eth_subscription"
|
|
306
|
-
and response.get("params") is not None
|
|
307
|
-
and response["params"].get("subscription") is not None
|
|
308
|
-
and response["params"].get("result") is not None
|
|
309
|
-
):
|
|
373
|
+
# response from eth_subscription includes response["params"]["result"]
|
|
374
|
+
elif is_subscription_response:
|
|
310
375
|
return {
|
|
311
376
|
"subscription": response["params"]["subscription"],
|
|
312
377
|
"result": response["params"]["result"],
|
|
313
378
|
}
|
|
314
379
|
|
|
315
|
-
# Any other response type raises BadResponseFormat
|
|
316
|
-
else:
|
|
317
|
-
_raise_bad_response_format(response)
|
|
318
|
-
|
|
319
380
|
def request_blocking(
|
|
320
381
|
self,
|
|
321
382
|
method: Union[RPCEndpoint, Callable[..., RPCEndpoint]],
|
|
@@ -346,36 +407,147 @@ class RequestManager:
|
|
|
346
407
|
response, params, error_formatters, null_result_formatters
|
|
347
408
|
)
|
|
348
409
|
|
|
410
|
+
# -- batch requests management -- #
|
|
411
|
+
|
|
412
|
+
def _batch_requests(self) -> RequestBatcher[Method[Callable[..., Any]]]:
|
|
413
|
+
"""
|
|
414
|
+
Context manager for making batch requests
|
|
415
|
+
"""
|
|
416
|
+
if not isinstance(self.provider, (AsyncJSONBaseProvider, JSONBaseProvider)):
|
|
417
|
+
raise Web3TypeError("Batch requests are not supported by this provider.")
|
|
418
|
+
return RequestBatcher(self.w3)
|
|
419
|
+
|
|
420
|
+
def _make_batch_request(
|
|
421
|
+
self, requests_info: List[Tuple[Tuple["RPCEndpoint", Any], Sequence[Any]]]
|
|
422
|
+
) -> List[RPCResponse]:
|
|
423
|
+
"""
|
|
424
|
+
Make a batch request using the provider
|
|
425
|
+
"""
|
|
426
|
+
provider = cast(JSONBaseProvider, self.provider)
|
|
427
|
+
request_func = provider.batch_request_func(
|
|
428
|
+
cast("Web3", self.w3), cast("MiddlewareOnion", self.middleware_onion)
|
|
429
|
+
)
|
|
430
|
+
responses = request_func(
|
|
431
|
+
[
|
|
432
|
+
(method, params)
|
|
433
|
+
for (method, params), _response_formatters in requests_info
|
|
434
|
+
]
|
|
435
|
+
)
|
|
436
|
+
formatted_responses = [
|
|
437
|
+
self._format_batched_response(info, resp)
|
|
438
|
+
for info, resp in zip(requests_info, responses)
|
|
439
|
+
]
|
|
440
|
+
return list(formatted_responses)
|
|
441
|
+
|
|
442
|
+
async def _async_make_batch_request(
|
|
443
|
+
self,
|
|
444
|
+
requests_info: List[
|
|
445
|
+
Coroutine[Any, Any, Tuple[Tuple["RPCEndpoint", Any], Sequence[Any]]]
|
|
446
|
+
],
|
|
447
|
+
) -> List[RPCResponse]:
|
|
448
|
+
"""
|
|
449
|
+
Make an asynchronous batch request using the provider
|
|
450
|
+
"""
|
|
451
|
+
provider = cast(AsyncJSONBaseProvider, self.provider)
|
|
452
|
+
request_func = await provider.batch_request_func(
|
|
453
|
+
cast("AsyncWeb3", self.w3),
|
|
454
|
+
cast("MiddlewareOnion", self.middleware_onion),
|
|
455
|
+
)
|
|
456
|
+
# since we add items to the batch without awaiting, we unpack the coroutines
|
|
457
|
+
# and await them all here
|
|
458
|
+
unpacked_requests_info = await asyncio.gather(*requests_info)
|
|
459
|
+
responses = await request_func(
|
|
460
|
+
[
|
|
461
|
+
(method, params)
|
|
462
|
+
for (method, params), _response_formatters in unpacked_requests_info
|
|
463
|
+
]
|
|
464
|
+
)
|
|
465
|
+
|
|
466
|
+
if isinstance(self.provider, PersistentConnectionProvider):
|
|
467
|
+
# call _process_response for each response in the batch
|
|
468
|
+
return [await self._process_response(resp) for resp in responses]
|
|
469
|
+
|
|
470
|
+
formatted_responses = [
|
|
471
|
+
self._format_batched_response(info, resp)
|
|
472
|
+
for info, resp in zip(unpacked_requests_info, responses)
|
|
473
|
+
]
|
|
474
|
+
return list(formatted_responses)
|
|
475
|
+
|
|
476
|
+
def _format_batched_response(
|
|
477
|
+
self,
|
|
478
|
+
requests_info: Tuple[Tuple[RPCEndpoint, Any], Sequence[Any]],
|
|
479
|
+
response: RPCResponse,
|
|
480
|
+
) -> RPCResponse:
|
|
481
|
+
result_formatters, error_formatters, null_result_formatters = requests_info[1]
|
|
482
|
+
return apply_result_formatters(
|
|
483
|
+
result_formatters,
|
|
484
|
+
self.formatted_response(
|
|
485
|
+
response,
|
|
486
|
+
requests_info[0][1],
|
|
487
|
+
error_formatters,
|
|
488
|
+
null_result_formatters,
|
|
489
|
+
),
|
|
490
|
+
)
|
|
491
|
+
|
|
349
492
|
# -- persistent connection -- #
|
|
350
493
|
|
|
351
|
-
async def
|
|
494
|
+
async def socket_request(self, method: RPCEndpoint, params: Any) -> RPCResponse:
|
|
352
495
|
provider = cast(PersistentConnectionProvider, self._provider)
|
|
353
496
|
request_func = await provider.request_func(
|
|
354
|
-
cast("AsyncWeb3", self.w3),
|
|
355
|
-
cast(AsyncMiddlewareOnion, self.middleware_onion),
|
|
497
|
+
cast("AsyncWeb3", self.w3), cast("MiddlewareOnion", self.middleware_onion)
|
|
356
498
|
)
|
|
357
499
|
self.logger.debug(
|
|
358
|
-
"Making request to open
|
|
359
|
-
f"
|
|
500
|
+
"Making request to open socket connection and waiting for response: "
|
|
501
|
+
f"{provider.get_endpoint_uri_or_ipc_path()}, method: {method}"
|
|
360
502
|
)
|
|
361
503
|
response = await request_func(method, params)
|
|
362
|
-
return await self.
|
|
504
|
+
return await self._process_response(response)
|
|
505
|
+
|
|
506
|
+
async def send(self, method: RPCEndpoint, params: Any) -> None:
|
|
507
|
+
provider = cast(PersistentConnectionProvider, self._provider)
|
|
508
|
+
# run through the request processors of the middleware
|
|
509
|
+
for mw_class in self.middleware_onion.as_tuple_of_middleware():
|
|
510
|
+
mw = mw_class(self.w3)
|
|
511
|
+
method, params = mw.request_processor(method, params)
|
|
512
|
+
|
|
513
|
+
self.logger.debug(
|
|
514
|
+
"Sending request to open socket connection: "
|
|
515
|
+
f"{provider.get_endpoint_uri_or_ipc_path()}, method: {method}"
|
|
516
|
+
)
|
|
517
|
+
await provider.socket_send(provider.encode_rpc_request(method, params))
|
|
518
|
+
|
|
519
|
+
async def recv(self) -> RPCResponse:
|
|
520
|
+
provider = cast(PersistentConnectionProvider, self._provider)
|
|
521
|
+
self.logger.debug(
|
|
522
|
+
"Getting next response from open socket connection: "
|
|
523
|
+
f"{provider.get_endpoint_uri_or_ipc_path()}"
|
|
524
|
+
)
|
|
525
|
+
# pop from the queue since the listener task is responsible for reading
|
|
526
|
+
# directly from the socket
|
|
527
|
+
request_response_cache = self._request_processor._request_response_cache
|
|
528
|
+
_key, response = await request_response_cache.async_await_and_popitem(
|
|
529
|
+
last=False,
|
|
530
|
+
timeout=provider.request_timeout,
|
|
531
|
+
)
|
|
532
|
+
return await self._process_response(response)
|
|
363
533
|
|
|
364
534
|
def _persistent_message_stream(self) -> "_AsyncPersistentMessageStream":
|
|
365
535
|
return _AsyncPersistentMessageStream(self)
|
|
366
536
|
|
|
367
|
-
async def
|
|
368
|
-
return await self.
|
|
537
|
+
async def _get_next_message(self) -> RPCResponse:
|
|
538
|
+
return await self._message_stream().__anext__()
|
|
369
539
|
|
|
370
|
-
async def
|
|
540
|
+
async def _message_stream(self) -> AsyncGenerator[RPCResponse, None]:
|
|
371
541
|
if not isinstance(self._provider, PersistentConnectionProvider):
|
|
372
|
-
raise
|
|
373
|
-
"Only
|
|
374
|
-
"can listen to
|
|
542
|
+
raise Web3TypeError(
|
|
543
|
+
"Only providers that maintain an open, persistent connection "
|
|
544
|
+
"can listen to streams."
|
|
375
545
|
)
|
|
376
546
|
|
|
377
547
|
if self._provider._message_listener_task is None:
|
|
378
|
-
raise ProviderConnectionError(
|
|
548
|
+
raise ProviderConnectionError(
|
|
549
|
+
"No listener found for persistent connection."
|
|
550
|
+
)
|
|
379
551
|
|
|
380
552
|
while True:
|
|
381
553
|
try:
|
|
@@ -388,16 +560,17 @@ class RequestManager:
|
|
|
388
560
|
in self._request_processor.active_subscriptions
|
|
389
561
|
):
|
|
390
562
|
# if response is an active subscription response, process it
|
|
391
|
-
yield await self.
|
|
563
|
+
yield await self._process_response(response)
|
|
392
564
|
except TaskNotRunning:
|
|
565
|
+
await asyncio.sleep(0)
|
|
393
566
|
self._provider._handle_listener_task_exceptions()
|
|
394
567
|
self.logger.error(
|
|
395
568
|
"Message listener background task has stopped unexpectedly. "
|
|
396
569
|
"Stopping message stream."
|
|
397
570
|
)
|
|
398
|
-
|
|
571
|
+
return
|
|
399
572
|
|
|
400
|
-
async def
|
|
573
|
+
async def _process_response(self, response: RPCResponse) -> RPCResponse:
|
|
401
574
|
provider = cast(PersistentConnectionProvider, self._provider)
|
|
402
575
|
request_info = self._request_processor.get_request_information_for_response(
|
|
403
576
|
response
|
|
@@ -461,7 +634,4 @@ class _AsyncPersistentMessageStream:
|
|
|
461
634
|
return self
|
|
462
635
|
|
|
463
636
|
async def __anext__(self) -> RPCResponse:
|
|
464
|
-
|
|
465
|
-
return await self.manager._get_next_ws_message()
|
|
466
|
-
except ConnectionClosedOK:
|
|
467
|
-
raise StopAsyncIteration
|
|
637
|
+
return await self.manager._get_next_message()
|