chore(docs): add semaphore docs project

Former-commit-id: 5fac62ed44
This commit is contained in:
cedoor
2023-12-19 17:35:47 +01:00
parent fbd5d96760
commit 7cc273e01f
148 changed files with 10101 additions and 4 deletions

View File

@@ -22,7 +22,7 @@ circuits
# production
dist
build
docs
/docs
# misc
.DS_Store

6
.gitignore vendored
View File

@@ -63,7 +63,11 @@ node_modules/
# Production
build
dist
docs
/docs
# Docusaurus cache and generated files
.docusaurus
.cache-loader
# Hardhat
artifacts

View File

@@ -25,7 +25,7 @@ Verifier*.sol
# production
dist
build
docs
/docs
# github
.github/ISSUE_TEMPLATE

View File

@@ -0,0 +1 @@
443013b248927520dbfbe92135e304feb4c32181

View File

@@ -0,0 +1 @@
b3cadff6efb37a12712d12c2553ec703dbcaa4dd

5
apps/docs/.yarnrc.yml Normal file
View File

@@ -0,0 +1,5 @@
checksumBehavior: update
nodeLinker: node-modules
yarnPath: .yarn/releases/yarn-3.2.1.cjs

75
apps/docs/README.md Normal file
View File

@@ -0,0 +1,75 @@
<p align="center">
<h1 align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/semaphore-protocol/.github/blob/main/assets/semaphore-logo-light.svg">
<source media="(prefers-color-scheme: light)" srcset="https://github.com/semaphore-protocol/.github/blob/main/assets/semaphore-logo-dark.svg">
<img width="250" alt="Semaphore icon" src="https://github.com/semaphore-protocol/.github/blob/main/assets/semaphore-logo-dark.svg">
</picture>
</h1>
</p>
<p align="center">
<a href="https://github.com/semaphore-protocol" target="_blank">
<img src="https://img.shields.io/badge/project-Semaphore-blue.svg?style=flat-square">
</a>
<a href="https://github.com/semaphore-protocol/semaphore/blob/main/LICENSE">
<img alt="Github license" src="https://img.shields.io/github/license/semaphore-protocol/semaphore.svg?style=flat-square">
</a>
<a href="https://eslint.org/">
<img alt="Linter eslint" src="https://img.shields.io/badge/linter-eslint-8080f2?style=flat-square&logo=eslint" />
</a>
<a href="https://prettier.io/">
<img alt="Code style prettier" src="https://img.shields.io/badge/code%20style-prettier-f8bc45?style=flat-square&logo=prettier" />
</a>
</p>
<div align="center">
<h4>
<a href="https://github.com/semaphore-protocol/semaphore/blob/main/CONTRIBUTING.md">
👥 Contributing
</a>
<span>&nbsp;&nbsp;|&nbsp;&nbsp;</span>
<a href="https://github.com/semaphore-protocol/semaphore/blob/main/CODE_OF_CONDUCT.md">
🤝 Code of conduct
</a>
<span>&nbsp;&nbsp;|&nbsp;&nbsp;</span>
<a href="https://github.com/semaphore-protocol/semaphore/contribute">
🔎 Issues
</a>
<span>&nbsp;&nbsp;|&nbsp;&nbsp;</span>
<a href="https://semaphore.pse.dev/discord">
🗣️ Chat &amp; Support
</a>
</h4>
</div>
| 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=<Your GitHub username> 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.

View File

@@ -0,0 +1,3 @@
module.exports = {
presets: [require.resolve("@docusaurus/core/lib/babel/preset")]
}

View File

@@ -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:
//'<b>We are pleased to announce the release of <a target="_blank" rel="noopener noreferrer" href="https://github.com/semaphore-protocol/semaphore/releases/tag/v3.0.0">Semaphore V3</a> 🎉</b>',
//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
}
})
}

406
apps/docs/i18n/en/code.json Normal file
View File

@@ -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"
}
}

View File

@@ -0,0 +1,6 @@
{
"version.label": {
"message": "V1",
"description": "The label for version V1"
}
}

View File

@@ -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"
}
}

View File

@@ -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"
}
}

View File

@@ -0,0 +1,11 @@
{
"item.label.Whitepaper": {
"message": "Whitepaper"
},
"item.label.Documentation": {
"message": "Documentation"
},
"item.label.Github": {
"message": "Github"
}
}

406
apps/docs/i18n/es/code.json Normal file
View File

@@ -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"
}
}

View File

@@ -0,0 +1,6 @@
{
"version.label": {
"message": "V1",
"description": "The label for version V1"
}
}

View File

@@ -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.

View File

@@ -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.

View File

@@ -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

View File

@@ -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.

View File

@@ -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.

View File

@@ -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
```

View File

@@ -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.

View File

@@ -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 = <list of 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 }
)
```

View File

@@ -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 contracts 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.

View File

@@ -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"
}
}

View File

@@ -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)

View File

@@ -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) |

View File

@@ -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 relayers 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 <http://www.trusted-setup-pse.org>.
To learn more, see the [trusted setup ceremony](https://storage.googleapis.com/trustedsetup-a86f4.appspot.com/semaphore/semaphore_top_index.html).

View File

@@ -0,0 +1,4 @@
{
"label": "Guides",
"position": 2
}

View File

@@ -0,0 +1,122 @@
---
sidebar_position: 2
title: Groups
---
# Semaphore groups
<!--Working outline
- What is a group
- What do groups contain
- Identities
- Root
- What are they used for
- Create a group
- Use a group
- Add identities
- Remove identities
-->
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.

View File

@@ -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())
```

View File

@@ -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
```

View File

@@ -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/).

View File

@@ -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

View File

@@ -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.

View File

@@ -0,0 +1,4 @@
{
"label": "Technical reference",
"position": 4
}

View File

@@ -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.

View File

@@ -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.

View File

@@ -0,0 +1,4 @@
{
"label": "Use cases",
"position": 3
}

View File

@@ -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).

View File

@@ -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.
:::

View File

@@ -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"
}
}

View File

@@ -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)

View File

@@ -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
:::

View File

@@ -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.

View File

@@ -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 <http://www.trusted-setup-pse.org>.
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.

View File

@@ -0,0 +1,4 @@
{
"label": "Guides",
"position": 3
}

View File

@@ -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
<Tabs
defaultValue="npm"
groupId="package-managers"
values={[
{label: 'npm', value: 'npm'},
{label: 'Yarn', value: 'yarn'},
]}>
<TabItem value="npm">
```bash
npm install @semaphore-protocol/data
```
</TabItem>
<TabItem value="yarn">
```bash
yarn add @semaphore-protocol/data
```
</TabItem>
</Tabs>
## 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/<your-subgraph>/<your-version>"
)
```
### 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)
```
:::

View File

@@ -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:
<Tabs
defaultValue="npm"
groupId="package-managers"
values={[
{label: 'npm', value: 'npm'},
{label: 'Yarn', value: 'yarn'},
]}>
<TabItem value="npm">
```bash
npm install @semaphore-protocol/group
```
</TabItem>
<TabItem value="yarn">
```bash
yarn add @semaphore-protocol/group
```
</TabItem>
</Tabs>
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).
:::

View File

@@ -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:
<Tabs
defaultValue="npm"
groupId="package-managers"
values={[
{label: 'npm', value: 'npm'},
{label: 'Yarn', value: 'yarn'},
]}>
<TabItem value="npm">
```bash
npm install @semaphore-protocol/identity
```
</TabItem>
<TabItem value="yarn">
```bash
yarn add @semaphore-protocol/identity
```
</TabItem>
</Tabs>
### 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())
```

View File

@@ -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:
<Tabs
defaultValue="npm"
groupId="package-managers"
values={[
{label: 'npm', value: 'npm'},
{label: 'Yarn', value: 'yarn'},
]}>
<TabItem value="npm">
```bash
npm install @semaphore-protocol/proof
```
</TabItem>
<TabItem value="yarn">
```bash
yarn add @semaphore-protocol/proof
```
</TabItem>
</Tabs>
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).
:::

View File

@@ -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:
<Tabs
defaultValue="npm"
groupId="package-managers"
values={[
{label: 'npm', value: 'npm'},
{label: 'Yarn', value: 'yarn'},
]}>
<TabItem value="npm">
```bash
cd my-app
npm i
```
</TabItem>
<TabItem value="yarn">
```bash
cd my-app
yarn
```
</TabItem>
</Tabs>
## 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:
<Tabs
defaultValue="npm"
groupId="package-managers"
values={[
{label: 'npm', value: 'npm'},
{label: 'Yarn', value: 'yarn'},
]}>
<TabItem value="npm">
```bash
npm run compile
```
</TabItem>
<TabItem value="yarn">
```bash
yarn compile
```
</TabItem>
</Tabs>
### Pruebe los contratos
Pruebe sus contratos al correr:
<Tabs
defaultValue="npm"
groupId="package-managers"
values={[
{label: 'npm', value: 'npm'},
{label: 'Yarn', value: 'yarn'},
]}>
<TabItem value="npm">
```bash
npm test
```
</TabItem>
<TabItem value="yarn">
```bash
yarn test
```
</TabItem>
</Tabs>
Genere un reporte de la prueba de cobertura:
<Tabs
defaultValue="npm"
groupId="package-managers"
values={[
{label: 'npm', value: 'npm'},
{label: 'Yarn', value: 'yarn'},
]}>
<TabItem value="npm">
```bash
npm run test:coverage
```
</TabItem>
<TabItem value="yarn">
```bash
yarn test:coverage
```
</TabItem>
</Tabs>
O un reporte de la prueba de gas:
<Tabs
defaultValue="npm"
groupId="package-managers"
values={[
{label: 'npm', value: 'npm'},
{label: 'Yarn', value: 'yarn'},
]}>
<TabItem value="npm">
```bash
npm run test:report-gas
```
</TabItem>
<TabItem value="yarn">
```bash
yarn test:report-gas
```
</TabItem>
</Tabs>
### 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.
<Tabs
defaultValue="npm"
groupId="package-managers"
values={[
{label: 'npm', value: 'npm'},
{label: 'Yarn', value: 'yarn'},
]}>
<TabItem value="npm">
```bash
npm run deploy -- --semaphore <semaphore-address> --group <group-id> --network goerli
```
</TabItem>
<TabItem value="yarn">
```bash
yarn deploy --semaphore <semaphore-address> --group <group-id> --network goerli
```
</TabItem>
</Tabs>
:::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:
<Tabs
defaultValue="npm"
groupId="package-managers"
values={[
{label: 'npm', value: 'npm'},
{label: 'Yarn', value: 'yarn'},
]}>
<TabItem value="npm">
```bash
npm run dev
```
</TabItem>
<TabItem value="yarn">
```bash
yarn dev
```
</TabItem>
</Tabs>

View File

@@ -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á

View File

@@ -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.

View File

@@ -0,0 +1,4 @@
{
"label": "Technical reference",
"position": 4
}

View File

@@ -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.

View File

@@ -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.
::::

View File

@@ -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`
<Tabs
defaultValue="npm"
groupId="package-managers"
values={[
{label: 'npm', value: 'npm'},
{label: 'Yarn', value: 'yarn'},
]}>
<TabItem value="npm">
```bash
npm install @esbuild-plugins/node-globals-polyfill
```
</TabItem>
<TabItem value="yarn">
```bash
yarn add @esbuild-plugins/node-globals-polyfill
```
</TabItem>
</Tabs>
<Tabs
defaultValue="npm"
groupId="package-managers"
values={[
{label: 'npm', value: 'npm'},
{label: 'Yarn', value: 'yarn'},
]}>
<TabItem value="npm">
```bash
npm install @esbuild-plugins/node-modules-polyfill
```
</TabItem>
<TabItem value="yarn">
```bash
yarn add @esbuild-plugins/node-modules-polyfill
```
</TabItem>
</Tabs>
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.

View File

@@ -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).
:::

View File

@@ -0,0 +1,11 @@
{
"item.label.Whitepaper": {
"message": "Whitepaper"
},
"item.label.Documentation": {
"message": "Documentación"
},
"item.label.Github": {
"message": "Github"
}
}

50
apps/docs/package.json Normal file
View File

@@ -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"
]
}
}

29
apps/docs/sidebars.js Normal file
View File

@@ -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'],
},
],
*/
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,9 @@
.illustrationHero {
> path {
fill: var(--ifm-color-primary);
}
> path:first-child {
fill: #ff975d;
}
}

View File

@@ -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 (
<Link className={styles.linkButton} href={href} target="_blank">
<div className={styles.buttonText}>{children}</div>
<div className={styles.buttonIcon}>
<IconArrowRight />
</div>
</Link>
)
}

View File

@@ -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;
}

View File

@@ -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 (
<Link className={styles.outlineLinkButton} href={href} target="_blank">
<div>{children}</div>
<div className={styles.buttonIcon}>
<IconArrowTopRight />
</div>
</Link>
)
}

View File

@@ -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;
}

View File

@@ -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 (
<svg
className="custom-icon"
width={width}
height={height}
viewBox="0 0 10 15"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M0.585938 1.46206L2.00014 0.0478516L9.07124 7.11889L2.00014 14.19L0.585938 12.7758L6.24284 7.11889L0.585938 1.46206Z" />
</svg>
)
}

View File

@@ -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 (
<svg
className="custom-icon"
width={width}
height={height}
viewBox="0 0 14 14"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M5.85868 0.495117L5.85398 2.49512L10.4116 2.50578L0.808105 12.09L2.2209 13.5056L11.8507 3.89516L11.8399 8.50917L13.8399 8.51377L13.8587 0.513817L5.85868 0.495117Z" />
</svg>
)
}

View File

@@ -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 (
<svg
className="custom-icon"
width={width}
height={height}
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M19 9C19 11.3787 17.8135 13.4804 16 14.7453V22H13.4142L12 20.5858L10.5858 22H8V14.7453C6.18652 13.4804 5 11.3787 5 9C5 5.13401 8.13401 2 12 2C15.866 2 19 5.13401 19 9ZM17 9C17 11.7614 14.7614 14 12 14C9.23858 14 7 11.7614 7 9C7 6.23858 9.23858 4 12 4C14.7614 4 17 6.23858 17 9ZM10 19.7573L12 17.7573L14 19.7574V15.7101C13.3663 15.8987 12.695 16 12 16C11.305 16 10.6337 15.8987 10 15.7101V19.7573Z"
/>
</svg>
)
}

View File

@@ -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 (
<svg
className="custom-icon"
width={width}
height={width}
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M10.2427 13.6568L7.41421 10.8284L6 12.2426L10.2427 16.4853L17.3137 9.41421L15.8995 8L10.2427 13.6568Z" />
<circle cx="12" cy="12" r="11.15" strokeWidth="1.7" />
</svg>
)
}

View File

@@ -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 (
<svg
className="custom-icon"
width={width}
height={height}
viewBox="0 0 18 22"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M7 0C7.552 0 8 0.4928 8 1.1V5.5C8 6.1072 7.552 6.6 7 6.6H5V8.8H10V7.7C10 7.0928 10.448 6.6 11 6.6H17C17.552 6.6 18 7.0928 18 7.7V12.1C18 12.7072 17.552 13.2 17 13.2H11C10.448 13.2 10 12.7072 10 12.1V11H5V17.6H10V16.5C10 15.8928 10.448 15.4 11 15.4H17C17.552 15.4 18 15.8928 18 16.5V20.9C18 21.5072 17.552 22 17 22H11C10.448 22 10 21.5072 10 20.9V19.8H4C3.448 19.8 3 19.3072 3 18.7V6.6H1C0.448 6.6 0 6.1072 0 5.5V1.1C0 0.4928 0.448 0 1 0H7ZM16 17.6H12V19.8H16V17.6ZM16 8.8H12V11H16V8.8ZM6 2.2H2V4.4H6V2.2Z" />
</svg>
)
}

View File

@@ -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 (
<svg
className="custom-icon"
width={width}
height={height}
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M16 12C16 14.2091 14.2091 16 12 16C9.79086 16 8 14.2091 8 12C8 9.79086 9.79086 8 12 8C14.2091 8 16 9.79086 16 12ZM14 12C14 13.1046 13.1046 14 12 14C10.8954 14 10 13.1046 10 12C10 10.8954 10.8954 10 12 10C13.1046 10 14 10.8954 14 12Z"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M11.9999 3C17.5914 3 22.2897 6.82432 23.6218 12C22.2897 17.1757 17.5914 21 11.9999 21C6.40836 21 1.71006 17.1757 0.37793 12C1.71006 6.82432 6.40836 3 11.9999 3ZM11.9999 19C7.52431 19 3.7312 16.0581 2.45711 12C3.7312 7.94186 7.52431 5 11.9999 5C16.4755 5 20.2686 7.94186 21.5427 12C20.2686 16.0581 16.4755 19 11.9999 19Z"
/>
</svg>
)
}

View File

@@ -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 (
<svg
className="custom-icon"
width={width}
height={height}
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M9.34178 18.7824L7.41078 18.2644L8.19778 15.3254C7.01975 14.8909 5.9249 14.2577 4.96078 13.4534L2.80778 15.6074L1.39278 14.1924L3.54678 12.0394C2.33086 10.5831 1.51387 8.83611 1.17578 6.96935L3.14378 6.61035C3.90278 10.8124 7.57878 14.0004 11.9998 14.0004C16.4198 14.0004 20.0968 10.8124 20.8558 6.61035L22.8238 6.96835C22.4861 8.83537 21.6695 10.5827 20.4538 12.0394L22.6068 14.1924L21.1918 15.6074L19.0388 13.4534C18.0747 14.2577 16.9798 14.8909 15.8018 15.3254L16.5888 18.2654L14.6578 18.7824L13.8698 15.8424C12.6321 16.0544 11.3674 16.0544 10.1298 15.8424L9.34178 18.7824Z" />
</svg>
)
}

View File

@@ -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 (
<svg
className="custom-icon"
width={width}
height={height}
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M4 21H6V11H12V13H20V5H13V3H4V21ZM12 5H6V9H13V11H18V7H12V5Z"
/>
</svg>
)
}

View File

@@ -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 (
<svg
className="custom-icon"
width={width}
height={height}
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M10.305 12.45C9.64859 12.45 9.01906 12.1892 8.55491 11.7251C8.09076 11.2609 7.83 10.6314 7.83 9.975C7.83 9.31859 8.09076 8.68906 8.55491 8.22491C9.01906 7.76076 9.64859 7.5 10.305 7.5C10.9614 7.5 11.5909 7.76076 12.0551 8.22491C12.5192 8.68906 12.78 9.31859 12.78 9.975C12.78 10.6314 12.5192 11.2609 12.0551 11.7251C11.5909 12.1892 10.9614 12.45 10.305 12.45ZM10.8 21.5228V17.84C10.8 17.3032 10.9584 16.8093 11.2444 16.3682C10.2639 16.2287 9.26515 16.2953 8.31186 16.5637C7.35858 16.8321 6.47177 17.2964 5.7081 17.9269C6.91048 19.7062 8.72115 20.9853 10.8 21.5239V21.5228ZM4.6983 15.926C6.30484 14.7371 8.25139 14.0969 10.25 14.1C11.3973 14.1 12.4973 14.3068 13.5137 14.6852C14.4795 14.3079 15.6312 14.1 16.85 14.1C18.676 14.1 20.3535 14.5664 21.4766 15.3716C21.8479 14.0405 21.9005 12.6406 21.6301 11.2853C21.3596 9.9301 20.7738 8.65759 19.9201 7.57088C19.0664 6.48417 17.9687 5.6138 16.716 5.03026C15.4633 4.44673 14.0908 4.16643 12.7095 4.21207C11.3283 4.2577 9.97731 4.628 8.76585 5.29296C7.55439 5.95792 6.51657 6.89885 5.73644 8.03955C4.9563 9.18025 4.45579 10.4886 4.27543 11.8588C4.09507 13.2289 4.23994 14.6223 4.6983 15.926ZM20.5669 17.4946C20.1346 16.9083 18.6881 16.3 16.85 16.3C14.6434 16.3 13 17.1767 13 17.84V21.8C14.5266 21.8008 16.0272 21.4043 17.3541 20.6494C18.681 19.8946 19.7886 18.8073 20.568 17.4946H20.5669ZM13 24C6.9247 24 2 19.0753 2 13C2 6.9247 6.9247 2 13 2C19.0753 2 24 6.9247 24 13C24 19.0753 19.0753 24 13 24ZM16.85 13.55C16.2665 13.55 15.7069 13.3182 15.2944 12.9056C14.8818 12.4931 14.65 11.9335 14.65 11.35C14.65 10.7665 14.8818 10.2069 15.2944 9.79437C15.7069 9.38179 16.2665 9.15 16.85 9.15C17.4335 9.15 17.9931 9.38179 18.4056 9.79437C18.8182 10.2069 19.05 10.7665 19.05 11.35C19.05 11.9335 18.8182 12.4931 18.4056 12.9056C17.9931 13.3182 17.4335 13.55 16.85 13.55Z" />
</svg>
)
}

View File

@@ -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 (
<svg
className="custom-icon"
width={width}
height={height}
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M12.2256 2.00253C9.59172 1.94346 6.93894 2.9189 4.92893 4.92891C1.02369 8.83415 1.02369 15.1658 4.92893 19.071C8.83418 22.9763 15.1658 22.9763 19.0711 19.071C21.0811 17.061 22.0565 14.4082 21.9975 11.7743C21.9796 10.9772 21.8669 10.1818 21.6595 9.40643C21.0933 9.9488 20.5078 10.4276 19.9163 10.8425C18.5649 11.7906 17.1826 12.4053 15.9301 12.6837C14.0241 13.1072 12.7156 12.7156 12 12C11.2844 11.2844 10.8928 9.97588 11.3163 8.0699C11.5947 6.81738 12.2094 5.43511 13.1575 4.08368C13.5724 3.49221 14.0512 2.90664 14.5935 2.34046C13.8182 2.13305 13.0228 2.02041 12.2256 2.00253ZM17.6569 17.6568C18.9081 16.4056 19.6582 14.8431 19.9072 13.2186C16.3611 15.2643 12.638 15.4664 10.5858 13.4142C8.53361 11.362 8.73568 7.63895 10.7814 4.09281C9.1569 4.34184 7.59434 5.09193 6.34315 6.34313C3.21895 9.46732 3.21895 14.5326 6.34315 17.6568C9.46734 20.781 14.5327 20.781 17.6569 17.6568Z"
/>
</svg>
)
}

View File

@@ -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 (
<svg
className="custom-icon"
width={width}
height={height}
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M16 9C16 11.2091 14.2091 13 12 13C9.79086 13 8 11.2091 8 9C8 6.79086 9.79086 5 12 5C14.2091 5 16 6.79086 16 9ZM14 9C14 10.1046 13.1046 11 12 11C10.8954 11 10 10.1046 10 9C10 7.89543 10.8954 7 12 7C13.1046 7 14 7.89543 14 9Z"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M12 1C5.92487 1 1 5.92487 1 12C1 18.0751 5.92487 23 12 23C18.0751 23 23 18.0751 23 12C23 5.92487 18.0751 1 12 1ZM3 12C3 14.0902 3.71255 16.014 4.90798 17.5417C6.55245 15.3889 9.14627 14 12.0645 14C14.9448 14 17.5092 15.3531 19.1565 17.4583C20.313 15.9443 21 14.0524 21 12C21 7.02944 16.9706 3 12 3C7.02944 3 3 7.02944 3 12ZM12 21C9.84977 21 7.87565 20.2459 6.32767 18.9878C7.59352 17.1812 9.69106 16 12.0645 16C14.4084 16 16.4833 17.1521 17.7538 18.9209C16.1939 20.2191 14.1881 21 12 21Z"
/>
</svg>
)
}

View File

@@ -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 (
<svg
className="custom-icon"
width={width}
height={height}
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M12 18C10.4087 18 8.88258 17.3679 7.75736 16.2426C6.63214 15.1174 6 13.5913 6 12C6 10.4087 6.63214 8.88258 7.75736 7.75736C8.88258 6.63214 10.4087 6 12 6C13.5913 6 15.1174 6.63214 16.2426 7.75736C17.3679 8.88258 18 10.4087 18 12C18 13.5913 17.3679 15.1174 16.2426 16.2426C15.1174 17.3679 13.5913 18 12 18ZM12 16C13.0609 16 14.0783 15.5786 14.8284 14.8284C15.5786 14.0783 16 13.0609 16 12C16 10.9391 15.5786 9.92172 14.8284 9.17157C14.0783 8.42143 13.0609 8 12 8C10.9391 8 9.92172 8.42143 9.17157 9.17157C8.42143 9.92172 8 10.9391 8 12C8 13.0609 8.42143 14.0783 9.17157 14.8284C9.92172 15.5786 10.9391 16 12 16ZM11 1H13V4H11V1ZM11 20H13V23H11V20ZM3.515 4.929L4.929 3.515L7.05 5.636L5.636 7.05L3.515 4.93V4.929ZM16.95 18.364L18.364 16.95L20.485 19.071L19.071 20.485L16.95 18.364ZM19.071 3.514L20.485 4.929L18.364 7.05L16.95 5.636L19.071 3.515V3.514ZM5.636 16.95L7.05 18.364L4.929 20.485L3.515 19.071L5.636 16.95ZM23 11V13H20V11H23ZM4 11V13H1V11H4Z" />
</svg>
)
}

View File

@@ -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 (
<svg
className="custom-icon"
width={width}
height={height}
viewBox="0 0 20 18"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M10 4C10 6.20914 8.2091 8 6 8C3.79086 8 2 6.20914 2 4C2 1.79086 3.79086 0 6 0C8.2091 0 10 1.79086 10 4ZM8 4C8 5.10457 7.10457 6 6 6C4.89543 6 4 5.10457 4 4C4 2.89543 4.89543 2 6 2C7.10457 2 8 2.89543 8 4ZM10 12C10 11.4477 9.5523 11 9 11H3C2.44772 11 2 11.4477 2 12V18H0V12C0 10.3431 1.34315 9 3 9H9C10.6569 9 12 10.3431 12 12V18H10V12ZM16 4H18V6H20V8H18V10H16V8H14V6H16V4ZM18 13H16H14V15H16H18H20V13H18Z"
/>
</svg>
)
}

View File

@@ -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;
}
}

View File

@@ -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 (
<Layout
title={`Semaphore Protocol`}
description="A zero-knowledge protocol for anonymous signalling on Ethereum."
>
<div className={styles.container}>
<div className={styles.jumbotron}>
<div>
<h1>
{translate({
id: "jumbotron.title"
})}
</h1>
<p>
{translate({
id: "jumbotron.description"
})}
</p>
<div>
<LinkButton href="./docs/quick-setup">
{translate({
id: "quick-setup.button"
})}
</LinkButton>
<LinkButton href="https://github.com/semaphore-protocol/boilerplate">
{translate({
id: "boilerplate.button"
})}
</LinkButton>
</div>
</div>
<IllustrationHero />
</div>
<div className={styles.components}>
<h3>
{translate({
id: "components.description"
})}
</h3>
<div>
<OutlineLinkButton href="https://github.com/semaphore-protocol/semaphore/tree/main/packages/contracts">
{translate({
id: "components.button.solidity"
})}
</OutlineLinkButton>
<OutlineLinkButton href="https://github.com/semaphore-protocol/semaphore/tree/main/packages/circuits">
{translate({
id: "components.button.circuits"
})}
</OutlineLinkButton>
<OutlineLinkButton href="https://github.com/semaphore-protocol/semaphore#-packages">
{translate({
id: "components.button.libraries"
})}
</OutlineLinkButton>
</div>
</div>
<div className={styles.section}>
<div>
<div>
<h2>
{translate({
id: "section.identities.title"
})}
</h2>
<p>
{translate({
id: "section.identities.description"
})}
</p>
<LinkButton href="/docs/guides/identities">
{translate({
id: "section.identities.link"
})}
</LinkButton>
</div>
<div>
<CodeBlock language="ts">
{`import { Identity } from "@semaphore-protocol/identity"
const { trapdoor, nullifier, commitment } = new Identity()`}
</CodeBlock>
</div>
</div>
<div>
<div>
<IconEyeClose />
<h3>
{translate({
id: "section.identities.box1.title"
})}
</h3>
<p>
{translate({
id: "section.identities.box1.description"
})}
</p>
</div>
<div>
<IconEye />
<h3>
{translate({
id: "section.identities.box2.title"
})}
</h3>
<p>
{translate({
id: "section.identities.box2.description"
})}
</p>
</div>
<div>
<IconProfile />
<h3>
{translate({
id: "section.identities.box3.title"
})}
</h3>
<p>
{translate({
id: "section.identities.box3.description"
})}
</p>
</div>
</div>
</div>
<div className={styles.divider} />
<div className={styles.section}>
<div>
<div>
<h2>
{translate({
id: "section.groups.title"
})}
</h2>
<p>
{translate({
id: "section.groups.description"
})}
</p>
<LinkButton href="/docs/guides/groups">
{translate({
id: "section.groups.link"
})}
</LinkButton>
</div>
<div>
<CodeBlock language="ts">
{`import { Group } from "@semaphore-protocol/group"
const group = new Group()
group.addMember(commitment)`}
</CodeBlock>
</div>
</div>
<div>
<div>
<IconConnections />
<h3>
{translate({
id: "section.groups.box1.title"
})}
</h3>
<p>
{translate({
id: "section.groups.box1.description"
})}
</p>
</div>
<div>
<IconGroup />
<h3>
{translate({
id: "section.groups.box2.title"
})}
</h3>
<p>
{translate({
id: "section.groups.box2.description"
})}
</p>
</div>
<div>
<IconUnion />
<h3>
{translate({
id: "section.groups.box3.title"
})}
</h3>
<p>
{translate({
id: "section.groups.box3.description"
})}
</p>
</div>
</div>
</div>
<div className={styles.divider} />
<div className={styles.section}>
<div>
<div>
<h2>
{translate({
id: "section.proofs.title"
})}
</h2>
<p>
{translate({
id: "section.proofs.description"
})}
</p>
<LinkButton href="/docs/guides/proofs">
{translate({
id: "section.proofs.link"
})}
</LinkButton>
</div>
<div>
<CodeBlock language="ts">
{`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)`}
</CodeBlock>
</div>
</div>
<div>
<div>
<IconAwards />
<h3>
{translate({
id: "section.proofs.box1.title"
})}
</h3>
<p>
{translate({
id: "section.proofs.box1.description"
})}
</p>
</div>
<div>
<IconFlag />
<h3>
{translate({
id: "section.proofs.box2.title"
})}
</h3>
<p>
{translate({
id: "section.proofs.box2.description"
})}
</p>
</div>
<div>
<IconCheck />
<h3>
{translate({
id: "section.proofs.box3.title"
})}
</h3>
<p>
{translate({
id: "section.proofs.box3.description"
})}
</p>
</div>
</div>
</div>
</div>
</Layout>
)
}

View File

@@ -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);
}
}

View File

@@ -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 (
<footer className={clsx("footer")}>
<div className={clsx("container container-fluid", styles.container)}>
<div>
<div>
<h3>
{translate({
id: "footer.left.title"
})}
</h3>
<p>
{translate({
id: "footer.left.description"
})}
</p>
<Link href="https://pse.dev" target="_blank">
pse.dev
</Link>
</div>
<div>
<div>
<h3>
{translate({
id: "footer.right.usedby.title"
})}
</h3>
<Link href="https://github.com/Unirep" target="_blank">
{translate({
id: "footer.right.usedby.link1"
})}
</Link>
<Link href="https://interep.link/" target="_blank">
{translate({
id: "footer.right.usedby.link2"
})}
</Link>
</div>
<div>
<h3>
{translate({
id: "footer.right.learn.title"
})}
</h3>
<Link href="https://semaphore.pse.dev/github" target="_blank">
{translate({
id: "footer.right.learn.link1"
})}
</Link>
<Link href="/docs/introduction" target="_blank">
{translate({
id: "footer.right.learn.link2"
})}
</Link>
</div>
<div>
<h3>
{translate({
id: "footer.right.connect.title"
})}
</h3>
<Link href="https://semaphore.pse.dev/discord" target="_blank">
{translate({
id: "footer.right.connect.link1"
})}
</Link>
<Link href="https://twitter.com/PrivacyScaling" target="_blank">
{translate({
id: "footer.right.connect.link2"
})}
</Link>
</div>
</div>
</div>
<hr />
<div>
<p>
{translate({
id: "footer.copyright"
})}
</p>
<Logo
style={{ marginRight: -8 }}
className="navbar__brand"
imageClassName="navbar__logo"
titleClassName="navbar__title text--truncate"
/>
</div>
</div>
</footer>
)
}
export default React.memo(Footer)

View File

@@ -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);
}
}

View File

@@ -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 (
<span style={{ marginLeft: "0.6rem", position: "relative", top: "1px" }}>
<IconArrowTopRight />
</span>
)
}
export default IconExternalLink

View File

@@ -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") && <SearchOriginal {...props} />
}

View File

@@ -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")
)) && <OriginalNavBarItem {...props} />
)
}

View File

@@ -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 (
<div
className={clsx(styles.toggle, className, {
[styles.toggleChecked]: checked,
[styles.toggleFocused]: focused,
[styles.toggleDisabled]: disabled
})}
>
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events */}
<div className={styles.toggleTrack} role="button" tabIndex={-1} onClick={() => inputRef.current?.click()}>
<div className={styles.toggleTrackCheck}>
<span className={styles.toggleIcon}>
<IconMoon />
</span>
</div>
<div className={styles.toggleTrackX}>
<span className={styles.toggleIcon}>
<IconSun />
</span>
</div>
<div className={styles.toggleTrackThumb} />
</div>
<input
ref={inputRef}
checked={checked}
type="checkbox"
className={styles.toggleScreenReader}
aria-label="Switch between dark and light mode"
onChange={onChange}
onClick={() => setChecked(!checked)}
onFocus={() => setFocused(true)}
onBlur={() => setFocused(false)}
onKeyDown={(e) => {
if (e.key === "Enter") {
inputRef.current?.click()
}
}}
/>
</div>
)
})
export default function Toggle(props) {
const isBrowser = useIsBrowser()
return <ToggleComponent disabled={!isBrowser} {...props} />
}

View File

@@ -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);
}
}

1
apps/docs/src/types/global.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
declare module "*.scss"

0
apps/docs/static/.nojekyll vendored Normal file
View File

1
apps/docs/static/CNAME vendored Normal file
View File

@@ -0,0 +1 @@
semaphore.pse.dev

BIN
apps/docs/static/img/favicon.ico vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1,3 @@
<svg width="307" height="297" viewBox="0 0 307 297" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M79.6904 63.6726L191.365 0.301019C191.901 -0.00282284 192.535 -0.0804208 193.128 0.0854393L304.479 31.2364C306.468 31.7928 306.784 34.4801 304.978 35.482L160.442 115.672C159.321 116.294 158.921 117.709 159.549 118.826L223.992 233.345C224.619 234.459 224.223 235.87 223.108 236.494L115.628 296.705C115.096 297.004 114.467 297.079 113.878 296.915L1.69033 265.623C-0.304074 265.066 -0.616403 262.37 1.19806 261.372L142.351 183.779C143.479 183.159 143.883 181.736 143.248 180.616L78.8201 66.8205C78.1918 65.7108 78.5813 64.3019 79.6904 63.6726ZM104.391 68.5965L180.084 25.8849C182.163 24.7122 184.438 27.076 183.188 29.1083L140.865 97.8769C140.143 99.0502 138.561 99.3349 137.475 98.487L104.105 72.43C102.806 71.4157 102.956 69.4065 104.391 68.5965ZM121.115 275.355L197.913 232.02C199.702 231.01 199.382 228.342 197.406 227.784L137.532 210.86C136.302 210.512 135.024 211.229 134.679 212.459L117.755 272.717C117.198 274.702 119.32 276.367 121.115 275.355Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,3 @@
<svg width="307" height="297" viewBox="0 0 307 297" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M79.6904 63.6726L191.365 0.301019C191.901 -0.00282284 192.535 -0.0804208 193.128 0.0854393L304.479 31.2364C306.468 31.7928 306.784 34.4801 304.978 35.482L160.442 115.672C159.321 116.294 158.921 117.709 159.549 118.826L223.992 233.345C224.619 234.459 224.223 235.87 223.108 236.494L115.628 296.705C115.096 297.004 114.467 297.079 113.878 296.915L1.69033 265.623C-0.304074 265.066 -0.616403 262.37 1.19806 261.372L142.351 183.779C143.479 183.159 143.883 181.736 143.248 180.616L78.8201 66.8205C78.1918 65.7108 78.5813 64.3019 79.6904 63.6726ZM104.391 68.5965L180.084 25.8849C182.163 24.7122 184.438 27.076 183.188 29.1083L140.865 97.8769C140.143 99.0502 138.561 99.3349 137.475 98.487L104.105 72.43C102.806 71.4157 102.956 69.4065 104.391 68.5965ZM121.115 275.355L197.913 232.02C199.702 231.01 199.382 228.342 197.406 227.784L137.532 210.86C136.302 210.512 135.024 211.229 134.679 212.459L117.755 272.717C117.198 274.702 119.32 276.367 121.115 275.355Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.5 KiB

BIN
apps/docs/static/whitepaper-v1.pdf vendored Normal file

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More