Complete download compiler UI

pull/5228/head
ioedeveloper 5 months ago committed by bunsenstraat
parent 32fb30e60a
commit 092c7b9a88
  1. 5
      .gitignore
  2. 14
      apps/circuit-compiler/src/app/app.tsx
  3. 3
      apps/circuit-compiler/src/app/components/container.tsx
  4. 114
      apps/circuit-compiler/src/app/components/versions.tsx
  5. 13
      apps/circuit-compiler/src/app/reducers/state.ts
  6. 39
      apps/circuit-compiler/src/app/services/circomPluginClient.ts
  7. 3
      apps/circuit-compiler/src/app/types/index.ts
  8. 15
      apps/circuit-compiler/src/css/app.css
  9. 2
      apps/remixdesktop/log_input_signals.txt
  10. 24
      apps/remixdesktop/src/plugins/circomElectronBasePlugin.ts
  11. 52
      apps/remixdesktop/src/tools/circom.ts

5
.gitignore vendored

@ -69,8 +69,5 @@ apps/remixdesktop/build*
apps/remixdesktop/reports
apps/remixdesktop/logs/
apps/remixdesktop/bin/
apps/remixdesktop/circom-windows-amd64.exe
apps/remixdesktop/circom-macos-amd64
apps/remixdesktop/circom-linux-amd64
apps/remixdesktop/log_input_signals.txt
apps/remixdesktop/circom-download
logs

@ -7,6 +7,7 @@ import {CircuitAppContext} from './contexts'
import {appInitialState, appReducer} from './reducers/state'
import {CircomPluginClient} from './services/circomPluginClient'
import { compileCircuit } from './actions'
import { version } from 'os'
const plugin = new CircomPluginClient()
@ -21,6 +22,11 @@ function App() {
useEffect(() => {
plugin.internalEvents.on('circom_activated', () => {
(async () => {
const downloadList = await plugin.getCompilerDownloadList()
dispatch({ type: 'SET_VERSION_DOWNLOAD_LIST', payload: downloadList })
})();
// @ts-ignore
plugin.on('locale', 'localeChanged', (locale: any) => {
setLocale(locale)
@ -73,6 +79,14 @@ function App() {
dispatch({ type: 'SET_COMPILER_STATUS', payload: 'warning' })
dispatch({ type: 'SET_COMPILER_FEEDBACK', payload: report })
})
plugin.internalEvents.on('download_success', (version) => {
dispatch({ type: 'SET_COMPILER_VERSION', payload: version })
dispatch({ type: 'REMOVE_VERSION_FROM_DOWNLOAD_LIST', payload: version })
dispatch({ type: 'SET_COMPILER_FEEDBACK', payload: null })
})
plugin.internalEvents.on('download_failed', (version) => {
dispatch({ type: 'SET_COMPILER_FEEDBACK', payload: 'Download failed! Please check your internet connection and try again.' })
})
}, [])
useEffect(() => {

@ -31,7 +31,6 @@ export function Container () {
const handleVersionSelect = (version: string) => {
circuitApp.plugin.compilerVersion = version
circuitApp.dispatch({ type: 'SET_COMPILER_VERSION', payload: version })
}
const handleOpenErrorLocation = async (location: string, startRange: string) => {
@ -118,7 +117,7 @@ export function Container () {
>
<span className="far fa-file-certificate border-0 p-0 ml-2" onClick={() => showCompilerLicense()}></span>
</CustomTooltip>
<VersionList setVersion={handleVersionSelect} versionList={circuitApp.appState.versionList} currentVersion={circuitApp.appState.version} />
<VersionList setVersion={handleVersionSelect} versionList={circuitApp.appState.versionList} currentVersion={circuitApp.appState.version} downloadList={circuitApp.appState.versionDownloadList} />
<CompileOptions setCircuitAutoCompile={handleCircuitAutoCompile} setCircuitHideWarnings={handleCircuitHideWarnings} autoCompile={circuitApp.appState.autoCompile} hideWarnings={circuitApp.appState.hideWarnings} />
<Toggler title='circuit.advancedConfigurations' dataId=''>
<Configurations setPrimeValue={handlePrimeChange} primeValue={circuitApp.appState.primeValue} versionValue={circuitApp.appState.version} />

@ -1,8 +1,9 @@
import { AppState } from "../types";
import { Dropdown } from "react-bootstrap";
import React, { Ref } from "react";
import { AppState } from "../types"
import { Dropdown } from "react-bootstrap"
import React, { Ref } from "react"
import isElectron from 'is-electron'
export function VersionList ({ currentVersion, versionList, setVersion }: { versionList: AppState['versionList'], currentVersion: string, setVersion: (version: string) => void }) {
export function VersionList ({ currentVersion, versionList, downloadList, setVersion }: { versionList: AppState['versionList'], currentVersion: string, setVersion: (version: string) => void , downloadList: string[]}) {
const versionListKeys = Object.keys(versionList)
return (
<Dropdown>
@ -14,14 +15,18 @@ export function VersionList ({ currentVersion, versionList, setVersion }: { vers
<Dropdown.Menu as={CircomVersionMenu} className="w-100 custom-dropdown-items overflow-hidden">
{
versionListKeys.map((version, index) => (
versionListKeys.reverse().map((version, index) => (
<Dropdown.Item key={index} onClick={() => {
setVersion(version)
}}>
<div className='d-flex w-100 justify-content-between'>
<div className="text-truncate">
{ versionList[version].name }
<div>
<span className={`fas fa-check text-success mr-2 ${currentVersion === version ? 'visible' : 'invisible'}`}></span>
<span>
{ versionList[version].name }
</span>
</div>
{ isElectron() ? downloadList.includes(version) ? <div className='far fa-arrow-circle-down'></div> : <div className='fas fa-arrow-circle-down text-success ml-auto'></div> : null }
</div>
</Dropdown.Item>
))
@ -89,98 +94,3 @@ const CircomVersionMenu = React.forwardRef(
)
}
)
// export const CompilerDropdown = (props: compilerDropdownProps) => {
// const online = useContext(onLineContext)
// const platform = useContext(platformContext)
// const { customVersions, selectedVersion, defaultVersion, allversions, handleLoadVersion, _shouldBeAdded, onlyDownloaded } = props
// return (
// <Dropdown id="versionSelector" data-id="versionSelector">
// <Dropdown.Toggle as={CompilerMenuToggle} id="dropdown-custom-components" className="btn btn-light btn-block w-100 d-inline-block border border-dark form-control" icon={null}>
// <div style={{ flexGrow: 1, overflow: 'hidden', display:'flex', justifyContent:'left' }}>
// <div className="text-truncate">
// {customVersions.map((url, i) => {
// if (selectedVersion === url) return (<span data-id="selectedVersion" key={i}>custom</span>)
// })}
// {allversions.map((build, i) => {
// if ((selectedVersion || defaultVersion) === build.path) {
// return (<span data-id="selectedVersion" key={i}>{build.longVersion}</span>)
// }
// })}
// </div>
// </div>
// </Dropdown.Toggle>
// <Dropdown.Menu as={CompilerMenu} className="w-100 custom-dropdown-items overflow-hidden" data-id="custom-dropdown-items">
// {allversions.length <= 0 && (
// <Dropdown.Item
// key={`default`}
// data-id='builtin'
// onClick={() => {}}
// >
// <div className='d-flex w-100 justify-content-between'>
// {selectedVersion === defaultVersion ? <span className='fas fa-check text-success mr-2'></span> : null}
// <div style={{ flexGrow: 1, overflow: 'hidden' }}>
// <div className="text-truncate">
// {defaultVersion}
// </div>
// </div>
// </div>
// </Dropdown.Item>
// )}
// {allversions.length <= 0 && (
// <Dropdown.Item
// key={`builtin`}
// data-id='builtin'
// onClick={() => {}}
// >
// <div className='d-flex w-100 justify-content-between'>
// {selectedVersion === "builtin" ? <span className='fas fa-check text-success mr-2'></span> : null}
// <div style={{ flexGrow: 1, overflow: 'hidden' }}>
// <div className="text-truncate">
// builtin
// </div>
// </div>
// </div>
// </Dropdown.Item>
// )}
// {customVersions.map((url, i) => (
// <Dropdown.Item
// key={`custom-${i}`}
// data-id={`dropdown-item-${url}`}
// onClick={() => handleLoadVersion(url)}
// >
// <div className='d-flex w-100 justify-content-between'>
// {selectedVersion === url ? <span className='fas fa-check text-success mr-2'></span> : null}
// <div style={{ flexGrow: 1, overflow: 'hidden' }}>
// <div className="text-truncate">
// custom: {url}
// </div>
// </div>
// </div>
// </Dropdown.Item>
// ))}
// {allversions.map((build, i) => {
// if (onlyDownloaded && !build.isDownloaded) return null
// return _shouldBeAdded(build.longVersion) ? (
// <Dropdown.Item
// key={`soljson-${i}`}
// data-id={`dropdown-item-${build.path}`}
// onClick={() => handleLoadVersion(build.path)}
// >
// <div className='d-flex w-100 justify-content-between'>
// {selectedVersion === build.path ? <span className='fas fa-check text-success mr-2'></span> : null}
// <div style={{ flexGrow: 1, overflow: 'hidden' }}>
// <div className="text-truncate">
// {build.longVersion}
// </div>
// </div>
// {platform == appPlatformTypes.desktop ? (build.isDownloaded ? <div className='fas fa-arrow-circle-down text-success ml-auto'></div> : <div className='far fa-arrow-circle-down'></div>) : null}
// </div>
// </Dropdown.Item>
// ) : null
// })}
// </Dropdown.Menu>
// </Dropdown>
// );

@ -5,6 +5,7 @@ import { compiler_list } from 'circom_wasm'
export const appInitialState: AppState = {
version: compiler_list.latest,
versionList: compiler_list.wasm_builds,
versionDownloadList: [],
filePath: "",
filePathToId: {},
status: "idle",
@ -157,6 +158,18 @@ export const appReducer = (state = appInitialState, action: Actions): AppState =
zKey: action.payload
}
case 'SET_VERSION_DOWNLOAD_LIST':
return {
...state,
versionDownloadList: action.payload
}
case 'REMOVE_VERSION_FROM_DOWNLOAD_LIST':
return {
...state,
versionDownloadList: state.versionDownloadList.filter(version => version !== action.payload)
}
default:
throw new Error()
}

@ -46,11 +46,20 @@ export class CircomPluginClient extends PluginClient {
set compilerVersion (version: string) {
if (!compiler_list.versions.includes(version)) throw new Error("Unsupported compiler version")
this._compilationConfig.version = version
if (version === '2.1.5') this.compiler = compilerV215
else if (version === '2.1.6') this.compiler = compilerV216
else if (version === '2.1.7') this.compiler = compilerV217
else if (version === '2.1.8') this.compiler = compilerV218
else this.compiler = null
if (isElectron()) {
// @ts-ignore
this.call('circom', 'install', `v${version}`).then(() => {
this.internalEvents.emit('download_success', version)
}).catch((e) => {
this.internalEvents.emit('download_failed')
})
} else {
if (version === '2.1.5') this.compiler = compilerV215
else if (version === '2.1.6') this.compiler = compilerV216
else if (version === '2.1.7') this.compiler = compilerV217
else if (version === '2.1.8') this.compiler = compilerV218
else this.compiler = null
}
}
set compilerPrime (prime: PrimeValue) {
@ -141,8 +150,9 @@ export class CircomPluginClient extends PluginClient {
this.emit('statusChanged', { key: 'loading', title: 'Compiling...', type: 'info' })
// @ts-ignore
this.call('terminal', 'log', { type: 'log', value: 'Compiling ' + path })
const { version, prime } = this._compilationConfig
// @ts-ignore
const { stdout, stderr } = await this.call('circom', 'run', path, { prime: this._compilationConfig.prime, wasm: "", inputs: "" })
const { stdout, stderr } = await this.call('circom', 'run', path, `v${version}`, { prime: prime, wasm: "", inputs: "" })
const fileName = extractNameFromKey(path)
this.lastCompiledCircuitPath = extractParentFromKey(path) + "/.bin/" + fileName.replace('.circom', '_js') + "/" + fileName.replace('circom', 'wasm')
@ -223,9 +233,10 @@ export class CircomPluginClient extends PluginClient {
this.emit('statusChanged', { key: 'loading', title: 'Generating...', type: 'info' })
// @ts-ignore
this.call('terminal', 'log', { type: 'log', value: 'Generating R1CS for ' + path })
const { version, prime } = this._compilationConfig
// @ts-ignore
await this.call('circom', 'run', path, {
prime: this._compilationConfig.prime,
await this.call('circom', 'run', path, `v${version}`, {
prime: prime,
r1cs: ""
})
this.internalEvents.emit('circuit_generating_r1cs_done')
@ -463,4 +474,16 @@ export class CircomPluginClient extends PluginClient {
this.emit('statusChanged', { key: report.length, title: `You have ${report.length} problem${report.length === 1 ? '' : 's'}`, type: 'warning' })
}
}
async getCompilerDownloadList () {
if (!isElectron()) return []
else {
return await Promise.all(compiler_list.versions.map(async (version) => {
// @ts-ignore
const exists = await this.call('circom', 'isVersionInstalled', `v${version}`)
if (!exists) return version
}))
}
}
}

@ -23,6 +23,8 @@ export interface ICircuitAppContext {
export interface ActionPayloadTypes {
SET_COMPILER_VERSION: string,
SET_VERSION_DOWNLOAD_LIST: string[],
REMOVE_VERSION_FROM_DOWNLOAD_LIST: string,
SET_FILE_PATH: string,
SET_COMPILER_STATUS: CompilerStatus,
SET_PRIME_VALUE: PrimeValue,
@ -54,6 +56,7 @@ export type Actions = {[A in keyof ActionPayloadTypes]: Action<A>}[keyof ActionP
export interface AppState {
version: string,
versionList: typeof compiler_list.wasm_builds,
versionDownloadList: string[],
filePath: string,
filePathToId: Record<string, string>,
status: CompilerStatus,

@ -84,3 +84,18 @@ body {
bottom: 0;
right: 0;
}
.custom-dropdown-items {
padding: 0.25rem 0.25rem;
border-radius: .25rem;
background: var(--custom-select);
}
.custom-dropdown-items a {
border-radius: .25rem;
text-transform: none;
text-decoration: none;
font-weight: normal;
font-size: 0.875rem;
padding: 0.25rem 0.25rem;
width: auto;
color: var(--text);
}

@ -1,6 +1,6 @@
import { ElectronBasePlugin, ElectronBasePluginClient } from "@remixproject/plugin-electron"
import { Profile } from "@remixproject/plugin-utils"
import { getInstallationPath, circomCli, extractParentFromKey } from "../tools/circom"
import { getInstallationPath, circomCli, extractParentFromKey, getInstallationUrl, getLogInputSignalsPath } from "../tools/circom"
import path from "path"
import { existsSync, readFileSync } from "fs"
@ -23,7 +23,7 @@ const clientProfile: Profile = {
name: 'circom',
displayName: 'circom',
description: 'Circom Language Compiler',
methods: ['install', 'run', 'getInputs']
methods: ['install', 'run', 'getInputs', 'isVersionInstalled']
}
class CircomElectronPluginClient extends ElectronBasePluginClient {
@ -34,16 +34,18 @@ class CircomElectronPluginClient extends ElectronBasePluginClient {
this.onload()
}
async install() {
this.isCircomInstalled = await circomCli.isCircomInstalled()
async install(version = 'latest') {
this.isCircomInstalled = await circomCli.isCircomInstalled(version)
if (!this.isCircomInstalled) {
await circomCli.installCircom()
this.call('terminal' as any, 'logHtml', 'Downloading circom compiler from ' + getInstallationUrl(version))
await circomCli.installCircom(version)
this.isCircomInstalled = true
this.call('terminal' as any, 'logHtml', `Circom compiler (${version}) downloaded from ${getInstallationUrl(version)} to ${getInstallationPath(version)}`)
}
}
async run(filePath: string, options: Record<string, string>) {
if (!this.isCircomInstalled) await this.install()
async run(filePath: string, version = 'latest', options: Record<string, string>) {
if (!this.isCircomInstalled) await this.install(version)
// @ts-ignore
const wd = await this.call('fs', 'getWorkingDir')
// @ts-ignore
@ -54,11 +56,11 @@ class CircomElectronPluginClient extends ElectronBasePluginClient {
const depPath = path.join(wd, '.deps/https/raw.githubusercontent.com/iden3/')
const outputDir = extractParentFromKey(filePath) + '/.bin'
return await circomCli.run(`${filePath} -l ${depPath} -o ${outputDir}`, options)
return await circomCli.run(`${filePath} -l ${depPath} -o ${outputDir}`, version, options)
}
getInputs() {
const inputsFile = extractParentFromKey(getInstallationPath()) + '/log_input_signals.txt'
const inputsFile = getLogInputSignalsPath()
const inputsFileExists = existsSync(inputsFile)
const signals: string[] = []
@ -73,4 +75,8 @@ class CircomElectronPluginClient extends ElectronBasePluginClient {
return signals
}
}
async isVersionInstalled(version: string) {
return await circomCli.isCircomInstalled(version)
}
}

@ -35,56 +35,70 @@ async function downloadFile(url: string, dest: string) {
})
}
export function getInstallationPath(version = 'latest') {
export function getInstallationPath(version) {
switch (process.platform) {
case 'win32':
return path.join(app.getPath('temp'), version, 'circom-windows-amd64.exe')
return path.join(app.getPath('temp'), 'circom-download', version, 'circom-windows-amd64.exe')
case 'darwin':
return path.join(app.getAppPath(), version, 'circom-macos-amd64')
return path.join(app.getAppPath(), 'circom-download', version, 'circom-macos-amd64')
case 'linux':
return path.join(app.getAppPath(), version, 'circom-linux-amd64')
return path.join(app.getAppPath(), 'circom-download', version, 'circom-linux-amd64')
}
}
function getInstallationUrl(version = 'latest') {
export function getInstallationUrl(version) {
switch (process.platform) {
case 'win32':
return `https://github.com/iden3/circom/releases/${version}/download/circom-windows-amd64.exe`
return version === 'latest' ? 'https://github.com/iden3/circom/releases/latest/download/circom-windows-amd64.exe' : `https://github.com/iden3/circom/releases/download/${version}/circom-windows-amd64.exe`
case 'darwin':
return `https://github.com/iden3/circom/releases/${version}/download/circom-macos-amd64`
return version === 'latest' ? 'https://github.com/iden3/circom/releases/latest/download/circom-macos-amd64' : `https://github.com/iden3/circom/releases/download/${version}/circom-macos-amd64`
case 'linux':
return `https://github.com/iden3/circom/releases/${version}/download/circom-linux-amd64`
return version === 'latest' ? 'https://github.com/iden3/circom/releases/latest/download/circom-linux-amd64' : `https://github.com/iden3/circom/releases/download/${version}/circom-linux-amd64`
}
}
export function getLogInputSignalsPath() {
switch (process.platform) {
case 'win32':
return path.join(app.getPath('temp'), 'log_input_signals.txt')
case 'darwin':
return path.join(app.getAppPath(), 'log_input_signals.txt')
case 'linux':
return path.join(app.getAppPath(), 'log_input_signals.txt')
}
}
export const circomCli = {
async installCircom () {
async installCircom (version) {
const installationPath = getInstallationPath(version)
const installationUrl = getInstallationUrl(version)
if (!existsSync(path.dirname(installationPath))) fs.mkdirSync(path.dirname(installationPath), { recursive: true })
try {
const installationPath = getInstallationPath()
console.log('downloading circom to ', installationPath)
const installationUrl = getInstallationUrl()
await downloadFile(installationUrl, installationPath)
console.log('downloading done')
} catch (e) {
console.error(e)
fs.rmSync(installationPath)
throw new Error('Download failed!')
}
},
async isCircomInstalled () {
async isCircomInstalled (version) {
try {
const installationPath = getInstallationPath()
const installationPath = getInstallationPath(version)
return existsSync(installationPath)
} catch (e) {
return false
}
},
async run (filePath: string, options?: Record<string, string>) {
const installationPath = getInstallationPath()
async run (filePath: string, version: string, options?: Record<string, string>) {
const installationPath = getInstallationPath(version)
const cmd = `${installationPath} ${filePath} ${Object.keys(options || {}).map((key) => options[key] ? `--${key} ${options[key]}` : `--${key}`).join(' ')}`
return await execAsync(cmd)
@ -98,4 +112,4 @@ export const extractParentFromKey = (key: string):string => {
keyPath.pop()
return keyPath.join('/')
}
}

Loading…
Cancel
Save