Skip to content

Commit

Permalink
labelhash > namehash
Browse files Browse the repository at this point in the history
  • Loading branch information
TateB committed Apr 8, 2024
1 parent 056a364 commit 29287bd
Show file tree
Hide file tree
Showing 8 changed files with 90 additions and 60 deletions.
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"solidity.packageDefaultDependenciesContractsDirectory": "./contracts/src",
"solidity.packageDefaultDependenciesDirectory": "./contracts/lib"
"solidity.packageDefaultDependenciesDirectory": "./contracts/lib",
"solidity.compileUsingRemoteVersion": "v0.8.24+commit.e11b9ed9"
}
11 changes: 11 additions & 0 deletions contracts/src/BytesUtils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,17 @@ library BytesUtils {
return result;
}

function namehashUntilLabelOffset(
uint256[] memory labelArray,
uint256 labelOffset
) internal pure returns (uint256) {
bytes32 namehash = 0;
for (uint256 i = labelArray.length; i > labelOffset; i--) {
namehash = keccak256(abi.encodePacked(namehash, labelArray[i - 1]));
}
return uint256(namehash);
}

// function readNthLabelFromChild(
// bytes memory self,
// uint256 labelOffset
Expand Down
28 changes: 14 additions & 14 deletions contracts/src/EnsEthRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ contract EnsEthRegistry is IEnsReadRegistry {

mapping(uint256 => Node) nodes;

modifier authorized(bytes32 labelhash) {
address nodeOwner = ownerOf(uint256(labelhash));
modifier authorized(bytes32 namehash) {
address nodeOwner = ownerOf(uint256(namehash));
if (nodeOwner == msg.sender) {
_;
}
Expand All @@ -35,45 +35,45 @@ contract EnsEthRegistry is IEnsReadRegistry {
ejectionAuthority = _ejectionAuthority;
}

function getNode(uint256 tokenId) public view returns (Node memory) {
Node memory node = nodes[tokenId];
function getNode(uint256 namehash) public view returns (Node memory) {
Node memory node = nodes[namehash];
if (node.expiry < block.timestamp) {
return nodes[0];
}
return node;
}

function oneifyName(
uint256 tokenId,
uint256 namehash,
uint256 expiry,
address owner,
address _resolver,
address registry
) public onlyEjectionAuthority {
nodes[tokenId] = Node(owner, _resolver, registry, expiry);
nodes[namehash] = Node(owner, _resolver, registry, expiry);
}

function ownerOf(uint256 tokenId) public view returns (address) {
Node memory node = getNode(tokenId);
function ownerOf(uint256 namehash) public view returns (address) {
Node memory node = getNode(namehash);
return node.owner;
}

function resolver(uint256 tokenId) external view returns (address) {
Node memory node = getNode(tokenId);
function resolver(uint256 namehash) external view returns (address) {
Node memory node = getNode(namehash);
return node.resolver;
}

function recordExists(uint256 tokenId) external view returns (bool) {
Node memory node = getNode(tokenId);
function recordExists(uint256 namehash) external view returns (bool) {
Node memory node = getNode(namehash);
if (node.owner != address(0)) return true;
if (node.registry != address(0)) return true;
return false;
}

function getRegistry(
uint256 tokenId
uint256 namehash
) external view returns (IEnsReadRegistry) {
Node memory node = getNode(tokenId);
Node memory node = getNode(namehash);
return IEnsReadRegistry(node.registry);
}
}
39 changes: 20 additions & 19 deletions contracts/src/EnsRootRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ error Unauthorized();

contract EnsRootRegistry {
using BytesUtils for bytes;
using BytesUtils for uint256[];

address public immutable owner;

Expand Down Expand Up @@ -46,51 +47,51 @@ contract EnsRootRegistry {
tldRegistries[labelhash] = tldRegistry;
}

function getRegistry(
bytes calldata name
) external view returns (IEnsReadRegistry) {
(IEnsReadRegistry registry, ) = getRegistry(name, 0);
return registry;
}

function getRegistry(
bytes calldata name,
uint256 labelOffset
)
internal
view
returns (IEnsReadRegistry, bool isExact, uint256 labelhash)
{
) internal view returns (IEnsReadRegistry, uint256 labelhash) {
uint256[] memory labelArray = name.readLabelsToArray();
uint256 tldLabelhash = labelArray[labelArray.length - 1];

uint256 fullNamehash = labelArray.namehashUntilLabelOffset(0);

IEnsReadRegistry tldRegistry = getTldRegistry(tldLabelhash);

if (address(tldRegistry) == address(0) || labelArray.length == 1) {
isExact = labelArray.length == 1;
return (tldRegistry, isExact, tldLabelhash);
return (tldRegistry, fullNamehash);
}

IEnsReadRegistry previousRegistry = tldRegistry;

for (uint256 i = labelArray.length - 2; i > labelOffset; i--) {
uint256 namehash = labelArray.namehashUntilLabelOffset(i);
IEnsReadRegistry newRegistry = previousRegistry.getRegistry(
labelArray[i]
namehash
);
if (address(newRegistry) == address(0)) {
return (previousRegistry, false, labelArray[i + 1]);
return (previousRegistry, fullNamehash);
}
}

return (previousRegistry, true, labelArray[labelOffset]);
return (previousRegistry, fullNamehash);
}

function resolver(bytes calldata name) public view returns (address) {
(IEnsReadRegistry registry, , uint256 labelhash) = getRegistry(name, 0);
return registry.resolver(labelhash);
(IEnsReadRegistry registry, uint256 namehash) = getRegistry(name, 0);
return registry.resolver(namehash);
}

function ownerOf(bytes calldata name) public view returns (address) {
(
IEnsReadRegistry registry,
bool isExact,
uint256 labelhash
) = getRegistry(name, 0);
if (!isExact) return address(0);
return registry.ownerOf(labelhash);
(IEnsReadRegistry registry, uint256 namehash) = getRegistry(name, 1);
return registry.ownerOf(namehash);
}

function recordExists(uint256 labelhash) public view returns (bool) {
Expand Down
14 changes: 0 additions & 14 deletions contracts/src/IENSRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,6 @@
pragma solidity >=0.8.13;

interface IENSRegistry {
// Logged when the owner of a node transfers ownership to a new account.
event Transfer(bytes32 indexed node, address owner);

// Logged when the resolver for a node changes.
event NewResolver(bytes32 indexed node, address resolver);

function setRecord(bytes32 node, address owner, address resolver) external;

function setResolver(bytes32 node, address resolver) external;

function setOwner(bytes32 node, address owner) external;

function setApprovalForAll(address operator, bool approved) external;

function owner(bytes32 node) external view returns (address);

function resolver(bytes32 node) external view returns (address);
Expand Down
15 changes: 15 additions & 0 deletions contracts/test/BytesUtils.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {NameEncoder} from "./NameEncoder.sol";

contract BytesUtilsTest is Test {
using BytesUtils for bytes;
using BytesUtils for uint256[];
using NameEncoder for string;

function test_readLabelsToArray() public view {
Expand All @@ -19,6 +20,20 @@ contract BytesUtilsTest is Test {
assertEq(labelhashArray[2], uint256(keccak256("eth")));
}

function test_namehashUntilLabelOffset_zeroOffset() public pure {
uint256[] memory labelArray = new uint256[](3);
labelArray[0] = uint256(keccak256("one"));
labelArray[1] = uint256(keccak256("test"));
labelArray[2] = uint256(keccak256("eth"));
uint256 result = labelArray.namehashUntilLabelOffset(0);
assertEq(
result,
uint256(
0xdd0d907a053c5f43f9a1814db5dc1a0a4d979a740a04fd56146f63e22fb42f19
)
);
}

function calldatawrite(
bytes calldata self
) external pure returns (uint256[] memory) {
Expand Down
33 changes: 22 additions & 11 deletions contracts/test/Ens.t.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
import { expect } from "chai";
import hre from "hardhat";
import { Address, bytesToHex, getAddress } from "viem";
import { labelhash, packetToBytes } from "./utils";
import { labelhashUint256, namehashUint256, packetToBytes } from "./utils";

describe("Ens", function () {
async function deployEnsFixture() {
Expand All @@ -17,7 +17,7 @@ describe("Ens", function () {
walletClients[0].account.address,
]);
await ensRootRegistry.write.setTldRegistry([
labelhash("eth"),
labelhashUint256("eth"),
ensEthRegistry.address,
]);

Expand All @@ -28,13 +28,13 @@ describe("Ens", function () {

const oneifyName = async ({
ensEthRegistry,
label,
name,
expiry = BigInt(Math.floor(Date.now() / 1000) + 1000000),
owner: owner_,
resolver = "0x0000000000000000000000000000000000000000",
registry = "0x0000000000000000000000000000000000000000",
}: Pick<EnsFixture, "ensEthRegistry"> & {
label: string;
name: string;
expiry?: bigint;
owner?: Address;
resolver?: Address;
Expand All @@ -43,7 +43,7 @@ describe("Ens", function () {
const owner =
owner_ ?? (await hre.viem.getWalletClients())[0].account.address;
await ensEthRegistry.write.oneifyName([
labelhash(label),
namehashUint256(name),
expiry,
owner,
resolver,
Expand All @@ -56,17 +56,28 @@ describe("Ens", function () {
deployEnsFixture
);
const tldRegistry = await ensRootRegistry.read.getTldRegistry([
labelhash("eth"),
labelhashUint256("eth"),
]);
expect(tldRegistry).to.equal(getAddress(ensEthRegistry.address));
});

it("returns correct registry for test.eth", async () => {
const { ensRootRegistry, ensEthRegistry } = await loadFixture(
deployEnsFixture
);
await oneifyName({ ensEthRegistry, name: "test.eth" });
const registry = await ensRootRegistry.read.getRegistry([
bytesToHex(packetToBytes("test.eth")),
]);
expect(registry).to.equal(getAddress(ensEthRegistry.address));
});

it("returns owner when oneified", async () => {
const { ensRootRegistry, ensEthRegistry } = await loadFixture(
deployEnsFixture
);
const walletClients = await hre.viem.getWalletClients();
await oneifyName({ ensEthRegistry, label: "test" });
await oneifyName({ ensEthRegistry, name: "test.eth" });
const owner = await ensRootRegistry.read.ownerOf([
bytesToHex(packetToBytes("test.eth")),
]);
Expand All @@ -80,7 +91,7 @@ describe("Ens", function () {
const walletClients = await hre.viem.getWalletClients();
await oneifyName({
ensEthRegistry,
label: "test",
name: "test.eth",
resolver: walletClients[1].account.address,
});
const resolver = await ensRootRegistry.read.resolver([
Expand All @@ -92,15 +103,15 @@ describe("Ens", function () {
it("returns recordExists as true when tld exists", async () => {
const { ensRootRegistry } = await loadFixture(deployEnsFixture);
const recordExists = await ensRootRegistry.read.recordExists([
labelhash("eth"),
labelhashUint256("eth"),
]);
expect(recordExists).to.equal(true);
});

it("returns recordExists as false when tld does not exist", async () => {
const { ensRootRegistry } = await loadFixture(deployEnsFixture);
const recordExists = await ensRootRegistry.read.recordExists([
labelhash("test"),
labelhashUint256("test"),
]);
expect(recordExists).to.equal(false);
});
Expand All @@ -110,7 +121,7 @@ describe("Ens", function () {
deployEnsFixture
);
const walletClients = await hre.viem.getWalletClients();
await oneifyName({ ensEthRegistry, label: "test" });
await oneifyName({ ensEthRegistry, name: "test.eth" });
const owner = await ensRootRegistry.read.ownerOf([
bytesToHex(packetToBytes("test.eth")),
]);
Expand Down
7 changes: 6 additions & 1 deletion contracts/test/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
hexToBigInt,
labelhash as labelhashBytes32,
namehash,
stringToBytes,
type ByteArray,
} from "viem";
Expand Down Expand Up @@ -38,6 +39,10 @@ export function encodeLabelhash(hash: string) {
return `[${hash.slice(2)}]`;
}

export const labelhash = (label: string): bigint => {
export const labelhashUint256 = (label: string): bigint => {
return hexToBigInt(labelhashBytes32(label));
};

export const namehashUint256 = (name: string): bigint => {
return hexToBigInt(namehash(name));
};

0 comments on commit 29287bd

Please sign in to comment.