diff --git a/ens/async_ens.py b/ens/async_ens.py index 6e83773358..f4da906ae3 100644 --- a/ens/async_ens.py +++ b/ens/async_ens.py @@ -57,7 +57,7 @@ address_in, address_to_reverse_domain, default, - ens_encode_name, + dns_encode_name, init_async_web3, is_empty_name, is_none_or_zero_address, @@ -500,7 +500,7 @@ async def _resolve( calldata = resolver.encode_abi(*contract_func_with_args) contract_call_result = await resolver.caller.resolve( - ens_encode_name(normal_name), + dns_encode_name(normal_name), calldata, ) result = self._decode_ensip10_resolve_data( diff --git a/ens/ens.py b/ens/ens.py index 2cb2150a90..a5be5f713e 100644 --- a/ens/ens.py +++ b/ens/ens.py @@ -56,7 +56,7 @@ address_in, address_to_reverse_domain, default, - ens_encode_name, + dns_encode_name, init_web3, is_empty_name, is_none_or_zero_address, @@ -482,7 +482,7 @@ def _resolve( calldata = resolver.encode_abi(*contract_func_with_args) contract_call_result = resolver.caller.resolve( - ens_encode_name(normal_name), + dns_encode_name(normal_name), calldata, ) result = self._decode_ensip10_resolve_data( diff --git a/ens/utils.py b/ens/utils.py index 9db364a61c..824a5bd0f3 100644 --- a/ens/utils.py +++ b/ens/utils.py @@ -13,6 +13,7 @@ Union, cast, ) +import warnings from eth_typing import ( Address, @@ -132,7 +133,7 @@ def normalize_name(name: str) -> str: return normalize_name_ensip15(name).as_text -def ens_encode_name(name: str) -> bytes: +def dns_encode_name(name: str) -> HexBytes: r""" Encode a name according to DNS standards specified in section 3.1 of RFC1035 with the following validations: @@ -145,7 +146,7 @@ def ens_encode_name(name: str) -> bytes: :param str name: the dot-separated ENS name """ if is_empty_name(name): - return b"\x00" + return HexBytes(b"\x00") normalized_name = normalize_name(name) @@ -163,7 +164,17 @@ def ens_encode_name(name: str) -> bytes: dns_prepped_labels = [to_bytes(len(label)) + label for label in labels_as_bytes] # return the joined prepped labels in order and append the zero byte at the end: - return b"".join(dns_prepped_labels) + b"\x00" + return HexBytes(b"".join(dns_prepped_labels) + b"\x00") + + +def ens_encode_name(name: str) -> bytes: + warnings.warn( + "``ens_encode_name`` is deprecated and will be removed in the next " + "major version. Use ``dns_encode_name`` instead.", + DeprecationWarning, + stacklevel=2, + ) + return bytes(dns_encode_name(name)) def is_valid_name(name: str) -> bool: diff --git a/newsfragments/3700.deprecation.rst b/newsfragments/3700.deprecation.rst new file mode 100644 index 0000000000..fb80e417a4 --- /dev/null +++ b/newsfragments/3700.deprecation.rst @@ -0,0 +1 @@ +Deprecate ``ens_encode_name`` in favor of ``dns_encode_name``. diff --git a/newsfragments/3700.feature.rst b/newsfragments/3700.feature.rst new file mode 100644 index 0000000000..572bc50cb5 --- /dev/null +++ b/newsfragments/3700.feature.rst @@ -0,0 +1 @@ +Introduce ``ens.utils.dns_encode_name`` as a rename of the current ``ens_encode_name``, for consistency across other language implementations and with the ENS docs. Returns ``HexBytes`` instead of ``bytes``. diff --git a/tests/ens/test_offchain_resolution.py b/tests/ens/test_offchain_resolution.py index e3ae3366f8..9e4f3b1640 100644 --- a/tests/ens/test_offchain_resolution.py +++ b/tests/ens/test_offchain_resolution.py @@ -6,7 +6,7 @@ import requests from ens.utils import ( - ens_encode_name, + dns_encode_name, ) from web3.exceptions import ( OffchainLookup, @@ -172,14 +172,14 @@ def test_offchain_resolver_function_call_raises_with_ccip_read_disabled( # should fail here with `ccip_read_enabled` flag set to False with pytest.raises(OffchainLookup): offchain_resolver.functions.resolve( - ens_encode_name("offchainexample.eth"), + dns_encode_name("offchainexample.eth"), ENCODED_ADDR_CALLDATA, ).call(ccip_read_enabled=False) # pass flag on specific call via ContractCaller is also an option with pytest.raises(OffchainLookup): offchain_resolver.caller(ccip_read_enabled=False).resolve( - ens_encode_name("offchainexample.eth"), + dns_encode_name("offchainexample.eth"), ENCODED_ADDR_CALLDATA, ) diff --git a/tests/ens/test_utils.py b/tests/ens/test_utils.py index 432e8c067b..008cd34fe2 100644 --- a/tests/ens/test_utils.py +++ b/tests/ens/test_utils.py @@ -1,4 +1,7 @@ import pytest +from unittest import ( + mock, +) from unittest.mock import ( patch, ) @@ -20,6 +23,7 @@ ENSValidationError, ) from ens.utils import ( + dns_encode_name, ens_encode_name, init_async_web3, init_web3, @@ -70,8 +74,8 @@ def test_init_web3_adds_expected_middleware(): (f"abc-123.{'b' * 255}", b"\x07abc-123" + b"\xff" + b"b" * 255 + b"\x00"), ), ) -def test_ens_encode_name(name, expected): - assert ens_encode_name(name) == expected +def test_dns_encode_name(name, expected): + assert dns_encode_name(name) == expected @pytest.mark.parametrize( @@ -101,19 +105,19 @@ def test_ens_encode_name(name, expected): ), ), ) -def test_ens_encode_name_validating_total_encoded_name_size(name, expected): +def test_dns_encode_name_validating_total_encoded_name_size(name, expected): # This test is important because dns validation technically limits the # total encoded domain name size to 255. ENSIP-10 expects the name to be # DNS encoded with one of the validation exceptions being that the # total encoded size can be any length. - ens_encoded = ens_encode_name(name) - assert len(ens_encoded) > 255 - assert ens_encoded == expected + dns_encoded = dns_encode_name(name) + assert len(dns_encoded) > 255 + assert dns_encoded == expected @pytest.mark.parametrize("empty_name", ("", ".", None, " ", " ")) -def test_ens_encode_name_returns_single_zero_byte_for_empty_name(empty_name): - assert ens_encode_name(empty_name) == b"\00" +def test_dns_encode_name_returns_single_zero_byte_for_empty_name(empty_name): + assert dns_encode_name(empty_name) == b"\00" @pytest.mark.parametrize( @@ -127,21 +131,21 @@ def test_ens_encode_name_returns_single_zero_byte_for_empty_name(empty_name): (f"{'a' * 255}.{'1' * 255}.{'b' * 256}", 2), ), ) -def test_ens_encode_name_raises_ValidationError_on_label_lengths_over_63( +def test_dns_encode_name_raises_validation_error_on_label_lengths_over_63( name, invalid_label_index ): with pytest.raises( ENSValidationError, match=f"Label at position {invalid_label_index} too long" ): - ens_encode_name(name) + dns_encode_name(name) -def test_ens_encode_name_normalizes_name_before_encoding(): - assert ens_encode_name("Öbb.at") == ens_encode_name("öbb.at") - assert ens_encode_name("nhÉéÉéÉé.eth") == ens_encode_name("nhéééééé.eth") - assert ens_encode_name("TESTER.eth") == ens_encode_name("tester.eth") - assert ens_encode_name("test\u200btest.com") == ens_encode_name("testtest.com") - assert ens_encode_name("O\u0308bb.at") == ens_encode_name("öbb.at") +def test_dns_encode_name_normalizes_name_before_encoding(): + assert dns_encode_name("Öbb.at") == dns_encode_name("öbb.at") + assert dns_encode_name("nhÉéÉéÉé.eth") == dns_encode_name("nhéééééé.eth") + assert dns_encode_name("TESTER.eth") == dns_encode_name("tester.eth") + assert dns_encode_name("test\u200btest.com") == dns_encode_name("testtest.com") + assert dns_encode_name("O\u0308bb.at") == dns_encode_name("öbb.at") @pytest.mark.parametrize( @@ -167,7 +171,7 @@ def test_normal_name_to_hash(name, hashed): is_valid_name, BaseENS.namehash, BaseENS.nameprep, - ens_encode_name, + dns_encode_name, raw_name_to_hash, ), ) @@ -205,6 +209,16 @@ def test_label_to_hash_normalizes_name_using_ensip15(): ) +@mock.patch("ens.utils.dns_encode_name") +def test_ens_encode_name_issues_deprecation_warning_and_calls_dns_encode_name( + mock_dns_encode_name, +): + with pytest.warns(DeprecationWarning, match=r"``ens_encode_name`` is deprecated"): + ens_encode_name("foo") + + mock_dns_encode_name.assert_called_once_with("foo") + + # -- async -- #