web3 7.0.0b2__py3-none-any.whl → 7.7.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 +4 -4
- ens/async_ens.py +27 -15
- ens/base_ens.py +3 -1
- ens/contract_data.py +2 -2
- ens/ens.py +10 -7
- ens/exceptions.py +16 -29
- ens/specs/nf.json +1 -1
- ens/specs/normalization_spec.json +1 -1
- ens/utils.py +24 -32
- web3/__init__.py +23 -12
- web3/_utils/abi.py +157 -263
- web3/_utils/async_transactions.py +34 -20
- web3/_utils/batching.py +217 -0
- web3/_utils/blocks.py +6 -2
- web3/_utils/caching/__init__.py +12 -0
- web3/_utils/caching/caching_utils.py +433 -0
- web3/_utils/caching/request_caching_validation.py +287 -0
- web3/_utils/compat/__init__.py +2 -3
- web3/_utils/contract_sources/compile_contracts.py +1 -1
- web3/_utils/contract_sources/contract_data/ambiguous_function_contract.py +42 -0
- 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 +50 -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 +172 -220
- web3/_utils/datatypes.py +5 -1
- web3/_utils/decorators.py +6 -1
- web3/_utils/empty.py +1 -1
- web3/_utils/encoding.py +16 -12
- web3/_utils/error_formatters_utils.py +5 -3
- web3/_utils/events.py +78 -72
- web3/_utils/fee_utils.py +1 -3
- web3/_utils/filters.py +24 -22
- web3/_utils/formatters.py +2 -2
- web3/_utils/http.py +8 -2
- web3/_utils/http_session_manager.py +314 -0
- web3/_utils/math.py +14 -15
- web3/_utils/method_formatters.py +161 -34
- web3/_utils/module.py +2 -1
- web3/_utils/module_testing/__init__.py +3 -2
- web3/_utils/module_testing/eth_module.py +736 -583
- web3/_utils/module_testing/go_ethereum_debug_module.py +128 -0
- web3/_utils/module_testing/module_testing_utils.py +81 -24
- web3/_utils/module_testing/persistent_connection_provider.py +702 -220
- web3/_utils/module_testing/utils.py +114 -33
- web3/_utils/module_testing/web3_module.py +438 -17
- web3/_utils/normalizers.py +13 -11
- web3/_utils/rpc_abi.py +10 -22
- web3/_utils/threads.py +8 -7
- web3/_utils/transactions.py +32 -25
- web3/_utils/type_conversion.py +5 -1
- web3/_utils/validation.py +20 -17
- web3/beacon/__init__.py +5 -0
- web3/beacon/api_endpoints.py +3 -0
- web3/beacon/async_beacon.py +29 -6
- web3/beacon/beacon.py +24 -6
- web3/contract/__init__.py +7 -0
- web3/contract/async_contract.py +285 -82
- web3/contract/base_contract.py +556 -258
- web3/contract/contract.py +295 -84
- web3/contract/utils.py +251 -55
- web3/datastructures.py +49 -34
- web3/eth/__init__.py +7 -0
- web3/eth/async_eth.py +89 -69
- web3/eth/base_eth.py +7 -3
- web3/eth/eth.py +43 -66
- web3/exceptions.py +158 -83
- web3/gas_strategies/time_based.py +8 -6
- web3/geth.py +53 -184
- web3/main.py +77 -17
- web3/manager.py +362 -95
- web3/method.py +43 -15
- web3/middleware/__init__.py +17 -0
- web3/middleware/attrdict.py +12 -22
- web3/middleware/base.py +55 -2
- web3/middleware/filter.py +45 -23
- web3/middleware/formatting.py +6 -3
- web3/middleware/names.py +4 -1
- web3/middleware/signing.py +15 -6
- web3/middleware/stalecheck.py +2 -1
- web3/module.py +61 -25
- web3/providers/__init__.py +21 -0
- web3/providers/async_base.py +87 -32
- web3/providers/base.py +77 -32
- web3/providers/eth_tester/__init__.py +5 -0
- web3/providers/eth_tester/defaults.py +2 -55
- web3/providers/eth_tester/main.py +41 -15
- web3/providers/eth_tester/middleware.py +16 -17
- web3/providers/ipc.py +41 -17
- web3/providers/legacy_websocket.py +26 -1
- web3/providers/persistent/__init__.py +7 -0
- web3/providers/persistent/async_ipc.py +61 -121
- web3/providers/persistent/persistent.py +323 -16
- web3/providers/persistent/persistent_connection.py +54 -5
- web3/providers/persistent/request_processor.py +136 -56
- web3/providers/persistent/subscription_container.py +56 -0
- web3/providers/persistent/subscription_manager.py +233 -0
- web3/providers/persistent/websocket.py +29 -92
- web3/providers/rpc/__init__.py +5 -0
- web3/providers/rpc/async_rpc.py +73 -18
- web3/providers/rpc/rpc.py +73 -30
- web3/providers/rpc/utils.py +1 -13
- web3/scripts/install_pre_releases.py +33 -0
- web3/scripts/parse_pygeth_version.py +16 -0
- web3/testing.py +4 -4
- web3/tracing.py +9 -5
- web3/types.py +141 -74
- web3/utils/__init__.py +64 -5
- web3/utils/abi.py +790 -10
- web3/utils/address.py +8 -0
- web3/utils/async_exception_handling.py +20 -11
- web3/utils/caching.py +34 -4
- web3/utils/exception_handling.py +9 -12
- web3/utils/subscriptions.py +285 -0
- {web3-7.0.0b2.dist-info → web3-7.7.0.dist-info}/LICENSE +1 -1
- web3-7.7.0.dist-info/METADATA +130 -0
- web3-7.7.0.dist-info/RECORD +171 -0
- {web3-7.0.0b2.dist-info → web3-7.7.0.dist-info}/WHEEL +1 -1
- web3/_utils/caching.py +0 -155
- web3/_utils/contract_sources/contract_data/address_reflector.py +0 -29
- web3/_utils/module_testing/go_ethereum_personal_module.py +0 -300
- web3/_utils/request.py +0 -265
- web3-7.0.0b2.dist-info/METADATA +0 -106
- web3-7.0.0b2.dist-info/RECORD +0 -163
- /web3/_utils/{function_identifiers.py → abi_element_identifiers.py} +0 -0
- {web3-7.0.0b2.dist-info → web3-7.7.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
from asyncio import (
|
|
2
|
+
iscoroutinefunction,
|
|
3
|
+
)
|
|
4
|
+
import collections
|
|
5
|
+
import hashlib
|
|
6
|
+
import threading
|
|
7
|
+
from typing import (
|
|
8
|
+
TYPE_CHECKING,
|
|
9
|
+
Any,
|
|
10
|
+
Callable,
|
|
11
|
+
Coroutine,
|
|
12
|
+
Dict,
|
|
13
|
+
List,
|
|
14
|
+
Sequence,
|
|
15
|
+
Tuple,
|
|
16
|
+
Union,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
from eth_typing import (
|
|
20
|
+
ChainId,
|
|
21
|
+
)
|
|
22
|
+
from eth_utils import (
|
|
23
|
+
is_boolean,
|
|
24
|
+
is_bytes,
|
|
25
|
+
is_dict,
|
|
26
|
+
is_list_like,
|
|
27
|
+
is_null,
|
|
28
|
+
is_number,
|
|
29
|
+
is_text,
|
|
30
|
+
to_bytes,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
from web3._utils.caching import (
|
|
34
|
+
ASYNC_PROVIDER_TYPE,
|
|
35
|
+
SYNC_PROVIDER_TYPE,
|
|
36
|
+
)
|
|
37
|
+
from web3._utils.caching.request_caching_validation import (
|
|
38
|
+
UNCACHEABLE_BLOCK_IDS,
|
|
39
|
+
always_cache_request,
|
|
40
|
+
async_validate_from_block_id_in_params,
|
|
41
|
+
async_validate_from_blockhash_in_params,
|
|
42
|
+
async_validate_from_blocknum_in_result,
|
|
43
|
+
validate_from_block_id_in_params,
|
|
44
|
+
validate_from_blockhash_in_params,
|
|
45
|
+
validate_from_blocknum_in_result,
|
|
46
|
+
)
|
|
47
|
+
from web3._utils.empty import (
|
|
48
|
+
empty,
|
|
49
|
+
)
|
|
50
|
+
from web3._utils.rpc_abi import (
|
|
51
|
+
RPC,
|
|
52
|
+
)
|
|
53
|
+
from web3.exceptions import (
|
|
54
|
+
Web3TypeError,
|
|
55
|
+
)
|
|
56
|
+
from web3.types import (
|
|
57
|
+
RPCEndpoint,
|
|
58
|
+
)
|
|
59
|
+
from web3.utils import (
|
|
60
|
+
RequestCacheValidationThreshold,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
if TYPE_CHECKING:
|
|
64
|
+
from web3.providers import ( # noqa: F401
|
|
65
|
+
AsyncBaseProvider,
|
|
66
|
+
BaseProvider,
|
|
67
|
+
PersistentConnectionProvider,
|
|
68
|
+
)
|
|
69
|
+
from web3.types import ( # noqa: F401
|
|
70
|
+
AsyncMakeRequestFn,
|
|
71
|
+
MakeRequestFn,
|
|
72
|
+
RPCRequest,
|
|
73
|
+
RPCResponse,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def generate_cache_key(value: Any) -> str:
|
|
78
|
+
"""
|
|
79
|
+
Generates a cache key for the *args and **kwargs
|
|
80
|
+
"""
|
|
81
|
+
if is_bytes(value):
|
|
82
|
+
return hashlib.md5(value).hexdigest()
|
|
83
|
+
elif is_text(value):
|
|
84
|
+
return generate_cache_key(to_bytes(text=value))
|
|
85
|
+
elif is_boolean(value) or is_null(value) or is_number(value):
|
|
86
|
+
return generate_cache_key(repr(value))
|
|
87
|
+
elif is_dict(value):
|
|
88
|
+
return generate_cache_key((key, value[key]) for key in sorted(value.keys()))
|
|
89
|
+
elif is_list_like(value) or isinstance(value, collections.abc.Generator):
|
|
90
|
+
return generate_cache_key("".join(generate_cache_key(item) for item in value))
|
|
91
|
+
else:
|
|
92
|
+
raise Web3TypeError(
|
|
93
|
+
f"Cannot generate cache key for value {value} of type {type(value)}"
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class RequestInformation:
|
|
98
|
+
def __init__(
|
|
99
|
+
self,
|
|
100
|
+
method: RPCEndpoint,
|
|
101
|
+
params: Any,
|
|
102
|
+
response_formatters: Tuple[
|
|
103
|
+
Union[Dict[str, Callable[..., Any]], Callable[..., Any]],
|
|
104
|
+
Callable[..., Any],
|
|
105
|
+
Callable[..., Any],
|
|
106
|
+
],
|
|
107
|
+
subscription_id: str = None,
|
|
108
|
+
):
|
|
109
|
+
self.method = method
|
|
110
|
+
self.params = params
|
|
111
|
+
self.response_formatters = response_formatters
|
|
112
|
+
self.subscription_id = subscription_id
|
|
113
|
+
self.middleware_response_processors: List[Callable[..., Any]] = []
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
DEFAULT_VALIDATION_THRESHOLD = 60 * 60 # 1 hour
|
|
117
|
+
|
|
118
|
+
CHAIN_VALIDATION_THRESHOLD_DEFAULTS: Dict[
|
|
119
|
+
int, Union[RequestCacheValidationThreshold, int]
|
|
120
|
+
] = {
|
|
121
|
+
# Suggested safe values as defaults for each chain. Users can configure a different
|
|
122
|
+
# value if desired.
|
|
123
|
+
ChainId.ETH.value: RequestCacheValidationThreshold.FINALIZED,
|
|
124
|
+
ChainId.ARB1.value: 7 * 24 * 60 * 60, # 7 days
|
|
125
|
+
ChainId.ZKSYNC.value: 60 * 60, # 1 hour
|
|
126
|
+
ChainId.OETH.value: 3 * 60, # 3 minutes
|
|
127
|
+
ChainId.MATIC.value: 30 * 60, # 30 minutes
|
|
128
|
+
ChainId.ZKEVM.value: 60 * 60, # 1 hour
|
|
129
|
+
ChainId.BASE.value: 7 * 24 * 60 * 60, # 7 days
|
|
130
|
+
ChainId.SCR.value: 60 * 60, # 1 hour
|
|
131
|
+
ChainId.GNO.value: 5 * 60, # 5 minutes
|
|
132
|
+
ChainId.AVAX.value: 2 * 60, # 2 minutes
|
|
133
|
+
ChainId.BNB.value: 2 * 60, # 2 minutes
|
|
134
|
+
ChainId.FTM.value: 60, # 1 minute
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def is_cacheable_request(
|
|
139
|
+
provider: Union[ASYNC_PROVIDER_TYPE, SYNC_PROVIDER_TYPE],
|
|
140
|
+
method: RPCEndpoint,
|
|
141
|
+
params: Any,
|
|
142
|
+
) -> bool:
|
|
143
|
+
if not (provider.cache_allowed_requests and method in provider.cacheable_requests):
|
|
144
|
+
return False
|
|
145
|
+
elif method in BLOCKNUM_IN_PARAMS:
|
|
146
|
+
block_id = params[0]
|
|
147
|
+
if block_id in UNCACHEABLE_BLOCK_IDS:
|
|
148
|
+
return False
|
|
149
|
+
return True
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
# -- request caching -- #
|
|
153
|
+
|
|
154
|
+
ALWAYS_CACHE = {
|
|
155
|
+
RPC.eth_chainId,
|
|
156
|
+
RPC.web3_clientVersion,
|
|
157
|
+
RPC.net_version,
|
|
158
|
+
}
|
|
159
|
+
BLOCKNUM_IN_PARAMS = {
|
|
160
|
+
RPC.eth_getBlockByNumber,
|
|
161
|
+
RPC.eth_getRawTransactionByBlockNumberAndIndex,
|
|
162
|
+
RPC.eth_getBlockTransactionCountByNumber,
|
|
163
|
+
RPC.eth_getUncleByBlockNumberAndIndex,
|
|
164
|
+
RPC.eth_getUncleCountByBlockNumber,
|
|
165
|
+
}
|
|
166
|
+
BLOCK_IN_RESULT = {
|
|
167
|
+
RPC.eth_getBlockByHash,
|
|
168
|
+
RPC.eth_getTransactionByHash,
|
|
169
|
+
RPC.eth_getTransactionByBlockNumberAndIndex,
|
|
170
|
+
RPC.eth_getTransactionByBlockHashAndIndex,
|
|
171
|
+
RPC.eth_getBlockTransactionCountByHash,
|
|
172
|
+
}
|
|
173
|
+
BLOCKHASH_IN_PARAMS = {
|
|
174
|
+
RPC.eth_getRawTransactionByBlockHashAndIndex,
|
|
175
|
+
RPC.eth_getUncleByBlockHashAndIndex,
|
|
176
|
+
RPC.eth_getUncleCountByBlockHash,
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
INTERNAL_VALIDATION_MAP: Dict[
|
|
180
|
+
RPCEndpoint,
|
|
181
|
+
Callable[
|
|
182
|
+
[SYNC_PROVIDER_TYPE, Sequence[Any], Dict[str, Any]],
|
|
183
|
+
bool,
|
|
184
|
+
],
|
|
185
|
+
] = {
|
|
186
|
+
**{endpoint: always_cache_request for endpoint in ALWAYS_CACHE},
|
|
187
|
+
**{endpoint: validate_from_block_id_in_params for endpoint in BLOCKNUM_IN_PARAMS},
|
|
188
|
+
**{endpoint: validate_from_blocknum_in_result for endpoint in BLOCK_IN_RESULT},
|
|
189
|
+
**{endpoint: validate_from_blockhash_in_params for endpoint in BLOCKHASH_IN_PARAMS},
|
|
190
|
+
}
|
|
191
|
+
CACHEABLE_REQUESTS = tuple(INTERNAL_VALIDATION_MAP.keys())
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def set_threshold_if_empty(provider: SYNC_PROVIDER_TYPE) -> None:
|
|
195
|
+
current_threshold = provider.request_cache_validation_threshold
|
|
196
|
+
|
|
197
|
+
if current_threshold is empty or isinstance(
|
|
198
|
+
current_threshold, RequestCacheValidationThreshold
|
|
199
|
+
):
|
|
200
|
+
cache_allowed_requests = provider.cache_allowed_requests
|
|
201
|
+
try:
|
|
202
|
+
# turn off momentarily to avoid recursion
|
|
203
|
+
provider.cache_allowed_requests = False
|
|
204
|
+
chain_id_result = provider.make_request(RPCEndpoint("eth_chainId"), [])[
|
|
205
|
+
"result"
|
|
206
|
+
]
|
|
207
|
+
chain_id = int(chain_id_result, 16)
|
|
208
|
+
|
|
209
|
+
if current_threshold is empty:
|
|
210
|
+
provider.request_cache_validation_threshold = (
|
|
211
|
+
CHAIN_VALIDATION_THRESHOLD_DEFAULTS.get(
|
|
212
|
+
chain_id, DEFAULT_VALIDATION_THRESHOLD
|
|
213
|
+
)
|
|
214
|
+
)
|
|
215
|
+
except Exception:
|
|
216
|
+
provider.request_cache_validation_threshold = DEFAULT_VALIDATION_THRESHOLD
|
|
217
|
+
finally:
|
|
218
|
+
provider.cache_allowed_requests = cache_allowed_requests
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def _should_cache_response(
|
|
222
|
+
provider: SYNC_PROVIDER_TYPE,
|
|
223
|
+
method: RPCEndpoint,
|
|
224
|
+
params: Sequence[Any],
|
|
225
|
+
response: "RPCResponse",
|
|
226
|
+
) -> bool:
|
|
227
|
+
result = response.get("result", None)
|
|
228
|
+
if "error" in response or is_null(result):
|
|
229
|
+
return False
|
|
230
|
+
|
|
231
|
+
set_threshold_if_empty(provider)
|
|
232
|
+
if (
|
|
233
|
+
method in INTERNAL_VALIDATION_MAP
|
|
234
|
+
and provider.request_cache_validation_threshold is not None
|
|
235
|
+
):
|
|
236
|
+
return INTERNAL_VALIDATION_MAP[method](provider, params, result)
|
|
237
|
+
return True
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def handle_request_caching(
|
|
241
|
+
func: Callable[[SYNC_PROVIDER_TYPE, RPCEndpoint, Any], "RPCResponse"]
|
|
242
|
+
) -> Callable[..., "RPCResponse"]:
|
|
243
|
+
def wrapper(
|
|
244
|
+
provider: SYNC_PROVIDER_TYPE, method: RPCEndpoint, params: Any
|
|
245
|
+
) -> "RPCResponse":
|
|
246
|
+
if is_cacheable_request(provider, method, params):
|
|
247
|
+
request_cache = provider._request_cache
|
|
248
|
+
cache_key = generate_cache_key(
|
|
249
|
+
f"{threading.get_ident()}:{(method, params)}"
|
|
250
|
+
)
|
|
251
|
+
cache_result = request_cache.get_cache_entry(cache_key)
|
|
252
|
+
if cache_result is not None:
|
|
253
|
+
return cache_result
|
|
254
|
+
else:
|
|
255
|
+
response = func(provider, method, params)
|
|
256
|
+
if _should_cache_response(provider, method, params, response):
|
|
257
|
+
with provider._request_cache_lock:
|
|
258
|
+
request_cache.cache(cache_key, response)
|
|
259
|
+
return response
|
|
260
|
+
else:
|
|
261
|
+
return func(provider, method, params)
|
|
262
|
+
|
|
263
|
+
# save a reference to the decorator on the wrapped function
|
|
264
|
+
wrapper._decorator = handle_request_caching # type: ignore
|
|
265
|
+
return wrapper
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
# -- async -- #
|
|
269
|
+
|
|
270
|
+
ASYNC_VALIDATOR_TYPE = Callable[
|
|
271
|
+
["AsyncBaseProvider", Sequence[Any], Dict[str, Any]],
|
|
272
|
+
Union[bool, Coroutine[Any, Any, bool]],
|
|
273
|
+
]
|
|
274
|
+
|
|
275
|
+
ASYNC_INTERNAL_VALIDATION_MAP: Dict[RPCEndpoint, ASYNC_VALIDATOR_TYPE] = {
|
|
276
|
+
**{endpoint: always_cache_request for endpoint in ALWAYS_CACHE},
|
|
277
|
+
**{
|
|
278
|
+
endpoint: async_validate_from_block_id_in_params
|
|
279
|
+
for endpoint in BLOCKNUM_IN_PARAMS
|
|
280
|
+
},
|
|
281
|
+
**{
|
|
282
|
+
endpoint: async_validate_from_blocknum_in_result for endpoint in BLOCK_IN_RESULT
|
|
283
|
+
},
|
|
284
|
+
**{
|
|
285
|
+
endpoint: async_validate_from_blockhash_in_params
|
|
286
|
+
for endpoint in BLOCKHASH_IN_PARAMS
|
|
287
|
+
},
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
async def async_set_threshold_if_empty(provider: ASYNC_PROVIDER_TYPE) -> None:
|
|
292
|
+
current_threshold = provider.request_cache_validation_threshold
|
|
293
|
+
|
|
294
|
+
if current_threshold is empty or isinstance(
|
|
295
|
+
current_threshold, RequestCacheValidationThreshold
|
|
296
|
+
):
|
|
297
|
+
cache_allowed_requests = provider.cache_allowed_requests
|
|
298
|
+
try:
|
|
299
|
+
# turn off momentarily to avoid recursion
|
|
300
|
+
provider.cache_allowed_requests = False
|
|
301
|
+
chain_id_result = await provider.make_request(
|
|
302
|
+
RPCEndpoint("eth_chainId"), []
|
|
303
|
+
)
|
|
304
|
+
chain_id = int(chain_id_result["result"], 16)
|
|
305
|
+
|
|
306
|
+
if current_threshold is empty:
|
|
307
|
+
provider.request_cache_validation_threshold = (
|
|
308
|
+
CHAIN_VALIDATION_THRESHOLD_DEFAULTS.get(
|
|
309
|
+
chain_id, DEFAULT_VALIDATION_THRESHOLD
|
|
310
|
+
)
|
|
311
|
+
)
|
|
312
|
+
except Exception:
|
|
313
|
+
provider.request_cache_validation_threshold = DEFAULT_VALIDATION_THRESHOLD
|
|
314
|
+
finally:
|
|
315
|
+
provider.cache_allowed_requests = cache_allowed_requests
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
async def _async_should_cache_response(
|
|
319
|
+
provider: ASYNC_PROVIDER_TYPE,
|
|
320
|
+
method: RPCEndpoint,
|
|
321
|
+
params: Sequence[Any],
|
|
322
|
+
response: "RPCResponse",
|
|
323
|
+
) -> bool:
|
|
324
|
+
result = response.get("result", None)
|
|
325
|
+
if "error" in response or is_null(result):
|
|
326
|
+
return False
|
|
327
|
+
|
|
328
|
+
await async_set_threshold_if_empty(provider)
|
|
329
|
+
if (
|
|
330
|
+
method in ASYNC_INTERNAL_VALIDATION_MAP
|
|
331
|
+
and provider.request_cache_validation_threshold is not None
|
|
332
|
+
):
|
|
333
|
+
cache_validator = ASYNC_INTERNAL_VALIDATION_MAP[method]
|
|
334
|
+
return (
|
|
335
|
+
await cache_validator(provider, params, result)
|
|
336
|
+
if iscoroutinefunction(cache_validator)
|
|
337
|
+
else cache_validator(provider, params, result)
|
|
338
|
+
)
|
|
339
|
+
return True
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
def async_handle_request_caching(
|
|
343
|
+
func: Callable[
|
|
344
|
+
[ASYNC_PROVIDER_TYPE, RPCEndpoint, Any], Coroutine[Any, Any, "RPCResponse"]
|
|
345
|
+
],
|
|
346
|
+
) -> Callable[..., Coroutine[Any, Any, "RPCResponse"]]:
|
|
347
|
+
async def wrapper(
|
|
348
|
+
provider: ASYNC_PROVIDER_TYPE, method: RPCEndpoint, params: Any
|
|
349
|
+
) -> "RPCResponse":
|
|
350
|
+
if is_cacheable_request(provider, method, params):
|
|
351
|
+
request_cache = provider._request_cache
|
|
352
|
+
cache_key = generate_cache_key(
|
|
353
|
+
f"{threading.get_ident()}:{(method, params)}"
|
|
354
|
+
)
|
|
355
|
+
cache_result = request_cache.get_cache_entry(cache_key)
|
|
356
|
+
if cache_result is not None:
|
|
357
|
+
return cache_result
|
|
358
|
+
else:
|
|
359
|
+
response = await func(provider, method, params)
|
|
360
|
+
if await _async_should_cache_response(
|
|
361
|
+
provider, method, params, response
|
|
362
|
+
):
|
|
363
|
+
async with provider._request_cache_lock:
|
|
364
|
+
request_cache.cache(cache_key, response)
|
|
365
|
+
return response
|
|
366
|
+
else:
|
|
367
|
+
return await func(provider, method, params)
|
|
368
|
+
|
|
369
|
+
# save a reference to the decorator on the wrapped function
|
|
370
|
+
wrapper._decorator = async_handle_request_caching # type: ignore
|
|
371
|
+
return wrapper
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
def async_handle_send_caching(
|
|
375
|
+
func: Callable[
|
|
376
|
+
[ASYNC_PROVIDER_TYPE, RPCEndpoint, Any],
|
|
377
|
+
Coroutine[Any, Any, "RPCRequest"],
|
|
378
|
+
],
|
|
379
|
+
) -> Callable[..., Coroutine[Any, Any, "RPCRequest"]]:
|
|
380
|
+
async def wrapper(
|
|
381
|
+
provider: ASYNC_PROVIDER_TYPE, method: RPCEndpoint, params: Any
|
|
382
|
+
) -> "RPCRequest":
|
|
383
|
+
if is_cacheable_request(provider, method, params):
|
|
384
|
+
request_cache = provider._request_cache
|
|
385
|
+
cache_key = generate_cache_key(
|
|
386
|
+
f"{threading.get_ident()}:{(method, params)}"
|
|
387
|
+
)
|
|
388
|
+
cached_response = request_cache.get_cache_entry(cache_key)
|
|
389
|
+
if cached_response is not None:
|
|
390
|
+
# The request data isn't used, this just prevents a cached request from
|
|
391
|
+
# being sent - return an empty request object
|
|
392
|
+
return {"id": -1, "method": RPCEndpoint(""), "params": []}
|
|
393
|
+
return await func(provider, method, params)
|
|
394
|
+
|
|
395
|
+
# save a reference to the decorator on the wrapped function
|
|
396
|
+
wrapper._decorator = async_handle_send_caching # type: ignore
|
|
397
|
+
return wrapper
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
def async_handle_recv_caching(
|
|
401
|
+
func: Callable[
|
|
402
|
+
["PersistentConnectionProvider", "RPCRequest"],
|
|
403
|
+
Coroutine[Any, Any, "RPCResponse"],
|
|
404
|
+
]
|
|
405
|
+
) -> Callable[..., Coroutine[Any, Any, "RPCResponse"]]:
|
|
406
|
+
async def wrapper(
|
|
407
|
+
provider: "PersistentConnectionProvider",
|
|
408
|
+
rpc_request: "RPCRequest",
|
|
409
|
+
) -> "RPCResponse":
|
|
410
|
+
method = rpc_request["method"]
|
|
411
|
+
params = rpc_request["params"]
|
|
412
|
+
if is_cacheable_request(provider, method, params):
|
|
413
|
+
request_cache = provider._request_cache
|
|
414
|
+
cache_key = generate_cache_key(
|
|
415
|
+
f"{threading.get_ident()}:{(method, params)}"
|
|
416
|
+
)
|
|
417
|
+
cache_result = request_cache.get_cache_entry(cache_key)
|
|
418
|
+
if cache_result is not None:
|
|
419
|
+
return cache_result
|
|
420
|
+
else:
|
|
421
|
+
response = await func(provider, rpc_request)
|
|
422
|
+
if await _async_should_cache_response(
|
|
423
|
+
provider, method, params, response
|
|
424
|
+
):
|
|
425
|
+
async with provider._request_cache_lock:
|
|
426
|
+
request_cache.cache(cache_key, response)
|
|
427
|
+
return response
|
|
428
|
+
else:
|
|
429
|
+
return await func(provider, rpc_request)
|
|
430
|
+
|
|
431
|
+
# save a reference to the decorator on the wrapped function
|
|
432
|
+
wrapper._decorator = async_handle_recv_caching # type: ignore
|
|
433
|
+
return wrapper
|