# [Workshop] Build a web3 anonymous social media with UniRep. ## Introdoction UniRep is a private and non-repudiable data system. Users can receive attestations from attesters, and voluntarily prove facts about their data without revealing the data itself. Using the UniRep protocol, we can establish an anonymous social media platform where user interactions are governed by upvotes and downvotes. This system encourages greater accountability among users when posting and commenting. ## Speaker Vivian - github: https://github.com/vivianjeng - telegram: @vivianjeng ## Before join the workshop - The talk will be in mandarin (中文). - Install - [yarn v1](https://www.npmjs.com/package/yarn) - [nodejs v18^](https://nodejs.org/en/blog/release/v18.18.0) ## Learn more about UniRep - Website: unirep.io - Github: https://github.com/unirep/unirep - More projects: https://unirep.pages.dev/projects ## Slides: - https://docs.google.com/presentation/d/14EW8KtVtxl87zZKK9LhAVTdwGMz8gBO0wq2S_vGm370/edit?usp=sharing ## Join UniRep Social TW - Discord: https://discord.gg/rACuNgY5Qz - GitHub: https://github.com/social-tw ## contract ```solidity // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; import { Unirep } from "@unirep/contracts/Unirep.sol"; import { EpochKeyVerifierHelper } from '@unirep/contracts/verifierHelpers/EpochKeyVerifierHelper.sol'; // Uncomment this line to use console.log // import "hardhat/console.sol"; interface IVerifier { function verifyProof( uint256[7] calldata publicSignals, uint256[8] calldata proof ) external view returns (bool); } contract UnirepApp { Unirep public unirep; IVerifier internal dataVerifier; // use the deployed helper EpochKeyVerifierHelper public helper; uint256 public postCount = 1; mapping(uint256 => uint256) postIdEpochkeyMapping; constructor(Unirep _unirep, IVerifier _dataVerifier, EpochKeyVerifierHelper _helper, uint48 _epochLength) { // set unirep address unirep = _unirep; // set verifier address dataVerifier = _dataVerifier; // epk verifier helper helper = _helper; // sign up as an attester unirep.attesterSignUp(_epochLength); } // sign up users in this app function userSignUp( uint256[] calldata publicSignals, uint256[8] calldata proof ) public { unirep.userSignUp(publicSignals, proof); } function submitManyAttestations( uint256 epochKey, uint48 targetEpoch, uint[] calldata fieldIndices, uint[] calldata vals ) public { require(fieldIndices.length == vals.length, 'arrmismatch'); for (uint8 x = 0; x < fieldIndices.length; x++) { unirep.attest(epochKey, targetEpoch, fieldIndices[x], vals[x]); } } function submitAttestation( uint256 epochKey, uint48 targetEpoch, uint256 fieldIndex, uint256 val ) public { unirep.attest( epochKey, targetEpoch, fieldIndex, val ); } function verifyDataProof( uint256[7] calldata publicSignals, uint256[8] calldata proof ) public view returns(bool) { return dataVerifier.verifyProof( publicSignals, proof ); } function post( // epoch key proof uint256[] calldata epkPublicSignals, uint256[8] calldata epkProof, // content string calldata content ) public { // verify EpochKeyVerifierHelper.EpochKeySignals memory signals = helper.verifyAndCheckCaller(epkPublicSignals, epkProof); // mapping post id to epoch key postIdEpochkeyMapping[postCount] = signals.epochKey; postCount ++; } function vote( // epoch key proof uint256[] calldata epkPublicSignals, uint256[8] calldata epkProof, // post id uint256 postId, // vote uint256 upvote, uint256 downvote ) public { // verify EpochKeyVerifierHelper.EpochKeySignals memory signals = helper.verifyAndCheckCaller(epkPublicSignals, epkProof); // attest uint256 receiver = postIdEpochkeyMapping[postId]; uint256 fieldIndex = upvote > 0 ? 0 : 1; uint256 change = upvote > 0 ? upvote : downvote; unirep.attest( receiver, signals.epoch, fieldIndex, change ); } } ``` ## test ```typescript //@ts-ignore import { ethers } from 'hardhat' import { expect } from 'chai' import { deployUnirep, deployVerifierHelper } from '@unirep/contracts/deploy' import { stringifyBigInts } from '@unirep/utils' import { schema, UserState } from '@unirep/core' import { SQLiteConnector } from 'anondb/node' import { DataProof } from '@unirep-app/circuits' import { Circuit } from '@unirep/circuits' import { CircuitConfig } from '@unirep/circuits' import { Identity } from '@semaphore-protocol/identity' const { FIELD_COUNT } = CircuitConfig.default import { defaultProver as prover } from '@unirep-app/circuits/provers/defaultProver' async function genUserState(id, app) { // generate a user state const db = await SQLiteConnector.create(schema, ':memory:') const unirepAddress = await app.unirep() const attesterId = BigInt(app.address) const userState = new UserState({ db, prover, unirepAddress, provider: ethers.provider, attesterId, id, }) await userState.start() await userState.waitForSync() return userState } describe('Unirep App', function () { let unirep let app // epoch length const epochLength = 300 // generate random user id const id = new Identity() it('deployment', async function () { const [deployer] = await ethers.getSigners() unirep = await deployUnirep(deployer) const verifierF = await ethers.getContractFactory('DataProofVerifier') const verifier = await verifierF.deploy() await verifier.deployed() const epochKeyVerifierHelper = await deployVerifierHelper( unirep.address, deployer, Circuit.epochKey ) const App = await ethers.getContractFactory('UnirepApp') app = await App.deploy(unirep.address, verifier.address, epochKeyVerifierHelper.address, epochLength) await app.deployed() }) it('user sign up', async () => { const userState = await genUserState(id, app) // generate const { publicSignals, proof } = await userState.genUserSignUpProof() await app.userSignUp(publicSignals, proof).then((t) => t.wait()) userState.stop() }) it('post', async () => { const userState = await genUserState(id, app) const {publicSignals, proof}= await userState.genEpochKeyProof() const content = "hello world" await app.post(publicSignals, proof, content).then((t) => t.wait()) userState.stop() }) it('vote', async () => { const id2 = new Identity() const userState = await genUserState(id2, app) // user2 sign up { const { publicSignals, proof } = await userState.genUserSignUpProof() await app.userSignUp(publicSignals, proof).then((t) => t.wait()) await userState.waitForSync() } // user2 vote const {publicSignals, proof}= await userState.genEpochKeyProof() const postId = 1 const upvote = 3 const downvote = 0 await app.vote(publicSignals, proof, postId, upvote, downvote).then((t) => t.wait()) userState.stop() }) }) ```