feat: use hardhat-deploy, clean up poseidon hasher tests

This commit is contained in:
rymnc 2022-11-25 11:13:49 +05:30
parent a95e388991
commit 4cbfc0feb0
No known key found for this signature in database
GPG Key ID: C740033EE3F41EBD
24 changed files with 1558 additions and 211 deletions

9
common/chain-utils.ts Normal file
View File

@ -0,0 +1,9 @@
export const devNets = [
'hardhat',
'localhost',
'goerli',
]
export const prodNets = ['mainnet']
export const isDevNet = (networkName: string) => devNets.includes(networkName)

3
common/index.ts Normal file
View File

@ -0,0 +1,3 @@
// barrel for all other utils
export * from './interep-utils';
export * from './chain-utils';

33
common/interep-utils.ts Normal file
View File

@ -0,0 +1,33 @@
import {utils} from "ethers";
export const sToBytes32 = (str: string): string => {
return utils.formatBytes32String(str);
}
export const SNARK_SCALAR_FIELD = BigInt(
"21888242871839275222246405745257275088548364400416034343698204186575808495617"
)
export const createGroupId = (provider: string, name: string): bigint => {
const providerBytes = sToBytes32(provider);
const nameBytes = sToBytes32(name);
return BigInt(utils.solidityKeccak256(["bytes32", "bytes32"], [providerBytes, nameBytes])) % SNARK_SCALAR_FIELD
}
const providers = ['github', 'twitter', 'reddit'];
const tiers = ['bronze', 'silver', 'gold'];
export const getGroups = () => {
return providers.flatMap(provider => tiers.map(tier => {
return {
provider: sToBytes32(provider),
name: sToBytes32(tier),
root: 1,
depth: 10,
}
}));
}
export const getValidGroups = () => {
return getGroups().filter(group => group.name !== sToBytes32('bronze'));
}

View File

@ -1,98 +1,151 @@
pragma solidity 0.8.15;
import { IPoseidonHasher } from "./PoseidonHasher.sol";
import {IPoseidonHasher} from "./PoseidonHasher.sol";
import {IValidGroupStorage} from "./ValidGroupStorage.sol";
import {IInterep} from "@interep/contracts/IInterep.sol";
contract RLN {
uint256 public immutable MEMBERSHIP_DEPOSIT;
uint256 public immutable DEPTH;
uint256 public immutable SET_SIZE;
uint256 public immutable MEMBERSHIP_DEPOSIT;
uint256 public immutable DEPTH;
uint256 public immutable SET_SIZE;
uint256 public pubkeyIndex = 0;
mapping(uint256 => uint256) public members;
uint256 public pubkeyIndex = 0;
mapping(uint256 => uint256) public members;
IPoseidonHasher public poseidonHasher;
IPoseidonHasher public poseidonHasher;
IValidGroupStorage public validGroupStorage;
IInterep public interep;
event MemberRegistered(uint256 pubkey, uint256 index);
event MemberWithdrawn(uint256 pubkey, uint256 index);
event MemberRegistered(uint256 pubkey, uint256 index);
event MemberWithdrawn(uint256 pubkey, uint256 index);
constructor(
uint256 membershipDeposit,
uint256 depth,
address _poseidonHasher
) {
MEMBERSHIP_DEPOSIT = membershipDeposit;
DEPTH = depth;
SET_SIZE = 1 << depth;
poseidonHasher = IPoseidonHasher(_poseidonHasher);
}
constructor(
uint256 membershipDeposit,
uint256 depth,
address _poseidonHasher,
address _validGroupStorage
) {
MEMBERSHIP_DEPOSIT = membershipDeposit;
DEPTH = depth;
SET_SIZE = 1 << depth;
poseidonHasher = IPoseidonHasher(_poseidonHasher);
validGroupStorage = IValidGroupStorage(_validGroupStorage);
interep = IInterep(validGroupStorage.interep());
}
function register(uint256 pubkey) external payable {
require(pubkeyIndex < SET_SIZE, "RLN, register: set is full");
require(msg.value == MEMBERSHIP_DEPOSIT, "RLN, register: membership deposit is not satisfied");
_register(pubkey);
}
function register(uint256 pubkey) external payable {
require(pubkeyIndex < SET_SIZE, "RLN, register: set is full");
require(
msg.value == MEMBERSHIP_DEPOSIT,
"RLN, register: membership deposit is not satisfied"
);
_register(pubkey);
}
function registerBatch(uint256[] calldata pubkeys) external payable {
uint256 pubkeylen = pubkeys.length;
require(pubkeyIndex + pubkeylen <= SET_SIZE, "RLN, registerBatch: set is full");
require(msg.value == MEMBERSHIP_DEPOSIT * pubkeylen, "RLN, registerBatch: membership deposit is not satisfied");
for (uint256 i = 0; i < pubkeylen; i++) {
_register(pubkeys[i]);
}
}
function register(
uint256 groupId,
string calldata signal,
uint256 nullifierHash,
uint256 externalNullifier,
uint256[8] calldata proof,
uint256 pubkey
) external {
require(
validGroupStorage.isValidGroup(groupId),
"RLN, register: invalid interep group"
);
require(pubkeyIndex < SET_SIZE, "RLN, register: set is full");
// TODO: verify proof
_register(pubkey);
}
function _register(uint256 pubkey) internal {
members[pubkeyIndex] = pubkey;
emit MemberRegistered(pubkey, pubkeyIndex);
pubkeyIndex += 1;
}
function registerBatch(uint256[] calldata pubkeys) external payable {
uint256 pubkeylen = pubkeys.length;
require(
pubkeyIndex + pubkeylen <= SET_SIZE,
"RLN, registerBatch: set is full"
);
require(
msg.value == MEMBERSHIP_DEPOSIT * pubkeylen,
"RLN, registerBatch: membership deposit is not satisfied"
);
for (uint256 i = 0; i < pubkeylen; i++) {
_register(pubkeys[i]);
}
}
function withdrawBatch(
uint256[] calldata secrets,
uint256[] calldata pubkeyIndexes,
address payable[] calldata receivers
) external {
uint256 batchSize = secrets.length;
require(batchSize != 0, "RLN, withdrawBatch: batch size zero");
require(batchSize == pubkeyIndexes.length, "RLN, withdrawBatch: batch size mismatch pubkey indexes");
require(batchSize == receivers.length, "RLN, withdrawBatch: batch size mismatch receivers");
for (uint256 i = 0; i < batchSize; i++) {
_withdraw(secrets[i], pubkeyIndexes[i], receivers[i]);
}
}
function _register(uint256 pubkey) internal {
members[pubkeyIndex] = pubkey;
emit MemberRegistered(pubkey, pubkeyIndex);
pubkeyIndex += 1;
}
function withdraw(
uint256 secret,
uint256 _pubkeyIndex,
address payable receiver
) external {
_withdraw(secret, _pubkeyIndex, receiver);
}
function withdrawBatch(
uint256[] calldata secrets,
uint256[] calldata pubkeyIndexes,
address payable[] calldata receivers
) external {
uint256 batchSize = secrets.length;
require(batchSize != 0, "RLN, withdrawBatch: batch size zero");
require(
batchSize == pubkeyIndexes.length,
"RLN, withdrawBatch: batch size mismatch pubkey indexes"
);
require(
batchSize == receivers.length,
"RLN, withdrawBatch: batch size mismatch receivers"
);
for (uint256 i = 0; i < batchSize; i++) {
_withdraw(secrets[i], pubkeyIndexes[i], receivers[i]);
}
}
function _withdraw(
uint256 secret,
uint256 _pubkeyIndex,
address payable receiver
) internal {
require(_pubkeyIndex < SET_SIZE, "RLN, _withdraw: invalid pubkey index");
require(members[_pubkeyIndex] != 0, "RLN, _withdraw: member doesn't exist");
require(receiver != address(0), "RLN, _withdraw: empty receiver address");
function withdraw(
uint256 secret,
uint256 _pubkeyIndex,
address payable receiver
) external {
_withdraw(secret, _pubkeyIndex, receiver);
}
// derive public key
uint256 pubkey = hash(secret);
require(members[_pubkeyIndex] == pubkey, "RLN, _withdraw: not verified");
function _withdraw(
uint256 secret,
uint256 _pubkeyIndex,
address payable receiver
) internal {
require(
_pubkeyIndex < SET_SIZE,
"RLN, _withdraw: invalid pubkey index"
);
require(
members[_pubkeyIndex] != 0,
"RLN, _withdraw: member doesn't exist"
);
require(
receiver != address(0),
"RLN, _withdraw: empty receiver address"
);
// delete member
members[_pubkeyIndex] = 0;
// derive public key
uint256 pubkey = hash(secret);
require(
members[_pubkeyIndex] == pubkey,
"RLN, _withdraw: not verified"
);
// refund deposit
(bool sent, bytes memory data) = receiver.call{value: MEMBERSHIP_DEPOSIT}("");
// delete member
members[_pubkeyIndex] = 0;
// refund deposit
(bool sent, bytes memory data) = receiver.call{
value: MEMBERSHIP_DEPOSIT
}("");
require(sent, "transfer failed");
emit MemberWithdrawn(pubkey, _pubkeyIndex);
}
emit MemberWithdrawn(pubkey, _pubkeyIndex);
}
function hash(uint256 input) internal view returns (uint256) {
return poseidonHasher.hash(input);
}
}
function hash(uint256 input) internal view returns (uint256) {
return poseidonHasher.hash(input);
}
}

View File

@ -3,6 +3,12 @@ pragma solidity ^0.8.4;
import "@interep/contracts/Interep.sol";
interface IValidGroupStorage {
function isValidGroup(uint256 groupId) external view returns (bool);
function interep() external view returns (address);
}
contract ValidGroupStorage {
mapping(uint256 => bool) public validGroups;

View File

@ -0,0 +1,16 @@
import {HardhatRuntimeEnvironment} from 'hardhat/types';
import {DeployFunction} from 'hardhat-deploy/types';
const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
const {deployments, getUnnamedAccounts} = hre;
const {deploy} = deployments;
const [deployer] = await getUnnamedAccounts();
await deploy('PoseidonHasher', {
from: deployer,
log: true,
});
};
export default func;
func.tags = ['PoseidonHasher'];

View File

@ -0,0 +1,35 @@
import {HardhatRuntimeEnvironment} from 'hardhat/types';
import {DeployFunction} from 'hardhat-deploy/types';
import {
getGroups,
isDevNet
} from '../common';
const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
const {deployments, getUnnamedAccounts} = hre;
const {deploy} = deployments;
const [deployer] = await getUnnamedAccounts();
const interepTest = await deploy('InterepTest', {
from: deployer,
log: true,
args: [],
});
const contract = await hre.ethers.getContractAt('InterepTest', interepTest.address);
const groups = getGroups();
const groupInsertionTx = await contract.updateGroups(groups);
await groupInsertionTx.wait();
};
export default func;
func.tags = ['InterepTest'];
// skip when running on mainnet
func.skip = async (hre: HardhatRuntimeEnvironment) => {
if (isDevNet(hre.network.name)) {
return false;
}
return true;
}

View File

@ -0,0 +1,23 @@
import {HardhatRuntimeEnvironment} from 'hardhat/types';
import {DeployFunction} from 'hardhat-deploy/types';
import {getValidGroups, isDevNet} from '../common';
const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
const {deployments, getUnnamedAccounts} = hre;
const {deploy} = deployments;
const [deployer] = await getUnnamedAccounts();
if (!isDevNet(hre.network.name)) {
throw new Error('Interep not deployed on mainnet yet.')
}
const interepAddress = isDevNet(hre.network.name) ? (await deployments.get('InterepTest')).address : '0x0000000000000000000000000000000000000000';
await deploy('ValidGroupStorage', {
from: deployer,
log: true,
args: [interepAddress, getValidGroups()]
});
};
export default func;
func.tags = ['ValidGroupStorage'];

20
deploy/004_deploy_rln.ts Normal file
View File

@ -0,0 +1,20 @@
import {HardhatRuntimeEnvironment} from 'hardhat/types';
import {DeployFunction} from 'hardhat-deploy/types';
const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
const {deployments, getUnnamedAccounts} = hre;
const {deploy} = deployments;
const [deployer] = await getUnnamedAccounts();
const poseidonHasherAddress = (await deployments.get('PoseidonHasher')).address;
const validGroupStorageAddress = (await deployments.get('ValidGroupStorage')).address;
await deploy('RLN', {
from: deployer,
log: true,
args: [1000000000000000, 20, poseidonHasherAddress, validGroupStorageAddress]
});
};
export default func;
func.tags = ['RLN'];

View File

@ -0,0 +1 @@
31337

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,50 @@
{
"language": "Solidity",
"sources": {
"@interep/contracts/IInterep.sol": {
"content": "//SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\n/// @title Interep interface.\n/// @dev Interface of a Interep contract.\ninterface IInterep {\n struct Verifier {\n address contractAddress;\n uint8 merkleTreeDepth;\n }\n\n struct Group {\n bytes32 provider;\n bytes32 name;\n uint256 root;\n uint8 depth;\n }\n\n /// @dev Emitted when a Semaphore proof is verified.\n /// @param groupId: Id of the group.\n /// @param signal: Semaphore signal.\n event ProofVerified(uint256 indexed groupId, bytes32 signal);\n\n /// @dev Emitted when an Interep group is updated.\n /// @param groupId: Id of the group.\n /// @param provider: Provider of the group.\n /// @param name: Name of the group.\n /// @param root: Root hash of the tree.\n /// @param depth: Depth of the tree.\n event GroupUpdated(\n uint256 groupId,\n bytes32 indexed provider,\n bytes32 indexed name,\n uint256 root,\n uint8 indexed depth\n );\n\n /// @dev Updates the Interep groups.\n /// @param groups: List of Interep groups.\n function updateGroups(Group[] calldata groups) external;\n\n /// @dev Saves the nullifier hash to avoid double signaling and emits an event\n /// if the zero-knowledge proof is valid.\n /// @param groupId: Id of the group.\n /// @param signal: Semaphore signal.\n /// @param nullifierHash: Nullifier hash.\n /// @param externalNullifier: External nullifier.\n /// @param proof: Zero-knowledge proof.\n function verifyProof(\n uint256 groupId,\n bytes32 signal,\n uint256 nullifierHash,\n uint256 externalNullifier,\n uint256[8] calldata proof\n ) external;\n\n /// @dev Returns the root hash of an Interep group.\n /// @param groupId: Id of the group.\n /// @return Root hash of the group.\n function getRoot(uint256 groupId) external view returns (uint256);\n\n /// @dev Returns the tree depth of an Interep group.\n /// @param groupId: Id of the group.\n /// @return Tree depth of the group.\n function getDepth(uint256 groupId) external view returns (uint8);\n}\n"
},
"@semaphore-protocol/contracts/base/SemaphoreConstants.sol": {
"content": "//SPDX-License-Identifier: MIT\npragma solidity 0.8.4;\n\nuint256 constant SNARK_SCALAR_FIELD = 21888242871839275222246405745257275088548364400416034343698204186575808495617;\n"
},
"@semaphore-protocol/contracts/base/SemaphoreCore.sol": {
"content": "//SPDX-License-Identifier: MIT\npragma solidity 0.8.4;\n\nimport \"../interfaces/ISemaphoreCore.sol\";\nimport \"../interfaces/IVerifier.sol\";\n\n/// @title Semaphore core contract.\n/// @notice Minimal code to allow users to signal their endorsement of an arbitrary string.\n/// @dev The following code verifies that the proof is correct and saves the hash of the\n/// nullifier to prevent double-signaling. External nullifier and Merkle trees (i.e. groups) must be\n/// managed externally.\ncontract SemaphoreCore is ISemaphoreCore {\n /// @dev Asserts that no nullifier already exists and if the zero-knowledge proof is valid.\n /// Otherwise it reverts.\n /// @param signal: Semaphore signal.\n /// @param root: Root of the Merkle tree.\n /// @param nullifierHash: Nullifier hash.\n /// @param externalNullifier: External nullifier.\n /// @param proof: Zero-knowledge proof.\n /// @param verifier: Verifier address.\n function _verifyProof(\n bytes32 signal,\n uint256 root,\n uint256 nullifierHash,\n uint256 externalNullifier,\n uint256[8] calldata proof,\n IVerifier verifier\n ) internal view {\n uint256 signalHash = _hashSignal(signal);\n\n verifier.verifyProof(\n [proof[0], proof[1]],\n [[proof[2], proof[3]], [proof[4], proof[5]]],\n [proof[6], proof[7]],\n [root, nullifierHash, signalHash, externalNullifier]\n );\n }\n\n /// @dev Creates a keccak256 hash of the signal.\n /// @param signal: Semaphore signal.\n /// @return Hash of the signal.\n function _hashSignal(bytes32 signal) private pure returns (uint256) {\n return uint256(keccak256(abi.encodePacked(signal))) >> 8;\n }\n}\n"
},
"@semaphore-protocol/contracts/interfaces/ISemaphoreCore.sol": {
"content": "//SPDX-License-Identifier: MIT\npragma solidity 0.8.4;\n\n/// @title SemaphoreCore interface.\n/// @dev Interface of SemaphoreCore contract.\ninterface ISemaphoreCore {\n /// @notice Emitted when a proof is verified correctly and a new nullifier hash is added.\n /// @param nullifierHash: Hash of external and identity nullifiers.\n event NullifierHashAdded(uint256 nullifierHash);\n}\n"
},
"@semaphore-protocol/contracts/interfaces/IVerifier.sol": {
"content": "//SPDX-License-Identifier: MIT\npragma solidity 0.8.4;\n\n/// @title Verifier interface.\n/// @dev Interface of Verifier contract.\ninterface IVerifier {\n function verifyProof(\n uint256[2] memory a,\n uint256[2][2] memory b,\n uint256[2] memory c,\n uint256[4] memory input\n ) external view;\n}\n"
},
"contracts/InterepTest.sol": {
"content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport \"@interep/contracts/IInterep.sol\";\nimport \"@semaphore-protocol/contracts/interfaces/IVerifier.sol\";\nimport \"@semaphore-protocol/contracts/base/SemaphoreCore.sol\";\nimport \"@semaphore-protocol/contracts/base/SemaphoreConstants.sol\";\n\ncontract InterepTest is IInterep, SemaphoreCore {\n mapping(uint256 => Group) public groups;\n\n /// @dev mimics https://github.com/interep-project/contracts/blob/main/contracts/Interep.sol but ignores the verification mechanism\n constructor() {}\n\n /// @dev See {IInterep-updateGroups}.\n function updateGroups(Group[] calldata _groups) external override {\n for (uint8 i = 0; i < _groups.length; i++) {\n uint256 groupId = uint256(\n keccak256(\n abi.encodePacked(_groups[i].provider, _groups[i].name)\n )\n ) % SNARK_SCALAR_FIELD;\n\n _updateGroup(groupId, _groups[i]);\n }\n }\n\n /// @dev See {IInterep-getRoot}.\n function getRoot(uint256 groupId) public view override returns (uint256) {\n return groups[groupId].root;\n }\n\n /// @dev See {IInterep-getDepth}.\n function getDepth(uint256 groupId) public view override returns (uint8) {\n return groups[groupId].depth;\n }\n\n /// @dev Updates an Interep group.\n /// @param groupId: Id of the group.\n /// @param group: Group data.\n function _updateGroup(uint256 groupId, Group calldata group) private {\n groups[groupId] = group;\n\n emit GroupUpdated(\n groupId,\n group.provider,\n group.name,\n group.root,\n group.depth\n );\n }\n\n function verifyProof(\n uint256 groupId,\n bytes32 signal,\n uint256 nullifierHash,\n uint256 externalNullifier,\n uint256[8] calldata proof\n ) external override {}\n}\n"
}
},
"settings": {
"optimizer": {
"enabled": false,
"runs": 200
},
"outputSelection": {
"*": {
"*": [
"abi",
"evm.bytecode",
"evm.deployedBytecode",
"evm.methodIdentifiers",
"metadata",
"devdoc",
"userdoc",
"storageLayout",
"evm.gasEstimates"
],
"": [
"ast"
]
}
},
"metadata": {
"useLiteralContent": true
}
}
}

View File

@ -1,10 +1,13 @@
import * as dotenv from "dotenv";
import { HardhatUserConfig, task } from "hardhat/config";
import { NetworksUserConfig } from "hardhat/types";
import "hardhat-deploy";
import "@nomiclabs/hardhat-ethers";
import "@nomiclabs/hardhat-waffle";
import "hardhat-gas-reporter";
import "solidity-coverage";
import { NetworksUserConfig } from "hardhat/types";
dotenv.config();
const {GOERLI_URL,PRIVATE_KEY} = process.env;

View File

@ -13,15 +13,16 @@
},
"devDependencies": {
"@interep/contracts": "0.6.0",
"@semaphore-protocol/contracts": "2.6.1",
"@nomiclabs/hardhat-ethers": "^2.0.6",
"@nomiclabs/hardhat-ethers": "npm:hardhat-deploy-ethers",
"@nomiclabs/hardhat-etherscan": "^3.1.0",
"@nomiclabs/hardhat-waffle": "^2.0.3",
"@semaphore-protocol/contracts": "2.6.1",
"@types/mocha": "^9.1.1",
"chai": "^4.3.6",
"ethereum-waffle": "^3.4.4",
"ethers": "^5.6.9",
"ethers": "^5.7.2",
"hardhat": "^2.9.9",
"hardhat-deploy": "0.11.20",
"hardhat-gas-reporter": "^1.0.8",
"solidity-coverage": "^0.7.21",
"ts-node": "^10.8.1",
@ -30,4 +31,4 @@
"dependencies": {
"dotenv": "^16.0.1"
}
}
}

View File

@ -1,37 +0,0 @@
// We require the Hardhat Runtime Environment explicitly here. This is optional
// but useful for running the script in a standalone fashion through `node <script>`.
//
// When running the script with `npx hardhat run <script>` you'll find the Hardhat
// Runtime Environment's members available in the global scope.
import { ethers } from "hardhat";
async function main() {
// Hardhat always runs the compile task when running scripts with its command
// line interface.
//
// If this script is run directly using `node` you may want to call compile
// manually to make sure everything is compiled
// await hre.run('compile');
// We get the contract to deploy
const PoseidonHasher = await ethers.getContractFactory("PoseidonHasher");
const poseidonHasher = await PoseidonHasher.deploy();
await poseidonHasher.deployed();
console.log("PoseidonHasher deployed to:", poseidonHasher.address);
const Rln = await ethers.getContractFactory("RLN");
const rln = await Rln.deploy(1000000000000000,20,poseidonHasher.address);
await rln.deployed();
console.log("Rln deployed to:", rln.address);
}
// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});

View File

@ -1,7 +1,7 @@
import { assert } from "chai";
import { ethers } from "hardhat";
describe("Rln", function () {
describe.skip("Rln", function () {
it("Deploying", async function () {
const PoseidonHasher = await ethers.getContractFactory("PoseidonHasher");
const poseidonHasher = await PoseidonHasher.deploy();

View File

@ -1,21 +1,17 @@
import { expect } from "chai";
import { ethers } from "hardhat";
import { ethers, deployments } from "hardhat";
describe("Rln", function () {
it("Deploying", async function () {
const PoseidonHasher = await ethers.getContractFactory("PoseidonHasher");
const poseidonHasher = await PoseidonHasher.deploy();
await poseidonHasher.deployed();
console.log("PoseidonHasher deployed to:", poseidonHasher.address);
describe("PoseidonHash", () => {
beforeEach(async () => {
await deployments.fixture(["PoseidonHasher"]);
});
it("should hash correctly", async function () {
const poseidonHasher = await ethers.getContract("PoseidonHasher");
// We test hashing for a random number
const hash = await poseidonHasher.hash("19014214495641488759237505126948346942972912379615652741039992445865937985820");
console.log("Hash:", hash);
//Expect 0x0c3ac305f6a4fe9bfeb3eba978bc876e2a99208b8b56c80160cfb54ba8f02368
expect(hash._hex).to.eql("0x0c3ac305f6a4fe9bfeb3eba978bc876e2a99208b8b56c80160cfb54ba8f02368");
});
});

View File

@ -1,74 +1,15 @@
import {expect} from "chai";
import {Contract} from "ethers";
import {ethers} from "hardhat";
import {ethers, deployments} from "hardhat";
import {sToBytes32, createGroupId} from '../common';
const sToBytes32 = (str: string): string => {
return ethers.utils.formatBytes32String(str);
}
const SNARK_SCALAR_FIELD = BigInt(
"21888242871839275222246405745257275088548364400416034343698204186575808495617"
)
const createGroupId = (provider: string, name: string): bigint => {
const providerBytes = sToBytes32(provider);
const nameBytes = sToBytes32(name);
return BigInt(ethers.utils.solidityKeccak256(["bytes32", "bytes32"], [providerBytes, nameBytes])) % SNARK_SCALAR_FIELD
}
const providers = ['github', 'twitter', 'reddit'];
const tiers = ['bronze', 'silver', 'gold'];
const getGroups = () => {
return providers.flatMap(provider => tiers.map(tier => {
return {
provider: sToBytes32(provider),
name: sToBytes32(tier),
root: 1,
depth: 10,
}
}));
}
const scaffoldInterep = async () => {
// Deploy interep
const InterepTest = await ethers.getContractFactory("InterepTest");
const interepTest = await InterepTest.deploy();
await interepTest.deployed();
// add all combinations of providers and tiers into an array
const groups = getGroups();
// insert groups into interep membership contract
const groupInsertionTx = await interepTest.updateGroups(groups);
await groupInsertionTx.wait();
return interepTest
}
const scaffold = async () => {
const interepTest = await scaffoldInterep();
const interepAddress = interepTest.address;
// create valid group storage contract for rln
const ValidGroupStorage = await ethers.getContractFactory("ValidGroupStorage");
const groups = getGroups();
const filteredGroups = groups
.filter(group => group.name !== sToBytes32('bronze'));
const validGroupStorage = await ValidGroupStorage.deploy(interepAddress, filteredGroups);
await validGroupStorage.deployed();
expect(validGroupStorage.address).to.not.equal(0);
return validGroupStorage
}
describe("Valid Group Storage", () => {
let validGroupStorage: Contract;
beforeEach(async () => {
validGroupStorage = await scaffold();
await deployments.fixture(['InterepTest', 'ValidGroupStorage']);
})
it('should not deploy if an invalid group is passed in constructor', async () => {
const interepTest = await scaffoldInterep();
const interepTest = await ethers.getContract('InterepTest');
const interepAddress = interepTest.address;
const ValidGroupStorage = await ethers.getContractFactory("ValidGroupStorage");
@ -79,11 +20,13 @@ describe("Valid Group Storage", () => {
})
it("should return true for valid group", async () => {
const validGroupStorage = await ethers.getContract('ValidGroupStorage');
const valid = await validGroupStorage.isValidGroup(createGroupId('github', 'silver'));
expect(valid).to.be.true;
});
it("should return false for invalid group", async () => {
const validGroupStorage = await ethers.getContract('ValidGroupStorage');
const valid = await validGroupStorage.isValidGroup(createGroupId('github', 'bronze'));
expect(valid).to.be.false;
});

View File

@ -7,6 +7,13 @@
"outDir": "dist",
"declaration": true
},
"include": ["./scripts", "./test", "./typechain"],
"files": ["./hardhat.config.ts"]
}
"include": [
"./scripts",
"./test",
"./typechain",
"deploy"
],
"files": [
"./hardhat.config.ts"
]
}

116
yarn.lock
View File

@ -730,10 +730,10 @@
"@nomicfoundation/solidity-analyzer-win32-ia32-msvc" "0.1.0"
"@nomicfoundation/solidity-analyzer-win32-x64-msvc" "0.1.0"
"@nomiclabs/hardhat-ethers@^2.0.6":
version "2.2.1"
resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-ethers/-/hardhat-ethers-2.2.1.tgz#8057b43566a0e41abeb8142064a3c0d3f23dca86"
integrity sha512-RHWYwnxryWR8hzRmU4Jm/q4gzvXpetUOJ4OPlwH2YARcDB+j79+yAYCwO0lN1SUOb4++oOTJEe6AWLEc42LIvg==
"@nomiclabs/hardhat-ethers@npm:hardhat-deploy-ethers":
version "0.3.0-beta.13"
resolved "https://registry.yarnpkg.com/hardhat-deploy-ethers/-/hardhat-deploy-ethers-0.3.0-beta.13.tgz#b96086ff768ddf69928984d5eb0a8d78cfca9366"
integrity sha512-PdWVcKB9coqWV1L7JTpfXRCI91Cgwsm7KLmBcwZ8f0COSm1xtABHZTyz3fvF6p42cTnz1VM0QnfDvMFlIRkSNw==
"@nomiclabs/hardhat-etherscan@^3.1.0":
version "3.1.2"
@ -1116,7 +1116,7 @@
resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.1.tgz#dfd20e2dc35f027cdd6c1908e80a5ddc7499670e"
integrity sha512-ri0UmynRRvZiiUJdiz38MmIblKK+oH30MztdBVR95dv/Ubw6neWSb8u1XpRb72L4qsZOhz+L+z9JD40SJmfWow==
"@types/qs@^6.2.31":
"@types/qs@^6.2.31", "@types/qs@^6.9.7":
version "6.9.7"
resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb"
integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==
@ -1575,6 +1575,13 @@ aws4@^1.8.0:
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59"
integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==
axios@^0.21.1:
version "0.21.4"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575"
integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==
dependencies:
follow-redirects "^1.14.0"
babel-code-frame@^6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b"
@ -2600,7 +2607,7 @@ chokidar@3.3.0:
optionalDependencies:
fsevents "~2.1.1"
chokidar@3.5.3, chokidar@^3.4.0:
chokidar@3.5.3, chokidar@^3.4.0, chokidar@^3.5.2:
version "3.5.3"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==
@ -3007,7 +3014,7 @@ debug@3.2.6:
dependencies:
ms "^2.1.1"
debug@4, debug@4.3.4, debug@^4.1.1, debug@^4.3.1, debug@^4.3.3:
debug@4, debug@4.3.4, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3:
version "4.3.4"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
@ -3266,6 +3273,11 @@ emoji-regex@^8.0.0:
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
encode-utf8@^1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/encode-utf8/-/encode-utf8-1.0.3.tgz#f30fdd31da07fb596f281beb2f6b027851994cda"
integrity sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==
encodeurl@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
@ -3296,7 +3308,7 @@ end-of-stream@^1.1.0:
dependencies:
once "^1.4.0"
enquirer@^2.3.0:
enquirer@^2.3.0, enquirer@^2.3.6:
version "2.3.6"
resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d"
integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==
@ -3875,7 +3887,7 @@ ethers@^4.0.32, ethers@^4.0.40:
uuid "2.0.1"
xmlhttprequest "1.8.0"
ethers@^5.0.1, ethers@^5.0.2, ethers@^5.5.2, ethers@^5.6.9:
ethers@^5.0.1, ethers@^5.0.2, ethers@^5.5.2, ethers@^5.5.3, ethers@^5.7.2:
version "5.7.2"
resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.7.2.tgz#3a7deeabbb8c030d4126b24f84e525466145872e"
integrity sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==
@ -4198,7 +4210,14 @@ flow-stoplight@^1.0.0:
resolved "https://registry.yarnpkg.com/flow-stoplight/-/flow-stoplight-1.0.0.tgz#4a292c5bcff8b39fa6cc0cb1a853d86f27eeff7b"
integrity sha512-rDjbZUKpN8OYhB0IE/vY/I8UWO/602IIJEU/76Tv4LvYnwHCk0BCsvz4eRr9n+FQcri7L5cyaXOo0+/Kh4HisA==
follow-redirects@^1.12.1:
fmix@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/fmix/-/fmix-0.1.0.tgz#c7bbf124dec42c9d191cfb947d0a9778dd986c0c"
integrity sha512-Y6hyofImk9JdzU8k5INtTXX1cu8LDlePWDFU5sftm9H+zKCr5SGrVjdhkvsim646cw5zD0nADj8oHyXMZmCZ9w==
dependencies:
imul "^1.0.0"
follow-redirects@^1.12.1, follow-redirects@^1.14.0:
version "1.15.2"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13"
integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==
@ -4238,6 +4257,15 @@ form-data@^3.0.0:
combined-stream "^1.0.8"
mime-types "^2.1.12"
form-data@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
dependencies:
asynckit "^0.4.0"
combined-stream "^1.0.8"
mime-types "^2.1.12"
form-data@~2.3.2:
version "2.3.3"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"
@ -4285,6 +4313,15 @@ fs-extra@^0.30.0:
path-is-absolute "^1.0.0"
rimraf "^2.2.8"
fs-extra@^10.0.0:
version "10.1.0"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf"
integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==
dependencies:
graceful-fs "^4.2.0"
jsonfile "^6.0.1"
universalify "^2.0.0"
fs-extra@^4.0.2, fs-extra@^4.0.3:
version "4.0.3"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94"
@ -4645,6 +4682,25 @@ har-validator@~5.1.3:
ajv "^6.12.3"
har-schema "^2.0.0"
hardhat-deploy@0.11.20:
version "0.11.20"
resolved "https://registry.yarnpkg.com/hardhat-deploy/-/hardhat-deploy-0.11.20.tgz#d95499a0d29b75f1f1d3838c9a3eb6d2d0d20f57"
integrity sha512-6G2aFLW0mfZxY0ljDf8rxzEJkVh57gr3Eia5H2DeBIQNJh0dhBV2Y5LDq4E4J9K4Crq0DjpxGNTshEtAB9eWxA==
dependencies:
"@types/qs" "^6.9.7"
axios "^0.21.1"
chalk "^4.1.2"
chokidar "^3.5.2"
debug "^4.3.2"
enquirer "^2.3.6"
ethers "^5.5.3"
form-data "^4.0.0"
fs-extra "^10.0.0"
match-all "^1.2.6"
murmur-128 "^0.2.1"
qs "^6.9.4"
zksync-web3 "^0.8.1"
hardhat-gas-reporter@^1.0.8:
version "1.0.9"
resolved "https://registry.yarnpkg.com/hardhat-gas-reporter/-/hardhat-gas-reporter-1.0.9.tgz#9a2afb354bc3b6346aab55b1c02ca556d0e16450"
@ -4960,6 +5016,11 @@ immutable@^4.0.0-rc.12:
resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.1.0.tgz#f795787f0db780183307b9eb2091fcac1f6fafef"
integrity sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==
imul@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/imul/-/imul-1.0.1.tgz#9d5867161e8b3de96c2c38d5dc7cb102f35e2ac9"
integrity sha512-WFAgfwPLAjU66EKt6vRdTlKj4nAgIDQzh29JonLa4Bqtl6D8JrIMvWjCnx7xEjVNmP3U0fM5o8ZObk7d0f62bA==
indent-string@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251"
@ -5501,6 +5562,15 @@ jsonfile@^4.0.0:
optionalDependencies:
graceful-fs "^4.1.6"
jsonfile@^6.0.1:
version "6.1.0"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae"
integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==
dependencies:
universalify "^2.0.0"
optionalDependencies:
graceful-fs "^4.1.6"
jsonify@^0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.1.tgz#2aa3111dae3d34a0f151c63f3a45d995d9420978"
@ -5927,6 +5997,11 @@ markdown-table@^1.1.3:
resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-1.1.3.tgz#9fcb69bcfdb8717bfd0398c6ec2d93036ef8de60"
integrity sha512-1RUZVgQlpJSPWYbFSpmudq5nHY1doEIv89gBtF0s4gW1GF2XorxcA/70M5vq7rLv0a6mhOUccRsqkwhwLCIQ2Q==
match-all@^1.2.6:
version "1.2.6"
resolved "https://registry.yarnpkg.com/match-all/-/match-all-1.2.6.tgz#66d276ad6b49655551e63d3a6ee53e8be0566f8d"
integrity sha512-0EESkXiTkWzrQQntBu2uzKvLu6vVkUGz40nGPbSZuegcfE5UuSzNjLaIu76zJWuaT/2I3Z/8M06OlUOZLGwLlQ==
mcl-wasm@^0.7.1:
version "0.7.9"
resolved "https://registry.yarnpkg.com/mcl-wasm/-/mcl-wasm-0.7.9.tgz#c1588ce90042a8700c3b60e40efb339fc07ab87f"
@ -6314,6 +6389,15 @@ multihashes@^0.4.15, multihashes@~0.4.15:
multibase "^0.7.0"
varint "^5.0.0"
murmur-128@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/murmur-128/-/murmur-128-0.2.1.tgz#a9f6568781d2350ecb1bf80c14968cadbeaa4b4d"
integrity sha512-WseEgiRkI6aMFBbj8Cg9yBj/y+OdipwVC7zUo3W2W1JAJITwouUOtpqsmGSg67EQmwwSyod7hsVsWY5LsrfQVg==
dependencies:
encode-utf8 "^1.0.2"
fmix "^0.1.0"
imul "^1.0.0"
nano-json-stream-parser@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz#0cc8f6d0e2b622b479c40d499c46d64b755c6f5f"
@ -7027,7 +7111,7 @@ punycode@^2.1.0, punycode@^2.1.1:
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
qs@6.11.0, qs@^6.4.0, qs@^6.7.0:
qs@6.11.0, qs@^6.4.0, qs@^6.7.0, qs@^6.9.4:
version "6.11.0"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a"
integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==
@ -8569,6 +8653,11 @@ universalify@^0.1.0:
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
universalify@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717"
integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==
unorm@^1.3.3:
version "1.6.0"
resolved "https://registry.yarnpkg.com/unorm/-/unorm-1.6.0.tgz#029b289661fba714f1a9af439eb51d9b16c205af"
@ -9584,3 +9673,8 @@ yocto-queue@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
zksync-web3@^0.8.1:
version "0.8.1"
resolved "https://registry.yarnpkg.com/zksync-web3/-/zksync-web3-0.8.1.tgz#db289d8f6caf61f4d5ddc471fa3448d93208dc14"
integrity sha512-1A4aHPQ3MyuGjpv5X/8pVEN+MdZqMjfVmiweQSRjOlklXYu65wT9BGEOtCmMs5d3gIvLp4ssfTeuR5OCKOD2kw==