mirror of
https://github.com/semaphore-protocol/semaphore.git
synced 2026-01-09 14:48:12 -05:00
@@ -22,7 +22,7 @@ circuits
|
||||
# production
|
||||
dist
|
||||
build
|
||||
docs
|
||||
/docs
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -63,7 +63,11 @@ node_modules/
|
||||
# Production
|
||||
build
|
||||
dist
|
||||
docs
|
||||
/docs
|
||||
|
||||
# Docusaurus cache and generated files
|
||||
.docusaurus
|
||||
.cache-loader
|
||||
|
||||
# Hardhat
|
||||
artifacts
|
||||
|
||||
@@ -25,7 +25,7 @@ Verifier*.sol
|
||||
# production
|
||||
dist
|
||||
build
|
||||
docs
|
||||
/docs
|
||||
|
||||
# github
|
||||
.github/ISSUE_TEMPLATE
|
||||
|
||||
1
apps/docs/.yarn/install-state.gz.REMOVED.git-id
Normal file
1
apps/docs/.yarn/install-state.gz.REMOVED.git-id
Normal file
@@ -0,0 +1 @@
|
||||
443013b248927520dbfbe92135e304feb4c32181
|
||||
1
apps/docs/.yarn/releases/yarn-3.2.1.cjs.REMOVED.git-id
vendored
Normal file
1
apps/docs/.yarn/releases/yarn-3.2.1.cjs.REMOVED.git-id
vendored
Normal file
@@ -0,0 +1 @@
|
||||
b3cadff6efb37a12712d12c2553ec703dbcaa4dd
|
||||
5
apps/docs/.yarnrc.yml
Normal file
5
apps/docs/.yarnrc.yml
Normal 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
75
apps/docs/README.md
Normal 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> | </span>
|
||||
<a href="https://github.com/semaphore-protocol/semaphore/blob/main/CODE_OF_CONDUCT.md">
|
||||
🤝 Code of conduct
|
||||
</a>
|
||||
<span> | </span>
|
||||
<a href="https://github.com/semaphore-protocol/semaphore/contribute">
|
||||
🔎 Issues
|
||||
</a>
|
||||
<span> | </span>
|
||||
<a href="https://semaphore.pse.dev/discord">
|
||||
🗣️ Chat & 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.
|
||||
3
apps/docs/babel.config.js
Normal file
3
apps/docs/babel.config.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
presets: [require.resolve("@docusaurus/core/lib/babel/preset")]
|
||||
}
|
||||
102
apps/docs/docusaurus.config.js
Normal file
102
apps/docs/docusaurus.config.js
Normal 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
406
apps/docs/i18n/en/code.json
Normal 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"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"version.label": {
|
||||
"message": "V1",
|
||||
"description": "The label for version V1"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
11
apps/docs/i18n/en/docusaurus-theme-classic/navbar.json
Normal file
11
apps/docs/i18n/en/docusaurus-theme-classic/navbar.json
Normal 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
406
apps/docs/i18n/es/code.json
Normal 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"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"version.label": {
|
||||
"message": "V1",
|
||||
"description": "The label for version V1"
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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
|
||||
```
|
||||
@@ -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.
|
||||
@@ -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 }
|
||||
)
|
||||
```
|
||||
@@ -0,0 +1,105 @@
|
||||
---
|
||||
id: introduction
|
||||
title: What Is Semaphore?
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
[Semaphore](https://github.com/appliedzkp/semaphore) is a zero-knowledge gadget
|
||||
which allows Ethereum users to prove their membership of a set which they had
|
||||
previously joined without revealing their original identity. At the same time,
|
||||
it allows users to signal their endorsement of an arbitrary string. It is
|
||||
designed to be a simple and generic privacy layer for Ethereum dApps. Use cases
|
||||
include private voting, whistleblowing, mixers, and anonymous authentication.
|
||||
Finally, it provides a simple built-in mechanism to prevent double-signalling
|
||||
or double-spending.
|
||||
|
||||
This gadget comprises of smart contracts and
|
||||
[zero-knowledge](https://z.cash/technology/zksnarks/) components which work in
|
||||
tandem. The Semaphore smart contract handles state, permissions, and proof
|
||||
verification on-chain. The zero-knowledge components work off-chain to allow
|
||||
the user to generate proofs, which allow the smart contract to update its state
|
||||
if these proofs are valid.
|
||||
|
||||
For a formal description of Semaphore and its underlying cryptographic
|
||||
mechanisms, also see this document
|
||||
[here](https://github.com/appliedzkp/semaphore/tree/master/spec).
|
||||
|
||||
Semaphore is designed for smart contract and dApp developers, not end users.
|
||||
Developers should abstract its features away in order to provide user-friendly
|
||||
privacy.
|
||||
|
||||
Try a simple demo [here](https://weijiekoh.github.io/semaphore-ui/) or read a
|
||||
high-level description of Semaphore
|
||||
[here](https://medium.com/coinmonks/to-mixers-and-beyond-presenting-semaphore-a-privacy-gadget-built-on-ethereum-4c8b00857c9b).
|
||||
|
||||
## Basic features
|
||||
|
||||
In sum, Semaphore provides the ability to:
|
||||
|
||||
1. Register an identity in a smart contract, and then:
|
||||
|
||||
2. Broadcast a signal:
|
||||
|
||||
- Anonymously prove that their identity is in the set of registered
|
||||
identities, and at the same time:
|
||||
|
||||
- Publicly store an arbitrary string in the contract, if and only if that
|
||||
string is unique to the user and the contract’s current external
|
||||
nullifier, which is a unique value akin to a topic. This means that
|
||||
double-signalling the same message under the same external nullifier is
|
||||
not possible.
|
||||
|
||||
### External nullifiers
|
||||
|
||||
Think of an external nullifier as a voting booth where each user may only cast
|
||||
one vote. If they try to cast a second vote a the same booth, that vote is
|
||||
invalid.
|
||||
|
||||
An external nullifier is any 29-byte value. Semaphore always starts with one
|
||||
external nullifier, which is set upon contract deployment. The owner of the
|
||||
Semaphore contract may add more external nullifiers, deactivate, or reactivate
|
||||
existing ones.
|
||||
|
||||
The first time a particular user broadcasts a signal to an active external
|
||||
nullifier `n`, and if the user's proof of membership of the set of registered
|
||||
users is valid, the transaction will succeed. The second time she does so to
|
||||
the same `n`, however, her transaction will fail.
|
||||
|
||||
Additionally, all signals broadcast transactions to a deactivated external
|
||||
nullifier will fail.
|
||||
|
||||
Each client application must use the above features of Semaphore in a unique
|
||||
way to achieve its privacy goals. A mixer, for instance, would use one external
|
||||
nullifier as such:
|
||||
|
||||
| Signal | External nullifier |
|
||||
| ----------------------------------------------------------------------------- | ---------------------------- |
|
||||
| The hash of the recipient's address, relayer's address, and the relayer's fee | The mixer contract's address |
|
||||
|
||||
This allows anonymous withdrawals of funds (via a transaction relayer, who is
|
||||
rewarded with a fee), and prevents double-spending as there is only one
|
||||
external nullifier.
|
||||
|
||||
An anonymous voting app would be configured differently:
|
||||
|
||||
| Signal | External nullifier |
|
||||
| ----------------------------------- | ------------------------ |
|
||||
| The hash of the respondent's answer | The hash of the question |
|
||||
|
||||
This allows any user to vote with an arbitary response (e.g. yes, no, or maybe)
|
||||
to any question. The user, however, can only vote once per question.
|
||||
|
||||
## About the code
|
||||
|
||||
This repository contains the code for Semaphore's contracts written in
|
||||
Soliidty, and zk-SNARK circuits written in
|
||||
[circom](https://github.com/iden3/circom). It also contains Typescript code to
|
||||
execute tests.
|
||||
|
||||
The code has been audited by ABDK Consulting. Their suggested security and
|
||||
efficiency fixes have been applied.
|
||||
|
||||
A multi-party computation to produce the zk-SNARK proving and verification keys
|
||||
for Semaphore will begin in the near future.
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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) |
|
||||
@@ -0,0 +1,64 @@
|
||||
---
|
||||
sidebar_position: 7
|
||||
---
|
||||
|
||||
# Glossary
|
||||
|
||||
## Semaphore identity
|
||||
|
||||
The identity of a user in the Semaphore protocol.
|
||||
An identity contains the following three values:
|
||||
|
||||
- [Identity commitment](#identity-commitment): the public value.
|
||||
- Identity trapdoor and identity nullifier: secret values known only by the user.
|
||||
|
||||
## Semaphore group
|
||||
|
||||
A group is a binary incremental [Merkle tree](#merkle-tree) in which each leaf contains an [identity commitment](#identity-commitment) for a user.
|
||||
The identity commitment proves that the user is a group member without revealing the Semaphore identity of the user.
|
||||
|
||||
Semaphore uses the **Poseidon** hash function to create Merkle trees.
|
||||
For more information, see the [Poseidon website](https://www.poseidon-hash.info/).
|
||||
|
||||
## Identity commitment
|
||||
|
||||
The public [Semaphore identity](#semaphore-identity) value used in [Semaphore groups](#semaphore-group).
|
||||
|
||||
Semaphore uses the **Poseidon** hash function to create the identity commitment from the Semaphore identity secret values.
|
||||
For more information, see the [Poseidon website](https://www.poseidon-hash.info/).
|
||||
|
||||
## Merkle tree
|
||||
|
||||
A tree in which every leaf (i.e., a node that doesn't have children) is labelled with the cryptographic hash of a data block,
|
||||
and every node that isn't a leaf is labelled with the cryptographic hash of its child node labels.
|
||||
In zero-knowledge protocols, Merkle trees can be used to efficiently summarize and validate large data sets.
|
||||
To validate that a tree contains a specific leaf, a verifier only needs a portion of the complete data structure.
|
||||
|
||||
For more information, see [Merkle tree in Wikipedia](https://en.wikipedia.org/wiki/Merkle_tree).
|
||||
|
||||
## Nullifier
|
||||
|
||||
A value used to prevent double entry or double signalling.
|
||||
|
||||
See [Circuit nullifier hash](/docs/technical-reference/circuits/#nullifier-hash).
|
||||
|
||||
## Relay
|
||||
|
||||
A third-party who receives a fee for including relayed transactions in the blockchain (McMenamin, Daza, and Fitz. https://eprint.iacr.org/2022/155.pdf, p.3).
|
||||
To preserve the anonymity of the user broadcasting a signal with Semaphore, an application may use a relayer to post the signal transaction to Ethereum on behalf of the user.
|
||||
|
||||
Applications may provide rewards for relayers and implement front-running prevention mechanisms, such as requiring the signals to include the relayer’s address, binding the
|
||||
signal to that specific address (https://semaphore.pse.dev/whitepaper-v1.pdf, p.6).
|
||||
|
||||
## Trusted setup files
|
||||
|
||||
The secure, verifiable parameters generated by Semaphore's trusted setup ceremony.
|
||||
Semaphore uses the trusted setup files to generate and verify valid zero-knowledge proofs.
|
||||
To generate or verify valid zero-knowledge proofs with Semaphore, applications must include the following Semaphore _trusted setup_ files:
|
||||
|
||||
- semaphore.zkey
|
||||
- semaphore.wasm
|
||||
- semaphore.json
|
||||
|
||||
For a complete list of ready-to-use files, see <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).
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"label": "Guides",
|
||||
"position": 2
|
||||
}
|
||||
@@ -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.
|
||||
@@ -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())
|
||||
```
|
||||
@@ -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
|
||||
```
|
||||
@@ -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/).
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"label": "Technical reference",
|
||||
"position": 4
|
||||
}
|
||||
@@ -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)
|
||||
|
||||

|
||||
|
||||
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.
|
||||
@@ -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.
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"label": "Use cases",
|
||||
"position": 3
|
||||
}
|
||||
@@ -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).
|
||||
@@ -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.
|
||||
:::
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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
|
||||
:::
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"label": "Guides",
|
||||
"position": 3
|
||||
}
|
||||
@@ -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)
|
||||
```
|
||||
:::
|
||||
@@ -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).
|
||||
:::
|
||||
@@ -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())
|
||||
```
|
||||
@@ -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).
|
||||
:::
|
||||
@@ -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>
|
||||
@@ -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á
|
||||
@@ -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.
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"label": "Technical reference",
|
||||
"position": 4
|
||||
}
|
||||
@@ -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)
|
||||
|
||||

|
||||
|
||||
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.
|
||||
@@ -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.
|
||||
::::
|
||||
@@ -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.
|
||||
@@ -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).
|
||||
:::
|
||||
11
apps/docs/i18n/es/docusaurus-theme-classic/navbar.json
Normal file
11
apps/docs/i18n/es/docusaurus-theme-classic/navbar.json
Normal 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
50
apps/docs/package.json
Normal 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
29
apps/docs/sidebars.js
Normal 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'],
|
||||
},
|
||||
],
|
||||
*/
|
||||
}
|
||||
35
apps/docs/src/components/IllustrationHero/index.tsx
Normal file
35
apps/docs/src/components/IllustrationHero/index.tsx
Normal file
File diff suppressed because one or more lines are too long
@@ -0,0 +1,9 @@
|
||||
.illustrationHero {
|
||||
> path {
|
||||
fill: var(--ifm-color-primary);
|
||||
}
|
||||
|
||||
> path:first-child {
|
||||
fill: #ff975d;
|
||||
}
|
||||
}
|
||||
20
apps/docs/src/components/LinkButton/index.tsx
Normal file
20
apps/docs/src/components/LinkButton/index.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
44
apps/docs/src/components/LinkButton/styles.module.scss
Normal file
44
apps/docs/src/components/LinkButton/styles.module.scss
Normal 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;
|
||||
}
|
||||
20
apps/docs/src/components/OutlineLinkButton/index.tsx
Normal file
20
apps/docs/src/components/OutlineLinkButton/index.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
21
apps/docs/src/components/icons/IconArrowRight.tsx
Normal file
21
apps/docs/src/components/icons/IconArrowRight.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
21
apps/docs/src/components/icons/IconArrowTopRight.tsx
Normal file
21
apps/docs/src/components/icons/IconArrowTopRight.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
25
apps/docs/src/components/icons/IconAwards.tsx
Normal file
25
apps/docs/src/components/icons/IconAwards.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
22
apps/docs/src/components/icons/IconCheck.tsx
Normal file
22
apps/docs/src/components/icons/IconCheck.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
21
apps/docs/src/components/icons/IconConnections.tsx
Normal file
21
apps/docs/src/components/icons/IconConnections.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
30
apps/docs/src/components/icons/IconEye.tsx
Normal file
30
apps/docs/src/components/icons/IconEye.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
21
apps/docs/src/components/icons/IconEyeClose.tsx
Normal file
21
apps/docs/src/components/icons/IconEyeClose.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
25
apps/docs/src/components/icons/IconFlag.tsx
Normal file
25
apps/docs/src/components/icons/IconFlag.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
21
apps/docs/src/components/icons/IconGroup.tsx
Normal file
21
apps/docs/src/components/icons/IconGroup.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
25
apps/docs/src/components/icons/IconMoon.tsx
Normal file
25
apps/docs/src/components/icons/IconMoon.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
30
apps/docs/src/components/icons/IconProfile.tsx
Normal file
30
apps/docs/src/components/icons/IconProfile.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
21
apps/docs/src/components/icons/IconSun.tsx
Normal file
21
apps/docs/src/components/icons/IconSun.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
25
apps/docs/src/components/icons/IconUnion.tsx
Normal file
25
apps/docs/src/components/icons/IconUnion.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
410
apps/docs/src/css/custom.scss
Normal file
410
apps/docs/src/css/custom.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
318
apps/docs/src/pages/index.tsx
Normal file
318
apps/docs/src/pages/index.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
258
apps/docs/src/pages/styles.module.scss
Normal file
258
apps/docs/src/pages/styles.module.scss
Normal 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);
|
||||
}
|
||||
}
|
||||
105
apps/docs/src/theme/Footer/index.tsx
Normal file
105
apps/docs/src/theme/Footer/index.tsx
Normal 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)
|
||||
137
apps/docs/src/theme/Footer/styles.module.scss
Normal file
137
apps/docs/src/theme/Footer/styles.module.scss
Normal 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);
|
||||
}
|
||||
}
|
||||
18
apps/docs/src/theme/IconExternalLink/index.tsx
Normal file
18
apps/docs/src/theme/IconExternalLink/index.tsx
Normal 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
|
||||
11
apps/docs/src/theme/Navbar/Search/index.tsx
Normal file
11
apps/docs/src/theme/Navbar/Search/index.tsx
Normal 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} />
|
||||
}
|
||||
33
apps/docs/src/theme/NavbarItem/index.tsx
Normal file
33
apps/docs/src/theme/NavbarItem/index.tsx
Normal 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} />
|
||||
)
|
||||
}
|
||||
64
apps/docs/src/theme/Toggle/index.tsx
Normal file
64
apps/docs/src/theme/Toggle/index.tsx
Normal 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} />
|
||||
}
|
||||
110
apps/docs/src/theme/Toggle/styles.module.scss
Normal file
110
apps/docs/src/theme/Toggle/styles.module.scss
Normal 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
1
apps/docs/src/types/global.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
declare module "*.scss"
|
||||
0
apps/docs/static/.nojekyll
vendored
Normal file
0
apps/docs/static/.nojekyll
vendored
Normal file
1
apps/docs/static/CNAME
vendored
Normal file
1
apps/docs/static/CNAME
vendored
Normal file
@@ -0,0 +1 @@
|
||||
semaphore.pse.dev
|
||||
BIN
apps/docs/static/img/favicon.ico
vendored
Normal file
BIN
apps/docs/static/img/favicon.ico
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
3
apps/docs/static/img/semaphore-icon-dark.svg
vendored
Normal file
3
apps/docs/static/img/semaphore-icon-dark.svg
vendored
Normal 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 |
3
apps/docs/static/img/semaphore-icon.svg
vendored
Normal file
3
apps/docs/static/img/semaphore-icon.svg
vendored
Normal 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 |
3
apps/docs/static/img/semaphore-logo.svg
vendored
Normal file
3
apps/docs/static/img/semaphore-logo.svg
vendored
Normal file
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
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
Reference in New Issue
Block a user