From 94487f6f58832e22b143167c6c4185a08a46a035 Mon Sep 17 00:00:00 2001 From: Vojtech Simetka Date: Tue, 2 May 2023 22:45:15 +0200 Subject: [PATCH] feat: simplified local deployment --- README.md | 70 ++++++++++++++++++- packages/contracts/hardhat.config.ts | 33 +++------ packages/contracts/package.json | 2 +- packages/contracts/scripts/deploy-unirep.ts | 12 ---- packages/contracts/tasks/deploy.ts | 58 +++++++++------ .../contracts/test/GlobalAnonymousFeed.ts | 2 +- packages/relayer/.env.example | 4 +- packages/relayer/src/app.ts | 6 +- packages/relayer/src/config/index.ts | 6 +- packages/relayer/src/routes/root.ts | 10 +-- packages/relayer/src/services/epoch.ts | 6 +- packages/ui/.env | 4 +- 12 files changed, 135 insertions(+), 78 deletions(-) delete mode 100644 packages/contracts/scripts/deploy-unirep.ts diff --git a/README.md b/README.md index 38d046f0..4d36c138 100644 --- a/README.md +++ b/README.md @@ -48,8 +48,76 @@ Also, curating and creating Players earn reputation (REP) for positive gameplay We are using ZK-Proof technology and [Waku](https://waku.org/) to ensure privacy, with a hat-tip to [Unirep](https://medium.com/privacy-scaling-explorations/unirep-a-private-and-non-repudiable-reputation-system-7fb5c6478549), and [Semaphore](https://semaphore.appliedzkp.org/). ## For Developers -**Would you like to launch and play with Kurate locally?** +0. Install all dependencies +```sh +pnpm i +``` + +1. Start blockchain and deploy contracts +```sh +cd packages/contracts +``` + +```sh +pnpm start:blockchain +``` + +In another terminal window, compile, deploy the contracts +``` +pnpm start +``` + +If successfully, the output should say: +``` +GlobalAnonymousFeedContract contract has been deployed +Don't forget to set the variables for both the UI and relayer + +PUBLIC_GLOBAL_ANONYMOUS_FEED_ADDRESS=0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0 +PUBLIC_PROVIDER=http://localhost:8545 + +Relayer only +PRIVATE_KEY=... + +UI only +PUBLIC_RELAYER_URL=... +``` + +2. Start relayer +```sh +cd packages/contracts +``` +Set the environment variables according to the contract deployment (for private key you can use any hardhat key). Should be: +```sh +PUBLIC_GLOBAL_ANONYMOUS_FEED_ADDRESS=0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0 +PUBLIC_PROVIDER=http://localhost:8545 +PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 +``` + +Build and start the relayer +``` +pnpm build +pnpm start +``` + +3. Start UI +```sh +cd packages/ui +``` + +Set the environment variables according to the contract deployment and where the relayer lives: Should be: +```sh +PUBLIC_GLOBAL_ANONYMOUS_FEED_ADDRESS=0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0 +PUBLIC_PROVIDER=http://localhost:8545 +PUBLIC_RELAYER_URL=http://localhost:3000 +``` + +Start the UI with +```sh +pnpm dev +``` + +You can now open the app at http://localhost:5173/ . Just make sure you are using either the `zkitter` or the `zkitter-god-node` adapter. You can configure those in `/dev` route (http://localhost:5173/dev) **Are you interested in contributing to Kurate?** diff --git a/packages/contracts/hardhat.config.ts b/packages/contracts/hardhat.config.ts index 3248c807..3caabb9c 100644 --- a/packages/contracts/hardhat.config.ts +++ b/packages/contracts/hardhat.config.ts @@ -14,35 +14,25 @@ import "./tasks/deploy" dotenvConfig({ path: resolve(__dirname, "../../.env") }) function getNetworks(): NetworksUserConfig { - if (process.env.ETHEREUM_URL && process.env.ETHEREUM_PRIVATE_KEY) { - const accounts = [`0x${process.env.ETHEREUM_PRIVATE_KEY}`] + const networks: NetworksUserConfig = { + localhost: { + url: 'http://127.0.0.1:8545', + chainId: 31337 + } + } + if (process.env.ETHEREUM_URL && process.env.ETHEREUM_PRIVATE_KEY) { return { - goerli: { - url: process.env.ETHEREUM_URL, - chainId: 5, - accounts - }, + ...networks, // arbitrum goerli agor: { url: 'https://goerli-rollup.arbitrum.io/rpc', chainId: 421613, - accounts, - }, - sepolia: { - url: process.env.ETHEREUM_URL, - chainId: 11155111, - accounts + accounts: [process.env.ETHEREUM_PRIVATE_KEY], }, - localhost: { - url: 'http://127.0.0.1:7545', - chainId: 1337, - accounts, - } } } - - return {} + return networks } const hardhatConfig: HardhatUserConfig = { @@ -54,9 +44,6 @@ const hardhatConfig: HardhatUserConfig = { artifacts: config.paths.build.contracts }, networks: { - hardhat: { - chainId: 1337 - }, ...getNetworks() }, gasReporter: { diff --git a/packages/contracts/package.json b/packages/contracts/package.json index fd16bb45..6da213fb 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -4,7 +4,7 @@ "private": true, "main": "index.js", "scripts": { - "start": "pnpm run compile && pnpm run deploy -- --network localhost", + "start": "pnpm run compile && pnpm run deploy --network localhost", "start:blockchain": "hardhat node", "compile": "hardhat compile", "download:snark-artifacts": "hardhat run scripts/download-snark-artifacts.ts", diff --git a/packages/contracts/scripts/deploy-unirep.ts b/packages/contracts/scripts/deploy-unirep.ts deleted file mode 100644 index 0b43e2c2..00000000 --- a/packages/contracts/scripts/deploy-unirep.ts +++ /dev/null @@ -1,12 +0,0 @@ -import {ethers} from 'ethers' -import { Unirep } from '@unirep/contracts' -import { deployUnirep } from '@unirep/contracts/deploy' - -const privateKey = process.env.ETHEREUM_PRIVATE_KEY as string; -const provider = new ethers.providers.JsonRpcProvider(process.env.ETHEREUM_URL); -const deployer = new ethers.Wallet(privateKey, provider); - -(async () => { - const unirepContract: Unirep = await deployUnirep(deployer) - console.log(unirepContract); -})(); diff --git a/packages/contracts/tasks/deploy.ts b/packages/contracts/tasks/deploy.ts index dd756737..2044d7c6 100644 --- a/packages/contracts/tasks/deploy.ts +++ b/packages/contracts/tasks/deploy.ts @@ -1,43 +1,57 @@ +import { Unirep } from "@unirep/contracts"; +import { deployUnirep } from "@unirep/contracts/deploy"; import { task, types } from "hardhat/config" +// 28800 seconds = 8 hours per epoch +const DEFAULT_EPOCH_LENGTH = 28800; + task("deploy", "Deploy a GlobalAnonymousFeed contract") .addOptionalParam("unirep", "unirep contract address", undefined, types.string) .addOptionalParam("logs", "Print the logs", true, types.boolean) - .setAction(async ({ logs, unirep: unirepAddress }, { ethers, run }) => { + .addOptionalParam("epoch", `Epoch length (defaults to ${DEFAULT_EPOCH_LENGTH}`, DEFAULT_EPOCH_LENGTH, types.int) + .setAction(async ({ logs, unirep: unirepAddress, epoch: epochLength }, { ethers }) => { const globalAnonymousFeedFactory = await ethers.getContractFactory("GlobalAnonymousFeed"); const [deployer] = await ethers.getSigners(); - console.log( - "Deploying contracts with the account:", - deployer.address - ); + logs && console.log(`Deploying contracts with the account: ${deployer.address}`); + logs && console.log("Account balance:", ethers.utils.formatEther(await deployer.getBalance())); + + if (!unirepAddress) { + logs && console.log("Unirep contract address not provided, deploying Unirep first\n") + const unirepContract: Unirep = await deployUnirep(deployer) + unirepAddress = unirepContract.address; + } - console.log("Account balance:", (await deployer.getBalance()).toString()); + logs && console.log(`\nUnirep contract address: ${unirepAddress}`); const gasPrice = await globalAnonymousFeedFactory.signer.getGasPrice(); - // const estimatedGas = await globalAnonymousFeedFactory.signer.estimateGas(globalAnonymousFeedFactory.getDeployTransaction('0xF309DDf2Cc1b2701fED5171C5150092bAc946f07', 28800)); - const estimatedGas = await globalAnonymousFeedFactory.signer.estimateGas(globalAnonymousFeedFactory.getDeployTransaction('0x5e5384c3EA26185BADF41d6980397eB4D36b850e', 60)); - console.log(`Estimated gas: ${estimatedGas}`); - console.log(`Gas Price: ${gasPrice}`) + const estimatedGas = await globalAnonymousFeedFactory.signer.estimateGas(globalAnonymousFeedFactory.getDeployTransaction(unirepAddress, epochLength)); + logs && console.log(`Estimated gas: ${estimatedGas}`); + logs && console.log(`Gas price: ${gasPrice}`) + const deploymentPrice = gasPrice.mul(estimatedGas); const deployerBalance = await globalAnonymousFeedFactory.signer.getBalance(); - console.log(`Deployer balance: ${ethers.utils.formatEther(deployerBalance)}`); - console.log(`Deployment price: ${ethers.utils.formatEther(deploymentPrice)}`); - // This is unirep@2.0.0-beta-1 contract address on Arbitrum Goerli - // https://developer.unirep.io/docs/testnet-deployment - // 28800 seconds = 8 hours per epoch - // const globalAnonymousFeedContract = await globalAnonymousFeedFactory.deploy('0xF309DDf2Cc1b2701fED5171C5150092bAc946f07', 28800); - // This was used for local dev - // 60 seconds = 1 minute per epoch - const globalAnonymousFeedContract = await globalAnonymousFeedFactory.deploy('0x5e5384c3EA26185BADF41d6980397eB4D36b850e', 60); + logs && console.log(`Deployer balance: ${ethers.utils.formatEther(deployerBalance)}`); + logs && console.log(`Deployment price: ${ethers.utils.formatEther(deploymentPrice)}`); + + const globalAnonymousFeedContract = await globalAnonymousFeedFactory.deploy(unirepAddress, epochLength); await globalAnonymousFeedContract.deployed(); - if (logs) { - console.info(`GlobalAnonymousFeedContract contract has been deployed to: ${globalAnonymousFeedContract.address}`); - } + logs && console.info("\n-----------------------------------------------------------------"); + logs && console.info('GlobalAnonymousFeedContract contract has been deployed'); + logs && console.log("Don't forget to set the variables for both the UI and relayer\n"); + + logs && console.log(`PUBLIC_GLOBAL_ANONYMOUS_FEED_ADDRESS=${globalAnonymousFeedContract.address}`); + logs && console.log(`PUBLIC_PROVIDER=${ethers.provider.connection.url}`); + + logs && console.log("\nRelayer only"); + logs && console.log(`PRIVATE_KEY=...`); + + logs && console.log("\nUI only"); + logs && console.log(`PUBLIC_RELAYER_URL=...`); return globalAnonymousFeedContract; }) diff --git a/packages/contracts/test/GlobalAnonymousFeed.ts b/packages/contracts/test/GlobalAnonymousFeed.ts index d8e9d749..2a7b0fef 100644 --- a/packages/contracts/test/GlobalAnonymousFeed.ts +++ b/packages/contracts/test/GlobalAnonymousFeed.ts @@ -62,7 +62,7 @@ describe("Global Anonymous Feed Contract", () => { 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); + postContract = new GlobalAnonymousFeed__factory(signer).attach(process.env.PUBLIC_GLOBAL_ANONYMOUS_FEED_ADDRESS as string); unirepContract = getUnirepContract('0x5e5384c3EA26185BADF41d6980397eB4D36b850e', signer); // unirepContract = getUnirepContract('0xF309DDf2Cc1b2701fED5171C5150092bAc946f07', signer); // signupVerifier = new ethers.Contract( diff --git a/packages/relayer/.env.example b/packages/relayer/.env.example index 8eb4dc98..ece9d581 100644 --- a/packages/relayer/.env.example +++ b/packages/relayer/.env.example @@ -1,3 +1,3 @@ -RPC_URL=https://goerli-rollup.arbitrum.io/rpc -GLOBAL_ANONYMOUS_FEED_ADDRESS=0x0 +PUBLIC_PROVIDER=https://goerli-rollup.arbitrum.io/rpc +PUBLIC_GLOBAL_ANONYMOUS_FEED_ADDRESS=0x0 PRIVATE_KEY= diff --git a/packages/relayer/src/app.ts b/packages/relayer/src/app.ts index 455ce2e3..bc2fd199 100644 --- a/packages/relayer/src/app.ts +++ b/packages/relayer/src/app.ts @@ -3,7 +3,7 @@ import AutoLoad, { AutoloadPluginOptions } from "@fastify/autoload"; import { FastifyPluginAsync } from "fastify"; import { JsonSchemaToTsProvider } from "@fastify/type-provider-json-schema-to-ts"; import { syncGroup } from "./services/rln"; -import { GLOBAL_ANONYMOUS_FEED_ADDRESS, RPC_URL } from "./config"; +import { PUBLIC_GLOBAL_ANONYMOUS_FEED_ADDRESS, PUBLIC_PROVIDER } from "./config"; import { getDefaultProvider } from "@ethersproject/providers"; import epochSealer from "./services/epoch"; @@ -22,8 +22,8 @@ const app: FastifyPluginAsync = async ( fastify.withTypeProvider(); // Services - const provider = getDefaultProvider(RPC_URL); - syncGroup(provider, GLOBAL_ANONYMOUS_FEED_ADDRESS); + const provider = getDefaultProvider(PUBLIC_PROVIDER); + syncGroup(provider, PUBLIC_GLOBAL_ANONYMOUS_FEED_ADDRESS); epochSealer.start(); diff --git a/packages/relayer/src/config/index.ts b/packages/relayer/src/config/index.ts index 9a3aff1d..3e6cd0bf 100644 --- a/packages/relayer/src/config/index.ts +++ b/packages/relayer/src/config/index.ts @@ -3,9 +3,9 @@ import { config } from "dotenv"; config(); const { - GLOBAL_ANONYMOUS_FEED_ADDRESS = "", - RPC_URL = "", + PUBLIC_GLOBAL_ANONYMOUS_FEED_ADDRESS = "", + PUBLIC_PROVIDER = "", PRIVATE_KEY = "", } = process.env; -export { GLOBAL_ANONYMOUS_FEED_ADDRESS, RPC_URL, PRIVATE_KEY }; +export { PUBLIC_GLOBAL_ANONYMOUS_FEED_ADDRESS, PUBLIC_PROVIDER, PRIVATE_KEY }; diff --git a/packages/relayer/src/routes/root.ts b/packages/relayer/src/routes/root.ts index adab7444..b914365c 100644 --- a/packages/relayer/src/routes/root.ts +++ b/packages/relayer/src/routes/root.ts @@ -2,7 +2,7 @@ import { FastifyPluginAsyncJsonSchemaToTs } from "@fastify/type-provider-json-sc // import { rlnRegistry, verifyProof } from "../services/rln"; import { getDefaultProvider } from "@ethersproject/providers"; import { Wallet } from "@ethersproject/wallet"; -import { GLOBAL_ANONYMOUS_FEED_ADDRESS, PRIVATE_KEY, RPC_URL } from "../config"; +import { PUBLIC_GLOBAL_ANONYMOUS_FEED_ADDRESS, PRIVATE_KEY, PUBLIC_PROVIDER } from "../config"; import { GlobalAnonymousFeed__factory } from "../abi"; import cors from '@fastify/cors' const path = require('path') @@ -331,11 +331,11 @@ const getBodySchemaWithoutRep = () => { const root: FastifyPluginAsyncJsonSchemaToTs = async ( fastify ): Promise => { - console.log(RPC_URL) - const provider = getDefaultProvider(RPC_URL); + console.log(PUBLIC_PROVIDER) + const provider = getDefaultProvider(PUBLIC_PROVIDER); const wallet = new Wallet(PRIVATE_KEY, provider); const feed = GlobalAnonymousFeed__factory.connect( - GLOBAL_ANONYMOUS_FEED_ADDRESS, + PUBLIC_GLOBAL_ANONYMOUS_FEED_ADDRESS, wallet ); @@ -360,7 +360,7 @@ const root: FastifyPluginAsyncJsonSchemaToTs = async ( return } const hostname = new URL(origin as string).hostname - if(hostname === "localhost"){ + if(hostname === "localhost" || hostname === "127.0.0.1" || hostname === "kurate.vercel.app"){ // Request from localhost will pass cb(null, true) return diff --git a/packages/relayer/src/services/epoch.ts b/packages/relayer/src/services/epoch.ts index ed61135c..63177e5e 100644 --- a/packages/relayer/src/services/epoch.ts +++ b/packages/relayer/src/services/epoch.ts @@ -1,5 +1,5 @@ import {getDefaultProvider} from "@ethersproject/providers"; -import {GLOBAL_ANONYMOUS_FEED_ADDRESS, PRIVATE_KEY, RPC_URL} from "../config"; +import {PUBLIC_GLOBAL_ANONYMOUS_FEED_ADDRESS, PRIVATE_KEY, PUBLIC_PROVIDER} from "../config"; import {Wallet} from "@ethersproject/wallet"; import {GlobalAnonymousFeed, GlobalAnonymousFeed__factory} from "../abi"; import {BuildOrderedTree, Circuit, CircuitConfig, Prover} from "@unirep/circuits"; @@ -23,10 +23,10 @@ class EpochSealer { nextEpochEnd: number = 0; constructor() { - const provider = getDefaultProvider(RPC_URL); + const provider = getDefaultProvider(PUBLIC_PROVIDER); this.wallet = new Wallet(PRIVATE_KEY, provider); this.contract = GlobalAnonymousFeed__factory.connect( - GLOBAL_ANONYMOUS_FEED_ADDRESS, + PUBLIC_GLOBAL_ANONYMOUS_FEED_ADDRESS, this.wallet ); } diff --git a/packages/ui/.env b/packages/ui/.env index 058cc778..2480fd00 100644 --- a/packages/ui/.env +++ b/packages/ui/.env @@ -4,8 +4,8 @@ PUBLIC_GLOBAL_ANONYMOUS_FEED_ADDRESS=0x0bd11870C1EBA2e4c316fb580253abAbfBF060A0 PUBLIC_RELAYER_URL=https://relayer.kurate.apyos.dev # development -#PUBLIC_PROVIDER=http://127.0.0.1:7545 -#PUBLIC_GLOBAL_ANONYMOUS_FEED_ADDRESS=0x3bDf5135574a26EEF8648e5a51e6a9a9d87eea6E +#PUBLIC_PROVIDER=http://localhost:8545 +#PUBLIC_GLOBAL_ANONYMOUS_FEED_ADDRESS=0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0 #PUBLIC_RELAYER_URL=http://localhost:3000 PUBLIC_ADAPTER=firebase