From 1cabf35af1d4bfc3787fe4eefb88360477844ab9 Mon Sep 17 00:00:00 2001 From: ioedeveloper Date: Fri, 26 May 2023 10:45:57 +0100 Subject: [PATCH] Create react UI for circom plugin --- apps/circuit-compiler/project.json | 59 ++++++++++++ .../src/app/actions/dispatch.ts | 6 ++ .../circuit-compiler/src/app/actions/index.ts | 14 +++ apps/circuit-compiler/src/app/app.tsx | 56 ++++++++++++ .../src/app/contexts/index.ts | 4 + .../src/app/reducers/index.ts | 18 ++++ .../src/app/services/circomPluginClient.ts | 37 ++++++++ apps/circuit-compiler/src/app/types/index.ts | 22 +++++ apps/circuit-compiler/src/css/app.css | 0 .../src/example/simple.circom | 10 +++ apps/circuit-compiler/src/index.html | 15 ++++ apps/circuit-compiler/src/logo.svg | 1 + apps/circuit-compiler/src/main.tsx | 8 ++ apps/circuit-compiler/src/polyfills.ts | 7 ++ apps/circuit-compiler/src/profile.json | 17 ++++ apps/circuit-compiler/tsconfig.app.json | 24 +++++ apps/circuit-compiler/tsconfig.json | 17 ++++ apps/circuit-compiler/webpack.config.js | 90 +++++++++++++++++++ apps/remix-ide/project.json | 2 +- apps/remix-ide/src/remixAppManager.js | 2 +- apps/remix-ide/src/remixEngine.js | 1 + .../helper/src/lib/helper-components.tsx | 8 ++ .../src/lib/compiler-container.tsx | 10 ++- 23 files changed, 424 insertions(+), 4 deletions(-) create mode 100644 apps/circuit-compiler/project.json create mode 100644 apps/circuit-compiler/src/app/actions/dispatch.ts create mode 100644 apps/circuit-compiler/src/app/actions/index.ts create mode 100644 apps/circuit-compiler/src/app/app.tsx create mode 100644 apps/circuit-compiler/src/app/contexts/index.ts create mode 100644 apps/circuit-compiler/src/app/reducers/index.ts create mode 100644 apps/circuit-compiler/src/app/services/circomPluginClient.ts create mode 100644 apps/circuit-compiler/src/app/types/index.ts create mode 100644 apps/circuit-compiler/src/css/app.css create mode 100644 apps/circuit-compiler/src/example/simple.circom create mode 100644 apps/circuit-compiler/src/index.html create mode 100644 apps/circuit-compiler/src/logo.svg create mode 100644 apps/circuit-compiler/src/main.tsx create mode 100644 apps/circuit-compiler/src/polyfills.ts create mode 100644 apps/circuit-compiler/src/profile.json create mode 100644 apps/circuit-compiler/tsconfig.app.json create mode 100644 apps/circuit-compiler/tsconfig.json create mode 100644 apps/circuit-compiler/webpack.config.js diff --git a/apps/circuit-compiler/project.json b/apps/circuit-compiler/project.json new file mode 100644 index 0000000000..eb501cc21d --- /dev/null +++ b/apps/circuit-compiler/project.json @@ -0,0 +1,59 @@ +{ + "name": "circuit-compiler", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "apps/circuit-compiler/src", + "projectType": "application", + "implicitDependencies": ["remixd"], + "targets": { + "build": { + "executor": "@nrwl/webpack:webpack", + "outputs": ["{options.outputPath}"], + "defaultConfiguration": "development", + "options": { + "compiler": "babel", + "outputPath": "dist/apps/circuit-compiler", + "index": "apps/circuit-compiler/src/index.html", + "baseHref": "./", + "main": "apps/circuit-compiler/src/main.tsx", + "polyfills": "apps/circuit-compiler/src/polyfills.ts", + "tsConfig": "apps/circuit-compiler/tsconfig.app.json", + "assets": ["apps/circuit-compiler/src/profile.json"], + "styles": ["apps/circuit-compiler/src/css/app.css"], + "scripts": [], + "webpackConfig": "apps/circuit-compiler/webpack.config.js" + }, + "configurations": { + "development": { + }, + "production": { + "fileReplacements": [ + { + "replace": "apps/circuit-compiler/src/environments/environment.ts", + "with": "apps/circuit-compiler/src/environments/environment.prod.ts" + } + ] + } + } + }, + "serve": { + "executor": "@nrwl/webpack:dev-server", + "defaultConfiguration": "development", + "options": { + "buildTarget": "circuit-compiler:build", + "hmr": true, + "baseHref": "/" + }, + "configurations": { + "development": { + "buildTarget": "circuit-compiler:build:development", + "port": 2023 + }, + "production": { + "buildTarget": "circuit-compiler:build:production" + } + } + } + }, + "tags": [] + } + \ No newline at end of file diff --git a/apps/circuit-compiler/src/app/actions/dispatch.ts b/apps/circuit-compiler/src/app/actions/dispatch.ts new file mode 100644 index 0000000000..cd45037ce1 --- /dev/null +++ b/apps/circuit-compiler/src/app/actions/dispatch.ts @@ -0,0 +1,6 @@ +import { Dispatch } from "react" +import { Action } from "../types" + +export const dispatchCheckRemixd = (status: boolean) => (dispatch: Dispatch>) => { + dispatch({ type: 'SET_REMIXD_CONNECTION_STATUS', payload: status }) +} \ No newline at end of file diff --git a/apps/circuit-compiler/src/app/actions/index.ts b/apps/circuit-compiler/src/app/actions/index.ts new file mode 100644 index 0000000000..8201a298f1 --- /dev/null +++ b/apps/circuit-compiler/src/app/actions/index.ts @@ -0,0 +1,14 @@ +import { Dispatch } from 'react' +import { CircomPluginClient } from '../services/circomPluginClient' + +const plugin = new CircomPluginClient() + +export const initCircomPluginActions = () => async (dispatch: Dispatch) => { + plugin.internalEvents.on('connectionChanged', (connected: boolean) => { + dispatch({ type: 'SET_REMIXD_CONNECTION_STATUS', payload: connected }) + }) +} + +export function activateRemixd () { + plugin.activateRemixDeamon() +} \ No newline at end of file diff --git a/apps/circuit-compiler/src/app/app.tsx b/apps/circuit-compiler/src/app/app.tsx new file mode 100644 index 0000000000..ef447282d8 --- /dev/null +++ b/apps/circuit-compiler/src/app/app.tsx @@ -0,0 +1,56 @@ +import React, { useEffect, useReducer } from 'react' +import { RenderIf, RenderIfNot } from '@remix-ui/helper' +import { Alert, Button, Tabs, Tab } from 'react-bootstrap' + +import { AppContext } from './contexts' +import { appInitialState, appReducer } from './reducers' +import { activateRemixd, initCircomPluginActions } from './actions' + +function App() { + const [appState, dispatch] = useReducer(appReducer, appInitialState) + + useEffect(() => { + initCircomPluginActions()(dispatch) + }, []) + + const handleConnectRemixd = () => { + activateRemixd() + } + + const value = { + appState, + dispatch + } + + return ( + +
+ + + Requirements! +
    +
  1. Circuit Compiler requires that you have Rust lang installed on your machine.
  2. +
  3. Remix-IDE is connected to your local file system.
  4. +
+
+ +
+
+
+ +
+ + + + +
+
+
+
+ ) +} + +export default App \ No newline at end of file diff --git a/apps/circuit-compiler/src/app/contexts/index.ts b/apps/circuit-compiler/src/app/contexts/index.ts new file mode 100644 index 0000000000..8cf0b14937 --- /dev/null +++ b/apps/circuit-compiler/src/app/contexts/index.ts @@ -0,0 +1,4 @@ +import { createContext } from 'react' +import { IAppContext } from '../types' + +export const AppContext = createContext({} as IAppContext) diff --git a/apps/circuit-compiler/src/app/reducers/index.ts b/apps/circuit-compiler/src/app/reducers/index.ts new file mode 100644 index 0000000000..b9bb239c6f --- /dev/null +++ b/apps/circuit-compiler/src/app/reducers/index.ts @@ -0,0 +1,18 @@ +import { Actions, AppState } from "../types" + +export const appInitialState: AppState = { + isRemixdConnected: null +} + +export const appReducer = (state = appInitialState, action: Actions): AppState => { + switch (action.type) { + case 'SET_REMIXD_CONNECTION_STATUS': + return { + ...state, + isRemixdConnected: 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 new file mode 100644 index 0000000000..d5bcb1869f --- /dev/null +++ b/apps/circuit-compiler/src/app/services/circomPluginClient.ts @@ -0,0 +1,37 @@ +import { PluginClient } from '@remixproject/plugin' +import { createClient } from '@remixproject/plugin-webview' +import EventManager from 'events' + +export class CircomPluginClient extends PluginClient { + private connected: boolean + public internalEvents: EventManager + + constructor() { + super() + createClient(this) + this.internalEvents = new EventManager() + this.methods = ["sendAsync", "init", "deactivate"] + this.onload() + } + + init (): void { + console.log('initializing circom plugin...') + } + + onActivation(): void { + this.subscribeToEvents() + } + + activateRemixDeamon (): void { + this.call('manager', 'activatePlugin', 'remixd') + } + + subscribeToEvents (): void { + this.on('filePanel', 'setWorkspace', (workspace: { name: string, isLocalhost: boolean }) => { + if (this.connected !== workspace.isLocalhost) { + this.connected = workspace.isLocalhost + this.internalEvents.emit('connectionChanged', this.connected) + } + }) + } +} diff --git a/apps/circuit-compiler/src/app/types/index.ts b/apps/circuit-compiler/src/app/types/index.ts new file mode 100644 index 0000000000..fbc68b6d39 --- /dev/null +++ b/apps/circuit-compiler/src/app/types/index.ts @@ -0,0 +1,22 @@ +import { Dispatch } from "react" + +export interface IAppContext { + appState: AppState, + dispatch: Dispatch +} + +export interface ActionPayloadTypes { + SET_REMIXD_CONNECTION_STATUS: boolean +} + + +export interface Action { + type: T + payload: ActionPayloadTypes[T] +} + +export type Actions = { [A in keyof ActionPayloadTypes]: Action }[keyof ActionPayloadTypes] + +export interface AppState { + isRemixdConnected: boolean +} \ No newline at end of file diff --git a/apps/circuit-compiler/src/css/app.css b/apps/circuit-compiler/src/css/app.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apps/circuit-compiler/src/example/simple.circom b/apps/circuit-compiler/src/example/simple.circom new file mode 100644 index 0000000000..d9432fd9c2 --- /dev/null +++ b/apps/circuit-compiler/src/example/simple.circom @@ -0,0 +1,10 @@ +pragma circom 2.0.0; + +template Multiplier2() { + signal input a; + signal input b; + signal output c; + c <== a*b; + } + + component main = Multiplier2(); \ No newline at end of file diff --git a/apps/circuit-compiler/src/index.html b/apps/circuit-compiler/src/index.html new file mode 100644 index 0000000000..adb93d90f1 --- /dev/null +++ b/apps/circuit-compiler/src/index.html @@ -0,0 +1,15 @@ + + + + + Circuit - Compiler + + + + + + + +
+ + diff --git a/apps/circuit-compiler/src/logo.svg b/apps/circuit-compiler/src/logo.svg new file mode 100644 index 0000000000..267b6f5b78 --- /dev/null +++ b/apps/circuit-compiler/src/logo.svg @@ -0,0 +1 @@ +remix_logo1 \ No newline at end of file diff --git a/apps/circuit-compiler/src/main.tsx b/apps/circuit-compiler/src/main.tsx new file mode 100644 index 0000000000..b63438d846 --- /dev/null +++ b/apps/circuit-compiler/src/main.tsx @@ -0,0 +1,8 @@ +import React from 'react' +import ReactDOM from 'react-dom' +import App from './app/app' + +ReactDOM.render( + , + document.getElementById('root') +) \ No newline at end of file diff --git a/apps/circuit-compiler/src/polyfills.ts b/apps/circuit-compiler/src/polyfills.ts new file mode 100644 index 0000000000..2adf3d05b6 --- /dev/null +++ b/apps/circuit-compiler/src/polyfills.ts @@ -0,0 +1,7 @@ +/** + * Polyfill stable language features. These imports will be optimized by `@babel/preset-env`. + * + * See: https://github.com/zloirock/core-js#babel + */ +import 'core-js/stable'; +import 'regenerator-runtime/runtime'; diff --git a/apps/circuit-compiler/src/profile.json b/apps/circuit-compiler/src/profile.json new file mode 100644 index 0000000000..4f54bcee38 --- /dev/null +++ b/apps/circuit-compiler/src/profile.json @@ -0,0 +1,17 @@ +{ + "name": "circuit-compiler", + "kind": "provider", + "displayName": "Circuit Compiler", + "events": [], + "version": "2.0.0", + "methods": ["sendAsync", "init"], + "canActivate": ["remixd"], + "url": "", + "description": "Enables circuit compilation and computing a witness for ZK proofs", + "icon": "https://docs.circom.io/assets/images/favicon.png", + "location": "sidePanel", + "documentation": "", + "repo": "https://github.com/ethereum/remix-project/tree/master/apps/circuit-compiler", + "maintainedBy": "Remix", + "authorContact": "" +} diff --git a/apps/circuit-compiler/tsconfig.app.json b/apps/circuit-compiler/tsconfig.app.json new file mode 100644 index 0000000000..2272b8a388 --- /dev/null +++ b/apps/circuit-compiler/tsconfig.app.json @@ -0,0 +1,24 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "types": ["node"] + }, + "files": [ + "../../node_modules/@nrwl/react/typings/cssmodule.d.ts", + "../../node_modules/@nrwl/react/typings/image.d.ts" + ], + "exclude": [ + "jest.config.ts", + "**/*.spec.ts", + "**/*.test.ts", + "**/*.spec.tsx", + "**/*.test.tsx", + "**/*.spec.js", + "**/*.test.js", + "**/*.spec.jsx", + "**/*.test.jsx" + ], + "include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"] + } + \ No newline at end of file diff --git a/apps/circuit-compiler/tsconfig.json b/apps/circuit-compiler/tsconfig.json new file mode 100644 index 0000000000..3b6130a0a8 --- /dev/null +++ b/apps/circuit-compiler/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "jsx": "react-jsx", + "allowJs": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.app.json" + } + ] + } + \ No newline at end of file diff --git a/apps/circuit-compiler/webpack.config.js b/apps/circuit-compiler/webpack.config.js new file mode 100644 index 0000000000..51de9dd815 --- /dev/null +++ b/apps/circuit-compiler/webpack.config.js @@ -0,0 +1,90 @@ +const { composePlugins, withNx } = require('@nrwl/webpack') +const webpack = require('webpack') +const TerserPlugin = require("terser-webpack-plugin") +const CssMinimizerPlugin = require("css-minimizer-webpack-plugin") + +// Nx plugins for webpack. +module.exports = composePlugins(withNx(), (config) => { + // Update the webpack config as needed here. + // e.g. `config.plugins.push(new MyPlugin())` + // add fallback for node modules + config.resolve.fallback = { + ...config.resolve.fallback, + "crypto": require.resolve("crypto-browserify"), + "stream": require.resolve("stream-browserify"), + "path": require.resolve("path-browserify"), + "http": require.resolve("stream-http"), + "https": require.resolve("https-browserify"), + "constants": require.resolve("constants-browserify"), + "os": false, //require.resolve("os-browserify/browser"), + "timers": false, // require.resolve("timers-browserify"), + "zlib": require.resolve("browserify-zlib"), + "fs": false, + "module": false, + "tls": false, + "net": false, + "readline": false, + "child_process": false, + "buffer": require.resolve("buffer/"), + "vm": require.resolve('vm-browserify'), + } + + + // add externals + config.externals = { + ...config.externals, + solc: 'solc', + } + + // add public path + config.output.publicPath = './' + + // add copy & provide plugin + config.plugins.push( + new webpack.ProvidePlugin({ + Buffer: ['buffer', 'Buffer'], + url: ['url', 'URL'], + process: 'process/browser', + }) + ) + + // set the define plugin to load the WALLET_CONNECT_PROJECT_ID + config.plugins.push( + new webpack.DefinePlugin({ + WALLET_CONNECT_PROJECT_ID: JSON.stringify(process.env.WALLET_CONNECT_PROJECT_ID), + }) + ) + + // souce-map loader + config.module.rules.push({ + test: /\.js$/, + use: ["source-map-loader"], + enforce: "pre" + }) + + config.ignoreWarnings = [/Failed to parse source map/] // ignore source-map-loader warnings + + + // set minimizer + config.optimization.minimizer = [ + new TerserPlugin({ + parallel: true, + terserOptions: { + ecma: 2015, + compress: false, + mangle: false, + format: { + comments: false, + }, + }, + extractComments: false, + }), + new CssMinimizerPlugin(), + ]; + + config.watchOptions = { + ignored: /node_modules/ + } + + return config; +}); diff --git a/apps/remix-ide/project.json b/apps/remix-ide/project.json index 99b463867d..22e095fb9a 100644 --- a/apps/remix-ide/project.json +++ b/apps/remix-ide/project.json @@ -3,7 +3,7 @@ "$schema": "../../node_modules/nx/schemas/project-schema.json", "sourceRoot": "apps/remix-ide/src", "projectType": "application", - "implicitDependencies": ["doc-gen", "doc-viewer", "etherscan", "vyper", "solhint", "walletconnect"], + "implicitDependencies": ["doc-gen", "doc-viewer", "etherscan", "vyper", "solhint", "walletconnect", "circuit-compiler"], "targets": { "build": { "executor": "@nrwl/webpack:webpack", diff --git a/apps/remix-ide/src/remixAppManager.js b/apps/remix-ide/src/remixAppManager.js index 9c9f365f23..edb09e2ee7 100644 --- a/apps/remix-ide/src/remixAppManager.js +++ b/apps/remix-ide/src/remixAppManager.js @@ -17,7 +17,7 @@ const requiredModules = [ // services + layout views + system views // dependentModules shouldn't be manually activated (e.g hardhat is activated by remixd) const dependentModules = ['foundry', 'hardhat', 'truffle', 'slither'] -const loadLocalPlugins = ["doc-gen", "doc-viewer", "etherscan", "vyper", 'solhint', 'walletconnect'] +const loadLocalPlugins = ["doc-gen", "doc-viewer", "etherscan", "vyper", "solhint", "walletconnect", "circuit-compiler"] const sensitiveCalls = { 'fileManager': ['writeFile', 'copyFile', 'rename', 'copyDir'], diff --git a/apps/remix-ide/src/remixEngine.js b/apps/remix-ide/src/remixEngine.js index 3a0c905acd..4c2b708bfe 100644 --- a/apps/remix-ide/src/remixEngine.js +++ b/apps/remix-ide/src/remixEngine.js @@ -20,6 +20,7 @@ export class RemixEngine extends Engine { if (name === 'fetchAndCompile') return { queueTimeout: 60000 * 4 } if (name === 'walletconnect') return { queueTimeout: 60000 * 4 } if (name === 'udapp') return { queueTimeout: 60000 * 4 } + if (name === 'circuit-compiler') return { queueTimeout: 60000 * 4 } return { queueTimeout: 10000 } } diff --git a/libs/remix-ui/helper/src/lib/helper-components.tsx b/libs/remix-ui/helper/src/lib/helper-components.tsx index 753701598e..ea7d833d64 100644 --- a/libs/remix-ui/helper/src/lib/helper-components.tsx +++ b/libs/remix-ui/helper/src/lib/helper-components.tsx @@ -134,3 +134,11 @@ export const upgradeReportMsg = (report: LayoutCompatibilityReport) => (
{report.explain()}
) + +export function RenderIf({ condition, children }: { condition: boolean, children: JSX.Element }) { + return condition ? children : null +} + +export function RenderIfNot({ condition, children }: { condition: boolean, children: JSX.Element }) { + return condition ? null : children +} diff --git a/libs/remix-ui/solidity-compiler/src/lib/compiler-container.tsx b/libs/remix-ui/solidity-compiler/src/lib/compiler-container.tsx index bc0b25dd02..757d643df3 100644 --- a/libs/remix-ui/solidity-compiler/src/lib/compiler-container.tsx +++ b/libs/remix-ui/solidity-compiler/src/lib/compiler-container.tsx @@ -514,8 +514,8 @@ export const CompilerContainer = (props: CompilerContainerProps) => { const compile = () => { const currentFile = api.currentFile + if (currentFile.endsWith('.circom')) return compileCircuit() if (!isSolFileSelected()) return - _setCompilerVersionFromPragma(currentFile) let externalCompType if (hhCompilation) externalCompType = 'hardhat' @@ -526,8 +526,8 @@ export const CompilerContainer = (props: CompilerContainerProps) => { const compileAndRun = () => { const currentFile = api.currentFile + if (currentFile.endsWith('.circom')) return compileCircuit() if (!isSolFileSelected()) return - _setCompilerVersionFromPragma(currentFile) let externalCompType if (hhCompilation) externalCompType = 'hardhat' @@ -536,6 +536,12 @@ export const CompilerContainer = (props: CompilerContainerProps) => { compileTabLogic.runCompiler(externalCompType) } + const compileCircuit = () => { + const currentFile = api.currentFile + + console.log('Compiling circuit ' + currentFile) + } + const _updateVersionSelector = (version, customUrl = '') => { // update selectedversion of previous one got filtered out let selectedVersion = version