Skip to content
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

ENS - Stop inferring .eth TLD #1205

Merged
merged 5 commits into from
Jan 17, 2019
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
5 changes: 2 additions & 3 deletions docs/ens_overview.rst
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,8 @@ Look up the address for an ENS name
assert eth_address == '0x5B2063246F2191f18F2675ceDB8b28102e957458'


# ens.py will assume you want a .eth name if you don't specify a full name

assert ns.address('jasoncarver') == eth_address
The ``ENS`` module has no opinion as to which TLD you can use,
but will not infer a TLD if it is not provided with the name.


Get name from address
Expand Down
2 changes: 0 additions & 2 deletions ens/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,4 @@

MIN_ETH_LABEL_LENGTH = 7

RECOGNIZED_TLDS = ['eth', 'reverse', 'test', 'luxe', 'xyz']

REVERSE_REGISTRAR_DOMAIN = 'addr.reverse'
40 changes: 17 additions & 23 deletions ens/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,12 @@
address_to_reverse_domain,
default,
dict_copy,
dot_eth_name,
dot_eth_namehash,
init_web3,
is_valid_name,
label_to_hash,
name_to_hash,
normal_name_to_hash,
normalize_name,
raw_name_to_hash,
)

ENS_MAINNET_ADDR = '0x314159265dD8dbb310642f98f50C066173C1259b'
Expand All @@ -43,7 +42,7 @@ class ENS:
'''

labelhash = staticmethod(label_to_hash)
namehash = staticmethod(dot_eth_namehash)
namehash = staticmethod(raw_name_to_hash)
nameprep = staticmethod(normalize_name)
is_valid_name = staticmethod(is_valid_name)
reverse_domain = staticmethod(address_to_reverse_domain)
Expand Down Expand Up @@ -72,19 +71,14 @@ def fromWeb3(cls, web3, addr=None):
'''
return cls(web3.manager.provider, addr=addr)

def address(self, name, guess_tld=True):
def address(self, name):
'''
Look up the Ethereum address that `name` currently points to.

:param str name: an ENS name to look up
:param bool guess_tld: should `name` be appended with '.eth' if no common TLD found?
:raises InvalidName: if `name` has invalid syntax
'''
if guess_tld:
expanded = dot_eth_name(name)
else:
expanded = name
return self.resolve(expanded, 'addr')
return self.resolve(name, 'addr')

def name(self, address):
'''
Expand All @@ -109,9 +103,9 @@ def setup_address(self, name, address=default, transact={}):
and calls this method with ``sub.parentname.eth``,
then ``sub`` will be created as part of this call.

:param str name: ENS name to set up, in checksum format
:param str address: name will point to this address. If ``None``, erase the record.
If not specified, name will point to the owner's address.
:param str name: ENS name to set up
:param str address: name will point to this address, in checksum format. If ``None``,
njgheorghita marked this conversation as resolved.
Show resolved Hide resolved
erase the record. If not specified, name will point to the owner's address.
:param dict transact: the transaction configuration, like in
:meth:`~web3.eth.Eth.sendTransaction`
:raises InvalidName: if ``name`` has invalid syntax
Expand All @@ -133,7 +127,7 @@ def setup_address(self, name, address=default, transact={}):
address = EMPTY_ADDR_HEX
transact['from'] = owner
resolver = self._set_resolver(name, transact=transact)
return resolver.setAddr(dot_eth_namehash(name), address, transact=transact)
return resolver.setAddr(raw_name_to_hash(name), address, transact=transact)

@dict_copy
def setup_name(self, name, address=None, transact={}):
Expand Down Expand Up @@ -183,13 +177,13 @@ def resolve(self, name, get='addr'):
resolver = self.resolver(normal_name)
if resolver:
lookup_function = getattr(resolver, get)
namehash = name_to_hash(normal_name)
namehash = normal_name_to_hash(normal_name)
return lookup_function(namehash)
else:
return None

def resolver(self, normal_name):
resolver_addr = self.ens.resolver(name_to_hash(normal_name))
resolver_addr = self.ens.resolver(normal_name_to_hash(normal_name))
if not resolver_addr:
return None
return self._resolverContract(address=resolver_addr)
Expand All @@ -209,7 +203,7 @@ def owner(self, name):
:return: owner address
:rtype: str
'''
node = dot_eth_namehash(name)
node = raw_name_to_hash(name)
return self.ens.owner(node)

@dict_copy
Expand Down Expand Up @@ -270,7 +264,7 @@ def _first_owner(self, name):
'''
owner = None
unowned = []
pieces = dot_eth_name(name).split('.')
pieces = normalize_name(name).split('.')
while pieces and not owner:
name = '.'.join(pieces)
owner = self.owner(name)
Expand All @@ -283,7 +277,7 @@ def _claim_ownership(self, owner, unowned, owned, old_owner=None, transact={}):
transact['from'] = old_owner or owner
for label in reversed(unowned):
self.ens.setSubnodeOwner(
dot_eth_namehash(owned),
raw_name_to_hash(owned),
label_to_hash(label),
owner,
transact=transact
Expand All @@ -294,7 +288,7 @@ def _claim_ownership(self, owner, unowned, owned, old_owner=None, transact={}):
def _set_resolver(self, name, resolver_addr=None, transact={}):
if not resolver_addr:
resolver_addr = self.address('resolver.eth')
namehash = dot_eth_namehash(name)
namehash = raw_name_to_hash(name)
if self.ens.resolver(namehash) != resolver_addr:
self.ens.setResolver(
namehash,
Expand All @@ -306,12 +300,12 @@ def _set_resolver(self, name, resolver_addr=None, transact={}):
@dict_copy
def _setup_reverse(self, name, address, transact={}):
if name:
name = dot_eth_name(name)
name = normalize_name(name)
else:
name = ''
transact['from'] = address
return self._reverse_registrar().setName(name, transact=transact)

def _reverse_registrar(self):
addr = self.ens.owner(name_to_hash(REVERSE_REGISTRAR_DOMAIN))
addr = self.ens.owner(normal_name_to_hash(REVERSE_REGISTRAR_DOMAIN))
return self.web3.eth.contract(address=addr, abi=abis.REVERSE_REGISTRAR)
25 changes: 5 additions & 20 deletions ens/utils.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

import copy
import datetime
import functools
Expand All @@ -16,7 +15,6 @@
AUCTION_START_GAS_MARGINAL,
EMPTY_SHA3_BYTES,
MIN_ETH_LABEL_LENGTH,
RECOGNIZED_TLDS,
REVERSE_REGISTRAR_DOMAIN,
)
from ens.exceptions import (
Expand Down Expand Up @@ -108,18 +106,6 @@ def is_valid_name(name):
return False


def label_to_name(label, default_tld, recognized_tlds):
label = normalize_name(label)
pieces = label.split('.')
if pieces[-1] not in recognized_tlds:
pieces.append(default_tld)
return '.'.join(pieces)


def dot_eth_name(label):
return label_to_name(label, 'eth', RECOGNIZED_TLDS)


def name_to_label(name, registrar):
name = normalize_name(name)
if '.' not in name:
Expand Down Expand Up @@ -171,7 +157,7 @@ def label_to_hash(label):
return Web3().keccak(text=label)


def name_to_hash(name):
def normal_name_to_hash(name):
node = EMPTY_SHA3_BYTES
if name:
labels = name.split(".")
Expand All @@ -183,15 +169,14 @@ def name_to_hash(name):
return node


def dot_eth_namehash(name):
def raw_name_to_hash(name):
'''
Generate the namehash. This is also known as the ``node`` in ENS contracts.

In normal operation, generating the namehash is handled
behind the scenes. For advanced usage, it is a helpful utility.

This will add '.eth' to name if no TLD given. Also, it normalizes the name with
`nameprep
This normalizes the name with `nameprep
<https://github.com/ethereum/EIPs/blob/master/EIPS/eip-137.md#name-syntax>`_
before hashing.

Expand All @@ -200,8 +185,8 @@ def dot_eth_namehash(name):
:rtype: bytes
:raises InvalidName: if ``name`` has invalid syntax
'''
expanded_name = dot_eth_name(name)
return name_to_hash(expanded_name)
normalized_name = normalize_name(name)
return normal_name_to_hash(normalized_name)


def address_in(address, addresses):
Expand Down
Binary file added tests/.DS_Store
Binary file not shown.
Binary file added tests/core/.DS_Store
Binary file not shown.
Binary file added tests/core/pm-module/.DS_Store
Binary file not shown.
29 changes: 9 additions & 20 deletions tests/ens/test_setup_address.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

import pytest
from unittest.mock import (
patch,
Expand Down Expand Up @@ -32,12 +31,7 @@
'0x2a7ac1c833d35677c2ff34a908951de142cc1653de6080ad4e38f4c9cc00aafe',
),
(
'tester',
'tester.eth',
'0x2a7ac1c833d35677c2ff34a908951de142cc1653de6080ad4e38f4c9cc00aafe',
),
(
'TESTER',
'TESTER.eth',
'TESTER.eth',
'0x2a7ac1c833d35677c2ff34a908951de142cc1653de6080ad4e38f4c9cc00aafe',
),
Expand All @@ -63,27 +57,19 @@
'lots.of.subdomains.tester.eth',
'0x0d62a759aa1f1c9680de8603a12a5eb175cd1bfa79426229868eba99f4dce692',
),
(
'lots.of.subdomains.tester',
'lots.of.subdomains.tester.eth',
'0x0d62a759aa1f1c9680de8603a12a5eb175cd1bfa79426229868eba99f4dce692',
),
],
)
def test_set_address(ens, name, full_name, namehash_hex, TEST_ADDRESS):
assert ens.address(name) is None
owner = ens.owner('tester')
owner = ens.owner('tester.eth')

ens.setup_address(name, TEST_ADDRESS)
assert is_same_address(ens.address(name), TEST_ADDRESS)

# check that .eth is only appended if guess_tld is True
namehash = Web3.toBytes(hexstr=namehash_hex)
normal_name = ens.nameprep(full_name)
if ens.nameprep(name) == normal_name:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can either drop this if test, or have an else: assert False, "all names must be full names" below.

assert is_same_address(ens.address(name, guess_tld=False), TEST_ADDRESS)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think just this assertion should stay in, omitting guess_tld of course.

else:
assert ens.address(name, guess_tld=False) is None
assert is_same_address(ens.address(name), TEST_ADDRESS)

# check that the correct namehash is set:
assert is_same_address(ens.resolver(normal_name).addr(namehash), TEST_ADDRESS)
Expand All @@ -98,7 +84,7 @@ def test_set_address(ens, name, full_name, namehash_hex, TEST_ADDRESS):
@pytest.mark.parametrize(
'name, equivalent',
[
('TESTER', 'tester.eth'),
('TESTER.eth', 'tester.eth'),
('unicÖde.tester.eth', 'unicöde.tester.eth'),
],
)
Expand Down Expand Up @@ -165,12 +151,15 @@ def getowner(name):


def test_set_resolver_leave_default(ens, TEST_ADDRESS):
owner = ens.owner('tester')
owner = ens.owner('tester.eth')
ens.setup_address('leave-default-resolver.tester.eth', TEST_ADDRESS)
eth = ens.web3.eth
num_transactions = eth.getTransactionCount(owner)

ens.setup_address('leave-default-resolver.tester', '0x5B2063246F2191f18F2675ceDB8b28102e957458')
ens.setup_address(
'leave-default-resolver.tester.eth',
'0x5B2063246F2191f18F2675ceDB8b28102e957458'
)

# should skip setting the owner and setting the default resolver, and only
# set the name in the default resolver to point to the new address
Expand Down
19 changes: 2 additions & 17 deletions tests/ens/test_setup_name.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

import pytest

from ens.main import (
Expand Down Expand Up @@ -28,12 +27,7 @@ def TEST_ADDRESS(address_conversion_func):
'2a7ac1c833d35677c2ff34a908951de142cc1653de6080ad4e38f4c9cc00aafe',
),
(
'tester',
'tester.eth',
'2a7ac1c833d35677c2ff34a908951de142cc1653de6080ad4e38f4c9cc00aafe',
),
(
'TESTER',
'TESTER.eth',
'tester.eth',
'2a7ac1c833d35677c2ff34a908951de142cc1653de6080ad4e38f4c9cc00aafe',
),
Expand All @@ -58,25 +52,16 @@ def TEST_ADDRESS(address_conversion_func):
'lots.of.subdomains.tester.eth',
'0d62a759aa1f1c9680de8603a12a5eb175cd1bfa79426229868eba99f4dce692',
),
(
'lots.of.subdomains.tester',
'lots.of.subdomains.tester.eth',
'0d62a759aa1f1c9680de8603a12a5eb175cd1bfa79426229868eba99f4dce692',
),
],
)
def test_setup_name(ens, name, normalized_name, namehash_hex):
address = ens.web3.eth.accounts[3]
assert not ens.name(address)
owner = ens.owner('tester')
owner = ens.owner('tester.eth')

ens.setup_name(name, address)
assert ens.name(address) == normalized_name

# check that .eth is only appended if guess_tld is True
if ens.nameprep(name) != normalized_name:
assert ens.address(name, guess_tld=False) is None

# check that the correct namehash is set:
node = Web3.toBytes(hexstr=namehash_hex)
assert ens.resolver(normalized_name).addr(node) == address
Expand Down