-
Notifications
You must be signed in to change notification settings - Fork 1.8k
/
Copy pathtest_offchain_lookup.py
210 lines (179 loc) · 7.62 KB
/
test_offchain_lookup.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
import pytest
from eth_abi import (
abi,
)
from web3._utils.contract_sources.contract_data.offchain_lookup import (
OFFCHAIN_LOOKUP_DATA,
)
from web3._utils.module_testing.module_testing_utils import (
mock_offchain_lookup_request_response,
)
from web3._utils.type_conversion import (
to_hex_if_bytes,
)
from web3.exceptions import (
OffchainLookup,
TooManyRequests,
Web3ValidationError,
)
# "test offchain lookup" as an abi-encoded string
OFFCHAIN_LOOKUP_CONTRACT_TEST_DATA = "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001474657374206f6666636861696e206c6f6f6b7570000000000000000000000000" # noqa: E501
# "web3py" as an abi-encoded string
WEB3PY_AS_HEXBYTES = "0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000067765623370790000000000000000000000000000000000000000000000000000" # noqa: E501
@pytest.fixture
def offchain_lookup_contract_factory(w3):
return w3.eth.contract(**OFFCHAIN_LOOKUP_DATA)
@pytest.fixture
def offchain_lookup_contract(
w3,
wait_for_block,
offchain_lookup_contract_factory,
wait_for_transaction,
address_conversion_func,
):
wait_for_block(w3)
deploy_txn_hash = offchain_lookup_contract_factory.constructor().transact(
{"gas": 10000000}
)
deploy_receipt = wait_for_transaction(w3, deploy_txn_hash)
contract_address = address_conversion_func(deploy_receipt["contractAddress"])
bytecode = w3.eth.get_code(contract_address)
assert bytecode == offchain_lookup_contract_factory.bytecode_runtime
deployed_offchain_lookup = offchain_lookup_contract_factory(
address=contract_address
)
assert deployed_offchain_lookup.address == contract_address
return deployed_offchain_lookup
def test_offchain_lookup_functionality(
offchain_lookup_contract,
monkeypatch,
):
normalized_address = to_hex_if_bytes(offchain_lookup_contract.address)
mock_offchain_lookup_request_response(
monkeypatch,
mocked_request_url=f"https://web3.py/gateway/{normalized_address}/{OFFCHAIN_LOOKUP_CONTRACT_TEST_DATA}.json", # noqa: E501
mocked_json_data=WEB3PY_AS_HEXBYTES,
)
response = offchain_lookup_contract.caller.testOffchainLookup(
OFFCHAIN_LOOKUP_CONTRACT_TEST_DATA
)
assert abi.decode(["string"], response)[0] == "web3py"
def test_eth_call_offchain_lookup_raises_when_ccip_read_is_disabled(
w3, offchain_lookup_contract
):
# test ContractFunction call
with pytest.raises(OffchainLookup):
offchain_lookup_contract.functions.testOffchainLookup(
OFFCHAIN_LOOKUP_CONTRACT_TEST_DATA
).call(ccip_read_enabled=False)
# test ContractCaller call
with pytest.raises(OffchainLookup):
offchain_lookup_contract.caller(ccip_read_enabled=False).testOffchainLookup(
OFFCHAIN_LOOKUP_CONTRACT_TEST_DATA
)
# test global flag on the provider
w3.provider.global_ccip_read_enabled = False
with pytest.raises(OffchainLookup):
offchain_lookup_contract.caller.testOffchainLookup(
OFFCHAIN_LOOKUP_CONTRACT_TEST_DATA
)
with pytest.raises(OffchainLookup):
offchain_lookup_contract.caller(ccip_read_enabled=False).testOffchainLookup(
OFFCHAIN_LOOKUP_CONTRACT_TEST_DATA
)
w3.provider.global_ccip_read_enabled = True # cleanup
def test_offchain_lookup_call_flag_overrides_provider_flag(
w3,
offchain_lookup_contract,
monkeypatch,
):
normalized_address = to_hex_if_bytes(offchain_lookup_contract.address)
mock_offchain_lookup_request_response(
monkeypatch,
mocked_request_url=f"https://web3.py/gateway/{normalized_address}/{OFFCHAIN_LOOKUP_CONTRACT_TEST_DATA}.json", # noqa: E501
mocked_json_data=WEB3PY_AS_HEXBYTES,
)
w3.provider.global_ccip_read_enabled = False
response = offchain_lookup_contract.functions.testOffchainLookup(
OFFCHAIN_LOOKUP_CONTRACT_TEST_DATA
).call(ccip_read_enabled=True)
assert abi.decode(["string"], response)[0] == "web3py"
w3.provider.global_ccip_read_enabled = True
def test_offchain_lookup_raises_for_improperly_formatted_rest_request_response(
offchain_lookup_contract,
monkeypatch,
):
normalized_address = to_hex_if_bytes(offchain_lookup_contract.address)
mock_offchain_lookup_request_response(
monkeypatch,
mocked_request_url=f"https://web3.py/gateway/{normalized_address}/{OFFCHAIN_LOOKUP_CONTRACT_TEST_DATA}.json", # noqa: E501
mocked_json_data=WEB3PY_AS_HEXBYTES,
json_data_field="not_data",
)
with pytest.raises(Web3ValidationError, match="missing 'data' field"):
offchain_lookup_contract.caller.testOffchainLookup(
OFFCHAIN_LOOKUP_CONTRACT_TEST_DATA
)
@pytest.mark.parametrize("status_code_non_4xx_error", [100, 300, 500, 600])
def test_eth_call_offchain_lookup_tries_next_url_for_non_4xx_error_status_and_tests_POST( # noqa: E501
offchain_lookup_contract,
monkeypatch,
status_code_non_4xx_error,
) -> None:
normalized_contract_address = to_hex_if_bytes(
offchain_lookup_contract.address
).lower()
# The next url in our test contract doesn't contain '{data}',
# triggering the POST request logic. The idea here is to return
# a bad status for the first url (GET) and a success status from the
# second call (POST) to test both that we move on to the next url with
# non 4xx status and that the POST logic is also working as expected.
mock_offchain_lookup_request_response(
monkeypatch,
mocked_request_url=f"https://web3.py/gateway/{normalized_contract_address}/{OFFCHAIN_LOOKUP_CONTRACT_TEST_DATA}.json", # noqa: E501
mocked_status_code=status_code_non_4xx_error,
mocked_json_data=WEB3PY_AS_HEXBYTES,
)
mock_offchain_lookup_request_response(
monkeypatch,
http_method="POST",
mocked_request_url="https://web3.py/gateway",
mocked_status_code=200,
mocked_json_data=WEB3PY_AS_HEXBYTES,
sender=normalized_contract_address,
calldata=OFFCHAIN_LOOKUP_CONTRACT_TEST_DATA,
)
response = offchain_lookup_contract.caller.testOffchainLookup(
OFFCHAIN_LOOKUP_CONTRACT_TEST_DATA
)
assert abi.decode(["string"], response)[0] == "web3py"
@pytest.mark.parametrize("status_code_4xx_error", [400, 410, 450, 499])
def test_eth_call_offchain_lookup_calls_raise_for_status_for_4xx_status_code(
offchain_lookup_contract,
monkeypatch,
status_code_4xx_error,
) -> None:
normalized_contract_address = to_hex_if_bytes(
offchain_lookup_contract.address
).lower()
mock_offchain_lookup_request_response(
monkeypatch,
mocked_request_url=f"https://web3.py/gateway/{normalized_contract_address}/{OFFCHAIN_LOOKUP_CONTRACT_TEST_DATA}.json", # noqa: E501
mocked_status_code=status_code_4xx_error,
mocked_json_data=WEB3PY_AS_HEXBYTES,
)
with pytest.raises(Exception, match="called raise_for_status\\(\\)"):
offchain_lookup_contract.caller.testOffchainLookup(
OFFCHAIN_LOOKUP_CONTRACT_TEST_DATA
)
def test_offchain_lookup_raises_on_continuous_redirect(
offchain_lookup_contract,
monkeypatch,
):
normalized_address = to_hex_if_bytes(offchain_lookup_contract.address)
mock_offchain_lookup_request_response(
monkeypatch,
mocked_request_url=f"https://web3.py/gateway/{normalized_address}/0x.json",
)
with pytest.raises(TooManyRequests, match="Too many CCIP read redirects"):
offchain_lookup_contract.caller.continuousOffchainLookup()