commit
41ecde7fd7
@ -0,0 +1,43 @@ |
|||||||
|
import type { CircomPluginClient } from "../services/circomPluginClient" |
||||||
|
import { Actions, AppState } from "../types" |
||||||
|
|
||||||
|
export const compileCircuit = async (plugin: CircomPluginClient, appState: AppState) => { |
||||||
|
try { |
||||||
|
if (appState.status !== "compiling") { |
||||||
|
await plugin.compile(appState.filePath, { version: appState.version, prime: appState.primeValue }) |
||||||
|
} else { |
||||||
|
console.log('Exisiting circuit compilation in progress') |
||||||
|
} |
||||||
|
} catch (e) { |
||||||
|
plugin.internalEvents.emit('circuit_compiling_errored', e) |
||||||
|
console.error(e) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const generateR1cs = async (plugin: CircomPluginClient, appState: AppState) => { |
||||||
|
try { |
||||||
|
if (appState.status !== "generating") { |
||||||
|
await plugin.generateR1cs(appState.filePath, { version: appState.version, prime: appState.primeValue }) |
||||||
|
} else { |
||||||
|
console.log('Exisiting r1cs generation in progress') |
||||||
|
} |
||||||
|
} catch (e) { |
||||||
|
plugin.internalEvents.emit('circuit_generating_r1cs_errored', e) |
||||||
|
console.error('Generating R1CS failed: ', e) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const computeWitness = async (plugin: CircomPluginClient, status: string, witnessValues: Record<string, string>) => { |
||||||
|
try { |
||||||
|
if (status !== "computing") { |
||||||
|
const input = JSON.stringify(witnessValues) |
||||||
|
|
||||||
|
await plugin.computeWitness(input) |
||||||
|
} else { |
||||||
|
console.log('Exisiting witness computation in progress') |
||||||
|
} |
||||||
|
} catch (e) { |
||||||
|
plugin.internalEvents.emit('circuit_computing_witness_errored', e) |
||||||
|
console.error('Computing witness failed: ', e) |
||||||
|
} |
||||||
|
} |
@ -1,17 +1,143 @@ |
|||||||
import React, { useEffect } from 'react' |
import React, {useEffect, useReducer, useState} from 'react' |
||||||
|
import {RenderIf} from '@remix-ui/helper' |
||||||
|
import {IntlProvider} from 'react-intl' |
||||||
|
|
||||||
import { CircomPluginClient } from './services/circomPluginClient' |
import { Container } from './components/container' |
||||||
|
import {CircuitAppContext} from './contexts' |
||||||
|
import {appInitialState, appReducer} from './reducers/state' |
||||||
|
import {CircomPluginClient} from './services/circomPluginClient' |
||||||
|
import { compileCircuit } from './actions' |
||||||
|
|
||||||
|
const plugin = new CircomPluginClient() |
||||||
|
|
||||||
function App() { |
function App() { |
||||||
|
const [appState, dispatch] = useReducer(appReducer, appInitialState) |
||||||
|
const [locale, setLocale] = useState<{code: string; messages: any}>({ |
||||||
|
code: 'en', |
||||||
|
messages: null |
||||||
|
}) |
||||||
|
const [isContentChanged, setIsContentChanged] = useState<boolean>(false) |
||||||
|
const [isPluginActivated, setIsPluginActivated] = useState<boolean>(false) |
||||||
|
|
||||||
useEffect(() => { |
useEffect(() => { |
||||||
new CircomPluginClient() |
plugin.internalEvents.on('circom_activated', () => { |
||||||
|
// @ts-ignore
|
||||||
|
plugin.on('locale', 'localeChanged', (locale: any) => { |
||||||
|
setLocale(locale) |
||||||
|
}) |
||||||
|
plugin.on('fileManager', 'currentFileChanged', (filePath) => { |
||||||
|
if (filePath.endsWith('.circom')) { |
||||||
|
dispatch({ type: 'SET_FILE_PATH', payload: filePath }) |
||||||
|
plugin.parse(filePath) |
||||||
|
} else { |
||||||
|
dispatch({ type: 'SET_FILE_PATH', payload: '' }) |
||||||
|
} |
||||||
|
}) |
||||||
|
// @ts-ignore
|
||||||
|
plugin.on('editor', 'contentChanged', async (path: string, content: string) => { |
||||||
|
setIsContentChanged(true) |
||||||
|
if (path.endsWith('.circom')) { |
||||||
|
plugin.parse(path, content) |
||||||
|
} |
||||||
|
}) |
||||||
|
setIsPluginActivated(true) |
||||||
|
}) |
||||||
|
|
||||||
|
// compiling events
|
||||||
|
plugin.internalEvents.on('circuit_compiling_start', () => dispatch({ type: 'SET_COMPILER_STATUS', payload: 'compiling' })) |
||||||
|
plugin.internalEvents.on('circuit_compiling_done', (signalInputs: string[]) => { |
||||||
|
signalInputs = (signalInputs || []).filter(input => input) |
||||||
|
dispatch({ type: 'SET_SIGNAL_INPUTS', payload: signalInputs }) |
||||||
|
dispatch({ type: 'SET_COMPILER_STATUS', payload: 'idle' }) |
||||||
|
}) |
||||||
|
plugin.internalEvents.on('circuit_compiling_errored', compilerErrored) |
||||||
|
|
||||||
|
// r1cs events
|
||||||
|
plugin.internalEvents.on('circuit_generating_r1cs_start', () => dispatch({ type: 'SET_COMPILER_STATUS', payload: 'generating' })) |
||||||
|
plugin.internalEvents.on('circuit_generating_r1cs_done', () => dispatch({ type: 'SET_COMPILER_STATUS', payload: 'idle' })) |
||||||
|
plugin.internalEvents.on('circuit_generating_r1cs_errored', compilerErrored) |
||||||
|
|
||||||
|
// witness events
|
||||||
|
plugin.internalEvents.on('circuit_computing_witness_start', () => dispatch({ type: 'SET_COMPILER_STATUS', payload: 'computing' })) |
||||||
|
plugin.internalEvents.on('circuit_computing_witness_done', () => { |
||||||
|
dispatch({ type: 'SET_COMPILER_STATUS', payload: 'idle' }) |
||||||
|
dispatch({ type: 'SET_COMPILER_FEEDBACK', payload: null }) |
||||||
|
}) |
||||||
|
plugin.internalEvents.on('circuit_computing_witness_errored', compilerErrored) |
||||||
|
|
||||||
|
// parsing events
|
||||||
|
plugin.internalEvents.on('circuit_parsing_done', (_, filePathToId) => { |
||||||
|
dispatch({ type: 'SET_FILE_PATH_TO_ID', payload: filePathToId }) |
||||||
|
dispatch({ type: 'SET_COMPILER_FEEDBACK', payload: null }) |
||||||
|
}) |
||||||
|
plugin.internalEvents.on('circuit_parsing_errored', (report) => { |
||||||
|
dispatch({ type: 'SET_COMPILER_STATUS', payload: 'errored' }) |
||||||
|
dispatch({ type: 'SET_COMPILER_FEEDBACK', payload: report }) |
||||||
|
}) |
||||||
|
plugin.internalEvents.on('circuit_parsing_warning', (report) => { |
||||||
|
dispatch({ type: 'SET_COMPILER_STATUS', payload: 'warning' }) |
||||||
|
dispatch({ type: 'SET_COMPILER_FEEDBACK', payload: report }) |
||||||
|
}) |
||||||
}, []) |
}, []) |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
if (isContentChanged) { |
||||||
|
(async () => { |
||||||
|
if (appState.autoCompile) await compileCircuit(plugin, appState) |
||||||
|
})() |
||||||
|
setIsContentChanged(false) |
||||||
|
} |
||||||
|
}, [appState.autoCompile, isContentChanged]) |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
if (isPluginActivated) { |
||||||
|
setCurrentLocale() |
||||||
|
} |
||||||
|
}, [isPluginActivated]) |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
if (appState.filePath) { |
||||||
|
(async () => { |
||||||
|
if (appState.autoCompile) await compileCircuit(plugin, appState) |
||||||
|
})() |
||||||
|
} |
||||||
|
}, [appState.filePath]) |
||||||
|
|
||||||
|
const setCurrentLocale = async () => { |
||||||
|
// @ts-ignore
|
||||||
|
const currentLocale = await plugin.call('locale', 'currentLocale') |
||||||
|
|
||||||
|
setLocale(currentLocale) |
||||||
|
} |
||||||
|
|
||||||
|
const compilerErrored = (err: ErrorEvent) => { |
||||||
|
dispatch({ type: 'SET_COMPILER_STATUS', payload: 'errored' }) |
||||||
|
try { |
||||||
|
const report = JSON.parse(err.message) |
||||||
|
|
||||||
|
dispatch({ type: 'SET_COMPILER_FEEDBACK', payload: report }) |
||||||
|
} catch (e) { |
||||||
|
dispatch({ type: 'SET_COMPILER_FEEDBACK', payload: err.message }) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
const value = { |
||||||
|
appState, |
||||||
|
dispatch, |
||||||
|
plugin |
||||||
|
} |
||||||
|
|
||||||
return ( |
return ( |
||||||
<div className="App"> |
<div className="circuit_compiler_app"> |
||||||
|
<RenderIf condition={locale.messages}> |
||||||
|
<IntlProvider locale={locale.code} messages={locale.messages}> |
||||||
|
<CircuitAppContext.Provider value={value}> |
||||||
|
<Container /> |
||||||
|
</CircuitAppContext.Provider> |
||||||
|
</IntlProvider> |
||||||
|
</RenderIf> |
||||||
</div> |
</div> |
||||||
) |
) |
||||||
} |
} |
||||||
|
|
||||||
export default App |
export default App |
||||||
|
@ -0,0 +1,11 @@ |
|||||||
|
import { CompileBtn } from "./compileBtn"; |
||||||
|
import { R1CSBtn } from "./r1csBtn"; |
||||||
|
|
||||||
|
export function CircuitActions () { |
||||||
|
return ( |
||||||
|
<div className="pb-3"> |
||||||
|
<CompileBtn /> |
||||||
|
<R1CSBtn /> |
||||||
|
</div> |
||||||
|
) |
||||||
|
} |
@ -0,0 +1,51 @@ |
|||||||
|
import { CustomTooltip, RenderIf, RenderIfNot, extractNameFromKey } from "@remix-ui/helper"; |
||||||
|
import { useContext } from "react"; |
||||||
|
import { CircuitAppContext } from "../contexts"; |
||||||
|
import { FormattedMessage } from "react-intl"; |
||||||
|
import { compileCircuit } from "../actions"; |
||||||
|
|
||||||
|
export function CompileBtn () { |
||||||
|
const { plugin, appState } = useContext(CircuitAppContext) |
||||||
|
|
||||||
|
return ( |
||||||
|
<button |
||||||
|
className="btn btn-primary btn-block d-block w-100 text-break mb-1 mt-3" |
||||||
|
onClick={() => { compileCircuit(plugin, appState) }} |
||||||
|
disabled={(appState.filePath === "") || (appState.status === "compiling") || (appState.status === "generating")} |
||||||
|
> |
||||||
|
<CustomTooltip |
||||||
|
placement="auto" |
||||||
|
tooltipId="overlay-tooltip-compile" |
||||||
|
tooltipText={ |
||||||
|
<div className="text-left"> |
||||||
|
<div> |
||||||
|
<b>Ctrl+S</b> to compile {appState.filePath} |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
} |
||||||
|
> |
||||||
|
<div className="d-flex align-items-center justify-content-center"> |
||||||
|
<RenderIf condition={appState.status === 'compiling'}> |
||||||
|
<i className="fas fa-sync fa-spin mr-2" aria-hidden="true"></i> |
||||||
|
</RenderIf> |
||||||
|
<RenderIfNot condition={appState.status === 'compiling'}> |
||||||
|
<i className="fas fa-sync mr-2" aria-hidden="true"></i> |
||||||
|
</RenderIfNot> |
||||||
|
<div className="text-truncate overflow-hidden text-nowrap"> |
||||||
|
<span> |
||||||
|
<FormattedMessage id="circuit.compile" /> |
||||||
|
</span> |
||||||
|
<span className="ml-1 text-nowrap"> |
||||||
|
<RenderIf condition={appState.filePath === ""}> |
||||||
|
<FormattedMessage id="circuit.noFileSelected" /> |
||||||
|
</RenderIf> |
||||||
|
<RenderIfNot condition={appState.filePath === ""}> |
||||||
|
<>{extractNameFromKey(appState.filePath)}</> |
||||||
|
</RenderIfNot> |
||||||
|
</span> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</CustomTooltip> |
||||||
|
</button> |
||||||
|
) |
||||||
|
} |
@ -0,0 +1,36 @@ |
|||||||
|
import { useState } from "react" |
||||||
|
import { FormattedMessage } from "react-intl" |
||||||
|
import { RenderIf, RenderIfNot } from "@remix-ui/helper" |
||||||
|
|
||||||
|
export function ConfigToggler ({ children }: { children: JSX.Element }) { |
||||||
|
const [toggleExpander, setToggleExpander] = useState<boolean>(false) |
||||||
|
|
||||||
|
const toggleConfigurations = () => { |
||||||
|
setToggleExpander(!toggleExpander) |
||||||
|
} |
||||||
|
|
||||||
|
return ( |
||||||
|
<div> |
||||||
|
<div className="d-flex circuit_config_section justify-content-between" onClick={toggleConfigurations}> |
||||||
|
<div className="d-flex"> |
||||||
|
<label className="mt-1 circuit_config_section"> |
||||||
|
<FormattedMessage id="circuit.advancedConfigurations" /> |
||||||
|
</label> |
||||||
|
</div> |
||||||
|
<div> |
||||||
|
<span data-id="scConfigExpander" onClick={toggleConfigurations}> |
||||||
|
<RenderIf condition={toggleExpander}> |
||||||
|
<i className="fas fa-angle-down" aria-hidden="true"></i> |
||||||
|
</RenderIf> |
||||||
|
<RenderIfNot condition={toggleExpander}> |
||||||
|
<i className="fas fa-angle-right" aria-hidden="true"></i> |
||||||
|
</RenderIfNot> |
||||||
|
</span> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<RenderIf condition={toggleExpander}> |
||||||
|
{ children } |
||||||
|
</RenderIf> |
||||||
|
</div> |
||||||
|
) |
||||||
|
} |
@ -0,0 +1,38 @@ |
|||||||
|
import { CustomTooltip } from "@remix-ui/helper" |
||||||
|
import { FormattedMessage } from "react-intl" |
||||||
|
import { ConfigurationsProps, PrimeValue } from "../types" |
||||||
|
|
||||||
|
export function Configurations ({primeValue, setPrimeValue}: ConfigurationsProps) { |
||||||
|
return ( |
||||||
|
<div className="pb-2 border-bottom flex-column"> |
||||||
|
<div className="flex-column d-flex"> |
||||||
|
<div className="mb-2 ml-0"> |
||||||
|
<label className="circuit_inner_label form-check-label" htmlFor="circuitPrimeSelector"> |
||||||
|
<FormattedMessage id="circuit.prime" /> |
||||||
|
</label> |
||||||
|
<CustomTooltip |
||||||
|
placement={"auto"} |
||||||
|
tooltipId="circuitPrimeLabelTooltip" |
||||||
|
tooltipClasses="text-nowrap" |
||||||
|
tooltipText={<span>{'To choose the prime number to use to generate the circuit. Receives the name of the curve (bn128, bls12381, goldilocks) [default: bn128]'}</span>} |
||||||
|
> |
||||||
|
<div> |
||||||
|
<select |
||||||
|
onChange={(e) => setPrimeValue(e.target.value as PrimeValue)} |
||||||
|
value={primeValue} |
||||||
|
className="custom-select" |
||||||
|
style={{ |
||||||
|
pointerEvents: 'auto' |
||||||
|
}} |
||||||
|
> |
||||||
|
<option value="bn128">bn128</option> |
||||||
|
<option value="bls12381">bls12381</option> |
||||||
|
<option value="goldilocks">goldilocks</option> |
||||||
|
</select> |
||||||
|
</div> |
||||||
|
</CustomTooltip> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
) |
||||||
|
} |
@ -0,0 +1,81 @@ |
|||||||
|
import { useContext } from 'react' |
||||||
|
import { CustomTooltip, RenderIf } from '@remix-ui/helper' |
||||||
|
import {FormattedMessage} from 'react-intl' |
||||||
|
import { CircuitAppContext } from '../contexts' |
||||||
|
import { CompileOptions } from './options' |
||||||
|
import { VersionList } from './versions' |
||||||
|
import { ConfigToggler } from './configToggler' |
||||||
|
import { Configurations } from './configurations' |
||||||
|
import { CircuitActions } from './actions' |
||||||
|
import { WitnessToggler } from './witnessToggler' |
||||||
|
import { WitnessSection } from './witness' |
||||||
|
import { CompilerFeedback } from './feedback' |
||||||
|
import { PrimeValue } from '../types' |
||||||
|
|
||||||
|
export function Container () { |
||||||
|
const circuitApp = useContext(CircuitAppContext) |
||||||
|
|
||||||
|
const showCompilerLicense = (message = 'License not available') => { |
||||||
|
// @ts-ignore
|
||||||
|
circuitApp.plugin.call('notification', 'modal', { id: 'modal_circuit_compiler_license', title: 'Compiler License', message }) |
||||||
|
} |
||||||
|
|
||||||
|
const handleVersionSelect = (version: string) => { |
||||||
|
circuitApp.dispatch({ type: 'SET_COMPILER_VERSION', payload: version }) |
||||||
|
} |
||||||
|
|
||||||
|
const handleOpenErrorLocation = async (location: string, startRange: string) => { |
||||||
|
if (location) { |
||||||
|
const fullPathLocation = await circuitApp.plugin.resolveReportPath(location) |
||||||
|
|
||||||
|
await circuitApp.plugin.call('fileManager', 'open', fullPathLocation) |
||||||
|
// @ts-ignore
|
||||||
|
const startPosition: { lineNumber: number; column: number } = await circuitApp.plugin.call('editor', 'getPositionAt', startRange) |
||||||
|
// @ts-ignore
|
||||||
|
await circuitApp.plugin.call('editor', 'gotoLine', startPosition.lineNumber - 1, startPosition.column) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
const handlePrimeChange = (value: string) => { |
||||||
|
circuitApp.dispatch({ type: 'SET_PRIME_VALUE', payload: value as PrimeValue }) |
||||||
|
} |
||||||
|
|
||||||
|
const handleCircuitAutoCompile = (value: boolean) => { |
||||||
|
circuitApp.dispatch({ type: 'SET_AUTO_COMPILE', payload: value }) |
||||||
|
} |
||||||
|
|
||||||
|
const handleCircuitHideWarnings = (value: boolean) => { |
||||||
|
circuitApp.dispatch({ type: 'SET_HIDE_WARNINGS', payload: value }) |
||||||
|
} |
||||||
|
|
||||||
|
return ( |
||||||
|
<section> |
||||||
|
<article> |
||||||
|
<div className="pt-0 circuit_section"> |
||||||
|
<div className="mb-1"> |
||||||
|
<label className="circuit_label form-check-label" htmlFor="versionSelector"> |
||||||
|
<FormattedMessage id="circuit.compiler" /> |
||||||
|
</label> |
||||||
|
<CustomTooltip placement="top" tooltipId="showCompilerTooltip" tooltipClasses="text-nowrap" tooltipText={'See compiler license'}> |
||||||
|
<span className="fa fa-file-text-o border-0 p-0 ml-2" onClick={() => showCompilerLicense()}></span> |
||||||
|
</CustomTooltip> |
||||||
|
<VersionList setVersion={handleVersionSelect} versionList={circuitApp.appState.versionList} currentVersion={circuitApp.appState.version} /> |
||||||
|
<CompileOptions setCircuitAutoCompile={handleCircuitAutoCompile} setCircuitHideWarnings={handleCircuitHideWarnings} autoCompile={circuitApp.appState.autoCompile} hideWarnings={circuitApp.appState.hideWarnings} /> |
||||||
|
<ConfigToggler> |
||||||
|
<Configurations setPrimeValue={handlePrimeChange} primeValue={circuitApp.appState.primeValue} /> |
||||||
|
</ConfigToggler> |
||||||
|
<CircuitActions /> |
||||||
|
<RenderIf condition={circuitApp.appState.signalInputs.length > 0}> |
||||||
|
<WitnessToggler> |
||||||
|
<WitnessSection plugin={circuitApp.plugin} signalInputs={circuitApp.appState.signalInputs} status={circuitApp.appState.status} /> |
||||||
|
</WitnessToggler> |
||||||
|
</RenderIf> |
||||||
|
<RenderIf condition={circuitApp.appState.status !== 'compiling' && circuitApp.appState.status !== 'computing' && circuitApp.appState.status !== 'generating'}> |
||||||
|
<CompilerFeedback feedback={circuitApp.appState.feedback} filePathToId={circuitApp.appState.filePathToId} openErrorLocation={handleOpenErrorLocation} hideWarnings={circuitApp.appState.hideWarnings} /> |
||||||
|
</RenderIf> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</article> |
||||||
|
</section> |
||||||
|
) |
||||||
|
} |
@ -0,0 +1,60 @@ |
|||||||
|
import { useState } from 'react' |
||||||
|
import { CompilerFeedbackProps, CompilerReport } from '../types' |
||||||
|
import { RenderIf } from '@remix-ui/helper' |
||||||
|
import {CopyToClipboard} from '@remix-ui/clipboard' |
||||||
|
import { FeedbackAlert } from './feedbackAlert' |
||||||
|
|
||||||
|
export function CompilerFeedback ({ feedback, filePathToId, hideWarnings, openErrorLocation }: CompilerFeedbackProps) { |
||||||
|
const [ showException, setShowException ] = useState<boolean>(true) |
||||||
|
|
||||||
|
const handleCloseException = () => { |
||||||
|
setShowException(false) |
||||||
|
} |
||||||
|
|
||||||
|
const handleOpenError = (report: CompilerReport) => { |
||||||
|
if (report.labels.length > 0) { |
||||||
|
openErrorLocation(filePathToId[report.labels[0].file_id], report.labels[0].range.start) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return ( |
||||||
|
<div> |
||||||
|
<div className="circuit_errors_box py-4"> |
||||||
|
<RenderIf condition={ (typeof feedback === "string") && showException }> |
||||||
|
<div className="circuit_feedback error alert alert-danger"> |
||||||
|
<span> { feedback } </span> |
||||||
|
<div className="close" data-id="renderer" onClick={handleCloseException}> |
||||||
|
<i className="fas fa-times"></i> |
||||||
|
</div> |
||||||
|
<div className="d-flex pt-1 flex-row-reverse"> |
||||||
|
<span className="ml-3 pt-1 py-1" > |
||||||
|
<CopyToClipboard content={feedback} className="p-0 m-0 far fa-copy error" direction={'top'} /> |
||||||
|
</span> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</RenderIf> |
||||||
|
<RenderIf condition={ Array.isArray(feedback) }> |
||||||
|
<> |
||||||
|
{ |
||||||
|
Array.isArray(feedback) && feedback.map((response, index) => ( |
||||||
|
<div key={index} onClick={() => handleOpenError(response)}> |
||||||
|
<RenderIf condition={response.type === 'Error'}> |
||||||
|
<div className={`circuit_feedback ${response.type.toLowerCase()} alert alert-danger`}> |
||||||
|
<FeedbackAlert message={response.message} location={ response.labels[0] ? response.labels[0].message + ` ${filePathToId[response.labels[0].file_id]}:${response.labels[0].range.start}:${response.labels[0].range.end}` : null} /> |
||||||
|
</div> |
||||||
|
</RenderIf> |
||||||
|
<RenderIf condition={(response.type === 'Warning') && !hideWarnings}> |
||||||
|
<div className={`circuit_feedback ${response.type.toLowerCase()} alert alert-warning`}> |
||||||
|
<FeedbackAlert message={response.message} location={null} /> |
||||||
|
</div> |
||||||
|
</RenderIf> |
||||||
|
</div> |
||||||
|
) |
||||||
|
) |
||||||
|
} |
||||||
|
</> |
||||||
|
</RenderIf> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
) |
||||||
|
} |
@ -0,0 +1,31 @@ |
|||||||
|
import { useState } from 'react' |
||||||
|
import { FeedbackAlertProps } from '../types' |
||||||
|
import { RenderIf } from '@remix-ui/helper' |
||||||
|
import {CopyToClipboard} from '@remix-ui/clipboard' |
||||||
|
|
||||||
|
export function FeedbackAlert ({ message, location }: FeedbackAlertProps) { |
||||||
|
const [ showAlert, setShowAlert] = useState<boolean>(true) |
||||||
|
|
||||||
|
const handleCloseAlert = () => { |
||||||
|
setShowAlert(false) |
||||||
|
} |
||||||
|
|
||||||
|
return ( |
||||||
|
<RenderIf condition={showAlert}> |
||||||
|
<> |
||||||
|
<span> { message } </span> |
||||||
|
<RenderIf condition={location !== null}> |
||||||
|
<span> { location }</span> |
||||||
|
</RenderIf> |
||||||
|
<div className="close" data-id="renderer" onClick={handleCloseAlert}> |
||||||
|
<i className="fas fa-times"></i> |
||||||
|
</div> |
||||||
|
<div className="d-flex pt-1 flex-row-reverse"> |
||||||
|
<span className="ml-3 pt-1 py-1" > |
||||||
|
<CopyToClipboard content={message} className="p-0 m-0 far fa-copy error" direction={'top'} /> |
||||||
|
</span> |
||||||
|
</div> |
||||||
|
</> |
||||||
|
</RenderIf> |
||||||
|
) |
||||||
|
} |
@ -0,0 +1,36 @@ |
|||||||
|
import {FormattedMessage} from 'react-intl' |
||||||
|
import { CompileOptionsProps } from '../types' |
||||||
|
|
||||||
|
export function CompileOptions ({autoCompile, hideWarnings, setCircuitAutoCompile, setCircuitHideWarnings}: CompileOptionsProps) { |
||||||
|
|
||||||
|
return ( |
||||||
|
<div className='pb-2'> |
||||||
|
<div className="mt-2 custom-control custom-checkbox"> |
||||||
|
<input |
||||||
|
className="custom-control-input" |
||||||
|
type="checkbox" |
||||||
|
onChange={(e) => setCircuitAutoCompile(e.target.checked)} |
||||||
|
title="Auto compile" |
||||||
|
checked={autoCompile} |
||||||
|
id="autoCompileCircuit" |
||||||
|
/> |
||||||
|
<label className="form-check-label custom-control-label" htmlFor="autoCompileCircuit"> |
||||||
|
<FormattedMessage id="circuit.autoCompile" /> |
||||||
|
</label> |
||||||
|
</div> |
||||||
|
<div className="mt-1 mb-2 circuit_warnings_box custom-control custom-checkbox"> |
||||||
|
<input |
||||||
|
className="custom-control-input" |
||||||
|
onChange={(e) => setCircuitHideWarnings(e.target.checked)} |
||||||
|
id="hideCircuitWarnings" |
||||||
|
type="checkbox" |
||||||
|
title="Hide warnings" |
||||||
|
checked={hideWarnings} |
||||||
|
/> |
||||||
|
<label className="form-check-label custom-control-label" htmlFor="hideCircuitWarnings"> |
||||||
|
<FormattedMessage id="solidity.hideWarnings" /> |
||||||
|
</label> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
) |
||||||
|
} |
@ -0,0 +1,43 @@ |
|||||||
|
import { CustomTooltip, RenderIf, RenderIfNot } from "@remix-ui/helper"; |
||||||
|
import { useContext } from "react"; |
||||||
|
import { CircuitAppContext } from "../contexts"; |
||||||
|
import { FormattedMessage } from "react-intl"; |
||||||
|
import { generateR1cs } from "../actions"; |
||||||
|
|
||||||
|
export function R1CSBtn () { |
||||||
|
const { plugin, appState } = useContext(CircuitAppContext) |
||||||
|
|
||||||
|
return ( |
||||||
|
<button |
||||||
|
className="btn btn-secondary btn-block d-block w-100 text-break mb-1 mt-2" |
||||||
|
onClick={() => { generateR1cs(plugin, appState) }} |
||||||
|
disabled={(appState.filePath === "") || (appState.status === "compiling") || (appState.status === "generating") || (appState.status === "computing")} |
||||||
|
> |
||||||
|
<CustomTooltip |
||||||
|
placement="auto" |
||||||
|
tooltipId="overlay-tooltip-compile" |
||||||
|
tooltipText={ |
||||||
|
<div className="text-left"> |
||||||
|
<div> |
||||||
|
Outputs the constraints in r1cs format |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
} |
||||||
|
> |
||||||
|
<div className="d-flex align-items-center justify-content-center"> |
||||||
|
<RenderIf condition={appState.status === 'generating'}> |
||||||
|
<i className="fas fa-sync fa-spin mr-2" aria-hidden="true"></i> |
||||||
|
</RenderIf> |
||||||
|
<RenderIfNot condition={appState.status === 'generating'}> |
||||||
|
<i className="fas fa-sync mr-2" aria-hidden="true"></i> |
||||||
|
</RenderIfNot> |
||||||
|
<div className="text-truncate overflow-hidden text-nowrap"> |
||||||
|
<span> |
||||||
|
<FormattedMessage id="circuit.generateR1cs" /> |
||||||
|
</span> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</CustomTooltip> |
||||||
|
</button> |
||||||
|
) |
||||||
|
} |
@ -0,0 +1,26 @@ |
|||||||
|
import { RenderIf } from "@remix-ui/helper"; |
||||||
|
import { AppState } from "../types"; |
||||||
|
|
||||||
|
export function VersionList ({ currentVersion, versionList, setVersion }: { versionList: AppState['versionList'], currentVersion: string, setVersion: (version: string) => void }) { |
||||||
|
const versionListKeys = Object.keys(versionList) |
||||||
|
|
||||||
|
return ( |
||||||
|
<select |
||||||
|
value={currentVersion} |
||||||
|
onChange={(e) => setVersion(e.target.value)} |
||||||
|
className="custom-select" |
||||||
|
> |
||||||
|
<RenderIf condition={versionListKeys.length > 0}> |
||||||
|
<> |
||||||
|
{ |
||||||
|
versionListKeys.map((version, index) => ( |
||||||
|
<option value={version} key={index}> |
||||||
|
{ versionList[version].name } |
||||||
|
</option> |
||||||
|
)) |
||||||
|
} |
||||||
|
</> |
||||||
|
</RenderIf> |
||||||
|
</select> |
||||||
|
) |
||||||
|
} |
@ -0,0 +1,50 @@ |
|||||||
|
import { RenderIf, RenderIfNot } from "@remix-ui/helper"; |
||||||
|
import { FormattedMessage } from "react-intl"; |
||||||
|
import { CompilerStatus } from "../types"; |
||||||
|
import { computeWitness } from "../actions"; |
||||||
|
import { useState } from "react"; |
||||||
|
import type { CircomPluginClient } from "../services/circomPluginClient"; |
||||||
|
|
||||||
|
export function WitnessSection ({ plugin, signalInputs, status }: {plugin: CircomPluginClient, signalInputs: string[], status: CompilerStatus}) { |
||||||
|
const [witnessValues, setWitnessValues] = useState<Record<string, string>>({}) |
||||||
|
|
||||||
|
const handleSignalInput = (e: any) => { |
||||||
|
setWitnessValues({ |
||||||
|
...witnessValues, |
||||||
|
[e.target.name]: e.target.value |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
return ( |
||||||
|
<div className="pb-2 border-bottom flex-column"> |
||||||
|
<div className="flex-column d-flex"> |
||||||
|
<RenderIf condition={signalInputs.length > 0}> |
||||||
|
<> |
||||||
|
{ |
||||||
|
signalInputs.map((input, index) => ( |
||||||
|
<div className="mb-2 ml-0" key={index}> |
||||||
|
<label className="circuit_inner_label form-check-label" htmlFor="circuitPrimeSelector"> |
||||||
|
<FormattedMessage id="circuit.signalInput" /> { input } |
||||||
|
</label> |
||||||
|
<input className="form-control m-0 txinput" placeholder={input} name={input} onChange={handleSignalInput} /> |
||||||
|
</div> |
||||||
|
)) |
||||||
|
} |
||||||
|
<button |
||||||
|
className="btn btn-sm btn-secondary" |
||||||
|
onClick={() => { computeWitness(plugin, status, witnessValues) }} |
||||||
|
disabled={(status === "compiling") || (status === "generating") || (status === "computing")}> |
||||||
|
<RenderIf condition={status === 'computing'}> |
||||||
|
<i className="fas fa-sync fa-spin mr-2" aria-hidden="true"></i> |
||||||
|
</RenderIf> |
||||||
|
<RenderIfNot condition={status === 'computing'}> |
||||||
|
<i className="fas fa-sync mr-2" aria-hidden="true"></i> |
||||||
|
</RenderIfNot> |
||||||
|
<FormattedMessage id="circuit.compute" /> |
||||||
|
</button> |
||||||
|
</> |
||||||
|
</RenderIf> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
) |
||||||
|
} |
@ -0,0 +1,36 @@ |
|||||||
|
import { useState } from "react" |
||||||
|
import { FormattedMessage } from "react-intl" |
||||||
|
import { RenderIf, RenderIfNot } from "@remix-ui/helper" |
||||||
|
|
||||||
|
export function WitnessToggler ({ children }: { children: JSX.Element }) { |
||||||
|
const [toggleExpander, setToggleExpander] = useState<boolean>(false) |
||||||
|
|
||||||
|
const toggleConfigurations = () => { |
||||||
|
setToggleExpander(!toggleExpander) |
||||||
|
} |
||||||
|
|
||||||
|
return ( |
||||||
|
<div> |
||||||
|
<div className="d-flex circuit_config_section justify-content-between" onClick={toggleConfigurations}> |
||||||
|
<div className="d-flex"> |
||||||
|
<label className="mt-1 circuit_config_section"> |
||||||
|
<FormattedMessage id="circuit.computeWitness" /> |
||||||
|
</label> |
||||||
|
</div> |
||||||
|
<div> |
||||||
|
<span data-id="scConfigExpander" onClick={toggleConfigurations}> |
||||||
|
<RenderIf condition={toggleExpander}> |
||||||
|
<i className="fas fa-angle-down" aria-hidden="true"></i> |
||||||
|
</RenderIf> |
||||||
|
<RenderIfNot condition={toggleExpander}> |
||||||
|
<i className="fas fa-angle-right" aria-hidden="true"></i> |
||||||
|
</RenderIfNot> |
||||||
|
</span> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<RenderIf condition={toggleExpander}> |
||||||
|
{ children } |
||||||
|
</RenderIf> |
||||||
|
</div> |
||||||
|
) |
||||||
|
} |
@ -0,0 +1,4 @@ |
|||||||
|
import {createContext} from 'react' |
||||||
|
import {ICircuitAppContext} from '../types' |
||||||
|
|
||||||
|
export const CircuitAppContext = createContext<ICircuitAppContext>({} as ICircuitAppContext) |
@ -0,0 +1,77 @@ |
|||||||
|
import {Actions, AppState} from '../types' |
||||||
|
import { compiler_list } from 'circom_wasm' |
||||||
|
|
||||||
|
export const appInitialState: AppState = { |
||||||
|
version: compiler_list.latest, |
||||||
|
versionList: compiler_list.wasm_builds, |
||||||
|
filePath: "", |
||||||
|
filePathToId: {}, |
||||||
|
status: "idle", |
||||||
|
primeValue: "bn128", |
||||||
|
autoCompile: false, |
||||||
|
hideWarnings: false, |
||||||
|
signalInputs: [], |
||||||
|
feedback: null |
||||||
|
} |
||||||
|
|
||||||
|
export const appReducer = (state = appInitialState, action: Actions): AppState => { |
||||||
|
switch (action.type) { |
||||||
|
|
||||||
|
case 'SET_COMPILER_VERSION': |
||||||
|
return { |
||||||
|
...state, |
||||||
|
version: action.payload |
||||||
|
} |
||||||
|
|
||||||
|
case 'SET_FILE_PATH': |
||||||
|
return { |
||||||
|
...state, |
||||||
|
filePath: action.payload |
||||||
|
} |
||||||
|
|
||||||
|
case 'SET_COMPILER_STATUS': |
||||||
|
return { |
||||||
|
...state, |
||||||
|
status: action.payload |
||||||
|
} |
||||||
|
|
||||||
|
case 'SET_PRIME_VALUE': |
||||||
|
return { |
||||||
|
...state, |
||||||
|
primeValue: action.payload |
||||||
|
} |
||||||
|
|
||||||
|
case 'SET_AUTO_COMPILE': |
||||||
|
return { |
||||||
|
...state, |
||||||
|
autoCompile: action.payload |
||||||
|
} |
||||||
|
|
||||||
|
case 'SET_HIDE_WARNINGS': |
||||||
|
return { |
||||||
|
...state, |
||||||
|
hideWarnings: action.payload |
||||||
|
} |
||||||
|
|
||||||
|
case 'SET_SIGNAL_INPUTS': |
||||||
|
return { |
||||||
|
...state, |
||||||
|
signalInputs: action.payload |
||||||
|
} |
||||||
|
|
||||||
|
case 'SET_COMPILER_FEEDBACK': |
||||||
|
return { |
||||||
|
...state, |
||||||
|
feedback: action.payload |
||||||
|
} |
||||||
|
|
||||||
|
case 'SET_FILE_PATH_TO_ID': |
||||||
|
return { |
||||||
|
...state, |
||||||
|
filePathToId: action.payload |
||||||
|
} |
||||||
|
|
||||||
|
default: |
||||||
|
throw new Error() |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,87 @@ |
|||||||
|
import { compiler_list } from 'circom_wasm' |
||||||
|
import {Dispatch} from 'react' |
||||||
|
import { CircomPluginClient } from '../services/circomPluginClient' |
||||||
|
|
||||||
|
export type CompilerStatus = "compiling" | "generating" | "computing" | "idle" | "errored" | "warning" |
||||||
|
export interface ICircuitAppContext { |
||||||
|
appState: AppState |
||||||
|
dispatch: Dispatch<Actions>, |
||||||
|
plugin: CircomPluginClient |
||||||
|
} |
||||||
|
|
||||||
|
export interface ActionPayloadTypes { |
||||||
|
SET_COMPILER_VERSION: string, |
||||||
|
SET_FILE_PATH: string, |
||||||
|
SET_COMPILER_STATUS: CompilerStatus, |
||||||
|
SET_PRIME_VALUE: PrimeValue, |
||||||
|
SET_AUTO_COMPILE: boolean, |
||||||
|
SET_HIDE_WARNINGS: boolean, |
||||||
|
SET_SIGNAL_INPUTS: string[], |
||||||
|
SET_COMPILER_FEEDBACK: string | CompilerReport[] |
||||||
|
SET_FILE_PATH_TO_ID: Record<number, 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 interface AppState { |
||||||
|
version: string, |
||||||
|
versionList: typeof compiler_list.wasm_builds, |
||||||
|
filePath: string, |
||||||
|
filePathToId: Record<string, string>, |
||||||
|
status: CompilerStatus, |
||||||
|
primeValue: PrimeValue, |
||||||
|
autoCompile: boolean, |
||||||
|
hideWarnings: boolean, |
||||||
|
signalInputs: string[], |
||||||
|
feedback: string | CompilerReport[] |
||||||
|
} |
||||||
|
|
||||||
|
export type CompilationConfig = { |
||||||
|
prime: PrimeValue, |
||||||
|
version: string |
||||||
|
} |
||||||
|
|
||||||
|
export type PrimeValue = "bn128" | "bls12381" | "goldilocks" |
||||||
|
|
||||||
|
export type CompilerFeedbackProps = { |
||||||
|
feedback: string | CompilerReport[], |
||||||
|
filePathToId: Record<string, string>, |
||||||
|
openErrorLocation: (location: string, startRange: string) => void, |
||||||
|
hideWarnings: boolean |
||||||
|
} |
||||||
|
|
||||||
|
export type CompilerReport = { |
||||||
|
type: "Error" | "Bug" | "Help" | "Note" | "Warning" | "Unknown", |
||||||
|
message: string, |
||||||
|
labels: { |
||||||
|
style: "Primary" | "Secondary" | "Unknown", |
||||||
|
file_id: string, |
||||||
|
range: { |
||||||
|
start: string, |
||||||
|
end: string |
||||||
|
}, |
||||||
|
message: string |
||||||
|
}[], |
||||||
|
notes: string[] |
||||||
|
} |
||||||
|
|
||||||
|
export type FeedbackAlertProps = { |
||||||
|
message: string, |
||||||
|
location: string |
||||||
|
} |
||||||
|
|
||||||
|
export type ConfigurationsProps = { |
||||||
|
setPrimeValue: (prime: PrimeValue) => void, |
||||||
|
primeValue: PrimeValue |
||||||
|
} |
||||||
|
|
||||||
|
export type CompileOptionsProps = { |
||||||
|
setCircuitAutoCompile: (value: boolean) => void, |
||||||
|
setCircuitHideWarnings: (value: boolean) => void, |
||||||
|
autoCompile: boolean, |
||||||
|
hideWarnings: boolean |
||||||
|
} |
@ -0,0 +1,88 @@ |
|||||||
|
body { |
||||||
|
font-size : .8rem; |
||||||
|
} |
||||||
|
.circuit_section { |
||||||
|
padding: 12px 24px 16px; |
||||||
|
} |
||||||
|
.circuit_label { |
||||||
|
margin-bottom: 2px; |
||||||
|
font-size: 11px; |
||||||
|
line-height: 12px; |
||||||
|
text-transform: uppercase; |
||||||
|
} |
||||||
|
.circuit_warnings_box { |
||||||
|
display: flex; |
||||||
|
align-items: center; |
||||||
|
} |
||||||
|
.circuit_warnings_box label { |
||||||
|
margin: 0; |
||||||
|
} |
||||||
|
.circuit_config_section:hover { |
||||||
|
cursor: pointer; |
||||||
|
} |
||||||
|
.circuit_config_section { |
||||||
|
font-size: 1rem; |
||||||
|
} |
||||||
|
.circuit_config { |
||||||
|
display: flex; |
||||||
|
align-items: center; |
||||||
|
} |
||||||
|
.circuit_config label { |
||||||
|
margin: 0; |
||||||
|
} |
||||||
|
.circuit_inner_label { |
||||||
|
margin-bottom: 2px; |
||||||
|
font-size: 11px; |
||||||
|
line-height: 12px; |
||||||
|
text-transform: uppercase; |
||||||
|
} |
||||||
|
.circuit_errors_box { |
||||||
|
padding-left: 5px; |
||||||
|
padding-right: 5px; |
||||||
|
word-break: break-word; |
||||||
|
} |
||||||
|
.circuit_feedback.success, |
||||||
|
.circuit_feedback.error, |
||||||
|
.circuit_feedback.warning { |
||||||
|
white-space: pre-line; |
||||||
|
word-wrap: break-word; |
||||||
|
cursor: pointer; |
||||||
|
position: relative; |
||||||
|
margin: 0.5em 0 1em 0; |
||||||
|
border-radius: 5px; |
||||||
|
line-height: 20px; |
||||||
|
padding: 8px 15px; |
||||||
|
} |
||||||
|
|
||||||
|
.circuit_feedback.success pre, |
||||||
|
.circuit_feedback.error pre, |
||||||
|
.circuit_feedback.warning pre { |
||||||
|
white-space: pre-line; |
||||||
|
overflow-y: hidden; |
||||||
|
background-color: transparent; |
||||||
|
margin: 0; |
||||||
|
font-size: 12px; |
||||||
|
border: 0 none; |
||||||
|
padding: 0; |
||||||
|
border-radius: 0; |
||||||
|
} |
||||||
|
|
||||||
|
.circuit_feedback.success .close, |
||||||
|
.circuit_feedback.error .close, |
||||||
|
.circuit_feedback.warning .close { |
||||||
|
visibility: hidden; |
||||||
|
white-space: pre-line; |
||||||
|
font-weight: bold; |
||||||
|
position: absolute; |
||||||
|
color: hsl(0, 0%, 0%); /* black in style-guide.js */ |
||||||
|
top: 0; |
||||||
|
right: 0; |
||||||
|
padding: 0.5em; |
||||||
|
} |
||||||
|
|
||||||
|
.circuit_feedback.success a, |
||||||
|
.circuit_feedback.error a, |
||||||
|
.circuit_feedback.warning a { |
||||||
|
bottom: 0; |
||||||
|
right: 0; |
||||||
|
} |
@ -0,0 +1,27 @@ |
|||||||
|
pragma circom 2.1.4; |
||||||
|
|
||||||
|
include "circomlib/poseidon.circom"; |
||||||
|
// include "https://github.com/0xPARC/circom-secp256k1/blob/master/circuits/bigint.circom"; |
||||||
|
|
||||||
|
template Example () { |
||||||
|
signal input a; |
||||||
|
signal input b; |
||||||
|
signal output c; |
||||||
|
|
||||||
|
var unused = 4; |
||||||
|
c <== a * b; |
||||||
|
assert(a > 2); |
||||||
|
|
||||||
|
component hash = Poseidon(2); |
||||||
|
hash.inputs[0] <== a; |
||||||
|
hash.inputs[1] <== b; |
||||||
|
|
||||||
|
log("hash", hash.out); |
||||||
|
} |
||||||
|
|
||||||
|
component main { public [ a ] } = Example(); |
||||||
|
|
||||||
|
/* INPUT = { |
||||||
|
"a": "5", |
||||||
|
"b": "77" |
||||||
|
} */ |
File diff suppressed because one or more lines are too long
@ -0,0 +1,15 @@ |
|||||||
|
{ |
||||||
|
"circuit.compiler": "Compiler", |
||||||
|
"circuit.autoCompile": "Auto compile", |
||||||
|
"circuit.hideWarnings": "Hide warnings", |
||||||
|
"circuit.advancedConfigurations": "Advanced Configurations", |
||||||
|
"circuit.compilerConfiguration": "Compiler configuration", |
||||||
|
"circuit.prime": "Prime", |
||||||
|
"circuit.useConfigurationFile": "Use configuration file", |
||||||
|
"circuit.compile": "Compile", |
||||||
|
"circuit.noFileSelected": "no file selected", |
||||||
|
"circuit.generateR1cs": "Generate R1CS", |
||||||
|
"circuit.computeWitness": "Compute Witness", |
||||||
|
"circuit.signalInput": "Signal Input", |
||||||
|
"circuit.compute": "Compute" |
||||||
|
} |
@ -0,0 +1,15 @@ |
|||||||
|
{ |
||||||
|
"circuit.compiler": "Compiler", |
||||||
|
"circuit.autoCompile": "Auto compile", |
||||||
|
"circuit.hideWarnings": "Hide warnings", |
||||||
|
"circuit.advancedConfigurations": "Advanced Configurations", |
||||||
|
"circuit.compilerConfiguration": "Compiler configuration", |
||||||
|
"circuit.prime": "Prime", |
||||||
|
"circuit.useConfigurationFile": "Use configuration file", |
||||||
|
"circuit.compile": "Compile", |
||||||
|
"circuit.noFileSelected": "no file selected", |
||||||
|
"circuit.generateR1cs": "Generate R1CS", |
||||||
|
"circuit.computeWitness": "Compute Witness", |
||||||
|
"circuit.signalInput": "Signal Input", |
||||||
|
"circuit.compute": "Compute" |
||||||
|
} |
@ -0,0 +1,15 @@ |
|||||||
|
{ |
||||||
|
"circuit.compiler": "Compiler", |
||||||
|
"circuit.autoCompile": "Auto compile", |
||||||
|
"circuit.hideWarnings": "Hide warnings", |
||||||
|
"circuit.advancedConfigurations": "Advanced Configurations", |
||||||
|
"circuit.compilerConfiguration": "Compiler configuration", |
||||||
|
"circuit.prime": "Prime", |
||||||
|
"circuit.useConfigurationFile": "Use configuration file", |
||||||
|
"circuit.compile": "Compile", |
||||||
|
"circuit.noFileSelected": "no file selected", |
||||||
|
"circuit.generateR1cs": "Generate R1CS", |
||||||
|
"circuit.computeWitness": "Compute Witness", |
||||||
|
"circuit.signalInput": "Signal Input", |
||||||
|
"circuit.compute": "Compute" |
||||||
|
} |
@ -0,0 +1,16 @@ |
|||||||
|
{ |
||||||
|
"circuit-compiler.displayName": "Circuit 编译器", |
||||||
|
"circuit.compiler": "Compiler", |
||||||
|
"circuit.autoCompile": "自动编译", |
||||||
|
"circuit.hideWarnings": "隐藏警告", |
||||||
|
"circuit.advancedConfigurations": "高级配置", |
||||||
|
"circuit.compilerConfiguration": "编译器配置", |
||||||
|
"circuit.prime": "质数", |
||||||
|
"circuit.useConfigurationFile": "使用配置文件", |
||||||
|
"circuit.compile": "编译", |
||||||
|
"circuit.noFileSelected": "未选中文件", |
||||||
|
"circuit.generateR1cs": "生成R1CS", |
||||||
|
"circuit.computeWitness": "证人计算器", |
||||||
|
"circuit.signalInput": "输入信号", |
||||||
|
"circuit.compute": "计算" |
||||||
|
} |
After Width: | Height: | Size: 6.5 KiB |
After Width: | Height: | Size: 500 B |
File diff suppressed because one or more lines are too long
@ -1,4 +1,5 @@ |
|||||||
export * from './lib/solidity-compiler' |
export * from './lib/solidity-compiler' |
||||||
export * from './lib/logic' |
export * from './lib/logic' |
||||||
export * from './lib/logic/flattenerUtilities' |
export * from './lib/logic/flattenerUtilities' |
||||||
export * from './lib/api/compiler-api' |
export * from './lib/api/compiler-api' |
||||||
|
export * from './lib/types' |
||||||
|
@ -0,0 +1,23 @@ |
|||||||
|
// SPDX-License-Identifier: GPL-3.0 |
||||||
|
|
||||||
|
pragma solidity >=0.7.0 <0.9.0; |
||||||
|
import "remix_tests.sol"; |
||||||
|
import "remix_accounts.sol"; |
||||||
|
import "../contracts/MyToken.sol"; |
||||||
|
|
||||||
|
contract MyTokenTest { |
||||||
|
|
||||||
|
MyToken s; |
||||||
|
|
||||||
|
function beforeAll () public { |
||||||
|
address acc0 = TestsAccounts.getAccount(0); |
||||||
|
// acc0 will be set as initial owner |
||||||
|
s = new MyToken(acc0); |
||||||
|
} |
||||||
|
|
||||||
|
function testSetURI () public { |
||||||
|
string memory uri = "https://testuri.io/token"; |
||||||
|
s.setURI(uri); |
||||||
|
Assert.equal(s.uri(1), uri, "uri did not match"); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,22 @@ |
|||||||
|
// SPDX-License-Identifier: GPL-3.0 |
||||||
|
|
||||||
|
pragma solidity >=0.7.0 <0.9.0; |
||||||
|
import "remix_tests.sol"; |
||||||
|
import "remix_accounts.sol"; |
||||||
|
import "../contracts/MyToken.sol"; |
||||||
|
|
||||||
|
contract MyTokenTest { |
||||||
|
|
||||||
|
MyToken s; |
||||||
|
|
||||||
|
function beforeAll () public { |
||||||
|
address acc0 = TestsAccounts.getAccount(0); |
||||||
|
// acc0 will be set as initial owner |
||||||
|
s = new MyToken(acc0); |
||||||
|
} |
||||||
|
|
||||||
|
function testTokenNameAndSymbol () public { |
||||||
|
Assert.equal(s.name(), "MyToken", "token name did not match"); |
||||||
|
Assert.equal(s.symbol(), "MTK", "token symbol did not match"); |
||||||
|
} |
||||||
|
} |
@ -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…
Reference in new issue