diff --git a/apps/circuit-compiler/src/app/actions/index.ts b/apps/circuit-compiler/src/app/actions/index.ts index ed0e3f199a..440d098493 100644 --- a/apps/circuit-compiler/src/app/actions/index.ts +++ b/apps/circuit-compiler/src/app/actions/index.ts @@ -4,29 +4,24 @@ import { Actions, AppState } from "../types" export const compileCircuit = async (plugin: CircomPluginClient, appState: AppState, dispatch: React.Dispatch) => { try { if (appState.status !== "compiling") { - dispatch({ type: 'SET_COMPILER_STATUS', payload: 'compiling' }) await plugin.compile(appState.filePath, { version: appState.version, prime: appState.primeValue }) - dispatch({ type: 'SET_COMPILER_STATUS', payload: 'idle' }) } else { - console.log('Exisiting compilation in progress') + console.log('Exisiting circuit compilation in progress') } } catch (e) { - dispatch({ type: 'SET_COMPILER_STATUS', payload: 'errored' }) - console.error('Compiling failed: ', e) + console.error(e) + dispatch({ type: 'SET_SIGNAL_INPUTS', payload: [] }) } } export const generateR1cs = async (plugin: CircomPluginClient, appState: AppState, dispatch: React.Dispatch) => { try { if (appState.status !== "generating") { - dispatch({ type: 'SET_COMPILER_STATUS', payload: 'generating' }) await plugin.generateR1cs(appState.filePath, { version: appState.version, prime: appState.primeValue }) - dispatch({ type: 'SET_COMPILER_STATUS', payload: 'idle' }) } else { console.log('Exisiting r1cs generation in progress') } } catch (e) { - dispatch({ type: 'SET_COMPILER_STATUS', payload: 'errored' }) console.error('Generating R1CS failed: ', e) } } \ No newline at end of file diff --git a/apps/circuit-compiler/src/app/app.tsx b/apps/circuit-compiler/src/app/app.tsx index 93c405c6c5..3482ffc2ba 100644 --- a/apps/circuit-compiler/src/app/app.tsx +++ b/apps/circuit-compiler/src/app/app.tsx @@ -21,7 +21,7 @@ function App() { useEffect(() => { const plugin = new CircomPluginClient() - plugin.internalEvents.on('activated', () => { + plugin.internalEvents.on('circom_activated', () => { // @ts-ignore plugin.on('locale', 'localeChanged', (locale: any) => { setLocale(locale) @@ -40,6 +40,13 @@ function App() { }) setPlugin(plugin) }) + plugin.internalEvents.on('circuit_compiling', () => dispatch({ type: 'SET_COMPILER_STATUS', payload: 'compiling' })) + plugin.internalEvents.on('circuit_done', (signalInputs) => { + signalInputs = (signalInputs || []).filter(input => input) + dispatch({ type: 'SET_SIGNAL_INPUTS', payload: signalInputs }) + dispatch({ type: 'SET_COMPILER_STATUS', payload: 'idle' }) + }) + plugin.internalEvents.on('circuit_errored', (err) => dispatch({ type: 'SET_COMPILER_STATUS', payload: err.message })) }, []) useEffect(() => { @@ -48,7 +55,6 @@ function App() { if (appState.autoCompile) await compileCircuit(plugin, appState, dispatch) })() setIsContentChanged(false) - setSignalInput() } }, [appState.autoCompile, isContentChanged]) @@ -58,6 +64,14 @@ function App() { } }, [plugin]) + useEffect(() => { + if (appState.filePath) { + (async () => { + if (appState.autoCompile) await compileCircuit(plugin, appState, dispatch) + })() + } + }, [appState.filePath]) + const setCurrentLocale = async () => { // @ts-ignore const currentLocale = await plugin.call('locale', 'currentLocale') @@ -65,13 +79,6 @@ function App() { setLocale(currentLocale) } - const setSignalInput = () => { - const signalMatcher = /([a-z$_][a-z0-9$_]*)(\.[a-z$_][a-z0-9$_]*)*(\[\d+\])?/g - const signals = content.match(signalMatcher) - - console.log('signals: ', signals) - } - const value = { appState, dispatch, diff --git a/apps/circuit-compiler/src/app/components/container.tsx b/apps/circuit-compiler/src/app/components/container.tsx index 5eb1d690a6..d49bf14ab7 100644 --- a/apps/circuit-compiler/src/app/components/container.tsx +++ b/apps/circuit-compiler/src/app/components/container.tsx @@ -35,9 +35,11 @@ export function Container () { - - - + 0}> + + + + diff --git a/apps/circuit-compiler/src/app/components/witness.tsx b/apps/circuit-compiler/src/app/components/witness.tsx index 3b87fc5562..d4f3654176 100644 --- a/apps/circuit-compiler/src/app/components/witness.tsx +++ b/apps/circuit-compiler/src/app/components/witness.tsx @@ -1,25 +1,34 @@ -import { CustomTooltip } from "@remix-ui/helper"; +import { RenderIf, RenderIfNot } from "@remix-ui/helper"; import { FormattedMessage } from "react-intl"; +import { CompilerStatus } from "../types"; -export function WitnessSection () { +export function WitnessSection ({ signalInputs, status }: {signalInputs: string[], status: CompilerStatus}) { return (
-
- - {'To choose the prime number to use to generate the circuit. Receives the name of the curve (bn128, bls12381, goldilocks) [default: bn128]'}} - > -
- -
-
-
+ 0}> + <> + { + signalInputs.map((input, index) => ( +
+ + +
+ )) + } + + +
) diff --git a/apps/circuit-compiler/src/app/reducers/state.ts b/apps/circuit-compiler/src/app/reducers/state.ts index 3d15f79446..d331c41471 100644 --- a/apps/circuit-compiler/src/app/reducers/state.ts +++ b/apps/circuit-compiler/src/app/reducers/state.ts @@ -7,7 +7,8 @@ export const appInitialState: AppState = { filePath: "", status: "idle", primeValue: "bn128", - autoCompile: false + autoCompile: false, + signalInputs: [] } export const appReducer = (state = appInitialState, action: Actions): AppState => { @@ -43,6 +44,12 @@ export const appReducer = (state = appInitialState, action: Actions): AppState = autoCompile: action.payload } + case 'SET_SIGNAL_INPUTS': + return { + ...state, + signalInputs: action.payload + } + default: throw new Error() } diff --git a/apps/circuit-compiler/src/app/services/circomPluginClient.ts b/apps/circuit-compiler/src/app/services/circomPluginClient.ts index 3f051cc23f..59298beae4 100644 --- a/apps/circuit-compiler/src/app/services/circomPluginClient.ts +++ b/apps/circuit-compiler/src/app/services/circomPluginClient.ts @@ -2,8 +2,7 @@ import { PluginClient } from '@remixproject/plugin' import { createClient } from '@remixproject/plugin-webview' import EventManager from 'events' import pathModule from 'path' -// @ts-ignore -import { parse, compile, generate_witness, generate_r1cs, compiler_list } from '../../../pkg' +import { parse, compile, generate_witness, generate_r1cs, compiler_list } from 'circom_wasm' import { extractNameFromKey, extractParentFromKey } from '@remix-ui/helper' import { CompilationConfig } from '../types' @@ -13,6 +12,7 @@ export class CircomPluginClient extends PluginClient { version: "2.1.5", prime: "bn128" } + public lastCompiledCircuitPath: string = '' constructor() { super() @@ -34,7 +34,7 @@ export class CircomPluginClient extends PluginClient { } }) - this.internalEvents.emit('activated') + this.internalEvents.emit('circom_activated') } async parse(path: string, fileContent: string): Promise { @@ -107,6 +107,7 @@ export class CircomPluginClient extends PluginClient { } async compile(path: string, compilationConfig?: CompilationConfig): Promise { + this.internalEvents.emit('circuit_compiling') if (compilationConfig) { const { prime, version } = compilationConfig @@ -121,27 +122,31 @@ export class CircomPluginClient extends PluginClient { } buildFiles = await this.resolveDependencies(path, fileContent, buildFiles) - const compiledOutput = compile(path, buildFiles, { prime: this._compilationConfig.prime }) + const circuitApi = compile(path, buildFiles, { prime: this._compilationConfig.prime }) + const circuitProgram = circuitApi.program() - console.log('compiledOutput: ', compiledOutput) - console.log('compiledOutput.program: ', compiledOutput.program()) - console.log('compiledOutput.input_signals: ', compiledOutput.input_signals("Semaphore")) + if (circuitProgram.length < 1) { + const circuitErrors = circuitApi.report() - // if (compiledOutput.length < 1) { - // throw new Error("Compilation failed! See parsing errors.") - // } else { - // const fileName = extractNameFromKey(path) - // const writePath = extractParentFromKey(path) + "/.bin/" + fileName.replace('circom', 'wasm') - - // // @ts-ignore - // await this.call('fileManager', 'writeFile', writePath, new Uint8Array(compiledOutput), true) - // console.log('compilation successful!') - // } - // @ts-ignore - // const buffer = await this.call('fileManager', 'readFile', writePath, true) - // // @ts-ignore - // const dataRead = new Uint8Array(buffer) - // const witness = await generate_witness(dataRead, '{ "a": "5", "b": "77" }') + this.internalEvents.emit('circuit_errored', circuitErrors) + throw new Error(circuitErrors) + } else { + const fileName = extractNameFromKey(path) + + this.lastCompiledCircuitPath = extractParentFromKey(path) + "/.bin/" + fileName.replace('circom', 'wasm') + // @ts-ignore + await this.call('fileManager', 'writeFile', this.lastCompiledCircuitPath, circuitProgram, true) + const searchComponentName = fileContent.match(/component\s+main\s*(?:{[^{}]*})?\s*=\s*([A-Za-z_]\w*)\s*\(.*\)/) + + if (searchComponentName) { + const componentName = searchComponentName[1] + const signals = circuitApi.input_signals(componentName) + + this.internalEvents.emit('circuit_done', signals) + } else { + this.internalEvents.emit('circuit_done', []) + } + } // const witness = await generate_witness(compiledOutput, '{ "identityTrapdoor": "12656283236575022300303467601783819380815431272685589718060054649894766174337", "identityNullifier": "15178877681550417450385541477607788220584140707925739215609273992582659710290", "treePathIndices": "0", "treeSiblings": "1", "externalNullifier": "5df6e0e3480d6fbc32925076897ec6b9b935d75ae8f4d9f4858a426f8f6a4ab": "signalHash": "[85, 139, 239, 32, 221, 194, 165, 19, 20, 52, 104, 144, 41, 16, 40, 204, 171, 245, 198, 77, 94, 143, 30, 112, 105, 165, 33, 15, 62, 156, 18, 118]"}') // const ptau_final = "https://ipfs-cluster.ethdevops.io/ipfs/QmTiT4eiYz5KF7gQrDsgfCSTRv3wBPYJ4bRN1MmTRshpnW"; @@ -202,6 +207,7 @@ export class CircomPluginClient extends PluginClient { } async generateR1cs (path: string, compilationConfig?: CompilationConfig): Promise { + this.internalEvents.emit('circuit_generating') if (compilationConfig) { const { prime, version } = compilationConfig @@ -216,20 +222,35 @@ export class CircomPluginClient extends PluginClient { } buildFiles = await this.resolveDependencies(path, fileContent, buildFiles) - const r1cs = generate_r1cs(path, buildFiles, { prime: this._compilationConfig.prime }) + const r1csApi = generate_r1cs(path, buildFiles, { prime: this._compilationConfig.prime }) + const r1csProgram = r1csApi.program() + + if (r1csProgram.length < 1) { + const r1csErrors = r1csApi.report() - if (r1cs.length < 1) { - throw new Error("R1cs generation failed! See parsing errors.") + this.internalEvents.emit('circuit_errored', r1csErrors) + throw new Error(r1csErrors) } else { + this.internalEvents.emit('circuit_done') const fileName = extractNameFromKey(path) const writePath = extractParentFromKey(path) + "/.bin/" + fileName.replace('circom', 'r1cs') // @ts-ignore - await this.call('fileManager', 'writeFile', writePath, new Uint8Array(r1cs), true) - console.log('R1CS generation successful!') + await this.call('fileManager', 'writeFile', writePath, r1csProgram, true) } } + async computeWitness (input: string, wasmPath?: string): Promise { + this.internalEvents.emit('circuit_computing') + if (!wasmPath) wasmPath = this.lastCompiledCircuitPath + if (!wasmPath) throw new Error('No wasm file found') + + // @ts-ignore + const buffer: any = await this.call('fileManager', 'readFile', wasmPath, true) + const dataRead = new Uint8Array(buffer) + const witness = await generate_witness(dataRead, input) + } + async resolveDependencies(filePath: string, fileContent: string, output = {}, depPath: string = '', blackPath: string[] = []): Promise> { // extract all includes const includes = (fileContent.match(/include ['"].*['"]/g) || []).map((include) => include.replace(/include ['"]/g, '').replace(/['"]/g, '')) diff --git a/apps/circuit-compiler/src/app/types/index.ts b/apps/circuit-compiler/src/app/types/index.ts index 6165413668..277e28a67a 100644 --- a/apps/circuit-compiler/src/app/types/index.ts +++ b/apps/circuit-compiler/src/app/types/index.ts @@ -2,7 +2,7 @@ import { compiler_list } from 'circom_wasm' import {Dispatch} from 'react' import { CircomPluginClient } from '../services/circomPluginClient' -export type CompilerStatus = "compiling" | "generating" | "idle" | "errored" +export type CompilerStatus = "compiling" | "generating" | "computing" | "idle" | "errored" export interface ICircuitAppContext { appState: AppState dispatch: Dispatch, @@ -14,7 +14,8 @@ export interface ActionPayloadTypes { SET_FILE_PATH: string, SET_COMPILER_STATUS: CompilerStatus, SET_PRIME_VALUE: PrimeValue, - SET_AUTO_COMPILE: boolean + SET_AUTO_COMPILE: boolean, + SET_SIGNAL_INPUTS: string[] } export interface Action { type: T @@ -29,7 +30,8 @@ export interface AppState { filePath: string, status: CompilerStatus, primeValue: PrimeValue, - autoCompile: boolean + autoCompile: boolean, + signalInputs: string[] } export type CompilationConfig = { diff --git a/apps/circuit-compiler/src/profile.json b/apps/circuit-compiler/src/profile.json index e8395e2574..2bda2ccadb 100644 --- a/apps/circuit-compiler/src/profile.json +++ b/apps/circuit-compiler/src/profile.json @@ -9,7 +9,7 @@ "url": "", "description": "Enables circuit compilation and computing a witness for ZK proofs", "icon": "https://docs.circom.io/assets/images/favicon.png", - "location": "hiddenPanel", + "location": "sidePanel", "documentation": "", "repo": "https://github.com/ethereum/remix-project/tree/master/apps/circuit-compiler", "maintainedBy": "Remix", diff --git a/apps/remix-ide/src/app/tabs/locales/en/circuit.json b/apps/remix-ide/src/app/tabs/locales/en/circuit.json index 32dab62880..61184793f5 100644 --- a/apps/remix-ide/src/app/tabs/locales/en/circuit.json +++ b/apps/remix-ide/src/app/tabs/locales/en/circuit.json @@ -9,5 +9,7 @@ "circuit.compile": "Compile", "circuit.noFileSelected": "no file selected", "circuit.generateR1cs": "Generate R1CS", - "circuit.computeWitness": "Compute Witness" + "circuit.computeWitness": "Compute Witness", + "circuit.signalInput": "Signal Input", + "circuit.compute": "Compute" } diff --git a/apps/remix-ide/src/app/tabs/locales/es/circuit.json b/apps/remix-ide/src/app/tabs/locales/es/circuit.json index 32dab62880..61184793f5 100644 --- a/apps/remix-ide/src/app/tabs/locales/es/circuit.json +++ b/apps/remix-ide/src/app/tabs/locales/es/circuit.json @@ -9,5 +9,7 @@ "circuit.compile": "Compile", "circuit.noFileSelected": "no file selected", "circuit.generateR1cs": "Generate R1CS", - "circuit.computeWitness": "Compute Witness" + "circuit.computeWitness": "Compute Witness", + "circuit.signalInput": "Signal Input", + "circuit.compute": "Compute" } diff --git a/apps/remix-ide/src/app/tabs/locales/fr/circuit.json b/apps/remix-ide/src/app/tabs/locales/fr/circuit.json index 32dab62880..61184793f5 100644 --- a/apps/remix-ide/src/app/tabs/locales/fr/circuit.json +++ b/apps/remix-ide/src/app/tabs/locales/fr/circuit.json @@ -9,5 +9,7 @@ "circuit.compile": "Compile", "circuit.noFileSelected": "no file selected", "circuit.generateR1cs": "Generate R1CS", - "circuit.computeWitness": "Compute Witness" + "circuit.computeWitness": "Compute Witness", + "circuit.signalInput": "Signal Input", + "circuit.compute": "Compute" } diff --git a/apps/remix-ide/src/app/tabs/locales/zh/circuit.json b/apps/remix-ide/src/app/tabs/locales/zh/circuit.json index e4443c871e..73cd281a36 100644 --- a/apps/remix-ide/src/app/tabs/locales/zh/circuit.json +++ b/apps/remix-ide/src/app/tabs/locales/zh/circuit.json @@ -10,5 +10,7 @@ "circuit.compile": "编译", "circuit.noFileSelected": "未选中文件", "circuit.generateR1cs": "生成R1CS", - "circuit.computeWitness": "证人计算器" + "circuit.computeWitness": "证人计算器", + "circuit.signalInput": "输入信号", + "circuit.compute": "计算" } diff --git a/yarn.lock b/yarn.lock index 4aa35167a3..d6144d9470 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9729,7 +9729,7 @@ circom_runtime@0.1.22: "circom_wasm@https://github.com/ioedeveloper/circom_wasm.git": version "0.0.0-alpha.7" - resolved "https://github.com/ioedeveloper/circom_wasm.git#24d26dec739297a1bd3e61f74ca3b95b20f66a25" + resolved "https://github.com/ioedeveloper/circom_wasm.git#a8c53a02e97fa5e8533618a070d1b7fdb655fdc5" circular-json@^0.3.0: version "0.3.3"