diff --git a/.gitignore b/.gitignore index 006ec003be..a562abbcfa 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/apps/circuit-compiler/src/app/app.tsx b/apps/circuit-compiler/src/app/app.tsx index 1e3e6a4a9c..aae738303d 100644 --- a/apps/circuit-compiler/src/app/app.tsx +++ b/apps/circuit-compiler/src/app/app.tsx @@ -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(() => { diff --git a/apps/circuit-compiler/src/app/components/container.tsx b/apps/circuit-compiler/src/app/components/container.tsx index 765f33abe3..4acd5bfdbf 100644 --- a/apps/circuit-compiler/src/app/components/container.tsx +++ b/apps/circuit-compiler/src/app/components/container.tsx @@ -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 () { > showCompilerLicense()}> - + diff --git a/apps/circuit-compiler/src/app/components/versions.tsx b/apps/circuit-compiler/src/app/components/versions.tsx index f9f9e55884..5ced4e6295 100644 --- a/apps/circuit-compiler/src/app/components/versions.tsx +++ b/apps/circuit-compiler/src/app/components/versions.tsx @@ -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 ( @@ -14,14 +15,18 @@ export function VersionList ({ currentVersion, versionList, setVersion }: { vers { - versionListKeys.map((version, index) => ( + versionListKeys.reverse().map((version, index) => ( { setVersion(version) }}>
-
- { versionList[version].name } +
+ + + { versionList[version].name } +
+ { isElectron() ? downloadList.includes(version) ?
:
: null }
)) @@ -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 ( -// -// -//
-//
-// {customVersions.map((url, i) => { -// if (selectedVersion === url) return (custom) -// })} -// {allversions.map((build, i) => { - -// if ((selectedVersion || defaultVersion) === build.path) { -// return ({build.longVersion}) -// } -// })} -//
-//
-//
- -// -// {allversions.length <= 0 && ( -// {}} -// > -//
-// {selectedVersion === defaultVersion ? : null} -//
-//
-// {defaultVersion} -//
-//
-//
-//
-// )} -// {allversions.length <= 0 && ( -// {}} -// > -//
-// {selectedVersion === "builtin" ? : null} -//
-//
-// builtin -//
-//
-//
-//
-// )} -// {customVersions.map((url, i) => ( -// handleLoadVersion(url)} -// > -//
-// {selectedVersion === url ? : null} -//
-//
-// custom: {url} -//
-//
-//
-//
-// ))} -// {allversions.map((build, i) => { -// if (onlyDownloaded && !build.isDownloaded) return null -// return _shouldBeAdded(build.longVersion) ? ( -// handleLoadVersion(build.path)} -// > -//
-// {selectedVersion === build.path ? : null} -//
-//
-// {build.longVersion} -//
-//
-// {platform == appPlatformTypes.desktop ? (build.isDownloaded ?
:
) : null} -//
-//
-// ) : null -// })} -//
-//
-// ); diff --git a/apps/circuit-compiler/src/app/reducers/state.ts b/apps/circuit-compiler/src/app/reducers/state.ts index cc3db06ca2..dd07927e23 100644 --- a/apps/circuit-compiler/src/app/reducers/state.ts +++ b/apps/circuit-compiler/src/app/reducers/state.ts @@ -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() } diff --git a/apps/circuit-compiler/src/app/services/circomPluginClient.ts b/apps/circuit-compiler/src/app/services/circomPluginClient.ts index 083a2100b9..4e2d2359c6 100644 --- a/apps/circuit-compiler/src/app/services/circomPluginClient.ts +++ b/apps/circuit-compiler/src/app/services/circomPluginClient.ts @@ -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 + })) + } + } } diff --git a/apps/circuit-compiler/src/app/types/index.ts b/apps/circuit-compiler/src/app/types/index.ts index b1ad621b94..bd770bbec1 100644 --- a/apps/circuit-compiler/src/app/types/index.ts +++ b/apps/circuit-compiler/src/app/types/index.ts @@ -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}[keyof ActionP export interface AppState { version: string, versionList: typeof compiler_list.wasm_builds, + versionDownloadList: string[], filePath: string, filePathToId: Record, status: CompilerStatus, diff --git a/apps/circuit-compiler/src/css/app.css b/apps/circuit-compiler/src/css/app.css index f229a913b4..d1743dffbb 100644 --- a/apps/circuit-compiler/src/css/app.css +++ b/apps/circuit-compiler/src/css/app.css @@ -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); +} diff --git a/apps/remixdesktop/log_input_signals.txt b/apps/remixdesktop/log_input_signals.txt new file mode 100644 index 0000000000..11f5f685f9 --- /dev/null +++ b/apps/remixdesktop/log_input_signals.txt @@ -0,0 +1,2 @@ +main.a 1 +main.b 1 diff --git a/apps/remixdesktop/src/plugins/circomElectronBasePlugin.ts b/apps/remixdesktop/src/plugins/circomElectronBasePlugin.ts index bf7944e8ee..b1cf460b12 100644 --- a/apps/remixdesktop/src/plugins/circomElectronBasePlugin.ts +++ b/apps/remixdesktop/src/plugins/circomElectronBasePlugin.ts @@ -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) { - if (!this.isCircomInstalled) await this.install() + async run(filePath: string, version = 'latest', options: Record) { + 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) + } } \ No newline at end of file diff --git a/apps/remixdesktop/src/tools/circom.ts b/apps/remixdesktop/src/tools/circom.ts index 9ffb9f1d30..41b31b4d0b 100644 --- a/apps/remixdesktop/src/tools/circom.ts +++ b/apps/remixdesktop/src/tools/circom.ts @@ -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) { - const installationPath = getInstallationPath() + async run (filePath: string, version: string, options?: Record) { + 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('/') -} \ No newline at end of file +}