From 7cc273e01f6432e4c5e75e88e4359126b18b995e Mon Sep 17 00:00:00 2001 From: cedoor Date: Tue, 19 Dec 2023 17:35:47 +0100 Subject: [PATCH] chore(docs): add semaphore docs project Former-commit-id: 5fac62ed444b6b12052d470f9331aa91bd82ac33 --- .eslintignore | 2 +- .gitignore | 6 +- .prettierignore | 2 +- .../.yarn/install-state.gz.REMOVED.git-id | 1 + .../releases/yarn-3.2.1.cjs.REMOVED.git-id | 1 + apps/docs/.yarnrc.yml | 5 + apps/docs/README.md | 75 ++++ apps/docs/babel.config.js | 3 + apps/docs/docusaurus.config.js | 102 +++++ apps/docs/i18n/en/code.json | 406 +++++++++++++++++ .../version-V1.json | 6 + .../version-V2.json | 18 + .../version-V3.json | 14 + .../en/docusaurus-theme-classic/navbar.json | 11 + apps/docs/i18n/es/code.json | 406 +++++++++++++++++ .../version-V1.json | 6 + .../version-V1/audit.md | 17 + .../version-V1/contract-api.md | 105 +++++ .../version-V1/creditsandresources.md | 33 ++ .../version-V1/howitworks.md | 137 ++++++ .../version-V1/libsemaphore.md | 245 +++++++++++ .../version-V1/quickstart.md | 68 +++ .../version-V1/trustedsetup.md | 11 + .../version-V1/usage.md | 144 ++++++ .../version-V1/what-is-semaphore.md | 105 +++++ .../version-V2.json | 18 + .../version-V2/credits.md | 13 + .../version-V2/deployed-contracts.md | 35 ++ .../version-V2/glossary.md | 64 +++ .../version-V2/guides/_category_.json | 4 + .../version-V2/guides/groups.md | 122 ++++++ .../version-V2/guides/identities.md | 83 ++++ .../version-V2/guides/proofs.md | 110 +++++ .../version-V2/quick-setup.md | 304 +++++++++++++ .../version-V2/resources.md | 19 + .../version-V2/subgraph.md | 51 +++ .../technical-reference/_category_.json | 4 + .../technical-reference/circuits.md | 57 +++ .../technical-reference/contracts.md | 50 +++ .../version-V2/use-cases/_category_.json | 4 + .../version-V2/use-cases/private-voting.md | 137 ++++++ .../version-V2/what-is-semaphore.md | 49 +++ .../version-V3.json | 14 + .../version-V3/credits.md | 17 + .../version-V3/deployed-contracts.md | 21 + .../version-V3/faq.md | 80 ++++ .../version-V3/glossary.md | 66 +++ .../version-V3/guides/_category_.json | 4 + .../version-V3/guides/fetching-data.mdx | 173 ++++++++ .../version-V3/guides/groups.mdx | 151 +++++++ .../version-V3/guides/identities.mdx | 111 +++++ .../version-V3/guides/proofs.mdx | 121 ++++++ .../version-V3/quick-setup.mdx | 279 ++++++++++++ .../version-V3/resources.md | 33 ++ .../version-V3/subgraph.md | 51 +++ .../technical-reference/_category_.json | 4 + .../technical-reference/circuits.md | 57 +++ .../technical-reference/contracts.md | 50 +++ .../version-V3/troubleshooting.mdx | 223 ++++++++++ .../version-V3/what-is-semaphore.md | 53 +++ .../es/docusaurus-theme-classic/navbar.json | 11 + apps/docs/package.json | 50 +++ apps/docs/sidebars.js | 29 ++ .../src/components/IllustrationHero/index.tsx | 35 ++ .../IllustrationHero/styles.module.scss | 9 + apps/docs/src/components/LinkButton/index.tsx | 20 + .../components/LinkButton/styles.module.scss | 44 ++ .../components/OutlineLinkButton/index.tsx | 20 + .../OutlineLinkButton/styles.module.scss | 46 ++ .../src/components/icons/IconArrowRight.tsx | 21 + .../components/icons/IconArrowTopRight.tsx | 21 + apps/docs/src/components/icons/IconAwards.tsx | 25 ++ apps/docs/src/components/icons/IconCheck.tsx | 22 + .../src/components/icons/IconConnections.tsx | 21 + apps/docs/src/components/icons/IconEye.tsx | 30 ++ .../src/components/icons/IconEyeClose.tsx | 21 + apps/docs/src/components/icons/IconFlag.tsx | 25 ++ apps/docs/src/components/icons/IconGroup.tsx | 21 + apps/docs/src/components/icons/IconMoon.tsx | 25 ++ .../docs/src/components/icons/IconProfile.tsx | 30 ++ apps/docs/src/components/icons/IconSun.tsx | 21 + apps/docs/src/components/icons/IconUnion.tsx | 25 ++ apps/docs/src/css/custom.scss | 410 ++++++++++++++++++ apps/docs/src/pages/index.tsx | 318 ++++++++++++++ apps/docs/src/pages/styles.module.scss | 258 +++++++++++ apps/docs/src/theme/Footer/index.tsx | 105 +++++ apps/docs/src/theme/Footer/styles.module.scss | 137 ++++++ .../docs/src/theme/IconExternalLink/index.tsx | 18 + apps/docs/src/theme/Navbar/Search/index.tsx | 11 + apps/docs/src/theme/NavbarItem/index.tsx | 33 ++ apps/docs/src/theme/Toggle/index.tsx | 64 +++ apps/docs/src/theme/Toggle/styles.module.scss | 110 +++++ apps/docs/src/types/global.d.ts | 1 + apps/docs/static/.nojekyll | 0 apps/docs/static/CNAME | 1 + apps/docs/static/img/favicon.ico | Bin 0 -> 15406 bytes apps/docs/static/img/semaphore-icon-dark.svg | 3 + apps/docs/static/img/semaphore-icon.svg | 3 + apps/docs/static/img/semaphore-logo.svg | 3 + apps/docs/static/whitepaper-v1.pdf | Bin 0 -> 305914 bytes apps/docs/tsconfig.json | 7 + apps/docs/versioned_docs/version-V1/audit.md | 17 + .../versioned_docs/version-V1/contract-api.md | 105 +++++ .../version-V1/creditsandresources.md | 33 ++ .../versioned_docs/version-V1/howitworks.md | 137 ++++++ .../versioned_docs/version-V1/libsemaphore.md | 245 +++++++++++ .../versioned_docs/version-V1/quickstart.md | 68 +++ .../versioned_docs/version-V1/trustedsetup.md | 11 + apps/docs/versioned_docs/version-V1/usage.md | 144 ++++++ .../version-V1/what-is-semaphore.md | 105 +++++ .../docs/versioned_docs/version-V2/credits.md | 13 + .../version-V2/deployed-contracts.md | 35 ++ .../versioned_docs/version-V2/glossary.md | 64 +++ .../version-V2/guides/_category_.json | 4 + .../version-V2/guides/groups.md | 122 ++++++ .../version-V2/guides/identities.md | 83 ++++ .../version-V2/guides/proofs.md | 110 +++++ .../versioned_docs/version-V2/quick-setup.md | 304 +++++++++++++ .../versioned_docs/version-V2/resources.md | 19 + .../versioned_docs/version-V2/subgraph.md | 51 +++ .../technical-reference/_category_.json | 4 + .../technical-reference/circuits.md | 55 +++ .../technical-reference/contracts.md | 50 +++ .../version-V2/use-cases/_category_.json | 4 + .../version-V2/use-cases/private-voting.md | 137 ++++++ .../version-V2/what-is-semaphore.md | 49 +++ .../docs/versioned_docs/version-V3/credits.md | 17 + .../version-V3/deployed-contracts.md | 21 + apps/docs/versioned_docs/version-V3/faq.md | 80 ++++ .../versioned_docs/version-V3/glossary.md | 67 +++ .../version-V3/guides/_category_.json | 4 + .../version-V3/guides/fetching-data.mdx | 173 ++++++++ .../version-V3/guides/groups.mdx | 151 +++++++ .../version-V3/guides/identities.mdx | 111 +++++ .../version-V3/guides/proofs.mdx | 121 ++++++ .../versioned_docs/version-V3/quick-setup.mdx | 279 ++++++++++++ .../versioned_docs/version-V3/resources.md | 33 ++ .../versioned_docs/version-V3/subgraph.md | 51 +++ .../technical-reference/_category_.json | 4 + .../technical-reference/circuits.md | 55 +++ .../technical-reference/contracts.md | 51 +++ .../version-V3/troubleshooting.mdx | 222 ++++++++++ .../version-V3/what-is-semaphore.md | 54 +++ .../version-V1-sidebars.json | 8 + .../version-V2-sidebars.json | 8 + .../version-V3-sidebars.json | 8 + apps/docs/versions.json | 1 + yarn.lock.REMOVED.git-id | 2 +- 148 files changed, 10101 insertions(+), 4 deletions(-) create mode 100644 apps/docs/.yarn/install-state.gz.REMOVED.git-id create mode 100644 apps/docs/.yarn/releases/yarn-3.2.1.cjs.REMOVED.git-id create mode 100644 apps/docs/.yarnrc.yml create mode 100644 apps/docs/README.md create mode 100644 apps/docs/babel.config.js create mode 100644 apps/docs/docusaurus.config.js create mode 100644 apps/docs/i18n/en/code.json create mode 100644 apps/docs/i18n/en/docusaurus-plugin-content-docs/version-V1.json create mode 100644 apps/docs/i18n/en/docusaurus-plugin-content-docs/version-V2.json create mode 100644 apps/docs/i18n/en/docusaurus-plugin-content-docs/version-V3.json create mode 100644 apps/docs/i18n/en/docusaurus-theme-classic/navbar.json create mode 100644 apps/docs/i18n/es/code.json create mode 100644 apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V1.json create mode 100644 apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V1/audit.md create mode 100644 apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V1/contract-api.md create mode 100644 apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V1/creditsandresources.md create mode 100644 apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V1/howitworks.md create mode 100644 apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V1/libsemaphore.md create mode 100644 apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V1/quickstart.md create mode 100644 apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V1/trustedsetup.md create mode 100644 apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V1/usage.md create mode 100644 apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V1/what-is-semaphore.md create mode 100644 apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2.json create mode 100644 apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/credits.md create mode 100644 apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/deployed-contracts.md create mode 100644 apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/glossary.md create mode 100644 apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/guides/_category_.json create mode 100644 apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/guides/groups.md create mode 100644 apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/guides/identities.md create mode 100644 apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/guides/proofs.md create mode 100644 apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/quick-setup.md create mode 100644 apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/resources.md create mode 100644 apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/subgraph.md create mode 100644 apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/technical-reference/_category_.json create mode 100644 apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/technical-reference/circuits.md create mode 100644 apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/technical-reference/contracts.md create mode 100644 apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/use-cases/_category_.json create mode 100644 apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/use-cases/private-voting.md create mode 100644 apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/what-is-semaphore.md create mode 100644 apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3.json create mode 100644 apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/credits.md create mode 100644 apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/deployed-contracts.md create mode 100644 apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/faq.md create mode 100644 apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/glossary.md create mode 100644 apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/guides/_category_.json create mode 100644 apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/guides/fetching-data.mdx create mode 100644 apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/guides/groups.mdx create mode 100644 apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/guides/identities.mdx create mode 100644 apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/guides/proofs.mdx create mode 100644 apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/quick-setup.mdx create mode 100644 apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/resources.md create mode 100644 apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/subgraph.md create mode 100644 apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/technical-reference/_category_.json create mode 100644 apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/technical-reference/circuits.md create mode 100644 apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/technical-reference/contracts.md create mode 100644 apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/troubleshooting.mdx create mode 100644 apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/what-is-semaphore.md create mode 100644 apps/docs/i18n/es/docusaurus-theme-classic/navbar.json create mode 100644 apps/docs/package.json create mode 100644 apps/docs/sidebars.js create mode 100644 apps/docs/src/components/IllustrationHero/index.tsx create mode 100644 apps/docs/src/components/IllustrationHero/styles.module.scss create mode 100644 apps/docs/src/components/LinkButton/index.tsx create mode 100644 apps/docs/src/components/LinkButton/styles.module.scss create mode 100644 apps/docs/src/components/OutlineLinkButton/index.tsx create mode 100644 apps/docs/src/components/OutlineLinkButton/styles.module.scss create mode 100644 apps/docs/src/components/icons/IconArrowRight.tsx create mode 100644 apps/docs/src/components/icons/IconArrowTopRight.tsx create mode 100644 apps/docs/src/components/icons/IconAwards.tsx create mode 100644 apps/docs/src/components/icons/IconCheck.tsx create mode 100644 apps/docs/src/components/icons/IconConnections.tsx create mode 100644 apps/docs/src/components/icons/IconEye.tsx create mode 100644 apps/docs/src/components/icons/IconEyeClose.tsx create mode 100644 apps/docs/src/components/icons/IconFlag.tsx create mode 100644 apps/docs/src/components/icons/IconGroup.tsx create mode 100644 apps/docs/src/components/icons/IconMoon.tsx create mode 100644 apps/docs/src/components/icons/IconProfile.tsx create mode 100644 apps/docs/src/components/icons/IconSun.tsx create mode 100644 apps/docs/src/components/icons/IconUnion.tsx create mode 100644 apps/docs/src/css/custom.scss create mode 100644 apps/docs/src/pages/index.tsx create mode 100644 apps/docs/src/pages/styles.module.scss create mode 100644 apps/docs/src/theme/Footer/index.tsx create mode 100644 apps/docs/src/theme/Footer/styles.module.scss create mode 100644 apps/docs/src/theme/IconExternalLink/index.tsx create mode 100644 apps/docs/src/theme/Navbar/Search/index.tsx create mode 100644 apps/docs/src/theme/NavbarItem/index.tsx create mode 100644 apps/docs/src/theme/Toggle/index.tsx create mode 100644 apps/docs/src/theme/Toggle/styles.module.scss create mode 100644 apps/docs/src/types/global.d.ts create mode 100644 apps/docs/static/.nojekyll create mode 100644 apps/docs/static/CNAME create mode 100644 apps/docs/static/img/favicon.ico create mode 100644 apps/docs/static/img/semaphore-icon-dark.svg create mode 100644 apps/docs/static/img/semaphore-icon.svg create mode 100644 apps/docs/static/img/semaphore-logo.svg create mode 100644 apps/docs/static/whitepaper-v1.pdf create mode 100644 apps/docs/tsconfig.json create mode 100644 apps/docs/versioned_docs/version-V1/audit.md create mode 100644 apps/docs/versioned_docs/version-V1/contract-api.md create mode 100644 apps/docs/versioned_docs/version-V1/creditsandresources.md create mode 100644 apps/docs/versioned_docs/version-V1/howitworks.md create mode 100644 apps/docs/versioned_docs/version-V1/libsemaphore.md create mode 100644 apps/docs/versioned_docs/version-V1/quickstart.md create mode 100644 apps/docs/versioned_docs/version-V1/trustedsetup.md create mode 100644 apps/docs/versioned_docs/version-V1/usage.md create mode 100644 apps/docs/versioned_docs/version-V1/what-is-semaphore.md create mode 100644 apps/docs/versioned_docs/version-V2/credits.md create mode 100644 apps/docs/versioned_docs/version-V2/deployed-contracts.md create mode 100644 apps/docs/versioned_docs/version-V2/glossary.md create mode 100644 apps/docs/versioned_docs/version-V2/guides/_category_.json create mode 100644 apps/docs/versioned_docs/version-V2/guides/groups.md create mode 100644 apps/docs/versioned_docs/version-V2/guides/identities.md create mode 100644 apps/docs/versioned_docs/version-V2/guides/proofs.md create mode 100644 apps/docs/versioned_docs/version-V2/quick-setup.md create mode 100644 apps/docs/versioned_docs/version-V2/resources.md create mode 100644 apps/docs/versioned_docs/version-V2/subgraph.md create mode 100644 apps/docs/versioned_docs/version-V2/technical-reference/_category_.json create mode 100644 apps/docs/versioned_docs/version-V2/technical-reference/circuits.md create mode 100644 apps/docs/versioned_docs/version-V2/technical-reference/contracts.md create mode 100644 apps/docs/versioned_docs/version-V2/use-cases/_category_.json create mode 100644 apps/docs/versioned_docs/version-V2/use-cases/private-voting.md create mode 100644 apps/docs/versioned_docs/version-V2/what-is-semaphore.md create mode 100644 apps/docs/versioned_docs/version-V3/credits.md create mode 100644 apps/docs/versioned_docs/version-V3/deployed-contracts.md create mode 100644 apps/docs/versioned_docs/version-V3/faq.md create mode 100644 apps/docs/versioned_docs/version-V3/glossary.md create mode 100644 apps/docs/versioned_docs/version-V3/guides/_category_.json create mode 100644 apps/docs/versioned_docs/version-V3/guides/fetching-data.mdx create mode 100644 apps/docs/versioned_docs/version-V3/guides/groups.mdx create mode 100644 apps/docs/versioned_docs/version-V3/guides/identities.mdx create mode 100644 apps/docs/versioned_docs/version-V3/guides/proofs.mdx create mode 100644 apps/docs/versioned_docs/version-V3/quick-setup.mdx create mode 100644 apps/docs/versioned_docs/version-V3/resources.md create mode 100644 apps/docs/versioned_docs/version-V3/subgraph.md create mode 100644 apps/docs/versioned_docs/version-V3/technical-reference/_category_.json create mode 100644 apps/docs/versioned_docs/version-V3/technical-reference/circuits.md create mode 100644 apps/docs/versioned_docs/version-V3/technical-reference/contracts.md create mode 100644 apps/docs/versioned_docs/version-V3/troubleshooting.mdx create mode 100644 apps/docs/versioned_docs/version-V3/what-is-semaphore.md create mode 100644 apps/docs/versioned_sidebars/version-V1-sidebars.json create mode 100644 apps/docs/versioned_sidebars/version-V2-sidebars.json create mode 100644 apps/docs/versioned_sidebars/version-V3-sidebars.json create mode 100644 apps/docs/versions.json diff --git a/.eslintignore b/.eslintignore index 2f4ad0b4..93f17d2b 100644 --- a/.eslintignore +++ b/.eslintignore @@ -22,7 +22,7 @@ circuits # production dist build -docs +/docs # misc .DS_Store diff --git a/.gitignore b/.gitignore index e50cafc3..3ba4537b 100644 --- a/.gitignore +++ b/.gitignore @@ -63,7 +63,11 @@ node_modules/ # Production build dist -docs +/docs + +# Docusaurus cache and generated files +.docusaurus +.cache-loader # Hardhat artifacts diff --git a/.prettierignore b/.prettierignore index bc9b12a7..489cff64 100644 --- a/.prettierignore +++ b/.prettierignore @@ -25,7 +25,7 @@ Verifier*.sol # production dist build -docs +/docs # github .github/ISSUE_TEMPLATE diff --git a/apps/docs/.yarn/install-state.gz.REMOVED.git-id b/apps/docs/.yarn/install-state.gz.REMOVED.git-id new file mode 100644 index 00000000..7066b24b --- /dev/null +++ b/apps/docs/.yarn/install-state.gz.REMOVED.git-id @@ -0,0 +1 @@ +443013b248927520dbfbe92135e304feb4c32181 \ No newline at end of file diff --git a/apps/docs/.yarn/releases/yarn-3.2.1.cjs.REMOVED.git-id b/apps/docs/.yarn/releases/yarn-3.2.1.cjs.REMOVED.git-id new file mode 100644 index 00000000..ee9f521b --- /dev/null +++ b/apps/docs/.yarn/releases/yarn-3.2.1.cjs.REMOVED.git-id @@ -0,0 +1 @@ +b3cadff6efb37a12712d12c2553ec703dbcaa4dd \ No newline at end of file diff --git a/apps/docs/.yarnrc.yml b/apps/docs/.yarnrc.yml new file mode 100644 index 00000000..59da81d9 --- /dev/null +++ b/apps/docs/.yarnrc.yml @@ -0,0 +1,5 @@ +checksumBehavior: update + +nodeLinker: node-modules + +yarnPath: .yarn/releases/yarn-3.2.1.cjs diff --git a/apps/docs/README.md b/apps/docs/README.md new file mode 100644 index 00000000..a137d38f --- /dev/null +++ b/apps/docs/README.md @@ -0,0 +1,75 @@ +

+

+ + + + Semaphore icon + +

+

+ +

+ + + + + Github license + + + Linter eslint + + + Code style prettier + +

+ +
+

+ + 👥 Contributing + +   |   + + 🤝 Code of conduct + +   |   + + 🔎 Issues + +   |   + + 🗣️ Chat & Support + +

+
+ +| This repository contains the code for the Semaphore documentation published at [docs.semaphore.pse.dev](https://docs.semaphore.pse.dev). It uses Markdown syntax and the [Docusaurus](https://docusaurus.io/) site generator. | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | + +## 📜 Usage + +### Start the website + +To generate the HTML and start the site, run: + +```sh +yarn start +``` + +Visit the Semaphore docs site in your browser at [http://localhost:3000](http://localhost:3000). + +### Build + +``` +yarn build +``` + +The `build` command generates static content into the `build` directory that can be served by any static content hosting service. + +### Deploy + +``` +$ GIT_USER= USE_SSH=true yarn deploy +``` + +If you use GitHub pages for hosting, this command lets you build the website and push to the `gh-pages` branch. diff --git a/apps/docs/babel.config.js b/apps/docs/babel.config.js new file mode 100644 index 00000000..86c60203 --- /dev/null +++ b/apps/docs/babel.config.js @@ -0,0 +1,3 @@ +module.exports = { + presets: [require.resolve("@docusaurus/core/lib/babel/preset")] +} diff --git a/apps/docs/docusaurus.config.js b/apps/docs/docusaurus.config.js new file mode 100644 index 00000000..42ffda75 --- /dev/null +++ b/apps/docs/docusaurus.config.js @@ -0,0 +1,102 @@ +// @ts-check +// Note: type annotations allow type checking and IDEs autocompletion + +const lightCodeTheme = require("prism-react-renderer/themes/github") +const darkCodeTheme = require("prism-react-renderer/themes/dracula") + +/** @type {import('@docusaurus/types').Config} */ +module.exports = { + title: "Semaphore", + tagline: "Documentation and Guides", + url: "https://semaphore.pse.dev/", + baseUrl: "/", + favicon: "/img/favicon.ico", + onBrokenLinks: "throw", + onBrokenMarkdownLinks: "warn", + organizationName: "semaphore-protocol", + projectName: "semaphore", + trailingSlash: false, + + plugins: ["docusaurus-plugin-sass"], + + i18n: { + defaultLocale: "en", + locales: ["en", "es"] + }, + + presets: [ + [ + "classic", + /** @type {import('@docusaurus/preset-classic').Options} */ + ({ + docs: { + routeBasePath: "docs/", + sidebarPath: require.resolve("./sidebars.js"), + editUrl: "https://github.com/semaphore-protocol/website/edit/main/", + includeCurrentVersion: false + }, + theme: { + customCss: [require.resolve("./src/css/custom.scss")] + } + }) + ] + ], + + themeConfig: + /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ + ({ + //announcementBar: { + //id: "semaphore-v3", + //content: + //'We are pleased to announce the release of Semaphore V3 🎉', + //backgroundColor: "#DAE0FF", + //textColor: "#000000" + //}, + navbar: { + logo: { + alt: "Semaphore Logo", + src: "img/semaphore-logo.svg" + }, + items: [ + { + label: "Whitepaper", + to: "https://semaphore.pse.dev/whitepaper-v1.pdf", + position: "right", + className: "V1" + }, + { + label: "Documentation", + href: "/docs/introduction", + position: "right", + className: "homepage" + }, + { + label: "Github", + href: "https://github.com/semaphore-protocol", + position: "right" + }, + { + type: "localeDropdown", + position: "right" + } + ] + }, + colorMode: { + defaultMode: "dark", + // Should we use the prefers-color-scheme media-query, + // using user system preferences, instead of the hardcoded defaultMode + respectPrefersColorScheme: true + }, + prism: { + theme: lightCodeTheme, + darkTheme: darkCodeTheme, + additionalLanguages: ["solidity"] + }, + algolia: { + appId: "6P229KVKCB", + apiKey: "879bb5b002b6370f181f0f79f5c2afe2", + indexName: "semaphoreliedzkp", + contextualSearch: true + } + }) +} diff --git a/apps/docs/i18n/en/code.json b/apps/docs/i18n/en/code.json new file mode 100644 index 00000000..6e742118 --- /dev/null +++ b/apps/docs/i18n/en/code.json @@ -0,0 +1,406 @@ +{ + "jumbotron.title": { + "message": "Signal anonymously" + }, + "jumbotron.description": { + "message": "Using zero knowledge, Semaphore allows Ethereum users to prove their membership of a group and send signals such as votes or endorsements without revealing their original identity." + }, + "quick-setup.button": { + "message": "Quick setup" + }, + "boilerplate.button": { + "message": "Boilerplate" + }, + "components.description": { + "message": "Building an Ethereum dApp? Semaphore components make it simple to add a privacy layer!" + }, + "components.button.solidity": { + "message": "Solidity contract" + }, + "components.button.circuits": { + "message": "zkSNARK circuits" + }, + "components.button.libraries": { + "message": "JavaScript libraries" + }, + "section.identities.title": { + "message": "Semaphore identities" + }, + "section.identities.description": { + "message": "Given to all Semaphore group members, it is comprised of three parts: identity commitment, trapdoor, and nullifier." + }, + "section.identities.link": { + "message": "Create Semaphore identities" + }, + "section.identities.box1.title": { + "message": "Private values" + }, + "section.identities.box1.description": { + "message": "Trapdoor and nullifier values are the private values of the Semaphore identity. To avoid fraud, the owner must keep both values secret." + }, + "section.identities.box2.title": { + "message": "Public values" + }, + "section.identities.box2.description": { + "message": "Semaphore uses the Poseidon hash function to create the identity commitment from the identity private values. Identity commitments can be made public, similarly to Ethereum addresses." + }, + "section.identities.box3.title": { + "message": "Generate identities" + }, + "section.identities.box3.description": { + "message": "Semaphore identities can be generated deterministically or randomly. Deterministic identities can be generated from the hash of a secret message." + }, + "section.groups.title": { + "message": "Semaphore groups" + }, + "section.groups.description": { + "message": "Semaphore groups are binary incremental Merkle trees that store the public identity commitment of each member." + }, + "section.groups.link": { + "message": "Curate Semaphore groups" + }, + "section.groups.box1.title": { + "message": "Merkle trees" + }, + "section.groups.box1.description": { + "message": "Each leaf contains an identity commitment for a user. The identity commitment proves that the user is a group member without revealing the private identity of the user." + }, + "section.groups.box2.title": { + "message": "Types of groups" + }, + "section.groups.box2.description": { + "message": "Groups can be created and managed in a decentralized fashion with Semaphore contracts or off-chain with our JavaScript libraries." + }, + "section.groups.box3.title": { + "message": "Group management" + }, + "section.groups.box3.description": { + "message": "Users can join and leave groups by themselves, or an admin can add and remove them. Admins can be centralized authorities, Ethereum accounts, multi-sig wallets or smart contracts." + }, + "section.proofs.title": { + "message": "Semaphore proofs" + }, + "section.proofs.description": { + "message": "Semaphore group members can anonymously prove that they are part of a group and that they are generating their own proofs and signals." + }, + "section.proofs.link": { + "message": "Generate Semaphore proofs" + }, + "section.proofs.box1.title": { + "message": "Membership" + }, + "section.proofs.box1.description": { + "message": "Only users who are part of a group can generate a valid proof for that group." + }, + "section.proofs.box2.title": { + "message": "Signals" + }, + "section.proofs.box2.description": { + "message": "Group users can anonymously broadcast signals such as votes or endorsements without revealing their original identity." + }, + "section.proofs.box3.title": { + "message": "Verifiers" + }, + "section.proofs.box3.description": { + "message": "Semaphore proofs can be verified with Semaphore contracts on-chain or with our JavaScript libraries off-chain." + }, + "theme.ErrorPageContent.title": { + "message": "This page crashed.", + "description": "The title of the fallback page when the page crashed" + }, + "theme.ErrorPageContent.tryAgain": { + "message": "Try again", + "description": "The label of the button to try again when the page crashed" + }, + "theme.NotFound.title": { + "message": "Page Not Found", + "description": "The title of the 404 page" + }, + "theme.NotFound.p1": { + "message": "We could not find what you were looking for.", + "description": "The first paragraph of the 404 page" + }, + "theme.NotFound.p2": { + "message": "Please contact the owner of the site that linked you to the original URL and let them know their link is broken.", + "description": "The 2nd paragraph of the 404 page" + }, + "theme.admonition.note": { + "message": "note", + "description": "The default label used for the Note admonition (:::note)" + }, + "theme.admonition.tip": { + "message": "tip", + "description": "The default label used for the Tip admonition (:::tip)" + }, + "theme.admonition.danger": { + "message": "danger", + "description": "The default label used for the Danger admonition (:::danger)" + }, + "theme.admonition.info": { + "message": "info", + "description": "The default label used for the Info admonition (:::info)" + }, + "theme.admonition.caution": { + "message": "caution", + "description": "The default label used for the Caution admonition (:::caution)" + }, + "theme.BackToTopButton.buttonAriaLabel": { + "message": "Scroll back to top", + "description": "The ARIA label for the back to top button" + }, + "theme.blog.archive.title": { + "message": "Archive", + "description": "The page & hero title of the blog archive page" + }, + "theme.blog.archive.description": { + "message": "Archive", + "description": "The page & hero description of the blog archive page" + }, + "theme.blog.paginator.navAriaLabel": { + "message": "Blog list page navigation", + "description": "The ARIA label for the blog pagination" + }, + "theme.blog.paginator.newerEntries": { + "message": "Newer Entries", + "description": "The label used to navigate to the newer blog posts page (previous page)" + }, + "theme.blog.paginator.olderEntries": { + "message": "Older Entries", + "description": "The label used to navigate to the older blog posts page (next page)" + }, + "theme.blog.post.paginator.navAriaLabel": { + "message": "Blog post page navigation", + "description": "The ARIA label for the blog posts pagination" + }, + "theme.blog.post.paginator.newerPost": { + "message": "Newer Post", + "description": "The blog post button label to navigate to the newer/previous post" + }, + "theme.blog.post.paginator.olderPost": { + "message": "Older Post", + "description": "The blog post button label to navigate to the older/next post" + }, + "theme.blog.post.plurals": { + "message": "One post|{count} posts", + "description": "Pluralized label for \"{count} posts\". Use as much plural forms (separated by \"|\") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)" + }, + "theme.blog.tagTitle": { + "message": "{nPosts} tagged with \"{tagName}\"", + "description": "The title of the page for a blog tag" + }, + "theme.tags.tagsPageLink": { + "message": "View All Tags", + "description": "The label of the link targeting the tag list page" + }, + "theme.colorToggle.ariaLabel": { + "message": "Switch between dark and light mode (currently {mode})", + "description": "The ARIA label for the navbar color mode toggle" + }, + "theme.colorToggle.ariaLabel.mode.dark": { + "message": "dark mode", + "description": "The name for the dark color mode" + }, + "theme.colorToggle.ariaLabel.mode.light": { + "message": "light mode", + "description": "The name for the light color mode" + }, + "theme.docs.breadcrumbs.home": { + "message": "Home page", + "description": "The ARIA label for the home page in the breadcrumbs" + }, + "theme.docs.breadcrumbs.navAriaLabel": { + "message": "Breadcrumbs", + "description": "The ARIA label for the breadcrumbs" + }, + "theme.docs.DocCard.categoryDescription": { + "message": "{count} items", + "description": "The default description for a category card in the generated index about how many items this category includes" + }, + "theme.docs.paginator.navAriaLabel": { + "message": "Docs pages navigation", + "description": "The ARIA label for the docs pagination" + }, + "theme.docs.paginator.previous": { + "message": "Previous", + "description": "The label used to navigate to the previous doc" + }, + "theme.docs.paginator.next": { + "message": "Next", + "description": "The label used to navigate to the next doc" + }, + "theme.docs.tagDocListPageTitle.nDocsTagged": { + "message": "One doc tagged|{count} docs tagged", + "description": "Pluralized label for \"{count} docs tagged\". Use as much plural forms (separated by \"|\") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)" + }, + "theme.docs.tagDocListPageTitle": { + "message": "{nDocsTagged} with \"{tagName}\"", + "description": "The title of the page for a docs tag" + }, + "theme.docs.versionBadge.label": { + "message": "Version: {versionLabel}" + }, + "theme.docs.versions.unreleasedVersionLabel": { + "message": "This is unreleased documentation for {siteTitle} {versionLabel} version.", + "description": "The label used to tell the user that he's browsing an unreleased doc version" + }, + "theme.docs.versions.unmaintainedVersionLabel": { + "message": "This is documentation for {siteTitle} {versionLabel}, which is no longer actively maintained.", + "description": "The label used to tell the user that he's browsing an unmaintained doc version" + }, + "theme.docs.versions.latestVersionSuggestionLabel": { + "message": "For up-to-date documentation, see the {latestVersionLink} ({versionLabel}).", + "description": "The label used to tell the user to check the latest version" + }, + "theme.docs.versions.latestVersionLinkLabel": { + "message": "latest version", + "description": "The label used for the latest version suggestion link label" + }, + "theme.common.editThisPage": { + "message": "Edit this page", + "description": "The link label to edit the current page" + }, + "theme.common.headingLinkTitle": { + "message": "Direct link to heading", + "description": "Title for link to heading" + }, + "theme.lastUpdated.atDate": { + "message": " on {date}", + "description": "The words used to describe on which date a page has been last updated" + }, + "theme.lastUpdated.byUser": { + "message": " by {user}", + "description": "The words used to describe by who the page has been last updated" + }, + "theme.lastUpdated.lastUpdatedAtBy": { + "message": "Last updated{atDate}{byUser}", + "description": "The sentence used to display when a page has been last updated, and by who" + }, + "theme.navbar.mobileVersionsDropdown.label": { + "message": "Versions", + "description": "The label for the navbar versions dropdown on mobile view" + }, + "theme.tags.tagsListLabel": { + "message": "Tags:", + "description": "The label alongside a tag list" + }, + "theme.AnnouncementBar.closeButtonAriaLabel": { + "message": "Close", + "description": "The ARIA label for close button of announcement bar" + }, + "theme.blog.sidebar.navAriaLabel": { + "message": "Blog recent posts navigation", + "description": "The ARIA label for recent posts in the blog sidebar" + }, + "theme.CodeBlock.copied": { + "message": "Copied", + "description": "The copied button label on code blocks" + }, + "theme.CodeBlock.copyButtonAriaLabel": { + "message": "Copy code to clipboard", + "description": "The ARIA label for copy code blocks button" + }, + "theme.CodeBlock.copy": { + "message": "Copy", + "description": "The copy button label on code blocks" + }, + "theme.CodeBlock.wordWrapToggle": { + "message": "Toggle word wrap", + "description": "The title attribute for toggle word wrapping button of code block lines" + }, + "theme.DocSidebarItem.toggleCollapsedCategoryAriaLabel": { + "message": "Toggle the collapsible sidebar category '{label}'", + "description": "The ARIA label to toggle the collapsible sidebar category" + }, + "theme.navbar.mobileLanguageDropdown.label": { + "message": "Languages", + "description": "The label for the mobile language switcher dropdown" + }, + "theme.TOCCollapsible.toggleButtonLabel": { + "message": "On this page", + "description": "The label used by the button on the collapsible TOC component" + }, + "theme.blog.post.readMore": { + "message": "Read More", + "description": "The label used in blog post item excerpts to link to full blog posts" + }, + "theme.blog.post.readMoreLabel": { + "message": "Read more about {title}", + "description": "The ARIA label for the link to full blog posts from excerpts" + }, + "theme.blog.post.readingTime.plurals": { + "message": "One min read|{readingTime} min read", + "description": "Pluralized label for \"{readingTime} min read\". Use as much plural forms (separated by \"|\") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)" + }, + "theme.docs.sidebar.collapseButtonTitle": { + "message": "Collapse sidebar", + "description": "The title attribute for collapse button of doc sidebar" + }, + "theme.docs.sidebar.collapseButtonAriaLabel": { + "message": "Collapse sidebar", + "description": "The title attribute for collapse button of doc sidebar" + }, + "theme.docs.sidebar.closeSidebarButtonAriaLabel": { + "message": "Close navigation bar", + "description": "The ARIA label for close button of mobile sidebar" + }, + "theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": { + "message": "← Back to main menu", + "description": "The label of the back button to return to main menu, inside the mobile navbar sidebar secondary menu (notably used to display the docs sidebar)" + }, + "theme.docs.sidebar.toggleSidebarButtonAriaLabel": { + "message": "Toggle navigation bar", + "description": "The ARIA label for hamburger menu button of mobile navigation" + }, + "theme.docs.sidebar.expandButtonTitle": { + "message": "Expand sidebar", + "description": "The ARIA label and title attribute for expand button of doc sidebar" + }, + "theme.docs.sidebar.expandButtonAriaLabel": { + "message": "Expand sidebar", + "description": "The ARIA label and title attribute for expand button of doc sidebar" + }, + "theme.common.skipToMainContent": { + "message": "Skip to main content", + "description": "The skip to content label used for accessibility, allowing to rapidly navigate to main content with keyboard tab/enter navigation" + }, + "theme.tags.tagsPageTitle": { + "message": "Tags", + "description": "The title of the tag list page" + }, + "footer.left.title": { + "message": "About" + }, + "footer.left.description": { + "message": "Semaphore is part of Privacy & Scaling Explorations (PSE), a multidisciplinary team supported by the Ethereum Foundation. PSE explores new use cases for zero knowledge proofs and other cryptographic primitives." + }, + "footer.right.usedby.title": { + "message": "Used by" + }, + "footer.right.usedby.link1": { + "message": "Unirep" + }, + "footer.right.usedby.link2": { + "message": "Interep" + }, + "footer.right.learn.title": { + "message": "Learn" + }, + "footer.right.learn.link1": { + "message": "Github" + }, + "footer.right.learn.link2": { + "message": "Docs" + }, + "footer.right.connect.title": { + "message": "Connect" + }, + "footer.right.connect.link1": { + "message": "Discord" + }, + "footer.right.connect.link2": { + "message": "Twitter" + }, + "footer.copyright": { + "message": "Copyright © 2023 Ethereum Foundation" + } +} diff --git a/apps/docs/i18n/en/docusaurus-plugin-content-docs/version-V1.json b/apps/docs/i18n/en/docusaurus-plugin-content-docs/version-V1.json new file mode 100644 index 00000000..d2ada941 --- /dev/null +++ b/apps/docs/i18n/en/docusaurus-plugin-content-docs/version-V1.json @@ -0,0 +1,6 @@ +{ + "version.label": { + "message": "V1", + "description": "The label for version V1" + } +} diff --git a/apps/docs/i18n/en/docusaurus-plugin-content-docs/version-V2.json b/apps/docs/i18n/en/docusaurus-plugin-content-docs/version-V2.json new file mode 100644 index 00000000..030d6c12 --- /dev/null +++ b/apps/docs/i18n/en/docusaurus-plugin-content-docs/version-V2.json @@ -0,0 +1,18 @@ +{ + "version.label": { + "message": "V2", + "description": "The label for version V2" + }, + "sidebar.mySidebar.category.Guides": { + "message": "Guides", + "description": "The label for category Guides in sidebar mySidebar" + }, + "sidebar.mySidebar.category.Use cases": { + "message": "Use cases", + "description": "The label for category Use cases in sidebar mySidebar" + }, + "sidebar.mySidebar.category.Technical reference": { + "message": "Technical reference", + "description": "The label for category Technical reference in sidebar mySidebar" + } +} diff --git a/apps/docs/i18n/en/docusaurus-plugin-content-docs/version-V3.json b/apps/docs/i18n/en/docusaurus-plugin-content-docs/version-V3.json new file mode 100644 index 00000000..a3e04448 --- /dev/null +++ b/apps/docs/i18n/en/docusaurus-plugin-content-docs/version-V3.json @@ -0,0 +1,14 @@ +{ + "version.label": { + "message": "V3", + "description": "The label for version V3" + }, + "sidebar.mySidebar.category.Guides": { + "message": "Guides", + "description": "The label for category Guides in sidebar mySidebar" + }, + "sidebar.mySidebar.category.Technical reference": { + "message": "Technical reference", + "description": "The label for category Technical reference in sidebar mySidebar" + } +} diff --git a/apps/docs/i18n/en/docusaurus-theme-classic/navbar.json b/apps/docs/i18n/en/docusaurus-theme-classic/navbar.json new file mode 100644 index 00000000..b6d0d1b7 --- /dev/null +++ b/apps/docs/i18n/en/docusaurus-theme-classic/navbar.json @@ -0,0 +1,11 @@ +{ + "item.label.Whitepaper": { + "message": "Whitepaper" + }, + "item.label.Documentation": { + "message": "Documentation" + }, + "item.label.Github": { + "message": "Github" + } +} diff --git a/apps/docs/i18n/es/code.json b/apps/docs/i18n/es/code.json new file mode 100644 index 00000000..211ed58a --- /dev/null +++ b/apps/docs/i18n/es/code.json @@ -0,0 +1,406 @@ +{ + "jumbotron.title": { + "message": "Señaliza de forma anónima" + }, + "jumbotron.description": { + "message": "Utilizando zero knowledge (conocimiento cero), Semaphore permite a los usuarios de Ethereum demostrar su membresía en un grupo y enviar señales como votos o reconocimientos sin revelar su identidad original." + }, + "quick-setup.button": { + "message": "Configuración rápida" + }, + "boilerplate.button": { + "message": "Boilerplate" + }, + "components.description": { + "message": "¿Está desarrollando una dApp en Ethereum? ¡Los componentes Semaphore permiten agregar una capa de privacidad de forma sencilla!" + }, + "components.button.solidity": { + "message": "Contrato en Solidity" + }, + "components.button.circuits": { + "message": "Circuitos zkSNARK" + }, + "components.button.libraries": { + "message": "Librerías JavaScript" + }, + "section.identities.title": { + "message": "Identidades Semaphore" + }, + "section.identities.description": { + "message": "Otorgada a todos los miembros de un grupo Semaphore, está compuesta por tres partes: identity commitment (compromiso de identidad), trapdoor, y nullifier." + }, + "section.identities.link": { + "message": "Crear identidades Semaphore" + }, + "section.identities.box1.title": { + "message": "Valores privados" + }, + "section.identities.box1.description": { + "message": "Los valores trapdoor y nullifier son los valores privados de una identidad Semaphore. Para evitar fraude, la persona dueña debe mantener ambos valores en secreto." + }, + "section.identities.box2.title": { + "message": "Valores públicos" + }, + "section.identities.box2.description": { + "message": "Semaphore utiliza la función hash Poseidon para crear el identtity commitment a partir de los valores privados. Los identity commitments se pueden compartir públicamente, de forma similar a las direcciones Ethereum." + }, + "section.identities.box3.title": { + "message": "Generar identidades" + }, + "section.identities.box3.description": { + "message": "Las identidades Semaphore pueden generarse de forma determinística o aleatoria. Las identidades determinísticas se pueden generar a partir del hash de un mensaje secreto." + }, + "section.groups.title": { + "message": "Grupos Semaphore" + }, + "section.groups.description": { + "message": "Los grupos Semaphore son árboles de Merkle binarios e incrementales que almacenan el identity commitment público de cada miembro." + }, + "section.groups.link": { + "message": "Curaduría de grupos Semaphore" + }, + "section.groups.box1.title": { + "message": "Árboles de Merkle" + }, + "section.groups.box1.description": { + "message": "Cada hoja contiene el identity commitment de un usuario. El identity commitment demuestra que el usuario es miembro de un grupo sin revelar la identidad privada del usuario." + }, + "section.groups.box2.title": { + "message": "Tipos de grupos" + }, + "section.groups.box2.description": { + "message": "Los grupos pueden crearse y administrarse de forma descentralizada con contratos Semaphore o off-chain (fuera de la cadena) con nuestras librerías en JavaScript." + }, + "section.groups.box3.title": { + "message": "Administración de grupos" + }, + "section.groups.box3.description": { + "message": "Los usuarios pueden unirse y abandonar un grupo por si solos, o un administrador puede agregarles o removerles. Los administradores pueden ser autoridades centralizadas, cuentas Ethereum, carteras multi-sig o smart contracts (contratos inteligentes)." + }, + "section.proofs.title": { + "message": "Pruebas Semaphore" + }, + "section.proofs.description": { + "message": "Los miembros de un grupo Semaphore pueden demostrar de forma anónima que pertenecen a un grupo y que ellos están generando sus propias pruebas y señales." + }, + "section.proofs.link": { + "message": "Generar pruebas Semaphore" + }, + "section.proofs.box1.title": { + "message": "Membresía" + }, + "section.proofs.box1.description": { + "message": "Únicamente los usuarios que forman parte de un grupo pueden generar una prueba válida para ese grupo." + }, + "section.proofs.box2.title": { + "message": "Señales" + }, + "section.proofs.box2.description": { + "message": "Los usuarios del grupo pueden transmitir señales anónimamente, como votos o reconocimientos, sin revelar su identidad original." + }, + "section.proofs.box3.title": { + "message": "Verificadores" + }, + "section.proofs.box3.description": { + "message": "Las pruebas Semaphore pueden verificarse on-chain (dentro de la cadena) con contratos en Solidity o off-chain con nuestras librerías en JavaScript." + }, + "theme.ErrorPageContent.title": { + "message": "Esta página no está funcionando.", + "description": "The title of the fallback page when the page crashed" + }, + "theme.ErrorPageContent.tryAgain": { + "message": "Intente de nuevo", + "description": "The label of the button to try again when the page crashed" + }, + "theme.NotFound.title": { + "message": "Página No Encontrada", + "description": "The title of the 404 page" + }, + "theme.NotFound.p1": { + "message": "No pudimos encontrar lo que buscaba.", + "description": "The first paragraph of the 404 page" + }, + "theme.NotFound.p2": { + "message": "Comuníquese con el dueño del sitio que lo vinculó a la URL original y hágale saber que su vínculo está roto.", + "description": "The 2nd paragraph of the 404 page" + }, + "theme.admonition.note": { + "message": "nota", + "description": "The default label used for the Note admonition (:::note)" + }, + "theme.admonition.tip": { + "message": "tip", + "description": "The default label used for the Tip admonition (:::tip)" + }, + "theme.admonition.danger": { + "message": "peligro", + "description": "The default label used for the Danger admonition (:::danger)" + }, + "theme.admonition.info": { + "message": "información", + "description": "The default label used for the Info admonition (:::info)" + }, + "theme.admonition.caution": { + "message": "precaución", + "description": "The default label used for the Caution admonition (:::caution)" + }, + "theme.BackToTopButton.buttonAriaLabel": { + "message": "Volver al principio", + "description": "The ARIA label for the back to top button" + }, + "theme.blog.archive.title": { + "message": "Archivo", + "description": "The page & hero title of the blog archive page" + }, + "theme.blog.archive.description": { + "message": "Archivo", + "description": "The page & hero description of the blog archive page" + }, + "theme.blog.paginator.navAriaLabel": { + "message": "Navegación por la página de la lista de blogs ", + "description": "The ARIA label for the blog pagination" + }, + "theme.blog.paginator.newerEntries": { + "message": "Entradas más recientes", + "description": "The label used to navigate to the newer blog posts page (previous page)" + }, + "theme.blog.paginator.olderEntries": { + "message": "Entradas más antiguas", + "description": "The label used to navigate to the older blog posts page (next page)" + }, + "theme.blog.post.paginator.navAriaLabel": { + "message": "Barra de paginación de publicaciones del blog", + "description": "The ARIA label for the blog posts pagination" + }, + "theme.blog.post.paginator.newerPost": { + "message": "Publicación más reciente", + "description": "The blog post button label to navigate to the newer/previous post" + }, + "theme.blog.post.paginator.olderPost": { + "message": "Publicación más antigua", + "description": "The blog post button label to navigate to the older/next post" + }, + "theme.blog.post.plurals": { + "message": "Una publicación|{count} publicaciones", + "description": "Pluralized label for \"{count} posts\". Use as much plural forms (separated by \"|\") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)" + }, + "theme.blog.tagTitle": { + "message": "{nPosts} etiquetados con \"{tagName}\"", + "description": "The title of the page for a blog tag" + }, + "theme.tags.tagsPageLink": { + "message": "Ver todas las etiquetas", + "description": "The label of the link targeting the tag list page" + }, + "theme.colorToggle.ariaLabel": { + "message": "Cambiar entre el modo día y noche (actualmente en {mode})", + "description": "The ARIA label for the navbar color mode toggle" + }, + "theme.colorToggle.ariaLabel.mode.dark": { + "message": "modo noche", + "description": "The name for the dark color mode" + }, + "theme.colorToggle.ariaLabel.mode.light": { + "message": "modo día", + "description": "The name for the light color mode" + }, + "theme.docs.breadcrumbs.home": { + "message": "Página de inicio", + "description": "The ARIA label for the home page in the breadcrumbs" + }, + "theme.docs.breadcrumbs.navAriaLabel": { + "message": "Breadcrumbs", + "description": "The ARIA label for the breadcrumbs" + }, + "theme.docs.DocCard.categoryDescription": { + "message": "{count} items", + "description": "The default description for a category card in the generated index about how many items this category includes" + }, + "theme.docs.paginator.navAriaLabel": { + "message": "Navegación de páginas de documentos", + "description": "The ARIA label for the docs pagination" + }, + "theme.docs.paginator.previous": { + "message": "Anterior", + "description": "The label used to navigate to the previous doc" + }, + "theme.docs.paginator.next": { + "message": "Siguiente", + "description": "The label used to navigate to the next doc" + }, + "theme.docs.tagDocListPageTitle.nDocsTagged": { + "message": "Un documento etiquetado|{count} documentos etiquetados", + "description": "Pluralized label for \"{count} docs tagged\". Use as much plural forms (separated by \"|\") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)" + }, + "theme.docs.tagDocListPageTitle": { + "message": "{nDocsTagged} con \"{tagName}\"", + "description": "The title of the page for a docs tag" + }, + "theme.docs.versionBadge.label": { + "message": "Versión: {versionLabel}" + }, + "theme.docs.versions.unreleasedVersionLabel": { + "message": "Esta es documentación sin liberar para {siteTitle} {versionLabel} versión.", + "description": "The label used to tell the user that he's browsing an unreleased doc version" + }, + "theme.docs.versions.unmaintainedVersionLabel": { + "message": "Esta es documentación para {siteTitle} {versionLabel}, que ya no se mantiene activamente.", + "description": "The label used to tell the user that he's browsing an unmaintained doc version" + }, + "theme.docs.versions.latestVersionSuggestionLabel": { + "message": "Para la documentación actualizada, vea {latestVersionLink} ({versionLabel}).", + "description": "The label used to tell the user to check the latest version" + }, + "theme.docs.versions.latestVersionLinkLabel": { + "message": "última versión", + "description": "The label used for the latest version suggestion link label" + }, + "theme.common.editThisPage": { + "message": "Editar esta página", + "description": "The link label to edit the current page" + }, + "theme.common.headingLinkTitle": { + "message": "Enlace directo al encabezado", + "description": "Title for link to heading" + }, + "theme.lastUpdated.atDate": { + "message": " en {date}", + "description": "The words used to describe on which date a page has been last updated" + }, + "theme.lastUpdated.byUser": { + "message": " por {user}", + "description": "The words used to describe by who the page has been last updated" + }, + "theme.lastUpdated.lastUpdatedAtBy": { + "message": "Última actualización{atDate}{byUser}", + "description": "The sentence used to display when a page has been last updated, and by who" + }, + "theme.navbar.mobileVersionsDropdown.label": { + "message": "Versiones", + "description": "The label for the navbar versions dropdown on mobile view" + }, + "theme.tags.tagsListLabel": { + "message": "Etiquetas:", + "description": "The label alongside a tag list" + }, + "theme.AnnouncementBar.closeButtonAriaLabel": { + "message": "Cerrar", + "description": "The ARIA label for close button of announcement bar" + }, + "theme.blog.sidebar.navAriaLabel": { + "message": "Navegación de publicaciones recientes", + "description": "The ARIA label for recent posts in the blog sidebar" + }, + "theme.CodeBlock.copied": { + "message": "Copiado", + "description": "The copied button label on code blocks" + }, + "theme.CodeBlock.copyButtonAriaLabel": { + "message": "Copiar código al portapapeles", + "description": "The ARIA label for copy code blocks button" + }, + "theme.CodeBlock.copy": { + "message": "Copiar", + "description": "The copy button label on code blocks" + }, + "theme.CodeBlock.wordWrapToggle": { + "message": "Toggle word wrap", + "description": "The title attribute for toggle word wrapping button of code block lines" + }, + "theme.DocSidebarItem.toggleCollapsedCategoryAriaLabel": { + "message": "Toggle the collapsible sidebar category '{label}'", + "description": "The ARIA label to toggle the collapsible sidebar category" + }, + "theme.navbar.mobileLanguageDropdown.label": { + "message": "Idiomas", + "description": "The label for the mobile language switcher dropdown" + }, + "theme.TOCCollapsible.toggleButtonLabel": { + "message": "En esta página", + "description": "The label used by the button on the collapsible TOC component" + }, + "theme.blog.post.readMore": { + "message": "Leer Más", + "description": "The label used in blog post item excerpts to link to full blog posts" + }, + "theme.blog.post.readMoreLabel": { + "message": "Lea más de {title}", + "description": "The ARIA label for the link to full blog posts from excerpts" + }, + "theme.blog.post.readingTime.plurals": { + "message": "Lectura de un minuto|{readingTime} min de lectura", + "description": "Pluralized label for \"{readingTime} min read\". Use as much plural forms (separated by \"|\") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)" + }, + "theme.docs.sidebar.collapseButtonTitle": { + "message": "Colapsar barra lateral", + "description": "The title attribute for collapse button of doc sidebar" + }, + "theme.docs.sidebar.collapseButtonAriaLabel": { + "message": "Colapsar barra lateral", + "description": "The title attribute for collapse button of doc sidebar" + }, + "theme.docs.sidebar.closeSidebarButtonAriaLabel": { + "message": "Cerrar la barra de navegación", + "description": "The ARIA label for close button of mobile sidebar" + }, + "theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": { + "message": "← Volver al menú principal", + "description": "The label of the back button to return to main menu, inside the mobile navbar sidebar secondary menu (notably used to display the docs sidebar)" + }, + "theme.docs.sidebar.toggleSidebarButtonAriaLabel": { + "message": "Alternar la barra de navegación", + "description": "The ARIA label for hamburger menu button of mobile navigation" + }, + "theme.docs.sidebar.expandButtonTitle": { + "message": "Expandir barra lateral", + "description": "The ARIA label and title attribute for expand button of doc sidebar" + }, + "theme.docs.sidebar.expandButtonAriaLabel": { + "message": "Expandir barra lateral", + "description": "The ARIA label and title attribute for expand button of doc sidebar" + }, + "theme.common.skipToMainContent": { + "message": "Saltar al contenido principal", + "description": "The skip to content label used for accessibility, allowing to rapidly navigate to main content with keyboard tab/enter navigation" + }, + "theme.tags.tagsPageTitle": { + "message": "Etiquetas", + "description": "The title of the tag list page" + }, + "footer.left.title": { + "message": "Acerca de" + }, + "footer.left.description": { + "message": "Semaphore es parte de Privacy & Scaling Explorations (PSE), un equipo multidisciplinario apoyado por la Fundación Ethereum. PSE explora nuevos casos de uso para pruebas de conocimiento cero (ZKPs) y otras bases criptográficas." + }, + "footer.right.usedby.title": { + "message": "Utilizado en" + }, + "footer.right.usedby.link1": { + "message": "Unirep" + }, + "footer.right.usedby.link2": { + "message": "Interep" + }, + "footer.right.learn.title": { + "message": "Aprender" + }, + "footer.right.learn.link1": { + "message": "Github" + }, + "footer.right.learn.link2": { + "message": "Docs" + }, + "footer.right.connect.title": { + "message": "Conecta con nosotros" + }, + "footer.right.connect.link1": { + "message": "Discord" + }, + "footer.right.connect.link2": { + "message": "Twitter" + }, + "footer.copyright": { + "message": "Copyright © 2023 Ethereum Foundation" + } +} diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V1.json b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V1.json new file mode 100644 index 00000000..d2ada941 --- /dev/null +++ b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V1.json @@ -0,0 +1,6 @@ +{ + "version.label": { + "message": "V1", + "description": "The label for version V1" + } +} diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V1/audit.md b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V1/audit.md new file mode 100644 index 00000000..d8c79479 --- /dev/null +++ b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V1/audit.md @@ -0,0 +1,17 @@ +--- +sidebar_position: 8 +--- + +# Security audit + +The [Ethereum Foundation](https://ethereum.org/) and [POA +Network](https://www.poa.network/) commissioned [ABDK +Consulting](https://www.abdk.consulting) to audit the source code of Semaphore +as well as relevant circuits in +[circomlib](https://github.com/iden3/circomlib), which contains components +which the Semaphore zk-SNARK uses. + +The summary of the audit results can be found +[here](https://github.com/appliedzkp/semaphore/tree/master/audit). After three +rounds of fixes, all security and performance issues were fixed, and the few +remaining issues are minor and do not affect security. diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V1/contract-api.md b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V1/contract-api.md new file mode 100644 index 00000000..702a1ba5 --- /dev/null +++ b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V1/contract-api.md @@ -0,0 +1,105 @@ +--- +sidebar_position: 5 +--- + +# Contract API + +## Constructor + +**Contract ABI**: + +`constructor(uint8 _treeLevels, uint232 _firstExternalNullifier)` + +- `_treeLevels`: The depth of the identity tree. +- `_firstExternalNullifier`: The first identity nullifier to add. + +The depth of the identity tree determines how many identity commitments may be +added to this contract: `2 ^ _treeLevels`. Once the tree is full, further +insertions will fail with the revert reason `IncrementalMerkleTree: tree is full`. + +The first external nullifier will be added as an external nullifier to the +contract, and this external nullifier will be active once the deployment +completes. + +## Add, deactivate, or reactivate external nullifiiers + +**Contract ABI**: + +`addExternalNullifier(uint232 _externalNullifier)` + +Adds an external nullifier to the contract. Only the owner can do this. +This external nullifier is active once it is added. + +- `_externalNullifier`: The new external nullifier to set. + +`deactivateExternalNullifier(uint232 _externalNullifier)` + +- `_externalNullifier`: The existing external nullifier to deactivate. + +Deactivate an external nullifier. The external nullifier must already be active +for this function to work. Only the owner can do this. + +`reactivateExternalNullifier(uint232 _externalNullifier)` + +Reactivate an external nullifier. The external nullifier must already be +inactive for this function to work. Only the owner can do this. + +- `_externalNullifier`: The deactivated external nullifier to reactivate. + +## Insert identities + +**Contract ABI**: + +`function insertIdentity(uint256 _identityCommitment)` + +- `_identity_commitment`: The user's identity commitment, which is the hash of + their public key and their identity nullifier (a random 31-byte value). It + should be the output of a Pedersen hash. It is the responsibility of the + caller to verify this. + +**Off-chain `libsemaphore` helper functions**: + +Use `genIdentity()` to generate an `Identity` object, and +`genIdentityCommitment(identity: Identity)` to generate the +`_identityCommitment` value to pass to the contract. + +To convert `identity` to a string and back, so that you can store it in a +database or somewhere safe, use `serialiseIdentity()` and +`unSerialiseIdentity()`. + +See the [Usage section on inserting +identities](./usage#insert-identities) for more information. + +## Broadcast signals + +**Contract ABI**: + +``` +broadcastSignal( + bytes memory _signal, + uint256[8] memory _proof, + uint256 _root, + uint256 _nullifiersHash, + uint232 _externalNullifier +) +``` + +- `_signal`: the signal to broadcast. +- `_proof`: a zk-SNARK proof (see below). +- `_root`: The root of the identity tree, where the user's identity commitment + is the last-inserted leaf. +- `_nullifiersHash`: A uniquely derived hash of the external nullifier, user's + identity nullifier, and the Merkle path index to their identity commitment. + It ensures that a user cannot broadcast a signal with the same external + nullifier more than once. +- `_externalNullifier`: The external nullifier at which the signal is + broadcast. + +**Off-chain `libsemaphore` helper functions**: + +Use `libsemaphore`'s `genWitness()`, `genProof()`, `genPublicSignals()` and +finally `genBroadcastSignalParams()` to generate the parameters to the +contract's `broadcastSignal()` function. + +See the [Usage section on broadcasting +signals](./usage#broadcast-signals) for more information. diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V1/creditsandresources.md b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V1/creditsandresources.md new file mode 100644 index 00000000..5d712949 --- /dev/null +++ b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V1/creditsandresources.md @@ -0,0 +1,33 @@ +--- +sidebar_position: 9 +--- + +# Credits + +- Barry WhiteHat +- Chih Cheng Liang +- Kobi Gurkan +- Koh Wei Jie +- Harry Roberts + +Many thanks to: + +- ABDK Consulting +- Jordi Baylina / iden3 +- POA Network +- PepperSec +- Ethereum Foundation + +# Resources + +[To Mixers and Beyond: presenting Semaphore, a privacy gadget built on Ethereum](https://medium.com/coinmonks/to-mixers-and-beyond-presenting-semaphore-a-privacy-gadget-built-on-ethereum-4c8b00857c9b) - Koh Wei Jie + +[Privacy in Ethereum](https://www.youtube.com/watch?v=maDHYyj30kg) - Barry WhiteHat at the Taipei Ethereum Meetup + +[Snarks for mixing, signaling and scaling by](https://www.youtube.com/watch?v=lv6iK9qezBY) - Barry WhiteHat at Devcon 4 + +[Privacy in Ethereum](https://www.youtube.com/watch?v=zBUo7G95wYE) - Barry WhiteHat at Devcon 5 + +[A trustless Ethereum mixer using zero-knowledge signalling](https://www.youtube.com/watch?v=GzVT16lFOHU) - Koh Wei Jie and Barry WhiteHat at Devcon 5 + +[Hands-on Applications of Zero-Knowledge Signalling](https://www.youtube.com/watch?v=7wd2aAN2jXI) - Koh Wei Jie at Devcon 5 diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V1/howitworks.md b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V1/howitworks.md new file mode 100644 index 00000000..d2c602cf --- /dev/null +++ b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V1/howitworks.md @@ -0,0 +1,137 @@ +--- +sidebar_position: 2 +--- + +# How it works + +## Inserting identities + +An identity is comprised of the following information: + +1. An [EdDSA](https://en.wikipedia.org/wiki/EdDSA) private key. Note that it is + _not_ an Ethereum private key. +2. An identity nullifier, whih is a random 32-byte value. +3. An identity trapdoor, whih is a random 32-byte value. + +An identity commitment is the Pedersen hash of: + +1. The public key associated with the identity's private key. +2. The identity nullifier. +3. The identity trapdoor. + +To register an identity, the user must insert their identity commitment into +Semaphore's identity tree. They can do this by calling the Semaphore contract's +`insertIdentity(uint256 _identityCommitment)` function. See the [API +reference](./contract-api) for more information. + +## Broadcasting signals + +To broadcast a signal, the user must invoke this Semaphore contract function: + +``` +broadcastSignal( + bytes memory _signal, + uint256[8] memory _proof, + uint256 _root, + uint256 _nullifiersHash, + uint232 _externalNullifier +) +``` + +- `_signal`: the signal to broadcast. +- `_proof`: a zk-SNARK proof (see below). +- `_root`: The root of the identity tree, where the user's identity commitment + is the last-inserted leaf. +- `_nullifiersHash`: A uniquely derived hash of the external nullifier, user's + identity nullifier, and the Merkle path index to their identity commitment. + It ensures that a user cannot broadcast a signal with the same external + nullifier more than once. +- `_externalNullifier`: The external nullifier at which the signal is + broadcast. + +To zk-SNARK proof must satisfy the constraints created by Semaphore's zk-SNARK +circuit as described below: + +### The zk-SNARK circuit + +The +[semaphore-base.circom](https://github.com/appliedzkp/semaphore/blob/master/circuits/circom/semaphore-base.circom) +circuit helps to prove the following: + +### That the identity commitment exists in the Merkle tree + +**Private inputs:** + +- `identity_pk`: the user's EdDSA public key +- `identity_nullifier`: a random 32-byte value which the user should save +- `identity_trapdoor`: a random 32-byte value which the user should save +- `identity_path_elements`: the values along the Merkle path to the + user's identity commitment +- `identity_path_index[n_levels]`: the direction (left/right) per tree level + corresponding to the Merkle path to the user's identity commitment + +**Public inputs:** + +- `root`: The Merkle root of the identity tree + +**Procedure:** + +The circuit hashes the public key, identity nullifier, and identity trapdoor to +generate an **identity commitment**. It then verifies the Merkle proof against +the Merkle root and the identity commitment. + +### That the signal was only broadcasted once + +**Private inputs:** + +- `identity_nullifier`: as above +- `identity_path_index`: as above + +**Public inputs:** + +- `external_nullifier`: the 29-byte external nullifier - see above +- `nullifiers_hash`: the hash of the identity nullifier, external nullifier, + and Merkle path index (`identity_path_index`) + +**Procedure:** + +The circuit hashes the given identity nullifier, external nullifier, and Merkle +path index, and checks that it matches the given nullifiers hash. Additionally, +the smart contract ensures that it has not previously seen this nullifiers +hash. This way, double-signalling is impossible. + +### That the signal was truly broadcasted by the user who generated the proof + +**Private inputs:** + +- `identity_pk`: as above +- `auth_sig_r`: the `r` value of the signature of the signal +- `auth_sig_s`: the `s` value of the signature of the signal + +**Public inputs:** + +- `signal_hash`: the hash of the signal +- `external_nullifier`: the 29-byte external nullifier - see above + +**Procedure:** + +The circuit hashes the signal hash and the external nullifier, and verifies +this output against the given public key and signature. This ensures the +authenticity of the signal and prevents front-running attacks. + +## Cryptographic primitives + +Semaphore uses MiMC for the Merkle tree, Pedersen commmitments for the identity +commitments, Blake2 for the nullifiers hash, and EdDSA for the signature. + +MiMC is a relatively new hash function. We use the recommended MiMC +construction from [Albrecht et al](https://eprint.iacr.org/2016/492.pdf), and +there is a prize to break MiMC at [http://mimchash.org](http://mimchash.org) +which has not been claimed yet. + +We have also implemented a version of Semaphore which uses the Poseidon hash +function for the Merkle tree and EdDSA signature verification. This may have +better security than MiMC, allows identity insertions to save about 20% gas, +and roughly halves the proving time. Note, however, that the Poseidon-related +circuits and EVM bytecode generator have not been audited, so use it with +caution. To use it, checkout the `feat/poseidon` branch of this repository. diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V1/libsemaphore.md b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V1/libsemaphore.md new file mode 100644 index 00000000..959ad2b4 --- /dev/null +++ b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V1/libsemaphore.md @@ -0,0 +1,245 @@ +--- +sidebar_position: 6 +--- + +# libsemaphore + +[`libsemaphore`](https://www.npmjs.com/package/libsemaphore) is a helper +library for Semaphore written in Typescript. Any dApp written in Javascript or +Typescript should use it as it provides useful abstractions over common tasks +and objects, such as identities and proof generation. + +Note that only v1.0.14 and above works with the Semaphore code in this +repository. v0.0.x is compatible with the pre-audited Semaphore code. + +## Available types, interfaces, and functions + +### Types + +**`SnarkBigInt`** + +A big integer type compatible with the `snarkjs` library. Note that it is not +advisable to mix variables of this type with `bigNumber`s or `BigInt`s. +Encapsulates `snarkjs.bigInt`. + +**`EddsaPrivateKey`** + +An [EdDSA](https://tools.ietf.org/html/rfc8032) private key which should be 32 +bytes long. Encapsulates a [`Buffer`](https://nodejs.org/api/buffer.html). + +**`EddsaPublicKey`** + +An EdDSA public key. Encapsulates an array of `SnarkBigInt`s. + +**`SnarkProvingKey`** + +A proving key, which when used with a secret _witness_, generates a zk-SNARK +proof about said witness. Encapsulates a `Buffer`. + +**`SnarkVerifyingKey`** + +A verifying key which when used with public inputs to a zk-SNARK and a +`SnarkProof`, can prove the proof's validity. Encapsulates a `Buffer`. + +**`SnarkWitness`** + +The secret inputs to a zk-SNARK. Encapsulates an array of `SnarkBigInt`s. + +**`SnarkPublicSignals`** + +The public inputs to a zk-SNARK. Encapsulates an array of `SnarkBigInt`s. + +### Interfaces + +**`EddsaKeyPair`** + +Encapsulates an `EddsaPublicKey` and an `EddsaPrivateKey`. + +```ts +interface EddsaKeyPair { + pubKey: EddsaPublicKey + privKey: EddsaPrivateKey +} +``` + +**`Identity`** + +Encapsulates all information required to generate an identity commitment, and +is crucial to creating `SnarkProof`s to broadcast signals. + +```ts +interface Identity { + keypair: EddsaKeyPair + identityNullifier: SnarkBigInt + identityTrapdoor: SnarkBigInt +} +``` + +**`SnarkProof`** + +Note that `broadcastSignal()` accepts a `uint256[8]` array for its `_proof` +parameter. See `genBroadcastSignalParams()`. + +```ts +interface SnarkProof { + pi_a: SnarkBigInt[] + pi_b: SnarkBigInt[][] + pi_c: SnarkBigInt[] +} +``` + +### Functions + +**`genPubKey(privKey: EddsaPrivateKey): EddsaPublicKey`** + +Generates a public EdDSA key from a supplied private key. To generate a private +key, use `crypto.randomBytes(32)` where `crypto` is the built-in Node or +browser module. + +**`genIdentity(): Identity`** + +This is a convenience function to generate a fresh and random `Identity`. That +is, the 32-byte private key for the `EddsaKeyPair` is randomly generated, as +are the distinct 31-byte identity nullifier and the 31-byte identity trapdoor +values. + +**`serialiseIdentity(identity: Identity): string`** + +Converts an `Identity` into a JSON string which looks like this: + +```text +["e82cc2b8654705e427df423c6300307a873a2e637028fab3163cf95b18bb172e","a02e517dfb3a4184adaa951d02bfe0fe092d1ee34438721d798db75b8db083","15c6540bf7bddb0616984fccda7e954a0fb5ea4679ac686509dc4bd7ba9c3b"] +``` + +You can also spell this function as `serializeIdentity`. + +To convert this string back into an `Identity`, use `unSerialiseIdentity()`. + +**`unSerialiseIdentity(string: serialisedId): Identity`** + +Converts the `string` output of `serialiseIdentity()` to an `Identity`. + +You can also spell this function as `unSerializeIdentity`. + +**`genIdentityCommitment(identity: Identity): SnarkBigInt`** + +Generates an identity commitment, which is the hash of the public key, the +identity nullifier, and the identity trapdoor. + +**`async genProof(witness: SnarkWitness, provingKey: SnarkProvingKey): SnarkProof`** + +Generates a `SnarkProof`, which can be sent to the Semaphore contract's +`broadcastSignal()` function. It can also be verified off-chain using +`verifyProof()` below. + +**`genPublicSignals(witness: SnarkWitness, circuit: SnarkCircuit): SnarkPublicSignals`** + +Extracts the public signals to be supplied to the contract or `verifyProof()`. + +**`verifyProof(verifyingKey: SnarkVerifyingKey, proof: SnarkProof, publicSignals: SnarkPublicSignals): boolean`** + +Returns `true` if the given `proof` is valid, given the correct verifying key +and public signals. + +Returns `false` otherwise. + +**`signMsg(privKey: EddsaPrivateKey, msg: SnarkBigInt): EdDSAMiMcSpongeSignature)`** + +Encapsualtes `circomlib.eddsa.signMiMCSponge` to sign a message `msg` using private key `privKey`. + +**`verifySignature(msg: SnarkBigInt, signature: EdDSAMiMcSpongeSignature, pubKey: EddsaPublicKey)`: boolean** + +Returns `true` if the cryptographic `signature` of the signed `msg` is from the +private key associated with `pubKey`. + +Returns `false` otherwise. + +**`setupTree(levels: number, prefix: string): MerkleTree`** + +Returns a Merkle tree created using +[`semaphore-merkle-tree`](https://www.npmjs.com/package/semaphore-merkle-tree) +with the same number of levels which the Semaphore zk-SNARK circuit expects. +This tree is also configured to use `MimcSpongeHasher`, which is also what the +circuit expects. + +`levels` sets the number of levels of the tree. A tree with 20 levels, for +instance, supports up to 1048576 deposits. + +**`genCircuit(circuitDefinition: any)`** + +Returns a `new snarkjs.Circuit(circuitDefinition)`. The `circuitDefinition` +object should be the `JSON.parse`d result of the `circom` command which +converts a `.circom` file to a `.json` file. + +**`async genWitness(...)`** + +This function has the following signature: + +```ts +const genWitness = async ( + signal: string, + circuit: SnarkCircuit, + identity: Identity, + idCommitments: SnarkBigInt[] | BigInt[] | ethers.utils.BigNumber[], + treeDepth: number, + externalNullifier: SnarkBigInt, +) +``` + +- `signal` is the string you wish to broadcast. +- `circuit` is the output of `genCircuit()`. +- `identity` is the `Identity` whose identity commitment you want to prove is + in the set of registered identities. +- `idCommitments` is an array of registered identity commmitments; i.e. the + leaves of the tree. +- `treeDepth` is the number of levels which the Merkle tree used has +- `externalNullifier` is the current external nullifier + +It returns an object as such: + +- `witness`: The witness to pass to `genProof()`. +- `signal`: The computed signal for Semaphore. This is the hash of the + recipient's address, relayer's address, and fee. +- `signalHash`: The hash of the computed signal. +- `msg`: The hash of the external nullifier and the signal hash +- `signature`: The signature on the above msg. +- `tree`: The Merkle tree object after it has been updated with the identity commitment +- `identityPath`: The Merkle path to the identity commmitment +- `identityPathIndex`: The leaf index of the identity commitment +- `identityPathElements`: The elements along the above Merkle path + +Only `witness` is essential to generate the proof; the other data is only +useful for debugging and additional off-chain checks, such as verifying the +signature and the Merkle tree root. + +**`formatForVerifierContract = (proof: SnarkProof, publicSignals: SnarkPublicSignals`** + +Converts the data in `proof` and `publicSignals` to strings and rearranges +elements of `proof.pi_b` so that `snarkjs`'s `verifier.sol` will accept it. +To be specific, it returns an object as such: + +```ts +{ + a: [ proof.pi_a[0].toString(), proof.pi_a[1].toString() ], + b: [ + [ proof.pi_b[0][1].toString(), proof.pi_b[0][0].toString() ], + [ proof.pi_b[1][1].toString(), proof.pi_b[1][0].toString() ], + ], + c: [ proof.pi_c[0].toString(), proof.pi_c[1].toString() ], + input: publicSignals.map((x) => x.toString()), +} +``` + +**`stringifyBigInts = (obj: any) => object`** + +Encapsulates `snarkjs.stringifyBigInts()`. Makes it easy to convert `SnarkProof`s to JSON. + +**`unstringifyBigInts = (obj: any) => object`** + +Encapsulates `snarkjs.unstringifyBigInts()`. Makes it easy to convert JSON to `SnarkProof`s. + +**`genExternalNullifier = (plaintext: string) => string`** + +Each external nullifier must be at most 29 bytes large. This function +keccak-256-hashes a given `plaintext`, takes the last 29 bytes, and pads it +(from the start) with 0s, and returns the resulting hex string. diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V1/quickstart.md b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V1/quickstart.md new file mode 100644 index 00000000..0a10bf7e --- /dev/null +++ b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V1/quickstart.md @@ -0,0 +1,68 @@ +--- +sidebar_position: 3 +--- + +# Quick start + +Semaphore has been tested with Node 11.14.0. It will run with Node 12 LTE but +we highly recommend using Node 11.14.0 if you wish to develop on its source +code, as one of its dependencies, `script`, cannot compile when if you use Node 12. + +Use [`nvm`](https://github.com/nvm-sh/nvm) to manage your Node version. + +Clone this repository, install dependencies, and build the source code: + +```bash +git clone git@github.com:kobigurk/semaphore.git && \ +cd semaphore && \ +npm i && \ +npm run bootstrap && \ +npm run build +``` + +**Note**: we use `lerna` to manage the `circuits`, `config`, and `contracts` +subpackages. Do not run `npm install` within any of these directories. Instead, +just run `npm run bootstrap` in the main directory. + +Next, either download the compiled zk-SNARK circuit, proving key, and +verification key (note that these keys are for testing purposes, and not for +production, as there is no certainty that the toxic waste was securely +discarded). + +To download the circuit, proving key, and verification key, run: + +```bash +# Start from the base directory + +./circuits/scripts/download_snarks.sh +``` + +To generate the above files locally instead, run: + +```bash +# Start from the base directory + +./circuits/scripts/build_snarks.sh +``` + +This process should take about 45 minutes. + +Build the Solidity contracts (you need `solc` v 0.5.12 installed in your +`$PATH`): + +```bash +# Start from the base directory + +cd contracts && \ +npm run compileSol +``` + +Run tests while still in the `contracts/` directory: + +```bash +# The first command tests the Merkle tree contract and the second +# tests the Semaphore contract + +npm run test-semaphore && \ +npm run test-mt +``` diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V1/trustedsetup.md b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V1/trustedsetup.md new file mode 100644 index 00000000..a25473ef --- /dev/null +++ b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V1/trustedsetup.md @@ -0,0 +1,11 @@ +--- +sidebar_position: 7 +--- + +# Multi-party trusted setup + +The Semaphore authors will use the [Perpetual Powers of +Tau](https://github.com/weijiekoh/perpetualpowersoftau/) ceremony and a random +beacon as phase 1 of the trusted setup. + +More details about phase 2 will be released soon. diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V1/usage.md b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V1/usage.md new file mode 100644 index 00000000..d0f62b22 --- /dev/null +++ b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V1/usage.md @@ -0,0 +1,144 @@ +--- +sidebar_position: 4 +--- + +# Usage + +The Semaphore contract forms a base layer for other contracts to create +applications that rely on anonymous signaling. + +First, you should ensure that the proving key, verification key, and circuit +file, which are static, be easily available to your users. These may be hosted +in a CDN or bundled with your application code. + +The Semaphore team has not performed a trusted setup yet, so trustworthy +versions of these files are not available yet. + +Untrusted versions of these files, however, may be obtained via the +`circuits/scripts/download_snarks.sh` script. + +Next, to have full flexibility over Semaphore's mechanisms, write a Client +contract and set the owner of the Semaphore contract as the address of the +Client contract. You may also write a Client contract which deploys a Semaphore +contract in its constructor, or on the fly. + +With the Client contract as the owner of the Semaphore contract, the Client +contract may call owner-only Semaphore functions such as +`addExternalNullifier()`. + +## Add, deactivate, or reactivate external nullifiiers + +These functions add, deactivate, and reactivate an external nullifier respectively. +As each identity can only signal once to an external nullifier, and as a signal +can only be successfully broadcasted to an active external nullifier, these +functions enable use cases where it is necessary to have multiple external +nullifiers or to activate and/or deactivate them. + +Refer to the [high-level explanation of +Semaphore](https://medium.com/coinmonks/to-mixers-and-beyond-presenting-semaphore-a-privacy-gadget-built-on-ethereum-4c8b00857c9b) +for more details. + +## Set broadcast permissioning + +Note that `Semaphore.broadcastSignal()` is permissioned by default, so if you +wish for anyone to be able to broadcast a signal, the owner of the Semaphore +contract (either a Client contract or externally owned account) must first +invoke `setPermissioning(false)`. + +See [SemaphoreClient.sol](https://github.com/appliedzkp/semaphore/blob/master/contracts/sol/SemaphoreClient.sol) for an example. + +## Insert identities + +To generate an identity commitment, use the `libsemaphore` functions +`genIdentity()` and `genIdentityCommitment()` Typescript (or Javascript) +functions: + +```ts +const identity: Identity = genIdentity() +const identityCommitment = genIdentityCommitment(identity) +``` + +Be sure to store `identity` somewhere safe. The `serialiseIdentity()` function +can help with this: + +`const serialisedId: string = serialiseIdentity(identity: Identity)` + +It converts an `Identity` into a JSON string which looks like this: + +```text +["e82cc2b8654705e427df423c6300307a873a2e637028fab3163cf95b18bb172e","a02e517dfb3a4184adaa951d02bfe0fe092d1ee34438721d798db75b8db083","15c6540bf7bddb0616984fccda7e954a0fb5ea4679ac686509dc4bd7ba9c3b"] +``` + +To convert this string back into an `Identity`, use `unSerialiseIdentity()`. + +`const id: Identity = unSerialiseIdentity(serialisedId)` + +## Broadcast signals + +First obtain the leaves of the identity tree (in sequence, up to the user's +identity commitment, or more). + +```ts +const leaves = +``` + +Next, load the circuit from disk (or from a remote source): + +```ts +const circuitPath = path.join(__dirname, "/path/to/circuit.json") +const cirDef = JSON.parse(fs.readFileSync(circuitPath).toString()) +const circuit = genCircuit(cirDef) +``` + +Next, use `libsemaphore`'s `genWitness()` helper function as such: + +``` +const result = await genWitness( + signal, + circuit, + identity, + leaves, + num_levels, + external_nullifier, +) +``` + +- `signal`: a string which is the signal to broadcast. +- `circuit`: the output of `genCircuit()` (see above). +- `identity`: the user's identity as an `Identity` object. +- `leaves` the list of leaves in the tree (see above). +- `num_levels`: the depth of the Merkle tree. +- `external_nullifier`: the external nullifier at which to broadcast. + +Load the proving key from disk (or from a remote source): + +```ts +const provingKeyPath = path.join(__dirname, "/path/to/proving_key.bin") +const provingKey: SnarkProvingKey = fs.readFileSync(provingKeyPath) +``` + +Generate the proof (this takes about 30-45 seconds on a modern laptop): + +```ts +const proof = await genProof(result.witness, provingKey) +``` + +Generate the `broadcastSignal()` parameters: + +```ts +const publicSignals = genPublicSignals(result.witness, circuit) +const params = genBroadcastSignalParams(result, proof, publicSignals) +``` + +Finally, invoke `broadcastSignal()` with the parameters: + +```ts +const tx = await semaphoreClientContract.broadcastSignal( + ethers.utils.toUtf8Bytes(signal), + params.proof, + params.root, + params.nullifiersHash, + external_nullifier, + { gasLimit: 500000 } +) +``` diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V1/what-is-semaphore.md b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V1/what-is-semaphore.md new file mode 100644 index 00000000..40fa61b5 --- /dev/null +++ b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V1/what-is-semaphore.md @@ -0,0 +1,105 @@ +--- +id: introduction +title: What Is Semaphore? +sidebar_position: 1 +--- + +## Overview + +[Semaphore](https://github.com/appliedzkp/semaphore) is a zero-knowledge gadget +which allows Ethereum users to prove their membership of a set which they had +previously joined without revealing their original identity. At the same time, +it allows users to signal their endorsement of an arbitrary string. It is +designed to be a simple and generic privacy layer for Ethereum dApps. Use cases +include private voting, whistleblowing, mixers, and anonymous authentication. +Finally, it provides a simple built-in mechanism to prevent double-signalling +or double-spending. + +This gadget comprises of smart contracts and +[zero-knowledge](https://z.cash/technology/zksnarks/) components which work in +tandem. The Semaphore smart contract handles state, permissions, and proof +verification on-chain. The zero-knowledge components work off-chain to allow +the user to generate proofs, which allow the smart contract to update its state +if these proofs are valid. + +For a formal description of Semaphore and its underlying cryptographic +mechanisms, also see this document +[here](https://github.com/appliedzkp/semaphore/tree/master/spec). + +Semaphore is designed for smart contract and dApp developers, not end users. +Developers should abstract its features away in order to provide user-friendly +privacy. + +Try a simple demo [here](https://weijiekoh.github.io/semaphore-ui/) or read a +high-level description of Semaphore +[here](https://medium.com/coinmonks/to-mixers-and-beyond-presenting-semaphore-a-privacy-gadget-built-on-ethereum-4c8b00857c9b). + +## Basic features + +In sum, Semaphore provides the ability to: + +1. Register an identity in a smart contract, and then: + +2. Broadcast a signal: + + - Anonymously prove that their identity is in the set of registered + identities, and at the same time: + + - Publicly store an arbitrary string in the contract, if and only if that + string is unique to the user and the contract’s current external + nullifier, which is a unique value akin to a topic. This means that + double-signalling the same message under the same external nullifier is + not possible. + +### External nullifiers + +Think of an external nullifier as a voting booth where each user may only cast +one vote. If they try to cast a second vote a the same booth, that vote is +invalid. + +An external nullifier is any 29-byte value. Semaphore always starts with one +external nullifier, which is set upon contract deployment. The owner of the +Semaphore contract may add more external nullifiers, deactivate, or reactivate +existing ones. + +The first time a particular user broadcasts a signal to an active external +nullifier `n`, and if the user's proof of membership of the set of registered +users is valid, the transaction will succeed. The second time she does so to +the same `n`, however, her transaction will fail. + +Additionally, all signals broadcast transactions to a deactivated external +nullifier will fail. + +Each client application must use the above features of Semaphore in a unique +way to achieve its privacy goals. A mixer, for instance, would use one external +nullifier as such: + +| Signal | External nullifier | +| ----------------------------------------------------------------------------- | ---------------------------- | +| The hash of the recipient's address, relayer's address, and the relayer's fee | The mixer contract's address | + +This allows anonymous withdrawals of funds (via a transaction relayer, who is +rewarded with a fee), and prevents double-spending as there is only one +external nullifier. + +An anonymous voting app would be configured differently: + +| Signal | External nullifier | +| ----------------------------------- | ------------------------ | +| The hash of the respondent's answer | The hash of the question | + +This allows any user to vote with an arbitary response (e.g. yes, no, or maybe) +to any question. The user, however, can only vote once per question. + +## About the code + +This repository contains the code for Semaphore's contracts written in +Soliidty, and zk-SNARK circuits written in +[circom](https://github.com/iden3/circom). It also contains Typescript code to +execute tests. + +The code has been audited by ABDK Consulting. Their suggested security and +efficiency fixes have been applied. + +A multi-party computation to produce the zk-SNARK proving and verification keys +for Semaphore will begin in the near future. diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2.json b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2.json new file mode 100644 index 00000000..030d6c12 --- /dev/null +++ b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2.json @@ -0,0 +1,18 @@ +{ + "version.label": { + "message": "V2", + "description": "The label for version V2" + }, + "sidebar.mySidebar.category.Guides": { + "message": "Guides", + "description": "The label for category Guides in sidebar mySidebar" + }, + "sidebar.mySidebar.category.Use cases": { + "message": "Use cases", + "description": "The label for category Use cases in sidebar mySidebar" + }, + "sidebar.mySidebar.category.Technical reference": { + "message": "Technical reference", + "description": "The label for category Technical reference in sidebar mySidebar" + } +} diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/credits.md b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/credits.md new file mode 100644 index 00000000..44c69697 --- /dev/null +++ b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/credits.md @@ -0,0 +1,13 @@ +--- +sidebar_position: 9 +--- + +# Credits + +Semaphore V2 is the work of several people, for a complete list of contributors you can visit our [Github pages](https://github.com/semaphore-protocol/semaphore/graphs/contributors). + +- [Barry WhiteHat](https://github.com/barryWhiteHat) +- [Kobi Gurkan](https://github.com/kobigurk) +- [Koh Wei Jie](https://github.com/weijiekoh) +- [Andrija Novakovic](https://github.com/akinovak) +- [Cedoor](https://github.com/cedoor) diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/deployed-contracts.md b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/deployed-contracts.md new file mode 100644 index 00000000..628c82dc --- /dev/null +++ b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/deployed-contracts.md @@ -0,0 +1,35 @@ +--- +sidebar_position: 5 +--- + +# Deployed contracts + +## Verifiers + +| Contract | Goerli | Arbitrum One | +| -------------- | ----------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | +| Verifier16.sol | [0xA525...6629](https://goerli.etherscan.io/address/0xA5253ba39381Aa99c4C2C5A4D5C2deC036d06629) | [0x6143...1609](https://arbiscan.io/address/0x6143ECd9Fd1A00EDe1046d456f8aab53a7D71609) | +| Verifier17.sol | [0xe041...01Ab](https://goerli.etherscan.io/address/0xe0418A5f8fBF051D6cbc41Ff29855Dd2a02201Ab) | [0xAc12...Fb02](https://arbiscan.io/address/0xAc12fFFE354D6446eb50dd33E683B78FED73Fb02) | +| Verifier18.sol | [0x7CdB...6797](https://goerli.etherscan.io/address/0x7CdB3336d7d7c55Bce0FB1508594C54521656797) | [0x610a...C701](https://arbiscan.io/address/0x610aeF0F2da3CD1C8bDefe4BDB434Ee146E0C701) | +| Verifier19.sol | [0xbd87...190B](https://goerli.etherscan.io/address/0xbd870921d8A5398a3314C950d1fc63b8C3AB190B) | [0x5477...FCb4](https://arbiscan.io/address/0x5477725177035bbC9d70443eb921D29749D6FCb4) | +| Verifier20.sol | [0x2a96...E099](https://goerli.etherscan.io/address/0x2a96c5696F85e3d2aa918496806B5c5a4D93E099) | [0x3fB2...3136](https://arbiscan.io/address/0x3fB2C0988a37b76e760c44e6516aF720935f3136) | +| Verifier21.sol | [0x5Ec7...67Cd](https://goerli.etherscan.io/address/0x5Ec7d851a52A2a25CEc528F42a7ACA8EcF4667Cd) | [0xDc8f...7DF8](https://arbiscan.io/address/0xDc8f6B8A42836d4566256f4c6C53131DFD127DF8) | +| Verifier22.sol | [0x919d...aA5b](https://goerli.etherscan.io/address/0x919d3d9c05FA7411e334deA5a763354fC7B6aA5b) | [0x6962...32f2](https://arbiscan.io/address/0x6962b5e706be5278eeCb01c286b50A48484632f2) | +| Verifier23.sol | [0x6391...E552](https://goerli.etherscan.io/address/0x63917b00a6dA7865bEfdd107AfC83CC2e6BDE552) | [0x41e4...dF6B](https://arbiscan.io/address/0x41e4796Bd89B4BF04013b559c93fC32E9a2BdF6B) | +| Verifier24.sol | [0xd05C...0E7D](https://goerli.etherscan.io/address/0xd05CAd7d940114c1419098EE3cEA0776ab510E7D) | [0xD528...5b60](https://arbiscan.io/address/0xD528B1D1408ab3583af4694F92b0aFEbE33d5b60) | +| Verifier25.sol | [0x6D98...5ACb](https://goerli.etherscan.io/address/0x6D9862e6140D94E932d94c8BcE74a0BDD0ea5ACb) | [0x1683...33e7](https://arbiscan.io/address/0x1683a27EF9c10c5286dB56412E1272cD0Ca733e7) | +| Verifier26.sol | [0x8c29...6c77](https://goerli.etherscan.io/address/0x8c29e0b77e32f704F03eeCE01c041192A5EB6c77) | [0x7819...4C00](https://arbiscan.io/address/0x78194bB665d1E33b97eE45B1A755c15717E94C00) | +| Verifier27.sol | [0x066c...9c4C](https://goerli.etherscan.io/address/0x066cC22f8CA2A8D90D7Ff77D8a10A27e629c9c4C) | [0x997D...6E42](https://arbiscan.io/address/0x997Dac00E6701Ef7F3518280E5a9922801126E42) | +| Verifier28.sol | [0x698F...6D15](https://goerli.etherscan.io/address/0x698F9507f504E2BD238be7da56E8D9fee60C6D15) | [0xDd3C...68c6](https://arbiscan.io/address/0xDd3C7f4cBA2467aE41c0F614A3c3E24bC80268c6) | +| Verifier29.sol | [0xbBfC...6346](https://goerli.etherscan.io/address/0xbBfC2E201C3c3c6F50063c3Edb4746c6Fcb36346) | [0xe53e...60Cd](https://arbiscan.io/address/0xe53eF12093933D5df5691EAbA3821bD1c1EB60Cd) | +| Verifier30.sol | [0x06bc...4bD1](https://goerli.etherscan.io/address/0x06bcD633988c1CE7Bd134DbE2C12119b6f3E4bD1) | [0x7FeA...340e](https://arbiscan.io/address/0x7FeA07c536ABBB0E7FB3c833376EE4EaDc21340e) | +| Verifier31.sol | [0x133b...cCFE](https://goerli.etherscan.io/address/0x133b69Ce47BF20C49368354914DF47519Ca6cCFE) | [0xe453...2178](https://arbiscan.io/address/0xe4539a592df18936202480FBe77E47DE012F2178) | +| Verifier32.sol | [0xe297...2e2D](https://goerli.etherscan.io/address/0xe2978F79cb4AF62e5C990EE5c7E12fb22ee22e2D) | [0x98c9...32F4](https://arbiscan.io/address/0x98c90845A7870e215cBd7265DDC653E6c07032F4) | + +## Semaphore + +| Contract | Goerli | Arbitrum One | +| ------------------------- | ----------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | +| Semaphore.sol | [0x5259...262f](https://goerli.etherscan.io/address/0x5259d32659F1806ccAfcE593ED5a89eBAb85262f) | [0x8633...A604](https://arbiscan.io/address/0x86337c87A56117f8264bbaBA70e5a522C6E8A604) | +| IncrementalBinaryTree.sol | [0x61AE...B236](https://goerli.etherscan.io/address/0x61AE89E372492e53D941DECaaC9821649fa9B236) | [0x91cD...3948](https://arbiscan.io/address/0x91cD2B8573629d00BeC72EA1188d446897BD3948) | +| PoseidonT3.sol | [0xe0A4...350F](https://goerli.etherscan.io/address/0xe0A452533853310C371b50Bd91BB9DCC8961350F) | [0xe0c8...61d0](https://arbiscan.io/address/0xe0c8d1e53D9Bfc9071F6564755FCFf6cC0dB61d0) | diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/glossary.md b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/glossary.md new file mode 100644 index 00000000..235fa2b0 --- /dev/null +++ b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/glossary.md @@ -0,0 +1,64 @@ +--- +sidebar_position: 7 +--- + +# Glossary + +## Semaphore identity + +The identity of a user in the Semaphore protocol. +An identity contains the following three values: + +- [Identity commitment](#identity-commitment): the public value. +- Identity trapdoor and identity nullifier: secret values known only by the user. + +## Semaphore group + +A group is a binary incremental [Merkle tree](#merkle-tree) in which each leaf contains an [identity commitment](#identity-commitment) for a user. +The identity commitment proves that the user is a group member without revealing the Semaphore identity of the user. + +Semaphore uses the **Poseidon** hash function to create Merkle trees. +For more information, see the [Poseidon website](https://www.poseidon-hash.info/). + +## Identity commitment + +The public [Semaphore identity](#semaphore-identity) value used in [Semaphore groups](#semaphore-group). + +Semaphore uses the **Poseidon** hash function to create the identity commitment from the Semaphore identity secret values. +For more information, see the [Poseidon website](https://www.poseidon-hash.info/). + +## Merkle tree + +A tree in which every leaf (i.e., a node that doesn't have children) is labelled with the cryptographic hash of a data block, +and every node that isn't a leaf is labelled with the cryptographic hash of its child node labels. +In zero-knowledge protocols, Merkle trees can be used to efficiently summarize and validate large data sets. +To validate that a tree contains a specific leaf, a verifier only needs a portion of the complete data structure. + +For more information, see [Merkle tree in Wikipedia](https://en.wikipedia.org/wiki/Merkle_tree). + +## Nullifier + +A value used to prevent double entry or double signalling. + +See [Circuit nullifier hash](/docs/technical-reference/circuits/#nullifier-hash). + +## Relay + +A third-party who receives a fee for including relayed transactions in the blockchain (McMenamin, Daza, and Fitz. https://eprint.iacr.org/2022/155.pdf, p.3). +To preserve the anonymity of the user broadcasting a signal with Semaphore, an application may use a relayer to post the signal transaction to Ethereum on behalf of the user. + +Applications may provide rewards for relayers and implement front-running prevention mechanisms, such as requiring the signals to include the relayer’s address, binding the +signal to that specific address (https://semaphore.pse.dev/whitepaper-v1.pdf, p.6). + +## Trusted setup files + +The secure, verifiable parameters generated by Semaphore's trusted setup ceremony. +Semaphore uses the trusted setup files to generate and verify valid zero-knowledge proofs. +To generate or verify valid zero-knowledge proofs with Semaphore, applications must include the following Semaphore _trusted setup_ files: + +- semaphore.zkey +- semaphore.wasm +- semaphore.json + +For a complete list of ready-to-use files, see . +To learn more, see the [trusted setup ceremony](https://storage.googleapis.com/trustedsetup-a86f4.appspot.com/semaphore/semaphore_top_index.html). diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/guides/_category_.json b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/guides/_category_.json new file mode 100644 index 00000000..2a9d3fd8 --- /dev/null +++ b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/guides/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Guides", + "position": 2 +} diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/guides/groups.md b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/guides/groups.md new file mode 100644 index 00000000..546416ec --- /dev/null +++ b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/guides/groups.md @@ -0,0 +1,122 @@ +--- +sidebar_position: 2 +title: Groups +--- + +# Semaphore groups + + + +Use Semaphore in your application or smart contract to create off-chain and on-chain groups. + +A [Semaphore group](/docs/glossary/#semaphore-group) contains [identity commitments](/docs/glossary/#identity-commitment) of group members. +Example uses of groups include the following: + +- Poll question that attendees join to rate an event. +- Ballot that members join to vote on a proposal. +- Whistleblowers who are verified employees of an organization. + +A Semaphore group is an [incremental Merkle tree](/docs/glossary/#incremental-merkle-tree), and group members (i.e., [identity commitments](/docs/glossary/#identity-commitments)) are tree leaves. +Semaphore groups set the following two parameters: + +- **Tree depth**: the maximum number of members a group can contain (`max size = 2 ^ tree depth`). +- **Zero value**: the value used to calculate the zero nodes of the incremental Merkle tree. + +Learn how to work with groups. + +- [**Off-chain groups**](#off-chain-groups) +- [**On-chain groups**](#on-chain-groups) + +## Off-chain groups + +- [Create a group](#create-a-group) +- [Add members](#add-members) +- [Remove or update members](#remove-or-update-members) + +### Create a group + +Use the [`@semaphore-protocol/group`](https://github.com/semaphore-protocol/semaphore/tree/v2.6.1/packages/group) library `Group` class to create an off-chain group. + +#### Options + +- **Tree depth**: (_default `20`_) the maximum number of members a group can contain (`max size = 2 ^ tree depth`). +- **Zero value**: (_default `BigInt(0)`_) the value for a tree node that doesn't have a member assigned. + +To create a group with default _`treeDepth`_ and _`zeroValue`_, call the `Group` constructor without parameters--for example: + +```ts +import { Group } from "@semaphore-protocol/group" + +// Default parameters: treeDepth = 20, zeroValue = BigInt(0). +const group = new Group() +``` + +The following example code passes _`treeDepth`_ to create a group for `2 ^ 30 = 1073741824` members: + +```ts +import { Group } from "@semaphore-protocol/group" + +const group = new Group(30) +``` + +The following example code creates a group with a _`zeroValue`_ of `BigInt(1)`: + +```ts +import { Group } from "@semaphore-protocol/group" + +const group = new Group(20, BigInt(1)) +``` + +### Add members + +Use the `Group addMember` function to add a member (identity commitment) to a group--for example: + +```ts +group.addMember(identityCommitment) +``` + +To add a batch of members to a group, pass an array to the `Group addMembers` function--for example: + +```ts +group.addMembers([identityCommitment1, identityCommitment2]) +``` + +### Remove or update members + +To remove members from a group, pass the member index to the `Group removeMember` function--for example: + +```ts +group.removeMember(0) +``` + +To update members in a group, pass the member index and the new value to the `Group updateMember` function--for example: + +```ts +group.updateMember(0, 2) +``` + +:::caution +Removing a member from a group sets the node value to `zeroValue`. +Given that the node isn't removed, and the length of the `group.members` array doesn't change. +::: + +## On-chain groups + +The [`SemaphoreGroups`](https://github.com/semaphore-protocol/semaphore/tree/v2.6.1/packages/contracts/contracts/base/SemaphoreGroups.sol) contract uses the [`IncrementalBinaryTree`](https://github.com/privacy-scaling-explorations/zk-kit/blob/main/packages/incremental-merkle-tree.sol/contracts/IncrementalBinaryTree.sol) library and provides methods to create and manage groups. + +:::info +You can import `SemaphoreGroups` and other Semaphore contracts from the [`@semaphore-protocol/contracts`](https://github.com/semaphore-protocol/semaphore/tree/v2.6.1/packages/contracts) NPM module. +::: + +Alternatively, you can use an already deployed [`Semaphore`](https://github.com/semaphore-protocol/semaphore/tree/v2.6.1/packages/contracts/contracts/Semaphore.sol) contract and use its group external functions. diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/guides/identities.md b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/guides/identities.md new file mode 100644 index 00000000..ae3221bd --- /dev/null +++ b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/guides/identities.md @@ -0,0 +1,83 @@ +--- +sidebar_position: 1 +title: Identities +--- + +# Semaphore identities + +In order to join a [Semaphore group](/docs/glossary#semaphore-group), a user must first create a [Semaphore identity](/docs/glossary#semaphore-identity). +A Semaphore identity contains two values generated with the identity: + +- Identity trapdoor +- identity nullifier + +To use and verify the identity, the identity owner (user) must know the trapdoor and nullifier values. +To prevent fraud, the owner should keep both values secret. + +## Create identities + +In your code, use the [`@semaphore-protocol/identity`](https://github.com/semaphore-protocol/semaphore/tree/v2.6.1/packages/identity) library to create a Semaphore identity _deterministically_ (from the hash of a message) or _randomly_. + +- [**Create random identities**](#create-random-identities) +- [**Create deterministic identities**](#create-deterministic-identities) + +### Create random identities + +To create a random identity, instantiate `Identity` without any parameters--for example: + +```ts +import { Identity } from "@semaphore-protocol/identity" + +const { trapdoor, nullifier, commitment } = new Identity() +``` + +The new identity contains two random secret values: `trapdoor` and `nullifier`, and one public value: `commitment`. + +The Poseidon hash of the identity nullifier and trapdoor is called the _identity secret_, +and its hash is the _identity commitment_. + +An identity commitment, similarly to Ethereum addresses, is a public value used +in Semaphore groups to represent the identity of a group member. The secret values are similar to +Ethereum private keys and are used to generate Semaphore zero-knowledge proofs and authenticate signals. + +### Create deterministic identities + +If you pass a message as a parameter, Semaphore generates `trapdoor` and `nullifier` +from the _SHA256_ hash of the message. +The message might be a password or a message that the user cryptographically signs with a private key. + +When using deterministic identities, you should always keep the message secret. +Given that the hash is deterministic, anyone with the same message can recreate the same identity. + +```ts +const identity = new Identity("secret-message") +``` + +:::tip +Building a system to save or recover secret values of Semaphore identities is nontrivial. +You may choose to delegate such functionality to existing wallets such as Metamask--for example: + +1. In Metamask, a user signs a message with the private key of their Ethereum account. +2. In your application, the user creates a deterministic identity with the signed message. +3. The user can now recreate their Semaphore identity whenever they want by signing the same message with their Ethereum account in Metamask. + +::: + +## Save your identities + +You can output an identity as a JSON string that you can save and reuse later. +The `Identity.toString()` method generates a JSON array from an identity--for example: + +```ts +console.log(identity.toString()) // View the identity trapdoor and nullifier. + +// '["8255d...", "62c41..."]' +``` + +The array contains the trapdoor and nullifier. + +To reuse the saved identity, pass the JSON to the `Identity()` constructor. + +```ts +const identity2 = new Identity(identity.toString()) +``` diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/guides/proofs.md b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/guides/proofs.md new file mode 100644 index 00000000..e24a53bf --- /dev/null +++ b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/guides/proofs.md @@ -0,0 +1,110 @@ +--- +sidebar_position: 3 +title: Proofs +--- + +# Semaphore proofs + +Learn how to use Semaphore to generate and verify zero-knowledge proofs. + +Once a user joins their [Semaphore identity](/docs/glossary#semaphore-identity) to a [Semaphore group](/docs/glossary#semaphore-group), the user can signal anonymously with a zero-knowledge proof that proves the following: + +- The user is a member of the group. +- The same user created the signal and the proof. + +Developers can use Semaphore for the following: + +- [**Generate a proof off-chain**](#generate-a-proof-off-chain) +- [**Verify a proof off-chain**](#verify-a-proof-off-chain) +- [**Verify a proof on-chain**](#verify-a-proof-on-chain) + +## Generate a proof off-chain + +Use the [`@semaphore-protocol/proof`](https://github.com/semaphore-protocol/semaphore/tree/v2.6.1/packages/proof) library to generate an off-chain proof. +To generate a proof, pass the following properties to the `generateProof` function: + +- `identity`: The Semaphore identity of the user broadcasting the signal and generating the proof. +- `group`: The group to which the user belongs. +- `externalNullifier`: The value that prevents double-signaling. +- `signal`: The signal the user wants to send anonymously. +- `snarkArtifacts`: The `zkey` and `wasm` [trusted setup files](/docs/glossary/#trusted-setup-files). + +In the voting system use case, once all the voters have joined their [identities](/docs/guides/identities#create-an-identity) to the ballot [group](/docs/guides/groups), +a voter can generate a proof to vote for a proposal. +In the call to `generateProof`, the voting system passes the unique ballot ID (the [Merkle tree](/docs/glossary/#merkle-tree/) root of the group) as the +`externalNullifier` to prevent the voter signaling more than once for the ballot. +The following code sample shows how to use `generateProof` to generate the voting proof: + +```ts +import { generateProof } from "@semaphore-protocol/proof" + +const externalNullifier = group.root +const signal = "proposal_1" + +const fullProof = await generateProof(identity, group, externalNullifier, signal, { + zkeyFilePath: "./semaphore.zkey", + wasmFilePath: "./semaphore.wasm" +}) +``` + +## Verify a proof off-chain + +Use the [`@semaphore-protocol/proof`](https://github.com/semaphore-protocol/semaphore/tree/v2.6.1/packages/proof) library to verify a Semaphore proof off-chain. +To verify a proof, pass the following to the `verifyProof` function: + +- _`proof`_: the Semaphore proof. +- _`verificationKey`_: the JavaScript object in the `semaphore.json` [trusted setup file](/docs/glossary/#trusted-setup-files). + +The following code sample shows how to parse the verification key object from `semaphore.json` +and verify the previously generated proof: + +```ts +import { verifyProof } from "@semaphore-protocol/proof" + +const verificationKey = JSON.parse(fs.readFileSync("./semaphore.json", "utf-8")) + +await verifyProof(verificationKey, fullProof) // true or false. +``` + +`verifyProof` returns a Promise that resolves to `true` or `false`. + +## Verify a proof on-chain + +Use the [`SemaphoreCore`](https://github.com/semaphore-protocol/semaphore/tree/v2.6.1/packages/contracts/contracts/base/SemaphoreCore.sol) contract to verify proofs on-chain. It uses a verifier deployed to Ethereum and provides methods hash the signal and verify a proof. + +:::info +You can import `SemaphoreCore` and other Semaphore contracts from the [`@semaphore-protocol/contracts`](https://github.com/semaphore-protocol/semaphore/tree/v2.6.1/packages/contracts) NPM module. +::: + +To verify Semaphore proofs in your contract, import `SemaphoreCore` and pass the following to the `_verifyProof` internal method: + +- _`signal`_: The Semaphore signal to prove. +- _`root`_: The root of the Merkle tree. +- _`nullifierHash`_: a [nullifier hash](#retrieve-a-nullifier-hash). +- _`externalNullifier`_: The external nullifier. +- _`proof`_: A [_Solidity-compatible_ Semaphore proof](#generate-a-solidity-compatible-proof). +- _`verifier`_: The verifier address. + +Remember to save the `nullifierHash` on-chain to avoid double-signaling. + +Alternatively, you can use an already deployed [`Semaphore`](https://github.com/semaphore-protocol/semaphore/tree/v2.6.1/packages/contracts/Semaphore.sol) contract and use its `verifiyProof` external function. + +### Generate a Solidity-compatible proof + +To transform a proof to be compatible with Solidity contracts, pass the proof to the `packToSolidityProof` utility function--for example: + +```ts +import { packToSolidityProof } from "@semaphore-protocol/proof" + +const solidityProof = packToSolidityProof(fullProof.proof) +``` + +Semaphore returns a new Solidity-compatible instance of the proof. + +### Retrieve a nullifier hash + +To get the Semaphore proof nullifier hash, access the proof's `publicSignals.nullifierHash` property--for example: + +```ts +const { nullifierHash } = fullProof.publicSignals +``` diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/quick-setup.md b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/quick-setup.md new file mode 100644 index 00000000..0ae6ad38 --- /dev/null +++ b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/quick-setup.md @@ -0,0 +1,304 @@ +--- +sidebar_position: 2 +--- + +# Quick setup + +Set up a new Hardhat project with Semaphore. +Learn how to create and test an Ethereum smart contract that uses zero-knowledge +proofs to verify membership. + +To check out the code used in this guide, visit the +[quick-setup](https://github.com/semaphore-protocol/quick-setup) repository. + +1. [**Create a Node.js project**](#create-a-nodejs-project) +2. [**Install Hardhat**](#install-hardhat) +3. [**Install Semaphore packages**](#install-semaphore-packages) +4. [**Create the Semaphore contract**](#create-the-semaphore-contract) +5. [**Create a Hardhat task**](#create-a-hardhat-task) +6. [**Test your contracts**](#test-your-contract) +7. [**Deploy your contract**](#deploy-your-contract) + +## Create a Node.js project + +1. Follow the [Node.js _LTS version_](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) + instructions to install `node` (Hardhat may not work with Node.js _Current_). + +2. Follow the [Yarn](https://yarnpkg.com/getting-started/install) instructions + to download and install the `yarn` package manager. + +3. Create a directory for the project and change to the new directory. + + ```bash + mkdir semaphore-example + cd semaphore-example + ``` + +4. In your terminal, run `yarn init` to initialize the Node.js project. + +## Install Hardhat + +[Hardhat](https://hardhat.org/) is a development environment you can use to +compile, deploy, test, and debug Ethereum software. +Hardhat includes the Hardhat Network, a local Ethereum network for development. + +1. Use `yarn` to install [Hardhat](https://hardhat.org/getting-started/): + + ```bash + yarn add hardhat --dev + ``` + +2. Use `yarn` to run `hardhat` and create a JavaScript project: + + ```bash + yarn hardhat + # At the prompt, select "Create a JavaScript project" + # and then enter through the prompts. + ``` + +## Install Semaphore packages + +Semaphore provides contracts, JavaScript libraries and an Hardhat plugin for developers building zero-knowledge applications. + +- `@semaphore-protocol/contracts` provides contracts to manage groups and verify Semaphore proofs on-chain. +- JavaScript libraries help developers build zero-knowledge applications. +- `@semaphore-protocol/hardhat` allow developers Hardhat tasks to deploy verifiers and Semaphore contracts. + +To install these dependencies for your project, do the following: + +1. Use `yarn` to install `@semaphore-protocol/contracts`: + + ```bash + yarn add @semaphore-protocol/contracts@2.6.1 + ``` + +2. Use `yarn` to install the Semaphore JavaScript libraries and the Hardhat plugin: + + ```bash + yarn add @semaphore-protocol/identity@2.6.1 @semaphore-protocol/group@2.6.1 @semaphore-protocol/proof@2.6.1 @semaphore-protocol/hardhat@0.1.0 --dev + ``` + +For more detail about _Semaphore contracts_, see [Contracts](https://semaphore.pse.dev/docs/technical-reference/contracts). +To view the source of our packages, see the [semaphore](https://github.com/semaphore-protocol/semaphore/tree/v2.6.1#-packages) repository. + +## Create the Semaphore contract + +Create a `Greeter` contract that uses the `Semaphore.sol` contract: + +1. Rename `Lock.sol` to `Greeter.sol` and replace the content with the following: + + ```solidity title="./contracts/Greeter.sol" + //SPDX-License-Identifier: MIT + pragma solidity ^0.8.0; + + import "@semaphore-protocol/contracts/interfaces/ISemaphore.sol"; + + /// @title Greeter contract. + /// @dev The following code is just an example to show how Semaphore can be used. + contract Greeter { + event NewGreeting(bytes32 greeting); + event NewUser(uint256 identityCommitment, bytes32 username); + + ISemaphore public semaphore; + + uint256 groupId; + mapping(uint256 => bytes32) users; + + constructor(address semaphoreAddress, uint256 _groupId) { + semaphore = ISemaphore(semaphoreAddress); + groupId = _groupId; + + semaphore.createGroup(groupId, 20, 0, address(this)); + } + + function joinGroup(uint256 identityCommitment, bytes32 username) external { + semaphore.addMember(groupId, identityCommitment); + + users[identityCommitment] = username; + + emit NewUser(identityCommitment, username); + } + + function greet( + bytes32 greeting, + uint256 merkleTreeRoot, + uint256 nullifierHash, + uint256[8] calldata proof + ) external { + semaphore.verifyProof(groupId, merkleTreeRoot, greeting, nullifierHash, groupId, proof); + + emit NewGreeting(greeting); + } + } + ``` + +## Create a Hardhat task + +Hardhat lets you write [tasks](https://hardhat.org/guides/create-task.html#creating-a-task) +that automate building and deploying smart contracts and dApps. +To create a task that deploys the `Greeter` contract, do the following: + +1. Create a `tasks` folder and add a `./tasks/deploy.js` file that contains the following: + + ```javascript title="./tasks/deploy.js" + const { task, types } = require("hardhat/config") + + task("deploy", "Deploy a Greeter contract") + .addOptionalParam("semaphore", "Semaphore contract address", undefined, types.address) + .addParam("group", "Group identifier", 42, types.int) + .addOptionalParam("logs", "Print the logs", true, types.boolean) + .setAction(async ({ logs, semaphore: semaphoreAddress, group: groupId }, { ethers, run }) => { + if (!semaphoreAddress) { + const { address: verifierAddress } = await run("deploy:verifier", { logs, merkleTreeDepth: 20 }) + + const { address } = await run("deploy:semaphore", { + logs, + verifiers: [ + { + merkleTreeDepth: 20, + contractAddress: verifierAddress + } + ] + }) + + semaphoreAddress = address + } + + const Greeter = await ethers.getContractFactory("Greeter") + + const greeter = await Greeter.deploy(semaphoreAddress, groupId) + + await greeter.deployed() + + if (logs) { + console.log(`Greeter contract has been deployed to: ${greeter.address}`) + } + + return greeter + }) + ``` + +2. In your `hardhat.config.js` file, add the following: + + ```javascript title="./hardhat.config.js" + require("@nomiclabs/hardhat-waffle") + require("@semaphore-protocol/hardhat") + require("./tasks/deploy") // Your deploy task. + + module.exports = { + solidity: "0.8.4" + } + ``` + +## Test your contract + +[`hardhat-waffle`](https://hardhat.org/plugins/nomiclabs-hardhat-waffle.html) +lets you write tests with the [Waffle](https://getwaffle.io/) test framework +and [Chai assertions](https://www.chaijs.com/). + +1. Use `yarn` to install the `hardhat-waffle` plugin and dependencies for smart + contract tests: + + ```bash + yarn add -D @nomiclabs/hardhat-waffle 'ethereum-waffle@^3.0.0' \ + @nomiclabs/hardhat-ethers 'ethers@^5.0.0' chai + ``` + +2. Download the Semaphore [zk trusted setup files](http://www.trusted-setup-pse.org/) + and copy them to the `./static` folder. + + ```bash + cd static + wget http://www.trusted-setup-pse.org/semaphore/20/semaphore.zkey + wget http://www.trusted-setup-pse.org/semaphore/20/semaphore.wasm + ``` + + Learn more about [trusted setup files](/docs/glossary/#trusted-setup-files). + +3. Rename the `Lock.js` test file to `Greeter.js` and replace the content with the following: + + ```javascript title="./test/Greeter.js" + const { Identity } = require("@semaphore-protocol/identity") + const { Group } = require("@semaphore-protocol/group") + const { generateProof, packToSolidityProof, verifyProof } = require("@semaphore-protocol/proof") + const { expect } = require("chai") + const { run, ethers } = require("hardhat") + + describe("Greeter", function () { + let greeter + + const users = [] + const groupId = 42 + const group = new Group() + + before(async () => { + greeter = await run("deploy", { logs: false, group: groupId }) + + users.push({ + identity: new Identity(), + username: ethers.utils.formatBytes32String("anon1") + }) + + users.push({ + identity: new Identity(), + username: ethers.utils.formatBytes32String("anon2") + }) + + group.addMember(users[0].identity.generateCommitment()) + group.addMember(users[1].identity.generateCommitment()) + }) + + describe("# joinGroup", () => { + it("Should allow users to join the group", async () => { + for (let i = 0; i < group.members.length; i++) { + const transaction = greeter.joinGroup(group.members[i], users[i].username) + + await expect(transaction).to.emit(greeter, "NewUser").withArgs(group.members[i], users[i].username) + } + }) + }) + + describe("# greet", () => { + const wasmFilePath = "./static/semaphore.wasm" + const zkeyFilePath = "./static/semaphore.zkey" + + it("Should allow users to greet", async () => { + const greeting = ethers.utils.formatBytes32String("Hello World") + + const fullProof = await generateProof(users[1].identity, group, groupId, greeting, { + wasmFilePath, + zkeyFilePath + }) + const solidityProof = packToSolidityProof(fullProof.proof) + + const transaction = greeter.greet( + greeting, + fullProof.publicSignals.merkleRoot, + fullProof.publicSignals.nullifierHash, + solidityProof + ) + + await expect(transaction).to.emit(greeter, "NewGreeting").withArgs(greeting) + }) + }) + }) + ``` + +4. Run the following `yarn` commands to compile and test your contract: + + ```bash + yarn hardhat compile + yarn hardhat test + ``` + +## Deploy your contract + +To deploy your contract in a local Hardhat network (and use it in your dApp), run the following `yarn` commands: + +```bash +yarn hardhat node +yarn hardhat deploy --group 42 --network localhost # In another tab. +``` + +For a more complete demo that provides a starting point for your dApp, +see [semaphore-boilerplate](https://github.com/semaphore-protocol/boilerplate/). diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/resources.md b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/resources.md new file mode 100644 index 00000000..4cf095b6 --- /dev/null +++ b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/resources.md @@ -0,0 +1,19 @@ +--- +sidebar_position: 8 +--- + +# Resources + +[Semaphore V2 is Live!](https://medium.com/privacy-scaling-explorations/semaphore-v2-is-live-f263e9372579) - Privacy and Scaling Explorations + +[To Mixers and Beyond: presenting Semaphore, a privacy gadget built on Ethereum](https://medium.com/coinmonks/to-mixers-and-beyond-presenting-semaphore-a-privacy-gadget-built-on-ethereum-4c8b00857c9b) - Koh Wei Jie + +[Privacy in Ethereum](https://www.youtube.com/watch?v=maDHYyj30kg) - Barry WhiteHat at the Taipei Ethereum Meetup + +[Snarks for mixing, signaling and scaling by](https://www.youtube.com/watch?v=lv6iK9qezBY) - Barry WhiteHat at Devcon 4 + +[Privacy in Ethereum](https://www.youtube.com/watch?v=zBUo7G95wYE) - Barry WhiteHat at Devcon 5 + +[A trustless Ethereum mixer using zero-knowledge signalling](https://www.youtube.com/watch?v=GzVT16lFOHU) - Koh Wei Jie and Barry WhiteHat at Devcon 5 + +[Hands-on Applications of Zero-Knowledge Signalling](https://www.youtube.com/watch?v=7wd2aAN2jXI) - Koh Wei Jie at Devcon 5 diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/subgraph.md b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/subgraph.md new file mode 100644 index 00000000..6805aa7e --- /dev/null +++ b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/subgraph.md @@ -0,0 +1,51 @@ +--- +sidebar_position: 6 +--- + +# Subgraph + +[The Graph](https://thegraph.com/) is a protocol for indexing networks like Ethereum and IPFS. +Site owners publish _subgraphs_ that expose site data for anyone to query. +Semaphore's subgraph allows you to retrieve data from the [`Semaphore.sol`](https://github.com/semaphore-protocol/semaphore/tree/v2.6.1/packages/contracts/Semaphore.sol) smart contract. + +:::tip +The Graph protocol uses the [GraphQL](https://graphql.org/) query lanaguage. For examples, see the [GraphQL API documentation](https://thegraph.com/docs/developer/graphql-api). Visit the [subgraph repository](https://github.com/semaphore-protocol/subgraph) to see the list of Semaphore subgraphs. +::: + +## Schema + +### MerkleTree + +- `id`: unique identifier among all MerkleTree entities, +- `depth`: Merkle tree depth, +- `root`: Merkle tree root, +- `zeroValue`: Merkle tree zero value, +- `numberOfLeaves`: total number of tree leaves, +- `group`: link to the Group entity. + +### Group + +- `id`: unique identifier among all Group entities, +- `merkleTree`: link to the MerkleTree entity, +- `timestamp`: block timestamp, +- `admin`: admin of the group, +- `members`: list of group members. +- `verifiedProofs`: list of group proofs. + +### Member + +- `id`: unique identifier among all Member entities, +- `identityCommitment`: Semaphore identity commitment, +- `timestamp`: block timestamp, +- `index`: index of the tree leaf, +- `group`: link to the Group entity. + +### VerifiedProof + +- `id`: unique identifier among all VerifiedProof entities, +- `signal`: user's signal, +- `merkleTreeRoot`: Merkle tree root, +- `nullifierHash`: nullifier hash, +- `externalNullifier`: external nullifier, +- `timestamp`: block timestamp, +- `group`: link to the Group entity. diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/technical-reference/_category_.json b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/technical-reference/_category_.json new file mode 100644 index 00000000..2187e794 --- /dev/null +++ b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/technical-reference/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Technical reference", + "position": 4 +} diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/technical-reference/circuits.md b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/technical-reference/circuits.md new file mode 100644 index 00000000..b04d609b --- /dev/null +++ b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/technical-reference/circuits.md @@ -0,0 +1,57 @@ +--- +sidebar_position: 2 +--- + +# Circuits + +The [Semaphore circuit](https://github.com/semaphore-protocol/semaphore/tree/v2.6.1/packages/circuits) is the heart of the protocol and consists of three parts: + +- [**Proof of membership**](/docs/technical-reference/circuits#proof-of-membership) +- [**Nullifier hash**](/docs/technical-reference/circuits#nullifier-hash) +- [**Signal**](/docs/technical-reference/circuits#signal) + +![Semaphore circuit](https://github.com/semaphore-protocol/semaphore/raw/v2.6.1/packages/circuits/scheme.png) + +The diagram above shows how the input signals are used in the Semaphore circuit and how the outputs are calculated. + +## Proof of membership + +The circuit hashes the hash of the identity nullifier with the identity trapdoor to generate an identity commitment. Then, it verifies the proof of membership against the Merkle root and the identity commitment. + +**Private inputs:** + +- `treeSiblings[nLevels]`: the values along the Merkle path to the user's identity commitment, +- `treePathIndices[nLevels]`: the direction (0/1) per tree level corresponding to the Merkle path to the user's identity commitment, +- `identityNullifier`: the 32-byte identity secret used as nullifier, +- `identityTrapdoor`: the 32-byte identity secret used as trapdoor. + +**Public outputs:** + +- `root`: The Merkle root of the tree. + +## Nullifier hash + +The circuit hashes the identity nullifier with the external nullifier and then checks that the result matches the provided nullifier hash. +Nullifier hashes saved in a Semaphore smart contract allow the contract to reject a proof that contains a used nullifier hash. + +**Private inputs:** + +- `identityNullifier`: the 32-byte identity secret used as a nullifier. + +**Public inputs:** + +- `externalNullifier`: the 32-byte external nullifier. + +**Public outputs:** + +- `nullifierHash`: the hash of the identity nullifier and the external nullifier; used to prevent double-signaling. + +**Procedure:** + +## Signal + +The circuit calculates a dummy square of the signal hash to prevent any tampering with the proof. + +**Public inputs:** + +- `signalHash`: the hash of the user's signal. diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/technical-reference/contracts.md b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/technical-reference/contracts.md new file mode 100644 index 00000000..100ae45b --- /dev/null +++ b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/technical-reference/contracts.md @@ -0,0 +1,50 @@ +--- +sidebar_position: 3 +--- + +# Contracts + +Semaphore includes three types of contracts: + +- [**Base contracts**](/docs/technical-reference/contracts#base-contracts) +- [**Extension contracts**](/docs/technical-reference/contracts#extension-contracts) +- [**Verifiers**](/docs/technical-reference/contracts#verifiers) + +:::info +To use Semaphore contracts and interfaces in your project, +install the [`@semaphore-protocol/contracts`](https://github.com/semaphore-protocol/semaphore/tree/v2.6.1/packages/contracts) NPM package. +::: + +## Base contracts + +Semaphore provides the following base contracts: + +- [`SemaphoreCore.sol`](https://github.com/semaphore-protocol/semaphore/tree/v2.6.1/packages/contracts/base/SemaphoreCore.sol): contains the functions to verify Semaphore proofs; +- [`SemaphoreGroups.sol`](https://github.com/semaphore-protocol/semaphore/tree/v2.6.1/packages/contracts/base/SemaphoreGroups.sol): contains the functions to create groups and add/remove members. + +These contracts are closely related to the protocol. +You can inherit them in your contract or you can use [`Semaphore.sol`](https://github.com/semaphore-protocol/semaphore/tree/v2.6.1/packages/contracts/Semaphore.sol), which inherits them for you. +See our [deployed contracts](/docs/deployed-contracts#semaphore) to find the addresses for your network. + +:::info +While some dApps may use on-chain groups, others may prefer to use off-chain groups, saving only their tree roots in the contract. +::: + +## Extension contracts + +- [`SemaphoreVoting.sol`](https://github.com/semaphore-protocol/semaphore/tree/v2.6.1/packages/contracts/extensions/SemaphoreVoting.sol): voting contract that contains the essential functions to create polls, add voters, and anonymously cast votes. +- [`SemaphoreWhistleblowing.sol`](https://github.com/semaphore-protocol/semaphore/tree/v2.6.1/packages/contracts/extensions/SemaphoreWhistleblowing.sol): whistleblowing contract that contains the essential functions to create entities (for example: non-profit organizations), add whistleblowers, and anonymously publish leaks. + +These contracts extend the protocol to provide application logic for specific use-cases. +More extensions will be added in the future. + +## Verifiers + +To verify Semaphore proofs, the [`SemaphoreCore.sol`](https://github.com/semaphore-protocol/semaphore/tree/v2.6.1/packages/contracts/base/SemaphoreCore.sol) contract requires the address of a deployed verifier contract. +You can choose to manually deploy the [verifier](https://github.com/semaphore-protocol/semaphore/tree/v2.6.1/packages/contracts/verifiers) you prefer or you can use one of our [deployed verifiers](/docs/deployed-contracts#verifiers). + +Each verifier name indicates the tree depth that it can verify. +For example, given a Semaphore proof generated with a tree depth `20`: + +- The `Verifier20.sol` contract can verify the proof. +- The [group](/docs/guides/groups) used for the proof can have a maximum `2^20=1048576` members. diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/use-cases/_category_.json b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/use-cases/_category_.json new file mode 100644 index 00000000..25e2a80c --- /dev/null +++ b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/use-cases/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Use cases", + "position": 3 +} diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/use-cases/private-voting.md b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/use-cases/private-voting.md new file mode 100644 index 00000000..233f3818 --- /dev/null +++ b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/use-cases/private-voting.md @@ -0,0 +1,137 @@ +--- +sidebar_position: 1 +--- + +# Private voting use case + +The private voting use case describes how Semaphore interacts with your users and Ethereum to allow users to cast private votes in your application. +Learn how Semaphore enables applications to do the following: + +- Register members as voters. +- Allow members to vote anonymously. +- Prove voter membership. +- Record and prove votes. +- Prevent double-voting. + +## Roles + +- **[Developer or community admin](#developer-or-community-admin)** +- **[Community member (dApp user)](#community-member)** +- **[Relay](#relay)** + +### Developer or community admin + +As a developer or community admin, you deploy the following: + +- **Smart contract on Ethereum**: implements the Semaphore **base contract** to create a poll (Semaphore **group** that members join to vote), post transactions, and verify proofs on Ethereum. +- **Decentralized application (dApp)**: your application that provides a user interface (UI) where members join a poll and vote on a proposal. + +### Community member + +Community members connect their wallets to the dApp to take the following actions: + +1. Verify ownership of the community token. +2. Generate an anonymous ID. +3. Cast a vote. + +### Relay + +To preserve anonymity and avoid disclosing the member's wallet address, the dApp may use a [relay](/docs/glossary/#relay) to broadcast the vote. +The relay calls the **contract** function that then posts the member's vote transaction to Ethereum. + +## Private voting + +Consider a scenario where your community issues a token that users can mint. +The token might be a Proof of Attendance (POAP), NFT, or social token that your users can mint to receive membership and vote in your community. + +The voting scenario has the following steps: + +1. [Create a poll](#create-a-poll): Coordinator creates a poll, or _group_, in which members can vote on a proposal. +2. [Register voters](#register-voters): Members join the poll to vote. +3. [Record votes](#record-votes): Once the poll opens, members may cast one vote, or _signal_, on the topic. + +### Create a poll + +A community coordinator or dApp administrator uses the deployed smart contract to create an on-chain (Ethereum) poll, a [Semaphore group](/docs/guides/groups/) that members can join and cast votes to. + +In the following sample code, the voting contract declares a `createPoll` function that uses the Semaphore base `_createGroup` function: + +```ts title="https://github.com/semaphore-protocol/semaphore/contracts/extensions/SemaphoreVoting.sol" + +function createPoll( + uint256 pollId, + address coordinator, + uint8 depth +) public override { + require(address(verifiers[depth]) != address(0), "SemaphoreVoting: depth value is not supported"); + + // highlight-next-line + _createGroup(pollId, depth, 0); + + Poll memory poll; + + poll.coordinator = coordinator; + + polls[pollId] = poll; + + emit PollCreated(pollId, coordinator); +} +``` + +A poll is a Semaphore [group](/docs/guides/groups/) that stores the following: + +- A topic to vote on. +- The public ID of the poll creator. +- [Semaphore IDs](/docs/guides/identities/) of members who joined the poll. + +To create the poll, the administrator calls the smart contract function--for example: + +```ts +SemaphoreVoting.createPoll(pollId, coordinator, depth) +``` + +Next, learn how to [register voters](#register-voters) for the poll. + +### Register voters + +Before a user can register to vote, the dApp needs to verify membership by checking the user's wallet for the NFT. +To grant access to the wallet, the user clicks a `Connect wallet` button in the dApp and allows the dApp to check for the NFT. +Once a member is verified, the dApp provides the following member interactions: + +1. [Generate a private identity](#generate-a-private-identity). +2. [Join a poll](#join-a-poll). + +:::info +To learn how to connect to Ethereum wallets, visit the [ethers.js Getting Started documentation]((https://docs.ethers.io/v5/getting-started). +::: + +#### Generate a private identity + +To generate a private identity, the member completes a form in the dApp UI. +With the form values and the `@semaphore-protocol/identity` library, the dApp prompts the member to sign a wallet message and then generates the signed private identity. +The private identity is known only to the member and can be used in future interactions with the dApp. + +Next, learn how members [join a poll](#join-a-poll). + +#### Join a poll + +Once the member has a private identity for the dApp, the member may select a poll to vote in. +When the member selects a poll, the dApp does the following: + +1. Uses the `@semaphore-protocol/identity` library to generate an anonymous Semaphore ID, or _identity commitment_, from the private identity. +2. Calls a contract function that adds the new Semaphore ID to the on-chain poll. + +With a member registered for a poll, learn how the dApp [records votes](#record-votes). + +### Record votes + +Once members have joined a poll, the coordinator starts the poll to allow voting. +When a member votes (for example, by selecting a radio button), then the dApp takes the following actions: + +1. Uses the `@semaphore-protocol/proof` library to create a proof of the vote, the poll identifier, the Semaphore ID, and a [nullifier](/docs/glossary/#nullifier) that prevents double-voting. +2. Sends the vote proof to the [relay](#relay). + +### Related + +- To get started developing with Semaphore, see the [Quick setup](/docs/quick-setup/) guide. +- For an example app that you can use to start your own project, see [Semaphore boilerplate](https://github.com/semaphore-protocol/boilerplate). diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/what-is-semaphore.md b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/what-is-semaphore.md new file mode 100644 index 00000000..9674090c --- /dev/null +++ b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V2/what-is-semaphore.md @@ -0,0 +1,49 @@ +--- +id: introduction +title: What Is Semaphore? +sidebar_position: 1 +--- + +## Overview + +[Semaphore](https://github.com/semaphore-protocol/semaphore) is a [zero-knowledge](https://z.cash/technology/zksnarks) protocol that allows you to cast a signal (for example, a vote or endorsement) as a provable group member without revealing your identity. +Additionally, it provides a simple mechanism to prevent double-signaling. +Use cases include private voting, whistleblowing, anonymous DAOs and mixers. + +## Features + +With Semaphore, you can allow your users to do the following: + +1. [Create a Semaphore identity](/docs/guides/identities/). +2. [Add their Semaphore identity to a group (i.e. _Merkle tree_)](/docs/guides/groups/). +3. [Send a verifiable, anonymous signal (e.g a vote or endorsement)](/docs/guides/proofs/). + +When a user broadcasts a signal (for example: a vote), Semaphore zero-knowledge +proofs can ensure that the user has joined the group and hasn't already cast a signal with their nullifier. + +Semaphore uses on-chain Solidity contracts and off-chain JavaScript libraries that work in tandem. + +- Off chain, JavaScript libraries can be used to create identities, manage groups and generate proofs. +- On chain, Solidity contracts can be used to manage groups and verify proofs. + +## Developer benefits + +Semaphore is designed to be a simple and generic _privacy layer_ for decentralized applications (dApps) on Ethereum. It encourages modular application design, allowing dApp developers to choose and customize the on-chain and off-chain components they need. + +## About the code + +The core of the protocol is the [circuit logic](https://github.com/semaphore-protocol/semaphore/tree/v2.6.1/packages/circuits/scheme.png). +In addition to circuits, +Semaphore provides [Solidity contracts](https://github.com/semaphore-protocol/semaphore/tree/v2.6.1/packages/contracts) +and [JavaScript libraries](https://github.com/semaphore-protocol/semaphore/tree/v2.6.1#-packages) that allow developers to generate zero-knowledge proofs and verify them with minimal effort. + +### Audits + +| Version | Report | Scope | +| ------- | -------------------------------------------------------------------------------------------------------------------- | ------------------------ | +| v2.0.0 | [Semaphore_2.0.0_Audit.pdf](https://github.com/semaphore-protocol/semaphore/files/9850441/Semaphore_2.0.0_Audit.pdf) | `circuits`, `contracts` | +| v2.5.0 | [Semaphore_2.5.0_Audit.pdf](https://github.com/semaphore-protocol/semaphore/files/9845008/Semaphore_2.5.0_Audit.pdf) | `contracts`, `libraries` | + +:::info +If you are using the previous version of Semaphore, see the [Semaphore V1](/docs/V1/introduction) documentation. +::: diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3.json b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3.json new file mode 100644 index 00000000..1584f65f --- /dev/null +++ b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3.json @@ -0,0 +1,14 @@ +{ + "version.label": { + "message": "V3", + "description": "The label for version V3" + }, + "sidebar.mySidebar.category.Guides": { + "message": "Guías", + "description": "The label for category Guides in sidebar mySidebar" + }, + "sidebar.mySidebar.category.Technical reference": { + "message": "Referencia técnica", + "description": "The label for category Technical reference in sidebar mySidebar" + } +} diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/credits.md b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/credits.md new file mode 100644 index 00000000..25da2941 --- /dev/null +++ b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/credits.md @@ -0,0 +1,17 @@ +--- +sidebar_position: 10 +--- + +# Créditos + +Semaphore es el esfuerzo de varias personas, para ver una lista completa de contribuidores puede visitar nuestras [páginas en Github](https://github.com/semaphore-protocol/semaphore/graphs/contributors). + +- [Barry WhiteHat](https://github.com/barryWhiteHat) +- [Kobi Gurkan](https://github.com/kobigurk) +- [Koh Wei Jie](https://github.com/weijiekoh) +- [Andrija Novakovic](https://github.com/akinovak) +- [Cedoor](https://github.com/cedoor) +- [Rachel Aux](https://github.com/rachelaux) +- [Andy Guzman](https://github.com/aguzmant103) +- [Vivian Plasencia](https://github.com/vplasencia) +- [LauNaMu](https://github.com/0xyNaMu) diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/deployed-contracts.md b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/deployed-contracts.md new file mode 100644 index 00000000..ba916756 --- /dev/null +++ b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/deployed-contracts.md @@ -0,0 +1,21 @@ +--- +sidebar_position: 5 +--- + +# Contratos desplegados + +| Contrato | Goerli\* | Sepolia | Mumbai | Optimism Goerli\* | Arbitrum Goerli\* | Arbitrum One | +| ------------------------- | ----------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | +| Semaphore.sol | [0x3889...6131](https://goerli.etherscan.io/address/0x3889927F0B5Eb1a02C6E2C20b39a1Bd4EAd76131) | [0x3889...6131](https://sepolia.etherscan.io/address/0x3889927F0B5Eb1a02C6E2C20b39a1Bd4EAd76131) | [0x3889...6131](https://mumbai.polygonscan.com/address/0x3889927F0B5Eb1a02C6E2C20b39a1Bd4EAd76131) | [0x3889...6131](https://goerli-optimism.etherscan.io/address/0x3889927F0B5Eb1a02C6E2C20b39a1Bd4EAd76131) | [0x3889...6131](https://goerli.arbiscan.io/address/0x3889927F0B5Eb1a02C6E2C20b39a1Bd4EAd76131) | [0xc60E...1520](https://arbiscan.io/address/0xc60E0Ee1a2770d5F619858C641f14FC4a6401520) | +| SemaphoreVerifier.sol | [0xb908...217C](https://goerli.etherscan.io/address/0xb908Bcb798e5353fB90155C692BddE3b4937217C) | [0xb908...217C](https://sepolia.etherscan.io/address/0xb908Bcb798e5353fB90155C692BddE3b4937217C) | [0xb908...217C](https://mumbai.polygonscan.com/address/0xb908Bcb798e5353fB90155C692BddE3b4937217C) | [0xb908...217C](https://goerli-optimism.etherscan.io/address/0xb908Bcb798e5353fB90155C692BddE3b4937217C) | [0xb908...217C](https://goerli.arbiscan.io/address/0xb908Bcb798e5353fB90155C692BddE3b4937217C) | [0xCAbe...4d07](https://arbiscan.io/address/0xCAbeED6cB96a287000aBd834b0B79c05e6Ea4d07) | +| Pairing.sol | [0xEe44...d7e8](https://goerli.etherscan.io/address/0xEe44c1e83A768E80A3588B409f1A010f9D1dd7e8) | [0xEe44...d7e8](https://sepolia.etherscan.io/address/0xEe44c1e83A768E80A3588B409f1A010f9D1dd7e8) | [0xEe44...d7e8](https://mumbai.polygonscan.com/address/0xEe44c1e83A768E80A3588B409f1A010f9D1dd7e8) | [0xEe44...d7e8](https://goerli-optimism.etherscan.io/address/0xEe44c1e83A768E80A3588B409f1A010f9D1dd7e8) | [0xEe44...d7e8](https://goerli.arbiscan.io/address/0xEe44c1e83A768E80A3588B409f1A010f9D1dd7e8) | [0xE3a4...A74C](https://arbiscan.io/address/0xE3a4C2FE9f025405cA6F60f6E960B4558604A74C) | +| IncrementalBinaryTree.sol | [0x4621...7F49](https://goerli.etherscan.io/address/0x4621EE309EAc747425F0FEd51931dDC241A27F49) | [0x4621...7F49](https://sepolia.etherscan.io/address/0x4621EE309EAc747425F0FEd51931dDC241A27F49) | [0x4621...7F49](https://mumbai.polygonscan.com/address/0x4621EE309EAc747425F0FEd51931dDC241A27F49) | [0x4621...7F49](https://goerli-optimism.etherscan.io/address/0x4621EE309EAc747425F0FEd51931dDC241A27F49) | [0x4621...7F49](https://goerli.arbiscan.io/address/0x4621EE309EAc747425F0FEd51931dDC241A27F49) | [0xcDF8...fFb0](https://arbiscan.io/address/0xcDF8efE6334c68aF283C83f2F14648da51fcfFb0) | +| PoseidonT3.sol | [0xe136...2595](https://goerli.etherscan.io/address/0xe136aBACf78E05988154ed85F4Ea911105302595) | [0xe136...2595](https://sepolia.etherscan.io/address/0xe136aBACf78E05988154ed85F4Ea911105302595) | [0xe136...2595](https://mumbai.polygonscan.com/address/0xe136aBACf78E05988154ed85F4Ea911105302595) | [0xe136...2595](https://goerli-optimism.etherscan.io/address/0xe136aBACf78E05988154ed85F4Ea911105302595) | [0xe136...2595](https://goerli.arbiscan.io/address/0xe136aBACf78E05988154ed85F4Ea911105302595) | [0xe0c8...61d0](https://arbiscan.io/address/0xe0c8d1e53D9Bfc9071F6564755FCFf6cC0dB61d0) | + +:::caution +Está previsto que la testnet Goerli deje de funcionar en el cuarto trimestre de 2023. Consulte los siguientes enlaces para obtener más información. + +- Goerli: https://ethereum.org/en/developers/docs/networks/#goerli +- Optimism Goerli: https://community.optimism.io/docs/useful-tools/networks/#op-goerli +- Arbitrum Goerli: https://docs.arbitrum.io/for-devs/concepts/public-chains#arbitrum-goerli + ::: diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/faq.md b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/faq.md new file mode 100644 index 00000000..358454d1 --- /dev/null +++ b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/faq.md @@ -0,0 +1,80 @@ +--- +sidebar_position: 8 +--- + +# FAQ + +## ¿Qué es Semaphore? + +Semaphore es un protocolo de conocimiento cero (zero-knowledge) que permite a los usuarios demostrar su pertenencia a un grupo y enviar señales como votos, comentarios o mensajes de texto sin revelar la identidad del usuario. + +Esto significa que las señales no tienen conexión con las identidades. + +También proporciona un mecanismo simple para evitar la doble señalización, lo que significa que no puede verificar la misma prueba dos veces. + +## ¿Dónde puedo hacer preguntas sobre Semaphore? + +Puede hacer preguntas sobre Semaphore en [Discord](https://semaphore.pse.dev/discord) o abriendo un [Semaphore Discussion](https://github.com/semaphore-protocol/semaphore/discussions). + +## ¿Por qué las identidades requieren tanto `identity trapdoor` como `identity nullifier`? + +Tener dos valores privados proporciona una capa de seguridad adicional. Si alguien rompe el hash del nullifier (imagínese que existe cierta maleabilidad de que la preimagen de Poseidón es fácil de encontrar cuando se aplica un hash con un valor específico X, que es el external nullifier elegido por los desarrolladores), el atacante puede encontrar todos los mensajes que envió la misma persona, pero no puede encontrar a quién, porque también está el trapdoor, que podría ser más difícil de romper. + +## ¿Cuál es la diferencia entre `identity nullifier`, `external nullifier` y `nullifier hash`? + +El identity nullifier (anulador de identidad) es uno de los valores secretos del usuario, mientras que el external nullifier (anulador externo) se puede utilizar como un tema sobre el que los usuarios pueden generar una prueba válida (por ejemplo, enviar votos anónimos) un número limitado de veces. + +Tanto el identity nullifier como el external nullifier se utilizan para evitar que la misma prueba se verifique dos veces, lo que significa que si un usuario genera la misma prueba (con la misma identidad y el mismo external nullifier) dos veces, la segunda no será válida. + +Finalmente, el nullifier hash (hash del anulador) es solo el hash del identity nullifier y el external nullifier que se utiliza para comprobar si ya se ha generado la misma prueba. + +En el caso de una aplicación de votación, si tiene un grupo y desea que todos los miembros de este grupo voten solo una vez, puede usar la identificación del grupo como external nullifier. Cuando un usuario vota por primera vez, puede guardar el hash de su identity nullifier y la identificación del grupo (es decir, el nullifier hash) y evitar la doble votación comprobando si ese hash ya existe. + +Vea los [circuitos de Semaphore](https://semaphore.pse.dev/docs/technical-reference/circuits) para más información técnica, o el [Semaphore boilerplate](https://github.com/semaphore-protocol/boilerplate) para un caso de uso real. + +## ¿Por qué debo evitar que las pruebas se verifiquen dos veces? + +Dado que las pruebas de conocimiento cero son completamente anónimas, es importante evitar que las generadas por identidades elegibles sean reutilizadas por una parte malintencionada. + +En una aplicación de voto anónimo, por ejemplo, sin chequeos, se podría reutilizar una prueba válida para volver a votar. + +## ¿Dónde puedo encontrar ejemplos de aplicaciones que utilicen Semaphore? + +Puede encontrar algunas aplicaciones que usan Semaphore en [este blog post](https://mirror.xyz/privacy-scaling-explorations.eth/Yi4muh-vzDZmIqJIcM9Mawu2e7jw8MRnwxvhFcyfns8). + +## ¿Cómo puedo iniciar un proyecto usando Semaphore? + +Hay tres formas de comenzar a usar Semaphore en su proyecto: usando la [Semaphore CLI](https://github.com/semaphore-protocol/semaphore/tree/main/packages/cli), usando el [Semaphore boilerplate](https://github.com/semaphore-protocol/boilerplate) como una plantilla o bifurcándolo, o instalando los paquetes de Semaphore manualmente. + +### Semaphore CLI + +Para crear un nuevo proyecto podrías usar `npx` o instalar la [Semaphore CLI](https://github.com/semaphore-protocol/semaphore/tree/main/packages/cli) globalmente usando `npm` y entonces crear un nuevo proyecto usando el comando `semaphore create`. Vea [Configuración Rápida](https://semaphore.pse.dev/docs/quick-setup) para más información. + +Hay tres plantillas soportadas en este momento: `contracts-hardhat`, `monorepo-ethers` y `monorepo-subgraph`. + +- `contracts-hardhat`: Contiene un caso de uso básico de Semaphore. Viene con un contrato de muestra, una prueba (test) para ese contrato y una tarea (task) de muestra que implementa (deploys) ese contrato. +- `monorepo-ethers`: Es una aplicación completa que demuestra un caso de uso básico de Semaphore. Viene con un contrato de muestra, una prueba para ese contrato y una tarea de muestra que implementa ese contrato. También contiene una interfaz para usar el contrato. Esta plantilla usa [Ethers](https://github.com/ethers-io/ethers.js/) por detrás para obtener datos on-chain. +- `monorepo-subgraph`: Es lo mismo que la plantilla `monorepo-ethers`, pero usa [The Graph protocol](https://thegraph.com/) por detrás para obtener datos on-chain. + +La Semaphore CLI también se puede usar para obtener datos de grupo de una red soportada. Hay comandos como: `get-groups`, `get-group`, `get-members`, `get-proofs`: + +- `get-groups`: Muestra la lista de grupos de una red soportada. +- `get-group`: Muestra los datos de un grupo de una red soportada. +- `get-members`: Muestra los miembros de un grupo de una red soportada. +- `get-proofs`: Muestra las pruebas de un grupo de una red soportada. + +### Semaphore boilerplate + +Para crear un proyecto, también puede utilizar el [Semaphore boilerplate](https://github.com/semaphore-protocol/boilerplate). Puede bifurcarlo o usarlo como plantilla. + +Las plantillas de la Semaphore CLI y el Semaphore boilerplate contienen el mismo código, que es una aplicación de feedback en la que puede crear una identidad, unirse a un grupo, y enviar su feedback de forma anónima. Son casi lo mismo, la única diferencia es que las plantillas usan CSS para que pueda decidir el framework de CSS o librería que desea usar y el boilerplate usa [ChakraUI](https://chakra-ui.com/) por defecto. + +También puede probar la aplicación Semaphore boilerplate en vivo aquí: https://demo.semaphore.pse.dev. + +### Instalación manual + +Alternativamente, también puede instalar todos los paquetes manualmente usando npm o yarn siguiendo la [documentación de Semaphore](https://semaphore.pse.dev/docs/introduction). + +## ¿Cómo puedo contribuir al protocolo? + +Hay varias formas de contribuir al protocolo, puede encontrar más información al respecto aquí: https://github.com/semaphore-protocol#ways-to-contribute. diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/glossary.md b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/glossary.md new file mode 100644 index 00000000..e3bb4330 --- /dev/null +++ b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/glossary.md @@ -0,0 +1,66 @@ +--- +sidebar_position: 7 +--- + +# Glosario + +## Identidad Semaphore + +La identidad de un usuario en el protocolo Semaphore. +Una identidad contiene los tres valores que se mencionan a continuación: + +- [Compromiso de identidad](#identity-commitment) (identity commitment): el valor público. +- Identidad trampilla (identity trapdoor) y anulador de identidad (identity nullifier): valores secretos que únicamente son del conocimiento del usuario. + +## Compromiso de identidad (Identity commitment) + +El valor público de la [identidad Semaphore](#semaphore-identity) utilizado en los [grupos Semaphore](#semaphore-group). + +Semaphore utiliza la función hash [Poseidon](https://www.poseidon-hash.info/) para crear un compromiso de identidad a partir de los valores secretos de la identidad Semaphore. + +## Grupo Semaphore + +Un grupo es un [árbol de Merkle](#merkle-tree) binario e incremental en el que cada hoja contiene un [compromiso de identidad](#identity-commitment) para un usuario. +El compromiso de identidad comprueba que un usuario es un miembro del grupo sin revelar la identidad Semaphore del usuario. + +Semaphore utiliza la función hash **Poseidon** para crear árboles de Merkle. +Para mayor información, vea el [sitio web de Poseidon](https://www.poseidon-hash.info/). + +## Árbol de Merkle (Merkle tree) + +Un árbol en el que cada hoja (es decir, un nodo que no tiene hijos) es etiquetado con el hash criptográfico de un bloque de datos, +y cada nodo, que no es una hoja, es etiquetado con el hash criptográfico de las etiquetas de sus nodos hijos. +En los protocolos de conocimiento zero (ZK), los árboles de Merkle pueden ser utilizados para resumir y validar de forma eficiente grandes conjuntos de datos. +Para validar que un árbol contiene una hoja en específico, un verificador sólo necesita una porción de la estructura completa de datos. + +Para más información, vea [árbol de Merkle en Wikipedia](https://es.wikipedia.org/wiki/%C3%81rbol_de_Merkle). + +## Anulador (Nullifier) + +Un valor utilizado para prevenir registros dobles o dos señales emitidas por el mismo usuario. + +Ver [hash de circuito nullifier](/docs/technical-reference/circuits/#nullifier-hash). + +## Retransmisor (Relayer) + +Un tercero que recibe una comisión por incluir transacciones retransmitidas en la blockchain (McMenamin, Daza, and Fitz. https://eprint.iacr.org/2022/155.pdf, p.3). +Para preservar la anonimidad del usuario emitiendo una señal con Semaphore, una aplicación puede utilizar un retransmisor para publicar la transacción de la señal en Ethereum en nombre del usuario. + +Las aplicaciones pueden ofrecer recompensas a los retransmisores e implementar mecanismos para prevenir ventajas maliciosas, como requerir que las señales incluyan la dirección del retransmisor, vinculando así la señal a esa dirección en específico (https://semaphore.pse.dev/whitepaper-v1.pdf, p.6). + +## Archivos confiables de configuración (Trusted setup files) + +Los parámetros verificables y seguros generados por la ceremonia de configuración de confianza de Semaphore. +Semaphore utiliza los archivos confiables de configuración para generar y verificar pruebas válidas de conocimiento cero. +Para generar o verificar pruebas válidas de conocimiento cero con Semaphore, las aplicaciones deben incluir los siguientes archivos _confiables de configuración_ de Semaphore. + +- semaphore.zkey +- semaphore.wasm +- semaphore.json + +Para ver una lista completa de archivos listos para utilizarse, vea . +Para aprender más, vea la [ceremonia de configuración de confianza](https://storage.googleapis.com/trustedsetup-a86f4.appspot.com/semaphore/semaphore_top_index.html) (trusted setup ceremony). + +## Señales (Signals) + +El término "señales" en Semaphore se refiere a los valores que el usuario transmite al votar, confirmar, enviar un mensaje, etc. Por otro lado, "[señales](https://docs.circom.io/circom-language/signals/)" en Circom se refiere a datos que contienen elementos dentro del campo de Z/pZ. En Circom, las "señales" se pueden definir como entrada o salida y, de lo contrario, se consideran señales intermedias. diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/guides/_category_.json b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/guides/_category_.json new file mode 100644 index 00000000..680845a9 --- /dev/null +++ b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/guides/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Guides", + "position": 3 +} diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/guides/fetching-data.mdx b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/guides/fetching-data.mdx new file mode 100644 index 00000000..886a84ca --- /dev/null +++ b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/guides/fetching-data.mdx @@ -0,0 +1,173 @@ +--- +sidebar_position: 4 +title: Obteniendo datos +--- + +import Tabs from "@theme/Tabs" +import TabItem from "@theme/TabItem" + +# Semaphore data + +Para obtener datos on-chain del contrato [Semaphore.sol](https://github.com/semaphore-protocol/semaphore/blob/main/packages/contracts/contracts/Semaphore.sol), puedes usar la librería [@semaphore-protocol/data](https://github.com/semaphore-protocol/semaphore/tree/main/packages/data). + +Hay dos formas para hacer esto, usando [`SemaphoreSubgraph`](https://github.com/semaphore-protocol/semaphore/blob/main/packages/data/src/subgraph.ts) o [`SemaphoreEthers`](https://github.com/semaphore-protocol/semaphore/blob/main/packages/data/src/ethers.ts). La clase `SemaphoreSubgraph` usa el [subgrafo de Semaphore](https://github.com/semaphore-protocol/subgraph), el cual usa [The Graph Protocol](https://thegraph.com/) detrás del telón, y la clase `SemaphoreEthers` usa [Ethers](https://github.com/ethers-io/ethers.js/). + +- [**Obtener datos usando SemaphoreSubgraph**](#obtener-datos-usando-semaphoresubgraph) +- [**Obtener datos usando SemaphoreEthers**](#obtener-datos-usando-semaphoreethers) + +## Instalar librería + + + + +```bash +npm install @semaphore-protocol/data +``` + + + + +```bash +yarn add @semaphore-protocol/data +``` + + + + +## Obtener datos usando SemaphoreSubgraph + +Para obtener datos usando el subgrafo de Semaphore puedes usar la clase [`SemaphoreSubgraph`](https://github.com/semaphore-protocol/semaphore/blob/main/packages/data/src/subgraph.ts) del paquete [@semaphore-protocol/data](https://github.com/semaphore-protocol/semaphore/tree/main/packages/data). + +```typescript +import { SemaphoreSubgraph } from "@semaphore-protocol/data" + +const semaphoreSubgraph = new SemaphoreSubgraph() + +// or: +const semaphoreSubgraph = new SemaphoreSubgraph("arbitrum") + +// or: +const semaphoreSubgraph = new SemaphoreSubgraph( + "https://api.studio.thegraph.com/query/14377//" +) +``` + +### Obtener Ids de los grupos + +```typescript +const groupIds = await semaphoreSubgraph.getGroupIds() +``` + +### Obtener los grupos + +```typescript +const groups = await semaphoreSubgraph.getGroups() + +// or + +const groups = await semaphoreSubgraph.getGroups({ members: true, verifiedProofs: true }) +``` + +### Obtener un grupo + +```typescript +const group = await semaphoreSubgraph.getGroup("42") + +// or + +const { members, verifiedProofs } = semaphoreSubgraph.getGroup("42", { members: true, verifiedProofs: true }) +``` + +### Comprueba si un identity commitment es miembro de un grupo + +```ts +await semaphoreSubgraph.isGroupMember( + "42", + "16948514235341957898454876473214737047419402240398321289450170535251226167324" +) +``` + +:::info +Puedes crear un grupo off-chain usando la clase SemaphoreSubgraph para obtener los miembros de la siguiente forma: + +```typescript +import { Group } from "@semaphore-protocol/group" +import { SemaphoreSubgraph } from "@semaphore-protocol/data" + +const groupId = "3" +const semaphoreSubgraph = new SemaphoreSubgraph("sepolia") +const { members } = await semaphoreSubgraph.getGroup(groupId, { members: true }) +const group = new Group(groupId, 20, members) +``` +::: + +## Obtener datos usando SemaphoreEthers + +Para obtener datos usando Ethers puedes usar la clase [`SemaphoreEthers`](https://github.com/semaphore-protocol/semaphore/blob/main/packages/data/src/ethers.ts) del paquete [@semaphore-protocol/data](https://github.com/semaphore-protocol/semaphore/tree/main/packages/data). + +```typescript +import { SemaphoreEthers } from "@semaphore-protocol/data" + +const semaphoreEthers = new SemaphoreEthers() + +// or: +const semaphoreEthers = new SemaphoreEthers("homestead", { + address: "semaphore-address", + startBlock: 0 +}) + +// or: +const semaphoreEthers = new SemaphoreEthers("http://localhost:8545", { + address: "semaphore-address" +}) +``` + +### Obtener Ids de los grupos + +```typescript +const groupIds = await semaphoreEthers.getGroupIds() +``` + +### Obtener un grupo + +```typescript +const group = await semaphoreEthers.getGroup("42") +``` + +### Obtener el admin de un grupo + +```typescript +const admin = await semaphoreEthers.getGroupAdmin("42") +``` + +### Obtener los miembros de un grupo + +```typescript +const members = await semaphoreEthers.getGroupMembers("42") +``` + +### Obtener las pruebas verificadas de un grupo + +```typescript +const verifiedProofs = await semaphoreEthers.getGroupVerifiedProofs("42") +``` + +:::info +Puedes crear un grupo off-chain usando la clase SemaphoreEthers para obtener los miembros de la siguiente forma: + +```typescript +import { Group } from "@semaphore-protocol/group" +import { SemaphoreEthers } from "@semaphore-protocol/data" + +const groupId = "3" +const semaphoreEthers = new SemaphoreEthers("sepolia") +const members = await semaphoreEthers.getGroupMembers(groupId) +const group = new Group(groupId, 20, members) +``` +::: \ No newline at end of file diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/guides/groups.mdx b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/guides/groups.mdx new file mode 100644 index 00000000..8767278f --- /dev/null +++ b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/guides/groups.mdx @@ -0,0 +1,151 @@ +--- +sidebar_position: 2 +title: Grupos +--- + +import Tabs from "@theme/Tabs" +import TabItem from "@theme/TabItem" + +# Gupos Semaphore + +Un [grupo Semaphore](/docs/glossary/#semaphore-group) contiene los [identity commitments](/docs/glossary/#identity-commitment) (compromisos de identidad) de miembros del grupo. +Estos son algunos ejemplos de uso de los grupos: + +- Encuesta con preguntas a la que se unen las personas que acudieron a un evento para calificarlo, +- votación a la que se unen los miembros para votar por una propuesta, +- Denunciantes que están verificados como empleados de una organización. + +Un grupo Semaphore es un [árbol de Merkle incremental](/docs/glossary/#incremental-merkle-tree), y los miembros del grupo (por ejemplo, [identity commitments](/docs/glossary/#identity-commitments)) son las hojas del árbol. +Los grupos Semaphore determinan los siguientes tres parámetro: + +- **Group id**: un identificador único para el grupo; +- **Tree depth**: el número máximo de miembros que puede contener un grupo (`max size = 2 ^ tree depth`); +- **Members** la lista de miembros para inicializar el grupo. + +Aprenda cómo trabajar con grupos. + +- [**Grupos off-chain**](#off-chain-groups) +- [**Grupos on-chain**](#on-chain-groups) + +## Grupos off-chain (externos a la cadena) + +- [Crear un grupo](#create-a-group) +- [Añadir miembros](#add-members) +- [Remover o actualizar miembros](#remove-or-update-members) + +### Crear un grupo + +Utilice la clase `Group` de la librería [`@semaphore-protocol/group`](https://github.com/semaphore-protocol/semaphore/blob/main/packages/group) para crear un grupo off-chain con los siguientes parámetros: + +- `Group id`: un identificar único para el grupo; +- `Tree depth`: (_default `20`_) el número máximo de usuarios que puede contener un grupo, el valor por defecto es 20 (`max size = 2 ^ tree depth`). +- `Members`: (_default `[]`_) la lista de miembros para inicializar el grupo. + +#### Instalar librería: + + + + +```bash +npm install @semaphore-protocol/group +``` + + + + +```bash +yarn add @semaphore-protocol/group +``` + + + + +Para crear un grupo con el número de usuarios que aparece por defecto (20) _`treeDepth`_, llame la función para construir un `Group` sin el segundo parámetro. Por ejemplo: + +```ts +import { Group } from "@semaphore-protocol/group" + +const group = new Group(1) +``` + +El siguiente código de ejemplo pasa por _`treeDepth`_ para crear un grupo para `2 ^ 30 = 1073741824` miembros: + +```ts +import { Group } from "@semaphore-protocol/group" + +const group = new Group(1, 30) +``` + +También puede inicializar un grupo con varios miembros pasando la lista de identity commitments (miembros) como tercer parámetro al crear el grupo: + +```ts +import { Group } from "@semaphore-protocol/group" + +const members = [ + "11237622825477336339577122413451117718539783476837539122310492284566644730311", + "9332663527862709610616009715800254142772436825222910251631161087138559093425", + "13255821893820536903335282929376140649646180444238593676033702344407594536519" +] +const group = new Group(1, 20, members) +``` + +### Añadir miembros + +Utiliza la función `Group addMember` para añadir un miembro (es decir su "identity commitment") a un grupo. Por ejemplo: + +```ts +group.addMember(identityCommitment) +``` + +Para añadir un lote de miembros a un grupo, pasa una selección por la función `Group addMembers`. Por ejemplo: + +```ts +group.addMembers([identityCommitment1, identityCommitment2]) +``` + +:::caution +Cuando utiliza la misma identidad Semaphore en varios grupos, si un atacante toma control de esa identidad, todos los grupos de los que forma parte estarán comprometidos. Considere utilizar identidades diferentes para cada grupo. +::: + +### Remover o actualizar miembros + +Para remover miembros de un equipo, pasa el índice del miembro por la función `Group removeMember`. Por ejemplo: + +```ts +group.removeMember(0) +``` + +Para actualizar los miembros dentro de un grupo, pasa el índice del miembro y el nuevo valor por la función `Group updateMember`. Por ejemplo: + +```ts +group.updateMember(0, 2) +``` + +:::caution +Remover a un miembro de un grupo configura el valor del nodo a un valor especial (ejemplo, `zeroValue`). +Dado que ese nodo no se remueve y el largo de la selección de `group.members` no cambia. +::: + +## Grupos on-chain + +El contrato [`SemaphoreGroups`](https://github.com/semaphore-protocol/semaphore/blob/main/packages/contracts/contracts/base/SemaphoreGroups.sol) utiliza la librería del [`IncrementalBinaryTree`](https://github.com/privacy-scaling-explorations/zk-kit/blob/main/packages/incremental-merkle-tree.sol/contracts/IncrementalBinaryTree.sol) (árbol binario incremental) y provee métodos para crear y administrar grupos. + +:::información +puede importar el contrato `SemaphoreGroups.sol` y otros contratos Semaphore del módulo NPM [`@semaphore-protocol/contracts`](https://github.com/semaphore-protocol/semaphore/tree/main/packages/contracts). +::: + +Alternativamente, puede utilizar un contrato [`Semaphore.sol`](https://github.com/semaphore-protocol/semaphore/blob/main/packages/contracts/contracts/Semaphore.sol) ya desplegado y utilizar sus funciones externas para grupos. + +:::caution +`Semaphore.sol` no revisa si un miembro con un identity commitment en específico ya existe en un grupo. Esta revisión se debe realizar off-chain. +::: + +:::caution +`Semaphore.sol` incluye un mecanismo para verificar pruebas Semaphore creadas con raíces de árboles de Merkle antiguas. La duración de este mecanismo puede ser definido por el admin en la función `createGroup`. Por lo tanto, los miembros de un grupo pueden continuar generando pruebas válidas incluso después de ser removidos. Para más información ver el issue [#98](https://github.com/semaphore-protocol/semaphore/issues/98). +::: diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/guides/identities.mdx b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/guides/identities.mdx new file mode 100644 index 00000000..76ede8ef --- /dev/null +++ b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/guides/identities.mdx @@ -0,0 +1,111 @@ +--- +sidebar_position: 1 +title: Identidades +--- + +import Tabs from "@theme/Tabs" +import TabItem from "@theme/TabItem" + +# Identidades Semaphore + +Para unirse a un [grupo Semaphore](/docs/glossary#semaphore-group), un usuario primero deberá crear una [identidad Semaphore](/docs/glossary#semaphore-identity). +Una identidad Semaphore contiene dos valores generados junto con la identidad: + +- Identity trapdoor (Identidad trampilla) +- Identity nullifier (Anulador de identidad) + +Para utilizar y verificar su identidad, la persona dueña de la identidad (usuario) debe conocer los valores trapdoor y nullifier. +Para prevenir fraudes, la persona dueña debe conservar de forma secreta ambos valores. + +## Crear identidades + +En su código, utilice la librería [`@semaphore-protocol/identity`](https://github.com/semaphore-protocol/semaphore/tree/main/packages/identity) para crear una identidad Semaphore _de forma determinística_ (del hash de un mensaje) o _de forma aleatoria_. + +- [**Crear identidades aleatorias**](#create-random-identities) +- [**Crear identidades determinísticas**](#create-deterministic-identities) + +### Instalar librería: + + + + +```bash +npm install @semaphore-protocol/identity +``` + + + + +```bash +yarn add @semaphore-protocol/identity +``` + + + + +### Crear identidades aleatorias + +Para crear una identidad aleatoria, represente `Identity` sin algún parámetro. Por ejemplo: + +```ts +import { Identity } from "@semaphore-protocol/identity" + +const { trapdoor, nullifier, commitment } = new Identity() +``` + +La nueva identidad contiene dos valores aleatorios secretos: `trapdoor` y `nullifier`, y un valor público: `commitment`. + +El hash Poseidon del identity nullifier y trapdoor se conoce como _identity secret_ (el secreto de identidad), +y su hash es el _identity commitment_ (compromiso de identidad). + +Un identity commitment (compromiso de identidad), de forma similar a las direcciones Ethereum, es un valor público que se utiliza en los grupos Semaphore para representar la +identidad de un miembro del grupo. Los valores secretos son similares a las llaves privadas +Ethereum y se utilizan para generar pruebas de conocimiento cero (ZKP) Semaphore y autenticar señales. + +### Crear identidades determinísticas + +Si transmite un mensaje como un parámetro, Semaphore genera `trapdoor` y `nullifier` +del hash _SHA256_ del mensaje. +El mensaje puede ser una contraseña o un mensaje que el usuario firma de forma criptográfica con una llave privada. + +Al utilizar identidades determinísticas siempre deberá mantener secreto el mensaje. +Dado que el hash es determinístico, cualquier persona con el mismo mensaje puede recrear la misma identidad. + +```ts +const identity = new Identity("secret-message") +``` + +:::tip +Crear un sistema que guarde o recupere valores secretos de identidades Semaphore no es trivial. +Puede elegir delegar este tipo de funcionalidad a carteras existente como Metamask. Por ejemplo: + +1. En Metamask, un usuario firma un mensaje con la llave privada de su cuenta Ethereum. +2. En la aplicación que usted ofrece, el usuario crea una identidad determinística con el mensaje firmado. +3. Ahora el usuario puede recrear su identidad Semaphore cuando quiera al firmar el mismo mensaje con su cuenta Ethereum en Metamask. + +::: + +## Guarde sus identidades + +Puede generar una identidad como una cadena de caractéres JSON que puede guardar y reutilizar más tarde. +El método `Identity.toString()` genera una matriz JSON a partir de una identidad. Por ejemplo: + +```ts +console.log(identity.toString()) // Ver la identidad trampilla y anulador. + +// '["8255d...", "62c41..."]' +``` + +La matriz contiene la trapdoor y el nullifier. + +Para reutilizar la identidad guardada, transforme la cadena JSON al constructor `Identity()`. + +```ts +const identity2 = new Identity(identity.toString()) +``` diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/guides/proofs.mdx b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/guides/proofs.mdx new file mode 100644 index 00000000..0a5a1fd4 --- /dev/null +++ b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/guides/proofs.mdx @@ -0,0 +1,121 @@ +--- +sidebar_position: 3 +title: Pruebas +--- + +import Tabs from "@theme/Tabs" +import TabItem from "@theme/TabItem" + +# Pruebas Semaphore + +Una vez que un usuario liga su [identidad Semaphore](/docs/glossary#semaphore-identity) a un [grupo Semaphore](/docs/glossary#semaphore-group), el usuario puede emitir una señal anónima con una prueba de conocimiento cero (ZKP) que demuestre lo siguiente: + +- el usuario es un miembro del grupo, +- el mismo usuario creo tanto la señal como la prueba. + +Las y los desarrolladores pueden utilizar Semaphore para realizar las siguientes acciones: + +- [**Generar una prueba externa a la cadena (off-chain)**](#generate-a-proof-off-chain) +- [**Verificar una prueba externa a la cadena (off-chain)**](#verify-a-proof-off-chain) +- [**Verificar una prueba interna a la cadena (on-chain)**](#verify-a-proof-on-chain) + +## Generar una prueba off-chain + +Utilice la librería [`@semaphore-protocol/proof`](https://github.com/semaphore-protocol/semaphore/tree/main/packages/proof) para generar una prueba off-chain. +Para generar una prueba, transforme los siguientes parámetros con la función `generateProof`: + +- `identity`: la identidad Semaphore del usuario emitiendo la señal y generando la prueba; +- `group`: el grupo al cual pertenece el usuario; +- `externalNullifier`: el valor que impide la emisión de dos señales por el mismo usuario; +- `signal`: la señal que el usuario quiere enviar de forma anónima; +- `snarkArtifacts`: la `zkey` y `wasm` de los [archivos confiables de configuración](/docs/glossary/#trusted-setup-files). + +#### Instalar librería: + + + + +```bash +npm install @semaphore-protocol/proof +``` + + + + +```bash +yarn add @semaphore-protocol/proof +``` + + + + +En el caso de uso de un sistema de votación, una vez que todos los votantes hayan ligado sus [identidades](/docs/guides/identities#create-an-identity) al [grupo](/docs/guides/groups) de la votación, +un votante puede generar una prueba para votar por una propuesta. +En el llamado para `generateProof`(generar la prueba), el sistema de votación envía el ID único de la votación (la raíz del [árbol de Merkle](/docs/glossary/#merkle-tree/) del grupo) como el +`externalNullifier` para impedir que el votante emita más de una señal para esta votación. +La siguiente muestra de código demuestra cómo utilizar `generateProof` para generar una prueba de votación: + +```ts +import { generateProof } from "@semaphore-protocol/proof" + +const externalNullifier = group.root +const signal = 1 + +const fullProof = await generateProof(identity, group, externalNullifier, signal, { + zkeyFilePath: "./semaphore.zkey", + wasmFilePath: "./semaphore.wasm" +}) +``` + +:::info +Si estás generando la prueba en el lado del cliente, puedes evitar agregar los artefactos de snark porque se obtienen automáticamente: + +```ts +const fullProof = await generateProof(identity, group, externalNullifier, signal) +``` +::: + +## Verificar una prueba off-chain + +Utilice la librería [`@semaphore-protocol/proof`](https://github.com/semaphore-protocol/semaphore/tree/main/packages/proof) para verificar una prueba Semaphore off-chain. +Para verificar una prueba, transforme los siguientes parámetros con la función `verifyProof`: + +- `fullProof`: la prueba Semaphore; +- `treeDepth`: la profundidad del árbol de Merkle. + +La siguiente muestra de código demuestra cómo verificar la prueba generada previamente: + +```ts +import { verifyProof } from "@semaphore-protocol/proof" + +await verifyProof(fullProof, 20) // verdadero o falso. +``` + +`verifyProof` devolverá una Promesa que determina uno de los dos valores `verdadero` o `falso`. + +## Verificar una prueba on-chain + +Utilice el contrato [`Semaphore.sol`](/docs/technical-reference/contracts#semaphoresol) para verificar pruebas on-chain. + +:::info +Vea nuestros [contratos desplegados](/docs/deployed-contracts) para encontrar las direcciones adecuadas para su red. +:::: + +Para verificar las pruebas Semaphore en su contrato, importe `ISemaphore.sol`, transfórmelo a la dirección `Semaphore.sol` y llame el método `verifyProof` con los siguientes parámetros: + +- `groupId`: el identificador del grupo; +- `merkleTreeRoot`: la raíz del árbol de Merkle; +- `signal`: la señal que el usuario quiere enviar de forma anónima ; +- `nullifierHash`: un [nullifier hash](#retrieve-a-nullifier-hash) (hash anulador); +- `externalNullifier`: el valor que impide la emisión de dos señales por el mismo usuario; +- `proof`: una [prueba Semaphore que es compatible con Solidity](#generate-a-solidity-compatible-proof). + +:::info +Puede importar `ISemaphore.sol` y otros contratos Semaphore del módulo NPM [`@semaphore-protocol/contracts`](https://github.com/semaphore-protocol/semaphore/tree/main/packages/contracts). +::: diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/quick-setup.mdx b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/quick-setup.mdx new file mode 100644 index 00000000..cb7b7612 --- /dev/null +++ b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/quick-setup.mdx @@ -0,0 +1,279 @@ +--- +sidebar_position: 2 +--- + +import Tabs from "@theme/Tabs" +import TabItem from "@theme/TabItem" + +# Configuración rápida + +Semaphore ofrece un CLI oficial para configurar su proyecto con Hardhat. Si su NPM es versión 5.2 or más reciente puede utilizar NPX: + +```bash +npx @semaphore-protocol/cli@latest create my-app --template monorepo-ethers +``` + +De lo contrario, instale `@semaphore-protocol/cli` de forma global y corra el comando `init`: + +```bash +npm i -g @semaphore-protocol/cli@latest +semaphore create my-app --template monorepo-ethers +``` + +:::info +Los templates soportados son: [`contracts-hardhat`](https://github.com/semaphore-protocol/semaphore/tree/main/packages/cli-template-contracts-hardhat), [`monorepo-ethers`](https://github.com/semaphore-protocol/semaphore/tree/main/packages/cli-template-monorepo-ethers), [`monorepo-subgraph`](https://github.com/semaphore-protocol/semaphore/tree/main/packages/cli-template-monorepo-subgraph) +::: + +:::info +El CLI [`semaphore`](https://github.com/semaphore-protocol/semaphore/tree/main/packages/cli) también se puede utilizar para obtener información de los grupos que existen dentro de las redes soportadas por Semaphore (por ejemplo: `semaphore get-groups --network goerli`). +::: + +Para comenzar a trabajar en su proyecto, instale las siguientes dependencias: + + + + +```bash +cd my-app +npm i +``` + + + + +```bash +cd my-app +yarn +``` + + + + +## Output + +El comando `create` creará un directorio con el nombre my-app (o cualquier nombre que usted escoja) dentro de la carpeta actual. Ese directorio contendrá la estructura inicial del proyecto, que incluye un contrato simple (contract), una tarea (task) para desplegar lo, algunas pruebas (test) y una aplicación de Next.js (la carpeta web-app) para interactuar con el contrato. + +``` +my-app +├── .yarn +├── apps +│   └── contracts +│ │   └── contracts +| │ │   └── Feedback.sol +│ │   └── scripts +| │ │   └── download-snark-artifacts.ts +│ │   └── tasks +| │ │   └── deploy.ts +│ │   └── test +| │ │   └── Feedback.ts +│ │   └── hardhat.config.ts +│ │   └── package.json +│ │   └── tsconfig.json +│   └── web-app +├── scripts +│   └── copy-contracts-artifacts.ts +├── .editorconfig +├── .env +├── .env.example +├── .eslintignore +├── .eslintrc.json +├── .gitignore +├── .prettierignore +├── .prettierrc.json +├── .yarnrc.yml +├── package.json +├── README.md +└── tsconfig.json +``` + +El contrato `Feedback.sol` crea un grupo Semaphore, permite que los usuarios se unan a ese grupo con su identidad Semaphore, y, finalmente, permite que los miembros de ese grupo envíen un feedback anónimo. + +## Uso + +### Compilar contratos + +Vaya a la carpeta `contracts`: + +```bash +cd apps/contracts +``` + +Y compile sus contratos al correr: + + + + +```bash +npm run compile +``` + + + + +```bash +yarn compile +``` + + + + +### Pruebe los contratos + +Pruebe sus contratos al correr: + + + + +```bash +npm test +``` + + + + +```bash +yarn test +``` + + + + +Genere un reporte de la prueba de cobertura: + + + + +```bash +npm run test:coverage +``` + + + + +```bash +yarn test:coverage +``` + + + + +O un reporte de la prueba de gas: + + + + +```bash +npm run test:report-gas +``` + + + + +```bash +yarn test:report-gas +``` + + + + +### Desplegar contratos + +Siga las instrucciones a continuación para desplegar sus contratos: + +En la carpeta raíz del proyecto: + +1. Agregue sus variables de entorno en el archivo `.env`. + + :::note + Deberá por lo menos configurar un URL válido en Ethereum (ejemplo: Infura) y una llave privada con algunos ethers. + ::: + +2. Vaya a la carpeta `apps/contracts` y desplegue su contrato. + + + + + ```bash + npm run deploy -- --semaphore --group --network goerli + ``` + + + + + ```bash + yarn deploy --semaphore --group --network goerli + ``` + + + + + :::note + Revise las direcciones de los contratos de Semaphore [aquí](/docs/deployed-contracts). + ::: + + :::caution + El group id (id del grupo) es un número. + ::: + +### Inicie la app + +Inicie la aplicación: + + + + +```bash +npm run dev +``` + + + + +```bash +yarn dev +``` + + + diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/resources.md b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/resources.md new file mode 100644 index 00000000..a3062e36 --- /dev/null +++ b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/resources.md @@ -0,0 +1,33 @@ +--- +sidebar_position: 9 +--- + +# Recursos + +## Artículos + +[Propuesta de la comunidad: Semaphore: Señalización de conocimiento cero (ZK) en Ethereum (v1 Whitepaper)](https://semaphore.pse.dev/whitepaper-v1.pdf) - Kobi Gurkan, Koh Wei Jie y Barry WhiteHat + +[Para Mezcladores y más allá: presentando Semaphore, una herramienta de privacidad construida en Ethereum](https://medium.com/coinmonks/to-mixers-and-beyond-presenting-semaphore-a-privacy-gadget-built-on-ethereum-4c8b00857c9b) - Koh Wei Jie + +[¡La versión 2 de Semaphore está lista!](https://medium.com/privacy-scaling-explorations/semaphore-v2-is-live-f263e9372579) - Privacy and Scaling Explorations + +## Videos + +[Privacidad en Ethereum](https://www.youtube.com/watch?v=maDHYyj30kg) - Barry WhiteHat en el Meetup de Ethereum en Taipei + +[Snarks para mezclar, enviar señales y escalabilidad](https://www.youtube.com/watch?v=lv6iK9qezBY) - Barry WhiteHat en Devcon 4 + +[Privacidad en Ethereum](https://www.youtube.com/watch?v=zBUo7G95wYE) - Barry WhiteHat en Devcon 5 + +[Un mezclador de Ethereum trustless utilizando señalizaciones de conocimiento cero (ZK)](https://www.youtube.com/watch?v=GzVT16lFOHU) - Koh Wei Jie y Barry WhiteHat en Devcon 5 + +[Implementación de aplicaciones de señalizaciones de conocimiento cero (ZK)](https://www.youtube.com/watch?v=7wd2aAN2jXI) - Koh Wei Jie en Devcon 5 + +[Roadmap para Semaphore en Ethereum](https://www.youtube.com/watch?v=gOub903iWFs) - Barry WhiteHat en Zcon1 + +[Pruebas concisas en Ethereum](https://www.youtube.com/watch?v=TtsDNneTDDY) - Barry WhiteHat en el 2ndo ZKProof Workshop + +[Propuesta: Semaphore - Señalización de conocimiento cero (ZK) en Ethereum](https://www.youtube.com/watch?v=y5uV9eRb3-w) - Kobi Gurkan y Koh Wei Jie en el ZKProof Home Edition + +[Señalizaciones anónimas en Ethereum](https://www.youtube.com/watch?v=dxAfL91Sbw4) - Cedoor en Devcon 6 Bogotá diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/subgraph.md b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/subgraph.md new file mode 100644 index 00000000..22fa5e2d --- /dev/null +++ b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/subgraph.md @@ -0,0 +1,51 @@ +--- +sidebar_position: 6 +--- + +# Subgrafo + +[The Graph](https://thegraph.com/) es un protocolo para indexar redes como Ethererum o IPFS. +Las personas dueñas de los sitios publica _subgrafos_ que exponen los datos del sitio para que cualquiera los pueda consultar. +El subgrafo de Semaphore le permite obtener datos del contrato inteligente [`Semaphore.sol`](https://github.com/semaphore-protocol/semaphore/blob/main/packages/contracts/Semaphore.sol). + +:::tip +El protocolo The Graph utiliza el lenguaje de consulta [GraphQL](https://graphql.org/). Para ver ejemplos visite [GraphQL API documentation](https://thegraph.com/docs/developer/graphql-api). Para ver la lista de subgrafos de Semaphore visite el [repositorio de subgrafos](https://github.com/semaphore-protocol/subgraph). +::: + +## Esquema + +### Árbol de Merkle + +- `id`: identificador único entre todas las entidades de árboles de Merkle, +- `depth`: profundidad del árbol de Merkle, +- `root`: raíz del árbol de Merkle, +- `zeroValue`: valor cero del árbol de Merkle, +- `numberOfLeaves`: número total de hojas en el árbol, +- `group`: link a la entidad del grupo. + +### Grupo + +- `id`: identificador único entre todas las entidades del grupo, +- `merkleTree`: link a la entidad del árbol de Merkle, +- `timestamp`: timestamp (registro de tiempo) del bloque, +- `admin`: administrador del grupo, +- `members`: lista de los miembros del grupo, +- `verifiedProofs`: lista de las pruebas del grupo. + +### Miembro + +- `id`: identificador único entre todos los miembros, +- `identityCommitment`: compromiso de identidad Semaphore, +- `timestamp`: timestamp del bloque, +- `index`: índice de la hoja del árbol, +- `group`: link a la entidad del grupo. + +### PruebaVerificada + +- `id`: identificador único entre todas las entidades con una prueba verificada (VerifiedProof), +- `signal`: señal del usuario, +- `merkleTreeRoot`: raíz del árbol de Merkle, +- `nullifierHash`: hash nullifier (anulador), +- `externalNullifier`: nullifier externo, +- `timestamp`: timestamp del bloque, +- `group`: link a la entidad del grupo. diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/technical-reference/_category_.json b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/technical-reference/_category_.json new file mode 100644 index 00000000..2187e794 --- /dev/null +++ b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/technical-reference/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Technical reference", + "position": 4 +} diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/technical-reference/circuits.md b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/technical-reference/circuits.md new file mode 100644 index 00000000..0c170715 --- /dev/null +++ b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/technical-reference/circuits.md @@ -0,0 +1,57 @@ +--- +sidebar_position: 2 +--- + +# Circuitos + +El [circuito Semaphore](https://github.com/semaphore-protocol/semaphore/tree/main/packages/circuits) es el corazón del protocolo y está compuesto por tres partes: + +- [**Prueba de membresía**](/docs/technical-reference/circuits#proof-of-membership) +- [**Nullifier hash**](/docs/technical-reference/circuits#nullifier-hash) (hash anulador) +- [**Señal**](/docs/technical-reference/circuits#signal) + +![Semaphore circuit](https://github.com/semaphore-protocol/semaphore/raw/main/packages/circuits/scheme.png) + +El diagrama anterior muestra cómo se utilizan las señales de entrada en el circuito Semaphore y cómo se calculan los resultados. + +## Prueba de membresía + +El circuito resume criptográficamente (hashes) el nullifier hash de la identidad utilizando la identity trapdoor (identidad trampilla) para generar el compromiso de identidad. Después de esto, el circuito verifica la prueba de membresía contra la raíz de Merkle y el compromiso de identidad. + +**Insumos (inputs) privados:** + +- `treeSiblings[nLevels]`: los valores a lo largo del camino de Merkle rumbo al compromiso de identidad del usuario, +- `treePathIndices[nLevels]`: la dirección (0/1) por nivel del árbol correspondiente al camino de Merkle rumbo al compromiso de identidad del usuario, +- `identityNullifier`: la identidad secreta de 32-bits utilizada como anulador, +- `identityTrapdoor`: la identidad secreta de 32-bits utilizada como trampilla. + +**Resultados (outputs) públicos:** + +- `root`: La raíz de Merkle del árbol. + +## Hash anulador (Nullifier hash) + +El circuito resume criptográficamente (hashes) el identity nullifier con el nullifier externo y después revisa que el resultado coincida con el nullifier hash provisto. +Los nullifier hashes guardados en un contrato inteligente Semaphore permiten que el contrato rechace las pruebas que contengan un nullifier hash ya utilizado. + +**Insumos (inputs) privados:** + +- `identityNullifier`: el identity secret (secreto de identidad) de 32 bits que se utiliza como nullifier. + +**Insumos (inputs) públicos:** + +- `externalNullifier`: el nullifier externo de 32 bits. + +**Resultados (outputs) públicos:** + +- `nullifierHash`: el hash del identity nullifier y del nullifier externo; se utiliza para prevenir que el mismo usuario emita dos señales. + +**Procedimiento:** + +## Señal + +El circuito calcula un cuadrado ficticio del hash de la señal para prevenir que se altere la prueba. + +**Insumos (inputs) públicos:** + +- `signalHash`: El hash de la señal del usuario. diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/technical-reference/contracts.md b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/technical-reference/contracts.md new file mode 100644 index 00000000..dcca9923 --- /dev/null +++ b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/technical-reference/contracts.md @@ -0,0 +1,50 @@ +--- +sidebar_position: 3 +--- + +# Contratos + +Semaphore incluye dos tipos de contratos: + +- [**Contratos base**](/docs/technical-reference/contracts#base-contracts) +- [**Contratos para la extensión**](/docs/technical-reference/contracts#extension-contracts) + +así como [**Semaphore.sol**](/docs/technical-reference/contracts#semaphoresol), el principal contrato desplegado en las redes soportadas por Semaphore. + +:::info +Para utilizar los contratos e interfaces Semaphore en su proyecto, instale el paquete NPM [`@semaphore-protocol/contracts`](https://github.com/semaphore-protocol/semaphore/tree/main/packages/contracts). +::: + +## Contratos base + +Semaphore ofrece los siguientes contratos base: + +- [`SemaphoreVerifier.sol`](https://github.com/semaphore-protocol/semaphore/blob/main/packages/contracts/base/SemaphoreVerifier.sol): contiene una función para verificar pruebas Semaphore; +- [`SemaphoreGroups.sol`](https://github.com/semaphore-protocol/semaphore/blob/main/packages/contracts/base/SemaphoreGroups.sol): contiene las funciones para crear grupos y añadir/remover/actualizar miembros. + +Los contratos base están relacionados de forma muy cercana al protocolo. +Puede utilizarlos en su contrato o puede utilizar [**Semaphore.sol**](/docs/technical-reference/contracts#semaphoresol) que ya los tiene integrados. + +:::info +Si bien algunas dApps pueden utilizar grupos internos a la cadena, otros puede que prefieran utilizar grupos externos a la cadena, por lo que únicamente guardarán las raíces de sus árboles en el contrato. +::: + +## Contratos para la extensión + +- [`SemaphoreVoting.sol`](https://github.com/semaphore-protocol/semaphore/blob/main/packages/contracts/extensions/SemaphoreVoting.sol): contrato para votaciones que contiene las funciones esenciales para crear encuestas, añadir electores, y emitir votos de forma anónima; +- [`SemaphoreWhistleblowing.sol`](https://github.com/semaphore-protocol/semaphore/blob/main/packages/contracts/extensions/SemaphoreWhistleblowing.sol): contrato para denuncias que contiene las funciones esenciales para crear entidades (por ejemplo: organizaciones sin fines de lucro), añade denunciantes, y filtraciones publicadas de forma anónima. + +Estos contratos extienden las capacidades del protocolo y proveen una aplicación lógica para casos de uso específicos. +En un futuro se incluirán más extensiones. + +## Semaphore.sol + +[`Semaphore.sol`](https://github.com/semaphore-protocol/semaphore/blob/main/packages/contracts/contracts/Semaphore.sol) utiliza los contratos base como punto de partida, los integra y de forma adicional brinda: + +- un sistema que solamente permite que administradores (ej. cuentas de Ethereum o contratos inteligentes) controlen grupos; +- un mecanismo que guarda los [hashes anuladores](/docs/technical-reference/circuits#nullifier-hash) (nullifier hashes) de cada grupo y evita que el mismo miembro emita dos señales; +- un mecanismo que permite que pruebas Semaphore generadas con raíces de Merkle antiguas sean verificadas por un periodo de tiempo determinado por el administrador del grupo. + +:::info +Visitando [contratos desplegados](/docs/deployed-contracts) puede encontrar las direcciones correspondientes a su red. +:::: diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/troubleshooting.mdx b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/troubleshooting.mdx new file mode 100644 index 00000000..c88a87fc --- /dev/null +++ b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/troubleshooting.mdx @@ -0,0 +1,223 @@ +--- +sidebar_position: 11 +--- + +import Tabs from "@theme/Tabs" +import TabItem from "@theme/TabItem" + +# Solución de problemas + +Si estas sugerencias no funcionan, no dude en preguntar en las [Semaphore Discussions](https://github.com/semaphore-protocol/semaphore/discussions) o en el canal `dev-chat` en el [Semaphore Discord](https://semaphore.pse.dev/discord). + +## Usando Semaphore en the frontend + +Semaphore funciona con cualquier framework de JavaScript, pero el paquete [`@semaphore-protocol/proof`](https://github.com/semaphore-protocol/semaphore/tree/main/packages/proof) está usando [snarkjs](https://github.com/iden3/snarkjs), que usa módulos Node.js que no son compatibles con los frameworks de frontend y hay algunos cambios que debemos hacer para que funcione en el lado del cliente. + +### Semaphore con Nextjs + +Verá un error como este: + +``` +Module not found: Can't resolve 'fs' +``` + +Para resolver esto, en su archivo `next.config.js`, dentro del objeto `nextConfig`, agregue: + +```javascript +webpack: (config, { isServer }) => { + if (!isServer) { + config.resolve.fallback = { + fs: false + } + } + + return config + } +``` + +Su fichero `next.config.js` sería algo como esto: + +```javascript +/** @type {import('next').NextConfig} */ +const nextConfig = { + reactStrictMode: true, + webpack: (config, { isServer }) => { + if (!isServer) { + config.resolve.fallback = { + fs: false + } + } + + return config + } +} + +module.exports = nextConfig +``` + +### Semaphore con React + Vite o Vuejs + Vite + +Verá un error como este: + +```bash +readman.js:43 Uncaught ReferenceError: process is not defined + at stringToBase64 (threadman.js:43:5) + at threadman.js:50:22 +``` + +Para resolver eso: + +1- Instale `@esbuild-plugins/node-globals-polyfill` y `@esbuild-plugins/node-modules-polyfill` + + + + +```bash +npm install @esbuild-plugins/node-globals-polyfill +``` + + + + +```bash +yarn add @esbuild-plugins/node-globals-polyfill +``` + + + + + + + +```bash +npm install @esbuild-plugins/node-modules-polyfill +``` + + + + +```bash +yarn add @esbuild-plugins/node-modules-polyfill +``` + + + + +2- Modifique `vite.config.ts` para añadirlos: + +```typescript +import { NodeGlobalsPolyfillPlugin } from "@esbuild-plugins/node-globals-polyfill" +import { NodeModulesPolyfillPlugin } from "@esbuild-plugins/node-modules-polyfill" +``` + +y en `defineConfig` agregue: + +```typescript +optimizeDeps: { + esbuildOptions: { + // Enable esbuild polyfill plugins + plugins: [ + NodeGlobalsPolyfillPlugin({ + process: true, + buffer: true + }), + NodeModulesPolyfillPlugin() + ] + } +} +``` + +Su fichero `vite.config.ts` debería ser algo como: + +```typescript +import { fileURLToPath, URL } from "node:url" + +import { defineConfig } from "vite" +import vue from "@vitejs/plugin-vue" + +import { NodeGlobalsPolyfillPlugin } from "@esbuild-plugins/node-globals-polyfill" +import { NodeModulesPolyfillPlugin } from "@esbuild-plugins/node-modules-polyfill" + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [vue()], + resolve: { + alias: { + "@": fileURLToPath(new URL("./src", import.meta.url)) + } + }, + optimizeDeps: { + esbuildOptions: { + // Enable esbuild polyfill plugins + plugins: [ + NodeGlobalsPolyfillPlugin({ + process: true, + buffer: true + }), + NodeModulesPolyfillPlugin() + ] + } + } +}) +``` + +:::info + +En caso de React con Vite, si ve un subrayado ondulado rojo en cada módulo Semaphore que dice `Could not find a declaration file for module ...`, cambie el `moduleResolution` de `bundler` a `Node` en el fichero `tsconfig.json` dentro de `compilerOptions`. + +Su fichero `tsconfig.json` sería algo así: + +```json +{ + "compilerOptions": { + "target": "ESNext", + "lib": ["DOM", "DOM.Iterable", "ESNext"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "Node", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] +} +``` + +::: + +## Grupos de Semaphore + +### Creando un Grupo + +Cuando crea un grupo y se revierte la transacción, asegúrese de que la identificación del grupo que está utilizando no existe en la red que está utilizando. + +Para comprobarlo, puede utilizar la [Semaphore CLI](https://github.com/semaphore-protocol/semaphore/tree/main/packages/cli) con el comando `get-groups` y la red que está utilizando y luego, asegúrese de que su id de grupo no sea parte de esa lista. También puede utilizar el [Semaphore explorer](https://explorer.semaphore.pse.dev/). + +## Semaphore Proofs + +### Transacción revertida al usar el mismo external nullifier + +Cuando genera una prueba usando el mismo external nullifier que usó para verificar una prueba antes, la transacción se revertirá porque ese external nullifier ya se usó. Si desea enviar y verificar varias pruebas de la misma identidad, debe usar un external nullifier diferente cada vez que genere una prueba. diff --git a/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/what-is-semaphore.md b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/what-is-semaphore.md new file mode 100644 index 00000000..aeef6938 --- /dev/null +++ b/apps/docs/i18n/es/docusaurus-plugin-content-docs/version-V3/what-is-semaphore.md @@ -0,0 +1,53 @@ +--- +id: introduction +title: ¿Qué es Semaphore? +sidebar_position: 1 +--- + +## General + +[Semaphore](https://github.com/semaphore-protocol/semaphore) es un protocolo que utiliza [conocimiento cero (zero-knowledge)](https://z.cash/technology/zksnarks) y permite emitir una señal (por ejemplo: un voto o una aprobación) como una persona probablemente miembro de un grupo sin revelar su identidad. +Además, proporciona un mecanismo sencillo para impedir que un mismo usuario emita dos señales. +Algunos de los potenciales casos de uso son: votaciones, denuncias, DAOs anónimas y mezcladores. + +## Características + +Con Semaphore puede permitir que sus usuarios realicen las siguientes acciones: + +1. [Crear una identidad Semaphore](/docs/guides/identities/). +2. [Agregar su identidad Semaphore a un grupo (es decir: _Árbol de Merkle_)](/docs/guides/groups/). +3. [Enviar una señal anónima, verificable (ej. un voto o una aprobación)](/docs/guides/proofs/). + +Cuando un usuario emite una señal (por ejemplo: un voto), las pruebas de conocimiento cero (ZKP) pueden asegurar que el usuario se ha incorporado al grupo y aún no ha emitido una señal con su nullifier (anulador). + +Semaphore utiliza contratos internos a la cadena en Solidity y librerías de JavaScript externas a la cadena que funcionan de forma conjunta. + +- Externos a la cadena (off-chain), se pueden utilizar librerías de Javascript para crear identidades, organizar grupos y generar pruebas. +- Internos a la cadena (on-chain), se pueden utilizar contratos en Solidity para organizar grupos y verificar pruebas. + +## Beneficios para desarrolladores + +Semaphore está diseñado para ser un _componente de privacidad_ simple y genérico para aplicaciones descentralizadas (dApps) en Ethereum. Promueve el diseño modular de las aplicaciones, lo que permite que los desarrolladores de las dApps escojan y personalicen los componentes que necesitan externos e internos a la cadena. + +## Respecto al código + +La base del protocolo es la [lógica de circuitos](https://github.com/semaphore-protocol/semaphore/tree/main/packages/circuits/scheme.png) (circuit logic). +Además de los circuitos, +Semaphore ofrece [contratos en Solidity](https://github.com/semaphore-protocol/semaphore/tree/main/packages/contracts) +y [librerías en JavaScript](https://github.com/semaphore-protocol/semaphore#-packages) que permiten que los desarrolladores generen pruebas de conocimiento cero (ZKP) y las verifiquen con un esfuerzo mínimo. + +### Ceremonia de configuración de confianza (Trusted Setup Ceremony) + +Los [parámetros seguros](/docs/glossary#trusted-setup-files) para generar pruebas válidas con los circuitos Semaphore fueron generados en una [ceremonia de configuración de confianza](https://storage.googleapis.com/trustedsetup-a86f4.appspot.com/semaphore/semaphore_top_index.html) que se completó con más de 300 participantes el [29 de Marzo de 2022](https://etherscan.io/tx/0xec6dbe68883c7593c2bea82f55af18b3aeb5cc146e026d0083a9b3faa9aa0b65#eventlog). + +### Auditorías + +| Versión | Auditores | Reporte | Alcance | +| ------- | --------------------------------- | -------------------------------------------------------------------------------------------------------------------- | ------------------------ | +| v2.0.0 | [PSE](https://pse.dev/) | [Semaphore_2.0.0_Audit.pdf](https://github.com/semaphore-protocol/semaphore/files/9850441/Semaphore_2.0.0_Audit.pdf) | `circuits`, `contracts` | +| v2.5.0 | [PSE](https://pse.dev/) | [Semaphore_2.5.0_Audit.pdf](https://github.com/semaphore-protocol/semaphore/files/9845008/Semaphore_2.5.0_Audit.pdf) | `contracts`, `libraries` | +| v3.0.0 | [Veridise](https://veridise.com/) | [Semaphore_3.0.0_Audit.pdf](https://github.com/semaphore-protocol/semaphore/files/9845008/Semaphore_2.5.0_Audit.pdf) | `circuits`, `contracts` | + +:::info +Si está utilizando una de las versiones anteriores de Semaphore, vea la documentación de [Semaphore V1](/docs/V1/introduction) o de [Semaphore V2](/docs/V2/introduction). +::: diff --git a/apps/docs/i18n/es/docusaurus-theme-classic/navbar.json b/apps/docs/i18n/es/docusaurus-theme-classic/navbar.json new file mode 100644 index 00000000..805e2087 --- /dev/null +++ b/apps/docs/i18n/es/docusaurus-theme-classic/navbar.json @@ -0,0 +1,11 @@ +{ + "item.label.Whitepaper": { + "message": "Whitepaper" + }, + "item.label.Documentation": { + "message": "Documentación" + }, + "item.label.Github": { + "message": "Github" + } +} diff --git a/apps/docs/package.json b/apps/docs/package.json new file mode 100644 index 00000000..d984b26f --- /dev/null +++ b/apps/docs/package.json @@ -0,0 +1,50 @@ +{ + "name": "semaphore-docs", + "version": "2.0.0", + "private": true, + "scripts": { + "start": "docusaurus start", + "start:es": "docusaurus start --locale es", + "build": "docusaurus build", + "swizzle": "docusaurus swizzle", + "deploy": "docusaurus deploy", + "clear": "docusaurus clear", + "serve": "docusaurus serve", + "write-translations": "docusaurus write-translations", + "write-heading-ids": "docusaurus write-heading-ids" + }, + "dependencies": { + "@docusaurus/core": "2.2.0", + "@docusaurus/preset-classic": "2.2.0", + "@mdx-js/react": "^1.6.22", + "@svgr/webpack": "^5.5.0", + "clsx": "^1.2.1", + "docusaurus-plugin-sass": "^0.2.2", + "file-loader": "^6.2.0", + "prism-react-renderer": "^1.3.5", + "react": "^17.0.2", + "react-dom": "^17.0.2", + "sass": "^1.52.3", + "url-loader": "^4.1.1" + }, + "devDependencies": { + "@docusaurus/module-type-aliases": "2.2.0", + "@tsconfig/docusaurus": "^1.0.6", + "@types/react": "^17.0.14", + "@types/react-helmet": "^6.1.2", + "@types/react-router-dom": "^5.1.8", + "typescript": "^4.3.5" + }, + "browserslist": { + "production": [ + ">0.5%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + } +} diff --git a/apps/docs/sidebars.js b/apps/docs/sidebars.js new file mode 100644 index 00000000..3e32e2b9 --- /dev/null +++ b/apps/docs/sidebars.js @@ -0,0 +1,29 @@ +/** + * Creating a sidebar enables you to: + - create an ordered group of docs + - render a sidebar for each doc of that group + - provide next/previous navigation + + The sidebars can be generated from the filesystem, or explicitly defined here. + + Create as many sidebars as you want. + */ + +// @ts-check + +/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ +module.exports = { + // By default, Docusaurus generates a sidebar from the docs folder structure + tutorialSidebar: [{ type: "autogenerated", dirName: "." }] + + // But you can create a sidebar manually + /* + tutorialSidebar: [ + { + type: 'category', + label: 'Tutorial', + items: ['hello'], + }, + ], + */ +} diff --git a/apps/docs/src/components/IllustrationHero/index.tsx b/apps/docs/src/components/IllustrationHero/index.tsx new file mode 100644 index 00000000..39c72a2e --- /dev/null +++ b/apps/docs/src/components/IllustrationHero/index.tsx @@ -0,0 +1,35 @@ +import React from "react" +import styles from "./styles.module.scss" + +export default function IllustrationHero(): JSX.Element { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ) +} diff --git a/apps/docs/src/components/IllustrationHero/styles.module.scss b/apps/docs/src/components/IllustrationHero/styles.module.scss new file mode 100644 index 00000000..2a07e467 --- /dev/null +++ b/apps/docs/src/components/IllustrationHero/styles.module.scss @@ -0,0 +1,9 @@ +.illustrationHero { + > path { + fill: var(--ifm-color-primary); + } + + > path:first-child { + fill: #ff975d; + } +} diff --git a/apps/docs/src/components/LinkButton/index.tsx b/apps/docs/src/components/LinkButton/index.tsx new file mode 100644 index 00000000..2d12d8f4 --- /dev/null +++ b/apps/docs/src/components/LinkButton/index.tsx @@ -0,0 +1,20 @@ +import Link from "@docusaurus/Link" +import React from "react" +import IconArrowRight from "../icons/IconArrowRight" +import styles from "./styles.module.scss" + +export type LinkButtonProps = { + href: string + children: string +} + +export default function LinkButton({ href, children }: LinkButtonProps): JSX.Element { + return ( + +
{children}
+
+ +
+ + ) +} diff --git a/apps/docs/src/components/LinkButton/styles.module.scss b/apps/docs/src/components/LinkButton/styles.module.scss new file mode 100644 index 00000000..43b5061e --- /dev/null +++ b/apps/docs/src/components/LinkButton/styles.module.scss @@ -0,0 +1,44 @@ +.linkButton { + display: flex; + font-weight: bold; + + &:hover { + text-decoration: none; + } +} + +html[data-theme="light"] { + .linkButton { + color: var(--custom-blue-3); + + &:hover { + color: var(--custom-blue-3); + } + + svg > path { + fill: var(--custom-blue-3); + } + } +} + +html[data-theme="dark"] { + .linkButton { + color: var(--custom-blue-2); + + &:hover { + color: var(--custom-blue-2); + } + + svg > path { + fill: var(--custom-blue-2); + } + } +} + +.buttonText { + padding-right: 10px; +} + +.buttonIcon { + padding-top: 2px; +} diff --git a/apps/docs/src/components/OutlineLinkButton/index.tsx b/apps/docs/src/components/OutlineLinkButton/index.tsx new file mode 100644 index 00000000..767b68c7 --- /dev/null +++ b/apps/docs/src/components/OutlineLinkButton/index.tsx @@ -0,0 +1,20 @@ +import Link from "@docusaurus/Link" +import React from "react" +import IconArrowTopRight from "../icons/IconArrowTopRight" +import styles from "./styles.module.scss" + +export type OutlineLinkButtonProps = { + href: string + children: string +} + +export default function OutlineLinkButton({ href, children }: OutlineLinkButtonProps): JSX.Element { + return ( + +
{children}
+
+ +
+ + ) +} diff --git a/apps/docs/src/components/OutlineLinkButton/styles.module.scss b/apps/docs/src/components/OutlineLinkButton/styles.module.scss new file mode 100644 index 00000000..16742298 --- /dev/null +++ b/apps/docs/src/components/OutlineLinkButton/styles.module.scss @@ -0,0 +1,46 @@ +.outlineLinkButton { + display: flex; + justify-content: space-between; + font-size: 20px; + font-weight: bold; + padding: 4px 12px; + border-radius: 4px; + + &:hover { + text-decoration: none; + } +} + +html[data-theme="light"] { + .outlineLinkButton { + color: var(--custom-blue-3); + border: 2px solid var(--custom-blue-3); + + &:hover { + color: var(--custom-blue-3); + } + + svg > path { + fill: var(--custom-blue-3); + } + } +} + +html[data-theme="dark"] { + .outlineLinkButton { + color: var(--custom-blue-2); + border: 2px solid var(--custom-blue-2); + + &:hover { + color: var(--custom-blue-2); + } + + svg > path { + fill: var(--custom-blue-2); + } + } +} + +.buttonIcon { + padding-right: 8px; +} diff --git a/apps/docs/src/components/icons/IconArrowRight.tsx b/apps/docs/src/components/icons/IconArrowRight.tsx new file mode 100644 index 00000000..b0ef5d0a --- /dev/null +++ b/apps/docs/src/components/icons/IconArrowRight.tsx @@ -0,0 +1,21 @@ +import React from "react" + +export type IconArrowRightProps = { + width?: number + height?: number +} + +export default function IconArrowRight({ width = 10, height = 15 }: IconArrowRightProps): JSX.Element { + return ( + + + + ) +} diff --git a/apps/docs/src/components/icons/IconArrowTopRight.tsx b/apps/docs/src/components/icons/IconArrowTopRight.tsx new file mode 100644 index 00000000..5a59534b --- /dev/null +++ b/apps/docs/src/components/icons/IconArrowTopRight.tsx @@ -0,0 +1,21 @@ +import React from "react" + +export type IconArrowTopRightProps = { + width?: number + height?: number +} + +export default function IconArrowTopRight({ width = 13, height = 13 }: IconArrowTopRightProps): JSX.Element { + return ( + + + + ) +} diff --git a/apps/docs/src/components/icons/IconAwards.tsx b/apps/docs/src/components/icons/IconAwards.tsx new file mode 100644 index 00000000..d844894c --- /dev/null +++ b/apps/docs/src/components/icons/IconAwards.tsx @@ -0,0 +1,25 @@ +import React from "react" + +export type IconAwardsProps = { + width?: number + height?: number +} + +export default function IconAwards({ width = 24, height = 24 }: IconAwardsProps): JSX.Element { + return ( + + + + ) +} diff --git a/apps/docs/src/components/icons/IconCheck.tsx b/apps/docs/src/components/icons/IconCheck.tsx new file mode 100644 index 00000000..632d3ffd --- /dev/null +++ b/apps/docs/src/components/icons/IconCheck.tsx @@ -0,0 +1,22 @@ +import React from "react" + +export type IconCheckProps = { + width?: number + height?: number +} + +export default function IconCheck({ width = 24, height = 24 }: IconCheckProps): JSX.Element { + return ( + + + + + ) +} diff --git a/apps/docs/src/components/icons/IconConnections.tsx b/apps/docs/src/components/icons/IconConnections.tsx new file mode 100644 index 00000000..83ecb7ce --- /dev/null +++ b/apps/docs/src/components/icons/IconConnections.tsx @@ -0,0 +1,21 @@ +import React from "react" + +export type IconConnectionsProps = { + width?: number + height?: number +} + +export default function IconConnections({ width = 18, height = 22 }: IconConnectionsProps): JSX.Element { + return ( + + + + ) +} diff --git a/apps/docs/src/components/icons/IconEye.tsx b/apps/docs/src/components/icons/IconEye.tsx new file mode 100644 index 00000000..1a803823 --- /dev/null +++ b/apps/docs/src/components/icons/IconEye.tsx @@ -0,0 +1,30 @@ +import React from "react" + +export type IconEyeProps = { + width?: number + height?: number +} + +export default function IconEye({ width = 24, height = 24 }: IconEyeProps): JSX.Element { + return ( + + + + + ) +} diff --git a/apps/docs/src/components/icons/IconEyeClose.tsx b/apps/docs/src/components/icons/IconEyeClose.tsx new file mode 100644 index 00000000..95468f1c --- /dev/null +++ b/apps/docs/src/components/icons/IconEyeClose.tsx @@ -0,0 +1,21 @@ +import React from "react" + +export type IconEyeCloseProps = { + width?: number + height?: number +} + +export default function IconEyeClose({ width = 24, height = 24 }: IconEyeCloseProps): JSX.Element { + return ( + + + + ) +} diff --git a/apps/docs/src/components/icons/IconFlag.tsx b/apps/docs/src/components/icons/IconFlag.tsx new file mode 100644 index 00000000..b4f976a7 --- /dev/null +++ b/apps/docs/src/components/icons/IconFlag.tsx @@ -0,0 +1,25 @@ +import React from "react" + +export type IconFlagProps = { + width?: number + height?: number +} + +export default function IconFlag({ width = 24, height = 24 }: IconFlagProps): JSX.Element { + return ( + + + + ) +} diff --git a/apps/docs/src/components/icons/IconGroup.tsx b/apps/docs/src/components/icons/IconGroup.tsx new file mode 100644 index 00000000..834358f4 --- /dev/null +++ b/apps/docs/src/components/icons/IconGroup.tsx @@ -0,0 +1,21 @@ +import React from "react" + +export type IconGroupProps = { + width?: number + height?: number +} + +export default function IconGroup({ width = 24, height = 24 }: IconGroupProps): JSX.Element { + return ( + + + + ) +} diff --git a/apps/docs/src/components/icons/IconMoon.tsx b/apps/docs/src/components/icons/IconMoon.tsx new file mode 100644 index 00000000..e74eb6ef --- /dev/null +++ b/apps/docs/src/components/icons/IconMoon.tsx @@ -0,0 +1,25 @@ +import React from "react" + +export type IconMoonProps = { + width?: number + height?: number +} + +export default function IconMoon({ width = 24, height = 24 }: IconMoonProps): JSX.Element { + return ( + + + + ) +} diff --git a/apps/docs/src/components/icons/IconProfile.tsx b/apps/docs/src/components/icons/IconProfile.tsx new file mode 100644 index 00000000..9b16c3fd --- /dev/null +++ b/apps/docs/src/components/icons/IconProfile.tsx @@ -0,0 +1,30 @@ +import React from "react" + +export type IconProfileProps = { + width?: number + height?: number +} + +export default function IconProfile({ width = 24, height = 24 }: IconProfileProps): JSX.Element { + return ( + + + + + ) +} diff --git a/apps/docs/src/components/icons/IconSun.tsx b/apps/docs/src/components/icons/IconSun.tsx new file mode 100644 index 00000000..8efdf2cd --- /dev/null +++ b/apps/docs/src/components/icons/IconSun.tsx @@ -0,0 +1,21 @@ +import React from "react" + +export type IconSunProps = { + width?: number + height?: number +} + +export default function IconSun({ width = 24, height = 24 }: IconSunProps): JSX.Element { + return ( + + + + ) +} diff --git a/apps/docs/src/components/icons/IconUnion.tsx b/apps/docs/src/components/icons/IconUnion.tsx new file mode 100644 index 00000000..a499b3ff --- /dev/null +++ b/apps/docs/src/components/icons/IconUnion.tsx @@ -0,0 +1,25 @@ +import React from "react" + +export type IconUnionProps = { + width?: number + height?: number +} + +export default function IconUnion({ width = 20, height = 18 }: IconUnionProps): JSX.Element { + return ( + + + + ) +} diff --git a/apps/docs/src/css/custom.scss b/apps/docs/src/css/custom.scss new file mode 100644 index 00000000..d923d5ba --- /dev/null +++ b/apps/docs/src/css/custom.scss @@ -0,0 +1,410 @@ +/** + * Any CSS included here will be global. The classic template + * bundles Infima by default. Infima is a CSS framework designed to + * work well for content-centric websites. + */ + +/* You can override the default Infima variables here. */ + +@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap"); + +:root { + --ifm-color-primary: #3578e5; + --ifm-color-primary-dark: #1d68e1; + --ifm-color-primary-darker: #1b62d4; + --ifm-color-primary-darkest: #1751af; + --ifm-color-primary-light: #4e89e8; + --ifm-color-primary-lighter: #5a91ea; + --ifm-color-primary-lightest: #80aaef; + + --custom-dark-4: #0c0c15; + --custom-dark-3: #131320; + --custom-dark-2: #1f2034; + --custom-dark-1: #3b3b48; + --custom-gray-1: #8f9097; + --custom-gray-0: #c4c6ca; + --custom-blue-3: #5867bc; + --custom-blue-2: #758bff; + --custom-blue-1: #dae0ff; + --custom-blue-0: #f8f9ff; + + --collapse-button-bg-color-dark: #2e333a; + + --ifm-code-font-size: 95%; + + --ifm-h1-font-size: 1.75rem; + --ifm-h2-font-size: 1.5rem; + --ifm-h3-font-size: 1.25rem; + --ifm-h4-font-size: 1rem; + --ifm-h5-font-size: 0.875rem; + --ifm-h6-font-size: 0.85rem; + + --ifm-toc-border-width: 1px; + + /* --ifm-menu-color: var(--ifm-color-emphasis-900); */ + --ifm-menu-color-background-active: transparent; + --ifm-menu-color-background-hover: transparent; + + --ifm-global-shadow-lw: 0 1px 10px 0 rgba(0, 0, 0, 0.1); + --ifm-global-shadow-md: 0 5px 40px rgba(0, 0, 0, 0.1); + --ifm-global-shadow-tl: 0 12px 28px 0 rgba(0, 0, 0, 0.2), 0 2px 4px 0 rgba(0, 0, 0, 0.1); + + --ifm-global-spacing: 1.5rem; + /* --ifm-navbar-background-color: rgba(255, 0.2); */ +} + +html[data-theme="dark"] { + --ifm-background-color: var(--custom-dark-3); + --ifm-color-primary: var(--custom-gray-1); + --ifm-heading-color: var(--custom-gray-0); + --ifm-navbar-background-color: var(--custom-dark-3); + --ifm-global-shadow-md: 0 5px 4px rgba(255, 255, 255, 0.448); + --ifm-toc-border-color: var(--custom-dark-2); +} + +html[data-theme="light"] { + --ifm-background-color: #fcfcfc; + --ifm-color-primary: var(--custom-dark-1); + --ifm-heading-color: var(--custom-dark-2); + --ifm-navbar-background-color: #fcfcfc; + --ifm-toc-border-color: var(--custom-blue-1); +} + +html { + font-family: "Inter", sans-serif; + font-size: 16px; + font-variant: none; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); + font-feature-settings: "ss01" on, "ss02" on, "cv01" on, "cv03" on; + font-weight: 400; +} + +svg.custom-icon path, +svg.custom-icon react { + fill: var(--ifm-color-primary); +} + +svg.custom-icon circle { + stroke: var(--ifm-color-primary); +} + +p { + line-height: 1.5rem; + font-weight: 400; +} + +hr { + border-bottom-width: 0px; + border-top-width: 1px; + border-color: var(--ifm-color-emphasis-200); +} + +h1, +h2, +h3, +h4, +h5 { + font-family: "Inter", sans-serif; + font-weight: 400; +} + +h1 { + font-size: 32px; +} + +h2 { + font-size: 24px; +} + +h3 { + font-size: 20px; +} + +h4 { + font-size: 16px; +} + +.tocCollapsibleButton_node_modules-\@docusaurus-theme-classic-lib-next-theme-TOCCollapsible-styles-module { + padding-left: 0px !important; +} + +.menu__link--sublist:after { + background: var(--ifm-menu-link-sublist-icon) 50% / 1.5rem 1.5rem; + /* color: var( --ifm-color-primary); */ +} + +.navbar { + height: 90px; + align-items: center; + box-shadow: none; + font-size: 18px; + + .navbar__toggle { + margin-right: 20px; + } + + .navbar-sidebar__brand { + margin-bottom: 16px; + padding: 12px 20px; + } +} + +@media (max-width: 996px) { + .footer { + --ifm-footer-padding-horizontal: 20px; + } +} + +html[data-theme="dark"] { + .navbar { + border-bottom: var(--ifm-toc-border-color) 1px solid; + } + + .footer { + background-color: var(--custom-dark-4); + } + + .markdown a { + color: var(--custom-blue-2); + } + + h2 a { + color: var(--custom-blue-2); + } + + input:focus, + select:focus { + border-color: var(--custom-blue-2); + } + + li[role="tab"][aria-selected="true"] { + color: var(--custom-blue-2); + border-bottom-color: var(--custom-blue-2); + } +} + +html[data-theme="light"] { + .navbar { + border-bottom: var(--ifm-toc-border-color) 1px solid; + } + + .footer { + background-color: var(--custom-blue-1); + } + + .markdown a { + color: var(--custom-blue-3); + } + + h2 a { + color: var(--custom-blue-3); + } + + input:focus, + select:focus { + border-color: var(--custom-blue-3); + } + + li[role="tab"][aria-selected="true"] { + color: var(--custom-blue-3); + border-bottom-color: var(--custom-blue-3); + } +} + +.container { + padding: 0px; + /* margin: 4rem auto 0px; */ + max-width: 90% !important; +} + +.container.padding-top--md { + padding-top: 2rem !important; +} + +.react-toggle-track { + background-color: var(--ifm-color-emphasis-100) !important; +} + +.react-toggle-thumb { + background-color: var(--ifm-color-emphasis-200) !important; +} + +.react-toggle-thumb:hover { + box-shadow: none !important; +} + +.navbar__title { + font-family: "Inter", sans-serif; + color: var(--ifm-color-primary); + font-weight: 500; +} + +.navbar__link { + font-family: "Inter", sans-serif; + color: var(--ifm-color-primary); + font-weight: 400; +} + +.navbar__logo { + height: 25px; +} + +.menu__link { + font-size: 18px; + font-weight: 400; + padding: 8px 16px; +} + +html[data-theme="dark"] .navbar__logo { + filter: invert(100%) sepia(0%) saturate(7500%) hue-rotate(337deg) brightness(112%) contrast(104%); +} + +.docusaurus-highlight-code-line { + background-color: rgb(72, 77, 91); + display: block; + margin: 0 calc(-1 * var(--ifm-pre-padding)); + padding: 0 var(--ifm-pre-padding); + color: var(--ifm-color-secondary-darker); +} + +.badge { + margin-bottom: 16px; +} + +.badge--secondary { + --ifm-badge-background-color: var(--custom-blue-1); + --ifm-badge-border-color: var(--ifm-badge-background-color); + color: var(--custom-dark-3); +} + +html[data-theme="light"] .DocSearch { + /* --docsearch-primary-color: var(--ifm-color-primary); */ + /* --docsearch-text-color: var(--ifm-font-color-base); */ + --docsearch-muted-color: var(--ifm-color-emphasis-900); + --docsearch-container-background: rgba(94, 100, 112, 0.7); + /* Modal */ + --docsearch-modal-background: var(--ifm-color-secondary-lighter); + /* Search box */ + --docsearch-searchbox-background: var(--ifm-color-secondary); + --docsearch-searchbox-focus-background: var(--ifm-color-white); + --docsearch-searchbox-shadow: inset 0 0 0 2px var(--custom-blue-3); + /* Hit */ + --docsearch-hit-color: var(--ifm-font-color-base); + --docsearch-hit-active-color: var(--ifm-color-white); + --docsearch-hit-background: var(--ifm-color-white); + --docsearch-highlight-color: var(--custom-blue-3); + + /* Footer */ + --docsearch-footer-background: var(--ifm-color-white); +} + +html[data-theme="dark"] .DocSearch { + /* --docsearch-text-color: var(--ifm-font-color-100); */ + --docsearch-muted-color: var(--ifm-color-secondary-darkest); + --docsearch-container-background: rgba(47, 55, 69, 0.7); + /* Modal */ + --docsearch-modal-background: var(--ifm-background-color); + /* Search box */ + --docsearch-searchbox-background: var(--ifm-background-color); + --docsearch-searchbox-focus-background: var(--ifm-color-black); + --docsearch-searchbox-shadow: inset 0 0 0 2px var(--custom-blue-2); + /* Hit */ + --docsearch-hit-color: var(--ifm-font-color-base); + --docsearch-hit-active-color: var(--ifm-color-emphasis-700); + --docsearch-hit-background: var(--ifm-color-emphasis-100); + --docsearch-highlight-color: var(--custom-blue-2); + /* Footer */ + --docsearch-footer-background: var(--ifm-background-surface-color); + --docsearch-key-gradient: linear-gradient( + -26.5deg, + var(--ifm-color-emphasis-200) 0%, + var(--ifm-color-emphasis-100) 100% + ); +} + +.react-toggle-thumb :hover { + border: none; + outline: none; + box-shadow: none; +} +.react-toggle-thumb :focus { + border: none; + outline: none; + box-shadow: none; +} +.react-toggle-thumb :active { + border: none; + outline: none; + box-shadow: none; +} + +.menu { + /* background-color: var(--ifm-color-emphasis-0); */ + padding: 1.5rem 1rem !important; +} + +.dropdown__menu { + box-shadow: 0 5px 40px rgba(0, 0, 0, 0.1); +} + +.menu__link:hover { + text-decoration: underline; +} + +.menu__link--active { + font-weight: 600; +} + +.table-of-contents { + opacity: 0.4; +} + +.table-of-contents:hover { + opacity: 1; +} + +.table-of-contents__link:hover { + text-decoration: underline; +} + +.navbar__inner { + flex-wrap: unset; +} + +.navbar__item { + display: inline-block; +} + +.navbar__item.dropdown { + display: inline-block; +} + +.menu__list-item--collapsed .menu__link { + display: flex; +} + +.menu__link { + display: flex; +} + +@media (max-width: 960px) { + .menu { + /* background-color: var(--ifm-color-emphasis-0); */ + padding: initial !important; + } + + .container { + max-width: 100% !important; + padding: 1rem; + } + + .container.padding-top--md { + padding-top: initial; + } + + .navbar__item { + display: none !important; + } +} diff --git a/apps/docs/src/pages/index.tsx b/apps/docs/src/pages/index.tsx new file mode 100644 index 00000000..01639b0f --- /dev/null +++ b/apps/docs/src/pages/index.tsx @@ -0,0 +1,318 @@ +import { translate } from "@docusaurus/Translate" +import CodeBlock from "@theme/CodeBlock" +import Layout from "@theme/Layout" +import React from "react" +import IconAwards from "../components/icons/IconAwards" +import IconCheck from "../components/icons/IconCheck" +import IconConnections from "../components/icons/IconConnections" +import IconEye from "../components/icons/IconEye" +import IconEyeClose from "../components/icons/IconEyeClose" +import IconFlag from "../components/icons/IconFlag" +import IconGroup from "../components/icons/IconGroup" +import IconProfile from "../components/icons/IconProfile" +import IconUnion from "../components/icons/IconUnion" +import IllustrationHero from "../components/IllustrationHero" +import LinkButton from "../components/LinkButton" +import OutlineLinkButton from "../components/OutlineLinkButton" +import styles from "./styles.module.scss" + +export default function Home() { + return ( + +
+
+
+

+ {translate({ + id: "jumbotron.title" + })} +

+ +

+ {translate({ + id: "jumbotron.description" + })} +

+ +
+ + {translate({ + id: "quick-setup.button" + })} + + + + {translate({ + id: "boilerplate.button" + })} + +
+
+ + +
+ +
+

+ {translate({ + id: "components.description" + })} +

+
+ + {translate({ + id: "components.button.solidity" + })} + + + {translate({ + id: "components.button.circuits" + })} + + + {translate({ + id: "components.button.libraries" + })} + +
+
+ +
+
+
+

+ {translate({ + id: "section.identities.title" + })} +

+ +

+ {translate({ + id: "section.identities.description" + })} +

+ + + {translate({ + id: "section.identities.link" + })} + +
+
+ + {`import { Identity } from "@semaphore-protocol/identity" + +const { trapdoor, nullifier, commitment } = new Identity()`} + +
+
+
+
+ +

+ {translate({ + id: "section.identities.box1.title" + })} +

+

+ {translate({ + id: "section.identities.box1.description" + })} +

+
+
+ +

+ {translate({ + id: "section.identities.box2.title" + })} +

+

+ {translate({ + id: "section.identities.box2.description" + })} +

+
+
+ + +

+ {translate({ + id: "section.identities.box3.title" + })} +

+

+ {translate({ + id: "section.identities.box3.description" + })} +

+
+
+
+ +
+ +
+
+
+

+ {translate({ + id: "section.groups.title" + })} +

+ +

+ {translate({ + id: "section.groups.description" + })} +

+ + + {translate({ + id: "section.groups.link" + })} + +
+
+ + {`import { Group } from "@semaphore-protocol/group" + +const group = new Group() + +group.addMember(commitment)`} + +
+
+
+
+ +

+ {translate({ + id: "section.groups.box1.title" + })} +

+

+ {translate({ + id: "section.groups.box1.description" + })} +

+
+
+ +

+ {translate({ + id: "section.groups.box2.title" + })} +

+

+ {translate({ + id: "section.groups.box2.description" + })} +

+
+
+ + +

+ {translate({ + id: "section.groups.box3.title" + })} +

+

+ {translate({ + id: "section.groups.box3.description" + })} +

+
+
+
+ +
+ +
+
+
+

+ {translate({ + id: "section.proofs.title" + })} +

+ +

+ {translate({ + id: "section.proofs.description" + })} +

+ + + {translate({ + id: "section.proofs.link" + })} + +
+
+ + {`import { generateProof, verifyProof } from "@semaphore-protocol/proof" +import { formatBytes32String } from "@ethersproject/strings" + +const externalNullifier = formatBytes32String("Topic") +const signal = formatBytes32String("Hello world") + +const fullProof = await generateProof(identity, group, externalNullifier, signal, { + zkeyFilePath: "./semaphore.zkey", + wasmFilePath: "./semaphore.wasm" +}) + +await verifyProof(fullProof, group.depth)`} + +
+
+
+
+ +

+ {translate({ + id: "section.proofs.box1.title" + })} +

+

+ {translate({ + id: "section.proofs.box1.description" + })} +

+
+
+ +

+ {translate({ + id: "section.proofs.box2.title" + })} +

+

+ {translate({ + id: "section.proofs.box2.description" + })} +

+
+
+ +

+ {translate({ + id: "section.proofs.box3.title" + })} +

+

+ {translate({ + id: "section.proofs.box3.description" + })} +

+
+
+
+
+ + ) +} diff --git a/apps/docs/src/pages/styles.module.scss b/apps/docs/src/pages/styles.module.scss new file mode 100644 index 00000000..49357b9e --- /dev/null +++ b/apps/docs/src/pages/styles.module.scss @@ -0,0 +1,258 @@ +.container { + display: flex; + flex-direction: column; + max-width: 1280px; + margin: 0 auto; + margin-bottom: 4rem; + + h1 { + font-size: 42px; + } + + h2 { + font-size: 36px; + } + + p { + font-size: 20px; + color: var(--ifm-color-primary); + } +} + +.jumbotron { + display: flex; + justify-content: space-between; + margin: 80px 0; + + > div, + > svg { + margin: 0 20px; + } + + > div { + max-width: 600px; + + > div { + margin-top: 40px; + display: flex; + flex-direction: column; + align-items: flex-start; + + a { + font-size: 22px; + margin-bottom: 10px; + } + } + } + + > svg { + max-width: 500px; + min-width: 400px; + height: auto; + } +} + +@media (max-width: 768px) { + .jumbotron { + margin-top: 50px; + flex-direction: column; + align-items: center; + + > div { + max-width: 700px; + } + + > svg { + min-width: 300px; + } + } +} + +.components { + display: flex; + flex-direction: column; + margin: 50px 20px 80px 20px; + + > h3 { + text-align: center; + font-weight: bold; + margin-bottom: 60px; + } + + > div { + display: flex; + justify-content: space-between; + + > a { + flex: 1; + margin: 0 16px; + + :first-child { + margin-left: 0; + } + + :last-child { + margin-right: 0; + } + } + } +} + +@media (max-width: 768px) { + .components { + margin-bottom: 40px; + + > p { + margin-bottom: 20px; + } + + > div { + flex-direction: column; + align-items: center; + + > a { + width: 100%; + margin: 16px 0; + } + } + } +} + +.section { + display: flex; + flex-direction: column; + margin: 100px 20px; + + > div:first-child { + display: flex; + + > div { + margin: 0 20px; + flex: 1; + + &:first-child { + margin-left: 0; + } + + &:last-child { + margin-right: 0; + width: 100%; + } + } + } + + > div:last-child { + margin-top: 80px; + display: flex; + justify-content: space-between; + + > div { + flex: 1; + margin: 0 20px; + + > img { + margin-bottom: 10px; + } + + &:first-child { + margin-left: 0; + } + + &:last-child { + margin-right: 0; + } + } + + h3 { + font-weight: bold; + } + + p { + font-size: 16px; + } + } +} + +@media (max-width: 1280px) { + .section { + > div:first-child { + flex-direction: column; + + > div { + margin: 20px 0; + } + } + } +} + +@media (max-width: 768px) { + .section { + margin: 40px 20px; + + > div:last-child { + margin-top: 20px; + flex-direction: column; + + > div { + margin: 10px 0; + } + } + } +} + +.divider { + height: 3px; + margin-top: 60px; +} + +@media (max-width: 1280px) { + .divider { + margin: 0 20px; + } +} + +html[data-theme="light"] { + .container { + h1 { + color: var(--custom-dark-3); + } + } + + .section > div:last-child { + svg path, + svg react { + fill: var(--custom-dark-2); + } + + svg circle { + stroke: var(--custom-dark-2); + } + } + + .divider { + background-color: var(--custom-gray-0); + } +} + +html[data-theme="dark"] { + .container { + h1 { + color: var(--custom-blue-0); + } + } + + .section > div:last-child { + svg path, + svg react { + fill: var(--custom-gray-0); + } + + svg circle { + stroke: var(--custom-gray-0); + } + } + + .divider { + background-color: var(--custom-dark-2); + } +} diff --git a/apps/docs/src/theme/Footer/index.tsx b/apps/docs/src/theme/Footer/index.tsx new file mode 100644 index 00000000..e2f5da2a --- /dev/null +++ b/apps/docs/src/theme/Footer/index.tsx @@ -0,0 +1,105 @@ +import Link from "@docusaurus/Link" +import { translate } from "@docusaurus/Translate" +import Logo from "@theme/Logo" +import clsx from "clsx" +import React from "react" +import styles from "./styles.module.scss" + +function Footer() { + return ( +
+
+
+
+

+ {translate({ + id: "footer.left.title" + })} +

+

+ {translate({ + id: "footer.left.description" + })} +

+ + pse.dev + +
+
+
+

+ {translate({ + id: "footer.right.usedby.title" + })} +

+ + {translate({ + id: "footer.right.usedby.link1" + })} + + + {translate({ + id: "footer.right.usedby.link2" + })} + +
+
+

+ {translate({ + id: "footer.right.learn.title" + })} +

+ + + {translate({ + id: "footer.right.learn.link1" + })} + + + {translate({ + id: "footer.right.learn.link2" + })} + +
+
+

+ {translate({ + id: "footer.right.connect.title" + })} +

+ + {translate({ + id: "footer.right.connect.link1" + })} + + + {translate({ + id: "footer.right.connect.link2" + })} + +
+
+
+ +
+ +
+

+ {translate({ + id: "footer.copyright" + })} +

+ + +
+
+
+ ) +} + +export default React.memo(Footer) diff --git a/apps/docs/src/theme/Footer/styles.module.scss b/apps/docs/src/theme/Footer/styles.module.scss new file mode 100644 index 00000000..4d0b2daa --- /dev/null +++ b/apps/docs/src/theme/Footer/styles.module.scss @@ -0,0 +1,137 @@ +.container { + display: flex; + flex-direction: column; + font-size: 14px; + + a { + color: var(--custom-blue-2); + font-weight: bold; + font-size: 16px; + } + + h3 { + font-weight: bold; + font-size: 18px; + white-space: nowrap; + } + + > div:first-child { + display: flex; + margin-bottom: 20px; + + > div:first-child { + flex: 1; + margin-right: 20px; + + > p { + max-width: 400px; + } + } + + > div:last-child { + flex: 1; + display: flex; + justify-content: right; + + > div { + display: flex; + flex-direction: column; + margin-left: 200px; + + > a { + margin-bottom: 5px; + } + } + } + } + + > div:last-child { + display: flex; + justify-content: space-between; + margin-top: 20px; + + > p { + margin: 0; + } + } +} + +@media (max-width: 1280px) { + .container { + > div:first-child { + > div:last-child { + > div { + margin-left: 50px; + } + } + } + } +} + +@media (max-width: 768px) { + .container { + > div:first-child { + flex-direction: column; + + > div:first-child { + margin-bottom: 30px; + + > p { + margin: 20px 0; + max-width: inherit; + } + } + + > div:last-child { + justify-content: space-between; + + > div { + margin-left: 0; + } + + > p { + font-size: 16px; + font-weight: bold; + } + + > a { + margin: 5px 0; + } + } + } + } +} + +@media (max-width: 640px) { + .container { + > div:last-child { + > a { + display: none; + } + } + } +} + +html[data-theme="light"] { + .container { + a { + color: var(--custom-blue-3); + } + } + + hr { + border-color: var(--custom-gray-1); + } +} + +html[data-theme="dark"] { + .container { + a { + color: var(--custom-blue-2); + } + } + + hr { + border-color: var(--custom-dark-1); + } +} diff --git a/apps/docs/src/theme/IconExternalLink/index.tsx b/apps/docs/src/theme/IconExternalLink/index.tsx new file mode 100644 index 00000000..49b40d7b --- /dev/null +++ b/apps/docs/src/theme/IconExternalLink/index.tsx @@ -0,0 +1,18 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +import IconArrowTopRight from "@site/src/components/icons/IconArrowTopRight" +import React from "react" + +function IconExternalLink() { + return ( + + + + ) +} + +export default IconExternalLink diff --git a/apps/docs/src/theme/Navbar/Search/index.tsx b/apps/docs/src/theme/Navbar/Search/index.tsx new file mode 100644 index 00000000..63343b21 --- /dev/null +++ b/apps/docs/src/theme/Navbar/Search/index.tsx @@ -0,0 +1,11 @@ +import { useLocation } from "@docusaurus/router" +import SearchOriginal from "@theme-original/Navbar/Search" +import React from "react" + +export default function Search(props) { + const { pathname } = useLocation() + + const pathSegments = pathname.split("/") + + return pathSegments.includes("docs") && +} diff --git a/apps/docs/src/theme/NavbarItem/index.tsx b/apps/docs/src/theme/NavbarItem/index.tsx new file mode 100644 index 00000000..478b3bc9 --- /dev/null +++ b/apps/docs/src/theme/NavbarItem/index.tsx @@ -0,0 +1,33 @@ +import { useLocation } from "@docusaurus/router" +import useDocusaurusContext from "@docusaurus/useDocusaurusContext" +import OriginalNavBarItem from "@theme-original/NavbarItem" +import React from "react" + +export default function NavbarItem(props) { + const { pathname } = useLocation() + const { i18n } = useDocusaurusContext() + + const pathSegments = pathname.split("/") + + let docs: string, version: string + + if (i18n.locales.includes(pathSegments[1])) { + docs = pathSegments[2] + version = pathSegments[3] + } else { + docs = pathSegments[1] + version = pathSegments[2] + } + + let { className = "" } = props + + return ( + (!className || + !( + (className.includes("V1") && version !== "V1") || + (className.includes("V2") && version !== "V2") || + (className.includes("V3") && version !== "V3") || + (className.includes("homepage") && docs === "docs") + )) && + ) +} diff --git a/apps/docs/src/theme/Toggle/index.tsx b/apps/docs/src/theme/Toggle/index.tsx new file mode 100644 index 00000000..528fe34c --- /dev/null +++ b/apps/docs/src/theme/Toggle/index.tsx @@ -0,0 +1,64 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +import useIsBrowser from "@docusaurus/useIsBrowser" +import IconMoon from "@site/src/components/icons/IconMoon" +import IconSun from "@site/src/components/icons/IconSun" +import clsx from "clsx" +import React, { memo, useRef, useState } from "react" +import styles from "./styles.module.scss" // Based on react-toggle (https://github.com/aaronshaf/react-toggle/). + +const ToggleComponent = memo(({ className, switchConfig, checked: defaultChecked, disabled, onChange }) => { + const [checked, setChecked] = useState(defaultChecked) + const [focused, setFocused] = useState(false) + const inputRef = useRef(null) + return ( +
+ {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events */} +
inputRef.current?.click()}> +
+ + + +
+
+ + + +
+
+
+ + setChecked(!checked)} + onFocus={() => setFocused(true)} + onBlur={() => setFocused(false)} + onKeyDown={(e) => { + if (e.key === "Enter") { + inputRef.current?.click() + } + }} + /> +
+ ) +}) +export default function Toggle(props) { + const isBrowser = useIsBrowser() + + return +} diff --git a/apps/docs/src/theme/Toggle/styles.module.scss b/apps/docs/src/theme/Toggle/styles.module.scss new file mode 100644 index 00000000..c7ee6700 --- /dev/null +++ b/apps/docs/src/theme/Toggle/styles.module.scss @@ -0,0 +1,110 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +.toggle { + touch-action: pan-x; + position: relative; + cursor: pointer; + user-select: none; + margin-left: 10px; + -webkit-tap-highlight-color: transparent; +} + +.toggleScreenReader { + border: 0; + clip: rect(0 0 0 0); + height: 1px; + margin: -1px; + overflow: hidden; + position: absolute; + width: 1px; +} + +.toggleDisabled { + cursor: not-allowed; +} + +.toggleTrack { + width: 50px; + height: 24px; + border-radius: 30px; + background-color: var(--custom-dark-1); + transition: all 0.2s ease; +} + +.toggleTrackCheck { + position: absolute; + height: 15px; + top: 0; + bottom: 0; + margin: auto 0; + left: 8px; + opacity: 0; + transition: opacity 0.25s ease; +} + +.toggleChecked .toggleTrackCheck, +[data-theme="dark"] .toggle .toggleTrackCheck { + opacity: 1; + transition: opacity 0.25s ease; +} + +.toggleTrackX { + position: absolute; + width: 12px; + height: 15px; + top: 0; + bottom: 0; + margin: auto 0; + right: 10px; + opacity: 1; + transition: opacity 0.25s ease; +} + +.toggleChecked .toggleTrackX, +[data-theme="dark"] .toggle .toggleTrackX { + opacity: 0; +} + +.toggleTrackThumb { + position: absolute; + top: 1px; + left: 1px; + width: 22px; + height: 22px; + border: 1px solid #4d4d4d; + border-radius: 50%; + background-color: #fafafa; + transition: all 0.25s ease; +} + +.toggleFocused .toggleTrackThumb, +.toggle:hover .toggleTrackThumb { + box-shadow: 0 0 2px 3px var(--ifm-color-primary); +} + +/* stylelint-disable-next-line no-descending-specificity */ +.toggleChecked .toggleTrackThumb, +[data-theme="dark"] .toggle .toggleTrackThumb { + left: 27px; +} + +.toggle:active:not(.toggleDisabled) .toggleTrackThumb { + box-shadow: 0 0 5px 5px var(--ifm-color-primary); +} + +.toggleIcon { + align-items: center; + display: flex; + height: 15px; + justify-content: center; + width: 15px; + + svg > path { + fill: var(--custom-blue-0); + } +} diff --git a/apps/docs/src/types/global.d.ts b/apps/docs/src/types/global.d.ts new file mode 100644 index 00000000..e4089fc8 --- /dev/null +++ b/apps/docs/src/types/global.d.ts @@ -0,0 +1 @@ +declare module "*.scss" diff --git a/apps/docs/static/.nojekyll b/apps/docs/static/.nojekyll new file mode 100644 index 00000000..e69de29b diff --git a/apps/docs/static/CNAME b/apps/docs/static/CNAME new file mode 100644 index 00000000..1de24771 --- /dev/null +++ b/apps/docs/static/CNAME @@ -0,0 +1 @@ +semaphore.pse.dev diff --git a/apps/docs/static/img/favicon.ico b/apps/docs/static/img/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c695649f65a7539eee0bb8b381920793dbc45376 GIT binary patch literal 15406 zcmeI2*KZU_5Qpb<_Xltf_kajqKp2xvG8rrcMnnrC0TxCC33GtRNMwm5K!5-N55(gE zgiXdIiSz&%69giQ9E=T^oWs-A&DYSQ@!W!E*E_Roc~@F>&rEmMudA!8yMkf-X8dkc zs$`gD!1((Q!$>ge|G-!Y7)G38! z>(;Fy_0?nS+__WUym=$=MZa@f_UhG3zJC2Gix)2rxi2)%o;@pf@7`5&=-$11NMj5g zI#hCUawIV^QSRNlr+B%2`*vyBvZbqW5)u+*^5n^~VZ#QQF=K|B2W$HN{d-xta;59F ztXsFPEM2-(3JVKm@7}#KY0@OMzHWKwN4v7@LHY(&3L(5i&jieWs`*Dh{p@Gb%C1_q z>c4*Q#7+a6CheSP(AGGjNn2xxm*Jgpe)6q)_3F~KYgd^+f4-bJaY7zFdL&b(Op!n! z;7ad>gSBzv#xiQuD0OC`=g^b-f91**xp?uST)%!@1`Qe%aSTnGG?6oB&RFy`U0rg1 zpvyT&3knM4`0?Y?xpU`;;}|hwgt{+iy7sa|hYpHfojP^YS@8PxYuUVcvovkmG@>}T zU(A{{OP)P@R)QbgJ@mRFADoRJK73H?rQb)gtY5#rtXj27ot5wbIUG51q@q_;RHU$R zj<#;y+QJn{`g$zRdhP(+qwMQJoikjw^SWd|-n@C!!tmtD6X9OqHC;VE`}5_?mll2W zf&S)4xoOiTi}tl^*F4ol&iQoe)JZ;n{%p}7IB=k^b3c0Ys70H)6Q7sQnVFduZDbxB zqEDYbzF^?IxpCu$qJQ}CVL5&Jw5nqd?Ay0b+P81-DUNB=rm20uWXTdi=cT5mDxIhG zz{igtW!<`U(x_1*PjFnldR5sj?0vn)%a<=Nu^wP~@#2NdojX@*)v9H+)9uSrCM6{a z^swEwY}q2snl+Q!wQDO(ng{ms{{8#v42M@`q3i664#tM$%v-c*kqjR`+_I(Y^mYB* zxpNAubDXk1_wLC>mOW5*5|J9ccu&tyApcsqLZXl0MGAK*ui9z7(% zyr0j2Z{>f@pCgS0~K2auzOmyYNhO4;x5>{d3kx# zzkh#UeJ-KMA^w>G0|v4xv z40j*Sgjkd#a!t6iY2K5Afv&{v(eIcOHW|6DUgu(j!hh}zZoH*zR`Ovd@7!xka~b-IA=VENk8L{QWAy$o?c3id-XX<#z4bsoWFf3;pl3ah&sD zJq{c=plmYE5x=l&I>`Ls!Go2&Fb8t!=FOX@WSsetSLehW?76tOSlC0zr5@YQ5?zGP z1sffnV=v-6Lcg-dgJE{QLl&{o=;J4LJ1y*Ee2U0EGEOd`^FFO{BRk|3koSen$$YuP z+Q*Aly-Sxa$`*#78ZRZ7!MkVA9$BzpfjXnZ$@}+XUYxldJ9boO7yAMqW6z#F6_2B_ zSN)iCoJPftZN%5VVyu68j=_VyJm+ti*bB`Zlr-kRT*}Ly11p%x{V~&n;=ect!sft# D8b|@V literal 0 HcmV?d00001 diff --git a/apps/docs/static/img/semaphore-icon-dark.svg b/apps/docs/static/img/semaphore-icon-dark.svg new file mode 100644 index 00000000..57c3ad70 --- /dev/null +++ b/apps/docs/static/img/semaphore-icon-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/docs/static/img/semaphore-icon.svg b/apps/docs/static/img/semaphore-icon.svg new file mode 100644 index 00000000..9db87451 --- /dev/null +++ b/apps/docs/static/img/semaphore-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/docs/static/img/semaphore-logo.svg b/apps/docs/static/img/semaphore-logo.svg new file mode 100644 index 00000000..5fd48c27 --- /dev/null +++ b/apps/docs/static/img/semaphore-logo.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/docs/static/whitepaper-v1.pdf b/apps/docs/static/whitepaper-v1.pdf new file mode 100644 index 0000000000000000000000000000000000000000..c87f10b3b4b9b27dc4d3af421508c48881888f35 GIT binary patch literal 305914 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 z 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୛t2JE4 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

KM0^%zw7RR9^xx0IxXPf+0+nScCBsRFM5)+Ke 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%0D;f* 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 M&A3!mUH#p-0GHdOL;wH) literal 0 HcmV?d00001 diff --git a/apps/docs/tsconfig.json b/apps/docs/tsconfig.json new file mode 100644 index 00000000..41ffdec4 --- /dev/null +++ b/apps/docs/tsconfig.json @@ -0,0 +1,7 @@ +{ + // This file is not used in compilation. It is here just for a nice editor experience. + "extends": "@tsconfig/docusaurus/tsconfig.json", + "compilerOptions": { + "baseUrl": "." + } +} diff --git a/apps/docs/versioned_docs/version-V1/audit.md b/apps/docs/versioned_docs/version-V1/audit.md new file mode 100644 index 00000000..d8c79479 --- /dev/null +++ b/apps/docs/versioned_docs/version-V1/audit.md @@ -0,0 +1,17 @@ +--- +sidebar_position: 8 +--- + +# Security audit + +The [Ethereum Foundation](https://ethereum.org/) and [POA +Network](https://www.poa.network/) commissioned [ABDK +Consulting](https://www.abdk.consulting) to audit the source code of Semaphore +as well as relevant circuits in +[circomlib](https://github.com/iden3/circomlib), which contains components +which the Semaphore zk-SNARK uses. + +The summary of the audit results can be found +[here](https://github.com/appliedzkp/semaphore/tree/master/audit). After three +rounds of fixes, all security and performance issues were fixed, and the few +remaining issues are minor and do not affect security. diff --git a/apps/docs/versioned_docs/version-V1/contract-api.md b/apps/docs/versioned_docs/version-V1/contract-api.md new file mode 100644 index 00000000..702a1ba5 --- /dev/null +++ b/apps/docs/versioned_docs/version-V1/contract-api.md @@ -0,0 +1,105 @@ +--- +sidebar_position: 5 +--- + +# Contract API + +## Constructor + +**Contract ABI**: + +`constructor(uint8 _treeLevels, uint232 _firstExternalNullifier)` + +- `_treeLevels`: The depth of the identity tree. +- `_firstExternalNullifier`: The first identity nullifier to add. + +The depth of the identity tree determines how many identity commitments may be +added to this contract: `2 ^ _treeLevels`. Once the tree is full, further +insertions will fail with the revert reason `IncrementalMerkleTree: tree is full`. + +The first external nullifier will be added as an external nullifier to the +contract, and this external nullifier will be active once the deployment +completes. + +## Add, deactivate, or reactivate external nullifiiers + +**Contract ABI**: + +`addExternalNullifier(uint232 _externalNullifier)` + +Adds an external nullifier to the contract. Only the owner can do this. +This external nullifier is active once it is added. + +- `_externalNullifier`: The new external nullifier to set. + +`deactivateExternalNullifier(uint232 _externalNullifier)` + +- `_externalNullifier`: The existing external nullifier to deactivate. + +Deactivate an external nullifier. The external nullifier must already be active +for this function to work. Only the owner can do this. + +`reactivateExternalNullifier(uint232 _externalNullifier)` + +Reactivate an external nullifier. The external nullifier must already be +inactive for this function to work. Only the owner can do this. + +- `_externalNullifier`: The deactivated external nullifier to reactivate. + +## Insert identities + +**Contract ABI**: + +`function insertIdentity(uint256 _identityCommitment)` + +- `_identity_commitment`: The user's identity commitment, which is the hash of + their public key and their identity nullifier (a random 31-byte value). It + should be the output of a Pedersen hash. It is the responsibility of the + caller to verify this. + +**Off-chain `libsemaphore` helper functions**: + +Use `genIdentity()` to generate an `Identity` object, and +`genIdentityCommitment(identity: Identity)` to generate the +`_identityCommitment` value to pass to the contract. + +To convert `identity` to a string and back, so that you can store it in a +database or somewhere safe, use `serialiseIdentity()` and +`unSerialiseIdentity()`. + +See the [Usage section on inserting +identities](./usage#insert-identities) for more information. + +## Broadcast signals + +**Contract ABI**: + +``` +broadcastSignal( + bytes memory _signal, + uint256[8] memory _proof, + uint256 _root, + uint256 _nullifiersHash, + uint232 _externalNullifier +) +``` + +- `_signal`: the signal to broadcast. +- `_proof`: a zk-SNARK proof (see below). +- `_root`: The root of the identity tree, where the user's identity commitment + is the last-inserted leaf. +- `_nullifiersHash`: A uniquely derived hash of the external nullifier, user's + identity nullifier, and the Merkle path index to their identity commitment. + It ensures that a user cannot broadcast a signal with the same external + nullifier more than once. +- `_externalNullifier`: The external nullifier at which the signal is + broadcast. + +**Off-chain `libsemaphore` helper functions**: + +Use `libsemaphore`'s `genWitness()`, `genProof()`, `genPublicSignals()` and +finally `genBroadcastSignalParams()` to generate the parameters to the +contract's `broadcastSignal()` function. + +See the [Usage section on broadcasting +signals](./usage#broadcast-signals) for more information. diff --git a/apps/docs/versioned_docs/version-V1/creditsandresources.md b/apps/docs/versioned_docs/version-V1/creditsandresources.md new file mode 100644 index 00000000..5d712949 --- /dev/null +++ b/apps/docs/versioned_docs/version-V1/creditsandresources.md @@ -0,0 +1,33 @@ +--- +sidebar_position: 9 +--- + +# Credits + +- Barry WhiteHat +- Chih Cheng Liang +- Kobi Gurkan +- Koh Wei Jie +- Harry Roberts + +Many thanks to: + +- ABDK Consulting +- Jordi Baylina / iden3 +- POA Network +- PepperSec +- Ethereum Foundation + +# Resources + +[To Mixers and Beyond: presenting Semaphore, a privacy gadget built on Ethereum](https://medium.com/coinmonks/to-mixers-and-beyond-presenting-semaphore-a-privacy-gadget-built-on-ethereum-4c8b00857c9b) - Koh Wei Jie + +[Privacy in Ethereum](https://www.youtube.com/watch?v=maDHYyj30kg) - Barry WhiteHat at the Taipei Ethereum Meetup + +[Snarks for mixing, signaling and scaling by](https://www.youtube.com/watch?v=lv6iK9qezBY) - Barry WhiteHat at Devcon 4 + +[Privacy in Ethereum](https://www.youtube.com/watch?v=zBUo7G95wYE) - Barry WhiteHat at Devcon 5 + +[A trustless Ethereum mixer using zero-knowledge signalling](https://www.youtube.com/watch?v=GzVT16lFOHU) - Koh Wei Jie and Barry WhiteHat at Devcon 5 + +[Hands-on Applications of Zero-Knowledge Signalling](https://www.youtube.com/watch?v=7wd2aAN2jXI) - Koh Wei Jie at Devcon 5 diff --git a/apps/docs/versioned_docs/version-V1/howitworks.md b/apps/docs/versioned_docs/version-V1/howitworks.md new file mode 100644 index 00000000..d2c602cf --- /dev/null +++ b/apps/docs/versioned_docs/version-V1/howitworks.md @@ -0,0 +1,137 @@ +--- +sidebar_position: 2 +--- + +# How it works + +## Inserting identities + +An identity is comprised of the following information: + +1. An [EdDSA](https://en.wikipedia.org/wiki/EdDSA) private key. Note that it is + _not_ an Ethereum private key. +2. An identity nullifier, whih is a random 32-byte value. +3. An identity trapdoor, whih is a random 32-byte value. + +An identity commitment is the Pedersen hash of: + +1. The public key associated with the identity's private key. +2. The identity nullifier. +3. The identity trapdoor. + +To register an identity, the user must insert their identity commitment into +Semaphore's identity tree. They can do this by calling the Semaphore contract's +`insertIdentity(uint256 _identityCommitment)` function. See the [API +reference](./contract-api) for more information. + +## Broadcasting signals + +To broadcast a signal, the user must invoke this Semaphore contract function: + +``` +broadcastSignal( + bytes memory _signal, + uint256[8] memory _proof, + uint256 _root, + uint256 _nullifiersHash, + uint232 _externalNullifier +) +``` + +- `_signal`: the signal to broadcast. +- `_proof`: a zk-SNARK proof (see below). +- `_root`: The root of the identity tree, where the user's identity commitment + is the last-inserted leaf. +- `_nullifiersHash`: A uniquely derived hash of the external nullifier, user's + identity nullifier, and the Merkle path index to their identity commitment. + It ensures that a user cannot broadcast a signal with the same external + nullifier more than once. +- `_externalNullifier`: The external nullifier at which the signal is + broadcast. + +To zk-SNARK proof must satisfy the constraints created by Semaphore's zk-SNARK +circuit as described below: + +### The zk-SNARK circuit + +The +[semaphore-base.circom](https://github.com/appliedzkp/semaphore/blob/master/circuits/circom/semaphore-base.circom) +circuit helps to prove the following: + +### That the identity commitment exists in the Merkle tree + +**Private inputs:** + +- `identity_pk`: the user's EdDSA public key +- `identity_nullifier`: a random 32-byte value which the user should save +- `identity_trapdoor`: a random 32-byte value which the user should save +- `identity_path_elements`: the values along the Merkle path to the + user's identity commitment +- `identity_path_index[n_levels]`: the direction (left/right) per tree level + corresponding to the Merkle path to the user's identity commitment + +**Public inputs:** + +- `root`: The Merkle root of the identity tree + +**Procedure:** + +The circuit hashes the public key, identity nullifier, and identity trapdoor to +generate an **identity commitment**. It then verifies the Merkle proof against +the Merkle root and the identity commitment. + +### That the signal was only broadcasted once + +**Private inputs:** + +- `identity_nullifier`: as above +- `identity_path_index`: as above + +**Public inputs:** + +- `external_nullifier`: the 29-byte external nullifier - see above +- `nullifiers_hash`: the hash of the identity nullifier, external nullifier, + and Merkle path index (`identity_path_index`) + +**Procedure:** + +The circuit hashes the given identity nullifier, external nullifier, and Merkle +path index, and checks that it matches the given nullifiers hash. Additionally, +the smart contract ensures that it has not previously seen this nullifiers +hash. This way, double-signalling is impossible. + +### That the signal was truly broadcasted by the user who generated the proof + +**Private inputs:** + +- `identity_pk`: as above +- `auth_sig_r`: the `r` value of the signature of the signal +- `auth_sig_s`: the `s` value of the signature of the signal + +**Public inputs:** + +- `signal_hash`: the hash of the signal +- `external_nullifier`: the 29-byte external nullifier - see above + +**Procedure:** + +The circuit hashes the signal hash and the external nullifier, and verifies +this output against the given public key and signature. This ensures the +authenticity of the signal and prevents front-running attacks. + +## Cryptographic primitives + +Semaphore uses MiMC for the Merkle tree, Pedersen commmitments for the identity +commitments, Blake2 for the nullifiers hash, and EdDSA for the signature. + +MiMC is a relatively new hash function. We use the recommended MiMC +construction from [Albrecht et al](https://eprint.iacr.org/2016/492.pdf), and +there is a prize to break MiMC at [http://mimchash.org](http://mimchash.org) +which has not been claimed yet. + +We have also implemented a version of Semaphore which uses the Poseidon hash +function for the Merkle tree and EdDSA signature verification. This may have +better security than MiMC, allows identity insertions to save about 20% gas, +and roughly halves the proving time. Note, however, that the Poseidon-related +circuits and EVM bytecode generator have not been audited, so use it with +caution. To use it, checkout the `feat/poseidon` branch of this repository. diff --git a/apps/docs/versioned_docs/version-V1/libsemaphore.md b/apps/docs/versioned_docs/version-V1/libsemaphore.md new file mode 100644 index 00000000..959ad2b4 --- /dev/null +++ b/apps/docs/versioned_docs/version-V1/libsemaphore.md @@ -0,0 +1,245 @@ +--- +sidebar_position: 6 +--- + +# libsemaphore + +[`libsemaphore`](https://www.npmjs.com/package/libsemaphore) is a helper +library for Semaphore written in Typescript. Any dApp written in Javascript or +Typescript should use it as it provides useful abstractions over common tasks +and objects, such as identities and proof generation. + +Note that only v1.0.14 and above works with the Semaphore code in this +repository. v0.0.x is compatible with the pre-audited Semaphore code. + +## Available types, interfaces, and functions + +### Types + +**`SnarkBigInt`** + +A big integer type compatible with the `snarkjs` library. Note that it is not +advisable to mix variables of this type with `bigNumber`s or `BigInt`s. +Encapsulates `snarkjs.bigInt`. + +**`EddsaPrivateKey`** + +An [EdDSA](https://tools.ietf.org/html/rfc8032) private key which should be 32 +bytes long. Encapsulates a [`Buffer`](https://nodejs.org/api/buffer.html). + +**`EddsaPublicKey`** + +An EdDSA public key. Encapsulates an array of `SnarkBigInt`s. + +**`SnarkProvingKey`** + +A proving key, which when used with a secret _witness_, generates a zk-SNARK +proof about said witness. Encapsulates a `Buffer`. + +**`SnarkVerifyingKey`** + +A verifying key which when used with public inputs to a zk-SNARK and a +`SnarkProof`, can prove the proof's validity. Encapsulates a `Buffer`. + +**`SnarkWitness`** + +The secret inputs to a zk-SNARK. Encapsulates an array of `SnarkBigInt`s. + +**`SnarkPublicSignals`** + +The public inputs to a zk-SNARK. Encapsulates an array of `SnarkBigInt`s. + +### Interfaces + +**`EddsaKeyPair`** + +Encapsulates an `EddsaPublicKey` and an `EddsaPrivateKey`. + +```ts +interface EddsaKeyPair { + pubKey: EddsaPublicKey + privKey: EddsaPrivateKey +} +``` + +**`Identity`** + +Encapsulates all information required to generate an identity commitment, and +is crucial to creating `SnarkProof`s to broadcast signals. + +```ts +interface Identity { + keypair: EddsaKeyPair + identityNullifier: SnarkBigInt + identityTrapdoor: SnarkBigInt +} +``` + +**`SnarkProof`** + +Note that `broadcastSignal()` accepts a `uint256[8]` array for its `_proof` +parameter. See `genBroadcastSignalParams()`. + +```ts +interface SnarkProof { + pi_a: SnarkBigInt[] + pi_b: SnarkBigInt[][] + pi_c: SnarkBigInt[] +} +``` + +### Functions + +**`genPubKey(privKey: EddsaPrivateKey): EddsaPublicKey`** + +Generates a public EdDSA key from a supplied private key. To generate a private +key, use `crypto.randomBytes(32)` where `crypto` is the built-in Node or +browser module. + +**`genIdentity(): Identity`** + +This is a convenience function to generate a fresh and random `Identity`. That +is, the 32-byte private key for the `EddsaKeyPair` is randomly generated, as +are the distinct 31-byte identity nullifier and the 31-byte identity trapdoor +values. + +**`serialiseIdentity(identity: Identity): string`** + +Converts an `Identity` into a JSON string which looks like this: + +```text +["e82cc2b8654705e427df423c6300307a873a2e637028fab3163cf95b18bb172e","a02e517dfb3a4184adaa951d02bfe0fe092d1ee34438721d798db75b8db083","15c6540bf7bddb0616984fccda7e954a0fb5ea4679ac686509dc4bd7ba9c3b"] +``` + +You can also spell this function as `serializeIdentity`. + +To convert this string back into an `Identity`, use `unSerialiseIdentity()`. + +**`unSerialiseIdentity(string: serialisedId): Identity`** + +Converts the `string` output of `serialiseIdentity()` to an `Identity`. + +You can also spell this function as `unSerializeIdentity`. + +**`genIdentityCommitment(identity: Identity): SnarkBigInt`** + +Generates an identity commitment, which is the hash of the public key, the +identity nullifier, and the identity trapdoor. + +**`async genProof(witness: SnarkWitness, provingKey: SnarkProvingKey): SnarkProof`** + +Generates a `SnarkProof`, which can be sent to the Semaphore contract's +`broadcastSignal()` function. It can also be verified off-chain using +`verifyProof()` below. + +**`genPublicSignals(witness: SnarkWitness, circuit: SnarkCircuit): SnarkPublicSignals`** + +Extracts the public signals to be supplied to the contract or `verifyProof()`. + +**`verifyProof(verifyingKey: SnarkVerifyingKey, proof: SnarkProof, publicSignals: SnarkPublicSignals): boolean`** + +Returns `true` if the given `proof` is valid, given the correct verifying key +and public signals. + +Returns `false` otherwise. + +**`signMsg(privKey: EddsaPrivateKey, msg: SnarkBigInt): EdDSAMiMcSpongeSignature)`** + +Encapsualtes `circomlib.eddsa.signMiMCSponge` to sign a message `msg` using private key `privKey`. + +**`verifySignature(msg: SnarkBigInt, signature: EdDSAMiMcSpongeSignature, pubKey: EddsaPublicKey)`: boolean** + +Returns `true` if the cryptographic `signature` of the signed `msg` is from the +private key associated with `pubKey`. + +Returns `false` otherwise. + +**`setupTree(levels: number, prefix: string): MerkleTree`** + +Returns a Merkle tree created using +[`semaphore-merkle-tree`](https://www.npmjs.com/package/semaphore-merkle-tree) +with the same number of levels which the Semaphore zk-SNARK circuit expects. +This tree is also configured to use `MimcSpongeHasher`, which is also what the +circuit expects. + +`levels` sets the number of levels of the tree. A tree with 20 levels, for +instance, supports up to 1048576 deposits. + +**`genCircuit(circuitDefinition: any)`** + +Returns a `new snarkjs.Circuit(circuitDefinition)`. The `circuitDefinition` +object should be the `JSON.parse`d result of the `circom` command which +converts a `.circom` file to a `.json` file. + +**`async genWitness(...)`** + +This function has the following signature: + +```ts +const genWitness = async ( + signal: string, + circuit: SnarkCircuit, + identity: Identity, + idCommitments: SnarkBigInt[] | BigInt[] | ethers.utils.BigNumber[], + treeDepth: number, + externalNullifier: SnarkBigInt, +) +``` + +- `signal` is the string you wish to broadcast. +- `circuit` is the output of `genCircuit()`. +- `identity` is the `Identity` whose identity commitment you want to prove is + in the set of registered identities. +- `idCommitments` is an array of registered identity commmitments; i.e. the + leaves of the tree. +- `treeDepth` is the number of levels which the Merkle tree used has +- `externalNullifier` is the current external nullifier + +It returns an object as such: + +- `witness`: The witness to pass to `genProof()`. +- `signal`: The computed signal for Semaphore. This is the hash of the + recipient's address, relayer's address, and fee. +- `signalHash`: The hash of the computed signal. +- `msg`: The hash of the external nullifier and the signal hash +- `signature`: The signature on the above msg. +- `tree`: The Merkle tree object after it has been updated with the identity commitment +- `identityPath`: The Merkle path to the identity commmitment +- `identityPathIndex`: The leaf index of the identity commitment +- `identityPathElements`: The elements along the above Merkle path + +Only `witness` is essential to generate the proof; the other data is only +useful for debugging and additional off-chain checks, such as verifying the +signature and the Merkle tree root. + +**`formatForVerifierContract = (proof: SnarkProof, publicSignals: SnarkPublicSignals`** + +Converts the data in `proof` and `publicSignals` to strings and rearranges +elements of `proof.pi_b` so that `snarkjs`'s `verifier.sol` will accept it. +To be specific, it returns an object as such: + +```ts +{ + a: [ proof.pi_a[0].toString(), proof.pi_a[1].toString() ], + b: [ + [ proof.pi_b[0][1].toString(), proof.pi_b[0][0].toString() ], + [ proof.pi_b[1][1].toString(), proof.pi_b[1][0].toString() ], + ], + c: [ proof.pi_c[0].toString(), proof.pi_c[1].toString() ], + input: publicSignals.map((x) => x.toString()), +} +``` + +**`stringifyBigInts = (obj: any) => object`** + +Encapsulates `snarkjs.stringifyBigInts()`. Makes it easy to convert `SnarkProof`s to JSON. + +**`unstringifyBigInts = (obj: any) => object`** + +Encapsulates `snarkjs.unstringifyBigInts()`. Makes it easy to convert JSON to `SnarkProof`s. + +**`genExternalNullifier = (plaintext: string) => string`** + +Each external nullifier must be at most 29 bytes large. This function +keccak-256-hashes a given `plaintext`, takes the last 29 bytes, and pads it +(from the start) with 0s, and returns the resulting hex string. diff --git a/apps/docs/versioned_docs/version-V1/quickstart.md b/apps/docs/versioned_docs/version-V1/quickstart.md new file mode 100644 index 00000000..0a10bf7e --- /dev/null +++ b/apps/docs/versioned_docs/version-V1/quickstart.md @@ -0,0 +1,68 @@ +--- +sidebar_position: 3 +--- + +# Quick start + +Semaphore has been tested with Node 11.14.0. It will run with Node 12 LTE but +we highly recommend using Node 11.14.0 if you wish to develop on its source +code, as one of its dependencies, `script`, cannot compile when if you use Node 12. + +Use [`nvm`](https://github.com/nvm-sh/nvm) to manage your Node version. + +Clone this repository, install dependencies, and build the source code: + +```bash +git clone git@github.com:kobigurk/semaphore.git && \ +cd semaphore && \ +npm i && \ +npm run bootstrap && \ +npm run build +``` + +**Note**: we use `lerna` to manage the `circuits`, `config`, and `contracts` +subpackages. Do not run `npm install` within any of these directories. Instead, +just run `npm run bootstrap` in the main directory. + +Next, either download the compiled zk-SNARK circuit, proving key, and +verification key (note that these keys are for testing purposes, and not for +production, as there is no certainty that the toxic waste was securely +discarded). + +To download the circuit, proving key, and verification key, run: + +```bash +# Start from the base directory + +./circuits/scripts/download_snarks.sh +``` + +To generate the above files locally instead, run: + +```bash +# Start from the base directory + +./circuits/scripts/build_snarks.sh +``` + +This process should take about 45 minutes. + +Build the Solidity contracts (you need `solc` v 0.5.12 installed in your +`$PATH`): + +```bash +# Start from the base directory + +cd contracts && \ +npm run compileSol +``` + +Run tests while still in the `contracts/` directory: + +```bash +# The first command tests the Merkle tree contract and the second +# tests the Semaphore contract + +npm run test-semaphore && \ +npm run test-mt +``` diff --git a/apps/docs/versioned_docs/version-V1/trustedsetup.md b/apps/docs/versioned_docs/version-V1/trustedsetup.md new file mode 100644 index 00000000..a25473ef --- /dev/null +++ b/apps/docs/versioned_docs/version-V1/trustedsetup.md @@ -0,0 +1,11 @@ +--- +sidebar_position: 7 +--- + +# Multi-party trusted setup + +The Semaphore authors will use the [Perpetual Powers of +Tau](https://github.com/weijiekoh/perpetualpowersoftau/) ceremony and a random +beacon as phase 1 of the trusted setup. + +More details about phase 2 will be released soon. diff --git a/apps/docs/versioned_docs/version-V1/usage.md b/apps/docs/versioned_docs/version-V1/usage.md new file mode 100644 index 00000000..d0f62b22 --- /dev/null +++ b/apps/docs/versioned_docs/version-V1/usage.md @@ -0,0 +1,144 @@ +--- +sidebar_position: 4 +--- + +# Usage + +The Semaphore contract forms a base layer for other contracts to create +applications that rely on anonymous signaling. + +First, you should ensure that the proving key, verification key, and circuit +file, which are static, be easily available to your users. These may be hosted +in a CDN or bundled with your application code. + +The Semaphore team has not performed a trusted setup yet, so trustworthy +versions of these files are not available yet. + +Untrusted versions of these files, however, may be obtained via the +`circuits/scripts/download_snarks.sh` script. + +Next, to have full flexibility over Semaphore's mechanisms, write a Client +contract and set the owner of the Semaphore contract as the address of the +Client contract. You may also write a Client contract which deploys a Semaphore +contract in its constructor, or on the fly. + +With the Client contract as the owner of the Semaphore contract, the Client +contract may call owner-only Semaphore functions such as +`addExternalNullifier()`. + +## Add, deactivate, or reactivate external nullifiiers + +These functions add, deactivate, and reactivate an external nullifier respectively. +As each identity can only signal once to an external nullifier, and as a signal +can only be successfully broadcasted to an active external nullifier, these +functions enable use cases where it is necessary to have multiple external +nullifiers or to activate and/or deactivate them. + +Refer to the [high-level explanation of +Semaphore](https://medium.com/coinmonks/to-mixers-and-beyond-presenting-semaphore-a-privacy-gadget-built-on-ethereum-4c8b00857c9b) +for more details. + +## Set broadcast permissioning + +Note that `Semaphore.broadcastSignal()` is permissioned by default, so if you +wish for anyone to be able to broadcast a signal, the owner of the Semaphore +contract (either a Client contract or externally owned account) must first +invoke `setPermissioning(false)`. + +See [SemaphoreClient.sol](https://github.com/appliedzkp/semaphore/blob/master/contracts/sol/SemaphoreClient.sol) for an example. + +## Insert identities + +To generate an identity commitment, use the `libsemaphore` functions +`genIdentity()` and `genIdentityCommitment()` Typescript (or Javascript) +functions: + +```ts +const identity: Identity = genIdentity() +const identityCommitment = genIdentityCommitment(identity) +``` + +Be sure to store `identity` somewhere safe. The `serialiseIdentity()` function +can help with this: + +`const serialisedId: string = serialiseIdentity(identity: Identity)` + +It converts an `Identity` into a JSON string which looks like this: + +```text +["e82cc2b8654705e427df423c6300307a873a2e637028fab3163cf95b18bb172e","a02e517dfb3a4184adaa951d02bfe0fe092d1ee34438721d798db75b8db083","15c6540bf7bddb0616984fccda7e954a0fb5ea4679ac686509dc4bd7ba9c3b"] +``` + +To convert this string back into an `Identity`, use `unSerialiseIdentity()`. + +`const id: Identity = unSerialiseIdentity(serialisedId)` + +## Broadcast signals + +First obtain the leaves of the identity tree (in sequence, up to the user's +identity commitment, or more). + +```ts +const leaves = +``` + +Next, load the circuit from disk (or from a remote source): + +```ts +const circuitPath = path.join(__dirname, "/path/to/circuit.json") +const cirDef = JSON.parse(fs.readFileSync(circuitPath).toString()) +const circuit = genCircuit(cirDef) +``` + +Next, use `libsemaphore`'s `genWitness()` helper function as such: + +``` +const result = await genWitness( + signal, + circuit, + identity, + leaves, + num_levels, + external_nullifier, +) +``` + +- `signal`: a string which is the signal to broadcast. +- `circuit`: the output of `genCircuit()` (see above). +- `identity`: the user's identity as an `Identity` object. +- `leaves` the list of leaves in the tree (see above). +- `num_levels`: the depth of the Merkle tree. +- `external_nullifier`: the external nullifier at which to broadcast. + +Load the proving key from disk (or from a remote source): + +```ts +const provingKeyPath = path.join(__dirname, "/path/to/proving_key.bin") +const provingKey: SnarkProvingKey = fs.readFileSync(provingKeyPath) +``` + +Generate the proof (this takes about 30-45 seconds on a modern laptop): + +```ts +const proof = await genProof(result.witness, provingKey) +``` + +Generate the `broadcastSignal()` parameters: + +```ts +const publicSignals = genPublicSignals(result.witness, circuit) +const params = genBroadcastSignalParams(result, proof, publicSignals) +``` + +Finally, invoke `broadcastSignal()` with the parameters: + +```ts +const tx = await semaphoreClientContract.broadcastSignal( + ethers.utils.toUtf8Bytes(signal), + params.proof, + params.root, + params.nullifiersHash, + external_nullifier, + { gasLimit: 500000 } +) +``` diff --git a/apps/docs/versioned_docs/version-V1/what-is-semaphore.md b/apps/docs/versioned_docs/version-V1/what-is-semaphore.md new file mode 100644 index 00000000..40fa61b5 --- /dev/null +++ b/apps/docs/versioned_docs/version-V1/what-is-semaphore.md @@ -0,0 +1,105 @@ +--- +id: introduction +title: What Is Semaphore? +sidebar_position: 1 +--- + +## Overview + +[Semaphore](https://github.com/appliedzkp/semaphore) is a zero-knowledge gadget +which allows Ethereum users to prove their membership of a set which they had +previously joined without revealing their original identity. At the same time, +it allows users to signal their endorsement of an arbitrary string. It is +designed to be a simple and generic privacy layer for Ethereum dApps. Use cases +include private voting, whistleblowing, mixers, and anonymous authentication. +Finally, it provides a simple built-in mechanism to prevent double-signalling +or double-spending. + +This gadget comprises of smart contracts and +[zero-knowledge](https://z.cash/technology/zksnarks/) components which work in +tandem. The Semaphore smart contract handles state, permissions, and proof +verification on-chain. The zero-knowledge components work off-chain to allow +the user to generate proofs, which allow the smart contract to update its state +if these proofs are valid. + +For a formal description of Semaphore and its underlying cryptographic +mechanisms, also see this document +[here](https://github.com/appliedzkp/semaphore/tree/master/spec). + +Semaphore is designed for smart contract and dApp developers, not end users. +Developers should abstract its features away in order to provide user-friendly +privacy. + +Try a simple demo [here](https://weijiekoh.github.io/semaphore-ui/) or read a +high-level description of Semaphore +[here](https://medium.com/coinmonks/to-mixers-and-beyond-presenting-semaphore-a-privacy-gadget-built-on-ethereum-4c8b00857c9b). + +## Basic features + +In sum, Semaphore provides the ability to: + +1. Register an identity in a smart contract, and then: + +2. Broadcast a signal: + + - Anonymously prove that their identity is in the set of registered + identities, and at the same time: + + - Publicly store an arbitrary string in the contract, if and only if that + string is unique to the user and the contract’s current external + nullifier, which is a unique value akin to a topic. This means that + double-signalling the same message under the same external nullifier is + not possible. + +### External nullifiers + +Think of an external nullifier as a voting booth where each user may only cast +one vote. If they try to cast a second vote a the same booth, that vote is +invalid. + +An external nullifier is any 29-byte value. Semaphore always starts with one +external nullifier, which is set upon contract deployment. The owner of the +Semaphore contract may add more external nullifiers, deactivate, or reactivate +existing ones. + +The first time a particular user broadcasts a signal to an active external +nullifier `n`, and if the user's proof of membership of the set of registered +users is valid, the transaction will succeed. The second time she does so to +the same `n`, however, her transaction will fail. + +Additionally, all signals broadcast transactions to a deactivated external +nullifier will fail. + +Each client application must use the above features of Semaphore in a unique +way to achieve its privacy goals. A mixer, for instance, would use one external +nullifier as such: + +| Signal | External nullifier | +| ----------------------------------------------------------------------------- | ---------------------------- | +| The hash of the recipient's address, relayer's address, and the relayer's fee | The mixer contract's address | + +This allows anonymous withdrawals of funds (via a transaction relayer, who is +rewarded with a fee), and prevents double-spending as there is only one +external nullifier. + +An anonymous voting app would be configured differently: + +| Signal | External nullifier | +| ----------------------------------- | ------------------------ | +| The hash of the respondent's answer | The hash of the question | + +This allows any user to vote with an arbitary response (e.g. yes, no, or maybe) +to any question. The user, however, can only vote once per question. + +## About the code + +This repository contains the code for Semaphore's contracts written in +Soliidty, and zk-SNARK circuits written in +[circom](https://github.com/iden3/circom). It also contains Typescript code to +execute tests. + +The code has been audited by ABDK Consulting. Their suggested security and +efficiency fixes have been applied. + +A multi-party computation to produce the zk-SNARK proving and verification keys +for Semaphore will begin in the near future. diff --git a/apps/docs/versioned_docs/version-V2/credits.md b/apps/docs/versioned_docs/version-V2/credits.md new file mode 100644 index 00000000..44c69697 --- /dev/null +++ b/apps/docs/versioned_docs/version-V2/credits.md @@ -0,0 +1,13 @@ +--- +sidebar_position: 9 +--- + +# Credits + +Semaphore V2 is the work of several people, for a complete list of contributors you can visit our [Github pages](https://github.com/semaphore-protocol/semaphore/graphs/contributors). + +- [Barry WhiteHat](https://github.com/barryWhiteHat) +- [Kobi Gurkan](https://github.com/kobigurk) +- [Koh Wei Jie](https://github.com/weijiekoh) +- [Andrija Novakovic](https://github.com/akinovak) +- [Cedoor](https://github.com/cedoor) diff --git a/apps/docs/versioned_docs/version-V2/deployed-contracts.md b/apps/docs/versioned_docs/version-V2/deployed-contracts.md new file mode 100644 index 00000000..628c82dc --- /dev/null +++ b/apps/docs/versioned_docs/version-V2/deployed-contracts.md @@ -0,0 +1,35 @@ +--- +sidebar_position: 5 +--- + +# Deployed contracts + +## Verifiers + +| Contract | Goerli | Arbitrum One | +| -------------- | ----------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | +| Verifier16.sol | [0xA525...6629](https://goerli.etherscan.io/address/0xA5253ba39381Aa99c4C2C5A4D5C2deC036d06629) | [0x6143...1609](https://arbiscan.io/address/0x6143ECd9Fd1A00EDe1046d456f8aab53a7D71609) | +| Verifier17.sol | [0xe041...01Ab](https://goerli.etherscan.io/address/0xe0418A5f8fBF051D6cbc41Ff29855Dd2a02201Ab) | [0xAc12...Fb02](https://arbiscan.io/address/0xAc12fFFE354D6446eb50dd33E683B78FED73Fb02) | +| Verifier18.sol | [0x7CdB...6797](https://goerli.etherscan.io/address/0x7CdB3336d7d7c55Bce0FB1508594C54521656797) | [0x610a...C701](https://arbiscan.io/address/0x610aeF0F2da3CD1C8bDefe4BDB434Ee146E0C701) | +| Verifier19.sol | [0xbd87...190B](https://goerli.etherscan.io/address/0xbd870921d8A5398a3314C950d1fc63b8C3AB190B) | [0x5477...FCb4](https://arbiscan.io/address/0x5477725177035bbC9d70443eb921D29749D6FCb4) | +| Verifier20.sol | [0x2a96...E099](https://goerli.etherscan.io/address/0x2a96c5696F85e3d2aa918496806B5c5a4D93E099) | [0x3fB2...3136](https://arbiscan.io/address/0x3fB2C0988a37b76e760c44e6516aF720935f3136) | +| Verifier21.sol | [0x5Ec7...67Cd](https://goerli.etherscan.io/address/0x5Ec7d851a52A2a25CEc528F42a7ACA8EcF4667Cd) | [0xDc8f...7DF8](https://arbiscan.io/address/0xDc8f6B8A42836d4566256f4c6C53131DFD127DF8) | +| Verifier22.sol | [0x919d...aA5b](https://goerli.etherscan.io/address/0x919d3d9c05FA7411e334deA5a763354fC7B6aA5b) | [0x6962...32f2](https://arbiscan.io/address/0x6962b5e706be5278eeCb01c286b50A48484632f2) | +| Verifier23.sol | [0x6391...E552](https://goerli.etherscan.io/address/0x63917b00a6dA7865bEfdd107AfC83CC2e6BDE552) | [0x41e4...dF6B](https://arbiscan.io/address/0x41e4796Bd89B4BF04013b559c93fC32E9a2BdF6B) | +| Verifier24.sol | [0xd05C...0E7D](https://goerli.etherscan.io/address/0xd05CAd7d940114c1419098EE3cEA0776ab510E7D) | [0xD528...5b60](https://arbiscan.io/address/0xD528B1D1408ab3583af4694F92b0aFEbE33d5b60) | +| Verifier25.sol | [0x6D98...5ACb](https://goerli.etherscan.io/address/0x6D9862e6140D94E932d94c8BcE74a0BDD0ea5ACb) | [0x1683...33e7](https://arbiscan.io/address/0x1683a27EF9c10c5286dB56412E1272cD0Ca733e7) | +| Verifier26.sol | [0x8c29...6c77](https://goerli.etherscan.io/address/0x8c29e0b77e32f704F03eeCE01c041192A5EB6c77) | [0x7819...4C00](https://arbiscan.io/address/0x78194bB665d1E33b97eE45B1A755c15717E94C00) | +| Verifier27.sol | [0x066c...9c4C](https://goerli.etherscan.io/address/0x066cC22f8CA2A8D90D7Ff77D8a10A27e629c9c4C) | [0x997D...6E42](https://arbiscan.io/address/0x997Dac00E6701Ef7F3518280E5a9922801126E42) | +| Verifier28.sol | [0x698F...6D15](https://goerli.etherscan.io/address/0x698F9507f504E2BD238be7da56E8D9fee60C6D15) | [0xDd3C...68c6](https://arbiscan.io/address/0xDd3C7f4cBA2467aE41c0F614A3c3E24bC80268c6) | +| Verifier29.sol | [0xbBfC...6346](https://goerli.etherscan.io/address/0xbBfC2E201C3c3c6F50063c3Edb4746c6Fcb36346) | [0xe53e...60Cd](https://arbiscan.io/address/0xe53eF12093933D5df5691EAbA3821bD1c1EB60Cd) | +| Verifier30.sol | [0x06bc...4bD1](https://goerli.etherscan.io/address/0x06bcD633988c1CE7Bd134DbE2C12119b6f3E4bD1) | [0x7FeA...340e](https://arbiscan.io/address/0x7FeA07c536ABBB0E7FB3c833376EE4EaDc21340e) | +| Verifier31.sol | [0x133b...cCFE](https://goerli.etherscan.io/address/0x133b69Ce47BF20C49368354914DF47519Ca6cCFE) | [0xe453...2178](https://arbiscan.io/address/0xe4539a592df18936202480FBe77E47DE012F2178) | +| Verifier32.sol | [0xe297...2e2D](https://goerli.etherscan.io/address/0xe2978F79cb4AF62e5C990EE5c7E12fb22ee22e2D) | [0x98c9...32F4](https://arbiscan.io/address/0x98c90845A7870e215cBd7265DDC653E6c07032F4) | + +## Semaphore + +| Contract | Goerli | Arbitrum One | +| ------------------------- | ----------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | +| Semaphore.sol | [0x5259...262f](https://goerli.etherscan.io/address/0x5259d32659F1806ccAfcE593ED5a89eBAb85262f) | [0x8633...A604](https://arbiscan.io/address/0x86337c87A56117f8264bbaBA70e5a522C6E8A604) | +| IncrementalBinaryTree.sol | [0x61AE...B236](https://goerli.etherscan.io/address/0x61AE89E372492e53D941DECaaC9821649fa9B236) | [0x91cD...3948](https://arbiscan.io/address/0x91cD2B8573629d00BeC72EA1188d446897BD3948) | +| PoseidonT3.sol | [0xe0A4...350F](https://goerli.etherscan.io/address/0xe0A452533853310C371b50Bd91BB9DCC8961350F) | [0xe0c8...61d0](https://arbiscan.io/address/0xe0c8d1e53D9Bfc9071F6564755FCFf6cC0dB61d0) | diff --git a/apps/docs/versioned_docs/version-V2/glossary.md b/apps/docs/versioned_docs/version-V2/glossary.md new file mode 100644 index 00000000..235fa2b0 --- /dev/null +++ b/apps/docs/versioned_docs/version-V2/glossary.md @@ -0,0 +1,64 @@ +--- +sidebar_position: 7 +--- + +# Glossary + +## Semaphore identity + +The identity of a user in the Semaphore protocol. +An identity contains the following three values: + +- [Identity commitment](#identity-commitment): the public value. +- Identity trapdoor and identity nullifier: secret values known only by the user. + +## Semaphore group + +A group is a binary incremental [Merkle tree](#merkle-tree) in which each leaf contains an [identity commitment](#identity-commitment) for a user. +The identity commitment proves that the user is a group member without revealing the Semaphore identity of the user. + +Semaphore uses the **Poseidon** hash function to create Merkle trees. +For more information, see the [Poseidon website](https://www.poseidon-hash.info/). + +## Identity commitment + +The public [Semaphore identity](#semaphore-identity) value used in [Semaphore groups](#semaphore-group). + +Semaphore uses the **Poseidon** hash function to create the identity commitment from the Semaphore identity secret values. +For more information, see the [Poseidon website](https://www.poseidon-hash.info/). + +## Merkle tree + +A tree in which every leaf (i.e., a node that doesn't have children) is labelled with the cryptographic hash of a data block, +and every node that isn't a leaf is labelled with the cryptographic hash of its child node labels. +In zero-knowledge protocols, Merkle trees can be used to efficiently summarize and validate large data sets. +To validate that a tree contains a specific leaf, a verifier only needs a portion of the complete data structure. + +For more information, see [Merkle tree in Wikipedia](https://en.wikipedia.org/wiki/Merkle_tree). + +## Nullifier + +A value used to prevent double entry or double signalling. + +See [Circuit nullifier hash](/docs/technical-reference/circuits/#nullifier-hash). + +## Relay + +A third-party who receives a fee for including relayed transactions in the blockchain (McMenamin, Daza, and Fitz. https://eprint.iacr.org/2022/155.pdf, p.3). +To preserve the anonymity of the user broadcasting a signal with Semaphore, an application may use a relayer to post the signal transaction to Ethereum on behalf of the user. + +Applications may provide rewards for relayers and implement front-running prevention mechanisms, such as requiring the signals to include the relayer’s address, binding the +signal to that specific address (https://semaphore.pse.dev/whitepaper-v1.pdf, p.6). + +## Trusted setup files + +The secure, verifiable parameters generated by Semaphore's trusted setup ceremony. +Semaphore uses the trusted setup files to generate and verify valid zero-knowledge proofs. +To generate or verify valid zero-knowledge proofs with Semaphore, applications must include the following Semaphore _trusted setup_ files: + +- semaphore.zkey +- semaphore.wasm +- semaphore.json + +For a complete list of ready-to-use files, see . +To learn more, see the [trusted setup ceremony](https://storage.googleapis.com/trustedsetup-a86f4.appspot.com/semaphore/semaphore_top_index.html). diff --git a/apps/docs/versioned_docs/version-V2/guides/_category_.json b/apps/docs/versioned_docs/version-V2/guides/_category_.json new file mode 100644 index 00000000..2a9d3fd8 --- /dev/null +++ b/apps/docs/versioned_docs/version-V2/guides/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Guides", + "position": 2 +} diff --git a/apps/docs/versioned_docs/version-V2/guides/groups.md b/apps/docs/versioned_docs/version-V2/guides/groups.md new file mode 100644 index 00000000..546416ec --- /dev/null +++ b/apps/docs/versioned_docs/version-V2/guides/groups.md @@ -0,0 +1,122 @@ +--- +sidebar_position: 2 +title: Groups +--- + +# Semaphore groups + + + +Use Semaphore in your application or smart contract to create off-chain and on-chain groups. + +A [Semaphore group](/docs/glossary/#semaphore-group) contains [identity commitments](/docs/glossary/#identity-commitment) of group members. +Example uses of groups include the following: + +- Poll question that attendees join to rate an event. +- Ballot that members join to vote on a proposal. +- Whistleblowers who are verified employees of an organization. + +A Semaphore group is an [incremental Merkle tree](/docs/glossary/#incremental-merkle-tree), and group members (i.e., [identity commitments](/docs/glossary/#identity-commitments)) are tree leaves. +Semaphore groups set the following two parameters: + +- **Tree depth**: the maximum number of members a group can contain (`max size = 2 ^ tree depth`). +- **Zero value**: the value used to calculate the zero nodes of the incremental Merkle tree. + +Learn how to work with groups. + +- [**Off-chain groups**](#off-chain-groups) +- [**On-chain groups**](#on-chain-groups) + +## Off-chain groups + +- [Create a group](#create-a-group) +- [Add members](#add-members) +- [Remove or update members](#remove-or-update-members) + +### Create a group + +Use the [`@semaphore-protocol/group`](https://github.com/semaphore-protocol/semaphore/tree/v2.6.1/packages/group) library `Group` class to create an off-chain group. + +#### Options + +- **Tree depth**: (_default `20`_) the maximum number of members a group can contain (`max size = 2 ^ tree depth`). +- **Zero value**: (_default `BigInt(0)`_) the value for a tree node that doesn't have a member assigned. + +To create a group with default _`treeDepth`_ and _`zeroValue`_, call the `Group` constructor without parameters--for example: + +```ts +import { Group } from "@semaphore-protocol/group" + +// Default parameters: treeDepth = 20, zeroValue = BigInt(0). +const group = new Group() +``` + +The following example code passes _`treeDepth`_ to create a group for `2 ^ 30 = 1073741824` members: + +```ts +import { Group } from "@semaphore-protocol/group" + +const group = new Group(30) +``` + +The following example code creates a group with a _`zeroValue`_ of `BigInt(1)`: + +```ts +import { Group } from "@semaphore-protocol/group" + +const group = new Group(20, BigInt(1)) +``` + +### Add members + +Use the `Group addMember` function to add a member (identity commitment) to a group--for example: + +```ts +group.addMember(identityCommitment) +``` + +To add a batch of members to a group, pass an array to the `Group addMembers` function--for example: + +```ts +group.addMembers([identityCommitment1, identityCommitment2]) +``` + +### Remove or update members + +To remove members from a group, pass the member index to the `Group removeMember` function--for example: + +```ts +group.removeMember(0) +``` + +To update members in a group, pass the member index and the new value to the `Group updateMember` function--for example: + +```ts +group.updateMember(0, 2) +``` + +:::caution +Removing a member from a group sets the node value to `zeroValue`. +Given that the node isn't removed, and the length of the `group.members` array doesn't change. +::: + +## On-chain groups + +The [`SemaphoreGroups`](https://github.com/semaphore-protocol/semaphore/tree/v2.6.1/packages/contracts/contracts/base/SemaphoreGroups.sol) contract uses the [`IncrementalBinaryTree`](https://github.com/privacy-scaling-explorations/zk-kit/blob/main/packages/incremental-merkle-tree.sol/contracts/IncrementalBinaryTree.sol) library and provides methods to create and manage groups. + +:::info +You can import `SemaphoreGroups` and other Semaphore contracts from the [`@semaphore-protocol/contracts`](https://github.com/semaphore-protocol/semaphore/tree/v2.6.1/packages/contracts) NPM module. +::: + +Alternatively, you can use an already deployed [`Semaphore`](https://github.com/semaphore-protocol/semaphore/tree/v2.6.1/packages/contracts/contracts/Semaphore.sol) contract and use its group external functions. diff --git a/apps/docs/versioned_docs/version-V2/guides/identities.md b/apps/docs/versioned_docs/version-V2/guides/identities.md new file mode 100644 index 00000000..ae3221bd --- /dev/null +++ b/apps/docs/versioned_docs/version-V2/guides/identities.md @@ -0,0 +1,83 @@ +--- +sidebar_position: 1 +title: Identities +--- + +# Semaphore identities + +In order to join a [Semaphore group](/docs/glossary#semaphore-group), a user must first create a [Semaphore identity](/docs/glossary#semaphore-identity). +A Semaphore identity contains two values generated with the identity: + +- Identity trapdoor +- identity nullifier + +To use and verify the identity, the identity owner (user) must know the trapdoor and nullifier values. +To prevent fraud, the owner should keep both values secret. + +## Create identities + +In your code, use the [`@semaphore-protocol/identity`](https://github.com/semaphore-protocol/semaphore/tree/v2.6.1/packages/identity) library to create a Semaphore identity _deterministically_ (from the hash of a message) or _randomly_. + +- [**Create random identities**](#create-random-identities) +- [**Create deterministic identities**](#create-deterministic-identities) + +### Create random identities + +To create a random identity, instantiate `Identity` without any parameters--for example: + +```ts +import { Identity } from "@semaphore-protocol/identity" + +const { trapdoor, nullifier, commitment } = new Identity() +``` + +The new identity contains two random secret values: `trapdoor` and `nullifier`, and one public value: `commitment`. + +The Poseidon hash of the identity nullifier and trapdoor is called the _identity secret_, +and its hash is the _identity commitment_. + +An identity commitment, similarly to Ethereum addresses, is a public value used +in Semaphore groups to represent the identity of a group member. The secret values are similar to +Ethereum private keys and are used to generate Semaphore zero-knowledge proofs and authenticate signals. + +### Create deterministic identities + +If you pass a message as a parameter, Semaphore generates `trapdoor` and `nullifier` +from the _SHA256_ hash of the message. +The message might be a password or a message that the user cryptographically signs with a private key. + +When using deterministic identities, you should always keep the message secret. +Given that the hash is deterministic, anyone with the same message can recreate the same identity. + +```ts +const identity = new Identity("secret-message") +``` + +:::tip +Building a system to save or recover secret values of Semaphore identities is nontrivial. +You may choose to delegate such functionality to existing wallets such as Metamask--for example: + +1. In Metamask, a user signs a message with the private key of their Ethereum account. +2. In your application, the user creates a deterministic identity with the signed message. +3. The user can now recreate their Semaphore identity whenever they want by signing the same message with their Ethereum account in Metamask. + +::: + +## Save your identities + +You can output an identity as a JSON string that you can save and reuse later. +The `Identity.toString()` method generates a JSON array from an identity--for example: + +```ts +console.log(identity.toString()) // View the identity trapdoor and nullifier. + +// '["8255d...", "62c41..."]' +``` + +The array contains the trapdoor and nullifier. + +To reuse the saved identity, pass the JSON to the `Identity()` constructor. + +```ts +const identity2 = new Identity(identity.toString()) +``` diff --git a/apps/docs/versioned_docs/version-V2/guides/proofs.md b/apps/docs/versioned_docs/version-V2/guides/proofs.md new file mode 100644 index 00000000..967e25ce --- /dev/null +++ b/apps/docs/versioned_docs/version-V2/guides/proofs.md @@ -0,0 +1,110 @@ +--- +sidebar_position: 3 +title: Proofs +--- + +# Semaphore proofs + +Learn how to use Semaphore to generate and verify zero-knowledge proofs. + +Once a user joins their [Semaphore identity](/docs/glossary#semaphore-identity) to a [Semaphore group](/docs/glossary#semaphore-group), the user can signal anonymously with a zero-knowledge proof that proves the following: + +- The user is a member of the group. +- The same user created the signal and the proof. + +Developers can use Semaphore for the following: + +- [**Generate a proof off-chain**](#generate-a-proof-off-chain) +- [**Verify a proof off-chain**](#verify-a-proof-off-chain) +- [**Verify a proof on-chain**](#verify-a-proof-on-chain) + +## Generate a proof off-chain + +Use the [`@semaphore-protocol/proof`](https://github.com/semaphore-protocol/semaphore/tree/v2.6.1/packages/proof) library to generate an off-chain proof. +To generate a proof, pass the following properties to the `generateProof` function: + +- `identity`: The Semaphore identity of the user broadcasting the signal and generating the proof. +- `group`: The group to which the user belongs. +- `externalNullifier`: The value that prevents double-signaling. +- `signal`: The signal the user wants to send anonymously. +- `snarkArtifacts`: The `zkey` and `wasm` [trusted setup files](/docs/glossary/#trusted-setup-files). + +In the voting system use case, once all the voters have joined their [identities](/docs/guides/identities#create-an-identity) to the ballot [group](/docs/guides/groups), +a voter can generate a proof to vote for a proposal. +In the call to `generateProof`, the voting system passes the unique ballot ID (the [Merkle tree](/docs/glossary/#merkle-tree/) root of the group) as the +`externalNullifier` to prevent the voter signaling more than once for the ballot. +The following code sample shows how to use `generateProof` to generate the voting proof: + +```ts +import { generateProof } from "@semaphore-protocol/proof" + +const externalNullifier = group.root +const signal = "proposal_1" + +const fullProof = await generateProof(identity, group, externalNullifier, signal, { + zkeyFilePath: "./semaphore.zkey", + wasmFilePath: "./semaphore.wasm" +}) +``` + +## Verify a proof off-chain + +Use the [`@semaphore-protocol/proof`](https://github.com/semaphore-protocol/semaphore/tree/v2.6.1/packages/proof) library to verify a Semaphore proof off-chain. +To verify a proof, pass the following to the `verifyProof` function: + +- _`proof`_: the Semaphore proof. +- _`verificationKey`_: the JavaScript object in the `semaphore.json` [trusted setup file](/docs/glossary/#trusted-setup-files). + +The following code sample shows how to parse the verification key object from `semaphore.json` +and verify the previously generated proof: + +```ts +import { verifyProof } from "@semaphore-protocol/proof" + +const verificationKey = JSON.parse(fs.readFileSync("./semaphore.json", "utf-8")) + +await verifyProof(verificationKey, fullProof) // true or false. +``` + +`verifyProof` returns a Promise that resolves to `true` or `false`. + +## Verify a proof on-chain + +Use the [`SemaphoreCore`](https://github.com/semaphore-protocol/semaphore/tree/v2.6.1/packages/contracts/contracts/base/SemaphoreCore.sol) contract to verify proofs on-chain. It uses a verifier deployed to Ethereum and provides methods hash the signal and verify a proof. + +:::info +You can import `SemaphoreCore` and other Semaphore contracts from the [`@semaphore-protocol/contracts`](https://github.com/semaphore-protocol/semaphore/tree/v2.6.1/packages/contracts) NPM module. +::: + +To verify Semaphore proofs in your contract, import `SemaphoreCore` and pass the following to the `_verifyProof` internal method: + +- _`signal`_: The Semaphore signal to prove. +- _`root`_: The root of the Merkle tree. +- _`nullifierHash`_: a [nullifier hash](#retrieve-a-nullifier-hash). +- _`externalNullifier`_: The external nullifier. +- _`proof`_: A [_Solidity-compatible_ Semaphore proof](#generate-a-solidity-compatible-proof). +- _`verifier`_: The verifier address. + +Remember to save the `nullifierHash` on-chain to avoid double-signaling. + +Alternatively, you can use an already deployed [`Semaphore`](https://github.com/semaphore-protocol/semaphore/blob/v2.6.1/packages/contracts/contracts/Semaphore.sol) contract and use its `verifyProof` external function. + +### Generate a Solidity-compatible proof + +To transform a proof to be compatible with Solidity contracts, pass the proof to the `packToSolidityProof` utility function--for example: + +```ts +import { packToSolidityProof } from "@semaphore-protocol/proof" + +const solidityProof = packToSolidityProof(fullProof.proof) +``` + +Semaphore returns a new Solidity-compatible instance of the proof. + +### Retrieve a nullifier hash + +To get the Semaphore proof nullifier hash, access the proof's `publicSignals.nullifierHash` property--for example: + +```ts +const { nullifierHash } = fullProof.publicSignals +``` diff --git a/apps/docs/versioned_docs/version-V2/quick-setup.md b/apps/docs/versioned_docs/version-V2/quick-setup.md new file mode 100644 index 00000000..ac8cc2be --- /dev/null +++ b/apps/docs/versioned_docs/version-V2/quick-setup.md @@ -0,0 +1,304 @@ +--- +sidebar_position: 2 +--- + +# Quick setup + +Set up a new Hardhat project with Semaphore. +Learn how to create and test an Ethereum smart contract that uses zero-knowledge +proofs to verify membership. + +To check out the code used in this guide, visit the +[quick-setup](https://github.com/semaphore-protocol/quick-setup) repository. + +1. [**Create a Node.js project**](#create-a-nodejs-project) +2. [**Install Hardhat**](#install-hardhat) +3. [**Install Semaphore packages**](#install-semaphore-packages) +4. [**Create the Semaphore contract**](#create-the-semaphore-contract) +5. [**Create a Hardhat task**](#create-a-hardhat-task) +6. [**Test your contracts**](#test-your-contract) +7. [**Deploy your contract**](#deploy-your-contract) + +## Create a Node.js project + +1. Follow the [Node.js _LTS version_](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) + instructions to install `node` (Hardhat may not work with Node.js _Current_). + +2. Follow the [Yarn](https://yarnpkg.com/getting-started/install) instructions + to download and install the `yarn` package manager. + +3. Create a directory for the project and change to the new directory. + + ```bash + mkdir semaphore-example + cd semaphore-example + ``` + +4. In your terminal, run `yarn init` to initialize the Node.js project. + +## Install Hardhat + +[Hardhat](https://hardhat.org/) is a development environment you can use to +compile, deploy, test, and debug Ethereum software. +Hardhat includes the Hardhat Network, a local Ethereum network for development. + +1. Use `yarn` to install [Hardhat](https://hardhat.org/getting-started/): + + ```bash + yarn add hardhat --dev + ``` + +2. Use `yarn` to run `hardhat` and create a JavaScript project: + + ```bash + yarn hardhat + # At the prompt, select "Create a JavaScript project" + # and then enter through the prompts. + ``` + +## Install Semaphore packages + +Semaphore provides contracts, JavaScript libraries and a Hardhat plugin for developers building zero-knowledge applications. + +- `@semaphore-protocol/contracts` provides contracts to manage groups and verify Semaphore proofs on-chain. +- JavaScript libraries help developers build zero-knowledge applications. +- `@semaphore-protocol/hardhat` allows developers Hardhat tasks to deploy verifiers and Semaphore contracts. + +To install these dependencies for your project, do the following: + +1. Use `yarn` to install `@semaphore-protocol/contracts`: + + ```bash + yarn add @semaphore-protocol/contracts@2.6.1 + ``` + +2. Use `yarn` to install the Semaphore JavaScript libraries and the Hardhat plugin: + + ```bash + yarn add @semaphore-protocol/identity@2.6.1 @semaphore-protocol/group@2.6.1 @semaphore-protocol/proof@2.6.1 @semaphore-protocol/hardhat@0.1.0 --dev + ``` + +For more detail about _Semaphore contracts_, see [Contracts](https://semaphore.pse.dev/docs/technical-reference/contracts). +To view the source of our packages, see the [semaphore](https://github.com/semaphore-protocol/semaphore/tree/v2.6.1#-packages) repository. + +## Create the Semaphore contract + +Create a `Greeter` contract that uses the `Semaphore.sol` contract: + +1. Rename `Lock.sol` to `Greeter.sol` and replace the content with the following: + + ```solidity title="./contracts/Greeter.sol" + //SPDX-License-Identifier: MIT + pragma solidity ^0.8.0; + + import "@semaphore-protocol/contracts/interfaces/ISemaphore.sol"; + + /// @title Greeter contract. + /// @dev The following code is just an example to show how Semaphore can be used. + contract Greeter { + event NewGreeting(bytes32 greeting); + event NewUser(uint256 identityCommitment, bytes32 username); + + ISemaphore public semaphore; + + uint256 groupId; + mapping(uint256 => bytes32) users; + + constructor(address semaphoreAddress, uint256 _groupId) { + semaphore = ISemaphore(semaphoreAddress); + groupId = _groupId; + + semaphore.createGroup(groupId, 20, 0, address(this)); + } + + function joinGroup(uint256 identityCommitment, bytes32 username) external { + semaphore.addMember(groupId, identityCommitment); + + users[identityCommitment] = username; + + emit NewUser(identityCommitment, username); + } + + function greet( + bytes32 greeting, + uint256 merkleTreeRoot, + uint256 nullifierHash, + uint256[8] calldata proof + ) external { + semaphore.verifyProof(groupId, merkleTreeRoot, greeting, nullifierHash, groupId, proof); + + emit NewGreeting(greeting); + } + } + ``` + +## Create a Hardhat task + +Hardhat lets you write [tasks](https://hardhat.org/guides/create-task.html#creating-a-task) +that automate building and deploying smart contracts and dApps. +To create a task that deploys the `Greeter` contract, do the following: + +1. Create a `tasks` folder and add a `./tasks/deploy.js` file that contains the following: + + ```javascript title="./tasks/deploy.js" + const { task, types } = require("hardhat/config") + + task("deploy", "Deploy a Greeter contract") + .addOptionalParam("semaphore", "Semaphore contract address", undefined, types.address) + .addParam("group", "Group identifier", 42, types.int) + .addOptionalParam("logs", "Print the logs", true, types.boolean) + .setAction(async ({ logs, semaphore: semaphoreAddress, group: groupId }, { ethers, run }) => { + if (!semaphoreAddress) { + const { address: verifierAddress } = await run("deploy:verifier", { logs, merkleTreeDepth: 20 }) + + const { address } = await run("deploy:semaphore", { + logs, + verifiers: [ + { + merkleTreeDepth: 20, + contractAddress: verifierAddress + } + ] + }) + + semaphoreAddress = address + } + + const Greeter = await ethers.getContractFactory("Greeter") + + const greeter = await Greeter.deploy(semaphoreAddress, groupId) + + await greeter.deployed() + + if (logs) { + console.log(`Greeter contract has been deployed to: ${greeter.address}`) + } + + return greeter + }) + ``` + +2. In your `hardhat.config.js` file, add the following: + + ```javascript title="./hardhat.config.js" + require("@nomiclabs/hardhat-waffle") + require("@semaphore-protocol/hardhat") + require("./tasks/deploy") // Your deploy task. + + module.exports = { + solidity: "0.8.4" + } + ``` + +## Test your contract + +[`hardhat-waffle`](https://hardhat.org/plugins/nomiclabs-hardhat-waffle.html) +lets you write tests with the [Waffle](https://getwaffle.io/) test framework +and [Chai assertions](https://www.chaijs.com/). + +1. Use `yarn` to install the `hardhat-waffle` plugin and dependencies for smart + contract tests: + + ```bash + yarn add -D @nomiclabs/hardhat-waffle 'ethereum-waffle@^3.0.0' \ + @nomiclabs/hardhat-ethers 'ethers@^5.0.0' chai + ``` + +2. Download the Semaphore [zk trusted setup files](http://www.trusted-setup-pse.org/) + and copy them to the `./static` folder. + + ```bash + cd static + wget http://www.trusted-setup-pse.org/semaphore/20/semaphore.zkey + wget http://www.trusted-setup-pse.org/semaphore/20/semaphore.wasm + ``` + + Learn more about [trusted setup files](/docs/glossary/#trusted-setup-files). + +3. Rename the `Lock.js` test file to `Greeter.js` and replace the content with the following: + + ```javascript title="./test/Greeter.js" + const { Identity } = require("@semaphore-protocol/identity") + const { Group } = require("@semaphore-protocol/group") + const { generateProof, packToSolidityProof, verifyProof } = require("@semaphore-protocol/proof") + const { expect } = require("chai") + const { run, ethers } = require("hardhat") + + describe("Greeter", function () { + let greeter + + const users = [] + const groupId = 42 + const group = new Group() + + before(async () => { + greeter = await run("deploy", { logs: false, group: groupId }) + + users.push({ + identity: new Identity(), + username: ethers.utils.formatBytes32String("anon1") + }) + + users.push({ + identity: new Identity(), + username: ethers.utils.formatBytes32String("anon2") + }) + + group.addMember(users[0].identity.generateCommitment()) + group.addMember(users[1].identity.generateCommitment()) + }) + + describe("# joinGroup", () => { + it("Should allow users to join the group", async () => { + for (let i = 0; i < group.members.length; i++) { + const transaction = greeter.joinGroup(group.members[i], users[i].username) + + await expect(transaction).to.emit(greeter, "NewUser").withArgs(group.members[i], users[i].username) + } + }) + }) + + describe("# greet", () => { + const wasmFilePath = "./static/semaphore.wasm" + const zkeyFilePath = "./static/semaphore.zkey" + + it("Should allow users to greet", async () => { + const greeting = ethers.utils.formatBytes32String("Hello World") + + const fullProof = await generateProof(users[1].identity, group, groupId, greeting, { + wasmFilePath, + zkeyFilePath + }) + const solidityProof = packToSolidityProof(fullProof.proof) + + const transaction = greeter.greet( + greeting, + fullProof.publicSignals.merkleRoot, + fullProof.publicSignals.nullifierHash, + solidityProof + ) + + await expect(transaction).to.emit(greeter, "NewGreeting").withArgs(greeting) + }) + }) + }) + ``` + +4. Run the following `yarn` commands to compile and test your contract: + + ```bash + yarn hardhat compile + yarn hardhat test + ``` + +## Deploy your contract + +To deploy your contract in a local Hardhat network (and use it in your dApp), run the following `yarn` commands: + +```bash +yarn hardhat node +yarn hardhat deploy --group 42 --network localhost # In another tab. +``` + +For a more complete demo that provides a starting point for your dApp, +see [semaphore-boilerplate](https://github.com/semaphore-protocol/boilerplate/). diff --git a/apps/docs/versioned_docs/version-V2/resources.md b/apps/docs/versioned_docs/version-V2/resources.md new file mode 100644 index 00000000..4cf095b6 --- /dev/null +++ b/apps/docs/versioned_docs/version-V2/resources.md @@ -0,0 +1,19 @@ +--- +sidebar_position: 8 +--- + +# Resources + +[Semaphore V2 is Live!](https://medium.com/privacy-scaling-explorations/semaphore-v2-is-live-f263e9372579) - Privacy and Scaling Explorations + +[To Mixers and Beyond: presenting Semaphore, a privacy gadget built on Ethereum](https://medium.com/coinmonks/to-mixers-and-beyond-presenting-semaphore-a-privacy-gadget-built-on-ethereum-4c8b00857c9b) - Koh Wei Jie + +[Privacy in Ethereum](https://www.youtube.com/watch?v=maDHYyj30kg) - Barry WhiteHat at the Taipei Ethereum Meetup + +[Snarks for mixing, signaling and scaling by](https://www.youtube.com/watch?v=lv6iK9qezBY) - Barry WhiteHat at Devcon 4 + +[Privacy in Ethereum](https://www.youtube.com/watch?v=zBUo7G95wYE) - Barry WhiteHat at Devcon 5 + +[A trustless Ethereum mixer using zero-knowledge signalling](https://www.youtube.com/watch?v=GzVT16lFOHU) - Koh Wei Jie and Barry WhiteHat at Devcon 5 + +[Hands-on Applications of Zero-Knowledge Signalling](https://www.youtube.com/watch?v=7wd2aAN2jXI) - Koh Wei Jie at Devcon 5 diff --git a/apps/docs/versioned_docs/version-V2/subgraph.md b/apps/docs/versioned_docs/version-V2/subgraph.md new file mode 100644 index 00000000..6805aa7e --- /dev/null +++ b/apps/docs/versioned_docs/version-V2/subgraph.md @@ -0,0 +1,51 @@ +--- +sidebar_position: 6 +--- + +# Subgraph + +[The Graph](https://thegraph.com/) is a protocol for indexing networks like Ethereum and IPFS. +Site owners publish _subgraphs_ that expose site data for anyone to query. +Semaphore's subgraph allows you to retrieve data from the [`Semaphore.sol`](https://github.com/semaphore-protocol/semaphore/tree/v2.6.1/packages/contracts/Semaphore.sol) smart contract. + +:::tip +The Graph protocol uses the [GraphQL](https://graphql.org/) query lanaguage. For examples, see the [GraphQL API documentation](https://thegraph.com/docs/developer/graphql-api). Visit the [subgraph repository](https://github.com/semaphore-protocol/subgraph) to see the list of Semaphore subgraphs. +::: + +## Schema + +### MerkleTree + +- `id`: unique identifier among all MerkleTree entities, +- `depth`: Merkle tree depth, +- `root`: Merkle tree root, +- `zeroValue`: Merkle tree zero value, +- `numberOfLeaves`: total number of tree leaves, +- `group`: link to the Group entity. + +### Group + +- `id`: unique identifier among all Group entities, +- `merkleTree`: link to the MerkleTree entity, +- `timestamp`: block timestamp, +- `admin`: admin of the group, +- `members`: list of group members. +- `verifiedProofs`: list of group proofs. + +### Member + +- `id`: unique identifier among all Member entities, +- `identityCommitment`: Semaphore identity commitment, +- `timestamp`: block timestamp, +- `index`: index of the tree leaf, +- `group`: link to the Group entity. + +### VerifiedProof + +- `id`: unique identifier among all VerifiedProof entities, +- `signal`: user's signal, +- `merkleTreeRoot`: Merkle tree root, +- `nullifierHash`: nullifier hash, +- `externalNullifier`: external nullifier, +- `timestamp`: block timestamp, +- `group`: link to the Group entity. diff --git a/apps/docs/versioned_docs/version-V2/technical-reference/_category_.json b/apps/docs/versioned_docs/version-V2/technical-reference/_category_.json new file mode 100644 index 00000000..2187e794 --- /dev/null +++ b/apps/docs/versioned_docs/version-V2/technical-reference/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Technical reference", + "position": 4 +} diff --git a/apps/docs/versioned_docs/version-V2/technical-reference/circuits.md b/apps/docs/versioned_docs/version-V2/technical-reference/circuits.md new file mode 100644 index 00000000..07265883 --- /dev/null +++ b/apps/docs/versioned_docs/version-V2/technical-reference/circuits.md @@ -0,0 +1,55 @@ +--- +sidebar_position: 2 +--- + +# Circuits + +The [Semaphore circuit](https://github.com/semaphore-protocol/semaphore/tree/v2.6.1/packages/circuits) is the heart of the protocol and consists of three parts: + +- [**Proof of membership**](/docs/technical-reference/circuits#proof-of-membership) +- [**Nullifier hash**](/docs/technical-reference/circuits#nullifier-hash) +- [**Signal**](/docs/technical-reference/circuits#signal) + +![Semaphore circuit](https://github.com/semaphore-protocol/semaphore/raw/v2.6.1/packages/circuits/scheme.png) + +The diagram above shows how the input signals are used in the Semaphore circuit and how the outputs are calculated. + +## Proof of membership + +The circuit hashes the hash of the identity nullifier with the identity trapdoor to generate an identity commitment. Then, it verifies the proof of membership against the Merkle root and the identity commitment. + +**Private inputs:** + +- `treeSiblings[nLevels]`: the values along the Merkle path to the user's identity commitment, +- `treePathIndices[nLevels]`: the direction (0/1) per tree level corresponding to the Merkle path to the user's identity commitment, +- `identityNullifier`: the 32-byte identity secret used as nullifier, +- `identityTrapdoor`: the 32-byte identity secret used as trapdoor. + +**Public outputs:** + +- `root`: The Merkle root of the tree. + +## Nullifier hash + +The circuit hashes the identity nullifier with the external nullifier and then checks that the result matches the provided nullifier hash. +Nullifier hashes saved in a Semaphore smart contract allow the contract to reject a proof that contains a used nullifier hash. + +**Private inputs:** + +- `identityNullifier`: the 32-byte identity secret used as a nullifier. + +**Public inputs:** + +- `externalNullifier`: the 32-byte external nullifier. + +**Public outputs:** + +- `nullifierHash`: the hash of the identity nullifier and the external nullifier; used to prevent double-signaling. + +## Signal + +The circuit calculates a dummy square of the signal hash to prevent any tampering with the proof. + +**Public inputs:** + +- `signalHash`: the hash of the user's signal. diff --git a/apps/docs/versioned_docs/version-V2/technical-reference/contracts.md b/apps/docs/versioned_docs/version-V2/technical-reference/contracts.md new file mode 100644 index 00000000..d3caa5de --- /dev/null +++ b/apps/docs/versioned_docs/version-V2/technical-reference/contracts.md @@ -0,0 +1,50 @@ +--- +sidebar_position: 3 +--- + +# Contracts + +Semaphore includes three types of contracts: + +- [**Base contracts**](/docs/technical-reference/contracts#base-contracts) +- [**Extension contracts**](/docs/technical-reference/contracts#extension-contracts) +- [**Verifiers**](/docs/technical-reference/contracts#verifiers) + +:::info +To use Semaphore contracts and interfaces in your project, +install the [`@semaphore-protocol/contracts`](https://github.com/semaphore-protocol/semaphore/tree/v2.6.1/packages/contracts) NPM package. +::: + +## Base contracts + +Semaphore provides the following base contracts: + +- [`SemaphoreCore.sol`](https://github.com/semaphore-protocol/semaphore/blob/v2.6.1/packages/contracts/contracts/base/SemaphoreCore.sol): contains the functions to verify Semaphore proofs; +- [`SemaphoreGroups.sol`](https://github.com/semaphore-protocol/semaphore/blob/v2.6.1/packages/contracts/contracts/base/SemaphoreGroups.sol): contains the functions to create groups and add/remove members. + +These contracts are closely related to the protocol. +You can inherit them in your contract or you can use [`Semaphore.sol`](https://github.com/semaphore-protocol/semaphore/tree/v2.6.1/packages/contracts/contracts/Semaphore.sol), which inherits them for you. +See our [deployed contracts](/docs/deployed-contracts#semaphore) to find the addresses for your network. + +:::info +While some dApps may use on-chain groups, others may prefer to use off-chain groups, saving only their tree roots in the contract. +::: + +## Extension contracts + +- [`SemaphoreVoting.sol`](https://github.com/semaphore-protocol/semaphore/tree/v2.6.1/packages/contracts/contracts/extensions/SemaphoreVoting.sol): voting contract that contains the essential functions to create polls, add voters, and anonymously cast votes. +- [`SemaphoreWhistleblowing.sol`](https://github.com/semaphore-protocol/semaphore/tree/v2.6.1/packages/contracts/contracts/extensions/SemaphoreWhistleblowing.sol): whistleblowing contract that contains the essential functions to create entities (for example: non-profit organizations), add whistleblowers, and anonymously publish leaks. + +These contracts extend the protocol to provide application logic for specific use-cases. +More extensions will be added in the future. + +## Verifiers + +To verify Semaphore proofs, the [`SemaphoreCore.sol`](https://github.com/semaphore-protocol/semaphore/tree/v2.6.1/packages/contracts/contracts/base/SemaphoreCore.sol) contract requires the address of a deployed verifier contract. +You can choose to manually deploy the [verifier](https://github.com/semaphore-protocol/semaphore/tree/v2.6.1/packages/contracts/contracts/verifiers) you prefer or you can use one of our [deployed verifiers](/docs/deployed-contracts#verifiers). + +Each verifier name indicates the tree depth that it can verify. +For example, given a Semaphore proof generated with a tree depth `20`: + +- The `Verifier20.sol` contract can verify the proof. +- The [group](/docs/guides/groups) used for the proof can have a maximum `2^20=1048576` members. diff --git a/apps/docs/versioned_docs/version-V2/use-cases/_category_.json b/apps/docs/versioned_docs/version-V2/use-cases/_category_.json new file mode 100644 index 00000000..25e2a80c --- /dev/null +++ b/apps/docs/versioned_docs/version-V2/use-cases/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Use cases", + "position": 3 +} diff --git a/apps/docs/versioned_docs/version-V2/use-cases/private-voting.md b/apps/docs/versioned_docs/version-V2/use-cases/private-voting.md new file mode 100644 index 00000000..95909aff --- /dev/null +++ b/apps/docs/versioned_docs/version-V2/use-cases/private-voting.md @@ -0,0 +1,137 @@ +--- +sidebar_position: 1 +--- + +# Private voting use case + +The private voting use case describes how Semaphore interacts with your users and Ethereum to allow users to cast private votes in your application. +Learn how Semaphore enables applications to do the following: + +- Register members as voters. +- Allow members to vote anonymously. +- Prove voter membership. +- Record and prove votes. +- Prevent double-voting. + +## Roles + +- **[Developer or community admin](#developer-or-community-admin)** +- **[Community member (dApp user)](#community-member)** +- **[Relay](#relay)** + +### Developer or community admin + +As a developer or community admin, you deploy the following: + +- **Smart contract on Ethereum**: implements the Semaphore **base contract** to create a poll (Semaphore **group** that members join to vote), post transactions, and verify proofs on Ethereum. +- **Decentralized application (dApp)**: your application that provides a user interface (UI) where members join a poll and vote on a proposal. + +### Community member + +Community members connect their wallets to the dApp to take the following actions: + +1. Verify ownership of the community token. +2. Generate an anonymous ID. +3. Cast a vote. + +### Relay + +To preserve anonymity and avoid disclosing the member's wallet address, the dApp may use a [relay](/docs/glossary/#relay) to broadcast the vote. +The relay calls the **contract** function that then posts the member's vote transaction to Ethereum. + +## Private voting + +Consider a scenario where your community issues a token that users can mint. +The token might be a Proof of Attendance (POAP), NFT, or social token that your users can mint to receive membership and vote in your community. + +The voting scenario has the following steps: + +1. [Create a poll](#create-a-poll): Coordinator creates a poll, or _group_, in which members can vote on a proposal. +2. [Register voters](#register-voters): Members join the poll to vote. +3. [Record votes](#record-votes): Once the poll opens, members may cast one vote, or _signal_, on the topic. + +### Create a poll + +A community coordinator or dApp administrator uses the deployed smart contract to create an on-chain (Ethereum) poll, a [Semaphore group](/docs/guides/groups/) that members can join and cast votes to. + +In the following sample code, the voting contract declares a `createPoll` function that uses the Semaphore base `_createGroup` function: + +```ts title="https://github.com/semaphore-protocol/semaphore/contracts/extensions/SemaphoreVoting.sol" + +function createPoll( + uint256 pollId, + address coordinator, + uint8 depth +) public override { + require(address(verifiers[depth]) != address(0), "SemaphoreVoting: depth value is not supported"); + + // highlight-next-line + _createGroup(pollId, depth, 0); + + Poll memory poll; + + poll.coordinator = coordinator; + + polls[pollId] = poll; + + emit PollCreated(pollId, coordinator); +} +``` + +A poll is a Semaphore [group](/docs/guides/groups/) that stores the following: + +- A topic to vote on. +- The public ID of the poll creator. +- [Semaphore IDs](/docs/guides/identities/) of members who joined the poll. + +To create the poll, the administrator calls the smart contract function--for example: + +```ts +SemaphoreVoting.createPoll(pollId, coordinator, depth) +``` + +Next, learn how to [register voters](#register-voters) for the poll. + +### Register voters + +Before a user can register to vote, the dApp needs to verify membership by checking the user's wallet for the NFT. +To grant access to the wallet, the user clicks a `Connect wallet` button in the dApp and allows the dApp to check for the NFT. +Once a member is verified, the dApp provides the following member interactions: + +1. [Generate a private identity](#generate-a-private-identity). +2. [Join a poll](#join-a-poll). + +:::info +To learn how to connect to Ethereum wallets, visit the [ethers.js Getting Started documentation](https://docs.ethers.io/v5/getting-started). +::: + +#### Generate a private identity + +To generate a private identity, the member completes a form in the dApp UI. +With the form values and the `@semaphore-protocol/identity` library, the dApp prompts the member to sign a wallet message and then generates the signed private identity. +The private identity is known only to the member and can be used in future interactions with the dApp. + +Next, learn how members [join a poll](#join-a-poll). + +#### Join a poll + +Once the member has a private identity for the dApp, the member may select a poll to vote in. +When the member selects a poll, the dApp does the following: + +1. Uses the `@semaphore-protocol/identity` library to generate an anonymous Semaphore ID, or _identity commitment_, from the private identity. +2. Calls a contract function that adds the new Semaphore ID to the on-chain poll. + +With a member registered for a poll, learn how the dApp [records votes](#record-votes). + +### Record votes + +Once members have joined a poll, the coordinator starts the poll to allow voting. +When a member votes (for example, by selecting a radio button), then the dApp takes the following actions: + +1. Uses the `@semaphore-protocol/proof` library to create a proof of the vote, the poll identifier, the Semaphore ID, and a [nullifier](/docs/glossary/#nullifier) that prevents double-voting. +2. Sends the vote proof to the [relay](#relay). + +### Related + +- To get started developing with Semaphore, see the [Quick setup](/docs/quick-setup/) guide. +- For an example app that you can use to start your own project, see [Semaphore boilerplate](https://github.com/semaphore-protocol/boilerplate). diff --git a/apps/docs/versioned_docs/version-V2/what-is-semaphore.md b/apps/docs/versioned_docs/version-V2/what-is-semaphore.md new file mode 100644 index 00000000..9674090c --- /dev/null +++ b/apps/docs/versioned_docs/version-V2/what-is-semaphore.md @@ -0,0 +1,49 @@ +--- +id: introduction +title: What Is Semaphore? +sidebar_position: 1 +--- + +## Overview + +[Semaphore](https://github.com/semaphore-protocol/semaphore) is a [zero-knowledge](https://z.cash/technology/zksnarks) protocol that allows you to cast a signal (for example, a vote or endorsement) as a provable group member without revealing your identity. +Additionally, it provides a simple mechanism to prevent double-signaling. +Use cases include private voting, whistleblowing, anonymous DAOs and mixers. + +## Features + +With Semaphore, you can allow your users to do the following: + +1. [Create a Semaphore identity](/docs/guides/identities/). +2. [Add their Semaphore identity to a group (i.e. _Merkle tree_)](/docs/guides/groups/). +3. [Send a verifiable, anonymous signal (e.g a vote or endorsement)](/docs/guides/proofs/). + +When a user broadcasts a signal (for example: a vote), Semaphore zero-knowledge +proofs can ensure that the user has joined the group and hasn't already cast a signal with their nullifier. + +Semaphore uses on-chain Solidity contracts and off-chain JavaScript libraries that work in tandem. + +- Off chain, JavaScript libraries can be used to create identities, manage groups and generate proofs. +- On chain, Solidity contracts can be used to manage groups and verify proofs. + +## Developer benefits + +Semaphore is designed to be a simple and generic _privacy layer_ for decentralized applications (dApps) on Ethereum. It encourages modular application design, allowing dApp developers to choose and customize the on-chain and off-chain components they need. + +## About the code + +The core of the protocol is the [circuit logic](https://github.com/semaphore-protocol/semaphore/tree/v2.6.1/packages/circuits/scheme.png). +In addition to circuits, +Semaphore provides [Solidity contracts](https://github.com/semaphore-protocol/semaphore/tree/v2.6.1/packages/contracts) +and [JavaScript libraries](https://github.com/semaphore-protocol/semaphore/tree/v2.6.1#-packages) that allow developers to generate zero-knowledge proofs and verify them with minimal effort. + +### Audits + +| Version | Report | Scope | +| ------- | -------------------------------------------------------------------------------------------------------------------- | ------------------------ | +| v2.0.0 | [Semaphore_2.0.0_Audit.pdf](https://github.com/semaphore-protocol/semaphore/files/9850441/Semaphore_2.0.0_Audit.pdf) | `circuits`, `contracts` | +| v2.5.0 | [Semaphore_2.5.0_Audit.pdf](https://github.com/semaphore-protocol/semaphore/files/9845008/Semaphore_2.5.0_Audit.pdf) | `contracts`, `libraries` | + +:::info +If you are using the previous version of Semaphore, see the [Semaphore V1](/docs/V1/introduction) documentation. +::: diff --git a/apps/docs/versioned_docs/version-V3/credits.md b/apps/docs/versioned_docs/version-V3/credits.md new file mode 100644 index 00000000..66a881d4 --- /dev/null +++ b/apps/docs/versioned_docs/version-V3/credits.md @@ -0,0 +1,17 @@ +--- +sidebar_position: 10 +--- + +# Credits + +Semaphore is the work of several people, for a complete list of contributors you can visit our [Github pages](https://github.com/semaphore-protocol/semaphore/graphs/contributors). + +- [Barry WhiteHat](https://github.com/barryWhiteHat) +- [Kobi Gurkan](https://github.com/kobigurk) +- [Koh Wei Jie](https://github.com/weijiekoh) +- [Andrija Novakovic](https://github.com/akinovak) +- [Cedoor](https://github.com/cedoor) +- [Rachel Aux](https://github.com/rachelaux) +- [Andy Guzman](https://github.com/aguzmant103) +- [Vivian Plasencia](https://github.com/vplasencia) +- [LauNaMu](https://github.com/0xyNaMu) diff --git a/apps/docs/versioned_docs/version-V3/deployed-contracts.md b/apps/docs/versioned_docs/version-V3/deployed-contracts.md new file mode 100644 index 00000000..8205d19e --- /dev/null +++ b/apps/docs/versioned_docs/version-V3/deployed-contracts.md @@ -0,0 +1,21 @@ +--- +sidebar_position: 5 +--- + +# Deployed contracts + +| Contract | Goerli\* | Sepolia | Mumbai | Optimism Goerli\* | Arbitrum Goerli\* | Arbitrum One | +| ------------------------- | ----------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | +| Semaphore.sol | [0x3889...6131](https://goerli.etherscan.io/address/0x3889927F0B5Eb1a02C6E2C20b39a1Bd4EAd76131) | [0x3889...6131](https://sepolia.etherscan.io/address/0x3889927F0B5Eb1a02C6E2C20b39a1Bd4EAd76131) | [0x3889...6131](https://mumbai.polygonscan.com/address/0x3889927F0B5Eb1a02C6E2C20b39a1Bd4EAd76131) | [0x3889...6131](https://goerli-optimism.etherscan.io/address/0x3889927F0B5Eb1a02C6E2C20b39a1Bd4EAd76131) | [0x3889...6131](https://goerli.arbiscan.io/address/0x3889927F0B5Eb1a02C6E2C20b39a1Bd4EAd76131) | [0xc60E...1520](https://arbiscan.io/address/0xc60E0Ee1a2770d5F619858C641f14FC4a6401520) | +| SemaphoreVerifier.sol | [0xb908...217C](https://goerli.etherscan.io/address/0xb908Bcb798e5353fB90155C692BddE3b4937217C) | [0xb908...217C](https://sepolia.etherscan.io/address/0xb908Bcb798e5353fB90155C692BddE3b4937217C) | [0xb908...217C](https://mumbai.polygonscan.com/address/0xb908Bcb798e5353fB90155C692BddE3b4937217C) | [0xb908...217C](https://goerli-optimism.etherscan.io/address/0xb908Bcb798e5353fB90155C692BddE3b4937217C) | [0xb908...217C](https://goerli.arbiscan.io/address/0xb908Bcb798e5353fB90155C692BddE3b4937217C) | [0xCAbe...4d07](https://arbiscan.io/address/0xCAbeED6cB96a287000aBd834b0B79c05e6Ea4d07) | +| Pairing.sol | [0xEe44...d7e8](https://goerli.etherscan.io/address/0xEe44c1e83A768E80A3588B409f1A010f9D1dd7e8) | [0xEe44...d7e8](https://sepolia.etherscan.io/address/0xEe44c1e83A768E80A3588B409f1A010f9D1dd7e8) | [0xEe44...d7e8](https://mumbai.polygonscan.com/address/0xEe44c1e83A768E80A3588B409f1A010f9D1dd7e8) | [0xEe44...d7e8](https://goerli-optimism.etherscan.io/address/0xEe44c1e83A768E80A3588B409f1A010f9D1dd7e8) | [0xEe44...d7e8](https://goerli.arbiscan.io/address/0xEe44c1e83A768E80A3588B409f1A010f9D1dd7e8) | [0xE3a4...A74C](https://arbiscan.io/address/0xE3a4C2FE9f025405cA6F60f6E960B4558604A74C) | +| IncrementalBinaryTree.sol | [0x4621...7F49](https://goerli.etherscan.io/address/0x4621EE309EAc747425F0FEd51931dDC241A27F49) | [0x4621...7F49](https://sepolia.etherscan.io/address/0x4621EE309EAc747425F0FEd51931dDC241A27F49) | [0x4621...7F49](https://mumbai.polygonscan.com/address/0x4621EE309EAc747425F0FEd51931dDC241A27F49) | [0x4621...7F49](https://goerli-optimism.etherscan.io/address/0x4621EE309EAc747425F0FEd51931dDC241A27F49) | [0x4621...7F49](https://goerli.arbiscan.io/address/0x4621EE309EAc747425F0FEd51931dDC241A27F49) | [0xcDF8...fFb0](https://arbiscan.io/address/0xcDF8efE6334c68aF283C83f2F14648da51fcfFb0) | +| PoseidonT3.sol | [0xe136...2595](https://goerli.etherscan.io/address/0xe136aBACf78E05988154ed85F4Ea911105302595) | [0xe136...2595](https://sepolia.etherscan.io/address/0xe136aBACf78E05988154ed85F4Ea911105302595) | [0xe136...2595](https://mumbai.polygonscan.com/address/0xe136aBACf78E05988154ed85F4Ea911105302595) | [0xe136...2595](https://goerli-optimism.etherscan.io/address/0xe136aBACf78E05988154ed85F4Ea911105302595) | [0xe136...2595](https://goerli.arbiscan.io/address/0xe136aBACf78E05988154ed85F4Ea911105302595) | [0xe0c8...61d0](https://arbiscan.io/address/0xe0c8d1e53D9Bfc9071F6564755FCFf6cC0dB61d0) | + +:::caution +The Goerli testnet is planned to be deprecated in Q4 2023. Please, check the following links for more information. + +- Goerli: https://ethereum.org/en/developers/docs/networks/#goerli +- Optimism Goerli: https://community.optimism.io/docs/useful-tools/networks/#op-goerli +- Arbitrum Goerli: https://docs.arbitrum.io/for-devs/concepts/public-chains#arbitrum-goerli + ::: diff --git a/apps/docs/versioned_docs/version-V3/faq.md b/apps/docs/versioned_docs/version-V3/faq.md new file mode 100644 index 00000000..5ef01327 --- /dev/null +++ b/apps/docs/versioned_docs/version-V3/faq.md @@ -0,0 +1,80 @@ +--- +sidebar_position: 8 +--- + +# FAQ + +## What is Semaphore? + +Semaphore is a zero-knowledge protocol that allows users to prove their membership in a group and send signals such as votes, feedback, or text messages without revealing the user’s identity. + +This means that signals have no connection to the identities. + +It also provides a simple mechanism to prevent double-signaling, which means you cannot verify the same proof twice. + +## Where can I ask questions about Semaphore? + +You can ask questions about Semaphore on [Discord](https://semaphore.pse.dev/discord) or by opening a [Semaphore Discussion](https://github.com/semaphore-protocol/semaphore/discussions). + +## Why do identities require both the `identity trapdoor` and the `identity nullifier`? + +Having two private values provides an additional security layer. If someone breaks the nullifier hash (imagine there is some malleability that Poseidon preimage is easy to find when hashed with specific value X, which is the external nullifier chosen by devs), the attacker can find all messages the same person sent, but they cannot find which person, because there is also the trapdoor, which could be more difficult to break. + +## What is the difference between the `identity nullifier`, `external nullifier` and `nullifier hash`? + +The identity nullifier is one of the user's secret values, whearas the external nullifier can be used like a topic on which users can generate a valid proof (e.g. send anonymous votes) a limited number of times. + +Both the identity nullifier and the external nullifier are used to prevent the same proof from being verified twice, which means that if a user generates the same proof (with same identity and same external nullifier) twice, the second one will not be valid. + +Finally, the nullifier hash is just the hash of the identity nullifier and the external nullifier which is used to check if the same proof has already been generated. + +In the case of a voting application, if you have a group and you want all members of this group to vote only once, you can use the id of the group as an external nullifier. When a user votes the first time, you can save the hash of their identity nullifier and the group id (i.e. the nullifier hash) and prevent double-voting by checking if that hash already exists. + +See the [Semaphore circuits](https://semaphore.pse.dev/docs/technical-reference/circuits) for more technical information, or the [Semaphore boilerplate](https://github.com/semaphore-protocol/boilerplate) for a real use-case. + +## Why should I prevent proofs from being verified twice? + +Since zero-knowledge proofs are completely anonymous, it is important to prevent those generated by eligible identities from being reused by a malicious party. + +In an anonymous voting application for example, without any checks, a valid proof could be reused to vote again. + +## Where can I find examples of applications using Semaphore? + +You can find some applications that are using Semaphore in [this blog post](https://mirror.xyz/privacy-scaling-explorations.eth/Yi4muh-vzDZmIqJIcM9Mawu2e7jw8MRnwxvhFcyfns8). + +## How can I start a project using Semaphore? + +There are three ways you can start using Semaphore in your project: using the [Semaphore CLI](https://github.com/semaphore-protocol/semaphore/tree/main/packages/cli), using the [Semaphore boilerplate](https://github.com/semaphore-protocol/boilerplate) as a template or forking it, or installing the Semaphore packages manually. + +### Semaphore CLI + +To create a new project you could use `npx` or install the [Semaphore CLI](https://github.com/semaphore-protocol/semaphore/tree/main/packages/cli) globally using `npm` and then create the new project using the `semaphore create` command. See the [Quick Setup](https://semaphore.pse.dev/docs/quick-setup) for more information. + +There are three supported templates right now: `contracts-hardhat`, `monorepo-ethers` and `monorepo-subgraph`. + +- `contracts-hardhat`: It contains a basic Semaphore use case. It comes with a sample contract, a test for that contract and a sample task that deploys that contract. +- `monorepo-ethers`: It is a complete application that demonstrates a basic Semaphore use case. It comes with a sample contract, a test for that contract and a sample task that deploys that contract. It also contains a frontend to play around with the contract. This template uses [Ethers](https://github.com/ethers-io/ethers.js/) under the hood to get on-chain data. +- `monorepo-subgraph`: It is the same as the monorepo-ethers template, but uses [The Graph protocol](https://thegraph.com/) under the hood to get on-chain data. + +The Semaphore CLI can also be used to get group data from a supported network. There are commands like: `get-groups`, `get-group`, `get-members`, `get-proofs`: + +- `get-groups`: It shows the list of groups from a supported network. +- `get-group`: It shows the data of a group from a supported network. +- `get-members`: It shows the members of a group from a supported network. +- `get-proofs`: It shows the proofs of a group from a supported network. + +### Semaphore boilerplate + +To create a project, you could also use the [Semaphore boilerplate](https://github.com/semaphore-protocol/boilerplate). You could fork it or use it as a template. + +The Semaphore CLI templates and the Semaphore boilerplate contain the same code, which is a feedback application where you can create an identity, join a group, and send your feedback anonymously. They are almost the same, the only difference is that the templates use plain CSS so you can decide the CSS framework or library you want to use and the boilerplate uses [ChakraUI](https://chakra-ui.com/) by default. + +You can also try the live Semaphore boilerplate app here: https://demo.semaphore.pse.dev. + +### Manual install + +Alternatively, you can also install all packages manually using npm or yarn following the [Semaphore documentation](https://semaphore.pse.dev/docs/introduction). + +## How can I contribute to the protocol? + +There are several ways you could contribute to the protocol, you can find more information about it here: https://github.com/semaphore-protocol#ways-to-contribute. diff --git a/apps/docs/versioned_docs/version-V3/glossary.md b/apps/docs/versioned_docs/version-V3/glossary.md new file mode 100644 index 00000000..f12f6728 --- /dev/null +++ b/apps/docs/versioned_docs/version-V3/glossary.md @@ -0,0 +1,67 @@ +--- +sidebar_position: 7 +--- + +# Glossary + +## Semaphore identity + +The identity of a user in the Semaphore protocol. +An identity contains the following three values: + +- [Identity commitment](#identity-commitment): the public value. +- Identity trapdoor and identity nullifier: secret values known only by the user. + +## Identity commitment + +The public [Semaphore identity](#semaphore-identity) value used in [Semaphore groups](#semaphore-group). + +Semaphore uses the [Poseidon](https://www.poseidon-hash.info/) hash function to create the identity commitment from the Semaphore identity secret values. + +## Semaphore group + +A group is a binary incremental [Merkle tree](#merkle-tree) in which each leaf contains an [identity commitment](#identity-commitment) for a user. +The identity commitment proves that the user is a group member without revealing the Semaphore identity of the user. + +Semaphore uses the **Poseidon** hash function to create Merkle trees. +For more information, see the [Poseidon website](https://www.poseidon-hash.info/). + +## Merkle tree + +A tree in which every leaf (i.e., a node that doesn't have children) is labelled with the cryptographic hash of a data block, +and every node that isn't a leaf is labelled with the cryptographic hash of its child node labels. +In zero-knowledge protocols, Merkle trees can be used to efficiently summarize and validate large data sets. +To validate that a tree contains a specific leaf, a verifier only needs a portion of the complete data structure. + +For more information, see [Merkle tree in Wikipedia](https://en.wikipedia.org/wiki/Merkle_tree). + +## Nullifier + +A value used to prevent double entry or double signalling. + +See [Circuit nullifier hash](/docs/technical-reference/circuits/#nullifier-hash). + +## Relay + +A third-party who receives a fee for including relayed transactions in the blockchain (McMenamin, Daza, and Fitz. https://eprint.iacr.org/2022/155.pdf, p.3). +To preserve the anonymity of the user broadcasting a signal with Semaphore, an application may use a relayer to post the signal transaction to Ethereum on behalf of the user. + +Applications may provide rewards for relayers and implement front-running prevention mechanisms, such as requiring the signals to include the relayer’s address, binding the +signal to that specific address (https://semaphore.pse.dev/whitepaper-v1.pdf, p.6). + +## Trusted setup files + +The secure, verifiable parameters generated by Semaphore's trusted setup ceremony. +Semaphore uses the trusted setup files to generate and verify valid zero-knowledge proofs. +To generate or verify valid zero-knowledge proofs with Semaphore, applications must include the following Semaphore _trusted setup_ files: + +- semaphore.zkey +- semaphore.wasm +- semaphore.json + +For a complete list of ready-to-use files, see . +To learn more, see the [trusted setup ceremony](https://storage.googleapis.com/trustedsetup-a86f4.appspot.com/semaphore/semaphore_top_index.html). + +## Signals + +The term "signals" in Semaphore refers to the values the user broadcasts when voting, confirming, sending a message and so on. On the other hand, "[signals](https://docs.circom.io/circom-language/signals/)" in Circom refers to data that contain elements within the field of Z/pZ. In Circom, "signals" can be defined as input or output, and are considered intermediate signals otherwise. diff --git a/apps/docs/versioned_docs/version-V3/guides/_category_.json b/apps/docs/versioned_docs/version-V3/guides/_category_.json new file mode 100644 index 00000000..680845a9 --- /dev/null +++ b/apps/docs/versioned_docs/version-V3/guides/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Guides", + "position": 3 +} diff --git a/apps/docs/versioned_docs/version-V3/guides/fetching-data.mdx b/apps/docs/versioned_docs/version-V3/guides/fetching-data.mdx new file mode 100644 index 00000000..def27b02 --- /dev/null +++ b/apps/docs/versioned_docs/version-V3/guides/fetching-data.mdx @@ -0,0 +1,173 @@ +--- +sidebar_position: 4 +title: Fetching data +--- + +import Tabs from "@theme/Tabs" +import TabItem from "@theme/TabItem" + +# Semaphore data + +To fetch on-chain data from the [Semaphore.sol](https://github.com/semaphore-protocol/semaphore/blob/main/packages/contracts/contracts/Semaphore.sol) contract, you can use the [@semaphore-protocol/data](https://github.com/semaphore-protocol/semaphore/tree/main/packages/data) library. + +There are two ways to do this, using [`SemaphoreSubgraph`](https://github.com/semaphore-protocol/semaphore/blob/main/packages/data/src/subgraph.ts) or [`SemaphoreEthers`](https://github.com/semaphore-protocol/semaphore/blob/main/packages/data/src/ethers.ts). The `SemaphoreSubgraph` class uses the [Semaphore subgraph](https://github.com/semaphore-protocol/subgraph), which uses [The Graph Protocol](https://thegraph.com/) under the hood, and the `SemaphoreEthers` class uses [Ethers](https://github.com/ethers-io/ethers.js/). + +- [**Fetch data using SemaphoreSubgraph**](#fetch-data-using-semaphoresubgraph) +- [**Fetch data using SemaphoreEthers**](#fetch-data-using-semaphoreethers) + +## Install library + + + + +```bash +npm install @semaphore-protocol/data +``` + + + + +```bash +yarn add @semaphore-protocol/data +``` + + + + +## Fetch data using SemaphoreSubgraph + +To fetch data using the Semaphore subgraph you can use the [`SemaphoreSubgraph`](https://github.com/semaphore-protocol/semaphore/blob/main/packages/data/src/subgraph.ts) class from the [@semaphore-protocol/data](https://github.com/semaphore-protocol/semaphore/tree/main/packages/data) package. + +```typescript +import { SemaphoreSubgraph } from "@semaphore-protocol/data" + +const semaphoreSubgraph = new SemaphoreSubgraph() + +// or: +const semaphoreSubgraph = new SemaphoreSubgraph("arbitrum") + +// or: +const semaphoreSubgraph = new SemaphoreSubgraph( + "https://api.studio.thegraph.com/query/14377//" +) +``` + +### Get group Ids + +```typescript +const groupIds = await semaphoreSubgraph.getGroupIds() +``` + +### Get groups + +```typescript +const groups = await semaphoreSubgraph.getGroups() + +// or + +const groups = await semaphoreSubgraph.getGroups({ members: true, verifiedProofs: true }) +``` + +### Get group + +```typescript +const group = await semaphoreSubgraph.getGroup("42") + +// or + +const { members, verifiedProofs } = semaphoreSubgraph.getGroup("42", { members: true, verifiedProofs: true }) +``` + +### Check if an identity commitment is a member of a group + +```ts +await semaphoreSubgraph.isGroupMember( + "42", + "16948514235341957898454876473214737047419402240398321289450170535251226167324" +) +``` + +:::info +You can create an off-chain group using the SemaphoreSubgraph class to fetch members like so: + +```typescript +import { Group } from "@semaphore-protocol/group" +import { SemaphoreSubgraph } from "@semaphore-protocol/data" + +const groupId = "3" +const semaphoreSubgraph = new SemaphoreSubgraph("sepolia") +const { members } = await semaphoreSubgraph.getGroup(groupId, { members: true }) +const group = new Group(groupId, 20, members) +``` +::: + +## Fetch data using SemaphoreEthers + +To fetch data using Ethers you can use the [`SemaphoreEthers`](https://github.com/semaphore-protocol/semaphore/blob/main/packages/data/src/ethers.ts) class from the [@semaphore-protocol/data](https://github.com/semaphore-protocol/semaphore/tree/main/packages/data) package. + +```typescript +import { SemaphoreEthers } from "@semaphore-protocol/data" + +const semaphoreEthers = new SemaphoreEthers() + +// or: +const semaphoreEthers = new SemaphoreEthers("homestead", { + address: "semaphore-address", + startBlock: 0 +}) + +// or: +const semaphoreEthers = new SemaphoreEthers("http://localhost:8545", { + address: "semaphore-address" +}) +``` + +### Get group Ids + +```typescript +const groupIds = await semaphoreEthers.getGroupIds() +``` + +### Get group + +```typescript +const group = await semaphoreEthers.getGroup("42") +``` + +### Get group admin + +```typescript +const admin = await semaphoreEthers.getGroupAdmin("42") +``` + +### Get group members + +```typescript +const members = await semaphoreEthers.getGroupMembers("42") +``` + +### Get group verified proofs + +```typescript +const verifiedProofs = await semaphoreEthers.getGroupVerifiedProofs("42") +``` + +:::info +You can create an off-chain group using the SemaphoreEthers class to fetch members like so: + +```typescript +import { Group } from "@semaphore-protocol/group" +import { SemaphoreEthers } from "@semaphore-protocol/data" + +const groupId = "3" +const semaphoreEthers = new SemaphoreEthers("sepolia") +const members = await semaphoreEthers.getGroupMembers(groupId) +const group = new Group(groupId, 20, members) +``` +::: \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-V3/guides/groups.mdx b/apps/docs/versioned_docs/version-V3/guides/groups.mdx new file mode 100644 index 00000000..1db46319 --- /dev/null +++ b/apps/docs/versioned_docs/version-V3/guides/groups.mdx @@ -0,0 +1,151 @@ +--- +sidebar_position: 2 +title: Groups +--- + +import Tabs from "@theme/Tabs" +import TabItem from "@theme/TabItem" + +# Semaphore groups + +A [Semaphore group](/docs/glossary/#semaphore-group) contains [identity commitments](/docs/glossary/#identity-commitment) of group members. +Example uses of groups include the following: + +- poll question that attendees join to rate an event, +- ballot that members join to vote on a proposal, +- whistleblowers who are verified employees of an organization. + +A Semaphore group is an [incremental Merkle tree](/docs/glossary/#incremental-merkle-tree), and group members (i.e., [identity commitments](/docs/glossary/#identity-commitments)) are tree leaves. +Semaphore groups set the following three parameters: + +- **Group id**: a unique identifier for the group; +- **Tree depth**: the maximum number of members a group can contain (`max size = 2 ^ tree depth`); +- **Members**: the list of members to initialize the group. + +Learn how to work with groups. + +- [**Off-chain groups**](#off-chain-groups) +- [**On-chain groups**](#on-chain-groups) + +## Off-chain groups + +- [Create a group](#create-a-group) +- [Add members](#add-members) +- [Remove or update members](#remove-or-update-members) + +### Create a group + +Use the [`@semaphore-protocol/group`](https://github.com/semaphore-protocol/semaphore/blob/main/packages/group) library `Group` class to create an off-chain group with the following parameters: + +- `Group id`: a unique identifier for the group; +- `Tree depth`: (_default `20`_) the maximum number of members a group can contain (`max size = 2 ^ tree depth`). +- `Members`: (_default `[]`_) the list of members to initialize the group. + +#### Install library: + + + + +```bash +npm install @semaphore-protocol/group +``` + + + + +```bash +yarn add @semaphore-protocol/group +``` + + + + +To create a group with default _`treeDepth`_, call the `Group` constructor without the second parameter. For example: + +```ts +import { Group } from "@semaphore-protocol/group" + +const group = new Group(1) +``` + +The following example code passes _`treeDepth`_ to create a group for `2 ^ 30 = 1073741824` members: + +```ts +import { Group } from "@semaphore-protocol/group" + +const group = new Group(1, 30) +``` + +You can also initialize a group with multiple members by passing the list of identity commitments (members) as the third parameter when creating the group: + +```ts +import { Group } from "@semaphore-protocol/group" + +const members = [ + "11237622825477336339577122413451117718539783476837539122310492284566644730311", + "9332663527862709610616009715800254142772436825222910251631161087138559093425", + "13255821893820536903335282929376140649646180444238593676033702344407594536519" +] +const group = new Group(1, 20, members) +``` + +### Add members + +Use the `Group addMember` function to add a member (identity commitment) to a group. For example: + +```ts +group.addMember(identityCommitment) +``` + +To add a batch of members to a group, pass an array to the `Group addMembers` function. For example: + +```ts +group.addMembers([identityCommitment1, identityCommitment2]) +``` + +:::caution +When you use the same Semaphore identity across multiple groups, if an attacker takes control of that identity all the groups it is part of will be compromised. Consider using different identities for each group. +::: + +### Remove or update members + +To remove members from a group, pass the member index to the `Group removeMember` function. For example: + +```ts +group.removeMember(0) +``` + +To update members in a group, pass the member index and the new value to the `Group updateMember` function. For example: + +```ts +group.updateMember(0, 2) +``` + +:::caution +Removing a member from a group sets the node value to a special value (i.e. `zeroValue`). +Given that the node isn't removed, and the length of the `group.members` array doesn't change. +::: + +## On-chain groups + +The [`SemaphoreGroups`](https://github.com/semaphore-protocol/semaphore/blob/main/packages/contracts/contracts/base/SemaphoreGroups.sol) contract uses the [`IncrementalBinaryTree`](https://github.com/privacy-scaling-explorations/zk-kit/blob/main/packages/incremental-merkle-tree.sol/contracts/IncrementalBinaryTree.sol) library and provides methods to create and manage groups. + +:::info +You can import `SemaphoreGroups.sol` and other Semaphore contracts from the [`@semaphore-protocol/contracts`](https://github.com/semaphore-protocol/semaphore/tree/main/packages/contracts) NPM module. +::: + +Alternatively, you can use an already deployed [`Semaphore.sol`](https://github.com/semaphore-protocol/semaphore/blob/main/packages/contracts/contracts/Semaphore.sol) contract and use its group external functions. + +:::caution +`Semaphore.sol` does not check if a member with a specific identity commitment already exists in a group. This check must be done off-chain. +::: + +:::caution +`Semaphore.sol` includes a mechanism to verify Semaphore proofs created with old Merkle tree roots, the duration of which can be defined by the admin in the `createGroup` function. Members of a group could then continue to generate valid proofs even after being removed. For more info see the issue [#98](https://github.com/semaphore-protocol/semaphore/issues/98). +::: diff --git a/apps/docs/versioned_docs/version-V3/guides/identities.mdx b/apps/docs/versioned_docs/version-V3/guides/identities.mdx new file mode 100644 index 00000000..8d207801 --- /dev/null +++ b/apps/docs/versioned_docs/version-V3/guides/identities.mdx @@ -0,0 +1,111 @@ +--- +sidebar_position: 1 +title: Identities +--- + +import Tabs from "@theme/Tabs" +import TabItem from "@theme/TabItem" + +# Semaphore identities + +In order to join a [Semaphore group](/docs/glossary#semaphore-group), a user must first create a [Semaphore identity](/docs/glossary#semaphore-identity). +A Semaphore identity contains two values generated with the identity: + +- Identity trapdoor +- identity nullifier + +To use and verify the identity, the identity owner (user) must know the trapdoor and nullifier values. +To prevent fraud, the owner should keep both values secret. + +## Create identities + +In your code, use the [`@semaphore-protocol/identity`](https://github.com/semaphore-protocol/semaphore/tree/main/packages/identity) library to create a Semaphore identity _deterministically_ (from the hash of a message) or _randomly_. + +- [**Create random identities**](#create-random-identities) +- [**Create deterministic identities**](#create-deterministic-identities) + +### Install library: + + + + +```bash +npm install @semaphore-protocol/identity +``` + + + + +```bash +yarn add @semaphore-protocol/identity +``` + + + + +### Create random identities + +To create a random identity, instantiate `Identity` without any parameters. For example: + +```ts +import { Identity } from "@semaphore-protocol/identity" + +const { trapdoor, nullifier, commitment } = new Identity() +``` + +The new identity contains two random secret values: `trapdoor` and `nullifier`, and one public value: `commitment`. + +The Poseidon hash of the identity nullifier and trapdoor is called the _identity secret_, +and its hash is the _identity commitment_. + +An identity commitment, similarly to Ethereum addresses, is a public value used +in Semaphore groups to represent the identity of a group member. The secret values are similar to +Ethereum private keys and are used to generate Semaphore zero-knowledge proofs and authenticate signals. + +### Create deterministic identities + +If you pass a message as a parameter, Semaphore generates `trapdoor` and `nullifier` +from the _SHA256_ hash of the message. +The message might be a password or a message that the user cryptographically signs with a private key. + +When using deterministic identities, you should always keep the message secret. +Given that the hash is deterministic, anyone with the same message can recreate the same identity. + +```ts +const identity = new Identity("secret-message") +``` + +:::tip +Building a system to save or recover secret values of Semaphore identities is nontrivial. +You may choose to delegate such functionality to existing wallets such as Metamask. For example: + +1. In Metamask, a user signs a message with the private key of their Ethereum account. +2. In your application, the user creates a deterministic identity with the signed message. +3. The user can now recreate their Semaphore identity whenever they want by signing the same message with their Ethereum account in Metamask. + +::: + +## Save your identities + +You can output an identity as a JSON string that you can save and reuse later. +The `Identity.toString()` method generates a JSON array from an identity. For example: + +```ts +console.log(identity.toString()) // View the identity trapdoor and nullifier. + +// '["8255d...", "62c41..."]' +``` + +The array contains the trapdoor and nullifier. + +To reuse the saved identity, pass the JSON to the `Identity()` constructor. + +```ts +const identity2 = new Identity(identity.toString()) +``` diff --git a/apps/docs/versioned_docs/version-V3/guides/proofs.mdx b/apps/docs/versioned_docs/version-V3/guides/proofs.mdx new file mode 100644 index 00000000..db65cb0e --- /dev/null +++ b/apps/docs/versioned_docs/version-V3/guides/proofs.mdx @@ -0,0 +1,121 @@ +--- +sidebar_position: 3 +title: Proofs +--- + +import Tabs from "@theme/Tabs" +import TabItem from "@theme/TabItem" + +# Semaphore proofs + +Once a user joins their [Semaphore identity](/docs/glossary#semaphore-identity) to a [Semaphore group](/docs/glossary#semaphore-group), the user can signal anonymously with a zero-knowledge proof that proves the following: + +- the user is a member of the group, +- the same user created the signal and the proof. + +Developers can use Semaphore for the following: + +- [**Generate a proof off-chain**](#generate-a-proof-off-chain) +- [**Verify a proof off-chain**](#verify-a-proof-off-chain) +- [**Verify a proof on-chain**](#verify-a-proof-on-chain) + +## Generate a proof off-chain + +Use the [`@semaphore-protocol/proof`](https://github.com/semaphore-protocol/semaphore/tree/main/packages/proof) library to generate an off-chain proof. +To generate a proof, pass the following parameters to the `generateProof` function: + +- `identity`: the Semaphore identity of the user broadcasting the signal and generating the proof; +- `group`: the group to which the user belongs; +- `externalNullifier`: the value that prevents double-signaling; +- `signal`: the signal the user wants to send anonymously; +- `snarkArtifacts`: the `zkey` and `wasm` [trusted setup files](/docs/glossary/#trusted-setup-files). + +#### Install library: + + + + +```bash +npm install @semaphore-protocol/proof +``` + + + + +```bash +yarn add @semaphore-protocol/proof +``` + + + + +In the voting system use case, once all the voters have joined their [identities](/docs/guides/identities#create-an-identity) to the ballot [group](/docs/guides/groups), +a voter can generate a proof to vote for a proposal. +In the call to `generateProof`, the voting system passes the unique ballot ID (the [Merkle tree](/docs/glossary/#merkle-tree/) root of the group) as the +`externalNullifier` to prevent the voter signaling more than once for the ballot. +The following code sample shows how to use `generateProof` to generate the voting proof: + +```ts +import { generateProof } from "@semaphore-protocol/proof" + +const externalNullifier = group.root +const signal = 1 + +const fullProof = await generateProof(identity, group, externalNullifier, signal, { + zkeyFilePath: "./semaphore.zkey", + wasmFilePath: "./semaphore.wasm" +}) +``` + +:::info +If you are generating the proof on the client side, you can avoid adding the snark artifacts because they are fetched automatically: + +```ts +const fullProof = await generateProof(identity, group, externalNullifier, signal) +``` +::: + +## Verify a proof off-chain + +Use the [`@semaphore-protocol/proof`](https://github.com/semaphore-protocol/semaphore/tree/main/packages/proof) library to verify a Semaphore proof off-chain. +To verify a proof, pass the following to the `verifyProof` function: + +- `fullProof`: the Semaphore proof; +- `treeDepth`: the Merkle tree depth. + +The following code sample shows how to verify the previously generated proof: + +```ts +import { verifyProof } from "@semaphore-protocol/proof" + +await verifyProof(fullProof, 20) // true or false. +``` + +`verifyProof` returns a Promise that resolves to `true` or `false`. + +## Verify a proof on-chain + +Use the [`Semaphore.sol`](/docs/technical-reference/contracts#semaphoresol) contract to verify proofs on-chain. + +:::info +See our [deployed contracts](/docs/deployed-contracts) to find the addresses for your network. +:::: + +To verify Semaphore proofs in your contract, import `ISemaphore.sol`, pass it the `Semaphore.sol` address and call the `verifyProof` method with following parameters: + +- `groupId`: the identifier of the group; +- `merkleTreeRoot`: the root of the Merkle tree; +- `signal`: the signal the user wants to send anonymously; +- `nullifierHash`: a [nullifier hash](#retrieve-a-nullifier-hash); +- `externalNullifier`: the value that prevents double-signaling; +- `proof`: a [Solidity-compatible Semaphore proof](#generate-a-solidity-compatible-proof). + +:::info +You can import `ISemaphore.sol` and other Semaphore contracts from the [`@semaphore-protocol/contracts`](https://github.com/semaphore-protocol/semaphore/tree/main/packages/contracts) NPM module. +::: diff --git a/apps/docs/versioned_docs/version-V3/quick-setup.mdx b/apps/docs/versioned_docs/version-V3/quick-setup.mdx new file mode 100644 index 00000000..32f5672e --- /dev/null +++ b/apps/docs/versioned_docs/version-V3/quick-setup.mdx @@ -0,0 +1,279 @@ +--- +sidebar_position: 2 +--- + +import Tabs from "@theme/Tabs" +import TabItem from "@theme/TabItem" + +# Quick setup + +Semaphore provides an official CLI to set up your project with Hardhat. If your NPM version is 5.2 or higher you can use NPX: + +```bash +npx @semaphore-protocol/cli@latest create my-app --template monorepo-ethers +``` + +Otherwise, install `@semaphore-protocol/cli` globally and run the `create` command: + +```bash +npm i -g @semaphore-protocol/cli@latest +semaphore create my-app --template monorepo-ethers +``` + +:::info +The supported templates are: [`contracts-hardhat`](https://github.com/semaphore-protocol/semaphore/tree/main/packages/cli-template-contracts-hardhat), [`monorepo-ethers`](https://github.com/semaphore-protocol/semaphore/tree/main/packages/cli-template-monorepo-ethers), [`monorepo-subgraph`](https://github.com/semaphore-protocol/semaphore/tree/main/packages/cli-template-monorepo-subgraph). +::: + +:::info +The [`semaphore CLI`](https://github.com/semaphore-protocol/semaphore/tree/main/packages/cli) can also be used to get group data from a supported network (e.g `semaphore get-groups --network arbitrum-goerli`). +::: + +To start working on your project, install the dependencies: + + + + +```bash +cd my-app +npm i +``` + + + + +```bash +cd my-app +yarn +``` + + + + +## Output + +The `create` command will create a directory called my-app (or whatever name you choose) inside the current folder. That directory will contain the initial project structure, which includes a simple contract, a task to deploy it, some tests and a Next.js application (the web-app folder) to interact with that contract. + +``` +my-app +├── .yarn +├── apps +│   └── contracts +│ │   └── contracts +| │ │   └── Feedback.sol +│ │   └── scripts +| │ │   └── download-snark-artifacts.ts +│ │   └── tasks +| │ │   └── deploy.ts +│ │   └── test +| │ │   └── Feedback.ts +│ │   └── hardhat.config.ts +│ │   └── package.json +│ │   └── tsconfig.json +│   └── web-app +├── scripts +│   └── copy-contracts-artifacts.ts +├── .editorconfig +├── .env +├── .env.example +├── .eslintignore +├── .eslintrc.json +├── .gitignore +├── .prettierignore +├── .prettierrc.json +├── .yarnrc.yml +├── package.json +├── README.md +└── tsconfig.json +``` + +The `Feedback.sol` contract creates a Semaphore group, allows users to join that group with their Semaphore identity, and finally allows group members to send an anonymous feedback. + +## Usage + +### Compile contracts + +Go to the `contracts` folder: + +```bash +cd apps/contracts +``` + +And compile your contracts: + + + + +```bash +npm run compile +``` + + + + +```bash +yarn compile +``` + + + + +### Test contracts + +Test your contracts: + + + + +```bash +npm test +``` + + + + +```bash +yarn test +``` + + + + +Generate a test coverage report: + + + + +```bash +npm run test:coverage +``` + + + + +```bash +yarn test:coverage +``` + + + + +Or a test gas report: + + + + +```bash +npm run test:report-gas +``` + + + + +```bash +yarn test:report-gas +``` + + + + +### Deploy contracts + +Follow the instructions below to deploy your contracts: + +In the project root folder: + +1. Add your environment variables in the `.env` file. + + :::note + You should at least set a valid Infura API Key (you could use Alchemy as well) and a private key with some ethers. + ::: + +2. Go to the `apps/contracts` folder and deploy your contract. + + + + + ```bash + npm run deploy -- --semaphore --group --network arbitrum-goerli + ``` + + + + + ```bash + yarn deploy --semaphore --group --network arbitrum-goerli + ``` + + + + + :::note + Check the Semaphore contract addresses [here](/docs/deployed-contracts). + ::: + + :::caution + The group id is a number. + ::: + +### Start app + +Start the application: + + + + +```bash +npm run dev +``` + + + + +```bash +yarn dev +``` + + + diff --git a/apps/docs/versioned_docs/version-V3/resources.md b/apps/docs/versioned_docs/version-V3/resources.md new file mode 100644 index 00000000..7836d5cf --- /dev/null +++ b/apps/docs/versioned_docs/version-V3/resources.md @@ -0,0 +1,33 @@ +--- +sidebar_position: 9 +--- + +# Resources + +## Articles + +[Community Proposal: Semaphore: Zero-Knowledge Signaling on Ethereum (Whitepaper v1)](https://semaphore.pse.dev/whitepaper-v1.pdf) - Kobi Gurkan, Koh Wei Jie and Barry WhiteHat + +[To Mixers and Beyond: presenting Semaphore, a privacy gadget built on Ethereum](https://medium.com/coinmonks/to-mixers-and-beyond-presenting-semaphore-a-privacy-gadget-built-on-ethereum-4c8b00857c9b) - Koh Wei Jie + +[Semaphore V2 is Live!](https://medium.com/privacy-scaling-explorations/semaphore-v2-is-live-f263e9372579) - Privacy and Scaling Explorations + +## Videos + +[Privacy in Ethereum](https://www.youtube.com/watch?v=maDHYyj30kg) - Barry WhiteHat at the Taipei Ethereum Meetup + +[Snarks for mixing, signaling and scaling by](https://www.youtube.com/watch?v=lv6iK9qezBY) - Barry WhiteHat at Devcon 4 + +[Privacy in Ethereum](https://www.youtube.com/watch?v=zBUo7G95wYE) - Barry WhiteHat at Devcon 5 + +[A trustless Ethereum mixer using zero-knowledge signalling](https://www.youtube.com/watch?v=GzVT16lFOHU) - Koh Wei Jie and Barry WhiteHat at Devcon 5 + +[Hands-on Applications of Zero-Knowledge Signalling](https://www.youtube.com/watch?v=7wd2aAN2jXI) - Koh Wei Jie at Devcon 5 + +[Succinct Proofs in Ethereum](https://www.youtube.com/watch?v=TtsDNneTDDY) - Barry WhiteHat during the 2nd ZKProof Workshop + +[Semaphore Roadmap for Ethereum](https://www.youtube.com/watch?v=gOub903iWFs) - Barry WhiteHat at Zcon1 + +[Proposal: Semaphore - Zero-Knowledge Signaling on Ethereum](https://www.youtube.com/watch?v=y5uV9eRb3-w) - Kobi Gurkan and Koh Wei Jie at ZKProof Home Edition + +[Anonymous Signalling on Ethereum](https://www.youtube.com/watch?v=dxAfL91Sbw4) - Cedoor at Devcon 6 Bogotá diff --git a/apps/docs/versioned_docs/version-V3/subgraph.md b/apps/docs/versioned_docs/version-V3/subgraph.md new file mode 100644 index 00000000..113f1f5e --- /dev/null +++ b/apps/docs/versioned_docs/version-V3/subgraph.md @@ -0,0 +1,51 @@ +--- +sidebar_position: 6 +--- + +# Subgraph + +[The Graph](https://thegraph.com/) is a protocol for indexing networks like Ethereum and IPFS. +Site owners publish _subgraphs_ that expose site data for anyone to query. +Semaphore's subgraph allows you to retrieve data from the [`Semaphore.sol`](https://github.com/semaphore-protocol/semaphore/blob/main/packages/contracts/Semaphore.sol) smart contract. + +:::tip +The Graph protocol uses the [GraphQL](https://graphql.org/) query language. For examples, see the [GraphQL API documentation](https://thegraph.com/docs/developer/graphql-api). Visit the [subgraph repository](https://github.com/semaphore-protocol/subgraph) to see the list of Semaphore subgraphs. +::: + +## Schema + +### MerkleTree + +- `id`: unique identifier among all MerkleTree entities, +- `depth`: Merkle tree depth, +- `root`: Merkle tree root, +- `zeroValue`: Merkle tree zero value, +- `numberOfLeaves`: total number of tree leaves, +- `group`: link to the Group entity. + +### Group + +- `id`: unique identifier among all Group entities, +- `merkleTree`: link to the MerkleTree entity, +- `timestamp`: block timestamp, +- `admin`: admin of the group, +- `members`: list of group members. +- `verifiedProofs`: list of group proofs. + +### Member + +- `id`: unique identifier among all Member entities, +- `identityCommitment`: Semaphore identity commitment, +- `timestamp`: block timestamp, +- `index`: index of the tree leaf, +- `group`: link to the Group entity. + +### VerifiedProof + +- `id`: unique identifier among all VerifiedProof entities, +- `signal`: user's signal, +- `merkleTreeRoot`: Merkle tree root, +- `nullifierHash`: nullifier hash, +- `externalNullifier`: external nullifier, +- `timestamp`: block timestamp, +- `group`: link to the Group entity. diff --git a/apps/docs/versioned_docs/version-V3/technical-reference/_category_.json b/apps/docs/versioned_docs/version-V3/technical-reference/_category_.json new file mode 100644 index 00000000..2187e794 --- /dev/null +++ b/apps/docs/versioned_docs/version-V3/technical-reference/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Technical reference", + "position": 4 +} diff --git a/apps/docs/versioned_docs/version-V3/technical-reference/circuits.md b/apps/docs/versioned_docs/version-V3/technical-reference/circuits.md new file mode 100644 index 00000000..8cc83bc0 --- /dev/null +++ b/apps/docs/versioned_docs/version-V3/technical-reference/circuits.md @@ -0,0 +1,55 @@ +--- +sidebar_position: 2 +--- + +# Circuits + +The [Semaphore circuit](https://github.com/semaphore-protocol/semaphore/tree/main/packages/circuits) is the heart of the protocol and consists of three parts: + +- [**Proof of membership**](/docs/technical-reference/circuits#proof-of-membership) +- [**Nullifier hash**](/docs/technical-reference/circuits#nullifier-hash) +- [**Signal**](/docs/technical-reference/circuits#signal) + +![Semaphore circuit](https://github.com/semaphore-protocol/semaphore/raw/main/packages/circuits/scheme.png) + +The diagram above shows how the input signals are used in the Semaphore circuit and how the outputs are calculated. + +## Proof of membership + +The circuit hashes the hash of the identity nullifier with the identity trapdoor to generate an identity commitment. Then, it verifies the proof of membership against the Merkle root and the identity commitment. + +**Private inputs:** + +- `treeSiblings[nLevels]`: the values along the Merkle path to the user's identity commitment, +- `treePathIndices[nLevels]`: the direction (0/1) per tree level corresponding to the Merkle path to the user's identity commitment, +- `identityNullifier`: the 32-byte identity secret used as nullifier, +- `identityTrapdoor`: the 32-byte identity secret used as trapdoor. + +**Public outputs:** + +- `root`: The Merkle root of the tree. + +## Nullifier hash + +The circuit hashes the identity nullifier with the external nullifier and then checks that the result matches the provided nullifier hash. +Nullifier hashes saved in a Semaphore smart contract allow the contract to reject a proof that contains a used nullifier hash. + +**Private inputs:** + +- `identityNullifier`: the 32-byte identity secret used as a nullifier. + +**Public inputs:** + +- `externalNullifier`: the 32-byte external nullifier. + +**Public outputs:** + +- `nullifierHash`: the hash of the identity nullifier and the external nullifier; used to prevent double-signaling. + +## Signal + +The circuit calculates a dummy square of the signal hash to prevent any tampering with the proof. + +**Public inputs:** + +- `signalHash`: the hash of the user's signal. diff --git a/apps/docs/versioned_docs/version-V3/technical-reference/contracts.md b/apps/docs/versioned_docs/version-V3/technical-reference/contracts.md new file mode 100644 index 00000000..ad5c5b23 --- /dev/null +++ b/apps/docs/versioned_docs/version-V3/technical-reference/contracts.md @@ -0,0 +1,51 @@ +--- +sidebar_position: 3 +--- + +# Contracts + +Semaphore includes two types of contracts: + +- [**Base contracts**](/docs/technical-reference/contracts#base-contracts) +- [**Extension contracts**](/docs/technical-reference/contracts#extension-contracts) + +And [**Semaphore.sol**](/docs/technical-reference/contracts#semaphoresol), the main contract deployed on the networks supported by Semaphore. + +:::info +To use Semaphore contracts and interfaces in your project, +install the [`@semaphore-protocol/contracts`](https://github.com/semaphore-protocol/semaphore/tree/main/packages/contracts) NPM package. +::: + +## Base contracts + +Semaphore provides the following base contracts: + +- [`SemaphoreVerifier.sol`](https://github.com/semaphore-protocol/semaphore/blob/main/packages/contracts/contracts/base/SemaphoreVerifier.sol): contains a function to verify Semaphore proofs; +- [`SemaphoreGroups.sol`](https://github.com/semaphore-protocol/semaphore/blob/main/packages/contracts/contracts/base/SemaphoreGroups.sol): contains the functions to create groups and add/remove/update members. + +These contracts are closely related to the protocol. +You can use them in your contract or you can use [**Semaphore.sol**](/docs/technical-reference/contracts#semaphoresol), which integrates them for you. + +:::info +While some DApps may use on-chain groups, others may prefer to use off-chain groups, saving only their tree roots in the contract. +::: + +## Extension contracts + +- [`SemaphoreVoting.sol`](https://github.com/semaphore-protocol/semaphore/blob/main/packages/contracts/contracts/extensions/SemaphoreVoting.sol): voting contract that contains the essential functions to create polls, add voters, and anonymously cast votes; +- [`SemaphoreWhistleblowing.sol`](https://github.com/semaphore-protocol/semaphore/blob/main/packages/contracts/contracts/extensions/SemaphoreWhistleblowing.sol): whistleblowing contract that contains the essential functions to create entities (for example: non-profit organizations), add whistleblowers, and anonymously publish leaks. + +These contracts extend the protocol to provide application logic for specific use-cases. +More extensions will be added in the future. + +## Semaphore.sol + +[`Semaphore.sol`](https://github.com/semaphore-protocol/semaphore/blob/main/packages/contracts/contracts/Semaphore.sol) is based on the base contracts. It integrates them and additionally provides: + +- a system to allow only admins (i.e. Ethereum accounts or smart contracts) to manage groups; +- a mechanism to save the [nullifier hashes](/docs/technical-reference/circuits#nullifier-hash) of each group and prevent double-signaling; +- a mechanism to allow Semaphore proofs generated with old Merkle roots to be verified for a certain period of time defined by the group admin. + +:::info +See our [deployed contracts](/docs/deployed-contracts) to find the addresses for your network. +:::: diff --git a/apps/docs/versioned_docs/version-V3/troubleshooting.mdx b/apps/docs/versioned_docs/version-V3/troubleshooting.mdx new file mode 100644 index 00000000..a23427ff --- /dev/null +++ b/apps/docs/versioned_docs/version-V3/troubleshooting.mdx @@ -0,0 +1,222 @@ +--- +sidebar_position: 11 +--- + +import Tabs from "@theme/Tabs" +import TabItem from "@theme/TabItem" + +# Troubleshooting + +If these suggestions do not work, feel free to ask in the [Semaphore Discussions](https://github.com/semaphore-protocol/semaphore/discussions) or in the `dev-chat` channel in the [Semaphore Discord](https://semaphore.pse.dev/discord). + +## Using Semaphore in the frontend + +Semaphore works with any JavaScript frontend framework, but the [`@semaphore-protocol/proof`](https://github.com/semaphore-protocol/semaphore/tree/main/packages/proof) package is using [snarkjs](https://github.com/iden3/snarkjs), which uses Node.js modules which are not compatible with frontend frameworks and there are some changes that we need to do to make it work on the client side. + +### Semaphore with Nextjs + +You will see an error like this: + +``` +Module not found: Can't resolve 'fs' +``` + +To solve this, in your `next.config.js` file, inside the `nextConfig` object, add: + +```javascript +webpack: (config, { isServer }) => { + if (!isServer) { + config.resolve.fallback = { + fs: false + } + } + + return config + } +``` + +Your `next.config.js` file would be something like this: + +```javascript +/** @type {import('next').NextConfig} */ +const nextConfig = { + reactStrictMode: true, + webpack: (config, { isServer }) => { + if (!isServer) { + config.resolve.fallback = { + fs: false + } + } + + return config + } +} + +module.exports = nextConfig +``` + +### Semaphore with React + Vite or Vuejs + Vite + +You will see an error like this: + +```bash +readman.js:43 Uncaught ReferenceError: process is not defined + at stringToBase64 (threadman.js:43:5) + at threadman.js:50:22 +``` + +To solve that: + +1- Install `@esbuild-plugins/node-globals-polyfill` and `@esbuild-plugins/node-modules-polyfill` + + + + +```bash +npm install @esbuild-plugins/node-globals-polyfill +``` + + + + +```bash +yarn add @esbuild-plugins/node-globals-polyfill +``` + + + + + + + +```bash +npm install @esbuild-plugins/node-modules-polyfill +``` + + + + +```bash +yarn add @esbuild-plugins/node-modules-polyfill +``` + + + + +2- Modify the `vite.config.ts` to add them: + +```typescript +import { NodeGlobalsPolyfillPlugin } from "@esbuild-plugins/node-globals-polyfill" +import { NodeModulesPolyfillPlugin } from "@esbuild-plugins/node-modules-polyfill" +``` + +and in `defineConfig` add: + +```typescript +optimizeDeps: { + esbuildOptions: { + // Enable esbuild polyfill plugins + plugins: [ + NodeGlobalsPolyfillPlugin({ + process: true, + buffer: true + }), + NodeModulesPolyfillPlugin() + ] + } +} +``` + +Your `vite.config.ts` should be something like: + +```typescript +import { fileURLToPath, URL } from "node:url" + +import { defineConfig } from "vite" +import vue from "@vitejs/plugin-vue" + +import { NodeGlobalsPolyfillPlugin } from "@esbuild-plugins/node-globals-polyfill" +import { NodeModulesPolyfillPlugin } from "@esbuild-plugins/node-modules-polyfill" + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [vue()], + resolve: { + alias: { + "@": fileURLToPath(new URL("./src", import.meta.url)) + } + }, + optimizeDeps: { + esbuildOptions: { + // Enable esbuild polyfill plugins + plugins: [ + NodeGlobalsPolyfillPlugin({ + process: true, + buffer: true + }), + NodeModulesPolyfillPlugin() + ] + } + } +}) +``` + +:::info +In case of React with Vite, if you see a red wavy underline on every Semaphore module which says `Could not find a declaration file for module ...`, change the `moduleResolution` from `bundler` to `Node` in the `tsconfig.json` file inside `compilerOptions`. + +Your `tsconfig.json` file would be something like this: + +```json +{ + "compilerOptions": { + "target": "ESNext", + "lib": ["DOM", "DOM.Iterable", "ESNext"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "Node", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] +} +``` + +::: + +## Semaphore Groups + +### Creating a Group + +When you create a group and the transaction is reverted, make sure that the group id you are using does not exist on the network you are using. + +To check that, you can use the [Semaphore CLI](https://github.com/semaphore-protocol/semaphore/tree/main/packages/cli) with the command `get-groups` and the network you are using and then, make sure that your group id is not part of that list. You can also use the [Semaphore explorer](https://explorer.semaphore.pse.dev/). + +## Semaphore Proofs + +### Transaction reverted when using the same external nullifier + +When you generate a proof using the same external nullifier you used to verify a proof before, the transaction will be reverted because that external nullifier was already used. If you want to send and verify several proofs from the same identity, you should use a different external nullifier each time you generate a proof. diff --git a/apps/docs/versioned_docs/version-V3/what-is-semaphore.md b/apps/docs/versioned_docs/version-V3/what-is-semaphore.md new file mode 100644 index 00000000..f2284691 --- /dev/null +++ b/apps/docs/versioned_docs/version-V3/what-is-semaphore.md @@ -0,0 +1,54 @@ +--- +id: introduction +title: What Is Semaphore? +sidebar_position: 1 +--- + +## Overview + +[Semaphore](https://github.com/semaphore-protocol/semaphore) is a [zero-knowledge](https://z.cash/technology/zksnarks) protocol that allows you to cast a signal (for example, a vote or endorsement) as a provable group member without revealing your identity. +Additionally, it provides a simple mechanism to prevent double-signaling. +Use cases include private voting, whistleblowing, anonymous DAOs and mixers. + +## Features + +With Semaphore, you can allow your users to do the following: + +1. [Create a Semaphore identity](/docs/guides/identities/). +2. [Add their Semaphore identity to a group (i.e. _Merkle tree_)](/docs/guides/groups/). +3. [Send a verifiable, anonymous signal (e.g a vote or endorsement)](/docs/guides/proofs/). + +When a user broadcasts a signal (for example: a vote), Semaphore zero-knowledge +proofs can ensure that the user has joined the group and hasn't already cast a signal with their nullifier. + +Semaphore uses on-chain Solidity contracts and off-chain JavaScript libraries that work in tandem. + +- Off chain, JavaScript libraries can be used to create identities, manage groups and generate proofs. +- On chain, Solidity contracts can be used to manage groups and verify proofs. + +## Developer benefits + +Semaphore is designed to be a simple and generic _privacy layer_ for decentralized applications (dApps) on Ethereum. It encourages modular application design, allowing dApp developers to choose and customize the on-chain and off-chain components they need. + +## About the code + +The core of the protocol is the [circuit logic](https://github.com/semaphore-protocol/semaphore/tree/main/packages/circuits/scheme.png). +In addition to circuits, +Semaphore provides [Solidity contracts](https://github.com/semaphore-protocol/semaphore/tree/main/packages/contracts) +and [JavaScript libraries](https://github.com/semaphore-protocol/semaphore#-packages) that allow developers to generate zero-knowledge proofs and verify them with minimal effort. + +### Trusted Setup Ceremony + +The [secure parameters](/docs/glossary#trusted-setup-files) for generating valid proofs with Semaphore circuits were generated in a [Trusted Setup Ceremony](https://storage.googleapis.com/trustedsetup-a86f4.appspot.com/semaphore/semaphore_top_index.html) that was completed with over 300 participants on [29 March 2022](https://etherscan.io/tx/0xec6dbe68883c7593c2bea82f55af18b3aeb5cc146e026d0083a9b3faa9aa0b65#eventlog). + +### Audits + +| Version | Auditors | Report | Scope | +| ------- | --------------------------------- | --------------------------------------------------------------------------------------------------------------------- | ------------------------ | +| v2.0.0 | [PSE](https://pse.dev/) | [Semaphore_2.0.0_Audit.pdf](https://github.com/semaphore-protocol/semaphore/files/9850441/Semaphore_2.0.0_Audit.pdf) | `circuits`, `contracts` | +| v2.5.0 | [PSE](https://pse.dev/) | [Semaphore_2.5.0_Audit.pdf](https://github.com/semaphore-protocol/semaphore/files/9845008/Semaphore_2.5.0_Audit.pdf) | `contracts`, `libraries` | +| v3.0.0 | [Veridise](https://veridise.com/) | [Semaphore_3.0.0_Audit.pdf](https://github.com/semaphore-protocol/semaphore/files/10513776/Semaphore_3.0.0_Audit.pdf) | `circuits`, `contracts` | + +:::info +If you are using one of the previous versions of Semaphore, see the [Semaphore V1](/docs/V1/introduction) or the [Semaphore V2](/docs/V2/introduction) documentation. +::: diff --git a/apps/docs/versioned_sidebars/version-V1-sidebars.json b/apps/docs/versioned_sidebars/version-V1-sidebars.json new file mode 100644 index 00000000..dd1208ba --- /dev/null +++ b/apps/docs/versioned_sidebars/version-V1-sidebars.json @@ -0,0 +1,8 @@ +{ + "version-V1/mySidebar": [ + { + "type": "autogenerated", + "dirName": "." + } + ] +} diff --git a/apps/docs/versioned_sidebars/version-V2-sidebars.json b/apps/docs/versioned_sidebars/version-V2-sidebars.json new file mode 100644 index 00000000..5272f916 --- /dev/null +++ b/apps/docs/versioned_sidebars/version-V2-sidebars.json @@ -0,0 +1,8 @@ +{ + "version-V2/mySidebar": [ + { + "type": "autogenerated", + "dirName": "." + } + ] +} diff --git a/apps/docs/versioned_sidebars/version-V3-sidebars.json b/apps/docs/versioned_sidebars/version-V3-sidebars.json new file mode 100644 index 00000000..34a4519b --- /dev/null +++ b/apps/docs/versioned_sidebars/version-V3-sidebars.json @@ -0,0 +1,8 @@ +{ + "version-V3/mySidebar": [ + { + "type": "autogenerated", + "dirName": "." + } + ] +} diff --git a/apps/docs/versions.json b/apps/docs/versions.json new file mode 100644 index 00000000..b2bc2bf5 --- /dev/null +++ b/apps/docs/versions.json @@ -0,0 +1 @@ +["V3", "V2", "V1"] diff --git a/yarn.lock.REMOVED.git-id b/yarn.lock.REMOVED.git-id index 5fc4ce33..03714e17 100644 --- a/yarn.lock.REMOVED.git-id +++ b/yarn.lock.REMOVED.git-id @@ -1 +1 @@ -c45775a0c55f5fc9e8080a674039e009855093af \ No newline at end of file +4ba159813ad0f51d80fadd4798aaf869af7f7985 \ No newline at end of file