Files
semaphore/docs/versioned_docs/version-V2/quick-setup.md
cedoor 7095e03162 docs: fix worng link
Former-commit-id: adf6a0f866
2022-02-12 16:06:27 +01:00

7.4 KiB

sidebar_position
sidebar_position
2

Quick setup

The following setup will allow you to create a simple Hardhat project with Semaphore (contract and tests). If you need a more complete demo try semaphore-boilerplate. It can be a good starting point to create your DApp.

:::info Visit the semaphore-quick-setup repository to get a full view of the code used below. :::

Create a Node.js project

  1. Download and install Node.js/NPM and Yarn.
  2. Create your project:
$ mkdir semaphore-example
$ cd semaphore-example
$ yarn init

Install Hardhat and the Semaphore packages

  1. Install Hardhat and create a basic sample project:
$ yarn add hardhat --dev
$ yarn hardhat # Create a basic sample project and confirm the other requests.

:::tip Feel free to create more complex sample projects if you are an advanced Hardhat user. :::

  1. Install the Semaphore contracts and its ZK-kit libraries:
$ yarn add @appliedzkp/semaphore-contracts
$ yarn add @zk-kit/identity @zk-kit/protocols --dev

Create your contract

Rename Greeter.sol to Greeters.sol and insert the following code:

//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;

import "@appliedzkp/semaphore-contracts/base/SemaphoreCore.sol";

/// @title Greeters contract.
/// @dev The following code is just a example to show how Semaphore con be used.
contract Greeters is SemaphoreCore {
  // A new greeting is published every time a user's proof is validated.
  event NewGreeting(string greeting);

  // Greeters are identified by a Merkle root.
  // The offchain Merkle tree contains the greeters' identity commitments.
  uint256 public greeters;

  constructor(uint256 _greeters) {
    greeters = _greeters;
  }

  // Only users who create valid proofs can greet.
  // The external nullifier is in this example the root of the Merkle tree.
  function greet(
    string calldata _greeting,
    uint256 _nullifierHash,
    uint256[8] calldata _proof
  ) external {
    require(_isValidProof(_greeting, greeters, _nullifierHash, greeters, _proof), "Greeters: the proof is not valid");

    // Prevent double-greeting (nullifierHash = hash(root + identityNullifier)).
    // Every user can greet once.
    _saveNullifierHash(_nullifierHash);

    emit NewGreeting(_greeting);
  }
}

Create some identity commitments

Identity commitments are used as the leaves of the Merkle trees used in the protocol and represent the identity of the users. Create a static folder and add the following file:

[
  "9426253249246138013650573474062059446203468399013007463704855436559640562175",
  "6200634377081441056179822649025268043304989981899916286941956069781421654881",
  "19706772421195815860043593475869058320994241404138740034486179990871964981523"
]

:::info The previous identity commitments have been generated using @zk-kit/identity (with a message strategy) and Metamask for signing the messages with the first 3 Ethereum accounts of the Hardhat dev wallet. :::

Create a Hardhat task to deploy your contract

  1. Install @zk-kit/incremental-merkle-tree and circomlibjs@0.0.8 to create offchain Merkle trees.
$ yarn add @zk-kit/incremental-merkle-tree circomlibjs@0.0.8 --dev
  1. Create a tasks folder and add the following file:
const { IncrementalMerkleTree } = require("@zk-kit/incremental-merkle-tree")
const { poseidon } = require("circomlibjs")
const identityCommitments = require("../static/identityCommitments.json")
const { task, types } = require("hardhat/config")

task("deploy", "Deploy a Greeters contract")
  .addOptionalParam("logs", "Print the logs", true, types.boolean)
  .setAction(async ({ logs }, { ethers }) => {
    const ContractFactory = await ethers.getContractFactory("Greeters")
    const tree = new IncrementalMerkleTree(poseidon, 20, BigInt(0), 5)

    for (const identityCommitment of identityCommitments) {
      tree.insert(identityCommitment) // Insert the Merkle tree leaves.
    }

    const contract = await ContractFactory.deploy(tree.root)

    await contract.deployed()

    logs && console.log(`Greeters contract has been deployed to: ${contract.address}`)

    return contract
  })
  1. Import your new Hardhat task in the hardhat.config.js file:
require("@nomiclabs/hardhat-waffle")
require("./tasks/deploy")

module.exports = {
    solidity: "0.8.4"
}

Create your tests

  1. Creating proofs requires some static files, in the future these files will be hosted on a server and made public. For now you can use the ones used in our repository for testing. Copy these files in the static folder.

  2. Update the Hardhat test file:

const { Strategy, ZkIdentity } = require("@zk-kit/identity")
const { generateMerkleProof, Semaphore } = require("@zk-kit/protocols")
const identityCommitments = require("../static/identityCommitments.json")
const { expect } = require("chai")
const { run, ethers } = require("hardhat")

describe("Greeters", function () {
    let contract
    let signers

    before(async () => {
        contract = await run("deploy", { logs: false })

        signers = await ethers.getSigners()
    })

    describe("# greet", () => {
        const wasmFilePath = "./static/semaphore.wasm"
        const finalZkeyPath = "./static/semaphore_final.zkey"

        it("Should greet", async () => {
            const message = await signers[0].signMessage("Sign this message to create your identity!")

            const identity = new ZkIdentity(Strategy.MESSAGE, message)
            const identityCommitment = identity.genIdentityCommitment()

            const greeting = "Hello world"

            const merkleProof = generateMerkleProof(
                20,
                BigInt(0),
                5,
                identityCommitments.map(BigInt),
                identityCommitment
            )
            const witness = Semaphore.genWitness(
                identity.getTrapdoor(),
                identity.getNullifier(),
                merkleProof,
                merkleProof.root,
                greeting
            )

            const fullProof = await Semaphore.genProof(witness, wasmFilePath, finalZkeyPath)
            const solidityProof = Semaphore.packToSolidityProof(fullProof)

            const nullifierHash = Semaphore.genNullifierHash(merkleProof.root, identity.getNullifier())

            const transaction = contract.greet(greeting, nullifierHash, solidityProof)

            await expect(transaction).to.emit(contract, "NewGreeting").withArgs(greeting)
        })
    })
})
  1. Test your contract:
$ yarn hardhat test

Deploy your contract in a local network

You can also deploy your contract in a local Hardhat network and use it in your DApp:

$ yarn hardhat node
$ yarn hardhat deploy --network localhost