diff --git a/.github/actions/docs/build-status/script.sh b/.github/actions/docs/build-status/script.sh index a8d31ff4f4b..0b282557cf2 100755 --- a/.github/actions/docs/build-status/script.sh +++ b/.github/actions/docs/build-status/script.sh @@ -18,11 +18,11 @@ while [[ "$DEPLOY_STATUS" != "ready" && $COUNT -lt $MAX_RETRIES ]]; do exit 0 elif [[ "$DEPLOY_STATUS" == "error" ]]; then echo "deploy_status=failure" >> $GITHUB_OUTPUT - exit 0 + exit 1 fi echo "Deploy still running. Retrying..." done echo "deploy_status=failure" >> $GITHUB_OUTPUT -exit 0 +exit 1 diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 784988b0ce8..e81ede7199d 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,37 +1,23 @@ # Description - - - ## Problem\* - - Resolves ## Summary\* - - - -## Documentation - -- [ ] This PR requires documentation updates when merged. - - - [ ] I will submit a noir-lang/docs PR. - - +## Additional Context - - [ ] I will request for and support Dev Rel's help in documenting this PR. - - -## Additional Context +## Documentation\* - +Check one: +- [ ] No documentation needed. +- [ ] Documentation included in this PR. +- [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* diff --git a/.github/workflows/auto-pr-rebuild-script.yml b/.github/workflows/auto-pr-rebuild-script.yml index 5e46e46ed4c..7b551b0aece 100644 --- a/.github/workflows/auto-pr-rebuild-script.yml +++ b/.github/workflows/auto-pr-rebuild-script.yml @@ -1,13 +1,40 @@ name: Rebuild ACIR artifacts on: + pull_request: push: branches: - master jobs: + check-artifacts-requested: + name: Check if artifacts should be published + runs-on: ubuntu-22.04 + outputs: + publish: ${{ steps.check.outputs.publish }} + + steps: + - name: Check if artifacts should be published + id: check + run: | + if [ ${{ github.ref_name }} == "master" ]; then + # Always publish on master + echo "publish=true" >> "$GITHUB_OUTPUT" + else + # Only publish on PRs if label is set + HAS_ARTIFACT_LABEL=$(gh pr view $PR --repo $REPO_URL --json labels | jq '.labels | any(.name == "publish-acir")') + echo "publish=$HAS_ARTIFACT_LABEL" >> "$GITHUB_OUTPUT" + fi + env: + GH_TOKEN: ${{ github.token }} + PR: ${{ github.event.pull_request.number }} + REPO_URL: ${{ github.repositoryUrl }} + build-nargo: + name: Build nargo binary runs-on: ubuntu-22.04 + needs: [check-artifacts-requested] + if: ${{ needs.check-artifacts-requested.outputs.publish == true }} strategy: matrix: target: [x86_64-unknown-linux-gnu] @@ -42,6 +69,7 @@ jobs: retention-days: 3 auto-pr-rebuild-script: + name: Rebuild ACIR artifacts needs: [build-nargo] runs-on: ubuntu-latest @@ -71,10 +99,19 @@ jobs: chmod +x ./rebuild.sh ./rebuild.sh + - name: Upload ACIR artifacts + uses: actions/upload-artifact@v3 + with: + name: acir-artifacts + path: ./tooling/nargo_cli/tests/acir_artifacts + retention-days: 10 + - name: Check for changes in acir_artifacts directory id: check_changes + if: ${{ github.ref_name }} == "master" run: | git diff --quiet tooling/nargo_cli/tests/acir_artifacts/ || echo "::set-output name=changes::true" + - name: Create or Update PR if: steps.check_changes.outputs.changes == 'true' diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index af3565ba61d..23ae0e409e9 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -100,9 +100,11 @@ jobs: site-id: ${{ secrets.NETLIFY_SITE_ID }} continue-on-error: true + - name: Debugging - print deploy_status run: echo "${{ steps.check_deploy_status.outputs.deploy_status }}" + - name: Change PR Comment for Successful Deployment if: steps.check_deploy_status.outputs.deploy_status == 'success' uses: mshick/add-pr-comment@v2 @@ -134,3 +136,7 @@ jobs: Deployment didn't succeed. Please check logs below and resolve the issue 🧐 [![Netlify Status](https://api.netlify.com/api/v1/badges/${{ secrets.NETLIFY_SITE_ID }}/deploy-status?branch=${{ github.head_ref || github.ref }})](https://app.netlify.com/sites/noir-docs-v2/deploys) + + - name: Fail the workflow if deployment failed + if: steps.check_deploy_status.outputs.deploy_status == 'failure' + run: exit 1 diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml new file mode 100644 index 00000000000..2de546adec7 --- /dev/null +++ b/.github/workflows/publish-docs.yml @@ -0,0 +1,49 @@ +name: Publish Docs + +on: + workflow_dispatch: + inputs: + tag: + description: The tag to build Docs for + required: false + +jobs: + publish-docs: + runs-on: ubuntu-latest + if: ${{ inputs.tag != '' }} + permissions: + pull-requests: write + steps: + - name: Checkout sources + uses: actions/checkout@v4 + with: + ref: ${{ inputs.tag }} + + - name: Setup Node.js + uses: actions/setup-node@v2 + with: + node-version: '18' + + - name: Install Yarn dependencies + uses: ./.github/actions/setup + + - name: Cut a new version + working-directory: ./docs + run: yarn docusaurus docs:version ${{ inputs.tag }} + + - name: Build docs + run: yarn workspace docs build + + - name: Deploy to Netlify + uses: nwtgck/actions-netlify@v2.1 + with: + publish-dir: './docs/build' + production-branch: master + production-deploy: false # TODO change to true + github-token: ${{ secrets.GITHUB_TOKEN }} + enable-github-deployment: false + deploy-message: "Deploy from GitHub Actions for tag ${{ inputs.tag }}" + env: + NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} + NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }} + timeout-minutes: 1 diff --git a/.github/workflows/recrawler.yml b/.github/workflows/recrawler.yml new file mode 100644 index 00000000000..47f042bcd87 --- /dev/null +++ b/.github/workflows/recrawler.yml @@ -0,0 +1,22 @@ +# name: Algolia Recrawl +# on: +# push: +# branches: [ master ] +# workflow_dispatch: + +# jobs: +# algolia_recrawl: +# name: Algolia Recrawl +# runs-on: ubuntu-latest +# steps: +# - name: Algolia crawler creation and crawl +# uses: algolia/algoliasearch-crawler-github-actions@v1.1.0 +# id: algolia_crawler +# with: +# crawler-user-id: ${{ secrets.CRAWLER_USER_ID }} +# crawler-api-key: ${{ secrets.CRAWLER_API_KEY }} +# algolia-app-id: ${{ secrets.ALGOLIA_APP_ID }} +# algolia-api-key: ${{ secrets.ALGOLIA_API_KEY }} +# site-url: 'https://noir-lang.org/' +# crawler-name: noir-lang +# override-config: false diff --git a/.github/workflows/test-acvm-js.yml b/.github/workflows/test-acvm-js.yml index 8a469f6ff1e..c69f4995198 100644 --- a/.github/workflows/test-acvm-js.yml +++ b/.github/workflows/test-acvm-js.yml @@ -1,6 +1,11 @@ name: Test acvm_js -on: [push, pull_request] +on: + pull_request: + merge_group: + push: + branches: + - master # This will cancel previous runs when a branch or PR is updated concurrency: diff --git a/.github/workflows/test-noir_wasm.yml b/.github/workflows/test-noir_wasm.yml index 86362ef7ceb..eea4074e00a 100644 --- a/.github/workflows/test-noir_wasm.yml +++ b/.github/workflows/test-noir_wasm.yml @@ -101,12 +101,16 @@ jobs: name: nargo path: ./nargo - - name: Compile test program with Nargo CLI - working-directory: ./compiler/wasm/noir-script + - name: Compile fixtures with Nargo CLI + working-directory: ./compiler/wasm/fixtures run: | nargo_binary=${{ github.workspace }}/nargo/nargo chmod +x $nargo_binary - $nargo_binary compile + for dir in $(ls -d */); do + pushd $dir/noir-script + $nargo_binary compile + popd + done - name: Install Yarn dependencies uses: ./.github/actions/setup diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 55950a42d1e..3a52db26d13 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { - ".": "0.17.0", - "acvm-repo": "0.29.0" + ".": "0.18.0", + "acvm-repo": "0.30.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 5fce9e43321..68cd1decb44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,35 @@ # Changelog +## [0.18.0](https://github.com/noir-lang/noir/compare/v0.17.0...v0.18.0) (2023-10-25) + + +### ⚠ BREAKING CHANGES + +* expose pedersen hash in acir and bb solver ([#3269](https://github.com/noir-lang/noir/issues/3269)) +* Switch to new pedersen implementation ([#3151](https://github.com/noir-lang/noir/issues/3151)) + +### Features + +* Add crate for pub modifier ([#3271](https://github.com/noir-lang/noir/issues/3271)) ([e7a1a1a](https://github.com/noir-lang/noir/commit/e7a1a1a4b42b6b72c16f2204e33af80dbabba6b5)) +* Cache debug artifacts ([#3133](https://github.com/noir-lang/noir/issues/3133)) ([c5a6229](https://github.com/noir-lang/noir/commit/c5a622983e4049d82589f185be5e96c63ed6066d)) +* **debugger:** Print limited source code context ([#3217](https://github.com/noir-lang/noir/issues/3217)) ([dcda1c7](https://github.com/noir-lang/noir/commit/dcda1c7aed69ae8f55cd3f680e3cc1ece9de7541)) +* Expose pedersen hash in acir and bb solver ([#3269](https://github.com/noir-lang/noir/issues/3269)) ([0108b6c](https://github.com/noir-lang/noir/commit/0108b6c1e8dc0dfc766ab3c4944deae9354dec36)) +* Implement `bound_constraint_with_offset` in terms of `AcirVar`s ([#3233](https://github.com/noir-lang/noir/issues/3233)) ([8d89cb5](https://github.com/noir-lang/noir/commit/8d89cb59fe710859a96eaed4f988952bd727fb7d)) +* Implement euclidean division and signed division in terms of `AcirVar`s ([#3230](https://github.com/noir-lang/noir/issues/3230)) ([b8b7782](https://github.com/noir-lang/noir/commit/b8b77825410c0e1f95549259a51e2c40de1ec342)) +* Noir-wasm takes dependency graph ([#3213](https://github.com/noir-lang/noir/issues/3213)) ([a2c8ebd](https://github.com/noir-lang/noir/commit/a2c8ebd4a800d7ef042ac9cbe5ee6a837c715634)) +* Replace boolean range constraints with arithmetic opcodes ([#3234](https://github.com/noir-lang/noir/issues/3234)) ([949222c](https://github.com/noir-lang/noir/commit/949222c20d9e65152e3814d02da1c4c41ffc23a5)) +* **stdlib:** Optimize constraint counts in sha256/sha512 ([#3253](https://github.com/noir-lang/noir/issues/3253)) ([d3be552](https://github.com/noir-lang/noir/commit/d3be552149ab375b24b509603fcd7237b374ca5a)) +* Switch to new pedersen implementation ([#3151](https://github.com/noir-lang/noir/issues/3151)) ([35fb3f7](https://github.com/noir-lang/noir/commit/35fb3f7076d52db7ca3bef0a70a3dbccaf82f58d)) + + +### Bug Fixes + +* Add size checks to integer literals ([#3236](https://github.com/noir-lang/noir/issues/3236)) ([7f8fe8c](https://github.com/noir-lang/noir/commit/7f8fe8c88eb2d26ae3a93e2f74430fadc74b4836)) +* Fix lexer error formatting ([#3274](https://github.com/noir-lang/noir/issues/3274)) ([74bd517](https://github.com/noir-lang/noir/commit/74bd517fe7839465ff086ffe622462bed5159006)) +* Impl methods are no longer placed in contracts ([#3255](https://github.com/noir-lang/noir/issues/3255)) ([b673b07](https://github.com/noir-lang/noir/commit/b673b071663d9756d6346954fce7d4ec6e1577dd)) +* Recompile artefacts from a different noir version ([#3248](https://github.com/noir-lang/noir/issues/3248)) ([7347b27](https://github.com/noir-lang/noir/commit/7347b2742a5ad38d3d252e657810d061bab83e24)) +* Show println output before an error occurs in `nargo execute` ([#3211](https://github.com/noir-lang/noir/issues/3211)) ([2f0b80d](https://github.com/noir-lang/noir/commit/2f0b80dda8401ce8962c857dbcd9548e7fdde4aa)) + ## [0.17.0](https://github.com/noir-lang/noir/compare/v0.16.0...v0.17.0) (2023-10-20) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bc2acd40829..9cbbeeb677f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -187,7 +187,7 @@ In the docs folder, you'll find the current, unreleased version, which we call ` While the versioning is intended to be managed by the core maintainers, we feel it's important for external contributors to understand why and how is it maintained. To bump to a new version, run the following command, replacing with the intended version: ```bash -npm run docusaurus docs:version +yarn docusaurus docs:version ``` This should create a new version by copying the docs folder and the sidebars.js file to the relevant folders, as well as adding this version to versions.json. diff --git a/Cargo.lock b/Cargo.lock index 57b1e0e97d4..c4e7b19bbcb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,7 +4,7 @@ version = 3 [[package]] name = "acir" -version = "0.29.0" +version = "0.30.0" dependencies = [ "acir_field", "bincode", @@ -22,7 +22,7 @@ dependencies = [ [[package]] name = "acir_field" -version = "0.29.0" +version = "0.30.0" dependencies = [ "ark-bls12-381", "ark-bn254", @@ -36,7 +36,7 @@ dependencies = [ [[package]] name = "acvm" -version = "0.29.0" +version = "0.30.0" dependencies = [ "acir", "acvm_blackbox_solver", @@ -53,7 +53,7 @@ dependencies = [ [[package]] name = "acvm_blackbox_solver" -version = "0.29.0" +version = "0.30.0" dependencies = [ "acir", "blake2", @@ -66,7 +66,7 @@ dependencies = [ [[package]] name = "acvm_js" -version = "0.29.0" +version = "0.30.0" dependencies = [ "acvm", "barretenberg_blackbox_solver", @@ -87,7 +87,7 @@ dependencies = [ [[package]] name = "acvm_stdlib" -version = "0.29.0" +version = "0.30.0" dependencies = [ "acir", ] @@ -217,7 +217,7 @@ checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" [[package]] name = "arena" -version = "0.17.0" +version = "0.18.0" dependencies = [ "generational-arena", ] @@ -449,7 +449,7 @@ dependencies = [ [[package]] name = "barretenberg_blackbox_solver" -version = "0.29.0" +version = "0.30.0" dependencies = [ "acir", "acvm_blackbox_solver", @@ -573,7 +573,7 @@ dependencies = [ [[package]] name = "brillig" -version = "0.29.0" +version = "0.30.0" dependencies = [ "acir_field", "serde", @@ -581,7 +581,7 @@ dependencies = [ [[package]] name = "brillig_vm" -version = "0.29.0" +version = "0.30.0" dependencies = [ "acir", "acvm_blackbox_solver", @@ -1601,7 +1601,7 @@ dependencies = [ [[package]] name = "fm" -version = "0.17.0" +version = "0.18.0" dependencies = [ "codespan-reporting", "iter-extended", @@ -2068,6 +2068,7 @@ dependencies = [ "bitmaps", "rand_core", "rand_xoshiro", + "serde", "sized-chunks", "typenum", "version_check", @@ -2182,7 +2183,7 @@ dependencies = [ [[package]] name = "iter-extended" -version = "0.17.0" +version = "0.18.0" [[package]] name = "itertools" @@ -2401,7 +2402,7 @@ checksum = "7843ec2de400bcbc6a6328c958dc38e5359da6e93e72e37bc5246bf1ae776389" [[package]] name = "nargo" -version = "0.17.0" +version = "0.18.0" dependencies = [ "acvm", "base64", @@ -2421,7 +2422,7 @@ dependencies = [ [[package]] name = "nargo_cli" -version = "0.17.0" +version = "0.18.0" dependencies = [ "acvm", "assert_cmd", @@ -2469,7 +2470,7 @@ dependencies = [ [[package]] name = "nargo_fmt" -version = "0.17.0" +version = "0.18.0" dependencies = [ "bytecount", "noirc_frontend", @@ -2481,7 +2482,7 @@ dependencies = [ [[package]] name = "nargo_toml" -version = "0.17.0" +version = "0.18.0" dependencies = [ "dirs", "fm", @@ -2530,11 +2531,13 @@ dependencies = [ [[package]] name = "noir_debugger" -version = "0.17.0" +version = "0.18.0" dependencies = [ "acvm", + "codespan-reporting", "easy-repl", "nargo", + "noirc_errors", "noirc_printable_type", "owo-colors", "thiserror", @@ -2542,7 +2545,7 @@ dependencies = [ [[package]] name = "noir_lsp" -version = "0.17.0" +version = "0.18.0" dependencies = [ "acvm", "async-lsp", @@ -2564,7 +2567,7 @@ dependencies = [ [[package]] name = "noir_wasm" -version = "0.17.0" +version = "0.18.0" dependencies = [ "acvm", "build-data", @@ -2586,7 +2589,7 @@ dependencies = [ [[package]] name = "noirc_abi" -version = "0.17.0" +version = "0.18.0" dependencies = [ "acvm", "iter-extended", @@ -2603,7 +2606,7 @@ dependencies = [ [[package]] name = "noirc_abi_wasm" -version = "0.17.0" +version = "0.18.0" dependencies = [ "acvm", "build-data", @@ -2620,7 +2623,7 @@ dependencies = [ [[package]] name = "noirc_driver" -version = "0.17.0" +version = "0.18.0" dependencies = [ "acvm", "base64", @@ -2628,6 +2631,7 @@ dependencies = [ "clap", "fm", "fxhash", + "iter-extended", "noirc_abi", "noirc_errors", "noirc_evaluator", @@ -2637,7 +2641,7 @@ dependencies = [ [[package]] name = "noirc_errors" -version = "0.17.0" +version = "0.18.0" dependencies = [ "acvm", "chumsky", @@ -2650,7 +2654,7 @@ dependencies = [ [[package]] name = "noirc_evaluator" -version = "0.17.0" +version = "0.18.0" dependencies = [ "acvm", "fxhash", @@ -2660,12 +2664,13 @@ dependencies = [ "noirc_errors", "noirc_frontend", "num-bigint", + "serde", "thiserror", ] [[package]] name = "noirc_frontend" -version = "0.17.0" +version = "0.18.0" dependencies = [ "acvm", "arena", @@ -2676,6 +2681,7 @@ dependencies = [ "noirc_printable_type", "regex", "rustc-hash", + "serde", "serde_json", "small-ord-set", "smol_str", @@ -2686,7 +2692,7 @@ dependencies = [ [[package]] name = "noirc_printable_type" -version = "0.17.0" +version = "0.18.0" dependencies = [ "acvm", "iter-extended", diff --git a/Cargo.toml b/Cargo.toml index e80de516e72..78b15287060 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,7 @@ resolver = "2" [workspace.package] # x-release-please-start-version -version = "0.17.0" +version = "0.18.0" # x-release-please-end authors = ["The Noir Team "] edition = "2021" @@ -77,7 +77,7 @@ dirs = "4" lsp-types = "0.94" serde = { version = "1.0.136", features = ["derive"] } serde_json = "1.0" -smol_str = "0.1.17" +smol_str = { version = "0.1.17", features = ["serde"] } thiserror = "1.0.21" toml = "0.7.2" tower = "0.4" diff --git a/acvm-repo/CHANGELOG.md b/acvm-repo/CHANGELOG.md index fd3ad6223e0..b187addf7b7 100644 --- a/acvm-repo/CHANGELOG.md +++ b/acvm-repo/CHANGELOG.md @@ -5,6 +5,45 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.30.0](https://github.com/noir-lang/noir/compare/v0.29.0...v0.30.0) (2023-10-25) + + +### ⚠ BREAKING CHANGES + +* expose pedersen hash in acir and bb solver ([#3269](https://github.com/noir-lang/noir/issues/3269)) +* Switch to new pedersen implementation ([#3151](https://github.com/noir-lang/noir/issues/3151)) +* Pass ACIR to ACVM by reference rather than passing ownership ([#2872](https://github.com/noir-lang/noir/issues/2872)) +* **wasm:** improve and simplify wasm compiler interface ([#2976](https://github.com/noir-lang/noir/issues/2976)) +* Maintain shape of foreign call arguments ([#2935](https://github.com/noir-lang/noir/issues/2935)) + +### Features + +* **acvm_js:** Export black box solver functions ([#2812](https://github.com/noir-lang/noir/issues/2812)) ([da8a98e](https://github.com/noir-lang/noir/commit/da8a98ed312fe69cb0bdb8f9d0a70ee7a981398f)) +* **acvm:** Separate ACVM optimizations and transformations ([#2979](https://github.com/noir-lang/noir/issues/2979)) ([5865d1a](https://github.com/noir-lang/noir/commit/5865d1a1bca16e1853663c71f893ff81fa3f7185)) +* Add ACIR serializer C++ codegen ([#2961](https://github.com/noir-lang/noir/issues/2961)) ([7556982](https://github.com/noir-lang/noir/commit/7556982dbebe25eaa17240abbe270b771b55de45)) +* Add conditional compilation of methods based on the underlying field being used ([#3045](https://github.com/noir-lang/noir/issues/3045)) ([2e008e2](https://github.com/noir-lang/noir/commit/2e008e2438795bbc41b0641e830378b76bf2e194)) +* Expose pedersen hash in acir and bb solver ([#3269](https://github.com/noir-lang/noir/issues/3269)) ([0108b6c](https://github.com/noir-lang/noir/commit/0108b6c1e8dc0dfc766ab3c4944deae9354dec36)) +* Implement euclidean division and signed division in terms of `AcirVar`s ([#3230](https://github.com/noir-lang/noir/issues/3230)) ([b8b7782](https://github.com/noir-lang/noir/commit/b8b77825410c0e1f95549259a51e2c40de1ec342)) +* Maintain shape of foreign call arguments ([#2935](https://github.com/noir-lang/noir/issues/2935)) ([f7869e6](https://github.com/noir-lang/noir/commit/f7869e6fb492b617e776e538ac4babfa56261d26)) +* Pass ACIR to ACVM by reference rather than passing ownership ([#2872](https://github.com/noir-lang/noir/issues/2872)) ([b3a9c34](https://github.com/noir-lang/noir/commit/b3a9c343993ce3207de62106bda6cb2b2ef3de50)) +* Pass brillig bytecode to VM by reference ([#3030](https://github.com/noir-lang/noir/issues/3030)) ([4ee290b](https://github.com/noir-lang/noir/commit/4ee290b8b6f75bc1974a5750248570eeca8d244e)) +* Replace boolean range constraints with arithmetic opcodes ([#3234](https://github.com/noir-lang/noir/issues/3234)) ([949222c](https://github.com/noir-lang/noir/commit/949222c20d9e65152e3814d02da1c4c41ffc23a5)) +* Save Brillig execution state in ACVM ([#3026](https://github.com/noir-lang/noir/issues/3026)) ([88682da](https://github.com/noir-lang/noir/commit/88682da87ffc9e26da5c9e4b5a4d8e62a6ee43c6)) +* Solve `fixed_base_scalar_mul` black box functions in rust ([#3153](https://github.com/noir-lang/noir/issues/3153)) ([1c1afbc](https://github.com/noir-lang/noir/commit/1c1afbcddf0b5fdb39f00ad28ae90caf699d1265)) +* Switch to new pedersen implementation ([#3151](https://github.com/noir-lang/noir/issues/3151)) ([35fb3f7](https://github.com/noir-lang/noir/commit/35fb3f7076d52db7ca3bef0a70a3dbccaf82f58d)) +* **wasm:** Improve and simplify wasm compiler interface ([#2976](https://github.com/noir-lang/noir/issues/2976)) ([1b5124b](https://github.com/noir-lang/noir/commit/1b5124bc74f7ac5360db04b34d1b7b2284061fd3)) + + +### Bug Fixes + +* ACIR optimizer should update assertion messages ([#3010](https://github.com/noir-lang/noir/issues/3010)) ([758b6b6](https://github.com/noir-lang/noir/commit/758b6b62918907c1a39f3090a77419003551745e)) +* **acvm:** Return false rather than panicking on invalid ECDSA signatures ([#2783](https://github.com/noir-lang/noir/issues/2783)) ([155abc0](https://github.com/noir-lang/noir/commit/155abc0d99fff41c79163c16bf297d41e5dff0fa)) +* Determinism of fallback transformer ([#3100](https://github.com/noir-lang/noir/issues/3100)) ([12daad1](https://github.com/noir-lang/noir/commit/12daad19c902caf5ee9e2eb4b6847bde5a924353)) +* Fix method `program_counter`, change method signature ([#3012](https://github.com/noir-lang/noir/issues/3012)) ([5ea522b](https://github.com/noir-lang/noir/commit/5ea522b840ca0f6f90d02ca00f0de32f515d450f)) +* Minor problems with `aztec` publishing ([#3095](https://github.com/noir-lang/noir/issues/3095)) ([0fc8f20](https://github.com/noir-lang/noir/commit/0fc8f20b8b87d033d27ce18db039399c17f81837)) +* Prevent duplicated assert message transformation ([#3038](https://github.com/noir-lang/noir/issues/3038)) ([082a6d0](https://github.com/noir-lang/noir/commit/082a6d02dad67a25692bed15c340a16a848a320e)) +* Return error rather than panicking on unreadable circuits ([#3179](https://github.com/noir-lang/noir/issues/3179)) ([d4f61d3](https://github.com/noir-lang/noir/commit/d4f61d3d51d515e40a5fd02d35315889f841bf53)) + ## [0.29.0](https://github.com/noir-lang/noir/compare/v0.28.0...v0.29.0) (2023-10-20) diff --git a/acvm-repo/acir/Cargo.toml b/acvm-repo/acir/Cargo.toml index 90d2c743a79..af6429a3a3d 100644 --- a/acvm-repo/acir/Cargo.toml +++ b/acvm-repo/acir/Cargo.toml @@ -2,7 +2,7 @@ name = "acir" description = "ACIR is the IR that the VM processes, it is analogous to LLVM IR" # x-release-please-start-version -version = "0.29.0" +version = "0.30.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/acvm-repo/acir/codegen/acir.cpp b/acvm-repo/acir/codegen/acir.cpp index 1c01c4f23d5..c31fc4abd00 100644 --- a/acvm-repo/acir/codegen/acir.cpp +++ b/acvm-repo/acir/codegen/acir.cpp @@ -82,14 +82,24 @@ namespace Circuit { static SchnorrVerify bincodeDeserialize(std::vector); }; - struct Pedersen { + struct PedersenCommitment { std::vector inputs; uint32_t domain_separator; std::array outputs; - friend bool operator==(const Pedersen&, const Pedersen&); + friend bool operator==(const PedersenCommitment&, const PedersenCommitment&); std::vector bincodeSerialize() const; - static Pedersen bincodeDeserialize(std::vector); + static PedersenCommitment bincodeDeserialize(std::vector); + }; + + struct PedersenHash { + std::vector inputs; + uint32_t domain_separator; + Circuit::Witness output; + + friend bool operator==(const PedersenHash&, const PedersenHash&); + std::vector bincodeSerialize() const; + static PedersenHash bincodeDeserialize(std::vector); }; struct HashToField128Security { @@ -167,7 +177,7 @@ namespace Circuit { static RecursiveAggregation bincodeDeserialize(std::vector); }; - std::variant value; + std::variant value; friend bool operator==(const BlackBoxFuncCall&, const BlackBoxFuncCall&); std::vector bincodeSerialize() const; @@ -443,14 +453,24 @@ namespace Circuit { static SchnorrVerify bincodeDeserialize(std::vector); }; - struct Pedersen { + struct PedersenCommitment { Circuit::HeapVector inputs; Circuit::RegisterIndex domain_separator; Circuit::HeapArray output; - friend bool operator==(const Pedersen&, const Pedersen&); + friend bool operator==(const PedersenCommitment&, const PedersenCommitment&); + std::vector bincodeSerialize() const; + static PedersenCommitment bincodeDeserialize(std::vector); + }; + + struct PedersenHash { + Circuit::HeapVector inputs; + Circuit::RegisterIndex domain_separator; + Circuit::RegisterIndex output; + + friend bool operator==(const PedersenHash&, const PedersenHash&); std::vector bincodeSerialize() const; - static Pedersen bincodeDeserialize(std::vector); + static PedersenHash bincodeDeserialize(std::vector); }; struct FixedBaseScalarMul { @@ -463,7 +483,7 @@ namespace Circuit { static FixedBaseScalarMul bincodeDeserialize(std::vector); }; - std::variant value; + std::variant value; friend bool operator==(const BlackBoxOp&, const BlackBoxOp&); std::vector bincodeSerialize() const; @@ -1871,22 +1891,22 @@ Circuit::BlackBoxFuncCall::SchnorrVerify serde::Deserializable BlackBoxFuncCall::Pedersen::bincodeSerialize() const { + inline std::vector BlackBoxFuncCall::PedersenCommitment::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } - inline BlackBoxFuncCall::Pedersen BlackBoxFuncCall::Pedersen::bincodeDeserialize(std::vector input) { + inline BlackBoxFuncCall::PedersenCommitment BlackBoxFuncCall::PedersenCommitment::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw serde::deserialization_error("Some input bytes were not read"); } @@ -1897,7 +1917,7 @@ namespace Circuit { template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxFuncCall::Pedersen &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Circuit::BlackBoxFuncCall::PedersenCommitment &obj, Serializer &serializer) { serde::Serializable::serialize(obj.inputs, serializer); serde::Serializable::serialize(obj.domain_separator, serializer); serde::Serializable::serialize(obj.outputs, serializer); @@ -1905,14 +1925,58 @@ void serde::Serializable::serialize(const C template <> template -Circuit::BlackBoxFuncCall::Pedersen serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxFuncCall::Pedersen obj; +Circuit::BlackBoxFuncCall::PedersenCommitment serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::BlackBoxFuncCall::PedersenCommitment obj; obj.inputs = serde::Deserializable::deserialize(deserializer); obj.domain_separator = serde::Deserializable::deserialize(deserializer); obj.outputs = serde::Deserializable::deserialize(deserializer); return obj; } +namespace Circuit { + + inline bool operator==(const BlackBoxFuncCall::PedersenHash &lhs, const BlackBoxFuncCall::PedersenHash &rhs) { + if (!(lhs.inputs == rhs.inputs)) { return false; } + if (!(lhs.domain_separator == rhs.domain_separator)) { return false; } + if (!(lhs.output == rhs.output)) { return false; } + return true; + } + + inline std::vector BlackBoxFuncCall::PedersenHash::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline BlackBoxFuncCall::PedersenHash BlackBoxFuncCall::PedersenHash::bincodeDeserialize(std::vector input) { + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw serde::deserialization_error("Some input bytes were not read"); + } + return value; + } + +} // end of namespace Circuit + +template <> +template +void serde::Serializable::serialize(const Circuit::BlackBoxFuncCall::PedersenHash &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.inputs, serializer); + serde::Serializable::serialize(obj.domain_separator, serializer); + serde::Serializable::serialize(obj.output, serializer); +} + +template <> +template +Circuit::BlackBoxFuncCall::PedersenHash serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::BlackBoxFuncCall::PedersenHash obj; + obj.inputs = serde::Deserializable::deserialize(deserializer); + obj.domain_separator = serde::Deserializable::deserialize(deserializer); + obj.output = serde::Deserializable::deserialize(deserializer); + return obj; +} + namespace Circuit { inline bool operator==(const BlackBoxFuncCall::HashToField128Security &lhs, const BlackBoxFuncCall::HashToField128Security &rhs) { @@ -2594,22 +2658,66 @@ Circuit::BlackBoxOp::SchnorrVerify serde::Deserializable BlackBoxOp::PedersenCommitment::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline BlackBoxOp::PedersenCommitment BlackBoxOp::PedersenCommitment::bincodeDeserialize(std::vector input) { + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw serde::deserialization_error("Some input bytes were not read"); + } + return value; + } + +} // end of namespace Circuit + +template <> +template +void serde::Serializable::serialize(const Circuit::BlackBoxOp::PedersenCommitment &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.inputs, serializer); + serde::Serializable::serialize(obj.domain_separator, serializer); + serde::Serializable::serialize(obj.output, serializer); +} + +template <> +template +Circuit::BlackBoxOp::PedersenCommitment serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::BlackBoxOp::PedersenCommitment obj; + obj.inputs = serde::Deserializable::deserialize(deserializer); + obj.domain_separator = serde::Deserializable::deserialize(deserializer); + obj.output = serde::Deserializable::deserialize(deserializer); + return obj; +} + +namespace Circuit { + + inline bool operator==(const BlackBoxOp::PedersenHash &lhs, const BlackBoxOp::PedersenHash &rhs) { if (!(lhs.inputs == rhs.inputs)) { return false; } if (!(lhs.domain_separator == rhs.domain_separator)) { return false; } if (!(lhs.output == rhs.output)) { return false; } return true; } - inline std::vector BlackBoxOp::Pedersen::bincodeSerialize() const { + inline std::vector BlackBoxOp::PedersenHash::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } - inline BlackBoxOp::Pedersen BlackBoxOp::Pedersen::bincodeDeserialize(std::vector input) { + inline BlackBoxOp::PedersenHash BlackBoxOp::PedersenHash::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw serde::deserialization_error("Some input bytes were not read"); } @@ -2620,7 +2728,7 @@ namespace Circuit { template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxOp::Pedersen &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Circuit::BlackBoxOp::PedersenHash &obj, Serializer &serializer) { serde::Serializable::serialize(obj.inputs, serializer); serde::Serializable::serialize(obj.domain_separator, serializer); serde::Serializable::serialize(obj.output, serializer); @@ -2628,8 +2736,8 @@ void serde::Serializable::serialize(const Circuit template <> template -Circuit::BlackBoxOp::Pedersen serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxOp::Pedersen obj; +Circuit::BlackBoxOp::PedersenHash serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::BlackBoxOp::PedersenHash obj; obj.inputs = serde::Deserializable::deserialize(deserializer); obj.domain_separator = serde::Deserializable::deserialize(deserializer); obj.output = serde::Deserializable::deserialize(deserializer); diff --git a/acvm-repo/acir/src/circuit/black_box_functions.rs b/acvm-repo/acir/src/circuit/black_box_functions.rs index c56427a9f1a..9129f44008c 100644 --- a/acvm-repo/acir/src/circuit/black_box_functions.rs +++ b/acvm-repo/acir/src/circuit/black_box_functions.rs @@ -30,7 +30,9 @@ pub enum BlackBoxFunc { /// [grumpkin]: https://hackmd.io/@aztec-network/ByzgNxBfd#2-Grumpkin---A-curve-on-top-of-BN-254-for-SNARK-efficient-group-operations SchnorrVerify, /// Calculates a Pedersen commitment to the inputs. - Pedersen, + PedersenCommitment, + /// Calculates a Pedersen hash to the inputs. + PedersenHash, /// Hashes a set of inputs and applies the field modulus to the result /// to return a value which can be represented as a [`FieldElement`][acir_field::FieldElement] /// @@ -62,7 +64,8 @@ impl BlackBoxFunc { BlackBoxFunc::SHA256 => "sha256", BlackBoxFunc::SchnorrVerify => "schnorr_verify", BlackBoxFunc::Blake2s => "blake2s", - BlackBoxFunc::Pedersen => "pedersen", + BlackBoxFunc::PedersenCommitment => "pedersen", + BlackBoxFunc::PedersenHash => "pedersen_hash", BlackBoxFunc::HashToField128Security => "hash_to_field_128_security", BlackBoxFunc::EcdsaSecp256k1 => "ecdsa_secp256k1", BlackBoxFunc::FixedBaseScalarMul => "fixed_base_scalar_mul", @@ -79,7 +82,8 @@ impl BlackBoxFunc { "sha256" => Some(BlackBoxFunc::SHA256), "schnorr_verify" => Some(BlackBoxFunc::SchnorrVerify), "blake2s" => Some(BlackBoxFunc::Blake2s), - "pedersen" => Some(BlackBoxFunc::Pedersen), + "pedersen" => Some(BlackBoxFunc::PedersenCommitment), + "pedersen_hash" => Some(BlackBoxFunc::PedersenHash), "hash_to_field_128_security" => Some(BlackBoxFunc::HashToField128Security), "ecdsa_secp256k1" => Some(BlackBoxFunc::EcdsaSecp256k1), "ecdsa_secp256r1" => Some(BlackBoxFunc::EcdsaSecp256r1), diff --git a/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs b/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs index 472c4a043ba..22278bdc635 100644 --- a/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs +++ b/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs @@ -46,11 +46,16 @@ pub enum BlackBoxFuncCall { message: Vec, output: Witness, }, - Pedersen { + PedersenCommitment { inputs: Vec, domain_separator: u32, outputs: (Witness, Witness), }, + PedersenHash { + inputs: Vec, + domain_separator: u32, + output: Witness, + }, // 128 here specifies that this function // should have 128 bits of security HashToField128Security { @@ -138,11 +143,16 @@ impl BlackBoxFuncCall { message: vec![], output: Witness(0), }, - BlackBoxFunc::Pedersen => BlackBoxFuncCall::Pedersen { + BlackBoxFunc::PedersenCommitment => BlackBoxFuncCall::PedersenCommitment { inputs: vec![], domain_separator: 0, outputs: (Witness(0), Witness(0)), }, + BlackBoxFunc::PedersenHash => BlackBoxFuncCall::PedersenHash { + inputs: vec![], + domain_separator: 0, + output: Witness(0), + }, BlackBoxFunc::HashToField128Security => { BlackBoxFuncCall::HashToField128Security { inputs: vec![], output: Witness(0) } } @@ -187,7 +197,8 @@ impl BlackBoxFuncCall { BlackBoxFuncCall::SHA256 { .. } => BlackBoxFunc::SHA256, BlackBoxFuncCall::Blake2s { .. } => BlackBoxFunc::Blake2s, BlackBoxFuncCall::SchnorrVerify { .. } => BlackBoxFunc::SchnorrVerify, - BlackBoxFuncCall::Pedersen { .. } => BlackBoxFunc::Pedersen, + BlackBoxFuncCall::PedersenCommitment { .. } => BlackBoxFunc::PedersenCommitment, + BlackBoxFuncCall::PedersenHash { .. } => BlackBoxFunc::PedersenHash, BlackBoxFuncCall::HashToField128Security { .. } => BlackBoxFunc::HashToField128Security, BlackBoxFuncCall::EcdsaSecp256k1 { .. } => BlackBoxFunc::EcdsaSecp256k1, BlackBoxFuncCall::EcdsaSecp256r1 { .. } => BlackBoxFunc::EcdsaSecp256r1, @@ -207,7 +218,8 @@ impl BlackBoxFuncCall { BlackBoxFuncCall::SHA256 { inputs, .. } | BlackBoxFuncCall::Blake2s { inputs, .. } | BlackBoxFuncCall::Keccak256 { inputs, .. } - | BlackBoxFuncCall::Pedersen { inputs, .. } + | BlackBoxFuncCall::PedersenCommitment { inputs, .. } + | BlackBoxFuncCall::PedersenHash { inputs, .. } | BlackBoxFuncCall::HashToField128Security { inputs, .. } => inputs.to_vec(), BlackBoxFuncCall::AND { lhs, rhs, .. } | BlackBoxFuncCall::XOR { lhs, rhs, .. } => { vec![*lhs, *rhs] @@ -304,9 +316,10 @@ impl BlackBoxFuncCall { | BlackBoxFuncCall::HashToField128Security { output, .. } | BlackBoxFuncCall::SchnorrVerify { output, .. } | BlackBoxFuncCall::EcdsaSecp256k1 { output, .. } + | BlackBoxFuncCall::PedersenHash { output, .. } | BlackBoxFuncCall::EcdsaSecp256r1 { output, .. } => vec![*output], BlackBoxFuncCall::FixedBaseScalarMul { outputs, .. } - | BlackBoxFuncCall::Pedersen { outputs, .. } => vec![outputs.0, outputs.1], + | BlackBoxFuncCall::PedersenCommitment { outputs, .. } => vec![outputs.0, outputs.1], BlackBoxFuncCall::RANGE { .. } => vec![], BlackBoxFuncCall::Keccak256VariableLength { outputs, .. } => outputs.to_vec(), } @@ -395,7 +408,7 @@ impl std::fmt::Display for BlackBoxFuncCall { // SPECIFIC PARAMETERS match self { - BlackBoxFuncCall::Pedersen { domain_separator, .. } => { + BlackBoxFuncCall::PedersenCommitment { domain_separator, .. } => { write!(f, " domain_separator: {domain_separator}") } _ => write!(f, ""), diff --git a/acvm-repo/acir/tests/test_program_serialization.rs b/acvm-repo/acir/tests/test_program_serialization.rs index 6b2e85ab449..2a25129df48 100644 --- a/acvm-repo/acir/tests/test_program_serialization.rs +++ b/acvm-repo/acir/tests/test_program_serialization.rs @@ -77,9 +77,9 @@ fn fixed_base_scalar_mul_circuit() { circuit.write(&mut bytes).unwrap(); let expected_serialization: Vec = vec![ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 77, 138, 91, 10, 0, 48, 12, 194, 178, 215, 207, 78, 189, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 77, 138, 91, 10, 0, 48, 12, 194, 178, 215, 215, 46, 189, 163, 175, 165, 10, 21, 36, 10, 57, 192, 160, 146, 188, 226, 139, 78, 113, 69, 183, 190, 61, - 111, 218, 182, 231, 124, 68, 185, 243, 207, 92, 0, 0, 0, + 111, 218, 182, 231, 124, 122, 8, 177, 65, 92, 0, 0, 0, ]; assert_eq!(bytes, expected_serialization) @@ -87,7 +87,7 @@ fn fixed_base_scalar_mul_circuit() { #[test] fn pedersen_circuit() { - let pedersen = Opcode::BlackBoxFuncCall(BlackBoxFuncCall::Pedersen { + let pedersen = Opcode::BlackBoxFuncCall(BlackBoxFuncCall::PedersenCommitment { inputs: vec![FunctionInput { witness: Witness(1), num_bits: FieldElement::max_num_bits() }], outputs: (Witness(2), Witness(3)), domain_separator: 0, diff --git a/acvm-repo/acir_field/Cargo.toml b/acvm-repo/acir_field/Cargo.toml index a81e16b18a2..22ec76ff764 100644 --- a/acvm-repo/acir_field/Cargo.toml +++ b/acvm-repo/acir_field/Cargo.toml @@ -2,7 +2,7 @@ name = "acir_field" description = "The field implementation being used by ACIR." # x-release-please-start-version -version = "0.29.0" +version = "0.30.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/acvm-repo/acvm/Cargo.toml b/acvm-repo/acvm/Cargo.toml index 21029e90464..d52d39e1285 100644 --- a/acvm-repo/acvm/Cargo.toml +++ b/acvm-repo/acvm/Cargo.toml @@ -2,7 +2,7 @@ name = "acvm" description = "The virtual machine that processes ACIR given a backend/proof system." # x-release-please-start-version -version = "0.29.0" +version = "0.30.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/acvm-repo/acvm/src/compiler/optimizers/redundant_range.rs b/acvm-repo/acvm/src/compiler/optimizers/redundant_range.rs index b1696704108..766d3674113 100644 --- a/acvm-repo/acvm/src/compiler/optimizers/redundant_range.rs +++ b/acvm-repo/acvm/src/compiler/optimizers/redundant_range.rs @@ -1,6 +1,10 @@ use acir::{ - circuit::{opcodes::BlackBoxFuncCall, Circuit, Opcode}, - native_types::Witness, + circuit::{ + opcodes::{BlackBoxFuncCall, FunctionInput}, + Circuit, Opcode, + }, + native_types::{Expression, Witness}, + FieldElement, }; use std::collections::{BTreeMap, HashSet}; @@ -101,7 +105,7 @@ impl RangeOptimizer { if is_lowest_bit_size { already_seen_witness.insert(witness); new_order_list.push(order_list[idx]); - optimized_opcodes.push(opcode); + optimized_opcodes.push(optimized_range_opcode(witness, num_bits)); } } @@ -126,6 +130,20 @@ fn extract_range_opcode(opcode: &Opcode) -> Option<(Witness, u32)> { } } +fn optimized_range_opcode(witness: Witness, num_bits: u32) -> Opcode { + if num_bits == 1 { + Opcode::Arithmetic(Expression { + mul_terms: vec![(FieldElement::one(), witness, witness)], + linear_combinations: vec![(-FieldElement::one(), witness)], + q_c: FieldElement::zero(), + }) + } else { + Opcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE { + input: FunctionInput { witness, num_bits }, + }) + } +} + #[cfg(test)] mod tests { use std::collections::BTreeSet; diff --git a/acvm-repo/acvm/src/compiler/transformers/mod.rs b/acvm-repo/acvm/src/compiler/transformers/mod.rs index 6f9d78e4b93..d827b759666 100644 --- a/acvm-repo/acvm/src/compiler/transformers/mod.rs +++ b/acvm-repo/acvm/src/compiler/transformers/mod.rs @@ -130,7 +130,10 @@ pub(super) fn transform_internal( outputs, .. } - | acir::circuit::opcodes::BlackBoxFuncCall::Pedersen { outputs, .. } => { + | acir::circuit::opcodes::BlackBoxFuncCall::PedersenCommitment { + outputs, + .. + } => { transformer.mark_solvable(outputs.0); transformer.mark_solvable(outputs.1); } @@ -140,7 +143,8 @@ pub(super) fn transform_internal( } | acir::circuit::opcodes::BlackBoxFuncCall::EcdsaSecp256k1 { output, .. } | acir::circuit::opcodes::BlackBoxFuncCall::EcdsaSecp256r1 { output, .. } - | acir::circuit::opcodes::BlackBoxFuncCall::SchnorrVerify { output, .. } => { + | acir::circuit::opcodes::BlackBoxFuncCall::SchnorrVerify { output, .. } + | acir::circuit::opcodes::BlackBoxFuncCall::PedersenHash { output, .. } => { transformer.mark_solvable(*output); } } diff --git a/acvm-repo/acvm/src/pwg/blackbox/mod.rs b/acvm-repo/acvm/src/pwg/blackbox/mod.rs index c4d9d561f46..7e8ab8b948c 100644 --- a/acvm-repo/acvm/src/pwg/blackbox/mod.rs +++ b/acvm-repo/acvm/src/pwg/blackbox/mod.rs @@ -5,6 +5,8 @@ use acir::{ }; use acvm_blackbox_solver::{blake2s, keccak256, sha256}; +use self::pedersen::pedersen_hash; + use super::{insert_value, OpcodeNotSolvable, OpcodeResolutionError}; use crate::BlackBoxFunctionSolver; @@ -117,9 +119,12 @@ pub(crate) fn solve( message, *output, ), - BlackBoxFuncCall::Pedersen { inputs, domain_separator, outputs } => { + BlackBoxFuncCall::PedersenCommitment { inputs, domain_separator, outputs } => { pedersen(backend, initial_witness, inputs, *domain_separator, *outputs) } + BlackBoxFuncCall::PedersenHash { inputs, domain_separator, output } => { + pedersen_hash(backend, initial_witness, inputs, *domain_separator, *output) + } BlackBoxFuncCall::EcdsaSecp256k1 { public_key_x, public_key_y, diff --git a/acvm-repo/acvm/src/pwg/blackbox/pedersen.rs b/acvm-repo/acvm/src/pwg/blackbox/pedersen.rs index 44b4c91dc63..bb214c1ceaf 100644 --- a/acvm-repo/acvm/src/pwg/blackbox/pedersen.rs +++ b/acvm-repo/acvm/src/pwg/blackbox/pedersen.rs @@ -19,10 +19,28 @@ pub(super) fn pedersen( inputs.iter().map(|input| witness_to_value(initial_witness, input.witness)).collect(); let scalars: Vec<_> = scalars?.into_iter().cloned().collect(); - let (res_x, res_y) = backend.pedersen(&scalars, domain_separator)?; + let (res_x, res_y) = backend.pedersen_commitment(&scalars, domain_separator)?; insert_value(&outputs.0, res_x, initial_witness)?; insert_value(&outputs.1, res_y, initial_witness)?; Ok(()) } + +pub(super) fn pedersen_hash( + backend: &impl BlackBoxFunctionSolver, + initial_witness: &mut WitnessMap, + inputs: &[FunctionInput], + domain_separator: u32, + output: Witness, +) -> Result<(), OpcodeResolutionError> { + let scalars: Result, _> = + inputs.iter().map(|input| witness_to_value(initial_witness, input.witness)).collect(); + let scalars: Vec<_> = scalars?.into_iter().cloned().collect(); + + let res = backend.pedersen_hash(&scalars, domain_separator)?; + + insert_value(&output, res, initial_witness)?; + + Ok(()) +} diff --git a/acvm-repo/acvm/src/pwg/brillig.rs b/acvm-repo/acvm/src/pwg/brillig.rs index 6fc54d42eab..b0e5ec6dd48 100644 --- a/acvm-repo/acvm/src/pwg/brillig.rs +++ b/acvm-repo/acvm/src/pwg/brillig.rs @@ -14,13 +14,14 @@ use crate::{pwg::OpcodeNotSolvable, OpcodeResolutionError}; use super::{get_value, insert_value}; -pub(super) enum BrilligSolverStatus { +#[derive(Debug)] +pub enum BrilligSolverStatus { Finished, InProgress, ForeignCallWait(ForeignCallWaitInfo), } -pub(super) struct BrilligSolver<'b, B: BlackBoxFunctionSolver> { +pub struct BrilligSolver<'b, B: BlackBoxFunctionSolver> { vm: VM<'b, B>, acir_index: usize, } @@ -62,7 +63,7 @@ impl<'b, B: BlackBoxFunctionSolver> BrilligSolver<'b, B> { /// Constructs a solver for a Brillig block given the bytecode and initial /// witness. pub(super) fn new( - initial_witness: &mut WitnessMap, + initial_witness: &WitnessMap, brillig: &'b Brillig, bb_solver: &'b B, acir_index: usize, @@ -116,6 +117,15 @@ impl<'b, B: BlackBoxFunctionSolver> BrilligSolver<'b, B> { self.handle_vm_status(status) } + pub fn step(&mut self) -> Result { + let status = self.vm.process_opcode(); + self.handle_vm_status(status) + } + + pub fn program_counter(&self) -> usize { + self.vm.program_counter() + } + fn handle_vm_status( &self, vm_status: VMStatus, @@ -185,7 +195,7 @@ impl<'b, B: BlackBoxFunctionSolver> BrilligSolver<'b, B> { Ok(()) } - pub(super) fn resolve_pending_foreign_call(&mut self, foreign_call_result: ForeignCallResult) { + pub fn resolve_pending_foreign_call(&mut self, foreign_call_result: ForeignCallResult) { match self.vm.get_status() { VMStatus::ForeignCallWait { .. } => self.vm.resolve_foreign_call(foreign_call_result), _ => unreachable!("Brillig VM is not waiting for a foreign call"), diff --git a/acvm-repo/acvm/src/pwg/mod.rs b/acvm-repo/acvm/src/pwg/mod.rs index 057597e6392..6016d0d1f3e 100644 --- a/acvm-repo/acvm/src/pwg/mod.rs +++ b/acvm-repo/acvm/src/pwg/mod.rs @@ -10,12 +10,7 @@ use acir::{ }; use acvm_blackbox_solver::BlackBoxResolutionError; -use self::{ - arithmetic::ArithmeticSolver, - brillig::{BrilligSolver, BrilligSolverStatus}, - directives::solve_directives, - memory_op::MemoryOpSolver, -}; +use self::{arithmetic::ArithmeticSolver, directives::solve_directives, memory_op::MemoryOpSolver}; use crate::{BlackBoxFunctionSolver, Language}; use thiserror::Error; @@ -30,6 +25,7 @@ mod directives; mod blackbox; mod memory_op; +pub use self::brillig::{BrilligSolver, BrilligSolverStatus}; pub use brillig::ForeignCallWaitInfo; #[derive(Debug, Clone, PartialEq)] @@ -63,6 +59,11 @@ impl std::fmt::Display for ACVMStatus { } } +pub enum StepResult<'a, B: BlackBoxFunctionSolver> { + Status(ACVMStatus), + IntoBrillig(BrilligSolver<'a, B>), +} + // This enum represents the different cases in which an // opcode can be unsolvable. // The most common being that one of its input has not been @@ -192,6 +193,10 @@ impl<'a, B: BlackBoxFunctionSolver> ACVM<'a, B> { status } + pub fn get_status(&self) -> &ACVMStatus { + &self.status + } + /// Sets the VM status to [ACVMStatus::Failure] using the provided `error`. /// Returns the new status. fn fail(&mut self, error: OpcodeResolutionError) -> ACVMStatus { @@ -263,6 +268,13 @@ impl<'a, B: BlackBoxFunctionSolver> ACVM<'a, B> { res => res.map(|_| ()), }, }; + self.handle_opcode_resolution(resolution) + } + + fn handle_opcode_resolution( + &mut self, + resolution: Result<(), OpcodeResolutionError>, + ) -> ACVMStatus { match resolution { Ok(()) => { self.instruction_pointer += 1; @@ -302,34 +314,64 @@ impl<'a, B: BlackBoxFunctionSolver> ACVM<'a, B> { let Opcode::Brillig(brillig) = &self.opcodes[self.instruction_pointer] else { unreachable!("Not executing a Brillig opcode"); }; + let witness = &mut self.witness_map; if BrilligSolver::::should_skip(witness, brillig)? { - BrilligSolver::::zero_out_brillig_outputs(witness, brillig).map(|_| None) - } else { - // If we're resuming execution after resolving a foreign call then - // there will be a cached `BrilligSolver` to avoid recomputation. - let mut solver: BrilligSolver<'_, B> = match self.brillig_solver.take() { - Some(solver) => solver, - None => { - BrilligSolver::new(witness, brillig, self.backend, self.instruction_pointer)? - } - }; - match solver.solve()? { - BrilligSolverStatus::ForeignCallWait(foreign_call) => { - // Cache the current state of the solver - self.brillig_solver = Some(solver); - Ok(Some(foreign_call)) - } - BrilligSolverStatus::InProgress => { - unreachable!("Brillig solver still in progress") - } - BrilligSolverStatus::Finished => { - // Write execution outputs - solver.finalize(witness, brillig)?; - Ok(None) - } + return BrilligSolver::::zero_out_brillig_outputs(witness, brillig).map(|_| None); + } + + // If we're resuming execution after resolving a foreign call then + // there will be a cached `BrilligSolver` to avoid recomputation. + let mut solver: BrilligSolver<'_, B> = match self.brillig_solver.take() { + Some(solver) => solver, + None => BrilligSolver::new(witness, brillig, self.backend, self.instruction_pointer)?, + }; + match solver.solve()? { + BrilligSolverStatus::ForeignCallWait(foreign_call) => { + // Cache the current state of the solver + self.brillig_solver = Some(solver); + Ok(Some(foreign_call)) } + BrilligSolverStatus::InProgress => { + unreachable!("Brillig solver still in progress") + } + BrilligSolverStatus::Finished => { + // Write execution outputs + solver.finalize(witness, brillig)?; + Ok(None) + } + } + } + + pub fn step_into_brillig_opcode(&mut self) -> StepResult<'a, B> { + let Opcode::Brillig(brillig) = &self.opcodes[self.instruction_pointer] else { + return StepResult::Status(self.solve_opcode()); + }; + + let witness = &mut self.witness_map; + let should_skip = match BrilligSolver::::should_skip(witness, brillig) { + Ok(result) => result, + Err(err) => return StepResult::Status(self.handle_opcode_resolution(Err(err))), + }; + + if should_skip { + let resolution = BrilligSolver::::zero_out_brillig_outputs(witness, brillig); + return StepResult::Status(self.handle_opcode_resolution(resolution)); + } + + let solver = BrilligSolver::new(witness, brillig, self.backend, self.instruction_pointer); + match solver { + Ok(solver) => StepResult::IntoBrillig(solver), + Err(..) => StepResult::Status(self.handle_opcode_resolution(solver.map(|_| ()))), + } + } + + pub fn finish_brillig_with_solver(&mut self, solver: BrilligSolver<'a, B>) -> ACVMStatus { + if !matches!(&self.opcodes[self.instruction_pointer], Opcode::Brillig(..)) { + unreachable!("Not executing a Brillig opcode"); } + self.brillig_solver = Some(solver); + self.solve_opcode() } } diff --git a/acvm-repo/acvm/tests/solver.rs b/acvm-repo/acvm/tests/solver.rs index 1d287d70c1b..d578555c5dc 100644 --- a/acvm-repo/acvm/tests/solver.rs +++ b/acvm-repo/acvm/tests/solver.rs @@ -29,13 +29,20 @@ impl BlackBoxFunctionSolver for StubbedBackend { ) -> Result { panic!("Path not trodden by this test") } - fn pedersen( + fn pedersen_commitment( &self, _inputs: &[FieldElement], _domain_separator: u32, ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> { panic!("Path not trodden by this test") } + fn pedersen_hash( + &self, + _inputs: &[FieldElement], + _domain_separator: u32, + ) -> Result { + panic!("Path not trodden by this test") + } fn fixed_base_scalar_mul( &self, _low: &FieldElement, diff --git a/acvm-repo/acvm_js/Cargo.toml b/acvm-repo/acvm_js/Cargo.toml index 18ed39e046e..b63b0b7be6e 100644 --- a/acvm-repo/acvm_js/Cargo.toml +++ b/acvm-repo/acvm_js/Cargo.toml @@ -2,7 +2,7 @@ name = "acvm_js" description = "Typescript wrapper around the ACVM allowing execution of ACIR code" # x-release-please-start-version -version = "0.29.0" +version = "0.30.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/acvm-repo/acvm_js/package.json b/acvm-repo/acvm_js/package.json index b8f7642566d..4947fee5ad8 100644 --- a/acvm-repo/acvm_js/package.json +++ b/acvm-repo/acvm_js/package.json @@ -1,6 +1,6 @@ { "name": "@noir-lang/acvm_js", - "version": "0.29.0", + "version": "0.30.0", "repository": { "type": "git", "url": "https://github.com/noir-lang/acvm.git" diff --git a/acvm-repo/acvm_js/test/shared/fixed_base_scalar_mul.ts b/acvm-repo/acvm_js/test/shared/fixed_base_scalar_mul.ts index e5f35e2b8fc..0437bebc369 100644 --- a/acvm-repo/acvm_js/test/shared/fixed_base_scalar_mul.ts +++ b/acvm-repo/acvm_js/test/shared/fixed_base_scalar_mul.ts @@ -1,7 +1,7 @@ // See `fixed_base_scalar_mul_circuit` integration test in `acir/tests/test_program_serialization.rs`. export const bytecode = Uint8Array.from([ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 77, 138, 91, 10, 0, 48, 12, 194, 178, 215, 207, 78, 189, 163, 175, 165, 10, 21, 36, - 10, 57, 192, 160, 146, 188, 226, 139, 78, 113, 69, 183, 190, 61, 111, 218, 182, 231, 124, 68, 185, 243, 207, 92, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 77, 138, 91, 10, 0, 48, 12, 194, 178, 215, 215, 46, 189, 163, 175, 165, 10, 21, 36, + 10, 57, 192, 160, 146, 188, 226, 139, 78, 113, 69, 183, 190, 61, 111, 218, 182, 231, 124, 122, 8, 177, 65, 92, 0, 0, 0, ]); export const initialWitnessMap = new Map([ diff --git a/acvm-repo/acvm_js/test/shared/pedersen.ts b/acvm-repo/acvm_js/test/shared/pedersen.ts index a973718e31c..668ee2b510b 100644 --- a/acvm-repo/acvm_js/test/shared/pedersen.ts +++ b/acvm-repo/acvm_js/test/shared/pedersen.ts @@ -8,6 +8,6 @@ export const initialWitnessMap = new Map([[1, '0x0000000000000000000000000000000 export const expectedWitnessMap = new Map([ [1, '0x0000000000000000000000000000000000000000000000000000000000000001'], - [2, '0x09489945604c9686e698cb69d7bd6fc0cdb02e9faae3e1a433f1c342c1a5ecc4'], - [3, '0x24f50d25508b4dfb1e8a834e39565f646e217b24cb3a475c2e4991d1bb07a9d8'], + [2, '0x083e7911d835097629f0067531fc15cafd79a89beecb39903f69572c636f4a5a'], + [3, '0x1a7f5efaad7f315c25a918f30cc8d7333fccab7ad7c90f14de81bcc528f9935d'], ]); diff --git a/acvm-repo/acvm_js/test/shared/schnorr_verify.ts b/acvm-repo/acvm_js/test/shared/schnorr_verify.ts index afd57d8ffb5..f88a70ba4a1 100644 --- a/acvm-repo/acvm_js/test/shared/schnorr_verify.ts +++ b/acvm-repo/acvm_js/test/shared/schnorr_verify.ts @@ -15,72 +15,72 @@ export const bytecode = Uint8Array.from([ ]); export const initialWitnessMap = new Map([ - [1, '0x17cbd3ed3151ccfd170efe1d54280a6a4822640bf5c369908ad74ea21518a9c5'], - [2, '0x0e0456e3795c1a31f20035b741cd6158929eeccd320d299cfcac962865a6bc74'], - [3, '0x0000000000000000000000000000000000000000000000000000000000000005'], - [4, '0x00000000000000000000000000000000000000000000000000000000000000ca'], - [5, '0x000000000000000000000000000000000000000000000000000000000000001f'], - [6, '0x0000000000000000000000000000000000000000000000000000000000000092'], - [7, '0x0000000000000000000000000000000000000000000000000000000000000051'], - [8, '0x00000000000000000000000000000000000000000000000000000000000000f2'], - [9, '0x00000000000000000000000000000000000000000000000000000000000000f6'], - [10, '0x0000000000000000000000000000000000000000000000000000000000000045'], - [11, '0x000000000000000000000000000000000000000000000000000000000000002b'], - [12, '0x000000000000000000000000000000000000000000000000000000000000006b'], - [13, '0x00000000000000000000000000000000000000000000000000000000000000f9'], - [14, '0x0000000000000000000000000000000000000000000000000000000000000099'], - [15, '0x00000000000000000000000000000000000000000000000000000000000000c6'], - [16, '0x000000000000000000000000000000000000000000000000000000000000002c'], - [17, '0x000000000000000000000000000000000000000000000000000000000000000e'], - [18, '0x000000000000000000000000000000000000000000000000000000000000006f'], - [19, '0x00000000000000000000000000000000000000000000000000000000000000bf'], - [20, '0x0000000000000000000000000000000000000000000000000000000000000079'], - [21, '0x0000000000000000000000000000000000000000000000000000000000000089'], - [22, '0x00000000000000000000000000000000000000000000000000000000000000a6'], - [23, '0x00000000000000000000000000000000000000000000000000000000000000a0'], - [24, '0x0000000000000000000000000000000000000000000000000000000000000067'], - [25, '0x0000000000000000000000000000000000000000000000000000000000000012'], - [26, '0x00000000000000000000000000000000000000000000000000000000000000b5'], - [27, '0x00000000000000000000000000000000000000000000000000000000000000f3'], - [28, '0x00000000000000000000000000000000000000000000000000000000000000e9'], - [29, '0x00000000000000000000000000000000000000000000000000000000000000e2'], - [30, '0x000000000000000000000000000000000000000000000000000000000000005f'], - [31, '0x0000000000000000000000000000000000000000000000000000000000000043'], - [32, '0x0000000000000000000000000000000000000000000000000000000000000010'], - [33, '0x0000000000000000000000000000000000000000000000000000000000000025'], - [34, '0x0000000000000000000000000000000000000000000000000000000000000080'], - [35, '0x0000000000000000000000000000000000000000000000000000000000000055'], - [36, '0x000000000000000000000000000000000000000000000000000000000000004c'], - [37, '0x0000000000000000000000000000000000000000000000000000000000000013'], - [38, '0x00000000000000000000000000000000000000000000000000000000000000fd'], - [39, '0x000000000000000000000000000000000000000000000000000000000000001e'], - [40, '0x000000000000000000000000000000000000000000000000000000000000004d'], - [41, '0x00000000000000000000000000000000000000000000000000000000000000c0'], - [42, '0x0000000000000000000000000000000000000000000000000000000000000035'], - [43, '0x000000000000000000000000000000000000000000000000000000000000008a'], - [44, '0x00000000000000000000000000000000000000000000000000000000000000cd'], - [45, '0x0000000000000000000000000000000000000000000000000000000000000045'], - [46, '0x0000000000000000000000000000000000000000000000000000000000000021'], - [47, '0x00000000000000000000000000000000000000000000000000000000000000ec'], - [48, '0x00000000000000000000000000000000000000000000000000000000000000a3'], - [49, '0x0000000000000000000000000000000000000000000000000000000000000053'], - [50, '0x00000000000000000000000000000000000000000000000000000000000000c2'], - [51, '0x0000000000000000000000000000000000000000000000000000000000000054'], - [52, '0x0000000000000000000000000000000000000000000000000000000000000089'], - [53, '0x00000000000000000000000000000000000000000000000000000000000000b8'], - [54, '0x00000000000000000000000000000000000000000000000000000000000000dd'], - [55, '0x00000000000000000000000000000000000000000000000000000000000000b0'], - [56, '0x0000000000000000000000000000000000000000000000000000000000000079'], - [57, '0x00000000000000000000000000000000000000000000000000000000000000b3'], - [58, '0x000000000000000000000000000000000000000000000000000000000000001b'], - [59, '0x000000000000000000000000000000000000000000000000000000000000003f'], - [60, '0x0000000000000000000000000000000000000000000000000000000000000046'], - [61, '0x0000000000000000000000000000000000000000000000000000000000000036'], - [62, '0x0000000000000000000000000000000000000000000000000000000000000010'], - [63, '0x00000000000000000000000000000000000000000000000000000000000000b0'], - [64, '0x00000000000000000000000000000000000000000000000000000000000000fa'], - [65, '0x0000000000000000000000000000000000000000000000000000000000000027'], - [66, '0x00000000000000000000000000000000000000000000000000000000000000ef'], + [1, '0x04b260954662e97f00cab9adb773a259097f7a274b83b113532bce27fa3fb96a'], + [2, '0x2fd51571db6c08666b0edfbfbc57d432068bccd0110a39b166ab243da0037197'], + [3, '0x000000000000000000000000000000000000000000000000000000000000002e'], + [4, '0x00000000000000000000000000000000000000000000000000000000000000ce'], + [5, '0x0000000000000000000000000000000000000000000000000000000000000052'], + [6, '0x00000000000000000000000000000000000000000000000000000000000000aa'], + [7, '0x0000000000000000000000000000000000000000000000000000000000000087'], + [8, '0x000000000000000000000000000000000000000000000000000000000000002a'], + [9, '0x0000000000000000000000000000000000000000000000000000000000000049'], + [10, '0x000000000000000000000000000000000000000000000000000000000000009d'], + [11, '0x0000000000000000000000000000000000000000000000000000000000000050'], + [12, '0x000000000000000000000000000000000000000000000000000000000000007c'], + [13, '0x000000000000000000000000000000000000000000000000000000000000009a'], + [14, '0x00000000000000000000000000000000000000000000000000000000000000aa'], + [15, '0x00000000000000000000000000000000000000000000000000000000000000df'], + [16, '0x0000000000000000000000000000000000000000000000000000000000000023'], + [17, '0x0000000000000000000000000000000000000000000000000000000000000034'], + [18, '0x0000000000000000000000000000000000000000000000000000000000000010'], + [19, '0x000000000000000000000000000000000000000000000000000000000000008a'], + [20, '0x0000000000000000000000000000000000000000000000000000000000000047'], + [21, '0x0000000000000000000000000000000000000000000000000000000000000063'], + [22, '0x00000000000000000000000000000000000000000000000000000000000000e8'], + [23, '0x0000000000000000000000000000000000000000000000000000000000000037'], + [24, '0x0000000000000000000000000000000000000000000000000000000000000054'], + [25, '0x0000000000000000000000000000000000000000000000000000000000000096'], + [26, '0x000000000000000000000000000000000000000000000000000000000000003e'], + [27, '0x00000000000000000000000000000000000000000000000000000000000000d5'], + [28, '0x00000000000000000000000000000000000000000000000000000000000000ae'], + [29, '0x0000000000000000000000000000000000000000000000000000000000000024'], + [30, '0x000000000000000000000000000000000000000000000000000000000000002d'], + [31, '0x0000000000000000000000000000000000000000000000000000000000000020'], + [32, '0x0000000000000000000000000000000000000000000000000000000000000080'], + [33, '0x000000000000000000000000000000000000000000000000000000000000004d'], + [34, '0x0000000000000000000000000000000000000000000000000000000000000047'], + [35, '0x00000000000000000000000000000000000000000000000000000000000000a5'], + [36, '0x00000000000000000000000000000000000000000000000000000000000000bb'], + [37, '0x00000000000000000000000000000000000000000000000000000000000000f6'], + [38, '0x00000000000000000000000000000000000000000000000000000000000000c3'], + [39, '0x000000000000000000000000000000000000000000000000000000000000000b'], + [40, '0x000000000000000000000000000000000000000000000000000000000000003b'], + [41, '0x0000000000000000000000000000000000000000000000000000000000000065'], + [42, '0x00000000000000000000000000000000000000000000000000000000000000c9'], + [43, '0x0000000000000000000000000000000000000000000000000000000000000001'], + [44, '0x0000000000000000000000000000000000000000000000000000000000000085'], + [45, '0x0000000000000000000000000000000000000000000000000000000000000006'], + [46, '0x000000000000000000000000000000000000000000000000000000000000009e'], + [47, '0x000000000000000000000000000000000000000000000000000000000000002f'], + [48, '0x0000000000000000000000000000000000000000000000000000000000000010'], + [49, '0x00000000000000000000000000000000000000000000000000000000000000e6'], + [50, '0x0000000000000000000000000000000000000000000000000000000000000030'], + [51, '0x000000000000000000000000000000000000000000000000000000000000004a'], + [52, '0x0000000000000000000000000000000000000000000000000000000000000018'], + [53, '0x000000000000000000000000000000000000000000000000000000000000007c'], + [54, '0x00000000000000000000000000000000000000000000000000000000000000d0'], + [55, '0x00000000000000000000000000000000000000000000000000000000000000ab'], + [56, '0x0000000000000000000000000000000000000000000000000000000000000031'], + [57, '0x00000000000000000000000000000000000000000000000000000000000000d5'], + [58, '0x0000000000000000000000000000000000000000000000000000000000000063'], + [59, '0x0000000000000000000000000000000000000000000000000000000000000084'], + [60, '0x00000000000000000000000000000000000000000000000000000000000000a3'], + [61, '0x00000000000000000000000000000000000000000000000000000000000000a6'], + [62, '0x00000000000000000000000000000000000000000000000000000000000000d5'], + [63, '0x0000000000000000000000000000000000000000000000000000000000000091'], + [64, '0x000000000000000000000000000000000000000000000000000000000000000d'], + [65, '0x000000000000000000000000000000000000000000000000000000000000009c'], + [66, '0x00000000000000000000000000000000000000000000000000000000000000f9'], [67, '0x0000000000000000000000000000000000000000000000000000000000000000'], [68, '0x0000000000000000000000000000000000000000000000000000000000000001'], [69, '0x0000000000000000000000000000000000000000000000000000000000000002'], diff --git a/acvm-repo/barretenberg_blackbox_solver/Cargo.toml b/acvm-repo/barretenberg_blackbox_solver/Cargo.toml index 1a1431c0e42..c189c11cd10 100644 --- a/acvm-repo/barretenberg_blackbox_solver/Cargo.toml +++ b/acvm-repo/barretenberg_blackbox_solver/Cargo.toml @@ -2,7 +2,7 @@ name = "barretenberg_blackbox_solver" description = "A wrapper around a barretenberg WASM binary to execute black box functions for which there is no rust implementation" # x-release-please-start-version -version = "0.29.0" +version = "0.30.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/acvm-repo/barretenberg_blackbox_solver/build.rs b/acvm-repo/barretenberg_blackbox_solver/build.rs index 39db930b9d9..4269c86aba0 100644 --- a/acvm-repo/barretenberg_blackbox_solver/build.rs +++ b/acvm-repo/barretenberg_blackbox_solver/build.rs @@ -1,61 +1,14 @@ -use std::{ - fs::File, - io::{Cursor, Read}, - path::{Path, PathBuf}, -}; +use std::path::PathBuf; -const BARRETENBERG_ARCHIVE: &str = "BARRETENBERG_ARCHIVE"; const BARRETENBERG_BIN_DIR: &str = "BARRETENBERG_BIN_DIR"; -const BARRETENBERG_ARCHIVE_FALLBACK: &str = "https://github.com/AztecProtocol/barretenberg/releases/download/barretenberg-v0.5.0/acvm_backend.wasm.tar.gz"; -// const ARCHIVE_SHA256: &str = "1xpycikqlvsjcryi3hkbc4mwmmdz7zshw6f76vyf1qssq53asyfx"; - -fn unpack_wasm(archive_path: &Path, target_dir: &Path) -> Result<(), String> { - if archive_path.exists() && archive_path.is_file() { - let archive = File::open(archive_path).map_err(|_| "Could not read archive")?; - unpack_archive(archive, target_dir); - - Ok(()) - } else { - Err(format!("Unable to locate {BARRETENBERG_ARCHIVE} - Please set the BARRETENBERG_BIN_DIR env var to the directory where it exists, or ensure it's located at {}", archive_path.display())) - } -} - -fn unpack_archive(archive: T, target_dir: &Path) { - use flate2::read::GzDecoder; - use tar::Archive; - - let gz_decoder = GzDecoder::new(archive); - let mut archive = Archive::new(gz_decoder); - - archive.unpack(target_dir).unwrap(); -} - -/// Try to download the specified URL into a buffer which is returned. -fn download_binary_from_url(url: &str) -> Result>, String> { - let response = reqwest::blocking::get(url).map_err(|error| error.to_string())?; - - let bytes = response.bytes().unwrap(); - Ok(Cursor::new(bytes.to_vec())) -} - fn main() -> Result<(), String> { let out_dir = std::env::var("OUT_DIR").unwrap(); - match std::env::var(BARRETENBERG_ARCHIVE) { - Ok(archive_path) => { - unpack_wasm(&PathBuf::from(archive_path), &PathBuf::from(&out_dir))?; - println!("cargo:rustc-env={BARRETENBERG_BIN_DIR}={out_dir}"); - Ok(()) - } - Err(_) => { - let wasm_bytes = download_binary_from_url(BARRETENBERG_ARCHIVE_FALLBACK) - .expect("download should succeed"); + let dest_path = PathBuf::from(out_dir.clone()).join("acvm_backend.wasm"); - unpack_archive(wasm_bytes, &PathBuf::from(&out_dir)); - println!("cargo:rustc-env={BARRETENBERG_BIN_DIR}={out_dir}"); + println!("cargo:rustc-env={BARRETENBERG_BIN_DIR}={out_dir}"); + std::fs::copy("./src/acvm_backend.wasm", dest_path).unwrap(); - Ok(()) - } - } + Ok(()) } diff --git a/acvm-repo/barretenberg_blackbox_solver/src/acvm_backend.wasm b/acvm-repo/barretenberg_blackbox_solver/src/acvm_backend.wasm new file mode 100755 index 00000000000..bcf3bbf27ee Binary files /dev/null and b/acvm-repo/barretenberg_blackbox_solver/src/acvm_backend.wasm differ diff --git a/acvm-repo/barretenberg_blackbox_solver/src/lib.rs b/acvm-repo/barretenberg_blackbox_solver/src/lib.rs index b9486e97bd9..5d2ab834536 100644 --- a/acvm-repo/barretenberg_blackbox_solver/src/lib.rs +++ b/acvm-repo/barretenberg_blackbox_solver/src/lib.rs @@ -63,15 +63,26 @@ impl BlackBoxFunctionSolver for BarretenbergSolver { }) } - fn pedersen( + fn pedersen_commitment( &self, inputs: &[FieldElement], domain_separator: u32, ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> { #[allow(deprecated)] - self.blackbox_vendor - .encrypt(inputs.to_vec(), domain_separator) - .map_err(|err| BlackBoxResolutionError::Failed(BlackBoxFunc::Pedersen, err.to_string())) + self.blackbox_vendor.encrypt(inputs.to_vec(), domain_separator).map_err(|err| { + BlackBoxResolutionError::Failed(BlackBoxFunc::PedersenCommitment, err.to_string()) + }) + } + + fn pedersen_hash( + &self, + inputs: &[FieldElement], + domain_separator: u32, + ) -> Result { + #[allow(deprecated)] + self.blackbox_vendor.hash(inputs.to_vec(), domain_separator).map_err(|err| { + BlackBoxResolutionError::Failed(BlackBoxFunc::PedersenCommitment, err.to_string()) + }) } fn fixed_base_scalar_mul( diff --git a/acvm-repo/barretenberg_blackbox_solver/src/wasm/pedersen.rs b/acvm-repo/barretenberg_blackbox_solver/src/wasm/pedersen.rs index 05f3f014e64..c19554eb6bc 100644 --- a/acvm-repo/barretenberg_blackbox_solver/src/wasm/pedersen.rs +++ b/acvm-repo/barretenberg_blackbox_solver/src/wasm/pedersen.rs @@ -8,6 +8,8 @@ pub(crate) trait Pedersen { inputs: Vec, hash_index: u32, ) -> Result<(FieldElement, FieldElement), Error>; + + fn hash(&self, inputs: Vec, hash_index: u32) -> Result; } impl Pedersen for Barretenberg { @@ -33,18 +35,36 @@ impl Pedersen for Barretenberg { Ok((point_x, point_y)) } + + fn hash(&self, inputs: Vec, hash_index: u32) -> Result { + let input_buf = Assignments::from(inputs).to_bytes(); + let input_ptr = self.allocate(&input_buf)?; + let result_ptr: usize = 0; + + self.call_multiple( + "pedersen_plookup_compress_with_hash_index", + vec![&input_ptr, &result_ptr.into(), &hash_index.into()], + )?; + + let result_bytes: [u8; FIELD_BYTES] = self.read_memory(result_ptr); + + let hash = FieldElement::from_be_bytes_reduce(&result_bytes); + + Ok(hash) + } } #[test] fn pedersen_hash_to_point() -> Result<(), Error> { let barretenberg = Barretenberg::new(); - let (x, y) = barretenberg.encrypt(vec![FieldElement::zero(), FieldElement::one()], 0)?; + let (x, y) = barretenberg + .encrypt(vec![FieldElement::from(1 as u128), FieldElement::from(1 as u128)], 1)?; let expected_x = FieldElement::from_hex( - "0x0c5e1ddecd49de44ed5e5798d3f6fb7c71fe3d37f5bee8664cf88a445b5ba0af", + "0x12afb43195f5c621d1d2cabb5f629707095c5307fd4185a663d4e80bb083e878", ) .unwrap(); let expected_y = FieldElement::from_hex( - "0x230294a041e26fe80b827c2ef5cb8784642bbaa83842da2714d62b1f3c4f9752", + "0x25793f5b5e62beb92fd18a66050293a9fd554a2ff13bceba0339cae1a038d7c1", ) .unwrap(); diff --git a/acvm-repo/blackbox_solver/Cargo.toml b/acvm-repo/blackbox_solver/Cargo.toml index 3c240b21fee..f1a0444e014 100644 --- a/acvm-repo/blackbox_solver/Cargo.toml +++ b/acvm-repo/blackbox_solver/Cargo.toml @@ -2,7 +2,7 @@ name = "acvm_blackbox_solver" description = "A solver for the blackbox functions found in ACIR and Brillig" # x-release-please-start-version -version = "0.29.0" +version = "0.30.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/acvm-repo/blackbox_solver/src/lib.rs b/acvm-repo/blackbox_solver/src/lib.rs index 0c0fbae2bde..13d0f562415 100644 --- a/acvm-repo/blackbox_solver/src/lib.rs +++ b/acvm-repo/blackbox_solver/src/lib.rs @@ -34,11 +34,16 @@ pub trait BlackBoxFunctionSolver { signature: &[u8], message: &[u8], ) -> Result; - fn pedersen( + fn pedersen_commitment( &self, inputs: &[FieldElement], domain_separator: u32, ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError>; + fn pedersen_hash( + &self, + inputs: &[FieldElement], + domain_separator: u32, + ) -> Result; fn fixed_base_scalar_mul( &self, low: &FieldElement, diff --git a/acvm-repo/brillig/Cargo.toml b/acvm-repo/brillig/Cargo.toml index 2709d71662c..7a2592d6d32 100644 --- a/acvm-repo/brillig/Cargo.toml +++ b/acvm-repo/brillig/Cargo.toml @@ -2,7 +2,7 @@ name = "brillig" description = "Brillig is the bytecode ACIR uses for non-determinism." # x-release-please-start-version -version = "0.29.0" +version = "0.30.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/acvm-repo/brillig/src/black_box.rs b/acvm-repo/brillig/src/black_box.rs index be858a43cee..75fae0a10f0 100644 --- a/acvm-repo/brillig/src/black_box.rs +++ b/acvm-repo/brillig/src/black_box.rs @@ -42,7 +42,9 @@ pub enum BlackBoxOp { result: RegisterIndex, }, /// Calculates a Pedersen commitment to the inputs. - Pedersen { inputs: HeapVector, domain_separator: RegisterIndex, output: HeapArray }, + PedersenCommitment { inputs: HeapVector, domain_separator: RegisterIndex, output: HeapArray }, + /// Calculates a Pedersen hash to the inputs. + PedersenHash { inputs: HeapVector, domain_separator: RegisterIndex, output: RegisterIndex }, /// Performs scalar multiplication over the embedded curve. FixedBaseScalarMul { low: RegisterIndex, high: RegisterIndex, result: HeapArray }, } diff --git a/acvm-repo/brillig_vm/Cargo.toml b/acvm-repo/brillig_vm/Cargo.toml index 95a89b7275d..ac826585b5c 100644 --- a/acvm-repo/brillig_vm/Cargo.toml +++ b/acvm-repo/brillig_vm/Cargo.toml @@ -2,7 +2,7 @@ name = "brillig_vm" description = "The virtual machine that processes Brillig bytecode, used to introduce non-determinism to the ACVM" # x-release-please-start-version -version = "0.29.0" +version = "0.30.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/acvm-repo/brillig_vm/src/arithmetic.rs b/acvm-repo/brillig_vm/src/arithmetic.rs index 51ab8660452..263a733e3c4 100644 --- a/acvm-repo/brillig_vm/src/arithmetic.rs +++ b/acvm-repo/brillig_vm/src/arithmetic.rs @@ -25,18 +25,28 @@ pub(crate) fn evaluate_binary_bigint_op( a: BigUint, b: BigUint, bit_size: u32, -) -> BigUint { +) -> Result { let bit_modulo = &(BigUint::one() << bit_size); - match op { + let result = match op { // Perform addition, subtraction, and multiplication, applying a modulo operation to keep the result within the bit size. BinaryIntOp::Add => (a + b) % bit_modulo, BinaryIntOp::Sub => (bit_modulo + a - b) % bit_modulo, BinaryIntOp::Mul => (a * b) % bit_modulo, // Perform unsigned division using the modulo operation on a and b. - BinaryIntOp::UnsignedDiv => (a % bit_modulo) / (b % bit_modulo), + BinaryIntOp::UnsignedDiv => { + let b_mod = b % bit_modulo; + if b_mod.is_zero() { + return Err("Division by zero".to_owned()); + } + (a % bit_modulo) / b_mod + } // Perform signed division by first converting a and b to signed integers and then back to unsigned after the operation. BinaryIntOp::SignedDiv => { - let signed_div = to_big_signed(a, bit_size) / to_big_signed(b, bit_size); + let b_signed = to_big_signed(b, bit_size); + if b_signed.is_zero() { + return Err("Division by zero".to_owned()); + } + let signed_div = to_big_signed(a, bit_size) / b_signed; to_big_unsigned(signed_div, bit_size) } // Perform a == operation, returning 0 or 1 @@ -77,7 +87,9 @@ pub(crate) fn evaluate_binary_bigint_op( let b = b.to_u128().unwrap(); (a >> b) % bit_modulo } - } + }; + + Ok(result) } fn to_big_signed(a: BigUint, bit_size: u32) -> BigInt { @@ -111,7 +123,7 @@ mod tests { // Convert to big integers let lhs_big = BigUint::from(a); let rhs_big = BigUint::from(b); - let result_value = evaluate_binary_bigint_op(op, lhs_big, rhs_big, bit_size); + let result_value = evaluate_binary_bigint_op(op, lhs_big, rhs_big, bit_size).unwrap(); // Convert back to u128 result_value.to_u128().unwrap() } diff --git a/acvm-repo/brillig_vm/src/black_box.rs b/acvm-repo/brillig_vm/src/black_box.rs index ada8a2f5993..66d40c48aec 100644 --- a/acvm-repo/brillig_vm/src/black_box.rs +++ b/acvm-repo/brillig_vm/src/black_box.rs @@ -147,20 +147,34 @@ pub(crate) fn evaluate_black_box( memory.write_slice(registers.get(result.pointer).to_usize(), &[x.into(), y.into()]); Ok(()) } - BlackBoxOp::Pedersen { inputs, domain_separator, output } => { + BlackBoxOp::PedersenCommitment { inputs, domain_separator, output } => { let inputs: Vec = read_heap_vector(memory, registers, inputs).iter().map(|x| x.to_field()).collect(); let domain_separator: u32 = registers.get(*domain_separator).to_u128().try_into().map_err(|_| { BlackBoxResolutionError::Failed( - BlackBoxFunc::Pedersen, + BlackBoxFunc::PedersenCommitment, "Invalid signature length".to_string(), ) })?; - let (x, y) = solver.pedersen(&inputs, domain_separator)?; + let (x, y) = solver.pedersen_commitment(&inputs, domain_separator)?; memory.write_slice(registers.get(output.pointer).to_usize(), &[x.into(), y.into()]); Ok(()) } + BlackBoxOp::PedersenHash { inputs, domain_separator, output } => { + let inputs: Vec = + read_heap_vector(memory, registers, inputs).iter().map(|x| x.to_field()).collect(); + let domain_separator: u32 = + registers.get(*domain_separator).to_u128().try_into().map_err(|_| { + BlackBoxResolutionError::Failed( + BlackBoxFunc::PedersenCommitment, + "Invalid signature length".to_string(), + ) + })?; + let hash = solver.pedersen_hash(&inputs, domain_separator)?; + registers.set(*output, hash.into()); + Ok(()) + } } } diff --git a/acvm-repo/brillig_vm/src/lib.rs b/acvm-repo/brillig_vm/src/lib.rs index 48f6bf5f1c4..e0d30f7b2e0 100644 --- a/acvm-repo/brillig_vm/src/lib.rs +++ b/acvm-repo/brillig_vm/src/lib.rs @@ -177,8 +177,12 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { self.increment_program_counter() } Opcode::BinaryIntOp { op, bit_size, lhs, rhs, destination: result } => { - self.process_binary_int_op(*op, *bit_size, *lhs, *rhs, *result); - self.increment_program_counter() + if let Err(error) = self.process_binary_int_op(*op, *bit_size, *lhs, *rhs, *result) + { + self.fail(error) + } else { + self.increment_program_counter() + } } Opcode::Jump { location: destination } => self.set_program_counter(*destination), Opcode::JumpIf { condition, location: destination } => { @@ -391,17 +395,18 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { lhs: RegisterIndex, rhs: RegisterIndex, result: RegisterIndex, - ) { + ) -> Result<(), String> { let lhs_value = self.registers.get(lhs); let rhs_value = self.registers.get(rhs); // Convert to big integers let lhs_big = BigUint::from_bytes_be(&lhs_value.to_field().to_be_bytes()); let rhs_big = BigUint::from_bytes_be(&rhs_value.to_field().to_be_bytes()); - let result_value = evaluate_binary_bigint_op(&op, lhs_big, rhs_big, bit_size); + let result_value = evaluate_binary_bigint_op(&op, lhs_big, rhs_big, bit_size)?; // Convert back to field element self.registers .set(result, FieldElement::from_be_bytes_reduce(&result_value.to_bytes_be()).into()); + Ok(()) } } @@ -417,13 +422,20 @@ impl BlackBoxFunctionSolver for DummyBlackBoxSolver { ) -> Result { Ok(true) } - fn pedersen( + fn pedersen_commitment( &self, _inputs: &[FieldElement], _domain_separator: u32, ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> { Ok((2_u128.into(), 3_u128.into())) } + fn pedersen_hash( + &self, + _inputs: &[FieldElement], + _domain_separator: u32, + ) -> Result { + Ok(6_u128.into()) + } fn fixed_base_scalar_mul( &self, _low: &FieldElement, diff --git a/acvm-repo/stdlib/Cargo.toml b/acvm-repo/stdlib/Cargo.toml index 96e11a5aaf2..daba0fe9bba 100644 --- a/acvm-repo/stdlib/Cargo.toml +++ b/acvm-repo/stdlib/Cargo.toml @@ -2,7 +2,7 @@ name = "acvm_stdlib" description = "The ACVM standard library." # x-release-please-start-version -version = "0.29.0" +version = "0.30.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/compiler/integration-tests/circuits/recursion/src/main.nr b/compiler/integration-tests/circuits/recursion/src/main.nr index ffa3ccef90e..bc1f3e76d1d 100644 --- a/compiler/integration-tests/circuits/recursion/src/main.nr +++ b/compiler/integration-tests/circuits/recursion/src/main.nr @@ -6,15 +6,9 @@ fn main( public_inputs : [Field; 1], key_hash : Field, input_aggregation_object : [Field; 16], -) -> pub [Field;16]{ +) -> pub [Field; 16] { let vk : [Field] = verification_key; let p : [Field] = proof; let pi : [Field] = public_inputs; - std::verify_proof( - vk, - p, - pi, - key_hash, - input_aggregation_object - ) + std::verify_proof(vk, p, pi, key_hash, input_aggregation_object) } diff --git a/compiler/noirc_driver/Cargo.toml b/compiler/noirc_driver/Cargo.toml index f1c120e1687..31c0f695d0f 100644 --- a/compiler/noirc_driver/Cargo.toml +++ b/compiler/noirc_driver/Cargo.toml @@ -17,6 +17,7 @@ noirc_frontend.workspace = true noirc_evaluator.workspace = true noirc_abi.workspace = true acvm.workspace = true +iter-extended.workspace = true fm.workspace = true serde.workspace = true base64.workspace = true diff --git a/compiler/noirc_driver/src/contract.rs b/compiler/noirc_driver/src/contract.rs index da097d4cb86..d8688780a1f 100644 --- a/compiler/noirc_driver/src/contract.rs +++ b/compiler/noirc_driver/src/contract.rs @@ -5,6 +5,7 @@ use acvm::acir::circuit::Circuit; use fm::FileId; use noirc_abi::{Abi, ContractEvent}; use noirc_errors::debug_info::DebugInfo; +use noirc_evaluator::errors::SsaReport; use super::debug::DebugFile; use crate::program::{deserialize_circuit, serialize_circuit}; @@ -42,6 +43,7 @@ pub struct CompiledContract { pub events: Vec, pub file_map: BTreeMap, + pub warnings: Vec, } /// Each function in the contract will be compiled diff --git a/compiler/noirc_driver/src/lib.rs b/compiler/noirc_driver/src/lib.rs index f8908efc596..8ec92597a7c 100644 --- a/compiler/noirc_driver/src/lib.rs +++ b/compiler/noirc_driver/src/lib.rs @@ -6,6 +6,7 @@ use clap::Args; use debug::filter_relevant_files; use fm::FileId; +use iter_extended::vecmap; use noirc_abi::{AbiParameter, AbiType, ContractEvent}; use noirc_errors::{CustomDiagnostic, FileDiagnostic}; use noirc_evaluator::errors::RuntimeError; @@ -164,7 +165,7 @@ pub fn compile_main( cached_program: Option, force_compile: bool, ) -> CompilationResult { - let (_, warnings) = check_crate(context, crate_id, options.deny_warnings)?; + let (_, mut warnings) = check_crate(context, crate_id, options.deny_warnings)?; let main = match context.get_main_function(&crate_id) { Some(m) => m, @@ -180,6 +181,11 @@ pub fn compile_main( let compiled_program = compile_no_check(context, options, main, cached_program, force_compile) .map_err(FileDiagnostic::from)?; + let compilation_warnings = vecmap(compiled_program.warnings.clone(), FileDiagnostic::from); + if options.deny_warnings && !compilation_warnings.is_empty() { + return Err(compilation_warnings); + } + warnings.extend(compilation_warnings); if options.print_acir { println!("Compiled ACIR for main (unoptimized):"); @@ -259,6 +265,7 @@ fn compile_contract_inner( ) -> Result { let mut functions = Vec::new(); let mut errors = Vec::new(); + let mut warnings = Vec::new(); for contract_function in &contract.functions { let function_id = contract_function.function_id; let is_entry_point = contract_function.is_entry_point; @@ -280,6 +287,7 @@ fn compile_contract_inner( continue; } }; + warnings.extend(function.warnings); let modifiers = context.def_interner.function_modifiers(&function_id); let func_type = modifiers .contract_function_type @@ -315,6 +323,7 @@ fn compile_contract_inner( functions, file_map, noir_version: NOIR_ARTIFACT_VERSION_STRING.to_string(), + warnings, }) } else { Err(errors) @@ -347,7 +356,7 @@ pub fn compile_no_check( } } - let (circuit, debug, abi) = + let (circuit, debug, abi, warnings) = create_circuit(context, program, options.show_ssa, options.show_brillig)?; let file_map = filter_relevant_files(&[debug.clone()], &context.file_manager); @@ -359,5 +368,6 @@ pub fn compile_no_check( abi, file_map, noir_version: NOIR_ARTIFACT_VERSION_STRING.to_string(), + warnings, }) } diff --git a/compiler/noirc_driver/src/program.rs b/compiler/noirc_driver/src/program.rs index fe991567180..a940f6b20b8 100644 --- a/compiler/noirc_driver/src/program.rs +++ b/compiler/noirc_driver/src/program.rs @@ -5,6 +5,7 @@ use fm::FileId; use base64::Engine; use noirc_errors::debug_info::DebugInfo; +use noirc_evaluator::errors::SsaReport; use serde::{de::Error as DeserializationError, ser::Error as SerializationError}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; @@ -24,6 +25,7 @@ pub struct CompiledProgram { pub abi: noirc_abi::Abi, pub debug: DebugInfo, pub file_map: BTreeMap, + pub warnings: Vec, } pub(crate) fn serialize_circuit(circuit: &Circuit, s: S) -> Result diff --git a/compiler/noirc_evaluator/Cargo.toml b/compiler/noirc_evaluator/Cargo.toml index d70c9d3179e..c9f5f28478b 100644 --- a/compiler/noirc_evaluator/Cargo.toml +++ b/compiler/noirc_evaluator/Cargo.toml @@ -16,4 +16,5 @@ fxhash.workspace = true iter-extended.workspace = true thiserror.workspace = true num-bigint = "0.4" -im = "15.1" +im = { version = "15.1", features = ["serde"] } +serde.workspace = true diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs index e1d3f333f59..0d97dd12601 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs @@ -92,14 +92,14 @@ pub(crate) fn convert_black_box_call( ) } } - BlackBoxFunc::Pedersen => { + BlackBoxFunc::PedersenCommitment => { if let ( [message, RegisterOrMemory::RegisterIndex(domain_separator)], [RegisterOrMemory::HeapArray(result_array)], ) = (function_arguments, function_results) { let message_vector = convert_array_or_vector(brillig_context, message, bb_func); - brillig_context.black_box_op_instruction(BlackBoxOp::Pedersen { + brillig_context.black_box_op_instruction(BlackBoxOp::PedersenCommitment { inputs: message_vector, domain_separator: *domain_separator, output: *result_array, @@ -108,6 +108,22 @@ pub(crate) fn convert_black_box_call( unreachable!("ICE: Pedersen expects one array argument, a register for the domain separator, and one array result") } } + BlackBoxFunc::PedersenHash => { + if let ( + [message, RegisterOrMemory::RegisterIndex(domain_separator)], + [RegisterOrMemory::RegisterIndex(result)], + ) = (function_arguments, function_results) + { + let message_vector = convert_array_or_vector(brillig_context, message, bb_func); + brillig_context.black_box_op_instruction(BlackBoxOp::PedersenHash { + inputs: message_vector, + domain_separator: *domain_separator, + output: *result, + }); + } else { + unreachable!("ICE: Pedersen hash expects one array argument, a register for the domain separator, and one register result") + } + } BlackBoxFunc::SchnorrVerify => { if let ( [RegisterOrMemory::RegisterIndex(public_key_x), RegisterOrMemory::RegisterIndex(public_key_y), RegisterOrMemory::HeapArray(signature), message], diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir.rs index a4ea0362f06..880ae95dcd7 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir.rs @@ -1032,13 +1032,20 @@ pub(crate) mod tests { ) -> Result { Ok(true) } - fn pedersen( + fn pedersen_commitment( &self, _inputs: &[FieldElement], _domain_separator: u32, ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> { Ok((2_u128.into(), 3_u128.into())) } + fn pedersen_hash( + &self, + _inputs: &[FieldElement], + _domain_separator: u32, + ) -> Result { + Ok(6_u128.into()) + } fn fixed_base_scalar_mul( &self, _low: &FieldElement, diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs index cc13b959095..65db47dd2e0 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs @@ -404,7 +404,7 @@ impl DebugShow { result ); } - BlackBoxOp::Pedersen { inputs, domain_separator, output } => { + BlackBoxOp::PedersenCommitment { inputs, domain_separator, output } => { debug_println!( self.enable_debug_trace, " PEDERSEN {} {} -> {}", @@ -413,6 +413,15 @@ impl DebugShow { output ); } + BlackBoxOp::PedersenHash { inputs, domain_separator, output } => { + debug_println!( + self.enable_debug_trace, + " PEDERSEN_HASH {} {} -> {}", + inputs, + domain_separator, + output + ); + } BlackBoxOp::SchnorrVerify { public_key_x, public_key_y, diff --git a/compiler/noirc_evaluator/src/errors.rs b/compiler/noirc_evaluator/src/errors.rs index 3dc0194c8be..2429ca9e194 100644 --- a/compiler/noirc_evaluator/src/errors.rs +++ b/compiler/noirc_evaluator/src/errors.rs @@ -7,12 +7,13 @@ //! An Error of the former is a user Error //! //! An Error of the latter is an error in the implementation of the compiler -use acvm::acir::native_types::Expression; +use acvm::{acir::native_types::Expression, FieldElement}; use iter_extended::vecmap; use noirc_errors::{CustomDiagnostic as Diagnostic, FileDiagnostic}; use thiserror::Error; -use crate::ssa::ir::dfg::CallStack; +use crate::ssa::ir::{dfg::CallStack, types::NumericType}; +use serde::{Deserialize, Serialize}; #[derive(Debug, PartialEq, Eq, Clone, Error)] pub enum RuntimeError { @@ -29,6 +30,8 @@ pub enum RuntimeError { IndexOutOfBounds { index: usize, array_size: usize, call_stack: CallStack }, #[error("Range constraint of {num_bits} bits is too large for the Field size")] InvalidRangeConstraint { num_bits: u32, call_stack: CallStack }, + #[error("{value} does not fit within the type bounds for {typ}")] + IntegerOutOfBounds { value: FieldElement, typ: NumericType, call_stack: CallStack }, #[error("Expected array index to fit into a u64")] TypeConversion { from: String, into: String, call_stack: CallStack }, #[error("{name:?} is not initialized")] @@ -51,6 +54,37 @@ fn format_failed_constraint(message: &Option) -> String { } } +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum SsaReport { + Warning(InternalWarning), +} + +impl From for FileDiagnostic { + fn from(error: SsaReport) -> FileDiagnostic { + match error { + SsaReport::Warning(warning) => { + let InternalWarning::ReturnConstant { ref call_stack } = warning; + let call_stack = vecmap(call_stack, |location| *location); + let file_id = call_stack.last().map(|location| location.file).unwrap_or_default(); + let message = warning.to_string(); + let location = call_stack.last().expect("Expected RuntimeError to have a location"); + let diagnostic = Diagnostic::simple_warning( + message, + "constant value".to_string(), + location.span, + ); + diagnostic.in_file(file_id).with_call_stack(call_stack) + } + } + } +} + +#[derive(Debug, PartialEq, Eq, Clone, Error, Serialize, Deserialize)] +pub enum InternalWarning { + #[error("Returning a constant value is not allowed")] + ReturnConstant { call_stack: CallStack }, +} + #[derive(Debug, PartialEq, Eq, Clone, Error)] pub enum InternalError { #[error("ICE: Both expressions should have degree<=1")] @@ -67,8 +101,6 @@ pub enum InternalError { UndeclaredAcirVar { call_stack: CallStack }, #[error("ICE: Expected {expected:?}, found {found:?}")] UnExpected { expected: String, found: String, call_stack: CallStack }, - #[error("Returning a constant value is not allowed")] - ReturnConstant { call_stack: CallStack }, } impl RuntimeError { @@ -81,8 +113,7 @@ impl RuntimeError { | InternalError::MissingArg { call_stack, .. } | InternalError::NotAConstant { call_stack, .. } | InternalError::UndeclaredAcirVar { call_stack } - | InternalError::UnExpected { call_stack, .. } - | InternalError::ReturnConstant { call_stack, .. }, + | InternalError::UnExpected { call_stack, .. }, ) | RuntimeError::FailedConstraint { call_stack, .. } | RuntimeError::IndexOutOfBounds { call_stack, .. } @@ -91,6 +122,7 @@ impl RuntimeError { | RuntimeError::UnInitialized { call_stack, .. } | RuntimeError::UnknownLoopBound { call_stack } | RuntimeError::AssertConstantFailed { call_stack } + | RuntimeError::IntegerOutOfBounds { call_stack, .. } | RuntimeError::UnsupportedIntegerSize { call_stack, .. } => call_stack, } } @@ -108,12 +140,6 @@ impl From for FileDiagnostic { impl RuntimeError { fn into_diagnostic(self) -> Diagnostic { match self { - RuntimeError::InternalError(InternalError::ReturnConstant { ref call_stack }) => { - let message = self.to_string(); - let location = - call_stack.back().expect("Expected RuntimeError to have a location"); - Diagnostic::simple_error(message, "constant value".to_string(), location.span) - } RuntimeError::InternalError(cause) => { Diagnostic::simple_error( "Internal Consistency Evaluators Errors: \n diff --git a/compiler/noirc_evaluator/src/ssa.rs b/compiler/noirc_evaluator/src/ssa.rs index 131cf30a510..ff13878e129 100644 --- a/compiler/noirc_evaluator/src/ssa.rs +++ b/compiler/noirc_evaluator/src/ssa.rs @@ -9,7 +9,7 @@ use std::collections::BTreeSet; -use crate::errors::RuntimeError; +use crate::errors::{RuntimeError, SsaReport}; use acvm::acir::{ circuit::{Circuit, PublicInputs}, native_types::Witness, @@ -39,7 +39,7 @@ pub(crate) fn optimize_into_acir( print_brillig_trace: bool, ) -> Result { let abi_distinctness = program.return_distinctness; - let ssa = SsaBuilder::new(program, print_ssa_passes) + let ssa = SsaBuilder::new(program, print_ssa_passes)? .run_pass(Ssa::defunctionalize, "After Defunctionalization:") .run_pass(Ssa::inline_functions, "After Inlining:") // Run mem2reg with the CFG separated into blocks @@ -72,7 +72,7 @@ pub fn create_circuit( program: Program, enable_ssa_logging: bool, enable_brillig_logging: bool, -) -> Result<(Circuit, DebugInfo, Abi), RuntimeError> { +) -> Result<(Circuit, DebugInfo, Abi, Vec), RuntimeError> { let func_sig = program.main_function_signature.clone(); let mut generated_acir = optimize_into_acir(program, enable_ssa_logging, enable_brillig_logging)?; @@ -83,6 +83,7 @@ pub fn create_circuit( locations, input_witnesses, assert_messages, + warnings, .. } = generated_acir; @@ -119,7 +120,7 @@ pub fn create_circuit( let (optimized_circuit, transformation_map) = acvm::compiler::optimize(circuit); debug_info.update_acir(transformation_map); - Ok((optimized_circuit, debug_info, abi)) + Ok((optimized_circuit, debug_info, abi, warnings)) } // This is just a convenience object to bundle the ssa with `print_ssa_passes` for debug printing. @@ -129,8 +130,9 @@ struct SsaBuilder { } impl SsaBuilder { - fn new(program: Program, print_ssa_passes: bool) -> SsaBuilder { - SsaBuilder { print_ssa_passes, ssa: ssa_gen::generate_ssa(program) }.print("Initial SSA:") + fn new(program: Program, print_ssa_passes: bool) -> Result { + let ssa = ssa_gen::generate_ssa(program)?; + Ok(SsaBuilder { print_ssa_passes, ssa }.print("Initial SSA:")) } fn finish(self) -> Ssa { diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs index d846ede566f..8d0111fc8e3 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs @@ -1,7 +1,7 @@ use super::generated_acir::GeneratedAcir; use crate::brillig::brillig_gen::brillig_directive; use crate::brillig::brillig_ir::artifact::GeneratedBrillig; -use crate::errors::{InternalError, RuntimeError}; +use crate::errors::{InternalError, RuntimeError, SsaReport}; use crate::ssa::acir_gen::{AcirDynamicArray, AcirValue}; use crate::ssa::ir::dfg::CallStack; use crate::ssa::ir::types::Type as SsaType; @@ -22,6 +22,7 @@ use acvm::{ use acvm::{BlackBoxFunctionSolver, BlackBoxResolutionError}; use fxhash::FxHashMap as HashMap; use iter_extended::{try_vecmap, vecmap}; +use num_bigint::BigUint; use std::{borrow::Cow, hash::Hash}; #[derive(Clone, Debug, PartialEq, Eq, Hash)] @@ -427,20 +428,10 @@ impl AcirContext { let diff_expr = &lhs_expr - &rhs_expr; // Check to see if equality can be determined at compile-time. - if diff_expr.is_const() { - if diff_expr.is_zero() { - // Constraint is always true - assertion is unnecessary. - self.mark_variables_equivalent(lhs, rhs)?; - return Ok(()); - } else { - // Constraint is always false - this program is unprovable. - return Err(RuntimeError::FailedConstraint { - lhs: Box::new(lhs_expr), - rhs: Box::new(rhs_expr), - call_stack: self.get_call_stack(), - assert_message, - }); - }; + if diff_expr.is_zero() { + // Constraint is always true - assertion is unnecessary. + self.mark_variables_equivalent(lhs, rhs)?; + return Ok(()); } self.acir_ir.assert_is_zero(diff_expr); @@ -539,6 +530,20 @@ impl AcirContext { Ok(self.add_data(AcirVarData::from(sum_expr))) } + /// Adds a new Variable to context whose value will + /// be constrained to be the expression `lhs + k * rhs` + fn add_mul_var( + &mut self, + lhs: AcirVar, + k: FieldElement, + rhs: AcirVar, + ) -> Result { + let k_var = self.add_constant(k); + + let intermediate = self.mul_var(k_var, rhs)?; + self.add_var(lhs, intermediate) + } + /// Adds a new variable that is constrained to be the logical NOT of `x`. pub(crate) fn not_var(&mut self, x: AcirVar, typ: AcirType) -> Result { let bit_size = typ.bit_size(); @@ -555,19 +560,243 @@ impl AcirContext { bit_size: u32, predicate: AcirVar, ) -> Result<(AcirVar, AcirVar), RuntimeError> { - let lhs_expr = self.var_to_expression(lhs)?; + // lhs = rhs * q + r + // + // If predicate is zero, `q_witness` and `r_witness` will be 0 + let zero = self.add_constant(FieldElement::zero()); + if self.var_to_expression(predicate)?.is_zero() { + return Ok((zero, zero)); + } + + match (self.var_to_expression(lhs)?.to_const(), self.var_to_expression(rhs)?.to_const()) { + // If `lhs` and `rhs` are known constants then we can calculate the result at compile time. + // `rhs` must be non-zero. + (Some(lhs_const), Some(rhs_const)) if rhs_const != FieldElement::zero() => { + let quotient = lhs_const.to_u128() / rhs_const.to_u128(); + let remainder = lhs_const.to_u128() - quotient * rhs_const.to_u128(); + + let quotient_var = self.add_constant(FieldElement::from(quotient)); + let remainder_var = self.add_constant(FieldElement::from(remainder)); + return Ok((quotient_var, remainder_var)); + } + + // If `rhs` is one then the division is a noop. + (_, Some(rhs_const)) if rhs_const == FieldElement::one() => { + return Ok((lhs, zero)); + } + + _ => (), + } + + // Check that we the rhs is not zero. + // Otherwise, when executing the brillig quotient we may attempt to divide by zero, causing a VM panic. + // + // When the predicate is 0, the equation always passes. + // When the predicate is 1, the rhs must not be 0. + let one = self.add_constant(FieldElement::one()); + let rhs_expr = self.var_to_expression(rhs)?; - let predicate_expr = self.var_to_expression(predicate)?; + let rhs_is_nonzero_const = rhs_expr.is_const() && !rhs_expr.is_zero(); + if !rhs_is_nonzero_const { + match self.var_to_expression(predicate)?.to_const() { + Some(predicate) if predicate.is_one() => { + // If the predicate is known to be active, we simply assert that an inverse must exist. + // This implies that `rhs != 0`. + let _inverse = self.inv_var(rhs, one)?; + } - let (quotient, remainder) = - self.acir_ir.euclidean_division(&lhs_expr, &rhs_expr, bit_size, &predicate_expr)?; + _ => { + // Otherwise we must handle both potential cases. + let rhs_is_zero = self.eq_var(rhs, zero)?; + let rhs_is_not_zero = self.mul_var(rhs_is_zero, predicate)?; + self.assert_eq_var(rhs_is_not_zero, zero, None)?; + } + } + } - let quotient_var = self.add_data(AcirVarData::Witness(quotient)); - let remainder_var = self.add_data(AcirVarData::Witness(remainder)); + // maximum bit size for q and for [r and rhs] + let mut max_q_bits = bit_size; + let mut max_rhs_bits = bit_size; + // when rhs is constant, we can better estimate the maximum bit sizes + if let Some(rhs_const) = self.var_to_expression(rhs)?.to_const() { + max_rhs_bits = rhs_const.num_bits(); + if max_rhs_bits != 0 { + if max_rhs_bits > bit_size { + return Ok((zero, zero)); + } + max_q_bits = bit_size - max_rhs_bits + 1; + } + } + + // Avoids overflow: 'q*b+r < 2^max_q_bits*2^max_rhs_bits' + let mut avoid_overflow = false; + if max_q_bits + max_rhs_bits >= FieldElement::max_num_bits() - 1 { + // q*b+r can overflow; we avoid this when b is constant + if self.var_to_expression(rhs)?.is_const() { + avoid_overflow = true; + } else { + // we do not support unbounded division + unreachable!("overflow in unbounded division"); + } + } + + let [q_value, r_value]: [AcirValue; 2] = self + .brillig( + predicate, + brillig_directive::directive_quotient(bit_size + 1), + vec![ + AcirValue::Var(lhs, AcirType::unsigned(bit_size)), + AcirValue::Var(rhs, AcirType::unsigned(bit_size)), + ], + vec![AcirType::unsigned(max_q_bits), AcirType::unsigned(max_rhs_bits)], + )? + .try_into() + .expect("quotient only returns two values"); + let quotient_var = q_value.into_var()?; + let remainder_var = r_value.into_var()?; + + // Constrain `q < 2^{max_q_bits}`. + self.range_constrain_var(quotient_var, &NumericType::Unsigned { bit_size: max_q_bits })?; + + // Constrain `r < 2^{max_rhs_bits}`. + // + // If `rhs` is a power of 2, then is just a looser version of the following bound constraint. + // In the case where `rhs` isn't a power of 2 then this range constraint is required + // as the bound constraint creates a new witness. + // This opcode will be optimized out if it is redundant so we always add it for safety. + self.range_constrain_var(remainder_var, &NumericType::Unsigned { bit_size: max_rhs_bits })?; + + // Constrain `r < rhs`. + self.bound_constraint_with_offset(remainder_var, rhs, predicate, max_rhs_bits)?; + + // a * predicate == (b * q + r) * predicate + // => predicate * (a - b * q - r) == 0 + // When the predicate is 0, the equation always passes. + // When the predicate is 1, the euclidean division needs to be + // true. + let rhs_constraint = self.mul_var(rhs, quotient_var)?; + let rhs_constraint = self.add_var(rhs_constraint, remainder_var)?; + let rhs_constraint = self.mul_var(rhs_constraint, predicate)?; + + let lhs_constraint = self.mul_var(lhs, predicate)?; + self.assert_eq_var(lhs_constraint, rhs_constraint, None)?; + + if let Some(rhs_const) = self.var_to_expression(rhs)?.to_const() { + if avoid_overflow { + // we compute q0 = p/rhs + let rhs_big = BigUint::from_bytes_be(&rhs_const.to_be_bytes()); + let q0_big = FieldElement::modulus() / &rhs_big; + let q0 = FieldElement::from_be_bytes_reduce(&q0_big.to_bytes_be()); + let q0_var = self.add_constant(q0); + // when q == q0, b*q+r can overflow so we need to bound r to avoid the overflow. + + let size_predicate = self.eq_var(q0_var, quotient_var)?; + let predicate = self.mul_var(size_predicate, predicate)?; + // Ensure that there is no overflow, under q == q0 predicate + let max_r_big = FieldElement::modulus() - q0_big * rhs_big; + let max_r = FieldElement::from_be_bytes_reduce(&max_r_big.to_bytes_be()); + let max_r_var = self.add_constant(max_r); + + let max_r_predicate = self.mul_var(predicate, max_r_var)?; + let r_predicate = self.mul_var(remainder_var, predicate)?; + // Bound the remainder to be rhs, rhs-lhs = p+rhs-lhs > p-2^bits >= 2^bits (if log(p) >= bits + 1) + /// n.b: we do NOT check here that lhs and rhs are indeed 'bits' size + /// lhs < rhs <=> a+1<=b + /// TODO: Consolidate this with bounds_check function. + pub(super) fn bound_constraint_with_offset( + &mut self, + lhs: AcirVar, + rhs: AcirVar, + offset: AcirVar, + bits: u32, + ) -> Result<(), RuntimeError> { + const fn num_bits() -> usize { + std::mem::size_of::() * 8 + } + + fn bit_size_u128(a: u128) -> u32 where { + num_bits::() as u32 - a.leading_zeros() + } + + assert!( + bits < FieldElement::max_num_bits(), + "range check with bit size of the prime field is not implemented yet" + ); + + let mut lhs_offset = self.add_var(lhs, offset)?; + + // Optimization when rhs is const and fits within a u128 + let rhs_expr = self.var_to_expression(rhs)?; + if rhs_expr.is_const() && rhs_expr.q_c.fits_in_u128() { + // We try to move the offset to rhs + let rhs_offset = if self.is_constant_one(&offset) && rhs_expr.q_c.to_u128() >= 1 { + lhs_offset = lhs; + rhs_expr.q_c.to_u128() - 1 + } else { + rhs_expr.q_c.to_u128() + }; + // we now have lhs+offset <= rhs <=> lhs_offset <= rhs_offset + + let bit_size = bit_size_u128(rhs_offset); + // r = 2^bit_size - rhs_offset -1, is of bit size 'bit_size' by construction + let r = (1_u128 << bit_size) - rhs_offset - 1; + // however, since it is a constant, we can compute it's actual bit size + let r_bit_size = bit_size_u128(r); + // witness = lhs_offset + r + assert!(bits + r_bit_size < FieldElement::max_num_bits()); //we need to ensure lhs_offset + r does not overflow + + let r_var = self.add_constant(r.into()); + let aor = self.add_var(lhs_offset, r_var)?; + // lhs_offset<=rhs_offset <=> lhs_offset + r < rhs_offset + r = 2^bit_size <=> witness < 2^bit_size + self.range_constrain_var(aor, &NumericType::Unsigned { bit_size })?; + return Ok(()); + } + // General case: lhs_offset<=rhs <=> rhs-lhs_offset>=0 <=> rhs-lhs_offset is a 'bits' bit integer + let sub_expression = self.sub_var(rhs, lhs_offset)?; //rhs-lhs_offset + self.range_constrain_var(sub_expression, &NumericType::Unsigned { bit_size: bits })?; + + Ok(()) + } + + // Returns the 2-complement of lhs, using the provided sign bit in 'leading' + // if leading is zero, it returns lhs + // if leading is one, it returns 2^bit_size-lhs + fn two_complement( + &mut self, + lhs: AcirVar, + leading: AcirVar, + max_bit_size: u32, + ) -> Result { + let max_power_of_two = self.add_constant( + FieldElement::from(2_i128).pow(&FieldElement::from(max_bit_size as i128 - 1)), + ); + + let intermediate = self.sub_var(max_power_of_two, lhs)?; + let intermediate = self.mul_var(intermediate, leading)?; + + self.add_mul_var(lhs, FieldElement::from(2_i128), intermediate) + } + /// Returns the quotient and remainder such that lhs = rhs * quotient + remainder /// and |remainder| < |rhs| /// and remainder has the same sign than lhs @@ -578,14 +807,46 @@ impl AcirContext { rhs: AcirVar, bit_size: u32, ) -> Result<(AcirVar, AcirVar), RuntimeError> { - let l_witness = self.var_to_witness(lhs)?; - let r_witness = self.var_to_witness(rhs)?; + // We derive the signed division from the unsigned euclidean division. + // note that this is not euclidean division! + // If `x` is a signed integer, then `sign(x)x >= 0` + // so if `a` and `b` are signed integers, we can do the unsigned division: + // `sign(a)a = q1*sign(b)b + r1` + // => `a = sign(a)sign(b)q1*b + sign(a)r1` + // => `a = qb+r`, with `|r|<|b|` and `a` and `r` have the same sign. assert_ne!(bit_size, 0, "signed integer should have at least one bit"); - let (q, r) = - self.acir_ir.signed_division(&l_witness.into(), &r_witness.into(), bit_size)?; - Ok((self.add_data(q.into()), self.add_data(r.into()))) + // 2^{max_bit size-1} + let max_power_of_two = self.add_constant( + FieldElement::from(2_i128).pow(&FieldElement::from(bit_size as i128 - 1)), + ); + let one = self.add_constant(FieldElement::one()); + + // Get the sign bit of rhs by computing rhs / max_power_of_two + let (rhs_leading, _) = self.euclidean_division_var(rhs, max_power_of_two, bit_size, one)?; + + // Get the sign bit of lhs by computing lhs / max_power_of_two + let (lhs_leading, _) = self.euclidean_division_var(lhs, max_power_of_two, bit_size, one)?; + + // Signed to unsigned: + let unsigned_lhs = self.two_complement(lhs, lhs_leading, bit_size)?; + let unsigned_rhs = self.two_complement(rhs, rhs_leading, bit_size)?; + + // Performs the division using the unsigned values of lhs and rhs + let (q1, r1) = + self.euclidean_division_var(unsigned_lhs, unsigned_rhs, bit_size - 1, one)?; + + // Unsigned to signed: derive q and r from q1,r1 and the signs of lhs and rhs + // Quotient sign is lhs sign * rhs sign, whose resulting sign bit is the XOR of the sign bits + let sign_sum = self.add_var(lhs_leading, rhs_leading)?; + let sign_prod = self.mul_var(lhs_leading, rhs_leading)?; + let q_sign = self.add_mul_var(sign_sum, -FieldElement::from(2_i128), sign_prod)?; + + let quotient = self.two_complement(q1, q_sign, bit_size)?; + let remainder = self.two_complement(r1, lhs_leading, bit_size)?; + + Ok((quotient, remainder)) } /// Returns a variable which is constrained to be `lhs mod rhs` @@ -616,6 +877,13 @@ impl AcirContext { ) -> Result { match numeric_type { NumericType::Signed { bit_size } | NumericType::Unsigned { bit_size } => { + // If `variable` is constant then we don't need to add a constraint. + // We _do_ add a constraint if `variable` would fail the range check however so that we throw an error. + if let Some(constant) = self.var_to_expression(variable)?.to_const() { + if constant.num_bits() <= *bit_size { + return Ok(variable); + } + } let witness = self.var_to_witness(variable)?; self.acir_ir.range_constraint(witness, *bit_size)?; } @@ -636,19 +904,14 @@ impl AcirContext { max_bit_size: u32, ) -> Result { // 2^{rhs} - let divisor = FieldElement::from(2_i128).pow(&FieldElement::from(rhs as i128)); + let divisor = + self.add_constant(FieldElement::from(2_i128).pow(&FieldElement::from(rhs as i128))); + let one = self.add_constant(FieldElement::one()); - let lhs_data = &self.vars[&lhs]; - let lhs_expr = lhs_data.to_expression(); // Computes lhs = 2^{rhs} * q + r - let (_, remainder) = self.acir_ir.euclidean_division( - &lhs_expr, - &Expression::from_field(divisor), - max_bit_size, - &Expression::one(), - )?; + let (_, remainder) = self.euclidean_division_var(lhs, divisor, max_bit_size, one)?; - Ok(self.add_data(AcirVarData::from(remainder))) + Ok(remainder) } /// Returns an `AcirVar` which will be `1` if lhs >= rhs @@ -657,17 +920,69 @@ impl AcirContext { &mut self, lhs: AcirVar, rhs: AcirVar, - bit_size: u32, + max_bits: u32, predicate: AcirVar, ) -> Result { - let lhs_expr = self.var_to_expression(lhs)?; - let rhs_expr = self.var_to_expression(rhs)?; - let predicate_expr = self.var_to_expression(predicate)?; - - let is_greater_than_eq = - self.acir_ir.more_than_eq_comparison(&lhs_expr, &rhs_expr, bit_size, predicate_expr)?; + // Returns a `Witness` that is constrained to be: + // - `1` if lhs >= rhs + // - `0` otherwise + // + // We essentially computes the sign bit of `b-a` + // For this we sign-extend `b-a` with `c = 2^{max_bits} - (b - a)`, since both `a` and `b` are less than `2^{max_bits}` + // Then we get the bit sign of `c`, the 2-complement representation of `(b-a)`, which is a `max_bits+1` integer, + // by doing the euclidean division `c / 2^{max_bits}` + // + // To see why it really works; + // We first note that `c` is an integer of `(max_bits+1)` bits. Therefore, + // if `b-a>0`, then `c < 2^{max_bits}`, so the division by `2^{max_bits}` will give `0` + // If `b-a<=0`, then `c >= 2^{max_bits}`, so the division by `2^{max_bits}` will give `1`. + // + // In other words, `1` means `a >= b` and `0` means `b > a`. + // The important thing here is that `c` does not overflow nor underflow the field; + // - By construction we have `c >= 0`, so there is no underflow + // - We assert at the beginning that `2^{max_bits+1}` does not overflow the field, so neither c. - Ok(self.add_data(AcirVarData::Witness(is_greater_than_eq))) + // Ensure that 2^{max_bits + 1} is less than the field size + // + // TODO: perhaps this should be a user error, instead of an assert + assert!(max_bits + 1 < FieldElement::max_num_bits()); + let two_max_bits = self + .add_constant(FieldElement::from(2_i128).pow(&FieldElement::from(max_bits as i128))); + let diff = self.sub_var(lhs, rhs)?; + let comparison_evaluation = self.add_var(diff, two_max_bits)?; + + // Euclidean division by 2^{max_bits} : 2^{max_bits} + a - b = q * 2^{max_bits} + r + // + // 2^{max_bits} is of max_bits+1 bit size + // If a>b, then a-b is less than 2^{max_bits} - 1, so 2^{max_bits} + a - b is less than 2^{max_bits} + 2^{max_bits} - 1 = 2^{max_bits+1} - 1 + // If a <= b, then 2^{max_bits} + a - b is less than 2^{max_bits} <= 2^{max_bits+1} - 1 + // This means that both operands of the division have at most max_bits+1 bit size. + // + // case: a == b + // + // let k = 0; + // - 2^{max_bits} == q * 2^{max_bits} + r + // - This is only the case when q == 1 and r == 0 (assuming r is bounded to be less than 2^{max_bits}) + // + // case: a > b + // + // let k = a - b; + // - k + 2^{max_bits} == q * 2^{max_bits} + r + // - This is the case when q == 1 and r = k + // + // case: a < b + // + // let k = b - a + // - 2^{max_bits} - k == q * 2^{max_bits} + r + // - This is only the case when q == 0 and r == 2^{max_bits} - k + // + let (q, _) = self.euclidean_division_var( + comparison_evaluation, + two_max_bits, + max_bits + 1, + predicate, + )?; + Ok(q) } /// Returns an `AcirVar` which will be `1` if lhs < rhs @@ -697,7 +1012,7 @@ impl AcirContext { ) -> Result, RuntimeError> { // Separate out any arguments that should be constants let constants = match name { - BlackBoxFunc::Pedersen => { + BlackBoxFunc::PedersenCommitment | BlackBoxFunc::PedersenHash => { // The last argument of pedersen is the domain separator, which must be a constant let domain_var = match inputs.pop() { Some(domain_var) => domain_var.into_var()?, @@ -847,8 +1162,9 @@ impl AcirContext { } /// Terminates the context and takes the resulting `GeneratedAcir` - pub(crate) fn finish(mut self, inputs: Vec) -> GeneratedAcir { + pub(crate) fn finish(mut self, inputs: Vec, warnings: Vec) -> GeneratedAcir { self.acir_ir.input_witnesses = vecmap(inputs, Witness); + self.acir_ir.warnings = warnings; self.acir_ir } @@ -1264,12 +1580,19 @@ fn execute_brillig( ) -> Result { Err(BlackBoxResolutionError::Unsupported(BlackBoxFunc::SchnorrVerify)) } - fn pedersen( + fn pedersen_commitment( &self, _inputs: &[FieldElement], _domain_separator: u32, ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> { - Err(BlackBoxResolutionError::Unsupported(BlackBoxFunc::Pedersen)) + Err(BlackBoxResolutionError::Unsupported(BlackBoxFunc::PedersenCommitment)) + } + fn pedersen_hash( + &self, + _inputs: &[FieldElement], + _domain_separator: u32, + ) -> Result { + Err(BlackBoxResolutionError::Unsupported(BlackBoxFunc::PedersenHash)) } fn fixed_base_scalar_mul( &self, diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs index 3ce1c8893fa..f29d3c9ec05 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs @@ -4,7 +4,7 @@ use std::collections::BTreeMap; use crate::{ brillig::{brillig_gen::brillig_directive, brillig_ir::artifact::GeneratedBrillig}, - errors::{InternalError, RuntimeError}, + errors::{InternalError, RuntimeError, SsaReport}, ssa::ir::dfg::CallStack, }; @@ -53,6 +53,8 @@ pub(crate) struct GeneratedAcir { /// Correspondence between an opcode index and the error message associated with it. pub(crate) assert_messages: BTreeMap, + + pub(crate) warnings: Vec, } impl GeneratedAcir { @@ -167,11 +169,16 @@ impl GeneratedAcir { output: outputs[0], } } - BlackBoxFunc::Pedersen => BlackBoxFuncCall::Pedersen { + BlackBoxFunc::PedersenCommitment => BlackBoxFuncCall::PedersenCommitment { inputs: inputs[0].clone(), outputs: (outputs[0], outputs[1]), domain_separator: constants[0].to_u128() as u32, }, + BlackBoxFunc::PedersenHash => BlackBoxFuncCall::PedersenHash { + inputs: inputs[0].clone(), + output: outputs[0], + domain_separator: constants[0].to_u128() as u32, + }, BlackBoxFunc::EcdsaSecp256k1 => { BlackBoxFuncCall::EcdsaSecp256k1 { // 32 bytes for each public key co-ordinate @@ -290,24 +297,6 @@ impl GeneratedAcir { Ok(limb_witnesses) } - // Returns the 2-complement of lhs, using the provided sign bit in 'leading' - // if leading is zero, it returns lhs - // if leading is one, it returns 2^bit_size-lhs - fn two_complement( - &mut self, - lhs: &Expression, - leading: Witness, - max_bit_size: u32, - ) -> Expression { - let max_power_of_two = - FieldElement::from(2_i128).pow(&FieldElement::from(max_bit_size as i128 - 1)); - - let intermediate = - self.mul_with_witness(&(&Expression::from(max_power_of_two) - lhs), &leading.into()); - - lhs.add_mul(FieldElement::from(2_i128), &intermediate) - } - /// Returns an expression which represents `lhs * rhs` /// /// If one has multiplicative term and the other is of degree one or more, @@ -357,286 +346,6 @@ impl GeneratedAcir { (&*lhs_reduced * &*rhs_reduced).expect("Both expressions are reduced to be degree <= 1") } - /// Signed division lhs / rhs - /// We derive the signed division from the unsigned euclidean division. - /// note that this is not euclidean division! - // if x is a signed integer, then sign(x)x >= 0 - // so if a and b are signed integers, we can do the unsigned division: - // sign(a)a = q1*sign(b)b + r1 - // => a = sign(a)sign(b)q1*b + sign(a)r1 - // => a = qb+r, with |r|<|b| and a and r have the same sign. - pub(crate) fn signed_division( - &mut self, - lhs: &Expression, - rhs: &Expression, - max_bit_size: u32, - ) -> Result<(Expression, Expression), RuntimeError> { - // 2^{max_bit size-1} - let max_power_of_two = - FieldElement::from(2_i128).pow(&FieldElement::from(max_bit_size as i128 - 1)); - - // Get the sign bit of rhs by computing rhs / max_power_of_two - let (rhs_leading_witness, _) = self.euclidean_division( - rhs, - &max_power_of_two.into(), - max_bit_size, - &Expression::one(), - )?; - - // Get the sign bit of lhs by computing lhs / max_power_of_two - let (lhs_leading_witness, _) = self.euclidean_division( - lhs, - &max_power_of_two.into(), - max_bit_size, - &Expression::one(), - )?; - - // Signed to unsigned: - let unsigned_lhs = self.two_complement(lhs, lhs_leading_witness, max_bit_size); - let unsigned_rhs = self.two_complement(rhs, rhs_leading_witness, max_bit_size); - let unsigned_l_witness = self.get_or_create_witness(&unsigned_lhs); - let unsigned_r_witness = self.get_or_create_witness(&unsigned_rhs); - - // Performs the division using the unsigned values of lhs and rhs - let (q1, r1) = self.euclidean_division( - &unsigned_l_witness.into(), - &unsigned_r_witness.into(), - max_bit_size - 1, - &Expression::one(), - )?; - - // Unsigned to signed: derive q and r from q1,r1 and the signs of lhs and rhs - // Quotient sign is lhs sign * rhs sign, whose resulting sign bit is the XOR of the sign bits - let sign_sum = - &Expression::from(lhs_leading_witness) + &Expression::from(rhs_leading_witness); - let sign_prod = (&Expression::from(lhs_leading_witness) - * &Expression::from(rhs_leading_witness)) - .expect("Product of two witnesses so result is degree 2"); - let q_sign = sign_sum.add_mul(-FieldElement::from(2_i128), &sign_prod); - - let q_sign_witness = self.get_or_create_witness(&q_sign); - let quotient = self.two_complement(&q1.into(), q_sign_witness, max_bit_size); - let remainder = self.two_complement(&r1.into(), lhs_leading_witness, max_bit_size); - Ok((quotient, remainder)) - } - - /// Computes lhs/rhs by using euclidean division. - /// - /// Returns `q` for quotient and `r` for remainder such - /// that lhs = rhs * q + r - pub(crate) fn euclidean_division( - &mut self, - lhs: &Expression, - rhs: &Expression, - max_bit_size: u32, - predicate: &Expression, - ) -> Result<(Witness, Witness), RuntimeError> { - // lhs = rhs * q + r - // - // If predicate is zero, `q_witness` and `r_witness` will be 0 - - // Check that we the rhs is not zero. - // Otherwise, when executing the brillig quotient we may attempt to divide by zero, causing a VM panic. - // - // When the predicate is 0, the equation always passes. - // When the predicate is 1, the rhs must not be 0. - let rhs_is_nonzero_const = rhs.is_const() && !rhs.is_zero(); - if !rhs_is_nonzero_const { - match predicate.to_const() { - Some(predicate) if predicate.is_zero() => { - // If predicate is known to be inactive, we don't need to lay down constraints. - } - - Some(predicate) if predicate.is_one() => { - // If the predicate is known to be active, we simply assert that an inverse must exist. - // This implies that `rhs != 0`. - let unsafe_inverse = self.brillig_inverse(rhs.clone()); - let rhs_has_inverse = - self.mul_with_witness(rhs, &unsafe_inverse.into()) - FieldElement::one(); - self.assert_is_zero(rhs_has_inverse); - } - - _ => { - // Otherwise we must handle both potential cases. - let rhs_is_zero = self.is_zero(rhs); - let rhs_is_not_zero = self.mul_with_witness(&rhs_is_zero.into(), predicate); - self.assert_is_zero(rhs_is_not_zero); - } - } - } - - // maximum bit size for q and for [r and rhs] - let mut max_q_bits = max_bit_size; - let mut max_rhs_bits = max_bit_size; - // when rhs is constant, we can better estimate the maximum bit sizes - if let Some(rhs_const) = rhs.to_const() { - max_rhs_bits = rhs_const.num_bits(); - if max_rhs_bits != 0 { - if max_rhs_bits > max_bit_size { - let zero = self.get_or_create_witness(&Expression::zero()); - return Ok((zero, zero)); - } - max_q_bits = max_bit_size - max_rhs_bits + 1; - } - } - - // Avoids overflow: 'q*b+r < 2^max_q_bits*2^max_rhs_bits' - let mut avoid_overflow = false; - if max_q_bits + max_rhs_bits >= FieldElement::max_num_bits() - 1 { - // q*b+r can overflow; we avoid this when b is constant - if rhs.is_const() { - avoid_overflow = true; - } else { - // we do not support unbounded division - unreachable!("overflow in unbounded division"); - } - } - - let (q_witness, r_witness) = - self.brillig_quotient(lhs.clone(), rhs.clone(), predicate.clone(), max_bit_size + 1); - - // Constrain `q < 2^{max_q_bits}`. - self.range_constraint(q_witness, max_q_bits)?; - - // Constrain `r < 2^{max_rhs_bits}`. - // - // If `rhs` is a power of 2, then is just a looser version of the following bound constraint. - // In the case where `rhs` isn't a power of 2 then this range constraint is required - // as the bound constraint creates a new witness. - // This opcode will be optimized out if it is redundant so we always add it for safety. - self.range_constraint(r_witness, max_rhs_bits)?; - - // Constrain `r < rhs`. - self.bound_constraint_with_offset(&r_witness.into(), rhs, predicate, max_rhs_bits)?; - - // a * predicate == (b * q + r) * predicate - // => predicate * (a - b * q - r) == 0 - // When the predicate is 0, the equation always passes. - // When the predicate is 1, the euclidean division needs to be - // true. - let rhs_constraint = &self.mul_with_witness(rhs, &q_witness.into()) + r_witness; - let div_euclidean = &self.mul_with_witness(lhs, predicate) - - &self.mul_with_witness(&rhs_constraint, predicate); - - if let Some(rhs_const) = rhs.to_const() { - if avoid_overflow { - // we compute q0 = p/rhs - let rhs_big = BigUint::from_bytes_be(&rhs_const.to_be_bytes()); - let q0_big = FieldElement::modulus() / &rhs_big; - let q0 = FieldElement::from_be_bytes_reduce(&q0_big.to_bytes_be()); - // when q == q0, b*q+r can overflow so we need to bound r to avoid the overflow. - let size_predicate = - self.is_equal(&Expression::from_field(q0), &Expression::from(q_witness)); - let predicate = self.mul_with_witness(&size_predicate.into(), predicate); - // Ensure that there is no overflow, under q == q0 predicate - let max_r_big = FieldElement::modulus() - q0_big * rhs_big; - let max_r = FieldElement::from_be_bytes_reduce(&max_r_big.to_bytes_be()); - let max_r_predicate = - self.mul_with_witness(&predicate, &Expression::from_field(max_r)); - let r_predicate = self.mul_with_witness(&Expression::from(r_witness), &predicate); - // Bound the remainder to be (Witness, Witness) { - // Create the witness for the result - let q_witness = self.next_witness_index(); - let r_witness = self.next_witness_index(); - - let quotient_code = brillig_directive::directive_quotient(max_bit_size); - let inputs = vec![BrilligInputs::Single(lhs), BrilligInputs::Single(rhs)]; - let outputs = vec![BrilligOutputs::Simple(q_witness), BrilligOutputs::Simple(r_witness)]; - self.brillig(Some(predicate), quotient_code, inputs, outputs); - - (q_witness, r_witness) - } - - /// Generate constraints that are satisfied iff - /// lhs < rhs , when offset is 1, or - /// lhs <= rhs, when offset is 0 - /// bits is the bit size of a and b (or an upper bound of the bit size) - /// - /// lhs<=rhs is done by constraining b-a to a bit size of 'bits': - /// if lhs<=rhs, 0 <= rhs-lhs <= b < 2^bits - /// if lhs>rhs, rhs-lhs = p+rhs-lhs > p-2^bits >= 2^bits (if log(p) >= bits + 1) - /// n.b: we do NOT check here that lhs and rhs are indeed 'bits' size - /// lhs < rhs <=> a+1<=b - /// TODO: Consolidate this with bounds_check function. - fn bound_constraint_with_offset( - &mut self, - lhs: &Expression, - rhs: &Expression, - offset: &Expression, - bits: u32, - ) -> Result<(), RuntimeError> { - const fn num_bits() -> usize { - std::mem::size_of::() * 8 - } - - fn bit_size_u128(a: u128) -> u32 where { - num_bits::() as u32 - a.leading_zeros() - } - - assert!( - bits < FieldElement::max_num_bits(), - "range check with bit size of the prime field is not implemented yet" - ); - - let mut lhs_offset = lhs + offset; - - // Optimization when rhs is const and fits within a u128 - if rhs.is_const() && rhs.q_c.fits_in_u128() { - // We try to move the offset to rhs - let rhs_offset = if *offset == Expression::one() && rhs.q_c.to_u128() >= 1 { - lhs_offset = lhs.clone(); - rhs.q_c.to_u128() - 1 - } else { - rhs.q_c.to_u128() - }; - // we now have lhs+offset <= rhs <=> lhs_offset <= rhs_offset - - let bit_size = bit_size_u128(rhs_offset); - // r = 2^bit_size - rhs_offset -1, is of bit size 'bit_size' by construtction - let r = (1_u128 << bit_size) - rhs_offset - 1; - // however, since it is a constant, we can compute it's actual bit size - let r_bit_size = bit_size_u128(r); - // witness = lhs_offset + r - assert!(bits + r_bit_size < FieldElement::max_num_bits()); //we need to ensure lhs_offset + r does not overflow - let mut aor = lhs_offset; - aor.q_c += FieldElement::from(r); - let witness = self.get_or_create_witness(&aor); - // lhs_offset<=rhs_offset <=> lhs_offset + r < rhs_offset + r = 2^bit_size <=> witness < 2^bit_size - self.range_constraint(witness, bit_size)?; - return Ok(()); - } - // General case: lhs_offset<=rhs <=> rhs-lhs_offset>=0 <=> rhs-lhs_offset is a 'bits' bit integer - let sub_expression = rhs - &lhs_offset; //rhs-lhs_offset - let w = self.create_witness_for_expression(&sub_expression); - self.range_constraint(w, bits)?; - - Ok(()) - } - /// Adds an inversion brillig opcode. /// /// This code will invert `expr` without applying constraints @@ -786,75 +495,6 @@ impl GeneratedAcir { Ok(()) } - /// Returns a `Witness` that is constrained to be: - /// - `1` if lhs >= rhs - /// - `0` otherwise - /// - /// We essentially computes the sign bit of `b-a` - /// For this we sign-extend `b-a` with `c = 2^{max_bits} - (b - a)`, since both `a` and `b` are less than `2^{max_bits}` - /// Then we get the bit sign of `c`, the 2-complement representation of `(b-a)`, which is a `max_bits+1` integer, - /// by doing the euclidean division `c / 2^{max_bits}` - /// - /// To see why it really works; - /// We first note that `c` is an integer of `(max_bits+1)` bits. Therefore, - /// if `b-a>0`, then `c < 2^{max_bits}`, so the division by `2^{max_bits}` will give `0` - /// If `b-a<=0`, then `c >= 2^{max_bits}`, so the division by `2^{max_bits}` will give `1`. - /// - /// In other words, `1` means `a >= b` and `0` means `b > a`. - /// The important thing here is that `c` does not overflow nor underflow the field; - /// - By construction we have `c >= 0`, so there is no underflow - /// - We assert at the beginning that `2^{max_bits+1}` does not overflow the field, so neither c. - pub(crate) fn more_than_eq_comparison( - &mut self, - a: &Expression, - b: &Expression, - max_bits: u32, - predicate: Expression, - ) -> Result { - // Ensure that 2^{max_bits + 1} is less than the field size - // - // TODO: perhaps this should be a user error, instead of an assert - assert!(max_bits + 1 < FieldElement::max_num_bits()); - - // Compute : 2^{max_bits} + a - b - let two = FieldElement::from(2_i128); - let two_max_bits: FieldElement = two.pow(&FieldElement::from(max_bits as i128)); - let comparison_evaluation = (a - b) + two_max_bits; - - // euclidean division by 2^{max_bits} : 2^{max_bits} + a - b = q * 2^{max_bits} + r - // - // 2^{max_bits} is of max_bits+1 bit size - // If a>b, then a-b is less than 2^{max_bits} - 1, so 2^{max_bits} + a - b is less than 2^{max_bits} + 2^{max_bits} - 1 = 2^{max_bits+1} - 1 - // If a <= b, then 2^{max_bits} + a - b is less than 2^{max_bits} <= 2^{max_bits+1} - 1 - // This means that both operands of the division have at most max_bits+1 bit size. - // - // case: a == b - // - // let k = 0; - // - 2^{max_bits} == q * 2^{max_bits} + r - // - This is only the case when q == 1 and r == 0 (assuming r is bounded to be less than 2^{max_bits}) - // - // case: a > b - // - // let k = a - b; - // - k + 2^{max_bits} == q * 2^{max_bits} + r - // - This is the case when q == 1 and r = k - // - // case: a < b - // - // let k = b - a - // - 2^{max_bits} - k == q * 2^{max_bits} + r - // - This is only the case when q == 0 and r == 2^{max_bits} - k - // - let (q, _) = self.euclidean_division( - &comparison_evaluation, - &Expression::from(two_max_bits), - max_bits + 1, - &predicate, - )?; - Ok(q) - } - pub(crate) fn brillig( &mut self, predicate: Option, @@ -933,7 +573,8 @@ fn black_box_func_expected_input_size(name: BlackBoxFunc) -> Option { BlackBoxFunc::Keccak256 | BlackBoxFunc::SHA256 | BlackBoxFunc::Blake2s - | BlackBoxFunc::Pedersen + | BlackBoxFunc::PedersenCommitment + | BlackBoxFunc::PedersenHash | BlackBoxFunc::HashToField128Security => None, // Can only apply a range constraint to one @@ -964,8 +605,10 @@ fn black_box_expected_output_size(name: BlackBoxFunc) -> Option { BlackBoxFunc::Keccak256 | BlackBoxFunc::SHA256 | BlackBoxFunc::Blake2s => Some(32), // Hash to field returns a field element BlackBoxFunc::HashToField128Security => Some(1), - // Pedersen returns a point - BlackBoxFunc::Pedersen => Some(2), + // Pedersen commitment returns a point + BlackBoxFunc::PedersenCommitment => Some(2), + // Pedersen hash returns a field + BlackBoxFunc::PedersenHash => Some(1), // Can only apply a range constraint to one // witness at a time. BlackBoxFunc::RANGE => Some(0), diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs index bbd53a5a204..0c0ec7e88d2 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -23,7 +23,7 @@ use super::{ use crate::brillig::brillig_ir::artifact::GeneratedBrillig; use crate::brillig::brillig_ir::BrilligContext; use crate::brillig::{brillig_gen::brillig_fn::FunctionContext as BrilligFunctionContext, Brillig}; -use crate::errors::{InternalError, RuntimeError}; +use crate::errors::{InternalError, InternalWarning, RuntimeError, SsaReport}; pub(crate) use acir_ir::generated_acir::GeneratedAcir; use acvm::{ acir::{circuit::opcodes::BlockId, native_types::Expression}, @@ -201,9 +201,9 @@ impl Context { self.convert_ssa_instruction(*instruction_id, dfg, ssa, &brillig, last_array_uses)?; } - self.convert_ssa_return(entry_block.unwrap_terminator(), dfg)?; + let warnings = self.convert_ssa_return(entry_block.unwrap_terminator(), dfg)?; - Ok(self.acir_context.finish(input_witness.collect())) + Ok(self.acir_context.finish(input_witness.collect(), warnings)) } fn convert_brillig_main( @@ -240,7 +240,7 @@ impl Context { self.acir_context.return_var(acir_var)?; } - Ok(self.acir_context.finish(witness_inputs)) + Ok(self.acir_context.finish(witness_inputs, Vec::new())) } /// Adds and binds `AcirVar`s for each numeric block parameter or block parameter array element. @@ -1247,8 +1247,8 @@ impl Context { &mut self, terminator: &TerminatorInstruction, dfg: &DataFlowGraph, - ) -> Result<(), InternalError> { - let (return_values, _call_stack) = match terminator { + ) -> Result, InternalError> { + let (return_values, call_stack) = match terminator { TerminatorInstruction::Return { return_values, call_stack } => { (return_values, call_stack) } @@ -1258,16 +1258,16 @@ impl Context { // The return value may or may not be an array reference. Calling `flatten_value_list` // will expand the array if there is one. let return_acir_vars = self.flatten_value_list(return_values, dfg); + let mut warnings = Vec::new(); for acir_var in return_acir_vars { - // TODO(Guillaume) -- disabled as it has shown to break - // TODO with important programs. We will add it back once - // TODO we change it to a warning. - // if self.acir_context.is_constant(&acir_var) { - // return Err(InternalError::ReturnConstant { call_stack: call_stack.clone() }); - // } + if self.acir_context.is_constant(&acir_var) { + warnings.push(SsaReport::Warning(InternalWarning::ReturnConstant { + call_stack: call_stack.clone(), + })); + } self.acir_context.return_var(acir_var)?; } - Ok(()) + Ok(warnings) } /// Gets the cached `AcirVar` that was converted from the corresponding `ValueId`. If it does diff --git a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs index 546a614a27f..058f91adacb 100644 --- a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs @@ -1,4 +1,4 @@ -use std::borrow::Cow; +use std::{borrow::Cow, rc::Rc}; use acvm::FieldElement; use noirc_errors::Location; @@ -16,7 +16,8 @@ use super::{ basic_block::BasicBlock, dfg::{CallStack, InsertInstructionResult}, function::RuntimeType, - instruction::{InstructionId, Intrinsic}, + instruction::{Endian, InstructionId, Intrinsic}, + types::NumericType, }, ssa_gen::Ssa, }; @@ -183,6 +184,10 @@ impl FunctionBuilder { self } + pub(crate) fn get_call_stack(&self) -> CallStack { + self.call_stack.clone() + } + /// Insert a Load instruction at the end of the current block, loading from the given offset /// of the given address which should point to a previous Allocate instruction. Note that /// this is limited to loading a single value. Loading multiple values (such as a tuple) @@ -255,9 +260,126 @@ impl FunctionBuilder { arguments: Vec, result_types: Vec, ) -> Cow<[ValueId]> { + if let Value::Intrinsic(intrinsic) = &self.current_function.dfg[func] { + if intrinsic == &Intrinsic::WrappingShiftLeft { + let result_type = self.current_function.dfg.type_of_value(arguments[0]); + let bit_size = match result_type { + Type::Numeric(NumericType::Signed { bit_size }) + | Type::Numeric(NumericType::Unsigned { bit_size }) => bit_size, + _ => { + unreachable!("ICE: Truncation attempted on non-integer"); + } + }; + return self + .insert_wrapping_shift_left(arguments[0], arguments[1], bit_size) + .results(); + } + } + self.insert_instruction(Instruction::Call { func, arguments }, Some(result_types)).results() } + /// Insert ssa instructions which computes lhs << rhs by doing lhs*2^rhs + pub(crate) fn insert_shift_left(&mut self, lhs: ValueId, rhs: ValueId) -> ValueId { + let base = self.field_constant(FieldElement::from(2_u128)); + let pow = self.pow(base, rhs); + let typ = self.current_function.dfg.type_of_value(lhs); + let pow = self.insert_cast(pow, typ); + self.insert_binary(lhs, BinaryOp::Mul, pow) + } + + /// Insert ssa instructions which computes lhs << rhs by doing lhs*2^rhs + /// and truncate the result to bit_size + fn insert_wrapping_shift_left( + &mut self, + lhs: ValueId, + rhs: ValueId, + bit_size: u32, + ) -> InsertInstructionResult { + let base = self.field_constant(FieldElement::from(2_u128)); + let typ = self.current_function.dfg.type_of_value(lhs); + let (max_bit, pow) = if let Some(rhs_constant) = + self.current_function.dfg.get_numeric_constant(rhs) + { + // Happy case is that we know precisely by how many bits the the integer will + // increase: lhs_bit_size + rhs + let (rhs_bit_size_pow_2, overflows) = + 2_u32.overflowing_pow(rhs_constant.to_u128() as u32); + if overflows { + let zero = self.numeric_constant(FieldElement::zero(), typ); + return InsertInstructionResult::SimplifiedTo(zero); + } + let pow = self.numeric_constant(FieldElement::from(rhs_bit_size_pow_2 as u128), typ); + (bit_size + (rhs_constant.to_u128() as u32), pow) + } else { + // we use a predicate to nullify the result in case of overflow + let bit_size_var = + self.numeric_constant(FieldElement::from(bit_size as u128), typ.clone()); + let overflow = self.insert_binary(rhs, BinaryOp::Lt, bit_size_var); + let one = self.numeric_constant(FieldElement::one(), Type::unsigned(1)); + let predicate = self.insert_binary(overflow, BinaryOp::Eq, one); + let predicate = self.insert_cast(predicate, typ.clone()); + + let pow = self.pow(base, rhs); + let pow = self.insert_cast(pow, typ); + (FieldElement::max_num_bits(), self.insert_binary(predicate, BinaryOp::Mul, pow)) + }; + + let instruction = Instruction::Binary(Binary { lhs, rhs: pow, operator: BinaryOp::Mul }); + if max_bit <= bit_size { + self.insert_instruction(instruction, None) + } else { + let result = self.insert_instruction(instruction, None).first(); + self.insert_instruction( + Instruction::Truncate { value: result, bit_size, max_bit_size: max_bit }, + None, + ) + } + } + + /// Insert ssa instructions which computes lhs >> rhs by doing lhs/2^rhs + pub(crate) fn insert_shift_right(&mut self, lhs: ValueId, rhs: ValueId) -> ValueId { + let base = self.field_constant(FieldElement::from(2_u128)); + let pow = self.pow(base, rhs); + self.insert_binary(lhs, BinaryOp::Div, pow) + } + + /// Computes lhs^rhs via square&multiply, using the bits decomposition of rhs + /// Pseudo-code of the computation: + /// let mut r = 1; + /// let rhs_bits = to_bits(rhs); + /// for i in 1 .. bit_size + 1 { + /// let r_squared = r * r; + /// let b = rhs_bits[bit_size - i]; + /// r = (r_squared * lhs * b) + (1 - b) * r_squared; + /// } + pub(crate) fn pow(&mut self, lhs: ValueId, rhs: ValueId) -> ValueId { + let typ = self.current_function.dfg.type_of_value(rhs); + if let Type::Numeric(NumericType::Unsigned { bit_size }) = typ { + let to_bits = self.import_intrinsic_id(Intrinsic::ToBits(Endian::Little)); + let length = self.field_constant(FieldElement::from(bit_size as i128)); + let result_types = + vec![Type::field(), Type::Array(Rc::new(vec![Type::bool()]), bit_size as usize)]; + let rhs_bits = self.insert_call(to_bits, vec![rhs, length], result_types); + let rhs_bits = rhs_bits[1]; + let one = self.field_constant(FieldElement::one()); + let mut r = one; + for i in 1..bit_size + 1 { + let r_squared = self.insert_binary(r, BinaryOp::Mul, r); + let a = self.insert_binary(r_squared, BinaryOp::Mul, lhs); + let idx = self.field_constant(FieldElement::from((bit_size - i) as i128)); + let b = self.insert_array_get(rhs_bits, idx, Type::field()); + let r1 = self.insert_binary(a, BinaryOp::Mul, b); + let c = self.insert_binary(one, BinaryOp::Sub, b); + let r2 = self.insert_binary(c, BinaryOp::Mul, r_squared); + r = self.insert_binary(r1, BinaryOp::Add, r2); + } + r + } else { + unreachable!("Value must be unsigned in power operation"); + } + } + /// Insert an instruction to extract an element from an array pub(crate) fn insert_array_get( &mut self, diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs index badc1e82d50..a3d6396b668 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs @@ -46,6 +46,7 @@ pub(crate) enum Intrinsic { BlackBox(BlackBoxFunc), FromField, AsField, + WrappingShiftLeft, } impl std::fmt::Display for Intrinsic { @@ -68,6 +69,7 @@ impl std::fmt::Display for Intrinsic { Intrinsic::BlackBox(function) => write!(f, "{function}"), Intrinsic::FromField => write!(f, "from_field"), Intrinsic::AsField => write!(f, "as_field"), + Intrinsic::WrappingShiftLeft => write!(f, "wrapping_shift_left"), } } } @@ -92,7 +94,8 @@ impl Intrinsic { | Intrinsic::ToBits(_) | Intrinsic::ToRadix(_) | Intrinsic::FromField - | Intrinsic::AsField => false, + | Intrinsic::AsField + | Intrinsic::WrappingShiftLeft => false, // Some black box functions have side-effects Intrinsic::BlackBox(func) => matches!(func, BlackBoxFunc::RecursiveAggregation), @@ -119,6 +122,7 @@ impl Intrinsic { "to_be_bits" => Some(Intrinsic::ToBits(Endian::Big)), "from_field" => Some(Intrinsic::FromField), "as_field" => Some(Intrinsic::AsField), + "wrapping_shift_left" => Some(Intrinsic::WrappingShiftLeft), other => BlackBoxFunc::lookup(other).map(Intrinsic::BlackBox), } } diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs index 26d6739902d..da5544d7dc6 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs @@ -245,6 +245,9 @@ pub(super) fn simplify_call( let instruction = Instruction::Cast(arguments[0], ctrl_typevars.unwrap().remove(0)); SimplifyResult::SimplifiedToInstruction(instruction) } + Intrinsic::WrappingShiftLeft => { + unreachable!("ICE - wrapping shift left should have been proccessed before") + } } } @@ -413,7 +416,10 @@ fn simplify_black_box_func( simplify_signature(dfg, arguments, acvm::blackbox_solver::ecdsa_secp256r1_verify) } - BlackBoxFunc::FixedBaseScalarMul | BlackBoxFunc::SchnorrVerify | BlackBoxFunc::Pedersen => { + BlackBoxFunc::FixedBaseScalarMul + | BlackBoxFunc::SchnorrVerify + | BlackBoxFunc::PedersenCommitment + | BlackBoxFunc::PedersenHash => { // Currently unsolvable here as we rely on an implementation in the backend. SimplifyResult::None } diff --git a/compiler/noirc_evaluator/src/ssa/ir/types.rs b/compiler/noirc_evaluator/src/ssa/ir/types.rs index b576ee12d45..e69e936372d 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/types.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/types.rs @@ -1,5 +1,6 @@ use std::rc::Rc; +use acvm::FieldElement; use iter_extended::vecmap; /// A numeric type in the Intermediate representation @@ -11,7 +12,7 @@ use iter_extended::vecmap; /// Fields do not have a notion of ordering, so this distinction /// is reasonable. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)] -pub(crate) enum NumericType { +pub enum NumericType { Signed { bit_size: u32 }, Unsigned { bit_size: u32 }, NativeField, @@ -94,6 +95,26 @@ impl Type { } } +impl NumericType { + /// Returns true if the given Field value is within the numeric limits + /// for the current NumericType. + pub(crate) fn value_is_within_limits(self, field: FieldElement) -> bool { + match self { + NumericType::Signed { bit_size } => { + let min = -(2i128.pow(bit_size - 1)); + let max = 2u128.pow(bit_size - 1) - 1; + // Signed integers are odd since they will overflow the field value + field <= max.into() || field >= min.into() + } + NumericType::Unsigned { bit_size } => { + let max = 2u128.pow(bit_size) - 1; + field <= max.into() + } + NumericType::NativeField => true, + } + } +} + /// Composite Types are essentially flattened struct or tuple types. /// Array types may have these as elements where each flattened field is /// included in the array sequentially. diff --git a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs index 44dc8b098e7..f35e8023584 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs @@ -1284,8 +1284,8 @@ mod test { let zero = builder.field_constant(0_u128); let zero_array = builder.array_constant(im::Vector::unit(zero), array_type); let i_zero = builder.numeric_constant(0_u128, Type::unsigned(32)); - let pedersen = - builder.import_intrinsic_id(Intrinsic::BlackBox(acvm::acir::BlackBoxFunc::Pedersen)); + let pedersen = builder + .import_intrinsic_id(Intrinsic::BlackBox(acvm::acir::BlackBoxFunc::PedersenCommitment)); let v4 = builder.insert_call( pedersen, vec![zero_array, i_zero], diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs index 142e9f81397..25534c739e2 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs @@ -8,11 +8,12 @@ use noirc_frontend::monomorphization::ast::{self, LocalId, Parameters}; use noirc_frontend::monomorphization::ast::{FuncId, Program}; use noirc_frontend::{BinaryOpKind, Signedness}; +use crate::errors::RuntimeError; use crate::ssa::function_builder::FunctionBuilder; use crate::ssa::ir::dfg::DataFlowGraph; use crate::ssa::ir::function::FunctionId as IrFunctionId; use crate::ssa::ir::function::{Function, RuntimeType}; -use crate::ssa::ir::instruction::{BinaryOp, Endian, Intrinsic}; +use crate::ssa::ir::instruction::BinaryOp; use crate::ssa::ir::map::AtomicCounter; use crate::ssa::ir::types::{NumericType, Type}; use crate::ssa::ir::value::ValueId; @@ -240,48 +241,28 @@ impl<'a> FunctionContext<'a> { Values::empty() } - /// Insert ssa instructions which computes lhs << rhs by doing lhs*2^rhs - fn insert_shift_left(&mut self, lhs: ValueId, rhs: ValueId) -> ValueId { - let base = self.builder.field_constant(FieldElement::from(2_u128)); - let pow = self.pow(base, rhs); - let typ = self.builder.current_function.dfg.type_of_value(lhs); - let pow = self.builder.insert_cast(pow, typ); - self.builder.insert_binary(lhs, BinaryOp::Mul, pow) - } - - /// Insert ssa instructions which computes lhs >> rhs by doing lhs/2^rhs - fn insert_shift_right(&mut self, lhs: ValueId, rhs: ValueId) -> ValueId { - let base = self.builder.field_constant(FieldElement::from(2_u128)); - let pow = self.pow(base, rhs); - self.builder.insert_binary(lhs, BinaryOp::Div, pow) - } - - /// Computes lhs^rhs via square&multiply, using the bits decomposition of rhs - fn pow(&mut self, lhs: ValueId, rhs: ValueId) -> ValueId { - let typ = self.builder.current_function.dfg.type_of_value(rhs); - if let Type::Numeric(NumericType::Unsigned { bit_size }) = typ { - let to_bits = self.builder.import_intrinsic_id(Intrinsic::ToBits(Endian::Little)); - let length = self.builder.field_constant(FieldElement::from(bit_size as i128)); - let result_types = - vec![Type::field(), Type::Array(Rc::new(vec![Type::bool()]), bit_size as usize)]; - let rhs_bits = self.builder.insert_call(to_bits, vec![rhs, length], result_types); - let rhs_bits = rhs_bits[1]; - let one = self.builder.field_constant(FieldElement::one()); - let mut r = one; - for i in 1..bit_size + 1 { - let r1 = self.builder.insert_binary(r, BinaryOp::Mul, r); - let a = self.builder.insert_binary(r1, BinaryOp::Mul, lhs); - let idx = self.builder.field_constant(FieldElement::from((bit_size - i) as i128)); - let b = self.builder.insert_array_get(rhs_bits, idx, Type::field()); - let r2 = self.builder.insert_binary(a, BinaryOp::Mul, b); - let c = self.builder.insert_binary(one, BinaryOp::Sub, b); - let r3 = self.builder.insert_binary(c, BinaryOp::Mul, r1); - r = self.builder.insert_binary(r2, BinaryOp::Add, r3); - } - r + /// Insert a numeric constant into the current function + /// + /// Unlike FunctionBuilder::numeric_constant, this version checks the given constant + /// is within the range of the given type. This is needed for user provided values where + /// otherwise values like 2^128 can be assigned to a u8 without error or wrapping. + pub(super) fn checked_numeric_constant( + &mut self, + value: impl Into, + typ: Type, + ) -> Result { + let value = value.into(); + + if let Type::Numeric(typ) = typ { + if !typ.value_is_within_limits(value) { + let call_stack = self.builder.get_call_stack(); + return Err(RuntimeError::IntegerOutOfBounds { value, typ, call_stack }); + } } else { - unreachable!("Value must be unsigned in power operation"); + panic!("Expected type for numeric constant to be a numeric type, found {typ}"); } + + Ok(self.builder.numeric_constant(value, typ)) } /// Insert a binary instruction at the end of the current block. @@ -296,8 +277,8 @@ impl<'a> FunctionContext<'a> { location: Location, ) -> Values { let mut result = match operator { - BinaryOpKind::ShiftLeft => self.insert_shift_left(lhs, rhs), - BinaryOpKind::ShiftRight => self.insert_shift_right(lhs, rhs), + BinaryOpKind::ShiftLeft => self.builder.insert_shift_left(lhs, rhs), + BinaryOpKind::ShiftRight => self.builder.insert_shift_right(lhs, rhs), BinaryOpKind::Equal | BinaryOpKind::NotEqual if matches!(self.builder.type_of_value(lhs), Type::Array(..)) => { @@ -544,8 +525,11 @@ impl<'a> FunctionContext<'a> { /// This is operationally equivalent to extract_current_value_recursive, but splitting these /// into two separate functions avoids cloning the outermost `Values` returned by the recursive /// version, as it is only needed for recursion. - pub(super) fn extract_current_value(&mut self, lvalue: &ast::LValue) -> LValue { - match lvalue { + pub(super) fn extract_current_value( + &mut self, + lvalue: &ast::LValue, + ) -> Result { + Ok(match lvalue { ast::LValue::Ident(ident) => { let (reference, should_auto_deref) = self.ident_lvalue(ident); if should_auto_deref { @@ -555,18 +539,18 @@ impl<'a> FunctionContext<'a> { } } ast::LValue::Index { array, index, location, .. } => { - self.index_lvalue(array, index, location).2 + self.index_lvalue(array, index, location)?.2 } ast::LValue::MemberAccess { object, field_index } => { - let (old_object, object_lvalue) = self.extract_current_value_recursive(object); + let (old_object, object_lvalue) = self.extract_current_value_recursive(object)?; let object_lvalue = Box::new(object_lvalue); LValue::MemberAccess { old_object, object_lvalue, index: *field_index } } ast::LValue::Dereference { reference, .. } => { - let (reference, _) = self.extract_current_value_recursive(reference); + let (reference, _) = self.extract_current_value_recursive(reference)?; LValue::Dereference { reference } } - } + }) } fn dereference_lvalue(&mut self, values: &Values, element_type: &ast::Type) -> Values { @@ -596,16 +580,16 @@ impl<'a> FunctionContext<'a> { array: &ast::LValue, index: &ast::Expression, location: &Location, - ) -> (ValueId, ValueId, LValue, Option) { - let (old_array, array_lvalue) = self.extract_current_value_recursive(array); - let index = self.codegen_non_tuple_expression(index); + ) -> Result<(ValueId, ValueId, LValue, Option), RuntimeError> { + let (old_array, array_lvalue) = self.extract_current_value_recursive(array)?; + let index = self.codegen_non_tuple_expression(index)?; let array_lvalue = Box::new(array_lvalue); let array_values = old_array.clone().into_value_list(self); let location = *location; // A slice is represented as a tuple (length, slice contents). // We need to fetch the second value. - if array_values.len() > 1 { + Ok(if array_values.len() > 1 { let slice_lvalue = LValue::SliceIndex { old_slice: old_array, index, @@ -617,37 +601,45 @@ impl<'a> FunctionContext<'a> { let array_lvalue = LValue::Index { old_array: array_values[0], index, array_lvalue, location }; (array_values[0], index, array_lvalue, None) - } + }) } - fn extract_current_value_recursive(&mut self, lvalue: &ast::LValue) -> (Values, LValue) { + fn extract_current_value_recursive( + &mut self, + lvalue: &ast::LValue, + ) -> Result<(Values, LValue), RuntimeError> { match lvalue { ast::LValue::Ident(ident) => { let (variable, should_auto_deref) = self.ident_lvalue(ident); if should_auto_deref { let dereferenced = self.dereference_lvalue(&variable, &ident.typ); - (dereferenced, LValue::Dereference { reference: variable }) + Ok((dereferenced, LValue::Dereference { reference: variable })) } else { - (variable.clone(), LValue::Ident) + Ok((variable.clone(), LValue::Ident)) } } ast::LValue::Index { array, index, element_type, location } => { let (old_array, index, index_lvalue, max_length) = - self.index_lvalue(array, index, location); - let element = - self.codegen_array_index(old_array, index, element_type, *location, max_length); - (element, index_lvalue) + self.index_lvalue(array, index, location)?; + let element = self.codegen_array_index( + old_array, + index, + element_type, + *location, + max_length, + )?; + Ok((element, index_lvalue)) } ast::LValue::MemberAccess { object, field_index: index } => { - let (old_object, object_lvalue) = self.extract_current_value_recursive(object); + let (old_object, object_lvalue) = self.extract_current_value_recursive(object)?; let object_lvalue = Box::new(object_lvalue); let element = Self::get_field_ref(&old_object, *index).clone(); - (element, LValue::MemberAccess { old_object, object_lvalue, index: *index }) + Ok((element, LValue::MemberAccess { old_object, object_lvalue, index: *index })) } ast::LValue::Dereference { reference, element_type } => { - let (reference, _) = self.extract_current_value_recursive(reference); + let (reference, _) = self.extract_current_value_recursive(reference)?; let dereferenced = self.dereference_lvalue(&reference, element_type); - (dereferenced, LValue::Dereference { reference }) + Ok((dereferenced, LValue::Dereference { reference })) } } } diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs index d990a95c540..7677f5669cb 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -5,14 +5,17 @@ mod value; pub(crate) use program::Ssa; use context::SharedContext; -use iter_extended::vecmap; +use iter_extended::{try_vecmap, vecmap}; use noirc_errors::Location; use noirc_frontend::{ monomorphization::ast::{self, Binary, Expression, Program}, BinaryOpKind, }; -use crate::ssa::ir::{instruction::Intrinsic, types::NumericType}; +use crate::{ + errors::RuntimeError, + ssa::ir::{instruction::Intrinsic, types::NumericType}, +}; use self::{ context::FunctionContext, @@ -29,7 +32,7 @@ use super::ir::{ /// Generates SSA for the given monomorphized program. /// /// This function will generate the SSA but does not perform any optimizations on it. -pub(crate) fn generate_ssa(program: Program) -> Ssa { +pub(crate) fn generate_ssa(program: Program) -> Result { let return_location = program.return_location; let context = SharedContext::new(program); @@ -45,7 +48,7 @@ pub(crate) fn generate_ssa(program: Program) -> Ssa { if main.unconstrained { RuntimeType::Brillig } else { RuntimeType::Acir }, &context, ); - function_context.codegen_function_body(&main.body); + function_context.codegen_function_body(&main.body)?; if let Some(return_location) = return_location { let block = function_context.builder.current_block(); @@ -69,24 +72,25 @@ pub(crate) fn generate_ssa(program: Program) -> Ssa { while let Some((src_function_id, dest_id)) = context.pop_next_function_in_queue() { let function = &context.program[src_function_id]; function_context.new_function(dest_id, function); - function_context.codegen_function_body(&function.body); + function_context.codegen_function_body(&function.body)?; } - function_context.builder.finish() + Ok(function_context.builder.finish()) } impl<'a> FunctionContext<'a> { /// Codegen a function's body and set its return value to that of its last parameter. /// For functions returning nothing, this will be an empty list. - fn codegen_function_body(&mut self, body: &Expression) { - let return_value = self.codegen_expression(body); + fn codegen_function_body(&mut self, body: &Expression) -> Result<(), RuntimeError> { + let return_value = self.codegen_expression(body)?; let results = return_value.into_value_list(self); self.builder.terminate_with_return(results); + Ok(()) } - fn codegen_expression(&mut self, expr: &Expression) -> Values { + fn codegen_expression(&mut self, expr: &Expression) -> Result { match expr { - Expression::Ident(ident) => self.codegen_ident(ident), + Expression::Ident(ident) => Ok(self.codegen_ident(ident)), Expression::Literal(literal) => self.codegen_literal(literal), Expression::Block(block) => self.codegen_block(block), Expression::Unary(unary) => self.codegen_unary(unary), @@ -111,8 +115,8 @@ impl<'a> FunctionContext<'a> { /// Codegen any non-tuple expression so that we can unwrap the Values /// tree to return a single value for use with most SSA instructions. - fn codegen_non_tuple_expression(&mut self, expr: &Expression) -> ValueId { - self.codegen_expression(expr).into_leaf().eval(self) + fn codegen_non_tuple_expression(&mut self, expr: &Expression) -> Result { + Ok(self.codegen_expression(expr)?.into_leaf().eval(self)) } /// Codegen a reference to an ident. @@ -140,17 +144,19 @@ impl<'a> FunctionContext<'a> { self.codegen_ident_reference(ident).map(|value| value.eval(self).into()) } - fn codegen_literal(&mut self, literal: &ast::Literal) -> Values { + fn codegen_literal(&mut self, literal: &ast::Literal) -> Result { match literal { ast::Literal::Array(array) => { - let elements = vecmap(&array.contents, |element| self.codegen_expression(element)); + let elements = + try_vecmap(&array.contents, |element| self.codegen_expression(element))?; let typ = Self::convert_type(&array.typ).flatten(); - match array.typ { + Ok(match array.typ { ast::Type::Array(_, _) => self.codegen_array(elements, typ[0].clone()), ast::Type::Slice(_) => { let slice_length = self.builder.field_constant(array.contents.len() as u128); + let slice_contents = self.codegen_array(elements, typ[1].clone()); Tree::Branch(vec![slice_length.into(), slice_contents]) } @@ -158,37 +164,37 @@ impl<'a> FunctionContext<'a> { "ICE: array literal type must be an array or a slice, but got {}", array.typ ), - } + }) } - ast::Literal::Integer(value, typ) => { + ast::Literal::Integer(value, typ, location) => { + self.builder.set_location(*location); let typ = Self::convert_non_tuple_type(typ); - self.builder.numeric_constant(*value, typ).into() + self.checked_numeric_constant(*value, typ).map(Into::into) } ast::Literal::Bool(value) => { - self.builder.numeric_constant(*value as u128, Type::bool()).into() - } - ast::Literal::Str(string) => { - let elements = vecmap(string.as_bytes(), |byte| { - self.builder.numeric_constant(*byte as u128, Type::field()).into() - }); - let typ = Self::convert_non_tuple_type(&ast::Type::String(elements.len() as u64)); - self.codegen_array(elements, typ) + // Don't need to call checked_numeric_constant here since `value` can only be true or false + Ok(self.builder.numeric_constant(*value as u128, Type::bool()).into()) } + ast::Literal::Str(string) => Ok(self.codegen_string(string)), ast::Literal::FmtStr(string, number_of_fields, fields) => { // A caller needs multiple pieces of information to make use of a format string // The message string, the number of fields to be formatted, and the fields themselves - let string = Expression::Literal(ast::Literal::Str(string.clone())); - let number_of_fields = Expression::Literal(ast::Literal::Integer( - (*number_of_fields as u128).into(), - ast::Type::Field, - )); - let fields = *fields.clone(); - let fmt_str_tuple = &[string, number_of_fields, fields]; - self.codegen_tuple(fmt_str_tuple) + let string = self.codegen_string(string); + let field_count = self.builder.field_constant(*number_of_fields as u128); + let fields = self.codegen_expression(fields)?; + + Ok(Tree::Branch(vec![string, field_count.into(), fields])) } } } + fn codegen_string(&mut self, string: &str) -> Values { + let elements = + vecmap(string.as_bytes(), |byte| self.builder.field_constant(*byte as u128).into()); + let typ = Self::convert_non_tuple_type(&ast::Type::String(elements.len() as u64)); + self.codegen_array(elements, typ) + } + /// Codegen an array by allocating enough space for each element and inserting separate /// store instructions until each element is stored. The store instructions will be separated /// by add instructions to calculate the new offset address to store to next. @@ -211,35 +217,35 @@ impl<'a> FunctionContext<'a> { self.builder.array_constant(array, typ).into() } - fn codegen_block(&mut self, block: &[Expression]) -> Values { + fn codegen_block(&mut self, block: &[Expression]) -> Result { let mut result = Self::unit_value(); for expr in block { - result = self.codegen_expression(expr); + result = self.codegen_expression(expr)?; } - result + Ok(result) } - fn codegen_unary(&mut self, unary: &ast::Unary) -> Values { + fn codegen_unary(&mut self, unary: &ast::Unary) -> Result { match unary.operator { noirc_frontend::UnaryOp::Not => { - let rhs = self.codegen_expression(&unary.rhs); + let rhs = self.codegen_expression(&unary.rhs)?; let rhs = rhs.into_leaf().eval(self); - self.builder.insert_not(rhs).into() + Ok(self.builder.insert_not(rhs).into()) } noirc_frontend::UnaryOp::Minus => { - let rhs = self.codegen_expression(&unary.rhs); + let rhs = self.codegen_expression(&unary.rhs)?; let rhs = rhs.into_leaf().eval(self); let typ = self.builder.type_of_value(rhs); let zero = self.builder.numeric_constant(0u128, typ); - self.insert_binary( + Ok(self.insert_binary( zero, noirc_frontend::BinaryOpKind::Subtract, rhs, unary.location, - ) + )) } noirc_frontend::UnaryOp::MutableReference => { - self.codegen_reference(&unary.rhs).map(|rhs| { + Ok(self.codegen_reference(&unary.rhs)?.map(|rhs| { match rhs { value::Value::Normal(value) => { let alloc = self.builder.insert_allocate(); @@ -250,11 +256,11 @@ impl<'a> FunctionContext<'a> { // a Value::Normal so it is no longer automatically dereferenced. value::Value::Mutable(reference, _) => reference.into(), } - }) + })) } noirc_frontend::UnaryOp::Dereference { .. } => { - let rhs = self.codegen_expression(&unary.rhs); - self.dereference(&rhs, &unary.result_type) + let rhs = self.codegen_expression(&unary.rhs)?; + Ok(self.dereference(&rhs, &unary.result_type)) } } } @@ -267,26 +273,26 @@ impl<'a> FunctionContext<'a> { }) } - fn codegen_reference(&mut self, expr: &Expression) -> Values { + fn codegen_reference(&mut self, expr: &Expression) -> Result { match expr { - Expression::Ident(ident) => self.codegen_ident_reference(ident), + Expression::Ident(ident) => Ok(self.codegen_ident_reference(ident)), Expression::ExtractTupleField(tuple, index) => { - let tuple = self.codegen_reference(tuple); - Self::get_field(tuple, *index) + let tuple = self.codegen_reference(tuple)?; + Ok(Self::get_field(tuple, *index)) } other => self.codegen_expression(other), } } - fn codegen_binary(&mut self, binary: &ast::Binary) -> Values { - let lhs = self.codegen_non_tuple_expression(&binary.lhs); - let rhs = self.codegen_non_tuple_expression(&binary.rhs); - self.insert_binary(lhs, binary.operator, rhs, binary.location) + fn codegen_binary(&mut self, binary: &ast::Binary) -> Result { + let lhs = self.codegen_non_tuple_expression(&binary.lhs)?; + let rhs = self.codegen_non_tuple_expression(&binary.rhs)?; + Ok(self.insert_binary(lhs, binary.operator, rhs, binary.location)) } - fn codegen_index(&mut self, index: &ast::Index) -> Values { - let array_or_slice = self.codegen_expression(&index.collection).into_value_list(self); - let index_value = self.codegen_non_tuple_expression(&index.index); + fn codegen_index(&mut self, index: &ast::Index) -> Result { + let array_or_slice = self.codegen_expression(&index.collection)?.into_value_list(self); + let index_value = self.codegen_non_tuple_expression(&index.index)?; // Slices are represented as a tuple in the form: (length, slice contents). // Thus, slices require two value ids for their representation. let (array, slice_length) = if array_or_slice.len() > 1 { @@ -316,7 +322,7 @@ impl<'a> FunctionContext<'a> { element_type: &ast::Type, location: Location, length: Option, - ) -> Values { + ) -> Result { // base_index = index * type_size let type_size = Self::convert_type(element_type).size_of_type(); let type_size = self.builder.field_constant(type_size as u128); @@ -324,7 +330,7 @@ impl<'a> FunctionContext<'a> { self.builder.set_location(location).insert_binary(index, BinaryOp::Mul, type_size); let mut field_index = 0u128; - Self::map_type(element_type, |typ| { + Ok(Self::map_type(element_type, |typ| { let offset = self.make_offset(base_index, field_index); field_index += 1; @@ -339,7 +345,7 @@ impl<'a> FunctionContext<'a> { _ => unreachable!("must have array or slice but got {array_type}"), } self.builder.insert_array_get(array, offset, typ).into() - }) + })) } /// Prepare a slice access. @@ -374,11 +380,11 @@ impl<'a> FunctionContext<'a> { ); } - fn codegen_cast(&mut self, cast: &ast::Cast) -> Values { - let lhs = self.codegen_non_tuple_expression(&cast.lhs); + fn codegen_cast(&mut self, cast: &ast::Cast) -> Result { + let lhs = self.codegen_non_tuple_expression(&cast.lhs)?; let typ = Self::convert_non_tuple_type(&cast.r#type); self.builder.set_location(cast.location); - self.builder.insert_cast(lhs, typ).into() + Ok(self.builder.insert_cast(lhs, typ).into()) } /// Codegens a for loop, creating three new blocks in the process. @@ -398,7 +404,7 @@ impl<'a> FunctionContext<'a> { /// br loop_entry(v4) /// loop_end(): /// ... This is the current insert point after codegen_for finishes ... - fn codegen_for(&mut self, for_expr: &ast::For) -> Values { + fn codegen_for(&mut self, for_expr: &ast::For) -> Result { let loop_entry = self.builder.insert_block(); let loop_body = self.builder.insert_block(); let loop_end = self.builder.insert_block(); @@ -408,10 +414,10 @@ impl<'a> FunctionContext<'a> { let loop_index = self.builder.add_block_parameter(loop_entry, index_type); self.builder.set_location(for_expr.start_range_location); - let start_index = self.codegen_non_tuple_expression(&for_expr.start_range); + let start_index = self.codegen_non_tuple_expression(&for_expr.start_range)?; self.builder.set_location(for_expr.end_range_location); - let end_index = self.codegen_non_tuple_expression(&for_expr.end_range); + let end_index = self.codegen_non_tuple_expression(&for_expr.end_range)?; // Set the location of the initial jmp instruction to the start range. This is the location // used to issue an error if the start range cannot be determined at compile-time. @@ -431,13 +437,13 @@ impl<'a> FunctionContext<'a> { // Compile the loop body self.builder.switch_to_block(loop_body); self.define(for_expr.index_variable, loop_index.into()); - self.codegen_expression(&for_expr.block); + self.codegen_expression(&for_expr.block)?; let new_loop_index = self.make_offset(loop_index, 1); self.builder.terminate_with_jmp(loop_entry, vec![new_loop_index]); // Finish by switching back to the end of the loop self.builder.switch_to_block(loop_end); - Self::unit_value() + Ok(Self::unit_value()) } /// Codegens an if expression, handling the case of what to do if there is no 'else'. @@ -464,8 +470,8 @@ impl<'a> FunctionContext<'a> { /// br end_if() /// end_if: // No block parameter is needed. Without an else, the unit value is always returned. /// ... This is the current insert point after codegen_if finishes ... - fn codegen_if(&mut self, if_expr: &ast::If) -> Values { - let condition = self.codegen_non_tuple_expression(&if_expr.condition); + fn codegen_if(&mut self, if_expr: &ast::If) -> Result { + let condition = self.codegen_non_tuple_expression(&if_expr.condition)?; let then_block = self.builder.insert_block(); let else_block = self.builder.insert_block(); @@ -473,7 +479,7 @@ impl<'a> FunctionContext<'a> { self.builder.terminate_with_jmpif(condition, then_block, else_block); self.builder.switch_to_block(then_block); - let then_value = self.codegen_expression(&if_expr.consequence); + let then_value = self.codegen_expression(&if_expr.consequence)?; let mut result = Self::unit_value(); @@ -483,7 +489,7 @@ impl<'a> FunctionContext<'a> { self.builder.terminate_with_jmp(end_block, then_values); self.builder.switch_to_block(else_block); - let else_value = self.codegen_expression(alternative); + let else_value = self.codegen_expression(alternative)?; let else_values = else_value.into_value_list(self); self.builder.terminate_with_jmp(end_block, else_values); @@ -501,31 +507,36 @@ impl<'a> FunctionContext<'a> { self.builder.switch_to_block(else_block); } - result + Ok(result) } - fn codegen_tuple(&mut self, tuple: &[Expression]) -> Values { - Tree::Branch(vecmap(tuple, |expr| self.codegen_expression(expr))) + fn codegen_tuple(&mut self, tuple: &[Expression]) -> Result { + Ok(Tree::Branch(try_vecmap(tuple, |expr| self.codegen_expression(expr))?)) } - fn codegen_extract_tuple_field(&mut self, tuple: &Expression, field_index: usize) -> Values { - let tuple = self.codegen_expression(tuple); - Self::get_field(tuple, field_index) + fn codegen_extract_tuple_field( + &mut self, + tuple: &Expression, + field_index: usize, + ) -> Result { + let tuple = self.codegen_expression(tuple)?; + Ok(Self::get_field(tuple, field_index)) } /// Generate SSA for a function call. Note that calls to built-in functions /// and intrinsics are also represented by the function call instruction. - fn codegen_call(&mut self, call: &ast::Call) -> Values { - let function = self.codegen_non_tuple_expression(&call.func); - let arguments = call - .arguments - .iter() - .flat_map(|argument| self.codegen_expression(argument).into_value_list(self)) - .collect::>(); + fn codegen_call(&mut self, call: &ast::Call) -> Result { + let function = self.codegen_non_tuple_expression(&call.func)?; + let mut arguments = Vec::with_capacity(call.arguments.len()); + + for argument in &call.arguments { + let mut values = self.codegen_expression(argument)?.into_value_list(self); + arguments.append(&mut values); + } self.codegen_intrinsic_call_checks(function, &arguments, call.location); - self.insert_call(function, arguments, &call.return_type, call.location) + Ok(self.insert_call(function, arguments, &call.return_type, call.location)) } fn codegen_intrinsic_call_checks( @@ -539,7 +550,8 @@ impl<'a> FunctionContext<'a> { { match intrinsic { Intrinsic::SliceInsert => { - let one = self.builder.numeric_constant(1u128, Type::field()); + let one = self.builder.field_constant(1u128); + // We add one here in the case of a slice insert as a slice insert at the length of the slice // can be converted to a slice push back let len_plus_one = self.builder.insert_binary(arguments[0], BinaryOp::Add, one); @@ -560,8 +572,8 @@ impl<'a> FunctionContext<'a> { /// If the variable is immutable, no special handling is necessary and we can return the given /// ValueId directly. If it is mutable, we'll need to allocate space for the value and store /// the initial value before returning the allocate instruction. - fn codegen_let(&mut self, let_expr: &ast::Let) -> Values { - let mut values = self.codegen_expression(&let_expr.expression); + fn codegen_let(&mut self, let_expr: &ast::Let) -> Result { + let mut values = self.codegen_expression(&let_expr.expression)?; if let_expr.mutable { values = values.map(|value| { @@ -571,7 +583,7 @@ impl<'a> FunctionContext<'a> { } self.define(let_expr.id, values); - Self::unit_value() + Ok(Self::unit_value()) } fn codegen_constrain( @@ -579,17 +591,17 @@ impl<'a> FunctionContext<'a> { expr: &Expression, location: Location, assert_message: Option, - ) -> Values { + ) -> Result { match expr { // If we're constraining an equality to be true then constrain the two sides directly. Expression::Binary(Binary { lhs, operator: BinaryOpKind::Equal, rhs, .. }) => { - let lhs = self.codegen_non_tuple_expression(lhs); - let rhs = self.codegen_non_tuple_expression(rhs); + let lhs = self.codegen_non_tuple_expression(lhs)?; + let rhs = self.codegen_non_tuple_expression(rhs)?; self.builder.set_location(location).insert_constrain(lhs, rhs, assert_message); } _ => { - let expr = self.codegen_non_tuple_expression(expr); + let expr = self.codegen_non_tuple_expression(expr)?; let true_literal = self.builder.numeric_constant(true, Type::bool()); self.builder.set_location(location).insert_constrain( expr, @@ -598,19 +610,19 @@ impl<'a> FunctionContext<'a> { ); } } - Self::unit_value() + Ok(Self::unit_value()) } - fn codegen_assign(&mut self, assign: &ast::Assign) -> Values { - let lhs = self.extract_current_value(&assign.lvalue); - let rhs = self.codegen_expression(&assign.expression); + fn codegen_assign(&mut self, assign: &ast::Assign) -> Result { + let lhs = self.extract_current_value(&assign.lvalue)?; + let rhs = self.codegen_expression(&assign.expression)?; self.assign_new_value(lhs, rhs); - Self::unit_value() + Ok(Self::unit_value()) } - fn codegen_semi(&mut self, expr: &Expression) -> Values { - self.codegen_expression(expr); - Self::unit_value() + fn codegen_semi(&mut self, expr: &Expression) -> Result { + self.codegen_expression(expr)?; + Ok(Self::unit_value()) } } diff --git a/compiler/noirc_frontend/Cargo.toml b/compiler/noirc_frontend/Cargo.toml index 1cb6e0c5c51..246d6617c94 100644 --- a/compiler/noirc_frontend/Cargo.toml +++ b/compiler/noirc_frontend/Cargo.toml @@ -18,6 +18,7 @@ chumsky.workspace = true thiserror.workspace = true smol_str.workspace = true serde_json.workspace = true +serde.workspace = true rustc-hash = "1.1.0" small-ord-set = "0.1.3" regex = "1.9.1" @@ -27,4 +28,4 @@ strum = "0.24" strum_macros = "0.24" [features] -aztec = [] \ No newline at end of file +aztec = [] diff --git a/compiler/noirc_frontend/src/ast/expression.rs b/compiler/noirc_frontend/src/ast/expression.rs index 4c8e98a0d4d..66b94797822 100644 --- a/compiler/noirc_frontend/src/ast/expression.rs +++ b/compiler/noirc_frontend/src/ast/expression.rs @@ -3,7 +3,7 @@ use std::fmt::Display; use crate::token::{Attributes, Token}; use crate::{ - Distinctness, Ident, Path, Pattern, Recoverable, Statement, StatementKind, + Distinctness, FunctionVisibility, Ident, Path, Pattern, Recoverable, Statement, StatementKind, UnresolvedTraitConstraint, UnresolvedType, UnresolvedTypeData, Visibility, }; use acvm::FieldElement; @@ -366,8 +366,8 @@ pub struct FunctionDefinition { /// True if this function was defined with the 'unconstrained' keyword pub is_unconstrained: bool, - /// True if this function was defined with the 'pub' keyword - pub is_public: bool, + /// Indicate if this function was defined with the 'pub' keyword + pub visibility: FunctionVisibility, pub generics: UnresolvedGenerics, pub parameters: Vec<(Pattern, UnresolvedType, Visibility)>, @@ -644,7 +644,7 @@ impl FunctionDefinition { is_open: false, is_internal: false, is_unconstrained: false, - is_public: false, + visibility: FunctionVisibility::Private, generics: generics.clone(), parameters: p, body: body.clone(), diff --git a/compiler/noirc_frontend/src/ast/mod.rs b/compiler/noirc_frontend/src/ast/mod.rs index 03106e521c0..7ce01f461ed 100644 --- a/compiler/noirc_frontend/src/ast/mod.rs +++ b/compiler/noirc_frontend/src/ast/mod.rs @@ -270,6 +270,14 @@ impl UnresolvedTypeExpression { } } +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +/// Represents whether the function can be called outside its module/crate +pub enum FunctionVisibility { + Public, + Private, + PublicCrate, +} + #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] /// Represents whether the parameter is public or known only to the prover. pub enum Visibility { diff --git a/compiler/noirc_frontend/src/ast/statement.rs b/compiler/noirc_frontend/src/ast/statement.rs index 639d4d8f763..9fe78f40c59 100644 --- a/compiler/noirc_frontend/src/ast/statement.rs +++ b/compiler/noirc_frontend/src/ast/statement.rs @@ -413,7 +413,14 @@ pub enum LValue { } #[derive(Debug, PartialEq, Eq, Clone)] -pub struct ConstrainStatement(pub Expression, pub Option); +pub struct ConstrainStatement(pub Expression, pub Option, pub ConstrainKind); + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum ConstrainKind { + Assert, + AssertEq, + Constrain, +} #[derive(Debug, PartialEq, Eq, Clone)] pub enum Pattern { diff --git a/compiler/noirc_frontend/src/graph/mod.rs b/compiler/noirc_frontend/src/graph/mod.rs index 3a40c87d8e7..452aef74b36 100644 --- a/compiler/noirc_frontend/src/graph/mod.rs +++ b/compiler/noirc_frontend/src/graph/mod.rs @@ -8,6 +8,7 @@ use std::{fmt::Display, str::FromStr}; use fm::FileId; use rustc_hash::{FxHashMap, FxHashSet}; +use serde::{Deserialize, Serialize}; use smol_str::SmolStr; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] @@ -32,7 +33,7 @@ impl CrateId { } } -#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd, Serialize, Deserialize)] pub struct CrateName(SmolStr); impl CrateName { @@ -90,6 +91,11 @@ mod crate_name { assert!(!CrateName::is_valid_name(&bad_char_string)); } } + + #[test] + fn it_rejects_bad_crate_names_when_deserializing() { + assert!(serde_json::from_str::("bad-name").is_err()); + } } #[derive(Debug, Clone, Default, PartialEq, Eq)] diff --git a/compiler/noirc_frontend/src/hir/aztec_library.rs b/compiler/noirc_frontend/src/hir/aztec_library.rs index 6f988577839..ad286a0ab91 100644 --- a/compiler/noirc_frontend/src/hir/aztec_library.rs +++ b/compiler/noirc_frontend/src/hir/aztec_library.rs @@ -16,8 +16,8 @@ use crate::{ UnresolvedType, UnresolvedTypeData, Visibility, }; use crate::{ - ForLoopStatement, FunctionDefinition, ImportStatement, NoirStruct, PrefixExpression, - Signedness, StatementKind, StructType, Type, TypeImpl, UnaryOp, + ForLoopStatement, FunctionDefinition, FunctionVisibility, ImportStatement, NoirStruct, + PrefixExpression, Signedness, StatementKind, StructType, Type, TypeImpl, UnaryOp, }; use fm::FileId; @@ -170,15 +170,9 @@ pub(crate) fn transform( // Covers all functions in the ast for submodule in ast.submodules.iter_mut().filter(|submodule| submodule.is_contract) { - let storage_defined = check_for_storage_definition(&submodule.contents); - - if transform_module(&mut submodule.contents, storage_defined) { - match check_for_aztec_dependency(crate_id, context) { - Ok(()) => include_relevant_imports(&mut submodule.contents), - Err(file_id) => { - return Err((DefCollectorErrorKind::AztecNotFound {}, file_id)); - } - } + if transform_module(&mut submodule.contents, crate_id, context)? { + check_for_aztec_dependency(crate_id, context)?; + include_relevant_imports(&mut submodule.contents); } } Ok(ast) @@ -209,19 +203,59 @@ fn include_relevant_imports(ast: &mut SortedModule) { } /// Creates an error alerting the user that they have not downloaded the Aztec-noir library -fn check_for_aztec_dependency(crate_id: &CrateId, context: &Context) -> Result<(), FileId> { +fn check_for_aztec_dependency( + crate_id: &CrateId, + context: &Context, +) -> Result<(), (DefCollectorErrorKind, FileId)> { let crate_graph = &context.crate_graph[crate_id]; let has_aztec_dependency = crate_graph.dependencies.iter().any(|dep| dep.as_name() == "aztec"); if has_aztec_dependency { Ok(()) } else { - Err(crate_graph.root_file_id) + Err((DefCollectorErrorKind::AztecNotFound {}, crate_graph.root_file_id)) } } // Check to see if the user has defined a storage struct fn check_for_storage_definition(module: &SortedModule) -> bool { - module.types.iter().any(|function| function.name.0.contents == "Storage") + module.types.iter().any(|r#struct| r#struct.name.0.contents == "Storage") +} + +// Check if "compute_note_hash_and_nullifier(Field,Field,Field,[Field; N]) -> [Field; 4]" is defined +fn check_for_compute_note_hash_and_nullifier_definition(module: &SortedModule) -> bool { + module.functions.iter().any(|func| { + func.def.name.0.contents == "compute_note_hash_and_nullifier" + && func.def.parameters.len() == 4 + && func.def.parameters[0].1.typ == UnresolvedTypeData::FieldElement + && func.def.parameters[1].1.typ == UnresolvedTypeData::FieldElement + && func.def.parameters[2].1.typ == UnresolvedTypeData::FieldElement + // checks if the 4th parameter is an array and the Box in + // Array(Option, Box) contains only fields + && match &func.def.parameters[3].1.typ { + UnresolvedTypeData::Array(_, inner_type) => { + match inner_type.typ { + UnresolvedTypeData::FieldElement => true, + _ => false, + } + }, + _ => false, + } + // We check the return type the same way as we did the 4th parameter + && match &func.def.return_type { + FunctionReturnType::Default(_) => false, + FunctionReturnType::Ty(unresolved_type) => { + match &unresolved_type.typ { + UnresolvedTypeData::Array(_, inner_type) => { + match inner_type.typ { + UnresolvedTypeData::FieldElement => true, + _ => false, + } + }, + _ => false, + } + } + } + }) } /// Checks if an attribute is a custom attribute with a specific name @@ -236,9 +270,26 @@ fn is_custom_attribute(attr: &SecondaryAttribute, attribute_name: &str) -> bool /// Determines if ast nodes are annotated with aztec attributes. /// For annotated functions it calls the `transform` function which will perform the required transformations. /// Returns true if an annotated node is found, false otherwise -fn transform_module(module: &mut SortedModule, storage_defined: bool) -> bool { +fn transform_module( + module: &mut SortedModule, + crate_id: &CrateId, + context: &Context, +) -> Result { let mut has_transformed_module = false; + // Check for a user defined storage struct + let storage_defined = check_for_storage_definition(&module); + + if storage_defined && check_for_compute_note_hash_and_nullifier_definition(&module) { + let crate_graph = &context.crate_graph[crate_id]; + return Err(( + DefCollectorErrorKind::AztecComputeNoteHashAndNullifierNotFound { + span: Span::default(), // Add a default span so we know which contract file the error originates from + }, + crate_graph.root_file_id, + )); + } + for structure in module.types.iter() { if structure.attributes.iter().any(|attr| matches!(attr, SecondaryAttribute::Event)) { module.impls.push(generate_selector_impl(structure)); @@ -262,7 +313,7 @@ fn transform_module(module: &mut SortedModule, storage_defined: bool) -> bool { has_transformed_module = true; } } - has_transformed_module + Ok(has_transformed_module) } /// If it does, it will insert the following things: @@ -434,7 +485,7 @@ fn generate_selector_impl(structure: &NoirStruct) -> TypeImpl { &FunctionReturnType::Ty(make_type(UnresolvedTypeData::FieldElement)), ); - selector_fn_def.is_public = true; + selector_fn_def.visibility = FunctionVisibility::Public; // Seems to be necessary on contract modules selector_fn_def.return_visibility = Visibility::Public; diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs index a458814ce9e..e59ab3e59f9 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs @@ -1,7 +1,7 @@ use super::dc_mod::collect_defs; use super::errors::{DefCollectorErrorKind, DuplicateType}; use crate::graph::CrateId; -use crate::hir::def_map::{CrateDefMap, LocalModuleId, ModuleDefId, ModuleId}; +use crate::hir::def_map::{CrateDefMap, LocalModuleId, ModuleData, ModuleDefId, ModuleId}; use crate::hir::resolution::errors::ResolverError; use crate::hir::resolution::import::PathResolutionError; use crate::hir::resolution::path_resolver::PathResolver; @@ -13,9 +13,7 @@ use crate::hir::resolution::{ use crate::hir::type_check::{type_check_func, TypeCheckError, TypeChecker}; use crate::hir::Context; use crate::hir_def::traits::{Trait, TraitConstant, TraitFunction, TraitImpl, TraitType}; -use crate::node_interner::{ - FuncId, NodeInterner, StmtId, StructId, TraitId, TraitImplKey, TypeAliasId, -}; +use crate::node_interner::{FuncId, NodeInterner, StmtId, StructId, TraitId, TypeAliasId}; use crate::parser::{ParserError, SortedModule}; use crate::{ @@ -92,6 +90,7 @@ pub struct UnresolvedTraitImpl { pub trait_path: Path, pub object_type: UnresolvedType, pub methods: UnresolvedFunctions, + pub generics: UnresolvedGenerics, } #[derive(Clone)] @@ -126,7 +125,7 @@ pub struct DefCollector { pub enum CompilationError { ParseError(ParserError), DefinitionError(DefCollectorErrorKind), - ResolveError(ResolverError), + ResolverError(ResolverError), TypeError(TypeCheckError), } @@ -135,7 +134,7 @@ impl From for CustomDiagnostic { match value { CompilationError::ParseError(error) => error.into(), CompilationError::DefinitionError(error) => error.into(), - CompilationError::ResolveError(error) => error.into(), + CompilationError::ResolverError(error) => error.into(), CompilationError::TypeError(error) => error.into(), } } @@ -155,7 +154,7 @@ impl From for CompilationError { impl From for CompilationError { fn from(value: ResolverError) -> Self { - CompilationError::ResolveError(value) + CompilationError::ResolverError(value) } } impl From for CompilationError { @@ -296,12 +295,6 @@ impl DefCollector { // globals will need to reference the struct type they're initialized to to ensure they are valid. resolved_globals.extend(resolve_globals(context, other_globals, crate_id)); - // Before we resolve any function symbols we must go through our impls and - // re-collect the methods within into their proper module. This cannot be - // done before resolution since we need to be able to resolve the type of the - // impl since that determines the module we should collect into. - errors.extend(collect_impls(context, crate_id, &def_collector.collected_impls)); - // Bind trait impls to their trait. Collect trait functions, that have a // default implementation, which hasn't been overridden. errors.extend(collect_trait_impls( @@ -310,6 +303,15 @@ impl DefCollector { &mut def_collector.collected_traits_impls, )); + // Before we resolve any function symbols we must go through our impls and + // re-collect the methods within into their proper module. This cannot be + // done before resolution since we need to be able to resolve the type of the + // impl since that determines the module we should collect into. + // + // These are resolved after trait impls so that struct methods are chosen + // over trait methods if there are name conflicts. + errors.extend(collect_impls(context, crate_id, &def_collector.collected_impls)); + // Lower each function in the crate. This is now possible since imports have been resolved let file_func_ids = resolve_free_functions( &mut context.def_interner, @@ -327,7 +329,6 @@ impl DefCollector { def_collector.collected_impls, &mut errors, ); - // resolve_trait_impls can fill different type of errors, therefore we pass errors by mut ref let file_trait_impls_ids = resolve_trait_impls( context, def_collector.collected_traits_impls, @@ -377,7 +378,6 @@ fn collect_impls( if let Some(struct_type) = get_struct_type(&typ) { let struct_type = struct_type.borrow(); - let type_module = struct_type.id.local_module_id(); // `impl`s are only allowed on types defined within the current crate if struct_type.id.krate() != crate_id { @@ -391,7 +391,7 @@ fn collect_impls( // Grab the module defined by the struct type. Note that impls are a case // where the module the methods are added to is not the same as the module // they are resolved in. - let module = &mut def_maps.get_mut(&crate_id).unwrap().modules[type_module.0]; + let module = get_module_mut(def_maps, struct_type.id.module_id()); for (_, method_id, method) in &unresolved.functions { // If this method was already declared, remove it from the module so it cannot @@ -413,6 +413,13 @@ fn collect_impls( errors } +fn get_module_mut( + def_maps: &mut BTreeMap, + module: ModuleId, +) -> &mut ModuleData { + &mut def_maps.get_mut(&module.krate).unwrap().modules[module.local_id.0] +} + fn collect_trait_impl_methods( interner: &mut NodeInterner, def_maps: &BTreeMap, @@ -494,25 +501,6 @@ fn collect_trait_impl_methods( errors } -fn add_method_to_struct_namespace( - current_def_map: &mut CrateDefMap, - struct_type: &Shared, - func_id: FuncId, - name_ident: &Ident, - trait_id: TraitId, -) -> Result<(), DefCollectorErrorKind> { - let struct_type = struct_type.borrow(); - let type_module = struct_type.id.local_module_id(); - let module = &mut current_def_map.modules[type_module.0]; - module.declare_trait_function(name_ident.clone(), func_id, trait_id).map_err( - |(first_def, second_def)| DefCollectorErrorKind::Duplicate { - typ: DuplicateType::TraitImplementation, - first_def, - second_def, - }, - ) -} - fn collect_trait_impl( context: &mut Context, crate_id: CrateId, @@ -535,28 +523,25 @@ fn collect_trait_impl( if let Some(trait_id) = trait_impl.trait_id { errors .extend(collect_trait_impl_methods(interner, def_maps, crate_id, trait_id, trait_impl)); - for (_, func_id, ast) in &trait_impl.methods.functions { - let file = def_maps[&crate_id].file_id(trait_impl.module_id); - - let path_resolver = StandardPathResolver::new(module); - let mut resolver = Resolver::new(interner, &path_resolver, def_maps, file); - resolver.add_generics(&ast.def.generics); - let typ = resolver.resolve_type(unresolved_type.clone()); - if let Some(struct_type) = get_struct_type(&typ) { - errors.extend(take_errors(trait_impl.file_id, resolver)); - let current_def_map = def_maps.get_mut(&struct_type.borrow().id.krate()).unwrap(); - match add_method_to_struct_namespace( - current_def_map, - struct_type, - *func_id, - ast.name_ident(), - trait_id, - ) { - Ok(()) => {} - Err(err) => { - errors.push((err.into(), trait_impl.file_id)); - } + let path_resolver = StandardPathResolver::new(module); + let file = def_maps[&crate_id].file_id(trait_impl.module_id); + let mut resolver = Resolver::new(interner, &path_resolver, def_maps, file); + resolver.add_generics(&trait_impl.generics); + let typ = resolver.resolve_type(unresolved_type); + errors.extend(take_errors(trait_impl.file_id, resolver)); + + if let Some(struct_type) = get_struct_type(&typ) { + let struct_type = struct_type.borrow(); + let module = get_module_mut(def_maps, struct_type.id.module_id()); + + for (_, method_id, method) in &trait_impl.methods.functions { + // If this method was already declared, remove it from the module so it cannot + // be accessed with the `TypeName::method` syntax. We'll check later whether the + // object types in each method overlap or not. If they do, we issue an error. + // If not, that is specialization which is allowed. + if module.declare_function(method.name_ident().clone(), *method_id).is_err() { + module.remove_function(method.name_ident()); } } } @@ -841,7 +826,7 @@ fn take_errors_filter_self_not_resolved( } fn take_errors(file_id: FileId, resolver: Resolver<'_>) -> Vec<(CompilationError, FileId)> { - resolver.take_errors().iter().cloned().map(|e| (e.into(), file_id)).collect() + vecmap(resolver.take_errors(), |e| (e.into(), file_id)) } /// Create the mappings from TypeId -> TraitType @@ -942,7 +927,7 @@ fn resolve_impls( let method_name = interner.function_name(method_id).to_owned(); if let Some(first_fn) = - interner.add_method(&self_type, method_name.clone(), *method_id) + interner.add_method(&self_type, method_name.clone(), *method_id, false) { let error = ResolverError::DuplicateDefinition { name: method_name, @@ -974,17 +959,14 @@ fn resolve_trait_impls( let local_mod_id = trait_impl.module_id; let module_id = ModuleId { krate: crate_id, local_id: local_mod_id }; let path_resolver = StandardPathResolver::new(module_id); - let trait_definition_ident = trait_impl.trait_path.last_segment(); let self_type_span = unresolved_type.span; - let self_type = { - let mut resolver = - Resolver::new(interner, &path_resolver, &context.def_maps, trait_impl.file_id); - resolver.resolve_type(unresolved_type.clone()) - }; - - let maybe_trait_id = trait_impl.trait_id; + let mut resolver = + Resolver::new(interner, &path_resolver, &context.def_maps, trait_impl.file_id); + resolver.add_generics(&trait_impl.generics); + let self_type = resolver.resolve_type(unresolved_type.clone()); + let generics = resolver.get_generics().to_vec(); let mut impl_methods = resolve_function_set( interner, @@ -992,16 +974,23 @@ fn resolve_trait_impls( &context.def_maps, trait_impl.methods.clone(), Some(self_type.clone()), - vec![], // TODO + generics, errors, ); + let maybe_trait_id = trait_impl.trait_id; if let Some(trait_id) = maybe_trait_id { for (_, func) in &impl_methods { interner.set_function_trait(*func, self_type.clone(), trait_id); } } + if matches!(self_type, Type::MutableReference(_)) { + let span = self_type_span.unwrap_or_else(|| trait_impl.trait_path.span()); + let error = DefCollectorErrorKind::MutableReferenceInTraitImpl { span }; + errors.push((error.into(), trait_impl.file_id)); + } + let mut new_resolver = Resolver::new(interner, &path_resolver, &context.def_maps, trait_impl.file_id); new_resolver.set_self_type(Some(self_type.clone())); @@ -1009,28 +998,27 @@ fn resolve_trait_impls( if let Some(trait_id) = maybe_trait_id { check_methods_signatures(&mut new_resolver, &impl_methods, trait_id, errors); - let key = TraitImplKey { typ: self_type.clone(), trait_id }; - if let Some(prev_trait_impl_ident) = interner.get_trait_implementation(&key) { - let err = DefCollectorErrorKind::Duplicate { - typ: DuplicateType::TraitImplementation, - first_def: prev_trait_impl_ident.borrow().ident.clone(), - second_def: trait_definition_ident.clone(), - }; - errors.push((err.into(), trait_impl.methods.file_id)); - } else { - let resolved_trait_impl = Shared::new(TraitImpl { - ident: trait_impl.trait_path.last_segment().clone(), + let resolved_trait_impl = Shared::new(TraitImpl { + ident: trait_impl.trait_path.last_segment().clone(), + typ: self_type.clone(), + trait_id, + file: trait_impl.file_id, + methods: vecmap(&impl_methods, |(_, func_id)| *func_id), + }); + + if let Some((prev_span, prev_file)) = + interner.add_trait_implementation(self_type.clone(), trait_id, resolved_trait_impl) + { + let error = DefCollectorErrorKind::OverlappingImpl { typ: self_type.clone(), - trait_id, - methods: vecmap(&impl_methods, |(_, func_id)| *func_id), - }); - if !interner.add_trait_implementation(&key, resolved_trait_impl.clone()) { - let error = DefCollectorErrorKind::TraitImplNotAllowedFor { - trait_path: trait_impl.trait_path.clone(), - span: self_type_span.unwrap_or_else(|| trait_impl.trait_path.span()), - }; - errors.push((error.into(), trait_impl.file_id)); - } + span: self_type_span.unwrap_or_else(|| trait_impl.trait_path.span()), + }; + errors.push((error.into(), trait_impl.file_id)); + + // The 'previous impl defined here' note must be a separate error currently + // since it may be in a different file and all errors have the same file id. + let error = DefCollectorErrorKind::OverlappingImplNote { span: prev_span }; + errors.push((error.into(), prev_file)); } methods.append(&mut impl_methods); @@ -1173,6 +1161,11 @@ fn resolve_function_set( resolver.set_self_type(self_type.clone()); resolver.set_trait_id(unresolved_functions.trait_id); + // Without this, impl methods can accidentally be placed in contracts. See #3254 + if self_type.is_some() { + resolver.set_in_contract(false); + } + let (hir_func, func_meta, errs) = resolver.resolve_function(func, func_id); interner.push_fn_meta(func_meta, func_id); interner.update_fn(func_id, hir_func); diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs index ed48d7fbb51..32f39ce4952 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs @@ -7,7 +7,7 @@ use noirc_errors::Location; use crate::{ graph::CrateId, hir::def_collector::dc_crate::{UnresolvedStruct, UnresolvedTrait}, - node_interner::{TraitId, TypeAliasId}, + node_interner::{FunctionModifiers, TraitId, TypeAliasId}, parser::{SortedModule, SortedSubModule}, FunctionDefinition, Ident, LetStatement, NoirFunction, NoirStruct, NoirTrait, NoirTraitImpl, NoirTypeAlias, TraitImplItem, TraitItem, TypeImpl, @@ -145,12 +145,13 @@ impl<'a> ModCollector<'a> { for trait_impl in impls { let trait_name = trait_impl.trait_name.clone(); - let unresolved_functions = + let mut unresolved_functions = self.collect_trait_impl_function_overrides(context, &trait_impl, krate); let module = ModuleId { krate, local_id: self.module_id }; - for (_, func_id, noir_function) in &unresolved_functions.functions { + for (_, func_id, noir_function) in &mut unresolved_functions.functions { + noir_function.def.where_clause.append(&mut trait_impl.where_clause.clone()); context.def_interner.push_function(*func_id, &noir_function.def, module); } @@ -160,6 +161,7 @@ impl<'a> ModCollector<'a> { trait_path: trait_name, methods: unresolved_functions, object_type: trait_impl.object_type, + generics: trait_impl.impl_generics, trait_id: None, // will be filled later }; @@ -378,11 +380,22 @@ impl<'a> ModCollector<'a> { body, } => { let func_id = context.def_interner.push_empty_fn(); + let modifiers = FunctionModifiers { + name: name.to_string(), + visibility: crate::FunctionVisibility::Public, + // TODO(Maddiaa): Investigate trait implementations with attributes see: https://github.com/noir-lang/noir/issues/2629 + attributes: crate::token::Attributes::empty(), + is_unconstrained: false, + contract_function_type: None, + is_internal: None, + }; + + context.def_interner.push_function_definition(func_id, modifiers, id.0); + match self.def_collector.def_map.modules[id.0.local_id.0] .declare_function(name.clone(), func_id) { Ok(()) => { - // TODO(Maddiaa): Investigate trait implementations with attributes see: https://github.com/noir-lang/noir/issues/2629 if let Some(body) = body { let impl_method = NoirFunction::normal(FunctionDefinition::normal( diff --git a/compiler/noirc_frontend/src/hir/def_collector/errors.rs b/compiler/noirc_frontend/src/hir/def_collector/errors.rs index 4e7f8ad286b..2abacbc64da 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/errors.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/errors.rs @@ -33,8 +33,12 @@ pub enum DefCollectorErrorKind { PathResolutionError(PathResolutionError), #[error("Non-struct type used in impl")] NonStructTypeInImpl { span: Span }, - #[error("Trait implementation is not allowed for this")] - TraitImplNotAllowedFor { trait_path: Path, span: Span }, + #[error("Cannot implement trait on a mutable reference type")] + MutableReferenceInTraitImpl { span: Span }, + #[error("Impl for type `{typ}` overlaps with existing impl")] + OverlappingImpl { span: Span, typ: crate::Type }, + #[error("Previous impl defined here")] + OverlappingImplNote { span: Span }, #[error("Cannot `impl` a type defined outside the current crate")] ForeignImpl { span: Span, type_name: String }, #[error("Mismatch number of parameters in of trait implementation")] @@ -57,13 +61,18 @@ pub enum DefCollectorErrorKind { ModuleAlreadyPartOfCrate { mod_name: Ident, span: Span }, #[error("Module was originally declared here")] ModuleOriginallyDefined { mod_name: Ident, span: Span }, - #[cfg(feature = "aztec")] - #[error("Aztec dependency not found. Please add aztec as a dependency in your Cargo.toml")] - AztecNotFound {}, #[error( "Either the type or the trait must be from the same crate as the trait implementation" )] TraitImplOrphaned { span: Span }, + + // Aztec feature flag errors + #[cfg(feature = "aztec")] + #[error("Aztec dependency not found. Please add aztec as a dependency in your Cargo.toml")] + AztecNotFound {}, + #[cfg(feature = "aztec")] + #[error("compute_note_hash_and_nullifier function not found. Define it in your contract.")] + AztecComputeNoteHashAndNullifierNotFound { span: Span }, } impl DefCollectorErrorKind { @@ -125,10 +134,25 @@ impl From for Diagnostic { "Only struct types may have implementation methods".into(), span, ), - DefCollectorErrorKind::TraitImplNotAllowedFor { trait_path, span } => { + DefCollectorErrorKind::MutableReferenceInTraitImpl { span } => Diagnostic::simple_error( + "Trait impls are not allowed on mutable reference types".into(), + "Try using a struct type here instead".into(), + span, + ), + DefCollectorErrorKind::OverlappingImpl { span, typ } => { + Diagnostic::simple_error( + format!("Impl for type `{typ}` overlaps with existing impl"), + "Overlapping impl".into(), + span, + ) + } + DefCollectorErrorKind::OverlappingImplNote { span } => { + // This should be a note or part of the previous error eventually. + // This must be an error to appear next to the previous OverlappingImpl + // error since we sort warnings first. Diagnostic::simple_error( - format!("Only limited types may implement trait `{trait_path}`"), - "Only limited types may implement traits".into(), + "Previous impl defined here".into(), + "Previous impl defined here".into(), span, ) } @@ -194,15 +218,21 @@ impl From for Diagnostic { let secondary = String::new(); Diagnostic::simple_error(message, secondary, span) } - #[cfg(feature = "aztec")] - DefCollectorErrorKind::AztecNotFound {} => Diagnostic::from_message( - "Aztec dependency not found. Please add aztec as a dependency in your Cargo.toml", - ), DefCollectorErrorKind::TraitImplOrphaned { span } => Diagnostic::simple_error( "Orphaned trait implementation".into(), "Either the type or the trait must be from the same crate as the trait implementation".into(), span, ), + #[cfg(feature = "aztec")] + DefCollectorErrorKind::AztecNotFound {} => Diagnostic::from_message( + "Aztec dependency not found. Please add aztec as a dependency in your Cargo.toml", + ), + #[cfg(feature = "aztec")] + DefCollectorErrorKind::AztecComputeNoteHashAndNullifierNotFound {span} => Diagnostic::simple_error( + "compute_note_hash_and_nullifier function not found. Define it in your contract.".into(), + "".into(), + span + ), } } } diff --git a/compiler/noirc_frontend/src/hir/resolution/errors.rs b/compiler/noirc_frontend/src/hir/resolution/errors.rs index afdf65e5c5c..c2f787313c6 100644 --- a/compiler/noirc_frontend/src/hir/resolution/errors.rs +++ b/compiler/noirc_frontend/src/hir/resolution/errors.rs @@ -78,6 +78,8 @@ pub enum ResolverError { InvalidClosureEnvironment { typ: Type, span: Span }, #[error("{name} is private and not visible from the current module")] PrivateFunctionCalled { name: String, span: Span }, + #[error("{name} is not visible from the current crate")] + NonCrateFunctionCalled { name: String, span: Span }, #[error("Only sized types may be used in the entry point to a program")] InvalidTypeForEntryPoint { span: Span }, } @@ -296,6 +298,9 @@ impl From for Diagnostic { ResolverError::PrivateFunctionCalled { span, name } => Diagnostic::simple_warning( format!("{name} is private and not visible from the current module"), format!("{name} is private"), span), + ResolverError::NonCrateFunctionCalled { span, name } => Diagnostic::simple_warning( + format!("{name} is not visible from the current crate"), + format!("{name} is only visible within its crate"), span), ResolverError::InvalidTypeForEntryPoint { span } => Diagnostic::simple_error( "Only sized types may be used in the entry point to a program".to_string(), "Slices, references, or any type containing them may not be used in main or a contract function".to_string(), span), diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index a4a85d85929..b3796418ab3 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -36,10 +36,10 @@ use crate::{ StatementKind, }; use crate::{ - ArrayLiteral, ContractFunctionType, Distinctness, Generics, LValue, NoirStruct, NoirTypeAlias, - Path, PathKind, Pattern, Shared, StructType, Type, TypeAliasType, TypeBinding, TypeVariable, - UnaryOp, UnresolvedGenerics, UnresolvedTraitConstraint, UnresolvedType, UnresolvedTypeData, - UnresolvedTypeExpression, Visibility, ERROR_IDENT, + ArrayLiteral, ContractFunctionType, Distinctness, FunctionVisibility, Generics, LValue, + NoirStruct, NoirTypeAlias, Path, PathKind, Pattern, Shared, StructType, Type, TypeAliasType, + TypeBinding, TypeVariable, UnaryOp, UnresolvedGenerics, UnresolvedTraitConstraint, + UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression, Visibility, ERROR_IDENT, }; use fm::FileId; use iter_extended::vecmap; @@ -87,6 +87,14 @@ pub struct Resolver<'a> { /// Set to the current type if we're resolving an impl self_type: Option, + /// True if the current module is a contract. + /// This is usually determined by self.path_resolver.module_id(), but it can + /// be overriden for impls. Impls are an odd case since the methods within resolve + /// as if they're in the parent module, but should be placed in a child module. + /// Since they should be within a child module, in_contract is manually set to false + /// for these so we can still resolve them in the parent module without them being in a contract. + in_contract: bool, + /// Contains a mapping of the current struct or functions's generics to /// unique type variables if we're resolving a struct. Empty otherwise. /// This is a Vec rather than a map to preserve the order a functions generics @@ -119,6 +127,9 @@ impl<'a> Resolver<'a> { def_maps: &'a BTreeMap, file: FileId, ) -> Resolver<'a> { + let module_id = path_resolver.module_id(); + let in_contract = module_id.module(def_maps).is_contract; + Self { path_resolver, def_maps, @@ -131,6 +142,7 @@ impl<'a> Resolver<'a> { errors: Vec::new(), lambda_stack: Vec::new(), file, + in_contract, } } @@ -805,9 +817,16 @@ impl<'a> Resolver<'a> { } } + /// Override whether this name resolver is within a contract or not. + /// This will affect which types are allowed as parameters to methods as well + /// as which modifiers are allowed on a function. + pub(crate) fn set_in_contract(&mut self, in_contract: bool) { + self.in_contract = in_contract; + } + /// True if the 'pub' keyword is allowed on parameters in this function fn pub_allowed(&self, func: &NoirFunction) -> bool { - if self.in_contract() { + if self.in_contract { !func.def.is_unconstrained } else { func.name() == MAIN_FUNCTION @@ -815,7 +834,7 @@ impl<'a> Resolver<'a> { } fn is_entry_point_function(&self, func: &NoirFunction) -> bool { - if self.in_contract() { + if self.in_contract { func.attributes().is_contract_entry_point() } else { func.name() == MAIN_FUNCTION @@ -824,7 +843,7 @@ impl<'a> Resolver<'a> { /// True if the `distinct` keyword is allowed on a function's return type fn distinct_allowed(&self, func: &NoirFunction) -> bool { - if self.in_contract() { + if self.in_contract { // "open" and "unconstrained" functions are compiled to brillig and thus duplication of // witness indices in their abis is not a concern. !func.def.is_unconstrained && !func.def.is_open @@ -836,7 +855,7 @@ impl<'a> Resolver<'a> { fn handle_function_type(&mut self, function: &FuncId) { let function_type = self.interner.function_modifiers(function).contract_function_type; - if !self.in_contract() && function_type == Some(ContractFunctionType::Open) { + if !self.in_contract && function_type == Some(ContractFunctionType::Open) { let span = self.interner.function_ident(function).span(); self.errors.push(ResolverError::ContractFunctionTypeInNormalFunction { span }); self.interner.function_modifiers_mut(function).contract_function_type = None; @@ -844,7 +863,7 @@ impl<'a> Resolver<'a> { } fn handle_is_function_internal(&mut self, function: &FuncId) { - if !self.in_contract() { + if !self.in_contract { if self.interner.function_modifiers(function).is_internal == Some(true) { let span = self.interner.function_ident(function).span(); self.push_err(ResolverError::ContractFunctionInternalInNormalFunction { span }); @@ -1039,20 +1058,39 @@ impl<'a> Resolver<'a> { } } - // Issue an error if the given private function is being called from a non-child module - fn check_can_reference_private_function(&mut self, func: FuncId, span: Span) { + // Issue an error if the given private function is being called from a non-child module, or + // if the given pub(crate) function is being called from another crate + fn check_can_reference_function( + &mut self, + func: FuncId, + span: Span, + visibility: FunctionVisibility, + ) { let function_module = self.interner.function_module(func); let current_module = self.path_resolver.module_id(); let same_crate = function_module.krate == current_module.krate; let krate = function_module.krate; let current_module = current_module.local_id; - - if !same_crate - || !self.module_descendent_of_target(krate, function_module.local_id, current_module) - { - let name = self.interner.function_name(&func).to_string(); - self.errors.push(ResolverError::PrivateFunctionCalled { span, name }); + let name = self.interner.function_name(&func).to_string(); + match visibility { + FunctionVisibility::Public => (), + FunctionVisibility::Private => { + if !same_crate + || !self.module_descendent_of_target( + krate, + function_module.local_id, + current_module, + ) + { + self.errors.push(ResolverError::PrivateFunctionCalled { span, name }); + } + } + FunctionVisibility::PublicCrate => { + if !same_crate { + self.errors.push(ResolverError::NonCrateFunctionCalled { span, name }); + } + } } } @@ -1146,9 +1184,15 @@ impl<'a> Resolver<'a> { if hir_ident.id != DefinitionId::dummy_id() { match self.interner.definition(hir_ident.id).kind { DefinitionKind::Function(id) => { - if self.interner.function_visibility(id) == Visibility::Private { + if self.interner.function_visibility(id) + != FunctionVisibility::Public + { let span = hir_ident.location.span; - self.check_can_reference_private_function(id, span); + self.check_can_reference_function( + id, + span, + self.interner.function_visibility(id), + ); } } DefinitionKind::Global(_) => {} @@ -1605,11 +1649,6 @@ impl<'a> Resolver<'a> { } } - fn in_contract(&self) -> bool { - let module_id = self.path_resolver.module_id(); - module_id.module(self.def_maps).is_contract - } - fn resolve_fmt_str_literal(&mut self, str: String, call_expr_span: Span) -> HirLiteral { let re = Regex::new(r"\{([a-zA-Z0-9_]+)\}") .expect("ICE: an invalid regex pattern was used for checking format strings"); diff --git a/compiler/noirc_frontend/src/hir/type_check/expr.rs b/compiler/noirc_frontend/src/hir/type_check/expr.rs index 7f21bdbebda..00534cc3aa2 100644 --- a/compiler/noirc_frontend/src/hir/type_check/expr.rs +++ b/compiler/noirc_frontend/src/hir/type_check/expr.rs @@ -880,20 +880,14 @@ impl<'interner> TypeChecker<'interner> { // This may be a struct or a primitive type. Type::MutableReference(element) => self .interner - .lookup_mut_primitive_trait_method(element.as_ref(), method_name) + .lookup_primitive_trait_method_mut(element.as_ref(), method_name) .map(HirMethodReference::FuncId) .or_else(|| self.lookup_method(element, method_name, expr_id)), // If we fail to resolve the object to a struct type, we have no way of type // checking its arguments as we can't even resolve the name of the function Type::Error => None, - // In the future we could support methods for non-struct types if we have a context - // (in the interner?) essentially resembling HashMap - other => match self - .interner - .lookup_primitive_method(other, method_name) - .or_else(|| self.interner.lookup_primitive_trait_method(other, method_name)) - { + other => match self.interner.lookup_primitive_method(other, method_name) { Some(method_id) => Some(HirMethodReference::FuncId(method_id)), None => { self.errors.push(TypeCheckError::UnresolvedMethodCall { diff --git a/compiler/noirc_frontend/src/hir/type_check/mod.rs b/compiler/noirc_frontend/src/hir/type_check/mod.rs index 035dbbc3518..2a12d68406d 100644 --- a/compiler/noirc_frontend/src/hir/type_check/mod.rs +++ b/compiler/noirc_frontend/src/hir/type_check/mod.rs @@ -15,7 +15,7 @@ pub use errors::TypeCheckError; use crate::{ hir_def::{expr::HirExpression, stmt::HirStatement}, - node_interner::{ExprId, FuncId, NodeInterner, StmtId, TraitImplKey}, + node_interner::{ExprId, FuncId, NodeInterner, StmtId}, Type, }; @@ -65,8 +65,10 @@ pub fn type_check_func(interner: &mut NodeInterner, func_id: FuncId) -> Vec, // methods[i] is the implementation of trait.methods[i] for Type typ } diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 110334f331e..59404c5f298 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -1069,6 +1069,75 @@ impl Type { } } + /// Replace each NamedGeneric (and TypeVariable) in this type with a fresh type variable + pub(crate) fn instantiate_named_generics(&self, interner: &NodeInterner) -> Type { + let mut substitutions = HashMap::new(); + self.find_all_unbound_type_variables(interner, &mut substitutions); + self.substitute(&substitutions) + } + + /// For each unbound type variable in the current type, add a type binding to the given list + /// to bind the unbound type variable to a fresh type variable. + fn find_all_unbound_type_variables( + &self, + interner: &NodeInterner, + bindings: &mut TypeBindings, + ) { + match self { + Type::FieldElement + | Type::Integer(_, _) + | Type::Bool + | Type::Unit + | Type::TraitAsType(_) + | Type::Constant(_) + | Type::NotConstant + | Type::Error => (), + Type::Array(length, elem) => { + length.find_all_unbound_type_variables(interner, bindings); + elem.find_all_unbound_type_variables(interner, bindings); + } + Type::String(length) => length.find_all_unbound_type_variables(interner, bindings), + Type::FmtString(length, env) => { + length.find_all_unbound_type_variables(interner, bindings); + env.find_all_unbound_type_variables(interner, bindings); + } + Type::Struct(_, generics) => { + for generic in generics { + generic.find_all_unbound_type_variables(interner, bindings); + } + } + Type::Tuple(fields) => { + for field in fields { + field.find_all_unbound_type_variables(interner, bindings); + } + } + Type::Function(args, ret, env) => { + for arg in args { + arg.find_all_unbound_type_variables(interner, bindings); + } + ret.find_all_unbound_type_variables(interner, bindings); + env.find_all_unbound_type_variables(interner, bindings); + } + Type::MutableReference(elem) => { + elem.find_all_unbound_type_variables(interner, bindings); + } + Type::Forall(_, typ) => typ.find_all_unbound_type_variables(interner, bindings), + Type::TypeVariable(type_variable, _) | Type::NamedGeneric(type_variable, _) => { + match &*type_variable.borrow() { + TypeBinding::Bound(binding) => { + binding.find_all_unbound_type_variables(interner, bindings); + } + TypeBinding::Unbound(id) => { + if !bindings.contains_key(id) { + let fresh_type_variable = interner.next_type_variable(); + bindings.insert(*id, (type_variable.clone(), fresh_type_variable)); + } + } + } + } + } + } + /// Substitute any type variables found within this type with the /// given bindings if found. If a type variable is not found within /// the given TypeBindings, it is unchanged. diff --git a/compiler/noirc_frontend/src/lexer/errors.rs b/compiler/noirc_frontend/src/lexer/errors.rs index 9bc9a820239..a2a4056f1d0 100644 --- a/compiler/noirc_frontend/src/lexer/errors.rs +++ b/compiler/noirc_frontend/src/lexer/errors.rs @@ -11,15 +11,15 @@ use thiserror::Error; pub enum LexerErrorKind { #[error("An unexpected character {:?} was found.", found)] UnexpectedCharacter { span: Span, expected: String, found: Option }, - #[error("NotADoubleChar : {:?} is not a double char token", found)] + #[error("Internal error: Tried to lex {:?} as a double char token", found)] NotADoubleChar { span: Span, found: Token }, - #[error("InvalidIntegerLiteral : {:?} is not a integer", found)] + #[error("Invalid integer literal, {:?} is not a integer", found)] InvalidIntegerLiteral { span: Span, found: String }, - #[error("MalformedFuncAttribute : {:?} is not a valid attribute", found)] + #[error("{:?} is not a valid attribute", found)] MalformedFuncAttribute { span: Span, found: String }, - #[error("TooManyBits")] + #[error("Integer type is larger than the maximum supported size of u127")] TooManyBits { span: Span, max: u32, got: u32 }, - #[error("LogicalAnd used instead of bitwise and")] + #[error("Logical and used instead of bitwise and")] LogicalAnd { span: Span }, #[error("Unterminated block comment")] UnterminatedBlockComment { span: Span }, diff --git a/compiler/noirc_frontend/src/monomorphization/ast.rs b/compiler/noirc_frontend/src/monomorphization/ast.rs index c67b8f8bcec..0a005d766fe 100644 --- a/compiler/noirc_frontend/src/monomorphization/ast.rs +++ b/compiler/noirc_frontend/src/monomorphization/ast.rs @@ -81,7 +81,7 @@ pub struct For { #[derive(Debug, Clone, Hash)] pub enum Literal { Array(ArrayLiteral), - Integer(FieldElement, Type), + Integer(FieldElement, Type, Location), Bool(bool), Str(String), FmtStr(String, u64, Box), diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index 764159a4df3..0785f150a1e 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -24,7 +24,7 @@ use crate::{ stmt::{HirAssignStatement, HirLValue, HirLetStatement, HirPattern, HirStatement}, types, }, - node_interner::{self, DefinitionKind, NodeInterner, StmtId, TraitImplKey, TraitMethodId}, + node_interner::{self, DefinitionKind, NodeInterner, StmtId, TraitMethodId}, token::FunctionAttribute, ContractFunctionType, FunctionKind, Type, TypeBinding, TypeBindings, TypeVariableKind, Visibility, @@ -312,7 +312,8 @@ impl<'interner> Monomorphizer<'interner> { HirExpression::Literal(HirLiteral::Bool(value)) => Literal(Bool(value)), HirExpression::Literal(HirLiteral::Integer(value)) => { let typ = self.convert_type(&self.interner.id_type(expr)); - Literal(Integer(value, typ)) + let location = self.interner.id_location(expr); + Literal(Integer(value, typ, location)) } HirExpression::Literal(HirLiteral::Array(array)) => match array { HirArrayLiteral::Standard(array) => self.standard_array(expr, array), @@ -672,7 +673,8 @@ impl<'interner> Monomorphizer<'interner> { }; let value = FieldElement::from(value as u128); - ast::Expression::Literal(ast::Literal::Integer(value, ast::Type::Field)) + let location = self.interner.id_location(expr_id); + ast::Expression::Literal(ast::Literal::Integer(value, ast::Type::Field, location)) } } } @@ -818,10 +820,7 @@ impl<'interner> Monomorphizer<'interner> { let trait_impl = self .interner - .get_trait_implementation(&TraitImplKey { - typ: self_type.follow_bindings(), - trait_id: method.trait_id, - }) + .lookup_trait_implementation(self_type.follow_bindings(), method.trait_id) .expect("ICE: missing trait impl - should be caught during type checking"); let hir_func_id = trait_impl.borrow().methods[method.method_index]; @@ -990,30 +989,32 @@ impl<'interner> Monomorphizer<'interner> { if let ast::Expression::Ident(ident) = func { if let Definition::Builtin(opcode) = &ident.definition { // TODO(#1736): Move this builtin to the SSA pass + let location = self.interner.expr_location(expr_id); return match opcode.as_str() { - "modulus_num_bits" => Some(ast::Expression::Literal(ast::Literal::Integer( - (FieldElement::max_num_bits() as u128).into(), - ast::Type::Field, - ))), + "modulus_num_bits" => { + let bits = (FieldElement::max_num_bits() as u128).into(); + let typ = ast::Type::Field; + Some(ast::Expression::Literal(ast::Literal::Integer(bits, typ, location))) + } "zeroed" => { let location = self.interner.expr_location(expr_id); Some(self.zeroed_value_of_type(result_type, location)) } "modulus_le_bits" => { let bits = FieldElement::modulus().to_radix_le(2); - Some(self.modulus_array_literal(bits, 1)) + Some(self.modulus_array_literal(bits, 1, location)) } "modulus_be_bits" => { let bits = FieldElement::modulus().to_radix_be(2); - Some(self.modulus_array_literal(bits, 1)) + Some(self.modulus_array_literal(bits, 1, location)) } "modulus_be_bytes" => { let bytes = FieldElement::modulus().to_bytes_be(); - Some(self.modulus_array_literal(bytes, 8)) + Some(self.modulus_array_literal(bytes, 8, location)) } "modulus_le_bytes" => { let bytes = FieldElement::modulus().to_bytes_le(); - Some(self.modulus_array_literal(bytes, 8)) + Some(self.modulus_array_literal(bytes, 8, location)) } _ => None, }; @@ -1022,12 +1023,17 @@ impl<'interner> Monomorphizer<'interner> { None } - fn modulus_array_literal(&self, bytes: Vec, arr_elem_bits: u32) -> ast::Expression { + fn modulus_array_literal( + &self, + bytes: Vec, + arr_elem_bits: u32, + location: Location, + ) -> ast::Expression { use ast::*; let int_type = Type::Integer(crate::Signedness::Unsigned, arr_elem_bits); let bytes_as_expr = vecmap(bytes, |byte| { - Expression::Literal(Literal::Integer((byte as u128).into(), int_type.clone())) + Expression::Literal(Literal::Integer((byte as u128).into(), int_type.clone(), location)) }); let typ = Type::Array(bytes_as_expr.len() as u64, Box::new(int_type)); @@ -1277,7 +1283,8 @@ impl<'interner> Monomorphizer<'interner> { ) -> ast::Expression { match typ { ast::Type::Field | ast::Type::Integer(..) => { - ast::Expression::Literal(ast::Literal::Integer(0_u128.into(), typ.clone())) + let typ = typ.clone(); + ast::Expression::Literal(ast::Literal::Integer(0_u128.into(), typ, location)) } ast::Type::Bool => ast::Expression::Literal(ast::Literal::Bool(false)), // There is no unit literal currently. Replace it with 'false' since it should be ignored diff --git a/compiler/noirc_frontend/src/monomorphization/printer.rs b/compiler/noirc_frontend/src/monomorphization/printer.rs index ff2b7d0d256..e79330de6f8 100644 --- a/compiler/noirc_frontend/src/monomorphization/printer.rs +++ b/compiler/noirc_frontend/src/monomorphization/printer.rs @@ -93,7 +93,7 @@ impl AstPrinter { self.print_comma_separated(&array.contents, f)?; write!(f, "]") } - super::ast::Literal::Integer(x, _) => x.fmt(f), + super::ast::Literal::Integer(x, _, _) => x.fmt(f), super::ast::Literal::Bool(x) => x.fmt(f), super::ast::Literal::Str(s) => s.fmt(f), super::ast::Literal::FmtStr(s, _, _) => { diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index 8512e38fec0..ef5f72df4a2 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -21,17 +21,10 @@ use crate::hir_def::{ }; use crate::token::{Attributes, SecondaryAttribute}; use crate::{ - ContractFunctionType, FunctionDefinition, Generics, Shared, TypeAliasType, TypeBinding, - TypeBindings, TypeVariable, TypeVariableId, TypeVariableKind, Visibility, + ContractFunctionType, FunctionDefinition, FunctionVisibility, Generics, Shared, TypeAliasType, + TypeBinding, TypeBindings, TypeVariable, TypeVariableId, TypeVariableKind, }; -#[derive(Eq, PartialEq, Hash, Clone)] -pub struct TraitImplKey { - pub typ: Type, - pub trait_id: TraitId, - // pub generics: Generics - TODO -} - type StructAttributes = Vec; /// The node interner is the central storage location of all nodes in Noir's Hir (the @@ -92,7 +85,19 @@ pub struct NodeInterner { // Trait implementation map // For each type that implements a given Trait ( corresponding TraitId), there should be an entry here // The purpose for this hashmap is to detect duplication of trait implementations ( if any ) - trait_implementations: HashMap>, + // + // Indexed by TraitImplIds + trait_implementations: Vec>, + + /// Trait implementations on each type. This is expected to always have the same length as + /// `self.trait_implementations`. + /// + /// For lack of a better name, this maps a trait id and type combination + /// to a corresponding impl if one is available for the type. Due to generics, + /// we cannot map from Type directly to impl, we need to iterate a Vec of all impls + /// of that trait to see if any type may match. This can be further optimized later + /// by splitting it up by type. + trait_implementation_map: HashMap>, /// Map from ExprId (referring to a Function/Method call) to its corresponding TypeBindings, /// filled out during type checking from instantiated variables. Used during monomorphization @@ -113,16 +118,27 @@ pub struct NodeInterner { /// may have both `impl Struct { fn foo(){} }` and `impl Struct { fn foo(){} }`. /// If this happens, the returned Vec will have 2 entries and we'll need to further /// disambiguate them by checking the type of each function. - struct_methods: HashMap<(StructId, String), Vec>, + struct_methods: HashMap<(StructId, String), Methods>, /// Methods on primitive types defined in the stdlib. - primitive_methods: HashMap<(TypeMethodKey, String), FuncId>, + primitive_methods: HashMap<(TypeMethodKey, String), Methods>, // For trait implementation functions, this is their self type and trait they belong to func_id_to_trait: HashMap, +} - /// Trait implementations on primitive types - primitive_trait_impls: HashMap<(Type, String), FuncId>, +/// Represents the methods on a given type that each share the same name. +/// +/// Methods are split into inherent methods and trait methods. If there is +/// ever a name that is defined on both a type directly, and defined indirectly +/// via a trait impl, the direct (inherent) name will always take precedence. +/// +/// Additionally, types can define specialized impls with methods of the same name +/// as long as these specialized impls do not overlap. E.g. `impl Struct` and `impl Struct` +#[derive(Default)] +pub struct Methods { + direct: Vec, + trait_impl_methods: Vec, } /// All the information from a function that is filled out during definition collection rather than @@ -132,7 +148,7 @@ pub struct FunctionModifiers { pub name: String, /// Whether the function is `pub` or not. - pub visibility: Visibility, + pub visibility: FunctionVisibility, pub attributes: Attributes, @@ -155,7 +171,7 @@ impl FunctionModifiers { pub fn new() -> Self { Self { name: String::new(), - visibility: Visibility::Public, + visibility: FunctionVisibility::Public, attributes: Attributes::empty(), is_unconstrained: false, is_internal: None, @@ -257,6 +273,9 @@ impl TraitId { } } +#[derive(Debug, Eq, PartialEq, Hash, Clone, Copy)] +pub struct TraitImplId(usize); + #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct TraitMethodId { pub trait_id: TraitId, @@ -380,14 +399,14 @@ impl Default for NodeInterner { struct_attributes: HashMap::new(), type_aliases: Vec::new(), traits: HashMap::new(), - trait_implementations: HashMap::new(), + trait_implementations: Vec::new(), + trait_implementation_map: HashMap::new(), instantiation_bindings: HashMap::new(), field_indices: HashMap::new(), next_type_variable_id: std::cell::Cell::new(0), globals: HashMap::new(), struct_methods: HashMap::new(), primitive_methods: HashMap::new(), - primitive_trait_impls: HashMap::new(), }; // An empty block expression is used often, we add this into the `node` on startup @@ -618,9 +637,10 @@ impl NodeInterner { #[cfg(test)] pub fn push_test_function_definition(&mut self, name: String) -> FuncId { let id = self.push_fn(HirFunction::empty()); - let modifiers = FunctionModifiers::new(); + let mut modifiers = FunctionModifiers::new(); + modifiers.name = name; let module = ModuleId::dummy_id(); - self.push_function_definition(name, id, modifiers, module); + self.push_function_definition(id, modifiers, module); id } @@ -631,28 +651,27 @@ impl NodeInterner { module: ModuleId, ) -> DefinitionId { use ContractFunctionType::*; - let name = function.name.0.contents.clone(); // We're filling in contract_function_type and is_internal now, but these will be verified // later during name resolution. let modifiers = FunctionModifiers { name: function.name.0.contents.clone(), - visibility: if function.is_public { Visibility::Public } else { Visibility::Private }, + visibility: function.visibility, attributes: function.attributes.clone(), is_unconstrained: function.is_unconstrained, contract_function_type: Some(if function.is_open { Open } else { Secret }), is_internal: Some(function.is_internal), }; - self.push_function_definition(name, id, modifiers, module) + self.push_function_definition(id, modifiers, module) } pub fn push_function_definition( &mut self, - name: String, func: FuncId, modifiers: FunctionModifiers, module: ModuleId, ) -> DefinitionId { + let name = modifiers.name.clone(); self.function_modifiers.insert(func, modifiers); self.function_modules.insert(func, module); self.push_definition(name, false, DefinitionKind::Function(func)) @@ -670,7 +689,7 @@ impl NodeInterner { /// /// The underlying function_visibilities map is populated during def collection, /// so this function can be called anytime afterward. - pub fn function_visibility(&self, func: FuncId) -> Visibility { + pub fn function_visibility(&self, func: FuncId) -> FunctionVisibility { self.function_modifiers[&func].visibility } @@ -891,6 +910,7 @@ impl NodeInterner { self_type: &Type, method_name: String, method_id: FuncId, + is_trait_method: bool, ) -> Option { match self_type { Type::Struct(struct_type, _generics) => { @@ -901,68 +921,79 @@ impl NodeInterner { } let key = (id, method_name); - self.struct_methods.entry(key).or_default().push(method_id); + self.struct_methods.entry(key).or_default().add_method(method_id, is_trait_method); None } Type::Error => None, + Type::MutableReference(element) => { + self.add_method(element, method_name, method_id, is_trait_method) + } other => { let key = get_type_method_key(self_type).unwrap_or_else(|| { unreachable!("Cannot add a method to the unsupported type '{}'", other) }); - self.primitive_methods.insert((key, method_name), method_id) + self.primitive_methods + .entry((key, method_name)) + .or_default() + .add_method(method_id, is_trait_method); + None } } } - pub fn get_trait_implementation(&self, key: &TraitImplKey) -> Option> { - self.trait_implementations.get(key).cloned() + pub fn get_trait_implementation(&self, id: TraitImplId) -> Shared { + self.trait_implementations[id.0].clone() + } + + pub fn lookup_trait_implementation( + &self, + object_type: Type, + trait_id: TraitId, + ) -> Option> { + let impls = self.trait_implementation_map.get(&trait_id)?; + for (existing_object_type, impl_id) in impls { + if object_type.try_unify(existing_object_type).is_ok() { + return Some(self.get_trait_implementation(*impl_id)); + } + } + None } pub fn add_trait_implementation( &mut self, - key: &TraitImplKey, + object_type: Type, + trait_id: TraitId, trait_impl: Shared, - ) -> bool { - self.trait_implementations.insert(key.clone(), trait_impl.clone()); - match &key.typ { - Type::Struct(..) => { - for func_id in &trait_impl.borrow().methods { - let method_name = self.function_name(func_id).to_owned(); - self.add_method(&key.typ, method_name, *func_id); - } - true - } - Type::FieldElement - | Type::Unit - | Type::Array(..) - | Type::Integer(..) - | Type::Bool - | Type::Tuple(..) - | Type::String(..) - | Type::FmtString(..) - | Type::Function(..) - | Type::MutableReference(..) => { - for func_id in &trait_impl.borrow().methods { - let method_name = self.function_name(func_id).to_owned(); - let key = (key.typ.clone(), method_name); - self.primitive_trait_impls.insert(key, *func_id); + ) -> Option<(Span, FileId)> { + let id = TraitImplId(self.trait_implementations.len()); + + self.trait_implementations.push(trait_impl.clone()); + + if let Some(entries) = self.trait_implementation_map.get(&trait_id) { + // Check that this new impl does not overlap with any existing impls first + for (existing_object_type, existing_impl_id) in entries { + // Instantiate named generics so that S overlaps with S + let object_type = object_type.instantiate_named_generics(self); + let existing_object_type = existing_object_type.instantiate_named_generics(self); + + if object_type.try_unify(&existing_object_type).is_ok() { + // Overlapping impl + let existing_impl = &self.trait_implementations[existing_impl_id.0]; + let existing_impl = existing_impl.borrow(); + return Some((existing_impl.ident.span(), existing_impl.file)); } - true } - // We should allow implementing traits NamedGenerics will also eventually be possible once we support generics - // impl Foo for T - // but it's fine not to include these until we do. - Type::NamedGeneric(..) => false, - // prohibited are internal types (like NotConstant, TypeVariable, Forall, and Error) that - // aren't possible for users to write anyway - Type::TypeVariable(..) - | Type::Forall(..) - | Type::NotConstant - | Type::Constant(..) - | Type::TraitAsType(..) - | Type::Error => false, } + + for method in &trait_impl.borrow().methods { + let method_name = self.function_name(method).to_owned(); + self.add_method(&object_type, method_name, *method, true); + } + + let entries = self.trait_implementation_map.entry(trait_id).or_default(); + entries.push((object_type, id)); + None } /// Search by name for a method on the given struct. @@ -981,25 +1012,94 @@ impl NodeInterner { typ: &Type, id: StructId, method_name: &str, - check_type: bool, + force_type_check: bool, ) -> Option { let methods = self.struct_methods.get(&(id, method_name.to_owned()))?; // If there is only one method, just return it immediately. // It will still be typechecked later. - if !check_type && methods.len() == 1 { - return Some(methods[0]); + if !force_type_check { + if let Some(method) = methods.get_unambiguous() { + return Some(method); + } + } + + self.find_matching_method(typ, methods, method_name) + } + + /// Select the 1 matching method with an object type matching `typ` + fn find_matching_method( + &self, + typ: &Type, + methods: &Methods, + method_name: &str, + ) -> Option { + if let Some(method) = methods.find_matching_method(typ, self) { + Some(method) + } else { + // Failed to find a match for the type in question, switch to looking at impls + // for all types `T`, e.g. `impl Foo for T` + let key = &(TypeMethodKey::Generic, method_name.to_owned()); + let global_methods = self.primitive_methods.get(key)?; + global_methods.find_matching_method(typ, self) } + } + + /// Looks up a given method name on the given primitive type. + pub fn lookup_primitive_method(&self, typ: &Type, method_name: &str) -> Option { + let key = get_type_method_key(typ)?; + let methods = self.primitive_methods.get(&(key, method_name.to_owned()))?; + self.find_matching_method(typ, methods, method_name) + } + pub fn lookup_primitive_trait_method_mut( + &self, + typ: &Type, + method_name: &str, + ) -> Option { + let typ = Type::MutableReference(Box::new(typ.clone())); + self.lookup_primitive_method(&typ, method_name) + } +} + +impl Methods { + /// Get a single, unambiguous reference to a name if one exists. + /// If not, there may be multiple methods of the same name for a given + /// type or there may be no methods at all. + fn get_unambiguous(&self) -> Option { + if self.direct.len() == 1 { + Some(self.direct[0]) + } else if self.direct.is_empty() && self.trait_impl_methods.len() == 1 { + Some(self.trait_impl_methods[0]) + } else { + None + } + } + + fn add_method(&mut self, method: FuncId, is_trait_method: bool) { + if is_trait_method { + self.trait_impl_methods.push(method); + } else { + self.direct.push(method); + } + } + + /// Iterate through each method, starting with the direct methods + fn iter(&self) -> impl Iterator + '_ { + self.direct.iter().copied().chain(self.trait_impl_methods.iter().copied()) + } + + /// Select the 1 matching method with an object type matching `typ` + fn find_matching_method(&self, typ: &Type, interner: &NodeInterner) -> Option { // When adding methods we always check they do not overlap, so there should be // at most 1 matching method in this list. - for method in methods { - match self.function_meta(method).typ.instantiate(self).0 { + for method in self.iter() { + match interner.function_meta(&method).typ.instantiate(interner).0 { Type::Function(args, _, _) => { if let Some(object) = args.get(0) { // TODO #3089: This is dangerous! try_unify may commit type bindings even on failure if object.try_unify(typ).is_ok() { - return Some(*method); + return Some(method); } } } @@ -1007,29 +1107,8 @@ impl NodeInterner { other => unreachable!("Expected function type, found {other}"), } } - None } - - /// Looks up a given method name on the given primitive type. - pub fn lookup_primitive_method(&self, typ: &Type, method_name: &str) -> Option { - get_type_method_key(typ) - .and_then(|key| self.primitive_methods.get(&(key, method_name.to_owned())).copied()) - } - - pub fn lookup_primitive_trait_method(&self, typ: &Type, method_name: &str) -> Option { - self.primitive_trait_impls.get(&(typ.clone(), method_name.to_string())).copied() - } - - pub fn lookup_mut_primitive_trait_method( - &self, - typ: &Type, - method_name: &str, - ) -> Option { - self.primitive_trait_impls - .get(&(Type::MutableReference(Box::new(typ.clone())), method_name.to_string())) - .copied() - } } /// These are the primitive type variants that we support adding methods to @@ -1041,9 +1120,11 @@ enum TypeMethodKey { Array, Bool, String, + FmtString, Unit, Tuple, Function, + Generic, } fn get_type_method_key(typ: &Type) -> Option { @@ -1056,20 +1137,20 @@ fn get_type_method_key(typ: &Type) -> Option { Type::TypeVariable(_, TypeVariableKind::IntegerOrField) => Some(FieldOrInt), Type::Bool => Some(Bool), Type::String(_) => Some(String), + Type::FmtString(_, _) => Some(FmtString), Type::Unit => Some(Unit), Type::Tuple(_) => Some(Tuple), Type::Function(_, _, _) => Some(Function), + Type::NamedGeneric(_, _) => Some(Generic), Type::MutableReference(element) => get_type_method_key(element), // We do not support adding methods to these types Type::TypeVariable(_, _) - | Type::NamedGeneric(_, _) | Type::Forall(_, _) | Type::Constant(_) | Type::Error | Type::NotConstant | Type::Struct(_, _) - | Type::TraitAsType(_) - | Type::FmtString(_, _) => None, + | Type::TraitAsType(_) => None, } } diff --git a/compiler/noirc_frontend/src/parser/errors.rs b/compiler/noirc_frontend/src/parser/errors.rs index 0c0d4e7645c..b9b11d8c99e 100644 --- a/compiler/noirc_frontend/src/parser/errors.rs +++ b/compiler/noirc_frontend/src/parser/errors.rs @@ -128,7 +128,7 @@ impl std::fmt::Display for ParserError { impl From for Diagnostic { fn from(error: ParserError) -> Diagnostic { - match &error.reason { + match error.reason { Some(reason) => { match reason { ParserErrorReason::ConstrainDeprecated => Diagnostic::simple_error( @@ -146,11 +146,11 @@ impl From for Diagnostic { "".into(), error.span, ), - reason @ ParserErrorReason::ExpectedPatternButFoundType(ty) => { - Diagnostic::simple_error(reason.to_string(), format!("{ty} is a type and cannot be used as a variable name"), error.span) + ParserErrorReason::ExpectedPatternButFoundType(ty) => { + Diagnostic::simple_error("Expected a ; separating these two statements".into(), format!("{ty} is a type and cannot be used as a variable name"), error.span) } + ParserErrorReason::Lexer(error) => error.into(), other => { - Diagnostic::simple_error(format!("{other}"), String::new(), error.span) } } diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index c9765ca1946..cee824a51c9 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -37,11 +37,12 @@ use crate::lexer::Lexer; use crate::parser::{force, ignore_then_commit, statement_recovery}; use crate::token::{Attribute, Attributes, Keyword, SecondaryAttribute, Token, TokenKind}; use crate::{ - BinaryOp, BinaryOpKind, BlockExpression, ConstrainStatement, Distinctness, FunctionDefinition, - FunctionReturnType, Ident, IfExpression, InfixExpression, LValue, Lambda, Literal, - NoirFunction, NoirStruct, NoirTrait, NoirTraitImpl, NoirTypeAlias, Path, PathKind, Pattern, - Recoverable, Statement, TraitBound, TraitImplItem, TraitItem, TypeImpl, UnaryOp, - UnresolvedTraitConstraint, UnresolvedTypeExpression, UseTree, UseTreeKind, Visibility, + BinaryOp, BinaryOpKind, BlockExpression, ConstrainKind, ConstrainStatement, Distinctness, + FunctionDefinition, FunctionReturnType, FunctionVisibility, Ident, IfExpression, + InfixExpression, LValue, Lambda, Literal, NoirFunction, NoirStruct, NoirTrait, NoirTraitImpl, + NoirTypeAlias, Path, PathKind, Pattern, Recoverable, Statement, TraitBound, TraitImplItem, + TraitItem, TypeImpl, UnaryOp, UnresolvedTraitConstraint, UnresolvedTypeExpression, UseTree, + UseTreeKind, Visibility, }; use chumsky::prelude::*; @@ -183,9 +184,15 @@ fn function_definition(allow_self: bool) -> impl NoirParser { name, attributes: attrs, is_unconstrained: modifiers.0, - is_open: modifiers.1, - is_internal: modifiers.2, - is_public: modifiers.3, + is_open: modifiers.2, + is_internal: modifiers.3, + visibility: if modifiers.1 { + FunctionVisibility::PublicCrate + } else if modifiers.4 { + FunctionVisibility::Public + } else { + FunctionVisibility::Private + }, generics, parameters, body, @@ -198,19 +205,34 @@ fn function_definition(allow_self: bool) -> impl NoirParser { }) } -/// function_modifiers: 'unconstrained'? 'open'? 'internal'? +/// function_modifiers: 'unconstrained'? 'pub(crate)'? 'pub'? 'open'? 'internal'? /// -/// returns (is_unconstrained, is_open, is_internal) for whether each keyword was present -fn function_modifiers() -> impl NoirParser<(bool, bool, bool, bool)> { +/// returns (is_unconstrained, is_pub_crate, is_open, is_internal, is_pub) for whether each keyword was present +fn function_modifiers() -> impl NoirParser<(bool, bool, bool, bool, bool)> { keyword(Keyword::Unconstrained) .or_not() + .then(is_pub_crate()) .then(keyword(Keyword::Pub).or_not()) .then(keyword(Keyword::Open).or_not()) .then(keyword(Keyword::Internal).or_not()) - .map(|(((unconstrained, public), open), internal)| { - (unconstrained.is_some(), open.is_some(), internal.is_some(), public.is_some()) + .map(|((((unconstrained, pub_crate), public), open), internal)| { + ( + unconstrained.is_some(), + pub_crate, + open.is_some(), + internal.is_some(), + public.is_some(), + ) }) } +fn is_pub_crate() -> impl NoirParser { + (keyword(Keyword::Pub) + .then_ignore(just(Token::LeftParen)) + .then_ignore(keyword(Keyword::Crate)) + .then_ignore(just(Token::RightParen))) + .or_not() + .map(|a| a.is_some()) +} /// non_empty_ident_list: ident ',' non_empty_ident_list /// | ident @@ -800,7 +822,7 @@ where keyword(Keyword::Constrain).labelled(ParsingRuleLabel::Statement), expr_parser, ) - .map(|expr| StatementKind::Constrain(ConstrainStatement(expr, None))) + .map(|expr| StatementKind::Constrain(ConstrainStatement(expr, None, ConstrainKind::Constrain))) .validate(|expr, span, emit| { emit(ParserError::with_reason(ParserErrorReason::ConstrainDeprecated, span)); expr @@ -828,7 +850,11 @@ where } } - StatementKind::Constrain(ConstrainStatement(condition, message_str)) + StatementKind::Constrain(ConstrainStatement( + condition, + message_str, + ConstrainKind::Assert, + )) }) } @@ -859,7 +885,11 @@ where emit(ParserError::with_reason(ParserErrorReason::AssertMessageNotString, span)); } } - StatementKind::Constrain(ConstrainStatement(predicate, message_str)) + StatementKind::Constrain(ConstrainStatement( + predicate, + message_str, + ConstrainKind::AssertEq, + )) }) } @@ -1993,7 +2023,7 @@ mod test { match parse_with(assertion(expression()), "assert(x == y, \"assertion message\")").unwrap() { - StatementKind::Constrain(ConstrainStatement(_, message)) => { + StatementKind::Constrain(ConstrainStatement(_, message, _)) => { assert_eq!(message, Some("assertion message".to_owned())); } _ => unreachable!(), @@ -2017,7 +2047,7 @@ mod test { match parse_with(assertion_eq(expression()), "assert_eq(x, y, \"assertion message\")") .unwrap() { - StatementKind::Constrain(ConstrainStatement(_, message)) => { + StatementKind::Constrain(ConstrainStatement(_, message, _)) => { assert_eq!(message, Some("assertion message".to_owned())); } _ => unreachable!(), diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 91fce8d8862..6a1cf80accd 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -461,7 +461,7 @@ mod test { for (err, _file_id) in errors { match &err { - CompilationError::ResolveError(ResolverError::PathResolutionError( + CompilationError::ResolverError(ResolverError::PathResolutionError( PathResolutionError::Unresolved(ident), )) => { assert_eq!(ident, "NotAType"); @@ -533,18 +533,18 @@ mod test { } } - fn main() { - } + fn main() {} "; let errors = get_program_errors(src); assert!(!has_parser_error(&errors)); assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); for (err, _file_id) in errors { match &err { - CompilationError::DefinitionError( - DefCollectorErrorKind::TraitImplNotAllowedFor { trait_path, span: _ }, - ) => { - assert_eq!(trait_path.as_string(), "Default"); + CompilationError::ResolverError(ResolverError::Expected { + expected, got, .. + }) => { + assert_eq!(expected, "type"); + assert_eq!(got, "function"); } _ => { panic!("No other errors are expected! Found = {:?}", err); @@ -658,18 +658,15 @@ mod test { "; let errors = get_program_errors(src); assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); + assert!(errors.len() == 2, "Expected 2 errors, got: {:?}", errors); for (err, _file_id) in errors { match &err { - CompilationError::DefinitionError(DefCollectorErrorKind::Duplicate { - typ, - first_def, - second_def, - }) => { - assert_eq!(typ, &DuplicateType::TraitImplementation); - assert_eq!(first_def, "Default"); - assert_eq!(second_def, "Default"); - } + CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImpl { + .. + }) => (), + CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImplNote { + .. + }) => (), _ => { panic!("No other errors are expected! Found = {:?}", err); } @@ -699,18 +696,15 @@ mod test { "; let errors = get_program_errors(src); assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); + assert!(errors.len() == 2, "Expected 2 errors, got: {:?}", errors); for (err, _file_id) in errors { match &err { - CompilationError::DefinitionError(DefCollectorErrorKind::Duplicate { - typ, - first_def, - second_def, - }) => { - assert_eq!(typ, &DuplicateType::TraitImplementation); - assert_eq!(first_def, "Default"); - assert_eq!(second_def, "Default"); - } + CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImpl { + .. + }) => (), + CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImplNote { + .. + }) => (), _ => { panic!("No other errors are expected! Found = {:?}", err); } @@ -810,7 +804,7 @@ mod test { assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); // It should be regarding the unused variable match &errors[0].0 { - CompilationError::ResolveError(ResolverError::UnusedVariable { ident }) => { + CompilationError::ResolverError(ResolverError::UnusedVariable { ident }) => { assert_eq!(&ident.0.contents, "y"); } _ => unreachable!("we should only have an unused var error"), @@ -829,7 +823,7 @@ mod test { assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); // It should be regarding the unresolved var `z` (Maybe change to undeclared and special case) match &errors[0].0 { - CompilationError::ResolveError(ResolverError::VariableNotDeclared { + CompilationError::ResolverError(ResolverError::VariableNotDeclared { name, span: _, }) => assert_eq!(name, "z"), @@ -848,7 +842,7 @@ mod test { assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); for (compilation_error, _file_id) in errors { match compilation_error { - CompilationError::ResolveError(err) => { + CompilationError::ResolverError(err) => { match err { ResolverError::PathResolutionError(PathResolutionError::Unresolved( name, @@ -892,7 +886,7 @@ mod test { // `foo::bar` does not exist for (compilation_error, _file_id) in errors { match compilation_error { - CompilationError::ResolveError(err) => { + CompilationError::ResolverError(err) => { match err { ResolverError::UnusedVariable { ident } => { assert_eq!(&ident.0.contents, "z"); @@ -1069,12 +1063,13 @@ mod test { for (err, _file_id) in errors { match &err { - CompilationError::ResolveError(ResolverError::VariableNotDeclared { - name, .. + CompilationError::ResolverError(ResolverError::VariableNotDeclared { + name, + .. }) => { assert_eq!(name, "i"); } - CompilationError::ResolveError(ResolverError::NumericConstantInFormatString { + CompilationError::ResolverError(ResolverError::NumericConstantInFormatString { name, .. }) => { diff --git a/compiler/source-resolver/package.json b/compiler/source-resolver/package.json index 8fa2763c2e9..0726b544610 100644 --- a/compiler/source-resolver/package.json +++ b/compiler/source-resolver/package.json @@ -1,6 +1,6 @@ { "name": "@noir-lang/source-resolver", - "version": "0.17.0", + "version": "0.18.0", "license": "MIT", "main": "./lib-node/index_node.js", "types": "./types/index_node.d.ts", diff --git a/compiler/wasm/fixtures/deps/lib-a/Nargo.toml b/compiler/wasm/fixtures/deps/lib-a/Nargo.toml new file mode 100644 index 00000000000..3a02ccc1086 --- /dev/null +++ b/compiler/wasm/fixtures/deps/lib-a/Nargo.toml @@ -0,0 +1,8 @@ +[package] +name="lib_a" +type="lib" +authors = [""] +compiler_version = "0.1" + +[dependencies] +lib_b = { path = "../lib-b" } diff --git a/compiler/wasm/fixtures/deps/lib-a/src/lib.nr b/compiler/wasm/fixtures/deps/lib-a/src/lib.nr new file mode 100644 index 00000000000..abb4302ba38 --- /dev/null +++ b/compiler/wasm/fixtures/deps/lib-a/src/lib.nr @@ -0,0 +1,7 @@ + +use dep::lib_b::assert_non_zero; + +pub fn divide(a: u64, b: u64) -> u64 { + assert_non_zero(b); + a / b +} diff --git a/compiler/wasm/fixtures/deps/lib-b/Nargo.toml b/compiler/wasm/fixtures/deps/lib-b/Nargo.toml new file mode 100644 index 00000000000..99db61e4bfa --- /dev/null +++ b/compiler/wasm/fixtures/deps/lib-b/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name="lib_b" +type="lib" +authors = [""] +compiler_version = "0.1" + +[dependencies] diff --git a/compiler/wasm/fixtures/deps/lib-b/src/lib.nr b/compiler/wasm/fixtures/deps/lib-b/src/lib.nr new file mode 100644 index 00000000000..ad8b26020e3 --- /dev/null +++ b/compiler/wasm/fixtures/deps/lib-b/src/lib.nr @@ -0,0 +1,4 @@ + +pub fn assert_non_zero(x: u64) { + assert(x != 0); +} diff --git a/compiler/wasm/fixtures/deps/noir-script/Nargo.toml b/compiler/wasm/fixtures/deps/noir-script/Nargo.toml new file mode 100644 index 00000000000..0df22c2be7e --- /dev/null +++ b/compiler/wasm/fixtures/deps/noir-script/Nargo.toml @@ -0,0 +1,8 @@ +[package] +name="noir_wasm_testing" +type="bin" +authors = [""] +compiler_version = "0.1" + +[dependencies] +lib_a = { path="../lib-a" } diff --git a/compiler/wasm/fixtures/deps/noir-script/src/main.nr b/compiler/wasm/fixtures/deps/noir-script/src/main.nr new file mode 100644 index 00000000000..d46597fca2e --- /dev/null +++ b/compiler/wasm/fixtures/deps/noir-script/src/main.nr @@ -0,0 +1,4 @@ +use dep::lib_a::divide; +fn main(x : u64, y : pub u64) { + divide(x, y); +} diff --git a/compiler/wasm/noir-script/Nargo.toml b/compiler/wasm/fixtures/simple/noir-script/Nargo.toml similarity index 100% rename from compiler/wasm/noir-script/Nargo.toml rename to compiler/wasm/fixtures/simple/noir-script/Nargo.toml diff --git a/compiler/wasm/noir-script/src/main.nr b/compiler/wasm/fixtures/simple/noir-script/src/main.nr similarity index 64% rename from compiler/wasm/noir-script/src/main.nr rename to compiler/wasm/fixtures/simple/noir-script/src/main.nr index 36fcc1916f5..b1889529f86 100644 --- a/compiler/wasm/noir-script/src/main.nr +++ b/compiler/wasm/fixtures/simple/noir-script/src/main.nr @@ -1,3 +1,3 @@ fn main(x : u64, y : pub u64) { - assert(x < y); + assert(x < y); } diff --git a/compiler/wasm/package.json b/compiler/wasm/package.json index 808a9f93caa..3ed8d03cf5d 100644 --- a/compiler/wasm/package.json +++ b/compiler/wasm/package.json @@ -3,7 +3,7 @@ "collaborators": [ "The Noir Team " ], - "version": "0.17.0", + "version": "0.18.0", "license": "(MIT OR Apache-2.0)", "main": "./nodejs/noir_wasm.js", "types": "./web/noir_wasm.d.ts", diff --git a/compiler/wasm/src/compile.rs b/compiler/wasm/src/compile.rs index 66f08dbabf5..ac6219ee625 100644 --- a/compiler/wasm/src/compile.rs +++ b/compiler/wasm/src/compile.rs @@ -1,6 +1,6 @@ use fm::FileManager; use gloo_utils::format::JsValueSerdeExt; -use js_sys::Array; +use js_sys::Object; use nargo::artifacts::{ contract::{PreprocessedContract, PreprocessedContractFunction}, program::PreprocessedProgram, @@ -9,29 +9,54 @@ use noirc_driver::{ add_dep, compile_contract, compile_main, prepare_crate, prepare_dependency, CompileOptions, CompiledContract, CompiledProgram, NOIR_ARTIFACT_VERSION_STRING, }; -use noirc_frontend::{graph::CrateGraph, hir::Context}; -use std::path::Path; +use noirc_frontend::{ + graph::{CrateGraph, CrateId, CrateName}, + hir::Context, +}; +use serde::Deserialize; +use std::{collections::HashMap, path::Path}; use wasm_bindgen::prelude::*; -use crate::errors::JsCompileError; +use crate::errors::{CompileError, JsCompileError}; const BACKEND_IDENTIFIER: &str = "acvm-backend-barretenberg"; +#[wasm_bindgen(typescript_custom_section)] +const DEPENDENCY_GRAPH: &'static str = r#" +export type DependencyGraph = { + root_dependencies: readonly string[]; + library_dependencies: Readonly>; +} +"#; + #[wasm_bindgen] extern "C" { - #[wasm_bindgen(extends = Array, js_name = "StringArray", typescript_type = "string[]")] + #[wasm_bindgen(extends = Object, js_name = "DependencyGraph", typescript_type = "DependencyGraph")] #[derive(Clone, Debug, PartialEq, Eq)] - pub type StringArray; + pub type JsDependencyGraph; +} + +#[derive(Deserialize)] +struct DependencyGraph { + root_dependencies: Vec, + library_dependencies: HashMap>, } #[wasm_bindgen] pub fn compile( entry_point: String, contracts: Option, - dependencies: Option, + dependency_graph: Option, ) -> Result { console_error_panic_hook::set_once(); + let dependency_graph: DependencyGraph = if let Some(dependency_graph) = dependency_graph { + ::into_serde(&JsValue::from(dependency_graph)) + .map_err(|err| err.to_string())? + } else { + DependencyGraph { root_dependencies: vec![], library_dependencies: HashMap::new() } + }; + let root = Path::new("/"); let fm = FileManager::new(root, Box::new(get_non_stdlib_asset)); let graph = CrateGraph::default(); @@ -40,12 +65,7 @@ pub fn compile( let path = Path::new(&entry_point); let crate_id = prepare_crate(&mut context, path); - let dependencies: Vec = dependencies - .map(|array| array.iter().map(|element| element.as_string().unwrap()).collect()) - .unwrap_or_default(); - for dependency in dependencies { - add_noir_lib(&mut context, dependency.as_str()); - } + process_dependency_graph(&mut context, dependency_graph); let compile_options = CompileOptions::default(); @@ -57,7 +77,11 @@ pub fn compile( if contracts.unwrap_or_default() { let compiled_contract = compile_contract(&mut context, crate_id, &compile_options) .map_err(|errs| { - JsCompileError::new("Failed to compile contract", errs, &context.file_manager) + CompileError::with_file_diagnostics( + "Failed to compile contract", + errs, + &context.file_manager, + ) })? .0; @@ -71,7 +95,11 @@ pub fn compile( } else { let compiled_program = compile_main(&mut context, crate_id, &compile_options, None, true) .map_err(|errs| { - JsCompileError::new("Failed to compile program", errs, &context.file_manager) + CompileError::with_file_diagnostics( + "Failed to compile program", + errs, + &context.file_manager, + ) })? .0; @@ -85,34 +113,38 @@ pub fn compile( } } -fn add_noir_lib(context: &mut Context, library_name: &str) { - let path_to_lib = Path::new(&library_name).join("lib.nr"); - let library_crate_id = prepare_dependency(context, &path_to_lib); - - add_dep(context, *context.root_crate_id(), library_crate_id, library_name.parse().unwrap()); - - // TODO: Remove this code that attaches every crate to every other crate as a dependency - let root_crate_id = context.root_crate_id(); - let stdlib_crate_id = context.stdlib_crate_id(); - let other_crate_ids: Vec<_> = context - .crate_graph - .iter_keys() - .filter(|crate_id| { - // We don't want to attach this crate to itself or stdlib, nor re-attach it to the root crate - crate_id != &library_crate_id - && crate_id != root_crate_id - && crate_id != stdlib_crate_id - }) - .collect(); +fn process_dependency_graph(context: &mut Context, dependency_graph: DependencyGraph) { + let mut crate_names: HashMap<&CrateName, CrateId> = HashMap::new(); - for crate_id in other_crate_ids { - context - .crate_graph - .add_dep(crate_id, library_name.parse().unwrap(), library_crate_id) - .unwrap_or_else(|_| panic!("ICE: Cyclic error triggered by {library_name} library")); + for lib in &dependency_graph.root_dependencies { + let crate_id = add_noir_lib(context, lib); + crate_names.insert(lib, crate_id); + + add_dep(context, *context.root_crate_id(), crate_id, lib.clone()); + } + + for (lib_name, dependencies) in &dependency_graph.library_dependencies { + // first create the library crate if needed + // this crate might not have been registered yet because of the order of the HashMap + // e.g. {root: [lib1], libs: { lib2 -> [lib3], lib1 -> [lib2] }} + let crate_id = + *crate_names.entry(lib_name).or_insert_with(|| add_noir_lib(context, lib_name)); + + for dependency_name in dependencies { + let dep_crate_id: &CrateId = crate_names + .entry(dependency_name) + .or_insert_with(|| add_noir_lib(context, dependency_name)); + + add_dep(context, crate_id, *dep_crate_id, dependency_name.clone()); + } } } +fn add_noir_lib(context: &mut Context, library_name: &CrateName) -> CrateId { + let path_to_lib = Path::new(&library_name.to_string()).join("lib.nr"); + prepare_dependency(context, &path_to_lib) +} + fn preprocess_program(program: CompiledProgram) -> PreprocessedProgram { PreprocessedProgram { hash: program.hash, @@ -168,3 +200,101 @@ cfg_if::cfg_if! { } } } + +#[cfg(test)] +mod test { + use fm::FileManager; + use noirc_driver::prepare_crate; + use noirc_frontend::{ + graph::{CrateGraph, CrateName}, + hir::Context, + }; + + use super::{process_dependency_graph, DependencyGraph}; + use std::{collections::HashMap, path::Path}; + + fn mock_get_non_stdlib_asset(_path_to_file: &Path) -> std::io::Result { + Ok("".to_string()) + } + + fn setup_test_context() -> Context { + let fm = FileManager::new(Path::new("/"), Box::new(mock_get_non_stdlib_asset)); + let graph = CrateGraph::default(); + let mut context = Context::new(fm, graph); + + prepare_crate(&mut context, Path::new("/main.nr")); + + context + } + + fn crate_name(name: &str) -> CrateName { + name.parse().unwrap() + } + + #[test] + fn test_works_with_empty_dependency_graph() { + let mut context = setup_test_context(); + let dependency_graph = + DependencyGraph { root_dependencies: vec![], library_dependencies: HashMap::new() }; + + process_dependency_graph(&mut context, dependency_graph); + + // one stdlib + one root crate + assert_eq!(context.crate_graph.number_of_crates(), 2); + } + + #[test] + fn test_works_with_root_dependencies() { + let mut context = setup_test_context(); + let dependency_graph = DependencyGraph { + root_dependencies: vec![crate_name("lib1")], + library_dependencies: HashMap::new(), + }; + + process_dependency_graph(&mut context, dependency_graph); + + assert_eq!(context.crate_graph.number_of_crates(), 3); + } + + #[test] + fn test_works_with_duplicate_root_dependencies() { + let mut context = setup_test_context(); + let dependency_graph = DependencyGraph { + root_dependencies: vec![crate_name("lib1"), crate_name("lib1")], + library_dependencies: HashMap::new(), + }; + + process_dependency_graph(&mut context, dependency_graph); + + assert_eq!(context.crate_graph.number_of_crates(), 3); + } + + #[test] + fn test_works_with_transitive_dependencies() { + let mut context = setup_test_context(); + let dependency_graph = DependencyGraph { + root_dependencies: vec![crate_name("lib1")], + library_dependencies: HashMap::from([ + (crate_name("lib1"), vec![crate_name("lib2")]), + (crate_name("lib2"), vec![crate_name("lib3")]), + ]), + }; + + process_dependency_graph(&mut context, dependency_graph); + + assert_eq!(context.crate_graph.number_of_crates(), 5); + } + + #[test] + fn test_works_with_missing_dependencies() { + let mut context = setup_test_context(); + let dependency_graph = DependencyGraph { + root_dependencies: vec![crate_name("lib1")], + library_dependencies: HashMap::from([(crate_name("lib2"), vec![crate_name("lib3")])]), + }; + + process_dependency_graph(&mut context, dependency_graph); + + assert_eq!(context.crate_graph.number_of_crates(), 5); + } +} diff --git a/compiler/wasm/src/errors.rs b/compiler/wasm/src/errors.rs index 7090bf6f19f..9aafcadc27f 100644 --- a/compiler/wasm/src/errors.rs +++ b/compiler/wasm/src/errors.rs @@ -1,5 +1,6 @@ use gloo_utils::format::JsValueSerdeExt; -use serde::{Deserialize, Serialize}; +use js_sys::JsString; +use serde::Serialize; use wasm_bindgen::prelude::*; use fm::FileManager; @@ -9,7 +10,7 @@ use noirc_errors::FileDiagnostic; const DIAGNOSTICS: &'static str = r#" export type Diagnostic = { message: string; - file_path: string; + file: string; secondaries: ReadonlyArray<{ message: string; start: number; @@ -17,66 +18,116 @@ export type Diagnostic = { }>; } -interface CompileError { +export interface CompileError extends Error { + message: string; diagnostics: ReadonlyArray; } "#; -#[derive(Serialize, Deserialize)] -struct JsDiagnosticLabel { +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(extends = js_sys::Error, js_name = "CompileError", typescript_type = "CompileError")] + #[derive(Clone, Debug, PartialEq, Eq)] + pub type JsCompileError; + + #[wasm_bindgen(constructor, js_class = "Error")] + fn constructor(message: JsString) -> JsCompileError; +} + +impl JsCompileError { + const DIAGNOSTICS_PROP: &'static str = "diagnostics"; + const NAME_PROP: &'static str = "name"; + const ERROR_NAME: &'static str = "CompileError"; + + pub fn new(message: String, diagnostics: Vec) -> Self { + let err = JsCompileError::constructor(JsString::from(message)); + + js_sys::Reflect::set( + &err, + &JsString::from(JsCompileError::NAME_PROP), + &JsString::from(JsCompileError::ERROR_NAME), + ) + .unwrap(); + + js_sys::Reflect::set( + &err, + &JsString::from(JsCompileError::DIAGNOSTICS_PROP), + &::from_serde(&diagnostics).unwrap(), + ) + .unwrap(); + + err + } +} + +impl From for JsCompileError { + fn from(value: String) -> Self { + JsCompileError::new(value, vec![]) + } +} + +impl From for JsCompileError { + fn from(value: CompileError) -> Self { + JsCompileError::new(value.message, value.diagnostics) + } +} + +#[derive(Serialize)] +struct DiagnosticLabel { message: String, start: u32, end: u32, } -#[derive(Serialize, Deserialize)] -struct JsDiagnostic { +#[derive(Serialize)] +pub struct Diagnostic { message: String, - file_path: String, - secondaries: Vec, + file: String, + secondaries: Vec, } -impl JsDiagnostic { - fn new(file_diagnostic: &FileDiagnostic, file_path: String) -> JsDiagnostic { +impl Diagnostic { + fn new(file_diagnostic: &FileDiagnostic, file: String) -> Diagnostic { let diagnostic = &file_diagnostic.diagnostic; let message = diagnostic.message.clone(); let secondaries = diagnostic .secondaries .iter() - .map(|label| JsDiagnosticLabel { + .map(|label| DiagnosticLabel { message: label.message.clone(), start: label.span.start(), end: label.span.end(), }) .collect(); - JsDiagnostic { message, file_path, secondaries } + Diagnostic { message, file, secondaries } } } -#[wasm_bindgen(getter_with_clone, js_name = "CompileError")] -pub struct JsCompileError { - pub message: js_sys::JsString, - pub diagnostics: JsValue, +#[derive(Serialize)] +pub struct CompileError { + pub message: String, + pub diagnostics: Vec, } -impl JsCompileError { - pub fn new( +impl CompileError { + pub fn new(message: &str) -> CompileError { + CompileError { message: message.to_string(), diagnostics: vec![] } + } + + pub fn with_file_diagnostics( message: &str, file_diagnostics: Vec, file_manager: &FileManager, - ) -> JsCompileError { + ) -> CompileError { let diagnostics: Vec<_> = file_diagnostics .iter() .map(|err| { - JsDiagnostic::new(err, file_manager.path(err.file_id).to_str().unwrap().to_string()) + Diagnostic::new(err, file_manager.path(err.file_id).to_str().unwrap().to_string()) }) .collect(); - JsCompileError { - message: js_sys::JsString::from(message.to_string()), - diagnostics: ::from_serde(&diagnostics).unwrap(), - } + CompileError { message: message.to_string(), diagnostics } } } diff --git a/compiler/wasm/test/browser/index.test.ts b/compiler/wasm/test/browser/index.test.ts index 02263d9adfb..cad2ada0c61 100644 --- a/compiler/wasm/test/browser/index.test.ts +++ b/compiler/wasm/test/browser/index.test.ts @@ -1,68 +1,98 @@ import { expect } from '@esm-bundle/chai'; import initNoirWasm, { compile } from '@noir-lang/noir_wasm'; import { initializeResolver } from '@noir-lang/source-resolver'; -import { nargoArtifactPath, noirSourcePath } from '../shared'; +import { + depsScriptExpectedArtifact, + depsScriptSourcePath, + libASourcePath, + libBSourcePath, + simpleScriptExpectedArtifact, + simpleScriptSourcePath, +} from '../shared'; beforeEach(async () => { await initNoirWasm(); }); async function getFileContent(path: string): Promise { - const mainnrSourceURL = new URL(path, import.meta.url); - const response = await fetch(mainnrSourceURL); + const url = new URL(path, import.meta.url); + const response = await fetch(url); return await response.text(); } -async function getSource(): Promise { - return getFileContent(noirSourcePath); -} - // eslint-disable-next-line @typescript-eslint/no-explicit-any -async function getPrecompiledSource(): Promise { - const compiledData = await getFileContent(nargoArtifactPath); +async function getPrecompiledSource(path: string): Promise { + const compiledData = await getFileContent(path); return JSON.parse(compiledData); } -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export async function compileNoirSource(noir_source: string): Promise { - console.log('Compiling Noir source...'); +describe('noir wasm', () => { + describe('can compile script without dependencies', () => { + beforeEach(async () => { + const source = await getFileContent(simpleScriptSourcePath); + initializeResolver((id: string) => { + console.log(`Resolving source ${id}`); - initializeResolver((id: string) => { - console.log(`Resolving source ${id}`); + if (typeof source === 'undefined') { + throw Error(`Could not resolve source for '${id}'`); + } else if (id !== '/main.nr') { + throw Error(`Unexpected id: '${id}'`); + } else { + return source; + } + }); + }); - const source = noir_source; + it('matching nargos compilation', async () => { + const wasmCircuit = await compile('/main.nr'); + const cliCircuit = await getPrecompiledSource(simpleScriptExpectedArtifact); - if (typeof source === 'undefined') { - throw Error(`Could not resolve source for '${id}'`); - } else if (id !== '/main.nr') { - throw Error(`Unexpected id: '${id}'`); - } else { - return source; - } + // We don't expect the hashes to match due to how `noir_wasm` handles dependencies + expect(wasmCircuit.bytecode).to.eq(cliCircuit.bytecode); + expect(wasmCircuit.abi).to.deep.eq(cliCircuit.abi); + expect(wasmCircuit.backend).to.eq(cliCircuit.backend); + }).timeout(20e3); // 20 seconds }); - try { - const compiled_noir = compile('main.nr'); + describe('can compile script with dependencies', () => { + beforeEach(async () => { + const [scriptSource, libASource, libBSource] = await Promise.all([ + getFileContent(depsScriptSourcePath), + getFileContent(libASourcePath), + getFileContent(libBSourcePath), + ]); - console.log('Noir source compilation done.'); + initializeResolver((file: string) => { + switch (file) { + case '/script/main.nr': + return scriptSource; - return compiled_noir; - } catch (e) { - console.log('Error while compiling:', e); - } -} + case '/lib_a/lib.nr': + return libASource; + + case '/lib_b/lib.nr': + return libBSource; -describe('noir wasm compilation', () => { - it('matches nargos compilation', async () => { - const source = await getSource(); + default: + return ''; + } + }); + }); - const wasmCircuit = await compileNoirSource(source); + it('matching nargos compilation', async () => { + const wasmCircuit = await compile('/script/main.nr', false, { + root_dependencies: ['lib_a'], + library_dependencies: { + lib_a: ['lib_b'], + }, + }); - const cliCircuit = await getPrecompiledSource(); + const cliCircuit = await getPrecompiledSource(depsScriptExpectedArtifact); - // We don't expect the hashes to match due to how `noir_wasm` handles dependencies - expect(wasmCircuit.bytecode).to.eq(cliCircuit.bytecode); - expect(wasmCircuit.abi).to.deep.eq(cliCircuit.abi); - expect(wasmCircuit.backend).to.eq(cliCircuit.backend); - }).timeout(20e3); // 20 seconds + // We don't expect the hashes to match due to how `noir_wasm` handles dependencies + expect(wasmCircuit.bytecode).to.eq(cliCircuit.bytecode); + expect(wasmCircuit.abi).to.deep.eq(cliCircuit.abi); + expect(wasmCircuit.backend).to.eq(cliCircuit.backend); + }).timeout(20e3); // 20 seconds + }); }); diff --git a/compiler/wasm/test/node/index.test.ts b/compiler/wasm/test/node/index.test.ts index 4ec6d83c3c3..2b430abdca4 100644 --- a/compiler/wasm/test/node/index.test.ts +++ b/compiler/wasm/test/node/index.test.ts @@ -1,26 +1,70 @@ import { expect } from 'chai'; -import { nargoArtifactPath, noirSourcePath } from '../shared'; +import { + depsScriptSourcePath, + depsScriptExpectedArtifact, + libASourcePath, + libBSourcePath, + simpleScriptSourcePath, + simpleScriptExpectedArtifact, +} from '../shared'; import { readFileSync } from 'node:fs'; -import { join } from 'node:path'; +import { join, resolve } from 'node:path'; import { compile } from '@noir-lang/noir_wasm'; - -const absoluteNoirSourcePath = join(__dirname, noirSourcePath); -const absoluteNargoArtifactPath = join(__dirname, nargoArtifactPath); +import { initializeResolver } from '@noir-lang/source-resolver'; // eslint-disable-next-line @typescript-eslint/no-explicit-any -async function getPrecompiledSource(): Promise { - const compiledData = readFileSync(absoluteNargoArtifactPath).toString(); +async function getPrecompiledSource(path: string): Promise { + const compiledData = readFileSync(resolve(__dirname, path)).toString(); return JSON.parse(compiledData); } describe('noir wasm compilation', () => { - it('matches nargos compilation', async () => { - const wasmCircuit = await compile(absoluteNoirSourcePath); - const cliCircuit = await getPrecompiledSource(); - - // We don't expect the hashes to match due to how `noir_wasm` handles dependencies - expect(wasmCircuit.bytecode).to.eq(cliCircuit.bytecode); - expect(wasmCircuit.abi).to.deep.eq(cliCircuit.abi); - expect(wasmCircuit.backend).to.eq(cliCircuit.backend); - }).timeout(10e3); + describe('can compile simple scripts', () => { + it('matching nargos compilation', async () => { + const wasmCircuit = await compile(join(__dirname, simpleScriptSourcePath)); + const cliCircuit = await getPrecompiledSource(simpleScriptExpectedArtifact); + + // We don't expect the hashes to match due to how `noir_wasm` handles dependencies + expect(wasmCircuit.bytecode).to.eq(cliCircuit.bytecode); + expect(wasmCircuit.abi).to.deep.eq(cliCircuit.abi); + expect(wasmCircuit.backend).to.eq(cliCircuit.backend); + }).timeout(10e3); + }); + + describe('can compile scripts with dependencies', () => { + beforeEach(() => { + // this test requires a custom resolver in order to correctly resolve dependencies + initializeResolver((file) => { + switch (file) { + case '/script/main.nr': + return readFileSync(join(__dirname, depsScriptSourcePath), 'utf-8'); + + case '/lib_a/lib.nr': + return readFileSync(join(__dirname, libASourcePath), 'utf-8'); + + case '/lib_b/lib.nr': + return readFileSync(join(__dirname, libBSourcePath), 'utf-8'); + + default: + return ''; + } + }); + }); + + it('matching nargos compilation', async () => { + const wasmCircuit = await compile('/script/main.nr', false, { + root_dependencies: ['lib_a'], + library_dependencies: { + lib_a: ['lib_b'], + }, + }); + + const cliCircuit = await getPrecompiledSource(depsScriptExpectedArtifact); + + // We don't expect the hashes to match due to how `noir_wasm` handles dependencies + expect(wasmCircuit.bytecode).to.eq(cliCircuit.bytecode); + expect(wasmCircuit.abi).to.deep.eq(cliCircuit.abi); + expect(wasmCircuit.backend).to.eq(cliCircuit.backend); + }).timeout(10e3); + }); }); diff --git a/compiler/wasm/test/shared.ts b/compiler/wasm/test/shared.ts index f726316cd74..6fc370f7ac8 100644 --- a/compiler/wasm/test/shared.ts +++ b/compiler/wasm/test/shared.ts @@ -1,2 +1,8 @@ -export const noirSourcePath = '../../noir-script/src/main.nr'; -export const nargoArtifactPath = '../../noir-script/target/noir_wasm_testing.json'; +export const simpleScriptSourcePath = '../../fixtures/simple/noir-script/src/main.nr'; +export const simpleScriptExpectedArtifact = '../../fixtures/simple/noir-script/target/noir_wasm_testing.json'; + +export const depsScriptSourcePath = '../../fixtures/deps/noir-script/src/main.nr'; +export const depsScriptExpectedArtifact = '../../fixtures/deps/noir-script/target/noir_wasm_testing.json'; + +export const libASourcePath = '../../fixtures/deps/lib-a/src/lib.nr'; +export const libBSourcePath = '../../fixtures/deps/lib-b/src/lib.nr'; diff --git a/docs/docs/language_concepts/01_functions.md b/docs/docs/language_concepts/01_functions.md index 2168c9203b6..47cdea0cf04 100644 --- a/docs/docs/language_concepts/01_functions.md +++ b/docs/docs/language_concepts/01_functions.md @@ -20,6 +20,12 @@ By default, functions are visible only within the package they are defined. To m pub fn foo() {} ``` +You can also restrict the visibility of the function to only the crate it was defined in, by specifying `pub(crate)`: + +```rust +pub(crate) fn foo() {} //foo can only be called within its crate +``` + All parameters in a function must have a type and all types are known at compile time. The parameter is pre-pended with a colon and the parameter type. Multiple parameters are separated using a comma. @@ -132,6 +138,36 @@ follows: assert(MyStruct::sum(s) == 42); ``` +It is also possible to specialize which method is chosen depending on the [generic](./06_generics.md) type that is used. In this example, the `foo` function returns different values depending on its type: + +```rust +struct Foo {} + +impl Foo { + fn foo(self) -> Field { 1 } +} + +impl Foo { + fn foo(self) -> Field { 2 } +} + +fn main() { + let f1: Foo = Foo{}; + let f2: Foo = Foo{}; + assert(f1.foo() + f2.foo() == 3); +} +``` + +Also note that impls with the same method name defined in them cannot overlap. For example, if we already have `foo` defined for `Foo` and `Foo` like we do above, we cannot also define `foo` in an `impl Foo` since it would be ambiguous which version of `foo` to choose. + +```rust +// Including this impl in the same project as the above snippet would +// cause an overlapping impls error +impl Foo { + fn foo(self) -> Field { 3 } +} +``` + ## Lambdas Lambdas are anonymous functions. They follow the syntax of Rust - `|arg1, arg2, ..., argN| return_expression`. @@ -148,19 +184,21 @@ See [Lambdas](./08_lambdas.md) for more details. Attributes are metadata that can be applied to a function, using the following syntax: `#[attribute(value)]`. Supported attributes include: + - **builtin**: the function is implemented by the compiler, for efficiency purposes. -- **deprecated**: mark the function as *deprecated*. Calling the function will generate a warning: `warning: use of deprecated function` +- **deprecated**: mark the function as _deprecated_. Calling the function will generate a warning: `warning: use of deprecated function` - **field**: Used to enable conditional compilation of code depending on the field size. See below for more details -- **oracle**: mark the function as *oracle*; meaning it is an external unconstrained function, implemented in noir_js. See [Unconstrained](./05_unconstrained.md) and [Noir js](../noir_js/noir_js.md) for more details. +- **oracle**: mark the function as _oracle_; meaning it is an external unconstrained function, implemented in noir_js. See [Unconstrained](./05_unconstrained.md) and [NoirJS](../noir_js/noir_js.md) for more details. - **test**: mark the function as unit tests. See [Tests](../nargo/02_testing.md) for more details ### Field Attribute + The field attribute defines which field the function is compatible for. The function is conditionally compiled, under the condition that the field attribute matches the Noir native field. The field can be defined implicitly, by using the name of the elliptic curve usually associated to it - for instance bn254, bls12_381 - or explicitly by using the field (prime) order, in decimal or hexadecimal form. As a result, it is possible to define multiple versions of a function with each version specialized for a different field attribute. This can be useful when a function requires different parameters depending on the underlying elliptic curve. - Example: we define the function `foo()` three times below. Once for the default Noir bn254 curve, once for the field $\mathbb F_{23}$, which will normally never be used by Noir, and once again for the bls12_381 curve. + ```rust #[field(bn254)] fn foo() -> u32 { @@ -184,4 +222,4 @@ fn foo() -> u32 { } ``` -If the field name is not known to Noir, it will discard the function. Field names are case insensitive. \ No newline at end of file +If the field name is not known to Noir, it will discard the function. Field names are case insensitive. diff --git a/docs/docs/language_concepts/data_types/01_integers.md b/docs/docs/language_concepts/data_types/01_integers.md index d9c5e20e795..3a115f2fc23 100644 --- a/docs/docs/language_concepts/data_types/01_integers.md +++ b/docs/docs/language_concepts/data_types/01_integers.md @@ -15,9 +15,16 @@ keywords: An integer type is a range constrained field type. The Noir frontend currently supports unsigned, arbitrary-sized integer types. +> **Note:** When an integer is defined in Noir without a specific type, it will default to `Field`. The one exception is for loop indices which default to `u64` since comparisons on `Field`s are not possible. + An integer type is specified first with the letter `u`, indicating its unsigned nature, followed by its length in bits (e.g. `32`). For example, a `u32` variable can store a value in the range of -$\\([0,2^{32}-1]\\)$: +$\\([0,2^{32}-1]\\)$. + +> **Note:** The default proving backend supports both even (e.g. `u16`, `u48`) and odd (e.g. `u5`, `u3`) +> sized integer types. + +Taking a look of how the type is used: ```rust fn main(x : Field, y : u32) { @@ -28,6 +35,3 @@ fn main(x : Field, y : u32) { `x`, `y` and `z` are all private values in this example. However, `x` is a field while `y` and `z` are unsigned 32-bit integers. If `y` or `z` exceeds the range $\\([0,2^{32}-1]\\)$, proofs created will be rejected by the verifier. - -> **Note:** The default backend supports both even (e.g. `u16`, `u48`) and odd (e.g. `u5`, `u3`) -> sized integer types. diff --git a/docs/docs/language_concepts/data_types/05_slices.mdx b/docs/docs/language_concepts/data_types/05_slices.mdx index 7fd07225a4e..1be0ec4a137 100644 --- a/docs/docs/language_concepts/data_types/05_slices.mdx +++ b/docs/docs/language_concepts/data_types/05_slices.mdx @@ -4,7 +4,7 @@ description: Explore the Slice data type in Noir. Understand its methods, see re keywords: [noir, slice type, methods, examples, subarrays] --- -import Experimental from '../../../src/components/Notes/_experimental.mdx'; +import Experimental from '@site/src/components/Notes/_experimental.mdx'; @@ -21,7 +21,7 @@ fn main() -> pub Field { } ``` -View the corresponding test file [here]([test-file]. +View the corresponding test file [here][test-file]. [test-file]: https://github.com/noir-lang/noir/blob/f387ec1475129732f72ba294877efdf6857135ac/crates/nargo_cli/tests/test_data_ssa_refactor/slices/src/main.nr diff --git a/docs/docs/language_concepts/data_types/06_vectors.mdx b/docs/docs/language_concepts/data_types/06_vectors.mdx index 4d3406f20c0..4617e90d038 100644 --- a/docs/docs/language_concepts/data_types/06_vectors.mdx +++ b/docs/docs/language_concepts/data_types/06_vectors.mdx @@ -4,7 +4,7 @@ description: Delve into the Vector data type in Noir. Learn about its methods, p keywords: [noir, vector type, methods, examples, dynamic arrays] --- -import Experimental from '../../../src/components/Notes/_experimental.mdx'; +import Experimental from '@site/src/components/Notes/_experimental.mdx'; diff --git a/docs/docs/nargo/01_commands.md b/docs/docs/nargo/01_commands.md index fbbe2b27666..65e2bdb44e3 100644 --- a/docs/docs/nargo/01_commands.md +++ b/docs/docs/nargo/01_commands.md @@ -20,12 +20,12 @@ keywords: ## General options -| Option | Description | -|---------------------|------------------------------------------------------| -| `--show-ssa` | Emit debug information for the intermediate SSA IR | -| `--deny-warnings` | Quit execution when warnings are emitted | -| `--silence-warnings`| Suppress warnings | -| `-h, --help` | Print help | +| Option | Description | +| -------------------- | -------------------------------------------------- | +| `--show-ssa` | Emit debug information for the intermediate SSA IR | +| `--deny-warnings` | Quit execution when warnings are emitted | +| `--silence-warnings` | Suppress warnings | +| `-h, --help` | Print help | ## `nargo help [subcommand]` @@ -33,9 +33,9 @@ Prints the list of available commands or specific information of a subcommand. _Arguments_ -| Argument | Description | -|---------------|--------------------------------------------------------| -| ``| The subcommand whose help message to display | +| Argument | Description | +| -------------- | -------------------------------------------- | +| `` | The subcommand whose help message to display | ## `nargo backend` @@ -43,20 +43,20 @@ Installs and selects custom backends used to generate and verify proofs. ### Commands -| Command | Description | -|------------|--------------------------------------------------| -| `current` | Prints the name of the currently active backend | -| `ls` | Prints the list of currently installed backends | -| `use` | Select the backend to use | -| `install` | Install a new backend from a URL | -| `uninstall`| Uninstalls a backend | -| `help` | Print this message or the help of the given subcommand(s) | +| Command | Description | +| ----------- | --------------------------------------------------------- | +| `current` | Prints the name of the currently active backend | +| `ls` | Prints the list of currently installed backends | +| `use` | Select the backend to use | +| `install` | Install a new backend from a URL | +| `uninstall` | Uninstalls a backend | +| `help` | Print this message or the help of the given subcommand(s) | ### Options -| Option | Description | -|---------------|--------------| -| `-h, --help` | Print help | +| Option | Description | +| ------------ | ----------- | +| `-h, --help` | Print help | ## `nargo check` @@ -65,14 +65,14 @@ values of the Noir program respectively. ### Options -| Option | Description | -|----------------------|---------------------------------------------------| -| `--package `| The name of the package to check | -| `--workspace` | Check all packages in the workspace | -| `--print-acir` | Display the ACIR for compiled circuit | -| `--deny-warnings` | Treat all warnings as errors | -| `--silence-warnings` | Suppress warnings | -| `-h, --help` | Print help | +| Option | Description | +| --------------------- | ------------------------------------- | +| `--package ` | The name of the package to check | +| `--workspace` | Check all packages in the workspace | +| `--print-acir` | Display the ACIR for compiled circuit | +| `--deny-warnings` | Treat all warnings as errors | +| `--silence-warnings` | Suppress warnings | +| `-h, --help` | Print help | ### `nargo codegen-verifier` @@ -80,14 +80,14 @@ Generate a Solidity verifier smart contract for the program. ### Options -| Option | Description | -|----------------------|---------------------------------------------------| -| `--package `| The name of the package to codegen | -| `--workspace` | Codegen all packages in the workspace | -| `--print-acir` | Display the ACIR for compiled circuit | -| `--deny-warnings` | Treat all warnings as errors | -| `--silence-warnings` | Suppress warnings | -| `-h, --help` | Print help | +| Option | Description | +| --------------------- | ------------------------------------- | +| `--package ` | The name of the package to codegen | +| `--workspace` | Codegen all packages in the workspace | +| `--print-acir` | Display the ACIR for compiled circuit | +| `--deny-warnings` | Treat all warnings as errors | +| `--silence-warnings` | Suppress warnings | +| `-h, --help` | Print help | ## `nargo compile` @@ -98,15 +98,15 @@ You can also use "build" as an alias for compile (e.g. `nargo build`). ### Options -| Option | Description | -|----------------------|----------------------------------------------------| -| `--include-keys` | Include Proving and Verification keys in the build artifacts | -| `--package `| The name of the package to compile | -| `--workspace` | Compile all packages in the workspace | -| `--print-acir` | Display the ACIR for compiled circuit | -| `--deny-warnings` | Treat all warnings as errors | -| `--silence-warnings` | Suppress warnings | -| `-h, --help` | Print help | +| Option | Description | +| --------------------- | ------------------------------------------------------------ | +| `--include-keys` | Include Proving and Verification keys in the build artifacts | +| `--package ` | The name of the package to compile | +| `--workspace` | Compile all packages in the workspace | +| `--print-acir` | Display the ACIR for compiled circuit | +| `--deny-warnings` | Treat all warnings as errors | +| `--silence-warnings` | Suppress warnings | +| `-h, --help` | Print help | ## `nargo new ` @@ -114,19 +114,19 @@ Creates a new Noir project in a new folder. **Arguments** -| Argument | Description | -|-----------|------------------------------------| -| `` | The path to save the new project | +| Argument | Description | +| -------- | -------------------------------- | +| `` | The path to save the new project | ### Options -| Option | Description | -|-------------------|-------------------------------------------------| -| `--name ` | Name of the package [default: package directory name] | -| `--lib` | Use a library template | -| `--bin` | Use a binary template [default] | -| `--contract` | Use a contract template | -| `-h, --help` | Print help | +| Option | Description | +| --------------- | ----------------------------------------------------- | +| `--name ` | Name of the package [default: package directory name] | +| `--lib` | Use a library template | +| `--bin` | Use a binary template [default] | +| `--contract` | Use a contract template | +| `-h, --help` | Print help | ## `nargo init` @@ -134,13 +134,13 @@ Creates a new Noir project in the current directory. ### Options -| Option | Description | -|-------------------|-------------------------------------------------| -| `--name ` | Name of the package [default: current directory name] | -| `--lib` | Use a library template | -| `--bin` | Use a binary template [default] | -| `--contract` | Use a contract template | -| `-h, --help` | Print help | +| Option | Description | +| --------------- | ----------------------------------------------------- | +| `--name ` | Name of the package [default: current directory name] | +| `--lib` | Use a library template | +| `--bin` | Use a binary template [default] | +| `--contract` | Use a contract template | +| `-h, --help` | Print help | ## `nargo execute [WITNESS_NAME]` @@ -148,21 +148,21 @@ Runs the Noir program and prints its return value. **Arguments** -| Argument | Description | -|-----------------|------------------------------------------------| -| `[WITNESS_NAME]`| Write the execution witness to named file | +| Argument | Description | +| ---------------- | ----------------------------------------- | +| `[WITNESS_NAME]` | Write the execution witness to named file | ### Options -| Option | Description | -|-------------------------------|------------------------------------------------------------------| +| Option | Description | +| --------------------------------- | ------------------------------------------------------------------------------------ | | `-p, --prover-name ` | The name of the toml file which contains the inputs for the prover [default: Prover] | -| `--package ` | The name of the package to execute | -| `--workspace` | Execute all packages in the workspace | -| `--print-acir` | Display the ACIR for compiled circuit | -| `--deny-warnings` | Treat all warnings as errors | -| `--silence-warnings` | Suppress warnings | -| `-h, --help` | Print help | +| `--package ` | The name of the package to execute | +| `--workspace` | Execute all packages in the workspace | +| `--print-acir` | Display the ACIR for compiled circuit | +| `--deny-warnings` | Treat all warnings as errors | +| `--silence-warnings` | Suppress warnings | +| `-h, --help` | Print help | _Usage_ @@ -178,17 +178,17 @@ Creates a proof for the program. ### Options -| Option | Description | -|-------------------------------|------------------------------------------------------------------| -| `-p, --prover-name ` | The name of the toml file which contains the inputs for the prover [default: Prover] | +| Option | Description | +| ------------------------------------- | ---------------------------------------------------------------------------------------- | +| `-p, --prover-name ` | The name of the toml file which contains the inputs for the prover [default: Prover] | | `-v, --verifier-name ` | The name of the toml file which contains the inputs for the verifier [default: Verifier] | -| `--verify` | Verify proof after proving | -| `--package ` | The name of the package to prove | -| `--workspace` | Prove all packages in the workspace | -| `--print-acir` | Display the ACIR for compiled circuit | -| `--deny-warnings` | Treat all warnings as errors | -| `--silence-warnings` | Suppress warnings | -| `-h, --help` | Print help | +| `--verify` | Verify proof after proving | +| `--package ` | The name of the package to prove | +| `--workspace` | Prove all packages in the workspace | +| `--print-acir` | Display the ACIR for compiled circuit | +| `--deny-warnings` | Treat all warnings as errors | +| `--silence-warnings` | Suppress warnings | +| `-h, --help` | Print help | ## `nargo verify` @@ -196,15 +196,15 @@ Given a proof and a program, verify whether the proof is valid. ### Options -| Option | Description | -|-------------------------------|------------------------------------------------------------------| +| Option | Description | +| ------------------------------------- | ---------------------------------------------------------------------------------------- | | `-v, --verifier-name ` | The name of the toml file which contains the inputs for the verifier [default: Verifier] | -| `--package ` | The name of the package to verify | -| `--workspace` | Verify all packages in the workspace | -| `--print-acir` | Display the ACIR for compiled circuit | -| `--deny-warnings` | Treat all warnings as errors | -| `--silence-warnings` | Suppress warnings | -| `-h, --help` | Print help | +| `--package ` | The name of the package to verify | +| `--workspace` | Verify all packages in the workspace | +| `--print-acir` | Display the ACIR for compiled circuit | +| `--deny-warnings` | Treat all warnings as errors | +| `--silence-warnings` | Suppress warnings | +| `-h, --help` | Print help | ## `nargo test [TEST_NAME]` @@ -217,16 +217,16 @@ See an example on the [testing page](./testing). ### Options -| Option | Description | -|----------------------|---------------------------------------------------| -| `--show-output` | Display output of `println` statements | -| `--exact` | Only run tests that match exactly | -| `--package `| The name of the package to test | -| `--workspace` | Test all packages in the workspace | -| `--print-acir` | Display the ACIR for compiled circuit | -| `--deny-warnings` | Treat all warnings as errors | -| `--silence-warnings` | Suppress warnings | -| `-h, --help` | Print help | +| Option | Description | +| --------------------- | -------------------------------------- | +| `--show-output` | Display output of `println` statements | +| `--exact` | Only run tests that match exactly | +| `--package ` | The name of the package to test | +| `--workspace` | Test all packages in the workspace | +| `--print-acir` | Display the ACIR for compiled circuit | +| `--deny-warnings` | Treat all warnings as errors | +| `--silence-warnings` | Suppress warnings | +| `-h, --help` | Print help | ## `nargo info` @@ -244,3 +244,7 @@ above information about each function of the contract. Start a long-running Language Server process that communicates over stdin/stdout. Usually this command is not run by a user, but instead will be run by a Language Client, such as [vscode-noir](https://github.com/noir-lang/vscode-noir). + +## `nargo fmt` + +Automatically formats your Noir source code based on the default formatting settings. diff --git a/docs/docs/nargo/04_language_server.md b/docs/docs/nargo/04_language_server.md index 543af9ec5ff..144cd249c4b 100644 --- a/docs/docs/nargo/04_language_server.md +++ b/docs/docs/nargo/04_language_server.md @@ -25,7 +25,7 @@ Currently, Noir provides a Language Client for Visual Studio Code via the [vscod > > If LSP features seem to be missing / malfunctioning, make sure you are opening your Noir project directly (instead of as a sub-folder) in your VSCode instance. -When you language server is running correctly and the VSCode plugin is installed, you should see handy codelens buttons for compilation, execution, and tests: +When your language server is running correctly and the VSCode plugin is installed, you should see handy codelens buttons for compilation, measuring circuit size, execution, and tests: ![Compile and Execute](./../../static/img/codelens_compile_execute.png) ![Run test](../../static/img/codelens_run_test.png) diff --git a/docs/docs/noir_js/getting_started/01_tiny_noir_app.md b/docs/docs/noir_js/getting_started/01_tiny_noir_app.md index 9878554dd60..1b0c8bfc353 100644 --- a/docs/docs/noir_js/getting_started/01_tiny_noir_app.md +++ b/docs/docs/noir_js/getting_started/01_tiny_noir_app.md @@ -1,16 +1,24 @@ --- -title: Full Stack Noir App +title: End-to-end description: Learn how to setup a new app that uses Noir to generate and verify zero-knowledge SNARK proofs in a typescript or javascript environment keywords: [how to, guide, javascript, typescript, noir, barretenberg, zero-knowledge, proofs] --- -Noir JS works both on the browser and on the server, and works for both ESM and CJS module systems. In this page, we will learn how can we write a simple test and a simple web app to verify the standard Noir example. +NoirJS works both on the browser and on the server, and works for both ESM and CJS module systems. In this page, we will learn how can we write a simple test and a simple web app to verify the standard Noir example. ## Before we start +:::note + +Feel free to use whatever versions, just keep in mind that Nargo and the NoirJS packages are meant to be in sync. For example, Nargo 0.18.x matches `noir_js@0.18.x`, etc. + +In this guide, we will be pinned to 0.17.0. + +::: + Make sure you have Node installed on your machine by opening a terminal and executing `node --version`. If you don't see a version, you should install [node](https://github.com/nvm-sh/nvm). You can also use `yarn` if you prefer that package manager over npm (which comes with node). -First of all, follow the the [Nargo guide](../../getting_started/00_nargo_installation.md) to install nargo and create a new project with `nargo new circuit`. Once there, `cd` into the `circuit` folder. You should then be able to compile your circuit into `json` format and see it inside the `target` folder: +First of all, follow the the [Nargo guide](../../getting_started/00_nargo_installation.md) to install nargo version 0.17.0 and create a new project with `nargo new circuit`. Once there, `cd` into the `circuit` folder. You should then be able to compile your circuit into `json` format and see it inside the `target` folder: ```bash nargo compile @@ -34,10 +42,10 @@ Go back to the previous folder and start a new project by running run `npm init` ## Installing dependencies -We'll need two `npm` packages. These packages will provide us the methods we need to run and verify proofs. Let's install them: +We'll need two `npm` packages. These packages will provide us the methods we need to run and verify proofs: ```bash -npm i @noir-lang/backend_barretenberg @noir-lang/noir_js +npm i @noir-lang/backend_barretenberg@^0.17.0 @noir-lang/noir_js@^0.17.0 ``` To serve our page, we can use a build tool such as `vite`. Because we're gonna use some `wasm` files, we need to install a plugin as well. Run: @@ -175,7 +183,7 @@ You'll see other files and folders showing up (like `package-lock.json`, `yarn.l ## Importing our dependencies -We're starting with the good stuff now. At the top of a new the typescript file, import the packages: +We're starting with the good stuff now. At the top of the new javascript file, import the packages: ```ts import { BarretenbergBackend } from '@noir-lang/backend_barretenberg'; @@ -209,18 +217,8 @@ Our dependencies exported two classes: `BarretenbergBackend` and `Noir`. Let's ` ```ts const backend = new BarretenbergBackend(circuit); const noir = new Noir(circuit, backend); - -display('logs', 'Init... ⌛'); -await noir.init(); -display('logs', 'Init... ✅'); ``` -You're probably eager to see stuff happening, so go and run your app now! - -From your terminal, run `npm start` (or `yarn start`). If it doesn't open a browser for you, just visit `localhost:5173`. You'll see your app with the two logs: - -![Getting Started 0](./../../../static/img/noir_getting_started_0.png) - ## Proving Now we're ready to prove stuff! Let's feed some inputs to our circuit and calculate the proof: @@ -230,10 +228,12 @@ const input = { x: 1, y: 2 }; display('logs', 'Generating proof... ⌛'); const proof = await noir.generateFinalProof(input); display('logs', 'Generating proof... ✅'); -display('results', proof); +display('results', proof.proof); ``` -Save your doc and vite should refresh your page automatically. On a modern laptop, proof will generate in less than 100ms, and you'll see this: +You're probably eager to see stuff happening, so go and run your app now! + +From your terminal, run `npm start` (or `yarn start`). If it doesn't open a browser for you, just visit `localhost:5173`. On a modern laptop, proof will generate in less than 100ms, and you'll see this: ![Getting Started 0](./../../../static/img/noir_getting_started_1.png) @@ -252,3 +252,5 @@ By saving, your app will refresh and here's our complete Tiny Noir App! ## Further Reading You can see how noirjs is used in a full stack Next.js hardhat application in the [noir-starter repo here](https://github.com/noir-lang/noir-starter/tree/main/next-hardhat). The example shows how to calculate a proof in the browser and verify it with a deployed Solidity verifier contract from noirjs. + +You should also check out the more advanced examples in the [noir-examples repo](https://github.com/noir-lang/noir-examples), where you'll find reference usage for some cool apps. diff --git a/docs/docs/noir_js/noir_js.md b/docs/docs/noir_js/noir_js.md index baaf409b522..23ea550e156 100644 --- a/docs/docs/noir_js/noir_js.md +++ b/docs/docs/noir_js/noir_js.md @@ -1,22 +1,36 @@ --- -title: Noir JS -description: Learn how to use noir js to use Noir in a Typescript or Javascript environment +title: NoirJS +description: Interact with Noir in Typescript or Javascript keywords: [Noir project, javascript, typescript, node.js, browser, react] --- -Noir JS are a set of typescript libraries that make it easy to use Noir on your dapp, webapp, node.js server, website, etc. +NoirJS is a TypeScript library that make it easy to use Noir on your dapp, webapp, Node.js server, website, etc. -It is composed of two major elements: +A typical workflow would be composed of two major elements: -- Noir -- Backend proving system +- NoirJS +- Proving backend of choice's JavaScript package -Your only concern should be to write Noir. Noir.js will work out-of-the box and abstract all the components, such as the ACVM and others. +To install NoirJS, install Node.js if you have not already and run this in your JavaScript project: -## Barretenberg +```bash +npm i @noir-lang/noir_js +``` -Since Noir is backend agnostic, you can instantiate `noir_js` without any backend (i.e. to execute a function). But for proving, you should instantiate it with any of the supported backends through their own `js` interface. +## Proving backend -Aztec Labs maintains the `barretenberg` backend. You can use it to instantiate your `Noir` class. +Since Noir is backend agnostic, you can instantiate NoirJS without any backend (i.e. to execute a function). But for proving, you would have to instantiate NoirJS with any of the supported backends through their own `js` interface. + +### Barretenberg + +Aztec Labs maintains the `barretenberg` proving backend, which you can instantiate and make use of alongside NoirJS. It is also the default proving backend installed and used with Nargo, the Noir CLI tool. + +To install its JavaScript library, run this in your project: + +```bash +npm i @noir-lang/backend_barretenberg +``` + +For more details on how to instantiate and use the libraries, refer to the [Full Noir App Guide](./getting_started/01_tiny_noir_app.md) and [Reference](./reference/01_noirjs.md) sections. diff --git a/docs/docs/noir_js/reference/02_noirjs.md b/docs/docs/noir_js/reference/01_noirjs.md similarity index 100% rename from docs/docs/noir_js/reference/02_noirjs.md rename to docs/docs/noir_js/reference/01_noirjs.md diff --git a/docs/docs/noir_js/reference/01_bb_backend.md b/docs/docs/noir_js/reference/02_bb_backend.md similarity index 99% rename from docs/docs/noir_js/reference/01_bb_backend.md rename to docs/docs/noir_js/reference/02_bb_backend.md index 446bf9820ea..21c2ff32b57 100644 --- a/docs/docs/noir_js/reference/01_bb_backend.md +++ b/docs/docs/noir_js/reference/02_bb_backend.md @@ -41,7 +41,7 @@ constructor(acirCircuit, (numberOfThreads = 1)); | Parameter | Type | Description | | ----------------- | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `acirCircuit` | Object | A circuit represented in a `json` format, containing the ABI and bytecode Tipically obtained by running [`nargo compile`](../../nargo/01_commands.md). This is the same circuit expected to be passed to [the Noir class](02_noirjs.md) | +| `acirCircuit` | Object | A circuit represented in a `json` format, containing the ABI and bytecode Tipically obtained by running [`nargo compile`](../../nargo/01_commands.md). This is the same circuit expected to be passed to [the Noir class](01_noirjs.md) | | `numberOfThreads` | Number (optional) | The number of threads to be used by the backend. Defaults to 1. | ### Usage diff --git a/docs/docs/standard_library/cryptographic_primitives/00_hashes.mdx b/docs/docs/standard_library/cryptographic_primitives/00_hashes.mdx index bb2621b6499..2cc3cd81e4e 100644 --- a/docs/docs/standard_library/cryptographic_primitives/00_hashes.mdx +++ b/docs/docs/standard_library/cryptographic_primitives/00_hashes.mdx @@ -7,7 +7,7 @@ keywords: [cryptographic primitives, Noir project, sha256, blake2s, pedersen, mimc_bn254, mimc, hash] --- -import BlackBoxInfo from '../../../src/components/Notes/_blackbox.mdx'; +import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; ## sha256 diff --git a/docs/docs/standard_library/cryptographic_primitives/01_scalar.mdx b/docs/docs/standard_library/cryptographic_primitives/01_scalar.mdx index 0d7990859b5..c7eed820a80 100644 --- a/docs/docs/standard_library/cryptographic_primitives/01_scalar.mdx +++ b/docs/docs/standard_library/cryptographic_primitives/01_scalar.mdx @@ -4,7 +4,7 @@ description: See how you can perform scalar multiplications over a fixed base in keywords: [cryptographic primitives, Noir project, scalar multiplication] --- -import BlackBoxInfo from '../../../src/components/Notes/_blackbox.mdx'; +import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; ## scalar_mul::fixed_base_embedded_curve diff --git a/docs/docs/standard_library/cryptographic_primitives/02_schnorr.mdx b/docs/docs/standard_library/cryptographic_primitives/02_schnorr.mdx index 73737eb54bb..c184ce28120 100644 --- a/docs/docs/standard_library/cryptographic_primitives/02_schnorr.mdx +++ b/docs/docs/standard_library/cryptographic_primitives/02_schnorr.mdx @@ -4,7 +4,7 @@ description: Learn how you can verify Schnorr signatures using Noir keywords: [cryptographic primitives, Noir project, schnorr, signatures] --- -import BlackBoxInfo from '../../../src/components/Notes/_blackbox.mdx'; +import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; ## schnorr::verify_signature diff --git a/docs/docs/standard_library/cryptographic_primitives/05_eddsa.mdx b/docs/docs/standard_library/cryptographic_primitives/05_eddsa.mdx index 39dfb604669..9a5beb55ee9 100644 --- a/docs/docs/standard_library/cryptographic_primitives/05_eddsa.mdx +++ b/docs/docs/standard_library/cryptographic_primitives/05_eddsa.mdx @@ -4,7 +4,7 @@ description: Learn about the cryptographic primitives regarding EdDSA keywords: [cryptographic primitives, Noir project, eddsa, signatures] --- -import BlackBoxInfo from '../../../src/components/Notes/_blackbox.mdx'; +import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; ## eddsa::eddsa_poseidon_verify diff --git a/docs/sidebars.js b/docs/sidebars.js index 205ecb76038..8fddb677a58 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -98,7 +98,7 @@ const sidebars = { }, { type: 'category', - label: 'Noir JS', + label: 'NoirJS', link: { type: 'doc', id: 'noir_js/noir_js', @@ -106,21 +106,21 @@ const sidebars = { items: [ { type: 'category', - label: 'Reference', + label: 'Guides', items: [ { type: 'autogenerated', - dirName: 'noir_js/reference', + dirName: 'noir_js/getting_started', }, ], }, { type: 'category', - label: 'Guides', + label: 'Reference', items: [ { type: 'autogenerated', - dirName: 'noir_js/getting_started', + dirName: 'noir_js/reference', }, ], }, diff --git a/docs/static/img/codelens_compile_execute.png b/docs/static/img/codelens_compile_execute.png index 347490f529f..040e3af2704 100644 Binary files a/docs/static/img/codelens_compile_execute.png and b/docs/static/img/codelens_compile_execute.png differ diff --git a/docs/static/img/codelens_run_test.png b/docs/static/img/codelens_run_test.png index 3ff94df0903..568869fb839 100644 Binary files a/docs/static/img/codelens_run_test.png and b/docs/static/img/codelens_run_test.png differ diff --git a/docs/static/img/noir_getting_started_0.png b/docs/static/img/noir_getting_started_0.png deleted file mode 100644 index 9e1f569ceec..00000000000 Binary files a/docs/static/img/noir_getting_started_0.png and /dev/null differ diff --git a/docs/versioned_docs/version-0.17.0/examples/merkle-proof.mdx b/docs/versioned_docs/version-0.17.0/examples/merkle-proof.mdx new file mode 100644 index 00000000000..6430780817c --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/examples/merkle-proof.mdx @@ -0,0 +1,48 @@ +--- +title: Merkle Proof Membership +description: + Learn how to use merkle membership proof in Noir to prove that a given leaf is a member of a + merkle tree with a specified root, at a given index. +keywords: + [merkle proof, merkle membership proof, Noir, rust, hash function, Pedersen, sha256, merkle tree] +--- + +Let's walk through an example of a merkle membership proof in Noir that proves that a given leaf is +in a merkle tree. + +```rust +use dep::std; + +fn main(message : [Field; 62], index : Field, hashpath : [Field; 40], root : Field) { + let leaf = std::hash::hash_to_field(message); + let merkle_root = std::merkle::compute_merkle_root(leaf, index, hashpath); + assert(merkle_root == root); +} + +``` + +The message is hashed using `hash_to_field`. The specific hash function that is being used is chosen +by the backend. The only requirement is that this hash function can heuristically be used as a +random oracle. If only collision resistance is needed, then one can call `std::hash::pedersen` +instead. + +```rust +let leaf = std::hash::hash_to_field(message); +``` + +The leaf is then passed to a compute_merkle_root function with the root, index and hashpath. The returned root can then be asserted to be the same as the provided root. + +```rust +let merkle_root = std::merkle::compute_merkle_root(leaf, index, hashpath); +assert (merkle_root == root); +``` + +> **Note:** It is possible to re-implement the merkle tree implementation without standard library. +> However, for most usecases, it is enough. In general, the standard library will always opt to be +> as conservative as possible, while striking a balance with efficiency. + +An example, the merkle membership proof, only requires a hash function that has collision +resistance, hence a hash function like Pedersen is allowed, which in most cases is more efficient +than the even more conservative sha256. + +[View an example on the starter repo](https://github.com/noir-lang/noir-examples/blob/3ea09545cabfa464124ec2f3ea8e60c608abe6df/stealthdrop/circuits/src/main.nr#L20) diff --git a/docs/versioned_docs/version-0.17.0/getting_started/00_nargo_installation.md b/docs/versioned_docs/version-0.17.0/getting_started/00_nargo_installation.md new file mode 100644 index 00000000000..26a665a0d51 --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/getting_started/00_nargo_installation.md @@ -0,0 +1,256 @@ +--- +title: Nargo Installation +description: + nargo is a command line tool for interacting with Noir programs (e.g. compiling, proving, + verifying and more). Learn how to install and use Nargo for your projects with this comprehensive + guide. +keywords: [Nargo, command line tool, Noir programs, installation guide, how to use Nargo] +--- + +`nargo` is a command line tool for interacting with Noir programs (e.g. compiling, proving, +verifying and more). + +Alternatively, the interactions can also be performed in [NoirJS](../noir_js/noir_js.md). + +### UltraPlonk + +Nargo versions <0.5.0 of `aztec_backend` and `aztec_wasm_backend` are based on the TurboPlonk +version of Aztec Backend, which lacks efficient implementations of useful primitives (e.g. Keccak256 in 18k constraints, ECDSA verification in 36k constraints) that the UltraPlonk version offers. + +## Installation + +There are four approaches for installing Nargo: + +- [Option 1: Noirup](#option-1-noirup) +- [Option 2: Binaries](#option-2-binaries) +- [Option 3: Install via Nix](#option-3-install-via-nix) +- [Option 4: Compile from Source](#option-4-compile-from-source) + +Optionally you can also install [Noir VS Code extension] for syntax highlighting. + +### Option 1: Noirup + +If you're on OSX or Linux, the easiest way to start using Noir and Nargo is via noirup. Just open a +terminal and run: + +```bash +curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash +``` + +Close the terminal, open another one, and run + +```bash +noirup +``` + +Done, you should have the latest version working. You can check with `nargo --version`. + +You can also install nightlies, specific versions +or branches, check out the [noirup repository](https://github.com/noir-lang/noirup) for more +information. + +#### GitHub Actions + +You can use `noirup` with GitHub Actions for CI/CD and automated testing. It is as simple as +installing `noirup` and running tests in your GitHub Action `yml` file. + +See the +[config file](https://github.com/TomAFrench/noir-hashes/blob/master/.github/workflows/noir.yml) in +this repo containing hash functions in Noir for an example. + +#### Nightly versions + +To install the nightly version of Noir (updated daily) run: + +```bash +noirup -n +``` + +### Option 2: Binaries + +See [GitHub Releases](https://github.com/noir-lang/noir/releases) for the latest and previous +platform specific binaries. + +#### Step 1 + +Paste and run the following in the terminal to extract and install the binary: + +> **macOS / Linux:** If you are prompted with `Permission denied` when running commands, prepend +> `sudo` and re-run it. + +##### macOS (Apple Silicon) + +```bash +mkdir -p $HOME/.nargo/bin && \ +curl -o $HOME/.nargo/bin/nargo-aarch64-apple-darwin.tar.gz -L https://github.com/noir-lang/noir/releases/download/v0.6.0/nargo-aarch64-apple-darwin.tar.gz && \ +tar -xvf $HOME/.nargo/bin/nargo-aarch64-apple-darwin.tar.gz -C $HOME/.nargo/bin/ && \ +echo '\nexport PATH=$PATH:$HOME/.nargo/bin' >> ~/.zshrc && \ +source ~/.zshrc +``` + +##### macOS (Intel) + +```bash +mkdir -p $HOME/.nargo/bin && \ +curl -o $HOME/.nargo/bin/nargo-x86_64-apple-darwin.tar.gz -L https://github.com/noir-lang/noir/releases/download/v0.6.0/nargo-x86_64-apple-darwin.tar.gz && \ +tar -xvf $HOME/.nargo/bin/nargo-x86_64-apple-darwin.tar.gz -C $HOME/.nargo/bin/ && \ +echo '\nexport PATH=$PATH:$HOME/.nargo/bin' >> ~/.zshrc && \ +source ~/.zshrc +``` + +##### Windows (PowerShell) + +Open PowerShell as Administrator and run: + +```powershell +mkdir -f -p "$env:USERPROFILE\.nargo\bin\"; ` +Invoke-RestMethod -Method Get -Uri https://github.com/noir-lang/noir/releases/download/v0.4.1/nargo-x86_64-pc-windows-msvc.zip -Outfile "$env:USERPROFILE\.nargo\bin\nargo-x86_64-pc-windows-msvc.zip"; ` +Expand-Archive -Path "$env:USERPROFILE\.nargo\bin\nargo-x86_64-pc-windows-msvc.zip" -DestinationPath "$env:USERPROFILE\.nargo\bin\"; ` +$Reg = "Registry::HKLM\System\CurrentControlSet\Control\Session Manager\Environment"; ` +$OldPath = (Get-ItemProperty -Path "$Reg" -Name PATH).Path; ` +$NewPath = $OldPath + ’;’ + "$env:USERPROFILE\.nargo\bin\"; ` +Set-ItemProperty -Path "$Reg" -Name PATH –Value "$NewPath"; ` +$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") +``` + +##### Linux (Bash) + +```bash +mkdir -p $HOME/.nargo/bin && \ +curl -o $HOME/.nargo/bin/nargo-x86_64-unknown-linux-gnu.tar.gz -L https://github.com/noir-lang/noir/releases/download/v0.6.0/nargo-x86_64-unknown-linux-gnu.tar.gz && \ +tar -xvf $HOME/.nargo/bin/nargo-x86_64-unknown-linux-gnu.tar.gz -C $HOME/.nargo/bin/ && \ +echo -e '\nexport PATH=$PATH:$HOME/.nargo/bin' >> ~/.bashrc && \ +source ~/.bashrc +``` + +#### Step 2 + +Check if the installation was successful by running `nargo --help`. + +> **macOS:** If you are prompted with an OS alert, right-click and open the _nargo_ executable from +> Finder. Close the new terminal popped up and `nargo` should now be accessible. + +For a successful installation, you should see something similar to the following after running the +command: + +```sh +$ nargo --help + +Noir's package manager + +Usage: nargo + +Commands: + check Checks the constraint system for errors + codegen-verifier Generates a Solidity verifier smart contract for the program + compile Compile the program and its secret execution trace into ACIR format + new Create a new binary project + execute Executes a circuit to calculate its return value + prove Create proof for this program. The proof is returned as a hex encoded string + verify Given a proof and a program, verify whether the proof is valid + test Run the tests for this program + gates Counts the occurrences of different gates in circuit + help Print this message or the help of the given subcommand(s) +``` + +### Option 3: Compile from Source + +Due to the large number of native dependencies, Noir projects uses [Nix](https://nixos.org/) and [direnv](https://direnv.net/) to streamline the development experience. It helps mitigating ssues commonly associated with dependency management, such as conflicts between required package versions for different projects (often referred to as "dependency hell"). + +Combined with direnv, which automatically sets or unsets environment variables based on the directory, it further simplifies the development process by seamlessly integrating with the developer's shell, facilitating an efficient and reliable workflow for managing and deploying Noir projects with multiple dependencies. + +#### Setting up your environment + +For the best experience, please follow these instructions to setup your environment: + +1. Install Nix following [their guide](https://nixos.org/download.html) for your operating system. +2. Create the file `~/.config/nix/nix.conf` with the contents: + +```ini +experimental-features = nix-command +extra-experimental-features = flakes +``` + +3. Install direnv into your Nix profile by running: + +```sh +nix profile install nixpkgs#direnv +``` + +4. Add direnv to your shell following [their guide](https://direnv.net/docs/hook.html). + 1. For bash or zshell, add `eval "$(direnv hook bash)"` or `eval "$(direnv hook zsh)"` to your ~/.bashrc or ~/.zshrc file, respectively. +5. Restart your shell. + +#### Shell & editor experience + +Now that your environment is set up, you can get to work on the project. + +1. Clone the repository, such as: + +```sh +git clone git@github.com:noir-lang/noir +``` + +> Replacing `noir` with whichever repository you want to work on. + +2. Navigate to the directory: + +```sh +cd noir +``` + +> Replacing `noir` with whichever repository you cloned. + +3. You should see a **direnv error** because projects aren't allowed by default. Make sure you've reviewed and trust our `.envrc` file, then you need to run: + +```sh +direnv allow +``` + +4. Now, wait awhile for all the native dependencies to be built. This will take some time and direnv will warn you that it is taking a long time, but we just need to let it run. + +5. Once you are presented with your prompt again, you can start your editor within the project directory (we recommend [VSCode](https://code.visualstudio.com/)): + +```sh +code . +``` + +6. (Recommended) When launching VSCode for the first time, you should be prompted to install our recommended plugins. We highly recommend installing these for the best development experience. + +#### Building and testing + +Assuming you are using `direnv` to populate your environment, building and testing the project can be done +with the typical `cargo build`, `cargo test`, and `cargo clippy` commands. You'll notice that the `cargo` version matches the version we specify in `flake.nix`, which is 1.66.0 at the time of this writing. + +If you want to build the entire project in an isolated sandbox, you can use Nix commands: + +1. `nix build .` (or `nix build . -L` for verbose output) to build the project in a Nix sandbox. +2. `nix flake check` (or `nix flake check -L` for verbose output) to run clippy and tests in a Nix sandbox. + +#### Without `direnv` + +If you have hesitations with using direnv, you can launch a subshell with `nix develop` and then launch your editor from within the subshell. However, if VSCode was already launched in the project directory, the environment won't be updated. + +Advanced: If you aren't using direnv nor launching your editor within the subshell, you can try to install Barretenberg and other global dependencies the package needs. This is an advanced workflow and likely won't receive support! + +## Uninstalling Nargo + +### Noirup + +If you installed Noir with `noirup`, you can uninstall Noir by removing the files in `~/.nargo`, `~/nargo` and `~/noir_cache`. + +```bash +rm -r ~/.nargo +rm -r ~/nargo +rm -r ~/noir_cache +``` + +### Nix + +If you installed Noir with Nix or from source, you can remove the binary located at `~/.nix-profile/bin/nargo`. + +```bash +rm ~/.nix-profile/bin/nargo +``` + +[noir vs code extension]: https://marketplace.visualstudio.com/items?itemName=noir-lang.vscode-noir diff --git a/docs/versioned_docs/version-0.17.0/getting_started/01_hello_world.md b/docs/versioned_docs/version-0.17.0/getting_started/01_hello_world.md new file mode 100644 index 00000000000..8b4416beba1 --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/getting_started/01_hello_world.md @@ -0,0 +1,147 @@ +--- +title: Create A Project +description: + Learn how to create and verify your first Noir program using Nargo, a programming language for + zero-knowledge proofs. +keywords: + [ + Nargo, + Noir, + zero-knowledge proofs, + programming language, + create Noir program, + verify Noir program, + step-by-step guide, + ] +--- + +Now that we have installed Nargo, it is time to make our first hello world program! + +## Create a Project Directory + +Noir code can live anywhere on your computer. Let us create a _projects_ folder in the home +directory to house our Noir programs. + +For Linux, macOS, and Windows PowerShell, create the directory and change directory into it by +running: + +```sh +mkdir ~/projects +cd ~/projects +``` + +For Windows CMD, run: + +```sh +> mkdir "%USERPROFILE%\projects" +> cd /d "%USERPROFILE%\projects" +``` + +## Create Our First Nargo Project + +Now that we are in the projects directory, create a new Nargo project by running: + +```sh +nargo new hello_world +``` + +> **Note:** `hello_world` can be any arbitrary project name, we are simply using `hello_world` for +> demonstration. +> +> In production, the common practice is to name the project folder as `circuits` for better +> identifiability when sitting alongside other folders in the codebase (e.g. `contracts`, `scripts`, +> `test`). + +A `hello_world` folder would be created. Similar to Rust, the folder houses _src/main.nr_ and +_Nargo.toml_ that contains the source code and environmental options of your Noir program +respectively. + +### Intro to Noir Syntax + +Let us take a closer look at _main.nr_. The default _main.nr_ generated should look like this: + +```rust +fn main(x : Field, y : pub Field) { + assert(x != y); +} +``` + +The first line of the program specifies the program's inputs: + +```rust +x : Field, y : pub Field +``` + +Program inputs in Noir are private by default (e.g. `x`), but can be labeled public using the +keyword `pub` (e.g. `y`). To learn more about private and public values, check the +[Data Types](../language_concepts/data_types) section. + +The next line of the program specifies its body: + +```rust +assert(x != y); +``` + +The Noir syntax `assert` can be interpreted as something similar to constraints in other zk-contract languages. + +For more Noir syntax, check the [Language Concepts](../language_concepts/comments) chapter. + +## Build In/Output Files + +Change directory into _hello_world_ and build in/output files for your Noir program by running: + +```sh +cd hello_world +nargo check +``` + +Two additional files would be generated in your project directory: + +_Prover.toml_ houses input values, and _Verifier.toml_ houses public values. + +## Prove Our Noir Program + +Now that the project is set up, we can create a proof of correct execution on our Noir program. + +Fill in input values for execution in the _Prover.toml_ file. For example: + +```toml +x = "1" +y = "2" +``` + +Prove the valid execution of your Noir program: + +```sh +nargo prove +``` + +A new folder _proofs_ would then be generated in your project directory, containing the proof file +`.proof`, where the project name is defined in Nargo.toml. + +The _Verifier.toml_ file would also be updated with the public values computed from program +execution (in this case the value of `y`): + +```toml +y = "0x0000000000000000000000000000000000000000000000000000000000000002" +``` + +> **Note:** Values in _Verifier.toml_ are computed as 32-byte hex values. + +## Verify Our Noir Program + +Once a proof is generated, we can verify correct execution of our Noir program by verifying the +proof file. + +Verify your proof by running: + +```sh +nargo verify +``` + +The verification will complete in silence if it is successful. If it fails, it will log the +corresponding error instead. + +Congratulations, you have now created and verified a proof for your very first Noir program! + +In the [next section](breakdown), we will go into more detail on each step performed. diff --git a/docs/versioned_docs/version-0.17.0/getting_started/02_breakdown.md b/docs/versioned_docs/version-0.17.0/getting_started/02_breakdown.md new file mode 100644 index 00000000000..bc0e742fb4e --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/getting_started/02_breakdown.md @@ -0,0 +1,198 @@ +--- +title: Project Breakdown +description: + Learn about the anatomy of a Nargo project, including the purpose of the Prover and Verifier TOML + files, and how to prove and verify your program. +keywords: + [Nargo, Nargo project, Prover.toml, Verifier.toml, proof verification, private asset transfer] +--- + +This section breaks down our hello world program in section _1.2_. We elaborate on the project +structure and what the `prove` and `verify` commands did in the previous section. + +## Anatomy of a Nargo Project + +Upon creating a new project with `nargo new` and building the in/output files with `nargo check` +commands, you would get a minimal Nargo project of the following structure: + + - src + - Prover.toml + - Verifier.toml + - Nargo.toml + +The source directory _src_ holds the source code for your Noir program. By default only a _main.nr_ +file will be generated within it. + +### Prover.toml + +_Prover.toml_ is used for specifying the input values for executing and proving the program. You can specify `toml` files with different names by using the `--prover-name` or `-p` flags, see the [Prover](#provertoml) section below. Optionally you may specify expected output values for prove-time checking as well. + +### Verifier.toml + +_Verifier.toml_ contains public in/output values computed when executing the Noir program. + +### Nargo.toml + +_Nargo.toml_ contains the environmental options of your project. It contains a "package" section and a "dependencies" section. + +Example Nargo.toml: + +```toml +[package] +name = "noirstarter" +type = "bin" +authors = ["Alice"] +compiler_version = "0.9.0" +description = "Getting started with Noir" +entry = "circuit/main.nr" +license = "MIT" + +[dependencies] +ecrecover = {tag = "v0.9.0", git = "https://github.com/colinnielsen/ecrecover-noir.git"} +``` + +Nargo.toml for a [workspace](../modules_packages_crates/workspaces) will look a bit different. For example: + +```toml +[workspace] +members = ["crates/a", "crates/b"] +default-member = "crates/a" +``` + +#### Package section + +The package section requires a number of fields including: + +- `name` (**required**) - the name of the package +- `type` (**required**) - can be "bin", "lib", or "contract" to specify whether its a binary, library or Aztec contract +- `authors` (optional) - authors of the project +- `compiler_version` (optional) - specifies the version of the compiler to use. This is not currently enforced by the compiler, but will be in future versions. +- `description` (optional) +- `entry` (optional) - a relative filepath to use as the entry point into your package (overrides the default of `src/lib.nr` or `src/main.nr`) +- `backend` (optional) +- `license` (optional) + +#### Dependencies section + +This is where you will specify any dependencies for your project. See the [Dependencies page](../modules_packages_crates/dependencies) for more info. + +`./proofs/` and `./contract/` directories will not be immediately visible until you create a proof or +verifier contract respectively. + +### main.nr + +The _main.nr_ file contains a `main` method, this method is the entry point into your Noir program. + +In our sample program, _main.nr_ looks like this: + +```rust +fn main(x : Field, y : Field) { + assert(x != y); +} +``` + +The parameters `x` and `y` can be seen as the API for the program and must be supplied by the +prover. Since neither `x` nor `y` is marked as public, the verifier does not supply any inputs, when +verifying the proof. + +The prover supplies the values for `x` and `y` in the _Prover.toml_ file. + +As for the program body, `assert` ensures the satisfaction of the condition (e.g. `x != y`) is +constrained by the proof of the execution of said program (i.e. if the condition was not met, the +verifier would reject the proof as an invalid proof). + +### Prover.toml + +The _Prover.toml_ file is a file which the prover uses to supply his witness values(both private and +public). + +In our hello world program the _Prover.toml_ file looks like this: + +```toml +x = "1" +y = "2" +``` + +When the command `nargo prove` is executed, two processes happen: + +1. Noir creates a proof that `x` which holds the value of `1` and `y` which holds the value of `2` + is not equal. This not equal constraint is due to the line `assert(x != y)`. + +2. Noir creates and stores the proof of this statement in the _proofs_ directory in a file called your-project.proof. So if your project is named "private_voting" (defined in the project Nargo.toml), the proof will be saved at `./proofs/private_voting.proof`. Opening this file will display the proof in hex format. + +#### Arrays of Structs + +The following code shows how to pass an array of structs to a Noir program to generate a proof. + +```rust +// main.nr +struct Foo { + bar: Field, + baz: Field, +} + +fn main(foos: [Foo; 3]) -> pub Field { + foos[2].bar + foos[2].baz +} +``` + +Prover.toml: + +```toml +[[foos]] # foos[0] +bar = 0 +baz = 0 + +[[foos]] # foos[1] +bar = 0 +baz = 0 + +[[foos]] # foos[2] +bar = 1 +baz = 2 +``` + +#### Custom toml files + +You can specify a `toml` file with a different name to use for proving by using the `--prover-name` or `-p` flags. + +This command looks for proof inputs in the default **Prover.toml** and generates the proof and saves it at `./proofs/.proof`: + +```bash +nargo prove +``` + +This command looks for proof inputs in the custom **OtherProver.toml** and generates proof and saves it at `./proofs/.proof`: + +```bash +nargo prove -p OtherProver +``` + +## Verifying a Proof + +When the command `nargo verify` is executed, two processes happen: + +1. Noir checks in the _proofs_ directory for a proof file with the project name (eg. test_project.proof) + +2. If that file is found, the proof's validity is checked + +> **Note:** The validity of the proof is linked to the current Noir program; if the program is +> changed and the verifier verifies the proof, it will fail because the proof is not valid for the +> _modified_ Noir program. + +In production, the prover and the verifier are usually two separate entities. A prover would +retrieve the necessary inputs, execute the Noir program, generate a proof and pass it to the +verifier. The verifier would then retrieve the public inputs from usually external sources and +verifies the validity of the proof against it. + +Take a private asset transfer as an example: + +A user on browser as the prover would retrieve private inputs (e.g. the user's private key) and +public inputs (e.g. the user's encrypted balance on-chain), compute the transfer, generate a proof +and submit it to the verifier smart contract. + +The verifier contract would then draw the user's encrypted balance directly from the blockchain and +verify the proof submitted against it. If the verification passes, additional functions in the +verifier contract could trigger (e.g. approve the asset transfer). + +Now that you understand the concepts, you'll probably want some editor feedback while you are writing more complex code. diff --git a/docs/versioned_docs/version-0.17.0/index.md b/docs/versioned_docs/version-0.17.0/index.md new file mode 100644 index 00000000000..9ebe1d54944 --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/index.md @@ -0,0 +1,99 @@ +--- +title: Introducing Noir +description: + Learn about the public alpha release of Noir, a domain specific language heavily influenced by + Rust that compiles to an intermediate language which can be compiled to an arithmetic circuit or a + rank-1 constraint system. +keywords: + [ + Noir, + Domain Specific Language, + Rust, + Intermediate Language, + Arithmetic Circuit, + Rank-1 Constraint System, + Ethereum Developers, + Protocol Developers, + Blockchain Developers, + Proving System, + Smart Contract Language, + ] +slug: / +--- + +## What is Noir? + +Noir is a Domain Specific Language for SNARK proving systems. It has been designed to use any ACIR compatible proving system. + +It's design choices are influenced heavily by Rust and focuses on a simple, familiar syntax. + +## Who is Noir for? + +Noir can be used for a variety of purposes. + +### Solidity Developers + +Noir currently includes a command to create a Solidity contract which verifies your Noir program. This will +be modularised in the future; however, as of the alpha, you can use the [`nargo codegen-verifier`](./nargo/commands#nargo-codegen-verifier) command to create +a verifier contract. + +### Protocol Developers + +As a protocol developer, you may not want to use the Aztec backend due to it not being a fit for +your stack, or maybe you simply want to use a different proving system. Since Noir does not compile +to a specific proof system, it is possible for protocol developers to replace the PLONK-based +proving system with a different proving system altogether. + +### Blockchain developers + +As a blockchain developer, you will be constrained by parameters set by your blockchain (for example, the +proving system and smart contract language has been pre-defined). In order for you to use Noir in +your blockchain, a proving system backend and a smart contract interface +must be implemented for it. + +## What's new about Noir? + +Noir is simple and flexible in its design, as it does not compile immediately to a fixed +NP-complete language. Instead, Noir compiles to an intermediate language (ACIR), which itself can be compiled +to an arithmetic circuit (if choosing to target Aztec's barretenberg backend) or a rank-1 constraint system (if choosing to target an R1CS backend like Arkwork's Marlin backend, or others). + +This in itself brings up a few challenges within the design process, but allows one to decouple the programming language completely from the backend. This is similar in theory to LLVM. + +## Current Features + +Compiler: + +- Module System +- For expressions +- Arrays +- Bit Operations +- Binary operations (<, <=, >, >=, +, -, \*, /, %) [See documentation for an extensive list] +- Unsigned integers +- If statements +- Structures and Tuples +- Generics + +ACIR Supported OPCODES: + +- Sha256 +- Blake2s +- Schnorr signature verification +- MerkleMembership +- Pedersen +- HashToField + +## Libraries + +Noir does not currently have an official package manager. You can find a list of available Noir libraries in the [awesome-noir repo here](https://github.com/noir-lang/awesome-noir#libraries). + +Some libraries that are available today include: + +- [Standard Library](https://github.com/noir-lang/noir/tree/master/noir_stdlib) - the Noir Standard Library +- [Ethereum Storage Proof Verification](https://github.com/aragonzkresearch/noir-trie-proofs) - a library that contains the primitives necessary for RLP decoding (in the form of look-up table construction) and Ethereum state and storage proof verification (or verification of any trie proof involving 32-byte long keys) +- [BigInt](https://github.com/shuklaayush/noir-bigint) - a library that provides a custom BigUint56 data type, allowing for computations on large unsigned integers +- [ECrecover](https://github.com/colinnielsen/ecrecover-noir/tree/main) - a library to verify an ECDSA signature and return the source Ethereum address +- [Sparse Merkle Tree Verifier](https://github.com/vocdoni/smtverifier-noir/tree/main) - a library for verification of sparse Merkle trees +- [Signed Int](https://github.com/resurgencelabs/signed_int) - a library for accessing a custom Signed Integer data type, allowing access to negative numbers on Noir +- [Fraction](https://github.com/resurgencelabs/fraction) - a library for accessing fractional number data type in Noir, allowing results that aren't whole numbers + +See the section on [dependencies](./modules_packages_crates/dependencies) for more information. diff --git a/docs/versioned_docs/version-0.17.0/language_concepts/01_functions.md b/docs/versioned_docs/version-0.17.0/language_concepts/01_functions.md new file mode 100644 index 00000000000..47cdea0cf04 --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/language_concepts/01_functions.md @@ -0,0 +1,225 @@ +--- +title: Functions +description: + Learn how to declare functions and methods in Noir, a programming language with Rust semantics. + This guide covers parameter declaration, return types, call expressions, and more. +keywords: [Noir, Rust, functions, methods, parameter declaration, return types, call expressions] +--- + +Functions in Noir follow the same semantics of Rust, though Noir does not support early returns. + +To declare a function the `fn` keyword is used. + +```rust +fn foo() {} +``` + +By default, functions are visible only within the package they are defined. To make them visible outside of that package (for example, as part of a [library](../modules_packages_crates/crates_and_packages.md#libraries)), you should mark them as `pub`: + +```rust +pub fn foo() {} +``` + +You can also restrict the visibility of the function to only the crate it was defined in, by specifying `pub(crate)`: + +```rust +pub(crate) fn foo() {} //foo can only be called within its crate +``` + +All parameters in a function must have a type and all types are known at compile time. The parameter +is pre-pended with a colon and the parameter type. Multiple parameters are separated using a comma. + +```rust +fn foo(x : Field, y : pub Field){} +``` + +The return type of a function can be stated by using the `->` arrow notation. The function below +states that the foo function must return a `Field`. If the function returns no value, then the arrow +is omitted. + +```rust +fn foo(x : Field, y : pub Field) -> Field { + x + y +} +``` + +Note that a `return` keyword is unneeded in this case - the last expression in a function's body is +returned. + +## Main function + +If you're writing a binary, the `main` function is the starting point of your program. You can pass all types of expressions to it, as long as they have a fixed size at compile time: + +```rust +fn main(x : Field) // this is fine: passing a Field +fn main(x : [Field; 2]) // this is also fine: passing a Field with known size at compile-time +fn main(x : (Field, bool)) // 👌: passing a (Field, bool) tuple means size 2 +fn main(x : str<5>) // this is fine, as long as you pass a string of size 5 + +fn main(x : Vec) // can't compile, has variable size +fn main(x : [Field]) // can't compile, has variable size +fn main(....// i think you got it by now +``` + +Keep in mind [tests](../nargo/02_testing.md) don't differentiate between `main` and any other function. The following snippet passes tests, but won't compile or prove: + +```rust +fn main(x : [Field]) { + assert(x[0] == 1); +} + +#[test] +fn test_one() { + main([1, 2]); +} +``` + +```bash +$ nargo test +[testing] Running 1 test functions +[testing] Testing test_one... ok +[testing] All tests passed + +$ nargo check +The application panicked (crashed). +Message: Cannot have variable sized arrays as a parameter to main +``` + +## Call Expressions + +Calling a function in Noir is executed by using the function name and passing in the necessary +arguments. + +Below we show how to call the `foo` function from the `main` function using a call expression: + +```rust +fn main(x : Field, y : Field) { + let z = foo(x); +} + +fn foo(x : Field) -> Field { + x + x +} +``` + +## Methods + +You can define methods in Noir on any struct type in scope. + +```rust +struct MyStruct { + foo: Field, + bar: Field, +} + +impl MyStruct { + fn new(foo: Field) -> MyStruct { + MyStruct { + foo, + bar: 2, + } + } + + fn sum(self) -> Field { + self.foo + self.bar + } +} + +fn main() { + let s = MyStruct::new(40); + assert(s.sum() == 42); +} +``` + +Methods are just syntactic sugar for functions, so if we wanted to we could also call `sum` as +follows: + +```rust +assert(MyStruct::sum(s) == 42); +``` + +It is also possible to specialize which method is chosen depending on the [generic](./06_generics.md) type that is used. In this example, the `foo` function returns different values depending on its type: + +```rust +struct Foo {} + +impl Foo { + fn foo(self) -> Field { 1 } +} + +impl Foo { + fn foo(self) -> Field { 2 } +} + +fn main() { + let f1: Foo = Foo{}; + let f2: Foo = Foo{}; + assert(f1.foo() + f2.foo() == 3); +} +``` + +Also note that impls with the same method name defined in them cannot overlap. For example, if we already have `foo` defined for `Foo` and `Foo` like we do above, we cannot also define `foo` in an `impl Foo` since it would be ambiguous which version of `foo` to choose. + +```rust +// Including this impl in the same project as the above snippet would +// cause an overlapping impls error +impl Foo { + fn foo(self) -> Field { 3 } +} +``` + +## Lambdas + +Lambdas are anonymous functions. They follow the syntax of Rust - `|arg1, arg2, ..., argN| return_expression`. + +```rust +let add_50 = |val| val + 50; +assert(add_50(100) == 150); +``` + +See [Lambdas](./08_lambdas.md) for more details. + +## Attributes + +Attributes are metadata that can be applied to a function, using the following syntax: `#[attribute(value)]`. + +Supported attributes include: + +- **builtin**: the function is implemented by the compiler, for efficiency purposes. +- **deprecated**: mark the function as _deprecated_. Calling the function will generate a warning: `warning: use of deprecated function` +- **field**: Used to enable conditional compilation of code depending on the field size. See below for more details +- **oracle**: mark the function as _oracle_; meaning it is an external unconstrained function, implemented in noir_js. See [Unconstrained](./05_unconstrained.md) and [NoirJS](../noir_js/noir_js.md) for more details. +- **test**: mark the function as unit tests. See [Tests](../nargo/02_testing.md) for more details + +### Field Attribute + +The field attribute defines which field the function is compatible for. The function is conditionally compiled, under the condition that the field attribute matches the Noir native field. +The field can be defined implicitly, by using the name of the elliptic curve usually associated to it - for instance bn254, bls12_381 - or explicitly by using the field (prime) order, in decimal or hexadecimal form. +As a result, it is possible to define multiple versions of a function with each version specialized for a different field attribute. This can be useful when a function requires different parameters depending on the underlying elliptic curve. + +Example: we define the function `foo()` three times below. Once for the default Noir bn254 curve, once for the field $\mathbb F_{23}$, which will normally never be used by Noir, and once again for the bls12_381 curve. + +```rust +#[field(bn254)] +fn foo() -> u32 { + 1 +} + +#[field(23)] +fn foo() -> u32 { + 2 +} + +// This commented code would not compile as foo would be defined twice because it is the same field as bn254 +// #[field(21888242871839275222246405745257275088548364400416034343698204186575808495617)] +// fn foo() -> u32 { +// 2 +// } + +#[field(bls12_381)] +fn foo() -> u32 { + 3 +} +``` + +If the field name is not known to Noir, it will discard the function. Field names are case insensitive. diff --git a/docs/versioned_docs/version-0.17.0/language_concepts/02_control_flow.md b/docs/versioned_docs/version-0.17.0/language_concepts/02_control_flow.md new file mode 100644 index 00000000000..a7f85360197 --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/language_concepts/02_control_flow.md @@ -0,0 +1,44 @@ +--- +title: Control Flow +description: + Learn how to use loops and if expressions in the Noir programming language. Discover the syntax + and examples for for loops and if-else statements. +keywords: [Noir programming language, loops, for loop, if-else statements, Rust syntax] +--- + +## Loops + +Noir has one kind of loop: the `for` loop. `for` loops allow you to repeat a block of code multiple +times. + +The following block of code between the braces is run 10 times. + +```rust +for i in 0..10 { + // do something +}; +``` + +The index for loops is of type `u64`. + +## If Expressions + +Noir supports `if-else` statements. The syntax is most similar to Rust's where it is not required +for the statement's conditional to be surrounded by parentheses. + +```rust +let a = 0; +let mut x: u32 = 0; + +if a == 0 { + if a != 0 { + x = 6; + } else { + x = 2; + } +} else { + x = 5; + assert(x == 5); +} +assert(x == 2); +``` diff --git a/docs/versioned_docs/version-0.17.0/language_concepts/03_ops.md b/docs/versioned_docs/version-0.17.0/language_concepts/03_ops.md new file mode 100644 index 00000000000..da02b126059 --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/language_concepts/03_ops.md @@ -0,0 +1,97 @@ +--- +title: Logical Operations +description: + Learn about the supported arithmetic and logical operations in the Noir programming language. + Discover how to perform operations on private input types, integers, and booleans. +keywords: + [ + Noir programming language, + supported operations, + arithmetic operations, + logical operations, + predicate operators, + bitwise operations, + short-circuiting, + backend, + ] +--- + +# Operations + +## Table of Supported Operations + +| Operation | Description | Requirements | +| :-------- | :------------------------------------------------------------: | -------------------------------------: | +| + | Adds two private input types together | Types must be private input | +| - | Subtracts two private input types together | Types must be private input | +| \* | Multiplies two private input types together | Types must be private input | +| / | Divides two private input types together | Types must be private input | +| ^ | XOR two private input types together | Types must be integer | +| & | AND two private input types together | Types must be integer | +| \| | OR two private input types together | Types must be integer | +| << | Left shift an integer by another integer amount | Types must be integer | +| >> | Right shift an integer by another integer amount | Types must be integer | +| ! | Bitwise not of a value | Type must be integer or boolean | +| < | returns a bool if one value is less than the other | Upper bound must have a known bit size | +| <= | returns a bool if one value is less than or equal to the other | Upper bound must have a known bit size | +| > | returns a bool if one value is more than the other | Upper bound must have a known bit size | +| >= | returns a bool if one value is more than or equal to the other | Upper bound must have a known bit size | +| == | returns a bool if one value is equal to the other | Both types must not be constants | +| != | returns a bool if one value is not equal to the other | Both types must not be constants | + +### Predicate Operators + +`<,<=, !=, == , >, >=` are known as predicate/comparison operations because they compare two values. +This differs from the operations such as `+` where the operands are used in _computation_. + +### Bitwise Operations Example + +```rust +fn main(x : Field) { + let y = x as u32; + let z = y & y; +} +``` + +`z` is implicitly constrained to be the result of `y & y`. The `&` operand is used to denote bitwise +`&`. + +> `x & x` would not compile as `x` is a `Field` and not an integer type. + +### Logical Operators + +Noir has no support for the logical operators `||` and `&&`. This is because encoding the +short-circuiting that these operators require can be inefficient for Noir's backend. Instead you can +use the bitwise operators `|` and `&` which operate indentically for booleans, just without the +short-circuiting. + +```rust +let my_val = 5; + +let mut flag = 1; +if (my_val > 6) | (my_val == 0) { + flag = 0; +} +assert(flag == 1); + +if (my_val != 10) & (my_val < 50) { + flag = 0; +} +assert(flag == 0); +``` + +### Shorthand operators + +Noir shorthand operators for most of the above operators, namely `+=, -=, *=, /=, %=, &=, |=, ^=, <<=`, and `>>=`. These allow for more concise syntax. For example: + +```rust +let mut i = 0; +i = i + 1; +``` + +could be written as: + +```rust +let mut i = 0; +i += 1; +``` diff --git a/docs/versioned_docs/version-0.17.0/language_concepts/04_assert.md b/docs/versioned_docs/version-0.17.0/language_concepts/04_assert.md new file mode 100644 index 00000000000..7427ec6cc63 --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/language_concepts/04_assert.md @@ -0,0 +1,26 @@ +--- +title: Assert Function +description: + Learn about the assert function in Noir, which can be used to explicitly constrain the predicate or + comparison expression that follows to be true, and what happens if the expression is false at + runtime. +keywords: [Noir programming language, assert statement, predicate expression, comparison expression] +--- + +Noir includes a special `assert` function which will explicitly constrain the predicate/comparison +expression that follows to be true. If this expression is false at runtime, the program will fail to +be proven. Example: + +```rust +fn main(x : Field, y : Field) { + assert(x == y); +} +``` + +You can optionally provide a message to be logged when the assertion fails: + +```rust +assert(x == y, "x and y are not equal"); +``` + +> Assertions only work for predicate operations, such as `==`. If there's any ambiguity on the operation, the program will fail to compile. For example, it is unclear if `assert(x + y)` would check for `x + y == 0` or simply would return `true`. diff --git a/docs/versioned_docs/version-0.17.0/language_concepts/05_unconstrained.md b/docs/versioned_docs/version-0.17.0/language_concepts/05_unconstrained.md new file mode 100644 index 00000000000..6b621eda3eb --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/language_concepts/05_unconstrained.md @@ -0,0 +1,96 @@ +--- +title: Unconstrained Functions +description: "Learn about what unconstrained functions in Noir are, how to use them and when you'd want to." + +keywords: [Noir programming language, unconstrained, open] +--- + + + +Unconstrained functions are functions which do not constrain any of the included computation and allow for non-determinisitic computation. + +## Why? + +Zero-knowledge (ZK) domain-specific languages (DSL) enable developers to generate ZK proofs from their programs by compiling code down to the constraints of an NP complete language (such as R1CS or PLONKish languages). However, the hard bounds of a constraint system can be very limiting to the functionality of a ZK DSL. + +Enabling a circuit language to perform unconstrained execution is a powerful tool. Said another way, unconstrained execution lets developers generate witnesses from code that does not generate any constraints. Being able to execute logic outside of a circuit is critical for both circuit performance and constructing proofs on information that is external to a circuit. + +Fetching information from somewhere external to a circuit can also be used to enable developers to improve circuit efficiency. + +A ZK DSL does not just prove computation, but proves that some computation was handled correctly. Thus, it is necessary that when we switch from performing some operation directly inside of a circuit to inside of an unconstrained environment that the appropriate constraints are still laid down elsewhere in the circuit. + +## Example + +An in depth example might help drive the point home. This example comes from the excellent [post](https://discord.com/channels/1113924620781883405/1124022445054111926/1128747641853972590) by Tom in the Noir Discord. + +Let's look at how we can optimize a function to turn a `u72` into an array of `u8`s. + +```rust +fn main(num: u72) -> pub [u8; 8] { + let mut out: [u8; 8] = [0; 8]; + for i in 0..8 { + out[i] = (num >> (56 - (i * 8)) as u72 & 0xff) as u8; + } + + out +} +``` + +``` +Total ACIR opcodes generated for language PLONKCSat { width: 3 }: 91 +Backend circuit size: 3619 +``` + +A lot of the operations in this function are optimized away by the compiler (all the bit-shifts turn into divisions by constants). However we can save a bunch of gates by casting to u8 a bit earlier. This automatically truncates the bit-shifted value to fit in a u8 which allows us to remove the XOR against 0xff. This saves us ~480 gates in total. + +```rust +fn main(num: u72) -> pub [u8; 8] { + let mut out: [u8; 8] = [0; 8]; + for i in 0..8 { + out[i] = (num >> (56 - (i * 8)) as u8; + } + + out +} +``` + +``` +Total ACIR opcodes generated for language PLONKCSat { width: 3 }: 75 +Backend circuit size: 3143 +``` + +Those are some nice savings already but we can do better. This code is all constrained so we're proving every step of calculating out using num, but we don't actually care about how we calculate this, just that it's correct. This is where brillig comes in. + +It turns out that truncating a u72 into a u8 is hard to do inside a snark, each time we do as u8 we lay down 4 ACIR opcodes which get converted into multiple gates. It's actually much easier to calculate num from out than the other way around. All we need to do is multiply each element of out by a constant and add them all together, both relatively easy operations inside a snark. + +We can then run u72_to_u8 as unconstrained brillig code in order to calculate out, then use that result in our constrained function and assert that if we were to do the reverse calculation we'd get back num. This looks a little like the below: + +```rust +fn main(num: u72) -> pub [u8; 8] { + let out = u72_to_u8(num); + + let mut reconstructed_num: u72 = 0; + for i in 0..8 { + reconstructed_num += (out[i] as u72 << (56 - (8 * i))); + } + assert(num == reconstructed_num); + out +} + +unconstrained fn u72_to_u8(num: u72) -> [u8; 8] { + let mut out: [u8; 8] = [0; 8]; + for i in 0..8 { + out[i] = (num >> (56 - (i * 8))) as u8; + } + out +} +``` + +``` +Total ACIR opcodes generated for language PLONKCSat { width: 3 }: 78 +Backend circuit size: 2902 +``` + +This ends up taking off another ~250 gates from our circuit! We've ended up with more ACIR opcodes than before but they're easier for the backend to prove (resulting in fewer gates). + +Generally we want to use brillig whenever there's something that's easy to verify but hard to compute within the circuit. For example, if you wanted to calculate a square root of a number it'll be a much better idea to calculate this in brillig and then assert that if you square the result you get back your number. diff --git a/docs/versioned_docs/version-0.17.0/language_concepts/06_generics.md b/docs/versioned_docs/version-0.17.0/language_concepts/06_generics.md new file mode 100644 index 00000000000..b700bd5bc5b --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/language_concepts/06_generics.md @@ -0,0 +1,113 @@ +--- +title: Generics +description: Learn how to use Generics in Noir +keywords: [Noir, Rust, generics, functions, structs] +--- + +Generics allow you to use the same functions with multiple different concrete data types. You can +read more about the concept of generics in the Rust documentation +[here](https://doc.rust-lang.org/book/ch10-01-syntax.html). + +Here is a trivial example showing the identity function that supports any type. In Rust, it is +common to refer to the most general type as `T`. We follow the same convention in Noir. + +```rust +fn id(x: T) -> T { + x +} +``` + +## In Structs + +Generics are useful for specifying types in structs. For example, we can specify that a field in a +struct will be of a certain generic type. In this case `value` is of type `T`. + +```rust +struct RepeatedValue { + value: T, + count: Field, +} + +impl RepeatedValue { + fn new(value: T) -> Self { + Self { value, count: 1 } + } + + fn increment(mut repeated: Self) -> Self { + repeated.count += 1; + repeated + } + + fn print(self) { + for _i in 0 .. self.count { + dep::std::println(self.value); + } + } +} + +fn main() { + let mut repeated = RepeatedValue::new("Hello!"); + repeated = repeated.increment(); + repeated.print(); +} +``` + +The `print` function will print `Hello!` an arbitrary number of times, twice in this case. + +If we want to be generic over array lengths (which are type-level integers), we can use numeric +generics. Using these looks just like using regular generics, but these generics can resolve to +integers at compile-time, rather than resolving to types. Here's an example of a struct that is +generic over the size of the array it contains internally: + +```rust +struct BigInt { + limbs: [u32; N], +} + +impl BigInt { + // `N` is in scope of all methods in the impl + fn first(first: BigInt, second: BigInt) -> Self { + assert(first.limbs != second.limbs); + first + + fn second(first: BigInt, second: Self) -> Self { + assert(first.limbs != second.limbs); + second + } +} +``` + +## Calling functions on generic parameters + +Unlike Rust, Noir does not have traits, so how can one translate the equivalent of a trait bound in +Rust into Noir? That is, how can we write a function that is generic over some type `T`, while also +requiring there is a function like `eq: fn(T, T) -> bool` that works on the type? + +The answer is that we can translate this by passing in the function manually. Here's an example of +implementing array equality in Noir: + +```rust +fn array_eq(array1: [T; N], array2: [T; N], elem_eq: fn(T, T) -> bool) -> bool { + if array1.len() != array2.len() { + false + } else { + let mut result = true; + for i in 0 .. array1.len() { + result &= elem_eq(array1[i], array2[i]); + } + result + } +} + +fn main() { + assert(array_eq([1, 2, 3], [1, 2, 3], |a, b| a == b)); + + // We can use array_eq even for arrays of structs, as long as we have + // an equality function for these structs we can pass in + let array = [MyStruct::new(), MyStruct::new()]; + assert(array_eq(array, array, MyStruct::eq)); +} +``` + +You can see an example of generics in the tests +[here](https://github.com/noir-lang/noir/blob/master/crates/nargo_cli/tests/test_data/generics/src/main.nr). diff --git a/docs/versioned_docs/version-0.17.0/language_concepts/07_mutability.md b/docs/versioned_docs/version-0.17.0/language_concepts/07_mutability.md new file mode 100644 index 00000000000..4641521b1d9 --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/language_concepts/07_mutability.md @@ -0,0 +1,92 @@ +--- +title: Mutability +description: + Learn about mutable variables, constants, and globals in Noir programming language. Discover how + to declare, modify, and use them in your programs. +keywords: [noir programming language, mutability in noir, mutable variables, constants, globals] +--- + +Variables in noir can be declared mutable via the `mut` keyword. Mutable variables can be reassigned +to via an assignment expression. + +```rust +let x = 2; +x = 3; // error: x must be mutable to be assigned to + +let mut y = 3; +let y = 4; // OK +``` + +The `mut` modifier can also apply to patterns: + +```rust +let (a, mut b) = (1, 2); +a = 11; // error: a must be mutable to be assigned to +b = 12; // OK + +let mut (c, d) = (3, 4); +c = 13; // OK +d = 14; // OK + +// etc. +let MyStruct { x: mut y } = MyStruct { x: a }; +// y is now in scope +``` + +Note that mutability in noir is local and everything is passed by value, so if a called function +mutates its parameters then the parent function will keep the old value of the parameters. + +```rust +fn main() -> Field { + let x = 3; + helper(x); + x // x is still 3 +} + +fn helper(mut x: i32) { + x = 4; +} +``` + +## Comptime Values + +:::warning + +The 'comptime' keyword was removed in version 0.10. The comptime keyword and syntax are currently still kept and parsed for backwards compatibility, but are now deprecated and will issue a warning when used. `comptime` has been removed because it is no longer needed for accessing arrays. + +::: + +## Globals + +Noir also supports global variables. However, they must be known at compile-time. The global type can also be inferred by the compiler entirely. Globals can also be used to specify array +annotations for function parameters and can be imported from submodules. + +```rust +global N: Field = 5; // Same as `global N: Field = 5` + +fn main(x : Field, y : [Field; N]) { + let res = x * N; + + assert(res == y[0]); + + let res2 = x * mysubmodule::N; + assert(res != res2); +} + +mod mysubmodule { + use dep::std; + + global N: Field = 10; + + fn my_helper() -> Field { + let x = N; + x + } +} +``` + +## Why only local mutability? + +Witnesses in a proving system are immutable in nature. Noir aims to _closely_ mirror this setting +without applying additional overhead to the user. Modeling a mutable reference is not as +straightforward as on conventional architectures and would incur some possibly unexpected overhead. diff --git a/docs/versioned_docs/version-0.17.0/language_concepts/08_lambdas.md b/docs/versioned_docs/version-0.17.0/language_concepts/08_lambdas.md new file mode 100644 index 00000000000..ae1e6aecab1 --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/language_concepts/08_lambdas.md @@ -0,0 +1,80 @@ +--- +title: Lambdas +description: Learn how to use anonymous functions in Noir programming language. +keywords: [Noir programming language, lambda, closure, function, anonymous function] +--- + +## Introduction + +Lambdas are anonymous functions. The syntax is `|arg1, arg2, ..., argN| return_expression`. + +```rust +let add_50 = |val| val + 50; +assert(add_50(100) == 150); +``` + +A block can be used as the body of a lambda, allowing you to declare local variables inside it: + +```rust +let cool = || { + let x = 100; + let y = 100; + x + y +} + +assert(cool() == 200); +``` + +## Closures + +Inside the body of a lambda, you can use variables defined in the enclosing function. Such lambdas are called **closures**. In this example `x` is defined inside `main` and is accessed from within the lambda: + +```rust +fn main() { + let x = 100; + let closure = || x + 150; + assert(closure() == 250); +} +``` + +## Passing closures to higher-order functions + +It may catch you by surprise that the following code fails to compile: + +```rust +fn foo(f: fn () -> Field) -> Field { + f() +} + +fn main() { + let (x, y) = (50, 50); + assert(foo(|| x + y) == 100); // error :( +} +``` + +The reason is that the closure's capture environment affects its type - we have a closure that captures two Fields and `foo` +expects a regular function as an argument - those are incompatible. +:::note + +Variables contained within the `||` are the closure's parameters, and the expression that follows it is the closure's body. The capture environment is comprised of any variables used in the closure's body that are not parameters. + +E.g. in |x| x + y, y would be a captured variable, but x would not be, since it is a parameter of the closure. + +::: +The syntax for the type of a closure is `fn[env](args) -> ret_type`, where `env` is the capture environment of the closure - +in this example that's `(Field, Field)`. + +The best solution in our case is to make `foo` generic over the environment type of its parameter, so that it can be called +with closures with any environment, as well as with regular functions: + +```rust +fn foo(f: fn[Env]() -> Field) -> Field { + f() +} + +fn main() { + let (x, y) = (50, 50); + assert(foo(|| x + y) == 100); // compiles fine + assert(foo(|| 60) == 60); // compiles fine +} +``` diff --git a/docs/versioned_docs/version-0.17.0/language_concepts/09_comments.md b/docs/versioned_docs/version-0.17.0/language_concepts/09_comments.md new file mode 100644 index 00000000000..3bb4d2f25a4 --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/language_concepts/09_comments.md @@ -0,0 +1,32 @@ +--- +title: Comments +description: + Learn how to write comments in Noir programming language. A comment is a line of code that is + ignored by the compiler, but it can be read by programmers. Single-line and multi-line comments + are supported in Noir. +keywords: [Noir programming language, comments, single-line comments, multi-line comments] +--- + +A comment is a line in your codebase which the compiler ignores, however it can be read by +programmers. + +Here is a single line comment: + +```rust +// This is a comment and is ignored +``` + +`//` is used to tell the compiler to ignore the rest of the line. + +Noir also supports multi-line block comments. Start a block comment with `/*` and end the block with `*/`. + +Noir does not natively support doc comments. You may be able to use [Rust doc comments](https://doc.rust-lang.org/reference/comments.html) in your code to leverage some Rust documentation build tools with Noir code. + +```rust +/* + This is a block comment describing a complex function. +*/ +fn main(x : Field, y : pub Field) { + assert(x != y); +} +``` diff --git a/docs/versioned_docs/version-0.17.0/language_concepts/10_distinct.md b/docs/versioned_docs/version-0.17.0/language_concepts/10_distinct.md new file mode 100644 index 00000000000..e7ff7f5017a --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/language_concepts/10_distinct.md @@ -0,0 +1,63 @@ +--- +title: Distinct Witnesses +--- + +The `distinct` keyword prevents repetitions of witness indices in the program's ABI. This ensures +that the witnesses being returned as public inputs are all unique. + +The `distinct` keyword is only used for return values on program entry points (usually the `main()` +function). + +When using `distinct` and `pub` simultaneously, `distinct` comes first. See the example below. + +You can read more about the problem this solves +[here](https://github.com/noir-lang/noir/issues/1183). + +## Example + +Without the `distinct` keyword, the following program + +```rust +fn main(x : pub Field, y : pub Field) -> pub [Field; 4] { + let a = 1; + let b = 1; + [x + 1, y, a, b] +} +``` + +compiles to + +```json +{ + //... + "abi": { + //... + "param_witnesses": { "x": [1], "y": [2] }, + "return_witnesses": [3, 2, 4, 4] + } +} +``` + +Whereas (with the `distinct` keyword) + +```rust +fn main(x : pub Field, y : pub Field) -> distinct pub [Field; 4] { + let a = 1; + let b = 1; + [x + 1, y, a, b] +} +``` + +compiles to + +```json +{ + //... + "abi": { + //... + "param_witnesses": { "x": [1], "y": [2] }, + //... + "return_witnesses": [3, 4, 5, 6] + } +} +``` diff --git a/docs/versioned_docs/version-0.17.0/language_concepts/11_shadowing.md b/docs/versioned_docs/version-0.17.0/language_concepts/11_shadowing.md new file mode 100644 index 00000000000..efd743e764f --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/language_concepts/11_shadowing.md @@ -0,0 +1,43 @@ +--- +title: Shadowing +--- + +Noir allows for inheriting variables' values and re-declaring them with the same name similar to Rust, known as shadowing. + +For example, the following function is valid in Noir: + +```rust +fn main() { + let x = 5; + + { + let x = x * 2; + assert (x == 10); + } + + assert (x == 5); +} +``` + +In this example, a variable x is first defined with the value 5. + +The local scope that follows shadows the original x, i.e. creates a local mutable x based on the value of the original x. It is given a value of 2 times the original x. + +When we return to the main scope, x once again refers to just the original x, which stays at the value of 5. + +## Temporal mutability + +One way that shadowing is useful, in addition to ergonomics across scopes, is for temporarily mutating variables. + +```rust +fn main() { + let age = 30; + // age = age + 5; // Would error as `age` is immutable by default. + + let mut age = age + 5; // Temporarily mutates `age` with a new value. + + let age = age; // Locks `age`'s mutability again. + + assert (age == 35); +} +``` diff --git a/docs/versioned_docs/version-0.17.0/language_concepts/data_types.md b/docs/versioned_docs/version-0.17.0/language_concepts/data_types.md new file mode 100644 index 00000000000..d546cc463a8 --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/language_concepts/data_types.md @@ -0,0 +1,96 @@ +--- +title: Data Types +description: + Get a clear understanding of the two categories of Noir data types - primitive types and compound + types. Learn about their characteristics, differences, and how to use them in your Noir + programming. +keywords: + [ + noir, + data types, + primitive types, + compound types, + private types, + public types, + ] +--- + +Every value in Noir has a type, which determines which operations are valid for it. + +All values in Noir are fundamentally composed of `Field` elements. For a more approachable +developing experience, abstractions are added on top to introduce different data types in Noir. + +Noir has two category of data types: primitive types (e.g. `Field`, integers, `bool`) and compound +types that group primitive types (e.g. arrays, tuples, structs). Each value can either be private or +public. + +## Private & Public Types + +A **private value** is known only to the Prover, while a **public value** is known by both the +Prover and Verifier. Mark values as `private` when the value should only be known to the prover. All +primitive types (including individual fields of compound types) in Noir are private by default, and +can be marked public when certain values are intended to be revealed to the Verifier. + +> **Note:** For public values defined in Noir programs paired with smart contract verifiers, once +> the proofs are verified on-chain the values can be considered known to everyone that has access to +> that blockchain. + +Public data types are treated no differently to private types apart from the fact that their values +will be revealed in proofs generated. Simply changing the value of a public type will not change the +circuit (where the same goes for changing values of private types as well). + +_Private values_ are also referred to as _witnesses_ sometimes. + +> **Note:** The terms private and public when applied to a type (e.g. `pub Field`) have a different +> meaning than when applied to a function (e.g. `pub fn foo() {}`). +> +> The former is a visibility modifier for the Prover to interpret if a value should be made known to +> the Verifier, while the latter is a visibility modifier for the compiler to interpret if a +> function should be made accessible to external Noir programs like in other languages. + +### pub Modifier + +All data types in Noir are private by default. Types are explicitly declared as public using the +`pub` modifier: + +```rust +fn main(x : Field, y : pub Field) -> pub Field { + x + y +} +``` + +In this example, `x` is **private** while `y` and `x + y` (the return value) are **public**. Note +that visibility is handled **per variable**, so it is perfectly valid to have one input that is +private and another that is public. + +> **Note:** Public types can only be declared through parameters on `main`. + +## Type Aliases + +A type alias is a new name for an existing type. Type aliases are declared with the keyword `type`: + +```rust +type Id = u8; + +fn main() { + let id: Id = 1; + let zero: u8 = 0; + assert(zero + 1 == id); +} +``` + +Type aliases can also be used with [generics](./06_generics.md): + +```rust +type Id = Size; + +fn main() { + let id: Id = 1; + let zero: u32 = 0; + assert(zero + 1 == id); +} +``` + +### BigInt + +You can acheive BigInt functionality using the [Noir BigInt](https://github.com/shuklaayush/noir-bigint) library. diff --git a/docs/versioned_docs/version-0.17.0/language_concepts/data_types/00_fields.md b/docs/versioned_docs/version-0.17.0/language_concepts/data_types/00_fields.md new file mode 100644 index 00000000000..658a0441ffb --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/language_concepts/data_types/00_fields.md @@ -0,0 +1,165 @@ +--- +title: Fields +description: + Dive deep into the Field data type in Noir. Understand its methods, practical examples, and best practices to effectively use Fields in your Noir programs. +keywords: + [ + noir, + field type, + methods, + examples, + best practices, + ] +--- + +The field type corresponds to the native field type of the proving backend. + +The size of a Noir field depends on the elliptic curve's finite field for the proving backend +adopted. For example, a field would be a 254-bit integer when paired with the default backend that +spans the Grumpkin curve. + +Fields support integer arithmetic and are often used as the default numeric type in Noir: + +```rust +fn main(x : Field, y : Field) { + let z = x + y; +} +``` + +`x`, `y` and `z` are all private fields in this example. Using the `let` keyword we defined a new +private value `z` constrained to be equal to `x + y`. + +If proving efficiency is of priority, fields should be used as a default for solving problems. +Smaller integer types (e.g. `u64`) incur extra range constraints. + +## Methods + +After declaring a Field, you can use these common methods on it: + +### to_le_bits + +Transforms the field into an array of bits, Little Endian. + +```rust +fn to_le_bits(_x : Field, _bit_size: u32) -> [u1; N] +``` + +example: + +```rust +fn main() { + let field = 2; + let bits = field.to_le_bits(32); +} +``` + +### to_be_bits + +Transforms the field into an array of bits, Big Endian. + +```rust +fn to_be_bits(_x : Field, _bit_size: u32) -> [u1; N] +``` + +example: + +```rust +fn main() { + let field = 2; + let bits = field.to_be_bits(32); +} +``` + +### to_le_bytes + +Transforms into an array of bytes, Little Endian + +```rust +fn to_le_bytes(_x : Field, byte_size: u32) -> [u8] +``` + +example: + +```rust +fn main() { + let field = 2; + let bytes = field.to_le_bytes(4); +} +``` + +### to_be_bytes + +Transforms into an array of bytes, Big Endian + +```rust +fn to_be_bytes(_x : Field, byte_size: u32) -> [u8] +``` + +example: + +```rust +fn main() { + let field = 2; + let bytes = field.to_be_bytes(4); +} +``` + +### to_le_radix + +Decomposes into a vector over the specified base, Little Endian + +```rust +fn to_le_radix(_x : Field, _radix: u32, _result_len: u32) -> [u8] +``` + +example: + +```rust +fn main() { + let field = 2; + let radix = field.to_le_radix(256, 4); +} +``` + +### to_be_radix + +Decomposes into a vector over the specified base, Big Endian + +```rust +fn to_be_radix(_x : Field, _radix: u32, _result_len: u32) -> [u8] +``` + +example: + +```rust +fn main() { + let field = 2; + let radix = field.to_be_radix(256, 4); +} +``` + +### pow_32 + +Returns the value to the power of the specified exponent + +```rust +fn pow_32(self, exponent: Field) -> Field +``` + +example: + +```rust +fn main() { + let field = 2 + let pow = field.pow_32(4); + assert(pow == 16); +} +``` + +### sgn0 + +Parity of (prime) Field element, i.e. sgn0(x mod p) = 0 if x ∈ {0, ..., p-1} is even, otherwise sgn0(x mod p) = 1. + +```rust +fn sgn0(self) -> u1 +``` diff --git a/docs/versioned_docs/version-0.17.0/language_concepts/data_types/01_integers.md b/docs/versioned_docs/version-0.17.0/language_concepts/data_types/01_integers.md new file mode 100644 index 00000000000..d9c5e20e795 --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/language_concepts/data_types/01_integers.md @@ -0,0 +1,33 @@ +--- +title: Integers +description: + Explore the Integer data type in Noir. Learn about its methods, see real-world examples, and grasp how to efficiently use Integers in your Noir code. +keywords: + [ + noir, + integer types, + methods, + examples, + arithmetic, + ] +--- + +An integer type is a range constrained field type. The Noir frontend currently supports unsigned, +arbitrary-sized integer types. + +An integer type is specified first with the letter `u`, indicating its unsigned nature, followed by +its length in bits (e.g. `32`). For example, a `u32` variable can store a value in the range of +$\\([0,2^{32}-1]\\)$: + +```rust +fn main(x : Field, y : u32) { + let z = x as u32 + y; +} +``` + +`x`, `y` and `z` are all private values in this example. However, `x` is a field while `y` and `z` +are unsigned 32-bit integers. If `y` or `z` exceeds the range $\\([0,2^{32}-1]\\)$, proofs created +will be rejected by the verifier. + +> **Note:** The default backend supports both even (e.g. `u16`, `u48`) and odd (e.g. `u5`, `u3`) +> sized integer types. diff --git a/docs/versioned_docs/version-0.17.0/language_concepts/data_types/02_booleans.md b/docs/versioned_docs/version-0.17.0/language_concepts/data_types/02_booleans.md new file mode 100644 index 00000000000..885db167d83 --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/language_concepts/data_types/02_booleans.md @@ -0,0 +1,30 @@ +--- +title: Booleans +description: + Delve into the Boolean data type in Noir. Understand its methods, practical examples, and best practices for using Booleans in your Noir programs. +keywords: + [ + noir, + boolean type, + methods, + examples, + logical operations, + ] +--- + + +The `bool` type in Noir has two possible values: `true` and `false`: + +```rust +fn main() { + let t = true; + let f: bool = false; +} +``` + +> **Note:** When returning a boolean value, it will show up as a value of 1 for `true` and 0 for +> `false` in _Verifier.toml_. + +The boolean type is most commonly used in conditionals like `if` expressions and `assert` +statements. More about conditionals is covered in the [Control Flow](../control_flow) and +[Assert Function](../assert) sections. diff --git a/docs/versioned_docs/version-0.17.0/language_concepts/data_types/03_strings.md b/docs/versioned_docs/version-0.17.0/language_concepts/data_types/03_strings.md new file mode 100644 index 00000000000..c42f34ec3ad --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/language_concepts/data_types/03_strings.md @@ -0,0 +1,63 @@ +--- +title: Strings +description: + Discover the String data type in Noir. Learn about its methods, see real-world examples, and understand how to effectively manipulate and use Strings in Noir. +keywords: + [ + noir, + string type, + methods, + examples, + concatenation, + ] +--- + + +The string type is a fixed length value defined with `str`. + +You can use strings in `assert()` functions or print them with +`std::println()`. See more about [Logging](../../standard_library/logging). + +```rust +use dep::std; + +fn main(message : pub str<11>, hex_as_string : str<4>) { + std::println(message); + assert(message == "hello world"); + assert(hex_as_string == "0x41"); +} +``` + +You can convert a `str` to a byte array by calling `as_bytes()` +or a vector by calling `as_bytes_vec()`. + +```rust +fn main() { + let message = "hello world"; + let message_bytes = message.as_bytes(); + let mut message_vec = message.as_bytes_vec(); + assert(message_bytes.len() == 11); + assert(message_bytes[0] == 104); + assert(message_bytes[0] == message_vec.get(0)); +} +``` + +## Escape characters + +You can use escape characters for your strings: + +| Escape Sequence | Description | +|-----------------|-----------------| +| `\r` | Carriage Return | +| `\n` | Newline | +| `\t` | Tab | +| `\0` | Null Character | +| `\"` | Double Quote | +| `\\` | Backslash | + +Example: + +```rust +let s = "Hello \"world" // prints "Hello "world" +let s = "hey \tyou"; // prints "hey you" +``` diff --git a/docs/versioned_docs/version-0.17.0/language_concepts/data_types/04_arrays.md b/docs/versioned_docs/version-0.17.0/language_concepts/data_types/04_arrays.md new file mode 100644 index 00000000000..bdbd1798bef --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/language_concepts/data_types/04_arrays.md @@ -0,0 +1,244 @@ +--- +title: Arrays +description: + Dive into the Array data type in Noir. Grasp its methods, practical examples, and best practices for efficiently using Arrays in your Noir code. +keywords: + [ + noir, + array type, + methods, + examples, + indexing, + ] +--- + +An array is one way of grouping together values into one compound type. Array types can be inferred +or explicitly specified via the syntax `[; ]`: + +```rust +fn main(x : Field, y : Field) { + let my_arr = [x, y]; + let your_arr: [Field; 2] = [x, y]; +} +``` + +Here, both `my_arr` and `your_arr` are instantiated as an array containing two `Field` elements. + +Array elements can be accessed using indexing: + +```rust +fn main() { + let a = [1, 2, 3, 4, 5]; + + let first = a[0]; + let second = a[1]; +} +``` + +All elements in an array must be of the same type (i.e. homogeneous). That is, an array cannot group +a `Field` value and a `u8` value together for example. + +You can write mutable arrays, like: + +```rust +fn main() { + let mut arr = [1, 2, 3, 4, 5]; + assert(arr[0] == 1); + + arr[0] = 42; + assert(arr[0] == 42); +} +``` + +You can instantiate a new array of a fixed size with the same value repeated for each element. The following example instantiates an array of length 32 where each element is of type Field and has the value 0. + +```rust +let array: [Field; 32] = [0; 32]; +``` + +Like in Rust, arrays in Noir are a fixed size. However, if you wish to convert an array to a [slice](./slices), you can just call `as_slice` on your array: + +```rust +let array: [Field; 32] = [0; 32]; +let sl = array.as_slice() +``` + +You can define multidimensional arrays: + +```rust +let array : [[Field; 2]; 2]; +let element = array[0][0]; +``` + +## Types + +You can create arrays of primitive types or structs. There is not yet support for nested arrays +(arrays of arrays) or arrays of structs that contain arrays. + +## Methods + +For convenience, the STD provides some ready-to-use, common methods for arrays: + +### len + +Returns the length of an array + +```rust +fn len(_array: [T; N]) -> comptime Field +``` + +example + +```rust +fn main() { + let array = [42, 42]; + assert(array.len() == 2); +} +``` + +### sort + +Returns a new sorted array. The original array remains untouched. Notice that this function will +only work for arrays of fields or integers, not for any arbitrary type. This is because the sorting +logic it uses internally is optimized specifically for these values. If you need a sort function to +sort any type, you should use the function `sort_via` described below. + +```rust +fn sort(_array: [T; N]) -> [T; N] +``` + +example + +```rust +fn main() { + let arr = [42, 32]; + let sorted = arr.sort(); + assert(sorted == [32, 42]); +} +``` + +### sort_via + +Sorts the array with a custom comparison function + +```rust +fn sort_via(mut a: [T; N], ordering: fn(T, T) -> bool) -> [T; N] +``` + +example + +```rust +fn main() { + let arr = [42, 32] + let sorted_ascending = arr.sort_via(|a, b| a < b); + assert(sorted_ascending == [32, 42]); // verifies + + let sorted_descending = arr.sort_via(|a, b| a > b); + assert(sorted_descending == [32, 42]); // does not verify +} +``` + +### map + +Applies a function to each element of the array, returning a new array containing the mapped elements. + +```rust +fn map(f: fn(T) -> U) -> [U; N] +``` + +example + +```rust +let a = [1, 2, 3]; +let b = a.map(|a| a * 2); // b is now [2, 4, 6] +``` + +### fold + +Applies a function to each element of the array, returning the final accumulated value. The first +parameter is the initial value. + +```rust +fn fold(mut accumulator: U, f: fn(U, T) -> U) -> U +``` + +This is a left fold, so the given function will be applied to the accumulator and first element of +the array, then the second, and so on. For a given call the expected result would be equivalent to: + +```rust +let a1 = [1]; +let a2 = [1, 2]; +let a3 = [1, 2, 3]; + +let f = |a, b| a - b; +a1.fold(10, f) //=> f(10, 1) +a2.fold(10, f) //=> f(f(10, 1), 2) +a3.fold(10, f) //=> f(f(f(10, 1), 2), 3) +``` + +example: + +```rust + +fn main() { + let arr = [2, 2, 2, 2, 2]; + let folded = arr.fold(0, |a, b| a + b); + assert(folded == 10); +} + +``` + +### reduce + +Same as fold, but uses the first element as starting element. + +```rust +fn reduce(f: fn(T, T) -> T) -> T +``` + +example: + +```rust +fn main() { + let arr = [2, 2, 2, 2, 2]; + let reduced = arr.reduce(|a, b| a + b); + assert(reduced == 10); +} +``` + +### all + +Returns true if all the elements satisfy the given predicate + +```rust +fn all(predicate: fn(T) -> bool) -> bool +``` + +example: + +```rust +fn main() { + let arr = [2, 2, 2, 2, 2]; + let all = arr.all(|a| a == 2); + assert(all); +} +``` + +### any + +Returns true if any of the elements satisfy the given predicate + +```rust +fn any(predicate: fn(T) -> bool) -> bool +``` + +example: + +```rust +fn main() { + let arr = [2, 2, 2, 2, 5]; + let any = arr.any(|a| a == 5); + assert(any); +} + +``` diff --git a/docs/versioned_docs/version-0.17.0/language_concepts/data_types/05_slices.mdx b/docs/versioned_docs/version-0.17.0/language_concepts/data_types/05_slices.mdx new file mode 100644 index 00000000000..0d599ab7b24 --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/language_concepts/data_types/05_slices.mdx @@ -0,0 +1,146 @@ +--- +title: Slices +description: Explore the Slice data type in Noir. Understand its methods, see real-world examples, and learn how to effectively use Slices in your Noir programs. +keywords: [noir, slice type, methods, examples, subarrays] +--- + +import Experimental from '../../../../src/components/Notes/_experimental.mdx'; + + + +A slice is a dynamically-sized view into a sequence of elements. They can be resized at runtime, but because they don't own the data, they cannot be returned from a circuit. You can treat slices as arrays without a constrained size. + +```rust +use dep::std::slice; + +fn main() -> pub Field { + let mut slice: [Field] = [0; 2]; + + let mut new_slice = slice.push_back(6); + new_slice.len() +} +``` + +View the corresponding test file [here]([test-file]. + +[test-file]: https://github.com/noir-lang/noir/blob/f387ec1475129732f72ba294877efdf6857135ac/crates/nargo_cli/tests/test_data_ssa_refactor/slices/src/main.nr + +## Methods + +For convenience, the STD provides some ready-to-use, common methods for slices: + +### push_back + +Pushes a new element to the end of the slice, returning a new slice with a length one greater than the original unmodified slice. + +```rust +fn push_back(_self: [T], _elem: T) -> [T] +``` + +example: + +```rust +fn main() -> pub Field { + let mut slice: [Field] = [0; 2]; + + let mut new_slice = slice.push_back(6); + new_slice.len() +} +``` + +View the corresponding test file [here][test-file]. + +### push_front + +Returns a new array with the specified element inserted at index 0. The existing elements indexes are incremented by 1. + +```rust +fn push_front(_self: Self, _elem: T) -> Self +``` + +Example: + +```rust +let mut new_slice: [Field] = []; +new_slice = new_slice.push_front(20); +assert(new_slice[0] == 20); // returns true +``` + +View the corresponding test file [here][test-file]. + +### pop_front + +Returns a tuple of two items, the first element of the array and the rest of the array. + +```rust +fn pop_front(_self: Self) -> (T, Self) +``` + +Example: + +```rust +let (first_elem, rest_of_slice) = slice.pop_front(); +``` + +View the corresponding test file [here][test-file]. + +### pop_back + +Returns a tuple of two items, the beginning of the array with the last element omitted and the last element. + +```rust +fn pop_back(_self: Self) -> (Self, T) +``` + +Example: + +```rust +let (popped_slice, last_elem) = slice.pop_back(); +``` + +View the corresponding test file [here][test-file]. + +### append + +Loops over a slice and adds it to the end of another. + +```rust +fn append(mut self, other: Self) -> Self +``` + +Example: + +```rust +let append = [1, 2].append([3, 4, 5]); +``` + +### insert + +Inserts an element at a specified index and shifts all following elements by 1. + +```rust +fn insert(_self: Self, _index: Field, _elem: T) -> Self +``` + +Example: + +```rust +new_slice = rest_of_slice.insert(2, 100); +assert(new_slice[2] == 100); +``` + +View the corresponding test file [here][test-file]. + +### remove + +Remove an element at a specified index, shifting all elements after it to the left, returning the altered slice and the removed element. + +```rust +fn remove(_self: Self, _index: Field) -> (Self, T) +``` + +Example: + +```rust +let (remove_slice, removed_elem) = slice.remove(3); +``` diff --git a/docs/versioned_docs/version-0.17.0/language_concepts/data_types/06_vectors.mdx b/docs/versioned_docs/version-0.17.0/language_concepts/data_types/06_vectors.mdx new file mode 100644 index 00000000000..75a44f6f78e --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/language_concepts/data_types/06_vectors.mdx @@ -0,0 +1,172 @@ +--- +title: Vectors +description: Delve into the Vector data type in Noir. Learn about its methods, practical examples, and best practices for using Vectors in your Noir code. +keywords: [noir, vector type, methods, examples, dynamic arrays] +--- + +import Experimental from '../../../../src/components/Notes/_experimental.mdx'; + + + +A vector is a collection type similar to Rust's Vector type. It's convenient way to use slices as mutable arrays. + +Example: + +```rust +use dep::std::collections::vec::Vec; + +let mut vector: Vec = Vec::new(); +for i in 0..5 { + vector.push(i); +} +assert(vector.len() == 5); +``` + +## Methods + +### new + +Creates a new, empty vector. + +```rust +pub fn new() -> Self { + Self { slice: [] } +} +``` + +Example: + +```rust +let empty_vector: Vec = Vec::new(); +assert(empty_vector.len() == 0); +``` + +### from_slice + +Creates a vector containing each element from a given slice. Mutations to the resulting vector will not affect the original slice. + +```rust +pub fn from_slice(slice: [T]) -> Self { + Self { slice } +} +``` + +Example: + +```rust +let arr: [Field] = [1, 2, 3]; +let vector_from_slice = Vec::from_slice(arr); +assert(vector_from_slice.len() == 3); +``` + +### get + +Retrieves an element from the vector at a given index. Panics if the index points beyond the vector's end. + +```rust +pub fn get(self, index: Field) -> T { + self.slice[index] +} +``` + +Example: + +```rust +let vector: Vec = Vec::from_slice([10, 20, 30]); +assert(vector.get(1) == 20); +``` + +### push + +Adds a new element to the vector's end, returning a new vector with a length one greater than the original unmodified vector. + +```rust +pub fn push(&mut self, elem: T) { + self.slice = self.slice.push_back(elem); +} +``` + +Example: + +```rust +let mut vector: Vec = Vec::new(); +vector.push(10); +assert(vector.len() == 1); +``` + +### pop + +Removes an element from the vector's end, returning a new vector with a length one less than the original vector, along with the removed element. Panics if the vector's length is zero. + +```rust +pub fn pop(&mut self) -> T { + let (popped_slice, last_elem) = self.slice.pop_back(); + self.slice = popped_slice; + last_elem +} +``` + +Example: + +```rust +let mut vector = Vec::from_slice([10, 20]); +let popped_elem = vector.pop(); +assert(popped_elem == 20); +assert(vector.len() == 1); +``` + +### insert + +Inserts an element at a specified index, shifting subsequent elements to the right. + +```rust +pub fn insert(&mut self, index: Field, elem: T) { + self.slice = self.slice.insert(index, elem); +} +``` + +Example: + +```rust +let mut vector = Vec::from_slice([10, 30]); +vector.insert(1, 20); +assert(vector.get(1) == 20); +``` + +### remove + +Removes an element at a specified index, shifting subsequent elements to the left, and returns the removed element. + +```rust +pub fn remove(&mut self, index: Field) -> T { + let (new_slice, elem) = self.slice.remove(index); + self.slice = new_slice; + elem +} +``` + +Example: + +```rust +let mut vector = Vec::from_slice([10, 20, 30]); +let removed_elem = vector.remove(1); +assert(removed_elem == 20); +assert(vector.len() == 2); +``` + +### len + +Returns the number of elements in the vector. + +```rust +pub fn len(self) -> Field { + self.slice.len() +} +``` + +Example: + +```rust +let empty_vector: Vec = Vec::new(); +assert(empty_vector.len() == 0); +``` diff --git a/docs/versioned_docs/version-0.17.0/language_concepts/data_types/07_tuples.md b/docs/versioned_docs/version-0.17.0/language_concepts/data_types/07_tuples.md new file mode 100644 index 00000000000..5f6cab974a8 --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/language_concepts/data_types/07_tuples.md @@ -0,0 +1,47 @@ +--- +title: Tuples +description: + Dive into the Tuple data type in Noir. Understand its methods, practical examples, and best practices for efficiently using Tuples in your Noir code. +keywords: + [ + noir, + tuple type, + methods, + examples, + multi-value containers, + ] +--- + +A tuple collects multiple values like an array, but with the added ability to collect values of +different types: + +```rust +fn main() { + let tup: (u8, u64, Field) = (255, 500, 1000); +} +``` + +One way to access tuple elements is via destructuring using pattern matching: + +```rust +fn main() { + let tup = (1, 2); + + let (one, two) = tup; + + let three = one + two; +} +``` + +Another way to access tuple elements is via direct member access, using a period (`.`) followed by +the index of the element we want to access. Index `0` corresponds to the first tuple element, `1` to +the second and so on: + +```rust +fn main() { + let tup = (5, 6, 7, 8); + + let five = tup.0; + let eight = tup.3; +} +``` diff --git a/docs/versioned_docs/version-0.17.0/language_concepts/data_types/08_structs.md b/docs/versioned_docs/version-0.17.0/language_concepts/data_types/08_structs.md new file mode 100644 index 00000000000..85649dfb389 --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/language_concepts/data_types/08_structs.md @@ -0,0 +1,73 @@ +--- +title: Structs +description: + Explore the Struct data type in Noir. Learn about its methods, see real-world examples, and grasp how to effectively define and use Structs in your Noir programs. +keywords: + [ + noir, + struct type, + methods, + examples, + data structures, + ] +--- + +A struct also allows for grouping multiple values of different types. Unlike tuples, we can also +name each field. + +> **Note:** The usage of _field_ here refers to each element of the struct and is unrelated to the +> field type of Noir. + +Defining a struct requires giving it a name and listing each field within as `: ` pairs: + +```rust +struct Animal { + hands: Field, + legs: Field, + eyes: u8, +} +``` + +An instance of a struct can then be created with actual values in `: ` pairs in any +order. Struct fields are accessible using their given names: + +```rust +fn main() { + let legs = 4; + + let dog = Animal { + eyes: 2, + hands: 0, + legs, + }; + + let zero = dog.hands; +} +``` + +Structs can also be destructured in a pattern, binding each field to a new variable: + +```rust +fn main() { + let Animal { hands, legs: feet, eyes } = get_octopus(); + + let ten = hands + feet + eyes as u8; +} + +fn get_octopus() -> Animal { + let octopus = Animal { + hands: 0, + legs: 8, + eyes: 2, + }; + + octopus +} +``` + +The new variables can be bound with names different from the original struct field names, as +showcased in the `legs --> feet` binding in the example above. + +:::note +You can use Structs as inputs to the `main` function, but you can't output them +::: diff --git a/docs/versioned_docs/version-0.17.0/language_concepts/data_types/09_references.md b/docs/versioned_docs/version-0.17.0/language_concepts/data_types/09_references.md new file mode 100644 index 00000000000..b0c35ce2cb9 --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/language_concepts/data_types/09_references.md @@ -0,0 +1,22 @@ +--- +title: References +--- + +Noir supports first-class references. References are a bit like pointers: they point to a specific address that can be followed to access the data stored at that address. You can use Rust-like syntax to use pointers in Noir: the `&` operator references the variable, the `*` operator dereferences it. + +Example: + +```rust +fn main() { + let mut x = 2; + + // you can reference x as &mut and pass it to multiplyBy2 + multiplyBy2(&mut x); +} + +// you can access &mut here +fn multiplyBy2(x: &mut Field) { + // and dereference it with * + *x = *x * 2; +} +``` diff --git a/docs/versioned_docs/version-0.17.0/language_concepts/data_types/10_function_types.md b/docs/versioned_docs/version-0.17.0/language_concepts/data_types/10_function_types.md new file mode 100644 index 00000000000..1ec92efd594 --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/language_concepts/data_types/10_function_types.md @@ -0,0 +1,25 @@ +--- +title: Function types +--- + +Noir supports higher-order functions. The syntax for a function type is as follows: + +```rust +fn(arg1_type, arg2_type, ...) -> return_type +``` + +Example: + +```rust +fn assert_returns_100(f: fn() -> Field) { // f takes no args and returns a Field + assert(f() == 100); +} + +fn main() { + assert_returns_100(|| 100); // ok + assert_returns_100(|| 150); // fails +} +``` + +A function type also has an optional capture environment - this is necessary to support closures. +See [Lambdas](../08_lambdas.md) for more details. diff --git a/docs/versioned_docs/version-0.17.0/migration_notes.md b/docs/versioned_docs/version-0.17.0/migration_notes.md new file mode 100644 index 00000000000..48a8abcf22e --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/migration_notes.md @@ -0,0 +1,83 @@ +--- +title: Migration notes +description: Read about migration notes from previous versions, which could solve problems while updating +keywords: [Noir, notes, migration, updating, upgrading] +--- + +Noir is in full-speed development. Things break fast, wild, and often. This page attempts to leave some notes on errors you might encounter when upgrading and how to resolve them until proper patches are built. + +## ≥0.14 + +The index of the [for loops](./language_concepts/02_control_flow.md#loops) is now of type `u64` instead of `Field`. An example refactor would be: + +```rust +for i in 0..10 { + let i = i as Field; +} +``` + +## ≥v0.11.0 and Nargo backend + +From this version onwards, Nargo starts managing backends through the `nargo backend` command. Upgrading to the versions per usual steps might lead to: + +### `backend encountered an error` + +This is likely due to the existing locally installed version of proving backend (e.g. barretenberg) is incompatible with the version of Nargo in use. + +To fix the issue: + +1. Uninstall the existing backend + +```bash +nargo backend uninstall acvm-backend-barretenberg +``` + +You may replace _acvm-backend-barretenberg_ with the name of your backend listed in `nargo backend ls` or in ~/.nargo/backends. + +2. Reinstall a compatible version of the proving backend. + +If you are using the default barretenberg backend, simply run: + +``` +nargo prove +``` + +with you Noir program. + +This will trigger the download and installation of the latest version of barretenberg compatible with your Nargo in use. + +### `backend encountered an error: illegal instruction` + +On certain Intel-based systems, an `illegal instruction` error may arise due to incompatibility of barretenberg with certain CPU instructions. + +To fix the issue: + +1. Uninstall the existing backend + +```bash +nargo backend uninstall acvm-backend-barretenberg +``` + +You may replace _acvm-backend-barretenberg_ with the name of your backend listed in `nargo backend ls` or in ~/.nargo/backends. + +2. Reinstall a compatible version of the proving backend. + +If you are using the default barretenberg backend, simply run: + +``` +nargo backend install acvm-backend-barretenberg https://github.com/noir-lang/barretenberg-js-binary/raw/master/run-bb.tar.gz +``` + +This downloads and installs a specific bb.js based version of barretenberg binary from GitHub. + +The gzipped filed is running this bash script: , where we need to gzip it as the Nargo currently expect the backend to be zipped up. + +Then run: + +``` +DESIRED_BINARY_VERSION=0.8.1 nargo info +``` + +This overrides the bb native binary with a bb.js node application instead, which should be compatible with most if not all hardware. This does come with the drawback of being generally slower than native binary. + +0.8.1 indicates bb.js version 0.8.1, so if you change that it will update to a different version or the default version in the script if none was supplied. diff --git a/docs/versioned_docs/version-0.17.0/modules_packages_crates/crates_and_packages.md b/docs/versioned_docs/version-0.17.0/modules_packages_crates/crates_and_packages.md new file mode 100644 index 00000000000..fb83a33d94e --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/modules_packages_crates/crates_and_packages.md @@ -0,0 +1,42 @@ +--- +title: Crates and Packages +description: Learn how to use Crates and Packages in your Noir project +keywords: [Nargo, dependencies, package management, crates, package] +--- + +## Crates + +A crate is the smallest amount of code that the Noir compiler considers at a time. +Crates can contain modules, and the modules may be defined in other files that get compiled with the crate, as we’ll see in the coming sections. + +### Crate Types + +A Noir crate can come in several forms: binaries, libraries or contracts. + +#### Binaries + +_Binary crates_ are programs which you can compile to an ACIR circuit which you can then create proofs against. Each must have a function called `main` that defines the ACIR circuit which is to be proved. + +#### Libraries + +_Library crates_ don't have a `main` function and they don't compile down to ACIR. Instead they define functionality intended to be shared with multiple projects, and eventually included in a binary crate. + +#### Contracts + +Contract crates are similar to binary crates in that they compile to ACIR which you can create proofs against. They are different in that they do not have a single `main` function, but are a collection of functions to be deployed to the [Aztec network](https://aztec.network). You can learn more about the technical details of Aztec in the [monorepo](https://github.com/AztecProtocol/aztec-packages) or contract [examples](https://github.com/AztecProtocol/aztec-packages/tree/master/yarn-project/noir-contracts/src/contracts). + +### Crate Root + +Every crate has a root, which is the source file that the compiler starts, this is also known as the root module. The Noir compiler does not enforce any conditions on the name of the file which is the crate root, however if you are compiling via Nargo the crate root must be called `lib.nr` or `main.nr` for library or binary crates respectively. + +## Packages + +A Nargo _package_ is a collection of one of more crates that provides a set of functionality. A package must include a Nargo.toml file. + +A package _must_ contain either a library or a binary crate, but not both. + +### Differences from Cargo Packages + +One notable difference between Rust's Cargo and Noir's Nargo is that while Cargo allows a package to contain an unlimited number of binary crates and a single library crate, Nargo currently only allows a package to contain a single crate. + +In future this restriction may be lifted to allow a Nargo package to contain both a binary and library crate or multiple binary crates. diff --git a/docs/versioned_docs/version-0.17.0/modules_packages_crates/dependencies.md b/docs/versioned_docs/version-0.17.0/modules_packages_crates/dependencies.md new file mode 100644 index 00000000000..75f95aaa305 --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/modules_packages_crates/dependencies.md @@ -0,0 +1,123 @@ +--- +title: Dependencies +description: + Learn how to specify and manage dependencies in Nargo, allowing you to upload packages to GitHub + and use them easily in your project. +keywords: [Nargo, dependencies, GitHub, package management, versioning] +--- + +Nargo allows you to upload packages to GitHub and use them as dependencies. + +## Specifying a dependency + +Specifying a dependency requires a tag to a specific commit and the git url to the url containing +the package. + +Currently, there are no requirements on the tag contents. If requirements are added, it would follow +semver 2.0 guidelines. + +> Note: Without a `tag` , there would be no versioning and dependencies would change each time you +> compile your project. + +For example, to add the [ecrecover-noir library](https://github.com/colinnielsen/ecrecover-noir) to your project, add it to `Nargo.toml`: + +```toml +# Nargo.toml + +[dependencies] +ecrecover = {tag = "v0.8.0", git = "https://github.com/colinnielsen/ecrecover-noir"} +``` + +If the module is in a subdirectory, you can define a subdirectory in your git repository, for example: + +```toml +# Nargo.toml + +[dependencies] +easy_private_token_contract = {tag ="v0.1.0-alpha62", git = "https://github.com/AztecProtocol/aztec-packages", directory = "yarn-project/noir-contracts/src/contracts/easy_private_token_contract"} +``` + +## Specifying a local dependency + +You can also specify dependencies that are local to your machine. + +For example, this file structure has a library and binary crate + +```tree +├── binary_crate +│   ├── Nargo.toml +│   └── src +│   └── main.nr +└── liba + ├── Nargo.toml + └── src + └── lib.nr +``` + +Inside of the binary crate, you can specify: + +```toml +# Nargo.toml + +[dependencies] +libA = { path = "../liba" } +``` + +## Importing dependencies + +You can import a dependency to a Noir file using the following syntax. For example, to import the +ecrecover-noir library and local liba referenced above: + +```rust +use dep::ecrecover; +use dep::libA; +``` + +You can also import only the specific parts of dependency that you want to use, like so: + +```rust +use dep::std::hash::sha256; +use dep::std::scalar_mul::fixed_base_embedded_curve; +``` + +Lastly, as demonstrated in the +[elliptic curve example](../standard_library/cryptographic_primitives/ec_primitives#examples), you +can import multiple items in the same line by enclosing them in curly braces: + +```rust +use dep::std::ec::tecurve::affine::{Curve, Point}; +``` + +We don't have a way to consume libraries from inside a [workspace](./workspaces) as external dependencies right now. + +Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. + +## Dependencies of Dependencies + +Note that when you import a dependency, you also get access to all of the dependencies of that package. + +For example, the [phy_vector](https://github.com/resurgencelabs/phy_vector) library imports an [fraction](https://github.com/resurgencelabs/fraction) library. If you're importing the phy_vector library, then you can access the functions in fractions library like so: + +```rust +use dep::phy_vector; + +fn main(x : Field, y : pub Field) { + //... + let f = phy_vector::fraction::toFraction(true, 2, 1); + //... +} +``` + +## Available Libraries + +Noir does not currently have an official package manager. You can find a list of available Noir libraries in the [awesome-noir repo here](https://github.com/noir-lang/awesome-noir#libraries). + +Some libraries that are available today include: + +- [Standard Library](https://github.com/noir-lang/noir/tree/master/noir_stdlib) - the Noir Standard Library +- [Ethereum Storage Proof Verification](https://github.com/aragonzkresearch/noir-trie-proofs) - a library that contains the primitives necessary for RLP decoding (in the form of look-up table construction) and Ethereum state and storage proof verification (or verification of any trie proof involving 32-byte long keys) +- [BigInt](https://github.com/shuklaayush/noir-bigint) - a library that provides a custom BigUint56 data type, allowing for computations on large unsigned integers +- [ECrecover](https://github.com/colinnielsen/ecrecover-noir/tree/main) - a library to verify an ECDSA signature and return the source Ethereum address +- [Sparse Merkle Tree Verifier](https://github.com/vocdoni/smtverifier-noir/tree/main) - a library for verification of sparse Merkle trees +- [Signed Int](https://github.com/resurgencelabs/signed_int) - a library for accessing a custom Signed Integer data type, allowing access to negative numbers on Noir +- [Fraction](https://github.com/resurgencelabs/fraction) - a library for accessing fractional number data type in Noir, allowing results that aren't whole numbers diff --git a/docs/versioned_docs/version-0.17.0/modules_packages_crates/modules.md b/docs/versioned_docs/version-0.17.0/modules_packages_crates/modules.md new file mode 100644 index 00000000000..147c9b284e8 --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/modules_packages_crates/modules.md @@ -0,0 +1,104 @@ +--- +title: Modules +description: + Learn how to organize your files using modules in Noir, following the same convention as Rust's + module system. Examples included. +keywords: [Noir, Rust, modules, organizing files, sub-modules] +--- + +Noir's module system follows the same convention as the _newer_ version of Rust's module system. + +## Purpose of Modules + +Modules are used to organise files. Without modules all of your code would need to live in a single +file. In Noir, the compiler does not automatically scan all of your files to detect modules. This +must be done explicitly by the developer. + +## Examples + +### Importing a module in the crate root + +Filename : `src/main.nr` + +```rust +mod foo; + +fn main() { + foo::hello_world(); +} +``` + +Filename : `src/foo.nr` + +```rust +fn from_foo() {} +``` + +In the above snippet, the crate root is the `src/main.nr` file. The compiler sees the module +declaration `mod foo` which prompts it to look for a foo.nr file. + +Visually this module hierarchy looks like the following : + +``` +crate + ├── main + │ + └── foo + └── from_foo + +``` + +### Importing a module throughout the tree + +All modules are accessible from the `crate::` namespace. + +``` +crate + ├── bar + ├── foo + └── main + +``` + +In the above snippet, if `bar` would like to use functions in `foo`, it can do so by `use crate::foo::function_name`. + +### Sub-modules + +Filename : `src/main.nr` + +```rust +mod foo; + +fn main() { + foo::from_foo(); +} +``` + +Filename : `src/foo.nr` + +```rust +mod bar; +fn from_foo() {} +``` + +Filename : `src/foo/bar.nr` + +```rust +fn from_bar() {} +``` + +In the above snippet, we have added an extra module to the module tree; `bar`. `bar` is a submodule +of `foo` hence we declare bar in `foo.nr` with `mod bar`. Since `foo` is not the crate root, the +compiler looks for the file associated with the `bar` module in `src/foo/bar.nr` + +Visually the module hierarchy looks as follows: + +``` +crate + ├── main + │ + └── foo + ├── from_foo + └── bar + └── from_bar +``` diff --git a/docs/versioned_docs/version-0.17.0/modules_packages_crates/workspaces.md b/docs/versioned_docs/version-0.17.0/modules_packages_crates/workspaces.md new file mode 100644 index 00000000000..d9ac92667c9 --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/modules_packages_crates/workspaces.md @@ -0,0 +1,39 @@ +--- +title: Workspaces +--- + +Workspaces are a feature of nargo that allow you to manage multiple related Noir packages in a single repository. A workspace is essentially a group of related projects that share common build output directories and configurations. + +Each Noir project (with it's own Nargo.toml file) can be thought of as a package. Each package is expected to contain exactly one "named circuit", being the "name" defined in Nargo.toml with the program logic defined in `./src/main.nr`. + +For a project with the following structure: + +```tree +├── crates +│   ├── a +│   │   ├── Nargo.toml +│   │   └── src +│   │   └── main.nr +│   └── b +│   ├── Nargo.toml +│   └── src +│   └── main.nr +├── Nargo.toml +└── Prover.toml +``` + +You can define a workspace in Nargo.toml like so: + +```toml +[workspace] +members = ["crates/a", "crates/b"] +default-member = "crates/a" +``` + +`members` indicates which packages are included in the workspace. As such, all member packages of a workspace will be processed when the `--workspace` flag is used with various commands or if a `default-member` is not specified. + +`default-member` indicates which package various commands process by default. + +Libraries can be defined in a workspace. Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. + +Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. diff --git a/docs/versioned_docs/version-0.17.0/nargo/01_commands.md b/docs/versioned_docs/version-0.17.0/nargo/01_commands.md new file mode 100644 index 00000000000..65e2bdb44e3 --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/nargo/01_commands.md @@ -0,0 +1,250 @@ +--- +title: Commands +description: + Noir CLI Commands for Noir Prover and Verifier to create, execute, prove and verify programs, + generate Solidity verifier smart contract and compile into JSON file containing ACIR + representation and ABI of circuit. +keywords: + [ + Nargo, + Noir CLI, + Noir Prover, + Noir Verifier, + generate Solidity verifier, + compile JSON file, + ACIR representation, + ABI of circuit, + TypeScript, + ] +--- + +## General options + +| Option | Description | +| -------------------- | -------------------------------------------------- | +| `--show-ssa` | Emit debug information for the intermediate SSA IR | +| `--deny-warnings` | Quit execution when warnings are emitted | +| `--silence-warnings` | Suppress warnings | +| `-h, --help` | Print help | + +## `nargo help [subcommand]` + +Prints the list of available commands or specific information of a subcommand. + +_Arguments_ + +| Argument | Description | +| -------------- | -------------------------------------------- | +| `` | The subcommand whose help message to display | + +## `nargo backend` + +Installs and selects custom backends used to generate and verify proofs. + +### Commands + +| Command | Description | +| ----------- | --------------------------------------------------------- | +| `current` | Prints the name of the currently active backend | +| `ls` | Prints the list of currently installed backends | +| `use` | Select the backend to use | +| `install` | Install a new backend from a URL | +| `uninstall` | Uninstalls a backend | +| `help` | Print this message or the help of the given subcommand(s) | + +### Options + +| Option | Description | +| ------------ | ----------- | +| `-h, --help` | Print help | + +## `nargo check` + +Generate the `Prover.toml` and `Verifier.toml` files for specifying prover and verifier in/output +values of the Noir program respectively. + +### Options + +| Option | Description | +| --------------------- | ------------------------------------- | +| `--package ` | The name of the package to check | +| `--workspace` | Check all packages in the workspace | +| `--print-acir` | Display the ACIR for compiled circuit | +| `--deny-warnings` | Treat all warnings as errors | +| `--silence-warnings` | Suppress warnings | +| `-h, --help` | Print help | + +### `nargo codegen-verifier` + +Generate a Solidity verifier smart contract for the program. + +### Options + +| Option | Description | +| --------------------- | ------------------------------------- | +| `--package ` | The name of the package to codegen | +| `--workspace` | Codegen all packages in the workspace | +| `--print-acir` | Display the ACIR for compiled circuit | +| `--deny-warnings` | Treat all warnings as errors | +| `--silence-warnings` | Suppress warnings | +| `-h, --help` | Print help | + +## `nargo compile` + +Compile the program into a JSON build artifact file containing the ACIR representation and the ABI +of the circuit. This build artifact can then be used to generate and verify proofs. + +You can also use "build" as an alias for compile (e.g. `nargo build`). + +### Options + +| Option | Description | +| --------------------- | ------------------------------------------------------------ | +| `--include-keys` | Include Proving and Verification keys in the build artifacts | +| `--package ` | The name of the package to compile | +| `--workspace` | Compile all packages in the workspace | +| `--print-acir` | Display the ACIR for compiled circuit | +| `--deny-warnings` | Treat all warnings as errors | +| `--silence-warnings` | Suppress warnings | +| `-h, --help` | Print help | + +## `nargo new ` + +Creates a new Noir project in a new folder. + +**Arguments** + +| Argument | Description | +| -------- | -------------------------------- | +| `` | The path to save the new project | + +### Options + +| Option | Description | +| --------------- | ----------------------------------------------------- | +| `--name ` | Name of the package [default: package directory name] | +| `--lib` | Use a library template | +| `--bin` | Use a binary template [default] | +| `--contract` | Use a contract template | +| `-h, --help` | Print help | + +## `nargo init` + +Creates a new Noir project in the current directory. + +### Options + +| Option | Description | +| --------------- | ----------------------------------------------------- | +| `--name ` | Name of the package [default: current directory name] | +| `--lib` | Use a library template | +| `--bin` | Use a binary template [default] | +| `--contract` | Use a contract template | +| `-h, --help` | Print help | + +## `nargo execute [WITNESS_NAME]` + +Runs the Noir program and prints its return value. + +**Arguments** + +| Argument | Description | +| ---------------- | ----------------------------------------- | +| `[WITNESS_NAME]` | Write the execution witness to named file | + +### Options + +| Option | Description | +| --------------------------------- | ------------------------------------------------------------------------------------ | +| `-p, --prover-name ` | The name of the toml file which contains the inputs for the prover [default: Prover] | +| `--package ` | The name of the package to execute | +| `--workspace` | Execute all packages in the workspace | +| `--print-acir` | Display the ACIR for compiled circuit | +| `--deny-warnings` | Treat all warnings as errors | +| `--silence-warnings` | Suppress warnings | +| `-h, --help` | Print help | + +_Usage_ + +The inputs to the circuit are read from the `Prover.toml` file generated by `nargo check`, which +must be filled in. + +To save the witness to file, run the command with a value for the `WITNESS_NAME` argument. A +`.tr` file will then be saved in the `./target` folder. + +## `nargo prove` + +Creates a proof for the program. + +### Options + +| Option | Description | +| ------------------------------------- | ---------------------------------------------------------------------------------------- | +| `-p, --prover-name ` | The name of the toml file which contains the inputs for the prover [default: Prover] | +| `-v, --verifier-name ` | The name of the toml file which contains the inputs for the verifier [default: Verifier] | +| `--verify` | Verify proof after proving | +| `--package ` | The name of the package to prove | +| `--workspace` | Prove all packages in the workspace | +| `--print-acir` | Display the ACIR for compiled circuit | +| `--deny-warnings` | Treat all warnings as errors | +| `--silence-warnings` | Suppress warnings | +| `-h, --help` | Print help | + +## `nargo verify` + +Given a proof and a program, verify whether the proof is valid. + +### Options + +| Option | Description | +| ------------------------------------- | ---------------------------------------------------------------------------------------- | +| `-v, --verifier-name ` | The name of the toml file which contains the inputs for the verifier [default: Verifier] | +| `--package ` | The name of the package to verify | +| `--workspace` | Verify all packages in the workspace | +| `--print-acir` | Display the ACIR for compiled circuit | +| `--deny-warnings` | Treat all warnings as errors | +| `--silence-warnings` | Suppress warnings | +| `-h, --help` | Print help | + +## `nargo test [TEST_NAME]` + +Nargo will automatically compile and run any functions which have the decorator `#[test]` on them if +you run `nargo test`. To print `println` statements in tests, use the `--show-output` flag. + +Takes an optional `--exact` flag which allows you to select tests based on an exact name. + +See an example on the [testing page](./testing). + +### Options + +| Option | Description | +| --------------------- | -------------------------------------- | +| `--show-output` | Display output of `println` statements | +| `--exact` | Only run tests that match exactly | +| `--package ` | The name of the package to test | +| `--workspace` | Test all packages in the workspace | +| `--print-acir` | Display the ACIR for compiled circuit | +| `--deny-warnings` | Treat all warnings as errors | +| `--silence-warnings` | Suppress warnings | +| `-h, --help` | Print help | + +## `nargo info` + +Prints a table containing the information of the package. + +Currently the table provide + +1. The number of ACIR opcodes +2. The final number gates in the circuit used by a backend + +If the file contains a contract the table will provide the +above information about each function of the contract. + +## `nargo lsp` + +Start a long-running Language Server process that communicates over stdin/stdout. +Usually this command is not run by a user, but instead will be run by a Language Client, such as [vscode-noir](https://github.com/noir-lang/vscode-noir). + +## `nargo fmt` + +Automatically formats your Noir source code based on the default formatting settings. diff --git a/docs/versioned_docs/version-0.17.0/nargo/02_testing.md b/docs/versioned_docs/version-0.17.0/nargo/02_testing.md new file mode 100644 index 00000000000..da767274efd --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/nargo/02_testing.md @@ -0,0 +1,61 @@ +--- +title: Testing in Noir +description: Learn how to use Nargo to test your Noir program in a quick and easy way +keywords: [Nargo, testing, Noir, compile, test] +--- + +You can test your Noir programs using Noir circuits. + +Nargo will automatically compile and run any functions which have the decorator `#[test]` on them if +you run `nargo test`. + +For example if you have a program like: + +```rust +fn add(x: u64, y: u64) -> u64 { + x + y +} +#[test] +fn test_add() { + assert(add(2,2) == 4); + assert(add(0,1) == 1); + assert(add(1,0) == 1); +} +``` + +Running `nargo test` will test that the `test_add` function can be executed while satisfying the all +the contraints which allows you to test that add returns the expected values. Test functions can't +have any arguments currently. + +### Test fail + +You can write tests that are expected to fail by using the decorator `#[test(should_fail)]`. For example: + +```rust +fn add(x: u64, y: u64) -> u64 { + x + y +} +#[test(should_fail)] +fn test_add() { + assert(add(2,2) == 5); +} +``` + +You can be more specific and make it fail with a specific reason by using `should_fail_with = "`: + +```rust +fn main(african_swallow_avg_speed : Field) { + assert(african_swallow_avg_speed == 65, "What is the airspeed velocity of an unladen swallow"); +} + +#[test] +fn test_king_arthur() { + main(65); +} + +#[test(should_fail_with = "What is the airspeed velocity of an unladen swallow")] +fn test_bridgekeeper() { + main(32); +} + +``` diff --git a/docs/versioned_docs/version-0.17.0/nargo/03_solidity_verifier.md b/docs/versioned_docs/version-0.17.0/nargo/03_solidity_verifier.md new file mode 100644 index 00000000000..9ac60cb0ba7 --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/nargo/03_solidity_verifier.md @@ -0,0 +1,129 @@ +--- +title: Solidity Verifier +description: + Learn how to run the verifier as a smart contract on the blockchain. Compile a Solidity verifier + contract for your Noir program and deploy it on any EVM blockchain acting as a verifier smart + contract. Read more to find out! +keywords: + [ + solidity verifier, + smart contract, + blockchain, + compiler, + plonk_vk.sol, + EVM blockchain, + verifying Noir programs, + proving backend, + Barretenberg, + ] +--- + +For certain applications, it may be desirable to run the verifier as a smart contract instead of on +a local machine. + +Compile a Solidity verifier contract for your Noir program by running: + +```sh +nargo codegen-verifier +``` + +A new `contract` folder would then be generated in your project directory, containing the Solidity +file `plonk_vk.sol`. It can be deployed on any EVM blockchain acting as a verifier smart contract. + +> **Note:** It is possible to compile verifier contracts of Noir programs for other smart contract +> platforms as long as the proving backend supplies an implementation. +> +> Barretenberg, the default proving backend for Nargo, supports compilation of verifier contracts in +> Solidity only for the time being. + +## Verify + +To verify a proof using the Solidity verifier contract, call the `verify` function with the +following signature: + +```solidity +function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external view returns (bool) +``` + +You can see an example of how the `verify` function is called in the example zk voting application [here](https://github.com/noir-lang/noir-examples/blob/33e598c257e2402ea3a6b68dd4c5ad492bce1b0a/foundry-voting/src/zkVote.sol#L35): + +```solidity +function castVote(bytes calldata proof, uint proposalId, uint vote, bytes32 nullifierHash) public returns (bool) { + // ... + bytes32[] memory publicInputs = new bytes32[](4); + publicInputs[0] = merkleRoot; + publicInputs[1] = bytes32(proposalId); + publicInputs[2] = bytes32(vote); + publicInputs[3] = nullifierHash; + require(verifier.verify(proof, publicInputs), "Invalid proof"); +``` + +### Public Inputs + +:::tip + +A circuit doesn't have the concept of a return value. Return values are just syntactic sugar in +Noir. + +Under the hood, the return value is passed as an input to the circuit and is checked at the end of +the circuit program. + +::: + +The verifier contract uses the output (return) value of a Noir program as a public input. So if you +have the following function + +```rust +fn main( + // Public inputs + pubkey_x: pub Field, + pubkey_y: pub Field, + // Private inputs + priv_key: Field, +) -> pub Field +``` + +then `verify` in `plonk_vk.sol` will expect 3 public inputs. Passing two inputs will result in an +error like `Reason: PUBLIC_INPUT_COUNT_INVALID(3, 2)`. + +In this case the 3 inputs to `verify` would be ordered as `[pubkey_x, pubkey_y, return]`. + +#### Struct inputs + +Consider the following program: + +```rust +struct Type1 { + val1: Field, + val2: Field, +} + +struct Nested { + t1: Type1, + is_true: bool, +} + +fn main(x: pub Field, nested: pub Nested, y: pub Field) { + //... +} +``` + +Structs will be flattened so that the array of inputs is 1-dimensional array. The order of these inputs would be flattened to: `[x, nested.t1.val1, nested.t1.val2, nested.is_true, y]` + +## Noir for EVM chains + +You can currently deploy the Solidity verifier contracts to most EVM compatible chains. EVM chains that have been tested and are known to work include: + +- Optimism +- Arbitrum +- Polygon PoS +- Scroll +- Celo + +Other EVM chains should work, but have not been tested directly by our team. If you test any other chains, please open a PR on this page to update the list. See [this doc](https://github.com/noir-lang/noir-starter/tree/main/with-foundry#testing-on-chain) for more info about testing verifier contracts on different EVM chains. + +### Unsupported chains + +Unfortunately not all "EVM" chains are supported. + +**zkSync** and the **Polygon zkEVM** do _not_ currently support proof verification via Solidity verifier contracts. They are missing the bn256 precompile contract that the verifier contract requires. Once these chains support this precompile, they may work. diff --git a/docs/versioned_docs/version-0.17.0/nargo/04_language_server.md b/docs/versioned_docs/version-0.17.0/nargo/04_language_server.md new file mode 100644 index 00000000000..03e81044b9c --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/nargo/04_language_server.md @@ -0,0 +1,42 @@ +--- +title: Language Server +description: Learn about the Noir Language Server, how to install the components, and configuration that may be required. +keywords: [Nargo, Language Server, LSP, VSCode, Visual Studio Code] +--- + +This section helps you install and configure the Noir Language Server. + +The Language Server Protocol (LSP) has two components, the [Server](#language-server) and the [Client](#language-client). Below we describe each in the context of Noir. + +## Language Server + +The Server component is provided by the Nargo command line tool that you installed at the beginning of this guide. +As long as Nargo is installed and you've used it to run other commands in this guide, it should be good to go! + +If you'd like to verify that the `nargo lsp` command is available, you can run `nargo --help` and look for `lsp` in the list of commands. If you see it, you're using a version of Noir with LSP support. + +## Language Client + +The Client component is usually an editor plugin that launches the Server. It communicates LSP messages between the editor and the Server. For example, when you save a file, the Client will alert the Server, so it can try to compile the project and report any errors. + +Currently, Noir provides a Language Client for Visual Studio Code via the [vscode-noir](https://github.com/noir-lang/vscode-noir) extension. You can install it via the [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=noir-lang.vscode-noir). + +> **Note:** Noir's Language Server Protocol support currently assumes users' VSCode workspace root to be the same as users' Noir project root (i.e. where Nargo.toml lies). +> +> If LSP features seem to be missing / malfunctioning, make sure you are opening your Noir project directly (instead of as a sub-folder) in your VSCode instance. + +When your language server is running correctly and the VSCode plugin is installed, you should see handy codelens buttons for compilation, measuring circuit size, execution, and tests: + +![Compile and Execute](./../../../static/img/codelens_compile_execute.png) +![Run test](../../../static/img/codelens_run_test.png) + +You should also see your tests in the `testing` panel: + +![Testing panel](./../../../static/img/codelens_testing_panel.png) + +### Configuration + +- **Noir: Enable LSP** - If checked, the extension will launch the Language Server via `nargo lsp` and communicate with it. +- **Noir: Nargo Flags** - Additional flags may be specified if you require them to be added when the extension calls `nargo lsp`. +- **Noir: Nargo Path** - An absolute path to a Nargo binary with the `lsp` command. This may be useful if Nargo is not within the `PATH` of your editor. +- **Noir > Trace: Server** - Setting this to `"messages"` or `"verbose"` will log LSP messages between the Client and Server. Useful for debugging. diff --git a/docs/versioned_docs/version-0.17.0/noir_js/getting_started/01_tiny_noir_app.md b/docs/versioned_docs/version-0.17.0/noir_js/getting_started/01_tiny_noir_app.md new file mode 100644 index 00000000000..876d308d650 --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/noir_js/getting_started/01_tiny_noir_app.md @@ -0,0 +1,256 @@ +--- +title: End-to-end +description: Learn how to setup a new app that uses Noir to generate and verify zero-knowledge SNARK proofs in a typescript or javascript environment +keywords: [how to, guide, javascript, typescript, noir, barretenberg, zero-knowledge, proofs] +--- + +NoirJS works both on the browser and on the server, and works for both ESM and CJS module systems. In this page, we will learn how can we write a simple test and a simple web app to verify the standard Noir example. + +## Before we start + +:::note + +Feel free to use whatever versions, just keep in mind that Nargo and the NoirJS packages are meant to be in sync. For example, Nargo 0.18.x matches `noir_js@0.18.x`, etc. + +In this guide, we will be pinned to 0.17.0. + +::: + +Make sure you have Node installed on your machine by opening a terminal and executing `node --version`. If you don't see a version, you should install [node](https://github.com/nvm-sh/nvm). You can also use `yarn` if you prefer that package manager over npm (which comes with node). + +First of all, follow the the [Nargo guide](../../getting_started/00_nargo_installation.md) to install nargo version 0.17.0 and create a new project with `nargo new circuit`. Once there, `cd` into the `circuit` folder. You should then be able to compile your circuit into `json` format and see it inside the `target` folder: + +```bash +nargo compile +``` + +Your folder structure should look like: + +```tree +. +└── circuit + ├── Nargo.toml + ├── src + │ └── main.nr + └── target + └── circuit.json +``` + +## Starting a new project + +Go back to the previous folder and start a new project by running run `npm init`. You can configure your project or just leave the defaults, and see a `package.json` appear in your root folder. + +## Installing dependencies + +We'll need two `npm` packages. These packages will provide us the methods we need to run and verify proofs: + +```bash +npm i @noir-lang/backend_barretenberg@^0.17.0 @noir-lang/noir_js@^0.17.0 +``` + +To serve our page, we can use a build tool such as `vite`. Because we're gonna use some `wasm` files, we need to install a plugin as well. Run: + +```bash +npm i --save-dev vite rollup-plugin-copy +``` + +Since we're on the dependency world, we may as well define a nice starting script. Vite makes it easy. Just open `package.json`, find the block "scripts" and add this just below the line with `"test" : "echo......."`: + +```json + "start": "vite --open" +``` + +If you want do build a static website, you can also add some build and preview scripts: + +```json + "build": "vite build", + "preview": "vite preview" +``` + +## Vite plugins + +Vite is great, but support from `wasm` doesn't work out-of-the-box. We're gonna write a quick plugin and use another one. Just copy and paste this into a file named `vite.config.js`. You don't need to understand it, just trust me bro. + +```js +import { defineConfig } from 'vite'; +import copy from 'rollup-plugin-copy'; +import fs from 'fs'; +import path from 'path'; + +const wasmContentTypePlugin = { + name: 'wasm-content-type-plugin', + configureServer(server) { + server.middlewares.use(async (req, res, next) => { + if (req.url.endsWith('.wasm')) { + res.setHeader('Content-Type', 'application/wasm'); + const newPath = req.url.replace('deps', 'dist'); + const targetPath = path.join(__dirname, newPath); + const wasmContent = fs.readFileSync(targetPath); + return res.end(wasmContent); + } + next(); + }); + }, +}; + +export default defineConfig(({ command }) => { + if (command === 'serve') { + return { + plugins: [ + copy({ + targets: [{ src: 'node_modules/**/*.wasm', dest: 'node_modules/.vite/dist' }], + copySync: true, + hook: 'buildStart', + }), + command === 'serve' ? wasmContentTypePlugin : [], + ], + }; + } + + return {}; +}); +``` + +## HTML + +Here's the simplest HTML with some terrible UI. Create a file called `index.html` and paste this: + +```html + + + + + + +

Very basic Noir app

+
+

Logs

+

Proof

+
+ + +``` + +## Some good old vanilla Javascript + +Create a new file `app.js`, which is where our javascript code will live. Let's start with this code inside: + +```js +document.addEventListener('DOMContentLoaded', async () => { + // here's where the magic happens +}); + +function display(container, msg) { + const c = document.getElementById(container); + const p = document.createElement('p'); + p.textContent = msg; + c.appendChild(p); +} +``` + +We can manipulate our website with this little function, so we can see our website working. + +## Adding Noir + +If you come from the previous page, your folder structure should look like this: + +```tree +├── app.js +├── circuit +│ ├── Nargo.toml +│ ├── src +│ │ └── main.nr +│ └── target +│ └── circuit.json +├── index.html +├── package.json +└── vite.config.js +``` + +You'll see other files and folders showing up (like `package-lock.json`, `yarn.lock`, `node_modules`) but you shouldn't have to care about those. + +## Importing our dependencies + +We're starting with the good stuff now. At the top of the new javascript file, import the packages: + +```ts +import { BarretenbergBackend } from '@noir-lang/backend_barretenberg'; +import { Noir } from '@noir-lang/noir_js'; +``` + +We also need to import the `circuit` JSON file we created. If you have the suggested folder structure, you can add this line: + +```ts +import circuit from './circuit/target/circuit.json'; +``` + +## Write code + +:::note + +We're gonna be adding code inside the `document.addEventListener...etc` block: + +```js +// forget stuff here +document.addEventListener('DOMContentLoaded', async () => { + // here's where the magic happens +}); +// forget stuff here +``` + +::: + +Our dependencies exported two classes: `BarretenbergBackend` and `Noir`. Let's `init` them and add some logs, just to flex: + +```ts +const backend = new BarretenbergBackend(circuit); +const noir = new Noir(circuit, backend); +``` + +## Proving + +Now we're ready to prove stuff! Let's feed some inputs to our circuit and calculate the proof: + +```js +const input = { x: 1, y: 2 }; +display('logs', 'Generating proof... ⌛'); +const proof = await noir.generateFinalProof(input); +display('logs', 'Generating proof... ✅'); +display('results', proof.proof); +``` + +You're probably eager to see stuff happening, so go and run your app now! + +From your terminal, run `npm start` (or `yarn start`). If it doesn't open a browser for you, just visit `localhost:5173`. On a modern laptop, proof will generate in less than 100ms, and you'll see this: + +![Getting Started 0](./../../../../static/img/noir_getting_started_1.png) + +If you're human, you shouldn't be able to understand anything on the "proof" box. That's OK. We like you, human. + +In any case, this means your proof was generated! But you shouldn't trust me just yet. Add these lines to see it being verified: + +```js +display('logs', 'Verifying proof... ⌛'); +const verification = await noir.verifyFinalProof(proof); +if (verification) display('logs', 'Verifying proof... ✅'); +``` + +By saving, your app will refresh and here's our complete Tiny Noir App! + +## Further Reading + +You can see how noirjs is used in a full stack Next.js hardhat application in the [noir-starter repo here](https://github.com/noir-lang/noir-starter/tree/main/next-hardhat). The example shows how to calculate a proof in the browser and verify it with a deployed Solidity verifier contract from noirjs. + +You should also check out the more advanced examples in the [noir-examples repo](https://github.com/noir-lang/noir-examples), where you'll find reference usage for some cool apps. diff --git a/docs/versioned_docs/version-0.17.0/noir_js/noir_js.md b/docs/versioned_docs/version-0.17.0/noir_js/noir_js.md new file mode 100644 index 00000000000..23ea550e156 --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/noir_js/noir_js.md @@ -0,0 +1,36 @@ +--- +title: NoirJS +description: Interact with Noir in Typescript or Javascript +keywords: [Noir project, javascript, typescript, node.js, browser, react] +--- + +NoirJS is a TypeScript library that make it easy to use Noir on your dapp, webapp, Node.js server, website, etc. + +A typical workflow would be composed of two major elements: + +- NoirJS +- Proving backend of choice's JavaScript package + + + +To install NoirJS, install Node.js if you have not already and run this in your JavaScript project: + +```bash +npm i @noir-lang/noir_js +``` + +## Proving backend + +Since Noir is backend agnostic, you can instantiate NoirJS without any backend (i.e. to execute a function). But for proving, you would have to instantiate NoirJS with any of the supported backends through their own `js` interface. + +### Barretenberg + +Aztec Labs maintains the `barretenberg` proving backend, which you can instantiate and make use of alongside NoirJS. It is also the default proving backend installed and used with Nargo, the Noir CLI tool. + +To install its JavaScript library, run this in your project: + +```bash +npm i @noir-lang/backend_barretenberg +``` + +For more details on how to instantiate and use the libraries, refer to the [Full Noir App Guide](./getting_started/01_tiny_noir_app.md) and [Reference](./reference/01_noirjs.md) sections. diff --git a/docs/versioned_docs/version-0.17.0/noir_js/reference/01_noirjs.md b/docs/versioned_docs/version-0.17.0/noir_js/reference/01_noirjs.md new file mode 100644 index 00000000000..d9e5a0c6115 --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/noir_js/reference/01_noirjs.md @@ -0,0 +1,143 @@ +--- +title: Noir +description: Reference to noir_js library and the Noir class +keywords: [Noir project, javascript, typescript, node.js, browser, react, class, reference] +--- + +## Table of Contents + +- [constructor](#constructor) +- [init](#init) +- [generateFinalProof](#generatefinalproof) +- [verifyFinalProof](#verifyfinalproof) + +## `constructor` + +The `constructor` is a method used to create and initialize objects created within the `Noir` class. In the `Noir` class constructor, you need to pass two parameters: `circuit` and `backend`. + +### Syntax + +```js +constructor(circuit, backend); +``` + +### Parameters + +| Parameter | Type | Description | +| --------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `circuit` | Object | A circuit represented in a `json` format, containing the ABI and bytecode. Typically obtained by running [`nargo compile`](../../nargo/01_commands.md) | +| `backend` | Object | A backend instance, before initialization. | + +### Usage + +```js +const noir = new Noir(circuit, backend); +``` + +## `init` + +This async method should be called after class instantiation. It will run processes on the ACVM, instantiate your backend, etc. + +### Syntax + +```js +async init() +``` + +### Parameters + +This method takes no parameters + +### Usage + +```js +await noirInstance.init(); +``` + +## `execute` + +This async method allows to execute a circuit to get its witness and return value. [`generateFinalProof`](#generatefinalproof) calls it for you, but you can call it directly (i.e. to feed directly to a backend, or to get the return value). + +### Syntax + +```js +async execute(inputs) +``` + +### Parameters + +| Parameter | Type | Description | +| --------- | ------ | ------------------------------------------------ | +| `inputs` | Object | An object containing the inputs to your circuit. | + +### Returns + +| Return value | Type | Description | +| ------------ | --------------------- | --------------------------------------------------- | +| `witness` | Promise | The witness | +| `returnValue` | Promise | The return value | + +### Usage + +```js +const { witness, returnValue } = await noir.execute(inputs) +``` + +## `generateFinalProof` + +This async method generates a witness and a proof given an object as input. + +### Syntax + +```js +async generateFinalproof(input) +``` + +### Parameters + +| Parameter | Type | Description | +| --------- | ------ | ------------------------------------------------ | +| `input` | Object | An object containing the inputs to your circuit. | + +### Returns + +| Return value | Type | Description | +| ------------ | --------------------- | --------------------------------------------------- | +| `proof` | Promise | An array with the byte representation of the proof. | + +### Usage + +```js +// consider the Standard Noir Example given with nargo init +const input = { x: 1, y: 2 }; +noirInstance.generateProof(input); +``` + +## `verifyFinalProof` + +This async method instantiates the verification key and verifies your proof. + +### Syntax + +```js +async verifyFinalProof(proof) +``` + +### Parameters + +| Parameter | Type | Description | +| --------- | ---------- | --------------------------------------------------------------------------------------------- | +| `proof` | Uint8Array | The Uint8Array representation of your proof, usually obtained by calling `generateFinalProof` | + +### Returns + +| Return value | Type | Description | +| ------------ | ------------------ | -------------------------------------------- | +| `verified` | Promise | A boolean for whether the proof was verified | + +### Usage + +```js +const proof = noirInstance.generateProof(input); +noirInstance.verifyFinalProof(proof); +``` diff --git a/docs/versioned_docs/version-0.17.0/noir_js/reference/02_bb_backend.md b/docs/versioned_docs/version-0.17.0/noir_js/reference/02_bb_backend.md new file mode 100644 index 00000000000..21c2ff32b57 --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/noir_js/reference/02_bb_backend.md @@ -0,0 +1,272 @@ +--- +title: BarretenbergBackend +description: Reference documentation for the barretenberg_backend library and the BarretenbergBackend class +keywords: + [ + BarretenbergBackend, + Barretenberg, + javascript, + typescript, + node.js, + browser, + class, + reference, + noir_js, + ] +--- + +## Table of Contents + +- [constructor](#constructor) +- [generateFinalProof](#generatefinalproof) +- [generateIntermediateProof](#generateintermediateproof) +- [generateProof](#generateproof) +- [generateIntermediateProofArtifacts](#generateintermediateproofartifacts) +- [verifyFinalProof](#verifyfinalproof) +- [verifyIntermediateProof](#verifyintermediateproof) +- [verifyProof](#verifyproof) +- [destroy](#destroy) + +## `constructor` + +The `constructor` is a method used to create and initialize objects created within the `BarretenbergBackend` class. In this class, you should pass at least one argument for the `circuit`. + +### Syntax + +```js +constructor(acirCircuit, (numberOfThreads = 1)); +``` + +### Parameters + +| Parameter | Type | Description | +| ----------------- | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `acirCircuit` | Object | A circuit represented in a `json` format, containing the ABI and bytecode Tipically obtained by running [`nargo compile`](../../nargo/01_commands.md). This is the same circuit expected to be passed to [the Noir class](01_noirjs.md) | +| `numberOfThreads` | Number (optional) | The number of threads to be used by the backend. Defaults to 1. | + +### Usage + +```js +const backend = new BarretenbergBackend(acirCircuit); +``` + +## `generateFinalProof` + +An async wrapper around the [generateProof](#generateproof) method that passes a `false` flag. Usually called by the Noir class. + +### Syntax + +```js +async generateFinalProof(decompressedWitness) +``` + +### Parameters + +| Parameter | Type | Description | +| --------------------- | ------ | -------------------------------------------------------- | +| `decompressedWitness` | Object | The decompressed witness for generating the final proof. | + +### Returns + +| Return value | Type | Description | +| ------------ | -------------------- | --------------------------------------------------------- | +| `proof` | Promise | An array with the byte representation of the final proof. | + +### Usage + +```js +const finalProof = await backend.generateFinalProof(decompressedWitness); +``` + +## `generateIntermediateProof` + +An async wrapper around the [generateProof](#generateproof) method that passes a `true` flag. It's not currently being used by the Noir class, but developers can call this method directly to use Noir's recursive features. + +### Syntax + +```js +async generateIntermediateProof(witness) +``` + +### Parameters + +| Parameter | Type | Description | +| --------- | ------ | -------------------------------------------------- | +| `witness` | Object | The witness for generating the intermediate proof. | + +### Returns + +| Return value | Type | Description | +| ------------ | -------------------- | --------------------------------------------------------------- | +| `proof` | Promise | An array with the byte representation of the intermediate proof | + +### Usage + +```js +const intermediateProof = await backend.generateIntermediateProof(witness); +``` + +## `generateProof` + +This async method generates a proof. Takes the witness generated by ACVM, and a boolean that evaluates to `true` when the proof _is_ meant to be verified in another circuit. Not currently used by the Noir class. + +### Syntax + +```js +async generateProof(decompressedWitness, makeEasyToVerifyInCircuit) +``` + +### Parameters + +| Parameter | Type | Description | +| --------------------------- | ------- | ---------------------------------------------------------------------------------------------- | +| `decompressedWitness` | Object | The decompressed witness for generating the proof. | +| `makeEasyToVerifyInCircuit` | Boolean | A flag indicating whether to generate proof components for easy verification within a circuit. | + +### Returns + +| Return value | Type | Description | +| ------------ | -------------------- | -------------------------------------------------- | +| `proof` | Promise | An array with the byte representation of the proof | + +### Usage + +```js +const proof = await backend.generateProof(decompressedWitness, makeEasyToVerifyInCircuit); +``` + +## `generateIntermediateProofArtifacts` + +This async method returns the artifacts needed to verify the intermediate proof in another circuit. It's not currently being used by the Noir class, but developers can call this method directly to use Noir's recursive features. + +### Syntax + +```js +async generateIntermediateProofArtifacts(proof, numOfPublicInputs = 0) +``` + +### Parameters + +| Parameter | Type | Description | +| ------------------- | ----------------- | ---------------------------------------------------------------- | +| `proof` | Object | The proof object. | +| `numOfPublicInputs` | Number (optional) | The number of public inputs in the inner proof, defaulting to 0. | + +### Returns + +| Return value | Type | Description | +| --------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `proofAsFields` | string[] | An array of strings with the hexadecimal representation of the [Fields](../../language_concepts/data_types/00_fields.md) that make up a proof | +| `vkAsFields` | string[] | An array of strings with the hexadecimal representation of the [Fields](../../language_concepts/data_types/00_fields.md) that make up the verification key | +| `vkHash` | string | A pedersen hash of the verification key | + +### Usage + +```js +const artifacts = await backend.generateIntermediateProofArtifacts(proof, numOfPublicInputs); +``` + +## `verifyFinalProof` + +An async wrapper around [verifyProof](#verifyproof) that sets the `false` flag. Usually called by the Noir class. + +### Syntax + +```js +async verifyFinalProof(proof) +``` + +### Parameters + +| Parameter | Type | Description | +| --------- | ------ | --------------------------- | +| `proof` | Object | The proof object to verify. | + +### Returns + +| Return value | Type | Description | +| ------------ | ------------------ | -------------------------------------------- | +| `verified` | Promise | A boolean for whether the proof was verified | + +### Usage + +```js +const isValidFinal = await backend.verifyFinalProof(proof); +``` + +## `verifyIntermediateProof` + +An async wrapper around [verifyProof](#verifyproof) that sets the `true` flag. It's not currently being used by the Noir class, but developers can call this method directly to use Noir's recursive features. + +### Syntax + +```js +async verifyIntermediateProof(proof) +``` + +### Parameters + +| Parameter | Type | Description | +| --------- | ------ | ---------------------------------------- | +| `proof` | Object | The intermediate proof object to verify. | + +### Returns + +| Return value | Type | Description | +| ------------ | ------------------ | -------------------------------------------- | +| `verified` | Promise | A boolean for whether the proof was verified | + +### Usage + +```js +const isValidIntermediate = await backend.verifyIntermediateProof(proof); +``` + +## `verifyProof` + +This async method verifies a proof. Takes the proof, and a boolean that evaluates to `true` when the proof is intermediate. + +### Syntax + +```js +async verifyProof(proof, makeEasyToVerifyInCircuit) +``` + +### Parameters + +| Parameter | Type | Description | +| --------------------------- | ------- | ------------------------------------------------------------ | +| `proof` | Object | The proof object to verify | +| `makeEasyToVerifyInCircuit` | Boolean | A flag indicating whether the proof is intermediate or final | + +### Returns + +| Parameter | Type | Description | +| ---------- | ------------------ | -------------------------------------------- | +| `verified` | Promise\ | A boolean for whether the proof was verified | + +### Usage + +```js +const isValid = await backend.verifyProof(proof, makeEasyToVerifyInCircuit); +``` + +## `destroy` + +This method destroys the resources allocated in the [instantiate](#instantiate) method. Noir doesn't currently call this method, but it's highly recommended that developers do so in order to save resources. + +### Syntax + +```js +async destroy() +``` + +### Parameters + +This method takes no parameters. + +### Usage + +```js +await backend.destroy(); +``` diff --git a/docs/versioned_docs/version-0.17.0/standard_library/black_box_fns.md b/docs/versioned_docs/version-0.17.0/standard_library/black_box_fns.md new file mode 100644 index 00000000000..c758846b688 --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/standard_library/black_box_fns.md @@ -0,0 +1,45 @@ +--- +title: Black Box Functions +description: Black box functions are functions in Noir that rely on backends implementing support for specialized constraints. +keywords: [noir, black box functions] +--- + +Black box functions are functions in Noir that rely on backends implementing support for specialized constraints. This makes certain zk-snark unfriendly computations cheaper than if they were implemented in Noir. + +:::warning + +It is likely that not all backends will support a particular black box function. + +::: + +Because it is not guaranteed that all backends will support black box functions, it is possible that certain Noir programs won't compile against a particular backend if they use an unsupported black box function. It is possible to fallback to less efficient implementations written in Noir/ACIR in some cases. + +Black box functions are specified with the `#[foreign(black_box_fn)]` attribute. For example, the SHA256 function in the Noir [source code](https://github.com/noir-lang/noir/blob/v0.5.1/noir_stdlib/src/hash.nr) looks like: + +```rust +#[foreign(sha256)] +fn sha256(_input : [u8; N]) -> [u8; 32] {} +``` + +## Function list + +Here is a list of the current black box functions that are supported by UltraPlonk: + +- AES +- [SHA256](./cryptographic_primitives/hashes#sha256) +- [Schnorr signature verification](./cryptographic_primitives/schnorr) +- [Blake2s](./cryptographic_primitives/hashes#blake2s) +- [Pedersen](./cryptographic_primitives/hashes#pedersen) +- [HashToField128Security](./cryptographic_primitives/hashes#hash_to_field) +- [ECDSA signature verification](./cryptographic_primitives/ecdsa_sig_verification) +- [Fixed base scalar multiplication](./cryptographic_primitives/scalar) +- [Compute merkle root](./merkle_trees#compute_merkle_root) +- AND +- XOR +- RANGE +- [Keccak256](./cryptographic_primitives/hashes#keccak256) +- [Recursive proof verification](./recursion) + +Most black box functions are included as part of the Noir standard library, however `AND`, `XOR` and `RANGE` are used as part of the Noir language syntax. For instance, using the bitwise operator `&` will invoke the `AND` black box function. To ensure compatibility across backends, the ACVM has fallback implementations of `AND`, `XOR` and `RANGE` defined in its standard library which it can seamlessly fallback to if the backend doesn't support them. + +You can view the black box functions defined in the ACVM code [here](https://github.com/noir-lang/acvm/blob/acir-v0.12.0/acir/src/circuit/black_box_functions.rs). diff --git a/docs/versioned_docs/version-0.17.0/standard_library/cryptographic_primitives.md b/docs/versioned_docs/version-0.17.0/standard_library/cryptographic_primitives.md new file mode 100644 index 00000000000..2df4f929474 --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/standard_library/cryptographic_primitives.md @@ -0,0 +1,14 @@ +--- +title: Cryptographic primitives in Noir +description: + Learn about the cryptographic primitives ready to use for any Noir project +keywords: + [ + cryptographic primitives, + Noir project, + ] +--- + +The Noir team is progressively adding new cryptographic primitives to the standard library. Reach out for news or if you would be interested in adding more of these calculations in Noir. + +Some methods are available thanks to the Aztec backend, not being performed using Noir. When using other backends, these methods may or may not be supplied. diff --git a/docs/versioned_docs/version-0.17.0/standard_library/cryptographic_primitives/00_hashes.mdx b/docs/versioned_docs/version-0.17.0/standard_library/cryptographic_primitives/00_hashes.mdx new file mode 100644 index 00000000000..be780125ac5 --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/standard_library/cryptographic_primitives/00_hashes.mdx @@ -0,0 +1,146 @@ +--- +title: Hash methods +description: + Learn about the cryptographic primitives ready to use for any Noir project, including sha256, + blake2s, pedersen, mimc_bn254 and mimc +keywords: + [cryptographic primitives, Noir project, sha256, blake2s, pedersen, mimc_bn254, mimc, hash] +--- + +import BlackBoxInfo from '../../../../src/components/Notes/_blackbox.mdx'; + +## sha256 + +Given an array of bytes, returns the resulting sha256 hash. + +```rust +fn sha256(_input : [u8]) -> [u8; 32] +``` + +example: + +```rust +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let hash = std::hash::sha256(x); +} +``` + + + +## blake2s + +Given an array of bytes, returns an array with the Blake2 hash + +```rust +fn blake2s(_input : [u8]) -> [u8; 32] +``` + +example: + +```rust +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let hash = std::hash::blake2s(x); +} +``` + + + +## pedersen + +Given an array of Fields, returns the Pedersen hash. + +```rust +fn pedersen(_input : [Field]) -> [Field; 2] +``` + +example: + +```rust +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let hash = std::hash::pedersen(x); +} +``` + + + +## keccak256 + +Given an array of bytes (`u8`), returns the resulting keccak hash as an array of 32 bytes +(`[u8; 32]`). Specify a message_size to hash only the first `message_size` bytes +of the input. + +```rust +fn keccak256(_input : [u8; N], _message_size: u32) -> [u8; 32] +``` + +example: + +```rust +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let message_size = 4; + let hash = std::hash::keccak256(x, message_size); +} +``` + + + +## poseidon + +Given an array of Fields, returns a new Field with the Poseidon Hash. Mind that you need to specify +how many inputs are there to your Poseidon function. + +```rust +// example for hash_1, hash_2 accepts an array of length 2, etc +fn hash_1(input: [Field; 1]) -> Field +``` + +example: + +```rust +fn main() +{ + let hash1 = std::hash::poseidon::bn254::hash_2([1, 2]); + assert(hash1 == 0x115cc0f5e7d690413df64c6b9662e9cf2a3617f2743245519e19607a4417189a); +} +``` + +## mimc_bn254 and mimc + +`mimc_bn254` is `mimc`, but with hardcoded parameters for the BN254 curve. You can use it by +providing an array of Fields, and it returns a Field with the hash. You can use the `mimc` method if +you're willing to input your own constants: + +```rust +fn mimc(x: Field, k: Field, constants: [Field; N], exp : Field) -> Field +``` + +otherwise, use the `mimc_bn254` method: + +```rust +fn mimc_bn254(array: [Field; N]) -> Field +``` + +example: + +```rust + +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let hash = std::hash::mimc_bn254(x); +} +``` + +## hash_to_field + +```rust +fn hash_to_field(_input : [Field; N]) -> Field {} +``` + +Calculates the `blake2s` hash of the inputs and returns the hash modulo the field modulus to return +a value which can be represented as a `Field`. + + diff --git a/docs/versioned_docs/version-0.17.0/standard_library/cryptographic_primitives/01_scalar.mdx b/docs/versioned_docs/version-0.17.0/standard_library/cryptographic_primitives/01_scalar.mdx new file mode 100644 index 00000000000..f48d45a5816 --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/standard_library/cryptographic_primitives/01_scalar.mdx @@ -0,0 +1,27 @@ +--- +title: Scalar multiplication +description: See how you can perform scalar multiplications over a fixed base in Noir +keywords: [cryptographic primitives, Noir project, scalar multiplication] +--- + +import BlackBoxInfo from '../../../../src/components/Notes/_blackbox.mdx'; + +## scalar_mul::fixed_base_embedded_curve + +Performs scalar multiplication over the embedded curve whose coordinates are defined by the +configured noir field. For the BN254 scalar field, this is BabyJubJub or Grumpkin. + +```rust +fn fixed_base_embedded_curve(_input : Field) -> [Field; 2] +``` + +example + +```rust +fn main(x : Field) { + let scal = std::scalar_mul::fixed_base_embedded_curve(x); + std::println(scal); +} +``` + + diff --git a/docs/versioned_docs/version-0.17.0/standard_library/cryptographic_primitives/02_schnorr.mdx b/docs/versioned_docs/version-0.17.0/standard_library/cryptographic_primitives/02_schnorr.mdx new file mode 100644 index 00000000000..61770d71cd8 --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/standard_library/cryptographic_primitives/02_schnorr.mdx @@ -0,0 +1,37 @@ +--- +title: Schnorr Signatures +description: Learn how you can verify Schnorr signatures using Noir +keywords: [cryptographic primitives, Noir project, schnorr, signatures] +--- + +import BlackBoxInfo from '../../../../src/components/Notes/_blackbox.mdx'; + +## schnorr::verify_signature + +Verifier for Schnorr signatures over the embedded curve (for BN254 it is Grumpkin). + +```rust +fn verify_signature(_public_key_x: Field, _public_key_y: Field, _signature: [u8; 64], _message: [u8]) -> bool +``` + +where `_signature` can be generated like so using the npm package +[@noir-lang/barretenberg](https://www.npmjs.com/package/@noir-lang/barretenberg) + +```js +const { BarretenbergWasm } = require('@noir-lang/barretenberg/dest/wasm'); +const { Schnorr } = require('@noir-lang/barretenberg/dest/crypto/schnorr'); + +... + +const barretenberg = await BarretenbergWasm.new(); +const schnorr = new Schnorr(barretenberg); +const pubKey = schnorr.computePublicKey(privateKey); +const message = ... +const signature = Array.from( + schnorr.constructSignature(hash, privateKey).toBuffer() +); + +... +``` + + diff --git a/docs/versioned_docs/version-0.17.0/standard_library/cryptographic_primitives/03_ecdsa_sig_verification.mdx b/docs/versioned_docs/version-0.17.0/standard_library/cryptographic_primitives/03_ecdsa_sig_verification.mdx new file mode 100644 index 00000000000..1a2f2ef13e2 --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/standard_library/cryptographic_primitives/03_ecdsa_sig_verification.mdx @@ -0,0 +1,45 @@ +--- +title: ECDSA Signature Verification +description: Learn about the cryptographic primitives regarding ECDSA over the secp256k1 and secp256r1 curves +keywords: [cryptographic primitives, Noir project, ecdsa, secp256k1, secp256r1, signatures] +--- + +import BlackBoxInfo from '../../../../src/components/Notes/_blackbox.mdx'; + +Noir supports ECDSA signatures verification over the secp256k1 and secp256r1 curves. + +## ecdsa_secp256k1::verify_signature + +Verifier for ECDSA Secp256k1 signatures + +```rust +fn verify_signature(_public_key_x : [u8; 32], _public_key_y : [u8; 32], _signature: [u8; 64], _message: [u8]) -> bool +``` + +example: + +```rust +fn main(hashed_message : [u8;32], pub_key_x : [u8;32], pub_key_y : [u8;32], signature : [u8;64]) { + let valid_signature = std::ecdsa_secp256k1::verify_signature(pub_key_x, pub_key_y, signature, hashed_message); + assert(valid_signature); +} +``` + +## ecdsa_secp256r1::verify_signature + +Verifier for ECDSA Secp256r1 signatures + +```rust +fn verify_signature(_public_key_x : [u8; 32], _public_key_y : [u8; 32], _signature: [u8; 64], _message: [u8]) -> bool +``` + +example: + +```rust +fn main(hashed_message : [u8;32], pub_key_x : [u8;32], pub_key_y : [u8;32], signature : [u8;64]) { + let valid_signature = std::ecdsa_secp256r1::verify_signature(pub_key_x, pub_key_y, signature, hashed_message); + assert(valid_signature); +} +``` + + diff --git a/docs/versioned_docs/version-0.17.0/standard_library/cryptographic_primitives/04_ec_primitives.md b/docs/versioned_docs/version-0.17.0/standard_library/cryptographic_primitives/04_ec_primitives.md new file mode 100644 index 00000000000..6e6b19b6861 --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/standard_library/cryptographic_primitives/04_ec_primitives.md @@ -0,0 +1,101 @@ +--- +title: Elliptic Curve Primitives +keywords: [cryptographic primitives, Noir project] +--- + +Data structures and methods on them that allow you to carry out computations involving elliptic +curves over the (mathematical) field corresponding to `Field`. For the field currently at our +disposal, applications would involve a curve embedded in BN254, e.g. the +[Baby Jubjub curve](https://eips.ethereum.org/EIPS/eip-2494). + +## Data structures + +### Elliptic curve configurations + +(`std::ec::{tecurve,montcurve,swcurve}::{affine,curvegroup}::Curve`), i.e. the specific elliptic +curve you want to use, which would be specified using any one of the methods +`std::ec::{tecurve,montcurve,swcurve}::{affine,curvegroup}::new` which take the coefficients in the +defining equation together with a generator point as parameters. You can find more detail in the +comments in +[`noir_stdlib/src/ec.nr`](https://github.com/noir-lang/noir/blob/master/noir_stdlib/src/ec.nr), but +the gist of it is that the elliptic curves of interest are usually expressed in one of the standard +forms implemented here (Twisted Edwards, Montgomery and Short Weierstraß), and in addition to that, +you could choose to use `affine` coordinates (Cartesian coordinates - the usual (x,y) - possibly +together with a point at infinity) or `curvegroup` coordinates (some form of projective coordinates +requiring more coordinates but allowing for more efficient implementations of elliptic curve +operations). Conversions between all of these forms are provided, and under the hood these +conversions are done whenever an operation is more efficient in a different representation (or a +mixed coordinate representation is employed). + +### Points + +(`std::ec::{tecurve,montcurve,swcurve}::{affine,curvegroup}::Point`), i.e. points lying on the +elliptic curve. For a curve configuration `c` and a point `p`, it may be checked that `p` +does indeed lie on `c` by calling `c.contains(p1)`. + +## Methods + +(given a choice of curve representation, e.g. use `std::ec::tecurve::affine::Curve` and use +`std::ec::tecurve::affine::Point`) + +- The **zero element** is given by `Point::zero()`, and we can verify whether a point `p: Point` is + zero by calling `p.is_zero()`. +- **Equality**: Points `p1: Point` and `p2: Point` may be checked for equality by calling + `p1.eq(p2)`. +- **Addition**: For `c: Curve` and points `p1: Point` and `p2: Point` on the curve, adding these two + points is accomplished by calling `c.add(p1,p2)`. +- **Negation**: For a point `p: Point`, `p.negate()` is its negation. +- **Subtraction**: For `c` and `p1`, `p2` as above, subtracting `p2` from `p1` is accomplished by + calling `c.subtract(p1,p2)`. +- **Scalar multiplication**: For `c` as above, `p: Point` a point on the curve and `n: Field`, + scalar multiplication is given by `c.mul(n,p)`. If instead `n :: [u1; N]`, i.e. `n` is a bit + array, the `bit_mul` method may be used instead: `c.bit_mul(n,p)` +- **Multi-scalar multiplication**: For `c` as above and arrays `n: [Field; N]` and `p: [Point; N]`, + multi-scalar multiplication is given by `c.msm(n,p)`. +- **Coordinate representation conversions**: The `into_group` method converts a point or curve + configuration in the affine representation to one in the CurveGroup representation, and + `into_affine` goes in the other direction. +- **Curve representation conversions**: `tecurve` and `montcurve` curves and points are equivalent + and may be converted between one another by calling `into_montcurve` or `into_tecurve` on their + configurations or points. `swcurve` is more general and a curve c of one of the other two types + may be converted to this representation by calling `c.into_swcurve()`, whereas a point `p` lying + on the curve given by `c` may be mapped to its corresponding `swcurve` point by calling + `c.map_into_swcurve(p)`. +- **Map-to-curve methods**: The Elligator 2 method of mapping a field element `n: Field` into a + `tecurve` or `montcurve` with configuration `c` may be called as `c.elligator2_map(n)`. For all of + the curve configurations, the SWU map-to-curve method may be called as `c.swu_map(z,n)`, where + `z: Field` depends on `Field` and `c` and must be chosen by the user (the conditions it needs to + satisfy are specified in the comments + [here](https://github.com/noir-lang/noir/blob/master/noir_stdlib/src/ec.nr)). + +## Examples + +The +[ec_baby_jubjub test](https://github.com/noir-lang/noir/blob/master/crates/nargo_cli/tests/test_data/ec_baby_jubjub/src/main.nr) +illustrates all of the above primitives on various forms of the Baby Jubjub curve. A couple of more +interesting examples in Noir would be: + +Public-key cryptography: Given an elliptic curve and a 'base point' on it, determine the public key +from the private key. This is a matter of using scalar multiplication. In the case of Baby Jubjub, +for example, this code would do: + +```rust +use dep::std::ec::tecurve::affine::{Curve, Point}; + +fn bjj_pub_key(priv_key: Field) -> Point +{ + + let bjj = Curve::new(168700, 168696, G::new(995203441582195749578291179787384436505546430278305826713579947235728471134,5472060717959818805561601436314318772137091100104008585924551046643952123905)); + + let base_pt = Point::new(5299619240641551281634865583518297030282874472190772894086521144482721001553, 16950150798460657717958625567821834550301663161624707787222815936182638968203); + + bjj.mul(priv_key,base_pt) +} +``` + +This would come in handy in a Merkle proof. + +- EdDSA signature verification: This is a matter of combining these primitives with a suitable hash + function. See + [feat(stdlib): EdDSA sig verification noir#1136](https://github.com/noir-lang/noir/pull/1136) for + the case of Baby Jubjub and the Poseidon hash function. diff --git a/docs/versioned_docs/version-0.17.0/standard_library/cryptographic_primitives/05_eddsa.mdx b/docs/versioned_docs/version-0.17.0/standard_library/cryptographic_primitives/05_eddsa.mdx new file mode 100644 index 00000000000..49df8796105 --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/standard_library/cryptographic_primitives/05_eddsa.mdx @@ -0,0 +1,17 @@ +--- +title: EdDSA Verification +description: Learn about the cryptographic primitives regarding EdDSA +keywords: [cryptographic primitives, Noir project, eddsa, signatures] +--- + +import BlackBoxInfo from '../../../../src/components/Notes/_blackbox.mdx'; + +## eddsa::eddsa_poseidon_verify + +Verifier for EdDSA signatures + +```rust +fn eddsa_poseidon_verify(public_key_x : Field, public_key_y : Field, signature_s: Field, signature_r8_x: Field, signature_r8_y: Field, message: Field) -> bool +``` + + diff --git a/docs/versioned_docs/version-0.17.0/standard_library/logging.md b/docs/versioned_docs/version-0.17.0/standard_library/logging.md new file mode 100644 index 00000000000..7e2fd9b9aff --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/standard_library/logging.md @@ -0,0 +1,62 @@ +--- +title: Logging +description: + Learn how to use the println statement for debugging in Noir with this tutorial. Understand the + basics of logging in Noir and how to implement it in your code. +keywords: + [ + noir logging, + println statement, + debugging in noir, + noir std library, + logging tutorial, + basic logging in noir, + noir logging implementation, + noir debugging techniques, + rust, + ] +--- + +The standard library provides a familiar `println` statement you can use. Despite being a limited +implementation of rust's `println!` macro, this construct can be useful for debugging. + +You can print the output of println statements in your Noir code by using the `nargo execute` command or the `--show-output` flag when using `nargo test` (provided there are println statements in your tests). + +It is recommended to use `nargo execute` if you want to debug failing constrains with `println` statements. This is due to every input in a test being a constant rather than a witness, so we issue an error during compilation while we only print during execution (which comes after compilation). `println` will not work for failed constraints caught at compile time. + +The `println` statement is unconstrained, so it works for outputting integers, fields, strings, and even structs or expressions. For example: + +```rust +use dep::std; + +struct Person { + age : Field, + height : Field, +} + +fn main(age : Field, height : Field) { + let person = Person { age : age, height : height }; + std::println(person); + std::println(age + height); + std::println("Hello world!"); +} + +``` + +You can print multiple different types in the same statement and string as well as a new "fmtstr" type. A `fmtstr` can be specified in the same way as a normal string it just should be prepended with an "f" character: + +```rust + let fmt_str = f"i: {i}, j: {j}"; + std::println(fmt_str); + + let s = myStruct { y: x, x: y }; + std::println(s); + + std::println(f"i: {i}, s: {s}"); + + std::println(x); + std::println([x, y]); + + let foo = fooStruct { my_struct: s, foo: 15 }; + std::println(f"s: {s}, foo: {foo}"); +``` diff --git a/docs/versioned_docs/version-0.17.0/standard_library/merkle_trees.md b/docs/versioned_docs/version-0.17.0/standard_library/merkle_trees.md new file mode 100644 index 00000000000..9761105f4f2 --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/standard_library/merkle_trees.md @@ -0,0 +1,58 @@ +--- +title: Merkle Trees +description: Learn about Merkle Trees in Noir with this tutorial. Explore the basics of computing a merkle root using a proof, with examples. +keywords: + [ + Merkle trees in Noir, + Noir programming language, + check membership, + computing root from leaf, + Noir Merkle tree implementation, + Merkle tree tutorial, + Merkle tree code examples, + Noir libraries, + pedersen hash., + ] +--- + +## compute_merkle_root + +Returns the root of the tree from the provided leaf and its hash path, using a [Pedersen hash](cryptographic_primitives/00_hashes.mdx#pedersen). + +```rust +fn compute_merkle_root(leaf : Field, index : Field, hash_path: [Field]) -> Field +``` + +example: + +```rust +/** + // these values are for this example only + index = "0" + priv_key = "0x000000000000000000000000000000000000000000000000000000616c696365" + secret = "0x1929ea3ab8d9106a899386883d9428f8256cfedb3c4f6b66bf4aa4d28a79988f" + note_hash_path = [ + "0x1e61bdae0f027b1b2159e1f9d3f8d00fa668a952dddd822fda80dc745d6f65cc", + "0x0e4223f3925f98934393c74975142bd73079ab0621f4ee133cee050a3c194f1a", + "0x2fd7bb412155bf8693a3bd2a3e7581a679c95c68a052f835dddca85fa1569a40" + ] + */ +fn main(index: Field, priv_key: Field, secret: Field, note_hash_path: [Field; 3]) { + + let pubkey = std::scalar_mul::fixed_base_embedded_curve(priv_key); + let pubkey_x = pubkey[0]; + let pubkey_y = pubkey[1]; + let note_commitment = std::hash::pedersen([pubkey_x, pubkey_y, secret]); + + let root = std::merkle::compute_merkle_root(note_commitment[0], index, note_hash_path); + std::println(root); +} +``` + +To check merkle tree membership: + +1. Include a merkle root as a program input. +2. Compute the merkle root of a given leaf, index and hash path. +3. Assert the merkle roots are equal. + +For more info about merkle trees, see the Wikipedia [page](https://en.wikipedia.org/wiki/Merkle_tree). diff --git a/docs/versioned_docs/version-0.17.0/standard_library/options.md b/docs/versioned_docs/version-0.17.0/standard_library/options.md new file mode 100644 index 00000000000..3d3139fb98b --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/standard_library/options.md @@ -0,0 +1,99 @@ +--- +title: Option Type +--- + +The `Option` type is a way to express that a value might be present (`Some(T))` or absent (`None`). It's a safer way to handle potential absence of values, compared to using nulls in many other languages. + +```rust +struct Option { + None, + Some(T), +} +``` + +You can import the Option type into your Noir program like so: + +```rust +use dep::std::option::Option; + +fn main() { + let none = Option::none(); + let some = Option::some(3); +} +``` + +See [this test](https://github.com/noir-lang/noir/blob/5cbfb9c4a06c8865c98ff2b594464b037d821a5c/crates/nargo_cli/tests/test_data/option/src/main.nr) for a more comprehensive set of examples of each of the methods described below. + +## Methods + +### none + +Constructs a none value. + +### some + +Constructs a some wrapper around a given value. + +### is_none + +Returns true if the Option is None. + +### is_some + +Returns true of the Option is Some. + +### unwrap + +Asserts `self.is_some()` and returns the wrapped value. + +### unwrap_unchecked + +Returns the inner value without asserting `self.is_some()`. This method can be useful within an if condition when we already know that `option.is_some()`. If the option is None, there is no guarantee what value will be returned, only that it will be of type T for an `Option`. + +### unwrap_or + +Returns the wrapped value if `self.is_some()`. Otherwise, returns the given default value. + +### unwrap_or_else + +Returns the wrapped value if `self.is_some()`. Otherwise, calls the given function to return a default value. + +### map + +If self is `Some(x)`, this returns `Some(f(x))`. Otherwise, this returns `None`. + +### map_or + +If self is `Some(x)`, this returns `f(x)`. Otherwise, this returns the given default value. + +### map_or_else + +If self is `Some(x)`, this returns `f(x)`. Otherwise, this returns `default()`. + +### and + +Returns None if self is None. Otherwise, this returns `other`. + +### and_then + +If self is None, this returns None. Otherwise, this calls the given function with the Some value contained within self, and returns the result of that call. In some languages this function is called `flat_map` or `bind`. + +### or + +If self is Some, return self. Otherwise, return `other`. + +### or_else + +If self is Some, return self. Otherwise, return `default()`. + +### xor + +If only one of the two Options is Some, return that option. Otherwise, if both options are Some or both are None, None is returned. + +### filter + +Returns `Some(x)` if self is `Some(x)` and `predicate(x)` is true. Otherwise, this returns `None`. + +### flatten + +Flattens an `Option>` into a `Option`. This returns `None` if the outer Option is None. Otherwise, this returns the inner Option. diff --git a/docs/versioned_docs/version-0.17.0/standard_library/recursion.md b/docs/versioned_docs/version-0.17.0/standard_library/recursion.md new file mode 100644 index 00000000000..ff4c63acaa7 --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/standard_library/recursion.md @@ -0,0 +1,96 @@ +--- +title: Recursive Proofs +description: Learn about how to write recursive proofs in Noir. +keywords: [recursion, recursive proofs, verification_key, aggregation object, verify_proof] +--- + +Noir supports recursively verifying proofs, meaning you verify the proof of a Noir program in another Noir program. This enables creating proofs of arbitrary size by doing step-wise verification of smaller components of a large proof. + +The `verify_proof` function takes a verification key, proof and public inputs for a zk program, as well as a key hash and an input aggregation object. The key hash is used to check the validity of the verification key and the input aggregation object is required by some proving systems. The `verify_proof` function returns an output aggregation object that can then be fed into future iterations of the proof verification if required. + +```rust +#[foreign(verify_proof)] +fn verify_proof(_verification_key : [Field], _proof : [Field], _public_input : Field, _key_hash : Field, _input_aggregation_object : [Field]) -> [Field] {} +``` + +:::info + +This is a black box function. Read [this section](./black_box_fns) to learn more about black box functions in Noir. + +::: + +## Aggregation Object + +The purpose of the input aggregation object is a little less clear though (and the output aggregation object that is returned from the `std::verify_proof` method). Recursive zkSNARK schemes do not necessarily "verify a proof" in the sense that you expect a true or false to be spit out by the verifier. Rather an aggregation object is built over the public inputs. In the case of PLONK the recursive aggregation object is two G1 points (expressed as 16 witness values). The final verifier (in our case this is most often the smart contract verifier) has to be aware of this aggregation object to execute a pairing and check the validity of these points (thus completing the recursive verification). + +So for example in this circuit: + +```rust +use dep::std; + +fn main( + verification_key : [Field; 114], + proof : [Field; 94], + public_inputs : [Field; 1], + key_hash : Field, + input_aggregation_object : [Field; 16], + proof_b : [Field; 94], +) -> pub [Field; 16] { + let output_aggregation_object_a = std::verify_proof( + verification_key, + proof, + public_inputs, + key_hash, + input_aggregation_object + ); + + let output_aggregation_object = std::verify_proof( + verification_key, + proof_b, + public_inputs, + key_hash, + output_aggregation_object_a + ); + + let mut output = [0; 16]; + for i in 0..16 { + output[i] = output_aggregation_object[i]; + } + output +} +``` + +In this example we have a circuit, that generates proofs A and B, that is being verified in circuit C. Assuming that the proof being passed in is not already a recursive proof, the `input_aggregation_object` will be all zeros. It will then generate an `output_aggregation_object`. This blob of data then becomes the `input_aggregation_object` of the next recursive aggregation we wish to compute. We can see here as the same public inputs, verification key, and key hash are used that we are verifying two proofs generated from the same circuit in this single circuit. `std::verify_proof` returns a `[Field]` because the size of an aggregation object is proof system dependent--in barretenberg, aggregation objects are two G1 points, while in Halo2, the aggregation object is a list of G1 points that is log the circuit size. So for the final step we convert the slice into an array of size 16 because we are generating proofs using UltraPlonk. + +## Parameters + +### `verification_key` + +The verification key for the zk program that is being verified. + +### `proof` + +The proof for the zk program that is being verified. + +### `public_inputs` + +These represent the public inputs of the proof we are verifying. They should be checked against in the circuit after construction of a new aggregation state. + +### `key_hash` + +A key hash is used to check the validity of the verification key. The circuit implementing this opcode can use this hash to ensure that the key provided to the circuit matches the key produced by the circuit creator. + +### `input_aggregation_object` + +An aggregation object is blob of data that the top-level verifier must run some proof system specific algorithm on to complete verification. The size is proof system specific and will be set by the backend integrating this opcode. The input aggregation object is only not `None` when we are verifying a previous recursive aggregation in the current circuit. If this is the first recursive aggregation there is no input aggregation object. It is left to the backend to determine how to handle when there is no input aggregation object. + +## Return value + +### `output_aggregation_object` + +This is the result of a recursive aggregation and is what will be fed into the next verifier. +The next verifier can either perform a final verification (returning true or false) or perform another recursive aggregation where this output aggregation object will be the input aggregation object of the next recursive aggregation. + +## Example + +You can see an example of how to do recursive proofs in [this example recursion demo repo](https://github.com/noir-lang/noir-examples/tree/master/recursion). diff --git a/docs/versioned_docs/version-0.17.0/standard_library/zeroed.md b/docs/versioned_docs/version-0.17.0/standard_library/zeroed.md new file mode 100644 index 00000000000..97dab02dac2 --- /dev/null +++ b/docs/versioned_docs/version-0.17.0/standard_library/zeroed.md @@ -0,0 +1,25 @@ +--- +title: Zeroed Function +description: + The zeroed function returns a zeroed value of any type. +keywords: + [ + zeroed + ] +--- + +Implements `fn zeroed() -> T` to return a zeroed value of any type. This function is generally unsafe to use as the zeroed bit pattern is not guaranteed to be valid for all types. It can however, be useful in cases when the value is guaranteed not to be used such as in a BoundedVec library implementing a growable vector, up to a certain length, backed by an array. The array can be initialized with zeroed values which are guaranteed to be inaccessible until the vector is pushed to. Similarly, enumerations in noir can be implemented using this method by providing zeroed values for the unused variants. + +You can access the function at `std::unsafe::zeroed`. + +This function currently supports the following types: + +- Field +- Bool +- Uint +- Array +- String +- Tuple +- Function + +Using it on other types could result in unexpected behavior. diff --git a/docs/versioned_sidebars/version-0.17.0-sidebars.json b/docs/versioned_sidebars/version-0.17.0-sidebars.json new file mode 100644 index 00000000000..a9ec39925d9 --- /dev/null +++ b/docs/versioned_sidebars/version-0.17.0-sidebars.json @@ -0,0 +1,141 @@ +{ + "sidebar": [ + { + "type": "doc", + "id": "index", + "label": "Noir" + }, + { + "type": "category", + "label": "Getting Started", + "items": [ + { + "type": "autogenerated", + "dirName": "getting_started" + } + ] + }, + { + "type": "category", + "label": "Examples", + "items": [ + { + "type": "autogenerated", + "dirName": "examples" + } + ] + }, + { + "type": "category", + "label": "Nargo", + "items": [ + { + "type": "autogenerated", + "dirName": "nargo" + } + ] + }, + { + "type": "category", + "label": "Language Concepts", + "items": [ + { + "type": "category", + "label": "Data Types", + "link": { + "type": "doc", + "id": "language_concepts/data_types" + }, + "items": [ + { + "type": "autogenerated", + "dirName": "language_concepts/data_types" + } + ] + }, + "language_concepts/functions", + "language_concepts/control_flow", + "language_concepts/ops", + "language_concepts/assert", + "language_concepts/unconstrained", + "language_concepts/generics", + "language_concepts/mutability", + "language_concepts/lambdas", + "language_concepts/comments", + "language_concepts/distinct", + "language_concepts/shadowing" + ] + }, + { + "type": "category", + "label": "Noir Standard Library", + "items": [ + { + "type": "category", + "label": "Cryptographic Primitives", + "link": { + "type": "doc", + "id": "standard_library/cryptographic_primitives" + }, + "items": [ + { + "type": "autogenerated", + "dirName": "standard_library/cryptographic_primitives" + } + ] + }, + "standard_library/recursion", + "standard_library/logging", + "standard_library/merkle_trees", + "standard_library/zeroed", + "standard_library/black_box_fns", + "standard_library/options" + ] + }, + { + "type": "category", + "label": "Modules, Packages and Crates", + "items": [ + { + "type": "autogenerated", + "dirName": "modules_packages_crates" + } + ] + }, + { + "type": "category", + "label": "NoirJS", + "link": { + "type": "doc", + "id": "noir_js/noir_js" + }, + "items": [ + { + "type": "category", + "label": "Guides", + "items": [ + { + "type": "autogenerated", + "dirName": "noir_js/getting_started" + } + ] + }, + { + "type": "category", + "label": "Reference", + "items": [ + { + "type": "autogenerated", + "dirName": "noir_js/reference" + } + ] + } + ] + }, + { + "type": "doc", + "id": "migration_notes", + "label": "Migration notes" + } + ] +} diff --git a/docs/versions.json b/docs/versions.json index 26d22536e02..872105b2fb4 100644 --- a/docs/versions.json +++ b/docs/versions.json @@ -1,4 +1,5 @@ [ + "0.17.0", "0.10.5", "0.9.0", "0.7.1", diff --git a/flake.nix b/flake.nix index efe64734b60..5eeb50a6132 100644 --- a/flake.nix +++ b/flake.nix @@ -73,15 +73,15 @@ # Configuration shared between builds config = { # x-release-please-start-version - version = "0.17.0"; + version = "0.18.0"; # x-release-please-end src = pkgs.lib.cleanSourceWith { src = craneLib.path ./.; # Custom filter with various file extensions that we rely upon to build packages - # Currently: `.nr`, `.sol`, `.sh`, `.json`, `.md` + # Currently: `.nr`, `.sol`, `.sh`, `.json`, `.md` and `.wasm` filter = path: type: - (builtins.match ".*\.(nr|sol|sh|json|md)$" path != null) || (craneLib.filterCargoSources path type); + (builtins.match ".*\.(nr|sol|sh|json|md|wasm)$" path != null) || (craneLib.filterCargoSources path type); }; # TODO(#1198): It'd be nice to include these flags when running `cargo clippy` in a devShell. diff --git a/noir_stdlib/src/ec/consts/te.nr b/noir_stdlib/src/ec/consts/te.nr index b5847e77f34..1159ac558b0 100644 --- a/noir_stdlib/src/ec/consts/te.nr +++ b/noir_stdlib/src/ec/consts/te.nr @@ -8,9 +8,8 @@ struct BabyJubjub { suborder: Field, } +#[field(bn254)] pub fn baby_jubjub() -> BabyJubjub { - assert(compat::is_bn254()); - BabyJubjub { // Baby Jubjub (ERC-2494) parameters in affine representation curve: TECurve::new( diff --git a/noir_stdlib/src/eddsa.nr b/noir_stdlib/src/eddsa.nr index ff8c1da1397..31fac549fc5 100644 --- a/noir_stdlib/src/eddsa.nr +++ b/noir_stdlib/src/eddsa.nr @@ -33,7 +33,6 @@ pub fn eddsa_poseidon_verify( ) -> bool { // Verifies by testing: // S * B8 = R8 + H(R8, A, m) * A8 - let bjj = baby_jubjub(); let pub_key = TEPoint::new(pub_key_x, pub_key_y); @@ -46,13 +45,7 @@ pub fn eddsa_poseidon_verify( assert(lt_bytes32(signature_s, bjj.suborder)); // Calculate the h = H(R, A, msg) - let hash: Field = poseidon::bn254::hash_5([ - signature_r8_x, - signature_r8_y, - pub_key_x, - pub_key_y, - message, - ]); + let hash: Field = poseidon::bn254::hash_5([signature_r8_x, signature_r8_y, pub_key_x, pub_key_y, message]); // Calculate second part of the right side: right2 = h*8*A diff --git a/noir_stdlib/src/hash.nr b/noir_stdlib/src/hash.nr index 78e71aefb65..67e01c36e1c 100644 --- a/noir_stdlib/src/hash.nr +++ b/noir_stdlib/src/hash.nr @@ -13,6 +13,13 @@ pub fn pedersen(input : [Field; N]) -> [Field; 2] { #[foreign(pedersen)] pub fn pedersen_with_separator(_input : [Field; N], _separator : u32) -> [Field; 2] {} +pub fn pedersen_hash(input : [Field; N]) -> Field { + pedersen_hash_with_separator(input, 0) +} + +#[foreign(pedersen_hash)] +pub fn pedersen_hash_with_separator(_input : [Field; N], _separator : u32) -> Field {} + #[foreign(hash_to_field_128_security)] pub fn hash_to_field(_input : [Field; N]) -> Field {} @@ -39,6 +46,7 @@ fn mimc(x: Field, k: Field, constants: [Field; N], exp : Field) -> Field { global MIMC_BN254_ROUNDS = 91; //mimc implementation with hardcoded parameters for BN254 curve. +#[field(bn254)] pub fn mimc_bn254(array: [Field; N]) -> Field { //mimc parameters let exponent = 7; diff --git a/noir_stdlib/src/hash/poseidon/bn254.nr b/noir_stdlib/src/hash/poseidon/bn254.nr index fadf45138ab..f93ff577c97 100644 --- a/noir_stdlib/src/hash/poseidon/bn254.nr +++ b/noir_stdlib/src/hash/poseidon/bn254.nr @@ -7,6 +7,7 @@ use crate::hash::poseidon::apply_matrix; // Optimised permutation for this particular field; uses hardcoded rf and rp values, // which should agree with those in pos_conf. +#[field(bn254)] pub fn permute( pos_conf: PoseidonConfig, mut state: [Field; O]) @@ -65,6 +66,7 @@ pub fn permute( } // Corresponding absorption. +#[field(bn254)] fn absorb( pos_conf: PoseidonConfig, mut state: [Field; O], // Initial state; usually [0; O] @@ -98,6 +100,7 @@ fn absorb( } // Variable-length Poseidon-128 sponge as suggested in second bullet point of §3 of https://eprint.iacr.org/2019/458.pdf +#[field(bn254)] pub fn sponge(msg: [Field; N]) -> Field { absorb(consts::x5_5_config(), [0;5], 4, 1, msg)[1] } diff --git a/noir_stdlib/src/hash/poseidon/bn254/perm.nr b/noir_stdlib/src/hash/poseidon/bn254/perm.nr index 8b2f93c2d92..890f54fdb3f 100644 --- a/noir_stdlib/src/hash/poseidon/bn254/perm.nr +++ b/noir_stdlib/src/hash/poseidon/bn254/perm.nr @@ -3,6 +3,7 @@ use crate::hash::poseidon::bn254::consts; use crate::hash::poseidon::bn254::permute; use crate::hash::poseidon::PoseidonConfig; +#[field(bn254)] pub fn x5_2(mut state: [Field; 2]) -> [Field; 2] { state = permute( consts::x5_2_config(), @@ -11,6 +12,7 @@ pub fn x5_2(mut state: [Field; 2]) -> [Field; 2] { state } +#[field(bn254)] pub fn x5_3(mut state: [Field; 3]) -> [Field; 3] { state = permute( consts::x5_3_config(), @@ -19,6 +21,7 @@ pub fn x5_3(mut state: [Field; 3]) -> [Field; 3] { state } +#[field(bn254)] pub fn x5_4(mut state: [Field; 4]) -> [Field; 4] { state = permute( consts::x5_4_config(), @@ -27,6 +30,7 @@ pub fn x5_4(mut state: [Field; 4]) -> [Field; 4] { state } +#[field(bn254)] pub fn x5_5(mut state: [Field; 5]) -> [Field; 5] { state = permute( consts::x5_5_config(), @@ -35,6 +39,7 @@ pub fn x5_5(mut state: [Field; 5]) -> [Field; 5] { state } +#[field(bn254)] pub fn x5_6(mut state: [Field; 6]) -> [Field; 6] { state = permute( consts::x5_6_config(), @@ -43,6 +48,7 @@ pub fn x5_6(mut state: [Field; 6]) -> [Field; 6] { state } +#[field(bn254)] pub fn x5_7(mut state: [Field; 7]) -> [Field; 7] { state = permute( consts::x5_7_config(), @@ -51,6 +57,7 @@ pub fn x5_7(mut state: [Field; 7]) -> [Field; 7] { state } +#[field(bn254)] pub fn x5_8(mut state: [Field; 8]) -> [Field; 8] { state = permute( consts::x5_8_config(), @@ -59,6 +66,7 @@ pub fn x5_8(mut state: [Field; 8]) -> [Field; 8] { state } +#[field(bn254)] pub fn x5_9(mut state: [Field; 9]) -> [Field; 9] { state = permute( consts::x5_9_config(), @@ -67,6 +75,7 @@ pub fn x5_9(mut state: [Field; 9]) -> [Field; 9] { state } +#[field(bn254)] pub fn x5_10(mut state: [Field; 10]) -> [Field; 10] { state = permute( consts::x5_10_config(), @@ -75,6 +84,7 @@ pub fn x5_10(mut state: [Field; 10]) -> [Field; 10] { state } +#[field(bn254)] pub fn x5_11(mut state: [Field; 11]) -> [Field; 11] { state = permute( consts::x5_11_config(), @@ -83,6 +93,7 @@ pub fn x5_11(mut state: [Field; 11]) -> [Field; 11] { state } +#[field(bn254)] pub fn x5_12(mut state: [Field; 12]) -> [Field; 12] { state = permute( consts::x5_12_config(), @@ -91,6 +102,7 @@ pub fn x5_12(mut state: [Field; 12]) -> [Field; 12] { state } +#[field(bn254)] pub fn x5_13(mut state: [Field; 13]) -> [Field; 13] { state = permute( consts::x5_13_config(), @@ -99,6 +111,7 @@ pub fn x5_13(mut state: [Field; 13]) -> [Field; 13] { state } +#[field(bn254)] pub fn x5_14(mut state: [Field; 14]) -> [Field; 14] { state = permute( consts::x5_14_config(), @@ -107,6 +120,7 @@ pub fn x5_14(mut state: [Field; 14]) -> [Field; 14] { state } +#[field(bn254)] pub fn x5_15(mut state: [Field; 15]) -> [Field; 15] { state = permute( consts::x5_15_config(), @@ -115,6 +129,7 @@ pub fn x5_15(mut state: [Field; 15]) -> [Field; 15] { state } +#[field(bn254)] pub fn x5_16(mut state: [Field; 16]) -> [Field; 16] { state = permute( consts::x5_16_config(), @@ -123,6 +138,7 @@ pub fn x5_16(mut state: [Field; 16]) -> [Field; 16] { state } +#[field(bn254)] pub fn x5_17(mut state: [Field; 17]) -> [Field; 17] { state = permute( consts::x5_17_config(), diff --git a/noir_stdlib/src/lib.nr b/noir_stdlib/src/lib.nr index 26cf7a225ee..2e34c017db6 100644 --- a/noir_stdlib/src/lib.nr +++ b/noir_stdlib/src/lib.nr @@ -60,6 +60,7 @@ pub fn wrapping_mul(x : T, y: T) -> T { crate::from_field(crate::as_field(x) * crate::as_field(y)) } -pub fn wrapping_shift_left(x : T, y: T) -> T { - crate::from_field(crate::as_field(x) * 2.pow_32(crate::as_field(y))) -} +/// Shift-left x by y bits +/// If the result overflow the bitsize; it does not fail and returns 0 instead +#[builtin(wrapping_shift_left)] +pub fn wrapping_shift_left(x : T, y: T) -> T {} diff --git a/noir_stdlib/src/sha256.nr b/noir_stdlib/src/sha256.nr index f6c22aa1d5f..358b647a078 100644 --- a/noir_stdlib/src/sha256.nr +++ b/noir_stdlib/src/sha256.nr @@ -5,7 +5,9 @@ // Auxiliary mappings; names as in FIPS PUB 180-4 fn rotr32(a: u32, b: u32) -> u32 // 32-bit right rotation { - (a >> b) | (a << (32 as u32 - b)) + // None of the bits overlap between `(a >> b)` and `(a << (32 - b))` + // Addition is then equivalent to OR, with fewer constraints. + (a >> b) + (crate::wrapping_shift_left(a, 32 - b)) } fn ch(x: u32, y: u32, z: u32) -> u32 diff --git a/noir_stdlib/src/sha512.nr b/noir_stdlib/src/sha512.nr index e5cac7b1554..7d3412f517b 100644 --- a/noir_stdlib/src/sha512.nr +++ b/noir_stdlib/src/sha512.nr @@ -5,7 +5,9 @@ // Auxiliary mappings; names as in FIPS PUB 180-4 fn rotr64(a: u64, b: u64) -> u64 // 64-bit right rotation { - (a >> b) | (a << (64 - b)) + // None of the bits overlap between `(a >> b)` and `(a << (64 - b))` + // Addition is then equivalent to OR, with fewer constraints. + (a >> b) + (crate::wrapping_shift_left(a, 64 - b)) } fn sha_ch(x: u64, y: u64, z: u64) -> u64 diff --git a/tooling/backend_interface/test-binaries/mock_backend/src/info_cmd.rs b/tooling/backend_interface/test-binaries/mock_backend/src/info_cmd.rs index 043cef5934c..e9a7842ba24 100644 --- a/tooling/backend_interface/test-binaries/mock_backend/src/info_cmd.rs +++ b/tooling/backend_interface/test-binaries/mock_backend/src/info_cmd.rs @@ -17,6 +17,7 @@ const INFO_RESPONSE: &str = r#"{ "keccak256", "schnorr_verify", "pedersen", + "pedersen_hash", "hash_to_field_128_security", "ecdsa_secp256k1", "ecdsa_secp256r1", diff --git a/tooling/bb_abstraction_leaks/build.rs b/tooling/bb_abstraction_leaks/build.rs index 1ae5a28f5a8..fe6861a80b8 100644 --- a/tooling/bb_abstraction_leaks/build.rs +++ b/tooling/bb_abstraction_leaks/build.rs @@ -10,7 +10,7 @@ use const_format::formatcp; const USERNAME: &str = "AztecProtocol"; const REPO: &str = "aztec-packages"; -const VERSION: &str = "0.8.10"; +const VERSION: &str = "0.12.0"; const TAG: &str = formatcp!("aztec-packages-v{}", VERSION); const API_URL: &str = diff --git a/tooling/debugger/Cargo.toml b/tooling/debugger/Cargo.toml index e01abd9ac61..53c71754da4 100644 --- a/tooling/debugger/Cargo.toml +++ b/tooling/debugger/Cargo.toml @@ -12,6 +12,8 @@ license.workspace = true acvm.workspace = true nargo.workspace = true noirc_printable_type.workspace = true +noirc_errors.workspace = true thiserror.workspace = true +codespan-reporting.workspace = true easy-repl = "0.2.1" -owo-colors = "3" \ No newline at end of file +owo-colors = "3" diff --git a/tooling/debugger/src/context.rs b/tooling/debugger/src/context.rs new file mode 100644 index 00000000000..025e6404bcb --- /dev/null +++ b/tooling/debugger/src/context.rs @@ -0,0 +1,152 @@ +use acvm::acir::circuit::{Opcode, OpcodeLocation}; +use acvm::pwg::{ + ACVMStatus, BrilligSolver, BrilligSolverStatus, ForeignCallWaitInfo, StepResult, ACVM, +}; +use acvm::BlackBoxFunctionSolver; +use acvm::{acir::circuit::Circuit, acir::native_types::WitnessMap}; + +use nargo::errors::ExecutionError; +use nargo::ops::ForeignCallExecutor; +use nargo::NargoError; + +#[derive(Debug)] +pub(super) enum DebugCommandResult { + Done, + Ok, + Error(NargoError), +} + +pub(super) struct DebugContext<'a, B: BlackBoxFunctionSolver> { + acvm: ACVM<'a, B>, + brillig_solver: Option>, + foreign_call_executor: ForeignCallExecutor, + show_output: bool, +} + +impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { + pub(super) fn new( + blackbox_solver: &'a B, + circuit: &'a Circuit, + initial_witness: WitnessMap, + ) -> Self { + Self { + acvm: ACVM::new(blackbox_solver, &circuit.opcodes, initial_witness), + brillig_solver: None, + foreign_call_executor: ForeignCallExecutor::default(), + show_output: true, + } + } + + pub(super) fn get_opcodes(&self) -> &[Opcode] { + self.acvm.opcodes() + } + + pub(super) fn get_current_opcode_location(&self) -> Option { + let ip = self.acvm.instruction_pointer(); + if ip >= self.get_opcodes().len() { + None + } else if let Some(ref solver) = self.brillig_solver { + Some(OpcodeLocation::Brillig { + acir_index: ip, + brillig_index: solver.program_counter(), + }) + } else { + Some(OpcodeLocation::Acir(ip)) + } + } + + fn step_brillig_opcode(&mut self) -> DebugCommandResult { + let Some(mut solver) = self.brillig_solver.take() else { + unreachable!("Missing Brillig solver"); + }; + match solver.step() { + Ok(status) => match status { + BrilligSolverStatus::InProgress => { + self.brillig_solver = Some(solver); + DebugCommandResult::Ok + } + BrilligSolverStatus::Finished => { + let status = self.acvm.finish_brillig_with_solver(solver); + self.handle_acvm_status(status) + } + BrilligSolverStatus::ForeignCallWait(foreign_call) => { + self.brillig_solver = Some(solver); + self.handle_foreign_call(foreign_call) + } + }, + Err(err) => DebugCommandResult::Error(NargoError::ExecutionError( + ExecutionError::SolvingError(err), + )), + } + } + + fn handle_foreign_call(&mut self, foreign_call: ForeignCallWaitInfo) -> DebugCommandResult { + let foreign_call_result = + self.foreign_call_executor.execute(&foreign_call, self.show_output); + match foreign_call_result { + Ok(foreign_call_result) => { + self.acvm.resolve_pending_foreign_call(foreign_call_result); + // TODO: should we retry executing the opcode somehow in this case? + DebugCommandResult::Ok + } + Err(error) => DebugCommandResult::Error(error), + } + } + + fn handle_acvm_status(&mut self, status: ACVMStatus) -> DebugCommandResult { + if let ACVMStatus::RequiresForeignCall(foreign_call) = status { + self.handle_foreign_call(foreign_call) + } else { + match status { + ACVMStatus::Solved => DebugCommandResult::Done, + ACVMStatus::InProgress => DebugCommandResult::Ok, + ACVMStatus::Failure(error) => DebugCommandResult::Error( + NargoError::ExecutionError(ExecutionError::SolvingError(error)), + ), + ACVMStatus::RequiresForeignCall(_) => { + unreachable!("Unexpected pending foreign call resolution"); + } + } + } + } + + pub(super) fn step_into_opcode(&mut self) -> DebugCommandResult { + if matches!(self.brillig_solver, Some(_)) { + self.step_brillig_opcode() + } else { + match self.acvm.step_into_brillig_opcode() { + StepResult::IntoBrillig(solver) => { + self.brillig_solver = Some(solver); + self.step_brillig_opcode() + } + StepResult::Status(status) => self.handle_acvm_status(status), + } + } + } + + pub(super) fn step_acir_opcode(&mut self) -> DebugCommandResult { + let status = if let Some(solver) = self.brillig_solver.take() { + self.acvm.finish_brillig_with_solver(solver) + } else { + self.acvm.solve_opcode() + }; + self.handle_acvm_status(status) + } + + pub(super) fn cont(&mut self) -> DebugCommandResult { + loop { + let result = self.step_into_opcode(); + if !matches!(result, DebugCommandResult::Ok) { + return result; + } + } + } + + pub(super) fn is_solved(&self) -> bool { + matches!(self.acvm.get_status(), ACVMStatus::Solved) + } + + pub fn finalize(self) -> WitnessMap { + self.acvm.finalize() + } +} diff --git a/tooling/debugger/src/lib.rs b/tooling/debugger/src/lib.rs index f8e9db19234..42ae79fe411 100644 --- a/tooling/debugger/src/lib.rs +++ b/tooling/debugger/src/lib.rs @@ -1,186 +1,17 @@ -use acvm::acir::circuit::OpcodeLocation; -use acvm::pwg::{ACVMStatus, ErrorLocation, OpcodeResolutionError, ACVM}; +mod context; +mod repl; + use acvm::BlackBoxFunctionSolver; use acvm::{acir::circuit::Circuit, acir::native_types::WitnessMap}; use nargo::artifacts::debug::DebugArtifact; -use nargo::errors::ExecutionError; use nargo::NargoError; -use nargo::ops::ForeignCallExecutor; - -use easy_repl::{command, CommandStatus, Critical, Repl}; -use std::cell::{Cell, RefCell}; - -use owo_colors::OwoColorize; - -enum SolveResult { - Done, - Ok, -} - -struct DebugContext<'backend, B: BlackBoxFunctionSolver> { - acvm: ACVM<'backend, B>, - debug_artifact: DebugArtifact, - foreign_call_executor: ForeignCallExecutor, - circuit: &'backend Circuit, - show_output: bool, -} - -impl<'backend, B: BlackBoxFunctionSolver> DebugContext<'backend, B> { - fn step_opcode(&mut self) -> Result { - let solver_status = self.acvm.solve_opcode(); - - match solver_status { - ACVMStatus::Solved => Ok(SolveResult::Done), - ACVMStatus::InProgress => Ok(SolveResult::Ok), - ACVMStatus::Failure(error) => { - let call_stack = match &error { - OpcodeResolutionError::UnsatisfiedConstrain { - opcode_location: ErrorLocation::Resolved(opcode_location), - } => Some(vec![*opcode_location]), - OpcodeResolutionError::BrilligFunctionFailed { call_stack, .. } => { - Some(call_stack.clone()) - } - _ => None, - }; - - Err(NargoError::ExecutionError(match call_stack { - Some(call_stack) => { - if let Some(assert_message) = self.circuit.get_assert_message( - *call_stack.last().expect("Call stacks should not be empty"), - ) { - ExecutionError::AssertionFailed(assert_message.to_owned(), call_stack) - } else { - ExecutionError::SolvingError(error) - } - } - None => ExecutionError::SolvingError(error), - })) - } - ACVMStatus::RequiresForeignCall(foreign_call) => { - let foreign_call_result = - self.foreign_call_executor.execute(&foreign_call, self.show_output)?; - self.acvm.resolve_pending_foreign_call(foreign_call_result); - Ok(SolveResult::Ok) - } - } - } - - fn show_current_vm_status(&self) { - let ip = self.acvm.instruction_pointer(); - let opcodes = self.acvm.opcodes(); - if ip >= opcodes.len() { - println!("Finished execution"); - } else { - println!("Stopped at opcode {}: {}", ip, opcodes[ip]); - Self::show_source_code_location(&OpcodeLocation::Acir(ip), &self.debug_artifact); - } - } - - fn show_source_code_location(location: &OpcodeLocation, debug_artifact: &DebugArtifact) { - let locations = debug_artifact.debug_symbols[0].opcode_location(location); - if let Some(locations) = locations { - for loc in locations { - let file = &debug_artifact.file_map[&loc.file]; - let source = &file.source.as_str(); - let start = loc.span.start() as usize; - let end = loc.span.end() as usize; - println!("At {}:{start}-{end}", file.path.as_path().display()); - println!( - "\n{}{}{}\n", - &source[0..start].to_string().dimmed(), - &source[start..end], - &source[end..].to_string().dimmed(), - ); - } - } - } - - fn cont(&mut self) -> Result { - loop { - match self.step_opcode()? { - SolveResult::Done => break, - SolveResult::Ok => {} - } - } - Ok(SolveResult::Done) - } - - fn finalize(self) -> WitnessMap { - self.acvm.finalize() - } -} - -fn map_command_status(result: SolveResult) -> CommandStatus { - match result { - SolveResult::Ok => CommandStatus::Done, - SolveResult::Done => CommandStatus::Quit, - } -} - pub fn debug_circuit( blackbox_solver: &B, circuit: &Circuit, debug_artifact: DebugArtifact, initial_witness: WitnessMap, - show_output: bool, ) -> Result, NargoError> { - let context = RefCell::new(DebugContext { - acvm: ACVM::new(blackbox_solver, &circuit.opcodes, initial_witness), - foreign_call_executor: ForeignCallExecutor::default(), - circuit, - debug_artifact, - show_output, - }); - let ref_step = &context; - let ref_cont = &context; - - let solved = Cell::new(false); - - context.borrow().show_current_vm_status(); - - let handle_result = |result| { - solved.set(matches!(result, SolveResult::Done)); - Ok(map_command_status(result)) - }; - - let mut repl = Repl::builder() - .add( - "s", - command! { - "step to the next opcode", - () => || { - let result = ref_step.borrow_mut().step_opcode().into_critical()?; - ref_step.borrow().show_current_vm_status(); - handle_result(result) - } - }, - ) - .add( - "c", - command! { - "continue execution until the end of the program", - () => || { - println!("(Continuing execution...)"); - let result = ref_cont.borrow_mut().cont().into_critical()?; - handle_result(result) - } - }, - ) - .build() - .expect("Failed to initialize debugger repl"); - - repl.run().expect("Debugger error"); - - // REPL execution has finished. - // Drop it so that we can move fields out from `context` again. - drop(repl); - - if solved.get() { - let solved_witness = context.into_inner().finalize(); - Ok(Some(solved_witness)) - } else { - Ok(None) - } + repl::run(blackbox_solver, circuit, &debug_artifact, initial_witness) } diff --git a/tooling/debugger/src/repl.rs b/tooling/debugger/src/repl.rs new file mode 100644 index 00000000000..028d5120b07 --- /dev/null +++ b/tooling/debugger/src/repl.rs @@ -0,0 +1,241 @@ +use crate::context::{DebugCommandResult, DebugContext}; + +use acvm::acir::circuit::OpcodeLocation; +use acvm::BlackBoxFunctionSolver; +use acvm::{acir::circuit::Circuit, acir::native_types::WitnessMap}; + +use nargo::artifacts::debug::DebugArtifact; +use nargo::NargoError; + +use easy_repl::{command, CommandStatus, Repl}; +use std::cell::RefCell; + +use codespan_reporting::files::Files; +use noirc_errors::Location; + +use owo_colors::OwoColorize; + +use std::ops::Range; + +pub struct ReplDebugger<'a, B: BlackBoxFunctionSolver> { + context: DebugContext<'a, B>, + debug_artifact: &'a DebugArtifact, + last_result: DebugCommandResult, +} + +impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { + pub fn new( + blackbox_solver: &'a B, + circuit: &'a Circuit, + debug_artifact: &'a DebugArtifact, + initial_witness: WitnessMap, + ) -> Self { + let context = DebugContext::new(blackbox_solver, circuit, initial_witness); + Self { context, debug_artifact, last_result: DebugCommandResult::Ok } + } + + pub fn show_current_vm_status(&self) { + let location = self.context.get_current_opcode_location(); + let opcodes = self.context.get_opcodes(); + + match location { + None => println!("Finished execution"), + Some(location) => { + match location { + OpcodeLocation::Acir(ip) => { + println!("At opcode {}: {}", ip, opcodes[ip]) + } + OpcodeLocation::Brillig { acir_index: ip, brillig_index } => println!( + "At opcode {} in Brillig block {}: {}", + brillig_index, ip, opcodes[ip] + ), + } + self.show_source_code_location(&location); + } + } + } + + fn print_location_path(&self, loc: Location) { + let line_number = self.debug_artifact.location_line_number(loc).unwrap(); + let column_number = self.debug_artifact.location_column_number(loc).unwrap(); + + println!( + "At {}:{line_number}:{column_number}", + self.debug_artifact.name(loc.file).unwrap() + ); + } + + fn show_source_code_location(&self, location: &OpcodeLocation) { + let locations = self.debug_artifact.debug_symbols[0].opcode_location(location); + let Some(locations) = locations else { return }; + for loc in locations { + self.print_location_path(loc); + + let loc_line_index = self.debug_artifact.location_line_index(loc).unwrap(); + + // How many lines before or after the location's line we + // print + let context_lines = 5; + + let first_line_to_print = + if loc_line_index < context_lines { 0 } else { loc_line_index - context_lines }; + + let last_line_index = self.debug_artifact.last_line_index(loc).unwrap(); + let last_line_to_print = std::cmp::min(loc_line_index + context_lines, last_line_index); + + let source = self.debug_artifact.location_source_code(loc).unwrap(); + for (current_line_index, line) in source.lines().enumerate() { + let current_line_number = current_line_index + 1; + + if current_line_index < first_line_to_print { + // Ignore lines before range starts + continue; + } else if current_line_index == first_line_to_print && current_line_index > 0 { + // Denote that there's more lines before but we're not showing them + print_line_of_ellipsis(current_line_index); + } + + if current_line_index > last_line_to_print { + // Denote that there's more lines after but we're not showing them, + // and stop printing + print_line_of_ellipsis(current_line_number); + break; + } + + if current_line_index == loc_line_index { + // Highlight current location + let Range { start: loc_start, end: loc_end } = + self.debug_artifact.location_in_line(loc).unwrap(); + println!( + "{:>3} {:2} {}{}{}", + current_line_number, + "->", + &line[0..loc_start].to_string().dimmed(), + &line[loc_start..loc_end], + &line[loc_end..].to_string().dimmed() + ); + } else { + print_dimmed_line(current_line_number, line); + } + } + } + } + + fn validate_in_progress(&self) -> bool { + match self.last_result { + DebugCommandResult::Ok => true, + DebugCommandResult::Done => { + println!("Execution finished"); + false + } + DebugCommandResult::Error(ref error) => { + println!("ERROR: {}", error); + self.show_current_vm_status(); + false + } + } + } + + fn handle_debug_command_result(&mut self, result: DebugCommandResult) { + self.last_result = result; + self.show_current_vm_status(); + } + + fn step_acir_opcode(&mut self) { + if self.validate_in_progress() { + let result = self.context.step_acir_opcode(); + self.handle_debug_command_result(result); + } + } + + fn step_into_opcode(&mut self) { + if self.validate_in_progress() { + let result = self.context.step_into_opcode(); + self.handle_debug_command_result(result); + } + } + + fn cont(&mut self) { + if self.validate_in_progress() { + println!("(Continuing execution...)"); + let result = self.context.cont(); + self.handle_debug_command_result(result); + } + } + + fn is_solved(&self) -> bool { + self.context.is_solved() + } + + fn finalize(self) -> WitnessMap { + self.context.finalize() + } +} + +fn print_line_of_ellipsis(line_number: usize) { + println!("{}", format!("{:>3} {}", line_number, "...").dimmed()); +} + +fn print_dimmed_line(line_number: usize, line: &str) { + println!("{}", format!("{:>3} {:2} {}", line_number, "", line).dimmed()); +} + +pub fn run( + blackbox_solver: &B, + circuit: &Circuit, + debug_artifact: &DebugArtifact, + initial_witness: WitnessMap, +) -> Result, NargoError> { + let context = + RefCell::new(ReplDebugger::new(blackbox_solver, circuit, debug_artifact, initial_witness)); + let ref_context = &context; + + ref_context.borrow().show_current_vm_status(); + + let mut repl = Repl::builder() + .add( + "step", + command! { + "step to the next ACIR opcode", + () => || { + ref_context.borrow_mut().step_acir_opcode(); + Ok(CommandStatus::Done) + } + }, + ) + .add( + "into", + command! { + "step into to the next opcode", + () => || { + ref_context.borrow_mut().step_into_opcode(); + Ok(CommandStatus::Done) + } + }, + ) + .add( + "continue", + command! { + "continue execution until the end of the program", + () => || { + ref_context.borrow_mut().cont(); + Ok(CommandStatus::Done) + } + }, + ) + .build() + .expect("Failed to initialize debugger repl"); + + repl.run().expect("Debugger error"); + + // REPL execution has finished. + // Drop it so that we can move fields out from `context` again. + drop(repl); + + if context.borrow().is_solved() { + let solved_witness = context.into_inner().finalize(); + Ok(Some(solved_witness)) + } else { + Ok(None) + } +} diff --git a/tooling/lsp/src/solver.rs b/tooling/lsp/src/solver.rs index 2e8bf6ddc0a..090f71d63b4 100644 --- a/tooling/lsp/src/solver.rs +++ b/tooling/lsp/src/solver.rs @@ -16,12 +16,12 @@ impl BlackBoxFunctionSolver for WrapperSolver { self.0.schnorr_verify(public_key_x, public_key_y, signature, message) } - fn pedersen( + fn pedersen_commitment( &self, inputs: &[acvm::FieldElement], domain_separator: u32, ) -> Result<(acvm::FieldElement, acvm::FieldElement), acvm::BlackBoxResolutionError> { - self.0.pedersen(inputs, domain_separator) + self.0.pedersen_commitment(inputs, domain_separator) } fn fixed_base_scalar_mul( @@ -31,6 +31,14 @@ impl BlackBoxFunctionSolver for WrapperSolver { ) -> Result<(acvm::FieldElement, acvm::FieldElement), acvm::BlackBoxResolutionError> { self.0.fixed_base_scalar_mul(low, high) } + + fn pedersen_hash( + &self, + inputs: &[acvm::FieldElement], + domain_separator: u32, + ) -> Result { + self.0.pedersen_hash(inputs, domain_separator) + } } // We also have a mocked implementation of the `BlackBoxFunctionSolver` trait for use in tests @@ -50,7 +58,7 @@ impl BlackBoxFunctionSolver for MockBackend { unimplemented!() } - fn pedersen( + fn pedersen_commitment( &self, _inputs: &[acvm::FieldElement], _domain_separator: u32, @@ -65,4 +73,12 @@ impl BlackBoxFunctionSolver for MockBackend { ) -> Result<(acvm::FieldElement, acvm::FieldElement), acvm::BlackBoxResolutionError> { unimplemented!() } + + fn pedersen_hash( + &self, + _inputs: &[acvm::FieldElement], + _domain_separator: u32, + ) -> Result { + unimplemented!() + } } diff --git a/tooling/nargo/src/artifacts/debug.rs b/tooling/nargo/src/artifacts/debug.rs index 31ce1d7bb6a..19d8b88e641 100644 --- a/tooling/nargo/src/artifacts/debug.rs +++ b/tooling/nargo/src/artifacts/debug.rs @@ -1,6 +1,7 @@ use codespan_reporting::files::{Error, Files, SimpleFile}; use noirc_driver::DebugFile; -use noirc_errors::debug_info::DebugInfo; +use noirc_errors::{debug_info::DebugInfo, Location}; +use noirc_evaluator::errors::SsaReport; use serde::{Deserialize, Serialize}; use std::{ collections::{BTreeMap, BTreeSet}, @@ -15,6 +16,7 @@ use fm::{FileId, FileManager, PathString}; pub struct DebugArtifact { pub debug_symbols: Vec, pub file_map: BTreeMap, + pub warnings: Vec, } impl DebugArtifact { @@ -43,7 +45,54 @@ impl DebugArtifact { ); } - Self { debug_symbols, file_map } + Self { debug_symbols, file_map, warnings: Vec::new() } + } + + /// Given a location, returns its file's source code + pub fn location_source_code(&self, location: Location) -> Result<&str, Error> { + self.source(location.file) + } + + /// Given a location, returns the index of the line it starts at + pub fn location_line_index(&self, location: Location) -> Result { + let location_start = location.span.start() as usize; + self.line_index(location.file, location_start) + } + + /// Given a location, returns the line number it starts at + pub fn location_line_number(&self, location: Location) -> Result { + let location_start = location.span.start() as usize; + let line_index = self.line_index(location.file, location_start)?; + self.line_number(location.file, line_index) + } + + /// Given a location, returns the column number it starts at + pub fn location_column_number(&self, location: Location) -> Result { + let location_start = location.span.start() as usize; + let line_index = self.line_index(location.file, location_start)?; + self.column_number(location.file, line_index, location_start) + } + + /// Given a location, returns a Span relative to its line's + /// position in the file. This is useful when processing a file's + /// contents on a per-line-basis. + pub fn location_in_line(&self, location: Location) -> Result, Error> { + let location_start = location.span.start() as usize; + let location_end = location.span.end() as usize; + let line_index = self.line_index(location.file, location_start)?; + let line_span = self.line_range(location.file, line_index)?; + + let start_in_line = location_start - line_span.start; + let end_in_line = location_end - line_span.start; + + Ok(Range { start: start_in_line, end: end_in_line }) + } + + /// Given a location, returns the last line index + /// of its file + pub fn last_line_index(&self, location: Location) -> Result { + let source = self.source(location.file)?; + self.line_index(location.file, source.len()) } } diff --git a/tooling/nargo/src/errors.rs b/tooling/nargo/src/errors.rs index ea6e7fa8108..0c920716f2a 100644 --- a/tooling/nargo/src/errors.rs +++ b/tooling/nargo/src/errors.rs @@ -2,7 +2,10 @@ use acvm::{ acir::circuit::OpcodeLocation, pwg::{ErrorLocation, OpcodeResolutionError}, }; -use noirc_errors::{debug_info::DebugInfo, CustomDiagnostic, FileDiagnostic, Location}; +use noirc_errors::{debug_info::DebugInfo, CustomDiagnostic, FileDiagnostic}; + +pub use noirc_errors::Location; + use noirc_printable_type::ForeignCallError; use thiserror::Error; diff --git a/tooling/nargo_cli/src/cli/compile_cmd.rs b/tooling/nargo_cli/src/cli/compile_cmd.rs index 6486dfa5e6d..156359dab04 100644 --- a/tooling/nargo_cli/src/cli/compile_cmd.rs +++ b/tooling/nargo_cli/src/cli/compile_cmd.rs @@ -1,5 +1,6 @@ use std::path::Path; +use acvm::acir::circuit::opcodes::BlackBoxFuncCall; use acvm::acir::circuit::Opcode; use acvm::Language; use backend_interface::BackendOpcodeSupport; @@ -192,6 +193,7 @@ fn compile_program( noir_version: preprocessed_program.noir_version, debug: debug_artifact.debug_symbols.remove(0), file_map: debug_artifact.file_map, + warnings: debug_artifact.warnings, }) } else { None @@ -212,9 +214,18 @@ fn compile_program( } }; + // TODO: we say that pedersen hashing is supported by all backends for now + let is_opcode_supported_pedersen_hash = |opcode: &Opcode| -> bool { + if let Opcode::BlackBoxFuncCall(BlackBoxFuncCall::PedersenHash { .. }) = opcode { + true + } else { + is_opcode_supported(opcode) + } + }; + // Apply backend specific optimizations. let optimized_program = - nargo::ops::optimize_program(program, np_language, &is_opcode_supported) + nargo::ops::optimize_program(program, np_language, &is_opcode_supported_pedersen_hash) .expect("Backend does not support an opcode that is in the IR"); save_program(optimized_program.clone(), package, &workspace.target_directory_path()); @@ -256,8 +267,11 @@ fn save_program(program: CompiledProgram, package: &Package, circuit_dir: &Path) save_program_to_file(&preprocessed_program, &package.name, circuit_dir); - let debug_artifact = - DebugArtifact { debug_symbols: vec![program.debug], file_map: program.file_map }; + let debug_artifact = DebugArtifact { + debug_symbols: vec![program.debug], + file_map: program.file_map, + warnings: program.warnings, + }; let circuit_name: String = (&package.name).into(); save_debug_artifact_to_file(&debug_artifact, &circuit_name, circuit_dir); } @@ -270,6 +284,7 @@ fn save_contract(contract: CompiledContract, package: &Package, circuit_dir: &Pa let debug_artifact = DebugArtifact { debug_symbols: contract.functions.iter().map(|function| function.debug.clone()).collect(), file_map: contract.file_map, + warnings: contract.warnings, }; let preprocessed_functions = vecmap(contract.functions, |func| PreprocessedContractFunction { diff --git a/tooling/nargo_cli/src/cli/debug_cmd.rs b/tooling/nargo_cli/src/cli/debug_cmd.rs index 579d285ed6b..89fdc5dbc28 100644 --- a/tooling/nargo_cli/src/cli/debug_cmd.rs +++ b/tooling/nargo_cli/src/cli/debug_cmd.rs @@ -112,6 +112,7 @@ pub(crate) fn debug_program( let debug_artifact = DebugArtifact { debug_symbols: vec![compiled_program.debug.clone()], file_map: compiled_program.file_map.clone(), + warnings: compiled_program.warnings.clone(), }; noir_debugger::debug_circuit( @@ -119,7 +120,6 @@ pub(crate) fn debug_program( &compiled_program.circuit, debug_artifact, initial_witness, - true, ) .map_err(CliError::from) } diff --git a/tooling/nargo_cli/src/cli/execute_cmd.rs b/tooling/nargo_cli/src/cli/execute_cmd.rs index 0c6a1cecd5d..bba498bdf35 100644 --- a/tooling/nargo_cli/src/cli/execute_cmd.rs +++ b/tooling/nargo_cli/src/cli/execute_cmd.rs @@ -113,6 +113,7 @@ pub(crate) fn execute_program( let debug_artifact = DebugArtifact { debug_symbols: vec![compiled_program.debug.clone()], file_map: compiled_program.file_map.clone(), + warnings: compiled_program.warnings.clone(), }; if let Some(diagnostic) = try_to_diagnose_runtime_error(&err, &compiled_program.debug) { diff --git a/tooling/nargo_cli/tests/acir_artifacts/2_div/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/2_div/target/acir.gz index 42caed06578..37012dece4e 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/2_div/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/2_div/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/3_add/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/3_add/target/acir.gz index cef79cbc46c..490b5f41330 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/3_add/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/3_add/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/4_sub/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/4_sub/target/acir.gz index a6681ca770d..633bec13563 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/4_sub/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/4_sub/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/4_sub/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/4_sub/target/witness.gz index ce60a15c551..68e9df80789 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/4_sub/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/4_sub/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/5_over/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/5_over/target/acir.gz index 053840dca8b..681a0290f75 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/5_over/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/5_over/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/5_over/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/5_over/target/witness.gz index 122614793e2..b0a38188cab 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/5_over/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/5_over/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/6_array/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/6_array/target/acir.gz index bbc40bbae03..f25a320b01e 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/6_array/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/6_array/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/6_array/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/6_array/target/witness.gz index e1ab57f56a1..6aba7ca03c0 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/6_array/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/6_array/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/7_function/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/7_function/target/acir.gz index d08ebb1a635..8b417c250a3 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/7_function/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/7_function/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/7_function/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/7_function/target/witness.gz index 250f96eab04..9dcb6d30140 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/7_function/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/7_function/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/arithmetic_binary_operations/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/arithmetic_binary_operations/target/acir.gz index fb233f84cbf..fd31cc3bfa6 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/arithmetic_binary_operations/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/arithmetic_binary_operations/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/array_dynamic/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/array_dynamic/target/acir.gz index 1b4f0912b08..57701adcf3f 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/array_dynamic/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/array_dynamic/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/array_dynamic/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/array_dynamic/target/witness.gz index 41a4ec0817e..de5a1d07227 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/array_dynamic/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/array_dynamic/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/array_sort/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/array_sort/target/acir.gz index 08ff95bdd92..42d701ede8a 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/array_sort/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/array_sort/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/bit_and/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/bit_and/target/acir.gz index bb100a3693a..5fb7041cdf1 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/bit_and/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/bit_and/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/bit_shifts_comptime/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/bit_shifts_comptime/target/acir.gz index fdea9f57fa8..647dc586b04 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/bit_shifts_comptime/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/bit_shifts_comptime/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/bit_shifts_comptime/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/bit_shifts_comptime/target/witness.gz index 5de81c483c2..13d2ec1b7b5 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/bit_shifts_comptime/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/bit_shifts_comptime/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/bit_shifts_runtime/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/bit_shifts_runtime/target/acir.gz index e6a8f03a590..7a59062156f 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/bit_shifts_runtime/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/bit_shifts_runtime/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/bit_shifts_runtime/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/bit_shifts_runtime/target/witness.gz index 105d4dc8781..28357d49cd1 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/bit_shifts_runtime/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/bit_shifts_runtime/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/bool_not/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/bool_not/target/acir.gz index bffd6c3f723..233a1e25f33 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/bool_not/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/bool_not/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/bool_or/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/bool_or/target/acir.gz index b2550099bf6..697832be207 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/bool_or/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/bool_or/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_acir_as_brillig/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/brillig_acir_as_brillig/target/acir.gz index bdd4d5bed9d..b7a2d88a36e 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_acir_as_brillig/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/brillig_acir_as_brillig/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_assert/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/brillig_assert/target/acir.gz index db07a3c5aa3..2cf3b7251e6 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_assert/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/brillig_assert/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_calls/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/brillig_calls/target/acir.gz index e60666c73c8..b91295d3cec 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_calls/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/brillig_calls/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_fns_as_values/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/brillig_fns_as_values/target/acir.gz index eb55a029ec8..e8bede3cbed 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_fns_as_values/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/brillig_fns_as_values/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_keccak/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/brillig_keccak/target/acir.gz index ae815259717..1a64fd03980 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_keccak/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/brillig_keccak/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_not/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/brillig_not/target/acir.gz index 3d625215150..9702ca340a5 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_not/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/brillig_not/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_pedersen/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/brillig_pedersen/target/acir.gz index d876646a63e..b8eaf5a13a0 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_pedersen/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/brillig_pedersen/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_pedersen/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/brillig_pedersen/target/witness.gz index 860141e7ad9..b26110156a0 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_pedersen/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/brillig_pedersen/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_scalar_mul/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/brillig_scalar_mul/target/acir.gz index dfeb06cc1f0..9f5f787c655 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_scalar_mul/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/brillig_scalar_mul/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_schnorr/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/brillig_schnorr/target/witness.gz index 8992e084146..17d93cc4d19 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_schnorr/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/brillig_schnorr/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_unitialised_arrays/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/brillig_unitialised_arrays/target/acir.gz index 4ab33919248..7bc3304fdaa 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_unitialised_arrays/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/brillig_unitialised_arrays/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_unitialised_arrays/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/brillig_unitialised_arrays/target/witness.gz index 869536a1d98..2cbd61f9c4a 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_unitialised_arrays/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/brillig_unitialised_arrays/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/conditional_1/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/conditional_1/target/acir.gz index 0bad56713bd..7380f30d354 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/conditional_1/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/conditional_1/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/conditional_1/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/conditional_1/target/witness.gz index bd0870eefc9..2ff44d73044 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/conditional_1/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/conditional_1/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/conditional_regression_661/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/conditional_regression_661/target/acir.gz index 41a4dbd0a2c..01de08c56d2 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/conditional_regression_661/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/conditional_regression_661/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/double_verify_proof/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/double_verify_proof/target/acir.gz index 45f9234a182..a2faad65143 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/double_verify_proof/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/double_verify_proof/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/double_verify_proof/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/double_verify_proof/target/witness.gz index a8a403956a5..251984d6292 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/double_verify_proof/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/double_verify_proof/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/ecdsa_secp256k1/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/ecdsa_secp256k1/target/acir.gz index 2b594788817..9108d663e86 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/ecdsa_secp256k1/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/ecdsa_secp256k1/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/ecdsa_secp256r1/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/ecdsa_secp256r1/target/acir.gz index 0b8cf56b8d5..ec6bc2c73a0 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/ecdsa_secp256r1/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/ecdsa_secp256r1/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/eddsa/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/eddsa/target/acir.gz index 049b7326ea8..275afa4c6e0 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/eddsa/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/eddsa/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/eddsa/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/eddsa/target/witness.gz index fcce3069d87..f87a868de3d 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/eddsa/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/eddsa/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/field_attribute/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/field_attribute/target/acir.gz index a68a3a3b566..9401237fd8c 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/field_attribute/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/field_attribute/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/hash_to_field/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/hash_to_field/target/acir.gz index 4c5bcaa1a31..c2c8c9f9469 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/hash_to_field/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/hash_to_field/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/higher_order_functions/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/higher_order_functions/target/acir.gz index 53bbd84e74c..eab354be13d 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/higher_order_functions/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/higher_order_functions/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/higher_order_functions/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/higher_order_functions/target/witness.gz index 3998fbc8d59..329d15dfb17 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/higher_order_functions/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/higher_order_functions/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/keccak256/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/keccak256/target/acir.gz index 6b57ed0fb85..cb74273e4d7 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/keccak256/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/keccak256/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/main_bool_arg/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/main_bool_arg/target/acir.gz index 753ad7c9687..d054abe1df0 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/main_bool_arg/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/main_bool_arg/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/merkle_insert/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/merkle_insert/target/acir.gz index 5db7b8b1539..889352e685f 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/merkle_insert/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/merkle_insert/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/merkle_insert/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/merkle_insert/target/witness.gz index 7fdb4d1f9e5..c4ded4eea08 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/merkle_insert/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/merkle_insert/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/modulus/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/modulus/target/acir.gz index 3ac23ecd933..a99124a5e3a 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/modulus/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/modulus/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/nested_array_dynamic/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/nested_array_dynamic/target/acir.gz index 8eb05c0382d..c7559671f2a 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/nested_array_dynamic/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/nested_array_dynamic/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/nested_array_dynamic/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/nested_array_dynamic/target/witness.gz index d5dc73c9bb4..7efd9b48ca4 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/nested_array_dynamic/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/nested_array_dynamic/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/nested_slice_dynamic/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/nested_slice_dynamic/target/acir.gz index d76d3a08041..fcfd2ab066d 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/nested_slice_dynamic/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/nested_slice_dynamic/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/nested_slice_dynamic/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/nested_slice_dynamic/target/witness.gz index 8d168b8c19c..ba1f5565170 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/nested_slice_dynamic/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/nested_slice_dynamic/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/pedersen_check/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/pedersen_check/target/acir.gz index 0c96568b826..02c9f32e3c5 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/pedersen_check/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/pedersen_check/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/pedersen_check/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/pedersen_check/target/witness.gz index a2cef03fe16..caf34e2b734 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/pedersen_check/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/pedersen_check/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/regression/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/regression/target/acir.gz index be72e6abb75..ed80135203d 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/regression/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/regression/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/regression/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/regression/target/witness.gz index 10c1c42caa0..76afd0975f6 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/regression/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/regression/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/regression_2854/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/regression_2854/target/acir.gz index 8a9e4ed2b65..6f4ffaa488f 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/regression_2854/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/regression_2854/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/regression_mem_op_predicate/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/regression_mem_op_predicate/target/acir.gz index 403c0dd43f1..20e8fa769a5 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/regression_mem_op_predicate/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/regression_mem_op_predicate/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/regression_mem_op_predicate/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/regression_mem_op_predicate/target/witness.gz index e8dc551cf13..000b4483daf 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/regression_mem_op_predicate/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/regression_mem_op_predicate/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/scalar_mul/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/scalar_mul/target/acir.gz index e06ad09e176..0bf8db7df70 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/scalar_mul/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/scalar_mul/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/schnorr/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/schnorr/target/acir.gz index 84a646ab241..4005c95b18d 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/schnorr/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/schnorr/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/schnorr/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/schnorr/target/witness.gz index ebc1ed7f713..91324bd7284 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/schnorr/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/schnorr/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/sha256/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/sha256/target/acir.gz index 8537218ec5c..a2de8064bb5 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/sha256/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/sha256/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/sha2_blocks/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/sha2_blocks/target/acir.gz index 3c21d9a4bf8..1e9c237d083 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/sha2_blocks/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/sha2_blocks/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/sha2_blocks/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/sha2_blocks/target/witness.gz index 40ca3d3f63f..6bedf3922b0 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/sha2_blocks/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/sha2_blocks/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/sha2_byte/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/sha2_byte/target/acir.gz index b285f9c24e5..881bf967590 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/sha2_byte/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/sha2_byte/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/sha2_byte/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/sha2_byte/target/witness.gz index 66d8125a852..746c6fc56a2 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/sha2_byte/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/sha2_byte/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/signed_arithmetic/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/signed_arithmetic/target/acir.gz index a5305fbd1bd..505e8f56bb9 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/signed_arithmetic/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/signed_arithmetic/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/signed_arithmetic/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/signed_arithmetic/target/witness.gz index 5a1d190cdf0..c180c733428 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/signed_arithmetic/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/signed_arithmetic/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/signed_division/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/signed_division/target/acir.gz index e3663c2a7c9..906e4f2010b 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/signed_division/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/signed_division/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/signed_division/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/signed_division/target/witness.gz index 598c8658469..c56799e636a 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/signed_division/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/signed_division/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/simple_bitwise/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/simple_bitwise/target/acir.gz index 71e5cc3ce65..25a54000fdb 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/simple_bitwise/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/simple_bitwise/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/simple_comparison/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/simple_comparison/target/acir.gz index 87ad6a62a57..452780c4d30 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/simple_comparison/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/simple_comparison/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/simple_not/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/simple_not/target/acir.gz index 8f3fd60cc4d..a47defb9fe6 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/simple_not/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/simple_not/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/simple_radix/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/simple_radix/target/acir.gz index 78096c19e45..80a70ba7ee3 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/simple_radix/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/simple_radix/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/simple_radix/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/simple_radix/target/witness.gz index 6ecc30eb054..e730d65c475 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/simple_radix/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/simple_radix/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/simple_shield/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/simple_shield/target/acir.gz index 8fbb88d62bd..0600c1dfc78 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/simple_shield/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/simple_shield/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/simple_shield/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/simple_shield/target/witness.gz index 1a33f9f79e2..0bc6d3e8fbc 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/simple_shield/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/simple_shield/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/slice_dynamic_index/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/slice_dynamic_index/target/acir.gz index f53fbca1f59..08d3fa2de40 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/slice_dynamic_index/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/slice_dynamic_index/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/slice_dynamic_index/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/slice_dynamic_index/target/witness.gz index 0075dd044dc..a077aaf9e56 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/slice_dynamic_index/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/slice_dynamic_index/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/slices/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/slices/target/acir.gz index b8a2b19d38b..70a3700cbaf 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/slices/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/slices/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/slices/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/slices/target/witness.gz index d9d8d036339..07ac75fe038 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/slices/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/slices/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/strings/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/strings/target/witness.gz index 8ee2f7c9148..72a93aabbfe 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/strings/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/strings/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/submodules/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/submodules/target/acir.gz index b2550099bf6..697832be207 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/submodules/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/submodules/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/to_be_bytes/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/to_be_bytes/target/acir.gz index f0742c6cf2b..aec6f367127 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/to_be_bytes/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/to_be_bytes/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/to_be_bytes/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/to_be_bytes/target/witness.gz index 9ffa155a84f..88ed370f861 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/to_be_bytes/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/to_be_bytes/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/to_bytes_consistent/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/to_bytes_consistent/target/acir.gz index a69c76b3198..c505c205faa 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/to_bytes_consistent/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/to_bytes_consistent/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/to_bytes_consistent/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/to_bytes_consistent/target/witness.gz index 3b931590419..5f82918d57a 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/to_bytes_consistent/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/to_bytes_consistent/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/to_bytes_integration/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/to_bytes_integration/target/acir.gz index 4e34c8da2d1..247509bd50a 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/to_bytes_integration/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/to_bytes_integration/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/to_bytes_integration/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/to_bytes_integration/target/witness.gz index b25db55391c..a672966388a 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/to_bytes_integration/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/to_bytes_integration/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/to_le_bytes/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/to_le_bytes/target/acir.gz index 1445a969536..14286c04ef9 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/to_le_bytes/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/to_le_bytes/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/to_le_bytes/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/to_le_bytes/target/witness.gz index b3948caad12..44c71164ae4 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/to_le_bytes/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/to_le_bytes/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/tuple_inputs/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/tuple_inputs/target/acir.gz index cf2b43945fe..79ae7dccb3d 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/tuple_inputs/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/tuple_inputs/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/tuples/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/tuples/target/acir.gz index 154974745fa..a053f565e5b 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/tuples/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/tuples/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/tuples/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/tuples/target/witness.gz index e48187b08c1..10cffba7141 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/tuples/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/tuples/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/compile_failure/brillig_assert_fail/src/main.nr b/tooling/nargo_cli/tests/compile_failure/brillig_assert_fail/src/main.nr index 801a818c816..e9379a29876 100644 --- a/tooling/nargo_cli/tests/compile_failure/brillig_assert_fail/src/main.nr +++ b/tooling/nargo_cli/tests/compile_failure/brillig_assert_fail/src/main.nr @@ -1,7 +1,7 @@ // Tests a very simple program. // // The features being tested is using assert on brillig -fn main(x: Field) { +fn main(x: Field) { assert(1 == conditional(x as bool)); } diff --git a/tooling/nargo_cli/tests/compile_failure/div_by_zero_constants/src/main.nr b/tooling/nargo_cli/tests/compile_failure/div_by_zero_constants/src/main.nr index 2259d51e6de..58adc5444b1 100644 --- a/tooling/nargo_cli/tests/compile_failure/div_by_zero_constants/src/main.nr +++ b/tooling/nargo_cli/tests/compile_failure/div_by_zero_constants/src/main.nr @@ -3,4 +3,4 @@ use dep::std; fn main() { let a: Field = 3 / 0; std::println(a); -} \ No newline at end of file +} diff --git a/tooling/nargo_cli/tests/compile_failure/div_by_zero_modulo/src/main.nr b/tooling/nargo_cli/tests/compile_failure/div_by_zero_modulo/src/main.nr index f20c39486e0..d938ab7fca8 100644 --- a/tooling/nargo_cli/tests/compile_failure/div_by_zero_modulo/src/main.nr +++ b/tooling/nargo_cli/tests/compile_failure/div_by_zero_modulo/src/main.nr @@ -1,7 +1,7 @@ fn main() { let a: u32 = 6; - let b = 3; - let c = 0; - let res = (a*b) % c; - assert(res != 5); -} \ No newline at end of file + let b = 3; + let c = 0; + let res = (a * b) % c; + assert(res != 5); +} diff --git a/tooling/nargo_cli/tests/compile_failure/dup_trait_implementation_4/src/main.nr b/tooling/nargo_cli/tests/compile_failure/dup_trait_implementation_4/src/main.nr index b9f712ceff0..e03fe8b3eeb 100644 --- a/tooling/nargo_cli/tests/compile_failure/dup_trait_implementation_4/src/main.nr +++ b/tooling/nargo_cli/tests/compile_failure/dup_trait_implementation_4/src/main.nr @@ -2,5 +2,4 @@ mod module1; mod module2; mod module3; -fn main() { -} +fn main() {} diff --git a/tooling/nargo_cli/tests/compile_failure/dup_trait_implementation_5/src/main.nr b/tooling/nargo_cli/tests/compile_failure/dup_trait_implementation_5/src/main.nr index 78e3867e4a1..b5719009898 100644 --- a/tooling/nargo_cli/tests/compile_failure/dup_trait_implementation_5/src/main.nr +++ b/tooling/nargo_cli/tests/compile_failure/dup_trait_implementation_5/src/main.nr @@ -3,5 +3,4 @@ mod module2; mod module3; mod module4; -fn main() { -} +fn main() {} diff --git a/tooling/nargo_cli/tests/compile_failure/duplicate_declaration/src/main.nr b/tooling/nargo_cli/tests/compile_failure/duplicate_declaration/src/main.nr index 09337e51c93..4803f2005c9 100644 --- a/tooling/nargo_cli/tests/compile_failure/duplicate_declaration/src/main.nr +++ b/tooling/nargo_cli/tests/compile_failure/duplicate_declaration/src/main.nr @@ -5,4 +5,4 @@ fn hello(x : Field) -> Field { fn hello(x : Field) -> Field { x -} \ No newline at end of file +} diff --git a/tooling/nargo_cli/tests/compile_failure/dyn_index_fail_nested_array/src/main.nr b/tooling/nargo_cli/tests/compile_failure/dyn_index_fail_nested_array/src/main.nr index e26625457d9..9f2ae3a0618 100644 --- a/tooling/nargo_cli/tests/compile_failure/dyn_index_fail_nested_array/src/main.nr +++ b/tooling/nargo_cli/tests/compile_failure/dyn_index_fail_nested_array/src/main.nr @@ -5,4 +5,4 @@ struct Foo { fn main(mut x : [Foo; 3], y : pub Field) { assert(x[y + 2].a == 5); -} \ No newline at end of file +} diff --git a/tooling/nargo_cli/tests/compile_failure/dynamic_index_failure/src/main.nr b/tooling/nargo_cli/tests/compile_failure/dynamic_index_failure/src/main.nr index 2665356ccd6..10d4d1f02d1 100644 --- a/tooling/nargo_cli/tests/compile_failure/dynamic_index_failure/src/main.nr +++ b/tooling/nargo_cli/tests/compile_failure/dynamic_index_failure/src/main.nr @@ -20,4 +20,4 @@ fn main(mut x: [u32; 5], z: Field) { // } // } // assert(x[idx] != 0); -} \ No newline at end of file +} diff --git a/tooling/nargo_cli/tests/compile_failure/field_modulo/src/main.nr b/tooling/nargo_cli/tests/compile_failure/field_modulo/src/main.nr index b27ba0892b3..a482b68806b 100644 --- a/tooling/nargo_cli/tests/compile_failure/field_modulo/src/main.nr +++ b/tooling/nargo_cli/tests/compile_failure/field_modulo/src/main.nr @@ -1,4 +1,3 @@ - fn main(x: Field) -> pub Field { x % 2 } diff --git a/tooling/nargo_cli/tests/compile_failure/integer_literal_overflow/Nargo.toml b/tooling/nargo_cli/tests/compile_failure/integer_literal_overflow/Nargo.toml new file mode 100644 index 00000000000..f29ec0408ea --- /dev/null +++ b/tooling/nargo_cli/tests/compile_failure/integer_literal_overflow/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "integer_literal_overflow" +type = "bin" +authors = [""] +compiler_version = "0.16.0" + +[dependencies] \ No newline at end of file diff --git a/tooling/nargo_cli/tests/compile_failure/integer_literal_overflow/src/main.nr b/tooling/nargo_cli/tests/compile_failure/integer_literal_overflow/src/main.nr new file mode 100644 index 00000000000..ab1cb457fee --- /dev/null +++ b/tooling/nargo_cli/tests/compile_failure/integer_literal_overflow/src/main.nr @@ -0,0 +1,6 @@ + +fn main() { + foo(1234) +} + +fn foo(_x: u4) {} diff --git a/tooling/nargo_cli/tests/compile_failure/overflowing_assignment/src/main.nr b/tooling/nargo_cli/tests/compile_failure/overflowing_assignment/src/main.nr index 78535a853a6..6b529103ca3 100644 --- a/tooling/nargo_cli/tests/compile_failure/overflowing_assignment/src/main.nr +++ b/tooling/nargo_cli/tests/compile_failure/overflowing_assignment/src/main.nr @@ -1,5 +1,5 @@ fn main() { let x:u8 = -1; let y:u8 = 300; - assert(x!=y); + assert(x != y); } diff --git a/tooling/nargo_cli/tests/compile_failure/overlapping_generic_impls/Nargo.toml b/tooling/nargo_cli/tests/compile_failure/overlapping_generic_impls/Nargo.toml new file mode 100644 index 00000000000..e48565a2111 --- /dev/null +++ b/tooling/nargo_cli/tests/compile_failure/overlapping_generic_impls/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "overlapping_generic_impls" +type = "bin" +authors = [""] +compiler_version = "0.18.0" + +[dependencies] \ No newline at end of file diff --git a/tooling/nargo_cli/tests/compile_failure/overlapping_generic_impls/src/main.nr b/tooling/nargo_cli/tests/compile_failure/overlapping_generic_impls/src/main.nr new file mode 100644 index 00000000000..abf905ad71b --- /dev/null +++ b/tooling/nargo_cli/tests/compile_failure/overlapping_generic_impls/src/main.nr @@ -0,0 +1,7 @@ + +trait Trait { fn t(self); } + +impl Trait for T { fn t(self){} } +impl Trait for u32 { fn t(self){} } + +fn main() {} diff --git a/tooling/nargo_cli/tests/compile_failure/slice_insert_failure/src/main.nr b/tooling/nargo_cli/tests/compile_failure/slice_insert_failure/src/main.nr index dad80bff7ab..72918037c16 100644 --- a/tooling/nargo_cli/tests/compile_failure/slice_insert_failure/src/main.nr +++ b/tooling/nargo_cli/tests/compile_failure/slice_insert_failure/src/main.nr @@ -8,4 +8,4 @@ fn main(x : Field, y : pub Field) { } slice = slice.insert(10, 100); -} \ No newline at end of file +} diff --git a/tooling/nargo_cli/tests/compile_failure/slice_remove_failure/src/main.nr b/tooling/nargo_cli/tests/compile_failure/slice_remove_failure/src/main.nr index 7c308488ae8..1cd335f4b34 100644 --- a/tooling/nargo_cli/tests/compile_failure/slice_remove_failure/src/main.nr +++ b/tooling/nargo_cli/tests/compile_failure/slice_remove_failure/src/main.nr @@ -8,4 +8,4 @@ fn main(x : Field, y : pub Field) { } let (removed_slice, removed_elem) = slice.remove(10); -} \ No newline at end of file +} diff --git a/tooling/nargo_cli/tests/compile_success_contract/contract_with_impl/Nargo.toml b/tooling/nargo_cli/tests/compile_success_contract/contract_with_impl/Nargo.toml new file mode 100644 index 00000000000..99340cf80b5 --- /dev/null +++ b/tooling/nargo_cli/tests/compile_success_contract/contract_with_impl/Nargo.toml @@ -0,0 +1,8 @@ +[package] +name = "simple_contract" +type = "contract" +authors = [""] +compiler_version = "0.1" + +[dependencies] + diff --git a/tooling/nargo_cli/tests/compile_success_contract/contract_with_impl/src/main.nr b/tooling/nargo_cli/tests/compile_success_contract/contract_with_impl/src/main.nr new file mode 100644 index 00000000000..fa04e0d3e7b --- /dev/null +++ b/tooling/nargo_cli/tests/compile_success_contract/contract_with_impl/src/main.nr @@ -0,0 +1,8 @@ + +contract Foo { + struct T { x: [Field] } + + impl T { + fn t(self){} + } +} diff --git a/tooling/nargo_cli/tests/compile_success_contract/non_entry_point_method/src/main.nr b/tooling/nargo_cli/tests/compile_success_contract/non_entry_point_method/src/main.nr index 5c7152029c9..f1b739e4b88 100644 --- a/tooling/nargo_cli/tests/compile_success_contract/non_entry_point_method/src/main.nr +++ b/tooling/nargo_cli/tests/compile_success_contract/non_entry_point_method/src/main.nr @@ -3,4 +3,4 @@ contract Foo { #[contract_library_method] fn has_mut(_context : &mut PlaceholderStruct) {} -} \ No newline at end of file +} diff --git a/tooling/nargo_cli/tests/compile_success_empty/attributes_multiple/src/main.nr b/tooling/nargo_cli/tests/compile_success_empty/attributes_multiple/src/main.nr index 46b761065ff..581fb989b4c 100644 --- a/tooling/nargo_cli/tests/compile_success_empty/attributes_multiple/src/main.nr +++ b/tooling/nargo_cli/tests/compile_success_empty/attributes_multiple/src/main.nr @@ -1,8 +1,7 @@ - fn main() { another_func() } #[aztec(private)] #[internal] -fn another_func() {} \ No newline at end of file +fn another_func() {} diff --git a/tooling/nargo_cli/tests/compile_success_empty/auto_deref/src/main.nr b/tooling/nargo_cli/tests/compile_success_empty/auto_deref/src/main.nr index e0b4a447fd0..d69aff7e0d4 100644 --- a/tooling/nargo_cli/tests/compile_success_empty/auto_deref/src/main.nr +++ b/tooling/nargo_cli/tests/compile_success_empty/auto_deref/src/main.nr @@ -1,4 +1,3 @@ - fn main() { let a = &mut &mut &mut [1, 2, 3]; assert(a[0] == 1); diff --git a/tooling/nargo_cli/tests/compile_success_empty/brillig_field_binary_operations/src/main.nr b/tooling/nargo_cli/tests/compile_success_empty/brillig_field_binary_operations/src/main.nr index e7b0afccc3e..85002cef01d 100644 --- a/tooling/nargo_cli/tests/compile_success_empty/brillig_field_binary_operations/src/main.nr +++ b/tooling/nargo_cli/tests/compile_success_empty/brillig_field_binary_operations/src/main.nr @@ -1,5 +1,5 @@ // Tests arithmetic operations on fields -fn main() { +fn main() { let x = 4; let y = 2; assert((x + y) == add(x, y)); @@ -22,4 +22,4 @@ unconstrained fn mul(x : Field, y : Field) -> Field { unconstrained fn div(x : Field, y : Field) -> Field { x / y -} \ No newline at end of file +} diff --git a/tooling/nargo_cli/tests/compile_success_empty/brillig_modulo/src/main.nr b/tooling/nargo_cli/tests/compile_success_empty/brillig_modulo/src/main.nr index 1cab78ecb95..57dc516bd97 100644 --- a/tooling/nargo_cli/tests/compile_success_empty/brillig_modulo/src/main.nr +++ b/tooling/nargo_cli/tests/compile_success_empty/brillig_modulo/src/main.nr @@ -7,9 +7,9 @@ fn main() { assert(signed_modulo(5, 3) == 2); assert(signed_modulo(2, 3) == 2); - let minus_two: i4 = 14; - let minus_three: i4 = 13; - let minus_five: i4 = 11; + let minus_two: i4 = -2; // 14 + let minus_three: i4 = -3; // 13 + let minus_five: i4 = -5; // 11 // (5 / -3) * -3 + 2 = -1 * -3 + 2 = 3 + 2 = 5 assert(signed_modulo(5, minus_three) == 2); diff --git a/tooling/nargo_cli/tests/compile_success_empty/brillig_to_bits/src/main.nr b/tooling/nargo_cli/tests/compile_success_empty/brillig_to_bits/src/main.nr index a2ab0d4bc5a..294da58f68a 100644 --- a/tooling/nargo_cli/tests/compile_success_empty/brillig_to_bits/src/main.nr +++ b/tooling/nargo_cli/tests/compile_success_empty/brillig_to_bits/src/main.nr @@ -20,5 +20,4 @@ unconstrained fn main() { let le_bit = le_bits_x[3-i]; assert(be_bit == le_bit); } - } diff --git a/tooling/nargo_cli/tests/compile_success_empty/closure_explicit_types/src/main.nr b/tooling/nargo_cli/tests/compile_success_empty/closure_explicit_types/src/main.nr index 133bc1b4206..b5008c5d6dd 100644 --- a/tooling/nargo_cli/tests/compile_success_empty/closure_explicit_types/src/main.nr +++ b/tooling/nargo_cli/tests/compile_success_empty/closure_explicit_types/src/main.nr @@ -1,4 +1,3 @@ - fn ret_normal_lambda1() -> fn() -> Field { || 10 } @@ -70,10 +69,10 @@ fn main() { assert(accepts_closure3(|| y as u64 + z) == 70); let w = 50; - assert(add_results(|| 100, || x ) == 150); - assert(add_results(|| x + 100, || w + x ) == 250); + assert(add_results(|| 100, || x) == 150); + assert(add_results(|| x + 100, || w + x) == 250); - let arr = [1,2,3,4]; + let arr = [1, 2, 3, 4]; assert(map(arr, |n| n + 1) == [2, 3, 4, 5]); assert(map(arr, |n| n + x) == [51, 52, 53, 54]); diff --git a/tooling/nargo_cli/tests/compile_success_empty/comptime_sort/src/main.nr b/tooling/nargo_cli/tests/compile_success_empty/comptime_sort/src/main.nr index f8bd38654c0..a24a6ebaba6 100644 --- a/tooling/nargo_cli/tests/compile_success_empty/comptime_sort/src/main.nr +++ b/tooling/nargo_cli/tests/compile_success_empty/comptime_sort/src/main.nr @@ -1,6 +1,5 @@ - fn main() { - let unsorted: [u8; 3] = [3,1,2]; + let unsorted: [u8; 3] = [3, 1, 2]; let sorted = unsorted.sort(); assert(sorted[0] == 1); assert(sorted[1] == 2); diff --git a/tooling/nargo_cli/tests/compile_success_empty/conditional_regression_547/src/main.nr b/tooling/nargo_cli/tests/compile_success_empty/conditional_regression_547/src/main.nr index 41ccbb00a2a..f97199288a4 100644 --- a/tooling/nargo_cli/tests/compile_success_empty/conditional_regression_547/src/main.nr +++ b/tooling/nargo_cli/tests/compile_success_empty/conditional_regression_547/src/main.nr @@ -13,10 +13,5 @@ fn main(x: Field) -> pub Field { } fn safe_inverse(n: Field) -> Field { - if n == 0 { - 0 - } - else { - 1 / n - } + if n == 0 { 0 } else { 1 / n } } diff --git a/tooling/nargo_cli/tests/compile_success_empty/ec_baby_jubjub/src/main.nr b/tooling/nargo_cli/tests/compile_success_empty/ec_baby_jubjub/src/main.nr index 3372e969d4b..2060ac57e31 100644 --- a/tooling/nargo_cli/tests/compile_success_empty/ec_baby_jubjub/src/main.nr +++ b/tooling/nargo_cli/tests/compile_success_empty/ec_baby_jubjub/src/main.nr @@ -13,39 +13,39 @@ use dep::std::ec::montcurve::curvegroup::Point as MG; fn main() { // This test only makes sense if Field is the right prime field. - if 21888242871839275222246405745257275088548364400416034343698204186575808495617 == 0 - { + if 21888242871839275222246405745257275088548364400416034343698204186575808495617 == 0 { // Define Baby Jubjub (ERC-2494) parameters in affine representation - let bjj_affine = AffineCurve::new(168700, 168696, Gaffine::new(995203441582195749578291179787384436505546430278305826713579947235728471134,5472060717959818805561601436314318772137091100104008585924551046643952123905)); + let bjj_affine = AffineCurve::new(168700, 168696, Gaffine::new( + 995203441582195749578291179787384436505546430278305826713579947235728471134, 5472060717959818805561601436314318772137091100104008585924551046643952123905 + )); // Test addition - let p1_affine = Gaffine::new(17777552123799933955779906779655732241715742912184938656739573121738514868268, 2626589144620713026669568689430873010625803728049924121243784502389097019475); - let p2_affine = Gaffine::new(16540640123574156134436876038791482806971768689494387082833631921987005038935, 20819045374670962167435360035096875258406992893633759881276124905556507972311); - - let p3_affine = bjj_affine.add(p1_affine, p2_affine); - assert( - p3_affine.eq(Gaffine::new( - 7916061937171219682591368294088513039687205273691143098332585753343424131937, - 14035240266687799601661095864649209771790948434046947201833777492504781204499 - )) + let p1_affine = Gaffine::new( + 17777552123799933955779906779655732241715742912184938656739573121738514868268, 2626589144620713026669568689430873010625803728049924121243784502389097019475 ); + let p2_affine = Gaffine::new( + 16540640123574156134436876038791482806971768689494387082833631921987005038935, 20819045374670962167435360035096875258406992893633759881276124905556507972311 + ); + + let p3_affine = bjj_affine.add(p1_affine, p2_affine); + assert(p3_affine.eq(Gaffine::new( + 7916061937171219682591368294088513039687205273691143098332585753343424131937, 14035240266687799601661095864649209771790948434046947201833777492504781204499 + ))); // Test scalar multiplication let p4_affine = bjj_affine.mul(2, p1_affine); - assert( - p4_affine.eq(Gaffine::new( - 6890855772600357754907169075114257697580319025794532037257385534741338397365, - 4338620300185947561074059802482547481416142213883829469920100239455078257889 - )) - ); - assert(p4_affine.eq(bjj_affine.bit_mul([0,1], p1_affine))); + assert(p4_affine.eq(Gaffine::new( + 6890855772600357754907169075114257697580319025794532037257385534741338397365, + 4338620300185947561074059802482547481416142213883829469920100239455078257889, + ))); + assert(p4_affine.eq(bjj_affine.bit_mul([0, 1], p1_affine))); // Test subtraction let p5_affine = bjj_affine.subtract(p3_affine, p3_affine); assert(p5_affine.eq(Gaffine::zero())); // Check that these points are on the curve - assert( + assert( bjj_affine.contains(bjj_affine.gen) & bjj_affine.contains(p1_affine) & bjj_affine.contains(p2_affine) & @@ -56,7 +56,6 @@ fn main() { // Test CurveGroup equivalents let bjj = bjj_affine.into_group(); // Baby Jubjub - let p1 = p1_affine.into_group(); let p2 = p2_affine.into_group(); let p3 = p3_affine.into_group(); @@ -68,21 +67,14 @@ fn main() { // Test scalar multiplication assert(p4.eq(bjj.mul(2, p1))); - assert(p4.eq(bjj.bit_mul([0,1], p1))); + assert(p4.eq(bjj.bit_mul([0, 1], p1))); // Test subtraction assert(G::zero().eq(bjj.subtract(p3, p3))); assert(p5.eq(G::zero())); // Check that these points are on the curve - assert( - bjj.contains(bjj.gen) & - bjj.contains(p1) & - bjj.contains(p2) & - bjj.contains(p3) & - bjj.contains(p4) & - bjj.contains(p5) - ); + assert(bjj.contains(bjj.gen) & bjj.contains(p1) & bjj.contains(p2) & bjj.contains(p3) & bjj.contains(p4) & bjj.contains(p5)); // Test SWCurve equivalents of the above // First the affine representation @@ -95,32 +87,18 @@ fn main() { let p5_swcurve_affine = bjj_affine.map_into_swcurve(p5_affine); // Addition - assert( - p3_swcurve_affine.eq( - bjj_swcurve_affine.add( - p1_swcurve_affine, - p2_swcurve_affine - ) - ) - ); + assert(p3_swcurve_affine.eq(bjj_swcurve_affine.add(p1_swcurve_affine, p2_swcurve_affine))); // Doubling assert(p4_swcurve_affine.eq(bjj_swcurve_affine.mul(2, p1_swcurve_affine))); - assert(p4_swcurve_affine.eq(bjj_swcurve_affine.bit_mul([0,1], p1_swcurve_affine))); + assert(p4_swcurve_affine.eq(bjj_swcurve_affine.bit_mul([0, 1], p1_swcurve_affine))); // Subtraction assert(SWGaffine::zero().eq(bjj_swcurve_affine.subtract(p3_swcurve_affine, p3_swcurve_affine))); assert(p5_swcurve_affine.eq(SWGaffine::zero())); // Check that these points are on the curve - assert( - bjj_swcurve_affine.contains(bjj_swcurve_affine.gen) & - bjj_swcurve_affine.contains(p1_swcurve_affine) & - bjj_swcurve_affine.contains(p2_swcurve_affine) & - bjj_swcurve_affine.contains(p3_swcurve_affine) & - bjj_swcurve_affine.contains(p4_swcurve_affine) & - bjj_swcurve_affine.contains(p5_swcurve_affine) - ); + assert(bjj_swcurve_affine.contains(bjj_swcurve_affine.gen) & bjj_swcurve_affine.contains(p1_swcurve_affine) & bjj_swcurve_affine.contains(p2_swcurve_affine) & bjj_swcurve_affine.contains(p3_swcurve_affine) & bjj_swcurve_affine.contains(p4_swcurve_affine) & bjj_swcurve_affine.contains(p5_swcurve_affine)); // Then the CurveGroup representation let bjj_swcurve = bjj.into_swcurve(); @@ -132,25 +110,18 @@ fn main() { let p5_swcurve = bjj.map_into_swcurve(p5); // Addition - assert(p3_swcurve.eq(bjj_swcurve.add(p1_swcurve,p2_swcurve))); + assert(p3_swcurve.eq(bjj_swcurve.add(p1_swcurve, p2_swcurve))); // Doubling assert(p4_swcurve.eq(bjj_swcurve.mul(2, p1_swcurve))); - assert(p4_swcurve.eq(bjj_swcurve.bit_mul([0,1], p1_swcurve))); + assert(p4_swcurve.eq(bjj_swcurve.bit_mul([0, 1], p1_swcurve))); // Subtraction assert(SWG::zero().eq(bjj_swcurve.subtract(p3_swcurve, p3_swcurve))); assert(p5_swcurve.eq(SWG::zero())); // Check that these points are on the curve - assert( - bjj_swcurve.contains(bjj_swcurve.gen) & - bjj_swcurve.contains(p1_swcurve) & - bjj_swcurve.contains(p2_swcurve) & - bjj_swcurve.contains(p3_swcurve) & - bjj_swcurve.contains(p4_swcurve) & - bjj_swcurve.contains(p5_swcurve) - ); + assert(bjj_swcurve.contains(bjj_swcurve.gen) & bjj_swcurve.contains(p1_swcurve) & bjj_swcurve.contains(p2_swcurve) & bjj_swcurve.contains(p3_swcurve) & bjj_swcurve.contains(p4_swcurve) & bjj_swcurve.contains(p5_swcurve)); // Test MontCurve conversions // First the affine representation @@ -167,21 +138,14 @@ fn main() { // Doubling assert(p4_montcurve_affine.eq(bjj_montcurve_affine.mul(2, p1_montcurve_affine))); - assert(p4_montcurve_affine.eq(bjj_montcurve_affine.bit_mul([0,1], p1_montcurve_affine))); + assert(p4_montcurve_affine.eq(bjj_montcurve_affine.bit_mul([0, 1], p1_montcurve_affine))); // Subtraction assert(MGaffine::zero().eq(bjj_montcurve_affine.subtract(p3_montcurve_affine, p3_montcurve_affine))); assert(p5_montcurve_affine.eq(MGaffine::zero())); // Check that these points are on the curve - assert( - bjj_montcurve_affine.contains(bjj_montcurve_affine.gen) & - bjj_montcurve_affine.contains(p1_montcurve_affine) & - bjj_montcurve_affine.contains(p2_montcurve_affine) & - bjj_montcurve_affine.contains(p3_montcurve_affine) & - bjj_montcurve_affine.contains(p4_montcurve_affine) & - bjj_montcurve_affine.contains(p5_montcurve_affine) - ); + assert(bjj_montcurve_affine.contains(bjj_montcurve_affine.gen) & bjj_montcurve_affine.contains(p1_montcurve_affine) & bjj_montcurve_affine.contains(p2_montcurve_affine) & bjj_montcurve_affine.contains(p3_montcurve_affine) & bjj_montcurve_affine.contains(p4_montcurve_affine) & bjj_montcurve_affine.contains(p5_montcurve_affine)); // Then the CurveGroup representation let bjj_montcurve = bjj.into_montcurve(); @@ -197,30 +161,27 @@ fn main() { // Doubling assert(p4_montcurve.eq(bjj_montcurve.mul(2, p1_montcurve))); - assert(p4_montcurve.eq(bjj_montcurve.bit_mul([0,1], p1_montcurve))); + assert(p4_montcurve.eq(bjj_montcurve.bit_mul([0, 1], p1_montcurve))); // Subtraction assert(MG::zero().eq(bjj_montcurve.subtract(p3_montcurve, p3_montcurve))); assert(p5_montcurve.eq(MG::zero())); // Check that these points are on the curve - assert( - bjj_montcurve.contains(bjj_montcurve.gen) & - bjj_montcurve.contains(p1_montcurve) & - bjj_montcurve.contains(p2_montcurve) & - bjj_montcurve.contains(p3_montcurve) & - bjj_montcurve.contains(p4_montcurve) & - bjj_montcurve.contains(p5_montcurve) - ); + assert(bjj_montcurve.contains(bjj_montcurve.gen) & bjj_montcurve.contains(p1_montcurve) & bjj_montcurve.contains(p2_montcurve) & bjj_montcurve.contains(p3_montcurve) & bjj_montcurve.contains(p4_montcurve) & bjj_montcurve.contains(p5_montcurve)); // Elligator 2 map-to-curve let ell2_pt_map = bjj_affine.elligator2_map(27); - assert(ell2_pt_map.eq(MGaffine::new(7972459279704486422145701269802978968072470631857513331988813812334797879121, 8142420778878030219043334189293412482212146646099536952861607542822144507872).into_tecurve())); + assert(ell2_pt_map.eq(MGaffine::new( + 7972459279704486422145701269802978968072470631857513331988813812334797879121, 8142420778878030219043334189293412482212146646099536952861607542822144507872 + ).into_tecurve())); // SWU map-to-curve - let swu_pt_map = bjj_affine.swu_map(5,27); - - assert(swu_pt_map.eq(bjj_affine.map_from_swcurve(SWGaffine::new(2162719247815120009132293839392097468339661471129795280520343931405114293888, 5341392251743377373758788728206293080122949448990104760111875914082289313973)))); - } + let swu_pt_map = bjj_affine.swu_map(5, 27); + + assert(swu_pt_map.eq(bjj_affine.map_from_swcurve(SWGaffine::new( + 2162719247815120009132293839392097468339661471129795280520343931405114293888, 5341392251743377373758788728206293080122949448990104760111875914082289313973 + )))); + } } diff --git a/tooling/nargo_cli/tests/compile_success_empty/generators/src/main.nr b/tooling/nargo_cli/tests/compile_success_empty/generators/src/main.nr index 2f6f90a8c57..9a65c5fd3d2 100644 --- a/tooling/nargo_cli/tests/compile_success_empty/generators/src/main.nr +++ b/tooling/nargo_cli/tests/compile_success_empty/generators/src/main.nr @@ -28,7 +28,7 @@ fn fibonacci_generator() -> fn[(&mut Field, &mut Field)]() -> Field { // we'll be able to un-hardcode the array length if we have the ::<> syntax proposed in https://github.com/noir-lang/noir/issues/2458 fn get_some(generator: fn[Env]() -> Field) -> [Field; 5] { - [0,0,0,0,0].map(|_| generator()) + [0, 0, 0, 0, 0].map(|_| generator()) } fn test_fib() { @@ -44,9 +44,9 @@ fn test_fib() { fn test_counter() { let counter = make_counter(); - assert(counter() == 1); - assert(counter() == 2); - assert(counter() == 3); + assert(counter() == 1); + assert(counter() == 2); + assert(counter() == 3); assert(get_some(counter) == [4, 5, 6, 7, 8]); } diff --git a/tooling/nargo_cli/tests/compile_success_empty/higher_order_fn_selector/src/main.nr b/tooling/nargo_cli/tests/compile_success_empty/higher_order_fn_selector/src/main.nr index e1a13919f4c..d3fd4d5f94a 100644 --- a/tooling/nargo_cli/tests/compile_success_empty/higher_order_fn_selector/src/main.nr +++ b/tooling/nargo_cli/tests/compile_success_empty/higher_order_fn_selector/src/main.nr @@ -1,4 +1,3 @@ - fn g(x: &mut Field) -> () { *x *= 2; } @@ -8,11 +7,7 @@ fn h(x: &mut Field) -> () { } fn selector(flag: &mut bool) -> fn(&mut Field) -> () { - let my_func = if *flag { - g - } else { - h - }; + let my_func = if *flag { g } else { h }; // Flip the flag for the next function call *flag = !(*flag); @@ -20,7 +15,6 @@ fn selector(flag: &mut bool) -> fn(&mut Field) -> () { } fn main() { - let mut flag: bool = true; let mut x: Field = 100; @@ -34,5 +28,4 @@ fn main() { returned_func2(&mut y); assert(y == 300); - } diff --git a/tooling/nargo_cli/tests/compile_success_empty/impl_with_where_clause/Nargo.toml b/tooling/nargo_cli/tests/compile_success_empty/impl_with_where_clause/Nargo.toml new file mode 100644 index 00000000000..070af7d82c2 --- /dev/null +++ b/tooling/nargo_cli/tests/compile_success_empty/impl_with_where_clause/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "impl_with_where_clause" +type = "bin" +authors = [""] +compiler_version = "0.10.5" + +[dependencies] diff --git a/tooling/nargo_cli/tests/compile_success_empty/impl_with_where_clause/src/main.nr b/tooling/nargo_cli/tests/compile_success_empty/impl_with_where_clause/src/main.nr new file mode 100644 index 00000000000..be9fe0b110d --- /dev/null +++ b/tooling/nargo_cli/tests/compile_success_empty/impl_with_where_clause/src/main.nr @@ -0,0 +1,29 @@ + +fn main() { + let array: [Field; 3] = [1, 2, 3]; + assert(array.eq(array)); + + // Ensure this still works if we have to infer the type of the integer literals + let array = [1, 2, 3]; + assert(array.eq(array)); +} + +trait Eq { + fn eq(self, other: Self) -> bool; +} + +impl Eq for [T; N] where T: Eq { + fn eq(self, other: Self) -> bool { + let mut ret = true; + for i in 0 .. self.len() { + ret &= self[i].eq(other[i]); + } + ret + } +} + +impl Eq for Field { + fn eq(self, other: Field) -> bool { + self == other + } +} diff --git a/tooling/nargo_cli/tests/compile_success_empty/intrinsic_die/src/main.nr b/tooling/nargo_cli/tests/compile_success_empty/intrinsic_die/src/main.nr index 08c5cce4034..aef6a13271b 100644 --- a/tooling/nargo_cli/tests/compile_success_empty/intrinsic_die/src/main.nr +++ b/tooling/nargo_cli/tests/compile_success_empty/intrinsic_die/src/main.nr @@ -1,11 +1,9 @@ use dep::std; // This test checks that we perform dead-instruction-elimination on intrinsic functions. - fn main(x: Field) { let bytes = x.to_be_bytes(32); - + let hash = std::hash::pedersen([x]); let _p1 = std::scalar_mul::fixed_base_embedded_curve(x, 0); - } diff --git a/tooling/nargo_cli/tests/compile_success_empty/option/src/main.nr b/tooling/nargo_cli/tests/compile_success_empty/option/src/main.nr index 22229014eef..1f879bd375f 100644 --- a/tooling/nargo_cli/tests/compile_success_empty/option/src/main.nr +++ b/tooling/nargo_cli/tests/compile_success_empty/option/src/main.nr @@ -2,7 +2,6 @@ use dep::std::option::Option; fn main() { let ten = 10; // giving this a name, to ensure that the Option functions work with closures - let none = Option::none(); let some = Option::some(3); diff --git a/tooling/nargo_cli/tests/compile_success_empty/regression_2099/src/main.nr b/tooling/nargo_cli/tests/compile_success_empty/regression_2099/src/main.nr index b96e664dedf..ab74bd2c826 100644 --- a/tooling/nargo_cli/tests/compile_success_empty/regression_2099/src/main.nr +++ b/tooling/nargo_cli/tests/compile_success_empty/regression_2099/src/main.nr @@ -11,11 +11,19 @@ use dep::std::ec::montcurve::curvegroup::Point as MG; fn main() { // Define Baby Jubjub (ERC-2494) parameters in affine representation - let bjj_affine = AffineCurve::new(168700, 168696, Gaffine::new(995203441582195749578291179787384436505546430278305826713579947235728471134,5472060717959818805561601436314318772137091100104008585924551046643952123905)); + let bjj_affine = AffineCurve::new(168700, 168696, Gaffine::new( + 995203441582195749578291179787384436505546430278305826713579947235728471134, 5472060717959818805561601436314318772137091100104008585924551046643952123905 + )); // Test addition - let p1_affine = Gaffine::new(17777552123799933955779906779655732241715742912184938656739573121738514868268, 2626589144620713026669568689430873010625803728049924121243784502389097019475); - let p2_affine = Gaffine::new(16540640123574156134436876038791482806971768689494387082833631921987005038935, 20819045374670962167435360035096875258406992893633759881276124905556507972311); + let p1_affine = Gaffine::new( + 17777552123799933955779906779655732241715742912184938656739573121738514868268, + 2626589144620713026669568689430873010625803728049924121243784502389097019475 + ); + let p2_affine = Gaffine::new( + 16540640123574156134436876038791482806971768689494387082833631921987005038935, + 20819045374670962167435360035096875258406992893633759881276124905556507972311 + ); let _p3_affine = bjj_affine.add(p1_affine, p2_affine); // Test SWCurve equivalents of the above @@ -25,13 +33,8 @@ fn main() { let p1_swcurve_affine = bjj_affine.map_into_swcurve(p1_affine); let p2_swcurve_affine = bjj_affine.map_into_swcurve(p2_affine); - let _p3_swcurve_affine_from_add = bjj_swcurve_affine.add( - p1_swcurve_affine, - p2_swcurve_affine - ); + let _p3_swcurve_affine_from_add = bjj_swcurve_affine.add(p1_swcurve_affine, p2_swcurve_affine); // Check that these points are on the curve - assert( - bjj_swcurve_affine.contains(p1_swcurve_affine) - ); + assert(bjj_swcurve_affine.contains(p1_swcurve_affine)); } diff --git a/tooling/nargo_cli/tests/compile_success_empty/ret_fn_ret_cl/src/main.nr b/tooling/nargo_cli/tests/compile_success_empty/ret_fn_ret_cl/src/main.nr index a368dc58529..13ac6c8d764 100644 --- a/tooling/nargo_cli/tests/compile_success_empty/ret_fn_ret_cl/src/main.nr +++ b/tooling/nargo_cli/tests/compile_success_empty/ret_fn_ret_cl/src/main.nr @@ -17,7 +17,6 @@ fn ret_fn() -> fn(Field) -> Field { // }; // inner_closure // } - fn ret_lambda() -> fn(Field) -> Field { let cl = |z: Field| -> Field { z + 1 @@ -31,7 +30,6 @@ fn main(x : Field) { // let result_closure = ret_closure(); // assert(result_closure(x) == x + 1); - let result_lambda = ret_lambda(); assert(result_lambda(x) == x + 1); } diff --git a/tooling/nargo_cli/tests/compile_success_empty/simple_program_no_body/src/main.nr b/tooling/nargo_cli/tests/compile_success_empty/simple_program_no_body/src/main.nr index b87f216b461..1c66955e126 100644 --- a/tooling/nargo_cli/tests/compile_success_empty/simple_program_no_body/src/main.nr +++ b/tooling/nargo_cli/tests/compile_success_empty/simple_program_no_body/src/main.nr @@ -6,4 +6,4 @@ // // This program will never fail since there are // no assertions being applied. -fn main(_x : Field, _y : pub Field) {} \ No newline at end of file +fn main(_x : Field, _y : pub Field) {} diff --git a/tooling/nargo_cli/tests/compile_success_empty/simple_range/src/main.nr b/tooling/nargo_cli/tests/compile_success_empty/simple_range/src/main.nr index 9a4b9033493..38f95d99f9b 100644 --- a/tooling/nargo_cli/tests/compile_success_empty/simple_range/src/main.nr +++ b/tooling/nargo_cli/tests/compile_success_empty/simple_range/src/main.nr @@ -1,6 +1,6 @@ // Tests a very simple program. // // The features being tested is casting to an integer -fn main(x : Field) { +fn main(x : Field) { let _z = x as u32; -} \ No newline at end of file +} diff --git a/tooling/nargo_cli/tests/compile_success_empty/specialization/src/main.nr b/tooling/nargo_cli/tests/compile_success_empty/specialization/src/main.nr index 32102ad1437..9cd32e0f1eb 100644 --- a/tooling/nargo_cli/tests/compile_success_empty/specialization/src/main.nr +++ b/tooling/nargo_cli/tests/compile_success_empty/specialization/src/main.nr @@ -9,7 +9,7 @@ impl Foo { } fn main() { - let f1: Foo = Foo{}; - let f2: Foo = Foo{}; + let f1: Foo = Foo {}; + let f2: Foo = Foo {}; assert(f1.foo() + f2.foo() == 3); } diff --git a/tooling/nargo_cli/tests/compile_success_empty/str_as_bytes/src/main.nr b/tooling/nargo_cli/tests/compile_success_empty/str_as_bytes/src/main.nr index 6890b156b15..6fdd926ce7f 100644 --- a/tooling/nargo_cli/tests/compile_success_empty/str_as_bytes/src/main.nr +++ b/tooling/nargo_cli/tests/compile_success_empty/str_as_bytes/src/main.nr @@ -2,17 +2,17 @@ use dep::std; fn main() { let a = "hello"; let b = a.as_bytes(); - assert(b[0]==104); - assert(b[1]==101); - assert(b[2]==108); - assert(b[3]==108); - assert(b[4]==111); - assert(b.len()==5); + assert(b[0] == 104); + assert(b[1] == 101); + assert(b[2] == 108); + assert(b[3] == 108); + assert(b[4] == 111); + assert(b.len() == 5); let mut c = a.as_bytes_vec(); - assert(c.get(0)==104); - assert(c.get(1)==101); - assert(c.get(2)==108); - assert(c.get(3)==108); - assert(c.get(4)==111); - assert(c.len()==5); + assert(c.get(0) == 104); + assert(c.get(1) == 101); + assert(c.get(2) == 108); + assert(c.get(3) == 108); + assert(c.get(4) == 111); + assert(c.len() == 5); } diff --git a/tooling/nargo_cli/tests/compile_success_empty/to_bits/src/main.nr b/tooling/nargo_cli/tests/compile_success_empty/to_bits/src/main.nr index 65ff9dcac01..5da3ca9e060 100644 --- a/tooling/nargo_cli/tests/compile_success_empty/to_bits/src/main.nr +++ b/tooling/nargo_cli/tests/compile_success_empty/to_bits/src/main.nr @@ -1,4 +1,3 @@ - fn main() { let field = 1000; let be_bits = field.to_be_bits(16); @@ -19,5 +18,4 @@ fn main() { let le_bit = le_bits_x[3-i]; assert(be_bit == le_bit); } - } diff --git a/tooling/nargo_cli/tests/compile_success_empty/trait_generics/Nargo.toml b/tooling/nargo_cli/tests/compile_success_empty/trait_generics/Nargo.toml new file mode 100644 index 00000000000..9da56eebf35 --- /dev/null +++ b/tooling/nargo_cli/tests/compile_success_empty/trait_generics/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "trait_generics" +type = "bin" +authors = [""] +compiler_version = "0.10.5" + +[dependencies] diff --git a/tooling/nargo_cli/tests/compile_success_empty/trait_generics/src/main.nr b/tooling/nargo_cli/tests/compile_success_empty/trait_generics/src/main.nr new file mode 100644 index 00000000000..ccda969f930 --- /dev/null +++ b/tooling/nargo_cli/tests/compile_success_empty/trait_generics/src/main.nr @@ -0,0 +1,45 @@ + +struct Empty {} + +trait Foo { + fn foo(self) -> u32; +} + +impl Foo for Empty { + fn foo(_self: Self) -> u32 { 32 } +} + +impl Foo for Empty { + fn foo(_self: Self) -> u32 { 64 } +} + +fn main() { + let x: Empty = Empty {}; + let y: Empty = Empty {}; + let z = Empty {}; + + assert(x.foo() == 32); + assert(y.foo() == 64); + + // Types matching multiple impls will currently choose + // the first matching one instead of erroring + assert(z.foo() == 32); + + // Ensure we can call a generic impl + let x: u8 = 7; + let y: i8 = 8; + let s2_u8 = S2 { x }; + let s2_i8 = S2 { x: y }; + assert(s2_u8.t2().x == 7); + assert(s2_i8.t2().x == 8); +} + +trait T2 { + fn t2(self) -> Self; +} + +struct S2 { x: T } + +impl T2 for S2 { + fn t2(self) -> Self { self } +} diff --git a/tooling/nargo_cli/tests/compile_success_empty/trait_override_implementation/src/main.nr b/tooling/nargo_cli/tests/compile_success_empty/trait_override_implementation/src/main.nr index f5f01c79ad6..f359937b739 100644 --- a/tooling/nargo_cli/tests/compile_success_empty/trait_override_implementation/src/main.nr +++ b/tooling/nargo_cli/tests/compile_success_empty/trait_override_implementation/src/main.nr @@ -4,9 +4,8 @@ trait Default { fn default(x: Field, y: Field) -> Self; fn method2(x: Field) -> Field { - x + x } - } struct Foo { @@ -20,7 +19,7 @@ impl Default for Foo { } fn method2(x: Field) -> Field { - x * 3 + x * 3 } } @@ -40,32 +39,35 @@ impl F for Bar { fn f3(self) -> Field { 30 } } -impl F for &mut Bar { - fn f1(self) -> Field { 101 } - fn f5(self) -> Field { 505 } -} +// Impls on mutable references are temporarily disabled +// impl F for &mut Bar { +// fn f1(self) -> Field { 101 } +// fn f5(self) -> Field { 505 } +// } fn main(x: Field) { let first = Foo::method2(x); assert(first == 3 * x); let bar = Bar{}; - assert(bar.f1() == 10); - assert(bar.f2() == 2); - assert(bar.f3() == 30); - assert(bar.f4() == 4); - assert(bar.f5() == 50); + assert(bar.f1() == 10, "1"); + assert(bar.f2() == 2, "2"); + assert(bar.f3() == 30, "3"); + assert(bar.f4() == 4, "4"); + assert(bar.f5() == 50, "5"); let mut bar_mut = Bar{}; - assert((&mut bar_mut).f1() == 101); - assert((&mut bar_mut).f2() == 2); - assert((&mut bar_mut).f3() == 3); - assert((&mut bar_mut).f4() == 4); - assert((&mut bar_mut).f5() == 505); - assert(bar_mut.f1() == 10); - assert(bar_mut.f2() == 2); - assert(bar_mut.f3() == 30); - assert(bar_mut.f4() == 4); - assert(bar_mut.f5() == 50); -} \ No newline at end of file + // Impls on mutable references are temporarily disabled + // assert_eq((&mut bar_mut).f1(), 101); + // assert((&mut bar_mut).f2() == 2, "7"); + // assert((&mut bar_mut).f3() == 3, "8"); + // assert((&mut bar_mut).f4() == 4, "9"); + // assert((&mut bar_mut).f5() == 505, "10"); + + assert(bar_mut.f1() == 10, "10"); + assert(bar_mut.f2() == 2, "12"); + assert(bar_mut.f3() == 30, "13"); + assert(bar_mut.f4() == 4, "14"); + assert(bar_mut.f5() == 50, "15"); +} diff --git a/tooling/nargo_cli/tests/compile_success_empty/unconstrained_empty/src/main.nr b/tooling/nargo_cli/tests/compile_success_empty/unconstrained_empty/src/main.nr index 6a3e59c5fed..5e5fb297236 100644 --- a/tooling/nargo_cli/tests/compile_success_empty/unconstrained_empty/src/main.nr +++ b/tooling/nargo_cli/tests/compile_success_empty/unconstrained_empty/src/main.nr @@ -1,2 +1 @@ -unconstrained fn main() { -} +unconstrained fn main() {} diff --git a/tooling/nargo_cli/tests/compile_success_empty/unit/src/main.nr b/tooling/nargo_cli/tests/compile_success_empty/unit/src/main.nr index 2cb1f7d7c66..603829ec6db 100644 --- a/tooling/nargo_cli/tests/compile_success_empty/unit/src/main.nr +++ b/tooling/nargo_cli/tests/compile_success_empty/unit/src/main.nr @@ -9,6 +9,10 @@ fn main() { } fn f1() {} -fn f2() { () } +fn f2() { + () +} fn f3() -> () {} -fn f4() -> () { () } +fn f4() -> () { + () +} diff --git a/tooling/nargo_cli/tests/compile_success_empty/unused_variables/src/main.nr b/tooling/nargo_cli/tests/compile_success_empty/unused_variables/src/main.nr index d0ffa8a0d91..5c27340caca 100644 --- a/tooling/nargo_cli/tests/compile_success_empty/unused_variables/src/main.nr +++ b/tooling/nargo_cli/tests/compile_success_empty/unused_variables/src/main.nr @@ -1,3 +1 @@ -fn main(x : Field, y : pub Field) { - -} \ No newline at end of file +fn main(x : Field, y : pub Field) {} diff --git a/tooling/nargo_cli/tests/execution_success/1327_concrete_in_generic/src/main.nr b/tooling/nargo_cli/tests/execution_success/1327_concrete_in_generic/src/main.nr index 368d4811e45..5d139111c53 100644 --- a/tooling/nargo_cli/tests/execution_success/1327_concrete_in_generic/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/1327_concrete_in_generic/src/main.nr @@ -58,9 +58,7 @@ fn d_method(input: D) -> Field { } fn get_d_method_interface() -> MethodInterface { - MethodInterface { - some_method_on_t_d: d_method, - } + MethodInterface { some_method_on_t_d: d_method } } // --- diff --git a/tooling/nargo_cli/tests/execution_success/2_div/src/main.nr b/tooling/nargo_cli/tests/execution_success/2_div/src/main.nr index ff0dee755cc..e16cc5be229 100644 --- a/tooling/nargo_cli/tests/execution_success/2_div/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/2_div/src/main.nr @@ -2,6 +2,6 @@ fn main(mut x: u32, y: u32, z: u32) { let a = x % y; assert(x / y == z); - assert(a == x - z*y); + assert(a == x - z * y); assert((50 as u64) % (9 as u64) == 5); } diff --git a/tooling/nargo_cli/tests/execution_success/3_add/src/main.nr b/tooling/nargo_cli/tests/execution_success/3_add/src/main.nr index 2884415b81a..480348dc1cf 100644 --- a/tooling/nargo_cli/tests/execution_success/3_add/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/3_add/src/main.nr @@ -4,5 +4,5 @@ fn main(mut x: u32, y: u32, z: u32) { assert(x == z); x *= 8; - assert(x>9); + assert(x > 9); } diff --git a/tooling/nargo_cli/tests/execution_success/6/src/main.nr b/tooling/nargo_cli/tests/execution_success/6/src/main.nr index 8b350de16c1..152d3da40bd 100644 --- a/tooling/nargo_cli/tests/execution_success/6/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/6/src/main.nr @@ -14,7 +14,7 @@ fn main(x: [u8; 5], result: pub [u8; 32]) { digest = std::hash::sha256(x); assert(digest == result); - let y = [12,45,78,41]; + let y = [12, 45, 78, 41]; let h = std::hash::mimc_bn254(y); - assert(h == 18226366069841799622585958305961373004333097209608110160936134895615261821931); + assert(h == 18226366069841799622585958305961373004333097209608110160936134895615261821931); } diff --git a/tooling/nargo_cli/tests/execution_success/7_function/src/main.nr b/tooling/nargo_cli/tests/execution_success/7_function/src/main.nr index 8fd85fa31cf..32926171098 100644 --- a/tooling/nargo_cli/tests/execution_success/7_function/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/7_function/src/main.nr @@ -49,7 +49,7 @@ fn test3(x: [u8; 3]) -> [u8; 3] { } fn test_multiple(x: u32, y: u32) -> (u32, u32) { - (y,x) + (y, x) } fn test_multiple2() -> my_struct { @@ -71,11 +71,11 @@ struct my2 { } fn test_multiple4(s: my_struct) { - assert(s.a == s.b+2); + assert(s.a == s.b + 2); } fn test_multiple5(a: (u32, u32)) { - assert(a.0 == a.1+2); + assert(a.0 == a.1 + 2); } fn test_multiple6(a: my2, b: my_struct, c: (my2, my_struct)) { @@ -93,20 +93,17 @@ fn bar() -> [Field; 1] { } fn main(x: u32 , y: u32 , a: Field, arr1: [u32; 9], arr2: [u32; 9]) { - let mut ss: my_struct = my_struct { b: x, a: x+2, }; + let mut ss: my_struct = my_struct { b: x, a: x + 2 }; test_multiple4(ss); - test_multiple5((ss.a,ss.b)); - let my = my2 { - aa: ss, - bb: ss, - }; + test_multiple5((ss.a, ss.b)); + let my = my2 { aa: ss, bb: ss }; ss.a = 61; - test_multiple6(my, ss, (my,ss)); + test_multiple6(my, ss, (my, ss)); let my_block = { let mut ab = f2(a); ab = ab + a; - (x,ab) + (x, ab) }; assert(my_block.1 == 4); diff --git a/tooling/nargo_cli/tests/execution_success/array_dynamic/src/main.nr b/tooling/nargo_cli/tests/execution_success/array_dynamic/src/main.nr index 14b6f7d3b85..dd732f37e9b 100644 --- a/tooling/nargo_cli/tests/execution_success/array_dynamic/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/array_dynamic/src/main.nr @@ -1,18 +1,18 @@ fn main(x: [u32; 5], mut z: u32, t: u32, index: [Field;5], index2: [Field;5], offset: Field, sublen: Field) { - let idx = (z - 5*t - 5) as Field; + let idx = (z - 5 * t - 5) as Field; //dynamic array test dyn_array(x, idx, idx - 3); //regression for issue 1283 let mut s = 0; - let x3 = [246,159,32,176,8]; + let x3 = [246, 159, 32, 176, 8]; for i in 0..5 { s += x3[index[i]]; } - assert(s!=0); + assert(s != 0); if 3 < (sublen as u32) { - assert(index[offset + 3] == index2[3]); + assert(index[offset + 3] == index2[3]); } } diff --git a/tooling/nargo_cli/tests/execution_success/array_eq/src/main.nr b/tooling/nargo_cli/tests/execution_success/array_eq/src/main.nr index d1771ed91a6..5bbd595898c 100644 --- a/tooling/nargo_cli/tests/execution_success/array_eq/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/array_eq/src/main.nr @@ -1,4 +1,4 @@ // Simple example of checking where two arrays are equal fn main(a: [Field; 32], b: [Field; 32]) { - assert(a == b); + assert(a == b); } diff --git a/tooling/nargo_cli/tests/execution_success/array_neq/src/main.nr b/tooling/nargo_cli/tests/execution_success/array_neq/src/main.nr index be734dea368..a3e51dc5066 100644 --- a/tooling/nargo_cli/tests/execution_success/array_neq/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/array_neq/src/main.nr @@ -1,4 +1,4 @@ // Simple example of checking where two arrays are different fn main(a: [Field; 32], b: [Field; 32]) { - assert(a != b); + assert(a != b); } diff --git a/tooling/nargo_cli/tests/execution_success/bit_and/src/main.nr b/tooling/nargo_cli/tests/execution_success/bit_and/src/main.nr index 35671abea63..8b5074d5a24 100644 --- a/tooling/nargo_cli/tests/execution_success/bit_and/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/bit_and/src/main.nr @@ -3,7 +3,7 @@ fn main(x : Field, y : Field) { let x_as_u8 = x as u8; let y_as_u8 = y as u8; - + assert((x_as_u8 & y_as_u8) == x_as_u8); //bitwise and with 1 bit: diff --git a/tooling/nargo_cli/tests/execution_success/bit_shifts_comptime/src/main.nr b/tooling/nargo_cli/tests/execution_success/bit_shifts_comptime/src/main.nr index 209861e74fa..f46c7ea1ea8 100644 --- a/tooling/nargo_cli/tests/execution_success/bit_shifts_comptime/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/bit_shifts_comptime/src/main.nr @@ -3,21 +3,21 @@ fn main(x: u64) { let three: u64 = 3; // shifts on constant values - assert(two << 2 == 8); - assert((two << 3) / 8 == two); - assert((three >> 1) == 1); + assert(two << 2 == 8); + assert((two << 3) / 8 == two); + assert((three >> 1) == 1); // shifts on runtime values - assert(x << 1 == 128); - assert(x >> 2 == 16); + assert(x << 1 == 128); + assert(x >> 2 == 16); regression_2250(); } fn regression_2250() { let a: u1 = 1 >> 1; - assert(a == 0); + assert(a == 0); let b: u32 = 1 >> 32; - assert(b == 0); + assert(b == 0); } diff --git a/tooling/nargo_cli/tests/execution_success/bit_shifts_runtime/src/main.nr b/tooling/nargo_cli/tests/execution_success/bit_shifts_runtime/src/main.nr index f4d9c3916a6..b415a573ad5 100644 --- a/tooling/nargo_cli/tests/execution_success/bit_shifts_runtime/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/bit_shifts_runtime/src/main.nr @@ -1,9 +1,9 @@ fn main(x: u64, y: u64) { // runtime shifts on compile-time known values - assert(64 << y == 128); - assert(64 >> y == 32); + assert(64 << y == 128); + assert(64 >> y == 32); // runtime shifts on runtime values - assert(x << y == 128); - assert(x >> y == 32); + assert(x << y == 128); + assert(x >> y == 32); } diff --git a/tooling/nargo_cli/tests/execution_success/brillig_conditional/src/main.nr b/tooling/nargo_cli/tests/execution_success/brillig_conditional/src/main.nr index a3d92ab58e9..e77d4d9479e 100644 --- a/tooling/nargo_cli/tests/execution_success/brillig_conditional/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/brillig_conditional/src/main.nr @@ -6,9 +6,5 @@ fn main(x: Field) { } unconstrained fn conditional(x : bool) -> Field { - if x { - 4 - }else { - 5 - } + if x { 4 } else { 5 } } diff --git a/tooling/nargo_cli/tests/execution_success/brillig_keccak/src/main.nr b/tooling/nargo_cli/tests/execution_success/brillig_keccak/src/main.nr index 46c2a59f6b6..38258af0adf 100644 --- a/tooling/nargo_cli/tests/execution_success/brillig_keccak/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/brillig_keccak/src/main.nr @@ -11,13 +11,13 @@ fn main(x: Field, result: [u8; 32]) { //#1399: variable meesage size let message_size = 4; - let hash_a = keccak256([1,2,3,4], message_size); - let hash_b = keccak256([1,2,3,4,0,0,0,0], message_size); + let hash_a = keccak256([1, 2, 3, 4], message_size); + let hash_b = keccak256([1, 2, 3, 4, 0, 0, 0, 0], message_size); assert(hash_a == hash_b); let message_size_big = 8; - let hash_c = keccak256([1,2,3,4,0,0,0,0], message_size_big); + let hash_c = keccak256([1, 2, 3, 4, 0, 0, 0, 0], message_size_big); assert(hash_a != hash_c); } diff --git a/tooling/nargo_cli/tests/execution_success/brillig_nested_arrays/src/main.nr b/tooling/nargo_cli/tests/execution_success/brillig_nested_arrays/src/main.nr index e182585f5e5..d0a60ac0a58 100644 --- a/tooling/nargo_cli/tests/execution_success/brillig_nested_arrays/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/brillig_nested_arrays/src/main.nr @@ -13,16 +13,16 @@ unconstrained fn access_nested(notes: [MyNote; 2], x: Field, y: Field) -> Field } unconstrained fn create_inside_brillig(x: Field, y: Field) { - let header = Header { params: [1, 2, 3]}; - let note0 = MyNote { array: [1, 2], plain : 3, header }; - let note1 = MyNote { array: [4, 5], plain : 6, header }; + let header = Header { params: [1, 2, 3] }; + let note0 = MyNote { array: [1, 2], plain: 3, header }; + let note1 = MyNote { array: [4, 5], plain: 6, header }; assert(access_nested([note0, note1], x, y) == (2 + 4 + 3 + 1)); } fn main(x: Field, y: Field) { - let header = Header { params: [1, 2, 3]}; - let note0 = MyNote { array: [1, 2], plain : 3, header }; - let note1 = MyNote { array: [4, 5], plain : 6, header }; + let header = Header { params: [1, 2, 3] }; + let note0 = MyNote { array: [1, 2], plain: 3, header }; + let note1 = MyNote { array: [4, 5], plain: 6, header }; create_inside_brillig(x, y); assert(access_nested([note0, note1], x, y) == (2 + 4 + 3 + 1)); diff --git a/tooling/nargo_cli/tests/execution_success/brillig_pedersen/Prover.toml b/tooling/nargo_cli/tests/execution_success/brillig_pedersen/Prover.toml index 2fb3b1e1abf..db1ebdf6c51 100644 --- a/tooling/nargo_cli/tests/execution_success/brillig_pedersen/Prover.toml +++ b/tooling/nargo_cli/tests/execution_success/brillig_pedersen/Prover.toml @@ -2,5 +2,6 @@ x = "0" y = "1" salt = "42" -out_x = "0x0c5e1ddecd49de44ed5e5798d3f6fb7c71fe3d37f5bee8664cf88a445b5ba0af" -out_y = "0x230294a041e26fe80b827c2ef5cb8784642bbaa83842da2714d62b1f3c4f9752" \ No newline at end of file +out_x = "0x054aa86a73cb8a34525e5bbed6e43ba1198e860f5f3950268f71df4591bde402" +out_y = "0x209dcfbf2cfb57f9f6046f44d71ac6faf87254afc7407c04eb621a6287cac126" +out_hash = "0x0d98561fb02ca04d00801dfdc118b2a24cea0351963587712a28d368041370e1" diff --git a/tooling/nargo_cli/tests/execution_success/brillig_pedersen/src/main.nr b/tooling/nargo_cli/tests/execution_success/brillig_pedersen/src/main.nr index 24de7f0ac83..5aecf4fb84e 100644 --- a/tooling/nargo_cli/tests/execution_success/brillig_pedersen/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/brillig_pedersen/src/main.nr @@ -1,11 +1,15 @@ use dep::std; -unconstrained fn main(x: Field, y: Field, salt: Field, out_x: Field, out_y: Field ) { +unconstrained fn main(x: Field, y: Field, salt: Field, out_x: Field, out_y: Field, out_hash: Field) { let res = std::hash::pedersen_with_separator([x, y], 0); assert(res[0] == out_x); assert(res[1] == out_y); + let res_hash = std::hash::pedersen_hash_with_separator([x, y], 0); + assert_eq(res_hash, out_hash); - let raw_data = [x,y]; + assert(res_hash != res[0]); + + let raw_data = [x, y]; let mut state = 0; for i in 0..(2 as u32) { state = state * 8 + raw_data[i]; diff --git a/tooling/nargo_cli/tests/execution_success/brillig_recursion/src/main.nr b/tooling/nargo_cli/tests/execution_success/brillig_recursion/src/main.nr index 43d1034fbbf..ebfc48c9198 100644 --- a/tooling/nargo_cli/tests/execution_success/brillig_recursion/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/brillig_recursion/src/main.nr @@ -6,9 +6,5 @@ fn main(x: u32) { } unconstrained fn fibonacci(x : u32) -> u32 { - if x <= 1 { - x - } else { - fibonacci(x - 1) + fibonacci(x - 2) - } + if x <= 1 { x } else { fibonacci(x - 1) + fibonacci(x - 2) } } diff --git a/tooling/nargo_cli/tests/execution_success/brillig_references/src/main.nr b/tooling/nargo_cli/tests/execution_success/brillig_references/src/main.nr index 46372940bc8..4118e4216a1 100644 --- a/tooling/nargo_cli/tests/execution_success/brillig_references/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/brillig_references/src/main.nr @@ -18,12 +18,7 @@ unconstrained fn main(mut x: Field) { assert(**nested_allocations.y == 1); // Test nested struct allocations with a mutable reference to an array. - let mut c = C { - foo: 0, - bar: &mut C2 { - array: &mut [1, 2], - }, - }; + let mut c = C { foo: 0, bar: &mut C2 { array: &mut [1, 2] } }; *c.bar.array = [3, 4]; let arr: [Field; 2] = *c.bar.array; assert(arr[0] == 3); diff --git a/tooling/nargo_cli/tests/execution_success/brillig_scalar_mul/src/main.nr b/tooling/nargo_cli/tests/execution_success/brillig_scalar_mul/src/main.nr index b95a8d4ef4a..c85bc433a76 100644 --- a/tooling/nargo_cli/tests/execution_success/brillig_scalar_mul/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/brillig_scalar_mul/src/main.nr @@ -15,8 +15,8 @@ unconstrained fn main( priv_key = b; pub_x = b_pub_x; pub_y = b_pub_y; - } + } let res = std::scalar_mul::fixed_base_embedded_curve(priv_key, 0); - assert(res[0] == pub_x); - assert(res[1] == pub_y); + assert(res[0] == pub_x); + assert(res[1] == pub_y); } diff --git a/tooling/nargo_cli/tests/execution_success/brillig_schnorr/Prover.toml b/tooling/nargo_cli/tests/execution_success/brillig_schnorr/Prover.toml index 5fe6bd2546f..2faf2018e07 100644 --- a/tooling/nargo_cli/tests/execution_success/brillig_schnorr/Prover.toml +++ b/tooling/nargo_cli/tests/execution_success/brillig_schnorr/Prover.toml @@ -1,10 +1,70 @@ -message = [0,1,2,3,4,5,6,7,8,9] +message = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] message_field = "0x010203040506070809" -pub_key_x = "0x17cbd3ed3151ccfd170efe1d54280a6a4822640bf5c369908ad74ea21518a9c5" -pub_key_y = "0x0e0456e3795c1a31f20035b741cd6158929eeccd320d299cfcac962865a6bc74" +pub_key_x = "0x04b260954662e97f00cab9adb773a259097f7a274b83b113532bce27fa3fb96a" +pub_key_y = "0x2fd51571db6c08666b0edfbfbc57d432068bccd0110a39b166ab243da0037197" signature = [ - 5, 202, 31, 146, 81, 242, 246, 69, 43, 107, 249, 153, 198, 44, 14, 111, 191, 121, 137, 166, - 160, 103, 18, 181, 243, 233, 226, 95, 67, 16, 37, 128, 85, 76, 19, 253, 30, 77, 192, 53, 138, - 205, 69, 33, 236, 163, 83, 194, 84, 137, 184, 221, 176, 121, 179, 27, 63, 70, 54, 16, 176, - 250, 39, 239, -] \ No newline at end of file + 1, + 13, + 119, + 112, + 212, + 39, + 233, + 41, + 84, + 235, + 255, + 93, + 245, + 172, + 186, + 83, + 157, + 253, + 76, + 77, + 33, + 128, + 178, + 15, + 214, + 67, + 105, + 107, + 177, + 234, + 77, + 48, + 27, + 237, + 155, + 84, + 39, + 84, + 247, + 27, + 22, + 8, + 176, + 230, + 24, + 115, + 145, + 220, + 254, + 122, + 135, + 179, + 171, + 4, + 214, + 202, + 64, + 199, + 19, + 84, + 239, + 138, + 124, + 12, +] diff --git a/tooling/nargo_cli/tests/execution_success/brillig_schnorr/src/main.nr b/tooling/nargo_cli/tests/execution_success/brillig_schnorr/src/main.nr index 4212839601f..f30299b8a7b 100644 --- a/tooling/nargo_cli/tests/execution_success/brillig_schnorr/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/brillig_schnorr/src/main.nr @@ -12,10 +12,10 @@ unconstrained fn main(message: [u8; 10], message_field: Field, pub_key_x: Field, // Is there ever a situation where someone would want // to ensure that a signature was invalid? // Check that passing a slice as the message is valid - let valid_signature = std::schnorr::verify_signature(pub_key_x,pub_key_y,signature, message_field_bytes); + let valid_signature = std::schnorr::verify_signature(pub_key_x, pub_key_y, signature, message_field_bytes); assert(valid_signature); // Check that passing an array as the message is valid - let valid_signature = std::schnorr::verify_signature(pub_key_x,pub_key_y,signature, message); + let valid_signature = std::schnorr::verify_signature(pub_key_x, pub_key_y, signature, message); assert(valid_signature); } diff --git a/tooling/nargo_cli/tests/execution_success/brillig_to_bytes_integration/src/main.nr b/tooling/nargo_cli/tests/execution_success/brillig_to_bytes_integration/src/main.nr index 775d1e512d9..bc1b96a40b5 100644 --- a/tooling/nargo_cli/tests/execution_success/brillig_to_bytes_integration/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/brillig_to_bytes_integration/src/main.nr @@ -6,7 +6,7 @@ unconstrained fn main(x : Field, _y: Field) { let be_byte_array = y.to_be_bytes(31); // The result of this byte array will be little-endian let le_byte_array = x.to_le_bytes(31); - + assert(le_byte_array[0] == 60); assert(le_byte_array[0] == be_byte_array[30]); assert(le_byte_array[1] == be_byte_array[29]); diff --git a/tooling/nargo_cli/tests/execution_success/conditional_1/src/main.nr b/tooling/nargo_cli/tests/execution_success/conditional_1/src/main.nr index 176106ba890..bd29f135082 100644 --- a/tooling/nargo_cli/tests/execution_success/conditional_1/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/conditional_1/src/main.nr @@ -40,7 +40,7 @@ fn main(a: u32, mut c: [u32; 4], x: [u8; 5], result: pub [u8; 32]) { //Test case for conditional with arrays from function parameters - let b = sort([1,2,3,4]); + let b = sort([1, 2, 3, 4]); assert(b[0] == 1); if a == 0 { @@ -51,7 +51,7 @@ fn main(a: u32, mut c: [u32; 4], x: [u8; 5], result: pub [u8; 32]) { c[0] = 1; c[1] = c[2] / a + 11 % a; let f1 = a as Field; - assert(10/f1 != 0); + assert(10 / f1 != 0); } assert(c[0] == 3); diff --git a/tooling/nargo_cli/tests/execution_success/conditional_2/src/main.nr b/tooling/nargo_cli/tests/execution_success/conditional_2/src/main.nr index 70237d4b638..c4d60d8b999 100644 --- a/tooling/nargo_cli/tests/execution_success/conditional_2/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/conditional_2/src/main.nr @@ -11,7 +11,7 @@ fn test3 (x: u8) { } fn test4() -> [u32; 4] { - let b: [u32; 4] = [1,2,3,4]; + let b: [u32; 4] = [1, 2, 3, 4]; b } diff --git a/tooling/nargo_cli/tests/execution_success/conditional_regression_421/src/main.nr b/tooling/nargo_cli/tests/execution_success/conditional_regression_421/src/main.nr index 60052c20610..5789afb76db 100644 --- a/tooling/nargo_cli/tests/execution_success/conditional_regression_421/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/conditional_regression_421/src/main.nr @@ -2,13 +2,9 @@ fn main(a: u32, mut c: [u32; 4]) { //Issue reported in #421 if a == c[0] { assert(c[0] == 0); - } else { - if a == c[1] { - assert(c[1] == 0); - } else { - if a == c[2] { - assert(c[2] == 0); - } - } + } else if a == c[1] { + assert(c[1] == 0); + } else if a == c[2] { + assert(c[2] == 0); } } diff --git a/tooling/nargo_cli/tests/execution_success/conditional_regression_661/src/main.nr b/tooling/nargo_cli/tests/execution_success/conditional_regression_661/src/main.nr index efdc1ddbce4..5d8734897d7 100644 --- a/tooling/nargo_cli/tests/execution_success/conditional_regression_661/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/conditional_regression_661/src/main.nr @@ -21,7 +21,7 @@ fn issue_661_foo(array: [u32;4], b:u32) ->[u32;1] { } fn issue_661_bar(a : [u32;4]) ->[u32;4] { - let mut b: [u32; 4] = [0;4]; + let mut b: [u32; 4] = [0; 4]; b[0]=a[0]+1; b } diff --git a/tooling/nargo_cli/tests/execution_success/conditional_regression_short_circuit/src/main.nr b/tooling/nargo_cli/tests/execution_success/conditional_regression_short_circuit/src/main.nr index 57387ed5052..b119619990e 100644 --- a/tooling/nargo_cli/tests/execution_success/conditional_regression_short_circuit/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/conditional_regression_short_circuit/src/main.nr @@ -33,6 +33,6 @@ fn call_intrinsic(x: [u8; 5], result: [u8; 32]) { } fn test4() -> [u32; 4] { - let b: [u32; 4] = [1,2,3,4]; + let b: [u32; 4] = [1, 2, 3, 4]; b } diff --git a/tooling/nargo_cli/tests/execution_success/double_verify_proof/Prover.toml b/tooling/nargo_cli/tests/execution_success/double_verify_proof/Prover.toml index aa7b1e1371c..3e6d996d0e1 100644 --- a/tooling/nargo_cli/tests/execution_success/double_verify_proof/Prover.toml +++ b/tooling/nargo_cli/tests/execution_success/double_verify_proof/Prover.toml @@ -1,6 +1,12 @@ input_aggregation_object = ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"] -key_hash = "0x17a5d2b205c1bf45b015ba33bc2f0beb7fbb36682f31f953b8d4d093c8644be5" -proof = ["0x0000000000000000000000000000000000000000000000000000000000000003","0x0000000000000000000000000000008f66908323784e7c5259f4eefab77ca881","0x0000000000000000000000000000000000109cac7b943f9b737d7b023d4f5d8a","0x000000000000000000000000000000e991d3ac0a68a252bd3cd09cd1b43fe1b4","0x000000000000000000000000000000000014213d346a426777351fdabaa0fa26","0x000000000000000000000000000000e4603692a76af630015380b08d0e13c239","0x0000000000000000000000000000000000149e7046461203c1b72392bb93c262","0x000000000000000000000000000000c27ffc719f223ca6ef5476a5b66f03a4a8","0x000000000000000000000000000000000003718c62098243e3c1f38090e61753","0x000000000000000000000000000000749492aa98716ce5bf7c06e5c2a0a8a528","0x000000000000000000000000000000000018e4c7d33848bccdc3eed924bfaa15","0x0000000000000000000000000000004e10a37f41fd7c4fe32982daa498530d62","0x00000000000000000000000000000000001b76c8c59489c63f11280187109dd7","0x0000000000000000000000000000002a6cd84d3b8537a7c3cb0cb9461f02e4bb","0x0000000000000000000000000000000000197e524fd48ca5ccb30d6c5ffe689d","0x0000000000000000000000000000000013bf25498ce1f51078c06dac450c0325","0x000000000000000000000000000000000018d347b88a0c32e32571deb9b40466","0x00000000000000000000000000000060d496191298eb1b1c2ce18f9a4afcfc55","0x000000000000000000000000000000000024e11b8e8fcb45b8628cb9cc565513","0x00000000000000000000000000000004e976f6d12fff6250eea2d21c570d3d6a","0x00000000000000000000000000000000000967dbd89d2c7dc0121ea71ded7203","0x000000000000000000000000000000d96f810588c0daa43e88d765a3f82ea9b7","0x00000000000000000000000000000000001f69d7015fe6694bd1d4d61049dae9","0x000000000000000000000000000000c539910d0f81a890fa3d996a676db39640","0x000000000000000000000000000000000026d8b64020a669e24f740b4eba633a","0x000000000000000000000000000000c53cc90f99c40eb5d449f38180d9e9c8b6","0x00000000000000000000000000000000001071ddf2bacc2367dfb2c5084b7dd1","0x0000000000000000000000000000001b9791181eb174db1a50d903fa9fea9999","0x0000000000000000000000000000000000118c059d41a95311a5c361c6a9a00d","0x0000000000000000000000000000003caf4ad04551a3ffba19cc6a1fff457370","0x00000000000000000000000000000000001dc4d8be804c5289fbf54183f93149","0x00000000000000000000000000000050766764bb82799df5172043c515956263","0x00000000000000000000000000000000000a5849adbac9c33e53571b29aab672","0x0000000000000000000000000000002edb078e589d44ac93e283680b34adf574","0x000000000000000000000000000000000015e9e187c4fb683ca78d52a2a0301b","0x00000000000000000000000000000048ac0f1db3575ed0f84d61ab6cbdd53d9f","0x00000000000000000000000000000000002ddc4243fbc7104347d29a823194ae","0x00000000000000000000000000000070ad92aeea2bdea4277ffdfa3d3ed93443","0x000000000000000000000000000000000003bad3e3aae806c278094cb682a8e0","0x000000000000000000000000000000fb74b99eb44c80d8f7ba83d7e9e2efa5c0","0x00000000000000000000000000000000002819cc14e399c1dadc4f921e2a58fa","0x000000000000000000000000000000e3938bb3e7866c6499ec44fb72549efca0","0x00000000000000000000000000000000002d8264d5cdc2109da12e1864aca147","0x000000000000000000000000000000b12d7828cacbe86350f0b171b0cb0d1cd4","0x0000000000000000000000000000000000244155cecb315326f05641cac9475c","0x070b059f9471e22eed5a9ea08093dba3c59c941634611884c5f0f1a1a6b93e5c","0x118124ada70b46c7d23a6ca8b90d545f30e028b1689fe5c55c86bf55f42e0401","0x25dca6ad78c03ce1f7783cc39a5ea5ef90b318d5edf4f1367d4451c1db3c113e","0x0d9557b4e661b5c53b84fcb41f05d15c0ca112430db16f56d0ab54032fffe734","0x06aedf13a3b182784f4d64162f4706759f95e42fc8dc17d1b8b5f551dafdc007","0x132f97ab5f1f8817689b17e336125c5273d6970a1b3b0901fd26d193a4d2dce4","0x1b0c9980b5b09343e807d82bad307a06d1bfadcd1fa50be666c2907d31ef43e1","0x1ce7000cb24ecc1f2ff9d9507b2290513fed574a84d893811cb54a3c0bc51ccc","0x2e1df58d36444c2dfda98991847422f56ef66f079d26eb7f8110d0d7c46b2c0c","0x166c2f821be7c3e3e4440980e73770840194f14d003778b7fbcdd2690776255c","0x1ae8390287e2eb117851a5011575ba3801e5ee5c66a8f7432e2a2fb13c276008","0x047c09806bfb272d940d9b802e3e49b40050fd0f66717e8b325c5d4834b13aac","0x08f81300d7f64e5b281b37005c7c936800a1fa1ecce2fd1664b8ba9069627558","0x2ed7260e623b68d580304751341bb72141314b881e9e8dade626bf5cde8a077c","0x23e04c035fd9396ca06cdc0171f24da00287e87b338bf45992e2ea41a589d560","0x285c5583cbd4609a347a7781a030975402d8e58a99fd72e4c53f4789da3b100c","0x2cd85f0437cf87c7c8881301ce6ee1080329e29a006ef16ff79ba4d20eec4ab8","0x12eb74da267caf98c494db16c87f90f510fdca1f8095b40156a6f0bb066e3400","0x2267004535c434df4cbee1a356e48b1f317cb849ac69c3eb94e377d2274f1e08","0x2c9d4ce9d1d8b8cf1cb90cbc3e121f570c8260c53b48ed2070d474d5a6f12c4e","0x2c6c83ffaad6f30da5aa696973ccfbd0337cb7a5e5f9e5fc8e826dce21e8f51c","0x056c23922e9435f93853044ba96a1c12db97f47053987df5187269ce884ec00f","0x09e82d129a8f5d26cc609fcbd7a865c6dbe8f17fba09fc804acec716bcfffabb","0x0e643693068a8454606e3b4c05e6af7adc39ee8f207b7b0b7d2b245ef1b13567","0x12e040137285ab81f47bd6cc3424f92edc8aeb9e86ecf996af8781a726627013","0x00f01a11c2990ecba44568cb7b2bd25edb46f760ed26ff69e6160c86539d8563","0x28a91699dfa4e85e18e8621d39a147a40930701d2d88546e01adb71a1f8e407f","0x000000000000000000000000000000009d7cc0b2d2bdef816f4fb17e7a6f6c08","0x00000000000000000000000000000000bcfc1a7030171f681f2c6e97c61f4e70","0x00000000000000000000000000000000dc7b742d8d704f4ecf092bb111cf30d8","0x13b099dc4869006fde9df04bf36f4c8f08d4491cc6229ac36a98f93214c79b6a","0x008fa95e0d431d617d8d3288fde7f8bbe36492019943e2018564633528575892","0x0fc66c06bdff20dba4dc491d5cd13cc209c4d2d9e29802db665bb397c2a4e754","0x0fe48ae6623efbaadce6d6b75b87be6caa19c2fd4d94a74149ceb6bcb88251e1","0x1bb41738028855cb5e0085edcd62cff208121427ea19a57425a0cf6bb68deb93","0x0fbc646333ddc21ab1a77b01a35973a56d5a617c482a21a231497fd3cc9b74c1","0x19ab9eaa1a902faff2dd9baa19ff00cea9086baa8c28bcdb95f7a3549eaf09b4","0x25e2b7a7643df4d964cd787b593888b00abfe3ce79e8deaa6d68fd1686b84bcb","0x2d134d7eea07414451e54854d61d5b71245434d0638bba9a1184914f65f2521c","0x03df94e38e9eed8586acd277d180d5d515b49d89d37525f871be2ff4552c586c","0x0b102abb146839f073c4a2514e65a8962f48ee8bbd1801e815d9c42d34665ebd","0x000000000000000000000000000000b7a4109cb92b514401fb63667454a9c892","0x0000000000000000000000000000000000016fce7f8ef56fef466636f3fbc3de","0x00000000000000000000000000000005f2d1c401a7aa14ac7e9fce7c21ec2e1a","0x00000000000000000000000000000000000621322c74c5d0da5eb71a4f2b046f","0x00000000000000000000000000000073d774ad7f61b1c1b93800f7838cca6bde","0x00000000000000000000000000000000002d603cc025e6af192394df113d4677","0x00000000000000000000000000000066a2a702b4d4b1a24af9c56cacb18ae4b8","0x00000000000000000000000000000000000124a3c25b427cfb6fca07525c5b8d"] +# key_hash = "0x17a5d2b205c1bf45b015ba33bc2f0beb7fbb36682f31f953b8d4d093c8644be5" +# proof = ["0x0000000000000000000000000000000000000000000000000000000000000003","0x0000000000000000000000000000008f66908323784e7c5259f4eefab77ca881","0x0000000000000000000000000000000000109cac7b943f9b737d7b023d4f5d8a","0x000000000000000000000000000000e991d3ac0a68a252bd3cd09cd1b43fe1b4","0x000000000000000000000000000000000014213d346a426777351fdabaa0fa26","0x000000000000000000000000000000e4603692a76af630015380b08d0e13c239","0x0000000000000000000000000000000000149e7046461203c1b72392bb93c262","0x000000000000000000000000000000c27ffc719f223ca6ef5476a5b66f03a4a8","0x000000000000000000000000000000000003718c62098243e3c1f38090e61753","0x000000000000000000000000000000749492aa98716ce5bf7c06e5c2a0a8a528","0x000000000000000000000000000000000018e4c7d33848bccdc3eed924bfaa15","0x0000000000000000000000000000004e10a37f41fd7c4fe32982daa498530d62","0x00000000000000000000000000000000001b76c8c59489c63f11280187109dd7","0x0000000000000000000000000000002a6cd84d3b8537a7c3cb0cb9461f02e4bb","0x0000000000000000000000000000000000197e524fd48ca5ccb30d6c5ffe689d","0x0000000000000000000000000000000013bf25498ce1f51078c06dac450c0325","0x000000000000000000000000000000000018d347b88a0c32e32571deb9b40466","0x00000000000000000000000000000060d496191298eb1b1c2ce18f9a4afcfc55","0x000000000000000000000000000000000024e11b8e8fcb45b8628cb9cc565513","0x00000000000000000000000000000004e976f6d12fff6250eea2d21c570d3d6a","0x00000000000000000000000000000000000967dbd89d2c7dc0121ea71ded7203","0x000000000000000000000000000000d96f810588c0daa43e88d765a3f82ea9b7","0x00000000000000000000000000000000001f69d7015fe6694bd1d4d61049dae9","0x000000000000000000000000000000c539910d0f81a890fa3d996a676db39640","0x000000000000000000000000000000000026d8b64020a669e24f740b4eba633a","0x000000000000000000000000000000c53cc90f99c40eb5d449f38180d9e9c8b6","0x00000000000000000000000000000000001071ddf2bacc2367dfb2c5084b7dd1","0x0000000000000000000000000000001b9791181eb174db1a50d903fa9fea9999","0x0000000000000000000000000000000000118c059d41a95311a5c361c6a9a00d","0x0000000000000000000000000000003caf4ad04551a3ffba19cc6a1fff457370","0x00000000000000000000000000000000001dc4d8be804c5289fbf54183f93149","0x00000000000000000000000000000050766764bb82799df5172043c515956263","0x00000000000000000000000000000000000a5849adbac9c33e53571b29aab672","0x0000000000000000000000000000002edb078e589d44ac93e283680b34adf574","0x000000000000000000000000000000000015e9e187c4fb683ca78d52a2a0301b","0x00000000000000000000000000000048ac0f1db3575ed0f84d61ab6cbdd53d9f","0x00000000000000000000000000000000002ddc4243fbc7104347d29a823194ae","0x00000000000000000000000000000070ad92aeea2bdea4277ffdfa3d3ed93443","0x000000000000000000000000000000000003bad3e3aae806c278094cb682a8e0","0x000000000000000000000000000000fb74b99eb44c80d8f7ba83d7e9e2efa5c0","0x00000000000000000000000000000000002819cc14e399c1dadc4f921e2a58fa","0x000000000000000000000000000000e3938bb3e7866c6499ec44fb72549efca0","0x00000000000000000000000000000000002d8264d5cdc2109da12e1864aca147","0x000000000000000000000000000000b12d7828cacbe86350f0b171b0cb0d1cd4","0x0000000000000000000000000000000000244155cecb315326f05641cac9475c","0x070b059f9471e22eed5a9ea08093dba3c59c941634611884c5f0f1a1a6b93e5c","0x118124ada70b46c7d23a6ca8b90d545f30e028b1689fe5c55c86bf55f42e0401","0x25dca6ad78c03ce1f7783cc39a5ea5ef90b318d5edf4f1367d4451c1db3c113e","0x0d9557b4e661b5c53b84fcb41f05d15c0ca112430db16f56d0ab54032fffe734","0x06aedf13a3b182784f4d64162f4706759f95e42fc8dc17d1b8b5f551dafdc007","0x132f97ab5f1f8817689b17e336125c5273d6970a1b3b0901fd26d193a4d2dce4","0x1b0c9980b5b09343e807d82bad307a06d1bfadcd1fa50be666c2907d31ef43e1","0x1ce7000cb24ecc1f2ff9d9507b2290513fed574a84d893811cb54a3c0bc51ccc","0x2e1df58d36444c2dfda98991847422f56ef66f079d26eb7f8110d0d7c46b2c0c","0x166c2f821be7c3e3e4440980e73770840194f14d003778b7fbcdd2690776255c","0x1ae8390287e2eb117851a5011575ba3801e5ee5c66a8f7432e2a2fb13c276008","0x047c09806bfb272d940d9b802e3e49b40050fd0f66717e8b325c5d4834b13aac","0x08f81300d7f64e5b281b37005c7c936800a1fa1ecce2fd1664b8ba9069627558","0x2ed7260e623b68d580304751341bb72141314b881e9e8dade626bf5cde8a077c","0x23e04c035fd9396ca06cdc0171f24da00287e87b338bf45992e2ea41a589d560","0x285c5583cbd4609a347a7781a030975402d8e58a99fd72e4c53f4789da3b100c","0x2cd85f0437cf87c7c8881301ce6ee1080329e29a006ef16ff79ba4d20eec4ab8","0x12eb74da267caf98c494db16c87f90f510fdca1f8095b40156a6f0bb066e3400","0x2267004535c434df4cbee1a356e48b1f317cb849ac69c3eb94e377d2274f1e08","0x2c9d4ce9d1d8b8cf1cb90cbc3e121f570c8260c53b48ed2070d474d5a6f12c4e","0x2c6c83ffaad6f30da5aa696973ccfbd0337cb7a5e5f9e5fc8e826dce21e8f51c","0x056c23922e9435f93853044ba96a1c12db97f47053987df5187269ce884ec00f","0x09e82d129a8f5d26cc609fcbd7a865c6dbe8f17fba09fc804acec716bcfffabb","0x0e643693068a8454606e3b4c05e6af7adc39ee8f207b7b0b7d2b245ef1b13567","0x12e040137285ab81f47bd6cc3424f92edc8aeb9e86ecf996af8781a726627013","0x00f01a11c2990ecba44568cb7b2bd25edb46f760ed26ff69e6160c86539d8563","0x28a91699dfa4e85e18e8621d39a147a40930701d2d88546e01adb71a1f8e407f","0x000000000000000000000000000000009d7cc0b2d2bdef816f4fb17e7a6f6c08","0x00000000000000000000000000000000bcfc1a7030171f681f2c6e97c61f4e70","0x00000000000000000000000000000000dc7b742d8d704f4ecf092bb111cf30d8","0x13b099dc4869006fde9df04bf36f4c8f08d4491cc6229ac36a98f93214c79b6a","0x008fa95e0d431d617d8d3288fde7f8bbe36492019943e2018564633528575892","0x0fc66c06bdff20dba4dc491d5cd13cc209c4d2d9e29802db665bb397c2a4e754","0x0fe48ae6623efbaadce6d6b75b87be6caa19c2fd4d94a74149ceb6bcb88251e1","0x1bb41738028855cb5e0085edcd62cff208121427ea19a57425a0cf6bb68deb93","0x0fbc646333ddc21ab1a77b01a35973a56d5a617c482a21a231497fd3cc9b74c1","0x19ab9eaa1a902faff2dd9baa19ff00cea9086baa8c28bcdb95f7a3549eaf09b4","0x25e2b7a7643df4d964cd787b593888b00abfe3ce79e8deaa6d68fd1686b84bcb","0x2d134d7eea07414451e54854d61d5b71245434d0638bba9a1184914f65f2521c","0x03df94e38e9eed8586acd277d180d5d515b49d89d37525f871be2ff4552c586c","0x0b102abb146839f073c4a2514e65a8962f48ee8bbd1801e815d9c42d34665ebd","0x000000000000000000000000000000b7a4109cb92b514401fb63667454a9c892","0x0000000000000000000000000000000000016fce7f8ef56fef466636f3fbc3de","0x00000000000000000000000000000005f2d1c401a7aa14ac7e9fce7c21ec2e1a","0x00000000000000000000000000000000000621322c74c5d0da5eb71a4f2b046f","0x00000000000000000000000000000073d774ad7f61b1c1b93800f7838cca6bde","0x00000000000000000000000000000000002d603cc025e6af192394df113d4677","0x00000000000000000000000000000066a2a702b4d4b1a24af9c56cacb18ae4b8","0x00000000000000000000000000000000000124a3c25b427cfb6fca07525c5b8d"] +# public_inputs = ["0x0000000000000000000000000000000000000000000000000000000000000003"] +# verification_key = ["0x2b337de1c8c14f22ec9b9e2f96afef3652627366f8170a0a948dad4ac1bd5e80","0x0000000000000000000000000000000000000000000000000000000000000008","0x0000000000000000000000000000000000000000000000000000000000000005","0x0000000000000000000000000000000000000000000000000000000000000008","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x00000000000000000000000000000092139c61bae1a44f0fc7689507414be688","0x00000000000000000000000000000000000160ce4e279582f91bde4f03f5e9a2","0x0000000000000000000000000000005dc2d37f658c3b2d60f24740eb13b65d79","0x000000000000000000000000000000000007e3e8a5d98a1177ec85bf88f163a5","0x000000000000000000000000000000dc3035fbd7ff16412a8fd7da587a935298","0x000000000000000000000000000000000023d08e2817ac16990004ed11d8fc66","0x000000000000000000000000000000356a5ad59c646c746a8d09f5d154e47c4f","0x00000000000000000000000000000000000708529196af3c8e16ffa580c26182","0x0000000000000000000000000000002ddfe70eb7a1280596e8e4a804f118a6dd","0x000000000000000000000000000000000013757e15a0905f298303784a161b21","0x000000000000000000000000000000a23a729df796935c7824e3a26be794829b","0x000000000000000000000000000000000005775b6c146c4a59856e869fe5a70e","0x000000000000000000000000000000eef0c9e088fd2d45aa40311082d1f2809b","0x00000000000000000000000000000000001d539ccbfc556d0ad59307a218de65","0x000000000000000000000000000000a2c848beceb6ab7806fd3b88037b8410fc","0x0000000000000000000000000000000000177004deeb1f9d401fd7b1af1a5ac8","0x0000000000000000000000000000002508eb63672a733f20de1a97644be4f540","0x00000000000000000000000000000000000d82d51f2f75d806285fd248c819b8","0x000000000000000000000000000000d002f9100cbba8a29f13b11513c53c59d0","0x000000000000000000000000000000000006cd3b0e3460533b9e5ea2cdc0fcbb","0x000000000000000000000000000000f45ea38a93b2f810c5633ddb54927c1c96","0x000000000000000000000000000000000021791de65f9a28ec7024b1a87ab4f3","0x000000000000000000000000000000926511a0439502c86885a8c6f0327aa7ad","0x000000000000000000000000000000000029fa14a969c5d81ed3abbbfb11220a","0x000000000000000000000000000000b84c3258e8206f560e5b5b18cbeafef87e","0x00000000000000000000000000000000002a910445cd8fc895e5d235cd8ea185","0x000000000000000000000000000000887e67f15e84bcb8507a5064a363f6043b","0x000000000000000000000000000000000014dc6643d801c3ef27c2066b6e2bb4","0x000000000000000000000000000000e38e900b42c314ba803088e8fbf125203f","0x000000000000000000000000000000000020690fd4869db418306046b38161dc","0x0000000000000000000000000000001e2fa856bf7951b8292b1e88185993629c","0x0000000000000000000000000000000000048a85e0bbac7c60ad3d78f601f63c","0x0000000000000000000000000000006f457719495073d3666d77a625aeab0c51","0x00000000000000000000000000000000002623ad892dc62b1fa7d0a650f0d470","0x000000000000000000000000000000dbfcc8a467e021c03b13f74a9f79c3a10c","0x0000000000000000000000000000000000295f6f10976c37bd9c6f96bb7187d5","0x000000000000000000000000000000c13ef9a937cc12420fb38d9ab8e848e85e","0x000000000000000000000000000000000003560a3b334e887532f605c9cb7628","0x0000000000000000000000000000009bcebf08a4599cdda0fb96312d4dc0c7a9","0x000000000000000000000000000000000015adc8bb1e01c835f48959d1237bd6","0x00000000000000000000000000000047762ab839e4ff63c77605a9f383da37c2","0x000000000000000000000000000000000016a8c3c53d89660cf271522cd301fb","0x000000000000000000000000000000f0c8539a0b5f94420a513f9c305b932bfe","0x00000000000000000000000000000000002957ba01d9de5638f808f88a692533","0x000000000000000000000000000000ab17c6189d67d3bf5dd2f3885de0151b6f","0x0000000000000000000000000000000000060d8aa43fdc434d1942263f364d95","0x0000000000000000000000000000005d292333b3adb497f00b4bc32d45229060","0x00000000000000000000000000000000001a1018a66221883639f2898a66f345","0x00000000000000000000000000000006555a806b1993291deba0dc44e2abf431","0x00000000000000000000000000000000000cacff7099a9d5e35a21f4a00b2dc3","0x000000000000000000000000000000f50c11ba95d349c36d143eefd12e494950","0x00000000000000000000000000000000001022e8c5f02d639bc9dd8bc4407f99","0x000000000000000000000000000000c76828795098eda73d50b4b585c60afc60","0x00000000000000000000000000000000002bf09c0ec7011e93888962f2406630","0x00000000000000000000000000000049e5c83a8978d832fb8e144548e3ca1adb","0x00000000000000000000000000000000000e0ec242c2e160a984f61ca5adf5f5","0x0000000000000000000000000000009c5d6e08a6605ab4513748ac0fa017dd1c","0x00000000000000000000000000000000001f54baa07558e5fb055bd9ba49c067","0x0000000000000000000000000000001e1ee7ee29bbb5e4b080c6091c1433ce62","0x000000000000000000000000000000000024aec62a9d9763499267dc98c33428","0x0000000000000000000000000000001a96755946ff16f0d6632365f0eb0ab4d4","0x000000000000000000000000000000000028cf3e22bcd53782ebc3e0490e27e5","0x00000000000000000000000000000043148d7d8c9ba43f2133fab4201435a364","0x0000000000000000000000000000000000234ce541f1f5117dd404cfaf01a229","0x000000000000000000000000000000a7fb95ffb461d9514a1070e2d2403982ef","0x00000000000000000000000000000000003016955028b6390f446c3fd0c5b424","0x00000000000000000000000000000008863c3b7cd7cddc20ba79ce915051c56e","0x000000000000000000000000000000000013ef666111b0be56a235983d397d2a","0x000000000000000000000000000000e3993f465fc9f56e93ac769e597b752c1c","0x0000000000000000000000000000000000217f7c4235161e9a3c16c45b6ca499","0x0000000000000000000000000000008ffa4cd96bc67b0b7df5678271e1114075","0x0000000000000000000000000000000000256467bfcb63d9fdcb5dde397757ad","0x00000000000000000000000000000054e5eb270bb64bde6e6ececadfd8c3236c","0x00000000000000000000000000000000000e52d1bd75812c33c6f3d79ee4b94c","0x000000000000000000000000000000484a2c641dce55bc2dd64ef0cd790a7fea","0x00000000000000000000000000000000000ff417d256be43e73c8b1aa85bdda3","0x0000000000000000000000000000000b72e7b7713ab5da44e0f864182e748a23","0x00000000000000000000000000000000001a221055f1625ad833a44705f5f74e","0x00000000000000000000000000000067a99a34e9b81a17ad001db02e29bcb82a","0x000000000000000000000000000000000018a6c02e398389827568fa960e86e2","0x000000000000000000000000000000bb29f26f9890d6cc6401f4921d5884edca","0x00000000000000000000000000000000000868357b28039385c5a5058b6d358e","0x00000000000000000000000000000036fb6e229dde8edf7ec858b12d7e8be485","0x00000000000000000000000000000000001060afe929554ca473103f5e68193c","0x00000000000000000000000000000015226e07e207744c0857074dcab883af4a","0x00000000000000000000000000000000000b1c02619282755533457230b19b4a","0x0000000000000000000000000000001f2a0277e4807e6e1cbabca21dde5eb5e1","0x00000000000000000000000000000000000d928deafed363659688ed4ccdef52","0x000000000000000000000000000000363f0c994e91cecad25835338edee2294f","0x00000000000000000000000000000000002eea648c8732596b1314fe2a4d2f05","0x000000000000000000000000000000b2671d2ae51d31c1210433c3972bb64578","0x00000000000000000000000000000000000ab49886c2b94bd0bd3f6ed1dbbe2c"] +# proof_b = ["0x0000000000000000000000000000000000000000000000000000000000000003","0x0000000000000000000000000000002ab91b132e624f2a408aa8c9bf31cca8d7","0x000000000000000000000000000000000015ad57528e0f065c820cc5ad4eab81","0x0000000000000000000000000000001acb78b1b6a5c9a6ec8bf2272b463014da","0x0000000000000000000000000000000000117fd65346e04bf3666d2ab3f24c90","0x000000000000000000000000000000aad0adaf9a768ba6a178f804edac5c8943","0x000000000000000000000000000000000004a11c7d31f25c20e3af16f9b01f71","0x0000000000000000000000000000001f0ae9bb921893ce2710148eb1fcd99e39","0x0000000000000000000000000000000000123fda5008d3709f5afeda01de1930","0x000000000000000000000000000000971c2a8d0119097fd82b7a8074a14853f8","0x000000000000000000000000000000000009965b998750710678da7891d8aba6","0x0000000000000000000000000000002d6ef3813ba14a5f5202afed6b1c41de1c","0x000000000000000000000000000000000020366bfdb2f9279c43d66f90dfdf4d","0x00000000000000000000000000000041389f221eadec33e1b87518668c3bc92e","0x00000000000000000000000000000000000d3858169bb0432ab761d4be8ef03e","0x000000000000000000000000000000c1dbfe670dc912cb0fa1a0f633f81a4cef","0x00000000000000000000000000000000000fc0c403e668b0f51e07089082c32f","0x0000000000000000000000000000009a4fba9bf1369f637fd295c8bf795c9d02","0x00000000000000000000000000000000001d6d1e7286ce52401e6ea79d2cfa3d","0x0000000000000000000000000000004762bf7702ffe7a2c147e704280cd50bba","0x0000000000000000000000000000000000205797cdeaeff9a8d5ea4b95d41b1a","0x000000000000000000000000000000b3d43cc863ba8d98f51118c0db70761079","0x00000000000000000000000000000000002d2a3d10381bc6b47a693c1692b1b6","0x000000000000000000000000000000d35a69fb0e68729f71e651799c0d19e9eb","0x00000000000000000000000000000000002ade1dc7741b7f397271c10e596557","0x0000000000000000000000000000001a67b44714687085004e4142f700043298","0x00000000000000000000000000000000001bb7bbb7f45876b1d72e5d20cee106","0x00000000000000000000000000000025f1f1cbf43fad70cba255b37a19e88b0c","0x00000000000000000000000000000000000cc46b215fbd8e4b233cc74aab250b","0x0000000000000000000000000000008168026f51135fc1670664bc50e629917f","0x000000000000000000000000000000000004d822d80ba0c1bcbd4b000573c6f9","0x000000000000000000000000000000d85756249b937277eba3f5dcb89c56e7bb","0x000000000000000000000000000000000019a3a7a5b20dac138d7ddb1d499134","0x0000000000000000000000000000007621614c7ebc31a2177011f9da01668eb3","0x000000000000000000000000000000000024e9beb5d616ab120073170fc431e8","0x00000000000000000000000000000031fbf901896e958fbbed3e5c57aebbdd04","0x0000000000000000000000000000000000005ac0f10fcc255e179a40518875d4","0x0000000000000000000000000000002dab820c019bcca563b7dbdd26974653e9","0x00000000000000000000000000000000001a5655ec1a67f722b14c65d5c2197f","0x0000000000000000000000000000008e277e490196db5c19d09a9034e10c6432","0x000000000000000000000000000000000003f13b1af07db07eec88698d0aaf2a","0x0000000000000000000000000000002d618452e2b4c790d0551ea5863ed62e76","0x00000000000000000000000000000000001a7171e790a433a972d80218fb482d","0x0000000000000000000000000000005669975cd5bf65a739c0a35a8ab9b7963b","0x00000000000000000000000000000000000d27ffb6f00c86a0ce76a8067d1bce","0x03a0054fe9f93ab96e7c7ed6ec1ac641dffd99a1c804ee5db52cf1efa1a12c15","0x059324381c89c12c87d0f6c27963c31647721fdb02c125961da1a21cbfb3ed1c","0x04a5ead891b7c3f30329e6abcf2ac6903c3c1d8e68874f6baf3a6fc00e84533a","0x03c02f6b862734acf9d0c5133f8141b3a008c5499336a588b376a5dd86d9c837","0x1dd26b35c21c584c410df89d1fd549e7f5da9bb4fd290b7c528d92fbd652f5ad","0x2c8e7ef6f7a130769ae74d0f47aeab5c443492ef4b1ed0b3a9d61dfca80cbdda","0x2b074486c21c62e6eccf3191b3ab3c8df0fb98f0c44b9f0e9e2c281b908b83a6","0x149a6d620be135bba6bbfe8ac826df37567c8be78007e47cdcf5d6e4683d339e","0x119fdfd330036bde31af71e43bd5e191460605e4760d08a6e0ebddbdb5abfeeb","0x1713efc63c00b2de4f68e696d9d30c5603963484f4829e716de2796640864b09","0x1bb1862114cda3712c177b1b6bca0ecd9de7723925698aee83dc91ade7078d3e","0x049d965ad8ccf092dcae948491f702779a513db430e6ec7d15fa1847a6814235","0x093b2cb5b199e125b95d290923ee04ef34a27b6861cdd8fa2bf4308f4d02846a","0x2710c6cd6f14f8071976509d1002e915bfc182b843a3967134de380302423c72","0x24ecb2d6c6678496e574a4248fb813bcd289eda1873763e8afd0c23d340a11a8","0x298a49319f347529c22338a921af16346cdb2b55b81e5065c5cada84da8b53dd","0x2e27df8c780165bc9ed1cd2db3a618ac072c6909e9053ce2dbc4f2cc810c9612","0x07350f3a2e23c1ccbde0d39370159060de5b8df40ae7c58d3f9852b371f1adac","0x2fdf8bf8e2fa2acad0f6d6a3f73e7dc516e8e2d167128bf3a560972339122835","0x0d3ec457703c228d4b6cd1635df9d9bde51997d0228edef64d667cbd16f3cb70","0x148320b9ceab1f3be840dc38b0344e7db0755283d1eacf2dd472e99ee0fb473f","0x06febdcf4869a6b89fdeb0805612c53e139afc29d119a54bc3d72dc7de0f1a7b","0x0b9c542a2136974b7c8d4504e809c7b5adec39de020091c8d9d1460f84905cb0","0x1039ea84fa0387de593bd9897a00ca2d483d779232e77e45efcb5e572b119ee5","0x14d780dfd2d0787135ea6e0e0bf7cca4e28eb54663ce6ac305c5769ed192e11a","0x026127746f9cb625c3301bfbc41bc2c67949be75a032b8ceaddd1580378dd846","0x123cf1180af5fdf09444de423947c9a71790f2c85468b51ecc25fb7bf075a0d5","0x000000000000000000000000000000008419a4f769ceb72c3ac28f559331a5df","0x000000000000000000000000000000009e852c5c1891a89b79b64599e3d52d72","0x00000000000000000000000000000000b8f0b3c0c7549a0ab8a9fbde3478b505","0x056af493dda97ae84cdbbf9ce379e35bdd66e1223eebacdc4a6c2c92553604f4","0x023624c49a722bc8dc5d945b4b10be8ed6c608020e65038a470b5a407375c8aa","0x0ed9f8dd445bda548ef08b7a2ff233867c41b72786f98054597833a68cc9b900","0x2cbf3d04669aa3a0dcda95e19da879f36029abe28317f1ee69be28ddef2a0b87","0x284ca7049611e293aa4535dd7841a540996609d541814373b387b00069636f14","0x246a69ce4030b1e8a675eec89960bfe188bd4073f07afe74f7a77c0698c80bc5","0x1bbdab5d007c4743fbcbf3cc89252baf0b0e1b645b977434ccd4e7560d124761","0x210427e70ee1b484bbb0b4e98263faf24a45325236eed618d51dcb1cb3a9f60d","0x1fbc24b0bd5b0b8c514e138317cc332962714dd306b34939768d723d6ea2ca8e","0x1e74217a6bd46293e6eb721cad346b607a9d6953d677bc5a17fd195e299b9f0f","0x1d2c1e441a4db99b7c88d0b6429ca39792c984d4a63c2f7ab96cc07ee4947390","0x00000000000000000000000000000005b1e3524625c466540f3f7468172403cb","0x000000000000000000000000000000000013bb985f9d5562699347b5dfbc441e","0x000000000000000000000000000000f4fb87d7f481bb198aa6237a0c9ffd3c22","0x0000000000000000000000000000000000254c5f1b76e278f4c71cf5e71533dd","0x0000000000000000000000000000005a72a28b51be9c538b4d28b5106b9239b8","0x00000000000000000000000000000000000d02d80e1a73c82cb0dd8af1aabb3f","0x000000000000000000000000000000434c46502fc1c425a72a4717a3e44c3415","0x00000000000000000000000000000000001c8d74d967b9b65ff2772592a15d0e"] + +key_hash = "0x096129b1c6e108252fc5c829c4cc9b7e8f0d1fd9f29c2532b563d6396645e08f" +proof = ["0x0000000000000000000000000000000000000000000000000000000000000003","0x000000000000000000000000000000d62b795bec274279129a71195796825fcc","0x00000000000000000000000000000000000793ab763140f20a68a6bd2721fd74","0x00000000000000000000000000000053141d06d3307b36153f321511199e579c","0x00000000000000000000000000000000000a4b55d6c21f98a9c434911dcb5c67","0x0000000000000000000000000000005f9d324c0abd22cec92d99dbec438e9491","0x0000000000000000000000000000000000240dfafe1b53dc27147cbab14ea893","0x000000000000000000000000000000044a61d3aac32c6931247cf334a19d9611","0x000000000000000000000000000000000003f0f8cf4207bfa85c23ec9f8d0c88","0x00000000000000000000000000000002168a470e39ba2ac266f6b474de12045f","0x000000000000000000000000000000000025791e7d3feab542345c00ec5a30df","0x000000000000000000000000000000dcafd76d4c3640969c80e017b951ef6397","0x00000000000000000000000000000000001d27f75a1256771e88e0c86fc42dbc","0x0000000000000000000000000000007347ae7d2d9d7fc2b8f0baa014ee1fed9f","0x000000000000000000000000000000000018bd927f42bf7caf9555f56f09000d","0x000000000000000000000000000000041f765f83cbe5904c8f453f70a4531d10","0x00000000000000000000000000000000001858aabeeb5331a221419f4fed1c19","0x000000000000000000000000000000d254a54caaedf8287b9af951b2f2611121","0x000000000000000000000000000000000005ab493623c9563cf2e55ba5f18200","0x00000000000000000000000000000014f24cddc1a02440dc63637df8032c8074","0x000000000000000000000000000000000011950c16cef98471b1d78b935195a4","0x000000000000000000000000000000b0340b459e6bd5cc8f031c8654a502897f","0x00000000000000000000000000000000000e1cf3968dac4545a76a2ae58e512c","0x0000000000000000000000000000002adf7218aa06ca0d2c2e600dcc39193a2d","0x00000000000000000000000000000000001302e7e4b0f14749bd885ca25588b6","0x00000000000000000000000000000092009ce4056e79ab815d8cdfd4491138ae","0x000000000000000000000000000000000018af11e853c6cf2f0f6274b0da8133","0x000000000000000000000000000000dd3dc6f49232141718527b3a0e4b26e21d","0x00000000000000000000000000000000001a877853348a8b695c4f9a9aa4ce68","0x000000000000000000000000000000aecfc56ba07155450b368140d6324023b5","0x000000000000000000000000000000000029c11052798c57ece614617d33fcc2","0x000000000000000000000000000000eb106ffc816d16fb84e84b0b61157b2603","0x000000000000000000000000000000000026c3cac16206899a21cb5126841446","0x000000000000000000000000000000a782ed54805fe845068b362b58e2fa34ec","0x00000000000000000000000000000000000cf046a1bfcc666b7f28b572676073","0x000000000000000000000000000000b931c8dda60bb4aca4cc817f5540f1209f","0x000000000000000000000000000000000024ad50c3936fafc3d190e6a4874223","0x000000000000000000000000000000cce90cfbaf5671c8c8652db28a3a9566f7","0x000000000000000000000000000000000003574db9d0f84380c9635660f86354","0x0000000000000000000000000000003eb3e1dc31846a90f721e7a08c6d6dc4f7","0x000000000000000000000000000000000028999a700cd1abae1a288eebb9a91c","0x000000000000000000000000000000c1be4d385b11387e14eb9817050d772f78","0x000000000000000000000000000000000003c56b5bad8b4484c66ac921f1f102","0x000000000000000000000000000000ace245cabf0f00dc7fd253dd8af0377a14","0x0000000000000000000000000000000000107f1731fcf34b364c813599fa1df7","0x035b937d404932b542b706eb810ef4a7dca4566d4dde1ad6a8717f46167ead7e","0x17608cef3dc7960f41cb1295706df663727d45ee598a61e05e989d111449fb65","0x054712a950ad67da3aa860e49e6891f99b586b7f37caff94eb013fdb374b61ee","0x04b755083086c769b7f593e0e48d68dc54be808203351380ca5566a48149d8bb","0x17d7670b0915235f626fdc1d7e1134d2be906ef138d7843384b3ebc23b1d630f","0x064cf544ab5f4e3dab47960502cccc83321fb275068dfbdd3a2fcbc6dddcaa65","0x083338262712e2b66769ea40d9f412b18caa1bc81a51ff5a50b6c41f8c4b3d23","0x0cdd38958cab97defde00f4a5961b6fd676e29d9f2c352f6bb2c68b91f83f8af","0x02c8bdd005c2f43a0a8cbb2744916ce5c322dfa5b23367a829c12699f4036d32","0x25bac73c7e7b659fbea3135b7a0decf9db8dc3045bd2837dae337c64cc722546","0x19eb361aa419d37bce3d2e8b2b7692a02a9559e83d7f3d8fe9169970fbbc2cba","0x2494bd5106d00e05c7ea60e632e9fe03773b7f2c5b662aa37ec512a01f4a0775","0x18c52c2f2c6e7be1d7847c15e452a3a9c64316103d12e4b5b9a82fac4e940ee9","0x0e0342810456ef78f498c1bfa085a5f3cbc06db1f32fabd0ea9ad27dccac1680","0x024c13d6ef56af33ed7164ea8e47ddecc8a487b000d8b1b45edcd3895a503ba2","0x26e0d127f626bd39b55bc5d0c131dbf03fe006dc5c3edc57dda1e629799a4317","0x1b1140061bc52b15c4f5e100729a81968ee79dc03deb966a18850335a8e44a8b","0x1bb76f945199e71d531a89288912087a02dd0e83020e65d671485bf2e5e86e1a","0x29269900859c6d86e404185b415bf3b279cd100f38cfdb0077e8d6a299c4fd35","0x22b5e94bae2f6f0cdb424a3b12c4bf82cec3fb228e012c1974ed457827bbe012","0x18d3543a93249778e7a57936170dae85ffc47c2567f2d0076a32c0bb86fcf10a","0x03721dc2670206cde42a175fd56bcce32cf6cb8801450a8e8e4b3d4e07785973","0x2806db136dd214d3ac1478460855cae6a4324ab45cab35320d104fee26c260e8","0x1c3749f1937082afbbae9375b9be708cf339e1983e57ef4447f36cfa560c685c","0x1067b8cfb90ef08bcb48aea56b2716334241787c2004a95682d68a0685566fd0","0x0f41aee4416398f1d48ffc302403273cddef34a41f98507c53682041d82e51ff","0x10d854c9f0bfbdff7ca91a68f4978e9a79e7b14243d92f465f17bdf88d9f64f8","0x00000000000000000000000000000000018938b11099e0cdc05ddab84a153a97","0x0000000000000000000000000000000001d7dda1471f0dc3b3a3d3438c197982","0x00000000000000000000000000000000022682917da43ab9a6e9cbcece1db86d","0x2453913e6b0f36eab883ac4b0e0604d56aaeb9c55e641135173e63c342f1a660","0x05216c1b58dc43a49d01aaba3113b0e86be450fc17d28016e648e7162a1b67fb","0x152b34845a0222a2b41354c0d395a250d8363dc18748647d85acd89d6934ec56","0x1dfc6e971ce82b7dcda1f7f282713c6e22a8c79258a61209bda69719806da544","0x2968dd8b3af8e3953f1fbbd72f4c49b8270597bb27d4037adc157ac6083bee60","0x1b9425b88a4c7d39b3d75afe66917a9aa1d2055724392bc01fb918d84ff1410e","0x04ab571f236d8e750904dc307dd274003d9130f1a7110e4c1521cfb408877c73","0x2ad84f26fdc5831545272d02b806bb0e6dae44e71f73552c4eb9ff06030748c7","0x020e632b99d325db774b8630fb50b9a4e74d35b7f27d9fc02c65087ee747e42c","0x09a8c5a3171268cb61c02515c01c109889200ed13f415ae54df2078bbb887f92","0x1143281a9451abbb4c34c3fa84e7678c2af2e7ea8c05160a6f7f06988fc91af8","0x000000000000000000000000000000cbda736ca5cf6bc75413c2cc9e28ab0a68","0x00000000000000000000000000000000001ee78c9cc56aa5991062ae2e338587","0x000000000000000000000000000000bc9bfcdebb486f4cb314e681d2cc5f8df6","0x00000000000000000000000000000000000ad538431d04771bca7f633cb659ff","0x000000000000000000000000000000d45b317afcefa466a59bba9e171f1af70c","0x0000000000000000000000000000000000133c50180ea17932e4881124e7a7c6","0x000000000000000000000000000000fc9ed37f543775849f3e84eaa06f77f992","0x00000000000000000000000000000000001372873c9c051d1baff99248b8f70e"] public_inputs = ["0x0000000000000000000000000000000000000000000000000000000000000003"] verification_key = ["0x2b337de1c8c14f22ec9b9e2f96afef3652627366f8170a0a948dad4ac1bd5e80","0x0000000000000000000000000000000000000000000000000000000000000008","0x0000000000000000000000000000000000000000000000000000000000000005","0x0000000000000000000000000000000000000000000000000000000000000008","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x00000000000000000000000000000092139c61bae1a44f0fc7689507414be688","0x00000000000000000000000000000000000160ce4e279582f91bde4f03f5e9a2","0x0000000000000000000000000000005dc2d37f658c3b2d60f24740eb13b65d79","0x000000000000000000000000000000000007e3e8a5d98a1177ec85bf88f163a5","0x000000000000000000000000000000dc3035fbd7ff16412a8fd7da587a935298","0x000000000000000000000000000000000023d08e2817ac16990004ed11d8fc66","0x000000000000000000000000000000356a5ad59c646c746a8d09f5d154e47c4f","0x00000000000000000000000000000000000708529196af3c8e16ffa580c26182","0x0000000000000000000000000000002ddfe70eb7a1280596e8e4a804f118a6dd","0x000000000000000000000000000000000013757e15a0905f298303784a161b21","0x000000000000000000000000000000a23a729df796935c7824e3a26be794829b","0x000000000000000000000000000000000005775b6c146c4a59856e869fe5a70e","0x000000000000000000000000000000eef0c9e088fd2d45aa40311082d1f2809b","0x00000000000000000000000000000000001d539ccbfc556d0ad59307a218de65","0x000000000000000000000000000000a2c848beceb6ab7806fd3b88037b8410fc","0x0000000000000000000000000000000000177004deeb1f9d401fd7b1af1a5ac8","0x0000000000000000000000000000002508eb63672a733f20de1a97644be4f540","0x00000000000000000000000000000000000d82d51f2f75d806285fd248c819b8","0x000000000000000000000000000000d002f9100cbba8a29f13b11513c53c59d0","0x000000000000000000000000000000000006cd3b0e3460533b9e5ea2cdc0fcbb","0x000000000000000000000000000000f45ea38a93b2f810c5633ddb54927c1c96","0x000000000000000000000000000000000021791de65f9a28ec7024b1a87ab4f3","0x000000000000000000000000000000926511a0439502c86885a8c6f0327aa7ad","0x000000000000000000000000000000000029fa14a969c5d81ed3abbbfb11220a","0x000000000000000000000000000000b84c3258e8206f560e5b5b18cbeafef87e","0x00000000000000000000000000000000002a910445cd8fc895e5d235cd8ea185","0x000000000000000000000000000000887e67f15e84bcb8507a5064a363f6043b","0x000000000000000000000000000000000014dc6643d801c3ef27c2066b6e2bb4","0x000000000000000000000000000000e38e900b42c314ba803088e8fbf125203f","0x000000000000000000000000000000000020690fd4869db418306046b38161dc","0x0000000000000000000000000000001e2fa856bf7951b8292b1e88185993629c","0x0000000000000000000000000000000000048a85e0bbac7c60ad3d78f601f63c","0x0000000000000000000000000000006f457719495073d3666d77a625aeab0c51","0x00000000000000000000000000000000002623ad892dc62b1fa7d0a650f0d470","0x000000000000000000000000000000dbfcc8a467e021c03b13f74a9f79c3a10c","0x0000000000000000000000000000000000295f6f10976c37bd9c6f96bb7187d5","0x000000000000000000000000000000c13ef9a937cc12420fb38d9ab8e848e85e","0x000000000000000000000000000000000003560a3b334e887532f605c9cb7628","0x0000000000000000000000000000009bcebf08a4599cdda0fb96312d4dc0c7a9","0x000000000000000000000000000000000015adc8bb1e01c835f48959d1237bd6","0x00000000000000000000000000000047762ab839e4ff63c77605a9f383da37c2","0x000000000000000000000000000000000016a8c3c53d89660cf271522cd301fb","0x000000000000000000000000000000f0c8539a0b5f94420a513f9c305b932bfe","0x00000000000000000000000000000000002957ba01d9de5638f808f88a692533","0x000000000000000000000000000000ab17c6189d67d3bf5dd2f3885de0151b6f","0x0000000000000000000000000000000000060d8aa43fdc434d1942263f364d95","0x0000000000000000000000000000005d292333b3adb497f00b4bc32d45229060","0x00000000000000000000000000000000001a1018a66221883639f2898a66f345","0x00000000000000000000000000000006555a806b1993291deba0dc44e2abf431","0x00000000000000000000000000000000000cacff7099a9d5e35a21f4a00b2dc3","0x000000000000000000000000000000f50c11ba95d349c36d143eefd12e494950","0x00000000000000000000000000000000001022e8c5f02d639bc9dd8bc4407f99","0x000000000000000000000000000000c76828795098eda73d50b4b585c60afc60","0x00000000000000000000000000000000002bf09c0ec7011e93888962f2406630","0x00000000000000000000000000000049e5c83a8978d832fb8e144548e3ca1adb","0x00000000000000000000000000000000000e0ec242c2e160a984f61ca5adf5f5","0x0000000000000000000000000000009c5d6e08a6605ab4513748ac0fa017dd1c","0x00000000000000000000000000000000001f54baa07558e5fb055bd9ba49c067","0x0000000000000000000000000000001e1ee7ee29bbb5e4b080c6091c1433ce62","0x000000000000000000000000000000000024aec62a9d9763499267dc98c33428","0x0000000000000000000000000000001a96755946ff16f0d6632365f0eb0ab4d4","0x000000000000000000000000000000000028cf3e22bcd53782ebc3e0490e27e5","0x00000000000000000000000000000043148d7d8c9ba43f2133fab4201435a364","0x0000000000000000000000000000000000234ce541f1f5117dd404cfaf01a229","0x000000000000000000000000000000a7fb95ffb461d9514a1070e2d2403982ef","0x00000000000000000000000000000000003016955028b6390f446c3fd0c5b424","0x00000000000000000000000000000008863c3b7cd7cddc20ba79ce915051c56e","0x000000000000000000000000000000000013ef666111b0be56a235983d397d2a","0x000000000000000000000000000000e3993f465fc9f56e93ac769e597b752c1c","0x0000000000000000000000000000000000217f7c4235161e9a3c16c45b6ca499","0x0000000000000000000000000000008ffa4cd96bc67b0b7df5678271e1114075","0x0000000000000000000000000000000000256467bfcb63d9fdcb5dde397757ad","0x00000000000000000000000000000054e5eb270bb64bde6e6ececadfd8c3236c","0x00000000000000000000000000000000000e52d1bd75812c33c6f3d79ee4b94c","0x000000000000000000000000000000484a2c641dce55bc2dd64ef0cd790a7fea","0x00000000000000000000000000000000000ff417d256be43e73c8b1aa85bdda3","0x0000000000000000000000000000000b72e7b7713ab5da44e0f864182e748a23","0x00000000000000000000000000000000001a221055f1625ad833a44705f5f74e","0x00000000000000000000000000000067a99a34e9b81a17ad001db02e29bcb82a","0x000000000000000000000000000000000018a6c02e398389827568fa960e86e2","0x000000000000000000000000000000bb29f26f9890d6cc6401f4921d5884edca","0x00000000000000000000000000000000000868357b28039385c5a5058b6d358e","0x00000000000000000000000000000036fb6e229dde8edf7ec858b12d7e8be485","0x00000000000000000000000000000000001060afe929554ca473103f5e68193c","0x00000000000000000000000000000015226e07e207744c0857074dcab883af4a","0x00000000000000000000000000000000000b1c02619282755533457230b19b4a","0x0000000000000000000000000000001f2a0277e4807e6e1cbabca21dde5eb5e1","0x00000000000000000000000000000000000d928deafed363659688ed4ccdef52","0x000000000000000000000000000000363f0c994e91cecad25835338edee2294f","0x00000000000000000000000000000000002eea648c8732596b1314fe2a4d2f05","0x000000000000000000000000000000b2671d2ae51d31c1210433c3972bb64578","0x00000000000000000000000000000000000ab49886c2b94bd0bd3f6ed1dbbe2c"] -proof_b = ["0x0000000000000000000000000000000000000000000000000000000000000003","0x0000000000000000000000000000002ab91b132e624f2a408aa8c9bf31cca8d7","0x000000000000000000000000000000000015ad57528e0f065c820cc5ad4eab81","0x0000000000000000000000000000001acb78b1b6a5c9a6ec8bf2272b463014da","0x0000000000000000000000000000000000117fd65346e04bf3666d2ab3f24c90","0x000000000000000000000000000000aad0adaf9a768ba6a178f804edac5c8943","0x000000000000000000000000000000000004a11c7d31f25c20e3af16f9b01f71","0x0000000000000000000000000000001f0ae9bb921893ce2710148eb1fcd99e39","0x0000000000000000000000000000000000123fda5008d3709f5afeda01de1930","0x000000000000000000000000000000971c2a8d0119097fd82b7a8074a14853f8","0x000000000000000000000000000000000009965b998750710678da7891d8aba6","0x0000000000000000000000000000002d6ef3813ba14a5f5202afed6b1c41de1c","0x000000000000000000000000000000000020366bfdb2f9279c43d66f90dfdf4d","0x00000000000000000000000000000041389f221eadec33e1b87518668c3bc92e","0x00000000000000000000000000000000000d3858169bb0432ab761d4be8ef03e","0x000000000000000000000000000000c1dbfe670dc912cb0fa1a0f633f81a4cef","0x00000000000000000000000000000000000fc0c403e668b0f51e07089082c32f","0x0000000000000000000000000000009a4fba9bf1369f637fd295c8bf795c9d02","0x00000000000000000000000000000000001d6d1e7286ce52401e6ea79d2cfa3d","0x0000000000000000000000000000004762bf7702ffe7a2c147e704280cd50bba","0x0000000000000000000000000000000000205797cdeaeff9a8d5ea4b95d41b1a","0x000000000000000000000000000000b3d43cc863ba8d98f51118c0db70761079","0x00000000000000000000000000000000002d2a3d10381bc6b47a693c1692b1b6","0x000000000000000000000000000000d35a69fb0e68729f71e651799c0d19e9eb","0x00000000000000000000000000000000002ade1dc7741b7f397271c10e596557","0x0000000000000000000000000000001a67b44714687085004e4142f700043298","0x00000000000000000000000000000000001bb7bbb7f45876b1d72e5d20cee106","0x00000000000000000000000000000025f1f1cbf43fad70cba255b37a19e88b0c","0x00000000000000000000000000000000000cc46b215fbd8e4b233cc74aab250b","0x0000000000000000000000000000008168026f51135fc1670664bc50e629917f","0x000000000000000000000000000000000004d822d80ba0c1bcbd4b000573c6f9","0x000000000000000000000000000000d85756249b937277eba3f5dcb89c56e7bb","0x000000000000000000000000000000000019a3a7a5b20dac138d7ddb1d499134","0x0000000000000000000000000000007621614c7ebc31a2177011f9da01668eb3","0x000000000000000000000000000000000024e9beb5d616ab120073170fc431e8","0x00000000000000000000000000000031fbf901896e958fbbed3e5c57aebbdd04","0x0000000000000000000000000000000000005ac0f10fcc255e179a40518875d4","0x0000000000000000000000000000002dab820c019bcca563b7dbdd26974653e9","0x00000000000000000000000000000000001a5655ec1a67f722b14c65d5c2197f","0x0000000000000000000000000000008e277e490196db5c19d09a9034e10c6432","0x000000000000000000000000000000000003f13b1af07db07eec88698d0aaf2a","0x0000000000000000000000000000002d618452e2b4c790d0551ea5863ed62e76","0x00000000000000000000000000000000001a7171e790a433a972d80218fb482d","0x0000000000000000000000000000005669975cd5bf65a739c0a35a8ab9b7963b","0x00000000000000000000000000000000000d27ffb6f00c86a0ce76a8067d1bce","0x03a0054fe9f93ab96e7c7ed6ec1ac641dffd99a1c804ee5db52cf1efa1a12c15","0x059324381c89c12c87d0f6c27963c31647721fdb02c125961da1a21cbfb3ed1c","0x04a5ead891b7c3f30329e6abcf2ac6903c3c1d8e68874f6baf3a6fc00e84533a","0x03c02f6b862734acf9d0c5133f8141b3a008c5499336a588b376a5dd86d9c837","0x1dd26b35c21c584c410df89d1fd549e7f5da9bb4fd290b7c528d92fbd652f5ad","0x2c8e7ef6f7a130769ae74d0f47aeab5c443492ef4b1ed0b3a9d61dfca80cbdda","0x2b074486c21c62e6eccf3191b3ab3c8df0fb98f0c44b9f0e9e2c281b908b83a6","0x149a6d620be135bba6bbfe8ac826df37567c8be78007e47cdcf5d6e4683d339e","0x119fdfd330036bde31af71e43bd5e191460605e4760d08a6e0ebddbdb5abfeeb","0x1713efc63c00b2de4f68e696d9d30c5603963484f4829e716de2796640864b09","0x1bb1862114cda3712c177b1b6bca0ecd9de7723925698aee83dc91ade7078d3e","0x049d965ad8ccf092dcae948491f702779a513db430e6ec7d15fa1847a6814235","0x093b2cb5b199e125b95d290923ee04ef34a27b6861cdd8fa2bf4308f4d02846a","0x2710c6cd6f14f8071976509d1002e915bfc182b843a3967134de380302423c72","0x24ecb2d6c6678496e574a4248fb813bcd289eda1873763e8afd0c23d340a11a8","0x298a49319f347529c22338a921af16346cdb2b55b81e5065c5cada84da8b53dd","0x2e27df8c780165bc9ed1cd2db3a618ac072c6909e9053ce2dbc4f2cc810c9612","0x07350f3a2e23c1ccbde0d39370159060de5b8df40ae7c58d3f9852b371f1adac","0x2fdf8bf8e2fa2acad0f6d6a3f73e7dc516e8e2d167128bf3a560972339122835","0x0d3ec457703c228d4b6cd1635df9d9bde51997d0228edef64d667cbd16f3cb70","0x148320b9ceab1f3be840dc38b0344e7db0755283d1eacf2dd472e99ee0fb473f","0x06febdcf4869a6b89fdeb0805612c53e139afc29d119a54bc3d72dc7de0f1a7b","0x0b9c542a2136974b7c8d4504e809c7b5adec39de020091c8d9d1460f84905cb0","0x1039ea84fa0387de593bd9897a00ca2d483d779232e77e45efcb5e572b119ee5","0x14d780dfd2d0787135ea6e0e0bf7cca4e28eb54663ce6ac305c5769ed192e11a","0x026127746f9cb625c3301bfbc41bc2c67949be75a032b8ceaddd1580378dd846","0x123cf1180af5fdf09444de423947c9a71790f2c85468b51ecc25fb7bf075a0d5","0x000000000000000000000000000000008419a4f769ceb72c3ac28f559331a5df","0x000000000000000000000000000000009e852c5c1891a89b79b64599e3d52d72","0x00000000000000000000000000000000b8f0b3c0c7549a0ab8a9fbde3478b505","0x056af493dda97ae84cdbbf9ce379e35bdd66e1223eebacdc4a6c2c92553604f4","0x023624c49a722bc8dc5d945b4b10be8ed6c608020e65038a470b5a407375c8aa","0x0ed9f8dd445bda548ef08b7a2ff233867c41b72786f98054597833a68cc9b900","0x2cbf3d04669aa3a0dcda95e19da879f36029abe28317f1ee69be28ddef2a0b87","0x284ca7049611e293aa4535dd7841a540996609d541814373b387b00069636f14","0x246a69ce4030b1e8a675eec89960bfe188bd4073f07afe74f7a77c0698c80bc5","0x1bbdab5d007c4743fbcbf3cc89252baf0b0e1b645b977434ccd4e7560d124761","0x210427e70ee1b484bbb0b4e98263faf24a45325236eed618d51dcb1cb3a9f60d","0x1fbc24b0bd5b0b8c514e138317cc332962714dd306b34939768d723d6ea2ca8e","0x1e74217a6bd46293e6eb721cad346b607a9d6953d677bc5a17fd195e299b9f0f","0x1d2c1e441a4db99b7c88d0b6429ca39792c984d4a63c2f7ab96cc07ee4947390","0x00000000000000000000000000000005b1e3524625c466540f3f7468172403cb","0x000000000000000000000000000000000013bb985f9d5562699347b5dfbc441e","0x000000000000000000000000000000f4fb87d7f481bb198aa6237a0c9ffd3c22","0x0000000000000000000000000000000000254c5f1b76e278f4c71cf5e71533dd","0x0000000000000000000000000000005a72a28b51be9c538b4d28b5106b9239b8","0x00000000000000000000000000000000000d02d80e1a73c82cb0dd8af1aabb3f","0x000000000000000000000000000000434c46502fc1c425a72a4717a3e44c3415","0x00000000000000000000000000000000001c8d74d967b9b65ff2772592a15d0e"] \ No newline at end of file +proof_b = ["0x0000000000000000000000000000000000000000000000000000000000000003","0x000000000000000000000000000000f05c69448ca29bdf52076f9b073bb30fed","0x000000000000000000000000000000000028c86bb3e27b4aaaaef126f7df5349","0x00000000000000000000000000000026ae031fc93594375dfc7f3bbe027f97d5","0x000000000000000000000000000000000000dd12c7290fe7f775796a233b8590","0x000000000000000000000000000000c1ee6631704de424d010c5c4ac8293ac49","0x00000000000000000000000000000000002f41818c9aa83f5c8d9bdd128015b9","0x000000000000000000000000000000b50a5801482f7e3a5de8ab3cce0f10b0d3","0x000000000000000000000000000000000022a0bc69c293dbf293b25bc9eef7f8","0x0000000000000000000000000000003b02abf1967ef394154dc15d763135e903","0x00000000000000000000000000000000000d8a2ee46acc6d1ed8d517b56d47c8","0x00000000000000000000000000000039bf0d1b3d8cf9de898f101c626e978d78","0x0000000000000000000000000000000000008faa7df2451a24d291a9b584f1a5","0x000000000000000000000000000000c1dae329ed7adf63a2d89a5f16fb98b6d8","0x00000000000000000000000000000000001ff0bc16fc0bd4aa2d6255690453c2","0x000000000000000000000000000000d12d7589f853a9b472613efa56689beaf1","0x00000000000000000000000000000000002d6fbc798f4403751df6aeee8bedd3","0x0000000000000000000000000000007c1fa069cb17194fecf88db9dd54a4ee36","0x0000000000000000000000000000000000268e026f9814822a42b2d59eec5d24","0x000000000000000000000000000000c3fb56beab774218cd63498fc050a5fd9b","0x00000000000000000000000000000000000071c014d7b5063f005a0bc2ee1af4","0x000000000000000000000000000000ae12b25371c6af42bbe0a85cddd2eaebc7","0x000000000000000000000000000000000026d270e1ffc9c7c344c694dfadda83","0x00000000000000000000000000000080280858c6be461716921caa3c26f3f6f3","0x000000000000000000000000000000000001dcdd3f39e27d0ce6aa5d14dff4c1","0x000000000000000000000000000000080e1d2c913c834ebcf7e0600c076c08fd","0x00000000000000000000000000000000002df3d142217694e65fb7c355d62764","0x000000000000000000000000000000e5e336f3f59d77e500f49771bfbeb12e83","0x000000000000000000000000000000000028fffe08bdc4c0690643d2e1a1275f","0x000000000000000000000000000000db5618b32afc13e18f21b39f3fbede9d11","0x00000000000000000000000000000000001d244818370d43fb7e8bc67e03787b","0x0000000000000000000000000000006bcc1fd3f9f78449ad1df1bc11bc379edd","0x000000000000000000000000000000000009ac9cbb285edbf5b3a973f3f5f1cb","0x000000000000000000000000000000fd885905b6c0fc95bb4dd0b11f6797d4b3","0x000000000000000000000000000000000021f07995cdd835145e19c38127c562","0x000000000000000000000000000000bbbf2b975c2c97ae4b45c4a52059e53ee3","0x000000000000000000000000000000000024158163788841cf4590bbc1e89a90","0x0000000000000000000000000000009aca93d2b1386ea412d4b36ea5bb9894a8","0x00000000000000000000000000000000002532d1d210e8ed4c2f5c00cbaaa475","0x000000000000000000000000000000634a88caa1d77cb6b5fe77cac31458fc31","0x00000000000000000000000000000000000bdf18bae92fce7cfddab5520cac6e","0x000000000000000000000000000000622e9626255170ccec77602c755aa193e1","0x000000000000000000000000000000000001d4edba370e04436a988bad05dada","0x000000000000000000000000000000b52934323a0aec8f803cdaafee2ab7bfb2","0x0000000000000000000000000000000000155312af5e0e25ca9fd61aef9e58ed","0x06270b517855f6f6a608e432883d1d1030a12a1e33022dc142b7728691421da2","0x2af7c794d7b720b25eb1df0afd8c8e3c15b6e518194c3caea7966a5f8210ff04","0x073fe573aeb27d81a5713be93e1365390dcbc3c8e7439ff1d36a84cc014f5642","0x11351b961147431e54535248b58b35cf5cddb9b13827899167617d7a96794d64","0x297c9421c9c3db286770787c35b86bc41583386491b4ae55e5fa81aefa21efc4","0x0f4eeca3ff4a3495f859898937688652d33f9b4dd3e003e12adf15278e0997c3","0x133e3d8b82721d40d919f2326810ba6f07eff3f7d20d86b2bde692a811522019","0x2c502f53c9698b73bb8c8f9b9cf2d705d16a64a7040348b4b39c637a2064316c","0x0cbc1971e1c566cde9d9125c91cdc88e817db182692f836c1a5170a6246eaf73","0x12c47793e7db706c637cd4b4d96d227f569850176b852b1fe8ad522ddb38ef0e","0x0cd7b300e9309a135285be1aeb02b152f97931a7357ab6d609a2cb1970aab877","0x2a7789dfe286c9d0a7592f1c9316e730cb14c9d843aefc4764d76e7f8571c96a","0x248ac54ce3dbf37796621882a4ac76046df5ab680da487fd85cce76b1ae392d3","0x149d1d07cebe320f77b03533e34912545cedeae62bd9778d37724728762b5710","0x00fe29daebdaed61309790e70e2dcefa3f3af4c6c965ce424b8dbcf09b8e4b49","0x2b75b3bace61b731d7f0c003a144b62b0a4fbe9f0d14ca89b0652b70210014b3","0x2588ef27cfb6e0d8c6f9a969b2da44fead30a02ed70a563fd15aa45bb671de1c","0x2b74d7674b55642697b4a1e226eddb0e4918b2d57aa5b99093dc46cadcdea000","0x244c626845d3a5040f08f01e9611f968ad675ca857789149b13a0cfa83a2e064","0x2cb8d02f90cae33fd7bcfb80af4aff067c4f5fc4b3f9228d5b8f768bc8f6c971","0x1372f3d1f04e0c39a50e823d5da03d70bebe19a1b8e28f8c2ff601cc0bfc0095","0x19af6601d2613426a50b7c35d60562a5f2f2634e6af56dac13459632e15570ee","0x13c2a16ed3b65dcd9414659be79af17995d344de34eaf962343b0f1e76c73a57","0x0dd5dcdbd50b8774831d4f01f930804d38b4266dfee085185530880a0c3903c0","0x07e91848d660b11b722638680ac60f20db9507fdc8d610ce762600f5a1aacd29","0x1f9c2a94d10c0a7fb60292cfc46fd3d2501181bea0ffe1f5f2501d474be3a785","0x14edb9c5bd389eae08a5ea2a7a1662894e1e878c142084d966a625bef68cf7c3","0x00000000000000000000000000000000cecd01810814d175f0a533f0067618c4","0x00000000000000000000000000000000f82935013ce5c82720c63e533af41db8","0x000000000000000000000000000000012185688171b6bed850e748b66f7222ac","0x2dd7f5ff2150155c2ac86ebe28d9ecbca2eea812b0021ab2bceae111cfea8325","0x04ea6c2daf2b9e827d2213c3d03953410dcf1ed67ba34a3c00e772be92606a8b","0x163f2bd18dcde52f99b9867c944780fd718d1612927053b139b280fc55013d1b","0x05e388fd160ccac30a8f7b18a4bd042f705e92b5937e8c0e9478e2ff623907c6","0x00ba3f6f527d6ed3ff17a63b1d5be3c42bdfae88fdf63311fc7b871157939309","0x16187d9daa8c2e5a1a9ab15be7ca6a8feebfb31bea76f9a3ca69381881c70561","0x0f64522e4904edb7377b14a7b9dad848829167324ef5c016346b3ad8251191ee","0x273bbe6000a4001dce369e5a36cc0b0ca3fd351665b688238aa8c556a6ca6b8e","0x022d2232efb2faa8307846c9a4c697aabad1b7f1336b35ad72fa8922975b49d9","0x0d82d478bff3955c4b0a34ef94427ca5f9da23147ad953c89f2e428277ec2825","0x18d886be90343010659c231583be61a138e28e37c24771e3cb61fbe2587d0671","0x000000000000000000000000000000196ba6a58dbeb7c34cb1d6287e23d434de","0x00000000000000000000000000000000001df8ae8a1589590f8863c1fefd8dfd","0x000000000000000000000000000000f30e11b2c5fbefa166cbb9f58c5f8e1a4c","0x000000000000000000000000000000000026420ade7666bc0ab1cf1fd9d0c534","0x0000000000000000000000000000000feb5b7d8260d25a1ee1ce76ff461673fc","0x00000000000000000000000000000000002bd2ac6223a80671b777bf5dca70a4","0x000000000000000000000000000000690f757006d2fa1ddb0114c9f268783537","0x000000000000000000000000000000000023ad36feadd91e50118f32e97a0204"] \ No newline at end of file diff --git a/tooling/nargo_cli/tests/execution_success/double_verify_proof/src/main.nr b/tooling/nargo_cli/tests/execution_success/double_verify_proof/src/main.nr index c679730e3c0..92e8326a895 100644 --- a/tooling/nargo_cli/tests/execution_success/double_verify_proof/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/double_verify_proof/src/main.nr @@ -9,19 +9,11 @@ fn main( proof_b : [Field; 94], ) -> pub [Field; 16] { let output_aggregation_object_a = std::verify_proof( - verification_key.as_slice(), - proof.as_slice(), - public_inputs.as_slice(), - key_hash, - input_aggregation_object + verification_key.as_slice(), proof.as_slice(), public_inputs.as_slice(), key_hash, input_aggregation_object ); let output_aggregation_object = std::verify_proof( - verification_key.as_slice(), - proof_b.as_slice(), - public_inputs.as_slice(), - key_hash, - output_aggregation_object_a + verification_key.as_slice(), proof_b.as_slice(), public_inputs.as_slice(), key_hash, output_aggregation_object_a ); let mut output = [0; 16]; diff --git a/tooling/nargo_cli/tests/execution_success/ecdsa_secp256k1/src/main.nr b/tooling/nargo_cli/tests/execution_success/ecdsa_secp256k1/src/main.nr index 5af8008f291..38a6ebd13e5 100644 --- a/tooling/nargo_cli/tests/execution_success/ecdsa_secp256k1/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/ecdsa_secp256k1/src/main.nr @@ -3,8 +3,8 @@ use dep::std; fn main(message : [u8;38],hashed_message : [u8;32], pub_key_x : [u8;32], pub_key_y : [u8;32], signature : [u8;64]) { // Hash the message, since secp256k1 expects a hashed_message let expected = std::hash::sha256(message); - assert(hashed_message == expected); + assert(hashed_message == expected); let valid_signature = std::ecdsa_secp256k1::verify_signature(pub_key_x, pub_key_y, signature, hashed_message); - assert(valid_signature); + assert(valid_signature); } diff --git a/tooling/nargo_cli/tests/execution_success/ecdsa_secp256r1/src/main.nr b/tooling/nargo_cli/tests/execution_success/ecdsa_secp256r1/src/main.nr index eda78cd81a6..840a401f109 100644 --- a/tooling/nargo_cli/tests/execution_success/ecdsa_secp256r1/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/ecdsa_secp256r1/src/main.nr @@ -2,5 +2,5 @@ use dep::std; fn main(hashed_message : [u8;32], pub_key_x : [u8;32], pub_key_y : [u8;32], signature : [u8;64]) { let valid_signature = std::ecdsa_secp256r1::verify_signature(pub_key_x, pub_key_y, signature, hashed_message); - assert(valid_signature); + assert(valid_signature); } diff --git a/tooling/nargo_cli/tests/execution_success/eddsa/src/main.nr b/tooling/nargo_cli/tests/execution_success/eddsa/src/main.nr index 870a20fe01a..f82b3224c3d 100644 --- a/tooling/nargo_cli/tests/execution_success/eddsa/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/eddsa/src/main.nr @@ -37,7 +37,7 @@ fn main(msg: pub Field, _priv_key_a: Field, _priv_key_b: Field) { // ]); // let s_a = (r_a + _priv_key_a * h_a) % bjj.suborder; // modulus computed manually - let s_a = 30333430637424319196043722294837632681219980330991241982145549329256671548; + let s_a = 30333430637424319196043722294837632681219980330991241982145549329256671548; // let s_b = (r_b + _priv_key_b * h_b) % bjj.suborder; // modulus computed manually let s_b = 1646085314320208098241070054368798527940102577261034947654839408482102287019; diff --git a/tooling/nargo_cli/tests/execution_success/field_attribute/src/main.nr b/tooling/nargo_cli/tests/execution_success/field_attribute/src/main.nr index d6d71781899..dda83db40de 100644 --- a/tooling/nargo_cli/tests/execution_success/field_attribute/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/field_attribute/src/main.nr @@ -16,4 +16,4 @@ fn foo() -> u32 { #[field(bls12_381)] fn foo() -> u32 { 3 -} \ No newline at end of file +} diff --git a/tooling/nargo_cli/tests/execution_success/global_consts/src/main.nr b/tooling/nargo_cli/tests/execution_success/global_consts/src/main.nr index 0c3a4fcd771..07d88cb2049 100644 --- a/tooling/nargo_cli/tests/execution_success/global_consts/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/global_consts/src/main.nr @@ -27,10 +27,10 @@ fn main(a: [Field; M + N - N], b: [Field; 30 + N / 2], c : pub [Field; foo::MAGI assert(test_struct.y[i] != NESTED[1][0].v); } - assert(N != M); + assert(N != M); let expected: u32 = 42; - assert(foo::TYPE_INFERRED == expected); + assert(foo::TYPE_INFERRED == expected); let mut y = 5; let mut x = M; @@ -40,30 +40,30 @@ fn main(a: [Field; M + N - N], b: [Field; 30 + N / 2], c : pub [Field; foo::MAGI y = i; } - assert(y == 24); - assert(x == 10); + assert(y == 24); + assert(x == 10); let q = multiplyByM(3); - assert(q == 96); + assert(q == 96); arrays_neq(a, b); let t: [Field; T_LEN] = [N, M]; - assert(t[1] == 32); + assert(t[1] == 32); - assert(15 == mysubmodule::my_helper()); + assert(15 == mysubmodule::my_helper()); let add_submodules_N = mysubmodule::N + foo::bar::N; - assert(15 == add_submodules_N); + assert(15 == add_submodules_N); let add_from_bar_N = mysubmodule::N + foo::bar::from_bar(1); - assert(15 == add_from_bar_N); + assert(15 == add_from_bar_N); // Example showing an array filled with (mysubmodule::N + 2) 0's let sugared = [0; mysubmodule::N + 2]; - assert(sugared[mysubmodule::N + 1] == 0); + assert(sugared[mysubmodule::N + 1] == 0); let arr: [Field; mysubmodule::N] = [N; 10]; - assert((arr[0] == 5) & (arr[9] == 5)); + assert((arr[0] == 5) & (arr[9] == 5)); foo::from_foo(d); baz::from_baz(c); @@ -74,7 +74,7 @@ fn multiplyByM(x: Field) -> Field { } fn arrays_neq(a: [Field; M], b: [Field; M]) { - assert(a != b); + assert(a != b); } mod mysubmodule { diff --git a/tooling/nargo_cli/tests/execution_success/if_else_chain/src/main.nr b/tooling/nargo_cli/tests/execution_success/if_else_chain/src/main.nr index 9fd5fbfd3d3..2705d5b3111 100644 --- a/tooling/nargo_cli/tests/execution_success/if_else_chain/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/if_else_chain/src/main.nr @@ -9,7 +9,7 @@ fn main(a: u32, mut c: [u32; 4]) { // expect to match this case assert(c[3] == 0); } else { - assert(c[0] == 10); + assert(c[0] == 10); } } diff --git a/tooling/nargo_cli/tests/execution_success/keccak256/src/main.nr b/tooling/nargo_cli/tests/execution_success/keccak256/src/main.nr index 9bca6a7c1bf..511d668bc3d 100644 --- a/tooling/nargo_cli/tests/execution_success/keccak256/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/keccak256/src/main.nr @@ -10,13 +10,13 @@ fn main(x: Field, result: [u8; 32]) { //#1399: variable meesage size let message_size = 4; - let hash_a = std::hash::keccak256([1,2,3,4], message_size); - let hash_b = std::hash::keccak256([1,2,3,4,0,0,0,0], message_size); + let hash_a = std::hash::keccak256([1, 2, 3, 4], message_size); + let hash_b = std::hash::keccak256([1, 2, 3, 4, 0, 0, 0, 0], message_size); assert(hash_a == hash_b); let message_size_big = 8; - let hash_c = std::hash::keccak256([1,2,3,4,0,0,0,0], message_size_big); + let hash_c = std::hash::keccak256([1, 2, 3, 4, 0, 0, 0, 0], message_size_big); assert(hash_a != hash_c); } diff --git a/tooling/nargo_cli/tests/execution_success/merkle_insert/Prover.toml b/tooling/nargo_cli/tests/execution_success/merkle_insert/Prover.toml index fca4a077df4..3f69ad1cffb 100644 --- a/tooling/nargo_cli/tests/execution_success/merkle_insert/Prover.toml +++ b/tooling/nargo_cli/tests/execution_success/merkle_insert/Prover.toml @@ -1,11 +1,11 @@ -old_root = "0x285785b10eca49cf456b935f1c9787ff571f306c1bc62549c31a9199a633f9f8" +old_root = "0x200fbe381993186866de8eb5a9cc33845de6b084be3ff67f6e34d35b8a786157" old_leaf = "0x1cdcf02431ba623767fe389337d011df1048dcc24b98ed81cec97627bab454a0" old_hash_path = [ "0x1cdcf02431ba623767fe389337d011df1048dcc24b98ed81cec97627bab454a0", "0x0b5e9666e7323ce925c28201a97ddf4144ac9d148448ed6f49f9008719c1b85b", "0x22ec636f8ad30ef78c42b7fe2be4a4cacf5a445cfb5948224539f59a11d70775", ] -new_root = "0x2d05c2650e6c2ef02c6dc7fae7f517b8ac191386666c0b5a68130a8c11092f5f" +new_root = "0x029ead881a6684995190589a67874f188f33a433211df82f56c1351c96530387" leaf = "0x085ca53be9c9d95b57e6e5fc91c5d531ad9e63e85dd71af7e35562991774b435" index = "0" -mimc_input = [12,45,78,41] +mimc_input = [12, 45, 78, 41] diff --git a/tooling/nargo_cli/tests/execution_success/mock_oracle/src/main.nr b/tooling/nargo_cli/tests/execution_success/mock_oracle/src/main.nr index 07553f05d6c..90fca7993cc 100644 --- a/tooling/nargo_cli/tests/execution_success/mock_oracle/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/mock_oracle/src/main.nr @@ -9,12 +9,9 @@ struct Point { unconstrained fn foo_oracle(_point: Point, _array: [Field; 4]) -> Field {} unconstrained fn main() { - let array = [1,2,3,4]; - let another_array = [4,3,2,1]; - let point = Point { - x: 14, - y: 27, - }; + let array = [1, 2, 3, 4]; + let another_array = [4, 3, 2, 1]; + let point = Point { x: 14, y: 27 }; OracleMock::mock("foo").returns(42).times(1); let mock = OracleMock::mock("foo").returns(0); diff --git a/tooling/nargo_cli/tests/execution_success/nested_array_dynamic/src/main.nr b/tooling/nargo_cli/tests/execution_success/nested_array_dynamic/src/main.nr index 5538c08d778..cd10b5e7347 100644 --- a/tooling/nargo_cli/tests/execution_success/nested_array_dynamic/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/nested_array_dynamic/src/main.nr @@ -38,7 +38,7 @@ fn main(mut x : [Foo; 4], y : pub Field) { x[y - 1].b = [100, 101, 102]; } assert(x[2].b == [100, 101, 102]); - + assert(x[y - 3].bar.inner == [100, 101, 102]); assert(x[y - 2].bar.inner == [103, 104, 105]); assert(x[y - 1].bar.inner == [106, 107, 108]); @@ -52,7 +52,7 @@ fn main(mut x : [Foo; 4], y : pub Field) { assert(foo_parents[y - 3].foos[y - 2].b == [5, 6, 21]); assert(foo_parents[y - 3].foos[y - 1].b == [100, 101, 102]); assert(foo_parents[y - 3].foos[y].b == [11, 12, 23]); - + assert(foo_parents[y - 3].foos[y].a == 50); assert(foo_parents[1].foos[1].b == [5, 6, 21]); @@ -74,6 +74,5 @@ fn main(mut x : [Foo; 4], y : pub Field) { // Set a dynamic array value foo_parents[y - 2].foos[y - 3].b = foo_parents[y - 2].foos[y - 2].b; assert(foo_parents[1].foos[0].b == [20, 19, 5000]); - } diff --git a/tooling/nargo_cli/tests/execution_success/nested_arrays_from_brillig/src/main.nr b/tooling/nargo_cli/tests/execution_success/nested_arrays_from_brillig/src/main.nr index 46c4ebe938e..1bcbd7d5421 100644 --- a/tooling/nargo_cli/tests/execution_success/nested_arrays_from_brillig/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/nested_arrays_from_brillig/src/main.nr @@ -13,9 +13,9 @@ fn access_nested(notes: [MyNote; 2]) -> Field { } unconstrained fn create_inside_brillig(values: [Field; 6]) -> [MyNote; 2] { - let header = Header { params: [values[0], values[1], values[2]]}; - let note0 = MyNote { array: [values[0], values[1]], plain : values[2], header }; - let note1 = MyNote { array: [values[3], values[4]], plain : values[5], header }; + let header = Header { params: [values[0], values[1], values[2]] }; + let note0 = MyNote { array: [values[0], values[1]], plain: values[2], header }; + let note1 = MyNote { array: [values[3], values[4]], plain: values[5], header }; [note0, note1] } diff --git a/tooling/nargo_cli/tests/execution_success/nested_slice_dynamic/src/main.nr b/tooling/nargo_cli/tests/execution_success/nested_slice_dynamic/src/main.nr index a2de5141786..2d7c52da495 100644 --- a/tooling/nargo_cli/tests/execution_success/nested_slice_dynamic/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/nested_slice_dynamic/src/main.nr @@ -41,7 +41,7 @@ fn main(y : Field) { x[y - 1].b = [100, 101, 102]; } assert(x[2].b == [100, 101, 102]); - + assert(x[y - 3].bar.inner == [100, 101, 102]); assert(x[y - 2].bar.inner == [103, 104, 105]); assert(x[y - 1].bar.inner == [106, 107, 108]); diff --git a/tooling/nargo_cli/tests/execution_success/pedersen_check/Prover.toml b/tooling/nargo_cli/tests/execution_success/pedersen_check/Prover.toml index 2fb3b1e1abf..db1ebdf6c51 100644 --- a/tooling/nargo_cli/tests/execution_success/pedersen_check/Prover.toml +++ b/tooling/nargo_cli/tests/execution_success/pedersen_check/Prover.toml @@ -2,5 +2,6 @@ x = "0" y = "1" salt = "42" -out_x = "0x0c5e1ddecd49de44ed5e5798d3f6fb7c71fe3d37f5bee8664cf88a445b5ba0af" -out_y = "0x230294a041e26fe80b827c2ef5cb8784642bbaa83842da2714d62b1f3c4f9752" \ No newline at end of file +out_x = "0x054aa86a73cb8a34525e5bbed6e43ba1198e860f5f3950268f71df4591bde402" +out_y = "0x209dcfbf2cfb57f9f6046f44d71ac6faf87254afc7407c04eb621a6287cac126" +out_hash = "0x0d98561fb02ca04d00801dfdc118b2a24cea0351963587712a28d368041370e1" diff --git a/tooling/nargo_cli/tests/execution_success/pedersen_check/src/main.nr b/tooling/nargo_cli/tests/execution_success/pedersen_check/src/main.nr index 0e5c6b2c5b4..e5e8c740ea9 100644 --- a/tooling/nargo_cli/tests/execution_success/pedersen_check/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/pedersen_check/src/main.nr @@ -1,11 +1,15 @@ use dep::std; -fn main(x: Field, y: Field, salt: Field, out_x: Field, out_y: Field ) { +fn main(x: Field, y: Field, salt: Field, out_x: Field, out_y: Field, out_hash: Field) { let res = std::hash::pedersen([x, y]); assert(res[0] == out_x); assert(res[1] == out_y); + let res_hash = std::hash::pedersen_hash_with_separator([x, y], 0); + assert_eq(res_hash, out_hash); - let raw_data = [x,y]; + assert(res_hash != res[0]); + + let raw_data = [x, y]; let mut state = 0; for i in 0..2 { state = state * 8 + raw_data[i]; diff --git a/tooling/nargo_cli/tests/execution_success/poseidonsponge_x5_254/src/main.nr b/tooling/nargo_cli/tests/execution_success/poseidonsponge_x5_254/src/main.nr index 6d75dcccc66..910a17c8c89 100644 --- a/tooling/nargo_cli/tests/execution_success/poseidonsponge_x5_254/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/poseidonsponge_x5_254/src/main.nr @@ -3,6 +3,6 @@ use dep::std::hash::poseidon; fn main(x: [Field; 7]) { // Test optimized sponge let result = poseidon::bn254::sponge(x); - + assert(result == 0x080ae1669d62f0197190573d4a325bfb8d8fc201ce3127cbac0c47a7ac81ac48); } diff --git a/tooling/nargo_cli/tests/execution_success/references/src/main.nr b/tooling/nargo_cli/tests/execution_success/references/src/main.nr index 5265519f2a8..33fe69dc96e 100644 --- a/tooling/nargo_cli/tests/execution_success/references/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/references/src/main.nr @@ -22,12 +22,7 @@ fn main(mut x: Field) { assert(**nested_allocations.y == 1); // Test nested struct allocations with a mutable reference to an array. - let mut c = C { - foo: 0, - bar: &mut C2 { - array: &mut [1, 2], - }, - }; + let mut c = C { foo: 0, bar: &mut C2 { array: &mut [1, 2] } }; *c.bar.array = [3, 4]; assert(*c.bar.array == [3, 4]); @@ -135,15 +130,14 @@ fn regression_2218(x: Field, y: Field) -> Field { if x != 20 { *q1 = 10; *q2 = 2; // now we'd expect q1 == q2 == 2 - - assert(*q1 == 2); + assert(*q1 == 2); } else { *q2 = 15; assert(*q1 == 15); } } else { *q2 = 20; - assert(*q1 == 20); + assert(*q1 == 20); } // Have to assign value to return it let value = *q1; @@ -232,7 +226,7 @@ fn regression_2218_loop(x: Field, y: Field) { } } else { *q2 = 20; - assert(*q1 == 20); + assert(*q1 == 20); } assert(*q1 == 2); } diff --git a/tooling/nargo_cli/tests/execution_success/regression/src/main.nr b/tooling/nargo_cli/tests/execution_success/regression/src/main.nr index 010468a450a..f8ac3823e08 100644 --- a/tooling/nargo_cli/tests/execution_success/regression/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/regression/src/main.nr @@ -1,7 +1,7 @@ global NIBBLE_LENGTH: Field = 16; fn compact_decode(input: [u8; N], length: Field) -> ([u4; NIBBLE_LENGTH], Field) { - assert(2*input.len() as u64 <= NIBBLE_LENGTH as u64); + assert(2 * input.len() as u64 <= NIBBLE_LENGTH as u64); assert(length as u64 <= input.len() as u64); let mut nibble = [0 as u4; NIBBLE_LENGTH]; @@ -9,8 +9,7 @@ fn compact_decode(input: [u8; N], length: Field) -> ([u4; NIBBLE_LENGTH], Fie let first_nibble = (input[0] >> 4) as u4; let parity = first_nibble as u1; - if parity == 1 - { + if parity == 1 { nibble[0] = (input[0] & 0x0f) as u4; for i in 1..input.len() { @@ -21,9 +20,7 @@ fn compact_decode(input: [u8; N], length: Field) -> ([u4; NIBBLE_LENGTH], Fie nibble[2*i] = (x & 0x0f) as u4; } } - } - else - { + } else { for i in 0..2 { if (i as u64) < length as u64 - 1 @@ -35,7 +32,7 @@ fn compact_decode(input: [u8; N], length: Field) -> ([u4; NIBBLE_LENGTH], Fie } } - let out = (nibble, 2*length + (parity as Field) - 2); + let out = (nibble, 2 * length + (parity as Field) - 2); out } @@ -43,30 +40,24 @@ fn compact_decode(input: [u8; N], length: Field) -> ([u4; NIBBLE_LENGTH], Fie fn enc(value: [u8; N], value_length: Field) -> ([u8; 32], Field) { assert(value.len() as u8 >= value_length as u8); let mut out_value = [0; 32]; - if value_length == 0 - { - let out = (out_value, value_length); - out - } - else { if value_length as u8 < 31 - { + if value_length == 0 { + let out = (out_value, value_length); + out + } else if value_length as u8 < 31 { out_value[0] = 0x80 + value_length as u8; for i in 1..value.len() { out_value[i] = value[i-1]; } - - let out = (out_value, value_length + 1); - out - } - else - { - let out = (out_value, 32); - out + let out = (out_value, value_length + 1); + + out + } else { + let out = (out_value, 32); + out } - } } fn bitshift_literal_0() -> u64 { @@ -90,17 +81,21 @@ fn bitshift_variable(idx: u64) -> u64 { fn main(x: [u8; 5], z: Field) { //Issue 1144 - let (nib, len) = compact_decode(x,z); - assert(len == 5); - assert([nib[0], nib[1], nib[2], nib[3], nib[4]] == [15, 1, 12, 11, 8]); + let (nib, len) = compact_decode(x, z); + assert(len == 5); + assert([nib[0], nib[1], nib[2], nib[3], nib[4]] == [15, 1, 12, 11, 8]); // Issue 1169 - let val1 = [0xb8,0x8f,0x61,0xe6,0xfb,0xda,0x83,0xfb,0xff,0xfa,0xbe,0x36,0x41,0x12,0x13,0x74,0x80,0x39,0x80,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]; + let val1 = [0xb8, 0x8f, 0x61, 0xe6, 0xfb, 0xda, 0x83, 0xfb, 0xff, 0xfa, 0xbe, 0x36, 0x41, 0x12, 0x13, + 0x74, 0x80, 0x39, 0x80, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00]; let val1_length = 20; - let enc_val1 = enc(val1,val1_length); + let enc_val1 = enc(val1, val1_length); - assert(enc_val1.0 == [0x94,0xb8,0x8f,0x61,0xe6,0xfb,0xda,0x83,0xfb,0xff,0xfa,0xbe,0x36,0x41,0x12,0x13,0x74,0x80,0x39,0x80,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]); + assert(enc_val1.0 == [0x94, 0xb8, 0x8f, 0x61, 0xe6, 0xfb, 0xda, 0x83, 0xfb, 0xff, 0xfa, 0xbe, 0x36, 0x41, 0x12, + 0x13, 0x74, 0x80, 0x39, 0x80, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00]); assert(enc_val1.1 == 21); // Issue 2399 diff --git a/tooling/nargo_cli/tests/execution_success/regression_mem_op_predicate/src/main.nr b/tooling/nargo_cli/tests/execution_success/regression_mem_op_predicate/src/main.nr index 368ca50397f..2592d02ec2e 100644 --- a/tooling/nargo_cli/tests/execution_success/regression_mem_op_predicate/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/regression_mem_op_predicate/src/main.nr @@ -1,8 +1,8 @@ fn main(mut x: [u32; 5], idx: Field) { // We should not hit out of bounds here as we have a predicate // that should not be hit - if idx as u32 < 3 { + if idx as u32 < 3 { x[idx] = 10; - } - assert(x[4] == 111); + } + assert(x[4] == 111); } diff --git a/tooling/nargo_cli/tests/execution_success/scalar_mul/src/main.nr b/tooling/nargo_cli/tests/execution_success/scalar_mul/src/main.nr index 87641b407d6..f62496d61d5 100644 --- a/tooling/nargo_cli/tests/execution_success/scalar_mul/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/scalar_mul/src/main.nr @@ -15,8 +15,8 @@ fn main( priv_key = b; pub_x = b_pub_x; pub_y = b_pub_y; - } + } let res = std::scalar_mul::fixed_base_embedded_curve(priv_key, 0); - assert(res[0] == pub_x); - assert(res[1] == pub_y); + assert(res[0] == pub_x); + assert(res[1] == pub_y); } diff --git a/tooling/nargo_cli/tests/execution_success/schnorr/Prover.toml b/tooling/nargo_cli/tests/execution_success/schnorr/Prover.toml index 5fe6bd2546f..2faf2018e07 100644 --- a/tooling/nargo_cli/tests/execution_success/schnorr/Prover.toml +++ b/tooling/nargo_cli/tests/execution_success/schnorr/Prover.toml @@ -1,10 +1,70 @@ -message = [0,1,2,3,4,5,6,7,8,9] +message = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] message_field = "0x010203040506070809" -pub_key_x = "0x17cbd3ed3151ccfd170efe1d54280a6a4822640bf5c369908ad74ea21518a9c5" -pub_key_y = "0x0e0456e3795c1a31f20035b741cd6158929eeccd320d299cfcac962865a6bc74" +pub_key_x = "0x04b260954662e97f00cab9adb773a259097f7a274b83b113532bce27fa3fb96a" +pub_key_y = "0x2fd51571db6c08666b0edfbfbc57d432068bccd0110a39b166ab243da0037197" signature = [ - 5, 202, 31, 146, 81, 242, 246, 69, 43, 107, 249, 153, 198, 44, 14, 111, 191, 121, 137, 166, - 160, 103, 18, 181, 243, 233, 226, 95, 67, 16, 37, 128, 85, 76, 19, 253, 30, 77, 192, 53, 138, - 205, 69, 33, 236, 163, 83, 194, 84, 137, 184, 221, 176, 121, 179, 27, 63, 70, 54, 16, 176, - 250, 39, 239, -] \ No newline at end of file + 1, + 13, + 119, + 112, + 212, + 39, + 233, + 41, + 84, + 235, + 255, + 93, + 245, + 172, + 186, + 83, + 157, + 253, + 76, + 77, + 33, + 128, + 178, + 15, + 214, + 67, + 105, + 107, + 177, + 234, + 77, + 48, + 27, + 237, + 155, + 84, + 39, + 84, + 247, + 27, + 22, + 8, + 176, + 230, + 24, + 115, + 145, + 220, + 254, + 122, + 135, + 179, + 171, + 4, + 214, + 202, + 64, + 199, + 19, + 84, + 239, + 138, + 124, + 12, +] diff --git a/tooling/nargo_cli/tests/execution_success/schnorr/src/main.nr b/tooling/nargo_cli/tests/execution_success/schnorr/src/main.nr index 3c8881b2f39..f974027f1cf 100644 --- a/tooling/nargo_cli/tests/execution_success/schnorr/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/schnorr/src/main.nr @@ -12,10 +12,10 @@ fn main(message: [u8; 10], message_field: Field, pub_key_x: Field, pub_key_y: Fi // Is there ever a situation where someone would want // to ensure that a signature was invalid? // Check that passing a slice as the message is valid - let valid_signature = std::schnorr::verify_signature(pub_key_x,pub_key_y,signature, message_field_bytes); + let valid_signature = std::schnorr::verify_signature(pub_key_x, pub_key_y, signature, message_field_bytes); assert(valid_signature); // Check that passing an array as the message is valid - let valid_signature = std::schnorr::verify_signature(pub_key_x,pub_key_y,signature, message); + let valid_signature = std::schnorr::verify_signature(pub_key_x, pub_key_y, signature, message); assert(valid_signature); } diff --git a/tooling/nargo_cli/tests/execution_success/sha2_blocks/src/main.nr b/tooling/nargo_cli/tests/execution_success/sha2_blocks/src/main.nr index 52891d18de2..a491530dd7d 100644 --- a/tooling/nargo_cli/tests/execution_success/sha2_blocks/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/sha2_blocks/src/main.nr @@ -4,18 +4,31 @@ use dep::std; fn main(x: [u8; 3], result256: [u8; 32], result512: [u8; 64]) { // One-block tests. let mut digest256 = std::sha256::digest(x); - assert(digest256 == result256); + assert(digest256 == result256); let mut digest512 = std::sha512::digest(x); - assert(digest512 == result512); + assert(digest512 == result512); // Two-block SHA256 test. Taken from https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/SHA256.pdf - let y: [u8; 56] = [97,98,99,100,98,99,100,101,99,100,101,102,100,101,102,103,101,102,103,104,102,103,104,105,103,104,105,106,104,105,106,107,105,106,107,108,106,107,108,109,107,108,109,110,108,109,110,111,109,110,111,112,110,111,112,113]; // "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + let y: [u8; 56] = [97, 98, 99, 100, 98, 99, 100, 101, 99, 100, 101, 102, 100, 101, 102, 103, 101, 102, 103, + 104, 102, 103, 104, 105, 103, 104, 105, 106, 104, 105, 106, 107, 105, 106, 107, 108, 106, + 107, 108, 109, 107, 108, 109, 110, 108, 109, 110, 111, 109, 110, 111, 112, 110, 111, 112, + 113]; // "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" digest256 = std::sha256::digest(y); - assert(digest256 == [36,141,106,97,210,6,56,184,229,192,38,147,12,62,96,57,163,60,228,89,100,255,33,103,246,236,237,212,25,219,6,193]); + assert(digest256 == [36, 141, 106, 97, 210, 6, 56, 184, 229, 192, 38, 147, 12, 62, 96, 57, 163, 60, 228, 89, 100, + 255, 33, 103, 246, 236, 237, 212, 25, 219, 6, 193]); // Two-block SHA256 test. Taken from https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/SHA512.pdf - let z: [u8; 112] = [97,98,99,100,101,102,103,104,98,99,100,101,102,103,104,105,99,100,101,102,103,104,105,106,100,101,102,103,104,105,106,107,101,102,103,104,105,106,107,108,102,103,104,105,106,107,108,109,103,104,105,106,107,108,109,110,104,105,106,107,108,109,110,111,105,106,107,108,109,110,111,112,106,107,108,109,110,111,112,113,107,108,109,110,111,112,113,114,108,109,110,111,112,113,114,115,109,110,111,112,113,114,115,116,110,111,112,113,114,115,116,117]; // "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu" + let z: [u8; 112] = [97, 98, 99, 100, 101, 102, 103, 104, 98, 99, 100, 101, 102, 103, 104, 105, 99, 100, 101, + 102, 103, 104, 105, 106, 100, 101, 102, 103, 104, 105, 106, 107, 101, 102, 103, 104, 105, + 106, 107, 108, 102, 103, 104, 105, 106, 107, 108, 109, 103, 104, 105, 106, 107, 108, 109, + 110, 104, 105, 106, 107, 108, 109, 110, 111, 105, 106, 107, 108, 109, 110, 111, 112, 106, + 107, 108, 109, 110, 111, 112, 113, 107, 108, 109, 110, 111, 112, 113, 114, 108, 109, 110, + 111, 112, 113, 114, 115, 109, 110, 111, 112, 113, 114, 115, 116, 110, 111, 112, 113, 114, + 115, 116, 117]; // "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu" digest512 = std::sha512::digest(z); - assert(digest512 == [142,149,155,117,218,227,19,218,140,244,247,40,20,252,20,63,143,119,121,198,235,159,127,161,114,153,174,173,182,136,144,24,80,29,40,158,73,0,247,228,51,27,153,222,196,181,67,58,199,211,41,238,182,221,38,84,94,150,229,91,135,75,233,9]); + assert(digest512 == [142, 149, 155, 117, 218, 227, 19, 218, 140, 244, 247, 40, 20, 252, 20, 63, 143, 119, 121, + 198, 235, 159, 127, 161, 114, 153, 174, 173, 182, 136, 144, 24, 80, 29, 40, 158, 73, 0, 247, + 228, 51, 27, 153, 222, 196, 181, 67, 58, 199, 211, 41, 238, 182, 221, 38, 84, 94, 150, 229, + 91, 135, 75, 233, 9]); } diff --git a/tooling/nargo_cli/tests/execution_success/sha2_byte/src/main.nr b/tooling/nargo_cli/tests/execution_success/sha2_byte/src/main.nr index 6470ccd8379..fa8ddfbdf69 100644 --- a/tooling/nargo_cli/tests/execution_success/sha2_byte/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/sha2_byte/src/main.nr @@ -3,8 +3,8 @@ use dep::std; fn main(x: Field, result256: [u8; 32], result512: [u8; 64]) { let digest256 = std::sha256::digest([x as u8]); - assert(digest256 == result256); + assert(digest256 == result256); let digest512 = std::sha512::digest([x as u8]); - assert(digest512 == result512); + assert(digest512 == result512); } diff --git a/tooling/nargo_cli/tests/execution_success/simple_2d_array/src/main.nr b/tooling/nargo_cli/tests/execution_success/simple_2d_array/src/main.nr index c098a679a3b..2ecdd4bc15f 100644 --- a/tooling/nargo_cli/tests/execution_success/simple_2d_array/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/simple_2d_array/src/main.nr @@ -3,6 +3,6 @@ fn main(x: Field, y: Field, array_input: [[Field; 2]; 2]) { assert(array_input[0][0] == x); assert(array_input[0][1] == y); - let arr: [[Field; 2]; 1] = [[3,3]]; + let arr: [[Field; 2]; 1] = [[3, 3]]; assert_eq(arr[0], array_input[1]); } diff --git a/tooling/nargo_cli/tests/execution_success/simple_shield/Prover.toml b/tooling/nargo_cli/tests/execution_success/simple_shield/Prover.toml index 5a9b2f21b9b..55492a9bf6d 100644 --- a/tooling/nargo_cli/tests/execution_success/simple_shield/Prover.toml +++ b/tooling/nargo_cli/tests/execution_success/simple_shield/Prover.toml @@ -1,6 +1,6 @@ # Random test key priv_key = "0x000000000000000000000000000000000000000000000000000000616c696365" -note_root = "0x21386402d57460963f45f32577dc3902c38a6f6fab9ec7b1b708a92e48745de7" +note_root = "0x0f8cd7b45fbb4e08f63126f1673ce9e574cfbda70f36c2e5182f9e3dbe6524c0" index = "0" note_hash_path = [ "0x1cdcf02431ba623767fe389337d011df1048dcc24b98ed81cec97627bab454a0", diff --git a/tooling/nargo_cli/tests/execution_success/slices/src/main.nr b/tooling/nargo_cli/tests/execution_success/slices/src/main.nr index 8d53e013f96..25c2ac27157 100644 --- a/tooling/nargo_cli/tests/execution_success/slices/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/slices/src/main.nr @@ -61,7 +61,6 @@ fn regression_2083() { let y = y.push_front((10, 11)); // [(10, 11), (1, 2), (3, 4), (5, 6)] let y = y.push_front((12, 13)); // [(12, 13), (10, 11), (1, 2), (3, 4), (5, 6)] - assert(y[1].0 == 10); let y = y.insert(1, (55, 56)); // [(12, 13), (55, 56), (10, 11), (1, 2), (3, 4), (5, 6)] @@ -149,15 +148,7 @@ fn merge_slices_else(x: Field) { // Test returning a merged slice without a mutation fn merge_slices_return(x: Field, y: Field) -> [Field] { let slice = [0; 2]; - if x != y { - if x != 20 { - slice.push_back(y) - } else { - slice - } - } else { - slice - } + if x != y { if x != 20 { slice.push_back(y) } else { slice } } else { slice } } // Test mutating a slice inside of an if statement diff --git a/tooling/nargo_cli/tests/execution_success/strings/src/main.nr b/tooling/nargo_cli/tests/execution_success/strings/src/main.nr index 02e2fa678cd..42e8f7967ae 100644 --- a/tooling/nargo_cli/tests/execution_success/strings/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/strings/src/main.nr @@ -25,7 +25,7 @@ fn main(message : pub str<11>, y : Field, hex_as_string : str<4>, hex_as_field : let hash = std::hash::pedersen([x]); std::println(hash); - + assert(hex_as_string == "0x41"); // assert(hex_as_string != 0x41); This will fail with a type mismatch between str[4] and Field assert(hex_as_field == 0x41); diff --git a/tooling/nargo_cli/tests/execution_success/struct/src/main.nr b/tooling/nargo_cli/tests/execution_success/struct/src/main.nr index 2bd5a1aec28..745815227d9 100644 --- a/tooling/nargo_cli/tests/execution_success/struct/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/struct/src/main.nr @@ -34,11 +34,7 @@ struct MyStruct { my_nest: Nested, } fn test_struct_in_tuple(a_bool : bool,x:Field, y:Field) -> (MyStruct, bool) { - let my_struct = MyStruct { - my_bool: a_bool, - my_int: 5, - my_nest: Nested{a:x,b:y}, - }; + let my_struct = MyStruct { my_bool: a_bool, my_int: 5, my_nest: Nested { a: x, b: y } }; (my_struct, a_bool) } @@ -57,7 +53,7 @@ struct Unit; fn main(x: Field, y: Field) { let unit = Unit {}; - let first = Foo::default(x,y); + let first = Foo::default(x, y); let p = Pair { first, second: 1 }; assert(p.bar() == x); @@ -65,7 +61,7 @@ fn main(x: Field, y: Field) { assert(p.first.array[0] != p.first.array[1]); // Nested structs - let (struct_from_tuple, a_bool) = test_struct_in_tuple(true,x,y); + let (struct_from_tuple, a_bool) = test_struct_in_tuple(true, x, y); assert(struct_from_tuple.my_bool == true); assert(a_bool == true); assert(struct_from_tuple.my_int == 5); diff --git a/tooling/nargo_cli/tests/execution_success/to_be_bytes/src/main.nr b/tooling/nargo_cli/tests/execution_success/to_be_bytes/src/main.nr index 81b08ed1785..025738c42e6 100644 --- a/tooling/nargo_cli/tests/execution_success/to_be_bytes/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/to_be_bytes/src/main.nr @@ -5,8 +5,8 @@ fn main(x : Field) -> pub [u8; 31] { for i in 0..31 { bytes[i] = byte_array[i]; } - if ( bytes[30] != 60) | (bytes[29] != 33) | (bytes[28] != 31) { - assert(false); + if (bytes[30] != 60) | (bytes[29] != 33) | (bytes[28] != 31) { + assert(false); } bytes } diff --git a/tooling/nargo_cli/tests/execution_success/to_bytes_integration/src/main.nr b/tooling/nargo_cli/tests/execution_success/to_bytes_integration/src/main.nr index 43a35f05488..6825d7f5562 100644 --- a/tooling/nargo_cli/tests/execution_success/to_bytes_integration/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/to_bytes_integration/src/main.nr @@ -4,7 +4,7 @@ fn main(x : Field, a: Field) { let y: Field = 2040124; let be_byte_array = y.to_be_bytes(31); let le_byte_array = x.to_le_bytes(31); - + assert(le_byte_array[0] == 60); assert(le_byte_array[0] == be_byte_array[30]); assert(le_byte_array[1] == be_byte_array[29]); diff --git a/tooling/nargo_cli/tests/execution_success/trait_impl_base_type/src/main.nr b/tooling/nargo_cli/tests/execution_success/trait_impl_base_type/src/main.nr index 47e919cf39c..e01e243a78f 100644 --- a/tooling/nargo_cli/tests/execution_success/trait_impl_base_type/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/trait_impl_base_type/src/main.nr @@ -81,26 +81,6 @@ fn some_func(x: u32) -> u32 { } -trait MutFieldable { - fn mut_to_field(self) -> Field; -} - -impl MutFieldable for &mut u64 { - fn mut_to_field(self) -> Field { - 1337 as Field - } -} - -fn a(y: &mut u64) -> Field { - y.mut_to_field() -} - -impl Fieldable for &mut u64 { - fn to_field(self) -> Field { - 777 as Field - } -} - impl Fieldable for u64 { fn to_field(self) -> Field { 66 as Field @@ -134,8 +114,5 @@ fn main(x: u32) { assert(some_func.to_field() == 17); let mut y = 0 as u64; - assert(a(&mut y) == 1337); - assert((&mut y).mut_to_field() == 1337); - assert((&mut y).to_field() == 777); assert(y.to_field() == 66); -} \ No newline at end of file +} diff --git a/tooling/nargo_cli/tests/execution_success/tuple_inputs/src/main.nr b/tooling/nargo_cli/tests/execution_success/tuple_inputs/src/main.nr index 2fc44a8fc75..64621411c84 100644 --- a/tooling/nargo_cli/tests/execution_success/tuple_inputs/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/tuple_inputs/src/main.nr @@ -27,6 +27,7 @@ fn main(pair : (Field, Field), x: [(u8, u8, u8); 2], struct_pair: (Foo, Bar)) -> } else { (pair.0 + 1, pair.0) }; + assert(u == pair.0 + 1); assert(v == pair.0); diff --git a/tooling/nargo_cli/tests/execution_success/tuples/src/main.nr b/tooling/nargo_cli/tests/execution_success/tuples/src/main.nr index e836c66a0fa..b6f6d695f2c 100644 --- a/tooling/nargo_cli/tests/execution_success/tuples/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/tuples/src/main.nr @@ -7,12 +7,8 @@ fn main(x: Field, y: Field) { assert(a == 0); assert(b == 1); - let (u, v) = if x as u32 < 1 { - (x, x + 1) - } else { - (x + 1, x) - }; - assert(u == x+1); + let (u, v) = if x as u32 < 1 { (x, x + 1) } else { (x + 1, x) }; + assert(u == x + 1); assert(v == x); // Test mutating tuples diff --git a/tooling/nargo_cli/tests/execution_success/type_aliases/src/main.nr b/tooling/nargo_cli/tests/execution_success/type_aliases/src/main.nr index 573a501367f..c9db6d81f90 100644 --- a/tooling/nargo_cli/tests/execution_success/type_aliases/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/type_aliases/src/main.nr @@ -22,9 +22,7 @@ fn main(x : [Field; 2]) { let e: Three = (c, d); assert(e.0 == 1); - let s = MyStruct { - foo: 10 - }; + let s = MyStruct { foo: 10 }; assert(s.foo == 10); let _regression2502: Regression2502Alias = Regression2502 {}; diff --git a/tooling/nargo_cli/tests/noir_test_failure/should_fail_mismatch/src/main.nr b/tooling/nargo_cli/tests/noir_test_failure/should_fail_mismatch/src/main.nr index b2672221c34..923c27d17d1 100644 --- a/tooling/nargo_cli/tests/noir_test_failure/should_fail_mismatch/src/main.nr +++ b/tooling/nargo_cli/tests/noir_test_failure/should_fail_mismatch/src/main.nr @@ -1,4 +1,3 @@ - #[test(should_fail_with = "Not equal")] fn test_different_string() { assert_eq(0, 1, "Different string"); @@ -14,4 +13,4 @@ fn test_with_extra_space() { #[test(should_fail_with = "Not equal")] fn test_runtime_mismatch() { assert_eq(dep::std::hash::pedersen([27])[0], 0, "Not equal "); -} \ No newline at end of file +} diff --git a/tooling/nargo_cli/tests/rebuild.sh b/tooling/nargo_cli/tests/rebuild.sh index 9776745a1f7..d89402fb1cf 100755 --- a/tooling/nargo_cli/tests/rebuild.sh +++ b/tooling/nargo_cli/tests/rebuild.sh @@ -42,13 +42,14 @@ for dir in $base_path/*; do # Delete the JSON file after extracting bytecode field rm ./target/${dir_name}.json - # Delete the target directory in acir_artifacts if it exists + # Clear the target directory in acir_artifacts if [ -d "$current_dir/acir_artifacts/$dir_name/target" ]; then rm -r "$current_dir/acir_artifacts/$dir_name/target" fi + mkdir $current_dir/acir_artifacts/$dir_name/target - # Move the target directory to the corresponding directory in acir_artifacts - mv ./target/ $current_dir/acir_artifacts/$dir_name/ + # Move the artifacts from the target directory to the corresponding directory in acir_artifacts + mv ./target/*.gz $current_dir/acir_artifacts/$dir_name/target/ cd $base_path fi diff --git a/tooling/nargo_cli/tests/test_libraries/diamond_deps_2/src/lib.nr b/tooling/nargo_cli/tests/test_libraries/diamond_deps_2/src/lib.nr index 7ee59d2bc9d..b0d09c70648 100644 --- a/tooling/nargo_cli/tests/test_libraries/diamond_deps_2/src/lib.nr +++ b/tooling/nargo_cli/tests/test_libraries/diamond_deps_2/src/lib.nr @@ -1,6 +1,5 @@ global RESOLVE_THIS = 3; - pub fn call_dep2(x : Field, y : Field) -> Field { x + y } diff --git a/tooling/nargo_fmt/src/config.rs b/tooling/nargo_fmt/src/config.rs index aaa5cbac3d0..b81fc47cf09 100644 --- a/tooling/nargo_fmt/src/config.rs +++ b/tooling/nargo_fmt/src/config.rs @@ -47,6 +47,7 @@ config! { remove_nested_parens: bool, true, "Remove nested parens"; short_array_element_width_threshold: usize, 10, "Width threshold for an array element to be considered short"; array_width: usize, 100, "Maximum width of an array literal before falling back to vertical formatting"; + single_line_if_else_max_width: usize, 50, "Maximum line length for single line if-else expressions"; } impl Config { diff --git a/tooling/nargo_fmt/src/lib.rs b/tooling/nargo_fmt/src/lib.rs index 3e8ee570c09..39e978aa306 100644 --- a/tooling/nargo_fmt/src/lib.rs +++ b/tooling/nargo_fmt/src/lib.rs @@ -30,6 +30,6 @@ pub use config::Config; pub fn format(source: &str, parsed_module: ParsedModule, config: &Config) -> String { let mut fmt = FmtVisitor::new(source, config); - fmt.visit_module(parsed_module); + fmt.visit_file(parsed_module); fmt.finish() } diff --git a/tooling/nargo_fmt/src/utils.rs b/tooling/nargo_fmt/src/utils.rs index d1c642b87d2..a13c0298ed7 100644 --- a/tooling/nargo_fmt/src/utils.rs +++ b/tooling/nargo_fmt/src/utils.rs @@ -119,15 +119,13 @@ impl<'me, T> Exprs<'me, T> { } pub(crate) trait FindToken { - fn find_token(&self, token: Token) -> Option; + fn find_token(&self, token: Token) -> Option; fn find_token_with(&self, f: impl Fn(&Token) -> bool) -> Option; } impl FindToken for str { - fn find_token(&self, token: Token) -> Option { - Lexer::new(self) - .flatten() - .find_map(|it| (it.token() == &token).then(|| it.to_span().start())) + fn find_token(&self, token: Token) -> Option { + Lexer::new(self).flatten().find_map(|it| (it.token() == &token).then(|| it.to_span())) } fn find_token_with(&self, f: impl Fn(&Token) -> bool) -> Option { @@ -163,7 +161,9 @@ pub(crate) fn find_comment_end(slice: &str, is_last: bool) -> usize { } let newline_index = slice.find('\n'); - if let Some(separator_index) = slice.find_token(Token::Comma).map(|index| index as usize) { + if let Some(separator_index) = + slice.find_token(Token::Comma).map(|index| index.start() as usize) + { match (block_open_index, newline_index) { (Some(block), None) if block > separator_index => separator_index + 1, (Some(block), None) => { @@ -218,7 +218,7 @@ impl Item for Expression { } fn format(self, visitor: &FmtVisitor) -> String { - visitor.format_expr(self) + visitor.format_sub_expr(self) } } @@ -232,7 +232,7 @@ impl Item for (Ident, Expression) { let (name, expr) = self; let name = name.0.contents; - let expr = visitor.format_expr(expr); + let expr = visitor.format_sub_expr(expr); if name == expr { name diff --git a/tooling/nargo_fmt/src/visitor.rs b/tooling/nargo_fmt/src/visitor.rs index 32e41cb34db..20eb332f9f3 100644 --- a/tooling/nargo_fmt/src/visitor.rs +++ b/tooling/nargo_fmt/src/visitor.rs @@ -2,9 +2,12 @@ mod expr; mod item; mod stmt; -use noirc_frontend::{hir::resolution::errors::Span, token::Token}; +use noirc_frontend::{hir::resolution::errors::Span, lexer::Lexer, token::Token}; -use crate::{config::Config, utils::FindToken}; +use crate::{ + config::Config, + utils::{self, FindToken}, +}; pub(crate) struct FmtVisitor<'me> { config: &'me Config, @@ -30,11 +33,20 @@ impl<'me> FmtVisitor<'me> { &self.source[span.start() as usize..span.end() as usize] } + fn span_after(&self, span: impl Into, token: Token) -> Span { + let span = span.into(); + + let slice = self.slice(span); + let offset = slice.find_token(token).unwrap().end(); + + (span.start() + offset..span.end()).into() + } + fn span_before(&self, span: impl Into, token: Token) -> Span { let span = span.into(); let slice = self.slice(span); - let offset = slice.find_token(token).unwrap(); + let offset = slice.find_token(token).unwrap().start(); (span.start() + offset..span.end()).into() } @@ -76,9 +88,10 @@ impl<'me> FmtVisitor<'me> { } #[track_caller] - fn push_rewrite(&mut self, s: String, span: Span) { + fn push_rewrite(&mut self, rewrite: String, span: Span) { + let rewrite = utils::recover_comment_removed(self.slice(span), rewrite); self.format_missing_indent(span.start(), true); - self.push_str(&s); + self.push_str(&rewrite); } fn format_missing(&mut self, end: u32) { @@ -123,10 +136,44 @@ impl<'me> FmtVisitor<'me> { self.push_vertical_spaces(slice); process_last_slice(self, "", slice); } else { - process_last_slice(self, slice, slice); + if !self.at_start() { + if self.buffer.ends_with('{') { + self.push_str("\n"); + } else { + self.push_vertical_spaces(slice); + } + } + + let (result, last_end) = self.format_comment_in_block(slice); + + if result.is_empty() { + process_last_slice(self, slice, slice); + } else { + self.push_str(result.trim_end()); + let subslice = &slice[last_end as usize..]; + process_last_slice(self, subslice, subslice); + } } } + fn format_comment_in_block(&mut self, slice: &str) -> (String, u32) { + let mut result = String::new(); + let mut last_end = 0; + + for spanned in Lexer::new(slice).skip_comments(false).flatten() { + let span = spanned.to_span(); + last_end = span.end(); + + if let Token::LineComment(_, _) | Token::BlockComment(_, _) = spanned.token() { + result.push_str(&self.indent.to_string()); + result.push_str(&slice[span.start() as usize..span.end() as usize]); + result.push('\n'); + } + } + + (result, last_end) + } + fn push_vertical_spaces(&mut self, slice: &str) { let newline_upper_bound = 2; let newline_lower_bound = 1; @@ -164,7 +211,7 @@ impl<'me> FmtVisitor<'me> { } } -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, Default)] struct Indent { block_indent: usize, } @@ -197,3 +244,9 @@ struct Shape { width: usize, indent: Indent, } + +#[derive(PartialEq, Eq, Debug)] +pub(crate) enum ExpressionType { + Statement, + SubExpression, +} diff --git a/tooling/nargo_fmt/src/visitor/expr.rs b/tooling/nargo_fmt/src/visitor/expr.rs index 89674f3f17a..604d5ca9caf 100644 --- a/tooling/nargo_fmt/src/visitor/expr.rs +++ b/tooling/nargo_fmt/src/visitor/expr.rs @@ -1,30 +1,38 @@ use noirc_frontend::{ hir::resolution::errors::Span, token::Token, ArrayLiteral, BlockExpression, - ConstructorExpression, Expression, ExpressionKind, Literal, Statement, UnaryOp, + ConstructorExpression, Expression, ExpressionKind, IfExpression, Literal, Statement, + StatementKind, UnaryOp, }; -use super::{FmtVisitor, Shape}; +use super::{ExpressionType, FmtVisitor, Indent, Shape}; use crate::{ utils::{self, Expr, FindToken, Item}, Config, }; impl FmtVisitor<'_> { - pub(crate) fn visit_expr(&mut self, expr: Expression) { + pub(crate) fn visit_expr(&mut self, expr: Expression, expr_type: ExpressionType) { let span = expr.span; - let rewrite = self.format_expr(expr); - let rewrite = utils::recover_comment_removed(self.slice(span), rewrite); + let rewrite = self.format_expr(expr, expr_type); self.push_rewrite(rewrite, span); self.last_position = span.end(); } - pub(crate) fn format_expr(&self, Expression { kind, mut span }: Expression) -> String { + pub(crate) fn format_sub_expr(&self, expression: Expression) -> String { + self.format_expr(expression, ExpressionType::SubExpression) + } + + pub(crate) fn format_expr( + &self, + Expression { kind, mut span }: Expression, + expr_type: ExpressionType, + ) -> String { match kind { ExpressionKind::Block(block) => { let mut visitor = self.fork(); - visitor.visit_block(block, span, true); + visitor.visit_block(block, span); visitor.buffer } ExpressionKind::Prefix(prefix) => { @@ -41,24 +49,24 @@ impl FmtVisitor<'_> { } }; - format!("{op}{}", self.format_expr(prefix.rhs)) + format!("{op}{}", self.format_sub_expr(prefix.rhs)) } ExpressionKind::Cast(cast) => { - format!("{} as {}", self.format_expr(cast.lhs), cast.r#type) + format!("{} as {}", self.format_sub_expr(cast.lhs), cast.r#type) } ExpressionKind::Infix(infix) => { format!( "{} {} {}", - self.format_expr(infix.lhs), + self.format_sub_expr(infix.lhs), infix.operator.contents.as_string(), - self.format_expr(infix.rhs) + self.format_sub_expr(infix.rhs) ) } ExpressionKind::Call(call_expr) => { let args_span = self.span_before(call_expr.func.span.end()..span.end(), Token::LeftParen); - let callee = self.format_expr(*call_expr.func); + let callee = self.format_sub_expr(*call_expr.func); let args = format_parens(self.fork(), false, call_expr.arguments, args_span); format!("{callee}{args}") @@ -69,21 +77,21 @@ impl FmtVisitor<'_> { Token::LeftParen, ); - let object = self.format_expr(method_call_expr.object); + let object = self.format_sub_expr(method_call_expr.object); let method = method_call_expr.method_name.to_string(); let args = format_parens(self.fork(), false, method_call_expr.arguments, args_span); format!("{object}.{method}{args}") } ExpressionKind::MemberAccess(member_access_expr) => { - let lhs_str = self.format_expr(member_access_expr.lhs); + let lhs_str = self.format_sub_expr(member_access_expr.lhs); format!("{}.{}", lhs_str, member_access_expr.rhs) } ExpressionKind::Index(index_expr) => { let index_span = self .span_before(index_expr.collection.span.end()..span.end(), Token::LeftBracket); - let collection = self.format_expr(index_expr.collection); + let collection = self.format_sub_expr(index_expr.collection); let index = format_brackets(self.fork(), false, vec![index_expr.index], index_span); format!("{collection}{index}") @@ -96,8 +104,8 @@ impl FmtVisitor<'_> { self.slice(span).to_string() } Literal::Array(ArrayLiteral::Repeated { repeated_element, length }) => { - let repeated = self.format_expr(*repeated_element); - let length = self.format_expr(*length); + let repeated = self.format_sub_expr(*repeated_element); + let length = self.format_sub_expr(*length); format!("[{repeated}; {length}]") } @@ -131,7 +139,7 @@ impl FmtVisitor<'_> { } if !leading.contains("//") && !trailing.contains("//") { - let sub_expr = self.format_expr(*sub_expr); + let sub_expr = self.format_sub_expr(*sub_expr); format!("({leading}{sub_expr}{trailing})") } else { let mut visitor = self.fork(); @@ -140,7 +148,7 @@ impl FmtVisitor<'_> { visitor.indent.block_indent(self.config); let nested_indent = visitor.indent.to_string_with_newline(); - let sub_expr = visitor.format_expr(*sub_expr); + let sub_expr = visitor.format_sub_expr(*sub_expr); let mut result = String::new(); result.push('('); @@ -165,17 +173,73 @@ impl FmtVisitor<'_> { } } ExpressionKind::Constructor(constructor) => { - let type_name = self.slice(constructor.type_name.span()); + let type_name = self.slice(span.start()..constructor.type_name.span().end()); let fields_span = self .span_before(constructor.type_name.span().end()..span.end(), Token::LeftBrace); self.format_struct_lit(type_name, fields_span, *constructor) } - // TODO: - _expr => self.slice(span).to_string(), + ExpressionKind::If(if_expr) => { + let allow_single_line = expr_type == ExpressionType::SubExpression; + + if allow_single_line { + let mut visitor = self.fork(); + visitor.indent = Indent::default(); + if let Some(line) = visitor.format_if_single_line(*if_expr.clone()) { + return line; + } + } + + self.format_if(*if_expr) + } + ExpressionKind::Lambda(_) | ExpressionKind::Variable(_) => self.slice(span).to_string(), + ExpressionKind::Error => unreachable!(), } } + fn format_if(&self, if_expr: IfExpression) -> String { + let condition_str = self.format_sub_expr(if_expr.condition); + let consequence_str = self.format_sub_expr(if_expr.consequence); + + let mut result = format!("if {condition_str} {consequence_str}"); + + if let Some(alternative) = if_expr.alternative { + let alternative = if let Some(ExpressionKind::If(if_expr)) = + extract_simple_expr(alternative.clone()).map(|expr| expr.kind) + { + self.format_if(*if_expr) + } else { + self.format_expr(alternative, ExpressionType::Statement) + }; + + result.push_str(" else "); + result.push_str(&alternative); + }; + + result + } + + fn format_if_single_line(&self, if_expr: IfExpression) -> Option { + let condition_str = self.format_sub_expr(if_expr.condition); + let consequence_str = self.format_sub_expr(extract_simple_expr(if_expr.consequence)?); + + let if_str = if let Some(alternative) = if_expr.alternative { + let alternative_str = if let Some(ExpressionKind::If(_)) = + extract_simple_expr(alternative.clone()).map(|expr| expr.kind) + { + return None; + } else { + self.format_expr(extract_simple_expr(alternative)?, ExpressionType::Statement) + }; + + format!("if {} {{ {} }} else {{ {} }}", condition_str, consequence_str, alternative_str) + } else { + format!("if {{{}}} {{{}}}", condition_str, consequence_str) + }; + + (if_str.len() <= self.config.single_line_if_else_max_width).then_some(if_str) + } + fn format_struct_lit( &self, type_name: &str, @@ -184,6 +248,7 @@ impl FmtVisitor<'_> { ) -> String { let fields = { let mut visitor = self.fork(); + let is_unit_struct = constructor.fields.is_empty(); visitor.indent.block_indent(visitor.config); @@ -206,6 +271,8 @@ impl FmtVisitor<'_> { nested_indent.indent.to_string_with_newline(), visitor.shape().indent.to_string_with_newline() ) + } else if is_unit_struct { + exprs } else { format!(" {exprs} ") } @@ -214,14 +281,9 @@ impl FmtVisitor<'_> { format!("{type_name} {{{fields}}}") } - pub(crate) fn visit_block( - &mut self, - block: BlockExpression, - block_span: Span, - should_indent: bool, - ) { + pub(crate) fn visit_block(&mut self, block: BlockExpression, block_span: Span) { if block.is_empty() { - self.visit_empty_block(block_span, should_indent); + self.visit_empty_block(block_span); return; } @@ -239,10 +301,7 @@ impl FmtVisitor<'_> { self.last_position = block_span.end(); - self.push_str("\n"); - if should_indent { - self.push_str(&self.indent.to_string()); - } + self.push_str(&self.indent.to_string_with_newline()); self.push_str("}"); } @@ -255,19 +314,18 @@ impl FmtVisitor<'_> { } } - fn visit_empty_block(&mut self, block_span: Span, should_indent: bool) { + pub(crate) fn visit_empty_block(&mut self, block_span: Span) { let slice = self.slice(block_span); let comment_str = slice[1..slice.len() - 1].trim(); let block_str = if comment_str.is_empty() { "{}".to_string() } else { self.indent.block_indent(self.config); - let open_indent = self.indent.to_string(); + let (comment_str, _) = self.format_comment_in_block(comment_str); + let comment_str = comment_str.trim_matches('\n'); self.indent.block_unindent(self.config); - let close_indent = if should_indent { self.indent.to_string() } else { String::new() }; - - let ret = format!("{{\n{open_indent}{comment_str}\n{close_indent}}}"); - ret + let close_indent = self.indent.to_string(); + format!("{{\n{comment_str}\n{close_indent}}}") }; self.last_position = block_span.end(); self.push_str(&block_str); @@ -515,3 +573,15 @@ fn has_single_line_comment(slice: &str) -> bool { fn no_long_exprs(exprs: &[Expr], max_width: usize) -> bool { exprs.iter().all(|expr| expr.value.len() <= max_width) } + +fn extract_simple_expr(expr: Expression) -> Option { + if let ExpressionKind::Block(mut block) = expr.kind { + if block.len() == 1 { + if let StatementKind::Expression(expr) = block.pop().unwrap() { + return expr.into(); + } + } + } + + None +} diff --git a/tooling/nargo_fmt/src/visitor/item.rs b/tooling/nargo_fmt/src/visitor/item.rs index 09031082fa3..96821fb4f9d 100644 --- a/tooling/nargo_fmt/src/visitor/item.rs +++ b/tooling/nargo_fmt/src/visitor/item.rs @@ -1,5 +1,6 @@ use noirc_frontend::{ parser::{Item, ItemKind}, + token::Token, NoirFunction, ParsedModule, }; @@ -10,24 +11,72 @@ impl super::FmtVisitor<'_> { (slice.trim_end().to_string(), force_brace_newline) } - pub(crate) fn visit_module(&mut self, module: ParsedModule) { + pub(crate) fn visit_file(&mut self, module: ParsedModule) { + self.visit_module(module); + self.format_missing_indent(self.source.len() as u32, false); + } + + fn visit_module(&mut self, module: ParsedModule) { for Item { kind, span } in module.items { match kind { ItemKind::Function(func) => { let (fn_before_block, force_brace_newline) = self.format_fn_before_block(func.clone(), span.start()); - self.format_missing_indent(span.start(), false); + self.format_missing_indent(span.start(), true); self.push_str(&fn_before_block); self.push_str(if force_brace_newline { "\n" } else { " " }); - self.visit_block(func.def.body, func.def.span, false); + self.visit_block(func.def.body, func.def.span); + } + ItemKind::Submodules(module) => { + let name = module.name; + + self.format_missing(span.start()); + + let after_brace = self.span_after(span, Token::LeftBrace).start(); + self.last_position = after_brace; + + let keyword = if module.is_contract { "contract" } else { "mod" }; + + let indent = if self.at_start() + || self.buffer.ends_with(|ch: char| ch.is_whitespace()) + { + self.indent.to_string() + } else { + self.indent.to_string_with_newline() + }; + self.push_str(&format!("{indent}{keyword} {name} ")); + + if module.contents.items.is_empty() { + self.visit_empty_block((after_brace - 1..span.end()).into()); + } else { + self.push_str("{"); + let indent = self.with_indent(|this| { + this.visit_module(module.contents); + + let mut indent = this.indent; + indent.block_unindent(self.config); + indent.to_string_with_newline() + }); + self.push_str(&format!("{indent}}}")); + } + + self.last_position = span.end(); + } + ItemKind::Import(_) + | ItemKind::Struct(_) + | ItemKind::Trait(_) + | ItemKind::TraitImpl(_) + | ItemKind::Impl(_) + | ItemKind::TypeAlias(_) + | ItemKind::Global(_) + | ItemKind::ModuleDecl(_) => { + self.push_rewrite(self.slice(span).to_string(), span); + self.last_position = span.end(); } - _ => self.format_missing(span.end()), } } - - self.format_missing_indent(self.source.len() as u32, false); } } diff --git a/tooling/nargo_fmt/src/visitor/stmt.rs b/tooling/nargo_fmt/src/visitor/stmt.rs index 724f6bc7156..32f1126cb9d 100644 --- a/tooling/nargo_fmt/src/visitor/stmt.rs +++ b/tooling/nargo_fmt/src/visitor/stmt.rs @@ -1,23 +1,64 @@ -use noirc_frontend::{Statement, StatementKind}; +use std::iter::zip; + +use noirc_frontend::{ConstrainKind, ConstrainStatement, ExpressionKind, Statement, StatementKind}; + +use super::ExpressionType; impl super::FmtVisitor<'_> { pub(crate) fn visit_stmts(&mut self, stmts: Vec) { - for Statement { kind, span } in stmts { + let len = stmts.len(); + + for (Statement { kind, span }, index) in zip(stmts, 1..) { + let is_last = index == len; + match kind { - StatementKind::Expression(expr) => self.visit_expr(expr), + StatementKind::Expression(expr) => self.visit_expr( + expr, + if is_last { ExpressionType::SubExpression } else { ExpressionType::Statement }, + ), StatementKind::Semi(expr) => { - self.visit_expr(expr); + self.visit_expr(expr, ExpressionType::Statement); self.push_str(";"); } StatementKind::Let(let_stmt) => { let let_str = self.slice(span.start()..let_stmt.expression.span.start()).trim_end(); - let expr_str = self.format_expr(let_stmt.expression); + let expr_str = + self.format_expr(let_stmt.expression, ExpressionType::SubExpression); self.push_rewrite(format!("{let_str} {expr_str};"), span); } + StatementKind::Constrain(ConstrainStatement(expr, message, kind)) => { + let message = + message.map_or(String::new(), |message| format!(", \"{message}\"")); + let constrain = match kind { + ConstrainKind::Assert => { + let assertion = self.format_sub_expr(expr); + + format!("assert({assertion}{message});") + } + ConstrainKind::AssertEq => { + if let ExpressionKind::Infix(infix) = expr.kind { + let lhs = self.format_sub_expr(infix.lhs); + let rhs = self.format_sub_expr(infix.rhs); + + format!("assert_eq({lhs}, {rhs}{message});") + } else { + unreachable!() + } + } + ConstrainKind::Constrain => { + let expr = self.format_sub_expr(expr); + format!("constrain {expr};") + } + }; + + self.push_rewrite(constrain, span); + } + StatementKind::For(_) | StatementKind::Assign(_) => { + self.push_rewrite(self.slice(span).to_string(), span); + } StatementKind::Error => unreachable!(), - _ => self.format_missing(span.end()), } self.last_position = span.end(); diff --git a/tooling/nargo_fmt/tests/expected/add.nr b/tooling/nargo_fmt/tests/expected/add.nr index 6f2892942c1..341ed06f3e6 100644 --- a/tooling/nargo_fmt/tests/expected/add.nr +++ b/tooling/nargo_fmt/tests/expected/add.nr @@ -3,5 +3,5 @@ fn main(mut x: u32, y: u32, z: u32) { assert(x == z); x *= 8; - assert(x>9); + assert(x > 9); } diff --git a/tooling/nargo_fmt/tests/expected/call.nr b/tooling/nargo_fmt/tests/expected/call.nr index 8a104b6d5e0..8f627ed1223 100644 --- a/tooling/nargo_fmt/tests/expected/call.nr +++ b/tooling/nargo_fmt/tests/expected/call.nr @@ -18,4 +18,14 @@ fn foo() { my_function(some_function(10, "arg1", another_function()), another_func(20, some_function(), 30)); outer_function(some_function(), another_function(some_function(), some_value)); + + assert_eq(x, y); + + assert_eq(x, y, "message"); + + assert(x); + + assert(x, "message"); + + assert(x == y); } diff --git a/tooling/nargo_fmt/tests/expected/contract.nr b/tooling/nargo_fmt/tests/expected/contract.nr new file mode 100644 index 00000000000..54fe6ff1419 --- /dev/null +++ b/tooling/nargo_fmt/tests/expected/contract.nr @@ -0,0 +1,73 @@ +// Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +// Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +// Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +// Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. +contract Benchmarking { + use dep::value_note::{ + utils::{increment, decrement}, + value_note::{VALUE_NOTE_LEN, ValueNote, ValueNoteMethods}, + }; + + use dep::aztec::{ + context::{Context}, + note::{utils as note_utils, note_getter_options::NoteGetterOptions, note_header::NoteHeader}, + selector::compute_selector, + log::emit_unencrypted_log, + state_vars::{map::Map, public_state::PublicState, set::Set}, + types::type_serialization::field_serialization::{FieldSerializationMethods, FIELD_SERIALIZED_LEN}, + types::address::{AztecAddress}, + }; + + struct Storage { + notes: Map>, + balances: Map>, + } + + impl Storage { + fn init(context: Context) -> pub Self { + Storage { + notes: Map::new(context, 1, |context, slot| { Set::new(context, slot, ValueNoteMethods) }), + balances: Map::new(context, 2, |context, slot| { PublicState::new(context, slot, FieldSerializationMethods) }), + } + } + } + + #[aztec(private)] + fn constructor() {} + + // Nec tincidunt praesent semper feugiat nibh sed pulvinar. Nibh nisl condimentum id venenatis a. + #[aztec(private)] + fn create_note(owner: Field, value: Field) { + increment(storage.notes.at(owner), value, owner); + } + + // Diam quam nulla porttitor massa id. Elit ullamcorper dignissim cras tincidunt lobortis feugiat. + #[aztec(private)] + fn recreate_note(owner: Field, index: u32) { + let owner_notes = storage.notes.at(owner); + let getter_options = NoteGetterOptions::new().set_limit(1).set_offset(index); + let notes = owner_notes.get_notes(getter_options); + let note = notes[0].unwrap_unchecked(); + owner_notes.remove(note); + increment(owner_notes, note.value, owner); + } + + // Ultrices in iaculis nunc sed augue lacus. + #[aztec(public)] + fn increment_balance(owner: Field, value: Field) { + let current = storage.balances.at(owner).read(); + storage.balances.at(owner).write(current + value); + let _callStackItem1 = context.call_public_function(context.this_address(), compute_selector("broadcast(Field)"), [owner]); + } + + // Est ultricies integer quis auctor elit sed. In nibh mauris cursus mattis molestie a iaculis. + #[aztec(public)] + fn broadcast(owner: Field) { + emit_unencrypted_log(&mut context, storage.balances.at(owner).read()); + } + + unconstrained fn compute_note_hash_and_nullifier(contract_address: Field, nonce: Field, storage_slot: Field, preimage: [Field; VALUE_NOTE_LEN]) -> [Field; 4] { + let note_header = NoteHeader::new(contract_address, nonce, storage_slot); + note_utils::compute_note_hash_and_nullifier(ValueNoteMethods, note_header, preimage) + } +} diff --git a/tooling/nargo_fmt/tests/expected/expr.nr b/tooling/nargo_fmt/tests/expected/expr.nr index 6c95b6925ef..c0047e90175 100644 --- a/tooling/nargo_fmt/tests/expected/expr.nr +++ b/tooling/nargo_fmt/tests/expected/expr.nr @@ -50,7 +50,7 @@ fn only_comment() { fn only_comments() { // Keep this here -// Keep this here + // Keep this here } fn only_comments() { @@ -94,3 +94,29 @@ fn parenthesized() { fn constructor() { Point { x: 5, y: 10 }; } + +fn if_expr() { + if true { + println("Hello :D"); + } +} + +fn return_if_expr() { + if true { 42 } else { 40 + 2 } +} + +fn return_if_expr() { + if true { + 42 + }; + + if true { 42 } else { 40 + 2 } +} + +fn if_if() { + if cond { + some(); + } else { + none(); + }.bar().baz(); +} diff --git a/tooling/nargo_fmt/tests/expected/if.nr b/tooling/nargo_fmt/tests/expected/if.nr new file mode 100644 index 00000000000..30a60e50577 --- /dev/null +++ b/tooling/nargo_fmt/tests/expected/if.nr @@ -0,0 +1,52 @@ +fn main() { + let (x,y) = if is_square(gx1) { + (x1, sqrt(gx1)) + } else { + (x2, sqrt(gx2)) + }; + + let n = if x != y { + if x != 20 { slice.push_back(y) } else { slice } + } else { + slice + }; + + if false { + (); + (); + } + + if false // lone if comment + { + (); + (); + } + + let a = if 0 > 1 { 0 } else { 0 }; + + if true { + (); + } else if false { + (); + (); + } else { + (); + (); + (); + } + + if true // else-if-chain if comment + { + (); + } + else if false // else-if-chain else-if comment + { + (); + (); + } else // else-if-chain else comment + { + (); + (); + (); + } +} diff --git a/tooling/nargo_fmt/tests/expected/let.nr b/tooling/nargo_fmt/tests/expected/let.nr index c53f43c7e65..dccdb76328c 100644 --- a/tooling/nargo_fmt/tests/expected/let.nr +++ b/tooling/nargo_fmt/tests/expected/let.nr @@ -48,4 +48,11 @@ fn let_() { }; let expr = Expr { /*A boolean literal (true, false).*/ kind: ExprKind::Bool(true) }; + + let mut V = dep::crate2::MyStruct { Q: x }; + let mut V = dep::crate2::MyStruct {}; + let mut V = dep::crate2::MyStruct {/*test*/}; + let mut V = dep::crate2::MyStruct { + // sad + }; } diff --git a/tooling/nargo_fmt/tests/expected/nested_if_else.nr b/tooling/nargo_fmt/tests/expected/nested_if_else.nr index 8aa120e3b18..dfd203189e8 100644 --- a/tooling/nargo_fmt/tests/expected/nested_if_else.nr +++ b/tooling/nargo_fmt/tests/expected/nested_if_else.nr @@ -1,3 +1,9 @@ fn nested_if_else() { - if false { 1 } else if false { 2 } else { 3 } + if false { + 1 + } else if false { + 2 + } else { + 3 + } } diff --git a/tooling/nargo_fmt/tests/expected/submodule.nr b/tooling/nargo_fmt/tests/expected/submodule.nr new file mode 100644 index 00000000000..88e005b7ac2 --- /dev/null +++ b/tooling/nargo_fmt/tests/expected/submodule.nr @@ -0,0 +1,30 @@ +mod a { + // hello +} + +mod a { + // hello + mod b { + // hello + } +} + +mod a { + mod b { + mod c {} + } +} + +mod a { + // 1 + // 2 + // 3 + /*test*/ +} + +mod a { + /**/ + mod b { + mod c {} + } +} diff --git a/tooling/nargo_fmt/tests/expected/tuple.nr b/tooling/nargo_fmt/tests/expected/tuple.nr index b190a5f7c55..c3b32904f15 100644 --- a/tooling/nargo_fmt/tests/expected/tuple.nr +++ b/tooling/nargo_fmt/tests/expected/tuple.nr @@ -23,7 +23,7 @@ fn main() { (/*1*/ 1, /*2*/ 2); -// FIXME: + // FIXME: (((//2 1,),),); (/*a*/ diff --git a/tooling/nargo_fmt/tests/input/call.nr b/tooling/nargo_fmt/tests/input/call.nr index f76157e83ca..24e61c806cc 100644 --- a/tooling/nargo_fmt/tests/input/call.nr +++ b/tooling/nargo_fmt/tests/input/call.nr @@ -32,4 +32,14 @@ fn foo() { another_function( some_function(), some_value) ); + + assert_eq( x, y ); + + assert_eq( x, y, "message" ); + + assert( x ); + + assert( x, "message" ); + + assert( x == y ); } diff --git a/tooling/nargo_fmt/tests/input/contract.nr b/tooling/nargo_fmt/tests/input/contract.nr new file mode 100644 index 00000000000..54fe6ff1419 --- /dev/null +++ b/tooling/nargo_fmt/tests/input/contract.nr @@ -0,0 +1,73 @@ +// Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +// Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +// Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +// Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. +contract Benchmarking { + use dep::value_note::{ + utils::{increment, decrement}, + value_note::{VALUE_NOTE_LEN, ValueNote, ValueNoteMethods}, + }; + + use dep::aztec::{ + context::{Context}, + note::{utils as note_utils, note_getter_options::NoteGetterOptions, note_header::NoteHeader}, + selector::compute_selector, + log::emit_unencrypted_log, + state_vars::{map::Map, public_state::PublicState, set::Set}, + types::type_serialization::field_serialization::{FieldSerializationMethods, FIELD_SERIALIZED_LEN}, + types::address::{AztecAddress}, + }; + + struct Storage { + notes: Map>, + balances: Map>, + } + + impl Storage { + fn init(context: Context) -> pub Self { + Storage { + notes: Map::new(context, 1, |context, slot| { Set::new(context, slot, ValueNoteMethods) }), + balances: Map::new(context, 2, |context, slot| { PublicState::new(context, slot, FieldSerializationMethods) }), + } + } + } + + #[aztec(private)] + fn constructor() {} + + // Nec tincidunt praesent semper feugiat nibh sed pulvinar. Nibh nisl condimentum id venenatis a. + #[aztec(private)] + fn create_note(owner: Field, value: Field) { + increment(storage.notes.at(owner), value, owner); + } + + // Diam quam nulla porttitor massa id. Elit ullamcorper dignissim cras tincidunt lobortis feugiat. + #[aztec(private)] + fn recreate_note(owner: Field, index: u32) { + let owner_notes = storage.notes.at(owner); + let getter_options = NoteGetterOptions::new().set_limit(1).set_offset(index); + let notes = owner_notes.get_notes(getter_options); + let note = notes[0].unwrap_unchecked(); + owner_notes.remove(note); + increment(owner_notes, note.value, owner); + } + + // Ultrices in iaculis nunc sed augue lacus. + #[aztec(public)] + fn increment_balance(owner: Field, value: Field) { + let current = storage.balances.at(owner).read(); + storage.balances.at(owner).write(current + value); + let _callStackItem1 = context.call_public_function(context.this_address(), compute_selector("broadcast(Field)"), [owner]); + } + + // Est ultricies integer quis auctor elit sed. In nibh mauris cursus mattis molestie a iaculis. + #[aztec(public)] + fn broadcast(owner: Field) { + emit_unencrypted_log(&mut context, storage.balances.at(owner).read()); + } + + unconstrained fn compute_note_hash_and_nullifier(contract_address: Field, nonce: Field, storage_slot: Field, preimage: [Field; VALUE_NOTE_LEN]) -> [Field; 4] { + let note_header = NoteHeader::new(contract_address, nonce, storage_slot); + note_utils::compute_note_hash_and_nullifier(ValueNoteMethods, note_header, preimage) + } +} diff --git a/tooling/nargo_fmt/tests/input/expr.nr b/tooling/nargo_fmt/tests/input/expr.nr index ff2ac1e10a2..28ba9cb0585 100644 --- a/tooling/nargo_fmt/tests/input/expr.nr +++ b/tooling/nargo_fmt/tests/input/expr.nr @@ -103,4 +103,33 @@ fn parenthesized() { fn constructor() { Point{x :5, y: 10 }; +} + +fn if_expr() { + if true { println("Hello :D"); } +} + +fn return_if_expr() { + if true { +42 +} +else +{ 40 + 2 } +} + +fn return_if_expr() { + if true {42}; + + if true { + 42 + } + else { + 40 + + 2 } +} + +fn if_if() { +if cond { some(); } else { none(); } + .bar() + .baz(); } \ No newline at end of file diff --git a/tooling/nargo_fmt/tests/input/if.nr b/tooling/nargo_fmt/tests/input/if.nr new file mode 100644 index 00000000000..ab39df006ea --- /dev/null +++ b/tooling/nargo_fmt/tests/input/if.nr @@ -0,0 +1,56 @@ +fn main() { + let (x,y) = if is_square(gx1) {(x1, sqrt(gx1))} else {(x2, sqrt(gx2))}; + + let n = if x != y { if x != 20 { slice.push_back(y) } else { slice } } else { slice }; + + if false + { + (); + (); + } + + if false // lone if comment + { + (); + (); + } + + + let a = + if 0 > 1 { + 0 + } + else + { + 0 + }; + + + if true + { + (); + } else if false { + (); + (); + } + else { + (); + (); + (); + } + + if true // else-if-chain if comment + { + (); + } + else if false // else-if-chain else-if comment + { + (); + (); + } else // else-if-chain else comment + { + (); + (); + (); + } +} diff --git a/tooling/nargo_fmt/tests/input/let.nr b/tooling/nargo_fmt/tests/input/let.nr index c603d44a4ad..ae23f0150d9 100644 --- a/tooling/nargo_fmt/tests/input/let.nr +++ b/tooling/nargo_fmt/tests/input/let.nr @@ -24,4 +24,11 @@ kind: ExprKind::Bool(true), }; let expr = Expr {/*A boolean literal (true, false).*/kind: ExprKind::Bool(true),}; + + let mut V = dep::crate2::MyStruct { Q: x }; + let mut V = dep::crate2::MyStruct {}; + let mut V = dep::crate2::MyStruct {/*test*/}; + let mut V = dep::crate2::MyStruct { + // sad + }; } diff --git a/tooling/nargo_fmt/tests/input/submodule.nr b/tooling/nargo_fmt/tests/input/submodule.nr new file mode 100644 index 00000000000..d5b162fd28d --- /dev/null +++ b/tooling/nargo_fmt/tests/input/submodule.nr @@ -0,0 +1,25 @@ +mod a { +// hello +} + +mod a { +// hello + mod b { +// hello + } +} + +mod a {mod b {mod c {}}} + +mod a { +// 1 +// 2 +// 3 +/*test*/ +} + +mod a { + /**/ mod b { + mod c {} + } +} diff --git a/tooling/noir_js/package.json b/tooling/noir_js/package.json index 8c11349f410..09dfac939d2 100644 --- a/tooling/noir_js/package.json +++ b/tooling/noir_js/package.json @@ -3,7 +3,7 @@ "collaborators": [ "The Noir Team " ], - "version": "0.17.0", + "version": "0.18.0", "packageManager": "yarn@3.5.1", "license": "(MIT OR Apache-2.0)", "type": "module", diff --git a/tooling/noir_js/src/index.ts b/tooling/noir_js/src/index.ts index 3d424ca0dea..35a3e716b6b 100644 --- a/tooling/noir_js/src/index.ts +++ b/tooling/noir_js/src/index.ts @@ -1,8 +1,16 @@ import * as acvm from '@noir-lang/acvm_js'; import * as abi from '@noir-lang/noirc_abi'; - export { acvm, abi }; -export { WitnessMap } from '@noir-lang/acvm_js'; +export { + ecdsa_secp256r1_verify, + ecdsa_secp256k1_verify, + keccak256, + blake2s256, + sha256, + xor, + and, +} from '@noir-lang/acvm_js'; +export { WitnessMap, ForeignCallHandler, ForeignCallInput, ForeignCallOutput } from '@noir-lang/acvm_js'; export { Noir } from './program.js'; diff --git a/tooling/noir_js/test/noir_compiled_examples/assert_lt/src/main.nr b/tooling/noir_js/test/noir_compiled_examples/assert_lt/src/main.nr index 8deda68c051..67b8c84bdcd 100644 --- a/tooling/noir_js/test/noir_compiled_examples/assert_lt/src/main.nr +++ b/tooling/noir_js/test/noir_compiled_examples/assert_lt/src/main.nr @@ -1,4 +1,4 @@ fn main(x : u64, y : pub u64) -> pub u64 { - assert(x < y); - x + y + assert(x < y); + x + y } diff --git a/tooling/noir_js_backend_barretenberg/package.json b/tooling/noir_js_backend_barretenberg/package.json index ae56c5dd507..fda43583629 100644 --- a/tooling/noir_js_backend_barretenberg/package.json +++ b/tooling/noir_js_backend_barretenberg/package.json @@ -3,7 +3,7 @@ "collaborators": [ "The Noir Team " ], - "version": "0.17.0", + "version": "0.18.0", "packageManager": "yarn@3.5.1", "license": "(MIT OR Apache-2.0)", "type": "module", @@ -32,7 +32,7 @@ "lint": "NODE_NO_WARNINGS=1 eslint . --ext .ts --ignore-path ./.eslintignore --max-warnings 0" }, "dependencies": { - "@aztec/bb.js": "0.8.10", + "@aztec/bb.js": "0.12.0", "@noir-lang/types": "workspace:*", "fflate": "^0.8.0" }, diff --git a/tooling/noir_js_types/package.json b/tooling/noir_js_types/package.json index fb0f806140e..06f7ce41d1f 100644 --- a/tooling/noir_js_types/package.json +++ b/tooling/noir_js_types/package.json @@ -4,7 +4,7 @@ "The Noir Team " ], "packageManager": "yarn@3.5.1", - "version": "0.17.0", + "version": "0.18.0", "license": "(MIT OR Apache-2.0)", "files": [ "lib", diff --git a/tooling/noirc_abi_wasm/package.json b/tooling/noirc_abi_wasm/package.json index 338251253cb..f98340f7131 100644 --- a/tooling/noirc_abi_wasm/package.json +++ b/tooling/noirc_abi_wasm/package.json @@ -3,7 +3,7 @@ "collaborators": [ "The Noir Team " ], - "version": "0.17.0", + "version": "0.18.0", "license": "(MIT OR Apache-2.0)", "files": [ "nodejs", diff --git a/yarn.lock b/yarn.lock index 71338f8f3a3..2efc4eabfec 100644 --- a/yarn.lock +++ b/yarn.lock @@ -221,9 +221,9 @@ __metadata: languageName: node linkType: hard -"@aztec/bb.js@npm:0.8.10": - version: 0.8.10 - resolution: "@aztec/bb.js@npm:0.8.10" +"@aztec/bb.js@npm:0.12.0": + version: 0.12.0 + resolution: "@aztec/bb.js@npm:0.12.0" dependencies: comlink: ^4.4.1 commander: ^10.0.1 @@ -231,7 +231,7 @@ __metadata: tslib: ^2.4.0 bin: bb.js: dest/node/main.js - checksum: c77f6e27f626edca1477e4d94794d43b373dfcb527f00579e20270fc92794f9e4bc5df2c25ebbce564700c114cdf69e0b213ddb0192c24af4fc4cdf468918702 + checksum: d9d57b893b9b1c61949cb9bd911d4b7e1ece34965ccb9e122b39cd4e2edac9f06858abbe05c23f66141c9a74ff4861a354bfc5d62e5dcf772cfe8d1c783e2562 languageName: node linkType: hard @@ -3434,7 +3434,7 @@ __metadata: version: 0.0.0-use.local resolution: "@noir-lang/backend_barretenberg@workspace:tooling/noir_js_backend_barretenberg" dependencies: - "@aztec/bb.js": 0.8.10 + "@aztec/bb.js": 0.12.0 "@noir-lang/types": "workspace:*" "@types/node": ^20.6.2 "@types/prettier": ^3