# [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()
})
})
```