Skip to content

Commit

Permalink
Non-breaking support for ENSIP-15
Browse files Browse the repository at this point in the history
- Add back the logic that exists for ``normalize_name`` and add a flag for normalizing via ENSIP-15 so as not to break the library. This flag will likely be removed and become the default in web3.py ``v7``.
- Add a flag on the ENS class itself that turns on ENSIP-15 normalization for all internal class methods
- Re-structure some imports and get rid of circular dependency issue
  • Loading branch information
fselmo committed Jun 20, 2023
1 parent c9cfd00 commit c87bd0f
Show file tree
Hide file tree
Showing 8 changed files with 105 additions and 64 deletions.
20 changes: 10 additions & 10 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -254,12 +254,12 @@ jobs:
environment:
TOXENV: py37-ens

py37-ens-ensip15:
py37-ensip15:
<<: *common
docker:
- image: cimg/python:3.7
environment:
TOXENV: py37-ens-ensip15
TOXENV: py37-ensip15

py37-ethpm:
<<: *ethpm_steps
Expand Down Expand Up @@ -358,12 +358,12 @@ jobs:
environment:
TOXENV: py38-ens

py38-ens-ensip15:
py38-ensip15:
<<: *common
docker:
- image: cimg/python:3.8
environment:
TOXENV: py38-ens-ensip15
TOXENV: py38-ensip15

py38-ethpm:
<<: *ethpm_steps
Expand Down Expand Up @@ -462,12 +462,12 @@ jobs:
environment:
TOXENV: py39-ens

py39-ens-ensip15:
py39-ensip15:
<<: *common
docker:
- image: cimg/python:3.9
environment:
TOXENV: py39-ens-ensip15
TOXENV: py39-ensip15

py39-ethpm:
<<: *ethpm_steps
Expand Down Expand Up @@ -565,12 +565,12 @@ jobs:
environment:
TOXENV: py310-ens

py310-ens-ensip15:
py310-ensip15:
<<: *common
docker:
- image: cimg/python:3.10
environment:
TOXENV: py310-ens-ensip15
TOXENV: py310-ensip15

py310-ethpm:
<<: *ethpm_steps
Expand Down Expand Up @@ -673,12 +673,12 @@ jobs:
environment:
TOXENV: py311-ens

py311-ens-ensip15:
py311-ensip15:
<<: *common
docker:
- image: cimg/python:3.11
environment:
TOXENV: py311-ens-ensip15
TOXENV: py311-ensip15

py311-ethpm:
<<: *ethpm_steps
Expand Down
26 changes: 18 additions & 8 deletions ens/normalization.py → ens/_normalization.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
)
import json
import os
from sys import (
version_info,
)
from typing import (
Any,
Dict,
Expand All @@ -18,12 +21,19 @@
NFD,
)

from ens.exceptions import (
from .exceptions import (
InvalidName,
)
from web3._utils.compat import (
Literal,
)

# TODO: remove once web3 supports python>=3.8
if version_info >= (3, 8):
from typing import (
Literal,
)
else:
from typing_extensions import ( # noqa: F401 type: ignore
Literal,
)


def _json_list_mapping_to_dict(
Expand Down Expand Up @@ -214,7 +224,7 @@ def _cps_to_text(cps: Union[List[List[int]], List[int]]) -> str:
return "".join(chr(cp) if isinstance(cp, int) else _cps_to_text(cp) for cp in cps)


def validate_tokens_and_get_label_type(tokens: List[Token]) -> str:
def _validate_tokens_and_get_label_type(tokens: List[Token]) -> str:
"""
Validate tokens and return the label type.
Expand Down Expand Up @@ -381,7 +391,7 @@ def _build_and_validate_label_from_tokens(tokens: List[Token]) -> Label:
nfc = NFC(chars)
token._normalized_codepoints = [ord(c) for c in nfc]

label_type = validate_tokens_and_get_label_type(tokens)
label_type = _validate_tokens_and_get_label_type(tokens)

label = Label()
label.type = label_type
Expand Down Expand Up @@ -465,8 +475,8 @@ def normalize_name_ensip15(name: str) -> ENSNormalizedName:
buffer.append(leading_codepoint)
else:
raise InvalidName(
f"Invalid codepoint: {leading_codepoint} "
f"({hex(leading_codepoint)})"
f"Invalid character: '{chr(leading_codepoint)}' | "
f"codepoint {leading_codepoint} ({hex(leading_codepoint)})"
)

if len(buffer) > 0 and len(_input) == 0:
Expand Down
12 changes: 6 additions & 6 deletions ens/async_ens.py
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ async def resolver(self, name: str) -> Optional["AsyncContract"]:
:param str name: The ENS name
"""
normal_name = normalize_name(name)
normal_name = normalize_name(name, ensip15=self.ensip15_normalization)
resolver = await self._get_resolver(normal_name)
return resolver[0]

Expand All @@ -353,7 +353,7 @@ async def get_text(self, name: str, key: str) -> str:
:raises ResolverNotFound: If no resolver is found for the provided name
"""
node = raw_name_to_hash(name)
normal_name = normalize_name(name)
normal_name = normalize_name(name, ensip15=self.ensip15_normalization)

r = await self.resolver(normal_name)
if r:
Expand Down Expand Up @@ -395,7 +395,7 @@ async def set_text(

owner = await self.owner(name)
node = raw_name_to_hash(name)
normal_name = normalize_name(name)
normal_name = normalize_name(name, ensip15=self.ensip15_normalization)

transaction_dict = merge({"from": owner}, transact)

Expand Down Expand Up @@ -466,7 +466,7 @@ async def _resolve(
name: str,
fn_name: str = "addr",
) -> Optional[Union[ChecksumAddress, str]]:
normal_name = normalize_name(name)
normal_name = normalize_name(name, ensip15=self.ensip15_normalization)

resolver, current_name = await self._get_resolver(normal_name, fn_name)
if not resolver:
Expand Down Expand Up @@ -518,7 +518,7 @@ async def _first_owner(
"""
owner = None
unowned = []
pieces = normalize_name(name).split(".")
pieces = normalize_name(name, ensip15=self.ensip15_normalization).split(".")
while pieces and is_none_or_zero_address(owner):
name = ".".join(pieces)
owner = await self.owner(name)
Expand Down Expand Up @@ -550,7 +550,7 @@ async def _setup_reverse(
address: ChecksumAddress,
transact: Optional["TxParams"] = None,
) -> HexBytes:
name = normalize_name(name) if name else ""
name = normalize_name(name, ensip15=self.ensip15_normalization) if name else ""
if not transact:
transact = {}
transact = deepcopy(transact)
Expand Down
16 changes: 9 additions & 7 deletions ens/base_ens.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
HexBytes,
)

from ens.utils import (
from .utils import (
address_to_reverse_domain,
get_abi_output_types,
is_valid_name,
Expand All @@ -41,6 +41,8 @@ class BaseENS:
_resolver_contract: Union[Type["Contract"], Type["AsyncContract"]] = None
_reverse_resolver_contract: Union[Type["Contract"], Type["AsyncContract"]] = None

ensip15_normalization: bool = False

@property
def strict_bytes_type_checking(self) -> bool:
return self.w3.strict_bytes_type_checking
Expand All @@ -56,18 +58,18 @@ def labelhash(label: str) -> HexBytes:

@staticmethod
@wraps(raw_name_to_hash)
def namehash(name: str) -> HexBytes:
return raw_name_to_hash(name)
def namehash(name: str, ensip15: bool = False) -> HexBytes:
return raw_name_to_hash(name, ensip15=ensip15)

@staticmethod
@wraps(normalize_name)
def nameprep(name: str) -> str:
return normalize_name(name)
def nameprep(name: str, ensip15: bool = False) -> str:
return normalize_name(name, ensip15=ensip15)

@staticmethod
@wraps(is_valid_name)
def is_valid_name(name: str) -> bool:
return is_valid_name(name)
def is_valid_name(name: str, ensip15: bool = False) -> bool:
return is_valid_name(name, ensip15=ensip15)

@staticmethod
@wraps(address_to_reverse_domain)
Expand Down
22 changes: 11 additions & 11 deletions ens/ens.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,27 +29,27 @@
HexBytes,
)

from ens import (
from . import (
abis,
)
from ens.base_ens import (
from .base_ens import (
BaseENS,
)
from ens.constants import (
from .constants import (
EMPTY_ADDR_HEX,
ENS_MAINNET_ADDR,
EXTENDED_RESOLVER_INTERFACE_ID,
GET_TEXT_INTERFACE_ID,
REVERSE_REGISTRAR_DOMAIN,
)
from ens.exceptions import (
from .exceptions import (
AddressMismatch,
ResolverNotFound,
UnauthorizedError,
UnownedName,
UnsupportedFunction,
)
from ens.utils import (
from .utils import (
address_in,
address_to_reverse_domain,
default,
Expand Down Expand Up @@ -326,7 +326,7 @@ def resolver(self, name: str) -> Optional["Contract"]:
:param str name: The ENS name
"""
normal_name = normalize_name(name)
normal_name = normalize_name(name, ensip15=self.ensip15_normalization)
return self._get_resolver(normal_name)[0]

def reverser(self, target_address: ChecksumAddress) -> Optional["Contract"]:
Expand All @@ -346,7 +346,7 @@ def get_text(self, name: str, key: str) -> str:
:raises ResolverNotFound: If no resolver is found for the provided name
"""
node = raw_name_to_hash(name)
normal_name = normalize_name(name)
normal_name = normalize_name(name, ensip15=self.ensip15_normalization)

r = self.resolver(normal_name)
if r:
Expand Down Expand Up @@ -388,7 +388,7 @@ def set_text(

owner = self.owner(name)
node = raw_name_to_hash(name)
normal_name = normalize_name(name)
normal_name = normalize_name(name, ensip15=self.ensip15_normalization)

transaction_dict = merge({"from": owner}, transact)

Expand Down Expand Up @@ -451,7 +451,7 @@ def _set_resolver(
def _resolve(
self, name: str, fn_name: str = "addr"
) -> Optional[Union[ChecksumAddress, str]]:
normal_name = normalize_name(name)
normal_name = normalize_name(name, ensip15=self.ensip15_normalization)

resolver, current_name = self._get_resolver(normal_name, fn_name)
if not resolver:
Expand Down Expand Up @@ -501,7 +501,7 @@ def _first_owner(
"""
owner = None
unowned = []
pieces = normalize_name(name).split(".")
pieces = normalize_name(name, ensip15=self.ensip15_normalization).split(".")
while pieces and is_none_or_zero_address(owner):
name = ".".join(pieces)
owner = self.owner(name)
Expand Down Expand Up @@ -533,7 +533,7 @@ def _setup_reverse(
address: ChecksumAddress,
transact: Optional["TxParams"] = None,
) -> HexBytes:
name = normalize_name(name) if name else ""
name = normalize_name(name, ensip15=self.ensip15_normalization) if name else ""
if not transact:
transact = {}
transact = deepcopy(transact)
Expand Down
Loading

0 comments on commit c87bd0f

Please sign in to comment.