diff --git a/package.json b/package.json index e5f52b1..50a25ae 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "@ensdomains/eth-ens-namehash": "^2.0.15", "@openzeppelin/contracts": "^4.9.0", "@openzeppelin/upgrades-core": "^1.26.2", + "base58-solidity": "^1.0.2", "content-hash": "^2.5.2", "js-sha3": "^0.8.0", "ts-node": "^10.9.1", diff --git a/test/UpdateENSDataProposal.t.sol b/test/UpdateENSDataProposal.t.sol index ceb4e20..e95b356 100644 --- a/test/UpdateENSDataProposal.t.sol +++ b/test/UpdateENSDataProposal.t.sol @@ -8,6 +8,8 @@ import { console2 } from "@forge-std/console2.sol"; import { IENSResolver } from "@interfaces/IENSResolver.sol"; import { IENSRegistry } from "@interfaces/IENSRegistry.sol"; +import "node_modules/base58-solidity/contracts/Base58.sol"; + import { ENSNamehash } from "./ENSNamehash.sol"; contract TestExampleProposal is ProposalUtils { @@ -25,13 +27,79 @@ contract TestExampleProposal is ProposalUtils { return ENSNamehash.namehash(bytes(domain)); } + function calculateIpfsContenthash(string memory ipfsCid) internal pure returns (bytes memory) { + if (bytes(ipfsCid).length == 0) return bytes(""); + return bytes.concat(hex"e3010170", Base58.decodeFromString(ipfsCid)); + } + function createAndExecuteProposal() public { address proposalAddress = address(new UpdateENSDataProposal()); proposeAndExecute(proposalAddress); } - function test() public { - createAndExecuteProposal(); + struct DomainInfo { + string ensName; + string ipfsCid; + } + + DomainInfo[] subdomains = [ + DomainInfo("minified.sources.tornadocash.eth", ""), // Minified domain itself don't have IPFS contenthash + DomainInfo("packages.sources.tornadocash.eth", ""), // Packages domain also, it is just a root domain for npm packages + DomainInfo("sources.tornadocash.eth", "QmX2RqM2g98EM1C7UWx2uW7Cz9ALQSCRkogDxEJDZbNH15"), + DomainInfo("download.sources.tornadocash.eth", "QmXrkrmZYvVCBtsZYdpsyhRcfNERYnrcTmjgLfgwWEn2XE"), + DomainInfo("classic-ui.sources.tornadocash.eth", "QmacsxDCzyUCsmG6W5Nz5arHPjNms5Bwc7QqfYM6utASWj"), + DomainInfo("nova.sources.tornadocash.eth", "QmaYVcnwab7eR8JStD11JwXUkiCLfBvth2eByMBSxWhfVP"), + DomainInfo("docs.sources.tornadocash.eth", "QmZC7e8KqB7fdyRFj3iu45i3sTABVsYVnmMzZ1bsn1VpMn"), + DomainInfo("relayers-ui.sources.tornadocash.eth", "QmT5vRziiwZKDZUkDX4BqxKvXmcz3W2tLtGkE9VrtdCQuu"), + DomainInfo("torn-token.sources.tornadocash.eth", "QmfRtPH3gRwTbbNEAfkvVNeGauBAcFHBzuET7JfmKvfFr1"), + DomainInfo("classic-relayer.sources.tornadocash.eth", "QmUTA3MABmeNU9RvJK1cQ7L28KRLFVA8ebaq7gotQXAKrg"), + DomainInfo("nova-relayer.sources.tornadocash.eth", "QmbsX9ScTxZ5Tqy1a8ecdQNacStiCDwFufUyCJ5HXrgMSj"), + DomainInfo("cli.sources.tornadocash.eth", "Qmc1XYtApEGsJpMfSjTAYQoYHakNyJcfJUtRFC3FBLD1AD"), + DomainInfo("info-page.sources.tornadocash.eth", "QmQwNEb8SdFkiPDMAk3ncktqbZCTGknVN3sH33LAMSaADB"), + DomainInfo("classic-ui.minified.sources.tornadocash.eth", "QmQGsukwaYhkKJ1bHW3rZJTc83Rh7xFogMMu8GMDRcbAt8"), + DomainInfo("nova.minified.sources.tornadocash.eth", "QmecystQd1aGDfWp93EndSpU8gGGYXgyAhfFEKXsZfPA2m"), + DomainInfo("cli.minified.sources.tornadocash.eth", "QmUFAL29scANSqvrVTejoRrpMzKpJJE6j4BKrKGyKNM5XH"), + DomainInfo("websnark.packages.sources.tornadocash.eth", "QmRqZ55oP7Vyq39cJrqLSAhiLEbhCLm6QTJKghhHHogKmQ"), + DomainInfo("circomlib.packages.sources.tornadocash.eth", "QmVDdK5YowqaMuQuAPwW2Hq4GSSNebVBfd3qsKUhvQZxVv"), + DomainInfo("snarkjs.packages.sources.tornadocash.eth", "QmdoqswophQXo5JrQQnbpWS5562eizT8vAK8XQDsHyKLs5"), + DomainInfo("oracles.packages.sources.tornadocash.eth", "QmYUM1Kx6ju5ZBu6TDdRfZZjG64fCNbyKxWySMsLprPQGY"), + DomainInfo("gas-price-oracle.packages.sources.tornadocash.eth", "QmW8zZ1Dv32j9HpjP3aExNJvxcshYoGTsjQaX8VZzRTXxx"), + DomainInfo("config.packages.sources.tornadocash.eth", "QmRPK6AqffoB721RfaWtRr1GjdpVN7x4g8ZcG27RqvLweR"), + DomainInfo("anonymity-mining.packages.sources.tornadocash.eth", "QmTfy4wGgYMKczEjtKNRZTEpnzfYyqtPhbc3fzJqwPSpzF"), + DomainInfo("trees.packages.sources.tornadocash.eth", "QmanV67Tzu7jLdeVXStjJ7iVVYMPAvnHhmxwsCSXALAtGK"), + DomainInfo("fixed-merkle-tree.packages.sources.tornadocash.eth", "QmUtj3m6y5sEw4Y7V7PnAS7pKp9gbJGJ2eGAMWnwXAPRP9"), + DomainInfo("tx-manager.packages.sources.tornadocash.eth", "QmYNvuaKH47QJuFairApChBvoRtbezvnWU12tPs8cHUZzA"), + DomainInfo("merkle-root-updater.packages.sources.tornadocash.eth", "QmTVin6iNu5Mp6YPo3jTpCNKdE5JdvM4r4tqXZJtac2UQ4") + ]; + + function testSubdomainsRegistered() public executeCurrentProposalBefore { + for (uint256 i = 0; i < subdomains.length; i++) { + bytes32 node = calculateDomainNode(subdomains[i].ensName); + bool isRegistered = ensRegistry.recordExists(node); + + if (isRegistered) { + console2.log("Subdomain %s registered", subdomains[i].ensName); + } else { + console2.log("Subdomain %s isn't registered!", subdomains[i].ensName); + } + require(isRegistered, "subdomain not registered"); + } + } + + function testContenthashesOnSubdomainsAreCorrect() public executeCurrentProposalBefore { + for (uint256 i = 0; i < subdomains.length; i++) { + bytes32 node = calculateDomainNode(subdomains[i].ensName); + bytes memory desiredIpfsContenthash = calculateIpfsContenthash(subdomains[i].ipfsCid); + bytes memory realContenthash = ensResolver.contenthash(node); + assertEq(desiredIpfsContenthash, realContenthash, "contenthash is wrong"); + } + } + + function testOwnerOfAllSubdomainsIsGovernance() public executeCurrentProposalBefore { + for (uint256 i = 0; i < subdomains.length; i++) { + bytes32 node = calculateDomainNode(subdomains[i].ensName); + require(ensRegistry.owner(node) == governanceAddress, "owner is not governance"); + } } }