web3 6.20.2__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.2.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.2.dist-info → web3-7.0.0.dist-info}/WHEEL +1 -1
- {web3-6.20.2.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.2.dist-info/METADATA +0 -103
- web3-6.20.2.dist-info/RECORD +0 -283
- web3-6.20.2.dist-info/entry_points.txt +0 -2
- /web3/_utils/{function_identifiers.py → abi_element_identifiers.py} +0 -0
|
@@ -10,9 +10,12 @@ from types import (
|
|
|
10
10
|
)
|
|
11
11
|
from typing import (
|
|
12
12
|
Any,
|
|
13
|
+
List,
|
|
13
14
|
Optional,
|
|
15
|
+
Tuple,
|
|
14
16
|
Type,
|
|
15
17
|
Union,
|
|
18
|
+
cast,
|
|
16
19
|
)
|
|
17
20
|
|
|
18
21
|
from eth_typing import (
|
|
@@ -25,6 +28,12 @@ from websockets.legacy.client import (
|
|
|
25
28
|
WebSocketClientProtocol,
|
|
26
29
|
)
|
|
27
30
|
|
|
31
|
+
from web3._utils.batching import (
|
|
32
|
+
sort_batch_response_by_response_ids,
|
|
33
|
+
)
|
|
34
|
+
from web3._utils.caching import (
|
|
35
|
+
handle_request_caching,
|
|
36
|
+
)
|
|
28
37
|
from web3.exceptions import (
|
|
29
38
|
Web3ValidationError,
|
|
30
39
|
)
|
|
@@ -37,7 +46,7 @@ from web3.types import (
|
|
|
37
46
|
)
|
|
38
47
|
|
|
39
48
|
RESTRICTED_WEBSOCKET_KWARGS = {"uri", "loop"}
|
|
40
|
-
DEFAULT_WEBSOCKET_TIMEOUT =
|
|
49
|
+
DEFAULT_WEBSOCKET_TIMEOUT = 30
|
|
41
50
|
|
|
42
51
|
|
|
43
52
|
def _start_event_loop(loop: asyncio.AbstractEventLoop) -> None:
|
|
@@ -82,8 +91,8 @@ class PersistentWebSocket:
|
|
|
82
91
|
self.ws = None
|
|
83
92
|
|
|
84
93
|
|
|
85
|
-
class
|
|
86
|
-
logger = logging.getLogger("web3.providers.
|
|
94
|
+
class LegacyWebSocketProvider(JSONBaseProvider):
|
|
95
|
+
logger = logging.getLogger("web3.providers.WebSocketProvider")
|
|
87
96
|
_loop = None
|
|
88
97
|
|
|
89
98
|
def __init__(
|
|
@@ -91,13 +100,14 @@ class WebsocketProvider(JSONBaseProvider):
|
|
|
91
100
|
endpoint_uri: Optional[Union[URI, str]] = None,
|
|
92
101
|
websocket_kwargs: Optional[Any] = None,
|
|
93
102
|
websocket_timeout: int = DEFAULT_WEBSOCKET_TIMEOUT,
|
|
103
|
+
**kwargs: Any,
|
|
94
104
|
) -> None:
|
|
95
105
|
self.endpoint_uri = URI(endpoint_uri)
|
|
96
106
|
self.websocket_timeout = websocket_timeout
|
|
97
107
|
if self.endpoint_uri is None:
|
|
98
108
|
self.endpoint_uri = get_default_endpoint()
|
|
99
|
-
if
|
|
100
|
-
|
|
109
|
+
if LegacyWebSocketProvider._loop is None:
|
|
110
|
+
LegacyWebSocketProvider._loop = _get_threaded_loop()
|
|
101
111
|
if websocket_kwargs is None:
|
|
102
112
|
websocket_kwargs = {}
|
|
103
113
|
else:
|
|
@@ -110,7 +120,7 @@ class WebsocketProvider(JSONBaseProvider):
|
|
|
110
120
|
f"in websocket_kwargs, found: {found_restricted_keys}"
|
|
111
121
|
)
|
|
112
122
|
self.conn = PersistentWebSocket(self.endpoint_uri, websocket_kwargs)
|
|
113
|
-
super().__init__()
|
|
123
|
+
super().__init__(**kwargs)
|
|
114
124
|
|
|
115
125
|
def __str__(self) -> str:
|
|
116
126
|
return f"WS connection {self.endpoint_uri}"
|
|
@@ -124,12 +134,27 @@ class WebsocketProvider(JSONBaseProvider):
|
|
|
124
134
|
await asyncio.wait_for(conn.recv(), timeout=self.websocket_timeout)
|
|
125
135
|
)
|
|
126
136
|
|
|
137
|
+
@handle_request_caching
|
|
127
138
|
def make_request(self, method: RPCEndpoint, params: Any) -> RPCResponse:
|
|
128
139
|
self.logger.debug(
|
|
129
140
|
f"Making request WebSocket. URI: {self.endpoint_uri}, " f"Method: {method}"
|
|
130
141
|
)
|
|
131
142
|
request_data = self.encode_rpc_request(method, params)
|
|
132
143
|
future = asyncio.run_coroutine_threadsafe(
|
|
133
|
-
self.coro_make_request(request_data),
|
|
144
|
+
self.coro_make_request(request_data), LegacyWebSocketProvider._loop
|
|
134
145
|
)
|
|
135
146
|
return future.result()
|
|
147
|
+
|
|
148
|
+
def make_batch_request(
|
|
149
|
+
self, requests: List[Tuple[RPCEndpoint, Any]]
|
|
150
|
+
) -> List[RPCResponse]:
|
|
151
|
+
self.logger.debug(
|
|
152
|
+
f"Making batch request WebSocket. URI: {self.endpoint_uri}, "
|
|
153
|
+
f"Methods: {requests}"
|
|
154
|
+
)
|
|
155
|
+
request_data = self.encode_batch_rpc_request(requests)
|
|
156
|
+
future = asyncio.run_coroutine_threadsafe(
|
|
157
|
+
self.coro_make_request(request_data), LegacyWebSocketProvider._loop
|
|
158
|
+
)
|
|
159
|
+
response = cast(List[RPCResponse], future.result())
|
|
160
|
+
return sort_batch_response_by_response_ids(response)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from .persistent import (
|
|
2
|
+
PersistentConnectionProvider,
|
|
3
|
+
)
|
|
4
|
+
from .persistent_connection import (
|
|
5
|
+
PersistentConnection,
|
|
6
|
+
)
|
|
7
|
+
from .request_processor import (
|
|
8
|
+
RequestProcessor,
|
|
9
|
+
)
|
|
10
|
+
from .async_ipc import (
|
|
11
|
+
AsyncIPCProvider,
|
|
12
|
+
)
|
|
13
|
+
from .websocket import (
|
|
14
|
+
WebSocketProvider,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"PersistentConnectionProvider",
|
|
19
|
+
"PersistentConnection",
|
|
20
|
+
"AsyncIPCProvider",
|
|
21
|
+
"WebSocketProvider",
|
|
22
|
+
]
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import errno
|
|
3
|
+
import json
|
|
4
|
+
from json import (
|
|
5
|
+
JSONDecodeError,
|
|
6
|
+
)
|
|
7
|
+
import logging
|
|
8
|
+
from pathlib import (
|
|
9
|
+
Path,
|
|
10
|
+
)
|
|
11
|
+
import sys
|
|
12
|
+
from typing import (
|
|
13
|
+
Any,
|
|
14
|
+
Optional,
|
|
15
|
+
Tuple,
|
|
16
|
+
Union,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
from eth_utils import (
|
|
20
|
+
to_text,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
from web3.types import (
|
|
24
|
+
RPCResponse,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
from . import (
|
|
28
|
+
PersistentConnectionProvider,
|
|
29
|
+
)
|
|
30
|
+
from ...exceptions import (
|
|
31
|
+
ProviderConnectionError,
|
|
32
|
+
Web3TypeError,
|
|
33
|
+
)
|
|
34
|
+
from ..ipc import (
|
|
35
|
+
get_default_ipc_path,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
async def async_get_ipc_socket(
|
|
40
|
+
ipc_path: str,
|
|
41
|
+
) -> Tuple[asyncio.StreamReader, asyncio.StreamWriter]:
|
|
42
|
+
if sys.platform == "win32":
|
|
43
|
+
# On Windows named pipe is used. Simulate socket with it.
|
|
44
|
+
from web3._utils.windows import (
|
|
45
|
+
NamedPipe,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
return NamedPipe(ipc_path)
|
|
49
|
+
else:
|
|
50
|
+
return await asyncio.open_unix_connection(ipc_path)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class AsyncIPCProvider(PersistentConnectionProvider):
|
|
54
|
+
logger = logging.getLogger("web3.providers.AsyncIPCProvider")
|
|
55
|
+
|
|
56
|
+
_reader: Optional[asyncio.StreamReader] = None
|
|
57
|
+
_writer: Optional[asyncio.StreamWriter] = None
|
|
58
|
+
_decoder: json.JSONDecoder = json.JSONDecoder()
|
|
59
|
+
_raw_message: str = ""
|
|
60
|
+
|
|
61
|
+
def __init__(
|
|
62
|
+
self,
|
|
63
|
+
ipc_path: Optional[Union[str, Path]] = None,
|
|
64
|
+
# `PersistentConnectionProvider` kwargs can be passed through
|
|
65
|
+
**kwargs: Any,
|
|
66
|
+
) -> None:
|
|
67
|
+
if ipc_path is None:
|
|
68
|
+
self.ipc_path = get_default_ipc_path()
|
|
69
|
+
elif isinstance(ipc_path, str) or isinstance(ipc_path, Path):
|
|
70
|
+
self.ipc_path = str(Path(ipc_path).expanduser().resolve())
|
|
71
|
+
else:
|
|
72
|
+
raise Web3TypeError("ipc_path must be of type string or pathlib.Path")
|
|
73
|
+
|
|
74
|
+
super().__init__(**kwargs)
|
|
75
|
+
|
|
76
|
+
def __str__(self) -> str:
|
|
77
|
+
return f"<{self.__class__.__name__} {self.ipc_path}>"
|
|
78
|
+
|
|
79
|
+
async def is_connected(self, show_traceback: bool = False) -> bool:
|
|
80
|
+
if not self._writer or not self._reader:
|
|
81
|
+
return False
|
|
82
|
+
|
|
83
|
+
try:
|
|
84
|
+
await self.make_request("web3_clientVersion", [])
|
|
85
|
+
return True
|
|
86
|
+
except (OSError, ProviderConnectionError) as e:
|
|
87
|
+
if show_traceback:
|
|
88
|
+
raise ProviderConnectionError(
|
|
89
|
+
f"Problem connecting to provider with error: {type(e)}: {e}"
|
|
90
|
+
)
|
|
91
|
+
return False
|
|
92
|
+
|
|
93
|
+
async def socket_send(self, request_data: bytes) -> None:
|
|
94
|
+
if self._writer is None:
|
|
95
|
+
raise ProviderConnectionError(
|
|
96
|
+
"Connection to ipc socket has not been initiated for the provider."
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
return await asyncio.wait_for(
|
|
100
|
+
self._socket_send(request_data), timeout=self.request_timeout
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
async def socket_recv(self) -> RPCResponse:
|
|
104
|
+
while True:
|
|
105
|
+
# yield to the event loop to allow other tasks to run
|
|
106
|
+
await asyncio.sleep(0)
|
|
107
|
+
|
|
108
|
+
try:
|
|
109
|
+
response, pos = self._decoder.raw_decode(self._raw_message)
|
|
110
|
+
self._raw_message = self._raw_message[pos:].lstrip()
|
|
111
|
+
return response
|
|
112
|
+
except JSONDecodeError:
|
|
113
|
+
# read more data from the socket if the current raw message is
|
|
114
|
+
# incomplete
|
|
115
|
+
self._raw_message += to_text(await self._reader.read(4096)).lstrip()
|
|
116
|
+
|
|
117
|
+
# -- private methods -- #
|
|
118
|
+
|
|
119
|
+
async def _socket_send(self, request_data: bytes) -> None:
|
|
120
|
+
try:
|
|
121
|
+
self._writer.write(request_data)
|
|
122
|
+
await self._writer.drain()
|
|
123
|
+
except OSError as e:
|
|
124
|
+
# Broken pipe
|
|
125
|
+
if e.errno == errno.EPIPE:
|
|
126
|
+
# one extra attempt, then give up
|
|
127
|
+
await self._reset_socket()
|
|
128
|
+
self._writer.write(request_data)
|
|
129
|
+
await self._writer.drain()
|
|
130
|
+
|
|
131
|
+
async def _reset_socket(self) -> None:
|
|
132
|
+
self._writer.close()
|
|
133
|
+
await self._writer.wait_closed()
|
|
134
|
+
self._reader, self._writer = await async_get_ipc_socket(self.ipc_path)
|
|
135
|
+
|
|
136
|
+
async def _provider_specific_connect(self) -> None:
|
|
137
|
+
self._reader, self._writer = await async_get_ipc_socket(self.ipc_path)
|
|
138
|
+
|
|
139
|
+
async def _provider_specific_disconnect(self) -> None:
|
|
140
|
+
if self._writer and not self._writer.is_closing():
|
|
141
|
+
self._writer.close()
|
|
142
|
+
await self._writer.wait_closed()
|
|
143
|
+
self._writer = None
|
|
144
|
+
if self._reader:
|
|
145
|
+
self._reader = None
|
|
146
|
+
|
|
147
|
+
async def _provider_specific_socket_reader(self) -> RPCResponse:
|
|
148
|
+
return await self.socket_recv()
|
|
149
|
+
|
|
150
|
+
def _error_log_listener_task_exception(self, e: Exception) -> None:
|
|
151
|
+
super()._error_log_listener_task_exception(e)
|
|
152
|
+
# reset the raw message buffer on exception when error logging
|
|
153
|
+
self._raw_message = ""
|
|
@@ -1,68 +1,91 @@
|
|
|
1
1
|
from abc import (
|
|
2
2
|
ABC,
|
|
3
|
+
abstractmethod,
|
|
3
4
|
)
|
|
4
5
|
import asyncio
|
|
6
|
+
import json
|
|
5
7
|
import logging
|
|
6
8
|
from typing import (
|
|
9
|
+
Any,
|
|
10
|
+
List,
|
|
7
11
|
Optional,
|
|
12
|
+
Tuple,
|
|
13
|
+
Union,
|
|
14
|
+
cast,
|
|
8
15
|
)
|
|
9
16
|
|
|
10
17
|
from websockets import (
|
|
11
18
|
ConnectionClosed,
|
|
12
|
-
ConnectionClosedOK,
|
|
13
|
-
WebSocketClientProtocol,
|
|
14
19
|
WebSocketException,
|
|
15
20
|
)
|
|
16
21
|
|
|
22
|
+
from web3._utils.batching import (
|
|
23
|
+
BATCH_REQUEST_ID,
|
|
24
|
+
sort_batch_response_by_response_ids,
|
|
25
|
+
)
|
|
17
26
|
from web3._utils.caching import (
|
|
27
|
+
async_handle_request_caching,
|
|
18
28
|
generate_cache_key,
|
|
19
29
|
)
|
|
20
30
|
from web3.exceptions import (
|
|
31
|
+
PersistentConnectionClosedOK,
|
|
21
32
|
ProviderConnectionError,
|
|
22
33
|
TaskNotRunning,
|
|
23
34
|
TimeExhausted,
|
|
35
|
+
Web3AttributeError,
|
|
24
36
|
)
|
|
25
37
|
from web3.providers.async_base import (
|
|
26
38
|
AsyncJSONBaseProvider,
|
|
27
39
|
)
|
|
28
|
-
from web3.providers.
|
|
40
|
+
from web3.providers.persistent.request_processor import (
|
|
29
41
|
RequestProcessor,
|
|
30
42
|
)
|
|
31
43
|
from web3.types import (
|
|
44
|
+
RPCEndpoint,
|
|
32
45
|
RPCId,
|
|
33
46
|
RPCResponse,
|
|
34
47
|
)
|
|
35
48
|
|
|
36
|
-
DEFAULT_PERSISTENT_CONNECTION_TIMEOUT =
|
|
49
|
+
DEFAULT_PERSISTENT_CONNECTION_TIMEOUT = 30.0
|
|
37
50
|
|
|
38
51
|
|
|
39
52
|
class PersistentConnectionProvider(AsyncJSONBaseProvider, ABC):
|
|
40
53
|
logger = logging.getLogger("web3.providers.PersistentConnectionProvider")
|
|
41
54
|
has_persistent_connection = True
|
|
42
|
-
endpoint_uri: Optional[str] = None
|
|
43
|
-
|
|
44
|
-
_max_connection_retries: int = 5
|
|
45
55
|
|
|
46
|
-
_ws: Optional[WebSocketClientProtocol] = None
|
|
47
56
|
_request_processor: RequestProcessor
|
|
48
57
|
_message_listener_task: Optional["asyncio.Task[None]"] = None
|
|
49
58
|
_listen_event: asyncio.Event = asyncio.Event()
|
|
50
59
|
|
|
60
|
+
_batch_request_counter: Optional[int] = None
|
|
61
|
+
|
|
51
62
|
def __init__(
|
|
52
63
|
self,
|
|
53
64
|
request_timeout: float = DEFAULT_PERSISTENT_CONNECTION_TIMEOUT,
|
|
54
65
|
subscription_response_queue_size: int = 500,
|
|
55
|
-
request_information_cache_size: int = 500,
|
|
56
66
|
silence_listener_task_exceptions: bool = False,
|
|
67
|
+
max_connection_retries: int = 5,
|
|
68
|
+
**kwargs: Any,
|
|
57
69
|
) -> None:
|
|
58
|
-
super().__init__()
|
|
70
|
+
super().__init__(**kwargs)
|
|
59
71
|
self._request_processor = RequestProcessor(
|
|
60
72
|
self,
|
|
61
73
|
subscription_response_queue_size=subscription_response_queue_size,
|
|
62
|
-
request_information_cache_size=request_information_cache_size,
|
|
63
74
|
)
|
|
64
75
|
self.request_timeout = request_timeout
|
|
65
76
|
self.silence_listener_task_exceptions = silence_listener_task_exceptions
|
|
77
|
+
self._max_connection_retries = max_connection_retries
|
|
78
|
+
|
|
79
|
+
def get_endpoint_uri_or_ipc_path(self) -> str:
|
|
80
|
+
if hasattr(self, "endpoint_uri"):
|
|
81
|
+
return str(self.endpoint_uri)
|
|
82
|
+
elif hasattr(self, "ipc_path"):
|
|
83
|
+
return str(self.ipc_path)
|
|
84
|
+
else:
|
|
85
|
+
raise Web3AttributeError(
|
|
86
|
+
"`PersistentConnectionProvider` must have either `endpoint_uri` or "
|
|
87
|
+
"`ipc_path` attribute."
|
|
88
|
+
)
|
|
66
89
|
|
|
67
90
|
async def connect(self) -> None:
|
|
68
91
|
_connection_attempts = 0
|
|
@@ -72,7 +95,9 @@ class PersistentConnectionProvider(AsyncJSONBaseProvider, ABC):
|
|
|
72
95
|
while _connection_attempts != self._max_connection_retries:
|
|
73
96
|
try:
|
|
74
97
|
_connection_attempts += 1
|
|
75
|
-
self.logger.info(
|
|
98
|
+
self.logger.info(
|
|
99
|
+
f"Connecting to: {self.get_endpoint_uri_or_ipc_path()}"
|
|
100
|
+
)
|
|
76
101
|
await self._provider_specific_connect()
|
|
77
102
|
self._message_listener_task = asyncio.create_task(
|
|
78
103
|
self._message_listener()
|
|
@@ -80,16 +105,18 @@ class PersistentConnectionProvider(AsyncJSONBaseProvider, ABC):
|
|
|
80
105
|
self._message_listener_task.add_done_callback(
|
|
81
106
|
self._message_listener_callback
|
|
82
107
|
)
|
|
83
|
-
self.logger.info(
|
|
108
|
+
self.logger.info(
|
|
109
|
+
f"Successfully connected to: {self.get_endpoint_uri_or_ipc_path()}"
|
|
110
|
+
)
|
|
84
111
|
break
|
|
85
112
|
except (WebSocketException, OSError) as e:
|
|
86
113
|
if _connection_attempts == self._max_connection_retries:
|
|
87
114
|
raise ProviderConnectionError(
|
|
88
|
-
f"Could not connect to: {self.
|
|
115
|
+
f"Could not connect to: {self.get_endpoint_uri_or_ipc_path()}. "
|
|
89
116
|
f"Retries exceeded max of {self._max_connection_retries}."
|
|
90
117
|
) from e
|
|
91
118
|
self.logger.info(
|
|
92
|
-
f"Could not connect to: {self.
|
|
119
|
+
f"Could not connect to: {self.get_endpoint_uri_or_ipc_path()}. "
|
|
93
120
|
f"Retrying in {round(_backoff_time, 1)} seconds.",
|
|
94
121
|
exc_info=True,
|
|
95
122
|
)
|
|
@@ -109,7 +136,47 @@ class PersistentConnectionProvider(AsyncJSONBaseProvider, ABC):
|
|
|
109
136
|
|
|
110
137
|
await self._provider_specific_disconnect()
|
|
111
138
|
self._request_processor.clear_caches()
|
|
112
|
-
self.logger.info(
|
|
139
|
+
self.logger.info(
|
|
140
|
+
f"Successfully disconnected from: {self.get_endpoint_uri_or_ipc_path()}"
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
@async_handle_request_caching
|
|
144
|
+
async def make_request(self, method: RPCEndpoint, params: Any) -> RPCResponse:
|
|
145
|
+
request_data = self.encode_rpc_request(method, params)
|
|
146
|
+
await self.socket_send(request_data)
|
|
147
|
+
|
|
148
|
+
current_request_id = json.loads(request_data)["id"]
|
|
149
|
+
response = await self._get_response_for_request_id(current_request_id)
|
|
150
|
+
|
|
151
|
+
return response
|
|
152
|
+
|
|
153
|
+
async def make_batch_request(
|
|
154
|
+
self, requests: List[Tuple[RPCEndpoint, Any]]
|
|
155
|
+
) -> List[RPCResponse]:
|
|
156
|
+
request_data = self.encode_batch_rpc_request(requests)
|
|
157
|
+
await self.socket_send(request_data)
|
|
158
|
+
|
|
159
|
+
response = cast(
|
|
160
|
+
List[RPCResponse], await self._get_response_for_request_id(BATCH_REQUEST_ID)
|
|
161
|
+
)
|
|
162
|
+
return response
|
|
163
|
+
|
|
164
|
+
# -- abstract methods -- #
|
|
165
|
+
|
|
166
|
+
@abstractmethod
|
|
167
|
+
async def socket_send(self, request_data: bytes) -> None:
|
|
168
|
+
"""
|
|
169
|
+
Send an encoded RPC request to the provider over the persistent connection.
|
|
170
|
+
"""
|
|
171
|
+
raise NotImplementedError("Must be implemented by subclasses")
|
|
172
|
+
|
|
173
|
+
@abstractmethod
|
|
174
|
+
async def socket_recv(self) -> RPCResponse:
|
|
175
|
+
"""
|
|
176
|
+
Receive, decode, and return an RPC response from the provider over the
|
|
177
|
+
persistent connection.
|
|
178
|
+
"""
|
|
179
|
+
raise NotImplementedError("Must be implemented by subclasses")
|
|
113
180
|
|
|
114
181
|
# -- private methods -- #
|
|
115
182
|
|
|
@@ -119,7 +186,7 @@ class PersistentConnectionProvider(AsyncJSONBaseProvider, ABC):
|
|
|
119
186
|
async def _provider_specific_disconnect(self) -> None:
|
|
120
187
|
raise NotImplementedError("Must be implemented by subclasses")
|
|
121
188
|
|
|
122
|
-
async def
|
|
189
|
+
async def _provider_specific_socket_reader(self) -> RPCResponse:
|
|
123
190
|
raise NotImplementedError("Must be implemented by subclasses")
|
|
124
191
|
|
|
125
192
|
def _message_listener_callback(
|
|
@@ -141,14 +208,28 @@ class PersistentConnectionProvider(AsyncJSONBaseProvider, ABC):
|
|
|
141
208
|
# the use of sleep(0) seems to be the most efficient way to yield control
|
|
142
209
|
# back to the event loop to share the loop with other tasks.
|
|
143
210
|
await asyncio.sleep(0)
|
|
211
|
+
|
|
144
212
|
try:
|
|
145
|
-
await self.
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
213
|
+
response = await self._provider_specific_socket_reader()
|
|
214
|
+
|
|
215
|
+
if isinstance(response, list):
|
|
216
|
+
response = sort_batch_response_by_response_ids(response)
|
|
217
|
+
|
|
218
|
+
subscription = (
|
|
219
|
+
response.get("method") == "eth_subscription"
|
|
220
|
+
if not isinstance(response, list)
|
|
221
|
+
else False
|
|
222
|
+
)
|
|
223
|
+
await self._request_processor.cache_raw_response(
|
|
224
|
+
response, subscription=subscription
|
|
225
|
+
)
|
|
226
|
+
except PersistentConnectionClosedOK as e:
|
|
227
|
+
self.logger.info(
|
|
228
|
+
"Message listener background task has ended gracefully: "
|
|
229
|
+
f"{e.user_message}"
|
|
230
|
+
)
|
|
231
|
+
# trigger a return to end the listener task and initiate the callback fn
|
|
232
|
+
return
|
|
152
233
|
except Exception as e:
|
|
153
234
|
if not self.silence_listener_task_exceptions:
|
|
154
235
|
raise e
|
|
@@ -182,7 +263,7 @@ class PersistentConnectionProvider(AsyncJSONBaseProvider, ABC):
|
|
|
182
263
|
raise msg_listener_task.exception()
|
|
183
264
|
|
|
184
265
|
async def _get_response_for_request_id(
|
|
185
|
-
self, request_id: RPCId, timeout: Optional[float] = None
|
|
266
|
+
self, request_id: Union[RPCId, List[RPCId]], timeout: Optional[float] = None
|
|
186
267
|
) -> RPCResponse:
|
|
187
268
|
if timeout is None:
|
|
188
269
|
timeout = self.request_timeout
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
from typing import (
|
|
2
|
+
TYPE_CHECKING,
|
|
3
|
+
Any,
|
|
4
|
+
Dict,
|
|
5
|
+
)
|
|
6
|
+
|
|
7
|
+
from web3.types import (
|
|
8
|
+
RPCEndpoint,
|
|
9
|
+
RPCResponse,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from web3.main import ( # noqa: F401
|
|
14
|
+
AsyncWeb3,
|
|
15
|
+
)
|
|
16
|
+
from web3.manager import ( # noqa: F401
|
|
17
|
+
_AsyncPersistentMessageStream,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class PersistentConnection:
|
|
22
|
+
"""
|
|
23
|
+
A class that houses the public API for interacting with the persistent connection
|
|
24
|
+
via a `AsyncWeb3` instance instantiated with a `PersistentConnectionProvider` class.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(self, w3: "AsyncWeb3"):
|
|
28
|
+
self._manager = w3.manager
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
def subscriptions(self) -> Dict[str, Any]:
|
|
32
|
+
"""
|
|
33
|
+
Return the active subscriptions on the persistent connection.
|
|
34
|
+
|
|
35
|
+
:return: The active subscriptions on the persistent connection.
|
|
36
|
+
:rtype: Dict[str, Any]
|
|
37
|
+
"""
|
|
38
|
+
return self._manager._request_processor.active_subscriptions
|
|
39
|
+
|
|
40
|
+
async def make_request(self, method: RPCEndpoint, params: Any) -> RPCResponse:
|
|
41
|
+
"""
|
|
42
|
+
Make a request to the persistent connection and return the response. This method
|
|
43
|
+
does not process the response as it would when invoking a method via the
|
|
44
|
+
appropriate module on the `AsyncWeb3` instance,
|
|
45
|
+
e.g. `w3.eth.get_block("latest")`.
|
|
46
|
+
|
|
47
|
+
:param method: The RPC method, e.g. `eth_getBlockByNumber`.
|
|
48
|
+
:param params: The RPC method parameters, e.g. `["0x1337", False]`.
|
|
49
|
+
|
|
50
|
+
:return: The processed response from the persistent connection.
|
|
51
|
+
:rtype: RPCResponse
|
|
52
|
+
"""
|
|
53
|
+
return await self._manager.socket_request(method, params)
|
|
54
|
+
|
|
55
|
+
async def send(self, method: RPCEndpoint, params: Any) -> None:
|
|
56
|
+
"""
|
|
57
|
+
Send a raw, unprocessed message to the persistent connection.
|
|
58
|
+
|
|
59
|
+
:param method: The RPC method, e.g. `eth_getBlockByNumber`.
|
|
60
|
+
:param params: The RPC method parameters, e.g. `["0x1337", False]`.
|
|
61
|
+
|
|
62
|
+
:return: None
|
|
63
|
+
"""
|
|
64
|
+
await self._manager.send(method, params)
|
|
65
|
+
|
|
66
|
+
async def recv(self) -> RPCResponse:
|
|
67
|
+
"""
|
|
68
|
+
Receive the next unprocessed response for a request from the persistent
|
|
69
|
+
connection.
|
|
70
|
+
|
|
71
|
+
:return: The next unprocessed response for a request from the persistent
|
|
72
|
+
connection.
|
|
73
|
+
:rtype: RPCResponse
|
|
74
|
+
"""
|
|
75
|
+
return await self._manager.recv()
|
|
76
|
+
|
|
77
|
+
def process_subscriptions(self) -> "_AsyncPersistentMessageStream":
|
|
78
|
+
"""
|
|
79
|
+
Asynchronous iterator that yields messages from the subscription message stream.
|
|
80
|
+
|
|
81
|
+
:return: The subscription message stream.
|
|
82
|
+
:rtype: _AsyncPersistentMessageStream
|
|
83
|
+
"""
|
|
84
|
+
return self._manager._persistent_message_stream()
|