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
|
@@ -1,13 +1,23 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
from dataclasses import (
|
|
3
|
+
dataclass,
|
|
4
|
+
)
|
|
2
5
|
import pytest
|
|
3
6
|
from typing import (
|
|
4
7
|
TYPE_CHECKING,
|
|
5
8
|
Any,
|
|
6
9
|
Dict,
|
|
10
|
+
Generator,
|
|
11
|
+
List,
|
|
7
12
|
Tuple,
|
|
13
|
+
Union,
|
|
8
14
|
cast,
|
|
9
15
|
)
|
|
10
16
|
|
|
17
|
+
from eth_typing import (
|
|
18
|
+
ChecksumAddress,
|
|
19
|
+
HexStr,
|
|
20
|
+
)
|
|
11
21
|
from eth_utils import (
|
|
12
22
|
is_hexstr,
|
|
13
23
|
)
|
|
@@ -15,6 +25,13 @@ from hexbytes import (
|
|
|
15
25
|
HexBytes,
|
|
16
26
|
)
|
|
17
27
|
|
|
28
|
+
from web3 import (
|
|
29
|
+
AsyncWeb3,
|
|
30
|
+
PersistentConnectionProvider,
|
|
31
|
+
)
|
|
32
|
+
from web3.beacon import (
|
|
33
|
+
AsyncBeacon,
|
|
34
|
+
)
|
|
18
35
|
from web3.datastructures import (
|
|
19
36
|
AttributeDict,
|
|
20
37
|
)
|
|
@@ -22,214 +39,226 @@ from web3.middleware import (
|
|
|
22
39
|
ExtraDataToPOAMiddleware,
|
|
23
40
|
)
|
|
24
41
|
from web3.types import (
|
|
42
|
+
BlockData,
|
|
25
43
|
FormattedEthSubscriptionResponse,
|
|
44
|
+
LogReceipt,
|
|
45
|
+
Nonce,
|
|
46
|
+
RPCEndpoint,
|
|
47
|
+
TxData,
|
|
48
|
+
Wei,
|
|
49
|
+
)
|
|
50
|
+
from web3.utils import (
|
|
51
|
+
EthSubscription,
|
|
52
|
+
)
|
|
53
|
+
from web3.utils.subscriptions import (
|
|
54
|
+
LogsSubscription,
|
|
55
|
+
LogsSubscriptionContext,
|
|
56
|
+
NewHeadsSubscription,
|
|
57
|
+
NewHeadsSubscriptionContext,
|
|
58
|
+
PendingTxSubscription,
|
|
59
|
+
PendingTxSubscriptionContext,
|
|
26
60
|
)
|
|
27
61
|
|
|
28
62
|
if TYPE_CHECKING:
|
|
29
|
-
from web3.
|
|
30
|
-
|
|
63
|
+
from web3.contract.async_contract import (
|
|
64
|
+
AsyncContract,
|
|
65
|
+
AsyncContractFunction,
|
|
66
|
+
)
|
|
67
|
+
from web3.providers.persistent.subscription_manager import (
|
|
68
|
+
SubscriptionContainer,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
# LogIndexedAndNotIndexed event args
|
|
73
|
+
INDEXED_ADDR = "0xdEad000000000000000000000000000000000000"
|
|
74
|
+
INDEXED_UINT256 = 1337
|
|
75
|
+
NON_INDEXED_ADDR = "0xbeeF000000000000000000000000000000000000"
|
|
76
|
+
NON_INDEXED_UINT256 = 1999
|
|
77
|
+
NON_INDEXED_STRING = "test logs subscriptions"
|
|
78
|
+
|
|
79
|
+
SOME_BLOCK_KEYS = [
|
|
80
|
+
"number",
|
|
81
|
+
"hash",
|
|
82
|
+
"parentHash",
|
|
83
|
+
"transactionsRoot",
|
|
84
|
+
"stateRoot",
|
|
85
|
+
"receiptsRoot",
|
|
86
|
+
"gasLimit",
|
|
87
|
+
"gasUsed",
|
|
88
|
+
"timestamp",
|
|
89
|
+
"baseFeePerGas",
|
|
90
|
+
"withdrawalsRoot",
|
|
91
|
+
]
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@dataclass
|
|
95
|
+
class SubscriptionHandlerTest:
|
|
96
|
+
passed: bool = False
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
async def new_heads_handler(
|
|
100
|
+
handler_context: NewHeadsSubscriptionContext,
|
|
101
|
+
) -> None:
|
|
102
|
+
w3 = handler_context.async_w3
|
|
103
|
+
sub = handler_context.subscription
|
|
104
|
+
assert isinstance(w3, AsyncWeb3)
|
|
105
|
+
provider = cast(PersistentConnectionProvider, w3.provider)
|
|
106
|
+
assert isinstance(provider.get_endpoint_uri_or_ipc_path(), str)
|
|
107
|
+
|
|
108
|
+
assert isinstance(sub, EthSubscription)
|
|
109
|
+
|
|
110
|
+
block = handler_context.result
|
|
111
|
+
assert block is not None
|
|
112
|
+
assert all(k in block.keys() for k in SOME_BLOCK_KEYS)
|
|
113
|
+
|
|
114
|
+
assert handler_context.new_heads_handler_test.passed is False
|
|
115
|
+
handler_context.new_heads_handler_test.passed = True
|
|
116
|
+
assert await sub.unsubscribe()
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
async def pending_tx_handler(
|
|
120
|
+
handler_context: PendingTxSubscriptionContext,
|
|
121
|
+
) -> None:
|
|
122
|
+
w3 = handler_context.async_w3
|
|
123
|
+
sub = handler_context.subscription
|
|
124
|
+
tx = handler_context.result
|
|
125
|
+
|
|
126
|
+
assert w3 is not None
|
|
127
|
+
provider = cast(PersistentConnectionProvider, w3.provider)
|
|
128
|
+
assert isinstance(provider.get_endpoint_uri_or_ipc_path(), str)
|
|
129
|
+
|
|
130
|
+
assert isinstance(sub, PendingTxSubscription)
|
|
131
|
+
|
|
132
|
+
assert tx is not None
|
|
133
|
+
tx = cast(TxData, tx)
|
|
134
|
+
accts = await w3.eth.accounts
|
|
135
|
+
assert tx["from"] == accts[0]
|
|
136
|
+
await w3.eth.wait_for_transaction_receipt(tx["hash"])
|
|
137
|
+
|
|
138
|
+
assert handler_context.pending_tx_handler_test.passed is False
|
|
139
|
+
handler_context.pending_tx_handler_test.passed = True
|
|
140
|
+
assert await sub.unsubscribe()
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
async def logs_handler(
|
|
144
|
+
handler_context: LogsSubscriptionContext,
|
|
145
|
+
) -> None:
|
|
146
|
+
w3 = handler_context.async_w3
|
|
147
|
+
sub = handler_context.subscription
|
|
148
|
+
log_receipt = handler_context.result
|
|
149
|
+
|
|
150
|
+
assert w3 is not None
|
|
151
|
+
provider = cast(PersistentConnectionProvider, w3.provider)
|
|
152
|
+
assert isinstance(provider.get_endpoint_uri_or_ipc_path(), str)
|
|
153
|
+
|
|
154
|
+
assert isinstance(sub, LogsSubscription)
|
|
155
|
+
event_data = handler_context.event.process_log(log_receipt)
|
|
156
|
+
assert event_data.args.indexedAddress == INDEXED_ADDR
|
|
157
|
+
assert event_data.args.indexedUint256 == INDEXED_UINT256
|
|
158
|
+
assert event_data.args.nonIndexedAddress == NON_INDEXED_ADDR
|
|
159
|
+
assert event_data.args.nonIndexedUint256 == NON_INDEXED_UINT256
|
|
160
|
+
assert event_data.args.nonIndexedString == NON_INDEXED_STRING
|
|
161
|
+
|
|
162
|
+
assert handler_context.logs_handler_test.passed is False
|
|
163
|
+
handler_context.logs_handler_test.passed = True
|
|
164
|
+
assert await sub.unsubscribe()
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
async def idle_handler(
|
|
168
|
+
_handler_context: Any,
|
|
169
|
+
) -> None:
|
|
170
|
+
pass
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
async def emit_contract_event(
|
|
174
|
+
async_w3: AsyncWeb3,
|
|
175
|
+
acct: ChecksumAddress,
|
|
176
|
+
contract_function: "AsyncContractFunction",
|
|
177
|
+
args: Any = (),
|
|
178
|
+
delay: float = 0.25,
|
|
179
|
+
) -> None:
|
|
180
|
+
await asyncio.sleep(delay)
|
|
181
|
+
tx_hash = await contract_function(*args).transact({"from": acct})
|
|
182
|
+
receipt = await async_w3.eth.wait_for_transaction_receipt(tx_hash)
|
|
183
|
+
assert receipt["status"] == 1
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
async def log_indexed_and_non_indexed_args_task(
|
|
187
|
+
async_w3: AsyncWeb3,
|
|
188
|
+
async_emitter_contract: "AsyncContract",
|
|
189
|
+
acct: ChecksumAddress,
|
|
190
|
+
delay: float = 0.1,
|
|
191
|
+
) -> "asyncio.Task[None]":
|
|
192
|
+
return asyncio.create_task(
|
|
193
|
+
emit_contract_event(
|
|
194
|
+
async_w3,
|
|
195
|
+
acct,
|
|
196
|
+
async_emitter_contract.functions.logIndexedAndNotIndexedArgs,
|
|
197
|
+
args=(
|
|
198
|
+
INDEXED_ADDR,
|
|
199
|
+
INDEXED_UINT256,
|
|
200
|
+
NON_INDEXED_ADDR,
|
|
201
|
+
NON_INDEXED_UINT256,
|
|
202
|
+
NON_INDEXED_STRING,
|
|
203
|
+
),
|
|
204
|
+
delay=delay,
|
|
205
|
+
)
|
|
31
206
|
)
|
|
32
207
|
|
|
33
208
|
|
|
209
|
+
def assert_no_subscriptions_left(sub_container: "SubscriptionContainer") -> None:
|
|
210
|
+
assert len(sub_container) == 0
|
|
211
|
+
assert len(sub_container.subscriptions) == 0
|
|
212
|
+
assert len(sub_container.subscriptions_by_id) == 0
|
|
213
|
+
assert len(sub_container.subscriptions_by_label) == 0
|
|
214
|
+
assert len(sub_container.handler_subscriptions) == 0
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
async def clean_up_task(task: "asyncio.Task[Any]") -> None:
|
|
218
|
+
task.cancel()
|
|
219
|
+
try:
|
|
220
|
+
await task
|
|
221
|
+
except asyncio.CancelledError:
|
|
222
|
+
pass
|
|
223
|
+
|
|
224
|
+
|
|
34
225
|
class PersistentConnectionProviderTest:
|
|
226
|
+
@pytest.fixture(autouse=True)
|
|
227
|
+
def clear_caches(self, async_w3: AsyncWeb3) -> Generator[None, None, None]:
|
|
228
|
+
yield
|
|
229
|
+
async_w3.provider._request_processor.clear_caches()
|
|
230
|
+
async_w3.subscription_manager.total_handler_calls = 0
|
|
231
|
+
|
|
232
|
+
@staticmethod
|
|
233
|
+
async def seed_transactions_to_geth(
|
|
234
|
+
async_w3: AsyncWeb3,
|
|
235
|
+
acct: ChecksumAddress,
|
|
236
|
+
num_txs: int = 1,
|
|
237
|
+
delay: float = 0.1,
|
|
238
|
+
) -> None:
|
|
239
|
+
nonce = int(await async_w3.eth.get_transaction_count(acct))
|
|
240
|
+
|
|
241
|
+
async def send_tx() -> None:
|
|
242
|
+
nonlocal nonce
|
|
243
|
+
await async_w3.eth.send_transaction(
|
|
244
|
+
{
|
|
245
|
+
"from": acct,
|
|
246
|
+
"to": acct,
|
|
247
|
+
"value": Wei(nonce),
|
|
248
|
+
"gas": 21000,
|
|
249
|
+
"nonce": Nonce(nonce),
|
|
250
|
+
}
|
|
251
|
+
)
|
|
252
|
+
nonce += 1
|
|
253
|
+
|
|
254
|
+
for _ in range(num_txs):
|
|
255
|
+
await asyncio.sleep(delay)
|
|
256
|
+
await send_tx()
|
|
257
|
+
|
|
35
258
|
@pytest.mark.asyncio
|
|
36
259
|
@pytest.mark.parametrize(
|
|
37
260
|
"subscription_params,ws_subscription_response,expected_formatted_result",
|
|
38
261
|
(
|
|
39
|
-
(
|
|
40
|
-
("newHeads",),
|
|
41
|
-
{
|
|
42
|
-
"jsonrpc": "2.0",
|
|
43
|
-
"method": "eth_subscription",
|
|
44
|
-
"params": {
|
|
45
|
-
"subscription": "THIS_WILL_BE_REPLACED_IN_THE_TEST",
|
|
46
|
-
"result": {
|
|
47
|
-
"number": "0x539",
|
|
48
|
-
"hash": "0xb46b85928f2c2264c2bf7ad5c6d6985664f1527e744193ef990cc0d3da5afc5e", # noqa: E501
|
|
49
|
-
"parentHash": "0xb46b85928f2c2264c2bf7ad5c6d6985664f1527e744193ef990cc0d3da5afc5e", # noqa: E501
|
|
50
|
-
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", # noqa: E501
|
|
51
|
-
"logsBloom": "0x00",
|
|
52
|
-
"transactionsRoot": "0x56260fe8298aff6d360e3a68fa855693f25dcb2708d8a7e509e8519b265d3988", # noqa: E501
|
|
53
|
-
"stateRoot": "0x56260fe8298aff6d360e3a68fa855693f25dcb2708d8a7e509e8519b265d3988", # noqa: E501
|
|
54
|
-
"receiptsRoot": "0x56260fe8298aff6d360e3a68fa855693f25dcb2708d8a7e509e8519b265d3988", # noqa: E501
|
|
55
|
-
"miner": "0x0000000000000000000000000000000000000000",
|
|
56
|
-
"difficulty": "0x0",
|
|
57
|
-
"extraData": "0x496c6c756d696e61746520446d6f63726174697a6520447374726962757465", # noqa: E501
|
|
58
|
-
"gasLimit": "0x1c9c380",
|
|
59
|
-
"gasUsed": "0xd1ce44",
|
|
60
|
-
"timestamp": "0x539",
|
|
61
|
-
"baseFeePerGas": "0x26f93fef9",
|
|
62
|
-
"withdrawalsRoot": "0x56260fe8298aff6d360e3a68fa855693f25dcb2708d8a7e509e8519b265d3988", # noqa: E501
|
|
63
|
-
"nonce": "0x0000000000000000",
|
|
64
|
-
"mixHash": "0x73e9e036ec894047f29954571d4b6d9e8717de7304269c263cbf150caa4e0768", # noqa: E501
|
|
65
|
-
},
|
|
66
|
-
},
|
|
67
|
-
},
|
|
68
|
-
AttributeDict(
|
|
69
|
-
{
|
|
70
|
-
"number": 1337,
|
|
71
|
-
"hash": HexBytes(
|
|
72
|
-
"0xb46b85928f2c2264c2bf7ad5c6d6985664f1527e744193ef990cc0d3da5afc5e" # noqa: E501
|
|
73
|
-
),
|
|
74
|
-
"parentHash": HexBytes(
|
|
75
|
-
"0xb46b85928f2c2264c2bf7ad5c6d6985664f1527e744193ef990cc0d3da5afc5e" # noqa: E501
|
|
76
|
-
),
|
|
77
|
-
"sha3Uncles": HexBytes(
|
|
78
|
-
"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" # noqa: E501
|
|
79
|
-
),
|
|
80
|
-
"logsBloom": HexBytes("0x00"),
|
|
81
|
-
"transactionsRoot": HexBytes(
|
|
82
|
-
"0x56260fe8298aff6d360e3a68fa855693f25dcb2708d8a7e509e8519b265d3988" # noqa: E501
|
|
83
|
-
),
|
|
84
|
-
"stateRoot": HexBytes(
|
|
85
|
-
"0x56260fe8298aff6d360e3a68fa855693f25dcb2708d8a7e509e8519b265d3988" # noqa: E501
|
|
86
|
-
),
|
|
87
|
-
"receiptsRoot": HexBytes(
|
|
88
|
-
"0x56260fe8298aff6d360e3a68fa855693f25dcb2708d8a7e509e8519b265d3988" # noqa: E501
|
|
89
|
-
),
|
|
90
|
-
"miner": "0x0000000000000000000000000000000000000000",
|
|
91
|
-
"difficulty": 0,
|
|
92
|
-
"extraData": HexBytes(
|
|
93
|
-
"0x496c6c756d696e61746520446d6f63726174697a6520447374726962757465" # noqa: E501
|
|
94
|
-
),
|
|
95
|
-
"gasLimit": 30000000,
|
|
96
|
-
"gasUsed": 13749828,
|
|
97
|
-
"timestamp": 1337,
|
|
98
|
-
"baseFeePerGas": 10461904633,
|
|
99
|
-
"withdrawalsRoot": HexBytes(
|
|
100
|
-
"0x56260fe8298aff6d360e3a68fa855693f25dcb2708d8a7e509e8519b265d3988" # noqa: E501
|
|
101
|
-
),
|
|
102
|
-
"nonce": HexBytes("0x0000000000000000"),
|
|
103
|
-
"mixHash": HexBytes(
|
|
104
|
-
"0x73e9e036ec894047f29954571d4b6d9e8717de7304269c263cbf150caa4e0768" # noqa: E501
|
|
105
|
-
),
|
|
106
|
-
}
|
|
107
|
-
),
|
|
108
|
-
),
|
|
109
|
-
(
|
|
110
|
-
("newPendingTransactions", True),
|
|
111
|
-
{
|
|
112
|
-
"jsonrpc": "2.0",
|
|
113
|
-
"method": "eth_subscription",
|
|
114
|
-
"params": {
|
|
115
|
-
"subscription": "THIS_WILL_BE_REPLACED_IN_THE_TEST",
|
|
116
|
-
"result": {
|
|
117
|
-
"blockHash": None,
|
|
118
|
-
"blockNumber": None,
|
|
119
|
-
"from": "0x0000000000000000000000000000000000000000",
|
|
120
|
-
"gas": "0xf2f4",
|
|
121
|
-
"gasPrice": "0x29035f36f",
|
|
122
|
-
"maxFeePerGas": "0x29035f36f",
|
|
123
|
-
"maxPriorityFeePerGas": "0x3b9aca00",
|
|
124
|
-
"hash": "0xb46b85928f2c2264c2bf7ad5c6d6985664f1527e744193ef990cc0d3da5afc5e", # noqa: E501
|
|
125
|
-
"input": "0x00",
|
|
126
|
-
"nonce": "0x2013",
|
|
127
|
-
"to": "0x0000000000000000000000000000000000000000",
|
|
128
|
-
"transactionIndex": None,
|
|
129
|
-
"value": "0x0",
|
|
130
|
-
"type": "0x2",
|
|
131
|
-
"accessList": [],
|
|
132
|
-
"chainId": "0x1",
|
|
133
|
-
"v": "0x1",
|
|
134
|
-
"r": "0x3c144a7c00ed3118d55445cd5be2ae4620ca377f7c685e9c5f3687671d4dece1", # noqa: E501
|
|
135
|
-
"s": "0x284de67cbf75fec8a9edb368dee3a37cf6faba87f0af4413b2f869ebfa87d002", # noqa: E501
|
|
136
|
-
"yParity": "0x1",
|
|
137
|
-
},
|
|
138
|
-
},
|
|
139
|
-
},
|
|
140
|
-
AttributeDict(
|
|
141
|
-
{
|
|
142
|
-
"blockHash": None,
|
|
143
|
-
"blockNumber": None,
|
|
144
|
-
"from": "0x0000000000000000000000000000000000000000",
|
|
145
|
-
"gas": 62196,
|
|
146
|
-
"gasPrice": 11009389423,
|
|
147
|
-
"maxFeePerGas": 11009389423,
|
|
148
|
-
"maxPriorityFeePerGas": 1000000000,
|
|
149
|
-
"hash": HexBytes(
|
|
150
|
-
"0xb46b85928f2c2264c2bf7ad5c6d6985664f1527e744193ef990cc0d3da5afc5e" # noqa: E501
|
|
151
|
-
),
|
|
152
|
-
"input": HexBytes("0x00"),
|
|
153
|
-
"nonce": 8211,
|
|
154
|
-
"to": "0x0000000000000000000000000000000000000000",
|
|
155
|
-
"transactionIndex": None,
|
|
156
|
-
"value": 0,
|
|
157
|
-
"type": 2,
|
|
158
|
-
"accessList": [],
|
|
159
|
-
"chainId": 1,
|
|
160
|
-
"v": 1,
|
|
161
|
-
"r": HexBytes(
|
|
162
|
-
"0x3c144a7c00ed3118d55445cd5be2ae4620ca377f7c685e9c5f3687671d4dece1" # noqa: E501
|
|
163
|
-
),
|
|
164
|
-
"s": HexBytes(
|
|
165
|
-
"0x284de67cbf75fec8a9edb368dee3a37cf6faba87f0af4413b2f869ebfa87d002" # noqa: E501
|
|
166
|
-
),
|
|
167
|
-
"yParity": 1,
|
|
168
|
-
}
|
|
169
|
-
),
|
|
170
|
-
),
|
|
171
|
-
(
|
|
172
|
-
("newPendingTransactions", False),
|
|
173
|
-
{
|
|
174
|
-
"jsonrpc": "2.0",
|
|
175
|
-
"method": "eth_subscription",
|
|
176
|
-
"params": {
|
|
177
|
-
"subscription": "THIS_WILL_BE_REPLACED_IN_THE_TEST",
|
|
178
|
-
"result": "0xb46b85928f2c2264c2bf7ad5c6d6985664f1527e744193ef990cc0d3da5afc5e", # noqa: E501
|
|
179
|
-
},
|
|
180
|
-
},
|
|
181
|
-
HexBytes(
|
|
182
|
-
"0xb46b85928f2c2264c2bf7ad5c6d6985664f1527e744193ef990cc0d3da5afc5e"
|
|
183
|
-
),
|
|
184
|
-
),
|
|
185
|
-
(
|
|
186
|
-
("logs", {"address": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"}),
|
|
187
|
-
{
|
|
188
|
-
"jsonrpc": "2.0",
|
|
189
|
-
"method": "eth_subscription",
|
|
190
|
-
"params": {
|
|
191
|
-
"subscription": "THIS_WILL_BE_REPLACED_IN_THE_TEST",
|
|
192
|
-
"result": {
|
|
193
|
-
"removed": False,
|
|
194
|
-
"logIndex": "0x0",
|
|
195
|
-
"transactionIndex": "0x0",
|
|
196
|
-
"transactionHash": "0x56260fe8298aff6d360e3a68fa855693f25dcb2708d8a7e509e8519b265d3988", # noqa: E501
|
|
197
|
-
"blockHash": "0xb46b85928f2c2264c2bf7ad5c6d6985664f1527e744193ef990cc0d3da5afc5e", # noqa: E501
|
|
198
|
-
"blockNumber": "0x539",
|
|
199
|
-
"address": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
|
|
200
|
-
"data": "0x00",
|
|
201
|
-
"topics": [
|
|
202
|
-
"0xe1fffdd4923d04f559f4d29e8bfc6cda04eb5b0d3c460751c2402c5c5cc9105c", # noqa: E501
|
|
203
|
-
"0x00000000000000000000000016250d5630b4cf539739df2c5dacb4c659f2482d", # noqa: E501
|
|
204
|
-
],
|
|
205
|
-
},
|
|
206
|
-
},
|
|
207
|
-
},
|
|
208
|
-
AttributeDict(
|
|
209
|
-
{
|
|
210
|
-
"removed": False,
|
|
211
|
-
"logIndex": 0,
|
|
212
|
-
"transactionIndex": 0,
|
|
213
|
-
"transactionHash": HexBytes(
|
|
214
|
-
"0x56260fe8298aff6d360e3a68fa855693f25dcb2708d8a7e509e8519b265d3988" # noqa: E501
|
|
215
|
-
),
|
|
216
|
-
"blockHash": HexBytes(
|
|
217
|
-
"0xb46b85928f2c2264c2bf7ad5c6d6985664f1527e744193ef990cc0d3da5afc5e" # noqa: E501
|
|
218
|
-
),
|
|
219
|
-
"blockNumber": 1337,
|
|
220
|
-
"address": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
|
|
221
|
-
"data": HexBytes("0x00"),
|
|
222
|
-
"topics": [
|
|
223
|
-
HexBytes(
|
|
224
|
-
"0xe1fffdd4923d04f559f4d29e8bfc6cda04eb5b0d3c460751c2402c5c5cc9105c" # noqa: E501
|
|
225
|
-
),
|
|
226
|
-
HexBytes(
|
|
227
|
-
"0x00000000000000000000000016250d5630b4cf539739df2c5dacb4c659f2482d" # noqa: E501
|
|
228
|
-
),
|
|
229
|
-
],
|
|
230
|
-
}
|
|
231
|
-
),
|
|
232
|
-
),
|
|
233
262
|
(
|
|
234
263
|
("syncing",),
|
|
235
264
|
{
|
|
@@ -268,17 +297,13 @@ class PersistentConnectionProviderTest:
|
|
|
268
297
|
),
|
|
269
298
|
),
|
|
270
299
|
ids=[
|
|
271
|
-
"newHeads",
|
|
272
|
-
"newPendingTransactions-FullTxs",
|
|
273
|
-
"newPendingTransactions-TxHashes",
|
|
274
|
-
"logs",
|
|
275
300
|
"syncing-False",
|
|
276
301
|
"syncing-True",
|
|
277
302
|
],
|
|
278
303
|
)
|
|
279
|
-
async def
|
|
304
|
+
async def test_async_eth_subscribe_syncing_mocked(
|
|
280
305
|
self,
|
|
281
|
-
async_w3:
|
|
306
|
+
async_w3: AsyncWeb3,
|
|
282
307
|
subscription_params: Tuple[Any, ...],
|
|
283
308
|
ws_subscription_response: Dict[str, Any],
|
|
284
309
|
expected_formatted_result: Any,
|
|
@@ -301,12 +326,264 @@ class PersistentConnectionProviderTest:
|
|
|
301
326
|
assert response["result"] == expected_formatted_result
|
|
302
327
|
|
|
303
328
|
# only testing one message, so break here
|
|
329
|
+
await async_w3.eth.unsubscribe(sub_id)
|
|
330
|
+
break
|
|
331
|
+
|
|
332
|
+
assert_no_subscriptions_left(
|
|
333
|
+
async_w3.subscription_manager._subscription_container
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
@pytest.mark.asyncio
|
|
337
|
+
async def test_async_eth_subscribe_new_heads(self, async_w3: AsyncWeb3) -> None:
|
|
338
|
+
sub_id = await async_w3.eth.subscribe("newHeads")
|
|
339
|
+
assert is_hexstr(sub_id)
|
|
340
|
+
|
|
341
|
+
async for msg in async_w3.socket.process_subscriptions():
|
|
342
|
+
response = cast(FormattedEthSubscriptionResponse, msg)
|
|
343
|
+
assert response["subscription"] == sub_id
|
|
344
|
+
result = cast(BlockData, response["result"])
|
|
345
|
+
assert all(k in result.keys() for k in SOME_BLOCK_KEYS)
|
|
346
|
+
break
|
|
347
|
+
|
|
348
|
+
assert await async_w3.eth.unsubscribe(sub_id)
|
|
349
|
+
assert_no_subscriptions_left(
|
|
350
|
+
async_w3.subscription_manager._subscription_container
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
@pytest.mark.asyncio
|
|
354
|
+
async def test_async_eth_subscribe_creates_and_handles_new_heads_subscription_type(
|
|
355
|
+
self,
|
|
356
|
+
async_w3: AsyncWeb3,
|
|
357
|
+
) -> None:
|
|
358
|
+
sub_manager = async_w3.subscription_manager
|
|
359
|
+
new_heads_handler_test = SubscriptionHandlerTest()
|
|
360
|
+
|
|
361
|
+
sub_id = await async_w3.eth.subscribe(
|
|
362
|
+
"newHeads",
|
|
363
|
+
handler=new_heads_handler,
|
|
364
|
+
handler_context={"new_heads_handler_test": new_heads_handler_test},
|
|
365
|
+
)
|
|
366
|
+
assert is_hexstr(sub_id)
|
|
367
|
+
|
|
368
|
+
assert len(sub_manager.subscriptions) == 1
|
|
369
|
+
sub = sub_manager.subscriptions[0]
|
|
370
|
+
assert isinstance(sub, NewHeadsSubscription)
|
|
371
|
+
|
|
372
|
+
await sub_manager.handle_subscriptions()
|
|
373
|
+
|
|
374
|
+
assert new_heads_handler_test.passed
|
|
375
|
+
assert len(sub_manager.subscriptions) == 0
|
|
376
|
+
|
|
377
|
+
assert sub_manager.total_handler_calls == 1
|
|
378
|
+
assert sub.handler_call_count == 1
|
|
379
|
+
|
|
380
|
+
@pytest.mark.asyncio
|
|
381
|
+
async def test_async_eth_subscribe_process_pending_tx_true(
|
|
382
|
+
self,
|
|
383
|
+
async_w3: AsyncWeb3,
|
|
384
|
+
) -> None:
|
|
385
|
+
sub_id = await async_w3.eth.subscribe("newPendingTransactions", True)
|
|
386
|
+
assert is_hexstr(sub_id)
|
|
387
|
+
|
|
388
|
+
accts = await async_w3.eth.accounts
|
|
389
|
+
acct = accts[0]
|
|
390
|
+
|
|
391
|
+
num_txs = 2
|
|
392
|
+
original_nonce = await async_w3.eth.get_transaction_count(acct)
|
|
393
|
+
tx_seeder_task = asyncio.create_task(
|
|
394
|
+
self.seed_transactions_to_geth(async_w3, acct, num_txs=num_txs)
|
|
395
|
+
)
|
|
396
|
+
|
|
397
|
+
nonce = int(original_nonce)
|
|
398
|
+
tx_hash = None
|
|
399
|
+
async for msg in async_w3.socket.process_subscriptions():
|
|
400
|
+
response = cast(FormattedEthSubscriptionResponse, msg)
|
|
401
|
+
assert response["subscription"] == sub_id
|
|
402
|
+
result = cast(TxData, response["result"])
|
|
403
|
+
assert result["gas"] == 21000
|
|
404
|
+
assert result["from"] == acct
|
|
405
|
+
assert result["to"] == acct
|
|
406
|
+
assert int(result["value"]) == int(nonce)
|
|
407
|
+
tx_hash = result["hash"]
|
|
408
|
+
assert tx_hash is not None
|
|
409
|
+
|
|
410
|
+
nonce += 1
|
|
411
|
+
if nonce == int(original_nonce) + num_txs:
|
|
412
|
+
break
|
|
413
|
+
|
|
414
|
+
# cleanup
|
|
415
|
+
assert await async_w3.eth.unsubscribe(sub_id)
|
|
416
|
+
assert_no_subscriptions_left(
|
|
417
|
+
async_w3.subscription_manager._subscription_container
|
|
418
|
+
)
|
|
419
|
+
async_w3.provider._request_processor.clear_caches()
|
|
420
|
+
await async_w3.eth.wait_for_transaction_receipt(tx_hash)
|
|
421
|
+
await clean_up_task(tx_seeder_task)
|
|
422
|
+
|
|
423
|
+
@pytest.mark.asyncio
|
|
424
|
+
async def test_async_eth_subscribe_and_process_pending_tx_false(
|
|
425
|
+
self,
|
|
426
|
+
async_w3: AsyncWeb3,
|
|
427
|
+
) -> None:
|
|
428
|
+
sub_id = await async_w3.eth.subscribe("newPendingTransactions")
|
|
429
|
+
assert is_hexstr(sub_id)
|
|
430
|
+
|
|
431
|
+
accts = await async_w3.eth.accounts
|
|
432
|
+
acct = accts[0]
|
|
433
|
+
await async_w3.eth.get_transaction_count(acct)
|
|
434
|
+
|
|
435
|
+
num_txs = 2
|
|
436
|
+
tx_seeder_task = asyncio.create_task(
|
|
437
|
+
self.seed_transactions_to_geth(async_w3, acct, num_txs=num_txs)
|
|
438
|
+
)
|
|
439
|
+
|
|
440
|
+
tx_hash = None
|
|
441
|
+
i = 0
|
|
442
|
+
async for msg in async_w3.socket.process_subscriptions():
|
|
443
|
+
response = cast(FormattedEthSubscriptionResponse, msg)
|
|
444
|
+
assert response["subscription"] == sub_id
|
|
445
|
+
assert isinstance(response["result"], HexBytes)
|
|
446
|
+
tx_hash = response["result"]
|
|
447
|
+
|
|
448
|
+
i += 1
|
|
449
|
+
if i == num_txs:
|
|
450
|
+
break
|
|
451
|
+
|
|
452
|
+
# cleanup
|
|
453
|
+
await async_w3.eth.unsubscribe(sub_id)
|
|
454
|
+
assert_no_subscriptions_left(
|
|
455
|
+
async_w3.subscription_manager._subscription_container
|
|
456
|
+
)
|
|
457
|
+
await async_w3.eth.wait_for_transaction_receipt(tx_hash)
|
|
458
|
+
await clean_up_task(tx_seeder_task)
|
|
459
|
+
|
|
460
|
+
@pytest.mark.asyncio
|
|
461
|
+
async def test_async_eth_subscribe_creates_and_handles_pending_tx_subscription_type(
|
|
462
|
+
self,
|
|
463
|
+
async_w3: AsyncWeb3,
|
|
464
|
+
) -> None:
|
|
465
|
+
sub_manager = async_w3.subscription_manager
|
|
466
|
+
pending_tx_handler_test = SubscriptionHandlerTest()
|
|
467
|
+
|
|
468
|
+
sub_id = await async_w3.eth.subscribe(
|
|
469
|
+
"newPendingTransactions",
|
|
470
|
+
True,
|
|
471
|
+
handler=pending_tx_handler,
|
|
472
|
+
handler_context={"pending_tx_handler_test": pending_tx_handler_test},
|
|
473
|
+
)
|
|
474
|
+
assert is_hexstr(sub_id)
|
|
475
|
+
|
|
476
|
+
assert len(sub_manager.subscriptions) == 1
|
|
477
|
+
sub = sub_manager.subscriptions[0]
|
|
478
|
+
assert isinstance(sub, PendingTxSubscription)
|
|
479
|
+
|
|
480
|
+
# seed transactions to geth
|
|
481
|
+
accts = await async_w3.eth.accounts
|
|
482
|
+
acct = accts[0]
|
|
483
|
+
tx_seeder_task = asyncio.create_task(
|
|
484
|
+
self.seed_transactions_to_geth(async_w3, acct)
|
|
485
|
+
)
|
|
486
|
+
await sub_manager.handle_subscriptions()
|
|
487
|
+
|
|
488
|
+
assert pending_tx_handler_test.passed
|
|
489
|
+
assert len(sub_manager.subscriptions) == 0
|
|
490
|
+
|
|
491
|
+
assert sub_manager.total_handler_calls == 1
|
|
492
|
+
assert sub.handler_call_count == 1
|
|
493
|
+
|
|
494
|
+
# cleanup
|
|
495
|
+
sub_manager.total_handler_calls = 0
|
|
496
|
+
await clean_up_task(tx_seeder_task)
|
|
497
|
+
|
|
498
|
+
@pytest.mark.asyncio
|
|
499
|
+
async def test_async_eth_subscribe_and_process_logs(
|
|
500
|
+
self, async_w3: AsyncWeb3, async_emitter_contract: "AsyncContract"
|
|
501
|
+
) -> None:
|
|
502
|
+
event = async_emitter_contract.events.LogIndexedAndNotIndexed
|
|
503
|
+
event_topic = async_w3.keccak(text=event.abi_element_identifier).to_0x_hex()
|
|
504
|
+
|
|
505
|
+
sub_id = await async_w3.eth.subscribe(
|
|
506
|
+
"logs",
|
|
507
|
+
{
|
|
508
|
+
"address": async_emitter_contract.address,
|
|
509
|
+
"topics": [HexStr(event_topic)],
|
|
510
|
+
},
|
|
511
|
+
)
|
|
512
|
+
assert is_hexstr(sub_id)
|
|
513
|
+
|
|
514
|
+
accts = await async_w3.eth.accounts
|
|
515
|
+
acct = accts[0]
|
|
516
|
+
emit_event_task = await log_indexed_and_non_indexed_args_task(
|
|
517
|
+
async_w3, async_emitter_contract, acct
|
|
518
|
+
)
|
|
519
|
+
|
|
520
|
+
async for msg in async_w3.socket.process_subscriptions():
|
|
521
|
+
response = cast(FormattedEthSubscriptionResponse, msg)
|
|
522
|
+
assert response["subscription"] == sub_id
|
|
523
|
+
log_receipt = cast(LogReceipt, response["result"])
|
|
524
|
+
event_data = event.process_log(log_receipt)
|
|
525
|
+
assert event_data.args.indexedAddress == INDEXED_ADDR
|
|
526
|
+
assert event_data.args.indexedUint256 == INDEXED_UINT256
|
|
527
|
+
assert event_data.args.nonIndexedAddress == NON_INDEXED_ADDR
|
|
528
|
+
assert event_data.args.nonIndexedUint256 == NON_INDEXED_UINT256
|
|
529
|
+
assert event_data.args.nonIndexedString == NON_INDEXED_STRING
|
|
304
530
|
break
|
|
305
531
|
|
|
532
|
+
assert await async_w3.eth.unsubscribe(sub_id)
|
|
533
|
+
assert len(async_w3.subscription_manager.subscriptions) == 0
|
|
534
|
+
await clean_up_task(emit_event_task)
|
|
535
|
+
|
|
536
|
+
@pytest.mark.asyncio
|
|
537
|
+
async def test_async_eth_subscribe_creates_and_handles_logs_subscription_type(
|
|
538
|
+
self,
|
|
539
|
+
async_w3: AsyncWeb3,
|
|
540
|
+
async_emitter_contract: "AsyncContract",
|
|
541
|
+
) -> None:
|
|
542
|
+
sub_manager = async_w3.subscription_manager
|
|
543
|
+
|
|
544
|
+
event = async_emitter_contract.events.LogIndexedAndNotIndexed
|
|
545
|
+
|
|
546
|
+
logs_handler_test = SubscriptionHandlerTest()
|
|
547
|
+
sub_id = await async_w3.eth.subscribe(
|
|
548
|
+
"logs",
|
|
549
|
+
{
|
|
550
|
+
"address": async_emitter_contract.address,
|
|
551
|
+
"topics": [event.topic],
|
|
552
|
+
},
|
|
553
|
+
handler=logs_handler,
|
|
554
|
+
handler_context={
|
|
555
|
+
"logs_handler_test": logs_handler_test,
|
|
556
|
+
"event": event,
|
|
557
|
+
},
|
|
558
|
+
)
|
|
559
|
+
assert is_hexstr(sub_id)
|
|
560
|
+
|
|
561
|
+
assert len(sub_manager.subscriptions) == 1
|
|
562
|
+
sub = sub_manager.subscriptions[0]
|
|
563
|
+
assert isinstance(sub, LogsSubscription)
|
|
564
|
+
|
|
565
|
+
accts = await async_w3.eth.accounts
|
|
566
|
+
acct = accts[0]
|
|
567
|
+
emit_event_task = await log_indexed_and_non_indexed_args_task(
|
|
568
|
+
async_w3, async_emitter_contract, acct
|
|
569
|
+
)
|
|
570
|
+
|
|
571
|
+
await sub_manager.handle_subscriptions()
|
|
572
|
+
|
|
573
|
+
assert logs_handler_test.passed
|
|
574
|
+
assert len(sub_manager.subscriptions) == 0
|
|
575
|
+
|
|
576
|
+
assert sub_manager.total_handler_calls == 1
|
|
577
|
+
assert sub.handler_call_count == 1
|
|
578
|
+
|
|
579
|
+
# cleanup
|
|
580
|
+
sub_manager.total_handler_calls = 0
|
|
581
|
+
await clean_up_task(emit_event_task)
|
|
582
|
+
|
|
306
583
|
@pytest.mark.asyncio
|
|
307
584
|
async def test_async_extradata_poa_middleware_on_eth_subscription(
|
|
308
585
|
self,
|
|
309
|
-
async_w3:
|
|
586
|
+
async_w3: AsyncWeb3,
|
|
310
587
|
) -> None:
|
|
311
588
|
async_w3.middleware_onion.inject(
|
|
312
589
|
ExtraDataToPOAMiddleware, "poa_middleware", layer=0
|
|
@@ -343,12 +620,13 @@ class PersistentConnectionProviderTest:
|
|
|
343
620
|
break
|
|
344
621
|
|
|
345
622
|
# clean up
|
|
623
|
+
assert await async_w3.eth.unsubscribe(sub_id)
|
|
346
624
|
async_w3.middleware_onion.remove("poa_middleware")
|
|
347
625
|
|
|
348
626
|
@pytest.mark.asyncio
|
|
349
627
|
async def test_asyncio_gather_for_multiple_requests_matches_the_responses(
|
|
350
628
|
self,
|
|
351
|
-
async_w3:
|
|
629
|
+
async_w3: AsyncWeb3,
|
|
352
630
|
) -> None:
|
|
353
631
|
(
|
|
354
632
|
latest,
|
|
@@ -371,22 +649,10 @@ class PersistentConnectionProviderTest:
|
|
|
371
649
|
assert isinstance(pending, AttributeDict)
|
|
372
650
|
|
|
373
651
|
# assert block values
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
"transactionsRoot",
|
|
379
|
-
"stateRoot",
|
|
380
|
-
"receiptsRoot",
|
|
381
|
-
"size",
|
|
382
|
-
"gasLimit",
|
|
383
|
-
"gasUsed",
|
|
384
|
-
"timestamp",
|
|
385
|
-
"transactions",
|
|
386
|
-
"baseFeePerGas",
|
|
387
|
-
]
|
|
388
|
-
assert all(k in latest.keys() for k in some_block_keys)
|
|
389
|
-
assert all(k in pending.keys() for k in some_block_keys)
|
|
652
|
+
assert latest is not None
|
|
653
|
+
assert all(k in latest.keys() for k in SOME_BLOCK_KEYS)
|
|
654
|
+
assert pending is not None
|
|
655
|
+
assert all(k in pending.keys() for k in SOME_BLOCK_KEYS)
|
|
390
656
|
|
|
391
657
|
assert isinstance(block_num, int)
|
|
392
658
|
assert latest["number"] == block_num
|
|
@@ -394,3 +660,219 @@ class PersistentConnectionProviderTest:
|
|
|
394
660
|
assert isinstance(chain_id, int)
|
|
395
661
|
assert isinstance(chain_id2, int)
|
|
396
662
|
assert isinstance(chain_id3, int)
|
|
663
|
+
|
|
664
|
+
@pytest.mark.asyncio
|
|
665
|
+
async def test_async_public_socket_api(self, async_w3: AsyncWeb3) -> None:
|
|
666
|
+
# clear all caches and queues
|
|
667
|
+
async_w3.provider._request_processor.clear_caches()
|
|
668
|
+
|
|
669
|
+
# send a request over the socket
|
|
670
|
+
await async_w3.socket.send(
|
|
671
|
+
RPCEndpoint("eth_getBlockByNumber"), ["latest", True]
|
|
672
|
+
)
|
|
673
|
+
|
|
674
|
+
# recv and validate the unprocessed response
|
|
675
|
+
response = await async_w3.socket.recv()
|
|
676
|
+
assert "id" in response, "Expected 'id' key in response."
|
|
677
|
+
assert "jsonrpc" in response, "Expected 'jsonrpc' key in response."
|
|
678
|
+
assert "result" in response, "Expected 'result' key in response."
|
|
679
|
+
assert all(k in response["result"].keys() for k in SOME_BLOCK_KEYS)
|
|
680
|
+
assert not isinstance(response["result"]["number"], int) # assert not processed
|
|
681
|
+
|
|
682
|
+
# make a request over the socket
|
|
683
|
+
response = await async_w3.socket.make_request(
|
|
684
|
+
RPCEndpoint("eth_getBlockByNumber"), ["latest", True]
|
|
685
|
+
)
|
|
686
|
+
assert "id" in response, "Expected 'id' key in response."
|
|
687
|
+
assert "jsonrpc" in response, "Expected 'jsonrpc' key in response."
|
|
688
|
+
assert "result" in response, "Expected 'result' key in response."
|
|
689
|
+
assert all(k in response["result"].keys() for k in SOME_BLOCK_KEYS)
|
|
690
|
+
assert not isinstance(response["result"]["number"], int) # assert not processed
|
|
691
|
+
|
|
692
|
+
@pytest.mark.asyncio
|
|
693
|
+
async def test_async_subscription_manager_subscribes_to_many_subscriptions(
|
|
694
|
+
self, async_w3: AsyncWeb3, async_emitter_contract: "AsyncContract"
|
|
695
|
+
) -> None:
|
|
696
|
+
sub_manager = async_w3.subscription_manager
|
|
697
|
+
|
|
698
|
+
event = async_emitter_contract.events.LogIndexedAndNotIndexed
|
|
699
|
+
event_topic = async_w3.keccak(text=event.abi_element_identifier).to_0x_hex()
|
|
700
|
+
|
|
701
|
+
new_heads_handler_test = SubscriptionHandlerTest()
|
|
702
|
+
pending_tx_handler_test = SubscriptionHandlerTest()
|
|
703
|
+
logs_handler_test = SubscriptionHandlerTest()
|
|
704
|
+
|
|
705
|
+
await sub_manager.subscribe(
|
|
706
|
+
[
|
|
707
|
+
NewHeadsSubscription(
|
|
708
|
+
handler=new_heads_handler,
|
|
709
|
+
handler_context={"new_heads_handler_test": new_heads_handler_test},
|
|
710
|
+
),
|
|
711
|
+
PendingTxSubscription(
|
|
712
|
+
full_transactions=True,
|
|
713
|
+
handler=pending_tx_handler,
|
|
714
|
+
handler_context={
|
|
715
|
+
"pending_tx_handler_test": pending_tx_handler_test
|
|
716
|
+
},
|
|
717
|
+
),
|
|
718
|
+
LogsSubscription(
|
|
719
|
+
address=async_emitter_contract.address,
|
|
720
|
+
topics=[HexStr(event_topic)],
|
|
721
|
+
handler=logs_handler,
|
|
722
|
+
handler_context={
|
|
723
|
+
"logs_handler_test": logs_handler_test,
|
|
724
|
+
"event": event,
|
|
725
|
+
},
|
|
726
|
+
),
|
|
727
|
+
]
|
|
728
|
+
)
|
|
729
|
+
|
|
730
|
+
# emit contract event for `logs` subscription
|
|
731
|
+
accts = await async_w3.eth.accounts
|
|
732
|
+
acct = accts[0]
|
|
733
|
+
emit_event_task = await log_indexed_and_non_indexed_args_task(
|
|
734
|
+
async_w3, async_emitter_contract, acct
|
|
735
|
+
)
|
|
736
|
+
|
|
737
|
+
# get subscriptions before they are unsubscribed and removed
|
|
738
|
+
subs = sub_manager.subscriptions
|
|
739
|
+
|
|
740
|
+
await sub_manager.handle_subscriptions()
|
|
741
|
+
|
|
742
|
+
# assert unsubscribed and removed subscriptions
|
|
743
|
+
assert len(sub_manager.subscriptions) == 0
|
|
744
|
+
|
|
745
|
+
assert sub_manager.total_handler_calls == 3
|
|
746
|
+
assert all(sub.handler_call_count == 1 for sub in subs)
|
|
747
|
+
|
|
748
|
+
assert new_heads_handler_test.passed
|
|
749
|
+
assert pending_tx_handler_test.passed
|
|
750
|
+
assert logs_handler_test.passed
|
|
751
|
+
|
|
752
|
+
# cleanup
|
|
753
|
+
sub_manager.total_handler_calls = 0
|
|
754
|
+
await clean_up_task(emit_event_task)
|
|
755
|
+
|
|
756
|
+
@pytest.mark.asyncio
|
|
757
|
+
async def test_subscription_handler_context(self, async_w3: AsyncWeb3) -> None:
|
|
758
|
+
base_url = "http://localhost:1337"
|
|
759
|
+
async_beacon = AsyncBeacon(base_url)
|
|
760
|
+
handler_test = SubscriptionHandlerTest()
|
|
761
|
+
|
|
762
|
+
async def test_sub_handler(
|
|
763
|
+
handler_context: NewHeadsSubscriptionContext,
|
|
764
|
+
) -> None:
|
|
765
|
+
beacon = handler_context.beacon
|
|
766
|
+
assert isinstance(beacon, AsyncBeacon)
|
|
767
|
+
assert beacon.base_url == base_url
|
|
768
|
+
assert handler_context.int1 == 1337
|
|
769
|
+
assert handler_context.str1 == "foo"
|
|
770
|
+
assert handler_context.int2 == 1999
|
|
771
|
+
assert handler_context.str2 == "bar"
|
|
772
|
+
|
|
773
|
+
handler_context.handler_test.passed = True
|
|
774
|
+
unsubscribed = await handler_context.subscription.unsubscribe()
|
|
775
|
+
assert unsubscribed
|
|
776
|
+
|
|
777
|
+
subscribed = await async_w3.eth.subscribe(
|
|
778
|
+
"newHeads",
|
|
779
|
+
label="foo",
|
|
780
|
+
handler=test_sub_handler,
|
|
781
|
+
handler_context={
|
|
782
|
+
"beacon": async_beacon,
|
|
783
|
+
"int1": 1337,
|
|
784
|
+
"str1": "foo",
|
|
785
|
+
"int2": 1999,
|
|
786
|
+
"str2": "bar",
|
|
787
|
+
"handler_test": handler_test,
|
|
788
|
+
},
|
|
789
|
+
)
|
|
790
|
+
assert is_hexstr(subscribed)
|
|
791
|
+
|
|
792
|
+
sub_manager = async_w3.subscription_manager
|
|
793
|
+
|
|
794
|
+
await sub_manager.handle_subscriptions()
|
|
795
|
+
|
|
796
|
+
assert len(sub_manager.subscriptions) == 0
|
|
797
|
+
assert sub_manager.total_handler_calls == 1
|
|
798
|
+
assert handler_test.passed
|
|
799
|
+
|
|
800
|
+
# cleanup
|
|
801
|
+
sub_manager.total_handler_calls = 0
|
|
802
|
+
|
|
803
|
+
@pytest.mark.asyncio
|
|
804
|
+
async def test_subscriptions_with_handler_and_without(
|
|
805
|
+
self, async_w3: AsyncWeb3
|
|
806
|
+
) -> None:
|
|
807
|
+
handler_test = SubscriptionHandlerTest()
|
|
808
|
+
stream_passed = False
|
|
809
|
+
|
|
810
|
+
async def test_sub_handler(
|
|
811
|
+
handler_context: NewHeadsSubscriptionContext,
|
|
812
|
+
) -> None:
|
|
813
|
+
handler_context.handler_test.passed = True
|
|
814
|
+
await handler_context.subscription.unsubscribe()
|
|
815
|
+
|
|
816
|
+
async def handle_subscription_stream() -> None:
|
|
817
|
+
nonlocal stream_passed
|
|
818
|
+
async for msg in async_w3.socket.process_subscriptions():
|
|
819
|
+
response = cast(FormattedEthSubscriptionResponse, msg)
|
|
820
|
+
assert sub_manager.get_by_id(response["subscription"]) is not None
|
|
821
|
+
assert response["result"] is not None
|
|
822
|
+
# wait for the handler to unsubscribe:
|
|
823
|
+
stream_passed = True
|
|
824
|
+
await async_w3.eth.unsubscribe(response["subscription"])
|
|
825
|
+
break
|
|
826
|
+
|
|
827
|
+
await async_w3.eth.subscribe(
|
|
828
|
+
"newHeads",
|
|
829
|
+
handler=test_sub_handler,
|
|
830
|
+
label="managed",
|
|
831
|
+
handler_context={"handler_test": handler_test},
|
|
832
|
+
)
|
|
833
|
+
await async_w3.eth.subscribe("newHeads", label="streamed")
|
|
834
|
+
|
|
835
|
+
sub_manager = async_w3.subscription_manager
|
|
836
|
+
assert len(sub_manager.subscriptions) == 2
|
|
837
|
+
|
|
838
|
+
await asyncio.gather(
|
|
839
|
+
sub_manager.handle_subscriptions(),
|
|
840
|
+
handle_subscription_stream(),
|
|
841
|
+
)
|
|
842
|
+
|
|
843
|
+
assert len(sub_manager.subscriptions) == 0
|
|
844
|
+
assert sub_manager.total_handler_calls == 1
|
|
845
|
+
assert handler_test.passed
|
|
846
|
+
assert stream_passed
|
|
847
|
+
|
|
848
|
+
# cleanup
|
|
849
|
+
sub_manager.total_handler_calls = 0
|
|
850
|
+
|
|
851
|
+
@pytest.mark.asyncio
|
|
852
|
+
async def test_handle_subscriptions_breaks_on_unsubscribe(
|
|
853
|
+
self,
|
|
854
|
+
async_w3: AsyncWeb3,
|
|
855
|
+
) -> None:
|
|
856
|
+
async def unsubscribe_subs(
|
|
857
|
+
subs: List[Union[NewHeadsSubscription, LogsSubscription]]
|
|
858
|
+
) -> None:
|
|
859
|
+
for sub in subs:
|
|
860
|
+
await sub.unsubscribe()
|
|
861
|
+
|
|
862
|
+
sub_manager = async_w3.subscription_manager
|
|
863
|
+
sub1 = NewHeadsSubscription(label="foo", handler=idle_handler)
|
|
864
|
+
sub2 = LogsSubscription(label="bar", handler=idle_handler)
|
|
865
|
+
|
|
866
|
+
await sub_manager.subscribe([sub1, sub2])
|
|
867
|
+
|
|
868
|
+
assert sub_manager.subscriptions == [sub1, sub2]
|
|
869
|
+
|
|
870
|
+
unsubscribe_task = asyncio.create_task(unsubscribe_subs([sub1, sub2]))
|
|
871
|
+
# With no subscriptions in the queue, ``handle_subscriptions`` should hang
|
|
872
|
+
# indefinitely. Test that when the last subscription is unsubscribed from,
|
|
873
|
+
# the method breaks out of the loop. This is done via a raised
|
|
874
|
+
# ``SubscriptionProcessingFinished`` within the ``TaskReliantQueue``.
|
|
875
|
+
await sub_manager.handle_subscriptions()
|
|
876
|
+
|
|
877
|
+
assert_no_subscriptions_left(sub_manager._subscription_container)
|
|
878
|
+
await clean_up_task(unsubscribe_task)
|