Skip to content

Commit

Permalink
Merge pull request #19 from ConsenSys/CZK-103-smart-contracts-unit-tests
Browse files Browse the repository at this point in the history
Czk 103 smart contracts unit tests
  • Loading branch information
Julink-eth authored Apr 17, 2023
2 parents 20dee8d + d02bf7d commit bb92289
Show file tree
Hide file tree
Showing 8 changed files with 175 additions and 28 deletions.
3 changes: 3 additions & 0 deletions packages/contracts/.solcover.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
skipFiles: [],
};
39 changes: 23 additions & 16 deletions packages/contracts/contracts/l1/LineaResolverStub.sol
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ abstract contract SupportsInterface is ISupportsInterface {

contract LineaResolverStub is IExtendedResolver, SupportsInterface {
string[] public gateways;
address public rollup;
address public l2resolver;

error OffchainLookup(
Expand All @@ -54,9 +53,8 @@ contract LineaResolverStub is IExtendedResolver, SupportsInterface {
bytes extraData
);

constructor(string[] memory _gateways, address _rollup, address _l2resolver) {
constructor(string[] memory _gateways, address _l2resolver) {
gateways = _gateways;
rollup = _rollup;
l2resolver = _l2resolver;
}

Expand Down Expand Up @@ -94,8 +92,9 @@ contract LineaResolverStub is IExtendedResolver, SupportsInterface {
) external view returns (bytes memory) {
// We only resolve if the addr(bytes32) is called otherwise we simply return an empty response
bytes4 signature = bytes4(extraData[0:4]);

if (signature != bytes4(0x3b3b57de)) {
return abi.encode("");
return "";
}

// This is the hash name of the domain name
Expand All @@ -111,16 +110,20 @@ contract LineaResolverStub is IExtendedResolver, SupportsInterface {

// step 2: check storage values, get itemId first and then get the address result
bytes32 tokenIdSlot = keccak256(abi.encodePacked(node, uint256(6)));
bytes32 tokenId = getStorageValue(
(bool tokenIdExists, bytes32 tokenId) = getStorageValue(
l2resolver,
tokenIdSlot,
proof.stateRoot,
proof.accountProof,
proof.tokenIdStorageProof
);

if (!tokenIdExists) {
return "";
}

bytes32 ownerSlot = keccak256(abi.encodePacked(tokenId, uint256(2)));
bytes32 owner = getStorageValue(
(, bytes32 owner) = getStorageValue(
l2resolver,
ownerSlot,
proof.stateRoot,
Expand All @@ -131,27 +134,31 @@ contract LineaResolverStub is IExtendedResolver, SupportsInterface {
return abi.encode(owner);
}

function getl2Resolver() external view returns (address) {
return l2resolver;
}

function getStorageValue(
address target,
bytes32 slot,
bytes32 stateRoot,
bytes memory stateTrieWitness,
bytes memory storageTrieWitness
) internal pure returns (bytes32) {
(bool exists, bytes memory encodedResolverAccount) = Lib_SecureMerkleTrie
.get(abi.encodePacked(target), stateTrieWitness, stateRoot);
require(exists, "Account does not exist");
) internal pure returns (bool exists, bytes32) {
(
bool accountExists,
bytes memory encodedResolverAccount
) = Lib_SecureMerkleTrie.get(
abi.encodePacked(target),
stateTrieWitness,
stateRoot
);
require(accountExists, "Account does not exist");
Lib_OVMCodec.EVMAccount memory account = Lib_OVMCodec.decodeEVMAccount(
encodedResolverAccount
);
(bool storageExists, bytes memory retrievedValue) = Lib_SecureMerkleTrie
.get(abi.encodePacked(slot), storageTrieWitness, account.storageRoot);
require(storageExists, "Storage value does not exist");
return toBytes32PadLeft(Lib_RLPReader.readBytes(retrievedValue));
if (storageExists) {
return (true, toBytes32PadLeft(Lib_RLPReader.readBytes(retrievedValue)));
}
return (false, bytes32(0));
}

// Ported old function from Lib_BytesUtils.sol
Expand Down
6 changes: 0 additions & 6 deletions packages/contracts/scripts/constants.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
export const ROLLUP_ADDRESSES = {
goerli: "0xE87d317eB8dcc9afE24d9f63D6C760e52Bc18A40",
hardhat: "0xE87d317eB8dcc9afE24d9f63D6C760e52Bc18A40",
localhost: "0xE87d317eB8dcc9afE24d9f63D6C760e52Bc18A40",
};

export const REGISTRY_ADDRESS = {
goerli: "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e",
hardhat: "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e",
Expand Down
7 changes: 3 additions & 4 deletions packages/contracts/scripts/deployL1.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ethers, network, run } from "hardhat";
import { REGISTRY_ADDRESS, ROLLUP_ADDRESSES } from "./constants";
import { REGISTRY_ADDRESS } from "./constants";
const ensRegistryAbi = require("../abi/ENSRegistry.json");

const HARDHAT_NETWORK_CHAIN_ID = 31337;
Expand All @@ -22,9 +22,8 @@ async function main() {

// Deploy Linea Resolver Stub to L1
const gatewayUrl = process.env.GATEWAY_URL ? process.env.GATEWAY_URL : "http://localhost:8080/{sender}/{data}.json";
const rollupAddress = ROLLUP_ADDRESSES[network.name as keyof typeof ROLLUP_ADDRESSES];
const LineaResolverStub = await ethers.getContractFactory("LineaResolverStub");
const lineaResolverStub = await LineaResolverStub.deploy([gatewayUrl], rollupAddress, L2_RESOLVER_ADDRESS);
const lineaResolverStub = await LineaResolverStub.deploy([gatewayUrl], L2_RESOLVER_ADDRESS);
await lineaResolverStub.deployed();
console.log(`LineaResolverStub deployed to ${lineaResolverStub.address}`);
const registryAddr = REGISTRY_ADDRESS[network.name as keyof typeof REGISTRY_ADDRESS];
Expand All @@ -40,7 +39,7 @@ async function main() {
setTimeout(async () => {
await run("verify:verify", {
address: lineaResolverStub.address,
constructorArguments: [[gatewayUrl], rollupAddress, L2_RESOLVER_ADDRESS],
constructorArguments: [[gatewayUrl], L2_RESOLVER_ADDRESS],
});
}, 20000);
}
Expand Down
110 changes: 110 additions & 0 deletions packages/contracts/test/LineaResolverStub.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { loadFixture } from "@nomicfoundation/hardhat-network-helpers";
import { ethers } from "hardhat";
import { expect } from "chai";
import { defaultAbiCoder } from "@ethersproject/abi";
// We use mocked results from the gateway and L2 resolver for the unit tests
import { DOMAIN_NAME, EXPECTED_RESOLVE_WITH_PROOF_RESULT, GATEWAY_URL, L2_RESOLVER_ADDRESS, MOCKED_PROOF, MOCKED_PROOF_UNDEFINED } from "./mocks/proof";

describe("LineaResolver", function () {
async function deployContractsFixture() {
const [owner] = await ethers.getSigners();

const gateways = [GATEWAY_URL];
const hash = ethers.utils.namehash(DOMAIN_NAME);

const undefinedDomain = "undefined.lineatest.eth";
const undefinedHash = ethers.utils.namehash(undefinedDomain);

// Deploy ResolverStub
const LineaResolverStub = await ethers.getContractFactory("LineaResolverStub");
const lineaResolverStub = await LineaResolverStub.deploy(gateways, L2_RESOLVER_ADDRESS);
await lineaResolverStub.deployed();

return {
owner,
lineaResolverStub,
hash,
undefinedHash,
gateways,
};
}

describe("initialization", async () => {
it("The contract should have been deployed correctly", async function () {
const { lineaResolverStub } = await loadFixture(deployContractsFixture);

expect(await lineaResolverStub.gateways(0)).to.be.equal(GATEWAY_URL);
expect(await lineaResolverStub.l2resolver()).to.be.equal(L2_RESOLVER_ADDRESS);
});
});

describe("resolveWithProof", async () => {
it("Should return the expected address", async function () {
const { lineaResolverStub, hash } = await loadFixture(deployContractsFixture);
// We prefix with the function signature 'addr(bytes32)' as bytes4
const extraData = "0x3b3b57de" + hash.slice(2);
const result = await lineaResolverStub.resolveWithProof(
defaultAbiCoder.encode(["(bytes32,bytes,bytes,bytes32,bytes,bytes)"], [Object.values(MOCKED_PROOF)]),
extraData,
);
expect(result).to.equal(EXPECTED_RESOLVE_WITH_PROOF_RESULT);
});

it("Should revert if hash does not match the proof", async function () {
const { lineaResolverStub, undefinedHash } = await loadFixture(deployContractsFixture);
const extraData = "0x3b3b57de" + undefinedHash.slice(2);
await expect(
lineaResolverStub.resolveWithProof(defaultAbiCoder.encode(["(bytes32,bytes,bytes,bytes32,bytes,bytes)"], [Object.values(MOCKED_PROOF)]), extraData),
).to.be.revertedWith("Invalid large internal hash");
});

it("Should return empty bytes if the function signature is not the one expected", async function () {
const { lineaResolverStub, undefinedHash } = await loadFixture(deployContractsFixture);
const extraData = "0x00000000" + undefinedHash.slice(2);
const result = await lineaResolverStub.resolveWithProof(
defaultAbiCoder.encode(["(bytes32,bytes,bytes,bytes32,bytes,bytes)"], [Object.values(MOCKED_PROOF)]),
extraData,
);
expect(result).to.be.equal("0x");
});

it("Should return empty bytes if the domain does not exists but the proof is correct", async function () {
const { lineaResolverStub, undefinedHash } = await loadFixture(deployContractsFixture);
const extraData = "0x3b3b57de" + undefinedHash.slice(2);
const result = await lineaResolverStub.resolveWithProof(
defaultAbiCoder.encode(["(bytes32,bytes,bytes,bytes32,bytes,bytes)"], [Object.values(MOCKED_PROOF_UNDEFINED)]),
extraData,
);
expect(result).to.be.equal("0x");
});
});

describe("resolve", async () => {
it("Should revert with OffchainLookup when calling resolve", async function () {
const { lineaResolverStub, hash } = await loadFixture(deployContractsFixture);
const extraData = "0x3b3b57de" + hash.slice(2);
const encodedName = ethers.utils.dnsEncode(DOMAIN_NAME);
try {
await lineaResolverStub.resolve(encodedName, extraData);
} catch (error) {
expect(error.errorName).to.equal("OffchainLookup");
}
});
});

describe("supportsInterface", async () => {
it("should return true for valid interfaceId", async () => {
const { lineaResolverStub } = await loadFixture(deployContractsFixture);
const anotherInterfaceId = "0x9061b923";
const result = await lineaResolverStub.supportsInterface(anotherInterfaceId);
expect(result).to.be.equal(true);
});

it("should return false for invalid interfaceId", async () => {
const { lineaResolverStub } = await loadFixture(deployContractsFixture);
const invalidInterfaceId = "0x89abcdef";
const result = await lineaResolverStub.supportsInterface(invalidInterfaceId);
expect(result).to.be.equal(false);
});
});
});
Loading

0 comments on commit bb92289

Please sign in to comment.