From 4aca88d1e70e6965a12bfa92fe2c13fe808fae4f Mon Sep 17 00:00:00 2001 From: sshmatrix <19473027+sshmatrix@users.noreply.github.com> Date: Tue, 15 Aug 2023 12:03:20 +0530 Subject: [PATCH] Fix for getRecordhash() (#46) * README update * Purge masterhash() * README.md update * README.md update: 2 * Reinstate signedRedirect() & ownerhash[] * some more NatSpec * Goerli-v4: 0x4b9A4521442485ad7e1b25295125240b5f3F5aaC * Remove redundant parts; more natSpec * update ownerhash[addr] to accept keccak(addr) * natSpec for GatewayManager * Mainnet: 0x57532d78FfBcC6ac5534A9b39899C7eC89082CdA * Fixes for bugs in v1-beta * Fixes for bugs in v1-beta (#34) * Some natSpec and fix for avatar/contenthash * Updated Fixes in v1.0.0-beta (#36) * Fixes for bugs in v1-beta * Some natSpec and fix for avatar/contenthash * Fix error in test.yml * Fix errors in test.yml (#38) * Fixes for bugs in v1-beta * Some natSpec and fix for avatar/contenthash * Fix error in test.yml * Review of #40 * Fix typo-2 * Re-try merge (#43) * README update * Purge masterhash() * README.md update * README.md update: 2 * Reinstate signedRedirect() & ownerhash[] * some more NatSpec * Goerli-v4: 0x4b9A4521442485ad7e1b25295125240b5f3F5aaC * Remove redundant parts; more natSpec * update ownerhash[addr] to accept keccak(addr) * natSpec for GatewayManager * Mainnet: 0x57532d78FfBcC6ac5534A9b39899C7eC89082CdA * Fixes for bugs in v1-beta * Fixes for bugs in v1-beta (#34) * Some natSpec and fix for avatar/contenthash * Updated Fixes in v1.0.0-beta (#36) * Fixes for bugs in v1-beta * Some natSpec and fix for avatar/contenthash * Fix error in test.yml * Fix errors in test.yml (#38) * Fixes for bugs in v1-beta * Some natSpec and fix for avatar/contenthash * Fix error in test.yml * Review of #40 * Fix typo-2 * Typo-2 * Typo-2.1 * Typo-2.2 * Get ChainID from ENV * rSync * getRecordhash() fix * Tests for wrapped names --- src/CCIP2ETH.sol | 50 ++-- src/Interface.sol | 8 +- test/CCIP2ETH.t.sol | 549 +++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 548 insertions(+), 59 deletions(-) diff --git a/src/CCIP2ETH.sol b/src/CCIP2ETH.sol index 895def0..503c307 100644 --- a/src/CCIP2ETH.sol +++ b/src/CCIP2ETH.sol @@ -69,14 +69,15 @@ contract CCIP2ETH is iCCIP2ETH { /// @dev - Sets ENS TESTNET wrapper contract /// @TODO - [!!!] REMOVE FOR MAINNET - isWrapper[0x0000000000000000000000000000000000000000] = true; - emit UpdatedWrapper(0x0000000000000000000000000000000000000000, true); + isWrapper[0x114D4603199df73e7D157787f8778E21fCd13066] = true; + emit UpdatedWrapper(0x114D4603199df73e7D157787f8778E21fCd13066, true); /// @dev - Set necessary interfaces supportsInterface[iERC165.supportsInterface.selector] = true; supportsInterface[iENSIP10.resolve.selector] = true; supportsInterface[type(iERC173).interfaceId] = true; supportsInterface[iCCIP2ETH.setRecordhash.selector] = true; + supportsInterface[iCCIP2ETH.setOwnerhash.selector] = true; supportsInterface[iCallbackType.signedRecord.selector] = true; supportsInterface[iCallbackType.signedRedirect.selector] = true; } @@ -92,11 +93,13 @@ contract CCIP2ETH is iCCIP2ETH { /** * @dev Gets recordhash for a node * @param _node - Namehash of domain.eth, or bytes32(address _Owner) - * @param _recordhash - IPNS Contenthash to set as recordhash + * @return _recordhash - IPNS contenthash that is set as recordhash */ function getRecordhash(bytes32 _node) external view returns (bytes memory _recordhash) { _recordhash = recordhash[_node]; - if (_recordhash.length == 0) { + // Check if first 12 bytes are bytes12(0) + bool _isQueryOwnerhash = _node & bytes32(uint256(0xFFFFFFFFFFFFFFFFFFFFFFFF) >> 96) == bytes32(0); + if (_recordhash.length == 0 && !_isQueryOwnerhash) { address _owner = ENS.owner(_node); if (isWrapper[_owner]) { _owner = iToken(_owner).ownerOf(uint256(_node)); @@ -112,7 +115,7 @@ contract CCIP2ETH is iCCIP2ETH { * @dev Sets standard recordhash for a node * Note - Only ENS owner or manager of node can call * @param _node - Namehash of domain.eth - * @param _recordhash - IPNS Contenthash to set as recordhash + * @param _recordhash - IPNS contenthash to set as recordhash */ function setRecordhash(bytes32 _node, bytes calldata _recordhash) external payable { address _owner = ENS.owner(_node); @@ -131,7 +134,7 @@ contract CCIP2ETH is iCCIP2ETH { * Note - Without the constant prefix hex'e5010172002408011220' * Note - Only ENS owner or manager of node can call * @param _node - Namehash of domain.eth - * @param _recordhash - Short IPNS Contenthash to set as recordhash + * @param _recordhash - Short IPNS contenthash to set as recordhash */ function setShortRecordhash(bytes32 _node, bytes32 _recordhash) external payable { address _owner = ENS.owner(_node); @@ -148,7 +151,7 @@ contract CCIP2ETH is iCCIP2ETH { /** * @dev Sets ownerhash for an owner * Note - Wallet-specific fallback recordhash - * @param _recordhash - Short IPNS Contenthash to set as ownerhash + * @param _recordhash - Short IPNS contenthash to set as ownerhash */ function setOwnerhash(bytes calldata _recordhash) external payable { if (msg.value < ownerhashFees) { @@ -162,7 +165,7 @@ contract CCIP2ETH is iCCIP2ETH { * @dev Sets ownerhash for an owner * Note - Without the constant prefix hex'e5010172002408011220' * Note - Wallet-specific fallback recordhash - * @param _recordhash - Short IPNS Contenthash to set as ownerhash + * @param _recordhash - Short IPNS contenthash to set as ownerhash */ function setShortOwnerhash(bytes32 _recordhash) external payable { if (msg.value < ownerhashFees) { @@ -178,8 +181,8 @@ contract CCIP2ETH is iCCIP2ETH { * @dev Sets recordhash for a subnode * Note - Only ENS owner or manager of parent node can call * @param _node - Namehash of domain.eth - * @param _subdomain - Subdomain labels; a.domain.eth = "a" - * @param _recordhash - Contenthash to set as recordhash + * @param _subdomain - Subdomain labels; sub.domain.eth = "sub" + * @param _recordhash - IPNS contenthash to set as recordhash */ function setSubRecordhash(bytes32 _node, string calldata _subdomain, bytes calldata _recordhash) external payable { address _owner = ENS.owner(_node); @@ -199,7 +202,7 @@ contract CCIP2ETH is iCCIP2ETH { * Note - Only ENS owner or manager of parent node can call * @param _node - Namehash of domain.eth * @param _subdomain - Subdomain labels; a.b.c.domain.eth = [a, b, c] - * @param _recordhash - Contenthash to set as recordhash + * @param _recordhash - IPNS contenthash to set as recordhash */ function setDeepSubRecordhash(bytes32 _node, string[] calldata _subdomain, bytes calldata _recordhash) external @@ -566,11 +569,22 @@ contract CCIP2ETH is iCCIP2ETH { } /// @dev : Management functions + + /// @dev - Returns owner of the contract + function owner() public view returns (address) { + return gateway.owner(); + } + /// @dev - Updates ChainID in case of a hardfork + + function updateChainID() public { + chainID = gateway.uintToString(block.chainid); + } /** * @dev Sets fees for ownerhash * Note - Set to 0 at launch * @param _wei - Fees in WEI per EOA */ + function updateOwnerhashFees(uint256 _wei) external OnlyDev { ownerhashFees = _wei; } @@ -616,20 +630,6 @@ contract CCIP2ETH is iCCIP2ETH { emit UpdatedWrapper(_addr, _set); } - /** - * @dev - Updates Chain ID in case of a hardfork - */ - function updateChainID() public { - chainID = gateway.uintToString(block.chainid); - } - - /** - * @dev - Owner of contract - */ - function owner() public view returns (address) { - return gateway.owner(); - } - /** * @dev Withdraw Ether to owner; to be used for tips or in case some Ether gets locked in the contract */ diff --git a/src/Interface.sol b/src/Interface.sol index 427b8db..06d7bc1 100644 --- a/src/Interface.sol +++ b/src/Interface.sol @@ -38,15 +38,15 @@ interface iCCIP2ETH is iENSIP10 { returns (address _signer); function setRecordhash(bytes32 _node, bytes calldata _recordhash) external payable; function setShortRecordhash(bytes32 _node, bytes32 _recordhash) external payable; + function setSubRecordhash(bytes32 _node, string memory _subdomain, bytes calldata _recordhash) external payable; + function setDeepSubRecordhash(bytes32 _node, string[] memory _subdomains, bytes calldata _recordhash) + external + payable; function setOwnerhash(bytes calldata _recordhash) external payable; function redirectService(bytes calldata _encoded, bytes calldata _requested) external view returns (bytes4 _selector, bytes32 _namehash, bytes memory _redirectRequest, string memory _domain); - function setDeepSubRecordhash(bytes32 _node, string[] memory _subdomains, bytes calldata _recordhash) - external - payable; - function setSubRecordhash(bytes32 _node, string memory _subdomain, bytes calldata _recordhash) external payable; } interface iGatewayManager is iERC173 { diff --git a/test/CCIP2ETH.t.sol b/test/CCIP2ETH.t.sol index 60b34a6..f80d6ec 100644 --- a/test/CCIP2ETH.t.sol +++ b/test/CCIP2ETH.t.sol @@ -12,9 +12,10 @@ interface xENS is iENS { /** * @author freetib.eth, sshmatrix.eth - * @title CCIP2.eth Resolver tester + * @title CCIP2.eth Resolver tester + * Note Tests unwrapped/legacy domains */ -contract CCIP2ETHTest is Test { +contract CCIP2ETHTestLegacy is Test { // using Surl for *; error OffchainLookup(address sender, string[] urls, bytes callData, bytes4 callbackFunction, bytes extraData); @@ -37,24 +38,20 @@ contract CCIP2ETHTest is Test { ccip2eth = new CCIP2ETH(address(gateway)); } - function testRecordHash() public { + /// @dev Console logs + function test1_01_ConsoleLog() public view { bytes[] memory _name = new bytes[](2); - _name[0] = "ccip2"; + _name[0] = "virgil"; _name[1] = "eth"; - (bytes32 _namehash,) = utils.Encode(_name); + (bytes32 _namehash, bytes memory _encoded) = utils.Encode(_name); address _addr = ENS.owner(_namehash); - bytes memory _recordhash = - hex"e50101720024080112203c5aba6c9b5055a5fa12281c486188ed8ae2b6ef394b3d981b00d17a4b51735c"; - vm.prank(_addr); - ccip2eth.setRecordhash(_namehash, _recordhash); - bytes32 shorthash = 0x3c5aba6c9b5055a5fa12281c486188ed8ae2b6ef394b3d981b00d17a4b51735c; - vm.prank(_addr); - ccip2eth.setShortRecordhash(_namehash, shorthash); - assertEq(_recordhash, ccip2eth.getRecordhash(_namehash)); + _addr; + _encoded; + //console.log(_addr); } - /// @dev Get some values - function test1_UtilsSetup() public { + /// @dev Get some values + function test1_02_UtilsSetup() public { bytes[] memory _name = new bytes[](2); _name[0] = "virgil"; _name[1] = "eth"; @@ -70,17 +67,15 @@ contract CCIP2ETHTest is Test { } /// @dev Test CCIP-Read call for a domain - function test2_ResolveLevel2() public { + function test1_03_ResolveLevel2() public { bytes[] memory _name = new bytes[](2); - _name[0] = "ccip2"; + _name[0] = "00081"; _name[1] = "eth"; (bytes32 _namehash, bytes memory _encoded) = utils.Encode(_name); address _addr = ENS.owner(_namehash); bytes memory _recordhash = hex"e50101720024080112203c5aba6c9b5055a5fa12281c486188ed8ae2b6ef394b3d981b00d17a4b51735c"; vm.prank(_addr); - //ENS.setOwner(_namehash, address(this)); - //ENS.setResolver(_namehash, address(ccip2eth)); ccip2eth.setRecordhash(_namehash, _recordhash); (string memory _path, string memory _domain) = utils.Format(_encoded); bytes memory _request = abi.encodeWithSelector(iResolver.addr.selector, _namehash); @@ -104,7 +99,7 @@ contract CCIP2ETHTest is Test { } /// @dev Test subdomain-level CCIP-Read call - function test3_ResolveLevel3() public { + function test1_04_ResolveLevel3() public { bytes[] memory _name = new bytes[](3); _name[0] = "blog"; _name[1] = "vitalik"; @@ -138,7 +133,7 @@ contract CCIP2ETHTest is Test { } /// @dev Test deep CCIP-Read call - function test4_ResolveLevel7() public { + function test1_05_ResolveLevel7() public { bytes[] memory _base = new bytes[](2); _base[0] = "vitalik"; _base[1] = "eth"; @@ -183,9 +178,9 @@ contract CCIP2ETHTest is Test { } /// @dev CCIP end-to-end test with on-chain signer - function test5_CCIPCallbackApprovedOnChain() public { + function test1_06_CCIPCallbackApprovedOnChain() public { bytes[] memory _name = new bytes[](2); - _name[0] = "domain"; + _name[0] = "00081"; _name[1] = "eth"; (bytes32 _node, bytes memory _encoded) = utils.Encode(_name); address _owner = ENS.owner(_node); @@ -248,9 +243,9 @@ contract CCIP2ETHTest is Test { } /// @dev CCIP end-to-end test with off-chain signer (with fake parameters) - function test6_CCIPCallbackApprovedOffChain() public { + function test1_07_CCIPCallbackApprovedOffChain() public { bytes[] memory _name = new bytes[](2); - _name[0] = "domain"; + _name[0] = "00081"; _name[1] = "eth"; (bytes32 _node, bytes memory _encoded) = utils.Encode(_name); @@ -334,7 +329,7 @@ contract CCIP2ETHTest is Test { } /// @dev CCIP end-to-end with off-chain signer and real parameters - function test7_CCIPCallbackApprovedOffChain_WithRealParameters() public { + function test1_08_CCIPCallbackApprovedOffChain_WithRealParameters() public { bytes[] memory _name = new bytes[](2); _name[0] = "00081"; _name[1] = "eth"; @@ -420,7 +415,7 @@ contract CCIP2ETHTest is Test { } /// @dev Test setting regular and short recordhash - function test8_setRecordHash() public { + function test1_09_SetRecordHash() public { bytes[] memory _name = new bytes[](2); _name[0] = "00081"; _name[1] = "eth"; @@ -437,9 +432,9 @@ contract CCIP2ETHTest is Test { } /// @dev Test setting recordhash for subdomain - function test9_setSubRecordhash() public { + function test1_10_SetSubRecordhash() public { bytes[] memory _name = new bytes[](2); - _name[0] = "domain"; + _name[0] = "00081"; _name[1] = "eth"; (bytes32 _node,) = utils.Encode(_name); vm.prank(ENS.owner(_node)); @@ -453,7 +448,7 @@ contract CCIP2ETHTest is Test { } /// @dev Test setting deep recordhash - function test10_setDeepSubRecordhash() public { + function test1_11_SetDeepSubRecordhash() public { string[] memory _subdomains = new string[](2); _subdomains[0] = "hello"; _subdomains[1] = "world"; @@ -471,6 +466,500 @@ contract CCIP2ETHTest is Test { ccip2eth.setDeepSubRecordhash(_node, _subdomains, _recordhash); _encoded; } + + /// @dev Test getting recordhash and ownerhash + function test1_12_GetRecordhash() public { + bytes[] memory _name = new bytes[](2); + _name[0] = "00081"; + _name[1] = "eth"; + (bytes32 _node, bytes memory _encoded) = utils.Encode(_name); + address _owner = ENS.owner(_node); + bytes memory _recordhash = ccip2eth.getRecordhash(_node); + bytes memory _ownerhash = ccip2eth.getRecordhash(bytes32(uint256(uint160(_owner)))); + assertEq(_recordhash, bytes("")); + assertEq(_ownerhash, bytes("")); + bytes memory _recordhash_ = + hex"e501017200240801122008dd085b86d16226791544f4628c4efc0936c69221fef17dfac843d9713233bb"; + vm.prank(_owner); + ccip2eth.setRecordhash(_node, _recordhash_); + bytes memory __recordhash = ccip2eth.getRecordhash(_node); + assertEq(__recordhash, _recordhash_); + bytes memory _ownerhash_ = + hex"e50101720024080112200000000000000000000000000000000c0936c69221fef17dfac843d971323300"; + vm.prank(_owner); + ccip2eth.setOwnerhash(_ownerhash_); + bytes memory __ownerhash = ccip2eth.getRecordhash(bytes32(uint256(uint160(_owner)))); + assertEq(__ownerhash, _ownerhash_); + _encoded; + } +} + +/** + * @author freetib.eth, sshmatrix.eth + * @title CCIP2.eth Resolver tester + * Note Tests wrapped domains + */ +contract CCIP2ETHTestWrapped is Test { + // using Surl for *; + error OffchainLookup(address sender, string[] urls, bytes callData, bytes4 callbackFunction, bytes extraData); + + address EOA = address(this); + CCIP2ETH public ccip2eth; + iGatewayManager public gateway; + xENS public ENS = xENS(0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e); + uint256 public PrivateKeyOwner = 0xdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd; + uint256 public PrivateKeySigner = 0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc; + + Utils public utils = new Utils(); + string chainID; + + /// @dev Setup + bytes32 dotETH = keccak256(abi.encodePacked(bytes32(0), keccak256("eth"))); + + function setUp() public { + gateway = new GatewayManager(); + chainID = gateway.uintToString(block.chainid); + ccip2eth = new CCIP2ETH(address(gateway)); + } + + /// @dev Console logs + function test2_01_ConsoleLog() public view { + bytes[] memory _name = new bytes[](2); + _name[0] = "g100kcat"; + _name[1] = "eth"; + (bytes32 _namehash, bytes memory _encoded) = utils.Encode(_name); + address _addr = ENS.owner(_namehash); + _encoded; + _addr; + //console.log(_addr); + } + + /// @dev Test CCIP-Read call for a domain + function test2_02_ResolveLevel2() public { + bytes[] memory _name = new bytes[](2); + _name[0] = "g100kcat"; + _name[1] = "eth"; + (bytes32 _namehash, bytes memory _encoded) = utils.Encode(_name); + address _addr = ENS.owner(_namehash); + bytes memory _recordhash = + hex"e50101720024080112203c5aba6c9b5055a5fa12281c486188ed8ae2b6ef394b3d981b00d17a4b51735c"; + vm.prank(_addr); + ccip2eth.setRecordhash(_namehash, _recordhash); + (string memory _path, string memory _domain) = utils.Format(_encoded); + bytes memory _request = abi.encodeWithSelector(iResolver.addr.selector, _namehash); + string memory _recType = gateway.funcToJson(_request); + bytes32 _checkhash = keccak256( + abi.encodePacked(address(ccip2eth), blockhash(block.number - 1), _addr, _domain, _recType, _request) + ); + vm.expectRevert( + abi.encodeWithSelector( + iENSIP10.OffchainLookup.selector, + address(ccip2eth), + gateway.randomGateways( + _recordhash, string.concat("/.well-known/", _path, "/", _recType), uint256(_checkhash) + ), + abi.encodePacked(uint16(block.timestamp / 60)), + ccip2eth.__callback.selector, + abi.encode(_namehash, block.number - 1, _checkhash, _domain, _recType, _path, _encoded, _request) + ) + ); + ccip2eth.resolve(_encoded, _request); + } + + /// @dev Test subdomain-level CCIP-Read call + function test2_03_ResolveLevel3() public { + bytes[] memory _name = new bytes[](3); + _name[0] = "0"; + _name[1] = "g100kcat"; + _name[2] = "eth"; + (bytes32 _namehash, bytes memory _encoded) = utils.Encode(_name); + address _addr = ENS.owner(_namehash); + vm.prank(_addr); + ENS.setOwner(_namehash, address(this)); + bytes memory _recordhash = + hex"e50101720024080112203c5aba6c9b5055a5fa12281c486188ed8ae2b6ef394b3d981b00d17a4b51735c"; + ccip2eth.setRecordhash(_namehash, _recordhash); + (string memory _path, string memory _domain) = utils.Format(_encoded); + bytes memory _request = abi.encodeWithSelector(iResolver.text.selector, _namehash, abi.encode(string("avatar"))); + string memory _recType = gateway.funcToJson(_request); + bytes32 _checkhash = keccak256( + abi.encodePacked(address(ccip2eth), blockhash(block.number - 1), address(this), _domain, _recType, _request) + ); + vm.expectRevert( + abi.encodeWithSelector( + iENSIP10.OffchainLookup.selector, + address(ccip2eth), + gateway.randomGateways( + _recordhash, string.concat("/.well-known/", _path, "/", _recType), uint256(_checkhash) + ), + abi.encodePacked(uint16(block.timestamp / 60)), + ccip2eth.__callback.selector, + abi.encode(_namehash, block.number - 1, _checkhash, _domain, _recType, _path, _encoded, _request) + ) + ); + ccip2eth.resolve(_encoded, _request); + } + + /// @dev Test deep CCIP-Read call + function test2_04_ResolveLevel7() public { + bytes[] memory _base = new bytes[](2); + _base[0] = "g100kcat"; + _base[1] = "eth"; + (bytes32 _baseNode, bytes memory _encoded) = utils.Encode(_base); + address _addr = ENS.owner(_baseNode); + vm.prank(_addr); + ENS.setOwner(_baseNode, address(this)); // Owner records set at level 2 only + bytes memory _recordhash = + hex"e50101720024080112203c5aba6c9b5055a5fa12281c486188ed8ae2b6ef394b3d981b00d17a4b51735c"; + ccip2eth.setRecordhash(_baseNode, _recordhash); + + bytes[] memory _name = new bytes[](7); + _name[0] = "never"; + _name[1] = "gonna"; + _name[2] = "give"; + _name[3] = "you"; + _name[4] = "up"; + _name[5] = "g100kcat"; + _name[6] = "eth"; + bytes32 _namehash; // Full namehash + (_namehash, _encoded) = utils.Encode(_name); + (string memory _path, string memory _domain) = utils.Format(_encoded); + bytes memory _request = abi.encodeWithSelector(iResolver.text.selector, _namehash, "avatar"); + string memory _recType = gateway.funcToJson(_request); + bytes32 _checkhash = keccak256( + abi.encodePacked(address(ccip2eth), blockhash(block.number - 1), address(this), _domain, _recType, _request) + ); + assertEq(_path, "eth/g100kcat/up/you/give/gonna/never"); + vm.expectRevert( + abi.encodeWithSelector( + iENSIP10.OffchainLookup.selector, + address(ccip2eth), + gateway.randomGateways( + _recordhash, string.concat("/.well-known/", _path, "/", _recType), uint256(_checkhash) + ), + abi.encodePacked(uint16(block.timestamp / 60)), + ccip2eth.__callback.selector, + abi.encode(_baseNode, block.number - 1, _checkhash, _domain, _recType, _path, _encoded, _request) + ) + ); + ccip2eth.resolve(_encoded, _request); + } + + /// @dev CCIP end-to-end test with on-chain signer + function test2_05_CCIPCallbackApprovedOnChain() public { + bytes[] memory _name = new bytes[](2); + _name[0] = "g100kcat"; + _name[1] = "eth"; + (bytes32 _node, bytes memory _encoded) = utils.Encode(_name); + address _owner = ENS.owner(_node); + + uint256 SignerKey = 0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc; + address _signer = vm.addr(SignerKey); + vm.prank(_owner); + ENS.setOwner(_node, address(this)); + ccip2eth.approve(_node, _signer, true); + bytes memory _recordhash = + hex"e50101720024080112203c5aba6c9b5055a5fa12281c486188ed8ae2b6ef394b3d981b00d17a4b51735c"; + ccip2eth.setRecordhash(_node, _recordhash); + + (string memory _path, string memory _domain) = utils.Format(_encoded); + bytes memory _request = abi.encodeWithSelector(iResolver.addr.selector, _node); + string memory _recType = gateway.funcToJson(_request); + bytes32 _checkhash = keccak256( + abi.encodePacked(address(ccip2eth), blockhash(block.number - 1), address(this), _domain, _recType, _request) + ); + bytes memory _extradata = + abi.encode(_node, block.number - 1, _checkhash, _domain, _recType, _path, _encoded, _request); + vm.expectRevert( + abi.encodeWithSelector( + iENSIP10.OffchainLookup.selector, + address(ccip2eth), + gateway.randomGateways( + _recordhash, string.concat("/.well-known/", _path, "/", _recType), uint256(_checkhash) + ), + abi.encodePacked(uint16(block.timestamp / 60)), + ccip2eth.__callback.selector, + _extradata + ) + ); + ccip2eth.resolve(_encoded, _request); + bytes memory _result = abi.encode(address(this)); + string memory signRequest = string.concat( + "Requesting Signature To Update ENS Record\n", + "\nOrigin: ", + _domain, + "\nRecord Type: address/60", + "\nExtradata: 0x", + gateway.bytesToHexString(abi.encodePacked(keccak256(_result)), 0), + "\nSigned By: eip155:", + chainID, + ":", + gateway.toChecksumAddress(address(_signer)) + ); + bytes32 _digest = keccak256( + abi.encodePacked( + "\x19Ethereum Signed Message:\n", gateway.uintToString(bytes(signRequest).length), signRequest + ) + ); + assertTrue(ccip2eth.approved(_node, _signer)); + assertTrue(ccip2eth.isApprovedSigner(address(this), _node, _signer)); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(SignerKey, _digest); + bytes memory _signature = abi.encodePacked(r, s, v); + bytes memory _response = + abi.encodeWithSelector(iCallbackType.signedRecord.selector, _signer, _signature, bytes("0"), _result); + assertEq(_result, ccip2eth.__callback(_response, _extradata)); + } + + /// @dev CCIP end-to-end test with off-chain signer (with fake parameters) + function test2_06_CCIPCallbackApprovedOffChain() public { + bytes[] memory _name = new bytes[](2); + _name[0] = "g100kcat"; + _name[1] = "eth"; + (bytes32 _node, bytes memory _encoded) = utils.Encode(_name); + + uint256 OwnerKey = 0xdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd; + uint256 SignerKey = 0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc; + address _owner = vm.addr(OwnerKey); + address _signer = vm.addr(SignerKey); + vm.prank(ENS.owner(_node)); + ENS.setOwner(_node, _owner); + bytes memory _recordhash = + hex"e50101720024080112203c5aba6c9b5055a5fa12281c486188ed8ae2b6ef394b3d981b00d17a4b51735c"; + vm.prank(_owner); + ccip2eth.setRecordhash(_node, _recordhash); + + (string memory _path, string memory _domain) = utils.Format(_encoded); + bytes memory _request = abi.encodeWithSelector(iResolver.addr.selector, _node); + string memory _recType = gateway.funcToJson(_request); + bytes32 _checkhash = keccak256( + abi.encodePacked(address(ccip2eth), blockhash(block.number - 1), _owner, _domain, _recType, _request) + ); + bytes memory _extradata = + abi.encode(_node, block.number - 1, _checkhash, _domain, _recType, _path, _encoded, _request); + vm.expectRevert( + abi.encodeWithSelector( + iENSIP10.OffchainLookup.selector, + address(ccip2eth), + gateway.randomGateways( + _recordhash, string.concat("/.well-known/", _path, "/", _recType), uint256(_checkhash) + ), + abi.encodePacked(uint16(block.timestamp / 60)), + ccip2eth.__callback.selector, + _extradata + ) + ); + ccip2eth.resolve(_encoded, _request); + bytes memory _result = abi.encode(address(this)); + string memory signRequest = string.concat( + "Requesting Signature To Update ENS Record\n", + "\nOrigin: ", + _domain, + "\nRecord Type: address/60", + "\nExtradata: 0x", + gateway.bytesToHexString(abi.encodePacked(keccak256(_result)), 0), + "\nSigned By: eip155:", + chainID, + ":", + gateway.toChecksumAddress(address(_signer)) + ); + bytes32 _digest = keccak256( + abi.encodePacked( + "\x19Ethereum Signed Message:\n", gateway.uintToString(bytes(signRequest).length), signRequest + ) + ); + assertTrue(!ccip2eth.approved(_node, _signer)); + assertTrue(!ccip2eth.isApprovedSigner(address(this), _node, _signer)); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(SignerKey, _digest); + bytes memory _recordSig = abi.encodePacked(r, s, v); + signRequest = string.concat( + "Requesting Signature To Approve ENS Records Signer\n", + "\nOrigin: ", + _domain, + "\nApproved Signer: eip155:", + chainID, + ":", + gateway.toChecksumAddress(_signer), + "\nApproved By: eip155:", + chainID, + ":", + gateway.toChecksumAddress(_owner) + ); + _digest = keccak256( + abi.encodePacked( + "\x19Ethereum Signed Message:\n", gateway.uintToString(bytes(signRequest).length), signRequest + ) + ); + (v, r, s) = vm.sign(OwnerKey, _digest); + bytes memory _approvedSig = abi.encodePacked(r, s, v); + bytes memory _response = + abi.encodeWithSelector(iCallbackType.signedRecord.selector, _signer, _recordSig, _approvedSig, _result); + assertEq(_result, ccip2eth.__callback(_response, _extradata)); + } + + /// @dev CCIP end-to-end with off-chain signer and real parameters + function test2_07_CCIPCallbackApprovedOffChain_WithRealParameters() public { + bytes[] memory _name = new bytes[](2); + _name[0] = "g100kcat"; + _name[1] = "eth"; + (bytes32 _node, bytes memory _encoded) = utils.Encode(_name); + + uint256 OwnerKey = 0xdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd; + uint256 SignerKey = 0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc; + address _owner = vm.addr(OwnerKey); + address _signer = vm.addr(SignerKey); + vm.prank(ENS.owner(_node)); + ENS.setOwner(_node, _owner); + bytes memory _recordhash = + hex"e501017200240801122008dd085b86d16226791544f4628c4efc0936c69221fef17dfac843d9713233bb"; + vm.prank(_owner); + ccip2eth.setRecordhash(_node, _recordhash); + + (string memory _path, string memory _domain) = utils.Format(_encoded); + bytes memory _request = abi.encodeWithSelector(iResolver.addr.selector, _node); + string memory _recType = gateway.funcToJson(_request); + bytes32 _checkhash = keccak256( + abi.encodePacked(address(ccip2eth), blockhash(block.number - 1), _owner, _domain, _recType, _request) + ); + bytes memory _extradata = + abi.encode(_node, block.number - 1, _checkhash, _domain, _recType, _path, _encoded, _request); + vm.expectRevert( + abi.encodeWithSelector( + iENSIP10.OffchainLookup.selector, + address(ccip2eth), + gateway.randomGateways( + _recordhash, string.concat("/.well-known/", _path, "/", _recType), uint256(_checkhash) + ), + abi.encodePacked(uint16(block.timestamp / 60)), + ccip2eth.__callback.selector, + _extradata + ) + ); + ccip2eth.resolve(_encoded, _request); + bytes memory _result = abi.encode(address(0x1111000000000000000000000000000000000001)); + string memory signRequest = string.concat( + "Requesting Signature To Update ENS Record\n", + "\nOrigin: ", + _domain, + "\nRecord Type: address/60", + "\nExtradata: 0x", + gateway.bytesToHexString(abi.encodePacked(keccak256(_result)), 0), + "\nSigned By: eip155:", + chainID, + ":", + gateway.toChecksumAddress(address(_signer)) + ); + bytes32 _digest = keccak256( + abi.encodePacked( + "\x19Ethereum Signed Message:\n", gateway.uintToString(bytes(signRequest).length), signRequest + ) + ); + assertTrue(!ccip2eth.approved(_node, _signer)); + assertTrue(!ccip2eth.isApprovedSigner(address(this), _node, _signer)); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(SignerKey, _digest); + bytes memory _recordSig = abi.encodePacked(r, s, v); + signRequest = string.concat( + "Requesting Signature To Approve ENS Records Signer\n", + "\nOrigin: ", + _domain, + "\nApproved Signer: eip155:", + chainID, + ":", + gateway.toChecksumAddress(_signer), + "\nApproved By: eip155:", + chainID, + ":", + gateway.toChecksumAddress(_owner) + ); + _digest = keccak256( + abi.encodePacked( + "\x19Ethereum Signed Message:\n", gateway.uintToString(bytes(signRequest).length), signRequest + ) + ); + (v, r, s) = vm.sign(OwnerKey, _digest); + bytes memory _approvedSig = abi.encodePacked(r, s, v); + bytes memory _response = + abi.encodeWithSelector(iCallbackType.signedRecord.selector, _signer, _recordSig, _approvedSig, _result); + assertEq(_result, ccip2eth.__callback(_response, _extradata)); + } + + /// @dev Test setting regular and short recordhash + function test2_08_SetRecordHash() public { + bytes[] memory _name = new bytes[](2); + _name[0] = "g100kcat"; + _name[1] = "eth"; + (bytes32 _namehash,) = utils.Encode(_name); + address _addr = ENS.owner(_namehash); + bytes memory _recordhash = + hex"e50101720024080112203c5aba6c9b5055a5fa12281c486188ed8ae2b6ef394b3d981b00d17a4b51735c"; + vm.prank(_addr); + ccip2eth.setRecordhash(_namehash, _recordhash); + bytes32 shorthash = 0x3c5aba6c9b5055a5fa12281c486188ed8ae2b6ef394b3d981b00d17a4b51735c; + vm.prank(_addr); + ccip2eth.setShortRecordhash(_namehash, shorthash); + assertEq(_recordhash, ccip2eth.getRecordhash(_namehash)); + } + + /// @dev Test setting recordhash for subdomain + function test2_09_SetSubRecordhash() public { + bytes[] memory _name = new bytes[](2); + _name[0] = "g100kcat"; + _name[1] = "eth"; + (bytes32 _node,) = utils.Encode(_name); + vm.prank(ENS.owner(_node)); + ENS.setOwner(_node, EOA); + bytes memory _recordhash = + hex"e501017200240801122008dd085b86d16226791544f4628c4efc0936c69221fef17dfac843d9713233bb"; + ccip2eth.setSubRecordhash(_node, "test", _recordhash); + vm.expectRevert(abi.encodeWithSelector(CCIP2ETH.NotAuthorised.selector, "NOT_APPROVED")); + vm.prank(address(0xc0ffee)); + ccip2eth.setSubRecordhash(_node, "test", _recordhash); + } + + /// @dev Test setting deep recordhash + function test2_10_SetDeepSubRecordhash() public { + string[] memory _subdomains = new string[](2); + _subdomains[0] = "hello"; + _subdomains[1] = "world"; + bytes[] memory _name = new bytes[](2); + _name[0] = "g100kcat"; + _name[1] = "eth"; + (bytes32 _node, bytes memory _encoded) = utils.Encode(_name); + vm.prank(ENS.owner(_node)); + ENS.setOwner(_node, EOA); + bytes memory _recordhash = + hex"e501017200240801122008dd085b86d16226791544f4628c4efc0936c69221fef17dfac843d9713233bb"; + ccip2eth.setDeepSubRecordhash(_node, _subdomains, _recordhash); + vm.expectRevert(abi.encodeWithSelector(CCIP2ETH.NotAuthorised.selector, "NOT_APPROVED")); + vm.prank(address(0xc0ffee)); + ccip2eth.setDeepSubRecordhash(_node, _subdomains, _recordhash); + _encoded; + } + + /// @dev Test getting recordhash and ownerhash + function test2_11_GetRecordhash() public { + bytes[] memory _name = new bytes[](2); + _name[0] = "g100kcat"; + _name[1] = "eth"; + (bytes32 _node, bytes memory _encoded) = utils.Encode(_name); + address _owner = ENS.owner(_node); + bytes memory _recordhash = ccip2eth.getRecordhash(_node); + bytes memory _ownerhash = ccip2eth.getRecordhash(bytes32(uint256(uint160(_owner)))); + assertEq(_recordhash, bytes("")); + assertEq(_ownerhash, bytes("")); + bytes memory _recordhash_ = + hex"e501017200240801122008dd085b86d16226791544f4628c4efc0936c69221fef17dfac843d9713233bb"; + vm.prank(_owner); + ccip2eth.setRecordhash(_node, _recordhash_); + bytes memory __recordhash = ccip2eth.getRecordhash(_node); + assertEq(__recordhash, _recordhash_); + bytes memory _ownerhash_ = + hex"e50101720024080112200000000000000000000000000000000c0936c69221fef17dfac843d971323300"; + vm.prank(_owner); + ccip2eth.setOwnerhash(_ownerhash_); + bytes memory __ownerhash = ccip2eth.getRecordhash(bytes32(uint256(uint160(_owner)))); + assertEq(__ownerhash, _ownerhash_); + _encoded; + } } /// @dev Utility functions