Skip to content

Commit

Permalink
Merge branch 'feat/subgraph-pr' into feat/subgraph
Browse files Browse the repository at this point in the history
  • Loading branch information
ctrlc03 authored Jun 6, 2024
2 parents dbbeb36 + f0b482d commit 5d25d3a
Show file tree
Hide file tree
Showing 10 changed files with 713 additions and 4 deletions.
87 changes: 87 additions & 0 deletions contracts/contracts/gatekeepers/zupass/ZupassGatekeeper.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

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

import { SignUpGatekeeper } from "../SignUpGatekeeper.sol";

import { ZupassGroth16Verifier } from "./ZupassGroth16Verifier.sol";

/// @title ZupassGatekeeper
/// @notice This contract allows to gatekeep MACI signups
/// by requiring new voters to own a certain Zupass event ticket
contract ZupassGatekeeper is SignUpGatekeeper, Ownable(msg.sender) {
/// @notice the Zupass event UUID converted to bigint
uint256 public immutable validEventId;

/// @notice the Zupass event signer converted to bigint
uint256 public immutable validSigner1;
uint256 public immutable validSigner2;

/// @notice the ZupassGroth16Verifier contract address
ZupassGroth16Verifier public immutable verifier;

/// @notice the reference to the MACI contract
address public maci;

/// @notice a mapping of ticket IDs to whether they have been used
mapping(uint256 => bool) public registeredTickets;

/// @notice custom errors
error AlreadyRegistered();
error InvalidProof();
error InvalidEventId();
error InvalidSigners();
error InvalidWatermark();
error OnlyMACI();
error ZeroAddress();

/// @notice Creates a new ZupassGatekeeper
/// @param _validEventId Zupass event UUID converted to bigint
/// @param _validSigner1 Zupass event signer[0] converted to bigint
/// @param _validSigner2 Zupass event signer[1] converted to bigint
/// @param _verifier The ZupassGroth16Verifier contract address
constructor(uint256 _validEventId, uint256 _validSigner1, uint256 _validSigner2, address _verifier) payable {
validEventId = _validEventId;
validSigner1 = _validSigner1;
validSigner2 = _validSigner2;
verifier = ZupassGroth16Verifier(_verifier);
}

/// @notice Adds an uninitialised MACI instance to allow for token signups
/// @param _maci The MACI contract interface to be stored
function setMaciInstance(address _maci) public override onlyOwner {
if (_maci == address(0)) revert ZeroAddress();
maci = _maci;
}

/// @notice Registers the user only if they have the Zupass event ticket
/// @param _user The user's Ethereum address.
/// @param _data The ABI-encoded proof and public signals.
function register(address _user, bytes memory _data) public override {
if (maci != msg.sender) revert OnlyMACI();

// Decode the given _data bytes
(uint256[2] memory _pA, uint256[2][2] memory _pB, uint256[2] memory _pC, uint256[38] memory _pubSignals) = abi
.decode(_data, (uint256[2], uint256[2][2], uint256[2], uint256[38]));

// Ticket ID is stored at index 0
uint256 ticketId = _pubSignals[0];
if (registeredTickets[ticketId]) revert AlreadyRegistered();

registeredTickets[ticketId] = true;

// Verify proof
if (!verifier.verifyProof(_pA, _pB, _pC, _pubSignals)) revert InvalidProof();

// Event id is stored at index 15
if (_pubSignals[15] != validEventId) revert InvalidEventId();

// Signers are stored at index 13 and 14
if (_pubSignals[13] != validSigner1 || _pubSignals[14] != validSigner2) revert InvalidSigners();

// Watermark is stored at index 37
// user address converted to bigint is used as the watermark
if (_pubSignals[37] != uint256(uint160(_user))) revert InvalidWatermark();
}
}
428 changes: 428 additions & 0 deletions contracts/contracts/gatekeepers/zupass/ZupassGroth16Verifier.sol

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions contracts/deploy-config-example.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@
"decoderAddress": "0xe53C60F8069C2f0c3a84F9B3DB5cf56f3100ba56",
"passingScore": 5
},
"ZupassGatekeeper": {
"deploy": false,
"signer1": "13908133709081944902758389525983124100292637002438232157513257158004852609027",
"signer2": "7654374482676219729919246464135900991450848628968334062174564799457623790084",
"eventId": "69c0caaa-c65d-5345-a20c-867774f18c67",
"zupassVerifier": "0x2272cdb3596617886d0F48524DA486044E0376d6"
},
"MACI": {
"stateTreeDepth": 10,
"gatekeeper": "EASGatekeeper"
Expand Down
5 changes: 4 additions & 1 deletion contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
"test:eas_gatekeeper": "pnpm run test ./tests/EASGatekeeper.test.ts",
"test:hats_gatekeeper": "pnpm run test ./tests/HatsGatekeeper.test.ts",
"test:gitcoin_gatekeeper": "pnpm run test ./tests/GitcoinPassportGatekeeper.test.ts",
"test:zupass_gatekeeper": "pnpm run test ./tests/ZupassGatekeeper.test.ts",
"deploy": "hardhat deploy-full",
"deploy-poll": "hardhat deploy-poll",
"verify": "hardhat verify-full",
Expand Down Expand Up @@ -83,7 +84,8 @@
"maci-core": "1.2.2",
"maci-crypto": "1.2.2",
"maci-domainobjs": "1.2.2",
"solidity-docgen": "^0.6.0-beta.36"
"solidity-docgen": "^0.6.0-beta.36",
"uuid": "^9.0.0"
},
"devDependencies": {
"@types/chai": "^4.3.11",
Expand All @@ -92,6 +94,7 @@
"@types/mocha": "^10.0.6",
"@types/node": "^20.12.12",
"@types/snarkjs": "^0.7.8",
"@types/uuid": "^9.0.2",
"chai": "^4.3.10",
"dotenv": "^16.4.5",
"hardhat-artifactor": "^0.2.0",
Expand Down
43 changes: 42 additions & 1 deletion contracts/tasks/deploy/maci/02-gatekeepers.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ESupportedChains } from "../../helpers/constants";
import { ContractStorage } from "../../helpers/ContractStorage";
import { Deployment } from "../../helpers/Deployment";
import { uuidToBigInt } from "../../helpers/numericParser";
import { EContracts, IDeployParams } from "../../helpers/types";

const deployment = Deployment.getInstance();
Expand All @@ -17,20 +18,27 @@ deployment.deployTask("full:deploy-gatekeepers", "Deploy gatekeepers").then((tas
const freeForAllGatekeeperContractAddress = storage.getAddress(EContracts.FreeForAllGatekeeper, hre.network.name);
const easGatekeeperContractAddress = storage.getAddress(EContracts.EASGatekeeper, hre.network.name);
const gitcoinGatekeeperContractAddress = storage.getAddress(EContracts.GitcoinPassportGatekeeper, hre.network.name);
const zupassGatekeeperContractAddress = storage.getAddress(EContracts.ZupassGatekeeper, hre.network.name);
const deployFreeForAllGatekeeper = deployment.getDeployConfigField(EContracts.FreeForAllGatekeeper, "deploy");
const deployEASGatekeeper = deployment.getDeployConfigField(EContracts.EASGatekeeper, "deploy");
const deployGitcoinGatekeeper = deployment.getDeployConfigField(EContracts.GitcoinPassportGatekeeper, "deploy");
const deployZupassGatekeeper = deployment.getDeployConfigField(EContracts.ZupassGatekeeper, "deploy");

const skipDeployFreeForAllGatekeeper = deployFreeForAllGatekeeper !== true;
const skipDeployEASGatekeeper = deployEASGatekeeper !== true;
const skipDeployGitcoinGatekeeper = deployGitcoinGatekeeper !== true;
const skipDeployZupassGatekeeper = deployZupassGatekeeper !== true;

const canSkipDeploy =
incremental &&
(freeForAllGatekeeperContractAddress || skipDeployFreeForAllGatekeeper) &&
(easGatekeeperContractAddress || skipDeployEASGatekeeper) &&
(gitcoinGatekeeperContractAddress || skipDeployGitcoinGatekeeper) &&
(!skipDeployFreeForAllGatekeeper || !skipDeployEASGatekeeper || !skipDeployGitcoinGatekeeper);
(zupassGatekeeperContractAddress || skipDeployZupassGatekeeper) &&
(!skipDeployFreeForAllGatekeeper ||
!skipDeployEASGatekeeper ||
!skipDeployGitcoinGatekeeper ||
!skipDeployZupassGatekeeper);

if (canSkipDeploy) {
return;
Expand Down Expand Up @@ -110,5 +118,38 @@ deployment.deployTask("full:deploy-gatekeepers", "Deploy gatekeepers").then((tas
network: hre.network.name,
});
}

if (!skipDeployZupassGatekeeper) {
const eventId = deployment.getDeployConfigField<string>(EContracts.ZupassGatekeeper, "eventId", true);
const validEventId = uuidToBigInt(eventId);
const validSigner1 = deployment.getDeployConfigField<string>(EContracts.ZupassGatekeeper, "signer1", true);
const validSigner2 = deployment.getDeployConfigField<string>(EContracts.ZupassGatekeeper, "signer2", true);
let verifier = deployment.getDeployConfigField<string | undefined>(EContracts.ZupassGatekeeper, "zupassVerifier");

if (!verifier) {
const verifierContract = await deployment.deployContract({
name: EContracts.ZupassGroth16Verifier,
signer: deployer,
});
verifier = await verifierContract.getAddress();
}

const ZupassGatekeeperContract = await deployment.deployContract(
{
name: EContracts.ZupassGatekeeper,
signer: deployer,
},
validEventId,
validSigner1,
validSigner2,
verifier,
);
await storage.register({
id: EContracts.ZupassGatekeeper,
contract: ZupassGatekeeperContract,
args: [validEventId.toString(), validSigner1, validSigner2, verifier],
network: hre.network.name,
});
}
}),
);
9 changes: 8 additions & 1 deletion contracts/tasks/deploy/maci/08-maci.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { EASGatekeeper, GitcoinPassportGatekeeper, MACI } from "../../../typechain-types";
import type { EASGatekeeper, GitcoinPassportGatekeeper, ZupassGatekeeper, MACI } from "../../../typechain-types";

import { ContractStorage } from "../../helpers/ContractStorage";
import { Deployment } from "../../helpers/Deployment";
Expand Down Expand Up @@ -81,6 +81,13 @@ deployment.deployTask("full:deploy-maci", "Deploy MACI contract").then((task) =>
});
const maciInstanceAddress = await maciContract.getAddress();

await gatekeeperContract.setMaciInstance(maciInstanceAddress).then((tx) => tx.wait());
} else if (gatekeeper === EContracts.ZupassGatekeeper) {
const gatekeeperContract = await deployment.getContract<ZupassGatekeeper>({
name: EContracts.ZupassGatekeeper,
address: gatekeeperContractAddress,
});
const maciInstanceAddress = await maciContract.getAddress();
await gatekeeperContract.setMaciInstance(maciInstanceAddress).then((tx) => tx.wait());
}

Expand Down
10 changes: 10 additions & 0 deletions contracts/tasks/helpers/numericParser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { parse as uuidParse } from "uuid";

/**
* Converts a UUID string into a bigint.
*/
export function uuidToBigInt(v: string): bigint {
// a uuid is just a particular representation of 16 bytes
const bytes = uuidParse(v);
return BigInt(`0x${Buffer.from(bytes).toString("hex")}`);
}
2 changes: 2 additions & 0 deletions contracts/tasks/helpers/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,8 @@ export enum EContracts {
FreeForAllGatekeeper = "FreeForAllGatekeeper",
EASGatekeeper = "EASGatekeeper",
GitcoinPassportGatekeeper = "GitcoinPassportGatekeeper",
ZupassGatekeeper = "ZupassGatekeeper",
ZupassGroth16Verifier = "ZupassGroth16Verifier",
Verifier = "Verifier",
MACI = "MACI",
StateAq = "StateAq",
Expand Down
Loading

0 comments on commit 5d25d3a

Please sign in to comment.