Add compute witness button

pull/4144/head
ioedeveloper 1 year ago
parent 4a40340f41
commit 63620c85dd
  1. 11
      apps/circuit-compiler/src/app/actions/index.ts
  2. 25
      apps/circuit-compiler/src/app/app.tsx
  3. 8
      apps/circuit-compiler/src/app/components/container.tsx
  4. 43
      apps/circuit-compiler/src/app/components/witness.tsx
  5. 9
      apps/circuit-compiler/src/app/reducers/state.ts
  6. 75
      apps/circuit-compiler/src/app/services/circomPluginClient.ts
  7. 8
      apps/circuit-compiler/src/app/types/index.ts
  8. 2
      apps/circuit-compiler/src/profile.json
  9. 4
      apps/remix-ide/src/app/tabs/locales/en/circuit.json
  10. 4
      apps/remix-ide/src/app/tabs/locales/es/circuit.json
  11. 4
      apps/remix-ide/src/app/tabs/locales/fr/circuit.json
  12. 4
      apps/remix-ide/src/app/tabs/locales/zh/circuit.json
  13. 2
      yarn.lock

@ -4,29 +4,24 @@ import { Actions, AppState } from "../types"
export const compileCircuit = async (plugin: CircomPluginClient, appState: AppState, dispatch: React.Dispatch<Actions>) => { export const compileCircuit = async (plugin: CircomPluginClient, appState: AppState, dispatch: React.Dispatch<Actions>) => {
try { try {
if (appState.status !== "compiling") { if (appState.status !== "compiling") {
dispatch({ type: 'SET_COMPILER_STATUS', payload: 'compiling' })
await plugin.compile(appState.filePath, { version: appState.version, prime: appState.primeValue }) await plugin.compile(appState.filePath, { version: appState.version, prime: appState.primeValue })
dispatch({ type: 'SET_COMPILER_STATUS', payload: 'idle' })
} else { } else {
console.log('Exisiting compilation in progress') console.log('Exisiting circuit compilation in progress')
} }
} catch (e) { } catch (e) {
dispatch({ type: 'SET_COMPILER_STATUS', payload: 'errored' }) console.error(e)
console.error('Compiling failed: ', e) dispatch({ type: 'SET_SIGNAL_INPUTS', payload: [] })
} }
} }
export const generateR1cs = async (plugin: CircomPluginClient, appState: AppState, dispatch: React.Dispatch<Actions>) => { export const generateR1cs = async (plugin: CircomPluginClient, appState: AppState, dispatch: React.Dispatch<Actions>) => {
try { try {
if (appState.status !== "generating") { if (appState.status !== "generating") {
dispatch({ type: 'SET_COMPILER_STATUS', payload: 'generating' })
await plugin.generateR1cs(appState.filePath, { version: appState.version, prime: appState.primeValue }) await plugin.generateR1cs(appState.filePath, { version: appState.version, prime: appState.primeValue })
dispatch({ type: 'SET_COMPILER_STATUS', payload: 'idle' })
} else { } else {
console.log('Exisiting r1cs generation in progress') console.log('Exisiting r1cs generation in progress')
} }
} catch (e) { } catch (e) {
dispatch({ type: 'SET_COMPILER_STATUS', payload: 'errored' })
console.error('Generating R1CS failed: ', e) console.error('Generating R1CS failed: ', e)
} }
} }

@ -21,7 +21,7 @@ function App() {
useEffect(() => { useEffect(() => {
const plugin = new CircomPluginClient() const plugin = new CircomPluginClient()
plugin.internalEvents.on('activated', () => { plugin.internalEvents.on('circom_activated', () => {
// @ts-ignore // @ts-ignore
plugin.on('locale', 'localeChanged', (locale: any) => { plugin.on('locale', 'localeChanged', (locale: any) => {
setLocale(locale) setLocale(locale)
@ -40,6 +40,13 @@ function App() {
}) })
setPlugin(plugin) 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(() => { useEffect(() => {
@ -48,7 +55,6 @@ function App() {
if (appState.autoCompile) await compileCircuit(plugin, appState, dispatch) if (appState.autoCompile) await compileCircuit(plugin, appState, dispatch)
})() })()
setIsContentChanged(false) setIsContentChanged(false)
setSignalInput()
} }
}, [appState.autoCompile, isContentChanged]) }, [appState.autoCompile, isContentChanged])
@ -58,6 +64,14 @@ function App() {
} }
}, [plugin]) }, [plugin])
useEffect(() => {
if (appState.filePath) {
(async () => {
if (appState.autoCompile) await compileCircuit(plugin, appState, dispatch)
})()
}
}, [appState.filePath])
const setCurrentLocale = async () => { const setCurrentLocale = async () => {
// @ts-ignore // @ts-ignore
const currentLocale = await plugin.call('locale', 'currentLocale') const currentLocale = await plugin.call('locale', 'currentLocale')
@ -65,13 +79,6 @@ function App() {
setLocale(currentLocale) 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 = { const value = {
appState, appState,
dispatch, dispatch,

@ -35,9 +35,11 @@ export function Container () {
<Configurations /> <Configurations />
</ConfigToggler> </ConfigToggler>
<CircuitActions /> <CircuitActions />
<WitnessToggler> <RenderIf condition={circuitApp.appState.signalInputs.length > 0}>
<WitnessSection /> <WitnessToggler>
</WitnessToggler> <WitnessSection signalInputs={circuitApp.appState.signalInputs} status={circuitApp.appState.status} />
</WitnessToggler>
</RenderIf>
</div> </div>
</div> </div>
</article> </article>

@ -1,25 +1,34 @@
import { CustomTooltip } from "@remix-ui/helper"; import { RenderIf, RenderIfNot } from "@remix-ui/helper";
import { FormattedMessage } from "react-intl"; import { FormattedMessage } from "react-intl";
import { CompilerStatus } from "../types";
export function WitnessSection () { export function WitnessSection ({ signalInputs, status }: {signalInputs: string[], status: CompilerStatus}) {
return ( return (
<div className="pb-2 border-bottom flex-column"> <div className="pb-2 border-bottom flex-column">
<div className="flex-column d-flex"> <div className="flex-column d-flex">
<div className="mb-2 ml-0"> <RenderIf condition={signalInputs.length > 0}>
<label className="circuit_inner_label form-check-label" htmlFor="circuitPrimeSelector"> <>
<FormattedMessage id="circuit.prime" /> {
</label> signalInputs.map((input, index) => (
<CustomTooltip <div className="mb-2 ml-0" key={index}>
placement={"auto"} <label className="circuit_inner_label form-check-label" htmlFor="circuitPrimeSelector">
tooltipId="circuitPrimeLabelTooltip" <FormattedMessage id="circuit.signalInput" /> { input }
tooltipClasses="text-nowrap" </label>
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>} <input className="form-control m-0 txinput" placeholder={input} />
> </div>
<div> ))
<input className="form-control m-0 txinput" placeholder="Signal A" /> }
</div> <button className="btn btn-sm btn-secondary" >
</CustomTooltip> <RenderIf condition={status === 'computing'}>
</div> <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>
</div> </div>
) )

@ -7,7 +7,8 @@ export const appInitialState: AppState = {
filePath: "", filePath: "",
status: "idle", status: "idle",
primeValue: "bn128", primeValue: "bn128",
autoCompile: false autoCompile: false,
signalInputs: []
} }
export const appReducer = (state = appInitialState, action: Actions): AppState => { export const appReducer = (state = appInitialState, action: Actions): AppState => {
@ -43,6 +44,12 @@ export const appReducer = (state = appInitialState, action: Actions): AppState =
autoCompile: action.payload autoCompile: action.payload
} }
case 'SET_SIGNAL_INPUTS':
return {
...state,
signalInputs: action.payload
}
default: default:
throw new Error() throw new Error()
} }

@ -2,8 +2,7 @@ import { PluginClient } from '@remixproject/plugin'
import { createClient } from '@remixproject/plugin-webview' import { createClient } from '@remixproject/plugin-webview'
import EventManager from 'events' import EventManager from 'events'
import pathModule from 'path' import pathModule from 'path'
// @ts-ignore import { parse, compile, generate_witness, generate_r1cs, compiler_list } from 'circom_wasm'
import { parse, compile, generate_witness, generate_r1cs, compiler_list } from '../../../pkg'
import { extractNameFromKey, extractParentFromKey } from '@remix-ui/helper' import { extractNameFromKey, extractParentFromKey } from '@remix-ui/helper'
import { CompilationConfig } from '../types' import { CompilationConfig } from '../types'
@ -13,6 +12,7 @@ export class CircomPluginClient extends PluginClient {
version: "2.1.5", version: "2.1.5",
prime: "bn128" prime: "bn128"
} }
public lastCompiledCircuitPath: string = ''
constructor() { constructor() {
super() 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<void> { async parse(path: string, fileContent: string): Promise<void> {
@ -107,6 +107,7 @@ export class CircomPluginClient extends PluginClient {
} }
async compile(path: string, compilationConfig?: CompilationConfig): Promise<void> { async compile(path: string, compilationConfig?: CompilationConfig): Promise<void> {
this.internalEvents.emit('circuit_compiling')
if (compilationConfig) { if (compilationConfig) {
const { prime, version } = compilationConfig const { prime, version } = compilationConfig
@ -121,27 +122,31 @@ export class CircomPluginClient extends PluginClient {
} }
buildFiles = await this.resolveDependencies(path, fileContent, buildFiles) 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) if (circuitProgram.length < 1) {
console.log('compiledOutput.program: ', compiledOutput.program()) const circuitErrors = circuitApi.report()
console.log('compiledOutput.input_signals: ', compiledOutput.input_signals("Semaphore"))
// if (compiledOutput.length < 1) { this.internalEvents.emit('circuit_errored', circuitErrors)
// throw new Error("Compilation failed! See parsing errors.") throw new Error(circuitErrors)
// } else { } else {
// const fileName = extractNameFromKey(path) const fileName = extractNameFromKey(path)
// const writePath = extractParentFromKey(path) + "/.bin/" + fileName.replace('circom', 'wasm')
this.lastCompiledCircuitPath = extractParentFromKey(path) + "/.bin/" + fileName.replace('circom', 'wasm')
// // @ts-ignore // @ts-ignore
// await this.call('fileManager', 'writeFile', writePath, new Uint8Array(compiledOutput), true) await this.call('fileManager', 'writeFile', this.lastCompiledCircuitPath, circuitProgram, true)
// console.log('compilation successful!') const searchComponentName = fileContent.match(/component\s+main\s*(?:{[^{}]*})?\s*=\s*([A-Za-z_]\w*)\s*\(.*\)/)
// }
// @ts-ignore if (searchComponentName) {
// const buffer = await this.call('fileManager', 'readFile', writePath, true) const componentName = searchComponentName[1]
// // @ts-ignore const signals = circuitApi.input_signals(componentName)
// const dataRead = new Uint8Array(buffer)
// const witness = await generate_witness(dataRead, '{ "a": "5", "b": "77" }') 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 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"; // 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<void> { async generateR1cs (path: string, compilationConfig?: CompilationConfig): Promise<void> {
this.internalEvents.emit('circuit_generating')
if (compilationConfig) { if (compilationConfig) {
const { prime, version } = compilationConfig const { prime, version } = compilationConfig
@ -216,20 +222,35 @@ export class CircomPluginClient extends PluginClient {
} }
buildFiles = await this.resolveDependencies(path, fileContent, buildFiles) 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) { this.internalEvents.emit('circuit_errored', r1csErrors)
throw new Error("R1cs generation failed! See parsing errors.") throw new Error(r1csErrors)
} else { } else {
this.internalEvents.emit('circuit_done')
const fileName = extractNameFromKey(path) const fileName = extractNameFromKey(path)
const writePath = extractParentFromKey(path) + "/.bin/" + fileName.replace('circom', 'r1cs') const writePath = extractParentFromKey(path) + "/.bin/" + fileName.replace('circom', 'r1cs')
// @ts-ignore // @ts-ignore
await this.call('fileManager', 'writeFile', writePath, new Uint8Array(r1cs), true) await this.call('fileManager', 'writeFile', writePath, r1csProgram, true)
console.log('R1CS generation successful!')
} }
} }
async computeWitness (input: string, wasmPath?: string): Promise<void> {
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<Record<string, string>> { async resolveDependencies(filePath: string, fileContent: string, output = {}, depPath: string = '', blackPath: string[] = []): Promise<Record<string, string>> {
// extract all includes // extract all includes
const includes = (fileContent.match(/include ['"].*['"]/g) || []).map((include) => include.replace(/include ['"]/g, '').replace(/['"]/g, '')) const includes = (fileContent.match(/include ['"].*['"]/g) || []).map((include) => include.replace(/include ['"]/g, '').replace(/['"]/g, ''))

@ -2,7 +2,7 @@ import { compiler_list } from 'circom_wasm'
import {Dispatch} from 'react' import {Dispatch} from 'react'
import { CircomPluginClient } from '../services/circomPluginClient' import { CircomPluginClient } from '../services/circomPluginClient'
export type CompilerStatus = "compiling" | "generating" | "idle" | "errored" export type CompilerStatus = "compiling" | "generating" | "computing" | "idle" | "errored"
export interface ICircuitAppContext { export interface ICircuitAppContext {
appState: AppState appState: AppState
dispatch: Dispatch<Actions>, dispatch: Dispatch<Actions>,
@ -14,7 +14,8 @@ export interface ActionPayloadTypes {
SET_FILE_PATH: string, SET_FILE_PATH: string,
SET_COMPILER_STATUS: CompilerStatus, SET_COMPILER_STATUS: CompilerStatus,
SET_PRIME_VALUE: PrimeValue, SET_PRIME_VALUE: PrimeValue,
SET_AUTO_COMPILE: boolean SET_AUTO_COMPILE: boolean,
SET_SIGNAL_INPUTS: string[]
} }
export interface Action<T extends keyof ActionPayloadTypes> { export interface Action<T extends keyof ActionPayloadTypes> {
type: T type: T
@ -29,7 +30,8 @@ export interface AppState {
filePath: string, filePath: string,
status: CompilerStatus, status: CompilerStatus,
primeValue: PrimeValue, primeValue: PrimeValue,
autoCompile: boolean autoCompile: boolean,
signalInputs: string[]
} }
export type CompilationConfig = { export type CompilationConfig = {

@ -9,7 +9,7 @@
"url": "", "url": "",
"description": "Enables circuit compilation and computing a witness for ZK proofs", "description": "Enables circuit compilation and computing a witness for ZK proofs",
"icon": "https://docs.circom.io/assets/images/favicon.png", "icon": "https://docs.circom.io/assets/images/favicon.png",
"location": "hiddenPanel", "location": "sidePanel",
"documentation": "", "documentation": "",
"repo": "https://github.com/ethereum/remix-project/tree/master/apps/circuit-compiler", "repo": "https://github.com/ethereum/remix-project/tree/master/apps/circuit-compiler",
"maintainedBy": "Remix", "maintainedBy": "Remix",

@ -9,5 +9,7 @@
"circuit.compile": "Compile", "circuit.compile": "Compile",
"circuit.noFileSelected": "no file selected", "circuit.noFileSelected": "no file selected",
"circuit.generateR1cs": "Generate R1CS", "circuit.generateR1cs": "Generate R1CS",
"circuit.computeWitness": "Compute Witness" "circuit.computeWitness": "Compute Witness",
"circuit.signalInput": "Signal Input",
"circuit.compute": "Compute"
} }

@ -9,5 +9,7 @@
"circuit.compile": "Compile", "circuit.compile": "Compile",
"circuit.noFileSelected": "no file selected", "circuit.noFileSelected": "no file selected",
"circuit.generateR1cs": "Generate R1CS", "circuit.generateR1cs": "Generate R1CS",
"circuit.computeWitness": "Compute Witness" "circuit.computeWitness": "Compute Witness",
"circuit.signalInput": "Signal Input",
"circuit.compute": "Compute"
} }

@ -9,5 +9,7 @@
"circuit.compile": "Compile", "circuit.compile": "Compile",
"circuit.noFileSelected": "no file selected", "circuit.noFileSelected": "no file selected",
"circuit.generateR1cs": "Generate R1CS", "circuit.generateR1cs": "Generate R1CS",
"circuit.computeWitness": "Compute Witness" "circuit.computeWitness": "Compute Witness",
"circuit.signalInput": "Signal Input",
"circuit.compute": "Compute"
} }

@ -10,5 +10,7 @@
"circuit.compile": "编译", "circuit.compile": "编译",
"circuit.noFileSelected": "未选中文件", "circuit.noFileSelected": "未选中文件",
"circuit.generateR1cs": "生成R1CS", "circuit.generateR1cs": "生成R1CS",
"circuit.computeWitness": "证人计算器" "circuit.computeWitness": "证人计算器",
"circuit.signalInput": "输入信号",
"circuit.compute": "计算"
} }

@ -9729,7 +9729,7 @@ circom_runtime@0.1.22:
"circom_wasm@https://github.com/ioedeveloper/circom_wasm.git": "circom_wasm@https://github.com/ioedeveloper/circom_wasm.git":
version "0.0.0-alpha.7" 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: circular-json@^0.3.0:
version "0.3.3" version "0.3.3"

Loading…
Cancel
Save