├── web
├── src
│ ├── vite-env.d.ts
│ ├── App.css
│ ├── components
│ │ ├── ColorModeSwitch.tsx
│ │ ├── Login.tsx
│ │ ├── Avatars.tsx
│ │ ├── SearchResults.tsx
│ │ ├── Contribution.tsx
│ │ └── ProjectCard.tsx
│ ├── pages
│ │ └── LandingPage
│ │ │ ├── LandingPage.tsx
│ │ │ ├── Banner.tsx
│ │ │ └── HeroComponent.tsx
│ ├── main.tsx
│ ├── App.tsx
│ ├── index.css
│ ├── assets
│ │ └── LogoIcon.tsx
│ ├── types
│ │ └── snarkjs.d.ts
│ ├── Layout.tsx
│ ├── context
│ │ ├── ProjectPageContext.tsx
│ │ └── StateContext.tsx
│ └── helpers
│ │ ├── constants.ts
│ │ ├── functions.ts
│ │ ├── firebase.ts
│ │ └── interfaces.ts
├── .firebaserc
├── vite.config.ts
├── tsconfig.node.json
├── firebase.json
├── .gitignore
├── .eslintrc.cjs
├── index.html
├── tsconfig.json
├── public
│ └── vite.svg
├── package.json
├── .default.env
└── README.md
├── .github
├── .lighthouserc.json
├── workflows
│ ├── check-ceremonies.yml
│ ├── validate-ceremony-setup.yml
│ ├── check-pr-files.yml
│ ├── validate-directory-and-prefix.yml
│ ├── deploy-hosting.yml
│ ├── lighthouse-pr-audit.yml
│ └── setup-ceremony.yml
├── actions
│ └── check-ceremonies.js
├── ISSUE_TEMPLATE
│ └── bug_report.md
├── pull_request_template.md
└── PULL_REQUEST_TEMPLATE
│ └── pull_request_template.md
├── LICENSE
├── SECURITY.md
├── README.md
├── CONTRIBUTING.md
├── CODE_OF_CONDUCT.md
└── ceremonies
├── README.md
├── setup_ceremony_config.sh
└── rln-trusted-setup-ceremony
└── p0tionConfig.json
/web/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/web/src/App.css:
--------------------------------------------------------------------------------
1 | #root {
2 | max-width: 100vw;
3 | margin: 0 auto;
4 | padding: 2rem;
5 | text-align: center;
6 | }
7 |
--------------------------------------------------------------------------------
/.github/.lighthouserc.json:
--------------------------------------------------------------------------------
1 | {
2 | "ci": {
3 | "collect": {
4 | "settings": {
5 | "preset": "desktop"
6 | }
7 | }
8 | }
9 | }
--------------------------------------------------------------------------------
/web/.firebaserc:
--------------------------------------------------------------------------------
1 | {
2 | "projects": {
3 | "dev": "p0tion-ci-environment",
4 | "staging": "pse-p0tion-staging",
5 | "prod": "pse-p0tion-production"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/web/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import react from '@vitejs/plugin-react'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | })
8 |
--------------------------------------------------------------------------------
/web/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "skipLibCheck": true,
5 | "module": "ESNext",
6 | "moduleResolution": "bundler",
7 | "allowSyntheticDefaultImports": true
8 | },
9 | "include": ["vite.config.ts"]
10 | }
11 |
--------------------------------------------------------------------------------
/web/firebase.json:
--------------------------------------------------------------------------------
1 | {
2 | "hosting": {
3 | "public": "dist",
4 | "ignore": [
5 | "firebase.json",
6 | "**/.*",
7 | "**/node_modules/**"
8 | ],
9 | "rewrites": [
10 | {
11 | "source": "**",
12 | "destination": "/index.html"
13 | }
14 | ]
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/web/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
26 | # Environment
27 | .env
28 |
29 | # Firebase
30 | .firebase
31 |
--------------------------------------------------------------------------------
/web/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: { browser: true, es2020: true },
3 | extends: [
4 | 'eslint:recommended',
5 | 'plugin:@typescript-eslint/recommended',
6 | 'plugin:react-hooks/recommended',
7 | ],
8 | parser: '@typescript-eslint/parser',
9 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
10 | plugins: ['react-refresh'],
11 | rules: {
12 | 'react-refresh/only-export-components': 'warn',
13 | },
14 | }
15 |
--------------------------------------------------------------------------------
/web/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Definitely Setup
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/web/src/components/ColorModeSwitch.tsx:
--------------------------------------------------------------------------------
1 | import { IconButton, useColorMode } from "@chakra-ui/react";
2 | import { FaSun, FaMoon } from "react-icons/fa";
3 |
4 | export const ColorModeSwitch: React.FC = () => {
5 | const { colorMode, toggleColorMode } = useColorMode();
6 | return (
7 | : }
12 | onClick={toggleColorMode}
13 | />
14 | );
15 | };
16 |
--------------------------------------------------------------------------------
/.github/workflows/check-ceremonies.yml:
--------------------------------------------------------------------------------
1 | name: Check Ceremonies
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 |
8 | jobs:
9 | check-ceremonies:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: Checkout repo
13 | uses: actions/checkout@v2
14 |
15 | - name: Install Node.js
16 | uses: actions/setup-node@v2
17 | with:
18 | node-version: '14'
19 |
20 | - name: Install Dependencies
21 | run: npm ci
22 |
23 | - name: Check ceremonies
24 | run: node .github/actions/check-ceremonies.js
25 |
--------------------------------------------------------------------------------
/web/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2022",
4 | "useDefineForClassFields": true,
5 | "lib": ["ES2022", "DOM", "DOM.Iterable"],
6 | "module": "ESNext",
7 | "skipLibCheck": true,
8 |
9 | /* Bundler mode */
10 | "moduleResolution": "bundler",
11 | "allowImportingTsExtensions": true,
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "noEmit": true,
15 | "jsx": "react-jsx",
16 |
17 | /* Linting */
18 | "strict": true,
19 | "noUnusedLocals": true,
20 | "noUnusedParameters": true,
21 | "noFallthroughCasesInSwitch": true
22 | },
23 | "include": ["src"],
24 | "references": [{ "path": "./tsconfig.node.json" }]
25 | }
26 |
--------------------------------------------------------------------------------
/.github/actions/check-ceremonies.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 |
4 | const ceremoniesPath = path.join(__dirname, '..', '..', 'ceremonies');
5 |
6 | fs.readdir(ceremoniesPath, (err, folders) => {
7 | if (err) {
8 | console.error('Failed to list ceremonies:', err);
9 | process.exit(1);
10 | }
11 |
12 | folders.forEach(folder => {
13 | const configPath = path.join(ceremoniesPath, folder, 'p0tionConfig.json');
14 |
15 | fs.readFile(configPath, 'utf-8', (err, data) => {
16 | if (err) {
17 | console.error(`Failed to read p0tionConfig.json for ceremony "${folder}":`, err);
18 | return;
19 | }
20 |
21 | console.log(`p0tionConfig.json for ceremony "${folder}":\n`, data);
22 | //TODO: call CLI or page devs
23 | });
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/web/src/components/Login.tsx:
--------------------------------------------------------------------------------
1 | import { useContext } from "react"
2 | import { signInWithGitHub, signOutWithGitHub } from "../helpers/p0tion"
3 | import { StateContext } from "../context/StateContext";
4 |
5 | /**
6 | * Component for GitHub login
7 | */
8 | export const Login = () => {
9 |
10 | const { user, setUser } = useContext(StateContext);
11 |
12 | const login = async () => {
13 | const user = await signInWithGitHub()
14 | if (setUser) setUser(user)
15 | }
16 |
17 | const handleSignOut = async () => {
18 | await signOutWithGitHub()
19 | if (setUser) setUser(undefined)
20 | }
21 |
22 | return (
23 | <>
24 |
25 | {user ? "Logout" : "Login"}
26 |
27 | >
28 | )
29 | }
30 |
--------------------------------------------------------------------------------
/web/src/pages/LandingPage/LandingPage.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from "react";
2 | import { StateContext } from "../../context/StateContext";
3 | import { HeroComponent } from "./HeroComponent";
4 | import SearchResults from "../../components/SearchResults";
5 | import { CircularProgress } from "@chakra-ui/react";
6 |
7 | const LandingPage: React.FC = () => {
8 | const { projects, waitingQueue, search, loading } = useContext(StateContext);
9 |
10 | if (loading) {
11 | return (
12 | Fetching ceremonies...
13 | )
14 | }
15 |
16 | const render = search ? (
17 |
18 | ) : (
19 |
23 | );
24 | return render;
25 | };
26 |
27 | export default LandingPage;
28 |
--------------------------------------------------------------------------------
/web/src/components/Avatars.tsx:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import { Avatar, Box } from "@chakra-ui/react"
3 |
4 | interface ScrollingAvatarsProps {
5 | images?: string[]
6 | }
7 |
8 | /**
9 | * Display the participants Avatars in a scrolling list
10 | * @param {ScrollingAvatarsProps} - the images to show
11 | */
12 | const ScrollingAvatars: React.FC = ({ images }) => {
13 | return (
14 |
23 | {images && images.length > 0 && images.map((image: string, index: any) => (
24 |
29 | ))}
30 |
31 | )
32 | }
33 |
34 | export default ScrollingAvatars
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Smartphone (please complete the following information):**
32 | - Device: [e.g. iPhone6]
33 | - OS: [e.g. iOS8.1]
34 | - Browser [e.g. stock browser, safari]
35 | - Version [e.g. 22]
36 |
37 | **Additional context**
38 | Add any other context about the problem here.
39 |
--------------------------------------------------------------------------------
/web/src/main.tsx:
--------------------------------------------------------------------------------
1 | import ReactDOM from "react-dom/client";
2 | import App from "./App.tsx";
3 | import "./index.css";
4 | import { ChakraProvider, extendTheme,ColorModeScript } from "@chakra-ui/react";
5 |
6 | import '@fontsource/poppins/700.css'
7 | import '@fontsource/poppins/600.css'
8 | import '@fontsource/poppins/400.css'
9 | import '@fontsource/poppins/200.css'
10 |
11 | //@ts-ignore
12 | if (typeof global === 'undefined') {
13 | //@ts-ignore
14 | window.global = window;
15 | }
16 |
17 |
18 | const colors = {
19 | brand: {
20 | 900: "#000000",
21 | 800: "#000000",
22 | 700: "#000000"
23 | }
24 | };
25 |
26 | const theme = extendTheme({
27 | fonts: {
28 | body: "Poppins",
29 | heading: "Sofia Sans"
30 | },
31 | colors,
32 | config: {
33 | initialColorMode: "dark",
34 | useSystemColorMode: false,
35 | },
36 | });
37 |
38 | ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
39 | <>
40 |
41 |
42 |
43 |
44 | >
45 | );
46 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Ethereum Foundation
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/web/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
2 | import Layout from "./Layout";
3 | import { StateProvider } from "./context/StateContext";
4 | import { ProjectPageProvider } from "./context/ProjectPageContext";
5 | import ProjectPage from "./pages/ProjectPage";
6 | import LandingPage from "./pages/LandingPage/LandingPage";
7 |
8 | function App() {
9 | return (
10 |
11 |
12 |
13 | }>
14 | {/* Render the SearchResults component for the index route */}
15 | } />
16 | {/* Render the ProjectPage component for the "projects/:title" route */}
17 |
21 |
22 |
23 | }
24 | />
25 |
26 |
27 |
28 |
29 | );
30 | }
31 |
32 | export default App;
33 |
--------------------------------------------------------------------------------
/.github/workflows/validate-ceremony-setup.yml:
--------------------------------------------------------------------------------
1 | # Run for each PR that changes files in the ceremonies directory
2 | name: Check PR with new ceremony files
3 |
4 | on:
5 | pull_request:
6 | paths:
7 | - 'ceremonies/**'
8 |
9 | jobs:
10 | check:
11 | runs-on: ubuntu-latest
12 | if: endsWith(github.head_ref, '-ceremony')
13 |
14 | steps:
15 | - name: Checkout code
16 | uses: actions/checkout@v2
17 |
18 | - name: Setup Node.js
19 | uses: actions/setup-node@v2
20 | with:
21 | node-version: '16'
22 |
23 | - name: Install p0tion
24 | run: npm install -g @p0tion/phase2cli
25 |
26 | # we want to validate that the p0tionConfig.json file is valid
27 | - name: Run npm script and check output
28 | run: |
29 | echo $GITHUB_HEAD_REF
30 | result=$(phase2cli validate --template ./ceremonies/$(echo $GITHUB_HEAD_REF | tr '[:upper:]' '[:lower:]')/p0tionConfig.json)
31 | if [[ "$result" != "true" ]]; then
32 | echo "NPM script returned false."
33 | exit 1
34 | fi
35 | echo "The ceremony files are valid and the circuits have < 1M constraints"
36 | env:
37 | # read only token when the PR originates from a fork
38 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
--------------------------------------------------------------------------------
/.github/workflows/check-pr-files.yml:
--------------------------------------------------------------------------------
1 | # check that a PR modified the correct files and nothing more
2 | name: Check PR Files
3 |
4 | on:
5 | pull_request:
6 | paths:
7 | - 'ceremonies/**'
8 |
9 | jobs:
10 | check-files:
11 | runs-on: ubuntu-latest
12 | if: endsWith(github.head_ref, '-ceremony')
13 |
14 | steps:
15 | - name: Checkout code
16 | uses: actions/checkout@v2
17 |
18 | - name: Get changed files
19 | id: get-changed-files
20 | uses: jitterbit/get-changed-files@v1
21 | with:
22 | format: 'csv'
23 |
24 | - name: Validate files
25 | run: |
26 | changed_files="${{ steps.get-changed-files.outputs.all }}"
27 | IFS=', ' read -r -a files <<< "$changed_files"
28 | config_found=false
29 | for file in "${files[@]}"; do
30 | if [[ $file == ceremonies/$(echo $GITHUB_HEAD_REF | tr '[:upper:]' '[:lower:]')/p0tionConfig.json ]]; then
31 | config_found=true
32 | else
33 | echo "Invalid file detected: $file"
34 | exit 1
35 | fi
36 | done
37 |
38 | if [[ $config_found = false ]]; then
39 | echo "No p0tionConfig.json file found in PR."
40 | exit 1
41 | fi
42 |
43 | echo "p0tionConfig.json file is present and no other files were changed."
44 |
--------------------------------------------------------------------------------
/.github/workflows/validate-directory-and-prefix.yml:
--------------------------------------------------------------------------------
1 | # We need the directory of the ceremony to match the prefix of the ceremony.
2 | name: Validate Directory name and prefix
3 |
4 | on:
5 | pull_request:
6 | paths:
7 | - 'ceremonies/**'
8 |
9 | jobs:
10 | verify:
11 | runs-on: ubuntu-latest
12 | if: endsWith(github.head_ref, '-ceremony')
13 |
14 | steps:
15 | - uses: actions/checkout@v2
16 |
17 | - name: Set up Node.js
18 | uses: actions/setup-node@v2
19 | with:
20 | node-version: '16'
21 |
22 | - name: Install jq
23 | run: sudo apt-get install jq -y
24 |
25 | - name: Read JSON and get title
26 | run: |
27 | title=$(jq -r '.title' ./ceremonies/$(echo $GITHUB_HEAD_REF | tr '[:upper:]' '[:lower:]')/p0tionConfig.json)
28 | echo "Title: $title"
29 | echo "TITLE=$title" >> $GITHUB_ENV
30 |
31 | - name: Transform and compare
32 | run: |
33 | prefix=$(echo "${TITLE}" | tr '[:upper:]' '[:lower:]' | tr ' ' '-' | tr -dc '[:alnum:]-\n\r')
34 | echo "Converted Prefix: $prefix"
35 | DIR_NAME="$(echo $GITHUB_HEAD_REF | tr '[:upper:]' '[:lower:]')"
36 | echo "Dir name: $DIR_NAME"
37 | if [ "$prefix" != $DIR_NAME ]; then
38 | echo "Error: The ceremony artifacts directory name and ceremony prefix do not match!"
39 | exit 1
40 | else
41 | echo "Directory name and ceremony prefix match."
42 | fi
--------------------------------------------------------------------------------
/web/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vite-project",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite --port 8080",
8 | "build": "tsc && vite build",
9 | "deploy:dev": "firebase deploy --only hosting --project dev",
10 | "deploy:staging": "firebase deploy --only hosting --project staging",
11 | "deploy:prod": "firebase deploy --only hosting --project prod",
12 | "lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
13 | "preview": "vite preview"
14 | },
15 | "dependencies": {
16 | "@chakra-ui/react": "^2.7.1",
17 | "@fontsource/poppins": "^5.0.5",
18 | "@octokit/auth-oauth-device": "^6.0.0",
19 | "@octokit/request": "^8.1.1",
20 | "firebase": "^9.23.0",
21 | "randomf": "^0.0.3",
22 | "react": "^18.2.0",
23 | "react-dom": "^18.2.0",
24 | "react-icons": "^4.10.1",
25 | "react-joyride": "^2.5.4",
26 | "react-router-dom": "^6.14.1",
27 | "snarkjs": "^0.7.0",
28 | "types.js": "link:@octokit/auth-oauth-device/dist-types/types.js",
29 | "zod": "^3.21.4"
30 | },
31 | "devDependencies": {
32 | "@types/react": "^18.0.37",
33 | "@types/react-dom": "^18.0.11",
34 | "@typescript-eslint/eslint-plugin": "^5.59.0",
35 | "@typescript-eslint/parser": "^5.59.0",
36 | "@vitejs/plugin-react": "^4.0.0",
37 | "eslint": "^8.38.0",
38 | "eslint-plugin-react-hooks": "^4.6.0",
39 | "eslint-plugin-react-refresh": "^0.3.4",
40 | "process": "^0.11.10",
41 | "typescript": "^5.0.2",
42 | "vite": "^4.3.9"
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/web/.default.env:
--------------------------------------------------------------------------------
1 | # The Firebase Application API key for making request against the services.
2 | # nb. this is going to be auto-generated when creating a new application.
3 | VITE_FIREBASE_API_KEY="YOUR-FIREBASE-API-KEY"
4 | # The URL to Firebase Authentication service (should point to default).
5 | # nb. this is going to be auto-generated when creating a new application.
6 | VITE_FIREBASE_AUTH_DOMAIN="YOUR-FIREBASE-AUTH-DOMAIN"
7 | # The Firebase Application project id (should match with application name).
8 | VITE_FIREBASE_PROJECT_ID="YOUR-FIREBASE-PROJECT-ID"
9 | # The Firebase unique message sender identifier (to recognize the application user).
10 | # nb. this is going to be auto-generated when creating a new application.
11 | VITE_FIREBASE_MESSAGING_SENDER_ID="YOUR-FIREBASE-MESSAGING-SENDER-ID"
12 | # The Firebase unique identifier for your application.
13 | # nb. this is going to be auto-generated when creating a new application.
14 | VITE_FIREBASE_APP_ID="YOUR-FIREBASE-APP-ID"
15 | # The AWS region where your buckets are located.
16 | VITE_AWS_REGION="YOUR-AWS-REGION"
17 | # The postfix used to create S3 buckets for storing the ceremonies artifacts.
18 | VITE_CONFIG_CEREMONY_BUCKET_POSTFIX="YOUR-CEREMONY-BUCKET-POSTFIX"
19 | # The GitHub auth id
20 | VITE_GITHUB_AUTH_ID="YOUR_GITHUB_AUTH_ID"
21 | # the verify contribution endpoint
22 | VITE_FIREBASE_CF_URL_VERIFY_CONTRIBUTION="YOUR_VERIFY_CONTRIBUTION_ENDPOINT"
23 | # GitHub reputation checks
24 | VITE_GITHUB_FOLLOWERS="YOUR_GITHUB_FOLLOWERS_THRESHOLD"
25 | VITE_GITHUB_FOLLOWING="YOUR_GITHUB_FOLLOWING_THRESHOLD"
26 | VITE_GITHUB_REPOS="YOUR_PUBLIC_GITHUB_REPOS_THRESHOLD"
--------------------------------------------------------------------------------
/web/src/components/SearchResults.tsx:
--------------------------------------------------------------------------------
1 | // SearchResults.tsx
2 |
3 | import { Box, HStack, SimpleGrid, Text, VStack } from "@chakra-ui/react";
4 | import { StateContext } from "../context/StateContext";
5 | import { ProjectCard } from "./ProjectCard";
6 | import { useContext, useEffect } from "react";
7 |
8 | export default function SearchResults() {
9 | const { projects, search } = useContext(StateContext);
10 |
11 | useEffect(() => console.log("search", search), [search]);
12 |
13 | // console.log("search for", search)
14 | const results = projects.filter((project) =>
15 | project.ceremony.data.title.toLowerCase().includes(search.toLowerCase())
16 | );
17 |
18 | return (
19 |
20 |
21 | {results.length} results found
22 |
23 |
24 |
25 |
26 |
27 | {results.length > 0 ? (
28 |
29 | {results.map((project, index) => (
30 | // Render ProjectCard for each project in the results
31 |
32 | ))}
33 |
34 | ) : (
35 | No results found.
36 | )}
37 |
38 |
39 |
40 | );
41 | }
42 |
--------------------------------------------------------------------------------
/web/src/index.css:
--------------------------------------------------------------------------------
1 | @import url("https://fonts.googleapis.com/css2?family=Sofia+Sans:wght@200;300;400;500;700;800&display=swap");
2 |
3 | :root {
4 | font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
5 | line-height: 1.5;
6 | font-weight: 400;
7 |
8 | color-scheme: light dark;
9 | color: rgba(255, 255, 255, 0.87);
10 | background-color: #000000;
11 | padding: 0%;
12 | font-synthesis: none;
13 | text-rendering: optimizeLegibility;
14 | -webkit-font-smoothing: antialiased;
15 | -moz-osx-font-smoothing: grayscale;
16 | -webkit-text-size-adjust: 100%;
17 | }
18 |
19 | a {
20 | font-weight: 500;
21 | color: #646cff;
22 | text-decoration: inherit;
23 | }
24 | a:hover {
25 | color: #535bf2;
26 | }
27 |
28 | body {
29 | margin: 0;
30 | }
31 |
32 | .r-kemksi {
33 | background-color: #646cff;
34 | }
35 |
36 | /* button {
37 | border-radius: 8px;
38 | border: 1px solid transparent;
39 | padding: 0.6em 1.2em;
40 | font-size: 1em;
41 | font-weight: 500;
42 | font-family: inherit;
43 | background-color: #1a1a1a;
44 | cursor: pointer;
45 | transition: border-color 0.25s;
46 | } */
47 | /* button:hover {
48 | border-color: #646cff;
49 | } */
50 | /* button:focus,
51 | button:focus-visible {
52 | outline: 0px auto -webkit-focus-ring-color;
53 | } */
54 |
55 | @media (prefers-color-scheme: light) {
56 | :root {
57 | color: #213547;
58 | background-color: #ffffff;
59 | }
60 | a:hover {
61 | color: #747bff;
62 | }
63 | button {
64 | background-color: #f9f9f9;
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/.github/workflows/deploy-hosting.yml:
--------------------------------------------------------------------------------
1 | # Deploy to Firebase hosting
2 | name: Deploy to Firebase - deploy on push
3 |
4 | # only on selected branches
5 | on:
6 | push:
7 | branches: [main, dev, staging]
8 |
9 | jobs:
10 | build_and_deploy_to_firebase_hosting:
11 | defaults:
12 | run:
13 | working-directory: ./web
14 | runs-on: ubuntu-latest
15 | environment:
16 | ${{ (github.ref == 'refs/heads/main' && 'p0tion-production') ||
17 | (github.ref == 'refs/heads/staging' && 'p0tion-staging') ||
18 | (github.ref == 'refs/heads/dev' && 'p0tion-development') }}
19 | steps:
20 | - uses: actions/checkout@v3
21 |
22 | - name: Install deps and build
23 | run: |
24 | echo "${{ secrets.ENV_FILE }}" > ./.env
25 | npm install -g pnpm
26 | npm install -g firebase-tools
27 | pnpm install
28 | pnpm build
29 |
30 | - name: Write serviceAccountKey to a JSON file
31 | uses: jsdaniell/create-json@v1.2.1
32 | with:
33 | name: "./web/serviceAccountKey.json"
34 | json: ${{ secrets.FIREBASE_SERVICE_ACCOUNT }}
35 |
36 | # Conditional deployment based on the target branch
37 | - name: Deploy
38 | run: |
39 | if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then
40 | pnpm deploy:prod
41 | elif [[ "${{ github.ref }}" == "refs/heads/dev" ]]; then
42 | pnpm deploy:dev
43 | elif [[ "${{ github.ref }}" == "refs/heads/staging" ]]; then
44 | pnpm deploy:staging
45 | fi
46 | env:
47 | GOOGLE_APPLICATION_CREDENTIALS: ./serviceAccountKey.json
48 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | Security vulnerabilities should be disclosed to the project maintainers through [Discord], or alternatively via telegram.
4 |
5 | [Discord]: https://discord.com/invite/bTdZfpc69U
6 |
7 | ## Bug Bounty
8 |
9 | At this time there is no active bug bounty for DefinitelySetup.
10 |
11 | ## Security Patches
12 |
13 | Security vulnerabilities will be patched as soon as responsibly possible, and published as an advisory on this repository (see [advisories]) and on the affected npm packages.
14 |
15 | [advisories]: https://github.com/privacy-scaling-explorations/DefinitelySetup/security/advisories
16 |
17 | ### Supported Versions
18 |
19 | Security patches will be released for the latest minor of a given major release. For example, if an issue is found in versions >=4.6.0 and the latest is 4.8.0, the patch will be released only in version 4.8.1.
20 |
21 | Only critical severity bug fixes will be backported to past major releases.
22 |
23 | | Version | Critical security fixes | Other security fixes |
24 | | ------- | ----------------------- | -------------------- |
25 | | 4.x | :white_check_mark: | :white_check_mark: |
26 | | 3.4 | :white_check_mark: | :x: |
27 | | 2.5 | :white_check_mark: | :x: |
28 | | < 2.0 | :x: | :x: |
29 |
30 | ## Legal
31 |
32 | You are solely responsible for any use of DefinitelySetup and you assume all risks associated with any such use. This Security Policy in no way evidences or represents an on-going duty by any contributor, including the Ethereum Foundation (EF), to correct any flaws or alert you to all or any of the potential risks of utilizing the project.
--------------------------------------------------------------------------------
/web/src/assets/LogoIcon.tsx:
--------------------------------------------------------------------------------
1 | import { Icon, IconProps, useColorMode } from "@chakra-ui/react";
2 |
3 | export const LogoIcon: React.FC = ({ boxSize, color, ...props }) => {
4 | const { colorMode } = useColorMode();
5 | return (
6 |
7 |
12 |
13 | );
14 | };
15 |
--------------------------------------------------------------------------------
/web/src/types/snarkjs.d.ts:
--------------------------------------------------------------------------------
1 | /** Declaration file generated by dts-gen */
2 |
3 | declare module "snarkjs" {
4 | export = snarkjs
5 |
6 | declare const snarkjs: {
7 | fflonk: {
8 | exportSolidityCallData: any
9 | exportSolidityVerifier: any
10 | fullProve: any
11 | prove: any
12 | setup: any
13 | verify: any
14 | }
15 | groth16: {
16 | exportSolidityCallData: any
17 | fullProve: any
18 | prove: any
19 | verify: any
20 | }
21 | plonk: {
22 | exportSolidityCallData: any
23 | fullProve: any
24 | prove: any
25 | setup: any
26 | verify: any
27 | }
28 | powersOfTau: {
29 | beacon: any
30 | challengeContribute: any
31 | contribute: any
32 | convert: any
33 | exportChallenge: any
34 | exportJson: any
35 | importResponse: any
36 | newAccumulator: any
37 | preparePhase2: any
38 | truncate: any
39 | verify: any
40 | }
41 | r1cs: {
42 | exportJson: any
43 | info: any
44 | print: any
45 | }
46 | wtns: {
47 | calculate: any
48 | check: any
49 | debug: any
50 | exportJson: any
51 | }
52 | zKey: {
53 | beacon: any
54 | bellmanContribute: any
55 | contribute: any
56 | exportBellman: any
57 | exportJson: any
58 | exportSolidityVerifier: any
59 | exportVerificationKey: any
60 | importBellman: any
61 | newZKey: any
62 | verifyFromInit: any
63 | verifyFromR1cs: any
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/web/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | DefinitelySetup frontend 🌵
4 |
5 | The frontend for DefinitelySetup.
6 |
7 |
8 |
23 |
24 | | This folder contains the frontend code for DefinitelySetup. DefinitelySetup is a product designed to run and monitor Trusted Setup ceremonies for groth16 based snarks. |
25 | | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
26 |
27 |
28 | ## 🛠 Installation
29 |
30 | ### pnpm
31 |
32 | First, ensure **pnpm** is installed on your machine.
33 |
34 | ```bash
35 | npm install -g pnpm
36 | ```
37 |
38 | Then, install required dependencies with:
39 |
40 | ```bash
41 | pnpm install
42 | ```
43 |
44 | ## 📜 Usage
45 |
46 | ### Local Development
47 |
48 | **Prerequisities**
49 |
50 | - Node.js version 16.0 or higher.
51 | - pnpm version 8.6.7 or higher.
52 |
53 | Copy the `.default.env` file to `.env`:
54 |
55 | ```bash
56 | cp .env.default .env
57 | ```
58 |
59 | And add your environment variables.
60 |
61 | ### Build
62 |
63 | Run:
64 |
65 | ```bash
66 | pnpm build
67 | ```
68 |
69 | ### Start development server
70 |
71 | ```bash
72 | pnpm dev
73 | ```
--------------------------------------------------------------------------------
/web/src/pages/LandingPage/Banner.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from "react";
2 | import { Box, Text, HStack } from "@chakra-ui/react";
3 |
4 | // Define the type for banner item props
5 | interface BannerItemProps {
6 | imageUrl: string;
7 | altText: string;
8 | bannerText?: string;
9 | }
10 |
11 | // Define the type for scroll banner props
12 | interface ScrollBannerProps {
13 | imageArray: BannerItemProps[];
14 | }
15 |
16 | // Create BannerItem component
17 | const BannerItem: FC = ({ bannerText }) => (
18 |
30 |
31 | {bannerText}
32 |
33 |
34 | );
35 |
36 |
37 | // Create ScrollBanner component
38 | const ScrollBanner: FC = ({ imageArray }) => {
39 | // Create a merged array to replicate the infinite loop
40 | const loopArray = [...imageArray, ...imageArray];
41 |
42 | return (
43 |
55 |
73 |
74 | {loopArray.map((item, index) => (
75 |
76 | ))}
77 |
78 |
79 |
80 | );
81 | };
82 |
83 | export { ScrollBanner, BannerItem };
84 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | DefinitelySetup Open BETA
4 |
5 | The repository for high quality Trusted setups for groth16 based SNARKS.
6 |
7 |
8 | ## Introduction
9 | DefinitelySetup is a product designed to run Trusted Setup ceremonies for Groth16 based snarks. This document provides step-by-step guide on how to utilize DefinitelySetup.
10 |
11 | ### Instructions for DefinitelySetup
12 |
13 | You will generally need to prepare the p0tion infrastructure. See the [p0tion repo](https://github.com/privacy-scaling-explorations/p0tion) for details. A ceremony setup can be initiated either using GitHub actions, triggered by a PR, or by a command in the p0tion CLI (with coordinator privileges).
14 |
15 | Steps for using DefinitelySetup to run your own ceremony:
16 |
17 | - Prepare your files: Before anything else, you will need to prepare your R1CS, wasm, and ceremony config files for uploading to the DefinitelySetup repository.
18 |
19 | - Create a Pull Request: Once your files are ready, you'll need to create a pull request (PR) in the DefinitelySetup repository. Use the provided PR template to guide you in filling out the necessary information.
20 |
21 | - Approval and Merging: If your circuit's constraint size is less than 1M, your PR will be automatically approved and merged.
22 |
23 | - Starting the Ceremony: Once your PR is merged, the ceremony will commence. You and other users will be able to see the ceremony on the DefinitelySetup website.
24 |
25 | - Contribute via the CLI: During the ceremony, you can use the provided CLI to contribute to the process. Detailed instructions for using the CLI will be provided on the DefinitelySetup website. The browser can also be used if the circuit size is sufficiently small.
26 |
27 | - Download Finalized Zkeys: After the ceremony concludes, the finalized zkeys will be made available for download. Ensure to check back frequently for updates or await notifications regarding completion.
28 |
29 | Please note that these are the fundamental steps and additional details or steps may be necessary based on the specifics of your project or ceremony configuration.
30 |
31 | Remember, DefinitelySetup is designed to simplify and streamline the process of running Trusted Setup ceremonies, and we're here to support you through each step of the process.
32 |
--------------------------------------------------------------------------------
/web/src/pages/LandingPage/HeroComponent.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Box,
3 | Text,
4 | Heading,
5 | VStack,
6 | HStack,
7 | Spacer,
8 | SimpleGrid
9 | } from "@chakra-ui/react";
10 | import { HeroComponentProps } from "../../helpers/interfaces";
11 | import { ProjectCard } from "../../components/ProjectCard";
12 | import { ScrollBanner } from "./Banner";
13 |
14 | export function HeroComponent({ projects, waitingQueue }: HeroComponentProps) {
15 | const bannerImages: any[] = []
16 | const sortedProjects = projects.sort((a, b) => b.ceremony.data.endDate - a.ceremony.data.endDate);
17 |
18 | for (const queue of waitingQueue) {
19 | bannerImages.push({
20 | imageUrl: "https://res.cloudinary.com/pse-qf-maci/image/upload/v1690230945/Banner_qb6zlf.png",
21 | altText: queue.ceremonyName,
22 | bannerText: `${queue.ceremonyName} Waiting Queue for Circuit ${queue.circuitName}: ${
23 | queue.waitingQueue ?? "no circuits!"
24 | } `
25 | })
26 | }
27 |
28 | return (
29 | <>
30 |
31 |
32 |
33 |
34 |
42 |
43 |
44 | Ceremonies
45 |
46 |
47 |
48 |
54 |
55 | {projects.length > 0 ? (
56 |
57 | {sortedProjects.map((project, index) => (
58 |
59 | ))}
60 |
61 | ) : (
62 | No ceremonies live yet!
63 | )}
64 |
65 |
66 |
67 | >
68 | );
69 | }
--------------------------------------------------------------------------------
/web/src/components/Contribution.tsx:
--------------------------------------------------------------------------------
1 | import { Box, Button, Spinner, Text } from "@chakra-ui/react"
2 | import React, { useState } from "react"
3 | import { contribute } from "../helpers/p0tion"
4 |
5 | /**
6 | * Components that allows to contribute to a ceremony on the browser
7 | * @param props
8 | * @returns
9 | */
10 | export const Contribution = (props: any): React.JSX.Element => {
11 | const [ status, setStatus ] = useState("")
12 | const [ isLoading, setIsLoading ] = useState(false)
13 | const [ attestationLink, setAttestationLink ] = useState("")
14 |
15 | const ceremonyId = props.ceremonyId
16 |
17 | // function that is passed to the contribute function to update the status of the contribution
18 | const handleChanges = (message: string, loading?: boolean, attestationLink?: string) => {
19 | setStatus(message)
20 | if (typeof loading === 'boolean') setIsLoading(loading)
21 | if (typeof attestationLink === 'string') {
22 | setAttestationLink(attestationLink)
23 | }
24 | }
25 |
26 | return (
27 | <>
28 |
29 |
30 | {
31 | status === "" &&
32 | Press contribute to join the ceremony
33 | }
34 | If contributing on your phone, please do not leave the current browser tab
35 |
36 |
37 | {status}
38 | {isLoading && }
39 |
40 | {
41 | status === "" &&
42 | contribute(ceremonyId, handleChanges)}>
43 | Contribute
44 |
45 | }
46 | {
47 | attestationLink &&
48 |
49 |
50 | Attestation
51 |
52 |
53 | }
54 |
55 | >
56 | )
57 | }
--------------------------------------------------------------------------------
/web/src/components/ProjectCard.tsx:
--------------------------------------------------------------------------------
1 | import { Link } from "react-router-dom";
2 |
3 | import {
4 | VStack,
5 | HStack,
6 | Text,
7 | Badge,
8 | Heading,
9 | Breadcrumb,
10 | BreadcrumbItem
11 | } from "@chakra-ui/react";
12 |
13 | import { Project } from "../helpers/interfaces";
14 |
15 |
16 | interface ProjectCardProps {
17 | project: Project;
18 | }
19 |
20 | export function ProjectCard({ project }: ProjectCardProps) {
21 | // Format a date to the form "mm.dd.yy"
22 | const formatDate = (date: Date): string =>
23 | `${String(date.getMonth() + 1).padStart(2, "0")}.${String(date.getDate()).padStart(2, "0")}.${String(date.getFullYear()).slice(-2)}`;
24 |
25 | // Return a truncated string with the start and end, and an ellipsis in the middle
26 | const truncateString = (str: string, numCharacters = 5): string =>
27 | str.length <= numCharacters * 2 ? str : `${str.slice(0, numCharacters)}...${str.slice(-numCharacters)}`;
28 |
29 | // Get a human-readable string indicating how far in the future or past a date is
30 | const getTimeDifference = (date: Date): string => {
31 | const currentDate = new Date();
32 | const differenceInTime = date.getTime() - currentDate.getTime();
33 | const differenceInDays = Math.round(differenceInTime / (1000 * 3600 * 24));
34 |
35 | if (differenceInDays < 0) return `${Math.abs(differenceInDays)} days ago`;
36 | if (differenceInDays > 0) return `${differenceInDays} days from now`;
37 | return "Today";
38 | };
39 |
40 | return (
41 |
42 |
43 |
44 | {project.ceremony.data.title}
45 | {project.ceremony.data.description}
46 |
47 |
48 |
49 |
56 |
57 | {project.ceremony.data.timeoutMechanismType ? "Fixed" : "Flexible"}
58 |
59 | Penalty: {project.ceremony.data.penalty}
60 | {project.ceremony.data.state}
61 | {project.ceremony.data.type}
62 | {truncateString(project.ceremony.uid, 5)}
63 |
64 |
65 |
66 |
67 | {formatDate(new Date(project.ceremony.data.startDate))}
68 |
69 |
70 |
71 | Deadline: {getTimeDifference(new Date(project.ceremony.data.endDate))}
72 |
73 |
74 |
75 |
76 |
77 |
78 | );
79 | }
80 |
--------------------------------------------------------------------------------
/.github/workflows/lighthouse-pr-audit.yml:
--------------------------------------------------------------------------------
1 | name: Frontend-Lighthouse
2 |
3 |
4 | on:
5 | pull_request:
6 | paths:
7 | - 'web/**'
8 |
9 | jobs:
10 | build_and_preview:
11 | runs-on: ubuntu-22.04
12 | defaults:
13 | run:
14 | working-directory: ./web
15 |
16 | steps:
17 | - uses: actions/checkout@v3
18 |
19 | - uses: pnpm/action-setup@v2
20 | with:
21 | version: 8
22 |
23 | - uses: actions/setup-node@v3
24 | with:
25 | node-version: '20'
26 | cache: 'pnpm'
27 | cache-dependency-path: './web/pnpm-lock.yaml'
28 |
29 | - name: Install deps and build
30 | run: |
31 | echo "${{ secrets.PREVIEW_ENV_FILE }}" > ./.env
32 |
33 | pnpm install
34 | pnpm build
35 |
36 | - uses: FirebaseExtended/action-hosting-deploy@v0
37 | id: firebase_preview_url
38 | with:
39 | repoToken: "${{ secrets.GITHUB_TOKEN }}"
40 | firebaseServiceAccount: "${{ secrets.PREVIEW_FIREBASE_SERVICE_ACCOUNT }}"
41 | expires: 2d
42 | projectId: pse-p0tion-staging
43 | channelId: "preview-${{ github.sha }}"
44 | entryPoint: ./web/
45 |
46 | - name: '[Mobile] Audit preview URL with Lighthouse'
47 | id: lighthouse_audit_mobile
48 | uses: treosh/lighthouse-ci-action@v10
49 | with:
50 | urls: ${{ steps.firebase_preview_url.outputs.details_url }}
51 | temporaryPublicStorage: true
52 |
53 | - name: '[Desktop] Audit preview URL with Lighthouse'
54 | id: lighthouse_audit_desktop
55 | uses: treosh/lighthouse-ci-action@v10
56 | with:
57 | urls: ${{ steps.firebase_preview_url.outputs.details_url }}
58 | temporaryPublicStorage: true
59 | configPath: './.github/.lighthouserc.json'
60 |
61 | - name: Format lighthouse score
62 | id: format_lighthouse_score
63 | uses: actions/github-script@v3
64 | with:
65 | github-token: ${{secrets.GITHUB_TOKEN}}
66 | script: |
67 | const score = res => res >= 90 ? '🟢' : res >= 50 ? '🟠' : '🔴'
68 | const formatResult = (res) => Math.round((res * 100))
69 |
70 | const desktop_result = ${{ steps.lighthouse_audit_desktop.outputs.manifest }}[0].summary
71 | const desktop_links = ${{ steps.lighthouse_audit_desktop.outputs.links }}
72 | Object.keys(desktop_result).forEach(key => desktop_result[key] = formatResult(desktop_result[key]))
73 |
74 | const mobile_result = ${{ steps.lighthouse_audit_mobile.outputs.manifest }}[0].summary
75 | const mobile_links = ${{ steps.lighthouse_audit_mobile.outputs.links }}
76 | Object.keys(mobile_result).forEach(key => mobile_result[key] = formatResult(mobile_result[key]))
77 |
78 | const comment = [
79 | `⚡️ [(Desktop) Lighthouse report](${Object.values(desktop_links)[0]}) for the changes in this PR:`,
80 | '| Category | Score |',
81 | '| --- | --- |',
82 | `| ${score(desktop_result.performance)} Performance | ${desktop_result.performance} |`,
83 | `| ${score(desktop_result.accessibility)} Accessibility | ${desktop_result.accessibility} |`,
84 | `| ${score(desktop_result['best-practices'])} Best practices | ${desktop_result['best-practices']} |`,
85 | `| ${score(desktop_result.seo)} SEO | ${desktop_result.seo} |`,
86 | `| ${score(desktop_result.pwa)} PWA | ${desktop_result.pwa} |`,
87 | ' ',
88 | `⚡️ [(Mobile) Lighthouse report](${Object.values(mobile_links)[0]}) for the changes in this PR:`,
89 | '| Category | Score |',
90 | '| --- | --- |',
91 | `| ${score(mobile_result.performance)} Performance | ${mobile_result.performance} |`,
92 | `| ${score(mobile_result.accessibility)} Accessibility | ${mobile_result.accessibility} |`,
93 | `| ${score(mobile_result['best-practices'])} Best practices | ${mobile_result['best-practices']} |`,
94 | `| ${score(mobile_result.seo)} SEO | ${mobile_result.seo} |`,
95 | `| ${score(mobile_result.pwa)} PWA | ${mobile_result.pwa} |`,
96 | ' ',
97 | `*Lighthouse ran on [${Object.keys(mobile_links)[0]}](${Object.keys(mobile_links)[0]})*`
98 | ].join('\n')
99 | core.setOutput("comment", comment);
100 |
101 | - name: Add comment to PR
102 | id: comment_to_pr
103 | uses: marocchino/sticky-pull-request-comment@v1
104 | with:
105 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
106 | number: ${{ github.event.issue.number }}
107 | header: lighthouse
108 | message: |
109 | ${{ steps.format_lighthouse_score.outputs.comment }}
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | :tada: Thank you for being interested in contributing to DefinitelySetup! :tada:
4 |
5 | Feel welcome and read the following sections in order to know how to ask questions and how to work on something.
6 |
7 | All members of our community are expected to follow our [Code of Conduct](/CODE_OF_CONDUCT.md). Please make sure you are welcoming and friendly in all of our spaces.
8 |
9 | We're really glad you're reading this, because we need volunteer developers to help this project come to fruition. There is a lot we want to achieve, and this can only be made possible thanks to your support. 👏
10 |
11 | ## Issues
12 |
13 | The best way to contribute to our projects is by opening a [new issue](https://github.com/privacy-scaling-explorations/DefinitelySetup/issues) or tackling one of the issues listed [here](https://github.com/privacy-scaling-explorations/DefinitelySetup/contribute).
14 |
15 | ## Pull Requests
16 |
17 | Pull requests are great if you want to add a feature or fix a bug. Here's a quick guide:
18 |
19 | 1. Fork the repo.
20 |
21 | 2. Run the tests. We only take pull requests with passing tests.
22 |
23 | 3. Add a test for your change. Only refactoring and documentation changes require no new tests.
24 |
25 | 4. Make sure to check out the [Style Guide](/CONTRIBUTING#style-guide) and ensure that your code complies with the rules.
26 |
27 | 5. Make the test pass.
28 |
29 | 6. Commit your changes.
30 |
31 | 7. Push to your fork and submit a pull request on our `dev` branch. Please provide us with some explanation of why you made the changes you made. For new features make sure to explain a standard use case to us.
32 |
33 | ## CI (Github Actions) Tests
34 |
35 | We use GitHub Actions to test each PR before it is merged.
36 |
37 | When you submit your PR (or later change that code), a CI build will automatically be kicked off. A note will be added to the PR, and will indicate the current status of the build.
38 |
39 | ## Style Guide
40 |
41 | ### Code rules
42 |
43 | We always use ESLint and Prettier. To check that your code follows the rules, simply run the npm script `yarn lint`. When committing, `eslint` is run automatically, so you will be required to fix any error before being able to push a commit. We highly recommend to tackle warnings as well.
44 |
45 | ### Commits rules
46 |
47 | For commits it is recommended to use [Conventional Commits](https://www.conventionalcommits.org).
48 |
49 | Don't worry if it looks complicated, after `git add`, `git commit` will make the next steps interactive.
50 |
51 | Each commit message consists of a **header**, a **body** and a **footer**. The **header** has a special format that includes a **type**, a **scope** and a **subject**:
52 |
53 | ():
54 |
55 |
56 |
57 |
58 |
59 | The **header** is mandatory and the **scope** of the header must contain the name of the component you are working on.
60 |
61 | #### Type
62 |
63 | The type must be one of the following:
64 |
65 | - feat: A new feature
66 | - fix: A bug fix
67 | - docs: Documentation only changes
68 | - style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
69 | - refactor: A code change that neither fixes a bug nor adds a feature (improvements of the code structure)
70 | - perf: A code change that improves the performance
71 | - test: Adding missing or correcting existing tests
72 | - build: Changes that affect the build system or external dependencies (example scopes: gulp, npm)
73 | - ci: Changes to CI configuration files and scripts (example scopes: travis, circle)
74 | - chore: Other changes that don't modify src or test files
75 | - revert: Reverts a previous commit
76 |
77 | #### Scope
78 |
79 | The scope should be the name of the feature or package modified (as perceived by the person reading the changelog generated from commit messages).
80 |
81 | #### Subject
82 |
83 | The subject contains a succinct description of the change:
84 |
85 | - Use the imperative, present tense: "change" not "changed" nor "changes"
86 | - Don't capitalize the first letter
87 | - No dot (.) at the end
88 |
89 | #### Body
90 |
91 | Just as in the subject, use the imperative, present tense: "change" not "changed" nor "changes". The body should include the motivation for the change and contrast this with previous behavior.
92 |
93 | ### Branch rules
94 |
95 | - There must be a `main` branch, used only for the releases.
96 | - There must be a `dev` branch, used to merge all the branches under it.
97 | - Avoid long descriptive names for long-lived branches.
98 | - Use kebab-case (no CamelCase).
99 | - Use grouping tokens (words) at the beginning of your branch names (in a similar way to the `type` of commit).
100 | - Define and use short lead tokens to differentiate branches in a way that is meaningful to your workflow.
101 | - Use slashes to separate parts of your branch names.
102 | - Remove branch after merge if it is not important.
103 |
104 | Examples:
105 |
106 | ```bash
107 | git branch -b docs/readme
108 | git branch -b test/a-feature
109 | git branch -b feat/sidebar
110 | git branch -b fix/b-feature
111 | ```
112 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
6 |
7 | We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
8 |
9 | ## Our Standards
10 |
11 | Examples of behavior that contributes to a positive environment for our community include:
12 |
13 | - Demonstrating empathy and kindness toward other people
14 | - Being respectful of differing opinions, viewpoints, and experiences
15 | - Giving and gracefully accepting constructive feedback
16 | - Accepting responsibility and apologizing to those affected by our mistakes,
17 | and learning from the experience
18 | - Focusing on what is best not just for us as individuals, but for the
19 | overall community
20 |
21 | Examples of unacceptable behavior include:
22 |
23 | - The use of sexualized language or imagery, and sexual attention or
24 | advances of any kind
25 | - Trolling, insulting or derogatory comments, and personal or political attacks
26 | - Public or private harassment
27 | - Publishing others' private information, such as a physical or email
28 | address, without their explicit permission
29 | - Other conduct which could reasonably be considered inappropriate in a
30 | professional setting
31 |
32 | ## Enforcement Responsibilities
33 |
34 | Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.
35 |
36 | Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.
37 |
38 | ## Scope
39 |
40 | This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
41 |
42 | ## Enforcement
43 |
44 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement.
45 | All complaints will be reviewed and investigated promptly and fairly.
46 |
47 | All community leaders are obligated to respect the privacy and security of the reporter of any incident.
48 |
49 | ## Enforcement Guidelines
50 |
51 | Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct:
52 |
53 | ### 1. Correction
54 |
55 | **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.
56 |
57 | **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.
58 |
59 | ### 2. Warning
60 |
61 | **Community Impact**: A violation through a single incident or series of actions.
62 |
63 | **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban.
64 |
65 | ### 3. Temporary Ban
66 |
67 | **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior.
68 |
69 | **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban.
70 |
71 | ### 4. Permanent Ban
72 |
73 | **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.
74 |
75 | **Consequence**: A permanent ban from any sort of public interaction within the community.
76 |
77 | ## Attribution
78 |
79 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, available at
80 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
81 |
82 | Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity).
83 |
84 | [homepage]: https://www.contributor-covenant.org
85 |
86 | For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations.
87 |
--------------------------------------------------------------------------------
/web/src/Layout.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext, useEffect, useState } from "react";
2 | import { Link, Outlet } from "react-router-dom";
3 | import {
4 | VStack,
5 | Progress,
6 | HStack,
7 | Box,
8 | Input,
9 | Button,
10 | Image,
11 | AspectRatio,
12 | InputGroup,
13 | InputLeftElement,
14 | InputRightElement,
15 | Heading,
16 | Spacer
17 | } from "@chakra-ui/react";
18 | import { FaSearch } from "react-icons/fa";
19 | import { StateContext } from "./context/StateContext";
20 | import { ColorModeSwitch } from "./components/ColorModeSwitch";
21 | import Joyride, { STATUS } from "react-joyride";
22 | import { searchBarSteps } from "./helpers/utils";
23 | import { LogoIcon } from "./assets/LogoIcon";
24 | import { Login } from "./components/Login";
25 |
26 | const Layout: React.FC> = () => {
27 | const { search, setSearch, loading, setLoading } = useContext(StateContext);
28 | const [runTutorial, setRunTutorial] = useState(false);
29 |
30 | // handle the callback from joyride
31 | const handleJoyrideCallback = (data: any) => {
32 | const { status } = data;
33 | if ([STATUS.FINISHED, STATUS.SKIPPED].includes(status)) {
34 | // Need to set our running state to false, so we can restart if we click start again.
35 | setRunTutorial(false);
36 | }
37 | };
38 |
39 | useEffect(() => {
40 | const timer = setTimeout(() => {
41 | setLoading(false);
42 | }, 3000);
43 |
44 | setRunTutorial(true);
45 | return () => clearTimeout(timer);
46 | }, []);
47 |
48 | return (
49 | <>
50 |
70 |
71 |
85 |
86 |
87 |
97 |
98 |
99 |
100 | DefinitelySetup
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
117 |
118 |
126 |
127 |
128 |
129 |
130 |
131 | setSearch(e.target.value)}
138 | borderRadius={0}
139 | fontSize={14}
140 | color={"gray.600"}
141 | />
142 |
143 |
153 | Search
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 | >
162 | );
163 | };
164 |
165 | export default Layout;
166 |
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | # DefinitelySetup Pull Request Template
2 | ## Title
3 |
4 | [Short description of the new ceremony]
5 |
6 | ## Description
7 |
8 | Please provide a detailed description of the ceremony you are proposing.
9 |
10 | ## Uploads
11 |
12 | - [ ] R1CS file
13 | - [ ] wasm file
14 | - [ ] Ceremony config file
15 |
16 | **Process**
17 |
18 | - open the PR from a branch named $YOUR_PROJECT_NAME-ceremony
19 | - fill the *p0tionConfig.json* file accordingly:
20 | + The title of the ceremony will end up being its prefix. Prefixes are simply the title all lowercase, with dashes (*-*) instead of whitespace. For example, given a title of *Example Ceremony*, the prefix will be *example-ceremony*.
21 | + Fill the rest of the fields with the desired data, ensuring that each circuit details are correct, and that you chose the required Verification method.
22 | + In the artifacts fields, please add the correct path which should be:
23 | *./ceremonies/$PREFIX/$CIRCUIT_NAME.$EXTENSION* - note that we require both the *r1cs* and the *wasm* files.
24 | + *Note* that you can use [p0tion phase2cli](https://github.com/privacy-scaling-explorations/p0tion) as follows to verify that the config file is correct:
25 | * `phase2cli validate --template $PATH_TO_THE_TEMPLATE`
26 | - create a directory inside *./ceremonies* and name it with the *prefix* (detailed in the bullet point above).
27 | - ensure that only these three files were added:
28 | + r1cs
29 | + wasm
30 | + p0tionConfig.json
31 | - the destination path of the PR should be either of:
32 | + main (for production runs)
33 | + staging (for a test run)
34 | + development (for a test run using experimental features such as VM verification)
35 |
36 | Failing to follow the above instructions, will result in the CI checks failing. If all is done accordingly, an administrator will approve and merge your PR and a ceremony will be setup for you.
37 |
38 | ## Ceremony Details
39 |
40 | **p0tionConfig.json** template:
41 |
42 | ```json
43 | {
44 | "title": "",
45 | "description": "",
46 | "startDate": "",
47 | "endDate": "",
48 | "timeoutMechanismType": "",
49 | "penalty": 10,
50 | "circuits": [
51 | {
52 | "description": "",
53 | "compiler": {
54 | "version": "",
55 | "commitHash": ""
56 | },
57 | "template": {
58 | "source": "",
59 | "commitHash": "",
60 | "paramsConfiguration": [6,8,3,2]
61 | },
62 | "verification": {
63 | "cfOrVm": "CF"
64 | },
65 | "artifacts": {
66 | "r1csLocalFilePath": "",
67 | "wasmLocalFilePath": ""
68 | },
69 | "name": "",
70 | "dynamicThreshold": 0,
71 | "fixedTimeWindow": 3600,
72 | "sequencePosition": 1
73 | }
74 | ]
75 | }
76 | ```
77 |
78 | **In-details**:
79 |
80 | - title - a string representing the title of your ceremony. Please note that this will form the prefix (more details in the previous section).
81 | - description - a string that can be used to describe a ceremony details
82 | - startDate - the desired start date of your ceremony. Please note that might be changed by the admin.
83 | - endDate - the end date of your ceremony.
84 | - timeoutMechanismType - the type of timeout you would like to use for your ceremony. Options are *FIXED* or *DYNAMIC*. A fixed timeout will always be the same and users who are unable to progress throughout the contribution steps due to either having a slow connection or stopping the ongoing process, will be subject to a timeout of this length. Dynamic on the other hand, will adjust depending on the average time it takes to contribute.
85 | - penalty - how long will a user need to wait before being able to join the waiting queue again
86 | - circuits - an array of circuit object:
87 | - description - a string describing the circuit
88 | - compiler - an object made of:
89 | - version - a string with the compiler version. e.g. "1.0"
90 | - commitHash - a string with the commit id (needs to be a GitHub commit hash)
91 | - template - an object made of:
92 | - source - a string with the URL of the circom file
93 | - commitHash - a string with the commit id (needs to be a GitHub commit hash)
94 | - paramsConfiguration - an array of numbers with the parameters of the circuit template
95 | - verification - an object detailing how the circuit's zKeys will be verified
96 | - cfOrVm - a string with either "CF" or "VM". If "VM" the following must be added:
97 | - vmConfigurationType - a string with the VM type - options:
98 | * "t3.large"
99 | * "t3.2xlarge"
100 | * please refer to [AWS docs](https://aws.amazon.com/ec2/instance-types/) for more instance types
101 | - vmDiskSize - a number with the size of the disk
102 | - vmDiskType - a string with the type of the disk - options:
103 | * "gp2"
104 | * "gp3"
105 | * "io1"
106 | * "st1"
107 | * "sc1"
108 | - artifacts - an object with the local paths to the r1cs and wasm
109 | - r1csLocalFilePath - a string with the r1cs path e.g. "./ceremonies/ceremonyPrefix/circuit.r1cs"
110 | - wasmLocalFilePath - a string with the r1cs path e.g. "./ceremonies/ceremonyPrefix/circuit.wasm"
111 | - name - a string with the circuit name
112 | - dynamicThreshold - if selected dynamic timeout please enter the threshold here as a number
113 | - fixedTimeWindow - if selected fixed timeout please enter the time window here as a number
114 | - sequencePosition - a number with the circuit sequence position. Each sequence must be different and it should start from 1. The circuit with the lowest sequence number will be the first one which users will contribute to.
115 |
116 | > Note: If the constraint size is less than 1M, your PR will be automatically approved and merged at the end of the week.
117 |
118 | ## Additional Notes
119 |
120 | If there are any additional notes, requirements or special instructions related to this ceremony, please specify them here.
121 |
122 | Confirmation
123 | - [ ] I have read and understood the DefinitelySetup guidelines and requirements.
124 | - [ ] I confirm that all uploaded files are correctly configured and adhere to the guidelines.
125 |
--------------------------------------------------------------------------------
/web/src/context/ProjectPageContext.tsx:
--------------------------------------------------------------------------------
1 | import React, { createContext, useContext, useEffect, useState } from "react";
2 | import { useNavigate, useParams } from "react-router-dom";
3 | import { DocumentData } from "firebase/firestore";
4 | import { z } from "zod";
5 | import { StateContext, useStateContext } from "./StateContext";
6 | import {
7 | getAllCollectionDocs,
8 | getCeremonyCircuits,
9 | getContributions,
10 | getFinalBeacon,
11 | getParticipantsAvatar
12 | } from "../helpers/firebase";
13 | import {
14 | CeremonyState,
15 | CircuitDocumentReferenceAndData,
16 | FinalBeacon,
17 | ParticipantDocumentReferenceAndData,
18 | ProjectData,
19 | ProjectPageContextProps,
20 | ZkeyDownloadLink
21 | } from "../helpers/interfaces";
22 | import { checkIfUserContributed, findLargestConstraint, formatZkeyIndex, processItems } from "../helpers/utils";
23 | import { awsRegion, bucketPostfix, finalContributionIndex, maxConstraintsForBrowser } from "../helpers/constants";
24 |
25 | export const ProjectDataSchema = z.object({
26 | circuits: z.optional(z.array(z.any())),
27 | participants: z.optional(z.array(z.any())),
28 | contributions: z.optional(z.array(z.any()))
29 | });
30 |
31 | export const defaultProjectData: ProjectData = {};
32 |
33 | type ProjectPageProviderProps = {
34 | children: React.ReactNode;
35 | };
36 |
37 | const ProjectPageContext = createContext({
38 | hasUserContributed: false,
39 | projectData: defaultProjectData,
40 | isLoading: false,
41 | runTutorial: false,
42 | avatars: [],
43 | largestCircuitConstraints: maxConstraintsForBrowser + 1, // contribution on browser has 100000 max constraints
44 | finalZkeys: []
45 | });
46 |
47 | export const useProjectPageContext = () => useContext(ProjectPageContext);
48 |
49 | export const ProjectPageProvider: React.FC = ({ children }) => {
50 | const navigate = useNavigate();
51 | const { loading: isLoading, setLoading: setIsLoading, runTutorial } = useContext(StateContext);
52 | const [ projectData, setProjectData ] = useState(defaultProjectData);
53 | const [ avatars, setAvatars ] = useState([]);
54 | const [ hasUserContributed, setHasUserContributed ] = useState(false);
55 | const [ largestCircuitConstraints, setLargestCircuitConstraints ] = useState(maxConstraintsForBrowser + 1) // contribution on browser has 100000 max constraints
56 | const [ finalZkeys, setFinalZkeys ] = useState([])
57 | const [ latestZkeys, setLatestZkeys ] = useState([])
58 | const [ finalBeacon, setFinalBeacon ] = useState()
59 |
60 | const { projects } = useStateContext();
61 | const { ceremonyName } = useParams();
62 |
63 | const project = projects.find((project) => project.ceremony.data.title === ceremonyName);
64 | const projectId = project?.ceremony.uid;
65 |
66 | useEffect(() => {
67 | const fetchData = async () => {
68 | setIsLoading(true);
69 | try {
70 | if (projectId === undefined || project === undefined) return
71 | const circuitsDocs = await getCeremonyCircuits(projectId);
72 | const circuits: CircuitDocumentReferenceAndData[] = circuitsDocs.map((document: DocumentData) => ({ uid: document.id, data: document.data }));
73 |
74 | const finalZkeys: ZkeyDownloadLink[] = []
75 | const lastZkeys: ZkeyDownloadLink[] = []
76 | for (const circuit of circuits) {
77 | const { prefix } = circuit.data
78 | finalZkeys.push(
79 | {
80 | zkeyFilename: `${prefix}_${finalContributionIndex}.zkey`,
81 | zkeyURL: `https://${project?.ceremony.data.prefix}${bucketPostfix}.s3.${awsRegion}.amazonaws.com/circuits/${
82 | prefix
83 | }/contributions/${prefix}_${finalContributionIndex}.zkey`
84 | }
85 | )
86 |
87 | // store the latest zkey for each circuit
88 | const { waitingQueue } = circuit.data
89 |
90 | // if it's not finalized then we skip
91 | if (project.ceremony.data.state !== CeremonyState.FINALIZED) continue
92 |
93 | const index = formatZkeyIndex(waitingQueue!.completedContributions)
94 | lastZkeys.push(
95 | {
96 | zkeyFilename: `${prefix}_${index}.zkey`,
97 | zkeyURL: `https://${project?.ceremony.data.prefix}${bucketPostfix}.s3.${awsRegion}.amazonaws.com/circuits/${
98 | prefix
99 | }/contributions/${prefix}_${index}.zkey`
100 | }
101 | )
102 | }
103 |
104 | setLatestZkeys(lastZkeys)
105 | setFinalZkeys(finalZkeys)
106 |
107 | // if we have data for the ceremony and it's finalized then we can get the final beacon
108 | if (project.ceremony && project.ceremony.data.state === CeremonyState.FINALIZED) {
109 | const beacon = await getFinalBeacon(projectId, project.ceremony.data.coordinatorId, circuits[0].uid)
110 | setFinalBeacon(beacon)
111 | }
112 |
113 | const participantsDocs = await getAllCollectionDocs(`ceremonies/${projectId}/participants`);
114 | const participants: ParticipantDocumentReferenceAndData[] = participantsDocs.map((document: DocumentData) => ({ uid: document.id, data: document.data() }));
115 |
116 | // run concurrent requests per circuit
117 | const args: any[][] = circuits.map((circuit: CircuitDocumentReferenceAndData) => [projectId, circuit.uid])
118 | const { results } = await processItems(args, getContributions, true)
119 |
120 | const contributions = results.flat()
121 |
122 | const updatedProjectData = { circuits, participants, contributions };
123 |
124 | const parsedData = ProjectDataSchema.parse(updatedProjectData);
125 |
126 | setProjectData(parsedData);
127 |
128 | const avatars = await getParticipantsAvatar(projectId)
129 | setAvatars(avatars)
130 |
131 | const hasContributed = await checkIfUserContributed(projectId)
132 | setHasUserContributed(hasContributed)
133 |
134 | const _constraints = findLargestConstraint(circuits)
135 | setLargestCircuitConstraints(_constraints)
136 | } catch (error) {
137 | console.error(error);
138 | navigate("/error");
139 | } finally {
140 | setIsLoading(false);
141 | }
142 | };
143 |
144 | fetchData();
145 | }, [navigate, projectId]);
146 |
147 |
148 | return (
149 |
150 | {children}
151 |
152 | );
153 | };
154 |
--------------------------------------------------------------------------------
/.github/workflows/setup-ceremony.yml:
--------------------------------------------------------------------------------
1 | name: Setup ceremony
2 |
3 | # this will only run on pushes to main/staing/dev
4 | on:
5 | push:
6 | branches: [ main, staging, dev ]
7 |
8 | env:
9 | AWS_REGION: eu-central-1
10 |
11 | jobs:
12 | # first we start a custom runner
13 | start-runner:
14 | if: github.ref != 'refs/heads/dev'
15 | name: Start self-hosted EC2 runner
16 | runs-on: ubuntu-latest
17 | environment:
18 | ${{ (github.ref == 'refs/heads/main' && 'p0tion-production') ||
19 | (github.ref == 'refs/heads/staging' && 'p0tion-staging') }}
20 | outputs:
21 | label: ${{ steps.start-ec2-runner.outputs.label }}
22 | ec2-instance-id: ${{ steps.start-ec2-runner.outputs.ec2-instance-id }}
23 | steps:
24 | - name: Configure AWS credentials
25 | uses: aws-actions/configure-aws-credentials@v2
26 | with:
27 | aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
28 | aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
29 | aws-region: ${{ env.AWS_REGION }}
30 |
31 | - name: Start EC2 runner
32 | id: start-ec2-runner
33 | uses: machulav/ec2-github-runner@v2
34 | with:
35 | mode: start
36 | github-token: ${{ secrets.GH_PERSONAL_ACCESS_TOKEN }}
37 | ec2-image-id: ami-0d65ed0872506990c
38 | ec2-instance-type: t3.2xlarge
39 | subnet-id: subnet-0817be1b2160793b5
40 | security-group-id: sg-0aea3cbb15e30a921
41 | aws-resource-tags: >
42 | [
43 | { "Key": "Name", "Value": "p0tion-github-runner" },
44 | { "Key": "GitHubRepository", "Value": "${{ github.repository }}" }
45 | ]
46 |
47 | # then we setup the ceremony
48 | setup-ceremonies:
49 | name: Setup ceremony using vm-runner
50 | needs: start-runner
51 | if: github.ref != 'refs/heads/dev'
52 | runs-on: ${{ needs.start-runner.outputs.label }} # run the job on the newly created runner
53 | environment:
54 | ${{ (github.ref == 'refs/heads/main' && 'p0tion-production') ||
55 | (github.ref == 'refs/heads/staging' && 'p0tion-staging') }}
56 | steps:
57 | - uses: actions/checkout@v2
58 |
59 | - name: Set up Node.js
60 | uses: actions/setup-node@v2
61 | with:
62 | node-version: '16'
63 |
64 | # install p0tion
65 | - name: Install p0tion globally
66 | run: npm install -g @p0tion/phase2cli
67 |
68 | # write env to file
69 | - name: Write env locally
70 | run: |
71 | echo "${{ secrets.PHASE2CLI_ENV_FILE }}" > ./.env
72 |
73 | # List ceremonies that have already been created
74 | - name: List existing ceremonies
75 | id: list_ceremonies
76 | run: |
77 | echo "$(phase2cli list)" > existing_ceremonies.txt
78 | cat existing_ceremonies.txt
79 |
80 | # List all the ceremonies in ./ceremonies
81 | - name: List all ceremonies
82 | id: list_all_ceremonies
83 | run: |
84 | echo "$(ls -d ceremonies/* | grep -v 'README.md' | cut -d'/' -f2)" > dir_output.txt
85 | cat dir_output.txt
86 |
87 | # want to setup only ceremonies that have not been setup already
88 | - name: Run p0tion and setup ceremony
89 | run: |
90 | IFS=',' read -ra EXISTING_CEREMONIES <<< $(cat existing_ceremonies.txt)
91 | ALL_CEREMONIES=()
92 | while IFS= read -r line; do
93 | ALL_CEREMONIES+=("$line")
94 | done < dir_output.txt
95 |
96 | for ceremony in "${ALL_CEREMONIES[@]}"; do
97 | if [[ ! " ${EXISTING_CEREMONIES[@]} " =~ " ${ceremony} " ]]; then
98 | echo 'Setting up ceremony: ./ceremonies/$ceremony/p0tionConfig.json'
99 | NODE_OPTIONS=--max-old-space-size=12288 phase2cli coordinate setup --template "./ceremonies/$ceremony/p0tionConfig.json" --auth "${{ secrets.ACCESS_TOKEN }}"
100 | fi
101 | done
102 |
103 | # on dev do not use runner
104 | setup-ceremonies-dev:
105 | name: Setup ceremony without vm-runner
106 | if: github.ref == 'refs/heads/dev'
107 | runs-on: ubuntu-latest
108 | environment: 'p0tion-development'
109 | steps:
110 | - uses: actions/checkout@v2
111 |
112 | - name: Set up Node.js
113 | uses: actions/setup-node@v2
114 | with:
115 | node-version: '16'
116 |
117 | # install p0tion
118 | - name: Install p0tion globally
119 | run: npm install -g @p0tion/phase2cli
120 |
121 | # write env to file
122 | - name: Write env locally
123 | run: |
124 | echo "${{ secrets.PHASE2CLI_ENV_FILE }}" > ./.env
125 |
126 | # List ceremonies that have already been created
127 | - name: List existing ceremonies
128 | id: list_ceremonies
129 | run: |
130 | echo "$(phase2cli list)" > existing_ceremonies.txt
131 | cat existing_ceremonies.txt
132 |
133 | # List all the ceremonies in ./ceremonies
134 | - name: List all ceremonies
135 | id: list_all_ceremonies
136 | run: |
137 | echo "$(ls -d ceremonies/* | grep -v 'README.md' | cut -d'/' -f2)" > dir_output.txt
138 | cat dir_output.txt
139 |
140 | # want to setup only ceremonies that have not been setup already
141 | - name: Run p0tion and setup ceremony
142 | run: |
143 | IFS=',' read -ra EXISTING_CEREMONIES <<< $(cat existing_ceremonies.txt)
144 | ALL_CEREMONIES=()
145 | while IFS= read -r line; do
146 | ALL_CEREMONIES+=("$line")
147 | done < dir_output.txt
148 |
149 | for ceremony in "${ALL_CEREMONIES[@]}"; do
150 | if [[ ! " ${EXISTING_CEREMONIES[@]} " =~ " ${ceremony} " ]]; then
151 | echo 'Setting up ceremony: ./ceremonies/$ceremony/p0tionConfig.json'
152 | phase2cli coordinate setup --template "./ceremonies/$ceremony/p0tionConfig.json" --auth "${{ secrets.ACCESS_TOKEN }}"
153 | fi
154 | done
155 |
156 | # after everything, make sure we stop the runner
157 | # do not set environment so there is no need to approve to stop the runner
158 | stop-runner:
159 | name: Stop self-hosted EC2 runner
160 | # run if previous failed and if not on dev branch
161 | if: ${{ always() && github.ref != 'refs/heads/dev' }}
162 | needs:
163 | - start-runner # required to get output from the start-runner job
164 | - setup-ceremonies # required to wait when the main job is done
165 | runs-on: ubuntu-latest
166 | steps:
167 | - name: Configure AWS credentials
168 | uses: aws-actions/configure-aws-credentials@v2
169 | with:
170 | aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
171 | aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
172 | aws-region: ${{ env.AWS_REGION }}
173 |
174 | - name: Stop EC2 runner
175 | uses: machulav/ec2-github-runner@v2
176 | with:
177 | mode: stop
178 | github-token: ${{ secrets.GH_PERSONAL_ACCESS_TOKEN }}
179 | label: ${{ needs.start-runner.outputs.label }}
180 | ec2-instance-id: ${{ needs.start-runner.outputs.ec2-instance-id }}
181 |
--------------------------------------------------------------------------------
/web/src/context/StateContext.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState, createContext, useEffect, useContext } from "react";
2 | import { CeremonyDocumentReferenceAndData, CeremonyState, CeremonyTimeoutType, CeremonyType, CircuitContributionVerificationMechanism, CircuitDocumentReferenceAndData, Project, State, StateProviderProps, WaitingQueue } from "../helpers/interfaces";
3 | import { getAllCollectionDocs, getCeremonyCircuitsWaitingQueue } from "../helpers/firebase";
4 | import { DocumentData } from 'firebase/firestore'
5 | import { commonTerms } from "../helpers/constants";
6 |
7 | export const StateContext = createContext({
8 | projects: [
9 | // Initial project data
10 | {
11 | ceremony: {
12 | uid: "i3kFOOUi8L42ooRWQh8N",
13 | data: {
14 | title: "example",
15 | prefix: "example",
16 | description: "This is an example ceremony",
17 | startDate: new Date("2023-07-01").getTime(),
18 | endDate: new Date("2023-07-31").getTime(),
19 | timeoutMechanismType: CeremonyTimeoutType.FIXED,
20 | penalty: 3600,
21 | state: CeremonyState.OPENED,
22 | type: CeremonyType.PHASE2,
23 | coordinatorId: "uKm6XEjOKoeZUKAf2goY4vamgHE4",
24 | lastUpdated: Date.now()
25 | }
26 | }
27 | }
28 | ],
29 | setProjects: () => null,
30 | circuit: {
31 | uid: "000000000000000000A1",
32 | data: {
33 | name: "Circuit Small",
34 | description: "Short description of Circuit Small",
35 | prefix: "circuit-small",
36 | sequencePosition: 1,
37 | fixedTimeWindow: 10,
38 | zKeySizeInBytes: 45020,
39 | lastUpdated: Date.now(),
40 | metadata: {
41 | constraints: 65,
42 | curve: "bn-128",
43 | labels: 79,
44 | outputs: 1,
45 | pot: 7,
46 | privateInputs: 0,
47 | publicInputs: 2,
48 | wires: 67
49 | },
50 | template: {
51 | commitHash: "295d995802b152a1dc73b5d0690ce3f8ca5d9b23",
52 | paramsConfiguration: ["2"],
53 | source: "https://github.com/0xjei/circom-starter/blob/dev/circuits/exercise/checkAscendingOrder.circom"
54 | },
55 | waitingQueue: {
56 | completedContributions: 0,
57 | contributors: [],
58 | currentContributor: "",
59 | failedContributions: 0
60 | },
61 | files: {
62 | initialZkeyBlake2bHash:
63 | "eea0a468524a984908bff6de1de09867ac5d5b0caed92c3332fd5ec61004f79505a784df9d23f69f33efbfef016ad3138871fa8ad63b6e8124a9d0721b0e9e32",
64 | initialZkeyFilename: "circuit_small_00000.zkey",
65 | initialZkeyStoragePath: "circuits/circuit_small/contributions/circuit_small_00000.zkey",
66 | potBlake2bHash:
67 | "34379653611c22a7647da22893c606f9840b38d1cb6da3368df85c2e0b709cfdb03a8efe91ce621a424a39fe4d5f5451266d91d21203148c2d7d61cf5298d119",
68 | potFilename: "powersOfTau28_hez_final_02.ptau",
69 | potStoragePath: "pot/powersOfTau28_hez_final_02.ptau",
70 | r1csBlake2bHash:
71 | "0739198d5578a4bdaeb2fa2a1043a1d9cac988472f97337a0a60c296052b82d6cecb6ae7ce503ab9864bc86a38cdb583f2d33877c41543cbf19049510bca7472",
72 | r1csFilename: "circuit_small.r1cs",
73 | r1csStoragePath: "circuits/circuit_small/circuit_small.r1cs",
74 | wasmBlake2bHash:
75 | "00d09469acaba682802bf92df24708cf3d499b759379f959c4b6932b14fe9e6bfccc793c3933eac4a76546171d402cab1ae3ce1b3291dbba8e2fb358d52bd77d",
76 | wasmFilename: "circuit_small.wasm",
77 | wasmStoragePath: "circuits/circuit_small/circuit_small.wasm"
78 | },
79 | avgTimings: {
80 | contributionComputation: 0,
81 | fullContribution: 0,
82 | verifyCloudFunction: 0
83 | },
84 | compiler: {
85 | commitHash: "ed807764a17ce06d8307cd611ab6b917247914f5",
86 | version: "2.0.5"
87 | },
88 | verification: {
89 | cfOrVm: CircuitContributionVerificationMechanism.CF,
90 | vm: {
91 | vmConfigurationType: ""
92 | }
93 | }
94 | }
95 | },
96 | setCircuit: () => null,
97 | search: "",
98 | setSearch: () => null,
99 | loading: false,
100 | setLoading: () => null,
101 | runTutorial: false,
102 | setRunTutorial: () => null,
103 | setUser: () => {},
104 | waitingQueue: [{} as WaitingQueue]
105 | });
106 |
107 | export const useInitialStateContext = () => {
108 | const [projects, setProjects] = useState([]);
109 | const [circuit, setCircuit] = useState();
110 | const [waitingQueue, setWaitingQueue] = useState([])
111 | const [search, setSearch] = useState("");
112 | const [loading, setLoading] = useState(false);
113 | const [runTutorial, setRunTutorial] = useState(false);
114 |
115 | // Fetch ceremony data.
116 | useEffect(() => {
117 | const fetchData = async () => {
118 | // 0. Prepare service.
119 | setLoading(true)
120 |
121 | // 1. Fetch data.
122 | const docs = await getAllCollectionDocs(commonTerms.collections.ceremonies.name)
123 |
124 | // 2. Post-process data.
125 | const ceremonies: CeremonyDocumentReferenceAndData[] = docs.map((document: DocumentData) => { return { uid: document.id, data: document.data() } })
126 | const ceremoniesVisibleInWeb = ceremonies.filter((ceremony) => ceremony.data.hideInWeb !== true)
127 | const projects: Project[] = ceremoniesVisibleInWeb.map((ceremony: CeremonyDocumentReferenceAndData) => { return { ceremony: ceremony } })
128 |
129 | const queue: WaitingQueue[] = []
130 | for (const project of projects) {
131 | if (project.ceremony.data.state === CeremonyState.OPENED) {
132 | const tmpQueue = await getCeremonyCircuitsWaitingQueue(project.ceremony.uid, project.ceremony.data.title)
133 | queue.push(...tmpQueue)
134 | }
135 | }
136 |
137 | setWaitingQueue(queue)
138 | // 3. Store data.
139 | setProjects(projects)
140 | setLoading(false)
141 | }
142 |
143 | setRunTutorial(true)
144 |
145 | fetchData()
146 |
147 | },[])
148 |
149 | return { waitingQueue, projects, setProjects, circuit, setCircuit, search, setSearch, loading, setLoading, runTutorial, setRunTutorial };
150 | };
151 |
152 | export const StateProvider: React.FC = ({ children }) => {
153 |
154 | const [user, setUser] = useState(
155 | localStorage.getItem("username") || undefined
156 | );
157 |
158 | useEffect(() => {
159 | const _user = localStorage.getItem("username")?.toString() || "";
160 | if (_user !== user) {
161 | setUser(_user);
162 | }
163 | }, [user]);
164 |
165 |
166 | const state = useInitialStateContext()
167 |
168 | return (
169 | // @ts-ignore
170 |
171 | {children}
172 |
173 | );
174 | };
175 |
176 | export const useStateContext = () => useContext(StateContext);
--------------------------------------------------------------------------------
/ceremonies/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | Ceremonies 🌵
4 |
5 | A folder where projects wanting to do a trusted setup phase2 ceremony can add their artifacts.
6 |
7 |
8 | | This folder contains a collection of ceremony artifacts (r1cs, wasm) and setup configuration file which are used to setup a phase2 trusted setup ceremony using [p0tion](https://github.com/privacy-scaling-explorations/p0tion/). |
9 | | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
10 |
11 | To request a new ceremony to be setup, you will need to submit a PR with a folder including your ceremony configuration file.
12 |
13 | ## Guidelines
14 |
15 | ### Process
16 |
17 | - open the PR from a branch named $YOUR_PROJECT_NAME-ceremony
18 | - fill the *p0tionConfig.json* file accordingly:
19 | + The title of the ceremony will end up being its prefix. Prefixes are simply the title all lowercase, with dashes (*-*) instead of whitespace. For example, given a title of *Example Ceremony*, the prefix will be *example-ceremony*.
20 | + Fill the rest of the fields with the desired data, ensuring that each circuit details are correct, and that you chose the required Verification method.
21 | + In the artifacts fields, please add the correct storage path on AWS (the URL to the artifacts) - note that we require both the *r1cs* and the *wasm* files.
22 | + *Note* that you can use [p0tion phase2cli](https://github.com/privacy-scaling-explorations/p0tion) as follows to verify that the config file is correct:
23 | * `phase2cli validate --template $PATH_TO_THE_TEMPLATE`
24 | - create a directory inside *./ceremonies* and name it with the *prefix* (detailed in the bullet point above).
25 | - ensure that only one file is added:
26 | + p0tionConfig.json
27 | - the destination path of the PR should be either of:
28 | + main (for production runs)
29 | + staging (for a test run)
30 | + development (for a test run using experimental features such as VM verification)
31 |
32 | ### Use setup_ceremony_config.sh to generate the configuration file
33 |
34 | If your ceremony includes a large number of circuit instances, you might want to use the bash script included in this folder - **setup_ceremony_config.sh**.
35 |
36 | The script will upload your artifacts to a S3 bucket of your choice (must be owned by yourself) and autofill the **p0tionConfig.json** file. You only need to have installed the following:
37 |
38 | * **jq**
39 | * **aws-cli** - please ensure that you set a profile with its credentials (access key id and secret access key). For more info please refer to [aws docs](https://aws.amazon.com/cli/)
40 |
41 | **Usage**:
42 |
43 | - `chmod +x setup_ceremony_config.sh`
44 | * `./setup_ceremony_config.sh`
45 |
46 | ### Template in details
47 |
48 | **p0tionConfig.json** template:
49 |
50 | ```json
51 | {
52 | "title": "",
53 | "description": "",
54 | "startDate": "",
55 | "endDate": "",
56 | "timeoutMechanismType": "",
57 | "penalty": "",
58 | "circuits": [
59 | {
60 | "description": "",
61 | "compiler": {
62 | "version": "",
63 | "commitHash": ""
64 | },
65 | "template": {
66 | "source": "",
67 | "commitHash": "",
68 | "paramsConfiguration": [""]
69 | },
70 | "verification": {
71 | "cfOrVm": ""
72 | },
73 | "artifacts": {
74 | "r1csStoragePath": "",
75 | "r1csStoragePath": ""
76 | },
77 | "name": "",
78 | "dynamicThreshold": "",
79 | "fixedTimeWindow": "",
80 | "sequencePosition": ""
81 | }
82 | ]
83 | }
84 | ```
85 |
86 | **In-details**:
87 |
88 | - title - a string representing the title of your ceremony. Please note that this will form the prefix (more details in the previous section).
89 | - description - a string that can be used to describe a ceremony details
90 | - startDate - the desired start date of your ceremony. Please note that might be changed by the admin.
91 | - endDate - the end date of your ceremony.
92 | - timeoutMechanismType - the type of timeout you would like to use for your ceremony. Options are *FIXED* or *DYNAMIC*. A fixed timeout will always be the same and users who are unable to progress throughout the contribution steps due to either having a slow connection or stopping the ongoing process, will be subject to a timeout of this length. Dynamic on the other hand, will adjust depending on the average time it takes to contribute.
93 | - penalty - how long will a user need to wait before being able to join the waiting queue again
94 | - circuits - an array of circuit object:
95 | - description - a string describing the circuit
96 | - compiler - an object made of:
97 | - version - a string with the compiler version. e.g. "1.0"
98 | - commitHash - a string with the commit id (needs to be a GitHub commit hash)
99 | - template - an object made of:
100 | - source - a string with the URL of the circom file
101 | - commitHash - a string with the commit id (needs to be a GitHub commit hash)
102 | - paramsConfiguration - an array of numbers with the parameters of the circuit template
103 | - verification - an object detailing how the circuit's zKeys will be verified
104 | - cfOrVm - a string with either "CF" or "VM". If "VM" the following must be added:
105 | - vmConfigurationType - a string with the VM type - options:
106 | * "t3.large"
107 | * "t3.2xlarge"
108 | * please refer to [AWS docs](https://aws.amazon.com/ec2/instance-types/) for more instance types
109 | - vmDiskSize - a number with the size of the disk
110 | - vmDiskType - a string with the type of the disk - options:
111 | * "gp2"
112 | * "gp3"
113 | * "io1"
114 | * "st1"
115 | * "sc1"
116 | * Example for **CF**:
117 | ```json
118 | "verification": {
119 | "cfOrVm": "CF",
120 | }
121 | ```
122 | * Example for **VM**:
123 | ```json
124 | "verification": {
125 | "cfOrVm": "VM",
126 | "vm": {
127 | "vmConfigurationType": "c5.4xlarge",
128 | "vmDiskSize": "30",
129 | "vmDiskType": "gp2"
130 | }
131 | }
132 | ```
133 | - artifacts - an object with the storage path to the r1cs and wasm on s3
134 | - r1csStoragePath - a string with the r1cs storage path on S3 e.g. "https://test-ceremony.region.amazonaws.com/circuit.r1cs"
135 | - wasmStoragePath - a string with the wasm storage path on s3 e.g. "https://test-ceremony.region.amazonaws.com/circuit.wasm"
136 | - name - a string with the circuit name
137 | - dynamicThreshold - if selected dynamic timeout please enter the threshold here as a number (as percentage 0 < dynamicThreshold < 100)
138 | - fixedTimeWindow - if selected fixed timeout please enter the time window here as a number (the unit is minutes)
139 | - sequencePosition - a number with the circuit sequence position. Each sequence must be different and it should start from 1. The circuit with the lowest sequence number will be the first one which users will contribute to.
140 |
141 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md:
--------------------------------------------------------------------------------
1 | # DefinitelySetup Pull Request Template
2 | ## Title
3 |
4 | [Short description of the new ceremony]
5 |
6 | ## Description
7 |
8 | Please provide a detailed description of the ceremony you are proposing.
9 |
10 | ## Uploads
11 |
12 | - [ ] R1CS files
13 | - [ ] wasm files
14 | - [ ] Ceremony config file (1 for all circuits)
15 |
16 | > Note: you can use the bash script **setup_cereremony_config.sh** to upload your artifacts to AWS S3 and fill the config file.
17 |
18 | **Process**
19 |
20 | > Feel free to delete this section once submitting your PR. This is left here as guidelines.
21 |
22 | - open the PR from a branch named $YOUR_PROJECT_NAME-ceremony
23 | - fill the *p0tionConfig.json* file accordingly:
24 | + The title of the ceremony will end up being its prefix. Prefixes are simply the title all lowercase, with dashes (*-*) instead of whitespace. For example, given a title of *Example Ceremony*, the prefix will be *example-ceremony*.
25 | + Fill the rest of the fields with the desired data, ensuring that each circuit details are correct, and that you chose the required Verification method.
26 | + In the artifacts fields, please add the correct storage path and bucket name - note that we require both the *r1cs* and the *wasm* files.
27 | + *Note* that you can use [p0tion phase2cli](https://github.com/privacy-scaling-explorations/p0tion) as follows to verify that the config file is correct:
28 | * `phase2cli validate --template $PATH_TO_THE_TEMPLATE`
29 | - create a directory inside *./ceremonies* and name it with the *prefix* (detailed in the bullet point above).
30 | - ensure that only these files were added:
31 | + r1cs for each circuit configuration
32 | + wasm for each circuit configuration
33 | + p0tionConfig.json
34 | - the destination path of the PR should be either of:
35 | + main (for production runs)
36 | + staging (for a test run)
37 | + development (for a test run using experimental features such as VM verification)
38 |
39 | > Note. If your circuit accepts metaparameters, and you desire including multiple parameter combinations in a single ceremony, add one circuit object inside the circuits array (of p0tionConfig.json) for EACH parameters combinations. This way, contributors will be able to contribute to the zKeys for all of the required parameters combinations.
40 |
41 | Failing to follow the above instructions, will result in the CI checks failing. If all is done accordingly, an administrator will approve and merge your PR and a ceremony will be setup for you.
42 |
43 | ## Ceremony Details
44 |
45 | **p0tionConfig.json** template:
46 |
47 | ```json
48 | {
49 | "title": "",
50 | "description": "",
51 | "startDate": "",
52 | "endDate": "",
53 | "timeoutMechanismType": "",
54 | "penalty": "",
55 | "circuits": [
56 | {
57 | "description": "",
58 | "compiler": {
59 | "version": "",
60 | "commitHash": ""
61 | },
62 | "template": {
63 | "source": "",
64 | "commitHash": "",
65 | "paramsConfiguration": [""]
66 | },
67 | "verification": {
68 | "cfOrVm": ""
69 | },
70 | "artifacts": {
71 | "bucket": "",
72 | "region": "",
73 | "r1csLocalFilePath": "",
74 | "wasmLocalFilePath": ""
75 | },
76 | "name": "",
77 | "dynamicThreshold": "",
78 | "fixedTimeWindow": "",
79 | "sequencePosition": ""
80 | }
81 | ]
82 | }
83 | ```
84 |
85 | **In-details**:
86 |
87 | - title - a string representing the title of your ceremony. Please note that this will form the prefix (more details in the previous section).
88 | - description - a string that can be used to describe a ceremony details
89 | - startDate - the desired start date of your ceremony. Please note that might be changed by the admin.
90 | - endDate - the end date of your ceremony.
91 | - timeoutMechanismType - the type of timeout you would like to use for your ceremony. Options are *FIXED* or *DYNAMIC*. A fixed timeout will always be the same and users who are unable to progress throughout the contribution steps due to either having a slow connection or stopping the ongoing process, will be subject to a timeout of this length. Dynamic on the other hand, will adjust depending on the average time it takes to contribute.
92 | - penalty - how long will a user need to wait before being able to join the waiting queue again
93 | - circuits - an array of circuit object:
94 | - description - a string describing the circuit
95 | - compiler - an object made of:
96 | - version - a string with the compiler version. e.g. "1.0"
97 | - commitHash - a string with the commit id (needs to be a GitHub commit hash)
98 | - template - an object made of:
99 | - source - a string with the URL of the circom file
100 | - commitHash - a string with the commit id (needs to be a GitHub commit hash)
101 | - paramsConfiguration - an array of numbers with the parameters of the circuit template
102 | - verification - an object detailing how the circuit's zKeys will be verified
103 | - cfOrVm - a string with either "CF" or "VM". If "VM" the following must be added:
104 | - vmConfigurationType - a string with the VM type - options:
105 | * "t3.large"
106 | * "t3.2xlarge"
107 | * please refer to [AWS docs](https://aws.amazon.com/ec2/instance-types/) for more instance types
108 | - vmDiskSize - a number with the size of the disk
109 | - vmDiskType - a string with the type of the disk - options:
110 | * "gp2"
111 | * "gp3"
112 | * "io1"
113 | * "st1"
114 | * "sc1"
115 | - artifacts - an object with the storage path to the r1cs and wasm on s3
116 | - bucket - a string with the bucket name
117 | - region - the AWS region where the bucket live
118 | - r1csStoragePath - a string with the r1cs storage path e.g. "test-ceremony/circuit.r1cs"
119 | - wasmStoragePath - a string with the wasm storage path e.g. "test-ceremony/circuit.wasm"
120 | - name - a string with the circuit name
121 | - dynamicThreshold - if selected dynamic timeout please enter the threshold here as a number
122 | - fixedTimeWindow - if selected fixed timeout please enter the time window here as a number
123 | - sequencePosition - a number with the circuit sequence position. Each sequence must be different and it should start from 1. The circuit with the lowest sequence number will be the first one which users will contribute to.
124 |
125 | > Note: If the constraint size is less than 1M, your PR will be automatically approved and merged at the end of the week.
126 |
127 | ## Additional Notes
128 |
129 | If there are any additional notes, requirements or special instructions related to this ceremony, please specify them here.
130 |
131 | Confirmation
132 |
133 | - [ ] I have read and understood the DefinitelySetup guidelines and requirements.
134 | - [ ] I confirm that all uploaded files are correctly configured and adhere to the guidelines:
135 | - [ ] The origin branch of the ceremony has the postfix **-ceremony**
136 | - [ ] The target branch is either dev/staging/main
137 | - [ ] I uploaded one r1cs and one wasm file for each of the parameters combinations I want users to contribute to
138 | - [ ] I named the directory following the ceremony prefix syntax (described in the sections above)
139 | - [ ] I run `phase2cli verify --template $MY_TEMPLATE_PATH` and the output was **true**
140 | - [ ] If more than one circuit instance, I have correctly set the *sequenceNumber* from 1 to n circuits
141 |
--------------------------------------------------------------------------------
/web/src/helpers/constants.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Commonly used terms.
3 | * @dev useful for creating paths, references to collections and queries, object properties, folder names, and so on.
4 | */
5 | export const commonTerms = {
6 | collections: {
7 | users: {
8 | name: "users",
9 | fields: {
10 | creationTime: "creationTime",
11 | displayName: "displayName",
12 | email: "email",
13 | emailVerified: "emailVerified",
14 | lastSignInTime: "lastSignInTime",
15 | lastUpdated: "lastUpdated",
16 | name: "name",
17 | photoURL: "photoURL"
18 | }
19 | },
20 | participants: {
21 | name: "participants",
22 | fields: {
23 | contributionProgress: "contributionProgress",
24 | contributionStartedAt: "contributionStartedAt",
25 | contributionStep: "contributionStep",
26 | contributions: "contributions",
27 | lastUpdated: "lastUpdated",
28 | status: "status",
29 | verificationStartedAt: "verificationStartedAt"
30 | }
31 | },
32 | avatars: {
33 | name: "avatars",
34 | fields: {
35 | avatarUrl: "avatarUrl"
36 | }
37 | },
38 | ceremonies: {
39 | name: "ceremonies",
40 | fields: {
41 | coordinatorId: "coordinatorId",
42 | description: "description",
43 | endDate: "endDate",
44 | lastUpdated: "lastUpdated",
45 | penalty: "penalty",
46 | prefix: "prefix",
47 | startDate: "startDate",
48 | state: "state",
49 | timeoutType: "timeoutType",
50 | title: "title",
51 | type: "type"
52 | }
53 | },
54 | circuits: {
55 | name: "circuits",
56 | fields: {
57 | avgTimings: "avgTimings",
58 | compiler: "compiler",
59 | description: "description",
60 | files: "files",
61 | lastUpdated: "lastUpdated",
62 | metadata: "metadata",
63 | name: "name",
64 | prefix: "prefix",
65 | sequencePosition: "sequencePosition",
66 | template: "template",
67 | timeoutMaxContributionWaitingTime: "timeoutMaxContributionWaitingTime",
68 | waitingQueue: "waitingQueue",
69 | zKeySizeInBytes: "zKeySizeInBytes",
70 | verification: "verification"
71 | }
72 | },
73 | contributions: {
74 | name: "contributions",
75 | fields: {
76 | contributionComputationTime: "contributionComputationTime",
77 | files: "files",
78 | lastUpdated: "lastUpdated",
79 | participantId: "participantId",
80 | valid: "valid",
81 | verificationComputationTime: "verificationComputationTime",
82 | zkeyIndex: "zKeyIndex"
83 | }
84 | },
85 | timeouts: {
86 | name: "timeouts",
87 | fields: {
88 | type: "type",
89 | startDate: "startDate",
90 | endDate: "endDate"
91 | }
92 | }
93 | },
94 | foldersAndPathsTerms: {
95 | output: `output`,
96 | setup: `setup`,
97 | contribute: `contribute`,
98 | finalize: `finalize`,
99 | pot: `pot`,
100 | zkeys: `zkeys`,
101 | wasm: `wasm`,
102 | vkeys: `vkeys`,
103 | metadata: `metadata`,
104 | transcripts: `transcripts`,
105 | attestation: `attestation`,
106 | verifiers: `verifiers`
107 | },
108 | cloudFunctionsNames: {
109 | setupCeremony: "setupCeremony",
110 | checkParticipantForCeremony: "checkParticipantForCeremony",
111 | progressToNextCircuitForContribution: "progressToNextCircuitForContribution",
112 | resumeContributionAfterTimeoutExpiration: "resumeContributionAfterTimeoutExpiration",
113 | createBucket: "createBucket",
114 | generateGetObjectPreSignedUrl: "generateGetObjectPreSignedUrl",
115 | progressToNextContributionStep: "progressToNextContributionStep",
116 | permanentlyStoreCurrentContributionTimeAndHash: "permanentlyStoreCurrentContributionTimeAndHash",
117 | startMultiPartUpload: "startMultiPartUpload",
118 | temporaryStoreCurrentContributionMultiPartUploadId: "temporaryStoreCurrentContributionMultiPartUploadId",
119 | temporaryStoreCurrentContributionUploadedChunkData: "temporaryStoreCurrentContributionUploadedChunkData",
120 | generatePreSignedUrlsParts: "generatePreSignedUrlsParts",
121 | completeMultiPartUpload: "completeMultiPartUpload",
122 | checkIfObjectExist: "checkIfObjectExist",
123 | verifyContribution: "verifycontribution",
124 | checkAndPrepareCoordinatorForFinalization: "checkAndPrepareCoordinatorForFinalization",
125 | finalizeCircuit: "finalizeCircuit",
126 | finalizeCeremony: "finalizeCeremony",
127 | downloadCircuitArtifacts: "downloadCircuitArtifacts",
128 | transferObject: "transferObject",
129 | }
130 | }
131 |
132 | const outputLocalFolderPath = `./${commonTerms.foldersAndPathsTerms.output}`
133 | const setupLocalFolderPath = `${outputLocalFolderPath}/${commonTerms.foldersAndPathsTerms.setup}`
134 | const contributeLocalFolderPath = `${outputLocalFolderPath}/${commonTerms.foldersAndPathsTerms.contribute}`
135 | const finalizeLocalFolderPath = `${outputLocalFolderPath}/${commonTerms.foldersAndPathsTerms.finalize}`
136 | const potLocalFolderPath = `${setupLocalFolderPath}/${commonTerms.foldersAndPathsTerms.pot}`
137 | const zkeysLocalFolderPath = `${setupLocalFolderPath}/${commonTerms.foldersAndPathsTerms.zkeys}`
138 | const wasmLocalFolderPath = `${setupLocalFolderPath}/${commonTerms.foldersAndPathsTerms.wasm}`
139 | const contributionsLocalFolderPath = `${contributeLocalFolderPath}/${commonTerms.foldersAndPathsTerms.zkeys}`
140 | const contributionTranscriptsLocalFolderPath = `${contributeLocalFolderPath}/${commonTerms.foldersAndPathsTerms.transcripts}`
141 | const attestationLocalFolderPath = `${contributeLocalFolderPath}/${commonTerms.foldersAndPathsTerms.attestation}`
142 | const finalZkeysLocalFolderPath = `${finalizeLocalFolderPath}/${commonTerms.foldersAndPathsTerms.zkeys}`
143 | const finalPotLocalFolderPath = `${finalizeLocalFolderPath}/${commonTerms.foldersAndPathsTerms.pot}`
144 | const finalTranscriptsLocalFolderPath = `${finalizeLocalFolderPath}/${commonTerms.foldersAndPathsTerms.transcripts}`
145 | const finalAttestationsLocalFolderPath = `${finalizeLocalFolderPath}/${commonTerms.foldersAndPathsTerms.attestation}`
146 | const verificationKeysLocalFolderPath = `${finalizeLocalFolderPath}/${commonTerms.foldersAndPathsTerms.vkeys}`
147 | const verifierContractsLocalFolderPath = `${finalizeLocalFolderPath}/${commonTerms.foldersAndPathsTerms.verifiers}`
148 |
149 | export const localPaths = {
150 | output: outputLocalFolderPath,
151 | setup: setupLocalFolderPath,
152 | contribute: contributeLocalFolderPath,
153 | finalize: finalizeLocalFolderPath,
154 | pot: potLocalFolderPath,
155 | zkeys: zkeysLocalFolderPath,
156 | wasm: wasmLocalFolderPath,
157 | contributions: contributionsLocalFolderPath,
158 | transcripts: contributionTranscriptsLocalFolderPath,
159 | attestations: attestationLocalFolderPath,
160 | finalZkeys: finalZkeysLocalFolderPath,
161 | finalPot: finalPotLocalFolderPath,
162 | finalTranscripts: finalTranscriptsLocalFolderPath,
163 | finalAttestations: finalAttestationsLocalFolderPath,
164 | verificationKeys: verificationKeysLocalFolderPath,
165 | verifierContracts: verifierContractsLocalFolderPath
166 | }
167 |
168 | export const genesisZkeyIndex = "00000"
169 | export const finalContributionIndex = "final"
170 | export const bucketPostfix = import.meta.env.VITE_CONFIG_CEREMONY_BUCKET_POSTFIX
171 | export const minRepos = import.meta.env.VITE_GITHUB_REPOS
172 | export const minFollowers = import.meta.env.VITE_GITHUB_FOLLOWERS
173 | export const minFollowing = import.meta.env.VITE_GITHUB_FOLLOWING
174 | export const verifyContributionURL = import.meta.env.VITE_FIREBASE_CF_URL_VERIFY_CONTRIBUTION
175 | export const apiKey = import.meta.env.VITE_FIREBASE_API_KEY
176 | export const authDomain = import.meta.env.VITE_FIREBASE_AUTH_DOMAIN
177 | export const projectId = import.meta.env.VITE_FIREBASE_PROJECT_ID
178 | export const messagingSenderId = import.meta.env.VITE_FIREBASE_MESSAGING_SENDER_ID
179 | export const appId = import.meta.env.VITE_FIREBASE_APP_ID
180 | export const awsRegion = import.meta.env.VITE_AWS_REGION
181 | export const maxConstraintsForBrowser = 1500000
182 |
--------------------------------------------------------------------------------
/web/src/helpers/functions.ts:
--------------------------------------------------------------------------------
1 | import { httpsCallable, httpsCallableFromURL } from "firebase/functions"
2 | import { commonTerms } from "./constants"
3 | import { firebaseFunctions } from "./firebase"
4 | import { DocumentSnapshot, onSnapshot } from "firebase/firestore"
5 | import { ETagWithPartNumber, FirebaseDocumentInfo } from "./interfaces"
6 |
7 | /**
8 | * Return a pre-signed url for a given object contained inside the provided AWS S3 bucket in order to perform a GET request.
9 | * @param bucketName - the name of the ceremony bucket.
10 | * @param objectKey - the storage path that locates the artifact to be downloaded in the bucket.
11 | * @returns > - the pre-signed url w/ GET request permissions for specified object key.
12 | */
13 | export const generateGetObjectPreSignedUrl = async (
14 | bucketName: string,
15 | objectKey: string
16 | ): Promise => {
17 | const cf = httpsCallable(firebaseFunctions, commonTerms.cloudFunctionsNames.generateGetObjectPreSignedUrl)
18 |
19 | const { data: getPreSignedUrl } = await cf({
20 | bucketName,
21 | objectKey
22 | })
23 |
24 | return String(getPreSignedUrl)
25 | }
26 |
27 | /**
28 | * Check the user's current participant status for the ceremony
29 | * @param ceremonyId - the unique identifier of the ceremony.
30 | * @returns - true when participant is able to contribute; otherwise false.
31 | */
32 | export const checkParticipantForCeremony = async (
33 | ceremonyId: string
34 | ): Promise => {
35 | const cf = httpsCallable(firebaseFunctions, "checkParticipantForCeremony")
36 |
37 | const { data } = await cf({ ceremonyId })
38 |
39 | return data
40 | }
41 |
42 |
43 | /**
44 | * Progress the participant to the next circuit preparing for the next contribution.
45 | * @param ceremonyId - the unique identifier of the ceremony.
46 | */
47 | export const progressToNextCircuitForContribution = async (ceremonyId: string): Promise => {
48 | const cf = httpsCallable(firebaseFunctions, "progressToNextCircuitForContribution")
49 |
50 | await cf({
51 | ceremonyId
52 | })
53 | }
54 |
55 | /**
56 | * Progress the participant to the next circuit preparing for the next contribution.
57 | * @param ceremonyId - the unique identifier of the ceremony.
58 | */
59 | export const progressToNextContributionStep = async (ceremonyId: string) => {
60 | const cf = httpsCallable(firebaseFunctions, commonTerms.cloudFunctionsNames.progressToNextContributionStep)
61 |
62 | await cf({
63 | ceremonyId
64 | })
65 | }
66 |
67 |
68 | /**
69 | * Write the information about current contribution hash and computation time for the current contributor.
70 | * @param functions - the Firebase cloud functions object instance.
71 | * @param ceremonyId - the unique identifier of the ceremony.
72 | * @param contributionComputationTime - the time when it was computed
73 | * @param contributingHash - the hash of the contribution
74 | */
75 | export const permanentlyStoreCurrentContributionTimeAndHash = async (
76 | ceremonyId: string,
77 | contributionComputationTime: number,
78 | contributionHash: string
79 | ) => {
80 | const cf = httpsCallable(firebaseFunctions, commonTerms.cloudFunctionsNames.permanentlyStoreCurrentContributionTimeAndHash)
81 | await cf({
82 | ceremonyId,
83 | contributionComputationTime,
84 | contributionHash
85 | })
86 | }
87 |
88 | /**
89 | * Start a new multi-part upload for a specific object in the given AWS S3 bucket.
90 | * @param bucketName - the name of the ceremony bucket.
91 | * @param objectKey - the storage path that locates the artifact to be downloaded in the bucket.
92 | * @param ceremonyId - the unique identifier of the ceremony.
93 | * @returns Promise - the multi-part upload id.
94 | */
95 | export const openMultiPartUpload = async (
96 | bucketName: string,
97 | objectKey: string,
98 | ceremonyId?: string
99 | ): Promise => {
100 | const cf = httpsCallable(firebaseFunctions, commonTerms.cloudFunctionsNames.startMultiPartUpload)
101 |
102 | const { data: uploadId } = await cf({
103 | bucketName,
104 | objectKey,
105 | ceremonyId
106 | })
107 |
108 | return String(uploadId)
109 | }
110 |
111 | /**
112 | * Write temporary information about the unique identifier about the opened multi-part upload to eventually resume the contribution.
113 | * @param ceremonyId - the unique identifier of the ceremony.
114 | * @param uploadId - the unique identifier of the multi-part upload.
115 | */
116 | export const temporaryStoreCurrentContributionMultiPartUploadId = async (
117 | ceremonyId: string,
118 | uploadId: string
119 | ) => {
120 | const cf = httpsCallable(
121 | firebaseFunctions,
122 | commonTerms.cloudFunctionsNames.temporaryStoreCurrentContributionMultiPartUploadId
123 | )
124 |
125 | await cf({
126 | ceremonyId,
127 | uploadId
128 | })
129 | }
130 |
131 |
132 | /**
133 | * Request to verify the newest contribution for the circuit.
134 | * @param ceremonyId - the unique identifier of the ceremony.
135 | * @param circuit - the document info about the circuit.
136 | * @param bucketName - the name of the ceremony bucket.
137 | * @param contributorOrCoordinatorIdentifier - the identifier of the contributor or coordinator (only when finalizing).
138 | * @param verifyContributionCloudFunctionEndpoint - the endpoint (direct url) necessary to call the V2 Cloud Function.
139 | * @returns > -
140 | */
141 | export const verifyContribution = async (
142 | ceremonyId: string,
143 | circuit: FirebaseDocumentInfo, // any just to avoid breaking the tests.
144 | bucketName: string,
145 | contributorOrCoordinatorIdentifier: string,
146 | verifyContributionCloudFunctionEndpoint: string
147 | ): Promise => {
148 | const cf = httpsCallableFromURL(firebaseFunctions, verifyContributionCloudFunctionEndpoint, {
149 | timeout: 3600000 // max timeout 60 minutes.
150 | })
151 |
152 | /**
153 | * @dev Force a race condition to fix #57.
154 | * TL;DR if the cloud function does not return despite having finished its execution, we use
155 | * a listener on the circuit, we check and retrieve the info about the correct execution and
156 | * return it manually. In other cases, it will be the function that returns either a timeout in case it
157 | * remains in execution for too long.
158 | */
159 | await Promise.race([
160 | cf({
161 | ceremonyId,
162 | circuitId: circuit.id,
163 | contributorOrCoordinatorIdentifier,
164 | bucketName
165 | }),
166 | new Promise((resolve): any => {
167 | setTimeout(() => {
168 | const unsubscribeToCeremonyCircuitListener = onSnapshot(
169 | circuit.ref,
170 | async (changedCircuit: DocumentSnapshot) => {
171 | // Check data.
172 | if (!circuit.data || !changedCircuit.data())
173 | throw Error(`Unable to retrieve circuit data from the ceremony.`)
174 |
175 | // Extract data.
176 | const { avgTimings: changedAvgTimings, waitingQueue: changedWaitingQueue } =
177 | changedCircuit.data()!
178 | const {
179 | contributionComputation: changedContributionComputation,
180 | fullContribution: changedFullContribution,
181 | verifyCloudFunction: changedVerifyCloudFunction
182 | } = changedAvgTimings
183 | const {
184 | failedContributions: changedFailedContributions,
185 | completedContributions: changedCompletedContributions
186 | } = changedWaitingQueue
187 |
188 | const { avgTimings: prevAvgTimings, waitingQueue: prevWaitingQueue } = changedCircuit.data()!
189 | const {
190 | contributionComputation: prevContributionComputation,
191 | fullContribution: prevFullContribution,
192 | verifyCloudFunction: prevVerifyCloudFunction
193 | } = prevAvgTimings
194 | const {
195 | failedContributions: prevFailedContributions,
196 | completedContributions: prevCompletedContributions
197 | } = prevWaitingQueue
198 |
199 | // Pre-conditions.
200 | const invalidContribution = prevFailedContributions === changedFailedContributions - 1
201 | const validContribution = prevCompletedContributions === changedCompletedContributions - 1
202 | const avgTimeUpdates =
203 | prevContributionComputation !== changedContributionComputation &&
204 | prevFullContribution !== changedFullContribution &&
205 | prevVerifyCloudFunction !== changedVerifyCloudFunction
206 |
207 | if ((invalidContribution || validContribution) && avgTimeUpdates) {
208 | resolve({})
209 | }
210 | }
211 | )
212 |
213 | // Unsubscribe from listener.
214 | unsubscribeToCeremonyCircuitListener()
215 | }, 3600000 - 1000) // 59:59 throws 1s before max time for CF execution.
216 | })
217 | ])
218 | }
219 |
220 | /**
221 | * Resume the contributor circuit contribution from scratch after the timeout expiration.
222 | * @param ceremonyId - the unique identifier of the ceremony.
223 | */
224 | export const resumeContributionAfterTimeoutExpiration = async (
225 | ceremonyId: string
226 | ): Promise => {
227 | const cf = httpsCallable(firebaseFunctions, commonTerms.cloudFunctionsNames.resumeContributionAfterTimeoutExpiration)
228 |
229 | await cf({
230 | ceremonyId
231 | })
232 | }
233 |
234 | /**
235 | * Generate a new pre-signed url for each chunk related to a started multi-part upload.
236 | * @param bucketName - the name of the ceremony bucket.
237 | * @param objectKey - the storage path that locates the artifact to be downloaded in the bucket.
238 | * @param uploadId - the unique identifier of the multi-part upload.
239 | * @param numberOfChunks - the number of pre-signed urls to be generated.
240 | * @param ceremonyId - the unique identifier of the ceremony.
241 | * @returns Promise> - the set of pre-signed urls (one for each chunk).
242 | */
243 | export const generatePreSignedUrlsParts = async (
244 | bucketName: string,
245 | objectKey: string,
246 | uploadId: string,
247 | numberOfParts: number,
248 | ceremonyId?: string
249 | ): Promise> => {
250 | const cf = httpsCallable(firebaseFunctions, commonTerms.cloudFunctionsNames.generatePreSignedUrlsParts)
251 |
252 | const { data: chunksUrls }: any = await cf({
253 | bucketName,
254 | objectKey,
255 | uploadId,
256 | numberOfParts,
257 | ceremonyId
258 | })
259 |
260 | return chunksUrls
261 | }
262 |
263 | /**
264 | * Write temporary information about the etags and part numbers for each uploaded chunk in order to make the upload resumable from last chunk.
265 | * @param ceremonyId - the unique identifier of the ceremony.
266 | * @param chunk - the information about the already uploaded chunk.
267 | */
268 | export const temporaryStoreCurrentContributionUploadedChunkData = async (
269 | ceremonyId: string,
270 | chunk: ETagWithPartNumber
271 | ) => {
272 | const cf = httpsCallable(
273 | firebaseFunctions,
274 | commonTerms.cloudFunctionsNames.temporaryStoreCurrentContributionUploadedChunkData
275 | )
276 | await cf({
277 | ceremonyId,
278 | chunk
279 | })
280 | }
281 |
282 | /**
283 | * Complete a multi-part upload for a specific object in the given AWS S3 bucket.
284 | * @param bucketName - the name of the ceremony bucket.
285 | * @param objectKey - the storage path that locates the artifact to be downloaded in the bucket.
286 | * @param uploadId - the unique identifier of the multi-part upload.
287 | * @param parts Array - the completed .
288 | * @param ceremonyId - the unique identifier of the ceremony.
289 | * @returns Promise - the location of the uploaded ceremony artifact.
290 | */
291 | export const completeMultiPartUpload = async (
292 | bucketName: string,
293 | objectKey: string,
294 | uploadId: string,
295 | parts: Array,
296 | ceremonyId?: string
297 | ): Promise => {
298 | // Call completeMultiPartUpload() Cloud Function.
299 | const cf = httpsCallable(firebaseFunctions, commonTerms.cloudFunctionsNames.completeMultiPartUpload)
300 |
301 | const { data: location }: any = await cf({
302 | bucketName,
303 | objectKey,
304 | uploadId,
305 | parts,
306 | ceremonyId
307 | })
308 |
309 | return String(location)
310 | }
--------------------------------------------------------------------------------
/ceremonies/setup_ceremony_config.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Defaults
4 | fixed_time_window=10
5 | dynamic_threshold=10
6 | penalty=10
7 |
8 | # Check if jq is installed
9 | if ! [ -x "$(command -v jq)" ]; then
10 | echo 'Error: jq is not installed. Please install jq and try again.' >&2
11 | exit 1
12 | fi
13 |
14 | # Check if AWS cli is installed
15 | if ! [ -x "$(command -v aws)" ]; then
16 | echo 'Error: aws is not installed. Please install aws, setup a profile and try again.' >&2
17 | echo 'More info on how to install and setup can be found here https://aws.amazon.com/cli/' >&2
18 | exit 1
19 | fi
20 |
21 | # Check if circom is installed
22 | if ! [ -x "$(command -v circom)" ]; then
23 | echo 'Error: circom is not installed. Please install circom and try again.' >&2
24 | echo 'You can install circom following the instructions here: https://docs.circom.io/getting-started/installation/' >&2
25 | echo 'Please note it is your responsibility to ensure that the correct version of circom is installed.' >&2
26 | exit 1
27 | fi
28 |
29 | echo -e '\033[35m'
30 | echo '██████╗ ███████╗███████╗██╗███╗ ██╗██╗████████╗███████╗██╗ ██╗ ██╗███████╗███████╗████████╗██╗ ██╗██████╗ '
31 | echo '██╔══██╗██╔════╝██╔════╝██║████╗ ██║██║╚══██╔══╝██╔════╝██║ ╚██╗ ██╔╝██╔════╝██╔════╝╚══██╔══╝██║ ██║██╔══██╗'
32 | echo '██║ ██║█████╗ █████╗ ██║██╔██╗ ██║██║ ██║ █████╗ ██║ ╚████╔╝ ███████╗█████╗ ██║ ██║ ██║██████╔╝'
33 | echo '██║ ██║██╔══╝ ██╔══╝ ██║██║╚██╗██║██║ ██║ ██╔══╝ ██║ ╚██╔╝ ╚════██║██╔══╝ ██║ ██║ ██║██╔═══╝ '
34 | echo '██████╔╝███████╗██║ ██║██║ ╚████║██║ ██║ ███████╗███████╗██║ ███████║███████╗ ██║ ╚██████╔╝██║ '
35 | echo '╚═════╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝ ╚══════╝╚══════╝╚═╝ ╚══════╝╚══════╝ ╚═╝ ╚═════╝ ╚═╝'
36 | echo -e '\033[0m'
37 | echo -e '\nWelcome to \033[35mDefinitelySetup\033[0m p0tion config generator! '
38 | echo -e "\nPlease ensure that each pair of r1cs and wasm files have the same name."
39 |
40 | # Ask for AWS details
41 | echo -e "\nEnter AWS region:"
42 | read region
43 | echo -e "\nEnter AWS profile:"
44 | read profile
45 | echo -e "\nEnter S3 bucket name:"
46 | read bucket
47 | echo -e "\nEnter the directory where the files are located:"
48 | read directory
49 |
50 | # Remove trailing slash if present:
51 | directory=${directory%/}
52 |
53 | # Ask if circuits are compiled
54 | are_compiled=""
55 | while [[ "$are_compiled" != "yes" && "$are_compiled" != "no" ]]; do
56 | echo -e "\nAre the circuits already compiled to r1cs and wasm? (yes/no)"
57 | read are_compiled
58 | done
59 |
60 | # If not, compile them
61 | if [ "$are_compiled" = "no" ]; then
62 | echo -e "\nCompiling circuits..."
63 | for file in $directory/*.circom
64 | do
65 | circom "$file" --r1cs --wasm --output $directory 2>/dev/null
66 | done
67 | fi
68 |
69 | # Ask for top level information
70 | echo -e "\nEnter title:"
71 | read title
72 | echo -e "\nEnter description:"
73 | read description
74 |
75 | # Determine OS
76 | os=$(uname)
77 |
78 | if [[ "$os" == "Darwin" ]]; then
79 | # macOS commands
80 | startDate=$(date -u -v+7d +'%Y-%m-%dT%H:%M:%S')
81 | endDate=$(date -u -v+1m -v+7d +'%Y-%m-%dT%H:%M:%S')
82 | elif [[ "$os" == "Linux" ]]; then
83 | # Linux commands
84 | startDate=$(date -u --date='1 week' +'%Y-%m-%dT%H:%M:%S')
85 | endDate=$(date -u --date='1 month 1 week' +'%Y-%m-%dT%H:%M:%S')
86 | else
87 | echo -e "\nError: Unsupported OS. Please use macOS or Linux." >&2
88 | exit 1
89 | fi
90 |
91 | timeoutMechanismType=""
92 | while [[ "$timeoutMechanismType" != "FIXED" && "$timeoutMechanismType" != "DYNAMIC" ]]; do
93 | echo -e "\nEnter timeout mechanism type (FIXED/DYNAMIC):"
94 | read timeoutMechanismType
95 | done
96 |
97 | compiler_version=$(circom --version | awk '{ print $3 }')
98 |
99 | echo -e "\nIs the compiler the same for all circuits? (yes/no)"
100 | read same_compiler
101 |
102 | if [ "$same_compiler" = "yes" ]; then
103 | # determine circom details
104 | case "$compiler_version" in
105 | "2.1.6")
106 | compiler_commit_hash="57b18f68794189753964bfb6e18e64385fed9c2c"
107 | ;;
108 | "2.1.5")
109 | compiler_commit_hash="127414e9088cc017a357233f30f3fd7d91a8906c"
110 | ;;
111 | "2.1.4")
112 | compiler_commit_hash="ca3345681549c859af1f3f42128e53e3e43fe5e2"
113 | ;;
114 | "2.1.3")
115 | compiler_commit_hash="1dadb4aea6551c774b5d1b65fdd4d42567d3064e"
116 | ;;
117 | "2.1.2")
118 | compiler_commit_hash="2fbf9657b37488fc4bb37a51283c4bc7d424f0cb"
119 | ;;
120 | "2.1.1")
121 | compiler_commit_hash="95f39184ae1a56fbfe736863156e2c70d083c658"
122 | ;;
123 | "2.1.0")
124 | compiler_commit_hash="b7ad01b11f9b4195e38ecc772291251260ab2c67"
125 | ;;
126 | "2.0.9")
127 | compiler_commit_hash="bdc9d9d57490f113161f3adf818120f064b7b5b2"
128 | ;;
129 | "2.0.8")
130 | compiler_commit_hash="5703a808f82f7ff941ed08b6616a85fb233f9950"
131 | ;;
132 | "2.0.7")
133 | compiler_commit_hash="c0d74c2b0d8b0a61b84e292b3ab16277285b5abf"
134 | ;;
135 | "2.0.6")
136 | compiler_commit_hash="3fa5592c41a59a8a79cb6a328606c64a4b451762"
137 | ;;
138 | "2.0.5")
139 | compiler_commit_hash="ed807764a17ce06d8307cd611ab6b917247914f5"
140 | ;;
141 | "2.0.4")
142 | compiler_commit_hash="58449c21da772267d23da2636f054030f0e11d15"
143 | ;;
144 | "2.0.3")
145 | compiler_commit_hash="68762c517e01dd8e309066e7b1bffe400aad9461"
146 | ;;
147 | "2.0.2")
148 | compiler_commit_hash="6b8e1dca925a922608859c8935d2c84b716d545e"
149 | ;;
150 | "2.0.1")
151 | compiler_commit_hash="b77966a3497a4d2505197f68f56860296e962b74"
152 | ;;
153 | *)
154 | echo -e "\nUnsupported circom version. Please use a version from the list."
155 | exit 1
156 | ;;
157 | esac
158 | fi
159 |
160 | echo -e "\nIs the template source and commit hash the same for all circuits? (yes/no)"
161 | read same_template
162 |
163 | if [ "$same_template" = "yes" ]; then
164 | echo -e "\nEnter template source:"
165 | read template_source
166 | echo -e "\nEnter template commit hash:"
167 | read template_commit_hash
168 | fi
169 |
170 | same_verification=""
171 | while [[ "$same_verification" != "yes" && "$same_verification" != "no" ]]; do
172 | echo -e "\nIs the verification method the same for all circuits? (yes/no)"
173 | read same_verification
174 | done
175 |
176 | if [ "$same_verification" = "yes" ]; then
177 | verification_method=""
178 | while [[ "$verification_method" != "CF" && "$verification_method" != "VM" ]]; do
179 | echo -e "\nEnter verification method (CF/VM):"
180 | read verification_method
181 | done
182 |
183 | if [ "$verification_method" = "VM" ]; then
184 | echo -e "\nTo find ec2 details please visit: https://aws.amazon.com/ec2/instance-types/"
185 | echo -e "\nEnter VM configuration type:"
186 | read vm_configuration_type
187 | echo -e "\nEnter VM disk size (number):"
188 | read vm_disk_size
189 | echo -e "\nEnter VM disk type:"
190 | read vm_disk_type
191 | fi
192 | fi
193 |
194 | # Find .r1cs and .wasm files recursively:
195 | # Create a temporary directory
196 | tmp_dir=$(mktemp -d $PWD/temp_dir_XXXXXXXX)
197 |
198 | # Copy all .wasm and .r1cs files to the temporary directory
199 | find $directory -type f \( -name "*.wasm" -o -name "*.r1cs" \) -exec cp {} $tmp_dir/ \;
200 |
201 | # Get a list of all files in the temporary directory
202 | file_list=$(ls $tmp_dir/*.{r1cs,wasm})
203 |
204 | echo -e "\nFile list: \n$file_list"
205 |
206 | # Create a new array to hold processed files
207 | processed_files=()
208 |
209 | circuits_array=()
210 |
211 | index=1
212 | for file_path in $file_list
213 | do
214 | # Get the base name
215 | base_name="${file_path%.*}"
216 |
217 | # Skip this iteration if this file has already been processed
218 | if [[ " ${processed_files[@]} " =~ " ${base_name} " ]]; then
219 | continue
220 | fi
221 |
222 | # Otherwise, add this base name to the processed files array
223 | processed_files+=("$base_name")
224 |
225 | # Upload
226 | echo -e "\nUploading $base_name.r1cs to $bucket in $region..."
227 | aws s3 cp $base_name.r1cs s3://$bucket/ --acl public-read --profile $profile --region $region
228 |
229 | echo -e "\nUploading $base_name.wasm to $bucket in $region..."
230 | aws s3 cp $base_name.wasm s3://$bucket/ --acl public-read --profile $profile --region $region
231 |
232 | # Get the file name only
233 | base_name=$(basename "${file_path%.*}")
234 |
235 | if [ "$same_compiler" = "no" ]; then
236 | echo -e "\nEnter compiler version:"
237 | read compiler_version
238 | echo -e "\nEnter compiler commit hash:"
239 | read compiler_commit_hash
240 | fi
241 |
242 | if [ "$same_template" = "no" ]; then
243 | echo -e "\nEnter template source:"
244 | read template_source
245 | echo -e "\nEnter template commit hash:"
246 | read template_commit_hash
247 | fi
248 |
249 | if [ "$same_verification" = "no" ]; then
250 | echo -e "\nEnter verification method (CF/VM):"
251 | read verification_method
252 |
253 | if [ "$verification_method" = "VM" ]; then
254 | echo -e "\nEnter VM configuration type:"
255 | read vm_configuration_type
256 | echo -e "\nEnter VM disk size (number):"
257 | read vm_disk_size
258 | echo -e "\nEnter VM disk type:"
259 | read vm_disk_type
260 | fi
261 | fi
262 |
263 | echo -e "\nEnter circuit description:"
264 | read circuit_description
265 |
266 | echo -e "\nEnter paramsConfiguration values as a comma-separated list (e.g. 6,8,3,2):"
267 | read param_str
268 | params=$(jq -nR '[(input | split(",")[] | tonumber)]' <<< "$param_str")
269 |
270 | # Create circuit object
271 | circuit=$(jq -n \
272 | --arg compiler_version "$compiler_version" \
273 | --arg compiler_commit_hash "$compiler_commit_hash" \
274 | --arg template_source "$template_source" \
275 | --arg template_commit_hash "$template_commit_hash" \
276 | --arg circuit_description "$circuit_description" \
277 | --arg bucket "$bucket" \
278 | --arg region "$region" \
279 | --arg index "$index" \
280 | --arg verification_method "$verification_method" \
281 | --arg vm_configuration_type "$vm_configuration_type" \
282 | --arg vm_disk_size "$vm_disk_size" \
283 | --arg vm_disk_type "$vm_disk_type" \
284 | --arg base_name "$base_name" \
285 | --argjson params "$params" \
286 | '{
287 | "description": $circuit_description,
288 | "compiler": {"version": $compiler_version, "commitHash": $compiler_commit_hash},
289 | "template": {"source": $template_source, "commitHash": $template_commit_hash, "paramsConfiguration": $params},
290 | "verification": ($verification_method | if . == "CF" then {"cfOrVm": "CF"} else {"cfOrVm": "VM", "vmConfigurationType": $vm_configuration_type, "vmDiskSize": $vm_disk_size|tonumber, "vmDiskType": $vm_disk_type} end),
291 | "artifacts": {"r1csStoragePath": ("https://" + $bucket + ".s3." + $region + ".amazonaws.com/" + $base_name + ".r1cs"), "wasmStoragePath": ("https://" + $bucket + ".s3." + $region + ".amazonaws.com/" + $base_name + ".wasm")},
292 | "name": $base_name,
293 | "sequencePosition": $index|tonumber
294 | }')
295 |
296 | # Conditionally add dynamicThreshold or fixedTimeWindow
297 | if [ "$timeoutMechanismType" = "FIXED" ]; then
298 | circuit=$(echo "$circuit" | jq --arg fixed_time_window "$fixed_time_window" '. + {fixedTimeWindow: $fixed_time_window|tonumber}')
299 | elif [ "$timeoutMechanismType" = "DYNAMIC" ]; then
300 | circuit=$(echo "$circuit" | jq --arg dynamic_threshold "$dynamic_threshold" '. + {dynamicThreshold: $dynamic_threshold|tonumber}')
301 | fi
302 |
303 | # append to the circuits array
304 | circuits_array+=("$circuit")
305 |
306 | index=$((index+1))
307 | done
308 |
309 | # convert bash array to json array
310 | circuits_json=$(echo -e "${circuits_array[@]}" | tr ' ' '\n' | jq -s -c '.')
311 |
312 | # Create json object
313 | json=$(jq -n \
314 | --arg title "$title" \
315 | --arg description "$description" \
316 | --arg startDate "$startDate" \
317 | --arg endDate "$endDate" \
318 | --arg timeoutMechanismType "$timeoutMechanismType" \
319 | --arg penalty "$penalty" \
320 | --argjson circuits "$circuits_json" \
321 | '{
322 | "title": $title,
323 | "description": $description,
324 | "startDate": $startDate,
325 | "endDate": $endDate,
326 | "timeoutMechanismType": $timeoutMechanismType,
327 | "penalty": $penalty|tonumber,
328 | "circuits": $circuits
329 | }')
330 |
331 | echo "$json" > p0tionConfig.json
332 |
333 | prefix=$(echo "${title}" | tr '[:upper:]' '[:lower:]' | tr ' ' '-' | tr -dc '[:alnum:]-\n\r')
334 |
335 | echo -e "\nCeremony json created. Please validate the p0tionConfig.json file before opening the PR. You can use phase2cli validate --template ./p0tionConfig.json"
336 | echo -e "\nParameters such as start date and end date are set automatically to be 1 week from now and to end in 1 month from now. Please feel free to change them. The same applies to contribution time window and penalty."
337 | echo -e "\nPlease open a PR named as $prefix and name the directory the same. The config file should be inside."
338 |
339 | # cleanup
340 | rm -r $tmp_dir
--------------------------------------------------------------------------------
/web/src/helpers/firebase.ts:
--------------------------------------------------------------------------------
1 | import {
2 | collection as collectionRef,
3 | doc,
4 | DocumentData,
5 | DocumentSnapshot,
6 | Firestore,
7 | getDoc,
8 | getDocs,
9 | QueryDocumentSnapshot,
10 | getFirestore,
11 | query,
12 | collection,
13 | where,
14 | QueryConstraint,
15 | QuerySnapshot
16 | } from "firebase/firestore"
17 | import { FirebaseApp, FirebaseOptions, initializeApp } from "firebase/app" // ref https://firebase.google.com/docs/web/setup#access-firebase.
18 | import { Functions, getFunctions } from "firebase/functions"
19 | import { getContributionsCollectionPath, getParticipantsCollectionPath, getTimeoutsCollectionPath, processItems } from "./utils"
20 | import { Auth, getAuth } from "firebase/auth"
21 | import { FirebaseDocumentInfo, WaitingQueue } from "./interfaces"
22 | import { apiKey, appId, authDomain, commonTerms, finalContributionIndex, messagingSenderId, projectId } from "./constants"
23 |
24 | // we init this here so we can use it throughout the functions below
25 | export let firestoreDatabase: Firestore
26 | export let firebaseApp: FirebaseApp
27 | export let firebaseAuth: Auth
28 | export let firebaseFunctions: Functions
29 |
30 | /**
31 | * This method initialize a Firebase app if no other app has already been initialized.
32 | * @param options - an object w/ every necessary Firebase option to init app.
33 | * @returns - the initialized Firebase app object.
34 | */
35 | const initializeFirebaseApp = (options: FirebaseOptions): FirebaseApp => initializeApp(options)
36 |
37 | /**
38 | * This method returns the Firestore database instance associated to the given Firebase application.
39 | * @param app - the Firebase application.
40 | * @returns - the Firebase Firestore associated to the application.
41 | */
42 | const getFirestoreDatabase = (app: FirebaseApp): Firestore => getFirestore(app)
43 |
44 | /**
45 | * Get circuits collection path for database reference.
46 | * @notice all circuits related documents are store under `ceremonies//circuits` collection path.
47 | * nb. This is a rule that must be satisfied. This is NOT an optional convention.
48 | * @param ceremonyId - the unique identifier of the ceremony.
49 | * @returns - the participants collection path.
50 | */
51 | export const getCircuitsCollectionPath = (ceremonyId: string): string =>
52 | `${commonTerms.collections.ceremonies.name}/${ceremonyId}/${commonTerms.collections.circuits.name}`
53 |
54 | /**
55 | * Return the core Firebase services instances (App, Database, Functions).
56 | * @param apiKey - the API key specified in the application config.
57 | * @param authDomain - the authDomain string specified in the application config.
58 | * @param projectId - the projectId specified in the application config.
59 | * @param messagingSenderId - the messagingSenderId specified in the application config.
60 | * @param appId - the appId specified in the application config.
61 | * @returns >
62 | */
63 | export const initializeFirebaseCoreServices = async (): Promise<{
64 | firebaseApp: FirebaseApp
65 | firestoreDatabase: Firestore
66 | firebaseFunctions: Functions
67 | }> => {
68 | const firebaseApp = initializeFirebaseApp({
69 | apiKey: apiKey,
70 | authDomain: authDomain,
71 | projectId: projectId,
72 | messagingSenderId: messagingSenderId,
73 | appId: appId
74 | })
75 | const firestoreDatabase = getFirestoreDatabase(firebaseApp)
76 | const firebaseFunctions = getFunctions(firebaseApp, "europe-west1")
77 |
78 | return {
79 | firebaseApp,
80 | firestoreDatabase,
81 | firebaseFunctions
82 | }
83 | }
84 |
85 | // Init the Firestore database instance.
86 | (async () => {
87 | const { firestoreDatabase: db, firebaseApp: app, firebaseFunctions: functions } = await initializeFirebaseCoreServices()
88 |
89 | firestoreDatabase = db
90 | firebaseApp = app
91 | firebaseAuth = getAuth(app)
92 | firebaseFunctions = functions
93 | })()
94 |
95 | /**
96 | * Fetch for all documents in a collection.
97 | * @param firestoreDatabase - the Firestore service instance associated to the current Firebase application.
98 | * @param collection - the name of the collection.
99 | * @returns >>> - return all documents (if any).
100 | */
101 | export const getAllCollectionDocs = async (
102 | collection: string
103 | ): Promise>> =>
104 | (await getDocs(collectionRef(firestoreDatabase, collection))).docs
105 |
106 | /**
107 | * Get a specific document from database.
108 | * @param firestoreDatabase - the Firestore service instance associated to the current Firebase application.
109 | * @param collection - the name of the collection.
110 | * @param documentId - the unique identifier of the document in the collection.
111 | * @returns >> - return the document from Firestore.
112 | */
113 | export const getDocumentById = async (
114 | collection: string,
115 | documentId: string
116 | ): Promise> => {
117 | const docRef = doc(firestoreDatabase, collection, documentId)
118 |
119 | return getDoc(docRef)
120 | }
121 |
122 | /**
123 | * Helper for obtaining uid and data for query document snapshots.
124 | * @param queryDocSnap > - the array of query document snapshot to be converted.
125 | * @returns Array
126 | */
127 | export const fromQueryToFirebaseDocumentInfo = (
128 | queryDocSnap: Array
129 | ): Array =>
130 | queryDocSnap.map((document: QueryDocumentSnapshot) => ({
131 | id: document.id,
132 | ref: document.ref,
133 | data: document.data()
134 | }))
135 |
136 | /**
137 | * Query for ceremony circuits.
138 | * @notice the order by sequence position is fundamental to maintain parallelism among contributions for different circuits.
139 | * @param firestoreDatabase - the Firestore service instance associated to the current Firebase application.
140 | * @param ceremonyId - the ceremony unique identifier.
141 | * @returns Promise> - the ceremony' circuits documents ordered by sequence position.
142 | */
143 | export const getCeremonyCircuits = async (
144 | ceremonyId: string
145 | ): Promise> =>
146 | fromQueryToFirebaseDocumentInfo(
147 | await getAllCollectionDocs(getCircuitsCollectionPath(ceremonyId))
148 | ).sort((a: any, b: any) => a.data.sequencePosition - b.data.sequencePosition)
149 |
150 |
151 | /**
152 | * Fetch all avatars for participants of a ceremony.
153 | * @param ceremonyId {string} - the ceremony unique identifier.
154 | * @returns {string[]} - An array of avatarURLs.
155 | */
156 | export const getParticipantsAvatar = async (
157 | ceremonyId: string,
158 | ): Promise => {
159 | // Get all participants of the ceremony
160 | const participantsDocs = await getAllCollectionDocs(`ceremonies/${ceremonyId}/participants`)
161 | const participantsData = fromQueryToFirebaseDocumentInfo(participantsDocs)
162 |
163 | // Get the IDs of the participants
164 | const participantIds = participantsData.map(participant => participant.id)
165 |
166 | // Chunk the IDs into groups of 10 or fewer due to Firestore's limitation
167 | const chunks: any[] = []
168 | while (participantIds.length) {
169 | chunks.push(participantIds.splice(0, 10))
170 | }
171 |
172 | // This function fetches avatars for a given chunk
173 | const fetchAvatarsForChunk = async (chunk: string[]): Promise => {
174 | const q = query(
175 | collection(firestoreDatabase, 'avatars'),
176 | where('__name__', 'in', chunk)
177 | );
178 |
179 | const avatarDocs = await getDocs(q)
180 |
181 | return avatarDocs.docs
182 | .filter(doc => doc.exists())
183 | .map(doc => doc.data().avatarUrl)
184 | };
185 |
186 | // Process all the chunks concurrently
187 | // @todo do something with the errors - for now ignore them
188 | const { results } = await processItems(chunks, fetchAvatarsForChunk, false)
189 | // Flattening the list of lists of avatar URLs
190 | const avatarURLs = results.flat()
191 |
192 | return avatarURLs
193 | }
194 |
195 |
196 | /**
197 | * Function to get contributions for each circuit
198 | * @param {Firestore} firestoreDatabase - the Firestore service instance associated to the current Firebase application.
199 | * @param {string} circuitId - the circuit unique identifier.
200 | * @param {string} ceremonyId - the ceremony unique identifier.
201 | * @returns {Array} - An array of contributions for the circuit.
202 | */
203 | export const getContributions = async (
204 | ceremonyId: string,
205 | circuitId: string
206 | ): Promise => {
207 | const contributionsDocs = await getAllCollectionDocs(`ceremonies/${ceremonyId}/circuits/${circuitId}/contributions`);
208 | return contributionsDocs.map((document: DocumentData) => ({ uid: document.id, data: document.data() }));
209 | }
210 |
211 | /**
212 | * Check and return the circuit document based on its sequence position among a set of circuits (if any).
213 | * @dev there should be only one circuit with a provided sequence position. This method checks and return an
214 | * error if none is found.
215 | * @param circuits > - the set of ceremony circuits documents.
216 | * @param sequencePosition - the sequence position (index) of the circuit to be found and returned.
217 | * @returns - the document of the circuit in the set of circuits that has the provided sequence position.
218 | */
219 | export const getCircuitBySequencePosition = (
220 | circuits: Array,
221 | sequencePosition: number
222 | ): FirebaseDocumentInfo => {
223 | // Filter by sequence position.
224 | const matchedCircuits = circuits.filter(
225 | (circuitDocument: FirebaseDocumentInfo) => circuitDocument.data.sequencePosition === sequencePosition
226 | )
227 |
228 | if (matchedCircuits.length !== 1)
229 | throw new Error(
230 | `Unable to find the circuit having position ${sequencePosition}. Run the command again and, if this error persists please contact the coordinator.`
231 | )
232 |
233 | return matchedCircuits[0]!
234 | }
235 |
236 |
237 | /**
238 | * Return the most up-to-date data about the participant document for the given ceremony.
239 | * @param firestoreDatabase - the Firestore service instance associated to the current Firebase application.
240 | * @param ceremonyId - the unique identifier of the ceremony.
241 | * @param participantId - the unique identifier of the participant.
242 | * @returns > - the most up-to-date participant data.
243 | */
244 | export const getLatestUpdatesFromParticipant = async (
245 | ceremonyId: string,
246 | participantId: string
247 | ): Promise => {
248 | // Fetch participant data.
249 | const participant = await getDocumentById(
250 | getParticipantsCollectionPath(ceremonyId),
251 | participantId
252 | )
253 |
254 | if (!participant.data()) return {}
255 |
256 | return participant.data()!
257 | }
258 |
259 | /**
260 | * Helper for query a collection based on certain constraints.
261 | * @param collection - the name of the collection.
262 | * @param queryConstraints > - a sequence of where conditions.
263 | * @returns >> - return the matching documents (if any).
264 | */
265 | export const queryCollection = async (
266 | collection: string,
267 | queryConstraints: Array
268 | ): Promise> => {
269 | // Make a query.
270 | const q = query(collectionRef(firestoreDatabase, collection), ...queryConstraints)
271 |
272 | // Get docs.
273 | const snap = await getDocs(q)
274 |
275 | return snap
276 | }
277 |
278 | /**
279 | * Query for a specific ceremony' circuit contribution from a given contributor (if any).
280 | * @notice if the caller is a coordinator, there could be more than one contribution (= the one from finalization applies to this criteria).
281 | * @param ceremonyId - the unique identifier of the ceremony.
282 | * @param circuitId - the unique identifier of the circuit.
283 | * @param participantId - the unique identifier of the participant.
284 | * @returns >> - the document info about the circuit contributions from contributor.
285 | */
286 | export const getCircuitContributionsFromContributor = async (
287 | ceremonyId: string,
288 | circuitId: string,
289 | participantId: string
290 | ): Promise> => {
291 | const participantContributionsQuerySnap = await queryCollection(
292 | getContributionsCollectionPath(ceremonyId, circuitId),
293 | [where(commonTerms.collections.contributions.fields.participantId, "==", participantId)]
294 | )
295 |
296 | return fromQueryToFirebaseDocumentInfo(participantContributionsQuerySnap.docs)
297 | }
298 |
299 |
300 | /**
301 | * Query for the active timeout from given participant for a given ceremony (if any).
302 | * @param ceremonyId - the identifier of the ceremony.
303 | * @param participantId - the identifier of the participant.
304 | * @returns >> - the document info about the current active participant timeout.
305 | */
306 | export const getCurrentActiveParticipantTimeout = async (
307 | ceremonyId: string,
308 | participantId: string
309 | ): Promise> => {
310 | const participantTimeoutQuerySnap = await queryCollection(
311 | getTimeoutsCollectionPath(ceremonyId, participantId),
312 | [where(commonTerms.collections.timeouts.fields.endDate, ">=", Date.now())]
313 | )
314 |
315 | return fromQueryToFirebaseDocumentInfo(participantTimeoutQuerySnap.docs)
316 | }
317 |
318 | /**
319 | * Get how many users in the waiting queue per circuit
320 | * @param ceremonyId {string} - the ceremony unique identifier.
321 | * @param ceremonyName {string} - the ceremony name.
322 | * @returns {WaitingQueue[]}
323 | */
324 | export const getCeremonyCircuitsWaitingQueue = async (
325 | ceremonyId: string,
326 | ceremonyName: string
327 | ): Promise => {
328 | const circuits = await getCeremonyCircuits(ceremonyId)
329 |
330 | const waiting: WaitingQueue[] = []
331 | for (const circuit of circuits) {
332 | const { waitingQueue, name } = circuit.data
333 | const { contributors } = waitingQueue
334 |
335 | waiting.push({
336 | ceremonyName: ceremonyName,
337 | circuitName: name,
338 | waitingQueue: contributors.length
339 | })
340 | }
341 |
342 | return waiting
343 | }
344 |
345 |
346 | /**
347 | * Get the final beacon used for finalizing a ceremony
348 | * @param ceremonyId
349 | */
350 | export const getFinalBeacon = async (ceremonyId: string, coordinatorId: string, circuitId: string) => {
351 | const contributions = await getCircuitContributionsFromContributor(ceremonyId, circuitId, coordinatorId)
352 |
353 | const filtered = contributions
354 | .filter((contributionDocument: any) => contributionDocument.data.zkeyIndex === finalContributionIndex)[0]
355 |
356 | if (!filtered)
357 | return {
358 | beacon: "",
359 | beaconHash: ""
360 | }
361 |
362 | return {
363 | beacon: filtered.data.beacon.value,
364 | beaconHash: filtered.data.beacon.hash
365 | }
366 | }
--------------------------------------------------------------------------------
/ceremonies/rln-trusted-setup-ceremony/p0tionConfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "RLN Trusted Setup Ceremony",
3 | "description": "This is a trusted setup ceremony for the Rate-Limiting Nullifier protocol",
4 | "startDate": "2023-07-31T10:30:00",
5 | "endDate": "2023-09-05T00:00:00",
6 | "timeoutMechanismType": "FIXED",
7 | "penalty": 3,
8 | "circuits": [
9 | {
10 | "description": "RLN withdraw circuit",
11 | "compiler": {
12 | "version": "2.1.5",
13 | "commitHash": "127414e9088cc017a357233f30f3fd7d91a8906c"
14 | },
15 | "template": {
16 | "source": "https://github.com/Rate-Limiting-Nullifier/circom-rln",
17 | "commitHash": "17f0fed7d8d19e8b127fd0b3e5295a4831193a0d",
18 | "paramsConfiguration": []
19 | },
20 | "verification": {
21 | "cfOrVm": "CF"
22 | },
23 | "artifacts": {
24 | "r1csStoragePath": "https://p0tion-test-definitely-setup.s3.amazonaws.com/withdraw.r1cs",
25 | "wasmStoragePath": "https://p0tion-test-definitely-setup.s3.amazonaws.com/withdraw.wasm"
26 | },
27 | "name": "RLN-Withdraw",
28 | "fixedTimeWindow": 3,
29 | "sequencePosition": 1
30 | },
31 | {
32 | "description": "RLN circuit with depth = 16",
33 | "compiler": {
34 | "version": "2.1.5",
35 | "commitHash": "127414e9088cc017a357233f30f3fd7d91a8906c"
36 | },
37 | "template": {
38 | "source": "https://github.com/Rate-Limiting-Nullifier/circom-rln",
39 | "commitHash": "17f0fed7d8d19e8b127fd0b3e5295a4831193a0d",
40 | "paramsConfiguration": [16, 16]
41 | },
42 | "verification": {
43 | "cfOrVm": "CF"
44 | },
45 | "artifacts": {
46 | "r1csStoragePath": "https://p0tion-test-definitely-setup.s3.amazonaws.com/rln16.r1cs",
47 | "wasmStoragePath": "https://p0tion-test-definitely-setup.s3.amazonaws.com/rln16.wasm"
48 | },
49 | "name": "RLN-16",
50 | "fixedTimeWindow": 3,
51 | "sequencePosition": 2
52 | },
53 | {
54 | "description": "RLN circuit with depth = 17",
55 | "compiler": {
56 | "version": "2.1.5",
57 | "commitHash": "127414e9088cc017a357233f30f3fd7d91a8906c"
58 | },
59 | "template": {
60 | "source": "https://github.com/Rate-Limiting-Nullifier/circom-rln",
61 | "commitHash": "17f0fed7d8d19e8b127fd0b3e5295a4831193a0d",
62 | "paramsConfiguration": [17, 16]
63 | },
64 | "verification": {
65 | "cfOrVm": "CF"
66 | },
67 | "artifacts": {
68 | "r1csStoragePath": "https://p0tion-test-definitely-setup.s3.amazonaws.com/rln17.r1cs",
69 | "wasmStoragePath": "https://p0tion-test-definitely-setup.s3.amazonaws.com/rln17.wasm"
70 | },
71 | "name": "RLN-17",
72 | "fixedTimeWindow": 3,
73 | "sequencePosition": 3
74 | },
75 | {
76 | "description": "RLN circuit with depth = 18",
77 | "compiler": {
78 | "version": "2.1.5",
79 | "commitHash": "127414e9088cc017a357233f30f3fd7d91a8906c"
80 | },
81 | "template": {
82 | "source": "https://github.com/Rate-Limiting-Nullifier/circom-rln",
83 | "commitHash": "17f0fed7d8d19e8b127fd0b3e5295a4831193a0d",
84 | "paramsConfiguration": [18, 16]
85 | },
86 | "verification": {
87 | "cfOrVm": "CF"
88 | },
89 | "artifacts": {
90 | "r1csStoragePath": "https://p0tion-test-definitely-setup.s3.amazonaws.com/rln18.r1cs",
91 | "wasmStoragePath": "https://p0tion-test-definitely-setup.s3.amazonaws.com/rln18.wasm"
92 | },
93 | "name": "RLN-18",
94 | "fixedTimeWindow": 3,
95 | "sequencePosition": 4
96 | },
97 | {
98 | "description": "RLN circuit with depth = 19",
99 | "compiler": {
100 | "version": "2.1.5",
101 | "commitHash": "127414e9088cc017a357233f30f3fd7d91a8906c"
102 | },
103 | "template": {
104 | "source": "https://github.com/Rate-Limiting-Nullifier/circom-rln",
105 | "commitHash": "17f0fed7d8d19e8b127fd0b3e5295a4831193a0d",
106 | "paramsConfiguration": [19, 16]
107 | },
108 | "verification": {
109 | "cfOrVm": "CF"
110 | },
111 | "artifacts": {
112 | "r1csStoragePath": "https://p0tion-test-definitely-setup.s3.amazonaws.com/rln19.r1cs",
113 | "wasmStoragePath": "https://p0tion-test-definitely-setup.s3.amazonaws.com/rln19.wasm"
114 | },
115 | "name": "RLN-19",
116 | "fixedTimeWindow": 3,
117 | "sequencePosition": 5
118 | },
119 | {
120 | "description": "RLN circuit with depth = 20",
121 | "compiler": {
122 | "version": "2.1.5",
123 | "commitHash": "127414e9088cc017a357233f30f3fd7d91a8906c"
124 | },
125 | "template": {
126 | "source": "https://github.com/Rate-Limiting-Nullifier/circom-rln",
127 | "commitHash": "17f0fed7d8d19e8b127fd0b3e5295a4831193a0d",
128 | "paramsConfiguration": [20, 16]
129 | },
130 | "verification": {
131 | "cfOrVm": "CF"
132 | },
133 | "artifacts": {
134 | "r1csStoragePath": "https://p0tion-test-definitely-setup.s3.amazonaws.com/rln20.r1cs",
135 | "wasmStoragePath": "https://p0tion-test-definitely-setup.s3.amazonaws.com/rln20.wasm"
136 | },
137 | "name": "RLN-20",
138 | "fixedTimeWindow": 3,
139 | "sequencePosition": 6
140 | },
141 | {
142 | "description": "RLN circuit with depth = 21",
143 | "compiler": {
144 | "version": "2.1.5",
145 | "commitHash": "127414e9088cc017a357233f30f3fd7d91a8906c"
146 | },
147 | "template": {
148 | "source": "https://github.com/Rate-Limiting-Nullifier/circom-rln",
149 | "commitHash": "17f0fed7d8d19e8b127fd0b3e5295a4831193a0d",
150 | "paramsConfiguration": [21, 16]
151 | },
152 | "verification": {
153 | "cfOrVm": "CF"
154 | },
155 | "artifacts": {
156 | "r1csStoragePath": "https://p0tion-test-definitely-setup.s3.amazonaws.com/rln21.r1cs",
157 | "wasmStoragePath": "https://p0tion-test-definitely-setup.s3.amazonaws.com/rln21.wasm"
158 | },
159 | "name": "RLN-21",
160 | "fixedTimeWindow": 3,
161 | "sequencePosition": 7
162 | },
163 | {
164 | "description": "RLN circuit with depth = 22",
165 | "compiler": {
166 | "version": "2.1.5",
167 | "commitHash": "127414e9088cc017a357233f30f3fd7d91a8906c"
168 | },
169 | "template": {
170 | "source": "https://github.com/Rate-Limiting-Nullifier/circom-rln",
171 | "commitHash": "17f0fed7d8d19e8b127fd0b3e5295a4831193a0d",
172 | "paramsConfiguration": [22, 16]
173 | },
174 | "verification": {
175 | "cfOrVm": "CF"
176 | },
177 | "artifacts": {
178 | "r1csStoragePath": "https://p0tion-test-definitely-setup.s3.amazonaws.com/rln22.r1cs",
179 | "wasmStoragePath": "https://p0tion-test-definitely-setup.s3.amazonaws.com/rln22.wasm"
180 | },
181 | "name": "RLN-22",
182 | "fixedTimeWindow": 3,
183 | "sequencePosition": 8
184 | },
185 | {
186 | "description": "RLN circuit with depth = 23",
187 | "compiler": {
188 | "version": "2.1.5",
189 | "commitHash": "127414e9088cc017a357233f30f3fd7d91a8906c"
190 | },
191 | "template": {
192 | "source": "https://github.com/Rate-Limiting-Nullifier/circom-rln",
193 | "commitHash": "17f0fed7d8d19e8b127fd0b3e5295a4831193a0d",
194 | "paramsConfiguration": [23, 16]
195 | },
196 | "verification": {
197 | "cfOrVm": "CF"
198 | },
199 | "artifacts": {
200 | "r1csStoragePath": "https://p0tion-test-definitely-setup.s3.amazonaws.com/rln23.r1cs",
201 | "wasmStoragePath": "https://p0tion-test-definitely-setup.s3.amazonaws.com/rln23.wasm"
202 | },
203 | "name": "RLN-23",
204 | "fixedTimeWindow": 3,
205 | "sequencePosition": 9
206 | },
207 | {
208 | "description": "RLN circuit with depth = 24",
209 | "compiler": {
210 | "version": "2.1.5",
211 | "commitHash": "127414e9088cc017a357233f30f3fd7d91a8906c"
212 | },
213 | "template": {
214 | "source": "https://github.com/Rate-Limiting-Nullifier/circom-rln",
215 | "commitHash": "17f0fed7d8d19e8b127fd0b3e5295a4831193a0d",
216 | "paramsConfiguration": [24, 16]
217 | },
218 | "verification": {
219 | "cfOrVm": "CF"
220 | },
221 | "artifacts": {
222 | "r1csStoragePath": "https://p0tion-test-definitely-setup.s3.amazonaws.com/rln24.r1cs",
223 | "wasmStoragePath": "https://p0tion-test-definitely-setup.s3.amazonaws.com/rln24.wasm"
224 | },
225 | "name": "RLN-24",
226 | "fixedTimeWindow": 3,
227 | "sequencePosition": 10
228 | },
229 | {
230 | "description": "RLN circuit with depth = 25",
231 | "compiler": {
232 | "version": "2.1.5",
233 | "commitHash": "127414e9088cc017a357233f30f3fd7d91a8906c"
234 | },
235 | "template": {
236 | "source": "https://github.com/Rate-Limiting-Nullifier/circom-rln",
237 | "commitHash": "17f0fed7d8d19e8b127fd0b3e5295a4831193a0d",
238 | "paramsConfiguration": [25, 16]
239 | },
240 | "verification": {
241 | "cfOrVm": "CF"
242 | },
243 | "artifacts": {
244 | "r1csStoragePath": "https://p0tion-test-definitely-setup.s3.amazonaws.com/rln25.r1cs",
245 | "wasmStoragePath": "https://p0tion-test-definitely-setup.s3.amazonaws.com/rln25.wasm"
246 | },
247 | "name": "RLN-25",
248 | "fixedTimeWindow": 3,
249 | "sequencePosition": 6
250 | },
251 | {
252 | "description": "RLN circuit with depth = 20",
253 | "compiler": {
254 | "version": "2.1.5",
255 | "commitHash": "127414e9088cc017a357233f30f3fd7d91a8906c"
256 | },
257 | "template": {
258 | "source": "https://github.com/Rate-Limiting-Nullifier/circom-rln",
259 | "commitHash": "17f0fed7d8d19e8b127fd0b3e5295a4831193a0d",
260 | "paramsConfiguration": [26, 16]
261 | },
262 | "verification": {
263 | "cfOrVm": "CF"
264 | },
265 | "artifacts": {
266 | "r1csStoragePath": "https://p0tion-test-definitely-setup.s3.amazonaws.com/rln26.r1cs",
267 | "wasmStoragePath": "https://p0tion-test-definitely-setup.s3.amazonaws.com/rln26.wasm"
268 | },
269 | "name": "RLN-26",
270 | "dynamicThreshold": 0,
271 | "fixedTimeWindow": 3,
272 | "sequencePosition": 12
273 | },
274 | {
275 | "description": "RLN circuit with depth = 27",
276 | "compiler": {
277 | "version": "2.1.5",
278 | "commitHash": "127414e9088cc017a357233f30f3fd7d91a8906c"
279 | },
280 | "template": {
281 | "source": "https://github.com/Rate-Limiting-Nullifier/circom-rln",
282 | "commitHash": "17f0fed7d8d19e8b127fd0b3e5295a4831193a0d",
283 | "paramsConfiguration": [27, 16]
284 | },
285 | "verification": {
286 | "cfOrVm": "CF"
287 | },
288 | "artifacts": {
289 | "r1csStoragePath": "https://p0tion-test-definitely-setup.s3.amazonaws.com/rln27.r1cs",
290 | "wasmStoragePath": "https://p0tion-test-definitely-setup.s3.amazonaws.com/rln27.wasm"
291 | },
292 | "name": "RLN-20",
293 | "dynamicThreshold": 0,
294 | "fixedTimeWindow": 3,
295 | "sequencePosition": 13
296 | },
297 | {
298 | "description": "RLN circuit with depth = 28",
299 | "compiler": {
300 | "version": "2.1.5",
301 | "commitHash": "127414e9088cc017a357233f30f3fd7d91a8906c"
302 | },
303 | "template": {
304 | "source": "https://github.com/Rate-Limiting-Nullifier/circom-rln",
305 | "commitHash": "17f0fed7d8d19e8b127fd0b3e5295a4831193a0d",
306 | "paramsConfiguration": [28, 16]
307 | },
308 | "verification": {
309 | "cfOrVm": "CF"
310 | },
311 | "artifacts": {
312 | "r1csStoragePath": "https://p0tion-test-definitely-setup.s3.amazonaws.com/rln28.r1cs",
313 | "wasmStoragePath": "https://p0tion-test-definitely-setup.s3.amazonaws.com/rln28.wasm"
314 | },
315 | "name": "RLN-28",
316 | "dynamicThreshold": 0,
317 | "fixedTimeWindow": 3,
318 | "sequencePosition": 14
319 | },
320 | {
321 | "description": "RLN circuit with depth = 29",
322 | "compiler": {
323 | "version": "2.1.5",
324 | "commitHash": "127414e9088cc017a357233f30f3fd7d91a8906c"
325 | },
326 | "template": {
327 | "source": "https://github.com/Rate-Limiting-Nullifier/circom-rln",
328 | "commitHash": "17f0fed7d8d19e8b127fd0b3e5295a4831193a0d",
329 | "paramsConfiguration": [29, 16]
330 | },
331 | "verification": {
332 | "cfOrVm": "CF"
333 | },
334 | "artifacts": {
335 | "r1csStoragePath": "https://p0tion-test-definitely-setup.s3.amazonaws.com/rln29.r1cs",
336 | "wasmStoragePath": "https://p0tion-test-definitely-setup.s3.amazonaws.com/rln29.wasm"
337 | },
338 | "name": "RLN-20",
339 | "dynamicThreshold": 0,
340 | "fixedTimeWindow": 3,
341 | "sequencePosition": 15
342 | },
343 | {
344 | "description": "RLN circuit with depth = 30",
345 | "compiler": {
346 | "version": "2.1.5",
347 | "commitHash": "127414e9088cc017a357233f30f3fd7d91a8906c"
348 | },
349 | "template": {
350 | "source": "https://github.com/Rate-Limiting-Nullifier/circom-rln",
351 | "commitHash": "17f0fed7d8d19e8b127fd0b3e5295a4831193a0d",
352 | "paramsConfiguration": [30, 16]
353 | },
354 | "verification": {
355 | "cfOrVm": "CF"
356 | },
357 | "artifacts": {
358 | "r1csStoragePath": "https://p0tion-test-definitely-setup.s3.amazonaws.com/rln30.r1cs",
359 | "wasmStoragePath": "https://p0tion-test-definitely-setup.s3.amazonaws.com/rln30.wasm"
360 | },
361 | "name": "RLN-30",
362 | "dynamicThreshold": 0,
363 | "fixedTimeWindow": 3,
364 | "sequencePosition": 16
365 | },
366 | {
367 | "description": "RLN circuit with depth = 31",
368 | "compiler": {
369 | "version": "2.1.5",
370 | "commitHash": "127414e9088cc017a357233f30f3fd7d91a8906c"
371 | },
372 | "template": {
373 | "source": "https://github.com/Rate-Limiting-Nullifier/circom-rln",
374 | "commitHash": "17f0fed7d8d19e8b127fd0b3e5295a4831193a0d",
375 | "paramsConfiguration": [31, 16]
376 | },
377 | "verification": {
378 | "cfOrVm": "CF"
379 | },
380 | "artifacts": {
381 | "r1csStoragePath": "https://p0tion-test-definitely-setup.s3.amazonaws.com/rln31.r1cs",
382 | "wasmStoragePath": "https://p0tion-test-definitely-setup.s3.amazonaws.com/rln31.wasm"
383 | },
384 | "name": "RLN-31",
385 | "dynamicThreshold": 0,
386 | "fixedTimeWindow": 3,
387 | "sequencePosition": 17
388 | },
389 | {
390 | "description": "RLN circuit with depth = 32",
391 | "compiler": {
392 | "version": "2.1.5",
393 | "commitHash": "127414e9088cc017a357233f30f3fd7d91a8906c"
394 | },
395 | "template": {
396 | "source": "https://github.com/Rate-Limiting-Nullifier/circom-rln",
397 | "commitHash": "17f0fed7d8d19e8b127fd0b3e5295a4831193a0d",
398 | "paramsConfiguration": [32, 16]
399 | },
400 | "verification": {
401 | "cfOrVm": "CF"
402 | },
403 | "artifacts": {
404 | "r1csStoragePath": "https://p0tion-test-definitely-setup.s3.amazonaws.com/rln32.r1cs",
405 | "wasmStoragePath": "https://p0tion-test-definitely-setup.s3.amazonaws.com/rln32.wasm"
406 | },
407 | "name": "RLN-32",
408 | "dynamicThreshold": 0,
409 | "fixedTimeWindow": 3,
410 | "sequencePosition": 18
411 | }
412 | ]
413 | }
414 |
--------------------------------------------------------------------------------
/web/src/helpers/interfaces.ts:
--------------------------------------------------------------------------------
1 | import { DocumentData, DocumentReference } from "firebase/firestore";
2 | import { z } from "zod";
3 | import { ProjectDataSchema } from "../context/ProjectPageContext";
4 |
5 | /**
6 | * Define different states of a ceremony.
7 | * @enum {string}
8 | * - SCHEDULED: when the ceremony setup has been properly completed but the contribution period has not yet started.
9 | * - OPENED: when the contribution period has started.
10 | * - PAUSED: When the coordinator has manually paused the ceremony (NB. currently not possible because the relevant functionality has not yet been implemented).
11 | * - CLOSED: when the contribution period has finished.
12 | * - FINALIZED: when the ceremony finalization has been properly completed.
13 | */
14 | export const enum CeremonyState {
15 | SCHEDULED = "SCHEDULED",
16 | OPENED = "OPENED",
17 | PAUSED = "PAUSED",
18 | CLOSED = "CLOSED",
19 | FINALIZED = "FINALIZED"
20 | }
21 |
22 | /**
23 | * Define the type of Trusted Setup ceremony (Phase 1 or Phase 2).
24 | * @enum {string}
25 | * - PHASE1: when the ceremony is a Phase 1 Trusted Setup ceremony.
26 | * - PHASE2: when the ceremony is a Phase 2 Trusted Setup ceremony.
27 | */
28 | export const enum CeremonyType {
29 | PHASE1 = "PHASE1",
30 | PHASE2 = "PHASE2"
31 | }
32 |
33 | /**
34 | * Define different status of a participant.
35 | * @enum {string}
36 | * - CREATED: when the participant document has been created in the database.
37 | * - WAITING: when the participant is waiting for a contribution (i.e., is currently queued or is waiting for its status to be checked after a timeout expiration).
38 | * - READY: when the participant is ready for a contribution.
39 | * - CONTRIBUTING: when the participant is currently contributing (i.e., not queued anymore, but the current contributor at this time).
40 | * - CONTRIBUTED: when the participant has completed successfully the contribution for all circuits in a ceremony. The participant may need to wait for the latest contribution verification while having this status.
41 | * - DONE: when the participant has completed contributions and verifications from coordinator.
42 | * - FINALIZING: when the coordinator is currently finalizing the ceremony.
43 | * - FINALIZED: when the coordinator has successfully finalized the ceremony.
44 | * - TIMEDOUT: when the participant has been timedout while contributing. This may happen due to network or memory issues, un/intentional crash, or contributions lasting for too long.
45 | * - EXHUMED: when the participant is ready to resume the contribution after a timeout expiration.
46 | */
47 | export const enum ParticipantStatus {
48 | CREATED = "CREATED",
49 | WAITING = "WAITING",
50 | READY = "READY",
51 | CONTRIBUTING = "CONTRIBUTING",
52 | CONTRIBUTED = "CONTRIBUTED",
53 | DONE = "DONE",
54 | FINALIZING = "FINALIZING",
55 | FINALIZED = "FINALIZED",
56 | TIMEDOUT = "TIMEDOUT",
57 | EXHUMED = "EXHUMED"
58 | }
59 |
60 | /**
61 | * Define different steps during which the participant may be during the contribution.
62 | * @enum {string}
63 | * - DOWNLOADING: when the participant is doing the download of the last contribution (from previous participant).
64 | * - COMPUTING: when the participant is actively computing the contribution.
65 | * - UPLOADING: when the participant is uploading the computed contribution.
66 | * - VERIFYING: when the participant is waiting from verification results from the coordinator.
67 | * - COMPLETED: when the participant has received the verification results from the coordinator and completed the contribution steps.
68 | */
69 | export const enum ParticipantContributionStep {
70 | DOWNLOADING = "DOWNLOADING",
71 | COMPUTING = "COMPUTING",
72 | UPLOADING = "UPLOADING",
73 | VERIFYING = "VERIFYING",
74 | COMPLETED = "COMPLETED"
75 | }
76 |
77 | /**
78 | * Define what type of timeout was performed.
79 | * @enum {string}
80 | * - BLOCKING_CONTRIBUTION: when the current contributor was blocking the waiting queue.
81 | * - BLOCKING_CLOUD_FUNCTION: when the contribution verification has gone beyond the time limit.
82 | */
83 | export const enum TimeoutType {
84 | BLOCKING_CONTRIBUTION = "BLOCKING_CONTRIBUTION",
85 | BLOCKING_CLOUD_FUNCTION = "BLOCKING_CLOUD_FUNCTION"
86 | }
87 |
88 | /**
89 | * Define what type of timeout mechanism is currently adopted for a ceremony.
90 | * @enum {string}
91 | * - DYNAMIC: self-update approach based on latest contribution time.
92 | * - FIXED: approach based on a fixed amount of time.
93 | */
94 | export const enum CeremonyTimeoutType {
95 | DYNAMIC = "DYNAMIC",
96 | FIXED = "FIXED"
97 | }
98 |
99 | /**
100 | * Define request type for pre-signed urls.
101 | */
102 | export const enum RequestType {
103 | PUT = "PUT",
104 | GET = "GET"
105 | }
106 |
107 | /**
108 | * Define the environment in use when testing.
109 | * @enum {string}
110 | * - DEVELOPMENT: tests are performed on the local Firebase emulator instance.
111 | * - PRODUCTION: tests are performed on the remote (deployed) Firebase application.
112 | */
113 | export const enum TestingEnvironment {
114 | DEVELOPMENT = "DEVELOPMENT",
115 | PRODUCTION = "PRODUCTION"
116 | }
117 |
118 | /**
119 | * Define what type of contribution verification mechanism is currently adopted for a circuit.
120 | * @enum {string}
121 | * - CF: Cloud Functions.
122 | * - VM: Virtual Machine.
123 | */
124 | export const enum CircuitContributionVerificationMechanism {
125 | CF = "CF",
126 | VM = "VM"
127 | }
128 |
129 | /**
130 | * Define the supported VM volume types.
131 | * @dev the VM volume types can be retrieved at https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ebs-volume-types.html
132 | * @enum {string}
133 | * - GP2: General Purpose SSD version 2.
134 | * - GP3: General Purpose SSD version 3.
135 | * - IO1: Provisioned IOPS SSD volumes version 1.
136 | * - ST1: Throughput Optimized HDD volumes.
137 | * - SC1: Cold HDD volumes.
138 | */
139 | export const enum DiskTypeForVM {
140 | GP2 = "gp2",
141 | GP3 = "gp3",
142 | IO1 = "io1",
143 | ST1 = "st1",
144 | SC1 = "sc1"
145 | }
146 |
147 | /**
148 | * Necessary data to define a ceremony database document.
149 | * @dev The data is both entered by the coordinator and derived.
150 | * @property {string} title - the title/name of the ceremony.
151 | * @property {string} description - a brief description of the ceremony.
152 | * @property {number} startDate - the start (opening to contributions) date for the ceremony (in ms).
153 | * @property {number} endDate - the end (closing to contributions) date for the ceremony (in ms).
154 | * @property {CeremonyTimeoutType} timeoutMechanismType - the timeout mechanism type used for avoiding blocking contribution behaviours.
155 | * @property {number} penalty - the amount of time expressed in minutes that the blocking contributor has to wait before joining the waiting queue again.
156 | * @property {string} prefix - the prefix of the ceremony derived from the name.
157 | * @property {CeremonyState} state - the current state of the ceremony.
158 | * @property {CeremonyType} type - the type of the ceremony.
159 | * @property {string} coordinatorId - the unique identifier of the coordinator.
160 | * @property {number} lastUpdated - the timestamp where the last update of the Firestore document has happened.
161 | */
162 | export interface CeremonyDocument {
163 | title: string
164 | description: string
165 | startDate: number
166 | endDate: number
167 | timeoutMechanismType: CeremonyTimeoutType
168 | penalty: number
169 | prefix: string
170 | state: CeremonyState
171 | type: CeremonyType
172 | coordinatorId: string
173 | lastUpdated: number
174 | hideInWeb?: boolean
175 | }
176 |
177 | /**
178 | * Necessary data to define a circuit database document.
179 | * @property {CircuitMetadata} metadata - the info about the circuit metadata.
180 | * @property {CircuitArtifacts} [files] - the references about the circuit artifacts.
181 | * @property {CircuitTimings} [avgTimings] - the info about the average timings for the circuit.
182 | * @property {SourceTemplateData} [template] - the info about the circuit template.
183 | * @property {CircomCompilerData} [compiler] - the info about the circom compiler.
184 | * @property {CircuitWaitingQueue} [waitingQueue] - the info about the circuit waiting queue.
185 | * @property {number} [lastUpdated] - the timestamp where the last update of the Firestore document has happened.
186 | */
187 | export interface CircuitDocument {
188 | description: string
189 | compiler: {
190 | version: string
191 | commitHash: string
192 | }
193 | template: {
194 | source: string
195 | commitHash: string
196 | paramsConfiguration: Array
197 | },
198 | verification: {
199 | cfOrVm: CircuitContributionVerificationMechanism
200 | vm?: {
201 | vmConfigurationType?: string
202 | vmDiskType?: DiskTypeForVM
203 | vmDiskSize?: number
204 | vmInstanceId?: string
205 | }
206 | },
207 | compilationArtifacts?: {
208 | r1csFilename: string
209 | wasmFilename: string
210 | }
211 | metadata?: {
212 | curve: string
213 | wires: number
214 | constraints: number
215 | privateInputs: number
216 | publicInputs: number
217 | labels: number
218 | outputs: number
219 | pot: number
220 | }
221 | name?: string
222 | dynamicThreshold?: number
223 | fixedTimeWindow?: number
224 | sequencePosition?: number
225 | prefix?: string
226 | zKeySizeInBytes?: number
227 | files?: {
228 | potFilename: string
229 | r1csFilename: string
230 | wasmFilename: string
231 | initialZkeyFilename: string
232 | potStoragePath: string
233 | r1csStoragePath: string
234 | wasmStoragePath: string
235 | initialZkeyStoragePath: string
236 | potBlake2bHash: string
237 | r1csBlake2bHash: string
238 | wasmBlake2bHash: string
239 | initialZkeyBlake2bHash: string
240 | }
241 | avgTimings?: {
242 | contributionComputation: number
243 | fullContribution: number
244 | verifyCloudFunction: number
245 | }
246 | waitingQueue?: {
247 | completedContributions: number
248 | contributors: Array
249 | currentContributor: string
250 | failedContributions: number
251 | }
252 | lastUpdated?: number
253 | }
254 |
255 |
256 | /**
257 | * Necessary data to define a participant database document.
258 | * @property {string} userId - the unique identifier of the user associated with the participant.
259 | * @property {number} contributionProgress - indicates the number of the circuit for which the user has to wait in the queue.
260 | * @property {ParticipantStatus} status - the current status of the participant.
261 | * @property {Array} contributions - the list of references to the contributions computed by the participant.
262 | * @property {number} lastUpdated - the timestamp where the last update of the Firestore document has happened.
263 | * @property {number} [contributionStartedAt] - the timestamp of when the latest contribution has started.
264 | * @property {number} [verificationStartedAt] - the timestamp of when the latest verification process has started.
265 | * @property {TemporaryParticipantContributionData} [tempContributionData] - the auxiliary data needed for resumption in an intermediate step of contribution.
266 | */
267 | export interface ParticipantDocument {
268 | userId: string
269 | contributionProgress: number
270 | status: ParticipantStatus
271 | contributions: Array<{
272 | doc: string
273 | computationTime: number
274 | hash: string
275 | }>
276 | lastUpdated: number
277 | contributionStartedAt: number
278 | contributionStep?: ParticipantContributionStep
279 | verificationStartedAt?: number
280 | tempContributionData?: {
281 | contributionComputationTime: number
282 | uploadId: string
283 | chunks: Array<{
284 | ETag: string | undefined
285 | PartNumber: number
286 | }>
287 | }
288 | }
289 |
290 | /**
291 | * Necessary data to define a contribution document.
292 | * @property {string} participantId - the unique identifier of the contributor.
293 | * @property {number} contributionComputationTime - the amount of time spent for the contribution (download, compute, upload).
294 | * @property {number} verificationComputationTime - the amount of time spent for the verification of the contribution.
295 | * @property {string} zkeyIndex - the index of the contribution.
296 | * @property {ContributionFiles} files - the references and hashes of the artifacts produced during the contribution (and verification).
297 | * @property {ContributionVerificationSoftware} verificationSoftware - the info about the verification software used to verify the contributions.
298 | * @property {boolean} valid - true if the contribution has been evaluated as valid; otherwise false.
299 | * @property {number} lastUpdated - the timestamp where the last update of the Firestore document has happened.
300 | * @property {BeaconInfo} beacon - the data about the value used to compute the final contribution while finalizing the ceremony (final contribution only).
301 | */
302 | export interface ContributionDocument {
303 | participantId: string
304 | contributionComputationTime: number
305 | verificationComputationTime: number
306 | zkeyIndex: string
307 | files: {
308 | transcriptFilename: string
309 | lastZkeyFilename: string
310 | transcriptStoragePath: string
311 | lastZkeyStoragePath: string
312 | transcriptBlake2bHash: string
313 | lastZkeyBlake2bHash: string
314 | verificationKeyBlake2bHash?: string
315 | verificationKeyFilename?: string
316 | verificationKeyStoragePath?: string
317 | verifierContractBlake2bHash?: string
318 | verifierContractFilename?: string
319 | verifierContractStoragePath?: string
320 | }
321 | verificationSoftware: {
322 | name: string
323 | version: string
324 | commitHash: string
325 | }
326 | valid: boolean
327 | lastUpdated: number
328 | beacon?: {
329 | value: string
330 | hash: string
331 | }
332 | }
333 |
334 | /**
335 | * Define a circuit document reference and data.
336 | * @dev must be used for generating fake/mock documents when testing.
337 | * @property {string} uid - the unique identifier of the document.
338 | * @property {CircuitDocument} doc - the info about the circuit document.
339 | */
340 | export interface CircuitDocumentReferenceAndData {
341 | uid: string
342 | data: CircuitDocument
343 | }
344 |
345 | /**
346 | * Define a ceremony document reference and data.
347 | * @dev must be used for generating fake/mock documents when testing.
348 | * @property {string} uid - the unique identifier of the document.
349 | * @property {CeremonyDocument} doc - the info about the ceremony document.
350 | */
351 | export interface CeremonyDocumentReferenceAndData {
352 | uid: string
353 | data: CeremonyDocument
354 | }
355 |
356 | /**
357 | * Define a participant document reference and data.
358 | * @dev must be used for generating fake/mock documents when testing.
359 | * @property {string} uid - the unique identifier of the document.
360 | * @property {ParticipantDocument} doc - the info about the participant document.
361 | */
362 | export interface ParticipantDocumentReferenceAndData {
363 | uid: string
364 | data: ParticipantDocument
365 | }
366 |
367 | /**
368 | * Define a contribution document reference and data.
369 | * @dev must be used for generating fake/mock documents when testing.
370 | * @property {string} uid - the unique identifier of the document.
371 | * @property {ContributionDocument} doc - the info about the contribution document.
372 | */
373 | export interface ContributionDocumentReferenceAndData {
374 | uid: string
375 | data: ContributionDocument
376 | }
377 |
378 |
379 | export interface HeroComponentProps {
380 | projects: Project[];
381 | waitingQueue: WaitingQueue[];
382 | }
383 |
384 | /**
385 | * Useful for interacting with reference and data from a Firestore document at the same time.
386 | * @typedef {Object} FirebaseDocumentInfo
387 | * @property {string} id - the unique identifier of the Firestore document.
388 | * @property {DocumentReference} ref - the Firestore reference for the document (useful for queries).
389 | * @property {DocumentData} data - the Firestore document whole data.
390 | */
391 | export type FirebaseDocumentInfo = {
392 | id: string
393 | ref: DocumentReference
394 | data: DocumentData
395 | }
396 |
397 | /**
398 | * Group a pre-signed url chunk core information.
399 | * @typedef {Object} ETagWithPartNumber
400 | * @property {string | null} ETag - a unique reference to this chunk associated to a pre-signed url.
401 | * @property {number} PartNumber - indicate where the chunk is positioned in order to reconhstruct the file with multiPartUpload/Download.
402 | */
403 | export type ETagWithPartNumber = {
404 | ETag: string | undefined
405 | PartNumber: number
406 | }
407 |
408 | /**
409 | * Auxiliary data needed for resumption in an intermediate step of contribution.
410 | * @dev The data is used when the current contributorinterrupts during the download, contribute, upload steps
411 | * to prevent it from having to start over but can pick up where it left off.
412 | * This restart operation does NOT interact with the timeout mechanism (which remains unchanged).
413 | * @typedef {Object} TemporaryParticipantContributionData
414 | * @property {number} contributionComputationTime - the time spent since the contribution start.
415 | * @property {string} uploadId - the unique identifier of the pre-signed url PUT request to upload the newest contribution.
416 | * @property {Array} chunks - the list of ETags and PartNumbers that make up the chunks.
417 | */
418 | export type TemporaryParticipantContributionData = {
419 | contributionComputationTime: number
420 | uploadId: string
421 | chunks: Array
422 | }
423 |
424 | /**
425 | * Define a custom file data chunk associated with a pre-signed url.
426 | * @dev Useful when interacting with AWS S3 buckets using pre-signed urls for multi-part upload or download storing temporary information on the database.
427 | * @typedef {Object} ChunkWithUrl
428 | * @property {number} partNumber - indicate where the chunk is positioned in order to reconhstruct the file with multiPartUpload/Download.
429 | * @property {Uint8Array} chunk - the piece of information in bytes.
430 | * @property {string} preSignedUrl - the unique reference to the pre-signed url to which this chunk is linked too.
431 | */
432 | export type ChunkWithUrl = {
433 | partNumber: number
434 | chunk: Uint8Array
435 | preSignedUrl: string
436 | }
437 |
438 | /**
439 | * Define a custom object for time management tasks.
440 | * @typedef {Object} Timing
441 | * @property {number} seconds
442 | * @property {number} minutes
443 | * @property {number} hours
444 | * @property {number} days
445 | */
446 | export type Timing = {
447 | seconds: number
448 | minutes: number
449 | hours: number
450 | days: number
451 | }
452 |
453 | /**
454 | * Data defining a contribution made by a participant.
455 | * @typedef {Object} Contribution
456 | * @property {string} doc - the unique identifier of the document related to the contribution.
457 | * @property {number} computationTime - the overall time spent while computing the contribution.
458 | * @property {string} hash - the contribution hash (generated as output from the snarkjs command).
459 | */
460 | export type Contribution = {
461 | doc: string
462 | computationTime: number
463 | hash: string
464 | }
465 |
466 | /**
467 | * Group the information when retrieving the validity of a contribution for a contributor.
468 | * @typedef {Object} ContributionValidity
469 | * @property {string} contributionId - the unique identifier of the contribution.
470 | * @property {string} circuitId - the unique identifier of the circuit for which the contribution was computed.
471 | * @property {boolean} valid - true if and only if the contribution is valid; otherwise false.
472 | */
473 | export type ContributionValidity = {
474 | contributionId: string
475 | circuitId: string
476 | valid: boolean
477 | }
478 |
479 | /**
480 | * Group the information for downloading a circuit's final zKey
481 | * @typedef {Object} ZkeyDownloadLink
482 | * @property {string} zkeyFilename - the name of the zKey file.
483 | * @property {string} zkeyURL - the url to download the zKey file.
484 | */
485 | export interface ZkeyDownloadLink {
486 | zkeyFilename: string
487 | zkeyURL: string
488 | }
489 |
490 | /**
491 | * Map a circuit to its waiting queue
492 | * @typedef {Object} WaitingQueue
493 | * @property {string} ceremonyName - the name of the ceremony.
494 | * @property {string} circuitName - the name of the circuit.
495 | * @property {number} waitingQueue - the number of participants in the waiting queue.
496 | */
497 | export interface WaitingQueue {
498 | ceremonyName: string
499 | circuitName: string
500 | waitingQueue: number
501 | }
502 |
503 | /**
504 | * Define the data structure for a project.
505 | * @typedef {Object} Project
506 | * @property {CeremonyDocumentReferenceAndData} ceremony - the reference and data of the ceremony.
507 | * @property {Array} [circuits] - the list of references and data of the circuits.
508 | * @property {Array} [participants] - the list of references and data of the participants.
509 | * @property {Array} [contributions] - the list of references and data of the contributions.
510 | * @property {string} [coordinatorId] - the unique identifier of the coordinator.
511 | */
512 | export interface Project {
513 | ceremony: CeremonyDocumentReferenceAndData
514 | circuits?: CircuitDocumentReferenceAndData[] | null
515 | participants?: ParticipantDocumentReferenceAndData[] | null
516 | contributions?: ContributionDocumentReferenceAndData[] | null
517 | coordinatorId?: string
518 | }
519 |
520 | export interface State {
521 | projects: Project[];
522 | setProjects: React.Dispatch>;
523 | circuit: CircuitDocumentReferenceAndData;
524 | setCircuit: React.Dispatch>;
525 | search: string;
526 | setSearch: React.Dispatch>;
527 | loading: boolean;
528 | setLoading: React.Dispatch>;
529 | runTutorial: boolean;
530 | setRunTutorial: React.Dispatch>;
531 | user?: string;
532 | setUser?: React.Dispatch>;
533 | waitingQueue: WaitingQueue[];
534 | }
535 |
536 | /**
537 | * Define the data structure for the project page context.
538 | * @typedef {Object} ProjectData
539 | * @property {string} ceremonyName - the name of the ceremony.
540 | * @property {string} ceremonyDescription - the description of the ceremony.
541 | * @property {string} ceremonyState - the state of the ceremony.
542 | * @property {string} ceremonyType - the type of the ceremony.
543 | */
544 | export type ProjectData = z.infer;
545 |
546 | /**
547 | * Define the data structure for the project page context.
548 | * @typedef {Object} ProjectPageContextProps
549 | * @property {boolean} hasUserContributed - true if the user has contributed to the project; otherwise false.
550 | * @property {ProjectData} projectData - the data about the project.
551 | * @property {boolean} isLoading - true if the data is being loaded; otherwise false.
552 | * @property {boolean} runTutorial - true if the tutorial should be run; otherwise false.
553 | * @property {string[]} [avatars] - the list of avatars for the participants.
554 | * @property {number} largestCircuitConstraints - the number of constraints of the largest circuit in the project.
555 | * @property {ZkeyDownloadLink[]} [finalZkeys] - the list of final zKeys for the project.
556 | * @property {FinalBeacon} [finalBeacon] - the beacon info for the final contribution.
557 | * @property {ZkeyDownloadLink[]} [latestZkeys] - the list of latest zKeys for the project.
558 | */
559 | export type ProjectPageContextProps = {
560 | hasUserContributed: boolean
561 | projectData: ProjectData | null
562 | isLoading: boolean
563 | runTutorial: boolean
564 | avatars?: string[]
565 | largestCircuitConstraints: number
566 | finalZkeys?: ZkeyDownloadLink[]
567 | finalBeacon?: FinalBeacon
568 | latestZkeys?: ZkeyDownloadLink[]
569 | };
570 |
571 | export type StateProviderProps = {
572 | children: React.ReactNode;
573 | };
574 |
575 | /**
576 | * Define the data structure for the beacon info.
577 | * @typedef {Object} BeaconInfo
578 | * @property {string} value - the value of the beacon.
579 | * @property {string} hash - the hash of the beacon.
580 | */
581 | export interface FinalBeacon {
582 | beacon: string
583 | beaconHash: string
584 | }
585 |
--------------------------------------------------------------------------------