2022-11-25 06:37:06 +00:00
|
|
|
import { expect, assert } from "chai";
|
2022-11-28 05:03:16 +00:00
|
|
|
import { BigNumber } from "ethers";
|
2022-11-25 06:01:06 +00:00
|
|
|
import { ethers, deployments } from "hardhat";
|
2022-11-28 12:53:59 +00:00
|
|
|
import {
|
|
|
|
createGroupId,
|
|
|
|
createInterepIdentity,
|
|
|
|
createInterepProof,
|
2022-11-28 13:19:15 +00:00
|
|
|
getGroups,
|
|
|
|
getValidGroups,
|
|
|
|
merkleTreeDepth,
|
2022-11-28 12:53:59 +00:00
|
|
|
sToBytes32,
|
|
|
|
} from "../common";
|
2022-11-25 06:01:06 +00:00
|
|
|
|
|
|
|
describe("RLN", () => {
|
|
|
|
beforeEach(async () => {
|
|
|
|
await deployments.fixture(["RLN"]);
|
2022-11-25 06:08:38 +00:00
|
|
|
});
|
2022-11-25 06:01:06 +00:00
|
|
|
|
|
|
|
it("should register new memberships", async () => {
|
|
|
|
const rln = await ethers.getContract("RLN", ethers.provider.getSigner(0));
|
2022-11-25 06:08:38 +00:00
|
|
|
|
2022-11-25 06:01:06 +00:00
|
|
|
const price = await rln.MEMBERSHIP_DEPOSIT();
|
|
|
|
|
|
|
|
// A valid pair of (id_secret, id_commitment) generated in rust
|
2022-11-25 06:08:38 +00:00
|
|
|
const idCommitment =
|
|
|
|
"0x0c3ac305f6a4fe9bfeb3eba978bc876e2a99208b8b56c80160cfb54ba8f02368";
|
2022-11-25 06:01:06 +00:00
|
|
|
|
2022-11-25 06:08:38 +00:00
|
|
|
const registerTx = await rln["register(uint256)"](idCommitment, {
|
|
|
|
value: price,
|
|
|
|
});
|
2022-11-25 06:01:06 +00:00
|
|
|
const txRegisterReceipt = await registerTx.wait();
|
|
|
|
|
2022-12-02 07:19:11 +00:00
|
|
|
const pubkey = txRegisterReceipt.events[0].args.idCommitment;
|
2022-11-25 06:01:06 +00:00
|
|
|
|
|
|
|
// We ensure the registered id_commitment is the one we passed
|
2022-11-25 06:08:38 +00:00
|
|
|
expect(
|
|
|
|
pubkey.toHexString() === idCommitment,
|
|
|
|
"registered commitment doesn't match passed commitment"
|
|
|
|
);
|
2022-11-25 06:01:06 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
it("should withdraw membership", async () => {
|
|
|
|
const rln = await ethers.getContract("RLN", ethers.provider.getSigner(0));
|
2022-11-25 06:08:38 +00:00
|
|
|
|
2022-11-25 06:01:06 +00:00
|
|
|
const price = await rln.MEMBERSHIP_DEPOSIT();
|
|
|
|
|
|
|
|
// A valid pair of (id_secret, id_commitment) generated in rust
|
2022-11-25 06:08:38 +00:00
|
|
|
const idSecret =
|
|
|
|
"0x2a09a9fd93c590c26b91effbb2499f07e8f7aa12e2b4940a3aed2411cb65e11c";
|
|
|
|
const idCommitment =
|
|
|
|
"0x0c3ac305f6a4fe9bfeb3eba978bc876e2a99208b8b56c80160cfb54ba8f02368";
|
2022-11-25 06:01:06 +00:00
|
|
|
|
2022-11-25 06:08:38 +00:00
|
|
|
const registerTx = await rln["register(uint256)"](idCommitment, {
|
|
|
|
value: price,
|
|
|
|
});
|
2022-12-02 07:19:11 +00:00
|
|
|
await registerTx.wait();
|
2022-11-25 06:01:06 +00:00
|
|
|
|
|
|
|
// We withdraw our id_commitment
|
2022-11-25 06:08:38 +00:00
|
|
|
const receiverAddress = "0x000000000000000000000000000000000000dead";
|
2022-12-02 07:19:11 +00:00
|
|
|
const withdrawTx = await rln["withdraw(uint256,address)"](
|
|
|
|
idSecret,
|
|
|
|
receiverAddress
|
|
|
|
);
|
2022-11-25 06:08:38 +00:00
|
|
|
|
|
|
|
const txWithdrawReceipt = await withdrawTx.wait();
|
|
|
|
|
2022-12-02 07:19:11 +00:00
|
|
|
const withdrawalPk = txWithdrawReceipt.events[0].args.idCommitment;
|
2022-11-25 06:08:38 +00:00
|
|
|
|
|
|
|
// We ensure the registered id_commitment is the one we passed and that the index is the same
|
|
|
|
expect(
|
|
|
|
withdrawalPk.toHexString() === idCommitment,
|
|
|
|
"withdraw commitment doesn't match registered commitment"
|
|
|
|
);
|
2022-12-02 07:19:11 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
it("should not withdraw stake without address", async () => {
|
|
|
|
const rln = await ethers.getContract("RLN", ethers.provider.getSigner(0));
|
|
|
|
|
|
|
|
const price = await rln.MEMBERSHIP_DEPOSIT();
|
|
|
|
|
|
|
|
// A valid pair of (id_secret, id_commitment) generated in rust
|
|
|
|
const idSecret =
|
|
|
|
"0x2a09a9fd93c590c26b91effbb2499f07e8f7aa12e2b4940a3aed2411cb65e11c";
|
|
|
|
const idCommitment =
|
|
|
|
"0x0c3ac305f6a4fe9bfeb3eba978bc876e2a99208b8b56c80160cfb54ba8f02368";
|
|
|
|
|
|
|
|
const registerTx = await rln["register(uint256)"](idCommitment, {
|
|
|
|
value: price,
|
|
|
|
});
|
|
|
|
await registerTx.wait();
|
|
|
|
|
|
|
|
// We withdraw our id_commitment
|
|
|
|
const withdrawTx = rln["withdraw(uint256)"](idSecret);
|
|
|
|
|
|
|
|
await expect(withdrawTx).to.be.revertedWith("RLN, _withdraw: staked");
|
|
|
|
});
|
|
|
|
|
|
|
|
it("should not withdraw withdraw stake if no stake exists", async () => {
|
|
|
|
const rln = await ethers.getContract("RLN", ethers.provider.getSigner(0));
|
|
|
|
|
|
|
|
const validGroupId = createGroupId("github", "silver");
|
|
|
|
const dummySignal = sToBytes32("foo");
|
|
|
|
const dummyNullifierHash = BigNumber.from(0);
|
|
|
|
const dummyExternalNullifier = BigNumber.from(0);
|
|
|
|
const dummyProof = Array(8).fill(BigNumber.from(0));
|
|
|
|
|
|
|
|
const idCommitment = BigNumber.from(
|
|
|
|
"0x0c3ac305f6a4fe9bfeb3eba978bc876e2a99208b8b56c80160cfb54ba8f02368"
|
|
|
|
);
|
|
|
|
const secret =
|
|
|
|
"0x2a09a9fd93c590c26b91effbb2499f07e8f7aa12e2b4940a3aed2411cb65e11c";
|
|
|
|
|
|
|
|
const registerTx = await rln[
|
|
|
|
"register(uint256,bytes32,uint256,uint256,uint256[8],uint256)"
|
|
|
|
](
|
|
|
|
validGroupId,
|
|
|
|
dummySignal,
|
|
|
|
dummyNullifierHash,
|
|
|
|
dummyExternalNullifier,
|
|
|
|
dummyProof,
|
|
|
|
idCommitment
|
|
|
|
);
|
|
|
|
|
|
|
|
await registerTx.wait();
|
|
|
|
|
|
|
|
const address = "0x000000000000000000000000000000000000dead";
|
|
|
|
const withdrawTx = rln["withdraw(uint256,address)"](secret, address);
|
|
|
|
|
|
|
|
await expect(withdrawTx).to.be.revertedWith(
|
|
|
|
"RLN, _withdraw: member has no stake"
|
2022-11-25 06:08:38 +00:00
|
|
|
);
|
|
|
|
});
|
2022-11-25 06:37:06 +00:00
|
|
|
|
2022-12-02 07:19:11 +00:00
|
|
|
it("should not allow multiple registrations with same pubkey", async () => {
|
2022-11-25 06:37:06 +00:00
|
|
|
const rln = await ethers.getContract("RLN", ethers.provider.getSigner(0));
|
|
|
|
|
|
|
|
const price = await rln.MEMBERSHIP_DEPOSIT();
|
|
|
|
|
|
|
|
// A valid pair of (id_secret, id_commitment) generated in rust
|
|
|
|
const idCommitment =
|
|
|
|
"0x0c3ac305f6a4fe9bfeb3eba978bc876e2a99208b8b56c80160cfb54ba8f02368";
|
|
|
|
|
|
|
|
const registerTx = await rln["register(uint256)"](idCommitment, {
|
|
|
|
value: price,
|
|
|
|
});
|
2022-12-02 07:19:11 +00:00
|
|
|
await registerTx.wait();
|
2022-11-25 06:37:06 +00:00
|
|
|
|
|
|
|
// Send the same tx again
|
2022-12-02 07:19:11 +00:00
|
|
|
const registerTx2 = rln["register(uint256)"](idCommitment, {
|
2022-11-25 06:37:06 +00:00
|
|
|
value: price,
|
|
|
|
});
|
2022-12-02 07:19:11 +00:00
|
|
|
|
|
|
|
await expect(registerTx2).to.be.revertedWith(
|
|
|
|
"RLN, _register: member already registered"
|
|
|
|
);
|
2022-11-25 06:40:37 +00:00
|
|
|
});
|
2022-11-25 06:37:06 +00:00
|
|
|
|
2022-11-28 05:03:16 +00:00
|
|
|
it("[interep] should register new memberships", async () => {
|
|
|
|
const rln = await ethers.getContract("RLN", ethers.provider.getSigner(0));
|
|
|
|
|
|
|
|
const validGroupId = createGroupId("github", "silver");
|
2022-11-28 09:31:35 +00:00
|
|
|
const dummySignal = sToBytes32("foo");
|
2022-11-28 05:03:16 +00:00
|
|
|
const dummyNullifierHash = BigNumber.from(0);
|
|
|
|
const dummyExternalNullifier = BigNumber.from(0);
|
|
|
|
const dummyProof = Array(8).fill(BigNumber.from(0));
|
|
|
|
const dummyPubkey = BigNumber.from(0);
|
|
|
|
|
|
|
|
const registerTx = await rln[
|
|
|
|
"register(uint256,bytes32,uint256,uint256,uint256[8],uint256)"
|
|
|
|
](
|
|
|
|
validGroupId,
|
|
|
|
dummySignal,
|
|
|
|
dummyNullifierHash,
|
|
|
|
dummyExternalNullifier,
|
|
|
|
dummyProof,
|
|
|
|
dummyPubkey
|
|
|
|
);
|
|
|
|
const txRegisterReceipt = await registerTx.wait();
|
|
|
|
|
|
|
|
const iface = new ethers.utils.Interface([
|
|
|
|
"event ProofVerified(uint256 indexed groupId, bytes32 signal)",
|
|
|
|
]);
|
|
|
|
const event = iface.parseLog(txRegisterReceipt.events[0]);
|
|
|
|
expect(event.args.groupId.toHexString()).to.equal(
|
|
|
|
BigNumber.from(validGroupId).toHexString()
|
|
|
|
);
|
|
|
|
expect(event.args.signal).to.equal(dummySignal);
|
|
|
|
|
2022-12-02 07:19:11 +00:00
|
|
|
const pubkey = txRegisterReceipt.events[1].args.idCommitment;
|
2022-11-28 05:03:16 +00:00
|
|
|
expect(pubkey.toHexString() === dummyPubkey.toHexString());
|
2022-11-25 06:37:06 +00:00
|
|
|
});
|
|
|
|
|
2022-11-28 13:19:15 +00:00
|
|
|
it("[interep] should generate proof for registration", async () => {
|
2022-11-28 12:53:59 +00:00
|
|
|
const signer = ethers.provider.getSigner(0);
|
|
|
|
const identity = await createInterepIdentity(signer, "github");
|
|
|
|
|
|
|
|
// create a proof to test
|
|
|
|
const proof = await createInterepProof({
|
|
|
|
identity,
|
|
|
|
members: [identity.getCommitment()],
|
|
|
|
groupProvider: "github",
|
|
|
|
groupTier: "silver",
|
|
|
|
signal: "foo",
|
|
|
|
externalNullifier: 1,
|
|
|
|
snarkArtifacts: {
|
|
|
|
wasmFilePath: "./test/snarkArtifacts/semaphore.wasm",
|
|
|
|
zkeyFilePath: "./test/snarkArtifacts/semaphore.zkey",
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
expect(proof.groupId).to.eql(
|
|
|
|
"19580063316323634959827976785370507245708993886389832860880129572471638471998"
|
|
|
|
);
|
|
|
|
expect(proof.publicSignals.merkleRoot).to.eql(
|
|
|
|
"10738127364751233254031334835017982823925916365031589705155005906674724477907"
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2022-11-28 13:19:15 +00:00
|
|
|
it("[interep] should register with valid proof", async () => {
|
|
|
|
// need to create new fixtures for this test
|
|
|
|
const { PoseidonHasher } = await deployments.fixture("PoseidonHasher");
|
|
|
|
const verifier20Factory = await ethers.getContractFactory("Verifier20");
|
|
|
|
const verifier20 = await verifier20Factory.deploy();
|
|
|
|
await verifier20.deployed();
|
|
|
|
const interepFactory = await ethers.getContractFactory(
|
|
|
|
"Interep",
|
|
|
|
ethers.provider.getSigner(0)
|
|
|
|
);
|
|
|
|
const interep = await interepFactory.deploy([
|
|
|
|
{
|
|
|
|
contractAddress: verifier20.address,
|
|
|
|
merkleTreeDepth: merkleTreeDepth,
|
|
|
|
},
|
|
|
|
]);
|
|
|
|
await interep.deployed();
|
|
|
|
const groupTx = await interep.updateGroups(getGroups());
|
|
|
|
await groupTx.wait();
|
|
|
|
|
|
|
|
const validGroupStorageFactory = await ethers.getContractFactory(
|
|
|
|
"ValidGroupStorage"
|
|
|
|
);
|
|
|
|
const validGroupStorage = await validGroupStorageFactory.deploy(
|
|
|
|
interep.address,
|
|
|
|
getValidGroups()
|
|
|
|
);
|
|
|
|
await validGroupStorage.deployed();
|
|
|
|
|
|
|
|
const rlnFactory = await ethers.getContractFactory(
|
|
|
|
"RLN",
|
|
|
|
ethers.provider.getSigner(0)
|
|
|
|
);
|
|
|
|
const rln = await rlnFactory.deploy(
|
|
|
|
1000000000000000,
|
|
|
|
20,
|
|
|
|
PoseidonHasher.address,
|
|
|
|
validGroupStorage.address
|
|
|
|
);
|
|
|
|
|
|
|
|
await rln.deployed();
|
|
|
|
|
|
|
|
const identity = await createInterepIdentity(
|
|
|
|
ethers.provider.getSigner(0),
|
|
|
|
"github"
|
|
|
|
);
|
|
|
|
|
|
|
|
// create a proof to test
|
|
|
|
const proof = await createInterepProof({
|
|
|
|
identity,
|
|
|
|
members: [identity.getCommitment()],
|
|
|
|
groupProvider: "github",
|
|
|
|
groupTier: "silver",
|
|
|
|
signal: sToBytes32("foo"),
|
|
|
|
externalNullifier: 1,
|
|
|
|
snarkArtifacts: {
|
|
|
|
wasmFilePath: "./test/snarkArtifacts/semaphore.wasm",
|
|
|
|
zkeyFilePath: "./test/snarkArtifacts/semaphore.zkey",
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
// update root of group
|
|
|
|
const groupUpdateTx = await interep.updateGroups([
|
|
|
|
{
|
|
|
|
provider: sToBytes32("github"),
|
|
|
|
name: sToBytes32("silver"),
|
|
|
|
root: proof.publicSignals.merkleRoot,
|
|
|
|
depth: 20,
|
|
|
|
},
|
|
|
|
]);
|
|
|
|
|
|
|
|
await groupUpdateTx.wait();
|
|
|
|
|
|
|
|
const registerTx = await rln[
|
|
|
|
"register(uint256,bytes32,uint256,uint256,uint256[8],uint256)"
|
|
|
|
](
|
|
|
|
proof.groupId,
|
|
|
|
proof.signal,
|
|
|
|
proof.publicSignals.nullifierHash,
|
|
|
|
proof.publicSignals.externalNullifier,
|
|
|
|
proof.solidityProof,
|
|
|
|
identity.getCommitment()
|
|
|
|
);
|
|
|
|
|
|
|
|
const txRegisterReceipt = await registerTx.wait();
|
|
|
|
|
2022-12-02 07:19:11 +00:00
|
|
|
expect(txRegisterReceipt.events[1].args.idCommitment.toHexString()).to.eql(
|
2022-11-28 13:19:15 +00:00
|
|
|
BigNumber.from(identity.getCommitment()).toHexString()
|
|
|
|
);
|
2022-11-25 06:37:06 +00:00
|
|
|
});
|
2022-12-02 07:19:11 +00:00
|
|
|
|
|
|
|
it("[interep] should revert with invalid proof", async () => {
|
|
|
|
// need to create new fixtures for this test
|
|
|
|
const { PoseidonHasher } = await deployments.fixture("PoseidonHasher");
|
|
|
|
const verifier20Factory = await ethers.getContractFactory("Verifier20");
|
|
|
|
const verifier20 = await verifier20Factory.deploy();
|
|
|
|
await verifier20.deployed();
|
|
|
|
const interepFactory = await ethers.getContractFactory(
|
|
|
|
"Interep",
|
|
|
|
ethers.provider.getSigner(0)
|
|
|
|
);
|
|
|
|
const interep = await interepFactory.deploy([
|
|
|
|
{
|
|
|
|
contractAddress: verifier20.address,
|
|
|
|
merkleTreeDepth: merkleTreeDepth,
|
|
|
|
},
|
|
|
|
]);
|
|
|
|
await interep.deployed();
|
|
|
|
const groupTx = await interep.updateGroups(getGroups());
|
|
|
|
await groupTx.wait();
|
|
|
|
|
|
|
|
const validGroupStorageFactory = await ethers.getContractFactory(
|
|
|
|
"ValidGroupStorage"
|
|
|
|
);
|
|
|
|
const validGroupStorage = await validGroupStorageFactory.deploy(
|
|
|
|
interep.address,
|
|
|
|
getValidGroups()
|
|
|
|
);
|
|
|
|
await validGroupStorage.deployed();
|
|
|
|
|
|
|
|
const rlnFactory = await ethers.getContractFactory(
|
|
|
|
"RLN",
|
|
|
|
ethers.provider.getSigner(0)
|
|
|
|
);
|
|
|
|
const rln = await rlnFactory.deploy(
|
|
|
|
1000000000000000,
|
|
|
|
20,
|
|
|
|
PoseidonHasher.address,
|
|
|
|
validGroupStorage.address
|
|
|
|
);
|
|
|
|
|
|
|
|
await rln.deployed();
|
|
|
|
|
|
|
|
const identity = await createInterepIdentity(
|
|
|
|
ethers.provider.getSigner(0),
|
|
|
|
"github"
|
|
|
|
);
|
|
|
|
|
|
|
|
// create a proof to test
|
|
|
|
const proof = await createInterepProof({
|
|
|
|
identity,
|
|
|
|
members: [identity.getCommitment()],
|
|
|
|
groupProvider: "github",
|
|
|
|
groupTier: "silver",
|
|
|
|
signal: sToBytes32("foo"),
|
|
|
|
externalNullifier: 1,
|
|
|
|
snarkArtifacts: {
|
|
|
|
wasmFilePath: "./test/snarkArtifacts/semaphore.wasm",
|
|
|
|
zkeyFilePath: "./test/snarkArtifacts/semaphore.zkey",
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
// do not update root of group
|
|
|
|
|
|
|
|
const registerTx = rln[
|
|
|
|
"register(uint256,bytes32,uint256,uint256,uint256[8],uint256)"
|
|
|
|
](
|
|
|
|
proof.groupId,
|
|
|
|
proof.signal,
|
|
|
|
proof.publicSignals.nullifierHash,
|
|
|
|
proof.publicSignals.externalNullifier,
|
|
|
|
proof.solidityProof,
|
|
|
|
identity.getCommitment()
|
|
|
|
);
|
|
|
|
|
|
|
|
await expect(registerTx).to.be.revertedWith("InvalidProof()");
|
|
|
|
});
|
|
|
|
|
|
|
|
it("[interep] should withdraw a registration", async () => {
|
|
|
|
const rln = await ethers.getContract("RLN", ethers.provider.getSigner(0));
|
|
|
|
|
|
|
|
const validGroupId = createGroupId("github", "silver");
|
|
|
|
const dummySignal = sToBytes32("foo");
|
|
|
|
const dummyNullifierHash = BigNumber.from(0);
|
|
|
|
const dummyExternalNullifier = BigNumber.from(0);
|
|
|
|
const dummyProof = Array(8).fill(BigNumber.from(0));
|
|
|
|
|
|
|
|
const idCommitment = BigNumber.from(
|
|
|
|
"0x0c3ac305f6a4fe9bfeb3eba978bc876e2a99208b8b56c80160cfb54ba8f02368"
|
|
|
|
);
|
|
|
|
const secret =
|
|
|
|
"0x2a09a9fd93c590c26b91effbb2499f07e8f7aa12e2b4940a3aed2411cb65e11c";
|
|
|
|
|
|
|
|
const registerTx = await rln[
|
|
|
|
"register(uint256,bytes32,uint256,uint256,uint256[8],uint256)"
|
|
|
|
](
|
|
|
|
validGroupId,
|
|
|
|
dummySignal,
|
|
|
|
dummyNullifierHash,
|
|
|
|
dummyExternalNullifier,
|
|
|
|
dummyProof,
|
|
|
|
idCommitment
|
|
|
|
);
|
|
|
|
|
|
|
|
await registerTx.wait();
|
|
|
|
|
|
|
|
const withdrawTx = await rln["withdraw(uint256)"](secret);
|
|
|
|
|
|
|
|
const txWithdrawReceipt = await withdrawTx.wait();
|
|
|
|
|
|
|
|
expect(txWithdrawReceipt.events[0].args.idCommitment).to.eql(idCommitment);
|
|
|
|
});
|
2022-11-25 06:08:38 +00:00
|
|
|
});
|