Skip to content

ens_encode_name -> dns_encode_name #3702

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions ens/async_ens.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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(
Expand Down
4 changes: 2 additions & 2 deletions ens/ens.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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(
Expand Down
17 changes: 14 additions & 3 deletions ens/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
Union,
cast,
)
import warnings

from eth_typing import (
Address,
Expand Down Expand Up @@ -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:
Expand All @@ -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)

Expand All @@ -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:
Expand Down
1 change: 1 addition & 0 deletions newsfragments/3700.deprecation.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Deprecate ``ens_encode_name`` in favor of ``dns_encode_name``.
1 change: 1 addition & 0 deletions newsfragments/3700.feature.rst
Original file line number Diff line number Diff line change
@@ -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``.
6 changes: 3 additions & 3 deletions tests/ens/test_offchain_resolution.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import requests

from ens.utils import (
ens_encode_name,
dns_encode_name,
)
from web3.exceptions import (
OffchainLookup,
Expand Down Expand Up @@ -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,
)

Expand Down
48 changes: 31 additions & 17 deletions tests/ens/test_utils.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import pytest
from unittest import (
mock,
)
from unittest.mock import (
patch,
)
Expand All @@ -20,6 +23,7 @@
ENSValidationError,
)
from ens.utils import (
dns_encode_name,
ens_encode_name,
init_async_web3,
init_web3,
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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(
Expand All @@ -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(
Expand All @@ -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,
),
)
Expand Down Expand Up @@ -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 -- #


Expand Down