diff --git a/contracts/dnsregistrar/DNSClaimChecker.sol b/contracts/dnsregistrar/DNSClaimChecker.sol index 63befda7..8df8c7da 100644 --- a/contracts/dnsregistrar/DNSClaimChecker.sol +++ b/contracts/dnsregistrar/DNSClaimChecker.sol @@ -40,11 +40,11 @@ library DNSClaimChecker { return (address(0x0), false); } - function parseRR(bytes memory rdata, uint256 idx, uint256 endIdx) - internal - pure - returns (address, bool) - { + function parseRR( + bytes memory rdata, + uint256 idx, + uint256 endIdx + ) internal pure returns (address, bool) { while (idx < endIdx) { uint256 len = rdata.readUint8(idx); idx += 1; diff --git a/contracts/dnsregistrar/DNSRegistrar.sol b/contracts/dnsregistrar/DNSRegistrar.sol index f3e030e1..953a9a3e 100644 --- a/contracts/dnsregistrar/DNSRegistrar.sol +++ b/contracts/dnsregistrar/DNSRegistrar.sol @@ -108,12 +108,12 @@ contract DNSRegistrar is IDNSRegistrar, IERC165 { name, input ); - if(msg.sender != owner) { + if (msg.sender != owner) { revert PermissionDenied(msg.sender, owner); } ens.setSubnodeRecord(rootNode, labelHash, owner, resolver, 0); if (addr != address(0)) { - if(resolver == address(0)) { + if (resolver == address(0)) { revert PreconditionNotMet(); } bytes32 node = keccak256(abi.encodePacked(rootNode, labelHash)); @@ -156,28 +156,25 @@ contract DNSRegistrar is IDNSRegistrar, IERC165 { bool found; (addr, found) = DNSClaimChecker.getOwnerAddress(name, data); - if(!found) { + if (!found) { revert NoOwnerRecordFound(); } emit Claim(node, addr, name, inception); } - function enableNode(bytes memory domain) - public - returns (bytes32 node) - { + function enableNode(bytes memory domain) public returns (bytes32 node) { // Name must be in the public suffix list. - if(!suffixes.isPublicSuffix(domain)) { + if (!suffixes.isPublicSuffix(domain)) { revert InvalidPublicSuffix(domain); } return _enableNode(domain, 0); } - function _enableNode(bytes memory domain, uint256 offset) - internal - returns(bytes32 node) - { + function _enableNode( + bytes memory domain, + uint256 offset + ) internal returns (bytes32 node) { uint256 len = domain.readUint8(offset); if (len == 0) { return bytes32(0); @@ -193,9 +190,15 @@ contract DNSRegistrar is IDNSRegistrar, IERC165 { root.setSubnodeOwner(label, address(this)); ens.setResolver(node, resolver); } else { - ens.setSubnodeRecord(parentNode, label, address(this), resolver, 0); + ens.setSubnodeRecord( + parentNode, + label, + address(this), + resolver, + 0 + ); } - } else if(owner != address(this)) { + } else if (owner != address(this)) { revert PreconditionNotMet(); } return node; diff --git a/contracts/dnsregistrar/OffchainDNSResolver.sol b/contracts/dnsregistrar/OffchainDNSResolver.sol index b8e94173..e5071676 100644 --- a/contracts/dnsregistrar/OffchainDNSResolver.sol +++ b/contracts/dnsregistrar/OffchainDNSResolver.sol @@ -19,7 +19,10 @@ error OffchainLookup( ); interface IDNSGateway { - function resolve(bytes memory name, uint16 qtype) external returns(DNSSEC.RRSetWithSignature[] memory); + function resolve( + bytes memory name, + uint16 qtype + ) external returns (DNSSEC.RRSetWithSignature[] memory); } uint16 constant CLASS_INET = 1; @@ -57,14 +60,19 @@ contract OffchainDNSResolver is IExtendedResolver { ); } - function resolveCallback(bytes calldata response, bytes calldata extraData) - external - view - returns (bytes memory) - { - (bytes memory name, bytes memory query) = abi.decode(extraData, (bytes, bytes)); - DNSSEC.RRSetWithSignature[] memory rrsets = abi.decode(response, (DNSSEC.RRSetWithSignature[])); - + function resolveCallback( + bytes calldata response, + bytes calldata extraData + ) external view returns (bytes memory) { + (bytes memory name, bytes memory query) = abi.decode( + extraData, + (bytes, bytes) + ); + DNSSEC.RRSetWithSignature[] memory rrsets = abi.decode( + response, + (DNSSEC.RRSetWithSignature[]) + ); + (bytes memory data, ) = oracle.verifyRRSet(rrsets); for ( RRUtils.RRIterator memory iter = data.iterateRRs(0); @@ -73,22 +81,44 @@ contract OffchainDNSResolver is IExtendedResolver { ) { // Ignore records with wrong name, type, or class bytes memory rrname = RRUtils.readName(iter.data, iter.offset); - if(!rrname.equals(name) || iter.class != CLASS_INET || iter.dnstype != TYPE_TXT) { + if ( + !rrname.equals(name) || + iter.class != CLASS_INET || + iter.dnstype != TYPE_TXT + ) { continue; } // Look for a valid ENS-DNS TXT record - (address dnsresolver, bytes memory context) = parseRR(iter.data, iter.rdataOffset, iter.nextOffset); + (address dnsresolver, bytes memory context) = parseRR( + iter.data, + iter.rdataOffset, + iter.nextOffset + ); // If we found a valid record, try to resolve it - if(dnsresolver != address(0)) { - if(IERC165(dnsresolver).supportsInterface(IExtendedDNSResolver.resolve.selector)) { - return IExtendedDNSResolver(dnsresolver).resolve(name, query, context); - } else if(IERC165(dnsresolver).supportsInterface(IExtendedResolver.resolve.selector)) { + if (dnsresolver != address(0)) { + if ( + IERC165(dnsresolver).supportsInterface( + IExtendedDNSResolver.resolve.selector + ) + ) { + return + IExtendedDNSResolver(dnsresolver).resolve( + name, + query, + context + ); + } else if ( + IERC165(dnsresolver).supportsInterface( + IExtendedResolver.resolve.selector + ) + ) { return IExtendedResolver(dnsresolver).resolve(name, query); } else { - (bool ok, bytes memory ret) = address(dnsresolver).staticcall(query); - if(ok) { + (bool ok, bytes memory ret) = address(dnsresolver) + .staticcall(query); + if (ok) { return ret; } else { revert CouldNotResolve(name); @@ -96,51 +126,73 @@ contract OffchainDNSResolver is IExtendedResolver { } } } - + // No valid records; revert. revert CouldNotResolve(name); } - function parseRR(bytes memory data, uint256 idx, uint256 lastIdx) internal view returns (address, bytes memory) { + function parseRR( + bytes memory data, + uint256 idx, + uint256 lastIdx + ) internal view returns (address, bytes memory) { bytes memory txt = readTXT(data, idx, lastIdx); - + // Must start with the magic word - if(txt.length < 5 || !txt.equals(0, "ENS1 ", 0, 5)) { + if (txt.length < 5 || !txt.equals(0, "ENS1 ", 0, 5)) { return (address(0), ""); } // Parse the name or address uint256 lastTxtIdx = txt.find(5, txt.length - 5, " "); - if(lastTxtIdx > txt.length) { + if (lastTxtIdx > txt.length) { address dnsResolver = parseAndResolve(txt, 5, txt.length); return (dnsResolver, ""); } else { address dnsResolver = parseAndResolve(txt, 5, lastTxtIdx); - return (dnsResolver, txt.substring(lastTxtIdx + 1, txt.length - lastTxtIdx - 1)); + return ( + dnsResolver, + txt.substring(lastTxtIdx + 1, txt.length - lastTxtIdx - 1) + ); } } - function readTXT(bytes memory data, uint256 startIdx, uint256 lastIdx) internal pure returns(bytes memory) { + function readTXT( + bytes memory data, + uint256 startIdx, + uint256 lastIdx + ) internal pure returns (bytes memory) { // TODO: Concatenate multiple text fields uint256 fieldLength = data.readUint8(startIdx); assert(startIdx + fieldLength < lastIdx); return data.substring(startIdx + 1, fieldLength); } - function parseAndResolve(bytes memory nameOrAddress, uint256 idx, uint256 lastIdx) internal view returns(address) { - if(nameOrAddress[idx] == '0' && nameOrAddress[idx + 1] == 'x') { - (address ret, bool valid) = nameOrAddress.hexToAddress(idx + 2, lastIdx); - if(valid) { + function parseAndResolve( + bytes memory nameOrAddress, + uint256 idx, + uint256 lastIdx + ) internal view returns (address) { + if (nameOrAddress[idx] == "0" && nameOrAddress[idx + 1] == "x") { + (address ret, bool valid) = nameOrAddress.hexToAddress( + idx + 2, + lastIdx + ); + if (valid) { return ret; } } return resolveName(nameOrAddress, idx, lastIdx); } - function resolveName(bytes memory name, uint256 idx, uint256 lastIdx) internal view returns(address) { + function resolveName( + bytes memory name, + uint256 idx, + uint256 lastIdx + ) internal view returns (address) { bytes32 node = textNamehash(name, idx, lastIdx); address resolver = ens.resolver(node); - if(resolver == address(0)) { + if (resolver == address(0)) { return address(0); } return IAddrResolver(resolver).addr(node); @@ -152,14 +204,21 @@ contract OffchainDNSResolver is IExtendedResolver { * @param idx Index to start at * @param lastIdx Index to end at */ - function textNamehash(bytes memory name, uint256 idx, uint256 lastIdx) internal view returns(bytes32) { - uint256 separator = name.find(idx, name.length - idx, bytes1('.')); + function textNamehash( + bytes memory name, + uint256 idx, + uint256 lastIdx + ) internal view returns (bytes32) { + uint256 separator = name.find(idx, name.length - idx, bytes1(".")); bytes32 parentNode = bytes32(0); - if(separator < lastIdx) { + if (separator < lastIdx) { parentNode = textNamehash(name, separator + 1, lastIdx); } else { separator = lastIdx; } - return keccak256(abi.encodePacked(parentNode, name.keccak(idx, separator - idx))); + return + keccak256( + abi.encodePacked(parentNode, name.keccak(idx, separator - idx)) + ); } } diff --git a/contracts/dnsregistrar/mocks/DummyExtendedDNSSECResolver.sol b/contracts/dnsregistrar/mocks/DummyExtendedDNSSECResolver.sol index d4f50149..fe52fabb 100644 --- a/contracts/dnsregistrar/mocks/DummyExtendedDNSSECResolver.sol +++ b/contracts/dnsregistrar/mocks/DummyExtendedDNSSECResolver.sol @@ -5,15 +5,17 @@ import "../../resolvers/profiles/IExtendedDNSResolver.sol"; import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; contract DummyExtendedDNSSECResolver is IExtendedDNSResolver, IERC165 { - function supportsInterface(bytes4 interfaceId) external pure override returns (bool) { + function supportsInterface( + bytes4 interfaceId + ) external pure override returns (bool) { return interfaceId == type(IExtendedDNSResolver).interfaceId; } - function resolve(bytes memory /* name */, bytes memory /* data */, bytes memory context) - external - view - override - returns (bytes memory) { - return abi.encode(context); - } + function resolve( + bytes memory /* name */, + bytes memory /* data */, + bytes memory context + ) external view override returns (bytes memory) { + return abi.encode(context); + } } diff --git a/contracts/dnsregistrar/mocks/DummyLegacyTextResolver.sol b/contracts/dnsregistrar/mocks/DummyLegacyTextResolver.sol index 9a90482c..3119722b 100644 --- a/contracts/dnsregistrar/mocks/DummyLegacyTextResolver.sol +++ b/contracts/dnsregistrar/mocks/DummyLegacyTextResolver.sol @@ -5,11 +5,16 @@ import "../../resolvers/profiles/ITextResolver.sol"; import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; contract DummyLegacyTextResolver is ITextResolver, IERC165 { - function supportsInterface(bytes4 interfaceId) external pure override returns (bool) { + function supportsInterface( + bytes4 interfaceId + ) external pure override returns (bool) { return interfaceId == type(ITextResolver).interfaceId; } - function text(bytes32 /* node */, string calldata key) external view override returns (string memory) { + function text( + bytes32 /* node */, + string calldata key + ) external view override returns (string memory) { return key; } } diff --git a/contracts/dnssec-oracle/BytesUtils.sol b/contracts/dnssec-oracle/BytesUtils.sol index a70fd4e5..5f04ead7 100644 --- a/contracts/dnssec-oracle/BytesUtils.sol +++ b/contracts/dnssec-oracle/BytesUtils.sol @@ -404,11 +404,11 @@ library BytesUtils { * @param idx The offset to start parsing at * @param lastIdx The (exclusive) last index in `str` to consider. Use `str.length` to scan the whole string. */ - function hexToAddress(bytes memory str, uint256 idx, uint256 lastIdx) - internal - pure - returns (address, bool) - { + function hexToAddress( + bytes memory str, + uint256 idx, + uint256 lastIdx + ) internal pure returns (address, bool) { if (lastIdx - idx < 40) return (address(0x0), false); uint256 ret = 0; for (uint256 i = idx; i < idx + 40; i++) { diff --git a/contracts/resolvers/profiles/ExtendedResolver.sol b/contracts/resolvers/profiles/ExtendedResolver.sol index 1707027b..e3159c34 100644 --- a/contracts/resolvers/profiles/ExtendedResolver.sol +++ b/contracts/resolvers/profiles/ExtendedResolver.sol @@ -2,13 +2,12 @@ pragma solidity ^0.8.4; contract ExtendedResolver { - function resolve(bytes memory /* name */, bytes memory data) - external - view - returns (bytes memory) - { + function resolve( + bytes memory /* name */, + bytes memory data + ) external view returns (bytes memory) { (bool success, bytes memory result) = address(this).staticcall(data); - if(success) { + if (success) { return result; } else { // Revert with the reason provided by the call diff --git a/contracts/resolvers/profiles/IExtendedDNSResolver.sol b/contracts/resolvers/profiles/IExtendedDNSResolver.sol index 0513cbc8..f59a07eb 100644 --- a/contracts/resolvers/profiles/IExtendedDNSResolver.sol +++ b/contracts/resolvers/profiles/IExtendedDNSResolver.sol @@ -2,8 +2,9 @@ pragma solidity ^0.8.4; interface IExtendedDNSResolver { - function resolve(bytes memory name, bytes memory data, bytes memory context) - external - view - returns (bytes memory); + function resolve( + bytes memory name, + bytes memory data, + bytes memory context + ) external view returns (bytes memory); } diff --git a/contracts/resolvers/profiles/IExtendedResolver.sol b/contracts/resolvers/profiles/IExtendedResolver.sol index 5d6aa03d..c5fbb217 100644 --- a/contracts/resolvers/profiles/IExtendedResolver.sol +++ b/contracts/resolvers/profiles/IExtendedResolver.sol @@ -2,8 +2,8 @@ pragma solidity ^0.8.4; interface IExtendedResolver { - function resolve(bytes memory name, bytes memory data) - external - view - returns (bytes memory); + function resolve( + bytes memory name, + bytes memory data + ) external view returns (bytes memory); } diff --git a/deploy/dnsregistrar/00_deploy_offchain_dns_resolver.ts b/deploy/dnsregistrar/00_deploy_offchain_dns_resolver.ts index 759fe7d9..f9147e27 100644 --- a/deploy/dnsregistrar/00_deploy_offchain_dns_resolver.ts +++ b/deploy/dnsregistrar/00_deploy_offchain_dns_resolver.ts @@ -12,10 +12,14 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { const tx = await deploy('OffchainDNSResolver', { from: deployer, - args: [registry.address, dnssec.address, "https://dnssec-oracle.ens.domains/"], + args: [ + registry.address, + dnssec.address, + 'https://dnssec-oracle.ens.domains/', + ], log: true, }) - console.log(`Deployed OffchainDNSResolver to ${tx.address}`); + console.log(`Deployed OffchainDNSResolver to ${tx.address}`) } func.tags = ['OffchainDNSResolver'] diff --git a/deploy/dnsregistrar/10_deploy_dnsregistrar.ts b/deploy/dnsregistrar/10_deploy_dnsregistrar.ts index 3186c49a..013ab572 100644 --- a/deploy/dnsregistrar/10_deploy_dnsregistrar.ts +++ b/deploy/dnsregistrar/10_deploy_dnsregistrar.ts @@ -26,15 +26,15 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { resolver.address, dnssec.address, publicSuffixList.address, - registry.address + registry.address, ], log: true, }) - console.log(`Deployed DNSRegistrar to ${tx.address}`); + console.log(`Deployed DNSRegistrar to ${tx.address}`) const tx2 = await root .connect(await ethers.getSigner(owner)) - .setController(tx.address, true); + .setController(tx.address, true) console.log(`Set DNSRegistrar as controller of Root (${tx2.hash})`) } diff --git a/package.json b/package.json index 2c8800e6..29e8d32b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@ensdomains/ens-contracts", - "version": "0.0.13", + "version": "0.0.19", "description": "ENS contracts", "scripts": { "test": "hardhat test", diff --git a/test/dnsregistrar/TestDNSRegistrar.js b/test/dnsregistrar/TestDNSRegistrar.js index 101cf309..a3220a69 100644 --- a/test/dnsregistrar/TestDNSRegistrar.js +++ b/test/dnsregistrar/TestDNSRegistrar.js @@ -190,7 +190,7 @@ contract('DNSRegistrar', function (accounts) { ZERO_ADDRESS, ZERO_ADDRESS, ) - await resolver.setApprovalForAll(registrar.address, true); + await resolver.setApprovalForAll(registrar.address, true) const proof = [ hexEncodeSignedSet(rootKeys(expiration, inception)), @@ -225,10 +225,7 @@ contract('DNSRegistrar', function (accounts) { it('does not allow setting the owner to 0 with an empty record', async () => { await exceptions.expectFailure( - registrar.proveAndClaim( - utils.hexEncodeName('foo.test'), - [] - ) + registrar.proveAndClaim(utils.hexEncodeName('foo.test'), []), ) }) }) diff --git a/test/dnsregistrar/TestOffchainDNSResolver.js b/test/dnsregistrar/TestOffchainDNSResolver.js index 5bde5e13..2a3c8ef5 100644 --- a/test/dnsregistrar/TestOffchainDNSResolver.js +++ b/test/dnsregistrar/TestOffchainDNSResolver.js @@ -6,8 +6,12 @@ const DNSRegistrarContract = artifacts.require('./DNSRegistrar.sol') const OwnedResolver = artifacts.require('./OwnedResolver.sol') const OffchainDNSResolver = artifacts.require('./OffchainDNSResolver.sol') const PublicResolver = artifacts.require('./PublicResolver.sol') -const DummyExtendedDNSSECResolver = artifacts.require('./DummyExtendedDNSSECResolver.sol') -const DummyLegacyTextResolver = artifacts.require('./DummyLegacyTextResolver.sol') +const DummyExtendedDNSSECResolver = artifacts.require( + './DummyExtendedDNSSECResolver.sol', +) +const DummyLegacyTextResolver = artifacts.require( + './DummyLegacyTextResolver.sol', +) const DNSSECImpl = artifacts.require('./DNSSECImpl') const namehash = require('eth-ens-namehash') const utils = require('./Helpers/Utils') @@ -18,7 +22,7 @@ const { ethers } = require('hardhat') const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000' -contract('OffchainDNSResolver', function(accounts) { +contract('OffchainDNSResolver', function (accounts) { var ens = null var root = null var dnssec = null @@ -50,16 +54,16 @@ contract('OffchainDNSResolver', function(accounts) { signature: new Buffer([]), }, }, - rrs: values.map(value => ({ + rrs: values.map((value) => ({ name, type: 'TXT', class: 'IN', ttl: 3600, data: Buffer.from(value, 'ascii'), })), - }); - - beforeEach(async function() { + }) + + beforeEach(async function () { ens = await ENSRegistry.new() root = await Root.new(ens.address) @@ -73,134 +77,223 @@ contract('OffchainDNSResolver', function(accounts) { utils.hexEncodeName('co.nz'), ]) - offchainResolver = await OffchainDNSResolver.new(ens.address, dnssec.address, "https://localhost:8000/query"); - ownedResolver = await OwnedResolver.new(); + offchainResolver = await OffchainDNSResolver.new( + ens.address, + dnssec.address, + 'https://localhost:8000/query', + ) + ownedResolver = await OwnedResolver.new() registrar = await DNSRegistrarContract.new( ZERO_ADDRESS, // Previous registrar offchainResolver.address, dnssec.address, suffixes.address, - ens.address + ens.address, ) await root.setController(registrar.address, true) await root.setController(accounts[0], true) }) - it('should respond to resolution requests with a CCIP read request to the DNS gateway', async function() { - const pr = await PublicResolver.at(offchainResolver.address); - const DNSGatewayInterface = new ethers.utils.Interface(IDNSGateway.abi); - const dnsName = utils.hexEncodeName('test.test'); - const callData = pr.contract.methods['addr(bytes32)'](namehash.hash('test.test')).encodeABI(); - await expect(offchainResolver.resolve( - dnsName, - callData - )).to.be.revertedWith('OffchainLookup(' - + '"' + offchainResolver.address + '", ' - + '["https://localhost:8000/query"], ' - + '"' + DNSGatewayInterface.encodeFunctionData('resolve', [dnsName, 16]) + '", ' - + '"' + ethers.utils.id('resolveCallback(bytes,bytes)').slice(0, 10) + '", ' - + '"' + ethers.utils.defaultAbiCoder.encode(['bytes', 'bytes'], [dnsName, callData]) + '"' - + ')' - ); + it('should respond to resolution requests with a CCIP read request to the DNS gateway', async function () { + const pr = await PublicResolver.at(offchainResolver.address) + const DNSGatewayInterface = new ethers.utils.Interface(IDNSGateway.abi) + const dnsName = utils.hexEncodeName('test.test') + const callData = pr.contract.methods['addr(bytes32)']( + namehash.hash('test.test'), + ).encodeABI() + await expect( + offchainResolver.resolve(dnsName, callData), + ).to.be.revertedWith( + 'OffchainLookup(' + + '"' + + offchainResolver.address + + '", ' + + '["https://localhost:8000/query"], ' + + '"' + + DNSGatewayInterface.encodeFunctionData('resolve', [dnsName, 16]) + + '", ' + + '"' + + ethers.utils.id('resolveCallback(bytes,bytes)').slice(0, 10) + + '", ' + + '"' + + ethers.utils.defaultAbiCoder.encode( + ['bytes', 'bytes'], + [dnsName, callData], + ) + + '"' + + ')', + ) }) function doResolveCallback(name, texts, callData) { const proof = [ hexEncodeSignedSet(rootKeys(expiration, inception)), hexEncodeSignedSet(testRrset(name, texts)), - ]; + ] const response = ethers.utils.defaultAbiCoder.encode( - ["tuple(bytes, bytes)[]"], - [proof] - ); - const dnsName = utils.hexEncodeName(name); + ['tuple(bytes, bytes)[]'], + [proof], + ) + const dnsName = utils.hexEncodeName(name) const extraData = ethers.utils.defaultAbiCoder.encode( - ["bytes", "bytes"], - [dnsName, callData] - ); - return offchainResolver.resolveCallback(response, extraData); + ['bytes', 'bytes'], + [dnsName, callData], + ) + return offchainResolver.resolveCallback(response, extraData) } - it('handles calls to resolveCallback() with valid DNS TXT records containing an address', async function() { - const name = "test.test"; - const testAddress = '0xfefeFEFeFEFEFEFEFeFefefefefeFEfEfefefEfe'; - await ownedResolver.setAddr(namehash.hash(name), testAddress); - const pr = await PublicResolver.at(offchainResolver.address); - const callData = pr.contract.methods['addr(bytes32)'](namehash.hash(name)).encodeABI(); - const result = await doResolveCallback(name, [`ENS1 ${ownedResolver.address}`], callData); - expect(ethers.utils.defaultAbiCoder.decode(['address'], result)[0]).to.equal(testAddress); + it('handles calls to resolveCallback() with valid DNS TXT records containing an address', async function () { + const name = 'test.test' + const testAddress = '0xfefeFEFeFEFEFEFEFeFefefefefeFEfEfefefEfe' + await ownedResolver.setAddr(namehash.hash(name), testAddress) + const pr = await PublicResolver.at(offchainResolver.address) + const callData = pr.contract.methods['addr(bytes32)']( + namehash.hash(name), + ).encodeABI() + const result = await doResolveCallback( + name, + [`ENS1 ${ownedResolver.address}`], + callData, + ) + expect( + ethers.utils.defaultAbiCoder.decode(['address'], result)[0], + ).to.equal(testAddress) }) - it('handles calls to resolveCallback() with extra data and a legacy resolver', async function() { - const name = "test.test"; - const testAddress = '0xfefeFEFeFEFEFEFEFeFefefefefeFEfEfefefEfe'; - await ownedResolver.setAddr(namehash.hash(name), testAddress); - const pr = await PublicResolver.at(offchainResolver.address); - const callData = pr.contract.methods['addr(bytes32)'](namehash.hash(name)).encodeABI(); - const result = await doResolveCallback(name, [`ENS1 ${ownedResolver.address} blah`], callData); - expect(ethers.utils.defaultAbiCoder.decode(['address'], result)[0]).to.equal(testAddress); + it('handles calls to resolveCallback() with extra data and a legacy resolver', async function () { + const name = 'test.test' + const testAddress = '0xfefeFEFeFEFEFEFEFeFefefefefeFEfEfefefEfe' + await ownedResolver.setAddr(namehash.hash(name), testAddress) + const pr = await PublicResolver.at(offchainResolver.address) + const callData = pr.contract.methods['addr(bytes32)']( + namehash.hash(name), + ).encodeABI() + const result = await doResolveCallback( + name, + [`ENS1 ${ownedResolver.address} blah`], + callData, + ) + expect( + ethers.utils.defaultAbiCoder.decode(['address'], result)[0], + ).to.equal(testAddress) }) - it('handles calls to resolveCallback() with valid DNS TXT records containing a name', async function() { + it('handles calls to resolveCallback() with valid DNS TXT records containing a name', async function () { // Configure dnsresolver.eth to resolve to the ownedResolver so we can use it in the test - await root.setSubnodeOwner(ethers.utils.id('eth'), accounts[0]); - await ens.setSubnodeOwner(namehash.hash('eth'), ethers.utils.id('dnsresolver'), accounts[0]); - await ens.setResolver(namehash.hash('dnsresolver.eth'), ownedResolver.address); - await ownedResolver.setAddr(namehash.hash('dnsresolver.eth'), ownedResolver.address); - - const name = "test.test"; - const testAddress = '0xfefeFEFeFEFEFEFEFeFefefefefeFEfEfefefEfe'; - await ownedResolver.setAddr(namehash.hash(name), testAddress); - const pr = await PublicResolver.at(offchainResolver.address); - const callData = pr.contract.methods['addr(bytes32)'](namehash.hash(name)).encodeABI(); - const result = await doResolveCallback(name, [`ENS1 dnsresolver.eth`], callData); - expect(ethers.utils.defaultAbiCoder.decode(['address'], result)[0]).to.equal(testAddress); + await root.setSubnodeOwner(ethers.utils.id('eth'), accounts[0]) + await ens.setSubnodeOwner( + namehash.hash('eth'), + ethers.utils.id('dnsresolver'), + accounts[0], + ) + await ens.setResolver( + namehash.hash('dnsresolver.eth'), + ownedResolver.address, + ) + await ownedResolver.setAddr( + namehash.hash('dnsresolver.eth'), + ownedResolver.address, + ) + + const name = 'test.test' + const testAddress = '0xfefeFEFeFEFEFEFEFeFefefefefeFEfEfefefEfe' + await ownedResolver.setAddr(namehash.hash(name), testAddress) + const pr = await PublicResolver.at(offchainResolver.address) + const callData = pr.contract.methods['addr(bytes32)']( + namehash.hash(name), + ).encodeABI() + const result = await doResolveCallback( + name, + [`ENS1 dnsresolver.eth`], + callData, + ) + expect( + ethers.utils.defaultAbiCoder.decode(['address'], result)[0], + ).to.equal(testAddress) }) - it('rejects calls to resolveCallback() with an invalid TXT record', async function() { - const name = "test.test"; - const testAddress = '0xfefeFEFeFEFEFEFEFeFefefefefeFEfEfefefEfe'; - await ownedResolver.setAddr(namehash.hash(name), testAddress); - const pr = await PublicResolver.at(offchainResolver.address); - const callData = pr.contract.methods['addr(bytes32)'](namehash.hash(name)).encodeABI(); - await expect(doResolveCallback(name, ['nonsense'], callData)).to.be.revertedWith('CouldNotResolve'); + it('rejects calls to resolveCallback() with an invalid TXT record', async function () { + const name = 'test.test' + const testAddress = '0xfefeFEFeFEFEFEFEFeFefefefefeFEfEfefefEfe' + await ownedResolver.setAddr(namehash.hash(name), testAddress) + const pr = await PublicResolver.at(offchainResolver.address) + const callData = pr.contract.methods['addr(bytes32)']( + namehash.hash(name), + ).encodeABI() + await expect( + doResolveCallback(name, ['nonsense'], callData), + ).to.be.revertedWith('CouldNotResolve') }) - it('handles calls to resolveCallback() where the valid TXT record is not the first', async function() { - const name = "test.test"; - const testAddress = '0xfefeFEFeFEFEFEFEFeFefefefefeFEfEfefefEfe'; - await ownedResolver.setAddr(namehash.hash(name), testAddress); - const pr = await PublicResolver.at(offchainResolver.address); - const callData = pr.contract.methods['addr(bytes32)'](namehash.hash(name)).encodeABI(); - const result = await doResolveCallback(name, ['foo', `ENS1 ${ownedResolver.address}`], callData); - expect(ethers.utils.defaultAbiCoder.decode(['address'], result)[0]).to.equal(testAddress); + it('handles calls to resolveCallback() where the valid TXT record is not the first', async function () { + const name = 'test.test' + const testAddress = '0xfefeFEFeFEFEFEFEFeFefefefefeFEfEfefefEfe' + await ownedResolver.setAddr(namehash.hash(name), testAddress) + const pr = await PublicResolver.at(offchainResolver.address) + const callData = pr.contract.methods['addr(bytes32)']( + namehash.hash(name), + ).encodeABI() + const result = await doResolveCallback( + name, + ['foo', `ENS1 ${ownedResolver.address}`], + callData, + ) + expect( + ethers.utils.defaultAbiCoder.decode(['address'], result)[0], + ).to.equal(testAddress) }) - it('respects the first record with a valid resolver', async function() { - const name = "test.test"; - const testAddress = '0xfefeFEFeFEFEFEFEFeFefefefefeFEfEfefefEfe'; - await ownedResolver.setAddr(namehash.hash(name), testAddress); - const pr = await PublicResolver.at(offchainResolver.address); - const callData = pr.contract.methods['addr(bytes32)'](namehash.hash(name)).encodeABI(); - const result = await doResolveCallback(name, ['ENS1 nonexistent.eth', 'ENS1 0x1234', `ENS1 ${ownedResolver.address}`], callData); - expect(ethers.utils.defaultAbiCoder.decode(['address'], result)[0]).to.equal(testAddress); + it('respects the first record with a valid resolver', async function () { + const name = 'test.test' + const testAddress = '0xfefeFEFeFEFEFEFEFeFefefefefeFEfEfefefEfe' + await ownedResolver.setAddr(namehash.hash(name), testAddress) + const pr = await PublicResolver.at(offchainResolver.address) + const callData = pr.contract.methods['addr(bytes32)']( + namehash.hash(name), + ).encodeABI() + const result = await doResolveCallback( + name, + ['ENS1 nonexistent.eth', 'ENS1 0x1234', `ENS1 ${ownedResolver.address}`], + callData, + ) + expect( + ethers.utils.defaultAbiCoder.decode(['address'], result)[0], + ).to.equal(testAddress) }) - it('correctly handles extra data in the TXT record when calling a resolver that supports it', async function() { - const name = "test.test"; - const resolver = await DummyExtendedDNSSECResolver.new(); - const pr = await PublicResolver.at(resolver.address); - const callData = pr.contract.methods['text'](namehash.hash(name), 'test').encodeABI(); - const result = await doResolveCallback(name, [`ENS1 ${resolver.address} foobie bletch`], callData); - expect(ethers.utils.defaultAbiCoder.decode(['string'], result)[0]).to.equal('foobie bletch'); + it('correctly handles extra data in the TXT record when calling a resolver that supports it', async function () { + const name = 'test.test' + const resolver = await DummyExtendedDNSSECResolver.new() + const pr = await PublicResolver.at(resolver.address) + const callData = pr.contract.methods['text']( + namehash.hash(name), + 'test', + ).encodeABI() + const result = await doResolveCallback( + name, + [`ENS1 ${resolver.address} foobie bletch`], + callData, + ) + expect(ethers.utils.defaultAbiCoder.decode(['string'], result)[0]).to.equal( + 'foobie bletch', + ) }) - it('correctly resolves using legacy resolvers without resolve() support', async function() { - const name = "test.test"; - const resolver = await DummyLegacyTextResolver.new(); - const callData = resolver.contract.methods['text'](namehash.hash(name), 'test').encodeABI(); - const result = await doResolveCallback(name, [`ENS1 ${resolver.address} foobie bletch`], callData); - expect(ethers.utils.defaultAbiCoder.decode(['string'], result)[0]).to.equal('test'); + it('correctly resolves using legacy resolvers without resolve() support', async function () { + const name = 'test.test' + const resolver = await DummyLegacyTextResolver.new() + const callData = resolver.contract.methods['text']( + namehash.hash(name), + 'test', + ).encodeABI() + const result = await doResolveCallback( + name, + [`ENS1 ${resolver.address} foobie bletch`], + callData, + ) + expect(ethers.utils.defaultAbiCoder.decode(['string'], result)[0]).to.equal( + 'test', + ) }) -}); \ No newline at end of file +})