Merge pull request #4164 from ethereum/semaphore-template

Semaphore template
pull/4129/head
David Disu 1 year ago committed by GitHub
commit 135b5dad44
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      apps/remix-ide/src/app/tabs/locales/en/filePanel.json
  2. 6
      apps/remix-ide/src/remixAppManager.js
  3. 10
      libs/remix-ui/workspace/src/lib/actions/events.ts
  4. 15
      libs/remix-ui/workspace/src/lib/actions/index.ts
  5. 118
      libs/remix-ui/workspace/src/lib/actions/payload.ts
  6. 23
      libs/remix-ui/workspace/src/lib/actions/workspace.ts
  7. 4
      libs/remix-ui/workspace/src/lib/components/file-explorer.tsx
  8. 4
      libs/remix-ui/workspace/src/lib/components/file-render.tsx
  9. 97
      libs/remix-ui/workspace/src/lib/reducers/workspace.ts
  10. 10
      libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx
  11. 120
      libs/remix-ui/workspace/src/lib/types/index.ts
  12. 3
      libs/remix-ui/workspace/src/lib/utils/constants.ts
  13. 1
      libs/remix-ws-templates/src/index.ts
  14. 9
      libs/remix-ws-templates/src/templates/semaphore/README.txt
  15. 90
      libs/remix-ws-templates/src/templates/semaphore/circuits/semaphore.circom
  16. 11
      libs/remix-ws-templates/src/templates/semaphore/circuits/simple.circom
  17. 40
      libs/remix-ws-templates/src/templates/semaphore/circuits/tree.circom
  18. 18
      libs/remix-ws-templates/src/templates/semaphore/index.ts
  19. 72
      libs/remix-ws-templates/src/templates/semaphore/scripts/run_setup.ts
  20. 102
      libs/remix-ws-templates/src/templates/semaphore/scripts/run_verification.ts
  21. 165
      libs/remix-ws-templates/src/templates/semaphore/templates/groth16_verifier.sol.ejs

@ -103,6 +103,7 @@
"filePanel.mintable": "Mintable",
"filePanel.burnable": "Burnable",
"filePanel.pausable": "Pausable",
"filePanel.semaphore": "Semaphore",
"filePanel.transparent": "Transparent",
"filePanel.initGitRepoTitle": "Check option to initialize workspace as a new git repository",
"filePanel.switchToBranchTitle1": "Checkout new branch from remote branch",

@ -74,8 +74,7 @@ const requiredModules = [
'compilationDetails',
'contractflattener',
'solidity-script',
'openaigpt',
'circuit-compiler'
'openaigpt'
]
// dependentModules shouldn't be manually activated (e.g hardhat is activated by remixd)
@ -114,7 +113,8 @@ export function isNative(name) {
'injected-ephemery-testnet-provider',
'injected',
'doc-gen',
'doc-viewer'
'doc-viewer',
'circuit-compiler'
]
return nativePlugins.includes(name) || requiredModules.includes(name)
}

@ -1,7 +1,7 @@
import { fileDecoration } from '@remix-ui/file-decorators'
import { extractParentFromKey } from '@remix-ui/helper'
import React from 'react'
import { action, WorkspaceTemplate } from '../types'
import { action, FileTree, WorkspaceTemplate } from '../types'
import { ROOT_PATH } from '../utils/constants'
import { displayNotification, displayPopUp, fileAddedSuccess, fileRemovedSuccess, fileRenamedSuccess, folderAddedSuccess, loadLocalhostError, loadLocalhostRequest, loadLocalhostSuccess, removeContextMenuItem, removeFocus, rootFolderChangedSuccess, setContextMenuItem, setMode, setReadOnlyMode, setFileDecorationSuccess } from './payload'
import { addInputField, createWorkspace, deleteWorkspace, fetchWorkspaceDirectory, renameWorkspace, switchToWorkspace, uploadFile } from './workspace'
@ -173,8 +173,8 @@ const folderAdded = async (folderPath: string) => {
const provider = plugin.fileManager.currentFileProvider()
const path = extractParentFromKey(folderPath) || ROOT_PATH
const promise = new Promise((resolve) => {
provider.resolveDirectory(path, (error, fileTree) => {
const promise: Promise<FileTree> = new Promise((resolve) => {
provider.resolveDirectory(path, (error, fileTree: FileTree) => {
if (error) console.error(error)
resolve(fileTree)
})
@ -196,8 +196,8 @@ const fileRemoved = async (removePath: string) => {
const fileRenamed = async (oldPath: string) => {
const provider = plugin.fileManager.currentFileProvider()
const path = extractParentFromKey(oldPath) || ROOT_PATH
const promise = new Promise((resolve) => {
provider.resolveDirectory(path, (error, fileTree) => {
const promise: Promise<FileTree> = new Promise((resolve) => {
provider.resolveDirectory(path, (error, fileTree: FileTree) => {
if (error) console.error(error)
resolve(fileTree)

@ -9,6 +9,7 @@ import { QueryParams } from '@remix-project/remix-lib'
import { fetchContractFromEtherscan } from '@remix-project/core-plugin' // eslint-disable-line
import JSZip from 'jszip'
import isElectron from 'is-electron'
import { Actions, FileTree } from '../types'
export * from './events'
export * from './workspace'
@ -16,7 +17,7 @@ export * from './workspace'
const queryParams = new QueryParams()
const _paq = window._paq = window._paq || []
let plugin, dispatch: React.Dispatch<any>
let plugin, dispatch: React.Dispatch<Actions>
export type UrlParametersType = {
gist: string,
@ -43,7 +44,7 @@ const basicWorkspaceInit = async (workspaces: { name: string; isGitRepo: boolean
}
}
export const initWorkspace = (filePanelPlugin) => async (reducerDispatch: React.Dispatch<any>) => {
export const initWorkspace = (filePanelPlugin) => async (reducerDispatch: React.Dispatch<Actions>) => {
if (filePanelPlugin) {
plugin = filePanelPlugin
dispatch = reducerDispatch
@ -144,18 +145,18 @@ export const initWorkspace = (filePanelPlugin) => async (reducerDispatch: React.
export const fetchDirectory = async (path: string) => {
const provider = plugin.fileManager.currentFileProvider()
const promise = new Promise((resolve) => {
provider.resolveDirectory(path, (error, fileTree) => {
provider.resolveDirectory(path, (error, fileTree: FileTree) => {
if (error) console.error(error)
resolve(fileTree)
})
})
dispatch(fetchDirectoryRequest(promise))
promise.then((fileTree) => {
dispatch(fetchDirectoryRequest())
promise.then((fileTree: FileTree) => {
dispatch(fetchDirectorySuccess(path, fileTree))
}).catch((error) => {
dispatch(fetchDirectoryError({ error }))
}).catch((error: ErrorEvent) => {
dispatch(fetchDirectoryError(error.message))
})
return promise
}

@ -1,292 +1,300 @@
import { fileDecoration } from '@remix-ui/file-decorators'
import { action } from '../types'
import { Action, ActionPayloadTypes, FileTree, WorkspaceElement, action } from '../types'
export const setCurrentWorkspace = (workspace: { name: string; isGitRepo: boolean; }) => {
export const setCurrentWorkspace = (workspace: { name: string; isGitRepo: boolean; }): Action<'SET_CURRENT_WORKSPACE'> => {
return {
type: 'SET_CURRENT_WORKSPACE',
payload: workspace
}
}
export const setWorkspaces = (workspaces: { name: string; isGitRepo: boolean; }[]) => {
export const setWorkspaces = (workspaces: { name: string; isGitRepo: boolean; }[]): Action<'SET_WORKSPACES'> => {
return {
type: 'SET_WORKSPACES',
payload: workspaces
}
}
export const setMode = (mode: 'browser' | 'localhost') => {
export const setMode = (mode: 'browser' | 'localhost'): Action<'SET_MODE'> => {
return {
type: 'SET_MODE',
payload: mode
}
}
export const fetchDirectoryError = (error: any) => {
export const fetchDirectoryError = (error: string): Action<'FETCH_DIRECTORY_ERROR'> => {
return {
type: 'FETCH_DIRECTORY_ERROR',
payload: error
}
}
export const fetchDirectoryRequest = (promise: Promise<any>) => {
export const fetchDirectoryRequest = (): Action<'FETCH_DIRECTORY_REQUEST'> => {
return {
type: 'FETCH_DIRECTORY_REQUEST',
payload: promise
payload: undefined
}
}
export const fetchDirectorySuccess = (path: string, fileTree) => {
export const fetchDirectorySuccess = (path: string, fileTree: FileTree): Action<'FETCH_DIRECTORY_SUCCESS'> => {
return {
type: 'FETCH_DIRECTORY_SUCCESS',
payload: { path, fileTree }
}
}
export const displayNotification = (title: string, message: string, labelOk: string, labelCancel: string, actionOk?: (...args) => void, actionCancel?: (...args) => void) => {
export const displayNotification = (title: string, message: string, labelOk: string, labelCancel: string, actionOk?: (...args) => void, actionCancel?: (...args) => void): Action<'DISPLAY_NOTIFICATION'> => {
return {
type: 'DISPLAY_NOTIFICATION',
payload: { title, message, labelOk, labelCancel, actionOk, actionCancel }
}
}
export const hideNotification = () => {
export const hideNotification = (): Action<'HIDE_NOTIFICATION'> => {
return {
type: 'HIDE_NOTIFICATION'
type: 'HIDE_NOTIFICATION',
payload: null
}
}
export const fileAddedSuccess = (filePath: string) => {
export const fileAddedSuccess = (filePath: string): Action<'FILE_ADDED_SUCCESS'> => {
return {
type: 'FILE_ADDED_SUCCESS',
payload: filePath
}
}
export const folderAddedSuccess = (path: string, folderPath: string, fileTree) => {
export const folderAddedSuccess = (path: string, folderPath: string, fileTree: FileTree): Action<'FOLDER_ADDED_SUCCESS'> => {
return {
type: 'FOLDER_ADDED_SUCCESS',
payload: { path, folderPath, fileTree }
}
}
export const fileRemovedSuccess = (removePath: string) => {
export const fileRemovedSuccess = (removePath: string): Action<'FILE_REMOVED_SUCCESS'> => {
return {
type: 'FILE_REMOVED_SUCCESS',
payload: removePath
}
}
export const fileRenamedSuccess = (path: string, oldPath: string, fileTree) => {
export const fileRenamedSuccess = (path: string, oldPath: string, fileTree: FileTree): Action<'FILE_RENAMED_SUCCESS'> => {
return {
type: 'FILE_RENAMED_SUCCESS',
payload: { path, oldPath, fileTree }
}
}
export const rootFolderChangedSuccess = (path: string) => {
export const rootFolderChangedSuccess = (path: string): Action<'ROOT_FOLDER_CHANGED'> => {
return {
type: 'ROOT_FOLDER_CHANGED',
payload: path
}
}
export const addInputFieldSuccess = (path: string, fileTree, type: 'file' | 'folder' | 'gist') => {
export const addInputFieldSuccess = (path: string, fileTree: FileTree, type: 'file' | 'folder'): Action<'ADD_INPUT_FIELD'> => {
return {
type: 'ADD_INPUT_FIELD',
payload: { path, fileTree, type }
}
}
export const removeInputFieldSuccess = (path: string) => {
export const removeInputFieldSuccess = (path: string): Action<'REMOVE_INPUT_FIELD'> => {
return {
type: 'REMOVE_INPUT_FIELD',
payload: { path }
}
}
export const setReadOnlyMode = (mode: boolean) => {
export const setReadOnlyMode = (mode: boolean): Action<'SET_READ_ONLY_MODE'> => {
return {
type: 'SET_READ_ONLY_MODE',
payload: mode
}
}
export const createWorkspaceError = (error: any) => {
export const createWorkspaceError = (error: string): Action<'CREATE_WORKSPACE_ERROR'> => {
return {
type: 'CREATE_WORKSPACE_ERROR',
payload: error
}
}
export const createWorkspaceRequest = (promise: Promise<any>) => {
export const createWorkspaceRequest = (): Action<'CREATE_WORKSPACE_REQUEST'> => {
return {
type: 'CREATE_WORKSPACE_REQUEST',
payload: promise
payload: null
}
}
export const createWorkspaceSuccess = (workspaceName: { name: string; isGitRepo: boolean; branches?: { remote: any; name: string; }[], currentBranch?: string }) => {
export const createWorkspaceSuccess = (workspaceName: ActionPayloadTypes['CREATE_WORKSPACE_SUCCESS']): Action<'CREATE_WORKSPACE_SUCCESS'> => {
return {
type: 'CREATE_WORKSPACE_SUCCESS',
payload: workspaceName
}
}
export const fetchWorkspaceDirectoryError = (error: any) => {
export const fetchWorkspaceDirectoryError = (error: string): Action<'FETCH_WORKSPACE_DIRECTORY_ERROR'> => {
return {
type: 'FETCH_WORKSPACE_DIRECTORY_ERROR',
payload: error
}
}
export const fetchWorkspaceDirectoryRequest = (promise: Promise<any>) => {
export const fetchWorkspaceDirectoryRequest = (): Action<'FETCH_WORKSPACE_DIRECTORY_REQUEST'> => {
return {
type: 'FETCH_WORKSPACE_DIRECTORY_REQUEST',
payload: promise
payload: null
}
}
export const fetchWorkspaceDirectorySuccess = (path: string, fileTree) => {
export const fetchWorkspaceDirectorySuccess = (path: string, fileTree: FileTree): Action<'FETCH_WORKSPACE_DIRECTORY_SUCCESS'> => {
return {
type: 'FETCH_WORKSPACE_DIRECTORY_SUCCESS',
payload: { path, fileTree }
}
}
export const setRenameWorkspace = (oldName: string, workspaceName: string) => {
export const setRenameWorkspace = (oldName: string, workspaceName: string): Action<'RENAME_WORKSPACE'> => {
return {
type: 'RENAME_WORKSPACE',
payload: { oldName, workspaceName }
}
}
export const setDeleteWorkspace = (workspaceName: string) => {
export const setDeleteWorkspace = (workspaceName: string): Action<'DELETE_WORKSPACE'> => {
return {
type: 'DELETE_WORKSPACE',
payload: workspaceName
}
}
export const displayPopUp = (message: string) => {
export const displayPopUp = (message: string): Action<'DISPLAY_POPUP_MESSAGE'> => {
return {
type: 'DISPLAY_POPUP_MESSAGE',
payload: message
}
}
export const hidePopUp = () => {
export const hidePopUp = (): Action<'HIDE_POPUP_MESSAGE'> => {
return {
type: 'HIDE_POPUP_MESSAGE'
type: 'HIDE_POPUP_MESSAGE',
payload: null
}
}
export const focusElement = (elements: { key: string, type: 'file' | 'folder' | 'gist' }[]) => {
export const focusElement = (elements: { key: string, type: WorkspaceElement }[]): Action<'SET_FOCUS_ELEMENT'> => {
return {
type: 'SET_FOCUS_ELEMENT',
payload: elements
}
}
export const removeFocus = (name: string) => {
export const removeFocus = (name: string): Action<'REMOVE_FOCUS_ELEMENT'> => {
return {
type: 'REMOVE_FOCUS_ELEMENT',
payload: name
}
}
export const setContextMenuItem = (item: action) => {
export const setContextMenuItem = (item: action): Action<'SET_CONTEXT_MENU_ITEM'> => {
return {
type: 'SET_CONTEXT_MENU_ITEM',
payload: item
}
}
export const removeContextMenuItem = (plugin) => {
export const removeContextMenuItem = (plugin: { name: string }): Action<'REMOVE_CONTEXT_MENU_ITEM'> => {
return {
type: 'REMOVE_CONTEXT_MENU_ITEM',
payload: plugin
}
}
export const setExpandPath = (paths: string[]) => {
export const setExpandPath = (paths: string[]): Action<'SET_EXPAND_PATH'> => {
return {
type: 'SET_EXPAND_PATH',
payload: paths
}
}
export const loadLocalhostError = (error: any) => {
export const loadLocalhostError = (error: string): Action<'LOAD_LOCALHOST_ERROR'> => {
return {
type: 'LOAD_LOCALHOST_ERROR',
payload: error
}
}
export const loadLocalhostRequest = () => {
export const loadLocalhostRequest = (): Action<'LOAD_LOCALHOST_REQUEST'> => {
return {
type: 'LOAD_LOCALHOST_REQUEST'
type: 'LOAD_LOCALHOST_REQUEST',
payload: null
}
}
export const loadLocalhostSuccess = () => {
export const loadLocalhostSuccess = (): Action<'LOAD_LOCALHOST_SUCCESS'> => {
return {
type: 'LOAD_LOCALHOST_SUCCESS'
type: 'LOAD_LOCALHOST_SUCCESS',
payload: null
}
}
export const fsInitializationCompleted = () => {
export const fsInitializationCompleted = (): Action<'FS_INITIALIZATION_COMPLETED'> => {
return {
type: 'FS_INITIALIZATION_COMPLETED'
type: 'FS_INITIALIZATION_COMPLETED',
payload: null
}
}
export const setFileDecorationSuccess = (items: fileDecoration[]) => {
export const setFileDecorationSuccess = (items: fileDecoration[]): Action<'SET_FILE_DECORATION_SUCCESS'> => {
return {
type: 'SET_FILE_DECORATION_SUCCESS',
payload: items
}
}
export const cloneRepositoryRequest = () => {
export const cloneRepositoryRequest = (): Action<'CLONE_REPOSITORY_REQUEST'> => {
return {
type: 'CLONE_REPOSITORY_REQUEST'
type: 'CLONE_REPOSITORY_REQUEST',
payload: null
}
}
export const cloneRepositorySuccess = () => {
export const cloneRepositorySuccess = (): Action<'CLONE_REPOSITORY_SUCCESS'> => {
return {
type: 'CLONE_REPOSITORY_SUCCESS'
type: 'CLONE_REPOSITORY_SUCCESS',
payload: null
}
}
export const cloneRepositoryFailed = () => {
export const cloneRepositoryFailed = (): Action<'CLONE_REPOSITORY_FAILED'> => {
return {
type: 'CLONE_REPOSITORY_FAILED'
type: 'CLONE_REPOSITORY_FAILED',
payload: null
}
}
export const setCurrentWorkspaceBranches = (branches?: { remote: any, name: string }[]) => {
export const setCurrentWorkspaceBranches = (branches?: { remote: any, name: string }[]): Action<'SET_CURRENT_WORKSPACE_BRANCHES'> => {
return {
type: 'SET_CURRENT_WORKSPACE_BRANCHES',
payload: branches
}
}
export const setCurrentWorkspaceCurrentBranch = (currentBranch?: string) => {
export const setCurrentWorkspaceCurrentBranch = (currentBranch?: string): Action<'SET_CURRENT_WORKSPACE_CURRENT_BRANCH'> => {
return {
type: 'SET_CURRENT_WORKSPACE_CURRENT_BRANCH',
payload: currentBranch
}
}
export const setCurrentWorkspaceIsGitRepo = (isRepo: boolean) => {
export const setCurrentWorkspaceIsGitRepo = (isRepo: boolean): Action<'SET_CURRENT_WORKSPACE_IS_GITREPO'> => {
return {
type: 'SET_CURRENT_WORKSPACE_IS_GITREPO',
payload: isRepo
}
}
export const setGitConfig = (config: {username: string, token: string, email: string}) => {
export const setGitConfig = (config: {username: string, token: string, email: string}): Action<'SET_GIT_CONFIG'> => {
return {
type: 'SET_GIT_CONFIG',
payload: config

@ -5,7 +5,7 @@ import axios, { AxiosResponse } from 'axios'
import { addInputFieldSuccess, cloneRepositoryFailed, cloneRepositoryRequest, cloneRepositorySuccess, createWorkspaceError, createWorkspaceRequest, createWorkspaceSuccess, displayNotification, displayPopUp, fetchWorkspaceDirectoryError, fetchWorkspaceDirectoryRequest, fetchWorkspaceDirectorySuccess, hideNotification, setCurrentWorkspace, setCurrentWorkspaceBranches, setCurrentWorkspaceCurrentBranch, setDeleteWorkspace, setMode, setReadOnlyMode, setRenameWorkspace, setCurrentWorkspaceIsGitRepo, setGitConfig } from './payload'
import { addSlash, checkSlash, checkSpecialChars } from '@remix-ui/helper'
import { JSONStandardInput, WorkspaceTemplate } from '../types'
import { FileTree, JSONStandardInput, WorkspaceTemplate } from '../types'
import { QueryParams } from '@remix-project/remix-lib'
import * as templateWithContent from '@remix-project/remix-ws-templates'
import { ROOT_PATH, slitherYml, solTestYml, tsSolTestYml } from '../utils/constants'
@ -58,8 +58,8 @@ export const setPlugin = (filePanelPlugin, reducerDispatch) => {
export const addInputField = async (type: 'file' | 'folder', path: string, cb?: (err: Error, result?: string | number | boolean | Record<string, any>) => void) => {
const provider = plugin.fileManager.currentFileProvider()
const promise = new Promise((resolve, reject) => {
provider.resolveDirectory(path, (error, fileTree) => {
const promise: Promise<FileTree> = new Promise((resolve, reject) => {
provider.resolveDirectory(path, (error, fileTree: FileTree) => {
if (error) {
cb && cb(error)
return reject(error)
@ -85,7 +85,7 @@ const removeSlash = (s: string) => {
export const createWorkspace = async (workspaceName: string, workspaceTemplateName: WorkspaceTemplate, opts = null, isEmpty = false, cb?: (err: Error, result?: string | number | boolean | Record<string, any>) => void, isGitRepo: boolean = false, createCommit: boolean = true) => {
await plugin.fileManager.closeAllFiles()
const promise = createWorkspaceTemplate(workspaceName, workspaceTemplateName)
dispatch(createWorkspaceRequest(promise))
dispatch(createWorkspaceRequest())
promise.then(async () => {
dispatch(createWorkspaceSuccess({ name: workspaceName, isGitRepo }))
await plugin.setWorkspace({ name: workspaceName, isLocalhost: false })
@ -136,10 +136,15 @@ export const createWorkspace = async (workspaceName: string, workspaceTemplateNa
const isActive = await plugin.call('manager', 'isActive', 'dgit')
if (!isActive) await plugin.call('manager', 'activatePlugin', 'dgit')
}
if (workspaceTemplateName === 'semaphore') {
const isCircomActive = await plugin.call('manager', 'isActive', 'circuit-compiler')
if (!isCircomActive) await plugin.call('manager', 'activatePlugin', 'circuit-compiler')
}
// this call needs to be here after the callback because it calls dGitProvider which also calls this function and that would cause an infinite loop
await plugin.setWorkspaces(await getWorkspaces())
}).catch((error) => {
dispatch(createWorkspaceError({ error }))
dispatch(createWorkspaceError(error.message))
cb && cb(error)
})
return promise
@ -269,19 +274,19 @@ export const workspaceExists = async (name: string) => {
export const fetchWorkspaceDirectory = async (path: string) => {
if (!path) return
const provider = plugin.fileManager.currentFileProvider()
const promise = new Promise((resolve) => {
provider.resolveDirectory(path, (error, fileTree) => {
const promise: Promise<FileTree> = new Promise((resolve) => {
provider.resolveDirectory(path, (error, fileTree: FileTree) => {
if (error) console.error(error)
resolve(fileTree)
})
})
dispatch(fetchWorkspaceDirectoryRequest(promise))
dispatch(fetchWorkspaceDirectoryRequest())
promise.then((fileTree) => {
dispatch(fetchWorkspaceDirectorySuccess(path, fileTree))
}).catch((error) => {
dispatch(fetchWorkspaceDirectoryError({ error }))
dispatch(fetchWorkspaceDirectoryError(error.message))
})
return promise
}

@ -3,7 +3,7 @@ import {useIntl} from 'react-intl'
import {TreeView} from '@remix-ui/tree-view' // eslint-disable-line
import {FileExplorerMenu} from './file-explorer-menu' // eslint-disable-line
import {FileExplorerContextMenu} from './file-explorer-context-menu' // eslint-disable-line
import {FileExplorerProps, FileType, WorkSpaceState} from '../types'
import {FileExplorerProps, FileType, WorkSpaceState, WorkspaceElement} from '../types'
import '../css/file-explorer.css'
import {checkSpecialChars, extractNameFromKey, extractParentFromKey, joinPath} from '@remix-ui/helper'
@ -151,7 +151,7 @@ export const FileExplorer = (props: FileExplorerProps) => {
)
}
const handleClickFile = (path: string, type: 'folder' | 'file' | 'gist') => {
const handleClickFile = (path: string, type: WorkspaceElement) => {
if (!state.ctrlKey) {
props.dispatchHandleClickFile(path, type)
} else {

@ -1,6 +1,6 @@
// eslint-disable-next-line no-use-before-define
import React, {SyntheticEvent, useEffect, useState} from 'react'
import {FileType} from '../types'
import {FileType, WorkspaceElement} from '../types'
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import {TreeView, TreeViewItem} from '@remix-ui/tree-view'
import {getPathIcon} from '@remix-ui/helper'
@ -14,7 +14,7 @@ export interface RenderFileProps {
file: FileType
index: number
focusEdit: {element: string; type: string; isNew: boolean; lastEdit: string}
focusElement: {key: string; type: 'file' | 'folder' | 'gist'}[]
focusElement: {key: string; type: WorkspaceElement}[]
focusContext: {element: string; x: number; y: number; type: string}
ctrlKey: boolean
expandPath: string[]

@ -1,12 +1,8 @@
import {extractNameFromKey} from '@remix-ui/helper'
import {action, FileType} from '../types'
import {action, Actions, FileType, WorkspaceElement} from '../types'
import * as _ from 'lodash'
import {fileDecoration} from '@remix-ui/file-decorators'
import {ROOT_PATH} from '../utils/constants'
interface Action {
type: string
payload: any
}
export interface BrowserState {
browser: {
currentWorkspace: string
@ -63,7 +59,7 @@ export interface BrowserState {
readonly: boolean
popup: string
focusEdit: string
focusElement: {key: string; type: 'file' | 'folder' | 'gist'}[]
focusElement: {key: string; type: WorkspaceElement}[]
initializingFS: boolean
gitConfig: {username: string; email: string; token: string}
}
@ -121,15 +117,10 @@ export const browserInitialState: BrowserState = {
gitConfig: {username: '', email: '', token: ''}
}
export const browserReducer = (state = browserInitialState, action: Action) => {
export const browserReducer = (state = browserInitialState, action: Actions) => {
switch (action.type) {
case 'SET_CURRENT_WORKSPACE': {
const payload = action.payload as {
name: string
isGitRepo: boolean
branches?: {remote: any; name: string}[]
currentBranch?: string
}
const payload = action.payload
const workspaces = state.browser.workspaces.find(
({name}) => name === payload.name
)
@ -147,12 +138,7 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
}
case 'SET_WORKSPACES': {
const payload = action.payload as {
name: string
isGitRepo: boolean
branches?: {remote: any; name: string}[]
currentBranch?: string
}[]
const payload = action.payload
return {
...state,
@ -164,7 +150,7 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
}
case 'SET_MODE': {
const payload = action.payload as 'browser' | 'localhost'
const payload = action.payload
return {
...state,
@ -191,7 +177,7 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
}
case 'FETCH_DIRECTORY_SUCCESS': {
const payload = action.payload as {path: string; fileTree}
const payload = action.payload
return {
...state,
@ -255,7 +241,7 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
}
case 'FETCH_WORKSPACE_DIRECTORY_SUCCESS': {
const payload = action.payload as {path: string; fileTree}
const payload = action.payload
return {
...state,
@ -302,14 +288,7 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
}
case 'DISPLAY_NOTIFICATION': {
const payload = action.payload as {
title: string
message: string
actionOk: () => void
actionCancel: () => void
labelOk: string
labelCancel: string
}
const payload = action.payload
return {
...state,
@ -335,7 +314,7 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
}
case 'FILE_ADDED_SUCCESS': {
const payload = action.payload as string
const payload = action.payload
return {
...state,
@ -365,11 +344,7 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
}
case 'FOLDER_ADDED_SUCCESS': {
const payload = action.payload as {
path: string
folderPath: string
fileTree
}
const payload = action.payload
return {
...state,
@ -404,7 +379,7 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
}
case 'FILE_REMOVED_SUCCESS': {
const payload = action.payload as string
const payload = action.payload
return {
...state,
@ -446,11 +421,7 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
}
case 'ADD_INPUT_FIELD': {
const payload = action.payload as {
path: string
fileTree
type: 'file' | 'folder'
}
const payload = action.payload
return {
...state,
@ -473,7 +444,7 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
}
case 'REMOVE_INPUT_FIELD': {
const payload = action.payload as {path: string; fileTree}
const payload = action.payload
return {
...state,
@ -496,7 +467,7 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
}
case 'SET_READ_ONLY_MODE': {
const payload = action.payload as boolean
const payload = action.payload
return {
...state,
@ -505,11 +476,7 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
}
case 'FILE_RENAMED_SUCCESS': {
const payload = action.payload as {
path: string
oldPath: string
fileTree
}
const payload = action.payload
return {
...state,
@ -543,12 +510,7 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
}
case 'CREATE_WORKSPACE_SUCCESS': {
const payload = action.payload as {
name: string
isGitRepo: boolean
branches?: {remote: any; name: string}[]
currentBranch?: string
}
const payload = action.payload
const workspaces = state.browser.workspaces.find(
({name}) => name === payload.name
)
@ -581,7 +543,7 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
}
case 'RENAME_WORKSPACE': {
const payload = action.payload as {oldName: string; workspaceName: string}
const payload = action.payload
let renamedWorkspace
const workspaces = state.browser.workspaces.filter(
({name, isGitRepo, branches, currentBranch}) => {
@ -611,7 +573,7 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
}
case 'DELETE_WORKSPACE': {
const payload = action.payload as string
const payload = action.payload
const workspaces = state.browser.workspaces.filter(
({name}) => name && name !== payload
)
@ -626,7 +588,7 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
}
case 'DISPLAY_POPUP_MESSAGE': {
const payload = action.payload as string
const payload = action.payload
return {
...state,
@ -642,10 +604,7 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
}
case 'SET_FOCUS_ELEMENT': {
const payload = action.payload as {
key: string
type: 'file' | 'folder' | 'gist'
}[]
const payload = action.payload
return {
...state,
@ -654,7 +613,7 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
}
case 'REMOVE_FOCUS_ELEMENT': {
const payload: string = action.payload
const payload = action.payload
return {
...state,
@ -665,7 +624,7 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
}
case 'SET_CONTEXT_MENU_ITEM': {
const payload = action.payload as action
const payload = action.payload
return {
...state,
@ -801,7 +760,7 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
}
case 'SET_CURRENT_WORKSPACE_BRANCHES': {
const payload: {remote: any; name: string}[] = action.payload
const payload = action.payload
return {
...state,
@ -817,7 +776,7 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
}
case 'SET_CURRENT_WORKSPACE_CURRENT_BRANCH': {
const payload: string = action.payload
const payload = action.payload
return {
...state,
@ -833,7 +792,7 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
}
case 'SET_CURRENT_WORKSPACE_IS_GITREPO': {
const payload: boolean = action.payload
const payload = action.payload
return {
...state,
@ -849,7 +808,7 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
}
case 'SET_GIT_CONFIG': {
const payload: {username: string; token: string; email: string} =
const payload =
action.payload
return {
...state,
@ -1138,7 +1097,7 @@ const addContextMenuItem = (
const removeContextMenuItem = (
state: BrowserState,
plugin
plugin: {name: string}
): {
registeredMenuItems: action[]
removedMenuItems: action[]

@ -1,7 +1,7 @@
import React, {useState, useEffect, useRef, useContext, SyntheticEvent, ChangeEvent, KeyboardEvent, MouseEvent} from 'react' // eslint-disable-line
import React, {useState, useEffect, useRef, useContext, ChangeEvent} from 'react' // eslint-disable-line
import {FormattedMessage, useIntl} from 'react-intl'
import {Dropdown} from 'react-bootstrap'
import {CustomIconsToggle, CustomMenu, CustomToggle, CustomTooltip, extractNameFromKey, extractParentFromKey} from '@remix-ui/helper'
import {CustomIconsToggle, CustomMenu, CustomToggle, extractNameFromKey, extractParentFromKey} from '@remix-ui/helper'
import {FileExplorer} from './components/file-explorer' // eslint-disable-line
import {FileSystemContext} from './contexts'
import './css/remix-ui-workspace.css'
@ -385,7 +385,6 @@ export function Workspace() {
// @ts-ignore
uupsRadioRef.current.checked = false
} else displayOzCustomRef.current.style.display = 'none'
// @ts-ignore
let displayName = TEMPLATE_NAMES[(workspaceCreateTemplateInput.current && workspaceCreateTemplateInput.current.value) || 'remixDefault']
displayName = global.plugin.getAvailableWorkspaceName(displayName)
@ -729,6 +728,11 @@ export function Workspace() {
{intl.formatMessage({id: 'filePanel.multiSigWallet'})}
</option>
</optgroup>
<optgroup style={{fontSize: 'medium'}} label="Circom ZKP">
<option style={{fontSize: 'small'}} value="semaphore">
{intl.formatMessage({id: 'filePanel.semaphore'})}
</option>
</optgroup>
</select>
<div id="ozcustomization" data-id="ozCustomization" ref={displayOzCustomRef} style={{display: 'none'}} className="mb-2">

@ -5,7 +5,7 @@ import { fileDecoration } from '@remix-ui/file-decorators'
import { RemixAppManager } from 'libs/remix-ui/plugin-manager/src/types'
import { ViewPlugin } from '@remixproject/engine-web'
export type action = { name: string, type?: Array<'folder' | 'gist' | 'file' | 'workspace'>, path?: string[], extension?: string[], pattern?: string[], id: string, multiselect: boolean, label: string, sticky?: boolean, group: number }
export type action = { name: string, type?: Array<WorkspaceElement>, path?: string[], extension?: string[], pattern?: string[], id: string, multiselect: boolean, label: string, sticky?: boolean, group: number }
export interface JSONStandardInput {
language: "Solidity";
settings?: any,
@ -17,7 +17,7 @@ export interface JSONStandardInput {
};
}
export type MenuItems = action[]
export type WorkspaceTemplate = 'gist-template' | 'code-template' | 'remixDefault' | 'blank' | 'ozerc20' | 'zeroxErc20' | 'ozerc721'
export type WorkspaceTemplate = 'gist-template' | 'code-template' | 'remixDefault' | 'blank' | 'ozerc20' | 'zeroxErc20' | 'ozerc721' | 'semaphore'
export interface WorkspaceProps {
plugin: FilePanelType
}
@ -88,7 +88,7 @@ export interface FileExplorerProps {
focusEdit: string,
hideIconsMenu: React.Dispatch<React.SetStateAction<boolean>>,
showIconsMenu: boolean,
focusElement: { key: string, type: 'file' | 'folder' | 'gist' }[],
focusElement: { key: string, type: WorkspaceElement }[],
dispatchCreateNewFile: (path: string, rootDir: string) => Promise<void>,
// eslint-disable-next-line no-undef
modal:(title: string, message: string | JSX.Element, okLabel: string, okFn: () => void, cancelLabel?: string, cancelFn?: () => void) => void,
@ -105,8 +105,8 @@ export interface FileExplorerProps {
dispatchRunScript: (path: string) => Promise<void>,
dispatchPublishToGist: (path?: string, type?: string) => Promise<void>,
dispatchEmitContextMenuEvent: (cmd: customAction) => Promise<void>,
dispatchHandleClickFile: (path: string, type: 'file' | 'folder' | 'gist') => Promise<void>,
dispatchSetFocusElement: (elements: { key: string, type: 'file' | 'folder' | 'gist' }[]) => Promise<void>,
dispatchHandleClickFile: (path: string, type: WorkspaceElement) => Promise<void>,
dispatchSetFocusElement: (elements: { key: string, type: WorkspaceElement }[]) => Promise<void>,
dispatchFetchDirectory:(path: string) => Promise<void>,
dispatchRemoveInputField:(path: string) => Promise<void>,
dispatchAddInputField:(path: string, type: 'file' | 'folder') => Promise<void>,
@ -114,7 +114,7 @@ export interface FileExplorerProps {
dispatchMoveFile: (src: string, dest: string) => Promise<void>,
dispatchMoveFolder: (src: string, dest: string) => Promise<void>,
handlePasteClick: (dest: string, destType: string) => void
handleCopyClick: (path: string, type: 'folder' | 'gist' | 'file' | 'workspace') => void
handleCopyClick: (path: string, type: WorkspaceElement) => void
addMenuItems: (items: MenuItems) => void
removeMenuItems: (items: MenuItems) => void
handleContextMenu: (pageX: number, pageY: number, path: string, content: string, type: string) => void
@ -171,7 +171,7 @@ export interface WorkSpaceState {
actions: {
id: string
name: string
type?: Array<'folder' | 'gist' | 'file' | 'workspace'>
type?: Array<WorkspaceElement>
path?: string[]
extension?: string[]
pattern?: string[]
@ -201,5 +201,107 @@ export type FileFocusContextType = {
export type CopyElementType = {
key: string
type: 'folder' | 'gist' | 'file' | 'workspace'
}
type: WorkspaceElement
}
export type FileTree = {
[x: string]: {
isDirectory: boolean
}
}
export interface ActionPayloadTypes {
SET_CURRENT_WORKSPACE: {
name: string
isGitRepo: boolean
branches?: {remote: string | undefined; name: string}[]
currentBranch?: string
},
SET_WORKSPACES: {
name: string
isGitRepo: boolean
branches?: {remote: string | undefined; name: string}[]
currentBranch?: string
}[],
SET_MODE: 'browser' | 'localhost',
FETCH_DIRECTORY_REQUEST: undefined | null,
FETCH_DIRECTORY_SUCCESS: { path: string; fileTree: FileTree },
FETCH_DIRECTORY_ERROR: string,
FETCH_WORKSPACE_DIRECTORY_REQUEST: undefined | null,
FETCH_WORKSPACE_DIRECTORY_SUCCESS: { path: string; fileTree: FileTree },
FETCH_WORKSPACE_DIRECTORY_ERROR: string,
DISPLAY_NOTIFICATION: {
title: string
message: string
actionOk: () => void
actionCancel: () => void
labelOk: string
labelCancel: string
},
HIDE_NOTIFICATION: undefined | null,
FILE_ADDED_SUCCESS: string,
FOLDER_ADDED_SUCCESS: {
path: string
folderPath: string
fileTree: FileTree
},
FILE_REMOVED_SUCCESS: string,
ROOT_FOLDER_CHANGED: string,
ADD_INPUT_FIELD: {
path: string
fileTree: FileTree
type: 'file' | 'folder'
},
REMOVE_INPUT_FIELD: { path: string; },
SET_READ_ONLY_MODE: boolean,
FILE_RENAMED_SUCCESS: {
path: string
oldPath: string
fileTree: FileTree
},
CREATE_WORKSPACE_REQUEST: undefined | null,
CREATE_WORKSPACE_SUCCESS: {
name: string
isGitRepo: boolean
branches?: { remote: string | undefined; name: string }[]
currentBranch?: string
},
CREATE_WORKSPACE_ERROR: string,
RENAME_WORKSPACE: { oldName: string; workspaceName: string },
DELETE_WORKSPACE: string,
DISPLAY_POPUP_MESSAGE: string,
HIDE_POPUP_MESSAGE: undefined | null,
SET_FOCUS_ELEMENT: {
key: string
type: WorkspaceElement
}[],
REMOVE_FOCUS_ELEMENT: string,
SET_CONTEXT_MENU_ITEM: action,
REMOVE_CONTEXT_MENU_ITEM: { name: string },
SET_EXPAND_PATH: string[],
LOAD_LOCALHOST_REQUEST: undefined | null,
LOAD_LOCALHOST_SUCCESS: undefined | null,
LOAD_LOCALHOST_ERROR: string,
CLONE_REPOSITORY_REQUEST: undefined | null,
CLONE_REPOSITORY_SUCCESS: undefined | null,
CLONE_REPOSITORY_FAILED: undefined | null,
FS_INITIALIZATION_COMPLETED: undefined | null,
SET_FILE_DECORATION_SUCCESS: fileDecoration[],
SET_CURRENT_WORKSPACE_BRANCHES: { remote: string | undefined; name: string }[],
SET_CURRENT_WORKSPACE_CURRENT_BRANCH: string,
SET_CURRENT_WORKSPACE_IS_GITREPO: boolean,
SET_GIT_CONFIG: {
username: string;
token: string;
email: string
}
}
export interface Action<T extends keyof ActionPayloadTypes> {
type: T,
payload: ActionPayloadTypes[T]
}
export type Actions = {[A in keyof ActionPayloadTypes]: Action<A>}[keyof ActionPayloadTypes]
export type WorkspaceElement = 'folder' | 'gist' | 'file' | 'workspace'

@ -80,5 +80,6 @@ export const TEMPLATE_NAMES = {
'ozerc721': 'OpenZeppelin ERC721',
'ozerc1155': 'OpenZeppelin ERC1155',
'zeroxErc20': '0xProject ERC20',
'gnosisSafeMultisig': 'Gnosis Safe'
'gnosisSafeMultisig': 'Gnosis Safe',
'semaphore': 'Semaphore'
}

@ -5,6 +5,7 @@ export { default as ozerc721 } from './templates/ozerc721'
export { default as ozerc1155 } from './templates/ozerc1155'
export { default as zeroxErc20 } from './templates/zeroxErc20'
export { default as gnosisSafeMultisig } from './templates/gnosisSafeMultisig'
export { default as semaphore } from './templates/semaphore'
export { contractDeployerScripts } from './script-templates/contract-deployer'
export { etherscanScripts } from './script-templates/etherscan'

@ -0,0 +1,9 @@
REMIX CIRCOM WORKSPACE
Welcome to the Remix Circom Workspace. This workspace becomes available when you create a new workspace using the 'Circom' template.
Directory Structure
The workspace comprises two main directories:
circuits: Contains sample semaphore contracts. These can be compiled to generate a witness.
scripts: Provides a sample script designed for a trusted setup using snarkjs. This script also aids in generating Solidity code, which is essential for on-chain deployment.

@ -0,0 +1,90 @@
pragma circom 2.0.0;
include "circomlib/poseidon.circom";
include "./tree.circom";
template CalculateSecret() {
signal input identityNullifier;
signal input identityTrapdoor;
signal output out;
component poseidon = Poseidon(2);
poseidon.inputs[0] <== identityNullifier;
poseidon.inputs[1] <== identityTrapdoor;
out <== poseidon.out;
}
template CalculateIdentityCommitment() {
signal input secret;
signal output out;
component poseidon = Poseidon(1);
poseidon.inputs[0] <== secret;
out <== poseidon.out;
}
template CalculateNullifierHash() {
signal input externalNullifier;
signal input identityNullifier;
signal output out;
component poseidon = Poseidon(2);
poseidon.inputs[0] <== externalNullifier;
poseidon.inputs[1] <== identityNullifier;
out <== poseidon.out;
}
// credits to : https://github.com/semaphore-protocol/semaphore
// The current Semaphore smart contracts require nLevels <= 32 and nLevels >= 16.
template Semaphore(nLevels) {
signal input identityNullifier;
signal input identityTrapdoor;
signal input treePathIndices[nLevels];
signal input treeSiblings[nLevels];
signal input signalHash;
signal input externalNullifier;
signal output root;
signal output nullifierHash;
component calculateSecret = CalculateSecret();
calculateSecret.identityNullifier <== identityNullifier;
calculateSecret.identityTrapdoor <== identityTrapdoor;
signal secret;
secret <== calculateSecret.out;
component calculateIdentityCommitment = CalculateIdentityCommitment();
calculateIdentityCommitment.secret <== secret;
component calculateNullifierHash = CalculateNullifierHash();
calculateNullifierHash.externalNullifier <== externalNullifier;
calculateNullifierHash.identityNullifier <== identityNullifier;
component inclusionProof = MerkleTreeInclusionProof(nLevels);
inclusionProof.leaf <== calculateIdentityCommitment.out;
for (var i = 0; i < nLevels; i++) {
inclusionProof.siblings[i] <== treeSiblings[i];
inclusionProof.pathIndices[i] <== treePathIndices[i];
}
root <== inclusionProof.root;
// Dummy square to prevent tampering signalHash.
signal signalHashSquared;
signalHashSquared <== signalHash * signalHash;
nullifierHash <== calculateNullifierHash.out;
}
component main {public [signalHash, externalNullifier]} = Semaphore(20);

@ -0,0 +1,11 @@
pragma circom 2.0.0;
template Multiplier2() {
signal input a;
signal input b;
signal output c;
c <== a*b;
}
component main = Multiplier2();

@ -0,0 +1,40 @@
pragma circom 2.0.0;
include "circomlib/poseidon.circom";
include "circomlib/mux1.circom";
template MerkleTreeInclusionProof(nLevels) {
signal input leaf;
signal input pathIndices[nLevels];
signal input siblings[nLevels];
signal output root;
component poseidons[nLevels];
component mux[nLevels];
signal hashes[nLevels + 1];
hashes[0] <== leaf;
for (var i = 0; i < nLevels; i++) {
pathIndices[i] * (1 - pathIndices[i]) === 0;
poseidons[i] = Poseidon(2);
mux[i] = MultiMux1(2);
mux[i].c[0][0] <== hashes[i];
mux[i].c[0][1] <== siblings[i];
mux[i].c[1][0] <== siblings[i];
mux[i].c[1][1] <== hashes[i];
mux[i].s <== pathIndices[i];
poseidons[i].inputs[0] <== mux[i].out[0];
poseidons[i].inputs[1] <== mux[i].out[1];
hashes[i + 1] <== poseidons[i].out;
}
root <== hashes[nLevels];
}

@ -0,0 +1,18 @@
export default async () => {
return {
// @ts-ignore
'circuits/semaphore.circom': (await import('raw-loader!./circuits/semaphore.circom')).default,
// @ts-ignore
'circuits/simple.circom': (await import('!!raw-loader!./circuits/simple.circom')).default,
// @ts-ignore
'circuits/tree.circom': (await import('!!raw-loader!./circuits/tree.circom')).default,
// @ts-ignore
'scripts/run_setup.ts': (await import('!!raw-loader!./scripts/run_setup.ts')).default,
// @ts-ignore
'scripts/run_verification.ts': (await import('!!raw-loader!./scripts/run_verification.ts')).default,
// @ts-ignore
'templates/groth16_verifier.sol.ejs': (await import('!!raw-loader!./templates/groth16_verifier.sol.ejs')).default,
// @ts-ignore
'README.txt': (await import('raw-loader!./README.txt')).default
}
}

@ -0,0 +1,72 @@
import { ethers, BigNumber } from 'ethers'
// eslint-disable-next-line @typescript-eslint/no-var-requires
const snarkjs = require('snarkjs');
const logger = {
info: (...args) => console.log(...args),
debug: (...args) => console.log(...args)
};
/**
* Creates a keccak256 hash of a message compatible with the SNARK scalar modulus.
* @param message The message to be hashed.
* @returns The message digest.
*/
function hash(message: any): bigint {
message = BigNumber.from(message).toTwos(256).toHexString()
message = ethers.utils.zeroPad(message, 32)
return BigInt(ethers.utils.keccak256(message)) >> BigInt(8)
}
(async () => {
try {
// @ts-ignore
await remix.call('circuit-compiler', 'generateR1cs', 'circuits/semaphore.circom');
const ptau_final = "https://ipfs-cluster.ethdevops.io/ipfs/QmTiT4eiYz5KF7gQrDsgfCSTRv3wBPYJ4bRN1MmTRshpnW";
// @ts-ignore
const r1csBuffer = await remix.call('fileManager', 'readFile', 'circuits/.bin/semaphore.r1cs', true);
// @ts-ignore
const r1cs = new Uint8Array(r1csBuffer);
const zkey_0 = { type: "mem" };
const zkey_1 = { type: "mem" };
const zkey_final = { type: "mem" };
console.log('newZkey')
await snarkjs.zKey.newZKey(r1cs, ptau_final, zkey_0);
console.log('contribute')
await snarkjs.zKey.contribute(zkey_0, zkey_1, "p2_C1", "pa_Entropy1");
console.log('beacon')
await snarkjs.zKey.beacon(zkey_1, zkey_final, "B3", "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20", 10);
console.log('verifyFromR1cs')
const verifyFromR1csResult = await snarkjs.zKey.verifyFromR1cs(r1cs, ptau_final, zkey_final);
console.assert(verifyFromR1csResult);
console.log('verifyFromInit')
const verifyFromInit = await snarkjs.zKey.verifyFromInit(zkey_0, ptau_final, zkey_final);
console.assert(verifyFromInit);
console.log('exportVerificationKey')
const vKey = await snarkjs.zKey.exportVerificationKey(zkey_final)
await remix.call('fileManager', 'writeFile', './zk/build/verification_key.json', JSON.stringify(vKey))
const templates = {
groth16: await remix.call('fileManager', 'readFile', 'templates/groth16_verifier.sol.ejs')
}
const solidityContract = await snarkjs.zKey.exportSolidityVerifier(zkey_final, templates)
await remix.call('fileManager', 'writeFile', './zk/build/zk_verifier.sol', solidityContract)
console.log('buffer', (zkey_final as any).data.length)
await remix.call('fileManager', 'writeFile', './zk/build/zk_setup.txt', JSON.stringify(Array.from(((zkey_final as any).data))))
console.log('setup done.')
} catch (e) {
console.error(e.message)
}
})()

@ -0,0 +1,102 @@
import { ethers, BigNumber } from 'ethers'
import { IncrementalMerkleTree } from "@zk-kit/incremental-merkle-tree"
import { poseidon } from "circomlibjs" // v0.0.8
// eslint-disable-next-line @typescript-eslint/no-var-requires
const snarkjs = require('snarkjs');
const logger = {
info: (...args) => console.log(...args),
debug: (...args) => console.log(...args),
error: (...args) => console.error(...args),
}
/**
* Creates a keccak256 hash of a message compatible with the SNARK scalar modulus.
* @param message The message to be hashed.
* @returns The message digest.
*/
function hash(message: any): bigint {
message = BigNumber.from(message).toTwos(256).toHexString()
message = ethers.utils.zeroPad(message, 32)
return BigInt(ethers.utils.keccak256(message)) >> BigInt(8)
}
(async () => {
try {
// @ts-ignore
const r1csBuffer = await remix.call('fileManager', 'readFile', 'circuits/.bin/semaphore.r1cs', true);
// @ts-ignore
const r1cs = new Uint8Array(r1csBuffer);
// @ts-ignore
const wasmBuffer = await remix.call('fileManager', 'readFile', 'circuits/.bin/semaphore.wasm', true);
// @ts-ignore
const wasm = new Uint8Array(wasmBuffer);
const zkey_final = {
type: "mem",
data: new Uint8Array(JSON.parse(await remix.call('fileManager', 'readFile', './zk/build/zk_setup.txt')))
}
const wtns = { type: "mem" };
const vKey = JSON.parse(await remix.call('fileManager', 'readFile', './zk/build/verification_key.json'))
// build list of identity commitments
const secrets = []
const identityCommitments = []
for (let k = 0; k < 2; k++) {
const identityTrapdoor = BigInt(ethers.utils.hexlify(ethers.utils.randomBytes(32)))
const identityNullifier = BigInt(ethers.utils.hexlify(ethers.utils.randomBytes(32)))
secrets.push({identityTrapdoor, identityNullifier})
const secret = poseidon([identityNullifier, identityTrapdoor])
const identityCommitment = poseidon([secret])
identityCommitments.push(identityCommitment)
}
//console.log('incremental tree', identityCommitments.map((x) => x.toString()))
let tree
try {
tree = new IncrementalMerkleTree(poseidon, 20, BigInt(0), 2, identityCommitments) // Binary tree.
} catch (e) {
console.error(e.message)
return
}
const index = tree.indexOf(identityCommitments[0])
console.log(index.toString())
const proof1 = tree.createProof(0)
console.log('prepare signals for id ', identityCommitments[0].toString(), tree.indexOf(identityCommitments[0]), proof1.siblings.map((x)=> x.toString()))
const signals = {
identityTrapdoor: secrets[0].identityTrapdoor,
identityNullifier: secrets[0].identityNullifier,
treePathIndices: proof1.pathIndices,
treeSiblings: proof1.siblings,
externalNullifier: hash(42),
signalHash: hash(ethers.utils.formatBytes32String("Hello World"))
}
console.log('calculate')
await snarkjs.wtns.calculate(signals, wasm, wtns);
console.log('check')
await snarkjs.wtns.check(r1cs, wtns, logger);
console.log('prove')
const { proof, publicSignals } = await snarkjs.groth16.prove(zkey_final, wtns);
const verified = await snarkjs.groth16.verify(vKey, publicSignals, proof, logger);
console.log('zk proof validity', verified);
proof1.root.toString() === publicSignals[0] ? console.log('merkle proof valid') : console.log('merkle proof invalid')
} catch (e) {
console.error(e.message)
}
})()

@ -0,0 +1,165 @@
// SPDX-License-Identifier: GPL-3.0
/*
Copyright 2021 0KIMS association.
This file is generated with [snarkJS](https://github.com/iden3/snarkjs).
snarkJS is a free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
snarkJS is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with snarkJS. If not, see <https://www.gnu.org/licenses/>.
*/
pragma solidity >=0.7.0 <0.9.0;
contract Groth16Verifier {
// Scalar field size
uint256 constant r = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
// Base field size
uint256 constant q = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
// Verification Key data
uint256 constant alphax = <%= vk_alpha_1[0] %>;
uint256 constant alphay = <%= vk_alpha_1[1] %>;
uint256 constant betax1 = <%= vk_beta_2[0][1] %>;
uint256 constant betax2 = <%= vk_beta_2[0][0] %>;
uint256 constant betay1 = <%= vk_beta_2[1][1] %>;
uint256 constant betay2 = <%= vk_beta_2[1][0] %>;
uint256 constant gammax1 = <%= vk_gamma_2[0][1] %>;
uint256 constant gammax2 = <%= vk_gamma_2[0][0] %>;
uint256 constant gammay1 = <%= vk_gamma_2[1][1] %>;
uint256 constant gammay2 = <%= vk_gamma_2[1][0] %>;
uint256 constant deltax1 = <%= vk_delta_2[0][1] %>;
uint256 constant deltax2 = <%= vk_delta_2[0][0] %>;
uint256 constant deltay1 = <%= vk_delta_2[1][1] %>;
uint256 constant deltay2 = <%= vk_delta_2[1][0] %>;
<% for (let i=0; i<IC.length; i++) { %>
uint256 constant IC<%=i%>x = <%=IC[i][0]%>;
uint256 constant IC<%=i%>y = <%=IC[i][1]%>;
<% } %>
// Memory data
uint16 constant pVk = 0;
uint16 constant pPairing = 128;
uint16 constant pLastMem = 896;
function verifyProof(uint[2] calldata _pA, uint[2][2] calldata _pB, uint[2] calldata _pC, uint[<%=IC.length-1%>] calldata _pubSignals) public view returns (bool) {
assembly {
function checkField(v) {
if iszero(lt(v, q)) {
mstore(0, 0)
return(0, 0x20)
}
}
// G1 function to multiply a G1 value(x,y) to value in an address
function g1_mulAccC(pR, x, y, s) {
let success
let mIn := mload(0x40)
mstore(mIn, x)
mstore(add(mIn, 32), y)
mstore(add(mIn, 64), s)
success := staticcall(sub(gas(), 2000), 7, mIn, 96, mIn, 64)
if iszero(success) {
mstore(0, 0)
return(0, 0x20)
}
mstore(add(mIn, 64), mload(pR))
mstore(add(mIn, 96), mload(add(pR, 32)))
success := staticcall(sub(gas(), 2000), 6, mIn, 128, pR, 64)
if iszero(success) {
mstore(0, 0)
return(0, 0x20)
}
}
function checkPairing(pA, pB, pC, pubSignals, pMem) -> isOk {
let _pPairing := add(pMem, pPairing)
let _pVk := add(pMem, pVk)
mstore(_pVk, IC0x)
mstore(add(_pVk, 32), IC0y)
// Compute the linear combination vk_x
<% for (let i = 1; i <= nPublic; i++) { %>
g1_mulAccC(_pVk, IC<%=i%>x, IC<%=i%>y, calldataload(add(pubSignals, <%=(i-1)*32%>)))
<% } %>
// -A
mstore(_pPairing, calldataload(pA))
mstore(add(_pPairing, 32), mod(sub(q, calldataload(add(pA, 32))), q))
// B
mstore(add(_pPairing, 64), calldataload(pB))
mstore(add(_pPairing, 96), calldataload(add(pB, 32)))
mstore(add(_pPairing, 128), calldataload(add(pB, 64)))
mstore(add(_pPairing, 160), calldataload(add(pB, 96)))
// alpha1
mstore(add(_pPairing, 192), alphax)
mstore(add(_pPairing, 224), alphay)
// beta2
mstore(add(_pPairing, 256), betax1)
mstore(add(_pPairing, 288), betax2)
mstore(add(_pPairing, 320), betay1)
mstore(add(_pPairing, 352), betay2)
// vk_x
mstore(add(_pPairing, 384), mload(add(pMem, pVk)))
mstore(add(_pPairing, 416), mload(add(pMem, add(pVk, 32))))
// gamma2
mstore(add(_pPairing, 448), gammax1)
mstore(add(_pPairing, 480), gammax2)
mstore(add(_pPairing, 512), gammay1)
mstore(add(_pPairing, 544), gammay2)
// C
mstore(add(_pPairing, 576), calldataload(pC))
mstore(add(_pPairing, 608), calldataload(add(pC, 32)))
// delta2
mstore(add(_pPairing, 640), deltax1)
mstore(add(_pPairing, 672), deltax2)
mstore(add(_pPairing, 704), deltay1)
mstore(add(_pPairing, 736), deltay2)
let success := staticcall(sub(gas(), 2000), 8, _pPairing, 768, _pPairing, 0x20)
isOk := and(success, mload(_pPairing))
}
let pMem := mload(0x40)
mstore(0x40, add(pMem, pLastMem))
// Validate that all evaluations ∈ F
<% for (let i=0; i<IC.length; i++) { %>
checkField(calldataload(add(_pubSignals, <%=i*32%>)))
<% } %>
// Validate all evaluations
let isValid := checkPairing(_pA, _pB, _pC, _pubSignals, pMem)
mstore(0, isValid)
return(0, 0x20)
}
}
}
Loading…
Cancel
Save