Merge pull request #4481 from ethereum/circom-constraints

Show Circom Log in Terminal and AskGPT for errors/warnings
pull/4492/head
yann300 10 months ago committed by GitHub
commit 6c72d7c231
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 41
      apps/circuit-compiler/src/app/components/container.tsx
  2. 16
      apps/circuit-compiler/src/app/components/feedback.tsx
  3. 3
      apps/circuit-compiler/src/app/components/feedbackAlert.tsx
  4. 26
      apps/circuit-compiler/src/app/services/circomPluginClient.ts
  5. 8
      apps/circuit-compiler/src/app/types/index.ts
  6. 2
      package.json
  7. 8
      yarn.lock

@ -10,7 +10,7 @@ import { CircuitActions } from './actions'
import { WitnessToggler } from './witnessToggler' import { WitnessToggler } from './witnessToggler'
import { WitnessSection } from './witness' import { WitnessSection } from './witness'
import { CompilerFeedback } from './feedback' import { CompilerFeedback } from './feedback'
import { PrimeValue } from '../types' import { CompilerReport, PrimeValue } from '../types'
export function Container () { export function Container () {
const circuitApp = useContext(CircuitAppContext) const circuitApp = useContext(CircuitAppContext)
@ -58,6 +58,43 @@ export function Container () {
circuitApp.dispatch({ type: 'SET_HIDE_WARNINGS', payload: value }) circuitApp.dispatch({ type: 'SET_HIDE_WARNINGS', payload: value })
} }
const askGPT = async (report: CompilerReport) => {
if (report.labels.length > 0) {
const location = circuitApp.appState.filePathToId[report.labels[0].file_id]
const error = report.labels[0].message
if (location) {
const fullPathLocation = await circuitApp.plugin.resolveReportPath(location)
const content = await circuitApp.plugin.call('fileManager', 'readFile', fullPathLocation)
const message = `
circom code: ${content}
error message: ${error}
full circom error: ${JSON.stringify(report, null, 2)}
explain why the error occurred and how to fix it.
`
// @ts-ignore
await circuitApp.plugin.call('openaigpt', 'message', message)
} else {
const message = `
error message: ${error}
full circom error: ${JSON.stringify(report, null, 2)}
explain why the error occurred and how to fix it.
`
// @ts-ignore
await circuitApp.plugin.call('openaigpt', 'message', message)
}
} else {
const error = report.message
const message = `
error message: ${error}
full circom error: ${JSON.stringify(report, null, 2)}
explain why the error occurred and how to fix it.
`
// @ts-ignore
await circuitApp.plugin.call('openaigpt', 'message', message)
}
}
return ( return (
<section> <section>
<article> <article>
@ -86,7 +123,7 @@ export function Container () {
</WitnessToggler> </WitnessToggler>
</RenderIf> </RenderIf>
<RenderIf condition={(circuitApp.appState.status !== 'compiling') && (circuitApp.appState.status !== 'computing') && (circuitApp.appState.status !== 'generating')}> <RenderIf condition={(circuitApp.appState.status !== 'compiling') && (circuitApp.appState.status !== 'computing') && (circuitApp.appState.status !== 'generating')}>
<CompilerFeedback feedback={circuitApp.appState.feedback} filePathToId={circuitApp.appState.filePathToId} openErrorLocation={handleOpenErrorLocation} hideWarnings={circuitApp.appState.hideWarnings} /> <CompilerFeedback feedback={circuitApp.appState.feedback} filePathToId={circuitApp.appState.filePathToId} openErrorLocation={handleOpenErrorLocation} hideWarnings={circuitApp.appState.hideWarnings} askGPT={askGPT} />
</RenderIf> </RenderIf>
</div> </div>
</div> </div>

@ -4,7 +4,7 @@ import { RenderIf } from '@remix-ui/helper'
import {CopyToClipboard} from '@remix-ui/clipboard' import {CopyToClipboard} from '@remix-ui/clipboard'
import { FeedbackAlert } from './feedbackAlert' import { FeedbackAlert } from './feedbackAlert'
export function CompilerFeedback ({ feedback, filePathToId, hideWarnings, openErrorLocation }: CompilerFeedbackProps) { export function CompilerFeedback ({ feedback, filePathToId, hideWarnings, openErrorLocation, askGPT }: CompilerFeedbackProps) {
const [ showException, setShowException ] = useState<boolean>(true) const [ showException, setShowException ] = useState<boolean>(true)
const handleCloseException = () => { const handleCloseException = () => {
@ -17,6 +17,10 @@ export function CompilerFeedback ({ feedback, filePathToId, hideWarnings, openEr
} }
} }
const handleAskGPT = (report: CompilerReport) => {
askGPT(report)
}
return ( return (
<div> <div>
<div className="circuit_errors_box py-4"> <div className="circuit_errors_box py-4">
@ -40,12 +44,18 @@ export function CompilerFeedback ({ feedback, filePathToId, hideWarnings, openEr
<div key={index} onClick={() => handleOpenError(response)}> <div key={index} onClick={() => handleOpenError(response)}>
<RenderIf condition={response.type === 'Error'}> <RenderIf condition={response.type === 'Error'}>
<div className={`circuit_feedback ${response.type.toLowerCase()} alert alert-danger`} data-id="circuit_feedback"> <div className={`circuit_feedback ${response.type.toLowerCase()} alert alert-danger`} data-id="circuit_feedback">
<FeedbackAlert message={response.message} location={ response.labels[0] ? response.labels[0].message + ` ${filePathToId[response.labels[0].file_id]}:${response.labels[0].range.start}:${response.labels[0].range.end}` : null} /> <FeedbackAlert
message={response.message}
location={ response.labels[0] ? response.labels[0].message + ` ${filePathToId[response.labels[0].file_id]}:${response.labels[0].range.start}:${response.labels[0].range.end}` : null}
askGPT={ () => handleAskGPT(response) } />
</div> </div>
</RenderIf> </RenderIf>
<RenderIf condition={(response.type === 'Warning') && !hideWarnings}> <RenderIf condition={(response.type === 'Warning') && !hideWarnings}>
<div className={`circuit_feedback ${response.type.toLowerCase()} alert alert-warning`} data-id="circuit_feedback"> <div className={`circuit_feedback ${response.type.toLowerCase()} alert alert-warning`} data-id="circuit_feedback">
<FeedbackAlert message={response.message} location={null} /> <FeedbackAlert
message={response.message}
location={null}
askGPT={() => { handleAskGPT(response) }} />
</div> </div>
</RenderIf> </RenderIf>
</div> </div>

@ -3,7 +3,7 @@ import { FeedbackAlertProps } from '../types'
import { RenderIf } from '@remix-ui/helper' import { RenderIf } from '@remix-ui/helper'
import {CopyToClipboard} from '@remix-ui/clipboard' import {CopyToClipboard} from '@remix-ui/clipboard'
export function FeedbackAlert ({ message, location }: FeedbackAlertProps) { export function FeedbackAlert ({ message, location, askGPT }: FeedbackAlertProps) {
const [ showAlert, setShowAlert] = useState<boolean>(true) const [ showAlert, setShowAlert] = useState<boolean>(true)
const handleCloseAlert = () => { const handleCloseAlert = () => {
@ -24,6 +24,7 @@ export function FeedbackAlert ({ message, location }: FeedbackAlertProps) {
<span className="ml-3 pt-1 py-1" > <span className="ml-3 pt-1 py-1" >
<CopyToClipboard content={message} className="p-0 m-0 far fa-copy error" direction={'top'} /> <CopyToClipboard content={message} className="p-0 m-0 far fa-copy error" direction={'top'} />
</span> </span>
<span className="border border-success text-success btn-sm" onClick={askGPT}>ASK GPT</span>
</div> </div>
</> </>
</RenderIf> </RenderIf>

@ -123,14 +123,18 @@ 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_start') this.internalEvents.emit('circuit_compiling_start')
// @ts-ignore
this.call('terminal', 'log', { type: 'log', value: 'Compiling ' + path })
const [parseErrors, filePathToId] = await this.parse(path) const [parseErrors, filePathToId] = await this.parse(path)
if (parseErrors && (parseErrors.length > 0)) { if (parseErrors && (parseErrors.length > 0)) {
if (parseErrors[0].type === 'Error') { if (parseErrors[0].type === 'Error') {
this.internalEvents.emit('circuit_parsing_errored', parseErrors, filePathToId) this.internalEvents.emit('circuit_parsing_errored', parseErrors, filePathToId)
this.logCompilerReport(parseErrors)
return return
} else if (parseErrors[0].type === 'Warning') { } else if (parseErrors[0].type === 'Warning') {
this.internalEvents.emit('circuit_parsing_warning', parseErrors, filePathToId) this.internalEvents.emit('circuit_parsing_warning', parseErrors, filePathToId)
this.logCompilerReport(parseErrors)
} }
} else { } else {
this.internalEvents.emit('circuit_parsing_done', parseErrors, filePathToId) this.internalEvents.emit('circuit_parsing_done', parseErrors, filePathToId)
@ -147,6 +151,7 @@ export class CircomPluginClient extends PluginClient {
if (circuitProgram.length < 1) { if (circuitProgram.length < 1) {
const circuitErrors = circuitApi.report() const circuitErrors = circuitApi.report()
this.logCompilerReport(circuitErrors)
throw new Error(circuitErrors) throw new Error(circuitErrors)
} else { } else {
this.lastCompiledFile = path this.lastCompiledFile = path
@ -166,19 +171,28 @@ export class CircomPluginClient extends PluginClient {
} else { } else {
this.internalEvents.emit('circuit_compiling_done', []) this.internalEvents.emit('circuit_compiling_done', [])
} }
circuitApi.log().map(log => {
log && this.call('terminal', 'log', { type: 'log', value: log })
})
// @ts-ignore
this.call('terminal', 'log', { type: 'typewritersuccess', value: 'Everything went okay, circom safe' })
} }
} }
async generateR1cs (path: string, compilationConfig?: CompilationConfig): Promise<void> { async generateR1cs (path: string, compilationConfig?: CompilationConfig): Promise<void> {
this.internalEvents.emit('circuit_generating_r1cs_start') this.internalEvents.emit('circuit_generating_r1cs_start')
// @ts-ignore
this.call('terminal', 'log', { type: 'log', value: 'Generating R1CS for ' + path })
const [parseErrors, filePathToId] = await this.parse(path) const [parseErrors, filePathToId] = await this.parse(path)
if (parseErrors && (parseErrors.length > 0)) { if (parseErrors && (parseErrors.length > 0)) {
if (parseErrors[0].type === 'Error') { if (parseErrors[0].type === 'Error') {
this.internalEvents.emit('circuit_parsing_errored', parseErrors) this.internalEvents.emit('circuit_parsing_errored', parseErrors)
this.logCompilerReport(parseErrors)
return return
} else if (parseErrors[0].type === 'Warning') { } else if (parseErrors[0].type === 'Warning') {
this.internalEvents.emit('circuit_parsing_warning', parseErrors) this.internalEvents.emit('circuit_parsing_warning', parseErrors)
this.logCompilerReport(parseErrors)
} }
} else { } else {
this.internalEvents.emit('circuit_parsing_done', parseErrors, filePathToId) this.internalEvents.emit('circuit_parsing_done', parseErrors, filePathToId)
@ -195,6 +209,7 @@ export class CircomPluginClient extends PluginClient {
if (r1csProgram.length < 1) { if (r1csProgram.length < 1) {
const r1csErrors = r1csApi.report() const r1csErrors = r1csApi.report()
this.logCompilerReport(r1csErrors)
throw new Error(r1csErrors) throw new Error(r1csErrors)
} else { } else {
this.internalEvents.emit('circuit_generating_r1cs_done') this.internalEvents.emit('circuit_generating_r1cs_done')
@ -203,6 +218,11 @@ export class CircomPluginClient extends PluginClient {
// @ts-ignore // @ts-ignore
await this.call('fileManager', 'writeFile', writePath, r1csProgram, true) await this.call('fileManager', 'writeFile', writePath, r1csProgram, true)
r1csApi.log().map(log => {
log && this.call('terminal', 'log', { type: 'log', value: log })
})
// @ts-ignore
this.call('terminal', 'log', { type: 'typewritersuccess', value: 'Everything went okay, circom safe' })
} }
} }
@ -373,4 +393,10 @@ export class CircomPluginClient extends PluginClient {
} }
} }
} }
async logCompilerReport (report: CompilerReport[]): Promise<void> {
this.call('terminal', 'log', { type: 'log', value: JSON.stringify(report, null, 2) })
if (report[0].type === 'Error') this.call('terminal', 'log', { type: 'error', value: 'previous errors were found' })
if (report[0].type === 'Warning') this.call('terminal', 'log', { type: 'log', value: 'previous warnings were found' })
}
} }

@ -1,6 +1,6 @@
import { compiler_list } from 'circom_wasm' import { compiler_list } from 'circom_wasm'
import {Dispatch} from 'react' import {Dispatch} from 'react'
import { CircomPluginClient } from '../services/circomPluginClient' import type { CircomPluginClient } from '../services/circomPluginClient'
export type CompilerStatus = "compiling" | "generating" | "computing" | "idle" | "errored" | "warning" export type CompilerStatus = "compiling" | "generating" | "computing" | "idle" | "errored" | "warning"
export interface ICircuitAppContext { export interface ICircuitAppContext {
@ -51,7 +51,8 @@ export type CompilerFeedbackProps = {
feedback: string | CompilerReport[], feedback: string | CompilerReport[],
filePathToId: Record<string, string>, filePathToId: Record<string, string>,
openErrorLocation: (location: string, startRange: string) => void, openErrorLocation: (location: string, startRange: string) => void,
hideWarnings: boolean hideWarnings: boolean,
askGPT: (report: CompilerReport) => void
} }
export type CompilerReport = { export type CompilerReport = {
@ -71,7 +72,8 @@ export type CompilerReport = {
export type FeedbackAlertProps = { export type FeedbackAlertProps = {
message: string, message: string,
location: string location: string,
askGPT: () => void
} }
export type ConfigurationsProps = { export type ConfigurationsProps = {

@ -164,7 +164,7 @@
"brace": "^0.8.0", "brace": "^0.8.0",
"change-case": "^4.1.1", "change-case": "^4.1.1",
"chokidar": "^2.1.8", "chokidar": "^2.1.8",
"circom_wasm": "^0.2.0", "circom_wasm": "^0.2.1",
"color-support": "^1.1.3", "color-support": "^1.1.3",
"commander": "^9.4.1", "commander": "^9.4.1",
"core-js": "^3.6.5", "core-js": "^3.6.5",

@ -10537,10 +10537,10 @@ circom_runtime@0.1.22:
dependencies: dependencies:
ffjavascript "0.2.57" ffjavascript "0.2.57"
circom_wasm@^0.2.0: circom_wasm@^0.2.1:
version "0.2.0" version "0.2.1"
resolved "https://registry.yarnpkg.com/circom_wasm/-/circom_wasm-0.2.0.tgz#c35537f0b1f5bfd3d88898306f46c3a3457e5589" resolved "https://registry.yarnpkg.com/circom_wasm/-/circom_wasm-0.2.1.tgz#11eeceb497c03461676b3bc21d7d71ac3310dd58"
integrity sha512-eqDCbAXJQkKnrAg0Ow3bdaGciGTooRKL20941JGzX8IcqgHoGiZxaSLATkYy97dbmJFrxe8Wr+mOGnvdbqN9bw== integrity sha512-57Xhg3nUcQX+aMr+sH8XyxklpPgAWohjGkaEbiJDv3UiUveFAB2pOFOOE4whoMm7mjxKbO4n4mVs1oC031ApQQ==
circular-json@^0.3.0: circular-json@^0.3.0:
version "0.3.3" version "0.3.3"

Loading…
Cancel
Save