Skip to content

Commit

Permalink
Merge pull request #1412 from privacy-scaling-explorations/test/mock-…
Browse files Browse the repository at this point in the history
…gatekeepers

test(eas): mock gatekeeper tests
  • Loading branch information
0xmad authored Apr 30, 2024
2 parents 4fc4cc7 + 8f3fd14 commit 86ee506
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 78 deletions.
2 changes: 1 addition & 1 deletion contracts/contracts/gatekeepers/EASGatekeeper.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
pragma solidity ^0.8.20;

import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";

Expand Down
104 changes: 104 additions & 0 deletions contracts/contracts/mocks/MockEAS.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import { IEAS } from "../interfaces/IEAS.sol";

/// @title MockEAS
/// @notice A mock contract to test the EASGatekeeper
contract MockEAS is IEAS {
address public immutable attester;
bytes32 public immutable schema;

address public recipient;

constructor(address _attester, bytes32 _schema, address _recipient) {
attester = _attester;
schema = _schema;
recipient = _recipient;
}

/// @notice Set the attestation recipient (mock)
function setRecipient(address _recipient) public {
recipient = _recipient;
}

/// @inheritdoc IEAS
function getAttestation(bytes32 attestationId) external view override returns (Attestation memory) {
// revoked
if (attestationId == 0x0000000000000000000000000000000000000000000000000000000000000001) {
return
Attestation({
uid: "0x000000000000000000000000000001",
schema: schema,
time: 0,
expirationTime: 0,
revocationTime: 1,
refUID: "0x000000000000000000000000000001",
recipient: recipient,
attester: attester,
revocable: true,
data: ""
});
// invalid schema
} else if (attestationId == 0x0000000000000000000000000000000000000000000000000000000000000002) {
return
Attestation({
uid: "0x000000000000000000000000000001",
schema: "0x000000000000000000000000000001",
time: 0,
expirationTime: 0,
revocationTime: 0,
refUID: "0x000000000000000000000000000001",
recipient: recipient,
attester: attester,
revocable: false,
data: ""
});
// invalid recipient
} else if (attestationId == 0x0000000000000000000000000000000000000000000000000000000000000003) {
return
Attestation({
uid: "0x000000000000000000000000000001",
schema: schema,
time: 0,
expirationTime: 0,
revocationTime: 0,
refUID: "0x000000000000000000000000000001",
recipient: address(0),
attester: attester,
revocable: false,
data: ""
});
// invalid attester
} else if (attestationId == 0x0000000000000000000000000000000000000000000000000000000000000004) {
return
Attestation({
uid: "0x000000000000000000000000000001",
schema: schema,
time: 0,
expirationTime: 0,
revocationTime: 0,
refUID: "0x000000000000000000000000000001",
recipient: recipient,
attester: address(0),
revocable: false,
data: ""
});
// valid
} else {
return
Attestation({
uid: "0x000000000000000000000000000001",
schema: schema,
time: 0,
expirationTime: 0,
revocationTime: 0,
refUID: "0x000000000000000000000000000001",
recipient: recipient,
attester: attester,
revocable: false,
data: ""
});
}
}
}
107 changes: 30 additions & 77 deletions contracts/tests/EASGatekeeper.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { expect } from "chai";
import dotenv from "dotenv";
import { AbiCoder, Signer, ZeroAddress, toBeArray } from "ethers";
import { ethers, network } from "hardhat";
import { Keypair } from "maci-domainobjs";

import { deployContract } from "../ts/deploy";
import { getDefaultSigner, getSigners, sleep } from "../ts/utils";
import { getDefaultSigner, getSigners } from "../ts/utils";
import { EASGatekeeper, MACI } from "../typechain-types";

import { STATE_TREE_DEPTH, initialVoiceCreditBalance } from "./constants";
Expand All @@ -17,52 +16,25 @@ describe("EAS Gatekeeper", () => {
let easGatekeeper: EASGatekeeper;
let signer: Signer;
let signerAddress: string;
let easAddress: string;

const schema = "0xfdcfdad2dbe7489e0ce56b260348b7f14e8365a8a325aef9834818c00d46b31b";
const easAddress = "0x4200000000000000000000000000000000000021";
const attestation = "0xe9d4e5a14ec840656d9def34075d9523d1536176d5f0a7d574f2e93bea641b66";
const revokedAttestation = "0x10207e0381318820574f8c99efde13b9dfe0b24114c9ec5337109d2435a8f13b";
const invalidAttesterAttestation = "0x5f66cb6eaebece82ec3a47918afee718afa7dca838faab1ee156df3a6187cb9c";
const attestationOwner = "0x849151d7D0bF1F34b70d5caD5149D28CC2308bf1";
const trustedAttester = "0x621477dBA416E12df7FF0d48E14c4D20DC85D7D9";
// random gitcoin passport attestation
const wrongAttestation = "0x8c60cf319b553194519098c7ecaad38fc0e818c538b939730c0b55bb1eeedaae";

const revokedAttestation = "0x0000000000000000000000000000000000000000000000000000000000000001";
const invalidSchemaAttestation = "0x0000000000000000000000000000000000000000000000000000000000000002";
const invalidRecipientAttestation = "0x0000000000000000000000000000000000000000000000000000000000000003";
const invalidAttesterAttestation = "0x0000000000000000000000000000000000000000000000000000000000000004";
// valid attestation
const attestation = "0x0000000000000000000000000000000000000000000000000000000000000000";

const user = new Keypair();

before(async () => {
// fork the optimism mainnet network
if (network.name === "hardhat") {
await network.provider.request({
method: "hardhat_reset",
params: [
{
forking: {
jsonRpcUrl: process.env.OP_RPC_URL || "https://optimism.drpc.org",
},
},
],
});
}
signer = await getDefaultSigner();
signerAddress = await signer.getAddress();
easGatekeeper = await deployContract("EASGatekeeper", signer, true, easAddress, trustedAttester, toBeArray(schema));
});

after(async () => {
// we reset
if (network.name === "hardhat") {
await network.provider.request({
method: "hardhat_reset",
params: [],
});
}
});

// add some sleep to ensure we don't have problems with the fork
// as one might use a free RPC plan
afterEach(async () => {
await sleep(3000);
const mockEAS = await deployContract("MockEAS", signer, true, signerAddress, toBeArray(schema), signerAddress);
easAddress = await mockEAS.getAddress();
easGatekeeper = await deployContract("EASGatekeeper", signer, true, easAddress, signerAddress, toBeArray(schema));
});

describe("Deployment", () => {
Expand All @@ -72,7 +44,7 @@ describe("EAS Gatekeeper", () => {

it("should fail to deploy when the eas contract address is not valid", async () => {
await expect(
deployContract("EASGatekeeper", signer, true, ZeroAddress, trustedAttester, toBeArray(schema)),
deployContract("EASGatekeeper", signer, true, ZeroAddress, signerAddress, toBeArray(schema)),
).to.be.revertedWithCustomError(easGatekeeper, "ZeroAddress");
});

Expand Down Expand Up @@ -117,76 +89,57 @@ describe("EAS Gatekeeper", () => {
it("should throw when the attestation is not owned by the caller (mocking maci.signUp call)", async () => {
await easGatekeeper.setMaciInstance(signerAddress).then((tx) => tx.wait());

await expect(easGatekeeper.register(signerAddress, toBeArray(attestation))).to.be.revertedWithCustomError(
await expect(easGatekeeper.register(signerAddress, invalidRecipientAttestation)).to.be.revertedWithCustomError(
easGatekeeper,
"NotYourAttestation",
);
});

it("should throw when the attestation has been revoked", async () => {
await expect(easGatekeeper.register(signerAddress, toBeArray(revokedAttestation))).to.be.revertedWithCustomError(
await expect(easGatekeeper.register(signerAddress, revokedAttestation)).to.be.revertedWithCustomError(
easGatekeeper,
"AttestationRevoked",
);
});

it("should throw when the attestation schema is not the one expected by the gatekeeper", async () => {
await easGatekeeper.setMaciInstance(signerAddress).then((tx) => tx.wait());
await expect(easGatekeeper.register(signerAddress, toBeArray(wrongAttestation))).to.be.revertedWithCustomError(
await expect(easGatekeeper.register(signerAddress, invalidSchemaAttestation)).to.be.revertedWithCustomError(
easGatekeeper,
"InvalidSchema",
);
});

it("should throw when the attestation is not signed by the attestation owner", async () => {
await easGatekeeper.setMaciInstance(signerAddress).then((tx) => tx.wait());
await expect(
easGatekeeper.register(signerAddress, toBeArray(invalidAttesterAttestation)),
).to.be.revertedWithCustomError(easGatekeeper, "AttesterNotTrusted");
await expect(easGatekeeper.register(signerAddress, invalidAttesterAttestation)).to.be.revertedWithCustomError(
easGatekeeper,
"AttesterNotTrusted",
);
});

it("should register a user if the register function is called with the valid data", async () => {
// impersonate a user that owns the attestation
await network.provider.request({
method: "hardhat_impersonateAccount",
params: [attestationOwner],
});

const userSigner = await ethers.getSigner(attestationOwner);

await easGatekeeper.setMaciInstance(await maciContract.getAddress()).then((tx) => tx.wait());

// signup via MACI
const tx = await maciContract
.connect(userSigner)
.signUp(
user.pubKey.asContractParam(),
toBeArray(attestation),
AbiCoder.defaultAbiCoder().encode(["uint256"], [1]),
);
const tx = await maciContract.signUp(
user.pubKey.asContractParam(),
attestation,
AbiCoder.defaultAbiCoder().encode(["uint256"], [1]),
);

const receipt = await tx.wait();

expect(receipt?.status).to.eq(1);
});

it("should prevent signing up twice", async () => {
// impersonate a user that owns the attestation
await network.provider.request({
method: "hardhat_impersonateAccount",
params: [attestationOwner],
});

const userSigner = await ethers.getSigner(attestationOwner);

await expect(
maciContract
.connect(userSigner)
.signUp(
user.pubKey.asContractParam(),
toBeArray(attestation),
AbiCoder.defaultAbiCoder().encode(["uint256"], [1]),
),
maciContract.signUp(
user.pubKey.asContractParam(),
attestation,
AbiCoder.defaultAbiCoder().encode(["uint256"], [1]),
),
).to.be.revertedWithCustomError(easGatekeeper, "AlreadyRegistered");
});
});
Expand Down

0 comments on commit 86ee506

Please sign in to comment.