-
Notifications
You must be signed in to change notification settings - Fork 1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #4144 from ethereum/circuit-compiler
Circuit compiler UI
- Loading branch information
Showing
41 changed files
with
1,524 additions
and
158 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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() { | ||
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(() => { | ||
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 ( | ||
<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> | ||
) | ||
} | ||
|
||
export default App | ||
export default App |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { CompileBtn } from "./compileBtn"; | ||
import { R1CSBtn } from "./r1csBtn"; | ||
|
||
export function CircuitActions () { | ||
return ( | ||
<div className="pb-3"> | ||
<CompileBtn /> | ||
<R1CSBtn /> | ||
</div> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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> | ||
) | ||
} |
36 changes: 36 additions & 0 deletions
36
apps/circuit-compiler/src/app/components/configToggler.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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> | ||
) | ||
} |
38 changes: 38 additions & 0 deletions
38
apps/circuit-compiler/src/app/components/configurations.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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> | ||
) | ||
} |
Oops, something went wrong.