diff --git a/README.md b/README.md index d109f7b..5bdff17 100644 --- a/README.md +++ b/README.md @@ -7,20 +7,20 @@ # Hardhat ZKit -**The ultimate environment for building with Circom.** +**The ultimate TypeScript environment for Circom development.** ## What -This hardhat plugin is a zero-config, one-stop Circom development environment that abstracts away circuits management hassle and lets you focus on the important - code. +This hardhat plugin is a zero-config, one-stop Circom development environment that streamlines circuits management and lets you focus on the important - code. - Developer-oriented abstractions that simplify `r1cs`, `zkey`, `vkey`, and `witness` generation processes. - Recompilation of only the modified circuits. - Full TypeScript typization of signals and ZK proofs. - Automatic downloads of phase-1 `ptau` files. - Convenient phase-2 contributions to `zkey` files. +- Available `witness` testing via chai assertions. - Invisible `wasm`-based Circom compiler management. - Simplified `node_modules` libraries resolution. -- Extensive development and testing API. - Rich plugin configuration. - And much more! @@ -32,14 +32,10 @@ npm install --save-dev @solarity/hardhat-zkit And add the following line to your `hardhat.config`: -```js -require("@solarity/hardhat-zkit"); -``` - -Or if you are using TypeScript: - ```ts -import "@solarity/hardhat-zkit"; +import "@solarity/hardhat-zkit"; // TypeScript + +require("@solarity/hardhat-zkit"); // JavaScript ``` > [!TIP] @@ -59,7 +55,6 @@ module.exports = { skipFiles: [], c: false, json: false, - sym: false, }, setupSettings: { contributionSettings: { @@ -91,7 +86,6 @@ Where: - `skipFiles` - The list of directories (or files) to be excluded from the compilation. - `c` - The flag to generate the c-based witness generator (generates wasm by default). - `json` - The flag to output the constraints in json format. - - `sym` - The flag to output the constraint system in an annotated mode. - `setupSettings` - `contributionSettings` - `contributionTemplate` - The option to indicate which proving system to use. @@ -107,11 +101,27 @@ Where: - `nativeCompiler` - The flag indicating whether to use the natively installed compiler. - `quiet` - The flag indicating whether to suppress the output. +### Tasks + +There are several hardhat tasks that the plugin provides: + +- `zkit:compile` task that compiles or recompiles the modified circuits with the main component. +- `zkit:setup` task that generates or regenerates `zkey` and `vkey` for the previously compiled circuits. +- `zkit:make` task that executes both `zkit:compile` and `zkit:setup` for convenience. +- `zkit:verifiers` task that generates Solidity verifiers for all the previously setup circuits. +- `zkit:clean` task that cleans up the generated artifacts, types, etc. + +To view the available options, run the help command: + +```bash +npx hardhat help +``` + ### Typization The plugin provides full TypeScript typization of Circom circuits leveraging [`zktype`](https://github.com/dl-solarity/zktype) library. -The following config may be added to `tsconfig.json` file to allow for a better user experience: +The following config may be added to `tsconfig.json` file to allow for a better development experience: ```json { @@ -123,23 +133,28 @@ The following config may be added to `tsconfig.json` file to allow for a better } ``` -### Tasks +### Testing -There are several hardhat tasks that the plugin provides: +In order to utilize user-friendly [Chai](https://www.chaijs.com/) assertions for witness and ZK proof testing, the [`chai-zkit`](https://github.com/dl-solarity/chai-zkit) package needs to be installed: -- `zkit:compile` task that compiles or recompiles the modified circuits with the main component. -- `zkit:setup` task that generates or regenerates `zkey` and `vkey` for the previously compiled circuits. -- `zkit:make` task that executes both `zkit:compile` and `zkit:setup` for convenience. -- `zkit:verifiers` task that generates Solidity verifiers for all the previously setup circuits. -- `zkit:clean` task that cleans up the generated artifacts, types, etc. +```bash +npm install --save-dev @solarity/chai-zkit +``` -To view the available options, run the help command: +And add the following line to your `hardhat.config`: -```bash -npx hardhat help +```ts +import "@solarity/chai-zkit"; // TypeScript + +require("@solarity/chai-zkit"); // JavaScript ``` -### Environment extensions +The package extends `expect` chai assertion to recognize typed `zktype` objects for frictionless testing experience. + +> [!NOTE] +> Please note that for witness testing purposes it is sufficient to compile the circuit just with `zkit:compile` task, without generating the keys. + +### Example The plugin extends the hardhat environment with the `zkit` object that allows typed circuits to be used in scripts and tests: @@ -172,16 +187,23 @@ component main = Multiplier(); ```ts -import { zkit } from "hardhat"; -import { Multiplier } from "@zkit"; // typed circuit-object +import { zkit } from "hardhat"; // hardhat-zkit plugin +import { expect } from "chai"; // chai-zkit extension +import { Multiplier } from "@zkit"; // zktype circuit-object async function main() { const circuit: Multiplier = await zkit.getCircuit("Multiplier"); - // OR await zkit.getCircuit("circuits/multiplier.circom:Multiplier"); + // or await zkit.getCircuit("circuits/multiplier.circom:Multiplier"); + + // witness testing + await expect(circuit) + .with.witnessInputs({ in1: "3", in2: "7" }) + .to.have.witnessOutputs({ out: "21" }); + // proof testing const proof = await circuit.generateProof({ in1: "4", in2: "2" }); - await circuit.verifyProof(proof); // success + expect(await circuit.verifyProof(proof)).to.be.true; } main() @@ -199,9 +221,9 @@ To see the plugin in action, place the `Multiplier` circuit in the `circuits` di npx hardhat zkit:make ``` -This command will compile the circuit leveraging `wasm`-based Circom compiler, download the necessary `ptau` file regarding the number of constraints, build the required `zkey` and `vkey` files, and generate TypeScript object wrappers to enable full typization of signals and ZK proofs. +This command will compile the circuit leveraging `wasm`-based Circom compiler, download the necessary `ptau` file regarding the number of circuit's constraints, build the required `zkey` and `vkey` files, and generate TypeScript object wrappers to enable full typization of signals and ZK proofs. -Afterward, you may proceed with the provided hardhat script. +Afterward, you may run the provided hardhat script. ### API reference @@ -225,6 +247,7 @@ Where: ## Known limitations -- There is a typization bug that the typer incorrectly parses public inputs if they are arrays (will be fixed in 0.3.1). -- Currently the Circom `2.1.8` is used to compile circuits. +- Circuits typization will not work if an expression is used to indicate the size of a signal array. Consider extending circuit's parameters if you have expressions like this: `signal arr[n + 1]`. +- Due to current `wasm` memory limitations (address space is 32-bit), the plugin may fail to compile especially large circuits. +- At present the `wasm`-based Circom `2.1.8` is used to compile circuits. - Temporarily, the only supported proving system is `groth16`. diff --git a/package-lock.json b/package-lock.json index 0497f08..edfa644 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,20 +1,20 @@ { "name": "@solarity/hardhat-zkit", - "version": "0.3.0", + "version": "0.3.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@solarity/hardhat-zkit", - "version": "0.3.0", + "version": "0.3.1", "license": "MIT", "workspaces": [ "test/fixture-projects/*" ], "dependencies": { - "@distributedlab/circom2": "0.2.18-rc.2", + "@distributedlab/circom2": "0.2.18-rc.4", "@solarity/zkit": "0.2.4", - "@solarity/zktype": "0.2.4", + "@solarity/zktype": "0.2.7", "chalk": "4.1.2", "cli-progress": "3.12.0", "cli-table3": "0.6.5", @@ -95,30 +95,30 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.7.tgz", - "integrity": "sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.2.tgz", + "integrity": "sha512-bYcppcpKBvX4znYaPEeFau03bp89ShqNMLs+rmdptMw+heSZh9+z84d2YG+K7cYLbWwzdjtDoW/uqZmPjulClQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.7.tgz", - "integrity": "sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.2.tgz", + "integrity": "sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.7", - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helpers": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/template": "^7.24.7", - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7", + "@babel/generator": "^7.25.0", + "@babel/helper-compilation-targets": "^7.25.2", + "@babel/helper-module-transforms": "^7.25.2", + "@babel/helpers": "^7.25.0", + "@babel/parser": "^7.25.0", + "@babel/template": "^7.25.0", + "@babel/traverse": "^7.25.2", + "@babel/types": "^7.25.2", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -149,12 +149,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.7.tgz", - "integrity": "sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.0.tgz", + "integrity": "sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==", "dev": true, "dependencies": { - "@babel/types": "^7.24.7", + "@babel/types": "^7.25.0", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" @@ -164,14 +164,14 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.7.tgz", - "integrity": "sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz", + "integrity": "sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.24.7", - "@babel/helper-validator-option": "^7.24.7", - "browserslist": "^4.22.2", + "@babel/compat-data": "^7.25.2", + "@babel/helper-validator-option": "^7.24.8", + "browserslist": "^4.23.1", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, @@ -203,43 +203,6 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz", - "integrity": "sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==", - "dev": true, - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz", - "integrity": "sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==", - "dev": true, - "dependencies": { - "@babel/template": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz", - "integrity": "sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==", - "dev": true, - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-module-imports": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", @@ -254,16 +217,15 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.7.tgz", - "integrity": "sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.2.tgz", + "integrity": "sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", "@babel/helper-module-imports": "^7.24.7", "@babel/helper-simple-access": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7" + "@babel/helper-validator-identifier": "^7.24.7", + "@babel/traverse": "^7.25.2" }, "engines": { "node": ">=6.9.0" @@ -285,22 +247,10 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", - "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-string-parser": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.7.tgz", - "integrity": "sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", + "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", "dev": true, "engines": { "node": ">=6.9.0" @@ -316,22 +266,22 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.7.tgz", - "integrity": "sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz", + "integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.7.tgz", - "integrity": "sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.0.tgz", + "integrity": "sha512-MjgLZ42aCm0oGjJj8CtSM3DB8NOOf8h2l7DCTePJs29u+v7yO/RBX9nShlKMgFnRks/Q4tBAe7Hxnov9VkGwLw==", "dev": true, "dependencies": { - "@babel/template": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/template": "^7.25.0", + "@babel/types": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -424,10 +374,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.7.tgz", - "integrity": "sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==", + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.3.tgz", + "integrity": "sha512-iLTJKDbJ4hMvFPgQwwsVoxtHyWpKKPBrxkANrSYewDPaPpT5py5yeVkgPIJ7XYXhndxJpaA3PyALSXQ7u8e/Dw==", "dev": true, + "dependencies": { + "@babel/types": "^7.25.2" + }, "bin": { "parser": "bin/babel-parser.js" }, @@ -436,33 +389,30 @@ } }, "node_modules/@babel/template": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.7.tgz", - "integrity": "sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz", + "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==", "dev": true, "dependencies": { "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/parser": "^7.25.0", + "@babel/types": "^7.25.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.7.tgz", - "integrity": "sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==", + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.3.tgz", + "integrity": "sha512-HefgyP1x754oGCsKmV5reSmtV7IXj/kpaE1XYY+D9G5PvKKoFfSbiS4M77MdjuwlZKDIKFCffq9rPU+H/s3ZdQ==", "dev": true, "dependencies": { "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-hoist-variables": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/types": "^7.24.7", + "@babel/generator": "^7.25.0", + "@babel/parser": "^7.25.3", + "@babel/template": "^7.25.0", + "@babel/types": "^7.25.2", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -480,12 +430,12 @@ } }, "node_modules/@babel/types": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.7.tgz", - "integrity": "sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.2.tgz", + "integrity": "sha512-YTnYtra7W9e6/oAZEHj0bJehPRUlLH9/fbpT5LfB0NhQXyALCRkRs3zH9v07IYhkgpqX6Z78FnuccZr/l4Fs4Q==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.24.7", + "@babel/helper-string-parser": "^7.24.8", "@babel/helper-validator-identifier": "^7.24.7", "to-fast-properties": "^2.0.0" }, @@ -525,14 +475,17 @@ } }, "node_modules/@distributedlab/circom2": { - "version": "0.2.18-rc.2", - "resolved": "https://registry.npmjs.org/@distributedlab/circom2/-/circom2-0.2.18-rc.2.tgz", - "integrity": "sha512-hBFZ6J7qvxXYgERDspIeG+hh4QfUrN4O7S7JftR1J3zvF5tJ5hpXyguHDn6STQOc4fLtnjTAWDZyIbnCihJWQA==", + "version": "0.2.18-rc.4", + "resolved": "https://registry.npmjs.org/@distributedlab/circom2/-/circom2-0.2.18-rc.4.tgz", + "integrity": "sha512-5ALgnpk+mdzZDeKwR3ZIjJXjVeWt+Qz4g/lWAR4cxTrnEhiQptDacg0wtd7WKBUghDpEruLZ4wXyIdRoXuJSaQ==", "dependencies": { "@wasmer/wasi": "^0.12.0", "is-typed-array": "^1.1.8", "path-browserify": "^1.0.1" }, + "bin": { + "circom2": "cli.js" + }, "engines": { "node": ">=15" }, @@ -2106,9 +2059,9 @@ } }, "node_modules/@solarity/zktype": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@solarity/zktype/-/zktype-0.2.4.tgz", - "integrity": "sha512-Vp87y0mFLli0VDoZV9WCAr1wExICi0pNZqDBFUyILvmvRnrjB9ILhZQuux8YEDSO6pBq+RSqPpx9M7IssFmd3Q==", + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@solarity/zktype/-/zktype-0.2.7.tgz", + "integrity": "sha512-L9Z0YweqBMtaKpkrlREifgasHKWUR7iNkHaL6BUefjjtZnPj6Ft8ycLVh5V+mn+vixVkHjPRyTVTWu04plmq1Q==", "dependencies": { "ejs": "3.1.10", "prettier": "3.3.3", @@ -2197,9 +2150,9 @@ } }, "node_modules/@types/chai": { - "version": "4.3.16", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.16.tgz", - "integrity": "sha512-PatH4iOdyh3MyWtmHVFXLWCCIhUbopaltqddG9BzB+gMIzee2MJrvd+jouii9Z3wzQJruGWAm7WOMjgfG8hQlQ==", + "version": "4.3.17", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.17.tgz", + "integrity": "sha512-zmZ21EWzR71B4Sscphjief5djsLre50M6lI622OSySTmn9DB3j+C3kWroHfBQWXbOBwbgg/M8CG/hUxDLIloow==", "dev": true }, "node_modules/@types/chai-as-promised": { @@ -2291,12 +2244,12 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.14.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.10.tgz", - "integrity": "sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==", + "version": "22.1.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.1.0.tgz", + "integrity": "sha512-AOmuRF0R2/5j1knA3c6G3HOk523Ga+l+ZXltX8SF1+5oqcXijjfTd8fY3XRZqSihEu9XhtQnKYLmkFaoxgsJHw==", "dev": true, "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~6.13.0" } }, "node_modules/@types/pbkdf2": { @@ -3140,9 +3093,9 @@ } }, "node_modules/browserslist": { - "version": "4.23.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.2.tgz", - "integrity": "sha512-qkqSyistMYdxAcw+CzbZwlBy8AGmS/eEWs+sEV5TnLRGDOL+C5M2EnH6tlZyg0YoAxGJAFKh61En9BR941GnHA==", + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz", + "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==", "dev": true, "funding": [ { @@ -3159,9 +3112,9 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001640", - "electron-to-chromium": "^1.4.820", - "node-releases": "^2.0.14", + "caniuse-lite": "^1.0.30001646", + "electron-to-chromium": "^1.5.4", + "node-releases": "^2.0.18", "update-browserslist-db": "^1.1.0" }, "bin": { @@ -3295,9 +3248,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001641", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001641.tgz", - "integrity": "sha512-Phv5thgl67bHYo1TtMY/MurjkHhV4EDaCosezRXgZ8jzA/Ub+wjxAvbGvjoFENStinwi5kCyOYV3mi5tOGykwA==", + "version": "1.0.30001651", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001651.tgz", + "integrity": "sha512-9Cf+Xv1jJNe1xPZLGuUXLNkE1BoDkqRqYyFJ9TDYSqhduqA4hu4oR9HluGoWYQC/aj8WHjsGVV+bwkh0+tegRg==", "dev": true, "funding": [ { @@ -3315,9 +3268,9 @@ ] }, "node_modules/chai": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.4.1.tgz", - "integrity": "sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", + "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", "dev": true, "dependencies": { "assertion-error": "^1.1.0", @@ -3326,7 +3279,7 @@ "get-func-name": "^2.0.2", "loupe": "^2.3.6", "pathval": "^1.1.1", - "type-detect": "^4.0.8" + "type-detect": "^4.1.0" }, "engines": { "node": ">=4" @@ -3985,9 +3938,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.823", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.823.tgz", - "integrity": "sha512-4h+oPeAiGQOHFyUJOqpoEcPj/xxlicxBzOErVeYVMMmAiXUXsGpsFd0QXBMaUUbnD8hhSfLf9uw+MlsoIA7j5w==", + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.5.tgz", + "integrity": "sha512-QR7/A7ZkMS8tZuoftC/jfqNkZLQO779SSW3YuZHP4eXpj3EffGLFcB/Xu9AAZQzLccTiCV+EmUo3ha4mQ9wnlA==", "dev": true }, "node_modules/elliptic": { @@ -4469,13 +4422,13 @@ } }, "node_modules/eslint-plugin-prettier": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz", - "integrity": "sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz", + "integrity": "sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==", "dev": true, "dependencies": { "prettier-linter-helpers": "^1.0.0", - "synckit": "^0.8.6" + "synckit": "^0.9.1" }, "engines": { "node": "^14.18.0 || >=16.0.0" @@ -4499,9 +4452,9 @@ } }, "node_modules/eslint-plugin-promise": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.4.0.tgz", - "integrity": "sha512-/KWWRaD3fGkVCZsdR0RU53PSthFmoHVhZl+y9+6DqeDLSikLdlUVpVEAmI6iCRR5QyOjBYBqHZV/bdv4DJ4Gtw==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.6.0.tgz", + "integrity": "sha512-57Zzfw8G6+Gq7axm2Pdo3gW/Rx3h9Yywgn61uE/3elTCOePEHVrn2i5CdfBwA1BLK0Q0WqctICIUSqXZW/VprQ==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -5863,12 +5816,12 @@ } }, "node_modules/husky": { - "version": "9.0.11", - "resolved": "https://registry.npmjs.org/husky/-/husky-9.0.11.tgz", - "integrity": "sha512-AB6lFlbwwyIqMdHYhwPe+kjOC3Oc5P3nThEoW/AaO2BX3vJDjWPFxYLxokUZOo6RNX20He3AaT8sESs9NJcmEw==", + "version": "9.1.4", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.4.tgz", + "integrity": "sha512-bho94YyReb4JV7LYWRWxZ/xr6TtOTt8cMfmQ39MQYJ7f/YE268s3GdghGwi+y4zAeqewE5zYLvuhV0M0ijsDEA==", "dev": true, "bin": { - "husky": "bin.mjs" + "husky": "bin.js" }, "engines": { "node": ">=18" @@ -5918,9 +5871,9 @@ } }, "node_modules/immutable": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.6.tgz", - "integrity": "sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", + "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==", "dev": true }, "node_modules/import-fresh": { @@ -6064,9 +6017,9 @@ } }, "node_modules/is-core-module": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.14.0.tgz", - "integrity": "sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A==", + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.0.tgz", + "integrity": "sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA==", "dependencies": { "hasown": "^2.0.2" }, @@ -6485,9 +6438,9 @@ } }, "node_modules/jake": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.1.tgz", - "integrity": "sha512-61btcOHNnLnsOdtLgA5efqQWjnSi/vow5HbI7HMdKKWqvrKR1bLK3BPlJn9gcSaP2ewuamUSMB5XEy76KUIS2w==", + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", + "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", "dependencies": { "async": "^3.2.3", "chalk": "^4.0.2", @@ -6918,9 +6871,9 @@ } }, "node_modules/mocha": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.6.0.tgz", - "integrity": "sha512-hxjt4+EEB0SA0ZDygSS015t65lJw/I2yRCS3Ae+SJ5FrbzrXgfYwJr96f0OvIXdj7h4lv/vLCrH3rkiuizFSvw==", + "version": "10.7.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.7.0.tgz", + "integrity": "sha512-v8/rBWr2VO5YkspYINnvu81inSz2y3ODJrhO175/Exzor1RcEZZkizgE2A+w/CAXXoESS8Kys5E62dOHGHzULA==", "dev": true, "dependencies": { "ansi-colors": "^4.1.3", @@ -7051,9 +7004,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", "dev": true }, "node_modules/normalize-path": { @@ -8082,9 +8035,9 @@ } }, "node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -8567,9 +8520,9 @@ } }, "node_modules/synckit": { - "version": "0.8.8", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.8.tgz", - "integrity": "sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==", + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.1.tgz", + "integrity": "sha512-7gr8p9TQP6RAHusBOSLs46F4564ZrjV8xFmw5zCmgmhGUcw2hxsShhJ6CEiHQMgPDwAQ1fWHPM0ypc4RMAig4A==", "dev": true, "dependencies": { "@pkgr/core": "^0.1.0", @@ -8894,9 +8847,9 @@ } }, "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", "dev": true, "engines": { "node": ">=4" @@ -9177,9 +9130,9 @@ } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.13.0.tgz", + "integrity": "sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg==", "dev": true }, "node_modules/universal-url": { diff --git a/package.json b/package.json index 73100fa..e671c01 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@solarity/hardhat-zkit", - "version": "0.3.0", - "description": "The ultimate environment for building with Circom", + "version": "0.3.1", + "description": "The ultimate TypeScript environment for Circom development", "main": "dist/src/index.js", "types": "dist/src/index.d.ts", "files": [ @@ -50,9 +50,9 @@ ] }, "dependencies": { - "@distributedlab/circom2": "0.2.18-rc.2", + "@distributedlab/circom2": "0.2.18-rc.4", "@solarity/zkit": "0.2.4", - "@solarity/zktype": "0.2.4", + "@solarity/zktype": "0.2.7", "chalk": "4.1.2", "cli-progress": "3.12.0", "cli-table3": "0.6.5", diff --git a/src/config/config.ts b/src/config/config.ts index 491976b..55ec84a 100644 --- a/src/config/config.ts +++ b/src/config/config.ts @@ -13,7 +13,6 @@ const defaultConfig: ZKitConfig = { skipFiles: [], c: false, json: false, - sym: false, }, setupSettings: { contributionSettings: { diff --git a/src/constants.ts b/src/constants.ts index 04800d1..80f1a86 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -10,6 +10,8 @@ export const CIRCUIT_ARTIFACT_VERSION = "hh-zkit-artifacts-1"; export const NODE_MODULES = "node_modules"; export const COMPILER_VERSION = "0.2.18"; +export const MAGIC_DESCRIPTOR = 1337; + export const BYTES_IN_MB = 1048576; export const MAX_PTAU_ID = 27; @@ -22,7 +24,6 @@ export const CIRCOM_FILE_REG_EXP = /\w+\.circom/; export const NODE_MODULES_REG_EXP = /^node_modules\//; export const URI_SCHEME_REG_EXP = /([a-zA-Z]+):\/\//; -export const MAIN_COMPONENT_REG_EXP = - /component\s+main\s*(?:{\s*public\s+\[\s*(?:\w+,?\s*)+\]\s*}|)\s*=\s*([\w-]+)\s*\((?:\d+,?\s*|\s*)*\)\s*;/gm; +export const MAIN_COMPONENT_REG_EXP = /component\s+main.*=\s*([\w-]+)\s*\((?:.*\s*)*\)\s*;/gm; export const CIRCUIT_ARTIFACTS_SUFFIX = "_artifacts.json"; diff --git a/src/core/compile/CompilationFilesResolver.ts b/src/core/compile/CompilationFilesResolver.ts index 5bb83fe..54935e3 100644 --- a/src/core/compile/CompilationFilesResolver.ts +++ b/src/core/compile/CompilationFilesResolver.ts @@ -34,13 +34,26 @@ export class CompilationFilesResolver { const sourceNames: string[] = await this._getSourceNamesFromSourcePaths(circuitsSourcePaths); - Reporter!.verboseLog("compilation-file-resolver", "All circuits source names: %o", [sourceNames]); + Reporter!.verboseLog("compilation-file-resolver", "All circuit source names: %o", [sourceNames]); - const dependencyGraph: DependencyGraph = await this._getDependencyGraph(sourceNames); + const allFilteredSourceNames: string[] = filterCircuitFiles( + sourceNames, + this._getCircuitsDirFullPath(), + this._zkitConfig.compilationSettings, + (sourceName: string): string => { + return getNormalizedFullPath(this._projectRoot, sourceName); + }, + ); + + Reporter!.verboseLog("compilation-file-resolver", "All filtered circuit source names: %o", [ + allFilteredSourceNames, + ]); - let resolvedFilesInfoToCompile: ResolvedFileInfo[] = this._filterResolvedFiles( + const dependencyGraph: DependencyGraph = await this._getDependencyGraph(allFilteredSourceNames); + + const resolvedFilesInfoToCompile: ResolvedFileInfo[] = this._filterResolvedFiles( dependencyGraph.getResolvedFiles(), - sourceNames, + allFilteredSourceNames, dependencyGraph, ); @@ -50,31 +63,28 @@ export class CompilationFilesResolver { this._invalidateCacheMissingArtifacts(resolvedFilesInfoToCompile); + let filteredResolvedFilesInfo: ResolvedFileInfo[]; + if (!force) { Reporter!.verboseLog("compilation-file-resolver", "Force flag disabled. Start filtering..."); - resolvedFilesInfoToCompile = resolvedFilesInfoToCompile.filter((fileInfo) => + filteredResolvedFilesInfo = resolvedFilesInfoToCompile.filter((fileInfo) => this._needsCompilation(fileInfo, compileFlags), ); + } else { + filteredResolvedFilesInfo = resolvedFilesInfoToCompile; } - const filteredResolvedFilesInfo: ResolvedFileInfo[] = filterCircuitFiles( - resolvedFilesInfoToCompile, - this._getCircuitsDirFullPath(), - this._zkitConfig.compilationSettings, - (resolvedFileInfo: ResolvedFileInfo): string => { - return resolvedFileInfo.resolvedFile.absolutePath; - }, + const filteredSourceNamesToCompile: string[] = filteredResolvedFilesInfo.map( + (file) => file.resolvedFile.sourceName, ); - const filteredResolvedFilesToCompile: ResolvedFile[] = filteredResolvedFilesInfo.map((file) => file.resolvedFile); - Reporter!.verboseLog("compilation-file-resolver", "Filtered circuit source names to compile: %o", [ - filteredResolvedFilesToCompile.map((file) => file.sourceName), + filteredSourceNamesToCompile, ]); Reporter!.reportCircuitListToCompile( - resolvedFilesInfoToCompile.map((file) => file.resolvedFile), - filteredResolvedFilesToCompile, + resolvedFilesInfoToCompile.map((file) => file.resolvedFile.sourceName), + filteredSourceNamesToCompile, ); return filteredResolvedFilesInfo; diff --git a/src/core/compile/CompilationProcessor.ts b/src/core/compile/CompilationProcessor.ts index 1a9b4e5..d8deff0 100644 --- a/src/core/compile/CompilationProcessor.ts +++ b/src/core/compile/CompilationProcessor.ts @@ -28,7 +28,6 @@ import { export class CompilationProcessor { private readonly _zkitConfig: ZKitConfig; private readonly _nodeModulesPath: string; - private readonly _verbose: boolean; constructor( private readonly _config: CompilationProccessorConfig, @@ -36,7 +35,6 @@ export class CompilationProcessor { hre: HardhatRuntimeEnvironment, ) { this._zkitConfig = hre.config.zkit; - this._verbose = hre.hardhatArguments.verbose; this._nodeModulesPath = getNormalizedFullPath(hre.config.paths.root, NODE_MODULES); Reporter!.verboseLog("compilation-processor", "Created CompilationProcessor with params: %O", [ @@ -93,19 +91,39 @@ export class CompilationProcessor { info.circuitName, info.circuitFileName, ); + const errorsFilePath: string = getNormalizedFullPath(info.tempArtifactsPath, "errors.log"); fsExtra.mkdirSync(info.tempArtifactsPath, { recursive: true }); const compileConfig: CompileConfig = { circuitFullPath: info.resolvedFile.absolutePath, artifactsFullPath: info.tempArtifactsPath, + errorFileFullPath: errorsFilePath, linkLibraries: this._getLinkLibraries(), compileFlags: this._config.compileFlags, - quiet: !this._verbose, + quiet: this._config.quiet, }; - await compiler.compile(compileConfig); - await wasmCompiler.generateAST(compileConfig); + try { + await compiler.compile(compileConfig); + await wasmCompiler.generateAST(compileConfig); + } catch (error) { + Reporter!.reportCircuitCompilationFail(spinnerId, info.circuitName, info.circuitFileName); + + if (!(error instanceof HardhatZKitError)) { + throw error; + } + + let internalMessageError: string = ""; + + if (fsExtra.existsSync(errorsFilePath)) { + internalMessageError = `\nWith internal error: ${fsExtra.readFileSync(errorsFilePath, "utf-8")}`; + } + + throw new HardhatZKitError(`${error.message}${internalMessageError}`); + } finally { + fsExtra.rmSync(errorsFilePath, { force: true }); + } if (info.circuitFileName !== info.circuitName) { renameFilesRecursively(info.tempArtifactsPath, info.circuitFileName, info.circuitName); diff --git a/src/core/compile/TypeGenerationProcessor.ts b/src/core/compile/TypeGenerationProcessor.ts index 8dceba0..424c9f1 100644 --- a/src/core/compile/TypeGenerationProcessor.ts +++ b/src/core/compile/TypeGenerationProcessor.ts @@ -1,6 +1,6 @@ import { HardhatRuntimeEnvironment } from "hardhat/types"; -import { CircuitTypesGenerator } from "@solarity/zktype"; +import { CircuitTypesGenerator, ASTParserError, ErrorObj } from "@solarity/zktype"; import { Reporter } from "../../reporter"; import { HardhatZKitError } from "../../errors"; @@ -60,6 +60,22 @@ export class TypeGenerationProcessor { }, ]); - await typesGenerator.generateTypes(); + const warnings: ErrorObj[] = await typesGenerator.generateTypes(); + + Reporter!.reportTypesGenerationWarnings(warnings); + + Reporter!.verboseLog("type-generation-processor", "Types generation warnings: %O", [ + warnings.map((warning) => { + if (!warning) { + return; + } + + if (warning instanceof ASTParserError) { + return JSON.stringify(warning.error); + } + + return warning.message; + }), + ]); } } diff --git a/src/core/compiler/CircomCompiler.ts b/src/core/compiler/CircomCompiler.ts index 127bdc8..3432731 100644 --- a/src/core/compiler/CircomCompiler.ts +++ b/src/core/compiler/CircomCompiler.ts @@ -4,6 +4,7 @@ import { HardhatZKitError } from "../../errors"; import { execCall } from "../../utils/utils"; import { ICircomCompiler, IWASMCircomCompiler, CompileConfig, BaseCompileConfig } from "../../types/core"; +import { MAGIC_DESCRIPTOR } from "../../constants"; // eslint-disable-next-line const { Context, CircomRunner, bindings } = require("@distributedlab/circom2"); @@ -39,12 +40,6 @@ export class NativeCircomCompiler extends BaseCircomCompiler { try { await execCall("circom", compilationArgs); } catch (err) { - if (config.quiet) { - throw new HardhatZKitError( - "Compilation failed with an unknown error. Use '--verbose' hardhat flag to see the compilation error.", - ); - } - throw new HardhatZKitError(`Compilation failed.\n${err}`); } } @@ -56,36 +51,30 @@ export class WASMCircomCompiler extends BaseCircomCompiler implements IWASMCirco } public async compile(config: CompileConfig) { + const errorFileDescriptor: number = fs.openSync(config.errorFileFullPath, "w"); const compilationArgs: string[] = this.getCompilationArgs(config); - const circomRunner: typeof CircomRunner = this._getCircomRunner(compilationArgs, config.quiet); + const circomRunner: typeof CircomRunner = this._getCircomRunner(compilationArgs, config.quiet, errorFileDescriptor); try { await circomRunner.execute(this._compiler); } catch (err) { - if (config.quiet) { - throw new HardhatZKitError( - "Compilation failed with an unknown error. Use '--verbose' hardhat flag to see the compilation error.", - ); - } - throw new HardhatZKitError(`Compilation failed.\n${err}`); + } finally { + fs.closeSync(errorFileDescriptor); } } public async generateAST(config: BaseCompileConfig) { + const errorFileDescriptor: number = fs.openSync(config.errorFileFullPath, "w"); const generationArgs: string[] = this.getASTGenerationArgs(config); - const circomRunner: typeof CircomRunner = this._getCircomRunner(generationArgs, config.quiet); + const circomRunner: typeof CircomRunner = this._getCircomRunner(generationArgs, config.quiet, errorFileDescriptor); try { await circomRunner.execute(this._compiler); } catch (err) { - if (config.quiet) { - throw new HardhatZKitError( - "AST generation failed with an unknown error. Use '--verbose' hardhat flag to see the generation error.", - ); - } - throw new HardhatZKitError(`AST generation failed.\n${err}`); + } finally { + fs.closeSync(errorFileDescriptor); } } @@ -97,7 +86,7 @@ export class WASMCircomCompiler extends BaseCircomCompiler implements IWASMCirco return args; } - private _getCircomRunner(callArgs: string[], quiet: boolean): typeof CircomRunner { + private _getCircomRunner(callArgs: string[], quiet: boolean, errDescriptor: number): typeof CircomRunner { return new CircomRunner({ args: callArgs, preopens: { "/": "/" }, @@ -108,7 +97,7 @@ export class WASMCircomCompiler extends BaseCircomCompiler implements IWASMCirco }, fs, }, - quiet, + descriptors: { stdout: MAGIC_DESCRIPTOR, stderr: quiet ? MAGIC_DESCRIPTOR : errDescriptor }, }); } } diff --git a/src/errors.ts b/src/errors.ts index cd9b8f0..a1e2a9e 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -5,5 +5,7 @@ import { PLUGIN_NAME } from "./constants"; export class HardhatZKitError extends NomicLabsHardhatPluginError { constructor(message: string, parent?: Error) { super(PLUGIN_NAME, message, parent); + + Object.setPrototypeOf(this, HardhatZKitError.prototype); } } diff --git a/src/index.ts b/src/index.ts index c90a928..45ed621 100644 --- a/src/index.ts +++ b/src/index.ts @@ -44,7 +44,7 @@ import { CIRCUITS_COMPILE_CACHE_FILENAME, CIRCUITS_SETUP_CACHE_FILENAME, COMPILE import { getNormalizedFullPath } from "./utils/path-utils"; import { - CompileShallowTaskConfig, + MakeTaskConfig, CompileTaskConfig, GenerateVerifiersTaskConfig, GetCircuitZKitConfig, @@ -70,10 +70,7 @@ extendEnvironment((hre) => { }); }); -const compileShallow: ActionType = async ( - taskArgs: CompileShallowTaskConfig, - env: HardhatRuntimeEnvironment, -) => { +const compile: ActionType = async (taskArgs: CompileTaskConfig, env: HardhatRuntimeEnvironment) => { const circuitsCompileCacheFullPath: string = getNormalizedFullPath( env.config.paths.cache, CIRCUITS_COMPILE_CACHE_FILENAME, @@ -95,7 +92,7 @@ const compileShallow: ActionType = async ( const compileFlags: CompileFlags = { r1cs: true, wasm: true, - sym: taskArgs.sym || env.config.zkit.compilationSettings.sym, + sym: true, json: taskArgs.json || env.config.zkit.compilationSettings.json, c: taskArgs.c || env.config.zkit.compilationSettings.c, }; @@ -113,6 +110,7 @@ const compileShallow: ActionType = async ( { compilerVersion: COMPILER_VERSION, compileFlags, + quiet: taskArgs.quiet || env.config.zkit.quiet, }, env.zkit.circuitArtifacts, env, @@ -192,7 +190,7 @@ const setup: ActionType = async (taskArgs: SetupTaskConfig, env await CircuitsSetupCache!.writeToFile(circuitsSetupCacheFullPath); }; -const make: ActionType = async (taskArgs: CompileTaskConfig, env: HardhatRuntimeEnvironment) => { +const make: ActionType = async (taskArgs: MakeTaskConfig, env: HardhatRuntimeEnvironment) => { await env.run(TASK_CIRCUITS_COMPILE, taskArgs); await env.run(TASK_CIRCUITS_SETUP, { force: taskArgs.force, quiet: taskArgs.quiet }); }; @@ -313,7 +311,6 @@ task(TASK_CLEAN).setAction(async (_taskArgs: any, env: HardhatRuntimeEnvironment }); task(TASK_CIRCUITS_MAKE, "Compile Circom circuits and generate all necessary artifacts") - .addFlag("sym", "Outputs witness in sym file in the compilation artifacts directory.") .addFlag("json", "Outputs constraints in json file in the compilation artifacts directory.") .addFlag("c", "Enables the generation of cpp files in the compilation artifacts directory.") .addFlag("force", "Force compilation ignoring cache.") @@ -321,12 +318,11 @@ task(TASK_CIRCUITS_MAKE, "Compile Circom circuits and generate all necessary art .setAction(make); task(TASK_CIRCUITS_COMPILE, "Compile Circom circuits") - .addFlag("sym", "Outputs witness in sym file in the compilation artifacts directory.") .addFlag("json", "Outputs constraints in json file in the compilation artifacts directory.") .addFlag("c", "Enables the generation of cpp files in the compilation artifacts directory.") .addFlag("force", "Force compilation ignoring cache.") .addFlag("quiet", "Suppresses logs during the compilation process.") - .setAction(compileShallow); + .setAction(compile); task(TASK_CIRCUITS_SETUP, "Create ZKey and Vkey files for compiled circuits") .addFlag("force", "Force compilation ignoring cache.") diff --git a/src/reporter/Reporter.ts b/src/reporter/Reporter.ts index a51b2a9..486fdfd 100644 --- a/src/reporter/Reporter.ts +++ b/src/reporter/Reporter.ts @@ -4,10 +4,11 @@ import debug from "debug"; import chalk from "chalk"; import CliTable3 from "cli-table3"; -import { ResolvedFile } from "hardhat/types"; import { emoji } from "hardhat/internal/cli/emoji"; import { pluralize } from "hardhat/internal/util/strings"; +import { ASTParserError, ErrorObj } from "@solarity/zktype"; + import { SpinnerProcessor } from "./SpinnerProcessor"; import { ProgressBarProcessor } from "./ProgressBarProcessor"; import { HardhatZKitError } from "../errors"; @@ -29,31 +30,28 @@ class BaseReporter { this._quiet = newValue; } - public reportCircuitListToCompile( - allResolvedFilesToCompile: ResolvedFile[], - filteredResolvedFilesToCompile: ResolvedFile[], - ) { + public reportCircuitListToCompile(filteredSourceNames: string[], filteredSourceNamesToCompile: string[]) { if (this.isQuiet()) return; - if (filteredResolvedFilesToCompile.length > 0) { + if (filteredSourceNamesToCompile.length > 0) { let filesToCompileMessage: string = `\n${chalk.bold("Circuits to compile:")}\n`; - for (const file of filteredResolvedFilesToCompile) { - filesToCompileMessage += `\n${chalk.green(">")} ${chalk.italic(file.sourceName)}`; + for (const sourceName of filteredSourceNamesToCompile) { + filesToCompileMessage += `\n${chalk.green(">")} ${chalk.italic(sourceName)}`; } console.log(filesToCompileMessage); } - const skippedFiles: ResolvedFile[] = allResolvedFilesToCompile.filter( - (file: ResolvedFile) => !filteredResolvedFilesToCompile.includes(file), + const skippedSourceNames: string[] = filteredSourceNames.filter( + (sourceName: string) => !filteredSourceNamesToCompile.includes(sourceName), ); - if (skippedFiles.length > 0) { + if (skippedSourceNames.length > 0) { let skippedFilesMessage: string = `\n${chalk.bold("Compilation skipped for:")}\n`; - for (const file of skippedFiles) { - skippedFilesMessage += `\n${chalk.yellow(">")} ${chalk.italic.grey(file.sourceName)}`; + for (const sourceName of skippedSourceNames) { + skippedFilesMessage += `\n${chalk.yellow(">")} ${chalk.italic.grey(sourceName)}`; } console.log(skippedFilesMessage); @@ -100,6 +98,20 @@ class BaseReporter { ); } + public reportCircuitCompilationFail(spinnerId: string | null, circuitName: string, circuitFileName: string) { + if (this.isQuiet() || !spinnerId) return; + + const fileNameMessage: string = circuitName === circuitFileName ? "" : chalk.grey(` (${circuitFileName}.circom)`); + const compilationTimeMessage: string = this._getSpinnerWorkingTimeMessage( + this._spinnerProcessor.getWorkingTime(spinnerId), + ); + + this._spinnerProcessor.failSpinner( + spinnerId, + `Failed to compile ${chalk.italic(circuitName)}${fileNameMessage} ${compilationTimeMessage}\n`, + ); + } + public reportCompilationResult(compilationInfoArr: CompilationInfo[]) { if (this.isQuiet()) return; @@ -122,6 +134,30 @@ class BaseReporter { console.log(output); } + public reportTypesGenerationWarnings(warnings: ErrorObj[]) { + if (this.isQuiet()) return; + + if (warnings.length > 0) { + let warningsMessage: string = `\n${chalk.bold("Failed to generate types for the following circuits:")}\n`; + + for (const warning of warnings) { + if (!warning) { + continue; + } + + let circuitNameMessage = ""; + + if (warning instanceof ASTParserError) { + circuitNameMessage = `${chalk.bold(warning.error.circuitFullNames)}: `; + } + + warningsMessage += `\n${chalk.yellow("⚠")} ${circuitNameMessage}${warning.message}`; + } + + console.log(warningsMessage); + } + } + public reportCircuitListToSetup( allCircuitsSetupInfo: CircuitSetupInfo[], filteredCircuitsSetupInfo: CircuitSetupInfo[], @@ -300,16 +336,6 @@ class BaseReporter { console.log(output); } - private _getFileSizeInMB(filePath: string | undefined): string { - if (!filePath) { - throw new HardhatZKitError("File path is undefined. Unable to get file size."); - } - - const fileSize: number = fs.statSync(filePath).size; - - return (fileSize / BYTES_IN_MB).toFixed(3); - } - public reportTypesGenerationHeaderWithSpinner(): string | null { if (this.isQuiet()) return null; @@ -427,6 +453,16 @@ class BaseReporter { return workingTime ? chalk.grey(`(${workingTime} s)`) : ""; } + private _getFileSizeInMB(filePath: string | undefined): string { + if (!filePath) { + throw new HardhatZKitError("File path is undefined. Unable to get file size."); + } + + const fileSize: number = fs.statSync(filePath).size; + + return (fileSize / BYTES_IN_MB).toFixed(3); + } + private _startSpinner(circuitName: string, spinnerIdSuffix: string, spinnerText: string): string | null { if (this.isQuiet()) return null; diff --git a/src/reporter/SpinnerProcessor.ts b/src/reporter/SpinnerProcessor.ts index 89269ca..591a396 100644 --- a/src/reporter/SpinnerProcessor.ts +++ b/src/reporter/SpinnerProcessor.ts @@ -21,6 +21,16 @@ export class SpinnerProcessor { this._idToSpinnerData.delete(spinnerId); } + public failSpinner(spinnerId: string, failMessage: string) { + const spinnerData = this._idToSpinnerData.get(spinnerId); + + if (!spinnerData) return; + + spinnerData.spinner.fail(failMessage); + + this._idToSpinnerData.delete(spinnerId); + } + public getWorkingTime(spinnerId: string, fractionDigits: number = 2): string | undefined { const spinnerData = this._idToSpinnerData.get(spinnerId); diff --git a/src/types/core/compile/compilation-processor.ts b/src/types/core/compile/compilation-processor.ts index f619573..d48e176 100644 --- a/src/types/core/compile/compilation-processor.ts +++ b/src/types/core/compile/compilation-processor.ts @@ -6,6 +6,7 @@ import { CompileFlags } from "../compiler/circom-compiler"; export type CompilationProccessorConfig = { compilerVersion: CompilerVersion; compileFlags: CompileFlags; + quiet: boolean; }; export type CompilationInfo = { diff --git a/src/types/core/compiler/circom-compiler.ts b/src/types/core/compiler/circom-compiler.ts index 130bdef..c075616 100644 --- a/src/types/core/compiler/circom-compiler.ts +++ b/src/types/core/compiler/circom-compiler.ts @@ -9,6 +9,7 @@ export type CompileFlags = { export type BaseCompileConfig = { circuitFullPath: string; artifactsFullPath: string; + errorFileFullPath: string; linkLibraries: string[]; quiet: boolean; }; diff --git a/src/types/tasks.ts b/src/types/tasks.ts index b4e9745..47e5442 100644 --- a/src/types/tasks.ts +++ b/src/types/tasks.ts @@ -1,8 +1,7 @@ import { VerifierTemplateType } from "@solarity/zkit"; -export type CompileShallowTaskConfig = { +export type MakeTaskConfig = { force: boolean; - sym: boolean; json: boolean; c: boolean; quiet: boolean; @@ -15,7 +14,6 @@ export type SetupTaskConfig = { export type CompileTaskConfig = { force: boolean; - sym: boolean; json: boolean; c: boolean; quiet: boolean; diff --git a/src/types/zkit-config.ts b/src/types/zkit-config.ts index f882bb4..498df4b 100644 --- a/src/types/zkit-config.ts +++ b/src/types/zkit-config.ts @@ -18,7 +18,6 @@ export type CircuitTypesSettings = { export type CompilationSettings = FileFilterSettings & { artifactsDir: string; c: boolean; - sym: boolean; json: boolean; }; diff --git a/test/integration/setup/config.ts b/test/integration/setup/config.ts index a2239b7..5c1a570 100644 --- a/test/integration/setup/config.ts +++ b/test/integration/setup/config.ts @@ -27,7 +27,6 @@ describe("config", () => { skipFiles: ["vendor"], c: false, json: false, - sym: false, }, setupSettings: { contributionSettings: { @@ -71,7 +70,6 @@ describe("config", () => { skipFiles: [], c: false, json: false, - sym: false, }, setupSettings: { contributionSettings: { diff --git a/test/unit/cache/circuits-compile-cache.test.ts b/test/unit/cache/circuits-compile-cache.test.ts index 4d405e9..adaf5cb 100644 --- a/test/unit/cache/circuits-compile-cache.test.ts +++ b/test/unit/cache/circuits-compile-cache.test.ts @@ -16,9 +16,9 @@ describe("CircuitsCompileCache", () => { const defaultCompileFlags: CompileFlags = { r1cs: true, wasm: true, + sym: true, c: false, json: false, - sym: false, }; async function getCacheEntry( diff --git a/test/unit/compile/core/circom-compiler-factory.test.ts b/test/unit/compile/core/circom-compiler-factory.test.ts index dd67dcc..5f04a5c 100644 --- a/test/unit/compile/core/circom-compiler-factory.test.ts +++ b/test/unit/compile/core/circom-compiler-factory.test.ts @@ -28,6 +28,7 @@ describe("CircomCompilerFactory", () => { this.hre.config.paths.root, "zkit/artifacts/test/mul2.circom", ); + const errorFileFullPath: string = getNormalizedFullPath(artifactsFullPath, "errors.log"); const typesDir: string = getNormalizedFullPath(this.hre.config.paths.root, "generated-types/zkit"); fsExtra.rmSync(artifactsFullPath, { recursive: true, force: true }); @@ -36,11 +37,14 @@ describe("CircomCompilerFactory", () => { await compiler.compile({ circuitFullPath, artifactsFullPath, + errorFileFullPath, linkLibraries: [], compileFlags: { ...defaultCompileFlags, sym: true }, quiet: true, }); + fsExtra.rmSync(errorFileFullPath, { force: true }); + expect(fsExtra.readdirSync(artifactsFullPath)).to.be.deep.eq(["mul2.r1cs", "mul2.sym", "mul2_js"]); fsExtra.rmSync(typesDir, { recursive: true, force: true }); diff --git a/test/unit/compile/core/circom-compiler.test.ts b/test/unit/compile/core/circom-compiler.test.ts index 8b91e55..be98c22 100644 --- a/test/unit/compile/core/circom-compiler.test.ts +++ b/test/unit/compile/core/circom-compiler.test.ts @@ -35,6 +35,7 @@ describe("WASMCircomCompiler", () => { this.hre.config.paths.root, "zkit/artifacts/test/mul2.circom", ); + const errorFileFullPath: string = getNormalizedFullPath(artifactsFullPath, "errors.log"); const typesDir: string = getNormalizedFullPath(this.hre.config.paths.root, "generated-types/zkit"); fsExtra.rmSync(artifactsFullPath, { recursive: true, force: true }); @@ -43,11 +44,14 @@ describe("WASMCircomCompiler", () => { await circomCompiler.compile({ circuitFullPath, artifactsFullPath, + errorFileFullPath, linkLibraries: [], compileFlags: { ...defaultCompileFlags, sym: true }, quiet: true, }); + fsExtra.rmSync(errorFileFullPath, { force: true }); + expect(fsExtra.readdirSync(artifactsFullPath)).to.be.deep.eq(["mul2.r1cs", "mul2.sym", "mul2_js"]); fsExtra.rmSync(typesDir, { recursive: true, force: true }); @@ -62,14 +66,15 @@ describe("WASMCircomCompiler", () => { this.hre.config.paths.root, "zkit/artifacts/test/mul2.circom", ); + const errorFileFullPath: string = getNormalizedFullPath(artifactsFullPath, "errors.log"); - const reason: string = - "Compilation failed with an unknown error. Use '--verbose' hardhat flag to see the compilation error."; + const reason: string = "Compilation failed.\nHardhatZKitError: Error during compiler execution. Exit code: 1."; await expect( circomCompiler.compile({ circuitFullPath, artifactsFullPath, + errorFileFullPath, linkLibraries: [], compileFlags: defaultCompileFlags, quiet: true, @@ -86,6 +91,7 @@ describe("WASMCircomCompiler", () => { this.hre.config.paths.root, "zkit/artifacts/test/mul2.circom", ); + const errorFileFullPath: string = getNormalizedFullPath(artifactsFullPath, "errors.log"); fsExtra.mkdirSync(artifactsFullPath, { recursive: true }); @@ -95,6 +101,7 @@ describe("WASMCircomCompiler", () => { circomCompiler.compile({ circuitFullPath, artifactsFullPath, + errorFileFullPath, linkLibraries: [], compileFlags: defaultCompileFlags, quiet: false, @@ -122,6 +129,7 @@ describe("WASMCircomCompiler", () => { this.hre.config.paths.root, "zkit/artifacts/test/hash2.circom", ); + const errorFileFullPath: string = getNormalizedFullPath(artifactsFullPath, "errors.log"); const typesDir: string = getNormalizedFullPath(this.hre.config.paths.root, "generated-types/zkit"); const nodeModulesPath: string = getNormalizedFullPath(getProjectRootPath(), NODE_MODULES); @@ -131,11 +139,14 @@ describe("WASMCircomCompiler", () => { await circomCompiler.compile({ circuitFullPath, artifactsFullPath, + errorFileFullPath, linkLibraries: [nodeModulesPath], compileFlags: { ...defaultCompileFlags, sym: true }, quiet: true, }); + fsExtra.rmSync(errorFileFullPath, { force: true }); + expect(fsExtra.readdirSync(artifactsFullPath)).to.be.deep.eq(["hash2.r1cs", "hash2.sym", "hash2_js"]); fsExtra.rmSync(typesDir, { recursive: true, force: true }); @@ -154,11 +165,13 @@ describe("WASMCircomCompiler", () => { it("should return correct compilation args", async function () { const circuitFullPath: string = "circuit-path"; const artifactsFullPath: string = "artifacts-path"; + const errorFileFullPath: string = "errors-path"; let expectedArgs: string[] = [circuitFullPath, "-o", artifactsFullPath, "--r1cs", "--wasm"]; let args: string[] = circomCompiler.getCompilationArgs({ circuitFullPath, artifactsFullPath, + errorFileFullPath, linkLibraries: [], compileFlags: defaultCompileFlags, quiet: true, @@ -170,6 +183,7 @@ describe("WASMCircomCompiler", () => { args = circomCompiler.getCompilationArgs({ circuitFullPath, artifactsFullPath, + errorFileFullPath, linkLibraries: [], compileFlags: { ...defaultCompileFlags, c: true, sym: true }, quiet: true,