Skip to content

Commit 9d3366d

Browse files
dbfreemfselmo
authored andcommitted
Additional testing for async ENS from PR ethereum#2501
1 parent 4bd7bd7 commit 9d3366d

File tree

3 files changed

+146
-1
lines changed

3 files changed

+146
-1
lines changed

tests/ens/conftest.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,15 @@ def async_extended_resolver(async_w3):
407407
)
408408

409409

410+
def async_offchain_resolver(async_w3):
411+
return async_w3.eth.contract(
412+
bytecode=offchain_resolver_bytecode,
413+
bytecode_runtime=offchain_resolver_bytecode_runtime,
414+
abi=offchain_resolver_abi,
415+
ContractFactoryClass=AsyncContract,
416+
)
417+
418+
410419
def async_ENS_factory(async_w3):
411420
return async_w3.eth.contract(
412421
bytecode="6060604052341561000f57600080fd5b60008080526020527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb58054600160a060020a033316600160a060020a0319909116179055610501806100626000396000f300606060405236156100805763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416630178b8bf811461008557806302571be3146100b757806306ab5923146100cd57806314ab9038146100f457806316a25cbd146101175780631896f70a1461014a5780635b0fc9c31461016c575b600080fd5b341561009057600080fd5b61009b60043561018e565b604051600160a060020a03909116815260200160405180910390f35b34156100c257600080fd5b61009b6004356101ac565b34156100d857600080fd5b6100f2600435602435600160a060020a03604435166101c7565b005b34156100ff57600080fd5b6100f260043567ffffffffffffffff60243516610289565b341561012257600080fd5b61012d600435610355565b60405167ffffffffffffffff909116815260200160405180910390f35b341561015557600080fd5b6100f2600435600160a060020a036024351661038c565b341561017757600080fd5b6100f2600435600160a060020a0360243516610432565b600090815260208190526040902060010154600160a060020a031690565b600090815260208190526040902054600160a060020a031690565b600083815260208190526040812054849033600160a060020a039081169116146101f057600080fd5b8484604051918252602082015260409081019051908190039020915083857fce0457fe73731f824cc272376169235128c118b49d344817417c6d108d155e8285604051600160a060020a03909116815260200160405180910390a3506000908152602081905260409020805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a03929092169190911790555050565b600082815260208190526040902054829033600160a060020a039081169116146102b257600080fd5b827f1d4f9bbfc9cab89d66e1a1562f2233ccbf1308cb4f63de2ead5787adddb8fa688360405167ffffffffffffffff909116815260200160405180910390a250600091825260208290526040909120600101805467ffffffffffffffff90921674010000000000000000000000000000000000000000027fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff909216919091179055565b60009081526020819052604090206001015474010000000000000000000000000000000000000000900467ffffffffffffffff1690565b600082815260208190526040902054829033600160a060020a039081169116146103b557600080fd5b827f335721b01866dc23fbee8b6b2c7b1e14d6f05c28cd35a2c934239f94095602a083604051600160a060020a03909116815260200160405180910390a250600091825260208290526040909120600101805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a03909216919091179055565b600082815260208190526040902054829033600160a060020a0390811691161461045b57600080fd5b827fd4735d920b0f87494915f556dd9b54c8f309026070caea5c737245152564d26683604051600160a060020a03909116815260200160405180910390a250600091825260208290526040909120805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a039092169190911790555600a165627a7a7230582050975b6c54a16d216b563f4c4960d6ebc5881eb1ec73c2ef1f87920a251159530029", # noqa: E501
@@ -583,6 +592,39 @@ async def async_ens_setup():
583592
extended_resolver.address
584593
).transact({'from': second_account})
585594

595+
# --- setup offchain resolver example --- #
596+
597+
# create offchain resolver
598+
offchain_resolver = await async_deploy(
599+
async_w3, async_offchain_resolver, ens_key,
600+
601+
# use a made up url and mock the call to this endpoint in tests
602+
args=[
603+
[
604+
"https://web3.py/gateway/{sender}/{data}.json", # for GET request testing
605+
"https://web3.py/gateway/{sender}.json", # for POST request testing
606+
],
607+
[to_checksum_address('0x4c40caf7f24a545095299972c381862469b080fb')]
608+
]
609+
)
610+
611+
# set owner of offchainexample.eth to an account controlled by tests
612+
await ens_contract.functions.setSubnodeOwner(
613+
eth_namehash,
614+
async_w3.keccak(text='offchainexample'),
615+
second_account
616+
).transact({'from': ens_key})
617+
618+
# ns.namehash('offchainexample.eth')
619+
offchain_example_namehash = bytes32(
620+
0x42041b0018edd29d7c17154b0c671acc0502ea0b3693cafbeadf58e6beaaa16c
621+
)
622+
623+
await ens_contract.functions.setResolver(
624+
offchain_example_namehash,
625+
offchain_resolver.address
626+
).transact({'from': second_account})
627+
586628
# --- finish setup --- #
587629

588630
# make the registrar the owner of the 'eth' name

tests/ens/test_offchain_resolution.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import pytest
22

3+
from aiohttp import (
4+
ClientSession,
5+
)
36
import requests
47

58
from ens.utils import (
@@ -70,6 +73,44 @@ def raise_for_status(): pass # noqa: E704
7073
def json(): return {'not_data': OFFCHAIN_RESOLVER_DATA} # noqa: E704
7174

7275

76+
class AsyncMockHttpSuccessResponse:
77+
status_code = 200
78+
79+
def __init__(self, request_type, *args, **_kwargs):
80+
# validate the expected urls
81+
if request_type == 'get':
82+
assert args[1] == EXPECTED_GET_URL
83+
elif request_type == 'post':
84+
assert args[1] == EXPECTED_POST_URL
85+
86+
@staticmethod
87+
def raise_for_status(): pass # noqa: E704
88+
89+
@staticmethod
90+
async def json(): return {'data': OFFCHAIN_RESOLVER_DATA} # noqa: E704
91+
92+
@property
93+
def status(self):
94+
return self.status_code
95+
96+
97+
class AsyncMockHttpBadFormatResponse:
98+
status_code = 200
99+
100+
def __init__(self, *args):
101+
assert args[1] == EXPECTED_GET_URL
102+
103+
@staticmethod
104+
def raise_for_status(): pass # noqa: E704
105+
106+
@staticmethod
107+
async def json(): return {'not_data': OFFCHAIN_RESOLVER_DATA} # noqa: E704'
108+
109+
@property
110+
def status(self):
111+
return self.status_code
112+
113+
73114
def test_offchain_resolution_with_get_request(ens, monkeypatch):
74115
# mock GET response with real return data from 'offchainexample.eth' resolver
75116
def mock_get(*args, **kwargs):
@@ -123,3 +164,45 @@ def test_offchain_resolver_function_call_raises_with_ccip_read_disabled(ens, mon
123164
ens_encode_name('offchainexample.eth'),
124165
ENCODED_ADDR_CALLDATA,
125166
)
167+
168+
169+
@pytest.mark.asyncio
170+
async def test_async_offchain_resolution_with_get_request(async_ens, monkeypatch):
171+
# mock GET response with real return data from 'offchainexample.eth' resolver
172+
async def mock_get(*args, **kwargs):
173+
return AsyncMockHttpSuccessResponse('get', *args, **kwargs)
174+
175+
monkeypatch.setattr(ClientSession, 'get', mock_get)
176+
177+
assert await async_ens.address('offchainexample.eth') == EXPECTED_RESOLVED_ADDRESS
178+
179+
180+
@pytest.mark.asyncio
181+
async def test_async_offchain_resolution_with_post_request(async_ens, monkeypatch):
182+
# mock POST response with real return data from 'offchainexample.eth' resolver
183+
async def mock_post(*args, **kwargs):
184+
return AsyncMockHttpSuccessResponse('post', *args, **kwargs)
185+
186+
monkeypatch.setattr(ClientSession, 'post', mock_post)
187+
188+
assert await async_ens.address('offchainexample.eth') == EXPECTED_RESOLVED_ADDRESS
189+
190+
191+
@pytest.mark.asyncio
192+
async def test_async_offchain_resolution_raises_when_all_supplied_urls_fail(async_ens):
193+
# with no mocked responses, requests to all urls will fail
194+
with pytest.raises(Exception, match='Offchain lookup failed for supplied urls.'):
195+
await async_ens.address('offchainexample.eth')
196+
197+
198+
@pytest.mark.asyncio
199+
async def test_async_offchain_resolution_with_improperly_formatted_http_response(async_ens,
200+
monkeypatch):
201+
async def mock_get(*args, **_):
202+
return AsyncMockHttpBadFormatResponse(*args)
203+
204+
monkeypatch.setattr(ClientSession, 'get', mock_get)
205+
with pytest.raises(ValidationError, match=(
206+
"Improperly formatted response for offchain lookup HTTP request - missing 'data' field."
207+
)):
208+
await async_ens.address('offchainexample.eth')

tests/ens/test_wildcard_resolution.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,29 @@ def test_wildcard_resolution_with_extended_resolver_for_subdomains(ens, subdomai
1010
assert resolved_child_address == '0x000000000000000000000000000000000000dEaD'
1111

1212

13-
def test_wildcard_resolution_with_extended_resolver_for_parent_ens_domain(ens):
13+
def test_async_wildcard_resolution_with_extended_resolver_for_parent_ens_domain(ens):
1414
# validate `extended-resolver.eth` by asserting it returns the specified hard-coded address from
1515
# `tests/test_contracts/ExtendedResolver.sol` which requires a specific condition to be
1616
# met for the parent domain `extended-resolver.eth`
1717
resolved_parent_address = ens.address('extended-resolver.eth')
1818
assert resolved_parent_address == '0x000000000000000000000000000000000000bEEF'
19+
20+
21+
@pytest.mark.asyncio
22+
@pytest.mark.parametrize('subdomain', ('sub1', 'sub2', 'rändöm', '🌈rainbow', 'faß'))
23+
async def test_async_wildcard_resolution_with_extended_resolver_for_subdomains(async_ens,
24+
subdomain):
25+
# validate subdomains of `extended-resolver.eth` by asserting it returns the specified
26+
# hard-coded address from `tests/test_contracts/ExtendedResolver.sol` which requires
27+
# certain conditions to be met that are specific to subdomains only
28+
resolved_child_address = await async_ens.address(f'{subdomain}.extended-resolver.eth')
29+
assert resolved_child_address == '0x000000000000000000000000000000000000dEaD'
30+
31+
32+
@pytest.mark.asyncio
33+
async def test_wildcard_resolution_with_extended_resolver_for_parent_ens_domain(async_ens):
34+
# validate `extended-resolver.eth` by asserting it returns the specified hard-coded address from
35+
# `tests/test_contracts/ExtendedResolver.sol` which requires a specific condition to be
36+
# met for the parent domain `extended-resolver.eth`
37+
resolved_parent_address = await async_ens.address('extended-resolver.eth')
38+
assert resolved_parent_address == '0x000000000000000000000000000000000000bEEF'

0 commit comments

Comments
 (0)