Kurate/packages/contracts/test/GlobalAnonymousFeed.ts
2023-03-29 14:01:34 +02:00

491 lines
20 KiB
TypeScript

import {ethers} from "hardhat"
import {GlobalAnonymousFeed, GlobalAnonymousFeed__factory} from "../build/typechain"
import type {Signer} from "ethers/lib/ethers"
import {BuildOrderedTree, Circuit, CircuitConfig, Prover} from '@unirep/circuits'
import {SnarkProof, SnarkPublicSignals, Strategy, stringifyBigInts, ZkIdentity} from '@unirep/utils'
import {UserState} from '@unirep/core'
import {defaultProver} from '@unirep/circuits/provers/defaultProver'
import {getUnirepContract, Unirep} from '@unirep/contracts'
const snarkjs = require('snarkjs');
const path = require('path');
const buildPath = path.join(process.cwd(), './node_modules/@unirep/circuits/zksnarkBuild/');
const {EPOCH_TREE_ARITY, EPOCH_TREE_DEPTH, FIELD_COUNT} = CircuitConfig.default;
const prover: Prover = {
genProofAndPublicSignals: async (
proofType: string | Circuit,
inputs: any
): Promise<{
proof: any,
publicSignals: any
}> => {
const circuitWasmPath = buildPath + `${proofType}.wasm`
const zkeyPath = buildPath + `${proofType}.zkey`
const { proof, publicSignals } = await snarkjs.groth16.fullProve(
inputs,
circuitWasmPath,
zkeyPath
)
return { proof, publicSignals }
},
verifyProof: async (
name: string | Circuit,
publicSignals: SnarkPublicSignals,
proof: SnarkProof
): Promise<boolean> => {
const vkey = require(buildPath + `${name}.vkey.json`)
return snarkjs.groth16.verify(vkey, publicSignals, proof)
},
getVKey: async (name: string | Circuit): Promise<any> => {
const vkey = require(buildPath + `${name}.vkey.json`)
return vkey;
}
}
describe("Global Anonymous Feed Contract", () => {
let postContract: GlobalAnonymousFeed
let unirepContract: Unirep
let signupVerifier: any
const groupId = 42
const identitySeed = 'identity'
let accounts: Signer[]
let provider: any;
const zkIdentity = new ZkIdentity(Strategy.SERIALIZED, '{"identityNullifier":"eadba25c151506b68aef691a6072b20a51d4030a5332e29483b10fa12c8ea7f6","identityTrapdoor":"e42266d7dd3b9702de1ec974c2741e347b5f6a9cadc4884d9dd9d20962f94b96","secret":["eadba25c151506b68aef691a6072b20a51d4030a5332e29483b10fa12c8ea7f6","e42266d7dd3b9702de1ec974c2741e347b5f6a9cadc4884d9dd9d20962f94b96"]}')
const zkIdentity2 = new ZkIdentity(Strategy.SERIALIZED, '{"identityNullifier":"6bd38cc008a35836c0c37496f709c6e0169c86c8a2cda7133f0729d8209380","identityTrapdoor":"eb8953d9665b258129dd8a745ed66a43f178fd1f787fc8bfc5d76c1f5f2fe4c2","secret":["6bd38cc008a35836c0c37496f709c6e0169c86c8a2cda7133f0729d8209380","eb8953d9665b258129dd8a745ed66a43f178fd1f787fc8bfc5d76c1f5f2fe4c2"]}')
before(async () => {
provider = new ethers.providers.JsonRpcProvider(process.env.ETHEREUM_URL)
const signer = new ethers.Wallet(process.env.ETHEREUM_PRIVATE_KEY as string, provider)
postContract = new GlobalAnonymousFeed__factory(signer).attach(process.env.GLOBAL_ANONYMOUS_FEED_ADDRESS as string);
// unirepContract = getUnirepContract('0x5e5384c3EA26185BADF41d6980397eB4D36b850e', signer);
unirepContract = getUnirepContract('0xF309DDf2Cc1b2701fED5171C5150092bAc946f07', signer);
signupVerifier = new ethers.Contract(
await unirepContract.signupVerifier(),
// '0xd5d0eCcCAD248661C4bB5B5c75DEa07d74A7EF6e',
[
{
"inputs": [
{
"internalType": "uint256[]",
"name": "input",
"type": "uint256[]"
},
{
"internalType": "uint256[8]",
"name": "_proof",
"type": "uint256[8]"
}
],
"name": "verifyProof",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
}
],
provider,
)
console.log(await postContract.attesterCurrentEpoch());
console.log(await postContract.attesterEpochRemainingTime());
console.log(buildPath);
})
describe("Global Anonymous Feed Contract", () => {
it("Should allow users to create persona", async () => {
// TODO: need to add getters for posts
// TODO: add ability to migrate admin
const state = new UserState({
prover: defaultProver, // a circuit prover
attesterId: (await postContract.attesterId()).toBigInt(),
unirepAddress: await postContract.unirep(),
provider, // an ethers.js provider
}, zkIdentity)
await state.sync.start();
await state.waitForSync();
const signupProof = await state.genUserSignUpProof();
const persona = await postContract.personas(0);
if (persona.name === '') {
await postContract["createPersona(string,string,string,bytes32,bytes32,bytes32[5])"](
'testing group',
'http://profile.image',
'http://cover.image',
'0x56904a481c9a7ff8e9b51310d8bd9a9b3e1f0c5ea3f82a58b070e0ceb888c685',
'0x56904a481c9a7ff8e9b51310d8bd9a9b3e1f0c5ea3f82a58b070e0ceb888c685',
[
'0x56904a481c9a7ff8e9b51310d8bd9a9b3e1f0c5ea3f82a58b070e0ceb888c685',
'0x56904a481c9a7ff8e9b51310d8bd9a9b3e1f0c5ea3f82a58b070e0ceb888c685',
'0x56904a481c9a7ff8e9b51310d8bd9a9b3e1f0c5ea3f82a58b070e0ceb888c685',
'0x56904a481c9a7ff8e9b51310d8bd9a9b3e1f0c5ea3f82a58b070e0ceb888c685',
'0x56904a481c9a7ff8e9b51310d8bd9a9b3e1f0c5ea3f82a58b070e0ceb888c685',
],
{ gasLimit: 6721974 }
);
}
});
it("Should allow users to join a group", async () => {
const state = new UserState({
prover: defaultProver, // a circuit prover
attesterId: (await postContract.attesterId()).toBigInt(),
unirepAddress: await postContract.unirep(),
provider, // an ethers.js provider
}, zkIdentity)
const state2 = new UserState({
prover: defaultProver, // a circuit prover
attesterId: (await postContract.attesterId()).toBigInt(),
unirepAddress: await postContract.unirep(),
provider, // an ethers.js provider
}, zkIdentity2)
await new Promise(r => setTimeout(r, 5000));
await state.sync.start();
await state.waitForSync();
await state2.sync.start();
await state2.waitForSync();
const signupProof = await state.genUserSignUpProof();
const memberSignedUp = await postContract.membersByPersona(0, signupProof.publicSignals[0]);
console.log(await unirepContract.signupVerifier());
console.log(await signupVerifier.verifyProof(signupProof.publicSignals, signupProof.proof));
if (!memberSignedUp) {
await postContract["joinPersona(uint256,uint256[],uint256[8])"](
0,
signupProof.publicSignals,
signupProof.proof,
{ gasLimit: 16721974 }
);
}
const signupProof2 = await state2.genUserSignUpProof();
const memberSignedUp2 = await postContract.membersByPersona(0, signupProof2.publicSignals[0]);
if (!memberSignedUp2) {
await postContract["joinPersona(uint256,uint256[],uint256[8])"](
0,
signupProof2.publicSignals,
signupProof2.proof,
{ gasLimit: 6721974 }
);
}
});
it('should be able to grant reputations', async () => {
const state = new UserState({
prover: defaultProver, // a circuit prover
attesterId: (await postContract.attesterId()).toBigInt(),
unirepAddress: await postContract.unirep(),
provider, // an ethers.js provider
}, zkIdentity);
const state2 = new UserState({
prover: defaultProver, // a circuit prover
attesterId: (await postContract.attesterId()).toBigInt(),
unirepAddress: await postContract.unirep(),
provider, // an ethers.js provider
}, zkIdentity2);
await state.sync.start();
await state.waitForSync();
await state2.sync.start();
await state2.waitForSync();
const repProofs = await state.genProveReputationProof({});
await postContract.grantReputation(100, repProofs.publicSignals, repProofs.proof, { gasLimit: 6721974 });
const repProofs2 = await state2.genProveReputationProof({});
await postContract.grantReputation(50, repProofs2.publicSignals, repProofs2.proof, { gasLimit: 6721974 });
});
it('should seal epoch with new attestations', async () => {
const remainingTime = (await postContract.attesterEpochRemainingTime()).toNumber() + 5;
console.log(remainingTime);
for (let i = 0; i < remainingTime; i++) {
await new Promise(r => setTimeout(r, 1000));
console.log(remainingTime - i);
}
const attestations = await unirepContract.queryFilter(unirepContract.filters.Attestation(
(await postContract.attesterCurrentEpoch()).toNumber(),
null,
(await postContract.attesterId()).toBigInt(),
));
const preimages = [];
let epoch = 0;
for (let i = 0; i < attestations.length; i++) {
const attestation = attestations[i];
epoch = attestation.args.epoch.toNumber();
preimages.push([
attestation.args.epochKey.toString(),
...Array(FIELD_COUNT)
.fill(0)
.map((_, i) => {
if (i === attestation.args.fieldIndex.toNumber()) {
return attestation.args.change;
} else {
return 0;
}
}),
]);
}
const { circuitInputs } = BuildOrderedTree.buildInputsForLeaves(preimages);
const { publicSignals, proof } = await prover.genProofAndPublicSignals(
Circuit.buildOrderedTree,
stringifyBigInts(circuitInputs),
);
const bot = new BuildOrderedTree(publicSignals, proof);
await postContract.sealEpoch(epoch, bot.publicSignals, bot.proof, { gasLimit: 6721974 })
});
it("should transition user", async () => {
const state = new UserState({
prover: defaultProver, // a circuit prover
attesterId: (await postContract.attesterId()).toBigInt(),
unirepAddress: await postContract.unirep(),
provider, // an ethers.js provider
}, zkIdentity);
const state2 = new UserState({
prover: defaultProver, // a circuit prover
attesterId: (await postContract.attesterId()).toBigInt(),
unirepAddress: await postContract.unirep(),
provider, // an ethers.js provider
}, zkIdentity2);
await state.sync.start();
await state.waitForSync();
await state2.sync.start();
await state2.waitForSync();
const ust = await state.genUserStateTransitionProof({});
await postContract.userStateTransition(ust.publicSignals, ust.proof, { gasLimit: 6721974 });
const ust2 = await state2.genUserStateTransitionProof({});
await postContract.userStateTransition(ust2.publicSignals, ust2.proof, { gasLimit: 6721974 });
});
it("Should allow users to propose a post", async () => {
const state = new UserState({
prover: defaultProver, // a circuit prover
attesterId: (await postContract.attesterId()).toBigInt(),
unirepAddress: await postContract.unirep(),
provider, // an ethers.js provider
}, zkIdentity)
await state.sync.start();
await state.waitForSync();
const repProofs = await state.genProveReputationProof({ minRep: 100 });
await postContract["proposeMessage(uint256,uint8,bytes32,uint256[],uint256[8])"](
0,
0,
'0x56904a481c9a7ff8e9b51310d8bd9a9b3e1f0c5ea3f82a58b070e0ceb888c683',
repProofs.publicSignals,
repProofs.proof,
{ gasLimit: 6721974 },
);
})
it("Should allow users to propose a comment", async () => {
const state = new UserState({
prover: defaultProver, // a circuit prover
attesterId: (await postContract.attesterId()).toBigInt(),
unirepAddress: await postContract.unirep(),
provider, // an ethers.js provider
}, zkIdentity)
await state.sync.start();
await state.waitForSync();
const repProofs = await state.genProveReputationProof({ minRep: 50 });
await postContract["proposeMessage(uint256,uint8,bytes32,uint256[],uint256[8])"](
0,
1,
'0x44444a481c9a7ff8e9b51310d8bd9a9b3e1f0c5ea3f82a58b070e0ceb888c683',
repProofs.publicSignals,
repProofs.proof,
{ gasLimit: 6721974 },
);
})
it("Should allow users to vote", async () => {
const state = new UserState({
prover: defaultProver, // a circuit prover
attesterId: (await postContract.attesterId()).toBigInt(),
unirepAddress: await postContract.unirep(),
provider, // an ethers.js provider
}, zkIdentity2)
await state.sync.start();
await state.waitForSync();
console.log(await postContract.attesterCurrentEpoch());
console.log(await postContract.attesterEpochRemainingTime());
const repProofs = await state.genProveReputationProof({});
await postContract.vote(
'0x56904a481c9a7ff8e9b51310d8bd9a9b3e1f0c5ea3f82a58b070e0ceb888c683',
true,
repProofs.publicSignals,
repProofs.proof,
{ gasLimit: 6721974 }
);
await postContract.vote(
'0x56904a481c9a7ff8e9b51310d8bd9a9b3e1f0c5ea3f82a58b070e0ceb888c683',
true,
repProofs.publicSignals,
repProofs.proof,
{ gasLimit: 6721974 }
);
await postContract.vote(
'0x56904a481c9a7ff8e9b51310d8bd9a9b3e1f0c5ea3f82a58b070e0ceb888c683',
false,
repProofs.publicSignals,
repProofs.proof,
{ gasLimit: 6721974 }
);
await postContract.vote(
'0x56904a481c9a7ff8e9b51310d8bd9a9b3e1f0c5ea3f82a58b070e0ceb888c683',
false,
repProofs.publicSignals,
repProofs.proof,
{ gasLimit: 6721974 }
);
await postContract.vote(
'0x56904a481c9a7ff8e9b51310d8bd9a9b3e1f0c5ea3f82a58b070e0ceb888c683',
false,
repProofs.publicSignals,
repProofs.proof,
{ gasLimit: 6721974 }
);
await postContract.vote(
'0x56904a481c9a7ff8e9b51310d8bd9a9b3e1f0c5ea3f82a58b070e0ceb888c683',
true,
repProofs.publicSignals,
repProofs.proof,
{ gasLimit: 6721974 }
);
await postContract.vote(
'0x56904a481c9a7ff8e9b51310d8bd9a9b3e1f0c5ea3f82a58b070e0ceb888c683',
true,
repProofs.publicSignals,
repProofs.proof,
{ gasLimit: 6721974 }
);
});
it("Should tally votes", async () => {
const remainingTime = (await postContract.attesterEpochRemainingTime()).toNumber() + 5;
console.log(remainingTime);
for (let i = 0; i < remainingTime; i++) {
await new Promise(r => setTimeout(r, 1000));
console.log(remainingTime - i);
}
const attestations = await unirepContract.queryFilter(unirepContract.filters.Attestation(
1,
null,
(await postContract.attesterId()).toBigInt(),
));
const mapping: any = {};
let epoch = 0;
for (let i = 0; i < attestations.length; i++) {
const attestation = attestations[i];
epoch = attestation.args.epoch.toNumber();
const epochKey = attestation.args.epochKey.toString();
mapping[epochKey] = mapping[epochKey] || [
attestation.args.epochKey.toString(),
...Array(FIELD_COUNT).fill(0),
];
const fieldIndex = attestation.args.fieldIndex.toNumber();
mapping[epochKey][fieldIndex + 1] = attestation.args.change.add(mapping[epochKey][fieldIndex + 1]);
}
const preimages = Object.values(mapping);
console.log(preimages);
const { circuitInputs } = BuildOrderedTree.buildInputsForLeaves(preimages);
const { publicSignals, proof } = await prover.genProofAndPublicSignals(
Circuit.buildOrderedTree,
stringifyBigInts(circuitInputs),
);
const bot = new BuildOrderedTree(publicSignals, proof);
await postContract.sealEpoch(epoch, bot.publicSignals, bot.proof, { gasLimit: 6721974 })
})
it("should transition user again", async () => {
const state = new UserState({
prover: defaultProver, // a circuit prover
attesterId: (await postContract.attesterId()).toBigInt(),
unirepAddress: await postContract.unirep(),
provider, // an ethers.js provider
}, zkIdentity);
const state2 = new UserState({
prover: defaultProver, // a circuit prover
attesterId: (await postContract.attesterId()).toBigInt(),
unirepAddress: await postContract.unirep(),
provider, // an ethers.js provider
}, zkIdentity2);
await state.sync.start();
await state2.sync.start();
await state.waitForSync();
await state2.waitForSync();
const ust = await state.genUserStateTransitionProof({});
await postContract.userStateTransition(ust.publicSignals, ust.proof, { gasLimit: 6721974 });
const ust2 = await state2.genUserStateTransitionProof({});
await postContract.userStateTransition(ust2.publicSignals, ust2.proof, { gasLimit: 6721974 });
await state.waitForSync();
await state2.waitForSync();
console.log((await state.genProveReputationProof({ minRep: 105 })));
console.log((await state2.genProveReputationProof({ minRep: 54 })));
});
})
})