diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000000..25dd24f015c --- /dev/null +++ b/.dockerignore @@ -0,0 +1,22 @@ +Dockerfile* +.dockerignore + +packages +**/package.tgz +**/target +**/node_modules +**/outputs + +# Source resolver +compiler/source-resolver/lib +compiler/source-resolver/lib-node + +# Noir.js +tooling/noir_js/lib + +# Wasm build artifacts +compiler/wasm/nodejs +compiler/wasm/web +tooling/noirc_abi_wasm/nodejs +tooling/noirc_abi_wasm/web +tooling/noir_js/lib \ No newline at end of file diff --git a/.github/workflows/auto-pr-rebuild-script.yml b/.github/workflows/auto-pr-rebuild-script.yml index 4937de98e04..336f2288878 100644 --- a/.github/workflows/auto-pr-rebuild-script.yml +++ b/.github/workflows/auto-pr-rebuild-script.yml @@ -99,7 +99,7 @@ jobs: git config --local user.email kevtheappdev@gmail.com - name: Run rebuild script - working-directory: tooling/nargo_cli/tests + working-directory: test_programs run: | chmod +x ./rebuild.sh ./rebuild.sh @@ -108,14 +108,14 @@ jobs: uses: actions/upload-artifact@v3 with: name: acir-artifacts - path: ./tooling/nargo_cli/tests/acir_artifacts + path: ./test_programs/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" + git diff --quiet test_programs/acir_artifacts/ || echo "::set-output name=changes::true" - name: Create or Update PR if: steps.check_changes.outputs.changes == 'true' @@ -125,6 +125,6 @@ jobs: commit-message: "chore: update acir artifacts" title: "chore: Update ACIR artifacts" body: "Automatic PR to update acir artifacts" - add-paths: tooling/nargo_cli/tests/acir_artifacts/*.gz + add-paths: test_programs/acir_artifacts/*.gz labels: "auto-pr" branch: "auto-pr-rebuild-script-branch" diff --git a/.github/workflows/build-aztec-feature-flag.yml b/.github/workflows/build-aztec-feature-flag.yml index 888a88a7f88..bacf74ba7b1 100644 --- a/.github/workflows/build-aztec-feature-flag.yml +++ b/.github/workflows/build-aztec-feature-flag.yml @@ -42,4 +42,4 @@ jobs: save-if: ${{ github.event_name != 'merge_group' }} - name: Build with feature flag - run: cargo build --features="noirc_frontend/aztec" + run: cargo build --features="noirc_driver/aztec" diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml deleted file mode 100644 index cdebb2b8bcc..00000000000 --- a/.github/workflows/build-docs.yml +++ /dev/null @@ -1,87 +0,0 @@ -name: Build docs - -on: - pull_request: - -jobs: - add_label: - runs-on: ubuntu-latest - outputs: - has_label: ${{ steps.check-labels.outputs.result }} - steps: - - name: Check if label is present - id: check-labels - uses: actions/github-script@v3 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const labels = context.payload.pull_request.labels.map(label => label.name); - if (labels.includes('documentation')) { - return true; - } - - // Fetch the list of files changed in the PR - const { data: files } = await github.pulls.listFiles({ - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: context.issue.number - }); - - // Check if any file is within the 'docs' folder - const docsChanged = files.some(file => file.filename.startsWith('docs/')); - return docsChanged; - - - name: Add label if not present - if: steps.check-labels.outputs.result == 'true' - uses: actions/github-script@v3 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const labels = context.payload.pull_request.labels.map(label => label.name); - if (!labels.includes('documentation')) { - github.issues.addLabels({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.issue.number, - labels: ['documentation'] - }) - } - - build_and_deploy: - runs-on: ubuntu-latest - permissions: - pull-requests: write - needs: add_label - if: needs.add_label.outputs.has_label == 'true' - steps: - - name: Checkout code - uses: actions/checkout@v2 - - - name: Setup Node.js - uses: actions/setup-node@v2 - with: - node-version: '18' - - - name: Install dependencies - run: yarn - - - name: Build docs - run: yarn workspace docs build - - - name: Remove pre-releases - working-directory: docs - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: yarn setStable - - - name: Deploy to Netlify - uses: nwtgck/actions-netlify@v2.1 - with: - publish-dir: './docs/build' - github-token: ${{ secrets.GITHUB_TOKEN }} - enable-github-deployment: false - deploy-message: "Deploy from GitHub Actions for PR ${{ github.event.number }}" - env: - NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} - NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }} - timeout-minutes: 1 diff --git a/.github/workflows/docs-pr.yml b/.github/workflows/docs-pr.yml new file mode 100644 index 00000000000..12526df80f0 --- /dev/null +++ b/.github/workflows/docs-pr.yml @@ -0,0 +1,92 @@ +name: Deploy preview for PR + +on: + pull_request: + +jobs: + add_label: + runs-on: ubuntu-latest + outputs: + has_label: ${{ steps.check-labels.outputs.result }} + steps: + - name: Check if label is present + id: check-labels + uses: actions/github-script@v3 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const labels = context.payload.pull_request.labels.map(label => label.name); + if (labels.includes('documentation')) { + return true; + } + + // Fetch the list of files changed in the PR + const { data: files } = await github.pulls.listFiles({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.issue.number + }); + + // Check if any file is within the 'docs' folder + const docsChanged = files.some(file => file.filename.startsWith('docs/')); + return docsChanged; + + - name: Add label if not present + if: steps.check-labels.outputs.result == 'true' + uses: actions/github-script@v3 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const labels = context.payload.pull_request.labels.map(label => label.name); + if (!labels.includes('documentation')) { + github.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + labels: ['documentation'] + }) + } + + build_and_deploy_preview: + runs-on: ubuntu-latest + permissions: + pull-requests: write + needs: add_label + if: needs.add_label.outputs.has_label == 'true' + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v2 + with: + node-version: '18' + + - name: Install wasm-bindgen-cli + uses: taiki-e/install-action@v2 + with: + tool: wasm-bindgen-cli@0.2.86 + + - name: Install wasm-opt + run: | + npm i wasm-opt -g + + - name: Install Yarn dependencies + uses: ./.github/actions/setup + + - name: Build docs + working-directory: docs + run: + yarn workspaces foreach -Rt run build + + - name: Deploy to Netlify + uses: nwtgck/actions-netlify@v2.1 + with: + publish-dir: './docs/build' + github-token: ${{ secrets.GITHUB_TOKEN }} + enable-github-deployment: false + deploy-message: "Deploy from GitHub Actions for PR ${{ github.event.number }}" + env: + NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} + NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }} + timeout-minutes: 1 diff --git a/.github/workflows/gates_report.yml b/.github/workflows/gates_report.yml index 41a68c65852..8e3ef768828 100644 --- a/.github/workflows/gates_report.yml +++ b/.github/workflows/gates_report.yml @@ -50,9 +50,7 @@ jobs: pull-requests: write steps: - - uses: actions/checkout@v3 - with: - submodules: recursive + - uses: actions/checkout@v4 - name: Download nargo binary uses: actions/download-artifact@v3 @@ -69,10 +67,10 @@ jobs: nargo -V - name: Generate gates report - working-directory: ./tooling/nargo_cli/tests + working-directory: ./test_programs run: | ./gates_report.sh - mv gates_report.json ../../../gates_report.json + mv gates_report.json ../gates_report.json - name: Compare gates reports id: gates_diff diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml index 4c20c9181db..4ef7dd89777 100644 --- a/.github/workflows/publish-docs.yml +++ b/.github/workflows/publish-docs.yml @@ -1,47 +1,47 @@ -name: Publish Docs +name: Publish documentation on: workflow_dispatch: inputs: - tag: - description: The tag to build Docs for + noir-ref: + description: The noir reference to checkout required: false + default: 'master' jobs: publish-docs: + name: Publish docs runs-on: ubuntu-latest - if: ${{ inputs.tag != '' }} - permissions: - pull-requests: write + steps: - - name: Checkout sources + - name: Checkout release branch uses: actions/checkout@v4 with: - ref: ${{ inputs.tag }} + ref: ${{ inputs.noir-ref }} + token: ${{ secrets.NOIR_RELEASES_TOKEN }} - name: Setup Node.js uses: actions/setup-node@v2 with: node-version: '18' - - - name: Install Yarn - run: npm install -g yarn - - name: Install Yarn dependencies - run: yarn - - - name: Cut a new version - working-directory: ./docs - run: yarn docusaurus docs:version ${{ inputs.tag }} - - - name: Remove pre-releases - id: get_version + - name: Install wasm-bindgen-cli + uses: taiki-e/install-action@v2 + with: + tool: wasm-bindgen-cli@0.2.86 + + - name: Install wasm-opt run: | - cd docs && yarn setStable + npm i wasm-opt -g + + - name: Install Yarn dependencies + uses: ./.github/actions/setup + + - name: Build docs for deploying + working-directory: docs + run: + yarn workspaces foreach -Rt run build - - name: Build docs - run: yarn workspace docs build - - name: Deploy to Netlify uses: nwtgck/actions-netlify@v2.1 with: @@ -50,7 +50,7 @@ jobs: production-deploy: true github-token: ${{ secrets.GITHUB_TOKEN }} enable-github-deployment: false - deploy-message: "Deploy from GitHub Actions for tag ${{ inputs.tag }}" + deploy-message: "Deploy from GitHub Actions for tag ${{ inputs.noir-ref }}" env: NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }} diff --git a/.github/workflows/publish-es-packages.yml b/.github/workflows/publish-es-packages.yml index 2e88ee2b77f..f421672c799 100644 --- a/.github/workflows/publish-es-packages.yml +++ b/.github/workflows/publish-es-packages.yml @@ -33,7 +33,7 @@ jobs: - name: Enable aztec features if: ${{ inputs.npm-tag == 'aztec' }} run: | - echo $'\n'"default = [\"aztec\"]"$'\n' >> compiler/noirc_frontend/Cargo.toml + echo $'\n'"default = [\"aztec\"]"$'\n' >> compiler/noirc_driver/Cargo.toml - name: Build wasm package run: | diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d785eefbc14..744b4e3effc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -50,6 +50,42 @@ jobs: git commit -m 'chore: Update lockfile' git push + + update-docs: + name: Update docs + needs: [release-please, update-lockfile] + if: ${{ needs.release-please.outputs.release-pr }} + runs-on: ubuntu-latest + steps: + - name: Checkout release branch + uses: actions/checkout@v4 + with: + ref: ${{ fromJSON(needs.release-please.outputs.release-pr).headBranchName }} + token: ${{ secrets.NOIR_RELEASES_TOKEN }} + + - 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 ${{ needs.release-please.outputs.tag-name }} + + - name: Configure git + run: | + git config --local user.name 'signorecello' + git config --local user.email 'github@zepedro.me' + + - name: Commit new documentation version + run: | + git add . + git commit -m "chore(docs): cut new docs version for tag ${{ needs.release-please.outputs.tag-name }}" + git push + build-binaries: name: Build binaries needs: [release-please] @@ -78,19 +114,18 @@ jobs: ref: master token: ${{ secrets.NOIR_REPO_TOKEN }} inputs: '{ "noir-ref": "${{ needs.release-please.outputs.tag-name }}", "npm-tag": "latest" }' - publish-docs: name: Publish docs needs: [release-please] if: ${{ needs.release-please.outputs.tag-name }} runs-on: ubuntu-latest + steps: - - name: Dispatch to publish workflow + - name: Dispatch to publish-docs uses: benc-uk/workflow-dispatch@v1 with: workflow: publish-docs.yml - repo: noir-lang/noir ref: master - token: ${{ secrets.GITHUB_TOKEN }} - inputs: '{ "tag": "${{ needs.release-please.outputs.tag-name }}"}' + token: ${{ secrets.NOIR_REPO_TOKEN }} + inputs: '{ "noir-ref": "${{ needs.release-please.outputs.tag-name }}" }' diff --git a/.github/workflows/test-abi_wasm.yml b/.github/workflows/test-abi_wasm.yml deleted file mode 100644 index 52223d874bf..00000000000 --- a/.github/workflows/test-abi_wasm.yml +++ /dev/null @@ -1,71 +0,0 @@ -name: ABI Wasm test - -on: - pull_request: - merge_group: - push: - branches: - - master - -# This will cancel previous runs when a branch or PR is updated -concurrency: - group: ${{ github.workflow }}-${{ github.head_ref || github.ref || github.run_id }} - cancel-in-progress: true - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - name: Checkout sources - uses: actions/checkout@v3 - - - name: Setup Nix - uses: ./.github/actions/nix - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - nix-cache-name: "noir" - cachix-auth-token: ${{ secrets.CACHIXAUTHTOKEN }} - - - name: Build noirc_abi_wasm - run: | - nix build -L .#noirc_abi_wasm - cp -r ./result/noirc_abi_wasm/nodejs ./tooling/noirc_abi_wasm - cp -r ./result/noirc_abi_wasm/web ./tooling/noirc_abi_wasm - - - name: Dereference symlink - run: echo "UPLOAD_PATH=$(readlink -f ./result/noirc_abi_wasm)" >> $GITHUB_ENV - - - name: Upload artifact - uses: actions/upload-artifact@v3 - with: - name: noirc_abi_wasm - path: ${{ env.UPLOAD_PATH }} - retention-days: 10 - - test: - runs-on: ubuntu-latest - needs: [build] - - steps: - - name: Checkout sources - uses: actions/checkout@v3 - - - name: Download wasm package artifact - uses: actions/download-artifact@v3 - with: - name: noirc_abi_wasm - path: ./tooling/noirc_abi_wasm - - - name: Install Yarn dependencies - uses: ./.github/actions/setup - - - name: Run node tests - run: yarn workspace @noir-lang/noirc_abi test - - - name: Install Playwright - uses: ./.github/actions/install-playwright - - - name: Run browser tests - run: yarn workspace @noir-lang/noirc_abi test:browser - diff --git a/.github/workflows/test-acvm-js.yml b/.github/workflows/test-acvm-js.yml deleted file mode 100644 index 14ce5d916c0..00000000000 --- a/.github/workflows/test-acvm-js.yml +++ /dev/null @@ -1,99 +0,0 @@ -name: Test acvm_js - -on: - pull_request: - merge_group: - push: - branches: - - master - -# This will cancel previous runs when a branch or PR is updated -concurrency: - group: ${{ github.workflow }}-${{ github.head_ref || github.ref || github.run_id }} - cancel-in-progress: true - -jobs: - build-acvm-js-package: - runs-on: ubuntu-latest - steps: - - name: Checkout sources - uses: actions/checkout@v4 - - - name: Setup Nix - uses: ./.github/actions/nix - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - nix-cache-name: "noir" - cachix-auth-token: ${{ secrets.CACHIXAUTHTOKEN }} - - - name: Build acvm-js - run: | - nix build -L .#acvm_js - - - name: Dereference symlink - run: echo "UPLOAD_PATH=$(readlink -f result)" >> $GITHUB_ENV - - - name: Upload artifact - uses: actions/upload-artifact@v3 - with: - name: acvm-js - path: ${{ env.UPLOAD_PATH }} - retention-days: 3 - - test-acvm_js-node: - needs: [build-acvm-js-package] - name: Node.js Tests - runs-on: ubuntu-latest - - steps: - - name: Checkout sources - uses: actions/checkout@v4 - - - name: Download artifact - uses: actions/download-artifact@v3 - with: - name: acvm-js - path: ./result - - - name: Move build artifacts - run: | - mv ./result/acvm_js/nodejs ./acvm-repo/acvm_js/nodejs - mv ./result/acvm_js/web ./acvm-repo/acvm_js/web - - - name: Set up test environment - uses: ./.github/actions/setup - - - name: Run node tests - run: yarn workspace @noir-lang/acvm_js test - - test-acvm_js-browser: - needs: [build-acvm-js-package] - name: Browser Tests - runs-on: ubuntu-latest - - steps: - - name: Checkout sources - uses: actions/checkout@v4 - - - name: Download artifact - uses: actions/download-artifact@v3 - with: - name: acvm-js - path: ./result - - - name: Move build artifacts - run: | - mv ./result/acvm_js/nodejs ./acvm-repo/acvm_js/nodejs - mv ./result/acvm_js/web ./acvm-repo/acvm_js/web - - - name: Set up test environment - uses: ./.github/actions/setup - - - name: Install playwright deps - run: | - npx playwright install - npx playwright install-deps - - - name: Run browser tests - working-directory: ./acvm-repo/acvm_js - run: yarn workspace @noir-lang/acvm_js test:browser diff --git a/.github/workflows/test-integration.yml b/.github/workflows/test-integration.yml deleted file mode 100644 index ad28d9c8e86..00000000000 --- a/.github/workflows/test-integration.yml +++ /dev/null @@ -1,199 +0,0 @@ -name: test-integration - -on: - workflow_dispatch: - pull_request: - merge_group: - schedule: - - cron: "0 2 * * *" # Run nightly at 2 AM UTC - -jobs: - build-nargo: - runs-on: ubuntu-latest - strategy: - matrix: - target: [x86_64-unknown-linux-gnu] - - steps: - - name: Checkout Noir repo - uses: actions/checkout@v4 - - - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.71.1 - - - uses: Swatinem/rust-cache@v2 - with: - key: ${{ matrix.target }} - cache-on-failure: true - save-if: ${{ github.event_name != 'merge_group' }} - - - name: Build Nargo - run: cargo build --package nargo_cli --release - - - name: Package artifacts - run: | - mkdir dist - cp ./target/release/nargo ./dist/nargo - 7z a -ttar -so -an ./dist/* | 7z a -si ./nargo-x86_64-unknown-linux-gnu.tar.gz - - - name: Upload artifact - uses: actions/upload-artifact@v3 - with: - name: nargo - path: ./dist/* - retention-days: 3 - - build-acvm-js: - runs-on: ubuntu-latest - steps: - - name: Checkout sources - uses: actions/checkout@v4 - - - name: Setup Nix - uses: ./.github/actions/nix - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - nix-cache-name: "noir" - cachix-auth-token: ${{ secrets.CACHIXAUTHTOKEN }} - - - name: Build acvm-js - run: | - nix build -L .#acvm_js - - - name: Dereference symlink - run: echo "UPLOAD_PATH=$(readlink -f result/acvm_js)" >> $GITHUB_ENV - - - name: Upload artifact - uses: actions/upload-artifact@v3 - with: - name: acvm-js - path: ${{ env.UPLOAD_PATH }} - retention-days: 3 - - build-wasm: - runs-on: ubuntu-latest - - steps: - - name: Checkout sources - uses: actions/checkout@v4 - - - name: Setup Nix - uses: ./.github/actions/nix - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - nix-cache-name: "noir" - cachix-auth-token: ${{ secrets.CACHIXAUTHTOKEN }} - - - name: Build wasm package - run: | - nix build -L .#noir_wasm - - - name: Dereference symlink - run: echo "UPLOAD_PATH=$(readlink -f ./result/noir_wasm)" >> $GITHUB_ENV - - - name: Upload artifact - uses: actions/upload-artifact@v3 - with: - name: noir_wasm - path: ${{ env.UPLOAD_PATH }} - retention-days: 3 - - build-noirc: - runs-on: ubuntu-latest - - steps: - - name: Checkout sources - uses: actions/checkout@v3 - - - name: Setup Nix - uses: ./.github/actions/nix - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - nix-cache-name: "noir" - cachix-auth-token: ${{ secrets.CACHIXAUTHTOKEN }} - - - name: Build noirc_abi_wasm - run: | - nix build -L .#noirc_abi_wasm - cp -r ./result/noirc_abi_wasm/nodejs ./tooling/noirc_abi_wasm - cp -r ./result/noirc_abi_wasm/web ./tooling/noirc_abi_wasm - - - name: Dereference symlink - run: echo "UPLOAD_PATH=$(readlink -f ./result/noirc_abi_wasm)" >> $GITHUB_ENV - - - name: Upload artifact - uses: actions/upload-artifact@v3 - with: - name: noirc_abi_wasm - path: ${{ env.UPLOAD_PATH }} - retention-days: 10 - - test-solidity-verifier: - runs-on: ubuntu-latest - needs: [build-acvm-js, build-wasm, build-nargo, build-noirc] - env: - CACHED_PATH: /tmp/nix-cache - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Download nargo binary - uses: actions/download-artifact@v3 - with: - name: nargo - path: ./nargo - - - name: Download acvm_js package artifact - uses: actions/download-artifact@v3 - with: - name: acvm-js - path: ./acvm-repo/acvm_js - - - name: Download noir_wasm package artifact - uses: actions/download-artifact@v3 - with: - name: noir_wasm - path: ./compiler/wasm - - - name: Download noirc_abi package artifact - uses: actions/download-artifact@v3 - with: - name: noirc_abi_wasm - path: ./tooling/noirc_abi_wasm - - - name: Set nargo on PATH - run: | - nargo_binary="${{ github.workspace }}/nargo/nargo" - chmod +x $nargo_binary - echo "$(dirname $nargo_binary)" >> $GITHUB_PATH - export PATH="$PATH:$(dirname $nargo_binary)" - nargo -V - - - name: Install Yarn dependencies - uses: ./.github/actions/setup - - - name: Install Playwright - uses: ./.github/actions/install-playwright - - - name: Setup `integration-tests` - run: | - yarn workspace @noir-lang/source-resolver build - yarn workspace @noir-lang/types build - yarn workspace @noir-lang/backend_barretenberg build - yarn workspace @noir-lang/noir_js build - - - name: Run `integration-tests` - run: | - yarn test:integration - - - name: Alert on nightly test failure - uses: JasonEtco/create-an-issue@v2 - if: ${{ failure() && github.event_name == 'schedule' }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - WORKFLOW_NAME: ${{ github.workflow }} - WORKFLOW_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - with: - update_existing: true - filename: .github/NIGHTLY_TEST_FAILURE.md diff --git a/.github/workflows/test-js-packages.yml b/.github/workflows/test-js-packages.yml new file mode 100644 index 00000000000..9ac61f34203 --- /dev/null +++ b/.github/workflows/test-js-packages.yml @@ -0,0 +1,439 @@ +name: Javascript Tests + +on: + pull_request: + merge_group: + push: + branches: + - master + +# This will cancel previous runs when a branch or PR is updated +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.ref || github.run_id }} + cancel-in-progress: true + +jobs: + build-nargo: + runs-on: ubuntu-22.04 + + steps: + - name: Checkout Noir repo + uses: actions/checkout@v4 + + - name: Setup toolchain + uses: dtolnay/rust-toolchain@1.71.1 + + - uses: Swatinem/rust-cache@v2 + with: + key: x86_64-unknown-linux-gnu + cache-on-failure: true + save-if: ${{ github.event_name != 'merge_group' }} + + - name: Build Nargo + run: cargo build --package nargo_cli --release + + - name: Package artifacts + run: | + mkdir dist + cp ./target/release/nargo ./dist/nargo + 7z a -ttar -so -an ./dist/* | 7z a -si ./nargo-x86_64-unknown-linux-gnu.tar.gz + + - name: Upload artifact + uses: actions/upload-artifact@v3 + with: + name: nargo + path: ./dist/* + retention-days: 3 + + build-noir-wasm: + runs-on: ubuntu-latest + + steps: + - name: Checkout sources + uses: actions/checkout@v4 + + - name: Setup Nix + uses: ./.github/actions/nix + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + nix-cache-name: "noir" + cachix-auth-token: ${{ secrets.CACHIXAUTHTOKEN }} + + - name: Build wasm package + run: | + nix build -L .#noir_wasm + + - name: Dereference symlink + run: echo "UPLOAD_PATH=$(readlink -f ./result/noir_wasm)" >> $GITHUB_ENV + + - name: Upload artifact + uses: actions/upload-artifact@v3 + with: + name: noir_wasm + path: ${{ env.UPLOAD_PATH }} + retention-days: 3 + + + build-acvm-js: + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v4 + + - name: Setup Nix + uses: ./.github/actions/nix + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + nix-cache-name: "noir" + cachix-auth-token: ${{ secrets.CACHIXAUTHTOKEN }} + + - name: Build acvm-js + run: | + nix build -L .#acvm_js + + - name: Dereference symlink + run: echo "UPLOAD_PATH=$(readlink -f result/acvm_js)" >> $GITHUB_ENV + + - name: Upload artifact + uses: actions/upload-artifact@v3 + with: + name: acvm-js + path: ${{ env.UPLOAD_PATH }} + retention-days: 3 + + build-noirc-abi: + runs-on: ubuntu-latest + + steps: + - name: Checkout sources + uses: actions/checkout@v3 + + - name: Setup Nix + uses: ./.github/actions/nix + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + nix-cache-name: "noir" + cachix-auth-token: ${{ secrets.CACHIXAUTHTOKEN }} + + - name: Build noirc_abi_wasm + run: | + nix build -L .#noirc_abi_wasm + + - name: Dereference symlink + run: echo "UPLOAD_PATH=$(readlink -f ./result/noirc_abi_wasm)" >> $GITHUB_ENV + + - name: Upload artifact + uses: actions/upload-artifact@v3 + with: + name: noirc_abi_wasm + path: ${{ env.UPLOAD_PATH }} + retention-days: 10 + + test-acvm_js-node: + needs: [build-acvm-js] + name: ACVM JS (Node.js) + runs-on: ubuntu-latest + + steps: + - name: Checkout sources + uses: actions/checkout@v4 + + - name: Download artifact + uses: actions/download-artifact@v3 + with: + name: acvm-js + path: ./acvm-repo/acvm_js + + - name: Set up test environment + uses: ./.github/actions/setup + + - name: Run node tests + run: yarn workspace @noir-lang/acvm_js test + + test-acvm_js-browser: + needs: [build-acvm-js] + name: ACVM JS (Browser) + runs-on: ubuntu-latest + + steps: + - name: Checkout sources + uses: actions/checkout@v4 + + - name: Download artifact + uses: actions/download-artifact@v3 + with: + name: acvm-js + path: ./acvm-repo/acvm_js + + - name: Set up test environment + uses: ./.github/actions/setup + + - name: Install playwright deps + run: | + npx playwright install + npx playwright install-deps + + - name: Run browser tests + run: yarn workspace @noir-lang/acvm_js test:browser + + test-noirc-abi: + needs: [build-noirc-abi] + name: noirc_abi + runs-on: ubuntu-latest + + steps: + - name: Checkout sources + uses: actions/checkout@v3 + + - name: Download wasm package artifact + uses: actions/download-artifact@v3 + with: + name: noirc_abi_wasm + path: ./tooling/noirc_abi_wasm + + - name: Install Yarn dependencies + uses: ./.github/actions/setup + + - name: Run node tests + run: yarn workspace @noir-lang/noirc_abi test + + - name: Install Playwright + uses: ./.github/actions/install-playwright + + - name: Run browser tests + run: yarn workspace @noir-lang/noirc_abi test:browser + + test-noir-js-backend-barretenberg: + needs: [build-noirc-abi] + name: noir-js-backend-barretenberg + runs-on: ubuntu-latest + timeout-minutes: 30 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Download wasm package artifact + uses: actions/download-artifact@v3 + with: + name: noirc_abi_wasm + path: ./tooling/noirc_abi_wasm + + - name: Install Yarn dependencies + uses: ./.github/actions/setup + + - name: Build noir_js_types + run: yarn workspace @noir-lang/types build + + - name: Run barretenberg wrapper tests + run: | + yarn workspace @noir-lang/backend_barretenberg test + + test-noir-js: + needs: [build-acvm-js, build-noirc-abi] + name: Noir JS + runs-on: ubuntu-latest + timeout-minutes: 30 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Download artifact + uses: actions/download-artifact@v3 + with: + name: acvm-js + path: ./acvm-repo/acvm_js + + - name: Download wasm package artifact + uses: actions/download-artifact@v3 + with: + name: noirc_abi_wasm + path: ./tooling/noirc_abi_wasm + + - name: Install Yarn dependencies + uses: ./.github/actions/setup + + - name: Build noir_js_types + run: yarn workspace @noir-lang/types build + + - name: Build barretenberg wrapper + run: yarn workspace @noir-lang/backend_barretenberg build + + - name: Run noir_js tests + run: | + yarn workspace @noir-lang/noir_js build + yarn workspace @noir-lang/noir_js test + + test-source-resolver: + name: source-resolver + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Install Yarn dependencies + uses: ./.github/actions/setup + + - name: Build @noir-lang/source-resolver + run: yarn workspace @noir-lang/source-resolver build + + - name: Run tests + run: yarn workspace @noir-lang/source-resolver test + + test-noir-wasm: + needs: [build-noir-wasm, build-nargo] + name: noir_wasm + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v4 + + - name: Download wasm package artifact + uses: actions/download-artifact@v3 + with: + name: noir_wasm + path: ./compiler/wasm + + - name: Download nargo binary + uses: actions/download-artifact@v3 + with: + name: nargo + path: ./nargo + + - name: Compile fixtures with Nargo CLI + working-directory: ./compiler/wasm/fixtures + run: | + nargo_binary=${{ github.workspace }}/nargo/nargo + chmod +x $nargo_binary + for dir in $(ls -d */); do + pushd $dir/noir-script + $nargo_binary compile + popd + done + + - name: Install Yarn dependencies + uses: ./.github/actions/setup + + - name: Install Playwright + uses: ./.github/actions/install-playwright + + - name: Install dependencies + run: | + yarn workspace @noir-lang/source-resolver build + + - name: Run node tests + run: yarn workspace @noir-lang/noir_wasm test:node + + - name: Run browser tests + run: yarn workspace @noir-lang/noir_wasm test:browser + + test-noir-codegen: + needs: [build-acvm-js, build-noirc-abi] + name: noir_codegen + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Download acvm_js package artifact + uses: actions/download-artifact@v3 + with: + name: acvm-js + path: ./acvm-repo/acvm_js + + - name: Download noirc_abi package artifact + uses: actions/download-artifact@v3 + with: + name: noirc_abi_wasm + path: ./tooling/noirc_abi_wasm + + - name: Install Yarn dependencies + uses: ./.github/actions/setup + + - name: Build noir_js_types + run: yarn workspace @noir-lang/types build + + - name: Build noir_js + run: yarn workspace @noir-lang/noir_js build + + - name: Run noir_codegen tests + run: yarn workspace @noir-lang/noir_codegen test + + test-integration: + name: Integration Tests + runs-on: ubuntu-latest + needs: [build-acvm-js, build-noir-wasm, build-nargo, build-noirc-abi] + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Download nargo binary + uses: actions/download-artifact@v3 + with: + name: nargo + path: ./nargo + + - name: Download acvm_js package artifact + uses: actions/download-artifact@v3 + with: + name: acvm-js + path: ./acvm-repo/acvm_js + + - name: Download noir_wasm package artifact + uses: actions/download-artifact@v3 + with: + name: noir_wasm + path: ./compiler/wasm + + - name: Download noirc_abi package artifact + uses: actions/download-artifact@v3 + with: + name: noirc_abi_wasm + path: ./tooling/noirc_abi_wasm + + - name: Set nargo on PATH + run: | + nargo_binary="${{ github.workspace }}/nargo/nargo" + chmod +x $nargo_binary + echo "$(dirname $nargo_binary)" >> $GITHUB_PATH + export PATH="$PATH:$(dirname $nargo_binary)" + nargo -V + + - name: Install Yarn dependencies + uses: ./.github/actions/setup + + - name: Install Playwright + uses: ./.github/actions/install-playwright + + - name: Setup `integration-tests` + run: | + # Note the lack of spaces between package names. + PACKAGES_TO_BUILD="@noir-lang/source-resolver,@noir-lang/types,@noir-lang/backend_barretenberg,@noir-lang/noir_js" + yarn workspaces foreach -vp --from "{$PACKAGES_TO_BUILD}" run build + + - name: Run `integration-tests` + run: | + yarn test:integration + + # This is a noop job which depends on all test jobs + # This allows us to add/remove test jobs without having to update the required workflows. + tests-end: + name: End + runs-on: ubuntu-latest + needs: + - test-acvm_js-node + - test-acvm_js-browser + - test-noirc-abi + - test-noir-js-backend-barretenberg + - test-noir-js + - test-source-resolver + - test-noir-wasm + - test-noir-codegen + - test-integration + + steps: + - name: Noop + run: echo "noop" diff --git a/.github/workflows/test-noir-js.yml b/.github/workflows/test-noir-js.yml deleted file mode 100644 index e74f2ee6015..00000000000 --- a/.github/workflows/test-noir-js.yml +++ /dev/null @@ -1,61 +0,0 @@ -name: Test Noir Js - -on: - pull_request: - merge_group: - push: - branches: - - master - -jobs: - test-noir-js: - name: Test Noir JS - runs-on: ubuntu-latest - timeout-minutes: 30 - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Install Yarn dependencies - uses: ./.github/actions/setup - - - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.71.1 - with: - targets: wasm32-unknown-unknown - - - uses: Swatinem/rust-cache@v2 - with: - key: wasm32-unknown-unknown-noir-js - cache-on-failure: true - save-if: ${{ github.event_name != 'merge_group' }} - - - name: Install jq - run: sudo apt-get install jq - - - name: Install wasm-bindgen-cli - uses: taiki-e/install-action@v2 - with: - tool: wasm-bindgen-cli@0.2.86 - - - name: Install wasm-opt - run: | - npm i wasm-opt -g - - - name: Build acvm_js - run: yarn workspace @noir-lang/acvm_js build - - - name: Build noirc_abi - run: yarn workspace @noir-lang/noirc_abi build - - - name: Build noir_js_types - run: yarn workspace @noir-lang/types build - - - name: Build barretenberg wrapper - run: yarn workspace @noir-lang/backend_barretenberg build - - - name: Run noir_js tests - run: | - yarn workspace @noir-lang/noir_js build - yarn workspace @noir-lang/noir_js test diff --git a/.github/workflows/test-noir_wasm.yml b/.github/workflows/test-noir_wasm.yml deleted file mode 100644 index 2175e20acd7..00000000000 --- a/.github/workflows/test-noir_wasm.yml +++ /dev/null @@ -1,125 +0,0 @@ -name: Wasm - -on: - pull_request: - merge_group: - push: - branches: - - master - -concurrency: - group: ${{ github.workflow }}-${{ github.head_ref || github.ref || github.run_id }} - cancel-in-progress: true - -jobs: - build-nargo: - runs-on: ubuntu-22.04 - strategy: - matrix: - target: [x86_64-unknown-linux-gnu] - - steps: - - name: Checkout Noir repo - uses: actions/checkout@v4 - - - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.71.1 - - - uses: Swatinem/rust-cache@v2 - with: - key: ${{ matrix.target }} - cache-on-failure: true - save-if: ${{ github.event_name != 'merge_group' }} - - - name: Build Nargo - run: cargo build --package nargo_cli --release - - - name: Package artifacts - run: | - mkdir dist - cp ./target/release/nargo ./dist/nargo - 7z a -ttar -so -an ./dist/* | 7z a -si ./nargo-x86_64-unknown-linux-gnu.tar.gz - - - name: Upload artifact - uses: actions/upload-artifact@v3 - with: - name: nargo - path: ./dist/* - retention-days: 3 - - build-wasm: - runs-on: ubuntu-latest - - steps: - - name: Checkout sources - uses: actions/checkout@v4 - - - name: Setup Nix - uses: ./.github/actions/nix - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - nix-cache-name: "noir" - cachix-auth-token: ${{ secrets.CACHIXAUTHTOKEN }} - - - name: Build wasm package - run: | - nix build -L .#noir_wasm - - - name: Dereference symlink - run: echo "UPLOAD_PATH=$(readlink -f ./result/noir_wasm)" >> $GITHUB_ENV - - - name: Upload artifact - uses: actions/upload-artifact@v3 - with: - name: noir_wasm - path: ${{ env.UPLOAD_PATH }} - retention-days: 3 - - test: - needs: [build-wasm, build-nargo] - name: Test noir_wasm - runs-on: ubuntu-latest - steps: - - name: Checkout noir-lang/noir - uses: actions/checkout@v4 - - - name: Download wasm package artifact - uses: actions/download-artifact@v3 - with: - name: noir_wasm - path: ./compiler/wasm/downloaded - - - name: Download nargo binary - uses: actions/download-artifact@v3 - with: - name: nargo - path: ./nargo - - - name: Compile fixtures with Nargo CLI - working-directory: ./compiler/wasm/fixtures - run: | - nargo_binary=${{ github.workspace }}/nargo/nargo - chmod +x $nargo_binary - for dir in $(ls -d */); do - pushd $dir/noir-script - $nargo_binary compile - popd - done - - - name: Install Yarn dependencies - uses: ./.github/actions/setup - - - name: Install Playwright - uses: ./.github/actions/install-playwright - - - name: Install dependencies - run: | - cp -r ./compiler/wasm/downloaded/nodejs ./compiler/wasm - cp -r ./compiler/wasm/downloaded/web ./compiler/wasm - yarn workspace @noir-lang/source-resolver build - - - name: Run node tests - run: yarn workspace @noir-lang/noir_wasm test:node - - - name: Run browser tests - run: yarn workspace @noir-lang/noir_wasm test:browser diff --git a/.github/workflows/test-source-resolver.yml b/.github/workflows/test-source-resolver.yml deleted file mode 100644 index 5dafe4c5fd9..00000000000 --- a/.github/workflows/test-source-resolver.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: Test Source Resolver - -on: - push: - paths: - - "compiler/source-resolver/**" - pull_request: - paths: - - "compiler/source-resolver/**" - -jobs: - test: - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Install Yarn dependencies - uses: ./.github/actions/setup - - - name: Build @noir-lang/source-resolver - run: yarn workspace @noir-lang/source-resolver build - - - name: Run tests - run: yarn workspace @noir-lang/source-resolver test diff --git a/.gitignore b/.gitignore index 169353af2b6..11f0ae3b975 100644 --- a/.gitignore +++ b/.gitignore @@ -36,9 +36,10 @@ result *.pk *.vk **/Verifier.toml +**/contract **/target -!tooling/nargo_cli/tests/acir_artifacts/*/target -!tooling/nargo_cli/tests/acir_artifacts/*/target/witness.gz +!test_programs/acir_artifacts/*/target +!test_programs/acir_artifacts/*/target/witness.gz !compiler/wasm/noir-script/target gates_report.json @@ -53,3 +54,9 @@ compiler/wasm/web tooling/noirc_abi_wasm/nodejs tooling/noirc_abi_wasm/web tooling/noir_js/lib + +**/package.tgz +packages + +# docs autogen build +/docs/docs/noir_js/reference/ diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 63162406f74..8a6f95c33e0 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { - ".": "0.19.0", - "acvm-repo": "0.31.0" + ".": "0.19.4", + "acvm-repo": "0.35.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 3738834eef8..077c9973806 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,72 @@ # Changelog +## [0.19.4](https://github.com/noir-lang/noir/compare/v0.19.3...v0.19.4) (2023-11-28) + + +### Features + +* Add --check option to nargo fmt for dry-run formatting verification ([#3530](https://github.com/noir-lang/noir/issues/3530)) ([4469707](https://github.com/noir-lang/noir/commit/4469707d97085fab0f7ade8d015dc827c56156ee)) +* Add support for tuple values in `noir_codegen` ([#3592](https://github.com/noir-lang/noir/issues/3592)) ([346d75f](https://github.com/noir-lang/noir/commit/346d75f9dd9261996d4d7bb80eb7e4118e8f8ce2)) +* Codegen typed interfaces for functions in `noir_codegen` ([#3533](https://github.com/noir-lang/noir/issues/3533)) ([290c463](https://github.com/noir-lang/noir/commit/290c463622a93a34293f73b5bf2aea7ade30a11c)) +* Export `CompiledCircuit` from codegened TS ([#3589](https://github.com/noir-lang/noir/issues/3589)) ([e06c675](https://github.com/noir-lang/noir/commit/e06c67500da11518caffe0e98bdb9cd7f5f89049)) +* Remove type arrays for flat slices ([#3466](https://github.com/noir-lang/noir/issues/3466)) ([8225b2b](https://github.com/noir-lang/noir/commit/8225b2b379ddf145f9418f8517478704f9aac350)) +* Send and receive unflattened public inputs to backend ([#3543](https://github.com/noir-lang/noir/issues/3543)) ([a7bdc67](https://github.com/noir-lang/noir/commit/a7bdc67ef3ec2037bffc4f1f472907cad786c319)) + + +### Bug Fixes + +* Compiler version error message ([#3558](https://github.com/noir-lang/noir/issues/3558)) ([026a358](https://github.com/noir-lang/noir/commit/026a3587b01ddc8f444ff588a7b3f3fd1a0bb386)) +* Remove quotes from println output ([#3574](https://github.com/noir-lang/noir/issues/3574)) ([127b6aa](https://github.com/noir-lang/noir/commit/127b6aa1ec8893275fdfa7795db7c52c4fc1d4dd)) +* Somewhat reduce mem2reg memory usage ([#3572](https://github.com/noir-lang/noir/issues/3572)) ([9b9ed89](https://github.com/noir-lang/noir/commit/9b9ed890e68b6c7f0671b05919bdc86f593c5df5)) +* Use 128 bits for constant bit shift ([#3586](https://github.com/noir-lang/noir/issues/3586)) ([2ca9b05](https://github.com/noir-lang/noir/commit/2ca9b059317f0513ea21153ebdb468c4f6633de5)) + +## [0.19.3](https://github.com/noir-lang/noir/compare/v0.19.2...v0.19.3) (2023-11-22) + + +### Features + +* Add debugger commands to introspect (and modify) the current state ([#3391](https://github.com/noir-lang/noir/issues/3391)) ([9e1ad85](https://github.com/noir-lang/noir/commit/9e1ad858cf8a1d9aba0137abe6a749267498bfaf)) +* Add LSP command to profile opcodes in vscode ([#3496](https://github.com/noir-lang/noir/issues/3496)) ([6fbf77a](https://github.com/noir-lang/noir/commit/6fbf77ae2b87a55db92344f5066a82ccaf6c2086)) +* Add lsp formatting ([#3433](https://github.com/noir-lang/noir/issues/3433)) ([286c876](https://github.com/noir-lang/noir/commit/286c87694fda185f25b05cec5504142643bc207f)) +* Allow providing custom foreign call executors to `execute_circuit` ([#3506](https://github.com/noir-lang/noir/issues/3506)) ([d27db33](https://github.com/noir-lang/noir/commit/d27db332f8c320ffd9b5520bebbd83ae09e31de7)) +* Compile without a backend ([#3437](https://github.com/noir-lang/noir/issues/3437)) ([d69cf5d](https://github.com/noir-lang/noir/commit/d69cf5debcc430bb019b6cc95774aac084776dda)) +* Enable the `fmt` command in the help menu ([#3328](https://github.com/noir-lang/noir/issues/3328)) ([63d414c](https://github.com/noir-lang/noir/commit/63d414c06a399525601e3db11dc48b180e93c2d8)) +* Handle constant index operations on simple slices ([#3464](https://github.com/noir-lang/noir/issues/3464)) ([7ae12f8](https://github.com/noir-lang/noir/commit/7ae12f8c5243d31b2f410c246ed6b9e2fcea5d4c)) + + +### Bug Fixes + +* "Missing trait impl" error in trait dispatch ([#3440](https://github.com/noir-lang/noir/issues/3440)) ([52daaec](https://github.com/noir-lang/noir/commit/52daaec504101fe3c0caa30441c17f30a34af475)) +* Adding proving key initialization ([#3322](https://github.com/noir-lang/noir/issues/3322)) ([3383740](https://github.com/noir-lang/noir/commit/3383740f9a0004f2ee77c9686f81baed6cd1917c)) +* Allow `where` clause on all functions and improve error message ([#3465](https://github.com/noir-lang/noir/issues/3465)) ([1647e33](https://github.com/noir-lang/noir/commit/1647e33564bf56ab8721a365f5fc6bcb38901412)) +* Apply predicate to over/underflow checks ([#3494](https://github.com/noir-lang/noir/issues/3494)) ([fc3edf7](https://github.com/noir-lang/noir/commit/fc3edf7aa5da9074614fa900bbcb57e512e3d56b)) +* **debugger:** Step through foreign calls and breakpoints inside Brillig blocks ([#3511](https://github.com/noir-lang/noir/issues/3511)) ([5d77d7a](https://github.com/noir-lang/noir/commit/5d77d7ac82a4df6995ca151b2c8070044cb1fe9d)) +* Fix crash when using undeclared traits ([#3509](https://github.com/noir-lang/noir/issues/3509)) ([8bb095a](https://github.com/noir-lang/noir/commit/8bb095af77d3b4043855841f1ae5799d75ed94f0)) +* Match rust behaviour for left-shift overflow ([#3518](https://github.com/noir-lang/noir/issues/3518)) ([2d7ceb1](https://github.com/noir-lang/noir/commit/2d7ceb17edda1d9e70901cfd13f45cdc0df0d28d)) +* Verify impls arising from function calls exist ([#3472](https://github.com/noir-lang/noir/issues/3472)) ([d7f919d](https://github.com/noir-lang/noir/commit/d7f919dcc001080ed24616ebbc37426ef7ac7638)) + +## [0.19.2](https://github.com/noir-lang/noir/compare/v0.19.1...v0.19.2) (2023-11-07) + + +### Features + +* Add profile info print out ([#3425](https://github.com/noir-lang/noir/issues/3425)) ([a8b5fa8](https://github.com/noir-lang/noir/commit/a8b5fa8e30dc27e64666381b7451569f350967d1)) + +## [0.19.1](https://github.com/noir-lang/noir/compare/v0.19.0...v0.19.1) (2023-11-07) + + +### Features + +* **acir:** Handle dynamic array operations for nested slices ([#3187](https://github.com/noir-lang/noir/issues/3187)) ([e026319](https://github.com/noir-lang/noir/commit/e026319fc25763d30781b90e6a4454ddb5d3bc7b)) +* Properly track equivalence of witnesses generated for black box functions ([#3428](https://github.com/noir-lang/noir/issues/3428)) ([20b70c2](https://github.com/noir-lang/noir/commit/20b70c29b4bd323d67ef56ab1933341a7747d3cb)) +* Use ranges instead of a vector for input witness ([#3314](https://github.com/noir-lang/noir/issues/3314)) ([b12b7ec](https://github.com/noir-lang/noir/commit/b12b7ecb995988c731be5f1f2f67fda952f1a228)) + + +### Bug Fixes + +* Follow dependencies when looking for a struct ([#3405](https://github.com/noir-lang/noir/issues/3405)) ([561b1b8](https://github.com/noir-lang/noir/commit/561b1b8f0b22d8b1800cb3552942a442a27c2a2c)) +* Overflow checks for constant folding ([#3420](https://github.com/noir-lang/noir/issues/3420)) ([b7a6383](https://github.com/noir-lang/noir/commit/b7a6383cf9dc3bc4a71b9644352340c1e9339c81)) + ## [0.19.0](https://github.com/noir-lang/noir/compare/v0.18.0...v0.19.0) (2023-11-02) diff --git a/Cargo.lock b/Cargo.lock index ad2f24ac2f2..43958d2f1cb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,7 +4,7 @@ version = 3 [[package]] name = "acir" -version = "0.31.0" +version = "0.35.0" dependencies = [ "acir_field", "base64", @@ -23,7 +23,7 @@ dependencies = [ [[package]] name = "acir_field" -version = "0.31.0" +version = "0.35.0" dependencies = [ "ark-bls12-381", "ark-bn254", @@ -37,7 +37,7 @@ dependencies = [ [[package]] name = "acvm" -version = "0.31.0" +version = "0.35.0" dependencies = [ "acir", "acvm_blackbox_solver", @@ -54,7 +54,7 @@ dependencies = [ [[package]] name = "acvm_blackbox_solver" -version = "0.31.0" +version = "0.35.0" dependencies = [ "acir", "blake2", @@ -67,7 +67,7 @@ dependencies = [ [[package]] name = "acvm_js" -version = "0.31.0" +version = "0.35.0" dependencies = [ "acvm", "barretenberg_blackbox_solver", @@ -88,7 +88,7 @@ dependencies = [ [[package]] name = "acvm_stdlib" -version = "0.31.0" +version = "0.35.0" dependencies = [ "acir", ] @@ -217,7 +217,7 @@ checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" [[package]] name = "arena" -version = "0.19.0" +version = "0.19.4" dependencies = [ "generational-arena", ] @@ -389,12 +389,12 @@ dependencies = [ [[package]] name = "async-lsp" -version = "0.0.5" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72bd600f2652d2cccb0a33ab4f92d163ab3930844c8b0ad3713a5ae3285eeb4e" +checksum = "138985dd8aefbefeaa66b01b7f5b2b6b4c333fcef1cc5f32c63a2aabe37d6de3" dependencies = [ "futures", - "lsp-types 0.94.0", + "lsp-types 0.94.1", "pin-project-lite", "rustix", "serde", @@ -413,6 +413,14 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "aztec_macros" +version = "0.19.4" +dependencies = [ + "iter-extended", + "noirc_frontend", +] + [[package]] name = "backend-interface" version = "0.11.0" @@ -449,7 +457,7 @@ dependencies = [ [[package]] name = "barretenberg_blackbox_solver" -version = "0.31.0" +version = "0.35.0" dependencies = [ "acir", "acvm_blackbox_solver", @@ -573,7 +581,7 @@ dependencies = [ [[package]] name = "brillig" -version = "0.31.0" +version = "0.35.0" dependencies = [ "acir_field", "serde", @@ -581,7 +589,7 @@ dependencies = [ [[package]] name = "brillig_vm" -version = "0.31.0" +version = "0.35.0" dependencies = [ "acir", "acvm_blackbox_solver", @@ -1596,7 +1604,7 @@ dependencies = [ [[package]] name = "fm" -version = "0.19.0" +version = "0.19.4" dependencies = [ "codespan-reporting", "iter-extended", @@ -2158,7 +2166,7 @@ dependencies = [ [[package]] name = "iter-extended" -version = "0.19.0" +version = "0.19.4" [[package]] name = "itertools" @@ -2275,9 +2283,9 @@ dependencies = [ [[package]] name = "lsp-types" -version = "0.94.0" +version = "0.94.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b63735a13a1f9cd4f4835223d828ed9c2e35c8c5e61837774399f558b6a1237" +checksum = "c66bfd44a06ae10647fe3f8214762e9369fd4248df1350924b4ef9e770a85ea1" dependencies = [ "bitflags 1.3.2", "serde", @@ -2380,7 +2388,7 @@ checksum = "7843ec2de400bcbc6a6328c958dc38e5359da6e93e72e37bc5246bf1ae776389" [[package]] name = "nargo" -version = "0.19.0" +version = "0.19.4" dependencies = [ "acvm", "codespan-reporting", @@ -2392,6 +2400,7 @@ dependencies = [ "noirc_evaluator", "noirc_frontend", "noirc_printable_type", + "rayon", "rustc_version", "serde", "thiserror", @@ -2399,7 +2408,7 @@ dependencies = [ [[package]] name = "nargo_cli" -version = "0.19.0" +version = "0.19.4" dependencies = [ "acvm", "assert_cmd", @@ -2435,6 +2444,7 @@ dependencies = [ "rustc_version", "serde", "serde_json", + "similar-asserts", "tempfile", "termcolor", "test-binary", @@ -2447,7 +2457,7 @@ dependencies = [ [[package]] name = "nargo_fmt" -version = "0.19.0" +version = "0.19.4" dependencies = [ "bytecount", "noirc_frontend", @@ -2459,7 +2469,7 @@ dependencies = [ [[package]] name = "nargo_toml" -version = "0.19.0" +version = "0.19.4" dependencies = [ "dirs", "fm", @@ -2508,7 +2518,7 @@ dependencies = [ [[package]] name = "noir_debugger" -version = "0.19.0" +version = "0.19.4" dependencies = [ "acvm", "codespan-reporting", @@ -2522,21 +2532,24 @@ dependencies = [ [[package]] name = "noir_lsp" -version = "0.19.0" +version = "0.19.4" dependencies = [ "acvm", "async-lsp", "cfg-if", "codespan-lsp", "codespan-reporting", - "lsp-types 0.94.0", + "fm", + "lsp-types 0.94.1", "nargo", + "nargo_fmt", "nargo_toml", "noirc_driver", "noirc_errors", "noirc_frontend", "serde", "serde_json", + "serde_with", "tokio", "tower", "wasm-bindgen", @@ -2544,7 +2557,7 @@ dependencies = [ [[package]] name = "noir_wasm" -version = "0.19.0" +version = "0.19.4" dependencies = [ "acvm", "build-data", @@ -2566,7 +2579,7 @@ dependencies = [ [[package]] name = "noirc_abi" -version = "0.19.0" +version = "0.19.4" dependencies = [ "acvm", "iter-extended", @@ -2583,7 +2596,7 @@ dependencies = [ [[package]] name = "noirc_abi_wasm" -version = "0.19.0" +version = "0.19.4" dependencies = [ "acvm", "build-data", @@ -2600,9 +2613,10 @@ dependencies = [ [[package]] name = "noirc_driver" -version = "0.19.0" +version = "0.19.4" dependencies = [ "acvm", + "aztec_macros", "build-data", "clap", "fm", @@ -2617,7 +2631,7 @@ dependencies = [ [[package]] name = "noirc_errors" -version = "0.19.0" +version = "0.19.4" dependencies = [ "acvm", "chumsky", @@ -2630,13 +2644,12 @@ dependencies = [ [[package]] name = "noirc_evaluator" -version = "0.19.0" +version = "0.19.4" dependencies = [ "acvm", "fxhash", "im", "iter-extended", - "noirc_abi", "noirc_errors", "noirc_frontend", "num-bigint", @@ -2646,7 +2659,7 @@ dependencies = [ [[package]] name = "noirc_frontend" -version = "0.19.0" +version = "0.19.4" dependencies = [ "acvm", "arena", @@ -2668,7 +2681,7 @@ dependencies = [ [[package]] name = "noirc_printable_type" -version = "0.19.0" +version = "0.19.4" dependencies = [ "acvm", "iter-extended", @@ -3644,9 +3657,9 @@ dependencies = [ [[package]] name = "self_cell" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c309e515543e67811222dbc9e3dd7e1056279b782e1dacffe4242b718734fb6" +checksum = "e388332cd64eb80cd595a00941baf513caffae8dce9cfd0467fc9c66397dade6" [[package]] name = "semver" diff --git a/Cargo.toml b/Cargo.toml index 66aba6d8457..5738fe94984 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [workspace] members = [ + "aztec_macros", "compiler/noirc_evaluator", "compiler/noirc_frontend", "compiler/noirc_errors", @@ -38,7 +39,7 @@ resolver = "2" [workspace.package] # x-release-please-start-version -version = "0.19.0" +version = "0.19.4" # x-release-please-end authors = ["The Noir Team "] edition = "2021" @@ -49,14 +50,14 @@ repository = "https://github.com/noir-lang/noir/" [workspace.dependencies] # ACVM workspace dependencies -acir = { path = "acvm-repo/acir", default-features = false } -acvm = { path = "acvm-repo/acvm" } -acir_field = { path = "acvm-repo/acir_field", default-features = false } -stdlib = { package = "acvm_stdlib", path = "acvm-repo/stdlib", default-features = false } -brillig = { path = "acvm-repo/brillig", default-features = false } -brillig_vm = { path = "acvm-repo/brillig_vm", default-features = false } -acvm_blackbox_solver = { path = "acvm-repo/blackbox_solver", default-features = false } -barretenberg_blackbox_solver = { path = "acvm-repo/barretenberg_blackbox_solver", default-features = false } +acir_field = { version = "0.35.0", path = "acvm-repo/acir_field", default-features = false } +acir = { version = "0.35.0", path = "acvm-repo/acir", default-features = false } +acvm = { version = "0.35.0", path = "acvm-repo/acvm" } +stdlib = { version = "0.35.0", package = "acvm_stdlib", path = "acvm-repo/stdlib", default-features = false } +brillig = { version = "0.35.0", path = "acvm-repo/brillig", default-features = false } +brillig_vm = { version = "0.35.0", path = "acvm-repo/brillig_vm", default-features = false } +acvm_blackbox_solver = { version = "0.35.0", path = "acvm-repo/blackbox_solver", default-features = false } +barretenberg_blackbox_solver = { version = "0.35.0", path = "acvm-repo/barretenberg_blackbox_solver", default-features = false } # Noir compiler workspace dependencies arena = { path = "compiler/utils/arena" } @@ -80,8 +81,8 @@ noirc_abi = { path = "tooling/noirc_abi" } bb_abstraction_leaks = { path = "tooling/bb_abstraction_leaks" } # LSP -async-lsp = { version = "0.0.5", default-features = false } -lsp-types = "0.94" +async-lsp = { version = "0.1.0", default-features = false } +lsp-types = "0.94.1" tower = "0.4" # Wasm @@ -118,8 +119,22 @@ hex = "0.4.2" const_format = "0.2.30" num-bigint = "0.4" num-traits = "0.2" +similar-asserts = "1.5.0" [profile.dev] # This is required to be able to run `cargo test` in acvm_js due to the `locals exceeds maximum` error. # See https://ritik-mishra.medium.com/resolving-the-wasm-pack-error-locals-exceed-maximum-ec3a9d96685b opt-level = 1 + + +[profile.size] +inherits = "release" +lto = true +opt-level = "z" + +[profile.size-aggressive] +inherits = "release" +strip = true +lto = true +panic = "abort" +opt-level = "z" diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000000..ac818cb8bd2 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,15 @@ +FROM rust:alpine3.17 +RUN apk update \ + && apk upgrade \ + && apk add --no-cache \ + build-base \ + bash +WORKDIR /usr/src/noir +COPY . . +RUN ./scripts/bootstrap_native.sh + +# When running the container, mount the current working directory to /project. +FROM alpine:3.17 +COPY --from=0 /usr/src/noir/target/release/nargo /usr/src/noir/target/release/nargo +WORKDIR /project +ENTRYPOINT ["/usr/src/noir/target/release/nargo"] \ No newline at end of file diff --git a/Dockerfile.packages b/Dockerfile.packages new file mode 100644 index 00000000000..11737014e3d --- /dev/null +++ b/Dockerfile.packages @@ -0,0 +1,19 @@ +FROM rust:alpine3.17 +RUN apk update \ + && apk upgrade \ + && apk add --no-cache \ + build-base \ + pkgconfig \ + openssl-dev \ + npm \ + yarn \ + bash \ + jq +WORKDIR /usr/src/noir +COPY . . +RUN ./scripts/bootstrap_packages.sh + +FROM scratch +COPY --from=0 /usr/src/noir/packages /usr/src/noir/packages +# For some unknown reason, on alpine only, we need this to exist. +COPY --from=0 /usr/src/noir/node_modules/@noir-lang /usr/src/noir/node_modules/@noir-lang \ No newline at end of file diff --git a/acvm-repo/CHANGELOG.md b/acvm-repo/CHANGELOG.md index 92fcbf11f03..ff3ba716680 100644 --- a/acvm-repo/CHANGELOG.md +++ b/acvm-repo/CHANGELOG.md @@ -5,6 +5,196 @@ 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.35.0](https://github.com/noir-lang/noir/compare/v0.34.0...v0.35.0) (2023-11-28) + + +### ⚠ BREAKING CHANGES + +* Move circuit serialization circuit into acir ([#3345](https://github.com/noir-lang/noir/issues/3345)) +* 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)) +* Add debugger commands to introspect (and modify) the current state ([#3391](https://github.com/noir-lang/noir/issues/3391)) ([9e1ad85](https://github.com/noir-lang/noir/commit/9e1ad858cf8a1d9aba0137abe6a749267498bfaf)) +* 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)) +* Extract Brillig VM to allow step debugging ([#3259](https://github.com/noir-lang/noir/issues/3259)) ([f6431f9](https://github.com/noir-lang/noir/commit/f6431f99711f15a96a4f7fed2f413daece94b5e1)) +* 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)) +* Refactor debugger and separate core from UI ([#3308](https://github.com/noir-lang/noir/issues/3308)) ([8466810](https://github.com/noir-lang/noir/commit/846681079ab7295b201480a5c8baebc45e858c6f)) +* 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)) + + +### Miscellaneous Chores + +* Move circuit serialization circuit into acir ([#3345](https://github.com/noir-lang/noir/issues/3345)) ([122119b](https://github.com/noir-lang/noir/commit/122119b7377cec1b7c42c586c64b69b3bdf4d539)) + +## [0.34.0](https://github.com/noir-lang/noir/compare/v0.33.0...v0.34.0) (2023-11-22) + + +### ⚠ BREAKING CHANGES + +* Move circuit serialization circuit into acir ([#3345](https://github.com/noir-lang/noir/issues/3345)) +* 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)) +* Add debugger commands to introspect (and modify) the current state ([#3391](https://github.com/noir-lang/noir/issues/3391)) ([9e1ad85](https://github.com/noir-lang/noir/commit/9e1ad858cf8a1d9aba0137abe6a749267498bfaf)) +* 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)) +* Extract Brillig VM to allow step debugging ([#3259](https://github.com/noir-lang/noir/issues/3259)) ([f6431f9](https://github.com/noir-lang/noir/commit/f6431f99711f15a96a4f7fed2f413daece94b5e1)) +* 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)) +* Refactor debugger and separate core from UI ([#3308](https://github.com/noir-lang/noir/issues/3308)) ([8466810](https://github.com/noir-lang/noir/commit/846681079ab7295b201480a5c8baebc45e858c6f)) +* 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)) + + +### Miscellaneous Chores + +* Move circuit serialization circuit into acir ([#3345](https://github.com/noir-lang/noir/issues/3345)) ([122119b](https://github.com/noir-lang/noir/commit/122119b7377cec1b7c42c586c64b69b3bdf4d539)) + +## [0.33.0](https://github.com/noir-lang/noir/compare/v0.32.0...v0.33.0) (2023-11-07) + + +### ⚠ BREAKING CHANGES + +* Move circuit serialization circuit into acir ([#3345](https://github.com/noir-lang/noir/issues/3345)) +* 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)) +* Extract Brillig VM to allow step debugging ([#3259](https://github.com/noir-lang/noir/issues/3259)) ([f6431f9](https://github.com/noir-lang/noir/commit/f6431f99711f15a96a4f7fed2f413daece94b5e1)) +* 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)) +* Refactor debugger and separate core from UI ([#3308](https://github.com/noir-lang/noir/issues/3308)) ([8466810](https://github.com/noir-lang/noir/commit/846681079ab7295b201480a5c8baebc45e858c6f)) +* 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)) + + +### Miscellaneous Chores + +* Move circuit serialization circuit into acir ([#3345](https://github.com/noir-lang/noir/issues/3345)) ([122119b](https://github.com/noir-lang/noir/commit/122119b7377cec1b7c42c586c64b69b3bdf4d539)) + +## [0.32.0](https://github.com/noir-lang/noir/compare/v0.31.0...v0.32.0) (2023-11-07) + + +### ⚠ BREAKING CHANGES + +* Move circuit serialization circuit into acir ([#3345](https://github.com/noir-lang/noir/issues/3345)) +* 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)) +* Extract Brillig VM to allow step debugging ([#3259](https://github.com/noir-lang/noir/issues/3259)) ([f6431f9](https://github.com/noir-lang/noir/commit/f6431f99711f15a96a4f7fed2f413daece94b5e1)) +* 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)) +* Refactor debugger and separate core from UI ([#3308](https://github.com/noir-lang/noir/issues/3308)) ([8466810](https://github.com/noir-lang/noir/commit/846681079ab7295b201480a5c8baebc45e858c6f)) +* 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)) + + +### Miscellaneous Chores + +* Move circuit serialization circuit into acir ([#3345](https://github.com/noir-lang/noir/issues/3345)) ([122119b](https://github.com/noir-lang/noir/commit/122119b7377cec1b7c42c586c64b69b3bdf4d539)) + ## [0.31.0](https://github.com/noir-lang/noir/compare/v0.30.0...v0.31.0) (2023-11-02) diff --git a/acvm-repo/acir/Cargo.toml b/acvm-repo/acir/Cargo.toml index 14f44782aed..8dd6a69a07d 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.31.0" +version = "0.35.0" # x-release-please-end authors.workspace = true edition.workspace = true 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 22278bdc635..70821913836 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 @@ -120,75 +120,6 @@ pub enum BlackBoxFuncCall { } impl BlackBoxFuncCall { - #[deprecated = "BlackBoxFuncCall::dummy() is unnecessary and will be removed in ACVM 0.24.0"] - pub fn dummy(bb_func: BlackBoxFunc) -> Self { - match bb_func { - BlackBoxFunc::AND => BlackBoxFuncCall::AND { - lhs: FunctionInput::dummy(), - rhs: FunctionInput::dummy(), - output: Witness(0), - }, - BlackBoxFunc::XOR => BlackBoxFuncCall::XOR { - lhs: FunctionInput::dummy(), - rhs: FunctionInput::dummy(), - output: Witness(0), - }, - BlackBoxFunc::RANGE => BlackBoxFuncCall::RANGE { input: FunctionInput::dummy() }, - BlackBoxFunc::SHA256 => BlackBoxFuncCall::SHA256 { inputs: vec![], outputs: vec![] }, - BlackBoxFunc::Blake2s => BlackBoxFuncCall::Blake2s { inputs: vec![], outputs: vec![] }, - BlackBoxFunc::SchnorrVerify => BlackBoxFuncCall::SchnorrVerify { - public_key_x: FunctionInput::dummy(), - public_key_y: FunctionInput::dummy(), - signature: vec![], - message: vec![], - output: Witness(0), - }, - 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) } - } - BlackBoxFunc::EcdsaSecp256k1 => BlackBoxFuncCall::EcdsaSecp256k1 { - public_key_x: vec![], - public_key_y: vec![], - signature: vec![], - hashed_message: vec![], - output: Witness(0), - }, - BlackBoxFunc::EcdsaSecp256r1 => BlackBoxFuncCall::EcdsaSecp256r1 { - public_key_x: vec![], - public_key_y: vec![], - signature: vec![], - hashed_message: vec![], - output: Witness(0), - }, - BlackBoxFunc::FixedBaseScalarMul => BlackBoxFuncCall::FixedBaseScalarMul { - low: FunctionInput::dummy(), - high: FunctionInput::dummy(), - outputs: (Witness(0), Witness(0)), - }, - BlackBoxFunc::Keccak256 => { - BlackBoxFuncCall::Keccak256 { inputs: vec![], outputs: vec![] } - } - BlackBoxFunc::RecursiveAggregation => BlackBoxFuncCall::RecursiveAggregation { - verification_key: vec![], - proof: vec![], - public_inputs: vec![], - key_hash: FunctionInput::dummy(), - input_aggregation_object: None, - output_aggregation_object: vec![], - }, - } - } - pub fn get_black_box_func(&self) -> BlackBoxFunc { match self { BlackBoxFuncCall::AND { .. } => BlackBoxFunc::AND, diff --git a/acvm-repo/acir_field/Cargo.toml b/acvm-repo/acir_field/Cargo.toml index 2fc54e01ce7..4039a14ce73 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.31.0" +version = "0.35.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/acvm-repo/acir_field/src/generic_ark.rs b/acvm-repo/acir_field/src/generic_ark.rs index 0f4be21ad54..e3e66fe7a92 100644 --- a/acvm-repo/acir_field/src/generic_ark.rs +++ b/acvm-repo/acir_field/src/generic_ark.rs @@ -143,6 +143,12 @@ impl From for FieldElement { } } +impl From for FieldElement { + fn from(a: usize) -> FieldElement { + FieldElement::from(a as u128) + } +} + impl From for FieldElement { fn from(boolean: bool) -> FieldElement { if boolean { diff --git a/acvm-repo/acvm/Cargo.toml b/acvm-repo/acvm/Cargo.toml index 5238bc60e7e..ca43c54b204 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.31.0" +version = "0.35.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/acvm-repo/acvm/src/pwg/brillig.rs b/acvm-repo/acvm/src/pwg/brillig.rs index b0e5ec6dd48..0db38c776e2 100644 --- a/acvm-repo/acvm/src/pwg/brillig.rs +++ b/acvm-repo/acvm/src/pwg/brillig.rs @@ -112,6 +112,22 @@ impl<'b, B: BlackBoxFunctionSolver> BrilligSolver<'b, B> { Ok(Self { vm, acir_index }) } + pub fn get_registers(&self) -> &Registers { + self.vm.get_registers() + } + + pub fn set_register(&mut self, register_index: usize, value: Value) { + self.vm.set_register(RegisterIndex(register_index), value); + } + + pub fn get_memory(&self) -> &[Value] { + self.vm.get_memory() + } + + pub fn write_memory_at(&mut self, ptr: usize, value: Value) { + self.vm.write_memory_at(ptr, value); + } + pub(super) fn solve(&mut self) -> Result { let status = self.vm.process_opcodes(); self.handle_vm_status(status) diff --git a/acvm-repo/acvm/src/pwg/mod.rs b/acvm-repo/acvm/src/pwg/mod.rs index 6016d0d1f3e..c1edf60161a 100644 --- a/acvm-repo/acvm/src/pwg/mod.rs +++ b/acvm-repo/acvm/src/pwg/mod.rs @@ -168,6 +168,14 @@ impl<'a, B: BlackBoxFunctionSolver> ACVM<'a, B> { &self.witness_map } + pub fn overwrite_witness( + &mut self, + witness: Witness, + value: FieldElement, + ) -> Option { + self.witness_map.insert(witness, value) + } + /// Returns a slice containing the opcodes of the circuit being executed. pub fn opcodes(&self) -> &[Opcode] { self.opcodes diff --git a/acvm-repo/acvm_js/Cargo.toml b/acvm-repo/acvm_js/Cargo.toml index 1e7560f3008..190675da35c 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.31.0" +version = "0.35.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/acvm-repo/acvm_js/build.sh b/acvm-repo/acvm_js/build.sh index 37f2fd0a5a9..24af149bcea 100755 --- a/acvm-repo/acvm_js/build.sh +++ b/acvm-repo/acvm_js/build.sh @@ -34,7 +34,7 @@ export CARGO_TARGET_DIR=$self_path/target rm -rf $self_path/outputs >/dev/null 2>&1 rm -rf $self_path/result >/dev/null 2>&1 -if [ -v out ]; then +if [ -n "$out" ]; then echo "Will install package to $out (defined outside installPhase.sh script)" else export out="$self_path/outputs/out" diff --git a/acvm-repo/acvm_js/package.json b/acvm-repo/acvm_js/package.json index daa53ba5e81..6b3efc35d1a 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.31.0", + "version": "0.35.0", "repository": { "type": "git", "url": "https://github.com/noir-lang/acvm.git" diff --git a/acvm-repo/barretenberg_blackbox_solver/Cargo.toml b/acvm-repo/barretenberg_blackbox_solver/Cargo.toml index 6ea280645bd..9669a4184a4 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.31.0" +version = "0.35.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/acvm-repo/blackbox_solver/Cargo.toml b/acvm-repo/blackbox_solver/Cargo.toml index f4217c75592..170de76875b 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.31.0" +version = "0.35.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/acvm-repo/brillig/Cargo.toml b/acvm-repo/brillig/Cargo.toml index 81a88d50fe7..19488c8fbe8 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.31.0" +version = "0.35.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/acvm-repo/brillig_vm/Cargo.toml b/acvm-repo/brillig_vm/Cargo.toml index 442d027e547..21c5cd3a5f5 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.31.0" +version = "0.35.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/acvm-repo/brillig_vm/src/lib.rs b/acvm-repo/brillig_vm/src/lib.rs index e0d30f7b2e0..482b9b36d77 100644 --- a/acvm-repo/brillig_vm/src/lib.rs +++ b/acvm-repo/brillig_vm/src/lib.rs @@ -164,10 +164,18 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { &self.registers } + pub fn set_register(&mut self, register_index: RegisterIndex, value: Value) { + self.registers.set(register_index, value); + } + pub fn get_memory(&self) -> &Vec { self.memory.values() } + pub fn write_memory_at(&mut self, ptr: usize, value: Value) { + self.memory.write(ptr, value); + } + /// Process a single opcode and modify the program counter. pub fn process_opcode(&mut self) -> VMStatus { let opcode = &self.bytecode[self.program_counter]; diff --git a/acvm-repo/stdlib/Cargo.toml b/acvm-repo/stdlib/Cargo.toml index e876492b1d0..9e0e08e1338 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.31.0" +version = "0.35.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/aztec_macros/Cargo.toml b/aztec_macros/Cargo.toml new file mode 100644 index 00000000000..04f74d3b022 --- /dev/null +++ b/aztec_macros/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "aztec_macros" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true +repository.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +noirc_frontend.workspace = true +iter-extended.workspace = true diff --git a/aztec_macros/src/lib.rs b/aztec_macros/src/lib.rs new file mode 100644 index 00000000000..6d3aa0d8b01 --- /dev/null +++ b/aztec_macros/src/lib.rs @@ -0,0 +1,1024 @@ +use iter_extended::vecmap; + +use noirc_frontend::macros_api::FieldElement; +use noirc_frontend::macros_api::{ + BlockExpression, CallExpression, CastExpression, Distinctness, Expression, ExpressionKind, + ForLoopStatement, ForRange, FunctionDefinition, FunctionReturnType, FunctionVisibility, + HirContext, HirExpression, HirLiteral, HirStatement, Ident, ImportStatement, IndexExpression, + LetStatement, Literal, MemberAccessExpression, MethodCallExpression, NoirFunction, NoirStruct, + Param, Path, PathKind, Pattern, PrefixExpression, SecondaryAttribute, Signedness, Span, + Statement, StatementKind, StructType, Type, TypeImpl, UnaryOp, UnresolvedType, + UnresolvedTypeData, Visibility, +}; +use noirc_frontend::macros_api::{CrateId, FileId}; +use noirc_frontend::macros_api::{MacroError, MacroProcessor}; +use noirc_frontend::macros_api::{ModuleDefId, NodeInterner, SortedModule, StructId}; + +pub struct AztecMacro; + +impl MacroProcessor for AztecMacro { + fn process_untyped_ast( + &self, + ast: SortedModule, + crate_id: &CrateId, + context: &HirContext, + ) -> Result { + transform(ast, crate_id, context) + } + + fn process_typed_ast(&self, crate_id: &CrateId, context: &mut HirContext) { + transform_hir(crate_id, context) + } +} + +#[derive(Debug, Clone)] +pub enum AztecMacroError { + AztecNotFound, + AztecComputeNoteHashAndNullifierNotFound { span: Span }, +} + +impl From for MacroError { + fn from(err: AztecMacroError) -> Self { + match err { + AztecMacroError::AztecNotFound {} => MacroError { + primary_message: "Aztec dependency not found. Please add aztec as a dependency in your Cargo.toml. For more information go to https://docs.aztec.network/dev_docs/debugging/aztecnr-errors#aztec-dependency-not-found-please-add-aztec-as-a-dependency-in-your-nargotoml".to_owned(), + secondary_message: None, + span: None, + }, + AztecMacroError::AztecComputeNoteHashAndNullifierNotFound { span } => MacroError { + primary_message: "compute_note_hash_and_nullifier function not found. Define it in your contract. For more information go to https://docs.aztec.network/dev_docs/debugging/aztecnr-errors#compute_note_hash_and_nullifier-function-not-found-define-it-in-your-contract".to_owned(), + secondary_message: None, + span: Some(span), + }, + } + } +} + +// +// Helper macros for creating noir ast nodes +// +fn ident(name: &str) -> Ident { + Ident::new(name.to_string(), Span::default()) +} + +fn ident_path(name: &str) -> Path { + Path::from_ident(ident(name)) +} + +fn path(ident: Ident) -> Path { + Path::from_ident(ident) +} + +fn expression(kind: ExpressionKind) -> Expression { + Expression::new(kind, Span::default()) +} + +fn variable(name: &str) -> Expression { + expression(ExpressionKind::Variable(ident_path(name))) +} + +fn variable_ident(identifier: Ident) -> Expression { + expression(ExpressionKind::Variable(path(identifier))) +} + +fn variable_path(path: Path) -> Expression { + expression(ExpressionKind::Variable(path)) +} + +fn method_call(object: Expression, method_name: &str, arguments: Vec) -> Expression { + expression(ExpressionKind::MethodCall(Box::new(MethodCallExpression { + object, + method_name: ident(method_name), + arguments, + }))) +} + +fn call(func: Expression, arguments: Vec) -> Expression { + expression(ExpressionKind::Call(Box::new(CallExpression { func: Box::new(func), arguments }))) +} + +fn pattern(name: &str) -> Pattern { + Pattern::Identifier(ident(name)) +} + +fn mutable(name: &str) -> Pattern { + Pattern::Mutable(Box::new(pattern(name)), Span::default()) +} + +fn mutable_assignment(name: &str, assigned_to: Expression) -> Statement { + make_statement(StatementKind::Let(LetStatement { + pattern: mutable(name), + r#type: make_type(UnresolvedTypeData::Unspecified), + expression: assigned_to, + })) +} + +fn mutable_reference(variable_name: &str) -> Expression { + expression(ExpressionKind::Prefix(Box::new(PrefixExpression { + operator: UnaryOp::MutableReference, + rhs: variable(variable_name), + }))) +} + +fn assignment(name: &str, assigned_to: Expression) -> Statement { + make_statement(StatementKind::Let(LetStatement { + pattern: pattern(name), + r#type: make_type(UnresolvedTypeData::Unspecified), + expression: assigned_to, + })) +} + +fn member_access(lhs: &str, rhs: &str) -> Expression { + expression(ExpressionKind::MemberAccess(Box::new(MemberAccessExpression { + lhs: variable(lhs), + rhs: ident(rhs), + }))) +} + +macro_rules! chained_path { + ( $base:expr $(, $tail:expr)* ) => { + { + let mut base_path = ident_path($base); + $( + base_path.segments.push(ident($tail)); + )* + base_path + } + } +} + +macro_rules! chained_dep { + ( $base:expr $(, $tail:expr)* ) => { + { + let mut base_path = ident_path($base); + base_path.kind = PathKind::Dep; + $( + base_path.segments.push(ident($tail)); + )* + base_path + } + } +} + +fn cast(lhs: Expression, ty: UnresolvedTypeData) -> Expression { + expression(ExpressionKind::Cast(Box::new(CastExpression { lhs, r#type: make_type(ty) }))) +} + +fn make_type(typ: UnresolvedTypeData) -> UnresolvedType { + UnresolvedType { typ, span: None } +} + +fn index_array(array: Ident, index: &str) -> Expression { + expression(ExpressionKind::Index(Box::new(IndexExpression { + collection: variable_path(path(array)), + index: variable(index), + }))) +} + +fn index_array_variable(array: Expression, index: &str) -> Expression { + expression(ExpressionKind::Index(Box::new(IndexExpression { + collection: array, + index: variable(index), + }))) +} + +fn import(path: Path) -> ImportStatement { + ImportStatement { path, alias: None } +} + +// +// Create AST Nodes for Aztec +// + +/// Traverses every function in the ast, calling `transform_function` which +/// determines if further processing is required +fn transform( + mut ast: SortedModule, + crate_id: &CrateId, + context: &HirContext, +) -> Result { + // Usage -> mut ast -> aztec_library::transform(&mut ast) + + // Covers all functions in the ast + for submodule in ast.submodules.iter_mut().filter(|submodule| submodule.is_contract) { + if transform_module(&mut submodule.contents, crate_id, context)? { + check_for_aztec_dependency(crate_id, context)?; + include_relevant_imports(&mut submodule.contents); + } + } + Ok(ast) +} + +// +// Transform Hir Nodes for Aztec +// + +/// Completes the Hir with data gathered from type resolution +fn transform_hir(crate_id: &CrateId, context: &mut HirContext) { + transform_events(crate_id, context); +} + +/// Includes an import to the aztec library if it has not been included yet +fn include_relevant_imports(ast: &mut SortedModule) { + // Create the aztec import path using the assumed chained_dep! macro + let aztec_import_path = import(chained_dep!("aztec")); + + // Check if the aztec import already exists + let is_aztec_imported = + ast.imports.iter().any(|existing_import| existing_import.path == aztec_import_path.path); + + // If aztec is not imported, add the import at the beginning + if !is_aztec_imported { + ast.imports.insert(0, aztec_import_path); + } +} + +/// Creates an error alerting the user that they have not downloaded the Aztec-noir library +fn check_for_aztec_dependency( + crate_id: &CrateId, + context: &HirContext, +) -> Result<(), (MacroError, 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((AztecMacroError::AztecNotFound.into(), 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(|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].typ.typ == UnresolvedTypeData::FieldElement + && func.def.parameters[1].typ.typ == UnresolvedTypeData::FieldElement + && func.def.parameters[2].typ.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].typ.typ { + UnresolvedTypeData::Array(_, inner_type) => { + matches!(inner_type.typ, UnresolvedTypeData::FieldElement) + }, + _ => 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) => { + matches!(inner_type.typ, UnresolvedTypeData::FieldElement) + }, + _ => false, + } + } + } + }) +} + +/// Checks if an attribute is a custom attribute with a specific name +fn is_custom_attribute(attr: &SecondaryAttribute, attribute_name: &str) -> bool { + if let SecondaryAttribute::Custom(custom_attr) = attr { + custom_attr.as_str() == attribute_name + } else { + false + } +} + +/// 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, + crate_id: &CrateId, + context: &HirContext, +) -> 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(( + AztecMacroError::AztecComputeNoteHashAndNullifierNotFound { span: Span::default() } + .into(), + 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)); + has_transformed_module = true; + } + } + + for func in module.functions.iter_mut() { + for secondary_attribute in func.def.attributes.secondary.clone() { + if is_custom_attribute(&secondary_attribute, "aztec(private)") { + transform_function("Private", func, storage_defined); + has_transformed_module = true; + } else if is_custom_attribute(&secondary_attribute, "aztec(public)") { + transform_function("Public", func, storage_defined); + has_transformed_module = true; + } + } + // Add the storage struct to the beginning of the function if it is unconstrained in an aztec contract + if storage_defined && func.def.is_unconstrained { + transform_unconstrained(func); + has_transformed_module = true; + } + } + Ok(has_transformed_module) +} + +/// If it does, it will insert the following things: +/// - A new Input that is provided for a kernel app circuit, named: {Public/Private}ContextInputs +/// - Hashes all of the function input variables +/// - This instantiates a helper function +fn transform_function(ty: &str, func: &mut NoirFunction, storage_defined: bool) { + let context_name = format!("{}Context", ty); + let inputs_name = format!("{}ContextInputs", ty); + let return_type_name = format!("{}CircuitPublicInputs", ty); + + // Add access to the storage struct + if storage_defined { + let storage_def = abstract_storage(&ty.to_lowercase(), false); + func.def.body.0.insert(0, storage_def); + } + + // Insert the context creation as the first action + let create_context = create_context(&context_name, &func.def.parameters); + func.def.body.0.splice(0..0, (create_context).iter().cloned()); + + // Add the inputs to the params + let input = create_inputs(&inputs_name); + func.def.parameters.insert(0, input); + + // Abstract return types such that they get added to the kernel's return_values + if let Some(return_values) = abstract_return_values(func) { + func.def.body.0.push(return_values); + } + + // Push the finish method call to the end of the function + let finish_def = create_context_finish(); + func.def.body.0.push(finish_def); + + let return_type = create_return_type(&return_type_name); + func.def.return_type = return_type; + func.def.return_visibility = Visibility::Public; + + // Distinct return types are only required for private functions + // Public functions should have open auto-inferred + match ty { + "Private" => func.def.return_distinctness = Distinctness::Distinct, + "Public" => func.def.is_open = true, + _ => (), + } +} + +/// Transform Unconstrained +/// +/// Inserts the following code at the beginning of an unconstrained function +/// ```noir +/// let storage = Storage::init(Context::none()); +/// ``` +/// +/// This will allow developers to access their contract' storage struct in unconstrained functions +fn transform_unconstrained(func: &mut NoirFunction) { + func.def.body.0.insert(0, abstract_storage("Unconstrained", true)); +} + +fn collect_crate_structs(crate_id: &CrateId, context: &HirContext) -> Vec { + context + .def_map(crate_id) + .expect("ICE: Missing crate in def_map") + .modules() + .iter() + .flat_map(|(_, module)| { + module.type_definitions().filter_map(|typ| { + if let ModuleDefId::TypeId(struct_id) = typ { + Some(struct_id) + } else { + None + } + }) + }) + .collect() +} + +/// Substitutes the signature literal that was introduced in the selector method previously with the actual signature. +fn transform_event(struct_id: StructId, interner: &mut NodeInterner) { + let struct_type = interner.get_struct(struct_id); + let selector_id = interner + .lookup_method(&Type::Struct(struct_type, vec![]), struct_id, "selector", false) + .expect("Selector method not found"); + let selector_function = interner.function(&selector_id); + + let compute_selector_statement = interner.statement( + selector_function + .block(interner) + .statements() + .first() + .expect("Compute selector statement not found"), + ); + + let compute_selector_expression = match compute_selector_statement { + HirStatement::Expression(expression_id) => match interner.expression(&expression_id) { + HirExpression::Call(hir_call_expression) => Some(hir_call_expression), + _ => None, + }, + _ => None, + } + .expect("Compute selector statement is not a call expression"); + + let first_arg_id = compute_selector_expression + .arguments + .first() + .expect("Missing argument for compute selector"); + + match interner.expression(first_arg_id) { + HirExpression::Literal(HirLiteral::Str(signature)) + if signature == SIGNATURE_PLACEHOLDER => + { + let selector_literal_id = first_arg_id; + + let structure = interner.get_struct(struct_id); + let signature = event_signature(&structure.borrow()); + interner.update_expression(*selector_literal_id, |expr| { + *expr = HirExpression::Literal(HirLiteral::Str(signature.clone())); + }); + + // Also update the type! It might have a different length now than the placeholder. + interner.push_expr_type( + selector_literal_id, + Type::String(Box::new(Type::Constant(signature.len() as u64))), + ); + } + _ => unreachable!("Signature placeholder literal does not match"), + } +} + +fn transform_events(crate_id: &CrateId, context: &mut HirContext) { + for struct_id in collect_crate_structs(crate_id, context) { + let attributes = context.def_interner.struct_attributes(&struct_id); + if attributes.iter().any(|attr| matches!(attr, SecondaryAttribute::Event)) { + transform_event(struct_id, &mut context.def_interner); + } + } +} + +const SIGNATURE_PLACEHOLDER: &str = "SIGNATURE_PLACEHOLDER"; + +/// Generates the impl for an event selector +/// +/// Inserts the following code: +/// ```noir +/// impl SomeStruct { +/// fn selector() -> Field { +/// aztec::oracle::compute_selector::compute_selector("SIGNATURE_PLACEHOLDER") +/// } +/// } +/// ``` +/// +/// This allows developers to emit events without having to write the signature of the event every time they emit it. +/// The signature cannot be known at this point since types are not resolved yet, so we use a signature placeholder. +/// It'll get resolved after by transforming the HIR. +fn generate_selector_impl(structure: &NoirStruct) -> TypeImpl { + let struct_type = make_type(UnresolvedTypeData::Named(path(structure.name.clone()), vec![])); + + let selector_fun_body = BlockExpression(vec![make_statement(StatementKind::Expression(call( + variable_path(chained_path!("aztec", "selector", "compute_selector")), + vec![expression(ExpressionKind::Literal(Literal::Str(SIGNATURE_PLACEHOLDER.to_string())))], + )))]); + + let mut selector_fn_def = FunctionDefinition::normal( + &ident("selector"), + &vec![], + &[], + &selector_fun_body, + &[], + &FunctionReturnType::Ty(make_type(UnresolvedTypeData::FieldElement)), + ); + + selector_fn_def.visibility = FunctionVisibility::Public; + + // Seems to be necessary on contract modules + selector_fn_def.return_visibility = Visibility::Public; + + TypeImpl { + object_type: struct_type, + type_span: structure.span, + generics: vec![], + methods: vec![NoirFunction::normal(selector_fn_def)], + } +} + +/// Helper function that returns what the private context would look like in the ast +/// This should make it available to be consumed within aztec private annotated functions. +/// +/// The replaced code: +/// ```noir +/// /// Before +/// fn foo(inputs: PrivateContextInputs) { +/// // ... +/// } +/// +/// /// After +/// #[aztec(private)] +/// fn foo() { +/// // ... +/// } +fn create_inputs(ty: &str) -> Param { + let context_ident = ident("inputs"); + let context_pattern = Pattern::Identifier(context_ident); + let type_path = chained_path!("aztec", "abi", ty); + let context_type = make_type(UnresolvedTypeData::Named(type_path, vec![])); + let visibility = Visibility::Private; + + Param { pattern: context_pattern, typ: context_type, visibility, span: Span::default() } +} + +/// Creates the private context object to be accessed within the function, the parameters need to be extracted to be +/// appended into the args hash object. +/// +/// The replaced code: +/// ```noir +/// #[aztec(private)] +/// fn foo(structInput: SomeStruct, arrayInput: [u8; 10], fieldInput: Field) -> Field { +/// // Create the hasher object +/// let mut hasher = Hasher::new(); +/// +/// // struct inputs call serialize on them to add an array of fields +/// hasher.add_multiple(structInput.serialize()); +/// +/// // Array inputs are iterated over and each element is added to the hasher (as a field) +/// for i in 0..arrayInput.len() { +/// hasher.add(arrayInput[i] as Field); +/// } +/// // Field inputs are added to the hasher +/// hasher.add({ident}); +/// +/// // Create the context +/// // The inputs (injected by this `create_inputs`) and completed hash object are passed to the context +/// let mut context = PrivateContext::new(inputs, hasher.hash()); +/// } +/// ``` +fn create_context(ty: &str, params: &[Param]) -> Vec { + let mut injected_expressions: Vec = vec![]; + + // `let mut hasher = Hasher::new();` + let let_hasher = mutable_assignment( + "hasher", // Assigned to + call( + variable_path(chained_path!("aztec", "abi", "Hasher", "new")), // Path + vec![], // args + ), + ); + + // Completes: `let mut hasher = Hasher::new();` + injected_expressions.push(let_hasher); + + // Iterate over each of the function parameters, adding to them to the hasher + params.iter().for_each(|Param { pattern, typ, span: _, visibility: _ }| { + match pattern { + Pattern::Identifier(identifier) => { + // Match the type to determine the padding to do + let unresolved_type = &typ.typ; + let expression = match unresolved_type { + // `hasher.add_multiple({ident}.serialize())` + UnresolvedTypeData::Named(..) => add_struct_to_hasher(identifier), + UnresolvedTypeData::Array(_, arr_type) => { + add_array_to_hasher(identifier, arr_type) + } + // `hasher.add({ident})` + UnresolvedTypeData::FieldElement => add_field_to_hasher(identifier), + // Add the integer to the hasher, casted to a field + // `hasher.add({ident} as Field)` + UnresolvedTypeData::Integer(..) | UnresolvedTypeData::Bool => { + add_cast_to_hasher(identifier) + } + _ => unreachable!("[Aztec Noir] Provided parameter type is not supported"), + }; + injected_expressions.push(expression); + } + _ => todo!(), // Maybe unreachable? + } + }); + + // Create the inputs to the context + let inputs_expression = variable("inputs"); + // `hasher.hash()` + let hash_call = method_call( + variable("hasher"), // variable + "hash", // method name + vec![], // args + ); + + // let mut context = {ty}::new(inputs, hash); + let let_context = mutable_assignment( + "context", // Assigned to + call( + variable_path(chained_path!("aztec", "context", ty, "new")), // Path + vec![inputs_expression, hash_call], // args + ), + ); + injected_expressions.push(let_context); + + // Return all expressions that will be injected by the hasher + injected_expressions +} + +/// Abstract Return Type +/// +/// This function intercepts the function's current return type and replaces it with pushes +/// To the kernel +/// +/// The replaced code: +/// ```noir +/// /// Before +/// #[aztec(private)] +/// fn foo() -> abi::PrivateCircuitPublicInputs { +/// // ... +/// let my_return_value: Field = 10; +/// context.return_values.push(my_return_value); +/// } +/// +/// /// After +/// #[aztec(private)] +/// fn foo() -> Field { +/// // ... +/// let my_return_value: Field = 10; +/// my_return_value +/// } +/// ``` +/// Similarly; Structs will be pushed to the context, after serialize() is called on them. +/// Arrays will be iterated over and each element will be pushed to the context. +/// Any primitive type that can be cast will be casted to a field and pushed to the context. +fn abstract_return_values(func: &NoirFunction) -> Option { + let current_return_type = func.return_type().typ; + let len = func.def.body.len(); + let last_statement = &func.def.body.0[len - 1]; + + // TODO: (length, type) => We can limit the size of the array returned to be limited by kernel size + // Doesn't need done until we have settled on a kernel size + // TODO: support tuples here and in inputs -> convert into an issue + + // Check if the return type is an expression, if it is, we can handle it + match last_statement { + Statement { kind: StatementKind::Expression(expression), .. } => { + match current_return_type { + // Call serialize on structs, push the whole array, calling push_array + UnresolvedTypeData::Named(..) => Some(make_struct_return_type(expression.clone())), + UnresolvedTypeData::Array(..) => Some(make_array_return_type(expression.clone())), + // Cast these types to a field before pushing + UnresolvedTypeData::Bool | UnresolvedTypeData::Integer(..) => { + Some(make_castable_return_type(expression.clone())) + } + UnresolvedTypeData::FieldElement => Some(make_return_push(expression.clone())), + _ => None, + } + } + _ => None, + } +} + +/// Abstract storage +/// +/// For private functions: +/// ```noir +/// #[aztec(private)] +/// fn lol() { +/// let storage = Storage::init(Context::private(context)); +/// } +/// ``` +/// +/// For public functions: +/// ```noir +/// #[aztec(public)] +/// fn lol() { +/// let storage = Storage::init(Context::public(context)); +/// } +/// ``` +/// +/// For unconstrained functions: +/// ```noir +/// unconstrained fn lol() { +/// let storage = Storage::init(Context::none()); +/// } +fn abstract_storage(typ: &str, unconstrained: bool) -> Statement { + let init_context_call = if unconstrained { + call( + variable_path(chained_path!("aztec", "context", "Context", "none")), // Path + vec![], // args + ) + } else { + call( + variable_path(chained_path!("aztec", "context", "Context", typ)), // Path + vec![mutable_reference("context")], // args + ) + }; + + assignment( + "storage", // Assigned to + call( + variable_path(chained_path!("Storage", "init")), // Path + vec![init_context_call], // args + ), + ) +} + +/// Context Return Values +/// +/// Creates an instance to the context return values +/// ```noir +/// `context.return_values` +/// ``` +fn context_return_values() -> Expression { + member_access("context", "return_values") +} + +fn make_statement(kind: StatementKind) -> Statement { + Statement { span: Span::default(), kind } +} + +/// Make return Push +/// +/// Translates to: +/// `context.return_values.push({push_value})` +fn make_return_push(push_value: Expression) -> Statement { + make_statement(StatementKind::Semi(method_call( + context_return_values(), + "push", + vec![push_value], + ))) +} + +/// Make Return push array +/// +/// Translates to: +/// `context.return_values.push_array({push_value})` +fn make_return_push_array(push_value: Expression) -> Statement { + make_statement(StatementKind::Semi(method_call( + context_return_values(), + "push_array", + vec![push_value], + ))) +} + +/// Make struct return type +/// +/// Translates to: +/// ```noir +/// `context.return_values.push_array({push_value}.serialize())` +fn make_struct_return_type(expression: Expression) -> Statement { + let serialized_call = method_call( + expression, // variable + "serialize", // method name + vec![], // args + ); + make_return_push_array(serialized_call) +} + +/// Make array return type +/// +/// Translates to: +/// ```noir +/// for i in 0..{ident}.len() { +/// context.return_values.push({ident}[i] as Field) +/// } +/// ``` +fn make_array_return_type(expression: Expression) -> Statement { + let inner_cast_expression = + cast(index_array_variable(expression.clone(), "i"), UnresolvedTypeData::FieldElement); + let assignment = make_statement(StatementKind::Semi(method_call( + context_return_values(), // variable + "push", // method name + vec![inner_cast_expression], + ))); + + create_loop_over(expression, vec![assignment]) +} + +/// Castable return type +/// +/// Translates to: +/// ```noir +/// context.return_values.push({ident} as Field) +/// ``` +fn make_castable_return_type(expression: Expression) -> Statement { + // Cast these types to a field before pushing + let cast_expression = cast(expression, UnresolvedTypeData::FieldElement); + make_return_push(cast_expression) +} + +/// Create Return Type +/// +/// Public functions return abi::PublicCircuitPublicInputs while +/// private functions return abi::PrivateCircuitPublicInputs +/// +/// This call constructs an ast token referencing the above types +/// The name is set in the function above `transform`, hence the +/// whole token name is passed in +/// +/// The replaced code: +/// ```noir +/// +/// /// Before +/// fn foo() -> abi::PrivateCircuitPublicInputs { +/// // ... +/// } +/// +/// /// After +/// #[aztec(private)] +/// fn foo() { +/// // ... +/// } +fn create_return_type(ty: &str) -> FunctionReturnType { + let return_path = chained_path!("aztec", "abi", ty); + + let ty = make_type(UnresolvedTypeData::Named(return_path, vec![])); + FunctionReturnType::Ty(ty) +} + +/// Create Context Finish +/// +/// Each aztec function calls `context.finish()` at the end of a function +/// to return values required by the kernel. +/// +/// The replaced code: +/// ```noir +/// /// Before +/// fn foo() -> abi::PrivateCircuitPublicInputs { +/// // ... +/// context.finish() +/// } +/// +/// /// After +/// #[aztec(private)] +/// fn foo() { +/// // ... +/// } +fn create_context_finish() -> Statement { + let method_call = method_call( + variable("context"), // variable + "finish", // method name + vec![], // args + ); + make_statement(StatementKind::Expression(method_call)) +} + +// +// Methods to create hasher inputs +// + +fn add_struct_to_hasher(identifier: &Ident) -> Statement { + // If this is a struct, we call serialize and add the array to the hasher + let serialized_call = method_call( + variable_path(path(identifier.clone())), // variable + "serialize", // method name + vec![], // args + ); + + make_statement(StatementKind::Semi(method_call( + variable("hasher"), // variable + "add_multiple", // method name + vec![serialized_call], // args + ))) +} + +fn create_loop_over(var: Expression, loop_body: Vec) -> Statement { + // If this is an array of primitive types (integers / fields) we can add them each to the hasher + // casted to a field + let span = var.span; + + // `array.len()` + let end_range_expression = method_call( + var, // variable + "len", // method name + vec![], // args + ); + + // What will be looped over + // - `hasher.add({ident}[i] as Field)` + let for_loop_block = expression(ExpressionKind::Block(BlockExpression(loop_body))); + + // `for i in 0..{ident}.len()` + make_statement(StatementKind::For(ForLoopStatement { + range: ForRange::Range( + expression(ExpressionKind::Literal(Literal::Integer(FieldElement::from(i128::from( + 0, + ))))), + end_range_expression, + ), + identifier: ident("i"), + block: for_loop_block, + span, + })) +} + +fn add_array_to_hasher(identifier: &Ident, arr_type: &UnresolvedType) -> Statement { + // If this is an array of primitive types (integers / fields) we can add them each to the hasher + // casted to a field + + // Wrap in the semi thing - does that mean ended with semi colon? + // `hasher.add({ident}[i] as Field)` + + let arr_index = index_array(identifier.clone(), "i"); + let (add_expression, hasher_method_name) = match arr_type.typ { + UnresolvedTypeData::Named(..) => { + let hasher_method_name = "add_multiple".to_owned(); + let call = method_call( + // All serialise on each element + arr_index, // variable + "serialize", // method name + vec![], // args + ); + (call, hasher_method_name) + } + _ => { + let hasher_method_name = "add".to_owned(); + let call = cast( + arr_index, // lhs - `ident[i]` + UnresolvedTypeData::FieldElement, // cast to - `as Field` + ); + (call, hasher_method_name) + } + }; + + let block_statement = make_statement(StatementKind::Semi(method_call( + variable("hasher"), // variable + &hasher_method_name, // method name + vec![add_expression], + ))); + + create_loop_over(variable_ident(identifier.clone()), vec![block_statement]) +} + +fn add_field_to_hasher(identifier: &Ident) -> Statement { + // `hasher.add({ident})` + let ident = variable_path(path(identifier.clone())); + make_statement(StatementKind::Semi(method_call( + variable("hasher"), // variable + "add", // method name + vec![ident], // args + ))) +} + +fn add_cast_to_hasher(identifier: &Ident) -> Statement { + // `hasher.add({ident} as Field)` + // `{ident} as Field` + let cast_operation = cast( + variable_path(path(identifier.clone())), // lhs + UnresolvedTypeData::FieldElement, // rhs + ); + + // `hasher.add({ident} as Field)` + make_statement(StatementKind::Semi(method_call( + variable("hasher"), // variable + "add", // method name + vec![cast_operation], // args + ))) +} + +/// Computes the aztec signature for a resolved type. +fn signature_of_type(typ: &Type) -> String { + match typ { + Type::Integer(Signedness::Signed, bit_size) => format!("i{}", bit_size), + Type::Integer(Signedness::Unsigned, bit_size) => format!("u{}", bit_size), + Type::FieldElement => "Field".to_owned(), + Type::Bool => "bool".to_owned(), + Type::Array(len, typ) => { + if let Type::Constant(len) = **len { + format!("[{};{len}]", signature_of_type(typ)) + } else { + unimplemented!("Cannot generate signature for array with length type {:?}", typ) + } + } + Type::Struct(def, args) => { + let fields = def.borrow().get_fields(args); + let fields = vecmap(fields, |(_, typ)| signature_of_type(&typ)); + format!("({})", fields.join(",")) + } + Type::Tuple(types) => { + let fields = vecmap(types, signature_of_type); + format!("({})", fields.join(",")) + } + _ => unimplemented!("Cannot generate signature for type {:?}", typ), + } +} + +/// Computes the signature for a resolved event type. +/// It has the form 'EventName(Field,(Field),[u8;2])' +fn event_signature(event: &StructType) -> String { + let fields = vecmap(event.get_fields(&[]), |(_, typ)| signature_of_type(&typ)); + format!("{}({})", event.name.0.contents, fields.join(",")) +} diff --git a/bootstrap.sh b/bootstrap.sh new file mode 100755 index 00000000000..bf672ac0ad2 --- /dev/null +++ b/bootstrap.sh @@ -0,0 +1,19 @@ +#!/bin/bash +set -eu + +cd $(dirname "$0") + +CMD=${1:-} + +if [ -n "$CMD" ]; then + if [ "$CMD" = "clean" ]; then + git clean -fdx + exit 0 + else + echo "Unknown command: $CMD" + exit 1 + fi +fi + +./scripts/bootstrap_native.sh +./scripts/bootstrap_packages.sh \ No newline at end of file diff --git a/compiler/integration-tests/circuits/main/Nargo.toml b/compiler/integration-tests/circuits/main/Nargo.toml deleted file mode 100644 index cc5a0a357fa..00000000000 --- a/compiler/integration-tests/circuits/main/Nargo.toml +++ /dev/null @@ -1,5 +0,0 @@ -[package] -name = "main" -type = "bin" -authors = [""] -[dependencies] diff --git a/compiler/integration-tests/circuits/recursion/src/main.nr b/compiler/integration-tests/circuits/recursion/src/main.nr index bc1f3e76d1d..e60e4e0b61a 100644 --- a/compiler/integration-tests/circuits/recursion/src/main.nr +++ b/compiler/integration-tests/circuits/recursion/src/main.nr @@ -5,10 +5,13 @@ fn main( proof : [Field; 94], public_inputs : [Field; 1], key_hash : Field, - input_aggregation_object : [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) +) -> pub [Field;16]{ + let input_aggregation_object = [0; 16]; + std::verify_proof( + verification_key.as_slice(), + proof.as_slice(), + public_inputs.as_slice(), + key_hash, + input_aggregation_object + ) } diff --git a/compiler/integration-tests/hardhat.config.ts b/compiler/integration-tests/hardhat.config.ts index 2c68b3fae0f..af4728c68ec 100644 --- a/compiler/integration-tests/hardhat.config.ts +++ b/compiler/integration-tests/hardhat.config.ts @@ -12,6 +12,9 @@ const config: HardhatUserConfig = { }, }, }, + mocha: { + timeout: 5 * 60 * 1000, + }, }; export default config; diff --git a/compiler/integration-tests/scripts/codegen-verifiers.sh b/compiler/integration-tests/scripts/codegen-verifiers.sh index d9d9d4f243f..b3a52217271 100644 --- a/compiler/integration-tests/scripts/codegen-verifiers.sh +++ b/compiler/integration-tests/scripts/codegen-verifiers.sh @@ -5,16 +5,22 @@ self_path=$(dirname "$(readlink -f "$0")") repo_root=$self_path/../../.. # Run codegen-verifier for 1_mul -mul_dir=$repo_root/tooling/nargo_cli/tests/execution_success/1_mul +mul_dir=$repo_root/test_programs/execution_success/1_mul nargo --program-dir $mul_dir codegen-verifier -# Run codegen-verifier for main -main_dir=$repo_root/compiler/integration-tests/circuits/main -nargo --program-dir $main_dir codegen-verifier +# Run codegen-verifier for assert_statement +assert_statement_dir=$repo_root/test_programs/execution_success/assert_statement +nargo --program-dir $assert_statement_dir codegen-verifier + +# Run codegen-verifier for recursion +recursion_dir=$repo_root/compiler/integration-tests/circuits/recursion +nargo --program-dir $recursion_dir codegen-verifier # Copy compiled contracts from the root of compiler/integration-tests contracts_dir=$self_path/../contracts +rm -rf $contracts_dir mkdir $contracts_dir cp $mul_dir/contract/1_mul/plonk_vk.sol $contracts_dir/1_mul.sol -cp $main_dir/contract/main/plonk_vk.sol $contracts_dir/main.sol +cp $assert_statement_dir/contract/assert_statement/plonk_vk.sol $contracts_dir/assert_statement.sol +cp $recursion_dir/contract/recursion/plonk_vk.sol $contracts_dir/recursion.sol diff --git a/compiler/integration-tests/test/browser/compile_prove_verify.test.ts b/compiler/integration-tests/test/browser/compile_prove_verify.test.ts index 95a1aa502ad..2aef56c23f9 100644 --- a/compiler/integration-tests/test/browser/compile_prove_verify.test.ts +++ b/compiler/integration-tests/test/browser/compile_prove_verify.test.ts @@ -19,11 +19,11 @@ compilerLogLevel('INFO'); const test_cases = [ { - case: 'tooling/nargo_cli/tests/execution_success/1_mul', + case: 'test_programs/execution_success/1_mul', numPublicInputs: 0, }, { - case: 'compiler/integration-tests/circuits/main', + case: 'test_programs/execution_success/assert_statement', numPublicInputs: 1, }, ]; diff --git a/compiler/integration-tests/test/browser/recursion.test.ts b/compiler/integration-tests/test/browser/recursion.test.ts index dbf74882654..308be81417f 100644 --- a/compiler/integration-tests/test/browser/recursion.test.ts +++ b/compiler/integration-tests/test/browser/recursion.test.ts @@ -23,7 +23,7 @@ await initACVM(); compilerLogLevel('INFO'); const base_relative_path = '../../../../..'; -const circuit_main = 'compiler/integration-tests/circuits/main'; +const circuit_main = 'test_programs/execution_success/assert_statement'; const circuit_recursion = 'compiler/integration-tests/circuits/recursion'; function getCircuit(noirSource: string): CompiledProgram { diff --git a/compiler/integration-tests/test/node/onchain_recursive_verification.test.ts b/compiler/integration-tests/test/node/onchain_recursive_verification.test.ts new file mode 100644 index 00000000000..353678b470b --- /dev/null +++ b/compiler/integration-tests/test/node/onchain_recursive_verification.test.ts @@ -0,0 +1,67 @@ +import { expect } from 'chai'; +import { ethers } from 'hardhat'; + +import { readFileSync } from 'node:fs'; +import { resolve } from 'path'; +import toml from 'toml'; + +import { compile, CompiledProgram, init_log_level as compilerLogLevel } from '@noir-lang/noir_wasm'; +import { Noir } from '@noir-lang/noir_js'; +import { BarretenbergBackend, flattenPublicInputs } from '@noir-lang/backend_barretenberg'; +import { Field, InputMap } from '@noir-lang/noirc_abi'; + +compilerLogLevel('INFO'); + +it(`smart contract can verify a recursive proof`, async () => { + const inner_source_path = resolve(`../../test_programs/execution_success/assert_statement/src/main.nr`); + const inner_program = (compile(inner_source_path) as { program: CompiledProgram }).program; + + const recursion_source_path = resolve(`./circuits/recursion/src/main.nr`); + const recursion_program = (compile(recursion_source_path) as { program: CompiledProgram }).program; + + // Intermediate proof + + const inner_backend = new BarretenbergBackend(inner_program); + const inner = new Noir(inner_program); + + const inner_prover_toml = readFileSync( + resolve(`../../test_programs/execution_success/assert_statement/Prover.toml`), + ).toString(); + const inner_inputs = toml.parse(inner_prover_toml); + + const { witness: main_witness } = await inner.execute(inner_inputs); + const intermediate_proof = await inner_backend.generateIntermediateProof(main_witness); + + expect(await inner_backend.verifyIntermediateProof(intermediate_proof)).to.be.true; + + const { proofAsFields, vkAsFields, vkHash } = await inner_backend.generateIntermediateProofArtifacts( + intermediate_proof, + 1, // 1 public input + ); + + // Final proof + + const recursion_backend = new BarretenbergBackend(recursion_program); + const recursion = new Noir(recursion_program, recursion_backend); + + const recursion_inputs: InputMap = { + verification_key: vkAsFields, + proof: proofAsFields, + public_inputs: [inner_inputs.y as Field], + key_hash: vkHash, + }; + + const recursion_proof = await recursion.generateFinalProof(recursion_inputs); + expect(await recursion.verifyFinalProof(recursion_proof)).to.be.true; + + // Smart contract verification + + const contract = await ethers.deployContract('contracts/recursion.sol:UltraVerifier', []); + + const result = await contract.verify.staticCall( + recursion_proof.proof, + flattenPublicInputs(recursion_proof.publicInputs), + ); + + expect(result).to.be.true; +}); diff --git a/compiler/integration-tests/test/node/smart_contract_verifier.test.ts b/compiler/integration-tests/test/node/smart_contract_verifier.test.ts index 738bc2df8dd..7dafada0ffb 100644 --- a/compiler/integration-tests/test/node/smart_contract_verifier.test.ts +++ b/compiler/integration-tests/test/node/smart_contract_verifier.test.ts @@ -7,19 +7,19 @@ import toml from 'toml'; import { compile, init_log_level as compilerLogLevel } from '@noir-lang/noir_wasm'; import { Noir } from '@noir-lang/noir_js'; -import { BarretenbergBackend } from '@noir-lang/backend_barretenberg'; +import { BarretenbergBackend, flattenPublicInputs } from '@noir-lang/backend_barretenberg'; compilerLogLevel('INFO'); const test_cases = [ { - case: 'tooling/nargo_cli/tests/execution_success/1_mul', + case: 'test_programs/execution_success/1_mul', compiled: 'contracts/1_mul.sol:UltraVerifier', numPublicInputs: 0, }, { - case: 'compiler/integration-tests/circuits/main', - compiled: 'contracts/main.sol:UltraVerifier', + case: 'test_programs/execution_success/assert_statement', + compiled: 'contracts/assert_statement.sol:UltraVerifier', numPublicInputs: 1, }, ]; @@ -57,9 +57,9 @@ test_cases.forEach((testInfo) => { // Smart contract verification - const contract = await ethers.deployContract(testInfo.compiled, [], {}); + const contract = await ethers.deployContract(testInfo.compiled, []); - const result = await contract.verify(proofData.proof, proofData.publicInputs); + const result = await contract.verify(proofData.proof, flattenPublicInputs(proofData.publicInputs)); expect(result).to.be.true; }); diff --git a/compiler/noirc_driver/Cargo.toml b/compiler/noirc_driver/Cargo.toml index 09044b39323..c717efed6f5 100644 --- a/compiler/noirc_driver/Cargo.toml +++ b/compiler/noirc_driver/Cargo.toml @@ -21,3 +21,8 @@ iter-extended.workspace = true fm.workspace = true serde.workspace = true fxhash.workspace = true + +aztec_macros ={path = "../../aztec_macros", optional = true} + +[features] +aztec = ["aztec_macros"] \ No newline at end of file diff --git a/compiler/noirc_driver/build.rs b/compiler/noirc_driver/build.rs index 7b5a645c026..6bef7f1fda7 100644 --- a/compiler/noirc_driver/build.rs +++ b/compiler/noirc_driver/build.rs @@ -1,9 +1,6 @@ const GIT_COMMIT: &&str = &"GIT_COMMIT"; fn main() { - // Rebuild if the tests have changed - println!("cargo:rerun-if-changed=tests"); - // Only use build_data if the environment variable isn't set // The environment variable is always set when working via Nix if std::env::var(GIT_COMMIT).is_err() { diff --git a/compiler/noirc_driver/src/abi_gen.rs b/compiler/noirc_driver/src/abi_gen.rs new file mode 100644 index 00000000000..1cad67ba5b7 --- /dev/null +++ b/compiler/noirc_driver/src/abi_gen.rs @@ -0,0 +1,141 @@ +use std::collections::BTreeMap; + +use acvm::acir::native_types::Witness; +use iter_extended::{btree_map, vecmap}; +use noirc_abi::{Abi, AbiParameter, AbiType}; +use noirc_frontend::{ + hir::Context, + hir_def::{function::Param, stmt::HirPattern}, + node_interner::{FuncId, NodeInterner}, +}; +use std::ops::Range; + +/// Arranges a function signature and a generated circuit's return witnesses into a +/// `noirc_abi::Abi`. +pub(super) fn gen_abi( + context: &Context, + func_id: &FuncId, + input_witnesses: Vec, + return_witnesses: Vec, +) -> Abi { + let (parameters, return_type) = compute_function_abi(context, func_id); + let param_witnesses = param_witnesses_from_abi_param(¶meters, input_witnesses); + Abi { parameters, return_type, param_witnesses, return_witnesses } +} + +pub(super) fn compute_function_abi( + context: &Context, + func_id: &FuncId, +) -> (Vec, Option) { + let func_meta = context.def_interner.function_meta(func_id); + + let (parameters, return_type) = func_meta.into_function_signature(); + let parameters = into_abi_params(context, parameters); + let return_type = return_type.map(|typ| AbiType::from_type(context, &typ)); + (parameters, return_type) +} + +/// Attempts to retrieve the name of this parameter. Returns None +/// if this parameter is a tuple or struct pattern. +fn get_param_name<'a>(pattern: &HirPattern, interner: &'a NodeInterner) -> Option<&'a str> { + match pattern { + HirPattern::Identifier(ident) => Some(interner.definition_name(ident.id)), + HirPattern::Mutable(pattern, _) => get_param_name(pattern, interner), + HirPattern::Tuple(_, _) => None, + HirPattern::Struct(_, _, _) => None, + } +} + +fn into_abi_params(context: &Context, params: Vec) -> Vec { + vecmap(params, |(pattern, typ, vis)| { + let param_name = get_param_name(&pattern, &context.def_interner) + .expect("Abi for tuple and struct parameters is unimplemented") + .to_owned(); + let as_abi = AbiType::from_type(context, &typ); + AbiParameter { name: param_name, typ: as_abi, visibility: vis.into() } + }) +} + +// Takes each abi parameter and shallowly maps to the expected witness range in which the +// parameter's constituent values live. +fn param_witnesses_from_abi_param( + abi_params: &Vec, + input_witnesses: Vec, +) -> BTreeMap>> { + let mut idx = 0_usize; + if input_witnesses.is_empty() { + return BTreeMap::new(); + } + + btree_map(abi_params, |param| { + let num_field_elements_needed = param.typ.field_count() as usize; + let param_witnesses = &input_witnesses[idx..idx + num_field_elements_needed]; + + // It's likely that `param_witnesses` will consist of mostly incrementing witness indices. + // We then want to collapse these into `Range`s to save space. + let param_witnesses = collapse_ranges(param_witnesses); + idx += num_field_elements_needed; + (param.name.clone(), param_witnesses) + }) +} + +/// Takes a vector of [`Witnesses`][`Witness`] and collapses it into a vector of [`Range`]s of [`Witnesses`][`Witness`]. +fn collapse_ranges(witnesses: &[Witness]) -> Vec> { + if witnesses.is_empty() { + return Vec::new(); + } + let mut wit = Vec::new(); + let mut last_wit: Witness = witnesses[0]; + + for (i, witness) in witnesses.iter().enumerate() { + if i == 0 { + continue; + }; + let witness_index = witness.witness_index(); + let prev_witness_index = witnesses[i - 1].witness_index(); + if witness_index != prev_witness_index + 1 { + wit.push(last_wit..Witness(prev_witness_index + 1)); + last_wit = *witness; + }; + } + + let last_witness = witnesses.last().unwrap().witness_index(); + wit.push(last_wit..Witness(last_witness + 1)); + + wit +} + +#[cfg(test)] +mod test { + use std::ops::Range; + + use acvm::acir::native_types::Witness; + + use super::collapse_ranges; + + #[test] + fn collapses_single_range() { + let witnesses: Vec<_> = vec![1, 2, 3].into_iter().map(Witness::from).collect(); + + let collapsed_witnesses = collapse_ranges(&witnesses); + + assert_eq!(collapsed_witnesses, vec![Range { start: Witness(1), end: Witness(4) },]); + } + + #[test] + fn collapse_ranges_correctly() { + let witnesses: Vec<_> = + vec![1, 2, 3, 5, 6, 2, 3, 4].into_iter().map(Witness::from).collect(); + + let collapsed_witnesses = collapse_ranges(&witnesses); + + assert_eq!( + collapsed_witnesses, + vec![ + Range { start: Witness(1), end: Witness(4) }, + Range { start: Witness(5), end: Witness(7) }, + Range { start: Witness(2), end: Witness(5) } + ] + ); + } +} diff --git a/compiler/noirc_driver/src/lib.rs b/compiler/noirc_driver/src/lib.rs index ec7a5091ffd..93ed26fb91a 100644 --- a/compiler/noirc_driver/src/lib.rs +++ b/compiler/noirc_driver/src/lib.rs @@ -8,16 +8,18 @@ use fm::FileId; use iter_extended::vecmap; use noirc_abi::{AbiParameter, AbiType, ContractEvent}; use noirc_errors::{CustomDiagnostic, FileDiagnostic}; +use noirc_evaluator::create_circuit; use noirc_evaluator::errors::RuntimeError; -use noirc_evaluator::{create_circuit, into_abi_params}; use noirc_frontend::graph::{CrateId, CrateName}; use noirc_frontend::hir::def_map::{Contract, CrateDefMap}; use noirc_frontend::hir::Context; +use noirc_frontend::macros_api::MacroProcessor; use noirc_frontend::monomorphization::monomorphize; use noirc_frontend::node_interner::FuncId; use serde::{Deserialize, Serialize}; use std::path::Path; +mod abi_gen; mod contract; mod debug; mod program; @@ -120,8 +122,13 @@ pub fn check_crate( crate_id: CrateId, deny_warnings: bool, ) -> CompilationResult<()> { + #[cfg(not(feature = "aztec"))] + let macros: Vec<&dyn MacroProcessor> = Vec::new(); + #[cfg(feature = "aztec")] + let macros = vec![&aztec_macros::AztecMacro as &dyn MacroProcessor]; + let mut errors = vec![]; - let diagnostics = CrateDefMap::collect_defs(crate_id, context); + let diagnostics = CrateDefMap::collect_defs(crate_id, context, macros); errors.extend(diagnostics.into_iter().map(|(error, file_id)| { let diagnostic: CustomDiagnostic = error.into(); diagnostic.in_file(file_id) @@ -140,12 +147,7 @@ pub fn compute_function_abi( ) -> Option<(Vec, Option)> { let main_function = context.get_main_function(crate_id)?; - let func_meta = context.def_interner.function_meta(&main_function); - - let (parameters, return_type) = func_meta.into_function_signature(); - let parameters = into_abi_params(context, parameters); - let return_type = return_type.map(|typ| AbiType::from_type(context, &typ)); - Some((parameters, return_type)) + Some(abi_gen::compute_function_abi(context, &main_function)) } /// Run the frontend to check the crate for errors then compile the main function if there were none @@ -345,9 +347,10 @@ pub fn compile_no_check( return Ok(cached_program.expect("cache must exist for hashes to match")); } - let (circuit, debug, abi, warnings) = - create_circuit(context, program, options.show_ssa, options.show_brillig)?; + let (circuit, debug, input_witnesses, return_witnesses, warnings) = + create_circuit(program, options.show_ssa, options.show_brillig)?; + let abi = abi_gen::gen_abi(context, &main_function, input_witnesses, return_witnesses); let file_map = filter_relevant_files(&[debug.clone()], &context.file_manager); Ok(CompiledProgram { diff --git a/compiler/noirc_errors/src/debug_info.rs b/compiler/noirc_errors/src/debug_info.rs index 946841c279b..888c24adc1a 100644 --- a/compiler/noirc_errors/src/debug_info.rs +++ b/compiler/noirc_errors/src/debug_info.rs @@ -4,6 +4,7 @@ use acvm::compiler::AcirTransformationMap; use serde_with::serde_as; use serde_with::DisplayFromStr; use std::collections::BTreeMap; +use std::collections::HashMap; use std::mem; use crate::Location; @@ -19,6 +20,14 @@ pub struct DebugInfo { pub locations: BTreeMap>, } +/// Holds OpCodes Counts for Acir and Brillig Opcodes +/// To be printed with `nargo info --profile-info` +#[derive(Default, Debug, Serialize, Deserialize, Clone)] +pub struct OpCodesCount { + pub acir_size: usize, + pub brillig_size: usize, +} + impl DebugInfo { pub fn new(locations: BTreeMap>) -> Self { DebugInfo { locations } @@ -42,4 +51,38 @@ impl DebugInfo { pub fn opcode_location(&self, loc: &OpcodeLocation) -> Option> { self.locations.get(loc).cloned() } + + pub fn count_span_opcodes(&self) -> HashMap { + let mut accumulator: HashMap> = HashMap::new(); + + for (opcode_location, locations) in self.locations.iter() { + for location in locations.iter() { + let opcodes = accumulator.entry(*location).or_insert(Vec::new()); + opcodes.push(opcode_location); + } + } + + let counted_opcodes = accumulator + .iter() + .map(|(location, opcodes)| { + let acir_opcodes: Vec<_> = opcodes + .iter() + .filter(|opcode_location| matches!(opcode_location, OpcodeLocation::Acir(_))) + .collect(); + let brillig_opcodes: Vec<_> = opcodes + .iter() + .filter(|opcode_location| { + matches!(opcode_location, OpcodeLocation::Brillig { .. }) + }) + .collect(); + let opcodes_count = OpCodesCount { + acir_size: acir_opcodes.len(), + brillig_size: brillig_opcodes.len(), + }; + (*location, opcodes_count) + }) + .collect(); + + counted_opcodes + } } diff --git a/compiler/noirc_errors/src/position.rs b/compiler/noirc_errors/src/position.rs index e308eb9a2c7..24b5c4d5ff0 100644 --- a/compiler/noirc_errors/src/position.rs +++ b/compiler/noirc_errors/src/position.rs @@ -65,6 +65,10 @@ impl Span { Span::inclusive(start, start) } + pub fn empty(position: u32) -> Span { + Span::from(position..position) + } + #[must_use] pub fn merge(self, other: Span) -> Span { Span(self.0.merge(other.0)) diff --git a/compiler/noirc_evaluator/Cargo.toml b/compiler/noirc_evaluator/Cargo.toml index c9f5f28478b..933ec2b300c 100644 --- a/compiler/noirc_evaluator/Cargo.toml +++ b/compiler/noirc_evaluator/Cargo.toml @@ -10,7 +10,6 @@ license.workspace = true [dependencies] noirc_frontend.workspace = true noirc_errors.workspace = true -noirc_abi.workspace = true acvm.workspace = true fxhash.workspace = true iter-extended.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 0d97dd12601..9979bf0cd29 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 @@ -1,9 +1,9 @@ -use acvm::acir::{ - brillig::{BlackBoxOp, HeapVector, RegisterOrMemory}, - BlackBoxFunc, -}; +use acvm::acir::{brillig::BlackBoxOp, BlackBoxFunc}; -use crate::brillig::brillig_ir::BrilligContext; +use crate::brillig::brillig_ir::{ + brillig_variable::{BrilligVariable, BrilligVector}, + BrilligContext, +}; /// Transforms SSA's black box function calls into the corresponding brillig instructions /// Extracting arguments and results from the SSA function call @@ -11,31 +11,31 @@ use crate::brillig::brillig_ir::BrilligContext; pub(crate) fn convert_black_box_call( brillig_context: &mut BrilligContext, bb_func: &BlackBoxFunc, - function_arguments: &[RegisterOrMemory], - function_results: &[RegisterOrMemory], + function_arguments: &[BrilligVariable], + function_results: &[BrilligVariable], ) { match bb_func { BlackBoxFunc::SHA256 => { - if let ([message], [RegisterOrMemory::HeapArray(result_array)]) = + if let ([message], [BrilligVariable::BrilligArray(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::Sha256 { - message: message_vector, - output: *result_array, + message: message_vector.to_heap_vector(), + output: result_array.to_heap_array(), }); } else { unreachable!("ICE: SHA256 expects one array argument and one array result") } } BlackBoxFunc::Blake2s => { - if let ([message], [RegisterOrMemory::HeapArray(result_array)]) = + if let ([message], [BrilligVariable::BrilligArray(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::Blake2s { - message: message_vector, - output: *result_array, + message: message_vector.to_heap_vector(), + output: result_array.to_heap_array(), }); } else { unreachable!("ICE: Blake2s expects one array argument and one array result") @@ -43,28 +43,28 @@ pub(crate) fn convert_black_box_call( } BlackBoxFunc::Keccak256 => { if let ( - [message, RegisterOrMemory::RegisterIndex(array_size)], - [RegisterOrMemory::HeapArray(result_array)], + [message, BrilligVariable::Simple(array_size)], + [BrilligVariable::BrilligArray(result_array)], ) = (function_arguments, function_results) { let mut message_vector = convert_array_or_vector(brillig_context, message, bb_func); message_vector.size = *array_size; brillig_context.black_box_op_instruction(BlackBoxOp::Keccak256 { - message: message_vector, - output: *result_array, + message: message_vector.to_heap_vector(), + output: result_array.to_heap_array(), }); } else { unreachable!("ICE: Keccak256 expects message, message size and result array") } } BlackBoxFunc::HashToField128Security => { - if let ([message], [RegisterOrMemory::RegisterIndex(result_register)]) = + if let ([message], [BrilligVariable::Simple(result_register)]) = (function_arguments, function_results) { let message_vector = convert_array_or_vector(brillig_context, message, bb_func); brillig_context.black_box_op_instruction(BlackBoxOp::HashToField128Security { - message: message_vector, + message: message_vector.to_heap_vector(), output: *result_register, }); } else { @@ -73,17 +73,17 @@ pub(crate) fn convert_black_box_call( } BlackBoxFunc::EcdsaSecp256k1 => { if let ( - [RegisterOrMemory::HeapArray(public_key_x), RegisterOrMemory::HeapArray(public_key_y), RegisterOrMemory::HeapArray(signature), message], - [RegisterOrMemory::RegisterIndex(result_register)], + [BrilligVariable::BrilligArray(public_key_x), BrilligVariable::BrilligArray(public_key_y), BrilligVariable::BrilligArray(signature), message], + [BrilligVariable::Simple(result_register)], ) = (function_arguments, function_results) { let message_hash_vector = convert_array_or_vector(brillig_context, message, bb_func); brillig_context.black_box_op_instruction(BlackBoxOp::EcdsaSecp256k1 { - hashed_msg: message_hash_vector, - public_key_x: *public_key_x, - public_key_y: *public_key_y, - signature: *signature, + hashed_msg: message_hash_vector.to_heap_vector(), + public_key_x: public_key_x.to_heap_array(), + public_key_y: public_key_y.to_heap_array(), + signature: signature.to_heap_array(), result: *result_register, }); } else { @@ -94,15 +94,15 @@ pub(crate) fn convert_black_box_call( } BlackBoxFunc::PedersenCommitment => { if let ( - [message, RegisterOrMemory::RegisterIndex(domain_separator)], - [RegisterOrMemory::HeapArray(result_array)], + [message, BrilligVariable::Simple(domain_separator)], + [BrilligVariable::BrilligArray(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::PedersenCommitment { - inputs: message_vector, + inputs: message_vector.to_heap_vector(), domain_separator: *domain_separator, - output: *result_array, + output: result_array.to_heap_array(), }); } else { unreachable!("ICE: Pedersen expects one array argument, a register for the domain separator, and one array result") @@ -110,13 +110,13 @@ pub(crate) fn convert_black_box_call( } BlackBoxFunc::PedersenHash => { if let ( - [message, RegisterOrMemory::RegisterIndex(domain_separator)], - [RegisterOrMemory::RegisterIndex(result)], + [message, BrilligVariable::Simple(domain_separator)], + [BrilligVariable::Simple(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, + inputs: message_vector.to_heap_vector(), domain_separator: *domain_separator, output: *result, }); @@ -126,8 +126,8 @@ pub(crate) fn convert_black_box_call( } BlackBoxFunc::SchnorrVerify => { if let ( - [RegisterOrMemory::RegisterIndex(public_key_x), RegisterOrMemory::RegisterIndex(public_key_y), RegisterOrMemory::HeapArray(signature), message], - [RegisterOrMemory::RegisterIndex(result_register)], + [BrilligVariable::Simple(public_key_x), BrilligVariable::Simple(public_key_y), BrilligVariable::BrilligArray(signature), message], + [BrilligVariable::Simple(result_register)], ) = (function_arguments, function_results) { let message_hash = convert_array_or_vector(brillig_context, message, bb_func); @@ -135,8 +135,8 @@ pub(crate) fn convert_black_box_call( brillig_context.black_box_op_instruction(BlackBoxOp::SchnorrVerify { public_key_x: *public_key_x, public_key_y: *public_key_y, - message: message_hash, - signature, + message: message_hash.to_heap_vector(), + signature: signature.to_heap_vector(), result: *result_register, }); } else { @@ -145,14 +145,14 @@ pub(crate) fn convert_black_box_call( } BlackBoxFunc::FixedBaseScalarMul => { if let ( - [RegisterOrMemory::RegisterIndex(low), RegisterOrMemory::RegisterIndex(high)], - [RegisterOrMemory::HeapArray(result_array)], + [BrilligVariable::Simple(low), BrilligVariable::Simple(high)], + [BrilligVariable::BrilligArray(result_array)], ) = (function_arguments, function_results) { brillig_context.black_box_op_instruction(BlackBoxOp::FixedBaseScalarMul { low: *low, high: *high, - result: *result_array, + result: result_array.to_heap_array(), }); } else { unreachable!( @@ -166,12 +166,12 @@ pub(crate) fn convert_black_box_call( fn convert_array_or_vector( brillig_context: &mut BrilligContext, - array_or_vector: &RegisterOrMemory, + array_or_vector: &BrilligVariable, bb_func: &BlackBoxFunc, -) -> HeapVector { +) -> BrilligVector { match array_or_vector { - RegisterOrMemory::HeapArray(array) => brillig_context.array_to_vector(array), - RegisterOrMemory::HeapVector(vector) => *vector, + BrilligVariable::BrilligArray(array) => brillig_context.array_to_vector(array), + BrilligVariable::BrilligVector(vector) => *vector, _ => unreachable!( "ICE: {} expected an array or a vector, but got {:?}", bb_func.name(), diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index 18fd822b07d..0e06a36fd94 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -1,6 +1,6 @@ +use crate::brillig::brillig_ir::brillig_variable::{BrilligArray, BrilligVariable, BrilligVector}; use crate::brillig::brillig_ir::{ - extract_heap_array, extract_register, extract_registers, BrilligBinaryOp, BrilligContext, - BRILLIG_INTEGER_ARITHMETIC_BIT_SIZE, + BrilligBinaryOp, BrilligContext, BRILLIG_INTEGER_ARITHMETIC_BIT_SIZE, }; use crate::ssa::ir::dfg::CallStack; use crate::ssa::ir::{ @@ -13,7 +13,7 @@ use crate::ssa::ir::{ types::{NumericType, Type}, value::{Value, ValueId}, }; -use acvm::acir::brillig::{BinaryFieldOp, BinaryIntOp, HeapArray, RegisterIndex, RegisterOrMemory}; +use acvm::acir::brillig::{BinaryFieldOp, BinaryIntOp, RegisterIndex, RegisterOrMemory}; use acvm::brillig_vm::brillig::HeapVector; use acvm::FieldElement; use fxhash::{FxHashMap as HashMap, FxHashSet as HashSet}; @@ -53,7 +53,7 @@ impl<'block> BrilligBlock<'block> { variables .get_available_variables(function_context) .into_iter() - .flat_map(extract_registers) + .flat_map(|variable| variable.extract_registers()) .collect(), ); let last_uses = function_context.liveness.get_last_uses(&block_id).clone(); @@ -159,7 +159,7 @@ impl<'block> BrilligBlock<'block> { .iter() .flat_map(|value_id| { let return_variable = self.convert_ssa_value(*value_id, dfg); - extract_registers(return_variable) + return_variable.extract_registers() }) .collect(); self.brillig_context.return_instruction(&return_registers); @@ -168,32 +168,44 @@ impl<'block> BrilligBlock<'block> { } /// Passes an arbitrary variable from the registers of the source to the registers of the destination - fn pass_variable(&mut self, source: RegisterOrMemory, destination: RegisterOrMemory) { + fn pass_variable(&mut self, source: BrilligVariable, destination: BrilligVariable) { match (source, destination) { ( - RegisterOrMemory::RegisterIndex(source_register), - RegisterOrMemory::RegisterIndex(destination_register), + BrilligVariable::Simple(source_register), + BrilligVariable::Simple(destination_register), ) => { self.brillig_context.mov_instruction(destination_register, source_register); } ( - RegisterOrMemory::HeapArray(HeapArray { pointer: source_pointer, .. }), - RegisterOrMemory::HeapArray(HeapArray { pointer: destination_pointer, .. }), + BrilligVariable::BrilligArray(BrilligArray { + pointer: source_pointer, + size: _, + rc: source_rc, + }), + BrilligVariable::BrilligArray(BrilligArray { + pointer: destination_pointer, + size: _, + rc: destination_rc, + }), ) => { self.brillig_context.mov_instruction(destination_pointer, source_pointer); + self.brillig_context.mov_instruction(destination_rc, source_rc); } ( - RegisterOrMemory::HeapVector(HeapVector { + BrilligVariable::BrilligVector(BrilligVector { pointer: source_pointer, size: source_size, + rc: source_rc, }), - RegisterOrMemory::HeapVector(HeapVector { + BrilligVariable::BrilligVector(BrilligVector { pointer: destination_pointer, size: destination_size, + rc: destination_rc, }), ) => { self.brillig_context.mov_instruction(destination_pointer, source_pointer); self.brillig_context.mov_instruction(destination_size, source_size); + self.brillig_context.mov_instruction(destination_rc, source_rc); } (_, _) => { unreachable!("ICE: Cannot pass value from {:?} to {:?}", source, destination); @@ -214,7 +226,7 @@ impl<'block> BrilligBlock<'block> { // In the case of arrays, the values should already be in memory and the register should // Be a valid pointer to the array. // For slices, two registers are passed, the pointer to the data and a register holding the size of the slice. - Type::Numeric(_) | Type::Array(..) | Type::Slice(..) | Type::Reference => { + Type::Numeric(_) | Type::Array(..) | Type::Slice(..) | Type::Reference(_) => { self.variables.get_block_param( self.function_context, self.block_id, @@ -264,7 +276,25 @@ impl<'block> BrilligBlock<'block> { result_value, dfg, ); - self.brillig_context.allocate_variable_instruction(address_register); + match dfg.type_of_value(result_value) { + Type::Reference(element) => match *element { + Type::Array(..) => { + self.brillig_context + .allocate_array_reference_instruction(address_register); + } + Type::Slice(..) => { + self.brillig_context + .allocate_vector_reference_instruction(address_register); + } + _ => { + self.brillig_context + .allocate_simple_reference_instruction(address_register); + } + }, + _ => { + unreachable!("ICE: Allocate on non-reference type") + } + } } Instruction::Store { address, value } => { let address_register = self.convert_ssa_register_value(*address, dfg); @@ -299,10 +329,11 @@ impl<'block> BrilligBlock<'block> { Value::ForeignFunction(func_name) => { let result_ids = dfg.instruction_results(instruction_id); - let input_registers = - vecmap(arguments, |value_id| self.convert_ssa_value(*value_id, dfg)); + let input_registers = vecmap(arguments, |value_id| { + self.convert_ssa_value(*value_id, dfg).to_register_or_memory() + }); let output_registers = vecmap(result_ids, |value_id| { - self.allocate_external_call_result(*value_id, dfg) + self.allocate_external_call_result(*value_id, dfg).to_register_or_memory() }); self.brillig_context.foreign_call_instruction( func_name.to_owned(), @@ -388,7 +419,7 @@ impl<'block> BrilligBlock<'block> { // or an array in the case of an array. if let Type::Numeric(_) = dfg.type_of_value(param_id) { let len_variable = self.convert_ssa_value(arguments[0], dfg); - let len_register_index = extract_register(len_variable); + let len_register_index = len_variable.extract_register(); self.brillig_context.mov_instruction(result_register, len_register_index); } else { self.convert_ssa_array_len(arguments[0], result_register, dfg); @@ -416,29 +447,29 @@ impl<'block> BrilligBlock<'block> { let results = dfg.instruction_results(instruction_id); - let target_len_variable = self.variables.define_variable( + let target_len = self.variables.define_register_variable( self.function_context, self.brillig_context, results[0], dfg, ); - let target_len = extract_register(target_len_variable); - let target_slice = self.variables.define_variable( - self.function_context, - self.brillig_context, - results[1], - dfg, - ); - - let heap_vec = self.brillig_context.extract_heap_vector(target_slice); + let target_vector = self + .variables + .define_variable( + self.function_context, + self.brillig_context, + results[1], + dfg, + ) + .extract_vector(); // Update the user-facing slice length self.brillig_context.mov_instruction(target_len, limb_count); self.brillig_context.radix_instruction( source, - heap_vec, + target_vector, radix, limb_count, matches!(endianness, Endian::Big), @@ -456,24 +487,29 @@ impl<'block> BrilligBlock<'block> { results[0], dfg, ); - let target_len = extract_register(target_len_variable); + let target_len = target_len_variable.extract_register(); - let target_slice = self.variables.define_variable( + let target_vector = match self.variables.define_variable( self.function_context, self.brillig_context, results[1], dfg, - ); + ) { + BrilligVariable::BrilligArray(array) => { + self.brillig_context.array_to_vector(&array) + } + BrilligVariable::BrilligVector(vector) => vector, + BrilligVariable::Simple(..) => unreachable!("ICE: ToBits on non-array"), + }; let radix = self.brillig_context.make_constant(2_usize.into()); - let heap_vec = self.brillig_context.extract_heap_vector(target_slice); // Update the user-facing slice length self.brillig_context.mov_instruction(target_len, limb_count); self.brillig_context.radix_instruction( source, - heap_vec, + target_vector, radix, limb_count, matches!(endianness, Endian::Big), @@ -523,8 +559,8 @@ impl<'block> BrilligBlock<'block> { let array_variable = self.convert_ssa_value(*array, dfg); let array_pointer = match array_variable { - RegisterOrMemory::HeapArray(HeapArray { pointer, .. }) => pointer, - RegisterOrMemory::HeapVector(HeapVector { pointer, .. }) => pointer, + BrilligVariable::BrilligArray(BrilligArray { pointer, .. }) => pointer, + BrilligVariable::BrilligVector(BrilligVector { pointer, .. }) => pointer, _ => unreachable!("ICE: array get on non-array"), }; @@ -574,6 +610,14 @@ impl<'block> BrilligBlock<'block> { self.brillig_context.deallocate_register(condition); self.brillig_context.deallocate_register(right); } + Instruction::IncrementRc { value } => { + let rc_register = match self.convert_ssa_value(*value, dfg) { + BrilligVariable::BrilligArray(BrilligArray { rc, .. }) + | BrilligVariable::BrilligVector(BrilligVector { rc, .. }) => rc, + _ => unreachable!("ICE: increment rc on non-array"), + }; + self.brillig_context.usize_op_in_place(rc_register, BinaryIntOp::Add, 1); + } _ => todo!("ICE: Instruction not supported {instruction:?}"), }; @@ -598,10 +642,7 @@ impl<'block> BrilligBlock<'block> { // Convert the arguments to registers casting those to the types of the receiving function let argument_registers: Vec = arguments .iter() - .flat_map(|argument_id| { - let variable_to_pass = self.convert_ssa_value(*argument_id, dfg); - extract_registers(variable_to_pass) - }) + .flat_map(|argument_id| self.convert_ssa_value(*argument_id, dfg).extract_registers()) .collect(); let result_ids = dfg.instruction_results(instruction_id); @@ -637,7 +678,7 @@ impl<'block> BrilligBlock<'block> { // Collect the registers that should have been returned let returned_registers: Vec = variables_assigned_to .iter() - .flat_map(|returned_variable| extract_registers(*returned_variable)) + .flat_map(|returned_variable| returned_variable.extract_registers()) .collect(); assert!( @@ -654,17 +695,13 @@ impl<'block> BrilligBlock<'block> { &mut self, array_pointer: RegisterIndex, index_register: RegisterIndex, - destination_variable: RegisterOrMemory, + destination_variable: BrilligVariable, ) { match destination_variable { - RegisterOrMemory::RegisterIndex(destination_register) => { + BrilligVariable::Simple(destination_register) => { self.brillig_context.array_get(array_pointer, index_register, destination_register); } - RegisterOrMemory::HeapArray(HeapArray { pointer, .. }) => { - self.brillig_context.array_get(array_pointer, index_register, pointer); - } - RegisterOrMemory::HeapVector(..) => { - // Vectors are stored as references inside arrays to be able to match SSA indexes + BrilligVariable::BrilligArray(..) | BrilligVariable::BrilligVector(..) => { let reference = self.brillig_context.allocate_register(); self.brillig_context.array_get(array_pointer, index_register, reference); self.brillig_context.load_variable_instruction(destination_variable, reference); @@ -677,25 +714,30 @@ impl<'block> BrilligBlock<'block> { /// With a specific value changed. fn convert_ssa_array_set( &mut self, - source_variable: RegisterOrMemory, - destination_variable: RegisterOrMemory, + source_variable: BrilligVariable, + destination_variable: BrilligVariable, index_register: RegisterIndex, - value_variable: RegisterOrMemory, + value_variable: BrilligVariable, ) { let destination_pointer = match destination_variable { - RegisterOrMemory::HeapArray(HeapArray { pointer, .. }) => pointer, - RegisterOrMemory::HeapVector(HeapVector { pointer, .. }) => pointer, + BrilligVariable::BrilligArray(BrilligArray { pointer, .. }) => pointer, + BrilligVariable::BrilligVector(BrilligVector { pointer, .. }) => pointer, _ => unreachable!("ICE: array set returns non-array"), }; - // First issue a array copy to the destination + let reference_count = match source_variable { + BrilligVariable::BrilligArray(BrilligArray { rc, .. }) + | BrilligVariable::BrilligVector(BrilligVector { rc, .. }) => rc, + _ => unreachable!("ICE: array set on non-array"), + }; + let (source_pointer, source_size_as_register) = match source_variable { - RegisterOrMemory::HeapArray(HeapArray { size, pointer }) => { + BrilligVariable::BrilligArray(BrilligArray { size, pointer, rc: _ }) => { let source_size_register = self.brillig_context.allocate_register(); self.brillig_context.const_instruction(source_size_register, size.into()); (pointer, source_size_register) } - RegisterOrMemory::HeapVector(HeapVector { size, pointer }) => { + BrilligVariable::BrilligVector(BrilligVector { size, pointer, rc: _ }) => { let source_size_register = self.brillig_context.allocate_register(); self.brillig_context.mov_instruction(source_size_register, size); (pointer, source_size_register) @@ -703,51 +745,96 @@ impl<'block> BrilligBlock<'block> { _ => unreachable!("ICE: array set on non-array"), }; - self.brillig_context - .allocate_array_instruction(destination_pointer, source_size_as_register); + let one = self.brillig_context.make_constant(1_usize.into()); + let condition = self.brillig_context.allocate_register(); - self.brillig_context.copy_array_instruction( - source_pointer, - destination_pointer, - source_size_as_register, + self.brillig_context.binary_instruction( + reference_count, + one, + condition, + BrilligBinaryOp::Field { op: BinaryFieldOp::Equals }, ); - if let RegisterOrMemory::HeapVector(HeapVector { size: target_size, .. }) = - destination_variable - { - self.brillig_context.mov_instruction(target_size, source_size_as_register); + self.brillig_context.branch_instruction(condition, |ctx, cond| { + if cond { + // Reference count is 1, we can mutate the array directly + ctx.mov_instruction(destination_pointer, source_pointer); + } else { + // First issue a array copy to the destination + ctx.allocate_array_instruction(destination_pointer, source_size_as_register); + + ctx.copy_array_instruction( + source_pointer, + destination_pointer, + source_size_as_register, + ); + } + }); + + match destination_variable { + BrilligVariable::BrilligArray(BrilligArray { rc: target_rc, .. }) => { + self.brillig_context.const_instruction(target_rc, 1_usize.into()); + } + BrilligVariable::BrilligVector(BrilligVector { + size: target_size, + rc: target_rc, + .. + }) => { + self.brillig_context.mov_instruction(target_size, source_size_as_register); + self.brillig_context.const_instruction(target_rc, 1_usize.into()); + } + _ => unreachable!("ICE: array set on non-array"), } // Then set the value in the newly created array self.store_variable_in_array(destination_pointer, index_register, value_variable); self.brillig_context.deallocate_register(source_size_as_register); + self.brillig_context.deallocate_register(one); + self.brillig_context.deallocate_register(condition); } - pub(crate) fn store_variable_in_array( - &mut self, + pub(crate) fn store_variable_in_array_with_ctx( + ctx: &mut BrilligContext, destination_pointer: RegisterIndex, index_register: RegisterIndex, - value_variable: RegisterOrMemory, + value_variable: BrilligVariable, ) { match value_variable { - RegisterOrMemory::RegisterIndex(value_register) => { - self.brillig_context.array_set(destination_pointer, index_register, value_register); + BrilligVariable::Simple(value_register) => { + ctx.array_set(destination_pointer, index_register, value_register); } - RegisterOrMemory::HeapArray(HeapArray { pointer, .. }) => { - self.brillig_context.array_set(destination_pointer, index_register, pointer); + BrilligVariable::BrilligArray(_) => { + let reference: RegisterIndex = ctx.allocate_register(); + ctx.allocate_array_reference_instruction(reference); + ctx.store_variable_instruction(reference, value_variable); + ctx.array_set(destination_pointer, index_register, reference); + ctx.deallocate_register(reference); } - RegisterOrMemory::HeapVector(_) => { - // Vectors are stored as references inside arrays to be able to match SSA indexes - let reference = self.brillig_context.allocate_register(); - self.brillig_context.allocate_variable_instruction(reference); - self.brillig_context.store_variable_instruction(reference, value_variable); - self.brillig_context.array_set(destination_pointer, index_register, reference); - self.brillig_context.deallocate_register(reference); + BrilligVariable::BrilligVector(_) => { + let reference = ctx.allocate_register(); + ctx.allocate_vector_reference_instruction(reference); + ctx.store_variable_instruction(reference, value_variable); + ctx.array_set(destination_pointer, index_register, reference); + ctx.deallocate_register(reference); } } } + pub(crate) fn store_variable_in_array( + &mut self, + destination_pointer: RegisterIndex, + index_register: RegisterIndex, + value_variable: BrilligVariable, + ) { + Self::store_variable_in_array_with_ctx( + self.brillig_context, + destination_pointer, + index_register, + value_variable, + ); + } + /// Convert the SSA slice operations to brillig slice operations fn convert_ssa_slice_intrinsic_call( &mut self, @@ -770,7 +857,7 @@ impl<'block> BrilligBlock<'block> { results[0], dfg, ) { - RegisterOrMemory::RegisterIndex(register_index) => register_index, + BrilligVariable::Simple(register_index) => register_index, _ => unreachable!("ICE: first value of a slice must be a register index"), }; @@ -781,7 +868,7 @@ impl<'block> BrilligBlock<'block> { dfg, ); - let target_vector = self.brillig_context.extract_heap_vector(target_variable); + let target_vector = target_variable.extract_vector(); let item_values = vecmap(&arguments[2..element_size + 2], |arg| { self.convert_ssa_value(*arg, dfg) }); @@ -797,7 +884,7 @@ impl<'block> BrilligBlock<'block> { results[0], dfg, ) { - RegisterOrMemory::RegisterIndex(register_index) => register_index, + BrilligVariable::Simple(register_index) => register_index, _ => unreachable!("ICE: first value of a slice must be a register index"), }; @@ -807,7 +894,7 @@ impl<'block> BrilligBlock<'block> { results[1], dfg, ); - let target_vector = self.brillig_context.extract_heap_vector(target_variable); + let target_vector = target_variable.extract_vector(); let item_values = vecmap(&arguments[2..element_size + 2], |arg| { self.convert_ssa_value(*arg, dfg) }); @@ -823,7 +910,7 @@ impl<'block> BrilligBlock<'block> { results[0], dfg, ) { - RegisterOrMemory::RegisterIndex(register_index) => register_index, + BrilligVariable::Simple(register_index) => register_index, _ => unreachable!("ICE: first value of a slice must be a register index"), }; @@ -834,7 +921,7 @@ impl<'block> BrilligBlock<'block> { dfg, ); - let target_vector = self.brillig_context.extract_heap_vector(target_variable); + let target_vector = target_variable.extract_vector(); let pop_variables = vecmap(&results[2..element_size + 2], |result| { self.variables.define_variable( @@ -856,7 +943,7 @@ impl<'block> BrilligBlock<'block> { results[element_size], dfg, ) { - RegisterOrMemory::RegisterIndex(register_index) => register_index, + BrilligVariable::Simple(register_index) => register_index, _ => unreachable!("ICE: first value of a slice must be a register index"), }; @@ -875,7 +962,7 @@ impl<'block> BrilligBlock<'block> { results[element_size + 1], dfg, ); - let target_vector = self.brillig_context.extract_heap_vector(target_variable); + let target_vector = target_variable.extract_vector(); self.update_slice_length(target_len, arguments[0], dfg, BinaryIntOp::Sub); @@ -888,7 +975,7 @@ impl<'block> BrilligBlock<'block> { results[0], dfg, ) { - RegisterOrMemory::RegisterIndex(register_index) => register_index, + BrilligVariable::Simple(register_index) => register_index, _ => unreachable!("ICE: first value of a slice must be a register index"), }; @@ -900,7 +987,7 @@ impl<'block> BrilligBlock<'block> { dfg, ); - let target_vector = self.brillig_context.extract_heap_vector(target_variable); + let target_vector = target_variable.extract_vector(); // Remove if indexing in insert is changed to flattened indexing // https://github.com/noir-lang/noir/issues/1889#issuecomment-1668048587 @@ -931,7 +1018,7 @@ impl<'block> BrilligBlock<'block> { results[0], dfg, ) { - RegisterOrMemory::RegisterIndex(register_index) => register_index, + BrilligVariable::Simple(register_index) => register_index, _ => unreachable!("ICE: first value of a slice must be a register index"), }; @@ -943,7 +1030,7 @@ impl<'block> BrilligBlock<'block> { target_id, dfg, ); - let target_vector = self.brillig_context.extract_heap_vector(target_variable); + let target_vector = target_variable.extract_vector(); // Remove if indexing in remove is changed to flattened indexing // https://github.com/noir-lang/noir/issues/1889#issuecomment-1668048587 @@ -998,7 +1085,7 @@ impl<'block> BrilligBlock<'block> { binary_op: BinaryIntOp, ) { let source_len_variable = self.convert_ssa_value(source_value, dfg); - let source_len = extract_register(source_len_variable); + let source_len = source_len_variable.extract_register(); self.brillig_context.usize_op(source_len, target_len, binary_op, 1); } @@ -1064,7 +1151,7 @@ impl<'block> BrilligBlock<'block> { } /// Converts an SSA `ValueId` into a `RegisterOrMemory`. Initializes if necessary. - fn convert_ssa_value(&mut self, value_id: ValueId, dfg: &DataFlowGraph) -> RegisterOrMemory { + fn convert_ssa_value(&mut self, value_id: ValueId, dfg: &DataFlowGraph) -> BrilligVariable { let value_id = dfg.resolve(value_id); let value = &dfg[value_id]; @@ -1082,7 +1169,7 @@ impl<'block> BrilligBlock<'block> { } else { let new_variable = self.variables.allocate_constant(self.brillig_context, value_id, dfg); - let register_index = extract_register(new_variable); + let register_index = new_variable.extract_register(); self.brillig_context.const_instruction(register_index, (*constant).into()); new_variable @@ -1097,19 +1184,21 @@ impl<'block> BrilligBlock<'block> { // Initialize the variable let pointer = match new_variable { - RegisterOrMemory::HeapArray(heap_array) => { + BrilligVariable::BrilligArray(brillig_array) => { self.brillig_context - .allocate_fixed_length_array(heap_array.pointer, array.len()); + .allocate_fixed_length_array(brillig_array.pointer, array.len()); + self.brillig_context + .const_instruction(brillig_array.rc, 1_usize.into()); - heap_array.pointer + brillig_array.pointer } - RegisterOrMemory::HeapVector(heap_vector) => { - self.brillig_context - .const_instruction(heap_vector.size, array.len().into()); + BrilligVariable::BrilligVector(vector) => { + self.brillig_context.const_instruction(vector.size, array.len().into()); self.brillig_context - .allocate_array_instruction(heap_vector.pointer, heap_vector.size); + .allocate_array_instruction(vector.pointer, vector.size); + self.brillig_context.const_instruction(vector.rc, 1_usize.into()); - heap_vector.pointer + vector.pointer } _ => unreachable!( "ICE: Cannot initialize array value created as {new_variable:?}" @@ -1138,7 +1227,7 @@ impl<'block> BrilligBlock<'block> { new_variable } } - _ => { + Value::Function(_) | Value::Intrinsic(_) | Value::ForeignFunction(_) => { todo!("ICE: Cannot convert value {value:?}") } } @@ -1151,14 +1240,14 @@ impl<'block> BrilligBlock<'block> { dfg: &DataFlowGraph, ) -> RegisterIndex { let variable = self.convert_ssa_value(value_id, dfg); - extract_register(variable) + variable.extract_register() } fn allocate_external_call_result( &mut self, result: ValueId, dfg: &DataFlowGraph, - ) -> RegisterOrMemory { + ) -> BrilligVariable { let typ = dfg[result].get_type(); match typ { Type::Numeric(_) => self.variables.define_variable( @@ -1175,8 +1264,10 @@ impl<'block> BrilligBlock<'block> { result, dfg, ); - let array = extract_heap_array(variable); + let array = variable.extract_array(); self.brillig_context.allocate_fixed_length_array(array.pointer, array.size); + self.brillig_context.const_instruction(array.rc, 1_usize.into()); + variable } Type::Slice(_) => { @@ -1186,12 +1277,14 @@ impl<'block> BrilligBlock<'block> { result, dfg, ); - let vector = self.brillig_context.extract_heap_vector(variable); + let vector = variable.extract_vector(); // Set the pointer to the current stack frame // The stack pointer will then be updated by the caller of this method // once the external call is resolved and the array size is known self.brillig_context.set_array_pointer(vector.pointer); + self.brillig_context.const_instruction(vector.rc, 1_usize.into()); + variable } _ => { @@ -1201,7 +1294,7 @@ impl<'block> BrilligBlock<'block> { } /// Gets the "user-facing" length of an array. - /// An array of structs with two fields would be stored as an 2 * array.len() heap array/heap vector. + /// An array of structs with two fields would be stored as an 2 * array.len() array/vector. /// So we divide the length by the number of subitems in an item to get the user-facing length. fn convert_ssa_array_len( &mut self, @@ -1213,11 +1306,11 @@ impl<'block> BrilligBlock<'block> { let element_size = dfg.type_of_value(array_id).element_size(); match array_variable { - RegisterOrMemory::HeapArray(HeapArray { size, .. }) => { + BrilligVariable::BrilligArray(BrilligArray { size, .. }) => { self.brillig_context .const_instruction(result_register, (size / element_size).into()); } - RegisterOrMemory::HeapVector(HeapVector { size, .. }) => { + BrilligVariable::BrilligVector(BrilligVector { size, .. }) => { self.brillig_context.usize_op( size, result_register, @@ -1240,7 +1333,7 @@ pub(crate) fn type_of_binary_operation(lhs_type: &Type, rhs_type: &Type) -> Type (_, Type::Function) | (Type::Function, _) => { unreachable!("Functions are invalid in binary operations") } - (_, Type::Reference) | (Type::Reference, _) => { + (_, Type::Reference(_)) | (Type::Reference(_), _) => { unreachable!("References are invalid in binary operations") } (_, Type::Array(..)) | (Type::Array(..), _) => { diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block_variables.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block_variables.rs index eb7bab8c971..f2e698c0aa9 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block_variables.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block_variables.rs @@ -1,8 +1,11 @@ -use acvm::brillig_vm::brillig::{HeapArray, HeapVector, RegisterIndex, RegisterOrMemory}; +use acvm::brillig_vm::brillig::RegisterIndex; use fxhash::{FxHashMap as HashMap, FxHashSet as HashSet}; use crate::{ - brillig::brillig_ir::{extract_register, BrilligContext}, + brillig::brillig_ir::{ + brillig_variable::{BrilligArray, BrilligVariable, BrilligVector}, + BrilligContext, + }, ssa::ir::{ basic_block::BasicBlockId, dfg::DataFlowGraph, @@ -16,7 +19,7 @@ use super::brillig_fn::FunctionContext; #[derive(Debug, Default)] pub(crate) struct BlockVariables { available_variables: HashSet, - available_constants: HashMap, + available_constants: HashMap, } impl BlockVariables { @@ -32,7 +35,7 @@ impl BlockVariables { pub(crate) fn get_available_variables( &self, function_context: &FunctionContext, - ) -> Vec { + ) -> Vec { self.available_variables .iter() .map(|value_id| { @@ -52,7 +55,7 @@ impl BlockVariables { brillig_context: &mut BrilligContext, value_id: ValueId, dfg: &DataFlowGraph, - ) -> RegisterOrMemory { + ) -> BrilligVariable { let value_id = dfg.resolve(value_id); let variable = allocate_value(value_id, brillig_context, dfg); @@ -74,7 +77,7 @@ impl BlockVariables { dfg: &DataFlowGraph, ) -> RegisterIndex { let variable = self.define_variable(function_context, brillig_context, value, dfg); - extract_register(variable) + variable.extract_register() } /// Removes a variable so it's not used anymore within this block. @@ -88,7 +91,7 @@ impl BlockVariables { function_context: &FunctionContext, value_id: ValueId, dfg: &DataFlowGraph, - ) -> RegisterOrMemory { + ) -> BrilligVariable { let value_id = dfg.resolve(value_id); if let Some(constant) = self.available_constants.get(&value_id) { *constant @@ -112,7 +115,7 @@ impl BlockVariables { brillig_context: &mut BrilligContext, value_id: ValueId, dfg: &DataFlowGraph, - ) -> RegisterOrMemory { + ) -> BrilligVariable { let value_id = dfg.resolve(value_id); let constant = allocate_value(value_id, brillig_context, dfg); self.available_constants.insert(value_id, constant); @@ -124,7 +127,7 @@ impl BlockVariables { &mut self, value_id: ValueId, dfg: &DataFlowGraph, - ) -> Option { + ) -> Option { let value_id = dfg.resolve(value_id); self.available_constants.get(&value_id).cloned() } @@ -141,7 +144,7 @@ impl BlockVariables { block_id: BasicBlockId, value_id: ValueId, dfg: &DataFlowGraph, - ) -> RegisterOrMemory { + ) -> BrilligVariable { let value_id = dfg.resolve(value_id); assert!( function_context @@ -166,25 +169,34 @@ pub(crate) fn allocate_value( value_id: ValueId, brillig_context: &mut BrilligContext, dfg: &DataFlowGraph, -) -> RegisterOrMemory { +) -> BrilligVariable { let typ = dfg.type_of_value(value_id); match typ { - Type::Numeric(_) | Type::Reference => { + Type::Numeric(_) | Type::Reference(_) => { let register = brillig_context.allocate_register(); - RegisterOrMemory::RegisterIndex(register) + BrilligVariable::Simple(register) } Type::Array(item_typ, elem_count) => { let pointer_register = brillig_context.allocate_register(); + let rc_register = brillig_context.allocate_register(); let size = compute_array_length(&item_typ, elem_count); - RegisterOrMemory::HeapArray(HeapArray { pointer: pointer_register, size }) + + BrilligVariable::BrilligArray(BrilligArray { + pointer: pointer_register, + size, + rc: rc_register, + }) } Type::Slice(_) => { let pointer_register = brillig_context.allocate_register(); let size_register = brillig_context.allocate_register(); - RegisterOrMemory::HeapVector(HeapVector { + let rc_register = brillig_context.allocate_register(); + + BrilligVariable::BrilligVector(BrilligVector { pointer: pointer_register, size: size_register, + rc: rc_register, }) } Type::Function => { diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_fn.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_fn.rs index ec72ceb2909..026def4ef11 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_fn.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_fn.rs @@ -1,9 +1,9 @@ -use acvm::brillig_vm::brillig::RegisterOrMemory; use iter_extended::vecmap; use crate::{ brillig::brillig_ir::{ artifact::{BrilligParameter, Label}, + brillig_variable::BrilligVariable, BrilligContext, }, ssa::ir::{ @@ -21,7 +21,7 @@ use super::{brillig_block_variables::allocate_value, variable_liveness::Variable pub(crate) struct FunctionContext { pub(crate) function_id: FunctionId, /// Map from SSA values its allocation. Since values can be only defined once in SSA form, we insert them here on when we allocate them at their definition. - pub(crate) ssa_value_allocations: HashMap, + pub(crate) ssa_value_allocations: HashMap, /// Block parameters are pre allocated at the function level. pub(crate) block_parameters: HashMap>, /// The block ids of the function in reverse post order. @@ -72,7 +72,7 @@ impl FunctionContext { fn ssa_type_to_parameter(typ: &Type) -> BrilligParameter { match typ { - Type::Numeric(_) | Type::Reference => BrilligParameter::Simple, + Type::Numeric(_) | Type::Reference(_) => BrilligParameter::Simple, Type::Array(item_type, size) => BrilligParameter::Array( vecmap(item_type.iter(), |item_typ| { FunctionContext::ssa_type_to_parameter(item_typ) diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs index 211d670e7d8..6402e6f9d97 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs @@ -1,13 +1,15 @@ -use acvm::brillig_vm::brillig::{BinaryIntOp, HeapVector, RegisterIndex, RegisterOrMemory}; +use acvm::brillig_vm::brillig::{BinaryIntOp, RegisterIndex}; + +use crate::brillig::brillig_ir::brillig_variable::{BrilligVariable, BrilligVector}; use super::brillig_block::BrilligBlock; impl<'block> BrilligBlock<'block> { pub(crate) fn slice_push_back_operation( &mut self, - target_vector: HeapVector, - source_vector: HeapVector, - variables_to_insert: &[RegisterOrMemory], + target_vector: BrilligVector, + source_vector: BrilligVector, + variables_to_insert: &[BrilligVariable], ) { // First we need to allocate the target vector incrementing the size by variables_to_insert.len() self.brillig_context.usize_op( @@ -17,6 +19,8 @@ impl<'block> BrilligBlock<'block> { variables_to_insert.len(), ); self.brillig_context.allocate_array_instruction(target_vector.pointer, target_vector.size); + // We initialize the RC of the target vector to 1 + self.brillig_context.const_instruction(target_vector.rc, 1_usize.into()); // Now we copy the source vector into the target vector self.brillig_context.copy_array_instruction( @@ -40,9 +44,9 @@ impl<'block> BrilligBlock<'block> { pub(crate) fn slice_push_front_operation( &mut self, - target_vector: HeapVector, - source_vector: HeapVector, - variables_to_insert: &[RegisterOrMemory], + target_vector: BrilligVector, + source_vector: BrilligVector, + variables_to_insert: &[BrilligVariable], ) { // First we need to allocate the target vector incrementing the size by variables_to_insert.len() self.brillig_context.usize_op( @@ -52,6 +56,8 @@ impl<'block> BrilligBlock<'block> { variables_to_insert.len(), ); self.brillig_context.allocate_array_instruction(target_vector.pointer, target_vector.size); + // We initialize the RC of the target vector to 1 + self.brillig_context.const_instruction(target_vector.rc, 1_usize.into()); // Now we offset the target pointer by variables_to_insert.len() let destination_copy_pointer = self.brillig_context.allocate_register(); @@ -81,9 +87,9 @@ impl<'block> BrilligBlock<'block> { pub(crate) fn slice_pop_front_operation( &mut self, - target_vector: HeapVector, - source_vector: HeapVector, - removed_items: &[RegisterOrMemory], + target_vector: BrilligVector, + source_vector: BrilligVector, + removed_items: &[BrilligVariable], ) { // First we need to allocate the target vector decrementing the size by removed_items.len() self.brillig_context.usize_op( @@ -93,6 +99,8 @@ impl<'block> BrilligBlock<'block> { removed_items.len(), ); self.brillig_context.allocate_array_instruction(target_vector.pointer, target_vector.size); + // We initialize the RC of the target vector to 1 + self.brillig_context.const_instruction(target_vector.rc, 1_usize.into()); // Now we offset the source pointer by removed_items.len() let source_copy_pointer = self.brillig_context.allocate_register(); @@ -121,9 +129,9 @@ impl<'block> BrilligBlock<'block> { pub(crate) fn slice_pop_back_operation( &mut self, - target_vector: HeapVector, - source_vector: HeapVector, - removed_items: &[RegisterOrMemory], + target_vector: BrilligVector, + source_vector: BrilligVector, + removed_items: &[BrilligVariable], ) { // First we need to allocate the target vector decrementing the size by removed_items.len() self.brillig_context.usize_op( @@ -133,6 +141,8 @@ impl<'block> BrilligBlock<'block> { removed_items.len(), ); self.brillig_context.allocate_array_instruction(target_vector.pointer, target_vector.size); + // We initialize the RC of the target vector to 1 + self.brillig_context.const_instruction(target_vector.rc, 1_usize.into()); // Now we copy all elements except the last items into the target vector self.brillig_context.copy_array_instruction( @@ -156,10 +166,10 @@ impl<'block> BrilligBlock<'block> { pub(crate) fn slice_insert_operation( &mut self, - target_vector: HeapVector, - source_vector: HeapVector, + target_vector: BrilligVector, + source_vector: BrilligVector, index: RegisterIndex, - items: &[RegisterOrMemory], + items: &[BrilligVariable], ) { // First we need to allocate the target vector incrementing the size by items.len() self.brillig_context.usize_op( @@ -169,6 +179,8 @@ impl<'block> BrilligBlock<'block> { items.len(), ); self.brillig_context.allocate_array_instruction(target_vector.pointer, target_vector.size); + // We initialize the RC of the target vector to 1 + self.brillig_context.const_instruction(target_vector.rc, 1_usize.into()); // Copy the elements to the left of the index self.brillig_context.copy_array_instruction( @@ -226,10 +238,10 @@ impl<'block> BrilligBlock<'block> { pub(crate) fn slice_remove_operation( &mut self, - target_vector: HeapVector, - source_vector: HeapVector, + target_vector: BrilligVector, + source_vector: BrilligVector, index: RegisterIndex, - removed_items: &[RegisterOrMemory], + removed_items: &[BrilligVariable], ) { // First we need to allocate the target vector decrementing the size by removed_items.len() self.brillig_context.usize_op( @@ -239,6 +251,8 @@ impl<'block> BrilligBlock<'block> { removed_items.len(), ); self.brillig_context.allocate_array_instruction(target_vector.pointer, target_vector.size); + // We initialize the RC of the target vector to 1 + self.brillig_context.const_instruction(target_vector.rc, 1_usize.into()); // Copy the elements to the left of the index self.brillig_context.copy_array_instruction( @@ -297,11 +311,11 @@ impl<'block> BrilligBlock<'block> { pub(crate) fn convert_array_or_vector_to_vector( &mut self, - source_variable: RegisterOrMemory, - ) -> HeapVector { + source_variable: BrilligVariable, + ) -> BrilligVector { match source_variable { - RegisterOrMemory::HeapVector(source_vector) => source_vector, - RegisterOrMemory::HeapArray(source_array) => { + BrilligVariable::BrilligVector(source_vector) => source_vector, + BrilligVariable::BrilligArray(source_array) => { self.brillig_context.array_to_vector(&source_array) } _ => unreachable!("ICE: unsupported slice push back source {:?}", source_variable), @@ -313,13 +327,16 @@ impl<'block> BrilligBlock<'block> { mod tests { use std::vec; - use acvm::acir::brillig::{HeapVector, Value}; - use acvm::brillig_vm::brillig::{RegisterIndex, RegisterOrMemory}; + use acvm::acir::brillig::Value; + use acvm::brillig_vm::brillig::RegisterIndex; use crate::brillig::brillig_gen::brillig_block::BrilligBlock; use crate::brillig::brillig_gen::brillig_block_variables::BlockVariables; use crate::brillig::brillig_gen::brillig_fn::FunctionContext; use crate::brillig::brillig_ir::artifact::BrilligParameter; + use crate::brillig::brillig_ir::brillig_variable::{ + BrilligArray, BrilligVariable, BrilligVector, + }; use crate::brillig::brillig_ir::tests::{ create_and_run_vm, create_context, create_entry_point_bytecode, }; @@ -373,33 +390,44 @@ mod tests { let (_, mut function_context, mut context) = create_test_environment(); // Allocate the parameters - let array_pointer = context.allocate_register(); + let array_variable = BrilligArray { + pointer: context.allocate_register(), + size: array.len(), + rc: context.allocate_register(), + }; let item_to_insert = context.allocate_register(); // Cast the source array to a vector - let array_size = context.make_constant(array.len().into()); + let source_vector = context.array_to_vector(&array_variable); // Allocate the results - let copied_array_pointer = context.allocate_register(); - let copied_array_size = context.allocate_register(); + let target_vector = BrilligVector { + pointer: context.allocate_register(), + size: context.allocate_register(), + rc: context.allocate_register(), + }; let mut block = create_brillig_block(&mut function_context, &mut context); if push_back { block.slice_push_back_operation( - HeapVector { pointer: copied_array_pointer, size: copied_array_size }, - HeapVector { pointer: array_pointer, size: array_size }, - &[RegisterOrMemory::RegisterIndex(item_to_insert)], + target_vector, + source_vector, + &[BrilligVariable::Simple(item_to_insert)], ); } else { block.slice_push_front_operation( - HeapVector { pointer: copied_array_pointer, size: copied_array_size }, - HeapVector { pointer: array_pointer, size: array_size }, - &[RegisterOrMemory::RegisterIndex(item_to_insert)], + target_vector, + source_vector, + &[BrilligVariable::Simple(item_to_insert)], ); } - context.return_instruction(&[copied_array_pointer, copied_array_size]); + context.return_instruction(&[ + target_vector.pointer, + target_vector.rc, + target_vector.size, + ]); let bytecode = create_entry_point_bytecode(context, arguments, returns).byte_code; let vm = create_and_run_vm( @@ -465,34 +493,45 @@ mod tests { let (_, mut function_context, mut context) = create_test_environment(); // Allocate the parameters - let array_pointer = context.allocate_register(); + let array_variable = BrilligArray { + pointer: context.allocate_register(), + size: array.len(), + rc: context.allocate_register(), + }; // Cast the source array to a vector - let array_size = context.make_constant(array.len().into()); + let source_vector = context.array_to_vector(&array_variable); // Allocate the results - let copied_array_pointer = context.allocate_register(); + let target_vector = BrilligVector { + pointer: context.allocate_register(), + size: context.allocate_register(), + rc: context.allocate_register(), + }; let removed_item = context.allocate_register(); - let copied_array_size = context.allocate_register(); - let mut block = create_brillig_block(&mut function_context, &mut context); if pop_back { block.slice_pop_back_operation( - HeapVector { pointer: copied_array_pointer, size: copied_array_size }, - HeapVector { pointer: array_pointer, size: array_size }, - &[RegisterOrMemory::RegisterIndex(removed_item)], + target_vector, + source_vector, + &[BrilligVariable::Simple(removed_item)], ); } else { block.slice_pop_front_operation( - HeapVector { pointer: copied_array_pointer, size: copied_array_size }, - HeapVector { pointer: array_pointer, size: array_size }, - &[RegisterOrMemory::RegisterIndex(removed_item)], + target_vector, + source_vector, + &[BrilligVariable::Simple(removed_item)], ); } - context.return_instruction(&[copied_array_pointer, copied_array_size, removed_item]); + context.return_instruction(&[ + target_vector.pointer, + target_vector.rc, + target_vector.size, + removed_item, + ]); let bytecode = create_entry_point_bytecode(context, arguments, returns).byte_code; let vm = create_and_run_vm(array.clone(), vec![Value::from(0_usize)], &bytecode); @@ -557,28 +596,38 @@ mod tests { let (_, mut function_context, mut context) = create_test_environment(); // Allocate the parameters - let array_pointer = context.allocate_register(); + let array_variable = BrilligArray { + pointer: context.allocate_register(), + size: array.len(), + rc: context.allocate_register(), + }; let item_to_insert = context.allocate_register(); let index_to_insert = context.allocate_register(); // Cast the source array to a vector - let array_size = context.make_constant(array.len().into()); + let source_vector = context.array_to_vector(&array_variable); // Allocate the results - let copied_array_pointer = context.allocate_register(); - - let copied_array_size = context.allocate_register(); + let target_vector = BrilligVector { + pointer: context.allocate_register(), + size: context.allocate_register(), + rc: context.allocate_register(), + }; let mut block = create_brillig_block(&mut function_context, &mut context); block.slice_insert_operation( - HeapVector { pointer: copied_array_pointer, size: copied_array_size }, - HeapVector { pointer: array_pointer, size: array_size }, + target_vector, + source_vector, index_to_insert, - &[RegisterOrMemory::RegisterIndex(item_to_insert)], + &[BrilligVariable::Simple(item_to_insert)], ); - context.return_instruction(&[copied_array_pointer, copied_array_size]); + context.return_instruction(&[ + target_vector.pointer, + target_vector.rc, + target_vector.size, + ]); let bytecode = create_entry_point_bytecode(context, arguments, returns).byte_code; let vm = create_and_run_vm( @@ -679,28 +728,39 @@ mod tests { let (_, mut function_context, mut context) = create_test_environment(); // Allocate the parameters - let array_pointer = context.allocate_register(); + let array_variable = BrilligArray { + pointer: context.allocate_register(), + size: array.len(), + rc: context.allocate_register(), + }; let index_to_insert = context.allocate_register(); // Cast the source array to a vector - let array_size = context.make_constant(array.len().into()); + let source_vector = context.array_to_vector(&array_variable); // Allocate the results - let copied_array_pointer = context.allocate_register(); + let target_vector = BrilligVector { + pointer: context.allocate_register(), + size: context.allocate_register(), + rc: context.allocate_register(), + }; let removed_item = context.allocate_register(); - let copied_array_size = context.allocate_register(); - let mut block = create_brillig_block(&mut function_context, &mut context); block.slice_remove_operation( - HeapVector { pointer: copied_array_pointer, size: copied_array_size }, - HeapVector { pointer: array_pointer, size: array_size }, + target_vector, + source_vector, index_to_insert, - &[RegisterOrMemory::RegisterIndex(removed_item)], + &[BrilligVariable::Simple(removed_item)], ); - context.return_instruction(&[copied_array_pointer, copied_array_size, removed_item]); + context.return_instruction(&[ + target_vector.pointer, + target_vector.rc, + target_vector.size, + removed_item, + ]); let bytecode = create_entry_point_bytecode(context, arguments, returns).byte_code; let vm = create_and_run_vm(array.clone(), vec![Value::from(0_usize), index], &bytecode); diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/variable_liveness.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/variable_liveness.rs index d57196288bf..05978c2c6ab 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/variable_liveness.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/variable_liveness.rs @@ -332,7 +332,7 @@ mod test { let v0 = builder.add_parameter(Type::field()); let v1 = builder.add_parameter(Type::field()); - let v3 = builder.insert_allocate(); + let v3 = builder.insert_allocate(Type::field()); let zero = builder.numeric_constant(0u128, Type::field()); builder.insert_store(v3, zero); @@ -439,7 +439,7 @@ mod test { let v0 = builder.add_parameter(Type::field()); let v1 = builder.add_parameter(Type::field()); - let v3 = builder.insert_allocate(); + let v3 = builder.insert_allocate(Type::field()); let zero = builder.numeric_constant(0u128, Type::field()); builder.insert_store(v3, zero); diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir.rs index 880ae95dcd7..ff182aaa7d2 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir.rs @@ -5,6 +5,7 @@ //! ssa types and types in this module. //! A similar paradigm can be seen with the `acir_ir` module. pub(crate) mod artifact; +pub(crate) mod brillig_variable; pub(crate) mod debug_show; pub(crate) mod registers; @@ -14,12 +15,13 @@ use crate::ssa::ir::dfg::CallStack; use self::{ artifact::{BrilligArtifact, UnresolvedJumpLocation}, + brillig_variable::{BrilligArray, BrilligVariable, BrilligVector}, registers::BrilligRegistersContext, }; use acvm::{ acir::brillig::{ - BinaryFieldOp, BinaryIntOp, BlackBoxOp, HeapArray, HeapVector, Opcode as BrilligOpcode, - RegisterIndex, RegisterOrMemory, Value, + BinaryFieldOp, BinaryIntOp, BlackBoxOp, Opcode as BrilligOpcode, RegisterIndex, + RegisterOrMemory, Value, }, FieldElement, }; @@ -88,6 +90,8 @@ pub(crate) struct BrilligContext { context_label: String, /// Section label, used to separate sections of code section_label: usize, + /// Stores the next available section + next_section: usize, /// IR printer debug_show: DebugShow, } @@ -100,6 +104,7 @@ impl BrilligContext { registers: BrilligRegistersContext::new(), context_label: String::default(), section_label: 0, + next_section: 1, debug_show: DebugShow::new(enable_debug_trace), } } @@ -161,10 +166,14 @@ impl BrilligContext { /// Allocates a variable in memory and stores the /// pointer to the array in `pointer_register` - pub(crate) fn allocate_variable_instruction(&mut self, pointer_register: RegisterIndex) { + fn allocate_variable_reference_instruction( + &mut self, + pointer_register: RegisterIndex, + size: usize, + ) { self.debug_show.allocate_instruction(pointer_register); - // A variable can be stored in up to two values, so we reserve two values for that. - let size_register = self.make_constant(2_u128.into()); + // A variable can be stored in up to three values, so we reserve three values for that. + let size_register = self.make_constant(size.into()); self.push_opcode(BrilligOpcode::Mov { destination: pointer_register, source: ReservedRegisters::stack_pointer(), @@ -177,6 +186,30 @@ impl BrilligContext { ); } + pub(crate) fn allocate_simple_reference_instruction( + &mut self, + pointer_register: RegisterIndex, + ) { + self.allocate_variable_reference_instruction(pointer_register, 1); + } + + pub(crate) fn allocate_array_reference_instruction(&mut self, pointer_register: RegisterIndex) { + self.allocate_variable_reference_instruction( + pointer_register, + BrilligArray::registers_count(), + ); + } + + pub(crate) fn allocate_vector_reference_instruction( + &mut self, + pointer_register: RegisterIndex, + ) { + self.allocate_variable_reference_instruction( + pointer_register, + BrilligVector::registers_count(), + ); + } + /// Gets the value in the array at index `index` and stores it in `result` pub(crate) fn array_get( &mut self, @@ -253,8 +286,8 @@ impl BrilligContext { { let iterator_register = self.make_constant(0_u128.into()); - let loop_label = self.next_section_label(); - self.enter_next_section(); + let (loop_section, loop_label) = self.reserve_next_section_label(); + self.enter_section(loop_section); // Loop body @@ -267,7 +300,7 @@ impl BrilligContext { BinaryIntOp::LessThan, ); - let exit_loop_label = self.next_section_label(); + let (exit_loop_section, exit_loop_label) = self.reserve_next_section_label(); self.not_instruction(iterator_less_than_iterations, 1, iterator_less_than_iterations); self.jump_if_instruction(iterator_less_than_iterations, exit_loop_label); @@ -281,12 +314,41 @@ impl BrilligContext { self.jump_instruction(loop_label); // Exit the loop - self.enter_next_section(); + self.enter_section(exit_loop_section); + // Deallocate our temporary registers self.deallocate_register(iterator_less_than_iterations); self.deallocate_register(iterator_register); } + /// This instruction will issue an if-then branch that will check if the condition is true + /// and if so, perform the instructions given in `f(self, true)` and otherwise perform the + /// instructions given in `f(self, false)`. A boolean is passed instead of two separate + /// functions to allow the given function to mutably alias its environment. + pub(crate) fn branch_instruction( + &mut self, + condition: RegisterIndex, + mut f: impl FnMut(&mut BrilligContext, bool), + ) { + // Reserve 3 sections + let (then_section, then_label) = self.reserve_next_section_label(); + let (otherwise_section, otherwise_label) = self.reserve_next_section_label(); + let (end_section, end_label) = self.reserve_next_section_label(); + + self.jump_if_instruction(condition, then_label.clone()); + self.jump_instruction(otherwise_label.clone()); + + self.enter_section(then_section); + f(self, true); + self.jump_instruction(end_label.clone()); + + self.enter_section(otherwise_section); + f(self, false); + self.jump_instruction(end_label.clone()); + + self.enter_section(end_section); + } + /// Adds a label to the next opcode pub(crate) fn enter_context(&mut self, label: T) { self.debug_show.enter_context(label.to_string()); @@ -299,23 +361,25 @@ impl BrilligContext { .add_label_at_position(self.current_section_label(), self.obj.index_of_next_opcode()); } - /// Increments the section label and adds a section label to the next opcode - fn enter_next_section(&mut self) { - self.section_label += 1; + /// Enter the given section + fn enter_section(&mut self, section: usize) { + self.section_label = section; self.obj .add_label_at_position(self.current_section_label(), self.obj.index_of_next_opcode()); } + /// Create, reserve, and return a new section label. + fn reserve_next_section_label(&mut self) -> (usize, String) { + let section = self.next_section; + self.next_section += 1; + (section, self.compute_section_label(section)) + } + /// Internal function used to compute the section labels fn compute_section_label(&self, section: usize) -> String { format!("{}-{}", self.context_label, section) } - /// Returns the next section label - fn next_section_label(&self) -> String { - self.compute_section_label(self.section_label + 1) - } - /// Returns the current section label fn current_section_label(&self) -> String { self.compute_section_label(self.section_label) @@ -371,15 +435,13 @@ impl BrilligContext { assert_message: Option, ) { self.debug_show.constrain_instruction(condition); - self.add_unresolved_jump( - BrilligOpcode::JumpIf { condition, location: 0 }, - self.next_section_label(), - ); + let (next_section, next_label) = self.reserve_next_section_label(); + self.add_unresolved_jump(BrilligOpcode::JumpIf { condition, location: 0 }, next_label); self.push_opcode(BrilligOpcode::Trap); if let Some(assert_message) = assert_message { self.obj.add_assert_message_to_last_opcode(assert_message); } - self.enter_next_section(); + self.enter_section(next_section); } /// Processes a return instruction. @@ -528,17 +590,24 @@ impl BrilligContext { /// Loads a variable stored previously pub(crate) fn load_variable_instruction( &mut self, - destination: RegisterOrMemory, + destination: BrilligVariable, variable_pointer: RegisterIndex, ) { match destination { - RegisterOrMemory::RegisterIndex(register_index) => { + BrilligVariable::Simple(register_index) => { self.load_instruction(register_index, variable_pointer); } - RegisterOrMemory::HeapArray(HeapArray { pointer, .. }) => { + BrilligVariable::BrilligArray(BrilligArray { pointer, size: _, rc }) => { self.load_instruction(pointer, variable_pointer); + + let rc_pointer = self.allocate_register(); + self.mov_instruction(rc_pointer, variable_pointer); + self.usize_op_in_place(rc_pointer, BinaryIntOp::Add, 1_usize); + + self.load_instruction(rc, rc_pointer); + self.deallocate_register(rc_pointer); } - RegisterOrMemory::HeapVector(HeapVector { pointer, size }) => { + BrilligVariable::BrilligVector(BrilligVector { pointer, size, rc }) => { self.load_instruction(pointer, variable_pointer); let size_pointer = self.allocate_register(); @@ -547,6 +616,13 @@ impl BrilligContext { self.load_instruction(size, size_pointer); self.deallocate_register(size_pointer); + + let rc_pointer = self.allocate_register(); + self.mov_instruction(rc_pointer, variable_pointer); + self.usize_op_in_place(rc_pointer, BinaryIntOp::Add, 2_usize); + + self.load_instruction(rc, rc_pointer); + self.deallocate_register(rc_pointer); } } } @@ -565,32 +641,38 @@ impl BrilligContext { pub(crate) fn store_variable_instruction( &mut self, variable_pointer: RegisterIndex, - source: RegisterOrMemory, + source: BrilligVariable, ) { - let size_pointer = self.allocate_register(); - self.mov_instruction(size_pointer, variable_pointer); - self.usize_op_in_place(size_pointer, BinaryIntOp::Add, 1_usize); - match source { - RegisterOrMemory::RegisterIndex(register_index) => { + BrilligVariable::Simple(register_index) => { self.store_instruction(variable_pointer, register_index); - let size_constant = self.make_constant(Value::from(1_usize)); - self.store_instruction(size_pointer, size_constant); - self.deallocate_register(size_constant); } - RegisterOrMemory::HeapArray(HeapArray { pointer, size }) => { + BrilligVariable::BrilligArray(BrilligArray { pointer, size: _, rc }) => { self.store_instruction(variable_pointer, pointer); - let size_constant = self.make_constant(Value::from(size)); - self.store_instruction(size_pointer, size_constant); - self.deallocate_register(size_constant); + + let rc_pointer: RegisterIndex = self.allocate_register(); + self.mov_instruction(rc_pointer, variable_pointer); + self.usize_op_in_place(rc_pointer, BinaryIntOp::Add, 1_usize); + self.store_instruction(rc_pointer, rc); + self.deallocate_register(rc_pointer); } - RegisterOrMemory::HeapVector(HeapVector { pointer, size }) => { + BrilligVariable::BrilligVector(BrilligVector { pointer, size, rc }) => { self.store_instruction(variable_pointer, pointer); + + let size_pointer = self.allocate_register(); + self.mov_instruction(size_pointer, variable_pointer); + self.usize_op_in_place(size_pointer, BinaryIntOp::Add, 1_usize); self.store_instruction(size_pointer, size); + + let rc_pointer: RegisterIndex = self.allocate_register(); + self.mov_instruction(rc_pointer, variable_pointer); + self.usize_op_in_place(rc_pointer, BinaryIntOp::Add, 2_usize); + self.store_instruction(rc_pointer, rc); + + self.deallocate_register(size_pointer); + self.deallocate_register(rc_pointer); } } - - self.deallocate_register(size_pointer); } /// Emits a truncate instruction. @@ -725,14 +807,14 @@ impl BrilligContext { } /// Saves all of the registers that have been used up until this point. - fn save_registers_of_vars(&mut self, vars: &[RegisterOrMemory]) -> Vec { + fn save_registers_of_vars(&mut self, vars: &[BrilligVariable]) -> Vec { // Save all of the used registers at this point in memory // because the function call will/may overwrite them. // // Note that here it is important that the stack pointer register is at register 0, // as after the first register save we add to the pointer. let mut used_registers: Vec<_> = - vars.iter().flat_map(|var| extract_registers(*var)).collect(); + vars.iter().flat_map(|var| var.extract_registers()).collect(); // Also dump the previous stack pointer used_registers.push(ReservedRegisters::previous_stack_pointer()); @@ -811,7 +893,7 @@ impl BrilligContext { pub(crate) fn pre_call_save_registers_prep_args( &mut self, arguments: &[RegisterIndex], - variables_to_save: &[RegisterOrMemory], + variables_to_save: &[BrilligVariable], ) -> Vec { // Save all the registers we have used to the stack. let saved_registers = self.save_registers_of_vars(variables_to_save); @@ -852,9 +934,9 @@ impl BrilligContext { } /// Utility method to transform a HeapArray to a HeapVector by making a runtime constant with the size. - pub(crate) fn array_to_vector(&mut self, array: &HeapArray) -> HeapVector { + pub(crate) fn array_to_vector(&mut self, array: &BrilligArray) -> BrilligVector { let size_register = self.make_constant(array.size.into()); - HeapVector { size: size_register, pointer: array.pointer } + BrilligVector { size: size_register, pointer: array.pointer, rc: array.rc } } /// Issues a blackbox operation. @@ -868,12 +950,13 @@ impl BrilligContext { pub(crate) fn radix_instruction( &mut self, source: RegisterIndex, - target_vector: HeapVector, + target_vector: BrilligVector, radix: RegisterIndex, limb_count: RegisterIndex, big_endian: bool, ) { self.mov_instruction(target_vector.size, limb_count); + self.const_instruction(target_vector.rc, 1_usize.into()); self.allocate_array_instruction(target_vector.pointer, target_vector.size); let shifted_register = self.allocate_register(); @@ -914,7 +997,7 @@ impl BrilligContext { } /// This instruction will reverse the order of the elements in a vector. - pub(crate) fn reverse_vector_in_place_instruction(&mut self, vector: HeapVector) { + pub(crate) fn reverse_vector_in_place_instruction(&mut self, vector: BrilligVector) { let iteration_count = self.allocate_register(); self.usize_op(vector.size, iteration_count, BinaryIntOp::UnsignedDiv, 2); @@ -949,51 +1032,12 @@ impl BrilligContext { self.deallocate_register(index_at_end_of_array); } - pub(crate) fn extract_heap_vector(&mut self, variable: RegisterOrMemory) -> HeapVector { - match variable { - RegisterOrMemory::HeapVector(vector) => vector, - RegisterOrMemory::HeapArray(array) => { - let size = self.allocate_register(); - self.const_instruction(size, array.size.into()); - HeapVector { pointer: array.pointer, size } - } - _ => unreachable!("ICE: Expected vector, got {variable:?}"), - } - } - /// Sets a current call stack that the next pushed opcodes will be associated with. pub(crate) fn set_call_stack(&mut self, call_stack: CallStack) { self.obj.set_call_stack(call_stack); } } -pub(crate) fn extract_register(variable: RegisterOrMemory) -> RegisterIndex { - match variable { - RegisterOrMemory::RegisterIndex(register_index) => register_index, - _ => unreachable!("ICE: Expected register, got {variable:?}"), - } -} - -pub(crate) fn extract_heap_array(variable: RegisterOrMemory) -> HeapArray { - match variable { - RegisterOrMemory::HeapArray(array) => array, - _ => unreachable!("ICE: Expected array, got {variable:?}"), - } -} - -/// Collects the registers that a given variable is stored in. -pub(crate) fn extract_registers(variable: RegisterOrMemory) -> Vec { - match variable { - RegisterOrMemory::RegisterIndex(register_index) => vec![register_index], - RegisterOrMemory::HeapArray(array) => { - vec![array.pointer] - } - RegisterOrMemory::HeapVector(vector) => { - vec![vector.pointer, vector.size] - } - } -} - /// Type to encapsulate the binary operation types in Brillig #[derive(Clone)] pub(crate) enum BrilligBinaryOp { diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/brillig_variable.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/brillig_variable.rs new file mode 100644 index 00000000000..46c54d55ecb --- /dev/null +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/brillig_variable.rs @@ -0,0 +1,99 @@ +use acvm::brillig_vm::brillig::{HeapArray, HeapVector, RegisterIndex, RegisterOrMemory}; +use serde::{Deserialize, Serialize}; + +/// The representation of a noir array in the Brillig IR +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Copy)] +pub(crate) struct BrilligArray { + pub(crate) pointer: RegisterIndex, + pub(crate) size: usize, + pub(crate) rc: RegisterIndex, +} + +impl BrilligArray { + pub(crate) fn to_heap_array(self) -> HeapArray { + HeapArray { pointer: self.pointer, size: self.size } + } + + pub(crate) fn registers_count() -> usize { + 2 + } + + pub(crate) fn extract_registers(self) -> Vec { + vec![self.pointer, self.rc] + } +} + +/// The representation of a noir slice in the Brillig IR +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Copy)] +pub(crate) struct BrilligVector { + pub(crate) pointer: RegisterIndex, + pub(crate) size: RegisterIndex, + pub(crate) rc: RegisterIndex, +} + +impl BrilligVector { + pub(crate) fn to_heap_vector(self) -> HeapVector { + HeapVector { pointer: self.pointer, size: self.size } + } + + pub(crate) fn registers_count() -> usize { + 3 + } + + pub(crate) fn extract_registers(self) -> Vec { + vec![self.pointer, self.size, self.rc] + } +} + +/// The representation of a noir value in the Brillig IR +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Copy)] +pub(crate) enum BrilligVariable { + Simple(RegisterIndex), + BrilligArray(BrilligArray), + BrilligVector(BrilligVector), +} + +impl BrilligVariable { + pub(crate) fn extract_register(self) -> RegisterIndex { + match self { + BrilligVariable::Simple(register_index) => register_index, + _ => unreachable!("ICE: Expected register, got {self:?}"), + } + } + + pub(crate) fn extract_array(self) -> BrilligArray { + match self { + BrilligVariable::BrilligArray(array) => array, + _ => unreachable!("ICE: Expected array, got {self:?}"), + } + } + + pub(crate) fn extract_vector(self) -> BrilligVector { + match self { + BrilligVariable::BrilligVector(vector) => vector, + _ => unreachable!("ICE: Expected vector, got {self:?}"), + } + } + + pub(crate) fn extract_registers(self) -> Vec { + match self { + BrilligVariable::Simple(register_index) => vec![register_index], + BrilligVariable::BrilligArray(array) => array.extract_registers(), + BrilligVariable::BrilligVector(vector) => vector.extract_registers(), + } + } + + pub(crate) fn to_register_or_memory(self) -> RegisterOrMemory { + match self { + BrilligVariable::Simple(register_index) => { + RegisterOrMemory::RegisterIndex(register_index) + } + BrilligVariable::BrilligArray(array) => { + RegisterOrMemory::HeapArray(array.to_heap_array()) + } + BrilligVariable::BrilligVector(vector) => { + RegisterOrMemory::HeapVector(vector.to_heap_vector()) + } + } + } +} diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs index fb426ad6876..48615988238 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs @@ -2,6 +2,7 @@ use crate::brillig::brillig_ir::ReservedRegisters; use super::{ artifact::{BrilligArtifact, BrilligParameter}, + brillig_variable::{BrilligArray, BrilligVariable}, debug_show::DebugShow, registers::BrilligRegistersContext, BrilligContext, @@ -20,6 +21,7 @@ impl BrilligContext { registers: BrilligRegistersContext::new(), context_label: String::default(), section_label: 0, + next_section: 1, debug_show: DebugShow::new(false), }; @@ -32,18 +34,39 @@ impl BrilligContext { } /// Adds the instructions needed to handle entry point parameters - /// - /// And sets the starting value of the reserved registers + /// The runtime will leave the parameters in the first `n` registers. + /// Arrays will be passed as pointers to the first element, with all the nested arrays flattened. + /// First, reserve the registers that contain the parameters. + /// This function also sets the starting value of the reserved registers fn entry_point_instruction(&mut self, arguments: Vec) { - // Translate the inputs by the reserved registers offset - for i in (0..arguments.len()).rev() { - self.push_opcode(BrilligOpcode::Mov { - destination: ReservedRegisters::user_register_index(i), - source: RegisterIndex::from(i), - }); - // Make sure we don't overwrite the arguments - self.allocate_register(); - } + let preallocated_registers: Vec<_> = + arguments.iter().enumerate().map(|(i, _)| RegisterIndex::from(i)).collect(); + self.set_allocated_registers(preallocated_registers.clone()); + + // Then allocate and initialize the variables that will hold the parameters + let argument_variables: Vec<_> = arguments + .iter() + .zip(preallocated_registers) + .map(|(argument, param_register)| match argument { + BrilligParameter::Simple => { + let variable_register = self.allocate_register(); + self.mov_instruction(variable_register, param_register); + BrilligVariable::Simple(variable_register) + } + BrilligParameter::Array(item_types, item_count) => { + let pointer_register = self.allocate_register(); + let rc_register = self.allocate_register(); + self.mov_instruction(pointer_register, param_register); + self.const_instruction(rc_register, 1_usize.into()); + BrilligVariable::BrilligArray(BrilligArray { + pointer: pointer_register, + size: item_types.len() * item_count, + rc: rc_register, + }) + } + BrilligParameter::Slice(_) => unimplemented!("Unsupported slices as parameter"), + }) + .collect(); // Calculate the initial value for the stack pointer register let size_arguments_memory: usize = arguments @@ -65,16 +88,24 @@ impl BrilligContext { value: 0_usize.into(), }); - for (index, parameter) in arguments.iter().enumerate() { + // Deflatten the arrays + for (parameter, assigned_variable) in arguments.iter().zip(&argument_variables) { if let BrilligParameter::Array(item_type, item_count) = parameter { if item_type.iter().any(|param| !matches!(param, BrilligParameter::Simple)) { - let pointer_register = ReservedRegisters::user_register_index(index); + let pointer_register = assigned_variable.extract_array().pointer; let deflattened_register = self.deflatten_array(item_type, *item_count, pointer_register); self.mov_instruction(pointer_register, deflattened_register); } } } + + // Move the parameters to the first user defined registers, to follow function call convention. + for (i, register) in + argument_variables.into_iter().flat_map(|arg| arg.extract_registers()).enumerate() + { + self.mov_instruction(ReservedRegisters::user_register_index(i), register); + } } /// Computes the size of a parameter if it was flattened @@ -92,6 +123,7 @@ impl BrilligContext { } /// Deflatten an array by recursively allocating nested arrays and copying the plain values. + /// Returns the pointer to the deflattened items. fn deflatten_array( &mut self, item_type: &[BrilligParameter], @@ -139,13 +171,25 @@ impl BrilligContext { *nested_array_item_count, nested_array_pointer, ); - self.array_set( - deflattened_array_pointer, - target_index, - deflattened_nested_array_pointer, + let reference = self.allocate_register(); + let rc = self.allocate_register(); + self.const_instruction(rc, 1_usize.into()); + + self.allocate_array_reference_instruction(reference); + self.store_variable_instruction( + reference, + BrilligVariable::BrilligArray(BrilligArray { + pointer: deflattened_nested_array_pointer, + size: nested_array_item_type.len() * nested_array_item_count, + rc, + }), ); + self.array_set(deflattened_array_pointer, target_index, reference); + self.deallocate_register(nested_array_pointer); + self.deallocate_register(reference); + self.deallocate_register(rc); source_offset += BrilligContext::flattened_size(subitem); } @@ -163,21 +207,36 @@ impl BrilligContext { } /// Adds the instructions needed to handle return parameters + /// The runtime expects the results in the first `n` registers. + /// Arrays are expected to be returned as pointers to the first element with all the nested arrays flattened. + /// However, the function called returns variables (that have extra data) and the returned arrays are unflattened. fn exit_point_instruction(&mut self, return_parameters: Vec) { - // Make sure we don't overwrite the return parameters - return_parameters.iter().for_each(|_| { - self.allocate_register(); - }); - - for (index, ret) in return_parameters.iter().enumerate() { - if let BrilligParameter::Array(item_type, item_count) = ret { + // First, we allocate the registers that hold the returned variables from the function call. + self.set_allocated_registers(vec![]); + let returned_variables: Vec<_> = return_parameters + .iter() + .map(|return_parameter| match return_parameter { + BrilligParameter::Simple => BrilligVariable::Simple(self.allocate_register()), + BrilligParameter::Array(item_types, item_count) => { + BrilligVariable::BrilligArray(BrilligArray { + pointer: self.allocate_register(), + size: item_types.len() * item_count, + rc: self.allocate_register(), + }) + } + BrilligParameter::Slice(..) => unreachable!("ICE: Cannot return slices"), + }) + .collect(); + // Now, we unflatten the returned arrays + for (return_param, returned_variable) in return_parameters.iter().zip(&returned_variables) { + if let BrilligParameter::Array(item_type, item_count) = return_param { if item_type.iter().any(|item| !matches!(item, BrilligParameter::Simple)) { - let returned_pointer = ReservedRegisters::user_register_index(index); + let returned_pointer = returned_variable.extract_array().pointer; let flattened_array_pointer = self.allocate_register(); self.allocate_fixed_length_array( flattened_array_pointer, - BrilligContext::flattened_size(ret), + BrilligContext::flattened_size(return_param), ); self.flatten_array( @@ -191,16 +250,18 @@ impl BrilligContext { } } } - // We want all functions to follow the calling convention of returning + // The VM expects us to follow the calling convention of returning // their results in the first `n` registers. So we to move the return values // to the first `n` registers once completed. // Move the results to registers 0..n - for i in 0..return_parameters.len() { - self.push_opcode(BrilligOpcode::Mov { - destination: i.into(), - source: ReservedRegisters::user_register_index(i), - }); + for (i, returned_variable) in returned_variables.into_iter().enumerate() { + let register = match returned_variable { + BrilligVariable::Simple(register) => register, + BrilligVariable::BrilligArray(array) => array.pointer, + BrilligVariable::BrilligVector(vector) => vector.pointer, + }; + self.push_opcode(BrilligOpcode::Mov { destination: i.into(), source: register }); } self.push_opcode(BrilligOpcode::Stop); } @@ -237,11 +298,22 @@ impl BrilligContext { target_offset += 1; } BrilligParameter::Array(nested_array_item_type, nested_array_item_count) => { - let nested_array_pointer = self.allocate_register(); + let nested_array_reference = self.allocate_register(); self.array_get( deflattened_array_pointer, source_index, - nested_array_pointer, + nested_array_reference, + ); + + let nested_array_variable = BrilligVariable::BrilligArray(BrilligArray { + pointer: self.allocate_register(), + size: nested_array_item_type.len() * nested_array_item_count, + rc: self.allocate_register(), + }); + + self.load_variable_instruction( + nested_array_variable, + nested_array_reference, ); let flattened_nested_array_pointer = self.allocate_register(); @@ -262,11 +334,15 @@ impl BrilligContext { nested_array_item_type, *nested_array_item_count, flattened_nested_array_pointer, - nested_array_pointer, + nested_array_variable.extract_array().pointer, ); - self.deallocate_register(nested_array_pointer); + self.deallocate_register(nested_array_reference); self.deallocate_register(flattened_nested_array_pointer); + nested_array_variable + .extract_registers() + .into_iter() + .for_each(|register| self.deallocate_register(register)); target_offset += BrilligContext::flattened_size(subitem); } @@ -288,6 +364,7 @@ mod tests { use crate::brillig::brillig_ir::{ artifact::BrilligParameter, + brillig_variable::BrilligArray, tests::{create_and_run_vm, create_context, create_entry_point_bytecode}, }; @@ -332,18 +409,24 @@ mod tests { Value::from(4_usize), Value::from(5_usize), Value::from(6_usize), - // The pointer to the nested array of the first item - Value::from(10_usize), - Value::from(3_usize), - // The pointer to the nested array of the second item + // The pointer to the nested reference of the first item Value::from(12_usize), + Value::from(3_usize), + // The pointer to the nested reference of the second item + Value::from(16_usize), Value::from(6_usize), // The nested array of the first item Value::from(1_usize), Value::from(2_usize), + // The nested reference of the first item + Value::from(10_usize), + Value::from(1_usize), // The nested array of the second item Value::from(4_usize), Value::from(5_usize), + // The nested reference of the second item + Value::from(14_usize), + Value::from(1_usize), ] ); } @@ -358,35 +441,31 @@ mod tests { Value::from(5_usize), Value::from(6_usize), ]; - let array_param = BrilligParameter::Array( vec![ - BrilligParameter::Simple, BrilligParameter::Array(vec![BrilligParameter::Simple], 2), + BrilligParameter::Simple, ], 2, ); - let arguments = vec![array_param.clone()]; let returns = vec![array_param]; let mut context = create_context(); // Allocate the parameter - let array_pointer = context.allocate_register(); + let brillig_array = BrilligArray { + pointer: context.allocate_register(), + size: 2, + rc: context.allocate_register(), + }; - context.return_instruction(&[array_pointer]); + context.return_instruction(&brillig_array.extract_registers()); let bytecode = create_entry_point_bytecode(context, arguments, returns).byte_code; let vm = create_and_run_vm(flattened_array.clone(), vec![Value::from(0_usize)], &bytecode); let memory = vm.get_memory(); - assert_eq!( - vm.get_registers().get(RegisterIndex(0)), - // The returned value will be past the original array and the deflattened array - Value::from(flattened_array.len() + (flattened_array.len() + 2)), - ); - assert_eq!( memory, &vec![ @@ -397,19 +476,25 @@ mod tests { Value::from(4_usize), Value::from(5_usize), Value::from(6_usize), - // The pointer to the nested array of the first item - Value::from(1_usize), - Value::from(10_usize), - // The pointer to the nested array of the second item - Value::from(4_usize), + // The pointer to the nested reference of the first item Value::from(12_usize), + Value::from(3_usize), + // The pointer to the nested reference of the second item + Value::from(16_usize), + Value::from(6_usize), // The nested array of the first item + Value::from(1_usize), Value::from(2_usize), - Value::from(3_usize), + // The nested reference of the first item + Value::from(10_usize), + Value::from(1_usize), // The nested array of the second item + Value::from(4_usize), Value::from(5_usize), - Value::from(6_usize), - // The values flattened again + // The nested reference of the second item + Value::from(14_usize), + Value::from(1_usize), + // The original flattened again Value::from(1_usize), Value::from(2_usize), Value::from(3_usize), @@ -418,5 +503,6 @@ mod tests { Value::from(6_usize), ] ); + assert_eq!(vm.get_registers().get(RegisterIndex(0)), 18_usize.into()); } } diff --git a/compiler/noirc_evaluator/src/errors.rs b/compiler/noirc_evaluator/src/errors.rs index 2429ca9e194..42818e8b19d 100644 --- a/compiler/noirc_evaluator/src/errors.rs +++ b/compiler/noirc_evaluator/src/errors.rs @@ -26,7 +26,7 @@ pub enum RuntimeError { }, #[error(transparent)] InternalError(#[from] InternalError), - #[error("Index out of bounds, array has size {index:?}, but index was {array_size:?}")] + #[error("Index out of bounds, array has size {array_size}, but index was {index}")] 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 }, @@ -63,16 +63,20 @@ 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 (secondary_message, call_stack) = match warning { + InternalWarning::ReturnConstant { call_stack } => { + ("constant value".to_string(), call_stack) + }, + InternalWarning::VerifyProof { call_stack } => { + ("verify_proof(...) aggregates data for the verifier, the actual verification will be done when the full proof is verified using nargo verify. nargo prove may generate an invalid proof if bad data is used as input to verify_proof".to_string(), call_stack) + }, + }; + let call_stack = vecmap(call_stack, |location| location); + let file_id = call_stack.last().map(|location| location.file).unwrap_or_default(); let location = call_stack.last().expect("Expected RuntimeError to have a location"); - let diagnostic = Diagnostic::simple_warning( - message, - "constant value".to_string(), - location.span, - ); + let diagnostic = + Diagnostic::simple_warning(message, secondary_message, location.span); diagnostic.in_file(file_id).with_call_stack(call_stack) } } @@ -83,6 +87,8 @@ impl From for FileDiagnostic { pub enum InternalWarning { #[error("Returning a constant value is not allowed")] ReturnConstant { call_stack: CallStack }, + #[error("Calling std::verify_proof(...) does not verify a proof")] + VerifyProof { call_stack: CallStack }, } #[derive(Debug, PartialEq, Eq, Clone, Error)] diff --git a/compiler/noirc_evaluator/src/lib.rs b/compiler/noirc_evaluator/src/lib.rs index b5b697e3b65..70751d3e541 100644 --- a/compiler/noirc_evaluator/src/lib.rs +++ b/compiler/noirc_evaluator/src/lib.rs @@ -11,5 +11,4 @@ pub mod ssa; pub mod brillig; -pub use ssa::abi_gen::into_abi_params; pub use ssa::create_circuit; diff --git a/compiler/noirc_evaluator/src/ssa.rs b/compiler/noirc_evaluator/src/ssa.rs index 24521b98bd1..8e1c62edc69 100644 --- a/compiler/noirc_evaluator/src/ssa.rs +++ b/compiler/noirc_evaluator/src/ssa.rs @@ -7,10 +7,7 @@ //! This module heavily borrows from Cranelift #![allow(dead_code)] -use std::{ - collections::{BTreeMap, BTreeSet}, - ops::Range, -}; +use std::collections::BTreeSet; use crate::{ brillig::Brillig, @@ -23,13 +20,12 @@ use acvm::acir::{ use noirc_errors::debug_info::DebugInfo; -use noirc_abi::Abi; - -use noirc_frontend::{hir::Context, monomorphization::ast::Program}; +use noirc_frontend::{ + hir_def::function::FunctionSignature, monomorphization::ast::Program, Visibility, +}; -use self::{abi_gen::gen_abi, acir_gen::GeneratedAcir, ssa_gen::Ssa}; +use self::{acir_gen::GeneratedAcir, ssa_gen::Ssa}; -pub mod abi_gen; mod acir_gen; pub(super) mod function_builder; pub mod ir; @@ -81,12 +77,12 @@ pub(crate) fn optimize_into_acir( /// Compiles the [`Program`] into [`ACIR`][acvm::acir::circuit::Circuit]. /// /// The output ACIR is is backend-agnostic and so must go through a transformation pass before usage in proof generation. +#[allow(clippy::type_complexity)] pub fn create_circuit( - context: &Context, program: Program, enable_ssa_logging: bool, enable_brillig_logging: bool, -) -> Result<(Circuit, DebugInfo, Abi, Vec), RuntimeError> { +) -> Result<(Circuit, DebugInfo, Vec, Vec, Vec), RuntimeError> { let func_sig = program.main_function_signature.clone(); let mut generated_acir = optimize_into_acir(program, enable_ssa_logging, enable_brillig_logging)?; @@ -101,15 +97,11 @@ pub fn create_circuit( .. } = generated_acir; - let abi = gen_abi(context, func_sig, input_witnesses, return_witnesses.clone()); - let public_abi = abi.clone().public_abi(); - - let public_parameters = PublicInputs(tree_to_set(&public_abi.param_witnesses)); + let (public_parameter_witnesses, private_parameters) = + split_public_and_private_inputs(&func_sig, &input_witnesses); - let all_parameters: BTreeSet = tree_to_set(&abi.param_witnesses); - let private_parameters = all_parameters.difference(&public_parameters.0).copied().collect(); - - let return_values = PublicInputs(return_witnesses.into_iter().collect()); + let public_parameters = PublicInputs(public_parameter_witnesses); + let return_values = PublicInputs(return_witnesses.iter().copied().collect()); let circuit = Circuit { current_witness_index, @@ -132,7 +124,41 @@ 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, warnings)) + Ok((optimized_circuit, debug_info, input_witnesses, return_witnesses, warnings)) +} + +// Takes each function argument and partitions the circuit's inputs witnesses according to its visibility. +fn split_public_and_private_inputs( + func_sig: &FunctionSignature, + input_witnesses: &[Witness], +) -> (BTreeSet, BTreeSet) { + let mut idx = 0_usize; + if input_witnesses.is_empty() { + return (BTreeSet::new(), BTreeSet::new()); + } + + func_sig + .0 + .iter() + .map(|(_, typ, visibility)| { + let num_field_elements_needed = typ.field_count() as usize; + let witnesses = input_witnesses[idx..idx + num_field_elements_needed].to_vec(); + idx += num_field_elements_needed; + (visibility, witnesses) + }) + .fold((BTreeSet::new(), BTreeSet::new()), |mut acc, (vis, witnesses)| { + // Split witnesses into sets based on their visibility. + if *vis == Visibility::Public { + for witness in witnesses { + acc.0.insert(witness); + } + } else { + for witness in witnesses { + acc.1.insert(witness); + } + } + (acc.0, acc.1) + }) } // This is just a convenience object to bundle the ssa with `print_ssa_passes` for debug printing. @@ -178,15 +204,3 @@ impl SsaBuilder { self } } - -// Flatten the witnesses in the map into a BTreeSet -fn tree_to_set(input: &BTreeMap>>) -> BTreeSet { - let mut result = BTreeSet::new(); - for range in input.values().flatten() { - for i in range.start.witness_index()..range.end.witness_index() { - result.insert(Witness(i)); - } - } - - result -} diff --git a/compiler/noirc_evaluator/src/ssa/abi_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/abi_gen/mod.rs deleted file mode 100644 index 948fb7d5263..00000000000 --- a/compiler/noirc_evaluator/src/ssa/abi_gen/mod.rs +++ /dev/null @@ -1,86 +0,0 @@ -use std::collections::BTreeMap; - -use acvm::acir::native_types::Witness; -use iter_extended::{btree_map, vecmap}; -use noirc_abi::{Abi, AbiParameter, AbiType}; -use noirc_frontend::{ - hir::Context, - hir_def::{ - function::{FunctionSignature, Param}, - stmt::HirPattern, - }, - node_interner::NodeInterner, -}; -use std::ops::Range; - -/// Attempts to retrieve the name of this parameter. Returns None -/// if this parameter is a tuple or struct pattern. -fn get_param_name<'a>(pattern: &HirPattern, interner: &'a NodeInterner) -> Option<&'a str> { - match pattern { - HirPattern::Identifier(ident) => Some(interner.definition_name(ident.id)), - HirPattern::Mutable(pattern, _) => get_param_name(pattern, interner), - HirPattern::Tuple(_, _) => None, - HirPattern::Struct(_, _, _) => None, - } -} - -pub fn into_abi_params(context: &Context, params: Vec) -> Vec { - vecmap(params, |(pattern, typ, vis)| { - let param_name = get_param_name(&pattern, &context.def_interner) - .expect("Abi for tuple and struct parameters is unimplemented") - .to_owned(); - let as_abi = AbiType::from_type(context, &typ); - AbiParameter { name: param_name, typ: as_abi, visibility: vis.into() } - }) -} - -/// Arranges a function signature and a generated circuit's return witnesses into a -/// `noirc_abi::Abi`. -pub(crate) fn gen_abi( - context: &Context, - func_sig: FunctionSignature, - input_witnesses: Vec>, - return_witnesses: Vec, -) -> Abi { - let (parameters, return_type) = func_sig; - let parameters = into_abi_params(context, parameters); - let return_type = return_type.map(|typ| AbiType::from_type(context, &typ)); - let param_witnesses = param_witnesses_from_abi_param(¶meters, input_witnesses); - Abi { parameters, return_type, param_witnesses, return_witnesses } -} - -// Takes each abi parameter and shallowly maps to the expected witness range in which the -// parameter's constituent values live. -fn param_witnesses_from_abi_param( - abi_params: &Vec, - input_witnesses: Vec>, -) -> BTreeMap>> { - let mut idx = 0_usize; - if input_witnesses.is_empty() { - return BTreeMap::new(); - } - let mut processed_range = input_witnesses[idx].start.witness_index(); - - btree_map(abi_params, |param| { - let num_field_elements_needed = param.typ.field_count(); - let mut wit = Vec::new(); - let mut processed_fields = 0; - while processed_fields < num_field_elements_needed { - let end = input_witnesses[idx].end.witness_index(); - if num_field_elements_needed <= end - processed_range { - wit.push( - Witness(processed_range)..Witness(processed_range + num_field_elements_needed), - ); - processed_range += num_field_elements_needed; - processed_fields += num_field_elements_needed; - } else { - // consume the current range - wit.push(Witness(processed_range)..input_witnesses[idx].end); - processed_fields += end - processed_range; - idx += 1; - processed_range = input_witnesses[idx].start.witness_index(); - } - } - (param.name.clone(), wit) - }) -} 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 4ee70ed9b22..17f9b742625 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 @@ -23,7 +23,6 @@ use acvm::{BlackBoxFunctionSolver, BlackBoxResolutionError}; use fxhash::FxHashMap as HashMap; use iter_extended::{try_vecmap, vecmap}; use num_bigint::BigUint; -use std::ops::RangeInclusive; use std::{borrow::Cow, hash::Hash}; #[derive(Clone, Debug, PartialEq, Eq, Hash)] @@ -115,7 +114,7 @@ impl AcirContext { self.acir_ir.current_witness_index() } - pub(crate) fn extract_witness(&self, inputs: &[AcirValue]) -> Vec { + pub(crate) fn extract_witness(&self, inputs: &[AcirValue]) -> Vec { inputs .iter() .flat_map(|value| value.clone().flatten()) @@ -126,14 +125,13 @@ impl AcirContext { .to_expression() .to_witness() .expect("ICE - cannot extract a witness") - .0 }) .collect() } /// Adds a constant to the context and assigns a Variable to represent it - pub(crate) fn add_constant(&mut self, constant: FieldElement) -> AcirVar { - let constant_data = AcirVarData::Const(constant); + pub(crate) fn add_constant(&mut self, constant: impl Into) -> AcirVar { + let constant_data = AcirVarData::Const(constant.into()); self.add_data(constant_data) } @@ -355,7 +353,7 @@ impl AcirContext { // Check to see if equality can be determined at compile-time. if diff_expr.is_const() { - return Ok(self.add_constant(diff_expr.is_zero().into())); + return Ok(self.add_constant(diff_expr.is_zero())); } let is_equal_witness = self.acir_ir.is_equal(&lhs_expr, &rhs_expr); @@ -382,9 +380,15 @@ impl AcirContext { rhs: AcirVar, typ: AcirType, ) -> Result { - let inputs = vec![AcirValue::Var(lhs, typ.clone()), AcirValue::Var(rhs, typ)]; - let outputs = self.black_box_function(BlackBoxFunc::AND, inputs, 1)?; - Ok(outputs[0]) + let bit_size = typ.bit_size(); + if bit_size == 1 { + // Operands are booleans. + self.mul_var(lhs, rhs) + } else { + let inputs = vec![AcirValue::Var(lhs, typ.clone()), AcirValue::Var(rhs, typ)]; + let outputs = self.black_box_function(BlackBoxFunc::AND, inputs, 1)?; + Ok(outputs[0]) + } } /// Returns an `AcirVar` that is the OR result of `lhs` & `rhs`. @@ -406,7 +410,7 @@ impl AcirContext { // max - ((max - a) AND (max -b)) // Subtracting from max flips the bits, so this is effectively: // (NOT a) NAND (NOT b) - let max = self.add_constant(FieldElement::from((1_u128 << bit_size) - 1)); + let max = self.add_constant((1_u128 << bit_size) - 1); let a = self.sub_var(max, lhs)?; let b = self.sub_var(max, rhs)?; let inputs = vec![AcirValue::Var(a, typ.clone()), AcirValue::Var(b, typ)]; @@ -549,7 +553,7 @@ impl AcirContext { pub(crate) fn not_var(&mut self, x: AcirVar, typ: AcirType) -> Result { let bit_size = typ.bit_size(); // Subtracting from max flips the bits - let max = self.add_constant(FieldElement::from((1_u128 << bit_size) - 1)); + let max = self.add_constant((1_u128 << bit_size) - 1); self.sub_var(max, x) } @@ -576,8 +580,8 @@ impl AcirContext { 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)); + let quotient_var = self.add_constant(quotient); + let remainder_var = self.add_constant(remainder); return Ok((quotient_var, remainder_var)); } @@ -774,7 +778,7 @@ impl AcirContext { // 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 r_var = self.add_constant(r); 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 }, None)?; @@ -922,7 +926,7 @@ impl AcirContext { ) -> Result { // 2^{rhs} let divisor = - self.add_constant(FieldElement::from(2_i128).pow(&FieldElement::from(rhs as i128))); + self.add_constant(FieldElement::from(2_u128).pow(&FieldElement::from(rhs as u128))); let one = self.add_constant(FieldElement::one()); // Computes lhs = 2^{rhs} * q + r @@ -933,7 +937,7 @@ impl AcirContext { /// Returns an `AcirVar` which will be `1` if lhs >= rhs /// and `0` otherwise. - fn more_than_eq_var( + pub(crate) fn more_than_eq_var( &mut self, lhs: AcirVar, rhs: AcirVar, @@ -1146,10 +1150,7 @@ impl AcirContext { // `Intrinsic::ToRadix` returns slices which are represented // by tuples with the structure (length, slice contents) Ok(vec![ - AcirValue::Var( - self.add_constant(FieldElement::from(limb_vars.len() as u128)), - AcirType::field(), - ), + AcirValue::Var(self.add_constant(limb_vars.len()), AcirType::field()), AcirValue::Array(limb_vars.into()), ]) } @@ -1162,7 +1163,7 @@ impl AcirContext { limb_count_var: AcirVar, result_element_type: AcirType, ) -> Result, RuntimeError> { - let two_var = self.add_constant(FieldElement::from(2_u128)); + let two_var = self.add_constant(2_u128); self.radix_decompose(endian, input_var, two_var, limb_count_var, result_element_type) } @@ -1182,27 +1183,10 @@ impl AcirContext { /// Terminates the context and takes the resulting `GeneratedAcir` pub(crate) fn finish( mut self, - inputs: Vec>, + inputs: Vec, warnings: Vec, ) -> GeneratedAcir { - let mut current_range = 0..0; - for range in inputs { - if current_range.end == *range.start() { - current_range.end = range.end() + 1; - } else { - if current_range.end != 0 { - self.acir_ir - .input_witnesses - .push(Witness(current_range.start)..Witness(current_range.end)); - } - current_range = *range.start()..range.end() + 1; - } - } - if current_range.end != 0 { - self.acir_ir - .input_witnesses - .push(Witness(current_range.start)..Witness(current_range.end)); - } + self.acir_ir.input_witnesses = inputs; self.acir_ir.warnings = warnings; self.acir_ir } @@ -1287,7 +1271,7 @@ impl AcirContext { AcirValue::DynamicArray(AcirDynamicArray { block_id, len, .. }) => { for i in 0..len { // We generate witnesses corresponding to the array values - let index_var = self.add_constant(FieldElement::from(i as u128)); + let index_var = self.add_constant(i); let value_read_var = self.read_from_memory(block_id, &index_var)?; let value_read = AcirValue::Var(value_read_var, AcirType::field()); 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 9bd83096adf..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 @@ -23,7 +23,6 @@ use acvm::{ }; use iter_extended::vecmap; use num_bigint::BigUint; -use std::ops::Range; #[derive(Debug, Default)] /// The output of the Acir-gen pass @@ -43,7 +42,7 @@ pub(crate) struct GeneratedAcir { pub(crate) return_witnesses: Vec, /// All witness indices which are inputs to the main function - pub(crate) input_witnesses: Vec>, + pub(crate) input_witnesses: Vec, /// Correspondence between an opcode index (in opcodes) and the source code call stack which generated it pub(crate) locations: BTreeMap, diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs index 7307689768f..c41674778ad 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -3,7 +3,6 @@ mod acir_ir; use std::collections::HashSet; use std::fmt::Debug; -use std::ops::RangeInclusive; use self::acir_ir::acir_variable::{AcirContext, AcirType, AcirVar}; use super::ir::dfg::CallStack; @@ -26,6 +25,8 @@ use crate::brillig::{brillig_gen::brillig_fn::FunctionContext as BrilligFunction use crate::errors::{InternalError, InternalWarning, RuntimeError, SsaReport}; pub(crate) use acir_ir::generated_acir::GeneratedAcir; +use acvm::acir::native_types::Witness; +use acvm::acir::BlackBoxFunc; use acvm::{ acir::{circuit::opcodes::BlockId, native_types::Expression}, FieldElement, @@ -100,14 +101,16 @@ pub(crate) struct AcirDynamicArray { len: usize, /// Identification for the ACIR dynamic array /// inner element type sizes array - element_type_sizes: BlockId, + element_type_sizes: Option, } impl Debug for AcirDynamicArray { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!( f, "id: {}, len: {}, element_type_sizes: {:?}", - self.block_id.0, self.len, self.element_type_sizes.0 + self.block_id.0, + self.len, + self.element_type_sizes.map(|block_id| block_id.0) ) } } @@ -130,6 +133,16 @@ impl AcirValue { } } + fn borrow_var(&self) -> Result { + match self { + AcirValue::Var(var, _) => Ok(*var), + AcirValue::DynamicArray(_) | AcirValue::Array(_) => Err(InternalError::General { + message: "Called AcirValue::borrow_var on an array".to_string(), + call_stack: CallStack::new(), + }), + } + } + fn flatten(self) -> Vec<(AcirVar, AcirType)> { match self { AcirValue::Var(var, typ) => vec![(var, typ)], @@ -213,14 +226,19 @@ impl Context { let dfg = &main_func.dfg; let entry_block = &dfg[main_func.entry_block()]; let input_witness = self.convert_ssa_block_params(entry_block.parameters(), dfg)?; - + let mut warnings = Vec::new(); for instruction_id in entry_block.instructions() { - self.convert_ssa_instruction(*instruction_id, dfg, ssa, &brillig, last_array_uses)?; + warnings.extend(self.convert_ssa_instruction( + *instruction_id, + dfg, + ssa, + &brillig, + last_array_uses, + )?); } - let warnings = self.convert_ssa_return(entry_block.unwrap_terminator(), dfg)?; - - Ok(self.acir_context.finish(vec![input_witness], warnings)) + warnings.extend(self.convert_ssa_return(entry_block.unwrap_terminator(), dfg)?); + Ok(self.acir_context.finish(input_witness, warnings)) } fn convert_brillig_main( @@ -256,8 +274,7 @@ impl Context { for acir_var in output_vars { self.acir_context.return_var(acir_var)?; } - let witnesses = vecmap(witness_inputs, |input| RangeInclusive::new(input, input)); - Ok(self.acir_context.finish(witnesses, Vec::new())) + Ok(self.acir_context.finish(witness_inputs, Vec::new())) } /// Adds and binds `AcirVar`s for each numeric block parameter or block parameter array element. @@ -265,7 +282,7 @@ impl Context { &mut self, params: &[ValueId], dfg: &DataFlowGraph, - ) -> Result, RuntimeError> { + ) -> Result, RuntimeError> { // The first witness (if any) is the next one let start_witness = self.acir_context.current_witness_index().0 + 1; for param_id in params { @@ -294,7 +311,8 @@ impl Context { self.ssa_values.insert(*param_id, value); } let end_witness = self.acir_context.current_witness_index().0; - Ok(start_witness..=end_witness) + let witnesses = (start_witness..=end_witness).map(Witness::from).collect(); + Ok(witnesses) } fn convert_ssa_block_param(&mut self, param_type: &Type) -> Result { @@ -377,9 +395,10 @@ impl Context { ssa: &Ssa, brillig: &Brillig, last_array_uses: &HashMap, - ) -> Result<(), RuntimeError> { + ) -> Result, RuntimeError> { let instruction = &dfg[instruction_id]; self.acir_context.set_call_stack(dfg.get_call_stack(instruction_id)); + let mut warnings = Vec::new(); match instruction { Instruction::Binary(binary) => { let result_acir_var = self.convert_ssa_binary(binary, dfg)?; @@ -432,8 +451,7 @@ impl Context { let mut read_dynamic_array_index = |block_id: BlockId, array_index: usize| -> Result { - let index_var = - self.acir_context.add_constant(FieldElement::from(array_index as u128)); + let index_var = self.acir_context.add_constant(array_index); self.acir_context.read_from_memory(block_id, &index_var) }; @@ -482,6 +500,14 @@ impl Context { } } Value::Intrinsic(intrinsic) => { + if matches!( + intrinsic, + Intrinsic::BlackBox(BlackBoxFunc::RecursiveAggregation) + ) { + warnings.push(SsaReport::Warning(InternalWarning::VerifyProof { + call_stack: self.acir_context.get_call_stack(), + })); + } let outputs = self .convert_ssa_intrinsic_call(*intrinsic, arguments, dfg, result_ids)?; @@ -548,6 +574,9 @@ impl Context { Instruction::Load { .. } => { unreachable!("Expected all load instructions to be removed before acir_gen") } + Instruction::IncrementRc { .. } => { + // Do nothing. Only Brillig needs to worry about reference counted arrays + } Instruction::RangeCheck { value, max_bit_size, assert_message } => { let acir_var = self.convert_numeric_value(*value, dfg)?; self.acir_context.range_constrain_var( @@ -558,7 +587,7 @@ impl Context { } } self.acir_context.set_call_stack(CallStack::new()); - Ok(()) + Ok(warnings) } fn gen_brillig_for( @@ -643,75 +672,76 @@ impl Context { store_value: Option, ) -> Result { let index_const = dfg.get_numeric_constant(index); - match dfg.type_of_value(array) { - Type::Array(_, _) => { - match self.convert_value(array, dfg) { - AcirValue::Var(acir_var, _) => { - return Err(RuntimeError::InternalError(InternalError::UnExpected { - expected: "an array value".to_string(), - found: format!("{acir_var:?}"), - call_stack: self.acir_context.get_call_stack(), - })) - } - AcirValue::Array(array) => { - if let Some(index_const) = index_const { - let array_size = array.len(); - let index = match index_const.try_to_u64() { - Some(index_const) => index_const as usize, - None => { - let call_stack = self.acir_context.get_call_stack(); - return Err(RuntimeError::TypeConversion { - from: "array index".to_string(), - into: "u64".to_string(), - call_stack, - }); + let value_type = dfg.type_of_value(array); + let (Type::Array(element_types, _) | Type::Slice(element_types)) = &value_type else { + unreachable!("ICE: expected array or slice type"); + + }; + + // TODO(#3188): Need to be able to handle constant index for slices to seriously reduce + // constraint sizes of nested slices + // This can only be done if we accurately flatten nested slices as otherwise we will reach + // index out of bounds errors. If the slice is already flat then we can treat them similarly to arrays. + if matches!(value_type, Type::Slice(_)) + && element_types.iter().any(|element| element.contains_slice_element()) + { + return Ok(false); + } + + match self.convert_value(array, dfg) { + AcirValue::Var(acir_var, _) => { + return Err(RuntimeError::InternalError(InternalError::UnExpected { + expected: "an array value".to_string(), + found: format!("{acir_var:?}"), + call_stack: self.acir_context.get_call_stack(), + })) + } + AcirValue::Array(array) => { + if let Some(index_const) = index_const { + let array_size = array.len(); + let index = match index_const.try_to_u64() { + Some(index_const) => index_const as usize, + None => { + let call_stack = self.acir_context.get_call_stack(); + return Err(RuntimeError::TypeConversion { + from: "array index".to_string(), + into: "u64".to_string(), + call_stack, + }); + } + }; + if self.acir_context.is_constant_one(&self.current_side_effects_enabled_var) { + // Report the error if side effects are enabled. + if index >= array_size { + let call_stack = self.acir_context.get_call_stack(); + return Err(RuntimeError::IndexOutOfBounds { + index, + array_size, + call_stack, + }); + } else { + let value = match store_value { + Some(store_value) => { + let store_value = self.convert_value(store_value, dfg); + AcirValue::Array(array.update(index, store_value)) } + None => array[index].clone(), }; - if self - .acir_context - .is_constant_one(&self.current_side_effects_enabled_var) - { - // Report the error if side effects are enabled. - if index >= array_size { - let call_stack = self.acir_context.get_call_stack(); - return Err(RuntimeError::IndexOutOfBounds { - index, - array_size, - call_stack, - }); - } else { - let value = match store_value { - Some(store_value) => { - let store_value = self.convert_value(store_value, dfg); - AcirValue::Array(array.update(index, store_value)) - } - None => array[index].clone(), - }; - self.define_result(dfg, instruction, value); - return Ok(true); - } - } - // If there is a predicate and the index is not out of range, we can directly perform the read - else if index < array_size && store_value.is_none() { - self.define_result(dfg, instruction, array[index].clone()); - return Ok(true); - } + self.define_result(dfg, instruction, value); + return Ok(true); } } - AcirValue::DynamicArray(_) => (), + // If there is a predicate and the index is not out of range, we can directly perform the read + else if index < array_size && store_value.is_none() { + self.define_result(dfg, instruction, array[index].clone()); + return Ok(true); + } } } - Type::Slice(_) => { - // TODO(#3188): Need to be able to handle constant index for slices to seriously reduce - // constraint sizes of nested slices - // This can only be done if we accurately flatten nested slices as other we will reach - // index out of bounds errors. + AcirValue::DynamicArray(_) => (), + }; - // Do nothing we only want dynamic checks for slices - } - _ => unreachable!("ICE: expected array or slice type"), - } Ok(false) } @@ -831,7 +861,7 @@ impl Context { ); let values = try_vecmap(0..*len, |i| { - let index_var = self.acir_context.add_constant(FieldElement::from(i as u128)); + let index_var = self.acir_context.add_constant(i); let read = self.acir_context.read_from_memory(*block_id, &index_var)?; Ok::(AcirValue::Var(read, AcirType::field())) @@ -876,7 +906,7 @@ impl Context { // The first max size is going to be the length of the parent slice // As we are fetching from the parent slice we just want its internal - // slize sizes. + // slice sizes. let slice_sizes = slice_sizes[1..].to_vec(); let value = self.array_get_value(&res_typ, block_id, &mut var_index, &slice_sizes)?; @@ -905,7 +935,7 @@ impl Context { // Read the value from the array at the specified index let read = self.acir_context.read_from_memory(block_id, var_index)?; - // Incremement the var_index in case of a nested array + // Increment the var_index in case of a nested array *var_index = self.acir_context.add_var(*var_index, one)?; let typ = AcirType::NumericType(numeric_type); @@ -1001,7 +1031,7 @@ impl Context { self.copy_dynamic_array(block_id, result_block_id, array_len)?; } - self.array_set_value(store_value, result_block_id, &mut var_index)?; + self.array_set_value(&store_value, result_block_id, &mut var_index)?; // Set new resulting array to have the same slice sizes as the instruction input if let Type::Slice(element_types) = &array_typ { @@ -1020,8 +1050,11 @@ impl Context { } } - let element_type_sizes = - self.init_element_type_sizes_array(&array_typ, array_id, None, dfg)?; + let element_type_sizes = if !can_omit_element_sizes_array(&array_typ) { + Some(self.init_element_type_sizes_array(&array_typ, array_id, None, dfg)?) + } else { + None + }; let result_value = AcirValue::DynamicArray(AcirDynamicArray { block_id: result_block_id, len: array_len, @@ -1033,7 +1066,7 @@ impl Context { fn array_set_value( &mut self, - value: AcirValue, + value: &AcirValue, block_id: BlockId, var_index: &mut AcirVar, ) -> Result<(), RuntimeError> { @@ -1041,8 +1074,8 @@ impl Context { match value { AcirValue::Var(store_var, _) => { // Write the new value into the new array at the specified index - self.acir_context.write_to_memory(block_id, var_index, &store_var)?; - // Incremement the var_index in case of a nested array + self.acir_context.write_to_memory(block_id, var_index, store_var)?; + // Increment the var_index in case of a nested array *var_index = self.acir_context.add_var(*var_index, one)?; } AcirValue::Array(values) => { @@ -1051,13 +1084,13 @@ impl Context { } } AcirValue::DynamicArray(AcirDynamicArray { block_id: inner_block_id, len, .. }) => { - let values = try_vecmap(0..len, |i| { - let index_var = self.acir_context.add_constant(FieldElement::from(i as u128)); + let values = try_vecmap(0..*len, |i| { + let index_var = self.acir_context.add_constant(i); - let read = self.acir_context.read_from_memory(inner_block_id, &index_var)?; + let read = self.acir_context.read_from_memory(*inner_block_id, &index_var)?; Ok::(AcirValue::Var(read, AcirType::field())) })?; - self.array_set_value(AcirValue::Array(values.into()), block_id, var_index)?; + self.array_set_value(&AcirValue::Array(values.into()), block_id, var_index)?; } } Ok(()) @@ -1146,27 +1179,29 @@ impl Context { element_type_sizes: inner_elem_type_sizes, .. }) => { - if self.initialized_arrays.contains(&inner_elem_type_sizes) { - let type_sizes_array_len = self.internal_mem_block_lengths.get(&inner_elem_type_sizes).copied().ok_or_else(|| - InternalError::General { - message: format!("Array {array_id}'s inner element type sizes array does not have a tracked length"), + if let Some(inner_elem_type_sizes) = inner_elem_type_sizes { + if self.initialized_arrays.contains(&inner_elem_type_sizes) { + let type_sizes_array_len = self.internal_mem_block_lengths.get(&inner_elem_type_sizes).copied().ok_or_else(|| + InternalError::General { + message: format!("Array {array_id}'s inner element type sizes array does not have a tracked length"), + call_stack: self.acir_context.get_call_stack(), + } + )?; + self.copy_dynamic_array( + inner_elem_type_sizes, + element_type_sizes, + type_sizes_array_len, + )?; + self.internal_mem_block_lengths + .insert(element_type_sizes, type_sizes_array_len); + return Ok(element_type_sizes); + } else { + return Err(InternalError::General { + message: format!("Array {array_id}'s inner element type sizes array should be initialized"), call_stack: self.acir_context.get_call_stack(), } - )?; - self.copy_dynamic_array( - inner_elem_type_sizes, - element_type_sizes, - type_sizes_array_len, - )?; - self.internal_mem_block_lengths - .insert(element_type_sizes, type_sizes_array_len); - return Ok(element_type_sizes); - } else { - return Err(InternalError::General { - message: format!("Array {array_id}'s inner element type sizes array should be initialized"), - call_stack: self.acir_context.get_call_stack(), + .into()); } - .into()); } } AcirValue::Array(values) => { @@ -1209,7 +1244,7 @@ impl Context { // The final array should will the flattened index at each outer array index let init_values = vecmap(flat_elem_type_sizes, |type_size| { - let var = self.acir_context.add_constant(FieldElement::from(type_size as u128)); + let var = self.acir_context.add_constant(type_size); AcirValue::Var(var, AcirType::field()) }); let element_type_sizes_len = init_values.len(); @@ -1266,7 +1301,7 @@ impl Context { array_len: usize, ) -> Result<(), RuntimeError> { let init_values = try_vecmap(0..array_len, |i| { - let index_var = self.acir_context.add_constant(FieldElement::from(i as u128)); + let index_var = self.acir_context.add_constant(i); let read = self.acir_context.read_from_memory(source, &index_var)?; Ok::(AcirValue::Var(read, AcirType::field())) @@ -1282,15 +1317,19 @@ impl Context { var_index: AcirVar, dfg: &DataFlowGraph, ) -> Result { - let element_type_sizes = - self.init_element_type_sizes_array(array_typ, array_id, None, dfg)?; + if !can_omit_element_sizes_array(array_typ) { + let element_type_sizes = + self.init_element_type_sizes_array(array_typ, array_id, None, dfg)?; - let predicate_index = - self.acir_context.mul_var(var_index, self.current_side_effects_enabled_var)?; - let flat_element_size_var = - self.acir_context.read_from_memory(element_type_sizes, &predicate_index)?; + let predicate_index = + self.acir_context.mul_var(var_index, self.current_side_effects_enabled_var)?; - Ok(flat_element_size_var) + self.acir_context + .read_from_memory(element_type_sizes, &predicate_index) + .map_err(RuntimeError::from) + } else { + Ok(var_index) + } } fn flattened_slice_size(&mut self, array_id: ValueId, dfg: &DataFlowGraph) -> usize { @@ -1549,7 +1588,7 @@ impl Context { (_, Type::Function) | (Type::Function, _) => { unreachable!("all functions should be inlined") } - (_, Type::Reference) | (Type::Reference, _) => { + (_, Type::Reference(_)) | (Type::Reference(_), _) => { unreachable!("References are invalid in binary operations") } (_, Type::Array(..)) | (Type::Array(..), _) => { @@ -1623,8 +1662,7 @@ impl Context { ) { // Subtractions must first have the integer modulus added before truncation can be // applied. This is done in order to prevent underflow. - let integer_modulus = - self.acir_context.add_constant(FieldElement::from(2_u128.pow(bit_size))); + let integer_modulus = self.acir_context.add_constant(2_u128.pow(bit_size)); var = self.acir_context.add_var(var, integer_modulus)?; } } @@ -1730,197 +1768,575 @@ impl Context { Intrinsic::ArrayLen => { let len = match self.convert_value(arguments[0], dfg) { AcirValue::Var(_, _) => unreachable!("Non-array passed to array.len() method"), - AcirValue::Array(values) => (values.len() as u128).into(), - AcirValue::DynamicArray(array) => (array.len as u128).into(), + AcirValue::Array(values) => values.len(), + AcirValue::DynamicArray(array) => array.len, }; Ok(vec![AcirValue::Var(self.acir_context.add_constant(len), AcirType::field())]) } Intrinsic::SlicePushBack => { + // arguments = [slice_length, slice_contents, ...elements_to_push] let slice_length = self.convert_value(arguments[0], dfg).into_var()?; - let (array_id, array_typ, _) = + let (slice_contents, slice_typ, _) = self.check_array_is_initialized(arguments[1], dfg)?; - let slice = self.convert_value(arguments[1], dfg); + let slice = self.convert_value(slice_contents, dfg); - // TODO(#3364): make sure that we have handled nested struct inputs - let element = self.convert_value(arguments[2], dfg); - let one = self.acir_context.add_constant(FieldElement::one()); - let new_slice_length = self.acir_context.add_var(slice_length, one)?; + let mut new_elem_size = Self::flattened_value_size(&slice); - // We attach the element no matter what in case len == capacity, as otherwise we - // may get an out of bounds error. let mut new_slice = Vector::new(); - self.slice_intrinsic_input(&mut new_slice, slice.clone())?; - new_slice.push_back(element.clone()); + self.slice_intrinsic_input(&mut new_slice, slice)?; + + let elements_to_push = &arguments[2..]; + // We only fill internal slices for nested slices (a slice inside of a slice). + // So we must directly push back elements for slices which are not a nested slice. + if !slice_typ.is_nested_slice() { + for elem in elements_to_push { + let element = self.convert_value(*elem, dfg); + + new_elem_size += Self::flattened_value_size(&element); + new_slice.push_back(element); + } + } + + // Increase the slice length by one to enable accessing more elements in the slice. + let one = self.acir_context.add_constant(FieldElement::one()); + let new_slice_length = self.acir_context.add_var(slice_length, one)?; - // TODO(#3364): This works for non-nested outputs - let len = Self::flattened_value_size(&slice); - let new_elem_size = Self::flattened_value_size(&element); let new_slice_val = AcirValue::Array(new_slice); let result_block_id = self.block_id(&result_ids[1]); - self.initialize_array( - result_block_id, - len + new_elem_size, - Some(new_slice_val.clone()), - )?; + self.initialize_array(result_block_id, new_elem_size, Some(new_slice_val.clone()))?; + // The previous slice length represents the index we want to write into. let mut var_index = slice_length; - self.array_set_value(element, result_block_id, &mut var_index)?; + // Dynamic arrays are represented as flat memory. We must flatten the user facing index + // to a flattened index that matches the complex slice structure. + if slice_typ.is_nested_slice() { + let element_size = slice_typ.element_size(); + + // Multiply the element size against the var index before fetching the flattened index + // This operation makes sure our user-facing slice index matches the strategy for indexing in SSA, + // which is how `get_flattened_index` expects its index input. + let element_size_var = self.acir_context.add_constant(element_size); + var_index = self.acir_context.mul_var(slice_length, element_size_var)?; + var_index = + self.get_flattened_index(&slice_typ, slice_contents, var_index, dfg)?; + } - let result = AcirValue::DynamicArray(AcirDynamicArray { - block_id: result_block_id, - len: len + new_elem_size, - element_type_sizes: self.init_element_type_sizes_array( - &array_typ, - array_id, + // Write the elements we wish to push back directly. + // The slice's underlying array value should already be filled with dummy data + // to enable this write to be within bounds. + // The dummy data is either attached during SSA gen or in this match case for non-nested slices. + // These values can then be accessed due to the increased dynamic slice length. + for elem in elements_to_push { + let element = self.convert_value(*elem, dfg); + self.array_set_value(&element, result_block_id, &mut var_index)?; + } + + let element_type_sizes = if !can_omit_element_sizes_array(&slice_typ) { + Some(self.init_element_type_sizes_array( + &slice_typ, + slice_contents, Some(new_slice_val), dfg, - )?, + )?) + } else { + None + }; + let result = AcirValue::DynamicArray(AcirDynamicArray { + block_id: result_block_id, + len: new_elem_size, + element_type_sizes, }); Ok(vec![AcirValue::Var(new_slice_length, AcirType::field()), result]) } Intrinsic::SlicePushFront => { + // arguments = [slice_length, slice_contents, ...elements_to_push] let slice_length = self.convert_value(arguments[0], dfg).into_var()?; - let slice: AcirValue = self.convert_value(arguments[1], dfg); - // TODO(#3364): make sure that we have handled nested struct inputs - let element = self.convert_value(arguments[2], dfg); + let (slice_contents, slice_typ, _) = + self.check_array_is_initialized(arguments[1], dfg)?; + let slice: AcirValue = self.convert_value(slice_contents, dfg); + + let mut new_slice_size = Self::flattened_value_size(&slice); + + // Increase the slice length by one to enable accessing more elements in the slice. let one = self.acir_context.add_constant(FieldElement::one()); let new_slice_length = self.acir_context.add_var(slice_length, one)?; let mut new_slice = Vector::new(); self.slice_intrinsic_input(&mut new_slice, slice)?; - new_slice.push_front(element); - Ok(vec![ - AcirValue::Var(new_slice_length, AcirType::field()), - AcirValue::Array(new_slice), - ]) + let elements_to_push = &arguments[2..]; + let mut elem_size = 0; + // We only fill internal slices for nested slices (a slice inside of a slice). + // So we must directly push front elements for slices which are not a nested slice. + if !slice_typ.is_nested_slice() { + for elem in elements_to_push.iter().rev() { + let element = self.convert_value(*elem, dfg); + + elem_size += Self::flattened_value_size(&element); + new_slice.push_front(element); + } + new_slice_size += elem_size; + } else { + // We have already filled the appropriate dummy values for nested slice during SSA gen. + // We need to account for that we do not go out of bounds by removing dummy data as we + // push elements to the front of our slice. + // Using this strategy we are able to avoid dynamic writes like we do for a SlicePushBack. + for elem in elements_to_push.iter().rev() { + let element = self.convert_value(*elem, dfg); + + let elem_size = Self::flattened_value_size(&element); + // Have to pop based off of the flattened value size as we read the + // slice intrinsic as a flat list of AcirValue::Var + for _ in 0..elem_size { + new_slice.pop_back(); + } + new_slice.push_front(element); + } + } + + let new_slice_val = AcirValue::Array(new_slice.clone()); + + let result_block_id = self.block_id(&result_ids[1]); + self.initialize_array( + result_block_id, + new_slice_size, + Some(new_slice_val.clone()), + )?; + + let element_type_sizes = if !can_omit_element_sizes_array(&slice_typ) { + Some(self.init_element_type_sizes_array( + &slice_typ, + slice_contents, + Some(new_slice_val), + dfg, + )?) + } else { + None + }; + let result = AcirValue::DynamicArray(AcirDynamicArray { + block_id: result_block_id, + len: new_slice_size, + element_type_sizes, + }); + + Ok(vec![AcirValue::Var(new_slice_length, AcirType::field()), result]) } Intrinsic::SlicePopBack => { + // arguments = [slice_length, slice_contents] let slice_length = self.convert_value(arguments[0], dfg).into_var()?; - let slice = self.convert_value(arguments[1], dfg); let one = self.acir_context.add_constant(FieldElement::one()); let new_slice_length = self.acir_context.sub_var(slice_length, one)?; - - let (_, _, block_id) = self.check_array_is_initialized(arguments[1], dfg)?; + // For a pop back operation we want to fetch from the `length - 1` as this is the + // last valid index that can be accessed in a slice. After the pop back operation + // the elements stored at that index will no longer be able to be accessed. let mut var_index = new_slice_length; - let elem = self.array_get_value( - &dfg.type_of_value(result_ids[2]), - block_id, - &mut var_index, - &[], - )?; - // TODO(#3364): make sure that we have handled nested struct inputs + let (slice_contents, slice_typ, block_id) = + self.check_array_is_initialized(arguments[1], dfg)?; + let slice = self.convert_value(slice_contents, dfg); + + let element_size = slice_typ.element_size(); + + let mut popped_elements = Vec::new(); + // Fetch the values we are popping off of the slice. + // In the case of non-nested slice the logic is simple as we do not + // need to account for the internal slice sizes or flattening the index. + // + // The pop back operation results are of the format [slice length, slice contents, popped elements]. + // Thus, we look at the result ids at index 2 and onwards to determine the type of each popped element. + if !slice_typ.is_nested_slice() { + for res in &result_ids[2..] { + let elem = self.array_get_value( + &dfg.type_of_value(*res), + block_id, + &mut var_index, + &[], + )?; + popped_elements.push(elem); + } + } else { + // Fetch the slice sizes of the nested slice. + let slice_sizes = self.slice_sizes.get(&slice_contents); + let mut slice_sizes = + slice_sizes.expect("ICE: should have slice sizes").clone(); + // We want to remove the parent size as we are fetching the child + slice_sizes.remove(0); + + // Multiply the element size against the var index before fetching the flattened index + // This operation makes sure our user-facing slice index matches the strategy for indexing in SSA, + // which is how `get_flattened_index` expects its index input. + let element_size_var = self.acir_context.add_constant(element_size); + // We want to use an index one less than the slice length + var_index = self.acir_context.mul_var(var_index, element_size_var)?; + var_index = + self.get_flattened_index(&slice_typ, slice_contents, var_index, dfg)?; + + for res in &result_ids[2..] { + let elem = self.array_get_value( + &dfg.type_of_value(*res), + block_id, + &mut var_index, + &slice_sizes, + )?; + popped_elements.push(elem); + } + } + let mut new_slice = Vector::new(); self.slice_intrinsic_input(&mut new_slice, slice)?; - Ok(vec![ + let mut results = vec![ AcirValue::Var(new_slice_length, AcirType::field()), AcirValue::Array(new_slice), - elem, - ]) + ]; + results.append(&mut popped_elements); + + Ok(results) } Intrinsic::SlicePopFront => { + // arguments = [slice_length, slice_contents] let slice_length = self.convert_value(arguments[0], dfg).into_var()?; - let slice = self.convert_value(arguments[1], dfg); + + let (slice_contents, slice_typ, block_id) = + self.check_array_is_initialized(arguments[1], dfg)?; + let slice = self.convert_value(slice_contents, dfg); let one = self.acir_context.add_constant(FieldElement::one()); let new_slice_length = self.acir_context.sub_var(slice_length, one)?; let mut new_slice = Vector::new(); self.slice_intrinsic_input(&mut new_slice, slice)?; - // TODO(#3364): make sure that we have handled nested struct inputs - let elem = new_slice - .pop_front() - .expect("There are no elements in this slice to be removed"); - Ok(vec![ - elem, - AcirValue::Var(new_slice_length, AcirType::field()), - AcirValue::Array(new_slice), - ]) + let element_size = slice_typ.element_size(); + + let mut popped_elements: Vec = Vec::new(); + let mut popped_elements_size = 0; + let mut var_index = self.acir_context.add_constant(FieldElement::zero()); + // Fetch the values we are popping off of the slice. + // In the case of non-nested slice the logic is simple as we do not + // need to account for the internal slice sizes or flattening the index. + // + // The pop front operation results are of the format [popped elements, slice length, slice contents]. + // Thus, we look at the result ids up to the element size to determine the type of each popped element. + if !slice_typ.is_nested_slice() { + for res in &result_ids[..element_size] { + let element = self.array_get_value( + &dfg.type_of_value(*res), + block_id, + &mut var_index, + &[], + )?; + let elem_size = Self::flattened_value_size(&element); + popped_elements_size += elem_size; + popped_elements.push(element); + } + } else { + let slice_sizes = self.slice_sizes.get(&slice_contents); + let mut slice_sizes = + slice_sizes.expect("ICE: should have slice sizes").clone(); + // We want to remove the parent size as we are fetching the child + slice_sizes.remove(0); + + for res in &result_ids[..element_size] { + let element = self.array_get_value( + &dfg.type_of_value(*res), + block_id, + &mut var_index, + &slice_sizes, + )?; + let elem_size = Self::flattened_value_size(&element); + popped_elements_size += elem_size; + popped_elements.push(element); + } + } + // It is expected that the `popped_elements_size` is the flattened size of the elements, + // as the input slice should be a dynamic array which is represented by flat memory. + new_slice = new_slice.slice(popped_elements_size..); + + popped_elements.push(AcirValue::Var(new_slice_length, AcirType::field())); + popped_elements.push(AcirValue::Array(new_slice)); + + Ok(popped_elements) } Intrinsic::SliceInsert => { - // Slice insert with a constant index + // arguments = [slice_length, slice_contents, insert_index, ...elements_to_insert] let slice_length = self.convert_value(arguments[0], dfg).into_var()?; - let slice = self.convert_value(arguments[1], dfg); - let index = self.convert_value(arguments[2], dfg).into_var()?; - let element = self.convert_value(arguments[3], dfg); + + let (slice_contents, slice_typ, block_id) = + self.check_array_is_initialized(arguments[1], dfg)?; + + let slice = self.convert_value(slice_contents, dfg); + let insert_index = self.convert_value(arguments[2], dfg).into_var()?; let one = self.acir_context.add_constant(FieldElement::one()); let new_slice_length = self.acir_context.add_var(slice_length, one)?; - // TODO(#2462): Slice insert is a little less obvious on how to implement due to the case - // of having a dynamic index - // The slice insert logic will need a more involved codegen - let index = self.acir_context.var_to_expression(index)?.to_const(); - let index = index - .expect("ICE: slice length should be fully tracked and constant by ACIR gen"); - let index = index.to_u128() as usize; + let slice_size = Self::flattened_value_size(&slice); + + // Fetch the flattened index from the user provided index argument. + let element_size = slice_typ.element_size(); + let element_size_var = self.acir_context.add_constant(element_size); + let flat_insert_index = + self.acir_context.mul_var(insert_index, element_size_var)?; + let flat_user_index = + self.get_flattened_index(&slice_typ, slice_contents, flat_insert_index, dfg)?; + + let elements_to_insert = &arguments[3..]; + // Determine the elements we need to write into our resulting dynamic array. + // We need to a fully flat list of AcirVar's as a dynamic array is represented with flat memory. + let mut inner_elem_size_usize = 0; + let mut flattened_elements = Vec::new(); + for elem in elements_to_insert { + let element = self.convert_value(*elem, dfg); + let elem_size = Self::flattened_value_size(&element); + inner_elem_size_usize += elem_size; + let mut flat_elem = element.flatten().into_iter().map(|(var, _)| var).collect(); + flattened_elements.append(&mut flat_elem); + } + let inner_elem_size = self.acir_context.add_constant(inner_elem_size_usize); + // Set the maximum flattened index at which a new element should be inserted. + let max_flat_user_index = + self.acir_context.add_var(flat_user_index, inner_elem_size)?; + + // Go through the entire slice argument and determine what value should be written to the new slice. + // 1. If we are below the starting insertion index we should insert the value that was already + // in the original slice. + // 2. If we are above the starting insertion index but below the max insertion index we should insert + // the flattened element arguments. + // 3. If we are above the max insertion index we should insert the previous value from the original slice, + // as during an insertion we want to shift all elements after the insertion up an index. + let result_block_id = self.block_id(&result_ids[1]); + self.initialize_array(result_block_id, slice_size, None)?; + let mut current_insert_index = 0; + for i in 0..slice_size { + let current_index = self.acir_context.add_constant(i); + + // Check that we are above the lower bound of the insertion index + let greater_eq_than_idx = self.acir_context.more_than_eq_var( + current_index, + flat_user_index, + 64, + self.current_side_effects_enabled_var, + )?; + // Check that we are below the upper bound of the insertion index + let less_than_idx = self.acir_context.less_than_var( + current_index, + max_flat_user_index, + 64, + self.current_side_effects_enabled_var, + )?; + + // Read from the original slice the value we want to insert into our new slice. + // We need to make sure of two things: + // 1. That we read the previous element for when we have an index greater than insertion index. + // 2. That the index we are reading from is within the array bounds + let shifted_index = if i < inner_elem_size_usize { + current_index + } else { + let index_minus_elem_size = + self.acir_context.add_constant(i - inner_elem_size_usize); - let mut new_slice = Vector::new(); - self.slice_intrinsic_input(&mut new_slice, slice)?; + let use_shifted_index_pred = self + .acir_context + .mul_var(index_minus_elem_size, greater_eq_than_idx)?; - // We do not return an index out of bounds error directly here - // as the length of the slice is dynamic, and length of `new_slice` - // represents the capacity of the slice, not the actual length. - // - // Constraints should be generated during SSA gen to tell the user - // they are attempting to insert at too large of an index. - // This check prevents a panic inside of the im::Vector insert method. - if index <= new_slice.len() { - // TODO(#3364): make sure that we have handled nested struct inputs - new_slice.insert(index, element); + let not_pred = self.acir_context.sub_var(one, greater_eq_than_idx)?; + let use_current_index_pred = + self.acir_context.mul_var(not_pred, current_index)?; + + self.acir_context.add_var(use_shifted_index_pred, use_current_index_pred)? + }; + + let value_shifted_index = + self.acir_context.read_from_memory(block_id, &shifted_index)?; + + // Final predicate to determine whether we are within the insertion bounds + let should_insert_value_pred = + self.acir_context.mul_var(greater_eq_than_idx, less_than_idx)?; + let insert_value_pred = self.acir_context.mul_var( + flattened_elements[current_insert_index], + should_insert_value_pred, + )?; + + let not_pred = self.acir_context.sub_var(one, should_insert_value_pred)?; + let shifted_value_pred = + self.acir_context.mul_var(not_pred, value_shifted_index)?; + + let new_value = + self.acir_context.add_var(insert_value_pred, shifted_value_pred)?; + + self.acir_context.write_to_memory( + result_block_id, + ¤t_index, + &new_value, + )?; + + current_insert_index += 1; + if inner_elem_size_usize == current_insert_index { + current_insert_index = 0; + } } - Ok(vec![ - AcirValue::Var(new_slice_length, AcirType::field()), - AcirValue::Array(new_slice), - ]) + // let new_slice_val = AcirValue::Array(new_slice); + let element_type_sizes = if !can_omit_element_sizes_array(&slice_typ) { + Some(self.init_element_type_sizes_array( + &slice_typ, + slice_contents, + Some(slice), + dfg, + )?) + } else { + None + }; + let result = AcirValue::DynamicArray(AcirDynamicArray { + block_id: result_block_id, + len: slice_size, + element_type_sizes, + }); + + Ok(vec![AcirValue::Var(new_slice_length, AcirType::field()), result]) } Intrinsic::SliceRemove => { - // Slice insert with a constant index + // arguments = [slice_length, slice_contents, remove_index] let slice_length = self.convert_value(arguments[0], dfg).into_var()?; - let slice = self.convert_value(arguments[1], dfg); - let index = self.convert_value(arguments[2], dfg).into_var()?; + + let (slice_contents, slice_typ, block_id) = + self.check_array_is_initialized(arguments[1], dfg)?; + + let slice = self.convert_value(slice_contents, dfg); + let remove_index = self.convert_value(arguments[2], dfg).into_var()?; let one = self.acir_context.add_constant(FieldElement::one()); let new_slice_length = self.acir_context.sub_var(slice_length, one)?; - // TODO(#2462): allow slice remove with a constant index - // Slice remove is a little less obvious on how to implement due to the case - // of having a dynamic index - // The slice remove logic will need a more involved codegen - let index = self.acir_context.var_to_expression(index)?.to_const(); - let index = index - .expect("ICE: slice length should be fully tracked and constant by ACIR gen"); - let index = index.to_u128() as usize; + let slice_size = Self::flattened_value_size(&slice); let mut new_slice = Vector::new(); self.slice_intrinsic_input(&mut new_slice, slice)?; - // We do not return an index out of bounds error directly here - // as the length of the slice is dynamic, and length of `new_slice` - // represents the capacity of the slice, not the actual length. - // - // Constraints should be generated during SSA gen to tell the user - // they are attempting to remove at too large of an index. - // This check prevents a panic inside of the im::Vector remove method. - let removed_elem = if index < new_slice.len() { - // TODO(#3364): make sure that we have handled nested struct inputs - new_slice.remove(index) + // Compiler sanity check + assert_eq!( + new_slice.len(), + slice_size, + "ICE: The read flattened slice should match the computed size" + ); + + // Fetch the flattened index from the user provided index argument. + let element_size = slice_typ.element_size(); + let element_size_var = self.acir_context.add_constant(element_size); + let flat_remove_index = + self.acir_context.mul_var(remove_index, element_size_var)?; + let flat_user_index = + self.get_flattened_index(&slice_typ, slice_contents, flat_remove_index, dfg)?; + + // Fetch the values we are remove from the slice. + // In the case of non-nested slice the logic is simple as we do not + // need to account for the internal slice sizes or flattening the index. + // As we fetch the values we can determine the size of the removed values + // which we will later use for writing the correct resulting slice. + let mut popped_elements = Vec::new(); + let mut popped_elements_size = 0; + // Set a temp index just for fetching from the original slice as `array_get_value` mutates + // the index internally. + let mut temp_index = flat_user_index; + if !slice_typ.is_nested_slice() { + for res in &result_ids[2..(2 + element_size)] { + let element = self.array_get_value( + &dfg.type_of_value(*res), + block_id, + &mut temp_index, + &[], + )?; + let elem_size = Self::flattened_value_size(&element); + popped_elements_size += elem_size; + popped_elements.push(element); + } } else { - // This is a dummy value which should never be used if the appropriate - // slice access checks are generated before this slice remove call. - AcirValue::Var(slice_length, AcirType::field()) + let slice_sizes = self.slice_sizes.get(&slice_contents); + let mut slice_sizes = + slice_sizes.expect("ICE: should have slice sizes").clone(); + // We want to remove the parent size as we are fetching the child + slice_sizes.remove(0); + + for res in &result_ids[2..(2 + element_size)] { + let element = self.array_get_value( + &dfg.type_of_value(*res), + block_id, + &mut temp_index, + &slice_sizes, + )?; + let elem_size = Self::flattened_value_size(&element); + popped_elements_size += elem_size; + popped_elements.push(element); + } + } + + // Go through the entire slice argument and determine what value should be written to the new slice. + // 1. If the current index is greater than the removal index we must write the next value + // from the original slice to the current index + // 2. At the end of the slice reading from the next value of the original slice + // can lead to a potential out of bounds error. In this case we just fetch from the original slice + // at the current index. As we are decreasing the slice in length, this is a safe operation. + let result_block_id = self.block_id(&result_ids[1]); + self.initialize_array(result_block_id, slice_size, None)?; + for i in 0..slice_size { + let current_index = self.acir_context.add_constant(i); + + let shifted_index = if (i + popped_elements_size) >= slice_size { + current_index + } else { + self.acir_context.add_constant(i + popped_elements_size) + }; + + let value_shifted_index = + self.acir_context.read_from_memory(block_id, &shifted_index)?; + let value_current_index = new_slice[i].borrow_var()?; + + let use_shifted_value = self.acir_context.more_than_eq_var( + current_index, + flat_user_index, + 64, + self.current_side_effects_enabled_var, + )?; + + let shifted_value_pred = + self.acir_context.mul_var(value_shifted_index, use_shifted_value)?; + let not_pred = self.acir_context.sub_var(one, use_shifted_value)?; + let current_value_pred = + self.acir_context.mul_var(not_pred, value_current_index)?; + + let new_value = + self.acir_context.add_var(shifted_value_pred, current_value_pred)?; + + self.acir_context.write_to_memory( + result_block_id, + ¤t_index, + &new_value, + )?; + } + + let new_slice_val = AcirValue::Array(new_slice); + let element_type_sizes = if !can_omit_element_sizes_array(&slice_typ) { + Some(self.init_element_type_sizes_array( + &slice_typ, + slice_contents, + Some(new_slice_val), + dfg, + )?) + } else { + None }; + let result = AcirValue::DynamicArray(AcirDynamicArray { + block_id: result_block_id, + len: slice_size, + element_type_sizes, + }); - Ok(vec![ - AcirValue::Var(new_slice_length, AcirType::field()), - AcirValue::Array(new_slice), - removed_elem, - ]) + let mut result = vec![AcirValue::Var(new_slice_length, AcirType::field()), result]; + result.append(&mut popped_elements); + + Ok(result) } _ => todo!("expected a black box function"), } @@ -1943,7 +2359,7 @@ impl Context { AcirValue::DynamicArray(AcirDynamicArray { block_id, len, .. }) => { for i in 0..len { // We generate witnesses corresponding to the array values - let index_var = self.acir_context.add_constant(FieldElement::from(i as u128)); + let index_var = self.acir_context.add_constant(i); let value_read_var = self.acir_context.read_from_memory(block_id, &index_var)?; @@ -2025,3 +2441,16 @@ impl Context { } } } + +// We can omit the element size array for arrays which have elements of size 1 and do not contain slices. +// TODO: remove restriction on size 1 elements. +fn can_omit_element_sizes_array(array_typ: &Type) -> bool { + if array_typ.contains_slice_element() { + return false; + } + let Type::Array(types, _) = array_typ else { + panic!("ICE: expected array type"); + }; + + types.len() == 1 && types[0].flattened_size() == 1 +} diff --git a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs index 058f91adacb..e01e1fe1a1d 100644 --- a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs @@ -170,8 +170,9 @@ impl FunctionBuilder { /// Insert an allocate instruction at the end of the current block, allocating the /// given amount of field elements. Returns the result of the allocate instruction, /// which is always a Reference to the allocated data. - pub(crate) fn insert_allocate(&mut self) -> ValueId { - self.insert_instruction(Instruction::Allocate, None).first() + pub(crate) fn insert_allocate(&mut self, element_type: Type) -> ValueId { + let reference_type = Type::Reference(Rc::new(element_type)); + self.insert_instruction(Instruction::Allocate, Some(vec![reference_type])).first() } pub(crate) fn set_location(&mut self, location: Location) -> &mut FunctionBuilder { @@ -260,22 +261,6 @@ 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() } @@ -290,50 +275,53 @@ impl FunctionBuilder { /// Insert ssa instructions which computes lhs << rhs by doing lhs*2^rhs /// and truncate the result to bit_size - fn insert_wrapping_shift_left( + pub(crate) fn insert_wrapping_shift_left( &mut self, lhs: ValueId, rhs: ValueId, bit_size: u32, - ) -> InsertInstructionResult { + ) -> ValueId { 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 (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_u128.overflowing_pow(rhs_constant.to_u128() as u32); + if overflows { + assert!(bit_size < 128, "ICE - shift left with big integers are not supported"); + if bit_size < 128 { + let zero = self.numeric_constant(FieldElement::zero(), typ); + return InsertInstructionResult::SimplifiedTo(zero).first(); + } + } + let pow = self.numeric_constant(FieldElement::from(rhs_bit_size_pow_2), 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) + self.insert_instruction(instruction, None).first() } else { let result = self.insert_instruction(instruction, None).first(); self.insert_instruction( Instruction::Truncate { value: result, bit_size, max_bit_size: max_bit }, None, ) + .first() } } @@ -471,6 +459,27 @@ impl FunctionBuilder { _ => None, } } + + /// Insert instructions to increment the reference count of any array(s) stored + /// within the given value. If the given value is not an array and does not contain + /// any arrays, this does nothing. + pub(crate) fn increment_array_reference_count(&mut self, value: ValueId) { + match self.type_of_value(value) { + Type::Numeric(_) => (), + Type::Function => (), + Type::Reference(element) => { + if element.contains_an_array() { + let value = self.insert_load(value, element.as_ref().clone()); + self.increment_array_reference_count(value); + } + } + Type::Array(..) | Type::Slice(..) => { + self.insert_instruction(Instruction::IncrementRc { value }, None); + // If there are nested arrays or slices, we wait until ArrayGet + // is issued to increment the count of that array. + } + } + } } impl std::ops::Index for FunctionBuilder { diff --git a/compiler/noirc_evaluator/src/ssa/ir/dfg.rs b/compiler/noirc_evaluator/src/ssa/ir/dfg.rs index 3cb6736007d..75b2cf962f7 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/dfg.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/dfg.rs @@ -318,7 +318,7 @@ impl DataFlowGraph { /// True if the type of this value is Type::Reference. /// Using this method over type_of_value avoids cloning the value's type. pub(crate) fn value_is_reference(&self, value: ValueId) -> bool { - matches!(self.values[value].get_type(), Type::Reference) + matches!(self.values[value].get_type(), Type::Reference(_)) } /// Appends a result type to the instruction. @@ -521,13 +521,13 @@ impl<'dfg> InsertInstructionResult<'dfg> { #[cfg(test)] mod tests { use super::DataFlowGraph; - use crate::ssa::ir::instruction::Instruction; + use crate::ssa::ir::{instruction::Instruction, types::Type}; #[test] fn make_instruction() { let mut dfg = DataFlowGraph::default(); let ins = Instruction::Allocate; - let ins_id = dfg.make_instruction(ins, None); + let ins_id = dfg.make_instruction(ins, Some(vec![Type::field()])); let results = dfg.instruction_results(ins_id); assert_eq!(results.len(), 1); diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs index 71e773e3f70..63b32766f62 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs @@ -46,7 +46,6 @@ pub(crate) enum Intrinsic { BlackBox(BlackBoxFunc), FromField, AsField, - WrappingShiftLeft, } impl std::fmt::Display for Intrinsic { @@ -69,7 +68,6 @@ 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"), } } } @@ -94,8 +92,7 @@ impl Intrinsic { | Intrinsic::ToBits(_) | Intrinsic::ToRadix(_) | Intrinsic::FromField - | Intrinsic::AsField - | Intrinsic::WrappingShiftLeft => false, + | Intrinsic::AsField => false, // Some black box functions have side-effects Intrinsic::BlackBox(func) => matches!(func, BlackBoxFunc::RecursiveAggregation), @@ -122,7 +119,6 @@ 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), } } @@ -186,6 +182,13 @@ pub(crate) enum Instruction { /// Creates a new array with the new value at the given index. All other elements are identical /// to those in the given array. This will not modify the original array. ArraySet { array: ValueId, index: ValueId, value: ValueId }, + + /// An instruction to increment the reference count of a value. + /// + /// This currently only has an effect in Brillig code where array sharing and copy on write is + /// implemented via reference counting. In ACIR code this is done with im::Vector and these + /// IncrementRc instructions are ignored. + IncrementRc { value: ValueId }, } impl Instruction { @@ -199,18 +202,19 @@ impl Instruction { match self { Instruction::Binary(binary) => binary.result_type(), Instruction::Cast(_, typ) => InstructionResultType::Known(typ.clone()), - Instruction::Allocate { .. } => InstructionResultType::Known(Type::Reference), Instruction::Not(value) | Instruction::Truncate { value, .. } => { InstructionResultType::Operand(*value) } Instruction::ArraySet { array, .. } => InstructionResultType::Operand(*array), Instruction::Constrain(..) | Instruction::Store { .. } - | Instruction::EnableSideEffects { .. } - | Instruction::RangeCheck { .. } => InstructionResultType::None, - Instruction::Load { .. } | Instruction::ArrayGet { .. } | Instruction::Call { .. } => { - InstructionResultType::Unknown - } + | Instruction::IncrementRc { .. } + | Instruction::RangeCheck { .. } + | Instruction::EnableSideEffects { .. } => InstructionResultType::None, + Instruction::Allocate { .. } + | Instruction::Load { .. } + | Instruction::ArrayGet { .. } + | Instruction::Call { .. } => InstructionResultType::Unknown, } } @@ -239,6 +243,7 @@ impl Instruction { | Allocate | Load { .. } | Store { .. } + | IncrementRc { .. } | RangeCheck { .. } => false, Call { func, .. } => match dfg[*func] { @@ -270,7 +275,11 @@ impl Instruction { | ArrayGet { .. } | ArraySet { .. } => false, - Constrain(..) | Store { .. } | EnableSideEffects { .. } | RangeCheck { .. } => true, + Constrain(..) + | Store { .. } + | EnableSideEffects { .. } + | IncrementRc { .. } + | RangeCheck { .. } => true, // Some `Intrinsic`s have side effects so we must check what kind of `Call` this is. Call { func, .. } => match dfg[*func] { @@ -327,6 +336,7 @@ impl Instruction { Instruction::ArraySet { array, index, value } => { Instruction::ArraySet { array: f(*array), index: f(*index), value: f(*value) } } + Instruction::IncrementRc { value } => Instruction::IncrementRc { value: f(*value) }, Instruction::RangeCheck { value, max_bit_size, assert_message } => { Instruction::RangeCheck { value: f(*value), @@ -378,7 +388,7 @@ impl Instruction { Instruction::EnableSideEffects { condition } => { f(*condition); } - Instruction::RangeCheck { value, .. } => { + Instruction::IncrementRc { value } | Instruction::RangeCheck { value, .. } => { f(*value); } } @@ -478,6 +488,7 @@ impl Instruction { Instruction::Allocate { .. } => None, Instruction::Load { .. } => None, Instruction::Store { .. } => None, + Instruction::IncrementRc { .. } => None, Instruction::RangeCheck { value, max_bit_size, .. } => { if let Some(numeric_constant) = dfg.get_numeric_constant(*value) { if numeric_constant.num_bits() < *max_bit_size { diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs index da5544d7dc6..b07e2df7bd3 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs @@ -245,9 +245,6 @@ 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") - } } } diff --git a/compiler/noirc_evaluator/src/ssa/ir/printer.rs b/compiler/noirc_evaluator/src/ssa/ir/printer.rs index 51e436643ab..2899b987c1d 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/printer.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/printer.rs @@ -172,8 +172,11 @@ pub(crate) fn display_instruction( show(*value) ) } + Instruction::IncrementRc { value } => { + writeln!(f, "inc_rc {}", show(*value)) + } Instruction::RangeCheck { value, max_bit_size, .. } => { - write!(f, "range_check {} to {} bits", show(*value), *max_bit_size,) + writeln!(f, "range_check {} to {} bits", show(*value), *max_bit_size,) } } } diff --git a/compiler/noirc_evaluator/src/ssa/ir/types.rs b/compiler/noirc_evaluator/src/ssa/ir/types.rs index 7fe0713e748..fbc95a16387 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/types.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/types.rs @@ -25,7 +25,7 @@ pub(crate) enum Type { Numeric(NumericType), /// A reference to some value, such as an array - Reference, + Reference(Rc), /// An immutable array value with the given element type and length Array(Rc, usize), @@ -86,24 +86,39 @@ impl Type { } Type::Slice(_) => true, Type::Numeric(_) => false, - Type::Reference => false, + Type::Reference(_) => false, Type::Function => false, } } /// Returns the flattened size of a Type pub(crate) fn flattened_size(&self) -> usize { - let mut size = 0; match self { Type::Array(elements, len) => { - size = elements.iter().fold(size, |sum, elem| sum + (elem.flattened_size() * len)); + elements.iter().fold(0, |sum, elem| sum + (elem.flattened_size() * len)) } Type::Slice(_) => { unimplemented!("ICE: cannot fetch flattened slice size"); } - _ => size += 1, + _ => 1, + } + } + + pub(crate) fn is_nested_slice(&self) -> bool { + if let Type::Slice(element_types) = self { + element_types.as_ref().iter().any(|typ| typ.contains_slice_element()) + } else { + false + } + } + + /// True if this type is an array (or slice) or internally contains an array (or slice) + pub(crate) fn contains_an_array(&self) -> bool { + match self { + Type::Numeric(_) | Type::Function => false, + Type::Array(_, _) | Type::Slice(_) => true, + Type::Reference(element) => element.contains_an_array(), } - size } } @@ -136,7 +151,7 @@ impl std::fmt::Display for Type { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Type::Numeric(numeric) => numeric.fmt(f), - Type::Reference => write!(f, "reference"), + Type::Reference(element) => write!(f, "&mut {element}"), Type::Array(element, length) => { let elements = vecmap(element.iter(), |element| element.to_string()); write!(f, "[{}; {length}]", elements.join(", ")) diff --git a/compiler/noirc_evaluator/src/ssa/opt/die.rs b/compiler/noirc_evaluator/src/ssa/opt/die.rs index 3db95f6ad99..53cdf72bbbf 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/die.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/die.rs @@ -7,7 +7,7 @@ use crate::ssa::{ basic_block::{BasicBlock, BasicBlockId}, dfg::DataFlowGraph, function::Function, - instruction::InstructionId, + instruction::{Instruction, InstructionId}, post_order::PostOrder, value::{Value, ValueId}, }, @@ -38,6 +38,8 @@ fn dead_instruction_elimination(function: &mut Function) { for block in blocks.as_slice() { context.remove_unused_instructions_in_block(function, *block); } + + context.remove_increment_rc_instructions(&mut function.dfg); } /// Per function context for tracking unused values and which instructions to remove. @@ -45,6 +47,11 @@ fn dead_instruction_elimination(function: &mut Function) { struct Context { used_values: HashSet, instructions_to_remove: HashSet, + + /// IncrementRc instructions must be revisited after the main DIE pass since + /// they are technically side-effectful but we stil want to remove them if their + /// `value` parameter is not used elsewhere. + increment_rc_instructions: Vec<(InstructionId, BasicBlockId)>, } impl Context { @@ -67,14 +74,19 @@ impl Context { let block = &function.dfg[block_id]; self.mark_terminator_values_as_used(function, block); - for instruction in block.instructions().iter().rev() { - if self.is_unused(*instruction, function) { - self.instructions_to_remove.insert(*instruction); + for instruction_id in block.instructions().iter().rev() { + if self.is_unused(*instruction_id, function) { + self.instructions_to_remove.insert(*instruction_id); } else { - let instruction = &function.dfg[*instruction]; - instruction.for_each_value(|value| { - self.mark_used_instruction_results(&function.dfg, value); - }); + let instruction = &function.dfg[*instruction_id]; + + if let Instruction::IncrementRc { .. } = instruction { + self.increment_rc_instructions.push((*instruction_id, block_id)); + } else { + instruction.for_each_value(|value| { + self.mark_used_instruction_results(&function.dfg, value); + }); + } } } @@ -119,11 +131,28 @@ impl Context { self.mark_used_instruction_results(dfg, *elem); } } + Value::Param { .. } => { + self.used_values.insert(value_id); + } _ => { // Does not comprise of any instruction results } } } + + fn remove_increment_rc_instructions(self, dfg: &mut DataFlowGraph) { + for (increment_rc, block) in self.increment_rc_instructions { + let value = match &dfg[increment_rc] { + Instruction::IncrementRc { value } => *value, + other => unreachable!("Expected IncrementRc instruction, found {other:?}"), + }; + + // This could be more efficient if we have to remove multiple IncrementRcs in a single block + if !self.used_values.contains(&value) { + dfg[block].instructions_mut().retain(|instruction| *instruction != increment_rc); + } + } + } } #[cfg(test)] @@ -176,10 +205,10 @@ mod test { builder.switch_to_block(b1); let _v3 = builder.add_block_parameter(b1, Type::field()); - let v4 = builder.insert_allocate(); + let v4 = builder.insert_allocate(Type::field()); let _v5 = builder.insert_load(v4, Type::field()); - let v6 = builder.insert_allocate(); + let v6 = builder.insert_allocate(Type::field()); builder.insert_store(v6, one); let v7 = builder.insert_load(v6, Type::field()); let v8 = builder.insert_binary(v7, BinaryOp::Add, one); diff --git a/compiler/noirc_evaluator/src/ssa/opt/fill_internal_slices.rs b/compiler/noirc_evaluator/src/ssa/opt/fill_internal_slices.rs index d12b01b9196..fede0e540e2 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/fill_internal_slices.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/fill_internal_slices.rs @@ -46,7 +46,7 @@ use crate::ssa::{ ir::{ basic_block::BasicBlockId, dfg::CallStack, - function::Function, + function::{Function, RuntimeType}, function_inserter::FunctionInserter, instruction::{Instruction, InstructionId, Intrinsic}, post_order::PostOrder, @@ -62,8 +62,14 @@ use fxhash::FxHashMap as HashMap; impl Ssa { pub(crate) fn fill_internal_slices(mut self) -> Ssa { for function in self.functions.values_mut() { - let mut context = Context::new(function); - context.process_blocks(); + // This pass is only necessary for generating ACIR and thus we should not + // process Brillig functions. + // The pass is also currently only setup to handle a function with a single flattened block. + // For complex Brillig functions we can expect this pass to panic. + if function.runtime() == RuntimeType::Acir { + let mut context = Context::new(function); + context.process_blocks(); + } } self } @@ -168,6 +174,9 @@ impl<'f> Context<'f> { panic!("ICE: should have inner slice set for {slice_value}") }); slice_sizes.insert(results[0], inner_slice.clone()); + if slice_value != results[0] { + self.mapped_slice_values.insert(slice_value, results[0]); + } } } } @@ -192,17 +201,11 @@ impl<'f> Context<'f> { let inner_sizes = slice_sizes.get_mut(array).expect("ICE expected slice sizes"); inner_sizes.1.push(*value); - - let value_parent = self.resolve_slice_parent(*value); - if slice_values.contains(&value_parent) { - // Map the value parent to the current array in case nested slices - // from the current array are set to larger values later in the program - self.mapped_slice_values.insert(value_parent, *array); - } } if let Some(inner_sizes) = slice_sizes.get_mut(array) { let inner_sizes = inner_sizes.clone(); + slice_sizes.insert(results[0], inner_sizes); self.mapped_slice_values.insert(*array, results[0]); @@ -218,14 +221,27 @@ impl<'f> Context<'f> { | Intrinsic::SlicePopBack | Intrinsic::SliceInsert | Intrinsic::SliceRemove => (1, 1), - Intrinsic::SlicePopFront => (1, 2), + // `pop_front` returns the popped element, and then the respective slice. + // This means in the case of a slice with structs, the result index of the popped slice + // will change depending on the number of elements in the struct. + // For example, a slice with four elements will look as such in SSA: + // v3, v4, v5, v6, v7, v8 = call slice_pop_front(v1, v2) + // where v7 is the slice length and v8 is the popped slice itself. + Intrinsic::SlicePopFront => (1, results.len() - 1), _ => return, }; + let slice_contents = arguments[argument_index]; match intrinsic { Intrinsic::SlicePushBack | Intrinsic::SlicePushFront | Intrinsic::SliceInsert => { - let slice_contents = arguments[argument_index]; + for arg in &arguments[(argument_index + 1)..] { + let element_typ = self.inserter.function.dfg.type_of_value(*arg); + if element_typ.contains_slice_element() { + slice_values.push(*arg); + self.compute_slice_sizes(*arg, slice_sizes); + } + } if let Some(inner_sizes) = slice_sizes.get_mut(&slice_contents) { inner_sizes.0 += 1; @@ -234,12 +250,12 @@ impl<'f> Context<'f> { self.mapped_slice_values .insert(slice_contents, results[result_index]); + self.slice_parents.insert(results[result_index], slice_contents); } } Intrinsic::SlicePopBack - | Intrinsic::SlicePopFront - | Intrinsic::SliceRemove => { - let slice_contents = arguments[argument_index]; + | Intrinsic::SliceRemove + | Intrinsic::SlicePopFront => { // We do not decrement the size on intrinsics that could remove values from a slice. // This is because we could potentially go back to the smaller slice and not fill in dummies. // This pass should be tracking the potential max that a slice ***could be*** @@ -249,6 +265,7 @@ impl<'f> Context<'f> { self.mapped_slice_values .insert(slice_contents, results[result_index]); + self.slice_parents.insert(results[result_index], slice_contents); } } _ => {} @@ -271,7 +288,6 @@ impl<'f> Context<'f> { if slice_values.contains(array) { let (new_array_op_instr, call_stack) = self.get_updated_array_op_instr(*array, slice_sizes, instruction); - self.inserter.push_instruction_value( new_array_op_instr, instruction, @@ -282,6 +298,55 @@ impl<'f> Context<'f> { self.inserter.push_instruction(instruction, block); } } + Instruction::Call { func: _, arguments } => { + let mut args_to_replace = Vec::new(); + for (i, arg) in arguments.iter().enumerate() { + let element_typ = self.inserter.function.dfg.type_of_value(*arg); + if slice_values.contains(arg) && element_typ.contains_slice_element() { + args_to_replace.push((i, *arg)); + } + } + if args_to_replace.is_empty() { + self.inserter.push_instruction(instruction, block); + } else { + // Using the original slice is ok to do as during collection of slice information + // we guarantee that only the arguments to slice intrinsic calls can be replaced. + let slice_contents = arguments[1]; + + let element_typ = self.inserter.function.dfg.type_of_value(arguments[1]); + let elem_depth = Self::compute_nested_slice_depth(&element_typ); + + let mut max_sizes = Vec::new(); + max_sizes.resize(elem_depth, 0); + // We want the max for the parent of the argument + let parent = self.resolve_slice_parent(slice_contents); + self.compute_slice_max_sizes(parent, slice_sizes, &mut max_sizes, 0); + + for (index, arg) in args_to_replace { + let element_typ = self.inserter.function.dfg.type_of_value(arg); + max_sizes.remove(0); + let new_array = + self.attach_slice_dummies(&element_typ, Some(arg), false, &max_sizes); + + let instruction_id = instruction; + let (instruction, call_stack) = + self.inserter.map_instruction(instruction_id); + let new_call_instr = match instruction { + Instruction::Call { func, mut arguments } => { + arguments[index] = new_array; + Instruction::Call { func, arguments } + } + _ => panic!("Expected call instruction"), + }; + self.inserter.push_instruction_value( + new_call_instr, + instruction_id, + block, + call_stack, + ); + } + } + } _ => { self.inserter.push_instruction(instruction, block); } @@ -308,6 +373,7 @@ impl<'f> Context<'f> { let typ = self.inserter.function.dfg.type_of_value(array_id); let depth = Self::compute_nested_slice_depth(&typ); max_sizes.resize(depth, 0); + max_sizes[0] = *current_size; self.compute_slice_max_sizes(array_id, slice_sizes, &mut max_sizes, 1); @@ -364,9 +430,12 @@ impl<'f> Context<'f> { if let Some(value) = value { let mut slice = im::Vector::new(); - let array = match self.inserter.function.dfg[value].clone() { + let value = self.inserter.function.dfg[value].clone(); + let array = match value { Value::Array { array, .. } => array, - _ => panic!("Expected an array value"), + _ => { + panic!("Expected an array value"); + } }; if is_parent_slice { @@ -398,7 +467,7 @@ impl<'f> Context<'f> { self.inserter.function.dfg.make_array(slice, typ.clone()) } } - Type::Reference => { + Type::Reference(_) => { unreachable!("ICE: Generating dummy data for references is unsupported") } Type::Function => { @@ -481,7 +550,6 @@ impl<'f> Context<'f> { self.compute_slice_max_sizes(*inner_slice, slice_sizes, max_sizes, depth + 1); } - max_sizes[depth] = max; if max > max_sizes[depth] { max_sizes[depth] = max; } diff --git a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs index f35e8023584..29df9d3c76d 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs @@ -662,6 +662,22 @@ impl<'f> Context<'f> { self.remember_store(address, value); Instruction::Store { address, value } } + Instruction::RangeCheck { value, max_bit_size, assert_message } => { + // Replace value with `value * predicate` to zero out value when predicate is inactive. + + // Condition needs to be cast to argument type in order to multiply them together. + let argument_type = self.inserter.function.dfg.type_of_value(value); + let casted_condition = self.insert_instruction( + Instruction::Cast(condition, argument_type), + call_stack.clone(), + ); + + let value = self.insert_instruction( + Instruction::binary(BinaryOp::Mul, value, casted_condition), + call_stack.clone(), + ); + Instruction::RangeCheck { value, max_bit_size, assert_message } + } other => other, } } else { @@ -801,7 +817,7 @@ mod test { #[test] fn merge_stores() { // fn main f0 { - // b0(v0: u1, v1: ref): + // b0(v0: u1, v1: &mut Field): // jmpif v0, then: b1, else: b2 // b1(): // store v1, Field 5 @@ -816,7 +832,7 @@ mod test { let b2 = builder.insert_block(); let v0 = builder.add_parameter(Type::bool()); - let v1 = builder.add_parameter(Type::Reference); + let v1 = builder.add_parameter(Type::Reference(Rc::new(Type::field()))); builder.terminate_with_jmpif(v0, b1, b2); @@ -878,7 +894,7 @@ mod test { let b3 = builder.insert_block(); let v0 = builder.add_parameter(Type::bool()); - let v1 = builder.add_parameter(Type::Reference); + let v1 = builder.add_parameter(Type::Reference(Rc::new(Type::field()))); builder.terminate_with_jmpif(v0, b1, b2); @@ -977,7 +993,7 @@ mod test { let c1 = builder.add_parameter(Type::bool()); let c4 = builder.add_parameter(Type::bool()); - let r1 = builder.insert_allocate(); + let r1 = builder.insert_allocate(Type::field()); let store_value = |builder: &mut FunctionBuilder, value: u128| { let value = builder.field_constant(value); @@ -1128,7 +1144,7 @@ mod test { builder.terminate_with_jmpif(v0, b1, b2); builder.switch_to_block(b1); - let v2 = builder.insert_allocate(); + let v2 = builder.insert_allocate(Type::field()); let zero = builder.field_constant(0u128); builder.insert_store(v2, zero); let _v4 = builder.insert_load(v2, Type::field()); @@ -1297,7 +1313,7 @@ mod test { let v8 = builder.insert_binary(v6, BinaryOp::Mod, i_two); let v9 = builder.insert_cast(v8, Type::bool()); - let v10 = builder.insert_allocate(); + let v10 = builder.insert_allocate(Type::field()); builder.insert_store(v10, zero); builder.terminate_with_jmpif(v9, b1, b2); @@ -1396,9 +1412,9 @@ mod test { let ten = builder.field_constant(10u128); let one_hundred = builder.field_constant(100u128); - let v0 = builder.insert_allocate(); + let v0 = builder.insert_allocate(Type::field()); builder.insert_store(v0, zero); - let v2 = builder.insert_allocate(); + let v2 = builder.insert_allocate(Type::field()); builder.insert_store(v2, two); let v4 = builder.insert_load(v2, Type::field()); let v5 = builder.insert_binary(v4, BinaryOp::Lt, two); diff --git a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/value_merger.rs b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/value_merger.rs index 32979f78632..446560f45f1 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/value_merger.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/value_merger.rs @@ -60,7 +60,7 @@ impl<'a> ValueMerger<'a> { typ @ Type::Slice(_) => { self.merge_slice_values(typ, then_condition, else_condition, then_value, else_value) } - Type::Reference => panic!("Cannot return references from an if expression"), + Type::Reference(_) => panic!("Cannot return references from an if expression"), Type::Function => panic!("Cannot return functions from an if expression"), } } @@ -333,7 +333,7 @@ impl<'a> ValueMerger<'a> { // to accurately construct dummy data unreachable!("ICE: Cannot return a slice of slices from an if expression") } - Type::Reference => { + Type::Reference(_) => { unreachable!("ICE: Merging references is unsupported") } Type::Function => { diff --git a/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs b/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs index e5fffaccdd0..fba6e6ab989 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs @@ -326,7 +326,7 @@ impl<'f> PerFunctionContext<'f> { match typ { Type::Numeric(_) => false, Type::Function => false, - Type::Reference => true, + Type::Reference(_) => true, Type::Array(elements, _) | Type::Slice(elements) => { elements.iter().any(Self::contains_references) } @@ -427,7 +427,7 @@ mod tests { let func_id = Id::test_new(0); let mut builder = FunctionBuilder::new("func".into(), func_id, RuntimeType::Acir); - let v0 = builder.insert_allocate(); + let v0 = builder.insert_allocate(Type::Array(Rc::new(vec![Type::field()]), 2)); let one = builder.field_constant(FieldElement::one()); let two = builder.field_constant(FieldElement::one()); @@ -468,7 +468,7 @@ mod tests { let func_id = Id::test_new(0); let mut builder = FunctionBuilder::new("func".into(), func_id, RuntimeType::Acir); - let v0 = builder.insert_allocate(); + let v0 = builder.insert_allocate(Type::field()); let one = builder.field_constant(FieldElement::one()); builder.insert_store(v0, one); let v1 = builder.insert_load(v0, Type::field()); @@ -502,7 +502,7 @@ mod tests { let func_id = Id::test_new(0); let mut builder = FunctionBuilder::new("func".into(), func_id, RuntimeType::Acir); - let v0 = builder.insert_allocate(); + let v0 = builder.insert_allocate(Type::field()); let const_one = builder.field_constant(FieldElement::one()); builder.insert_store(v0, const_one); builder.terminate_with_return(vec![v0]); @@ -562,7 +562,7 @@ mod tests { let main_id = Id::test_new(0); let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); - let v0 = builder.insert_allocate(); + let v0 = builder.insert_allocate(Type::field()); let five = builder.field_constant(5u128); builder.insert_store(v0, five); @@ -642,12 +642,12 @@ mod tests { let main_id = Id::test_new(0); let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); - let v0 = builder.insert_allocate(); + let v0 = builder.insert_allocate(Type::field()); let zero = builder.field_constant(0u128); builder.insert_store(v0, zero); - let v2 = builder.insert_allocate(); + let v2 = builder.insert_allocate(Type::Reference(Rc::new(Type::field()))); builder.insert_store(v2, v0); let v3 = builder.insert_load(v2, Type::field()); diff --git a/compiler/noirc_evaluator/src/ssa/opt/mem2reg/block.rs b/compiler/noirc_evaluator/src/ssa/opt/mem2reg/block.rs index 22c5705b723..532785d2928 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/mem2reg/block.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/mem2reg/block.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, collections::BTreeMap}; +use std::borrow::Cow; use crate::ssa::ir::{ function::Function, @@ -19,27 +19,27 @@ pub(super) struct Block { /// Maps a ValueId to the Expression it represents. /// Multiple ValueIds can map to the same Expression, e.g. /// dereferences to the same allocation. - pub(super) expressions: BTreeMap, + pub(super) expressions: im::OrdMap, /// Each expression is tracked as to how many aliases it /// may have. If there is only 1, we can attempt to optimize /// out any known loads to that alias. Note that "alias" here /// includes the original reference as well. - pub(super) aliases: BTreeMap, + pub(super) aliases: im::OrdMap, /// Each allocate instruction result (and some reference block parameters) /// will map to a Reference value which tracks whether the last value stored /// to the reference is known. - pub(super) references: BTreeMap, + pub(super) references: im::OrdMap, /// The last instance of a `Store` instruction to each address in this block - pub(super) last_stores: BTreeMap, + pub(super) last_stores: im::OrdMap, } /// An `Expression` here is used to represent a canonical key /// into the aliases map since otherwise two dereferences of the /// same address will be given different ValueIds. -#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq)] +#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)] pub(super) enum Expression { Dereference(Box), ArrayElement(Box), @@ -111,10 +111,7 @@ impl Block { } fn invalidate_all_references(&mut self) { - for reference_value in self.references.values_mut() { - *reference_value = ReferenceValue::Unknown; - } - + self.references.clear(); self.last_stores.clear(); } @@ -137,7 +134,7 @@ impl Block { } // Keep only the references present in both maps. - let mut intersection = BTreeMap::new(); + let mut intersection = im::OrdMap::new(); for (value_id, reference) in &other.references { if let Some(existing) = self.references.get(value_id) { intersection.insert(*value_id, existing.unify(*reference)); diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs index 4cf97acef9a..9d27ffc60d8 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs @@ -152,7 +152,8 @@ impl<'a> FunctionContext<'a> { /// Allocate a single slot of memory and store into it the given initial value of the variable. /// Always returns a Value::Mutable wrapping the allocate instruction. pub(super) fn new_mutable_variable(&mut self, value_to_store: ValueId) -> Value { - let alloc = self.builder.insert_allocate(); + let element_type = self.builder.current_function.dfg.type_of_value(value_to_store); + let alloc = self.builder.insert_allocate(element_type); self.builder.insert_store(alloc, value_to_store); let typ = self.builder.type_of_value(value_to_store); Value::Mutable(alloc, typ) @@ -177,7 +178,7 @@ impl<'a> FunctionContext<'a> { // A mutable reference wraps each element into a reference. // This can be multiple values if the element type is a tuple. ast::Type::MutableReference(element) => { - Self::map_type_helper(element, &mut |_| f(Type::Reference)) + Self::map_type_helper(element, &mut |typ| f(Type::Reference(Rc::new(typ)))) } ast::Type::FmtString(len, fields) => { // A format string is represented by multiple values @@ -231,8 +232,8 @@ impl<'a> FunctionContext<'a> { ast::Type::Slice(_) => panic!("convert_non_tuple_type called on a slice: {typ}"), ast::Type::MutableReference(element) => { // Recursive call to panic if element is a tuple - Self::convert_non_tuple_type(element); - Type::Reference + let element = Self::convert_non_tuple_type(element); + Type::Reference(Rc::new(element)) } } } @@ -289,6 +290,17 @@ impl<'a> FunctionContext<'a> { self.builder.insert_binary(positive_predicate, BinaryOp::Add, negative_predicate) } + /// Insert constraints ensuring that the operation does not overflow the bit size of the result + /// + /// If the result is unsigned, we simply range check against the bit size + /// + /// If the result is signed, we just prepare it for check_signed_overflow() by casting it to + /// an unsigned value representing the signed integer. + /// We need to use a bigger bit size depending on the operation, in case the operation does overflow, + /// Then, we delegate the overflow checks to check_signed_overflow() and cast the result back to its type. + /// Note that we do NOT want to check for overflows here, only check_signed_overflow() is allowed to do so. + /// This is because an overflow might be valid. For instance if 'a' is a signed integer, then 'a - a', as an unsigned result will always + /// overflow the bit size, however the operation is still valid (i.e it is not a signed overflow) fn check_overflow( &mut self, result: ValueId, @@ -352,22 +364,17 @@ impl<'a> FunctionContext<'a> { BinaryOpKind::ShiftLeft => "left shift", _ => unreachable!("operator {} should not overflow", operator), }; - let message = format!("attempt to {} with overflow", op_name); - let range_constraint = Instruction::RangeCheck { - value: result, - max_bit_size: bit_size, - assert_message: Some(message), - }; - self.builder.set_location(location).insert_instruction(range_constraint, None); + if operator == BinaryOpKind::ShiftLeft { - match result_type { - Type::Numeric(NumericType::Signed { bit_size }) - | Type::Numeric(NumericType::Unsigned { bit_size }) => { - self.builder.insert_truncate(result, bit_size, bit_size + 1) - } - _ => result, - } + self.check_left_shift_overflow(result, rhs, bit_size, location) } else { + let message = format!("attempt to {} with overflow", op_name); + let range_constraint = Instruction::RangeCheck { + value: result, + max_bit_size: bit_size, + assert_message: Some(message), + }; + self.builder.set_location(location).insert_instruction(range_constraint, None); result } } @@ -375,6 +382,42 @@ impl<'a> FunctionContext<'a> { } } + /// Overflow checks for shift-left + /// We use Rust behavior for shift left: + /// If rhs is more or equal than the bit size, then we overflow + /// If not, we do not overflow and shift left with 0 when bits are falling out of the bit size + fn check_left_shift_overflow( + &mut self, + result: ValueId, + rhs: ValueId, + bit_size: u32, + location: Location, + ) -> ValueId { + let max = self + .builder + .numeric_constant(FieldElement::from(bit_size as i128), Type::unsigned(bit_size)); + let overflow = self.builder.insert_binary(rhs, BinaryOp::Lt, max); + let one = self.builder.numeric_constant(FieldElement::one(), Type::bool()); + self.builder.set_location(location).insert_constrain( + overflow, + one, + Some("attempt to left shift with overflow".to_owned()), + ); + self.builder.insert_truncate(result, bit_size, bit_size + 1) + } + + /// Insert constraints ensuring that the operation does not overflow the bit size of the result + /// We assume that: + /// lhs and rhs are signed integers of bit size bit_size + /// result is the result of the operation, casted into an unsigned integer and not reduced + /// + /// overflow check for signed integer is less straightforward than for unsigned integers. + /// We first compute the sign of the operands, and then we use the following rules: + /// addition: positive operands => result must be positive (i.e less than half the bit size) + /// negative operands => result must be negative (i.e not positive) + /// different sign => no overflow + /// multiplication: we check that the product of the operands' absolute values does not overflow the bit size + /// then we check that the result has the proper sign, using the rule of signs fn check_signed_overflow( &mut self, result: ValueId, @@ -450,6 +493,7 @@ impl<'a> FunctionContext<'a> { _ => unreachable!("operator {} should not overflow", operator), } } + /// Insert a binary instruction at the end of the current block. /// Converts the form of the binary instruction as necessary /// (e.g. swapping arguments, inserting a not) to represent it in the IR. @@ -462,7 +506,15 @@ impl<'a> FunctionContext<'a> { location: Location, ) -> Values { let mut result = match operator { - BinaryOpKind::ShiftLeft => self.builder.insert_shift_left(lhs, rhs), + BinaryOpKind::ShiftLeft => { + let result_type = self.builder.current_function.dfg.type_of_value(lhs); + 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"), + }; + self.builder.insert_wrapping_shift_left(lhs, rhs, bit_size) + } BinaryOpKind::ShiftRight => self.builder.insert_shift_right(lhs, rhs), BinaryOpKind::Equal | BinaryOpKind::NotEqual if matches!(self.builder.type_of_value(lhs), Type::Array(..)) => @@ -549,7 +601,7 @@ impl<'a> FunctionContext<'a> { let loop_end = self.builder.insert_block(); // pre-loop - let result_alloc = self.builder.set_location(location).insert_allocate(); + let result_alloc = self.builder.set_location(location).insert_allocate(Type::bool()); let true_value = self.builder.numeric_constant(1u128, Type::bool()); self.builder.insert_store(result_alloc, true_value); let zero = self.builder.field_constant(0u128); diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs index 7677f5669cb..53f1bc863be 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -210,6 +210,13 @@ impl<'a> FunctionContext<'a> { for element in elements { element.for_each(|element| { let element = element.eval(self); + + // If we're referencing a sub-array in a larger nested array we need to + // increase the reference count of the sub array. This maintains a + // pessimistic reference count (since some are likely moved rather than shared) + // which is important for Brillig's copy on write optimization. This has no + // effect in ACIR code. + self.builder.increment_array_reference_count(element); array.push_back(element); }); } @@ -248,11 +255,12 @@ impl<'a> FunctionContext<'a> { Ok(self.codegen_reference(&unary.rhs)?.map(|rhs| { match rhs { value::Value::Normal(value) => { - let alloc = self.builder.insert_allocate(); + let rhs_type = self.builder.current_function.dfg.type_of_value(value); + let alloc = self.builder.insert_allocate(rhs_type); self.builder.insert_store(alloc, value); Tree::Leaf(value::Value::Normal(alloc)) } - // NOTE: The `.into()` here converts the Value::Mutable into + // The `.into()` here converts the Value::Mutable into // a Value::Normal so it is no longer automatically dereferenced. value::Value::Mutable(reference, _) => reference.into(), } @@ -300,6 +308,7 @@ impl<'a> FunctionContext<'a> { } else { (array_or_slice[0], None) }; + self.codegen_array_index( array, index_value, @@ -344,7 +353,13 @@ impl<'a> FunctionContext<'a> { } _ => unreachable!("must have array or slice but got {array_type}"), } - self.builder.insert_array_get(array, offset, typ).into() + + // Reference counting in brillig relies on us incrementing reference + // counts when nested arrays/slices are constructed or indexed. This + // has no effect in ACIR code. + let result = self.builder.insert_array_get(array, offset, typ); + self.builder.increment_array_reference_count(result); + result.into() })) } @@ -534,6 +549,11 @@ impl<'a> FunctionContext<'a> { arguments.append(&mut values); } + // If an array is passed as an argument we increase its reference count + for argument in &arguments { + self.builder.increment_array_reference_count(*argument); + } + self.codegen_intrinsic_call_checks(function, &arguments, call.location); Ok(self.insert_call(function, arguments, &call.return_type, call.location)) @@ -575,12 +595,18 @@ impl<'a> FunctionContext<'a> { 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| { - let value = value.eval(self); - Tree::Leaf(self.new_mutable_variable(value)) - }); - } + values = values.map(|value| { + let value = value.eval(self); + + // Make sure to increment array reference counts on each let binding + self.builder.increment_array_reference_count(value); + + Tree::Leaf(if let_expr.mutable { + self.new_mutable_variable(value) + } else { + value::Value::Normal(value) + }) + }); self.define(let_expr.id, values); Ok(Self::unit_value()) diff --git a/compiler/noirc_frontend/Cargo.toml b/compiler/noirc_frontend/Cargo.toml index 246d6617c94..6f3c35a814a 100644 --- a/compiler/noirc_frontend/Cargo.toml +++ b/compiler/noirc_frontend/Cargo.toml @@ -26,6 +26,3 @@ regex = "1.9.1" [dev-dependencies] strum = "0.24" strum_macros = "0.24" - -[features] -aztec = [] diff --git a/compiler/noirc_frontend/src/ast/expression.rs b/compiler/noirc_frontend/src/ast/expression.rs index 66b94797822..41807d7eca7 100644 --- a/compiler/noirc_frontend/src/ast/expression.rs +++ b/compiler/noirc_frontend/src/ast/expression.rs @@ -76,6 +76,10 @@ impl ExpressionKind { ExpressionKind::Literal(Literal::Str(contents)) } + pub fn raw_string(contents: String, hashes: u8) -> ExpressionKind { + ExpressionKind::Literal(Literal::RawStr(contents, hashes)) + } + pub fn format_string(contents: String) -> ExpressionKind { ExpressionKind::Literal(Literal::FmtStr(contents)) } @@ -312,6 +316,7 @@ pub enum Literal { Bool(bool), Integer(FieldElement), Str(String), + RawStr(String, u8), FmtStr(String), Unit, } @@ -370,7 +375,7 @@ pub struct FunctionDefinition { pub visibility: FunctionVisibility, pub generics: UnresolvedGenerics, - pub parameters: Vec<(Pattern, UnresolvedType, Visibility)>, + pub parameters: Vec, pub body: BlockExpression, pub span: Span, pub where_clause: Vec, @@ -379,6 +384,14 @@ pub struct FunctionDefinition { pub return_distinctness: Distinctness, } +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct Param { + pub visibility: Visibility, + pub pattern: Pattern, + pub typ: UnresolvedType, + pub span: Span, +} + #[derive(Debug, PartialEq, Eq, Clone)] pub enum FunctionReturnType { /// Returns type is not specified. @@ -499,6 +512,11 @@ impl Display for Literal { Literal::Bool(boolean) => write!(f, "{}", if *boolean { "true" } else { "false" }), Literal::Integer(integer) => write!(f, "{}", integer.to_u128()), Literal::Str(string) => write!(f, "\"{string}\""), + Literal::RawStr(string, num_hashes) => { + let hashes: String = + std::iter::once('#').cycle().take(*num_hashes as usize).collect(); + write!(f, "r{hashes}\"{string}\"{hashes}") + } Literal::FmtStr(string) => write!(f, "f\"{string}\""), Literal::Unit => write!(f, "()"), } @@ -634,8 +652,11 @@ impl FunctionDefinition { ) -> FunctionDefinition { let p = parameters .iter() - .map(|(ident, unresolved_type)| { - (Pattern::Identifier(ident.clone()), unresolved_type.clone(), Visibility::Private) + .map(|(ident, unresolved_type)| Param { + visibility: Visibility::Private, + pattern: Pattern::Identifier(ident.clone()), + typ: unresolved_type.clone(), + span: ident.span().merge(unresolved_type.span.unwrap()), }) .collect(); FunctionDefinition { @@ -661,8 +682,8 @@ impl Display for FunctionDefinition { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { writeln!(f, "{:?}", self.attributes)?; - let parameters = vecmap(&self.parameters, |(name, r#type, visibility)| { - format!("{name}: {visibility} {type}") + let parameters = vecmap(&self.parameters, |Param { visibility, pattern, typ, span: _ }| { + format!("{pattern}: {visibility} {typ}") }); let where_clause = vecmap(&self.where_clause, ToString::to_string); diff --git a/compiler/noirc_frontend/src/ast/function.rs b/compiler/noirc_frontend/src/ast/function.rs index e16c0fcba15..b8f385f52d3 100644 --- a/compiler/noirc_frontend/src/ast/function.rs +++ b/compiler/noirc_frontend/src/ast/function.rs @@ -4,7 +4,7 @@ use noirc_errors::Span; use crate::{ token::{Attributes, FunctionAttribute, SecondaryAttribute}, - FunctionReturnType, Ident, Pattern, Visibility, + FunctionReturnType, Ident, Param, Visibility, }; use super::{FunctionDefinition, UnresolvedType, UnresolvedTypeData}; @@ -45,6 +45,10 @@ impl NoirFunction { NoirFunction { kind: FunctionKind::Oracle, def } } + pub fn return_visibility(&self) -> Visibility { + self.def.return_visibility + } + pub fn return_type(&self) -> UnresolvedType { match &self.def.return_type { FunctionReturnType::Default(_) => { @@ -59,7 +63,7 @@ impl NoirFunction { pub fn name_ident(&self) -> &Ident { &self.def.name } - pub fn parameters(&self) -> &Vec<(Pattern, UnresolvedType, Visibility)> { + pub fn parameters(&self) -> &[Param] { &self.def.parameters } pub fn attributes(&self) -> &Attributes { diff --git a/compiler/noirc_frontend/src/ast/mod.rs b/compiler/noirc_frontend/src/ast/mod.rs index 7ce01f461ed..5cbed19620d 100644 --- a/compiler/noirc_frontend/src/ast/mod.rs +++ b/compiler/noirc_frontend/src/ast/mod.rs @@ -41,6 +41,8 @@ pub enum UnresolvedTypeData { FormatString(UnresolvedTypeExpression, Box), Unit, + Parenthesized(Box), + /// A Named UnresolvedType can be a struct type or a type variable Named(Path, Vec), @@ -152,6 +154,7 @@ impl std::fmt::Display for UnresolvedTypeData { Unit => write!(f, "()"), Error => write!(f, "error"), Unspecified => write!(f, "unspecified"), + Parenthesized(typ) => write!(f, "({typ})"), } } } diff --git a/compiler/noirc_frontend/src/ast/statement.rs b/compiler/noirc_frontend/src/ast/statement.rs index 7d19a079203..73b1f68778d 100644 --- a/compiler/noirc_frontend/src/ast/statement.rs +++ b/compiler/noirc_frontend/src/ast/statement.rs @@ -198,7 +198,11 @@ impl From for Expression { fn from(i: Ident) -> Expression { Expression { span: i.0.span(), - kind: ExpressionKind::Variable(Path { segments: vec![i], kind: PathKind::Plain }), + kind: ExpressionKind::Variable(Path { + span: i.span(), + segments: vec![i], + kind: PathKind::Plain, + }), } } } @@ -311,6 +315,7 @@ impl UseTree { pub struct Path { pub segments: Vec, pub kind: PathKind, + pub span: Span, } impl Path { @@ -330,18 +335,11 @@ impl Path { } pub fn from_ident(name: Ident) -> Path { - Path { segments: vec![name], kind: PathKind::Plain } + Path { span: name.span(), segments: vec![name], kind: PathKind::Plain } } pub fn span(&self) -> Span { - let mut segments = self.segments.iter(); - let first_segment = segments.next().expect("ice : cannot have an empty path"); - let mut span = first_segment.0.span(); - - for segment in segments { - span = span.merge(segment.0.span()); - } - span + self.span } pub fn last_segment(&self) -> Ident { @@ -436,6 +434,14 @@ pub enum Pattern { } impl Pattern { + pub fn span(&self) -> Span { + match self { + Pattern::Identifier(ident) => ident.span(), + Pattern::Mutable(_, span) | Pattern::Tuple(_, span) | Pattern::Struct(_, _, span) => { + *span + } + } + } pub fn name_ident(&self) -> &Ident { match self { Pattern::Identifier(name_ident) => name_ident, @@ -537,8 +543,11 @@ impl ForRange { // array.len() let segments = vec![array_ident]; - let array_ident = - ExpressionKind::Variable(Path { segments, kind: PathKind::Plain }); + let array_ident = ExpressionKind::Variable(Path { + segments, + kind: PathKind::Plain, + span: array_span, + }); let end_range = ExpressionKind::MethodCall(Box::new(MethodCallExpression { object: Expression::new(array_ident.clone(), array_span), @@ -553,8 +562,11 @@ impl ForRange { // array[i] let segments = vec![Ident::new(index_name, array_span)]; - let index_ident = - ExpressionKind::Variable(Path { segments, kind: PathKind::Plain }); + let index_ident = ExpressionKind::Variable(Path { + segments, + kind: PathKind::Plain, + span: array_span, + }); let loop_element = ExpressionKind::Index(Box::new(IndexExpression { collection: Expression::new(array_ident, array_span), diff --git a/compiler/noirc_frontend/src/hir/aztec_library.rs b/compiler/noirc_frontend/src/hir/aztec_library.rs deleted file mode 100644 index b0e0e4f04c8..00000000000 --- a/compiler/noirc_frontend/src/hir/aztec_library.rs +++ /dev/null @@ -1,1000 +0,0 @@ -use acvm::FieldElement; -use iter_extended::vecmap; -use noirc_errors::Span; - -use crate::graph::CrateId; -use crate::hir::def_collector::errors::DefCollectorErrorKind; -use crate::hir_def::expr::{HirExpression, HirLiteral}; -use crate::hir_def::stmt::HirStatement; -use crate::node_interner::{NodeInterner, StructId}; -use crate::parser::SortedModule; -use crate::token::SecondaryAttribute; -use crate::{ - hir::Context, BlockExpression, CallExpression, CastExpression, Distinctness, Expression, - ExpressionKind, FunctionReturnType, Ident, IndexExpression, LetStatement, Literal, - MemberAccessExpression, MethodCallExpression, NoirFunction, Path, PathKind, Pattern, Statement, - UnresolvedType, UnresolvedTypeData, Visibility, -}; -use crate::{ - ForLoopStatement, ForRange, FunctionDefinition, FunctionVisibility, ImportStatement, - NoirStruct, PrefixExpression, Signedness, StatementKind, StructType, Type, TypeImpl, UnaryOp, -}; -use fm::FileId; - -use super::def_map::ModuleDefId; - -// -// Helper macros for creating noir ast nodes -// -fn ident(name: &str) -> Ident { - Ident::new(name.to_string(), Span::default()) -} - -fn ident_path(name: &str) -> Path { - Path::from_ident(ident(name)) -} - -fn path(ident: Ident) -> Path { - Path::from_ident(ident) -} - -fn expression(kind: ExpressionKind) -> Expression { - Expression::new(kind, Span::default()) -} - -fn variable(name: &str) -> Expression { - expression(ExpressionKind::Variable(ident_path(name))) -} - -fn variable_ident(identifier: Ident) -> Expression { - expression(ExpressionKind::Variable(path(identifier))) -} - -fn variable_path(path: Path) -> Expression { - expression(ExpressionKind::Variable(path)) -} - -fn method_call(object: Expression, method_name: &str, arguments: Vec) -> Expression { - expression(ExpressionKind::MethodCall(Box::new(MethodCallExpression { - object, - method_name: ident(method_name), - arguments, - }))) -} - -fn call(func: Expression, arguments: Vec) -> Expression { - expression(ExpressionKind::Call(Box::new(CallExpression { func: Box::new(func), arguments }))) -} - -fn pattern(name: &str) -> Pattern { - Pattern::Identifier(ident(name)) -} - -fn mutable(name: &str) -> Pattern { - Pattern::Mutable(Box::new(pattern(name)), Span::default()) -} - -fn mutable_assignment(name: &str, assigned_to: Expression) -> Statement { - make_statement(StatementKind::Let(LetStatement { - pattern: mutable(name), - r#type: make_type(UnresolvedTypeData::Unspecified), - expression: assigned_to, - })) -} - -fn mutable_reference(variable_name: &str) -> Expression { - expression(ExpressionKind::Prefix(Box::new(PrefixExpression { - operator: UnaryOp::MutableReference, - rhs: variable(variable_name), - }))) -} - -fn assignment(name: &str, assigned_to: Expression) -> Statement { - make_statement(StatementKind::Let(LetStatement { - pattern: pattern(name), - r#type: make_type(UnresolvedTypeData::Unspecified), - expression: assigned_to, - })) -} - -fn member_access(lhs: &str, rhs: &str) -> Expression { - expression(ExpressionKind::MemberAccess(Box::new(MemberAccessExpression { - lhs: variable(lhs), - rhs: ident(rhs), - }))) -} - -macro_rules! chained_path { - ( $base:expr $(, $tail:expr)* ) => { - { - let mut base_path = ident_path($base); - $( - base_path.segments.push(ident($tail)); - )* - base_path - } - } -} - -macro_rules! chained_dep { - ( $base:expr $(, $tail:expr)* ) => { - { - let mut base_path = ident_path($base); - base_path.kind = PathKind::Dep; - $( - base_path.segments.push(ident($tail)); - )* - base_path - } - } -} - -fn cast(lhs: Expression, ty: UnresolvedTypeData) -> Expression { - expression(ExpressionKind::Cast(Box::new(CastExpression { lhs, r#type: make_type(ty) }))) -} - -fn make_type(typ: UnresolvedTypeData) -> UnresolvedType { - UnresolvedType { typ, span: None } -} - -fn index_array(array: Ident, index: &str) -> Expression { - expression(ExpressionKind::Index(Box::new(IndexExpression { - collection: variable_path(path(array)), - index: variable(index), - }))) -} - -fn index_array_variable(array: Expression, index: &str) -> Expression { - expression(ExpressionKind::Index(Box::new(IndexExpression { - collection: array, - index: variable(index), - }))) -} - -fn import(path: Path) -> ImportStatement { - ImportStatement { path, alias: None } -} - -// -// Create AST Nodes for Aztec -// - -/// Traverses every function in the ast, calling `transform_function` which -/// determines if further processing is required -pub(crate) fn transform( - mut ast: SortedModule, - crate_id: &CrateId, - context: &Context, -) -> Result { - // Usage -> mut ast -> aztec_library::transform(&mut ast) - - // Covers all functions in the ast - for submodule in ast.submodules.iter_mut().filter(|submodule| submodule.is_contract) { - if transform_module(&mut submodule.contents, crate_id, context)? { - check_for_aztec_dependency(crate_id, context)?; - include_relevant_imports(&mut submodule.contents); - } - } - Ok(ast) -} - -// -// Transform Hir Nodes for Aztec -// - -/// Completes the Hir with data gathered from type resolution -pub(crate) fn transform_hir(crate_id: &CrateId, context: &mut Context) { - transform_events(crate_id, context); -} - -/// Includes an import to the aztec library if it has not been included yet -fn include_relevant_imports(ast: &mut SortedModule) { - // Create the aztec import path using the assumed chained_dep! macro - let aztec_import_path = import(chained_dep!("aztec")); - - // Check if the aztec import already exists - let is_aztec_imported = - ast.imports.iter().any(|existing_import| existing_import.path == aztec_import_path.path); - - // If aztec is not imported, add the import at the beginning - if !is_aztec_imported { - ast.imports.insert(0, aztec_import_path); - } -} - -/// 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<(), (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((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(|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 -fn is_custom_attribute(attr: &SecondaryAttribute, attribute_name: &str) -> bool { - if let SecondaryAttribute::Custom(custom_attr) = attr { - custom_attr.as_str() == attribute_name - } else { - false - } -} - -/// 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, - 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)); - has_transformed_module = true; - } - } - - for func in module.functions.iter_mut() { - for secondary_attribute in func.def.attributes.secondary.clone() { - if is_custom_attribute(&secondary_attribute, "aztec(private)") { - transform_function("Private", func, storage_defined); - has_transformed_module = true; - } else if is_custom_attribute(&secondary_attribute, "aztec(public)") { - transform_function("Public", func, storage_defined); - has_transformed_module = true; - } - } - // Add the storage struct to the beginning of the function if it is unconstrained in an aztec contract - if storage_defined && func.def.is_unconstrained { - transform_unconstrained(func); - has_transformed_module = true; - } - } - Ok(has_transformed_module) -} - -/// If it does, it will insert the following things: -/// - A new Input that is provided for a kernel app circuit, named: {Public/Private}ContextInputs -/// - Hashes all of the function input variables -/// - This instantiates a helper function -fn transform_function(ty: &str, func: &mut NoirFunction, storage_defined: bool) { - let context_name = format!("{}Context", ty); - let inputs_name = format!("{}ContextInputs", ty); - let return_type_name = format!("{}CircuitPublicInputs", ty); - - // Add access to the storage struct - if storage_defined { - let storage_def = abstract_storage(&ty.to_lowercase(), false); - func.def.body.0.insert(0, storage_def); - } - - // Insert the context creation as the first action - let create_context = create_context(&context_name, &func.def.parameters); - func.def.body.0.splice(0..0, (create_context).iter().cloned()); - - // Add the inputs to the params - let input = create_inputs(&inputs_name); - func.def.parameters.insert(0, input); - - // Abstract return types such that they get added to the kernel's return_values - if let Some(return_values) = abstract_return_values(func) { - func.def.body.0.push(return_values); - } - - // Push the finish method call to the end of the function - let finish_def = create_context_finish(); - func.def.body.0.push(finish_def); - - let return_type = create_return_type(&return_type_name); - func.def.return_type = return_type; - func.def.return_visibility = Visibility::Public; - - // Distinct return types are only required for private functions - // Public functions should have open auto-inferred - match ty { - "Private" => func.def.return_distinctness = Distinctness::Distinct, - "Public" => func.def.is_open = true, - _ => (), - } -} - -/// Transform Unconstrained -/// -/// Inserts the following code at the beginning of an unconstrained function -/// ```noir -/// let storage = Storage::init(Context::none()); -/// ``` -/// -/// This will allow developers to access their contract' storage struct in unconstrained functions -fn transform_unconstrained(func: &mut NoirFunction) { - func.def.body.0.insert(0, abstract_storage("Unconstrained", true)); -} - -fn collect_crate_structs(crate_id: &CrateId, context: &Context) -> Vec { - context - .def_map(crate_id) - .expect("ICE: Missing crate in def_map") - .modules() - .iter() - .flat_map(|(_, module)| { - module.type_definitions().filter_map(|typ| { - if let ModuleDefId::TypeId(struct_id) = typ { - Some(struct_id) - } else { - None - } - }) - }) - .collect() -} - -/// Substitutes the signature literal that was introduced in the selector method previously with the actual signature. -fn transform_event(struct_id: StructId, interner: &mut NodeInterner) { - let struct_type = interner.get_struct(struct_id); - let selector_id = interner - .lookup_method(&Type::Struct(struct_type, vec![]), struct_id, "selector", false) - .expect("Selector method not found"); - let selector_function = interner.function(&selector_id); - - let compute_selector_statement = interner.statement( - selector_function - .block(interner) - .statements() - .first() - .expect("Compute selector statement not found"), - ); - - let compute_selector_expression = match compute_selector_statement { - HirStatement::Expression(expression_id) => match interner.expression(&expression_id) { - HirExpression::Call(hir_call_expression) => Some(hir_call_expression), - _ => None, - }, - _ => None, - } - .expect("Compute selector statement is not a call expression"); - - let first_arg_id = compute_selector_expression - .arguments - .first() - .expect("Missing argument for compute selector"); - - match interner.expression(first_arg_id) { - HirExpression::Literal(HirLiteral::Str(signature)) - if signature == SIGNATURE_PLACEHOLDER => - { - let selector_literal_id = first_arg_id; - - let structure = interner.get_struct(struct_id); - let signature = event_signature(&structure.borrow()); - interner.update_expression(*selector_literal_id, |expr| { - *expr = HirExpression::Literal(HirLiteral::Str(signature.clone())); - }); - - // Also update the type! It might have a different length now than the placeholder. - interner.push_expr_type( - selector_literal_id, - Type::String(Box::new(Type::Constant(signature.len() as u64))), - ); - } - _ => unreachable!("Signature placeholder literal does not match"), - } -} - -fn transform_events(crate_id: &CrateId, context: &mut Context) { - for struct_id in collect_crate_structs(crate_id, context) { - let attributes = context.def_interner.struct_attributes(&struct_id); - if attributes.iter().any(|attr| matches!(attr, SecondaryAttribute::Event)) { - transform_event(struct_id, &mut context.def_interner); - } - } -} - -const SIGNATURE_PLACEHOLDER: &str = "SIGNATURE_PLACEHOLDER"; - -/// Generates the impl for an event selector -/// -/// Inserts the following code: -/// ```noir -/// impl SomeStruct { -/// fn selector() -> Field { -/// aztec::oracle::compute_selector::compute_selector("SIGNATURE_PLACEHOLDER") -/// } -/// } -/// ``` -/// -/// This allows developers to emit events without having to write the signature of the event every time they emit it. -/// The signature cannot be known at this point since types are not resolved yet, so we use a signature placeholder. -/// It'll get resolved after by transforming the HIR. -fn generate_selector_impl(structure: &NoirStruct) -> TypeImpl { - let struct_type = make_type(UnresolvedTypeData::Named(path(structure.name.clone()), vec![])); - - let selector_fun_body = BlockExpression(vec![make_statement(StatementKind::Expression(call( - variable_path(chained_path!("aztec", "selector", "compute_selector")), - vec![expression(ExpressionKind::Literal(Literal::Str(SIGNATURE_PLACEHOLDER.to_string())))], - )))]); - - let mut selector_fn_def = FunctionDefinition::normal( - &ident("selector"), - &vec![], - &[], - &selector_fun_body, - &[], - &FunctionReturnType::Ty(make_type(UnresolvedTypeData::FieldElement)), - ); - - selector_fn_def.visibility = FunctionVisibility::Public; - - // Seems to be necessary on contract modules - selector_fn_def.return_visibility = Visibility::Public; - - TypeImpl { - object_type: struct_type, - type_span: structure.span, - generics: vec![], - methods: vec![NoirFunction::normal(selector_fn_def)], - } -} - -/// Helper function that returns what the private context would look like in the ast -/// This should make it available to be consumed within aztec private annotated functions. -/// -/// The replaced code: -/// ```noir -/// /// Before -/// fn foo(inputs: PrivateContextInputs) { -/// // ... -/// } -/// -/// /// After -/// #[aztec(private)] -/// fn foo() { -/// // ... -/// } -pub(crate) fn create_inputs(ty: &str) -> (Pattern, UnresolvedType, Visibility) { - let context_ident = ident("inputs"); - let context_pattern = Pattern::Identifier(context_ident); - let type_path = chained_path!("aztec", "abi", ty); - let context_type = make_type(UnresolvedTypeData::Named(type_path, vec![])); - let visibility = Visibility::Private; - - (context_pattern, context_type, visibility) -} - -/// Creates the private context object to be accessed within the function, the parameters need to be extracted to be -/// appended into the args hash object. -/// -/// The replaced code: -/// ```noir -/// #[aztec(private)] -/// fn foo(structInput: SomeStruct, arrayInput: [u8; 10], fieldInput: Field) -> Field { -/// // Create the hasher object -/// let mut hasher = Hasher::new(); -/// -/// // struct inputs call serialize on them to add an array of fields -/// hasher.add_multiple(structInput.serialize()); -/// -/// // Array inputs are iterated over and each element is added to the hasher (as a field) -/// for i in 0..arrayInput.len() { -/// hasher.add(arrayInput[i] as Field); -/// } -/// // Field inputs are added to the hasher -/// hasher.add({ident}); -/// -/// // Create the context -/// // The inputs (injected by this `create_inputs`) and completed hash object are passed to the context -/// let mut context = PrivateContext::new(inputs, hasher.hash()); -/// } -/// ``` -fn create_context(ty: &str, params: &[(Pattern, UnresolvedType, Visibility)]) -> Vec { - let mut injected_expressions: Vec = vec![]; - - // `let mut hasher = Hasher::new();` - let let_hasher = mutable_assignment( - "hasher", // Assigned to - call( - variable_path(chained_path!("aztec", "abi", "Hasher", "new")), // Path - vec![], // args - ), - ); - - // Completes: `let mut hasher = Hasher::new();` - injected_expressions.push(let_hasher); - - // Iterate over each of the function parameters, adding to them to the hasher - params.iter().for_each(|(pattern, typ, _vis)| { - match pattern { - Pattern::Identifier(identifier) => { - // Match the type to determine the padding to do - let unresolved_type = &typ.typ; - let expression = match unresolved_type { - // `hasher.add_multiple({ident}.serialize())` - UnresolvedTypeData::Named(..) => add_struct_to_hasher(identifier), - UnresolvedTypeData::Array(_, arr_type) => { - add_array_to_hasher(identifier, &arr_type) - } - // `hasher.add({ident})` - UnresolvedTypeData::FieldElement => add_field_to_hasher(identifier), - // Add the integer to the hasher, casted to a field - // `hasher.add({ident} as Field)` - UnresolvedTypeData::Integer(..) | UnresolvedTypeData::Bool => { - add_cast_to_hasher(identifier) - } - _ => unreachable!("[Aztec Noir] Provided parameter type is not supported"), - }; - injected_expressions.push(expression); - } - _ => todo!(), // Maybe unreachable? - } - }); - - // Create the inputs to the context - let inputs_expression = variable("inputs"); - // `hasher.hash()` - let hash_call = method_call( - variable("hasher"), // variable - "hash", // method name - vec![], // args - ); - - // let mut context = {ty}::new(inputs, hash); - let let_context = mutable_assignment( - "context", // Assigned to - call( - variable_path(chained_path!("aztec", "context", ty, "new")), // Path - vec![inputs_expression, hash_call], // args - ), - ); - injected_expressions.push(let_context); - - // Return all expressions that will be injected by the hasher - injected_expressions -} - -/// Abstract Return Type -/// -/// This function intercepts the function's current return type and replaces it with pushes -/// To the kernel -/// -/// The replaced code: -/// ```noir -/// /// Before -/// #[aztec(private)] -/// fn foo() -> abi::PrivateCircuitPublicInputs { -/// // ... -/// let my_return_value: Field = 10; -/// context.return_values.push(my_return_value); -/// } -/// -/// /// After -/// #[aztec(private)] -/// fn foo() -> Field { -/// // ... -/// let my_return_value: Field = 10; -/// my_return_value -/// } -/// ``` -/// Similarly; Structs will be pushed to the context, after serialize() is called on them. -/// Arrays will be iterated over and each element will be pushed to the context. -/// Any primitive type that can be cast will be casted to a field and pushed to the context. -fn abstract_return_values(func: &NoirFunction) -> Option { - let current_return_type = func.return_type().typ; - let len = func.def.body.len(); - let last_statement = &func.def.body.0[len - 1]; - - // TODO: (length, type) => We can limit the size of the array returned to be limited by kernel size - // Doesn't need done until we have settled on a kernel size - // TODO: support tuples here and in inputs -> convert into an issue - - // Check if the return type is an expression, if it is, we can handle it - match last_statement { - Statement { kind: StatementKind::Expression(expression), .. } => { - match current_return_type { - // Call serialize on structs, push the whole array, calling push_array - UnresolvedTypeData::Named(..) => Some(make_struct_return_type(expression.clone())), - UnresolvedTypeData::Array(..) => Some(make_array_return_type(expression.clone())), - // Cast these types to a field before pushing - UnresolvedTypeData::Bool | UnresolvedTypeData::Integer(..) => { - Some(make_castable_return_type(expression.clone())) - } - UnresolvedTypeData::FieldElement => Some(make_return_push(expression.clone())), - _ => None, - } - } - _ => None, - } -} - -/// Abstract storage -/// -/// For private functions: -/// ```noir -/// #[aztec(private)] -/// fn lol() { -/// let storage = Storage::init(Context::private(context)); -/// } -/// ``` -/// -/// For public functions: -/// ```noir -/// #[aztec(public)] -/// fn lol() { -/// let storage = Storage::init(Context::public(context)); -/// } -/// ``` -/// -/// For unconstrained functions: -/// ```noir -/// unconstrained fn lol() { -/// let storage = Storage::init(Context::none()); -/// } -fn abstract_storage(typ: &str, unconstrained: bool) -> Statement { - let init_context_call = if unconstrained { - call( - variable_path(chained_path!("aztec", "context", "Context", "none")), // Path - vec![], // args - ) - } else { - call( - variable_path(chained_path!("aztec", "context", "Context", typ)), // Path - vec![mutable_reference("context")], // args - ) - }; - - assignment( - "storage", // Assigned to - call( - variable_path(chained_path!("Storage", "init")), // Path - vec![init_context_call], // args - ), - ) -} - -/// Context Return Values -/// -/// Creates an instance to the context return values -/// ```noir -/// `context.return_values` -/// ``` -fn context_return_values() -> Expression { - member_access("context", "return_values") -} - -fn make_statement(kind: StatementKind) -> Statement { - Statement { span: Span::default(), kind } -} - -/// Make return Push -/// -/// Translates to: -/// `context.return_values.push({push_value})` -fn make_return_push(push_value: Expression) -> Statement { - make_statement(StatementKind::Semi(method_call( - context_return_values(), - "push", - vec![push_value], - ))) -} - -/// Make Return push array -/// -/// Translates to: -/// `context.return_values.push_array({push_value})` -fn make_return_push_array(push_value: Expression) -> Statement { - make_statement(StatementKind::Semi(method_call( - context_return_values(), - "push_array", - vec![push_value], - ))) -} - -/// Make struct return type -/// -/// Translates to: -/// ```noir -/// `context.return_values.push_array({push_value}.serialize())` -fn make_struct_return_type(expression: Expression) -> Statement { - let serialized_call = method_call( - expression, // variable - "serialize", // method name - vec![], // args - ); - make_return_push_array(serialized_call) -} - -/// Make array return type -/// -/// Translates to: -/// ```noir -/// for i in 0..{ident}.len() { -/// context.return_values.push({ident}[i] as Field) -/// } -/// ``` -fn make_array_return_type(expression: Expression) -> Statement { - let inner_cast_expression = - cast(index_array_variable(expression.clone(), "i"), UnresolvedTypeData::FieldElement); - let assignment = make_statement(StatementKind::Semi(method_call( - context_return_values(), // variable - "push", // method name - vec![inner_cast_expression], - ))); - - create_loop_over(expression, vec![assignment]) -} - -/// Castable return type -/// -/// Translates to: -/// ```noir -/// context.return_values.push({ident} as Field) -/// ``` -fn make_castable_return_type(expression: Expression) -> Statement { - // Cast these types to a field before pushing - let cast_expression = cast(expression, UnresolvedTypeData::FieldElement); - make_return_push(cast_expression) -} - -/// Create Return Type -/// -/// Public functions return abi::PublicCircuitPublicInputs while -/// private functions return abi::PrivateCircuitPublicInputs -/// -/// This call constructs an ast token referencing the above types -/// The name is set in the function above `transform`, hence the -/// whole token name is passed in -/// -/// The replaced code: -/// ```noir -/// -/// /// Before -/// fn foo() -> abi::PrivateCircuitPublicInputs { -/// // ... -/// } -/// -/// /// After -/// #[aztec(private)] -/// fn foo() { -/// // ... -/// } -pub(crate) fn create_return_type(ty: &str) -> FunctionReturnType { - let return_path = chained_path!("aztec", "abi", ty); - - let ty = make_type(UnresolvedTypeData::Named(return_path, vec![])); - FunctionReturnType::Ty(ty) -} - -/// Create Context Finish -/// -/// Each aztec function calls `context.finish()` at the end of a function -/// to return values required by the kernel. -/// -/// The replaced code: -/// ```noir -/// /// Before -/// fn foo() -> abi::PrivateCircuitPublicInputs { -/// // ... -/// context.finish() -/// } -/// -/// /// After -/// #[aztec(private)] -/// fn foo() { -/// // ... -/// } -pub(crate) fn create_context_finish() -> Statement { - let method_call = method_call( - variable("context"), // variable - "finish", // method name - vec![], // args - ); - make_statement(StatementKind::Expression(method_call)) -} - -// -// Methods to create hasher inputs -// - -fn add_struct_to_hasher(identifier: &Ident) -> Statement { - // If this is a struct, we call serialize and add the array to the hasher - let serialized_call = method_call( - variable_path(path(identifier.clone())), // variable - "serialize", // method name - vec![], // args - ); - - make_statement(StatementKind::Semi(method_call( - variable("hasher"), // variable - "add_multiple", // method name - vec![serialized_call], // args - ))) -} - -fn create_loop_over(var: Expression, loop_body: Vec) -> Statement { - // If this is an array of primitive types (integers / fields) we can add them each to the hasher - // casted to a field - let span = var.span.clone(); - - // `array.len()` - let end_range_expression = method_call( - var, // variable - "len", // method name - vec![], // args - ); - - // What will be looped over - // - `hasher.add({ident}[i] as Field)` - let for_loop_block = expression(ExpressionKind::Block(BlockExpression(loop_body))); - - // `for i in 0..{ident}.len()` - make_statement(StatementKind::For(ForLoopStatement { - range: ForRange::Range( - expression(ExpressionKind::Literal(Literal::Integer(FieldElement::from(i128::from( - 0, - ))))), - end_range_expression, - ), - identifier: ident("i"), - block: for_loop_block, - span, - })) -} - -fn add_array_to_hasher(identifier: &Ident, arr_type: &UnresolvedType) -> Statement { - // If this is an array of primitive types (integers / fields) we can add them each to the hasher - // casted to a field - - // Wrap in the semi thing - does that mean ended with semi colon? - // `hasher.add({ident}[i] as Field)` - - let arr_index = index_array(identifier.clone(), "i"); - let (add_expression, hasher_method_name) = match arr_type.typ { - UnresolvedTypeData::Named(..) => { - let hasher_method_name = "add_multiple".to_owned(); - let call = method_call( - // All serialise on each element - arr_index, // variable - "serialize", // method name - vec![], // args - ); - (call, hasher_method_name) - } - _ => { - let hasher_method_name = "add".to_owned(); - let call = cast( - arr_index, // lhs - `ident[i]` - UnresolvedTypeData::FieldElement, // cast to - `as Field` - ); - (call, hasher_method_name) - } - }; - - let block_statement = make_statement(StatementKind::Semi(method_call( - variable("hasher"), // variable - &hasher_method_name, // method name - vec![add_expression], - ))); - - create_loop_over(variable_ident(identifier.clone()), vec![block_statement]) -} - -fn add_field_to_hasher(identifier: &Ident) -> Statement { - // `hasher.add({ident})` - let ident = variable_path(path(identifier.clone())); - make_statement(StatementKind::Semi(method_call( - variable("hasher"), // variable - "add", // method name - vec![ident], // args - ))) -} - -fn add_cast_to_hasher(identifier: &Ident) -> Statement { - // `hasher.add({ident} as Field)` - // `{ident} as Field` - let cast_operation = cast( - variable_path(path(identifier.clone())), // lhs - UnresolvedTypeData::FieldElement, // rhs - ); - - // `hasher.add({ident} as Field)` - make_statement(StatementKind::Semi(method_call( - variable("hasher"), // variable - "add", // method name - vec![cast_operation], // args - ))) -} - -/// Computes the aztec signature for a resolved type. -fn signature_of_type(typ: &Type) -> String { - match typ { - Type::Integer(Signedness::Signed, bit_size) => format!("i{}", bit_size), - Type::Integer(Signedness::Unsigned, bit_size) => format!("u{}", bit_size), - Type::FieldElement => "Field".to_owned(), - Type::Bool => "bool".to_owned(), - Type::Array(len, typ) => { - if let Type::Constant(len) = **len { - format!("[{};{len}]", signature_of_type(typ)) - } else { - unimplemented!("Cannot generate signature for array with length type {:?}", typ) - } - } - Type::Struct(def, args) => { - let fields = def.borrow().get_fields(args); - let fields = vecmap(fields, |(_, typ)| signature_of_type(&typ)); - format!("({})", fields.join(",")) - } - Type::Tuple(types) => { - let fields = vecmap(types, signature_of_type); - format!("({})", fields.join(",")) - } - _ => unimplemented!("Cannot generate signature for type {:?}", typ), - } -} - -/// Computes the signature for a resolved event type. -/// It has the form 'EventName(Field,(Field),[u8;2])' -fn event_signature(event: &StructType) -> String { - let fields = vecmap(event.get_fields(&[]), |(_, typ)| signature_of_type(&typ)); - format!("{}({})", event.name.0.contents, fields.join(",")) -} 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 ce1cf675a07..86122530cde 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs @@ -1,33 +1,32 @@ use super::dc_mod::collect_defs; use super::errors::{DefCollectorErrorKind, DuplicateType}; use crate::graph::CrateId; -use crate::hir::def_map::{CrateDefMap, LocalModuleId, ModuleData, ModuleDefId, ModuleId}; +use crate::hir::def_map::{CrateDefMap, LocalModuleId, ModuleId}; use crate::hir::resolution::errors::ResolverError; -use crate::hir::resolution::import::PathResolutionError; -use crate::hir::resolution::path_resolver::PathResolver; + +use crate::hir::resolution::import::{resolve_imports, ImportDirective}; use crate::hir::resolution::resolver::Resolver; use crate::hir::resolution::{ - import::{resolve_imports, ImportDirective}, - path_resolver::StandardPathResolver, + collect_impls, collect_trait_impls, resolve_free_functions, resolve_globals, resolve_impls, + resolve_structs, resolve_trait_by_path, resolve_trait_impls, resolve_traits, + resolve_type_aliases, }; 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, TraitImplId, TypeAliasId, -}; + +use crate::macros_api::MacroProcessor; +use crate::node_interner::{FuncId, NodeInterner, StmtId, StructId, TraitId, TypeAliasId}; use crate::parser::{ParserError, SortedModule}; use crate::{ - ExpressionKind, Generics, Ident, LetStatement, Literal, NoirFunction, NoirStruct, NoirTrait, - NoirTypeAlias, Path, Shared, StructType, TraitItem, Type, TypeBinding, TypeVariableKind, - UnresolvedGenerics, UnresolvedTraitConstraint, UnresolvedType, + ExpressionKind, LetStatement, Literal, NoirFunction, NoirStruct, NoirTrait, NoirTypeAlias, + Path, Type, UnresolvedGenerics, UnresolvedTraitConstraint, UnresolvedType, }; use fm::FileId; use iter_extended::vecmap; use noirc_errors::{CustomDiagnostic, Span}; -use std::collections::{BTreeMap, HashMap, HashSet}; -use std::rc::Rc; +use std::collections::{BTreeMap, HashMap}; + use std::vec; /// Stores all of the unresolved functions in a particular file/mod @@ -124,6 +123,16 @@ pub struct DefCollector { pub(crate) collected_traits_impls: Vec, } +/// Maps the type and the module id in which the impl is defined to the functions contained in that +/// impl along with the generics declared on the impl itself. This also contains the Span +/// of the object_type of the impl, used to issue an error if the object type fails to resolve. +/// +/// Note that because these are keyed by unresolved types, the impl map is one of the few instances +/// of HashMap rather than BTreeMap. For this reason, we should be careful not to iterate over it +/// since it would be non-deterministic. +pub(crate) type ImplMap = + HashMap<(UnresolvedType, LocalModuleId), Vec<(UnresolvedGenerics, Span, UnresolvedFunctions)>>; + #[derive(Debug, Clone)] pub enum CompilationError { ParseError(ParserError), @@ -166,16 +175,6 @@ impl From for CompilationError { } } -/// Maps the type and the module id in which the impl is defined to the functions contained in that -/// impl along with the generics declared on the impl itself. This also contains the Span -/// of the object_type of the impl, used to issue an error if the object type fails to resolve. -/// -/// Note that because these are keyed by unresolved types, the impl map is one of the few instances -/// of HashMap rather than BTreeMap. For this reason, we should be careful not to iterate over it -/// since it would be non-deterministic. -type ImplMap = - HashMap<(UnresolvedType, LocalModuleId), Vec<(UnresolvedGenerics, Span, UnresolvedFunctions)>>; - impl DefCollector { fn new(def_map: CrateDefMap) -> DefCollector { DefCollector { @@ -199,6 +198,7 @@ impl DefCollector { context: &mut Context, ast: SortedModule, root_file_id: FileId, + macro_processors: Vec<&dyn MacroProcessor>, ) -> Vec<(CompilationError, FileId)> { let mut errors: Vec<(CompilationError, FileId)> = vec![]; let crate_id = def_map.krate; @@ -211,7 +211,11 @@ impl DefCollector { let crate_graph = &context.crate_graph[crate_id]; for dep in crate_graph.dependencies.clone() { - errors.extend(CrateDefMap::collect_defs(dep.crate_id, context)); + errors.extend(CrateDefMap::collect_defs( + dep.crate_id, + context, + macro_processors.clone(), + )); let dep_def_root = context.def_map(&dep.crate_id).expect("ice: def map was just created").root; @@ -341,10 +345,9 @@ impl DefCollector { errors.extend(resolved_globals.errors); - // We run hir transformations before type checks - #[cfg(feature = "aztec")] - crate::hir::aztec_library::transform_hir(&crate_id, context); - + for macro_processor in macro_processors { + macro_processor.process_typed_ast(&crate_id, context); + } errors.extend(type_check_globals(&mut context.def_interner, resolved_globals.globals)); // Type check all of the functions in the crate @@ -355,264 +358,6 @@ impl DefCollector { } } -/// Go through the list of impls and add each function within to the scope -/// of the module defined by its type. -fn collect_impls( - context: &mut Context, - crate_id: CrateId, - collected_impls: &ImplMap, -) -> Vec<(CompilationError, FileId)> { - let interner = &mut context.def_interner; - let def_maps = &mut context.def_maps; - let mut errors: Vec<(CompilationError, FileId)> = vec![]; - - for ((unresolved_type, module_id), methods) in collected_impls { - let path_resolver = - StandardPathResolver::new(ModuleId { local_id: *module_id, krate: crate_id }); - - let file = def_maps[&crate_id].file_id(*module_id); - - for (generics, span, unresolved) in methods { - let mut resolver = Resolver::new(interner, &path_resolver, def_maps, file); - resolver.add_generics(generics); - let typ = resolver.resolve_type(unresolved_type.clone()); - - errors.extend(take_errors(unresolved.file_id, resolver)); - - if let Some(struct_type) = get_struct_type(&typ) { - let struct_type = struct_type.borrow(); - - // `impl`s are only allowed on types defined within the current crate - if struct_type.id.krate() != crate_id { - let span = *span; - let type_name = struct_type.name.to_string(); - let error = DefCollectorErrorKind::ForeignImpl { span, type_name }; - errors.push((error.into(), unresolved.file_id)); - continue; - } - - // 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 = 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 - // 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()); - } - } - // Prohibit defining impls for primitive types if we're not in the stdlib - } else if typ != Type::Error && !crate_id.is_stdlib() { - let span = *span; - let error = DefCollectorErrorKind::NonStructTypeInImpl { span }; - errors.push((error.into(), unresolved.file_id)); - } - } - } - 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, - crate_id: CrateId, - trait_id: TraitId, - trait_impl: &mut UnresolvedTraitImpl, -) -> Vec<(CompilationError, FileId)> { - // In this Vec methods[i] corresponds to trait.methods[i]. If the impl has no implementation - // for a particular method, the default implementation will be added at that slot. - let mut ordered_methods = Vec::new(); - - let the_trait = interner.get_trait(trait_id); - - // check whether the trait implementation is in the same crate as either the trait or the type - let mut errors = - check_trait_impl_crate_coherence(interner, &the_trait, trait_impl, crate_id, def_maps); - // set of function ids that have a corresponding method in the trait - let mut func_ids_in_trait = HashSet::new(); - - for method in &the_trait.methods { - let overrides: Vec<_> = trait_impl - .methods - .functions - .iter() - .filter(|(_, _, f)| f.name() == method.name.0.contents) - .collect(); - - if overrides.is_empty() { - if let Some(default_impl) = &method.default_impl { - let func_id = interner.push_empty_fn(); - let module = ModuleId { local_id: trait_impl.module_id, krate: crate_id }; - interner.push_function(func_id, &default_impl.def, module); - func_ids_in_trait.insert(func_id); - ordered_methods.push(( - method.default_impl_module_id, - func_id, - *default_impl.clone(), - )); - } else { - let error = DefCollectorErrorKind::TraitMissingMethod { - trait_name: the_trait.name.clone(), - method_name: method.name.clone(), - trait_impl_span: trait_impl.object_type.span.expect("type must have a span"), - }; - errors.push((error.into(), trait_impl.file_id)); - } - } else { - for (_, func_id, _) in &overrides { - func_ids_in_trait.insert(*func_id); - } - - if overrides.len() > 1 { - let error = DefCollectorErrorKind::Duplicate { - typ: DuplicateType::TraitAssociatedFunction, - first_def: overrides[0].2.name_ident().clone(), - second_def: overrides[1].2.name_ident().clone(), - }; - errors.push((error.into(), trait_impl.file_id)); - } - - ordered_methods.push(overrides[0].clone()); - } - } - - // Emit MethodNotInTrait error for methods in the impl block that - // don't have a corresponding method signature defined in the trait - for (_, func_id, func) in &trait_impl.methods.functions { - if !func_ids_in_trait.contains(func_id) { - let error = DefCollectorErrorKind::MethodNotInTrait { - trait_name: the_trait.name.clone(), - impl_method: func.name_ident().clone(), - }; - errors.push((error.into(), trait_impl.file_id)); - } - } - - trait_impl.methods.functions = ordered_methods; - trait_impl.methods.trait_id = Some(trait_id); - errors -} - -fn collect_trait_impl( - context: &mut Context, - crate_id: CrateId, - trait_impl: &mut UnresolvedTraitImpl, -) -> Vec<(CompilationError, FileId)> { - let interner = &mut context.def_interner; - let def_maps = &mut context.def_maps; - let mut errors: Vec<(CompilationError, FileId)> = vec![]; - let unresolved_type = trait_impl.object_type.clone(); - let module = ModuleId { local_id: trait_impl.module_id, krate: crate_id }; - trait_impl.trait_id = - match resolve_trait_by_path(def_maps, module, trait_impl.trait_path.clone()) { - Ok(trait_id) => Some(trait_id), - Err(error) => { - errors.push((error.into(), trait_impl.file_id)); - None - } - }; - - if let Some(trait_id) = trait_impl.trait_id { - errors - .extend(collect_trait_impl_methods(interner, def_maps, crate_id, trait_id, trait_impl)); - - 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()); - } - } - } - } - errors -} - -fn collect_trait_impls( - context: &mut Context, - crate_id: CrateId, - collected_impls: &mut [UnresolvedTraitImpl], -) -> Vec<(CompilationError, FileId)> { - collected_impls - .iter_mut() - .flat_map(|trait_impl| collect_trait_impl(context, crate_id, trait_impl)) - .collect() -} - -fn check_trait_impl_crate_coherence( - interner: &mut NodeInterner, - the_trait: &Trait, - trait_impl: &UnresolvedTraitImpl, - current_crate: CrateId, - def_maps: &BTreeMap, -) -> Vec<(CompilationError, FileId)> { - let mut errors: Vec<(CompilationError, FileId)> = vec![]; - - let module = ModuleId { krate: current_crate, local_id: trait_impl.module_id }; - let file = def_maps[¤t_crate].file_id(trait_impl.module_id); - let path_resolver = StandardPathResolver::new(module); - let mut resolver = Resolver::new(interner, &path_resolver, def_maps, file); - - let object_crate = match resolver.resolve_type(trait_impl.object_type.clone()) { - Type::Struct(struct_type, _) => struct_type.borrow().id.krate(), - _ => CrateId::Dummy, - }; - - if current_crate != the_trait.crate_id && current_crate != object_crate { - let error = DefCollectorErrorKind::TraitImplOrphaned { - span: trait_impl.object_type.span.expect("object type must have a span"), - }; - errors.push((error.into(), trait_impl.file_id)); - } - - errors -} - -fn resolve_trait_by_path( - def_maps: &BTreeMap, - module: ModuleId, - path: Path, -) -> Result { - let path_resolver = StandardPathResolver::new(module); - - match path_resolver.resolve(def_maps, path.clone()) { - Ok(ModuleDefId::TraitId(trait_id)) => Ok(trait_id), - Ok(_) => Err(DefCollectorErrorKind::NotATrait { not_a_trait_name: path }), - Err(_) => Err(DefCollectorErrorKind::TraitNotFound { trait_path: path }), - } -} - -fn get_struct_type(typ: &Type) -> Option<&Shared> { - match typ { - Type::Struct(definition, _) => Some(definition), - _ => None, - } -} - /// Separate the globals Vec into two. The first element in the tuple will be the /// literal globals, except for arrays, and the second will be all other globals. /// We exclude array literals as they can contain complex types @@ -625,50 +370,6 @@ fn filter_literal_globals( }) } -pub struct ResolvedGlobals { - pub globals: Vec<(FileId, StmtId)>, - pub errors: Vec<(CompilationError, FileId)>, -} - -impl ResolvedGlobals { - pub fn extend(&mut self, oth: Self) { - self.globals.extend(oth.globals); - self.errors.extend(oth.errors); - } -} - -fn resolve_globals( - context: &mut Context, - globals: Vec, - crate_id: CrateId, -) -> ResolvedGlobals { - let mut errors: Vec<(CompilationError, FileId)> = vec![]; - let globals = vecmap(globals, |global| { - let module_id = ModuleId { local_id: global.module_id, krate: crate_id }; - let path_resolver = StandardPathResolver::new(module_id); - let storage_slot = context.next_storage_slot(module_id); - - let mut resolver = Resolver::new( - &mut context.def_interner, - &path_resolver, - &context.def_maps, - global.file_id, - ); - - let name = global.stmt_def.pattern.name_ident().clone(); - - let hir_stmt = resolver.resolve_global_let(global.stmt_def); - errors.extend(take_errors(global.file_id, resolver)); - - context.def_interner.update_global(global.stmt_id, hir_stmt); - - context.def_interner.push_global(global.stmt_id, name, global.module_id, storage_slot); - - (global.file_id, global.stmt_id) - }); - ResolvedGlobals { globals, errors } -} - fn type_check_globals( interner: &mut NodeInterner, global_ids: Vec<(FileId, StmtId)>, @@ -701,360 +402,8 @@ fn type_check_functions( .collect() } -/// Create the mappings from TypeId -> StructType -/// so that expressions can access the fields of structs -fn resolve_structs( - context: &mut Context, - structs: BTreeMap, - crate_id: CrateId, -) -> Vec<(CompilationError, FileId)> { - let mut errors: Vec<(CompilationError, FileId)> = vec![]; - // Resolve each field in each struct. - // Each struct should already be present in the NodeInterner after def collection. - for (type_id, typ) in structs { - let file_id = typ.file_id; - let (generics, fields, resolver_errors) = resolve_struct_fields(context, crate_id, typ); - errors.extend(vecmap(resolver_errors, |err| (err.into(), file_id))); - context.def_interner.update_struct(type_id, |struct_def| { - struct_def.set_fields(fields); - struct_def.generics = generics; - }); - } - errors -} - -fn resolve_trait_types( - _context: &mut Context, - _crate_id: CrateId, - _unresolved_trait: &UnresolvedTrait, -) -> (Vec, Vec<(CompilationError, FileId)>) { - // TODO - (vec![], vec![]) -} -fn resolve_trait_constants( - _context: &mut Context, - _crate_id: CrateId, - _unresolved_trait: &UnresolvedTrait, -) -> (Vec, Vec<(CompilationError, FileId)>) { - // TODO - (vec![], vec![]) -} - -fn resolve_trait_methods( - context: &mut Context, - trait_id: TraitId, - crate_id: CrateId, - unresolved_trait: &UnresolvedTrait, -) -> (Vec, Vec<(CompilationError, FileId)>) { - let interner = &mut context.def_interner; - let def_maps = &mut context.def_maps; - - let path_resolver = StandardPathResolver::new(ModuleId { - local_id: unresolved_trait.module_id, - krate: crate_id, - }); - let file = def_maps[&crate_id].file_id(unresolved_trait.module_id); - - let mut res = vec![]; - let mut resolver_errors = vec![]; - for item in &unresolved_trait.trait_def.items { - if let TraitItem::Function { - name, - generics, - parameters, - return_type, - where_clause: _, - body: _, - } = item - { - let the_trait = interner.get_trait(trait_id); - let self_type = - Type::TypeVariable(the_trait.self_type_typevar.clone(), TypeVariableKind::Normal); - - let mut resolver = Resolver::new(interner, &path_resolver, def_maps, file); - resolver.add_generics(generics); - resolver.set_self_type(Some(self_type)); - - let arguments = vecmap(parameters, |param| resolver.resolve_type(param.1.clone())); - let resolved_return_type = resolver.resolve_type(return_type.get_type().into_owned()); - let generics = resolver.get_generics().to_vec(); - - let name = name.clone(); - let span: Span = name.span(); - let default_impl_list: Vec<_> = unresolved_trait - .fns_with_default_impl - .functions - .iter() - .filter(|(_, _, q)| q.name() == name.0.contents) - .collect(); - let default_impl = if default_impl_list.len() == 1 { - Some(Box::new(default_impl_list[0].2.clone())) - } else { - None - }; - - let f = TraitFunction { - name, - generics, - arguments, - return_type: resolved_return_type, - span, - default_impl, - default_impl_file_id: unresolved_trait.file_id, - default_impl_module_id: unresolved_trait.module_id, - }; - res.push(f); - resolver_errors.extend(take_errors_filter_self_not_resolved(file, resolver)); - } - } - (res, resolver_errors) -} - -fn take_errors_filter_self_not_resolved( - file_id: FileId, - resolver: Resolver<'_>, -) -> Vec<(CompilationError, FileId)> { - resolver - .take_errors() - .iter() - .filter(|resolution_error| match resolution_error { - ResolverError::PathResolutionError(PathResolutionError::Unresolved(ident)) => { - &ident.0.contents != "Self" - } - _ => true, - }) - .cloned() - .map(|resolution_error| (resolution_error.into(), file_id)) - .collect() -} - -fn take_errors(file_id: FileId, resolver: Resolver<'_>) -> Vec<(CompilationError, FileId)> { - vecmap(resolver.take_errors(), |e| (e.into(), file_id)) -} - -/// Create the mappings from TypeId -> TraitType -/// so that expressions can access the elements of traits -fn resolve_traits( - context: &mut Context, - traits: BTreeMap, - crate_id: CrateId, -) -> Vec<(CompilationError, FileId)> { - for (trait_id, unresolved_trait) in &traits { - context.def_interner.push_empty_trait(*trait_id, unresolved_trait); - } - let mut res: Vec<(CompilationError, FileId)> = vec![]; - for (trait_id, unresolved_trait) in traits { - // Resolve order - // 1. Trait Types ( Trait constants can have a trait type, therefore types before constants) - let _ = resolve_trait_types(context, crate_id, &unresolved_trait); - // 2. Trait Constants ( Trait's methods can use trait types & constants, therefore they should be after) - let _ = resolve_trait_constants(context, crate_id, &unresolved_trait); - // 3. Trait Methods - let (methods, errors) = - resolve_trait_methods(context, trait_id, crate_id, &unresolved_trait); - res.extend(errors); - context.def_interner.update_trait(trait_id, |trait_def| { - trait_def.set_methods(methods); - }); - } - res -} - -fn resolve_struct_fields( - context: &mut Context, - krate: CrateId, - unresolved: UnresolvedStruct, -) -> (Generics, Vec<(Ident, Type)>, Vec) { - let path_resolver = - StandardPathResolver::new(ModuleId { local_id: unresolved.module_id, krate }); - let file_id = unresolved.file_id; - let (generics, fields, errors) = - Resolver::new(&mut context.def_interner, &path_resolver, &context.def_maps, file_id) - .resolve_struct_fields(unresolved.struct_def); - (generics, fields, errors) -} - -fn resolve_type_aliases( - context: &mut Context, - type_aliases: BTreeMap, - crate_id: CrateId, -) -> Vec<(CompilationError, FileId)> { - let mut errors: Vec<(CompilationError, FileId)> = vec![]; - for (type_id, unresolved_typ) in type_aliases { - let path_resolver = StandardPathResolver::new(ModuleId { - local_id: unresolved_typ.module_id, - krate: crate_id, - }); - let file = unresolved_typ.file_id; - let (typ, generics, resolver_errors) = - Resolver::new(&mut context.def_interner, &path_resolver, &context.def_maps, file) - .resolve_type_aliases(unresolved_typ.type_alias_def); - errors.extend(resolver_errors.iter().cloned().map(|e| (e.into(), file))); - context.def_interner.set_type_alias(type_id, typ, generics); - } - errors -} - -fn resolve_impls( - interner: &mut NodeInterner, - crate_id: CrateId, - def_maps: &BTreeMap, - collected_impls: ImplMap, - errors: &mut Vec<(CompilationError, FileId)>, -) -> Vec<(FileId, FuncId)> { - let mut file_method_ids = Vec::new(); - - for ((unresolved_type, module_id), methods) in collected_impls { - let path_resolver = - StandardPathResolver::new(ModuleId { local_id: module_id, krate: crate_id }); - - let file = def_maps[&crate_id].file_id(module_id); - - for (generics, _, functions) in methods { - let mut resolver = Resolver::new(interner, &path_resolver, def_maps, file); - resolver.add_generics(&generics); - let generics = resolver.get_generics().to_vec(); - let self_type = resolver.resolve_type(unresolved_type.clone()); - - let mut file_func_ids = resolve_function_set( - interner, - crate_id, - def_maps, - functions, - Some(self_type.clone()), - None, - generics, - errors, - ); - if self_type != Type::Error { - for (file_id, method_id) in &file_func_ids { - 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, false) - { - let error = ResolverError::DuplicateDefinition { - name: method_name, - first_span: interner.function_ident(&first_fn).span(), - second_span: interner.function_ident(method_id).span(), - }; - errors.push((error.into(), *file_id)); - } - } - } - file_method_ids.append(&mut file_func_ids); - } - } - - file_method_ids -} - -fn resolve_trait_impls( - context: &mut Context, - traits: Vec, - crate_id: CrateId, - errors: &mut Vec<(CompilationError, FileId)>, -) -> Vec<(FileId, FuncId)> { - let interner = &mut context.def_interner; - let mut methods = Vec::<(FileId, FuncId)>::new(); - - for trait_impl in traits { - let unresolved_type = trait_impl.object_type; - 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 self_type_span = unresolved_type.span; - - 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 impl_id = interner.next_trait_impl_id(); - - let mut impl_methods = resolve_function_set( - interner, - crate_id, - &context.def_maps, - trait_impl.methods.clone(), - Some(self_type.clone()), - Some(impl_id), - generics.clone(), - 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_generics(generics); - new_resolver.set_self_type(Some(self_type.clone())); - - if let Some(trait_id) = maybe_trait_id { - check_methods_signatures( - &mut new_resolver, - &impl_methods, - trait_id, - trait_impl.generics.len(), - errors, - ); - - let where_clause = trait_impl - .where_clause - .into_iter() - .flat_map(|item| new_resolver.resolve_trait_constraint(item)) - .collect(); - - 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, - where_clause, - methods: vecmap(&impl_methods, |(_, func_id)| *func_id), - }); - - if let Err((prev_span, prev_file)) = interner.add_trait_implementation( - self_type.clone(), - trait_id, - impl_id, - resolved_trait_impl, - ) { - let error = DefCollectorErrorKind::OverlappingImpl { - typ: self_type.clone(), - 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); - } - } - - methods -} - // TODO(vitkov): Move this out of here and into type_check -fn check_methods_signatures( +pub(crate) fn check_methods_signatures( resolver: &mut Resolver, impl_methods: &Vec<(FileId, FuncId)>, trait_id: TraitId, @@ -1147,72 +496,3 @@ fn check_methods_signatures( the_trait.self_type_typevar.borrow_mut().unbind(the_trait.self_type_typevar_id); } - -fn resolve_free_functions( - interner: &mut NodeInterner, - crate_id: CrateId, - def_maps: &BTreeMap, - collected_functions: Vec, - self_type: Option, - errors: &mut Vec<(CompilationError, FileId)>, -) -> Vec<(FileId, FuncId)> { - // Lower each function in the crate. This is now possible since imports have been resolved - collected_functions - .into_iter() - .flat_map(|unresolved_functions| { - resolve_function_set( - interner, - crate_id, - def_maps, - unresolved_functions, - self_type.clone(), - None, - vec![], // no impl generics - errors, - ) - }) - .collect() -} - -#[allow(clippy::too_many_arguments)] -fn resolve_function_set( - interner: &mut NodeInterner, - crate_id: CrateId, - def_maps: &BTreeMap, - mut unresolved_functions: UnresolvedFunctions, - self_type: Option, - trait_impl_id: Option, - impl_generics: Vec<(Rc, Shared, Span)>, - errors: &mut Vec<(CompilationError, FileId)>, -) -> Vec<(FileId, FuncId)> { - let file_id = unresolved_functions.file_id; - - let where_clause_errors = - unresolved_functions.resolve_trait_bounds_trait_ids(def_maps, crate_id); - errors.extend(where_clause_errors.iter().cloned().map(|e| (e.into(), file_id))); - - vecmap(unresolved_functions.functions, |(mod_id, func_id, func)| { - let module_id = ModuleId { krate: crate_id, local_id: mod_id }; - let path_resolver = StandardPathResolver::new(module_id); - - let mut resolver = Resolver::new(interner, &path_resolver, def_maps, file_id); - // Must use set_generics here to ensure we re-use the same generics from when - // the impl was originally collected. Otherwise the function will be using different - // TypeVariables for the same generic, causing it to instantiate incorrectly. - resolver.set_generics(impl_generics.clone()); - resolver.set_self_type(self_type.clone()); - resolver.set_trait_id(unresolved_functions.trait_id); - resolver.set_trait_impl_id(trait_impl_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); - errors.extend(errs.iter().cloned().map(|e| (e.into(), file_id))); - (file_id, func_id) - }) -} 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 17aa5e9951f..2f79333620e 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs @@ -528,11 +528,11 @@ impl<'a> ModCollector<'a> { }; errors.push((error.into(), location.file)); - let error2 = DefCollectorErrorKind::ModuleOriginallyDefined { + let error = DefCollectorErrorKind::ModuleOriginallyDefined { mod_name: mod_name.clone(), span: old_location.span, }; - errors.push((error2.into(), old_location.file)); + errors.push((error.into(), old_location.file)); return errors; } diff --git a/compiler/noirc_frontend/src/hir/def_collector/errors.rs b/compiler/noirc_frontend/src/hir/def_collector/errors.rs index edb39fe68d7..2b91c4b36c5 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/errors.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/errors.rs @@ -73,15 +73,16 @@ pub enum DefCollectorErrorKind { "Either the type or the trait must be from the same crate as the trait implementation" )] TraitImplOrphaned { span: Span }, + #[error("macro error : {0:?}")] + MacroError(MacroError), +} - // Aztec feature flag errors - // TODO(benesjan): https://github.com/AztecProtocol/aztec-packages/issues/2905 - #[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 }, +/// An error struct that macro processors can return. +#[derive(Debug, Clone)] +pub struct MacroError { + pub primary_message: String, + pub secondary_message: Option, + pub span: Option, } impl DefCollectorErrorKind { @@ -245,16 +246,9 @@ impl From for Diagnostic { "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 - ), + DefCollectorErrorKind::MacroError(macro_error) => { + Diagnostic::simple_error(macro_error.primary_message, macro_error.secondary_message.unwrap_or_default(), macro_error.span.unwrap_or_default()) + }, } } } diff --git a/compiler/noirc_frontend/src/hir/def_map/mod.rs b/compiler/noirc_frontend/src/hir/def_map/mod.rs index 345e5447bf5..5f38c80a5fe 100644 --- a/compiler/noirc_frontend/src/hir/def_map/mod.rs +++ b/compiler/noirc_frontend/src/hir/def_map/mod.rs @@ -1,6 +1,7 @@ use crate::graph::CrateId; use crate::hir::def_collector::dc_crate::{CompilationError, DefCollector}; use crate::hir::Context; +use crate::macros_api::MacroProcessor; use crate::node_interner::{FuncId, NodeInterner, StructId}; use crate::parser::{parse_program, ParsedModule, ParserError}; use crate::token::{FunctionAttribute, SecondaryAttribute, TestScope}; @@ -17,6 +18,8 @@ pub use module_data::*; mod namespace; pub use namespace::*; +use super::def_collector::errors::DefCollectorErrorKind; + /// The name that is used for a non-contract program's entry-point function. pub const MAIN_FUNCTION: &str = "main"; @@ -69,6 +72,7 @@ impl CrateDefMap { pub fn collect_defs( crate_id: CrateId, context: &mut Context, + macro_processors: Vec<&dyn MacroProcessor>, ) -> Vec<(CompilationError, FileId)> { // Check if this Crate has already been compiled // XXX: There is probably a better alternative for this. @@ -83,16 +87,18 @@ impl CrateDefMap { // First parse the root file. let root_file_id = context.crate_graph[crate_id].root_file_id; let (ast, parsing_errors) = parse_file(&context.file_manager, root_file_id); - let ast = ast.into_sorted(); - - #[cfg(feature = "aztec")] - let ast = match super::aztec_library::transform(ast, &crate_id, context) { - Ok(ast) => ast, - Err((error, file_id)) => { - errors.push((error.into(), file_id)); - return errors; - } - }; + let mut ast = ast.into_sorted(); + + for macro_processor in ¯o_processors { + ast = match macro_processor.process_untyped_ast(ast, &crate_id, context) { + Ok(ast) => ast, + Err((error, file_id)) => { + let def_error = DefCollectorErrorKind::MacroError(error); + errors.push((def_error.into(), file_id)); + return errors; + } + }; + } // Allocate a default Module for the root, giving it a ModuleId let mut modules: Arena = Arena::default(); @@ -107,7 +113,13 @@ impl CrateDefMap { }; // Now we want to populate the CrateDefMap using the DefCollector - errors.extend(DefCollector::collect(def_map, context, ast, root_file_id)); + errors.extend(DefCollector::collect( + def_map, + context, + ast, + root_file_id, + macro_processors.clone(), + )); errors.extend( parsing_errors.iter().map(|e| (e.clone().into(), root_file_id)).collect::>(), diff --git a/compiler/noirc_frontend/src/hir/mod.rs b/compiler/noirc_frontend/src/hir/mod.rs index 5a28d7b779a..154b695d552 100644 --- a/compiler/noirc_frontend/src/hir/mod.rs +++ b/compiler/noirc_frontend/src/hir/mod.rs @@ -4,9 +4,6 @@ pub mod resolution; pub mod scope; pub mod type_check; -#[cfg(feature = "aztec")] -pub(crate) mod aztec_library; - use crate::graph::{CrateGraph, CrateId}; use crate::hir_def::function::FuncMeta; use crate::node_interner::{FuncId, NodeInterner, StructId}; @@ -29,10 +26,6 @@ pub struct Context { /// A map of each file that already has been visited from a prior `mod foo;` declaration. /// This is used to issue an error if a second `mod foo;` is declared to the same file. pub visited_files: BTreeMap, - - /// Maps a given (contract) module id to the next available storage slot - /// for that contract. - pub storage_slots: BTreeMap, } #[derive(Debug, Copy, Clone)] @@ -42,8 +35,6 @@ pub enum FunctionNameMatch<'a> { Contains(&'a str), } -pub type StorageSlot = u32; - impl Context { pub fn new(file_manager: FileManager, crate_graph: CrateGraph) -> Context { Context { @@ -52,7 +43,6 @@ impl Context { visited_files: BTreeMap::new(), crate_graph, file_manager, - storage_slots: BTreeMap::new(), } } @@ -200,16 +190,4 @@ impl Context { fn module(&self, module_id: def_map::ModuleId) -> &def_map::ModuleData { module_id.module(&self.def_maps) } - - /// Returns the next available storage slot in the given module. - /// Returns None if the given module is not a contract module. - fn next_storage_slot(&mut self, module_id: def_map::ModuleId) -> Option { - let module = self.module(module_id); - - module.is_contract.then(|| { - let next_slot = self.storage_slots.entry(module_id).or_insert(0); - *next_slot += 1; - *next_slot - }) - } } diff --git a/compiler/noirc_frontend/src/hir/resolution/functions.rs b/compiler/noirc_frontend/src/hir/resolution/functions.rs new file mode 100644 index 00000000000..387f94e129c --- /dev/null +++ b/compiler/noirc_frontend/src/hir/resolution/functions.rs @@ -0,0 +1,85 @@ +use std::{collections::BTreeMap, rc::Rc}; + +use fm::FileId; +use iter_extended::vecmap; +use noirc_errors::Span; + +use crate::{ + graph::CrateId, + hir::{ + def_collector::dc_crate::{CompilationError, UnresolvedFunctions}, + def_map::{CrateDefMap, ModuleId}, + }, + node_interner::{FuncId, NodeInterner, TraitImplId}, + Shared, Type, TypeBinding, +}; + +use super::{path_resolver::StandardPathResolver, resolver::Resolver}; + +#[allow(clippy::too_many_arguments)] +pub(crate) fn resolve_function_set( + interner: &mut NodeInterner, + crate_id: CrateId, + def_maps: &BTreeMap, + mut unresolved_functions: UnresolvedFunctions, + self_type: Option, + trait_impl_id: Option, + impl_generics: Vec<(Rc, Shared, Span)>, + errors: &mut Vec<(CompilationError, FileId)>, +) -> Vec<(FileId, FuncId)> { + let file_id = unresolved_functions.file_id; + + let where_clause_errors = + unresolved_functions.resolve_trait_bounds_trait_ids(def_maps, crate_id); + errors.extend(where_clause_errors.iter().cloned().map(|e| (e.into(), file_id))); + + vecmap(unresolved_functions.functions, |(mod_id, func_id, func)| { + let module_id = ModuleId { krate: crate_id, local_id: mod_id }; + let path_resolver = StandardPathResolver::new(module_id); + + let mut resolver = Resolver::new(interner, &path_resolver, def_maps, file_id); + // Must use set_generics here to ensure we re-use the same generics from when + // the impl was originally collected. Otherwise the function will be using different + // TypeVariables for the same generic, causing it to instantiate incorrectly. + resolver.set_generics(impl_generics.clone()); + resolver.set_self_type(self_type.clone()); + resolver.set_trait_id(unresolved_functions.trait_id); + resolver.set_trait_impl_id(trait_impl_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); + errors.extend(errs.iter().cloned().map(|e| (e.into(), file_id))); + (file_id, func_id) + }) +} + +pub(crate) fn resolve_free_functions( + interner: &mut NodeInterner, + crate_id: CrateId, + def_maps: &BTreeMap, + collected_functions: Vec, + self_type: Option, + errors: &mut Vec<(CompilationError, FileId)>, +) -> Vec<(FileId, FuncId)> { + collected_functions + .into_iter() + .flat_map(|unresolved_functions| { + resolve_function_set( + interner, + crate_id, + def_maps, + unresolved_functions, + self_type.clone(), + None, + vec![], // no impl generics + errors, + ) + }) + .collect() +} diff --git a/compiler/noirc_frontend/src/hir/resolution/globals.rs b/compiler/noirc_frontend/src/hir/resolution/globals.rs new file mode 100644 index 00000000000..b5aec212dbf --- /dev/null +++ b/compiler/noirc_frontend/src/hir/resolution/globals.rs @@ -0,0 +1,55 @@ +use super::{path_resolver::StandardPathResolver, resolver::Resolver, take_errors}; +use crate::{ + graph::CrateId, + hir::{ + def_collector::dc_crate::{CompilationError, UnresolvedGlobal}, + def_map::ModuleId, + Context, + }, + node_interner::StmtId, +}; +use fm::FileId; +use iter_extended::vecmap; + +pub(crate) struct ResolvedGlobals { + pub(crate) globals: Vec<(FileId, StmtId)>, + pub(crate) errors: Vec<(CompilationError, FileId)>, +} + +impl ResolvedGlobals { + pub(crate) fn extend(&mut self, oth: Self) { + self.globals.extend(oth.globals); + self.errors.extend(oth.errors); + } +} + +pub(crate) fn resolve_globals( + context: &mut Context, + globals: Vec, + crate_id: CrateId, +) -> ResolvedGlobals { + let mut errors: Vec<(CompilationError, FileId)> = vec![]; + let globals = vecmap(globals, |global| { + let module_id = ModuleId { local_id: global.module_id, krate: crate_id }; + let path_resolver = StandardPathResolver::new(module_id); + + let mut resolver = Resolver::new( + &mut context.def_interner, + &path_resolver, + &context.def_maps, + global.file_id, + ); + + let name = global.stmt_def.pattern.name_ident().clone(); + + let hir_stmt = resolver.resolve_global_let(global.stmt_def); + errors.extend(take_errors(global.file_id, resolver)); + + context.def_interner.update_global(global.stmt_id, hir_stmt); + + context.def_interner.push_global(global.stmt_id, name, global.module_id); + + (global.file_id, global.stmt_id) + }); + ResolvedGlobals { globals, errors } +} diff --git a/compiler/noirc_frontend/src/hir/resolution/impls.rs b/compiler/noirc_frontend/src/hir/resolution/impls.rs new file mode 100644 index 00000000000..4aa70f00cfc --- /dev/null +++ b/compiler/noirc_frontend/src/hir/resolution/impls.rs @@ -0,0 +1,137 @@ +use std::collections::BTreeMap; + +use fm::FileId; + +use crate::{ + graph::CrateId, + hir::{ + def_collector::{ + dc_crate::{CompilationError, ImplMap}, + errors::DefCollectorErrorKind, + }, + def_map::{CrateDefMap, ModuleId}, + Context, + }, + node_interner::{FuncId, NodeInterner}, + Type, +}; + +use super::{ + errors::ResolverError, functions, get_module_mut, get_struct_type, + path_resolver::StandardPathResolver, resolver::Resolver, take_errors, +}; + +/// Go through the list of impls and add each function within to the scope +/// of the module defined by its type. +pub(crate) fn collect_impls( + context: &mut Context, + crate_id: CrateId, + collected_impls: &ImplMap, +) -> Vec<(CompilationError, FileId)> { + let interner = &mut context.def_interner; + let def_maps = &mut context.def_maps; + let mut errors: Vec<(CompilationError, FileId)> = vec![]; + + for ((unresolved_type, module_id), methods) in collected_impls { + let path_resolver = + StandardPathResolver::new(ModuleId { local_id: *module_id, krate: crate_id }); + + let file = def_maps[&crate_id].file_id(*module_id); + + for (generics, span, unresolved) in methods { + let mut resolver = Resolver::new(interner, &path_resolver, def_maps, file); + resolver.add_generics(generics); + let typ = resolver.resolve_type(unresolved_type.clone()); + + errors.extend(take_errors(unresolved.file_id, resolver)); + + if let Some(struct_type) = get_struct_type(&typ) { + let struct_type = struct_type.borrow(); + + // `impl`s are only allowed on types defined within the current crate + if struct_type.id.krate() != crate_id { + let span = *span; + let type_name = struct_type.name.to_string(); + let error = DefCollectorErrorKind::ForeignImpl { span, type_name }; + errors.push((error.into(), unresolved.file_id)); + continue; + } + + // 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 = 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 + // 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()); + } + } + // Prohibit defining impls for primitive types if we're not in the stdlib + } else if typ != Type::Error && !crate_id.is_stdlib() { + let span = *span; + let error = DefCollectorErrorKind::NonStructTypeInImpl { span }; + errors.push((error.into(), unresolved.file_id)); + } + } + } + errors +} + +pub(crate) fn resolve_impls( + interner: &mut NodeInterner, + crate_id: CrateId, + def_maps: &BTreeMap, + collected_impls: ImplMap, + errors: &mut Vec<(CompilationError, FileId)>, +) -> Vec<(FileId, FuncId)> { + let mut file_method_ids = Vec::new(); + + for ((unresolved_type, module_id), methods) in collected_impls { + let path_resolver = + StandardPathResolver::new(ModuleId { local_id: module_id, krate: crate_id }); + + let file = def_maps[&crate_id].file_id(module_id); + + for (generics, _, functions) in methods { + let mut resolver = Resolver::new(interner, &path_resolver, def_maps, file); + resolver.add_generics(&generics); + let generics = resolver.get_generics().to_vec(); + let self_type = resolver.resolve_type(unresolved_type.clone()); + + let mut file_func_ids = functions::resolve_function_set( + interner, + crate_id, + def_maps, + functions, + Some(self_type.clone()), + None, + generics, + errors, + ); + if self_type != Type::Error { + for (file_id, method_id) in &file_func_ids { + 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, false) + { + let error = ResolverError::DuplicateDefinition { + name: method_name, + first_span: interner.function_ident(&first_fn).span(), + second_span: interner.function_ident(method_id).span(), + }; + errors.push((error.into(), *file_id)); + } + } + } + file_method_ids.append(&mut file_func_ids); + } + } + + file_method_ids +} diff --git a/compiler/noirc_frontend/src/hir/resolution/import.rs b/compiler/noirc_frontend/src/hir/resolution/import.rs index 6f3140a65d4..5b59dcd2241 100644 --- a/compiler/noirc_frontend/src/hir/resolution/import.rs +++ b/compiler/noirc_frontend/src/hir/resolution/import.rs @@ -1,5 +1,5 @@ use iter_extended::partition_results; -use noirc_errors::CustomDiagnostic; +use noirc_errors::{CustomDiagnostic, Span}; use crate::graph::CrateId; use std::collections::BTreeMap; @@ -202,7 +202,11 @@ fn resolve_external_dep( // Create an import directive for the dependency crate let path_without_crate_name = &path[1..]; // XXX: This will panic if the path is of the form `use dep::std` Ideal algorithm will not distinguish between crate and module - let path = Path { segments: path_without_crate_name.to_vec(), kind: PathKind::Plain }; + let path = Path { + segments: path_without_crate_name.to_vec(), + kind: PathKind::Plain, + span: Span::default(), + }; let dep_directive = ImportDirective { module_id: dep_module.local_id, path, alias: directive.alias.clone() }; diff --git a/compiler/noirc_frontend/src/hir/resolution/mod.rs b/compiler/noirc_frontend/src/hir/resolution/mod.rs index 601e78015ca..8c16a9cca80 100644 --- a/compiler/noirc_frontend/src/hir/resolution/mod.rs +++ b/compiler/noirc_frontend/src/hir/resolution/mod.rs @@ -9,3 +9,50 @@ pub mod errors; pub mod import; pub mod path_resolver; pub mod resolver; + +mod functions; +mod globals; +mod impls; +mod structs; +mod traits; +mod type_aliases; + +pub(crate) use functions::resolve_free_functions; +pub(crate) use globals::resolve_globals; +pub(crate) use impls::{collect_impls, resolve_impls}; +pub(crate) use structs::resolve_structs; +pub(crate) use traits::{ + collect_trait_impls, resolve_trait_by_path, resolve_trait_impls, resolve_traits, +}; +pub(crate) use type_aliases::resolve_type_aliases; + +use crate::{ + graph::CrateId, + hir::{ + def_collector::dc_crate::CompilationError, + def_map::{CrateDefMap, ModuleData, ModuleId}, + }, + Shared, StructType, Type, +}; +use fm::FileId; +use iter_extended::vecmap; +use resolver::Resolver; +use std::collections::BTreeMap; + +fn take_errors(file_id: FileId, resolver: Resolver<'_>) -> Vec<(CompilationError, FileId)> { + vecmap(resolver.take_errors(), |e| (e.into(), file_id)) +} + +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 get_struct_type(typ: &Type) -> Option<&Shared> { + match typ { + Type::Struct(definition, _) => Some(definition), + _ => None, + } +} diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index e1b4a8bcce0..a788137a545 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -29,7 +29,7 @@ use crate::hir::def_map::{LocalModuleId, ModuleDefId, TryFromModuleDefId, MAIN_F use crate::hir_def::stmt::{HirAssignStatement, HirForStatement, HirLValue, HirPattern}; use crate::node_interner::{ DefinitionId, DefinitionKind, ExprId, FuncId, NodeInterner, StmtId, StructId, TraitId, - TraitImplId, + TraitImplId, TraitImplKind, }; use crate::{ hir::{def_map::CrateDefMap, resolution::path_resolver::PathResolver}, @@ -38,7 +38,7 @@ use crate::{ }; use crate::{ ArrayLiteral, ContractFunctionType, Distinctness, ForRange, FunctionVisibility, Generics, - LValue, NoirStruct, NoirTypeAlias, Path, PathKind, Pattern, Shared, StructType, Type, + LValue, NoirStruct, NoirTypeAlias, Param, Path, PathKind, Pattern, Shared, StructType, Type, TypeAliasType, TypeBinding, TypeVariable, UnaryOp, UnresolvedGenerics, UnresolvedTraitConstraint, UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression, Visibility, ERROR_IDENT, @@ -441,6 +441,7 @@ impl<'a> Resolver<'a> { MutableReference(element) => { Type::MutableReference(Box::new(self.resolve_type_inner(*element, new_variables))) } + Parenthesized(typ) => self.resolve_type_inner(*typ, new_variables), } } @@ -760,7 +761,7 @@ impl<'a> Resolver<'a> { let mut parameters = vec![]; let mut parameter_types = vec![]; - for (pattern, typ, visibility) in func.parameters().iter().cloned() { + for Param { visibility, pattern, typ, span: _ } in func.parameters().iter().cloned() { if visibility == Visibility::Public && !self.pub_allowed(func) { self.push_err(ResolverError::UnnecessaryPub { ident: func.name_ident().clone(), @@ -1203,12 +1204,17 @@ impl<'a> Resolver<'a> { } Literal::Integer(integer) => HirLiteral::Integer(integer), Literal::Str(str) => HirLiteral::Str(str), + Literal::RawStr(str, _) => HirLiteral::Str(str), Literal::FmtStr(str) => self.resolve_fmt_str_literal(str, expr.span), Literal::Unit => HirLiteral::Unit, }), ExpressionKind::Variable(path) => { - if let Some(expr) = self.resolve_trait_generic_path(&path) { - expr + if let Some((hir_expr, object_type)) = self.resolve_trait_generic_path(&path) { + let expr_id = self.interner.push_expr(hir_expr); + self.interner.push_expr_location(expr_id, expr.span, self.file); + self.interner + .select_impl_for_ident(expr_id, TraitImplKind::Assumed { object_type }); + return expr_id; } else { // If the Path is being used as an Expression, then it is referring to a global from a separate module // Otherwise, then it is referring to an Identifier @@ -1370,6 +1376,8 @@ impl<'a> Resolver<'a> { ExpressionKind::Parenthesized(sub_expr) => return self.resolve_expression(*sub_expr), }; + // If these lines are ever changed, make sure to change the early return + // in the ExpressionKind::Variable case as well let expr_id = self.interner.push_expr(hir_expr); self.interner.push_expr_location(expr_id, expr.span, self.file); expr_id @@ -1576,7 +1584,10 @@ impl<'a> Resolver<'a> { } // this resolves Self::some_static_method, inside an impl block (where we don't have a concrete self_type) - fn resolve_trait_static_method_by_self(&mut self, path: &Path) -> Option { + fn resolve_trait_static_method_by_self( + &mut self, + path: &Path, + ) -> Option<(HirExpression, Type)> { if let Some(trait_id) = self.trait_id { if path.kind == PathKind::Plain && path.segments.len() == 2 { let name = &path.segments[0].0.contents; @@ -1590,7 +1601,7 @@ impl<'a> Resolver<'a> { the_trait.self_type_typevar, crate::TypeVariableKind::Normal, ); - return Some(HirExpression::TraitMethodReference(self_type, method)); + return Some((HirExpression::TraitMethodReference(method), self_type)); } } } @@ -1599,7 +1610,10 @@ impl<'a> Resolver<'a> { } // this resolves a static trait method T::trait_method by iterating over the where clause - fn resolve_trait_method_by_named_generic(&mut self, path: &Path) -> Option { + fn resolve_trait_method_by_named_generic( + &mut self, + path: &Path, + ) -> Option<(HirExpression, Type)> { if path.segments.len() != 2 { return None; } @@ -1621,7 +1635,7 @@ impl<'a> Resolver<'a> { the_trait.find_method(path.segments.last().unwrap().clone()) { let self_type = self.resolve_type(typ.clone()); - return Some(HirExpression::TraitMethodReference(self_type, method)); + return Some((HirExpression::TraitMethodReference(method), self_type)); } } } @@ -1629,7 +1643,7 @@ impl<'a> Resolver<'a> { None } - fn resolve_trait_generic_path(&mut self, path: &Path) -> Option { + fn resolve_trait_generic_path(&mut self, path: &Path) -> Option<(HirExpression, Type)> { self.resolve_trait_static_method_by_self(path) .or_else(|| self.resolve_trait_method_by_named_generic(path)) } @@ -1774,6 +1788,7 @@ impl<'a> Resolver<'a> { self.verify_type_valid_for_program_input(element); } } + UnresolvedTypeData::Parenthesized(typ) => self.verify_type_valid_for_program_input(typ), } } diff --git a/compiler/noirc_frontend/src/hir/resolution/structs.rs b/compiler/noirc_frontend/src/hir/resolution/structs.rs new file mode 100644 index 00000000000..72a7b736436 --- /dev/null +++ b/compiler/noirc_frontend/src/hir/resolution/structs.rs @@ -0,0 +1,53 @@ +use std::collections::BTreeMap; + +use fm::FileId; +use iter_extended::vecmap; + +use crate::{ + graph::CrateId, + hir::{ + def_collector::dc_crate::{CompilationError, UnresolvedStruct}, + def_map::ModuleId, + Context, + }, + node_interner::StructId, + Generics, Ident, Type, +}; + +use super::{errors::ResolverError, path_resolver::StandardPathResolver, resolver::Resolver}; + +/// Create the mappings from TypeId -> StructType +/// so that expressions can access the fields of structs +pub(crate) fn resolve_structs( + context: &mut Context, + structs: BTreeMap, + crate_id: CrateId, +) -> Vec<(CompilationError, FileId)> { + let mut errors: Vec<(CompilationError, FileId)> = vec![]; + // Resolve each field in each struct. + // Each struct should already be present in the NodeInterner after def collection. + for (type_id, typ) in structs { + let file_id = typ.file_id; + let (generics, fields, resolver_errors) = resolve_struct_fields(context, crate_id, typ); + errors.extend(vecmap(resolver_errors, |err| (err.into(), file_id))); + context.def_interner.update_struct(type_id, |struct_def| { + struct_def.set_fields(fields); + struct_def.generics = generics; + }); + } + errors +} + +fn resolve_struct_fields( + context: &mut Context, + krate: CrateId, + unresolved: UnresolvedStruct, +) -> (Generics, Vec<(Ident, Type)>, Vec) { + let path_resolver = + StandardPathResolver::new(ModuleId { local_id: unresolved.module_id, krate }); + let file_id = unresolved.file_id; + let (generics, fields, errors) = + Resolver::new(&mut context.def_interner, &path_resolver, &context.def_maps, file_id) + .resolve_struct_fields(unresolved.struct_def); + (generics, fields, errors) +} diff --git a/compiler/noirc_frontend/src/hir/resolution/traits.rs b/compiler/noirc_frontend/src/hir/resolution/traits.rs new file mode 100644 index 00000000000..702e96362a6 --- /dev/null +++ b/compiler/noirc_frontend/src/hir/resolution/traits.rs @@ -0,0 +1,450 @@ +use std::collections::{BTreeMap, HashSet}; + +use fm::FileId; +use iter_extended::vecmap; +use noirc_errors::Span; + +use crate::{ + graph::CrateId, + hir::{ + def_collector::{ + dc_crate::{ + check_methods_signatures, CompilationError, UnresolvedTrait, UnresolvedTraitImpl, + }, + errors::{DefCollectorErrorKind, DuplicateType}, + }, + def_map::{CrateDefMap, ModuleDefId, ModuleId}, + Context, + }, + hir_def::traits::{Trait, TraitConstant, TraitFunction, TraitImpl, TraitType}, + node_interner::{FuncId, NodeInterner, TraitId}, + Path, Shared, TraitItem, Type, TypeVariableKind, +}; + +use super::{ + errors::ResolverError, + functions, get_module_mut, get_struct_type, + import::PathResolutionError, + path_resolver::{PathResolver, StandardPathResolver}, + resolver::Resolver, + take_errors, +}; + +/// Create the mappings from TypeId -> TraitType +/// so that expressions can access the elements of traits +pub(crate) fn resolve_traits( + context: &mut Context, + traits: BTreeMap, + crate_id: CrateId, +) -> Vec<(CompilationError, FileId)> { + for (trait_id, unresolved_trait) in &traits { + context.def_interner.push_empty_trait(*trait_id, unresolved_trait); + } + let mut res: Vec<(CompilationError, FileId)> = vec![]; + for (trait_id, unresolved_trait) in traits { + // Resolve order + // 1. Trait Types ( Trait constants can have a trait type, therefore types before constants) + let _ = resolve_trait_types(context, crate_id, &unresolved_trait); + // 2. Trait Constants ( Trait's methods can use trait types & constants, therefore they should be after) + let _ = resolve_trait_constants(context, crate_id, &unresolved_trait); + // 3. Trait Methods + let (methods, errors) = + resolve_trait_methods(context, trait_id, crate_id, &unresolved_trait); + res.extend(errors); + context.def_interner.update_trait(trait_id, |trait_def| { + trait_def.set_methods(methods); + }); + } + res +} + +fn resolve_trait_types( + _context: &mut Context, + _crate_id: CrateId, + _unresolved_trait: &UnresolvedTrait, +) -> (Vec, Vec<(CompilationError, FileId)>) { + // TODO + (vec![], vec![]) +} +fn resolve_trait_constants( + _context: &mut Context, + _crate_id: CrateId, + _unresolved_trait: &UnresolvedTrait, +) -> (Vec, Vec<(CompilationError, FileId)>) { + // TODO + (vec![], vec![]) +} + +fn resolve_trait_methods( + context: &mut Context, + trait_id: TraitId, + crate_id: CrateId, + unresolved_trait: &UnresolvedTrait, +) -> (Vec, Vec<(CompilationError, FileId)>) { + let interner = &mut context.def_interner; + let def_maps = &mut context.def_maps; + + let path_resolver = StandardPathResolver::new(ModuleId { + local_id: unresolved_trait.module_id, + krate: crate_id, + }); + let file = def_maps[&crate_id].file_id(unresolved_trait.module_id); + + let mut res = vec![]; + let mut resolver_errors = vec![]; + for item in &unresolved_trait.trait_def.items { + if let TraitItem::Function { + name, + generics, + parameters, + return_type, + where_clause: _, + body: _, + } = item + { + let the_trait = interner.get_trait(trait_id); + let self_type = + Type::TypeVariable(the_trait.self_type_typevar.clone(), TypeVariableKind::Normal); + + let mut resolver = Resolver::new(interner, &path_resolver, def_maps, file); + resolver.add_generics(generics); + resolver.set_self_type(Some(self_type)); + + let arguments = vecmap(parameters, |param| resolver.resolve_type(param.1.clone())); + let resolved_return_type = resolver.resolve_type(return_type.get_type().into_owned()); + let generics = resolver.get_generics().to_vec(); + + let name = name.clone(); + let span: Span = name.span(); + let default_impl_list: Vec<_> = unresolved_trait + .fns_with_default_impl + .functions + .iter() + .filter(|(_, _, q)| q.name() == name.0.contents) + .collect(); + let default_impl = if default_impl_list.len() == 1 { + Some(Box::new(default_impl_list[0].2.clone())) + } else { + None + }; + + let f = TraitFunction { + name, + generics, + arguments, + return_type: resolved_return_type, + span, + default_impl, + default_impl_file_id: unresolved_trait.file_id, + default_impl_module_id: unresolved_trait.module_id, + }; + res.push(f); + resolver_errors.extend(take_errors_filter_self_not_resolved(file, resolver)); + } + } + (res, resolver_errors) +} + +fn collect_trait_impl_methods( + interner: &mut NodeInterner, + def_maps: &BTreeMap, + crate_id: CrateId, + trait_id: TraitId, + trait_impl: &mut UnresolvedTraitImpl, +) -> Vec<(CompilationError, FileId)> { + // In this Vec methods[i] corresponds to trait.methods[i]. If the impl has no implementation + // for a particular method, the default implementation will be added at that slot. + let mut ordered_methods = Vec::new(); + + let the_trait = interner.get_trait(trait_id); + + // check whether the trait implementation is in the same crate as either the trait or the type + let mut errors = + check_trait_impl_crate_coherence(interner, &the_trait, trait_impl, crate_id, def_maps); + // set of function ids that have a corresponding method in the trait + let mut func_ids_in_trait = HashSet::new(); + + for method in &the_trait.methods { + let overrides: Vec<_> = trait_impl + .methods + .functions + .iter() + .filter(|(_, _, f)| f.name() == method.name.0.contents) + .collect(); + + if overrides.is_empty() { + if let Some(default_impl) = &method.default_impl { + let func_id = interner.push_empty_fn(); + let module = ModuleId { local_id: trait_impl.module_id, krate: crate_id }; + interner.push_function(func_id, &default_impl.def, module); + func_ids_in_trait.insert(func_id); + ordered_methods.push(( + method.default_impl_module_id, + func_id, + *default_impl.clone(), + )); + } else { + let error = DefCollectorErrorKind::TraitMissingMethod { + trait_name: the_trait.name.clone(), + method_name: method.name.clone(), + trait_impl_span: trait_impl.object_type.span.expect("type must have a span"), + }; + errors.push((error.into(), trait_impl.file_id)); + } + } else { + for (_, func_id, _) in &overrides { + func_ids_in_trait.insert(*func_id); + } + + if overrides.len() > 1 { + let error = DefCollectorErrorKind::Duplicate { + typ: DuplicateType::TraitAssociatedFunction, + first_def: overrides[0].2.name_ident().clone(), + second_def: overrides[1].2.name_ident().clone(), + }; + errors.push((error.into(), trait_impl.file_id)); + } + + ordered_methods.push(overrides[0].clone()); + } + } + + // Emit MethodNotInTrait error for methods in the impl block that + // don't have a corresponding method signature defined in the trait + for (_, func_id, func) in &trait_impl.methods.functions { + if !func_ids_in_trait.contains(func_id) { + let error = DefCollectorErrorKind::MethodNotInTrait { + trait_name: the_trait.name.clone(), + impl_method: func.name_ident().clone(), + }; + errors.push((error.into(), trait_impl.file_id)); + } + } + + trait_impl.methods.functions = ordered_methods; + trait_impl.methods.trait_id = Some(trait_id); + errors +} + +fn collect_trait_impl( + context: &mut Context, + crate_id: CrateId, + trait_impl: &mut UnresolvedTraitImpl, +) -> Vec<(CompilationError, FileId)> { + let interner = &mut context.def_interner; + let def_maps = &mut context.def_maps; + let mut errors: Vec<(CompilationError, FileId)> = vec![]; + let unresolved_type = trait_impl.object_type.clone(); + let module = ModuleId { local_id: trait_impl.module_id, krate: crate_id }; + trait_impl.trait_id = + match resolve_trait_by_path(def_maps, module, trait_impl.trait_path.clone()) { + Ok(trait_id) => Some(trait_id), + Err(error) => { + errors.push((error.into(), trait_impl.file_id)); + None + } + }; + + if let Some(trait_id) = trait_impl.trait_id { + errors + .extend(collect_trait_impl_methods(interner, def_maps, crate_id, trait_id, trait_impl)); + + 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()); + } + } + } + } + errors +} + +pub(crate) fn collect_trait_impls( + context: &mut Context, + crate_id: CrateId, + collected_impls: &mut [UnresolvedTraitImpl], +) -> Vec<(CompilationError, FileId)> { + collected_impls + .iter_mut() + .flat_map(|trait_impl| collect_trait_impl(context, crate_id, trait_impl)) + .collect() +} + +fn check_trait_impl_crate_coherence( + interner: &mut NodeInterner, + the_trait: &Trait, + trait_impl: &UnresolvedTraitImpl, + current_crate: CrateId, + def_maps: &BTreeMap, +) -> Vec<(CompilationError, FileId)> { + let mut errors: Vec<(CompilationError, FileId)> = vec![]; + + let module = ModuleId { krate: current_crate, local_id: trait_impl.module_id }; + let file = def_maps[¤t_crate].file_id(trait_impl.module_id); + let path_resolver = StandardPathResolver::new(module); + let mut resolver = Resolver::new(interner, &path_resolver, def_maps, file); + + let object_crate = match resolver.resolve_type(trait_impl.object_type.clone()) { + Type::Struct(struct_type, _) => struct_type.borrow().id.krate(), + _ => CrateId::Dummy, + }; + + if current_crate != the_trait.crate_id && current_crate != object_crate { + let error = DefCollectorErrorKind::TraitImplOrphaned { + span: trait_impl.object_type.span.expect("object type must have a span"), + }; + errors.push((error.into(), trait_impl.file_id)); + } + + errors +} + +pub(crate) fn resolve_trait_by_path( + def_maps: &BTreeMap, + module: ModuleId, + path: Path, +) -> Result { + let path_resolver = StandardPathResolver::new(module); + + match path_resolver.resolve(def_maps, path.clone()) { + Ok(ModuleDefId::TraitId(trait_id)) => Ok(trait_id), + Ok(_) => Err(DefCollectorErrorKind::NotATrait { not_a_trait_name: path }), + Err(_) => Err(DefCollectorErrorKind::TraitNotFound { trait_path: path }), + } +} +pub(crate) fn resolve_trait_impls( + context: &mut Context, + traits: Vec, + crate_id: CrateId, + errors: &mut Vec<(CompilationError, FileId)>, +) -> Vec<(FileId, FuncId)> { + let interner = &mut context.def_interner; + let mut methods = Vec::<(FileId, FuncId)>::new(); + + for trait_impl in traits { + let unresolved_type = trait_impl.object_type; + 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 self_type_span = unresolved_type.span; + + 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 impl_id = interner.next_trait_impl_id(); + + let mut impl_methods = functions::resolve_function_set( + interner, + crate_id, + &context.def_maps, + trait_impl.methods.clone(), + Some(self_type.clone()), + Some(impl_id), + generics.clone(), + 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_generics(generics); + new_resolver.set_self_type(Some(self_type.clone())); + + if let Some(trait_id) = maybe_trait_id { + check_methods_signatures( + &mut new_resolver, + &impl_methods, + trait_id, + trait_impl.generics.len(), + errors, + ); + + let where_clause = trait_impl + .where_clause + .into_iter() + .flat_map(|item| new_resolver.resolve_trait_constraint(item)) + .collect(); + + 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, + where_clause, + methods: vecmap(&impl_methods, |(_, func_id)| *func_id), + }); + + if let Err((prev_span, prev_file)) = interner.add_trait_implementation( + self_type.clone(), + trait_id, + impl_id, + resolved_trait_impl, + ) { + let error = DefCollectorErrorKind::OverlappingImpl { + typ: self_type.clone(), + 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); + } + } + + methods +} + +pub(crate) fn take_errors_filter_self_not_resolved( + file_id: FileId, + resolver: Resolver<'_>, +) -> Vec<(CompilationError, FileId)> { + resolver + .take_errors() + .iter() + .filter(|resolution_error| match resolution_error { + ResolverError::PathResolutionError(PathResolutionError::Unresolved(ident)) => { + &ident.0.contents != "Self" + } + _ => true, + }) + .cloned() + .map(|resolution_error| (resolution_error.into(), file_id)) + .collect() +} diff --git a/compiler/noirc_frontend/src/hir/resolution/type_aliases.rs b/compiler/noirc_frontend/src/hir/resolution/type_aliases.rs new file mode 100644 index 00000000000..f66f6c8dfa7 --- /dev/null +++ b/compiler/noirc_frontend/src/hir/resolution/type_aliases.rs @@ -0,0 +1,33 @@ +use super::{path_resolver::StandardPathResolver, resolver::Resolver}; +use crate::{ + graph::CrateId, + hir::{ + def_collector::dc_crate::{CompilationError, UnresolvedTypeAlias}, + def_map::ModuleId, + Context, + }, + node_interner::TypeAliasId, +}; +use fm::FileId; +use std::collections::BTreeMap; + +pub(crate) fn resolve_type_aliases( + context: &mut Context, + type_aliases: BTreeMap, + crate_id: CrateId, +) -> Vec<(CompilationError, FileId)> { + let mut errors: Vec<(CompilationError, FileId)> = vec![]; + for (type_id, unresolved_typ) in type_aliases { + let path_resolver = StandardPathResolver::new(ModuleId { + local_id: unresolved_typ.module_id, + krate: crate_id, + }); + let file = unresolved_typ.file_id; + let (typ, generics, resolver_errors) = + Resolver::new(&mut context.def_interner, &path_resolver, &context.def_maps, file) + .resolve_type_aliases(unresolved_typ.type_alias_def); + errors.extend(resolver_errors.iter().cloned().map(|e| (e.into(), file))); + context.def_interner.set_type_alias(type_id, typ, generics); + } + errors +} diff --git a/compiler/noirc_frontend/src/hir/type_check/errors.rs b/compiler/noirc_frontend/src/hir/type_check/errors.rs index 4a46391f0d4..267dbd6b5be 100644 --- a/compiler/noirc_frontend/src/hir/type_check/errors.rs +++ b/compiler/noirc_frontend/src/hir/type_check/errors.rs @@ -113,6 +113,8 @@ pub enum TypeCheckError { }, #[error("No matching impl found")] NoMatchingImplFound { constraints: Vec<(Type, String)>, span: Span }, + #[error("Constraint for `{typ}: {trait_name}` is not needed, another matching impl is already in scope")] + UnneededTraitConstraint { trait_name: String, typ: Type, span: Span }, } impl TypeCheckError { @@ -269,6 +271,10 @@ impl From for Diagnostic { diagnostic } + TypeCheckError::UnneededTraitConstraint { trait_name, typ, span } => { + let msg = format!("Constraint for `{typ}: {trait_name}` is not needed, another matching impl is already in scope"); + Diagnostic::simple_warning(msg, "Unnecessary trait constraint in where clause".into(), span) + } } } } diff --git a/compiler/noirc_frontend/src/hir/type_check/expr.rs b/compiler/noirc_frontend/src/hir/type_check/expr.rs index c0ff4dff6d5..74f076212fa 100644 --- a/compiler/noirc_frontend/src/hir/type_check/expr.rs +++ b/compiler/noirc_frontend/src/hir/type_check/expr.rs @@ -10,7 +10,7 @@ use crate::{ }, types::Type, }, - node_interner::{DefinitionKind, ExprId, FuncId, TraitMethodId}, + node_interner::{DefinitionKind, ExprId, FuncId, TraitId, TraitMethodId}, BinaryOpKind, Signedness, TypeBinding, TypeVariableKind, UnaryOp, }; @@ -53,6 +53,19 @@ impl<'interner> TypeChecker<'interner> { // variable to handle generic functions. let t = self.interner.id_type_substitute_trait_as_type(ident.id); let (typ, bindings) = t.instantiate(self.interner); + + // Push any trait constraints required by this definition to the context + // to be checked later when the type of this variable is further constrained. + if let Some(definition) = self.interner.try_definition(ident.id) { + if let DefinitionKind::Function(function) = definition.kind { + let function = self.interner.function_meta(&function); + for mut constraint in function.trait_constraints.clone() { + constraint.typ = constraint.typ.substitute(&bindings); + self.trait_constraints.push((constraint, *expr_id)); + } + } + } + self.interner.store_instantiation_bindings(*expr_id, bindings); typ } @@ -132,7 +145,9 @@ impl<'interner> TypeChecker<'interner> { HirExpression::Index(index_expr) => self.check_index_expression(expr_id, index_expr), HirExpression::Call(call_expr) => { self.check_if_deprecated(&call_expr.func); + let function = self.check_expression(&call_expr.func); + let args = vecmap(&call_expr.arguments, |arg| { let typ = self.check_expression(arg); (typ, *arg, self.interner.expr_span(arg)) @@ -160,21 +175,29 @@ impl<'interner> TypeChecker<'interner> { // so that the backend doesn't need to worry about methods let location = method_call.location; - let mut func_id = None; - if let HirMethodReference::FuncId(id) = method_ref { - func_id = Some(id); - - // Automatically add `&mut` if the method expects a mutable reference and - // the object is not already one. - if id != FuncId::dummy_id() { - let func_meta = self.interner.function_meta(&id); - self.try_add_mutable_reference_to_object( - &mut method_call, - &func_meta.typ, - &mut args, - ); + let trait_id = match &method_ref { + HirMethodReference::FuncId(func_id) => { + // Automatically add `&mut` if the method expects a mutable reference and + // the object is not already one. + if *func_id != FuncId::dummy_id() { + let func_meta = self.interner.function_meta(func_id); + self.try_add_mutable_reference_to_object( + &mut method_call, + &func_meta.typ, + &mut args, + ); + } + + let meta = self.interner.function_meta(func_id); + meta.trait_impl.map(|impl_id| { + self.interner + .get_trait_implementation(impl_id) + .borrow() + .trait_id + }) } - } + HirMethodReference::TraitMethodId(method) => Some(method.trait_id), + }; let (function_id, function_call) = method_call.into_function_call( method_ref.clone(), @@ -185,29 +208,8 @@ impl<'interner> TypeChecker<'interner> { let span = self.interner.expr_span(expr_id); let ret = self.check_method_call(&function_id, method_ref, args, span); - if let Some(func_id) = func_id { - let meta = self.interner.function_meta(&func_id); - - if let Some(impl_id) = meta.trait_impl { - let trait_impl = self.interner.get_trait_implementation(impl_id); - - let result = self.interner.lookup_trait_implementation( - &object_type, - trait_impl.borrow().trait_id, - ); - - if let Err(erroring_constraints) = result { - let constraints = vecmap(erroring_constraints, |constraint| { - let r#trait = self.interner.get_trait(constraint.trait_id); - (constraint.typ, r#trait.name.to_string()) - }); - - self.errors.push(TypeCheckError::NoMatchingImplFound { - constraints, - span, - }); - } - } + if let Some(trait_id) = trait_id { + self.verify_trait_constraint(&object_type, trait_id, function_id, span); } self.interner.replace_expr(expr_id, function_call); @@ -285,7 +287,7 @@ impl<'interner> TypeChecker<'interner> { Type::Function(params, Box::new(lambda.return_type), Box::new(env_type)) } - HirExpression::TraitMethodReference(_, method) => { + HirExpression::TraitMethodReference(method) => { let the_trait = self.interner.get_trait(method.trait_id); let method = &the_trait.methods[method.method_index]; @@ -305,6 +307,33 @@ impl<'interner> TypeChecker<'interner> { typ } + pub fn verify_trait_constraint( + &mut self, + object_type: &Type, + trait_id: TraitId, + function_ident_id: ExprId, + span: Span, + ) { + match self.interner.lookup_trait_implementation(object_type, trait_id) { + Ok(impl_kind) => self.interner.select_impl_for_ident(function_ident_id, impl_kind), + Err(erroring_constraints) => { + // Don't show any errors where try_get_trait returns None. + // This can happen if a trait is used that was never declared. + let constraints = erroring_constraints + .into_iter() + .map(|constraint| { + let r#trait = self.interner.try_get_trait(constraint.trait_id)?; + Some((constraint.typ, r#trait.name.to_string())) + }) + .collect::>>(); + + if let Some(constraints) = constraints { + self.errors.push(TypeCheckError::NoMatchingImplFound { constraints, span }); + } + } + } + } + /// Check if the given method type requires a mutable reference to the object type, and check /// if the given object type is already a mutable reference. If not, add one. /// This is used to automatically transform a method call: `foo.bar()` into a function @@ -512,13 +541,11 @@ impl<'interner> TypeChecker<'interner> { let func_meta = self.interner.function_meta(&func_id); let param_len = func_meta.parameters.len(); - (func_meta.typ, param_len) } - HirMethodReference::TraitMethodId(_, method) => { + HirMethodReference::TraitMethodId(method) => { let the_trait = self.interner.get_trait(method.trait_id); let method = &the_trait.methods[method.method_index]; - (method.get_type(), method.arguments.len()) } }; @@ -537,7 +564,6 @@ impl<'interner> TypeChecker<'interner> { self.interner.store_instantiation_bindings(*function_ident_id, instantiation_bindings); self.interner.push_expr_type(function_ident_id, function_type.clone()); - self.bind_function_type(function_type, arguments, span) } @@ -853,7 +879,7 @@ impl<'interner> TypeChecker<'interner> { method_name: &str, expr_id: &ExprId, ) -> Option { - match object_type { + match object_type.follow_bindings() { Type::Struct(typ, _args) => { let id = typ.borrow().id; match self.interner.lookup_method(object_type, id, method_name, false) { @@ -883,16 +909,15 @@ impl<'interner> TypeChecker<'interner> { for constraint in func_meta.trait_constraints { if *object_type == constraint.typ { - let the_trait = self.interner.get_trait(constraint.trait_id); - - for (method_index, method) in the_trait.methods.iter().enumerate() { - if method.name.0.contents == method_name { - let trait_method = - TraitMethodId { trait_id: constraint.trait_id, method_index }; - return Some(HirMethodReference::TraitMethodId( - object_type.clone(), - trait_method, - )); + if let Some(the_trait) = self.interner.try_get_trait(constraint.trait_id) { + for (method_index, method) in the_trait.methods.iter().enumerate() { + if method.name.0.contents == method_name { + let trait_method = TraitMethodId { + trait_id: constraint.trait_id, + method_index, + }; + return Some(HirMethodReference::TraitMethodId(trait_method)); + } } } } @@ -911,12 +936,20 @@ impl<'interner> TypeChecker<'interner> { .interner .lookup_primitive_trait_method_mut(element.as_ref(), method_name) .map(HirMethodReference::FuncId) - .or_else(|| self.lookup_method(element, method_name, expr_id)), + .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, - other => match self.interner.lookup_primitive_method(other, method_name) { + // The type variable must be unbound at this point since follow_bindings was called + Type::TypeVariable(_, TypeVariableKind::Normal) => { + let span = self.interner.expr_span(expr_id); + self.errors.push(TypeCheckError::TypeAnnotationsNeeded { span }); + None + } + + 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 3915132383b..19d9d370377 100644 --- a/compiler/noirc_frontend/src/hir/type_check/mod.rs +++ b/compiler/noirc_frontend/src/hir/type_check/mod.rs @@ -14,7 +14,7 @@ mod stmt; pub use errors::TypeCheckError; use crate::{ - hir_def::{expr::HirExpression, stmt::HirStatement}, + hir_def::{expr::HirExpression, stmt::HirStatement, traits::TraitConstraint}, node_interner::{ExprId, FuncId, NodeInterner, StmtId}, Type, }; @@ -28,6 +28,12 @@ pub struct TypeChecker<'interner> { interner: &'interner mut NodeInterner, errors: Vec, current_function: Option, + + /// Trait constraints are collected during type checking until they are + /// verified at the end of a function. This is because constraints arise + /// on each variable, but it is only until function calls when the types + /// needed for the trait constraint may become known. + trait_constraints: Vec<(TraitConstraint, ExprId)>, } /// Type checks a function and assigns the @@ -43,6 +49,24 @@ pub fn type_check_func(interner: &mut NodeInterner, func_id: FuncId) -> Vec Vec Vec (noirc_e impl<'interner> TypeChecker<'interner> { fn new(interner: &'interner mut NodeInterner) -> Self { - Self { delayed_type_checks: Vec::new(), interner, errors: vec![], current_function: None } + Self { + delayed_type_checks: Vec::new(), + interner, + errors: Vec::new(), + trait_constraints: Vec::new(), + current_function: None, + } } pub fn push_delayed_type_check(&mut self, f: TypeCheckFn) { self.delayed_type_checks.push(f); } - fn check_function_body( - mut self, - body: &ExprId, - ) -> (Type, Vec, Vec) { + fn check_function_body(&mut self, body: &ExprId) -> (Type, Vec) { let body_type = self.check_expression(body); - (body_type, self.delayed_type_checks, self.errors) + (body_type, std::mem::take(&mut self.delayed_type_checks)) } pub fn check_global(id: &StmtId, interner: &'interner mut NodeInterner) -> Vec { let mut this = Self { delayed_type_checks: Vec::new(), interner, - errors: vec![], + errors: Vec::new(), + trait_constraints: Vec::new(), current_function: None, }; this.check_statement(id); diff --git a/compiler/noirc_frontend/src/hir_def/expr.rs b/compiler/noirc_frontend/src/hir_def/expr.rs index 42803378ccb..755817ba1e6 100644 --- a/compiler/noirc_frontend/src/hir_def/expr.rs +++ b/compiler/noirc_frontend/src/hir_def/expr.rs @@ -29,7 +29,7 @@ pub enum HirExpression { If(HirIfExpression), Tuple(Vec), Lambda(HirLambda), - TraitMethodReference(Type, TraitMethodId), + TraitMethodReference(TraitMethodId), Error, } @@ -156,7 +156,7 @@ pub enum HirMethodReference { /// Or a method can come from a Trait impl block, in which case /// the actual function called will depend on the instantiated type, /// which can be only known during monomorphization. - TraitMethodId(Type, TraitMethodId), + TraitMethodId(TraitMethodId), } impl HirMethodCallExpression { @@ -174,8 +174,8 @@ impl HirMethodCallExpression { let id = interner.function_definition_id(func_id); HirExpression::Ident(HirIdent { location, id }) } - HirMethodReference::TraitMethodId(typ, method_id) => { - HirExpression::TraitMethodReference(typ, method_id) + HirMethodReference::TraitMethodId(method_id) => { + HirExpression::TraitMethodReference(method_id) } }; let func = interner.push_expr(expr); diff --git a/compiler/noirc_frontend/src/hir_def/traits.rs b/compiler/noirc_frontend/src/hir_def/traits.rs index 2a762a38fc6..062f505c179 100644 --- a/compiler/noirc_frontend/src/hir_def/traits.rs +++ b/compiler/noirc_frontend/src/hir_def/traits.rs @@ -151,5 +151,6 @@ impl TraitFunction { Box::new(self.return_type.clone()), Box::new(Type::Unit), ) + .generalize() } } diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index e2951f9185e..46818626a16 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -103,6 +103,47 @@ pub enum Type { Error, } +impl Type { + /// Returns the number of field elements required to represent the type once encoded. + pub fn field_count(&self) -> u32 { + match self { + Type::FieldElement | Type::Integer { .. } | Type::Bool => 1, + Type::Array(size, typ) => { + let length = size + .evaluate_to_u64() + .expect("Cannot have variable sized arrays as a parameter to main"); + let typ = typ.as_ref(); + (length as u32) * typ.field_count() + } + Type::Struct(ref def, args) => { + let struct_type = def.borrow(); + let fields = struct_type.get_fields(args); + fields.iter().fold(0, |acc, (_, field_type)| acc + field_type.field_count()) + } + Type::Tuple(fields) => { + fields.iter().fold(0, |acc, field_typ| acc + field_typ.field_count()) + } + Type::String(size) => { + let size = size + .evaluate_to_u64() + .expect("Cannot have variable sized strings as a parameter to main"); + size as u32 + } + Type::FmtString(_, _) + | Type::Unit + | Type::TypeVariable(_, _) + | Type::TraitAsType(_) + | Type::NamedGeneric(_, _) + | Type::Function(_, _, _) + | Type::MutableReference(_) + | Type::Forall(_, _) + | Type::Constant(_) + | Type::NotConstant + | Type::Error => unreachable!("This type cannot exist as a parameter to main"), + } + } +} + /// A list of TypeVariableIds to bind to a type. Storing the /// TypeVariable in addition to the matching TypeVariableId allows /// the binding to later be undone if needed. @@ -575,6 +616,32 @@ impl Type { _ => 0, } } + + /// Takes a monomorphic type and generalizes it over each of the given type variables. + pub(crate) fn generalize_from_variables( + self, + type_vars: HashMap, + ) -> Type { + let polymorphic_type_vars = vecmap(type_vars, |type_var| type_var); + Type::Forall(polymorphic_type_vars, Box::new(self)) + } + + /// Takes a monomorphic type and generalizes it over each of the type variables in the + /// given type bindings, ignoring what each type variable is bound to in the TypeBindings. + pub(crate) fn generalize_from_substitutions(self, type_bindings: TypeBindings) -> Type { + let polymorphic_type_vars = vecmap(type_bindings, |(id, (type_var, _))| (id, type_var)); + Type::Forall(polymorphic_type_vars, Box::new(self)) + } + + /// Takes a monomorphic type and generalizes it over each type variable found within. + /// + /// Note that Noir's type system assumes any Type::Forall are only present at top-level, + /// and thus all type variable's within a type are free. + pub(crate) fn generalize(self) -> Type { + let mut type_variables = HashMap::new(); + self.find_all_unbound_type_variables(&mut type_variables); + self.generalize_from_variables(type_variables) + } } impl std::fmt::Display for Type { @@ -926,8 +993,24 @@ impl Type { } } + (NamedGeneric(binding, _), other) if !binding.borrow().is_unbound() => { + if let TypeBinding::Bound(link) = &*binding.borrow() { + link.try_unify(other) + } else { + unreachable!("If guard ensures binding is bound") + } + } + + (other, NamedGeneric(binding, _)) if !binding.borrow().is_unbound() => { + if let TypeBinding::Bound(link) = &*binding.borrow() { + other.try_unify(link) + } else { + unreachable!("If guard ensures binding is bound") + } + } + (NamedGeneric(binding_a, name_a), NamedGeneric(binding_b, name_b)) => { - // Ensure NamedGenerics are never bound during type checking + // Unbound NamedGenerics are caught by the checks above assert!(binding_a.borrow().is_unbound()); assert!(binding_b.borrow().is_unbound()); @@ -1085,12 +1168,18 @@ impl Type { } /// Replace each NamedGeneric (and TypeVariable) in this type with a fresh type variable - pub(crate) fn instantiate_named_generics( + pub(crate) fn instantiate_type_variables( &self, interner: &NodeInterner, ) -> (Type, TypeBindings) { - let mut substitutions = HashMap::new(); - self.find_all_unbound_type_variables(interner, &mut substitutions); + let mut type_variables = HashMap::new(); + self.find_all_unbound_type_variables(&mut type_variables); + + let substitutions = type_variables + .into_iter() + .map(|(id, type_var)| (id, (type_var, interner.next_type_variable()))) + .collect(); + (self.substitute(&substitutions), substitutions) } @@ -1098,8 +1187,7 @@ impl Type { /// to bind the unbound type variable to a fresh type variable. fn find_all_unbound_type_variables( &self, - interner: &NodeInterner, - bindings: &mut TypeBindings, + type_variables: &mut HashMap, ) { match self { Type::FieldElement @@ -1111,44 +1199,43 @@ impl Type { | Type::NotConstant | Type::Error => (), Type::Array(length, elem) => { - length.find_all_unbound_type_variables(interner, bindings); - elem.find_all_unbound_type_variables(interner, bindings); + length.find_all_unbound_type_variables(type_variables); + elem.find_all_unbound_type_variables(type_variables); } - Type::String(length) => length.find_all_unbound_type_variables(interner, bindings), + Type::String(length) => length.find_all_unbound_type_variables(type_variables), Type::FmtString(length, env) => { - length.find_all_unbound_type_variables(interner, bindings); - env.find_all_unbound_type_variables(interner, bindings); + length.find_all_unbound_type_variables(type_variables); + env.find_all_unbound_type_variables(type_variables); } Type::Struct(_, generics) => { for generic in generics { - generic.find_all_unbound_type_variables(interner, bindings); + generic.find_all_unbound_type_variables(type_variables); } } Type::Tuple(fields) => { for field in fields { - field.find_all_unbound_type_variables(interner, bindings); + field.find_all_unbound_type_variables(type_variables); } } Type::Function(args, ret, env) => { for arg in args { - arg.find_all_unbound_type_variables(interner, bindings); + arg.find_all_unbound_type_variables(type_variables); } - ret.find_all_unbound_type_variables(interner, bindings); - env.find_all_unbound_type_variables(interner, bindings); + ret.find_all_unbound_type_variables(type_variables); + env.find_all_unbound_type_variables(type_variables); } Type::MutableReference(elem) => { - elem.find_all_unbound_type_variables(interner, bindings); + elem.find_all_unbound_type_variables(type_variables); } - Type::Forall(_, typ) => typ.find_all_unbound_type_variables(interner, bindings), + Type::Forall(_, typ) => typ.find_all_unbound_type_variables(type_variables), Type::TypeVariable(type_variable, _) | Type::NamedGeneric(type_variable, _) => { match &*type_variable.borrow() { TypeBinding::Bound(binding) => { - binding.find_all_unbound_type_variables(interner, bindings); + binding.find_all_unbound_type_variables(type_variables); } 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)); + if !type_variables.contains_key(id) { + type_variables.insert(*id, type_variable.clone()); } } } diff --git a/compiler/noirc_frontend/src/lexer/lexer.rs b/compiler/noirc_frontend/src/lexer/lexer.rs index 5f35e60dea7..7a2197ebb93 100644 --- a/compiler/noirc_frontend/src/lexer/lexer.rs +++ b/compiler/noirc_frontend/src/lexer/lexer.rs @@ -16,6 +16,7 @@ pub struct Lexer<'a> { position: Position, done: bool, skip_comments: bool, + skip_whitespaces: bool, } pub type SpannedTokenResult = Result; @@ -37,7 +38,13 @@ impl<'a> Lexer<'a> { } pub fn new(source: &'a str) -> Self { - Lexer { chars: source.char_indices(), position: 0, done: false, skip_comments: true } + Lexer { + chars: source.char_indices(), + position: 0, + done: false, + skip_comments: true, + skip_whitespaces: true, + } } pub fn skip_comments(mut self, flag: bool) -> Self { @@ -45,6 +52,11 @@ impl<'a> Lexer<'a> { self } + pub fn skip_whitespaces(mut self, flag: bool) -> Self { + self.skip_whitespaces = flag; + self + } + /// Iterates the cursor and returns the char at the new cursor position fn next_char(&mut self) -> Option { let (position, ch) = self.chars.next()?; @@ -82,9 +94,13 @@ impl<'a> Lexer<'a> { fn next_token(&mut self) -> SpannedTokenResult { match self.next_char() { - Some(x) if { x.is_whitespace() } => { - self.eat_whitespace(); - self.next_token() + Some(x) if x.is_whitespace() => { + let spanned = self.eat_whitespace(x); + if self.skip_whitespaces { + self.next_token() + } else { + Ok(spanned) + } } Some('<') => self.glue(Token::Less), Some('>') => self.glue(Token::Greater), @@ -110,6 +126,7 @@ impl<'a> Lexer<'a> { Some(']') => self.single_char_token(Token::RightBracket), Some('"') => self.eat_string_literal(), Some('f') => self.eat_format_string_or_alpha_numeric(), + Some('r') => self.eat_raw_string_or_alpha_numeric(), Some('#') => self.eat_attribute(), Some(ch) if ch.is_ascii_alphanumeric() || ch == '_' => self.eat_alpha_numeric(ch), Some(ch) => { @@ -384,6 +401,78 @@ impl<'a> Lexer<'a> { } } + fn eat_raw_string(&mut self) -> SpannedTokenResult { + let start = self.position; + + let beginning_hashes = self.eat_while(None, |ch| ch == '#'); + let beginning_hashes_count = beginning_hashes.chars().count(); + if beginning_hashes_count > 255 { + // too many hashes (unlikely in practice) + // also, Rust disallows 256+ hashes as well + return Err(LexerErrorKind::UnexpectedCharacter { + span: Span::single_char(start + 255), + found: Some('#'), + expected: "\"".to_owned(), + }); + } + + if !self.peek_char_is('"') { + return Err(LexerErrorKind::UnexpectedCharacter { + span: Span::single_char(self.position), + found: self.next_char(), + expected: "\"".to_owned(), + }); + } + self.next_char(); + + let mut str_literal = String::new(); + loop { + let chars = self.eat_while(None, |ch| ch != '"'); + str_literal.push_str(&chars[..]); + if !self.peek_char_is('"') { + return Err(LexerErrorKind::UnexpectedCharacter { + span: Span::single_char(self.position), + found: self.next_char(), + expected: "\"".to_owned(), + }); + } + self.next_char(); + let mut ending_hashes_count = 0; + while let Some('#') = self.peek_char() { + if ending_hashes_count == beginning_hashes_count { + break; + } + self.next_char(); + ending_hashes_count += 1; + } + if ending_hashes_count == beginning_hashes_count { + break; + } else { + str_literal.push('"'); + for _ in 0..ending_hashes_count { + str_literal.push('#'); + } + } + } + + let str_literal_token = Token::RawStr(str_literal, beginning_hashes_count as u8); + + let end = self.position; + Ok(str_literal_token.into_span(start, end)) + } + + fn eat_raw_string_or_alpha_numeric(&mut self) -> SpannedTokenResult { + // Problem: we commit to eating raw strings once we see one or two characters. + // This is unclean, but likely ok in all practical cases, and works with existing + // `Lexer` methods. + let peek1 = self.peek_char().unwrap_or('X'); + let peek2 = self.peek2_char().unwrap_or('X'); + match (peek1, peek2) { + ('#', '#') | ('#', '"') | ('"', _) => self.eat_raw_string(), + _ => self.eat_alpha_numeric('r'), + } + } + fn parse_comment(&mut self, start: u32) -> SpannedTokenResult { let doc_style = match self.peek_char() { Some('!') => { @@ -454,8 +543,10 @@ impl<'a> Lexer<'a> { } /// Skips white space. They are not significant in the source language - fn eat_whitespace(&mut self) { - self.eat_while(None, |ch| ch.is_whitespace()); + fn eat_whitespace(&mut self, initial_char: char) -> SpannedToken { + let start = self.position; + let whitespace = self.eat_while(initial_char.into(), |ch| ch.is_whitespace()); + SpannedToken::new(Token::Whitespace(whitespace), Span::inclusive(start, self.position)) } } diff --git a/compiler/noirc_frontend/src/lexer/token.rs b/compiler/noirc_frontend/src/lexer/token.rs index 28e17b2b88d..b16de42c0ba 100644 --- a/compiler/noirc_frontend/src/lexer/token.rs +++ b/compiler/noirc_frontend/src/lexer/token.rs @@ -15,6 +15,7 @@ pub enum Token { Int(FieldElement), Bool(bool), Str(String), + RawStr(String, u8), FmtStr(String), Keyword(Keyword), IntType(IntType), @@ -88,6 +89,8 @@ pub enum Token { #[allow(clippy::upper_case_acronyms)] EOF, + Whitespace(String), + /// An invalid character is one that is not in noir's language or grammar. /// /// We don't report invalid tokens in the source as errors until parsing to @@ -155,6 +158,10 @@ impl fmt::Display for Token { Token::Bool(b) => write!(f, "{b}"), Token::Str(ref b) => write!(f, "{b}"), Token::FmtStr(ref b) => write!(f, "f{b}"), + Token::RawStr(ref b, hashes) => { + let h: String = std::iter::once('#').cycle().take(hashes as usize).collect(); + write!(f, "r{h}\"{b}\"{h}") + } Token::Keyword(k) => write!(f, "{k}"), Token::Attribute(ref a) => write!(f, "{a}"), Token::LineComment(ref s, _style) => write!(f, "//{s}"), @@ -194,6 +201,7 @@ impl fmt::Display for Token { Token::Bang => write!(f, "!"), Token::EOF => write!(f, "end of input"), Token::Invalid(c) => write!(f, "{c}"), + Token::Whitespace(ref s) => write!(f, "{s}"), } } } @@ -224,7 +232,11 @@ impl Token { pub fn kind(&self) -> TokenKind { match *self { Token::Ident(_) => TokenKind::Ident, - Token::Int(_) | Token::Bool(_) | Token::Str(_) | Token::FmtStr(_) => TokenKind::Literal, + Token::Int(_) + | Token::Bool(_) + | Token::Str(_) + | Token::RawStr(..) + | Token::FmtStr(_) => TokenKind::Literal, Token::Keyword(_) => TokenKind::Keyword, Token::Attribute(_) => TokenKind::Attribute, ref tok => TokenKind::Token(tok.clone()), diff --git a/compiler/noirc_frontend/src/lib.rs b/compiler/noirc_frontend/src/lib.rs index 74057240de1..77107d3e7db 100644 --- a/compiler/noirc_frontend/src/lib.rs +++ b/compiler/noirc_frontend/src/lib.rs @@ -34,3 +34,46 @@ pub use hir_def::types::*; // Unit tests that involve all modules pub mod tests; + +// API for experimental macros feature +pub mod macros_api { + + pub use acvm::FieldElement; + pub use fm::FileId; + pub use noirc_errors::Span; + + pub use crate::graph::CrateId; + pub use crate::hir::def_collector::errors::MacroError; + pub use crate::hir_def::expr::{HirExpression, HirLiteral}; + pub use crate::hir_def::stmt::HirStatement; + pub use crate::node_interner::{NodeInterner, StructId}; + pub use crate::parser::SortedModule; + pub use crate::token::SecondaryAttribute; + + pub use crate::hir::def_map::ModuleDefId; + pub use crate::{ + hir::Context as HirContext, BlockExpression, CallExpression, CastExpression, Distinctness, + Expression, ExpressionKind, FunctionReturnType, Ident, IndexExpression, LetStatement, + Literal, MemberAccessExpression, MethodCallExpression, NoirFunction, Path, PathKind, + Pattern, Statement, UnresolvedType, UnresolvedTypeData, Visibility, + }; + pub use crate::{ + ForLoopStatement, ForRange, FunctionDefinition, FunctionVisibility, ImportStatement, + NoirStruct, Param, PrefixExpression, Signedness, StatementKind, StructType, Type, TypeImpl, + UnaryOp, + }; + + /// Methods to process the AST before and after type checking + pub trait MacroProcessor { + /// Function to manipulate the AST before type checking has been completed. + fn process_untyped_ast( + &self, + ast: SortedModule, + crate_id: &CrateId, + context: &HirContext, + ) -> Result; + /// Function to manipulate the AST after type checking has been completed. + /// The AST after type checking has been done is called the HIR. + fn process_typed_ast(&self, crate_id: &CrateId, context: &mut HirContext); + } +} diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index 44d734cd5f9..57e4e6cdeb0 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, TraitMethodId}, + node_interner::{self, DefinitionKind, NodeInterner, StmtId, TraitImplKind, TraitMethodId}, token::FunctionAttribute, ContractFunctionType, FunctionKind, Type, TypeBinding, TypeBindings, TypeVariableKind, Visibility, @@ -378,9 +378,9 @@ impl<'interner> Monomorphizer<'interner> { HirExpression::Lambda(lambda) => self.lambda(lambda, expr), - HirExpression::TraitMethodReference(typ, method) => { + HirExpression::TraitMethodReference(method) => { if let Type::Function(_, _, _) = self.interner.id_type(expr) { - self.resolve_trait_method_reference(typ, expr, method) + self.resolve_trait_method_reference(expr, method) } else { unreachable!( "Calling a non-function, this should've been caught in typechecking" @@ -812,7 +812,6 @@ impl<'interner> Monomorphizer<'interner> { fn resolve_trait_method_reference( &mut self, - self_type: HirType, expr_id: node_interner::ExprId, method: TraitMethodId, ) -> ast::Expression { @@ -820,10 +819,36 @@ impl<'interner> Monomorphizer<'interner> { let trait_impl = self .interner - .lookup_trait_implementation(&self_type, method.trait_id) + .get_selected_impl_for_ident(expr_id) .expect("ICE: missing trait impl - should be caught during type checking"); - let hir_func_id = trait_impl.borrow().methods[method.method_index]; + let hir_func_id = match trait_impl { + node_interner::TraitImplKind::Normal(impl_id) => { + self.interner.get_trait_implementation(impl_id).borrow().methods + [method.method_index] + } + node_interner::TraitImplKind::Assumed { object_type } => { + match self.interner.lookup_trait_implementation(&object_type, method.trait_id) { + Ok(TraitImplKind::Normal(impl_id)) => { + self.interner.get_trait_implementation(impl_id).borrow().methods + [method.method_index] + } + Ok(TraitImplKind::Assumed { .. }) => unreachable!( + "There should be no remaining Assumed impls during monomorphization" + ), + Err(constraints) => { + let failed_constraints = vecmap(constraints, |constraint| { + let id = constraint.trait_id; + let name = self.interner.get_trait(id).name.to_string(); + format!(" {}: {name}", constraint.typ) + }) + .join("\n"); + + unreachable!("Failed to find trait impl during monomorphization. The failed constraint(s) are:\n{failed_constraints}") + } + } + } + }; let func_def = self.lookup_function(hir_func_id, expr_id, &function_type); let func_id = match func_def { diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index d03ed5528d9..e66a6d57605 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -9,7 +9,6 @@ use crate::ast::Ident; use crate::graph::CrateId; use crate::hir::def_collector::dc_crate::{UnresolvedStruct, UnresolvedTrait, UnresolvedTypeAlias}; use crate::hir::def_map::{LocalModuleId, ModuleId}; -use crate::hir::StorageSlot; use crate::hir_def::stmt::HirLetStatement; use crate::hir_def::traits::TraitImpl; use crate::hir_def::traits::{Trait, TraitConstraint}; @@ -101,7 +100,13 @@ pub struct NodeInterner { /// 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>, + trait_implementation_map: HashMap>, + + /// When impls are found during type checking, we tag the function call's Ident + /// with the impl that was selected. For cases with where clauses, this may be + /// an Assumed (but verified) impl. In this case the monomorphizer should have + /// the context to get the concrete type of the object and select the correct impl itself. + selected_trait_implementations: 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 @@ -131,6 +136,18 @@ pub struct NodeInterner { func_id_to_trait: HashMap, } +/// A trait implementation is either a normal implementation that is present in the source +/// program via an `impl` block, or it is assumed to exist from a `where` clause or similar. +#[derive(Debug, Clone)] +pub enum TraitImplKind { + Normal(TraitImplId), + + /// Assumed impls don't have an impl id since they don't link back to any concrete part of the source code. + Assumed { + object_type: Type, + }, +} + /// 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 @@ -381,10 +398,6 @@ impl DefinitionKind { pub struct GlobalInfo { pub ident: Ident, pub local_id: LocalModuleId, - - /// Global definitions have an associated storage slot if they are defined within - /// a contract. If they're defined elsewhere, this value is None. - pub storage_slot: Option, } impl Default for NodeInterner { @@ -405,6 +418,7 @@ impl Default for NodeInterner { traits: HashMap::new(), trait_implementations: Vec::new(), trait_implementation_map: HashMap::new(), + selected_trait_implementations: HashMap::new(), instantiation_bindings: HashMap::new(), field_indices: HashMap::new(), next_type_variable_id: std::cell::Cell::new(0), @@ -559,14 +573,8 @@ impl NodeInterner { self.id_to_type.insert(definition_id.into(), typ); } - pub fn push_global( - &mut self, - stmt_id: StmtId, - ident: Ident, - local_id: LocalModuleId, - storage_slot: Option, - ) { - self.globals.insert(stmt_id, GlobalInfo { ident, local_id, storage_slot }); + pub fn push_global(&mut self, stmt_id: StmtId, ident: Ident, local_id: LocalModuleId) { + self.globals.insert(stmt_id, GlobalInfo { ident, local_id }); } /// Intern an empty global stmt. Used for collecting globals @@ -826,6 +834,10 @@ impl NodeInterner { self.traits[&id].clone() } + pub fn try_get_trait(&self, id: TraitId) -> Option { + self.traits.get(&id).cloned() + } + pub fn get_type_alias(&self, id: TypeAliasId) -> &TypeAliasType { &self.type_aliases[id.0] } @@ -960,7 +972,7 @@ impl NodeInterner { &self, object_type: &Type, trait_id: TraitId, - ) -> Result, Vec> { + ) -> Result> { self.lookup_trait_implementation_helper(object_type, trait_id, IMPL_SEARCH_RECURSION_LIMIT) } @@ -969,7 +981,7 @@ impl NodeInterner { object_type: &Type, trait_id: TraitId, recursion_limit: u32, - ) -> Result, Vec> { + ) -> Result> { let make_constraint = || TraitConstraint::new(object_type.clone(), trait_id); // Prevent infinite recursion when looking for impls @@ -980,23 +992,25 @@ impl NodeInterner { let impls = self.trait_implementation_map.get(&trait_id).ok_or_else(|| vec![make_constraint()])?; - for (existing_object_type, impl_id) in impls { - let (existing_object_type, type_bindings) = - existing_object_type.instantiate_named_generics(self); + for (existing_object_type, impl_kind) in impls { + let (existing_object_type, type_bindings) = existing_object_type.instantiate(self); if object_type.try_unify(&existing_object_type).is_ok() { - let trait_impl = self.get_trait_implementation(*impl_id); - - if let Err(mut errors) = self.validate_where_clause( - &trait_impl.borrow().where_clause, - &type_bindings, - recursion_limit, - ) { - errors.push(make_constraint()); - return Err(errors); + if let TraitImplKind::Normal(impl_id) = impl_kind { + let trait_impl = self.get_trait_implementation(*impl_id); + let trait_impl = trait_impl.borrow(); + + if let Err(mut errors) = self.validate_where_clause( + &trait_impl.where_clause, + &type_bindings, + recursion_limit, + ) { + errors.push(make_constraint()); + return Err(errors); + } } - return Ok(trait_impl); + return Ok(impl_kind.clone()); } } @@ -1022,6 +1036,30 @@ impl NodeInterner { Ok(()) } + /// Adds an "assumed" trait implementation to the currently known trait implementations. + /// Unlike normal trait implementations, these are only assumed to exist. They often correspond + /// to `where` clauses in functions where we assume there is some `T: Eq` even though we do + /// not yet know T. For these cases, we store an impl here so that we assume they exist and + /// can resolve them. They are then later verified when the function is called, and linked + /// properly after being monomorphized to the correct variant. + /// + /// Returns true on success, or false if there is already an overlapping impl in scope. + pub fn add_assumed_trait_implementation( + &mut self, + object_type: Type, + trait_id: TraitId, + ) -> bool { + // Make sure there are no overlapping impls + if self.lookup_trait_implementation(&object_type, trait_id).is_ok() { + return false; + } + + let entries = self.trait_implementation_map.entry(trait_id).or_default(); + entries.push((object_type.clone(), TraitImplKind::Assumed { object_type })); + true + } + + /// Adds a trait implementation to the list of known implementations. pub fn add_trait_implementation( &mut self, object_type: Type, @@ -1033,10 +1071,17 @@ impl NodeInterner { self.trait_implementations.push(trait_impl.clone()); - let (instantiated_object_type, _) = object_type.instantiate_named_generics(self); - if let Ok(existing_impl) = + // Ignoring overlapping TraitImplKind::Assumed impls here is perfectly fine. + // It should never happen since impls are defined at global scope, but even + // if they were, we should never prevent defining a new impl because a where + // clause already assumes it exists. + let (instantiated_object_type, substitutions) = + object_type.instantiate_type_variables(self); + + if let Ok(TraitImplKind::Normal(existing)) = self.lookup_trait_implementation(&instantiated_object_type, trait_id) { + let existing_impl = self.get_trait_implementation(existing); let existing_impl = existing_impl.borrow(); return Err((existing_impl.ident.span(), existing_impl.file)); } @@ -1046,8 +1091,11 @@ impl NodeInterner { self.add_method(&object_type, method_name, *method, true); } + // The object type is generalized so that a generic impl will apply + // to any type T, rather than just the generic type named T. + let generalized_object_type = object_type.generalize_from_substitutions(substitutions); let entries = self.trait_implementation_map.entry(trait_id).or_default(); - entries.push((object_type, impl_id)); + entries.push((generalized_object_type, TraitImplKind::Normal(impl_id))); Ok(()) } @@ -1119,9 +1167,29 @@ impl NodeInterner { /// Returns what the next trait impl id is expected to be. /// Note that this does not actually reserve the slot so care should /// be taken that the next trait impl added matches this ID. - pub(crate) fn next_trait_impl_id(&self) -> TraitImplId { + pub fn next_trait_impl_id(&self) -> TraitImplId { TraitImplId(self.trait_implementations.len()) } + + /// Removes all TraitImplKind::Assumed from the list of known impls for the given trait + pub fn remove_assumed_trait_implementations_for_trait(&mut self, trait_id: TraitId) { + let entries = self.trait_implementation_map.entry(trait_id).or_default(); + entries.retain(|(_, kind)| matches!(kind, TraitImplKind::Normal(_))); + } + + /// Tags the given identifier with the selected trait_impl so that monomorphization + /// can later recover which impl was selected, or alternatively see if it needs to + /// decide which impl to select (because the impl was Assumed). + pub fn select_impl_for_ident(&mut self, ident_id: ExprId, trait_impl: TraitImplKind) { + self.selected_trait_implementations.insert(ident_id, trait_impl); + } + + /// Tags the given identifier with the selected trait_impl so that monomorphization + /// can later recover which impl was selected, or alternatively see if it needs to + /// decide which (because the impl was Assumed). + pub fn get_selected_impl_for_ident(&self, ident_id: ExprId) -> Option { + self.selected_trait_implementations.get(&ident_id).cloned() + } } impl Methods { diff --git a/compiler/noirc_frontend/src/parser/errors.rs b/compiler/noirc_frontend/src/parser/errors.rs index b9b11d8c99e..09dc6dfff8d 100644 --- a/compiler/noirc_frontend/src/parser/errors.rs +++ b/compiler/noirc_frontend/src/parser/errors.rs @@ -30,8 +30,6 @@ pub enum ParserErrorReason { ComptimeDeprecated, #[error("{0} are experimental and aren't fully supported yet")] ExperimentalFeature(&'static str), - #[error("Where clauses are allowed only on functions with generic parameters")] - WhereClauseOnNonGenericFunction, #[error( "Multiple primary attributes found. Only one function attribute is allowed per function" )] @@ -99,6 +97,10 @@ impl ParserError { pub fn reason(&self) -> Option<&ParserErrorReason> { self.reason.as_ref() } + + pub fn is_warning(&self) -> bool { + matches!(self.reason(), Some(ParserErrorReason::ExperimentalFeature(_))) + } } impl std::fmt::Display for ParserError { diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 6d934421783..4c0500a0599 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -40,8 +40,8 @@ use crate::{ BinaryOp, BinaryOpKind, BlockExpression, ConstrainKind, ConstrainStatement, Distinctness, ForLoopStatement, ForRange, 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, + NoirTraitImpl, NoirTypeAlias, Param, Path, PathKind, Pattern, Recoverable, Statement, + TraitBound, TraitImplItem, TraitItem, TypeImpl, UnaryOp, UnresolvedTraitConstraint, UnresolvedTypeExpression, UseTree, UseTreeKind, Visibility, }; @@ -177,12 +177,11 @@ fn function_definition(allow_self: bool) -> impl NoirParser { let ((((attributes, modifiers), name), generics), parameters) = args; // Validate collected attributes, filtering them into function and secondary variants - let attrs = validate_attributes(attributes, span, emit); - validate_where_clause(&generics, &where_clause, span, emit); + let attributes = validate_attributes(attributes, span, emit); FunctionDefinition { span: body_span, name, - attributes: attrs, + attributes, is_unconstrained: modifiers.0, is_open: modifiers.2, is_internal: modifiers.3, @@ -343,9 +342,7 @@ fn lambda_parameters() -> impl NoirParser> { .labelled(ParsingRuleLabel::Parameter) } -fn function_parameters<'a>( - allow_self: bool, -) -> impl NoirParser> + 'a { +fn function_parameters<'a>(allow_self: bool) -> impl NoirParser> + 'a { let typ = parse_type().recover_via(parameter_recovery()); let full_parameter = pattern() @@ -353,7 +350,12 @@ fn function_parameters<'a>( .then_ignore(just(Token::Colon)) .then(optional_visibility()) .then(typ) - .map(|((name, visibility), typ)| (name, typ, visibility)); + .map_with_span(|((pattern, visibility), typ), span| Param { + visibility, + pattern, + typ, + span, + }); let self_parameter = if allow_self { self_parameter().boxed() } else { nothing().boxed() }; @@ -370,7 +372,7 @@ fn nothing() -> impl NoirParser { one_of([]).map(|_| unreachable!()) } -fn self_parameter() -> impl NoirParser<(Pattern, UnresolvedType, Visibility)> { +fn self_parameter() -> impl NoirParser { let mut_ref_pattern = just(Token::Ampersand).then_ignore(keyword(Keyword::Mut)); let mut_pattern = keyword(Keyword::Mut); @@ -399,7 +401,7 @@ fn self_parameter() -> impl NoirParser<(Pattern, UnresolvedType, Visibility)> { _ => (), } - (pattern, self_type, Visibility::Private) + Param { pattern, typ: self_type, visibility: Visibility::Private, span } }) } @@ -412,7 +414,6 @@ fn trait_definition() -> impl NoirParser { .then(trait_body()) .then_ignore(just(Token::RightBrace)) .validate(|(((name, generics), where_clause), items), span, emit| { - validate_where_clause(&generics, &where_clause, span, emit); emit(ParserError::with_reason(ParserErrorReason::ExperimentalFeature("Traits"), span)); TopLevelStatement::Trait(NoirTrait { name, generics, where_clause, span, items }) }) @@ -451,12 +452,9 @@ fn trait_function_declaration() -> impl NoirParser { .then(function_return_type().map(|(_, typ)| typ)) .then(where_clause()) .then(trait_function_body_or_semicolon) - .validate( - |(((((name, generics), parameters), return_type), where_clause), body), span, emit| { - validate_where_clause(&generics, &where_clause, span, emit); - TraitItem::Function { name, generics, parameters, return_type, where_clause, body } - }, - ) + .map(|(((((name, generics), parameters), return_type), where_clause), body)| { + TraitItem::Function { name, generics, parameters, return_type, where_clause, body } + }) } fn validate_attributes( @@ -514,17 +512,6 @@ fn validate_struct_attributes( struct_attributes } -fn validate_where_clause( - generics: &Vec, - where_clause: &Vec, - span: Span, - emit: &mut dyn FnMut(ParserError), -) { - if !where_clause.is_empty() && generics.is_empty() { - emit(ParserError::with_reason(ParserErrorReason::WhereClauseOnNonGenericFunction, span)); - } -} - /// Function declaration parameters differ from other parameters in that parameter /// patterns are not allowed in declarations. All parameters must be identifiers. fn function_declaration_parameters() -> impl NoirParser> { @@ -533,8 +520,8 @@ fn function_declaration_parameters() -> impl NoirParser (ident, param.1), + match param.pattern { + Pattern::Identifier(ident) => (ident, param.typ), other => { emit(ParserError::with_reason( ParserErrorReason::PatternInTraitFunctionParameter, @@ -542,7 +529,7 @@ fn function_declaration_parameters() -> impl NoirParser impl NoirParser { fn path() -> impl NoirParser { let idents = || ident().separated_by(just(Token::DoubleColon)).at_least(1); - let make_path = |kind| move |segments| Path { segments, kind }; + let make_path = |kind| move |segments, span| Path { segments, kind, span }; let prefix = |key| keyword(key).ignore_then(just(Token::DoubleColon)); - let path_kind = |key, kind| prefix(key).ignore_then(idents()).map(make_path(kind)); + let path_kind = |key, kind| prefix(key).ignore_then(idents()).map_with_span(make_path(kind)); choice(( path_kind(Keyword::Crate, PathKind::Crate), path_kind(Keyword::Dep, PathKind::Dep), - idents().map(make_path(PathKind::Plain)), + idents().map_with_span(make_path(PathKind::Plain)), )) } fn empty_path() -> impl NoirParser { - let make_path = |kind| move |_| Path { segments: Vec::new(), kind }; - let path_kind = |key, kind| keyword(key).map(make_path(kind)); + let make_path = |kind| move |_, span| Path { segments: Vec::new(), kind, span }; + let path_kind = |key, kind| keyword(key).map_with_span(make_path(kind)); choice((path_kind(Keyword::Crate, PathKind::Crate), path_kind(Keyword::Dep, PathKind::Dep))) } @@ -1028,13 +1015,24 @@ fn parse_type_inner( named_type(recursive_type_parser.clone()), named_trait(recursive_type_parser.clone()), array_type(recursive_type_parser.clone()), - recursive_type_parser.clone().delimited_by(just(Token::LeftParen), just(Token::RightParen)), + parenthesized_type(recursive_type_parser.clone()), tuple_type(recursive_type_parser.clone()), function_type(recursive_type_parser.clone()), mutable_reference_type(recursive_type_parser), )) } +fn parenthesized_type( + recursive_type_parser: impl NoirParser, +) -> impl NoirParser { + recursive_type_parser + .delimited_by(just(Token::LeftParen), just(Token::RightParen)) + .map_with_span(|typ, span| UnresolvedType { + typ: UnresolvedTypeData::Parenthesized(Box::new(typ)), + span: span.into(), + }) +} + fn optional_visibility() -> impl NoirParser { keyword(Keyword::Pub).or_not().map(|opt| match opt { Some(_) => Visibility::Public, @@ -1190,7 +1188,9 @@ where .ignore_then(type_parser.clone()) .then_ignore(just(Token::RightBracket)) .or_not() - .map_with_span(|t, span| t.unwrap_or_else(|| UnresolvedTypeData::Unit.with_span(span))); + .map_with_span(|t, span| { + t.unwrap_or_else(|| UnresolvedTypeData::Unit.with_span(Span::empty(span.end()))) + }); keyword(Keyword::Fn) .ignore_then(env) @@ -1670,6 +1670,7 @@ fn literal() -> impl NoirParser { Token::Int(x) => ExpressionKind::integer(x), Token::Bool(b) => ExpressionKind::boolean(b), Token::Str(s) => ExpressionKind::string(s), + Token::RawStr(s, hashes) => ExpressionKind::raw_string(s, hashes), Token::FmtStr(s) => ExpressionKind::format_string(s), unexpected => unreachable!("Non-literal {} parsed as a literal", unexpected), }) @@ -1758,6 +1759,51 @@ mod test { .collect() } + #[derive(Copy, Clone)] + struct Case { + source: &'static str, + errors: usize, + expect: &'static str, + } + + fn check_cases_with_errors(cases: &[Case], parser: P) + where + P: NoirParser + Clone, + T: std::fmt::Display, + { + let show_errors = |v| vecmap(&v, ToString::to_string).join("\n"); + + let results = vecmap(cases, |&case| { + let (opt, errors) = parse_recover(parser.clone(), case.source); + let actual = opt.map(|ast| ast.to_string()); + let actual = if let Some(s) = &actual { s.to_string() } else { "(none)".to_string() }; + + let result = ((errors.len(), actual.clone()), (case.errors, case.expect.to_string())); + if result.0 != result.1 { + let num_errors = errors.len(); + let shown_errors = show_errors(errors); + eprintln!( + concat!( + "\nExpected {expected_errors} error(s) and got {num_errors}:", + "\n\n{shown_errors}", + "\n\nFrom input: {src}", + "\nExpected AST: {expected_result}", + "\nActual AST: {actual}\n", + ), + expected_errors = case.errors, + num_errors = num_errors, + shown_errors = shown_errors, + src = case.source, + expected_result = case.expect, + actual = actual, + ); + } + result + }); + + assert_eq!(vecmap(&results, |t| t.0.clone()), vecmap(&results, |t| t.1.clone()),); + } + #[test] fn regression_skip_comment() { parse_all( @@ -2133,7 +2179,6 @@ mod test { "fn func_name(f: Field, y : pub Field, z : pub [u8;5],) where T: {}", "fn func_name(f: Field, y : pub Field, z : pub [u8;5],) where SomeTrait {}", "fn func_name(f: Field, y : pub Field, z : pub [u8;5],) SomeTrait {}", - "fn func_name(f: Field, y : pub Field, z : pub [u8;5],) where T: SomeTrait {}", // A leading plus is not allowed. "fn func_name(f: Field, y : T) where T: + SomeTrait {}", "fn func_name(f: Field, y : T) where T: TraitX + {}", @@ -2164,11 +2209,7 @@ mod test { parse_all_failing( trait_definition(), - vec![ - "trait MissingBody", - "trait WrongDelimiter { fn foo() -> u8, fn bar() -> u8 }", - "trait WhereClauseWithoutGenerics where A: SomeTrait { }", - ], + vec!["trait MissingBody", "trait WrongDelimiter { fn foo() -> u8, fn bar() -> u8 }"], ); } @@ -2409,116 +2450,192 @@ mod test { #[test] fn statement_recovery() { let cases = vec![ - ("let a = 4 + 3", 0, "let a: unspecified = (4 + 3)"), - ("let a: = 4 + 3", 1, "let a: error = (4 + 3)"), - ("let = 4 + 3", 1, "let $error: unspecified = (4 + 3)"), - ("let = ", 2, "let $error: unspecified = Error"), - ("let", 3, "let $error: unspecified = Error"), - ("foo = one two three", 1, "foo = plain::one"), - ("constrain", 2, "constrain Error"), - ("assert", 1, "constrain Error"), - ("constrain x ==", 2, "constrain (plain::x == Error)"), - ("assert(x ==)", 1, "constrain (plain::x == Error)"), - ("assert(x == x, x)", 1, "constrain (plain::x == plain::x)"), - ("assert_eq(x,)", 1, "constrain (Error == Error)"), - ("assert_eq(x, x, x, x)", 1, "constrain (Error == Error)"), - ("assert_eq(x, x, x)", 1, "constrain (plain::x == plain::x)"), + Case { source: "let a = 4 + 3", expect: "let a: unspecified = (4 + 3)", errors: 0 }, + Case { source: "let a: = 4 + 3", expect: "let a: error = (4 + 3)", errors: 1 }, + Case { source: "let = 4 + 3", expect: "let $error: unspecified = (4 + 3)", errors: 1 }, + Case { source: "let = ", expect: "let $error: unspecified = Error", errors: 2 }, + Case { source: "let", expect: "let $error: unspecified = Error", errors: 3 }, + Case { source: "foo = one two three", expect: "foo = plain::one", errors: 1 }, + Case { source: "constrain", expect: "constrain Error", errors: 2 }, + Case { source: "assert", expect: "constrain Error", errors: 1 }, + Case { source: "constrain x ==", expect: "constrain (plain::x == Error)", errors: 2 }, + Case { source: "assert(x ==)", expect: "constrain (plain::x == Error)", errors: 1 }, + Case { + source: "assert(x == x, x)", + expect: "constrain (plain::x == plain::x)", + errors: 1, + }, + Case { source: "assert_eq(x,)", expect: "constrain (Error == Error)", errors: 1 }, + Case { + source: "assert_eq(x, x, x, x)", + expect: "constrain (Error == Error)", + errors: 1, + }, + Case { + source: "assert_eq(x, x, x)", + expect: "constrain (plain::x == plain::x)", + errors: 1, + }, ]; - let show_errors = |v| vecmap(v, ToString::to_string).join("\n"); - - for (src, expected_errors, expected_result) in cases { - let (opt, errors) = parse_recover(fresh_statement(), src); - let actual = opt.map(|ast| ast.to_string()); - let actual = if let Some(s) = &actual { s } else { "(none)" }; - - assert_eq!((errors.len(), actual), (expected_errors, expected_result), - "\nExpected {} error(s) and got {}:\n\n{}\n\nFrom input: {}\nExpected AST: {}\nActual AST: {}\n", - expected_errors, errors.len(), show_errors(&errors), src, expected_result, actual - ); - } + check_cases_with_errors(&cases[..], fresh_statement()); } #[test] fn return_validation() { let cases = vec![ - ("{ return 42; }", 1, "{\n Error\n}"), - ("{ return 1; return 2; }", 2, "{\n Error\n Error\n}"), - ( - "{ return 123; let foo = 4 + 3; }", - 1, - "{\n Error\n let foo: unspecified = (4 + 3)\n}", - ), - ("{ return 1 + 2 }", 2, "{\n Error\n}"), - ("{ return; }", 1, "{\n Error\n}"), + Case { + source: "{ return 42; }", + expect: concat!("{\n", " Error\n", "}",), + errors: 1, + }, + Case { + source: "{ return 1; return 2; }", + expect: concat!("{\n", " Error\n", " Error\n", "}"), + errors: 2, + }, + Case { + source: "{ return 123; let foo = 4 + 3; }", + expect: concat!("{\n", " Error\n", " let foo: unspecified = (4 + 3)\n", "}"), + errors: 1, + }, + Case { + source: "{ return 1 + 2 }", + expect: concat!("{\n", " Error\n", "}",), + errors: 2, + }, + Case { source: "{ return; }", expect: concat!("{\n", " Error\n", "}",), errors: 1 }, ]; - let show_errors = |v| vecmap(&v, ToString::to_string).join("\n"); - - let results = vecmap(&cases, |&(src, expected_errors, expected_result)| { - let (opt, errors) = parse_recover(block(fresh_statement()), src); - let actual = opt.map(|ast| ast.to_string()); - let actual = if let Some(s) = &actual { s.to_string() } else { "(none)".to_string() }; - - let result = - ((errors.len(), actual.clone()), (expected_errors, expected_result.to_string())); - if result.0 != result.1 { - let num_errors = errors.len(); - let shown_errors = show_errors(errors); - eprintln!( - "\nExpected {expected_errors} error(s) and got {num_errors}:\n\n{shown_errors}\n\nFrom input: {src}\nExpected AST: {expected_result}\nActual AST: {actual}\n"); - } - result - }); - - assert_eq!(vecmap(&results, |t| t.0.clone()), vecmap(&results, |t| t.1.clone()),); + check_cases_with_errors(&cases[..], block(fresh_statement())); } #[test] fn expr_no_constructors() { let cases = vec![ - ( - "{ if structure { a: 1 } {} }", - 1, - "{\n if plain::structure {\n Error\n }\n {\n }\n}", - ), - ( - "{ if ( structure { a: 1 } ) {} }", - 0, - "{\n if ((plain::structure { a: 1 })) {\n }\n}", - ), - ("{ if ( structure {} ) {} }", 0, "{\n if ((plain::structure { })) {\n }\n}"), - ( - "{ if (a { x: 1 }, b { y: 2 }) {} }", - 0, - "{\n if ((plain::a { x: 1 }), (plain::b { y: 2 })) {\n }\n}", - ), - ( - "{ if ({ let foo = bar { baz: 42 }; foo == bar { baz: 42 }}) {} }", - 0, - "{\n if ({\n let foo: unspecified = (plain::bar { baz: 42 })\ - \n (plain::foo == (plain::bar { baz: 42 }))\n }) {\n }\n}", - ), + Case { + source: "{ if structure { a: 1 } {} }", + expect: concat!( + "{\n", + " if plain::structure {\n", + " Error\n", + " }\n", + " {\n", + " }\n", + "}", + ), + errors: 1, + }, + Case { + source: "{ if ( structure { a: 1 } ) {} }", + expect: concat!("{\n", " if ((plain::structure { a: 1 })) {\n", " }\n", "}",), + errors: 0, + }, + Case { + source: "{ if ( structure {} ) {} }", + expect: concat!("{\n", " if ((plain::structure { })) {\n", " }\n", "}"), + errors: 0, + }, + Case { + source: "{ if (a { x: 1 }, b { y: 2 }) {} }", + expect: concat!( + "{\n", + " if ((plain::a { x: 1 }), (plain::b { y: 2 })) {\n", + " }\n", + "}", + ), + errors: 0, + }, + Case { + source: "{ if ({ let foo = bar { baz: 42 }; foo == bar { baz: 42 }}) {} }", + expect: concat!( + "{\n", + " if ({\n", + " let foo: unspecified = (plain::bar { baz: 42 })\n", + " (plain::foo == (plain::bar { baz: 42 }))\n", + " }) {\n", + " }\n", + "}", + ), + errors: 0, + }, ]; - let show_errors = |v| vecmap(&v, ToString::to_string).join("\n"); + check_cases_with_errors(&cases[..], block(fresh_statement())); + } - let results = vecmap(&cases, |&(src, expected_errors, expected_result)| { - let (opt, errors) = parse_recover(block(fresh_statement()), src); - let actual = opt.map(|ast| ast.to_string()); - let actual = if let Some(s) = &actual { s.to_string() } else { "(none)".to_string() }; + #[test] + fn parse_raw_string_expr() { + let cases = vec![ + Case { source: r##" r"foo" "##, expect: r##"r"foo""##, errors: 0 }, + Case { source: r##" r#"foo"# "##, expect: r##"r#"foo"#"##, errors: 0 }, + // backslash + Case { source: r##" r"\\" "##, expect: r##"r"\\""##, errors: 0 }, + Case { source: r##" r#"\"# "##, expect: r##"r#"\"#"##, errors: 0 }, + Case { source: r##" r#"\\"# "##, expect: r##"r#"\\"#"##, errors: 0 }, + Case { source: r##" r#"\\\"# "##, expect: r##"r#"\\\"#"##, errors: 0 }, + // escape sequence + Case { + source: r##" r#"\t\n\\t\\n\\\t\\\n\\\\"# "##, + expect: r##"r#"\t\n\\t\\n\\\t\\\n\\\\"#"##, + errors: 0, + }, + Case { source: r##" r#"\\\\\\\\"# "##, expect: r##"r#"\\\\\\\\"#"##, errors: 0 }, + // mismatch - errors: + Case { source: r###" r#"foo"## "###, expect: r###"r#"foo"#"###, errors: 1 }, + Case { source: r###" r##"foo"# "###, expect: "(none)", errors: 2 }, + // mismatch: short: + Case { source: r###" r"foo"# "###, expect: r###"r"foo""###, errors: 1 }, + Case { source: r###" r#"foo" "###, expect: "(none)", errors: 2 }, + // empty string + Case { source: r####"r"""####, expect: r####"r"""####, errors: 0 }, + Case { source: r####"r###""###"####, expect: r####"r###""###"####, errors: 0 }, + // miscellaneous + Case { source: r###" r#\"foo\"# "###, expect: "plain::r", errors: 2 }, + Case { source: r###" r\"foo\" "###, expect: "plain::r", errors: 1 }, + Case { source: r###" r##"foo"# "###, expect: "(none)", errors: 2 }, + // missing 'r' letter + Case { source: r###" ##"foo"# "###, expect: r#""foo""#, errors: 2 }, + Case { source: r###" #"foo" "###, expect: "plain::foo", errors: 2 }, + // whitespace + Case { source: r###" r #"foo"# "###, expect: "plain::r", errors: 2 }, + Case { source: r###" r# "foo"# "###, expect: "plain::r", errors: 3 }, + Case { source: r###" r#"foo" # "###, expect: "(none)", errors: 2 }, + // after identifier + Case { source: r###" bar#"foo"# "###, expect: "plain::bar", errors: 2 }, + // nested + Case { + source: r###"r##"foo r#"bar"# r"baz" ### bye"##"###, + expect: r###"r##"foo r#"bar"# r"baz" ### bye"##"###, + errors: 0, + }, + ]; - let result = - ((errors.len(), actual.clone()), (expected_errors, expected_result.to_string())); - if result.0 != result.1 { - let num_errors = errors.len(); - let shown_errors = show_errors(errors); - eprintln!( - "\nExpected {expected_errors} error(s) and got {num_errors}:\n\n{shown_errors}\n\nFrom input: {src}\nExpected AST: {expected_result}\nActual AST: {actual}\n"); - } - result - }); + check_cases_with_errors(&cases[..], expression()); + } - assert_eq!(vecmap(&results, |t| t.0.clone()), vecmap(&results, |t| t.1.clone()),); + #[test] + fn parse_raw_string_lit() { + let lit_cases = vec![ + Case { source: r##" r"foo" "##, expect: r##"r"foo""##, errors: 0 }, + Case { source: r##" r#"foo"# "##, expect: r##"r#"foo"#"##, errors: 0 }, + // backslash + Case { source: r##" r"\\" "##, expect: r##"r"\\""##, errors: 0 }, + Case { source: r##" r#"\"# "##, expect: r##"r#"\"#"##, errors: 0 }, + Case { source: r##" r#"\\"# "##, expect: r##"r#"\\"#"##, errors: 0 }, + Case { source: r##" r#"\\\"# "##, expect: r##"r#"\\\"#"##, errors: 0 }, + // escape sequence + Case { + source: r##" r#"\t\n\\t\\n\\\t\\\n\\\\"# "##, + expect: r##"r#"\t\n\\t\\n\\\t\\\n\\\\"#"##, + errors: 0, + }, + Case { source: r##" r#"\\\\\\\\"# "##, expect: r##"r#"\\\\\\\\"#"##, errors: 0 }, + // mismatch - errors: + Case { source: r###" r#"foo"## "###, expect: r###"r#"foo"#"###, errors: 1 }, + Case { source: r###" r##"foo"# "###, expect: "(none)", errors: 2 }, + ]; + + check_cases_with_errors(&lit_cases[..], literal()); } } diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 6a1cf80accd..13ce71c4616 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -18,6 +18,7 @@ mod test { use crate::hir::resolution::import::PathResolutionError; use crate::hir::type_check::TypeCheckError; use crate::hir::Context; + use crate::macros_api::MacroProcessor; use crate::node_interner::{NodeInterner, StmtId}; use crate::graph::CrateGraph; @@ -79,12 +80,16 @@ mod test { krate: root_crate_id, extern_prelude: BTreeMap::new(), }; + + let empty_macro_processors: Vec<&dyn MacroProcessor> = Vec::new(); + // Now we want to populate the CrateDefMap using the DefCollector errors.extend(DefCollector::collect( def_map, &mut context, program.clone().into_sorted(), root_file_id, + empty_macro_processors, )); } (program, context, errors) diff --git a/compiler/noirc_printable_type/src/lib.rs b/compiler/noirc_printable_type/src/lib.rs index 348f5ef3274..e10e400b0db 100644 --- a/compiler/noirc_printable_type/src/lib.rs +++ b/compiler/noirc_printable_type/src/lib.rs @@ -168,15 +168,24 @@ fn fetch_printable_type( fn to_string(value: &PrintableValue, typ: &PrintableType) -> Option { let mut output = String::new(); match (value, typ) { - ( - PrintableValue::Field(f), - PrintableType::Field - // TODO(#2401): We should print the sign for these and probably print normal integers instead of field strings - | PrintableType::SignedInteger { .. } - | PrintableType::UnsignedInteger { .. }, - ) => { + (PrintableValue::Field(f), PrintableType::Field) => { output.push_str(&format_field_string(*f)); } + (PrintableValue::Field(f), PrintableType::UnsignedInteger { width }) => { + let uint_cast = f.to_u128() & ((1 << width) - 1); // Retain the lower 'width' bits + output.push_str(&uint_cast.to_string()); + } + (PrintableValue::Field(f), PrintableType::SignedInteger { width }) => { + let mut uint = f.to_u128(); // Interpret as uint + + // Extract sign relative to width of input + if (uint >> (width - 1)) == 1 { + output.push('-'); + uint = (uint ^ ((1 << width) - 1)) + 1; // Two's complement relative to width of input + } + + output.push_str(&uint.to_string()); + } (PrintableValue::Field(f), PrintableType::Boolean) => { if f.is_one() { output.push_str("true"); @@ -187,8 +196,11 @@ fn to_string(value: &PrintableValue, typ: &PrintableType) -> Option { (PrintableValue::Vec(vector), PrintableType::Array { typ, .. }) => { output.push('['); let mut values = vector.iter().peekable(); - while let Some(value) = values.next() { - output.push_str(&format!("{}", PrintableValueDisplay::Plain(value.clone(), *typ.clone()))); + while let Some(value) = values.next() { + output.push_str(&format!( + "{}", + PrintableValueDisplay::Plain(value.clone(), *typ.clone()) + )); if values.peek().is_some() { output.push_str(", "); } @@ -197,16 +209,19 @@ fn to_string(value: &PrintableValue, typ: &PrintableType) -> Option { } (PrintableValue::String(s), PrintableType::String { .. }) => { - output.push_str(&format!(r#""{s}""#)); + output.push_str(s); } (PrintableValue::Struct(map), PrintableType::Struct { name, fields, .. }) => { output.push_str(&format!("{name} {{ ")); let mut fields = fields.iter().peekable(); - while let Some((key, field_type)) = fields.next() { + while let Some((key, field_type)) = fields.next() { let value = &map[key]; - output.push_str(&format!("{key}: {}", PrintableValueDisplay::Plain(value.clone(), field_type.clone()))); + output.push_str(&format!( + "{key}: {}", + PrintableValueDisplay::Plain(value.clone(), field_type.clone()) + )); if fields.peek().is_some() { output.push_str(", "); } @@ -215,7 +230,7 @@ fn to_string(value: &PrintableValue, typ: &PrintableType) -> Option { output.push_str(" }"); } - _ => return None + _ => return None, }; Some(output) diff --git a/compiler/readme.md b/compiler/readme.md index 7f7c3b86988..d95dce88c7c 100644 --- a/compiler/readme.md +++ b/compiler/readme.md @@ -1,32 +1,11 @@ # Structure -Below we briefly describe the purpose of each crate in this repository. - -## acir - Abstract Circuit Intermediate Representation - -This is the intermediate representation that Noir compiles down to. It is agnostic to any particular NP-Complete language. - -## acvm - Abstract Circuit Virtual Machine - -This is the virtual machine that runs ACIR. Given a proving system to power it, one can create and verify proofs, create smart contracts that verify proofs. +Below we briefly describe the purpose of each crate related to the compiler in this repository. ## fm - File Manager This is the abstraction that the compiler uses to manage source files. -## lsp - -This is the platform agnostic implementation of Noir's Language Server. It implements the various features supported, but doesn't bind to any particular transport. Binding to a transport must be done when consuming the crate. - -## nargo - -This is the default package manager used by Noir. One may draw similarities to Rusts' Cargo. - -## noir_field - -Since the DSL allows a user to create constants which can be as large as the field size, we must have a datatype that is able to store them. This is the purpose of the field crate. -One could alternatively use a BigInt library and store the modulus in a Context struct that gets passed to every compiler pass. - ## noirc_abi When consuming input from the user, a common ABI must be provided such that input provided in JSON/TOML can be converted to noir data types. This crate defines such an ABI. @@ -42,7 +21,3 @@ This crate can be seen as the middle end. It is in charge of generating the ACIR ## noirc_frontend This crate comprises of the first few compiler passes that together we denote as the compiler frontend (in order): lexing, parsing, name resolution, type checking, and monomorphization. If any of these passes error, the resulting monomorphized AST will not be passed to the middle-end (noirc_evaluator) - -## wasm - -This crate is used to compile the Noir compiler into wasm. This is useful in the context where one wants to compile noir programs in the web browser. \ No newline at end of file diff --git a/compiler/source-resolver/package.json b/compiler/source-resolver/package.json index 28ad5570de3..85d6c4343aa 100644 --- a/compiler/source-resolver/package.json +++ b/compiler/source-resolver/package.json @@ -1,6 +1,6 @@ { "name": "@noir-lang/source-resolver", - "version": "0.19.0", + "version": "0.19.4", "license": "MIT", "main": "./lib-node/index_node.js", "types": "./types/index_node.d.ts", diff --git a/compiler/wasm/build.sh b/compiler/wasm/build.sh index 37f2fd0a5a9..24af149bcea 100755 --- a/compiler/wasm/build.sh +++ b/compiler/wasm/build.sh @@ -34,7 +34,7 @@ export CARGO_TARGET_DIR=$self_path/target rm -rf $self_path/outputs >/dev/null 2>&1 rm -rf $self_path/result >/dev/null 2>&1 -if [ -v out ]; then +if [ -n "$out" ]; then echo "Will install package to $out (defined outside installPhase.sh script)" else export out="$self_path/outputs/out" diff --git a/compiler/wasm/fixtures/deps/lib-a/src/lib.nr b/compiler/wasm/fixtures/deps/lib-a/src/lib.nr index abb4302ba38..3f8fa051daf 100644 --- a/compiler/wasm/fixtures/deps/lib-a/src/lib.nr +++ b/compiler/wasm/fixtures/deps/lib-a/src/lib.nr @@ -1,4 +1,3 @@ - use dep::lib_b::assert_non_zero; pub fn divide(a: u64, b: u64) -> u64 { diff --git a/compiler/wasm/fixtures/deps/lib-b/src/lib.nr b/compiler/wasm/fixtures/deps/lib-b/src/lib.nr index ad8b26020e3..a6db876ca56 100644 --- a/compiler/wasm/fixtures/deps/lib-b/src/lib.nr +++ b/compiler/wasm/fixtures/deps/lib-b/src/lib.nr @@ -1,4 +1,3 @@ - pub fn assert_non_zero(x: u64) { - assert(x != 0); + assert(x != 0); } diff --git a/compiler/wasm/fixtures/deps/noir-script/src/main.nr b/compiler/wasm/fixtures/deps/noir-script/src/main.nr index d46597fca2e..056bcc180b4 100644 --- a/compiler/wasm/fixtures/deps/noir-script/src/main.nr +++ b/compiler/wasm/fixtures/deps/noir-script/src/main.nr @@ -1,4 +1,4 @@ use dep::lib_a::divide; -fn main(x : u64, y : pub u64) { - divide(x, y); +fn main(x: u64, y: pub u64) { + divide(x, y); } diff --git a/compiler/wasm/fixtures/simple/noir-script/src/main.nr b/compiler/wasm/fixtures/simple/noir-script/src/main.nr index b1889529f86..1ac4eac96b4 100644 --- a/compiler/wasm/fixtures/simple/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) { +fn main(x: u64, y: pub u64) { assert(x < y); } diff --git a/compiler/wasm/package.json b/compiler/wasm/package.json index bec30febe15..eb1163ad62b 100644 --- a/compiler/wasm/package.json +++ b/compiler/wasm/package.json @@ -3,7 +3,7 @@ "collaborators": [ "The Noir Team " ], - "version": "0.19.0", + "version": "0.19.4", "license": "(MIT OR Apache-2.0)", "main": "./nodejs/noir_wasm.js", "types": "./web/noir_wasm.d.ts", diff --git a/cspell.json b/cspell.json index 90d3963566d..fc8d8d7e82c 100644 --- a/cspell.json +++ b/cspell.json @@ -82,6 +82,7 @@ "nixpkgs", "noirc", "noirup", + "nomicfoundation", "pedersen", "peekable", "plonkc", diff --git a/docs/.gitignore b/docs/.gitignore index c034d49ebde..e6e00b5b355 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -7,6 +7,7 @@ # Generated files .docusaurus .cache-loader +/docs/docs/noir_js/reference/ # Misc .DS_Store @@ -20,4 +21,4 @@ npm-debug.log* yarn-debug.log* yarn-error.log* -package-lock.json \ No newline at end of file +package-lock.json diff --git a/docs/docs/language_concepts/01_functions.md b/docs/docs/language_concepts/01_functions.md index 47cdea0cf04..5eb22170e54 100644 --- a/docs/docs/language_concepts/01_functions.md +++ b/docs/docs/language_concepts/01_functions.md @@ -30,7 +30,7 @@ All parameters in a function must have a type and all types are known at compile 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){} +fn foo(x : Field, y : Field){} ``` The return type of a function can be stated by using the `->` arrow notation. The function below @@ -38,7 +38,7 @@ states that the foo function must return a `Field`. If the function returns no v is omitted. ```rust -fn foo(x : Field, y : pub Field) -> Field { +fn foo(x : Field, y : Field) -> Field { x + y } ``` diff --git a/docs/docs/language_concepts/06_generics.md b/docs/docs/language_concepts/06_generics.md index b700bd5bc5b..9fb4177c2a8 100644 --- a/docs/docs/language_concepts/06_generics.md +++ b/docs/docs/language_concepts/06_generics.md @@ -110,4 +110,4 @@ fn main() { ``` 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). +[here](https://github.com/noir-lang/noir/blob/master/tooling/nargo_cli/tests/execution_success/generics/src/main.nr). diff --git a/docs/docs/language_concepts/07_mutability.md b/docs/docs/language_concepts/07_mutability.md index 4641521b1d9..ad902c42c9b 100644 --- a/docs/docs/language_concepts/07_mutability.md +++ b/docs/docs/language_concepts/07_mutability.md @@ -37,7 +37,7 @@ Note that mutability in noir is local and everything is passed by value, so if a mutates its parameters then the parent function will keep the old value of the parameters. ```rust -fn main() -> Field { +fn main() -> pub Field { let x = 3; helper(x); x // x is still 3 diff --git a/docs/docs/language_concepts/data_types/03_strings.md b/docs/docs/language_concepts/data_types/03_strings.md index c42f34ec3ad..e647a58472f 100644 --- a/docs/docs/language_concepts/data_types/03_strings.md +++ b/docs/docs/language_concepts/data_types/03_strings.md @@ -61,3 +61,19 @@ Example: let s = "Hello \"world" // prints "Hello "world" let s = "hey \tyou"; // prints "hey you" ``` + +## Raw strings + +A raw string begins with the letter `r` and is optionally delimited by a number of hashes `#`. + +Escape characters are *not* processed within raw strings. All contents are interpreted literally. + +Example: + +```rust +let s = r"Hello world"; +let s = r#"Simon says "hello world""#; + +// Any number of hashes may be used (>= 1) as long as the string also terminates with the same number of hashes +let s = r#####"One "#, Two "##, Three "###, Four "####, Five will end the string."#####; +``` diff --git a/docs/docs/language_concepts/data_types/08_structs.md b/docs/docs/language_concepts/data_types/08_structs.md index 85649dfb389..35421734639 100644 --- a/docs/docs/language_concepts/data_types/08_structs.md +++ b/docs/docs/language_concepts/data_types/08_structs.md @@ -67,7 +67,3 @@ fn get_octopus() -> Animal { 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/docs/modules_packages_crates/workspaces.md b/docs/docs/modules_packages_crates/workspaces.md index d9ac92667c9..a979ef9f0a5 100644 --- a/docs/docs/modules_packages_crates/workspaces.md +++ b/docs/docs/modules_packages_crates/workspaces.md @@ -36,4 +36,4 @@ default-member = "crates/a" 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. +Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. \ No newline at end of file diff --git a/docs/docs/noir_js/noir_js.md b/docs/docs/noir_js/noir_js.md index 23ea550e156..f895b22eaf8 100644 --- a/docs/docs/noir_js/noir_js.md +++ b/docs/docs/noir_js/noir_js.md @@ -33,4 +33,4 @@ To install its JavaScript library, run this in your project: 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. +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/noir_js/classes/Noir.md) sections. diff --git a/docs/docs/standard_library/cryptographic_primitives/00_hashes.mdx b/docs/docs/standard_library/cryptographic_primitives/00_hashes.mdx index 76745196681..38077af1ce1 100644 --- a/docs/docs/standard_library/cryptographic_primitives/00_hashes.mdx +++ b/docs/docs/standard_library/cryptographic_primitives/00_hashes.mdx @@ -124,8 +124,8 @@ example: ```rust fn main() { - let hash1 = std::hash::poseidon::bn254::hash_2([1, 2]); - assert(hash1 == 0x115cc0f5e7d690413df64c6b9662e9cf2a3617f2743245519e19607a4417189a); + let hash_2 = std::hash::poseidon::bn254::hash_2([1, 2]); + assert(hash2 == 0x115cc0f5e7d690413df64c6b9662e9cf2a3617f2743245519e19607a4417189a); } ``` diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js index 5128a99b000..c571fdc223a 100644 --- a/docs/docusaurus.config.js +++ b/docs/docusaurus.config.js @@ -70,7 +70,7 @@ const config = { }, items: [ { - href: 'https://github.com/noir-lang/docs', + href: 'https://github.com/noir-lang/noir/tree/master/docs', label: 'GitHub', position: 'right', }, @@ -146,6 +146,70 @@ const config = { indexName: 'noir-lang', }, }, + plugins: [ + [ + 'docusaurus-plugin-typedoc', + { + id: 'noir_js', + entryPoints: ['../tooling/noir_js/src/index.ts'], + tsconfig: '../tooling/noir_js/tsconfig.json', + entryPointStrategy: 'resolve', + out: 'docs/noir_js/reference/noir_js', + plugin: ['typedoc-plugin-markdown'], + name: 'Noir JS', + disableSources: true, + excludePrivate: true, + + sidebar: { + filteredIds: ['noir_js/reference/noir_js/index'], + }, + readme: 'none', + hidePageHeader: true, + hideBreadcrumbs: true, + hideInPageTOC: true, + useCodeBlocks: true, + typeDeclarationFormat: 'table', + propertiesFormat: 'table', + parametersFormat: 'table', + enumMembersFormat: 'table', + indexFormat: 'table', + outputFileStrategy: 'members', + memberPageTitle: '{name}', + membersWithOwnFile: ['Interface', 'Class', 'TypeAlias', 'Function'], + }, + ], + [ + 'docusaurus-plugin-typedoc', + { + id: 'noir_js_backend_barretenberg', + entryPoints: ['../tooling/noir_js_backend_barretenberg/src/index.ts'], + tsconfig: '../tooling/noir_js_backend_barretenberg/tsconfig.json', + entryPointStrategy: 'resolve', + out: 'docs/noir_js/reference/backend_barretenberg', + plugin: ['typedoc-plugin-markdown'], + name: 'Backend Barretenberg', + disableSources: true, + excludePrivate: true, + + sidebar: { + filteredIds: ['noir_js/reference/backend_barretenberg/index'], + }, + readme: 'none', + hidePageHeader: true, + hideBreadcrumbs: true, + hideInPageTOC: true, + useCodeBlocks: true, + typeDeclarationFormat: 'table', + propertiesFormat: 'table', + parametersFormat: 'table', + enumMembersFormat: 'table', + indexFormat: 'table', + outputFileStrategy: 'members', + memberPageTitle: '{name}', + membersWithOwnFile: ['Interface', 'Class', 'TypeAlias'], + }, + ], + ], }; module.exports = config; diff --git a/docs/package.json b/docs/package.json index 3d71493c0ff..ee211065683 100644 --- a/docs/package.json +++ b/docs/package.json @@ -4,8 +4,8 @@ "private": true, "scripts": { "start": "docusaurus start", - "build": "docusaurus build", - "setStable": "node ./scripts/setStable.js" + "build": "yarn version::stables && docusaurus build", + "version::stables": "node ./scripts/setStable.js" }, "dependencies": { "@docusaurus/core": "^2.4.0", @@ -13,14 +13,21 @@ "@docusaurus/preset-classic": "^2.4.0", "@easyops-cn/docusaurus-search-local": "^0.35.0", "@mdx-js/react": "^1.6.22", + "@noir-lang/noir_js": "workspace:*", "axios": "^1.4.0", "clsx": "^1.2.1", + "docusaurus-plugin-typedoc": "1.0.0-next.18", "hast-util-is-element": "^1.1.0", "prism-react-renderer": "^1.3.5", "react": "^17.0.2", "react-dom": "^17.0.2", "rehype-katex": "^5.0.0", - "remark-math": "^3.0.1" + "remark-math": "^3.0.1", + "typedoc": "^0.25.0", + "typedoc-plugin-frontmatter": "^0.0.2", + "typedoc-plugin-markdown": "4.0.0-next.25", + "typedoc-plugin-merge-modules": "^5.1.0", + "typescript": "^5.2.2" }, "devDependencies": { "@docusaurus/module-type-aliases": "^2.4.0" diff --git a/docs/scripts/setStable.js b/docs/scripts/setStable.js index d0954cd6708..4bbe283f4be 100644 --- a/docs/scripts/setStable.js +++ b/docs/scripts/setStable.js @@ -5,7 +5,7 @@ const axios = require('axios'); const { release } = require('os'); const IGNORE_VERSIONS = ['0.16.0']; -const NUMBER_OF_VERSIONS_TO_SHOW = 2; +const NUMBER_OF_VERSIONS_TO_SHOW = 4; async function main() { const versionsFile = path.join(__dirname, '../versions.json'); @@ -31,10 +31,11 @@ async function main() { const stables = data .filter((release) => !release.prerelease && !release.tag_name.includes('aztec')) .filter((release) => !IGNORE_VERSIONS.includes(release.tag_name.replace('v', ''))) - .map((release) => release.tag_name.replace('v', '')) + .map((release) => release.tag_name) .slice(0, NUMBER_OF_VERSIONS_TO_SHOW); console.log('Stables: ', stables); + fs.writeFileSync(versionsFile, JSON.stringify(stables, null, 2)); } diff --git a/docs/sidebars.js b/docs/sidebars.js index 8fddb677a58..3fd391cf09c 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -119,8 +119,22 @@ const sidebars = { label: 'Reference', items: [ { - type: 'autogenerated', - dirName: 'noir_js/reference', + type: 'category', + label: 'Noir JS', + link: { + type: 'doc', + id: 'noir_js/reference/noir_js/index', + }, + items: require('./docs/noir_js/reference/noir_js/typedoc-sidebar.cjs'), + }, + { + type: 'category', + label: 'Backend Barretenberg', + link: { + type: 'doc', + id: 'noir_js/reference/backend_barretenberg/index', + }, + items: require('./docs/noir_js/reference/backend_barretenberg/typedoc-sidebar.cjs'), }, ], }, diff --git a/docs/versioned_docs/version-0.10.5/language_concepts/01_functions.md b/docs/versioned_docs/version-0.10.5/language_concepts/01_functions.md deleted file mode 100644 index 7cb43c4c5f2..00000000000 --- a/docs/versioned_docs/version-0.10.5/language_concepts/01_functions.md +++ /dev/null @@ -1,99 +0,0 @@ ---- -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() {} -``` - -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. - -## 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); -``` - -## 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. diff --git a/docs/versioned_docs/version-0.10.5/language_concepts/07_mutability.md b/docs/versioned_docs/version-0.10.5/language_concepts/07_mutability.md deleted file mode 100644 index 4641521b1d9..00000000000 --- a/docs/versioned_docs/version-0.10.5/language_concepts/07_mutability.md +++ /dev/null @@ -1,92 +0,0 @@ ---- -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/01_functions.md b/docs/versioned_docs/version-0.17.0/language_concepts/01_functions.md deleted file mode 100644 index 47cdea0cf04..00000000000 --- a/docs/versioned_docs/version-0.17.0/language_concepts/01_functions.md +++ /dev/null @@ -1,225 +0,0 @@ ---- -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/06_generics.md b/docs/versioned_docs/version-0.17.0/language_concepts/06_generics.md deleted file mode 100644 index b700bd5bc5b..00000000000 --- a/docs/versioned_docs/version-0.17.0/language_concepts/06_generics.md +++ /dev/null @@ -1,113 +0,0 @@ ---- -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 deleted file mode 100644 index 4641521b1d9..00000000000 --- a/docs/versioned_docs/version-0.17.0/language_concepts/07_mutability.md +++ /dev/null @@ -1,92 +0,0 @@ ---- -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.6.0/language_concepts/01_functions.md b/docs/versioned_docs/version-0.6.0/language_concepts/01_functions.md deleted file mode 100644 index c4bc0545a1c..00000000000 --- a/docs/versioned_docs/version-0.6.0/language_concepts/01_functions.md +++ /dev/null @@ -1,88 +0,0 @@ ---- -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() {} -``` - -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. - -## 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); - constrain s.sum() == 42; -} -``` - -Methods are just syntactic sugar for functions, so if we wanted to we could also call `sum` as -follows: - -```rust -constrain MyStruct::sum(s) == 42 -``` diff --git a/docs/versioned_docs/version-0.6.0/language_concepts/06_generics.md b/docs/versioned_docs/version-0.6.0/language_concepts/06_generics.md deleted file mode 100644 index 6976666b23e..00000000000 --- a/docs/versioned_docs/version-0.6.0/language_concepts/06_generics.md +++ /dev/null @@ -1,116 +0,0 @@ ---- -title: Generics -description: - Learn how to use Generics in Noir -keywords: [Noir, Rust, generics, functions, structs] ---- - -# Generics - -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: comptime 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 { - constrain first.limbs != second.limbs; - first - - fn second(first: BigInt, second: Self) -> Self { - constrain 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() { - constrain 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()]; - constrain 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.6.0/language_concepts/07_mutability.md b/docs/versioned_docs/version-0.6.0/language_concepts/07_mutability.md deleted file mode 100644 index c8ccb4f8b9f..00000000000 --- a/docs/versioned_docs/version-0.6.0/language_concepts/07_mutability.md +++ /dev/null @@ -1,118 +0,0 @@ ---- -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] ---- - -# Mutability - -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 - -Comptime value are values that are known at compile-time. This is different to a witness -which changes per proof. If a comptime value that is being used in your program is changed, then your -circuit will also change. - -Below we show how to declare a comptime value: - -```rust -fn main() { - let a: comptime Field = 5; - - // `comptime Field` can also be inferred: - let a = 5; -} -``` - -Note that variables declared as mutable may not be comptime: - -```rust -fn main() { - // error: Cannot mark a comptime type as mutable - let mut a: comptime Field = 5; - - // a inferred as a private Field here - let mut a = 5; -} -``` - -## Globals - -Noir also supports global variables. However, they must be compile-time variables. If `comptime` is -not explicitly written in the type annotation the compiler will implicitly specify the declaration -as compile-time. They can then be used like any other compile-time variable inside functions. 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: comptime Field = 5` - -fn main(x : Field, y : [Field; N]) { - let res = x * N; - - constrain res == y[0]; - - let res2 = x * mysubmodule::N; - constrain res != res2; -} - -mod mysubmodule { - use dep::std; - - global N: Field = 10; - - fn my_helper() -> comptime 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.7.1/language_concepts/01_functions.md b/docs/versioned_docs/version-0.7.1/language_concepts/01_functions.md deleted file mode 100644 index 54c618599d2..00000000000 --- a/docs/versioned_docs/version-0.7.1/language_concepts/01_functions.md +++ /dev/null @@ -1,88 +0,0 @@ ---- -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() {} -``` - -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. - -## 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); -``` diff --git a/docs/versioned_docs/version-0.7.1/language_concepts/06_generics.md b/docs/versioned_docs/version-0.7.1/language_concepts/06_generics.md deleted file mode 100644 index a4c207e09e4..00000000000 --- a/docs/versioned_docs/version-0.7.1/language_concepts/06_generics.md +++ /dev/null @@ -1,116 +0,0 @@ ---- -title: Generics -description: - Learn how to use Generics in Noir -keywords: [Noir, Rust, generics, functions, structs] ---- - -# Generics - -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: comptime 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.7.1/language_concepts/07_mutability.md b/docs/versioned_docs/version-0.7.1/language_concepts/07_mutability.md deleted file mode 100644 index 5631a322659..00000000000 --- a/docs/versioned_docs/version-0.7.1/language_concepts/07_mutability.md +++ /dev/null @@ -1,118 +0,0 @@ ---- -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 - -Comptime values are values that are known at compile-time. This is different to a witness -which changes per proof. If a comptime value that is being used in your program is changed, then your -circuit will also change. - -Comptime is slightly different from Rust's `const`. Namely, it is a bit more flexible in that normal functions can accept comptime parameters. For example, this is used to verify an array index is known at compile-time. Note that the "known at compile-time" here means "known after function inlining is performed while optimizing the program" and not "known during type-checking." - -Below we show how to declare a comptime value: - -```rust -fn main() { - let a: comptime Field = 5; - - // `comptime Field` can also be inferred: - let a = 5; -} -``` - -Note that variables declared as mutable may not be comptime: - -```rust -fn main() { - // error: Cannot mark a comptime type as mutable - let mut a: comptime Field = 5; - - // a inferred as a private Field here - let mut a = 5; -} -``` - -## Globals - -Noir also supports global variables. However, they must be compile-time variables. If `comptime` is -not explicitly written in the type annotation the compiler will implicitly specify the declaration -as compile-time. They can then be used like any other compile-time variable inside functions. 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: comptime 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() -> comptime 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.9.0/language_concepts/01_functions.md b/docs/versioned_docs/version-0.9.0/language_concepts/01_functions.md deleted file mode 100644 index 54c618599d2..00000000000 --- a/docs/versioned_docs/version-0.9.0/language_concepts/01_functions.md +++ /dev/null @@ -1,88 +0,0 @@ ---- -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() {} -``` - -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. - -## 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); -``` diff --git a/docs/versioned_docs/version-0.9.0/language_concepts/06_generics.md b/docs/versioned_docs/version-0.9.0/language_concepts/06_generics.md deleted file mode 100644 index a4c207e09e4..00000000000 --- a/docs/versioned_docs/version-0.9.0/language_concepts/06_generics.md +++ /dev/null @@ -1,116 +0,0 @@ ---- -title: Generics -description: - Learn how to use Generics in Noir -keywords: [Noir, Rust, generics, functions, structs] ---- - -# Generics - -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: comptime 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.9.0/language_concepts/07_mutability.md b/docs/versioned_docs/version-0.9.0/language_concepts/07_mutability.md deleted file mode 100644 index 69798c7a276..00000000000 --- a/docs/versioned_docs/version-0.9.0/language_concepts/07_mutability.md +++ /dev/null @@ -1,120 +0,0 @@ ---- -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 - -Comptime values are values that are known at compile-time. This is different to a witness -which changes per proof. If a comptime value that is being used in your program is changed, then your -circuit will also change. - -Comptime is slightly different from Rust's `const`. Namely, it is a bit more flexible in that normal functions can accept comptime parameters. For example, this is used to verify an array index is known at compile-time. Note that the "known at compile-time" here means "known after function inlining is performed while optimizing the program" and not "known during type-checking." - -Below we show how to declare a comptime value: - -```rust -fn main() { - let a: comptime Field = 5; - - // `comptime Field` can also be inferred: - let a = 5; -} -``` - -Comptime variables can be mutuable, but must be known at compile time: - -```rust -fn main(runtime_var: Field) -> pub Field { - let known_at_compile_time: comptime Field = 1; - - // The next line will cause an error - let bad_var: comptime Field = runtime_var; - -} -``` - -As `runtime_var` is a argument to the circuit it cannot be known at compile time and so assigning it to a comptime variable should fail. A circuit's arguments is the only way in which non-comptime variables can enter the circuit (excluding [brillig](./unconstrained) foreign calls). - -## Globals - -Noir also supports global variables. However, they must be compile-time variables. If `comptime` is -not explicitly written in the type annotation the compiler will implicitly specify the declaration -as compile-time. They can then be used like any other compile-time variable inside functions. 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: comptime 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() -> comptime 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.10.5/examples/merkle-proof.mdx b/docs/versioned_docs/version-v0.10.5/examples/merkle-proof.mdx similarity index 100% rename from docs/versioned_docs/version-0.10.5/examples/merkle-proof.mdx rename to docs/versioned_docs/version-v0.10.5/examples/merkle-proof.mdx diff --git a/docs/versioned_docs/version-0.10.5/getting_started/00_nargo_installation.md b/docs/versioned_docs/version-v0.10.5/getting_started/00_nargo_installation.md similarity index 100% rename from docs/versioned_docs/version-0.10.5/getting_started/00_nargo_installation.md rename to docs/versioned_docs/version-v0.10.5/getting_started/00_nargo_installation.md diff --git a/docs/versioned_docs/version-0.10.5/getting_started/01_hello_world.md b/docs/versioned_docs/version-v0.10.5/getting_started/01_hello_world.md similarity index 100% rename from docs/versioned_docs/version-0.10.5/getting_started/01_hello_world.md rename to docs/versioned_docs/version-v0.10.5/getting_started/01_hello_world.md diff --git a/docs/versioned_docs/version-0.10.5/getting_started/02_breakdown.md b/docs/versioned_docs/version-v0.10.5/getting_started/02_breakdown.md similarity index 100% rename from docs/versioned_docs/version-0.10.5/getting_started/02_breakdown.md rename to docs/versioned_docs/version-v0.10.5/getting_started/02_breakdown.md diff --git a/docs/versioned_docs/version-0.10.5/getting_started/03_language_server.md b/docs/versioned_docs/version-v0.10.5/getting_started/03_language_server.md similarity index 100% rename from docs/versioned_docs/version-0.10.5/getting_started/03_language_server.md rename to docs/versioned_docs/version-v0.10.5/getting_started/03_language_server.md diff --git a/docs/versioned_docs/version-0.10.5/index.md b/docs/versioned_docs/version-v0.10.5/index.md similarity index 100% rename from docs/versioned_docs/version-0.10.5/index.md rename to docs/versioned_docs/version-v0.10.5/index.md diff --git a/docs/versioned_docs/version-v0.10.5/language_concepts/01_functions.md b/docs/versioned_docs/version-v0.10.5/language_concepts/01_functions.md new file mode 100644 index 00000000000..069d86c46d0 --- /dev/null +++ b/docs/versioned_docs/version-v0.10.5/language_concepts/01_functions.md @@ -0,0 +1,99 @@ +--- +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() {} +``` + +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 : 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 : Field) -> Field { + x + y +} +``` + +Note that a `return` keyword is unneeded in this case - the last expression in a function's body is +returned. + +## 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); +``` + +## 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. diff --git a/docs/versioned_docs/version-0.10.5/language_concepts/02_control_flow.md b/docs/versioned_docs/version-v0.10.5/language_concepts/02_control_flow.md similarity index 100% rename from docs/versioned_docs/version-0.10.5/language_concepts/02_control_flow.md rename to docs/versioned_docs/version-v0.10.5/language_concepts/02_control_flow.md diff --git a/docs/versioned_docs/version-0.10.5/language_concepts/03_ops.md b/docs/versioned_docs/version-v0.10.5/language_concepts/03_ops.md similarity index 100% rename from docs/versioned_docs/version-0.10.5/language_concepts/03_ops.md rename to docs/versioned_docs/version-v0.10.5/language_concepts/03_ops.md diff --git a/docs/versioned_docs/version-0.10.5/language_concepts/04_assert.md b/docs/versioned_docs/version-v0.10.5/language_concepts/04_assert.md similarity index 100% rename from docs/versioned_docs/version-0.10.5/language_concepts/04_assert.md rename to docs/versioned_docs/version-v0.10.5/language_concepts/04_assert.md diff --git a/docs/versioned_docs/version-0.10.5/language_concepts/05_unconstrained.md b/docs/versioned_docs/version-v0.10.5/language_concepts/05_unconstrained.md similarity index 100% rename from docs/versioned_docs/version-0.10.5/language_concepts/05_unconstrained.md rename to docs/versioned_docs/version-v0.10.5/language_concepts/05_unconstrained.md diff --git a/docs/versioned_docs/version-v0.10.5/language_concepts/06_generics.md b/docs/versioned_docs/version-v0.10.5/language_concepts/06_generics.md new file mode 100644 index 00000000000..9fb4177c2a8 --- /dev/null +++ b/docs/versioned_docs/version-v0.10.5/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/tooling/nargo_cli/tests/execution_success/generics/src/main.nr). diff --git a/docs/versioned_docs/version-v0.10.5/language_concepts/07_mutability.md b/docs/versioned_docs/version-v0.10.5/language_concepts/07_mutability.md new file mode 100644 index 00000000000..ad902c42c9b --- /dev/null +++ b/docs/versioned_docs/version-v0.10.5/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() -> pub 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.10.5/language_concepts/08_lambdas.md b/docs/versioned_docs/version-v0.10.5/language_concepts/08_lambdas.md similarity index 100% rename from docs/versioned_docs/version-0.10.5/language_concepts/08_lambdas.md rename to docs/versioned_docs/version-v0.10.5/language_concepts/08_lambdas.md diff --git a/docs/versioned_docs/version-0.10.5/language_concepts/09_comments.md b/docs/versioned_docs/version-v0.10.5/language_concepts/09_comments.md similarity index 100% rename from docs/versioned_docs/version-0.10.5/language_concepts/09_comments.md rename to docs/versioned_docs/version-v0.10.5/language_concepts/09_comments.md diff --git a/docs/versioned_docs/version-0.10.5/language_concepts/10_distinct.md b/docs/versioned_docs/version-v0.10.5/language_concepts/10_distinct.md similarity index 100% rename from docs/versioned_docs/version-0.10.5/language_concepts/10_distinct.md rename to docs/versioned_docs/version-v0.10.5/language_concepts/10_distinct.md diff --git a/docs/versioned_docs/version-0.10.5/language_concepts/11_shadowing.md b/docs/versioned_docs/version-v0.10.5/language_concepts/11_shadowing.md similarity index 100% rename from docs/versioned_docs/version-0.10.5/language_concepts/11_shadowing.md rename to docs/versioned_docs/version-v0.10.5/language_concepts/11_shadowing.md diff --git a/docs/versioned_docs/version-0.10.5/language_concepts/data_types.md b/docs/versioned_docs/version-v0.10.5/language_concepts/data_types.md similarity index 100% rename from docs/versioned_docs/version-0.10.5/language_concepts/data_types.md rename to docs/versioned_docs/version-v0.10.5/language_concepts/data_types.md diff --git a/docs/versioned_docs/version-0.10.5/language_concepts/data_types/00_fields.md b/docs/versioned_docs/version-v0.10.5/language_concepts/data_types/00_fields.md similarity index 100% rename from docs/versioned_docs/version-0.10.5/language_concepts/data_types/00_fields.md rename to docs/versioned_docs/version-v0.10.5/language_concepts/data_types/00_fields.md diff --git a/docs/versioned_docs/version-0.10.5/language_concepts/data_types/01_integers.md b/docs/versioned_docs/version-v0.10.5/language_concepts/data_types/01_integers.md similarity index 100% rename from docs/versioned_docs/version-0.10.5/language_concepts/data_types/01_integers.md rename to docs/versioned_docs/version-v0.10.5/language_concepts/data_types/01_integers.md diff --git a/docs/versioned_docs/version-0.10.5/language_concepts/data_types/02_booleans.md b/docs/versioned_docs/version-v0.10.5/language_concepts/data_types/02_booleans.md similarity index 100% rename from docs/versioned_docs/version-0.10.5/language_concepts/data_types/02_booleans.md rename to docs/versioned_docs/version-v0.10.5/language_concepts/data_types/02_booleans.md diff --git a/docs/versioned_docs/version-0.10.5/language_concepts/data_types/03_strings.md b/docs/versioned_docs/version-v0.10.5/language_concepts/data_types/03_strings.md similarity index 100% rename from docs/versioned_docs/version-0.10.5/language_concepts/data_types/03_strings.md rename to docs/versioned_docs/version-v0.10.5/language_concepts/data_types/03_strings.md diff --git a/docs/versioned_docs/version-0.10.5/language_concepts/data_types/04_arrays.md b/docs/versioned_docs/version-v0.10.5/language_concepts/data_types/04_arrays.md similarity index 100% rename from docs/versioned_docs/version-0.10.5/language_concepts/data_types/04_arrays.md rename to docs/versioned_docs/version-v0.10.5/language_concepts/data_types/04_arrays.md diff --git a/docs/versioned_docs/version-0.10.5/language_concepts/data_types/05_slices.md b/docs/versioned_docs/version-v0.10.5/language_concepts/data_types/05_slices.md similarity index 100% rename from docs/versioned_docs/version-0.10.5/language_concepts/data_types/05_slices.md rename to docs/versioned_docs/version-v0.10.5/language_concepts/data_types/05_slices.md diff --git a/docs/versioned_docs/version-0.10.5/language_concepts/data_types/06_vectors.md b/docs/versioned_docs/version-v0.10.5/language_concepts/data_types/06_vectors.md similarity index 100% rename from docs/versioned_docs/version-0.10.5/language_concepts/data_types/06_vectors.md rename to docs/versioned_docs/version-v0.10.5/language_concepts/data_types/06_vectors.md diff --git a/docs/versioned_docs/version-0.10.5/language_concepts/data_types/07_tuples.md b/docs/versioned_docs/version-v0.10.5/language_concepts/data_types/07_tuples.md similarity index 100% rename from docs/versioned_docs/version-0.10.5/language_concepts/data_types/07_tuples.md rename to docs/versioned_docs/version-v0.10.5/language_concepts/data_types/07_tuples.md diff --git a/docs/versioned_docs/version-0.10.5/language_concepts/data_types/08_structs.md b/docs/versioned_docs/version-v0.10.5/language_concepts/data_types/08_structs.md similarity index 100% rename from docs/versioned_docs/version-0.10.5/language_concepts/data_types/08_structs.md rename to docs/versioned_docs/version-v0.10.5/language_concepts/data_types/08_structs.md diff --git a/docs/versioned_docs/version-0.10.5/language_concepts/data_types/09_references.md b/docs/versioned_docs/version-v0.10.5/language_concepts/data_types/09_references.md similarity index 100% rename from docs/versioned_docs/version-0.10.5/language_concepts/data_types/09_references.md rename to docs/versioned_docs/version-v0.10.5/language_concepts/data_types/09_references.md diff --git a/docs/versioned_docs/version-0.10.5/language_concepts/data_types/10_function_types.md b/docs/versioned_docs/version-v0.10.5/language_concepts/data_types/10_function_types.md similarity index 100% rename from docs/versioned_docs/version-0.10.5/language_concepts/data_types/10_function_types.md rename to docs/versioned_docs/version-v0.10.5/language_concepts/data_types/10_function_types.md diff --git a/docs/versioned_docs/version-0.10.5/modules_packages_crates/crates_and_packages.md b/docs/versioned_docs/version-v0.10.5/modules_packages_crates/crates_and_packages.md similarity index 100% rename from docs/versioned_docs/version-0.10.5/modules_packages_crates/crates_and_packages.md rename to docs/versioned_docs/version-v0.10.5/modules_packages_crates/crates_and_packages.md diff --git a/docs/versioned_docs/version-0.10.5/modules_packages_crates/dependencies.md b/docs/versioned_docs/version-v0.10.5/modules_packages_crates/dependencies.md similarity index 100% rename from docs/versioned_docs/version-0.10.5/modules_packages_crates/dependencies.md rename to docs/versioned_docs/version-v0.10.5/modules_packages_crates/dependencies.md diff --git a/docs/versioned_docs/version-0.10.5/modules_packages_crates/modules.md b/docs/versioned_docs/version-v0.10.5/modules_packages_crates/modules.md similarity index 100% rename from docs/versioned_docs/version-0.10.5/modules_packages_crates/modules.md rename to docs/versioned_docs/version-v0.10.5/modules_packages_crates/modules.md diff --git a/docs/versioned_docs/version-0.10.5/modules_packages_crates/workspaces.md b/docs/versioned_docs/version-v0.10.5/modules_packages_crates/workspaces.md similarity index 100% rename from docs/versioned_docs/version-0.10.5/modules_packages_crates/workspaces.md rename to docs/versioned_docs/version-v0.10.5/modules_packages_crates/workspaces.md diff --git a/docs/versioned_docs/version-0.10.5/nargo/01_commands.md b/docs/versioned_docs/version-v0.10.5/nargo/01_commands.md similarity index 100% rename from docs/versioned_docs/version-0.10.5/nargo/01_commands.md rename to docs/versioned_docs/version-v0.10.5/nargo/01_commands.md diff --git a/docs/versioned_docs/version-0.10.5/nargo/02_testing.md b/docs/versioned_docs/version-v0.10.5/nargo/02_testing.md similarity index 100% rename from docs/versioned_docs/version-0.10.5/nargo/02_testing.md rename to docs/versioned_docs/version-v0.10.5/nargo/02_testing.md diff --git a/docs/versioned_docs/version-0.10.5/nargo/03_solidity_verifier.md b/docs/versioned_docs/version-v0.10.5/nargo/03_solidity_verifier.md similarity index 100% rename from docs/versioned_docs/version-0.10.5/nargo/03_solidity_verifier.md rename to docs/versioned_docs/version-v0.10.5/nargo/03_solidity_verifier.md diff --git a/docs/versioned_docs/version-0.10.5/standard_library/black_box_fns.md b/docs/versioned_docs/version-v0.10.5/standard_library/black_box_fns.md similarity index 100% rename from docs/versioned_docs/version-0.10.5/standard_library/black_box_fns.md rename to docs/versioned_docs/version-v0.10.5/standard_library/black_box_fns.md diff --git a/docs/versioned_docs/version-0.10.5/standard_library/cryptographic_primitives.md b/docs/versioned_docs/version-v0.10.5/standard_library/cryptographic_primitives.md similarity index 100% rename from docs/versioned_docs/version-0.10.5/standard_library/cryptographic_primitives.md rename to docs/versioned_docs/version-v0.10.5/standard_library/cryptographic_primitives.md diff --git a/docs/versioned_docs/version-0.10.5/standard_library/cryptographic_primitives/00_hashes.mdx b/docs/versioned_docs/version-v0.10.5/standard_library/cryptographic_primitives/00_hashes.mdx similarity index 100% rename from docs/versioned_docs/version-0.10.5/standard_library/cryptographic_primitives/00_hashes.mdx rename to docs/versioned_docs/version-v0.10.5/standard_library/cryptographic_primitives/00_hashes.mdx diff --git a/docs/versioned_docs/version-0.10.5/standard_library/cryptographic_primitives/01_scalar.mdx b/docs/versioned_docs/version-v0.10.5/standard_library/cryptographic_primitives/01_scalar.mdx similarity index 100% rename from docs/versioned_docs/version-0.10.5/standard_library/cryptographic_primitives/01_scalar.mdx rename to docs/versioned_docs/version-v0.10.5/standard_library/cryptographic_primitives/01_scalar.mdx diff --git a/docs/versioned_docs/version-0.10.5/standard_library/cryptographic_primitives/02_schnorr.mdx b/docs/versioned_docs/version-v0.10.5/standard_library/cryptographic_primitives/02_schnorr.mdx similarity index 100% rename from docs/versioned_docs/version-0.10.5/standard_library/cryptographic_primitives/02_schnorr.mdx rename to docs/versioned_docs/version-v0.10.5/standard_library/cryptographic_primitives/02_schnorr.mdx diff --git a/docs/versioned_docs/version-0.10.5/standard_library/cryptographic_primitives/03_ecdsa_sig_verification.mdx b/docs/versioned_docs/version-v0.10.5/standard_library/cryptographic_primitives/03_ecdsa_sig_verification.mdx similarity index 100% rename from docs/versioned_docs/version-0.10.5/standard_library/cryptographic_primitives/03_ecdsa_sig_verification.mdx rename to docs/versioned_docs/version-v0.10.5/standard_library/cryptographic_primitives/03_ecdsa_sig_verification.mdx diff --git a/docs/versioned_docs/version-0.10.5/standard_library/cryptographic_primitives/04_ec_primitives.md b/docs/versioned_docs/version-v0.10.5/standard_library/cryptographic_primitives/04_ec_primitives.md similarity index 100% rename from docs/versioned_docs/version-0.10.5/standard_library/cryptographic_primitives/04_ec_primitives.md rename to docs/versioned_docs/version-v0.10.5/standard_library/cryptographic_primitives/04_ec_primitives.md diff --git a/docs/versioned_docs/version-0.10.5/standard_library/cryptographic_primitives/05_eddsa.mdx b/docs/versioned_docs/version-v0.10.5/standard_library/cryptographic_primitives/05_eddsa.mdx similarity index 100% rename from docs/versioned_docs/version-0.10.5/standard_library/cryptographic_primitives/05_eddsa.mdx rename to docs/versioned_docs/version-v0.10.5/standard_library/cryptographic_primitives/05_eddsa.mdx diff --git a/docs/versioned_docs/version-0.10.5/standard_library/cryptographic_primitives/common/_blackbox.mdx b/docs/versioned_docs/version-v0.10.5/standard_library/cryptographic_primitives/common/_blackbox.mdx similarity index 100% rename from docs/versioned_docs/version-0.10.5/standard_library/cryptographic_primitives/common/_blackbox.mdx rename to docs/versioned_docs/version-v0.10.5/standard_library/cryptographic_primitives/common/_blackbox.mdx diff --git a/docs/versioned_docs/version-0.10.5/standard_library/logging.md b/docs/versioned_docs/version-v0.10.5/standard_library/logging.md similarity index 100% rename from docs/versioned_docs/version-0.10.5/standard_library/logging.md rename to docs/versioned_docs/version-v0.10.5/standard_library/logging.md diff --git a/docs/versioned_docs/version-0.10.5/standard_library/merkle_trees.md b/docs/versioned_docs/version-v0.10.5/standard_library/merkle_trees.md similarity index 100% rename from docs/versioned_docs/version-0.10.5/standard_library/merkle_trees.md rename to docs/versioned_docs/version-v0.10.5/standard_library/merkle_trees.md diff --git a/docs/versioned_docs/version-0.10.5/standard_library/options.md b/docs/versioned_docs/version-v0.10.5/standard_library/options.md similarity index 100% rename from docs/versioned_docs/version-0.10.5/standard_library/options.md rename to docs/versioned_docs/version-v0.10.5/standard_library/options.md diff --git a/docs/versioned_docs/version-0.10.5/standard_library/recursion.md b/docs/versioned_docs/version-v0.10.5/standard_library/recursion.md similarity index 100% rename from docs/versioned_docs/version-0.10.5/standard_library/recursion.md rename to docs/versioned_docs/version-v0.10.5/standard_library/recursion.md diff --git a/docs/versioned_docs/version-0.10.5/standard_library/zeroed.md b/docs/versioned_docs/version-v0.10.5/standard_library/zeroed.md similarity index 100% rename from docs/versioned_docs/version-0.10.5/standard_library/zeroed.md rename to docs/versioned_docs/version-v0.10.5/standard_library/zeroed.md diff --git a/docs/versioned_docs/version-0.10.5/typescript.md b/docs/versioned_docs/version-v0.10.5/typescript.md similarity index 100% rename from docs/versioned_docs/version-0.10.5/typescript.md rename to docs/versioned_docs/version-v0.10.5/typescript.md diff --git a/docs/versioned_docs/version-0.17.0/examples/merkle-proof.mdx b/docs/versioned_docs/version-v0.17.0/examples/merkle-proof.mdx similarity index 100% rename from docs/versioned_docs/version-0.17.0/examples/merkle-proof.mdx rename to docs/versioned_docs/version-v0.17.0/examples/merkle-proof.mdx diff --git a/docs/versioned_docs/version-0.17.0/getting_started/00_nargo_installation.md b/docs/versioned_docs/version-v0.17.0/getting_started/00_nargo_installation.md similarity index 100% rename from docs/versioned_docs/version-0.17.0/getting_started/00_nargo_installation.md rename to docs/versioned_docs/version-v0.17.0/getting_started/00_nargo_installation.md diff --git a/docs/versioned_docs/version-0.17.0/getting_started/01_hello_world.md b/docs/versioned_docs/version-v0.17.0/getting_started/01_hello_world.md similarity index 100% rename from docs/versioned_docs/version-0.17.0/getting_started/01_hello_world.md rename to docs/versioned_docs/version-v0.17.0/getting_started/01_hello_world.md diff --git a/docs/versioned_docs/version-0.17.0/getting_started/02_breakdown.md b/docs/versioned_docs/version-v0.17.0/getting_started/02_breakdown.md similarity index 100% rename from docs/versioned_docs/version-0.17.0/getting_started/02_breakdown.md rename to docs/versioned_docs/version-v0.17.0/getting_started/02_breakdown.md diff --git a/docs/versioned_docs/version-0.17.0/index.md b/docs/versioned_docs/version-v0.17.0/index.md similarity index 100% rename from docs/versioned_docs/version-0.17.0/index.md rename to docs/versioned_docs/version-v0.17.0/index.md diff --git a/docs/versioned_docs/version-v0.17.0/language_concepts/01_functions.md b/docs/versioned_docs/version-v0.17.0/language_concepts/01_functions.md new file mode 100644 index 00000000000..5eb22170e54 --- /dev/null +++ b/docs/versioned_docs/version-v0.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 : 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 : 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-v0.17.0/language_concepts/02_control_flow.md similarity index 100% rename from docs/versioned_docs/version-0.17.0/language_concepts/02_control_flow.md rename to docs/versioned_docs/version-v0.17.0/language_concepts/02_control_flow.md diff --git a/docs/versioned_docs/version-0.17.0/language_concepts/03_ops.md b/docs/versioned_docs/version-v0.17.0/language_concepts/03_ops.md similarity index 100% rename from docs/versioned_docs/version-0.17.0/language_concepts/03_ops.md rename to docs/versioned_docs/version-v0.17.0/language_concepts/03_ops.md diff --git a/docs/versioned_docs/version-0.17.0/language_concepts/04_assert.md b/docs/versioned_docs/version-v0.17.0/language_concepts/04_assert.md similarity index 100% rename from docs/versioned_docs/version-0.17.0/language_concepts/04_assert.md rename to docs/versioned_docs/version-v0.17.0/language_concepts/04_assert.md diff --git a/docs/versioned_docs/version-0.17.0/language_concepts/05_unconstrained.md b/docs/versioned_docs/version-v0.17.0/language_concepts/05_unconstrained.md similarity index 100% rename from docs/versioned_docs/version-0.17.0/language_concepts/05_unconstrained.md rename to docs/versioned_docs/version-v0.17.0/language_concepts/05_unconstrained.md diff --git a/docs/versioned_docs/version-v0.17.0/language_concepts/06_generics.md b/docs/versioned_docs/version-v0.17.0/language_concepts/06_generics.md new file mode 100644 index 00000000000..9fb4177c2a8 --- /dev/null +++ b/docs/versioned_docs/version-v0.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/tooling/nargo_cli/tests/execution_success/generics/src/main.nr). diff --git a/docs/versioned_docs/version-v0.17.0/language_concepts/07_mutability.md b/docs/versioned_docs/version-v0.17.0/language_concepts/07_mutability.md new file mode 100644 index 00000000000..ad902c42c9b --- /dev/null +++ b/docs/versioned_docs/version-v0.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() -> pub 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-v0.17.0/language_concepts/08_lambdas.md similarity index 100% rename from docs/versioned_docs/version-0.17.0/language_concepts/08_lambdas.md rename to docs/versioned_docs/version-v0.17.0/language_concepts/08_lambdas.md diff --git a/docs/versioned_docs/version-0.17.0/language_concepts/09_comments.md b/docs/versioned_docs/version-v0.17.0/language_concepts/09_comments.md similarity index 100% rename from docs/versioned_docs/version-0.17.0/language_concepts/09_comments.md rename to docs/versioned_docs/version-v0.17.0/language_concepts/09_comments.md diff --git a/docs/versioned_docs/version-0.17.0/language_concepts/10_distinct.md b/docs/versioned_docs/version-v0.17.0/language_concepts/10_distinct.md similarity index 100% rename from docs/versioned_docs/version-0.17.0/language_concepts/10_distinct.md rename to docs/versioned_docs/version-v0.17.0/language_concepts/10_distinct.md diff --git a/docs/versioned_docs/version-0.17.0/language_concepts/11_shadowing.md b/docs/versioned_docs/version-v0.17.0/language_concepts/11_shadowing.md similarity index 100% rename from docs/versioned_docs/version-0.17.0/language_concepts/11_shadowing.md rename to docs/versioned_docs/version-v0.17.0/language_concepts/11_shadowing.md diff --git a/docs/versioned_docs/version-0.17.0/language_concepts/data_types.md b/docs/versioned_docs/version-v0.17.0/language_concepts/data_types.md similarity index 100% rename from docs/versioned_docs/version-0.17.0/language_concepts/data_types.md rename to docs/versioned_docs/version-v0.17.0/language_concepts/data_types.md diff --git a/docs/versioned_docs/version-0.17.0/language_concepts/data_types/00_fields.md b/docs/versioned_docs/version-v0.17.0/language_concepts/data_types/00_fields.md similarity index 100% rename from docs/versioned_docs/version-0.17.0/language_concepts/data_types/00_fields.md rename to docs/versioned_docs/version-v0.17.0/language_concepts/data_types/00_fields.md diff --git a/docs/versioned_docs/version-0.17.0/language_concepts/data_types/01_integers.md b/docs/versioned_docs/version-v0.17.0/language_concepts/data_types/01_integers.md similarity index 100% rename from docs/versioned_docs/version-0.17.0/language_concepts/data_types/01_integers.md rename to docs/versioned_docs/version-v0.17.0/language_concepts/data_types/01_integers.md diff --git a/docs/versioned_docs/version-0.17.0/language_concepts/data_types/02_booleans.md b/docs/versioned_docs/version-v0.17.0/language_concepts/data_types/02_booleans.md similarity index 100% rename from docs/versioned_docs/version-0.17.0/language_concepts/data_types/02_booleans.md rename to docs/versioned_docs/version-v0.17.0/language_concepts/data_types/02_booleans.md diff --git a/docs/versioned_docs/version-0.17.0/language_concepts/data_types/03_strings.md b/docs/versioned_docs/version-v0.17.0/language_concepts/data_types/03_strings.md similarity index 100% rename from docs/versioned_docs/version-0.17.0/language_concepts/data_types/03_strings.md rename to docs/versioned_docs/version-v0.17.0/language_concepts/data_types/03_strings.md diff --git a/docs/versioned_docs/version-0.17.0/language_concepts/data_types/04_arrays.md b/docs/versioned_docs/version-v0.17.0/language_concepts/data_types/04_arrays.md similarity index 100% rename from docs/versioned_docs/version-0.17.0/language_concepts/data_types/04_arrays.md rename to docs/versioned_docs/version-v0.17.0/language_concepts/data_types/04_arrays.md diff --git a/docs/versioned_docs/version-0.17.0/language_concepts/data_types/05_slices.mdx b/docs/versioned_docs/version-v0.17.0/language_concepts/data_types/05_slices.mdx similarity index 100% rename from docs/versioned_docs/version-0.17.0/language_concepts/data_types/05_slices.mdx rename to docs/versioned_docs/version-v0.17.0/language_concepts/data_types/05_slices.mdx diff --git a/docs/versioned_docs/version-0.17.0/language_concepts/data_types/06_vectors.mdx b/docs/versioned_docs/version-v0.17.0/language_concepts/data_types/06_vectors.mdx similarity index 100% rename from docs/versioned_docs/version-0.17.0/language_concepts/data_types/06_vectors.mdx rename to docs/versioned_docs/version-v0.17.0/language_concepts/data_types/06_vectors.mdx diff --git a/docs/versioned_docs/version-0.17.0/language_concepts/data_types/07_tuples.md b/docs/versioned_docs/version-v0.17.0/language_concepts/data_types/07_tuples.md similarity index 100% rename from docs/versioned_docs/version-0.17.0/language_concepts/data_types/07_tuples.md rename to docs/versioned_docs/version-v0.17.0/language_concepts/data_types/07_tuples.md diff --git a/docs/versioned_docs/version-0.17.0/language_concepts/data_types/08_structs.md b/docs/versioned_docs/version-v0.17.0/language_concepts/data_types/08_structs.md similarity index 100% rename from docs/versioned_docs/version-0.17.0/language_concepts/data_types/08_structs.md rename to docs/versioned_docs/version-v0.17.0/language_concepts/data_types/08_structs.md diff --git a/docs/versioned_docs/version-0.17.0/language_concepts/data_types/09_references.md b/docs/versioned_docs/version-v0.17.0/language_concepts/data_types/09_references.md similarity index 100% rename from docs/versioned_docs/version-0.17.0/language_concepts/data_types/09_references.md rename to docs/versioned_docs/version-v0.17.0/language_concepts/data_types/09_references.md diff --git a/docs/versioned_docs/version-0.17.0/language_concepts/data_types/10_function_types.md b/docs/versioned_docs/version-v0.17.0/language_concepts/data_types/10_function_types.md similarity index 100% rename from docs/versioned_docs/version-0.17.0/language_concepts/data_types/10_function_types.md rename to docs/versioned_docs/version-v0.17.0/language_concepts/data_types/10_function_types.md diff --git a/docs/versioned_docs/version-0.17.0/migration_notes.md b/docs/versioned_docs/version-v0.17.0/migration_notes.md similarity index 100% rename from docs/versioned_docs/version-0.17.0/migration_notes.md rename to docs/versioned_docs/version-v0.17.0/migration_notes.md diff --git a/docs/versioned_docs/version-0.17.0/modules_packages_crates/crates_and_packages.md b/docs/versioned_docs/version-v0.17.0/modules_packages_crates/crates_and_packages.md similarity index 100% rename from docs/versioned_docs/version-0.17.0/modules_packages_crates/crates_and_packages.md rename to docs/versioned_docs/version-v0.17.0/modules_packages_crates/crates_and_packages.md diff --git a/docs/versioned_docs/version-0.17.0/modules_packages_crates/dependencies.md b/docs/versioned_docs/version-v0.17.0/modules_packages_crates/dependencies.md similarity index 100% rename from docs/versioned_docs/version-0.17.0/modules_packages_crates/dependencies.md rename to docs/versioned_docs/version-v0.17.0/modules_packages_crates/dependencies.md diff --git a/docs/versioned_docs/version-0.17.0/modules_packages_crates/modules.md b/docs/versioned_docs/version-v0.17.0/modules_packages_crates/modules.md similarity index 100% rename from docs/versioned_docs/version-0.17.0/modules_packages_crates/modules.md rename to docs/versioned_docs/version-v0.17.0/modules_packages_crates/modules.md diff --git a/docs/versioned_docs/version-0.17.0/modules_packages_crates/workspaces.md b/docs/versioned_docs/version-v0.17.0/modules_packages_crates/workspaces.md similarity index 100% rename from docs/versioned_docs/version-0.17.0/modules_packages_crates/workspaces.md rename to docs/versioned_docs/version-v0.17.0/modules_packages_crates/workspaces.md diff --git a/docs/versioned_docs/version-0.17.0/nargo/01_commands.md b/docs/versioned_docs/version-v0.17.0/nargo/01_commands.md similarity index 100% rename from docs/versioned_docs/version-0.17.0/nargo/01_commands.md rename to docs/versioned_docs/version-v0.17.0/nargo/01_commands.md diff --git a/docs/versioned_docs/version-0.17.0/nargo/02_testing.md b/docs/versioned_docs/version-v0.17.0/nargo/02_testing.md similarity index 100% rename from docs/versioned_docs/version-0.17.0/nargo/02_testing.md rename to docs/versioned_docs/version-v0.17.0/nargo/02_testing.md diff --git a/docs/versioned_docs/version-0.17.0/nargo/03_solidity_verifier.md b/docs/versioned_docs/version-v0.17.0/nargo/03_solidity_verifier.md similarity index 100% rename from docs/versioned_docs/version-0.17.0/nargo/03_solidity_verifier.md rename to docs/versioned_docs/version-v0.17.0/nargo/03_solidity_verifier.md diff --git a/docs/versioned_docs/version-0.17.0/nargo/04_language_server.md b/docs/versioned_docs/version-v0.17.0/nargo/04_language_server.md similarity index 100% rename from docs/versioned_docs/version-0.17.0/nargo/04_language_server.md rename to docs/versioned_docs/version-v0.17.0/nargo/04_language_server.md diff --git a/docs/versioned_docs/version-0.17.0/noir_js/getting_started/01_tiny_noir_app.md b/docs/versioned_docs/version-v0.17.0/noir_js/getting_started/01_tiny_noir_app.md similarity index 100% rename from docs/versioned_docs/version-0.17.0/noir_js/getting_started/01_tiny_noir_app.md rename to docs/versioned_docs/version-v0.17.0/noir_js/getting_started/01_tiny_noir_app.md diff --git a/docs/versioned_docs/version-0.17.0/noir_js/noir_js.md b/docs/versioned_docs/version-v0.17.0/noir_js/noir_js.md similarity index 100% rename from docs/versioned_docs/version-0.17.0/noir_js/noir_js.md rename to docs/versioned_docs/version-v0.17.0/noir_js/noir_js.md diff --git a/docs/versioned_docs/version-0.17.0/noir_js/reference/01_noirjs.md b/docs/versioned_docs/version-v0.17.0/noir_js/reference/01_noirjs.md similarity index 100% rename from docs/versioned_docs/version-0.17.0/noir_js/reference/01_noirjs.md rename to docs/versioned_docs/version-v0.17.0/noir_js/reference/01_noirjs.md diff --git a/docs/docs/noir_js/reference/02_bb_backend.md b/docs/versioned_docs/version-v0.17.0/noir_js/reference/02_bb_backend.md similarity index 100% rename from docs/docs/noir_js/reference/02_bb_backend.md rename to docs/versioned_docs/version-v0.17.0/noir_js/reference/02_bb_backend.md diff --git a/docs/versioned_docs/version-0.17.0/standard_library/black_box_fns.md b/docs/versioned_docs/version-v0.17.0/standard_library/black_box_fns.md similarity index 100% rename from docs/versioned_docs/version-0.17.0/standard_library/black_box_fns.md rename to docs/versioned_docs/version-v0.17.0/standard_library/black_box_fns.md diff --git a/docs/versioned_docs/version-0.17.0/standard_library/cryptographic_primitives.md b/docs/versioned_docs/version-v0.17.0/standard_library/cryptographic_primitives.md similarity index 100% rename from docs/versioned_docs/version-0.17.0/standard_library/cryptographic_primitives.md rename to docs/versioned_docs/version-v0.17.0/standard_library/cryptographic_primitives.md diff --git a/docs/versioned_docs/version-0.17.0/standard_library/cryptographic_primitives/00_hashes.mdx b/docs/versioned_docs/version-v0.17.0/standard_library/cryptographic_primitives/00_hashes.mdx similarity index 100% rename from docs/versioned_docs/version-0.17.0/standard_library/cryptographic_primitives/00_hashes.mdx rename to docs/versioned_docs/version-v0.17.0/standard_library/cryptographic_primitives/00_hashes.mdx diff --git a/docs/versioned_docs/version-0.17.0/standard_library/cryptographic_primitives/01_scalar.mdx b/docs/versioned_docs/version-v0.17.0/standard_library/cryptographic_primitives/01_scalar.mdx similarity index 100% rename from docs/versioned_docs/version-0.17.0/standard_library/cryptographic_primitives/01_scalar.mdx rename to docs/versioned_docs/version-v0.17.0/standard_library/cryptographic_primitives/01_scalar.mdx diff --git a/docs/versioned_docs/version-0.17.0/standard_library/cryptographic_primitives/02_schnorr.mdx b/docs/versioned_docs/version-v0.17.0/standard_library/cryptographic_primitives/02_schnorr.mdx similarity index 100% rename from docs/versioned_docs/version-0.17.0/standard_library/cryptographic_primitives/02_schnorr.mdx rename to docs/versioned_docs/version-v0.17.0/standard_library/cryptographic_primitives/02_schnorr.mdx diff --git a/docs/versioned_docs/version-0.17.0/standard_library/cryptographic_primitives/03_ecdsa_sig_verification.mdx b/docs/versioned_docs/version-v0.17.0/standard_library/cryptographic_primitives/03_ecdsa_sig_verification.mdx similarity index 100% rename from docs/versioned_docs/version-0.17.0/standard_library/cryptographic_primitives/03_ecdsa_sig_verification.mdx rename to docs/versioned_docs/version-v0.17.0/standard_library/cryptographic_primitives/03_ecdsa_sig_verification.mdx diff --git a/docs/versioned_docs/version-0.17.0/standard_library/cryptographic_primitives/04_ec_primitives.md b/docs/versioned_docs/version-v0.17.0/standard_library/cryptographic_primitives/04_ec_primitives.md similarity index 100% rename from docs/versioned_docs/version-0.17.0/standard_library/cryptographic_primitives/04_ec_primitives.md rename to docs/versioned_docs/version-v0.17.0/standard_library/cryptographic_primitives/04_ec_primitives.md diff --git a/docs/versioned_docs/version-0.17.0/standard_library/cryptographic_primitives/05_eddsa.mdx b/docs/versioned_docs/version-v0.17.0/standard_library/cryptographic_primitives/05_eddsa.mdx similarity index 100% rename from docs/versioned_docs/version-0.17.0/standard_library/cryptographic_primitives/05_eddsa.mdx rename to docs/versioned_docs/version-v0.17.0/standard_library/cryptographic_primitives/05_eddsa.mdx diff --git a/docs/versioned_docs/version-0.17.0/standard_library/logging.md b/docs/versioned_docs/version-v0.17.0/standard_library/logging.md similarity index 100% rename from docs/versioned_docs/version-0.17.0/standard_library/logging.md rename to docs/versioned_docs/version-v0.17.0/standard_library/logging.md diff --git a/docs/versioned_docs/version-0.17.0/standard_library/merkle_trees.md b/docs/versioned_docs/version-v0.17.0/standard_library/merkle_trees.md similarity index 100% rename from docs/versioned_docs/version-0.17.0/standard_library/merkle_trees.md rename to docs/versioned_docs/version-v0.17.0/standard_library/merkle_trees.md diff --git a/docs/versioned_docs/version-0.17.0/standard_library/options.md b/docs/versioned_docs/version-v0.17.0/standard_library/options.md similarity index 100% rename from docs/versioned_docs/version-0.17.0/standard_library/options.md rename to docs/versioned_docs/version-v0.17.0/standard_library/options.md diff --git a/docs/versioned_docs/version-0.17.0/standard_library/recursion.md b/docs/versioned_docs/version-v0.17.0/standard_library/recursion.md similarity index 100% rename from docs/versioned_docs/version-0.17.0/standard_library/recursion.md rename to docs/versioned_docs/version-v0.17.0/standard_library/recursion.md diff --git a/docs/versioned_docs/version-0.17.0/standard_library/zeroed.md b/docs/versioned_docs/version-v0.17.0/standard_library/zeroed.md similarity index 100% rename from docs/versioned_docs/version-0.17.0/standard_library/zeroed.md rename to docs/versioned_docs/version-v0.17.0/standard_library/zeroed.md diff --git a/docs/versioned_docs/version-v0.19.0/examples/merkle-proof.mdx b/docs/versioned_docs/version-v0.19.0/examples/merkle-proof.mdx new file mode 100644 index 00000000000..832fb4bb55e --- /dev/null +++ b/docs/versioned_docs/version-v0.19.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_hash` +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-v0.19.0/getting_started/00_nargo_installation.md b/docs/versioned_docs/version-v0.19.0/getting_started/00_nargo_installation.md new file mode 100644 index 00000000000..725c5f4d373 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.0/getting_started/00_nargo_installation.md @@ -0,0 +1,249 @@ +--- +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: Compile from Source](#option-3-compile-from-source) +- [Option 4: WSL for Windows](#option-4-wsl-for-windows) + +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 +``` + +##### 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 `rust-toolchain.toml`, which is 1.71.1 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! + +### Option 4: WSL (for Windows) + +The default backend for Noir (Barretenberg) doesn't provide Windows binaries at this time. For that reason, Noir cannot be installed nativerly. However, it is available by using Windows Subsystem for Linux (WSL). + +Step 1: Follow the instructions [here](https://learn.microsoft.com/en-us/windows/wsl/install) to install and run WSL. + +step 2: Follow the [Noirup instructions](#option-1-noirup). + +## 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-v0.19.0/getting_started/01_hello_world.md b/docs/versioned_docs/version-v0.19.0/getting_started/01_hello_world.md new file mode 100644 index 00000000000..8b4416beba1 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.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-v0.19.0/getting_started/02_breakdown.md b/docs/versioned_docs/version-v0.19.0/getting_started/02_breakdown.md new file mode 100644 index 00000000000..9a17f5d6360 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.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` - specifies the version of the compiler to use. This is enforced by the compiler and follow's [Rust's versioning](https://doc.rust-lang.org/cargo/reference/manifest.html#the-version-field), so a `compiler_version = 0.18.0` will enforce Nargo version 0.18.0, `compiler_version = ^0.18.0` will enforce anything above 0.18.0 but below 0.19.0, etc. For more information, see how [Rust handles these operators](https://docs.rs/semver/latest/semver/enum.Op.html) +- `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-v0.19.0/index.md b/docs/versioned_docs/version-v0.19.0/index.md new file mode 100644 index 00000000000..75e1abf2932 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.0/index.md @@ -0,0 +1,100 @@ +--- +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 Commitment +- Pedersen Hash +- 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-v0.19.0/language_concepts/01_functions.md b/docs/versioned_docs/version-v0.19.0/language_concepts/01_functions.md new file mode 100644 index 00000000000..5eb22170e54 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.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 : 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 : 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-v0.19.0/language_concepts/02_control_flow.md b/docs/versioned_docs/version-v0.19.0/language_concepts/02_control_flow.md new file mode 100644 index 00000000000..a7f85360197 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.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.7.1/language_concepts/03_ops.md b/docs/versioned_docs/version-v0.19.0/language_concepts/03_ops.md similarity index 100% rename from docs/versioned_docs/version-0.7.1/language_concepts/03_ops.md rename to docs/versioned_docs/version-v0.19.0/language_concepts/03_ops.md diff --git a/docs/versioned_docs/version-v0.19.0/language_concepts/04_assert.md b/docs/versioned_docs/version-v0.19.0/language_concepts/04_assert.md new file mode 100644 index 00000000000..7427ec6cc63 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.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.9.0/language_concepts/05_unconstrained.md b/docs/versioned_docs/version-v0.19.0/language_concepts/05_unconstrained.md similarity index 100% rename from docs/versioned_docs/version-0.9.0/language_concepts/05_unconstrained.md rename to docs/versioned_docs/version-v0.19.0/language_concepts/05_unconstrained.md diff --git a/docs/versioned_docs/version-0.10.5/language_concepts/06_generics.md b/docs/versioned_docs/version-v0.19.0/language_concepts/06_generics.md similarity index 100% rename from docs/versioned_docs/version-0.10.5/language_concepts/06_generics.md rename to docs/versioned_docs/version-v0.19.0/language_concepts/06_generics.md diff --git a/docs/versioned_docs/version-v0.19.0/language_concepts/07_mutability.md b/docs/versioned_docs/version-v0.19.0/language_concepts/07_mutability.md new file mode 100644 index 00000000000..ad902c42c9b --- /dev/null +++ b/docs/versioned_docs/version-v0.19.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() -> pub 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-v0.19.0/language_concepts/08_lambdas.md b/docs/versioned_docs/version-v0.19.0/language_concepts/08_lambdas.md new file mode 100644 index 00000000000..ae1e6aecab1 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.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.9.0/language_concepts/08_comments.md b/docs/versioned_docs/version-v0.19.0/language_concepts/09_comments.md similarity index 100% rename from docs/versioned_docs/version-0.9.0/language_concepts/08_comments.md rename to docs/versioned_docs/version-v0.19.0/language_concepts/09_comments.md diff --git a/docs/versioned_docs/version-0.9.0/language_concepts/09_distinct.md b/docs/versioned_docs/version-v0.19.0/language_concepts/10_distinct.md similarity index 100% rename from docs/versioned_docs/version-0.9.0/language_concepts/09_distinct.md rename to docs/versioned_docs/version-v0.19.0/language_concepts/10_distinct.md diff --git a/docs/versioned_docs/version-0.9.0/language_concepts/10_shadowing.md b/docs/versioned_docs/version-v0.19.0/language_concepts/11_shadowing.md similarity index 100% rename from docs/versioned_docs/version-0.9.0/language_concepts/10_shadowing.md rename to docs/versioned_docs/version-v0.19.0/language_concepts/11_shadowing.md diff --git a/docs/versioned_docs/version-v0.19.0/language_concepts/data_types.md b/docs/versioned_docs/version-v0.19.0/language_concepts/data_types.md new file mode 100644 index 00000000000..d546cc463a8 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.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-v0.19.0/language_concepts/data_types/00_fields.md b/docs/versioned_docs/version-v0.19.0/language_concepts/data_types/00_fields.md new file mode 100644 index 00000000000..658a0441ffb --- /dev/null +++ b/docs/versioned_docs/version-v0.19.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-v0.19.0/language_concepts/data_types/01_integers.md b/docs/versioned_docs/version-v0.19.0/language_concepts/data_types/01_integers.md new file mode 100644 index 00000000000..b1e7ad11bfd --- /dev/null +++ b/docs/versioned_docs/version-v0.19.0/language_concepts/data_types/01_integers.md @@ -0,0 +1,112 @@ +--- +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 supports arbitrarily-sized, both unsigned and signed integer types. + +:::info + +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. + +::: + +## Unsigned Integers + +An unsigned integer type is specified first with the letter `u` (indicating its unsigned nature) followed by its bit size (e.g. `8`): + +```rust +fn main() { + let x: u8 = 1; + let y: u8 = 1; + let z = x + y; + assert (z == 2); +} +``` + +The bit size determines the maximum value the integer type can store. For example, a `u8` variable can store a value in the range of 0 to 255 (i.e. $\\2^{8}-1\\$). + +## Signed Integers + +A signed integer type is specified first with the letter `i` (which stands for integer) followed by its bit size (e.g. `8`): + +```rust +fn main() { + let x: i8 = -1; + let y: i8 = -1; + let z = x + y; + assert (z == -2); +} +``` + +The bit size determines the maximum and minimum range of value the integer type can store. For example, an `i8` variable can store a value in the range of -128 to 127 (i.e. $\\-2^{7}\\$ to $\\2^{7}-1\\$). + +:::tip + +If you are using the default proving backend with Noir, both even (e.g. _u2_, _i2_) and odd (e.g. _u3_, _i3_) arbitrarily-sized integer types up to 127 bits (i.e. _u127_ and _i127_) are supported. + +::: + +## Overflows + +Computations that exceed the type boundaries will result in overflow errors. This happens with both signed and unsigned integers. For example, attempting to prove: + +```rust +fn main(x: u8, y: u8) { + let z = x + y; +} +``` + +With: + +```toml +x = "255" +y = "1" +``` + +Would result in: + +``` +$ nargo prove +error: Assertion failed: 'attempt to add with overflow' +┌─ ~/src/main.nr:9:13 +│ +│ let z = x + y; +│ ----- +│ += Call stack: + ... +``` + +A similar error would happen with signed integers: + +```rust +fn main() { + let x: i8 = -118; + let y: i8 = -11; + let z = x + y; +} +``` + +### Wrapping methods + +Although integer overflow is expected to error, some use-cases rely on wrapping. For these use-cases, the standard library provides `wrapping` variants of certain common operations: + +```rust +fn wrapping_add(x: T, y: T) -> T; +fn wrapping_sub(x: T, y: T) -> T; +fn wrapping_mul(x: T, y: T) -> T; +``` + +Example of how it is used: + +```rust +use dep::std; + +fn main(x: u8, y: u8) -> pub u8 { + std::wrapping_add(x + y) +} +``` diff --git a/docs/versioned_docs/version-v0.19.0/language_concepts/data_types/02_booleans.md b/docs/versioned_docs/version-v0.19.0/language_concepts/data_types/02_booleans.md new file mode 100644 index 00000000000..885db167d83 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.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-v0.19.0/language_concepts/data_types/03_strings.md b/docs/versioned_docs/version-v0.19.0/language_concepts/data_types/03_strings.md new file mode 100644 index 00000000000..c42f34ec3ad --- /dev/null +++ b/docs/versioned_docs/version-v0.19.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-v0.19.0/language_concepts/data_types/04_arrays.md b/docs/versioned_docs/version-v0.19.0/language_concepts/data_types/04_arrays.md new file mode 100644 index 00000000000..bdbd1798bef --- /dev/null +++ b/docs/versioned_docs/version-v0.19.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-v0.19.0/language_concepts/data_types/05_slices.mdx b/docs/versioned_docs/version-v0.19.0/language_concepts/data_types/05_slices.mdx new file mode 100644 index 00000000000..1be0ec4a137 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.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 '@site/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-v0.19.0/language_concepts/data_types/06_vectors.mdx b/docs/versioned_docs/version-v0.19.0/language_concepts/data_types/06_vectors.mdx new file mode 100644 index 00000000000..4617e90d038 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.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 '@site/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-v0.19.0/language_concepts/data_types/07_tuples.md b/docs/versioned_docs/version-v0.19.0/language_concepts/data_types/07_tuples.md new file mode 100644 index 00000000000..5f6cab974a8 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.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-v0.19.0/language_concepts/data_types/08_structs.md b/docs/versioned_docs/version-v0.19.0/language_concepts/data_types/08_structs.md new file mode 100644 index 00000000000..85649dfb389 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.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-v0.19.0/language_concepts/data_types/09_references.md b/docs/versioned_docs/version-v0.19.0/language_concepts/data_types/09_references.md new file mode 100644 index 00000000000..b0c35ce2cb9 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.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-v0.19.0/language_concepts/data_types/10_function_types.md b/docs/versioned_docs/version-v0.19.0/language_concepts/data_types/10_function_types.md new file mode 100644 index 00000000000..1ec92efd594 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.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-v0.19.0/migration_notes.md b/docs/versioned_docs/version-v0.19.0/migration_notes.md new file mode 100644 index 00000000000..e87eb1feaba --- /dev/null +++ b/docs/versioned_docs/version-v0.19.0/migration_notes.md @@ -0,0 +1,91 @@ +--- +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.19 + +### Enforcing `compiler_version` + +From this version on, the compiler will check for the `compiler_version` field in `Nargo.toml`, and will error if it doesn't match the current Nargo version in use. + +To update, please make sure this field in `Nargo.toml` matches the output of `nargo --version`. + +## ≥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-v0.19.0/modules_packages_crates/crates_and_packages.md b/docs/versioned_docs/version-v0.19.0/modules_packages_crates/crates_and_packages.md new file mode 100644 index 00000000000..fb83a33d94e --- /dev/null +++ b/docs/versioned_docs/version-v0.19.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-v0.19.0/modules_packages_crates/dependencies.md b/docs/versioned_docs/version-v0.19.0/modules_packages_crates/dependencies.md new file mode 100644 index 00000000000..75f95aaa305 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.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-v0.19.0/modules_packages_crates/modules.md b/docs/versioned_docs/version-v0.19.0/modules_packages_crates/modules.md new file mode 100644 index 00000000000..147c9b284e8 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.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-v0.19.0/modules_packages_crates/workspaces.md b/docs/versioned_docs/version-v0.19.0/modules_packages_crates/workspaces.md new file mode 100644 index 00000000000..d9ac92667c9 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.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-v0.19.0/nargo/01_commands.md b/docs/versioned_docs/version-v0.19.0/nargo/01_commands.md new file mode 100644 index 00000000000..65e2bdb44e3 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.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-v0.19.0/nargo/02_testing.md b/docs/versioned_docs/version-v0.19.0/nargo/02_testing.md new file mode 100644 index 00000000000..da767274efd --- /dev/null +++ b/docs/versioned_docs/version-v0.19.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.7.1/nargo/03_solidity_verifier.md b/docs/versioned_docs/version-v0.19.0/nargo/03_solidity_verifier.md similarity index 100% rename from docs/versioned_docs/version-0.7.1/nargo/03_solidity_verifier.md rename to docs/versioned_docs/version-v0.19.0/nargo/03_solidity_verifier.md diff --git a/docs/versioned_docs/version-v0.19.0/nargo/04_language_server.md b/docs/versioned_docs/version-v0.19.0/nargo/04_language_server.md new file mode 100644 index 00000000000..48c01465f6e --- /dev/null +++ b/docs/versioned_docs/version-v0.19.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](@site/static/img/codelens_compile_execute.png) +![Run test](@site/static/img/codelens_run_test.png) + +You should also see your tests in the `testing` panel: + +![Testing panel](@site/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-v0.19.0/noir_js/getting_started/01_tiny_noir_app.md b/docs/versioned_docs/version-v0.19.0/noir_js/getting_started/01_tiny_noir_app.md new file mode 100644 index 00000000000..c51ed61de52 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.0/noir_js/getting_started/01_tiny_noir_app.md @@ -0,0 +1,260 @@ +--- +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. + +You can find the complete app code for this guide [here](https://github.com/noir-lang/tiny-noirjs-app). + +## 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](@site/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! + +You can find the complete app code for this guide [here](https://github.com/noir-lang/tiny-noirjs-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-v0.19.0/noir_js/noir_js.md b/docs/versioned_docs/version-v0.19.0/noir_js/noir_js.md new file mode 100644 index 00000000000..23ea550e156 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.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/docs/noir_js/reference/01_noirjs.md b/docs/versioned_docs/version-v0.19.0/noir_js/reference/01_noirjs.md similarity index 100% rename from docs/docs/noir_js/reference/01_noirjs.md rename to docs/versioned_docs/version-v0.19.0/noir_js/reference/01_noirjs.md diff --git a/docs/versioned_docs/version-0.17.0/noir_js/reference/02_bb_backend.md b/docs/versioned_docs/version-v0.19.0/noir_js/reference/02_bb_backend.md similarity index 100% rename from docs/versioned_docs/version-0.17.0/noir_js/reference/02_bb_backend.md rename to docs/versioned_docs/version-v0.19.0/noir_js/reference/02_bb_backend.md diff --git a/docs/versioned_docs/version-v0.19.0/noir_js/reference/backend_barretenberg/.nojekyll b/docs/versioned_docs/version-v0.19.0/noir_js/reference/backend_barretenberg/.nojekyll new file mode 100644 index 00000000000..e2ac6616add --- /dev/null +++ b/docs/versioned_docs/version-v0.19.0/noir_js/reference/backend_barretenberg/.nojekyll @@ -0,0 +1 @@ +TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.19.0/noir_js/reference/backend_barretenberg/classes/BarretenbergBackend.md b/docs/versioned_docs/version-v0.19.0/noir_js/reference/backend_barretenberg/classes/BarretenbergBackend.md new file mode 100644 index 00000000000..5cbe9421b92 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.0/noir_js/reference/backend_barretenberg/classes/BarretenbergBackend.md @@ -0,0 +1,185 @@ +# BarretenbergBackend + +## Implements + +- [`Backend`](../interfaces/Backend.md) + +## Constructors + +### new BarretenbergBackend(acirCircuit, options) + +```ts +new BarretenbergBackend(acirCircuit, options): BarretenbergBackend +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `acirCircuit` | [`CompiledCircuit`](../type-aliases/CompiledCircuit.md) | +| `options` | [`BackendOptions`](../type-aliases/BackendOptions.md) | + +#### Returns + +[`BarretenbergBackend`](BarretenbergBackend.md) + +## Methods + +### destroy() + +```ts +destroy(): Promise +``` + +#### Returns + +`Promise`\<`void`\> + +#### Implementation of + +[`Backend`](../interfaces/Backend.md).[`destroy`](../interfaces/Backend.md#destroy) + +#### Description + +Destroys the backend + +*** + +### generateFinalProof() + +```ts +generateFinalProof(decompressedWitness): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `decompressedWitness` | `Uint8Array` | + +#### Returns + +`Promise`\<[`ProofData`](../type-aliases/ProofData.md)\> + +#### Implementation of + +[`Backend`](../interfaces/Backend.md).[`generateFinalProof`](../interfaces/Backend.md#generatefinalproof) + +#### Description + +Generates a final proof (not meant to be verified in another circuit) + +*** + +### generateIntermediateProof() + +```ts +generateIntermediateProof(witness): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `witness` | `Uint8Array` | + +#### Returns + +`Promise`\<[`ProofData`](../type-aliases/ProofData.md)\> + +#### Implementation of + +[`Backend`](../interfaces/Backend.md).[`generateIntermediateProof`](../interfaces/Backend.md#generateintermediateproof) + +#### Example + +```typescript +const intermediateProof = await backend.generateIntermediateProof(witness); +``` + +*** + +### generateIntermediateProofArtifacts() + +```ts +generateIntermediateProofArtifacts(proofData, numOfPublicInputs): Promise +``` + +#### Parameters + +| Parameter | Type | Default value | +| :------ | :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | `undefined` | +| `numOfPublicInputs` | `number` | `0` | + +#### Returns + +`Promise`\<`object`\> + +#### Implementation of + +[`Backend`](../interfaces/Backend.md).[`generateIntermediateProofArtifacts`](../interfaces/Backend.md#generateintermediateproofartifacts) + +#### Example + +```typescript +const artifacts = await backend.generateIntermediateProofArtifacts(proof, numOfPublicInputs); +``` + +*** + +### verifyFinalProof() + +```ts +verifyFinalProof(proofData): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | + +#### Returns + +`Promise`\<`boolean`\> + +#### Implementation of + +[`Backend`](../interfaces/Backend.md).[`verifyFinalProof`](../interfaces/Backend.md#verifyfinalproof) + +#### Description + +Verifies a final proof + +*** + +### verifyIntermediateProof() + +```ts +verifyIntermediateProof(proofData): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | + +#### Returns + +`Promise`\<`boolean`\> + +#### Implementation of + +[`Backend`](../interfaces/Backend.md).[`verifyIntermediateProof`](../interfaces/Backend.md#verifyintermediateproof) + +#### Example + +```typescript +const isValidIntermediate = await backend.verifyIntermediateProof(proof); +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.0/noir_js/reference/backend_barretenberg/index.md b/docs/versioned_docs/version-v0.19.0/noir_js/reference/backend_barretenberg/index.md new file mode 100644 index 00000000000..3680ba3ca77 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.0/noir_js/reference/backend_barretenberg/index.md @@ -0,0 +1,27 @@ +# Backend Barretenberg + +## Exports + +### Classes + +| Class | Description | +| :------ | :------ | +| [BarretenbergBackend](classes/BarretenbergBackend.md) | - | + +### Interfaces + +| Interface | Description | +| :------ | :------ | +| [Backend](interfaces/Backend.md) | - | + +### Type Aliases + +| Type alias | Description | +| :------ | :------ | +| [BackendOptions](type-aliases/BackendOptions.md) | - | +| [CompiledCircuit](type-aliases/CompiledCircuit.md) | - | +| [ProofData](type-aliases/ProofData.md) | - | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.0/noir_js/reference/backend_barretenberg/interfaces/Backend.md b/docs/versioned_docs/version-v0.19.0/noir_js/reference/backend_barretenberg/interfaces/Backend.md new file mode 100644 index 00000000000..3eb9645c8d2 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.0/noir_js/reference/backend_barretenberg/interfaces/Backend.md @@ -0,0 +1,132 @@ +# Backend + +## Methods + +### destroy() + +```ts +destroy(): Promise +``` + +#### Returns + +`Promise`\<`void`\> + +#### Description + +Destroys the backend + +*** + +### generateFinalProof() + +```ts +generateFinalProof(decompressedWitness): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `decompressedWitness` | `Uint8Array` | + +#### Returns + +`Promise`\<[`ProofData`](../type-aliases/ProofData.md)\> + +#### Description + +Generates a final proof (not meant to be verified in another circuit) + +*** + +### generateIntermediateProof() + +```ts +generateIntermediateProof(decompressedWitness): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `decompressedWitness` | `Uint8Array` | + +#### Returns + +`Promise`\<[`ProofData`](../type-aliases/ProofData.md)\> + +#### Description + +Generates an intermediate proof (meant to be verified in another circuit) + +*** + +### generateIntermediateProofArtifacts() + +```ts +generateIntermediateProofArtifacts(proofData, numOfPublicInputs): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | +| `numOfPublicInputs` | `number` | + +#### Returns + +`Promise`\<`object`\> + +#### Description + +Retrieves the artifacts from a proof in the Field format + +*** + +### verifyFinalProof() + +```ts +verifyFinalProof(proofData): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | + +#### Returns + +`Promise`\<`boolean`\> + +#### Description + +Verifies a final proof + +*** + +### verifyIntermediateProof() + +```ts +verifyIntermediateProof(proofData): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | + +#### Returns + +`Promise`\<`boolean`\> + +#### Description + +Verifies an intermediate proof + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.0/noir_js/reference/backend_barretenberg/type-aliases/BackendOptions.md b/docs/versioned_docs/version-v0.19.0/noir_js/reference/backend_barretenberg/type-aliases/BackendOptions.md new file mode 100644 index 00000000000..266ade75d17 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.0/noir_js/reference/backend_barretenberg/type-aliases/BackendOptions.md @@ -0,0 +1,19 @@ +# BackendOptions + +```ts +type BackendOptions: object; +``` + +## Description + +An options object, currently only used to specify the number of threads to use. + +## Type declaration + +| Member | Type | Description | +| :------ | :------ | :------ | +| `threads` | `number` | **Description**

Number of threads | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.0/noir_js/reference/backend_barretenberg/type-aliases/CompiledCircuit.md b/docs/versioned_docs/version-v0.19.0/noir_js/reference/backend_barretenberg/type-aliases/CompiledCircuit.md new file mode 100644 index 00000000000..34e0dd04205 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.0/noir_js/reference/backend_barretenberg/type-aliases/CompiledCircuit.md @@ -0,0 +1,20 @@ +# CompiledCircuit + +```ts +type CompiledCircuit: object; +``` + +## Description + +The representation of a compiled circuit + +## Type declaration + +| Member | Type | Description | +| :------ | :------ | :------ | +| `abi` | `Abi` | **Description**

ABI representation of the circuit | +| `bytecode` | `string` | **Description**

The bytecode of the circuit | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.0/noir_js/reference/backend_barretenberg/type-aliases/ProofData.md b/docs/versioned_docs/version-v0.19.0/noir_js/reference/backend_barretenberg/type-aliases/ProofData.md new file mode 100644 index 00000000000..4aeff73d3e4 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.0/noir_js/reference/backend_barretenberg/type-aliases/ProofData.md @@ -0,0 +1,20 @@ +# ProofData + +```ts +type ProofData: object; +``` + +## Description + +The representation of a proof + +## Type declaration + +| Member | Type | Description | +| :------ | :------ | :------ | +| `proof` | `Uint8Array` | **Description**

An byte array representing the proof | +| `publicInputs` | `Uint8Array`[] | **Description**

Public inputs of a proof | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.0/noir_js/reference/backend_barretenberg/typedoc-sidebar.cjs b/docs/versioned_docs/version-v0.19.0/noir_js/reference/backend_barretenberg/typedoc-sidebar.cjs new file mode 100644 index 00000000000..04e662c845f --- /dev/null +++ b/docs/versioned_docs/version-v0.19.0/noir_js/reference/backend_barretenberg/typedoc-sidebar.cjs @@ -0,0 +1,4 @@ +// @ts-check +/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ +const typedocSidebar = { items: [{"type":"category","label":"Classes","items":[{"type":"doc","id":"noir_js/reference/backend_barretenberg/classes/BarretenbergBackend","label":"BarretenbergBackend"}]},{"type":"category","label":"Interfaces","items":[{"type":"doc","id":"noir_js/reference/backend_barretenberg/interfaces/Backend","label":"Backend"}]},{"type":"category","label":"Type Aliases","items":[{"type":"doc","id":"noir_js/reference/backend_barretenberg/type-aliases/BackendOptions","label":"BackendOptions"},{"type":"doc","id":"noir_js/reference/backend_barretenberg/type-aliases/CompiledCircuit","label":"CompiledCircuit"},{"type":"doc","id":"noir_js/reference/backend_barretenberg/type-aliases/ProofData","label":"ProofData"}]}]}; +module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.19.0/noir_js/reference/noir_js/.nojekyll b/docs/versioned_docs/version-v0.19.0/noir_js/reference/noir_js/.nojekyll new file mode 100644 index 00000000000..e2ac6616add --- /dev/null +++ b/docs/versioned_docs/version-v0.19.0/noir_js/reference/noir_js/.nojekyll @@ -0,0 +1 @@ +TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.19.0/noir_js/reference/noir_js/classes/Noir.md b/docs/versioned_docs/version-v0.19.0/noir_js/reference/noir_js/classes/Noir.md new file mode 100644 index 00000000000..a8a0bb451c1 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.0/noir_js/reference/noir_js/classes/Noir.md @@ -0,0 +1,131 @@ +# Noir + +## Constructors + +### new Noir(circuit, backend) + +```ts +new Noir(circuit, backend?): Noir +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `circuit` | [`CompiledCircuit`](../type-aliases/CompiledCircuit.md) | +| `backend`? | `Backend` | + +#### Returns + +[`Noir`](Noir.md) + +## Methods + +### destroy() + +```ts +destroy(): Promise +``` + +#### Returns + +`Promise`\<`void`\> + +#### Description + +Destroys the underlying backend instance. + +#### Example + +```typescript +await noir.destroy(); +``` + +*** + +### execute() + +```ts +execute(inputs, foreignCallHandler?): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `inputs` | `InputMap` | +| `foreignCallHandler`? | [`ForeignCallHandler`](../type-aliases/ForeignCallHandler.md) | + +#### Returns + +`Promise`\<`object`\> + +#### Description + +Allows to execute a circuit to get its witness and return value. + +#### Example + +```typescript +async execute(inputs) +``` + +*** + +### generateFinalProof() + +```ts +generateFinalProof(inputs): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `inputs` | `InputMap` | + +#### Returns + +`Promise`\<[`ProofData`](../type-aliases/ProofData.md)\> + +#### Description + +Generates a witness and a proof given an object as input. + +#### Example + +```typescript +async generateFinalproof(input) +``` + +*** + +### verifyFinalProof() + +```ts +verifyFinalProof(proofData): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | + +#### Returns + +`Promise`\<`boolean`\> + +#### Description + +Instantiates the verification key and verifies a proof. + +#### Example + +```typescript +async verifyFinalProof(proof) +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.0/noir_js/reference/noir_js/functions/and.md b/docs/versioned_docs/version-v0.19.0/noir_js/reference/noir_js/functions/and.md new file mode 100644 index 00000000000..c783283e396 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.0/noir_js/reference/noir_js/functions/and.md @@ -0,0 +1,22 @@ +# and() + +```ts +and(lhs, rhs): string +``` + +Performs a bitwise AND operation between `lhs` and `rhs` + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `lhs` | `string` | | +| `rhs` | `string` | | + +## Returns + +`string` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.0/noir_js/reference/noir_js/functions/blake2s256.md b/docs/versioned_docs/version-v0.19.0/noir_js/reference/noir_js/functions/blake2s256.md new file mode 100644 index 00000000000..7882d0da8d5 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.0/noir_js/reference/noir_js/functions/blake2s256.md @@ -0,0 +1,21 @@ +# blake2s256() + +```ts +blake2s256(inputs): Uint8Array +``` + +Calculates the Blake2s256 hash of the input bytes + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `inputs` | `Uint8Array` | | + +## Returns + +`Uint8Array` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.0/noir_js/reference/noir_js/functions/ecdsa_secp256k1_verify.md b/docs/versioned_docs/version-v0.19.0/noir_js/reference/noir_js/functions/ecdsa_secp256k1_verify.md new file mode 100644 index 00000000000..0ba5783f0d5 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.0/noir_js/reference/noir_js/functions/ecdsa_secp256k1_verify.md @@ -0,0 +1,29 @@ +# ecdsa\_secp256k1\_verify() + +```ts +ecdsa_secp256k1_verify( + hashed_msg, + public_key_x_bytes, + public_key_y_bytes, + signature): boolean +``` + +Calculates the Blake2s256 hash of the input bytes and represents these as a single field element. +Verifies a ECDSA signature over the secp256k1 curve. + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `hashed_msg` | `Uint8Array` | | +| `public_key_x_bytes` | `Uint8Array` | | +| `public_key_y_bytes` | `Uint8Array` | | +| `signature` | `Uint8Array` | | + +## Returns + +`boolean` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.0/noir_js/reference/noir_js/functions/ecdsa_secp256r1_verify.md b/docs/versioned_docs/version-v0.19.0/noir_js/reference/noir_js/functions/ecdsa_secp256r1_verify.md new file mode 100644 index 00000000000..0b20ff68957 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.0/noir_js/reference/noir_js/functions/ecdsa_secp256r1_verify.md @@ -0,0 +1,28 @@ +# ecdsa\_secp256r1\_verify() + +```ts +ecdsa_secp256r1_verify( + hashed_msg, + public_key_x_bytes, + public_key_y_bytes, + signature): boolean +``` + +Verifies a ECDSA signature over the secp256r1 curve. + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `hashed_msg` | `Uint8Array` | | +| `public_key_x_bytes` | `Uint8Array` | | +| `public_key_y_bytes` | `Uint8Array` | | +| `signature` | `Uint8Array` | | + +## Returns + +`boolean` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.0/noir_js/reference/noir_js/functions/keccak256.md b/docs/versioned_docs/version-v0.19.0/noir_js/reference/noir_js/functions/keccak256.md new file mode 100644 index 00000000000..d10f155ce86 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.0/noir_js/reference/noir_js/functions/keccak256.md @@ -0,0 +1,21 @@ +# keccak256() + +```ts +keccak256(inputs): Uint8Array +``` + +Calculates the Keccak256 hash of the input bytes + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `inputs` | `Uint8Array` | | + +## Returns + +`Uint8Array` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.0/noir_js/reference/noir_js/functions/sha256.md b/docs/versioned_docs/version-v0.19.0/noir_js/reference/noir_js/functions/sha256.md new file mode 100644 index 00000000000..6ba4ecac022 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.0/noir_js/reference/noir_js/functions/sha256.md @@ -0,0 +1,21 @@ +# sha256() + +```ts +sha256(inputs): Uint8Array +``` + +Calculates the SHA256 hash of the input bytes + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `inputs` | `Uint8Array` | | + +## Returns + +`Uint8Array` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.0/noir_js/reference/noir_js/functions/xor.md b/docs/versioned_docs/version-v0.19.0/noir_js/reference/noir_js/functions/xor.md new file mode 100644 index 00000000000..8d762b895d3 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.0/noir_js/reference/noir_js/functions/xor.md @@ -0,0 +1,22 @@ +# xor() + +```ts +xor(lhs, rhs): string +``` + +Performs a bitwise XOR operation between `lhs` and `rhs` + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `lhs` | `string` | | +| `rhs` | `string` | | + +## Returns + +`string` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.0/noir_js/reference/noir_js/index.md b/docs/versioned_docs/version-v0.19.0/noir_js/reference/noir_js/index.md new file mode 100644 index 00000000000..58902c17b99 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.0/noir_js/reference/noir_js/index.md @@ -0,0 +1,36 @@ +# Noir JS + +## Exports + +### Classes + +| Class | Description | +| :------ | :------ | +| [Noir](classes/Noir.md) | - | + +### Type Aliases + +| Type alias | Description | +| :------ | :------ | +| [CompiledCircuit](type-aliases/CompiledCircuit.md) | - | +| [ForeignCallHandler](type-aliases/ForeignCallHandler.md) | A callback which performs an foreign call and returns the response. | +| [ForeignCallInput](type-aliases/ForeignCallInput.md) | - | +| [ForeignCallOutput](type-aliases/ForeignCallOutput.md) | - | +| [ProofData](type-aliases/ProofData.md) | - | +| [WitnessMap](type-aliases/WitnessMap.md) | - | + +### Functions + +| Function | Description | +| :------ | :------ | +| [and](functions/and.md) | Performs a bitwise AND operation between `lhs` and `rhs` | +| [blake2s256](functions/blake2s256.md) | Calculates the Blake2s256 hash of the input bytes | +| [ecdsa\_secp256k1\_verify](functions/ecdsa_secp256k1_verify.md) | Calculates the Blake2s256 hash of the input bytes and represents these as a single field element. | +| [ecdsa\_secp256r1\_verify](functions/ecdsa_secp256r1_verify.md) | Verifies a ECDSA signature over the secp256r1 curve. | +| [keccak256](functions/keccak256.md) | Calculates the Keccak256 hash of the input bytes | +| [sha256](functions/sha256.md) | Calculates the SHA256 hash of the input bytes | +| [xor](functions/xor.md) | Performs a bitwise XOR operation between `lhs` and `rhs` | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.0/noir_js/reference/noir_js/type-aliases/CompiledCircuit.md b/docs/versioned_docs/version-v0.19.0/noir_js/reference/noir_js/type-aliases/CompiledCircuit.md new file mode 100644 index 00000000000..34e0dd04205 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.0/noir_js/reference/noir_js/type-aliases/CompiledCircuit.md @@ -0,0 +1,20 @@ +# CompiledCircuit + +```ts +type CompiledCircuit: object; +``` + +## Description + +The representation of a compiled circuit + +## Type declaration + +| Member | Type | Description | +| :------ | :------ | :------ | +| `abi` | `Abi` | **Description**

ABI representation of the circuit | +| `bytecode` | `string` | **Description**

The bytecode of the circuit | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.0/noir_js/reference/noir_js/type-aliases/ForeignCallHandler.md b/docs/versioned_docs/version-v0.19.0/noir_js/reference/noir_js/type-aliases/ForeignCallHandler.md new file mode 100644 index 00000000000..812b8b16481 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.0/noir_js/reference/noir_js/type-aliases/ForeignCallHandler.md @@ -0,0 +1,24 @@ +# ForeignCallHandler + +```ts +type ForeignCallHandler: (name, inputs) => Promise; +``` + +A callback which performs an foreign call and returns the response. + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `name` | `string` | The identifier for the type of foreign call being performed. | +| `inputs` | [`ForeignCallInput`](ForeignCallInput.md)[] | An array of hex encoded inputs to the foreign call. | + +## Returns + +`Promise`\<[`ForeignCallOutput`](ForeignCallOutput.md)[]\> + +outputs - An array of hex encoded outputs containing the results of the foreign call. + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.0/noir_js/reference/noir_js/type-aliases/ForeignCallInput.md b/docs/versioned_docs/version-v0.19.0/noir_js/reference/noir_js/type-aliases/ForeignCallInput.md new file mode 100644 index 00000000000..dd95809186a --- /dev/null +++ b/docs/versioned_docs/version-v0.19.0/noir_js/reference/noir_js/type-aliases/ForeignCallInput.md @@ -0,0 +1,9 @@ +# ForeignCallInput + +```ts +type ForeignCallInput: string[]; +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.0/noir_js/reference/noir_js/type-aliases/ForeignCallOutput.md b/docs/versioned_docs/version-v0.19.0/noir_js/reference/noir_js/type-aliases/ForeignCallOutput.md new file mode 100644 index 00000000000..b71fb78a946 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.0/noir_js/reference/noir_js/type-aliases/ForeignCallOutput.md @@ -0,0 +1,9 @@ +# ForeignCallOutput + +```ts +type ForeignCallOutput: string | string[]; +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.0/noir_js/reference/noir_js/type-aliases/ProofData.md b/docs/versioned_docs/version-v0.19.0/noir_js/reference/noir_js/type-aliases/ProofData.md new file mode 100644 index 00000000000..4aeff73d3e4 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.0/noir_js/reference/noir_js/type-aliases/ProofData.md @@ -0,0 +1,20 @@ +# ProofData + +```ts +type ProofData: object; +``` + +## Description + +The representation of a proof + +## Type declaration + +| Member | Type | Description | +| :------ | :------ | :------ | +| `proof` | `Uint8Array` | **Description**

An byte array representing the proof | +| `publicInputs` | `Uint8Array`[] | **Description**

Public inputs of a proof | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.0/noir_js/reference/noir_js/type-aliases/WitnessMap.md b/docs/versioned_docs/version-v0.19.0/noir_js/reference/noir_js/type-aliases/WitnessMap.md new file mode 100644 index 00000000000..258c46f9d0c --- /dev/null +++ b/docs/versioned_docs/version-v0.19.0/noir_js/reference/noir_js/type-aliases/WitnessMap.md @@ -0,0 +1,9 @@ +# WitnessMap + +```ts +type WitnessMap: Map; +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.0/noir_js/reference/noir_js/typedoc-sidebar.cjs b/docs/versioned_docs/version-v0.19.0/noir_js/reference/noir_js/typedoc-sidebar.cjs new file mode 100644 index 00000000000..c18318850d0 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.0/noir_js/reference/noir_js/typedoc-sidebar.cjs @@ -0,0 +1,4 @@ +// @ts-check +/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ +const typedocSidebar = { items: [{"type":"category","label":"Classes","items":[{"type":"doc","id":"noir_js/reference/noir_js/classes/Noir","label":"Noir"}]},{"type":"category","label":"Type Aliases","items":[{"type":"doc","id":"noir_js/reference/noir_js/type-aliases/CompiledCircuit","label":"CompiledCircuit"},{"type":"doc","id":"noir_js/reference/noir_js/type-aliases/ForeignCallHandler","label":"ForeignCallHandler"},{"type":"doc","id":"noir_js/reference/noir_js/type-aliases/ForeignCallInput","label":"ForeignCallInput"},{"type":"doc","id":"noir_js/reference/noir_js/type-aliases/ForeignCallOutput","label":"ForeignCallOutput"},{"type":"doc","id":"noir_js/reference/noir_js/type-aliases/ProofData","label":"ProofData"},{"type":"doc","id":"noir_js/reference/noir_js/type-aliases/WitnessMap","label":"WitnessMap"}]},{"type":"category","label":"Functions","items":[{"type":"doc","id":"noir_js/reference/noir_js/functions/and","label":"and"},{"type":"doc","id":"noir_js/reference/noir_js/functions/blake2s256","label":"blake2s256"},{"type":"doc","id":"noir_js/reference/noir_js/functions/ecdsa_secp256k1_verify","label":"ecdsa_secp256k1_verify"},{"type":"doc","id":"noir_js/reference/noir_js/functions/ecdsa_secp256r1_verify","label":"ecdsa_secp256r1_verify"},{"type":"doc","id":"noir_js/reference/noir_js/functions/keccak256","label":"keccak256"},{"type":"doc","id":"noir_js/reference/noir_js/functions/sha256","label":"sha256"},{"type":"doc","id":"noir_js/reference/noir_js/functions/xor","label":"xor"}]}]}; +module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.19.0/standard_library/black_box_fns.md b/docs/versioned_docs/version-v0.19.0/standard_library/black_box_fns.md new file mode 100644 index 00000000000..1dfabfe8f22 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.0/standard_library/black_box_fns.md @@ -0,0 +1,46 @@ +--- +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 Hash](./cryptographic_primitives/hashes#pedersen_hash) +- [Pedersen Commitment](./cryptographic_primitives/hashes#pedersen_commitment) +- [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.6.0/standard_library/cryptographic_primitives.md b/docs/versioned_docs/version-v0.19.0/standard_library/cryptographic_primitives.md similarity index 100% rename from docs/versioned_docs/version-0.6.0/standard_library/cryptographic_primitives.md rename to docs/versioned_docs/version-v0.19.0/standard_library/cryptographic_primitives.md diff --git a/docs/versioned_docs/version-v0.19.0/standard_library/cryptographic_primitives/00_hashes.mdx b/docs/versioned_docs/version-v0.19.0/standard_library/cryptographic_primitives/00_hashes.mdx new file mode 100644 index 00000000000..76745196681 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.0/standard_library/cryptographic_primitives/00_hashes.mdx @@ -0,0 +1,167 @@ +--- +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 '@site/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_hash + +Given an array of Fields, returns the Pedersen hash. + +```rust +fn pedersen_hash(_input : [Field]) -> Field +``` + +example: + +```rust +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let hash = std::hash::pedersen_hash(x); +} +``` + + + + + +## pedersen_commitment + +Given an array of Fields, returns the Pedersen commitment. + +```rust +fn pedersen_commitment(_input : [Field]) -> [Field; 2] +``` + +example: + +```rust +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let commitment = std::hash::pedersen_commitment(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::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-v0.19.0/standard_library/cryptographic_primitives/01_scalar.mdx b/docs/versioned_docs/version-v0.19.0/standard_library/cryptographic_primitives/01_scalar.mdx new file mode 100644 index 00000000000..c7eed820a80 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.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 '@site/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-v0.19.0/standard_library/cryptographic_primitives/02_schnorr.mdx b/docs/versioned_docs/version-v0.19.0/standard_library/cryptographic_primitives/02_schnorr.mdx new file mode 100644 index 00000000000..c184ce28120 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.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 '@site/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-v0.19.0/standard_library/cryptographic_primitives/03_ecdsa_sig_verification.mdx b/docs/versioned_docs/version-v0.19.0/standard_library/cryptographic_primitives/03_ecdsa_sig_verification.mdx new file mode 100644 index 00000000000..72bce984821 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.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 '@site/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.9.0/standard_library/cryptographic_primitives/04_ec_primitives.md b/docs/versioned_docs/version-v0.19.0/standard_library/cryptographic_primitives/04_ec_primitives.md similarity index 100% rename from docs/versioned_docs/version-0.9.0/standard_library/cryptographic_primitives/04_ec_primitives.md rename to docs/versioned_docs/version-v0.19.0/standard_library/cryptographic_primitives/04_ec_primitives.md diff --git a/docs/versioned_docs/version-v0.19.0/standard_library/cryptographic_primitives/05_eddsa.mdx b/docs/versioned_docs/version-v0.19.0/standard_library/cryptographic_primitives/05_eddsa.mdx new file mode 100644 index 00000000000..9a5beb55ee9 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.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 '@site/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-v0.19.0/standard_library/logging.md b/docs/versioned_docs/version-v0.19.0/standard_library/logging.md new file mode 100644 index 00000000000..7e2fd9b9aff --- /dev/null +++ b/docs/versioned_docs/version-v0.19.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-v0.19.0/standard_library/merkle_trees.md b/docs/versioned_docs/version-v0.19.0/standard_library/merkle_trees.md new file mode 100644 index 00000000000..dc383a1426b --- /dev/null +++ b/docs/versioned_docs/version-v0.19.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_hash). + +```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-v0.19.0/standard_library/options.md b/docs/versioned_docs/version-v0.19.0/standard_library/options.md new file mode 100644 index 00000000000..3d3139fb98b --- /dev/null +++ b/docs/versioned_docs/version-v0.19.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-v0.19.0/standard_library/recursion.md b/docs/versioned_docs/version-v0.19.0/standard_library/recursion.md new file mode 100644 index 00000000000..ff4c63acaa7 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.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.6.0/standard_library/zeroed.md b/docs/versioned_docs/version-v0.19.0/standard_library/zeroed.md similarity index 100% rename from docs/versioned_docs/version-0.6.0/standard_library/zeroed.md rename to docs/versioned_docs/version-v0.19.0/standard_library/zeroed.md diff --git a/docs/versioned_docs/version-v0.19.1/examples/merkle-proof.mdx b/docs/versioned_docs/version-v0.19.1/examples/merkle-proof.mdx new file mode 100644 index 00000000000..832fb4bb55e --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/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_hash` +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-v0.19.1/getting_started/00_nargo_installation.md b/docs/versioned_docs/version-v0.19.1/getting_started/00_nargo_installation.md new file mode 100644 index 00000000000..725c5f4d373 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/getting_started/00_nargo_installation.md @@ -0,0 +1,249 @@ +--- +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: Compile from Source](#option-3-compile-from-source) +- [Option 4: WSL for Windows](#option-4-wsl-for-windows) + +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 +``` + +##### 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 `rust-toolchain.toml`, which is 1.71.1 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! + +### Option 4: WSL (for Windows) + +The default backend for Noir (Barretenberg) doesn't provide Windows binaries at this time. For that reason, Noir cannot be installed nativerly. However, it is available by using Windows Subsystem for Linux (WSL). + +Step 1: Follow the instructions [here](https://learn.microsoft.com/en-us/windows/wsl/install) to install and run WSL. + +step 2: Follow the [Noirup instructions](#option-1-noirup). + +## 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-v0.19.1/getting_started/01_hello_world.md b/docs/versioned_docs/version-v0.19.1/getting_started/01_hello_world.md new file mode 100644 index 00000000000..8b4416beba1 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/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-v0.19.1/getting_started/02_breakdown.md b/docs/versioned_docs/version-v0.19.1/getting_started/02_breakdown.md new file mode 100644 index 00000000000..9a17f5d6360 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/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` - specifies the version of the compiler to use. This is enforced by the compiler and follow's [Rust's versioning](https://doc.rust-lang.org/cargo/reference/manifest.html#the-version-field), so a `compiler_version = 0.18.0` will enforce Nargo version 0.18.0, `compiler_version = ^0.18.0` will enforce anything above 0.18.0 but below 0.19.0, etc. For more information, see how [Rust handles these operators](https://docs.rs/semver/latest/semver/enum.Op.html) +- `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-v0.19.1/index.md b/docs/versioned_docs/version-v0.19.1/index.md new file mode 100644 index 00000000000..75e1abf2932 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/index.md @@ -0,0 +1,100 @@ +--- +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 Commitment +- Pedersen Hash +- 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-v0.19.1/language_concepts/01_functions.md b/docs/versioned_docs/version-v0.19.1/language_concepts/01_functions.md new file mode 100644 index 00000000000..5eb22170e54 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/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 : 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 : 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-v0.19.1/language_concepts/02_control_flow.md b/docs/versioned_docs/version-v0.19.1/language_concepts/02_control_flow.md new file mode 100644 index 00000000000..a7f85360197 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/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.9.0/language_concepts/03_ops.md b/docs/versioned_docs/version-v0.19.1/language_concepts/03_ops.md similarity index 100% rename from docs/versioned_docs/version-0.9.0/language_concepts/03_ops.md rename to docs/versioned_docs/version-v0.19.1/language_concepts/03_ops.md diff --git a/docs/versioned_docs/version-v0.19.1/language_concepts/04_assert.md b/docs/versioned_docs/version-v0.19.1/language_concepts/04_assert.md new file mode 100644 index 00000000000..7427ec6cc63 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/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-v0.19.1/language_concepts/05_unconstrained.md b/docs/versioned_docs/version-v0.19.1/language_concepts/05_unconstrained.md new file mode 100644 index 00000000000..6b621eda3eb --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/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-v0.19.1/language_concepts/06_generics.md b/docs/versioned_docs/version-v0.19.1/language_concepts/06_generics.md new file mode 100644 index 00000000000..9fb4177c2a8 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/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/tooling/nargo_cli/tests/execution_success/generics/src/main.nr). diff --git a/docs/versioned_docs/version-v0.19.1/language_concepts/07_mutability.md b/docs/versioned_docs/version-v0.19.1/language_concepts/07_mutability.md new file mode 100644 index 00000000000..ad902c42c9b --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/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() -> pub 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-v0.19.1/language_concepts/08_lambdas.md b/docs/versioned_docs/version-v0.19.1/language_concepts/08_lambdas.md new file mode 100644 index 00000000000..ae1e6aecab1 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/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-v0.19.1/language_concepts/09_comments.md b/docs/versioned_docs/version-v0.19.1/language_concepts/09_comments.md new file mode 100644 index 00000000000..3bb4d2f25a4 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/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-v0.19.1/language_concepts/10_distinct.md b/docs/versioned_docs/version-v0.19.1/language_concepts/10_distinct.md new file mode 100644 index 00000000000..e7ff7f5017a --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/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-v0.19.1/language_concepts/11_shadowing.md b/docs/versioned_docs/version-v0.19.1/language_concepts/11_shadowing.md new file mode 100644 index 00000000000..efd743e764f --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/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-v0.19.1/language_concepts/data_types.md b/docs/versioned_docs/version-v0.19.1/language_concepts/data_types.md new file mode 100644 index 00000000000..d546cc463a8 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/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-v0.19.1/language_concepts/data_types/00_fields.md b/docs/versioned_docs/version-v0.19.1/language_concepts/data_types/00_fields.md new file mode 100644 index 00000000000..658a0441ffb --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/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-v0.19.1/language_concepts/data_types/01_integers.md b/docs/versioned_docs/version-v0.19.1/language_concepts/data_types/01_integers.md new file mode 100644 index 00000000000..b1e7ad11bfd --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/language_concepts/data_types/01_integers.md @@ -0,0 +1,112 @@ +--- +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 supports arbitrarily-sized, both unsigned and signed integer types. + +:::info + +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. + +::: + +## Unsigned Integers + +An unsigned integer type is specified first with the letter `u` (indicating its unsigned nature) followed by its bit size (e.g. `8`): + +```rust +fn main() { + let x: u8 = 1; + let y: u8 = 1; + let z = x + y; + assert (z == 2); +} +``` + +The bit size determines the maximum value the integer type can store. For example, a `u8` variable can store a value in the range of 0 to 255 (i.e. $\\2^{8}-1\\$). + +## Signed Integers + +A signed integer type is specified first with the letter `i` (which stands for integer) followed by its bit size (e.g. `8`): + +```rust +fn main() { + let x: i8 = -1; + let y: i8 = -1; + let z = x + y; + assert (z == -2); +} +``` + +The bit size determines the maximum and minimum range of value the integer type can store. For example, an `i8` variable can store a value in the range of -128 to 127 (i.e. $\\-2^{7}\\$ to $\\2^{7}-1\\$). + +:::tip + +If you are using the default proving backend with Noir, both even (e.g. _u2_, _i2_) and odd (e.g. _u3_, _i3_) arbitrarily-sized integer types up to 127 bits (i.e. _u127_ and _i127_) are supported. + +::: + +## Overflows + +Computations that exceed the type boundaries will result in overflow errors. This happens with both signed and unsigned integers. For example, attempting to prove: + +```rust +fn main(x: u8, y: u8) { + let z = x + y; +} +``` + +With: + +```toml +x = "255" +y = "1" +``` + +Would result in: + +``` +$ nargo prove +error: Assertion failed: 'attempt to add with overflow' +┌─ ~/src/main.nr:9:13 +│ +│ let z = x + y; +│ ----- +│ += Call stack: + ... +``` + +A similar error would happen with signed integers: + +```rust +fn main() { + let x: i8 = -118; + let y: i8 = -11; + let z = x + y; +} +``` + +### Wrapping methods + +Although integer overflow is expected to error, some use-cases rely on wrapping. For these use-cases, the standard library provides `wrapping` variants of certain common operations: + +```rust +fn wrapping_add(x: T, y: T) -> T; +fn wrapping_sub(x: T, y: T) -> T; +fn wrapping_mul(x: T, y: T) -> T; +``` + +Example of how it is used: + +```rust +use dep::std; + +fn main(x: u8, y: u8) -> pub u8 { + std::wrapping_add(x + y) +} +``` diff --git a/docs/versioned_docs/version-v0.19.1/language_concepts/data_types/02_booleans.md b/docs/versioned_docs/version-v0.19.1/language_concepts/data_types/02_booleans.md new file mode 100644 index 00000000000..885db167d83 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/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-v0.19.1/language_concepts/data_types/03_strings.md b/docs/versioned_docs/version-v0.19.1/language_concepts/data_types/03_strings.md new file mode 100644 index 00000000000..c42f34ec3ad --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/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-v0.19.1/language_concepts/data_types/04_arrays.md b/docs/versioned_docs/version-v0.19.1/language_concepts/data_types/04_arrays.md new file mode 100644 index 00000000000..bdbd1798bef --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/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-v0.19.1/language_concepts/data_types/05_slices.mdx b/docs/versioned_docs/version-v0.19.1/language_concepts/data_types/05_slices.mdx new file mode 100644 index 00000000000..1be0ec4a137 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/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 '@site/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-v0.19.1/language_concepts/data_types/06_vectors.mdx b/docs/versioned_docs/version-v0.19.1/language_concepts/data_types/06_vectors.mdx new file mode 100644 index 00000000000..4617e90d038 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/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 '@site/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-v0.19.1/language_concepts/data_types/07_tuples.md b/docs/versioned_docs/version-v0.19.1/language_concepts/data_types/07_tuples.md new file mode 100644 index 00000000000..5f6cab974a8 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/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-v0.19.1/language_concepts/data_types/08_structs.md b/docs/versioned_docs/version-v0.19.1/language_concepts/data_types/08_structs.md new file mode 100644 index 00000000000..35421734639 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/language_concepts/data_types/08_structs.md @@ -0,0 +1,69 @@ +--- +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. diff --git a/docs/versioned_docs/version-v0.19.1/language_concepts/data_types/09_references.md b/docs/versioned_docs/version-v0.19.1/language_concepts/data_types/09_references.md new file mode 100644 index 00000000000..b0c35ce2cb9 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/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-v0.19.1/language_concepts/data_types/10_function_types.md b/docs/versioned_docs/version-v0.19.1/language_concepts/data_types/10_function_types.md new file mode 100644 index 00000000000..1ec92efd594 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/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-v0.19.1/migration_notes.md b/docs/versioned_docs/version-v0.19.1/migration_notes.md new file mode 100644 index 00000000000..e87eb1feaba --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/migration_notes.md @@ -0,0 +1,91 @@ +--- +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.19 + +### Enforcing `compiler_version` + +From this version on, the compiler will check for the `compiler_version` field in `Nargo.toml`, and will error if it doesn't match the current Nargo version in use. + +To update, please make sure this field in `Nargo.toml` matches the output of `nargo --version`. + +## ≥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-v0.19.1/modules_packages_crates/crates_and_packages.md b/docs/versioned_docs/version-v0.19.1/modules_packages_crates/crates_and_packages.md new file mode 100644 index 00000000000..fb83a33d94e --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/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-v0.19.1/modules_packages_crates/dependencies.md b/docs/versioned_docs/version-v0.19.1/modules_packages_crates/dependencies.md new file mode 100644 index 00000000000..75f95aaa305 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/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-v0.19.1/modules_packages_crates/modules.md b/docs/versioned_docs/version-v0.19.1/modules_packages_crates/modules.md new file mode 100644 index 00000000000..147c9b284e8 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/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-v0.19.1/modules_packages_crates/workspaces.md b/docs/versioned_docs/version-v0.19.1/modules_packages_crates/workspaces.md new file mode 100644 index 00000000000..d9ac92667c9 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/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-v0.19.1/nargo/01_commands.md b/docs/versioned_docs/version-v0.19.1/nargo/01_commands.md new file mode 100644 index 00000000000..65e2bdb44e3 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/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-v0.19.1/nargo/02_testing.md b/docs/versioned_docs/version-v0.19.1/nargo/02_testing.md new file mode 100644 index 00000000000..da767274efd --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/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.9.0/nargo/03_solidity_verifier.md b/docs/versioned_docs/version-v0.19.1/nargo/03_solidity_verifier.md similarity index 100% rename from docs/versioned_docs/version-0.9.0/nargo/03_solidity_verifier.md rename to docs/versioned_docs/version-v0.19.1/nargo/03_solidity_verifier.md diff --git a/docs/versioned_docs/version-v0.19.1/nargo/04_language_server.md b/docs/versioned_docs/version-v0.19.1/nargo/04_language_server.md new file mode 100644 index 00000000000..48c01465f6e --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/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](@site/static/img/codelens_compile_execute.png) +![Run test](@site/static/img/codelens_run_test.png) + +You should also see your tests in the `testing` panel: + +![Testing panel](@site/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-v0.19.1/noir_js/getting_started/01_tiny_noir_app.md b/docs/versioned_docs/version-v0.19.1/noir_js/getting_started/01_tiny_noir_app.md new file mode 100644 index 00000000000..c51ed61de52 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/noir_js/getting_started/01_tiny_noir_app.md @@ -0,0 +1,260 @@ +--- +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. + +You can find the complete app code for this guide [here](https://github.com/noir-lang/tiny-noirjs-app). + +## 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](@site/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! + +You can find the complete app code for this guide [here](https://github.com/noir-lang/tiny-noirjs-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-v0.19.1/noir_js/noir_js.md b/docs/versioned_docs/version-v0.19.1/noir_js/noir_js.md new file mode 100644 index 00000000000..f895b22eaf8 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/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/noir_js/classes/Noir.md) sections. diff --git a/docs/versioned_docs/version-v0.19.1/noir_js/reference/backend_barretenberg/.nojekyll b/docs/versioned_docs/version-v0.19.1/noir_js/reference/backend_barretenberg/.nojekyll new file mode 100644 index 00000000000..e2ac6616add --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/noir_js/reference/backend_barretenberg/.nojekyll @@ -0,0 +1 @@ +TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.19.1/noir_js/reference/backend_barretenberg/classes/BarretenbergBackend.md b/docs/versioned_docs/version-v0.19.1/noir_js/reference/backend_barretenberg/classes/BarretenbergBackend.md new file mode 100644 index 00000000000..5cbe9421b92 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/noir_js/reference/backend_barretenberg/classes/BarretenbergBackend.md @@ -0,0 +1,185 @@ +# BarretenbergBackend + +## Implements + +- [`Backend`](../interfaces/Backend.md) + +## Constructors + +### new BarretenbergBackend(acirCircuit, options) + +```ts +new BarretenbergBackend(acirCircuit, options): BarretenbergBackend +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `acirCircuit` | [`CompiledCircuit`](../type-aliases/CompiledCircuit.md) | +| `options` | [`BackendOptions`](../type-aliases/BackendOptions.md) | + +#### Returns + +[`BarretenbergBackend`](BarretenbergBackend.md) + +## Methods + +### destroy() + +```ts +destroy(): Promise +``` + +#### Returns + +`Promise`\<`void`\> + +#### Implementation of + +[`Backend`](../interfaces/Backend.md).[`destroy`](../interfaces/Backend.md#destroy) + +#### Description + +Destroys the backend + +*** + +### generateFinalProof() + +```ts +generateFinalProof(decompressedWitness): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `decompressedWitness` | `Uint8Array` | + +#### Returns + +`Promise`\<[`ProofData`](../type-aliases/ProofData.md)\> + +#### Implementation of + +[`Backend`](../interfaces/Backend.md).[`generateFinalProof`](../interfaces/Backend.md#generatefinalproof) + +#### Description + +Generates a final proof (not meant to be verified in another circuit) + +*** + +### generateIntermediateProof() + +```ts +generateIntermediateProof(witness): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `witness` | `Uint8Array` | + +#### Returns + +`Promise`\<[`ProofData`](../type-aliases/ProofData.md)\> + +#### Implementation of + +[`Backend`](../interfaces/Backend.md).[`generateIntermediateProof`](../interfaces/Backend.md#generateintermediateproof) + +#### Example + +```typescript +const intermediateProof = await backend.generateIntermediateProof(witness); +``` + +*** + +### generateIntermediateProofArtifacts() + +```ts +generateIntermediateProofArtifacts(proofData, numOfPublicInputs): Promise +``` + +#### Parameters + +| Parameter | Type | Default value | +| :------ | :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | `undefined` | +| `numOfPublicInputs` | `number` | `0` | + +#### Returns + +`Promise`\<`object`\> + +#### Implementation of + +[`Backend`](../interfaces/Backend.md).[`generateIntermediateProofArtifacts`](../interfaces/Backend.md#generateintermediateproofartifacts) + +#### Example + +```typescript +const artifacts = await backend.generateIntermediateProofArtifacts(proof, numOfPublicInputs); +``` + +*** + +### verifyFinalProof() + +```ts +verifyFinalProof(proofData): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | + +#### Returns + +`Promise`\<`boolean`\> + +#### Implementation of + +[`Backend`](../interfaces/Backend.md).[`verifyFinalProof`](../interfaces/Backend.md#verifyfinalproof) + +#### Description + +Verifies a final proof + +*** + +### verifyIntermediateProof() + +```ts +verifyIntermediateProof(proofData): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | + +#### Returns + +`Promise`\<`boolean`\> + +#### Implementation of + +[`Backend`](../interfaces/Backend.md).[`verifyIntermediateProof`](../interfaces/Backend.md#verifyintermediateproof) + +#### Example + +```typescript +const isValidIntermediate = await backend.verifyIntermediateProof(proof); +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.1/noir_js/reference/backend_barretenberg/index.md b/docs/versioned_docs/version-v0.19.1/noir_js/reference/backend_barretenberg/index.md new file mode 100644 index 00000000000..3680ba3ca77 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/noir_js/reference/backend_barretenberg/index.md @@ -0,0 +1,27 @@ +# Backend Barretenberg + +## Exports + +### Classes + +| Class | Description | +| :------ | :------ | +| [BarretenbergBackend](classes/BarretenbergBackend.md) | - | + +### Interfaces + +| Interface | Description | +| :------ | :------ | +| [Backend](interfaces/Backend.md) | - | + +### Type Aliases + +| Type alias | Description | +| :------ | :------ | +| [BackendOptions](type-aliases/BackendOptions.md) | - | +| [CompiledCircuit](type-aliases/CompiledCircuit.md) | - | +| [ProofData](type-aliases/ProofData.md) | - | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.1/noir_js/reference/backend_barretenberg/interfaces/Backend.md b/docs/versioned_docs/version-v0.19.1/noir_js/reference/backend_barretenberg/interfaces/Backend.md new file mode 100644 index 00000000000..3eb9645c8d2 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/noir_js/reference/backend_barretenberg/interfaces/Backend.md @@ -0,0 +1,132 @@ +# Backend + +## Methods + +### destroy() + +```ts +destroy(): Promise +``` + +#### Returns + +`Promise`\<`void`\> + +#### Description + +Destroys the backend + +*** + +### generateFinalProof() + +```ts +generateFinalProof(decompressedWitness): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `decompressedWitness` | `Uint8Array` | + +#### Returns + +`Promise`\<[`ProofData`](../type-aliases/ProofData.md)\> + +#### Description + +Generates a final proof (not meant to be verified in another circuit) + +*** + +### generateIntermediateProof() + +```ts +generateIntermediateProof(decompressedWitness): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `decompressedWitness` | `Uint8Array` | + +#### Returns + +`Promise`\<[`ProofData`](../type-aliases/ProofData.md)\> + +#### Description + +Generates an intermediate proof (meant to be verified in another circuit) + +*** + +### generateIntermediateProofArtifacts() + +```ts +generateIntermediateProofArtifacts(proofData, numOfPublicInputs): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | +| `numOfPublicInputs` | `number` | + +#### Returns + +`Promise`\<`object`\> + +#### Description + +Retrieves the artifacts from a proof in the Field format + +*** + +### verifyFinalProof() + +```ts +verifyFinalProof(proofData): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | + +#### Returns + +`Promise`\<`boolean`\> + +#### Description + +Verifies a final proof + +*** + +### verifyIntermediateProof() + +```ts +verifyIntermediateProof(proofData): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | + +#### Returns + +`Promise`\<`boolean`\> + +#### Description + +Verifies an intermediate proof + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.1/noir_js/reference/backend_barretenberg/type-aliases/BackendOptions.md b/docs/versioned_docs/version-v0.19.1/noir_js/reference/backend_barretenberg/type-aliases/BackendOptions.md new file mode 100644 index 00000000000..266ade75d17 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/noir_js/reference/backend_barretenberg/type-aliases/BackendOptions.md @@ -0,0 +1,19 @@ +# BackendOptions + +```ts +type BackendOptions: object; +``` + +## Description + +An options object, currently only used to specify the number of threads to use. + +## Type declaration + +| Member | Type | Description | +| :------ | :------ | :------ | +| `threads` | `number` | **Description**

Number of threads | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.1/noir_js/reference/backend_barretenberg/type-aliases/CompiledCircuit.md b/docs/versioned_docs/version-v0.19.1/noir_js/reference/backend_barretenberg/type-aliases/CompiledCircuit.md new file mode 100644 index 00000000000..34e0dd04205 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/noir_js/reference/backend_barretenberg/type-aliases/CompiledCircuit.md @@ -0,0 +1,20 @@ +# CompiledCircuit + +```ts +type CompiledCircuit: object; +``` + +## Description + +The representation of a compiled circuit + +## Type declaration + +| Member | Type | Description | +| :------ | :------ | :------ | +| `abi` | `Abi` | **Description**

ABI representation of the circuit | +| `bytecode` | `string` | **Description**

The bytecode of the circuit | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.1/noir_js/reference/backend_barretenberg/type-aliases/ProofData.md b/docs/versioned_docs/version-v0.19.1/noir_js/reference/backend_barretenberg/type-aliases/ProofData.md new file mode 100644 index 00000000000..4aeff73d3e4 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/noir_js/reference/backend_barretenberg/type-aliases/ProofData.md @@ -0,0 +1,20 @@ +# ProofData + +```ts +type ProofData: object; +``` + +## Description + +The representation of a proof + +## Type declaration + +| Member | Type | Description | +| :------ | :------ | :------ | +| `proof` | `Uint8Array` | **Description**

An byte array representing the proof | +| `publicInputs` | `Uint8Array`[] | **Description**

Public inputs of a proof | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.1/noir_js/reference/backend_barretenberg/typedoc-sidebar.cjs b/docs/versioned_docs/version-v0.19.1/noir_js/reference/backend_barretenberg/typedoc-sidebar.cjs new file mode 100644 index 00000000000..04e662c845f --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/noir_js/reference/backend_barretenberg/typedoc-sidebar.cjs @@ -0,0 +1,4 @@ +// @ts-check +/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ +const typedocSidebar = { items: [{"type":"category","label":"Classes","items":[{"type":"doc","id":"noir_js/reference/backend_barretenberg/classes/BarretenbergBackend","label":"BarretenbergBackend"}]},{"type":"category","label":"Interfaces","items":[{"type":"doc","id":"noir_js/reference/backend_barretenberg/interfaces/Backend","label":"Backend"}]},{"type":"category","label":"Type Aliases","items":[{"type":"doc","id":"noir_js/reference/backend_barretenberg/type-aliases/BackendOptions","label":"BackendOptions"},{"type":"doc","id":"noir_js/reference/backend_barretenberg/type-aliases/CompiledCircuit","label":"CompiledCircuit"},{"type":"doc","id":"noir_js/reference/backend_barretenberg/type-aliases/ProofData","label":"ProofData"}]}]}; +module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.19.1/noir_js/reference/noir_js/.nojekyll b/docs/versioned_docs/version-v0.19.1/noir_js/reference/noir_js/.nojekyll new file mode 100644 index 00000000000..e2ac6616add --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/noir_js/reference/noir_js/.nojekyll @@ -0,0 +1 @@ +TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.19.1/noir_js/reference/noir_js/classes/Noir.md b/docs/versioned_docs/version-v0.19.1/noir_js/reference/noir_js/classes/Noir.md new file mode 100644 index 00000000000..a8a0bb451c1 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/noir_js/reference/noir_js/classes/Noir.md @@ -0,0 +1,131 @@ +# Noir + +## Constructors + +### new Noir(circuit, backend) + +```ts +new Noir(circuit, backend?): Noir +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `circuit` | [`CompiledCircuit`](../type-aliases/CompiledCircuit.md) | +| `backend`? | `Backend` | + +#### Returns + +[`Noir`](Noir.md) + +## Methods + +### destroy() + +```ts +destroy(): Promise +``` + +#### Returns + +`Promise`\<`void`\> + +#### Description + +Destroys the underlying backend instance. + +#### Example + +```typescript +await noir.destroy(); +``` + +*** + +### execute() + +```ts +execute(inputs, foreignCallHandler?): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `inputs` | `InputMap` | +| `foreignCallHandler`? | [`ForeignCallHandler`](../type-aliases/ForeignCallHandler.md) | + +#### Returns + +`Promise`\<`object`\> + +#### Description + +Allows to execute a circuit to get its witness and return value. + +#### Example + +```typescript +async execute(inputs) +``` + +*** + +### generateFinalProof() + +```ts +generateFinalProof(inputs): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `inputs` | `InputMap` | + +#### Returns + +`Promise`\<[`ProofData`](../type-aliases/ProofData.md)\> + +#### Description + +Generates a witness and a proof given an object as input. + +#### Example + +```typescript +async generateFinalproof(input) +``` + +*** + +### verifyFinalProof() + +```ts +verifyFinalProof(proofData): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | + +#### Returns + +`Promise`\<`boolean`\> + +#### Description + +Instantiates the verification key and verifies a proof. + +#### Example + +```typescript +async verifyFinalProof(proof) +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.1/noir_js/reference/noir_js/functions/and.md b/docs/versioned_docs/version-v0.19.1/noir_js/reference/noir_js/functions/and.md new file mode 100644 index 00000000000..c783283e396 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/noir_js/reference/noir_js/functions/and.md @@ -0,0 +1,22 @@ +# and() + +```ts +and(lhs, rhs): string +``` + +Performs a bitwise AND operation between `lhs` and `rhs` + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `lhs` | `string` | | +| `rhs` | `string` | | + +## Returns + +`string` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.1/noir_js/reference/noir_js/functions/blake2s256.md b/docs/versioned_docs/version-v0.19.1/noir_js/reference/noir_js/functions/blake2s256.md new file mode 100644 index 00000000000..7882d0da8d5 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/noir_js/reference/noir_js/functions/blake2s256.md @@ -0,0 +1,21 @@ +# blake2s256() + +```ts +blake2s256(inputs): Uint8Array +``` + +Calculates the Blake2s256 hash of the input bytes + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `inputs` | `Uint8Array` | | + +## Returns + +`Uint8Array` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.1/noir_js/reference/noir_js/functions/ecdsa_secp256k1_verify.md b/docs/versioned_docs/version-v0.19.1/noir_js/reference/noir_js/functions/ecdsa_secp256k1_verify.md new file mode 100644 index 00000000000..0ba5783f0d5 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/noir_js/reference/noir_js/functions/ecdsa_secp256k1_verify.md @@ -0,0 +1,29 @@ +# ecdsa\_secp256k1\_verify() + +```ts +ecdsa_secp256k1_verify( + hashed_msg, + public_key_x_bytes, + public_key_y_bytes, + signature): boolean +``` + +Calculates the Blake2s256 hash of the input bytes and represents these as a single field element. +Verifies a ECDSA signature over the secp256k1 curve. + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `hashed_msg` | `Uint8Array` | | +| `public_key_x_bytes` | `Uint8Array` | | +| `public_key_y_bytes` | `Uint8Array` | | +| `signature` | `Uint8Array` | | + +## Returns + +`boolean` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.1/noir_js/reference/noir_js/functions/ecdsa_secp256r1_verify.md b/docs/versioned_docs/version-v0.19.1/noir_js/reference/noir_js/functions/ecdsa_secp256r1_verify.md new file mode 100644 index 00000000000..0b20ff68957 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/noir_js/reference/noir_js/functions/ecdsa_secp256r1_verify.md @@ -0,0 +1,28 @@ +# ecdsa\_secp256r1\_verify() + +```ts +ecdsa_secp256r1_verify( + hashed_msg, + public_key_x_bytes, + public_key_y_bytes, + signature): boolean +``` + +Verifies a ECDSA signature over the secp256r1 curve. + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `hashed_msg` | `Uint8Array` | | +| `public_key_x_bytes` | `Uint8Array` | | +| `public_key_y_bytes` | `Uint8Array` | | +| `signature` | `Uint8Array` | | + +## Returns + +`boolean` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.1/noir_js/reference/noir_js/functions/keccak256.md b/docs/versioned_docs/version-v0.19.1/noir_js/reference/noir_js/functions/keccak256.md new file mode 100644 index 00000000000..d10f155ce86 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/noir_js/reference/noir_js/functions/keccak256.md @@ -0,0 +1,21 @@ +# keccak256() + +```ts +keccak256(inputs): Uint8Array +``` + +Calculates the Keccak256 hash of the input bytes + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `inputs` | `Uint8Array` | | + +## Returns + +`Uint8Array` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.1/noir_js/reference/noir_js/functions/sha256.md b/docs/versioned_docs/version-v0.19.1/noir_js/reference/noir_js/functions/sha256.md new file mode 100644 index 00000000000..6ba4ecac022 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/noir_js/reference/noir_js/functions/sha256.md @@ -0,0 +1,21 @@ +# sha256() + +```ts +sha256(inputs): Uint8Array +``` + +Calculates the SHA256 hash of the input bytes + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `inputs` | `Uint8Array` | | + +## Returns + +`Uint8Array` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.1/noir_js/reference/noir_js/functions/xor.md b/docs/versioned_docs/version-v0.19.1/noir_js/reference/noir_js/functions/xor.md new file mode 100644 index 00000000000..8d762b895d3 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/noir_js/reference/noir_js/functions/xor.md @@ -0,0 +1,22 @@ +# xor() + +```ts +xor(lhs, rhs): string +``` + +Performs a bitwise XOR operation between `lhs` and `rhs` + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `lhs` | `string` | | +| `rhs` | `string` | | + +## Returns + +`string` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.1/noir_js/reference/noir_js/index.md b/docs/versioned_docs/version-v0.19.1/noir_js/reference/noir_js/index.md new file mode 100644 index 00000000000..58902c17b99 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/noir_js/reference/noir_js/index.md @@ -0,0 +1,36 @@ +# Noir JS + +## Exports + +### Classes + +| Class | Description | +| :------ | :------ | +| [Noir](classes/Noir.md) | - | + +### Type Aliases + +| Type alias | Description | +| :------ | :------ | +| [CompiledCircuit](type-aliases/CompiledCircuit.md) | - | +| [ForeignCallHandler](type-aliases/ForeignCallHandler.md) | A callback which performs an foreign call and returns the response. | +| [ForeignCallInput](type-aliases/ForeignCallInput.md) | - | +| [ForeignCallOutput](type-aliases/ForeignCallOutput.md) | - | +| [ProofData](type-aliases/ProofData.md) | - | +| [WitnessMap](type-aliases/WitnessMap.md) | - | + +### Functions + +| Function | Description | +| :------ | :------ | +| [and](functions/and.md) | Performs a bitwise AND operation between `lhs` and `rhs` | +| [blake2s256](functions/blake2s256.md) | Calculates the Blake2s256 hash of the input bytes | +| [ecdsa\_secp256k1\_verify](functions/ecdsa_secp256k1_verify.md) | Calculates the Blake2s256 hash of the input bytes and represents these as a single field element. | +| [ecdsa\_secp256r1\_verify](functions/ecdsa_secp256r1_verify.md) | Verifies a ECDSA signature over the secp256r1 curve. | +| [keccak256](functions/keccak256.md) | Calculates the Keccak256 hash of the input bytes | +| [sha256](functions/sha256.md) | Calculates the SHA256 hash of the input bytes | +| [xor](functions/xor.md) | Performs a bitwise XOR operation between `lhs` and `rhs` | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.1/noir_js/reference/noir_js/type-aliases/CompiledCircuit.md b/docs/versioned_docs/version-v0.19.1/noir_js/reference/noir_js/type-aliases/CompiledCircuit.md new file mode 100644 index 00000000000..34e0dd04205 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/noir_js/reference/noir_js/type-aliases/CompiledCircuit.md @@ -0,0 +1,20 @@ +# CompiledCircuit + +```ts +type CompiledCircuit: object; +``` + +## Description + +The representation of a compiled circuit + +## Type declaration + +| Member | Type | Description | +| :------ | :------ | :------ | +| `abi` | `Abi` | **Description**

ABI representation of the circuit | +| `bytecode` | `string` | **Description**

The bytecode of the circuit | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.1/noir_js/reference/noir_js/type-aliases/ForeignCallHandler.md b/docs/versioned_docs/version-v0.19.1/noir_js/reference/noir_js/type-aliases/ForeignCallHandler.md new file mode 100644 index 00000000000..812b8b16481 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/noir_js/reference/noir_js/type-aliases/ForeignCallHandler.md @@ -0,0 +1,24 @@ +# ForeignCallHandler + +```ts +type ForeignCallHandler: (name, inputs) => Promise; +``` + +A callback which performs an foreign call and returns the response. + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `name` | `string` | The identifier for the type of foreign call being performed. | +| `inputs` | [`ForeignCallInput`](ForeignCallInput.md)[] | An array of hex encoded inputs to the foreign call. | + +## Returns + +`Promise`\<[`ForeignCallOutput`](ForeignCallOutput.md)[]\> + +outputs - An array of hex encoded outputs containing the results of the foreign call. + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.1/noir_js/reference/noir_js/type-aliases/ForeignCallInput.md b/docs/versioned_docs/version-v0.19.1/noir_js/reference/noir_js/type-aliases/ForeignCallInput.md new file mode 100644 index 00000000000..dd95809186a --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/noir_js/reference/noir_js/type-aliases/ForeignCallInput.md @@ -0,0 +1,9 @@ +# ForeignCallInput + +```ts +type ForeignCallInput: string[]; +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.1/noir_js/reference/noir_js/type-aliases/ForeignCallOutput.md b/docs/versioned_docs/version-v0.19.1/noir_js/reference/noir_js/type-aliases/ForeignCallOutput.md new file mode 100644 index 00000000000..b71fb78a946 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/noir_js/reference/noir_js/type-aliases/ForeignCallOutput.md @@ -0,0 +1,9 @@ +# ForeignCallOutput + +```ts +type ForeignCallOutput: string | string[]; +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.1/noir_js/reference/noir_js/type-aliases/ProofData.md b/docs/versioned_docs/version-v0.19.1/noir_js/reference/noir_js/type-aliases/ProofData.md new file mode 100644 index 00000000000..4aeff73d3e4 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/noir_js/reference/noir_js/type-aliases/ProofData.md @@ -0,0 +1,20 @@ +# ProofData + +```ts +type ProofData: object; +``` + +## Description + +The representation of a proof + +## Type declaration + +| Member | Type | Description | +| :------ | :------ | :------ | +| `proof` | `Uint8Array` | **Description**

An byte array representing the proof | +| `publicInputs` | `Uint8Array`[] | **Description**

Public inputs of a proof | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.1/noir_js/reference/noir_js/type-aliases/WitnessMap.md b/docs/versioned_docs/version-v0.19.1/noir_js/reference/noir_js/type-aliases/WitnessMap.md new file mode 100644 index 00000000000..258c46f9d0c --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/noir_js/reference/noir_js/type-aliases/WitnessMap.md @@ -0,0 +1,9 @@ +# WitnessMap + +```ts +type WitnessMap: Map; +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.1/noir_js/reference/noir_js/typedoc-sidebar.cjs b/docs/versioned_docs/version-v0.19.1/noir_js/reference/noir_js/typedoc-sidebar.cjs new file mode 100644 index 00000000000..c18318850d0 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/noir_js/reference/noir_js/typedoc-sidebar.cjs @@ -0,0 +1,4 @@ +// @ts-check +/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ +const typedocSidebar = { items: [{"type":"category","label":"Classes","items":[{"type":"doc","id":"noir_js/reference/noir_js/classes/Noir","label":"Noir"}]},{"type":"category","label":"Type Aliases","items":[{"type":"doc","id":"noir_js/reference/noir_js/type-aliases/CompiledCircuit","label":"CompiledCircuit"},{"type":"doc","id":"noir_js/reference/noir_js/type-aliases/ForeignCallHandler","label":"ForeignCallHandler"},{"type":"doc","id":"noir_js/reference/noir_js/type-aliases/ForeignCallInput","label":"ForeignCallInput"},{"type":"doc","id":"noir_js/reference/noir_js/type-aliases/ForeignCallOutput","label":"ForeignCallOutput"},{"type":"doc","id":"noir_js/reference/noir_js/type-aliases/ProofData","label":"ProofData"},{"type":"doc","id":"noir_js/reference/noir_js/type-aliases/WitnessMap","label":"WitnessMap"}]},{"type":"category","label":"Functions","items":[{"type":"doc","id":"noir_js/reference/noir_js/functions/and","label":"and"},{"type":"doc","id":"noir_js/reference/noir_js/functions/blake2s256","label":"blake2s256"},{"type":"doc","id":"noir_js/reference/noir_js/functions/ecdsa_secp256k1_verify","label":"ecdsa_secp256k1_verify"},{"type":"doc","id":"noir_js/reference/noir_js/functions/ecdsa_secp256r1_verify","label":"ecdsa_secp256r1_verify"},{"type":"doc","id":"noir_js/reference/noir_js/functions/keccak256","label":"keccak256"},{"type":"doc","id":"noir_js/reference/noir_js/functions/sha256","label":"sha256"},{"type":"doc","id":"noir_js/reference/noir_js/functions/xor","label":"xor"}]}]}; +module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.19.1/standard_library/black_box_fns.md b/docs/versioned_docs/version-v0.19.1/standard_library/black_box_fns.md new file mode 100644 index 00000000000..1dfabfe8f22 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/standard_library/black_box_fns.md @@ -0,0 +1,46 @@ +--- +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 Hash](./cryptographic_primitives/hashes#pedersen_hash) +- [Pedersen Commitment](./cryptographic_primitives/hashes#pedersen_commitment) +- [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.7.1/standard_library/cryptographic_primitives.md b/docs/versioned_docs/version-v0.19.1/standard_library/cryptographic_primitives.md similarity index 100% rename from docs/versioned_docs/version-0.7.1/standard_library/cryptographic_primitives.md rename to docs/versioned_docs/version-v0.19.1/standard_library/cryptographic_primitives.md diff --git a/docs/versioned_docs/version-v0.19.1/standard_library/cryptographic_primitives/00_hashes.mdx b/docs/versioned_docs/version-v0.19.1/standard_library/cryptographic_primitives/00_hashes.mdx new file mode 100644 index 00000000000..76745196681 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/standard_library/cryptographic_primitives/00_hashes.mdx @@ -0,0 +1,167 @@ +--- +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 '@site/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_hash + +Given an array of Fields, returns the Pedersen hash. + +```rust +fn pedersen_hash(_input : [Field]) -> Field +``` + +example: + +```rust +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let hash = std::hash::pedersen_hash(x); +} +``` + + + + + +## pedersen_commitment + +Given an array of Fields, returns the Pedersen commitment. + +```rust +fn pedersen_commitment(_input : [Field]) -> [Field; 2] +``` + +example: + +```rust +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let commitment = std::hash::pedersen_commitment(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::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-v0.19.1/standard_library/cryptographic_primitives/01_scalar.mdx b/docs/versioned_docs/version-v0.19.1/standard_library/cryptographic_primitives/01_scalar.mdx new file mode 100644 index 00000000000..c7eed820a80 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/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 '@site/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-v0.19.1/standard_library/cryptographic_primitives/02_schnorr.mdx b/docs/versioned_docs/version-v0.19.1/standard_library/cryptographic_primitives/02_schnorr.mdx new file mode 100644 index 00000000000..c184ce28120 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/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 '@site/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-v0.19.1/standard_library/cryptographic_primitives/03_ecdsa_sig_verification.mdx b/docs/versioned_docs/version-v0.19.1/standard_library/cryptographic_primitives/03_ecdsa_sig_verification.mdx new file mode 100644 index 00000000000..72bce984821 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/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 '@site/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-v0.19.1/standard_library/cryptographic_primitives/04_ec_primitives.md b/docs/versioned_docs/version-v0.19.1/standard_library/cryptographic_primitives/04_ec_primitives.md new file mode 100644 index 00000000000..6e6b19b6861 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/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-v0.19.1/standard_library/cryptographic_primitives/05_eddsa.mdx b/docs/versioned_docs/version-v0.19.1/standard_library/cryptographic_primitives/05_eddsa.mdx new file mode 100644 index 00000000000..9a5beb55ee9 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/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 '@site/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-v0.19.1/standard_library/logging.md b/docs/versioned_docs/version-v0.19.1/standard_library/logging.md new file mode 100644 index 00000000000..7e2fd9b9aff --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/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-v0.19.1/standard_library/merkle_trees.md b/docs/versioned_docs/version-v0.19.1/standard_library/merkle_trees.md new file mode 100644 index 00000000000..dc383a1426b --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/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_hash). + +```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-v0.19.1/standard_library/options.md b/docs/versioned_docs/version-v0.19.1/standard_library/options.md new file mode 100644 index 00000000000..3d3139fb98b --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/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-v0.19.1/standard_library/recursion.md b/docs/versioned_docs/version-v0.19.1/standard_library/recursion.md new file mode 100644 index 00000000000..ff4c63acaa7 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.1/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.7.1/standard_library/zeroed.md b/docs/versioned_docs/version-v0.19.1/standard_library/zeroed.md similarity index 100% rename from docs/versioned_docs/version-0.7.1/standard_library/zeroed.md rename to docs/versioned_docs/version-v0.19.1/standard_library/zeroed.md diff --git a/docs/versioned_docs/version-v0.19.2/examples/merkle-proof.mdx b/docs/versioned_docs/version-v0.19.2/examples/merkle-proof.mdx new file mode 100644 index 00000000000..832fb4bb55e --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/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_hash` +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-v0.19.2/getting_started/00_nargo_installation.md b/docs/versioned_docs/version-v0.19.2/getting_started/00_nargo_installation.md new file mode 100644 index 00000000000..725c5f4d373 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/getting_started/00_nargo_installation.md @@ -0,0 +1,249 @@ +--- +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: Compile from Source](#option-3-compile-from-source) +- [Option 4: WSL for Windows](#option-4-wsl-for-windows) + +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 +``` + +##### 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 `rust-toolchain.toml`, which is 1.71.1 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! + +### Option 4: WSL (for Windows) + +The default backend for Noir (Barretenberg) doesn't provide Windows binaries at this time. For that reason, Noir cannot be installed nativerly. However, it is available by using Windows Subsystem for Linux (WSL). + +Step 1: Follow the instructions [here](https://learn.microsoft.com/en-us/windows/wsl/install) to install and run WSL. + +step 2: Follow the [Noirup instructions](#option-1-noirup). + +## 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-v0.19.2/getting_started/01_hello_world.md b/docs/versioned_docs/version-v0.19.2/getting_started/01_hello_world.md new file mode 100644 index 00000000000..8b4416beba1 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/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-v0.19.2/getting_started/02_breakdown.md b/docs/versioned_docs/version-v0.19.2/getting_started/02_breakdown.md new file mode 100644 index 00000000000..9a17f5d6360 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/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` - specifies the version of the compiler to use. This is enforced by the compiler and follow's [Rust's versioning](https://doc.rust-lang.org/cargo/reference/manifest.html#the-version-field), so a `compiler_version = 0.18.0` will enforce Nargo version 0.18.0, `compiler_version = ^0.18.0` will enforce anything above 0.18.0 but below 0.19.0, etc. For more information, see how [Rust handles these operators](https://docs.rs/semver/latest/semver/enum.Op.html) +- `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-v0.19.2/index.md b/docs/versioned_docs/version-v0.19.2/index.md new file mode 100644 index 00000000000..75e1abf2932 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/index.md @@ -0,0 +1,100 @@ +--- +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 Commitment +- Pedersen Hash +- 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-v0.19.2/language_concepts/01_functions.md b/docs/versioned_docs/version-v0.19.2/language_concepts/01_functions.md new file mode 100644 index 00000000000..5eb22170e54 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/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 : 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 : 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-v0.19.2/language_concepts/02_control_flow.md b/docs/versioned_docs/version-v0.19.2/language_concepts/02_control_flow.md new file mode 100644 index 00000000000..a7f85360197 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/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-v0.19.2/language_concepts/03_ops.md b/docs/versioned_docs/version-v0.19.2/language_concepts/03_ops.md new file mode 100644 index 00000000000..da02b126059 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/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-v0.19.2/language_concepts/04_assert.md b/docs/versioned_docs/version-v0.19.2/language_concepts/04_assert.md new file mode 100644 index 00000000000..7427ec6cc63 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/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-v0.19.2/language_concepts/05_unconstrained.md b/docs/versioned_docs/version-v0.19.2/language_concepts/05_unconstrained.md new file mode 100644 index 00000000000..6b621eda3eb --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/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-v0.19.2/language_concepts/06_generics.md b/docs/versioned_docs/version-v0.19.2/language_concepts/06_generics.md new file mode 100644 index 00000000000..9fb4177c2a8 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/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/tooling/nargo_cli/tests/execution_success/generics/src/main.nr). diff --git a/docs/versioned_docs/version-v0.19.2/language_concepts/07_mutability.md b/docs/versioned_docs/version-v0.19.2/language_concepts/07_mutability.md new file mode 100644 index 00000000000..ad902c42c9b --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/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() -> pub 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-v0.19.2/language_concepts/08_lambdas.md b/docs/versioned_docs/version-v0.19.2/language_concepts/08_lambdas.md new file mode 100644 index 00000000000..ae1e6aecab1 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/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-v0.19.2/language_concepts/09_comments.md b/docs/versioned_docs/version-v0.19.2/language_concepts/09_comments.md new file mode 100644 index 00000000000..3bb4d2f25a4 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/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-v0.19.2/language_concepts/10_distinct.md b/docs/versioned_docs/version-v0.19.2/language_concepts/10_distinct.md new file mode 100644 index 00000000000..e7ff7f5017a --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/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-v0.19.2/language_concepts/11_shadowing.md b/docs/versioned_docs/version-v0.19.2/language_concepts/11_shadowing.md new file mode 100644 index 00000000000..efd743e764f --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/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-v0.19.2/language_concepts/data_types.md b/docs/versioned_docs/version-v0.19.2/language_concepts/data_types.md new file mode 100644 index 00000000000..d546cc463a8 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/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-v0.19.2/language_concepts/data_types/00_fields.md b/docs/versioned_docs/version-v0.19.2/language_concepts/data_types/00_fields.md new file mode 100644 index 00000000000..658a0441ffb --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/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-v0.19.2/language_concepts/data_types/01_integers.md b/docs/versioned_docs/version-v0.19.2/language_concepts/data_types/01_integers.md new file mode 100644 index 00000000000..b1e7ad11bfd --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/language_concepts/data_types/01_integers.md @@ -0,0 +1,112 @@ +--- +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 supports arbitrarily-sized, both unsigned and signed integer types. + +:::info + +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. + +::: + +## Unsigned Integers + +An unsigned integer type is specified first with the letter `u` (indicating its unsigned nature) followed by its bit size (e.g. `8`): + +```rust +fn main() { + let x: u8 = 1; + let y: u8 = 1; + let z = x + y; + assert (z == 2); +} +``` + +The bit size determines the maximum value the integer type can store. For example, a `u8` variable can store a value in the range of 0 to 255 (i.e. $\\2^{8}-1\\$). + +## Signed Integers + +A signed integer type is specified first with the letter `i` (which stands for integer) followed by its bit size (e.g. `8`): + +```rust +fn main() { + let x: i8 = -1; + let y: i8 = -1; + let z = x + y; + assert (z == -2); +} +``` + +The bit size determines the maximum and minimum range of value the integer type can store. For example, an `i8` variable can store a value in the range of -128 to 127 (i.e. $\\-2^{7}\\$ to $\\2^{7}-1\\$). + +:::tip + +If you are using the default proving backend with Noir, both even (e.g. _u2_, _i2_) and odd (e.g. _u3_, _i3_) arbitrarily-sized integer types up to 127 bits (i.e. _u127_ and _i127_) are supported. + +::: + +## Overflows + +Computations that exceed the type boundaries will result in overflow errors. This happens with both signed and unsigned integers. For example, attempting to prove: + +```rust +fn main(x: u8, y: u8) { + let z = x + y; +} +``` + +With: + +```toml +x = "255" +y = "1" +``` + +Would result in: + +``` +$ nargo prove +error: Assertion failed: 'attempt to add with overflow' +┌─ ~/src/main.nr:9:13 +│ +│ let z = x + y; +│ ----- +│ += Call stack: + ... +``` + +A similar error would happen with signed integers: + +```rust +fn main() { + let x: i8 = -118; + let y: i8 = -11; + let z = x + y; +} +``` + +### Wrapping methods + +Although integer overflow is expected to error, some use-cases rely on wrapping. For these use-cases, the standard library provides `wrapping` variants of certain common operations: + +```rust +fn wrapping_add(x: T, y: T) -> T; +fn wrapping_sub(x: T, y: T) -> T; +fn wrapping_mul(x: T, y: T) -> T; +``` + +Example of how it is used: + +```rust +use dep::std; + +fn main(x: u8, y: u8) -> pub u8 { + std::wrapping_add(x + y) +} +``` diff --git a/docs/versioned_docs/version-v0.19.2/language_concepts/data_types/02_booleans.md b/docs/versioned_docs/version-v0.19.2/language_concepts/data_types/02_booleans.md new file mode 100644 index 00000000000..885db167d83 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/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-v0.19.2/language_concepts/data_types/03_strings.md b/docs/versioned_docs/version-v0.19.2/language_concepts/data_types/03_strings.md new file mode 100644 index 00000000000..c42f34ec3ad --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/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-v0.19.2/language_concepts/data_types/04_arrays.md b/docs/versioned_docs/version-v0.19.2/language_concepts/data_types/04_arrays.md new file mode 100644 index 00000000000..bdbd1798bef --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/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-v0.19.2/language_concepts/data_types/05_slices.mdx b/docs/versioned_docs/version-v0.19.2/language_concepts/data_types/05_slices.mdx new file mode 100644 index 00000000000..1be0ec4a137 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/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 '@site/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-v0.19.2/language_concepts/data_types/06_vectors.mdx b/docs/versioned_docs/version-v0.19.2/language_concepts/data_types/06_vectors.mdx new file mode 100644 index 00000000000..4617e90d038 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/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 '@site/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-v0.19.2/language_concepts/data_types/07_tuples.md b/docs/versioned_docs/version-v0.19.2/language_concepts/data_types/07_tuples.md new file mode 100644 index 00000000000..5f6cab974a8 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/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-v0.19.2/language_concepts/data_types/08_structs.md b/docs/versioned_docs/version-v0.19.2/language_concepts/data_types/08_structs.md new file mode 100644 index 00000000000..9255455bdcb --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/language_concepts/data_types/08_structs.md @@ -0,0 +1,69 @@ +--- +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. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.19.2/language_concepts/data_types/09_references.md b/docs/versioned_docs/version-v0.19.2/language_concepts/data_types/09_references.md new file mode 100644 index 00000000000..b0c35ce2cb9 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/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-v0.19.2/language_concepts/data_types/10_function_types.md b/docs/versioned_docs/version-v0.19.2/language_concepts/data_types/10_function_types.md new file mode 100644 index 00000000000..1ec92efd594 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/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-v0.19.2/migration_notes.md b/docs/versioned_docs/version-v0.19.2/migration_notes.md new file mode 100644 index 00000000000..e87eb1feaba --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/migration_notes.md @@ -0,0 +1,91 @@ +--- +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.19 + +### Enforcing `compiler_version` + +From this version on, the compiler will check for the `compiler_version` field in `Nargo.toml`, and will error if it doesn't match the current Nargo version in use. + +To update, please make sure this field in `Nargo.toml` matches the output of `nargo --version`. + +## ≥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-v0.19.2/modules_packages_crates/crates_and_packages.md b/docs/versioned_docs/version-v0.19.2/modules_packages_crates/crates_and_packages.md new file mode 100644 index 00000000000..fb83a33d94e --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/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-v0.19.2/modules_packages_crates/dependencies.md b/docs/versioned_docs/version-v0.19.2/modules_packages_crates/dependencies.md new file mode 100644 index 00000000000..75f95aaa305 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/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-v0.19.2/modules_packages_crates/modules.md b/docs/versioned_docs/version-v0.19.2/modules_packages_crates/modules.md new file mode 100644 index 00000000000..147c9b284e8 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/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-v0.19.2/modules_packages_crates/workspaces.md b/docs/versioned_docs/version-v0.19.2/modules_packages_crates/workspaces.md new file mode 100644 index 00000000000..d9ac92667c9 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/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-v0.19.2/nargo/01_commands.md b/docs/versioned_docs/version-v0.19.2/nargo/01_commands.md new file mode 100644 index 00000000000..65e2bdb44e3 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/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-v0.19.2/nargo/02_testing.md b/docs/versioned_docs/version-v0.19.2/nargo/02_testing.md new file mode 100644 index 00000000000..da767274efd --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/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-v0.19.2/nargo/03_solidity_verifier.md b/docs/versioned_docs/version-v0.19.2/nargo/03_solidity_verifier.md new file mode 100644 index 00000000000..9ac60cb0ba7 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/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-v0.19.2/nargo/04_language_server.md b/docs/versioned_docs/version-v0.19.2/nargo/04_language_server.md new file mode 100644 index 00000000000..48c01465f6e --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/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](@site/static/img/codelens_compile_execute.png) +![Run test](@site/static/img/codelens_run_test.png) + +You should also see your tests in the `testing` panel: + +![Testing panel](@site/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-v0.19.2/noir_js/getting_started/01_tiny_noir_app.md b/docs/versioned_docs/version-v0.19.2/noir_js/getting_started/01_tiny_noir_app.md new file mode 100644 index 00000000000..c51ed61de52 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/noir_js/getting_started/01_tiny_noir_app.md @@ -0,0 +1,260 @@ +--- +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. + +You can find the complete app code for this guide [here](https://github.com/noir-lang/tiny-noirjs-app). + +## 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](@site/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! + +You can find the complete app code for this guide [here](https://github.com/noir-lang/tiny-noirjs-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-v0.19.2/noir_js/noir_js.md b/docs/versioned_docs/version-v0.19.2/noir_js/noir_js.md new file mode 100644 index 00000000000..f895b22eaf8 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/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/noir_js/classes/Noir.md) sections. diff --git a/docs/versioned_docs/version-v0.19.2/noir_js/reference/backend_barretenberg/.nojekyll b/docs/versioned_docs/version-v0.19.2/noir_js/reference/backend_barretenberg/.nojekyll new file mode 100644 index 00000000000..e2ac6616add --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/noir_js/reference/backend_barretenberg/.nojekyll @@ -0,0 +1 @@ +TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.19.2/noir_js/reference/backend_barretenberg/classes/BarretenbergBackend.md b/docs/versioned_docs/version-v0.19.2/noir_js/reference/backend_barretenberg/classes/BarretenbergBackend.md new file mode 100644 index 00000000000..5cbe9421b92 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/noir_js/reference/backend_barretenberg/classes/BarretenbergBackend.md @@ -0,0 +1,185 @@ +# BarretenbergBackend + +## Implements + +- [`Backend`](../interfaces/Backend.md) + +## Constructors + +### new BarretenbergBackend(acirCircuit, options) + +```ts +new BarretenbergBackend(acirCircuit, options): BarretenbergBackend +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `acirCircuit` | [`CompiledCircuit`](../type-aliases/CompiledCircuit.md) | +| `options` | [`BackendOptions`](../type-aliases/BackendOptions.md) | + +#### Returns + +[`BarretenbergBackend`](BarretenbergBackend.md) + +## Methods + +### destroy() + +```ts +destroy(): Promise +``` + +#### Returns + +`Promise`\<`void`\> + +#### Implementation of + +[`Backend`](../interfaces/Backend.md).[`destroy`](../interfaces/Backend.md#destroy) + +#### Description + +Destroys the backend + +*** + +### generateFinalProof() + +```ts +generateFinalProof(decompressedWitness): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `decompressedWitness` | `Uint8Array` | + +#### Returns + +`Promise`\<[`ProofData`](../type-aliases/ProofData.md)\> + +#### Implementation of + +[`Backend`](../interfaces/Backend.md).[`generateFinalProof`](../interfaces/Backend.md#generatefinalproof) + +#### Description + +Generates a final proof (not meant to be verified in another circuit) + +*** + +### generateIntermediateProof() + +```ts +generateIntermediateProof(witness): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `witness` | `Uint8Array` | + +#### Returns + +`Promise`\<[`ProofData`](../type-aliases/ProofData.md)\> + +#### Implementation of + +[`Backend`](../interfaces/Backend.md).[`generateIntermediateProof`](../interfaces/Backend.md#generateintermediateproof) + +#### Example + +```typescript +const intermediateProof = await backend.generateIntermediateProof(witness); +``` + +*** + +### generateIntermediateProofArtifacts() + +```ts +generateIntermediateProofArtifacts(proofData, numOfPublicInputs): Promise +``` + +#### Parameters + +| Parameter | Type | Default value | +| :------ | :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | `undefined` | +| `numOfPublicInputs` | `number` | `0` | + +#### Returns + +`Promise`\<`object`\> + +#### Implementation of + +[`Backend`](../interfaces/Backend.md).[`generateIntermediateProofArtifacts`](../interfaces/Backend.md#generateintermediateproofartifacts) + +#### Example + +```typescript +const artifacts = await backend.generateIntermediateProofArtifacts(proof, numOfPublicInputs); +``` + +*** + +### verifyFinalProof() + +```ts +verifyFinalProof(proofData): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | + +#### Returns + +`Promise`\<`boolean`\> + +#### Implementation of + +[`Backend`](../interfaces/Backend.md).[`verifyFinalProof`](../interfaces/Backend.md#verifyfinalproof) + +#### Description + +Verifies a final proof + +*** + +### verifyIntermediateProof() + +```ts +verifyIntermediateProof(proofData): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | + +#### Returns + +`Promise`\<`boolean`\> + +#### Implementation of + +[`Backend`](../interfaces/Backend.md).[`verifyIntermediateProof`](../interfaces/Backend.md#verifyintermediateproof) + +#### Example + +```typescript +const isValidIntermediate = await backend.verifyIntermediateProof(proof); +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.2/noir_js/reference/backend_barretenberg/index.md b/docs/versioned_docs/version-v0.19.2/noir_js/reference/backend_barretenberg/index.md new file mode 100644 index 00000000000..3680ba3ca77 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/noir_js/reference/backend_barretenberg/index.md @@ -0,0 +1,27 @@ +# Backend Barretenberg + +## Exports + +### Classes + +| Class | Description | +| :------ | :------ | +| [BarretenbergBackend](classes/BarretenbergBackend.md) | - | + +### Interfaces + +| Interface | Description | +| :------ | :------ | +| [Backend](interfaces/Backend.md) | - | + +### Type Aliases + +| Type alias | Description | +| :------ | :------ | +| [BackendOptions](type-aliases/BackendOptions.md) | - | +| [CompiledCircuit](type-aliases/CompiledCircuit.md) | - | +| [ProofData](type-aliases/ProofData.md) | - | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.2/noir_js/reference/backend_barretenberg/interfaces/Backend.md b/docs/versioned_docs/version-v0.19.2/noir_js/reference/backend_barretenberg/interfaces/Backend.md new file mode 100644 index 00000000000..3eb9645c8d2 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/noir_js/reference/backend_barretenberg/interfaces/Backend.md @@ -0,0 +1,132 @@ +# Backend + +## Methods + +### destroy() + +```ts +destroy(): Promise +``` + +#### Returns + +`Promise`\<`void`\> + +#### Description + +Destroys the backend + +*** + +### generateFinalProof() + +```ts +generateFinalProof(decompressedWitness): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `decompressedWitness` | `Uint8Array` | + +#### Returns + +`Promise`\<[`ProofData`](../type-aliases/ProofData.md)\> + +#### Description + +Generates a final proof (not meant to be verified in another circuit) + +*** + +### generateIntermediateProof() + +```ts +generateIntermediateProof(decompressedWitness): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `decompressedWitness` | `Uint8Array` | + +#### Returns + +`Promise`\<[`ProofData`](../type-aliases/ProofData.md)\> + +#### Description + +Generates an intermediate proof (meant to be verified in another circuit) + +*** + +### generateIntermediateProofArtifacts() + +```ts +generateIntermediateProofArtifacts(proofData, numOfPublicInputs): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | +| `numOfPublicInputs` | `number` | + +#### Returns + +`Promise`\<`object`\> + +#### Description + +Retrieves the artifacts from a proof in the Field format + +*** + +### verifyFinalProof() + +```ts +verifyFinalProof(proofData): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | + +#### Returns + +`Promise`\<`boolean`\> + +#### Description + +Verifies a final proof + +*** + +### verifyIntermediateProof() + +```ts +verifyIntermediateProof(proofData): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | + +#### Returns + +`Promise`\<`boolean`\> + +#### Description + +Verifies an intermediate proof + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.2/noir_js/reference/backend_barretenberg/type-aliases/BackendOptions.md b/docs/versioned_docs/version-v0.19.2/noir_js/reference/backend_barretenberg/type-aliases/BackendOptions.md new file mode 100644 index 00000000000..266ade75d17 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/noir_js/reference/backend_barretenberg/type-aliases/BackendOptions.md @@ -0,0 +1,19 @@ +# BackendOptions + +```ts +type BackendOptions: object; +``` + +## Description + +An options object, currently only used to specify the number of threads to use. + +## Type declaration + +| Member | Type | Description | +| :------ | :------ | :------ | +| `threads` | `number` | **Description**

Number of threads | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.2/noir_js/reference/backend_barretenberg/type-aliases/CompiledCircuit.md b/docs/versioned_docs/version-v0.19.2/noir_js/reference/backend_barretenberg/type-aliases/CompiledCircuit.md new file mode 100644 index 00000000000..34e0dd04205 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/noir_js/reference/backend_barretenberg/type-aliases/CompiledCircuit.md @@ -0,0 +1,20 @@ +# CompiledCircuit + +```ts +type CompiledCircuit: object; +``` + +## Description + +The representation of a compiled circuit + +## Type declaration + +| Member | Type | Description | +| :------ | :------ | :------ | +| `abi` | `Abi` | **Description**

ABI representation of the circuit | +| `bytecode` | `string` | **Description**

The bytecode of the circuit | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.2/noir_js/reference/backend_barretenberg/type-aliases/ProofData.md b/docs/versioned_docs/version-v0.19.2/noir_js/reference/backend_barretenberg/type-aliases/ProofData.md new file mode 100644 index 00000000000..4aeff73d3e4 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/noir_js/reference/backend_barretenberg/type-aliases/ProofData.md @@ -0,0 +1,20 @@ +# ProofData + +```ts +type ProofData: object; +``` + +## Description + +The representation of a proof + +## Type declaration + +| Member | Type | Description | +| :------ | :------ | :------ | +| `proof` | `Uint8Array` | **Description**

An byte array representing the proof | +| `publicInputs` | `Uint8Array`[] | **Description**

Public inputs of a proof | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.2/noir_js/reference/backend_barretenberg/typedoc-sidebar.cjs b/docs/versioned_docs/version-v0.19.2/noir_js/reference/backend_barretenberg/typedoc-sidebar.cjs new file mode 100644 index 00000000000..04e662c845f --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/noir_js/reference/backend_barretenberg/typedoc-sidebar.cjs @@ -0,0 +1,4 @@ +// @ts-check +/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ +const typedocSidebar = { items: [{"type":"category","label":"Classes","items":[{"type":"doc","id":"noir_js/reference/backend_barretenberg/classes/BarretenbergBackend","label":"BarretenbergBackend"}]},{"type":"category","label":"Interfaces","items":[{"type":"doc","id":"noir_js/reference/backend_barretenberg/interfaces/Backend","label":"Backend"}]},{"type":"category","label":"Type Aliases","items":[{"type":"doc","id":"noir_js/reference/backend_barretenberg/type-aliases/BackendOptions","label":"BackendOptions"},{"type":"doc","id":"noir_js/reference/backend_barretenberg/type-aliases/CompiledCircuit","label":"CompiledCircuit"},{"type":"doc","id":"noir_js/reference/backend_barretenberg/type-aliases/ProofData","label":"ProofData"}]}]}; +module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.19.2/noir_js/reference/noir_js/.nojekyll b/docs/versioned_docs/version-v0.19.2/noir_js/reference/noir_js/.nojekyll new file mode 100644 index 00000000000..e2ac6616add --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/noir_js/reference/noir_js/.nojekyll @@ -0,0 +1 @@ +TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.19.2/noir_js/reference/noir_js/classes/Noir.md b/docs/versioned_docs/version-v0.19.2/noir_js/reference/noir_js/classes/Noir.md new file mode 100644 index 00000000000..a8a0bb451c1 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/noir_js/reference/noir_js/classes/Noir.md @@ -0,0 +1,131 @@ +# Noir + +## Constructors + +### new Noir(circuit, backend) + +```ts +new Noir(circuit, backend?): Noir +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `circuit` | [`CompiledCircuit`](../type-aliases/CompiledCircuit.md) | +| `backend`? | `Backend` | + +#### Returns + +[`Noir`](Noir.md) + +## Methods + +### destroy() + +```ts +destroy(): Promise +``` + +#### Returns + +`Promise`\<`void`\> + +#### Description + +Destroys the underlying backend instance. + +#### Example + +```typescript +await noir.destroy(); +``` + +*** + +### execute() + +```ts +execute(inputs, foreignCallHandler?): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `inputs` | `InputMap` | +| `foreignCallHandler`? | [`ForeignCallHandler`](../type-aliases/ForeignCallHandler.md) | + +#### Returns + +`Promise`\<`object`\> + +#### Description + +Allows to execute a circuit to get its witness and return value. + +#### Example + +```typescript +async execute(inputs) +``` + +*** + +### generateFinalProof() + +```ts +generateFinalProof(inputs): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `inputs` | `InputMap` | + +#### Returns + +`Promise`\<[`ProofData`](../type-aliases/ProofData.md)\> + +#### Description + +Generates a witness and a proof given an object as input. + +#### Example + +```typescript +async generateFinalproof(input) +``` + +*** + +### verifyFinalProof() + +```ts +verifyFinalProof(proofData): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | + +#### Returns + +`Promise`\<`boolean`\> + +#### Description + +Instantiates the verification key and verifies a proof. + +#### Example + +```typescript +async verifyFinalProof(proof) +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.2/noir_js/reference/noir_js/functions/and.md b/docs/versioned_docs/version-v0.19.2/noir_js/reference/noir_js/functions/and.md new file mode 100644 index 00000000000..c783283e396 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/noir_js/reference/noir_js/functions/and.md @@ -0,0 +1,22 @@ +# and() + +```ts +and(lhs, rhs): string +``` + +Performs a bitwise AND operation between `lhs` and `rhs` + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `lhs` | `string` | | +| `rhs` | `string` | | + +## Returns + +`string` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.2/noir_js/reference/noir_js/functions/blake2s256.md b/docs/versioned_docs/version-v0.19.2/noir_js/reference/noir_js/functions/blake2s256.md new file mode 100644 index 00000000000..7882d0da8d5 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/noir_js/reference/noir_js/functions/blake2s256.md @@ -0,0 +1,21 @@ +# blake2s256() + +```ts +blake2s256(inputs): Uint8Array +``` + +Calculates the Blake2s256 hash of the input bytes + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `inputs` | `Uint8Array` | | + +## Returns + +`Uint8Array` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.2/noir_js/reference/noir_js/functions/ecdsa_secp256k1_verify.md b/docs/versioned_docs/version-v0.19.2/noir_js/reference/noir_js/functions/ecdsa_secp256k1_verify.md new file mode 100644 index 00000000000..0ba5783f0d5 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/noir_js/reference/noir_js/functions/ecdsa_secp256k1_verify.md @@ -0,0 +1,29 @@ +# ecdsa\_secp256k1\_verify() + +```ts +ecdsa_secp256k1_verify( + hashed_msg, + public_key_x_bytes, + public_key_y_bytes, + signature): boolean +``` + +Calculates the Blake2s256 hash of the input bytes and represents these as a single field element. +Verifies a ECDSA signature over the secp256k1 curve. + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `hashed_msg` | `Uint8Array` | | +| `public_key_x_bytes` | `Uint8Array` | | +| `public_key_y_bytes` | `Uint8Array` | | +| `signature` | `Uint8Array` | | + +## Returns + +`boolean` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.2/noir_js/reference/noir_js/functions/ecdsa_secp256r1_verify.md b/docs/versioned_docs/version-v0.19.2/noir_js/reference/noir_js/functions/ecdsa_secp256r1_verify.md new file mode 100644 index 00000000000..0b20ff68957 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/noir_js/reference/noir_js/functions/ecdsa_secp256r1_verify.md @@ -0,0 +1,28 @@ +# ecdsa\_secp256r1\_verify() + +```ts +ecdsa_secp256r1_verify( + hashed_msg, + public_key_x_bytes, + public_key_y_bytes, + signature): boolean +``` + +Verifies a ECDSA signature over the secp256r1 curve. + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `hashed_msg` | `Uint8Array` | | +| `public_key_x_bytes` | `Uint8Array` | | +| `public_key_y_bytes` | `Uint8Array` | | +| `signature` | `Uint8Array` | | + +## Returns + +`boolean` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.2/noir_js/reference/noir_js/functions/keccak256.md b/docs/versioned_docs/version-v0.19.2/noir_js/reference/noir_js/functions/keccak256.md new file mode 100644 index 00000000000..d10f155ce86 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/noir_js/reference/noir_js/functions/keccak256.md @@ -0,0 +1,21 @@ +# keccak256() + +```ts +keccak256(inputs): Uint8Array +``` + +Calculates the Keccak256 hash of the input bytes + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `inputs` | `Uint8Array` | | + +## Returns + +`Uint8Array` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.2/noir_js/reference/noir_js/functions/sha256.md b/docs/versioned_docs/version-v0.19.2/noir_js/reference/noir_js/functions/sha256.md new file mode 100644 index 00000000000..6ba4ecac022 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/noir_js/reference/noir_js/functions/sha256.md @@ -0,0 +1,21 @@ +# sha256() + +```ts +sha256(inputs): Uint8Array +``` + +Calculates the SHA256 hash of the input bytes + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `inputs` | `Uint8Array` | | + +## Returns + +`Uint8Array` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.2/noir_js/reference/noir_js/functions/xor.md b/docs/versioned_docs/version-v0.19.2/noir_js/reference/noir_js/functions/xor.md new file mode 100644 index 00000000000..8d762b895d3 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/noir_js/reference/noir_js/functions/xor.md @@ -0,0 +1,22 @@ +# xor() + +```ts +xor(lhs, rhs): string +``` + +Performs a bitwise XOR operation between `lhs` and `rhs` + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `lhs` | `string` | | +| `rhs` | `string` | | + +## Returns + +`string` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.2/noir_js/reference/noir_js/index.md b/docs/versioned_docs/version-v0.19.2/noir_js/reference/noir_js/index.md new file mode 100644 index 00000000000..58902c17b99 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/noir_js/reference/noir_js/index.md @@ -0,0 +1,36 @@ +# Noir JS + +## Exports + +### Classes + +| Class | Description | +| :------ | :------ | +| [Noir](classes/Noir.md) | - | + +### Type Aliases + +| Type alias | Description | +| :------ | :------ | +| [CompiledCircuit](type-aliases/CompiledCircuit.md) | - | +| [ForeignCallHandler](type-aliases/ForeignCallHandler.md) | A callback which performs an foreign call and returns the response. | +| [ForeignCallInput](type-aliases/ForeignCallInput.md) | - | +| [ForeignCallOutput](type-aliases/ForeignCallOutput.md) | - | +| [ProofData](type-aliases/ProofData.md) | - | +| [WitnessMap](type-aliases/WitnessMap.md) | - | + +### Functions + +| Function | Description | +| :------ | :------ | +| [and](functions/and.md) | Performs a bitwise AND operation between `lhs` and `rhs` | +| [blake2s256](functions/blake2s256.md) | Calculates the Blake2s256 hash of the input bytes | +| [ecdsa\_secp256k1\_verify](functions/ecdsa_secp256k1_verify.md) | Calculates the Blake2s256 hash of the input bytes and represents these as a single field element. | +| [ecdsa\_secp256r1\_verify](functions/ecdsa_secp256r1_verify.md) | Verifies a ECDSA signature over the secp256r1 curve. | +| [keccak256](functions/keccak256.md) | Calculates the Keccak256 hash of the input bytes | +| [sha256](functions/sha256.md) | Calculates the SHA256 hash of the input bytes | +| [xor](functions/xor.md) | Performs a bitwise XOR operation between `lhs` and `rhs` | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.2/noir_js/reference/noir_js/type-aliases/CompiledCircuit.md b/docs/versioned_docs/version-v0.19.2/noir_js/reference/noir_js/type-aliases/CompiledCircuit.md new file mode 100644 index 00000000000..34e0dd04205 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/noir_js/reference/noir_js/type-aliases/CompiledCircuit.md @@ -0,0 +1,20 @@ +# CompiledCircuit + +```ts +type CompiledCircuit: object; +``` + +## Description + +The representation of a compiled circuit + +## Type declaration + +| Member | Type | Description | +| :------ | :------ | :------ | +| `abi` | `Abi` | **Description**

ABI representation of the circuit | +| `bytecode` | `string` | **Description**

The bytecode of the circuit | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.2/noir_js/reference/noir_js/type-aliases/ForeignCallHandler.md b/docs/versioned_docs/version-v0.19.2/noir_js/reference/noir_js/type-aliases/ForeignCallHandler.md new file mode 100644 index 00000000000..812b8b16481 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/noir_js/reference/noir_js/type-aliases/ForeignCallHandler.md @@ -0,0 +1,24 @@ +# ForeignCallHandler + +```ts +type ForeignCallHandler: (name, inputs) => Promise; +``` + +A callback which performs an foreign call and returns the response. + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `name` | `string` | The identifier for the type of foreign call being performed. | +| `inputs` | [`ForeignCallInput`](ForeignCallInput.md)[] | An array of hex encoded inputs to the foreign call. | + +## Returns + +`Promise`\<[`ForeignCallOutput`](ForeignCallOutput.md)[]\> + +outputs - An array of hex encoded outputs containing the results of the foreign call. + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.2/noir_js/reference/noir_js/type-aliases/ForeignCallInput.md b/docs/versioned_docs/version-v0.19.2/noir_js/reference/noir_js/type-aliases/ForeignCallInput.md new file mode 100644 index 00000000000..dd95809186a --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/noir_js/reference/noir_js/type-aliases/ForeignCallInput.md @@ -0,0 +1,9 @@ +# ForeignCallInput + +```ts +type ForeignCallInput: string[]; +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.2/noir_js/reference/noir_js/type-aliases/ForeignCallOutput.md b/docs/versioned_docs/version-v0.19.2/noir_js/reference/noir_js/type-aliases/ForeignCallOutput.md new file mode 100644 index 00000000000..b71fb78a946 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/noir_js/reference/noir_js/type-aliases/ForeignCallOutput.md @@ -0,0 +1,9 @@ +# ForeignCallOutput + +```ts +type ForeignCallOutput: string | string[]; +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.2/noir_js/reference/noir_js/type-aliases/ProofData.md b/docs/versioned_docs/version-v0.19.2/noir_js/reference/noir_js/type-aliases/ProofData.md new file mode 100644 index 00000000000..4aeff73d3e4 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/noir_js/reference/noir_js/type-aliases/ProofData.md @@ -0,0 +1,20 @@ +# ProofData + +```ts +type ProofData: object; +``` + +## Description + +The representation of a proof + +## Type declaration + +| Member | Type | Description | +| :------ | :------ | :------ | +| `proof` | `Uint8Array` | **Description**

An byte array representing the proof | +| `publicInputs` | `Uint8Array`[] | **Description**

Public inputs of a proof | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.2/noir_js/reference/noir_js/type-aliases/WitnessMap.md b/docs/versioned_docs/version-v0.19.2/noir_js/reference/noir_js/type-aliases/WitnessMap.md new file mode 100644 index 00000000000..258c46f9d0c --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/noir_js/reference/noir_js/type-aliases/WitnessMap.md @@ -0,0 +1,9 @@ +# WitnessMap + +```ts +type WitnessMap: Map; +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.2/noir_js/reference/noir_js/typedoc-sidebar.cjs b/docs/versioned_docs/version-v0.19.2/noir_js/reference/noir_js/typedoc-sidebar.cjs new file mode 100644 index 00000000000..c18318850d0 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/noir_js/reference/noir_js/typedoc-sidebar.cjs @@ -0,0 +1,4 @@ +// @ts-check +/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ +const typedocSidebar = { items: [{"type":"category","label":"Classes","items":[{"type":"doc","id":"noir_js/reference/noir_js/classes/Noir","label":"Noir"}]},{"type":"category","label":"Type Aliases","items":[{"type":"doc","id":"noir_js/reference/noir_js/type-aliases/CompiledCircuit","label":"CompiledCircuit"},{"type":"doc","id":"noir_js/reference/noir_js/type-aliases/ForeignCallHandler","label":"ForeignCallHandler"},{"type":"doc","id":"noir_js/reference/noir_js/type-aliases/ForeignCallInput","label":"ForeignCallInput"},{"type":"doc","id":"noir_js/reference/noir_js/type-aliases/ForeignCallOutput","label":"ForeignCallOutput"},{"type":"doc","id":"noir_js/reference/noir_js/type-aliases/ProofData","label":"ProofData"},{"type":"doc","id":"noir_js/reference/noir_js/type-aliases/WitnessMap","label":"WitnessMap"}]},{"type":"category","label":"Functions","items":[{"type":"doc","id":"noir_js/reference/noir_js/functions/and","label":"and"},{"type":"doc","id":"noir_js/reference/noir_js/functions/blake2s256","label":"blake2s256"},{"type":"doc","id":"noir_js/reference/noir_js/functions/ecdsa_secp256k1_verify","label":"ecdsa_secp256k1_verify"},{"type":"doc","id":"noir_js/reference/noir_js/functions/ecdsa_secp256r1_verify","label":"ecdsa_secp256r1_verify"},{"type":"doc","id":"noir_js/reference/noir_js/functions/keccak256","label":"keccak256"},{"type":"doc","id":"noir_js/reference/noir_js/functions/sha256","label":"sha256"},{"type":"doc","id":"noir_js/reference/noir_js/functions/xor","label":"xor"}]}]}; +module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.19.2/standard_library/black_box_fns.md b/docs/versioned_docs/version-v0.19.2/standard_library/black_box_fns.md new file mode 100644 index 00000000000..1dfabfe8f22 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/standard_library/black_box_fns.md @@ -0,0 +1,46 @@ +--- +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 Hash](./cryptographic_primitives/hashes#pedersen_hash) +- [Pedersen Commitment](./cryptographic_primitives/hashes#pedersen_commitment) +- [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.9.0/standard_library/cryptographic_primitives.md b/docs/versioned_docs/version-v0.19.2/standard_library/cryptographic_primitives.md similarity index 100% rename from docs/versioned_docs/version-0.9.0/standard_library/cryptographic_primitives.md rename to docs/versioned_docs/version-v0.19.2/standard_library/cryptographic_primitives.md diff --git a/docs/versioned_docs/version-v0.19.2/standard_library/cryptographic_primitives/00_hashes.mdx b/docs/versioned_docs/version-v0.19.2/standard_library/cryptographic_primitives/00_hashes.mdx new file mode 100644 index 00000000000..76745196681 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/standard_library/cryptographic_primitives/00_hashes.mdx @@ -0,0 +1,167 @@ +--- +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 '@site/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_hash + +Given an array of Fields, returns the Pedersen hash. + +```rust +fn pedersen_hash(_input : [Field]) -> Field +``` + +example: + +```rust +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let hash = std::hash::pedersen_hash(x); +} +``` + + + + + +## pedersen_commitment + +Given an array of Fields, returns the Pedersen commitment. + +```rust +fn pedersen_commitment(_input : [Field]) -> [Field; 2] +``` + +example: + +```rust +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let commitment = std::hash::pedersen_commitment(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::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-v0.19.2/standard_library/cryptographic_primitives/01_scalar.mdx b/docs/versioned_docs/version-v0.19.2/standard_library/cryptographic_primitives/01_scalar.mdx new file mode 100644 index 00000000000..c7eed820a80 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/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 '@site/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-v0.19.2/standard_library/cryptographic_primitives/02_schnorr.mdx b/docs/versioned_docs/version-v0.19.2/standard_library/cryptographic_primitives/02_schnorr.mdx new file mode 100644 index 00000000000..c184ce28120 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/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 '@site/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-v0.19.2/standard_library/cryptographic_primitives/03_ecdsa_sig_verification.mdx b/docs/versioned_docs/version-v0.19.2/standard_library/cryptographic_primitives/03_ecdsa_sig_verification.mdx new file mode 100644 index 00000000000..72bce984821 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/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 '@site/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-v0.19.2/standard_library/cryptographic_primitives/04_ec_primitives.md b/docs/versioned_docs/version-v0.19.2/standard_library/cryptographic_primitives/04_ec_primitives.md new file mode 100644 index 00000000000..6e6b19b6861 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/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-v0.19.2/standard_library/cryptographic_primitives/05_eddsa.mdx b/docs/versioned_docs/version-v0.19.2/standard_library/cryptographic_primitives/05_eddsa.mdx new file mode 100644 index 00000000000..9a5beb55ee9 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/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 '@site/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-v0.19.2/standard_library/logging.md b/docs/versioned_docs/version-v0.19.2/standard_library/logging.md new file mode 100644 index 00000000000..7e2fd9b9aff --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/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-v0.19.2/standard_library/merkle_trees.md b/docs/versioned_docs/version-v0.19.2/standard_library/merkle_trees.md new file mode 100644 index 00000000000..dc383a1426b --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/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_hash). + +```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-v0.19.2/standard_library/options.md b/docs/versioned_docs/version-v0.19.2/standard_library/options.md new file mode 100644 index 00000000000..3d3139fb98b --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/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-v0.19.2/standard_library/recursion.md b/docs/versioned_docs/version-v0.19.2/standard_library/recursion.md new file mode 100644 index 00000000000..ff4c63acaa7 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.2/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.9.0/standard_library/zeroed.md b/docs/versioned_docs/version-v0.19.2/standard_library/zeroed.md similarity index 100% rename from docs/versioned_docs/version-0.9.0/standard_library/zeroed.md rename to docs/versioned_docs/version-v0.19.2/standard_library/zeroed.md diff --git a/docs/versioned_docs/version-v0.19.3/examples/merkle-proof.mdx b/docs/versioned_docs/version-v0.19.3/examples/merkle-proof.mdx new file mode 100644 index 00000000000..832fb4bb55e --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/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_hash` +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-v0.19.3/getting_started/00_nargo_installation.md b/docs/versioned_docs/version-v0.19.3/getting_started/00_nargo_installation.md new file mode 100644 index 00000000000..725c5f4d373 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/getting_started/00_nargo_installation.md @@ -0,0 +1,249 @@ +--- +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: Compile from Source](#option-3-compile-from-source) +- [Option 4: WSL for Windows](#option-4-wsl-for-windows) + +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 +``` + +##### 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 `rust-toolchain.toml`, which is 1.71.1 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! + +### Option 4: WSL (for Windows) + +The default backend for Noir (Barretenberg) doesn't provide Windows binaries at this time. For that reason, Noir cannot be installed nativerly. However, it is available by using Windows Subsystem for Linux (WSL). + +Step 1: Follow the instructions [here](https://learn.microsoft.com/en-us/windows/wsl/install) to install and run WSL. + +step 2: Follow the [Noirup instructions](#option-1-noirup). + +## 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-v0.19.3/getting_started/01_hello_world.md b/docs/versioned_docs/version-v0.19.3/getting_started/01_hello_world.md new file mode 100644 index 00000000000..8b4416beba1 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/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-v0.19.3/getting_started/02_breakdown.md b/docs/versioned_docs/version-v0.19.3/getting_started/02_breakdown.md new file mode 100644 index 00000000000..9a17f5d6360 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/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` - specifies the version of the compiler to use. This is enforced by the compiler and follow's [Rust's versioning](https://doc.rust-lang.org/cargo/reference/manifest.html#the-version-field), so a `compiler_version = 0.18.0` will enforce Nargo version 0.18.0, `compiler_version = ^0.18.0` will enforce anything above 0.18.0 but below 0.19.0, etc. For more information, see how [Rust handles these operators](https://docs.rs/semver/latest/semver/enum.Op.html) +- `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-v0.19.3/index.md b/docs/versioned_docs/version-v0.19.3/index.md new file mode 100644 index 00000000000..75e1abf2932 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/index.md @@ -0,0 +1,100 @@ +--- +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 Commitment +- Pedersen Hash +- 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-v0.19.3/language_concepts/01_functions.md b/docs/versioned_docs/version-v0.19.3/language_concepts/01_functions.md new file mode 100644 index 00000000000..5eb22170e54 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/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 : 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 : 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-v0.19.3/language_concepts/02_control_flow.md b/docs/versioned_docs/version-v0.19.3/language_concepts/02_control_flow.md new file mode 100644 index 00000000000..a7f85360197 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/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-v0.19.3/language_concepts/03_ops.md b/docs/versioned_docs/version-v0.19.3/language_concepts/03_ops.md new file mode 100644 index 00000000000..da02b126059 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/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-v0.19.3/language_concepts/04_assert.md b/docs/versioned_docs/version-v0.19.3/language_concepts/04_assert.md new file mode 100644 index 00000000000..7427ec6cc63 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/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-v0.19.3/language_concepts/05_unconstrained.md b/docs/versioned_docs/version-v0.19.3/language_concepts/05_unconstrained.md new file mode 100644 index 00000000000..6b621eda3eb --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/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-v0.19.3/language_concepts/06_generics.md b/docs/versioned_docs/version-v0.19.3/language_concepts/06_generics.md new file mode 100644 index 00000000000..9fb4177c2a8 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/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/tooling/nargo_cli/tests/execution_success/generics/src/main.nr). diff --git a/docs/versioned_docs/version-v0.19.3/language_concepts/07_mutability.md b/docs/versioned_docs/version-v0.19.3/language_concepts/07_mutability.md new file mode 100644 index 00000000000..ad902c42c9b --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/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() -> pub 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-v0.19.3/language_concepts/08_lambdas.md b/docs/versioned_docs/version-v0.19.3/language_concepts/08_lambdas.md new file mode 100644 index 00000000000..ae1e6aecab1 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/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-v0.19.3/language_concepts/09_comments.md b/docs/versioned_docs/version-v0.19.3/language_concepts/09_comments.md new file mode 100644 index 00000000000..3bb4d2f25a4 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/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-v0.19.3/language_concepts/10_distinct.md b/docs/versioned_docs/version-v0.19.3/language_concepts/10_distinct.md new file mode 100644 index 00000000000..e7ff7f5017a --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/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-v0.19.3/language_concepts/11_shadowing.md b/docs/versioned_docs/version-v0.19.3/language_concepts/11_shadowing.md new file mode 100644 index 00000000000..efd743e764f --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/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-v0.19.3/language_concepts/data_types.md b/docs/versioned_docs/version-v0.19.3/language_concepts/data_types.md new file mode 100644 index 00000000000..d546cc463a8 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/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-v0.19.3/language_concepts/data_types/00_fields.md b/docs/versioned_docs/version-v0.19.3/language_concepts/data_types/00_fields.md new file mode 100644 index 00000000000..658a0441ffb --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/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-v0.19.3/language_concepts/data_types/01_integers.md b/docs/versioned_docs/version-v0.19.3/language_concepts/data_types/01_integers.md new file mode 100644 index 00000000000..b1e7ad11bfd --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/language_concepts/data_types/01_integers.md @@ -0,0 +1,112 @@ +--- +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 supports arbitrarily-sized, both unsigned and signed integer types. + +:::info + +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. + +::: + +## Unsigned Integers + +An unsigned integer type is specified first with the letter `u` (indicating its unsigned nature) followed by its bit size (e.g. `8`): + +```rust +fn main() { + let x: u8 = 1; + let y: u8 = 1; + let z = x + y; + assert (z == 2); +} +``` + +The bit size determines the maximum value the integer type can store. For example, a `u8` variable can store a value in the range of 0 to 255 (i.e. $\\2^{8}-1\\$). + +## Signed Integers + +A signed integer type is specified first with the letter `i` (which stands for integer) followed by its bit size (e.g. `8`): + +```rust +fn main() { + let x: i8 = -1; + let y: i8 = -1; + let z = x + y; + assert (z == -2); +} +``` + +The bit size determines the maximum and minimum range of value the integer type can store. For example, an `i8` variable can store a value in the range of -128 to 127 (i.e. $\\-2^{7}\\$ to $\\2^{7}-1\\$). + +:::tip + +If you are using the default proving backend with Noir, both even (e.g. _u2_, _i2_) and odd (e.g. _u3_, _i3_) arbitrarily-sized integer types up to 127 bits (i.e. _u127_ and _i127_) are supported. + +::: + +## Overflows + +Computations that exceed the type boundaries will result in overflow errors. This happens with both signed and unsigned integers. For example, attempting to prove: + +```rust +fn main(x: u8, y: u8) { + let z = x + y; +} +``` + +With: + +```toml +x = "255" +y = "1" +``` + +Would result in: + +``` +$ nargo prove +error: Assertion failed: 'attempt to add with overflow' +┌─ ~/src/main.nr:9:13 +│ +│ let z = x + y; +│ ----- +│ += Call stack: + ... +``` + +A similar error would happen with signed integers: + +```rust +fn main() { + let x: i8 = -118; + let y: i8 = -11; + let z = x + y; +} +``` + +### Wrapping methods + +Although integer overflow is expected to error, some use-cases rely on wrapping. For these use-cases, the standard library provides `wrapping` variants of certain common operations: + +```rust +fn wrapping_add(x: T, y: T) -> T; +fn wrapping_sub(x: T, y: T) -> T; +fn wrapping_mul(x: T, y: T) -> T; +``` + +Example of how it is used: + +```rust +use dep::std; + +fn main(x: u8, y: u8) -> pub u8 { + std::wrapping_add(x + y) +} +``` diff --git a/docs/versioned_docs/version-v0.19.3/language_concepts/data_types/02_booleans.md b/docs/versioned_docs/version-v0.19.3/language_concepts/data_types/02_booleans.md new file mode 100644 index 00000000000..885db167d83 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/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-v0.19.3/language_concepts/data_types/03_strings.md b/docs/versioned_docs/version-v0.19.3/language_concepts/data_types/03_strings.md new file mode 100644 index 00000000000..c42f34ec3ad --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/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-v0.19.3/language_concepts/data_types/04_arrays.md b/docs/versioned_docs/version-v0.19.3/language_concepts/data_types/04_arrays.md new file mode 100644 index 00000000000..bdbd1798bef --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/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-v0.19.3/language_concepts/data_types/05_slices.mdx b/docs/versioned_docs/version-v0.19.3/language_concepts/data_types/05_slices.mdx new file mode 100644 index 00000000000..1be0ec4a137 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/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 '@site/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-v0.19.3/language_concepts/data_types/06_vectors.mdx b/docs/versioned_docs/version-v0.19.3/language_concepts/data_types/06_vectors.mdx new file mode 100644 index 00000000000..4617e90d038 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/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 '@site/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-v0.19.3/language_concepts/data_types/07_tuples.md b/docs/versioned_docs/version-v0.19.3/language_concepts/data_types/07_tuples.md new file mode 100644 index 00000000000..5f6cab974a8 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/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-v0.19.3/language_concepts/data_types/08_structs.md b/docs/versioned_docs/version-v0.19.3/language_concepts/data_types/08_structs.md new file mode 100644 index 00000000000..35421734639 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/language_concepts/data_types/08_structs.md @@ -0,0 +1,69 @@ +--- +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. diff --git a/docs/versioned_docs/version-v0.19.3/language_concepts/data_types/09_references.md b/docs/versioned_docs/version-v0.19.3/language_concepts/data_types/09_references.md new file mode 100644 index 00000000000..b0c35ce2cb9 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/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-v0.19.3/language_concepts/data_types/10_function_types.md b/docs/versioned_docs/version-v0.19.3/language_concepts/data_types/10_function_types.md new file mode 100644 index 00000000000..1ec92efd594 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/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-v0.19.3/migration_notes.md b/docs/versioned_docs/version-v0.19.3/migration_notes.md new file mode 100644 index 00000000000..e87eb1feaba --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/migration_notes.md @@ -0,0 +1,91 @@ +--- +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.19 + +### Enforcing `compiler_version` + +From this version on, the compiler will check for the `compiler_version` field in `Nargo.toml`, and will error if it doesn't match the current Nargo version in use. + +To update, please make sure this field in `Nargo.toml` matches the output of `nargo --version`. + +## ≥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-v0.19.3/modules_packages_crates/crates_and_packages.md b/docs/versioned_docs/version-v0.19.3/modules_packages_crates/crates_and_packages.md new file mode 100644 index 00000000000..fb83a33d94e --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/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-v0.19.3/modules_packages_crates/dependencies.md b/docs/versioned_docs/version-v0.19.3/modules_packages_crates/dependencies.md new file mode 100644 index 00000000000..75f95aaa305 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/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-v0.19.3/modules_packages_crates/modules.md b/docs/versioned_docs/version-v0.19.3/modules_packages_crates/modules.md new file mode 100644 index 00000000000..147c9b284e8 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/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-v0.19.3/modules_packages_crates/workspaces.md b/docs/versioned_docs/version-v0.19.3/modules_packages_crates/workspaces.md new file mode 100644 index 00000000000..a979ef9f0a5 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/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. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.19.3/nargo/01_commands.md b/docs/versioned_docs/version-v0.19.3/nargo/01_commands.md new file mode 100644 index 00000000000..65e2bdb44e3 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/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-v0.19.3/nargo/02_testing.md b/docs/versioned_docs/version-v0.19.3/nargo/02_testing.md new file mode 100644 index 00000000000..da767274efd --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/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-v0.19.3/nargo/03_solidity_verifier.md b/docs/versioned_docs/version-v0.19.3/nargo/03_solidity_verifier.md new file mode 100644 index 00000000000..9ac60cb0ba7 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/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-v0.19.3/nargo/04_language_server.md b/docs/versioned_docs/version-v0.19.3/nargo/04_language_server.md new file mode 100644 index 00000000000..48c01465f6e --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/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](@site/static/img/codelens_compile_execute.png) +![Run test](@site/static/img/codelens_run_test.png) + +You should also see your tests in the `testing` panel: + +![Testing panel](@site/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-v0.19.3/noir_js/getting_started/01_tiny_noir_app.md b/docs/versioned_docs/version-v0.19.3/noir_js/getting_started/01_tiny_noir_app.md new file mode 100644 index 00000000000..c51ed61de52 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/noir_js/getting_started/01_tiny_noir_app.md @@ -0,0 +1,260 @@ +--- +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. + +You can find the complete app code for this guide [here](https://github.com/noir-lang/tiny-noirjs-app). + +## 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](@site/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! + +You can find the complete app code for this guide [here](https://github.com/noir-lang/tiny-noirjs-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-v0.19.3/noir_js/noir_js.md b/docs/versioned_docs/version-v0.19.3/noir_js/noir_js.md new file mode 100644 index 00000000000..f895b22eaf8 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/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/noir_js/classes/Noir.md) sections. diff --git a/docs/versioned_docs/version-v0.19.3/noir_js/reference/backend_barretenberg/.nojekyll b/docs/versioned_docs/version-v0.19.3/noir_js/reference/backend_barretenberg/.nojekyll new file mode 100644 index 00000000000..e2ac6616add --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/noir_js/reference/backend_barretenberg/.nojekyll @@ -0,0 +1 @@ +TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.19.3/noir_js/reference/backend_barretenberg/classes/BarretenbergBackend.md b/docs/versioned_docs/version-v0.19.3/noir_js/reference/backend_barretenberg/classes/BarretenbergBackend.md new file mode 100644 index 00000000000..5cbe9421b92 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/noir_js/reference/backend_barretenberg/classes/BarretenbergBackend.md @@ -0,0 +1,185 @@ +# BarretenbergBackend + +## Implements + +- [`Backend`](../interfaces/Backend.md) + +## Constructors + +### new BarretenbergBackend(acirCircuit, options) + +```ts +new BarretenbergBackend(acirCircuit, options): BarretenbergBackend +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `acirCircuit` | [`CompiledCircuit`](../type-aliases/CompiledCircuit.md) | +| `options` | [`BackendOptions`](../type-aliases/BackendOptions.md) | + +#### Returns + +[`BarretenbergBackend`](BarretenbergBackend.md) + +## Methods + +### destroy() + +```ts +destroy(): Promise +``` + +#### Returns + +`Promise`\<`void`\> + +#### Implementation of + +[`Backend`](../interfaces/Backend.md).[`destroy`](../interfaces/Backend.md#destroy) + +#### Description + +Destroys the backend + +*** + +### generateFinalProof() + +```ts +generateFinalProof(decompressedWitness): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `decompressedWitness` | `Uint8Array` | + +#### Returns + +`Promise`\<[`ProofData`](../type-aliases/ProofData.md)\> + +#### Implementation of + +[`Backend`](../interfaces/Backend.md).[`generateFinalProof`](../interfaces/Backend.md#generatefinalproof) + +#### Description + +Generates a final proof (not meant to be verified in another circuit) + +*** + +### generateIntermediateProof() + +```ts +generateIntermediateProof(witness): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `witness` | `Uint8Array` | + +#### Returns + +`Promise`\<[`ProofData`](../type-aliases/ProofData.md)\> + +#### Implementation of + +[`Backend`](../interfaces/Backend.md).[`generateIntermediateProof`](../interfaces/Backend.md#generateintermediateproof) + +#### Example + +```typescript +const intermediateProof = await backend.generateIntermediateProof(witness); +``` + +*** + +### generateIntermediateProofArtifacts() + +```ts +generateIntermediateProofArtifacts(proofData, numOfPublicInputs): Promise +``` + +#### Parameters + +| Parameter | Type | Default value | +| :------ | :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | `undefined` | +| `numOfPublicInputs` | `number` | `0` | + +#### Returns + +`Promise`\<`object`\> + +#### Implementation of + +[`Backend`](../interfaces/Backend.md).[`generateIntermediateProofArtifacts`](../interfaces/Backend.md#generateintermediateproofartifacts) + +#### Example + +```typescript +const artifacts = await backend.generateIntermediateProofArtifacts(proof, numOfPublicInputs); +``` + +*** + +### verifyFinalProof() + +```ts +verifyFinalProof(proofData): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | + +#### Returns + +`Promise`\<`boolean`\> + +#### Implementation of + +[`Backend`](../interfaces/Backend.md).[`verifyFinalProof`](../interfaces/Backend.md#verifyfinalproof) + +#### Description + +Verifies a final proof + +*** + +### verifyIntermediateProof() + +```ts +verifyIntermediateProof(proofData): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | + +#### Returns + +`Promise`\<`boolean`\> + +#### Implementation of + +[`Backend`](../interfaces/Backend.md).[`verifyIntermediateProof`](../interfaces/Backend.md#verifyintermediateproof) + +#### Example + +```typescript +const isValidIntermediate = await backend.verifyIntermediateProof(proof); +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.3/noir_js/reference/backend_barretenberg/index.md b/docs/versioned_docs/version-v0.19.3/noir_js/reference/backend_barretenberg/index.md new file mode 100644 index 00000000000..3680ba3ca77 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/noir_js/reference/backend_barretenberg/index.md @@ -0,0 +1,27 @@ +# Backend Barretenberg + +## Exports + +### Classes + +| Class | Description | +| :------ | :------ | +| [BarretenbergBackend](classes/BarretenbergBackend.md) | - | + +### Interfaces + +| Interface | Description | +| :------ | :------ | +| [Backend](interfaces/Backend.md) | - | + +### Type Aliases + +| Type alias | Description | +| :------ | :------ | +| [BackendOptions](type-aliases/BackendOptions.md) | - | +| [CompiledCircuit](type-aliases/CompiledCircuit.md) | - | +| [ProofData](type-aliases/ProofData.md) | - | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.3/noir_js/reference/backend_barretenberg/interfaces/Backend.md b/docs/versioned_docs/version-v0.19.3/noir_js/reference/backend_barretenberg/interfaces/Backend.md new file mode 100644 index 00000000000..3eb9645c8d2 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/noir_js/reference/backend_barretenberg/interfaces/Backend.md @@ -0,0 +1,132 @@ +# Backend + +## Methods + +### destroy() + +```ts +destroy(): Promise +``` + +#### Returns + +`Promise`\<`void`\> + +#### Description + +Destroys the backend + +*** + +### generateFinalProof() + +```ts +generateFinalProof(decompressedWitness): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `decompressedWitness` | `Uint8Array` | + +#### Returns + +`Promise`\<[`ProofData`](../type-aliases/ProofData.md)\> + +#### Description + +Generates a final proof (not meant to be verified in another circuit) + +*** + +### generateIntermediateProof() + +```ts +generateIntermediateProof(decompressedWitness): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `decompressedWitness` | `Uint8Array` | + +#### Returns + +`Promise`\<[`ProofData`](../type-aliases/ProofData.md)\> + +#### Description + +Generates an intermediate proof (meant to be verified in another circuit) + +*** + +### generateIntermediateProofArtifacts() + +```ts +generateIntermediateProofArtifacts(proofData, numOfPublicInputs): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | +| `numOfPublicInputs` | `number` | + +#### Returns + +`Promise`\<`object`\> + +#### Description + +Retrieves the artifacts from a proof in the Field format + +*** + +### verifyFinalProof() + +```ts +verifyFinalProof(proofData): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | + +#### Returns + +`Promise`\<`boolean`\> + +#### Description + +Verifies a final proof + +*** + +### verifyIntermediateProof() + +```ts +verifyIntermediateProof(proofData): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | + +#### Returns + +`Promise`\<`boolean`\> + +#### Description + +Verifies an intermediate proof + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.3/noir_js/reference/backend_barretenberg/type-aliases/BackendOptions.md b/docs/versioned_docs/version-v0.19.3/noir_js/reference/backend_barretenberg/type-aliases/BackendOptions.md new file mode 100644 index 00000000000..266ade75d17 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/noir_js/reference/backend_barretenberg/type-aliases/BackendOptions.md @@ -0,0 +1,19 @@ +# BackendOptions + +```ts +type BackendOptions: object; +``` + +## Description + +An options object, currently only used to specify the number of threads to use. + +## Type declaration + +| Member | Type | Description | +| :------ | :------ | :------ | +| `threads` | `number` | **Description**

Number of threads | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.3/noir_js/reference/backend_barretenberg/type-aliases/CompiledCircuit.md b/docs/versioned_docs/version-v0.19.3/noir_js/reference/backend_barretenberg/type-aliases/CompiledCircuit.md new file mode 100644 index 00000000000..34e0dd04205 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/noir_js/reference/backend_barretenberg/type-aliases/CompiledCircuit.md @@ -0,0 +1,20 @@ +# CompiledCircuit + +```ts +type CompiledCircuit: object; +``` + +## Description + +The representation of a compiled circuit + +## Type declaration + +| Member | Type | Description | +| :------ | :------ | :------ | +| `abi` | `Abi` | **Description**

ABI representation of the circuit | +| `bytecode` | `string` | **Description**

The bytecode of the circuit | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.3/noir_js/reference/backend_barretenberg/type-aliases/ProofData.md b/docs/versioned_docs/version-v0.19.3/noir_js/reference/backend_barretenberg/type-aliases/ProofData.md new file mode 100644 index 00000000000..4aeff73d3e4 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/noir_js/reference/backend_barretenberg/type-aliases/ProofData.md @@ -0,0 +1,20 @@ +# ProofData + +```ts +type ProofData: object; +``` + +## Description + +The representation of a proof + +## Type declaration + +| Member | Type | Description | +| :------ | :------ | :------ | +| `proof` | `Uint8Array` | **Description**

An byte array representing the proof | +| `publicInputs` | `Uint8Array`[] | **Description**

Public inputs of a proof | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.3/noir_js/reference/backend_barretenberg/typedoc-sidebar.cjs b/docs/versioned_docs/version-v0.19.3/noir_js/reference/backend_barretenberg/typedoc-sidebar.cjs new file mode 100644 index 00000000000..04e662c845f --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/noir_js/reference/backend_barretenberg/typedoc-sidebar.cjs @@ -0,0 +1,4 @@ +// @ts-check +/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ +const typedocSidebar = { items: [{"type":"category","label":"Classes","items":[{"type":"doc","id":"noir_js/reference/backend_barretenberg/classes/BarretenbergBackend","label":"BarretenbergBackend"}]},{"type":"category","label":"Interfaces","items":[{"type":"doc","id":"noir_js/reference/backend_barretenberg/interfaces/Backend","label":"Backend"}]},{"type":"category","label":"Type Aliases","items":[{"type":"doc","id":"noir_js/reference/backend_barretenberg/type-aliases/BackendOptions","label":"BackendOptions"},{"type":"doc","id":"noir_js/reference/backend_barretenberg/type-aliases/CompiledCircuit","label":"CompiledCircuit"},{"type":"doc","id":"noir_js/reference/backend_barretenberg/type-aliases/ProofData","label":"ProofData"}]}]}; +module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.19.3/noir_js/reference/noir_js/.nojekyll b/docs/versioned_docs/version-v0.19.3/noir_js/reference/noir_js/.nojekyll new file mode 100644 index 00000000000..e2ac6616add --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/noir_js/reference/noir_js/.nojekyll @@ -0,0 +1 @@ +TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.19.3/noir_js/reference/noir_js/classes/Noir.md b/docs/versioned_docs/version-v0.19.3/noir_js/reference/noir_js/classes/Noir.md new file mode 100644 index 00000000000..a8a0bb451c1 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/noir_js/reference/noir_js/classes/Noir.md @@ -0,0 +1,131 @@ +# Noir + +## Constructors + +### new Noir(circuit, backend) + +```ts +new Noir(circuit, backend?): Noir +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `circuit` | [`CompiledCircuit`](../type-aliases/CompiledCircuit.md) | +| `backend`? | `Backend` | + +#### Returns + +[`Noir`](Noir.md) + +## Methods + +### destroy() + +```ts +destroy(): Promise +``` + +#### Returns + +`Promise`\<`void`\> + +#### Description + +Destroys the underlying backend instance. + +#### Example + +```typescript +await noir.destroy(); +``` + +*** + +### execute() + +```ts +execute(inputs, foreignCallHandler?): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `inputs` | `InputMap` | +| `foreignCallHandler`? | [`ForeignCallHandler`](../type-aliases/ForeignCallHandler.md) | + +#### Returns + +`Promise`\<`object`\> + +#### Description + +Allows to execute a circuit to get its witness and return value. + +#### Example + +```typescript +async execute(inputs) +``` + +*** + +### generateFinalProof() + +```ts +generateFinalProof(inputs): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `inputs` | `InputMap` | + +#### Returns + +`Promise`\<[`ProofData`](../type-aliases/ProofData.md)\> + +#### Description + +Generates a witness and a proof given an object as input. + +#### Example + +```typescript +async generateFinalproof(input) +``` + +*** + +### verifyFinalProof() + +```ts +verifyFinalProof(proofData): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | + +#### Returns + +`Promise`\<`boolean`\> + +#### Description + +Instantiates the verification key and verifies a proof. + +#### Example + +```typescript +async verifyFinalProof(proof) +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.3/noir_js/reference/noir_js/functions/and.md b/docs/versioned_docs/version-v0.19.3/noir_js/reference/noir_js/functions/and.md new file mode 100644 index 00000000000..c783283e396 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/noir_js/reference/noir_js/functions/and.md @@ -0,0 +1,22 @@ +# and() + +```ts +and(lhs, rhs): string +``` + +Performs a bitwise AND operation between `lhs` and `rhs` + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `lhs` | `string` | | +| `rhs` | `string` | | + +## Returns + +`string` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.3/noir_js/reference/noir_js/functions/blake2s256.md b/docs/versioned_docs/version-v0.19.3/noir_js/reference/noir_js/functions/blake2s256.md new file mode 100644 index 00000000000..7882d0da8d5 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/noir_js/reference/noir_js/functions/blake2s256.md @@ -0,0 +1,21 @@ +# blake2s256() + +```ts +blake2s256(inputs): Uint8Array +``` + +Calculates the Blake2s256 hash of the input bytes + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `inputs` | `Uint8Array` | | + +## Returns + +`Uint8Array` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.3/noir_js/reference/noir_js/functions/ecdsa_secp256k1_verify.md b/docs/versioned_docs/version-v0.19.3/noir_js/reference/noir_js/functions/ecdsa_secp256k1_verify.md new file mode 100644 index 00000000000..0ba5783f0d5 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/noir_js/reference/noir_js/functions/ecdsa_secp256k1_verify.md @@ -0,0 +1,29 @@ +# ecdsa\_secp256k1\_verify() + +```ts +ecdsa_secp256k1_verify( + hashed_msg, + public_key_x_bytes, + public_key_y_bytes, + signature): boolean +``` + +Calculates the Blake2s256 hash of the input bytes and represents these as a single field element. +Verifies a ECDSA signature over the secp256k1 curve. + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `hashed_msg` | `Uint8Array` | | +| `public_key_x_bytes` | `Uint8Array` | | +| `public_key_y_bytes` | `Uint8Array` | | +| `signature` | `Uint8Array` | | + +## Returns + +`boolean` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.3/noir_js/reference/noir_js/functions/ecdsa_secp256r1_verify.md b/docs/versioned_docs/version-v0.19.3/noir_js/reference/noir_js/functions/ecdsa_secp256r1_verify.md new file mode 100644 index 00000000000..0b20ff68957 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/noir_js/reference/noir_js/functions/ecdsa_secp256r1_verify.md @@ -0,0 +1,28 @@ +# ecdsa\_secp256r1\_verify() + +```ts +ecdsa_secp256r1_verify( + hashed_msg, + public_key_x_bytes, + public_key_y_bytes, + signature): boolean +``` + +Verifies a ECDSA signature over the secp256r1 curve. + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `hashed_msg` | `Uint8Array` | | +| `public_key_x_bytes` | `Uint8Array` | | +| `public_key_y_bytes` | `Uint8Array` | | +| `signature` | `Uint8Array` | | + +## Returns + +`boolean` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.3/noir_js/reference/noir_js/functions/keccak256.md b/docs/versioned_docs/version-v0.19.3/noir_js/reference/noir_js/functions/keccak256.md new file mode 100644 index 00000000000..d10f155ce86 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/noir_js/reference/noir_js/functions/keccak256.md @@ -0,0 +1,21 @@ +# keccak256() + +```ts +keccak256(inputs): Uint8Array +``` + +Calculates the Keccak256 hash of the input bytes + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `inputs` | `Uint8Array` | | + +## Returns + +`Uint8Array` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.3/noir_js/reference/noir_js/functions/sha256.md b/docs/versioned_docs/version-v0.19.3/noir_js/reference/noir_js/functions/sha256.md new file mode 100644 index 00000000000..6ba4ecac022 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/noir_js/reference/noir_js/functions/sha256.md @@ -0,0 +1,21 @@ +# sha256() + +```ts +sha256(inputs): Uint8Array +``` + +Calculates the SHA256 hash of the input bytes + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `inputs` | `Uint8Array` | | + +## Returns + +`Uint8Array` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.3/noir_js/reference/noir_js/functions/xor.md b/docs/versioned_docs/version-v0.19.3/noir_js/reference/noir_js/functions/xor.md new file mode 100644 index 00000000000..8d762b895d3 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/noir_js/reference/noir_js/functions/xor.md @@ -0,0 +1,22 @@ +# xor() + +```ts +xor(lhs, rhs): string +``` + +Performs a bitwise XOR operation between `lhs` and `rhs` + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `lhs` | `string` | | +| `rhs` | `string` | | + +## Returns + +`string` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.3/noir_js/reference/noir_js/index.md b/docs/versioned_docs/version-v0.19.3/noir_js/reference/noir_js/index.md new file mode 100644 index 00000000000..58902c17b99 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/noir_js/reference/noir_js/index.md @@ -0,0 +1,36 @@ +# Noir JS + +## Exports + +### Classes + +| Class | Description | +| :------ | :------ | +| [Noir](classes/Noir.md) | - | + +### Type Aliases + +| Type alias | Description | +| :------ | :------ | +| [CompiledCircuit](type-aliases/CompiledCircuit.md) | - | +| [ForeignCallHandler](type-aliases/ForeignCallHandler.md) | A callback which performs an foreign call and returns the response. | +| [ForeignCallInput](type-aliases/ForeignCallInput.md) | - | +| [ForeignCallOutput](type-aliases/ForeignCallOutput.md) | - | +| [ProofData](type-aliases/ProofData.md) | - | +| [WitnessMap](type-aliases/WitnessMap.md) | - | + +### Functions + +| Function | Description | +| :------ | :------ | +| [and](functions/and.md) | Performs a bitwise AND operation between `lhs` and `rhs` | +| [blake2s256](functions/blake2s256.md) | Calculates the Blake2s256 hash of the input bytes | +| [ecdsa\_secp256k1\_verify](functions/ecdsa_secp256k1_verify.md) | Calculates the Blake2s256 hash of the input bytes and represents these as a single field element. | +| [ecdsa\_secp256r1\_verify](functions/ecdsa_secp256r1_verify.md) | Verifies a ECDSA signature over the secp256r1 curve. | +| [keccak256](functions/keccak256.md) | Calculates the Keccak256 hash of the input bytes | +| [sha256](functions/sha256.md) | Calculates the SHA256 hash of the input bytes | +| [xor](functions/xor.md) | Performs a bitwise XOR operation between `lhs` and `rhs` | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.3/noir_js/reference/noir_js/type-aliases/CompiledCircuit.md b/docs/versioned_docs/version-v0.19.3/noir_js/reference/noir_js/type-aliases/CompiledCircuit.md new file mode 100644 index 00000000000..34e0dd04205 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/noir_js/reference/noir_js/type-aliases/CompiledCircuit.md @@ -0,0 +1,20 @@ +# CompiledCircuit + +```ts +type CompiledCircuit: object; +``` + +## Description + +The representation of a compiled circuit + +## Type declaration + +| Member | Type | Description | +| :------ | :------ | :------ | +| `abi` | `Abi` | **Description**

ABI representation of the circuit | +| `bytecode` | `string` | **Description**

The bytecode of the circuit | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.3/noir_js/reference/noir_js/type-aliases/ForeignCallHandler.md b/docs/versioned_docs/version-v0.19.3/noir_js/reference/noir_js/type-aliases/ForeignCallHandler.md new file mode 100644 index 00000000000..812b8b16481 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/noir_js/reference/noir_js/type-aliases/ForeignCallHandler.md @@ -0,0 +1,24 @@ +# ForeignCallHandler + +```ts +type ForeignCallHandler: (name, inputs) => Promise; +``` + +A callback which performs an foreign call and returns the response. + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `name` | `string` | The identifier for the type of foreign call being performed. | +| `inputs` | [`ForeignCallInput`](ForeignCallInput.md)[] | An array of hex encoded inputs to the foreign call. | + +## Returns + +`Promise`\<[`ForeignCallOutput`](ForeignCallOutput.md)[]\> + +outputs - An array of hex encoded outputs containing the results of the foreign call. + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.3/noir_js/reference/noir_js/type-aliases/ForeignCallInput.md b/docs/versioned_docs/version-v0.19.3/noir_js/reference/noir_js/type-aliases/ForeignCallInput.md new file mode 100644 index 00000000000..dd95809186a --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/noir_js/reference/noir_js/type-aliases/ForeignCallInput.md @@ -0,0 +1,9 @@ +# ForeignCallInput + +```ts +type ForeignCallInput: string[]; +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.3/noir_js/reference/noir_js/type-aliases/ForeignCallOutput.md b/docs/versioned_docs/version-v0.19.3/noir_js/reference/noir_js/type-aliases/ForeignCallOutput.md new file mode 100644 index 00000000000..b71fb78a946 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/noir_js/reference/noir_js/type-aliases/ForeignCallOutput.md @@ -0,0 +1,9 @@ +# ForeignCallOutput + +```ts +type ForeignCallOutput: string | string[]; +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.3/noir_js/reference/noir_js/type-aliases/ProofData.md b/docs/versioned_docs/version-v0.19.3/noir_js/reference/noir_js/type-aliases/ProofData.md new file mode 100644 index 00000000000..4aeff73d3e4 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/noir_js/reference/noir_js/type-aliases/ProofData.md @@ -0,0 +1,20 @@ +# ProofData + +```ts +type ProofData: object; +``` + +## Description + +The representation of a proof + +## Type declaration + +| Member | Type | Description | +| :------ | :------ | :------ | +| `proof` | `Uint8Array` | **Description**

An byte array representing the proof | +| `publicInputs` | `Uint8Array`[] | **Description**

Public inputs of a proof | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.3/noir_js/reference/noir_js/type-aliases/WitnessMap.md b/docs/versioned_docs/version-v0.19.3/noir_js/reference/noir_js/type-aliases/WitnessMap.md new file mode 100644 index 00000000000..258c46f9d0c --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/noir_js/reference/noir_js/type-aliases/WitnessMap.md @@ -0,0 +1,9 @@ +# WitnessMap + +```ts +type WitnessMap: Map; +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.3/noir_js/reference/noir_js/typedoc-sidebar.cjs b/docs/versioned_docs/version-v0.19.3/noir_js/reference/noir_js/typedoc-sidebar.cjs new file mode 100644 index 00000000000..c18318850d0 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/noir_js/reference/noir_js/typedoc-sidebar.cjs @@ -0,0 +1,4 @@ +// @ts-check +/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ +const typedocSidebar = { items: [{"type":"category","label":"Classes","items":[{"type":"doc","id":"noir_js/reference/noir_js/classes/Noir","label":"Noir"}]},{"type":"category","label":"Type Aliases","items":[{"type":"doc","id":"noir_js/reference/noir_js/type-aliases/CompiledCircuit","label":"CompiledCircuit"},{"type":"doc","id":"noir_js/reference/noir_js/type-aliases/ForeignCallHandler","label":"ForeignCallHandler"},{"type":"doc","id":"noir_js/reference/noir_js/type-aliases/ForeignCallInput","label":"ForeignCallInput"},{"type":"doc","id":"noir_js/reference/noir_js/type-aliases/ForeignCallOutput","label":"ForeignCallOutput"},{"type":"doc","id":"noir_js/reference/noir_js/type-aliases/ProofData","label":"ProofData"},{"type":"doc","id":"noir_js/reference/noir_js/type-aliases/WitnessMap","label":"WitnessMap"}]},{"type":"category","label":"Functions","items":[{"type":"doc","id":"noir_js/reference/noir_js/functions/and","label":"and"},{"type":"doc","id":"noir_js/reference/noir_js/functions/blake2s256","label":"blake2s256"},{"type":"doc","id":"noir_js/reference/noir_js/functions/ecdsa_secp256k1_verify","label":"ecdsa_secp256k1_verify"},{"type":"doc","id":"noir_js/reference/noir_js/functions/ecdsa_secp256r1_verify","label":"ecdsa_secp256r1_verify"},{"type":"doc","id":"noir_js/reference/noir_js/functions/keccak256","label":"keccak256"},{"type":"doc","id":"noir_js/reference/noir_js/functions/sha256","label":"sha256"},{"type":"doc","id":"noir_js/reference/noir_js/functions/xor","label":"xor"}]}]}; +module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.19.3/standard_library/black_box_fns.md b/docs/versioned_docs/version-v0.19.3/standard_library/black_box_fns.md new file mode 100644 index 00000000000..1dfabfe8f22 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/standard_library/black_box_fns.md @@ -0,0 +1,46 @@ +--- +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 Hash](./cryptographic_primitives/hashes#pedersen_hash) +- [Pedersen Commitment](./cryptographic_primitives/hashes#pedersen_commitment) +- [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-v0.19.3/standard_library/cryptographic_primitives.md b/docs/versioned_docs/version-v0.19.3/standard_library/cryptographic_primitives.md new file mode 100644 index 00000000000..2df4f929474 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/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-v0.19.3/standard_library/cryptographic_primitives/00_hashes.mdx b/docs/versioned_docs/version-v0.19.3/standard_library/cryptographic_primitives/00_hashes.mdx new file mode 100644 index 00000000000..76745196681 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/standard_library/cryptographic_primitives/00_hashes.mdx @@ -0,0 +1,167 @@ +--- +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 '@site/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_hash + +Given an array of Fields, returns the Pedersen hash. + +```rust +fn pedersen_hash(_input : [Field]) -> Field +``` + +example: + +```rust +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let hash = std::hash::pedersen_hash(x); +} +``` + + + + + +## pedersen_commitment + +Given an array of Fields, returns the Pedersen commitment. + +```rust +fn pedersen_commitment(_input : [Field]) -> [Field; 2] +``` + +example: + +```rust +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let commitment = std::hash::pedersen_commitment(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::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-v0.19.3/standard_library/cryptographic_primitives/01_scalar.mdx b/docs/versioned_docs/version-v0.19.3/standard_library/cryptographic_primitives/01_scalar.mdx new file mode 100644 index 00000000000..c7eed820a80 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/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 '@site/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-v0.19.3/standard_library/cryptographic_primitives/02_schnorr.mdx b/docs/versioned_docs/version-v0.19.3/standard_library/cryptographic_primitives/02_schnorr.mdx new file mode 100644 index 00000000000..c184ce28120 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/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 '@site/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-v0.19.3/standard_library/cryptographic_primitives/03_ecdsa_sig_verification.mdx b/docs/versioned_docs/version-v0.19.3/standard_library/cryptographic_primitives/03_ecdsa_sig_verification.mdx new file mode 100644 index 00000000000..72bce984821 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/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 '@site/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-v0.19.3/standard_library/cryptographic_primitives/04_ec_primitives.md b/docs/versioned_docs/version-v0.19.3/standard_library/cryptographic_primitives/04_ec_primitives.md new file mode 100644 index 00000000000..6e6b19b6861 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/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-v0.19.3/standard_library/cryptographic_primitives/05_eddsa.mdx b/docs/versioned_docs/version-v0.19.3/standard_library/cryptographic_primitives/05_eddsa.mdx new file mode 100644 index 00000000000..9a5beb55ee9 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/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 '@site/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-v0.19.3/standard_library/logging.md b/docs/versioned_docs/version-v0.19.3/standard_library/logging.md new file mode 100644 index 00000000000..7e2fd9b9aff --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/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-v0.19.3/standard_library/merkle_trees.md b/docs/versioned_docs/version-v0.19.3/standard_library/merkle_trees.md new file mode 100644 index 00000000000..dc383a1426b --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/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_hash). + +```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-v0.19.3/standard_library/options.md b/docs/versioned_docs/version-v0.19.3/standard_library/options.md new file mode 100644 index 00000000000..3d3139fb98b --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/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-v0.19.3/standard_library/recursion.md b/docs/versioned_docs/version-v0.19.3/standard_library/recursion.md new file mode 100644 index 00000000000..ff4c63acaa7 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/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-v0.19.3/standard_library/zeroed.md b/docs/versioned_docs/version-v0.19.3/standard_library/zeroed.md new file mode 100644 index 00000000000..97dab02dac2 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.3/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_docs/version-v0.19.4/examples/merkle-proof.mdx b/docs/versioned_docs/version-v0.19.4/examples/merkle-proof.mdx new file mode 100644 index 00000000000..832fb4bb55e --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/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_hash` +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-v0.19.4/getting_started/00_nargo_installation.md b/docs/versioned_docs/version-v0.19.4/getting_started/00_nargo_installation.md new file mode 100644 index 00000000000..725c5f4d373 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/getting_started/00_nargo_installation.md @@ -0,0 +1,249 @@ +--- +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: Compile from Source](#option-3-compile-from-source) +- [Option 4: WSL for Windows](#option-4-wsl-for-windows) + +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 +``` + +##### 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 `rust-toolchain.toml`, which is 1.71.1 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! + +### Option 4: WSL (for Windows) + +The default backend for Noir (Barretenberg) doesn't provide Windows binaries at this time. For that reason, Noir cannot be installed nativerly. However, it is available by using Windows Subsystem for Linux (WSL). + +Step 1: Follow the instructions [here](https://learn.microsoft.com/en-us/windows/wsl/install) to install and run WSL. + +step 2: Follow the [Noirup instructions](#option-1-noirup). + +## 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-v0.19.4/getting_started/01_hello_world.md b/docs/versioned_docs/version-v0.19.4/getting_started/01_hello_world.md new file mode 100644 index 00000000000..8b4416beba1 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/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-v0.19.4/getting_started/02_breakdown.md b/docs/versioned_docs/version-v0.19.4/getting_started/02_breakdown.md new file mode 100644 index 00000000000..9a17f5d6360 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/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` - specifies the version of the compiler to use. This is enforced by the compiler and follow's [Rust's versioning](https://doc.rust-lang.org/cargo/reference/manifest.html#the-version-field), so a `compiler_version = 0.18.0` will enforce Nargo version 0.18.0, `compiler_version = ^0.18.0` will enforce anything above 0.18.0 but below 0.19.0, etc. For more information, see how [Rust handles these operators](https://docs.rs/semver/latest/semver/enum.Op.html) +- `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-v0.19.4/index.md b/docs/versioned_docs/version-v0.19.4/index.md new file mode 100644 index 00000000000..75e1abf2932 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/index.md @@ -0,0 +1,100 @@ +--- +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 Commitment +- Pedersen Hash +- 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-v0.19.4/language_concepts/01_functions.md b/docs/versioned_docs/version-v0.19.4/language_concepts/01_functions.md new file mode 100644 index 00000000000..5eb22170e54 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/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 : 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 : 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-v0.19.4/language_concepts/02_control_flow.md b/docs/versioned_docs/version-v0.19.4/language_concepts/02_control_flow.md new file mode 100644 index 00000000000..a7f85360197 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/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-v0.19.4/language_concepts/03_ops.md b/docs/versioned_docs/version-v0.19.4/language_concepts/03_ops.md new file mode 100644 index 00000000000..da02b126059 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/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-v0.19.4/language_concepts/04_assert.md b/docs/versioned_docs/version-v0.19.4/language_concepts/04_assert.md new file mode 100644 index 00000000000..7427ec6cc63 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/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-v0.19.4/language_concepts/05_unconstrained.md b/docs/versioned_docs/version-v0.19.4/language_concepts/05_unconstrained.md new file mode 100644 index 00000000000..6b621eda3eb --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/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-v0.19.4/language_concepts/06_generics.md b/docs/versioned_docs/version-v0.19.4/language_concepts/06_generics.md new file mode 100644 index 00000000000..9fb4177c2a8 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/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/tooling/nargo_cli/tests/execution_success/generics/src/main.nr). diff --git a/docs/versioned_docs/version-v0.19.4/language_concepts/07_mutability.md b/docs/versioned_docs/version-v0.19.4/language_concepts/07_mutability.md new file mode 100644 index 00000000000..ad902c42c9b --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/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() -> pub 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-v0.19.4/language_concepts/08_lambdas.md b/docs/versioned_docs/version-v0.19.4/language_concepts/08_lambdas.md new file mode 100644 index 00000000000..ae1e6aecab1 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/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-v0.19.4/language_concepts/09_comments.md b/docs/versioned_docs/version-v0.19.4/language_concepts/09_comments.md new file mode 100644 index 00000000000..3bb4d2f25a4 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/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-v0.19.4/language_concepts/10_distinct.md b/docs/versioned_docs/version-v0.19.4/language_concepts/10_distinct.md new file mode 100644 index 00000000000..e7ff7f5017a --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/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-v0.19.4/language_concepts/11_shadowing.md b/docs/versioned_docs/version-v0.19.4/language_concepts/11_shadowing.md new file mode 100644 index 00000000000..efd743e764f --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/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-v0.19.4/language_concepts/data_types.md b/docs/versioned_docs/version-v0.19.4/language_concepts/data_types.md new file mode 100644 index 00000000000..d546cc463a8 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/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-v0.19.4/language_concepts/data_types/00_fields.md b/docs/versioned_docs/version-v0.19.4/language_concepts/data_types/00_fields.md new file mode 100644 index 00000000000..658a0441ffb --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/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-v0.19.4/language_concepts/data_types/01_integers.md b/docs/versioned_docs/version-v0.19.4/language_concepts/data_types/01_integers.md new file mode 100644 index 00000000000..b1e7ad11bfd --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/language_concepts/data_types/01_integers.md @@ -0,0 +1,112 @@ +--- +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 supports arbitrarily-sized, both unsigned and signed integer types. + +:::info + +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. + +::: + +## Unsigned Integers + +An unsigned integer type is specified first with the letter `u` (indicating its unsigned nature) followed by its bit size (e.g. `8`): + +```rust +fn main() { + let x: u8 = 1; + let y: u8 = 1; + let z = x + y; + assert (z == 2); +} +``` + +The bit size determines the maximum value the integer type can store. For example, a `u8` variable can store a value in the range of 0 to 255 (i.e. $\\2^{8}-1\\$). + +## Signed Integers + +A signed integer type is specified first with the letter `i` (which stands for integer) followed by its bit size (e.g. `8`): + +```rust +fn main() { + let x: i8 = -1; + let y: i8 = -1; + let z = x + y; + assert (z == -2); +} +``` + +The bit size determines the maximum and minimum range of value the integer type can store. For example, an `i8` variable can store a value in the range of -128 to 127 (i.e. $\\-2^{7}\\$ to $\\2^{7}-1\\$). + +:::tip + +If you are using the default proving backend with Noir, both even (e.g. _u2_, _i2_) and odd (e.g. _u3_, _i3_) arbitrarily-sized integer types up to 127 bits (i.e. _u127_ and _i127_) are supported. + +::: + +## Overflows + +Computations that exceed the type boundaries will result in overflow errors. This happens with both signed and unsigned integers. For example, attempting to prove: + +```rust +fn main(x: u8, y: u8) { + let z = x + y; +} +``` + +With: + +```toml +x = "255" +y = "1" +``` + +Would result in: + +``` +$ nargo prove +error: Assertion failed: 'attempt to add with overflow' +┌─ ~/src/main.nr:9:13 +│ +│ let z = x + y; +│ ----- +│ += Call stack: + ... +``` + +A similar error would happen with signed integers: + +```rust +fn main() { + let x: i8 = -118; + let y: i8 = -11; + let z = x + y; +} +``` + +### Wrapping methods + +Although integer overflow is expected to error, some use-cases rely on wrapping. For these use-cases, the standard library provides `wrapping` variants of certain common operations: + +```rust +fn wrapping_add(x: T, y: T) -> T; +fn wrapping_sub(x: T, y: T) -> T; +fn wrapping_mul(x: T, y: T) -> T; +``` + +Example of how it is used: + +```rust +use dep::std; + +fn main(x: u8, y: u8) -> pub u8 { + std::wrapping_add(x + y) +} +``` diff --git a/docs/versioned_docs/version-v0.19.4/language_concepts/data_types/02_booleans.md b/docs/versioned_docs/version-v0.19.4/language_concepts/data_types/02_booleans.md new file mode 100644 index 00000000000..885db167d83 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/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-v0.19.4/language_concepts/data_types/03_strings.md b/docs/versioned_docs/version-v0.19.4/language_concepts/data_types/03_strings.md new file mode 100644 index 00000000000..c42f34ec3ad --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/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-v0.19.4/language_concepts/data_types/04_arrays.md b/docs/versioned_docs/version-v0.19.4/language_concepts/data_types/04_arrays.md new file mode 100644 index 00000000000..bdbd1798bef --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/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-v0.19.4/language_concepts/data_types/05_slices.mdx b/docs/versioned_docs/version-v0.19.4/language_concepts/data_types/05_slices.mdx new file mode 100644 index 00000000000..1be0ec4a137 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/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 '@site/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-v0.19.4/language_concepts/data_types/06_vectors.mdx b/docs/versioned_docs/version-v0.19.4/language_concepts/data_types/06_vectors.mdx new file mode 100644 index 00000000000..4617e90d038 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/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 '@site/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-v0.19.4/language_concepts/data_types/07_tuples.md b/docs/versioned_docs/version-v0.19.4/language_concepts/data_types/07_tuples.md new file mode 100644 index 00000000000..5f6cab974a8 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/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-v0.19.4/language_concepts/data_types/08_structs.md b/docs/versioned_docs/version-v0.19.4/language_concepts/data_types/08_structs.md new file mode 100644 index 00000000000..35421734639 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/language_concepts/data_types/08_structs.md @@ -0,0 +1,69 @@ +--- +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. diff --git a/docs/versioned_docs/version-v0.19.4/language_concepts/data_types/09_references.md b/docs/versioned_docs/version-v0.19.4/language_concepts/data_types/09_references.md new file mode 100644 index 00000000000..b0c35ce2cb9 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/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-v0.19.4/language_concepts/data_types/10_function_types.md b/docs/versioned_docs/version-v0.19.4/language_concepts/data_types/10_function_types.md new file mode 100644 index 00000000000..1ec92efd594 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/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-v0.19.4/migration_notes.md b/docs/versioned_docs/version-v0.19.4/migration_notes.md new file mode 100644 index 00000000000..e87eb1feaba --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/migration_notes.md @@ -0,0 +1,91 @@ +--- +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.19 + +### Enforcing `compiler_version` + +From this version on, the compiler will check for the `compiler_version` field in `Nargo.toml`, and will error if it doesn't match the current Nargo version in use. + +To update, please make sure this field in `Nargo.toml` matches the output of `nargo --version`. + +## ≥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-v0.19.4/modules_packages_crates/crates_and_packages.md b/docs/versioned_docs/version-v0.19.4/modules_packages_crates/crates_and_packages.md new file mode 100644 index 00000000000..fb83a33d94e --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/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-v0.19.4/modules_packages_crates/dependencies.md b/docs/versioned_docs/version-v0.19.4/modules_packages_crates/dependencies.md new file mode 100644 index 00000000000..75f95aaa305 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/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-v0.19.4/modules_packages_crates/modules.md b/docs/versioned_docs/version-v0.19.4/modules_packages_crates/modules.md new file mode 100644 index 00000000000..147c9b284e8 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/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-v0.19.4/modules_packages_crates/workspaces.md b/docs/versioned_docs/version-v0.19.4/modules_packages_crates/workspaces.md new file mode 100644 index 00000000000..a979ef9f0a5 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/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. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.19.4/nargo/01_commands.md b/docs/versioned_docs/version-v0.19.4/nargo/01_commands.md new file mode 100644 index 00000000000..65e2bdb44e3 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/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-v0.19.4/nargo/02_testing.md b/docs/versioned_docs/version-v0.19.4/nargo/02_testing.md new file mode 100644 index 00000000000..da767274efd --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/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-v0.19.4/nargo/03_solidity_verifier.md b/docs/versioned_docs/version-v0.19.4/nargo/03_solidity_verifier.md new file mode 100644 index 00000000000..9ac60cb0ba7 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/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-v0.19.4/nargo/04_language_server.md b/docs/versioned_docs/version-v0.19.4/nargo/04_language_server.md new file mode 100644 index 00000000000..48c01465f6e --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/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](@site/static/img/codelens_compile_execute.png) +![Run test](@site/static/img/codelens_run_test.png) + +You should also see your tests in the `testing` panel: + +![Testing panel](@site/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-v0.19.4/noir_js/getting_started/01_tiny_noir_app.md b/docs/versioned_docs/version-v0.19.4/noir_js/getting_started/01_tiny_noir_app.md new file mode 100644 index 00000000000..c51ed61de52 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/noir_js/getting_started/01_tiny_noir_app.md @@ -0,0 +1,260 @@ +--- +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. + +You can find the complete app code for this guide [here](https://github.com/noir-lang/tiny-noirjs-app). + +## 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](@site/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! + +You can find the complete app code for this guide [here](https://github.com/noir-lang/tiny-noirjs-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-v0.19.4/noir_js/noir_js.md b/docs/versioned_docs/version-v0.19.4/noir_js/noir_js.md new file mode 100644 index 00000000000..f895b22eaf8 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/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/noir_js/classes/Noir.md) sections. diff --git a/docs/versioned_docs/version-v0.19.4/noir_js/reference/backend_barretenberg/.nojekyll b/docs/versioned_docs/version-v0.19.4/noir_js/reference/backend_barretenberg/.nojekyll new file mode 100644 index 00000000000..e2ac6616add --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/noir_js/reference/backend_barretenberg/.nojekyll @@ -0,0 +1 @@ +TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.19.4/noir_js/reference/backend_barretenberg/classes/BarretenbergBackend.md b/docs/versioned_docs/version-v0.19.4/noir_js/reference/backend_barretenberg/classes/BarretenbergBackend.md new file mode 100644 index 00000000000..5cbe9421b92 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/noir_js/reference/backend_barretenberg/classes/BarretenbergBackend.md @@ -0,0 +1,185 @@ +# BarretenbergBackend + +## Implements + +- [`Backend`](../interfaces/Backend.md) + +## Constructors + +### new BarretenbergBackend(acirCircuit, options) + +```ts +new BarretenbergBackend(acirCircuit, options): BarretenbergBackend +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `acirCircuit` | [`CompiledCircuit`](../type-aliases/CompiledCircuit.md) | +| `options` | [`BackendOptions`](../type-aliases/BackendOptions.md) | + +#### Returns + +[`BarretenbergBackend`](BarretenbergBackend.md) + +## Methods + +### destroy() + +```ts +destroy(): Promise +``` + +#### Returns + +`Promise`\<`void`\> + +#### Implementation of + +[`Backend`](../interfaces/Backend.md).[`destroy`](../interfaces/Backend.md#destroy) + +#### Description + +Destroys the backend + +*** + +### generateFinalProof() + +```ts +generateFinalProof(decompressedWitness): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `decompressedWitness` | `Uint8Array` | + +#### Returns + +`Promise`\<[`ProofData`](../type-aliases/ProofData.md)\> + +#### Implementation of + +[`Backend`](../interfaces/Backend.md).[`generateFinalProof`](../interfaces/Backend.md#generatefinalproof) + +#### Description + +Generates a final proof (not meant to be verified in another circuit) + +*** + +### generateIntermediateProof() + +```ts +generateIntermediateProof(witness): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `witness` | `Uint8Array` | + +#### Returns + +`Promise`\<[`ProofData`](../type-aliases/ProofData.md)\> + +#### Implementation of + +[`Backend`](../interfaces/Backend.md).[`generateIntermediateProof`](../interfaces/Backend.md#generateintermediateproof) + +#### Example + +```typescript +const intermediateProof = await backend.generateIntermediateProof(witness); +``` + +*** + +### generateIntermediateProofArtifacts() + +```ts +generateIntermediateProofArtifacts(proofData, numOfPublicInputs): Promise +``` + +#### Parameters + +| Parameter | Type | Default value | +| :------ | :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | `undefined` | +| `numOfPublicInputs` | `number` | `0` | + +#### Returns + +`Promise`\<`object`\> + +#### Implementation of + +[`Backend`](../interfaces/Backend.md).[`generateIntermediateProofArtifacts`](../interfaces/Backend.md#generateintermediateproofartifacts) + +#### Example + +```typescript +const artifacts = await backend.generateIntermediateProofArtifacts(proof, numOfPublicInputs); +``` + +*** + +### verifyFinalProof() + +```ts +verifyFinalProof(proofData): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | + +#### Returns + +`Promise`\<`boolean`\> + +#### Implementation of + +[`Backend`](../interfaces/Backend.md).[`verifyFinalProof`](../interfaces/Backend.md#verifyfinalproof) + +#### Description + +Verifies a final proof + +*** + +### verifyIntermediateProof() + +```ts +verifyIntermediateProof(proofData): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | + +#### Returns + +`Promise`\<`boolean`\> + +#### Implementation of + +[`Backend`](../interfaces/Backend.md).[`verifyIntermediateProof`](../interfaces/Backend.md#verifyintermediateproof) + +#### Example + +```typescript +const isValidIntermediate = await backend.verifyIntermediateProof(proof); +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.4/noir_js/reference/backend_barretenberg/index.md b/docs/versioned_docs/version-v0.19.4/noir_js/reference/backend_barretenberg/index.md new file mode 100644 index 00000000000..93b248b0f65 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/noir_js/reference/backend_barretenberg/index.md @@ -0,0 +1,45 @@ +# Backend Barretenberg + +## Exports + +### Classes + +| Class | Description | +| :------ | :------ | +| [BarretenbergBackend](classes/BarretenbergBackend.md) | - | + +### Interfaces + +| Interface | Description | +| :------ | :------ | +| [Backend](interfaces/Backend.md) | - | + +### Type Aliases + +| Type alias | Description | +| :------ | :------ | +| [BackendOptions](type-aliases/BackendOptions.md) | - | +| [CompiledCircuit](type-aliases/CompiledCircuit.md) | - | +| [ProofData](type-aliases/ProofData.md) | - | + +## Functions + +### flattenPublicInputs() + +```ts +flattenPublicInputs(publicInputs): string[] +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `publicInputs` | `WitnessMap` | + +#### Returns + +`string`[] + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.4/noir_js/reference/backend_barretenberg/interfaces/Backend.md b/docs/versioned_docs/version-v0.19.4/noir_js/reference/backend_barretenberg/interfaces/Backend.md new file mode 100644 index 00000000000..3eb9645c8d2 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/noir_js/reference/backend_barretenberg/interfaces/Backend.md @@ -0,0 +1,132 @@ +# Backend + +## Methods + +### destroy() + +```ts +destroy(): Promise +``` + +#### Returns + +`Promise`\<`void`\> + +#### Description + +Destroys the backend + +*** + +### generateFinalProof() + +```ts +generateFinalProof(decompressedWitness): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `decompressedWitness` | `Uint8Array` | + +#### Returns + +`Promise`\<[`ProofData`](../type-aliases/ProofData.md)\> + +#### Description + +Generates a final proof (not meant to be verified in another circuit) + +*** + +### generateIntermediateProof() + +```ts +generateIntermediateProof(decompressedWitness): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `decompressedWitness` | `Uint8Array` | + +#### Returns + +`Promise`\<[`ProofData`](../type-aliases/ProofData.md)\> + +#### Description + +Generates an intermediate proof (meant to be verified in another circuit) + +*** + +### generateIntermediateProofArtifacts() + +```ts +generateIntermediateProofArtifacts(proofData, numOfPublicInputs): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | +| `numOfPublicInputs` | `number` | + +#### Returns + +`Promise`\<`object`\> + +#### Description + +Retrieves the artifacts from a proof in the Field format + +*** + +### verifyFinalProof() + +```ts +verifyFinalProof(proofData): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | + +#### Returns + +`Promise`\<`boolean`\> + +#### Description + +Verifies a final proof + +*** + +### verifyIntermediateProof() + +```ts +verifyIntermediateProof(proofData): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | + +#### Returns + +`Promise`\<`boolean`\> + +#### Description + +Verifies an intermediate proof + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.4/noir_js/reference/backend_barretenberg/type-aliases/BackendOptions.md b/docs/versioned_docs/version-v0.19.4/noir_js/reference/backend_barretenberg/type-aliases/BackendOptions.md new file mode 100644 index 00000000000..266ade75d17 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/noir_js/reference/backend_barretenberg/type-aliases/BackendOptions.md @@ -0,0 +1,19 @@ +# BackendOptions + +```ts +type BackendOptions: object; +``` + +## Description + +An options object, currently only used to specify the number of threads to use. + +## Type declaration + +| Member | Type | Description | +| :------ | :------ | :------ | +| `threads` | `number` | **Description**

Number of threads | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.4/noir_js/reference/backend_barretenberg/type-aliases/CompiledCircuit.md b/docs/versioned_docs/version-v0.19.4/noir_js/reference/backend_barretenberg/type-aliases/CompiledCircuit.md new file mode 100644 index 00000000000..34e0dd04205 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/noir_js/reference/backend_barretenberg/type-aliases/CompiledCircuit.md @@ -0,0 +1,20 @@ +# CompiledCircuit + +```ts +type CompiledCircuit: object; +``` + +## Description + +The representation of a compiled circuit + +## Type declaration + +| Member | Type | Description | +| :------ | :------ | :------ | +| `abi` | `Abi` | **Description**

ABI representation of the circuit | +| `bytecode` | `string` | **Description**

The bytecode of the circuit | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.4/noir_js/reference/backend_barretenberg/type-aliases/ProofData.md b/docs/versioned_docs/version-v0.19.4/noir_js/reference/backend_barretenberg/type-aliases/ProofData.md new file mode 100644 index 00000000000..3eb360a78f1 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/noir_js/reference/backend_barretenberg/type-aliases/ProofData.md @@ -0,0 +1,20 @@ +# ProofData + +```ts +type ProofData: object; +``` + +## Description + +The representation of a proof + +## Type declaration + +| Member | Type | Description | +| :------ | :------ | :------ | +| `proof` | `Uint8Array` | **Description**

An byte array representing the proof | +| `publicInputs` | `WitnessMap` | **Description**

Public inputs of a proof | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.4/noir_js/reference/backend_barretenberg/typedoc-sidebar.cjs b/docs/versioned_docs/version-v0.19.4/noir_js/reference/backend_barretenberg/typedoc-sidebar.cjs new file mode 100644 index 00000000000..04e662c845f --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/noir_js/reference/backend_barretenberg/typedoc-sidebar.cjs @@ -0,0 +1,4 @@ +// @ts-check +/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ +const typedocSidebar = { items: [{"type":"category","label":"Classes","items":[{"type":"doc","id":"noir_js/reference/backend_barretenberg/classes/BarretenbergBackend","label":"BarretenbergBackend"}]},{"type":"category","label":"Interfaces","items":[{"type":"doc","id":"noir_js/reference/backend_barretenberg/interfaces/Backend","label":"Backend"}]},{"type":"category","label":"Type Aliases","items":[{"type":"doc","id":"noir_js/reference/backend_barretenberg/type-aliases/BackendOptions","label":"BackendOptions"},{"type":"doc","id":"noir_js/reference/backend_barretenberg/type-aliases/CompiledCircuit","label":"CompiledCircuit"},{"type":"doc","id":"noir_js/reference/backend_barretenberg/type-aliases/ProofData","label":"ProofData"}]}]}; +module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/.nojekyll b/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/.nojekyll new file mode 100644 index 00000000000..e2ac6616add --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/.nojekyll @@ -0,0 +1 @@ +TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/classes/Noir.md b/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/classes/Noir.md new file mode 100644 index 00000000000..e54116fb1d8 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/classes/Noir.md @@ -0,0 +1,131 @@ +# Noir + +## Constructors + +### new Noir(circuit, backend) + +```ts +new Noir(circuit, backend?): Noir +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `circuit` | [`CompiledCircuit`](../type-aliases/CompiledCircuit.md) | +| `backend`? | `Backend` | + +#### Returns + +[`Noir`](Noir.md) + +## Methods + +### destroy() + +```ts +destroy(): Promise +``` + +#### Returns + +`Promise`\<`void`\> + +#### Description + +Destroys the underlying backend instance. + +#### Example + +```typescript +await noir.destroy(); +``` + +*** + +### execute() + +```ts +execute(inputs, foreignCallHandler?): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `inputs` | [`InputMap`](../type-aliases/InputMap.md) | +| `foreignCallHandler`? | [`ForeignCallHandler`](../type-aliases/ForeignCallHandler.md) | + +#### Returns + +`Promise`\<`object`\> + +#### Description + +Allows to execute a circuit to get its witness and return value. + +#### Example + +```typescript +async execute(inputs) +``` + +*** + +### generateFinalProof() + +```ts +generateFinalProof(inputs): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `inputs` | [`InputMap`](../type-aliases/InputMap.md) | + +#### Returns + +`Promise`\<[`ProofData`](../type-aliases/ProofData.md)\> + +#### Description + +Generates a witness and a proof given an object as input. + +#### Example + +```typescript +async generateFinalproof(input) +``` + +*** + +### verifyFinalProof() + +```ts +verifyFinalProof(proofData): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | + +#### Returns + +`Promise`\<`boolean`\> + +#### Description + +Instantiates the verification key and verifies a proof. + +#### Example + +```typescript +async verifyFinalProof(proof) +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/functions/and.md b/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/functions/and.md new file mode 100644 index 00000000000..c783283e396 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/functions/and.md @@ -0,0 +1,22 @@ +# and() + +```ts +and(lhs, rhs): string +``` + +Performs a bitwise AND operation between `lhs` and `rhs` + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `lhs` | `string` | | +| `rhs` | `string` | | + +## Returns + +`string` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/functions/blake2s256.md b/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/functions/blake2s256.md new file mode 100644 index 00000000000..7882d0da8d5 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/functions/blake2s256.md @@ -0,0 +1,21 @@ +# blake2s256() + +```ts +blake2s256(inputs): Uint8Array +``` + +Calculates the Blake2s256 hash of the input bytes + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `inputs` | `Uint8Array` | | + +## Returns + +`Uint8Array` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/functions/ecdsa_secp256k1_verify.md b/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/functions/ecdsa_secp256k1_verify.md new file mode 100644 index 00000000000..0ba5783f0d5 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/functions/ecdsa_secp256k1_verify.md @@ -0,0 +1,29 @@ +# ecdsa\_secp256k1\_verify() + +```ts +ecdsa_secp256k1_verify( + hashed_msg, + public_key_x_bytes, + public_key_y_bytes, + signature): boolean +``` + +Calculates the Blake2s256 hash of the input bytes and represents these as a single field element. +Verifies a ECDSA signature over the secp256k1 curve. + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `hashed_msg` | `Uint8Array` | | +| `public_key_x_bytes` | `Uint8Array` | | +| `public_key_y_bytes` | `Uint8Array` | | +| `signature` | `Uint8Array` | | + +## Returns + +`boolean` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/functions/ecdsa_secp256r1_verify.md b/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/functions/ecdsa_secp256r1_verify.md new file mode 100644 index 00000000000..0b20ff68957 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/functions/ecdsa_secp256r1_verify.md @@ -0,0 +1,28 @@ +# ecdsa\_secp256r1\_verify() + +```ts +ecdsa_secp256r1_verify( + hashed_msg, + public_key_x_bytes, + public_key_y_bytes, + signature): boolean +``` + +Verifies a ECDSA signature over the secp256r1 curve. + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `hashed_msg` | `Uint8Array` | | +| `public_key_x_bytes` | `Uint8Array` | | +| `public_key_y_bytes` | `Uint8Array` | | +| `signature` | `Uint8Array` | | + +## Returns + +`boolean` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/functions/keccak256.md b/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/functions/keccak256.md new file mode 100644 index 00000000000..d10f155ce86 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/functions/keccak256.md @@ -0,0 +1,21 @@ +# keccak256() + +```ts +keccak256(inputs): Uint8Array +``` + +Calculates the Keccak256 hash of the input bytes + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `inputs` | `Uint8Array` | | + +## Returns + +`Uint8Array` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/functions/sha256.md b/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/functions/sha256.md new file mode 100644 index 00000000000..6ba4ecac022 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/functions/sha256.md @@ -0,0 +1,21 @@ +# sha256() + +```ts +sha256(inputs): Uint8Array +``` + +Calculates the SHA256 hash of the input bytes + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `inputs` | `Uint8Array` | | + +## Returns + +`Uint8Array` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/functions/xor.md b/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/functions/xor.md new file mode 100644 index 00000000000..8d762b895d3 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/functions/xor.md @@ -0,0 +1,22 @@ +# xor() + +```ts +xor(lhs, rhs): string +``` + +Performs a bitwise XOR operation between `lhs` and `rhs` + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `lhs` | `string` | | +| `rhs` | `string` | | + +## Returns + +`string` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/index.md b/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/index.md new file mode 100644 index 00000000000..348453c0059 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/index.md @@ -0,0 +1,37 @@ +# Noir JS + +## Exports + +### Classes + +| Class | Description | +| :------ | :------ | +| [Noir](classes/Noir.md) | - | + +### Type Aliases + +| Type alias | Description | +| :------ | :------ | +| [CompiledCircuit](type-aliases/CompiledCircuit.md) | - | +| [ForeignCallHandler](type-aliases/ForeignCallHandler.md) | A callback which performs an foreign call and returns the response. | +| [ForeignCallInput](type-aliases/ForeignCallInput.md) | - | +| [ForeignCallOutput](type-aliases/ForeignCallOutput.md) | - | +| [InputMap](type-aliases/InputMap.md) | - | +| [ProofData](type-aliases/ProofData.md) | - | +| [WitnessMap](type-aliases/WitnessMap.md) | - | + +### Functions + +| Function | Description | +| :------ | :------ | +| [and](functions/and.md) | Performs a bitwise AND operation between `lhs` and `rhs` | +| [blake2s256](functions/blake2s256.md) | Calculates the Blake2s256 hash of the input bytes | +| [ecdsa\_secp256k1\_verify](functions/ecdsa_secp256k1_verify.md) | Calculates the Blake2s256 hash of the input bytes and represents these as a single field element. | +| [ecdsa\_secp256r1\_verify](functions/ecdsa_secp256r1_verify.md) | Verifies a ECDSA signature over the secp256r1 curve. | +| [keccak256](functions/keccak256.md) | Calculates the Keccak256 hash of the input bytes | +| [sha256](functions/sha256.md) | Calculates the SHA256 hash of the input bytes | +| [xor](functions/xor.md) | Performs a bitwise XOR operation between `lhs` and `rhs` | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/type-aliases/CompiledCircuit.md b/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/type-aliases/CompiledCircuit.md new file mode 100644 index 00000000000..34e0dd04205 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/type-aliases/CompiledCircuit.md @@ -0,0 +1,20 @@ +# CompiledCircuit + +```ts +type CompiledCircuit: object; +``` + +## Description + +The representation of a compiled circuit + +## Type declaration + +| Member | Type | Description | +| :------ | :------ | :------ | +| `abi` | `Abi` | **Description**

ABI representation of the circuit | +| `bytecode` | `string` | **Description**

The bytecode of the circuit | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/type-aliases/ForeignCallHandler.md b/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/type-aliases/ForeignCallHandler.md new file mode 100644 index 00000000000..812b8b16481 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/type-aliases/ForeignCallHandler.md @@ -0,0 +1,24 @@ +# ForeignCallHandler + +```ts +type ForeignCallHandler: (name, inputs) => Promise; +``` + +A callback which performs an foreign call and returns the response. + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `name` | `string` | The identifier for the type of foreign call being performed. | +| `inputs` | [`ForeignCallInput`](ForeignCallInput.md)[] | An array of hex encoded inputs to the foreign call. | + +## Returns + +`Promise`\<[`ForeignCallOutput`](ForeignCallOutput.md)[]\> + +outputs - An array of hex encoded outputs containing the results of the foreign call. + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/type-aliases/ForeignCallInput.md b/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/type-aliases/ForeignCallInput.md new file mode 100644 index 00000000000..dd95809186a --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/type-aliases/ForeignCallInput.md @@ -0,0 +1,9 @@ +# ForeignCallInput + +```ts +type ForeignCallInput: string[]; +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/type-aliases/ForeignCallOutput.md b/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/type-aliases/ForeignCallOutput.md new file mode 100644 index 00000000000..b71fb78a946 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/type-aliases/ForeignCallOutput.md @@ -0,0 +1,9 @@ +# ForeignCallOutput + +```ts +type ForeignCallOutput: string | string[]; +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/type-aliases/InputMap.md b/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/type-aliases/InputMap.md new file mode 100644 index 00000000000..c714e999d93 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/type-aliases/InputMap.md @@ -0,0 +1,13 @@ +# InputMap + +```ts +type InputMap: object; +``` + +## Index signature + + \[`key`: `string`\]: `InputValue` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/type-aliases/ProofData.md b/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/type-aliases/ProofData.md new file mode 100644 index 00000000000..3eb360a78f1 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/type-aliases/ProofData.md @@ -0,0 +1,20 @@ +# ProofData + +```ts +type ProofData: object; +``` + +## Description + +The representation of a proof + +## Type declaration + +| Member | Type | Description | +| :------ | :------ | :------ | +| `proof` | `Uint8Array` | **Description**

An byte array representing the proof | +| `publicInputs` | `WitnessMap` | **Description**

Public inputs of a proof | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/type-aliases/WitnessMap.md b/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/type-aliases/WitnessMap.md new file mode 100644 index 00000000000..258c46f9d0c --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/type-aliases/WitnessMap.md @@ -0,0 +1,9 @@ +# WitnessMap + +```ts +type WitnessMap: Map; +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/typedoc-sidebar.cjs b/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/typedoc-sidebar.cjs new file mode 100644 index 00000000000..077ebeb133e --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/noir_js/reference/noir_js/typedoc-sidebar.cjs @@ -0,0 +1,4 @@ +// @ts-check +/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ +const typedocSidebar = { items: [{"type":"category","label":"Classes","items":[{"type":"doc","id":"noir_js/reference/noir_js/classes/Noir","label":"Noir"}]},{"type":"category","label":"Type Aliases","items":[{"type":"doc","id":"noir_js/reference/noir_js/type-aliases/CompiledCircuit","label":"CompiledCircuit"},{"type":"doc","id":"noir_js/reference/noir_js/type-aliases/ForeignCallHandler","label":"ForeignCallHandler"},{"type":"doc","id":"noir_js/reference/noir_js/type-aliases/ForeignCallInput","label":"ForeignCallInput"},{"type":"doc","id":"noir_js/reference/noir_js/type-aliases/ForeignCallOutput","label":"ForeignCallOutput"},{"type":"doc","id":"noir_js/reference/noir_js/type-aliases/InputMap","label":"InputMap"},{"type":"doc","id":"noir_js/reference/noir_js/type-aliases/ProofData","label":"ProofData"},{"type":"doc","id":"noir_js/reference/noir_js/type-aliases/WitnessMap","label":"WitnessMap"}]},{"type":"category","label":"Functions","items":[{"type":"doc","id":"noir_js/reference/noir_js/functions/and","label":"and"},{"type":"doc","id":"noir_js/reference/noir_js/functions/blake2s256","label":"blake2s256"},{"type":"doc","id":"noir_js/reference/noir_js/functions/ecdsa_secp256k1_verify","label":"ecdsa_secp256k1_verify"},{"type":"doc","id":"noir_js/reference/noir_js/functions/ecdsa_secp256r1_verify","label":"ecdsa_secp256r1_verify"},{"type":"doc","id":"noir_js/reference/noir_js/functions/keccak256","label":"keccak256"},{"type":"doc","id":"noir_js/reference/noir_js/functions/sha256","label":"sha256"},{"type":"doc","id":"noir_js/reference/noir_js/functions/xor","label":"xor"}]}]}; +module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.19.4/standard_library/black_box_fns.md b/docs/versioned_docs/version-v0.19.4/standard_library/black_box_fns.md new file mode 100644 index 00000000000..1dfabfe8f22 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/standard_library/black_box_fns.md @@ -0,0 +1,46 @@ +--- +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 Hash](./cryptographic_primitives/hashes#pedersen_hash) +- [Pedersen Commitment](./cryptographic_primitives/hashes#pedersen_commitment) +- [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-v0.19.4/standard_library/cryptographic_primitives.md b/docs/versioned_docs/version-v0.19.4/standard_library/cryptographic_primitives.md new file mode 100644 index 00000000000..2df4f929474 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/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-v0.19.4/standard_library/cryptographic_primitives/00_hashes.mdx b/docs/versioned_docs/version-v0.19.4/standard_library/cryptographic_primitives/00_hashes.mdx new file mode 100644 index 00000000000..76745196681 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/standard_library/cryptographic_primitives/00_hashes.mdx @@ -0,0 +1,167 @@ +--- +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 '@site/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_hash + +Given an array of Fields, returns the Pedersen hash. + +```rust +fn pedersen_hash(_input : [Field]) -> Field +``` + +example: + +```rust +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let hash = std::hash::pedersen_hash(x); +} +``` + + + + + +## pedersen_commitment + +Given an array of Fields, returns the Pedersen commitment. + +```rust +fn pedersen_commitment(_input : [Field]) -> [Field; 2] +``` + +example: + +```rust +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let commitment = std::hash::pedersen_commitment(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::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-v0.19.4/standard_library/cryptographic_primitives/01_scalar.mdx b/docs/versioned_docs/version-v0.19.4/standard_library/cryptographic_primitives/01_scalar.mdx new file mode 100644 index 00000000000..c7eed820a80 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/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 '@site/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-v0.19.4/standard_library/cryptographic_primitives/02_schnorr.mdx b/docs/versioned_docs/version-v0.19.4/standard_library/cryptographic_primitives/02_schnorr.mdx new file mode 100644 index 00000000000..c184ce28120 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/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 '@site/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-v0.19.4/standard_library/cryptographic_primitives/03_ecdsa_sig_verification.mdx b/docs/versioned_docs/version-v0.19.4/standard_library/cryptographic_primitives/03_ecdsa_sig_verification.mdx new file mode 100644 index 00000000000..72bce984821 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/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 '@site/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-v0.19.4/standard_library/cryptographic_primitives/04_ec_primitives.md b/docs/versioned_docs/version-v0.19.4/standard_library/cryptographic_primitives/04_ec_primitives.md new file mode 100644 index 00000000000..6e6b19b6861 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/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-v0.19.4/standard_library/cryptographic_primitives/05_eddsa.mdx b/docs/versioned_docs/version-v0.19.4/standard_library/cryptographic_primitives/05_eddsa.mdx new file mode 100644 index 00000000000..9a5beb55ee9 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/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 '@site/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-v0.19.4/standard_library/logging.md b/docs/versioned_docs/version-v0.19.4/standard_library/logging.md new file mode 100644 index 00000000000..7e2fd9b9aff --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/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-v0.19.4/standard_library/merkle_trees.md b/docs/versioned_docs/version-v0.19.4/standard_library/merkle_trees.md new file mode 100644 index 00000000000..dc383a1426b --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/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_hash). + +```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-v0.19.4/standard_library/options.md b/docs/versioned_docs/version-v0.19.4/standard_library/options.md new file mode 100644 index 00000000000..3d3139fb98b --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/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-v0.19.4/standard_library/recursion.md b/docs/versioned_docs/version-v0.19.4/standard_library/recursion.md new file mode 100644 index 00000000000..ff4c63acaa7 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/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-v0.19.4/standard_library/zeroed.md b/docs/versioned_docs/version-v0.19.4/standard_library/zeroed.md new file mode 100644 index 00000000000..97dab02dac2 --- /dev/null +++ b/docs/versioned_docs/version-v0.19.4/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_docs/version-0.6.0/examples/merkle-proof.md b/docs/versioned_docs/version-v0.6.0/examples/merkle-proof.md similarity index 100% rename from docs/versioned_docs/version-0.6.0/examples/merkle-proof.md rename to docs/versioned_docs/version-v0.6.0/examples/merkle-proof.md diff --git a/docs/versioned_docs/version-0.6.0/getting_started/00_nargo_installation.md b/docs/versioned_docs/version-v0.6.0/getting_started/00_nargo_installation.md similarity index 100% rename from docs/versioned_docs/version-0.6.0/getting_started/00_nargo_installation.md rename to docs/versioned_docs/version-v0.6.0/getting_started/00_nargo_installation.md diff --git a/docs/versioned_docs/version-0.6.0/getting_started/01_hello_world.md b/docs/versioned_docs/version-v0.6.0/getting_started/01_hello_world.md similarity index 100% rename from docs/versioned_docs/version-0.6.0/getting_started/01_hello_world.md rename to docs/versioned_docs/version-v0.6.0/getting_started/01_hello_world.md diff --git a/docs/versioned_docs/version-0.6.0/getting_started/02_breakdown.md b/docs/versioned_docs/version-v0.6.0/getting_started/02_breakdown.md similarity index 100% rename from docs/versioned_docs/version-0.6.0/getting_started/02_breakdown.md rename to docs/versioned_docs/version-v0.6.0/getting_started/02_breakdown.md diff --git a/docs/versioned_docs/version-0.6.0/index.md b/docs/versioned_docs/version-v0.6.0/index.md similarity index 100% rename from docs/versioned_docs/version-0.6.0/index.md rename to docs/versioned_docs/version-v0.6.0/index.md diff --git a/docs/versioned_docs/version-0.6.0/language_concepts/00_data_types.md b/docs/versioned_docs/version-v0.6.0/language_concepts/00_data_types.md similarity index 100% rename from docs/versioned_docs/version-0.6.0/language_concepts/00_data_types.md rename to docs/versioned_docs/version-v0.6.0/language_concepts/00_data_types.md diff --git a/docs/versioned_docs/version-v0.6.0/language_concepts/01_functions.md b/docs/versioned_docs/version-v0.6.0/language_concepts/01_functions.md new file mode 100644 index 00000000000..171b7d3dbda --- /dev/null +++ b/docs/versioned_docs/version-v0.6.0/language_concepts/01_functions.md @@ -0,0 +1,88 @@ +--- +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() {} +``` + +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 : 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 : Field) -> Field { + x + y +} +``` + +Note that a `return` keyword is unneeded in this case - the last expression in a function's body is +returned. + +## 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); + constrain s.sum() == 42; +} +``` + +Methods are just syntactic sugar for functions, so if we wanted to we could also call `sum` as +follows: + +```rust +constrain MyStruct::sum(s) == 42 +``` diff --git a/docs/versioned_docs/version-0.6.0/language_concepts/02_control_flow.md b/docs/versioned_docs/version-v0.6.0/language_concepts/02_control_flow.md similarity index 100% rename from docs/versioned_docs/version-0.6.0/language_concepts/02_control_flow.md rename to docs/versioned_docs/version-v0.6.0/language_concepts/02_control_flow.md diff --git a/docs/versioned_docs/version-0.6.0/language_concepts/03_ops.md b/docs/versioned_docs/version-v0.6.0/language_concepts/03_ops.md similarity index 100% rename from docs/versioned_docs/version-0.6.0/language_concepts/03_ops.md rename to docs/versioned_docs/version-v0.6.0/language_concepts/03_ops.md diff --git a/docs/versioned_docs/version-0.6.0/language_concepts/04_assert.md b/docs/versioned_docs/version-v0.6.0/language_concepts/04_assert.md similarity index 100% rename from docs/versioned_docs/version-0.6.0/language_concepts/04_assert.md rename to docs/versioned_docs/version-v0.6.0/language_concepts/04_assert.md diff --git a/docs/versioned_docs/version-0.6.0/language_concepts/05_constrain.md b/docs/versioned_docs/version-v0.6.0/language_concepts/05_constrain.md similarity index 100% rename from docs/versioned_docs/version-0.6.0/language_concepts/05_constrain.md rename to docs/versioned_docs/version-v0.6.0/language_concepts/05_constrain.md diff --git a/docs/versioned_docs/version-v0.6.0/language_concepts/06_generics.md b/docs/versioned_docs/version-v0.6.0/language_concepts/06_generics.md new file mode 100644 index 00000000000..4d6c01fd797 --- /dev/null +++ b/docs/versioned_docs/version-v0.6.0/language_concepts/06_generics.md @@ -0,0 +1,116 @@ +--- +title: Generics +description: + Learn how to use Generics in Noir +keywords: [Noir, Rust, generics, functions, structs] +--- + +# Generics + +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: comptime 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 { + constrain first.limbs != second.limbs; + first + + fn second(first: BigInt, second: Self) -> Self { + constrain 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() { + constrain 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()]; + constrain 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/tooling/nargo_cli/tests/execution_success/generics/src/main.nr). diff --git a/docs/versioned_docs/version-v0.6.0/language_concepts/07_mutability.md b/docs/versioned_docs/version-v0.6.0/language_concepts/07_mutability.md new file mode 100644 index 00000000000..a7240a54e5c --- /dev/null +++ b/docs/versioned_docs/version-v0.6.0/language_concepts/07_mutability.md @@ -0,0 +1,118 @@ +--- +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] +--- + +# Mutability + +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() -> pub Field { + let x = 3; + helper(x); + x // x is still 3 +} + +fn helper(mut x: i32) { + x = 4; +} +``` + +## Comptime values + +Comptime value are values that are known at compile-time. This is different to a witness +which changes per proof. If a comptime value that is being used in your program is changed, then your +circuit will also change. + +Below we show how to declare a comptime value: + +```rust +fn main() { + let a: comptime Field = 5; + + // `comptime Field` can also be inferred: + let a = 5; +} +``` + +Note that variables declared as mutable may not be comptime: + +```rust +fn main() { + // error: Cannot mark a comptime type as mutable + let mut a: comptime Field = 5; + + // a inferred as a private Field here + let mut a = 5; +} +``` + +## Globals + +Noir also supports global variables. However, they must be compile-time variables. If `comptime` is +not explicitly written in the type annotation the compiler will implicitly specify the declaration +as compile-time. They can then be used like any other compile-time variable inside functions. 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: comptime Field = 5` + +fn main(x : Field, y : [Field; N]) { + let res = x * N; + + constrain res == y[0]; + + let res2 = x * mysubmodule::N; + constrain res != res2; +} + +mod mysubmodule { + use dep::std; + + global N: Field = 10; + + fn my_helper() -> comptime 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.6.0/language_concepts/08_comments.md b/docs/versioned_docs/version-v0.6.0/language_concepts/08_comments.md similarity index 100% rename from docs/versioned_docs/version-0.6.0/language_concepts/08_comments.md rename to docs/versioned_docs/version-v0.6.0/language_concepts/08_comments.md diff --git a/docs/versioned_docs/version-0.6.0/language_concepts/09_distinct.md b/docs/versioned_docs/version-v0.6.0/language_concepts/09_distinct.md similarity index 100% rename from docs/versioned_docs/version-0.6.0/language_concepts/09_distinct.md rename to docs/versioned_docs/version-v0.6.0/language_concepts/09_distinct.md diff --git a/docs/versioned_docs/version-0.6.0/modules_packages_crates/crates_and_packages.md b/docs/versioned_docs/version-v0.6.0/modules_packages_crates/crates_and_packages.md similarity index 100% rename from docs/versioned_docs/version-0.6.0/modules_packages_crates/crates_and_packages.md rename to docs/versioned_docs/version-v0.6.0/modules_packages_crates/crates_and_packages.md diff --git a/docs/versioned_docs/version-0.6.0/modules_packages_crates/dependencies.md b/docs/versioned_docs/version-v0.6.0/modules_packages_crates/dependencies.md similarity index 100% rename from docs/versioned_docs/version-0.6.0/modules_packages_crates/dependencies.md rename to docs/versioned_docs/version-v0.6.0/modules_packages_crates/dependencies.md diff --git a/docs/versioned_docs/version-0.6.0/modules_packages_crates/modules.md b/docs/versioned_docs/version-v0.6.0/modules_packages_crates/modules.md similarity index 100% rename from docs/versioned_docs/version-0.6.0/modules_packages_crates/modules.md rename to docs/versioned_docs/version-v0.6.0/modules_packages_crates/modules.md diff --git a/docs/versioned_docs/version-0.6.0/nargo/01_commands.md b/docs/versioned_docs/version-v0.6.0/nargo/01_commands.md similarity index 100% rename from docs/versioned_docs/version-0.6.0/nargo/01_commands.md rename to docs/versioned_docs/version-v0.6.0/nargo/01_commands.md diff --git a/docs/versioned_docs/version-0.6.0/nargo/02_testing.md b/docs/versioned_docs/version-v0.6.0/nargo/02_testing.md similarity index 100% rename from docs/versioned_docs/version-0.6.0/nargo/02_testing.md rename to docs/versioned_docs/version-v0.6.0/nargo/02_testing.md diff --git a/docs/versioned_docs/version-0.6.0/nargo/03_solidity_verifier.md b/docs/versioned_docs/version-v0.6.0/nargo/03_solidity_verifier.md similarity index 100% rename from docs/versioned_docs/version-0.6.0/nargo/03_solidity_verifier.md rename to docs/versioned_docs/version-v0.6.0/nargo/03_solidity_verifier.md diff --git a/docs/versioned_docs/version-0.6.0/standard_library/array_methods.md b/docs/versioned_docs/version-v0.6.0/standard_library/array_methods.md similarity index 100% rename from docs/versioned_docs/version-0.6.0/standard_library/array_methods.md rename to docs/versioned_docs/version-v0.6.0/standard_library/array_methods.md diff --git a/docs/versioned_docs/version-0.6.0/standard_library/black_box_fns.md b/docs/versioned_docs/version-v0.6.0/standard_library/black_box_fns.md similarity index 100% rename from docs/versioned_docs/version-0.6.0/standard_library/black_box_fns.md rename to docs/versioned_docs/version-v0.6.0/standard_library/black_box_fns.md diff --git a/docs/versioned_docs/version-v0.6.0/standard_library/cryptographic_primitives.md b/docs/versioned_docs/version-v0.6.0/standard_library/cryptographic_primitives.md new file mode 100644 index 00000000000..2df4f929474 --- /dev/null +++ b/docs/versioned_docs/version-v0.6.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.6.0/standard_library/cryptographic_primitives/00_hashes.mdx b/docs/versioned_docs/version-v0.6.0/standard_library/cryptographic_primitives/00_hashes.mdx similarity index 100% rename from docs/versioned_docs/version-0.6.0/standard_library/cryptographic_primitives/00_hashes.mdx rename to docs/versioned_docs/version-v0.6.0/standard_library/cryptographic_primitives/00_hashes.mdx diff --git a/docs/versioned_docs/version-0.6.0/standard_library/cryptographic_primitives/01_scalar.mdx b/docs/versioned_docs/version-v0.6.0/standard_library/cryptographic_primitives/01_scalar.mdx similarity index 100% rename from docs/versioned_docs/version-0.6.0/standard_library/cryptographic_primitives/01_scalar.mdx rename to docs/versioned_docs/version-v0.6.0/standard_library/cryptographic_primitives/01_scalar.mdx diff --git a/docs/versioned_docs/version-0.6.0/standard_library/cryptographic_primitives/02_schnorr.mdx b/docs/versioned_docs/version-v0.6.0/standard_library/cryptographic_primitives/02_schnorr.mdx similarity index 100% rename from docs/versioned_docs/version-0.6.0/standard_library/cryptographic_primitives/02_schnorr.mdx rename to docs/versioned_docs/version-v0.6.0/standard_library/cryptographic_primitives/02_schnorr.mdx diff --git a/docs/versioned_docs/version-0.6.0/standard_library/cryptographic_primitives/03_ecdsa_secp256k1.mdx b/docs/versioned_docs/version-v0.6.0/standard_library/cryptographic_primitives/03_ecdsa_secp256k1.mdx similarity index 100% rename from docs/versioned_docs/version-0.6.0/standard_library/cryptographic_primitives/03_ecdsa_secp256k1.mdx rename to docs/versioned_docs/version-v0.6.0/standard_library/cryptographic_primitives/03_ecdsa_secp256k1.mdx diff --git a/docs/versioned_docs/version-0.6.0/standard_library/cryptographic_primitives/04_ec_primitives.md b/docs/versioned_docs/version-v0.6.0/standard_library/cryptographic_primitives/04_ec_primitives.md similarity index 100% rename from docs/versioned_docs/version-0.6.0/standard_library/cryptographic_primitives/04_ec_primitives.md rename to docs/versioned_docs/version-v0.6.0/standard_library/cryptographic_primitives/04_ec_primitives.md diff --git a/docs/versioned_docs/version-0.6.0/standard_library/cryptographic_primitives/common/_blackbox.mdx b/docs/versioned_docs/version-v0.6.0/standard_library/cryptographic_primitives/common/_blackbox.mdx similarity index 100% rename from docs/versioned_docs/version-0.6.0/standard_library/cryptographic_primitives/common/_blackbox.mdx rename to docs/versioned_docs/version-v0.6.0/standard_library/cryptographic_primitives/common/_blackbox.mdx diff --git a/docs/versioned_docs/version-0.6.0/standard_library/field_methods.md b/docs/versioned_docs/version-v0.6.0/standard_library/field_methods.md similarity index 100% rename from docs/versioned_docs/version-0.6.0/standard_library/field_methods.md rename to docs/versioned_docs/version-v0.6.0/standard_library/field_methods.md diff --git a/docs/versioned_docs/version-0.6.0/standard_library/logging.md b/docs/versioned_docs/version-v0.6.0/standard_library/logging.md similarity index 100% rename from docs/versioned_docs/version-0.6.0/standard_library/logging.md rename to docs/versioned_docs/version-v0.6.0/standard_library/logging.md diff --git a/docs/versioned_docs/version-0.6.0/standard_library/merkle_trees.md b/docs/versioned_docs/version-v0.6.0/standard_library/merkle_trees.md similarity index 100% rename from docs/versioned_docs/version-0.6.0/standard_library/merkle_trees.md rename to docs/versioned_docs/version-v0.6.0/standard_library/merkle_trees.md diff --git a/docs/versioned_docs/version-v0.6.0/standard_library/zeroed.md b/docs/versioned_docs/version-v0.6.0/standard_library/zeroed.md new file mode 100644 index 00000000000..97dab02dac2 --- /dev/null +++ b/docs/versioned_docs/version-v0.6.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_docs/version-0.6.0/typescript.md b/docs/versioned_docs/version-v0.6.0/typescript.md similarity index 100% rename from docs/versioned_docs/version-0.6.0/typescript.md rename to docs/versioned_docs/version-v0.6.0/typescript.md diff --git a/docs/versioned_docs/version-0.7.1/examples/merkle-proof.md b/docs/versioned_docs/version-v0.7.1/examples/merkle-proof.md similarity index 100% rename from docs/versioned_docs/version-0.7.1/examples/merkle-proof.md rename to docs/versioned_docs/version-v0.7.1/examples/merkle-proof.md diff --git a/docs/versioned_docs/version-0.7.1/getting_started/00_nargo_installation.md b/docs/versioned_docs/version-v0.7.1/getting_started/00_nargo_installation.md similarity index 100% rename from docs/versioned_docs/version-0.7.1/getting_started/00_nargo_installation.md rename to docs/versioned_docs/version-v0.7.1/getting_started/00_nargo_installation.md diff --git a/docs/versioned_docs/version-0.7.1/getting_started/01_hello_world.md b/docs/versioned_docs/version-v0.7.1/getting_started/01_hello_world.md similarity index 100% rename from docs/versioned_docs/version-0.7.1/getting_started/01_hello_world.md rename to docs/versioned_docs/version-v0.7.1/getting_started/01_hello_world.md diff --git a/docs/versioned_docs/version-0.7.1/getting_started/02_breakdown.md b/docs/versioned_docs/version-v0.7.1/getting_started/02_breakdown.md similarity index 100% rename from docs/versioned_docs/version-0.7.1/getting_started/02_breakdown.md rename to docs/versioned_docs/version-v0.7.1/getting_started/02_breakdown.md diff --git a/docs/versioned_docs/version-0.7.1/getting_started/03_language_server.md b/docs/versioned_docs/version-v0.7.1/getting_started/03_language_server.md similarity index 100% rename from docs/versioned_docs/version-0.7.1/getting_started/03_language_server.md rename to docs/versioned_docs/version-v0.7.1/getting_started/03_language_server.md diff --git a/docs/versioned_docs/version-0.7.1/index.md b/docs/versioned_docs/version-v0.7.1/index.md similarity index 100% rename from docs/versioned_docs/version-0.7.1/index.md rename to docs/versioned_docs/version-v0.7.1/index.md diff --git a/docs/versioned_docs/version-0.7.1/language_concepts/00_data_types.md b/docs/versioned_docs/version-v0.7.1/language_concepts/00_data_types.md similarity index 100% rename from docs/versioned_docs/version-0.7.1/language_concepts/00_data_types.md rename to docs/versioned_docs/version-v0.7.1/language_concepts/00_data_types.md diff --git a/docs/versioned_docs/version-v0.7.1/language_concepts/01_functions.md b/docs/versioned_docs/version-v0.7.1/language_concepts/01_functions.md new file mode 100644 index 00000000000..8fd63293c13 --- /dev/null +++ b/docs/versioned_docs/version-v0.7.1/language_concepts/01_functions.md @@ -0,0 +1,88 @@ +--- +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() {} +``` + +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 : 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 : Field) -> Field { + x + y +} +``` + +Note that a `return` keyword is unneeded in this case - the last expression in a function's body is +returned. + +## 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); +``` diff --git a/docs/versioned_docs/version-0.7.1/language_concepts/02_control_flow.md b/docs/versioned_docs/version-v0.7.1/language_concepts/02_control_flow.md similarity index 100% rename from docs/versioned_docs/version-0.7.1/language_concepts/02_control_flow.md rename to docs/versioned_docs/version-v0.7.1/language_concepts/02_control_flow.md diff --git a/docs/versioned_docs/version-v0.7.1/language_concepts/03_ops.md b/docs/versioned_docs/version-v0.7.1/language_concepts/03_ops.md new file mode 100644 index 00000000000..da02b126059 --- /dev/null +++ b/docs/versioned_docs/version-v0.7.1/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.7.1/language_concepts/04_assert.md b/docs/versioned_docs/version-v0.7.1/language_concepts/04_assert.md similarity index 100% rename from docs/versioned_docs/version-0.7.1/language_concepts/04_assert.md rename to docs/versioned_docs/version-v0.7.1/language_concepts/04_assert.md diff --git a/docs/versioned_docs/version-v0.7.1/language_concepts/06_generics.md b/docs/versioned_docs/version-v0.7.1/language_concepts/06_generics.md new file mode 100644 index 00000000000..66f2e85e16b --- /dev/null +++ b/docs/versioned_docs/version-v0.7.1/language_concepts/06_generics.md @@ -0,0 +1,116 @@ +--- +title: Generics +description: + Learn how to use Generics in Noir +keywords: [Noir, Rust, generics, functions, structs] +--- + +# Generics + +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: comptime 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/tooling/nargo_cli/tests/execution_success/generics/src/main.nr). diff --git a/docs/versioned_docs/version-v0.7.1/language_concepts/07_mutability.md b/docs/versioned_docs/version-v0.7.1/language_concepts/07_mutability.md new file mode 100644 index 00000000000..90a1f3379a2 --- /dev/null +++ b/docs/versioned_docs/version-v0.7.1/language_concepts/07_mutability.md @@ -0,0 +1,118 @@ +--- +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() -> pub Field { + let x = 3; + helper(x); + x // x is still 3 +} + +fn helper(mut x: i32) { + x = 4; +} +``` + +## Comptime Values + +Comptime values are values that are known at compile-time. This is different to a witness +which changes per proof. If a comptime value that is being used in your program is changed, then your +circuit will also change. + +Comptime is slightly different from Rust's `const`. Namely, it is a bit more flexible in that normal functions can accept comptime parameters. For example, this is used to verify an array index is known at compile-time. Note that the "known at compile-time" here means "known after function inlining is performed while optimizing the program" and not "known during type-checking." + +Below we show how to declare a comptime value: + +```rust +fn main() { + let a: comptime Field = 5; + + // `comptime Field` can also be inferred: + let a = 5; +} +``` + +Note that variables declared as mutable may not be comptime: + +```rust +fn main() { + // error: Cannot mark a comptime type as mutable + let mut a: comptime Field = 5; + + // a inferred as a private Field here + let mut a = 5; +} +``` + +## Globals + +Noir also supports global variables. However, they must be compile-time variables. If `comptime` is +not explicitly written in the type annotation the compiler will implicitly specify the declaration +as compile-time. They can then be used like any other compile-time variable inside functions. 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: comptime 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() -> comptime 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.7.1/language_concepts/08_comments.md b/docs/versioned_docs/version-v0.7.1/language_concepts/08_comments.md similarity index 100% rename from docs/versioned_docs/version-0.7.1/language_concepts/08_comments.md rename to docs/versioned_docs/version-v0.7.1/language_concepts/08_comments.md diff --git a/docs/versioned_docs/version-0.7.1/language_concepts/09_distinct.md b/docs/versioned_docs/version-v0.7.1/language_concepts/09_distinct.md similarity index 100% rename from docs/versioned_docs/version-0.7.1/language_concepts/09_distinct.md rename to docs/versioned_docs/version-v0.7.1/language_concepts/09_distinct.md diff --git a/docs/versioned_docs/version-0.7.1/modules_packages_crates/crates_and_packages.md b/docs/versioned_docs/version-v0.7.1/modules_packages_crates/crates_and_packages.md similarity index 100% rename from docs/versioned_docs/version-0.7.1/modules_packages_crates/crates_and_packages.md rename to docs/versioned_docs/version-v0.7.1/modules_packages_crates/crates_and_packages.md diff --git a/docs/versioned_docs/version-0.7.1/modules_packages_crates/dependencies.md b/docs/versioned_docs/version-v0.7.1/modules_packages_crates/dependencies.md similarity index 100% rename from docs/versioned_docs/version-0.7.1/modules_packages_crates/dependencies.md rename to docs/versioned_docs/version-v0.7.1/modules_packages_crates/dependencies.md diff --git a/docs/versioned_docs/version-0.7.1/modules_packages_crates/modules.md b/docs/versioned_docs/version-v0.7.1/modules_packages_crates/modules.md similarity index 100% rename from docs/versioned_docs/version-0.7.1/modules_packages_crates/modules.md rename to docs/versioned_docs/version-v0.7.1/modules_packages_crates/modules.md diff --git a/docs/versioned_docs/version-0.7.1/nargo/01_commands.md b/docs/versioned_docs/version-v0.7.1/nargo/01_commands.md similarity index 100% rename from docs/versioned_docs/version-0.7.1/nargo/01_commands.md rename to docs/versioned_docs/version-v0.7.1/nargo/01_commands.md diff --git a/docs/versioned_docs/version-0.7.1/nargo/02_testing.md b/docs/versioned_docs/version-v0.7.1/nargo/02_testing.md similarity index 100% rename from docs/versioned_docs/version-0.7.1/nargo/02_testing.md rename to docs/versioned_docs/version-v0.7.1/nargo/02_testing.md diff --git a/docs/versioned_docs/version-v0.7.1/nargo/03_solidity_verifier.md b/docs/versioned_docs/version-v0.7.1/nargo/03_solidity_verifier.md new file mode 100644 index 00000000000..9ac60cb0ba7 --- /dev/null +++ b/docs/versioned_docs/version-v0.7.1/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.7.1/standard_library/array_methods.md b/docs/versioned_docs/version-v0.7.1/standard_library/array_methods.md similarity index 100% rename from docs/versioned_docs/version-0.7.1/standard_library/array_methods.md rename to docs/versioned_docs/version-v0.7.1/standard_library/array_methods.md diff --git a/docs/versioned_docs/version-0.7.1/standard_library/black_box_fns.md b/docs/versioned_docs/version-v0.7.1/standard_library/black_box_fns.md similarity index 100% rename from docs/versioned_docs/version-0.7.1/standard_library/black_box_fns.md rename to docs/versioned_docs/version-v0.7.1/standard_library/black_box_fns.md diff --git a/docs/versioned_docs/version-v0.7.1/standard_library/cryptographic_primitives.md b/docs/versioned_docs/version-v0.7.1/standard_library/cryptographic_primitives.md new file mode 100644 index 00000000000..2df4f929474 --- /dev/null +++ b/docs/versioned_docs/version-v0.7.1/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.7.1/standard_library/cryptographic_primitives/00_hashes.mdx b/docs/versioned_docs/version-v0.7.1/standard_library/cryptographic_primitives/00_hashes.mdx similarity index 100% rename from docs/versioned_docs/version-0.7.1/standard_library/cryptographic_primitives/00_hashes.mdx rename to docs/versioned_docs/version-v0.7.1/standard_library/cryptographic_primitives/00_hashes.mdx diff --git a/docs/versioned_docs/version-0.7.1/standard_library/cryptographic_primitives/01_scalar.mdx b/docs/versioned_docs/version-v0.7.1/standard_library/cryptographic_primitives/01_scalar.mdx similarity index 100% rename from docs/versioned_docs/version-0.7.1/standard_library/cryptographic_primitives/01_scalar.mdx rename to docs/versioned_docs/version-v0.7.1/standard_library/cryptographic_primitives/01_scalar.mdx diff --git a/docs/versioned_docs/version-0.7.1/standard_library/cryptographic_primitives/02_schnorr.mdx b/docs/versioned_docs/version-v0.7.1/standard_library/cryptographic_primitives/02_schnorr.mdx similarity index 100% rename from docs/versioned_docs/version-0.7.1/standard_library/cryptographic_primitives/02_schnorr.mdx rename to docs/versioned_docs/version-v0.7.1/standard_library/cryptographic_primitives/02_schnorr.mdx diff --git a/docs/versioned_docs/version-0.7.1/standard_library/cryptographic_primitives/03_ecdsa_secp256k1.mdx b/docs/versioned_docs/version-v0.7.1/standard_library/cryptographic_primitives/03_ecdsa_secp256k1.mdx similarity index 100% rename from docs/versioned_docs/version-0.7.1/standard_library/cryptographic_primitives/03_ecdsa_secp256k1.mdx rename to docs/versioned_docs/version-v0.7.1/standard_library/cryptographic_primitives/03_ecdsa_secp256k1.mdx diff --git a/docs/versioned_docs/version-0.7.1/standard_library/cryptographic_primitives/04_ec_primitives.md b/docs/versioned_docs/version-v0.7.1/standard_library/cryptographic_primitives/04_ec_primitives.md similarity index 100% rename from docs/versioned_docs/version-0.7.1/standard_library/cryptographic_primitives/04_ec_primitives.md rename to docs/versioned_docs/version-v0.7.1/standard_library/cryptographic_primitives/04_ec_primitives.md diff --git a/docs/versioned_docs/version-0.7.1/standard_library/cryptographic_primitives/05_eddsa.mdx b/docs/versioned_docs/version-v0.7.1/standard_library/cryptographic_primitives/05_eddsa.mdx similarity index 100% rename from docs/versioned_docs/version-0.7.1/standard_library/cryptographic_primitives/05_eddsa.mdx rename to docs/versioned_docs/version-v0.7.1/standard_library/cryptographic_primitives/05_eddsa.mdx diff --git a/docs/versioned_docs/version-0.7.1/standard_library/cryptographic_primitives/common/_blackbox.mdx b/docs/versioned_docs/version-v0.7.1/standard_library/cryptographic_primitives/common/_blackbox.mdx similarity index 100% rename from docs/versioned_docs/version-0.7.1/standard_library/cryptographic_primitives/common/_blackbox.mdx rename to docs/versioned_docs/version-v0.7.1/standard_library/cryptographic_primitives/common/_blackbox.mdx diff --git a/docs/versioned_docs/version-0.7.1/standard_library/field_methods.md b/docs/versioned_docs/version-v0.7.1/standard_library/field_methods.md similarity index 100% rename from docs/versioned_docs/version-0.7.1/standard_library/field_methods.md rename to docs/versioned_docs/version-v0.7.1/standard_library/field_methods.md diff --git a/docs/versioned_docs/version-0.7.1/standard_library/logging.md b/docs/versioned_docs/version-v0.7.1/standard_library/logging.md similarity index 100% rename from docs/versioned_docs/version-0.7.1/standard_library/logging.md rename to docs/versioned_docs/version-v0.7.1/standard_library/logging.md diff --git a/docs/versioned_docs/version-0.7.1/standard_library/merkle_trees.md b/docs/versioned_docs/version-v0.7.1/standard_library/merkle_trees.md similarity index 100% rename from docs/versioned_docs/version-0.7.1/standard_library/merkle_trees.md rename to docs/versioned_docs/version-v0.7.1/standard_library/merkle_trees.md diff --git a/docs/versioned_docs/version-v0.7.1/standard_library/zeroed.md b/docs/versioned_docs/version-v0.7.1/standard_library/zeroed.md new file mode 100644 index 00000000000..97dab02dac2 --- /dev/null +++ b/docs/versioned_docs/version-v0.7.1/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_docs/version-0.7.1/typescript.md b/docs/versioned_docs/version-v0.7.1/typescript.md similarity index 100% rename from docs/versioned_docs/version-0.7.1/typescript.md rename to docs/versioned_docs/version-v0.7.1/typescript.md diff --git a/docs/versioned_docs/version-0.9.0/examples/merkle-proof.mdx b/docs/versioned_docs/version-v0.9.0/examples/merkle-proof.mdx similarity index 100% rename from docs/versioned_docs/version-0.9.0/examples/merkle-proof.mdx rename to docs/versioned_docs/version-v0.9.0/examples/merkle-proof.mdx diff --git a/docs/versioned_docs/version-0.9.0/getting_started/00_nargo_installation.md b/docs/versioned_docs/version-v0.9.0/getting_started/00_nargo_installation.md similarity index 100% rename from docs/versioned_docs/version-0.9.0/getting_started/00_nargo_installation.md rename to docs/versioned_docs/version-v0.9.0/getting_started/00_nargo_installation.md diff --git a/docs/versioned_docs/version-0.9.0/getting_started/01_hello_world.md b/docs/versioned_docs/version-v0.9.0/getting_started/01_hello_world.md similarity index 100% rename from docs/versioned_docs/version-0.9.0/getting_started/01_hello_world.md rename to docs/versioned_docs/version-v0.9.0/getting_started/01_hello_world.md diff --git a/docs/versioned_docs/version-0.9.0/getting_started/02_breakdown.md b/docs/versioned_docs/version-v0.9.0/getting_started/02_breakdown.md similarity index 100% rename from docs/versioned_docs/version-0.9.0/getting_started/02_breakdown.md rename to docs/versioned_docs/version-v0.9.0/getting_started/02_breakdown.md diff --git a/docs/versioned_docs/version-0.9.0/getting_started/03_language_server.md b/docs/versioned_docs/version-v0.9.0/getting_started/03_language_server.md similarity index 100% rename from docs/versioned_docs/version-0.9.0/getting_started/03_language_server.md rename to docs/versioned_docs/version-v0.9.0/getting_started/03_language_server.md diff --git a/docs/versioned_docs/version-0.9.0/index.md b/docs/versioned_docs/version-v0.9.0/index.md similarity index 100% rename from docs/versioned_docs/version-0.9.0/index.md rename to docs/versioned_docs/version-v0.9.0/index.md diff --git a/docs/versioned_docs/version-0.9.0/language_concepts/00_data_types.md b/docs/versioned_docs/version-v0.9.0/language_concepts/00_data_types.md similarity index 100% rename from docs/versioned_docs/version-0.9.0/language_concepts/00_data_types.md rename to docs/versioned_docs/version-v0.9.0/language_concepts/00_data_types.md diff --git a/docs/versioned_docs/version-v0.9.0/language_concepts/01_functions.md b/docs/versioned_docs/version-v0.9.0/language_concepts/01_functions.md new file mode 100644 index 00000000000..8fd63293c13 --- /dev/null +++ b/docs/versioned_docs/version-v0.9.0/language_concepts/01_functions.md @@ -0,0 +1,88 @@ +--- +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() {} +``` + +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 : 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 : Field) -> Field { + x + y +} +``` + +Note that a `return` keyword is unneeded in this case - the last expression in a function's body is +returned. + +## 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); +``` diff --git a/docs/versioned_docs/version-0.9.0/language_concepts/02_control_flow.md b/docs/versioned_docs/version-v0.9.0/language_concepts/02_control_flow.md similarity index 100% rename from docs/versioned_docs/version-0.9.0/language_concepts/02_control_flow.md rename to docs/versioned_docs/version-v0.9.0/language_concepts/02_control_flow.md diff --git a/docs/versioned_docs/version-v0.9.0/language_concepts/03_ops.md b/docs/versioned_docs/version-v0.9.0/language_concepts/03_ops.md new file mode 100644 index 00000000000..da02b126059 --- /dev/null +++ b/docs/versioned_docs/version-v0.9.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.9.0/language_concepts/04_assert.md b/docs/versioned_docs/version-v0.9.0/language_concepts/04_assert.md similarity index 100% rename from docs/versioned_docs/version-0.9.0/language_concepts/04_assert.md rename to docs/versioned_docs/version-v0.9.0/language_concepts/04_assert.md diff --git a/docs/versioned_docs/version-v0.9.0/language_concepts/05_unconstrained.md b/docs/versioned_docs/version-v0.9.0/language_concepts/05_unconstrained.md new file mode 100644 index 00000000000..6b621eda3eb --- /dev/null +++ b/docs/versioned_docs/version-v0.9.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-v0.9.0/language_concepts/06_generics.md b/docs/versioned_docs/version-v0.9.0/language_concepts/06_generics.md new file mode 100644 index 00000000000..66f2e85e16b --- /dev/null +++ b/docs/versioned_docs/version-v0.9.0/language_concepts/06_generics.md @@ -0,0 +1,116 @@ +--- +title: Generics +description: + Learn how to use Generics in Noir +keywords: [Noir, Rust, generics, functions, structs] +--- + +# Generics + +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: comptime 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/tooling/nargo_cli/tests/execution_success/generics/src/main.nr). diff --git a/docs/versioned_docs/version-v0.9.0/language_concepts/07_mutability.md b/docs/versioned_docs/version-v0.9.0/language_concepts/07_mutability.md new file mode 100644 index 00000000000..a9c93e61167 --- /dev/null +++ b/docs/versioned_docs/version-v0.9.0/language_concepts/07_mutability.md @@ -0,0 +1,120 @@ +--- +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() -> pub Field { + let x = 3; + helper(x); + x // x is still 3 +} + +fn helper(mut x: i32) { + x = 4; +} +``` + +## Comptime Values + +Comptime values are values that are known at compile-time. This is different to a witness +which changes per proof. If a comptime value that is being used in your program is changed, then your +circuit will also change. + +Comptime is slightly different from Rust's `const`. Namely, it is a bit more flexible in that normal functions can accept comptime parameters. For example, this is used to verify an array index is known at compile-time. Note that the "known at compile-time" here means "known after function inlining is performed while optimizing the program" and not "known during type-checking." + +Below we show how to declare a comptime value: + +```rust +fn main() { + let a: comptime Field = 5; + + // `comptime Field` can also be inferred: + let a = 5; +} +``` + +Comptime variables can be mutuable, but must be known at compile time: + +```rust +fn main(runtime_var: Field) -> pub Field { + let known_at_compile_time: comptime Field = 1; + + // The next line will cause an error + let bad_var: comptime Field = runtime_var; + +} +``` + +As `runtime_var` is a argument to the circuit it cannot be known at compile time and so assigning it to a comptime variable should fail. A circuit's arguments is the only way in which non-comptime variables can enter the circuit (excluding [brillig](./unconstrained) foreign calls). + +## Globals + +Noir also supports global variables. However, they must be compile-time variables. If `comptime` is +not explicitly written in the type annotation the compiler will implicitly specify the declaration +as compile-time. They can then be used like any other compile-time variable inside functions. 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: comptime 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() -> comptime 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-v0.9.0/language_concepts/08_comments.md b/docs/versioned_docs/version-v0.9.0/language_concepts/08_comments.md new file mode 100644 index 00000000000..3bb4d2f25a4 --- /dev/null +++ b/docs/versioned_docs/version-v0.9.0/language_concepts/08_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-v0.9.0/language_concepts/09_distinct.md b/docs/versioned_docs/version-v0.9.0/language_concepts/09_distinct.md new file mode 100644 index 00000000000..e7ff7f5017a --- /dev/null +++ b/docs/versioned_docs/version-v0.9.0/language_concepts/09_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-v0.9.0/language_concepts/10_shadowing.md b/docs/versioned_docs/version-v0.9.0/language_concepts/10_shadowing.md new file mode 100644 index 00000000000..efd743e764f --- /dev/null +++ b/docs/versioned_docs/version-v0.9.0/language_concepts/10_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.9.0/modules_packages_crates/crates_and_packages.md b/docs/versioned_docs/version-v0.9.0/modules_packages_crates/crates_and_packages.md similarity index 100% rename from docs/versioned_docs/version-0.9.0/modules_packages_crates/crates_and_packages.md rename to docs/versioned_docs/version-v0.9.0/modules_packages_crates/crates_and_packages.md diff --git a/docs/versioned_docs/version-0.9.0/modules_packages_crates/dependencies.md b/docs/versioned_docs/version-v0.9.0/modules_packages_crates/dependencies.md similarity index 100% rename from docs/versioned_docs/version-0.9.0/modules_packages_crates/dependencies.md rename to docs/versioned_docs/version-v0.9.0/modules_packages_crates/dependencies.md diff --git a/docs/versioned_docs/version-0.9.0/modules_packages_crates/modules.md b/docs/versioned_docs/version-v0.9.0/modules_packages_crates/modules.md similarity index 100% rename from docs/versioned_docs/version-0.9.0/modules_packages_crates/modules.md rename to docs/versioned_docs/version-v0.9.0/modules_packages_crates/modules.md diff --git a/docs/versioned_docs/version-0.9.0/nargo/01_commands.md b/docs/versioned_docs/version-v0.9.0/nargo/01_commands.md similarity index 100% rename from docs/versioned_docs/version-0.9.0/nargo/01_commands.md rename to docs/versioned_docs/version-v0.9.0/nargo/01_commands.md diff --git a/docs/versioned_docs/version-0.9.0/nargo/02_testing.md b/docs/versioned_docs/version-v0.9.0/nargo/02_testing.md similarity index 100% rename from docs/versioned_docs/version-0.9.0/nargo/02_testing.md rename to docs/versioned_docs/version-v0.9.0/nargo/02_testing.md diff --git a/docs/versioned_docs/version-v0.9.0/nargo/03_solidity_verifier.md b/docs/versioned_docs/version-v0.9.0/nargo/03_solidity_verifier.md new file mode 100644 index 00000000000..9ac60cb0ba7 --- /dev/null +++ b/docs/versioned_docs/version-v0.9.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.9.0/standard_library/black_box_fns.md b/docs/versioned_docs/version-v0.9.0/standard_library/black_box_fns.md similarity index 100% rename from docs/versioned_docs/version-0.9.0/standard_library/black_box_fns.md rename to docs/versioned_docs/version-v0.9.0/standard_library/black_box_fns.md diff --git a/docs/versioned_docs/version-v0.9.0/standard_library/cryptographic_primitives.md b/docs/versioned_docs/version-v0.9.0/standard_library/cryptographic_primitives.md new file mode 100644 index 00000000000..2df4f929474 --- /dev/null +++ b/docs/versioned_docs/version-v0.9.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.9.0/standard_library/cryptographic_primitives/00_hashes.mdx b/docs/versioned_docs/version-v0.9.0/standard_library/cryptographic_primitives/00_hashes.mdx similarity index 100% rename from docs/versioned_docs/version-0.9.0/standard_library/cryptographic_primitives/00_hashes.mdx rename to docs/versioned_docs/version-v0.9.0/standard_library/cryptographic_primitives/00_hashes.mdx diff --git a/docs/versioned_docs/version-0.9.0/standard_library/cryptographic_primitives/01_scalar.mdx b/docs/versioned_docs/version-v0.9.0/standard_library/cryptographic_primitives/01_scalar.mdx similarity index 100% rename from docs/versioned_docs/version-0.9.0/standard_library/cryptographic_primitives/01_scalar.mdx rename to docs/versioned_docs/version-v0.9.0/standard_library/cryptographic_primitives/01_scalar.mdx diff --git a/docs/versioned_docs/version-0.9.0/standard_library/cryptographic_primitives/02_schnorr.mdx b/docs/versioned_docs/version-v0.9.0/standard_library/cryptographic_primitives/02_schnorr.mdx similarity index 100% rename from docs/versioned_docs/version-0.9.0/standard_library/cryptographic_primitives/02_schnorr.mdx rename to docs/versioned_docs/version-v0.9.0/standard_library/cryptographic_primitives/02_schnorr.mdx diff --git a/docs/versioned_docs/version-0.9.0/standard_library/cryptographic_primitives/03_ecdsa_sig_verification.mdx b/docs/versioned_docs/version-v0.9.0/standard_library/cryptographic_primitives/03_ecdsa_sig_verification.mdx similarity index 100% rename from docs/versioned_docs/version-0.9.0/standard_library/cryptographic_primitives/03_ecdsa_sig_verification.mdx rename to docs/versioned_docs/version-v0.9.0/standard_library/cryptographic_primitives/03_ecdsa_sig_verification.mdx diff --git a/docs/versioned_docs/version-v0.9.0/standard_library/cryptographic_primitives/04_ec_primitives.md b/docs/versioned_docs/version-v0.9.0/standard_library/cryptographic_primitives/04_ec_primitives.md new file mode 100644 index 00000000000..6e6b19b6861 --- /dev/null +++ b/docs/versioned_docs/version-v0.9.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.9.0/standard_library/cryptographic_primitives/05_eddsa.mdx b/docs/versioned_docs/version-v0.9.0/standard_library/cryptographic_primitives/05_eddsa.mdx similarity index 100% rename from docs/versioned_docs/version-0.9.0/standard_library/cryptographic_primitives/05_eddsa.mdx rename to docs/versioned_docs/version-v0.9.0/standard_library/cryptographic_primitives/05_eddsa.mdx diff --git a/docs/versioned_docs/version-0.9.0/standard_library/cryptographic_primitives/common/_blackbox.mdx b/docs/versioned_docs/version-v0.9.0/standard_library/cryptographic_primitives/common/_blackbox.mdx similarity index 100% rename from docs/versioned_docs/version-0.9.0/standard_library/cryptographic_primitives/common/_blackbox.mdx rename to docs/versioned_docs/version-v0.9.0/standard_library/cryptographic_primitives/common/_blackbox.mdx diff --git a/docs/versioned_docs/version-0.9.0/standard_library/field_methods.md b/docs/versioned_docs/version-v0.9.0/standard_library/field_methods.md similarity index 100% rename from docs/versioned_docs/version-0.9.0/standard_library/field_methods.md rename to docs/versioned_docs/version-v0.9.0/standard_library/field_methods.md diff --git a/docs/versioned_docs/version-0.9.0/standard_library/logging.md b/docs/versioned_docs/version-v0.9.0/standard_library/logging.md similarity index 100% rename from docs/versioned_docs/version-0.9.0/standard_library/logging.md rename to docs/versioned_docs/version-v0.9.0/standard_library/logging.md diff --git a/docs/versioned_docs/version-0.9.0/standard_library/merkle_trees.md b/docs/versioned_docs/version-v0.9.0/standard_library/merkle_trees.md similarity index 100% rename from docs/versioned_docs/version-0.9.0/standard_library/merkle_trees.md rename to docs/versioned_docs/version-v0.9.0/standard_library/merkle_trees.md diff --git a/docs/versioned_docs/version-0.9.0/standard_library/recursion.md b/docs/versioned_docs/version-v0.9.0/standard_library/recursion.md similarity index 100% rename from docs/versioned_docs/version-0.9.0/standard_library/recursion.md rename to docs/versioned_docs/version-v0.9.0/standard_library/recursion.md diff --git a/docs/versioned_docs/version-0.9.0/standard_library/slice_methods.md b/docs/versioned_docs/version-v0.9.0/standard_library/slice_methods.md similarity index 100% rename from docs/versioned_docs/version-0.9.0/standard_library/slice_methods.md rename to docs/versioned_docs/version-v0.9.0/standard_library/slice_methods.md diff --git a/docs/versioned_docs/version-v0.9.0/standard_library/zeroed.md b/docs/versioned_docs/version-v0.9.0/standard_library/zeroed.md new file mode 100644 index 00000000000..97dab02dac2 --- /dev/null +++ b/docs/versioned_docs/version-v0.9.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_docs/version-0.9.0/typescript.md b/docs/versioned_docs/version-v0.9.0/typescript.md similarity index 100% rename from docs/versioned_docs/version-0.9.0/typescript.md rename to docs/versioned_docs/version-v0.9.0/typescript.md diff --git a/docs/versioned_sidebars/version-0.10.5-sidebars.json b/docs/versioned_sidebars/version-v0.10.5-sidebars.json similarity index 100% rename from docs/versioned_sidebars/version-0.10.5-sidebars.json rename to docs/versioned_sidebars/version-v0.10.5-sidebars.json diff --git a/docs/versioned_sidebars/version-0.17.0-sidebars.json b/docs/versioned_sidebars/version-v0.17.0-sidebars.json similarity index 100% rename from docs/versioned_sidebars/version-0.17.0-sidebars.json rename to docs/versioned_sidebars/version-v0.17.0-sidebars.json diff --git a/docs/versioned_sidebars/version-v0.19.0-sidebars.json b/docs/versioned_sidebars/version-v0.19.0-sidebars.json new file mode 100644 index 00000000000..a9ec39925d9 --- /dev/null +++ b/docs/versioned_sidebars/version-v0.19.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/versioned_sidebars/version-v0.19.1-sidebars.json b/docs/versioned_sidebars/version-v0.19.1-sidebars.json new file mode 100644 index 00000000000..6823055c5d3 --- /dev/null +++ b/docs/versioned_sidebars/version-v0.19.1-sidebars.json @@ -0,0 +1,288 @@ +{ + "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": "category", + "label": "Noir JS", + "link": { + "type": "doc", + "id": "noir_js/reference/noir_js/index" + }, + "items": [ + { + "type": "category", + "label": "Classes", + "items": [ + { + "type": "doc", + "id": "noir_js/reference/noir_js/classes/Noir", + "label": "Noir" + } + ] + }, + { + "type": "category", + "label": "Type Aliases", + "items": [ + { + "type": "doc", + "id": "noir_js/reference/noir_js/type-aliases/CompiledCircuit", + "label": "CompiledCircuit" + }, + { + "type": "doc", + "id": "noir_js/reference/noir_js/type-aliases/ForeignCallHandler", + "label": "ForeignCallHandler" + }, + { + "type": "doc", + "id": "noir_js/reference/noir_js/type-aliases/ForeignCallInput", + "label": "ForeignCallInput" + }, + { + "type": "doc", + "id": "noir_js/reference/noir_js/type-aliases/ForeignCallOutput", + "label": "ForeignCallOutput" + }, + { + "type": "doc", + "id": "noir_js/reference/noir_js/type-aliases/ProofData", + "label": "ProofData" + }, + { + "type": "doc", + "id": "noir_js/reference/noir_js/type-aliases/WitnessMap", + "label": "WitnessMap" + } + ] + }, + { + "type": "category", + "label": "Functions", + "items": [ + { + "type": "doc", + "id": "noir_js/reference/noir_js/functions/and", + "label": "and" + }, + { + "type": "doc", + "id": "noir_js/reference/noir_js/functions/blake2s256", + "label": "blake2s256" + }, + { + "type": "doc", + "id": "noir_js/reference/noir_js/functions/ecdsa_secp256k1_verify", + "label": "ecdsa_secp256k1_verify" + }, + { + "type": "doc", + "id": "noir_js/reference/noir_js/functions/ecdsa_secp256r1_verify", + "label": "ecdsa_secp256r1_verify" + }, + { + "type": "doc", + "id": "noir_js/reference/noir_js/functions/keccak256", + "label": "keccak256" + }, + { + "type": "doc", + "id": "noir_js/reference/noir_js/functions/sha256", + "label": "sha256" + }, + { + "type": "doc", + "id": "noir_js/reference/noir_js/functions/xor", + "label": "xor" + } + ] + } + ] + }, + { + "type": "category", + "label": "Backend Barretenberg", + "link": { + "type": "doc", + "id": "noir_js/reference/backend_barretenberg/index" + }, + "items": [ + { + "type": "category", + "label": "Classes", + "items": [ + { + "type": "doc", + "id": "noir_js/reference/backend_barretenberg/classes/BarretenbergBackend", + "label": "BarretenbergBackend" + } + ] + }, + { + "type": "category", + "label": "Interfaces", + "items": [ + { + "type": "doc", + "id": "noir_js/reference/backend_barretenberg/interfaces/Backend", + "label": "Backend" + } + ] + }, + { + "type": "category", + "label": "Type Aliases", + "items": [ + { + "type": "doc", + "id": "noir_js/reference/backend_barretenberg/type-aliases/BackendOptions", + "label": "BackendOptions" + }, + { + "type": "doc", + "id": "noir_js/reference/backend_barretenberg/type-aliases/CompiledCircuit", + "label": "CompiledCircuit" + }, + { + "type": "doc", + "id": "noir_js/reference/backend_barretenberg/type-aliases/ProofData", + "label": "ProofData" + } + ] + } + ] + } + ] + } + ] + }, + { + "type": "doc", + "id": "migration_notes", + "label": "Migration notes" + } + ] +} diff --git a/docs/versioned_sidebars/version-v0.19.2-sidebars.json b/docs/versioned_sidebars/version-v0.19.2-sidebars.json new file mode 100644 index 00000000000..6823055c5d3 --- /dev/null +++ b/docs/versioned_sidebars/version-v0.19.2-sidebars.json @@ -0,0 +1,288 @@ +{ + "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": "category", + "label": "Noir JS", + "link": { + "type": "doc", + "id": "noir_js/reference/noir_js/index" + }, + "items": [ + { + "type": "category", + "label": "Classes", + "items": [ + { + "type": "doc", + "id": "noir_js/reference/noir_js/classes/Noir", + "label": "Noir" + } + ] + }, + { + "type": "category", + "label": "Type Aliases", + "items": [ + { + "type": "doc", + "id": "noir_js/reference/noir_js/type-aliases/CompiledCircuit", + "label": "CompiledCircuit" + }, + { + "type": "doc", + "id": "noir_js/reference/noir_js/type-aliases/ForeignCallHandler", + "label": "ForeignCallHandler" + }, + { + "type": "doc", + "id": "noir_js/reference/noir_js/type-aliases/ForeignCallInput", + "label": "ForeignCallInput" + }, + { + "type": "doc", + "id": "noir_js/reference/noir_js/type-aliases/ForeignCallOutput", + "label": "ForeignCallOutput" + }, + { + "type": "doc", + "id": "noir_js/reference/noir_js/type-aliases/ProofData", + "label": "ProofData" + }, + { + "type": "doc", + "id": "noir_js/reference/noir_js/type-aliases/WitnessMap", + "label": "WitnessMap" + } + ] + }, + { + "type": "category", + "label": "Functions", + "items": [ + { + "type": "doc", + "id": "noir_js/reference/noir_js/functions/and", + "label": "and" + }, + { + "type": "doc", + "id": "noir_js/reference/noir_js/functions/blake2s256", + "label": "blake2s256" + }, + { + "type": "doc", + "id": "noir_js/reference/noir_js/functions/ecdsa_secp256k1_verify", + "label": "ecdsa_secp256k1_verify" + }, + { + "type": "doc", + "id": "noir_js/reference/noir_js/functions/ecdsa_secp256r1_verify", + "label": "ecdsa_secp256r1_verify" + }, + { + "type": "doc", + "id": "noir_js/reference/noir_js/functions/keccak256", + "label": "keccak256" + }, + { + "type": "doc", + "id": "noir_js/reference/noir_js/functions/sha256", + "label": "sha256" + }, + { + "type": "doc", + "id": "noir_js/reference/noir_js/functions/xor", + "label": "xor" + } + ] + } + ] + }, + { + "type": "category", + "label": "Backend Barretenberg", + "link": { + "type": "doc", + "id": "noir_js/reference/backend_barretenberg/index" + }, + "items": [ + { + "type": "category", + "label": "Classes", + "items": [ + { + "type": "doc", + "id": "noir_js/reference/backend_barretenberg/classes/BarretenbergBackend", + "label": "BarretenbergBackend" + } + ] + }, + { + "type": "category", + "label": "Interfaces", + "items": [ + { + "type": "doc", + "id": "noir_js/reference/backend_barretenberg/interfaces/Backend", + "label": "Backend" + } + ] + }, + { + "type": "category", + "label": "Type Aliases", + "items": [ + { + "type": "doc", + "id": "noir_js/reference/backend_barretenberg/type-aliases/BackendOptions", + "label": "BackendOptions" + }, + { + "type": "doc", + "id": "noir_js/reference/backend_barretenberg/type-aliases/CompiledCircuit", + "label": "CompiledCircuit" + }, + { + "type": "doc", + "id": "noir_js/reference/backend_barretenberg/type-aliases/ProofData", + "label": "ProofData" + } + ] + } + ] + } + ] + } + ] + }, + { + "type": "doc", + "id": "migration_notes", + "label": "Migration notes" + } + ] +} diff --git a/docs/versioned_sidebars/version-v0.19.3-sidebars.json b/docs/versioned_sidebars/version-v0.19.3-sidebars.json new file mode 100644 index 00000000000..6823055c5d3 --- /dev/null +++ b/docs/versioned_sidebars/version-v0.19.3-sidebars.json @@ -0,0 +1,288 @@ +{ + "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": "category", + "label": "Noir JS", + "link": { + "type": "doc", + "id": "noir_js/reference/noir_js/index" + }, + "items": [ + { + "type": "category", + "label": "Classes", + "items": [ + { + "type": "doc", + "id": "noir_js/reference/noir_js/classes/Noir", + "label": "Noir" + } + ] + }, + { + "type": "category", + "label": "Type Aliases", + "items": [ + { + "type": "doc", + "id": "noir_js/reference/noir_js/type-aliases/CompiledCircuit", + "label": "CompiledCircuit" + }, + { + "type": "doc", + "id": "noir_js/reference/noir_js/type-aliases/ForeignCallHandler", + "label": "ForeignCallHandler" + }, + { + "type": "doc", + "id": "noir_js/reference/noir_js/type-aliases/ForeignCallInput", + "label": "ForeignCallInput" + }, + { + "type": "doc", + "id": "noir_js/reference/noir_js/type-aliases/ForeignCallOutput", + "label": "ForeignCallOutput" + }, + { + "type": "doc", + "id": "noir_js/reference/noir_js/type-aliases/ProofData", + "label": "ProofData" + }, + { + "type": "doc", + "id": "noir_js/reference/noir_js/type-aliases/WitnessMap", + "label": "WitnessMap" + } + ] + }, + { + "type": "category", + "label": "Functions", + "items": [ + { + "type": "doc", + "id": "noir_js/reference/noir_js/functions/and", + "label": "and" + }, + { + "type": "doc", + "id": "noir_js/reference/noir_js/functions/blake2s256", + "label": "blake2s256" + }, + { + "type": "doc", + "id": "noir_js/reference/noir_js/functions/ecdsa_secp256k1_verify", + "label": "ecdsa_secp256k1_verify" + }, + { + "type": "doc", + "id": "noir_js/reference/noir_js/functions/ecdsa_secp256r1_verify", + "label": "ecdsa_secp256r1_verify" + }, + { + "type": "doc", + "id": "noir_js/reference/noir_js/functions/keccak256", + "label": "keccak256" + }, + { + "type": "doc", + "id": "noir_js/reference/noir_js/functions/sha256", + "label": "sha256" + }, + { + "type": "doc", + "id": "noir_js/reference/noir_js/functions/xor", + "label": "xor" + } + ] + } + ] + }, + { + "type": "category", + "label": "Backend Barretenberg", + "link": { + "type": "doc", + "id": "noir_js/reference/backend_barretenberg/index" + }, + "items": [ + { + "type": "category", + "label": "Classes", + "items": [ + { + "type": "doc", + "id": "noir_js/reference/backend_barretenberg/classes/BarretenbergBackend", + "label": "BarretenbergBackend" + } + ] + }, + { + "type": "category", + "label": "Interfaces", + "items": [ + { + "type": "doc", + "id": "noir_js/reference/backend_barretenberg/interfaces/Backend", + "label": "Backend" + } + ] + }, + { + "type": "category", + "label": "Type Aliases", + "items": [ + { + "type": "doc", + "id": "noir_js/reference/backend_barretenberg/type-aliases/BackendOptions", + "label": "BackendOptions" + }, + { + "type": "doc", + "id": "noir_js/reference/backend_barretenberg/type-aliases/CompiledCircuit", + "label": "CompiledCircuit" + }, + { + "type": "doc", + "id": "noir_js/reference/backend_barretenberg/type-aliases/ProofData", + "label": "ProofData" + } + ] + } + ] + } + ] + } + ] + }, + { + "type": "doc", + "id": "migration_notes", + "label": "Migration notes" + } + ] +} diff --git a/docs/versioned_sidebars/version-v0.19.4-sidebars.json b/docs/versioned_sidebars/version-v0.19.4-sidebars.json new file mode 100644 index 00000000000..a1675eca18d --- /dev/null +++ b/docs/versioned_sidebars/version-v0.19.4-sidebars.json @@ -0,0 +1,293 @@ +{ + "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": "category", + "label": "Noir JS", + "link": { + "type": "doc", + "id": "noir_js/reference/noir_js/index" + }, + "items": [ + { + "type": "category", + "label": "Classes", + "items": [ + { + "type": "doc", + "id": "noir_js/reference/noir_js/classes/Noir", + "label": "Noir" + } + ] + }, + { + "type": "category", + "label": "Type Aliases", + "items": [ + { + "type": "doc", + "id": "noir_js/reference/noir_js/type-aliases/CompiledCircuit", + "label": "CompiledCircuit" + }, + { + "type": "doc", + "id": "noir_js/reference/noir_js/type-aliases/ForeignCallHandler", + "label": "ForeignCallHandler" + }, + { + "type": "doc", + "id": "noir_js/reference/noir_js/type-aliases/ForeignCallInput", + "label": "ForeignCallInput" + }, + { + "type": "doc", + "id": "noir_js/reference/noir_js/type-aliases/ForeignCallOutput", + "label": "ForeignCallOutput" + }, + { + "type": "doc", + "id": "noir_js/reference/noir_js/type-aliases/InputMap", + "label": "InputMap" + }, + { + "type": "doc", + "id": "noir_js/reference/noir_js/type-aliases/ProofData", + "label": "ProofData" + }, + { + "type": "doc", + "id": "noir_js/reference/noir_js/type-aliases/WitnessMap", + "label": "WitnessMap" + } + ] + }, + { + "type": "category", + "label": "Functions", + "items": [ + { + "type": "doc", + "id": "noir_js/reference/noir_js/functions/and", + "label": "and" + }, + { + "type": "doc", + "id": "noir_js/reference/noir_js/functions/blake2s256", + "label": "blake2s256" + }, + { + "type": "doc", + "id": "noir_js/reference/noir_js/functions/ecdsa_secp256k1_verify", + "label": "ecdsa_secp256k1_verify" + }, + { + "type": "doc", + "id": "noir_js/reference/noir_js/functions/ecdsa_secp256r1_verify", + "label": "ecdsa_secp256r1_verify" + }, + { + "type": "doc", + "id": "noir_js/reference/noir_js/functions/keccak256", + "label": "keccak256" + }, + { + "type": "doc", + "id": "noir_js/reference/noir_js/functions/sha256", + "label": "sha256" + }, + { + "type": "doc", + "id": "noir_js/reference/noir_js/functions/xor", + "label": "xor" + } + ] + } + ] + }, + { + "type": "category", + "label": "Backend Barretenberg", + "link": { + "type": "doc", + "id": "noir_js/reference/backend_barretenberg/index" + }, + "items": [ + { + "type": "category", + "label": "Classes", + "items": [ + { + "type": "doc", + "id": "noir_js/reference/backend_barretenberg/classes/BarretenbergBackend", + "label": "BarretenbergBackend" + } + ] + }, + { + "type": "category", + "label": "Interfaces", + "items": [ + { + "type": "doc", + "id": "noir_js/reference/backend_barretenberg/interfaces/Backend", + "label": "Backend" + } + ] + }, + { + "type": "category", + "label": "Type Aliases", + "items": [ + { + "type": "doc", + "id": "noir_js/reference/backend_barretenberg/type-aliases/BackendOptions", + "label": "BackendOptions" + }, + { + "type": "doc", + "id": "noir_js/reference/backend_barretenberg/type-aliases/CompiledCircuit", + "label": "CompiledCircuit" + }, + { + "type": "doc", + "id": "noir_js/reference/backend_barretenberg/type-aliases/ProofData", + "label": "ProofData" + } + ] + } + ] + } + ] + } + ] + }, + { + "type": "doc", + "id": "migration_notes", + "label": "Migration notes" + } + ] +} diff --git a/docs/versioned_sidebars/version-0.6.0-sidebars.json b/docs/versioned_sidebars/version-v0.6.0-sidebars.json similarity index 100% rename from docs/versioned_sidebars/version-0.6.0-sidebars.json rename to docs/versioned_sidebars/version-v0.6.0-sidebars.json diff --git a/docs/versioned_sidebars/version-0.7.1-sidebars.json b/docs/versioned_sidebars/version-v0.7.1-sidebars.json similarity index 100% rename from docs/versioned_sidebars/version-0.7.1-sidebars.json rename to docs/versioned_sidebars/version-v0.7.1-sidebars.json diff --git a/docs/versioned_sidebars/version-0.9.0-sidebars.json b/docs/versioned_sidebars/version-v0.9.0-sidebars.json similarity index 100% rename from docs/versioned_sidebars/version-0.9.0-sidebars.json rename to docs/versioned_sidebars/version-v0.9.0-sidebars.json diff --git a/docs/versions.json b/docs/versions.json index e1a95eeb06d..7f45b37e432 100644 --- a/docs/versions.json +++ b/docs/versions.json @@ -1,4 +1,6 @@ [ - "0.17.0", - "0.10.5" + "v0.19.2", + "v0.19.1", + "v0.19.0", + "v0.17.0" ] \ No newline at end of file diff --git a/flake.nix b/flake.nix index ce9244ec04d..ac858c1714f 100644 --- a/flake.nix +++ b/flake.nix @@ -73,7 +73,7 @@ # Configuration shared between builds config = { # x-release-please-start-version - version = "0.19.0"; + version = "0.19.4"; # x-release-please-end src = pkgs.lib.cleanSourceWith { diff --git a/noir_stdlib/src/collections/vec.nr b/noir_stdlib/src/collections/vec.nr index 1c9a791961e..43d68e1d1e7 100644 --- a/noir_stdlib/src/collections/vec.nr +++ b/noir_stdlib/src/collections/vec.nr @@ -1,7 +1,6 @@ struct Vec { slice: [T] } - // A mutable vector type implemented as a wrapper around immutable slices. // A separate type is technically not needed but helps differentiate which operations are mutable. impl Vec { diff --git a/noir_stdlib/src/ec.nr b/noir_stdlib/src/ec.nr index 8b426f46825..86fb201408f 100644 --- a/noir_stdlib/src/ec.nr +++ b/noir_stdlib/src/ec.nr @@ -119,19 +119,15 @@ mod consts; // Commonly used curve presets // // *TODO: Replace Field with Bigint. // **TODO: Support arrays of structs to make this work. - - // Field-dependent constant ZETA = a non-square element of Field // Required for Elligator 2 map // TODO: Replace with built-in constant. global ZETA = 5; - // Field-dependent constants for Tonelli-Shanks algorithm (see sqrt function below) // TODO: Possibly make this built-in. global C1 = 28; global C3 = 40770029410420498293352137776570907027550720424234931066070132305055; global C5 = 19103219067921713944291392827692070036145651957329286315305642004821462161904; - // Higher-order version of scalar multiplication // TODO: Make this work so that the submodules' bit_mul may be defined in terms of it. //fn bit_mul(add: fn(T,T) -> T, e: T, bits: [u1; N], p: T) -> T { @@ -146,39 +142,32 @@ global C5 = 19103219067921713944291392827692070036145651957329286315305642004821 // // out //} - // TODO: Make this built-in. pub fn safe_inverse(x: Field) -> Field { - if x == 0 { - 0 - } else { - 1/x - } + if x == 0 { 0 } else { 1 / x } } - // Boolean indicating whether Field element is a square, i.e. whether there exists a y in Field s.t. x = y*y. pub fn is_square(x: Field) -> bool { - let v = pow(x, 0 - 1/2); + let v = pow(x, 0 - 1 / 2); - v*(v-1) == 0 + v * (v - 1) == 0 } - // Power function of two Field arguments of arbitrary size. // Adapted from std::field::pow_32. -pub fn pow(x: Field, y: Field) -> Field { // As in tests with minor modifications +pub fn pow(x: Field, y: Field) -> Field { + // As in tests with minor modifications let N_BITS = crate::field::modulus_num_bits(); let mut r = 1 as Field; let b = y.to_le_bits(N_BITS as u32); - + for i in 0..N_BITS { r *= r; r *= (b[N_BITS - 1 - i] as Field)*x + (1-b[N_BITS - 1 - i] as Field); } - + r } - // Tonelli-Shanks algorithm for computing the square root of a Field element. // Requires C1 = max{c: 2^c divides (p-1)}, where p is the order of Field // as well as C3 = (C2 - 1)/2, where C2 = (p-1)/(2^c1), @@ -186,27 +175,24 @@ pub fn pow(x: Field, y: Field) -> Field { // As in tests with minor modification // These are pre-computed above as globals. pub fn sqrt(x: Field) -> Field { let mut z = pow(x, C3); - let mut t = z*z*x; + let mut t = z * z * x; z *= x; let mut b = t; let mut c = C5; - - for i in 0..(C1-1) { - - for _j in 1..(C1-i-1) { - + + for i in 0..(C1 - 1) { + for _j in 1..(C1 - i - 1) { b *= b; - } - + z *= if b == 1 { 1 } else { c }; - + c *= c; - + t *= if b == 1 { 1 } else { c }; - + b = t; } - + z } diff --git a/noir_stdlib/src/ec/consts/te.nr b/noir_stdlib/src/ec/consts/te.nr index 1159ac558b0..e25f373593a 100644 --- a/noir_stdlib/src/ec/consts/te.nr +++ b/noir_stdlib/src/ec/consts/te.nr @@ -18,15 +18,15 @@ pub fn baby_jubjub() -> BabyJubjub { // G TEPoint::new( 995203441582195749578291179787384436505546430278305826713579947235728471134, - 5472060717959818805561601436314318772137091100104008585924551046643952123905, - ), + 5472060717959818805561601436314318772137091100104008585924551046643952123905 + ) ), // [8]G precalculated base8: TEPoint::new( 5299619240641551281634865583518297030282874472190772894086521144482721001553, - 16950150798460657717958625567821834550301663161624707787222815936182638968203, + 16950150798460657717958625567821834550301663161624707787222815936182638968203 ), // The size of the group formed from multiplying the base field by 8. - suborder: 2736030358979909402780800718157159386076813972158567259200215660948447373041, + suborder: 2736030358979909402780800718157159386076813972158567259200215660948447373041 } } diff --git a/noir_stdlib/src/ec/montcurve.nr b/noir_stdlib/src/ec/montcurve.nr index 6a57f258c8e..82d22837b46 100644 --- a/noir_stdlib/src/ec/montcurve.nr +++ b/noir_stdlib/src/ec/montcurve.nr @@ -12,7 +12,6 @@ mod affine { use crate::ec::safe_inverse; use crate::ec::sqrt; use crate::ec::ZETA; - // Curve specification struct Curve { // Montgomery Curve configuration (ky^2 = x^3 + j*x^2 + x) j: Field, @@ -20,7 +19,6 @@ mod affine { // Generator as point in Cartesian coordinates gen: Point } - // Point in Cartesian coordinates struct Point { x: Field, @@ -228,7 +226,6 @@ mod curvegroup { // Generator as point in projective coordinates gen: Point } - // Point in projective coordinates struct Point { x: Field, diff --git a/noir_stdlib/src/ec/swcurve.nr b/noir_stdlib/src/ec/swcurve.nr index 4aa210f6df3..e9b6f661843 100644 --- a/noir_stdlib/src/ec/swcurve.nr +++ b/noir_stdlib/src/ec/swcurve.nr @@ -7,7 +7,6 @@ mod affine { use crate::ec::safe_inverse; use crate::ec::is_square; use crate::ec::sqrt; - // Curve specification struct Curve { // Short Weierstraß curve // Coefficients in defining equation y^2 = x^3 + ax + b @@ -16,7 +15,6 @@ mod affine { // Generator as point in Cartesian coordinates gen: Point } - // Point in Cartesian coordinates struct Point { x: Field, @@ -184,7 +182,6 @@ mod curvegroup { // Points are represented by three-dimensional Jacobian coordinates. // See for details. use crate::ec::swcurve::affine; - // Curve specification struct Curve { // Short Weierstraß curve // Coefficients in defining equation y^2 = x^3 + axz^4 + bz^6 @@ -193,7 +190,6 @@ mod curvegroup { // Generator as point in Cartesian coordinates gen: Point } - // Point in three-dimensional Jacobian coordinates struct Point { x: Field, diff --git a/noir_stdlib/src/ec/tecurve.nr b/noir_stdlib/src/ec/tecurve.nr index 54b59e99a54..849b45ff012 100644 --- a/noir_stdlib/src/ec/tecurve.nr +++ b/noir_stdlib/src/ec/tecurve.nr @@ -9,7 +9,6 @@ mod affine { use crate::ec::montcurve::affine::Point as MPoint; use crate::ec::swcurve::affine::Curve as SWCurve; use crate::ec::swcurve::affine::Point as SWPoint; - // Curve specification struct Curve { // Twisted Edwards curve // Coefficients in defining equation ax^2 + y^2 = 1 + dx^2y^2 @@ -18,7 +17,6 @@ mod affine { // Generator as point in Cartesian coordinates gen: Point } - // Point in Cartesian coordinates struct Point { x: Field, @@ -76,7 +74,6 @@ mod affine { } } - impl Curve { // Curve constructor pub fn new(a: Field, d: Field, gen: Point) -> Curve { @@ -201,7 +198,6 @@ mod curvegroup { use crate::ec::montcurve::curvegroup::Point as MPoint; use crate::ec::swcurve::curvegroup::Curve as SWCurve; use crate::ec::swcurve::curvegroup::Point as SWPoint; - // Curve specification struct Curve { // Twisted Edwards curve // Coefficients in defining equation a(x^2 + y^2)z^2 = z^4 + dx^2y^2 @@ -210,7 +206,6 @@ mod curvegroup { // Generator as point in projective coordinates gen: Point } - // Point in extended twisted Edwards coordinates struct Point { x: Field, diff --git a/noir_stdlib/src/ecdsa_secp256k1.nr b/noir_stdlib/src/ecdsa_secp256k1.nr index 1ad25811b8a..b1f2b12c76b 100644 --- a/noir_stdlib/src/ecdsa_secp256k1.nr +++ b/noir_stdlib/src/ecdsa_secp256k1.nr @@ -1,2 +1,7 @@ #[foreign(ecdsa_secp256k1)] -pub fn verify_signature(_public_key_x : [u8; 32], _public_key_y : [u8; 32], _signature: [u8; 64], _message_hash: [u8; N]) -> bool {} +pub fn verify_signature( + _public_key_x: [u8; 32], + _public_key_y: [u8; 32], + _signature: [u8; 64], + _message_hash: [u8; N] +) -> bool {} diff --git a/noir_stdlib/src/ecdsa_secp256r1.nr b/noir_stdlib/src/ecdsa_secp256r1.nr index b47b69fa276..6c3cf4d7945 100644 --- a/noir_stdlib/src/ecdsa_secp256r1.nr +++ b/noir_stdlib/src/ecdsa_secp256r1.nr @@ -1,2 +1,7 @@ #[foreign(ecdsa_secp256r1)] -pub fn verify_signature(_public_key_x : [u8; 32], _public_key_y : [u8; 32], _signature: [u8; 64], _message_hash: [u8; N]) -> bool {} +pub fn verify_signature( + _public_key_x: [u8; 32], + _public_key_y: [u8; 32], + _signature: [u8; 64], + _message_hash: [u8; N] +) -> bool {} diff --git a/noir_stdlib/src/eddsa.nr b/noir_stdlib/src/eddsa.nr index 31fac549fc5..39051e23233 100644 --- a/noir_stdlib/src/eddsa.nr +++ b/noir_stdlib/src/eddsa.nr @@ -1,7 +1,6 @@ use crate::hash::poseidon; use crate::ec::consts::te::baby_jubjub; use crate::ec::tecurve::affine::Point as TEPoint; - // Returns true if x is less than y fn lt_bytes32(x: Field, y: Field) -> bool { let x_bytes = x.to_le_bytes(32); @@ -21,7 +20,6 @@ fn lt_bytes32(x: Field, y: Field) -> bool { } x_is_lt } - // Returns true if signature is valid pub fn eddsa_poseidon_verify( pub_key_x: Field, @@ -29,7 +27,7 @@ pub fn eddsa_poseidon_verify( signature_s: Field, signature_r8_x: Field, signature_r8_y: Field, - message: Field, + message: Field ) -> bool { // Verifies by testing: // S * B8 = R8 + H(R8, A, m) * A8 @@ -40,26 +38,19 @@ pub fn eddsa_poseidon_verify( let signature_r8 = TEPoint::new(signature_r8_x, signature_r8_y); assert(bjj.curve.contains(signature_r8)); - // Ensure S < Subgroup Order 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]); - // Calculate second part of the right side: right2 = h*8*A - // Multiply by 8 by doubling 3 times. This also ensures that the result is in the subgroup. let pub_key_mul_2 = bjj.curve.add(pub_key, pub_key); let pub_key_mul_4 = bjj.curve.add(pub_key_mul_2, pub_key_mul_2); let pub_key_mul_8 = bjj.curve.add(pub_key_mul_4, pub_key_mul_4); - // We check that A8 is not zero. assert(!pub_key_mul_8.is_zero()); - // Compute the right side: R8 + h * A8 let right = bjj.curve.add(signature_r8, bjj.curve.mul(hash, pub_key_mul_8)); - // Calculate left side of equation left = S * B8 let left = bjj.curve.mul(signature_s, bjj.base8); diff --git a/noir_stdlib/src/field.nr b/noir_stdlib/src/field.nr index 0c6e282f449..b4cb9b64e3c 100644 --- a/noir_stdlib/src/field.nr +++ b/noir_stdlib/src/field.nr @@ -1,4 +1,3 @@ - impl Field { pub fn to_le_bits(self: Self, bit_size: u32) -> [u1] { crate::assert_constant(bit_size); @@ -82,9 +81,8 @@ pub fn modulus_be_bytes() -> [u8] {} #[builtin(modulus_le_bytes)] pub fn modulus_le_bytes() -> [u8] {} - // Convert a 32 byte array to a field element -pub fn bytes32_to_field(bytes32 : [u8; 32]) -> Field { +pub fn bytes32_to_field(bytes32: [u8; 32]) -> Field { // Convert it to a field element let mut v = 1; let mut high = 0 as Field; @@ -95,7 +93,6 @@ pub fn bytes32_to_field(bytes32 : [u8; 32]) -> Field { low = low + (bytes32[16 + 15 - i] as Field) * v; v = v * 256; } - // Abuse that a % p + b % p = (a + b) % p and that low < p low + high * v -} \ No newline at end of file +} diff --git a/noir_stdlib/src/hash.nr b/noir_stdlib/src/hash.nr index f6a468048a0..157d6518367 100644 --- a/noir_stdlib/src/hash.nr +++ b/noir_stdlib/src/hash.nr @@ -2,41 +2,38 @@ mod poseidon; mod mimc; #[foreign(sha256)] -pub fn sha256(_input : [u8; N]) -> [u8; 32] {} +pub fn sha256(_input: [u8; N]) -> [u8; 32] {} #[foreign(blake2s)] -pub fn blake2s(_input : [u8; N]) -> [u8; 32] {} +pub fn blake2s(_input: [u8; N]) -> [u8; 32] {} struct PedersenPoint { x : Field, y : Field, } -pub fn pedersen_commitment(input : [Field; N]) -> PedersenPoint { +pub fn pedersen_commitment(input: [Field; N]) -> PedersenPoint { pedersen_commitment_with_separator(input, 0) } #[foreign(pedersen)] -pub fn __pedersen_commitment_with_separator(_input : [Field; N], _separator : u32) -> [Field; 2] {} - -pub fn pedersen_commitment_with_separator(input : [Field; N], separator : u32) -> PedersenPoint { - let values = __pedersen_commitment_with_separator(input, separator); - PedersenPoint { - x: values[0], - y: values[1], - } +pub fn __pedersen_commitment_with_separator(_input: [Field; N], _separator: u32) -> [Field; 2] {} + +pub fn pedersen_commitment_with_separator(input: [Field; N], separator: u32) -> PedersenPoint { + let values = __pedersen_commitment_with_separator(input, separator); + PedersenPoint { x: values[0], y: values[1] } } -pub fn pedersen_hash(input : [Field; N]) -> Field { +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 {} +pub fn pedersen_hash_with_separator(_input: [Field; N], _separator: u32) -> Field {} -pub fn hash_to_field(_input : [Field; N]) -> Field { +pub fn hash_to_field(_input: [Field; N]) -> Field { let mut inputs_as_bytes = []; - + for i in 0..N { let input_bytes = _input[i].to_le_bytes(32); for i in 0..32 { @@ -49,5 +46,5 @@ pub fn hash_to_field(_input : [Field; N]) -> Field { } #[foreign(keccak256)] -pub fn keccak256(_input : [u8; N], _message_size: u32) -> [u8; 32] {} +pub fn keccak256(_input: [u8; N], _message_size: u32) -> [u8; 32] {} diff --git a/noir_stdlib/src/hash/mimc.nr b/noir_stdlib/src/hash/mimc.nr index 7f778df14c1..10c0a48917c 100644 --- a/noir_stdlib/src/hash/mimc.nr +++ b/noir_stdlib/src/hash/mimc.nr @@ -3,20 +3,19 @@ // You must use constants generated for the native field // Rounds number should be ~ log(p)/log(exp) // For 254 bit primes, exponent 7 and 91 rounds seems to be recommended -fn mimc(x: Field, k: Field, constants: [Field; N], exp : Field) -> Field { +fn mimc(x: Field, k: Field, constants: [Field; N], exp: Field) -> Field { //round 0 let mut t = x + k; let mut h = t.pow_32(exp); //next rounds - for i in 1 .. constants.len() { + for i in 1..constants.len() { t = h + k + constants[i]; h = t.pow_32(exp); - }; + } h + k } global MIMC_BN254_ROUNDS = 91; - //mimc implementation with hardcoded parameters for BN254 curve. #[field(bn254)] pub fn mimc_bn254(array: [Field; N]) -> Field { @@ -24,7 +23,7 @@ pub fn mimc_bn254(array: [Field; N]) -> Field { let exponent = 7; //generated from seed "mimc" using keccak256 let constants: [Field; MIMC_BN254_ROUNDS] = [ - 0, + 0, 20888961410941983456478427210666206549300505294776164667214940546594746570981, 15265126113435022738560151911929040668591755459209400716467504685752745317193, 8334177627492981984476504167502758309043212251641796197711684499645635709656, @@ -114,7 +113,7 @@ pub fn mimc_bn254(array: [Field; N]) -> Field { 4212716923652881254737947578600828255798948993302968210248673545442808456151, 7594017890037021425366623750593200398174488805473151513558919864633711506220, 18979889247746272055963929241596362599320706910852082477600815822482192194401, - 13602139229813231349386885113156901793661719180900395818909719758150455500533, + 13602139229813231349386885113156901793661719180900395818909719758150455500533 ]; let mut r = 0; diff --git a/noir_stdlib/src/hash/poseidon.nr b/noir_stdlib/src/hash/poseidon.nr index bf45c3ecd23..3f4de73c0db 100644 --- a/noir_stdlib/src/hash/poseidon.nr +++ b/noir_stdlib/src/hash/poseidon.nr @@ -1,5 +1,4 @@ mod bn254; // Instantiations of Poseidon for prime field of the same order as BN254 - use crate::field::modulus_num_bits; struct PoseidonConfig { @@ -11,96 +10,83 @@ struct PoseidonConfig { mds: [Field; N] // MDS Matrix in row-major order } -pub fn config( +pub fn config( t: Field, rf: u8, rp: u8, alpha: Field, ark: [Field; M], - mds: [Field; N]) - -> PoseidonConfig { + mds: [Field; N] +) -> PoseidonConfig { // Input checks let mul = crate::wrapping_mul(t as u8, (rf + rp)); assert(mul == ark.len() as u8); assert(t * t == mds.len()); assert(alpha != 0); - - PoseidonConfig {t, rf, rp, alpha, ark, mds} -} + PoseidonConfig { t, rf, rp, alpha, ark, mds } +} // General Poseidon permutation on elements of type Field -fn permute( - pos_conf: PoseidonConfig, - mut state: [Field; O]) - -> [Field; O] { +fn permute(pos_conf: PoseidonConfig, mut state: [Field; O]) -> [Field; O] { let PoseidonConfig {t, rf, rp, alpha, ark, mds} = pos_conf; assert(t == state.len()); - - let mut count = 0; + let mut count = 0; // for r in 0..rf + rp - for r in 0..(ark.len()/state.len()) { + for r in 0..(ark.len() / state.len()) { for i in 0..state.len() { state[i] = state[i] + ark[count + i]; } // Shift by round constants - state[0] = state[0].pow_32(alpha); - // Check whether we are in a full round - if (r as u8 < rf/2) | (r as u8 >= rf/2 + rp) { + if (r as u8 < rf / 2) | (r as u8 >= rf / 2 + rp) { for i in 1..state.len() { state[i] = state[i].pow_32(alpha); } } - + state = apply_matrix(mds, state); // Apply MDS matrix count = count + t; } state } - // Absorption. Fully absorbs input message. -fn absorb( +fn absorb( pos_conf: PoseidonConfig, mut state: [Field; O], // Initial state; usually [0; O] rate: Field, // Rate capacity: Field, // Capacity; usually 1 - msg: [Field; P]) // Arbitrary length message - -> [Field; O] { + msg: [Field; P] +) -> [Field; O] { assert(pos_conf.t == rate + capacity); - + let mut i = 0; for k in 0..msg.len() { // Add current block to state state[capacity + i] += msg[k]; i = i+1; - // Enough to absorb if i == rate { state = permute(pos_conf, state); i = 0; } } - // If we have one more block to permute if i != 0 { state = permute(pos_conf, state); } - + state } - - // Check security of sponge instantiation fn check_security(rate: Field, width: Field, security: Field) -> bool { let n = modulus_num_bits(); - ((n-1)*(width-rate)/2) as u8 > security as u8 + ((n - 1) * (width - rate) / 2) as u8 > security as u8 } - // A*x where A is an n x n matrix in row-major order and x an n-vector fn apply_matrix(a: [Field; M], x: [Field; N]) -> [Field; N] { let mut y = x; @@ -108,9 +94,9 @@ fn apply_matrix(a: [Field; M], x: [Field; N]) -> [Field; N] { for i in 0..x.len() { y[i] = 0; for j in 0..x.len() { - y[i] = y[i] + a[x.len()*i + j]* x[j]; + y[i] = y[i] + a[x.len()*i + j]* x[j]; } } - + y } diff --git a/noir_stdlib/src/hash/poseidon/bn254.nr b/noir_stdlib/src/hash/poseidon/bn254.nr index f93ff577c97..0db6d9546dc 100644 --- a/noir_stdlib/src/hash/poseidon/bn254.nr +++ b/noir_stdlib/src/hash/poseidon/bn254.nr @@ -4,14 +4,10 @@ mod consts; use crate::hash::poseidon::PoseidonConfig; 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]) - -> [Field; O] { +pub fn permute(pos_conf: PoseidonConfig, mut state: [Field; O]) -> [Field; O] { let PoseidonConfig {t, rf: config_rf, rp: config_rp, alpha, ark, mds} = pos_conf; let rf = 8; let rp = [56, 57, 56, 60, 60, 63, 64, 63, 60, 66, 60, 65, 70, 60, 64, 68][state.len() - 2]; @@ -19,234 +15,222 @@ pub fn permute( assert(t == state.len()); assert(rf == config_rf as Field); assert(rp == config_rp as Field); - - let mut count = 0; + let mut count = 0; // First half of full rounds - for _r in 0..rf/2 { + for _r in 0..rf / 2 { for i in 0..state.len() { state[i] = state[i] + ark[count + i]; } // Shift by round constants - for i in 0..state.len() { state[i] = state[i].pow_32(alpha); } - + state = apply_matrix(mds, state); // Apply MDS matrix count = count + t; } - // Partial rounds for _r in 0..rp { for i in 0..state.len() { state[i] = state[i] + ark[count + i]; } // Shift by round constants - state[0] = state[0].pow_32(alpha); - + state = apply_matrix(mds, state); // Apply MDS matrix count = count + t; } - // Second half of full rounds - for _r in 0..rf/2 { + for _r in 0..rf / 2 { for i in 0..state.len() { state[i] = state[i] + ark[count + i]; } // Shift by round constants - for i in 0..state.len() { state[i] = state[i].pow_32(alpha); } - + state = apply_matrix(mds, state); // Apply MDS matrix count = count + t; } - + state } - // Corresponding absorption. #[field(bn254)] -fn absorb( +fn absorb( pos_conf: PoseidonConfig, mut state: [Field; O], // Initial state; usually [0; O] rate: Field, // Rate capacity: Field, // Capacity; usually 1 msg: [Field; P] // Arbitrary length message ) -> [Field; O] { - assert(pos_conf.t == rate + capacity); - + let mut i = 0; for k in 0..msg.len() { // Add current block to state state[capacity + i] += msg[k]; i = i+1; - // Enough to absorb if i == rate { state = permute(pos_conf, state); i = 0; } } - // If we have one more block to permute if i != 0 { state = permute(pos_conf, state); } - + state } - // 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] + absorb(consts::x5_5_config(), [0; 5], 4, 1, msg)[1] } - // Various instances of the Poseidon hash function // Consistent with Circom's implementation pub fn hash_1(input: [Field; 1]) -> Field { let mut state = [0; 2]; for i in 0..input.len() { - state[i+1] = input[i]; + state[i+1] = input[i]; } - + perm::x5_2(state)[0] } pub fn hash_2(input: [Field; 2]) -> Field { let mut state = [0; 3]; for i in 0..input.len() { - state[i+1] = input[i]; + state[i+1] = input[i]; } - + perm::x5_3(state)[0] } pub fn hash_3(input: [Field; 3]) -> Field { let mut state = [0; 4]; for i in 0..input.len() { - state[i+1] = input[i]; + state[i+1] = input[i]; } - + perm::x5_4(state)[0] } pub fn hash_4(input: [Field; 4]) -> Field { let mut state = [0; 5]; for i in 0..input.len() { - state[i+1] = input[i]; + state[i+1] = input[i]; } - + perm::x5_5(state)[0] } pub fn hash_5(input: [Field; 5]) -> Field { let mut state = [0; 6]; for i in 0..input.len() { - state[i+1] = input[i]; + state[i+1] = input[i]; } - + perm::x5_6(state)[0] } pub fn hash_6(input: [Field; 6]) -> Field { let mut state = [0; 7]; for i in 0..input.len() { - state[i+1] = input[i]; + state[i+1] = input[i]; } - + perm::x5_7(state)[0] } pub fn hash_7(input: [Field; 7]) -> Field { let mut state = [0; 8]; for i in 0..input.len() { - state[i+1] = input[i]; + state[i+1] = input[i]; } - + perm::x5_8(state)[0] } pub fn hash_8(input: [Field; 8]) -> Field { let mut state = [0; 9]; for i in 0..input.len() { - state[i+1] = input[i]; + state[i+1] = input[i]; } - + perm::x5_9(state)[0] } pub fn hash_9(input: [Field; 9]) -> Field { let mut state = [0; 10]; for i in 0..input.len() { - state[i+1] = input[i]; + state[i+1] = input[i]; } - + perm::x5_10(state)[0] } pub fn hash_10(input: [Field; 10]) -> Field { let mut state = [0; 11]; for i in 0..input.len() { - state[i+1] = input[i]; + state[i+1] = input[i]; } - + perm::x5_11(state)[0] } pub fn hash_11(input: [Field; 11]) -> Field { let mut state = [0; 12]; for i in 0..input.len() { - state[i+1] = input[i]; + state[i+1] = input[i]; } - + perm::x5_12(state)[0] } pub fn hash_12(input: [Field; 12]) -> Field { let mut state = [0; 13]; for i in 0..input.len() { - state[i+1] = input[i]; + state[i+1] = input[i]; } - + perm::x5_13(state)[0] } pub fn hash_13(input: [Field; 13]) -> Field { let mut state = [0; 14]; for i in 0..input.len() { - state[i+1] = input[i]; + state[i+1] = input[i]; } - + perm::x5_14(state)[0] } pub fn hash_14(input: [Field; 14]) -> Field { let mut state = [0; 15]; for i in 0..input.len() { - state[i+1] = input[i]; + state[i+1] = input[i]; } - + perm::x5_15(state)[0] } pub fn hash_15(input: [Field; 15]) -> Field { let mut state = [0; 16]; for i in 0..input.len() { - state[i+1] = input[i]; + state[i+1] = input[i]; } - + perm::x5_16(state)[0] } pub fn hash_16(input: [Field; 16]) -> Field { let mut state = [0; 17]; for i in 0..input.len() { - state[i+1] = input[i]; + state[i+1] = input[i]; } - + perm::x5_17(state)[0] } diff --git a/noir_stdlib/src/hash/poseidon/bn254/consts.nr b/noir_stdlib/src/hash/poseidon/bn254/consts.nr index ef4d5a6fce4..62b5f4b5212 100644 --- a/noir_stdlib/src/hash/poseidon/bn254/consts.nr +++ b/noir_stdlib/src/hash/poseidon/bn254/consts.nr @@ -2,21 +2,18 @@ // Consistent with https://github.com/iden3/circomlib/blob/master/circuits/poseidon.circom and https://github.com/iden3/circomlib/blob/master/circuits/poseidon_constants.circom use crate::hash::poseidon::PoseidonConfig; use crate::hash::poseidon::config; - // Number of full rounds global rf = 8; - // Number of partial rounds fn rp() -> [u8; 16] { [56, 57, 56, 60, 60, 63, 64, 63, 60, 66, 60, 65, 70, 60, 64, 68] } - // S-box power fn alpha() -> Field { 5 } - // Poseidon configurations for states of size 2 to 17. +// noir-fmt:ignore pub fn x5_2_config() -> PoseidonConfig<128, 4> { config( 2, @@ -28,6 +25,7 @@ pub fn x5_2_config() -> PoseidonConfig<128, 4> { ) } +// noir-fmt:ignore pub fn x5_3_config() -> PoseidonConfig<195, 9> { config( 3, @@ -38,7 +36,7 @@ pub fn x5_3_config() -> PoseidonConfig<195, 9> { [7511745149465107256748700652201246547602992235352608707588321460060273774987,10370080108974718697676803824769673834027675643658433702224577712625900127200,19705173408229649878903981084052839426532978878058043055305024233888854471533,18732019378264290557468133440468564866454307626475683536618613112504878618481,20870176810702568768751421378473869562658540583882454726129544628203806653987,7266061498423634438633389053804536045105766754026813321943009179476902321146,9131299761947733513298312097611845208338517739621853568979632113419485819303,10595341252162738537912664445405114076324478519622938027420701542910180337937,11597556804922396090267472882856054602429588299176362916247939723151043581408]) } - +// noir-fmt:ignore pub fn x5_4_config() -> PoseidonConfig<256, 16> { config( 4, @@ -49,7 +47,7 @@ pub fn x5_4_config() -> PoseidonConfig<256, 16> { [16023668707004248971294664614290028914393192768609916554276071736843535714477,17849615858846139011678879517964683507928512741474025695659909954675835121177,1013663139540921998616312712475594638459213772728467613870351821911056489570,13211800058103802189838759488224684841774731021206389709687693993627918500545,19204974983793400699898444372535256207646557857575315905278218870961389967884,3722304780857845144568029505892077496425786544014166938942516810831732569870,11920634922168932145084219049241528148129057802067880076377897257847125830511,6085682566123812000257211683010755099394491689511511633947011263229442977967,14672613178263529785795301930884172260797190868602674472542654261498546023746,20850178060552184587113773087797340350525370429749200838012809627359404457643,7082289538076771741936674361200789891432311337766695368327626572220036527624,1787876543469562003404632310460227730887431311758627706450615128255538398187,21407770160218607278833379114951608489910182969042472165261557405353704846967,16058955581309173858487265533260133430557379878452348481750737813742488209262,593311177550138061601452020934455734040559402531605836278498327468203888086,341662423637860635938968460722645910313598807845686354625820505885069260074] ) } - +// noir-fmt:ignore pub fn x5_5_config() -> PoseidonConfig<340, 25> { config( 5, @@ -60,7 +58,7 @@ pub fn x5_5_config() -> PoseidonConfig<340, 25> { [16789463359527776692258765063233607350971630674230623383979223533600140787105,17179611066821656668705197789232102741366879862607190942874777813024566441829,18653277315487164762584377009009109585010878033606596417396490909822722930739,7373070639853668650581790286343199505413793790160702463077019294817051722180,4823864393442908763804841692709014014130031798360007432734996408628916373879,19196309854577132760746782449135315310664418272926255500908899397538686486585,18123132816088485879885148351452823314623055244145916622592591084094232513914,18436594886553181913092702411547018228276047601279727265790147051821171174455,15167500404313194506503404655898040457721633218143681920692711693000769735187,9437986152015460505719924283993842205604222075968464846270136901243896809793,21445376105821232747280055223032050399373725161014449207033808524504027971613,49684738714301073369749035791061182456037935161360748355432247732088942674,9826409059947591908303145327284336313371973037536805760095514429930589897515,8494798325496773219358794086647759478982958403252584257436898618394561204124,21251937175072447337747316555423152807036003235223125066270735279039060889959,5539100337780919206842837176908516952801756637410959104376645017856664270896,6297628909516159190915174165284309160976659474973668336571577778869958189934,12792263637464508665199868777503118105486490400267592501708855807938962470650,17254685306085558791725544672172906900581495686070720065168939143671412445514,3590396502942934679818900672232030233017710909687947858184099000783280809247,19055249881366445073616526879263250763682650596233071589085239500077496415637,7367697936402141224946246030743627391716576575953707640061577218995381577033,1322791522030759131093883057746095061798181102708855007233180025036972924046,20456741074925985565499300081580917471340328842103779922028754640077047587707,9059147312071680695674575245237100802111605600478121517359780850134328696420] ) } - +// noir-fmt:ignore pub fn x5_6_config() -> PoseidonConfig<408, 36> { config( 6, @@ -71,7 +69,7 @@ pub fn x5_6_config() -> PoseidonConfig<408, 36> { [8266021233794274332054729525918686051968756165685671155584565440479247355160,7947823415909040438587565055355894256799314737783432792935458921778371169026,16508811191852041977017821887204137955816331040385276110261643892701458724933,1804800467126006102677564831888710635194614232739335985819349312754063580223,11189892034806587650995829160516587240879881493093022855087765921356611070470,20567450145123179140729389574352706949280207113956641415022972885523439610844,4666756311257455192796774305229624459258864488677689058174087310651786875914,11389253665835451896363091846189307652796786468610595637047377864063404843117,18793736599347263150867965517898541872137378991464725717839931503944801692688,4206344588923325482680116848820594823631536459347642329098796888497153867720,1739462481670645248707834504605096139894257554120906850613041004917967456145,18514227342636266640333254638454588508118462110178719555586534011641424431745,17887039315911403193186866703775654467672391491657957999455462537283842145802,2824959020572825365047639014537190268717891749361604043531643698340708119767,12521547103713919592301476538318318223836047611311454785951907894055964264287,8658146183671258251984364885894342376430874614261222570603159082682815800788,154390145585284450772861151318029820117470958184878116158462181541183085587,7593705166056392393963956710828665339496927193740869686529339432486182720653,5529559239163081088908568555890212324771345012509269613465629182165427812002,3729910453162885538930719732708124491456460687048972152311428493400220125686,11942815243552870715777415109008273807076911177089425348095503288499102855779,498938524453430895689241565973888863905147713935369405079343247530256066618,3976257517234324421403708035200810671331954932478384823208414346189926720724,723540703523219510043977323240437576248315561543814629392162302024056718473,13306548824219676333032339487546407241767961556934015003605485324283250885682,7970147269291664639740298762956131361316495463191268382513594527221399186752,20633313939958767604804835838065337107615699351647541991788258289962727735454,17162090859520817529294904484646695645841022315617926715432606252643123848792,9181379842957190051440498041153333325098774266789773971685141362947015398641,7051606617662816798224904133351061549832959857069896192072217769241273559278,16619522548478824222688310091434959542211899852679631815023615875678448806029,14965311177811968100298579672135357167599499478246106482433786066289128683961,9792733250919070275775594069208673385381167169182805600474820364274865306108,2069253833779081039049908513863485270550301879399727430830923273191877809560,15847298987712771667136245955631872888473964330474501593909263901393348546986,12244443532166430060291409356011430759892629145539185535677568234713942157668] ) } - +// noir-fmt:ignore pub fn x5_7_config() -> PoseidonConfig<497, 49> { config( 7, @@ -82,7 +80,7 @@ pub fn x5_7_config() -> PoseidonConfig<497, 49> { [19332164824128329382868318451458022991369413618825711961282217322674570624669,12346323761995603285640868741615937712088302657627126374070962894016296466118,3913895681115272361294397190916803190924061797587910478563401817340941991811,7048322889096718105055545382948709082135086733564574465991576956878202831861,10375086910057323893637057154182902576957472442368661576421122036461645295833,12765622911241487148932810040772504127756393086809438933166282251044289864727,266900212758702307861826326591090138389415348463003233900705815890364224151,14435131616556129905356866638030823183270286404767286105643513738132789033353,5780976801287540146775934937953368730928109502001687434229528186520268917700,1618320442446662026869390273942730786145909339107736579759397243640902802126,3818399583522206096165108192531271582827953520684743806492664825009577810261,11764506724346386316602508039052965575734225646587104133777798242528580374987,2414215974836165993714858157462355581258152126063378817495129367240311967136,17609437036230923129211608175600293197801044251801590649435913902851695334081,363438080029711424794236047863047716381155074181485245036621530063262917196,535766679023716739184211613469394818313893958493710642899297971974381051070,5305068908469731303772738758164870877638068032868328180355958394150421214337,10807632568240507366657354568432178961148417327580695024415275247652313539292,15964415873358391713354948903242729080763777490509563223190335273158191600135,20700362719972015883260687302741075186857660623182772413609788566925949033885,10135127975676256977820296631533839366076919827597067890970660746228807376456,4251490167543116819728642817282216847143714366441358372252125244838181656331,7745587495915033527847242564710473705100826890903278244320948416581724663023,11741113129223221800185946819924457344647035336264986754437921049066977440806,11630296782890656599545188109639399768829653360050213193782325240600583381364,16861140446185941149398487176581839232380972247302922484807333229513905651035,365879246117123675211400356410703684399715291171114630107795112994207447819,21725607857580053522363567649763546934441685061337033780528788383243719579033,9222866548596464928765000608129177609426964853736257576074550520759533736918,10261578281201197531384003420612639018011405529775212563256392340336951230146,15644037447921591571869862919382888810859308861783088910843592577202362807673,12752004188139535619565478547449108772137477456363099481095747591698702436636,4205805109630387448825516813913983509046636797101589615147198457314360427718,21047095155106717901091873146599497621258071512562421967648909471775919992713,15624165295872926124160584750951090817255240214488120310950503163805737026315,15064589937731741958666763896598138037875460434244947486199623542160035749721,1801577872277160959016940766173040841160105238799805406938450020949902989173,2896766420608048344829901127120623317655260981420052771341833288256800199953,12828791469509204618898135640019714232831708508424682785876476343251730674999,21363471986981372923191391880511344708743312828234098289107697080824665183315,21372706354350795416381912271616633829725494570576895047490974943034914894898,16006531510217730955981102005088687858079561573088629102219485906666961331083,2389357602244845938251345005183369360523566673990464798041306722747500447645,15275955107196234672088664710679934029171843237458844492987233368659104714648,8038797517535218686870517662905230585331773059774130312418943649247287196930,17923922393436914864421862212181654800719733137689602673604754147078808030201,12890519745320143484176500044628647247549456778462652469313611980363507314914,8058516556024397257577081553178859094042894928866720408652077334516681924252,768425396034382182896247252731538808045254601036758108993106260984310129743] ) } - +// noir-fmt:ignore pub fn x5_8_config() -> PoseidonConfig<576, 64> { config( 8, @@ -93,7 +91,7 @@ pub fn x5_8_config() -> PoseidonConfig<576, 64> { [12051363189633051999486642007657476767332174247874678146882148540363198906151,6387692555402871022209406699166470377527846400909826148301704257996818597444,5501161701967897191598344153113501150221327945211106479845703139297020305204,11704372055359680530622226011526065512090721245437046184430227296826364812961,1448611482943320179763394986273491989368427112997509352702795612841455555221,11429145481524962708631235759094055797723504985787912972575745356597208940857,18021858528471759023192195347788820214752298716891162685115069036283008604659,19817577944622399780828745167469547332167999743980557486183403063955748437619,16868980302925985719076889965831700407328155411673408077166038059874616424216,14717432944340806781505761211058502775325970511884444497202848327581753493322,6273484270523289845253546319956998489830555038697388950038256377785540828355,7726043103954429233325852791166106732104332590864071922310309250010129731951,21052353119157611359715869265647287129868507410601603360127523286602350622783,14881796557136180514390287939887071460258251160875710427576954128871507002642,16341327439981153879863707938117355436152690262312411284193970279829974799334,10737675906107372302108775622264379258926415910493665638388971468924879578019,17652699767629314433191915267767147860052614073432922215674211498672835339113,7457854400138129895665591719907473144796504905294990100367501377050420942800,2136850802972823585140870808569264373787409642804109426616292140046700710743,14029467347298896610468190615212519453678316548442709087191045978401072380889,17927699952921266007590534383984238136710494507499176330493504416180410161683,1404719213830610030709583332543456268094679432456284386108188509031502237811,15774757292079018355173698870903422490868220545526384876021336136892926326596,13992040374687149195439840459922227749294794072303579532004750946306028893274,19895094843870397064274579657905921299619388074084417486420154568847155746891,943833985612967248618844364501030453998731991825395875139617731659343743483,18334641092245356682448009823797080853859186519922476229272838591594967878678,12440287044655505483131716236615633401781045711053210640202766768864619378050,19130942564098572936370308509908873069169152245172660555660369853346605570826,13687979327148217614616687417475244897906227789285703940171633508277844471062,16887921327479880141959363366262254722342925451159884082370074726344024008329,20378003125024698406589040864014894045124234695859352480989552885205935609512,9961553412530901953022991497331082655746860319830309417179972582392489275965,17755268665220780466271147660314410613992814315871705414495724015443459797439,15394131279964876131165951719955566821453162041574233072088124095626652523043,12668230348320365182085867728169435383987570924921845106243310905832768752125,14046812111383844816383347755263287603387502282980410255379630204396960343368,11590093969266595252327261214735156204516524792938909229175092594303424141199,4623517074925959322927421514289132524032863498392441375476410779446526502799,11550389531965919926150256242174358326491059727918559332939872696684299343135,408487396317981846281976563618407581852133413686169882346565860317912856432,10717757571561029382519744040791773994731123262749372629687813122941078154016,21323787615496251932181222397986048515693661833099659753170924658480548866921,20780799310067873093555276926357624414275975377319941015818682052081980020892,9948385944800296129032348634683354181546876394979291412116493575442898426065,4957033413111065858035065225611730571499258914257595411830870977545212164095,5227254936689728148737265263965107718869714128941995977191096572191110991079,3582814872786080867997255427740166393615552773099677831398251586195329933975,2136737803483410555580163900871515004623198990079556379647848364282254542316,2965752098571712086281180512370022839542603960309127077035724860894697782076,1478525086510042909660572998242949118476342047444968703549274608283885678547,3563375996604290844805064443647611841824012587505923250907062088840679700555,15461452581843517997080348781604020486994675070532901120353124746087231692278,20472517020063295821544268171575414161230806406668271887185150097779785573889,21058001005918321995459971112208002381460494177332965873048074199074929946172,15805746645980285645504697043988763170971539673993759868487715403982423015009,7141240965656437676130015766799708612940092856280620325870466265817008351948,21418010338098024788434337801477243267248314524079104488811186206038748626642,20272108634229595317682817969506273496034097230124371921628691470754475805838,16734095147399743907618148751687506877774623133599770145304816136018239803101,8439324632051181834455499457268557602816180314723268640869118054114888151316,4953900961796661020464968131122569974581908893169105485631905994366550928492,18071625983692455679240094911529791119099077429122520426399552756115503123111,19638917592063029281156873227053827678889868373299664608974791764751784473040] ) } - +// noir-fmt:ignore pub fn x5_9_config() -> PoseidonConfig<639, 81> { config( 9, @@ -104,7 +102,7 @@ pub fn x5_9_config() -> PoseidonConfig<639, 81> { [708458300293891745856425423607721463509413916954480913172999113933455141974,14271228280974236486906321420750465147409060481575418066139408902283524749997,15852878306984329426654933335929774834335684656381336212668681628835945610740,14650063533814858868677752931082459040894187001723054833238582599403791885108,5582010871038992135003913294240928881356211983701117708338786934614118892655,17817167707934144056061336113828482446323869140602919022203233163412357573520,16618894908063983272770489218670262360190849213687934219652137459014587794085,10883405878649359800090160909097238327402403049670067541357916315880123123342,7439184039942350631846254109167666628442833987137988596039526179738154790587,2727663760525187222746025175304386977552466570311228286110141668880678011929,16992375884417886634716738306539629570444547136030480542879886913528563834233,4178586893949624406750122665277033849762243490544460031634329370298105635905,2517914797385699886738929430037355069462619900197972886482360691236776726214,20164173810534657634631187494276970100735049909727379228976555863615716408280,19970958827248077001061220127605534603528515080207197493660642269195127427214,15606275977308968307194602612931727810866183872589808138812916593200446820753,12261436001550634140750381230737452634746867040398895669545077774504957433511,10405309809257831434323731445544896504541938387524726028487604098725193737428,13408856444092113657034337770571899796129642125690066226794939383190876435468,19768080898957882918527124226120459667739640387901357739011662191034806046251,16749889646056241484852997428132695501278739424507088920371060969471495213919,12331609790192161246735870679870317366088443875784324655482358218146673901073,15769331739277556832196167201116801527901089923090632364403958141614820528626,5227172275505968397128736045169568430462701766148126842874241545343535393924,919073378344729780131814412541912290691661039815032069498359347682919854836,17858725475505870077023114050620337312678855554361132257763133392017321111169,21805188450184460363143840112266872832328782034569970452376470141743078343745,15808413311863154368918155104905222670782553225279887458053980771135357021692,12828907214414139667587331812274388831051429093098655261887619166452245292431,19323880880917307340820066456419195877039970908109908221992925424585030574269,17591732412986269470826282099678922890996647592922237928486497997144096433314,5282593184575641056912422403901924986019740793240905758215569065763629999318,16013130707598525718519250412251656096494468043256226360413191733653074896117,928381583587170989315021718439506896903185927814675820160976165627097308915,13354336789663524324458402003354905134416094005220899335023797754517805691310,8780135673134081873589118311874067764073719549433574820315100541871522642766,3334957744389892864165113989538814646945861179021194859030934481494560681812,10553413566358881045095498839713459314577909144176577153981801574128014927353,18894321506279909207228932263261226433242541255661384643559047811974513999438,20211894014628303327332299342564779073614790317614402383971270594430055013904,16723480621932556506775906903415088312771104391224076734252099577243237899106,1131872547334579236404174618548801749854242069301712398106619948805304881636,17386814048141719093058723520379257085987299288710382497237609774141718421404,13729980537487612221640320393867198844745491357830417754869369043292518007370,15860780436383591737179656321807464721751913977397035980422407138400867838633,14708550460111247278740231297332510059116901767161326454481923990389610737973,3132820559166321299152015048428879769905404947939291493327190426785911502819,8658132367999084824971296219169212568783540935524918908332001856872807119287,21064783047501777742084787259676320053480170916619513986794406566953069418035,20731000104011695148048713576219525164619502119638555785381543866326561323,17189725817866212967650950297463469529475851286172280116066228706121595462088,3310440878606659516028312898499559492876015493892608849966645073367377278233,18463918215326370595980949760897480127622730018343709491036454088497976892863,10894192430593140913557164014343360386192963621862346779515699758352916852228,5060610877870389107953459328006060153180283860738879092399406248484265273634,9068988823145592214189961315730261367007076042069390630024839612151270430414,13160707893890865447331361630522644819624543031829773191665491273833460019183,13920568292534026180186486064598876780779571940988254327823480971820885713801,3894011501178134026216736522445829906312115650019712122802932677318433032635,17895318821130376385979570244603067634449453259842805202694945793852667231847,9777993060458301797155055013115849176281006051494461044565335406558308324220,16521293541516305251718414192107787058980727971856888501176820100904791554730,7744063601405355255689420547832904761861257642931934580021876189691881462544,5444730929053688962452159157646022068806222098484627080046464163159451208522,1524118152994294864739915388438939180298324297960159419600850033701763764640,1334622237342346242862023763160346671504959163544406543315614662442562816653,16126317914306849967682996412350336172782726693375105190424151365140854833923,6345975085253358297751050638846919833013142450462810543971050115910612860460,2703875280053263252177031410407166981522153304496807669518295313468095058674,20550626512184448884716175825490086259235894802178999642552696391947509065676,15013718986700828670892638677446258841869291160144196138236407826511808592486,4682264015512203762723381542642871160915706748420642731100634327658667608042,12834108073603507925748862283503586970613250684810871463629807392488566121352,8422606792378744850363509404165092879785007388646473871019846954536829739979,9339209090550177650528715604504958143078492516052997365409534971861874881780,9141831918422847136631159987994781722269889810731887947045878986971886716767,18329180549061748373684938917948729366786279119056979983310618862430068636631,2009551904565170718789964252583363785971078331314490170341991643087565227885,3859729780601667888281187160881197567257456581829833310753128034179061564519,8535335342372994336873304745903510543599314397287086554558824692658347277251,14148514289641991520153975838000398174635263164584825009402034843810351225518] ) } - +// noir-fmt:ignore pub fn x5_10_config() -> PoseidonConfig<680, 100> { config( 10, @@ -115,7 +113,7 @@ pub fn x5_10_config() -> PoseidonConfig<680, 100> { [5029285279710800539227619495938136407778783814400587102957398897867261120664,21661833903534656620291231766157513264428291380933208423519374035927473262119,21013170147855726227668315492699186959893088673047129690411646575996043835024,15893628062504267735591398483514002406192781085288489283447316241330749546879,9860639032243003377544947110034203265885715041305770375052648470285182020229,10431760628292478929366440566994655480900443273305000842144090945543100651218,4662341343242273661833461144031815716144681076466659112993661636426666579986,6674279191498784183427663914511569570797862586816649467168170855788360268943,16895097041920841073767278653214275321407577186751547609698446652984399225877,8168606076413192332279322347673356872630772122089948509553934257426773045038,12091567755121016869657080116466607855522522017768906776539212195551888602502,4684576201081771194613696765517034834984066296253124029929753160055156611363,16693488266039456124835102259365515976900969074532557489095946797080826193662,7638443036775258881709317582832080783911189229963788890221615286494482929025,10111436214822932149781668218956845833675824936886829015449750181332010388640,896682691957564465177669890535917423987915406885797833670239687119295318467,12612639059115228106858238115822505521432423470330120640591982767272085175034,1851711744209473345586117150836616408053748535684022739058625441026889320297,14132260688735080257390420980422269734275443926576061985351678038992087770902,487493866037948515547037886552479973316400139387425953088274857424154262588,7712516772901240105339429973116360243232161870164307482409826131312962380842,20295556720945067049585659016570679551265845058805648954004989969704769135170,378208946912325140295069471345064814132951473534378635003955801655986417900,15111601008893945567629460471315838423301021468457758533702272669431620017222,1503682435556321218669089857094247703956565058167121192612334331910088441071,13084874799693933186811120569396911285611047490876409383659779579088985591229,17464483161247836988344436558341194021876261750085348252730901647076441211862,6628743087463083391707355927377412170189936607932592258517748766250528223430,15153763588458144568353947674975114179172744555450771328418442212716084083525,11217853102739260248713425002157925483291370125178251466195670948291389406199,11275485266433075885440484136400353724892671196084163231314370685019444807048,20167106354875398113371399754994549089359568833089630824992752829251678891797,14151330869211746069130604993916224881047448810615413435448712767752320095045,17260356243574396880210370581740651566334589568095587416844511054569255137183,12436078462666286197074526218535647721230687376129721353230123441759960021666,12001627458343654011606323250787666795709808266974343548842843520227918922255,15944850302839498288636342399223012131590208876255723227505947857641523034493,8444103924869263585176528654612076203716402818569041992813095331662367021655,13015682914180762871967848617514355587762125694235380084430680565032083402270,16200183380426364054409550129683752323493215428097334915015688753327665325485,13717643109958965551675619584464549580820722892266661529182798599670194908199,19801725181447377274232761944437523251067599053402428862557912155522673980500,8260354277364856843022982286494019620277496829494935775254726797533957063267,124621144162335766862972192337737579448571172779117809776129849377329817478,16488884047551411705397223604196364132975353217876182634038895586664127388979,17336432076451490238716890901095007360946878388179175784603587179384718443321,1210338460555723584699132156502555539583432069430631008706741082485009017102,5933432012048351362807861976737945204535374770355507745694008880123055490802,5127952499969178010015035020598142881788437616516517827214405489972695632240,21100924218139544842807404598627913291698574448527131003096325470925085906016,7683521602764604419863026286445694988900727173175219514555132623764360793654,20928394065137007852706990901925870323120588543710137320004640014111073449000,21375535333469484792161302750563386607223088895810564711097025913956371171769,8663517227154706072248636076587789834246541965140682871530851124960776424787,9182938389356039217318590654716613493414550996824701664670650439783557720226,8327338979442122743919832154397496089418582414082199116629974300650113777515,2474727241701323049333019668054716886184808783449917153147248751503852312804,8543922237501430855864877057711792269479294116675004771113148647309219620030,7863611214303285947093025404346084345102544167615769255495752297507346719791,1448902069752048144992778676670381235906144579949631101518897035253311063307,19501657783346989621892787238946890715709847672294934508902622542828235185048,17076525025777667838921778388186176564387475624769926249793144074465528465933,2381176586418291387279201678056498732033435079507661703992537801751492053086,20723508866659831749949206314442193102431573526415976696387848305764994281574,17461795780729443663350296040956479984433953861306521086706732257263430387445,14849025218838139413138931958408289986915143240245452275066866730847749323920,21207204042106390965753782189145584243052148578812105334769740484186308017901,3105302592226642624386332562899903659948819667537402316192380465808886843623,8765266846991616382097124552983206033439769882065573909634090515268812396114,9950016446092650730639179912416912603745831292536616469358668786853463197224,11739731747351277092817771330729393674312591071236310446088293450266807414263,5424991773995591044103668717299468589013142114099340604018933512575789323446,14582885509715812510585748465607279869582209618804039923778041514988867577359,12468934763690970929325823037406509081405444759649987929912706732364016057892,2792793293657306144108993077959195845478902430027171873963281969527327256602,8841327809851437433386666692145437950603022633472031964220924157605803799391,7845859360796082275932181771457755704129556353505380746504571839006944723429,10731793207832149137187382442869034250153492853628224932026933458041993639295,5597792614864287090861003890414825257635680048696075527563498604714157576447,2638669099010916296300870639816763122907432841565512299246441500223692345671,7150832464835357604208338666096132398994318721877322228060899549998179405057,5470477812928960639347760417261508685840724903499112719517942324191018679706,1063854480993555660259858748055514950231824974684462401269695511649059715242,14508243449586598349750829047481358081191713699373322296041764577478835760927,14872220983064543437506211589956319796231014912750035729896461676577407407598,9523202653584689553554068772241228948237208444616905879849472383190180438058,10557133197819890801524243760013157188954914093770589635201319240903423455316,4973822148190287060777561091733583032026446820262414806412485028147721872972,12017319043066808147670914562193696608548297038020764496633388575589573229927,20958507279974171556413354796214800332148109902768069171659933168603089927180,16142225389165963605704721785850680620029805525816101628767304750729950332962,21691255103889531967215183091383836488808797368461467004501598817850515277674,13360009791215314413428942977255018953699328534302248245107197249816193370823,5270206696221786165451075835596925139630328202641350960582852969440862939023,2626561181956261201864606929566987806068271006198808163435823619705436605447,5520368836328496672510351296660387187466158872913871354651108826881774455909,21597143280250120305740582323272730661347349587666707484376745221123282421748,5891209530846741397700015863630938364586207627850850447237189083999656313978,1202436381171550812585103405636986166232789491390007497511342220946215395818,9920320882147650877649039705433660083926352954797066179512349368247190410310] ) } - +// noir-fmt:ignore pub fn x5_11_config() -> PoseidonConfig<814, 121> { config( 11, @@ -126,7 +124,7 @@ pub fn x5_11_config() -> PoseidonConfig<814, 121> { [1098498142837982582047608372723518751721607512716925277273595859756333857326,498382712248562027578374863343601618793781182132084383060312181008958381971,19040726265283429618662679510157690394832296024968480927415996691029230011306,10367579130776133414495805974535693744211249758950881275217429221792836643614,1229596364469449066712193908302977020022727834238778132871229393863406546866,5594347757215876411130934611555467571639435097442631641074898978663329410864,625275312666547608222628560378372315159605662141936411119837279426221363981,15485529557721639677666143827295121022852505628489596851713462276650737776670,12156576509577081554587930818670905775536581975823788207855134544267814269606,1981640929928975005466842670997136169304057407742291166386016130552621471939,9375079124430521740651903984797221620963928972304905809259607327125669559872,268697279437287801043057266739136500465135819021738115532631740070584831216,9310725094036396036773344350803037792624399505581573214229419814378683970851,6144934044671205976376028664002834283864020049596457260475210339996948797436,4985941506647510031967748765284991041503308370910665002557248958100799063851,15851062719909725150709309168582658649310704358483047683106225599004779349418,9869770840966008659377598457679699092337106962689936558150689057592239644963,4964286354328869036674130011248598806906438908586967212984901377099285878228,13408525694456518383125684465410538061086669117275911801498275369395798296201,18263306792332242197764383101132914152275840410710698264525919817458731671889,10401786441956087930118823951510684636068781082958380915651220354850381871543,12496745101887166473879957440401384727148915595227764657145046356182346897947,56825204182651219072479187681186238157981743937496557304633023935549648224,7949519580094467639897040111470236633243836928348452962417270559805860514707,3509286722306670968352119363633866055096352721394520084890481975258162907251,21359945526252146173553061920944871506626324563977081669248710516265311530589,14649491209868365229844087258057697734286269047837985905275053819765825128984,12122186136173879572357400046587658543826161883897136171993927935307093999926,2666476328185593105035429309804341325262753927547102747066987631391232293139,21005241858197204874543384881533661499138265185107903730534607574687765896488,4866331653274711303641000079325074227730641553230218424779550288347820225149,938689939079340009195180604139206414955240264736983491692686499992823741696,950493909161345219342597929783079468041198261349024441783356363638640688155,8227093387774305505218050843028014038423742476679149203160700406235271548925,298899716277443866412562171123535849674476895336539413683307522836440058745,6985094123716229565713211140430519589886023406928617334981414752732877292051,4561102873171162160916461632027561255705058072826965137552144392802414262261,15422356128912397775473168682864290042256748428952418907369066530964035265216,21534011877473706794700774934355764894917955655606512952257743854629820348396,9461908500272520643111839486963426035162115487175673718316249722520977894185,9042969964854694648113546554619141983055960736166619708191725199599555275062,1441104948831954255692318866730011748129225465895791664253095290347818907280,19417400621113450826458192671383621002793369580946623762558060167661227354799,8244773274459817591888745631242804467035454174608673362960589130536385507190,17088086767144106377842029064730946925009348520592888187451688601493882340857,12886019902209719236096958359125451092745638766392722988311451127550961945660,20280862819329644063010032903732505647194710429034928708829957501178343790858,13239701144341900586601825324587185682073736334523805955933121583949546821724,2994618864933374534869864629648211464657674590007913715843569952783382900518,3072221011986428615228338853345294533299624086589539664037325300531050793357,13594276105600327401961157952766116939399999497643063180657161489419638074478,12904364780884039213184464580277965622079185353283126471569179129906875486852,15088962493677593800057541234990587773412340265413268221386103386021880406010,14138285403526705785804535000245522290348086552790608567368815987904186155718,13481415964846572771441311017814910258609608797603836070350286657768815710822,15459769479990273742477151452466966963353767555965255520456901549474045452607,8586052864861352028352866296665876117392195296860481710367953704812400661703,180502622991267551120688532508657597773982647209049478186474242637299204110,4785745751361586866577727263713743688205421961646731269452058881240942369409,4583871856798894230250707953295146343968130822948818555994825096960225600041,12377924729639905725281972784629126900954187435957722012223715002490809152047,21554415644278070156493674075483844873249829791940344144484983897474364915950,8390225843490125870104241611355504124284851919520955291024552578484662824128,2330476067094130593913781764168287559468546989640021387799865123741354870445,15749497374252464770935521609391859230015300749964554524771184068776070217841,16817654103281917947623051388088441309787140809596505043937473012669498321704,9987656178378986905964646161927549614205785047077068310684205046327286932204,21450061958292240283686535241652971764195183478875921481624114699420928365160,3904617432242099936494425054740854886663050476318725032541401300619628714123,21454964104289781104446533610149551385791852085041524046710270949744081353102,10768409462143965702783360646769759623397882338491564999208626639994081655791,19385613828688830964519526099114207553837496617978489639408163709100497624509,9385292780799468553063371906778802189174789542685475364513544798199315486080,19882577122462819381545089778080532575686772634821281258975533828284349988146,1462201549484596350490921057903425036211202388283463006651220816599917679116,16564642856725628254155356607086672564976261497486137590399143770170930986182,11606470848655267736219046910932382494518380844147406842964119623341701511194,546921055225672463086391798419385468083264065960104350335293012629066408625,12676737821548820987278730174038033161886561534502963159950183188070064038340,16429180804851559661054910451008618941371882312211198495282444364589225325606,8318514508896823373027050528521007144041407638548138855564062559664141902892,18546910687432012966956995548470714600618104024117576926439677823609854961263,12006683905722730408249989907056432037202625403043550391187503858618155798348,10816814135685807143320832554644398181525372167669730953193258726693903362148,1969445073620598650457101028079888612893685228913473332116076918643068711808,16873795316557869761040796336264749169213884122126281483001377666183529927793,8441268321647668856014389726368355391497206989491787976537908376817970369132,3378086906271763133245748026584767009750550242946195995254881868035794898559,12721353531573613369892164015903035636498816100971168742462654106875931342664,14969430369156214890953989610124286618925370029259450629468188666450865580556,8545723361883060050915916338313252821252873299513393695440138873537985282439,947668284380905375962163908708231363459059635485281084900173592739603282382,4418352807772484492818068921024797225893951828921880350002134747344565378254,18146914067008843660990756743559427698617136456156926109157771781314720068545,2353279078725994188579023195684884389261433430819033940093641668202046052763,18228226015329570627220992288018909552101992748538110505558715089403194764144,2251557590571495628913478692960173580728135227602564510397207128937882297417,20421664597091787362209209474226188711714308866665750343509458297343168321800,8187951594294388715811532560312339537604737243977265499957088579012554679278,15810834190411667509425096842396102750984990364193499272150958331088983323159,16884308240478579935994044823717491481297317573500280152191710196639752382061,234497484353824748419812158321111328486478789224631887096763967543932891899,21452418791072076854500976656696245147472896609273403517249960331326136475572,10860322289080285812992522532751459911253736747190334349942615321085283282595,149826608572716492570322179195234088797160854886751475825283168005807771516,11491761442863092383423796629001188933840969144934642247702733820824608517603,12099180244453415217270377899736157198045626379801787493348249001794558732373,13177983303979037999809722097100345612970493007300007493855625634642663397908,6849052800275826145043024580348093078809773712986428314364827674907764829568,21486255029472594818259653174918852363002807142725698741685253190938680807594,11451503340703054732459437884000132607423536025797075877436151438425159994269,8462539135531767509735697608276067216182907546891182278996691315801807234639,19944711893825824667372913293784300313762563232409638194240029859435259601775,10396631238556297232793544122243237485091433966091043100758266678889110827200,20667999270580360504376758654763163152764187226267414436968564661080084475852,10424436665500877000658892169756884171624649701456443210945810183301667922053,13894422482417998868290238401966517700776990643618129177567797594771207188055,9076475964444407787992938909179730031379198268423789105813333967195259669658,20479003631920854685589262232015009286810147171298477411667705150903826855301,9928015403359312830073752955992978705151208358029077246413002475277600546387,13981618256931763962905358530247354996931923386029793318275706908114940457317] ) } - +// noir-fmt:ignore pub fn x5_12_config() -> PoseidonConfig<816, 144> { config( 12, @@ -137,7 +135,7 @@ pub fn x5_12_config() -> PoseidonConfig<816, 144> { [20214838738486568883466588390719332066160511773018226407137866846447805607366,7161524737853996242838650618412058002168848579199128467811556550737619970970,2264369418377007316930430297757084139629356094085160360541578125176213258694,18691044064909968568998201940845291098399339626807500263611343942450116503516,15978743992268694554518277110515494413411623432213713029162001242329212269562,6711615239704822975151699228936015251056551262955961924747531220602950448829,14954997163751606686696628499315041796272082739441018134122451910369305642115,21573550100361192110069886620445669562472881453105471211193858578537227040439,21785281999660091964290541777959906196912107196794342243439922177000502203701,2946923208312508080510106804563669422427642075683605437758174474435322095802,14039283821812338763616072949057938719426671560747126284782727998420210694521,2531474643515410792989587528850930504447014242967363822821359471367799986101,14281461695965914110119049602449207565231627068856382054789426564141005041994,2155595480001027852247471998853878746887483662385654030663226564169133356539,6212474220474204735846033034823136351584003532895558668927059407038678087162,21589299957493491709069669042662513245508573637668760884022386808061869005942,5228547858762057503048110033821407961973668275986265942002757629551762149969,1151995769496843179907951142523838829938796346663877830241077357918848539138,16195901973518083237059346288792924649902586274815274684503783828189220931050,6205461827971201267719191643863468322713562983419848159871959495317073732623,21004710389082547785746156915318076260017385298749146368429985483091499557183,10094301525352802553607719810440185681054064961117719137647202357989110756759,1174362264673060234121108394303385502501621739298129145129042091221378391858,14586772089804608057953886654898255839796797046217599185042293580394420546552,704103301411330239947625288325002010320119746677418877341164806595452864925,7447867166827402056774077383104558156866119014007569966692643297177923018546,4252152864489296917539284690826221964698345550054947572793948075436067436040,3675525234832046985215853449128143168168428943627479235047788418993254287405,20125795627598431311475910664717716586147044241536953058242999762934679572886,9159576094573932436478222856304524043339640337232471953289062354187369243885,10410289328536677868407694844650868516861553712016012272941004725559785872650,18813119519933909103102649065156934680537361290190751928265976568411443987994,15043786404237278119878717250753259786450872051876817420168142382486008024593,16614805203312302723146840789675006378900903626996105116400354962001922700157,899949298359737140980259063526066233582477211127560605822280959405167872532,8350589775626940122507262589996655703528509795097550101006133878991750882468,10881253968160794744779175936360108103824976232977458894007732866457848744711,19359742822671794584060954988237182553116341604406926658049749172292672638977,10716853194721085390661796797973316855886234718612858006131046035921078793777,1194676839570189281149587289656564753779383829131008000754135056646064455278,11530412134598354110310733773537950950490005376234226554463355736782774653810,7158806839647137330333220334046918613209783693378018773439140974716028082046,18873459493111992992450800068055835432261777460679870727272006783676545919785,4597339034364379110034269874329162788488647975988086437272199171979371177111,16047595573111403874356093398802733070084530893238592035018321960924442437232,192949463851654477795020911703008125546432931266166268873310745978202434603,4826544617576366487123936439697751633333779280970103286526767080486441353413,10372441609969764399977561535165700928227575842447057367716683958896898456242,21479608666927871465054861416648367371602717876964759897062141685818604541372,18757812710789932354215078701254559681588101606101822541277700443926569010598,8502339138598356500092304059172334649791727023646195989902300809704249803746,21240184871409684692673423121366677112492469214890212851758021155034260698420,2702659403779176675766431784851669876796725738129029887042678538644093630255,18760062461290937265331504644060340132840729161526164449611377215801441916965,9598514148929007169331478849372274288455651725546984183500169574552892743616,12460679873938368098608659480431260988399308425323633114529665233186673892475,20582262751655750693560201069767758489467289978119794831247596435694971251287,7495462389257720258504478831214292184152544822380786356126692935003910627822,15847020891468169726540675640439992039404102490965287792626266482436024810091,13444178956365729587956577087448840645730541657243126743158358416431709484781,975733333906184480394673719901416555779305044861384485566696694336272649841,3016935868211088289963870855929013645268121688015888423636516996750583017171,20123197829824640950428347870445510232078708523077317828689832072338303017047,18496031799198869774970797646230665906722932354114482887753612521775690376535,448875332457320150287933426080386825611557032389972932765197125881964153702,15686083476904717209874986881961195356503069952883501862704199048297926079733,7399632407841430295111381086121470926608686430000074868388902950170939693998,19157441199146430337309347165554892283908758853741856357912555742738097866135,16120175937370916934366957179931217076202557540631878137626313655342796978134,17363463873417672052573440102339969267068334412527908172228668014397269133762,19764937897808275673467150361977575240242645746672288031015882089680753193420,4264866715026149043371443488601547814355809386242957666273811883512215893986,15361071774597522987390988933793735468585435977940286138223232105339041682390,13444894080484049025660420839638753203298145906295848687612728375851966859563,21344396291142953621865942956005813155481114773979414786364869990164493168988,13658955537084761077271566555621122724333408573482369456630860179831273897019,12249794154563702076745009616085271813195258124596555920372455161542247237218,2325936171131642979629131064685171177284405924159468319138840745089808621723,3077792516542862676300186898187316876000625806438082696997401720196346610884,21531439209065692564653170259849715075994439889756241721092517464459744953429,765723669836774164873260120197059605145439283015732643840276151768662398969,18430565163341347334129211602477703661982280889871625277638214021594755964149,15168574820004856312411802521805000105968244665018577358614174215627811033660,15609931056593305381714243964783225295053513474263648739398875586829969929857,13057901697952283349663465856361305032896972742145291496691475939407531431061,12802339382735521870414423620343194986509343830854539673006232369799979885569,7880620299082787885902391014823825400306816285911629201048471522567587002433,11085221899164994413080236199596538381402309021910771788195135649489784323294,5662061777175931509849062158785593075054461300898808576328927259801174692172,19620207415640534190314969542389551464821476135583607027506853686406083753807,1396012663571482634431038119696061726217340333644346862093678929991918911771,2833239640477482582925766504780450890721725782645633022528100604619065406714,9289278993548596713194730547769009982667061443580050906003394115646319823584,255970566924787837673441110425992267446525707891905710167559324774004600788,1607914894461957709182037732125046273691353312066921168498378132410220447224,18182823650001333075321511247233769219797858542696762318647781137777390858484,7933393968545943401801081658073805133658457161128306876557740191220424567009,20347911076420909832061080138703827506796370399337016973599032573292195453934,21705355682416154516146726727072637540254191883301906287097797468012136754530,4393707213821090202627671673506613966066953344462172841905371093203947245835,20015218870609611793683104277034599032802947064261475029925414549380954228847,1857954279082383201486002148223947538500417662449637107611316684336169564836,17075622394357639776259605879156105089449277115338468776425087829255085998708,7849339039625631210191134106813689727565217371730065596482503614929894173038,21512601485458872387622978217203423557092449252654722032828165846830386134980,18829825907628826679915224363735067358668372607988097049308159580108100510295,20145344934445170391525281095510437469996020214770888614087514859850402376676,9313354461544201805378332544085825218707302313348145137439630918018863309279,10073510764514576042491642785075889098754556687860419668420630560526859371108,11174613823246619246542059297257164876488140873425479553593597508295299674750,14486836623332191458290523271325176557072260063250466764030358280051240942286,1932726606175618877183776650118686155013167275195994452359992776843317764341,10586981584735794740885178709528523597770788130558769643251430679627096503451,17058731514535449611097320348142652958393616886312564975789110366863616539420,21546144187559470183347034044609056404441744756503592317087407561629774536920,5681882870231768621749544290358493454695957383787481123871436386675876219635,6603033703828934401094376159910456525337139277249114676008536852402499584614,1075347119451441392402288921187671249679641364496638526228862984392689015760,9887880282527621962449293235959776308591956208594163166185549404667941094205,3081779595493746844428351914840666042619592907445560123915127502392400574614,11109844704163389102553826717541117344605357734084342755776036340022417198082,5060345909602600407449982784585458050225699107485058131116856520723613936306,20049893406143885619592680397871697055896501875354968455865404394786911398458,11740809795693360891733016778293494519471041728488086332325924371909574885493,21414777615318644939200434569971788107661288737252832241371433802590137831626,19926309906539946638451151936495517057597377615887396199326446649447508281702,5833294070005894544680949322571753681474561462111500624110195335953784263127,20316262126697618722223967532370347145297985363803056816800332573538115921683,20118897454905330779316757365327082825225674670546613715170828215358297124461,15893782176793316439240260419014348246083695310846638270933249304684265430800,12847296795001788271556697499714377689095182754228824085698104180563585670787,17258109440267943312537478894153608811927087776527641627893802618672319064807,3727185744255496747036491258134142468721926815259510264718979349995349167789,2377620008282598351802066487452475263179928244128123362464911386705146759528,20330733534745333298462159658402131849518313653717741882717272744687077336453,3063488930518144343621406800230347607891775381984489334408858649400823600099,11822391183098027641060542512210687183510613996100060945754635806285989372827,2697686870567304805976687716601580249659499813659634827192211658186812105269,19466890284409856892962357589067669895394760875472697889494886746493744150398,1006970646211395884475799222625896618366447925898943829426435645426534803620,18668143903499292595688863135570950175417970684200058312498191992564173409237,374118929819602952730503470915153310582862106749955863047933775501492632816,8098759627317959799834443934069068232617039455327629644555780572940389866941,5647931789489182000343586961287147762347200093731102535565999902997464444183] ) } - +// noir-fmt:ignore pub fn x5_13_config() -> PoseidonConfig<949, 169> { config( 13, @@ -148,7 +146,7 @@ pub fn x5_13_config() -> PoseidonConfig<949, 169> { [5891205978627836991071144083270417159015157070199928807771268303875194037650,19534191765629085451497649051014772157774065629075791332793195826681584551273,5154833515272483128294702820663628026710043323095920240638701304804298499578,7917593571945709638335150893778153193741477651398934233734658265336884279055,5133163238095742835090645087711007173805146496653007212988348307349716673728,19458003745533910239158707983408152209004063097952693956218574434126899070042,18880819400751577287416293176849355951596193714265500681157024197361640709188,11433257809059443065528679883569438998689217744131300496692049205047550090935,12190874701550908088290603272755607342096152398135156662834905887211629834704,3673886960353993252497154566539843726250940370616316569888448647738018022083,15676037835112699420746702265028664494892600184195941373794207326709270851677,5045635616511022726309482514512221209262777381751689684810362174166837266849,2127981970274354891783037834911068612842150500572698772082540184222710046966,20602209860969247631763456039704661822909928028055826436834118113792574371849,4493454930923344041221912772221535937546211498548091351164191172571413962846,9203273896007845628978022785284502260659411177801743914849998536940830966257,8789969715987458351416076625905723956294447007026950650495844175328857015476,11215930522605102963220331022508304708093835095809374254680583403572809751875,15586225257380986275249577321891268850474446160605827963612996219805386502932,10011841080310254678847831138830289501574615297642717372149423820287945856079,18753960408421275836614693391945489354883623026056573684871883182548779915774,19761678450406536764850099419131825403291221931719095841429266089353917202891,2072721799733232450097521331054430993573774079728747665078766019206508544636,10615297085623730419003352440151421233872764050371800287995783143303761635742,11342862179098306415193268089882628716478112844171129584590093902612740081938,19276661508999391381559540866976056457016683157876039262437370976462342153704,3487982371390545412669202630006964510064537478038100559383689697998101381695,18019611455528497754832260490613820837001396745087990928355304172772344827985,18731969803319425707048526160946688629598037600738751037795347491343115736279,18895516979789867152215547520753345684534505439453679476362827176682322481937,8262582236770254192527817333585360308520228688394271238254948357217070957179,20124670759706967221386035867404978156598790198778905768160002435038055915086,10113512603622787997151768792636528607526786828914243169997168136386113705095,915897670578586705347124681284501818659263249883815809455861196950322359631,4168315355477923626825760085789663510629998017921421169386695143739645254818,13671994112691093230470350971338683534216964112891455368255908216954091232088,16886224211742114996348237388698196253930997227482938831037908300450123060344,3345133367703042017339663005080189359441174937067366586009093723866269451347,21528583089657067992968569213666076092311468898762774519530397406988724032331,15876161034145690426475777675897218203065468785806228994483284137836054650127,11419482087592638487692501143058453465931464811952523437138882421550619359191,4688593371456663565609532492788107789533208004746508669973851893459273665535,1201806670097794327812047975382630669548999745861216990648173033237996826404,4317641195125807665177432835324854194367911827066332519796115671103402289320,7278153623621857829571838333149240184056003767208572498343141321166882833584,5645725384264681461759518050072125001915558414870126637288820197391715227313,7876944044995178879031614460771670730631140542631541219377182212657483769883,1817650660807237476840344988506407016951597837358142384730920692904089879519,5477456541807551375261337022497006230056471139241891672239301675658950367705,8048211508931499636316723219242009338536169470228918968914286373316199192147,11051780522682663717015921863167554166331060525740053284975151807269395431450,13621239165564266256293257623306520070257833884735472109300551735647149439281,2212937635665982737914958126511628913962157442295931340442990688391698941226,15995366165560744217392074544914614299200056620022955679432568011246760194348,8186603384193770310414376291688089375415922904100458138117461272757184852177,19591014640167727612037871145675698427320771791339346286884839214170680861630,6878084246380728147562027775456286883121281123291292075367753371891198993189,2177050211387664317673794964274713735596159455191994291603735988793477650579,19810792753868883549077084303872022596455081266982011682803771833184330522738,7185072414158632003497987061744951789947697798790757573674746012007540132625,12527008463897431318214816404214269326472255194708737027196205809865368523993,6934500447964393691613594947677420114877610140521721836488732706620555412923,9978011727059765171039296158502240318826874847845043732001483806732259491882,21367223873262404675887107131444254925505042466133465670356500592380419754092,12370989539828127569760369184182336850673006315469364895069603190681475159813,4771734208255151020750966033073146490824522462970752283771863328392876062708,13551343845317029011162863399460125746613189002552375164523344552618567494698,8714428409330855425634336943651573814895603648572558273360471650145732556280,1770920281347553432035718101936298919487500537138994976517066719980578590089,3110069281490803391353365800012007306367848815614936878141004366920256162421,587101336788172489216190547515347729725635829809300681040400739386253763168,2745547964008447408503376832407161412155228373377123760513770235899201269964,15688219884606649780982718944113231917978875761031663915052600865004707442286,18825709614401251798160680403375685428394659815656068061728877494732182115807,7344398268236623675422623600003139537460576229211381042555723883054380022043,14666515770263042245313469306170077834894759906373286169967918153150186862642,6262353441640473491135912890626291592970997790093308164286742582769628052614,16647307543328963728423591228360400112670150511841037950484862728187168155597,11187123547390829437191210933751439038277808812390863028310714957203862953416,13431586020033401007013925927716006954532655767977222332198563123215088393612,4290575536028694423523505804878297212249395907285796384174966179335089734293,9525030500997851642842588010076299538258273880797610368114449809143832950303,8240494019366037169932737683997756281590058122972608854062263901069681117554,20634410655079842888667296641045124414486057143740858179591482529433244800210,2021226937398532158458585055746155459624344885692396128118875161667614679890,13363628208058779432402710458326211021009444288989875416757721068391318188214,14662421589311461388753832631349921077594459767269924751258576584313288868105,18478701421001679788418312436274921897007545359172305786472370930338255515306,779644354087716689348274274240595541489283221242495213448957276905050464536,16071592196394048404777963063722475415819376772419538934537115615105548954438,5095096770582161819227893847981354649325178848130636101047350986634230116037,1243295118881144894548654933667320243992122811397983231810580344448403973343,11884934205846782297010667633102865650294795721122133935339824653150509106639,7458968983251027899406062962031140351528726088472453510482489928822496580100,19572605475586099575374380909719328911692508931262625802140140705257944509766,14705309817743162695613012501115465410697971407093611587529338557210155341093,21814585268359946040619047839768523980706543116273413618895661291550785045639,18720305501197276565912107809183977276090965285535057360911917408689742145019,2134301697439195186325742384937390317718398738774895777564128344393744278579,16999326242022117650983709520661797031983791094852258286603416430772587131676,17897483181215416614794986081059087805317610826416633427262022077916365348849,19707797946013555426424189263942163273279448488563211841018471715309464788783,14555678829341308540860562709255991938855501651550888461483653488337939676588,17257409408848021559108687223120061819076248102607600439065783833668882002860,3083159817330696927114122348973911210253613266522114299928693807761894470034,5736074496230638296274343498461296106748247754169694274901381380232637436330,8207744709591183622611260068351833593643143431375276434360211505091128037806,16073710603427960567922233549405442518423088068367439127980767364626490766482,16125801016656798988611163501719363451449395969542389751490084517803061425074,16681204974924630782971582682795720527615927905982945319130944791490607417696,4072675318306311800303326010748274698511258916524447342766005819081244518392,4639558473350853876171991553789446979158416238030006798108198266387155784407,21611752344375994669307116989730257280581712049771305171467376136735830835317,14260028812889714557229612460335412900866192266288236233734870104907234066106,10936007367915129326030460455513265303372033050181411601450223671258879981020,2935032369592212871409648743766195391225915910970902425024269583756879977136,6634946569637959135045435256486295750841113686207268069245788404426148269439,11165382706437522214793349607928919108508947068233467479625942161240196013032,11449774151698349588558943383567398137746718964787475388291636245211033594857,13380655477865684658511486065341626238240224687038830357479380314844874141318,1556090800260299290436338214947407050034615159120446561828975680392439133850,15775229412830292677008903643751483031582598935755329498528261914480871637362,8325948986690458545596228454116700887740572176003019243020371356605705227449,1530006957398320461897940887072398082651602436763497487522949690691142033613,12096799031117418724262752691656478204625211872574986459351576236141568686903,9795222686269696766812901618791046177266354705263547407732303299461050133927,6370027108686216641431817942352503637286925173249339052075608610090399016749,16881432515653361341795686702127944966732199129855247935726797972793170639701,15351738821101585856536273249878864428731819041333447251288973888111661451683,14781212701946742438784746658712056984412254191444809612525158953148214912100,8091550554023025707193058566806958042583606199181127012808071174695106343115,20490495862854672187041438553984493686275844004543178578410057111061213880755,17273768908086623408127314492263145283983205996943328025810362733169697859553,19294495315219029609698328900049261980545541811825479502505060031138576089112,21659964890056395567978937545379633715401240283937936880886676845913261767053,17982445074266408124204445317785538167802060185652418682509052997213396963809,5844393214733022541634389892381973171593154485646342141850588851005973351855,18594392739980641449638044568401634303115397702236811642652121212412266849233,8182160648431978634742268417253967926524505311200391645921674911687111696608,11615953679796573972512524871850400010935503185676276201708450621253942540441,21529769273680682324960067458105490598252059794192747768640910191459525561125,20132110404365493609540330952369583903023878161175287878881490874880122068662,3315629685104800403477925330007368560121731964009894641262206418774875779654,20760167657218552641600617661638902204729190730886821404831449731791027856388,1071944235595105837840075721866496337725631499557288435858051131598364359180,4555500238184379140256400675916267083899817767012366155141824954325999920862,12550383095401941417929336389002623533497339330808819244458405679309028814897,16242206659396631583090203067480642438705316999988728264056381863300603790054,9739751068073084582742270086389269366580429568378316378361121134559060472609,16116599372526867528428771037225046273420084760475362036998100512782645537424,1491702476187736766465155485454380021314952117101776564498935819251125640550,15487639992401023512588653485898591177260483949656489128772103961337143916568,271607781293753174262565976623664759968566553298322241892863651425822643900,19977950168772761551778240748644447848771422612925087742515420134526515662483,1983058998587842721058540590280805932874010898105761473162166721490888500174,16107724245058262116195892590046753877901405295502298275519986777803068771465,15598969808860428995774387003121151871293432204528482957206560135604646381090,10303850219152079194793643117738367623863138704730923818757626414538626924093,18042134015692222011976424010811982750427479608456170876100049843530175591980,11698490610713203406365860893495577403678312400711273827266244766764136895745,15094763799279956189651458728729592947034549021140257103479085265792492443257,430096731859098382312819496464652103427606481282347941948051379574546904741,13960717870097098006695192936395126361902142536383841733120824678277505383661,4016844697779547662080765040347308557842613782172342555085194018348439875647,21390612758814913695838461279472506230937295081984131634720827665924667792338,15210153052017283712229693210655704356560541803226219169450332030313730637768,1405020343631287949667260064722407285384539868534544303707265307694982887517,923081188208761071201163943024810005498690637139303388717205039798310044759,11733793144318360060340673323677375331041661345320756410073354731608712531433] ) } - +// noir-fmt:ignore pub fn x5_14_config() -> PoseidonConfig<1092, 196> { config( 14, @@ -159,7 +157,7 @@ pub fn x5_14_config() -> PoseidonConfig<1092, 196> { [6418344278839121997761558068555633277874924383297235060428040203550148460392,8184778893153361724502825398680471446632259194563037088246379746010351701224,16103007220917684483813510789286348056519805127857943417823167430289370248882,1664056765289606259902279533842090994664529145577166075019619213360544081038,8189267971733078428327714274800548471520787418839983750528172780925243373109,20200362011107872066803394413626937903139046091127740529060907959370256880701,20173744990845412211550008592188171997284132875646553363208340317991304601908,6100976759548353184263451545777211359935324012594548692714862113681123862281,18589557631793259794347972680714314322014920073994928130094285735070065431315,774191617468212021433032703138192471679212014828288788368078839883023639562,20846157077077136618808870082870348881758556999252332666235423866411633465885,179369838162302125553370844232732082673907343764950159722856079645852949296,11918621764929568738238867861947162353407822663185301208219638335882724852664,11892816132266551039449220442540498829071526084314780371963132054098089857199,8372083054789567618141127024727893763407629214826505908070525202510871464376,16053969929668995455194287538126250235271805322133461918670713060143921845064,2376335354846722834191665301267088635977856417140218787958824096999457654386,8473768787638105406889032631028566536048259675966039382891971280359035809717,21341339254986951084972542739281440827262568377712659704364142966685544694259,17589661461505870684154172724042456165609752814624225501433267429299923279866,15569410058783548405562051853887675489795673100717406458705097884992918832740,17136771849960699223133648686528512780844440317239376836819594891344485260741,6846911134505306255484642794298233221271816466086606204236441947984501580805,6155197242495613968880604860842601809115711093712877692064968695514089066009,19128637117872288039529749360298403408432890998345732113683578733270478526860,13897081066927368111242039741190751095002227555149381235627883407347099966197,15568696369607733677931070776042128239961624292553408918258251667007167792827,4872469898347155985390501972210222637651779849978701189099596399070936407527,15828528305683107589096901962734302130704783164486093165237194118210566499447,10871050448146531061933214597828428556087596885695624079458134367495768692641,12715634392841723603484337993449575047813504874083783048519693646075495401499,12461797447292058643518857326956961860724022201671935652194772338021756072767,7893478478385050482361413970069327892342442172936930424757486435631374257835,13837601157198138379089270567935062044589875195895421018230835162664685103799,2902437942374588332095798046833013227638771976745087670719351344275677524328,15243444720000907684100993103252257759861356192301299607302082943722781818072,13807772002950549127635782715429970069282430778664455286304012934232212216437,3330690659470353458591000516836956263198779643900957759109026769401030561064,9121369550752168091283493588432030055987339875340008013960004670271046125246,6735940425069065421825600687003545396352913420833444906054707032006918504438,10202597085447755780667010145999569476263425973948073114389503196598866401333,5502428069812047253902185475715178213199056100606031376530298129227524479345,16644412428296109976288812904703533310452124314681543539244699753829624611041,1464363769917768267070780357984170193149530071521363359029977430003041058712,6585015011013232733155666746473112211355795487658434466373981554623709413097,4801399347914387190330170488585953929447785020623558112311440395749307391856,4112082032915966312546686950757741516554419896344654985273514512341960565972,21751105223203500709422523072334627517145925318798961657655356624798705469220,5444707496026644829581718915396971639630623832272466243662913303978147462160,5176202236175268355216491019205403607765794084697970681965844070084688885903,9306814982626014542830328909308268815941037317672165275254890008767120833129,18534005003473959617581160840952814838772020918476059581821124675494141739706,6315286473605163880327953940950267064586093973100720152185112301408202766527,1300066066523772310953083996541204931152213396378337235691979863222476540925,5067928636570884230858740896001720688497565237268231912398775294420405805509,2247332707031505717534336329466562637772796336116582429349574314234357562544,20247738241445291688801934041857783193064556313940428743035963593744371184825,18242310613256758222060583109097399506427347785291803988073944852458874238653,5027570650503078047623512055050961603632044524914664539379503584245086748701,5171024028215683374631525538977404535998938725569475537905229563957776539718,14944506678316768982115249709678873125670683211913646815567280613875065136559,12712774808537003317921729077604843728755135888798177519726799585299850232729,9704917525949617705578174784620125192847872142914494686332094535178307346294,17607293541936391773356775760016259535976314844718033784671775110177131099056,2540563558441922228123722363766317044293335648393481767268774186670778379682,675334497725789143197575210295522345522852999942555409554666160997486976627,19406946394055111935833464724872576724279555593094398662505995447037826031689,8556081865419484437161415079267380957278647215963771014478927152338629734722,10894080579102201350420363592007910695129177124810422954037601558695648517625,11291505870061211613677608925985023369330908318817731122393740145823745286941,8111345701049008354923033690718534410144029586027127161268652239467844080405,274549942856353845785392053622966579448945647367587770109876363273061435394,4517253471300397604524436610993375842851058850598308811197142784491633632173,752091920065645178354359484693693144698238715470242001046768604290011335506,11638201273705718037686872429810169270152128290712392950719778292205581621583,347106139963113855692776969661437659762102942024424980057845079543094637582,1650860247124995235720423411540903470864782697117302631679816892337391491913,8807792864573824348524944774842835780541376663413796103538010142332713129239,10125706840777901469452374931444076850109537279493387272544675392762656076263,5796395502360267028606772833560634747652475684290748049574810918512176530377,14560087906294745333868694378307270211143393355422503000240374502300675908843,5122547319055760404581514483238475236540700695938727023605315976547513018758,18167310745991466802649320845690518166441060923615799361858509593334862918793,10537655786889979411145772153623144374332103428122704649668615330715372491734,11129217221221505592409814225825346415830118409870141057639470413354985898470,18539905463366886572265160849053355497514707079972544561033138019460289616722,2634262960559341073751666364573144239402802922297326052528609874036724778513,1017221748705774701515487698826243372900579204850532621904642815268966836932,17514607882958712820434977243901228312981191640605665595923587928949930664369,11518730158165740016295488007249832672982237183652963149460237152135828893220,5440554707898671641433725775650782970626686693839748603607869391440450924607,9147974268228093219515993293158817299424235911620662153289209094112083583914,2705991777416683673465834362340927783587369064825521543877658261521166181909,17763220243034576321349123132976335282008457858814906290437636386824597776861,8436811158855395670172615633911662578819734431001698203244903207934616540973,4138616319619099661442960332848564292509386403496259477312793995345740346110,9291305887217504987438822522544685995385674372729325355668719643387679293273,14247130176192495492949419984506575479387281081069847984799112525576996955413,12065917784915207956255864287163607339403293417584561373088222422519855200010,19602746430676351974790620090862300757670420200072717172839368523905726952498,5634613092261261536249683912838127927279222476821160426063089671641025921660,5043354945289735676261322233679902891599697472766188245131942353534121350357,10504830582720502950783794220870443419986496189960249704278002268186024527222,14773755085011014730752864609252484899166588993023999143755304644745097411069,15143011250372146369484566133567610168314155803929775363148643430052742055690,9296771043817098875687880718048618354875037373605143322099708291917193542563,6699308928121904151061270394393246387724926649022798867634982215175491150673,8773562200655600334022608356584787571874596251190350038742467377669146368640,14351113364159322541281738731216101468935785403577478179790191188832046493458,4850132968055386067280912095292570467587181719320385350609813213372507065742,4016458733475657342057293585429845015911706822034026494547861057484844319736,894041043510502707790816962794342134661931379046385946357294445947889380857,6102901509904208647404960172211548023168535043766794923435155807898706549347,10993930772305308408754972248679389846078570700140268311987303413959565163567,11304109937008720250639855591630423562437629896442798574433639679310105935745,2101572929952921976477335632619843501489349436900225186251308254022656908969,13379549674365217138865497711163371499213584223441782700932894243483907931587,14594340674649653462364863346385318403203482098904810919462071947226757935441,10647634642733631076053157841634424396589261258510712678605967658799137793311,16930068860033006574251855928288208559689461032565007963103701897524112820278,17170753006961827437085793102001841977757115057767702296590976802337127094191,1342928489123424058754093123906133488378103161461686346784407392402405815911,355084123756415397117817901422581736826549282147584490336790542339114994995,1823092998982212793902589678970070284970565355035702454684653415443223765731,8048898551230697881244098474076915607341905762324328681953519068200924158237,20856146965109368880184165603695312280799455722407374791306423939515039038196,3432336669019104452964292940253917813738689308738628946720655243686051453920,7245438991832359030357502486353272155562833935372540938834365796871211337300,3373527222057951116939099747150655397463396105930227294848620975346900604136,12572707687046208161448564696692430877391470466184921096766838283998472321208,12384937031493229201662431219560608524659573369707802134091983819133706078063,20462122407152292556380602425948480298757076111535435284512383591498863731385,6622785585380387259638611864923325325049433660844748518649420260935388464763,15605049021670133989806310757216368923392900885847286376362426319164543334301,14810486805240500676478878806350063220126440203863567013665308737654824897393,15365995118119349988306703047684161019037838200991362610041019844939301169864,6214064907470154787016516294417895267553707205697453843662274325114029144555,21692877109550228305351038417910800050989918141485758185602644390604582268196,7958034635501608074408556743462969750498597833797740641656952760348926890967,3158783210920420629823137370399691077131051120551658723733852791768119909942,18819671751132769006885776104610704012597692175792895108910904654781356258396,4790325452934584098003515312147561388538271922609950099121379835100064976005,20767398895337785696143628393192433940154143348446221959395714109953426025406,21100955173515864394919601160078443835098475047602620649008862351655083694950,8821416529246363042674973639302381036248538012124317741611227124444469290957,11914021995756592924439683950035443074155751236836696847913625157420325157778,12885168008148287764892654933214399102034605991806678959486007885552095995306,4425275709248360981167156075250953689813562430283330510520431316578062369047,18741351324856319300007832572892392651059227679283122747711533395263296376480,684786537545646459534722549414114801405982388707719975074226812265976115381,13474309605538568830758681457803561340228343878559353019653388997246494295147,3099805728977762711509412402176547022850118685798164610570416531465753528610,5600770441217208920894248156825173605916487562076226390167638135518086562704,20319458329312269677818220957673767032133429098791178878849925747919552800984,14900034607445803864669093098274289660238359682591825230729114910199159461945,8290880045417869099036293157381173265982569186829040493570954991061128070937,8462436298566072387124764625279911648753174589013128335782578822029407672023,21859976115370951722195695482147975930683297060804395404861331672395447064815,14955925753372802734448760267566451635799685206346879293698701403481312089613,4065981601972608953318716423564125873552717375448854762316325928896715511302,16523330234791629267329601436136683980570251446067965291433875700044665982992,4829516041540259457263557932051130738530942234255982016822863700315113125159,2798840870433591285859944008589519120500790120158397573735407833376475300691,13606709616318037902590277227595176470474803011913892797662975188860939656033,18594031070203820621139505381680984797252179318940256457018473319622484612714,301595919185954926511829797241004376609778538475902874115890821742528188064,13821866899865257052128875138353714301663063129600329885914206051645789155403,17834817495579398016717972195069792832976969906967797534885124084571666971093,4636638693344568063056245513561087534038777759740617868439601311740450428791,19461094558355290408553329139619939302588770167904059927666632659470708174566,21540578068459849172758651661944081878533817564350404794583097681041474789761,14656354515483866868842463518575118611017107704362419507056354562832024225447,343560765432454876700403976911389497782754407830340801930660700142029455668,11849359483183996767521484728570765305101196328390648813094255236259946055317,19924264286421201319107239943837407504286665549555105416180422159851955997381,17166797941774367858319514708612731492719223682833034509589752159955652705269,214236542688014950402766598120744795751883810511956979430464011049570919618,19396313302865541199110558245182888004201133198902041449761385765703954241755,12933296125492490585010683271492258153065303286717891025958189212827073719981,17844270568289843619005627429285364222068221173113885235581641198349558307966,10272636157097124940050637562161546985351212351240072791897429490752295849402,5635761628763643716582314870927054669889487541831762994265390120663404976213,1986614715532243876888195532508093896345436225715262830320266933005235142097,1081989267417492031457620947123272024310181199896785177670601875483149154563,4271010751492446271792663900865386872716262596127435260947966701647960153580,9432817034096362918055925335622289410018247097406473925807967486887418646785,8420506466048434939907866067225441403679080978454172178355845135440301602325,7476612931957868870953074766426582066042735713430654491467754928300129405389,7004218213610744415525118134908394594325377744218316765510260162880307132371,7571768879245095309014348656876595924444469219593531275918644914891061799835,4094897072683886403820666572344492999293005026807228977160362626188210714131,16648450944544843693538650431603587114533063135176857105384828552675810607958,12887042974366913908128212909540148628955501312789089034196573637089727885967,7707639242815954375609957852547086529799646052590213478924819089112009057667,8823950326593148986044014943552213420762296615584865468926316064433145020153,3784973153813546220636380916429273484146041804997715013288159720673291711004] ) } - +// noir-fmt:ignore pub fn x5_15_config() -> PoseidonConfig<1020, 225> { config( 15, @@ -170,7 +168,7 @@ pub fn x5_15_config() -> PoseidonConfig<1020, 225> { [1954546571818731885139861264947334230782822161673023234242993080695489129982,11606713580838194823093847718802359011098299538034455148401855555744041817997,21778217939341959600865514937973379081571132553754734185669152755967486158807,11867012199835162777599593543744285374463489953452947402200134749407575327780,20668200962959535110219664454556867828577715202494491079283962871771719016091,6796938349934826085352626361055311106987991567096993611616805270698773290279,15108030096316731537404525399718062561401239877230837132486990179727134215091,18618583058942935584876943765894457772128324732451762769633954204661267055617,7446958258820445329937058505234183740111199633995331064868799738609505041620,11019126795578266009911151080068316016022306110283709712693862589215772062237,17644961526468872013219663511656737898249108220341985100127433673616476030536,4959721361611533340499147366149623398780635086479466110353420780375692399477,18273613083607267201259595982191169247452947601227995394305633285537292365096,17816466502776842735116945485728134149282831962573761460376746436502757322332,7013781340485780306773324480395548266877763825891494701746512901494705653158,7793291197073594006386853421213450159077336220644997691715731402410704643042,3426005025972529257284910903433598760993095858232060658495826014698591260944,18530832095369567225742997294004908129637537286260217494648426754251364141983,16489909564793485793960504581924093693287639849995832883358263301067754354184,15126602449686250365534081130480301443166072957206187316416707568927051456663,13994979972845996867477162556668014834350043400046615820474382058282820089898,17892071176071030024436108339592708638442586845389229765280031864359446154887,7094246519433675669226318744483462997736245331529142696208838903133391196837,19739600791550679646703071148379836779124671330492407813260108679076122705926,16577895124793812992345966235533166228538388644746952252700985971901794098608,2190990407832635064016354900528055762572032133913345251583721394536626731922,10838969594099257399038118686024400001327577210604256394537002295046250661365,13742554186879139633322968994905507641568437399912823098239782636831322642395,10281113667801091149613944447670705624056560574926411753502305328318932013688,7661208680673970050246952651218127022141152720979640414729369551173790735959,14369836811580283035547554195559038793886958236571577920508487931208924192768,20603628573396476191496332378884772502350107937108583985752646932901407759112,19296916296835469264085474516279583782033370007674993417080564950885860980156,10983867448590555143664432588641225682254935452824608025544914671100236945380,5670198946055747149234813634846142209283829958947146164536023332201358566553,4904816432035963931263837796941455228547544800276020247096183162764093041386,928528370618860212551901809222389226336726628142306562102221490519648216649,8727385187994811157471310113729025912812882704232858255495018737569420129281,19909768217191699902186248006262494556099457367519802119733085801884256380544,10635786582281955931244778086998962127059196955758207056871772748744817883737,7140512340052162441606422433836236465795273624186668144911701254961330905493,16598081311443832517669265039250197623929992506944409626575335140315057620768,2339664320384903939910962081546057089170206846484766939921698239663651706239,425509623704802982425483674266195224640999670982140442030650575449074971057,7922384239142329258156226873732902413897900318612725000714450267548570680404,8178140403014386057685967488315772252114289881535707170540858306748328725322,12689293740944871195190670877158259851710828253354810002997981152414697198513,13670630626216376948528966598720909229691593992164633421606526176324419533442,20189490101967313329851160663874367593390331759675962821030507426149184002493,18209972608416650990264895614325602746017028678399567737887116829945804399280,20353660437114078502000672122042327871511027701339587880609263231648053792209,367135858451744056025051491593060073950844607000402056456474235270560576836,13355850760886700974133527239382497141869096511168824351814359808886023658462,7206193356029734986150290058613471641978817208643432940709861990432648635433,7885684183122679587266799938650213096329650494585142531776846669540995068168,19085115218990181267812208821832153255121894513890241319104580206329327134131,16305675470941528258170184941405206862153955794946952667798249341324791515500,15443626257895746936982356461453477742473783071787883800166168668037169561924,101832047855527584987088220264952346019960111874606050409415217845556024488,10576438072746903138917852030571732352003417543540340689583487864994727144138,5996861730264922256270512962050361669936822432104376503237345294243995854540,14877973900502178557219336745361213333854789301797456671201806787562463919326,14807962843542542498914061591358875654692300327506360640837865566998761281322,3133673265931719924452668737189159279894652423873815799856403146721022028744,2314426743898183021393131908284299082806555710249089245305873178073379019830,15353836455896084897563929713128028858175788390437902641700134508986437653318,5529981971838869469294842442312128910934708838384001405870007618574232226406,21863108219378799978996648633069571801923287451100447450849597846874069699478,6773528450923012634292634195479655092490402578779439394568805920957004744133,12150245180431051120309675366247495517352377611113958096501103925281912163211,11142442323884902255425165263749428309435092933107089893872191462936441527962,7030165611221942623542847326918501014233687676615371955108018311000952911338,14168907664945894221515023422776939138433274836712427741726190314020662482321,17973846874050037633502661848899122581090847479984048542694367819419188584711,6612448947387099268202244798863603173886774177350810153571814261011002084641,20765273984039816245168454930434370234220284726385931011063596091927027011108,12525833775624943075128880966259784896817535866921245846013552547690890352574,18082141488353658073882233625595393585704703572017716887747923068948432979709,12080205172928213829055364897249628749790838461826174687455161036760925324146,10426041417079669712788047630796875947997831039494087898318197116078426849054,18333367395418670733687742418586004501344157225223371831515062599898496336393,10666844346085567030848134043176991777319226647942683934134661343455999941894,20287202184725945519955164847740850432153598475156573942705745729215691030796,17942851314410450183054332374663618442963349517519641485232231950262698445043,17672277011389568686180232934337352157780343745417630591806280730146798908966,17416106918062278234521335281965623696779795380548750331807903068461573518054,15034390385078628923681181367678507353936042776187855843799428131823528700439,15016371809918204032764565101078018512566551812562861502095795302834732872947,8176229788878503959342848267153225222150151339952249831413185552792554528595,14202549166569309182319866775579092322766621157492056208423752359103429675445,6628758202046565882882491271332141326521031973243028104017889062740759748530,20267845326067450413789379016153439637066264448919236391605812427944953078755,2438946774028723892708023594952994993532105735189593503088246493623252811398,5327774123437518227973303235331602588839413198088244869937007412210139714640,16416517260868931624699960600760845305546648577328049221217547593071007584547,21691457642736313179352706050711464825492028639914839210493298427277168769684,17369736170805089474636304643282290719726533149056710536120639858255935606857,19460761623902421883797374762298555710671254062576987287084819867262496770119,19770570144034267396127078712986043464609355920552337517533085194608634179666,20904722049832148410244764905538463264520496768863784169293376826852051688706,6086931305514615639236334006857789100145606465861722355610937306339550862,5885641636117295888159072068000551173102681944020015073964039109891861226751,2197549059218467728366947205357858398035068309306368786367182781869500529089,18571065033075196607590252486530861399537536653506142751073877421696969841444,8754088881400442345643534933850221698544089985357770542959718766123813634810,13673463077059539437815915980077307152850526782227553623693374041649724049605,827897346385894242944663854685785871137033256170575635435612086616249561082,3256718342276213157296321691616951565542440296040693556287210483669841487973,18851058760089844863118102177423730353882359970674430675542303019981882750705,21009037983427297652279654800570889577926790955118786173230430780907667982896,19370673591737489444054265538393258291592098201577177240415593550679982651270,9712794467513451079466753095103777002587307366322848872714822737566534970868,2452976395290300719873209484043914405675637974162415011707015440506646332236,13967023770779438454858978860214792451127045212657472381516830319420403024355,18974770134907327058718913691556562240688992993972407935785848010954975834526,18005343276101020422248769804338953747590444642920980587346957205121649916277,5364199751574723768938730543610903684616886041855996090009230701935892264768,16915141432748433783158989990154900013143930156431056052460984382677436665679,9810457740455050658326943855759399108575402539560713791636000764640385927272,15711844859260073612371012979328688796642677336582424352155989490394966892994,2523486975208775388230636032695576725855997180931786065064150527465407276212,12939257203114853364537111886847673104871159136302860798643783368852456402126,18563343508729873190283517746040347988882591986176993103658794343898711153424,9139767925154848725661711816797304370425590863714334419686930754659416933343,15630326979358561783620795846763021145439701823262337294600523076394775855291,16709031855693747432049197217266634836607267701623669179857743766059308291076,11081746589259753124653594380015131650915622160245647873370327872758282529429,10263829532434991046602117509549967441368717347217075372460446855507340910410,923380097607272775621985864770232207712801803675470117823528453022294342573,7060362012752050086965449046391069479205357779670943566418766734533864254186,4238871118210220589598309748597547342336859622832314796736348778105851398663,17520061366255155846404852213753339526277619564174678951991892080505590972066,10513625869281904114245087023227471195681135249236672625281292889978751612829,8435396899666453466602702234562279601174578748121165208762270334814881381944,8088078454252433245088686773582075061475116946251984942060002979516553775360,2719987334353537600366656327639544587227927648913835976421439609622334518069,1024019320641379568207362641296952564452568447918936948059464814762366331275,13889412498086825909291896540147715217051215696137185869382874120375855480535,16901630530096437974516169843541514517956751790577876352535912471650304576719,10977539500432168331426791033212854950231005312634634920276038346698892818211,3226460659346175003896135924099734866664778523605375090881255390738403665617,9695421696482260394078309163365413177132015345359377667904366174083251929056,11269053203885423427900382169641426379373759430258387104391167619152696936070,5178750298399029508026924620615685679610392082191932524033828050686124044833,12845878982355860044505488053971997443594073436267126888909625977253125523142,7530981388399357124357431126610695946065175975095472306777796169245607001944,14058213441446348117716607452759269076404820355922112411973297063005877380478,825603012201903073682942337154197674883919500964784037574585377691889312422,10236993586198323123785803013039327931978354285685191819502917433935835639701,9438970111160688934509448828104684236155844660381402912193342504181825365420,11536612122721678319037657954738688943272460041908847457419482683492719528721,4048128893355211133472225346691072554088570917135573376272009214966234274059,17007960555125781716346334106074020327440615260084049810189285619997415816473,2935744638594795881476272405224480626548498961519135317809162650064622710267,6384493312721061401062408865799313573644091862395937725107886310975229942194,10507848115923740198082149097677194763453026968238422206438345199258995348681,18755782391252715265425541321566381935042481942506333926799033963914433188574,1622030934879728521636669415221999170954870898240640125007128754133416241951,19178897048453000979590659690957596324038669245300140765620928017217201486492,7668471074291870526245483897884601792626426080083647233981123797699500787980,17022938204221917796509718984925895198444138607270396412440297468084153383727,10938747411001421463106680010228586254730710894241856448408311145676137003709,1892143994611253681927160695719312882099525827316460372080933907151205825399,2626413664304179483436880400231214693597358131317388676910101259191110264005,10976814250018194880310517382191223839713741375476951941358522267556104616194,3267603976137604608815546917515683877598008503122930381370588099122094818035,18223585230504941070194267966378685287221743128395324427323638965512681791787,4055897021092860484143383650117982675609656498724344612791022670810747280835,18652001434191198724037217430343155151673545332667032591923572773249520166995,1179210983342192637294098069949454912191992256395734070923896011947222260627,4403412539347069757548448548289536146089860634393235869990028179479631393017,18208249577016536190404023195559477353692681610041814639755282640930299265764,5253459060178003600605009461295776576191151024266914967218417406063967602725,2110599375707753504956307604156004992055199034205281989577749327575131764193,16838175205667561737978735977781331049290662487428014461885404122880770700959,6473428079010461623647807107937762759737150259352991014918746820779470984847,8337370031139132243770630686523670334344396638842630818638144451253681713442,5852133535345551978469538570221409138541345120679970583582105205614182914641,7693908046708935218171096369565374697059710647347392990894755587360287791527,6754690467826351700887172852005234976131445583530162984365436173814346372178,4362899351088205531982963806583486557201252717995038448293398829823910923472,11518397041006514564038599401506526387562942749501723393674197214904315107893,20606341697536003623317613291213380804130123512962185582210369767659416485838,16897394754877405156789353426985842311670174197348619627467370676352261158652,11049995264887964858828368499123384474282091734658191426291499678845498016770,18903841016151023909305743424460730902070062204415137089939274033985227379247,14501632343069777665672565757138143573066425682965558756989443143462299059377,11936194426294671569251421865691095594275214629276606276242590758676139955663,8684782852463301178275527204056308121145836348455196441596832143888384190591,13840275015334112173632265864573045139112521216777064496416258170300441524371,8976112149735004651499648151657522459186187854485087924493254571044062238478,2557541446593153253492913007582627823644717754910327615163106890559828872362,17214289010670114093415697072867115184169717632618753776383803280726887828982,9277044732799923560347274951803854995664839245943597020159605756847120319168,8665104485244718969383349524127237156930430459852710098382428996861193438718,2966017993337369327831105148290320997881600321998609267125699685969931023637,20703140915572601301330743722461592884427012015286406537986295678495182028439,12415056396133226456247587270673507158810318408454307171336801889305959276558,14096884501745579659192341381525893498221351476730589189812251926483574089240,11380799045102603249740262962085086862746282867943901718244205293076495402152,11397463999860523006350413477163951990037313029979805067679629038011006323456,3989254560279764593104713297200294887781503399600736805260233256187721580128,12670526207690537332382598355517632246478654103765566238996179271593311461311,3183711571356392622250181639411196710255078687860623143026450897732409025180,5610846600257417510307213084599977483933519996901968341741797171439650039141,3280606490416179974005759319341626407586508917823138878220428529454629673364,1201982324417186063031536229293952219287079604094432487446300310977814955299,5320694228353869326806779260357179675523748849691821041819016623807887766991,1117900147109997141002482710095589298468009897854242933473562031850755107739,3423874914270570048663861326353594445564054387733265522218157141041404209456,15544724812507000325032356684034497915485954044805225704411638706645864153677,5773122431233952373926318394008190376724448498619094117536291976195311877322,6101823265492636176451963193766486622777300610881276372298697176417958756135,15795396300010870823125638802470599845845464744307444307899702370966825169519,1323789030194931509684647858838729962688902410898850967128356366290242773839,5751046064881673173633677922158261597917572500845688735304279201507132509829,1621252171583823353515150633750260236914561977816101468910968069661001399932,12193773521417435700759146251386454694422997324768376620870510312596267301181,6582582178277044206368630428791785430498389945338461249283089656681050213384,14215781677876725356925186332463972498447213260700771466349787162195918816425,5782842445406193701766362226063474843566378485612132749823957396025995938674,5452153017648783662501027666999013472879951066118424395570738985848450672673,54899108049022846277426184613878330780751769989719315816516065174629128493,8847320923102214377720246218239804718366581789144394009065472718259977212062,7818599458828105010909362503034203380908251186730393621503231459166558068065] ) } - +// noir-fmt:ignore pub fn x5_16_config() -> PoseidonConfig<1152, 256> { config( 16, @@ -181,7 +179,7 @@ pub fn x5_16_config() -> PoseidonConfig<1152, 256> { [19647061463337916460942375553072101475191437675089764130648797272059706835097,2987900412319695329324667493933426290750629320482434345012869808788189293747,14313117549814523542459271158255968194819696107203500245376504355915249564569,635066671179149779961724809079155342626591882143599249747638714005480456001,14160366375280976850992425663667859199067402849136919009370279834492741756927,6973916440684075662378599037972982797550158082488606172483341283171694141353,407790128607292443078618781455551950270304278197678311107891073846005921099,1875793830194257638983834574124736838833728874912304344706772047211830871895,101555677977911034029979807139724697918613026657646487138174278033141465909,13298961474358064737775518932222238976786587146906206646633234612439936576772,6675018665213382228528485041578965344759847379196981998842754547093440230230,5085649234634970209690321129917296688853246686378177913913323311616242468355,10058141944442728296289308385948277117189357184119821310668675797744136293133,20711981720256091912789603700019290285604375596717389895155646132584571552203,20115432152302860531854002084546199214679745925822431241410388037137709465378,19426738311039094155622173280735935805207149231732138766959497422037163547769,7740589787985988848427674257205602851899971532434369842038308874897481875095,11072265639503386933704945672016505140436978537584329931993329650203494086219,6167282302581750408390138662907316184354012779517813053982109604767767995057,14593714320140781629003483490890381863557111469157054599498274206519671343499,17959188687624917851017921366866983692604241271917787434145985166811823698158,10852786592684215415216400376119268936907433212885674472022333115957039052793,15899441678259173360040901233792251513972059637300348276334545233380063193689,3640175378514868793712597306483649195648235320181954901691448087453970656158,19498930515578230344335483600141550927765501643188753803487668144320311818295,4153883544158745158953668931089517690854504894896391299015592025101035411270,19024468701496237603291237797335586206588375930028220273546773163298357041151,7469727364011292433851252680653746774195189525727608179319902706399363717756,2372143841469285674441303263292066347817168610069150223765733476276718069613,2516526351266496289030890575774410993157441063594813081137075222758309555822,20958751338961200084885567700868871946051162714262967700193597995642229058459,9198209373895042225521605474867845062450002141670817279014351290187429107128,543785608759854122795367682791595958842618445464321379849398930724000250504,10214529630060513503750965897811894289300014475522844219670830726679857175601,11576753654045835303746511804171201194442330501175712221979130082457712862265,6214928611453392028562534794962748192402530967301618657847917468183855957477,6248903930557664471829331572570457764958370320737816568669654972084840708363,3521559114442643806761280511561190556015853803605505266866910604261521098953,14207749404758918058098136067805881181486166837455095244160881284733449919110,9959485107346230833915817969343930335833003289106263613217998567268111531500,17002458248120505483758089120825692383088865286608827557586088545674133219848,9310286746554253001882911152696415122865977191166769045081952245779941262056,833245639626789987010046903814146615257437312131003591772116076699143834195,8257332153195419962290907487481324519003765405123021230564312430389478396079,15127724347963527967475442670935452967842333763417615675896327776913208692165,15791631600664089304301903868070551535052107017766205491164731100213785544191,3248589614829341629004884091016822219853816257771914825780122055933452087513,17215199223989028745431952733663229031216291778213241728328297124270973463797,3857684745108028860654397149812523817069881299315264066597992653650257401551,4707785116452305555993924679316564589154347100943642537399862884483438576343,19430682328356065477111453488344441289467658065205729792227680437122893422861,8005988640968242998051528980068908390083328633663970547195021707967989536508,1972474227742829959658839187518313253567182690341134307491795498427960575880,6504813065413498635983080741406156525863657160083764580567056987831449046042,5823311218891803691266204716746992257279538141703406410574718561307174926795,9892303067707797586148875186586047934481214044907972144908705198351662761557,20467423831764780786043971286447965746242601887189594828393353559483921550575,5337137105639218811346004301122986797373254603744281473362301032791465429184,2653918865001450389595199059314513619487087198676481143857196098234024054997,20026090683375374670866007502511215153733777854247692013299401340222837331064,16088029123818655662676092939046004587731443682967462740467056646463545748825,9880178757459464201483861677712096813007025248923714154921858424834034903165,1227858189983101698453184059397045112686910656353893224019532173573557918655,6965709790321124552058584230424761849742693958580766537537673695015364525547,5275724511243540616354496187333612866929959836267482390875038898914899476257,697708336385781014957549769788950342363636191998726381071876409126144042559,7274584324261857876506709208086520820725839679509101845928052585127373751594,1101072498472320542658663987709974387416478403320298285132888772486638626384,17063249509595154712877503960715103016753273139274556931196815282616091591377,20468232842910222775240425801279694589286852891430236774476461428028768660386,10839957331597622631657614186340514237771754591887181416690281526344756522470,1833441125433983427564061829081424752522350755265858559398836992598910515884,18955730579934733484387457001397648556717991843841809299503396866826874046919,8193171082824386660318148864436464606096456472585333569988889002087311061541,6800731829409783994258949782115883803874917294598056504156236185152975271613,4551221506539437319374784319536342657448457365716669137274070321896962382201,3888700085587860510427705376785182344099574784427861867496328978292244934753,8086322087822351497126170321910559010882234382816099821864406027930561491554,13275797274085199955841117698566970822958536692349164078040808025934114965830,11798506987450083560046523556681776539473600393190500985018551824337777992733,2379081429050928317988088394722736405728459402480510127050576787799908525809,2158947553437093664557813698796314628878318098916390925037304154608297340081,11904049624504424229914369023060185670359894203980447724969113153014864088654,21129595246904679929428089867320350013514202309069019924095527072919847726344,10893562472341509760161513998095439702562664638408764329166649578524495942254,14633782125268548143403043594739012390811363821154748677494041549086652426818,18155420130909256009162482779733306385315875131491307204196352931575522168643,12073522950076264054413053294532869251854443128423131910399999522064467473027,1433592116103756425832298952472313408701354429203600638317025112329710147915,2210939565463298865317782595691956567659826882335372151952428383797077275627,17035360868359161456401993589512915729326589319922635525934508061308509305732,21403800287219776827894322644981677663016408317172756418765747341745060868637,13463317002652268594305080031749651114168039804631789430404782211764311412845,19738499492349409431828527491123847227085394983018723982858408988105307624104,2012548380220619299131832783872761872147153098580334235039922730491934764706,3325274441705326523449614352431988173829782789776117744919906973769657338996,5261611144921901341966147913919865209616390993972727644394713260572315512744,18987697050242894331980397947115962487019662790026980590641254086717180862945,8658141027857622941054124779019043605220504649377920644749538450450805414621,11298428708044619749095290390778425959792777464903586113463716315584533582828,6730200291399992595132121834599191803078178940321882359439272645986988925939,16058286461189478903573915480209402516073069688039571574175048313793344696582,9740895146643188739739241045620497326490653096157416163918867637699590812365,17328668678982472669285290349933801381460489699965770954259262923597437466085,21089229510079204828717685354260991995629733636903215847138008238449607565274,20640971546156771190021485453412235742638585574517108137718546522103899393969,14758279983387100491873648446401986574422791750180622274744397880182747812100,1331898546985028774480334813742156878861378216830516346949642945416964272379,6432287430987511826080726363315893796139259314225964668680871966245781390173,16771287021606049252082476128446106722127174299597407353702759915141825150750,8558856604643032676967156921137773032066151674912302830855999926475047747086,3441849687388033123111488396776112259878496892302987380166582753348946609870,20817116194964519717309108464421257788806753886196720998666047916921548668924,19363239836951813038374327912605477961457473367759250309818663552575087804364,8719722538679135055399244869855972116946451760806505569767286592823561841553,18664054074328463099250618543796241821469021451703648566147509976488389212302,14668897608285076749626150823646322752663015099871458303607991619920343960884,8824985320268620533295858061606775496359110158594681923758227994736311199135,10765520116421824752776648993191019870707037690612646148788741126433863060128,20754227554163810768271776561488490692278680037121708279136293739447289576147,8507072847563043340105426835824153184629689984787563844408253684598778757305,6766982373679017786884251724806484438649942596522690604198707242527640673411,3038766798814116247860373387571799940341461487105503437312437210868806237693,16132175023628563044043762398003871532172614031006064729051923614189729264142,15583173149116838843387513514855791665649616393679968646432984027900294981739,19200443718712964237956082975258333421930476944060656325774330146577168149713,14490821043935432280588585568226041328772039440696419883978899443298638245193,1261830229525183456874822855513761625054204680497477037321364189175040481068,6528746667003363057717101918351735481714469206031070610241614606650021871543,16147698956945808666133328464174436996026072559773234518262594815923002983587,9020387669972688980419006674825908656426016085797207362353154226605692909040,6727316761823910734900206867002954254557029243225097482815337322560175181198,10066421681146255853671223544720366622786875122426340101570461526567311479729,18114193263469715956238812322551819970497722041025850638963351240642707536449,13327552382593937204593701292574430198134175441510741573417228229955049364251,2372604211171385703747757710474646305749482500024237878826421281702483230858,13257727745849193909326785093877285673934675536283265665870765530981203548766,6028193081122651452411463574343231811776375151328081689399842891316362242212,8508301356193721985012355411615100178521599009635936162890863637274261948848,19464559199695905284994131173285166577427724356611906328878634139911049316349,11574946347736941315258330071986157639717219704847732435648573723449097294965,14316018291870434740761571976364226850140038868497601980741769481529398163257,10918196690875147279977362872452345319770767457845834002916792583407449275430,8977373069224380198540140180493576791843577554452269707469880849832228035023,748498829648879147053737200607377785638767247375633990031472844537260809404,7873158704115081877804196477528352958470140833786962209738121862287852609943,8630532424574483719830065132415752445222218233997041715460638881404278125797,8690582614704926771670051368117061261335922283383440650770249469863376973533,11095347717221488007795836937657301037546485308926406743891578760518489637433,14135401679286508502504277387212121656373093920904597158275723422439876100612,14738090907871182095556666808390406734966899260337679732930591106508814238308,69949271807030541733792162811562320986072778465870031251424993196153906266,1261108319753649612663311207745706802298135850234573661502446278242936235395,16817683438765699400477322528948826720336276287491287100775393652707943792575,18841362612982270174762542916999427955157117780377439797570032391179795654286,21870116979686159000730008975387147484906370787640570497473602061164852395071,2765949947644452455039725847864010340741814376903283748968022076584286340602,17243705140322781483942034937718263695017457618400778609034996357553437986248,13677914966377093417490296499705767815775553283213762175449591670735007344873,13297897273167025228957171745153893110275891317806768533464851402665750442708,13343269561671098171091946421541340634645677702710756455899883309946421878045,14317554923995329326292532110843156058636017277221221405605647959782965284991,2449835610256525707119222686954432076774548565002604197859382557987062142872,17311927259294224200654531686487034697399582221230204382526629700762752029323,15886029754147081563564215095016637219622964863827251334461319377673888336370,12975391569205596000382467418571211360327385366404855968892273321920864753986,5192224731376769981697271181929966876988577937843948018413420047649317448463,10676192139479409715075805869252336543157972214291179434959380291895052573000,11177450837775344504988539319102121281143354970746599512770721409890402968920,4593200667847399069176143966880767249193687931869738276411303724780636851859,11038090380551563944847929106606106685586830480239388947878234434263502089528,3953526418885419728011595573117200571065709475826662733812952860173033412620,11423581837569206292763368836201420979900393158634684009052097987935130296343,20821758092880168608657749212670937227806187953778513378055795779476865339010,18497750301637542715216545677959957759969933594321504330433834545748130561538,12908315310864070359072899712184126229744818024807969170422172983759986468742,18124554128224712379655197019948407579501104121202515283344405665022477997811,20982975342803604005070898815103511622812678185245827078739170834137855132820,269825514811016046965635325890713556615518696022373524499024558861784638050,3147172016143608266119085281262979524079358702373693860744797997889998689295,14386832245166477008833710911810567249931220515383598373556096298357174022469,4556487278328022691163443795787718624849832853076824895328263286768388362379,12261472135716169178595791281788338424856082203277018628926152780653238868197,3899423277681311798156637809536718065846612626667684730473026778811334914007,19506309861341587023369919042973949592579256277585657370274971571040135953685,1364959409282923580897524375843789492029158451437094417717346158650761726050,16825446178437335546349323854223244861262257417842514939476542139147191650927,8507209116997169365742612629060440573797814488088100151461758543065101868641,15267004752470933248572062004321128218304784520473623806984809921883550707694,676031704648473427598859615894926588607941948575683685792835248653139785855,5619669402121492986528563034254744932241765329516105050276049374453441613893,14704798323824402102639327448519792804756861685698966598005087155126928897024,16320067378138810368504584396122999292945236808095532790918287639367557973453,8146733655224190459272793912328710535983522769849572460217349885461291275505,1038180418056776651442944028459510265058633383281360520702142043667403503844,19104250152149288692194160087229108962380481983770051876357439473931889382526,7003760916474780870091321276888809099928758016210575511830123521067523691017,10460150809039904156668983747345347198316841366879181914914418086579576664491,15677112907432790716289265075133862681087874169637399306212310599581157175963,15326287388823547786897864243344800490244989953594543352512215080754525987008,3009920542142872962638960374061083879344081833005888204328736035225746795718,1804978488347291728619316877980070589260679228487014904460247370976082660690,1328483773995482788116589592947585572244503408960547493086897090179230375909,5730439196427856076422854580234519707227885379777920110477133774925338997125,12981431367443547352573507131765244291012615436617972351790438163822109185806,4832711978673748239567077367987729540684018769513731999388791063624971084279,19167638139894327951096186708600927728591679782746822664161578344690189946483,9333793061773227893961520586484148770892826436173136355616167263506645189532,21458443518750111068075382716496819469049134888053123021475459386077573760694,10205061553685164402371459751106832224694007401400200656551443744478399832956,9830442925198991171494436686328858756494894392913894165312258843542937207416,13609869649628867442619044498926584416410429910199812031508542862339177409898,842857359216662427573900948838829890161571314532391785590044951074431433210,6704851129269714864143856350805682503777715960622547687053167421313207852468,20114446898395957281817578351485444375476540075666338352767091837280210668931,6130491715603374999851365684496448519606340852139128448851532580625582546602,15813600594451539718733724931622603275717510629305297903420212550967482486778,21327142130781371825633810115678136219928778056926678460292750153897861437357,4961568602907543961625596532526706517274095072299784820035412496941108876522,9960714813540172203971946479714057278358565379915043327324100653488017320531,19766028424299726292403979387148081559608033800073407130824876437622345769610,19128679427621049663909949398415698465159247423858348746959133844645715231748,5570166864868188450021144960131468276106515498742461735810689530781856406802,11574972995586621052272541749980259251569951388173301707052886832340902170154,20877646438494519923058752260065237612204466401313282232221152388173388627982,5249150519585813956946898091205522450918428396100844955321690157312140444303,2017741632554727420098342601911590665808744692822556685407780092354922864904,1195760854074467363227832424961613965990883686618742557387108941759791735821,11466582138640916980683611003811079018804741425452823176665968956853901549307,10985903304141344987201754580174851046824447026961915755527591886735857840658,2130569969210610976943124127703960718576010294778156297713757734434872381369,10738808247531379378397673739325665568136689079862172683794674460448121540040,20614646033198180892625991863201166456931543657809805208583071176938057085966,565997125213498936861304726982380864240733960104507001725677609359585569840,10632097546602816816944445466416073366486654512740953706603375905768461201631,12929362833112356946255271584627031609627907912912902502403907291451582319157,1034235212357952436868793031480652544314617407845212103021627643626485031876,19390762319422155950976700977771604452581304443563816543281343170335005291057,10630153844633439282958810722979033212891017579520387012386923832074337305798,14190876500956732147461925775340768352695586121304109940717530850819588911999,18722506055380266423054060346625995391170752586033471909467419883841861306716,3056819986793075694786756176651004538794778835114033048299678024064951583754,4672570536584218848208255703572454924953635491594309524306431682544106754221,17351556719883029551473146382008643929874047263147825865359162665615894766393,15361589507494181649833267508353254535596250253216356073840778948791727807159,13693471199005207103868448237123737586403755421081623632730820742927025187060,17755277847125531485682000612777686738631414648862078678723432159826928724703,5078737090654746516628738054730387217943533822956354885634428155918832329055,12147601749747781924069337935019031145159705806951967042421913575214356549816,14365231440612787726412058658929032228572314258997026523542183583106877612565,7278303658563994843919131396912585917500535615791945995381401546353032136647,16203755920169126984249498560164803107868240707492521482933949021054510520315,6791925607504018751125155518211487306271141824074365658905258365090537532910,13823494237593720607868138054959291887740146822262268248432322209124930846096,11009501160902109690977091445438703229756969339078969536565574715162502634351,14720462490975063947234490477382491041961626472580003583159938559677559185952] ) } - +// noir-fmt:ignore pub fn x5_17_config() -> PoseidonConfig<1292, 289> { config( 17, diff --git a/noir_stdlib/src/lib.nr b/noir_stdlib/src/lib.nr index e6b56d29542..8d878eecbb3 100644 --- a/noir_stdlib/src/lib.nr +++ b/noir_stdlib/src/lib.nr @@ -19,7 +19,6 @@ mod compat; mod option; mod string; mod test; - // Oracle calls are required to be wrapped in an unconstrained function // Thus, the only argument to the `println` oracle is expected to always be an ident #[oracle(println)] @@ -30,37 +29,35 @@ unconstrained pub fn println(input: T) { } #[foreign(recursive_aggregation)] -pub fn verify_proof(_verification_key : [Field], _proof : [Field], _public_inputs : [Field], _key_hash : Field, _input_aggregation_object : [Field; N]) -> [Field; N] {} - +pub fn verify_proof( + _verification_key: [Field], + _proof: [Field], + _public_inputs: [Field], + _key_hash: Field, + _input_aggregation_object: [Field; N] +) -> [Field; N] {} // Asserts that the given value is known at compile-time. // Useful for debugging for-loop bounds. #[builtin(assert_constant)] pub fn assert_constant(_x: T) {} - // from_field and as_field are private since they are not valid for every type. // `as` should be the default for users to cast between primitive types, and in the future // traits can be used to work with generic types. #[builtin(from_field)] -fn from_field(_x : Field) -> T {} +fn from_field(_x: Field) -> T {} #[builtin(as_field)] -fn as_field(_x : T) -> Field {} +fn as_field(_x: T) -> Field {} - -pub fn wrapping_add(x : T, y: T) -> T { +pub fn wrapping_add(x: T, y: T) -> T { crate::from_field(crate::as_field(x) + crate::as_field(y)) } -pub fn wrapping_sub(x : T, y: T) -> T { +pub fn wrapping_sub(x: T, y: T) -> T { //340282366920938463463374607431768211456 is 2^128, it is used to avoid underflow crate::from_field(crate::as_field(x) + 340282366920938463463374607431768211456 - crate::as_field(y)) } -pub fn wrapping_mul(x : T, y: T) -> T { +pub fn wrapping_mul(x: T, y: T) -> T { crate::from_field(crate::as_field(x) * 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/merkle.nr b/noir_stdlib/src/merkle.nr index 2dc9c9814b4..9b15fe7313d 100644 --- a/noir_stdlib/src/merkle.nr +++ b/noir_stdlib/src/merkle.nr @@ -1,7 +1,6 @@ // Regular merkle tree means a append-only merkle tree (Explain why this is the only way to have privacy and alternatives if you don't want it) // Currently we assume that it is a binary tree, so depth k implies a width of 2^k // XXX: In the future we can add an arity parameter - // Returns the merkle root of the tree from the provided leaf, its hashpath, using a pedersen hash function. pub fn compute_merkle_root(leaf: Field, index: Field, hash_path: [Field; N]) -> Field { let n = hash_path.len(); @@ -11,10 +10,10 @@ pub fn compute_merkle_root(leaf: Field, index: Field, hash_path: [Field; N]) let path_bit = index_bits[i] as bool; let (hash_left, hash_right) = if path_bit { (hash_path[i], current) - } else { + } else { (current, hash_path[i]) - }; - current = crate::hash::pedersen_hash([hash_left, hash_right]); - }; + }; + current = crate::hash::pedersen_hash([hash_left, hash_right]); + } current } diff --git a/noir_stdlib/src/scalar_mul.nr b/noir_stdlib/src/scalar_mul.nr index f654a810461..37cd935cdb9 100644 --- a/noir_stdlib/src/scalar_mul.nr +++ b/noir_stdlib/src/scalar_mul.nr @@ -5,4 +5,4 @@ // The embedded curve being used is decided by the // underlying proof system. #[foreign(fixed_base_scalar_mul)] -pub fn fixed_base_embedded_curve(_low : Field, _high : Field) -> [Field; 2] {} +pub fn fixed_base_embedded_curve(_low: Field, _high: Field) -> [Field; 2] {} diff --git a/noir_stdlib/src/schnorr.nr b/noir_stdlib/src/schnorr.nr index c78eae37243..5ed95096f97 100644 --- a/noir_stdlib/src/schnorr.nr +++ b/noir_stdlib/src/schnorr.nr @@ -1,2 +1,7 @@ #[foreign(schnorr_verify)] -pub fn verify_signature(_public_key_x: Field, _public_key_y: Field, _signature: [u8; 64], _message: [u8; N]) -> bool {} +pub fn verify_signature( + _public_key_x: Field, + _public_key_y: Field, + _signature: [u8; 64], + _message: [u8; N] +) -> bool {} diff --git a/noir_stdlib/src/sha256.nr b/noir_stdlib/src/sha256.nr index 0c9f9a01be0..39e39b8cb6e 100644 --- a/noir_stdlib/src/sha256.nr +++ b/noir_stdlib/src/sha256.nr @@ -1,83 +1,78 @@ // Implementation of SHA-256 mapping a byte array of variable length to // 32 bytes. // Internal functions act on 32-bit unsigned integers for simplicity. - // Auxiliary mappings; names as in FIPS PUB 180-4 fn rotr32(a: u32, b: u32) -> u32 // 32-bit right rotation { // 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)) + (a >> b) + (a << (32 - b)) } -fn ch(x: u32, y: u32, z: u32) -> u32 -{ +fn ch(x: u32, y: u32, z: u32) -> u32 { (x & y) ^ ((!x) & z) } -fn maj(x: u32, y: u32, z: u32) -> u32 -{ +fn maj(x: u32, y: u32, z: u32) -> u32 { (x & y) ^ (x & z) ^ (y & z) } -fn bigma0(x: u32) -> u32 -{ +fn bigma0(x: u32) -> u32 { rotr32(x, 2) ^ rotr32(x, 13) ^ rotr32(x, 22) } -fn bigma1(x: u32) -> u32 -{ +fn bigma1(x: u32) -> u32 { rotr32(x, 6) ^ rotr32(x, 11) ^ rotr32(x, 25) } -fn sigma0(x: u32) -> u32 -{ +fn sigma0(x: u32) -> u32 { rotr32(x, 7) ^ rotr32(x, 18) ^ (x >> 3) } -fn sigma1(x: u32) -> u32 -{ +fn sigma1(x: u32) -> u32 { rotr32(x, 17) ^ rotr32(x, 19) ^ (x >> 10) } fn sha_w(msg: [u32; 16]) -> [u32; 64] // Expanded message blocks { - let mut w: [u32;64] = [0; 64]; - - for j in 0..16 - { + + for j in 0..16 { w[j] = msg[j]; - }; + } - for j in 16..64 - { + for j in 16..64 { w[j] = crate::wrapping_add( crate::wrapping_add(sigma1(w[j-2]), w[j-7]), crate::wrapping_add(sigma0(w[j-15]), w[j-16]), ); - }; - + } + w } - // SHA-256 compression function -fn sha_c(msg: [u32; 16], hash: [u32; 8]) -> [u32; 8] -{ - - let K: [u32; 64] = [1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298]; // first 32 bits of fractional parts of cube roots of first 64 primes - +fn sha_c(msg: [u32; 16], hash: [u32; 8]) -> [u32; 8] { + let K: [u32; 64] = [ + 1116352408, 1899447441, 3049323471, 3921009573, 961987163, 1508970993, 2453635748, + 2870763221, 3624381080, 310598401, 607225278, 1426881987, 1925078388, 2162078206, + 2614888103, 3248222580, 3835390401, 4022224774, 264347078, 604807628, 770255983, 1249150122, + 1555081692, 1996064986, 2554220882, 2821834349, 2952996808, 3210313671, 3336571891, + 3584528711, 113926993, 338241895, 666307205, 773529912, 1294757372, 1396182291, 1695183700, + 1986661051, 2177026350, 2456956037, 2730485921, 2820302411, 3259730800, 3345764771, + 3516065817, 3600352804, 4094571909, 275423344, 430227734, 506948616, 659060556, 883997877, + 958139571, 1322822218, 1537002063, 1747873779, 1955562222, 2024104815, 2227730452, + 2361852424, 2428436474, 2756734187, 3204031479, 3329325298 + ]; // first 32 bits of fractional parts of cube roots of first 64 primes let mut out_h: [u32; 8] = hash; let w = sha_w(msg); - for j in 0..64 - { + for j in 0..64 { let t1 = crate::wrapping_add( crate::wrapping_add( crate::wrapping_add(out_h[7], bigma1(out_h[4])), ch(out_h[4], out_h[5], out_h[6]) ), crate::wrapping_add(K[j], w[j]) - ); + ); let t2 = crate::wrapping_add(bigma0(out_h[0]), maj(out_h[0], out_h[1], out_h[2])); out_h[7] = out_h[6]; out_h[6] = out_h[5]; @@ -87,58 +82,51 @@ fn sha_c(msg: [u32; 16], hash: [u32; 8]) -> [u32; 8] out_h[2] = out_h[1]; out_h[1] = out_h[0]; out_h[0] = crate::wrapping_add(t1, t2); - }; - + } + out_h } - // Convert 64-byte array to array of 16 u32s -fn msg_u8_to_u32(msg: [u8; 64]) -> [u32; 16] -{ +fn msg_u8_to_u32(msg: [u8; 64]) -> [u32; 16] { let mut msg32: [u32; 16] = [0; 16]; - for i in 0..16 - { - for j in 0..4 - { + for i in 0..16 { + for j in 0..4 { msg32[15 - i] = (msg32[15 - i] << 8) + msg[64 - 4*(i + 1) + j] as u32; } } - + msg32 } - - // SHA-256 hash function pub fn digest(msg: [u8; N]) -> [u8; 32] { let mut msg_block: [u8; 64] = [0; 64]; - let mut h: [u32; 8] = [1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225]; // Intermediate hash, starting with the canonical initial value + let mut h: [u32; 8] = [1779033703, 3144134277, 1013904242, 2773480762, 1359893119, 2600822924, 528734635, 1541459225]; // Intermediate hash, starting with the canonical initial value let mut c: [u32; 8] = [0; 8]; // Compression of current message block as sequence of u32 let mut out_h: [u8; 32] = [0; 32]; // Digest as sequence of bytes let mut i: u64 = 0; // Message byte pointer - - for k in 0 .. msg.len() { + for k in 0..msg.len() { // Populate msg_block msg_block[i as Field] = msg[k]; i = i + 1; - if i == 64 { // Enough to hash block + if i == 64 { + // Enough to hash block c = sha_c(msg_u8_to_u32(msg_block), h); for j in 0..8 { h[j] = crate::wrapping_add(c[j], h[j]); } - + i = 0; } } - // Pad the rest such that we have a [u32; 2] block at the end representing the length // of the message, and a block of 1 0 ... 0 following the message (i.e. [1 << 7, 0, ..., 0]). msg_block[i as Field] = 1 << 7; i = i + 1; - // If i >= 57, there aren't enough bits in the current message block to accomplish this, so // the 1 and 0s fill up the current block, which we then compress accordingly. - if i >= 57 { // Not enough bits (64) to store length. Fill up with zeros. + if i >= 57 { + // Not enough bits (64) to store length. Fill up with zeros. if i < 64 { for _i in 57..64 { if i <= 63 { @@ -152,11 +140,12 @@ pub fn digest(msg: [u8; N]) -> [u8; 32] { for j in 0..8 { h[j] = crate::wrapping_add(h[j], c[j]); } - + i = 0; } - for _i in 0..64 {// In any case, fill blocks up with zeros until the last 64 (i.e. until i = 56). + for _i in 0..64 { + // In any case, fill blocks up with zeros until the last 64 (i.e. until i = 56). if i < 56 { msg_block[i as Field] = 0; i = i + 1; @@ -169,14 +158,12 @@ pub fn digest(msg: [u8; N]) -> [u8; 32] { i += 8; } } - // Hash final padded block c = h; c = sha_c(msg_u8_to_u32(msg_block), c); for j in 0..8 { h[j] = crate::wrapping_add(h[j], c[j]); } - // Return final hash as byte array for j in 0..8 { for k in 0..4 { diff --git a/noir_stdlib/src/sha512.nr b/noir_stdlib/src/sha512.nr index 43e817b650d..155ba593bba 100644 --- a/noir_stdlib/src/sha512.nr +++ b/noir_stdlib/src/sha512.nr @@ -1,78 +1,65 @@ // Implementation of SHA-512 mapping a byte array of variable length to // 64 bytes. // Internal functions act on 64-bit unsigned integers for simplicity. - // Auxiliary mappings; names as in FIPS PUB 180-4 fn rotr64(a: u64, b: u64) -> u64 // 64-bit right rotation { // 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)) + (a >> b) + (a << (64 - b)) } -fn sha_ch(x: u64, y: u64, z: u64) -> u64 -{ +fn sha_ch(x: u64, y: u64, z: u64) -> u64 { (x & y) ^ (!x & z) } -fn sha_maj(x: u64, y: u64, z: u64) -> u64 -{ +fn sha_maj(x: u64, y: u64, z: u64) -> u64 { (x & y) ^ (x & z) ^ (y & z) } -fn sha_bigma0(x: u64) -> u64 -{ +fn sha_bigma0(x: u64) -> u64 { rotr64(x, 28) ^ rotr64(x, 34) ^ rotr64(x, 39) } -fn sha_bigma1(x: u64) -> u64 -{ +fn sha_bigma1(x: u64) -> u64 { rotr64(x, 14) ^ rotr64(x, 18) ^ rotr64(x, 41) } -fn sha_sigma0(x: u64) -> u64 -{ +fn sha_sigma0(x: u64) -> u64 { rotr64(x, 1) ^ rotr64(x, 8) ^ (x >> 7) } -fn sha_sigma1(x: u64) -> u64 -{ +fn sha_sigma1(x: u64) -> u64 { rotr64(x, 19) ^ rotr64(x, 61) ^ (x >> 6) } fn sha_w(msg: [u64; 16]) -> [u64; 80] // Expanded message blocks { - let mut w: [u64;80] = [0; 80]; - - for j in 0..16 - { + + for j in 0..16 { w[j] = msg[j]; - }; + } - for j in 16..80 - { - w[j] = crate::wrapping_add( + for j in 16..80 { + w[j] = crate::wrapping_add( crate::wrapping_add(sha_sigma1(w[j-2]), w[j-7]), crate::wrapping_add(sha_sigma0(w[j-15]), w[j-16]), ); - }; + } w } - // SHA-512 compression function -fn sha_c(msg: [u64; 16], hash: [u64; 8]) -> [u64; 8] -{ +fn sha_c(msg: [u64; 16], hash: [u64; 8]) -> [u64; 8] { + // noir-fmt:ignore let K: [u64; 80] = [4794697086780616226, 8158064640168781261, 13096744586834688815, 16840607885511220156, 4131703408338449720, 6480981068601479193, 10538285296894168987, 12329834152419229976, 15566598209576043074, 1334009975649890238, 2608012711638119052, 6128411473006802146, 8268148722764581231, 9286055187155687089, 11230858885718282805, 13951009754708518548, 16472876342353939154, 17275323862435702243, 1135362057144423861, 2597628984639134821, 3308224258029322869, 5365058923640841347, 6679025012923562964, 8573033837759648693, 10970295158949994411, 12119686244451234320, 12683024718118986047, 13788192230050041572, 14330467153632333762, 15395433587784984357, 489312712824947311, 1452737877330783856, 2861767655752347644, 3322285676063803686, 5560940570517711597, 5996557281743188959, 7280758554555802590, 8532644243296465576, 9350256976987008742, 10552545826968843579, 11727347734174303076, 12113106623233404929, 14000437183269869457, 14369950271660146224, 15101387698204529176, 15463397548674623760, 17586052441742319658, 1182934255886127544, 1847814050463011016, 2177327727835720531, 2830643537854262169, 3796741975233480872, 4115178125766777443, 5681478168544905931, 6601373596472566643, 7507060721942968483, 8399075790359081724, 8693463985226723168, 9568029438360202098, 10144078919501101548, 10430055236837252648, 11840083180663258601, 13761210420658862357, 14299343276471374635, 14566680578165727644, 15097957966210449927, 16922976911328602910, 17689382322260857208, 500013540394364858, 748580250866718886, 1242879168328830382, 1977374033974150939, 2944078676154940804, 3659926193048069267, 4368137639120453308, 4836135668995329356, 5532061633213252278, 6448918945643986474, 6902733635092675308, 7801388544844847127]; // first 64 bits of fractional parts of cube roots of first 80 primes - let mut out_h: [u64; 8] = hash; let w = sha_w(msg); - for j in 0..80 - { - let out1 = crate::wrapping_add(out_h[7] , sha_bigma1(out_h[4])); + for j in 0..80 { + let out1 = crate::wrapping_add(out_h[7], sha_bigma1(out_h[4])); let out2 = crate::wrapping_add(out1, sha_ch(out_h[4], out_h[5], out_h[6])); let t1 = crate::wrapping_add(crate::wrapping_add(out2, K[j]), w[j]); - let t2 = crate::wrapping_add( sha_bigma0(out_h[0]) , sha_maj(out_h[0], out_h[1], out_h[2])); + let t2 = crate::wrapping_add(sha_bigma0(out_h[0]), sha_maj(out_h[0], out_h[1], out_h[2])); out_h[7] = out_h[6]; out_h[6] = out_h[5]; out_h[5] = out_h[4]; @@ -81,58 +68,52 @@ fn sha_c(msg: [u64; 16], hash: [u64; 8]) -> [u64; 8] out_h[2] = out_h[1]; out_h[1] = out_h[0]; out_h[0] = crate::wrapping_add(t1, t2); - }; - + } + out_h } - // Convert 128-byte array to array of 16 u64s -fn msg_u8_to_u64(msg: [u8; 128]) -> [u64; 16] -{ +fn msg_u8_to_u64(msg: [u8; 128]) -> [u64; 16] { let mut msg64: [u64; 16] = [0; 16]; - for i in 0..16 - { - for j in 0..8 - { + for i in 0..16 { + for j in 0..8 { msg64[15 - i] = (msg64[15 - i] << 8) + msg[128 - 8*(i + 1) + j] as u64; - }; - }; - + } + } + msg64 } - // SHA-512 hash function -pub fn digest(msg: [u8; N]) -> [u8; 64] -{ +pub fn digest(msg: [u8; N]) -> [u8; 64] { let mut msg_block: [u8; 128] = [0; 128]; + // noir-fmt:ignore let mut h: [u64; 8] = [7640891576956012808, 13503953896175478587, 4354685564936845355, 11912009170470909681, 5840696475078001361, 11170449401992604703, 2270897969802886507, 6620516959819538809]; // Intermediate hash, starting with the canonical initial value let mut c: [u64; 8] = [0; 8]; // Compression of current message block as sequence of u64 let mut out_h: [u8; 64] = [0; 64]; // Digest as sequence of bytes let mut i: u64 = 0; // Message byte pointer - - for k in 0 .. msg.len() { + for k in 0..msg.len() { // Populate msg_block msg_block[i as Field] = msg[k]; i = i + 1; - if i == 128 { // Enough to hash block + if i == 128 { + // Enough to hash block c = sha_c(msg_u8_to_u64(msg_block), h); for j in 0..8 { h[j] = crate::wrapping_add(h[j], c[j]); } - + i = 0; } } - // Pad the rest such that we have a [u64; 2] block at the end representing the length // of the message, and a block of 1 0 ... 0 following the message (i.e. [1 << 7, 0, ..., 0]). msg_block[i as Field] = 1 << 7; i += 1; - // If i >= 113, there aren't enough bits in the current message block to accomplish this, so // the 1 and 0s fill up the current block, which we then compress accordingly. - if i >= 113 { // Not enough bits (128) to store length. Fill up with zeros. + if i >= 113 { + // Not enough bits (128) to store length. Fill up with zeros. if i < 128 { for _i in 113..128 { if i <= 127 { @@ -145,11 +126,12 @@ pub fn digest(msg: [u8; N]) -> [u8; 64] for j in 0..8 { h[j] = crate::wrapping_add(h[j], c[j]); } - + i = 0; } - for _i in 0..128 {// In any case, fill blocks up with zeros until the last 128 (i.e. until i = 112). + for _i in 0..128 { + // In any case, fill blocks up with zeros until the last 128 (i.e. until i = 112). if i < 112 { msg_block[i as Field] = 0; i += 1; @@ -162,13 +144,11 @@ pub fn digest(msg: [u8; N]) -> [u8; 64] i += 16; // Done. } } - // Hash final padded block c = sha_c(msg_u8_to_u64(msg_block), h); for j in 0..8 { h[j] = crate::wrapping_add(h[j], c[j]); } - // Return final hash as byte array for j in 0..8 { for k in 0..8 { diff --git a/noir_stdlib/src/slice.nr b/noir_stdlib/src/slice.nr index 249c39d3ae7..a5a9a38ed53 100644 --- a/noir_stdlib/src/slice.nr +++ b/noir_stdlib/src/slice.nr @@ -1,4 +1,3 @@ - impl [T] { /// Push a new element to the end of the slice, returning a /// new slice with a length one greater than the @@ -22,28 +21,16 @@ impl [T] { #[builtin(slice_pop_front)] pub fn pop_front(_self: Self) -> (T, Self) { } - pub fn insert(self, _index: Field, _elem: T) -> Self { - // TODO(#2462): Slice insert with a dynamic index - crate::assert_constant(_index); - self.__slice_insert(_index, _elem) - } - /// Insert an element at a specified index, shifting all elements /// after it to the right #[builtin(slice_insert)] - fn __slice_insert(_self: Self, _index: Field, _elem: T) -> Self { } - - pub fn remove(self, _index: Field) -> (Self, T) { - // TODO(#2462): Slice remove with a dynamic index - crate::assert_constant(_index); - self.__slice_remove(_index) - } + pub fn insert(_self: Self, _index: Field, _elem: T) -> Self { } /// Remove an element at a specified index, shifting all elements /// after it to the left, returning the altered slice and /// the removed element #[builtin(slice_remove)] - fn __slice_remove(_self: Self, _index: Field) -> (Self, T) { } + pub fn remove(_self: Self, _index: Field) -> (Self, T) { } // Append each element of the `other` slice to the end of `self`. // This returns a new slice and leaves both input slices unchanged. diff --git a/noir_stdlib/src/string.nr b/noir_stdlib/src/string.nr index 8b0c02f3dd0..e402abf9ab6 100644 --- a/noir_stdlib/src/string.nr +++ b/noir_stdlib/src/string.nr @@ -8,4 +8,4 @@ impl str { pub fn as_bytes_vec(self: Self) -> Vec { Vec::from_slice(self.as_bytes().as_slice()) } -} \ No newline at end of file +} diff --git a/noir_stdlib/src/test.nr b/noir_stdlib/src/test.nr index 18446a4ef85..47b31f4acea 100644 --- a/noir_stdlib/src/test.nr +++ b/noir_stdlib/src/test.nr @@ -1,17 +1,17 @@ #[oracle(create_mock)] -unconstrained fn create_mock_oracle(_name: str) -> Field {} +unconstrained fn create_mock_oracle(_name: str) -> Field {} #[oracle(set_mock_params)] -unconstrained fn set_mock_params_oracle

(_id: Field, _params: P) {} +unconstrained fn set_mock_params_oracle

(_id: Field, _params: P) {} #[oracle(set_mock_returns)] -unconstrained fn set_mock_returns_oracle(_id: Field, _returns: R) {} +unconstrained fn set_mock_returns_oracle(_id: Field, _returns: R) {} #[oracle(set_mock_times)] -unconstrained fn set_mock_times_oracle(_id: Field, _times: u64) {} +unconstrained fn set_mock_times_oracle(_id: Field, _times: u64) {} #[oracle(clear_mock)] -unconstrained fn clear_mock_oracle(_id: Field) {} +unconstrained fn clear_mock_oracle(_id: Field) {} struct OracleMock { id: Field, diff --git a/package.json b/package.json index 0e86b100b7c..bced1fb21e8 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "test": "yarn workspaces foreach run test", "test:integration": "yarn workspace integration-tests test", "clean:workspaces": "yarn workspaces foreach --exclude @noir-lang/root run clean", - "clean:root": "rm -rf ./result ./target", + "clean:root": "rm -rf ./result ./target ./packages", "clean": "yarn clean:workspaces && yarn clean:root", "lint": "yarn workspaces foreach --verbose run lint", "install:acvm_js": "yarn workspace @noir-lang/acvm_js run install:from:nix", diff --git a/release-please-config.json b/release-please-config.json index afc0bfd420d..7995ea37381 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -48,6 +48,11 @@ "type": "json", "path": "tooling/noirc_abi_wasm/package.json", "jsonpath": "$.version" + }, + { + "type": "json", + "path": "docs/docs/package.json", + "jsonpath": "$.version" } ] }, @@ -67,6 +72,46 @@ "brillig_vm/Cargo.toml", "stdlib/Cargo.toml", "flake.nix", + { + "type": "json", + "path": "acvm_js/package.json", + "jsonpath": "$.version" + }, + { + "type": "toml", + "path": "acir/Cargo.toml", + "jsonpath": "$.workspace.dependencies.acir.version" + }, + { + "type": "toml", + "path": "acir_field/Cargo.toml", + "jsonpath": "$.workspace.dependencies.acir_field.version" + }, + { + "type": "toml", + "path": "stdlib/Cargo.toml", + "jsonpath": "$.workspace.dependencies.stdlib.version" + }, + { + "type": "toml", + "path": "brillig/Cargo.toml", + "jsonpath": "$.workspace.dependencies.brillig.version" + }, + { + "type": "toml", + "path": "brillig_vm/Cargo.toml", + "jsonpath": "$.workspace.dependencies.brillig_vm.version" + }, + { + "type": "toml", + "path": "blackbox_solver/Cargo.toml", + "jsonpath": "$.workspace.dependencies.acvm_blackbox_solver.version" + }, + { + "type": "toml", + "path": "barretenberg_blackbox_solver/Cargo.toml", + "jsonpath": "$.workspace.dependencies.barretenberg_blackbox_solver.version" + }, { "type": "json", "path": "acvm_js/package.json", diff --git a/release-tests/test/6_array.test.js b/release-tests/test/6_array.test.js index 530b7f85bf4..43d4a389264 100644 --- a/release-tests/test/6_array.test.js +++ b/release-tests/test/6_array.test.js @@ -19,27 +19,27 @@ test("promise resolved", async () => { promiseResolved = true; }); -test("nargo builds ../tooling/nargo_cli/tests/execution_success/6_array sucessfully", async () => { +test("nargo builds ../test_programs/execution_success/6_array sucessfully", async () => { await within(async () => { - cd("../tooling/nargo_cli/tests/execution_success/6_array"); + cd("../test_programs/execution_success/6_array"); const command = `${NARGO_BIN} check`; await $`${command}`.nothrow(); }); }); -test("nargo creates proof ../tooling/nargo_cli/tests/execution_success/6_array sucessfully", async () => { +test("nargo creates proof ../test_programs/execution_success/6_array sucessfully", async () => { await within(async () => { - cd("../tooling/nargo_cli/tests/execution_success/6_array"); + cd("../test_programs/execution_success/6_array"); const command = `${NARGO_BIN} prove 6_array`; await $`${command}`.nothrow(); }); }); -test("nargo verifies proof ../tooling/nargo_cli/tests/execution_success/6_array sucessfully", async () => { +test("nargo verifies proof ../test_programs/execution_success/6_array sucessfully", async () => { await within(async () => { - cd("../tooling/nargo_cli/tests/execution_success/6_array"); + cd("../test_programs/execution_success/6_array"); const command = `${NARGO_BIN} verify 6_array`; await $`${command}`.nothrow(); diff --git a/scripts/bootstrap_native.sh b/scripts/bootstrap_native.sh new file mode 100755 index 00000000000..693a9d9678e --- /dev/null +++ b/scripts/bootstrap_native.sh @@ -0,0 +1,16 @@ +#!/bin/bash +set -eu + +cd $(dirname "$0")/.. + +# If this project has been subrepod into another project, set build data manually. +export SOURCE_DATE_EPOCH=$(date +%s) +export GIT_DIRTY=false +if [ -f ".gitrepo" ]; then + export GIT_COMMIT=$(awk '/commit =/ {print $3}' .gitrepo) +else + export GIT_COMMIT=$(git rev-parse --verify HEAD) +fi + +# Build native. +cargo build --features="noirc_driver/aztec" --release diff --git a/scripts/bootstrap_packages.sh b/scripts/bootstrap_packages.sh new file mode 100755 index 00000000000..5fce2675037 --- /dev/null +++ b/scripts/bootstrap_packages.sh @@ -0,0 +1,34 @@ +#!/bin/bash +set -eu + +cd $(dirname "$0")/.. + +./scripts/install_wasm-bindgen.sh + +# If this project has been subrepod into another project, set build data manually. +export SOURCE_DATE_EPOCH=$(date +%s) +export GIT_DIRTY=false +if [ -f ".gitrepo" ]; then + export GIT_COMMIT=$(awk '/commit =/ {print $3}' .gitrepo) +else + export GIT_COMMIT=$(git rev-parse --verify HEAD) +fi + +export cargoExtraArgs="--features noirc_driver/aztec" + +yarn +yarn build + +# We create a folder called packages, that contains each package as it would be published to npm, named correctly. +# These can be useful for testing, or portaling into other projects. +yarn workspaces foreach pack + +rm -rf packages && mkdir -p packages +tar zxfv acvm-repo/acvm_js/package.tgz -C packages && mv packages/package packages/acvm_js +tar zxfv compiler/source-resolver/package.tgz -C packages && mv packages/package packages/source-resolver +tar zxfv compiler/wasm/package.tgz -C packages && mv packages/package packages/noir_wasm +tar zxfv tooling/noir_codegen/package.tgz -C packages && mv packages/package packages/noir_codegen +tar zxfv tooling/noir_js/package.tgz -C packages && mv packages/package packages/noir_js +tar zxfv tooling/noir_js_backend_barretenberg/package.tgz -C packages && mv packages/package packages/backend_barretenberg +tar zxfv tooling/noir_js_types/package.tgz -C packages && mv packages/package packages/types +tar zxfv tooling/noirc_abi_wasm/package.tgz -C packages && mv packages/package packages/noirc_abi \ No newline at end of file diff --git a/scripts/install_wasm-bindgen.sh b/scripts/install_wasm-bindgen.sh new file mode 100755 index 00000000000..5e9f9127506 --- /dev/null +++ b/scripts/install_wasm-bindgen.sh @@ -0,0 +1,10 @@ +#!/bin/bash +set -eu + +cd $(dirname "$0")/.. + +# Install wasm-bindgen-cli. +if [ "$(wasm-bindgen --version | cut -d' ' -f2)" != "0.2.86" ]; then + echo "Building wasm-bindgen..." + RUSTFLAGS="-Ctarget-feature=-crt-static" cargo install -f wasm-bindgen-cli --version 0.2.86 +fi diff --git a/tooling/nargo_cli/tests/README.md b/test_programs/README.md similarity index 100% rename from tooling/nargo_cli/tests/README.md rename to test_programs/README.md diff --git a/tooling/nargo_cli/tests/acir_artifacts/1327_concrete_in_generic/target/acir.gz b/test_programs/acir_artifacts/1327_concrete_in_generic/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/1327_concrete_in_generic/target/acir.gz rename to test_programs/acir_artifacts/1327_concrete_in_generic/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/1327_concrete_in_generic/target/witness.gz b/test_programs/acir_artifacts/1327_concrete_in_generic/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/1327_concrete_in_generic/target/witness.gz rename to test_programs/acir_artifacts/1327_concrete_in_generic/target/witness.gz diff --git a/test_programs/acir_artifacts/1_mul/target/acir.gz b/test_programs/acir_artifacts/1_mul/target/acir.gz new file mode 100644 index 00000000000..7572c9ac2cf Binary files /dev/null and b/test_programs/acir_artifacts/1_mul/target/acir.gz differ diff --git a/test_programs/acir_artifacts/1_mul/target/witness.gz b/test_programs/acir_artifacts/1_mul/target/witness.gz new file mode 100644 index 00000000000..76f5c8a2fe2 Binary files /dev/null and b/test_programs/acir_artifacts/1_mul/target/witness.gz differ diff --git a/test_programs/acir_artifacts/2_div/target/acir.gz b/test_programs/acir_artifacts/2_div/target/acir.gz new file mode 100644 index 00000000000..46405fc2029 Binary files /dev/null and b/test_programs/acir_artifacts/2_div/target/acir.gz differ diff --git a/test_programs/acir_artifacts/2_div/target/witness.gz b/test_programs/acir_artifacts/2_div/target/witness.gz new file mode 100644 index 00000000000..3145b77d957 Binary files /dev/null and b/test_programs/acir_artifacts/2_div/target/witness.gz differ diff --git a/test_programs/acir_artifacts/3_add/target/acir.gz b/test_programs/acir_artifacts/3_add/target/acir.gz new file mode 100644 index 00000000000..42e66d90f73 Binary files /dev/null and b/test_programs/acir_artifacts/3_add/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/3_add/target/witness.gz b/test_programs/acir_artifacts/3_add/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/3_add/target/witness.gz rename to test_programs/acir_artifacts/3_add/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/4_sub/target/acir.gz b/test_programs/acir_artifacts/4_sub/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/4_sub/target/acir.gz rename to test_programs/acir_artifacts/4_sub/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/4_sub/target/witness.gz b/test_programs/acir_artifacts/4_sub/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/4_sub/target/witness.gz rename to test_programs/acir_artifacts/4_sub/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/5_over/target/acir.gz b/test_programs/acir_artifacts/5_over/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/5_over/target/acir.gz rename to test_programs/acir_artifacts/5_over/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/5_over/target/witness.gz b/test_programs/acir_artifacts/5_over/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/5_over/target/witness.gz rename to test_programs/acir_artifacts/5_over/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/6/target/acir.gz b/test_programs/acir_artifacts/6/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/6/target/acir.gz rename to test_programs/acir_artifacts/6/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/6/target/witness.gz b/test_programs/acir_artifacts/6/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/6/target/witness.gz rename to test_programs/acir_artifacts/6/target/witness.gz diff --git a/test_programs/acir_artifacts/6_array/target/acir.gz b/test_programs/acir_artifacts/6_array/target/acir.gz new file mode 100644 index 00000000000..787db190b49 Binary files /dev/null and b/test_programs/acir_artifacts/6_array/target/acir.gz differ diff --git a/test_programs/acir_artifacts/6_array/target/witness.gz b/test_programs/acir_artifacts/6_array/target/witness.gz new file mode 100644 index 00000000000..cc96fd18e00 Binary files /dev/null and b/test_programs/acir_artifacts/6_array/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/7/target/acir.gz b/test_programs/acir_artifacts/7/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/7/target/acir.gz rename to test_programs/acir_artifacts/7/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/7/target/witness.gz b/test_programs/acir_artifacts/7/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/7/target/witness.gz rename to test_programs/acir_artifacts/7/target/witness.gz diff --git a/test_programs/acir_artifacts/7_function/target/acir.gz b/test_programs/acir_artifacts/7_function/target/acir.gz new file mode 100644 index 00000000000..5ddc1ba38e6 Binary files /dev/null and b/test_programs/acir_artifacts/7_function/target/acir.gz differ diff --git a/test_programs/acir_artifacts/7_function/target/witness.gz b/test_programs/acir_artifacts/7_function/target/witness.gz new file mode 100644 index 00000000000..0bb522d210e Binary files /dev/null and b/test_programs/acir_artifacts/7_function/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/arithmetic_binary_operations/target/acir.gz b/test_programs/acir_artifacts/arithmetic_binary_operations/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/arithmetic_binary_operations/target/acir.gz rename to test_programs/acir_artifacts/arithmetic_binary_operations/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/arithmetic_binary_operations/target/witness.gz b/test_programs/acir_artifacts/arithmetic_binary_operations/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/arithmetic_binary_operations/target/witness.gz rename to test_programs/acir_artifacts/arithmetic_binary_operations/target/witness.gz diff --git a/test_programs/acir_artifacts/array_dynamic/target/acir.gz b/test_programs/acir_artifacts/array_dynamic/target/acir.gz new file mode 100644 index 00000000000..e6111539302 Binary files /dev/null and b/test_programs/acir_artifacts/array_dynamic/target/acir.gz differ diff --git a/test_programs/acir_artifacts/array_dynamic/target/witness.gz b/test_programs/acir_artifacts/array_dynamic/target/witness.gz new file mode 100644 index 00000000000..102bb7ad178 Binary files /dev/null and b/test_programs/acir_artifacts/array_dynamic/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/array_eq/target/acir.gz b/test_programs/acir_artifacts/array_eq/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/array_eq/target/acir.gz rename to test_programs/acir_artifacts/array_eq/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/array_eq/target/witness.gz b/test_programs/acir_artifacts/array_eq/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/array_eq/target/witness.gz rename to test_programs/acir_artifacts/array_eq/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/array_len/target/acir.gz b/test_programs/acir_artifacts/array_len/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/array_len/target/acir.gz rename to test_programs/acir_artifacts/array_len/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/array_len/target/witness.gz b/test_programs/acir_artifacts/array_len/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/array_len/target/witness.gz rename to test_programs/acir_artifacts/array_len/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/array_neq/target/acir.gz b/test_programs/acir_artifacts/array_neq/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/array_neq/target/acir.gz rename to test_programs/acir_artifacts/array_neq/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/array_neq/target/witness.gz b/test_programs/acir_artifacts/array_neq/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/array_neq/target/witness.gz rename to test_programs/acir_artifacts/array_neq/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/array_sort/target/acir.gz b/test_programs/acir_artifacts/array_sort/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/array_sort/target/acir.gz rename to test_programs/acir_artifacts/array_sort/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/array_sort/target/witness.gz b/test_programs/acir_artifacts/array_sort/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/array_sort/target/witness.gz rename to test_programs/acir_artifacts/array_sort/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/assert/target/acir.gz b/test_programs/acir_artifacts/assert/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/assert/target/acir.gz rename to test_programs/acir_artifacts/assert/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/assert/target/witness.gz b/test_programs/acir_artifacts/assert/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/assert/target/witness.gz rename to test_programs/acir_artifacts/assert/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/assert_statement/target/acir.gz b/test_programs/acir_artifacts/assert_statement/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/assert_statement/target/acir.gz rename to test_programs/acir_artifacts/assert_statement/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/assert_statement/target/witness.gz b/test_programs/acir_artifacts/assert_statement/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/assert_statement/target/witness.gz rename to test_programs/acir_artifacts/assert_statement/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/assign_ex/target/acir.gz b/test_programs/acir_artifacts/assign_ex/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/assign_ex/target/acir.gz rename to test_programs/acir_artifacts/assign_ex/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/assign_ex/target/witness.gz b/test_programs/acir_artifacts/assign_ex/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/assign_ex/target/witness.gz rename to test_programs/acir_artifacts/assign_ex/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/bit_and/target/acir.gz b/test_programs/acir_artifacts/bit_and/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/bit_and/target/acir.gz rename to test_programs/acir_artifacts/bit_and/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/bit_and/target/witness.gz b/test_programs/acir_artifacts/bit_and/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/bit_and/target/witness.gz rename to test_programs/acir_artifacts/bit_and/target/witness.gz diff --git a/test_programs/acir_artifacts/bit_shifts_comptime/target/acir.gz b/test_programs/acir_artifacts/bit_shifts_comptime/target/acir.gz new file mode 100644 index 00000000000..d6c006fd102 Binary files /dev/null and b/test_programs/acir_artifacts/bit_shifts_comptime/target/acir.gz differ diff --git a/test_programs/acir_artifacts/bit_shifts_comptime/target/witness.gz b/test_programs/acir_artifacts/bit_shifts_comptime/target/witness.gz new file mode 100644 index 00000000000..81bae695da1 Binary files /dev/null and b/test_programs/acir_artifacts/bit_shifts_comptime/target/witness.gz differ diff --git a/test_programs/acir_artifacts/bit_shifts_runtime/target/acir.gz b/test_programs/acir_artifacts/bit_shifts_runtime/target/acir.gz new file mode 100644 index 00000000000..1a800a63a57 Binary files /dev/null and b/test_programs/acir_artifacts/bit_shifts_runtime/target/acir.gz differ diff --git a/test_programs/acir_artifacts/bit_shifts_runtime/target/witness.gz b/test_programs/acir_artifacts/bit_shifts_runtime/target/witness.gz new file mode 100644 index 00000000000..2af844993dd Binary files /dev/null and b/test_programs/acir_artifacts/bit_shifts_runtime/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/bool_not/target/acir.gz b/test_programs/acir_artifacts/bool_not/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/bool_not/target/acir.gz rename to test_programs/acir_artifacts/bool_not/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/bool_not/target/witness.gz b/test_programs/acir_artifacts/bool_not/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/bool_not/target/witness.gz rename to test_programs/acir_artifacts/bool_not/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/bool_or/target/acir.gz b/test_programs/acir_artifacts/bool_or/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/bool_or/target/acir.gz rename to test_programs/acir_artifacts/bool_or/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/bool_or/target/witness.gz b/test_programs/acir_artifacts/bool_or/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/bool_or/target/witness.gz rename to test_programs/acir_artifacts/bool_or/target/witness.gz diff --git a/test_programs/acir_artifacts/brillig_acir_as_brillig/target/acir.gz b/test_programs/acir_artifacts/brillig_acir_as_brillig/target/acir.gz new file mode 100644 index 00000000000..dfcbcfe2d5b Binary files /dev/null and b/test_programs/acir_artifacts/brillig_acir_as_brillig/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_acir_as_brillig/target/witness.gz b/test_programs/acir_artifacts/brillig_acir_as_brillig/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/brillig_acir_as_brillig/target/witness.gz rename to test_programs/acir_artifacts/brillig_acir_as_brillig/target/witness.gz diff --git a/test_programs/acir_artifacts/brillig_arrays/target/acir.gz b/test_programs/acir_artifacts/brillig_arrays/target/acir.gz new file mode 100644 index 00000000000..afadfe0d7c8 Binary files /dev/null and b/test_programs/acir_artifacts/brillig_arrays/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_arrays/target/witness.gz b/test_programs/acir_artifacts/brillig_arrays/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/brillig_arrays/target/witness.gz rename to test_programs/acir_artifacts/brillig_arrays/target/witness.gz diff --git a/test_programs/acir_artifacts/brillig_assert/target/acir.gz b/test_programs/acir_artifacts/brillig_assert/target/acir.gz new file mode 100644 index 00000000000..10c49ba04e3 Binary files /dev/null and b/test_programs/acir_artifacts/brillig_assert/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_assert/target/witness.gz b/test_programs/acir_artifacts/brillig_assert/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/brillig_assert/target/witness.gz rename to test_programs/acir_artifacts/brillig_assert/target/witness.gz diff --git a/test_programs/acir_artifacts/brillig_blake2s/target/acir.gz b/test_programs/acir_artifacts/brillig_blake2s/target/acir.gz new file mode 100644 index 00000000000..5511eedede9 Binary files /dev/null and b/test_programs/acir_artifacts/brillig_blake2s/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_blake2s/target/witness.gz b/test_programs/acir_artifacts/brillig_blake2s/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/brillig_blake2s/target/witness.gz rename to test_programs/acir_artifacts/brillig_blake2s/target/witness.gz diff --git a/test_programs/acir_artifacts/brillig_calls/target/acir.gz b/test_programs/acir_artifacts/brillig_calls/target/acir.gz new file mode 100644 index 00000000000..71a5956d1f4 Binary files /dev/null and b/test_programs/acir_artifacts/brillig_calls/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_calls/target/witness.gz b/test_programs/acir_artifacts/brillig_calls/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/brillig_calls/target/witness.gz rename to test_programs/acir_artifacts/brillig_calls/target/witness.gz diff --git a/test_programs/acir_artifacts/brillig_calls_array/target/acir.gz b/test_programs/acir_artifacts/brillig_calls_array/target/acir.gz new file mode 100644 index 00000000000..970c95756c7 Binary files /dev/null and b/test_programs/acir_artifacts/brillig_calls_array/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_calls_array/target/witness.gz b/test_programs/acir_artifacts/brillig_calls_array/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/brillig_calls_array/target/witness.gz rename to test_programs/acir_artifacts/brillig_calls_array/target/witness.gz diff --git a/test_programs/acir_artifacts/brillig_calls_conditionals/target/acir.gz b/test_programs/acir_artifacts/brillig_calls_conditionals/target/acir.gz new file mode 100644 index 00000000000..1c06691acfe Binary files /dev/null and b/test_programs/acir_artifacts/brillig_calls_conditionals/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_calls_conditionals/target/witness.gz b/test_programs/acir_artifacts/brillig_calls_conditionals/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/brillig_calls_conditionals/target/witness.gz rename to test_programs/acir_artifacts/brillig_calls_conditionals/target/witness.gz diff --git a/test_programs/acir_artifacts/brillig_conditional/target/acir.gz b/test_programs/acir_artifacts/brillig_conditional/target/acir.gz new file mode 100644 index 00000000000..09f123508f7 Binary files /dev/null and b/test_programs/acir_artifacts/brillig_conditional/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_conditional/target/witness.gz b/test_programs/acir_artifacts/brillig_conditional/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/brillig_conditional/target/witness.gz rename to test_programs/acir_artifacts/brillig_conditional/target/witness.gz diff --git a/test_programs/acir_artifacts/brillig_ecdsa/target/acir.gz b/test_programs/acir_artifacts/brillig_ecdsa/target/acir.gz new file mode 100644 index 00000000000..5514ad75442 Binary files /dev/null and b/test_programs/acir_artifacts/brillig_ecdsa/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_ecdsa/target/witness.gz b/test_programs/acir_artifacts/brillig_ecdsa/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/brillig_ecdsa/target/witness.gz rename to test_programs/acir_artifacts/brillig_ecdsa/target/witness.gz diff --git a/test_programs/acir_artifacts/brillig_fns_as_values/target/acir.gz b/test_programs/acir_artifacts/brillig_fns_as_values/target/acir.gz new file mode 100644 index 00000000000..db2717ae656 Binary files /dev/null and b/test_programs/acir_artifacts/brillig_fns_as_values/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_fns_as_values/target/witness.gz b/test_programs/acir_artifacts/brillig_fns_as_values/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/brillig_fns_as_values/target/witness.gz rename to test_programs/acir_artifacts/brillig_fns_as_values/target/witness.gz diff --git a/test_programs/acir_artifacts/brillig_hash_to_field/target/acir.gz b/test_programs/acir_artifacts/brillig_hash_to_field/target/acir.gz new file mode 100644 index 00000000000..4ef5ef5d1cb Binary files /dev/null and b/test_programs/acir_artifacts/brillig_hash_to_field/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_hash_to_field/target/witness.gz b/test_programs/acir_artifacts/brillig_hash_to_field/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/brillig_hash_to_field/target/witness.gz rename to test_programs/acir_artifacts/brillig_hash_to_field/target/witness.gz diff --git a/test_programs/acir_artifacts/brillig_identity_function/target/acir.gz b/test_programs/acir_artifacts/brillig_identity_function/target/acir.gz new file mode 100644 index 00000000000..080769c4fe7 Binary files /dev/null and b/test_programs/acir_artifacts/brillig_identity_function/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_identity_function/target/witness.gz b/test_programs/acir_artifacts/brillig_identity_function/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/brillig_identity_function/target/witness.gz rename to test_programs/acir_artifacts/brillig_identity_function/target/witness.gz diff --git a/test_programs/acir_artifacts/brillig_keccak/target/acir.gz b/test_programs/acir_artifacts/brillig_keccak/target/acir.gz new file mode 100644 index 00000000000..6f22b23625c Binary files /dev/null and b/test_programs/acir_artifacts/brillig_keccak/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_keccak/target/witness.gz b/test_programs/acir_artifacts/brillig_keccak/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/brillig_keccak/target/witness.gz rename to test_programs/acir_artifacts/brillig_keccak/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_loop/target/acir.gz b/test_programs/acir_artifacts/brillig_loop/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/brillig_loop/target/acir.gz rename to test_programs/acir_artifacts/brillig_loop/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_loop/target/witness.gz b/test_programs/acir_artifacts/brillig_loop/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/brillig_loop/target/witness.gz rename to test_programs/acir_artifacts/brillig_loop/target/witness.gz diff --git a/test_programs/acir_artifacts/brillig_nested_arrays/target/acir.gz b/test_programs/acir_artifacts/brillig_nested_arrays/target/acir.gz new file mode 100644 index 00000000000..2fe7430584b Binary files /dev/null and b/test_programs/acir_artifacts/brillig_nested_arrays/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_nested_arrays/target/witness.gz b/test_programs/acir_artifacts/brillig_nested_arrays/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/brillig_nested_arrays/target/witness.gz rename to test_programs/acir_artifacts/brillig_nested_arrays/target/witness.gz diff --git a/test_programs/acir_artifacts/brillig_nested_slices/target/acir.gz b/test_programs/acir_artifacts/brillig_nested_slices/target/acir.gz new file mode 100644 index 00000000000..2247d8a8baa Binary files /dev/null and b/test_programs/acir_artifacts/brillig_nested_slices/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_nested_slices/target/witness.gz b/test_programs/acir_artifacts/brillig_nested_slices/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/brillig_nested_slices/target/witness.gz rename to test_programs/acir_artifacts/brillig_nested_slices/target/witness.gz diff --git a/test_programs/acir_artifacts/brillig_not/target/acir.gz b/test_programs/acir_artifacts/brillig_not/target/acir.gz new file mode 100644 index 00000000000..f97dbde95da Binary files /dev/null and b/test_programs/acir_artifacts/brillig_not/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_not/target/witness.gz b/test_programs/acir_artifacts/brillig_not/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/brillig_not/target/witness.gz rename to test_programs/acir_artifacts/brillig_not/target/witness.gz diff --git a/test_programs/acir_artifacts/brillig_oracle/target/acir.gz b/test_programs/acir_artifacts/brillig_oracle/target/acir.gz new file mode 100644 index 00000000000..a1854cf090a Binary files /dev/null and b/test_programs/acir_artifacts/brillig_oracle/target/acir.gz differ diff --git a/test_programs/acir_artifacts/brillig_oracle/target/witness.gz b/test_programs/acir_artifacts/brillig_oracle/target/witness.gz new file mode 100644 index 00000000000..bd1fe47aade Binary files /dev/null and b/test_programs/acir_artifacts/brillig_oracle/target/witness.gz differ diff --git a/test_programs/acir_artifacts/brillig_pedersen/target/acir.gz b/test_programs/acir_artifacts/brillig_pedersen/target/acir.gz new file mode 100644 index 00000000000..a8971591478 Binary files /dev/null and b/test_programs/acir_artifacts/brillig_pedersen/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_pedersen/target/witness.gz b/test_programs/acir_artifacts/brillig_pedersen/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/brillig_pedersen/target/witness.gz rename to test_programs/acir_artifacts/brillig_pedersen/target/witness.gz diff --git a/test_programs/acir_artifacts/brillig_recursion/target/acir.gz b/test_programs/acir_artifacts/brillig_recursion/target/acir.gz new file mode 100644 index 00000000000..88daa302398 Binary files /dev/null and b/test_programs/acir_artifacts/brillig_recursion/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_recursion/target/witness.gz b/test_programs/acir_artifacts/brillig_recursion/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/brillig_recursion/target/witness.gz rename to test_programs/acir_artifacts/brillig_recursion/target/witness.gz diff --git a/test_programs/acir_artifacts/brillig_references/target/acir.gz b/test_programs/acir_artifacts/brillig_references/target/acir.gz new file mode 100644 index 00000000000..d28e62ff67e Binary files /dev/null and b/test_programs/acir_artifacts/brillig_references/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_references/target/witness.gz b/test_programs/acir_artifacts/brillig_references/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/brillig_references/target/witness.gz rename to test_programs/acir_artifacts/brillig_references/target/witness.gz diff --git a/test_programs/acir_artifacts/brillig_scalar_mul/target/acir.gz b/test_programs/acir_artifacts/brillig_scalar_mul/target/acir.gz new file mode 100644 index 00000000000..51c25758d37 Binary files /dev/null and b/test_programs/acir_artifacts/brillig_scalar_mul/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_scalar_mul/target/witness.gz b/test_programs/acir_artifacts/brillig_scalar_mul/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/brillig_scalar_mul/target/witness.gz rename to test_programs/acir_artifacts/brillig_scalar_mul/target/witness.gz diff --git a/test_programs/acir_artifacts/brillig_schnorr/target/acir.gz b/test_programs/acir_artifacts/brillig_schnorr/target/acir.gz new file mode 100644 index 00000000000..95af8d0743f Binary files /dev/null and b/test_programs/acir_artifacts/brillig_schnorr/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_schnorr/target/witness.gz b/test_programs/acir_artifacts/brillig_schnorr/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/brillig_schnorr/target/witness.gz rename to test_programs/acir_artifacts/brillig_schnorr/target/witness.gz diff --git a/test_programs/acir_artifacts/brillig_sha256/target/acir.gz b/test_programs/acir_artifacts/brillig_sha256/target/acir.gz new file mode 100644 index 00000000000..f497023135a Binary files /dev/null and b/test_programs/acir_artifacts/brillig_sha256/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_sha256/target/witness.gz b/test_programs/acir_artifacts/brillig_sha256/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/brillig_sha256/target/witness.gz rename to test_programs/acir_artifacts/brillig_sha256/target/witness.gz diff --git a/test_programs/acir_artifacts/brillig_slices/target/acir.gz b/test_programs/acir_artifacts/brillig_slices/target/acir.gz new file mode 100644 index 00000000000..c58474d160c Binary files /dev/null and b/test_programs/acir_artifacts/brillig_slices/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_slices/target/witness.gz b/test_programs/acir_artifacts/brillig_slices/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/brillig_slices/target/witness.gz rename to test_programs/acir_artifacts/brillig_slices/target/witness.gz diff --git a/test_programs/acir_artifacts/brillig_to_be_bytes/target/acir.gz b/test_programs/acir_artifacts/brillig_to_be_bytes/target/acir.gz new file mode 100644 index 00000000000..011371d0bf5 Binary files /dev/null and b/test_programs/acir_artifacts/brillig_to_be_bytes/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_to_be_bytes/target/witness.gz b/test_programs/acir_artifacts/brillig_to_be_bytes/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/brillig_to_be_bytes/target/witness.gz rename to test_programs/acir_artifacts/brillig_to_be_bytes/target/witness.gz diff --git a/test_programs/acir_artifacts/brillig_to_bytes_integration/target/acir.gz b/test_programs/acir_artifacts/brillig_to_bytes_integration/target/acir.gz new file mode 100644 index 00000000000..e235e8e3134 Binary files /dev/null and b/test_programs/acir_artifacts/brillig_to_bytes_integration/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_to_bytes_integration/target/witness.gz b/test_programs/acir_artifacts/brillig_to_bytes_integration/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/brillig_to_bytes_integration/target/witness.gz rename to test_programs/acir_artifacts/brillig_to_bytes_integration/target/witness.gz diff --git a/test_programs/acir_artifacts/brillig_to_le_bytes/target/acir.gz b/test_programs/acir_artifacts/brillig_to_le_bytes/target/acir.gz new file mode 100644 index 00000000000..b2a486c7176 Binary files /dev/null and b/test_programs/acir_artifacts/brillig_to_le_bytes/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_to_le_bytes/target/witness.gz b/test_programs/acir_artifacts/brillig_to_le_bytes/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/brillig_to_le_bytes/target/witness.gz rename to test_programs/acir_artifacts/brillig_to_le_bytes/target/witness.gz diff --git a/test_programs/acir_artifacts/brillig_top_level/target/acir.gz b/test_programs/acir_artifacts/brillig_top_level/target/acir.gz new file mode 100644 index 00000000000..c200de2ac0e Binary files /dev/null and b/test_programs/acir_artifacts/brillig_top_level/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_top_level/target/witness.gz b/test_programs/acir_artifacts/brillig_top_level/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/brillig_top_level/target/witness.gz rename to test_programs/acir_artifacts/brillig_top_level/target/witness.gz diff --git a/test_programs/acir_artifacts/brillig_unitialised_arrays/target/acir.gz b/test_programs/acir_artifacts/brillig_unitialised_arrays/target/acir.gz new file mode 100644 index 00000000000..3b1f545133d Binary files /dev/null and b/test_programs/acir_artifacts/brillig_unitialised_arrays/target/acir.gz differ diff --git a/test_programs/acir_artifacts/brillig_unitialised_arrays/target/witness.gz b/test_programs/acir_artifacts/brillig_unitialised_arrays/target/witness.gz new file mode 100644 index 00000000000..9724de0f1d9 Binary files /dev/null and b/test_programs/acir_artifacts/brillig_unitialised_arrays/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/cast_bool/target/acir.gz b/test_programs/acir_artifacts/cast_bool/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/cast_bool/target/acir.gz rename to test_programs/acir_artifacts/cast_bool/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/cast_bool/target/witness.gz b/test_programs/acir_artifacts/cast_bool/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/cast_bool/target/witness.gz rename to test_programs/acir_artifacts/cast_bool/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/closures_mut_ref/target/acir.gz b/test_programs/acir_artifacts/closures_mut_ref/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/closures_mut_ref/target/acir.gz rename to test_programs/acir_artifacts/closures_mut_ref/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/closures_mut_ref/target/witness.gz b/test_programs/acir_artifacts/closures_mut_ref/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/closures_mut_ref/target/witness.gz rename to test_programs/acir_artifacts/closures_mut_ref/target/witness.gz diff --git a/test_programs/acir_artifacts/conditional_1/target/acir.gz b/test_programs/acir_artifacts/conditional_1/target/acir.gz new file mode 100644 index 00000000000..16ec8f28b53 Binary files /dev/null and b/test_programs/acir_artifacts/conditional_1/target/acir.gz differ diff --git a/test_programs/acir_artifacts/conditional_1/target/witness.gz b/test_programs/acir_artifacts/conditional_1/target/witness.gz new file mode 100644 index 00000000000..30cc2834841 Binary files /dev/null and b/test_programs/acir_artifacts/conditional_1/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/conditional_2/target/acir.gz b/test_programs/acir_artifacts/conditional_2/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/conditional_2/target/acir.gz rename to test_programs/acir_artifacts/conditional_2/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/conditional_2/target/witness.gz b/test_programs/acir_artifacts/conditional_2/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/conditional_2/target/witness.gz rename to test_programs/acir_artifacts/conditional_2/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/conditional_regression_421/target/acir.gz b/test_programs/acir_artifacts/conditional_regression_421/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/conditional_regression_421/target/acir.gz rename to test_programs/acir_artifacts/conditional_regression_421/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/conditional_regression_421/target/witness.gz b/test_programs/acir_artifacts/conditional_regression_421/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/conditional_regression_421/target/witness.gz rename to test_programs/acir_artifacts/conditional_regression_421/target/witness.gz diff --git a/test_programs/acir_artifacts/conditional_regression_661/target/acir.gz b/test_programs/acir_artifacts/conditional_regression_661/target/acir.gz new file mode 100644 index 00000000000..51e30b8bbc1 Binary files /dev/null and b/test_programs/acir_artifacts/conditional_regression_661/target/acir.gz differ diff --git a/test_programs/acir_artifacts/conditional_regression_661/target/witness.gz b/test_programs/acir_artifacts/conditional_regression_661/target/witness.gz new file mode 100644 index 00000000000..2683a9ba4ae Binary files /dev/null and b/test_programs/acir_artifacts/conditional_regression_661/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/conditional_regression_short_circuit/target/acir.gz b/test_programs/acir_artifacts/conditional_regression_short_circuit/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/conditional_regression_short_circuit/target/acir.gz rename to test_programs/acir_artifacts/conditional_regression_short_circuit/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/conditional_regression_short_circuit/target/witness.gz b/test_programs/acir_artifacts/conditional_regression_short_circuit/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/conditional_regression_short_circuit/target/witness.gz rename to test_programs/acir_artifacts/conditional_regression_short_circuit/target/witness.gz diff --git a/test_programs/acir_artifacts/conditional_regression_underflow/target/acir.gz b/test_programs/acir_artifacts/conditional_regression_underflow/target/acir.gz new file mode 100644 index 00000000000..df762d9205e Binary files /dev/null and b/test_programs/acir_artifacts/conditional_regression_underflow/target/acir.gz differ diff --git a/test_programs/acir_artifacts/conditional_regression_underflow/target/witness.gz b/test_programs/acir_artifacts/conditional_regression_underflow/target/witness.gz new file mode 100644 index 00000000000..939eb503b6f Binary files /dev/null and b/test_programs/acir_artifacts/conditional_regression_underflow/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/custom_entry/target/acir.gz b/test_programs/acir_artifacts/custom_entry/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/custom_entry/target/acir.gz rename to test_programs/acir_artifacts/custom_entry/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/custom_entry/target/witness.gz b/test_programs/acir_artifacts/custom_entry/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/custom_entry/target/witness.gz rename to test_programs/acir_artifacts/custom_entry/target/witness.gz diff --git a/test_programs/acir_artifacts/debug_logs/target/acir.gz b/test_programs/acir_artifacts/debug_logs/target/acir.gz new file mode 100644 index 00000000000..49568a14320 Binary files /dev/null and b/test_programs/acir_artifacts/debug_logs/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/debug_logs/target/witness.gz b/test_programs/acir_artifacts/debug_logs/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/debug_logs/target/witness.gz rename to test_programs/acir_artifacts/debug_logs/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/diamond_deps_0/target/acir.gz b/test_programs/acir_artifacts/diamond_deps_0/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/diamond_deps_0/target/acir.gz rename to test_programs/acir_artifacts/diamond_deps_0/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/diamond_deps_0/target/witness.gz b/test_programs/acir_artifacts/diamond_deps_0/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/diamond_deps_0/target/witness.gz rename to test_programs/acir_artifacts/diamond_deps_0/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/distinct_keyword/target/acir.gz b/test_programs/acir_artifacts/distinct_keyword/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/distinct_keyword/target/acir.gz rename to test_programs/acir_artifacts/distinct_keyword/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/distinct_keyword/target/witness.gz b/test_programs/acir_artifacts/distinct_keyword/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/distinct_keyword/target/witness.gz rename to test_programs/acir_artifacts/distinct_keyword/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/double_verify_proof/target/acir.gz b/test_programs/acir_artifacts/double_verify_proof/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/double_verify_proof/target/acir.gz rename to test_programs/acir_artifacts/double_verify_proof/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/double_verify_proof/target/witness.gz b/test_programs/acir_artifacts/double_verify_proof/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/double_verify_proof/target/witness.gz rename to test_programs/acir_artifacts/double_verify_proof/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/ecdsa_secp256k1/target/acir.gz b/test_programs/acir_artifacts/ecdsa_secp256k1/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/ecdsa_secp256k1/target/acir.gz rename to test_programs/acir_artifacts/ecdsa_secp256k1/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/ecdsa_secp256k1/target/witness.gz b/test_programs/acir_artifacts/ecdsa_secp256k1/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/ecdsa_secp256k1/target/witness.gz rename to test_programs/acir_artifacts/ecdsa_secp256k1/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/ecdsa_secp256r1/target/acir.gz b/test_programs/acir_artifacts/ecdsa_secp256r1/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/ecdsa_secp256r1/target/acir.gz rename to test_programs/acir_artifacts/ecdsa_secp256r1/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/ecdsa_secp256r1/target/witness.gz b/test_programs/acir_artifacts/ecdsa_secp256r1/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/ecdsa_secp256r1/target/witness.gz rename to test_programs/acir_artifacts/ecdsa_secp256r1/target/witness.gz diff --git a/test_programs/acir_artifacts/eddsa/target/acir.gz b/test_programs/acir_artifacts/eddsa/target/acir.gz new file mode 100644 index 00000000000..b8577dbd3ac Binary files /dev/null and b/test_programs/acir_artifacts/eddsa/target/acir.gz differ diff --git a/test_programs/acir_artifacts/eddsa/target/witness.gz b/test_programs/acir_artifacts/eddsa/target/witness.gz new file mode 100644 index 00000000000..7c125021d96 Binary files /dev/null and b/test_programs/acir_artifacts/eddsa/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/field_attribute/target/acir.gz b/test_programs/acir_artifacts/field_attribute/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/field_attribute/target/acir.gz rename to test_programs/acir_artifacts/field_attribute/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/field_attribute/target/witness.gz b/test_programs/acir_artifacts/field_attribute/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/field_attribute/target/witness.gz rename to test_programs/acir_artifacts/field_attribute/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/generics/target/acir.gz b/test_programs/acir_artifacts/generics/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/generics/target/acir.gz rename to test_programs/acir_artifacts/generics/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/generics/target/witness.gz b/test_programs/acir_artifacts/generics/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/generics/target/witness.gz rename to test_programs/acir_artifacts/generics/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/global_consts/target/acir.gz b/test_programs/acir_artifacts/global_consts/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/global_consts/target/acir.gz rename to test_programs/acir_artifacts/global_consts/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/global_consts/target/witness.gz b/test_programs/acir_artifacts/global_consts/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/global_consts/target/witness.gz rename to test_programs/acir_artifacts/global_consts/target/witness.gz diff --git a/test_programs/acir_artifacts/hash_to_field/target/acir.gz b/test_programs/acir_artifacts/hash_to_field/target/acir.gz new file mode 100644 index 00000000000..9be98aef491 Binary files /dev/null and b/test_programs/acir_artifacts/hash_to_field/target/acir.gz differ diff --git a/test_programs/acir_artifacts/hash_to_field/target/witness.gz b/test_programs/acir_artifacts/hash_to_field/target/witness.gz new file mode 100644 index 00000000000..743d797096b Binary files /dev/null and b/test_programs/acir_artifacts/hash_to_field/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/higher_order_functions/target/acir.gz b/test_programs/acir_artifacts/higher_order_functions/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/higher_order_functions/target/acir.gz rename to test_programs/acir_artifacts/higher_order_functions/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/higher_order_functions/target/witness.gz b/test_programs/acir_artifacts/higher_order_functions/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/higher_order_functions/target/witness.gz rename to test_programs/acir_artifacts/higher_order_functions/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/if_else_chain/target/acir.gz b/test_programs/acir_artifacts/if_else_chain/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/if_else_chain/target/acir.gz rename to test_programs/acir_artifacts/if_else_chain/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/if_else_chain/target/witness.gz b/test_programs/acir_artifacts/if_else_chain/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/if_else_chain/target/witness.gz rename to test_programs/acir_artifacts/if_else_chain/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/import/target/acir.gz b/test_programs/acir_artifacts/import/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/import/target/acir.gz rename to test_programs/acir_artifacts/import/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/import/target/witness.gz b/test_programs/acir_artifacts/import/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/import/target/witness.gz rename to test_programs/acir_artifacts/import/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/integer_array_indexing/target/acir.gz b/test_programs/acir_artifacts/integer_array_indexing/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/integer_array_indexing/target/acir.gz rename to test_programs/acir_artifacts/integer_array_indexing/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/integer_array_indexing/target/witness.gz b/test_programs/acir_artifacts/integer_array_indexing/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/integer_array_indexing/target/witness.gz rename to test_programs/acir_artifacts/integer_array_indexing/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/keccak256/target/acir.gz b/test_programs/acir_artifacts/keccak256/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/keccak256/target/acir.gz rename to test_programs/acir_artifacts/keccak256/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/keccak256/target/witness.gz b/test_programs/acir_artifacts/keccak256/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/keccak256/target/witness.gz rename to test_programs/acir_artifacts/keccak256/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/main_bool_arg/target/acir.gz b/test_programs/acir_artifacts/main_bool_arg/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/main_bool_arg/target/acir.gz rename to test_programs/acir_artifacts/main_bool_arg/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/main_bool_arg/target/witness.gz b/test_programs/acir_artifacts/main_bool_arg/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/main_bool_arg/target/witness.gz rename to test_programs/acir_artifacts/main_bool_arg/target/witness.gz diff --git a/test_programs/acir_artifacts/merkle_insert/target/acir.gz b/test_programs/acir_artifacts/merkle_insert/target/acir.gz new file mode 100644 index 00000000000..5468d947456 Binary files /dev/null and b/test_programs/acir_artifacts/merkle_insert/target/acir.gz differ diff --git a/test_programs/acir_artifacts/merkle_insert/target/witness.gz b/test_programs/acir_artifacts/merkle_insert/target/witness.gz new file mode 100644 index 00000000000..6351d29dd11 Binary files /dev/null and b/test_programs/acir_artifacts/merkle_insert/target/witness.gz differ diff --git a/test_programs/acir_artifacts/mock_oracle/target/acir.gz b/test_programs/acir_artifacts/mock_oracle/target/acir.gz new file mode 100644 index 00000000000..618e9cc8ad1 Binary files /dev/null and b/test_programs/acir_artifacts/mock_oracle/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/mock_oracle/target/witness.gz b/test_programs/acir_artifacts/mock_oracle/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/mock_oracle/target/witness.gz rename to test_programs/acir_artifacts/mock_oracle/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/modules/target/acir.gz b/test_programs/acir_artifacts/modules/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/modules/target/acir.gz rename to test_programs/acir_artifacts/modules/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/modules/target/witness.gz b/test_programs/acir_artifacts/modules/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/modules/target/witness.gz rename to test_programs/acir_artifacts/modules/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/modules_more/target/acir.gz b/test_programs/acir_artifacts/modules_more/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/modules_more/target/acir.gz rename to test_programs/acir_artifacts/modules_more/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/modules_more/target/witness.gz b/test_programs/acir_artifacts/modules_more/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/modules_more/target/witness.gz rename to test_programs/acir_artifacts/modules_more/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/modulus/target/acir.gz b/test_programs/acir_artifacts/modulus/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/modulus/target/acir.gz rename to test_programs/acir_artifacts/modulus/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/modulus/target/witness.gz b/test_programs/acir_artifacts/modulus/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/modulus/target/witness.gz rename to test_programs/acir_artifacts/modulus/target/witness.gz diff --git a/test_programs/acir_artifacts/nested_array_dynamic/target/acir.gz b/test_programs/acir_artifacts/nested_array_dynamic/target/acir.gz new file mode 100644 index 00000000000..762ace26416 Binary files /dev/null and b/test_programs/acir_artifacts/nested_array_dynamic/target/acir.gz differ diff --git a/test_programs/acir_artifacts/nested_array_dynamic/target/witness.gz b/test_programs/acir_artifacts/nested_array_dynamic/target/witness.gz new file mode 100644 index 00000000000..e469a0ee7a7 Binary files /dev/null and b/test_programs/acir_artifacts/nested_array_dynamic/target/witness.gz differ diff --git a/test_programs/acir_artifacts/nested_arrays_from_brillig/target/acir.gz b/test_programs/acir_artifacts/nested_arrays_from_brillig/target/acir.gz new file mode 100644 index 00000000000..1fba277a1f8 Binary files /dev/null and b/test_programs/acir_artifacts/nested_arrays_from_brillig/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/nested_arrays_from_brillig/target/witness.gz b/test_programs/acir_artifacts/nested_arrays_from_brillig/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/nested_arrays_from_brillig/target/witness.gz rename to test_programs/acir_artifacts/nested_arrays_from_brillig/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/nested_slice_dynamic/target/acir.gz b/test_programs/acir_artifacts/nested_slice_dynamic/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/nested_slice_dynamic/target/acir.gz rename to test_programs/acir_artifacts/nested_slice_dynamic/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/nested_slice_dynamic/target/witness.gz b/test_programs/acir_artifacts/nested_slice_dynamic/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/nested_slice_dynamic/target/witness.gz rename to test_programs/acir_artifacts/nested_slice_dynamic/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/pedersen_check/target/acir.gz b/test_programs/acir_artifacts/pedersen_check/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/pedersen_check/target/acir.gz rename to test_programs/acir_artifacts/pedersen_check/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/pedersen_check/target/witness.gz b/test_programs/acir_artifacts/pedersen_check/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/pedersen_check/target/witness.gz rename to test_programs/acir_artifacts/pedersen_check/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/poseidon_bn254_hash/target/acir.gz b/test_programs/acir_artifacts/poseidon_bn254_hash/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/poseidon_bn254_hash/target/acir.gz rename to test_programs/acir_artifacts/poseidon_bn254_hash/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/poseidon_bn254_hash/target/witness.gz b/test_programs/acir_artifacts/poseidon_bn254_hash/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/poseidon_bn254_hash/target/witness.gz rename to test_programs/acir_artifacts/poseidon_bn254_hash/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/poseidonsponge_x5_254/target/acir.gz b/test_programs/acir_artifacts/poseidonsponge_x5_254/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/poseidonsponge_x5_254/target/acir.gz rename to test_programs/acir_artifacts/poseidonsponge_x5_254/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/poseidonsponge_x5_254/target/witness.gz b/test_programs/acir_artifacts/poseidonsponge_x5_254/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/poseidonsponge_x5_254/target/witness.gz rename to test_programs/acir_artifacts/poseidonsponge_x5_254/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/pred_eq/target/acir.gz b/test_programs/acir_artifacts/pred_eq/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/pred_eq/target/acir.gz rename to test_programs/acir_artifacts/pred_eq/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/pred_eq/target/witness.gz b/test_programs/acir_artifacts/pred_eq/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/pred_eq/target/witness.gz rename to test_programs/acir_artifacts/pred_eq/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/references/target/acir.gz b/test_programs/acir_artifacts/references/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/references/target/acir.gz rename to test_programs/acir_artifacts/references/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/references/target/witness.gz b/test_programs/acir_artifacts/references/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/references/target/witness.gz rename to test_programs/acir_artifacts/references/target/witness.gz diff --git a/test_programs/acir_artifacts/regression/target/acir.gz b/test_programs/acir_artifacts/regression/target/acir.gz new file mode 100644 index 00000000000..f92aa2603b4 Binary files /dev/null and b/test_programs/acir_artifacts/regression/target/acir.gz differ diff --git a/test_programs/acir_artifacts/regression/target/witness.gz b/test_programs/acir_artifacts/regression/target/witness.gz new file mode 100644 index 00000000000..dc1f8e0e3f7 Binary files /dev/null and b/test_programs/acir_artifacts/regression/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/regression_2854/target/acir.gz b/test_programs/acir_artifacts/regression_2854/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/regression_2854/target/acir.gz rename to test_programs/acir_artifacts/regression_2854/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/regression_2854/target/witness.gz b/test_programs/acir_artifacts/regression_2854/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/regression_2854/target/witness.gz rename to test_programs/acir_artifacts/regression_2854/target/witness.gz diff --git a/test_programs/acir_artifacts/regression_mem_op_predicate/target/acir.gz b/test_programs/acir_artifacts/regression_mem_op_predicate/target/acir.gz new file mode 100644 index 00000000000..a408771e244 Binary files /dev/null and b/test_programs/acir_artifacts/regression_mem_op_predicate/target/acir.gz differ diff --git a/test_programs/acir_artifacts/regression_mem_op_predicate/target/witness.gz b/test_programs/acir_artifacts/regression_mem_op_predicate/target/witness.gz new file mode 100644 index 00000000000..2d0e8a4f685 Binary files /dev/null and b/test_programs/acir_artifacts/regression_mem_op_predicate/target/witness.gz differ diff --git a/test_programs/acir_artifacts/regression_method_cannot_be_found/target/acir.gz b/test_programs/acir_artifacts/regression_method_cannot_be_found/target/acir.gz new file mode 100644 index 00000000000..dd4d7ea5f45 Binary files /dev/null and b/test_programs/acir_artifacts/regression_method_cannot_be_found/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/regression_method_cannot_be_found/target/witness.gz b/test_programs/acir_artifacts/regression_method_cannot_be_found/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/regression_method_cannot_be_found/target/witness.gz rename to test_programs/acir_artifacts/regression_method_cannot_be_found/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/scalar_mul/target/acir.gz b/test_programs/acir_artifacts/scalar_mul/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/scalar_mul/target/acir.gz rename to test_programs/acir_artifacts/scalar_mul/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/scalar_mul/target/witness.gz b/test_programs/acir_artifacts/scalar_mul/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/scalar_mul/target/witness.gz rename to test_programs/acir_artifacts/scalar_mul/target/witness.gz diff --git a/test_programs/acir_artifacts/schnorr/target/acir.gz b/test_programs/acir_artifacts/schnorr/target/acir.gz new file mode 100644 index 00000000000..047e59422ee Binary files /dev/null and b/test_programs/acir_artifacts/schnorr/target/acir.gz differ diff --git a/test_programs/acir_artifacts/schnorr/target/witness.gz b/test_programs/acir_artifacts/schnorr/target/witness.gz new file mode 100644 index 00000000000..91bf1aeb7ad Binary files /dev/null and b/test_programs/acir_artifacts/schnorr/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/sha256/target/acir.gz b/test_programs/acir_artifacts/sha256/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/sha256/target/acir.gz rename to test_programs/acir_artifacts/sha256/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/sha256/target/witness.gz b/test_programs/acir_artifacts/sha256/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/sha256/target/witness.gz rename to test_programs/acir_artifacts/sha256/target/witness.gz diff --git a/test_programs/acir_artifacts/sha2_byte/target/acir.gz b/test_programs/acir_artifacts/sha2_byte/target/acir.gz new file mode 100644 index 00000000000..571fde25f2b Binary files /dev/null and b/test_programs/acir_artifacts/sha2_byte/target/acir.gz differ diff --git a/test_programs/acir_artifacts/sha2_byte/target/witness.gz b/test_programs/acir_artifacts/sha2_byte/target/witness.gz new file mode 100644 index 00000000000..08ca373be7e Binary files /dev/null and b/test_programs/acir_artifacts/sha2_byte/target/witness.gz differ diff --git a/test_programs/acir_artifacts/signed_arithmetic/target/acir.gz b/test_programs/acir_artifacts/signed_arithmetic/target/acir.gz new file mode 100644 index 00000000000..82747c17417 Binary files /dev/null and b/test_programs/acir_artifacts/signed_arithmetic/target/acir.gz differ diff --git a/test_programs/acir_artifacts/signed_arithmetic/target/witness.gz b/test_programs/acir_artifacts/signed_arithmetic/target/witness.gz new file mode 100644 index 00000000000..6627fd7d53f Binary files /dev/null and b/test_programs/acir_artifacts/signed_arithmetic/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/signed_division/target/acir.gz b/test_programs/acir_artifacts/signed_division/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/signed_division/target/acir.gz rename to test_programs/acir_artifacts/signed_division/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/signed_division/target/witness.gz b/test_programs/acir_artifacts/signed_division/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/signed_division/target/witness.gz rename to test_programs/acir_artifacts/signed_division/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/simple_2d_array/target/acir.gz b/test_programs/acir_artifacts/simple_2d_array/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/simple_2d_array/target/acir.gz rename to test_programs/acir_artifacts/simple_2d_array/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/simple_2d_array/target/witness.gz b/test_programs/acir_artifacts/simple_2d_array/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/simple_2d_array/target/witness.gz rename to test_programs/acir_artifacts/simple_2d_array/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/simple_add_and_ret_arr/target/acir.gz b/test_programs/acir_artifacts/simple_add_and_ret_arr/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/simple_add_and_ret_arr/target/acir.gz rename to test_programs/acir_artifacts/simple_add_and_ret_arr/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/simple_add_and_ret_arr/target/witness.gz b/test_programs/acir_artifacts/simple_add_and_ret_arr/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/simple_add_and_ret_arr/target/witness.gz rename to test_programs/acir_artifacts/simple_add_and_ret_arr/target/witness.gz diff --git a/test_programs/acir_artifacts/simple_bitwise/target/acir.gz b/test_programs/acir_artifacts/simple_bitwise/target/acir.gz new file mode 100644 index 00000000000..84fc5cc5de2 Binary files /dev/null and b/test_programs/acir_artifacts/simple_bitwise/target/acir.gz differ diff --git a/test_programs/acir_artifacts/simple_bitwise/target/witness.gz b/test_programs/acir_artifacts/simple_bitwise/target/witness.gz new file mode 100644 index 00000000000..2afa317a120 Binary files /dev/null and b/test_programs/acir_artifacts/simple_bitwise/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/simple_comparison/target/acir.gz b/test_programs/acir_artifacts/simple_comparison/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/simple_comparison/target/acir.gz rename to test_programs/acir_artifacts/simple_comparison/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/simple_comparison/target/witness.gz b/test_programs/acir_artifacts/simple_comparison/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/simple_comparison/target/witness.gz rename to test_programs/acir_artifacts/simple_comparison/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/simple_mut/target/acir.gz b/test_programs/acir_artifacts/simple_mut/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/simple_mut/target/acir.gz rename to test_programs/acir_artifacts/simple_mut/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/simple_mut/target/witness.gz b/test_programs/acir_artifacts/simple_mut/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/simple_mut/target/witness.gz rename to test_programs/acir_artifacts/simple_mut/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/simple_not/target/acir.gz b/test_programs/acir_artifacts/simple_not/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/simple_not/target/acir.gz rename to test_programs/acir_artifacts/simple_not/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/simple_not/target/witness.gz b/test_programs/acir_artifacts/simple_not/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/simple_not/target/witness.gz rename to test_programs/acir_artifacts/simple_not/target/witness.gz diff --git a/test_programs/acir_artifacts/simple_print/target/acir.gz b/test_programs/acir_artifacts/simple_print/target/acir.gz new file mode 100644 index 00000000000..9640753adfe Binary files /dev/null and b/test_programs/acir_artifacts/simple_print/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/simple_print/target/witness.gz b/test_programs/acir_artifacts/simple_print/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/simple_print/target/witness.gz rename to test_programs/acir_artifacts/simple_print/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/simple_program_addition/target/acir.gz b/test_programs/acir_artifacts/simple_program_addition/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/simple_program_addition/target/acir.gz rename to test_programs/acir_artifacts/simple_program_addition/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/simple_program_addition/target/witness.gz b/test_programs/acir_artifacts/simple_program_addition/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/simple_program_addition/target/witness.gz rename to test_programs/acir_artifacts/simple_program_addition/target/witness.gz diff --git a/test_programs/acir_artifacts/simple_radix/target/acir.gz b/test_programs/acir_artifacts/simple_radix/target/acir.gz new file mode 100644 index 00000000000..8c5fd3c0bd4 Binary files /dev/null and b/test_programs/acir_artifacts/simple_radix/target/acir.gz differ diff --git a/test_programs/acir_artifacts/simple_radix/target/witness.gz b/test_programs/acir_artifacts/simple_radix/target/witness.gz new file mode 100644 index 00000000000..4b051d62ee2 Binary files /dev/null and b/test_programs/acir_artifacts/simple_radix/target/witness.gz differ diff --git a/test_programs/acir_artifacts/simple_shield/target/acir.gz b/test_programs/acir_artifacts/simple_shield/target/acir.gz new file mode 100644 index 00000000000..1916c475919 Binary files /dev/null and b/test_programs/acir_artifacts/simple_shield/target/acir.gz differ diff --git a/test_programs/acir_artifacts/simple_shield/target/witness.gz b/test_programs/acir_artifacts/simple_shield/target/witness.gz new file mode 100644 index 00000000000..171330f5142 Binary files /dev/null and b/test_programs/acir_artifacts/simple_shield/target/witness.gz differ diff --git a/test_programs/acir_artifacts/simple_shift_left_right/target/acir.gz b/test_programs/acir_artifacts/simple_shift_left_right/target/acir.gz new file mode 100644 index 00000000000..bae747f46c6 Binary files /dev/null and b/test_programs/acir_artifacts/simple_shift_left_right/target/acir.gz differ diff --git a/test_programs/acir_artifacts/simple_shift_left_right/target/witness.gz b/test_programs/acir_artifacts/simple_shift_left_right/target/witness.gz new file mode 100644 index 00000000000..6bc0b91e147 Binary files /dev/null and b/test_programs/acir_artifacts/simple_shift_left_right/target/witness.gz differ diff --git a/test_programs/acir_artifacts/slice_dynamic_index/target/acir.gz b/test_programs/acir_artifacts/slice_dynamic_index/target/acir.gz new file mode 100644 index 00000000000..1bbc8ea075c Binary files /dev/null and b/test_programs/acir_artifacts/slice_dynamic_index/target/acir.gz differ diff --git a/test_programs/acir_artifacts/slice_dynamic_index/target/witness.gz b/test_programs/acir_artifacts/slice_dynamic_index/target/witness.gz new file mode 100644 index 00000000000..8c7e5f4fb95 Binary files /dev/null and b/test_programs/acir_artifacts/slice_dynamic_index/target/witness.gz differ diff --git a/test_programs/acir_artifacts/slice_struct_field/target/acir.gz b/test_programs/acir_artifacts/slice_struct_field/target/acir.gz new file mode 100644 index 00000000000..6b1f189a331 Binary files /dev/null and b/test_programs/acir_artifacts/slice_struct_field/target/acir.gz differ diff --git a/test_programs/acir_artifacts/slice_struct_field/target/witness.gz b/test_programs/acir_artifacts/slice_struct_field/target/witness.gz new file mode 100644 index 00000000000..f404e58ade3 Binary files /dev/null and b/test_programs/acir_artifacts/slice_struct_field/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/slices/target/acir.gz b/test_programs/acir_artifacts/slices/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/slices/target/acir.gz rename to test_programs/acir_artifacts/slices/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/slices/target/witness.gz b/test_programs/acir_artifacts/slices/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/slices/target/witness.gz rename to test_programs/acir_artifacts/slices/target/witness.gz diff --git a/test_programs/acir_artifacts/strings/target/acir.gz b/test_programs/acir_artifacts/strings/target/acir.gz new file mode 100644 index 00000000000..c489121a9b1 Binary files /dev/null and b/test_programs/acir_artifacts/strings/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/strings/target/witness.gz b/test_programs/acir_artifacts/strings/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/strings/target/witness.gz rename to test_programs/acir_artifacts/strings/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/struct/target/acir.gz b/test_programs/acir_artifacts/struct/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/struct/target/acir.gz rename to test_programs/acir_artifacts/struct/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/struct/target/witness.gz b/test_programs/acir_artifacts/struct/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/struct/target/witness.gz rename to test_programs/acir_artifacts/struct/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/struct_array_inputs/target/acir.gz b/test_programs/acir_artifacts/struct_array_inputs/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/struct_array_inputs/target/acir.gz rename to test_programs/acir_artifacts/struct_array_inputs/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/struct_array_inputs/target/witness.gz b/test_programs/acir_artifacts/struct_array_inputs/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/struct_array_inputs/target/witness.gz rename to test_programs/acir_artifacts/struct_array_inputs/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/struct_fields_ordering/target/acir.gz b/test_programs/acir_artifacts/struct_fields_ordering/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/struct_fields_ordering/target/acir.gz rename to test_programs/acir_artifacts/struct_fields_ordering/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/struct_fields_ordering/target/witness.gz b/test_programs/acir_artifacts/struct_fields_ordering/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/struct_fields_ordering/target/witness.gz rename to test_programs/acir_artifacts/struct_fields_ordering/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/struct_inputs/target/acir.gz b/test_programs/acir_artifacts/struct_inputs/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/struct_inputs/target/acir.gz rename to test_programs/acir_artifacts/struct_inputs/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/struct_inputs/target/witness.gz b/test_programs/acir_artifacts/struct_inputs/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/struct_inputs/target/witness.gz rename to test_programs/acir_artifacts/struct_inputs/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/submodules/target/acir.gz b/test_programs/acir_artifacts/submodules/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/submodules/target/acir.gz rename to test_programs/acir_artifacts/submodules/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/submodules/target/witness.gz b/test_programs/acir_artifacts/submodules/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/submodules/target/witness.gz rename to test_programs/acir_artifacts/submodules/target/witness.gz diff --git a/test_programs/acir_artifacts/to_be_bytes/target/acir.gz b/test_programs/acir_artifacts/to_be_bytes/target/acir.gz new file mode 100644 index 00000000000..df6294bc970 Binary files /dev/null and b/test_programs/acir_artifacts/to_be_bytes/target/acir.gz differ diff --git a/test_programs/acir_artifacts/to_be_bytes/target/witness.gz b/test_programs/acir_artifacts/to_be_bytes/target/witness.gz new file mode 100644 index 00000000000..b2ac9601bae Binary files /dev/null and b/test_programs/acir_artifacts/to_be_bytes/target/witness.gz differ diff --git a/test_programs/acir_artifacts/to_bytes_consistent/target/acir.gz b/test_programs/acir_artifacts/to_bytes_consistent/target/acir.gz new file mode 100644 index 00000000000..2371186e8fc Binary files /dev/null and b/test_programs/acir_artifacts/to_bytes_consistent/target/acir.gz differ diff --git a/test_programs/acir_artifacts/to_bytes_consistent/target/witness.gz b/test_programs/acir_artifacts/to_bytes_consistent/target/witness.gz new file mode 100644 index 00000000000..610802628c6 Binary files /dev/null and b/test_programs/acir_artifacts/to_bytes_consistent/target/witness.gz differ diff --git a/test_programs/acir_artifacts/to_bytes_integration/target/acir.gz b/test_programs/acir_artifacts/to_bytes_integration/target/acir.gz new file mode 100644 index 00000000000..4deef489b9c Binary files /dev/null and b/test_programs/acir_artifacts/to_bytes_integration/target/acir.gz differ diff --git a/test_programs/acir_artifacts/to_bytes_integration/target/witness.gz b/test_programs/acir_artifacts/to_bytes_integration/target/witness.gz new file mode 100644 index 00000000000..71d29209eba Binary files /dev/null and b/test_programs/acir_artifacts/to_bytes_integration/target/witness.gz differ diff --git a/test_programs/acir_artifacts/to_le_bytes/target/acir.gz b/test_programs/acir_artifacts/to_le_bytes/target/acir.gz new file mode 100644 index 00000000000..02d2bd105f1 Binary files /dev/null and b/test_programs/acir_artifacts/to_le_bytes/target/acir.gz differ diff --git a/test_programs/acir_artifacts/to_le_bytes/target/witness.gz b/test_programs/acir_artifacts/to_le_bytes/target/witness.gz new file mode 100644 index 00000000000..610802628c6 Binary files /dev/null and b/test_programs/acir_artifacts/to_le_bytes/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/trait_as_return_type/target/acir.gz b/test_programs/acir_artifacts/trait_as_return_type/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/trait_as_return_type/target/acir.gz rename to test_programs/acir_artifacts/trait_as_return_type/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/trait_as_return_type/target/witness.gz b/test_programs/acir_artifacts/trait_as_return_type/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/trait_as_return_type/target/witness.gz rename to test_programs/acir_artifacts/trait_as_return_type/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/trait_impl_base_type/target/acir.gz b/test_programs/acir_artifacts/trait_impl_base_type/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/trait_impl_base_type/target/acir.gz rename to test_programs/acir_artifacts/trait_impl_base_type/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/trait_impl_base_type/target/witness.gz b/test_programs/acir_artifacts/trait_impl_base_type/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/trait_impl_base_type/target/witness.gz rename to test_programs/acir_artifacts/trait_impl_base_type/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/traits_in_crates_1/target/acir.gz b/test_programs/acir_artifacts/traits_in_crates_1/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/traits_in_crates_1/target/acir.gz rename to test_programs/acir_artifacts/traits_in_crates_1/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/traits_in_crates_1/target/witness.gz b/test_programs/acir_artifacts/traits_in_crates_1/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/traits_in_crates_1/target/witness.gz rename to test_programs/acir_artifacts/traits_in_crates_1/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/traits_in_crates_2/target/acir.gz b/test_programs/acir_artifacts/traits_in_crates_2/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/traits_in_crates_2/target/acir.gz rename to test_programs/acir_artifacts/traits_in_crates_2/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/traits_in_crates_2/target/witness.gz b/test_programs/acir_artifacts/traits_in_crates_2/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/traits_in_crates_2/target/witness.gz rename to test_programs/acir_artifacts/traits_in_crates_2/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/tuple_inputs/target/acir.gz b/test_programs/acir_artifacts/tuple_inputs/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/tuple_inputs/target/acir.gz rename to test_programs/acir_artifacts/tuple_inputs/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/tuple_inputs/target/witness.gz b/test_programs/acir_artifacts/tuple_inputs/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/tuple_inputs/target/witness.gz rename to test_programs/acir_artifacts/tuple_inputs/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/tuples/target/acir.gz b/test_programs/acir_artifacts/tuples/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/tuples/target/acir.gz rename to test_programs/acir_artifacts/tuples/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/tuples/target/witness.gz b/test_programs/acir_artifacts/tuples/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/tuples/target/witness.gz rename to test_programs/acir_artifacts/tuples/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/type_aliases/target/acir.gz b/test_programs/acir_artifacts/type_aliases/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/type_aliases/target/acir.gz rename to test_programs/acir_artifacts/type_aliases/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/type_aliases/target/witness.gz b/test_programs/acir_artifacts/type_aliases/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/type_aliases/target/witness.gz rename to test_programs/acir_artifacts/type_aliases/target/witness.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/xor/target/acir.gz b/test_programs/acir_artifacts/xor/target/acir.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/xor/target/acir.gz rename to test_programs/acir_artifacts/xor/target/acir.gz diff --git a/tooling/nargo_cli/tests/acir_artifacts/xor/target/witness.gz b/test_programs/acir_artifacts/xor/target/witness.gz similarity index 100% rename from tooling/nargo_cli/tests/acir_artifacts/xor/target/witness.gz rename to test_programs/acir_artifacts/xor/target/witness.gz diff --git a/tooling/nargo_cli/tests/compile_failure/assert_constant_fail/Nargo.toml b/test_programs/compile_failure/assert_constant_fail/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/assert_constant_fail/Nargo.toml rename to test_programs/compile_failure/assert_constant_fail/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_failure/assert_constant_fail/src/main.nr b/test_programs/compile_failure/assert_constant_fail/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/assert_constant_fail/src/main.nr rename to test_programs/compile_failure/assert_constant_fail/src/main.nr diff --git a/tooling/nargo_cli/tests/compile_failure/assert_eq_struct/Nargo.toml b/test_programs/compile_failure/assert_eq_struct/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/assert_eq_struct/Nargo.toml rename to test_programs/compile_failure/assert_eq_struct/Nargo.toml diff --git a/test_programs/compile_failure/assert_eq_struct/src/main.nr b/test_programs/compile_failure/assert_eq_struct/src/main.nr new file mode 100644 index 00000000000..54b937e5f1d --- /dev/null +++ b/test_programs/compile_failure/assert_eq_struct/src/main.nr @@ -0,0 +1,5 @@ +struct myStruct {} +// `assert_eq` should not allow asserting equality between types for which `==` is not defined. +fn main(x: myStruct, y: pub myStruct) { + assert_eq(x, y); +} diff --git a/tooling/nargo_cli/tests/compile_failure/brillig_assert_fail/Nargo.toml b/test_programs/compile_failure/brillig_assert_fail/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/brillig_assert_fail/Nargo.toml rename to test_programs/compile_failure/brillig_assert_fail/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_failure/brillig_assert_fail/Prover.toml b/test_programs/compile_failure/brillig_assert_fail/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/brillig_assert_fail/Prover.toml rename to test_programs/compile_failure/brillig_assert_fail/Prover.toml diff --git a/test_programs/compile_failure/brillig_assert_fail/src/main.nr b/test_programs/compile_failure/brillig_assert_fail/src/main.nr new file mode 100644 index 00000000000..da9d4ec1ac8 --- /dev/null +++ b/test_programs/compile_failure/brillig_assert_fail/src/main.nr @@ -0,0 +1,11 @@ +// Tests a very simple program. +// +// The features being tested is using assert on brillig +fn main(x: Field) { + assert(1 == conditional(x as bool)); +} + +unconstrained fn conditional(x: bool) -> Field { + assert(x); + 1 +} diff --git a/tooling/nargo_cli/tests/compile_failure/constrain_typo/Nargo.toml b/test_programs/compile_failure/constrain_typo/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/constrain_typo/Nargo.toml rename to test_programs/compile_failure/constrain_typo/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_failure/constrain_typo/src/main.nr b/test_programs/compile_failure/constrain_typo/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/constrain_typo/src/main.nr rename to test_programs/compile_failure/constrain_typo/src/main.nr diff --git a/tooling/nargo_cli/tests/compile_failure/custom_entry_not_found/Nargo.toml b/test_programs/compile_failure/custom_entry_not_found/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/custom_entry_not_found/Nargo.toml rename to test_programs/compile_failure/custom_entry_not_found/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_failure/custom_entry_not_found/Prover.toml b/test_programs/compile_failure/custom_entry_not_found/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/custom_entry_not_found/Prover.toml rename to test_programs/compile_failure/custom_entry_not_found/Prover.toml diff --git a/tooling/nargo_cli/tests/compile_failure/custom_entry_not_found/src/main.nr b/test_programs/compile_failure/custom_entry_not_found/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/custom_entry_not_found/src/main.nr rename to test_programs/compile_failure/custom_entry_not_found/src/main.nr diff --git a/tooling/nargo_cli/tests/compile_failure/dep_impl_primitive/Nargo.toml b/test_programs/compile_failure/dep_impl_primitive/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/dep_impl_primitive/Nargo.toml rename to test_programs/compile_failure/dep_impl_primitive/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_failure/dep_impl_primitive/Prover.toml b/test_programs/compile_failure/dep_impl_primitive/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/dep_impl_primitive/Prover.toml rename to test_programs/compile_failure/dep_impl_primitive/Prover.toml diff --git a/test_programs/compile_failure/dep_impl_primitive/src/main.nr b/test_programs/compile_failure/dep_impl_primitive/src/main.nr new file mode 100644 index 00000000000..e61ae82b62c --- /dev/null +++ b/test_programs/compile_failure/dep_impl_primitive/src/main.nr @@ -0,0 +1,5 @@ +use dep::bad_impl; + +fn main(x: Field) { + x.something(); +} diff --git a/tooling/nargo_cli/tests/compile_failure/depend_on_bin/Nargo.toml b/test_programs/compile_failure/depend_on_bin/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/depend_on_bin/Nargo.toml rename to test_programs/compile_failure/depend_on_bin/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_failure/depend_on_bin/Prover.toml b/test_programs/compile_failure/depend_on_bin/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/depend_on_bin/Prover.toml rename to test_programs/compile_failure/depend_on_bin/Prover.toml diff --git a/tooling/nargo_cli/tests/compile_failure/depend_on_bin/src/main.nr b/test_programs/compile_failure/depend_on_bin/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/depend_on_bin/src/main.nr rename to test_programs/compile_failure/depend_on_bin/src/main.nr diff --git a/tooling/nargo_cli/tests/compile_failure/div_by_zero_constants/Nargo.toml b/test_programs/compile_failure/div_by_zero_constants/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/div_by_zero_constants/Nargo.toml rename to test_programs/compile_failure/div_by_zero_constants/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_failure/div_by_zero_constants/Prover.toml b/test_programs/compile_failure/div_by_zero_constants/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/div_by_zero_constants/Prover.toml rename to test_programs/compile_failure/div_by_zero_constants/Prover.toml diff --git a/tooling/nargo_cli/tests/compile_failure/div_by_zero_constants/src/main.nr b/test_programs/compile_failure/div_by_zero_constants/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/div_by_zero_constants/src/main.nr rename to test_programs/compile_failure/div_by_zero_constants/src/main.nr diff --git a/tooling/nargo_cli/tests/compile_failure/div_by_zero_modulo/Nargo.toml b/test_programs/compile_failure/div_by_zero_modulo/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/div_by_zero_modulo/Nargo.toml rename to test_programs/compile_failure/div_by_zero_modulo/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_failure/div_by_zero_modulo/Prover.toml b/test_programs/compile_failure/div_by_zero_modulo/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/div_by_zero_modulo/Prover.toml rename to test_programs/compile_failure/div_by_zero_modulo/Prover.toml diff --git a/tooling/nargo_cli/tests/compile_failure/div_by_zero_modulo/src/main.nr b/test_programs/compile_failure/div_by_zero_modulo/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/div_by_zero_modulo/src/main.nr rename to test_programs/compile_failure/div_by_zero_modulo/src/main.nr diff --git a/tooling/nargo_cli/tests/compile_failure/div_by_zero_numerator_witness/Nargo.toml b/test_programs/compile_failure/div_by_zero_numerator_witness/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/div_by_zero_numerator_witness/Nargo.toml rename to test_programs/compile_failure/div_by_zero_numerator_witness/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_failure/div_by_zero_numerator_witness/Prover.toml b/test_programs/compile_failure/div_by_zero_numerator_witness/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/div_by_zero_numerator_witness/Prover.toml rename to test_programs/compile_failure/div_by_zero_numerator_witness/Prover.toml diff --git a/tooling/nargo_cli/tests/compile_failure/div_by_zero_numerator_witness/src/main.nr b/test_programs/compile_failure/div_by_zero_numerator_witness/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/div_by_zero_numerator_witness/src/main.nr rename to test_programs/compile_failure/div_by_zero_numerator_witness/src/main.nr diff --git a/tooling/nargo_cli/tests/compile_failure/div_by_zero_witness/Nargo.toml b/test_programs/compile_failure/div_by_zero_witness/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/div_by_zero_witness/Nargo.toml rename to test_programs/compile_failure/div_by_zero_witness/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_failure/div_by_zero_witness/Prover.toml b/test_programs/compile_failure/div_by_zero_witness/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/div_by_zero_witness/Prover.toml rename to test_programs/compile_failure/div_by_zero_witness/Prover.toml diff --git a/test_programs/compile_failure/div_by_zero_witness/src/main.nr b/test_programs/compile_failure/div_by_zero_witness/src/main.nr new file mode 100644 index 00000000000..a814f88f320 --- /dev/null +++ b/test_programs/compile_failure/div_by_zero_witness/src/main.nr @@ -0,0 +1,6 @@ +use dep::std; +// It is expected that `y` must be equal to 0. +fn main(x: Field, y: pub Field) { + let a: Field = x / y; + std::println(a); +} diff --git a/tooling/nargo_cli/tests/compile_failure/dup_trait_implementation_4/Nargo.toml b/test_programs/compile_failure/dup_trait_implementation_4/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/dup_trait_implementation_4/Nargo.toml rename to test_programs/compile_failure/dup_trait_implementation_4/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_failure/dup_trait_implementation_4/Prover.toml b/test_programs/compile_failure/dup_trait_implementation_4/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/dup_trait_implementation_4/Prover.toml rename to test_programs/compile_failure/dup_trait_implementation_4/Prover.toml diff --git a/tooling/nargo_cli/tests/compile_failure/dup_trait_implementation_4/src/main.nr b/test_programs/compile_failure/dup_trait_implementation_4/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/dup_trait_implementation_4/src/main.nr rename to test_programs/compile_failure/dup_trait_implementation_4/src/main.nr diff --git a/tooling/nargo_cli/tests/compile_failure/dup_trait_implementation_4/src/module1.nr b/test_programs/compile_failure/dup_trait_implementation_4/src/module1.nr similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/dup_trait_implementation_4/src/module1.nr rename to test_programs/compile_failure/dup_trait_implementation_4/src/module1.nr diff --git a/tooling/nargo_cli/tests/compile_failure/dup_trait_implementation_4/src/module2.nr b/test_programs/compile_failure/dup_trait_implementation_4/src/module2.nr similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/dup_trait_implementation_4/src/module2.nr rename to test_programs/compile_failure/dup_trait_implementation_4/src/module2.nr diff --git a/test_programs/compile_failure/dup_trait_implementation_4/src/module3.nr b/test_programs/compile_failure/dup_trait_implementation_4/src/module3.nr new file mode 100644 index 00000000000..17874893190 --- /dev/null +++ b/test_programs/compile_failure/dup_trait_implementation_4/src/module3.nr @@ -0,0 +1,6 @@ +use crate::module1::MyTrait; +use crate::module2::MyStruct; +// those are not the same 'Path', but they refer to the same trait & impl +// so a Duplicate error should be thrown +impl MyTrait for MyStruct {} +impl crate::module1::MyTrait for crate::module2::MyStruct { } diff --git a/tooling/nargo_cli/tests/compile_failure/dup_trait_implementation_5/Nargo.toml b/test_programs/compile_failure/dup_trait_implementation_5/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/dup_trait_implementation_5/Nargo.toml rename to test_programs/compile_failure/dup_trait_implementation_5/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_failure/dup_trait_implementation_5/Prover.toml b/test_programs/compile_failure/dup_trait_implementation_5/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/dup_trait_implementation_5/Prover.toml rename to test_programs/compile_failure/dup_trait_implementation_5/Prover.toml diff --git a/tooling/nargo_cli/tests/compile_failure/dup_trait_implementation_5/src/main.nr b/test_programs/compile_failure/dup_trait_implementation_5/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/dup_trait_implementation_5/src/main.nr rename to test_programs/compile_failure/dup_trait_implementation_5/src/main.nr diff --git a/tooling/nargo_cli/tests/compile_failure/dup_trait_implementation_5/src/module1.nr b/test_programs/compile_failure/dup_trait_implementation_5/src/module1.nr similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/dup_trait_implementation_5/src/module1.nr rename to test_programs/compile_failure/dup_trait_implementation_5/src/module1.nr diff --git a/tooling/nargo_cli/tests/compile_failure/dup_trait_implementation_5/src/module2.nr b/test_programs/compile_failure/dup_trait_implementation_5/src/module2.nr similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/dup_trait_implementation_5/src/module2.nr rename to test_programs/compile_failure/dup_trait_implementation_5/src/module2.nr diff --git a/tooling/nargo_cli/tests/compile_failure/dup_trait_implementation_5/src/module3.nr b/test_programs/compile_failure/dup_trait_implementation_5/src/module3.nr similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/dup_trait_implementation_5/src/module3.nr rename to test_programs/compile_failure/dup_trait_implementation_5/src/module3.nr diff --git a/tooling/nargo_cli/tests/compile_failure/dup_trait_implementation_5/src/module4.nr b/test_programs/compile_failure/dup_trait_implementation_5/src/module4.nr similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/dup_trait_implementation_5/src/module4.nr rename to test_programs/compile_failure/dup_trait_implementation_5/src/module4.nr diff --git a/tooling/nargo_cli/tests/compile_failure/dup_trait_items_1/Nargo.toml b/test_programs/compile_failure/dup_trait_items_1/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/dup_trait_items_1/Nargo.toml rename to test_programs/compile_failure/dup_trait_items_1/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_failure/dup_trait_items_1/Prover.toml b/test_programs/compile_failure/dup_trait_items_1/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/dup_trait_items_1/Prover.toml rename to test_programs/compile_failure/dup_trait_items_1/Prover.toml diff --git a/test_programs/compile_failure/dup_trait_items_1/src/main.nr b/test_programs/compile_failure/dup_trait_items_1/src/main.nr new file mode 100644 index 00000000000..863139333f7 --- /dev/null +++ b/test_programs/compile_failure/dup_trait_items_1/src/main.nr @@ -0,0 +1,6 @@ +trait MyTrait { + fn SomeFunc(); + fn SomeFunc(); +} + +fn main() {} diff --git a/tooling/nargo_cli/tests/compile_failure/dup_trait_items_2/Nargo.toml b/test_programs/compile_failure/dup_trait_items_2/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/dup_trait_items_2/Nargo.toml rename to test_programs/compile_failure/dup_trait_items_2/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_failure/dup_trait_items_2/Prover.toml b/test_programs/compile_failure/dup_trait_items_2/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/dup_trait_items_2/Prover.toml rename to test_programs/compile_failure/dup_trait_items_2/Prover.toml diff --git a/test_programs/compile_failure/dup_trait_items_2/src/main.nr b/test_programs/compile_failure/dup_trait_items_2/src/main.nr new file mode 100644 index 00000000000..cdcac745208 --- /dev/null +++ b/test_programs/compile_failure/dup_trait_items_2/src/main.nr @@ -0,0 +1,6 @@ +trait MyTrait { + let SomeConst: u32; + let SomeConst: Field; +} + +fn main() {} diff --git a/tooling/nargo_cli/tests/compile_failure/dup_trait_items_3/Nargo.toml b/test_programs/compile_failure/dup_trait_items_3/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/dup_trait_items_3/Nargo.toml rename to test_programs/compile_failure/dup_trait_items_3/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_failure/dup_trait_items_3/Prover.toml b/test_programs/compile_failure/dup_trait_items_3/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/dup_trait_items_3/Prover.toml rename to test_programs/compile_failure/dup_trait_items_3/Prover.toml diff --git a/test_programs/compile_failure/dup_trait_items_3/src/main.nr b/test_programs/compile_failure/dup_trait_items_3/src/main.nr new file mode 100644 index 00000000000..f6ed35468b9 --- /dev/null +++ b/test_programs/compile_failure/dup_trait_items_3/src/main.nr @@ -0,0 +1,6 @@ +trait MyTrait { + type SomeType; + type SomeType; +} + +fn main() {} diff --git a/tooling/nargo_cli/tests/compile_failure/dup_trait_items_4/Nargo.toml b/test_programs/compile_failure/dup_trait_items_4/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/dup_trait_items_4/Nargo.toml rename to test_programs/compile_failure/dup_trait_items_4/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_failure/dup_trait_items_4/Prover.toml b/test_programs/compile_failure/dup_trait_items_4/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/dup_trait_items_4/Prover.toml rename to test_programs/compile_failure/dup_trait_items_4/Prover.toml diff --git a/test_programs/compile_failure/dup_trait_items_4/src/main.nr b/test_programs/compile_failure/dup_trait_items_4/src/main.nr new file mode 100644 index 00000000000..fc0a7011a5d --- /dev/null +++ b/test_programs/compile_failure/dup_trait_items_4/src/main.nr @@ -0,0 +1,6 @@ +trait MyTrait { + let MyItem: u32; + fn MyItem(); +} + +fn main() {} diff --git a/tooling/nargo_cli/tests/compile_failure/dup_trait_items_5/Nargo.toml b/test_programs/compile_failure/dup_trait_items_5/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/dup_trait_items_5/Nargo.toml rename to test_programs/compile_failure/dup_trait_items_5/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_failure/dup_trait_items_5/Prover.toml b/test_programs/compile_failure/dup_trait_items_5/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/dup_trait_items_5/Prover.toml rename to test_programs/compile_failure/dup_trait_items_5/Prover.toml diff --git a/test_programs/compile_failure/dup_trait_items_5/src/main.nr b/test_programs/compile_failure/dup_trait_items_5/src/main.nr new file mode 100644 index 00000000000..da42d27ac36 --- /dev/null +++ b/test_programs/compile_failure/dup_trait_items_5/src/main.nr @@ -0,0 +1,6 @@ +trait MyTrait { + fn MyItem(); + let MyItem: u32; +} + +fn main() {} diff --git a/tooling/nargo_cli/tests/compile_failure/dup_trait_items_6/Nargo.toml b/test_programs/compile_failure/dup_trait_items_6/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/dup_trait_items_6/Nargo.toml rename to test_programs/compile_failure/dup_trait_items_6/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_failure/dup_trait_items_6/Prover.toml b/test_programs/compile_failure/dup_trait_items_6/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/dup_trait_items_6/Prover.toml rename to test_programs/compile_failure/dup_trait_items_6/Prover.toml diff --git a/tooling/nargo_cli/tests/compile_failure/dup_trait_items_6/src/main.nr b/test_programs/compile_failure/dup_trait_items_6/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/dup_trait_items_6/src/main.nr rename to test_programs/compile_failure/dup_trait_items_6/src/main.nr diff --git a/tooling/nargo_cli/tests/compile_failure/duplicate_declaration/Nargo.toml b/test_programs/compile_failure/duplicate_declaration/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/duplicate_declaration/Nargo.toml rename to test_programs/compile_failure/duplicate_declaration/Nargo.toml diff --git a/test_programs/compile_failure/duplicate_declaration/src/main.nr b/test_programs/compile_failure/duplicate_declaration/src/main.nr new file mode 100644 index 00000000000..e4433ef4078 --- /dev/null +++ b/test_programs/compile_failure/duplicate_declaration/src/main.nr @@ -0,0 +1,8 @@ +// Duplicate functions should not compile +fn hello(x: Field) -> Field { + x +} + +fn hello(x: Field) -> Field { + x +} diff --git a/tooling/nargo_cli/tests/compile_failure/dyn_index_fail_nested_array/Nargo.toml b/test_programs/compile_failure/dyn_index_fail_nested_array/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/dyn_index_fail_nested_array/Nargo.toml rename to test_programs/compile_failure/dyn_index_fail_nested_array/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_failure/dyn_index_fail_nested_array/Prover.toml b/test_programs/compile_failure/dyn_index_fail_nested_array/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/dyn_index_fail_nested_array/Prover.toml rename to test_programs/compile_failure/dyn_index_fail_nested_array/Prover.toml diff --git a/test_programs/compile_failure/dyn_index_fail_nested_array/src/main.nr b/test_programs/compile_failure/dyn_index_fail_nested_array/src/main.nr new file mode 100644 index 00000000000..954d2e77c6e --- /dev/null +++ b/test_programs/compile_failure/dyn_index_fail_nested_array/src/main.nr @@ -0,0 +1,8 @@ +struct Foo { + a: Field, + b: Field, +} + +fn main(mut x: [Foo; 3], y: pub Field) { + assert(x[y + 2].a == 5); +} diff --git a/tooling/nargo_cli/tests/compile_failure/dynamic_index_failure/Nargo.toml b/test_programs/compile_failure/dynamic_index_failure/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/dynamic_index_failure/Nargo.toml rename to test_programs/compile_failure/dynamic_index_failure/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_failure/dynamic_index_failure/Prover.toml b/test_programs/compile_failure/dynamic_index_failure/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/dynamic_index_failure/Prover.toml rename to test_programs/compile_failure/dynamic_index_failure/Prover.toml diff --git a/test_programs/compile_failure/dynamic_index_failure/src/main.nr b/test_programs/compile_failure/dynamic_index_failure/src/main.nr new file mode 100644 index 00000000000..0af5f90eea6 --- /dev/null +++ b/test_programs/compile_failure/dynamic_index_failure/src/main.nr @@ -0,0 +1,21 @@ +fn main(mut x: [u32; 5], z: Field) { + let idx = z + 10; + + x[z] = 4; + // Dynamic index is greater than length of the array + assert(x[idx] != 0); + // TODO(#2133): Provide more accurate call stacks for arrays merged in if statements + // if z != 20 { + // x[0] = x[4]; + // } else { + // // TODO: Dynamic predicate still gives index out of bounds error + // if idx as u32 < 3 { + // x[idx] = 10; + // } + // x[idx] = 10; + // for i in 0..5 { + // x[idx] = x[i]; + // } + // } + // assert(x[idx] != 0); +} diff --git a/tooling/nargo_cli/tests/compile_failure/field_modulo/Nargo.toml b/test_programs/compile_failure/field_modulo/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/field_modulo/Nargo.toml rename to test_programs/compile_failure/field_modulo/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_failure/field_modulo/src/main.nr b/test_programs/compile_failure/field_modulo/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/field_modulo/src/main.nr rename to test_programs/compile_failure/field_modulo/src/main.nr diff --git a/tooling/nargo_cli/tests/compile_failure/integer_literal_overflow/Nargo.toml b/test_programs/compile_failure/integer_literal_overflow/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/integer_literal_overflow/Nargo.toml rename to test_programs/compile_failure/integer_literal_overflow/Nargo.toml diff --git a/test_programs/compile_failure/integer_literal_overflow/src/main.nr b/test_programs/compile_failure/integer_literal_overflow/src/main.nr new file mode 100644 index 00000000000..d89505c0085 --- /dev/null +++ b/test_programs/compile_failure/integer_literal_overflow/src/main.nr @@ -0,0 +1,5 @@ +fn main() { + foo(1234) +} + +fn foo(_x: u4) {} diff --git a/tooling/nargo_cli/tests/compile_failure/invalid_dependency_name/Nargo.toml b/test_programs/compile_failure/invalid_dependency_name/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/invalid_dependency_name/Nargo.toml rename to test_programs/compile_failure/invalid_dependency_name/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_failure/invalid_dependency_name/src/main.nr b/test_programs/compile_failure/invalid_dependency_name/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/invalid_dependency_name/src/main.nr rename to test_programs/compile_failure/invalid_dependency_name/src/main.nr diff --git a/tooling/nargo_cli/tests/compile_failure/multiple_contracts/Nargo.toml b/test_programs/compile_failure/multiple_contracts/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/multiple_contracts/Nargo.toml rename to test_programs/compile_failure/multiple_contracts/Nargo.toml diff --git a/test_programs/compile_failure/multiple_contracts/src/main.nr b/test_programs/compile_failure/multiple_contracts/src/main.nr new file mode 100644 index 00000000000..a6c49d75378 --- /dev/null +++ b/test_programs/compile_failure/multiple_contracts/src/main.nr @@ -0,0 +1,3 @@ +contract Foo {} + +contract Bar {} diff --git a/tooling/nargo_cli/tests/compile_failure/multiple_primary_attributes_fail/Nargo.toml b/test_programs/compile_failure/multiple_primary_attributes_fail/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/multiple_primary_attributes_fail/Nargo.toml rename to test_programs/compile_failure/multiple_primary_attributes_fail/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_failure/multiple_primary_attributes_fail/src/main.nr b/test_programs/compile_failure/multiple_primary_attributes_fail/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/multiple_primary_attributes_fail/src/main.nr rename to test_programs/compile_failure/multiple_primary_attributes_fail/src/main.nr diff --git a/tooling/nargo_cli/tests/compile_failure/mutability_regression_2911/Nargo.toml b/test_programs/compile_failure/mutability_regression_2911/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/mutability_regression_2911/Nargo.toml rename to test_programs/compile_failure/mutability_regression_2911/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_failure/mutability_regression_2911/src/main.nr b/test_programs/compile_failure/mutability_regression_2911/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/mutability_regression_2911/src/main.nr rename to test_programs/compile_failure/mutability_regression_2911/src/main.nr diff --git a/test_programs/compile_failure/no_impl_from_function/Nargo.toml b/test_programs/compile_failure/no_impl_from_function/Nargo.toml new file mode 100644 index 00000000000..0d243d0029d --- /dev/null +++ b/test_programs/compile_failure/no_impl_from_function/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "no_impl_from_function" +type = "bin" +authors = [""] + +[dependencies] diff --git a/test_programs/compile_failure/no_impl_from_function/src/main.nr b/test_programs/compile_failure/no_impl_from_function/src/main.nr new file mode 100644 index 00000000000..b0c485c2bf5 --- /dev/null +++ b/test_programs/compile_failure/no_impl_from_function/src/main.nr @@ -0,0 +1,26 @@ +fn main() { + let array: [Field; 3] = [1, 2, 3]; + assert(foo(array)); + + // Ensure this still works if we have to infer the type of the integer literals + let array = [1, 2, 3]; + assert(foo(array)); +} + +fn foo(x: T) -> bool where T: Eq { + x.eq(x) +} + +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 + } +} diff --git a/tooling/nargo_cli/tests/compile_failure/no_nested_impl/Nargo.toml b/test_programs/compile_failure/no_nested_impl/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/no_nested_impl/Nargo.toml rename to test_programs/compile_failure/no_nested_impl/Nargo.toml diff --git a/test_programs/compile_failure/no_nested_impl/src/main.nr b/test_programs/compile_failure/no_nested_impl/src/main.nr new file mode 100644 index 00000000000..916567a7c04 --- /dev/null +++ b/test_programs/compile_failure/no_nested_impl/src/main.nr @@ -0,0 +1,21 @@ +fn main() { + let a: [[[[Field; 2]; 2]; 2]; 2] = [[[[1, 2], [3, 4]], [[5, 6], [7, 8]]], [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]]; + assert(a.eq(a)); +} + +trait Eq { + fn eq(self, other: Self) -> bool; +} + +impl Eq for [T; 2] where T: Eq { + fn eq(self, other: Self) -> bool { + self[0].eq(other[0]) + & self[0].eq(other[0]) + } +} +// Impl for u32 but not Field +impl Eq for u32 { + fn eq(self, other: Self) -> bool { + self == other + } +} diff --git a/tooling/nargo_cli/tests/compile_failure/orphaned_trait_impl/Nargo.toml b/test_programs/compile_failure/orphaned_trait_impl/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/orphaned_trait_impl/Nargo.toml rename to test_programs/compile_failure/orphaned_trait_impl/Nargo.toml diff --git a/compiler/integration-tests/circuits/main/Prover.toml b/test_programs/compile_failure/orphaned_trait_impl/Prover.toml similarity index 100% rename from compiler/integration-tests/circuits/main/Prover.toml rename to test_programs/compile_failure/orphaned_trait_impl/Prover.toml diff --git a/tooling/nargo_cli/tests/compile_failure/orphaned_trait_impl/crate1/Nargo.toml b/test_programs/compile_failure/orphaned_trait_impl/crate1/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/orphaned_trait_impl/crate1/Nargo.toml rename to test_programs/compile_failure/orphaned_trait_impl/crate1/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_failure/orphaned_trait_impl/crate1/src/lib.nr b/test_programs/compile_failure/orphaned_trait_impl/crate1/src/lib.nr similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/orphaned_trait_impl/crate1/src/lib.nr rename to test_programs/compile_failure/orphaned_trait_impl/crate1/src/lib.nr diff --git a/tooling/nargo_cli/tests/compile_failure/orphaned_trait_impl/crate2/Nargo.toml b/test_programs/compile_failure/orphaned_trait_impl/crate2/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/orphaned_trait_impl/crate2/Nargo.toml rename to test_programs/compile_failure/orphaned_trait_impl/crate2/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_failure/orphaned_trait_impl/crate2/src/lib.nr b/test_programs/compile_failure/orphaned_trait_impl/crate2/src/lib.nr similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/orphaned_trait_impl/crate2/src/lib.nr rename to test_programs/compile_failure/orphaned_trait_impl/crate2/src/lib.nr diff --git a/test_programs/compile_failure/orphaned_trait_impl/src/main.nr b/test_programs/compile_failure/orphaned_trait_impl/src/main.nr new file mode 100644 index 00000000000..dfd88d8f074 --- /dev/null +++ b/test_programs/compile_failure/orphaned_trait_impl/src/main.nr @@ -0,0 +1,6 @@ +impl dep::crate1::MyTrait for dep::crate2::MyStruct { +} + +fn main(x: Field, y: pub Field) { + assert(x != y); +} diff --git a/tooling/nargo_cli/tests/compile_failure/overflowing_assignment/Nargo.toml b/test_programs/compile_failure/overflowing_assignment/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/overflowing_assignment/Nargo.toml rename to test_programs/compile_failure/overflowing_assignment/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_failure/overflowing_assignment/src/main.nr b/test_programs/compile_failure/overflowing_assignment/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/overflowing_assignment/src/main.nr rename to test_programs/compile_failure/overflowing_assignment/src/main.nr diff --git a/tooling/nargo_cli/tests/compile_failure/overlapping_generic_impls/Nargo.toml b/test_programs/compile_failure/overlapping_generic_impls/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/overlapping_generic_impls/Nargo.toml rename to test_programs/compile_failure/overlapping_generic_impls/Nargo.toml diff --git a/test_programs/compile_failure/overlapping_generic_impls/src/main.nr b/test_programs/compile_failure/overlapping_generic_impls/src/main.nr new file mode 100644 index 00000000000..95e4d53bf93 --- /dev/null +++ b/test_programs/compile_failure/overlapping_generic_impls/src/main.nr @@ -0,0 +1,6 @@ +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/package_name_empty/Nargo.toml b/test_programs/compile_failure/package_name_empty/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/package_name_empty/Nargo.toml rename to test_programs/compile_failure/package_name_empty/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_failure/package_name_empty/src/main.nr b/test_programs/compile_failure/package_name_empty/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/package_name_empty/src/main.nr rename to test_programs/compile_failure/package_name_empty/src/main.nr diff --git a/tooling/nargo_cli/tests/compile_failure/package_name_hyphen/Nargo.toml b/test_programs/compile_failure/package_name_hyphen/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/package_name_hyphen/Nargo.toml rename to test_programs/compile_failure/package_name_hyphen/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_failure/package_name_hyphen/src/main.nr b/test_programs/compile_failure/package_name_hyphen/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/package_name_hyphen/src/main.nr rename to test_programs/compile_failure/package_name_hyphen/src/main.nr diff --git a/tooling/nargo_cli/tests/compile_failure/primary_attribute_struct/Nargo.toml b/test_programs/compile_failure/primary_attribute_struct/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/primary_attribute_struct/Nargo.toml rename to test_programs/compile_failure/primary_attribute_struct/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_failure/primary_attribute_struct/src/main.nr b/test_programs/compile_failure/primary_attribute_struct/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/primary_attribute_struct/src/main.nr rename to test_programs/compile_failure/primary_attribute_struct/src/main.nr diff --git a/tooling/nargo_cli/tests/compile_failure/radix_non_constant_length/Nargo.toml b/test_programs/compile_failure/radix_non_constant_length/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/radix_non_constant_length/Nargo.toml rename to test_programs/compile_failure/radix_non_constant_length/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_failure/radix_non_constant_length/Prover.toml b/test_programs/compile_failure/radix_non_constant_length/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/radix_non_constant_length/Prover.toml rename to test_programs/compile_failure/radix_non_constant_length/Prover.toml diff --git a/test_programs/compile_failure/radix_non_constant_length/src/main.nr b/test_programs/compile_failure/radix_non_constant_length/src/main.nr new file mode 100644 index 00000000000..c6dd68d925c --- /dev/null +++ b/test_programs/compile_failure/radix_non_constant_length/src/main.nr @@ -0,0 +1,4 @@ +fn main(x: Field, y: pub u32) { + let bytes = x.to_be_bytes(y); + assert(bytes[0] == 0); +} diff --git a/test_programs/compile_failure/raw_string_huge/Nargo.toml b/test_programs/compile_failure/raw_string_huge/Nargo.toml new file mode 100644 index 00000000000..ecef0e2a07c --- /dev/null +++ b/test_programs/compile_failure/raw_string_huge/Nargo.toml @@ -0,0 +1,5 @@ +[package] +name = "raw_string_huge" +type = "bin" +authors = [""] +[dependencies] \ No newline at end of file diff --git a/test_programs/compile_failure/raw_string_huge/src/main.nr b/test_programs/compile_failure/raw_string_huge/src/main.nr new file mode 100644 index 00000000000..7bca9942e7a --- /dev/null +++ b/test_programs/compile_failure/raw_string_huge/src/main.nr @@ -0,0 +1,4 @@ +fn main() { + // Fails because of too many hashes for raw string (256+ hashes) + let _a = r##############################################################################################################################################################################################################################################################################"hello"##############################################################################################################################################################################################################################################################################; +} diff --git a/tooling/nargo_cli/tests/compile_failure/slice_access_failure/Nargo.toml b/test_programs/compile_failure/slice_access_failure/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/slice_access_failure/Nargo.toml rename to test_programs/compile_failure/slice_access_failure/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_failure/slice_access_failure/Prover.toml b/test_programs/compile_failure/slice_access_failure/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/slice_access_failure/Prover.toml rename to test_programs/compile_failure/slice_access_failure/Prover.toml diff --git a/test_programs/compile_failure/slice_access_failure/src/main.nr b/test_programs/compile_failure/slice_access_failure/src/main.nr new file mode 100644 index 00000000000..6e8b5c7d841 --- /dev/null +++ b/test_programs/compile_failure/slice_access_failure/src/main.nr @@ -0,0 +1,13 @@ +fn main(x: Field, y: pub Field) { + let mut slice = [0; 2]; + if x == y { + slice = slice.push_back(y); + slice = slice.push_back(x); + } else { + slice = slice.push_back(x); + } + // This constraint should fail as the slice length is 3 and the index is 3 + // The right hand side AND case ensures that the circuit inputs have not changed + // and we always hit the else case in the if statement above. + assert((slice[3] == 0) & (slice[2] != y)); +} diff --git a/tooling/nargo_cli/tests/compile_failure/slice_insert_failure/Nargo.toml b/test_programs/compile_failure/slice_insert_failure/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/slice_insert_failure/Nargo.toml rename to test_programs/compile_failure/slice_insert_failure/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_failure/slice_insert_failure/Prover.toml b/test_programs/compile_failure/slice_insert_failure/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/slice_insert_failure/Prover.toml rename to test_programs/compile_failure/slice_insert_failure/Prover.toml diff --git a/test_programs/compile_failure/slice_insert_failure/src/main.nr b/test_programs/compile_failure/slice_insert_failure/src/main.nr new file mode 100644 index 00000000000..38892f01e12 --- /dev/null +++ b/test_programs/compile_failure/slice_insert_failure/src/main.nr @@ -0,0 +1,11 @@ +fn main(x: Field, y: pub Field) { + let mut slice = [0; 2]; + if x == y { + slice = slice.push_back(y); + slice = slice.push_back(x); + } else { + slice = slice.push_back(x); + } + + slice = slice.insert(10, 100); +} diff --git a/tooling/nargo_cli/tests/compile_failure/slice_remove_failure/Nargo.toml b/test_programs/compile_failure/slice_remove_failure/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/slice_remove_failure/Nargo.toml rename to test_programs/compile_failure/slice_remove_failure/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_failure/slice_remove_failure/Prover.toml b/test_programs/compile_failure/slice_remove_failure/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/slice_remove_failure/Prover.toml rename to test_programs/compile_failure/slice_remove_failure/Prover.toml diff --git a/test_programs/compile_failure/slice_remove_failure/src/main.nr b/test_programs/compile_failure/slice_remove_failure/src/main.nr new file mode 100644 index 00000000000..f9faa25384b --- /dev/null +++ b/test_programs/compile_failure/slice_remove_failure/src/main.nr @@ -0,0 +1,11 @@ +fn main(x: Field, y: pub Field) { + let mut slice = [0; 2]; + if x == y { + slice = slice.push_back(y); + slice = slice.push_back(x); + } else { + slice = slice.push_back(x); + } + + let (removed_slice, removed_elem) = slice.remove(10); +} diff --git a/tooling/nargo_cli/tests/compile_failure/trait_incorrect_generic_count/Nargo.toml b/test_programs/compile_failure/trait_incorrect_generic_count/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/trait_incorrect_generic_count/Nargo.toml rename to test_programs/compile_failure/trait_incorrect_generic_count/Nargo.toml diff --git a/test_programs/compile_failure/trait_incorrect_generic_count/src/main.nr b/test_programs/compile_failure/trait_incorrect_generic_count/src/main.nr new file mode 100644 index 00000000000..d65d55894ea --- /dev/null +++ b/test_programs/compile_failure/trait_incorrect_generic_count/src/main.nr @@ -0,0 +1,12 @@ +fn main() { + let x: u32 = 0; + x.trait_fn(); +} + +trait Trait { + fn trait_fn(x: T) -> T {} +} + +impl Trait for u32 { + fn trait_fn(x: A) -> A { x } +} diff --git a/tooling/nargo_cli/tests/compile_failure/workspace_fail/Nargo.toml b/test_programs/compile_failure/workspace_fail/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/workspace_fail/Nargo.toml rename to test_programs/compile_failure/workspace_fail/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_failure/workspace_fail/crates/a/Nargo.toml b/test_programs/compile_failure/workspace_fail/crates/a/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/workspace_fail/crates/a/Nargo.toml rename to test_programs/compile_failure/workspace_fail/crates/a/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_failure/workspace_fail/crates/a/Prover.toml b/test_programs/compile_failure/workspace_fail/crates/a/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/workspace_fail/crates/a/Prover.toml rename to test_programs/compile_failure/workspace_fail/crates/a/Prover.toml diff --git a/test_programs/compile_failure/workspace_fail/crates/a/src/main.nr b/test_programs/compile_failure/workspace_fail/crates/a/src/main.nr new file mode 100644 index 00000000000..cf72627da2e --- /dev/null +++ b/test_programs/compile_failure/workspace_fail/crates/a/src/main.nr @@ -0,0 +1,3 @@ +fn main(x: Field, y: pub Field) { + assert(x == y); +} diff --git a/tooling/nargo_cli/tests/compile_failure/workspace_fail/crates/b/Nargo.toml b/test_programs/compile_failure/workspace_fail/crates/b/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/workspace_fail/crates/b/Nargo.toml rename to test_programs/compile_failure/workspace_fail/crates/b/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_failure/workspace_fail/crates/b/Prover.toml b/test_programs/compile_failure/workspace_fail/crates/b/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/workspace_fail/crates/b/Prover.toml rename to test_programs/compile_failure/workspace_fail/crates/b/Prover.toml diff --git a/test_programs/compile_failure/workspace_fail/crates/b/src/main.nr b/test_programs/compile_failure/workspace_fail/crates/b/src/main.nr new file mode 100644 index 00000000000..4e1fd3c9035 --- /dev/null +++ b/test_programs/compile_failure/workspace_fail/crates/b/src/main.nr @@ -0,0 +1,3 @@ +fn main(x: Field, y: pub Field) { + assert(x != y); +} diff --git a/tooling/nargo_cli/tests/compile_failure/workspace_missing_toml/Nargo.toml b/test_programs/compile_failure/workspace_missing_toml/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/workspace_missing_toml/Nargo.toml rename to test_programs/compile_failure/workspace_missing_toml/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_failure/workspace_missing_toml/crates/a/Prover.toml b/test_programs/compile_failure/workspace_missing_toml/crates/a/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/workspace_missing_toml/crates/a/Prover.toml rename to test_programs/compile_failure/workspace_missing_toml/crates/a/Prover.toml diff --git a/test_programs/compile_failure/workspace_missing_toml/crates/a/src/main.nr b/test_programs/compile_failure/workspace_missing_toml/crates/a/src/main.nr new file mode 100644 index 00000000000..cf72627da2e --- /dev/null +++ b/test_programs/compile_failure/workspace_missing_toml/crates/a/src/main.nr @@ -0,0 +1,3 @@ +fn main(x: Field, y: pub Field) { + assert(x == y); +} diff --git a/tooling/nargo_cli/tests/compile_failure/workspace_missing_toml/crates/b/Nargo.toml b/test_programs/compile_failure/workspace_missing_toml/crates/b/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/workspace_missing_toml/crates/b/Nargo.toml rename to test_programs/compile_failure/workspace_missing_toml/crates/b/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_failure/workspace_missing_toml/crates/b/Prover.toml b/test_programs/compile_failure/workspace_missing_toml/crates/b/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/workspace_missing_toml/crates/b/Prover.toml rename to test_programs/compile_failure/workspace_missing_toml/crates/b/Prover.toml diff --git a/test_programs/compile_failure/workspace_missing_toml/crates/b/src/main.nr b/test_programs/compile_failure/workspace_missing_toml/crates/b/src/main.nr new file mode 100644 index 00000000000..4e1fd3c9035 --- /dev/null +++ b/test_programs/compile_failure/workspace_missing_toml/crates/b/src/main.nr @@ -0,0 +1,3 @@ +fn main(x: Field, y: pub Field) { + assert(x != y); +} diff --git a/tooling/nargo_cli/tests/compile_success_contract/contract_with_impl/Nargo.toml b/test_programs/compile_success_contract/contract_with_impl/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_contract/contract_with_impl/Nargo.toml rename to test_programs/compile_success_contract/contract_with_impl/Nargo.toml diff --git a/test_programs/compile_success_contract/contract_with_impl/src/main.nr b/test_programs/compile_success_contract/contract_with_impl/src/main.nr new file mode 100644 index 00000000000..ddcb5d54d78 --- /dev/null +++ b/test_programs/compile_success_contract/contract_with_impl/src/main.nr @@ -0,0 +1,7 @@ +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/Nargo.toml b/test_programs/compile_success_contract/non_entry_point_method/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_contract/non_entry_point_method/Nargo.toml rename to test_programs/compile_success_contract/non_entry_point_method/Nargo.toml diff --git a/test_programs/compile_success_contract/non_entry_point_method/src/main.nr b/test_programs/compile_success_contract/non_entry_point_method/src/main.nr new file mode 100644 index 00000000000..b768653262a --- /dev/null +++ b/test_programs/compile_success_contract/non_entry_point_method/src/main.nr @@ -0,0 +1,6 @@ +contract Foo { + struct PlaceholderStruct{x : u32 } + + #[contract_library_method] + fn has_mut(_context: &mut PlaceholderStruct) {} +} diff --git a/tooling/nargo_cli/tests/compile_success_contract/simple_contract/Nargo.toml b/test_programs/compile_success_contract/simple_contract/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_contract/simple_contract/Nargo.toml rename to test_programs/compile_success_contract/simple_contract/Nargo.toml diff --git a/test_programs/compile_success_contract/simple_contract/src/main.nr b/test_programs/compile_success_contract/simple_contract/src/main.nr new file mode 100644 index 00000000000..88edd4ac2c3 --- /dev/null +++ b/test_programs/compile_success_contract/simple_contract/src/main.nr @@ -0,0 +1,14 @@ +contract Foo { + fn double(x: Field) -> pub Field { + x * 2 + } + fn triple(x: Field) -> pub Field { + x * 3 + } + internal fn quadruple(x: Field) -> pub Field { + x * 4 + } + open internal fn skibbidy(x: Field) -> pub Field { + x * 5 + } +} diff --git a/tooling/nargo_cli/tests/compile_success_empty/attributes_multiple/Nargo.toml b/test_programs/compile_success_empty/attributes_multiple/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/attributes_multiple/Nargo.toml rename to test_programs/compile_success_empty/attributes_multiple/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_success_empty/attributes_multiple/src/main.nr b/test_programs/compile_success_empty/attributes_multiple/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/attributes_multiple/src/main.nr rename to test_programs/compile_success_empty/attributes_multiple/src/main.nr diff --git a/tooling/nargo_cli/tests/compile_success_empty/attributes_struct/Nargo.toml b/test_programs/compile_success_empty/attributes_struct/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/attributes_struct/Nargo.toml rename to test_programs/compile_success_empty/attributes_struct/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_success_empty/attributes_struct/src/main.nr b/test_programs/compile_success_empty/attributes_struct/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/attributes_struct/src/main.nr rename to test_programs/compile_success_empty/attributes_struct/src/main.nr diff --git a/tooling/nargo_cli/tests/compile_success_empty/auto_deref/Nargo.toml b/test_programs/compile_success_empty/auto_deref/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/auto_deref/Nargo.toml rename to test_programs/compile_success_empty/auto_deref/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_success_empty/auto_deref/src/main.nr b/test_programs/compile_success_empty/auto_deref/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/auto_deref/src/main.nr rename to test_programs/compile_success_empty/auto_deref/src/main.nr diff --git a/tooling/nargo_cli/tests/compile_success_empty/brillig_cast/Nargo.toml b/test_programs/compile_success_empty/brillig_cast/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/brillig_cast/Nargo.toml rename to test_programs/compile_success_empty/brillig_cast/Nargo.toml diff --git a/test_programs/compile_success_empty/brillig_cast/src/main.nr b/test_programs/compile_success_empty/brillig_cast/src/main.nr new file mode 100644 index 00000000000..3ba29b52982 --- /dev/null +++ b/test_programs/compile_success_empty/brillig_cast/src/main.nr @@ -0,0 +1,49 @@ +// Tests a very simple Brillig function. +// +// The features being tested are cast operations on brillig +fn main() { + bool_casts(); + field_casts(); + uint_casts(); + int_casts(); + mixed_casts(); +} + +unconstrained fn bool_casts() { + assert(false == 0 as bool); + assert(true == 1 as bool); + assert(true == 3 as bool); +} + +unconstrained fn field_casts() { + assert(5 as u8 as Field == 5); + assert(16 as u4 as Field == 0); +} + +unconstrained fn uint_casts() { + let x: u32 = 100; + assert(x as u2 == 0); + assert(x as u4 == 4); + assert(x as u6 == 36); + assert(x as u8 == 100); + assert(x as u64 == 100); + assert(x as u126 == 100); +} + +unconstrained fn int_casts() { + let x: i32 = 100; + assert(x as i2 == 0); + assert(x as i4 == 4); + assert(x as i6 == -28 as i6); + assert(x as i8 == 100); + assert(x as i8 == 100); + assert(x as i8 == 100); +} + +unconstrained fn mixed_casts() { + assert(100 as u32 as i32 as u32 == 100); + assert(13 as u4 as i2 as u32 == 1); + assert(15 as u4 as i2 as u32 == 3); + assert(1 as u8 as bool == true); + assert(true as i8 == 1); +} diff --git a/tooling/nargo_cli/tests/compile_success_empty/brillig_field_binary_operations/Nargo.toml b/test_programs/compile_success_empty/brillig_field_binary_operations/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/brillig_field_binary_operations/Nargo.toml rename to test_programs/compile_success_empty/brillig_field_binary_operations/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_success_empty/brillig_field_binary_operations/Prover.toml b/test_programs/compile_success_empty/brillig_field_binary_operations/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/brillig_field_binary_operations/Prover.toml rename to test_programs/compile_success_empty/brillig_field_binary_operations/Prover.toml diff --git a/test_programs/compile_success_empty/brillig_field_binary_operations/src/main.nr b/test_programs/compile_success_empty/brillig_field_binary_operations/src/main.nr new file mode 100644 index 00000000000..54f06858846 --- /dev/null +++ b/test_programs/compile_success_empty/brillig_field_binary_operations/src/main.nr @@ -0,0 +1,25 @@ +// Tests arithmetic operations on fields +fn main() { + let x = 4; + let y = 2; + assert((x + y) == add(x, y)); + assert((x - y) == sub(x, y)); + assert((x * y) == mul(x, y)); + assert((x / y) == div(x, y)); +} + +unconstrained fn add(x: Field, y: Field) -> Field { + x + y +} + +unconstrained fn sub(x: Field, y: Field) -> Field { + x - y +} + +unconstrained fn mul(x: Field, y: Field) -> Field { + x * y +} + +unconstrained fn div(x: Field, y: Field) -> Field { + x / y +} diff --git a/tooling/nargo_cli/tests/compile_success_empty/brillig_integer_binary_operations/Nargo.toml b/test_programs/compile_success_empty/brillig_integer_binary_operations/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/brillig_integer_binary_operations/Nargo.toml rename to test_programs/compile_success_empty/brillig_integer_binary_operations/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_success_empty/brillig_integer_binary_operations/Prover.toml b/test_programs/compile_success_empty/brillig_integer_binary_operations/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/brillig_integer_binary_operations/Prover.toml rename to test_programs/compile_success_empty/brillig_integer_binary_operations/Prover.toml diff --git a/test_programs/compile_success_empty/brillig_integer_binary_operations/src/main.nr b/test_programs/compile_success_empty/brillig_integer_binary_operations/src/main.nr new file mode 100644 index 00000000000..a873bcd5dbd --- /dev/null +++ b/test_programs/compile_success_empty/brillig_integer_binary_operations/src/main.nr @@ -0,0 +1,77 @@ +// Tests arithmetic operations on integers +fn main() { + let x: u32 = 6; + let y: u32 = 2; + + assert((x + y) == add(x, y)); + + assert((x - y) == sub(x, y)); + + assert((x * y) == mul(x, y)); + + assert((x / y) == div(x, y)); + // TODO SSA => ACIR has some issues with i32 ops + assert(check_signed_div(6, 2, 3)); + + assert(eq(1, 2) == false); + assert(eq(1, 1)); + + assert(lt(x, y) == false); + assert(lt(y, x)); + + assert((x & y) == and(x, y)); + assert((x | y) == or(x, y)); + // TODO SSA => ACIR has some issues with xor ops + assert(check_xor(x, y, 4)); + assert((x >> y) == shr(x, y)); + assert((x << y) == shl(x, y)); +} + +unconstrained fn add(x: u32, y: u32) -> u32 { + x + y +} + +unconstrained fn sub(x: u32, y: u32) -> u32 { + x - y +} + +unconstrained fn mul(x: u32, y: u32) -> u32 { + x * y +} + +unconstrained fn div(x: u32, y: u32) -> u32 { + x / y +} + +unconstrained fn check_signed_div(x: i32, y: i32, result: i32) -> bool { + (x / y) == result +} + +unconstrained fn eq(x: u32, y: u32) -> bool { + x == y +} + +unconstrained fn lt(x: u32, y: u32) -> bool { + x < y +} + +unconstrained fn and(x: u32, y: u32) -> u32 { + x & y +} + +unconstrained fn or(x: u32, y: u32) -> u32 { + x | y +} + +unconstrained fn check_xor(x: u32, y: u32, result: u32) -> bool { + (x ^ y) == result +} + +unconstrained fn shr(x: u32, y: u32) -> u32 { + x >> y +} + +unconstrained fn shl(x: u32, y: u32) -> u32 { + x << y +} + diff --git a/tooling/nargo_cli/tests/compile_success_empty/brillig_modulo/Nargo.toml b/test_programs/compile_success_empty/brillig_modulo/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/brillig_modulo/Nargo.toml rename to test_programs/compile_success_empty/brillig_modulo/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_success_empty/brillig_modulo/Prover.toml b/test_programs/compile_success_empty/brillig_modulo/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/brillig_modulo/Prover.toml rename to test_programs/compile_success_empty/brillig_modulo/Prover.toml diff --git a/test_programs/compile_success_empty/brillig_modulo/src/main.nr b/test_programs/compile_success_empty/brillig_modulo/src/main.nr new file mode 100644 index 00000000000..ed0353b101a --- /dev/null +++ b/test_programs/compile_success_empty/brillig_modulo/src/main.nr @@ -0,0 +1,27 @@ +// Tests a very simple program. +// +// The features being tested is modulo operations on brillig +fn main() { + assert(modulo(47, 3) == 2); + assert(modulo(2, 3) == 2); + assert(signed_modulo(5, 3) == 2); + assert(signed_modulo(2, 3) == 2); + + 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); + // (-5 / 3) * 3 - 2 = -1 * 3 - 2 = -3 - 2 = -5 + assert(signed_modulo(minus_five, 3) == minus_two); + // (-5 / -3) * -3 - 2 = 1 * -3 - 2 = -3 - 2 = -5 + assert(signed_modulo(minus_five, minus_three) == minus_two); +} + +unconstrained fn modulo(x: u32, y: u32) -> u32 { + x % y +} + +unconstrained fn signed_modulo(x: i4, y: i4) -> i4 { + x % y +} diff --git a/tooling/nargo_cli/tests/compile_success_empty/brillig_to_bits/Nargo.toml b/test_programs/compile_success_empty/brillig_to_bits/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/brillig_to_bits/Nargo.toml rename to test_programs/compile_success_empty/brillig_to_bits/Nargo.toml diff --git a/test_programs/compile_success_empty/brillig_to_bits/src/main.nr b/test_programs/compile_success_empty/brillig_to_bits/src/main.nr new file mode 100644 index 00000000000..7ff3d2467b5 --- /dev/null +++ b/test_programs/compile_success_empty/brillig_to_bits/src/main.nr @@ -0,0 +1,23 @@ +use dep::std; + +unconstrained fn main() { + let field = 1000; + let be_bits = field.to_be_bits(16); + let le_bits = field.to_le_bits(16); + + for i in 0..16 { + let x = be_bits[i]; + let y = le_bits[15 - i]; + assert(x == y); + } + + let x = 3; + let be_bits_x = x.to_be_bits(4); + let le_bits_x = x.to_le_bits(4); + + for i in 0..4 { + let be_bit = be_bits_x[i]; + 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/Nargo.toml b/test_programs/compile_success_empty/closure_explicit_types/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/closure_explicit_types/Nargo.toml rename to test_programs/compile_success_empty/closure_explicit_types/Nargo.toml diff --git a/test_programs/compile_success_empty/closure_explicit_types/src/main.nr b/test_programs/compile_success_empty/closure_explicit_types/src/main.nr new file mode 100644 index 00000000000..eec2b90b5b2 --- /dev/null +++ b/test_programs/compile_success_empty/closure_explicit_types/src/main.nr @@ -0,0 +1,71 @@ +fn ret_normal_lambda1() -> fn() -> Field { + || 10 +} +// return lamda that captures a thing +fn ret_closure1() -> fn[(Field,)]() -> Field { + let x = 20; + || x + 10 +} +// return lamda that captures two things +fn ret_closure2() -> fn[(Field,Field)]() -> Field { + let x = 20; + let y = 10; + || x + y + 10 +} +// return lamda that captures two things with different types +fn ret_closure3() -> fn[(u32,u64)]() -> u64 { + let x: u32 = 20; + let y: u64 = 10; + || x as u64 + y + 10 +} +// accepts closure that has 1 thing in its env, calls it and returns the result +fn accepts_closure1(f: fn[(Field,)]() -> Field) -> Field { + f() +} +// accepts closure that has 1 thing in its env and returns it +fn accepts_closure2(f: fn[(Field,)]() -> Field) -> fn[(Field,)]() -> Field { + f +} +// accepts closure with different types in the capture group +fn accepts_closure3(f: fn[(u32, u64)]() -> u64) -> u64 { + f() +} +// generic over closure environments +fn add_results(f1: fn[Env1]() -> Field, f2: fn[Env2]() -> Field) -> Field { + f1() + f2() +} +// a *really* generic function +fn map(arr: [T; N], f: fn[Env](T) -> U) -> [U; N] { + let first_elem = f(arr[0]); + let mut ret = [first_elem; N]; + + for i in 1..N { + ret[i] = f(arr[i]); + } + + ret +} + +fn main() { + assert(ret_normal_lambda1()() == 10); + assert(ret_closure1()() == 30); + assert(ret_closure2()() == 40); + assert(ret_closure3()() == 40); + + let x = 50; + assert(accepts_closure1(|| x) == 50); + assert(accepts_closure2(|| x + 10)() == 60); + + let y: u32 = 30; + let z: u64 = 40; + 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); + + 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_recursion_regression/Nargo.toml b/test_programs/compile_success_empty/comptime_recursion_regression/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/comptime_recursion_regression/Nargo.toml rename to test_programs/compile_success_empty/comptime_recursion_regression/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_success_empty/comptime_recursion_regression/Prover.toml b/test_programs/compile_success_empty/comptime_recursion_regression/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/comptime_recursion_regression/Prover.toml rename to test_programs/compile_success_empty/comptime_recursion_regression/Prover.toml diff --git a/tooling/nargo_cli/tests/compile_success_empty/comptime_recursion_regression/src/main.nr b/test_programs/compile_success_empty/comptime_recursion_regression/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/comptime_recursion_regression/src/main.nr rename to test_programs/compile_success_empty/comptime_recursion_regression/src/main.nr diff --git a/tooling/nargo_cli/tests/compile_success_empty/comptime_sort/Nargo.toml b/test_programs/compile_success_empty/comptime_sort/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/comptime_sort/Nargo.toml rename to test_programs/compile_success_empty/comptime_sort/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_success_empty/comptime_sort/src/main.nr b/test_programs/compile_success_empty/comptime_sort/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/comptime_sort/src/main.nr rename to test_programs/compile_success_empty/comptime_sort/src/main.nr diff --git a/tooling/nargo_cli/tests/compile_success_empty/conditional_regression_547/Nargo.toml b/test_programs/compile_success_empty/conditional_regression_547/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/conditional_regression_547/Nargo.toml rename to test_programs/compile_success_empty/conditional_regression_547/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_success_empty/conditional_regression_547/Prover.toml b/test_programs/compile_success_empty/conditional_regression_547/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/conditional_regression_547/Prover.toml rename to test_programs/compile_success_empty/conditional_regression_547/Prover.toml diff --git a/test_programs/compile_success_empty/conditional_regression_547/src/main.nr b/test_programs/compile_success_empty/conditional_regression_547/src/main.nr new file mode 100644 index 00000000000..e47d23516a5 --- /dev/null +++ b/test_programs/compile_success_empty/conditional_regression_547/src/main.nr @@ -0,0 +1,16 @@ +fn main(x: Field) -> pub Field { + // Regression test for issue #547 + // Warning: it must be kept at the start of main + let arr: [u8; 2] = [1, 2]; + if arr[0] != arr[1] { + for i in 0..1 { + assert(i != 2); + } + } + // Regression for predicate simplification + x + safe_inverse(0) +} + +fn safe_inverse(n: Field) -> Field { + if n == 0 { 0 } else { 1 / n } +} diff --git a/tooling/nargo_cli/tests/compile_success_empty/conditional_regression_579/Nargo.toml b/test_programs/compile_success_empty/conditional_regression_579/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/conditional_regression_579/Nargo.toml rename to test_programs/compile_success_empty/conditional_regression_579/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_success_empty/conditional_regression_579/Prover.toml b/test_programs/compile_success_empty/conditional_regression_579/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/conditional_regression_579/Prover.toml rename to test_programs/compile_success_empty/conditional_regression_579/Prover.toml diff --git a/tooling/nargo_cli/tests/compile_success_empty/conditional_regression_579/src/main.nr b/test_programs/compile_success_empty/conditional_regression_579/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/conditional_regression_579/src/main.nr rename to test_programs/compile_success_empty/conditional_regression_579/src/main.nr diff --git a/tooling/nargo_cli/tests/compile_success_empty/conditional_regression_to_bits/Nargo.toml b/test_programs/compile_success_empty/conditional_regression_to_bits/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/conditional_regression_to_bits/Nargo.toml rename to test_programs/compile_success_empty/conditional_regression_to_bits/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_success_empty/conditional_regression_to_bits/Prover.toml b/test_programs/compile_success_empty/conditional_regression_to_bits/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/conditional_regression_to_bits/Prover.toml rename to test_programs/compile_success_empty/conditional_regression_to_bits/Prover.toml diff --git a/test_programs/compile_success_empty/conditional_regression_to_bits/src/main.nr b/test_programs/compile_success_empty/conditional_regression_to_bits/src/main.nr new file mode 100644 index 00000000000..5446cfbb1e4 --- /dev/null +++ b/test_programs/compile_success_empty/conditional_regression_to_bits/src/main.nr @@ -0,0 +1,28 @@ +use dep::std; + +fn main() { + //Regression for to_le_bits() constant evaluation + // binary array representation of u8 1 + let arr: [u8; 2] = [1, 2]; + let as_bits_hardcode_1 = [1, 0]; + let mut c1 = 0; + for i in 0..2 { + let mut as_bits = (arr[i] as Field).to_le_bits(2); + c1 = c1 + as_bits[0] as Field; + + if i == 0 { + assert(arr[i] == 1); // 1 + for k in 0..2 { + assert(as_bits_hardcode_1[k] == as_bits[k]); + } + } + if i == 1 { + assert(arr[i] == 2); //2 + for k in 0..2 { + assert(as_bits_hardcode_1[k] != as_bits[k]); + } + } + } + assert(c1 == 1); +} + diff --git a/tooling/nargo_cli/tests/compile_success_empty/ec_baby_jubjub/Nargo.toml b/test_programs/compile_success_empty/ec_baby_jubjub/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/ec_baby_jubjub/Nargo.toml rename to test_programs/compile_success_empty/ec_baby_jubjub/Nargo.toml diff --git a/test_programs/compile_success_empty/ec_baby_jubjub/src/main.nr b/test_programs/compile_success_empty/ec_baby_jubjub/src/main.nr new file mode 100644 index 00000000000..becd3c8927a --- /dev/null +++ b/test_programs/compile_success_empty/ec_baby_jubjub/src/main.nr @@ -0,0 +1,218 @@ +// Tests may be checked against https://github.com/cfrg/draft-irtf-cfrg-hash-to-curve/tree/main/poc +use dep::std::ec::tecurve::affine::Curve as AffineCurve; +use dep::std::ec::tecurve::affine::Point as Gaffine; +use dep::std::ec::tecurve::curvegroup::Curve; +use dep::std::ec::tecurve::curvegroup::Point as G; + +use dep::std::ec::swcurve::affine::Point as SWGaffine; +use dep::std::ec::swcurve::curvegroup::Point as SWG; + +use dep::std::ec::montcurve::affine::Point as MGaffine; +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 { + // Define Baby Jubjub (ERC-2494) parameters in affine representation + 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 + ) + ) + ); + // 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))); + // 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( + bjj_affine.contains(bjj_affine.gen) + & bjj_affine.contains(p1_affine) + & bjj_affine.contains(p2_affine) + & bjj_affine.contains(p3_affine) + & bjj_affine.contains(p4_affine) + & bjj_affine.contains(p5_affine) + ); + // 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(); + let p4 = p4_affine.into_group(); + let p5 = p5_affine.into_group(); + // Test addition + assert(p3.eq(bjj.add(p1, p2))); + // Test scalar multiplication + assert(p4.eq(bjj.mul(2, 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) + ); + // Test SWCurve equivalents of the above + // First the affine representation + let bjj_swcurve_affine = bjj_affine.into_swcurve(); + + 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 = bjj_affine.map_into_swcurve(p3_affine); + let p4_swcurve_affine = bjj_affine.map_into_swcurve(p4_affine); + 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))); + // 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))); + // 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) + ); + // Then the CurveGroup representation + let bjj_swcurve = bjj.into_swcurve(); + + let p1_swcurve = bjj.map_into_swcurve(p1); + let p2_swcurve = bjj.map_into_swcurve(p2); + let p3_swcurve = bjj.map_into_swcurve(p3); + let p4_swcurve = bjj.map_into_swcurve(p4); + let p5_swcurve = bjj.map_into_swcurve(p5); + // Addition + 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))); + // 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) + ); + // Test MontCurve conversions + // First the affine representation + let bjj_montcurve_affine = bjj_affine.into_montcurve(); + + let p1_montcurve_affine = p1_affine.into_montcurve(); + let p2_montcurve_affine = p2_affine.into_montcurve(); + let p3_montcurve_affine = p3_affine.into_montcurve(); + let p4_montcurve_affine = p4_affine.into_montcurve(); + let p5_montcurve_affine = p5_affine.into_montcurve(); + // Addition + assert(p3_montcurve_affine.eq(bjj_montcurve_affine.add(p1_montcurve_affine, p2_montcurve_affine))); + // 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))); + // 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) + ); + // Then the CurveGroup representation + let bjj_montcurve = bjj.into_montcurve(); + + let p1_montcurve = p1_montcurve_affine.into_group(); + let p2_montcurve = p2_montcurve_affine.into_group(); + let p3_montcurve = p3_montcurve_affine.into_group(); + let p4_montcurve = p4_montcurve_affine.into_group(); + let p5_montcurve = p5_montcurve_affine.into_group(); + // Addition + assert(p3_montcurve.eq(bjj_montcurve.add(p1_montcurve, p2_montcurve))); + // Doubling + assert(p4_montcurve.eq(bjj_montcurve.mul(2, 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) + ); + // 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() + ) + ); + // 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 + ) + ) + ) + ); + } +} diff --git a/tooling/nargo_cli/tests/compile_success_empty/generators/Nargo.toml b/test_programs/compile_success_empty/generators/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/generators/Nargo.toml rename to test_programs/compile_success_empty/generators/Nargo.toml diff --git a/test_programs/compile_success_empty/generators/src/main.nr b/test_programs/compile_success_empty/generators/src/main.nr new file mode 100644 index 00000000000..20bdedee50f --- /dev/null +++ b/test_programs/compile_success_empty/generators/src/main.nr @@ -0,0 +1,56 @@ +// TODO? +// the syntax for these return types is very difficult to get right :/ +// for arguments this can be handled with a generic Env (or with Fn traits when we add them) +// but for return types neither fo these will help, you need to type out the exact type +fn make_counter() -> fn[(&mut Field,)]() -> Field { + let mut x = &mut 0; + + || { + *x = *x + 1; + *x + } +} + +fn fibonacci_generator() -> fn[(&mut Field, &mut Field)]() -> Field { + let mut x = &mut 1; + let mut y = &mut 2; + + || { + let old_x = *x; + let old_y = *y; + + *y = *x + *y; + *x = old_y; + + old_x + } +} +// 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()) +} + +fn test_fib() { + let fib = fibonacci_generator(); + + assert(fib() == 1); + assert(fib() == 2); + assert(fib() == 3); + assert(fib() == 5); + + assert(get_some(fib) == [8, 13, 21, 34, 55]); +} + +fn test_counter() { + let counter = make_counter(); + assert(counter() == 1); + assert(counter() == 2); + assert(counter() == 3); + + assert(get_some(counter) == [4, 5, 6, 7, 8]); +} + +fn main() { + test_fib(); + test_counter(); +} diff --git a/tooling/nargo_cli/tests/compile_success_empty/higher_order_fn_selector/Nargo.toml b/test_programs/compile_success_empty/higher_order_fn_selector/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/higher_order_fn_selector/Nargo.toml rename to test_programs/compile_success_empty/higher_order_fn_selector/Nargo.toml diff --git a/test_programs/compile_success_empty/higher_order_fn_selector/src/main.nr b/test_programs/compile_success_empty/higher_order_fn_selector/src/main.nr new file mode 100644 index 00000000000..99093c581b5 --- /dev/null +++ b/test_programs/compile_success_empty/higher_order_fn_selector/src/main.nr @@ -0,0 +1,30 @@ +fn g(x: &mut Field) -> () { + *x *= 2; +} + +fn h(x: &mut Field) -> () { + *x *= 3; +} + +fn selector(flag: &mut bool) -> fn(&mut Field) -> () { + let my_func = if *flag { g } else { h }; + // Flip the flag for the next function call + *flag = !(*flag); + my_func +} + +fn main() { + let mut flag: bool = true; + + let mut x: Field = 100; + let returned_func = selector(&mut flag); + returned_func(&mut x); + + assert(x == 200); + + let mut y: Field = 100; + let returned_func2 = selector(&mut flag); + returned_func2(&mut y); + + assert(y == 300); +} diff --git a/tooling/nargo_cli/tests/compile_success_empty/impl_with_where_clause/Nargo.toml b/test_programs/compile_success_empty/impl_with_where_clause/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/impl_with_where_clause/Nargo.toml rename to test_programs/compile_success_empty/impl_with_where_clause/Nargo.toml diff --git a/test_programs/compile_success_empty/impl_with_where_clause/src/main.nr b/test_programs/compile_success_empty/impl_with_where_clause/src/main.nr new file mode 100644 index 00000000000..de3078be8ba --- /dev/null +++ b/test_programs/compile_success_empty/impl_with_where_clause/src/main.nr @@ -0,0 +1,27 @@ +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; 3] 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/inner_outer_cl/Nargo.toml b/test_programs/compile_success_empty/inner_outer_cl/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/inner_outer_cl/Nargo.toml rename to test_programs/compile_success_empty/inner_outer_cl/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_success_empty/inner_outer_cl/src/main.nr b/test_programs/compile_success_empty/inner_outer_cl/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/inner_outer_cl/src/main.nr rename to test_programs/compile_success_empty/inner_outer_cl/src/main.nr diff --git a/tooling/nargo_cli/tests/compile_success_empty/instruction_deduplication/Nargo.toml b/test_programs/compile_success_empty/instruction_deduplication/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/instruction_deduplication/Nargo.toml rename to test_programs/compile_success_empty/instruction_deduplication/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_success_empty/instruction_deduplication/Prover.toml b/test_programs/compile_success_empty/instruction_deduplication/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/instruction_deduplication/Prover.toml rename to test_programs/compile_success_empty/instruction_deduplication/Prover.toml diff --git a/test_programs/compile_success_empty/instruction_deduplication/src/main.nr b/test_programs/compile_success_empty/instruction_deduplication/src/main.nr new file mode 100644 index 00000000000..43c0a382185 --- /dev/null +++ b/test_programs/compile_success_empty/instruction_deduplication/src/main.nr @@ -0,0 +1,5 @@ +fn main(x: Field) { + // This is a regression test for #2450. + // The compiler should recognize that the `(x as u32)` instructions are duplicates and so have the same output. + assert(x as u32 == x as u32); +} diff --git a/tooling/nargo_cli/tests/compile_success_empty/intrinsic_die/Nargo.toml b/test_programs/compile_success_empty/intrinsic_die/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/intrinsic_die/Nargo.toml rename to test_programs/compile_success_empty/intrinsic_die/Nargo.toml diff --git a/test_programs/compile_success_empty/intrinsic_die/src/main.nr b/test_programs/compile_success_empty/intrinsic_die/src/main.nr new file mode 100644 index 00000000000..88f7a3634c1 --- /dev/null +++ b/test_programs/compile_success_empty/intrinsic_die/src/main.nr @@ -0,0 +1,8 @@ +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_commitment([x]); + let _p1 = std::scalar_mul::fixed_base_embedded_curve(x, 0); +} diff --git a/tooling/nargo_cli/tests/compile_success_empty/let_stmt/Nargo.toml b/test_programs/compile_success_empty/let_stmt/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/let_stmt/Nargo.toml rename to test_programs/compile_success_empty/let_stmt/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_success_empty/let_stmt/Prover.toml b/test_programs/compile_success_empty/let_stmt/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/let_stmt/Prover.toml rename to test_programs/compile_success_empty/let_stmt/Prover.toml diff --git a/tooling/nargo_cli/tests/compile_success_empty/let_stmt/src/main.nr b/test_programs/compile_success_empty/let_stmt/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/let_stmt/src/main.nr rename to test_programs/compile_success_empty/let_stmt/src/main.nr diff --git a/tooling/nargo_cli/tests/compile_success_empty/main_return/Nargo.toml b/test_programs/compile_success_empty/main_return/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/main_return/Nargo.toml rename to test_programs/compile_success_empty/main_return/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_success_empty/main_return/Prover.toml b/test_programs/compile_success_empty/main_return/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/main_return/Prover.toml rename to test_programs/compile_success_empty/main_return/Prover.toml diff --git a/tooling/nargo_cli/tests/compile_success_empty/main_return/src/main.nr b/test_programs/compile_success_empty/main_return/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/main_return/src/main.nr rename to test_programs/compile_success_empty/main_return/src/main.nr diff --git a/tooling/nargo_cli/tests/compile_success_empty/numeric_generics/Nargo.toml b/test_programs/compile_success_empty/numeric_generics/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/numeric_generics/Nargo.toml rename to test_programs/compile_success_empty/numeric_generics/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_success_empty/numeric_generics/Prover.toml b/test_programs/compile_success_empty/numeric_generics/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/numeric_generics/Prover.toml rename to test_programs/compile_success_empty/numeric_generics/Prover.toml diff --git a/tooling/nargo_cli/tests/compile_success_empty/numeric_generics/src/main.nr b/test_programs/compile_success_empty/numeric_generics/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/numeric_generics/src/main.nr rename to test_programs/compile_success_empty/numeric_generics/src/main.nr diff --git a/tooling/nargo_cli/tests/compile_success_empty/option/Nargo.toml b/test_programs/compile_success_empty/option/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/option/Nargo.toml rename to test_programs/compile_success_empty/option/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_success_empty/option/src/main.nr b/test_programs/compile_success_empty/option/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/option/src/main.nr rename to test_programs/compile_success_empty/option/src/main.nr diff --git a/test_programs/compile_success_empty/raw_string/Nargo.toml b/test_programs/compile_success_empty/raw_string/Nargo.toml new file mode 100644 index 00000000000..81147e65f34 --- /dev/null +++ b/test_programs/compile_success_empty/raw_string/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "raw_string" +type = "bin" +authors = [""] + +[dependencies] diff --git a/test_programs/compile_success_empty/raw_string/src/main.nr b/test_programs/compile_success_empty/raw_string/src/main.nr new file mode 100644 index 00000000000..ad8dfe82ae5 --- /dev/null +++ b/test_programs/compile_success_empty/raw_string/src/main.nr @@ -0,0 +1,13 @@ +global D = r#####"Hello "world""#####; + +fn main() { + let a = "Hello \"world\""; + let b = r#"Hello "world""#; + let c = r##"Hello "world""##; + assert(a == b); + assert(b == c); + assert(c == D); + let x = r#"Hello World"#; + let y = r"Hello World"; + assert(x == y); +} diff --git a/tooling/nargo_cli/tests/compile_success_empty/references_aliasing/Nargo.toml b/test_programs/compile_success_empty/references_aliasing/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/references_aliasing/Nargo.toml rename to test_programs/compile_success_empty/references_aliasing/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_success_empty/references_aliasing/Prover.toml b/test_programs/compile_success_empty/references_aliasing/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/references_aliasing/Prover.toml rename to test_programs/compile_success_empty/references_aliasing/Prover.toml diff --git a/test_programs/compile_success_empty/references_aliasing/src/main.nr b/test_programs/compile_success_empty/references_aliasing/src/main.nr new file mode 100644 index 00000000000..0d96bc2a734 --- /dev/null +++ b/test_programs/compile_success_empty/references_aliasing/src/main.nr @@ -0,0 +1,28 @@ +fn main() { + let mut x = 100; + let mut xref = &mut x; + increment(xref); + assert(*xref == 101); + + regression_2445(); +} + +fn increment(mut r: &mut Field) { + *r = *r + 1; +} +// If aliasing within arrays and constant folding within the mem2reg pass aren't +// handled, we'll fail to optimize out all the references in this function. +fn regression_2445() { + let mut var = 0; + let ref = &mut &mut var; + + let mut array = [ref, ref]; + + **array[0] = 1; + **array[1] = 2; + + assert(var == 2); + assert(**ref == 2); + assert(**array[0] == 2); + assert(**array[1] == 2); +} diff --git a/tooling/nargo_cli/tests/compile_success_empty/regression_2099/Nargo.toml b/test_programs/compile_success_empty/regression_2099/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/regression_2099/Nargo.toml rename to test_programs/compile_success_empty/regression_2099/Nargo.toml diff --git a/test_programs/compile_success_empty/regression_2099/src/main.nr b/test_programs/compile_success_empty/regression_2099/src/main.nr new file mode 100644 index 00000000000..f92373ce63a --- /dev/null +++ b/test_programs/compile_success_empty/regression_2099/src/main.nr @@ -0,0 +1,42 @@ +use dep::std::ec::tecurve::affine::Curve as AffineCurve; +use dep::std::ec::tecurve::affine::Point as Gaffine; +use dep::std::ec::tecurve::curvegroup::Curve; +use dep::std::ec::tecurve::curvegroup::Point as G; + +use dep::std::ec::swcurve::affine::Point as SWGaffine; +use dep::std::ec::swcurve::curvegroup::Point as SWG; + +use dep::std::ec::montcurve::affine::Point as MGaffine; +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 + ) + ); + // 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); + // Test SWCurve equivalents of the above + // First the affine representation + let bjj_swcurve_affine = bjj_affine.into_swcurve(); + + 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); + // Check that these points are on the curve + assert(bjj_swcurve_affine.contains(p1_swcurve_affine)); +} diff --git a/tooling/nargo_cli/tests/compile_success_empty/ret_fn_ret_cl/Nargo.toml b/test_programs/compile_success_empty/ret_fn_ret_cl/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/ret_fn_ret_cl/Nargo.toml rename to test_programs/compile_success_empty/ret_fn_ret_cl/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_success_empty/ret_fn_ret_cl/Prover.toml b/test_programs/compile_success_empty/ret_fn_ret_cl/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/ret_fn_ret_cl/Prover.toml rename to test_programs/compile_success_empty/ret_fn_ret_cl/Prover.toml diff --git a/test_programs/compile_success_empty/ret_fn_ret_cl/src/main.nr b/test_programs/compile_success_empty/ret_fn_ret_cl/src/main.nr new file mode 100644 index 00000000000..89083b076b6 --- /dev/null +++ b/test_programs/compile_success_empty/ret_fn_ret_cl/src/main.nr @@ -0,0 +1,33 @@ +fn f(x: Field) -> Field { + x + 1 +} + +fn ret_fn() -> fn(Field) -> Field { + f +} +// TODO: in the advanced implicitly generic function with closures branch +// which would support higher-order functions in a better way +// support returning closures: +// +// fn ret_closure() -> fn(Field) -> Field { +// let y = 1; +// let inner_closure = |z| -> Field{ +// z + y +// }; +// inner_closure +// } +fn ret_lambda() -> fn(Field) -> Field { + let cl = |z: Field| -> Field { + z + 1 + }; + cl +} + +fn main(x: Field) { + let result_fn = ret_fn(); + assert(result_fn(x) == x + 1); + // 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_array_param/Nargo.toml b/test_programs/compile_success_empty/simple_array_param/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/simple_array_param/Nargo.toml rename to test_programs/compile_success_empty/simple_array_param/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_success_empty/simple_array_param/Prover.toml b/test_programs/compile_success_empty/simple_array_param/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/simple_array_param/Prover.toml rename to test_programs/compile_success_empty/simple_array_param/Prover.toml diff --git a/test_programs/compile_success_empty/simple_array_param/src/main.nr b/test_programs/compile_success_empty/simple_array_param/src/main.nr new file mode 100644 index 00000000000..6dd7c34dab5 --- /dev/null +++ b/test_programs/compile_success_empty/simple_array_param/src/main.nr @@ -0,0 +1,6 @@ +// This program tests: +// - the allocation of virtual arrays for array params to main +// - load instructions for such arrays +fn main(xs: [Field; 2]) -> pub Field { + xs[1] +} diff --git a/tooling/nargo_cli/tests/compile_success_empty/simple_program_no_body/Nargo.toml b/test_programs/compile_success_empty/simple_program_no_body/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/simple_program_no_body/Nargo.toml rename to test_programs/compile_success_empty/simple_program_no_body/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_success_empty/simple_program_no_body/Prover.toml b/test_programs/compile_success_empty/simple_program_no_body/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/simple_program_no_body/Prover.toml rename to test_programs/compile_success_empty/simple_program_no_body/Prover.toml diff --git a/test_programs/compile_success_empty/simple_program_no_body/src/main.nr b/test_programs/compile_success_empty/simple_program_no_body/src/main.nr new file mode 100644 index 00000000000..21719018f3f --- /dev/null +++ b/test_programs/compile_success_empty/simple_program_no_body/src/main.nr @@ -0,0 +1,9 @@ +// Tests a very simple program. +// +// The features being tested are: +// - Abi generation of private and public +// main parameters. +// +// This program will never fail since there are +// no assertions being applied. +fn main(_x: Field, _y: pub Field) {} diff --git a/tooling/nargo_cli/tests/compile_success_empty/simple_range/Nargo.toml b/test_programs/compile_success_empty/simple_range/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/simple_range/Nargo.toml rename to test_programs/compile_success_empty/simple_range/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_success_empty/simple_range/Prover.toml b/test_programs/compile_success_empty/simple_range/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/simple_range/Prover.toml rename to test_programs/compile_success_empty/simple_range/Prover.toml diff --git a/test_programs/compile_success_empty/simple_range/src/main.nr b/test_programs/compile_success_empty/simple_range/src/main.nr new file mode 100644 index 00000000000..3f595cfd817 --- /dev/null +++ b/test_programs/compile_success_empty/simple_range/src/main.nr @@ -0,0 +1,6 @@ +// Tests a very simple program. +// +// The features being tested is casting to an integer +fn main(x: Field) { + let _z = x as u32; +} diff --git a/tooling/nargo_cli/tests/compile_success_empty/specialization/Nargo.toml b/test_programs/compile_success_empty/specialization/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/specialization/Nargo.toml rename to test_programs/compile_success_empty/specialization/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_success_empty/specialization/src/main.nr b/test_programs/compile_success_empty/specialization/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/specialization/src/main.nr rename to test_programs/compile_success_empty/specialization/src/main.nr diff --git a/tooling/nargo_cli/tests/compile_success_empty/str_as_bytes/Nargo.toml b/test_programs/compile_success_empty/str_as_bytes/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/str_as_bytes/Nargo.toml rename to test_programs/compile_success_empty/str_as_bytes/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_success_empty/str_as_bytes/src/main.nr b/test_programs/compile_success_empty/str_as_bytes/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/str_as_bytes/src/main.nr rename to test_programs/compile_success_empty/str_as_bytes/src/main.nr diff --git a/tooling/nargo_cli/tests/compile_success_empty/to_bits/Nargo.toml b/test_programs/compile_success_empty/to_bits/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/to_bits/Nargo.toml rename to test_programs/compile_success_empty/to_bits/Nargo.toml diff --git a/test_programs/compile_success_empty/to_bits/src/main.nr b/test_programs/compile_success_empty/to_bits/src/main.nr new file mode 100644 index 00000000000..84ace83903a --- /dev/null +++ b/test_programs/compile_success_empty/to_bits/src/main.nr @@ -0,0 +1,21 @@ +fn main() { + let field = 1000; + let be_bits = field.to_be_bits(16); + let le_bits = field.to_le_bits(16); + + for i in 0..16 { + let x = be_bits[i]; + let y = le_bits[15 - i]; + assert(x == y); + } + + let x = 3; + let be_bits_x = x.to_be_bits(4); + let le_bits_x = x.to_le_bits(4); + + for i in 0..4 { + let be_bit = be_bits_x[i]; + let le_bit = le_bits_x[3 - i]; + assert(be_bit == le_bit); + } +} diff --git a/tooling/nargo_cli/tests/compile_success_empty/trait_allowed_item_name_matches/Nargo.toml b/test_programs/compile_success_empty/trait_allowed_item_name_matches/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/trait_allowed_item_name_matches/Nargo.toml rename to test_programs/compile_success_empty/trait_allowed_item_name_matches/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_success_empty/trait_allowed_item_name_matches/Prover.toml b/test_programs/compile_success_empty/trait_allowed_item_name_matches/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/trait_allowed_item_name_matches/Prover.toml rename to test_programs/compile_success_empty/trait_allowed_item_name_matches/Prover.toml diff --git a/test_programs/compile_success_empty/trait_allowed_item_name_matches/src/main.nr b/test_programs/compile_success_empty/trait_allowed_item_name_matches/src/main.nr new file mode 100644 index 00000000000..44cad58c2a6 --- /dev/null +++ b/test_programs/compile_success_empty/trait_allowed_item_name_matches/src/main.nr @@ -0,0 +1,25 @@ +trait Trait1 { + // types and consts with the same name are allowed + type Tralala; + let Tralala: u32; +} + +trait Trait2 { + // consts and types with the same name are allowed + let Tralala: u32; + type Tralala; +} + +trait Trait3 { + // types and functions with the same name are allowed + type Tralala; + fn Tralala(); +} + +trait Trait4 { + // functions and types with the same name are allowed + fn Tralala(); + type Tralala; +} + +fn main() {} diff --git a/tooling/nargo_cli/tests/compile_success_empty/trait_associated_member_names_clashes/Nargo.toml b/test_programs/compile_success_empty/trait_associated_member_names_clashes/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/trait_associated_member_names_clashes/Nargo.toml rename to test_programs/compile_success_empty/trait_associated_member_names_clashes/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_success_empty/trait_associated_member_names_clashes/Prover.toml b/test_programs/compile_success_empty/trait_associated_member_names_clashes/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/trait_associated_member_names_clashes/Prover.toml rename to test_programs/compile_success_empty/trait_associated_member_names_clashes/Prover.toml diff --git a/tooling/nargo_cli/tests/compile_success_empty/trait_associated_member_names_clashes/src/main.nr b/test_programs/compile_success_empty/trait_associated_member_names_clashes/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/trait_associated_member_names_clashes/src/main.nr rename to test_programs/compile_success_empty/trait_associated_member_names_clashes/src/main.nr diff --git a/tooling/nargo_cli/tests/compile_success_empty/trait_default_implementation/Nargo.toml b/test_programs/compile_success_empty/trait_default_implementation/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/trait_default_implementation/Nargo.toml rename to test_programs/compile_success_empty/trait_default_implementation/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_success_empty/trait_default_implementation/Prover.toml b/test_programs/compile_success_empty/trait_default_implementation/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/trait_default_implementation/Prover.toml rename to test_programs/compile_success_empty/trait_default_implementation/Prover.toml diff --git a/tooling/nargo_cli/tests/compile_success_empty/trait_default_implementation/src/main.nr b/test_programs/compile_success_empty/trait_default_implementation/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/trait_default_implementation/src/main.nr rename to test_programs/compile_success_empty/trait_default_implementation/src/main.nr diff --git a/tooling/nargo_cli/tests/compile_success_empty/trait_function_calls/Nargo.toml b/test_programs/compile_success_empty/trait_function_calls/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/trait_function_calls/Nargo.toml rename to test_programs/compile_success_empty/trait_function_calls/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_success_empty/trait_function_calls/Prover.toml b/test_programs/compile_success_empty/trait_function_calls/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/trait_function_calls/Prover.toml rename to test_programs/compile_success_empty/trait_function_calls/Prover.toml diff --git a/test_programs/compile_success_empty/trait_function_calls/src/main.nr b/test_programs/compile_success_empty/trait_function_calls/src/main.nr new file mode 100644 index 00000000000..39d28a5a3b3 --- /dev/null +++ b/test_programs/compile_success_empty/trait_function_calls/src/main.nr @@ -0,0 +1,615 @@ +// a more thorough test for trait function/method calls from within trait function/method calls +// This tests all combinations of caller+callee pairs. For each of these, the following combination of properties are tried: +// *) method (has self parameter) vs function (no self parameter) +// *) default vs overriden vs overriden (no default) +// test order is: +// 1) trait method -> trait method +// 1a) trait default method -> trait default method +// 1b) trait default method -> trait overriden method +// 1c) trait default method -> trait overriden (no default) method +// 1d) trait overriden method -> trait default method +// 1e) trait overriden method -> trait overriden method +// 1f) trait overriden method -> trait overriden (no default) method +// 1g) trait overriden (no default) method -> trait default method +// 1h) trait overriden (no default) method -> trait overriden method +// 1i) trait overriden (no default) method -> trait overriden (no default) method +// 2) trait method -> trait function +// 2a) - subcases are the same as the above +// ... +// 2i) +// 3{a..i}) trait function -> trait method +// 4{a..i}) trait function -> trait function +// 1) trait method -> trait method +// 1a) trait default method -> trait default method +trait Trait1a { + fn trait_method1(self) -> Field { + self.trait_method2() * 7892 - self.vl + } + fn trait_method2(self) -> Field { + 43278 + } +} +struct Struct1a { vl: Field } +impl Trait1a for Struct1a { } +// 1b) trait default method -> trait overriden method +trait Trait1b { + fn trait_method1(self) -> Field { + self.trait_method2() * 2832 - self.vl + } + fn trait_method2(self) -> Field { + 9323 + } +} +struct Struct1b { vl: Field } +impl Trait1b for Struct1b { + fn trait_method2(self) -> Field { + 2394 + } +} +// 1c) trait default method -> trait overriden (no default) method +trait Trait1c { + fn trait_method1(self) -> Field { + self.trait_method2() * 7635 - self.vl + } + fn trait_method2(self) -> Field; +} +struct Struct1c { vl: Field } +impl Trait1c for Struct1c { + fn trait_method2(self) -> Field { + 5485 + } +} +// 1d) trait overriden method -> trait default method +trait Trait1d { + fn trait_method1(self) -> Field { + self.trait_method2() * 2825 - self.vl + } + fn trait_method2(self) -> Field { + 29341 + } +} +struct Struct1d { vl: Field } +impl Trait1d for Struct1d { + fn trait_method1(self) -> Field { + self.trait_method2() * 9342 - self.vl + } +} +// 1e) trait overriden method -> trait overriden method +trait Trait1e { + fn trait_method1(self) -> Field { + self.trait_method2() * 85465 - self.vl + } + fn trait_method2(self) -> Field { + 2381 + } +} +struct Struct1e { vl: Field } +impl Trait1e for Struct1e { + fn trait_method1(self) -> Field { + self.trait_method2() * 47324 - self.vl + } + fn trait_method2(self) -> Field { + 58945 + } +} +// 1f) trait overriden method -> trait overriden (no default) method +trait Trait1f { + fn trait_method1(self) -> Field { + self.trait_method2() * 43257 - self.vl + } + fn trait_method2(self) -> Field; +} +struct Struct1f { vl: Field } +impl Trait1f for Struct1f { + fn trait_method1(self) -> Field { + self.trait_method2() * 34875 - self.vl + } + fn trait_method2(self) -> Field { + 5748 + } +} +// 1g) trait overriden (no default) method -> trait default method +trait Trait1g { + fn trait_method1(self) -> Field; + fn trait_method2(self) -> Field { + 37845 + } +} +struct Struct1g { vl: Field } +impl Trait1g for Struct1g { + fn trait_method1(self) -> Field { + self.trait_method2() * 7854 - self.vl + } +} +// 1h) trait overriden (no default) method -> trait overriden method +trait Trait1h { + fn trait_method1(self) -> Field; + fn trait_method2(self) -> Field { + 7823 + } +} +struct Struct1h { vl: Field } +impl Trait1h for Struct1h { + fn trait_method1(self) -> Field { + self.trait_method2() * 3482 - self.vl + } + fn trait_method2(self) -> Field { + 8542 + } +} +// 1i) trait overriden (no default) method -> trait overriden (no default) method +trait Trait1i { + fn trait_method1(self) -> Field; + fn trait_method2(self) -> Field; +} +struct Struct1i { vl: Field } +impl Trait1i for Struct1i { + fn trait_method1(self) -> Field { + self.trait_method2() * 23478 - self.vl + } + fn trait_method2(self) -> Field { + 98543 + } +} +// 2) trait method -> trait function +// 2a) trait default method -> trait default function +trait Trait2a { + fn trait_method1(self) -> Field { + Self::trait_function2() * 2385 - self.vl + } + fn trait_function2() -> Field { + 7843 + } +} +struct Struct2a { vl: Field } +impl Trait2a for Struct2a { } +// 2b) trait default method -> trait overriden function +trait Trait2b { + fn trait_method1(self) -> Field { + Self::trait_function2() * 6583 - self.vl + } + fn trait_function2() -> Field { + 3752 + } +} +struct Struct2b { vl: Field } +impl Trait2b for Struct2b { + fn trait_function2() -> Field { + 8477 + } +} +// 2c) trait default method -> trait overriden (no default) function +trait Trait2c { + fn trait_method1(self) -> Field { + Self::trait_function2() * 2831 - self.vl + } + fn trait_function2() -> Field; +} +struct Struct2c { vl: Field } +impl Trait2c for Struct2c { + fn trait_function2() -> Field { + 8342 + } +} +// 2d) trait overriden method -> trait default function +trait Trait2d { + fn trait_method1(self) -> Field { + Self::trait_function2() * 924 - self.vl + } + fn trait_function2() -> Field { + 384 + } +} +struct Struct2d { vl: Field } +impl Trait2d for Struct2d { + fn trait_method1(self) -> Field { + Self::trait_function2() * 3984 - self.vl + } +} +// 2e) trait overriden method -> trait overriden function +trait Trait2e { + fn trait_method1(self) -> Field { + Self::trait_function2() * 3642 - self.vl + } + fn trait_function2() -> Field { + 97342 + } +} +struct Struct2e { vl: Field } +impl Trait2e for Struct2e { + fn trait_method1(self) -> Field { + Self::trait_function2() * 7363 - self.vl + } + fn trait_function2() -> Field { + 39400 + } +} +// 2f) trait overriden method -> trait overriden (no default) function +trait Trait2f { + fn trait_method1(self) -> Field { + Self::trait_function2() * 2783 - self.vl + } + fn trait_function2() -> Field; +} +struct Struct2f { vl: Field } +impl Trait2f for Struct2f { + fn trait_method1(self) -> Field { + Self::trait_function2() * 6362 - self.vl + } + fn trait_function2() -> Field { + 72311 + } +} +// 2g) trait overriden (no default) method -> trait default function +trait Trait2g { + fn trait_method1(self) -> Field; + fn trait_function2() -> Field { + 19273 + } +} +struct Struct2g { vl: Field } +impl Trait2g for Struct2g { + fn trait_method1(self) -> Field { + Self::trait_function2() * 9123 - self.vl + } +} +// 2h) trait overriden (no default) method -> trait overriden function +trait Trait2h { + fn trait_method1(self) -> Field; + fn trait_function2() -> Field { + 1281 + } +} +struct Struct2h { vl: Field } +impl Trait2h for Struct2h { + fn trait_method1(self) -> Field { + Self::trait_function2() * 4833 - self.vl + } + fn trait_function2() -> Field { + 5335 + } +} +// 2i) trait overriden (no default) method -> trait overriden (no default) function +trait Trait2i { + fn trait_method1(self) -> Field; + fn trait_function2() -> Field; +} +struct Struct2i { vl: Field } +impl Trait2i for Struct2i { + fn trait_method1(self) -> Field { + Self::trait_function2() * 2291 - self.vl + } + fn trait_function2() -> Field { + 3322 + } +} +// 3 trait function -> trait method +// 3a) trait default function -> trait default method +trait Trait3a { + fn trait_function1(a: Field, b: Self) -> Field { + b.trait_method2() * 8344 - b.vl + a + } + fn trait_method2(self) -> Field { + 19212 + } +} +struct Struct3a { vl: Field } +impl Trait3a for Struct3a { } +// 3b) trait default function -> trait overriden method +trait Trait3b { + fn trait_function1(a: Field, b: Self) -> Field { + b.trait_method2() * 9233 - b.vl + a + } + fn trait_method2(self) -> Field { + 9111 + } +} +struct Struct3b { vl: Field } +impl Trait3b for Struct3b { + fn trait_method2(self) -> Field { + 2392 + } +} +// 3c) trait default function -> trait overriden (no default) method +trait Trait3c { + fn trait_function1(a: Field, b: Self) -> Field { + b.trait_method2() * 2822 - b.vl + a + } + fn trait_method2(self) -> Field; +} +struct Struct3c { vl: Field } +impl Trait3c for Struct3c { + fn trait_method2(self) -> Field { + 7743 + } +} +// 3d) trait overriden function -> trait default method +trait Trait3d { + fn trait_function1(a: Field, b: Self) -> Field { + b.trait_method2() * 291 - b.vl + a + } + fn trait_method2(self) -> Field { + 3328 + } +} +struct Struct3d { vl: Field } +impl Trait3d for Struct3d { + fn trait_function1(a: Field, b: Self) -> Field { + b.trait_method2() * 4933 - b.vl + a + } +} +// 3e) trait overriden function -> trait overriden method +trait Trait3e { + fn trait_function1(a: Field, b: Self) -> Field { + b.trait_method2() * 71231 - b.vl + a + } + fn trait_method2(self) -> Field { + 373 + } +} +struct Struct3e { vl: Field } +impl Trait3e for Struct3e { + fn trait_function1(a: Field, b: Self) -> Field { + b.trait_method2() * 81232 - b.vl + a + } + fn trait_method2(self) -> Field { + 80002 + } +} +// 3f) trait overriden function -> trait overriden (no default) method +trait Trait3f { + fn trait_function1(a: Field, b: Self) -> Field { + b.trait_method2() * 28223 - b.vl + a + } + fn trait_method2(self) -> Field; +} +struct Struct3f { vl: Field } +impl Trait3f for Struct3f { + fn trait_function1(a: Field, b: Self) -> Field { + b.trait_method2() * 29223 - b.vl + a + } + fn trait_method2(self) -> Field { + 63532 + } +} +// 3g) trait overriden (no default) function -> trait default method +trait Trait3g { + fn trait_function1(a: Field, b: Self) -> Field; + fn trait_method2(self) -> Field { + 8887 + } +} +struct Struct3g { vl: Field } +impl Trait3g for Struct3g { + fn trait_function1(a: Field, b: Self) -> Field { + b.trait_method2() * 31337 - b.vl + a + } +} +// 3h) trait overriden (no default) function -> trait overriden method +trait Trait3h { + fn trait_function1(a: Field, b: Self) -> Field; + fn trait_method2(self) -> Field { + 293 + } +} +struct Struct3h { vl: Field } +impl Trait3h for Struct3h { + fn trait_function1(a: Field, b: Self) -> Field { + b.trait_method2() * 74747 - b.vl + a + } + fn trait_method2(self) -> Field { + 6283 + } +} +// 3i) trait overriden (no default) function -> trait overriden (no default) method +trait Trait3i { + fn trait_function1(a: Field, b: Self) -> Field; + fn trait_method2(self) -> Field; +} +struct Struct3i { vl: Field } +impl Trait3i for Struct3i { + fn trait_function1(a: Field, b: Self) -> Field { + b.trait_method2() * 1237 - b.vl + a + } + fn trait_method2(self) -> Field { + 84352 + } +} +// 4) trait function -> trait function +// 4a) trait default function -> trait default function +trait Trait4a { + fn trait_function1() -> Field { + Self::trait_function2() * 3842 + } + fn trait_function2() -> Field { + 2932 + } +} +struct Struct4a { vl: Field } +impl Trait4a for Struct4a { } +// 4b) trait default function -> trait overriden function +trait Trait4b { + fn trait_function1() -> Field { + Self::trait_function2() * 3842 + } + fn trait_function2() -> Field { + 2932 + } +} +struct Struct4b { vl: Field } +impl Trait4b for Struct4b { + fn trait_function2() -> Field { + 9353 + } +} +// 4c) trait default function -> trait overriden (no default) function +trait Trait4c { + fn trait_function1() -> Field { + Self::trait_function2() * 7832 + } + fn trait_function2() -> Field; +} +struct Struct4c { vl: Field } +impl Trait4c for Struct4c { + fn trait_function2() -> Field { + 2928 + } +} +// 4d) trait overriden function -> trait default function +trait Trait4d { + fn trait_function1() -> Field { + Self::trait_function2() * 2283 + } + fn trait_function2() -> Field { + 9332 + } +} +struct Struct4d { vl: Field } +impl Trait4d for Struct4d { + fn trait_function1() -> Field { + Self::trait_function2() * 8374 + } +} +// 4e) trait overriden function -> trait overriden function +trait Trait4e { + fn trait_function1() -> Field { + Self::trait_function2() * 94329 + } + fn trait_function2() -> Field { + 28328 + } +} +struct Struct4e { vl: Field } +impl Trait4e for Struct4e { + fn trait_function1() -> Field { + Self::trait_function2() * 12323 + } + fn trait_function2() -> Field { + 38434 + } +} +// 4f) trait overriden function -> trait overriden (no default) function +trait Trait4f { + fn trait_function1() -> Field { + Self::trait_function2() * 23723 + } + fn trait_function2() -> Field; +} +struct Struct4f { vl: Field } +impl Trait4f for Struct4f { + fn trait_function1() -> Field { + Self::trait_function2() * 21392 + } + fn trait_function2() -> Field { + 4394 + } +} +// 4g) trait overriden (no default) function -> trait default function +trait Trait4g { + fn trait_function1() -> Field; + fn trait_function2() -> Field { + 2932 + } +} +struct Struct4g { vl: Field } +impl Trait4g for Struct4g { + fn trait_function1() -> Field { + Self::trait_function2() * 3345 + } +} +// 4h) trait overriden (no default) function -> trait overriden function +trait Trait4h { + fn trait_function1() -> Field; + fn trait_function2() -> Field { + 5756 + } +} +struct Struct4h { vl: Field } +impl Trait4h for Struct4h { + fn trait_function1() -> Field { + Self::trait_function2() * 6478 + } + fn trait_function2() -> Field { + 5435 + } +} +// 4i) trait overriden (no default) function -> trait overriden (no default) function +trait Trait4i { + fn trait_function1() -> Field; + fn trait_function2() -> Field; +} +struct Struct4i { vl: Field } +impl Trait4i for Struct4i { + fn trait_function1() -> Field { + Self::trait_function2() * 8239 + } + fn trait_function2() -> Field { + 2032 + } +} + +fn main() { + let t1a = Struct1a { vl: 1234 }; + assert(t1a.trait_method1() == 341548742); + let t1b = Struct1b { vl: 4444 }; + assert(t1b.trait_method1() == 6775364); + let t1c = Struct1c { vl: 3946 }; + assert(t1c.trait_method1() == 41874029); + let t1d = Struct1d { vl: 9234 }; + assert(t1d.trait_method1() == 274094388); + let t1e = Struct1e { vl: 5438 }; + assert(t1e.trait_method1() == 2789507742); + let t1f = Struct1f { vl: 6237 }; + assert(t1f.trait_method1() == 200455263); + let t1g = Struct1g { vl: 43587 }; + assert(t1g.trait_method1() == 297191043); + let t1h = Struct1h { vl: 3984 }; + assert(t1h.trait_method1() == 29739260); + let t1i = Struct1i { vl: 9234 }; + assert(t1i.trait_method1() == 2313583320); + let t2a = Struct2a { vl: 4362 }; + assert(t2a.trait_method1() == 18701193); + let t2b = Struct2b { vl: 8347 }; + assert(t2b.trait_method1() == 55795744); + let t2c = Struct2c { vl: 1923 }; + assert(t2c.trait_method1() == 23614279); + let t2d = Struct2d { vl: 92384 }; + assert(t2d.trait_method1() == 1437472); + let t2e = Struct2e { vl: 83943 }; + assert(t2e.trait_method1() == 290018257); + let t2f = Struct2f { vl: 8237 }; + assert(t2f.trait_method1() == 460034345); + let t2g = Struct2g { vl: 1232 }; + assert(t2g.trait_method1() == 175826347); + let t2h = Struct2h { vl: 7222 }; + assert(t2h.trait_method1() == 25776833); + let t2i = Struct2i { vl: 1821 }; + assert(t2i.trait_method1() == 7608881); + let t3a = Struct3a { vl: 93248 }; + assert(Struct3a::trait_function1(5, t3a) == 160211685); + let t3b = Struct3b { vl: 76763 }; + assert(Struct3b::trait_function1(62, t3b) == 22008635); + let t3c = Struct3c { vl: 3833 }; + assert(Struct3c::trait_function1(25, t3c) == 21846938); + let t3d = Struct3d { vl: 5645 }; + assert(Struct3d::trait_function1(73, t3d) == 16411452); + let t3e = Struct3e { vl: 22912 }; + assert(Struct3e::trait_function1(92, t3e) == 6498699644); + let t3f = Struct3f { vl: 3256 }; + assert(Struct3f::trait_function1(77, t3f) == 1856592457); + let t3g = Struct3g { vl: 22832 }; + assert(Struct3g::trait_function1(23, t3g) == 278469110); + let t3h = Struct3h { vl: 4933 }; + assert(Struct3h::trait_function1(17, t3h) == 469630485); + let t3i = Struct3i { vl: 39432 }; + assert(Struct3i::trait_function1(54, t3i) == 104304046); + assert(Struct4a::trait_function1() == 11264744); + assert(Struct4b::trait_function1() == 35934226); + assert(Struct4c::trait_function1() == 22932096); + assert(Struct4d::trait_function1() == 78146168); + assert(Struct4e::trait_function1() == 473622182); + assert(Struct4f::trait_function1() == 93996448); + assert(Struct4g::trait_function1() == 9807540); + assert(Struct4h::trait_function1() == 35207930); + assert(Struct4i::trait_function1() == 16741648); +} diff --git a/tooling/nargo_cli/tests/compile_success_empty/trait_generics/Nargo.toml b/test_programs/compile_success_empty/trait_generics/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/trait_generics/Nargo.toml rename to test_programs/compile_success_empty/trait_generics/Nargo.toml diff --git a/test_programs/compile_success_empty/trait_generics/src/main.nr b/test_programs/compile_success_empty/trait_generics/src/main.nr new file mode 100644 index 00000000000..bb6d6e74726 --- /dev/null +++ b/test_programs/compile_success_empty/trait_generics/src/main.nr @@ -0,0 +1,59 @@ +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); + + call_impl_with_generic_struct(); + call_impl_with_generic_function(); +} +// Ensure we can call a generic impl +fn call_impl_with_generic_struct() { + 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 } +} + +fn call_impl_with_generic_function() { + assert(3.t3(7) == 7); +} + +trait T3 { + fn t3(self, x: T) -> T; +} + +impl T3 for u32 { + fn t3(self, y: U) -> U { y } +} diff --git a/tooling/nargo_cli/tests/compile_success_empty/trait_multi_module_test/Nargo.toml b/test_programs/compile_success_empty/trait_multi_module_test/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/trait_multi_module_test/Nargo.toml rename to test_programs/compile_success_empty/trait_multi_module_test/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_success_empty/trait_multi_module_test/Prover.toml b/test_programs/compile_success_empty/trait_multi_module_test/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/trait_multi_module_test/Prover.toml rename to test_programs/compile_success_empty/trait_multi_module_test/Prover.toml diff --git a/tooling/nargo_cli/tests/compile_success_empty/trait_multi_module_test/src/main.nr b/test_programs/compile_success_empty/trait_multi_module_test/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/trait_multi_module_test/src/main.nr rename to test_programs/compile_success_empty/trait_multi_module_test/src/main.nr diff --git a/tooling/nargo_cli/tests/compile_success_empty/trait_multi_module_test/src/module1.nr b/test_programs/compile_success_empty/trait_multi_module_test/src/module1.nr similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/trait_multi_module_test/src/module1.nr rename to test_programs/compile_success_empty/trait_multi_module_test/src/module1.nr diff --git a/tooling/nargo_cli/tests/compile_success_empty/trait_multi_module_test/src/module2.nr b/test_programs/compile_success_empty/trait_multi_module_test/src/module2.nr similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/trait_multi_module_test/src/module2.nr rename to test_programs/compile_success_empty/trait_multi_module_test/src/module2.nr diff --git a/test_programs/compile_success_empty/trait_multi_module_test/src/module3.nr b/test_programs/compile_success_empty/trait_multi_module_test/src/module3.nr new file mode 100644 index 00000000000..2485a2ba7a1 --- /dev/null +++ b/test_programs/compile_success_empty/trait_multi_module_test/src/module3.nr @@ -0,0 +1,4 @@ +use crate::module1::MyTrait; +use crate::module2::MyStruct; +// ensure we can implement traits that are imported with the `use` syntax +impl MyTrait for MyStruct {} diff --git a/tooling/nargo_cli/tests/compile_success_empty/trait_multi_module_test/src/module4.nr b/test_programs/compile_success_empty/trait_multi_module_test/src/module4.nr similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/trait_multi_module_test/src/module4.nr rename to test_programs/compile_success_empty/trait_multi_module_test/src/module4.nr diff --git a/tooling/nargo_cli/tests/compile_success_empty/trait_multi_module_test/src/module5.nr b/test_programs/compile_success_empty/trait_multi_module_test/src/module5.nr similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/trait_multi_module_test/src/module5.nr rename to test_programs/compile_success_empty/trait_multi_module_test/src/module5.nr diff --git a/tooling/nargo_cli/tests/compile_success_empty/trait_multi_module_test/src/module6.nr b/test_programs/compile_success_empty/trait_multi_module_test/src/module6.nr similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/trait_multi_module_test/src/module6.nr rename to test_programs/compile_success_empty/trait_multi_module_test/src/module6.nr diff --git a/tooling/nargo_cli/tests/compile_success_empty/trait_override_implementation/Nargo.toml b/test_programs/compile_success_empty/trait_override_implementation/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/trait_override_implementation/Nargo.toml rename to test_programs/compile_success_empty/trait_override_implementation/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_success_empty/trait_override_implementation/Prover.toml b/test_programs/compile_success_empty/trait_override_implementation/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/trait_override_implementation/Prover.toml rename to test_programs/compile_success_empty/trait_override_implementation/Prover.toml diff --git a/test_programs/compile_success_empty/trait_override_implementation/src/main.nr b/test_programs/compile_success_empty/trait_override_implementation/src/main.nr new file mode 100644 index 00000000000..a385efc63fd --- /dev/null +++ b/test_programs/compile_success_empty/trait_override_implementation/src/main.nr @@ -0,0 +1,71 @@ +use dep::std; + +trait Default { + fn default(x: Field, y: Field) -> Self; + + fn method2(x: Field) -> Field { + x + } +} + +struct Foo { + bar: Field, + array: [Field; 2], +} + +impl Default for Foo { + fn default(x: Field,y: Field) -> Self { + Self { bar: x, array: [x,y] } + } + + fn method2(x: Field) -> Field { + x * 3 + } +} + +trait F { + fn f1(self) -> Field; + fn f2(self) -> Field { 2 } + fn f3(self) -> Field { 3 } + fn f4(self) -> Field { 4 } + fn f5(self) -> Field { 5 } +} + +struct Bar {} + +impl F for Bar { + fn f5(self) -> Field { 50 } + fn f1(self) -> Field { 10 } + fn f3(self) -> Field { 30 } +} +// 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, "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 {}; + // 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/trait_self/Nargo.toml b/test_programs/compile_success_empty/trait_static_methods/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/trait_self/Nargo.toml rename to test_programs/compile_success_empty/trait_static_methods/Nargo.toml diff --git a/test_programs/compile_success_empty/trait_static_methods/src/main.nr b/test_programs/compile_success_empty/trait_static_methods/src/main.nr new file mode 100644 index 00000000000..0150da68315 --- /dev/null +++ b/test_programs/compile_success_empty/trait_static_methods/src/main.nr @@ -0,0 +1,41 @@ +trait ATrait { + fn asd() -> Self; + + fn static_method() -> Field { + Self::static_method_2() + } + + fn static_method_2() -> Field { + 100 + } +} + +struct Foo { + x: Field +} +impl ATrait for Foo { + fn asd() -> Self { + // This should pass as Self should be bound to Foo while typechecking this + Foo{x: 100} + } +} + +struct Bar { + x: Field +} +impl ATrait for Bar { + // The trait method is declared as returning `Self` + // but explicitly specifying the type in the impl should work + fn asd() -> Bar { + Bar{x: 100} + } + + fn static_method_2() -> Field { + 200 + } +} + +fn main() { + assert(Foo::static_method() == 100); + assert(Bar::static_method() == 200); +} diff --git a/tooling/nargo_cli/tests/compile_success_empty/trait_where_clause/Nargo.toml b/test_programs/compile_success_empty/trait_where_clause/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/trait_where_clause/Nargo.toml rename to test_programs/compile_success_empty/trait_where_clause/Nargo.toml diff --git a/test_programs/compile_success_empty/trait_where_clause/src/main.nr b/test_programs/compile_success_empty/trait_where_clause/src/main.nr new file mode 100644 index 00000000000..5fd7e78abf4 --- /dev/null +++ b/test_programs/compile_success_empty/trait_where_clause/src/main.nr @@ -0,0 +1,56 @@ +// TODO(#2568): Currently we only support trait constraints in a few cases. +// There's a bunch of other places where they can pop up: +// - trait methods (trait Foo where T: ... { ) +// - structs (struct Foo where T: ...) +// import the traits from another module to ensure the where clauses are ok with that +mod the_trait; +use crate::the_trait::Asd; +use crate::the_trait::StaticTrait; + +struct Add10 { x: Field, } +struct Add20 { x: Field, } +struct Add30 { x: Field, } +struct AddXY { x: Field, y: Field, } + +impl Asd for Add10 { fn asd(self) -> Field { self.x + 10 } } +impl Asd for Add20 { fn asd(self) -> Field { self.x + 20 } } +impl Asd for Add30 { fn asd(self) -> Field { self.x + 30 } } + +impl Asd for AddXY { + fn asd(self) -> Field { + self.x + self.y + } +} + +struct Static100 {} +impl StaticTrait for Static100 { + // use default implementation for static_function, which returns 100 +} + +struct Static200 {} +impl StaticTrait for Static200 { + fn static_function(slf: Self) -> Field { 200 } +} + +fn assert_asd_eq_100(t: T) where T: crate::the_trait::Asd { + assert(t.asd() == 100); +} + +fn add_one_to_static_function(t: T) -> Field where T: StaticTrait { + T::static_function(t) + 1 +} + +fn main() { + let x = Add10 { x: 90 }; + let z = Add20 { x: 80 }; + let a = Add30 { x: 70 }; + let xy = AddXY { x: 30, y: 70 }; + + assert_asd_eq_100(x); + assert_asd_eq_100(z); + assert_asd_eq_100(a); + assert_asd_eq_100(xy); + + assert(add_one_to_static_function(Static100 {}) == 101); + assert(add_one_to_static_function(Static200 {}) == 201); +} diff --git a/tooling/nargo_cli/tests/compile_success_empty/trait_where_clause/src/the_trait.nr b/test_programs/compile_success_empty/trait_where_clause/src/the_trait.nr similarity index 98% rename from tooling/nargo_cli/tests/compile_success_empty/trait_where_clause/src/the_trait.nr rename to test_programs/compile_success_empty/trait_where_clause/src/the_trait.nr index d84210c4b44..c5cac4a1186 100644 --- a/tooling/nargo_cli/tests/compile_success_empty/trait_where_clause/src/the_trait.nr +++ b/test_programs/compile_success_empty/trait_where_clause/src/the_trait.nr @@ -6,4 +6,4 @@ trait StaticTrait { fn static_function(slf: Self) -> Field { 100 } -} \ No newline at end of file +} diff --git a/tooling/nargo_cli/tests/compile_success_empty/traits/Nargo.toml b/test_programs/compile_success_empty/traits/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/traits/Nargo.toml rename to test_programs/compile_success_empty/traits/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_success_empty/traits/Prover.toml b/test_programs/compile_success_empty/traits/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/traits/Prover.toml rename to test_programs/compile_success_empty/traits/Prover.toml diff --git a/test_programs/compile_success_empty/traits/src/main.nr b/test_programs/compile_success_empty/traits/src/main.nr new file mode 100644 index 00000000000..784ff01a883 --- /dev/null +++ b/test_programs/compile_success_empty/traits/src/main.nr @@ -0,0 +1,21 @@ +use dep::std; + +trait Default { + fn default(x: Field, y: Field) -> Self; +} + +struct Foo { + bar: Field, + array: [Field; 2], +} + +impl Default for Foo { + fn default(x: Field,y: Field) -> Self { + Self { bar: x, array: [x,y] } + } +} + +fn main(x: Field, y: Field) { + let first = Foo::default(x, y); + assert(first.bar == x); +} diff --git a/tooling/nargo_cli/tests/compile_success_empty/unary_operators/Nargo.toml b/test_programs/compile_success_empty/unary_operators/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/unary_operators/Nargo.toml rename to test_programs/compile_success_empty/unary_operators/Nargo.toml diff --git a/test_programs/compile_success_empty/unary_operators/src/main.nr b/test_programs/compile_success_empty/unary_operators/src/main.nr new file mode 100644 index 00000000000..ef622fd3eb9 --- /dev/null +++ b/test_programs/compile_success_empty/unary_operators/src/main.nr @@ -0,0 +1,7 @@ +fn main() { + let x = -1; + assert(x == 1 - 2); + + let y: i32 = -1; + assert(x == 1 - 2); +} diff --git a/tooling/nargo_cli/tests/compile_success_empty/unconstrained_empty/Nargo.toml b/test_programs/compile_success_empty/unconstrained_empty/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/unconstrained_empty/Nargo.toml rename to test_programs/compile_success_empty/unconstrained_empty/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_success_empty/unconstrained_empty/src/main.nr b/test_programs/compile_success_empty/unconstrained_empty/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/unconstrained_empty/src/main.nr rename to test_programs/compile_success_empty/unconstrained_empty/src/main.nr diff --git a/tooling/nargo_cli/tests/compile_success_empty/unit/Nargo.toml b/test_programs/compile_success_empty/unit/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/unit/Nargo.toml rename to test_programs/compile_success_empty/unit/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_success_empty/unit/src/main.nr b/test_programs/compile_success_empty/unit/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/unit/src/main.nr rename to test_programs/compile_success_empty/unit/src/main.nr diff --git a/tooling/nargo_cli/tests/compile_success_empty/unused_variables/Nargo.toml b/test_programs/compile_success_empty/unused_variables/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/unused_variables/Nargo.toml rename to test_programs/compile_success_empty/unused_variables/Nargo.toml diff --git a/test_programs/compile_success_empty/unused_variables/src/main.nr b/test_programs/compile_success_empty/unused_variables/src/main.nr new file mode 100644 index 00000000000..f82cace0509 --- /dev/null +++ b/test_programs/compile_success_empty/unused_variables/src/main.nr @@ -0,0 +1 @@ +fn main(x: Field, y: pub Field) {} diff --git a/tooling/nargo_cli/tests/compile_success_empty/vectors/Nargo.toml b/test_programs/compile_success_empty/vectors/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/vectors/Nargo.toml rename to test_programs/compile_success_empty/vectors/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_success_empty/vectors/Prover.toml b/test_programs/compile_success_empty/vectors/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/vectors/Prover.toml rename to test_programs/compile_success_empty/vectors/Prover.toml diff --git a/tooling/nargo_cli/tests/compile_success_empty/vectors/src/main.nr b/test_programs/compile_success_empty/vectors/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/vectors/src/main.nr rename to test_programs/compile_success_empty/vectors/src/main.nr diff --git a/tooling/nargo_cli/tests/compile_success_empty/workspace_reexport_bug/Nargo.toml b/test_programs/compile_success_empty/workspace_reexport_bug/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/workspace_reexport_bug/Nargo.toml rename to test_programs/compile_success_empty/workspace_reexport_bug/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_success_empty/workspace_reexport_bug/binary/Nargo.toml b/test_programs/compile_success_empty/workspace_reexport_bug/binary/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/workspace_reexport_bug/binary/Nargo.toml rename to test_programs/compile_success_empty/workspace_reexport_bug/binary/Nargo.toml diff --git a/test_programs/compile_success_empty/workspace_reexport_bug/binary/src/main.nr b/test_programs/compile_success_empty/workspace_reexport_bug/binary/src/main.nr new file mode 100644 index 00000000000..ab0ae9a48b8 --- /dev/null +++ b/test_programs/compile_success_empty/workspace_reexport_bug/binary/src/main.nr @@ -0,0 +1,2 @@ +use dep::library::ReExportMeFromAnotherLib; +fn main(_x: ReExportMeFromAnotherLib) {} diff --git a/tooling/nargo_cli/tests/compile_success_empty/workspace_reexport_bug/library/Nargo.toml b/test_programs/compile_success_empty/workspace_reexport_bug/library/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/workspace_reexport_bug/library/Nargo.toml rename to test_programs/compile_success_empty/workspace_reexport_bug/library/Nargo.toml diff --git a/test_programs/compile_success_empty/workspace_reexport_bug/library/src/lib.nr b/test_programs/compile_success_empty/workspace_reexport_bug/library/src/lib.nr new file mode 100644 index 00000000000..8e84662ed03 --- /dev/null +++ b/test_programs/compile_success_empty/workspace_reexport_bug/library/src/lib.nr @@ -0,0 +1,2 @@ +// Re-export +use dep::library2::ReExportMeFromAnotherLib; diff --git a/tooling/nargo_cli/tests/compile_success_empty/workspace_reexport_bug/library2/Nargo.toml b/test_programs/compile_success_empty/workspace_reexport_bug/library2/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_success_empty/workspace_reexport_bug/library2/Nargo.toml rename to test_programs/compile_success_empty/workspace_reexport_bug/library2/Nargo.toml diff --git a/test_programs/compile_success_empty/workspace_reexport_bug/library2/src/lib.nr b/test_programs/compile_success_empty/workspace_reexport_bug/library2/src/lib.nr new file mode 100644 index 00000000000..7e5a29a1424 --- /dev/null +++ b/test_programs/compile_success_empty/workspace_reexport_bug/library2/src/lib.nr @@ -0,0 +1,5 @@ +// When we re-export this type from another library and then use it in +// main, we get a panic +struct ReExportMeFromAnotherLib { + x : Field, +} diff --git a/tooling/nargo_cli/tests/execution_success/1327_concrete_in_generic/Nargo.toml b/test_programs/execution_success/1327_concrete_in_generic/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/1327_concrete_in_generic/Nargo.toml rename to test_programs/execution_success/1327_concrete_in_generic/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/1327_concrete_in_generic/Prover.toml b/test_programs/execution_success/1327_concrete_in_generic/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/1327_concrete_in_generic/Prover.toml rename to test_programs/execution_success/1327_concrete_in_generic/Prover.toml diff --git a/test_programs/execution_success/1327_concrete_in_generic/src/main.nr b/test_programs/execution_success/1327_concrete_in_generic/src/main.nr new file mode 100644 index 00000000000..e1d601b13c9 --- /dev/null +++ b/test_programs/execution_success/1327_concrete_in_generic/src/main.nr @@ -0,0 +1,64 @@ +// --- +fn new_concrete_c_over_d() -> C { + let d_method_interface = get_d_method_interface(); + C::new(d_method_interface) +} +// --- +// Map +struct B { + new_concrete_t_c_constructor: fn()->T_C, + } + +impl B { + fn new(new_concrete_t_c_constructor: fn () -> T_C) -> B { + B { new_concrete_t_c_constructor } + } + + fn get_t_c(self) -> T_C { + let new_concrete_t_c_constructor = self.new_concrete_t_c_constructor; + new_concrete_t_c_constructor() + } + } +// --- +// Set +struct C { + t_d_interface: MethodInterface, + } + +impl C { + fn new (t_d_interface: MethodInterface) -> Self { + C { t_d_interface } + } + + fn call_method_of_t_d(self, t_d: T_D) -> Field { + let some_method_on_t_d = self.t_d_interface.some_method_on_t_d; + some_method_on_t_d(t_d) + } + } +// --- +struct MethodInterface { + some_method_on_t_d: fn(T_D)->Field, + } +// --- +// Note +struct D { + d: Field, + } + +fn d_method(input: D) -> Field { + input.d * input.d +} + +fn get_d_method_interface() -> MethodInterface { + MethodInterface { some_method_on_t_d: d_method } +} +// --- +fn main(input: Field) -> pub Field { + let b: B> = B::new(new_concrete_c_over_d); + let c: C = b.get_t_c(); // Singleton + let d: D = D { d: input }; // Note + let output = c.call_method_of_t_d(d); + + output +} +// --- diff --git a/test_programs/execution_success/1_mul/Nargo.toml b/test_programs/execution_success/1_mul/Nargo.toml new file mode 100644 index 00000000000..94b36157cca --- /dev/null +++ b/test_programs/execution_success/1_mul/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "1_mul" +version = "0.1.0" +type = "bin" +authors = [""] + +[dependencies] diff --git a/tooling/nargo_cli/tests/execution_success/1_mul/Prover.toml b/test_programs/execution_success/1_mul/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/1_mul/Prover.toml rename to test_programs/execution_success/1_mul/Prover.toml diff --git a/test_programs/execution_success/1_mul/src/main.nr b/test_programs/execution_success/1_mul/src/main.nr new file mode 100644 index 00000000000..8f4032dbd75 --- /dev/null +++ b/test_programs/execution_success/1_mul/src/main.nr @@ -0,0 +1,9 @@ +// Test unsafe integer multiplication with overflow: 12^8 = 429 981 696 +// The circuit should handle properly the growth of the bit size +fn main(mut x: u32, y: u32, z: u32) { + x *= y; + x *= x; //144 + x *= x; //20736 + x *= x; //429 981 696 + assert(x == z); +} diff --git a/tooling/nargo_cli/tests/execution_success/2_div/Nargo.toml b/test_programs/execution_success/2_div/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/2_div/Nargo.toml rename to test_programs/execution_success/2_div/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/2_div/Prover.toml b/test_programs/execution_success/2_div/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/2_div/Prover.toml rename to test_programs/execution_success/2_div/Prover.toml diff --git a/tooling/nargo_cli/tests/execution_success/2_div/src/main.nr b/test_programs/execution_success/2_div/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/execution_success/2_div/src/main.nr rename to test_programs/execution_success/2_div/src/main.nr diff --git a/tooling/nargo_cli/tests/execution_success/3_add/Nargo.toml b/test_programs/execution_success/3_add/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/3_add/Nargo.toml rename to test_programs/execution_success/3_add/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/3_add/Prover.toml b/test_programs/execution_success/3_add/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/3_add/Prover.toml rename to test_programs/execution_success/3_add/Prover.toml diff --git a/tooling/nargo_cli/tests/execution_success/3_add/src/main.nr b/test_programs/execution_success/3_add/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/execution_success/3_add/src/main.nr rename to test_programs/execution_success/3_add/src/main.nr diff --git a/tooling/nargo_cli/tests/execution_success/4_sub/Nargo.toml b/test_programs/execution_success/4_sub/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/4_sub/Nargo.toml rename to test_programs/execution_success/4_sub/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/4_sub/Prover.toml b/test_programs/execution_success/4_sub/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/4_sub/Prover.toml rename to test_programs/execution_success/4_sub/Prover.toml diff --git a/tooling/nargo_cli/tests/execution_success/4_sub/src/main.nr b/test_programs/execution_success/4_sub/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/execution_success/4_sub/src/main.nr rename to test_programs/execution_success/4_sub/src/main.nr diff --git a/tooling/nargo_cli/tests/execution_success/5_over/Nargo.toml b/test_programs/execution_success/5_over/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/5_over/Nargo.toml rename to test_programs/execution_success/5_over/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/5_over/Prover.toml b/test_programs/execution_success/5_over/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/5_over/Prover.toml rename to test_programs/execution_success/5_over/Prover.toml diff --git a/test_programs/execution_success/5_over/src/main.nr b/test_programs/execution_success/5_over/src/main.nr new file mode 100644 index 00000000000..f24ff06cb2a --- /dev/null +++ b/test_programs/execution_success/5_over/src/main.nr @@ -0,0 +1,10 @@ +use dep::std; +// Test unsafe integer arithmetic +// Test odd bits integer +fn main(mut x: u32, y: u32) { + x = std::wrapping_mul(x,x); + assert(y == x); + + let c: u3 = 2; + assert(c > x as u3); +} diff --git a/tooling/nargo_cli/tests/execution_success/6/Nargo.toml b/test_programs/execution_success/6/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/6/Nargo.toml rename to test_programs/execution_success/6/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/6/Prover.toml b/test_programs/execution_success/6/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/6/Prover.toml rename to test_programs/execution_success/6/Prover.toml diff --git a/test_programs/execution_success/6/src/main.nr b/test_programs/execution_success/6/src/main.nr new file mode 100644 index 00000000000..5ecb809e68b --- /dev/null +++ b/test_programs/execution_success/6/src/main.nr @@ -0,0 +1,19 @@ +// Sha256 circuit where the input is 5 bytes +// not five field elements since sha256 operates over +// bytes. +// +// If you do not cast, it will take all the bytes from the field element! +// Mimc input is an array of field elements +// The function is called mimc_bn254 to emphasize its parameters are chosen for bn254 curve, it should be used only with a proving system using the same curve (e.g Plonk from Aztec) +use dep::std; + +fn main(x: [u8; 5], result: pub [u8; 32]) { + let mut digest = std::hash::sha256(x); + digest[0] = 5 as u8; + digest = std::hash::sha256(x); + assert(digest == result); + + let y = [12, 45, 78, 41]; + let h = std::hash::mimc::mimc_bn254(y); + assert(h == 18226366069841799622585958305961373004333097209608110160936134895615261821931); +} diff --git a/tooling/nargo_cli/tests/execution_success/6_array/Nargo.toml b/test_programs/execution_success/6_array/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/6_array/Nargo.toml rename to test_programs/execution_success/6_array/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/6_array/Prover.toml b/test_programs/execution_success/6_array/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/6_array/Prover.toml rename to test_programs/execution_success/6_array/Prover.toml diff --git a/test_programs/execution_success/6_array/src/main.nr b/test_programs/execution_success/6_array/src/main.nr new file mode 100644 index 00000000000..6aa05f58b71 --- /dev/null +++ b/test_programs/execution_success/6_array/src/main.nr @@ -0,0 +1,53 @@ +use dep::std; +//Basic tests for arrays +fn main(x: [u32; 5], y: [u32; 5], mut z: u32, t: u32) { + let mut c = 2301; + z = y[4]; + //Test 1: + for i in 0..5 { + c = z*z*y[i]; + z -= c; + } + assert(z == 0); //y[4]=0, so c and z are always 0 + //Test 2: + c = 2301 as u32; + for i in 0..5 { + c = t+2 as u32; + c = std::wrapping_mul(std::wrapping_mul(z,z),x[i]); + z =std::wrapping_add(z, std::wrapping_sub(x[i]*y[i] , c)); + } + assert(z == 3814912846); + //Test 3: + c = 2300001 as u32; + z = y[4]; + for i in 0..5 { + z = z + x[i]*y[i]; + for _i in 0..3 { + c = std::wrapping_sub(i as u32,2 as u32); + z = std::wrapping_mul(z,c); + } + } + assert(z == 41472); + //Test 4: + z = y[4]; + for i in 0..3 { + z += x[i] * y[i]; + for j in 0..2 { + z += x[i+j] - y[i+j]; + } + } + assert(z == 11539); + //Test 5: + let cc = if z < 1 { x } else { y }; + assert(cc[0] == y[0]); + // Test 6: for-each loops + for y_elem in y { + for x_elem in x { + assert(x_elem != y_elem); + } + } + // Test 7: Arrays of tuples/structs + let mut tuple_array = [(1, 2), (3, 4), (5, 6)]; + tuple_array[1] = (7, 8); + assert(tuple_array[1].1 == 8); +} diff --git a/tooling/nargo_cli/tests/execution_success/7/Nargo.toml b/test_programs/execution_success/7/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/7/Nargo.toml rename to test_programs/execution_success/7/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/7/Prover.toml b/test_programs/execution_success/7/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/7/Prover.toml rename to test_programs/execution_success/7/Prover.toml diff --git a/tooling/nargo_cli/tests/execution_success/7/src/main.nr b/test_programs/execution_success/7/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/execution_success/7/src/main.nr rename to test_programs/execution_success/7/src/main.nr diff --git a/tooling/nargo_cli/tests/execution_success/7_function/Nargo.toml b/test_programs/execution_success/7_function/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/7_function/Nargo.toml rename to test_programs/execution_success/7_function/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/7_function/Prover.toml b/test_programs/execution_success/7_function/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/7_function/Prover.toml rename to test_programs/execution_success/7_function/Prover.toml diff --git a/test_programs/execution_success/7_function/src/main.nr b/test_programs/execution_success/7_function/src/main.nr new file mode 100644 index 00000000000..95568dd4ccd --- /dev/null +++ b/test_programs/execution_success/7_function/src/main.nr @@ -0,0 +1,139 @@ +//Tests for function calling +fn f1(mut x: Field) -> Field { + x = x + 1; + x = f2(x); + x +} + +fn f2(mut x: Field) -> Field { + x += 2; + x +} +// Simple example +fn test0(mut a: Field) { + a = f2(a); + assert(a == 3); +} +// Nested call +fn test1(mut a: Field) { + a = f1(a); + assert(a == 4); +} + +fn test2(z: Field, t: u32) { + let a = z + t as Field; + assert(a == 64); + let e = pow(z, t as Field); + assert(e == 714924299); +} + +fn pow(base: Field, exponent: Field) -> Field { + let mut r = 1 as Field; + let b = exponent.to_le_bits(32 as u32); + for i in 1..33 { + r = r*r; + r = (b[32-i] as Field) * (r * base) + (1 - b[32-i] as Field) * r; + } + r +} + +fn test3(x: [u8; 3]) -> [u8; 3] { + let mut buffer = [0 as u8; 3]; + for i in 0..3 { + buffer[i] = x[i]; + } + assert(buffer == x); + buffer +} + +fn test_multiple(x: u32, y: u32) -> (u32, u32) { + (y, x) +} + +fn test_multiple2() -> my_struct { + my_struct { a: 5 as u32, b: 7 as u32 } +} + +fn test_multiple3(x: u32, y: u32) { + assert(x == y); +} + +struct my_struct { + a: u32, + b: u32, +} + +struct my2 { + aa: my_struct, + bb: my_struct, +} + +fn test_multiple4(s: my_struct) { + assert(s.a == s.b + 2); +} + +fn test_multiple5(a: (u32, u32)) { + assert(a.0 == a.1 + 2); +} + +fn test_multiple6(a: my2, b: my_struct, c: (my2, my_struct)) { + test_multiple4(a.aa); + test_multiple5((b.a, b.b)); + assert(c.0.aa.a == c.1.a); +} + +fn foo(a: [Field; N]) -> [Field; N] { + a +} + +fn bar() -> [Field; 1] { + foo([0]) +} + +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 }; + test_multiple4(ss); + test_multiple5((ss.a, ss.b)); + let my = my2 { aa: ss, bb: ss }; + ss.a = 61; + test_multiple6(my, ss, (my, ss)); + + let my_block = { + let mut ab = f2(a); + ab = ab + a; + (x, ab) + }; + assert(my_block.1 == 4); + + test0(a); + test1(a); + test2(x as Field, y); + assert(bar()[0] == 0); + + let mut b = [0 as u8, 5 as u8, 2 as u8]; + let c = test3(b); + assert(b == c); + b[0] = 1 as u8; + let cc = test3(b); + assert(c != cc); + let e = test_multiple(x, y); + assert(e.1 == e.0 + 54 as u32); + let d = test_multiple2(); + assert(d.b == d.a + 2 as u32); + test_multiple3(y, y); + //Regression test for issue #628: + let result = first(arr_to_field(arr1), arr_to_field(arr2)); + assert(result[0] == arr1[0] as Field); +} +// Issue #628 +fn arr_to_field(arr: [u32; 9]) -> [Field; 9] { + let mut as_field: [Field; 9] = [0 as Field; 9]; + for i in 0..9 { + as_field[i] = arr[i] as Field; + } + as_field +} + +fn first(a: [Field; 9], _b: [Field; 9]) -> [Field; 9] { + a +} diff --git a/tooling/nargo_cli/tests/execution_success/arithmetic_binary_operations/Nargo.toml b/test_programs/execution_success/arithmetic_binary_operations/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/arithmetic_binary_operations/Nargo.toml rename to test_programs/execution_success/arithmetic_binary_operations/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/arithmetic_binary_operations/Prover.toml b/test_programs/execution_success/arithmetic_binary_operations/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/arithmetic_binary_operations/Prover.toml rename to test_programs/execution_success/arithmetic_binary_operations/Prover.toml diff --git a/test_programs/execution_success/arithmetic_binary_operations/src/main.nr b/test_programs/execution_success/arithmetic_binary_operations/src/main.nr new file mode 100644 index 00000000000..69554f413a4 --- /dev/null +++ b/test_programs/execution_success/arithmetic_binary_operations/src/main.nr @@ -0,0 +1,15 @@ +// Tests a very simple program. +// +// The features being tested are: +// Binary addition, multiplication, division, constant modulo +// x = 3, y = 4, z = 5 +fn main(x: Field, y: Field, z: Field) -> pub Field { + //cast + assert(y as u1 == 0); + + let a = x + x; // 3 + 3 = 6 + let b = a - y; // 6 - 4 = 2 + let c = b * z; // 2 * 5 = 10 + let d = c / a; // 10 / 6 (This uses field inversion, so we test it by multiplying by `a`) + d * a +} diff --git a/tooling/nargo_cli/tests/execution_success/array_dynamic/Nargo.toml b/test_programs/execution_success/array_dynamic/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/array_dynamic/Nargo.toml rename to test_programs/execution_success/array_dynamic/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/array_dynamic/Prover.toml b/test_programs/execution_success/array_dynamic/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/array_dynamic/Prover.toml rename to test_programs/execution_success/array_dynamic/Prover.toml diff --git a/test_programs/execution_success/array_dynamic/src/main.nr b/test_programs/execution_success/array_dynamic/src/main.nr new file mode 100644 index 00000000000..dde7bacc455 --- /dev/null +++ b/test_programs/execution_success/array_dynamic/src/main.nr @@ -0,0 +1,38 @@ +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; + //dynamic array test + dyn_array(x, idx, idx - 3); + //regression for issue 1283 + let mut s = 0; + let x3 = [246, 159, 32, 176, 8]; + for i in 0..5 { + s += x3[index[i]]; + } + assert(s != 0); + + if 3 < (sublen as u32) { + assert(index[offset + 3] == index2[3]); + } +} + +fn dyn_array(mut x: [u32; 5], y: Field, z: Field) { + assert(x[y] == 111); + assert(x[z] == 101); + x[z] = 0; + assert(x[y] == 111); + assert(x[1] == 0); + if y as u32 < 10 { + x[y] = x[y] - 2; + } else { + x[y] = 0; + } + assert(x[4] == 109); +} diff --git a/tooling/nargo_cli/tests/execution_success/array_eq/Nargo.toml b/test_programs/execution_success/array_eq/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/array_eq/Nargo.toml rename to test_programs/execution_success/array_eq/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/array_eq/Prover.toml b/test_programs/execution_success/array_eq/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/array_eq/Prover.toml rename to test_programs/execution_success/array_eq/Prover.toml diff --git a/tooling/nargo_cli/tests/execution_success/array_eq/src/main.nr b/test_programs/execution_success/array_eq/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/execution_success/array_eq/src/main.nr rename to test_programs/execution_success/array_eq/src/main.nr diff --git a/tooling/nargo_cli/tests/execution_success/array_len/Nargo.toml b/test_programs/execution_success/array_len/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/array_len/Nargo.toml rename to test_programs/execution_success/array_len/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/array_len/Prover.toml b/test_programs/execution_success/array_len/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/array_len/Prover.toml rename to test_programs/execution_success/array_len/Prover.toml diff --git a/test_programs/execution_success/array_len/src/main.nr b/test_programs/execution_success/array_len/src/main.nr new file mode 100644 index 00000000000..b60762f4636 --- /dev/null +++ b/test_programs/execution_success/array_len/src/main.nr @@ -0,0 +1,24 @@ +fn len_plus_1(array: [T; N]) -> Field { + array.len() + 1 +} + +fn add_lens(a: [T; N], b: [Field; M]) -> Field { + a.len() + b.len() +} + +fn nested_call(b: [Field; N]) -> Field { + len_plus_1(b) +} + +fn main(x: Field, len3: [u8; 3], len4: [Field; 4]) { + assert(len_plus_1(len3) == 4); + assert(len_plus_1(len4) == 5); + assert(add_lens(len3, len4) == 7); + assert(nested_call(len4) == 5); + // std::array::len returns a compile-time known value + assert(len4[len3.len()] == 4); + // Regression for #1023, ensure .len still works after calling to_le_bytes on a witness. + // This was needed because normally .len is evaluated before acir-gen where to_le_bytes + // on a witness is only evaluated during/after acir-gen. + assert(x.to_le_bytes(8).len() != 0); +} diff --git a/tooling/nargo_cli/tests/execution_success/array_neq/Nargo.toml b/test_programs/execution_success/array_neq/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/array_neq/Nargo.toml rename to test_programs/execution_success/array_neq/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/array_neq/Prover.toml b/test_programs/execution_success/array_neq/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/array_neq/Prover.toml rename to test_programs/execution_success/array_neq/Prover.toml diff --git a/tooling/nargo_cli/tests/execution_success/array_neq/src/main.nr b/test_programs/execution_success/array_neq/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/execution_success/array_neq/src/main.nr rename to test_programs/execution_success/array_neq/src/main.nr diff --git a/tooling/nargo_cli/tests/execution_success/array_sort/Nargo.toml b/test_programs/execution_success/array_sort/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/array_sort/Nargo.toml rename to test_programs/execution_success/array_sort/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/array_sort/Prover.toml b/test_programs/execution_success/array_sort/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/array_sort/Prover.toml rename to test_programs/execution_success/array_sort/Prover.toml diff --git a/test_programs/execution_success/array_sort/src/main.nr b/test_programs/execution_success/array_sort/src/main.nr new file mode 100644 index 00000000000..4f40a2cee84 --- /dev/null +++ b/test_programs/execution_success/array_sort/src/main.nr @@ -0,0 +1,6 @@ +fn main(xs: [u8; 3]) { + let sorted = xs.sort(); + assert(sorted[0] == 1); + assert(sorted[1] == 2); + assert(sorted[2] == 3); +} diff --git a/tooling/nargo_cli/tests/execution_success/assert/Nargo.toml b/test_programs/execution_success/assert/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/assert/Nargo.toml rename to test_programs/execution_success/assert/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/assert/Prover.toml b/test_programs/execution_success/assert/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/assert/Prover.toml rename to test_programs/execution_success/assert/Prover.toml diff --git a/tooling/nargo_cli/tests/execution_success/assert/src/main.nr b/test_programs/execution_success/assert/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/execution_success/assert/src/main.nr rename to test_programs/execution_success/assert/src/main.nr diff --git a/tooling/nargo_cli/tests/execution_success/assert_statement/Nargo.toml b/test_programs/execution_success/assert_statement/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/assert_statement/Nargo.toml rename to test_programs/execution_success/assert_statement/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/assert_statement/Prover.toml b/test_programs/execution_success/assert_statement/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/assert_statement/Prover.toml rename to test_programs/execution_success/assert_statement/Prover.toml diff --git a/test_programs/execution_success/assert_statement/src/main.nr b/test_programs/execution_success/assert_statement/src/main.nr new file mode 100644 index 00000000000..2646a0b85c2 --- /dev/null +++ b/test_programs/execution_success/assert_statement/src/main.nr @@ -0,0 +1,7 @@ +// Tests a very simple program. +// +// The features being tested is assertion +fn main(x: Field, y: pub Field) { + assert(x == y, "x and y are not equal"); + assert_eq(x, y, "x and y are not equal"); +} diff --git a/tooling/nargo_cli/tests/execution_success/assign_ex/Nargo.toml b/test_programs/execution_success/assign_ex/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/assign_ex/Nargo.toml rename to test_programs/execution_success/assign_ex/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/assign_ex/Prover.toml b/test_programs/execution_success/assign_ex/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/assign_ex/Prover.toml rename to test_programs/execution_success/assign_ex/Prover.toml diff --git a/test_programs/execution_success/assign_ex/src/main.nr b/test_programs/execution_success/assign_ex/src/main.nr new file mode 100644 index 00000000000..b5cfc162cc4 --- /dev/null +++ b/test_programs/execution_success/assign_ex/src/main.nr @@ -0,0 +1,14 @@ +fn main(x: Field, y: Field) { + let mut z = x + y; + assert(z == 3); + z = x * y; + assert(z == 2); + + regression_3057(); +} +// Ensure parsing parenthesized lvalues works +fn regression_3057() { + let mut array = [[0, 1], [2, 3]]; + (array[0])[1] = 2; + assert(array[0][1] == 2); +} diff --git a/tooling/nargo_cli/tests/execution_success/bit_and/Nargo.toml b/test_programs/execution_success/bit_and/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/bit_and/Nargo.toml rename to test_programs/execution_success/bit_and/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/bit_and/Prover.toml b/test_programs/execution_success/bit_and/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/bit_and/Prover.toml rename to test_programs/execution_success/bit_and/Prover.toml diff --git a/test_programs/execution_success/bit_and/src/main.nr b/test_programs/execution_success/bit_and/src/main.nr new file mode 100644 index 00000000000..0bc1d9a49bd --- /dev/null +++ b/test_programs/execution_success/bit_and/src/main.nr @@ -0,0 +1,16 @@ +// You can only do bit operations with integers. +// (Kobi/Daira/Circom/#37) https://github.com/iden3/circom/issues/37 +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: + let flag = (x == 0) & (y == 16); + assert(flag); + //bitwise and with odd bits: + let x_as_u11 = x as u11; + let y_as_u11 = y as u11; + assert((x_as_u11 & y_as_u11) == x_as_u11); +} + diff --git a/tooling/nargo_cli/tests/execution_success/bit_shifts_comptime/Nargo.toml b/test_programs/execution_success/bit_shifts_comptime/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/bit_shifts_comptime/Nargo.toml rename to test_programs/execution_success/bit_shifts_comptime/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/bit_shifts_comptime/Prover.toml b/test_programs/execution_success/bit_shifts_comptime/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/bit_shifts_comptime/Prover.toml rename to test_programs/execution_success/bit_shifts_comptime/Prover.toml diff --git a/test_programs/execution_success/bit_shifts_comptime/src/main.nr b/test_programs/execution_success/bit_shifts_comptime/src/main.nr new file mode 100644 index 00000000000..9bb1028173d --- /dev/null +++ b/test_programs/execution_success/bit_shifts_comptime/src/main.nr @@ -0,0 +1,26 @@ +fn main(x: u64) { + let two: u64 = 2; + let three: u64 = 3; + // shifts on constant values + 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); + + regression_2250(); + + //regression for 3481 + assert(x << 63 == 0); + + assert_eq((1 as u56) << (32 as u56), 0x0100000000); +} + +fn regression_2250() { + let a: u1 = 1 >> 1; + assert(a == 0); + + let b: u32 = 1 >> 32; + assert(b == 0); +} diff --git a/tooling/nargo_cli/tests/execution_success/bit_shifts_runtime/Nargo.toml b/test_programs/execution_success/bit_shifts_runtime/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/bit_shifts_runtime/Nargo.toml rename to test_programs/execution_success/bit_shifts_runtime/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/bit_shifts_runtime/Prover.toml b/test_programs/execution_success/bit_shifts_runtime/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/bit_shifts_runtime/Prover.toml rename to test_programs/execution_success/bit_shifts_runtime/Prover.toml diff --git a/test_programs/execution_success/bit_shifts_runtime/src/main.nr b/test_programs/execution_success/bit_shifts_runtime/src/main.nr new file mode 100644 index 00000000000..a2c873a7e7f --- /dev/null +++ b/test_programs/execution_success/bit_shifts_runtime/src/main.nr @@ -0,0 +1,8 @@ +fn main(x: u64, y: u64) { + // runtime shifts on compile-time known values + assert(64 << y == 128); + assert(64 >> y == 32); + // runtime shifts on runtime values + assert(x << y == 128); + assert(x >> y == 32); +} diff --git a/tooling/nargo_cli/tests/execution_success/bool_not/Nargo.toml b/test_programs/execution_success/bool_not/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/bool_not/Nargo.toml rename to test_programs/execution_success/bool_not/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/bool_not/Prover.toml b/test_programs/execution_success/bool_not/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/bool_not/Prover.toml rename to test_programs/execution_success/bool_not/Prover.toml diff --git a/tooling/nargo_cli/tests/execution_success/bool_not/src/main.nr b/test_programs/execution_success/bool_not/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/execution_success/bool_not/src/main.nr rename to test_programs/execution_success/bool_not/src/main.nr diff --git a/tooling/nargo_cli/tests/execution_success/bool_or/Nargo.toml b/test_programs/execution_success/bool_or/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/bool_or/Nargo.toml rename to test_programs/execution_success/bool_or/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/bool_or/Prover.toml b/test_programs/execution_success/bool_or/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/bool_or/Prover.toml rename to test_programs/execution_success/bool_or/Prover.toml diff --git a/tooling/nargo_cli/tests/execution_success/bool_or/src/main.nr b/test_programs/execution_success/bool_or/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/execution_success/bool_or/src/main.nr rename to test_programs/execution_success/bool_or/src/main.nr diff --git a/tooling/nargo_cli/tests/execution_success/brillig_acir_as_brillig/Nargo.toml b/test_programs/execution_success/brillig_acir_as_brillig/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_acir_as_brillig/Nargo.toml rename to test_programs/execution_success/brillig_acir_as_brillig/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/brillig_acir_as_brillig/Prover.toml b/test_programs/execution_success/brillig_acir_as_brillig/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_acir_as_brillig/Prover.toml rename to test_programs/execution_success/brillig_acir_as_brillig/Prover.toml diff --git a/test_programs/execution_success/brillig_acir_as_brillig/src/main.nr b/test_programs/execution_success/brillig_acir_as_brillig/src/main.nr new file mode 100644 index 00000000000..5bd6ce0adb2 --- /dev/null +++ b/test_programs/execution_success/brillig_acir_as_brillig/src/main.nr @@ -0,0 +1,42 @@ +fn main(x: u32) { + assert(entry_point(x) == 2); + swap_entry_point(x, x + 1); + assert(deep_entry_point(x) == 4); +} + +fn inner(x: u32) -> u32 { + x + 1 +} + +unconstrained fn entry_point(x: u32) -> u32 { + inner(x + 1) +} + +fn swap(x: u32, y: u32) -> (u32, u32) { + (y, x) +} + +unconstrained fn swap_entry_point(x: u32, y: u32) { + let swapped = swap(x, y); + assert(swapped.0 == y); + assert(swapped.1 == x); + let swapped_twice = swap(swapped.0, swapped.1); + assert(swapped_twice.0 == x); + assert(swapped_twice.1 == y); +} + +fn level_3(x: u32) -> u32 { + x + 1 +} + +fn level_2(x: u32) -> u32 { + level_3(x + 1) +} + +fn level_1(x: u32) -> u32 { + level_2(x + 1) +} + +unconstrained fn deep_entry_point(x: u32) -> u32 { + level_1(x + 1) +} diff --git a/tooling/nargo_cli/tests/execution_success/brillig_arrays/Nargo.toml b/test_programs/execution_success/brillig_arrays/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_arrays/Nargo.toml rename to test_programs/execution_success/brillig_arrays/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/brillig_arrays/Prover.toml b/test_programs/execution_success/brillig_arrays/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_arrays/Prover.toml rename to test_programs/execution_success/brillig_arrays/Prover.toml diff --git a/tooling/nargo_cli/tests/execution_success/brillig_arrays/src/main.nr b/test_programs/execution_success/brillig_arrays/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_arrays/src/main.nr rename to test_programs/execution_success/brillig_arrays/src/main.nr diff --git a/tooling/nargo_cli/tests/execution_success/brillig_assert/Nargo.toml b/test_programs/execution_success/brillig_assert/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_assert/Nargo.toml rename to test_programs/execution_success/brillig_assert/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/brillig_assert/Prover.toml b/test_programs/execution_success/brillig_assert/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_assert/Prover.toml rename to test_programs/execution_success/brillig_assert/Prover.toml diff --git a/test_programs/execution_success/brillig_assert/src/main.nr b/test_programs/execution_success/brillig_assert/src/main.nr new file mode 100644 index 00000000000..91e4cebd9d3 --- /dev/null +++ b/test_programs/execution_success/brillig_assert/src/main.nr @@ -0,0 +1,12 @@ +// Tests a very simple program. +// +// The features being tested is using assert on brillig +fn main(x: Field) { + assert(1 == conditional(x as bool)); +} + +unconstrained fn conditional(x: bool) -> Field { + assert(x, "x is false"); + assert_eq(x, true, "x is false"); + 1 +} diff --git a/tooling/nargo_cli/tests/execution_success/brillig_blake2s/Nargo.toml b/test_programs/execution_success/brillig_blake2s/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_blake2s/Nargo.toml rename to test_programs/execution_success/brillig_blake2s/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/brillig_blake2s/Prover.toml b/test_programs/execution_success/brillig_blake2s/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_blake2s/Prover.toml rename to test_programs/execution_success/brillig_blake2s/Prover.toml diff --git a/test_programs/execution_success/brillig_blake2s/src/main.nr b/test_programs/execution_success/brillig_blake2s/src/main.nr new file mode 100644 index 00000000000..5bd52666ae9 --- /dev/null +++ b/test_programs/execution_success/brillig_blake2s/src/main.nr @@ -0,0 +1,11 @@ +use dep::std; +// Tests a very simple program. +// +// The features being tested is blake2s in brillig +fn main(x: [u8; 5], result: [u8; 32]) { + assert(blake2s(x) == result); +} + +unconstrained fn blake2s(x: [u8; 5]) -> [u8; 32] { + std::hash::blake2s(x) +} diff --git a/tooling/nargo_cli/tests/execution_success/brillig_calls/Nargo.toml b/test_programs/execution_success/brillig_calls/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_calls/Nargo.toml rename to test_programs/execution_success/brillig_calls/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/brillig_calls/Prover.toml b/test_programs/execution_success/brillig_calls/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_calls/Prover.toml rename to test_programs/execution_success/brillig_calls/Prover.toml diff --git a/test_programs/execution_success/brillig_calls/src/main.nr b/test_programs/execution_success/brillig_calls/src/main.nr new file mode 100644 index 00000000000..5c39713f5bb --- /dev/null +++ b/test_programs/execution_success/brillig_calls/src/main.nr @@ -0,0 +1,58 @@ +// Tests a very simple program. +// +// The features being tested is brillig calls +fn main(x: u32) { + assert(entry_point(x) == 2); + swap_entry_point(x, x + 1); + assert(deep_entry_point(x) == 4); + multiple_values_entry_point(x); +} + +unconstrained fn returns_multiple_values(x: u32) -> (u32, u32, u32, u32) { + (x + 1, x + 2, x + 3, x + 4) +} + +unconstrained fn multiple_values_entry_point(x: u32) { + let (a, b, c, d) = returns_multiple_values(x); + assert(a == x + 1); + assert(b == x + 2); + assert(c == x + 3); + assert(d == x + 4); +} + +unconstrained fn inner(x: u32) -> u32 { + x + 1 +} + +unconstrained fn entry_point(x: u32) -> u32 { + inner(x + 1) +} + +unconstrained fn swap(x: u32, y: u32) -> (u32, u32) { + (y, x) +} + +unconstrained fn swap_entry_point(x: u32, y: u32) { + let swapped = swap(x, y); + assert(swapped.0 == y); + assert(swapped.1 == x); + let swapped_twice = swap(swapped.0, swapped.1); + assert(swapped_twice.0 == x); + assert(swapped_twice.1 == y); +} + +unconstrained fn level_3(x: u32) -> u32 { + x + 1 +} + +unconstrained fn level_2(x: u32) -> u32 { + level_3(x + 1) +} + +unconstrained fn level_1(x: u32) -> u32 { + level_2(x + 1) +} + +unconstrained fn deep_entry_point(x: u32) -> u32 { + level_1(x + 1) +} diff --git a/tooling/nargo_cli/tests/execution_success/brillig_calls_array/Nargo.toml b/test_programs/execution_success/brillig_calls_array/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_calls_array/Nargo.toml rename to test_programs/execution_success/brillig_calls_array/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/brillig_calls_array/Prover.toml b/test_programs/execution_success/brillig_calls_array/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_calls_array/Prover.toml rename to test_programs/execution_success/brillig_calls_array/Prover.toml diff --git a/test_programs/execution_success/brillig_calls_array/src/main.nr b/test_programs/execution_success/brillig_calls_array/src/main.nr new file mode 100644 index 00000000000..1b1d89f6366 --- /dev/null +++ b/test_programs/execution_success/brillig_calls_array/src/main.nr @@ -0,0 +1,33 @@ +// Tests a very simple program. +// +// The features being tested is brillig calls passing arrays around +fn main(x: [u32; 3]) { + assert(entry_point(x) == 9); + another_entry_point(x); +} + +unconstrained fn inner(x: [u32; 3]) -> [u32; 3] { + [x[0] + 1, x[1] + 1, x[2] + 1] +} + +unconstrained fn entry_point(x: [u32; 3]) -> u32 { + let y = inner(x); + y[0] + y[1] + y[2] +} + +unconstrained fn nested_fn_that_allocates(value: u32) -> u32 { + let x = [value, value, value]; + let y = inner(x); + y[0] + y[1] + y[2] +} + +unconstrained fn another_entry_point(x: [u32; 3]) { + assert(x[0] == 1); + assert(x[1] == 2); + assert(x[2] == 3); + assert(nested_fn_that_allocates(1) == 6); + // x should be unchanged + assert(x[0] == 1); + assert(x[1] == 2); + assert(x[2] == 3); +} diff --git a/tooling/nargo_cli/tests/execution_success/brillig_calls_conditionals/Nargo.toml b/test_programs/execution_success/brillig_calls_conditionals/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_calls_conditionals/Nargo.toml rename to test_programs/execution_success/brillig_calls_conditionals/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/brillig_calls_conditionals/Prover.toml b/test_programs/execution_success/brillig_calls_conditionals/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_calls_conditionals/Prover.toml rename to test_programs/execution_success/brillig_calls_conditionals/Prover.toml diff --git a/tooling/nargo_cli/tests/execution_success/brillig_calls_conditionals/src/main.nr b/test_programs/execution_success/brillig_calls_conditionals/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_calls_conditionals/src/main.nr rename to test_programs/execution_success/brillig_calls_conditionals/src/main.nr diff --git a/tooling/nargo_cli/tests/execution_success/brillig_conditional/Nargo.toml b/test_programs/execution_success/brillig_conditional/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_conditional/Nargo.toml rename to test_programs/execution_success/brillig_conditional/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/brillig_conditional/Prover.toml b/test_programs/execution_success/brillig_conditional/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_conditional/Prover.toml rename to test_programs/execution_success/brillig_conditional/Prover.toml diff --git a/test_programs/execution_success/brillig_conditional/src/main.nr b/test_programs/execution_success/brillig_conditional/src/main.nr new file mode 100644 index 00000000000..a59336a877b --- /dev/null +++ b/test_programs/execution_success/brillig_conditional/src/main.nr @@ -0,0 +1,10 @@ +// Tests a very simple program. +// +// The features being tested is basic conditonal on brillig +fn main(x: Field) { + assert(4 == conditional(x == 1)); +} + +unconstrained fn conditional(x: bool) -> Field { + if x { 4 } else { 5 } +} diff --git a/test_programs/execution_success/brillig_cow/Nargo.toml b/test_programs/execution_success/brillig_cow/Nargo.toml new file mode 100644 index 00000000000..d191eb53ddf --- /dev/null +++ b/test_programs/execution_success/brillig_cow/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "brillig_cow" +type = "bin" +authors = [""] + +[dependencies] diff --git a/test_programs/execution_success/brillig_cow/Prover.toml b/test_programs/execution_success/brillig_cow/Prover.toml new file mode 100644 index 00000000000..6533d218b15 --- /dev/null +++ b/test_programs/execution_success/brillig_cow/Prover.toml @@ -0,0 +1,7 @@ +original = [0, 1, 2, 3, 4] +index = 2 + +[expected_result] +original = [0, 1, 2, 3, 4] +modified_once = [0, 1, 27, 3, 4] +modified_twice = [0, 1, 27, 27, 4] diff --git a/test_programs/execution_success/brillig_cow/src/main.nr b/test_programs/execution_success/brillig_cow/src/main.nr new file mode 100644 index 00000000000..7d847e085fe --- /dev/null +++ b/test_programs/execution_success/brillig_cow/src/main.nr @@ -0,0 +1,54 @@ +// Tests the copy on write optimization for arrays. We look for cases where we are modifying an array in place when we shouldn't. + +global ARRAY_SIZE = 5; + +struct ExecutionResult { + original: [Field; ARRAY_SIZE], + modified_once: [Field; ARRAY_SIZE], + modified_twice: [Field; ARRAY_SIZE], +} + +impl ExecutionResult { + fn is_equal(self, other: ExecutionResult) -> bool { + (self.original == other.original) & + (self.modified_once == other.modified_once) & + (self.modified_twice == other.modified_twice) + } +} + +fn modify_in_inlined_constrained(original: [Field; ARRAY_SIZE], index: u64) -> ExecutionResult { + let mut modified = original; + + modified[index] = 27; + + let modified_once = modified; + + modified[index+1] = 27; + + ExecutionResult { + original, + modified_once, + modified_twice: modified + } +} + +unconstrained fn modify_in_unconstrained(original: [Field; ARRAY_SIZE], index: u64) -> ExecutionResult { + let mut modified = original; + + modified[index] = 27; + + let modified_once = modified; + + modified[index+1] = 27; + + ExecutionResult { + original, + modified_once, + modified_twice: modified + } +} + +unconstrained fn main(original: [Field; ARRAY_SIZE], index: u64, expected_result: ExecutionResult) { + assert(expected_result.is_equal(modify_in_unconstrained(original, index))); + assert(expected_result.is_equal(modify_in_inlined_constrained(original, index))); +} diff --git a/tooling/nargo_cli/tests/execution_success/brillig_ecdsa/Nargo.toml b/test_programs/execution_success/brillig_ecdsa/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_ecdsa/Nargo.toml rename to test_programs/execution_success/brillig_ecdsa/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/brillig_ecdsa/Prover.toml b/test_programs/execution_success/brillig_ecdsa/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_ecdsa/Prover.toml rename to test_programs/execution_success/brillig_ecdsa/Prover.toml diff --git a/test_programs/execution_success/brillig_ecdsa/src/main.nr b/test_programs/execution_success/brillig_ecdsa/src/main.nr new file mode 100644 index 00000000000..23f017aa336 --- /dev/null +++ b/test_programs/execution_success/brillig_ecdsa/src/main.nr @@ -0,0 +1,11 @@ +use dep::std; +// Tests a very simple program. +// +// The features being tested is ecdsa in brillig +fn main(hashed_message: [u8;32], pub_key_x: [u8;32], pub_key_y: [u8;32], signature: [u8;64]) { + assert(ecdsa(hashed_message, pub_key_x, pub_key_y, signature)); +} + +unconstrained fn ecdsa(hashed_message: [u8;32], pub_key_x: [u8;32], pub_key_y: [u8;32], signature: [u8;64]) -> bool { + std::ecdsa_secp256k1::verify_signature(pub_key_x, pub_key_y, signature, hashed_message) +} diff --git a/tooling/nargo_cli/tests/execution_success/brillig_fns_as_values/Nargo.toml b/test_programs/execution_success/brillig_fns_as_values/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_fns_as_values/Nargo.toml rename to test_programs/execution_success/brillig_fns_as_values/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/brillig_fns_as_values/Prover.toml b/test_programs/execution_success/brillig_fns_as_values/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_fns_as_values/Prover.toml rename to test_programs/execution_success/brillig_fns_as_values/Prover.toml diff --git a/test_programs/execution_success/brillig_fns_as_values/src/main.nr b/test_programs/execution_success/brillig_fns_as_values/src/main.nr new file mode 100644 index 00000000000..2f5d14583d5 --- /dev/null +++ b/test_programs/execution_success/brillig_fns_as_values/src/main.nr @@ -0,0 +1,36 @@ +use dep::std; + +struct MyStruct { + operation: fn (u32) -> u32, +} + +fn main(x: u32) { + assert(wrapper(increment, x) == x + 1); + assert(wrapper(increment_acir, x) == x + 1); + assert(wrapper(decrement, x) == std::wrapping_sub(x, 1)); + assert(wrapper_with_struct(MyStruct { operation: increment }, x) == x + 1); + assert(wrapper_with_struct(MyStruct { operation: decrement }, x) == std::wrapping_sub(x, 1)); + // https://github.com/noir-lang/noir/issues/1975 + assert(increment(x) == x + 1); +} + +unconstrained fn wrapper(func: fn (u32) -> u32, param: u32) -> u32 { + func(param) +} + +unconstrained fn increment(x: u32) -> u32 { + x + 1 +} + +unconstrained fn decrement(x: u32) -> u32 { + x - 1 +} + +unconstrained fn wrapper_with_struct(my_struct: MyStruct, param: u32) -> u32 { + let func = my_struct.operation; + func(param) +} + +fn increment_acir(x: u32) -> u32 { + x + 1 +} diff --git a/tooling/nargo_cli/tests/execution_success/brillig_hash_to_field/Nargo.toml b/test_programs/execution_success/brillig_hash_to_field/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_hash_to_field/Nargo.toml rename to test_programs/execution_success/brillig_hash_to_field/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/brillig_hash_to_field/Prover.toml b/test_programs/execution_success/brillig_hash_to_field/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_hash_to_field/Prover.toml rename to test_programs/execution_success/brillig_hash_to_field/Prover.toml diff --git a/test_programs/execution_success/brillig_hash_to_field/src/main.nr b/test_programs/execution_success/brillig_hash_to_field/src/main.nr new file mode 100644 index 00000000000..4b4177a521e --- /dev/null +++ b/test_programs/execution_success/brillig_hash_to_field/src/main.nr @@ -0,0 +1,11 @@ +use dep::std; +// Tests a very simple program. +// +// The features being tested is hash_to_field in brillig +fn main(input: Field) -> pub Field { + hash_to_field(input) +} + +unconstrained fn hash_to_field(input: Field) -> Field { + std::hash::hash_to_field([input]) +} diff --git a/tooling/nargo_cli/tests/execution_success/brillig_identity_function/Nargo.toml b/test_programs/execution_success/brillig_identity_function/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_identity_function/Nargo.toml rename to test_programs/execution_success/brillig_identity_function/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/brillig_identity_function/Prover.toml b/test_programs/execution_success/brillig_identity_function/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_identity_function/Prover.toml rename to test_programs/execution_success/brillig_identity_function/Prover.toml diff --git a/test_programs/execution_success/brillig_identity_function/src/main.nr b/test_programs/execution_success/brillig_identity_function/src/main.nr new file mode 100644 index 00000000000..f41188b1f0d --- /dev/null +++ b/test_programs/execution_success/brillig_identity_function/src/main.nr @@ -0,0 +1,32 @@ +struct myStruct { + foo: Field, + foo_arr: [Field; 2], +} +// Tests a very simple program. +// +// The features being tested is the identity function in Brillig +fn main(x: Field) { + assert(x == identity(x)); + // TODO: add support for array comparison + let arr = identity_array([x, x]); + assert(x == arr[0]); + assert(x == arr[1]); + + let s = myStruct { foo: x, foo_arr: [x, x] }; + let identity_struct = identity_struct(s); + assert(x == identity_struct.foo); + assert(x == identity_struct.foo_arr[0]); + assert(x == identity_struct.foo_arr[1]); +} + +unconstrained fn identity(x: Field) -> Field { + x +} + +unconstrained fn identity_array(arr: [Field; 2]) -> [Field; 2] { + arr +} + +unconstrained fn identity_struct(s: myStruct) -> myStruct { + s +} diff --git a/tooling/nargo_cli/tests/execution_success/brillig_keccak/Nargo.toml b/test_programs/execution_success/brillig_keccak/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_keccak/Nargo.toml rename to test_programs/execution_success/brillig_keccak/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/brillig_keccak/Prover.toml b/test_programs/execution_success/brillig_keccak/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_keccak/Prover.toml rename to test_programs/execution_success/brillig_keccak/Prover.toml diff --git a/test_programs/execution_success/brillig_keccak/src/main.nr b/test_programs/execution_success/brillig_keccak/src/main.nr new file mode 100644 index 00000000000..1e9b65a6eb4 --- /dev/null +++ b/test_programs/execution_success/brillig_keccak/src/main.nr @@ -0,0 +1,25 @@ +use dep::std; +// Tests a very simple program. +// +// The features being tested is keccak256 in brillig +fn main(x: Field, result: [u8; 32]) { + // We use the `as` keyword here to denote the fact that we want to take just the first byte from the x Field + // The padding is taken care of by the program + let digest = keccak256([x as u8], 1); + assert(digest == result); + //#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); + + 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); + + assert(hash_a != hash_c); +} + +unconstrained fn keccak256(data: [u8; N], msg_len: u32) -> [u8; 32] { + std::hash::keccak256(data, msg_len) +} diff --git a/tooling/nargo_cli/tests/execution_success/brillig_loop/Nargo.toml b/test_programs/execution_success/brillig_loop/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_loop/Nargo.toml rename to test_programs/execution_success/brillig_loop/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/brillig_loop/Prover.toml b/test_programs/execution_success/brillig_loop/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_loop/Prover.toml rename to test_programs/execution_success/brillig_loop/Prover.toml diff --git a/tooling/nargo_cli/tests/execution_success/brillig_loop/src/main.nr b/test_programs/execution_success/brillig_loop/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_loop/src/main.nr rename to test_programs/execution_success/brillig_loop/src/main.nr diff --git a/tooling/nargo_cli/tests/execution_success/brillig_nested_arrays/Nargo.toml b/test_programs/execution_success/brillig_nested_arrays/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_nested_arrays/Nargo.toml rename to test_programs/execution_success/brillig_nested_arrays/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/brillig_nested_arrays/Prover.toml b/test_programs/execution_success/brillig_nested_arrays/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_nested_arrays/Prover.toml rename to test_programs/execution_success/brillig_nested_arrays/Prover.toml diff --git a/tooling/nargo_cli/tests/execution_success/brillig_nested_arrays/src/main.nr b/test_programs/execution_success/brillig_nested_arrays/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_nested_arrays/src/main.nr rename to test_programs/execution_success/brillig_nested_arrays/src/main.nr diff --git a/tooling/nargo_cli/tests/execution_success/brillig_nested_slices/Nargo.toml b/test_programs/execution_success/brillig_nested_slices/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_nested_slices/Nargo.toml rename to test_programs/execution_success/brillig_nested_slices/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/brillig_nested_slices/Prover.toml b/test_programs/execution_success/brillig_nested_slices/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_nested_slices/Prover.toml rename to test_programs/execution_success/brillig_nested_slices/Prover.toml diff --git a/tooling/nargo_cli/tests/execution_success/brillig_nested_slices/src/main.nr b/test_programs/execution_success/brillig_nested_slices/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_nested_slices/src/main.nr rename to test_programs/execution_success/brillig_nested_slices/src/main.nr diff --git a/tooling/nargo_cli/tests/execution_success/brillig_not/Nargo.toml b/test_programs/execution_success/brillig_not/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_not/Nargo.toml rename to test_programs/execution_success/brillig_not/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/brillig_not/Prover.toml b/test_programs/execution_success/brillig_not/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_not/Prover.toml rename to test_programs/execution_success/brillig_not/Prover.toml diff --git a/test_programs/execution_success/brillig_not/src/main.nr b/test_programs/execution_success/brillig_not/src/main.nr new file mode 100644 index 00000000000..d34b3edb4b6 --- /dev/null +++ b/test_programs/execution_success/brillig_not/src/main.nr @@ -0,0 +1,11 @@ +// Tests a very simple Brillig function. +// +// The features being tested is not instruction on brillig +fn main(x: Field, y: Field) { + assert(false == not_operator(x as bool)); + assert(true == not_operator(y as bool)); +} + +unconstrained fn not_operator(x: bool) -> bool { + !x +} diff --git a/tooling/nargo_cli/tests/execution_success/brillig_oracle/Nargo.toml b/test_programs/execution_success/brillig_oracle/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_oracle/Nargo.toml rename to test_programs/execution_success/brillig_oracle/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/brillig_oracle/Prover.toml b/test_programs/execution_success/brillig_oracle/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_oracle/Prover.toml rename to test_programs/execution_success/brillig_oracle/Prover.toml diff --git a/test_programs/execution_success/brillig_oracle/src/main.nr b/test_programs/execution_success/brillig_oracle/src/main.nr new file mode 100644 index 00000000000..490b7b605e3 --- /dev/null +++ b/test_programs/execution_success/brillig_oracle/src/main.nr @@ -0,0 +1,42 @@ +use dep::std::slice; +use dep::std::test::OracleMock; + +// Tests oracle usage in brillig/unconstrained functions +fn main(x: Field) { + let size = 20; + // TODO: Add a method along the lines of `(0..size).to_array()`. + let mut mock_oracle_response = [0; 20]; + // TODO: Add an `array.reverse()` method. + let mut reversed_mock_oracle_response = [0; 20]; + for i in 0..size { + mock_oracle_response[i] = i; + reversed_mock_oracle_response[19 - i] = i; + } + + // TODO: this method of returning a slice feels hacky. + let _ = OracleMock::mock("get_number_sequence").with_params(size).returns((20, mock_oracle_response)); + let _ = OracleMock::mock("get_reverse_number_sequence").with_params(size).returns((20, reversed_mock_oracle_response)); + + get_number_sequence_wrapper(size); +} + +// Define oracle functions which we have mocked above +#[oracle(get_number_sequence)] +unconstrained fn get_number_sequence(_size: Field) -> [Field] {} + +#[oracle(get_reverse_number_sequence)] +unconstrained fn get_reverse_number_sequence(_size: Field) -> [Field] {} + +unconstrained fn get_number_sequence_wrapper(size: Field) { + let slice = get_number_sequence(size); + for i in 0..20 as u32 { + assert(slice[i] == i as Field); + } + + let reversed_slice = get_reverse_number_sequence(size); + // Regression test that we have not overwritten memory + for i in 0..20 as u32 { + assert(slice[i] == reversed_slice[19 - i]); + } +} + diff --git a/tooling/nargo_cli/tests/execution_success/brillig_pedersen/Nargo.toml b/test_programs/execution_success/brillig_pedersen/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_pedersen/Nargo.toml rename to test_programs/execution_success/brillig_pedersen/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/brillig_pedersen/Prover.toml b/test_programs/execution_success/brillig_pedersen/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_pedersen/Prover.toml rename to test_programs/execution_success/brillig_pedersen/Prover.toml diff --git a/test_programs/execution_success/brillig_pedersen/src/main.nr b/test_programs/execution_success/brillig_pedersen/src/main.nr new file mode 100644 index 00000000000..2379818c454 --- /dev/null +++ b/test_programs/execution_success/brillig_pedersen/src/main.nr @@ -0,0 +1,22 @@ +use dep::std; + +unconstrained fn main(x: Field, y: Field, salt: Field, out_x: Field, out_y: Field, out_hash: Field) { + let res = std::hash::pedersen_commitment_with_separator([x, y], 0); + assert(res.x == out_x); + assert(res.y == out_y); + + let res_hash = std::hash::pedersen_hash_with_separator([x, y], 0); + assert_eq(res_hash, out_hash); + + assert(res_hash != res.x); + + let raw_data = [x, y]; + let mut state = 0; + for i in 0..(2 as u32) { + state = state * 8 + raw_data[i]; + } + state += salt; + let hash = std::hash::pedersen_commitment_with_separator([state], 0); + assert(std::hash::pedersen_commitment_with_separator([43], 0).x == hash.x); +} + diff --git a/tooling/nargo_cli/tests/execution_success/brillig_recursion/Nargo.toml b/test_programs/execution_success/brillig_recursion/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_recursion/Nargo.toml rename to test_programs/execution_success/brillig_recursion/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/brillig_recursion/Prover.toml b/test_programs/execution_success/brillig_recursion/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_recursion/Prover.toml rename to test_programs/execution_success/brillig_recursion/Prover.toml diff --git a/test_programs/execution_success/brillig_recursion/src/main.nr b/test_programs/execution_success/brillig_recursion/src/main.nr new file mode 100644 index 00000000000..a87ef28bc56 --- /dev/null +++ b/test_programs/execution_success/brillig_recursion/src/main.nr @@ -0,0 +1,14 @@ +// Tests a very simple program. +// +// The feature being tested is brillig recursion +fn main(x: u32) { + assert(fibonacci(x) == 55); +} + +unconstrained fn fibonacci(x: u32) -> u32 { + if x <= 1 { + x + } else { + fibonacci(x - 1) + fibonacci(x - 2) + } +} diff --git a/tooling/nargo_cli/tests/execution_success/brillig_references/Nargo.toml b/test_programs/execution_success/brillig_references/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_references/Nargo.toml rename to test_programs/execution_success/brillig_references/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/brillig_references/Prover.toml b/test_programs/execution_success/brillig_references/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_references/Prover.toml rename to test_programs/execution_success/brillig_references/Prover.toml diff --git a/test_programs/execution_success/brillig_references/src/main.nr b/test_programs/execution_success/brillig_references/src/main.nr new file mode 100644 index 00000000000..e1f906beb0a --- /dev/null +++ b/test_programs/execution_success/brillig_references/src/main.nr @@ -0,0 +1,49 @@ +unconstrained fn main(mut x: Field) { + add1(&mut x); + assert(x == 3); + // https://github.com/noir-lang/noir/issues/1899 + // let mut s = S { y: x }; + // s.add2(); + // assert(s.y == 5); + // Test that normal mutable variables are still copied + let mut a = 0; + mutate_copy(a); + assert(a == 0); + // Test something 3 allocations deep + let mut nested_allocations = Nested { y: &mut &mut 0 }; + add1(*nested_allocations.y); + 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] } }; + *c.bar.array = [3, 4]; + let arr: [Field; 2] = *c.bar.array; + assert(arr[0] == 3); + assert(arr[1] == 4); +} + +unconstrained fn add1(x: &mut Field) { + *x += 1; +} + +struct S { y: Field } + +struct Nested { y: &mut &mut Field } + +struct C { + foo: Field, + bar: &mut C2, +} + +struct C2 { + array: &mut [Field; 2] +} + +impl S { + unconstrained fn add2(&mut self) { + self.y += 2; + } +} + +unconstrained fn mutate_copy(mut a: Field) { + a = 7; +} diff --git a/tooling/nargo_cli/tests/execution_success/brillig_scalar_mul/Nargo.toml b/test_programs/execution_success/brillig_scalar_mul/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_scalar_mul/Nargo.toml rename to test_programs/execution_success/brillig_scalar_mul/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/brillig_scalar_mul/Prover.toml b/test_programs/execution_success/brillig_scalar_mul/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_scalar_mul/Prover.toml rename to test_programs/execution_success/brillig_scalar_mul/Prover.toml diff --git a/test_programs/execution_success/brillig_scalar_mul/src/main.nr b/test_programs/execution_success/brillig_scalar_mul/src/main.nr new file mode 100644 index 00000000000..ab2f79eb815 --- /dev/null +++ b/test_programs/execution_success/brillig_scalar_mul/src/main.nr @@ -0,0 +1,23 @@ +use dep::std; + +unconstrained fn main( + a: Field, + a_pub_x: pub Field, + a_pub_y: pub Field, + b: Field, + b_pub_x: pub Field, + b_pub_y: pub Field +) { + let mut priv_key = a; + let mut pub_x: Field = a_pub_x; + let mut pub_y: Field = a_pub_y; + if a != 1 { + // Change `a` in Prover.toml to test input `b` + 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); +} diff --git a/tooling/nargo_cli/tests/execution_success/brillig_schnorr/Nargo.toml b/test_programs/execution_success/brillig_schnorr/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_schnorr/Nargo.toml rename to test_programs/execution_success/brillig_schnorr/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/brillig_schnorr/Prover.toml b/test_programs/execution_success/brillig_schnorr/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_schnorr/Prover.toml rename to test_programs/execution_success/brillig_schnorr/Prover.toml diff --git a/test_programs/execution_success/brillig_schnorr/src/main.nr b/test_programs/execution_success/brillig_schnorr/src/main.nr new file mode 100644 index 00000000000..4cc79ae7e07 --- /dev/null +++ b/test_programs/execution_success/brillig_schnorr/src/main.nr @@ -0,0 +1,25 @@ +use dep::std; +// Note: If main has any unsized types, then the verifier will never be able +// to figure out the circuit instance +unconstrained fn main( + message: [u8; 10], + message_field: Field, + pub_key_x: Field, + pub_key_y: Field, + signature: [u8; 64] +) { + // Regression for issue #2421 + // We want to make sure that we can accurately verify a signature whose message is a slice vs. an array + let message_field_bytes = message_field.to_be_bytes(10); + for i in 0..10 { + assert(message[i] == message_field_bytes[i]); + } + // 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); + 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); + assert(valid_signature); +} diff --git a/test_programs/execution_success/brillig_set_slice_of_slice/Nargo.toml b/test_programs/execution_success/brillig_set_slice_of_slice/Nargo.toml new file mode 100644 index 00000000000..071254266f4 --- /dev/null +++ b/test_programs/execution_success/brillig_set_slice_of_slice/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "brillig_set_slice_of_slice" +type = "bin" +authors = [""] +compiler_version = ">=0.19.4" + +[dependencies] \ No newline at end of file diff --git a/test_programs/execution_success/brillig_set_slice_of_slice/src/main.nr b/test_programs/execution_success/brillig_set_slice_of_slice/src/main.nr new file mode 100644 index 00000000000..c0e9c7d172f --- /dev/null +++ b/test_programs/execution_success/brillig_set_slice_of_slice/src/main.nr @@ -0,0 +1,51 @@ +struct Property +{ + key : [u8], + value : [u8], +} + +struct JSON +{ + doc : [Property] +} + +unconstrained fn slice_eq(self: [u8], other: [u8]) -> bool { + let mut equal = true; + for i in 0..self.len() { + if self[i] != other[i] { + equal = false; + } + } + equal +} + +// This test acts a regression for issue #3476 +unconstrained fn main() { + let mut json = JSON { doc: [] }; + let mut prop = Property { key: [], value:[] }; + + let other_prop = Property { key: [0, 1, 2], value:[10] }; + json.doc = json.doc.push_back(other_prop); + + for i in 0..3 { + prop.key = prop.key.push_back(i as u8); + } + prop.value = prop.value.push_back(5); + + // add property to json or replace existing + let len : Field = json.doc.len(); + let mut found = false; + for i in 0..len + { + if (!found) + { + if (slice_eq(prop.key, json.doc[i].key)) + { + json.doc[i].value = prop.value; + found = true; + } + } + } + assert(found == true); + assert(json.doc[0].value[0] == 5); +} \ No newline at end of file diff --git a/tooling/nargo_cli/tests/execution_success/brillig_sha256/Nargo.toml b/test_programs/execution_success/brillig_sha256/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_sha256/Nargo.toml rename to test_programs/execution_success/brillig_sha256/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/brillig_sha256/Prover.toml b/test_programs/execution_success/brillig_sha256/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_sha256/Prover.toml rename to test_programs/execution_success/brillig_sha256/Prover.toml diff --git a/test_programs/execution_success/brillig_sha256/src/main.nr b/test_programs/execution_success/brillig_sha256/src/main.nr new file mode 100644 index 00000000000..e76109df9c3 --- /dev/null +++ b/test_programs/execution_success/brillig_sha256/src/main.nr @@ -0,0 +1,14 @@ +use dep::std; +// Tests a very simple program. +// +// The features being tested is sha256 in brillig +fn main(x: Field, result: [u8; 32]) { + assert(result == sha256(x)); +} + +unconstrained fn sha256(x: Field) -> [u8; 32] { + // We use the `as` keyword here to denote the fact that we want to take just the first byte from the x Field + // The padding is taken care of by the program + std::hash::sha256([x as u8]) +} + diff --git a/tooling/nargo_cli/tests/execution_success/brillig_slices/Nargo.toml b/test_programs/execution_success/brillig_slices/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_slices/Nargo.toml rename to test_programs/execution_success/brillig_slices/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/brillig_slices/Prover.toml b/test_programs/execution_success/brillig_slices/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_slices/Prover.toml rename to test_programs/execution_success/brillig_slices/Prover.toml diff --git a/test_programs/execution_success/brillig_slices/src/main.nr b/test_programs/execution_success/brillig_slices/src/main.nr new file mode 100644 index 00000000000..48bc8a76bb8 --- /dev/null +++ b/test_programs/execution_success/brillig_slices/src/main.nr @@ -0,0 +1,140 @@ +use dep::std::slice; +unconstrained fn main(x: Field, y: Field) { + let mut slice: [Field] = [y, x]; + assert(slice.len() == 2); + + slice = slice.push_back(7); + assert(slice.len() == 3); + assert(slice[0] == y); + assert(slice[1] == x); + assert(slice[2] == 7); + // Array set on slice target + slice[0] = x; + slice[1] = y; + slice[2] = 1; + + assert(slice[0] == x); + assert(slice[1] == y); + assert(slice[2] == 1); + + slice = push_front_to_slice(slice, 2); + assert(slice.len() == 4); + assert(slice[0] == 2); + assert(slice[1] == x); + assert(slice[2] == y); + assert(slice[3] == 1); + + let (item, popped_front_slice) = slice.pop_front(); + slice = popped_front_slice; + assert(item == 2); + + assert(slice.len() == 3); + assert(slice[0] == x); + assert(slice[1] == y); + assert(slice[2] == 1); + + let (popped_back_slice, another_item) = slice.pop_back(); + slice = popped_back_slice; + assert(another_item == 1); + + assert(slice.len() == 2); + assert(slice[0] == x); + assert(slice[1] == y); + + slice = slice.insert(1, 2); + assert(slice.len() == 3); + assert(slice[0] == x); + assert(slice[1] == 2); + assert(slice[2] == y); + + let (removed_slice, should_be_2) = slice.remove(1); + slice = removed_slice; + assert(should_be_2 == 2); + + assert(slice.len() == 2); + assert(slice[0] == x); + assert(slice[1] == y); + + let (slice_with_only_x, should_be_y) = slice.remove(1); + slice = slice_with_only_x; + assert(should_be_y == y); + + assert(slice.len() == 1); + assert(slice[0] == x); + + let (empty_slice, should_be_x) = slice.remove(0); + assert(should_be_x == x); + assert(empty_slice.len() == 0); + + regression_merge_slices(x, y); +} +// Tests slice passing to/from functions +unconstrained fn push_front_to_slice(slice: [T], item: T) -> [T] { + slice.push_front(item) +} +// The parameters to this function must come from witness values (inputs to main) +unconstrained fn regression_merge_slices(x: Field, y: Field) { + merge_slices_if(x, y); + merge_slices_else(x); +} + +unconstrained fn merge_slices_if(x: Field, y: Field) { + let slice = merge_slices_return(x, y); + assert(slice[2] == 10); + assert(slice.len() == 3); + + let slice = merge_slices_mutate(x, y); + assert(slice[3] == 5); + assert(slice.len() == 4); + + let slice = merge_slices_mutate_in_loop(x, y); + assert(slice[6] == 4); + assert(slice.len() == 7); +} + +unconstrained fn merge_slices_else(x: Field) { + let slice = merge_slices_return(x, 5); + assert(slice[0] == 0); + assert(slice[1] == 0); + assert(slice.len() == 2); + + let slice = merge_slices_mutate(x, 5); + assert(slice[2] == 5); + assert(slice.len() == 3); + + let slice = merge_slices_mutate_in_loop(x, 5); + assert(slice[2] == 5); + assert(slice.len() == 3); +} +// Test returning a merged slice without a mutation +unconstrained 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 + } +} +// Test mutating a slice inside of an if statement +unconstrained fn merge_slices_mutate(x: Field, y: Field) -> [Field] { + let mut slice = [0; 2]; + if x != y { + slice = slice.push_back(y); + slice = slice.push_back(x); + } else { + slice = slice.push_back(x); + } + slice +} +// Test mutating a slice inside of a loop in an if statement +unconstrained fn merge_slices_mutate_in_loop(x: Field, y: Field) -> [Field] { + let mut slice = [0; 2]; + if x != y { + for i in 0..5 { + slice = slice.push_back(i); + } + } else { + slice = slice.push_back(x); + } + slice +} diff --git a/tooling/nargo_cli/tests/execution_success/brillig_to_be_bytes/Nargo.toml b/test_programs/execution_success/brillig_to_be_bytes/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_to_be_bytes/Nargo.toml rename to test_programs/execution_success/brillig_to_be_bytes/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/brillig_to_be_bytes/Prover.toml b/test_programs/execution_success/brillig_to_be_bytes/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_to_be_bytes/Prover.toml rename to test_programs/execution_success/brillig_to_be_bytes/Prover.toml diff --git a/test_programs/execution_success/brillig_to_be_bytes/src/main.nr b/test_programs/execution_success/brillig_to_be_bytes/src/main.nr new file mode 100644 index 00000000000..9d78411f060 --- /dev/null +++ b/test_programs/execution_success/brillig_to_be_bytes/src/main.nr @@ -0,0 +1,12 @@ +unconstrained fn main(x: Field) -> pub [u8; 31] { + // The result of this byte array will be big-endian + let byte_array = x.to_be_bytes(31); + let mut bytes = [0; 31]; + for i in 0..31 { + bytes[i] = byte_array[i]; + } + assert(bytes[30] == 60); + assert(bytes[29] == 33); + assert(bytes[28] == 31); + bytes +} diff --git a/tooling/nargo_cli/tests/execution_success/brillig_to_bytes_integration/Nargo.toml b/test_programs/execution_success/brillig_to_bytes_integration/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_to_bytes_integration/Nargo.toml rename to test_programs/execution_success/brillig_to_bytes_integration/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/brillig_to_bytes_integration/Prover.toml b/test_programs/execution_success/brillig_to_bytes_integration/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_to_bytes_integration/Prover.toml rename to test_programs/execution_success/brillig_to_bytes_integration/Prover.toml diff --git a/test_programs/execution_success/brillig_to_bytes_integration/src/main.nr b/test_programs/execution_success/brillig_to_bytes_integration/src/main.nr new file mode 100644 index 00000000000..e8e5b9db9ca --- /dev/null +++ b/test_programs/execution_success/brillig_to_bytes_integration/src/main.nr @@ -0,0 +1,27 @@ +use dep::std; + +unconstrained fn main(x: Field, _y: Field) { + // The result of this byte array will be big-endian + let y: Field = 2040124; + 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]); + assert(le_byte_array[2] == be_byte_array[28]); + + let z = 0 - 1; + let p_bytes = std::field::modulus_le_bytes(); + let z_bytes = z.to_le_bytes(32); + assert(p_bytes[10] == z_bytes[10]); + assert(p_bytes[0] == z_bytes[0] as u8 + 1 as u8); + + let p_bits = std::field::modulus_le_bits(); + let z_bits = z.to_le_bits(std::field::modulus_num_bits() as u32); + assert(z_bits[0] == 0); + assert(p_bits[100] == z_bits[100]); + + _y.to_le_bits(std::field::modulus_num_bits() as u32); +} diff --git a/tooling/nargo_cli/tests/execution_success/brillig_to_le_bytes/Nargo.toml b/test_programs/execution_success/brillig_to_le_bytes/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_to_le_bytes/Nargo.toml rename to test_programs/execution_success/brillig_to_le_bytes/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/brillig_to_le_bytes/Prover.toml b/test_programs/execution_success/brillig_to_le_bytes/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_to_le_bytes/Prover.toml rename to test_programs/execution_success/brillig_to_le_bytes/Prover.toml diff --git a/test_programs/execution_success/brillig_to_le_bytes/src/main.nr b/test_programs/execution_success/brillig_to_le_bytes/src/main.nr new file mode 100644 index 00000000000..77d292cf01b --- /dev/null +++ b/test_programs/execution_success/brillig_to_le_bytes/src/main.nr @@ -0,0 +1,10 @@ +unconstrained fn main(x: Field) -> pub [u8; 31] { + // The result of this byte array will be little-endian + let byte_array = x.to_le_bytes(31); + assert(byte_array.len() == 31); + let mut bytes = [0; 31]; + for i in 0..31 { + bytes[i] = byte_array[i]; + } + bytes +} diff --git a/tooling/nargo_cli/tests/execution_success/brillig_top_level/Nargo.toml b/test_programs/execution_success/brillig_top_level/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_top_level/Nargo.toml rename to test_programs/execution_success/brillig_top_level/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/brillig_top_level/Prover.toml b/test_programs/execution_success/brillig_top_level/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_top_level/Prover.toml rename to test_programs/execution_success/brillig_top_level/Prover.toml diff --git a/tooling/nargo_cli/tests/execution_success/brillig_top_level/src/main.nr b/test_programs/execution_success/brillig_top_level/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_top_level/src/main.nr rename to test_programs/execution_success/brillig_top_level/src/main.nr diff --git a/tooling/nargo_cli/tests/execution_success/brillig_unitialised_arrays/Nargo.toml b/test_programs/execution_success/brillig_unitialised_arrays/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_unitialised_arrays/Nargo.toml rename to test_programs/execution_success/brillig_unitialised_arrays/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/brillig_unitialised_arrays/Prover.toml b/test_programs/execution_success/brillig_unitialised_arrays/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_unitialised_arrays/Prover.toml rename to test_programs/execution_success/brillig_unitialised_arrays/Prover.toml diff --git a/tooling/nargo_cli/tests/execution_success/brillig_unitialised_arrays/src/main.nr b/test_programs/execution_success/brillig_unitialised_arrays/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/execution_success/brillig_unitialised_arrays/src/main.nr rename to test_programs/execution_success/brillig_unitialised_arrays/src/main.nr diff --git a/tooling/nargo_cli/tests/execution_success/cast_bool/Nargo.toml b/test_programs/execution_success/cast_bool/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/cast_bool/Nargo.toml rename to test_programs/execution_success/cast_bool/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/cast_bool/Prover.toml b/test_programs/execution_success/cast_bool/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/cast_bool/Prover.toml rename to test_programs/execution_success/cast_bool/Prover.toml diff --git a/tooling/nargo_cli/tests/execution_success/cast_bool/src/main.nr b/test_programs/execution_success/cast_bool/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/execution_success/cast_bool/src/main.nr rename to test_programs/execution_success/cast_bool/src/main.nr diff --git a/tooling/nargo_cli/tests/execution_success/closures_mut_ref/Nargo.toml b/test_programs/execution_success/closures_mut_ref/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/closures_mut_ref/Nargo.toml rename to test_programs/execution_success/closures_mut_ref/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/closures_mut_ref/Prover.toml b/test_programs/execution_success/closures_mut_ref/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/closures_mut_ref/Prover.toml rename to test_programs/execution_success/closures_mut_ref/Prover.toml diff --git a/test_programs/execution_success/closures_mut_ref/src/main.nr b/test_programs/execution_success/closures_mut_ref/src/main.nr new file mode 100644 index 00000000000..5a743d1b633 --- /dev/null +++ b/test_programs/execution_success/closures_mut_ref/src/main.nr @@ -0,0 +1,30 @@ +fn main(mut x: Field) { + let one = 1; + let add1 = |z| { + *z = *z + one; + }; + + let two = 2; + let add2 = |z| { + *z = *z + two; + }; + + add1(&mut x); + assert(x == 1); + + add2(&mut x); + assert(x == 3); + + issue_2120(); +} +// https://github.com/noir-lang/noir/issues/2120 +fn issue_2120() { + let x1 = &mut 42; + let set_x1 = |y| { *x1 = y; }; + + assert(*x1 == 42); + set_x1(44); + assert(*x1 == 44); + set_x1(*x1); + assert(*x1 == 44); +} diff --git a/tooling/nargo_cli/tests/execution_success/conditional_1/Nargo.toml b/test_programs/execution_success/conditional_1/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/conditional_1/Nargo.toml rename to test_programs/execution_success/conditional_1/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/conditional_1/Prover.toml b/test_programs/execution_success/conditional_1/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/conditional_1/Prover.toml rename to test_programs/execution_success/conditional_1/Prover.toml diff --git a/test_programs/execution_success/conditional_1/src/main.nr b/test_programs/execution_success/conditional_1/src/main.nr new file mode 100644 index 00000000000..5064c82bce9 --- /dev/null +++ b/test_programs/execution_success/conditional_1/src/main.nr @@ -0,0 +1,93 @@ +use dep::std; + +fn sort(mut a: [u32; 4]) -> [u32; 4] { + for i in 1..4 { + for j in 0..i { + if a[i] < a[j] { + let c = a[j]; + a[j] = a[i]; + a[i] = c; + } + } + } + a +} + +fn must_be_zero(x: u8) { + assert(x == 0); +} + +fn main(a: u32, mut c: [u32; 4], x: [u8; 5], result: pub [u8; 32]) { + //Test case for short-circuit + let mut data = [0 as u32; 32]; + let mut ba = a; + for i in 0..32 { + let i_u32 = i as u32; + if i_u32 == a { + for j in 0..4 { + data[i + j] = c[4 - 1 - j]; + for k in 0..4 { + ba = ba +data[k]; + } + if ba == 4864 { + c[3]=ba; + } + } + } + } + assert(data[31] == 0); + assert(ba != 13); + //Test case for conditional with arrays from function parameters + let b = sort([1, 2, 3, 4]); + assert(b[0] == 1); + + if a == 0 { + must_be_zero(0); + c[0] = 3; + } else { + must_be_zero(1); + c[0] = 1; + c[1] = c[2] / a + 11 % a; + let f1 = a as Field; + assert(10 / f1 != 0); + } + assert(c[0] == 3); + + let mut y = 0; + if a == 0 { + let digest = std::hash::sha256(x); + y = digest[0]; + } else { + y = 5; + } + assert(y == result[0]); + c = sort(c); + assert(c[0] == 0); + //test 1 + let mut x: u32 = 0; + if a == 0 { + c[0] = 12; + if a != 0 { + x = 6; + } else { + x = 2; + assert(x == 2); + } + } else { + x = 5; + assert(x == 5); + } + if c[0] == 0 { + x = 3; + } + assert(x == 2); + //test2: loops + let mut x: u32 = 0; + x = a - a; + for i in 0..4 { + if c[i] == 0 { + x = i as u32 +2; + } + } + assert(x == 0); +} diff --git a/tooling/nargo_cli/tests/execution_success/conditional_2/Nargo.toml b/test_programs/execution_success/conditional_2/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/conditional_2/Nargo.toml rename to test_programs/execution_success/conditional_2/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/conditional_2/Prover.toml b/test_programs/execution_success/conditional_2/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/conditional_2/Prover.toml rename to test_programs/execution_success/conditional_2/Prover.toml diff --git a/test_programs/execution_success/conditional_2/src/main.nr b/test_programs/execution_success/conditional_2/src/main.nr new file mode 100644 index 00000000000..5b3f64f6be5 --- /dev/null +++ b/test_programs/execution_success/conditional_2/src/main.nr @@ -0,0 +1,51 @@ +use dep::std; + +fn must_be_zero(x: u8) { + assert(x == 0); +} + +fn test3(x: u8) { + if x == 0 { + must_be_zero(x); + } +} + +fn test4() -> [u32; 4] { + let b: [u32; 4] = [1, 2, 3, 4]; + b +} + +fn main(a: u32, mut c: [u32; 4]) { + test3(1); + + if a == 0 { + c = test4(); + } else { + assert(c[1] != 2); + } + if false { + c[1] = 5; + } + assert(c[1] == 2); + + test5(4); + // Test case for function synchronisation + let mut c_sync = 0; + if a == 42 { + c_sync = foo2(); + } else { + c_sync = foo2() + foo2(); + } + assert(c_sync == 6); +} + +fn test5(a: u32) { + if a > 1 { + let q = a / 2; + assert(q == 2); + } +} + +fn foo2() -> Field { + 3 +} diff --git a/tooling/nargo_cli/tests/execution_success/conditional_regression_421/Nargo.toml b/test_programs/execution_success/conditional_regression_421/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/conditional_regression_421/Nargo.toml rename to test_programs/execution_success/conditional_regression_421/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/conditional_regression_421/Prover.toml b/test_programs/execution_success/conditional_regression_421/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/conditional_regression_421/Prover.toml rename to test_programs/execution_success/conditional_regression_421/Prover.toml diff --git a/tooling/nargo_cli/tests/execution_success/conditional_regression_421/src/main.nr b/test_programs/execution_success/conditional_regression_421/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/execution_success/conditional_regression_421/src/main.nr rename to test_programs/execution_success/conditional_regression_421/src/main.nr diff --git a/tooling/nargo_cli/tests/execution_success/conditional_regression_661/Nargo.toml b/test_programs/execution_success/conditional_regression_661/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/conditional_regression_661/Nargo.toml rename to test_programs/execution_success/conditional_regression_661/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/conditional_regression_661/Prover.toml b/test_programs/execution_success/conditional_regression_661/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/conditional_regression_661/Prover.toml rename to test_programs/execution_success/conditional_regression_661/Prover.toml diff --git a/test_programs/execution_success/conditional_regression_661/src/main.nr b/test_programs/execution_success/conditional_regression_661/src/main.nr new file mode 100644 index 00000000000..03102eb775e --- /dev/null +++ b/test_programs/execution_success/conditional_regression_661/src/main.nr @@ -0,0 +1,28 @@ +fn main(a: u32, mut c: [u32; 4]) { + // Regression for issue #661: + let mut c_661: [u32; 1] = [0]; + if a > 5 { + c_661 = issue_661_foo(issue_661_bar(c), a); + } else { + c_661 = issue_661_foo(issue_661_bar(c), a + 2); + } + assert(c_661[0] < 20000); +} + +fn test5(a: u32) { + if a > 1 { + let q = a / 2; + assert(q == 2); + } +} + +fn issue_661_foo(array: [u32;4], b: u32) -> [u32;1] { + [array[0] + b] +} + +fn issue_661_bar(a: [u32;4]) -> [u32;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/Nargo.toml b/test_programs/execution_success/conditional_regression_short_circuit/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/conditional_regression_short_circuit/Nargo.toml rename to test_programs/execution_success/conditional_regression_short_circuit/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/conditional_regression_short_circuit/Prover.toml b/test_programs/execution_success/conditional_regression_short_circuit/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/conditional_regression_short_circuit/Prover.toml rename to test_programs/execution_success/conditional_regression_short_circuit/Prover.toml diff --git a/test_programs/execution_success/conditional_regression_short_circuit/src/main.nr b/test_programs/execution_success/conditional_regression_short_circuit/src/main.nr new file mode 100644 index 00000000000..d260fa49dc3 --- /dev/null +++ b/test_programs/execution_success/conditional_regression_short_circuit/src/main.nr @@ -0,0 +1,38 @@ +use dep::std; + +fn main(a: u32, mut c: [u32; 4], x: [u8; 5], result: pub [u8; 32]) { + //regression for short-circuit2 + if 35 == a { + assert(false); + } + bar(a as Field); + + if a == 3 { + c = test4(); + } + assert(c[1] != 2); + call_intrinsic(x, result); +} + +fn foo() { + let mut x = 1; + x /= 0; +} + +fn bar(x: Field) { + if x == 15 { + foo(); + } +} + +fn call_intrinsic(x: [u8; 5], result: [u8; 32]) { + let mut digest = std::hash::sha256(x); + digest[0] = 5 as u8; + digest = std::hash::sha256(x); + assert(digest == result); +} + +fn test4() -> [u32; 4] { + let b: [u32; 4] = [1, 2, 3, 4]; + b +} diff --git a/test_programs/execution_success/conditional_regression_underflow/Nargo.toml b/test_programs/execution_success/conditional_regression_underflow/Nargo.toml new file mode 100644 index 00000000000..75c4fb43b2f --- /dev/null +++ b/test_programs/execution_success/conditional_regression_underflow/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "conditional_regression_underflow" +type = "bin" +authors = [""] + +[dependencies] diff --git a/test_programs/execution_success/conditional_regression_underflow/Prover.toml b/test_programs/execution_success/conditional_regression_underflow/Prover.toml new file mode 100644 index 00000000000..ca65e8b9428 --- /dev/null +++ b/test_programs/execution_success/conditional_regression_underflow/Prover.toml @@ -0,0 +1 @@ +x = "4" \ No newline at end of file diff --git a/test_programs/execution_success/conditional_regression_underflow/src/main.nr b/test_programs/execution_success/conditional_regression_underflow/src/main.nr new file mode 100644 index 00000000000..a101af32505 --- /dev/null +++ b/test_programs/execution_success/conditional_regression_underflow/src/main.nr @@ -0,0 +1,15 @@ +// Regression test for https://github.com/noir-lang/noir/issues/3493 +fn main(x: u4) { + if x == 10 { + x + 15; + } + if x == 9 { + x << 3; + } + if x == 8 { + x * 3; + } + if x == 7 { + x - 8; + } +} diff --git a/tooling/nargo_cli/tests/execution_success/custom_entry/Nargo.toml b/test_programs/execution_success/custom_entry/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/custom_entry/Nargo.toml rename to test_programs/execution_success/custom_entry/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/custom_entry/Prover.toml b/test_programs/execution_success/custom_entry/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/custom_entry/Prover.toml rename to test_programs/execution_success/custom_entry/Prover.toml diff --git a/tooling/nargo_cli/tests/execution_success/custom_entry/src/foobarbaz.nr b/test_programs/execution_success/custom_entry/src/foobarbaz.nr similarity index 100% rename from tooling/nargo_cli/tests/execution_success/custom_entry/src/foobarbaz.nr rename to test_programs/execution_success/custom_entry/src/foobarbaz.nr diff --git a/tooling/nargo_cli/tests/execution_success/debug_logs/Nargo.toml b/test_programs/execution_success/debug_logs/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/debug_logs/Nargo.toml rename to test_programs/execution_success/debug_logs/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/debug_logs/Prover.toml b/test_programs/execution_success/debug_logs/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/debug_logs/Prover.toml rename to test_programs/execution_success/debug_logs/Prover.toml diff --git a/test_programs/execution_success/debug_logs/src/main.nr b/test_programs/execution_success/debug_logs/src/main.nr new file mode 100644 index 00000000000..6accdf725d9 --- /dev/null +++ b/test_programs/execution_success/debug_logs/src/main.nr @@ -0,0 +1,81 @@ +use dep::std; + +fn main(x: Field, y: pub Field) { + let string = "i: {i}, j: {j}"; + std::println(string); + // A `fmtstr` lets you easily perform string interpolation. + let fmt_str: fmtstr<14, (Field, Field)> = f"i: {x}, j: {y}"; + let fmt_str = string_identity(fmt_str); + std::println(fmt_str); + + let fmt_str_no_type = f"i: {x}, j: {y}"; + std::println(fmt_str_no_type); + + let fmt_str_generic = string_with_generics(fmt_str_no_type); + std::println(fmt_str_generic); + + let s = myStruct { y: x, x: y }; + std::println(s); + + std::println(f"randomstring{x}{x}"); + + let fmt_str = string_with_partial_generics(f"i: {x}, s: {s}"); + std::println(fmt_str); + + std::println(x); + std::println([x, y]); + + let foo = fooStruct { my_struct: s, foo: 15 }; + std::println(f"s: {s}, foo: {foo}"); + + std::println(f"x: 0, y: 1"); + + let s_2 = myStruct { x: 20, y: 30 }; + std::println(f"s1: {s}, s2: {s_2}"); + + let bar = fooStruct { my_struct: s_2, foo: 20 }; + std::println(f"foo1: {foo}, foo2: {bar}"); + + let struct_string = if x != 5 { f"{foo}" } else { f"{bar}" }; + std::println(struct_string); + + regression_2906(); +} + +fn string_identity(string: fmtstr<14, (Field, Field)>) -> fmtstr<14, (Field, Field)> { + string +} + +fn string_with_generics(string: fmtstr) -> fmtstr { + string +} + +fn string_with_partial_generics(string: fmtstr) -> fmtstr { + string +} + +struct myStruct { + y: Field, + x: Field, +} + +struct fooStruct { + my_struct: myStruct, + foo: Field, +} + +fn regression_2906() { + let array_two_vals = [1, 2]; + dep::std::println(f"array_two_vals: {array_two_vals}"); + + let label_two_vals = "12"; + dep::std::println(f"label_two_vals: {label_two_vals}"); + + let array_five_vals = [1, 2, 3, 4, 5]; + dep::std::println(f"array_five_vals: {array_five_vals}"); + + let label_five_vals = "12345"; + dep::std::println(f"label_five_vals: {label_five_vals}"); + + dep::std::println(f"array_five_vals: {array_five_vals}, label_five_vals: {label_five_vals}"); +} diff --git a/tooling/nargo_cli/tests/execution_success/diamond_deps_0/Nargo.toml b/test_programs/execution_success/diamond_deps_0/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/diamond_deps_0/Nargo.toml rename to test_programs/execution_success/diamond_deps_0/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/diamond_deps_0/Prover.toml b/test_programs/execution_success/diamond_deps_0/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/diamond_deps_0/Prover.toml rename to test_programs/execution_success/diamond_deps_0/Prover.toml diff --git a/test_programs/execution_success/diamond_deps_0/src/main.nr b/test_programs/execution_success/diamond_deps_0/src/main.nr new file mode 100644 index 00000000000..ca95c6e0aa8 --- /dev/null +++ b/test_programs/execution_success/diamond_deps_0/src/main.nr @@ -0,0 +1,7 @@ +use dep::dep1::call_dep1_then_dep2; +use dep::dep2::call_dep2; +use dep::dep2::RESOLVE_THIS; + +fn main(x: Field, y: pub Field) -> pub Field { + call_dep1_then_dep2(x, y) + call_dep2(x, y) + RESOLVE_THIS +} diff --git a/tooling/nargo_cli/tests/execution_success/distinct_keyword/Nargo.toml b/test_programs/execution_success/distinct_keyword/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/distinct_keyword/Nargo.toml rename to test_programs/execution_success/distinct_keyword/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/distinct_keyword/Prover.toml b/test_programs/execution_success/distinct_keyword/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/distinct_keyword/Prover.toml rename to test_programs/execution_success/distinct_keyword/Prover.toml diff --git a/tooling/nargo_cli/tests/execution_success/distinct_keyword/src/main.nr b/test_programs/execution_success/distinct_keyword/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/execution_success/distinct_keyword/src/main.nr rename to test_programs/execution_success/distinct_keyword/src/main.nr diff --git a/tooling/nargo_cli/tests/execution_success/double_verify_proof/Nargo.toml b/test_programs/execution_success/double_verify_proof/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/double_verify_proof/Nargo.toml rename to test_programs/execution_success/double_verify_proof/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/double_verify_proof/Prover.toml b/test_programs/execution_success/double_verify_proof/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/double_verify_proof/Prover.toml rename to test_programs/execution_success/double_verify_proof/Prover.toml diff --git a/test_programs/execution_success/double_verify_proof/src/main.nr b/test_programs/execution_success/double_verify_proof/src/main.nr new file mode 100644 index 00000000000..98cd534266a --- /dev/null +++ b/test_programs/execution_success/double_verify_proof/src/main.nr @@ -0,0 +1,32 @@ +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.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 + ); + + let mut output = [0; 16]; + for i in 0..16 { + output[i] = output_aggregation_object[i]; + } + output +} diff --git a/tooling/nargo_cli/tests/execution_success/ecdsa_secp256k1/Nargo.toml b/test_programs/execution_success/ecdsa_secp256k1/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/ecdsa_secp256k1/Nargo.toml rename to test_programs/execution_success/ecdsa_secp256k1/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/ecdsa_secp256k1/Prover.toml b/test_programs/execution_success/ecdsa_secp256k1/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/ecdsa_secp256k1/Prover.toml rename to test_programs/execution_success/ecdsa_secp256k1/Prover.toml diff --git a/test_programs/execution_success/ecdsa_secp256k1/src/main.nr b/test_programs/execution_success/ecdsa_secp256k1/src/main.nr new file mode 100644 index 00000000000..2f410755f74 --- /dev/null +++ b/test_programs/execution_success/ecdsa_secp256k1/src/main.nr @@ -0,0 +1,16 @@ +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); + + let valid_signature = std::ecdsa_secp256k1::verify_signature(pub_key_x, pub_key_y, signature, hashed_message); + assert(valid_signature); +} diff --git a/tooling/nargo_cli/tests/execution_success/ecdsa_secp256r1/Nargo.toml b/test_programs/execution_success/ecdsa_secp256r1/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/ecdsa_secp256r1/Nargo.toml rename to test_programs/execution_success/ecdsa_secp256r1/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/ecdsa_secp256r1/Prover.toml b/test_programs/execution_success/ecdsa_secp256r1/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/ecdsa_secp256r1/Prover.toml rename to test_programs/execution_success/ecdsa_secp256r1/Prover.toml diff --git a/test_programs/execution_success/ecdsa_secp256r1/src/main.nr b/test_programs/execution_success/ecdsa_secp256r1/src/main.nr new file mode 100644 index 00000000000..d23573d13a6 --- /dev/null +++ b/test_programs/execution_success/ecdsa_secp256r1/src/main.nr @@ -0,0 +1,6 @@ +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); +} diff --git a/tooling/nargo_cli/tests/execution_success/eddsa/Nargo.toml b/test_programs/execution_success/eddsa/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/eddsa/Nargo.toml rename to test_programs/execution_success/eddsa/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/eddsa/Prover.toml b/test_programs/execution_success/eddsa/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/eddsa/Prover.toml rename to test_programs/execution_success/eddsa/Prover.toml diff --git a/test_programs/execution_success/eddsa/src/main.nr b/test_programs/execution_success/eddsa/src/main.nr new file mode 100644 index 00000000000..12e8ea92785 --- /dev/null +++ b/test_programs/execution_success/eddsa/src/main.nr @@ -0,0 +1,46 @@ +use dep::std::compat; +use dep::std::ec::consts::te::baby_jubjub; +use dep::std::hash; +use dep::std::eddsa::eddsa_poseidon_verify; +fn main(msg: pub Field, _priv_key_a: Field, _priv_key_b: Field) { + // Skip this test for non-bn254 backends + if compat::is_bn254() { + let bjj = baby_jubjub(); + + let pub_key_a = bjj.curve.mul(_priv_key_a, bjj.curve.gen); + // let pub_key_b = bjj.curve.mul(_priv_key_b, bjj.curve.gen); + // Manually computed as fields can't use modulo. Importantantly the commitment is within + // the subgroup order. Note that choice of hash is flexible for this step. + // let r_a = hash::pedersen_commitment([_priv_key_a, msg])[0] % bjj.suborder; // modulus computed manually + let r_a = 1414770703199880747815475415092878800081323795074043628810774576767372531818; + // let r_b = hash::pedersen_commitment([_priv_key_b, msg])[0] % bjj.suborder; // modulus computed manually + let r_b = 571799555715456644614141527517766533395606396271089506978608487688924659618; + + let r8_a = bjj.curve.mul(r_a, bjj.base8); + let r8_b = bjj.curve.mul(r_b, bjj.base8); + // let h_a: [Field; 6] = hash::poseidon::bn254::hash_5([ + // r8_a.x, + // r8_a.y, + // pub_key_a.x, + // pub_key_a.y, + // msg, + // ]); + // let h_b: [Field; 6] = hash::poseidon::bn254::hash_5([ + // r8_b.x, + // r8_b.y, + // pub_key_b.x, + // pub_key_b.y, + // msg, + // ]); + // let s_a = (r_a + _priv_key_a * h_a) % bjj.suborder; // modulus computed manually + let s_a = 30333430637424319196043722294837632681219980330991241982145549329256671548; + // let s_b = (r_b + _priv_key_b * h_b) % bjj.suborder; // modulus computed manually + let s_b = 1646085314320208098241070054368798527940102577261034947654839408482102287019; + // User A verifies their signature over the message + assert(eddsa_poseidon_verify(pub_key_a.x, pub_key_a.y, s_a, r8_a.x, r8_a.y, msg)); + // User B's signature over the message can't be used with user A's pub key + assert(!eddsa_poseidon_verify(pub_key_a.x, pub_key_a.y, s_b, r8_b.x, r8_b.y, msg)); + // User A's signature over the message can't be used with another message + assert(!eddsa_poseidon_verify(pub_key_a.x, pub_key_a.y, s_a, r8_a.x, r8_a.y, msg + 1)); + } +} diff --git a/tooling/nargo_cli/tests/execution_success/field_attribute/Nargo.toml b/test_programs/execution_success/field_attribute/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/field_attribute/Nargo.toml rename to test_programs/execution_success/field_attribute/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/field_attribute/Prover.toml b/test_programs/execution_success/field_attribute/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/field_attribute/Prover.toml rename to test_programs/execution_success/field_attribute/Prover.toml diff --git a/tooling/nargo_cli/tests/execution_success/field_attribute/src/main.nr b/test_programs/execution_success/field_attribute/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/execution_success/field_attribute/src/main.nr rename to test_programs/execution_success/field_attribute/src/main.nr diff --git a/tooling/nargo_cli/tests/execution_success/generics/Nargo.toml b/test_programs/execution_success/generics/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/generics/Nargo.toml rename to test_programs/execution_success/generics/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/generics/Prover.toml b/test_programs/execution_success/generics/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/generics/Prover.toml rename to test_programs/execution_success/generics/Prover.toml diff --git a/test_programs/execution_success/generics/src/main.nr b/test_programs/execution_success/generics/src/main.nr new file mode 100644 index 00000000000..3edce1ed8e7 --- /dev/null +++ b/test_programs/execution_success/generics/src/main.nr @@ -0,0 +1,67 @@ +struct Bar { + one: Field, + two: Field, + other: T, +} + +fn foo(bar: Bar) { + assert(bar.one == bar.two); +} + +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 + } +} + +impl Bar { + fn get_other(self) -> Field { + self.other + } +} + +fn main(x: Field, y: Field) { + let bar1: Bar = Bar { one: x, two: y, other: 0 }; + let bar2 = Bar { one: x, two: y, other: [0] }; + + foo(bar1); + foo(bar2); + // Test generic impls + let int1 = BigInt { limbs: [1] }; + let int2 = BigInt { limbs: [2] }; + let BigInt { limbs } = int1.second(int2).first(int1); + assert(limbs == int2.limbs); + // Test impl exclusively for Bar + assert(bar1.get_other() == bar1.other); + // Expected type error + // assert(bar2.get_other() == bar2.other); + let one = x; + let two = y; + let nested_generics: Bar> = Bar { one, two, other: Bar { one, two, other: 0 } }; + assert(nested_generics.other.other == bar1.get_other()); + + let _ = regression_2055([1, 2, 3]); +} + +fn regression_2055(bytes: [u8; LEN]) -> Field { + let mut f = 0; + let mut b = 1; + let mut len = LEN - 1; // FAILS + for i in 0..LEN { + let j = len - i; + f += (bytes[j] as Field) * b; + b *= 256; + } + f +} diff --git a/tooling/nargo_cli/tests/execution_success/global_consts/Nargo.toml b/test_programs/execution_success/global_consts/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/global_consts/Nargo.toml rename to test_programs/execution_success/global_consts/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/global_consts/Prover.toml b/test_programs/execution_success/global_consts/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/global_consts/Prover.toml rename to test_programs/execution_success/global_consts/Prover.toml diff --git a/test_programs/execution_success/global_consts/src/baz.nr b/test_programs/execution_success/global_consts/src/baz.nr new file mode 100644 index 00000000000..4271de81118 --- /dev/null +++ b/test_programs/execution_success/global_consts/src/baz.nr @@ -0,0 +1,5 @@ +pub fn from_baz(x: [Field; crate::foo::MAGIC_NUMBER]) { + for i in 0..crate::foo::MAGIC_NUMBER { + assert(x[i] == crate::foo::MAGIC_NUMBER); + } +} diff --git a/test_programs/execution_success/global_consts/src/foo.nr b/test_programs/execution_success/global_consts/src/foo.nr new file mode 100644 index 00000000000..7b0ae75b74b --- /dev/null +++ b/test_programs/execution_success/global_consts/src/foo.nr @@ -0,0 +1,11 @@ +mod bar; + +global N: Field = 5; +global MAGIC_NUMBER: Field = 3; +global TYPE_INFERRED = 42; + +pub fn from_foo(x: [Field; bar::N]) { + for i in 0..bar::N { + assert(x[i] == bar::N); + } +} diff --git a/test_programs/execution_success/global_consts/src/foo/bar.nr b/test_programs/execution_success/global_consts/src/foo/bar.nr new file mode 100644 index 00000000000..b8d0b85b0f3 --- /dev/null +++ b/test_programs/execution_success/global_consts/src/foo/bar.nr @@ -0,0 +1,5 @@ +global N: Field = 5; + +pub fn from_bar(x: Field) -> Field { + x * N +} diff --git a/test_programs/execution_success/global_consts/src/main.nr b/test_programs/execution_success/global_consts/src/main.nr new file mode 100644 index 00000000000..a749ec77da6 --- /dev/null +++ b/test_programs/execution_success/global_consts/src/main.nr @@ -0,0 +1,96 @@ +mod foo; +mod baz; + +global M: Field = 32; +global L: Field = 10; // Unused globals currently allowed +global N: Field = 5; +global T_LEN = 2; // Type inference is allowed on globals +//global N: Field = 5; // Uncomment to see duplicate globals error +struct Dummy { + x: [Field; N], + y: [Field; foo::MAGIC_NUMBER] +} + +struct Test { + v: Field, +} +global VALS: [Test; 1] = [Test { v: 100 }]; +global NESTED = [VALS, VALS]; + +fn main( + a: [Field; M + N - N], + b: [Field; 30 + N / 2], + c: pub [Field; foo::MAGIC_NUMBER], + d: [Field; foo::bar::N] +) { + let test_struct = Dummy { x: d, y: c }; + + for i in 0..foo::MAGIC_NUMBER { + assert(c[i] == foo::MAGIC_NUMBER); + assert(test_struct.y[i] == foo::MAGIC_NUMBER); + assert(test_struct.y[i] != NESTED[1][0].v); + } + + assert(N != M); + + let expected: u32 = 42; + assert(foo::TYPE_INFERRED == expected); + + let mut y = 5; + let mut x = M; + for i in 0..N * N { + let M: Field = 10; + x = M; + + y = i; + } + assert(y == 24); + assert(x == 10); + + let q = multiplyByM(3); + assert(q == 96); + + arrays_neq(a, b); + + let t: [Field; T_LEN] = [N, M]; + assert(t[1] == 32); + + assert(15 == mysubmodule::my_helper()); + + let add_submodules_N = mysubmodule::N + foo::bar::N; + assert(15 == add_submodules_N); + let add_from_bar_N = mysubmodule::N + foo::bar::from_bar(1); + 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); + + let arr: [Field; mysubmodule::N] = [N; 10]; + assert((arr[0] == 5) & (arr[9] == 5)); + + foo::from_foo(d); + baz::from_baz(c); +} + +fn multiplyByM(x: Field) -> Field { + x * M +} + +fn arrays_neq(a: [Field; M], b: [Field; M]) { + assert(a != b); +} + +mod mysubmodule { + global N: Field = 10; + global L: Field = 50; + + fn my_bool_or(x: u1, y: u1) { + assert(x | y == 1); + } + + pub fn my_helper() -> Field { + let N: Field = 15; // Like in Rust, local variables override globals + let x = N; + x + } +} diff --git a/tooling/nargo_cli/tests/execution_success/hash_to_field/Nargo.toml b/test_programs/execution_success/hash_to_field/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/hash_to_field/Nargo.toml rename to test_programs/execution_success/hash_to_field/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/hash_to_field/Prover.toml b/test_programs/execution_success/hash_to_field/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/hash_to_field/Prover.toml rename to test_programs/execution_success/hash_to_field/Prover.toml diff --git a/test_programs/execution_success/hash_to_field/src/main.nr b/test_programs/execution_success/hash_to_field/src/main.nr new file mode 100644 index 00000000000..5af1c5af55e --- /dev/null +++ b/test_programs/execution_success/hash_to_field/src/main.nr @@ -0,0 +1,5 @@ +use dep::std; + +fn main(input: Field) -> pub Field { + std::hash::hash_to_field([input]) +} diff --git a/tooling/nargo_cli/tests/execution_success/higher_order_functions/Nargo.toml b/test_programs/execution_success/higher_order_functions/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/higher_order_functions/Nargo.toml rename to test_programs/execution_success/higher_order_functions/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/higher_order_functions/Prover.toml b/test_programs/execution_success/higher_order_functions/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/higher_order_functions/Prover.toml rename to test_programs/execution_success/higher_order_functions/Prover.toml diff --git a/test_programs/execution_success/higher_order_functions/src/main.nr b/test_programs/execution_success/higher_order_functions/src/main.nr new file mode 100644 index 00000000000..6583f961d58 --- /dev/null +++ b/test_programs/execution_success/higher_order_functions/src/main.nr @@ -0,0 +1,99 @@ +fn main(w: Field) -> pub Field { + let f = if 3 * 7 > 200 as u32 { foo } else { bar }; + assert(f()[1] == 2); + // Lambdas: + assert(twice(|x| x * 2, 5) == 20); + assert((|x, y| x + y + 1)(2, 3) == 6); + // nested lambdas + assert( + (|a, b| { + a + (|c| c + 2)(b) + })(0, 1) + == 3 + ); + // Closures: + let a = 42; + let g = || a; + assert(g() == 42); + // When you copy mutable variables, + // the capture of the copies shouldn't change: + let mut x = 2; + x = x + 1; + let z = x; + // Add extra mutations to ensure we can mutate x without the + // captured z changing. + x = x + 1; + assert((|y| y + z)(1) == 4); + // When you capture mutable variables, + // again, the captured variable doesn't change: + let closure_capturing_mutable = (|y| y + x); + assert(closure_capturing_mutable(1) == 5); + x += 1; + assert(closure_capturing_mutable(1) == 5); + + regression_2154(); + + let ret = twice(add1, 3); + + test_array_functions(); + w + ret +} +/// Test the array functions in std::array +fn test_array_functions() { + let two = 2; // giving this a name, to ensure that the Option functions work with closures + let myarray: [i32; 3] = [1, 2, 3]; + assert(myarray.any(|n| n > 2)); + assert(myarray.any(|n| n > two)); + + let evens: [i32; 3] = myarray.map(|n| n * two); // [2, 4, 6] + assert(evens.all(|n| n > 1)); + assert(evens.all(|n| n >= two)); + + assert(evens.fold(0, |a, b| a + b) == 12); + assert(evens.fold(0, |a, b| a + b + two) == 18); + assert(evens.reduce(|a, b| a + b) == 12); + assert(evens.reduce(|a, b| a + b + two) == 16); + // TODO: is this a sort_via issue with the new backend, + // or something more general? + // + // currently it fails only with `--experimental-ssa` with + // "not yet implemented: Cast into signed" + // but it worked with the original ssa backend + // (before dropping it) + // + // opened #2121 for it + // https://github.com/noir-lang/noir/issues/2121 + // let descending = myarray.sort_via(|a, b| a > b); + // assert(descending == [3, 2, 1]); + assert(evens.map(|n| n / 2) == myarray); + assert(evens.map(|n| n / two) == myarray); +} + +fn foo() -> [u32; 2] { + [1, 3] +} + +fn bar() -> [u32; 2] { + [3, 2] +} + +fn add1(x: Field) -> Field { + x + 1 +} + +fn twice(f: fn(Field) -> Field, x: Field) -> Field { + f(f(x)) +} +// Fixing an ICE, where rewriting the closures +// during monomorphization didn't correspond +// to an internal `if` type +// found by @jfecher: +// https://github.com/noir-lang/noir/pull/1959#issuecomment-1658992989 +// issue https://github.com/noir-lang/noir/issues/2154 +fn regression_2154() { + let x: u32 = 32; + + let closure_if_else = if x > 2 { || x } else { || x + 2342 }; + + assert(closure_if_else() == 32); +} diff --git a/tooling/nargo_cli/tests/execution_success/if_else_chain/Nargo.toml b/test_programs/execution_success/if_else_chain/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/if_else_chain/Nargo.toml rename to test_programs/execution_success/if_else_chain/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/if_else_chain/Prover.toml b/test_programs/execution_success/if_else_chain/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/if_else_chain/Prover.toml rename to test_programs/execution_success/if_else_chain/Prover.toml diff --git a/tooling/nargo_cli/tests/execution_success/if_else_chain/src/main.nr b/test_programs/execution_success/if_else_chain/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/execution_success/if_else_chain/src/main.nr rename to test_programs/execution_success/if_else_chain/src/main.nr diff --git a/tooling/nargo_cli/tests/execution_success/import/Nargo.toml b/test_programs/execution_success/import/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/import/Nargo.toml rename to test_programs/execution_success/import/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/import/Prover.toml b/test_programs/execution_success/import/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/import/Prover.toml rename to test_programs/execution_success/import/Prover.toml diff --git a/test_programs/execution_success/import/src/import.nr b/test_programs/execution_success/import/src/import.nr new file mode 100644 index 00000000000..ef3f0d94c28 --- /dev/null +++ b/test_programs/execution_success/import/src/import.nr @@ -0,0 +1,3 @@ +pub fn hello(x: Field) -> Field { + x +} diff --git a/test_programs/execution_success/import/src/main.nr b/test_programs/execution_success/import/src/main.nr new file mode 100644 index 00000000000..7dcc16fed16 --- /dev/null +++ b/test_programs/execution_success/import/src/main.nr @@ -0,0 +1,9 @@ +mod import; +use crate::import::hello; + +fn main(x: Field, y: Field) { + let _k = dep::std::hash::pedersen_commitment([x]); + let _l = hello(x); + + assert(x != import::hello(y)); +} diff --git a/tooling/nargo_cli/tests/execution_success/integer_array_indexing/Nargo.toml b/test_programs/execution_success/integer_array_indexing/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/integer_array_indexing/Nargo.toml rename to test_programs/execution_success/integer_array_indexing/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/integer_array_indexing/Prover.toml b/test_programs/execution_success/integer_array_indexing/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/integer_array_indexing/Prover.toml rename to test_programs/execution_success/integer_array_indexing/Prover.toml diff --git a/tooling/nargo_cli/tests/execution_success/integer_array_indexing/src/main.nr b/test_programs/execution_success/integer_array_indexing/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/execution_success/integer_array_indexing/src/main.nr rename to test_programs/execution_success/integer_array_indexing/src/main.nr diff --git a/tooling/nargo_cli/tests/execution_success/keccak256/Nargo.toml b/test_programs/execution_success/keccak256/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/keccak256/Nargo.toml rename to test_programs/execution_success/keccak256/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/keccak256/Prover.toml b/test_programs/execution_success/keccak256/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/keccak256/Prover.toml rename to test_programs/execution_success/keccak256/Prover.toml diff --git a/test_programs/execution_success/keccak256/src/main.nr b/test_programs/execution_success/keccak256/src/main.nr new file mode 100644 index 00000000000..ff2167694d6 --- /dev/null +++ b/test_programs/execution_success/keccak256/src/main.nr @@ -0,0 +1,21 @@ +// Keccak256 example +// +use dep::std; + +fn main(x: Field, result: [u8; 32]) { + // We use the `as` keyword here to denote the fact that we want to take just the first byte from the x Field + // The padding is taken care of by the program + let digest = std::hash::keccak256([x as u8], 1); + assert(digest == result); + //#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); + + 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); + + assert(hash_a != hash_c); +} diff --git a/tooling/nargo_cli/tests/execution_success/main_bool_arg/Nargo.toml b/test_programs/execution_success/main_bool_arg/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/main_bool_arg/Nargo.toml rename to test_programs/execution_success/main_bool_arg/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/main_bool_arg/Prover.toml b/test_programs/execution_success/main_bool_arg/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/main_bool_arg/Prover.toml rename to test_programs/execution_success/main_bool_arg/Prover.toml diff --git a/test_programs/execution_success/main_bool_arg/src/main.nr b/test_programs/execution_success/main_bool_arg/src/main.nr new file mode 100644 index 00000000000..111a23ec0c2 --- /dev/null +++ b/test_programs/execution_success/main_bool_arg/src/main.nr @@ -0,0 +1,8 @@ +fn main(x: bool, y: [bool;2]) { + if x { + assert(1 != 2); + } + + assert(x); + assert(y[0] != y[1]); +} diff --git a/tooling/nargo_cli/tests/execution_success/merkle_insert/Nargo.toml b/test_programs/execution_success/merkle_insert/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/merkle_insert/Nargo.toml rename to test_programs/execution_success/merkle_insert/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/merkle_insert/Prover.toml b/test_programs/execution_success/merkle_insert/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/merkle_insert/Prover.toml rename to test_programs/execution_success/merkle_insert/Prover.toml diff --git a/test_programs/execution_success/merkle_insert/src/main.nr b/test_programs/execution_success/merkle_insert/src/main.nr new file mode 100644 index 00000000000..ac9a7b34ea3 --- /dev/null +++ b/test_programs/execution_success/merkle_insert/src/main.nr @@ -0,0 +1,22 @@ +use dep::std; +use dep::std::hash::mimc; + +fn main( + old_root: Field, + old_leaf: Field, + old_hash_path: [Field; 3], + new_root: pub Field, + leaf: Field, + index: Field, + mimc_input: [Field; 4] +) { + assert(old_root == std::merkle::compute_merkle_root(old_leaf, index, old_hash_path)); + + let calculated_root = std::merkle::compute_merkle_root(leaf, index, old_hash_path); + assert(new_root == calculated_root); + + let h = mimc::mimc_bn254(mimc_input); + // Regression test for PR #891 + std::println(h); + assert(h == 18226366069841799622585958305961373004333097209608110160936134895615261821931); +} diff --git a/tooling/nargo_cli/tests/execution_success/mock_oracle/Nargo.toml b/test_programs/execution_success/mock_oracle/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/mock_oracle/Nargo.toml rename to test_programs/execution_success/mock_oracle/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/mock_oracle/Prover.toml b/test_programs/execution_success/mock_oracle/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/mock_oracle/Prover.toml rename to test_programs/execution_success/mock_oracle/Prover.toml diff --git a/tooling/nargo_cli/tests/execution_success/mock_oracle/src/main.nr b/test_programs/execution_success/mock_oracle/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/execution_success/mock_oracle/src/main.nr rename to test_programs/execution_success/mock_oracle/src/main.nr diff --git a/tooling/nargo_cli/tests/execution_success/modules/Nargo.toml b/test_programs/execution_success/modules/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/modules/Nargo.toml rename to test_programs/execution_success/modules/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/modules/Prover.toml b/test_programs/execution_success/modules/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/modules/Prover.toml rename to test_programs/execution_success/modules/Prover.toml diff --git a/test_programs/execution_success/modules/src/foo.nr b/test_programs/execution_success/modules/src/foo.nr new file mode 100644 index 00000000000..ef3f0d94c28 --- /dev/null +++ b/test_programs/execution_success/modules/src/foo.nr @@ -0,0 +1,3 @@ +pub fn hello(x: Field) -> Field { + x +} diff --git a/tooling/nargo_cli/tests/execution_success/modules/src/main.nr b/test_programs/execution_success/modules/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/execution_success/modules/src/main.nr rename to test_programs/execution_success/modules/src/main.nr diff --git a/tooling/nargo_cli/tests/execution_success/modules_more/Nargo.toml b/test_programs/execution_success/modules_more/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/modules_more/Nargo.toml rename to test_programs/execution_success/modules_more/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/modules_more/Prover.toml b/test_programs/execution_success/modules_more/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/modules_more/Prover.toml rename to test_programs/execution_success/modules_more/Prover.toml diff --git a/test_programs/execution_success/modules_more/src/foo.nr b/test_programs/execution_success/modules_more/src/foo.nr new file mode 100644 index 00000000000..fa531a1a2f0 --- /dev/null +++ b/test_programs/execution_success/modules_more/src/foo.nr @@ -0,0 +1,5 @@ +mod bar; + +fn hello(x: Field) -> Field { + x +} diff --git a/test_programs/execution_success/modules_more/src/foo/bar.nr b/test_programs/execution_success/modules_more/src/foo/bar.nr new file mode 100644 index 00000000000..1665f720be6 --- /dev/null +++ b/test_programs/execution_success/modules_more/src/foo/bar.nr @@ -0,0 +1,3 @@ +pub fn from_bar(x: Field) -> Field { + x +} diff --git a/test_programs/execution_success/modules_more/src/main.nr b/test_programs/execution_success/modules_more/src/main.nr new file mode 100644 index 00000000000..93b76d62845 --- /dev/null +++ b/test_programs/execution_success/modules_more/src/main.nr @@ -0,0 +1,5 @@ +mod foo; +// An example of the module system +fn main(x: Field, y: Field) { + assert(x != foo::bar::from_bar(y)); +} diff --git a/tooling/nargo_cli/tests/execution_success/modulus/Nargo.toml b/test_programs/execution_success/modulus/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/modulus/Nargo.toml rename to test_programs/execution_success/modulus/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/modulus/Prover.toml b/test_programs/execution_success/modulus/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/modulus/Prover.toml rename to test_programs/execution_success/modulus/Prover.toml diff --git a/test_programs/execution_success/modulus/src/main.nr b/test_programs/execution_success/modulus/src/main.nr new file mode 100644 index 00000000000..35f63fdc8c5 --- /dev/null +++ b/test_programs/execution_success/modulus/src/main.nr @@ -0,0 +1,25 @@ +use dep::std; + +fn main(bn254_modulus_be_bytes: [u8; 32], bn254_modulus_be_bits: [u1; 254]) { + let modulus_size = std::field::modulus_num_bits(); + // NOTE: The constraints used in this circuit will only work when testing nargo with the plonk bn254 backend + assert(modulus_size == 254); + + let modulus_be_byte_array = std::field::modulus_be_bytes(); + for i in 0..32 { + assert(modulus_be_byte_array[i] == bn254_modulus_be_bytes[i]); + } + let modulus_le_byte_array = std::field::modulus_le_bytes(); + for i in 0..32 { + assert(modulus_le_byte_array[i] == bn254_modulus_be_bytes[31 - i]); + } + + let modulus_be_bits = std::field::modulus_be_bits(); + for i in 0..254 { + assert(modulus_be_bits[i] == bn254_modulus_be_bits[i]); + } + let modulus_le_bits = std::field::modulus_le_bits(); + for i in 0..254 { + assert(modulus_le_bits[i] == bn254_modulus_be_bits[253 - i]); + } +} diff --git a/tooling/nargo_cli/tests/execution_success/nested_array_dynamic/Nargo.toml b/test_programs/execution_success/nested_array_dynamic/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/nested_array_dynamic/Nargo.toml rename to test_programs/execution_success/nested_array_dynamic/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/nested_array_dynamic/Prover.toml b/test_programs/execution_success/nested_array_dynamic/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/nested_array_dynamic/Prover.toml rename to test_programs/execution_success/nested_array_dynamic/Prover.toml diff --git a/test_programs/execution_success/nested_array_dynamic/src/main.nr b/test_programs/execution_success/nested_array_dynamic/src/main.nr new file mode 100644 index 00000000000..2c53822d6b9 --- /dev/null +++ b/test_programs/execution_success/nested_array_dynamic/src/main.nr @@ -0,0 +1,76 @@ +struct Bar { + inner: [Field; 3], +} + +struct Foo { + a: Field, + b: [Field; 3], + bar: Bar, +} + +struct FooParent { + array: [Field; 3], + foos: [Foo; 4], +} + +fn main(mut x: [Foo; 4], y: pub Field) { + assert(x[y - 3].a == 1); + assert(x[y - 3].b == [2, 3, 20]); + assert(x[y - 2].a == 4); + assert(x[y - 2].b == [5, 6, 21]); + assert(x[y - 1].a == 7); + assert(x[y - 1].b == [8, 9, 22]); + assert(x[y].a == 10); + assert(x[y].b == [11, 12, 23]); + assert(x[y].bar.inner == [109, 110, 111]); + // Check dynamic array set + if y != 2 { + x[y].a = 50; + } else { + x[y].a = 100; + } + assert(x[3].a == 50); + + if y == 2 { + x[y - 1].b = [50, 51, 52]; + } else { + 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]); + assert(x[y].bar.inner == [109, 110, 111]); + + let foo_parent_one = FooParent { array: [0, 1, 2], foos: x }; + let foo_parent_two = FooParent { array: [3, 4, 5], foos: x }; + let mut foo_parents = [foo_parent_one, foo_parent_two]; + + assert(foo_parents[y - 3].foos[y - 3].b == [2, 3, 20]); + 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]); + if y == 2 { + foo_parents[y - 2].foos[y - 2].b = [10, 9, 8]; + } else { + foo_parents[y - 2].foos[y - 2].b = [20, 19, 18]; + } + assert(foo_parents[1].foos[1].b == [20, 19, 18]); + + assert(foo_parents[1].foos[1].b[2] == 18); + if y == 3 { + foo_parents[y - 2].foos[y - 2].b[y - 1] = 5000; + } else { + foo_parents[y - 2].foos[y - 2].b[y - 1] = 1000; + } + assert(foo_parents[1].foos[1].b[2] == 5000); + // 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/Nargo.toml b/test_programs/execution_success/nested_arrays_from_brillig/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/nested_arrays_from_brillig/Nargo.toml rename to test_programs/execution_success/nested_arrays_from_brillig/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/nested_arrays_from_brillig/Prover.toml b/test_programs/execution_success/nested_arrays_from_brillig/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/nested_arrays_from_brillig/Prover.toml rename to test_programs/execution_success/nested_arrays_from_brillig/Prover.toml diff --git a/tooling/nargo_cli/tests/execution_success/nested_arrays_from_brillig/src/main.nr b/test_programs/execution_success/nested_arrays_from_brillig/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/execution_success/nested_arrays_from_brillig/src/main.nr rename to test_programs/execution_success/nested_arrays_from_brillig/src/main.nr diff --git a/tooling/nargo_cli/tests/execution_success/nested_slice_dynamic/Nargo.toml b/test_programs/execution_success/nested_slice_dynamic/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/nested_slice_dynamic/Nargo.toml rename to test_programs/execution_success/nested_slice_dynamic/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/nested_slice_dynamic/Prover.toml b/test_programs/execution_success/nested_slice_dynamic/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/nested_slice_dynamic/Prover.toml rename to test_programs/execution_success/nested_slice_dynamic/Prover.toml diff --git a/test_programs/execution_success/nested_slice_dynamic/src/main.nr b/test_programs/execution_success/nested_slice_dynamic/src/main.nr new file mode 100644 index 00000000000..a3007d5d0dc --- /dev/null +++ b/test_programs/execution_success/nested_slice_dynamic/src/main.nr @@ -0,0 +1,49 @@ +struct Bar { + inner: [Field; 3], +} + +struct Foo { + a: Field, + b: [Field; 3], + bar: Bar, +} + +fn main(y: Field) { + let foo_one = Foo { a: 1, b: [2, 3, 20], bar: Bar { inner: [100, 101, 102] } }; + let foo_two = Foo { a: 4, b: [5, 6, 21], bar: Bar { inner: [103, 104, 105] } }; + let foo_three = Foo { a: 7, b: [8, 9, 22], bar: Bar { inner: [106, 107, 108] } }; + let foo_four = Foo { a: 10, b: [11, 12, 23], bar: Bar { inner: [109, 110, 111] } }; + let mut x = [foo_one]; + x = x.push_back(foo_two); + x = x.push_back(foo_three); + x = x.push_back(foo_four); + + assert(x[y - 3].a == 1); + assert(x[y - 3].b == [2, 3, 20]); + assert(x[y - 2].a == 4); + assert(x[y - 2].b == [5, 6, 21]); + assert(x[y - 1].a == 7); + assert(x[y - 1].b == [8, 9, 22]); + assert(x[y].a == 10); + assert(x[y].b == [11, 12, 23]); + assert(x[y].bar.inner == [109, 110, 111]); + + if y != 2 { + x[y - 2].a = 50; + } else { + x[y - 2].a = 100; + } + assert(x[y - 2].a == 50); + + if y == 2 { + x[y - 1].b = [50, 51, 52]; + } else { + 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]); + assert(x[y].bar.inner == [109, 110, 111]); +} diff --git a/tooling/nargo_cli/tests/execution_success/pedersen_check/Nargo.toml b/test_programs/execution_success/pedersen_check/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/pedersen_check/Nargo.toml rename to test_programs/execution_success/pedersen_check/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/pedersen_check/Prover.toml b/test_programs/execution_success/pedersen_check/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/pedersen_check/Prover.toml rename to test_programs/execution_success/pedersen_check/Prover.toml diff --git a/test_programs/execution_success/pedersen_check/src/main.nr b/test_programs/execution_success/pedersen_check/src/main.nr new file mode 100644 index 00000000000..90ef218249b --- /dev/null +++ b/test_programs/execution_success/pedersen_check/src/main.nr @@ -0,0 +1,22 @@ +use dep::std; + +fn main(x: Field, y: Field, salt: Field, out_x: Field, out_y: Field, out_hash: Field) { + let res = std::hash::pedersen_commitment([x, y]); + assert(res.x == out_x); + assert(res.y == out_y); + + let res_hash = std::hash::pedersen_hash_with_separator([x, y], 0); + assert_eq(res_hash, out_hash); + + assert(res_hash != res.x); + + let raw_data = [x, y]; + let mut state = 0; + for i in 0..2 { + state = state * 8 + raw_data[i]; + } + state += salt; + let hash = std::hash::pedersen_commitment([state]); + assert(std::hash::pedersen_commitment([43]).x == hash.x); +} + diff --git a/tooling/nargo_cli/tests/execution_success/poseidon_bn254_hash/Nargo.toml b/test_programs/execution_success/poseidon_bn254_hash/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/poseidon_bn254_hash/Nargo.toml rename to test_programs/execution_success/poseidon_bn254_hash/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/poseidon_bn254_hash/Prover.toml b/test_programs/execution_success/poseidon_bn254_hash/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/poseidon_bn254_hash/Prover.toml rename to test_programs/execution_success/poseidon_bn254_hash/Prover.toml diff --git a/tooling/nargo_cli/tests/execution_success/poseidon_bn254_hash/src/main.nr b/test_programs/execution_success/poseidon_bn254_hash/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/execution_success/poseidon_bn254_hash/src/main.nr rename to test_programs/execution_success/poseidon_bn254_hash/src/main.nr diff --git a/tooling/nargo_cli/tests/execution_success/poseidonsponge_x5_254/Nargo.toml b/test_programs/execution_success/poseidonsponge_x5_254/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/poseidonsponge_x5_254/Nargo.toml rename to test_programs/execution_success/poseidonsponge_x5_254/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/poseidonsponge_x5_254/Prover.toml b/test_programs/execution_success/poseidonsponge_x5_254/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/poseidonsponge_x5_254/Prover.toml rename to test_programs/execution_success/poseidonsponge_x5_254/Prover.toml diff --git a/tooling/nargo_cli/tests/execution_success/poseidonsponge_x5_254/src/main.nr b/test_programs/execution_success/poseidonsponge_x5_254/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/execution_success/poseidonsponge_x5_254/src/main.nr rename to test_programs/execution_success/poseidonsponge_x5_254/src/main.nr diff --git a/tooling/nargo_cli/tests/execution_success/pred_eq/Nargo.toml b/test_programs/execution_success/pred_eq/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/pred_eq/Nargo.toml rename to test_programs/execution_success/pred_eq/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/pred_eq/Prover.toml b/test_programs/execution_success/pred_eq/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/pred_eq/Prover.toml rename to test_programs/execution_success/pred_eq/Prover.toml diff --git a/tooling/nargo_cli/tests/execution_success/pred_eq/src/main.nr b/test_programs/execution_success/pred_eq/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/execution_success/pred_eq/src/main.nr rename to test_programs/execution_success/pred_eq/src/main.nr diff --git a/tooling/nargo_cli/tests/execution_success/references/Nargo.toml b/test_programs/execution_success/references/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/references/Nargo.toml rename to test_programs/execution_success/references/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/references/Prover.toml b/test_programs/execution_success/references/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/references/Prover.toml rename to test_programs/execution_success/references/Prover.toml diff --git a/test_programs/execution_success/references/src/main.nr b/test_programs/execution_success/references/src/main.nr new file mode 100644 index 00000000000..1a9be5f82b9 --- /dev/null +++ b/test_programs/execution_success/references/src/main.nr @@ -0,0 +1,226 @@ +fn main(mut x: Field) { + add1(&mut x); + assert(x == 3); + + let mut s = S { y: x }; + s.add2(); + assert(s.y == 5); + // Regression for #1946: Method resolution error when calling &mut methods with a variable of type &mut T + let s_ref = &mut s; + s_ref.add2(); + assert(s.y == 7); + // Test that normal mutable variables are still copied + let mut a = 0; + mutate_copy(a); + assert(a == 0); + // Test something 3 allocations deep + let mut nested_allocations = Nested { y: &mut &mut 0 }; + add1(*nested_allocations.y); + 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] } }; + *c.bar.array = [3, 4]; + assert(*c.bar.array == [3, 4]); + + regression_1887(); + regression_2054(); + regression_2030(); + regression_2255(); + + assert(x == 3); + regression_2218_if_inner_if(x, 10); + regression_2218_if_inner_else(20, x); + regression_2218_else(x, 3); + regression_2218_loop(x, 10); + + regression_2560(s_ref); +} + +fn add1(x: &mut Field) { + *x += 1; +} + +struct S { y: Field } + +struct Nested { y: &mut &mut Field } + +struct C { + foo: Field, + bar: &mut C2, +} + +struct C2 { + array: &mut [Field; 2] +} + +impl S { + fn add2(&mut self) { + self.y += 2; + } + + fn get_y(self) -> Field { + self.y + } +} + +fn mutate_copy(mut a: Field) { + a = 7; +} +// Previously the `foo.bar` in `foo.bar.mutate()` would insert an automatic dereference +// of `foo` which caused the method to wrongly be mutating a copy of bar rather than the original. +fn regression_1887() { + let foo = &mut Foo { bar: Bar { x: 0 } }; + foo.bar.mutate(); + assert(foo.bar.x == 32); +} + +struct Foo { bar: Bar } +struct Bar { x: Field } + +impl Bar { + fn mutate(&mut self) { + self.x = 32; + } +} +// Ensure that mutating a variable does not also mutate its copy +fn regression_2054() { + let mut x = 2; + let z = x; + + x += 1; + assert(z == 2); +} +// The compiler was still trying to convert an LValue from an array of structs to struct of arrays indexing, +// even though this conversion was mostly removed elsewhere. +fn regression_2030() { + let ref = &mut 0; + let mut array = [ref, ref]; + let _ = *array[0]; + *array[0] = 1; +} +// The `mut x: &mut ...` caught a bug handling lvalues where a double-dereference would occur internally +// in one step rather than being tracked by two separate steps. This lead to assigning the 1 value to the +// incorrect outer `mut` reference rather than the correct `&mut` reference. +fn regression_2255() { + let x = &mut 0; + regression_2255_helper(x); + assert(*x == 1); +} + +fn regression_2255_helper(mut x: &mut Field) { + *x = 1; +} + +fn regression_2218(x: Field, y: Field) -> Field { + let q = &mut &mut 0; + let q1 = *q; + let q2 = *q; + + if x != y { + *q1 = 1; + // Make sure that we correct load reference aliases through multiple blocks + if x != 20 { + *q1 = 10; + *q2 = 2; // now we'd expect q1 == q2 == 2 + assert(*q1 == 2); + } else { + *q2 = 15; + assert(*q1 == 15); + } + } else { + *q2 = 20; + assert(*q1 == 20); + } + // Have to assign value to return it + let value = *q1; + value +} + +fn regression_2218_if_inner_if(x: Field, y: Field) { + let value = regression_2218(x, y); + assert(value == 2); +} + +fn regression_2218_if_inner_else(x: Field, y: Field) { + let value = regression_2218(x, y); + assert(value == 15); +} + +fn regression_2218_else(x: Field, y: Field) { + let value = regression_2218(x, y); + assert(value == 20); +} + +fn regression_2218_loop(x: Field, y: Field) { + let q = &mut &mut 0; + let q1 = *q; + let q2 = *q; + + for _ in 0..1 { + if x != y { + *q1 = 10; + *q2 = 2; // now we'd expect q1 == q2 == 2 + assert(*q1 == 2); + } else { + *q2 = 20; + assert(*q1 == 20); + } + } + assert(*q1 == 2); + + for _ in 0..1 { + for _ in 0..5 { + if x != y { + *q1 = 1; + // Make sure that we correct load reference aliases through multiple blocks + if x != 20 { + *q1 = 10; + *q2 = 2; // now we'd expect q1 == q2 == 2 + assert(*q1 == 2); + } + } else { + *q2 = 20; + assert(*q1 == 20); + } + } + if x != y { + *q1 = 1; + for _ in 0..5 { + // Make sure that we correct load reference aliases through multiple blocks + if x != 20 { + *q1 = 10; + *q2 = 2; // now we'd expect q1 == q2 == 2 + assert(*q1 == 2); + } + } + } else { + *q2 = 20; + assert(*q1 == 20); + } + } + assert(*q1 == 2); + + if x != y { + for _ in 0..5 { + if x != y { + *q1 = 1; + // Make sure that we correct load reference aliases through multiple blocks + if x != 20 { + *q1 = 10; + *q2 = 2; // now we'd expect q1 == q2 == 2 + assert(*q1 == 2); + } + } + } + } else { + *q2 = 20; + assert(*q1 == 20); + } + assert(*q1 == 2); +} +// This is more a feature test than a proper regression. +// Before, we never automatically dereferenced objects in method calls to their value types. +// Now, we insert as many `*` as necessary to get to `S`. +fn regression_2560(s_ref: &mut S) { + assert(s_ref.get_y() == 7); +} diff --git a/tooling/nargo_cli/tests/execution_success/regression/Nargo.toml b/test_programs/execution_success/regression/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/regression/Nargo.toml rename to test_programs/execution_success/regression/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/regression/Prover.toml b/test_programs/execution_success/regression/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/regression/Prover.toml rename to test_programs/execution_success/regression/Prover.toml diff --git a/test_programs/execution_success/regression/src/main.nr b/test_programs/execution_success/regression/src/main.nr new file mode 100644 index 00000000000..08112d4c616 --- /dev/null +++ b/test_programs/execution_success/regression/src/main.nr @@ -0,0 +1,109 @@ +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(length as u64 <= input.len() as u64); + + let mut nibble = [0 as u4; NIBBLE_LENGTH]; + + let first_nibble = (input[0] >> 4) as u4; + let parity = first_nibble as u1; + + if parity == 1 { + nibble[0] = (input[0] & 0x0f) as u4; + for i in 1..input.len() { + if i as u64 < length as u64 { + let x = input[i]; + nibble[2*i - 1] = (x >> 4) as u4; + nibble[2*i] = (x & 0x0f) as u4; + } + } + } else { + for i in 0..2 { + if (i as u64) < length as u64 - 1 { + let x = input[i + 1]; + nibble[2*i] = (x >> 4) as u4; + nibble[2*i + 1] = (x & 0x0f) as u4; + } + } + } + + let out = (nibble, 2 * length + (parity as Field) - 2); + + out +} + +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 { + 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 + } +} + +fn bitshift_literal_0() -> u64 { + let mut bits: u64 = 0; + bits |= 1 << 0; + + bits +} +fn bitshift_literal_4() -> u64 { + let mut bits: u64 = 0; + bits |= 1 << 4; + + bits +} +fn bitshift_variable(idx: u64) -> u64 { + let mut bits: u64 = 0; + bits |= 1 << idx; + + bits +} + +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]); + // 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_length = 20; + + 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.1 == 21); + // Issue 2399 + let result_0 = bitshift_literal_0(); + assert(result_0 == 1); + let result_4 = bitshift_literal_4(); + assert(result_4 == 16); + let result_0 = bitshift_variable(0); + assert(result_0 == 1); + let result_4 = bitshift_variable(4); + assert(result_4 == 16); +} diff --git a/tooling/nargo_cli/tests/execution_success/regression_2854/Nargo.toml b/test_programs/execution_success/regression_2854/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/regression_2854/Nargo.toml rename to test_programs/execution_success/regression_2854/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/regression_2854/Prover.toml b/test_programs/execution_success/regression_2854/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/regression_2854/Prover.toml rename to test_programs/execution_success/regression_2854/Prover.toml diff --git a/test_programs/execution_success/regression_2854/src/main.nr b/test_programs/execution_success/regression_2854/src/main.nr new file mode 100644 index 00000000000..eccff8225b6 --- /dev/null +++ b/test_programs/execution_success/regression_2854/src/main.nr @@ -0,0 +1,3 @@ +fn main(x: Field) -> pub i127 { + x as i127 +} diff --git a/tooling/nargo_cli/tests/execution_success/regression_mem_op_predicate/Nargo.toml b/test_programs/execution_success/regression_mem_op_predicate/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/regression_mem_op_predicate/Nargo.toml rename to test_programs/execution_success/regression_mem_op_predicate/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/regression_mem_op_predicate/Prover.toml b/test_programs/execution_success/regression_mem_op_predicate/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/regression_mem_op_predicate/Prover.toml rename to test_programs/execution_success/regression_mem_op_predicate/Prover.toml diff --git a/tooling/nargo_cli/tests/execution_success/regression_mem_op_predicate/src/main.nr b/test_programs/execution_success/regression_mem_op_predicate/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/execution_success/regression_mem_op_predicate/src/main.nr rename to test_programs/execution_success/regression_mem_op_predicate/src/main.nr diff --git a/tooling/nargo_cli/tests/execution_success/regression_method_cannot_be_found/Nargo.toml b/test_programs/execution_success/regression_method_cannot_be_found/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/regression_method_cannot_be_found/Nargo.toml rename to test_programs/execution_success/regression_method_cannot_be_found/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/regression_method_cannot_be_found/Prover.toml b/test_programs/execution_success/regression_method_cannot_be_found/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/regression_method_cannot_be_found/Prover.toml rename to test_programs/execution_success/regression_method_cannot_be_found/Prover.toml diff --git a/test_programs/execution_success/regression_method_cannot_be_found/src/main.nr b/test_programs/execution_success/regression_method_cannot_be_found/src/main.nr new file mode 100644 index 00000000000..1a6931a6870 --- /dev/null +++ b/test_programs/execution_success/regression_method_cannot_be_found/src/main.nr @@ -0,0 +1,23 @@ +use dep::std; +struct Item { + id: Field, +} + +impl Item { + fn log(self) { + let id = self.id; + std::println(id); + } +} + +fn create(something: V) -> V { + something +} + +fn main() { + let a = Item { id: 1 }; + let b = create(a); + let _id = b.id; + // Regression for: cannot find this method + b.log(); +} diff --git a/tooling/nargo_cli/tests/execution_success/scalar_mul/Nargo.toml b/test_programs/execution_success/scalar_mul/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/scalar_mul/Nargo.toml rename to test_programs/execution_success/scalar_mul/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/scalar_mul/Prover.toml b/test_programs/execution_success/scalar_mul/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/scalar_mul/Prover.toml rename to test_programs/execution_success/scalar_mul/Prover.toml diff --git a/test_programs/execution_success/scalar_mul/src/main.nr b/test_programs/execution_success/scalar_mul/src/main.nr new file mode 100644 index 00000000000..2ddf22cf71e --- /dev/null +++ b/test_programs/execution_success/scalar_mul/src/main.nr @@ -0,0 +1,23 @@ +use dep::std; + +fn main( + a: Field, + a_pub_x: pub Field, + a_pub_y: pub Field, + b: Field, + b_pub_x: pub Field, + b_pub_y: pub Field +) { + let mut priv_key = a; + let mut pub_x: Field = a_pub_x; + let mut pub_y: Field = a_pub_y; + if a != 1 { + // Change `a` in Prover.toml to test input `b` + 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); +} diff --git a/tooling/nargo_cli/tests/execution_success/schnorr/Nargo.toml b/test_programs/execution_success/schnorr/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/schnorr/Nargo.toml rename to test_programs/execution_success/schnorr/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/schnorr/Prover.toml b/test_programs/execution_success/schnorr/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/schnorr/Prover.toml rename to test_programs/execution_success/schnorr/Prover.toml diff --git a/test_programs/execution_success/schnorr/src/main.nr b/test_programs/execution_success/schnorr/src/main.nr new file mode 100644 index 00000000000..107af152625 --- /dev/null +++ b/test_programs/execution_success/schnorr/src/main.nr @@ -0,0 +1,25 @@ +use dep::std; +// Note: If main has any unsized types, then the verifier will never be able +// to figure out the circuit instance +fn main( + message: [u8; 10], + message_field: Field, + pub_key_x: Field, + pub_key_y: Field, + signature: [u8; 64] +) { + // Regression for issue #2421 + // We want to make sure that we can accurately verify a signature whose message is a slice vs. an array + let message_field_bytes = message_field.to_be_bytes(10); + for i in 0..10 { + assert(message[i] == message_field_bytes[i]); + } + // 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); + 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); + assert(valid_signature); +} diff --git a/tooling/nargo_cli/tests/execution_success/sha256/Nargo.toml b/test_programs/execution_success/sha256/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/sha256/Nargo.toml rename to test_programs/execution_success/sha256/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/sha256/Prover.toml b/test_programs/execution_success/sha256/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/sha256/Prover.toml rename to test_programs/execution_success/sha256/Prover.toml diff --git a/tooling/nargo_cli/tests/execution_success/sha256/src/main.nr b/test_programs/execution_success/sha256/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/execution_success/sha256/src/main.nr rename to test_programs/execution_success/sha256/src/main.nr diff --git a/tooling/nargo_cli/tests/execution_success/sha2_byte/Nargo.toml b/test_programs/execution_success/sha2_byte/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/sha2_byte/Nargo.toml rename to test_programs/execution_success/sha2_byte/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/sha2_byte/Prover.toml b/test_programs/execution_success/sha2_byte/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/sha2_byte/Prover.toml rename to test_programs/execution_success/sha2_byte/Prover.toml diff --git a/tooling/nargo_cli/tests/execution_success/sha2_byte/src/main.nr b/test_programs/execution_success/sha2_byte/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/execution_success/sha2_byte/src/main.nr rename to test_programs/execution_success/sha2_byte/src/main.nr diff --git a/tooling/nargo_cli/tests/execution_success/signed_arithmetic/Nargo.toml b/test_programs/execution_success/signed_arithmetic/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/signed_arithmetic/Nargo.toml rename to test_programs/execution_success/signed_arithmetic/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/signed_arithmetic/Prover.toml b/test_programs/execution_success/signed_arithmetic/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/signed_arithmetic/Prover.toml rename to test_programs/execution_success/signed_arithmetic/Prover.toml diff --git a/test_programs/execution_success/signed_arithmetic/src/main.nr b/test_programs/execution_success/signed_arithmetic/src/main.nr new file mode 100644 index 00000000000..7a997d177ba --- /dev/null +++ b/test_programs/execution_success/signed_arithmetic/src/main.nr @@ -0,0 +1,32 @@ +fn main(mut x: i32, mut y: i32, z: i32) { + let mut s1: i8 = 5; + let mut s2: i8 = 8; + assert(s1 + s2 == 13); + assert(x + y == 13); + + s2= -8; + y = -y; + assert(s1 + s2 == -3); + assert(x + y == -3); + + s1 = -15; + assert(s1 - s2 == -7); + assert(z - y == -7); + + s1 = -5; + s2 = 8; + x = -x; + y = -y; + assert(s1 - s2 == -13); + assert(x - y == -13); + + s2 = -8; + y = -y; + assert(s1 * s2 == 40); + assert(x * y == 40); + + s1 = 1; + s2 = -8; + assert(s1 * s2 == -8); + assert(x / x * y == -8); +} diff --git a/tooling/nargo_cli/tests/execution_success/signed_division/Nargo.toml b/test_programs/execution_success/signed_division/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/signed_division/Nargo.toml rename to test_programs/execution_success/signed_division/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/signed_division/Prover.toml b/test_programs/execution_success/signed_division/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/signed_division/Prover.toml rename to test_programs/execution_success/signed_division/Prover.toml diff --git a/test_programs/execution_success/signed_division/src/main.nr b/test_programs/execution_success/signed_division/src/main.nr new file mode 100644 index 00000000000..6289a2f9ed9 --- /dev/null +++ b/test_programs/execution_success/signed_division/src/main.nr @@ -0,0 +1,21 @@ +use dep::std; +// Testing signed integer division: +// 7/3 = 2 +// -7/3 = -2 +// -7/-3 = 2 +// 7/-3 = -2 +fn main(mut x: i32, mut y: i32, mut z: i32) { + // 7/3 = 2 + assert(x / y == z); + // -7/3 = -2 + let minus_x = std::wrapping_sub(0, x); + let minus_z = std::wrapping_sub(0, z); + let minus_y = std::wrapping_sub(0, y); + assert(x + minus_x == 0); + assert(z + minus_z == 0); + assert(minus_x / y == minus_z); + // -7/-3 = 2 + assert(minus_x / minus_y == z); + // 7/-3 = -2 + assert(x / minus_y == minus_z); +} diff --git a/tooling/nargo_cli/tests/execution_success/simple_2d_array/Nargo.toml b/test_programs/execution_success/simple_2d_array/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/simple_2d_array/Nargo.toml rename to test_programs/execution_success/simple_2d_array/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/simple_2d_array/Prover.toml b/test_programs/execution_success/simple_2d_array/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/simple_2d_array/Prover.toml rename to test_programs/execution_success/simple_2d_array/Prover.toml diff --git a/tooling/nargo_cli/tests/execution_success/simple_2d_array/src/main.nr b/test_programs/execution_success/simple_2d_array/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/execution_success/simple_2d_array/src/main.nr rename to test_programs/execution_success/simple_2d_array/src/main.nr diff --git a/tooling/nargo_cli/tests/execution_success/simple_add_and_ret_arr/Nargo.toml b/test_programs/execution_success/simple_add_and_ret_arr/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/simple_add_and_ret_arr/Nargo.toml rename to test_programs/execution_success/simple_add_and_ret_arr/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/simple_add_and_ret_arr/Prover.toml b/test_programs/execution_success/simple_add_and_ret_arr/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/simple_add_and_ret_arr/Prover.toml rename to test_programs/execution_success/simple_add_and_ret_arr/Prover.toml diff --git a/tooling/nargo_cli/tests/execution_success/simple_add_and_ret_arr/src/main.nr b/test_programs/execution_success/simple_add_and_ret_arr/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/execution_success/simple_add_and_ret_arr/src/main.nr rename to test_programs/execution_success/simple_add_and_ret_arr/src/main.nr diff --git a/tooling/nargo_cli/tests/execution_success/simple_bitwise/Nargo.toml b/test_programs/execution_success/simple_bitwise/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/simple_bitwise/Nargo.toml rename to test_programs/execution_success/simple_bitwise/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/simple_bitwise/Prover.toml b/test_programs/execution_success/simple_bitwise/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/simple_bitwise/Prover.toml rename to test_programs/execution_success/simple_bitwise/Prover.toml diff --git a/tooling/nargo_cli/tests/execution_success/simple_bitwise/src/main.nr b/test_programs/execution_success/simple_bitwise/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/execution_success/simple_bitwise/src/main.nr rename to test_programs/execution_success/simple_bitwise/src/main.nr diff --git a/tooling/nargo_cli/tests/execution_success/simple_comparison/Nargo.toml b/test_programs/execution_success/simple_comparison/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/simple_comparison/Nargo.toml rename to test_programs/execution_success/simple_comparison/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/simple_comparison/Prover.toml b/test_programs/execution_success/simple_comparison/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/simple_comparison/Prover.toml rename to test_programs/execution_success/simple_comparison/Prover.toml diff --git a/test_programs/execution_success/simple_comparison/src/main.nr b/test_programs/execution_success/simple_comparison/src/main.nr new file mode 100644 index 00000000000..05800440459 --- /dev/null +++ b/test_programs/execution_success/simple_comparison/src/main.nr @@ -0,0 +1,6 @@ +// Tests a very simple program. +// +// The features being tested is comparison +fn main(x: Field, y: Field) { + assert(x as u32 < y as u32); +} diff --git a/tooling/nargo_cli/tests/execution_success/simple_mut/Nargo.toml b/test_programs/execution_success/simple_mut/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/simple_mut/Nargo.toml rename to test_programs/execution_success/simple_mut/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/simple_mut/Prover.toml b/test_programs/execution_success/simple_mut/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/simple_mut/Prover.toml rename to test_programs/execution_success/simple_mut/Prover.toml diff --git a/test_programs/execution_success/simple_mut/src/main.nr b/test_programs/execution_success/simple_mut/src/main.nr new file mode 100644 index 00000000000..d0715dbafe0 --- /dev/null +++ b/test_programs/execution_success/simple_mut/src/main.nr @@ -0,0 +1,6 @@ +// A simple program to test mutable variables +fn main(x: Field) -> pub Field { + let mut y = 2; + y += x; + y +} diff --git a/tooling/nargo_cli/tests/execution_success/simple_not/Nargo.toml b/test_programs/execution_success/simple_not/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/simple_not/Nargo.toml rename to test_programs/execution_success/simple_not/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/simple_not/Prover.toml b/test_programs/execution_success/simple_not/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/simple_not/Prover.toml rename to test_programs/execution_success/simple_not/Prover.toml diff --git a/test_programs/execution_success/simple_not/src/main.nr b/test_programs/execution_success/simple_not/src/main.nr new file mode 100644 index 00000000000..ba172625fe4 --- /dev/null +++ b/test_programs/execution_success/simple_not/src/main.nr @@ -0,0 +1,4 @@ +// A simple program for testing the NOT op +fn main(x: bool) -> pub bool { + !x +} diff --git a/tooling/nargo_cli/tests/execution_success/simple_print/Nargo.toml b/test_programs/execution_success/simple_print/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/simple_print/Nargo.toml rename to test_programs/execution_success/simple_print/Nargo.toml diff --git a/tooling/nargo_cli/tests/compile_failure/orphaned_trait_impl/Prover.toml b/test_programs/execution_success/simple_print/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/compile_failure/orphaned_trait_impl/Prover.toml rename to test_programs/execution_success/simple_print/Prover.toml diff --git a/test_programs/execution_success/simple_print/src/main.nr b/test_programs/execution_success/simple_print/src/main.nr new file mode 100644 index 00000000000..6038b995af0 --- /dev/null +++ b/test_programs/execution_success/simple_print/src/main.nr @@ -0,0 +1,8 @@ +// Simple program for testing the logging +// of single witnesses and witness arrays. +use dep::std; + +fn main(x: Field, y: pub Field) { + std::println(x); + std::println([x, y]); +} diff --git a/tooling/nargo_cli/tests/execution_success/simple_program_addition/Nargo.toml b/test_programs/execution_success/simple_program_addition/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/simple_program_addition/Nargo.toml rename to test_programs/execution_success/simple_program_addition/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/simple_program_addition/Prover.toml b/test_programs/execution_success/simple_program_addition/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/simple_program_addition/Prover.toml rename to test_programs/execution_success/simple_program_addition/Prover.toml diff --git a/test_programs/execution_success/simple_program_addition/src/main.nr b/test_programs/execution_success/simple_program_addition/src/main.nr new file mode 100644 index 00000000000..0390d79e83b --- /dev/null +++ b/test_programs/execution_success/simple_program_addition/src/main.nr @@ -0,0 +1,5 @@ +// The feature being tested is handling of +// a binary operation. +fn main(x: Field) -> pub Field { + x + 1 +} diff --git a/tooling/nargo_cli/tests/execution_success/simple_radix/Nargo.toml b/test_programs/execution_success/simple_radix/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/simple_radix/Nargo.toml rename to test_programs/execution_success/simple_radix/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/simple_radix/Prover.toml b/test_programs/execution_success/simple_radix/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/simple_radix/Prover.toml rename to test_programs/execution_success/simple_radix/Prover.toml diff --git a/test_programs/execution_success/simple_radix/src/main.nr b/test_programs/execution_success/simple_radix/src/main.nr new file mode 100644 index 00000000000..4a335e1bade --- /dev/null +++ b/test_programs/execution_success/simple_radix/src/main.nr @@ -0,0 +1,7 @@ +// Simple program to test to_radix +fn main(x: Field) { + let bits = x.to_le_bits(3); + assert(bits[0] == 0); + assert(bits[1] == 1); + assert(bits[2] == 0); +} diff --git a/tooling/nargo_cli/tests/execution_success/simple_shield/Nargo.toml b/test_programs/execution_success/simple_shield/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/simple_shield/Nargo.toml rename to test_programs/execution_success/simple_shield/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/simple_shield/Prover.toml b/test_programs/execution_success/simple_shield/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/simple_shield/Prover.toml rename to test_programs/execution_success/simple_shield/Prover.toml diff --git a/test_programs/execution_success/simple_shield/src/main.nr b/test_programs/execution_success/simple_shield/src/main.nr new file mode 100644 index 00000000000..c46d3b4594c --- /dev/null +++ b/test_programs/execution_success/simple_shield/src/main.nr @@ -0,0 +1,29 @@ +use dep::std; + +fn main( + // Public key of note + // all notes have the same denomination + priv_key: Field, + // Merkle membership proof + note_root: pub Field, + index: Field, + note_hash_path: [Field; 3], + // Receiver public key + to_pubkey_x: Field, + to_pubkey_y: Field +) -> pub [Field; 2] { + // Compute public key from private key to show ownership + let pubkey = std::scalar_mul::fixed_base_embedded_curve(priv_key, 0); + let pubkey_x = pubkey[0]; + let pubkey_y = pubkey[1]; + // Compute input note commitment + let note_commitment = std::hash::pedersen_commitment([pubkey_x, pubkey_y]); + // Compute input note nullifier + let nullifier = std::hash::pedersen_commitment([note_commitment.x, index, priv_key]); + // Compute output note nullifier + let receiver_note_commitment = std::hash::pedersen_commitment([to_pubkey_x, to_pubkey_y]); + // Check that the input note nullifier is in the root + assert(note_root == std::merkle::compute_merkle_root(note_commitment.x, index, note_hash_path)); + + [nullifier.x, receiver_note_commitment.x] +} diff --git a/tooling/nargo_cli/tests/execution_success/simple_shift_left_right/Nargo.toml b/test_programs/execution_success/simple_shift_left_right/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/simple_shift_left_right/Nargo.toml rename to test_programs/execution_success/simple_shift_left_right/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/simple_shift_left_right/Prover.toml b/test_programs/execution_success/simple_shift_left_right/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/simple_shift_left_right/Prover.toml rename to test_programs/execution_success/simple_shift_left_right/Prover.toml diff --git a/test_programs/execution_success/simple_shift_left_right/src/main.nr b/test_programs/execution_success/simple_shift_left_right/src/main.nr new file mode 100644 index 00000000000..40698af6ce7 --- /dev/null +++ b/test_programs/execution_success/simple_shift_left_right/src/main.nr @@ -0,0 +1,8 @@ +// Tests a very simple program. +// +// The features being tested are left and right shifts. +fn main(x: u32) { + let z = x >> 4; + let t = x << 4; + assert(z == t >> 8); +} diff --git a/tooling/nargo_cli/tests/execution_success/slice_dynamic_index/Nargo.toml b/test_programs/execution_success/slice_dynamic_index/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/slice_dynamic_index/Nargo.toml rename to test_programs/execution_success/slice_dynamic_index/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/slice_dynamic_index/Prover.toml b/test_programs/execution_success/slice_dynamic_index/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/slice_dynamic_index/Prover.toml rename to test_programs/execution_success/slice_dynamic_index/Prover.toml diff --git a/test_programs/execution_success/slice_dynamic_index/src/main.nr b/test_programs/execution_success/slice_dynamic_index/src/main.nr new file mode 100644 index 00000000000..374d2ba4c26 --- /dev/null +++ b/test_programs/execution_success/slice_dynamic_index/src/main.nr @@ -0,0 +1,308 @@ +fn main(x: Field) { + // The parameters to this function must come directly from witness values (inputs to main). + regression_dynamic_slice_index(x - 1, x - 4); +} + +fn regression_dynamic_slice_index(x: Field, y: Field) { + let mut slice = []; + for i in 0..5 { + slice = slice.push_back(i); + } + assert(slice.len() == 5); + + dynamic_slice_index_set_if(slice, x, y); + dynamic_slice_index_set_else(slice, x, y); + dynamic_slice_index_set_nested_if_else_else(slice, x, y); + dynamic_slice_index_set_nested_if_else_if(slice, x, y + 1); + dynamic_slice_index_if(slice, x); + dynamic_array_index_if([0, 1, 2, 3, 4], x); + dynamic_slice_index_else(slice, x); + + dynamic_slice_merge_if(slice, x); + dynamic_slice_merge_else(slice, x); + dynamic_slice_merge_two_ifs(slice, x); + dynamic_slice_merge_mutate_between_ifs(slice, x, y); + dynamic_slice_merge_push_then_pop(slice, x, y); +} + +fn dynamic_slice_index_set_if(mut slice: [Field], x: Field, y: Field) { + assert(slice[x] == 4); + assert(slice[y] == 1); + slice[y] = 0; + assert(slice[x] == 4); + assert(slice[1] == 0); + if x as u32 < 10 { + assert(slice[x] == 4); + slice[x] = slice[x] - 2; + slice[x - 1] = slice[x]; + } else { + slice[x] = 0; + } + assert(slice[3] == 2); + assert(slice[4] == 2); +} + +fn dynamic_slice_index_set_else(mut slice: [Field], x: Field, y: Field) { + assert(slice[x] == 4); + assert(slice[y] == 1); + slice[y] = 0; + assert(slice[x] == 4); + assert(slice[1] == 0); + if x as u32 > 10 { + assert(slice[x] == 4); + slice[x] = slice[x] - 2; + slice[x - 1] = slice[x]; + } else { + slice[x] = 0; + } + assert(slice[4] == 0); +} +// This tests the case of missing a store instruction in the else branch +// of merging slices +fn dynamic_slice_index_if(mut slice: [Field], x: Field) { + if x as u32 < 10 { + assert(slice[x] == 4); + slice[x] = slice[x] - 2; + } else { + assert(slice[x] == 0); + } + assert(slice[4] == 2); +} + +fn dynamic_array_index_if(mut array: [Field; 5], x: Field) { + if x as u32 < 10 { + assert(array[x] == 4); + array[x] = array[x] - 2; + } else { + assert(array[x] == 0); + } + assert(array[4] == 2); +} +// This tests the case of missing a store instruction in the then branch +// of merging slices +fn dynamic_slice_index_else(mut slice: [Field], x: Field) { + if x as u32 > 10 { + assert(slice[x] == 0); + } else { + assert(slice[x] == 4); + slice[x] = slice[x] - 2; + } + assert(slice[4] == 2); +} + +fn dynamic_slice_merge_if(mut slice: [Field], x: Field) { + if x as u32 < 10 { + assert(slice[x] == 4); + slice[x] = slice[x] - 2; + + slice = slice.push_back(10); + // Having an array set here checks whether we appropriately + // handle a slice length that is not yet resolving to a constant + // during flattening + slice[x] = 10; + assert(slice[slice.len() - 1] == 10); + assert(slice.len() == 6); + + slice[x] = 20; + slice[x] = slice[x] + 10; + + slice = slice.push_front(11); + assert(slice[0] == 11); + assert(slice.len() == 7); + assert(slice[5] == 30); + + slice = slice.push_front(12); + assert(slice[0] == 12); + assert(slice.len() == 8); + assert(slice[6] == 30); + + let (popped_slice, last_elem) = slice.pop_back(); + assert(last_elem == 10); + assert(popped_slice.len() == 7); + + let (first_elem, rest_of_slice) = popped_slice.pop_front(); + assert(first_elem == 12); + assert(rest_of_slice.len() == 6); + + slice = rest_of_slice.insert(x - 2, 20); + assert(slice[2] == 20); + assert(slice[6] == 30); + assert(slice.len() == 7); + + let (removed_slice, removed_elem) = slice.remove(x - 1); + // The deconstructed tuple assigns to the slice but is not seen outside of the if statement + // without a direct assignment + slice = removed_slice; + + assert(removed_elem == 1); + assert(slice.len() == 6); + } else { + assert(slice[x] == 0); + slice = slice.push_back(20); + } + + assert(slice.len() == 6); + assert(slice[slice.len() - 1] == 30); +} + +fn dynamic_slice_merge_else(mut slice: [Field], x: Field) { + if x as u32 > 10 { + assert(slice[x] == 0); + slice[x] = 2; + } else { + assert(slice[x] == 4); + slice[x] = slice[x] - 2; + slice = slice.push_back(10); + } + assert(slice.len() == 6); + assert(slice[slice.len() - 1] == 10); + + slice = slice.push_back(20); + assert(slice.len() == 7); + assert(slice[slice.len() - 1] == 20); +} + +fn dynamic_slice_index_set_nested_if_else_else(mut slice: [Field], x: Field, y: Field) { + assert(slice[x] == 4); + assert(slice[y] == 1); + slice[y] = 0; + assert(slice[x] == 4); + assert(slice[1] == 0); + if x as u32 < 10 { + slice[x] = slice[x] - 2; + if y != 1 { + slice[x] = slice[x] + 20; + } else { + if x == 5 { + // We should not hit this case + assert(slice[x] == 22); + } else { + slice[x] = 10; + slice = slice.push_back(15); + assert(slice.len() == 6); + } + assert(slice[4] == 10); + } + } else { + slice[x] = 0; + } + assert(slice[4] == 10); + assert(slice.len() == 6); + assert(slice[slice.len() - 1] == 15); + + slice = slice.push_back(20); + assert(slice.len() == 7); + assert(slice[slice.len() - 1] == 20); +} + +fn dynamic_slice_index_set_nested_if_else_if(mut slice: [Field], x: Field, y: Field) { + assert(slice[x] == 4); + assert(slice[y] == 2); + slice[y] = 0; + assert(slice[x] == 4); + assert(slice[2] == 0); + if x as u32 < 10 { + slice[x] = slice[x] - 2; + // TODO: this panics as we have a load for the slice in flattening + if y == 1 { + slice[x] = slice[x] + 20; + } else { + if x == 4 { + slice[x] = 5; + } + assert(slice[4] == 5); + } + } else { + slice[x] = 0; + } + assert(slice[4] == 5); +} + +fn dynamic_slice_merge_two_ifs(mut slice: [Field], x: Field) { + if x as u32 > 10 { + assert(slice[x] == 0); + slice[x] = 2; + } else { + assert(slice[x] == 4); + slice[x] = slice[x] - 2; + slice = slice.push_back(10); + } + + assert(slice.len() == 6); + assert(slice[slice.len() - 1] == 10); + + if x == 20 { + slice = slice.push_back(20); + } + + slice = slice.push_back(15); + + assert(slice.len() == 7); + assert(slice[slice.len() - 1] == 15); + + slice = slice.push_back(20); + assert(slice.len() == 8); + assert(slice[slice.len() - 1] == 20); +} + +fn dynamic_slice_merge_mutate_between_ifs(mut slice: [Field], x: Field, y: Field) { + if x != y { + slice[x] = 50; + slice = slice.push_back(y); + slice = slice.push_back(x); + } else { + slice[x] = slice[x] - 2; + slice = slice.push_back(x); + } + + slice = slice.push_back(30); + assert(slice.len() == 8); + + if x == 20 { + slice = slice.push_back(20); + } + + slice = slice.push_back(15); + + if x != 20 { + slice = slice.push_back(50); + } + + slice = slice.push_back(60); + assert(slice.len() == 11); + assert(slice[x] == 50); + assert(slice[slice.len() - 4] == 30); + assert(slice[slice.len() - 3] == 15); + assert(slice[slice.len() - 2] == 50); + assert(slice[slice.len() - 1] == 60); +} + +fn dynamic_slice_merge_push_then_pop(mut slice: [Field], x: Field, y: Field) { + if x != y { + slice[x] = 5; + slice = slice.push_back(y); + slice = slice.push_back(x); + assert(slice.len() == 7); + + let (popped_slice, elem) = slice.pop_back(); + assert(slice.len() == 7); + assert(elem == x); + slice = popped_slice; + } else { + slice = slice.push_back(x); + } + + slice = slice.push_back(30); + assert(slice.len() == 7); + + if x == 20 { + slice = slice.push_back(20); + } + + let (slice, elem) = slice.pop_back(); + assert(elem == 30); + + let (_, elem) = slice.pop_back(); + assert(elem == y); +} + diff --git a/tooling/nargo_cli/tests/execution_success/slice_struct_field/Nargo.toml b/test_programs/execution_success/slice_struct_field/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/slice_struct_field/Nargo.toml rename to test_programs/execution_success/slice_struct_field/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/slice_struct_field/Prover.toml b/test_programs/execution_success/slice_struct_field/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/slice_struct_field/Prover.toml rename to test_programs/execution_success/slice_struct_field/Prover.toml diff --git a/test_programs/execution_success/slice_struct_field/src/main.nr b/test_programs/execution_success/slice_struct_field/src/main.nr new file mode 100644 index 00000000000..a5b971ada4b --- /dev/null +++ b/test_programs/execution_success/slice_struct_field/src/main.nr @@ -0,0 +1,472 @@ +struct FooParent { + parent_arr: [Field; 3], + foos: [Foo], +} + +struct Bar { + inner: [Field; 3], +} + +struct Foo { + a: Field, + b: [Field], + bar: Bar, +} + +fn main(y: pub Field) { + let mut b_one = [2, 3, 20]; + b_one = b_one.push_back(20); + let foo_one = Foo { a: 1, b: b_one, bar: Bar { inner: [100, 101, 102] } }; + + let mut b_two = [5, 6, 21]; + b_two = b_two.push_back(21); + let foo_two = Foo { a: 4, b: b_two, bar: Bar { inner: [103, 104, 105] } }; + + let foo_three = Foo { a: 7, b: [8, 9, 22], bar: Bar { inner: [106, 107, 108] } }; + let mut foo_four = Foo { a: 10, b: [11, 12, 23], bar: Bar { inner: [109, 110, 111] } }; + + let mut x = [foo_one, foo_two]; + x = x.push_back(foo_three); + x = x.push_back(foo_four); + + assert(x[y - 3].a == 1); + let struct_slice = x[y - 3].b; + for i in 0..4 { + assert(struct_slice[i] == b_one[i]); + } + + assert(x[y - 2].a == 4); + let struct_slice = x[y - 2].b; + for i in 0..4 { + assert(struct_slice[i] == b_two[i]); + } + + assert(x[y - 1].a == 7); + let struct_slice = x[y - 1].b; + assert(struct_slice[0] == 8); + assert(struct_slice[1] == 9); + assert(struct_slice[2] == 22); + + assert(x[y].a == 10); + let struct_slice = x[y].b; + assert(struct_slice[0] == 11); + assert(struct_slice[1] == 12); + assert(struct_slice[2] == 23); + assert(x[y].bar.inner == [109, 110, 111]); + + 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]); + assert(x[y].bar.inner == [109, 110, 111]); + // Check that switching the lhs and rhs is still valid + assert([109, 110, 111] == x[y].bar.inner); + + 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]); + assert(x[y].bar.inner == [109, 110, 111]); + // Check that switching the lhs and rhs is still valid + assert([109, 110, 111] == x[y].bar.inner); + + // TODO: Enable merging nested slices + // if y != 2 { + // x[y].a = 50; + // } else { + // x[y].a = 100; + // } + // assert(x[3].a == 50); + // if y == 2 { + // x[y - 1].b = [50, 51, 52]; + // } else { + // x[y - 1].b = [100, 101, 102]; + // } + // assert(x[2].b[0] == 100); + // assert(x[2].b[1] == 101); + // assert(x[2].b[2] == 102); + + let q = x.push_back(foo_four); + let foo_parent_one = FooParent { parent_arr: [0, 1, 2], foos: x }; + let foo_parent_two = FooParent { parent_arr: [3, 4, 5], foos: q }; + let mut foo_parents = [foo_parent_one]; + foo_parents = foo_parents.push_back(foo_parent_two); + // TODO: make a separate test for entirely compile time + // foo_parents[1].foos.push_back(foo_four); + // TODO: Merging nested slices is broken + // if y == 3 { + // foo_parents[y - 2].foos[y - 1].b[y - 1] = 5000; + // } else { + // foo_parents[y - 2].foos[y - 1].b[y - 1] = 1000; + // } + + assert(foo_parents[y - 2].foos[y - 2].b[y - 1] == 21); + foo_parents[y - 2].foos[y - 2].b[y - 1] = 5000; + assert(foo_parents[y - 2].foos[y - 2].b[y - 1] == 5000); + + let b_array = foo_parents[y - 2].foos[y - 3].b; + assert(foo_parents[y - 2].foos[y - 3].a == 1); + assert(b_array[0] == 2); + assert(b_array[1] == 3); + assert(b_array[2] == 20); + assert(b_array[3] == 20); + + let b_array = foo_parents[y - 2].foos[y - 2].b; + assert(foo_parents[y - 2].foos[y - 2].a == 4); + assert(b_array[0] == 5); + assert(b_array[1] == 6); + assert(b_array[2] == 5000); + assert(b_array[3] == 21); + + assert(foo_parents[y - 2].foos[y - 1].a == 7); + foo_parents[y - 2].foos[y - 1].a = 50; + assert(foo_parents[y - 2].foos[y - 1].a == 50); + + let b_array = foo_parents[y - 2].foos[y - 1].b; + assert(b_array[0] == 8); + assert(b_array[1] == 9); + assert(b_array[2] == 22); + assert(b_array.len() == 3); + + // // Test setting a nested array with non-dynamic + let x = [5, 6, 5000, 21, 100, 101].as_slice(); + foo_parents[y - 2].foos[y - 1].b = x; + + assert(foo_parents[y - 2].foos[y - 1].b.len() == 6); + assert(foo_parents[y - 2].foos[y - 1].b[4] == 100); + assert(foo_parents[y - 2].foos[y - 1].b[5] == 101); + + // Need to account for that foo_parents is not modified outside of this function + test_basic_intrinsics_nested_slices(foo_parents, y); + test_complex_intrinsic_nested_slices(foo_parents, y); + + foo_parents[y - 2].foos[y - 1].b = foo_parents[y - 2].foos[y - 1].b.push_back(500); + assert(foo_parents[y - 2].foos[y - 1].b.len() == 7); + assert(foo_parents[y - 2].foos[y - 1].b[6] == 500); + + let (popped_slice, last_elem) = foo_parents[y - 2].foos[y - 1].b.pop_back(); + foo_parents[y - 2].foos[y - 1].b = popped_slice; + assert(foo_parents[y - 2].foos[y - 1].b.len() == 6); + assert(last_elem == 500); + + foo_parents[y - 2].foos[y - 1].b = foo_parents[y - 2].foos[y - 1].b.push_front(11); + assert(foo_parents[y - 2].foos[y - 1].b.len() == 7); + assert(foo_parents[y - 2].foos[y - 1].b[0] == 11); + + assert(foo_parents[y - 2].foos.len() == 5); + foo_four.a = 40; + foo_parents[y - 2].foos = foo_parents[y - 2].foos.push_back(foo_four); + assert(foo_parents[y - 2].foos.len() == 6); + assert(foo_parents[y - 2].foos[y + 2].bar.inner == [109, 110, 111]); + + foo_parents[y - 2].foos = foo_parents[y - 2].foos.push_back(foo_four); + assert(foo_parents[y - 2].foos.len() == 7); + assert(foo_parents[y - 2].foos[6].a == 40); + assert(foo_parents[y - 2].foos[5].bar.inner == [109, 110, 111]); + assert(foo_parents[y - 2].foos[6].bar.inner == [109, 110, 111]); + + foo_parents[y - 2].foos = foo_parents[y - 2].foos.push_back(foo_four); + assert(foo_parents[y - 2].foos.len() == 8); + assert(foo_parents[y - 2].foos[6].a == 40); + assert(foo_parents[y - 2].foos[5].bar.inner == [109, 110, 111]); + assert(foo_parents[y - 2].foos[6].bar.inner == [109, 110, 111]); + + foo_parents[y - 2].foos = foo_parents[y - 2].foos.push_back(foo_four); + assert(foo_parents[y - 2].foos.len() == 9); + + foo_parents[y - 2].foos = foo_parents[y - 2].foos.push_back(foo_four); + assert(foo_parents[y - 2].foos.len() == 10); + + let b_array = foo_parents[y - 2].foos[y - 1].b; + assert(b_array[0] == 11); + assert(b_array[1] == 5); + assert(b_array[2] == 6); + assert(b_array[3] == 5000); + + let b_array = foo_parents[y - 2].foos[y].b; + assert(foo_parents[y - 2].foos[y].a == 10); + assert(b_array[0] == 11); + assert(b_array[1] == 12); + assert(b_array[2] == 23); + + assert(foo_parents[y - 2].foos[y - 3].bar.inner == [100, 101, 102]); + assert(foo_parents[y - 2].foos[y - 2].bar.inner == [103, 104, 105]); + assert(foo_parents[y - 2].foos[y - 1].bar.inner == [106, 107, 108]); + assert(foo_parents[y - 2].foos[y].bar.inner == [109, 110, 111]); +} + +fn test_basic_intrinsics_nested_slices(mut foo_parents: [FooParent], y: Field) { + foo_parents[y - 2].foos[y - 1].b = foo_parents[y - 2].foos[y - 1].b.push_back(500); + assert(foo_parents[y - 2].foos[y - 1].b.len() == 7); + assert(foo_parents[y - 2].foos[y - 1].b[6] == 500); + + let (popped_slice, last_elem) = foo_parents[y - 2].foos[y - 1].b.pop_back(); + foo_parents[y - 2].foos[y - 1].b = popped_slice; + assert(foo_parents[y - 2].foos[y - 1].b.len() == 6); + assert(last_elem == 500); + + foo_parents[y - 2].foos[y - 1].b = foo_parents[y - 2].foos[y - 1].b.push_front(11); + assert(foo_parents[y - 2].foos[y - 1].b.len() == 7); + assert(foo_parents[y - 2].foos[y - 1].b[0] == 11); + + let (first_elem, rest_of_slice) = foo_parents[y - 2].foos[y - 1].b.pop_front(); + foo_parents[y - 2].foos[y - 1].b = rest_of_slice; + assert(foo_parents[y - 2].foos[y - 1].b.len() == 6); + assert(first_elem == 11); + + foo_parents[y - 2].foos[y - 1].b = foo_parents[y - 2].foos[y - 1].b.insert(2, 20); + assert(foo_parents[y - 2].foos[y - 1].b.len() == 7); + assert(foo_parents[y - 2].foos[y - 1].b[y - 1] == 20); + assert(foo_parents[y - 2].foos[y - 1].b[y] == 5000); + assert(foo_parents[y - 2].foos[y - 1].b[6] == 101); + + let (rest_of_slice, removed_elem) = foo_parents[y - 2].foos[y - 1].b.remove(3); + foo_parents[y - 2].foos[y - 1].b = rest_of_slice; + assert(removed_elem == 5000); + assert(foo_parents[y - 2].foos[y - 1].b.len() == 6); + assert(foo_parents[y - 2].foos[y - 1].b[2] == 20); + assert(foo_parents[y - 2].foos[y - 1].b[3] == 21); +} + +// This method test intrinsics on nested slices with complex inputs such as +// pushing a `Foo` struct onto a slice in `FooParents`. +fn test_complex_intrinsic_nested_slices(mut foo_parents: [FooParent], y: Field) { + let mut foo = Foo { a: 13, b: [14, 15, 16], bar: Bar { inner: [109, 110, 111] } }; + assert(foo_parents[y - 2].foos.len() == 5); + foo.a = 40; + foo_parents[y - 2].foos = foo_parents[y - 2].foos.push_back(foo); + assert(foo_parents[1].foos.len() == 6); + assert(foo_parents[1].foos[5].a == 40); + assert(foo_parents[1].foos[5].b[0] == 14); + assert(foo_parents[1].foos[5].b[2] == 16); + assert(foo_parents[1].foos[5].b.len() == 3); + assert(foo_parents[1].foos[5].bar.inner == [109, 110, 111]); + + foo_parents[y - 2].foos[y - 1].b = foo_parents[y - 2].foos[y - 1].b.push_back(500); + assert(foo_parents[1].foos[2].b.len() == 7); + assert(foo_parents[1].foos[2].b[6] == 500); + assert(foo_parents[1].foos[2].bar.inner == [106, 107, 108]); + assert(foo_parents[1].foos[5].a == 40); + assert(foo_parents[1].foos[5].b[0] == 14); + assert(foo_parents[1].foos[5].b[2] == 16); + assert(foo_parents[1].foos[5].b.len() == 3); + assert(foo_parents[1].foos[5].bar.inner == [109, 110, 111]); + + let (popped_slice, last_foo) = foo_parents[y - 2].foos.pop_back(); + foo_parents[y - 2].foos = popped_slice; + assert(foo_parents[y - 2].foos.len() == 5); + assert(last_foo.a == 40); + assert(last_foo.b[0] == 14); + assert(last_foo.b[1] == 15); + assert(last_foo.b[2] == 16); + assert(last_foo.bar.inner == [109, 110, 111]); + + foo_parents[y - 2].foos = foo_parents[y - 2].foos.push_front(foo); + assert(foo_parents[1].foos.len() == 6); + assert(foo_parents[1].foos[0].a == 40); + assert(foo_parents[1].foos[0].b[0] == 14); + assert(foo_parents[1].foos[0].b[1] == 15); + assert(foo_parents[1].foos[0].b[2] == 16); + assert(foo_parents[1].foos[5].a == 10); + assert(foo_parents[1].foos[5].b.len() == 3); + assert(foo_parents[1].foos[5].b[0] == 11); + assert(foo_parents[1].foos[5].b[2] == 23); + assert(foo_parents[1].foos[5].bar.inner == [109, 110, 111]); + + assert(foo_parents[1].foos[1].a == 1); + assert(foo_parents[1].foos[1].bar.inner == [100, 101, 102]); + + let (first_foo, rest_of_slice) = foo_parents[y - 2].foos.pop_front(); + + foo_parents[y - 2].foos = rest_of_slice; + assert(first_foo.a == 40); + assert(first_foo.b[0] == 14); + assert(first_foo.b[1] == 15); + assert(first_foo.b[2] == 16); + assert(first_foo.bar.inner == [109, 110, 111]); + + assert(foo_parents[1].foos[0].a == 1); + assert(foo_parents[1].foos[0].b[0] == 2); + assert(foo_parents[1].foos[0].b[1] == 3); + assert(foo_parents[1].foos[0].b[2] == 20); + assert(foo_parents[1].foos[0].b[3] == 20); + assert(foo_parents[1].foos[0].bar.inner == [100, 101, 102]); + + test_insert_remove_const_index(foo_parents, y, foo); + + // Check values before insertion + assert(foo_parents[1].foos[1].a == 4); + assert(foo_parents[1].foos[1].b[0] == 5); + assert(foo_parents[1].foos[1].b[1] == 6); + assert(foo_parents[1].foos[1].b[2] == 5000); + assert(foo_parents[1].foos[1].b[3] == 21); + assert(foo_parents[1].foos[1].bar.inner == [103, 104, 105]); + + assert(foo_parents[1].foos.len() == 5); + assert(foo_parents[1].foos[2].a == 50); + assert(foo_parents[1].foos[2].b[0] == 5); + assert(foo_parents[1].foos[2].b[2] == 5000); + assert(foo_parents[1].foos[2].bar.inner == [106, 107, 108]); + + assert(foo_parents[1].foos[3].a == 10); + assert(foo_parents[1].foos[3].b[0] == 11); + assert(foo_parents[1].foos[3].b[2] == 23); + assert(foo_parents[1].foos[3].bar.inner == [109, 110, 111]); + + foo_parents[y - 2].foos = foo_parents[y - 2].foos.insert(y - 1, foo); + assert(foo_parents[1].foos.len() == 6); + + // Check values correctly moved after insertion + assert(foo_parents[1].foos[0].a == 1); + assert(foo_parents[1].foos[0].b[0] == 2); + assert(foo_parents[1].foos[0].b[1] == 3); + assert(foo_parents[1].foos[0].b[2] == 20); + assert(foo_parents[1].foos[0].b[3] == 20); + assert(foo_parents[1].foos[0].bar.inner == [100, 101, 102]); + + assert(foo_parents[1].foos[1].a == 4); + assert(foo_parents[1].foos[1].b[0] == 5); + assert(foo_parents[1].foos[1].b[1] == 6); + assert(foo_parents[1].foos[1].b[2] == 5000); + assert(foo_parents[1].foos[1].b[3] == 21); + assert(foo_parents[1].foos[1].bar.inner == [103, 104, 105]); + + assert(foo_parents[1].foos[2].a == 40); + assert(foo_parents[1].foos[2].b[0] == 14); + assert(foo_parents[1].foos[2].b[2] == 16); + assert(foo_parents[1].foos[2].bar.inner == [109, 110, 111]); + + assert(foo_parents[1].foos[3].a == 50); + assert(foo_parents[1].foos[3].b[0] == 5); + assert(foo_parents[1].foos[3].b[2] == 5000); + assert(foo_parents[1].foos[3].bar.inner == [106, 107, 108]); + + assert(foo_parents[1].foos[4].a == 10); + assert(foo_parents[1].foos[4].b[0] == 11); + assert(foo_parents[1].foos[4].b[2] == 23); + assert(foo_parents[1].foos[4].bar.inner == [109, 110, 111]); + + assert(foo_parents[1].foos[5].a == 10); + assert(foo_parents[1].foos[5].b[0] == 11); + assert(foo_parents[1].foos[5].b[2] == 23); + assert(foo_parents[1].foos[5].bar.inner == [109, 110, 111]); + + let (rest_of_slice, removed_elem) = foo_parents[y - 2].foos.remove(y - 1); + foo_parents[1].foos = rest_of_slice; + + // Check that the accurate element was removed + assert(removed_elem.a == 40); + assert(removed_elem.b[0] == 14); + assert(removed_elem.b[2] == 16); + assert(removed_elem.bar.inner == [109, 110, 111]); + + // Check that we have altered our slice accurately following a removal + assert(foo_parents[1].foos[1].a == 4); + assert(foo_parents[1].foos[1].b[0] == 5); + assert(foo_parents[1].foos[1].b[1] == 6); + assert(foo_parents[1].foos[1].b[2] == 5000); + assert(foo_parents[1].foos[1].b[3] == 21); + assert(foo_parents[1].foos[1].bar.inner == [103, 104, 105]); + + assert(foo_parents[1].foos[2].a == 50); + assert(foo_parents[1].foos[2].b[0] == 5); + assert(foo_parents[1].foos[2].b[2] == 5000); + assert(foo_parents[1].foos[2].bar.inner == [106, 107, 108]); + + assert(foo_parents[1].foos[3].a == 10); + assert(foo_parents[1].foos[3].b[0] == 11); + assert(foo_parents[1].foos[3].b[2] == 23); + assert(foo_parents[1].foos[3].bar.inner == [109, 110, 111]); + + assert(foo_parents[1].foos[4].b[0] == 11); + assert(foo_parents[1].foos[4].b[2] == 23); + assert(foo_parents[1].foos[4].bar.inner == [109, 110, 111]); +} + +fn test_insert_remove_const_index(mut foo_parents: [FooParent], y: Field, foo: Foo) { + // Check values before insertion + assert(foo_parents[1].foos[1].a == 4); + assert(foo_parents[1].foos[1].b[0] == 5); + assert(foo_parents[1].foos[1].b[1] == 6); + assert(foo_parents[1].foos[1].b[2] == 5000); + assert(foo_parents[1].foos[1].b[3] == 21); + assert(foo_parents[1].foos[1].bar.inner == [103, 104, 105]); + + assert(foo_parents[1].foos.len() == 5); + assert(foo_parents[1].foos[2].a == 50); + assert(foo_parents[1].foos[2].b[0] == 5); + assert(foo_parents[1].foos[2].b[2] == 5000); + assert(foo_parents[1].foos[2].bar.inner == [106, 107, 108]); + + assert(foo_parents[1].foos[3].a == 10); + assert(foo_parents[1].foos[3].b[0] == 11); + assert(foo_parents[1].foos[3].b[2] == 23); + assert(foo_parents[1].foos[3].bar.inner == [109, 110, 111]); + + foo_parents[y - 2].foos = foo_parents[y - 2].foos.insert(2, foo); + assert(foo_parents[1].foos.len() == 6); + + // Check values correctly moved after insertion + assert(foo_parents[1].foos[0].a == 1); + assert(foo_parents[1].foos[0].b[0] == 2); + assert(foo_parents[1].foos[0].b[1] == 3); + assert(foo_parents[1].foos[0].b[2] == 20); + assert(foo_parents[1].foos[0].b[3] == 20); + assert(foo_parents[1].foos[0].bar.inner == [100, 101, 102]); + + assert(foo_parents[1].foos[1].a == 4); + assert(foo_parents[1].foos[1].b[0] == 5); + assert(foo_parents[1].foos[1].b[1] == 6); + assert(foo_parents[1].foos[1].b[2] == 5000); + assert(foo_parents[1].foos[1].b[3] == 21); + assert(foo_parents[1].foos[1].bar.inner == [103, 104, 105]); + + assert(foo_parents[1].foos[2].a == 40); + assert(foo_parents[1].foos[2].b[0] == 14); + assert(foo_parents[1].foos[2].b[2] == 16); + assert(foo_parents[1].foos[2].bar.inner == [109, 110, 111]); + + assert(foo_parents[1].foos[3].a == 50); + assert(foo_parents[1].foos[3].b[0] == 5); + assert(foo_parents[1].foos[3].b[2] == 5000); + assert(foo_parents[1].foos[3].bar.inner == [106, 107, 108]); + + assert(foo_parents[1].foos[4].a == 10); + assert(foo_parents[1].foos[4].b[0] == 11); + assert(foo_parents[1].foos[4].b[2] == 23); + assert(foo_parents[1].foos[4].bar.inner == [109, 110, 111]); + + assert(foo_parents[1].foos[5].a == 10); + assert(foo_parents[1].foos[5].b[0] == 11); + assert(foo_parents[1].foos[5].b[2] == 23); + assert(foo_parents[1].foos[5].bar.inner == [109, 110, 111]); + + let (rest_of_slice, removed_elem) = foo_parents[y - 2].foos.remove(2); + foo_parents[1].foos = rest_of_slice; + + // Check that the accurate element was removed + assert(removed_elem.a == 40); + assert(removed_elem.b[0] == 14); + assert(removed_elem.b[2] == 16); + assert(removed_elem.bar.inner == [109, 110, 111]); + + // Check that we have altered our slice accurately following a removal + assert(foo_parents[1].foos[1].a == 4); + assert(foo_parents[1].foos[1].b[0] == 5); + assert(foo_parents[1].foos[1].b[1] == 6); + assert(foo_parents[1].foos[1].b[2] == 5000); + assert(foo_parents[1].foos[1].b[3] == 21); + assert(foo_parents[1].foos[1].bar.inner == [103, 104, 105]); + + assert(foo_parents[1].foos[2].a == 50); + assert(foo_parents[1].foos[2].b[0] == 5); + assert(foo_parents[1].foos[2].b[2] == 5000); + assert(foo_parents[1].foos[2].bar.inner == [106, 107, 108]); + + assert(foo_parents[1].foos[3].a == 10); + assert(foo_parents[1].foos[3].b[0] == 11); + assert(foo_parents[1].foos[3].b[2] == 23); + assert(foo_parents[1].foos[3].bar.inner == [109, 110, 111]); + + assert(foo_parents[1].foos[4].b[0] == 11); + assert(foo_parents[1].foos[4].b[2] == 23); + assert(foo_parents[1].foos[4].bar.inner == [109, 110, 111]); +} diff --git a/tooling/nargo_cli/tests/execution_success/slices/Nargo.toml b/test_programs/execution_success/slices/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/slices/Nargo.toml rename to test_programs/execution_success/slices/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/slices/Prover.toml b/test_programs/execution_success/slices/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/slices/Prover.toml rename to test_programs/execution_success/slices/Prover.toml diff --git a/test_programs/execution_success/slices/src/main.nr b/test_programs/execution_success/slices/src/main.nr new file mode 100644 index 00000000000..c377d2e5b2f --- /dev/null +++ b/test_programs/execution_success/slices/src/main.nr @@ -0,0 +1,299 @@ +use dep::std::slice; +use dep::std; + +fn main(x: Field, y: pub Field) { + let mut slice = [0; 2]; + assert(slice[0] == 0); + assert(slice[0] != 1); + slice[0] = x; + assert(slice[0] == x); + + let slice_plus_10 = slice.push_back(y); + assert(slice_plus_10[2] == 10); + assert(slice_plus_10[2] != 8); + assert(slice_plus_10.len() == 3); + + let mut new_slice = []; + for i in 0..5 { + new_slice = new_slice.push_back(i); + } + assert(new_slice.len() == 5); + + new_slice = new_slice.push_front(20); + assert(new_slice[0] == 20); + assert(new_slice.len() == 6); + + let (popped_slice, last_elem) = new_slice.pop_back(); + assert(last_elem == 4); + assert(popped_slice.len() == 5); + + let (first_elem, rest_of_slice) = popped_slice.pop_front(); + assert(first_elem == 20); + assert(rest_of_slice.len() == 4); + + new_slice = rest_of_slice.insert(2, 100); + assert(new_slice[2] == 100); + assert(new_slice[4] == 3); + assert(new_slice.len() == 5); + + let (remove_slice, removed_elem) = new_slice.remove(3); + assert(removed_elem == 2); + assert(remove_slice[3] == 3); + assert(remove_slice.len() == 4); + + let append = [1, 2].append([3, 4, 5]); + assert(append.len() == 5); + assert(append[0] == 1); + assert(append[4] == 5); + + regression_2083(); + // The parameters to this function must come from witness values (inputs to main) + regression_merge_slices(x, y); + regression_2370(); +} +// Ensure that slices of struct/tuple values work. +fn regression_2083() { + let y = [(1, 2)]; + let y = y.push_back((3, 4)); // [(1, 2), (3, 4)] + let y = y.push_back((5, 6)); // [(1, 2), (3, 4), (5, 6)] + assert(y[2].1 == 6); + + 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)] + assert(y[0].1 == 13); + assert(y[1].1 == 56); + assert(y[2].0 == 10); + + let (y, x) = y.remove(2); // [(12, 13), (55, 56), (1, 2), (3, 4), (5, 6)] + assert(y[2].0 == 1); + assert(x.0 == 10); + assert(x.1 == 11); + + let (x, y) = y.pop_front(); // [(55, 56), (1, 2), (3, 4), (5, 6)] + assert(y[0].0 == 55); + assert(x.0 == 12); + assert(x.1 == 13); + + let (y, x) = y.pop_back(); // [(55, 56), (1, 2), (3, 4)] + assert(y.len() == 3); + assert(x.0 == 5); + assert(x.1 == 6); +} +// The parameters to this function must come from witness values (inputs to main) +fn regression_merge_slices(x: Field, y: Field) { + merge_slices_if(x, y); + merge_slices_else(x); +} + +fn merge_slices_if(x: Field, y: Field) { + let slice = merge_slices_return(x, y); + assert(slice.len() == 3); + assert(slice[2] == 10); + + let slice = merge_slices_mutate(x, y); + assert(slice.len() == 4); + assert(slice[3] == 5); + + let slice = merge_slices_mutate_in_loop(x, y); + assert(slice.len() == 7); + assert(slice[6] == 4); + + let slice = merge_slices_mutate_two_ifs(x, y); + assert(slice.len() == 6); + assert(slice[3] == 5); + assert(slice[4] == 15); + assert(slice[5] == 30); + + let slice = merge_slices_mutate_between_ifs(x, y); + assert(slice.len() == 8); + assert(slice[3] == 5); + assert(slice[4] == 30); + assert(slice[5] == 15); + assert(slice[6] == 50); + assert(slice[7] == 60); + + merge_slices_push_then_pop(x, y); + + let slice = merge_slices_push_then_insert(x, y); + assert(slice.len() == 7); + assert(slice[1] == 50); + assert(slice[2] == 0); + assert(slice[5] == 30); + assert(slice[6] == 100); + + let slice = merge_slices_remove_between_ifs(x, y); + assert(slice.len() == 5); +} + +fn merge_slices_else(x: Field) { + let slice = merge_slices_return(x, 5); + assert(slice[0] == 0); + assert(slice[1] == 0); + assert(slice.len() == 2); + + let slice = merge_slices_mutate(x, 5); + assert(slice[2] == 5); + assert(slice.len() == 3); + + let slice = merge_slices_mutate_in_loop(x, 5); + assert(slice[2] == 5); + assert(slice.len() == 3); +} +// 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 + } +} +// Test mutating a slice inside of an if statement +fn merge_slices_mutate(x: Field, y: Field) -> [Field] { + let mut slice = [0; 2]; + if x != y { + slice = slice.push_back(y); + slice = slice.push_back(x); + } else { + slice = slice.push_back(x); + } + slice +} +// Test mutating a slice inside of a loop in an if statement +fn merge_slices_mutate_in_loop(x: Field, y: Field) -> [Field] { + let mut slice = [0; 2]; + if x != y { + for i in 0..5 { + slice = slice.push_back(i); + } + } else { + slice = slice.push_back(x); + } + slice +} + +fn merge_slices_mutate_two_ifs(x: Field, y: Field) -> [Field] { + let mut slice = [0; 2]; + if x != y { + slice = slice.push_back(y); + slice = slice.push_back(x); + } else { + slice = slice.push_back(x); + } + + if x == 20 { + slice = slice.push_back(20); + } + + slice = slice.push_back(15); + slice = slice.push_back(30); + + slice +} + +fn merge_slices_mutate_between_ifs(x: Field, y: Field) -> [Field] { + let mut slice = [0; 2]; + if x != y { + slice = slice.push_back(y); + slice = slice.push_back(x); + } else { + slice = slice.push_back(x); + } + + slice = slice.push_back(30); + + if x == 20 { + slice = slice.push_back(20); + } + + slice = slice.push_back(15); + + if x != 20 { + slice = slice.push_back(50); + } + + slice = slice.push_back(60); + + slice +} + +fn merge_slices_push_then_pop(x: Field, y: Field) { + let mut slice = [0; 2]; + if x != y { + slice = slice.push_back(y); + slice = slice.push_back(x); + } else { + slice = slice.push_back(x); + } + + slice = slice.push_back(30); + + if x == 20 { + slice = slice.push_back(20); + } + + let (slice, elem) = slice.pop_back(); + assert(slice.len() == 4); + assert(elem == 30); + + let (slice, elem) = slice.pop_back(); + assert(slice.len() == 3); + assert(elem == x); +} + +fn merge_slices_push_then_insert(x: Field, y: Field) -> [Field] { + let mut slice = [0; 2]; + if x != y { + slice = slice.push_back(y); + slice = slice.push_back(x); + } else { + slice = slice.push_back(x); + } + + slice = slice.push_back(30); + + if x == 20 { + slice = slice.push_back(20); + slice = slice.push_back(15); + } + + slice = slice.insert(1, 50); + // Test that we can use slice insert the same as slice push back + slice = slice.insert(6, 100); + + slice +} + +fn merge_slices_remove_between_ifs(x: Field, y: Field) -> [Field] { + let mut slice = [0; 2]; + if x != y { + slice = slice.push_back(y); + slice = slice.push_back(x); + } else { + slice = slice.push_back(x); + } + + let (mut slice, elem) = slice.remove(2); + assert(elem == y); + + if x == 20 { + slice = slice.push_back(20); + } + + slice = slice.push_back(15); + + if x != 20 { + slice = slice.push_back(50); + } + + slice +} +// Previously, we'd get a type error when trying to assign an array of a different size to +// an existing array variable. Now, we infer the variable must be a slice. +fn regression_2370() { + let mut slice = []; + slice = [1, 2, 3]; +} diff --git a/tooling/nargo_cli/tests/execution_success/strings/Nargo.toml b/test_programs/execution_success/strings/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/strings/Nargo.toml rename to test_programs/execution_success/strings/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/strings/Prover.toml b/test_programs/execution_success/strings/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/strings/Prover.toml rename to test_programs/execution_success/strings/Prover.toml diff --git a/test_programs/execution_success/strings/src/main.nr b/test_programs/execution_success/strings/src/main.nr new file mode 100644 index 00000000000..cb2218837f3 --- /dev/null +++ b/test_programs/execution_success/strings/src/main.nr @@ -0,0 +1,73 @@ +use dep::std; +// Test global string literals +global HELLO_WORLD = "hello world"; + +fn main(message: pub str<11>, y: Field, hex_as_string: str<4>, hex_as_field: Field) { + let mut bad_message = "hello world"; + + assert(message == "hello world"); + assert(message == HELLO_WORLD); + let x = 10; + let z = x * 5; + std::println(10); + + std::println(z); // x * 5 in println not yet supported + std::println(x); + + let array = [1, 2, 3, 5, 8]; + assert(y == 5); // Change to y != 5 to see how the later print statements are not called + std::println(array); + + bad_message = "hell\0\"world"; + std::println(bad_message); + assert(message != bad_message); + + let hash = std::hash::pedersen_commitment([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); +} + +#[test] +fn test_prints_strings() { + let message = "hello world!"; + + std::println(message); + std::println("goodbye world"); +} + +#[test] +fn test_prints_array() { + let array = [1, 2, 3, 5, 8]; + + let s = Test { a: 1, b: 2, c: [3, 4] }; + std::println(s); + + std::println(array); + + let hash = std::hash::pedersen_commitment(array); + std::println(hash); +} + +fn failed_constraint(hex_as_field: Field) { + // TODO(#2116): Note that `println` will not work if a failed constraint can be + // evaluated at compile time. + // When this method is called from a test method or with constant values + // a `Failed constraint` compile error will be caught before this `println` + // is executed as the input will be a constant. + std::println(hex_as_field); + assert(hex_as_field != 0x41); +} + +#[test] +fn test_failed_constraint() { + failed_constraint(0x41); +} + +struct Test { + a: Field, + b: Field, + c: [Field; 2], +} diff --git a/tooling/nargo_cli/tests/execution_success/struct/Nargo.toml b/test_programs/execution_success/struct/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/struct/Nargo.toml rename to test_programs/execution_success/struct/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/struct/Prover.toml b/test_programs/execution_success/struct/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/struct/Prover.toml rename to test_programs/execution_success/struct/Prover.toml diff --git a/test_programs/execution_success/struct/src/main.nr b/test_programs/execution_success/struct/src/main.nr new file mode 100644 index 00000000000..45c5e347e5a --- /dev/null +++ b/test_programs/execution_success/struct/src/main.nr @@ -0,0 +1,75 @@ +struct Foo { + bar: Field, + array: [Field; 2], +} + +struct Pair { + first: Foo, + second: Field, +} + +impl Foo { + fn default(x: Field,y: Field) -> Self { + Self { bar: 0, array: [x,y] } + } +} + +impl Pair { + fn foo(p: Self) -> Foo { + p.first + } + + fn bar(self) -> Field { + self.foo().bar + } +} + +struct Nested { + a: Field, + b: Field +} +struct MyStruct { + my_bool: bool, + my_int: u32, + 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 } }; + (my_struct, a_bool) +} + +struct Animal { + legs: Field, + eyes: u8, +} + +fn get_dog() -> Animal { + let dog = Animal { legs: 4, eyes: 2 }; + dog +} + +struct Unit; + +fn main(x: Field, y: Field) { + let unit = Unit {}; + + let first = Foo::default(x, y); + let p = Pair { first, second: 1 }; + + assert(p.bar() == x); + assert(p.second == y); + assert(p.first.array[0] != p.first.array[1]); + // Nested structs + 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); + assert(struct_from_tuple.my_nest.a == 0); + // Regression test for issue #670 + let Animal { legs, eyes } = get_dog(); + let six = legs + eyes as Field; + + assert(six == 6); + + let Animal { legs: _, eyes: _ } = get_dog(); +} diff --git a/tooling/nargo_cli/tests/execution_success/struct_array_inputs/Nargo.toml b/test_programs/execution_success/struct_array_inputs/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/struct_array_inputs/Nargo.toml rename to test_programs/execution_success/struct_array_inputs/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/struct_array_inputs/Prover.toml b/test_programs/execution_success/struct_array_inputs/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/struct_array_inputs/Prover.toml rename to test_programs/execution_success/struct_array_inputs/Prover.toml diff --git a/tooling/nargo_cli/tests/execution_success/struct_array_inputs/src/main.nr b/test_programs/execution_success/struct_array_inputs/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/execution_success/struct_array_inputs/src/main.nr rename to test_programs/execution_success/struct_array_inputs/src/main.nr diff --git a/tooling/nargo_cli/tests/execution_success/struct_fields_ordering/Nargo.toml b/test_programs/execution_success/struct_fields_ordering/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/struct_fields_ordering/Nargo.toml rename to test_programs/execution_success/struct_fields_ordering/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/struct_fields_ordering/Prover.toml b/test_programs/execution_success/struct_fields_ordering/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/struct_fields_ordering/Prover.toml rename to test_programs/execution_success/struct_fields_ordering/Prover.toml diff --git a/test_programs/execution_success/struct_fields_ordering/src/main.nr b/test_programs/execution_success/struct_fields_ordering/src/main.nr new file mode 100644 index 00000000000..1a2e2d462e2 --- /dev/null +++ b/test_programs/execution_success/struct_fields_ordering/src/main.nr @@ -0,0 +1,12 @@ +// Note that fields are not in alphabetical order. +// We want to check that this ordering is maintained +struct myStruct { + foo: u32, + bar: Field, +} + +fn main(y: pub myStruct) { + assert(y.foo == 5); + assert(y.bar == 7); +} + diff --git a/tooling/nargo_cli/tests/execution_success/struct_inputs/Nargo.toml b/test_programs/execution_success/struct_inputs/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/struct_inputs/Nargo.toml rename to test_programs/execution_success/struct_inputs/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/struct_inputs/Prover.toml b/test_programs/execution_success/struct_inputs/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/struct_inputs/Prover.toml rename to test_programs/execution_success/struct_inputs/Prover.toml diff --git a/tooling/nargo_cli/tests/execution_success/struct_inputs/src/foo.nr b/test_programs/execution_success/struct_inputs/src/foo.nr similarity index 100% rename from tooling/nargo_cli/tests/execution_success/struct_inputs/src/foo.nr rename to test_programs/execution_success/struct_inputs/src/foo.nr diff --git a/tooling/nargo_cli/tests/execution_success/struct_inputs/src/foo/bar.nr b/test_programs/execution_success/struct_inputs/src/foo/bar.nr similarity index 100% rename from tooling/nargo_cli/tests/execution_success/struct_inputs/src/foo/bar.nr rename to test_programs/execution_success/struct_inputs/src/foo/bar.nr diff --git a/test_programs/execution_success/struct_inputs/src/main.nr b/test_programs/execution_success/struct_inputs/src/main.nr new file mode 100644 index 00000000000..5b03483cbaf --- /dev/null +++ b/test_programs/execution_success/struct_inputs/src/main.nr @@ -0,0 +1,34 @@ +mod foo; + +struct myStruct { + foo: u32, + bar: Field, + message: str<5>, +} + +fn main(x: Field, y: pub myStruct, z: pub foo::bar::barStruct, a: pub foo::fooStruct) -> pub Field { + let struct_from_bar = foo::bar::barStruct { val: 1, array: [0, 1], message: "hello" }; + + check_inner_struct(a, z); + + for i in 0..struct_from_bar.array.len() { + assert(struct_from_bar.array[i] == z.array[i]); + } + assert(z.val == struct_from_bar.val); + + assert((struct_from_bar.val * x) == x); + + assert(x != y.bar); + + assert(y.message == "hello"); + assert(a.bar_struct.message == struct_from_bar.message); + + a.bar_struct.array[1] +} + +fn check_inner_struct(a: foo::fooStruct, z: foo::bar::barStruct) { + assert(a.bar_struct.val == z.val); + for i in 0..a.bar_struct.array.len() { + assert(a.bar_struct.array[i] == z.array[i]); + } +} diff --git a/tooling/nargo_cli/tests/execution_success/submodules/Nargo.toml b/test_programs/execution_success/submodules/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/submodules/Nargo.toml rename to test_programs/execution_success/submodules/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/submodules/Prover.toml b/test_programs/execution_success/submodules/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/submodules/Prover.toml rename to test_programs/execution_success/submodules/Prover.toml diff --git a/tooling/nargo_cli/tests/execution_success/submodules/src/main.nr b/test_programs/execution_success/submodules/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/execution_success/submodules/src/main.nr rename to test_programs/execution_success/submodules/src/main.nr diff --git a/tooling/nargo_cli/tests/execution_success/to_be_bytes/Nargo.toml b/test_programs/execution_success/to_be_bytes/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/to_be_bytes/Nargo.toml rename to test_programs/execution_success/to_be_bytes/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/to_be_bytes/Prover.toml b/test_programs/execution_success/to_be_bytes/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/to_be_bytes/Prover.toml rename to test_programs/execution_success/to_be_bytes/Prover.toml diff --git a/test_programs/execution_success/to_be_bytes/src/main.nr b/test_programs/execution_success/to_be_bytes/src/main.nr new file mode 100644 index 00000000000..64d54ddff66 --- /dev/null +++ b/test_programs/execution_success/to_be_bytes/src/main.nr @@ -0,0 +1,12 @@ +fn main(x: Field) -> pub [u8; 31] { + // The result of this byte array will be big-endian + let byte_array = x.to_be_bytes(31); + let mut bytes = [0; 31]; + for i in 0..31 { + bytes[i] = byte_array[i]; + } + if (bytes[30] != 60) | (bytes[29] != 33) | (bytes[28] != 31) { + assert(false); + } + bytes +} diff --git a/tooling/nargo_cli/tests/execution_success/to_bytes_consistent/Nargo.toml b/test_programs/execution_success/to_bytes_consistent/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/to_bytes_consistent/Nargo.toml rename to test_programs/execution_success/to_bytes_consistent/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/to_bytes_consistent/Prover.toml b/test_programs/execution_success/to_bytes_consistent/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/to_bytes_consistent/Prover.toml rename to test_programs/execution_success/to_bytes_consistent/Prover.toml diff --git a/test_programs/execution_success/to_bytes_consistent/src/main.nr b/test_programs/execution_success/to_bytes_consistent/src/main.nr new file mode 100644 index 00000000000..638b34c9bab --- /dev/null +++ b/test_programs/execution_success/to_bytes_consistent/src/main.nr @@ -0,0 +1,13 @@ +// This test aims to check that we have consistent behavior +// between a `to_be_bytes` call (which is radix decomposition under the hood) +// with constant inputs or with witness inputs. +// x = 2040124 +fn main(x: Field) { + let byte_array = x.to_be_bytes(31); + let x_as_constant = 2040124; + let constant_byte_array = x_as_constant.to_be_bytes(31); + assert(constant_byte_array.len() == byte_array.len()); + for i in 0..constant_byte_array.len() { + assert(constant_byte_array[i] == byte_array[i]); + } +} diff --git a/tooling/nargo_cli/tests/execution_success/to_bytes_integration/Nargo.toml b/test_programs/execution_success/to_bytes_integration/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/to_bytes_integration/Nargo.toml rename to test_programs/execution_success/to_bytes_integration/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/to_bytes_integration/Prover.toml b/test_programs/execution_success/to_bytes_integration/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/to_bytes_integration/Prover.toml rename to test_programs/execution_success/to_bytes_integration/Prover.toml diff --git a/test_programs/execution_success/to_bytes_integration/src/main.nr b/test_programs/execution_success/to_bytes_integration/src/main.nr new file mode 100644 index 00000000000..3c43caf1806 --- /dev/null +++ b/test_programs/execution_success/to_bytes_integration/src/main.nr @@ -0,0 +1,25 @@ +use dep::std; + +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]); + assert(le_byte_array[2] == be_byte_array[28]); + + let z = 0 - 1; + let p_bytes = std::field::modulus_le_bytes(); + let z_bytes = z.to_le_bytes(32); + assert(p_bytes[10] == z_bytes[10]); + assert(p_bytes[0] == z_bytes[0] as u8 + 1 as u8); + + let p_bits = std::field::modulus_le_bits(); + let z_bits = z.to_le_bits(std::field::modulus_num_bits() as u32); + assert(z_bits[0] == 0); + assert(p_bits[100] == z_bits[100]); + + a.to_le_bits(std::field::modulus_num_bits() as u32); +} diff --git a/tooling/nargo_cli/tests/execution_success/to_le_bytes/Nargo.toml b/test_programs/execution_success/to_le_bytes/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/to_le_bytes/Nargo.toml rename to test_programs/execution_success/to_le_bytes/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/to_le_bytes/Prover.toml b/test_programs/execution_success/to_le_bytes/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/to_le_bytes/Prover.toml rename to test_programs/execution_success/to_le_bytes/Prover.toml diff --git a/test_programs/execution_success/to_le_bytes/src/main.nr b/test_programs/execution_success/to_le_bytes/src/main.nr new file mode 100644 index 00000000000..05eefc0f143 --- /dev/null +++ b/test_programs/execution_success/to_le_bytes/src/main.nr @@ -0,0 +1,11 @@ +fn main(x: Field) -> pub [u8; 31] { + // The result of this byte array will be little-endian + let byte_array = x.to_le_bytes(31); + assert(byte_array.len() == 31); + + let mut bytes = [0; 31]; + for i in 0..31 { + bytes[i] = byte_array[i]; + } + bytes +} diff --git a/tooling/nargo_cli/tests/execution_success/trait_as_return_type/Nargo.toml b/test_programs/execution_success/trait_as_return_type/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/trait_as_return_type/Nargo.toml rename to test_programs/execution_success/trait_as_return_type/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/trait_as_return_type/Prover.toml b/test_programs/execution_success/trait_as_return_type/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/trait_as_return_type/Prover.toml rename to test_programs/execution_success/trait_as_return_type/Prover.toml diff --git a/test_programs/execution_success/trait_as_return_type/src/main.nr b/test_programs/execution_success/trait_as_return_type/src/main.nr new file mode 100644 index 00000000000..f6828a356c1 --- /dev/null +++ b/test_programs/execution_success/trait_as_return_type/src/main.nr @@ -0,0 +1,51 @@ +trait SomeTrait { + fn magic_number(self) -> Field; +} + +struct A {} +struct B {} +struct C { + x: Field +} + +impl SomeTrait for A { + fn magic_number(self) -> Field { + 2 + } +} + +impl SomeTrait for B { + fn magic_number(self) -> Field { + 4 + } +} + +impl SomeTrait for C { + fn magic_number(self) -> Field { + self.x + } +} + +fn factory_a() -> impl SomeTrait { + A {} +} + +fn factory_b() -> impl SomeTrait { + B {} +} + +fn factory_c(x: Field) -> impl SomeTrait { + C { x } +} +// x = 15 +fn main(x: u32) { + let a = factory_a(); + let b = B {}; + let b2 = factory_b(); + assert(a.magic_number() == 2); + assert(b.magic_number() == 4); + assert(b2.magic_number() == 4); + let c = factory_c(10); + assert(c.magic_number() == 10); + assert(factory_c(13).magic_number() == 13); +} diff --git a/tooling/nargo_cli/tests/execution_success/trait_impl_base_type/Nargo.toml b/test_programs/execution_success/trait_impl_base_type/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/trait_impl_base_type/Nargo.toml rename to test_programs/execution_success/trait_impl_base_type/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/trait_impl_base_type/Prover.toml b/test_programs/execution_success/trait_impl_base_type/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/trait_impl_base_type/Prover.toml rename to test_programs/execution_success/trait_impl_base_type/Prover.toml diff --git a/test_programs/execution_success/trait_impl_base_type/src/main.nr b/test_programs/execution_success/trait_impl_base_type/src/main.nr new file mode 100644 index 00000000000..bb11dcab73a --- /dev/null +++ b/test_programs/execution_success/trait_impl_base_type/src/main.nr @@ -0,0 +1,115 @@ +trait Fieldable { + fn to_field(self) -> Field; +} + +impl Fieldable for u32 { + fn to_field(self) -> Field { + let res = self as Field; + res * 3 + } +} + +impl Fieldable for [u32; 3] { + fn to_field(self) -> Field { + let res = self[0] + self[1] + self[2]; + res as Field + } +} + +impl Fieldable for bool { + fn to_field(self) -> Field { + if self { + 14 + } else { + 3 + } + } +} + +impl Fieldable for (u32, bool) { + fn to_field(self) -> Field { + if self.1 { + self.0 as Field + } else { + 32 + } + } +} + +impl Fieldable for Field { + fn to_field(self) -> Field { + self + } +} + +impl Fieldable for str<6> { + fn to_field(self) -> Field { + 6 + } +} + +impl Fieldable for () { + fn to_field(self) -> Field { + 0 + } +} + +type Point2D = [Field; 2]; +type Point2DAlias = Point2D; + +impl Fieldable for Point2DAlias { + fn to_field(self) -> Field { + self[0] + self[1] + } +} + +impl Fieldable for fmtstr<14, (Field, Field)> { + fn to_field(self) -> Field { + 52 + } +} + +impl Fieldable for fn(u32) -> u32 { + fn to_field(self) -> Field { + self(10) as Field + } +} + +fn some_func(x: u32) -> u32 { + x * 2 - 3 +} + +impl Fieldable for u64 { + fn to_field(self) -> Field { + 66 as Field + } +} +// x = 15 +fn main(x: u32) { + assert(x.to_field() == 15); + let arr: [u32; 3] = [3, 5, 8]; + assert(arr.to_field() == 16); + let b_true = 2 == 2; + assert(b_true.to_field() == 14); + let b_false = 2 == 3; + assert(b_false.to_field() == 3); + let f = 13 as Field; + assert(f.to_field() == 13); + let k_true = (12 as u32, true); + assert(k_true.to_field() == 12); + let k_false = (11 as u32, false); + assert(k_false.to_field() == 32); + let m = "String"; + assert(m.to_field() == 6); + let unit = (); + assert(unit.to_field() == 0); + let point: Point2DAlias = [2, 3]; + assert(point.to_field() == 5); + let i: Field = 2; + let j: Field = 6; + assert(f"i: {i}, j: {j}".to_field() == 52); + assert(some_func.to_field() == 17); + + let mut y = 0 as u64; + assert(y.to_field() == 66); +} diff --git a/tooling/nargo_cli/tests/execution_success/traits_in_crates_1/Nargo.toml b/test_programs/execution_success/traits_in_crates_1/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/traits_in_crates_1/Nargo.toml rename to test_programs/execution_success/traits_in_crates_1/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/traits_in_crates_1/Prover.toml b/test_programs/execution_success/traits_in_crates_1/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/traits_in_crates_1/Prover.toml rename to test_programs/execution_success/traits_in_crates_1/Prover.toml diff --git a/tooling/nargo_cli/tests/execution_success/traits_in_crates_1/crate1/Nargo.toml b/test_programs/execution_success/traits_in_crates_1/crate1/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/traits_in_crates_1/crate1/Nargo.toml rename to test_programs/execution_success/traits_in_crates_1/crate1/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/traits_in_crates_1/crate1/src/lib.nr b/test_programs/execution_success/traits_in_crates_1/crate1/src/lib.nr similarity index 100% rename from tooling/nargo_cli/tests/execution_success/traits_in_crates_1/crate1/src/lib.nr rename to test_programs/execution_success/traits_in_crates_1/crate1/src/lib.nr diff --git a/tooling/nargo_cli/tests/execution_success/traits_in_crates_1/crate2/Nargo.toml b/test_programs/execution_success/traits_in_crates_1/crate2/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/traits_in_crates_1/crate2/Nargo.toml rename to test_programs/execution_success/traits_in_crates_1/crate2/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/traits_in_crates_1/crate2/src/lib.nr b/test_programs/execution_success/traits_in_crates_1/crate2/src/lib.nr similarity index 100% rename from tooling/nargo_cli/tests/execution_success/traits_in_crates_1/crate2/src/lib.nr rename to test_programs/execution_success/traits_in_crates_1/crate2/src/lib.nr diff --git a/test_programs/execution_success/traits_in_crates_1/src/main.nr b/test_programs/execution_success/traits_in_crates_1/src/main.nr new file mode 100644 index 00000000000..7ba2f63c5c0 --- /dev/null +++ b/test_programs/execution_success/traits_in_crates_1/src/main.nr @@ -0,0 +1,5 @@ +fn main(x: Field, y: pub Field) { + let mut V = dep::crate2::MyStruct { Q: x }; + V.Add10(); + assert(V.Q == y); +} diff --git a/tooling/nargo_cli/tests/execution_success/traits_in_crates_2/Nargo.toml b/test_programs/execution_success/traits_in_crates_2/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/traits_in_crates_2/Nargo.toml rename to test_programs/execution_success/traits_in_crates_2/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/traits_in_crates_2/Prover.toml b/test_programs/execution_success/traits_in_crates_2/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/traits_in_crates_2/Prover.toml rename to test_programs/execution_success/traits_in_crates_2/Prover.toml diff --git a/tooling/nargo_cli/tests/execution_success/traits_in_crates_2/crate1/Nargo.toml b/test_programs/execution_success/traits_in_crates_2/crate1/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/traits_in_crates_2/crate1/Nargo.toml rename to test_programs/execution_success/traits_in_crates_2/crate1/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/traits_in_crates_2/crate1/src/lib.nr b/test_programs/execution_success/traits_in_crates_2/crate1/src/lib.nr similarity index 100% rename from tooling/nargo_cli/tests/execution_success/traits_in_crates_2/crate1/src/lib.nr rename to test_programs/execution_success/traits_in_crates_2/crate1/src/lib.nr diff --git a/tooling/nargo_cli/tests/execution_success/traits_in_crates_2/crate2/Nargo.toml b/test_programs/execution_success/traits_in_crates_2/crate2/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/traits_in_crates_2/crate2/Nargo.toml rename to test_programs/execution_success/traits_in_crates_2/crate2/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/traits_in_crates_2/crate2/src/lib.nr b/test_programs/execution_success/traits_in_crates_2/crate2/src/lib.nr similarity index 100% rename from tooling/nargo_cli/tests/execution_success/traits_in_crates_2/crate2/src/lib.nr rename to test_programs/execution_success/traits_in_crates_2/crate2/src/lib.nr diff --git a/test_programs/execution_success/traits_in_crates_2/src/main.nr b/test_programs/execution_success/traits_in_crates_2/src/main.nr new file mode 100644 index 00000000000..7ba2f63c5c0 --- /dev/null +++ b/test_programs/execution_success/traits_in_crates_2/src/main.nr @@ -0,0 +1,5 @@ +fn main(x: Field, y: pub Field) { + let mut V = dep::crate2::MyStruct { Q: x }; + V.Add10(); + assert(V.Q == y); +} diff --git a/tooling/nargo_cli/tests/execution_success/tuple_inputs/Nargo.toml b/test_programs/execution_success/tuple_inputs/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/tuple_inputs/Nargo.toml rename to test_programs/execution_success/tuple_inputs/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/tuple_inputs/Prover.toml b/test_programs/execution_success/tuple_inputs/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/tuple_inputs/Prover.toml rename to test_programs/execution_success/tuple_inputs/Prover.toml diff --git a/test_programs/execution_success/tuple_inputs/src/main.nr b/test_programs/execution_success/tuple_inputs/src/main.nr new file mode 100644 index 00000000000..38fec58f14f --- /dev/null +++ b/test_programs/execution_success/tuple_inputs/src/main.nr @@ -0,0 +1,35 @@ +struct Bar { + inner: [Field; 3], +} + +struct Foo { + a: Field, + b: [Field; 3], + bar: Bar, +} + +fn main(pair: (Field, Field), x: [(u8, u8, u8); 2], struct_pair: (Foo, Bar)) -> pub (Field, u8) { + let mut start_val = 0; + for i in 0..2 { + assert(x[i].0 == start_val); + assert(x[i].1 == start_val + 1); + assert(x[i].2 == start_val + 2); + start_val += 3; + } + + assert(struct_pair.0.a == 1); + assert(struct_pair.0.b == [2, 3, 20]); + assert(struct_pair.0.bar.inner == [100, 101, 102]); + assert(struct_pair.1.inner == [103, 104, 105]); + + let (u, v) = if pair.0 as u32 < 1 { + (pair.0, pair.0 + 1) + } else { + (pair.0 + 1, pair.0) + }; + + assert(u == pair.0 + 1); + assert(v == pair.0); + + (u, v as u8) +} diff --git a/tooling/nargo_cli/tests/execution_success/tuples/Nargo.toml b/test_programs/execution_success/tuples/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/tuples/Nargo.toml rename to test_programs/execution_success/tuples/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/tuples/Prover.toml b/test_programs/execution_success/tuples/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/tuples/Prover.toml rename to test_programs/execution_success/tuples/Prover.toml diff --git a/test_programs/execution_success/tuples/src/main.nr b/test_programs/execution_success/tuples/src/main.nr new file mode 100644 index 00000000000..5526fcad422 --- /dev/null +++ b/test_programs/execution_success/tuples/src/main.nr @@ -0,0 +1,22 @@ +fn main(x: Field, y: Field) { + let pair = (x, y); + assert(pair.0 == 1); + assert(pair.1 == 0); + + let (a, b) = if true { (0, 1) } else { (2, 3) }; + 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); + assert(v == x); + // Test mutating tuples + let mut mutable = ((0, 0), 1, 2, 3); + mutable.0 = (x, y); + mutable.2 = 7; + assert(mutable.0.0 == 1); + assert(mutable.0.1 == 0); + assert(mutable.1 == 1); + assert(mutable.2 == 7); + assert(mutable.3 == 3); +} diff --git a/tooling/nargo_cli/tests/execution_success/type_aliases/Nargo.toml b/test_programs/execution_success/type_aliases/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/type_aliases/Nargo.toml rename to test_programs/execution_success/type_aliases/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/type_aliases/Prover.toml b/test_programs/execution_success/type_aliases/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/type_aliases/Prover.toml rename to test_programs/execution_success/type_aliases/Prover.toml diff --git a/test_programs/execution_success/type_aliases/src/main.nr b/test_programs/execution_success/type_aliases/src/main.nr new file mode 100644 index 00000000000..ee62b0b7260 --- /dev/null +++ b/test_programs/execution_success/type_aliases/src/main.nr @@ -0,0 +1,33 @@ +type Foo = [T; 2]; + +type Bar = Field; + +type One = (A, B); +type Two = One; +type Three = Two; + +struct MyStruct { + foo: Bar, +} + +fn main(x: [Field; 2]) { + let a: Foo = [1, 2]; + assert(a[0] != x[0]); + + let b: Bar = 2; + assert(x[0] == b); + + let c: u8 = 1; + let d: u32 = 2; + let e: Three = (c, d); + assert(e.0 == 1); + + let s = MyStruct { foo: 10 }; + assert(s.foo == 10); + + let _regression2502: Regression2502Alias = Regression2502 {}; +} +// An ICE was occurring if a type alias referred to a struct before it was initialized +// during name resolution. The fix was to initialize structs during def collection instead. +type Regression2502Alias = Regression2502; +struct Regression2502 {} diff --git a/tooling/nargo_cli/tests/execution_success/workspace/Nargo.toml b/test_programs/execution_success/workspace/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/workspace/Nargo.toml rename to test_programs/execution_success/workspace/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/workspace/Prover.toml b/test_programs/execution_success/workspace/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/workspace/Prover.toml rename to test_programs/execution_success/workspace/Prover.toml diff --git a/tooling/nargo_cli/tests/execution_success/workspace/crates/a/Nargo.toml b/test_programs/execution_success/workspace/crates/a/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/workspace/crates/a/Nargo.toml rename to test_programs/execution_success/workspace/crates/a/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/workspace/crates/a/Prover.toml b/test_programs/execution_success/workspace/crates/a/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/workspace/crates/a/Prover.toml rename to test_programs/execution_success/workspace/crates/a/Prover.toml diff --git a/test_programs/execution_success/workspace/crates/a/src/main.nr b/test_programs/execution_success/workspace/crates/a/src/main.nr new file mode 100644 index 00000000000..cf72627da2e --- /dev/null +++ b/test_programs/execution_success/workspace/crates/a/src/main.nr @@ -0,0 +1,3 @@ +fn main(x: Field, y: pub Field) { + assert(x == y); +} diff --git a/tooling/nargo_cli/tests/execution_success/workspace/crates/b/Nargo.toml b/test_programs/execution_success/workspace/crates/b/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/workspace/crates/b/Nargo.toml rename to test_programs/execution_success/workspace/crates/b/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/workspace/crates/b/Prover.toml b/test_programs/execution_success/workspace/crates/b/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/workspace/crates/b/Prover.toml rename to test_programs/execution_success/workspace/crates/b/Prover.toml diff --git a/test_programs/execution_success/workspace/crates/b/src/main.nr b/test_programs/execution_success/workspace/crates/b/src/main.nr new file mode 100644 index 00000000000..4e1fd3c9035 --- /dev/null +++ b/test_programs/execution_success/workspace/crates/b/src/main.nr @@ -0,0 +1,3 @@ +fn main(x: Field, y: pub Field) { + assert(x != y); +} diff --git a/tooling/nargo_cli/tests/execution_success/workspace_default_member/Nargo.toml b/test_programs/execution_success/workspace_default_member/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/workspace_default_member/Nargo.toml rename to test_programs/execution_success/workspace_default_member/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/workspace_default_member/Prover.toml b/test_programs/execution_success/workspace_default_member/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/workspace_default_member/Prover.toml rename to test_programs/execution_success/workspace_default_member/Prover.toml diff --git a/tooling/nargo_cli/tests/execution_success/workspace_default_member/a/Nargo.toml b/test_programs/execution_success/workspace_default_member/a/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/workspace_default_member/a/Nargo.toml rename to test_programs/execution_success/workspace_default_member/a/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/workspace_default_member/a/Prover.toml b/test_programs/execution_success/workspace_default_member/a/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/workspace_default_member/a/Prover.toml rename to test_programs/execution_success/workspace_default_member/a/Prover.toml diff --git a/test_programs/execution_success/workspace_default_member/a/src/main.nr b/test_programs/execution_success/workspace_default_member/a/src/main.nr new file mode 100644 index 00000000000..cf72627da2e --- /dev/null +++ b/test_programs/execution_success/workspace_default_member/a/src/main.nr @@ -0,0 +1,3 @@ +fn main(x: Field, y: pub Field) { + assert(x == y); +} diff --git a/tooling/nargo_cli/tests/execution_success/workspace_default_member/b/Nargo.toml b/test_programs/execution_success/workspace_default_member/b/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/workspace_default_member/b/Nargo.toml rename to test_programs/execution_success/workspace_default_member/b/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/workspace_default_member/b/Prover.toml b/test_programs/execution_success/workspace_default_member/b/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/workspace_default_member/b/Prover.toml rename to test_programs/execution_success/workspace_default_member/b/Prover.toml diff --git a/compiler/integration-tests/circuits/main/src/main.nr b/test_programs/execution_success/workspace_default_member/b/src/main.nr similarity index 100% rename from compiler/integration-tests/circuits/main/src/main.nr rename to test_programs/execution_success/workspace_default_member/b/src/main.nr diff --git a/tooling/nargo_cli/tests/execution_success/xor/Nargo.toml b/test_programs/execution_success/xor/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/xor/Nargo.toml rename to test_programs/execution_success/xor/Nargo.toml diff --git a/tooling/nargo_cli/tests/execution_success/xor/Prover.toml b/test_programs/execution_success/xor/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/execution_success/xor/Prover.toml rename to test_programs/execution_success/xor/Prover.toml diff --git a/test_programs/execution_success/xor/src/main.nr b/test_programs/execution_success/xor/src/main.nr new file mode 100644 index 00000000000..4ceef1b518f --- /dev/null +++ b/test_programs/execution_success/xor/src/main.nr @@ -0,0 +1,5 @@ +fn main(x: u32, y: pub u32) { + let m = x ^ y; + + assert(m != 10); +} diff --git a/tooling/nargo_cli/tests/gates_report.sh b/test_programs/gates_report.sh similarity index 100% rename from tooling/nargo_cli/tests/gates_report.sh rename to test_programs/gates_report.sh diff --git a/tooling/nargo_cli/tests/noir_test_failure/should_fail_mismatch/Nargo.toml b/test_programs/noir_test_failure/should_fail_mismatch/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/noir_test_failure/should_fail_mismatch/Nargo.toml rename to test_programs/noir_test_failure/should_fail_mismatch/Nargo.toml diff --git a/tooling/nargo_cli/tests/noir_test_failure/should_fail_mismatch/Prover.toml b/test_programs/noir_test_failure/should_fail_mismatch/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/noir_test_failure/should_fail_mismatch/Prover.toml rename to test_programs/noir_test_failure/should_fail_mismatch/Prover.toml diff --git a/test_programs/noir_test_failure/should_fail_mismatch/src/main.nr b/test_programs/noir_test_failure/should_fail_mismatch/src/main.nr new file mode 100644 index 00000000000..a677b10b0cd --- /dev/null +++ b/test_programs/noir_test_failure/should_fail_mismatch/src/main.nr @@ -0,0 +1,14 @@ +#[test(should_fail_with = "Not equal")] +fn test_different_string() { + assert_eq(0, 1, "Different string"); +} +// The assert message has a space +#[test(should_fail_with = "Not equal")] +fn test_with_extra_space() { + assert_eq(0, 1, "Not equal "); +} +// The assert message has a space +#[test(should_fail_with = "Not equal")] +fn test_runtime_mismatch() { + assert_eq(dep::std::hash::pedersen_commitment([27])[0], 0, "Not equal "); +} diff --git a/tooling/nargo_cli/tests/noir_test_success/should_fail_with_matches/Nargo.toml b/test_programs/noir_test_success/should_fail_with_matches/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/noir_test_success/should_fail_with_matches/Nargo.toml rename to test_programs/noir_test_success/should_fail_with_matches/Nargo.toml diff --git a/tooling/nargo_cli/tests/noir_test_success/should_fail_with_matches/Prover.toml b/test_programs/noir_test_success/should_fail_with_matches/Prover.toml similarity index 100% rename from tooling/nargo_cli/tests/noir_test_success/should_fail_with_matches/Prover.toml rename to test_programs/noir_test_success/should_fail_with_matches/Prover.toml diff --git a/tooling/nargo_cli/tests/noir_test_success/should_fail_with_matches/src/main.nr b/test_programs/noir_test_success/should_fail_with_matches/src/main.nr similarity index 100% rename from tooling/nargo_cli/tests/noir_test_success/should_fail_with_matches/src/main.nr rename to test_programs/noir_test_success/should_fail_with_matches/src/main.nr diff --git a/test_programs/rebuild.sh b/test_programs/rebuild.sh new file mode 100755 index 00000000000..dfc3dc5c967 --- /dev/null +++ b/test_programs/rebuild.sh @@ -0,0 +1,72 @@ +#!/bin/bash +set -e + +process_dir() { + local dir=$1 + local current_dir=$2 + local dir_name=$(basename "$dir") + + if [[ ! -d "$current_dir/acir_artifacts/$dir_name" ]]; then + mkdir -p $current_dir/acir_artifacts/$dir_name + fi + + cd $dir + if [ -d ./target/ ]; then + rm -r ./target/ + fi + nargo compile && nargo execute witness + + if [ -f ./target/witness.tr ]; then + mv ./target/witness.tr ./target/witness.gz + fi + + if [ -f ./target/${dir_name}.json ]; then + jq -r '.bytecode' ./target/${dir_name}.json | base64 -d > ./target/acir.gz + fi + + rm ./target/${dir_name}.json + + 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 + + mv ./target/*.gz $current_dir/acir_artifacts/$dir_name/target/ + + cd $current_dir +} + +export -f process_dir + +excluded_dirs=("workspace" "workspace_default_member") +current_dir=$(pwd) +base_path="$current_dir/execution_success" + +rm -rf $current_dir/acir_artifacts +mkdir -p $current_dir/acir_artifacts + +# Gather directories to process. +dirs_to_process=() +for dir in $base_path/*; do + if [[ ! -d $dir ]] || [[ " ${excluded_dirs[@]} " =~ " $(basename "$dir") " ]]; then + continue + fi + dirs_to_process+=("$dir") +done + +# Process each directory in parallel +pids=() +for dir in "${dirs_to_process[@]}"; do + process_dir "$dir" "$current_dir" & + pids+=($!) +done + +# Check the exit status of each background job. +for pid in "${pids[@]}"; do + wait $pid || exit_status=$? +done + +# Exit with a failure status if any job failed. +if [ ! -z "$exit_status" ]; then + exit $exit_status +fi \ No newline at end of file diff --git a/tooling/nargo_cli/tests/test_libraries/bad_impl/Nargo.toml b/test_programs/test_libraries/bad_impl/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/test_libraries/bad_impl/Nargo.toml rename to test_programs/test_libraries/bad_impl/Nargo.toml diff --git a/tooling/nargo_cli/tests/test_libraries/bad_impl/src/lib.nr b/test_programs/test_libraries/bad_impl/src/lib.nr similarity index 100% rename from tooling/nargo_cli/tests/test_libraries/bad_impl/src/lib.nr rename to test_programs/test_libraries/bad_impl/src/lib.nr diff --git a/tooling/nargo_cli/tests/test_libraries/bad_name/Nargo.toml b/test_programs/test_libraries/bad_name/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/test_libraries/bad_name/Nargo.toml rename to test_programs/test_libraries/bad_name/Nargo.toml diff --git a/tooling/nargo_cli/tests/test_libraries/bad_name/src/lib.nr b/test_programs/test_libraries/bad_name/src/lib.nr similarity index 100% rename from tooling/nargo_cli/tests/test_libraries/bad_name/src/lib.nr rename to test_programs/test_libraries/bad_name/src/lib.nr diff --git a/tooling/nargo_cli/tests/test_libraries/bin_dep/Nargo.toml b/test_programs/test_libraries/bin_dep/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/test_libraries/bin_dep/Nargo.toml rename to test_programs/test_libraries/bin_dep/Nargo.toml diff --git a/test_programs/test_libraries/bin_dep/src/main.nr b/test_programs/test_libraries/bin_dep/src/main.nr new file mode 100644 index 00000000000..042c85a8afb --- /dev/null +++ b/test_programs/test_libraries/bin_dep/src/main.nr @@ -0,0 +1,3 @@ +fn call_dep1_then_dep2(x: Field, y: Field) { + assert(x == y); +} diff --git a/tooling/nargo_cli/tests/test_libraries/diamond_deps_1/Nargo.toml b/test_programs/test_libraries/diamond_deps_1/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/test_libraries/diamond_deps_1/Nargo.toml rename to test_programs/test_libraries/diamond_deps_1/Nargo.toml diff --git a/test_programs/test_libraries/diamond_deps_1/src/lib.nr b/test_programs/test_libraries/diamond_deps_1/src/lib.nr new file mode 100644 index 00000000000..60c001ec64e --- /dev/null +++ b/test_programs/test_libraries/diamond_deps_1/src/lib.nr @@ -0,0 +1,5 @@ +use dep::dep2::call_dep2; + +pub fn call_dep1_then_dep2(x: Field, y: Field) -> Field { + call_dep2(x, y) +} diff --git a/tooling/nargo_cli/tests/test_libraries/diamond_deps_2/Nargo.toml b/test_programs/test_libraries/diamond_deps_2/Nargo.toml similarity index 100% rename from tooling/nargo_cli/tests/test_libraries/diamond_deps_2/Nargo.toml rename to test_programs/test_libraries/diamond_deps_2/Nargo.toml diff --git a/test_programs/test_libraries/diamond_deps_2/src/lib.nr b/test_programs/test_libraries/diamond_deps_2/src/lib.nr new file mode 100644 index 00000000000..46dce3d5600 --- /dev/null +++ b/test_programs/test_libraries/diamond_deps_2/src/lib.nr @@ -0,0 +1,5 @@ +global RESOLVE_THIS = 3; + +pub fn call_dep2(x: Field, y: Field) -> Field { + x + y +} diff --git a/tooling/backend_interface/src/lib.rs b/tooling/backend_interface/src/lib.rs index 6c91c181a92..36ebe5ebb91 100644 --- a/tooling/backend_interface/src/lib.rs +++ b/tooling/backend_interface/src/lib.rs @@ -153,6 +153,34 @@ impl BackendOpcodeSupport { } } } + + pub fn all() -> BackendOpcodeSupport { + BackendOpcodeSupport { + opcodes: HashSet::from([ + "arithmetic".to_string(), + "directive".to_string(), + "brillig".to_string(), + "memory_init".to_string(), + "memory_op".to_string(), + ]), + black_box_functions: HashSet::from([ + "sha256".to_string(), + "schnorr_verify".to_string(), + "blake2s".to_string(), + "pedersen".to_string(), + "pedersen_hash".to_string(), + "hash_to_field_128_security".to_string(), + "ecdsa_secp256k1".to_string(), + "fixed_base_scalar_mul".to_string(), + "and".to_string(), + "xor".to_string(), + "range".to_string(), + "keccak256".to_string(), + "recursive_aggregation".to_string(), + "ecdsa_secp256r1".to_string(), + ]), + } + } } #[cfg(test)] diff --git a/tooling/backend_interface/src/proof_system.rs b/tooling/backend_interface/src/proof_system.rs index 95da1462d4e..dcf1dad56b0 100644 --- a/tooling/backend_interface/src/proof_system.rs +++ b/tooling/backend_interface/src/proof_system.rs @@ -36,6 +36,18 @@ impl Backend { InfoCommand { crs_path: self.crs_directory() }.run(binary_path) } + /// If we cannot get a valid backend, returns the default backend which supports all the opcodes + /// and uses Plonk with width 3 + /// The function also prints a message saying we could not find a backend + pub fn get_backend_info_or_default(&self) -> (Language, BackendOpcodeSupport) { + if let Ok(backend_info) = self.get_backend_info() { + (backend_info.0, backend_info.1) + } else { + println!("No valid backend found, defaulting to Plonk with width 3 and all opcodes supported"); + (Language::PLONKCSat { width: 3 }, BackendOpcodeSupport::all()) + } + } + pub fn prove( &self, circuit: &Circuit, diff --git a/tooling/bb_abstraction_leaks/build.rs b/tooling/bb_abstraction_leaks/build.rs index fe6861a80b8..166e61a5a97 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.12.0"; +const VERSION: &str = "0.16.0"; const TAG: &str = formatcp!("aztec-packages-v{}", VERSION); const API_URL: &str = @@ -30,8 +30,11 @@ fn main() -> Result<(), String> { }; // Arm builds of linux are not supported + // We do not panic because we allow users to run nargo without a backend. if let (Os::Linux, Arch::AARCH64) = (&os, &arch) { - panic!("ARM64 builds of linux are not supported") + println!( + "cargo:warning=ARM64 builds of linux are not supported for the barretenberg binary" + ); }; println!("cargo:rustc-env=BB_BINARY_URL={}", get_bb_download_url(arch, os)); diff --git a/tooling/debugger/src/context.rs b/tooling/debugger/src/context.rs index a97bc9e8b86..4c429ca2a67 100644 --- a/tooling/debugger/src/context.rs +++ b/tooling/debugger/src/context.rs @@ -1,9 +1,10 @@ -use acvm::acir::circuit::{Opcode, OpcodeLocation}; +use acvm::acir::circuit::{Circuit, Opcode, OpcodeLocation}; +use acvm::acir::native_types::{Witness, WitnessMap}; +use acvm::brillig_vm::{brillig::Value, Registers}; use acvm::pwg::{ ACVMStatus, BrilligSolver, BrilligSolverStatus, ForeignCallWaitInfo, StepResult, ACVM, }; -use acvm::BlackBoxFunctionSolver; -use acvm::{acir::circuit::Circuit, acir::native_types::WitnessMap}; +use acvm::{BlackBoxFunctionSolver, FieldElement}; use nargo::artifacts::debug::DebugArtifact; use nargo::errors::{ExecutionError, Location}; @@ -23,9 +24,8 @@ pub(super) enum DebugCommandResult { pub(super) struct DebugContext<'a, B: BlackBoxFunctionSolver> { acvm: ACVM<'a, B>, brillig_solver: Option>, - foreign_call_executor: ForeignCallExecutor, + foreign_call_executor: Box, debug_artifact: &'a DebugArtifact, - show_output: bool, breakpoints: HashSet, } @@ -35,13 +35,13 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { circuit: &'a Circuit, debug_artifact: &'a DebugArtifact, initial_witness: WitnessMap, + foreign_call_executor: Box, ) -> Self { Self { acvm: ACVM::new(blackbox_solver, &circuit.opcodes, initial_witness), brillig_solver: None, - foreign_call_executor: ForeignCallExecutor::default(), + foreign_call_executor, debug_artifact, - show_output: true, breakpoints: HashSet::new(), } } @@ -50,6 +50,18 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { self.acvm.opcodes() } + pub(super) fn get_witness_map(&self) -> &WitnessMap { + self.acvm.witness_map() + } + + pub(super) fn overwrite_witness( + &mut self, + witness: Witness, + value: FieldElement, + ) -> Option { + self.acvm.overwrite_witness(witness, value) + } + pub(super) fn get_current_opcode_location(&self) -> Option { let ip = self.acvm.instruction_pointer(); if ip >= self.get_opcodes().len() { @@ -64,11 +76,11 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { } } - // Returns the callstack in source code locations for the currently - // executing opcode. This can be None if the execution finished (and - // get_current_opcode_location() returns None) or if the opcode is not - // mapped to a specific source location in the debug artifact (which can - // happen for certain opcodes inserted synthetically by the compiler) + /// Returns the callstack in source code locations for the currently + /// executing opcode. This can be `None` if the execution finished (and + /// `get_current_opcode_location()` returns `None`) or if the opcode is not + /// mapped to a specific source location in the debug artifact (which can + /// happen for certain opcodes inserted synthetically by the compiler) pub(super) fn get_current_source_location(&self) -> Option> { self.get_current_opcode_location() .as_ref() @@ -106,15 +118,19 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { } fn handle_foreign_call(&mut self, foreign_call: ForeignCallWaitInfo) -> DebugCommandResult { - let foreign_call_result = - self.foreign_call_executor.execute(&foreign_call, self.show_output); + let foreign_call_result = self.foreign_call_executor.execute(&foreign_call); match foreign_call_result { Ok(foreign_call_result) => { - self.acvm.resolve_pending_foreign_call(foreign_call_result); + if let Some(mut solver) = self.brillig_solver.take() { + solver.resolve_pending_foreign_call(foreign_call_result); + self.brillig_solver = Some(solver); + } else { + 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), + Err(error) => DebugCommandResult::Error(error.into()), } } @@ -158,13 +174,50 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { } } + fn currently_executing_brillig(&self) -> bool { + if self.brillig_solver.is_some() { + return true; + } + + match self.get_current_opcode_location() { + Some(OpcodeLocation::Brillig { .. }) => true, + Some(OpcodeLocation::Acir(acir_index)) => { + matches!(self.get_opcodes()[acir_index], Opcode::Brillig(_)) + } + _ => false, + } + } + + fn get_current_acir_index(&self) -> Option { + self.get_current_opcode_location().map(|opcode_location| match opcode_location { + OpcodeLocation::Acir(acir_index) => acir_index, + OpcodeLocation::Brillig { acir_index, .. } => acir_index, + }) + } + + fn step_out_of_brillig_opcode(&mut self) -> DebugCommandResult { + let Some(start_acir_index) = self.get_current_acir_index() else { + return DebugCommandResult::Done; + }; + loop { + let result = self.step_into_opcode(); + if !matches!(result, DebugCommandResult::Ok) { + return result; + } + let new_acir_index = self.get_current_acir_index().unwrap(); + if new_acir_index != start_acir_index { + return DebugCommandResult::Ok; + } + } + } + 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) + if self.currently_executing_brillig() { + self.step_out_of_brillig_opcode() } else { - self.acvm.solve_opcode() - }; - self.handle_acvm_status(status) + let status = self.acvm.solve_opcode(); + self.handle_acvm_status(status) + } } pub(super) fn next(&mut self) -> DebugCommandResult { @@ -190,6 +243,32 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { } } + pub(super) fn is_executing_brillig(&self) -> bool { + let opcodes = self.get_opcodes(); + let acir_index = self.acvm.instruction_pointer(); + acir_index < opcodes.len() && matches!(opcodes[acir_index], Opcode::Brillig(..)) + } + + pub(super) fn get_brillig_registers(&self) -> Option<&Registers> { + self.brillig_solver.as_ref().map(|solver| solver.get_registers()) + } + + pub(super) fn set_brillig_register(&mut self, register_index: usize, value: FieldElement) { + if let Some(solver) = self.brillig_solver.as_mut() { + solver.set_register(register_index, value.into()); + } + } + + pub(super) fn get_brillig_memory(&self) -> Option<&[Value]> { + self.brillig_solver.as_ref().map(|solver| solver.get_memory()) + } + + pub(super) fn write_brillig_memory(&mut self, ptr: usize, value: FieldElement) { + if let Some(solver) = self.brillig_solver.as_mut() { + solver.write_memory_at(ptr, value.into()); + } + } + fn breakpoint_reached(&self) -> bool { if let Some(location) = self.get_current_opcode_location() { self.breakpoints.contains(&location) @@ -240,3 +319,223 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { self.acvm.finalize() } } + +#[cfg(test)] +struct StubbedSolver; + +#[cfg(test)] +impl BlackBoxFunctionSolver for StubbedSolver { + fn schnorr_verify( + &self, + _public_key_x: &FieldElement, + _public_key_y: &FieldElement, + _signature: &[u8], + _message: &[u8], + ) -> Result { + unimplemented!(); + } + + fn pedersen_commitment( + &self, + _inputs: &[FieldElement], + _domain_separator: u32, + ) -> Result<(FieldElement, FieldElement), acvm::BlackBoxResolutionError> { + unimplemented!(); + } + + fn pedersen_hash( + &self, + _inputs: &[FieldElement], + _domain_separator: u32, + ) -> Result { + unimplemented!(); + } + + fn fixed_base_scalar_mul( + &self, + _low: &FieldElement, + _high: &FieldElement, + ) -> Result<(FieldElement, FieldElement), acvm::BlackBoxResolutionError> { + unimplemented!(); + } +} + +#[cfg(test)] +#[test] +fn test_resolve_foreign_calls_stepping_into_brillig() { + use std::collections::BTreeMap; + + use acvm::acir::{ + brillig::{Opcode as BrilligOpcode, RegisterIndex, RegisterOrMemory}, + circuit::brillig::{Brillig, BrilligInputs}, + native_types::Expression, + }; + + use nargo::ops::DefaultForeignCallExecutor; + + let fe_0 = FieldElement::zero(); + let fe_1 = FieldElement::one(); + let w_x = Witness(1); + + let blackbox_solver = &StubbedSolver; + + let brillig_opcodes = Brillig { + inputs: vec![BrilligInputs::Single(Expression { + linear_combinations: vec![(fe_1, w_x)], + ..Expression::default() + })], + outputs: vec![], + bytecode: vec![ + BrilligOpcode::Const { destination: RegisterIndex::from(1), value: Value::from(fe_0) }, + BrilligOpcode::ForeignCall { + function: "clear_mock".into(), + destinations: vec![], + inputs: vec![RegisterOrMemory::RegisterIndex(RegisterIndex::from(0))], + }, + BrilligOpcode::Stop, + ], + predicate: None, + }; + let opcodes = vec![Opcode::Brillig(brillig_opcodes)]; + let current_witness_index = 2; + let circuit = &Circuit { current_witness_index, opcodes, ..Circuit::default() }; + + let debug_symbols = vec![]; + let file_map = BTreeMap::new(); + let warnings = vec![]; + let debug_artifact = &DebugArtifact { debug_symbols, file_map, warnings }; + + let initial_witness = BTreeMap::from([(Witness(1), fe_1)]).into(); + + let mut context = DebugContext::new( + blackbox_solver, + circuit, + debug_artifact, + initial_witness, + Box::new(DefaultForeignCallExecutor::new(true)), + ); + + assert_eq!(context.get_current_opcode_location(), Some(OpcodeLocation::Acir(0))); + + // execute the first Brillig opcode (const) + let result = context.step_into_opcode(); + assert!(matches!(result, DebugCommandResult::Ok)); + assert_eq!( + context.get_current_opcode_location(), + Some(OpcodeLocation::Brillig { acir_index: 0, brillig_index: 1 }) + ); + + // try to execute the second Brillig opcode (and resolve the foreign call) + let result = context.step_into_opcode(); + assert!(matches!(result, DebugCommandResult::Ok)); + assert_eq!( + context.get_current_opcode_location(), + Some(OpcodeLocation::Brillig { acir_index: 0, brillig_index: 1 }) + ); + + // retry the second Brillig opcode (foreign call should be finished) + let result = context.step_into_opcode(); + assert!(matches!(result, DebugCommandResult::Ok)); + assert_eq!( + context.get_current_opcode_location(), + Some(OpcodeLocation::Brillig { acir_index: 0, brillig_index: 2 }) + ); + + // last Brillig opcode + let result = context.step_into_opcode(); + assert!(matches!(result, DebugCommandResult::Done)); + assert_eq!(context.get_current_opcode_location(), None); +} + +#[cfg(test)] +#[test] +fn test_break_brillig_block_while_stepping_acir_opcodes() { + use std::collections::BTreeMap; + + use acvm::acir::{ + brillig::{Opcode as BrilligOpcode, RegisterIndex}, + circuit::brillig::{Brillig, BrilligInputs, BrilligOutputs}, + native_types::Expression, + }; + use acvm::brillig_vm::brillig::BinaryFieldOp; + use nargo::ops::DefaultForeignCallExecutor; + + let fe_0 = FieldElement::zero(); + let fe_1 = FieldElement::one(); + let w_x = Witness(1); + let w_y = Witness(2); + let w_z = Witness(3); + + let blackbox_solver = &StubbedSolver; + + // This Brillig block is equivalent to: z = x + y + let brillig_opcodes = Brillig { + inputs: vec![ + BrilligInputs::Single(Expression { + linear_combinations: vec![(fe_1, w_x)], + ..Expression::default() + }), + BrilligInputs::Single(Expression { + linear_combinations: vec![(fe_1, w_y)], + ..Expression::default() + }), + ], + outputs: vec![BrilligOutputs::Simple(w_z)], + bytecode: vec![ + BrilligOpcode::BinaryFieldOp { + destination: RegisterIndex::from(0), + op: BinaryFieldOp::Add, + lhs: RegisterIndex::from(0), + rhs: RegisterIndex::from(1), + }, + BrilligOpcode::Stop, + ], + predicate: None, + }; + let opcodes = vec![ + // z = x + y + Opcode::Brillig(brillig_opcodes), + // x + y - z = 0 + Opcode::Arithmetic(Expression { + mul_terms: vec![], + linear_combinations: vec![(fe_1, w_x), (fe_1, w_y), (-fe_1, w_z)], + q_c: fe_0, + }), + ]; + let current_witness_index = 3; + let circuit = &Circuit { current_witness_index, opcodes, ..Circuit::default() }; + + let debug_symbols = vec![]; + let file_map = BTreeMap::new(); + let warnings = vec![]; + let debug_artifact = &DebugArtifact { debug_symbols, file_map, warnings }; + + let initial_witness = BTreeMap::from([(Witness(1), fe_1), (Witness(2), fe_1)]).into(); + + let mut context = DebugContext::new( + blackbox_solver, + circuit, + debug_artifact, + initial_witness, + Box::new(DefaultForeignCallExecutor::new(true)), + ); + + // set breakpoint + let breakpoint_location = OpcodeLocation::Brillig { acir_index: 0, brillig_index: 1 }; + assert!(context.add_breakpoint(breakpoint_location)); + + // execute the first ACIR opcode (Brillig block) -> should reach the breakpoint instead + let result = context.step_acir_opcode(); + assert!(matches!(result, DebugCommandResult::BreakpointReached(_))); + assert_eq!(context.get_current_opcode_location(), Some(breakpoint_location)); + + // continue execution to the next ACIR opcode + let result = context.step_acir_opcode(); + assert!(matches!(result, DebugCommandResult::Ok)); + assert_eq!(context.get_current_opcode_location(), Some(OpcodeLocation::Acir(1))); + + // last ACIR opcode + let result = context.step_acir_opcode(); + assert!(matches!(result, DebugCommandResult::Done)); + assert_eq!(context.get_current_opcode_location(), None); +} diff --git a/tooling/debugger/src/repl.rs b/tooling/debugger/src/repl.rs index 320d8edf63a..cb6539cbdb1 100644 --- a/tooling/debugger/src/repl.rs +++ b/tooling/debugger/src/repl.rs @@ -1,11 +1,10 @@ use crate::context::{DebugCommandResult, DebugContext}; -use acvm::acir::circuit::{Opcode, OpcodeLocation}; -use acvm::BlackBoxFunctionSolver; -use acvm::{acir::circuit::Circuit, acir::native_types::WitnessMap}; +use acvm::acir::circuit::{Circuit, Opcode, OpcodeLocation}; +use acvm::acir::native_types::{Witness, WitnessMap}; +use acvm::{BlackBoxFunctionSolver, FieldElement}; -use nargo::artifacts::debug::DebugArtifact; -use nargo::NargoError; +use nargo::{artifacts::debug::DebugArtifact, ops::DefaultForeignCallExecutor, NargoError}; use easy_repl::{command, CommandStatus, Repl}; use std::cell::RefCell; @@ -33,8 +32,13 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { debug_artifact: &'a DebugArtifact, initial_witness: WitnessMap, ) -> Self { - let context = - DebugContext::new(blackbox_solver, circuit, debug_artifact, initial_witness.clone()); + let context = DebugContext::new( + blackbox_solver, + circuit, + debug_artifact, + initial_witness.clone(), + Box::new(DefaultForeignCallExecutor::new(true)), + ); Self { context, blackbox_solver, @@ -274,6 +278,7 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { self.circuit, self.debug_artifact, self.initial_witness.clone(), + Box::new(DefaultForeignCallExecutor::new(true)), ); for opcode_location in breakpoints { self.context.add_breakpoint(opcode_location); @@ -283,6 +288,93 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { self.show_current_vm_status(); } + pub fn show_witness_map(&self) { + let witness_map = self.context.get_witness_map(); + // NOTE: we need to clone() here to get the iterator + for (witness, value) in witness_map.clone().into_iter() { + println!("_{} = {value}", witness.witness_index()); + } + } + + pub fn show_witness(&self, index: u32) { + if let Some(value) = self.context.get_witness_map().get_index(index) { + println!("_{} = {value}", index); + } + } + + pub fn update_witness(&mut self, index: u32, value: String) { + let Some(field_value) = FieldElement::try_from_str(&value) else { + println!("Invalid witness value: {value}"); + return; + }; + + let witness = Witness::from(index); + _ = self.context.overwrite_witness(witness, field_value); + println!("_{} = {value}", index); + } + + pub fn show_brillig_registers(&self) { + if !self.context.is_executing_brillig() { + println!("Not executing a Brillig block"); + return; + } + + let Some(registers) = self.context.get_brillig_registers() else { + // this can happen when just entering the Brillig block since ACVM + // would have not initialized the Brillig VM yet; in fact, the + // Brillig code may be skipped altogether + println!("Brillig VM registers not available"); + return; + }; + + for (index, value) in registers.inner.iter().enumerate() { + println!("{index} = {}", value.to_field()); + } + } + + pub fn set_brillig_register(&mut self, index: usize, value: String) { + let Some(field_value) = FieldElement::try_from_str(&value) else { + println!("Invalid value: {value}"); + return; + }; + if !self.context.is_executing_brillig() { + println!("Not executing a Brillig block"); + return; + } + self.context.set_brillig_register(index, field_value); + } + + pub fn show_brillig_memory(&self) { + if !self.context.is_executing_brillig() { + println!("Not executing a Brillig block"); + return; + } + + let Some(memory) = self.context.get_brillig_memory() else { + // this can happen when just entering the Brillig block since ACVM + // would have not initialized the Brillig VM yet; in fact, the + // Brillig code may be skipped altogether + println!("Brillig VM memory not available"); + return; + }; + + for (index, value) in memory.iter().enumerate() { + println!("{index} = {}", value.to_field()); + } + } + + pub fn write_brillig_memory(&mut self, index: usize, value: String) { + let Some(field_value) = FieldElement::try_from_str(&value) else { + println!("Invalid value: {value}"); + return; + }; + if !self.context.is_executing_brillig() { + println!("Not executing a Brillig block"); + return; + } + self.context.write_brillig_memory(index, field_value); + } + fn is_solved(&self) -> bool { self.context.is_solved() } @@ -393,6 +485,76 @@ pub fn run( } }, ) + .add( + "witness", + command! { + "show witness map", + () => || { + ref_context.borrow().show_witness_map(); + Ok(CommandStatus::Done) + } + }, + ) + .add( + "witness", + command! { + "display a single witness from the witness map", + (index: u32) => |index| { + ref_context.borrow().show_witness(index); + Ok(CommandStatus::Done) + } + }, + ) + .add( + "witness", + command! { + "update a witness with the given value", + (index: u32, value: String) => |index, value| { + ref_context.borrow_mut().update_witness(index, value); + Ok(CommandStatus::Done) + } + }, + ) + .add( + "registers", + command! { + "show Brillig registers (valid when executing a Brillig block)", + () => || { + ref_context.borrow().show_brillig_registers(); + Ok(CommandStatus::Done) + } + }, + ) + .add( + "regset", + command! { + "update a Brillig register with the given value", + (index: usize, value: String) => |index, value| { + ref_context.borrow_mut().set_brillig_register(index, value); + Ok(CommandStatus::Done) + } + }, + ) + .add( + "memory", + command! { + "show Brillig memory (valid when executing a Brillig block)", + () => || { + ref_context.borrow().show_brillig_memory(); + Ok(CommandStatus::Done) + } + }, + ) + .add( + "memset", + command! { + "update a Brillig memory cell with the given value", + (index: usize, value: String) => |index, value| { + ref_context.borrow_mut().write_brillig_memory(index, value); + Ok(CommandStatus::Done) + } + }, + ) .build() .expect("Failed to initialize debugger repl"); diff --git a/tooling/lsp/Cargo.toml b/tooling/lsp/Cargo.toml index a1d58a0de49..67778c744db 100644 --- a/tooling/lsp/Cargo.toml +++ b/tooling/lsp/Cargo.toml @@ -14,6 +14,7 @@ codespan-lsp.workspace = true codespan-reporting.workspace = true lsp-types.workspace = true nargo.workspace = true +nargo_fmt.workspace = true nargo_toml.workspace = true noirc_driver.workspace = true noirc_errors.workspace = true @@ -23,6 +24,8 @@ serde_json.workspace = true tower.workspace = true cfg-if.workspace = true async-lsp = { workspace = true, features = ["omni-trait"] } +serde_with = "3.2.0" +fm.workspace = true [target.'cfg(all(target_arch = "wasm32", not(target_os = "wasi")))'.dependencies] wasm-bindgen.workspace = true diff --git a/tooling/lsp/src/lib.rs b/tooling/lsp/src/lib.rs index 6e71f3d642d..d8a992155dd 100644 --- a/tooling/lsp/src/lib.rs +++ b/tooling/lsp/src/lib.rs @@ -4,6 +4,7 @@ #![cfg_attr(not(test), warn(unused_crate_dependencies, unused_extern_crates))] use std::{ + collections::HashMap, future::Future, ops::{self, ControlFlow}, path::{Path, PathBuf}, @@ -26,7 +27,8 @@ use notifications::{ on_did_open_text_document, on_did_save_text_document, on_exit, on_initialized, }; use requests::{ - on_code_lens_request, on_initialize, on_shutdown, on_test_run_request, on_tests_request, + on_code_lens_request, on_formatting, on_initialize, on_profile_run_request, on_shutdown, + on_test_run_request, on_tests_request, }; use serde_json::Value as JsonValue; use tower::Service; @@ -44,11 +46,17 @@ pub struct LspState { root_path: Option, client: ClientSocket, solver: WrapperSolver, + input_files: HashMap, } impl LspState { fn new(client: &ClientSocket, solver: impl BlackBoxFunctionSolver + 'static) -> Self { - Self { client: client.clone(), root_path: None, solver: WrapperSolver(Box::new(solver)) } + Self { + client: client.clone(), + root_path: None, + solver: WrapperSolver(Box::new(solver)), + input_files: HashMap::new(), + } } } @@ -62,10 +70,12 @@ impl NargoLspService { let mut router = Router::new(state); router .request::(on_initialize) + .request::(on_formatting) .request::(on_shutdown) .request::(on_code_lens_request) .request::(on_tests_request) .request::(on_test_run_request) + .request::(on_profile_run_request) .notification::(on_initialized) .notification::(on_did_change_configuration) .notification::(on_did_open_text_document) diff --git a/tooling/lsp/src/notifications/mod.rs b/tooling/lsp/src/notifications/mod.rs index 93fa8baf6ac..f6484f49d48 100644 --- a/tooling/lsp/src/notifications/mod.rs +++ b/tooling/lsp/src/notifications/mod.rs @@ -30,23 +30,27 @@ pub(super) fn on_did_change_configuration( } pub(super) fn on_did_open_text_document( - _state: &mut LspState, - _params: DidOpenTextDocumentParams, + state: &mut LspState, + params: DidOpenTextDocumentParams, ) -> ControlFlow> { + state.input_files.insert(params.text_document.uri.to_string(), params.text_document.text); ControlFlow::Continue(()) } pub(super) fn on_did_change_text_document( - _state: &mut LspState, - _params: DidChangeTextDocumentParams, + state: &mut LspState, + params: DidChangeTextDocumentParams, ) -> ControlFlow> { + let text = params.content_changes.into_iter().next().unwrap().text; + state.input_files.insert(params.text_document.uri.to_string(), text); ControlFlow::Continue(()) } pub(super) fn on_did_close_text_document( - _state: &mut LspState, - _params: DidCloseTextDocumentParams, + state: &mut LspState, + params: DidCloseTextDocumentParams, ) -> ControlFlow> { + state.input_files.remove(¶ms.text_document.uri.to_string()); ControlFlow::Continue(()) } diff --git a/tooling/lsp/src/requests/code_lens_request.rs b/tooling/lsp/src/requests/code_lens_request.rs index 7794fb8444f..602ed268981 100644 --- a/tooling/lsp/src/requests/code_lens_request.rs +++ b/tooling/lsp/src/requests/code_lens_request.rs @@ -23,6 +23,9 @@ const INFO_CODELENS_TITLE: &str = "Info"; const EXECUTE_COMMAND: &str = "nargo.execute"; const EXECUTE_CODELENS_TITLE: &str = "Execute"; +const PROFILE_COMMAND: &str = "nargo.profile"; +const PROFILE_CODELENS_TITLE: &str = "Profile"; + fn with_arrow(title: &str) -> String { format!("{ARROW} {title}") } @@ -163,6 +166,16 @@ fn on_code_lens_request_inner( let execute_lens = CodeLens { range, command: Some(execute_command), data: None }; lenses.push(execute_lens); + + let profile_command = Command { + title: PROFILE_CODELENS_TITLE.to_string(), + command: PROFILE_COMMAND.into(), + arguments: Some(package_selection_args(&workspace, package)), + }; + + let profile_lens = CodeLens { range, command: Some(profile_command), data: None }; + + lenses.push(profile_lens); } } @@ -200,6 +213,16 @@ fn on_code_lens_request_inner( let info_lens = CodeLens { range, command: Some(info_command), data: None }; lenses.push(info_lens); + + let profile_command = Command { + title: PROFILE_CODELENS_TITLE.to_string(), + command: PROFILE_COMMAND.into(), + arguments: Some(package_selection_args(&workspace, package)), + }; + + let profile_lens = CodeLens { range, command: Some(profile_command), data: None }; + + lenses.push(profile_lens); } } } diff --git a/tooling/lsp/src/requests/mod.rs b/tooling/lsp/src/requests/mod.rs index 166adb10b5a..a319f2593a4 100644 --- a/tooling/lsp/src/requests/mod.rs +++ b/tooling/lsp/src/requests/mod.rs @@ -1,7 +1,9 @@ use std::future::Future; -use crate::types::{CodeLensOptions, InitializeParams, TextDocumentSyncOptions}; +use crate::types::{CodeLensOptions, InitializeParams}; use async_lsp::ResponseError; +use lsp_types::{Position, TextDocumentSyncCapability, TextDocumentSyncKind}; +use nargo_fmt::Config; use crate::{ types::{InitializeResult, NargoCapability, NargoTestsOptions, ServerCapabilities}, @@ -19,11 +21,13 @@ use crate::{ // and params passed in. mod code_lens_request; +mod profile_run; mod test_run; mod tests; pub(crate) use { - code_lens_request::on_code_lens_request, test_run::on_test_run_request, tests::on_tests_request, + code_lens_request::on_code_lens_request, profile_run::on_profile_run_request, + test_run::on_test_run_request, tests::on_tests_request, }; pub(crate) fn on_initialize( @@ -33,8 +37,7 @@ pub(crate) fn on_initialize( state.root_path = params.root_uri.and_then(|root_uri| root_uri.to_file_path().ok()); async { - let text_document_sync = - TextDocumentSyncOptions { save: Some(true.into()), ..Default::default() }; + let text_document_sync = TextDocumentSyncCapability::Kind(TextDocumentSyncKind::FULL); let code_lens = CodeLensOptions { resolve_provider: Some(false) }; @@ -48,8 +51,9 @@ pub(crate) fn on_initialize( Ok(InitializeResult { capabilities: ServerCapabilities { - text_document_sync: Some(text_document_sync.into()), + text_document_sync: Some(text_document_sync), code_lens_provider: Some(code_lens), + document_formatting_provider: true, nargo: Some(nargo), }, server_info: None, @@ -57,6 +61,42 @@ pub(crate) fn on_initialize( } } +pub(crate) fn on_formatting( + state: &mut LspState, + params: lsp_types::DocumentFormattingParams, +) -> impl Future>, ResponseError>> { + std::future::ready(on_formatting_inner(state, params)) +} + +fn on_formatting_inner( + state: &LspState, + params: lsp_types::DocumentFormattingParams, +) -> Result>, ResponseError> { + let path = params.text_document.uri.to_string(); + + if let Some(source) = state.input_files.get(&path) { + let (module, errors) = noirc_frontend::parse_program(source); + if !errors.is_empty() { + return Ok(None); + } + + let new_text = nargo_fmt::format(source, module, &Config::default()); + + let start_position = Position { line: 0, character: 0 }; + let end_position = Position { + line: source.lines().count() as u32, + character: source.chars().count() as u32, + }; + + Ok(Some(vec![lsp_types::TextEdit { + range: lsp_types::Range::new(start_position, end_position), + new_text, + }])) + } else { + Ok(None) + } +} + pub(crate) fn on_shutdown( _state: &mut LspState, _params: (), @@ -68,7 +108,7 @@ pub(crate) fn on_shutdown( mod initialization { use async_lsp::ClientSocket; use lsp_types::{ - CodeLensOptions, InitializeParams, TextDocumentSyncCapability, TextDocumentSyncOptions, + CodeLensOptions, InitializeParams, TextDocumentSyncCapability, TextDocumentSyncKind, }; use tokio::test; @@ -86,10 +126,11 @@ mod initialization { assert!(matches!( response.capabilities, ServerCapabilities { - text_document_sync: Some(TextDocumentSyncCapability::Options( - TextDocumentSyncOptions { save: Some(_), .. } + text_document_sync: Some(TextDocumentSyncCapability::Kind( + TextDocumentSyncKind::FULL )), code_lens_provider: Some(CodeLensOptions { resolve_provider: Some(false) }), + document_formatting_provider: true, .. } )); diff --git a/tooling/lsp/src/requests/profile_run.rs b/tooling/lsp/src/requests/profile_run.rs new file mode 100644 index 00000000000..84888d30ba5 --- /dev/null +++ b/tooling/lsp/src/requests/profile_run.rs @@ -0,0 +1,102 @@ +use std::{ + collections::{BTreeMap, HashMap}, + future::{self, Future}, +}; + +use acvm::{acir::circuit::Opcode, Language}; +use async_lsp::{ErrorCode, ResponseError}; +use nargo::artifacts::debug::DebugArtifact; +use nargo_toml::{find_package_manifest, resolve_workspace_from_toml, PackageSelection}; +use noirc_driver::{CompileOptions, DebugFile, NOIR_ARTIFACT_VERSION_STRING}; +use noirc_errors::{debug_info::OpCodesCount, Location}; + +use crate::{ + types::{NargoProfileRunParams, NargoProfileRunResult}, + LspState, +}; +use fm::FileId; + +pub(crate) fn on_profile_run_request( + state: &mut LspState, + params: NargoProfileRunParams, +) -> impl Future> { + future::ready(on_profile_run_request_inner(state, params)) +} + +fn on_profile_run_request_inner( + state: &LspState, + params: NargoProfileRunParams, +) -> Result { + let root_path = state.root_path.as_deref().ok_or_else(|| { + ResponseError::new(ErrorCode::REQUEST_FAILED, "Could not find project root") + })?; + + let toml_path = find_package_manifest(root_path, root_path).map_err(|err| { + // If we cannot find a manifest, we can't run the test + ResponseError::new(ErrorCode::REQUEST_FAILED, err) + })?; + + let crate_name = params.package; + + let workspace = resolve_workspace_from_toml( + &toml_path, + PackageSelection::DefaultOrAll, + Some(NOIR_ARTIFACT_VERSION_STRING.to_string()), + ) + .map_err(|err| { + // If we found a manifest, but the workspace is invalid, we raise an error about it + ResponseError::new(ErrorCode::REQUEST_FAILED, err) + })?; + + // Since we filtered on crate name, this should be the only item in the iterator + match workspace.into_iter().next() { + Some(_package) => { + let (binary_packages, contract_packages): (Vec<_>, Vec<_>) = workspace + .into_iter() + .filter(|package| !package.is_library()) + .cloned() + .partition(|package| package.is_binary()); + + // # TODO(#3504): Consider how to incorporate Backend relevant information in wider context. + let is_opcode_supported = |_opcode: &Opcode| true; + let np_language = Language::PLONKCSat { width: 3 }; + + let (compiled_programs, compiled_contracts) = nargo::ops::compile_workspace( + &workspace, + &binary_packages, + &contract_packages, + np_language, + is_opcode_supported, + &CompileOptions::default(), + ) + .map_err(|err| ResponseError::new(ErrorCode::REQUEST_FAILED, err))?; + + let mut opcodes_counts: HashMap = HashMap::new(); + let mut file_map: BTreeMap = BTreeMap::new(); + for compiled_program in &compiled_programs { + let span_opcodes = compiled_program.debug.count_span_opcodes(); + let debug_artifact: DebugArtifact = compiled_program.clone().into(); + opcodes_counts.extend(span_opcodes); + file_map.extend(debug_artifact.file_map); + } + + for compiled_contract in &compiled_contracts { + let functions = &compiled_contract.functions; + let debug_artifact: DebugArtifact = compiled_contract.clone().into(); + file_map.extend(debug_artifact.file_map); + for contract_function in functions { + let span_opcodes = contract_function.debug.count_span_opcodes(); + opcodes_counts.extend(span_opcodes); + } + } + + let result = NargoProfileRunResult { file_map, opcodes_counts }; + + Ok(result) + } + None => Err(ResponseError::new( + ErrorCode::REQUEST_FAILED, + format!("Could not locate package named: {crate_name}"), + )), + } +} diff --git a/tooling/lsp/src/types.rs b/tooling/lsp/src/types.rs index 10f1764c63f..ba964cba0c1 100644 --- a/tooling/lsp/src/types.rs +++ b/tooling/lsp/src/types.rs @@ -1,5 +1,10 @@ +use fm::FileId; +use noirc_driver::DebugFile; +use noirc_errors::{debug_info::OpCodesCount, Location}; use noirc_frontend::graph::CrateName; use serde::{Deserialize, Serialize}; +use serde_with::serde_as; +use std::collections::{BTreeMap, HashMap}; // Re-providing lsp_types that we don't need to override pub(crate) use lsp_types::{ @@ -7,19 +12,19 @@ pub(crate) use lsp_types::{ DidChangeConfigurationParams, DidChangeTextDocumentParams, DidCloseTextDocumentParams, DidOpenTextDocumentParams, DidSaveTextDocumentParams, InitializeParams, InitializedParams, LogMessageParams, MessageType, Position, PublishDiagnosticsParams, Range, ServerInfo, - TextDocumentSyncCapability, TextDocumentSyncOptions, Url, + TextDocumentSyncCapability, Url, }; pub(crate) mod request { use lsp_types::{request::Request, InitializeParams}; use super::{ - InitializeResult, NargoTestRunParams, NargoTestRunResult, NargoTestsParams, - NargoTestsResult, + InitializeResult, NargoProfileRunParams, NargoProfileRunResult, NargoTestRunParams, + NargoTestRunResult, NargoTestsParams, NargoTestsResult, }; // Re-providing lsp_types that we don't need to override - pub(crate) use lsp_types::request::{CodeLensRequest as CodeLens, Shutdown}; + pub(crate) use lsp_types::request::{CodeLensRequest as CodeLens, Formatting, Shutdown}; #[derive(Debug)] pub(crate) struct Initialize; @@ -44,6 +49,14 @@ pub(crate) mod request { type Result = NargoTestsResult; const METHOD: &'static str = "nargo/tests"; } + + #[derive(Debug)] + pub(crate) struct NargoProfileRun; + impl Request for NargoProfileRun { + type Params = NargoProfileRunParams; + type Result = NargoProfileRunResult; + const METHOD: &'static str = "nargo/profile/run"; + } } pub(crate) mod notification { @@ -99,6 +112,9 @@ pub(crate) struct ServerCapabilities { #[serde(skip_serializing_if = "Option::is_none")] pub(crate) code_lens_provider: Option, + /// The server provides document formatting. + pub(crate) document_formatting_provider: bool, + /// The server handles and provides custom nargo messages. #[serde(skip_serializing_if = "Option::is_none")] pub(crate) nargo: Option, @@ -186,5 +202,16 @@ pub(crate) struct NargoTestRunResult { pub(crate) result: String, pub(crate) message: Option, } +#[derive(Debug, Serialize, Deserialize)] +pub(crate) struct NargoProfileRunParams { + pub(crate) package: CrateName, +} +#[serde_as] +#[derive(Debug, Serialize, Deserialize)] +pub(crate) struct NargoProfileRunResult { + pub(crate) file_map: BTreeMap, + #[serde_as(as = "Vec<(_, _)>")] + pub(crate) opcodes_counts: HashMap, +} pub(crate) type CodeLensResult = Option>; diff --git a/tooling/nargo/Cargo.toml b/tooling/nargo/Cargo.toml index e6674c043d7..f8269459968 100644 --- a/tooling/nargo/Cargo.toml +++ b/tooling/nargo/Cargo.toml @@ -24,3 +24,4 @@ iter-extended.workspace = true serde.workspace = true thiserror.workspace = true codespan-reporting.workspace = true +rayon = "1.8.0" \ No newline at end of file diff --git a/tooling/nargo/src/artifacts/debug.rs b/tooling/nargo/src/artifacts/debug.rs index 19d8b88e641..40acc7db8f8 100644 --- a/tooling/nargo/src/artifacts/debug.rs +++ b/tooling/nargo/src/artifacts/debug.rs @@ -1,5 +1,5 @@ use codespan_reporting::files::{Error, Files, SimpleFile}; -use noirc_driver::DebugFile; +use noirc_driver::{CompiledContract, CompiledProgram, DebugFile}; use noirc_errors::{debug_info::DebugInfo, Location}; use noirc_evaluator::errors::SsaReport; use serde::{Deserialize, Serialize}; @@ -96,6 +96,32 @@ impl DebugArtifact { } } +impl From for DebugArtifact { + fn from(compiled_program: CompiledProgram) -> Self { + DebugArtifact { + debug_symbols: vec![compiled_program.debug], + file_map: compiled_program.file_map, + warnings: compiled_program.warnings, + } + } +} + +impl From<&CompiledContract> for DebugArtifact { + fn from(compiled_artifact: &CompiledContract) -> Self { + let all_functions_debug: Vec = compiled_artifact + .functions + .iter() + .map(|contract_function| contract_function.debug.clone()) + .collect(); + + DebugArtifact { + debug_symbols: all_functions_debug, + file_map: compiled_artifact.file_map.clone(), + warnings: compiled_artifact.warnings.clone(), + } + } +} + impl<'a> Files<'a> for DebugArtifact { type FileId = FileId; type Name = PathString; diff --git a/tooling/nargo/src/errors.rs b/tooling/nargo/src/errors.rs index ba52d127e9e..bca8ca24767 100644 --- a/tooling/nargo/src/errors.rs +++ b/tooling/nargo/src/errors.rs @@ -2,13 +2,36 @@ use acvm::{ acir::circuit::OpcodeLocation, pwg::{ErrorLocation, OpcodeResolutionError}, }; -use noirc_errors::{debug_info::DebugInfo, CustomDiagnostic, FileDiagnostic}; +use noirc_errors::{ + debug_info::DebugInfo, reporter::ReportedErrors, CustomDiagnostic, FileDiagnostic, +}; pub use noirc_errors::Location; +use noirc_frontend::graph::CrateName; use noirc_printable_type::ForeignCallError; use thiserror::Error; +/// Errors covering situations where a package cannot be compiled. +#[derive(Debug, Error)] +pub enum CompileError { + #[error("Package `{0}` has type `lib` but only `bin` types can be compiled")] + LibraryCrate(CrateName), + + #[error("Package `{0}` is expected to have a `main` function but it does not")] + MissingMainFunction(CrateName), + + /// Errors encountered while compiling the Noir program. + /// These errors are already written to stderr. + #[error("Aborting due to {} previous error{}", .0.error_count, if .0.error_count == 1 { "" } else { "s" })] + ReportedErrors(ReportedErrors), +} +impl From for CompileError { + fn from(errors: ReportedErrors) -> Self { + Self::ReportedErrors(errors) + } +} + #[derive(Debug, Error)] pub enum NargoError { /// Error while compiling Noir into ACIR. diff --git a/tooling/nargo/src/ops/compile.rs b/tooling/nargo/src/ops/compile.rs new file mode 100644 index 00000000000..d4164eaa865 --- /dev/null +++ b/tooling/nargo/src/ops/compile.rs @@ -0,0 +1,154 @@ +use acvm::{acir::circuit::Opcode, Language}; +use fm::FileManager; +use noirc_driver::{CompilationResult, CompileOptions, CompiledContract, CompiledProgram}; + +use crate::errors::CompileError; +use crate::prepare_package; +use crate::{package::Package, workspace::Workspace}; + +use rayon::prelude::*; + +/// Compiles workspace. +/// +/// # Errors +/// +/// This function will return an error if there are any compilations errors reported. +pub fn compile_workspace( + workspace: &Workspace, + binary_packages: &[Package], + contract_packages: &[Package], + np_language: Language, + is_opcode_supported: impl Fn(&Opcode) -> bool + std::marker::Sync, + compile_options: &CompileOptions, +) -> Result<(Vec, Vec), CompileError> { + // Compile all of the packages in parallel. + let program_results: Vec<(FileManager, CompilationResult)> = binary_packages + .par_iter() + .map(|package| { + compile_program(workspace, package, compile_options, np_language, &is_opcode_supported) + }) + .collect(); + let contract_results: Vec<(FileManager, CompilationResult)> = + contract_packages + .par_iter() + .map(|package| { + compile_contract(package, compile_options, np_language, &is_opcode_supported) + }) + .collect(); + + // Report any warnings/errors which were encountered during compilation. + let compiled_programs: Vec = program_results + .into_iter() + .map(|(file_manager, compilation_result)| { + report_errors( + compilation_result, + &file_manager, + compile_options.deny_warnings, + compile_options.silence_warnings, + ) + }) + .collect::>()?; + let compiled_contracts: Vec = contract_results + .into_iter() + .map(|(file_manager, compilation_result)| { + report_errors( + compilation_result, + &file_manager, + compile_options.deny_warnings, + compile_options.silence_warnings, + ) + }) + .collect::>()?; + + Ok((compiled_programs, compiled_contracts)) +} + +pub fn compile_program( + workspace: &Workspace, + package: &Package, + compile_options: &CompileOptions, + np_language: Language, + is_opcode_supported: &impl Fn(&Opcode) -> bool, +) -> (FileManager, CompilationResult) { + let (mut context, crate_id) = + prepare_package(package, Box::new(|path| std::fs::read_to_string(path))); + + let program_artifact_path = workspace.package_build_path(package); + let mut debug_artifact_path = program_artifact_path.clone(); + debug_artifact_path.set_file_name(format!("debug_{}.json", package.name)); + + let (program, warnings) = + match noirc_driver::compile_main(&mut context, crate_id, compile_options, None, true) { + Ok(program_and_warnings) => program_and_warnings, + Err(errors) => { + return (context.file_manager, Err(errors)); + } + }; + + // 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( + acvm::acir::circuit::opcodes::BlackBoxFuncCall::PedersenHash { .. }, + ) = opcode + { + true + } else { + is_opcode_supported(opcode) + } + }; + + // Apply backend specific optimizations. + let optimized_program = + crate::ops::optimize_program(program, np_language, &is_opcode_supported_pedersen_hash) + .expect("Backend does not support an opcode that is in the IR"); + + (context.file_manager, Ok((optimized_program, warnings))) +} + +fn compile_contract( + package: &Package, + compile_options: &CompileOptions, + np_language: Language, + is_opcode_supported: &impl Fn(&Opcode) -> bool, +) -> (FileManager, CompilationResult) { + let (mut context, crate_id) = + prepare_package(package, Box::new(|path| std::fs::read_to_string(path))); + let (contract, warnings) = + match noirc_driver::compile_contract(&mut context, crate_id, compile_options) { + Ok(contracts_and_warnings) => contracts_and_warnings, + Err(errors) => { + return (context.file_manager, Err(errors)); + } + }; + + let optimized_contract = + crate::ops::optimize_contract(contract, np_language, &is_opcode_supported) + .expect("Backend does not support an opcode that is in the IR"); + + (context.file_manager, Ok((optimized_contract, warnings))) +} + +pub(crate) fn report_errors( + result: CompilationResult, + file_manager: &FileManager, + deny_warnings: bool, + silence_warnings: bool, +) -> Result { + let (t, warnings) = result.map_err(|errors| { + noirc_errors::reporter::report_all( + file_manager.as_file_map(), + &errors, + deny_warnings, + silence_warnings, + ) + })?; + + noirc_errors::reporter::report_all( + file_manager.as_file_map(), + &warnings, + deny_warnings, + silence_warnings, + ); + + Ok(t) +} diff --git a/tooling/nargo/src/ops/execute.rs b/tooling/nargo/src/ops/execute.rs index cf14934d61e..d7cb44188c4 100644 --- a/tooling/nargo/src/ops/execute.rs +++ b/tooling/nargo/src/ops/execute.rs @@ -7,16 +7,14 @@ use crate::NargoError; use super::foreign_calls::ForeignCallExecutor; -pub fn execute_circuit( - blackbox_solver: &B, +pub fn execute_circuit( circuit: &Circuit, initial_witness: WitnessMap, - show_output: bool, + blackbox_solver: &B, + foreign_call_executor: &mut F, ) -> Result { let mut acvm = ACVM::new(blackbox_solver, &circuit.opcodes, initial_witness); - let mut foreign_call_executor = ForeignCallExecutor::default(); - loop { let solver_status = acvm.solve(); @@ -50,8 +48,7 @@ pub fn execute_circuit( })); } ACVMStatus::RequiresForeignCall(foreign_call) => { - let foreign_call_result = - foreign_call_executor.execute(&foreign_call, show_output)?; + let foreign_call_result = foreign_call_executor.execute(&foreign_call)?; acvm.resolve_pending_foreign_call(foreign_call_result); } } diff --git a/tooling/nargo/src/ops/foreign_calls.rs b/tooling/nargo/src/ops/foreign_calls.rs index 4d20a0bd4f0..1ca270a5bf7 100644 --- a/tooling/nargo/src/ops/foreign_calls.rs +++ b/tooling/nargo/src/ops/foreign_calls.rs @@ -2,17 +2,19 @@ use acvm::{ acir::brillig::{ForeignCallParam, ForeignCallResult, Value}, pwg::ForeignCallWaitInfo, }; -use iter_extended::vecmap; use noirc_printable_type::{decode_string_value, ForeignCallError, PrintableValueDisplay}; -use crate::NargoError; +pub trait ForeignCallExecutor { + fn execute( + &mut self, + foreign_call: &ForeignCallWaitInfo, + ) -> Result; +} /// This enumeration represents the Brillig foreign calls that are natively supported by nargo. /// After resolution of a foreign call, nargo will restart execution of the ACVM pub(crate) enum ForeignCall { Println, - Sequence, - ReverseSequence, CreateMock, SetMockParams, SetMockReturns, @@ -30,8 +32,6 @@ impl ForeignCall { pub(crate) fn name(&self) -> &'static str { match self { ForeignCall::Println => "println", - ForeignCall::Sequence => "get_number_sequence", - ForeignCall::ReverseSequence => "get_reverse_number_sequence", ForeignCall::CreateMock => "create_mock", ForeignCall::SetMockParams => "set_mock_params", ForeignCall::SetMockReturns => "set_mock_returns", @@ -43,8 +43,6 @@ impl ForeignCall { pub(crate) fn lookup(op_name: &str) -> Option { match op_name { "println" => Some(ForeignCall::Println), - "get_number_sequence" => Some(ForeignCall::Sequence), - "get_reverse_number_sequence" => Some(ForeignCall::ReverseSequence), "create_mock" => Some(ForeignCall::CreateMock), "set_mock_params" => Some(ForeignCall::SetMockParams), "set_mock_returns" => Some(ForeignCall::SetMockReturns), @@ -89,51 +87,59 @@ impl MockedCall { } #[derive(Debug, Default)] -pub struct ForeignCallExecutor { +pub struct DefaultForeignCallExecutor { /// Mocks have unique ids used to identify them in Noir, allowing to update or remove them. last_mock_id: usize, /// The registered mocks mocked_responses: Vec, + /// Whether to print [`ForeignCall::Println`] output. + show_output: bool, } -impl ForeignCallExecutor { - pub fn execute( +impl DefaultForeignCallExecutor { + pub fn new(show_output: bool) -> Self { + DefaultForeignCallExecutor { show_output, ..DefaultForeignCallExecutor::default() } + } +} + +impl DefaultForeignCallExecutor { + fn extract_mock_id( + foreign_call_inputs: &[ForeignCallParam], + ) -> Result<(usize, &[ForeignCallParam]), ForeignCallError> { + let (id, params) = + foreign_call_inputs.split_first().ok_or(ForeignCallError::MissingForeignCallInputs)?; + Ok((id.unwrap_value().to_usize(), params)) + } + + fn find_mock_by_id(&mut self, id: usize) -> Option<&mut MockedCall> { + self.mocked_responses.iter_mut().find(|response| response.id == id) + } + + fn parse_string(param: &ForeignCallParam) -> String { + let fields: Vec<_> = param.values().into_iter().map(|value| value.to_field()).collect(); + decode_string_value(&fields) + } + + fn execute_println(foreign_call_inputs: &[ForeignCallParam]) -> Result<(), ForeignCallError> { + let display_values: PrintableValueDisplay = foreign_call_inputs.try_into()?; + println!("{display_values}"); + Ok(()) + } +} + +impl ForeignCallExecutor for DefaultForeignCallExecutor { + fn execute( &mut self, foreign_call: &ForeignCallWaitInfo, - show_output: bool, - ) -> Result { + ) -> Result { let foreign_call_name = foreign_call.function.as_str(); match ForeignCall::lookup(foreign_call_name) { Some(ForeignCall::Println) => { - if show_output { + if self.show_output { Self::execute_println(&foreign_call.inputs)?; } Ok(ForeignCallResult { values: vec![] }) } - Some(ForeignCall::Sequence) => { - let sequence_length: u128 = - foreign_call.inputs[0].unwrap_value().to_field().to_u128(); - let sequence = vecmap(0..sequence_length, Value::from); - - Ok(ForeignCallResult { - values: vec![ - ForeignCallParam::Single(sequence_length.into()), - ForeignCallParam::Array(sequence), - ], - }) - } - Some(ForeignCall::ReverseSequence) => { - let sequence_length: u128 = - foreign_call.inputs[0].unwrap_value().to_field().to_u128(); - let sequence = vecmap((0..sequence_length).rev(), Value::from); - - Ok(ForeignCallResult { - values: vec![ - ForeignCallParam::Single(sequence_length.into()), - ForeignCallParam::Array(sequence), - ], - }) - } Some(ForeignCall::CreateMock) => { let mock_oracle_name = Self::parse_string(&foreign_call.inputs[0]); assert!(ForeignCall::lookup(&mock_oracle_name).is_none()); @@ -202,27 +208,4 @@ impl ForeignCallExecutor { } } } - - fn extract_mock_id( - foreign_call_inputs: &[ForeignCallParam], - ) -> Result<(usize, &[ForeignCallParam]), ForeignCallError> { - let (id, params) = - foreign_call_inputs.split_first().ok_or(ForeignCallError::MissingForeignCallInputs)?; - Ok((id.unwrap_value().to_usize(), params)) - } - - fn find_mock_by_id(&mut self, id: usize) -> Option<&mut MockedCall> { - self.mocked_responses.iter_mut().find(|response| response.id == id) - } - - fn parse_string(param: &ForeignCallParam) -> String { - let fields: Vec<_> = param.values().into_iter().map(|value| value.to_field()).collect(); - decode_string_value(&fields) - } - - fn execute_println(foreign_call_inputs: &[ForeignCallParam]) -> Result<(), NargoError> { - let display_values: PrintableValueDisplay = foreign_call_inputs.try_into()?; - println!("{display_values}"); - Ok(()) - } } diff --git a/tooling/nargo/src/ops/mod.rs b/tooling/nargo/src/ops/mod.rs index 491320482cf..34487ed9770 100644 --- a/tooling/nargo/src/ops/mod.rs +++ b/tooling/nargo/src/ops/mod.rs @@ -1,8 +1,10 @@ +pub use self::compile::{compile_program, compile_workspace}; pub use self::execute::execute_circuit; -pub use self::foreign_calls::ForeignCallExecutor; +pub use self::foreign_calls::{DefaultForeignCallExecutor, ForeignCallExecutor}; pub use self::optimize::{optimize_contract, optimize_program}; pub use self::test::{run_test, TestStatus}; +mod compile; mod execute; mod foreign_calls; mod optimize; diff --git a/tooling/nargo/src/ops/test.rs b/tooling/nargo/src/ops/test.rs index d2ef2659e4d..5bfdd6d15d0 100644 --- a/tooling/nargo/src/ops/test.rs +++ b/tooling/nargo/src/ops/test.rs @@ -6,7 +6,7 @@ use noirc_frontend::hir::{def_map::TestFunction, Context}; use crate::{errors::try_to_diagnose_runtime_error, NargoError}; -use super::execute_circuit; +use super::{execute_circuit, DefaultForeignCallExecutor}; pub enum TestStatus { Pass, @@ -26,8 +26,12 @@ pub fn run_test( Ok(program) => { // Run the backend to ensure the PWG evaluates functions like std::hash::pedersen, // otherwise constraints involving these expressions will not error. - let circuit_execution = - execute_circuit(blackbox_solver, &program.circuit, WitnessMap::new(), show_output); + let circuit_execution = execute_circuit( + &program.circuit, + WitnessMap::new(), + blackbox_solver, + &mut DefaultForeignCallExecutor::new(show_output), + ); test_status_program_compile_pass(test_function, program.debug, circuit_execution) } Err(err) => test_status_program_compile_fail(err, test_function), diff --git a/tooling/nargo/src/package.rs b/tooling/nargo/src/package.rs index 94c7c5b9c98..ecbf3585210 100644 --- a/tooling/nargo/src/package.rs +++ b/tooling/nargo/src/package.rs @@ -43,6 +43,7 @@ impl Dependency { #[derive(Clone)] pub struct Package { + pub version: Option, // A semver string which specifies the compiler version required to compile this package pub compiler_required_version: Option, pub root_dir: PathBuf, diff --git a/tooling/nargo_cli/Cargo.toml b/tooling/nargo_cli/Cargo.toml index 1a08514dc5f..07298ae25d2 100644 --- a/tooling/nargo_cli/Cargo.toml +++ b/tooling/nargo_cli/Cargo.toml @@ -43,6 +43,7 @@ tower.workspace = true async-lsp = { workspace = true, features = ["client-monitor", "stdio", "tracing", "tokio"] } const_format.workspace = true hex.workspace = true +similar-asserts.workspace = true termcolor = "1.1.2" color-eyre = "0.6.2" tokio = { version = "1.0", features = ["io-std"] } diff --git a/tooling/nargo_cli/build.rs b/tooling/nargo_cli/build.rs index 0d1acca6ee9..27a9b83d836 100644 --- a/tooling/nargo_cli/build.rs +++ b/tooling/nargo_cli/build.rs @@ -33,11 +33,11 @@ fn main() { // Try to find the directory that Cargo sets when it is running; otherwise fallback to assuming the CWD // is the root of the repository and append the crate path - let manifest_dir = match std::env::var("CARGO_MANIFEST_DIR") { - Ok(dir) => PathBuf::from(dir), - Err(_) => std::env::current_dir().unwrap().join("crates").join("nargo_cli"), + let root_dir = match std::env::var("CARGO_MANIFEST_DIR") { + Ok(dir) => PathBuf::from(dir).parent().unwrap().parent().unwrap().to_path_buf(), + Err(_) => std::env::current_dir().unwrap(), }; - let test_dir = manifest_dir.join("tests"); + let test_dir = root_dir.join("test_programs"); generate_execution_success_tests(&mut test_file, &test_dir); generate_noir_test_success_tests(&mut test_file, &test_dir); diff --git a/tooling/nargo_cli/src/cli/check_cmd.rs b/tooling/nargo_cli/src/cli/check_cmd.rs index 79f199381fa..57b36b8932b 100644 --- a/tooling/nargo_cli/src/cli/check_cmd.rs +++ b/tooling/nargo_cli/src/cli/check_cmd.rs @@ -1,9 +1,9 @@ use crate::backends::Backend; -use crate::errors::{CliError, CompileError}; +use crate::errors::CliError; use clap::Args; use iter_extended::btree_map; -use nargo::{package::Package, prepare_package}; +use nargo::{errors::CompileError, package::Package, prepare_package}; use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_abi::{AbiParameter, AbiType, MAIN_RETURN_NAME}; use noirc_driver::{ diff --git a/tooling/nargo_cli/src/cli/codegen_verifier_cmd.rs b/tooling/nargo_cli/src/cli/codegen_verifier_cmd.rs index 59143526b24..02c83adb59a 100644 --- a/tooling/nargo_cli/src/cli/codegen_verifier_cmd.rs +++ b/tooling/nargo_cli/src/cli/codegen_verifier_cmd.rs @@ -6,8 +6,8 @@ use super::{ use crate::backends::Backend; use crate::errors::CliError; -use acvm::acir::circuit::Opcode; use acvm::Language; +use backend_interface::BackendOpcodeSupport; use bb_abstraction_leaks::ACVM_BACKEND_BARRETENBERG; use clap::Args; use nargo::package::Package; @@ -54,7 +54,7 @@ pub(crate) fn run( package, &args.compile_options, np_language, - &|opcode| opcode_support.is_opcode_supported(opcode), + &opcode_support, )?; let contract_dir = workspace.contracts_directory_path(package); @@ -74,15 +74,10 @@ fn smart_contract_for_package( package: &Package, compile_options: &CompileOptions, np_language: Language, - is_opcode_supported: &impl Fn(&Opcode) -> bool, + opcode_support: &BackendOpcodeSupport, ) -> Result { - let program = compile_bin_package( - workspace, - package, - compile_options, - np_language, - &is_opcode_supported, - )?; + let program = + compile_bin_package(workspace, package, compile_options, np_language, opcode_support)?; let mut smart_contract_string = backend.eth_contract(&program.circuit)?; diff --git a/tooling/nargo_cli/src/cli/compile_cmd.rs b/tooling/nargo_cli/src/cli/compile_cmd.rs index 156359dab04..69533292bbd 100644 --- a/tooling/nargo_cli/src/cli/compile_cmd.rs +++ b/tooling/nargo_cli/src/cli/compile_cmd.rs @@ -10,6 +10,7 @@ use nargo::artifacts::contract::PreprocessedContract; use nargo::artifacts::contract::PreprocessedContractFunction; use nargo::artifacts::debug::DebugArtifact; use nargo::artifacts::program::PreprocessedProgram; +use nargo::errors::CompileError; use nargo::package::Package; use nargo::prepare_package; use nargo::workspace::Workspace; @@ -21,7 +22,7 @@ use noirc_frontend::graph::CrateName; use clap::Args; use crate::backends::Backend; -use crate::errors::{CliError, CompileError}; +use crate::errors::CliError; use super::fs::program::{ read_debug_artifact_from_file, read_program_from_file, save_contract_to_file, @@ -75,7 +76,7 @@ pub(crate) fn run( .cloned() .partition(|package| package.is_binary()); - let (np_language, opcode_support) = backend.get_backend_info()?; + let (np_language, opcode_support) = backend.get_backend_info_or_default(); let (_, compiled_contracts) = compile_workspace( &workspace, &binary_packages, @@ -101,12 +102,11 @@ pub(super) fn compile_workspace( opcode_support: &BackendOpcodeSupport, compile_options: &CompileOptions, ) -> Result<(Vec, Vec), CliError> { - let is_opcode_supported = |opcode: &_| opcode_support.is_opcode_supported(opcode); - // Compile all of the packages in parallel. let program_results: Vec<(FileManager, CompilationResult)> = binary_packages .par_iter() .map(|package| { + let is_opcode_supported = |opcode: &_| opcode_support.is_opcode_supported(opcode); compile_program(workspace, package, compile_options, np_language, &is_opcode_supported) }) .collect(); @@ -114,6 +114,7 @@ pub(super) fn compile_workspace( contract_packages .par_iter() .map(|package| { + let is_opcode_supported = |opcode: &_| opcode_support.is_opcode_supported(opcode); compile_contract(package, compile_options, np_language, &is_opcode_supported) }) .collect(); @@ -150,14 +151,16 @@ pub(crate) fn compile_bin_package( package: &Package, compile_options: &CompileOptions, np_language: Language, - is_opcode_supported: &impl Fn(&Opcode) -> bool, + opcode_support: &BackendOpcodeSupport, ) -> Result { if package.is_library() { return Err(CompileError::LibraryCrate(package.name.clone()).into()); } let (file_manager, compilation_result) = - compile_program(workspace, package, compile_options, np_language, &is_opcode_supported); + compile_program(workspace, package, compile_options, np_language, &|opcode| { + opcode_support.is_opcode_supported(opcode) + }); let program = report_errors( compilation_result, diff --git a/tooling/nargo_cli/src/cli/debug_cmd.rs b/tooling/nargo_cli/src/cli/debug_cmd.rs index 1d344058312..5204e0f122c 100644 --- a/tooling/nargo_cli/src/cli/debug_cmd.rs +++ b/tooling/nargo_cli/src/cli/debug_cmd.rs @@ -1,3 +1,5 @@ +use std::path::PathBuf; + use acvm::acir::native_types::WitnessMap; use clap::Args; @@ -56,32 +58,50 @@ pub(crate) fn run( return Ok(()); }; - let compiled_program = - compile_bin_package(&workspace, package, &args.compile_options, np_language, &|opcode| { - opcode_support.is_opcode_supported(opcode) - })?; + let compiled_program = compile_bin_package( + &workspace, + package, + &args.compile_options, + np_language, + &opcode_support, + )?; - println!("[{}] Starting debugger", package.name); - let (return_value, solved_witness) = - debug_program_and_decode(compiled_program, package, &args.prover_name)?; + run_async(package, compiled_program, &args.prover_name, &args.witness_name, target_dir) +} - if let Some(solved_witness) = solved_witness { - println!("[{}] Circuit witness successfully solved", package.name); +fn run_async( + package: &Package, + program: CompiledProgram, + prover_name: &str, + witness_name: &Option, + target_dir: &PathBuf, +) -> Result<(), CliError> { + use tokio::runtime::Builder; + let runtime = Builder::new_current_thread().enable_all().build().unwrap(); - if let Some(return_value) = return_value { - println!("[{}] Circuit output: {return_value:?}", package.name); - } + runtime.block_on(async { + println!("[{}] Starting debugger", package.name); + let (return_value, solved_witness) = + debug_program_and_decode(program, package, prover_name)?; + + if let Some(solved_witness) = solved_witness { + println!("[{}] Circuit witness successfully solved", package.name); - if let Some(witness_name) = &args.witness_name { - let witness_path = save_witness_to_dir(solved_witness, witness_name, target_dir)?; + if let Some(return_value) = return_value { + println!("[{}] Circuit output: {return_value:?}", package.name); + } - println!("[{}] Witness saved to {}", package.name, witness_path.display()); + if let Some(witness_name) = witness_name { + let witness_path = save_witness_to_dir(solved_witness, witness_name, target_dir)?; + + println!("[{}] Witness saved to {}", package.name, witness_path.display()); + } + } else { + println!("Debugger execution halted."); } - } else { - println!("Debugger execution halted."); - } - Ok(()) + Ok(()) + }) } fn debug_program_and_decode( diff --git a/tooling/nargo_cli/src/cli/execute_cmd.rs b/tooling/nargo_cli/src/cli/execute_cmd.rs index 3a0da2b1134..2f69b4c7df7 100644 --- a/tooling/nargo_cli/src/cli/execute_cmd.rs +++ b/tooling/nargo_cli/src/cli/execute_cmd.rs @@ -4,6 +4,7 @@ use clap::Args; use nargo::artifacts::debug::DebugArtifact; use nargo::constants::PROVER_INPUT_FILE; use nargo::errors::try_to_diagnose_runtime_error; +use nargo::ops::DefaultForeignCallExecutor; use nargo::package::Package; use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_abi::input_parser::{Format, InputValue}; @@ -55,14 +56,14 @@ pub(crate) fn run( )?; let target_dir = &workspace.target_directory_path(); - let (np_language, opcode_support) = backend.get_backend_info()?; + let (np_language, opcode_support) = backend.get_backend_info_or_default(); for package in &workspace { let compiled_program = compile_bin_package( &workspace, package, &args.compile_options, np_language, - &|opcode| opcode_support.is_opcode_supported(opcode), + &opcode_support, )?; let (return_value, solved_witness) = @@ -106,10 +107,10 @@ pub(crate) fn execute_program( let initial_witness = compiled_program.abi.encode(inputs_map, None)?; let solved_witness_err = nargo::ops::execute_circuit( - &blackbox_solver, &compiled_program.circuit, initial_witness, - true, + &blackbox_solver, + &mut DefaultForeignCallExecutor::new(true), ); match solved_witness_err { Ok(solved_witness) => Ok(solved_witness), diff --git a/tooling/nargo_cli/src/cli/fmt_cmd.rs b/tooling/nargo_cli/src/cli/fmt_cmd.rs index 42fe5a0d362..ec3d373a483 100644 --- a/tooling/nargo_cli/src/cli/fmt_cmd.rs +++ b/tooling/nargo_cli/src/cli/fmt_cmd.rs @@ -5,16 +5,23 @@ use fm::FileManager; use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_driver::NOIR_ARTIFACT_VERSION_STRING; use noirc_errors::CustomDiagnostic; -use noirc_frontend::hir::def_map::parse_file; +use noirc_frontend::{hir::def_map::parse_file, parser::ParserError}; use crate::errors::CliError; use super::NargoConfig; +/// Format the Noir files in a workspace #[derive(Debug, Clone, Args)] -pub(crate) struct FormatCommand {} +pub(crate) struct FormatCommand { + /// Run noirfmt in check mode + #[arg(long)] + check: bool, +} + +pub(crate) fn run(args: FormatCommand, config: NargoConfig) -> Result<(), CliError> { + let check_mode = args.check; -pub(crate) fn run(_args: FormatCommand, config: NargoConfig) -> Result<(), CliError> { let toml_path = get_package_manifest(&config.program_dir)?; let workspace = resolve_workspace_from_toml( &toml_path, @@ -25,6 +32,8 @@ pub(crate) fn run(_args: FormatCommand, config: NargoConfig) -> Result<(), CliEr let config = nargo_fmt::Config::read(&config.program_dir) .map_err(|err| CliError::Generic(err.to_string()))?; + let mut check_exit_code_one = false; + for package in &workspace { let mut file_manager = FileManager::new(&package.root_dir, Box::new(|path| std::fs::read_to_string(path))); @@ -33,7 +42,8 @@ pub(crate) fn run(_args: FormatCommand, config: NargoConfig) -> Result<(), CliEr let file_id = file_manager.add_file(&entry.path()).expect("file exists"); let (parsed_module, errors) = parse_file(&file_manager, file_id); - if !errors.is_empty() { + let is_all_warnings = errors.iter().all(ParserError::is_warning); + if !is_all_warnings { let errors = errors .into_iter() .map(|error| { @@ -51,16 +61,40 @@ pub(crate) fn run(_args: FormatCommand, config: NargoConfig) -> Result<(), CliEr return Ok(()); } - let source = nargo_fmt::format( - file_manager.fetch_file(file_id).source(), - parsed_module, - &config, - ); + let original = file_manager.fetch_file(file_id).source(); + let formatted = nargo_fmt::format(original, parsed_module, &config); + + if check_mode { + let diff = similar_asserts::SimpleDiff::from_str( + original, + &formatted, + "original", + "formatted", + ) + .to_string(); + + if !diff.lines().next().is_some_and(|line| line.contains("Invisible differences")) { + if !check_exit_code_one { + check_exit_code_one = true; + } - std::fs::write(entry.path(), source) + println!("{diff}"); + } + + Ok(()) + } else { + std::fs::write(entry.path(), formatted) + } }) .map_err(|error| CliError::Generic(error.to_string()))?; } + + if check_exit_code_one { + std::process::exit(1); + } else if check_mode { + println!("No formatting changes were detected"); + } + Ok(()) } diff --git a/tooling/nargo_cli/src/cli/info_cmd.rs b/tooling/nargo_cli/src/cli/info_cmd.rs index de0c63c3ab3..b0f771bfc1c 100644 --- a/tooling/nargo_cli/src/cli/info_cmd.rs +++ b/tooling/nargo_cli/src/cli/info_cmd.rs @@ -1,12 +1,15 @@ +use std::collections::HashMap; + use acvm::Language; use backend_interface::BackendError; use clap::Args; use iter_extended::vecmap; -use nargo::package::Package; +use nargo::{artifacts::debug::DebugArtifact, package::Package}; use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_driver::{ CompileOptions, CompiledContract, CompiledProgram, NOIR_ARTIFACT_VERSION_STRING, }; +use noirc_errors::{debug_info::OpCodesCount, Location}; use noirc_frontend::graph::CrateName; use prettytable::{row, table, Row}; use rayon::prelude::*; @@ -36,6 +39,9 @@ pub(crate) struct InfoCommand { #[clap(long, hide = true)] json: bool, + #[clap(long, hide = true)] + profile_info: bool, + #[clap(flatten)] compile_options: CompileOptions, } @@ -61,7 +67,7 @@ pub(crate) fn run( .cloned() .partition(|package| package.is_binary()); - let (np_language, opcode_support) = backend.get_backend_info()?; + let (np_language, opcode_support) = backend.get_backend_info_or_default(); let (compiled_programs, compiled_contracts) = compile_workspace( &workspace, &binary_packages, @@ -71,6 +77,23 @@ pub(crate) fn run( &args.compile_options, )?; + if args.profile_info { + for compiled_program in &compiled_programs { + let span_opcodes = compiled_program.debug.count_span_opcodes(); + let debug_artifact: DebugArtifact = compiled_program.clone().into(); + print_span_opcodes(span_opcodes, &debug_artifact); + } + + for compiled_contract in &compiled_contracts { + let debug_artifact: DebugArtifact = compiled_contract.clone().into(); + let functions = &compiled_contract.functions; + for contract_function in functions { + let span_opcodes = contract_function.debug.count_span_opcodes(); + print_span_opcodes(span_opcodes, &debug_artifact); + } + } + } + let program_info = binary_packages .into_par_iter() .zip(compiled_programs) @@ -121,6 +144,55 @@ pub(crate) fn run( Ok(()) } +/// Provides profiling information on +/// +/// Number of OpCodes in relation to Noir source file +/// and line number information +fn print_span_opcodes( + span_opcodes_map: HashMap, + debug_artifact: &DebugArtifact, +) { + let mut pairs: Vec<(&Location, &OpCodesCount)> = span_opcodes_map.iter().collect(); + + pairs.sort_by(|a, b| { + a.1.acir_size.cmp(&b.1.acir_size).then_with(|| a.1.brillig_size.cmp(&b.1.brillig_size)) + }); + + for (location, opcodes_count) in pairs { + let debug_file = debug_artifact.file_map.get(&location.file).unwrap(); + + let start_byte = byte_index(&debug_file.source, location.span.start() + 1); + let end_byte = byte_index(&debug_file.source, location.span.end() + 1); + let range = start_byte..end_byte; + let span_content = &debug_file.source[range]; + let line = debug_artifact.location_line_index(*location).unwrap() + 1; + println!( + "Ln. {}: {} (ACIR:{}, Brillig:{} opcode|s) in file: {}", + line, + span_content, + opcodes_count.acir_size, + opcodes_count.brillig_size, + debug_file.path.to_str().unwrap() + ); + } +} +fn byte_index(string: &str, index: u32) -> usize { + let mut byte_index = 0; + let mut char_index = 0; + + #[allow(clippy::explicit_counter_loop)] + for (byte_offset, _) in string.char_indices() { + if char_index == index { + return byte_index; + } + + byte_index = byte_offset; + char_index += 1; + } + + byte_index +} + #[derive(Debug, Default, Serialize)] struct InfoReport { programs: Vec, diff --git a/tooling/nargo_cli/src/cli/mod.rs b/tooling/nargo_cli/src/cli/mod.rs index 8d22fb1b204..88c6b57a98c 100644 --- a/tooling/nargo_cli/src/cli/mod.rs +++ b/tooling/nargo_cli/src/cli/mod.rs @@ -60,7 +60,6 @@ pub(crate) struct NargoConfig { enum NargoCommand { Backend(backend_cmd::BackendCommand), Check(check_cmd::CheckCommand), - #[command(hide = true)] // Hidden while the feature has not been extensively tested Fmt(fmt_cmd::FormatCommand), CodegenVerifier(codegen_verifier_cmd::CodegenVerifierCommand), #[command(alias = "build")] diff --git a/tooling/nargo_cli/src/cli/prove_cmd.rs b/tooling/nargo_cli/src/cli/prove_cmd.rs index 3586e73ff2e..54b148ec3a2 100644 --- a/tooling/nargo_cli/src/cli/prove_cmd.rs +++ b/tooling/nargo_cli/src/cli/prove_cmd.rs @@ -64,7 +64,7 @@ pub(crate) fn run( package, &args.compile_options, np_language, - &|opcode| opcode_support.is_opcode_supported(opcode), + &opcode_support, )?; prove_package( diff --git a/tooling/nargo_cli/src/cli/verify_cmd.rs b/tooling/nargo_cli/src/cli/verify_cmd.rs index 8c6d92b3d2f..2f8a6efbba4 100644 --- a/tooling/nargo_cli/src/cli/verify_cmd.rs +++ b/tooling/nargo_cli/src/cli/verify_cmd.rs @@ -55,7 +55,7 @@ pub(crate) fn run( package, &args.compile_options, np_language, - &|opcode| opcode_support.is_opcode_supported(opcode), + &opcode_support, )?; verify_package(backend, &workspace, package, program, &args.verifier_name)?; diff --git a/tooling/nargo_cli/src/errors.rs b/tooling/nargo_cli/src/errors.rs index b73a7888f32..92da74c71d4 100644 --- a/tooling/nargo_cli/src/errors.rs +++ b/tooling/nargo_cli/src/errors.rs @@ -1,10 +1,8 @@ use acvm::acir::native_types::WitnessMapError; use hex::FromHexError; -use nargo::NargoError; +use nargo::{errors::CompileError, NargoError}; use nargo_toml::ManifestError; use noirc_abi::errors::{AbiError, InputParserError}; -use noirc_errors::reporter::ReportedErrors; -use noirc_frontend::graph::CrateName; use std::path::PathBuf; use thiserror::Error; @@ -87,24 +85,3 @@ pub(crate) enum BackendError { #[error("Backend installation failed: {0}")] InstallationError(#[from] std::io::Error), } - -/// Errors covering situations where a package cannot be compiled. -#[derive(Debug, Error)] -pub(crate) enum CompileError { - #[error("Package `{0}` has type `lib` but only `bin` types can be compiled")] - LibraryCrate(CrateName), - - #[error("Package `{0}` is expected to have a `main` function but it does not")] - MissingMainFunction(CrateName), - - /// Errors encountered while compiling the Noir program. - /// These errors are already written to stderr. - #[error("Aborting due to {} previous error{}", .0.error_count, if .0.error_count == 1 { "" } else { "s" })] - ReportedErrors(ReportedErrors), -} - -impl From for CompileError { - fn from(errors: ReportedErrors) -> Self { - Self::ReportedErrors(errors) - } -} diff --git a/tooling/nargo_cli/tests/acir_artifacts/1_mul/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/1_mul/target/acir.gz deleted file mode 100644 index 30f01fbe4c9..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/1_mul/target/acir.gz and /dev/null differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/1_mul/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/1_mul/target/witness.gz deleted file mode 100644 index 0cb3b023bd8..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/1_mul/target/witness.gz and /dev/null differ 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 deleted file mode 100644 index 8402fc06345..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/2_div/target/acir.gz and /dev/null differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/2_div/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/2_div/target/witness.gz deleted file mode 100644 index a96d45a78c9..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/2_div/target/witness.gz and /dev/null 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 deleted file mode 100644 index ea19ef263ab..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/3_add/target/acir.gz and /dev/null 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 deleted file mode 100644 index 456b0b85764..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/6_array/target/acir.gz and /dev/null 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 deleted file mode 100644 index e299cd32775..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/6_array/target/witness.gz and /dev/null 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 deleted file mode 100644 index c4d16bae8d0..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/7_function/target/acir.gz and /dev/null 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 deleted file mode 100644 index d2fc717b0fa..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/7_function/target/witness.gz and /dev/null 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 deleted file mode 100644 index 0da270ceb11..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/array_dynamic/target/acir.gz and /dev/null 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 deleted file mode 100644 index 2a158985525..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/array_dynamic/target/witness.gz and /dev/null 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 deleted file mode 100644 index 85b3d90ff80..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/bit_shifts_comptime/target/acir.gz and /dev/null 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 deleted file mode 100644 index 890e987dd74..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/bit_shifts_comptime/target/witness.gz and /dev/null 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 deleted file mode 100644 index b8c81ed3207..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/bit_shifts_runtime/target/acir.gz and /dev/null 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 deleted file mode 100644 index 1e562739c41..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/bit_shifts_runtime/target/witness.gz and /dev/null 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 deleted file mode 100644 index 3c5c89d2c22..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_acir_as_brillig/target/acir.gz and /dev/null differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_arrays/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/brillig_arrays/target/acir.gz deleted file mode 100644 index a093703d4b3..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_arrays/target/acir.gz and /dev/null 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 deleted file mode 100644 index 2cf3b7251e6..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_assert/target/acir.gz and /dev/null differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_blake2s/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/brillig_blake2s/target/acir.gz deleted file mode 100644 index be47506c42f..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_blake2s/target/acir.gz and /dev/null 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 deleted file mode 100644 index cd9a2a527dd..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_calls/target/acir.gz and /dev/null differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_calls_array/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/brillig_calls_array/target/acir.gz deleted file mode 100644 index 59b89c22bc3..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_calls_array/target/acir.gz and /dev/null differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_calls_conditionals/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/brillig_calls_conditionals/target/acir.gz deleted file mode 100644 index e4155f58ead..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_calls_conditionals/target/acir.gz and /dev/null differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_conditional/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/brillig_conditional/target/acir.gz deleted file mode 100644 index 9117511d800..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_conditional/target/acir.gz and /dev/null differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_ecdsa/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/brillig_ecdsa/target/acir.gz deleted file mode 100644 index cdc28517544..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_ecdsa/target/acir.gz and /dev/null 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 deleted file mode 100644 index dc8f2962444..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_fns_as_values/target/acir.gz and /dev/null differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_hash_to_field/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/brillig_hash_to_field/target/acir.gz deleted file mode 100644 index 73c742a2dd1..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_hash_to_field/target/acir.gz and /dev/null differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_identity_function/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/brillig_identity_function/target/acir.gz deleted file mode 100644 index 4e17ecc5d7b..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_identity_function/target/acir.gz and /dev/null 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 deleted file mode 100644 index 1a64fd03980..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_keccak/target/acir.gz and /dev/null differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_nested_arrays/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/brillig_nested_arrays/target/acir.gz deleted file mode 100644 index f69df4781ec..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_nested_arrays/target/acir.gz and /dev/null differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_nested_slices/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/brillig_nested_slices/target/acir.gz deleted file mode 100644 index 100a208bcd8..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_nested_slices/target/acir.gz and /dev/null 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 deleted file mode 100644 index 9702ca340a5..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_not/target/acir.gz and /dev/null differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_oracle/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/brillig_oracle/target/acir.gz deleted file mode 100644 index db158f61882..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_oracle/target/acir.gz and /dev/null differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_oracle/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/brillig_oracle/target/witness.gz deleted file mode 100644 index 3fead7f6b2e..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_oracle/target/witness.gz and /dev/null 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 deleted file mode 100644 index 27f6f353d25..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_pedersen/target/acir.gz and /dev/null differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_recursion/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/brillig_recursion/target/acir.gz deleted file mode 100644 index c0c91d81546..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_recursion/target/acir.gz and /dev/null differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_references/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/brillig_references/target/acir.gz deleted file mode 100644 index 4069ca2cb5e..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_references/target/acir.gz and /dev/null 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 deleted file mode 100644 index 9f5f787c655..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_scalar_mul/target/acir.gz and /dev/null differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_schnorr/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/brillig_schnorr/target/acir.gz deleted file mode 100644 index 625ae64a11d..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_schnorr/target/acir.gz and /dev/null differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_sha256/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/brillig_sha256/target/acir.gz deleted file mode 100644 index 49f7e6afcf4..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_sha256/target/acir.gz and /dev/null differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_slices/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/brillig_slices/target/acir.gz deleted file mode 100644 index 57ca1d59fed..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_slices/target/acir.gz and /dev/null differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_to_be_bytes/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/brillig_to_be_bytes/target/acir.gz deleted file mode 100644 index 1249975b27c..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_to_be_bytes/target/acir.gz and /dev/null differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_to_bytes_integration/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/brillig_to_bytes_integration/target/acir.gz deleted file mode 100644 index fe9ac34ed17..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_to_bytes_integration/target/acir.gz and /dev/null differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_to_le_bytes/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/brillig_to_le_bytes/target/acir.gz deleted file mode 100644 index 834f57e1ee7..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_to_le_bytes/target/acir.gz and /dev/null differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_top_level/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/brillig_top_level/target/acir.gz deleted file mode 100644 index 4b2fbcd3462..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_top_level/target/acir.gz and /dev/null 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 deleted file mode 100644 index ac18684a07e..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_unitialised_arrays/target/acir.gz and /dev/null 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 deleted file mode 100644 index ceaf7ad008e..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_unitialised_arrays/target/witness.gz and /dev/null 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 deleted file mode 100644 index 4c4f03e29e2..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/conditional_1/target/acir.gz and /dev/null 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 deleted file mode 100644 index 32aeab751fc..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/conditional_1/target/witness.gz and /dev/null 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 deleted file mode 100644 index d5236f6b685..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/conditional_regression_661/target/acir.gz and /dev/null differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/conditional_regression_661/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/conditional_regression_661/target/witness.gz deleted file mode 100644 index 0a87702a8b0..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/conditional_regression_661/target/witness.gz and /dev/null differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/debug_logs/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/debug_logs/target/acir.gz deleted file mode 100644 index ea9187f4084..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/debug_logs/target/acir.gz and /dev/null 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 deleted file mode 100644 index cec0caab082..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/eddsa/target/acir.gz and /dev/null 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 deleted file mode 100644 index 477da9a8739..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/eddsa/target/witness.gz and /dev/null 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 deleted file mode 100644 index b8d272e1271..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/hash_to_field/target/acir.gz and /dev/null differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/hash_to_field/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/hash_to_field/target/witness.gz deleted file mode 100644 index cbead933c40..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/hash_to_field/target/witness.gz and /dev/null 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 deleted file mode 100644 index 09c3efbdf85..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/merkle_insert/target/acir.gz and /dev/null 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 deleted file mode 100644 index f1625f0c2cf..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/merkle_insert/target/witness.gz and /dev/null differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/mock_oracle/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/mock_oracle/target/acir.gz deleted file mode 100644 index c45cd40f28e..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/mock_oracle/target/acir.gz and /dev/null 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 deleted file mode 100644 index 9f518522755..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/nested_array_dynamic/target/acir.gz and /dev/null 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 deleted file mode 100644 index 62a1378dece..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/nested_array_dynamic/target/witness.gz and /dev/null differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/nested_arrays_from_brillig/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/nested_arrays_from_brillig/target/acir.gz deleted file mode 100644 index 270cfcaf53c..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/nested_arrays_from_brillig/target/acir.gz and /dev/null 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 deleted file mode 100644 index 4ce69feb82a..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/regression/target/acir.gz and /dev/null 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 deleted file mode 100644 index 7de138bb05e..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/regression/target/witness.gz and /dev/null 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 deleted file mode 100644 index 5c0339446c5..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/regression_mem_op_predicate/target/acir.gz and /dev/null 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 deleted file mode 100644 index 095aef252ee..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/regression_mem_op_predicate/target/witness.gz and /dev/null differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/regression_method_cannot_be_found/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/regression_method_cannot_be_found/target/acir.gz deleted file mode 100644 index f29fbef8d3f..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/regression_method_cannot_be_found/target/acir.gz and /dev/null 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 deleted file mode 100644 index d1d827365c3..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/schnorr/target/acir.gz and /dev/null 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 deleted file mode 100644 index 64537ff7375..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/schnorr/target/witness.gz and /dev/null 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 deleted file mode 100644 index 3d0d312ded9..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/sha2_byte/target/acir.gz and /dev/null 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 deleted file mode 100644 index 6d885c84c21..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/sha2_byte/target/witness.gz and /dev/null 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 deleted file mode 100644 index da0a21a47db..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/signed_arithmetic/target/acir.gz and /dev/null 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 deleted file mode 100644 index c7113d46d66..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/signed_arithmetic/target/witness.gz and /dev/null 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 deleted file mode 100644 index d55e2e851c0..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/simple_bitwise/target/acir.gz and /dev/null differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/simple_bitwise/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/simple_bitwise/target/witness.gz deleted file mode 100644 index 0319e8c0062..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/simple_bitwise/target/witness.gz and /dev/null differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/simple_print/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/simple_print/target/acir.gz deleted file mode 100644 index 5a2c524f26b..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/simple_print/target/acir.gz and /dev/null 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 deleted file mode 100644 index e99ba23f4b4..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/simple_radix/target/acir.gz and /dev/null 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 deleted file mode 100644 index 20064d105da..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/simple_radix/target/witness.gz and /dev/null 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 deleted file mode 100644 index f0edce7ee23..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/simple_shield/target/acir.gz and /dev/null 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 deleted file mode 100644 index f8b24dec230..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/simple_shield/target/witness.gz and /dev/null differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/simple_shift_left_right/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/simple_shift_left_right/target/acir.gz deleted file mode 100644 index 32bff4f6bab..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/simple_shift_left_right/target/acir.gz and /dev/null differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/simple_shift_left_right/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/simple_shift_left_right/target/witness.gz deleted file mode 100644 index 6f15adc6525..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/simple_shift_left_right/target/witness.gz and /dev/null 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 deleted file mode 100644 index 509c889e23c..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/slice_dynamic_index/target/acir.gz and /dev/null 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 deleted file mode 100644 index 2e1d2a604c6..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/slice_dynamic_index/target/witness.gz and /dev/null differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/slice_struct_field/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/slice_struct_field/target/acir.gz deleted file mode 100644 index 760f1e01a80..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/slice_struct_field/target/acir.gz and /dev/null differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/slice_struct_field/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/slice_struct_field/target/witness.gz deleted file mode 100644 index 6a62419c931..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/slice_struct_field/target/witness.gz and /dev/null differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/strings/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/strings/target/acir.gz deleted file mode 100644 index 424f4bd2d0f..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/strings/target/acir.gz and /dev/null 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 deleted file mode 100644 index d7de6222904..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/to_be_bytes/target/acir.gz and /dev/null 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 deleted file mode 100644 index 9027dae5645..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/to_be_bytes/target/witness.gz and /dev/null 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 deleted file mode 100644 index d893d6f55c9..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/to_bytes_consistent/target/acir.gz and /dev/null 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 deleted file mode 100644 index 9313438a5ff..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/to_bytes_consistent/target/witness.gz and /dev/null 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 deleted file mode 100644 index b47dd38d6b3..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/to_bytes_integration/target/acir.gz and /dev/null 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 deleted file mode 100644 index 617301d310b..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/to_bytes_integration/target/witness.gz and /dev/null 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 deleted file mode 100644 index 1b7d71c53ff..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/to_le_bytes/target/acir.gz and /dev/null 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 deleted file mode 100644 index b27845dc122..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/to_le_bytes/target/witness.gz and /dev/null differ diff --git a/tooling/nargo_cli/tests/compile_failure/assert_eq_struct/src/main.nr b/tooling/nargo_cli/tests/compile_failure/assert_eq_struct/src/main.nr deleted file mode 100644 index c2eac091733..00000000000 --- a/tooling/nargo_cli/tests/compile_failure/assert_eq_struct/src/main.nr +++ /dev/null @@ -1,6 +0,0 @@ -struct myStruct {} - -// `assert_eq` should not allow asserting equality between types for which `==` is not defined. -fn main(x : myStruct, y : pub myStruct) { - assert_eq(x, y); -} 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 deleted file mode 100644 index e9379a29876..00000000000 --- a/tooling/nargo_cli/tests/compile_failure/brillig_assert_fail/src/main.nr +++ /dev/null @@ -1,11 +0,0 @@ -// Tests a very simple program. -// -// The features being tested is using assert on brillig -fn main(x: Field) { - assert(1 == conditional(x as bool)); -} - -unconstrained fn conditional(x : bool) -> Field { - assert(x); - 1 -} diff --git a/tooling/nargo_cli/tests/compile_failure/dep_impl_primitive/src/main.nr b/tooling/nargo_cli/tests/compile_failure/dep_impl_primitive/src/main.nr deleted file mode 100644 index ff9a40da18e..00000000000 --- a/tooling/nargo_cli/tests/compile_failure/dep_impl_primitive/src/main.nr +++ /dev/null @@ -1,5 +0,0 @@ -use dep::bad_impl; - -fn main(x : Field) { - x.something(); -} diff --git a/tooling/nargo_cli/tests/compile_failure/div_by_zero_witness/src/main.nr b/tooling/nargo_cli/tests/compile_failure/div_by_zero_witness/src/main.nr deleted file mode 100644 index 4ce567e49a6..00000000000 --- a/tooling/nargo_cli/tests/compile_failure/div_by_zero_witness/src/main.nr +++ /dev/null @@ -1,7 +0,0 @@ -use dep::std; - -// It is expected that `y` must be equal to 0. -fn main(x : Field, y : pub Field) { - let a: Field = x / y; - std::println(a); -} diff --git a/tooling/nargo_cli/tests/compile_failure/dup_trait_implementation_4/src/module3.nr b/tooling/nargo_cli/tests/compile_failure/dup_trait_implementation_4/src/module3.nr deleted file mode 100644 index a7612345cf1..00000000000 --- a/tooling/nargo_cli/tests/compile_failure/dup_trait_implementation_4/src/module3.nr +++ /dev/null @@ -1,7 +0,0 @@ -use crate::module1::MyTrait; -use crate::module2::MyStruct; - -// those are not the same 'Path', but they refer to the same trait & impl -// so a Duplicate error should be thrown -impl MyTrait for MyStruct {} -impl crate::module1::MyTrait for crate::module2::MyStruct { } diff --git a/tooling/nargo_cli/tests/compile_failure/dup_trait_items_1/src/main.nr b/tooling/nargo_cli/tests/compile_failure/dup_trait_items_1/src/main.nr deleted file mode 100644 index 9055d6e3998..00000000000 --- a/tooling/nargo_cli/tests/compile_failure/dup_trait_items_1/src/main.nr +++ /dev/null @@ -1,7 +0,0 @@ -trait MyTrait { - fn SomeFunc(); - fn SomeFunc(); -} - -fn main() { -} diff --git a/tooling/nargo_cli/tests/compile_failure/dup_trait_items_2/src/main.nr b/tooling/nargo_cli/tests/compile_failure/dup_trait_items_2/src/main.nr deleted file mode 100644 index 312b1e5a9d4..00000000000 --- a/tooling/nargo_cli/tests/compile_failure/dup_trait_items_2/src/main.nr +++ /dev/null @@ -1,7 +0,0 @@ -trait MyTrait { - let SomeConst: u32; - let SomeConst: Field; -} - -fn main() { -} diff --git a/tooling/nargo_cli/tests/compile_failure/dup_trait_items_3/src/main.nr b/tooling/nargo_cli/tests/compile_failure/dup_trait_items_3/src/main.nr deleted file mode 100644 index ca97a9a143d..00000000000 --- a/tooling/nargo_cli/tests/compile_failure/dup_trait_items_3/src/main.nr +++ /dev/null @@ -1,7 +0,0 @@ -trait MyTrait { - type SomeType; - type SomeType; -} - -fn main() { -} diff --git a/tooling/nargo_cli/tests/compile_failure/dup_trait_items_4/src/main.nr b/tooling/nargo_cli/tests/compile_failure/dup_trait_items_4/src/main.nr deleted file mode 100644 index da0532e39c1..00000000000 --- a/tooling/nargo_cli/tests/compile_failure/dup_trait_items_4/src/main.nr +++ /dev/null @@ -1,7 +0,0 @@ -trait MyTrait { - let MyItem: u32; - fn MyItem(); -} - -fn main() { -} diff --git a/tooling/nargo_cli/tests/compile_failure/dup_trait_items_5/src/main.nr b/tooling/nargo_cli/tests/compile_failure/dup_trait_items_5/src/main.nr deleted file mode 100644 index 4881a338a84..00000000000 --- a/tooling/nargo_cli/tests/compile_failure/dup_trait_items_5/src/main.nr +++ /dev/null @@ -1,7 +0,0 @@ -trait MyTrait { - fn MyItem(); - let MyItem: u32; -} - -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 deleted file mode 100644 index 4803f2005c9..00000000000 --- a/tooling/nargo_cli/tests/compile_failure/duplicate_declaration/src/main.nr +++ /dev/null @@ -1,8 +0,0 @@ -// Duplicate functions should not compile -fn hello(x : Field) -> Field { - x -} - -fn hello(x : Field) -> Field { - x -} 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 deleted file mode 100644 index 9f2ae3a0618..00000000000 --- a/tooling/nargo_cli/tests/compile_failure/dyn_index_fail_nested_array/src/main.nr +++ /dev/null @@ -1,8 +0,0 @@ -struct Foo { - a: Field, - b: Field, -} - -fn main(mut x : [Foo; 3], y : pub Field) { - assert(x[y + 2].a == 5); -} 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 deleted file mode 100644 index 10d4d1f02d1..00000000000 --- a/tooling/nargo_cli/tests/compile_failure/dynamic_index_failure/src/main.nr +++ /dev/null @@ -1,23 +0,0 @@ -fn main(mut x: [u32; 5], z: Field) { - let idx = z + 10; - - x[z] = 4; - - // Dynamic index is greater than length of the array - assert(x[idx] != 0); - - // TODO(#2133): Provide more accurate call stacks for arrays merged in if statements - // if z != 20 { - // x[0] = x[4]; - // } else { - // // TODO: Dynamic predicate still gives index out of bounds error - // if idx as u32 < 3 { - // x[idx] = 10; - // } - // x[idx] = 10; - // for i in 0..5 { - // x[idx] = x[i]; - // } - // } - // assert(x[idx] != 0); -} 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 deleted file mode 100644 index ab1cb457fee..00000000000 --- a/tooling/nargo_cli/tests/compile_failure/integer_literal_overflow/src/main.nr +++ /dev/null @@ -1,6 +0,0 @@ - -fn main() { - foo(1234) -} - -fn foo(_x: u4) {} diff --git a/tooling/nargo_cli/tests/compile_failure/multiple_contracts/src/main.nr b/tooling/nargo_cli/tests/compile_failure/multiple_contracts/src/main.nr deleted file mode 100644 index 0562ca9ccd5..00000000000 --- a/tooling/nargo_cli/tests/compile_failure/multiple_contracts/src/main.nr +++ /dev/null @@ -1,4 +0,0 @@ -contract Foo {} - - -contract Bar {} diff --git a/tooling/nargo_cli/tests/compile_failure/no_nested_impl/src/main.nr b/tooling/nargo_cli/tests/compile_failure/no_nested_impl/src/main.nr deleted file mode 100644 index 97ab248740a..00000000000 --- a/tooling/nargo_cli/tests/compile_failure/no_nested_impl/src/main.nr +++ /dev/null @@ -1,22 +0,0 @@ -fn main() { - let a: [[[[Field; 2]; 2]; 2]; 2] = [[[[1, 2], [3, 4]], [[5, 6], [7, 8]]], [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]]; - assert(a.eq(a)); -} - -trait Eq { - fn eq(self, other: Self) -> bool; -} - -impl Eq for [T; 2] where T: Eq { - fn eq(self, other: Self) -> bool { - self[0].eq(other[0]) - & self[0].eq(other[0]) - } -} - -// Impl for u32 but not Field -impl Eq for u32 { - fn eq(self, other: Self) -> bool { - self == other - } -} diff --git a/tooling/nargo_cli/tests/compile_failure/orphaned_trait_impl/src/main.nr b/tooling/nargo_cli/tests/compile_failure/orphaned_trait_impl/src/main.nr deleted file mode 100644 index d245bd68ea1..00000000000 --- a/tooling/nargo_cli/tests/compile_failure/orphaned_trait_impl/src/main.nr +++ /dev/null @@ -1,6 +0,0 @@ -impl dep::crate1::MyTrait for dep::crate2::MyStruct { -} - -fn main(x : Field, y : pub Field) { - assert(x != y); -} 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 deleted file mode 100644 index abf905ad71b..00000000000 --- a/tooling/nargo_cli/tests/compile_failure/overlapping_generic_impls/src/main.nr +++ /dev/null @@ -1,7 +0,0 @@ - -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/radix_non_constant_length/src/main.nr b/tooling/nargo_cli/tests/compile_failure/radix_non_constant_length/src/main.nr deleted file mode 100644 index adfbd265a1d..00000000000 --- a/tooling/nargo_cli/tests/compile_failure/radix_non_constant_length/src/main.nr +++ /dev/null @@ -1,4 +0,0 @@ -fn main(x : Field, y : pub u32) { - let bytes = x.to_be_bytes(y); - assert(bytes[0] == 0); -} diff --git a/tooling/nargo_cli/tests/compile_failure/slice_access_failure/src/main.nr b/tooling/nargo_cli/tests/compile_failure/slice_access_failure/src/main.nr deleted file mode 100644 index dc651cd514d..00000000000 --- a/tooling/nargo_cli/tests/compile_failure/slice_access_failure/src/main.nr +++ /dev/null @@ -1,13 +0,0 @@ -fn main(x : Field, y : pub Field) { - let mut slice = [0; 2]; - if x == y { - slice = slice.push_back(y); - slice = slice.push_back(x); - } else { - slice = slice.push_back(x); - } - // This constraint should fail as the slice length is 3 and the index is 3 - // The right hand side AND case ensures that the circuit inputs have not changed - // and we always hit the else case in the if statement above. - assert((slice[3] == 0) & (slice[2] != y)); -} 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 deleted file mode 100644 index 72918037c16..00000000000 --- a/tooling/nargo_cli/tests/compile_failure/slice_insert_failure/src/main.nr +++ /dev/null @@ -1,11 +0,0 @@ -fn main(x : Field, y : pub Field) { - let mut slice = [0; 2]; - if x == y { - slice = slice.push_back(y); - slice = slice.push_back(x); - } else { - slice = slice.push_back(x); - } - - slice = slice.insert(10, 100); -} 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 deleted file mode 100644 index 1cd335f4b34..00000000000 --- a/tooling/nargo_cli/tests/compile_failure/slice_remove_failure/src/main.nr +++ /dev/null @@ -1,11 +0,0 @@ -fn main(x : Field, y : pub Field) { - let mut slice = [0; 2]; - if x == y { - slice = slice.push_back(y); - slice = slice.push_back(x); - } else { - slice = slice.push_back(x); - } - - let (removed_slice, removed_elem) = slice.remove(10); -} diff --git a/tooling/nargo_cli/tests/compile_failure/trait_incorrect_generic_count/src/main.nr b/tooling/nargo_cli/tests/compile_failure/trait_incorrect_generic_count/src/main.nr deleted file mode 100644 index 702c3153717..00000000000 --- a/tooling/nargo_cli/tests/compile_failure/trait_incorrect_generic_count/src/main.nr +++ /dev/null @@ -1,13 +0,0 @@ - -fn main(){ - let x: u32 = 0; - x.trait_fn(); -} - -trait Trait { - fn trait_fn(x: T) -> T {} -} - -impl Trait for u32 { - fn trait_fn(x: A) -> A { x } -} diff --git a/tooling/nargo_cli/tests/compile_failure/workspace_fail/crates/a/src/main.nr b/tooling/nargo_cli/tests/compile_failure/workspace_fail/crates/a/src/main.nr deleted file mode 100644 index 550e5034a7b..00000000000 --- a/tooling/nargo_cli/tests/compile_failure/workspace_fail/crates/a/src/main.nr +++ /dev/null @@ -1,3 +0,0 @@ -fn main(x : Field, y : pub Field) { - assert(x == y); -} diff --git a/tooling/nargo_cli/tests/compile_failure/workspace_fail/crates/b/src/main.nr b/tooling/nargo_cli/tests/compile_failure/workspace_fail/crates/b/src/main.nr deleted file mode 100644 index 6e170de75fc..00000000000 --- a/tooling/nargo_cli/tests/compile_failure/workspace_fail/crates/b/src/main.nr +++ /dev/null @@ -1,3 +0,0 @@ -fn main(x : Field, y : pub Field) { - assert(x != y); -} diff --git a/tooling/nargo_cli/tests/compile_failure/workspace_missing_toml/crates/a/src/main.nr b/tooling/nargo_cli/tests/compile_failure/workspace_missing_toml/crates/a/src/main.nr deleted file mode 100644 index 550e5034a7b..00000000000 --- a/tooling/nargo_cli/tests/compile_failure/workspace_missing_toml/crates/a/src/main.nr +++ /dev/null @@ -1,3 +0,0 @@ -fn main(x : Field, y : pub Field) { - assert(x == y); -} diff --git a/tooling/nargo_cli/tests/compile_failure/workspace_missing_toml/crates/b/src/main.nr b/tooling/nargo_cli/tests/compile_failure/workspace_missing_toml/crates/b/src/main.nr deleted file mode 100644 index 6e170de75fc..00000000000 --- a/tooling/nargo_cli/tests/compile_failure/workspace_missing_toml/crates/b/src/main.nr +++ /dev/null @@ -1,3 +0,0 @@ -fn main(x : Field, y : pub Field) { - assert(x != y); -} 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 deleted file mode 100644 index fa04e0d3e7b..00000000000 --- a/tooling/nargo_cli/tests/compile_success_contract/contract_with_impl/src/main.nr +++ /dev/null @@ -1,8 +0,0 @@ - -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 deleted file mode 100644 index f1b739e4b88..00000000000 --- a/tooling/nargo_cli/tests/compile_success_contract/non_entry_point_method/src/main.nr +++ /dev/null @@ -1,6 +0,0 @@ -contract Foo { - struct PlaceholderStruct{x : u32 } - - #[contract_library_method] - fn has_mut(_context : &mut PlaceholderStruct) {} -} diff --git a/tooling/nargo_cli/tests/compile_success_contract/simple_contract/src/main.nr b/tooling/nargo_cli/tests/compile_success_contract/simple_contract/src/main.nr deleted file mode 100644 index dd762ca3d21..00000000000 --- a/tooling/nargo_cli/tests/compile_success_contract/simple_contract/src/main.nr +++ /dev/null @@ -1,6 +0,0 @@ -contract Foo { - fn double(x: Field) -> pub Field { x * 2 } - fn triple(x: Field) -> pub Field { x * 3 } - internal fn quadruple(x: Field) -> pub Field { x * 4 } - open internal fn skibbidy(x: Field) -> pub Field { x * 5 } -} diff --git a/tooling/nargo_cli/tests/compile_success_empty/brillig_cast/src/main.nr b/tooling/nargo_cli/tests/compile_success_empty/brillig_cast/src/main.nr deleted file mode 100644 index e258a8f2640..00000000000 --- a/tooling/nargo_cli/tests/compile_success_empty/brillig_cast/src/main.nr +++ /dev/null @@ -1,50 +0,0 @@ -// Tests a very simple Brillig function. -// -// The features being tested are cast operations on brillig -fn main() { - bool_casts(); - field_casts(); - uint_casts(); - int_casts(); - mixed_casts(); -} - -unconstrained fn bool_casts() { - assert(false == 0 as bool); - assert(true == 1 as bool); - assert(true == 3 as bool); -} - -unconstrained fn field_casts() { - assert(5 as u8 as Field == 5); - assert(16 as u4 as Field == 0); -} - -unconstrained fn uint_casts() { - let x: u32 = 100; - assert(x as u2 == 0); - assert(x as u4 == 4); - assert(x as u6 == 36); - assert(x as u8 == 100); - assert(x as u64 == 100); - assert(x as u126 == 100); -} - -unconstrained fn int_casts() { - let x: i32 = 100; - assert(x as i2 == 0); - assert(x as i4 == 4); - assert(x as i6 == -28 as i6); - assert(x as i8 == 100); - assert(x as i8 == 100); - assert(x as i8 == 100); -} - - -unconstrained fn mixed_casts() { - assert(100 as u32 as i32 as u32 == 100); - assert(13 as u4 as i2 as u32 == 1); - assert(15 as u4 as i2 as u32 == 3); - assert(1 as u8 as bool == true); - assert(true as i8 == 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 deleted file mode 100644 index 85002cef01d..00000000000 --- a/tooling/nargo_cli/tests/compile_success_empty/brillig_field_binary_operations/src/main.nr +++ /dev/null @@ -1,25 +0,0 @@ -// Tests arithmetic operations on fields -fn main() { - let x = 4; - let y = 2; - assert((x + y) == add(x, y)); - assert((x - y) == sub(x, y)); - assert((x * y) == mul(x, y)); - assert((x / y) == div(x, y)); -} - -unconstrained fn add(x : Field, y : Field) -> Field { - x + y -} - -unconstrained fn sub(x : Field, y : Field) -> Field { - x - y -} - -unconstrained fn mul(x : Field, y : Field) -> Field { - x * y -} - -unconstrained fn div(x : Field, y : Field) -> Field { - x / y -} diff --git a/tooling/nargo_cli/tests/compile_success_empty/brillig_integer_binary_operations/src/main.nr b/tooling/nargo_cli/tests/compile_success_empty/brillig_integer_binary_operations/src/main.nr deleted file mode 100644 index 72f614f1e63..00000000000 --- a/tooling/nargo_cli/tests/compile_success_empty/brillig_integer_binary_operations/src/main.nr +++ /dev/null @@ -1,80 +0,0 @@ -// Tests arithmetic operations on integers -fn main() { - let x: u32 = 6; - let y: u32 = 2; - - assert((x + y) == add(x, y)); - - assert((x - y) == sub(x, y)); - - assert((x * y) == mul(x, y)); - - assert((x / y) == div(x, y)); - - // TODO SSA => ACIR has some issues with i32 ops - assert(check_signed_div(6, 2, 3)); - - assert(eq(1, 2) == false); - assert(eq(1, 1)); - - assert(lt(x, y) == false); - assert(lt(y, x)); - - assert((x & y) == and(x, y)); - assert((x | y) == or(x, y)); - - // TODO SSA => ACIR has some issues with xor ops - - assert(check_xor(x, y, 4)); - assert((x >> y) == shr(x, y)); - assert((x << y) == shl(x, y)); -} - -unconstrained fn add(x : u32, y : u32) -> u32 { - x + y -} - -unconstrained fn sub(x : u32, y : u32) -> u32 { - x - y -} - -unconstrained fn mul(x : u32, y : u32) -> u32 { - x * y -} - -unconstrained fn div(x : u32, y : u32) -> u32 { - x / y -} - -unconstrained fn check_signed_div(x: i32, y: i32, result: i32) -> bool { - (x / y) == result -} - -unconstrained fn eq(x : u32, y : u32) -> bool { - x == y -} - -unconstrained fn lt(x : u32, y : u32) -> bool { - x < y -} - -unconstrained fn and(x : u32, y : u32) -> u32 { - x & y -} - -unconstrained fn or(x : u32, y : u32) -> u32 { - x | y -} - -unconstrained fn check_xor(x : u32, y : u32, result: u32) -> bool { - (x ^ y) == result -} - -unconstrained fn shr(x : u32, y : u32) -> u32 { - x >> y -} - -unconstrained fn shl(x : u32, y : u32) -> u32 { - x << y -} - 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 deleted file mode 100644 index 57dc516bd97..00000000000 --- a/tooling/nargo_cli/tests/compile_success_empty/brillig_modulo/src/main.nr +++ /dev/null @@ -1,28 +0,0 @@ -// Tests a very simple program. -// -// The features being tested is modulo operations on brillig -fn main() { - assert(modulo(47, 3) == 2); - assert(modulo(2, 3) == 2); - assert(signed_modulo(5, 3) == 2); - assert(signed_modulo(2, 3) == 2); - - 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); - // (-5 / 3) * 3 - 2 = -1 * 3 - 2 = -3 - 2 = -5 - assert(signed_modulo(minus_five, 3) == minus_two); - // (-5 / -3) * -3 - 2 = 1 * -3 - 2 = -3 - 2 = -5 - assert(signed_modulo(minus_five, minus_three) == minus_two); -} - -unconstrained fn modulo(x: u32, y: u32) -> u32 { - x % y -} - -unconstrained fn signed_modulo(x: i4, y: i4) -> i4 { - x % y -} 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 deleted file mode 100644 index 294da58f68a..00000000000 --- a/tooling/nargo_cli/tests/compile_success_empty/brillig_to_bits/src/main.nr +++ /dev/null @@ -1,23 +0,0 @@ -use dep::std; - -unconstrained fn main() { - let field = 1000; - let be_bits = field.to_be_bits(16); - let le_bits = field.to_le_bits(16); - - for i in 0..16 { - let x = be_bits[i]; - let y = le_bits[15-i]; - assert(x == y); - } - - let x = 3; - let be_bits_x = x.to_be_bits(4); - let le_bits_x = x.to_le_bits(4); - - for i in 0..4 { - let be_bit = be_bits_x[i]; - 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 deleted file mode 100644 index b5008c5d6dd..00000000000 --- a/tooling/nargo_cli/tests/compile_success_empty/closure_explicit_types/src/main.nr +++ /dev/null @@ -1,79 +0,0 @@ -fn ret_normal_lambda1() -> fn() -> Field { - || 10 -} - -// return lamda that captures a thing -fn ret_closure1() -> fn[(Field,)]() -> Field { - let x = 20; - || x + 10 -} - -// return lamda that captures two things -fn ret_closure2() -> fn[(Field,Field)]() -> Field { - let x = 20; - let y = 10; - || x + y + 10 -} - -// return lamda that captures two things with different types -fn ret_closure3() -> fn[(u32,u64)]() -> u64 { - let x: u32 = 20; - let y: u64 = 10; - || x as u64 + y + 10 -} - -// accepts closure that has 1 thing in its env, calls it and returns the result -fn accepts_closure1(f: fn[(Field,)]() -> Field) -> Field { - f() -} - -// accepts closure that has 1 thing in its env and returns it -fn accepts_closure2(f: fn[(Field,)]() -> Field) -> fn[(Field,)]() -> Field { - f -} - -// accepts closure with different types in the capture group -fn accepts_closure3(f: fn[(u32, u64)]() -> u64) -> u64 { - f() -} - -// generic over closure environments -fn add_results(f1: fn[Env1]() -> Field, f2: fn[Env2]() -> Field) -> Field { - f1() + f2() -} - -// a *really* generic function -fn map(arr: [T; N], f: fn[Env](T) -> U) -> [U; N] { - let first_elem = f(arr[0]); - let mut ret = [first_elem; N]; - - for i in 1 .. N { - ret[i] = f(arr[i]); - } - - ret -} - -fn main() { - assert(ret_normal_lambda1()() == 10); - assert(ret_closure1()() == 30); - assert(ret_closure2()() == 40); - assert(ret_closure3()() == 40); - - let x = 50; - assert(accepts_closure1(|| x) == 50); - assert(accepts_closure2(|| x + 10)() == 60); - - let y: u32 = 30; - let z: u64 = 40; - 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); - - 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/conditional_regression_547/src/main.nr b/tooling/nargo_cli/tests/compile_success_empty/conditional_regression_547/src/main.nr deleted file mode 100644 index f97199288a4..00000000000 --- a/tooling/nargo_cli/tests/compile_success_empty/conditional_regression_547/src/main.nr +++ /dev/null @@ -1,17 +0,0 @@ -fn main(x: Field) -> pub Field { - // Regression test for issue #547 - // Warning: it must be kept at the start of main - let arr: [u8; 2] = [1, 2]; - if arr[0] != arr[1] { - for i in 0..1 { - assert(i != 2); - } - } - - // Regression for predicate simplification - x + safe_inverse(0) -} - -fn safe_inverse(n: Field) -> Field { - if n == 0 { 0 } else { 1 / n } -} diff --git a/tooling/nargo_cli/tests/compile_success_empty/conditional_regression_to_bits/src/main.nr b/tooling/nargo_cli/tests/compile_success_empty/conditional_regression_to_bits/src/main.nr deleted file mode 100644 index e5450334c5e..00000000000 --- a/tooling/nargo_cli/tests/compile_success_empty/conditional_regression_to_bits/src/main.nr +++ /dev/null @@ -1,28 +0,0 @@ -use dep::std; - -fn main() { - //Regression for to_le_bits() constant evaluation - // binary array representation of u8 1 - let arr: [u8; 2] = [1, 2]; - let as_bits_hardcode_1 = [1, 0]; - let mut c1 = 0; - for i in 0..2 { - let mut as_bits = (arr[i] as Field).to_le_bits(2); - c1 = c1 + as_bits[0] as Field; - - if i == 0 { - assert(arr[i] == 1);// 1 - for k in 0..2 { - assert(as_bits_hardcode_1[k] == as_bits[k]); - } - } - if i == 1 { - assert(arr[i] == 2);//2 - for k in 0..2 { - assert(as_bits_hardcode_1[k] != as_bits[k]); - } - } - } - assert(c1 == 1); -} - 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 deleted file mode 100644 index 2060ac57e31..00000000000 --- a/tooling/nargo_cli/tests/compile_success_empty/ec_baby_jubjub/src/main.nr +++ /dev/null @@ -1,187 +0,0 @@ -// Tests may be checked against https://github.com/cfrg/draft-irtf-cfrg-hash-to-curve/tree/main/poc - -use dep::std::ec::tecurve::affine::Curve as AffineCurve; -use dep::std::ec::tecurve::affine::Point as Gaffine; -use dep::std::ec::tecurve::curvegroup::Curve; -use dep::std::ec::tecurve::curvegroup::Point as G; - -use dep::std::ec::swcurve::affine::Point as SWGaffine; -use dep::std::ec::swcurve::curvegroup::Point as SWG; - -use dep::std::ec::montcurve::affine::Point as MGaffine; -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 { - // Define Baby Jubjub (ERC-2494) parameters in affine representation - 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 - ))); - - // 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))); - - // 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( - bjj_affine.contains(bjj_affine.gen) & - bjj_affine.contains(p1_affine) & - bjj_affine.contains(p2_affine) & - bjj_affine.contains(p3_affine) & - bjj_affine.contains(p4_affine) & - bjj_affine.contains(p5_affine) - ); - - // 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(); - let p4 = p4_affine.into_group(); - let p5 = p5_affine.into_group(); - - // Test addition - assert(p3.eq(bjj.add(p1, p2))); - - // Test scalar multiplication - assert(p4.eq(bjj.mul(2, 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)); - - // Test SWCurve equivalents of the above - // First the affine representation - let bjj_swcurve_affine = bjj_affine.into_swcurve(); - - 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 = bjj_affine.map_into_swcurve(p3_affine); - let p4_swcurve_affine = bjj_affine.map_into_swcurve(p4_affine); - 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))); - - // 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))); - - // 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)); - - // Then the CurveGroup representation - let bjj_swcurve = bjj.into_swcurve(); - - let p1_swcurve = bjj.map_into_swcurve(p1); - let p2_swcurve = bjj.map_into_swcurve(p2); - let p3_swcurve = bjj.map_into_swcurve(p3); - let p4_swcurve = bjj.map_into_swcurve(p4); - let p5_swcurve = bjj.map_into_swcurve(p5); - - // Addition - 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))); - - // 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)); - - // Test MontCurve conversions - // First the affine representation - let bjj_montcurve_affine = bjj_affine.into_montcurve(); - - let p1_montcurve_affine = p1_affine.into_montcurve(); - let p2_montcurve_affine = p2_affine.into_montcurve(); - let p3_montcurve_affine = p3_affine.into_montcurve(); - let p4_montcurve_affine = p4_affine.into_montcurve(); - let p5_montcurve_affine = p5_affine.into_montcurve(); - - // Addition - assert(p3_montcurve_affine.eq(bjj_montcurve_affine.add(p1_montcurve_affine, p2_montcurve_affine))); - - // 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))); - - // 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)); - - // Then the CurveGroup representation - let bjj_montcurve = bjj.into_montcurve(); - - let p1_montcurve = p1_montcurve_affine.into_group(); - let p2_montcurve = p2_montcurve_affine.into_group(); - let p3_montcurve = p3_montcurve_affine.into_group(); - let p4_montcurve = p4_montcurve_affine.into_group(); - let p5_montcurve = p5_montcurve_affine.into_group(); - - // Addition - assert(p3_montcurve.eq(bjj_montcurve.add(p1_montcurve, p2_montcurve))); - - // Doubling - assert(p4_montcurve.eq(bjj_montcurve.mul(2, 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)); - - // 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())); - - // 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 - )))); - } -} 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 deleted file mode 100644 index 9a65c5fd3d2..00000000000 --- a/tooling/nargo_cli/tests/compile_success_empty/generators/src/main.nr +++ /dev/null @@ -1,57 +0,0 @@ -// TODO? -// the syntax for these return types is very difficult to get right :/ -// for arguments this can be handled with a generic Env (or with Fn traits when we add them) -// but for return types neither fo these will help, you need to type out the exact type -fn make_counter() -> fn[(&mut Field,)]() -> Field { - let mut x = &mut 0; - - || { - *x = *x + 1; - *x - } -} - -fn fibonacci_generator() -> fn[(&mut Field, &mut Field)]() -> Field { - let mut x = &mut 1; - let mut y = &mut 2; - - || { - let old_x = *x; - let old_y = *y; - - *y = *x + *y; - *x = old_y; - - old_x - } -} - -// 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()) -} - -fn test_fib() { - let fib = fibonacci_generator(); - - assert(fib() == 1); - assert(fib() == 2); - assert(fib() == 3); - assert(fib() == 5); - - assert(get_some(fib) == [8, 13, 21, 34, 55]); -} - -fn test_counter() { - let counter = make_counter(); - assert(counter() == 1); - assert(counter() == 2); - assert(counter() == 3); - - assert(get_some(counter) == [4, 5, 6, 7, 8]); -} - -fn main() { - test_fib(); - test_counter(); -} 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 deleted file mode 100644 index d3fd4d5f94a..00000000000 --- a/tooling/nargo_cli/tests/compile_success_empty/higher_order_fn_selector/src/main.nr +++ /dev/null @@ -1,31 +0,0 @@ -fn g(x: &mut Field) -> () { - *x *= 2; -} - -fn h(x: &mut Field) -> () { - *x *= 3; -} - -fn selector(flag: &mut bool) -> fn(&mut Field) -> () { - let my_func = if *flag { g } else { h }; - - // Flip the flag for the next function call - *flag = !(*flag); - my_func -} - -fn main() { - let mut flag: bool = true; - - let mut x: Field = 100; - let returned_func = selector(&mut flag); - returned_func(&mut x); - - assert(x == 200); - - let mut y: Field = 100; - let returned_func2 = selector(&mut flag); - returned_func2(&mut y); - - assert(y == 300); -} 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 deleted file mode 100644 index 0a4bd65eb0c..00000000000 --- a/tooling/nargo_cli/tests/compile_success_empty/impl_with_where_clause/src/main.nr +++ /dev/null @@ -1,30 +0,0 @@ - -fn main() { - // Test is temporarily disabled, see #3409 - // 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; 3] 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/instruction_deduplication/src/main.nr b/tooling/nargo_cli/tests/compile_success_empty/instruction_deduplication/src/main.nr deleted file mode 100644 index 09aa8ee47f2..00000000000 --- a/tooling/nargo_cli/tests/compile_success_empty/instruction_deduplication/src/main.nr +++ /dev/null @@ -1,5 +0,0 @@ -fn main(x : Field) { - // This is a regression test for #2450. - // The compiler should recognize that the `(x as u32)` instructions are duplicates and so have the same output. - assert(x as u32 == x as u32); -} 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 deleted file mode 100644 index c07c35d521a..00000000000 --- a/tooling/nargo_cli/tests/compile_success_empty/intrinsic_die/src/main.nr +++ /dev/null @@ -1,9 +0,0 @@ -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_commitment([x]); - let _p1 = std::scalar_mul::fixed_base_embedded_curve(x, 0); -} diff --git a/tooling/nargo_cli/tests/compile_success_empty/references_aliasing/src/main.nr b/tooling/nargo_cli/tests/compile_success_empty/references_aliasing/src/main.nr deleted file mode 100644 index 02057732f35..00000000000 --- a/tooling/nargo_cli/tests/compile_success_empty/references_aliasing/src/main.nr +++ /dev/null @@ -1,29 +0,0 @@ -fn main() { - let mut x = 100; - let mut xref = &mut x; - increment(xref); - assert(*xref == 101); - - regression_2445(); -} - -fn increment(mut r: &mut Field) { - *r = *r + 1; -} - -// If aliasing within arrays and constant folding within the mem2reg pass aren't -// handled, we'll fail to optimize out all the references in this function. -fn regression_2445() { - let mut var = 0; - let ref = &mut &mut var; - - let mut array = [ref, ref]; - - **array[0] = 1; - **array[1] = 2; - - assert(var == 2); - assert(**ref == 2); - assert(**array[0] == 2); - assert(**array[1] == 2); -} 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 deleted file mode 100644 index ab74bd2c826..00000000000 --- a/tooling/nargo_cli/tests/compile_success_empty/regression_2099/src/main.nr +++ /dev/null @@ -1,40 +0,0 @@ -use dep::std::ec::tecurve::affine::Curve as AffineCurve; -use dep::std::ec::tecurve::affine::Point as Gaffine; -use dep::std::ec::tecurve::curvegroup::Curve; -use dep::std::ec::tecurve::curvegroup::Point as G; - -use dep::std::ec::swcurve::affine::Point as SWGaffine; -use dep::std::ec::swcurve::curvegroup::Point as SWG; - -use dep::std::ec::montcurve::affine::Point as MGaffine; -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 - )); - - // 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); - - // Test SWCurve equivalents of the above - // First the affine representation - let bjj_swcurve_affine = bjj_affine.into_swcurve(); - - 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); - - // Check that these points are on the curve - 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 deleted file mode 100644 index 13ac6c8d764..00000000000 --- a/tooling/nargo_cli/tests/compile_success_empty/ret_fn_ret_cl/src/main.nr +++ /dev/null @@ -1,35 +0,0 @@ -fn f(x: Field) -> Field { - x + 1 -} - -fn ret_fn() -> fn(Field) -> Field { - f -} - -// TODO: in the advanced implicitly generic function with closures branch -// which would support higher-order functions in a better way -// support returning closures: -// -// fn ret_closure() -> fn(Field) -> Field { -// let y = 1; -// let inner_closure = |z| -> Field{ -// z + y -// }; -// inner_closure -// } -fn ret_lambda() -> fn(Field) -> Field { - let cl = |z: Field| -> Field { - z + 1 - }; - cl -} - -fn main(x : Field) { - let result_fn = ret_fn(); - assert(result_fn(x) == x + 1); - - // 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_array_param/src/main.nr b/tooling/nargo_cli/tests/compile_success_empty/simple_array_param/src/main.nr deleted file mode 100644 index 60978bab519..00000000000 --- a/tooling/nargo_cli/tests/compile_success_empty/simple_array_param/src/main.nr +++ /dev/null @@ -1,6 +0,0 @@ -// This program tests: -// - the allocation of virtual arrays for array params to main -// - load instructions for such arrays -fn main(xs : [Field; 2]) -> pub Field { - xs[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 deleted file mode 100644 index 1c66955e126..00000000000 --- a/tooling/nargo_cli/tests/compile_success_empty/simple_program_no_body/src/main.nr +++ /dev/null @@ -1,9 +0,0 @@ -// Tests a very simple program. -// -// The features being tested are: -// - Abi generation of private and public -// main parameters. -// -// This program will never fail since there are -// no assertions being applied. -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 deleted file mode 100644 index 38f95d99f9b..00000000000 --- a/tooling/nargo_cli/tests/compile_success_empty/simple_range/src/main.nr +++ /dev/null @@ -1,6 +0,0 @@ -// Tests a very simple program. -// -// The features being tested is casting to an integer -fn main(x : Field) { - let _z = x as u32; -} 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 deleted file mode 100644 index 5da3ca9e060..00000000000 --- a/tooling/nargo_cli/tests/compile_success_empty/to_bits/src/main.nr +++ /dev/null @@ -1,21 +0,0 @@ -fn main() { - let field = 1000; - let be_bits = field.to_be_bits(16); - let le_bits = field.to_le_bits(16); - - for i in 0..16 { - let x = be_bits[i]; - let y = le_bits[15-i]; - assert(x == y); - } - - let x = 3; - let be_bits_x = x.to_be_bits(4); - let le_bits_x = x.to_le_bits(4); - - for i in 0..4 { - let be_bit = be_bits_x[i]; - let le_bit = le_bits_x[3-i]; - assert(be_bit == le_bit); - } -} diff --git a/tooling/nargo_cli/tests/compile_success_empty/trait_allowed_item_name_matches/src/main.nr b/tooling/nargo_cli/tests/compile_success_empty/trait_allowed_item_name_matches/src/main.nr deleted file mode 100644 index 7db61e854fc..00000000000 --- a/tooling/nargo_cli/tests/compile_success_empty/trait_allowed_item_name_matches/src/main.nr +++ /dev/null @@ -1,26 +0,0 @@ -trait Trait1 { - // types and consts with the same name are allowed - type Tralala; - let Tralala: u32; -} - -trait Trait2 { - // consts and types with the same name are allowed - let Tralala: u32; - type Tralala; -} - -trait Trait3 { - // types and functions with the same name are allowed - type Tralala; - fn Tralala(); -} - -trait Trait4 { - // functions and types with the same name are allowed - fn Tralala(); - type Tralala; -} - -fn main() { -} diff --git a/tooling/nargo_cli/tests/compile_success_empty/trait_function_calls/src/main.nr b/tooling/nargo_cli/tests/compile_success_empty/trait_function_calls/src/main.nr deleted file mode 100644 index a3a19fe9dec..00000000000 --- a/tooling/nargo_cli/tests/compile_success_empty/trait_function_calls/src/main.nr +++ /dev/null @@ -1,654 +0,0 @@ -// a more thorough test for trait function/method calls from within trait function/method calls - -// This tests all combinations of caller+callee pairs. For each of these, the following combination of properties are tried: -// *) method (has self parameter) vs function (no self parameter) -// *) default vs overriden vs overriden (no default) - -// test order is: -// 1) trait method -> trait method -// 1a) trait default method -> trait default method -// 1b) trait default method -> trait overriden method -// 1c) trait default method -> trait overriden (no default) method -// 1d) trait overriden method -> trait default method -// 1e) trait overriden method -> trait overriden method -// 1f) trait overriden method -> trait overriden (no default) method -// 1g) trait overriden (no default) method -> trait default method -// 1h) trait overriden (no default) method -> trait overriden method -// 1i) trait overriden (no default) method -> trait overriden (no default) method -// 2) trait method -> trait function -// 2a) - subcases are the same as the above -// ... -// 2i) -// 3{a..i}) trait function -> trait method -// 4{a..i}) trait function -> trait function - -// 1) trait method -> trait method -// 1a) trait default method -> trait default method -trait Trait1a { - fn trait_method1(self) -> Field { - self.trait_method2() * 7892 - self.vl - } - fn trait_method2(self) -> Field { - 43278 - } -} -struct Struct1a { vl: Field } -impl Trait1a for Struct1a { } - -// 1b) trait default method -> trait overriden method -trait Trait1b { - fn trait_method1(self) -> Field { - self.trait_method2() * 2832 - self.vl - } - fn trait_method2(self) -> Field { - 9323 - } -} -struct Struct1b { vl: Field } -impl Trait1b for Struct1b { - fn trait_method2(self) -> Field { - 2394 - } -} - -// 1c) trait default method -> trait overriden (no default) method -trait Trait1c { - fn trait_method1(self) -> Field { - self.trait_method2() * 7635 - self.vl - } - fn trait_method2(self) -> Field; -} -struct Struct1c { vl: Field } -impl Trait1c for Struct1c { - fn trait_method2(self) -> Field { - 5485 - } -} - -// 1d) trait overriden method -> trait default method -trait Trait1d { - fn trait_method1(self) -> Field { - self.trait_method2() * 2825 - self.vl - } - fn trait_method2(self) -> Field { - 29341 - } -} -struct Struct1d { vl: Field } -impl Trait1d for Struct1d { - fn trait_method1(self) -> Field { - self.trait_method2() * 9342 - self.vl - } -} - -// 1e) trait overriden method -> trait overriden method -trait Trait1e { - fn trait_method1(self) -> Field { - self.trait_method2() * 85465 - self.vl - } - fn trait_method2(self) -> Field { - 2381 - } -} -struct Struct1e { vl: Field } -impl Trait1e for Struct1e { - fn trait_method1(self) -> Field { - self.trait_method2() * 47324 - self.vl - } - fn trait_method2(self) -> Field { - 58945 - } -} - -// 1f) trait overriden method -> trait overriden (no default) method -trait Trait1f { - fn trait_method1(self) -> Field { - self.trait_method2() * 43257 - self.vl - } - fn trait_method2(self) -> Field; -} -struct Struct1f { vl: Field } -impl Trait1f for Struct1f { - fn trait_method1(self) -> Field { - self.trait_method2() * 34875 - self.vl - } - fn trait_method2(self) -> Field { - 5748 - } -} - -// 1g) trait overriden (no default) method -> trait default method -trait Trait1g { - fn trait_method1(self) -> Field; - fn trait_method2(self) -> Field { - 37845 - } -} -struct Struct1g { vl: Field } -impl Trait1g for Struct1g { - fn trait_method1(self) -> Field { - self.trait_method2() * 7854 - self.vl - } -} - -// 1h) trait overriden (no default) method -> trait overriden method -trait Trait1h { - fn trait_method1(self) -> Field; - fn trait_method2(self) -> Field { - 7823 - } -} -struct Struct1h { vl: Field } -impl Trait1h for Struct1h { - fn trait_method1(self) -> Field { - self.trait_method2() * 3482 - self.vl - } - fn trait_method2(self) -> Field { - 8542 - } -} - -// 1i) trait overriden (no default) method -> trait overriden (no default) method -trait Trait1i { - fn trait_method1(self) -> Field; - fn trait_method2(self) -> Field; -} -struct Struct1i { vl: Field } -impl Trait1i for Struct1i { - fn trait_method1(self) -> Field { - self.trait_method2() * 23478 - self.vl - } - fn trait_method2(self) -> Field { - 98543 - } -} - -// 2) trait method -> trait function -// 2a) trait default method -> trait default function -trait Trait2a { - fn trait_method1(self) -> Field { - Self::trait_function2() * 2385 - self.vl - } - fn trait_function2() -> Field { - 7843 - } -} -struct Struct2a { vl: Field } -impl Trait2a for Struct2a { } - -// 2b) trait default method -> trait overriden function -trait Trait2b { - fn trait_method1(self) -> Field { - Self::trait_function2() * 6583 - self.vl - } - fn trait_function2() -> Field { - 3752 - } -} -struct Struct2b { vl: Field } -impl Trait2b for Struct2b { - fn trait_function2() -> Field { - 8477 - } -} - -// 2c) trait default method -> trait overriden (no default) function -trait Trait2c { - fn trait_method1(self) -> Field { - Self::trait_function2() * 2831 - self.vl - } - fn trait_function2() -> Field; -} -struct Struct2c { vl: Field } -impl Trait2c for Struct2c { - fn trait_function2() -> Field { - 8342 - } -} - -// 2d) trait overriden method -> trait default function -trait Trait2d { - fn trait_method1(self) -> Field { - Self::trait_function2() * 924 - self.vl - } - fn trait_function2() -> Field { - 384 - } -} -struct Struct2d { vl: Field } -impl Trait2d for Struct2d { - fn trait_method1(self) -> Field { - Self::trait_function2() * 3984 - self.vl - } -} - -// 2e) trait overriden method -> trait overriden function -trait Trait2e { - fn trait_method1(self) -> Field { - Self::trait_function2() * 3642 - self.vl - } - fn trait_function2() -> Field { - 97342 - } -} -struct Struct2e { vl: Field } -impl Trait2e for Struct2e { - fn trait_method1(self) -> Field { - Self::trait_function2() * 7363 - self.vl - } - fn trait_function2() -> Field { - 39400 - } -} - -// 2f) trait overriden method -> trait overriden (no default) function -trait Trait2f { - fn trait_method1(self) -> Field { - Self::trait_function2() * 2783 - self.vl - } - fn trait_function2() -> Field; -} -struct Struct2f { vl: Field } -impl Trait2f for Struct2f { - fn trait_method1(self) -> Field { - Self::trait_function2() * 6362 - self.vl - } - fn trait_function2() -> Field { - 72311 - } -} - -// 2g) trait overriden (no default) method -> trait default function -trait Trait2g { - fn trait_method1(self) -> Field; - fn trait_function2() -> Field { - 19273 - } -} -struct Struct2g { vl: Field } -impl Trait2g for Struct2g { - fn trait_method1(self) -> Field { - Self::trait_function2() * 9123 - self.vl - } -} - -// 2h) trait overriden (no default) method -> trait overriden function -trait Trait2h { - fn trait_method1(self) -> Field; - fn trait_function2() -> Field { - 1281 - } -} -struct Struct2h { vl: Field } -impl Trait2h for Struct2h { - fn trait_method1(self) -> Field { - Self::trait_function2() * 4833 - self.vl - } - fn trait_function2() -> Field { - 5335 - } -} - -// 2i) trait overriden (no default) method -> trait overriden (no default) function -trait Trait2i { - fn trait_method1(self) -> Field; - fn trait_function2() -> Field; -} -struct Struct2i { vl: Field } -impl Trait2i for Struct2i { - fn trait_method1(self) -> Field { - Self::trait_function2() * 2291 - self.vl - } - fn trait_function2() -> Field { - 3322 - } -} - -// 3 trait function -> trait method -// 3a) trait default function -> trait default method -trait Trait3a { - fn trait_function1(a: Field, b: Self) -> Field { - b.trait_method2() * 8344 - b.vl + a - } - fn trait_method2(self) -> Field { - 19212 - } -} -struct Struct3a { vl: Field } -impl Trait3a for Struct3a { } - -// 3b) trait default function -> trait overriden method -trait Trait3b { - fn trait_function1(a: Field, b: Self) -> Field { - b.trait_method2() * 9233 - b.vl + a - } - fn trait_method2(self) -> Field { - 9111 - } -} -struct Struct3b { vl: Field } -impl Trait3b for Struct3b { - fn trait_method2(self) -> Field { - 2392 - } -} - -// 3c) trait default function -> trait overriden (no default) method -trait Trait3c { - fn trait_function1(a: Field, b: Self) -> Field { - b.trait_method2() * 2822 - b.vl + a - } - fn trait_method2(self) -> Field; -} -struct Struct3c { vl: Field } -impl Trait3c for Struct3c { - fn trait_method2(self) -> Field { - 7743 - } -} - -// 3d) trait overriden function -> trait default method -trait Trait3d { - fn trait_function1(a: Field, b: Self) -> Field { - b.trait_method2() * 291 - b.vl + a - } - fn trait_method2(self) -> Field { - 3328 - } -} -struct Struct3d { vl: Field } -impl Trait3d for Struct3d { - fn trait_function1(a: Field, b: Self) -> Field { - b.trait_method2() * 4933 - b.vl + a - } -} - -// 3e) trait overriden function -> trait overriden method -trait Trait3e { - fn trait_function1(a: Field, b: Self) -> Field { - b.trait_method2() * 71231 - b.vl + a - } - fn trait_method2(self) -> Field { - 373 - } -} -struct Struct3e { vl: Field } -impl Trait3e for Struct3e { - fn trait_function1(a: Field, b: Self) -> Field { - b.trait_method2() * 81232 - b.vl + a - } - fn trait_method2(self) -> Field { - 80002 - } -} - -// 3f) trait overriden function -> trait overriden (no default) method -trait Trait3f { - fn trait_function1(a: Field, b: Self) -> Field { - b.trait_method2() * 28223 - b.vl + a - } - fn trait_method2(self) -> Field; -} -struct Struct3f { vl: Field } -impl Trait3f for Struct3f { - fn trait_function1(a: Field, b: Self) -> Field { - b.trait_method2() * 29223 - b.vl + a - } - fn trait_method2(self) -> Field { - 63532 - } -} - -// 3g) trait overriden (no default) function -> trait default method -trait Trait3g { - fn trait_function1(a: Field, b: Self) -> Field; - fn trait_method2(self) -> Field { - 8887 - } -} -struct Struct3g { vl: Field } -impl Trait3g for Struct3g { - fn trait_function1(a: Field, b: Self) -> Field { - b.trait_method2() * 31337 - b.vl + a - } -} - -// 3h) trait overriden (no default) function -> trait overriden method -trait Trait3h { - fn trait_function1(a: Field, b: Self) -> Field; - fn trait_method2(self) -> Field { - 293 - } -} -struct Struct3h { vl: Field } -impl Trait3h for Struct3h { - fn trait_function1(a: Field, b: Self) -> Field { - b.trait_method2() * 74747 - b.vl + a - } - fn trait_method2(self) -> Field { - 6283 - } -} - -// 3i) trait overriden (no default) function -> trait overriden (no default) method -trait Trait3i { - fn trait_function1(a: Field, b: Self) -> Field; - fn trait_method2(self) -> Field; -} -struct Struct3i { vl: Field } -impl Trait3i for Struct3i { - fn trait_function1(a: Field, b: Self) -> Field { - b.trait_method2() * 1237 - b.vl + a - } - fn trait_method2(self) -> Field { - 84352 - } -} - -// 4) trait function -> trait function -// 4a) trait default function -> trait default function -trait Trait4a { - fn trait_function1() -> Field { - Self::trait_function2() * 3842 - } - fn trait_function2() -> Field { - 2932 - } -} -struct Struct4a { vl: Field } -impl Trait4a for Struct4a { } - -// 4b) trait default function -> trait overriden function -trait Trait4b { - fn trait_function1() -> Field { - Self::trait_function2() * 3842 - } - fn trait_function2() -> Field { - 2932 - } -} -struct Struct4b { vl: Field } -impl Trait4b for Struct4b { - fn trait_function2() -> Field { - 9353 - } -} - -// 4c) trait default function -> trait overriden (no default) function -trait Trait4c { - fn trait_function1() -> Field { - Self::trait_function2() * 7832 - } - fn trait_function2() -> Field; -} -struct Struct4c { vl: Field } -impl Trait4c for Struct4c { - fn trait_function2() -> Field { - 2928 - } -} - -// 4d) trait overriden function -> trait default function -trait Trait4d { - fn trait_function1() -> Field { - Self::trait_function2() * 2283 - } - fn trait_function2() -> Field { - 9332 - } -} -struct Struct4d { vl: Field } -impl Trait4d for Struct4d { - fn trait_function1() -> Field { - Self::trait_function2() * 8374 - } -} - -// 4e) trait overriden function -> trait overriden function -trait Trait4e { - fn trait_function1() -> Field { - Self::trait_function2() * 94329 - } - fn trait_function2() -> Field { - 28328 - } -} -struct Struct4e { vl: Field } -impl Trait4e for Struct4e { - fn trait_function1() -> Field { - Self::trait_function2() * 12323 - } - fn trait_function2() -> Field { - 38434 - } -} - -// 4f) trait overriden function -> trait overriden (no default) function -trait Trait4f { - fn trait_function1() -> Field { - Self::trait_function2() * 23723 - } - fn trait_function2() -> Field; -} -struct Struct4f { vl: Field } -impl Trait4f for Struct4f { - fn trait_function1() -> Field { - Self::trait_function2() * 21392 - } - fn trait_function2() -> Field { - 4394 - } -} - -// 4g) trait overriden (no default) function -> trait default function -trait Trait4g { - fn trait_function1() -> Field; - fn trait_function2() -> Field { - 2932 - } -} -struct Struct4g { vl: Field } -impl Trait4g for Struct4g { - fn trait_function1() -> Field { - Self::trait_function2() * 3345 - } -} - -// 4h) trait overriden (no default) function -> trait overriden function -trait Trait4h { - fn trait_function1() -> Field; - fn trait_function2() -> Field { - 5756 - } -} -struct Struct4h { vl: Field } -impl Trait4h for Struct4h { - fn trait_function1() -> Field { - Self::trait_function2() * 6478 - } - fn trait_function2() -> Field { - 5435 - } -} - -// 4i) trait overriden (no default) function -> trait overriden (no default) function -trait Trait4i { - fn trait_function1() -> Field; - fn trait_function2() -> Field; -} -struct Struct4i { vl: Field } -impl Trait4i for Struct4i { - fn trait_function1() -> Field { - Self::trait_function2() * 8239 - } - fn trait_function2() -> Field { - 2032 - } -} - - -fn main() { - let t1a = Struct1a { vl: 1234 }; - assert(t1a.trait_method1() == 341548742); - let t1b = Struct1b { vl: 4444 }; - assert(t1b.trait_method1() == 6775364); - let t1c = Struct1c { vl: 3946 }; - assert(t1c.trait_method1() == 41874029); - let t1d = Struct1d { vl: 9234 }; - assert(t1d.trait_method1() == 274094388); - let t1e = Struct1e { vl: 5438 }; - assert(t1e.trait_method1() == 2789507742); - let t1f = Struct1f { vl: 6237 }; - assert(t1f.trait_method1() == 200455263); - let t1g = Struct1g { vl: 43587 }; - assert(t1g.trait_method1() == 297191043); - let t1h = Struct1h { vl: 3984 }; - assert(t1h.trait_method1() == 29739260); - let t1i = Struct1i { vl: 9234 }; - assert(t1i.trait_method1() == 2313583320); - let t2a = Struct2a { vl: 4362 }; - assert(t2a.trait_method1() == 18701193); - let t2b = Struct2b { vl: 8347 }; - assert(t2b.trait_method1() == 55795744); - let t2c = Struct2c { vl: 1923 }; - assert(t2c.trait_method1() == 23614279); - let t2d = Struct2d { vl: 92384 }; - assert(t2d.trait_method1() == 1437472); - let t2e = Struct2e { vl: 83943 }; - assert(t2e.trait_method1() == 290018257); - let t2f = Struct2f { vl: 8237 }; - assert(t2f.trait_method1() == 460034345); - let t2g = Struct2g { vl: 1232 }; - assert(t2g.trait_method1() == 175826347); - let t2h = Struct2h { vl: 7222 }; - assert(t2h.trait_method1() == 25776833); - let t2i = Struct2i { vl: 1821 }; - assert(t2i.trait_method1() == 7608881); - let t3a = Struct3a { vl: 93248 }; - assert(Struct3a::trait_function1(5, t3a) == 160211685); - let t3b = Struct3b { vl: 76763 }; - assert(Struct3b::trait_function1(62, t3b) == 22008635); - let t3c = Struct3c { vl: 3833 }; - assert(Struct3c::trait_function1(25, t3c) == 21846938); - let t3d = Struct3d { vl: 5645 }; - assert(Struct3d::trait_function1(73, t3d) == 16411452); - let t3e = Struct3e { vl: 22912 }; - assert(Struct3e::trait_function1(92, t3e) == 6498699644); - let t3f = Struct3f { vl: 3256 }; - assert(Struct3f::trait_function1(77, t3f) == 1856592457); - let t3g = Struct3g { vl: 22832 }; - assert(Struct3g::trait_function1(23, t3g) == 278469110); - let t3h = Struct3h { vl: 4933 }; - assert(Struct3h::trait_function1(17, t3h) == 469630485); - let t3i = Struct3i { vl: 39432 }; - assert(Struct3i::trait_function1(54, t3i) == 104304046); - assert(Struct4a::trait_function1() == 11264744); - assert(Struct4b::trait_function1() == 35934226); - assert(Struct4c::trait_function1() == 22932096); - assert(Struct4d::trait_function1() == 78146168); - assert(Struct4e::trait_function1() == 473622182); - assert(Struct4f::trait_function1() == 93996448); - assert(Struct4g::trait_function1() == 9807540); - assert(Struct4h::trait_function1() == 35207930); - assert(Struct4i::trait_function1() == 16741648); -} 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 deleted file mode 100644 index 504132eea8d..00000000000 --- a/tooling/nargo_cli/tests/compile_success_empty/trait_generics/src/main.nr +++ /dev/null @@ -1,62 +0,0 @@ - -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); - - call_impl_with_generic_struct(); - call_impl_with_generic_function(); -} - -// Ensure we can call a generic impl -fn call_impl_with_generic_struct() { - 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 } -} - -fn call_impl_with_generic_function() { - assert(3.t3(7) == 7); -} - -trait T3 { - fn t3(self, x: T) -> T; -} - -impl T3 for u32 { - fn t3(self, y: U) -> U { y } -} diff --git a/tooling/nargo_cli/tests/compile_success_empty/trait_multi_module_test/src/module3.nr b/tooling/nargo_cli/tests/compile_success_empty/trait_multi_module_test/src/module3.nr deleted file mode 100644 index 4b2fbc9bfcc..00000000000 --- a/tooling/nargo_cli/tests/compile_success_empty/trait_multi_module_test/src/module3.nr +++ /dev/null @@ -1,5 +0,0 @@ -use crate::module1::MyTrait; -use crate::module2::MyStruct; - -// ensure we can implement traits that are imported with the `use` syntax -impl MyTrait for MyStruct {} 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 deleted file mode 100644 index 763784f64e8..00000000000 --- a/tooling/nargo_cli/tests/compile_success_empty/trait_override_implementation/src/main.nr +++ /dev/null @@ -1,77 +0,0 @@ -use dep::std; - -trait Default { - fn default(x: Field, y: Field) -> Self; - - fn method2(x: Field) -> Field { - x - } -} - -struct Foo { - bar: Field, - array: [Field; 2], -} - -impl Default for Foo { - fn default(x: Field,y: Field) -> Self { - Self { bar: x, array: [x,y] } - } - - fn method2(x: Field) -> Field { - x * 3 - } -} - -trait F { - fn f1(self) -> Field; - fn f2(self) -> Field { 2 } - fn f3(self) -> Field { 3 } - fn f4(self) -> Field { 4 } - fn f5(self) -> Field { 5 } -} - -struct Bar {} - -impl F for Bar { - fn f5(self) -> Field { 50 } - fn f1(self) -> Field { 10 } - fn f3(self) -> Field { 30 } -} - - -// 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, "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{}; - - // 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/trait_self/src/main.nr b/tooling/nargo_cli/tests/compile_success_empty/trait_self/src/main.nr deleted file mode 100644 index 5bc372ba09b..00000000000 --- a/tooling/nargo_cli/tests/compile_success_empty/trait_self/src/main.nr +++ /dev/null @@ -1,41 +0,0 @@ -trait ATrait { - fn asd() -> Self; - - fn static_method() -> Field { - Self::static_method_2() - } - - fn static_method_2() -> Field { - 100 - } -} - -struct Foo { - x: Field -} -impl ATrait for Foo { - fn asd() -> Self { - // This should pass as Self should be bound to Foo while typechecking this - Foo{x: 100} - } -} - -struct Bar { - x: Field -} -impl ATrait for Bar { - // The trait method is declared as returning `Self` - // but explicitly specifying the type in the impl should work - fn asd() -> Bar { - Bar{x: 100} - } - - fn static_method_2() -> Field { - 200 - } -} - -fn main() { - assert(Foo::static_method() == 100); - assert(Bar::static_method() == 200); -} \ No newline at end of file diff --git a/tooling/nargo_cli/tests/compile_success_empty/trait_self/trait_self/Nargo.toml b/tooling/nargo_cli/tests/compile_success_empty/trait_self/trait_self/Nargo.toml deleted file mode 100644 index 71c541ccd4f..00000000000 --- a/tooling/nargo_cli/tests/compile_success_empty/trait_self/trait_self/Nargo.toml +++ /dev/null @@ -1,6 +0,0 @@ -[package] -name = "trait_self" -type = "bin" -authors = [""] - -[dependencies] \ No newline at end of file diff --git a/tooling/nargo_cli/tests/compile_success_empty/trait_self/trait_self/src/main.nr b/tooling/nargo_cli/tests/compile_success_empty/trait_self/trait_self/src/main.nr deleted file mode 100644 index f4f73822cc3..00000000000 --- a/tooling/nargo_cli/tests/compile_success_empty/trait_self/trait_self/src/main.nr +++ /dev/null @@ -1,18 +0,0 @@ -struct Foo { - x: Field -} - -trait Asd { - fn asd() -> Self; -} - -impl Asd for Foo { - // the Self should typecheck properly - fn asd() -> Self { - Foo{x: 100} - } -} - -fn main() { - assert(Foo::asd().x == 100); -} \ No newline at end of file diff --git a/tooling/nargo_cli/tests/compile_success_empty/trait_where_clause/src/main.nr b/tooling/nargo_cli/tests/compile_success_empty/trait_where_clause/src/main.nr deleted file mode 100644 index 001bd5a6ec6..00000000000 --- a/tooling/nargo_cli/tests/compile_success_empty/trait_where_clause/src/main.nr +++ /dev/null @@ -1,58 +0,0 @@ -// TODO(#2568): Currently we only support trait constraints in a few cases. -// There's a bunch of other places where they can pop up: -// - trait methods (trait Foo where T: ... { ) -// - structs (struct Foo where T: ...) - -// import the traits from another module to ensure the where clauses are ok with that -mod the_trait; -use crate::the_trait::Asd; -use crate::the_trait::StaticTrait; - -struct Add10 { x: Field, } -struct Add20 { x: Field, } -struct Add30 { x: Field, } -struct AddXY { x: Field, y: Field, } - -impl Asd for Add10 { fn asd(self) -> Field { self.x + 10 } } -impl Asd for Add20 { fn asd(self) -> Field { self.x + 20 } } -impl Asd for Add30 { fn asd(self) -> Field { self.x + 30 } } - -impl Asd for AddXY { - fn asd(self) -> Field { - self.x + self.y - } -} - -struct Static100 {} -impl StaticTrait for Static100 { - // use default implementatino for static_function, which returns 100 -} - -struct Static200 {} -impl StaticTrait for Static200 { - fn static_function(slf: Self) -> Field { 200 } -} - -fn assert_asd_eq_100(t: T) where T: crate::the_trait::Asd { - assert(t.asd() == 100); -} - -fn add_one_to_static_function(t: T) -> Field where T: StaticTrait { - T::static_function(t) + 1 -} - -fn main() { - let x = Add10{ x: 90 }; - let z = Add20{ x: 80 }; - let a = Add30{ x: 70 }; - let xy = AddXY{ x: 30, y: 70 }; - - // Temporarily disabled, see #3409 - // assert_asd_eq_100(x); - // assert_asd_eq_100(z); - // assert_asd_eq_100(a); - // assert_asd_eq_100(xy); - - // assert(add_one_to_static_function(Static100{}) == 101); - // assert(add_one_to_static_function(Static200{}) == 201); -} diff --git a/tooling/nargo_cli/tests/compile_success_empty/traits/src/main.nr b/tooling/nargo_cli/tests/compile_success_empty/traits/src/main.nr deleted file mode 100644 index 2333c5da244..00000000000 --- a/tooling/nargo_cli/tests/compile_success_empty/traits/src/main.nr +++ /dev/null @@ -1,21 +0,0 @@ -use dep::std; - -trait Default { - fn default(x: Field, y: Field) -> Self; -} - -struct Foo { - bar: Field, - array: [Field; 2], -} - -impl Default for Foo { - fn default(x: Field,y: Field) -> Self { - Self { bar: x, array: [x,y] } - } -} - -fn main(x: Field, y: Field) { - let first = Foo::default(x,y); - assert(first.bar == x); -} diff --git a/tooling/nargo_cli/tests/compile_success_empty/unary_operators/src/main.nr b/tooling/nargo_cli/tests/compile_success_empty/unary_operators/src/main.nr deleted file mode 100644 index 1c9145fd81f..00000000000 --- a/tooling/nargo_cli/tests/compile_success_empty/unary_operators/src/main.nr +++ /dev/null @@ -1,7 +0,0 @@ -fn main() { - let x = -1; - assert(x == 1 - 2); - - let y: i32 = -1; - assert(x == 1 - 2); -} \ No newline at end of file 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 deleted file mode 100644 index 5c27340caca..00000000000 --- a/tooling/nargo_cli/tests/compile_success_empty/unused_variables/src/main.nr +++ /dev/null @@ -1 +0,0 @@ -fn main(x : Field, y : pub Field) {} diff --git a/tooling/nargo_cli/tests/compile_success_empty/workspace_reexport_bug/binary/src/main.nr b/tooling/nargo_cli/tests/compile_success_empty/workspace_reexport_bug/binary/src/main.nr deleted file mode 100644 index 742ec4187af..00000000000 --- a/tooling/nargo_cli/tests/compile_success_empty/workspace_reexport_bug/binary/src/main.nr +++ /dev/null @@ -1,2 +0,0 @@ -use dep::library::ReExportMeFromAnotherLib; -fn main(_x : ReExportMeFromAnotherLib) {} diff --git a/tooling/nargo_cli/tests/compile_success_empty/workspace_reexport_bug/library/src/lib.nr b/tooling/nargo_cli/tests/compile_success_empty/workspace_reexport_bug/library/src/lib.nr deleted file mode 100644 index 41f7b2aeffd..00000000000 --- a/tooling/nargo_cli/tests/compile_success_empty/workspace_reexport_bug/library/src/lib.nr +++ /dev/null @@ -1,2 +0,0 @@ -// Re-export -use dep::library2::ReExportMeFromAnotherLib; \ No newline at end of file diff --git a/tooling/nargo_cli/tests/compile_success_empty/workspace_reexport_bug/library2/src/lib.nr b/tooling/nargo_cli/tests/compile_success_empty/workspace_reexport_bug/library2/src/lib.nr deleted file mode 100644 index 354558183e7..00000000000 --- a/tooling/nargo_cli/tests/compile_success_empty/workspace_reexport_bug/library2/src/lib.nr +++ /dev/null @@ -1,5 +0,0 @@ -// When we re-export this type from another library and then use it in -// main, we get a panic -struct ReExportMeFromAnotherLib { - x : Field, -} \ No newline at end of file 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 deleted file mode 100644 index 5d139111c53..00000000000 --- a/tooling/nargo_cli/tests/execution_success/1327_concrete_in_generic/src/main.nr +++ /dev/null @@ -1,74 +0,0 @@ -// --- -fn new_concrete_c_over_d() -> C { - let d_method_interface = get_d_method_interface(); - C::new(d_method_interface) -} - -// --- - - // Map - struct B { - new_concrete_t_c_constructor: fn()->T_C, - } - - impl B { - fn new(new_concrete_t_c_constructor: fn () -> T_C) -> B { - B { new_concrete_t_c_constructor } - } - - fn get_t_c(self) -> T_C { - let new_concrete_t_c_constructor = self.new_concrete_t_c_constructor; - new_concrete_t_c_constructor() - } - } - -// --- - - // Set - struct C { - t_d_interface: MethodInterface, - } - - impl C { - fn new (t_d_interface: MethodInterface) -> Self { - C { t_d_interface } - } - - fn call_method_of_t_d(self, t_d: T_D) -> Field { - let some_method_on_t_d = self.t_d_interface.some_method_on_t_d; - some_method_on_t_d(t_d) - } - } - -// --- - - struct MethodInterface { - some_method_on_t_d: fn(T_D)->Field, - } - -// --- - - // Note - struct D { - d: Field, - } - -fn d_method(input: D) -> Field { - input.d * input.d -} - -fn get_d_method_interface() -> MethodInterface { - MethodInterface { some_method_on_t_d: d_method } -} - -// --- -fn main(input: Field) -> pub Field { - let b: B> = B::new(new_concrete_c_over_d); - let c: C = b.get_t_c(); // Singleton - let d: D = D { d: input }; // Note - let output = c.call_method_of_t_d(d); - - output -} - -// --- diff --git a/tooling/nargo_cli/tests/execution_success/1_mul/Nargo.toml b/tooling/nargo_cli/tests/execution_success/1_mul/Nargo.toml deleted file mode 100644 index a0fd8d98027..00000000000 --- a/tooling/nargo_cli/tests/execution_success/1_mul/Nargo.toml +++ /dev/null @@ -1,6 +0,0 @@ -[package] -name = "1_mul" -type = "bin" -authors = [""] - -[dependencies] diff --git a/tooling/nargo_cli/tests/execution_success/1_mul/src/main.nr b/tooling/nargo_cli/tests/execution_success/1_mul/src/main.nr deleted file mode 100644 index 4587b4b5947..00000000000 --- a/tooling/nargo_cli/tests/execution_success/1_mul/src/main.nr +++ /dev/null @@ -1,9 +0,0 @@ -// Test unsafe integer multiplication with overflow: 12^8 = 429 981 696 -// The circuit should handle properly the growth of the bit size -fn main(mut x: u32, y: u32, z: u32) { - x *= y; - x *= x; //144 - x *= x; //20736 - x *= x; //429 981 696 - assert(x == z); -} diff --git a/tooling/nargo_cli/tests/execution_success/5_over/src/main.nr b/tooling/nargo_cli/tests/execution_success/5_over/src/main.nr deleted file mode 100644 index 42f339533aa..00000000000 --- a/tooling/nargo_cli/tests/execution_success/5_over/src/main.nr +++ /dev/null @@ -1,11 +0,0 @@ -use dep::std; - -// Test unsafe integer arithmetic -// Test odd bits integer -fn main(mut x: u32, y: u32) { - x = std::wrapping_mul(x,x); - assert(y == x); - - let c: u3 = 2; - assert(c > x as u3); -} diff --git a/tooling/nargo_cli/tests/execution_success/6/src/main.nr b/tooling/nargo_cli/tests/execution_success/6/src/main.nr deleted file mode 100644 index 4cc8886df04..00000000000 --- a/tooling/nargo_cli/tests/execution_success/6/src/main.nr +++ /dev/null @@ -1,20 +0,0 @@ -// Sha256 circuit where the input is 5 bytes -// not five field elements since sha256 operates over -// bytes. -// -// If you do not cast, it will take all the bytes from the field element! - -// Mimc input is an array of field elements -// The function is called mimc_bn254 to emphasize its parameters are chosen for bn254 curve, it should be used only with a proving system using the same curve (e.g Plonk from Aztec) -use dep::std; - -fn main(x: [u8; 5], result: pub [u8; 32]) { - let mut digest = std::hash::sha256(x); - digest[0] = 5 as u8; - digest = std::hash::sha256(x); - assert(digest == result); - - let y = [12, 45, 78, 41]; - let h = std::hash::mimc::mimc_bn254(y); - assert(h == 18226366069841799622585958305961373004333097209608110160936134895615261821931); -} diff --git a/tooling/nargo_cli/tests/execution_success/6_array/src/main.nr b/tooling/nargo_cli/tests/execution_success/6_array/src/main.nr deleted file mode 100644 index f9873e78cc4..00000000000 --- a/tooling/nargo_cli/tests/execution_success/6_array/src/main.nr +++ /dev/null @@ -1,59 +0,0 @@ -use dep::std; -//Basic tests for arrays -fn main(x: [u32; 5], y: [u32; 5], mut z: u32, t: u32) { - let mut c = 2301; - z = y[4]; - //Test 1: - for i in 0..5 { - c = z*z*y[i]; - z -= c; - } - assert(z == 0); //y[4]=0, so c and z are always 0 - - //Test 2: - c = 2301 as u32; - for i in 0..5 { - c = t+2 as u32; - c = std::wrapping_mul(std::wrapping_mul(z,z),x[i]); - z =std::wrapping_add(z, std::wrapping_sub(x[i]*y[i] , c)); - } - assert(z == 3814912846); - - //Test 3: - c = 2300001 as u32; - z = y[4]; - for i in 0..5 { - z = z + x[i]*y[i]; - for _i in 0..3 { - c = std::wrapping_sub(i as u32,2 as u32); - z = std::wrapping_mul(z,c); - } - } - assert(z == 41472); - - //Test 4: - z = y[4]; - for i in 0..3 { - z += x[i] * y[i]; - for j in 0..2 { - z += x[i+j] - y[i+j]; - } - } - assert(z == 11539); - - //Test 5: - let cc = if z < 1 { x } else { y }; - assert(cc[0] == y[0]); - - // Test 6: for-each loops - for y_elem in y { - for x_elem in x { - assert(x_elem != y_elem); - } - } - - // Test 7: Arrays of tuples/structs - let mut tuple_array = [(1, 2), (3, 4), (5, 6)]; - tuple_array[1] = (7, 8); - assert(tuple_array[1].1 == 8); -} 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 deleted file mode 100644 index 32926171098..00000000000 --- a/tooling/nargo_cli/tests/execution_success/7_function/src/main.nr +++ /dev/null @@ -1,143 +0,0 @@ -//Tests for function calling -fn f1(mut x: Field) -> Field { - x = x + 1; - x = f2(x); - x -} - -fn f2(mut x: Field) -> Field { - x += 2; - x -} - -// Simple example -fn test0(mut a: Field) { - a = f2(a); - assert(a == 3); -} - -// Nested call -fn test1(mut a: Field) { - a = f1(a); - assert(a == 4); -} - -fn test2(z: Field, t: u32 ) { - let a = z + t as Field; - assert(a == 64); - let e = pow(z, t as Field); - assert(e == 714924299); -} - -fn pow(base: Field, exponent: Field) -> Field { - let mut r = 1 as Field; - let b = exponent.to_le_bits(32 as u32); - for i in 1..33 { - r = r*r; - r = (b[32-i] as Field) * (r * base) + (1 - b[32-i] as Field) * r; - } - r -} - -fn test3(x: [u8; 3]) -> [u8; 3] { - let mut buffer = [0 as u8; 3]; - for i in 0..3 { - buffer[i] = x[i]; - } - assert(buffer == x); - buffer -} - -fn test_multiple(x: u32, y: u32) -> (u32, u32) { - (y, x) -} - -fn test_multiple2() -> my_struct { - my_struct { a: 5 as u32, b: 7 as u32 } -} - -fn test_multiple3(x: u32, y: u32) { - assert(x == y); -} - -struct my_struct { - a: u32, - b: u32, -} - -struct my2 { - aa: my_struct, - bb: my_struct, -} - -fn test_multiple4(s: my_struct) { - assert(s.a == s.b + 2); -} - -fn test_multiple5(a: (u32, u32)) { - assert(a.0 == a.1 + 2); -} - -fn test_multiple6(a: my2, b: my_struct, c: (my2, my_struct)) { - test_multiple4(a.aa); - test_multiple5((b.a, b.b)); - assert(c.0.aa.a == c.1.a); -} - -fn foo(a: [Field; N]) -> [Field; N] { - a -} - -fn bar() -> [Field; 1] { - foo([0]) -} - -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 }; - test_multiple4(ss); - test_multiple5((ss.a, ss.b)); - let my = my2 { aa: ss, bb: ss }; - ss.a = 61; - test_multiple6(my, ss, (my, ss)); - - let my_block = { - let mut ab = f2(a); - ab = ab + a; - (x, ab) - }; - assert(my_block.1 == 4); - - test0(a); - test1(a); - test2(x as Field, y); - assert(bar()[0] == 0); - - let mut b = [0 as u8, 5 as u8, 2 as u8]; - let c = test3(b); - assert(b == c); - b[0] = 1 as u8; - let cc = test3(b); - assert(c != cc); - let e = test_multiple(x, y); - assert(e.1 == e.0 + 54 as u32); - let d = test_multiple2(); - assert(d.b == d.a + 2 as u32); - test_multiple3(y, y); - - //Regression test for issue #628: - let result = first(arr_to_field(arr1), arr_to_field(arr2)); - assert(result[0] == arr1[0] as Field); -} - -// Issue #628 -fn arr_to_field(arr: [u32; 9]) -> [Field; 9] { - let mut as_field: [Field; 9] = [0 as Field; 9]; - for i in 0..9 { - as_field[i] = arr[i] as Field; - } - as_field -} - -fn first(a: [Field; 9], _b: [Field; 9]) -> [Field; 9] { - a -} diff --git a/tooling/nargo_cli/tests/execution_success/arithmetic_binary_operations/src/main.nr b/tooling/nargo_cli/tests/execution_success/arithmetic_binary_operations/src/main.nr deleted file mode 100644 index 8fb7bcdbeb2..00000000000 --- a/tooling/nargo_cli/tests/execution_success/arithmetic_binary_operations/src/main.nr +++ /dev/null @@ -1,15 +0,0 @@ -// Tests a very simple program. -// -// The features being tested are: -// Binary addition, multiplication, division, constant modulo -// x = 3, y = 4, z = 5 -fn main(x : Field, y : Field, z : Field) -> pub Field { - //cast - assert(y as u1 == 0); - - let a = x + x; // 3 + 3 = 6 - let b = a - y; // 6 - 4 = 2 - let c = b * z; // 2 * 5 = 10 - let d = c / a; // 10 / 6 (This uses field inversion, so we test it by multiplying by `a`) - d * a -} 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 deleted file mode 100644 index dd732f37e9b..00000000000 --- a/tooling/nargo_cli/tests/execution_success/array_dynamic/src/main.nr +++ /dev/null @@ -1,31 +0,0 @@ -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; - //dynamic array test - dyn_array(x, idx, idx - 3); - - //regression for issue 1283 - let mut s = 0; - let x3 = [246, 159, 32, 176, 8]; - for i in 0..5 { - s += x3[index[i]]; - } - assert(s != 0); - - if 3 < (sublen as u32) { - assert(index[offset + 3] == index2[3]); - } -} - -fn dyn_array(mut x: [u32; 5], y: Field, z: Field) { - assert(x[y] == 111); - assert(x[z] == 101); - x[z] = 0; - assert(x[y] == 111); - assert(x[1] == 0); - if y as u32 < 10 { - x[y] = x[y] - 2; - } else { - x[y] = 0; - } - assert(x[4] == 109); -} diff --git a/tooling/nargo_cli/tests/execution_success/array_len/src/main.nr b/tooling/nargo_cli/tests/execution_success/array_len/src/main.nr deleted file mode 100644 index 3e9d3603311..00000000000 --- a/tooling/nargo_cli/tests/execution_success/array_len/src/main.nr +++ /dev/null @@ -1,26 +0,0 @@ -fn len_plus_1(array: [T; N]) -> Field { - array.len() + 1 -} - -fn add_lens(a: [T; N], b: [Field; M]) -> Field { - a.len() + b.len() -} - -fn nested_call(b: [Field; N]) -> Field { - len_plus_1(b) -} - -fn main(x: Field, len3: [u8; 3], len4: [Field; 4]) { - assert(len_plus_1(len3) == 4); - assert(len_plus_1(len4) == 5); - assert(add_lens(len3, len4) == 7); - assert(nested_call(len4) == 5); - - // std::array::len returns a compile-time known value - assert(len4[len3.len()] == 4); - - // Regression for #1023, ensure .len still works after calling to_le_bytes on a witness. - // This was needed because normally .len is evaluated before acir-gen where to_le_bytes - // on a witness is only evaluated during/after acir-gen. - assert(x.to_le_bytes(8).len() != 0); -} diff --git a/tooling/nargo_cli/tests/execution_success/array_sort/src/main.nr b/tooling/nargo_cli/tests/execution_success/array_sort/src/main.nr deleted file mode 100644 index 17df7b23551..00000000000 --- a/tooling/nargo_cli/tests/execution_success/array_sort/src/main.nr +++ /dev/null @@ -1,6 +0,0 @@ -fn main(xs : [u8; 3]) { - let sorted = xs.sort(); - assert(sorted[0] == 1); - assert(sorted[1] == 2); - assert(sorted[2] == 3); -} diff --git a/tooling/nargo_cli/tests/execution_success/assert_statement/src/main.nr b/tooling/nargo_cli/tests/execution_success/assert_statement/src/main.nr deleted file mode 100644 index f9359ded812..00000000000 --- a/tooling/nargo_cli/tests/execution_success/assert_statement/src/main.nr +++ /dev/null @@ -1,7 +0,0 @@ -// Tests a very simple program. -// -// The features being tested is assertion -fn main(x : Field, y : pub Field) { - assert(x == y, "x and y are not equal"); - assert_eq(x, y, "x and y are not equal"); -} diff --git a/tooling/nargo_cli/tests/execution_success/assign_ex/src/main.nr b/tooling/nargo_cli/tests/execution_success/assign_ex/src/main.nr deleted file mode 100644 index 75cd841a301..00000000000 --- a/tooling/nargo_cli/tests/execution_success/assign_ex/src/main.nr +++ /dev/null @@ -1,15 +0,0 @@ -fn main(x: Field, y: Field) { - let mut z = x + y; - assert(z == 3); - z = x * y; - assert(z == 2); - - regression_3057(); -} - -// Ensure parsing parenthesized lvalues works -fn regression_3057() { - let mut array = [[0, 1], [2, 3]]; - (array[0])[1] = 2; - assert(array[0][1] == 2); -} 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 deleted file mode 100644 index 8b5074d5a24..00000000000 --- a/tooling/nargo_cli/tests/execution_success/bit_and/src/main.nr +++ /dev/null @@ -1,18 +0,0 @@ -// You can only do bit operations with integers. -// (Kobi/Daira/Circom/#37) https://github.com/iden3/circom/issues/37 -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: - let flag = (x == 0) & (y == 16); - assert(flag); - - //bitwise and with odd bits: - let x_as_u11 = x as u11; - let y_as_u11 = y as u11; - assert((x_as_u11 & y_as_u11) == x_as_u11); -} - 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 deleted file mode 100644 index f46c7ea1ea8..00000000000 --- a/tooling/nargo_cli/tests/execution_success/bit_shifts_comptime/src/main.nr +++ /dev/null @@ -1,23 +0,0 @@ -fn main(x: u64) { - let two: u64 = 2; - let three: u64 = 3; - - // shifts on constant values - 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); - - regression_2250(); -} - -fn regression_2250() { - let a: u1 = 1 >> 1; - assert(a == 0); - - let b: u32 = 1 >> 32; - 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 deleted file mode 100644 index b415a573ad5..00000000000 --- a/tooling/nargo_cli/tests/execution_success/bit_shifts_runtime/src/main.nr +++ /dev/null @@ -1,9 +0,0 @@ -fn main(x: u64, y: u64) { - // runtime shifts on compile-time known values - assert(64 << y == 128); - assert(64 >> y == 32); - - // runtime shifts on runtime values - assert(x << y == 128); - assert(x >> y == 32); -} diff --git a/tooling/nargo_cli/tests/execution_success/brillig_acir_as_brillig/src/main.nr b/tooling/nargo_cli/tests/execution_success/brillig_acir_as_brillig/src/main.nr deleted file mode 100644 index da5982dcd04..00000000000 --- a/tooling/nargo_cli/tests/execution_success/brillig_acir_as_brillig/src/main.nr +++ /dev/null @@ -1,42 +0,0 @@ -fn main(x: u32) { - assert(entry_point(x) == 2); - swap_entry_point(x, x + 1); - assert(deep_entry_point(x) == 4); -} - -fn inner(x : u32) -> u32 { - x + 1 -} - -unconstrained fn entry_point(x : u32) -> u32 { - inner(x + 1) -} - -fn swap(x: u32, y:u32) -> (u32, u32) { - (y, x) -} - -unconstrained fn swap_entry_point(x: u32, y: u32) { - let swapped = swap(x, y); - assert(swapped.0 == y); - assert(swapped.1 == x); - let swapped_twice = swap(swapped.0, swapped.1); - assert(swapped_twice.0 == x); - assert(swapped_twice.1 == y); -} - -fn level_3(x : u32) -> u32 { - x + 1 -} - -fn level_2(x : u32) -> u32 { - level_3(x + 1) -} - -fn level_1(x : u32) -> u32 { - level_2(x + 1) -} - -unconstrained fn deep_entry_point(x : u32) -> u32 { - level_1(x + 1) -} diff --git a/tooling/nargo_cli/tests/execution_success/brillig_assert/src/main.nr b/tooling/nargo_cli/tests/execution_success/brillig_assert/src/main.nr deleted file mode 100644 index d17c0b4f991..00000000000 --- a/tooling/nargo_cli/tests/execution_success/brillig_assert/src/main.nr +++ /dev/null @@ -1,12 +0,0 @@ -// Tests a very simple program. -// -// The features being tested is using assert on brillig -fn main(x: Field) { - assert(1 == conditional(x as bool)); -} - -unconstrained fn conditional(x : bool) -> Field { - assert(x, "x is false"); - assert_eq(x, true, "x is false"); - 1 -} diff --git a/tooling/nargo_cli/tests/execution_success/brillig_blake2s/src/main.nr b/tooling/nargo_cli/tests/execution_success/brillig_blake2s/src/main.nr deleted file mode 100644 index df20b6a9ce1..00000000000 --- a/tooling/nargo_cli/tests/execution_success/brillig_blake2s/src/main.nr +++ /dev/null @@ -1,12 +0,0 @@ -use dep::std; - -// Tests a very simple program. -// -// The features being tested is blake2s in brillig -fn main(x: [u8; 5], result: [u8; 32]) { - assert(blake2s(x) == result); -} - -unconstrained fn blake2s(x: [u8; 5]) -> [u8; 32] { - std::hash::blake2s(x) -} diff --git a/tooling/nargo_cli/tests/execution_success/brillig_calls/src/main.nr b/tooling/nargo_cli/tests/execution_success/brillig_calls/src/main.nr deleted file mode 100644 index 656d241d2f5..00000000000 --- a/tooling/nargo_cli/tests/execution_success/brillig_calls/src/main.nr +++ /dev/null @@ -1,58 +0,0 @@ -// Tests a very simple program. -// -// The features being tested is brillig calls -fn main(x: u32) { - assert(entry_point(x) == 2); - swap_entry_point(x, x + 1); - assert(deep_entry_point(x) == 4); - multiple_values_entry_point(x); -} - -unconstrained fn returns_multiple_values(x : u32) -> (u32, u32, u32, u32) { - (x + 1, x + 2, x + 3, x + 4) -} - -unconstrained fn multiple_values_entry_point(x: u32) { - let (a, b, c, d) = returns_multiple_values(x); - assert(a == x + 1); - assert(b == x + 2); - assert(c == x + 3); - assert(d == x + 4); -} - -unconstrained fn inner(x : u32) -> u32 { - x + 1 -} - -unconstrained fn entry_point(x : u32) -> u32 { - inner(x + 1) -} - -unconstrained fn swap(x: u32, y:u32) -> (u32, u32) { - (y, x) -} - -unconstrained fn swap_entry_point(x: u32, y: u32) { - let swapped = swap(x, y); - assert(swapped.0 == y); - assert(swapped.1 == x); - let swapped_twice = swap(swapped.0, swapped.1); - assert(swapped_twice.0 == x); - assert(swapped_twice.1 == y); -} - -unconstrained fn level_3(x : u32) -> u32 { - x + 1 -} - -unconstrained fn level_2(x : u32) -> u32 { - level_3(x + 1) -} - -unconstrained fn level_1(x : u32) -> u32 { - level_2(x + 1) -} - -unconstrained fn deep_entry_point(x : u32) -> u32 { - level_1(x + 1) -} diff --git a/tooling/nargo_cli/tests/execution_success/brillig_calls_array/src/main.nr b/tooling/nargo_cli/tests/execution_success/brillig_calls_array/src/main.nr deleted file mode 100644 index d5702ac4f82..00000000000 --- a/tooling/nargo_cli/tests/execution_success/brillig_calls_array/src/main.nr +++ /dev/null @@ -1,33 +0,0 @@ -// Tests a very simple program. -// -// The features being tested is brillig calls passing arrays around -fn main(x: [u32; 3]) { - assert(entry_point(x) == 9); - another_entry_point(x); -} - -unconstrained fn inner(x : [u32; 3]) -> [u32; 3] { - [x[0] + 1, x[1] + 1, x[2] + 1] -} - -unconstrained fn entry_point(x : [u32; 3]) -> u32 { - let y = inner(x); - y[0] + y[1] + y[2] -} - -unconstrained fn nested_fn_that_allocates(value: u32) -> u32 { - let x = [value, value, value]; - let y = inner(x); - y[0] + y[1] + y[2] -} - -unconstrained fn another_entry_point(x: [u32; 3]) { - assert(x[0] == 1); - assert(x[1] == 2); - assert(x[2] == 3); - assert(nested_fn_that_allocates(1) == 6); - // x should be unchanged - assert(x[0] == 1); - assert(x[1] == 2); - assert(x[2] == 3); -} 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 deleted file mode 100644 index e77d4d9479e..00000000000 --- a/tooling/nargo_cli/tests/execution_success/brillig_conditional/src/main.nr +++ /dev/null @@ -1,10 +0,0 @@ -// Tests a very simple program. -// -// The features being tested is basic conditonal on brillig -fn main(x: Field) { - assert(4 == conditional(x == 1)); -} - -unconstrained fn conditional(x : bool) -> Field { - if x { 4 } else { 5 } -} diff --git a/tooling/nargo_cli/tests/execution_success/brillig_ecdsa/src/main.nr b/tooling/nargo_cli/tests/execution_success/brillig_ecdsa/src/main.nr deleted file mode 100644 index 9b4627adf40..00000000000 --- a/tooling/nargo_cli/tests/execution_success/brillig_ecdsa/src/main.nr +++ /dev/null @@ -1,12 +0,0 @@ -use dep::std; - -// Tests a very simple program. -// -// The features being tested is ecdsa in brillig -fn main(hashed_message : [u8;32], pub_key_x : [u8;32], pub_key_y : [u8;32], signature : [u8;64]) { - assert(ecdsa(hashed_message, pub_key_x, pub_key_y, signature)); -} - -unconstrained fn ecdsa(hashed_message : [u8;32], pub_key_x : [u8;32], pub_key_y : [u8;32], signature : [u8;64]) -> bool { - std::ecdsa_secp256k1::verify_signature(pub_key_x, pub_key_y, signature, hashed_message) -} diff --git a/tooling/nargo_cli/tests/execution_success/brillig_fns_as_values/src/main.nr b/tooling/nargo_cli/tests/execution_success/brillig_fns_as_values/src/main.nr deleted file mode 100644 index d22959853b3..00000000000 --- a/tooling/nargo_cli/tests/execution_success/brillig_fns_as_values/src/main.nr +++ /dev/null @@ -1,36 +0,0 @@ -use dep::std; - -struct MyStruct { - operation: fn (u32) -> u32, -} - -fn main(x: u32) { - assert(wrapper(increment, x) == x + 1); - assert(wrapper(increment_acir, x) == x + 1); - assert(wrapper(decrement, x) == std::wrapping_sub(x, 1)); - assert(wrapper_with_struct(MyStruct { operation: increment }, x) == x + 1); - assert(wrapper_with_struct(MyStruct { operation: decrement }, x) == std::wrapping_sub(x, 1)); - // https://github.com/noir-lang/noir/issues/1975 - assert(increment(x) == x + 1); -} - -unconstrained fn wrapper(func: fn (u32) -> u32, param: u32) -> u32 { - func(param) -} - -unconstrained fn increment(x: u32) -> u32 { - x + 1 -} - -unconstrained fn decrement(x: u32) -> u32 { - x - 1 -} - -unconstrained fn wrapper_with_struct(my_struct: MyStruct, param: u32) -> u32 { - let func = my_struct.operation; - func(param) -} - -fn increment_acir(x: u32) -> u32 { - x + 1 -} diff --git a/tooling/nargo_cli/tests/execution_success/brillig_hash_to_field/src/main.nr b/tooling/nargo_cli/tests/execution_success/brillig_hash_to_field/src/main.nr deleted file mode 100644 index ee743c19ee5..00000000000 --- a/tooling/nargo_cli/tests/execution_success/brillig_hash_to_field/src/main.nr +++ /dev/null @@ -1,12 +0,0 @@ -use dep::std; - -// Tests a very simple program. -// -// The features being tested is hash_to_field in brillig -fn main(input: Field) -> pub Field { - hash_to_field(input) -} - -unconstrained fn hash_to_field(input : Field) -> Field { - std::hash::hash_to_field([input]) -} diff --git a/tooling/nargo_cli/tests/execution_success/brillig_identity_function/src/main.nr b/tooling/nargo_cli/tests/execution_success/brillig_identity_function/src/main.nr deleted file mode 100644 index ca646140f21..00000000000 --- a/tooling/nargo_cli/tests/execution_success/brillig_identity_function/src/main.nr +++ /dev/null @@ -1,33 +0,0 @@ -struct myStruct { - foo: Field, - foo_arr: [Field; 2], -} - -// Tests a very simple program. -// -// The features being tested is the identity function in Brillig -fn main(x : Field) { - assert(x == identity(x)); - // TODO: add support for array comparison - let arr = identity_array([x, x]); - assert(x == arr[0]); - assert(x == arr[1]); - - let s = myStruct { foo: x, foo_arr: [x, x] }; - let identity_struct = identity_struct(s); - assert(x == identity_struct.foo); - assert(x == identity_struct.foo_arr[0]); - assert(x == identity_struct.foo_arr[1]); -} - -unconstrained fn identity(x : Field) -> Field { - x -} - -unconstrained fn identity_array(arr : [Field; 2]) -> [Field; 2] { - arr -} - -unconstrained fn identity_struct(s : myStruct) -> myStruct { - s -} 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 deleted file mode 100644 index 38258af0adf..00000000000 --- a/tooling/nargo_cli/tests/execution_success/brillig_keccak/src/main.nr +++ /dev/null @@ -1,27 +0,0 @@ -use dep::std; - -// Tests a very simple program. -// -// The features being tested is keccak256 in brillig -fn main(x: Field, result: [u8; 32]) { - // We use the `as` keyword here to denote the fact that we want to take just the first byte from the x Field - // The padding is taken care of by the program - let digest = keccak256([x as u8], 1); - assert(digest == result); - - //#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); - - 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); - - assert(hash_a != hash_c); -} - -unconstrained fn keccak256(data: [u8; N], msg_len: u32) -> [u8; 32] { - std::hash::keccak256(data, msg_len) -} diff --git a/tooling/nargo_cli/tests/execution_success/brillig_not/src/main.nr b/tooling/nargo_cli/tests/execution_success/brillig_not/src/main.nr deleted file mode 100644 index 34ed2199902..00000000000 --- a/tooling/nargo_cli/tests/execution_success/brillig_not/src/main.nr +++ /dev/null @@ -1,11 +0,0 @@ -// Tests a very simple Brillig function. -// -// The features being tested is not instruction on brillig -fn main(x: Field, y : Field) { - assert(false == not_operator(x as bool)); - assert(true == not_operator(y as bool)); -} - -unconstrained fn not_operator(x : bool) -> bool { - !x -} diff --git a/tooling/nargo_cli/tests/execution_success/brillig_oracle/src/main.nr b/tooling/nargo_cli/tests/execution_success/brillig_oracle/src/main.nr deleted file mode 100644 index ece23996a65..00000000000 --- a/tooling/nargo_cli/tests/execution_success/brillig_oracle/src/main.nr +++ /dev/null @@ -1,29 +0,0 @@ -use dep::std::slice; - -// Tests oracle usage in brillig/unconstrained functions -fn main(x: Field) { - get_number_sequence_wrapper(20); -} - -// TODO(#1911): This function does not need to be an oracle but acts -// as a useful test while we finalize code generation for slices in Brillig -#[oracle(get_number_sequence)] -unconstrained fn get_number_sequence(_size: Field) -> [Field] {} - -// TODO(#1911) -#[oracle(get_reverse_number_sequence)] -unconstrained fn get_reverse_number_sequence(_size: Field) -> [Field] {} - -unconstrained fn get_number_sequence_wrapper(size: Field) { - let slice = get_number_sequence(size); - for i in 0..19 as u32 { - assert(slice[i] == i as Field); - } - - let reversed_slice = get_reverse_number_sequence(size); - // Regression test that we have not overwritten memory - for i in 0..19 as u32 { - assert(slice[i] == reversed_slice[19 - i]); - } -} - 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 deleted file mode 100644 index 1a793dd3f05..00000000000 --- a/tooling/nargo_cli/tests/execution_success/brillig_pedersen/src/main.nr +++ /dev/null @@ -1,24 +0,0 @@ -use dep::std; - -unconstrained fn main(x: Field, y: Field, salt: Field, out_x: Field, out_y: Field, out_hash: Field) { - let res = std::hash::pedersen_commitment_with_separator([x, y], 0); - assert(res.x == out_x); - assert(res.y == out_y); - - - let res_hash = std::hash::pedersen_hash_with_separator([x, y], 0); - assert_eq(res_hash, out_hash); - - assert(res_hash != res.x); - - - let raw_data = [x, y]; - let mut state = 0; - for i in 0..(2 as u32) { - state = state * 8 + raw_data[i]; - } - state += salt; - let hash = std::hash::pedersen_commitment_with_separator([state], 0); - assert(std::hash::pedersen_commitment_with_separator([43], 0).x == hash.x); -} - 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 deleted file mode 100644 index ebfc48c9198..00000000000 --- a/tooling/nargo_cli/tests/execution_success/brillig_recursion/src/main.nr +++ /dev/null @@ -1,10 +0,0 @@ -// Tests a very simple program. -// -// The feature being tested is brillig recursion -fn main(x: u32) { - assert(fibonacci(x) == 55); -} - -unconstrained fn fibonacci(x : u32) -> u32 { - 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 deleted file mode 100644 index 4118e4216a1..00000000000 --- a/tooling/nargo_cli/tests/execution_success/brillig_references/src/main.nr +++ /dev/null @@ -1,53 +0,0 @@ -unconstrained fn main(mut x: Field) { - add1(&mut x); - assert(x == 3); - - // https://github.com/noir-lang/noir/issues/1899 - // let mut s = S { y: x }; - // s.add2(); - // assert(s.y == 5); - - // Test that normal mutable variables are still copied - let mut a = 0; - mutate_copy(a); - assert(a == 0); - - // Test something 3 allocations deep - let mut nested_allocations = Nested { y: &mut &mut 0 }; - add1(*nested_allocations.y); - 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] } }; - *c.bar.array = [3, 4]; - let arr: [Field; 2] = *c.bar.array; - assert(arr[0] == 3); - assert(arr[1] == 4); -} - -unconstrained fn add1(x: &mut Field) { - *x += 1; -} - -struct S { y: Field } - -struct Nested { y: &mut &mut Field } - -struct C { - foo: Field, - bar: &mut C2, -} - -struct C2 { - array: &mut [Field; 2] -} - -impl S { - unconstrained fn add2(&mut self) { - self.y += 2; - } -} - -unconstrained fn mutate_copy(mut a: Field) { - a = 7; -} 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 deleted file mode 100644 index c85bc433a76..00000000000 --- a/tooling/nargo_cli/tests/execution_success/brillig_scalar_mul/src/main.nr +++ /dev/null @@ -1,22 +0,0 @@ -use dep::std; - -unconstrained fn main( - a: Field, - a_pub_x: pub Field, - a_pub_y: pub Field, - b: Field, - b_pub_x: pub Field, - b_pub_y: pub Field -) { - let mut priv_key = a; - let mut pub_x: Field = a_pub_x; - let mut pub_y: Field = a_pub_y; - if a != 1 { // Change `a` in Prover.toml to test input `b` - 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); -} 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 deleted file mode 100644 index f30299b8a7b..00000000000 --- a/tooling/nargo_cli/tests/execution_success/brillig_schnorr/src/main.nr +++ /dev/null @@ -1,21 +0,0 @@ -use dep::std; - -// Note: If main has any unsized types, then the verifier will never be able -// to figure out the circuit instance -unconstrained fn main(message: [u8; 10], message_field: Field, pub_key_x: Field, pub_key_y: Field, signature: [u8; 64]) { - // Regression for issue #2421 - // We want to make sure that we can accurately verify a signature whose message is a slice vs. an array - let message_field_bytes = message_field.to_be_bytes(10); - for i in 0..10 { - assert(message[i] == message_field_bytes[i]); - } - // 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); - 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); - assert(valid_signature); -} diff --git a/tooling/nargo_cli/tests/execution_success/brillig_sha256/src/main.nr b/tooling/nargo_cli/tests/execution_success/brillig_sha256/src/main.nr deleted file mode 100644 index 077ff70048e..00000000000 --- a/tooling/nargo_cli/tests/execution_success/brillig_sha256/src/main.nr +++ /dev/null @@ -1,15 +0,0 @@ -use dep::std; - -// Tests a very simple program. -// -// The features being tested is sha256 in brillig -fn main(x: Field, result: [u8; 32]) { - assert(result == sha256(x)); -} - -unconstrained fn sha256(x: Field) -> [u8; 32] { - // We use the `as` keyword here to denote the fact that we want to take just the first byte from the x Field - // The padding is taken care of by the program - std::hash::sha256([x as u8]) -} - diff --git a/tooling/nargo_cli/tests/execution_success/brillig_slices/src/main.nr b/tooling/nargo_cli/tests/execution_success/brillig_slices/src/main.nr deleted file mode 100644 index 403956bc23d..00000000000 --- a/tooling/nargo_cli/tests/execution_success/brillig_slices/src/main.nr +++ /dev/null @@ -1,150 +0,0 @@ -use dep::std::slice; -unconstrained fn main(x: Field, y: Field) { - let mut slice: [Field] = [y, x]; - assert(slice.len() == 2); - - slice = slice.push_back(7); - assert(slice.len() == 3); - assert(slice[0] == y); - assert(slice[1] == x); - assert(slice[2] == 7); - - // Array set on slice target - slice[0] = x; - slice[1] = y; - slice[2] = 1; - - assert(slice[0] == x); - assert(slice[1] == y); - assert(slice[2] == 1); - - slice = push_front_to_slice(slice, 2); - assert(slice.len() == 4); - assert(slice[0] == 2); - assert(slice[1] == x); - assert(slice[2] == y); - assert(slice[3] == 1); - - let (item, popped_front_slice) = slice.pop_front(); - slice = popped_front_slice; - assert(item == 2); - - assert(slice.len() == 3); - assert(slice[0] == x); - assert(slice[1] == y); - assert(slice[2] == 1); - - let (popped_back_slice, another_item) = slice.pop_back(); - slice = popped_back_slice; - assert(another_item == 1); - - assert(slice.len() == 2); - assert(slice[0] == x); - assert(slice[1] == y); - - slice = slice.insert(1, 2); - assert(slice.len() == 3); - assert(slice[0] == x); - assert(slice[1] == 2); - assert(slice[2] == y); - - let (removed_slice, should_be_2) = slice.remove(1); - slice = removed_slice; - assert(should_be_2 == 2); - - assert(slice.len() == 2); - assert(slice[0] == x); - assert(slice[1] == y); - - let (slice_with_only_x, should_be_y) = slice.remove(1); - slice = slice_with_only_x; - assert(should_be_y == y); - - assert(slice.len() == 1); - assert(slice[0] == x); - - let (empty_slice, should_be_x) = slice.remove(0); - assert(should_be_x == x); - assert(empty_slice.len() == 0); - - regression_merge_slices(x, y); -} - -// Tests slice passing to/from functions -unconstrained fn push_front_to_slice(slice: [T], item: T) -> [T] { - slice.push_front(item) -} - -// The parameters to this function must come from witness values (inputs to main) -unconstrained fn regression_merge_slices(x: Field, y: Field) { - merge_slices_if(x, y); - merge_slices_else(x); -} - -unconstrained fn merge_slices_if(x: Field, y: Field) { - let slice = merge_slices_return(x, y); - assert(slice[2] == 10); - assert(slice.len() == 3); - - let slice = merge_slices_mutate(x, y); - assert(slice[3] == 5); - assert(slice.len() == 4); - - let slice = merge_slices_mutate_in_loop(x, y); - assert(slice[6] == 4); - assert(slice.len() == 7); -} - -unconstrained fn merge_slices_else(x: Field) { - let slice = merge_slices_return(x, 5); - assert(slice[0] == 0); - assert(slice[1] == 0); - assert(slice.len() == 2); - - let slice = merge_slices_mutate(x, 5); - assert(slice[2] == 5); - assert(slice.len() == 3); - - let slice = merge_slices_mutate_in_loop(x, 5); - assert(slice[2] == 5); - assert(slice.len() == 3); -} - -// Test returning a merged slice without a mutation -unconstrained 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 - } -} - -// Test mutating a slice inside of an if statement -unconstrained fn merge_slices_mutate(x: Field, y: Field) -> [Field] { - let mut slice = [0; 2]; - if x != y { - slice = slice.push_back(y); - slice = slice.push_back(x); - } else { - slice = slice.push_back(x); - } - slice -} - -// Test mutating a slice inside of a loop in an if statement -unconstrained fn merge_slices_mutate_in_loop(x: Field, y: Field) -> [Field] { - let mut slice = [0; 2]; - if x != y { - for i in 0..5 { - slice = slice.push_back(i); - } - } else { - slice = slice.push_back(x); - } - slice -} diff --git a/tooling/nargo_cli/tests/execution_success/brillig_to_be_bytes/src/main.nr b/tooling/nargo_cli/tests/execution_success/brillig_to_be_bytes/src/main.nr deleted file mode 100644 index d1e1cb9c9a5..00000000000 --- a/tooling/nargo_cli/tests/execution_success/brillig_to_be_bytes/src/main.nr +++ /dev/null @@ -1,12 +0,0 @@ -unconstrained fn main(x : Field) -> pub [u8; 31] { - // The result of this byte array will be big-endian - let byte_array = x.to_be_bytes(31); - let mut bytes = [0; 31]; - for i in 0..31 { - bytes[i] = byte_array[i]; - } - assert(bytes[30] == 60); - assert(bytes[29] == 33); - assert(bytes[28] == 31); - bytes -} 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 deleted file mode 100644 index bc1b96a40b5..00000000000 --- a/tooling/nargo_cli/tests/execution_success/brillig_to_bytes_integration/src/main.nr +++ /dev/null @@ -1,27 +0,0 @@ -use dep::std; - -unconstrained fn main(x : Field, _y: Field) { - // The result of this byte array will be big-endian - let y: Field = 2040124; - 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]); - assert(le_byte_array[2] == be_byte_array[28]); - - let z = 0 - 1; - let p_bytes = std::field::modulus_le_bytes(); - let z_bytes = z.to_le_bytes(32); - assert(p_bytes[10] == z_bytes[10]); - assert(p_bytes[0] == z_bytes[0] as u8 + 1 as u8); - - let p_bits = std::field::modulus_le_bits(); - let z_bits = z.to_le_bits(std::field::modulus_num_bits() as u32); - assert(z_bits[0] == 0); - assert(p_bits[100] == z_bits[100]); - - _y.to_le_bits(std::field::modulus_num_bits() as u32); -} diff --git a/tooling/nargo_cli/tests/execution_success/brillig_to_le_bytes/src/main.nr b/tooling/nargo_cli/tests/execution_success/brillig_to_le_bytes/src/main.nr deleted file mode 100644 index 1b1315ea411..00000000000 --- a/tooling/nargo_cli/tests/execution_success/brillig_to_le_bytes/src/main.nr +++ /dev/null @@ -1,10 +0,0 @@ -unconstrained fn main(x : Field) -> pub [u8; 31] { - // The result of this byte array will be little-endian - let byte_array = x.to_le_bytes(31); - assert(byte_array.len() == 31); - let mut bytes = [0; 31]; - for i in 0..31 { - bytes[i] = byte_array[i]; - } - bytes -} diff --git a/tooling/nargo_cli/tests/execution_success/closures_mut_ref/src/main.nr b/tooling/nargo_cli/tests/execution_success/closures_mut_ref/src/main.nr deleted file mode 100644 index 99de3dd31e4..00000000000 --- a/tooling/nargo_cli/tests/execution_success/closures_mut_ref/src/main.nr +++ /dev/null @@ -1,31 +0,0 @@ -fn main(mut x: Field) { - let one = 1; - let add1 = |z| { - *z = *z + one; - }; - - let two = 2; - let add2 = |z| { - *z = *z + two; - }; - - add1(&mut x); - assert(x == 1); - - add2(&mut x); - assert(x == 3); - - issue_2120(); -} - -// https://github.com/noir-lang/noir/issues/2120 -fn issue_2120() { - let x1 = &mut 42; - let set_x1 = |y| { *x1 = y; }; - - assert(*x1 == 42); - set_x1(44); - assert(*x1 == 44); - set_x1(*x1); - assert(*x1 == 44); -} 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 deleted file mode 100644 index bd29f135082..00000000000 --- a/tooling/nargo_cli/tests/execution_success/conditional_1/src/main.nr +++ /dev/null @@ -1,97 +0,0 @@ -use dep::std; - -fn sort(mut a: [u32; 4]) -> [u32; 4] { - for i in 1..4 { - for j in 0..i { - if a[i] < a[j] { - let c = a[j]; - a[j] = a[i]; - a[i] = c; - } - } - } - a -} - -fn must_be_zero(x: u8) { - assert(x == 0); -} - -fn main(a: u32, mut c: [u32; 4], x: [u8; 5], result: pub [u8; 32]) { - //Test case for short-circuit - let mut data = [0 as u32; 32]; - let mut ba = a; - for i in 0..32 { - let i_u32 = i as u32; - if i_u32 == a { - for j in 0..4 { - data[i + j] = c[4 - 1 - j]; - for k in 0..4 { - ba = ba +data[k]; - } - if ba == 4864 { - c[3]=ba; - } - } - } - } - assert(data[31] == 0); - assert(ba != 13); - - - //Test case for conditional with arrays from function parameters - let b = sort([1, 2, 3, 4]); - assert(b[0] == 1); - - if a == 0 { - must_be_zero(0); - c[0] = 3; - } else { - must_be_zero(1); - c[0] = 1; - c[1] = c[2] / a + 11 % a; - let f1 = a as Field; - assert(10 / f1 != 0); - } - assert(c[0] == 3); - - let mut y = 0; - if a == 0 { - let digest = std::hash::sha256(x); - y = digest[0]; - } else { - y = 5; - } - assert(y == result[0]); - c = sort(c); - assert(c[0] == 0); - - //test 1 - let mut x: u32 = 0; - if a == 0 { - c[0] = 12; - if a != 0 { - x = 6; - } else { - x = 2; - assert(x == 2); - } - } else { - x = 5; - assert(x == 5); - } - if c[0] == 0 { - x = 3; - } - assert(x == 2); - - //test2: loops - let mut x: u32 = 0; - x = a - a; - for i in 0..4 { - if c[i] == 0 { - x = i as u32 +2; - } - } - assert(x == 0); -} 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 deleted file mode 100644 index c4d60d8b999..00000000000 --- a/tooling/nargo_cli/tests/execution_success/conditional_2/src/main.nr +++ /dev/null @@ -1,52 +0,0 @@ -use dep::std; - -fn must_be_zero(x: u8) { - assert(x == 0); -} - -fn test3 (x: u8) { - if x == 0 { - must_be_zero(x); - } -} - -fn test4() -> [u32; 4] { - let b: [u32; 4] = [1, 2, 3, 4]; - b -} - -fn main(a: u32, mut c: [u32; 4]) { - test3(1); - - if a == 0 { - c = test4(); - } else { - assert(c[1] != 2); - } - if false { - c[1] = 5; - } - assert(c[1] == 2); - - test5(4); - - // Test case for function synchronisation - let mut c_sync = 0; - if a == 42 { - c_sync = foo2(); - } else { - c_sync = foo2() + foo2(); - } - assert(c_sync == 6); -} - -fn test5(a : u32) { - if a > 1 { - let q = a / 2; - assert(q == 2); - } -} - -fn foo2() -> Field { - 3 -} 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 deleted file mode 100644 index 5d8734897d7..00000000000 --- a/tooling/nargo_cli/tests/execution_success/conditional_regression_661/src/main.nr +++ /dev/null @@ -1,28 +0,0 @@ -fn main(a: u32, mut c: [u32; 4]) { - // Regression for issue #661: - let mut c_661: [u32; 1] = [0]; - if a > 5 { - c_661 = issue_661_foo(issue_661_bar(c), a); - } else { - c_661 = issue_661_foo(issue_661_bar(c), a + 2); - } - assert(c_661[0] < 20000); -} - -fn test5(a : u32) { - if a > 1 { - let q = a / 2; - assert(q == 2); - } -} - -fn issue_661_foo(array: [u32;4], b:u32) ->[u32;1] { - [array[0] + b] -} - -fn issue_661_bar(a : [u32;4]) ->[u32;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 deleted file mode 100644 index b119619990e..00000000000 --- a/tooling/nargo_cli/tests/execution_success/conditional_regression_short_circuit/src/main.nr +++ /dev/null @@ -1,38 +0,0 @@ -use dep::std; - -fn main(a: u32, mut c: [u32; 4], x: [u8; 5], result: pub [u8; 32]) { - //regression for short-circuit2 - if 35 == a { - assert(false); - } - bar(a as Field); - - if a == 3 { - c = test4(); - } - assert(c[1] != 2); - call_intrinsic(x, result); -} - -fn foo() { - let mut x = 1; - x /= 0; -} - -fn bar(x:Field) { - if x == 15 { - foo(); - } -} - -fn call_intrinsic(x: [u8; 5], result: [u8; 32]) { - let mut digest = std::hash::sha256(x); - digest[0] = 5 as u8; - digest = std::hash::sha256(x); - assert(digest == result); -} - -fn test4() -> [u32; 4] { - let b: [u32; 4] = [1, 2, 3, 4]; - b -} diff --git a/tooling/nargo_cli/tests/execution_success/debug_logs/src/main.nr b/tooling/nargo_cli/tests/execution_success/debug_logs/src/main.nr deleted file mode 100644 index 23c6b8a76d4..00000000000 --- a/tooling/nargo_cli/tests/execution_success/debug_logs/src/main.nr +++ /dev/null @@ -1,82 +0,0 @@ -use dep::std; - -fn main(x : Field, y : pub Field) { - let string = "i: {i}, j: {j}"; - std::println(string); - - // A `fmtstr` lets you easily perform string interpolation. - let fmt_str: fmtstr<14, (Field, Field)> = f"i: {x}, j: {y}"; - let fmt_str = string_identity(fmt_str); - std::println(fmt_str); - - let fmt_str_no_type = f"i: {x}, j: {y}"; - std::println(fmt_str_no_type); - - let fmt_str_generic = string_with_generics(fmt_str_no_type); - std::println(fmt_str_generic); - - let s = myStruct { y: x, x: y }; - std::println(s); - - std::println(f"randomstring{x}{x}"); - - let fmt_str = string_with_partial_generics(f"i: {x}, s: {s}"); - std::println(fmt_str); - - std::println(x); - std::println([x, y]); - - let foo = fooStruct { my_struct: s, foo: 15 }; - std::println(f"s: {s}, foo: {foo}"); - - std::println(f"x: 0, y: 1"); - - let s_2 = myStruct { x: 20, y: 30 }; - std::println(f"s1: {s}, s2: {s_2}"); - - let bar = fooStruct { my_struct: s_2, foo: 20 }; - std::println(f"foo1: {foo}, foo2: {bar}"); - - let struct_string = if x != 5 { f"{foo}" } else { f"{bar}" }; - std::println(struct_string); - - regression_2906(); -} - -fn string_identity(string: fmtstr<14, (Field, Field)>) -> fmtstr<14, (Field, Field)> { - string -} - -fn string_with_generics(string: fmtstr) -> fmtstr { - string -} - -fn string_with_partial_generics(string: fmtstr) -> fmtstr { - string -} - -struct myStruct { - y: Field, - x: Field, -} - -struct fooStruct { - my_struct: myStruct, - foo: Field, -} - -fn regression_2906() { - let array_two_vals = [1, 2]; - dep::std::println(f"array_two_vals: {array_two_vals}"); - - let label_two_vals = "12"; - dep::std::println(f"label_two_vals: {label_two_vals}"); - - let array_five_vals = [1, 2, 3, 4, 5]; - dep::std::println(f"array_five_vals: {array_five_vals}"); - - let label_five_vals = "12345"; - dep::std::println(f"label_five_vals: {label_five_vals}"); - - dep::std::println(f"array_five_vals: {array_five_vals}, label_five_vals: {label_five_vals}"); -} diff --git a/tooling/nargo_cli/tests/execution_success/diamond_deps_0/src/main.nr b/tooling/nargo_cli/tests/execution_success/diamond_deps_0/src/main.nr deleted file mode 100644 index 1c4dba1c791..00000000000 --- a/tooling/nargo_cli/tests/execution_success/diamond_deps_0/src/main.nr +++ /dev/null @@ -1,7 +0,0 @@ -use dep::dep1::call_dep1_then_dep2; -use dep::dep2::call_dep2; -use dep::dep2::RESOLVE_THIS; - -fn main(x : Field, y : pub Field) -> pub Field { - call_dep1_then_dep2(x, y) + call_dep2(x, y) + RESOLVE_THIS -} 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 deleted file mode 100644 index 92e8326a895..00000000000 --- a/tooling/nargo_cli/tests/execution_success/double_verify_proof/src/main.nr +++ /dev/null @@ -1,24 +0,0 @@ -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.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 - ); - - let mut output = [0; 16]; - for i in 0..16 { - output[i] = output_aggregation_object[i]; - } - output -} 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 deleted file mode 100644 index 38a6ebd13e5..00000000000 --- a/tooling/nargo_cli/tests/execution_success/ecdsa_secp256k1/src/main.nr +++ /dev/null @@ -1,10 +0,0 @@ -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); - - let valid_signature = std::ecdsa_secp256k1::verify_signature(pub_key_x, pub_key_y, signature, hashed_message); - 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 deleted file mode 100644 index 840a401f109..00000000000 --- a/tooling/nargo_cli/tests/execution_success/ecdsa_secp256r1/src/main.nr +++ /dev/null @@ -1,6 +0,0 @@ -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); -} diff --git a/tooling/nargo_cli/tests/execution_success/eddsa/src/main.nr b/tooling/nargo_cli/tests/execution_success/eddsa/src/main.nr deleted file mode 100644 index 724ff1a640c..00000000000 --- a/tooling/nargo_cli/tests/execution_success/eddsa/src/main.nr +++ /dev/null @@ -1,53 +0,0 @@ -use dep::std::compat; -use dep::std::ec::consts::te::baby_jubjub; -use dep::std::hash; -use dep::std::eddsa::eddsa_poseidon_verify; -fn main(msg: pub Field, _priv_key_a: Field, _priv_key_b: Field) { - // Skip this test for non-bn254 backends - if compat::is_bn254() { - let bjj = baby_jubjub(); - - let pub_key_a = bjj.curve.mul(_priv_key_a, bjj.curve.gen); - // let pub_key_b = bjj.curve.mul(_priv_key_b, bjj.curve.gen); - - // Manually computed as fields can't use modulo. Importantantly the commitment is within - // the subgroup order. Note that choice of hash is flexible for this step. - // let r_a = hash::pedersen_commitment([_priv_key_a, msg])[0] % bjj.suborder; // modulus computed manually - let r_a = 1414770703199880747815475415092878800081323795074043628810774576767372531818; - // let r_b = hash::pedersen_commitment([_priv_key_b, msg])[0] % bjj.suborder; // modulus computed manually - let r_b = 571799555715456644614141527517766533395606396271089506978608487688924659618; - - let r8_a = bjj.curve.mul(r_a, bjj.base8); - let r8_b = bjj.curve.mul(r_b, bjj.base8); - - // let h_a: [Field; 6] = hash::poseidon::bn254::hash_5([ - // r8_a.x, - // r8_a.y, - // pub_key_a.x, - // pub_key_a.y, - // msg, - // ]); - - // let h_b: [Field; 6] = hash::poseidon::bn254::hash_5([ - // r8_b.x, - // r8_b.y, - // pub_key_b.x, - // pub_key_b.y, - // msg, - // ]); - - // let s_a = (r_a + _priv_key_a * h_a) % bjj.suborder; // modulus computed manually - let s_a = 30333430637424319196043722294837632681219980330991241982145549329256671548; - // let s_b = (r_b + _priv_key_b * h_b) % bjj.suborder; // modulus computed manually - let s_b = 1646085314320208098241070054368798527940102577261034947654839408482102287019; - - // User A verifies their signature over the message - assert(eddsa_poseidon_verify(pub_key_a.x, pub_key_a.y, s_a, r8_a.x, r8_a.y, msg)); - - // User B's signature over the message can't be used with user A's pub key - assert(!eddsa_poseidon_verify(pub_key_a.x, pub_key_a.y, s_b, r8_b.x, r8_b.y, msg)); - - // User A's signature over the message can't be used with another message - assert(!eddsa_poseidon_verify(pub_key_a.x, pub_key_a.y, s_a, r8_a.x, r8_a.y, msg + 1)); - } -} diff --git a/tooling/nargo_cli/tests/execution_success/generics/src/main.nr b/tooling/nargo_cli/tests/execution_success/generics/src/main.nr deleted file mode 100644 index 9a1effcd318..00000000000 --- a/tooling/nargo_cli/tests/execution_success/generics/src/main.nr +++ /dev/null @@ -1,70 +0,0 @@ -struct Bar { - one: Field, - two: Field, - other: T, -} - -fn foo(bar: Bar) { - assert(bar.one == bar.two); -} - -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 - } -} - -impl Bar { - fn get_other(self) -> Field { - self.other - } -} - -fn main(x: Field, y: Field) { - let bar1: Bar = Bar { one: x, two: y, other: 0 }; - let bar2 = Bar { one: x, two: y, other: [0] }; - - foo(bar1); - foo(bar2); - - // Test generic impls - let int1 = BigInt { limbs: [1] }; - let int2 = BigInt { limbs: [2] }; - let BigInt { limbs } = int1.second(int2).first(int1); - assert(limbs == int2.limbs); - - // Test impl exclusively for Bar - assert(bar1.get_other() == bar1.other); - - // Expected type error - // assert(bar2.get_other() == bar2.other); - let one = x; - let two = y; - let nested_generics: Bar> = Bar { one, two, other: Bar { one, two, other: 0 } }; - assert(nested_generics.other.other == bar1.get_other()); - - let _ = regression_2055([1, 2, 3]); -} - -fn regression_2055(bytes: [u8; LEN]) -> Field { - let mut f = 0; - let mut b = 1; - let mut len = LEN - 1; // FAILS - for i in 0..LEN { - let j = len - i; - f += (bytes[j] as Field) * b; - b *= 256; - } - f -} diff --git a/tooling/nargo_cli/tests/execution_success/global_consts/src/baz.nr b/tooling/nargo_cli/tests/execution_success/global_consts/src/baz.nr deleted file mode 100644 index d5b13407574..00000000000 --- a/tooling/nargo_cli/tests/execution_success/global_consts/src/baz.nr +++ /dev/null @@ -1,5 +0,0 @@ -pub fn from_baz(x : [Field; crate::foo::MAGIC_NUMBER]) { - for i in 0..crate::foo::MAGIC_NUMBER { - assert(x[i] == crate::foo::MAGIC_NUMBER); - }; -} diff --git a/tooling/nargo_cli/tests/execution_success/global_consts/src/foo.nr b/tooling/nargo_cli/tests/execution_success/global_consts/src/foo.nr deleted file mode 100644 index 2d28111728b..00000000000 --- a/tooling/nargo_cli/tests/execution_success/global_consts/src/foo.nr +++ /dev/null @@ -1,11 +0,0 @@ -mod bar; - -global N: Field = 5; -global MAGIC_NUMBER: Field = 3; -global TYPE_INFERRED = 42; - -pub fn from_foo(x : [Field; bar::N]) { - for i in 0..bar::N { - assert(x[i] == bar::N); - }; -} diff --git a/tooling/nargo_cli/tests/execution_success/global_consts/src/foo/bar.nr b/tooling/nargo_cli/tests/execution_success/global_consts/src/foo/bar.nr deleted file mode 100644 index b4b43442cc8..00000000000 --- a/tooling/nargo_cli/tests/execution_success/global_consts/src/foo/bar.nr +++ /dev/null @@ -1,5 +0,0 @@ -global N: Field = 5; - -pub fn from_bar(x : Field) -> Field { - x * N -} 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 deleted file mode 100644 index 07d88cb2049..00000000000 --- a/tooling/nargo_cli/tests/execution_success/global_consts/src/main.nr +++ /dev/null @@ -1,93 +0,0 @@ -mod foo; -mod baz; - -global M: Field = 32; -global L: Field = 10; // Unused globals currently allowed -global N: Field = 5; -global T_LEN = 2; // Type inference is allowed on globals -//global N: Field = 5; // Uncomment to see duplicate globals error - -struct Dummy { - x: [Field; N], - y: [Field; foo::MAGIC_NUMBER] -} - -struct Test { - v: Field, -} -global VALS: [Test; 1] = [Test { v: 100 }]; -global NESTED = [VALS, VALS]; - -fn main(a: [Field; M + N - N], b: [Field; 30 + N / 2], c : pub [Field; foo::MAGIC_NUMBER], d: [Field; foo::bar::N]) { - let test_struct = Dummy { x: d, y: c }; - - for i in 0..foo::MAGIC_NUMBER { - assert(c[i] == foo::MAGIC_NUMBER); - assert(test_struct.y[i] == foo::MAGIC_NUMBER); - assert(test_struct.y[i] != NESTED[1][0].v); - } - - assert(N != M); - - let expected: u32 = 42; - assert(foo::TYPE_INFERRED == expected); - - let mut y = 5; - let mut x = M; - for i in 0..N*N { - let M: Field = 10; - x = M; - - y = i; - } - assert(y == 24); - assert(x == 10); - - let q = multiplyByM(3); - assert(q == 96); - - arrays_neq(a, b); - - let t: [Field; T_LEN] = [N, M]; - assert(t[1] == 32); - - assert(15 == mysubmodule::my_helper()); - - let add_submodules_N = mysubmodule::N + foo::bar::N; - assert(15 == add_submodules_N); - let add_from_bar_N = mysubmodule::N + foo::bar::from_bar(1); - 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); - - let arr: [Field; mysubmodule::N] = [N; 10]; - assert((arr[0] == 5) & (arr[9] == 5)); - - foo::from_foo(d); - baz::from_baz(c); -} - -fn multiplyByM(x: Field) -> Field { - x * M -} - -fn arrays_neq(a: [Field; M], b: [Field; M]) { - assert(a != b); -} - -mod mysubmodule { - global N: Field = 10; - global L: Field = 50; - - fn my_bool_or(x: u1, y: u1) { - assert(x | y == 1); - } - - pub fn my_helper() -> Field { - let N: Field = 15; // Like in Rust, local variables override globals - let x = N; - x - } -} diff --git a/tooling/nargo_cli/tests/execution_success/hash_to_field/src/main.nr b/tooling/nargo_cli/tests/execution_success/hash_to_field/src/main.nr deleted file mode 100644 index 2b7d59cd8b6..00000000000 --- a/tooling/nargo_cli/tests/execution_success/hash_to_field/src/main.nr +++ /dev/null @@ -1,5 +0,0 @@ -use dep::std; - -fn main(input : Field) -> pub Field { - std::hash::hash_to_field([input]) -} \ No newline at end of file diff --git a/tooling/nargo_cli/tests/execution_success/higher_order_functions/src/main.nr b/tooling/nargo_cli/tests/execution_success/higher_order_functions/src/main.nr deleted file mode 100644 index 71eacfe4da5..00000000000 --- a/tooling/nargo_cli/tests/execution_success/higher_order_functions/src/main.nr +++ /dev/null @@ -1,113 +0,0 @@ -fn main(w: Field) -> pub Field { - let f = if 3 * 7 > 200 as u32 { foo } else { bar }; - assert(f()[1] == 2); - // Lambdas: - assert(twice(|x| x * 2, 5) == 20); - assert((|x, y| x + y + 1)(2, 3) == 6); - - // nested lambdas - assert((|a, b| { - a + (|c| c + 2)(b) - })(0, 1) == 3); - - - // Closures: - let a = 42; - let g = || a; - assert(g() == 42); - - // When you copy mutable variables, - // the capture of the copies shouldn't change: - let mut x = 2; - x = x + 1; - let z = x; - - // Add extra mutations to ensure we can mutate x without the - // captured z changing. - x = x + 1; - assert((|y| y + z)(1) == 4); - - // When you capture mutable variables, - // again, the captured variable doesn't change: - let closure_capturing_mutable = (|y| y + x); - assert(closure_capturing_mutable(1) == 5); - x += 1; - assert(closure_capturing_mutable(1) == 5); - - regression_2154(); - - let ret = twice(add1, 3); - - test_array_functions(); - w + ret -} - -/// Test the array functions in std::array -fn test_array_functions() { - let two = 2; // giving this a name, to ensure that the Option functions work with closures - - let myarray: [i32; 3] = [1, 2, 3]; - assert(myarray.any(|n| n > 2)); - assert(myarray.any(|n| n > two)); - - let evens: [i32; 3] = myarray.map(|n| n * two); // [2, 4, 6] - - assert(evens.all(|n| n > 1)); - assert(evens.all(|n| n >= two)); - - assert(evens.fold(0, |a, b| a + b) == 12); - assert(evens.fold(0, |a, b| a + b + two) == 18); - assert(evens.reduce(|a, b| a + b) == 12); - assert(evens.reduce(|a, b| a + b + two) == 16); - - // TODO: is this a sort_via issue with the new backend, - // or something more general? - // - // currently it fails only with `--experimental-ssa` with - // "not yet implemented: Cast into signed" - // but it worked with the original ssa backend - // (before dropping it) - // - // opened #2121 for it - // https://github.com/noir-lang/noir/issues/2121 - - // let descending = myarray.sort_via(|a, b| a > b); - // assert(descending == [3, 2, 1]); - - assert(evens.map(|n| n / 2) == myarray); - assert(evens.map(|n| n / two) == myarray); -} - -fn foo() -> [u32; 2] { - [1, 3] -} - -fn bar() -> [u32; 2] { - [3, 2] -} - -fn add1(x: Field) -> Field { - x + 1 -} - -fn twice(f: fn(Field) -> Field, x: Field) -> Field { - f(f(x)) -} - -// Fixing an ICE, where rewriting the closures -// during monomorphization didn't correspond -// to an internal `if` type -// found by @jfecher: -// https://github.com/noir-lang/noir/pull/1959#issuecomment-1658992989 -// issue https://github.com/noir-lang/noir/issues/2154 -fn regression_2154() { - let x: u32 = 32; - - let closure_if_else = if x > 2 { - || x - } else { - || x + 2342 - }; - - assert(closure_if_else() == 32); -} diff --git a/tooling/nargo_cli/tests/execution_success/import/src/import.nr b/tooling/nargo_cli/tests/execution_success/import/src/import.nr deleted file mode 100644 index 4df05bbc288..00000000000 --- a/tooling/nargo_cli/tests/execution_success/import/src/import.nr +++ /dev/null @@ -1,3 +0,0 @@ -pub fn hello(x : Field) -> Field { - x -} diff --git a/tooling/nargo_cli/tests/execution_success/import/src/main.nr b/tooling/nargo_cli/tests/execution_success/import/src/main.nr deleted file mode 100644 index b85ee74ff69..00000000000 --- a/tooling/nargo_cli/tests/execution_success/import/src/main.nr +++ /dev/null @@ -1,9 +0,0 @@ -mod import; -use crate::import::hello; - -fn main(x : Field, y : Field) { - let _k = dep::std::hash::pedersen_commitment([x]); - let _l = hello(x); - - assert(x != import::hello(y)); -} diff --git a/tooling/nargo_cli/tests/execution_success/keccak256/src/main.nr b/tooling/nargo_cli/tests/execution_success/keccak256/src/main.nr deleted file mode 100644 index 511d668bc3d..00000000000 --- a/tooling/nargo_cli/tests/execution_success/keccak256/src/main.nr +++ /dev/null @@ -1,22 +0,0 @@ -// Keccak256 example -// -use dep::std; - -fn main(x: Field, result: [u8; 32]) { - // We use the `as` keyword here to denote the fact that we want to take just the first byte from the x Field - // The padding is taken care of by the program - let digest = std::hash::keccak256([x as u8], 1); - assert(digest == result); - - //#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); - - 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); - - assert(hash_a != hash_c); -} diff --git a/tooling/nargo_cli/tests/execution_success/main_bool_arg/src/main.nr b/tooling/nargo_cli/tests/execution_success/main_bool_arg/src/main.nr deleted file mode 100644 index 0615a7dbca4..00000000000 --- a/tooling/nargo_cli/tests/execution_success/main_bool_arg/src/main.nr +++ /dev/null @@ -1,8 +0,0 @@ -fn main(x : bool, y: [bool;2]) { - if x { - assert(1 != 2); - } - - assert(x); - assert(y[0] != y[1]); -} diff --git a/tooling/nargo_cli/tests/execution_success/merkle_insert/src/main.nr b/tooling/nargo_cli/tests/execution_success/merkle_insert/src/main.nr deleted file mode 100644 index 87b8533c274..00000000000 --- a/tooling/nargo_cli/tests/execution_success/merkle_insert/src/main.nr +++ /dev/null @@ -1,22 +0,0 @@ -use dep::std; -use dep::std::hash::mimc; - -fn main( - old_root: Field, - old_leaf: Field, - old_hash_path: [Field; 3], - new_root: pub Field, - leaf: Field, - index: Field, - mimc_input: [Field; 4], -) { - assert(old_root == std::merkle::compute_merkle_root(old_leaf, index, old_hash_path)); - - let calculated_root = std::merkle::compute_merkle_root(leaf, index, old_hash_path); - assert(new_root == calculated_root); - - let h = mimc::mimc_bn254(mimc_input); - // Regression test for PR #891 - std::println(h); - assert(h == 18226366069841799622585958305961373004333097209608110160936134895615261821931); -} diff --git a/tooling/nargo_cli/tests/execution_success/modules/src/foo.nr b/tooling/nargo_cli/tests/execution_success/modules/src/foo.nr deleted file mode 100644 index 4df05bbc288..00000000000 --- a/tooling/nargo_cli/tests/execution_success/modules/src/foo.nr +++ /dev/null @@ -1,3 +0,0 @@ -pub fn hello(x : Field) -> Field { - x -} diff --git a/tooling/nargo_cli/tests/execution_success/modules_more/src/foo.nr b/tooling/nargo_cli/tests/execution_success/modules_more/src/foo.nr deleted file mode 100644 index ee0d20082f5..00000000000 --- a/tooling/nargo_cli/tests/execution_success/modules_more/src/foo.nr +++ /dev/null @@ -1,5 +0,0 @@ -mod bar; - -fn hello(x : Field) -> Field { - x -} diff --git a/tooling/nargo_cli/tests/execution_success/modules_more/src/foo/bar.nr b/tooling/nargo_cli/tests/execution_success/modules_more/src/foo/bar.nr deleted file mode 100644 index 3d07fd9b72f..00000000000 --- a/tooling/nargo_cli/tests/execution_success/modules_more/src/foo/bar.nr +++ /dev/null @@ -1,3 +0,0 @@ -pub fn from_bar(x : Field) -> Field { - x -} diff --git a/tooling/nargo_cli/tests/execution_success/modules_more/src/main.nr b/tooling/nargo_cli/tests/execution_success/modules_more/src/main.nr deleted file mode 100644 index 8862e5a8650..00000000000 --- a/tooling/nargo_cli/tests/execution_success/modules_more/src/main.nr +++ /dev/null @@ -1,6 +0,0 @@ -mod foo; - -// An example of the module system -fn main(x: Field, y: Field) { - assert(x != foo::bar::from_bar(y)); -} diff --git a/tooling/nargo_cli/tests/execution_success/modulus/src/main.nr b/tooling/nargo_cli/tests/execution_success/modulus/src/main.nr deleted file mode 100644 index e8d0cc0541a..00000000000 --- a/tooling/nargo_cli/tests/execution_success/modulus/src/main.nr +++ /dev/null @@ -1,25 +0,0 @@ -use dep::std; - -fn main(bn254_modulus_be_bytes : [u8; 32], bn254_modulus_be_bits : [u1; 254]) { - let modulus_size = std::field::modulus_num_bits(); - // NOTE: The constraints used in this circuit will only work when testing nargo with the plonk bn254 backend - assert(modulus_size == 254); - - let modulus_be_byte_array = std::field::modulus_be_bytes(); - for i in 0..32 { - assert(modulus_be_byte_array[i] == bn254_modulus_be_bytes[i]); - } - let modulus_le_byte_array = std::field::modulus_le_bytes(); - for i in 0..32 { - assert(modulus_le_byte_array[i] == bn254_modulus_be_bytes[31-i]); - } - - let modulus_be_bits = std::field::modulus_be_bits(); - for i in 0..254 { - assert(modulus_be_bits[i] == bn254_modulus_be_bits[i]); - } - let modulus_le_bits = std::field::modulus_le_bits(); - for i in 0..254 { - assert(modulus_le_bits[i] == bn254_modulus_be_bits[253-i]); - } -} 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 deleted file mode 100644 index 0be7d688608..00000000000 --- a/tooling/nargo_cli/tests/execution_success/nested_array_dynamic/src/main.nr +++ /dev/null @@ -1,78 +0,0 @@ -struct Bar { - inner: [Field; 3], -} - -struct Foo { - a: Field, - b: [Field; 3], - bar: Bar, -} - -struct FooParent { - array: [Field; 3], - foos: [Foo; 4], -} - -fn main(mut x : [Foo; 4], y : pub Field) { - assert(x[y - 3].a == 1); - assert(x[y - 3].b == [2, 3, 20]); - assert(x[y - 2].a == 4); - assert(x[y - 2].b == [5, 6, 21]); - assert(x[y - 1].a == 7); - assert(x[y - 1].b == [8, 9, 22]); - assert(x[y].a == 10); - assert(x[y].b == [11, 12, 23]); - assert(x[y].bar.inner == [109, 110, 111]); - - // Check dynamic array set - if y != 2 { - x[y].a = 50; - } else { - x[y].a = 100; - } - assert(x[3].a == 50); - - if y == 2 { - x[y - 1].b = [50, 51, 52]; - } else { - 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]); - assert(x[y].bar.inner == [109, 110, 111]); - - let foo_parent_one = FooParent { array: [0, 1, 2], foos: x }; - let foo_parent_two = FooParent { array: [3, 4, 5], foos: x }; - let mut foo_parents = [foo_parent_one, foo_parent_two]; - - assert(foo_parents[y - 3].foos[y - 3].b == [2, 3, 20]); - 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]); - if y == 2 { - foo_parents[y - 2].foos[y - 2].b = [10, 9, 8]; - } else { - foo_parents[y - 2].foos[y - 2].b = [20, 19, 18]; - } - assert(foo_parents[1].foos[1].b == [20, 19, 18]); - - assert(foo_parents[1].foos[1].b[2] == 18); - if y == 3 { - foo_parents[y - 2].foos[y - 2].b[y - 1] = 5000; - } else { - foo_parents[y - 2].foos[y - 2].b[y - 1] = 1000; - } - assert(foo_parents[1].foos[1].b[2] == 5000); - - // 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_slice_dynamic/src/main.nr b/tooling/nargo_cli/tests/execution_success/nested_slice_dynamic/src/main.nr deleted file mode 100644 index 2d7c52da495..00000000000 --- a/tooling/nargo_cli/tests/execution_success/nested_slice_dynamic/src/main.nr +++ /dev/null @@ -1,49 +0,0 @@ -struct Bar { - inner: [Field; 3], -} - -struct Foo { - a: Field, - b: [Field; 3], - bar: Bar, -} - -fn main(y : Field) { - let foo_one = Foo { a: 1, b: [2, 3, 20], bar: Bar { inner: [100, 101, 102] } }; - let foo_two = Foo { a: 4, b: [5, 6, 21], bar: Bar { inner: [103, 104, 105] } }; - let foo_three = Foo { a: 7, b: [8, 9, 22], bar: Bar { inner: [106, 107, 108] } }; - let foo_four = Foo { a: 10, b: [11, 12, 23], bar: Bar { inner: [109, 110, 111] } }; - let mut x = [foo_one]; - x = x.push_back(foo_two); - x = x.push_back(foo_three); - x = x.push_back(foo_four); - - assert(x[y - 3].a == 1); - assert(x[y - 3].b == [2, 3, 20]); - assert(x[y - 2].a == 4); - assert(x[y - 2].b == [5, 6, 21]); - assert(x[y - 1].a == 7); - assert(x[y - 1].b == [8, 9, 22]); - assert(x[y].a == 10); - assert(x[y].b == [11, 12, 23]); - assert(x[y].bar.inner == [109, 110, 111]); - - if y != 2 { - x[y - 2].a = 50; - } else { - x[y - 2].a = 100; - } - assert(x[y - 2].a == 50); - - if y == 2 { - x[y - 1].b = [50, 51, 52]; - } else { - 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]); - assert(x[y].bar.inner == [109, 110, 111]); -} 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 deleted file mode 100644 index f25c1f68a18..00000000000 --- a/tooling/nargo_cli/tests/execution_success/pedersen_check/src/main.nr +++ /dev/null @@ -1,23 +0,0 @@ -use dep::std; - -fn main(x: Field, y: Field, salt: Field, out_x: Field, out_y: Field, out_hash: Field) { - let res = std::hash::pedersen_commitment([x, y]); - assert(res.x == out_x); - assert(res.y == out_y); - - let res_hash = std::hash::pedersen_hash_with_separator([x, y], 0); - assert_eq(res_hash, out_hash); - - assert(res_hash != res.x); - - - let raw_data = [x, y]; - let mut state = 0; - for i in 0..2 { - state = state * 8 + raw_data[i]; - } - state += salt; - let hash = std::hash::pedersen_commitment([state]); - assert(std::hash::pedersen_commitment([43]).x == hash.x); -} - diff --git a/tooling/nargo_cli/tests/execution_success/references/src/main.nr b/tooling/nargo_cli/tests/execution_success/references/src/main.nr deleted file mode 100644 index 33fe69dc96e..00000000000 --- a/tooling/nargo_cli/tests/execution_success/references/src/main.nr +++ /dev/null @@ -1,239 +0,0 @@ -fn main(mut x: Field) { - add1(&mut x); - assert(x == 3); - - let mut s = S { y: x }; - s.add2(); - assert(s.y == 5); - - // Regression for #1946: Method resolution error when calling &mut methods with a variable of type &mut T - let s_ref = &mut s; - s_ref.add2(); - assert(s.y == 7); - - // Test that normal mutable variables are still copied - let mut a = 0; - mutate_copy(a); - assert(a == 0); - - // Test something 3 allocations deep - let mut nested_allocations = Nested { y: &mut &mut 0 }; - add1(*nested_allocations.y); - 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] } }; - *c.bar.array = [3, 4]; - assert(*c.bar.array == [3, 4]); - - regression_1887(); - regression_2054(); - regression_2030(); - regression_2255(); - - assert(x == 3); - regression_2218_if_inner_if(x, 10); - regression_2218_if_inner_else(20, x); - regression_2218_else(x, 3); - regression_2218_loop(x, 10); - - regression_2560(s_ref); -} - -fn add1(x: &mut Field) { - *x += 1; -} - -struct S { y: Field } - -struct Nested { y: &mut &mut Field } - -struct C { - foo: Field, - bar: &mut C2, -} - -struct C2 { - array: &mut [Field; 2] -} - -impl S { - fn add2(&mut self) { - self.y += 2; - } - - fn get_y(self) -> Field { - self.y - } -} - -fn mutate_copy(mut a: Field) { - a = 7; -} - -// Previously the `foo.bar` in `foo.bar.mutate()` would insert an automatic dereference -// of `foo` which caused the method to wrongly be mutating a copy of bar rather than the original. -fn regression_1887() { - let foo = &mut Foo { bar: Bar { x: 0 } }; - foo.bar.mutate(); - assert(foo.bar.x == 32); -} - -struct Foo { bar: Bar } -struct Bar { x: Field } - -impl Bar { - fn mutate(&mut self) { - self.x = 32; - } -} - -// Ensure that mutating a variable does not also mutate its copy -fn regression_2054() { - let mut x = 2; - let z = x; - - x += 1; - assert(z == 2); -} - -// The compiler was still trying to convert an LValue from an array of structs to struct of arrays indexing, -// even though this conversion was mostly removed elsewhere. -fn regression_2030() { - let ref = &mut 0; - let mut array = [ref, ref]; - let _ = *array[0]; - *array[0] = 1; -} - -// The `mut x: &mut ...` caught a bug handling lvalues where a double-dereference would occur internally -// in one step rather than being tracked by two separate steps. This lead to assigning the 1 value to the -// incorrect outer `mut` reference rather than the correct `&mut` reference. -fn regression_2255() { - let x = &mut 0; - regression_2255_helper(x); - assert(*x == 1); -} - -fn regression_2255_helper(mut x: &mut Field) { - *x = 1; -} - -fn regression_2218(x: Field, y: Field) -> Field { - let q = &mut &mut 0; - let q1 = *q; - let q2 = *q; - - if x != y { - *q1 = 1; - // Make sure that we correct load reference aliases through multiple blocks - if x != 20 { - *q1 = 10; - *q2 = 2; // now we'd expect q1 == q2 == 2 - assert(*q1 == 2); - } else { - *q2 = 15; - assert(*q1 == 15); - } - } else { - *q2 = 20; - assert(*q1 == 20); - } - // Have to assign value to return it - let value = *q1; - value -} - -fn regression_2218_if_inner_if(x: Field, y: Field) { - let value = regression_2218(x, y); - assert(value == 2); -} - -fn regression_2218_if_inner_else(x: Field, y: Field) { - let value = regression_2218(x, y); - assert(value == 15); -} - -fn regression_2218_else(x: Field, y: Field) { - let value = regression_2218(x, y); - assert(value == 20); -} - -fn regression_2218_loop(x: Field, y: Field) { - let q = &mut &mut 0; - let q1 = *q; - let q2 = *q; - - for _ in 0..1 { - if x != y { - *q1 = 10; - *q2 = 2; // now we'd expect q1 == q2 == 2 - - assert(*q1 == 2); - } else { - *q2 = 20; - assert(*q1 == 20); - } - } - assert(*q1 == 2); - - for _ in 0..1 { - for _ in 0..5 { - if x != y { - *q1 = 1; - // Make sure that we correct load reference aliases through multiple blocks - if x != 20 { - *q1 = 10; - *q2 = 2; // now we'd expect q1 == q2 == 2 - - assert(*q1 == 2); - } - } else { - *q2 = 20; - assert(*q1 == 20); - } - } - if x != y { - *q1 = 1; - for _ in 0..5 { - // Make sure that we correct load reference aliases through multiple blocks - if x != 20 { - *q1 = 10; - *q2 = 2; // now we'd expect q1 == q2 == 2 - - assert(*q1 == 2); - } - } - } else { - *q2 = 20; - assert(*q1 == 20); - } - } - assert(*q1 == 2); - - if x != y { - for _ in 0..5 { - if x != y { - *q1 = 1; - // Make sure that we correct load reference aliases through multiple blocks - if x != 20 { - *q1 = 10; - *q2 = 2; // now we'd expect q1 == q2 == 2 - - assert(*q1 == 2); - } - } - } - } else { - *q2 = 20; - assert(*q1 == 20); - } - assert(*q1 == 2); -} - -// This is more a feature test than a proper regression. -// Before, we never automatically dereferenced objects in method calls to their value types. -// Now, we insert as many `*` as necessary to get to `S`. -fn regression_2560(s_ref: &mut S) { - assert(s_ref.get_y() == 7); -} diff --git a/tooling/nargo_cli/tests/execution_success/regression/src/main.nr b/tooling/nargo_cli/tests/execution_success/regression/src/main.nr deleted file mode 100644 index f8ac3823e08..00000000000 --- a/tooling/nargo_cli/tests/execution_success/regression/src/main.nr +++ /dev/null @@ -1,110 +0,0 @@ -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(length as u64 <= input.len() as u64); - - let mut nibble = [0 as u4; NIBBLE_LENGTH]; - - let first_nibble = (input[0] >> 4) as u4; - let parity = first_nibble as u1; - - if parity == 1 { - nibble[0] = (input[0] & 0x0f) as u4; - for i in 1..input.len() - { - if i as u64 < length as u64 - { - let x = input[i]; - nibble[2*i - 1] = (x >> 4) as u4; - nibble[2*i] = (x & 0x0f) as u4; - } - } - } else { - for i in 0..2 - { - if (i as u64) < length as u64 - 1 - { - let x = input[i + 1]; - nibble[2*i] = (x >> 4) as u4; - nibble[2*i + 1] = (x & 0x0f) as u4; - } - } - } - - let out = (nibble, 2 * length + (parity as Field) - 2); - - out -} - -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 { - 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 - } -} - -fn bitshift_literal_0() -> u64 { - let mut bits: u64 = 0; - bits |= 1 << 0; - - bits -} -fn bitshift_literal_4() -> u64 { - let mut bits: u64 = 0; - bits |= 1 << 4; - - bits -} -fn bitshift_variable(idx: u64) -> u64 { - let mut bits: u64 = 0; - bits |= 1 << idx; - - bits -} - -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]); - - // 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_length = 20; - - 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.1 == 21); - - // Issue 2399 - let result_0 = bitshift_literal_0(); - assert(result_0 == 1); - let result_4 = bitshift_literal_4(); - assert(result_4 == 16); - let result_0 = bitshift_variable(0); - assert(result_0 == 1); - let result_4 = bitshift_variable(4); - assert(result_4 == 16); -} diff --git a/tooling/nargo_cli/tests/execution_success/regression_2854/src/main.nr b/tooling/nargo_cli/tests/execution_success/regression_2854/src/main.nr deleted file mode 100644 index 10ada5faeec..00000000000 --- a/tooling/nargo_cli/tests/execution_success/regression_2854/src/main.nr +++ /dev/null @@ -1,3 +0,0 @@ -fn main(x: Field) -> pub i127 { - x as i127 -} \ No newline at end of file diff --git a/tooling/nargo_cli/tests/execution_success/regression_method_cannot_be_found/src/main.nr b/tooling/nargo_cli/tests/execution_success/regression_method_cannot_be_found/src/main.nr deleted file mode 100644 index 4c1f771ae6c..00000000000 --- a/tooling/nargo_cli/tests/execution_success/regression_method_cannot_be_found/src/main.nr +++ /dev/null @@ -1,23 +0,0 @@ -use dep::std; -struct Item { - id: Field, -} - -impl Item { - fn log(self) { - let id = self.id; - std::println(id); - } -} - -fn create(something: V) -> V { - something -} - -fn main() { - let a = Item { id: 1 }; - let b = create(a); - let _id = b.id; - // Regression for: cannot find this method - b.log(); -} 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 deleted file mode 100644 index f62496d61d5..00000000000 --- a/tooling/nargo_cli/tests/execution_success/scalar_mul/src/main.nr +++ /dev/null @@ -1,22 +0,0 @@ -use dep::std; - -fn main( - a: Field, - a_pub_x: pub Field, - a_pub_y: pub Field, - b: Field, - b_pub_x: pub Field, - b_pub_y: pub Field -) { - let mut priv_key = a; - let mut pub_x: Field = a_pub_x; - let mut pub_y: Field = a_pub_y; - if a != 1 { // Change `a` in Prover.toml to test input `b` - 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); -} diff --git a/tooling/nargo_cli/tests/execution_success/schnorr/src/main.nr b/tooling/nargo_cli/tests/execution_success/schnorr/src/main.nr deleted file mode 100644 index f974027f1cf..00000000000 --- a/tooling/nargo_cli/tests/execution_success/schnorr/src/main.nr +++ /dev/null @@ -1,21 +0,0 @@ -use dep::std; - -// Note: If main has any unsized types, then the verifier will never be able -// to figure out the circuit instance -fn main(message: [u8; 10], message_field: Field, pub_key_x: Field, pub_key_y: Field, signature: [u8; 64]) { - // Regression for issue #2421 - // We want to make sure that we can accurately verify a signature whose message is a slice vs. an array - let message_field_bytes = message_field.to_be_bytes(10); - for i in 0..10 { - assert(message[i] == message_field_bytes[i]); - } - // 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); - 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); - assert(valid_signature); -} diff --git a/tooling/nargo_cli/tests/execution_success/signed_arithmetic/src/main.nr b/tooling/nargo_cli/tests/execution_success/signed_arithmetic/src/main.nr deleted file mode 100644 index c2a1b580f40..00000000000 --- a/tooling/nargo_cli/tests/execution_success/signed_arithmetic/src/main.nr +++ /dev/null @@ -1,32 +0,0 @@ -fn main(mut x: i32, mut y: i32, z: i32) { - let mut s1: i8 = 5; - let mut s2: i8 = 8; - assert(s1+s2 == 13); - assert(x + y == 13); - - s2= -8; - y = -y; - assert(s1+s2 == -3); - assert(x + y == -3); - - s1 = -15; - assert(s1-s2 == -7); - assert(z-y == -7); - - s1 = -5; - s2 = 8; - x = -x; - y = -y; - assert(s1-s2 == -13); - assert(x-y == -13); - - s2 = -8; - y = -y; - assert(s1*s2 == 40); - assert(x*y == 40); - - s1 = 1; - s2 = -8; - assert(s1*s2 == -8); - assert(x/x*y == -8); -} diff --git a/tooling/nargo_cli/tests/execution_success/signed_division/src/main.nr b/tooling/nargo_cli/tests/execution_success/signed_division/src/main.nr deleted file mode 100644 index 651df10e963..00000000000 --- a/tooling/nargo_cli/tests/execution_success/signed_division/src/main.nr +++ /dev/null @@ -1,24 +0,0 @@ -use dep::std; -// Testing signed integer division: -// 7/3 = 2 -// -7/3 = -2 -// -7/-3 = 2 -// 7/-3 = -2 -fn main(mut x: i32, mut y: i32, mut z: i32) { - // 7/3 = 2 - assert(x / y == z); - - // -7/3 = -2 - let minus_x = std::wrapping_sub(0,x); - let minus_z = std::wrapping_sub(0,z); - let minus_y = std::wrapping_sub(0,y); - assert(x+minus_x == 0); - assert(z+minus_z == 0); - assert(minus_x / y == minus_z); - - // -7/-3 = 2 - assert(minus_x / minus_y == z); - - // 7/-3 = -2 - assert(x / minus_y == minus_z); -} diff --git a/tooling/nargo_cli/tests/execution_success/simple_comparison/src/main.nr b/tooling/nargo_cli/tests/execution_success/simple_comparison/src/main.nr deleted file mode 100644 index f224b8a9bee..00000000000 --- a/tooling/nargo_cli/tests/execution_success/simple_comparison/src/main.nr +++ /dev/null @@ -1,6 +0,0 @@ -// Tests a very simple program. -// -// The features being tested is comparison -fn main(x : Field, y : Field) { - assert(x as u32 < y as u32); -} diff --git a/tooling/nargo_cli/tests/execution_success/simple_mut/src/main.nr b/tooling/nargo_cli/tests/execution_success/simple_mut/src/main.nr deleted file mode 100644 index bba0eca5fab..00000000000 --- a/tooling/nargo_cli/tests/execution_success/simple_mut/src/main.nr +++ /dev/null @@ -1,6 +0,0 @@ -// A simple program to test mutable variables -fn main(x : Field) -> pub Field { - let mut y = 2; - y += x; - y -} diff --git a/tooling/nargo_cli/tests/execution_success/simple_not/src/main.nr b/tooling/nargo_cli/tests/execution_success/simple_not/src/main.nr deleted file mode 100644 index 4b17fd91eca..00000000000 --- a/tooling/nargo_cli/tests/execution_success/simple_not/src/main.nr +++ /dev/null @@ -1,4 +0,0 @@ -// A simple program for testing the NOT op -fn main(x : bool) -> pub bool { - !x -} diff --git a/tooling/nargo_cli/tests/execution_success/simple_print/Prover.toml b/tooling/nargo_cli/tests/execution_success/simple_print/Prover.toml deleted file mode 100644 index 2c1854573a4..00000000000 --- a/tooling/nargo_cli/tests/execution_success/simple_print/Prover.toml +++ /dev/null @@ -1,2 +0,0 @@ -x = 1 -y = 2 diff --git a/tooling/nargo_cli/tests/execution_success/simple_print/src/main.nr b/tooling/nargo_cli/tests/execution_success/simple_print/src/main.nr deleted file mode 100644 index 5f49aa548bc..00000000000 --- a/tooling/nargo_cli/tests/execution_success/simple_print/src/main.nr +++ /dev/null @@ -1,8 +0,0 @@ -// Simple program for testing the logging -// of single witnesses and witness arrays. -use dep::std; - -fn main(x : Field, y : pub Field) { - std::println(x); - std::println([x, y]); -} diff --git a/tooling/nargo_cli/tests/execution_success/simple_program_addition/src/main.nr b/tooling/nargo_cli/tests/execution_success/simple_program_addition/src/main.nr deleted file mode 100644 index 082d9b5071f..00000000000 --- a/tooling/nargo_cli/tests/execution_success/simple_program_addition/src/main.nr +++ /dev/null @@ -1,5 +0,0 @@ -// The feature being tested is handling of -// a binary operation. -fn main(x : Field) -> pub Field { - x + 1 -} diff --git a/tooling/nargo_cli/tests/execution_success/simple_radix/src/main.nr b/tooling/nargo_cli/tests/execution_success/simple_radix/src/main.nr deleted file mode 100644 index 67268a932a3..00000000000 --- a/tooling/nargo_cli/tests/execution_success/simple_radix/src/main.nr +++ /dev/null @@ -1,7 +0,0 @@ -// Simple program to test to_radix -fn main(x : Field) { - let bits = x.to_le_bits(3); - assert(bits[0] == 0); - assert(bits[1] == 1); - assert(bits[2] == 0); -} diff --git a/tooling/nargo_cli/tests/execution_success/simple_shield/src/main.nr b/tooling/nargo_cli/tests/execution_success/simple_shield/src/main.nr deleted file mode 100644 index f8fec2cb4d9..00000000000 --- a/tooling/nargo_cli/tests/execution_success/simple_shield/src/main.nr +++ /dev/null @@ -1,35 +0,0 @@ -use dep::std; - -fn main( - // Public key of note - // all notes have the same denomination - priv_key: Field, - - // Merkle membership proof - note_root: pub Field, - index: Field, - note_hash_path: [Field; 3], - - // Receiver public key - to_pubkey_x: Field, - to_pubkey_y: Field, -) -> pub [Field; 2] { - // Compute public key from private key to show ownership - let pubkey = std::scalar_mul::fixed_base_embedded_curve(priv_key, 0); - let pubkey_x = pubkey[0]; - let pubkey_y = pubkey[1]; - - // Compute input note commitment - let note_commitment = std::hash::pedersen_commitment([pubkey_x, pubkey_y]); - - // Compute input note nullifier - let nullifier = std::hash::pedersen_commitment([note_commitment.x, index, priv_key]); - - // Compute output note nullifier - let receiver_note_commitment = std::hash::pedersen_commitment([to_pubkey_x, to_pubkey_y]); - - // Check that the input note nullifier is in the root - assert(note_root == std::merkle::compute_merkle_root(note_commitment.x, index, note_hash_path)); - - [nullifier.x, receiver_note_commitment.x] -} diff --git a/tooling/nargo_cli/tests/execution_success/simple_shift_left_right/src/main.nr b/tooling/nargo_cli/tests/execution_success/simple_shift_left_right/src/main.nr deleted file mode 100644 index 517ef429458..00000000000 --- a/tooling/nargo_cli/tests/execution_success/simple_shift_left_right/src/main.nr +++ /dev/null @@ -1,8 +0,0 @@ -// Tests a very simple program. -// -// The features being tested are left and right shifts. -fn main(x : u32) { - let z = x >> 4; - let t = x << 4; - assert(z == t >> 8); -} diff --git a/tooling/nargo_cli/tests/execution_success/slice_dynamic_index/src/main.nr b/tooling/nargo_cli/tests/execution_success/slice_dynamic_index/src/main.nr deleted file mode 100644 index 3f3177b1b5a..00000000000 --- a/tooling/nargo_cli/tests/execution_success/slice_dynamic_index/src/main.nr +++ /dev/null @@ -1,312 +0,0 @@ -fn main(x : Field) { - // The parameters to this function must come directly from witness values (inputs to main). - regression_dynamic_slice_index(x - 1, x - 4); -} - -fn regression_dynamic_slice_index(x: Field, y: Field) { - let mut slice = []; - for i in 0..5 { - slice = slice.push_back(i); - } - assert(slice.len() == 5); - - dynamic_slice_index_set_if(slice, x, y); - dynamic_slice_index_set_else(slice, x, y); - dynamic_slice_index_set_nested_if_else_else(slice, x, y); - dynamic_slice_index_set_nested_if_else_if(slice, x, y + 1); - dynamic_slice_index_if(slice, x); - dynamic_array_index_if([0, 1, 2, 3, 4], x); - dynamic_slice_index_else(slice, x); - - dynamic_slice_merge_if(slice, x); - dynamic_slice_merge_else(slice, x); - dynamic_slice_merge_two_ifs(slice, x); - dynamic_slice_merge_mutate_between_ifs(slice, x, y); - dynamic_slice_merge_push_then_pop(slice, x, y); -} - -fn dynamic_slice_index_set_if(mut slice: [Field], x: Field, y: Field) { - assert(slice[x] == 4); - assert(slice[y] == 1); - slice[y] = 0; - assert(slice[x] == 4); - assert(slice[1] == 0); - if x as u32 < 10 { - assert(slice[x] == 4); - slice[x] = slice[x] - 2; - slice[x - 1] = slice[x]; - } else { - slice[x] = 0; - } - assert(slice[3] == 2); - assert(slice[4] == 2); -} - -fn dynamic_slice_index_set_else(mut slice: [Field], x: Field, y: Field) { - assert(slice[x] == 4); - assert(slice[y] == 1); - slice[y] = 0; - assert(slice[x] == 4); - assert(slice[1] == 0); - if x as u32 > 10 { - assert(slice[x] == 4); - slice[x] = slice[x] - 2; - slice[x - 1] = slice[x]; - } else { - slice[x] = 0; - } - assert(slice[4] == 0); -} - -// This tests the case of missing a store instruction in the else branch -// of merging slices -fn dynamic_slice_index_if(mut slice: [Field], x: Field) { - if x as u32 < 10 { - assert(slice[x] == 4); - slice[x] = slice[x] - 2; - } else { - assert(slice[x] == 0); - } - assert(slice[4] == 2); -} - -fn dynamic_array_index_if(mut array: [Field; 5], x: Field) { - if x as u32 < 10 { - assert(array[x] == 4); - array[x] = array[x] - 2; - } else { - assert(array[x] == 0); - } - assert(array[4] == 2); -} - -// This tests the case of missing a store instruction in the then branch -// of merging slices -fn dynamic_slice_index_else(mut slice: [Field], x: Field) { - if x as u32 > 10 { - assert(slice[x] == 0); - } else { - assert(slice[x] == 4); - slice[x] = slice[x] - 2; - } - assert(slice[4] == 2); -} - -fn dynamic_slice_merge_if(mut slice: [Field], x: Field) { - if x as u32 < 10 { - assert(slice[x] == 4); - slice[x] = slice[x] - 2; - - slice = slice.push_back(10); - // Having an array set here checks whether we appropriately - // handle a slice length that is not yet resolving to a constant - // during flattening - slice[x] = 10; - assert(slice[slice.len() - 1] == 10); - assert(slice.len() == 6); - - slice[x] = 20; - slice[x] = slice[x] + 10; - - slice = slice.push_front(11); - assert(slice[0] == 11); - assert(slice.len() == 7); - assert(slice[5] == 30); - - slice = slice.push_front(12); - assert(slice[0] == 12); - assert(slice.len() == 8); - assert(slice[6] == 30); - - let (popped_slice, last_elem) = slice.pop_back(); - assert(last_elem == 10); - assert(popped_slice.len() == 7); - - let (first_elem, rest_of_slice) = popped_slice.pop_front(); - assert(first_elem == 12); - assert(rest_of_slice.len() == 6); - - // TODO(#2462): SliceInsert and SliceRemove with a dynamic index are not yet implemented in ACIR gen - slice = rest_of_slice.insert(2, 20); - assert(slice[2] == 20); - assert(slice[6] == 30); - assert(slice.len() == 7); - - // TODO(#2462): SliceInsert and SliceRemove with a dynamic index are not yet implemented in ACIR gen - let (removed_slice, removed_elem) = slice.remove(3); - // The deconstructed tuple assigns to the slice but is not seen outside of the if statement - // without a direct assignment - slice = removed_slice; - - assert(removed_elem == 1); - assert(slice.len() == 6); - } else { - assert(slice[x] == 0); - slice = slice.push_back(20); - } - - assert(slice.len() == 6); - assert(slice[slice.len() - 1] == 30); -} - -fn dynamic_slice_merge_else(mut slice: [Field], x: Field) { - if x as u32 > 10 { - assert(slice[x] == 0); - slice[x] = 2; - } else { - assert(slice[x] == 4); - slice[x] = slice[x] - 2; - slice = slice.push_back(10); - } - assert(slice.len() == 6); - assert(slice[slice.len() - 1] == 10); - - slice = slice.push_back(20); - assert(slice.len() == 7); - assert(slice[slice.len() - 1] == 20); -} - -fn dynamic_slice_index_set_nested_if_else_else(mut slice: [Field], x: Field, y: Field) { - assert(slice[x] == 4); - assert(slice[y] == 1); - slice[y] = 0; - assert(slice[x] == 4); - assert(slice[1] == 0); - if x as u32 < 10 { - slice[x] = slice[x] - 2; - if y != 1 { - slice[x] = slice[x] + 20; - } else { - if x == 5 { - // We should not hit this case - assert(slice[x] == 22); - } else { - slice[x] = 10; - slice = slice.push_back(15); - assert(slice.len() == 6); - } - assert(slice[4] == 10); - } - } else { - slice[x] = 0; - } - assert(slice[4] == 10); - assert(slice.len() == 6); - assert(slice[slice.len() - 1] == 15); - - slice = slice.push_back(20); - assert(slice.len() == 7); - assert(slice[slice.len() - 1] == 20); -} - -fn dynamic_slice_index_set_nested_if_else_if(mut slice: [Field], x: Field, y: Field) { - assert(slice[x] == 4); - assert(slice[y] == 2); - slice[y] = 0; - assert(slice[x] == 4); - assert(slice[2] == 0); - if x as u32 < 10 { - slice[x] = slice[x] - 2; - // TODO: this panics as we have a load for the slice in flattening - if y == 1 { - slice[x] = slice[x] + 20; - } else { - if x == 4 { - slice[x] = 5; - } - assert(slice[4] == 5); - } - } else { - slice[x] = 0; - } - assert(slice[4] == 5); -} - -fn dynamic_slice_merge_two_ifs(mut slice: [Field], x: Field) { - if x as u32 > 10 { - assert(slice[x] == 0); - slice[x] = 2; - } else { - assert(slice[x] == 4); - slice[x] = slice[x] - 2; - slice = slice.push_back(10); - } - - assert(slice.len() == 6); - assert(slice[slice.len() - 1] == 10); - - if x == 20 { - slice = slice.push_back(20); - } - - slice = slice.push_back(15); - - assert(slice.len() == 7); - assert(slice[slice.len() - 1] == 15); - - slice = slice.push_back(20); - assert(slice.len() == 8); - assert(slice[slice.len() - 1] == 20); -} - -fn dynamic_slice_merge_mutate_between_ifs(mut slice: [Field], x: Field, y: Field) { - if x != y { - slice[x] = 50; - slice = slice.push_back(y); - slice = slice.push_back(x); - } else { - slice[x] = slice[x] - 2; - slice = slice.push_back(x); - } - - slice = slice.push_back(30); - assert(slice.len() == 8); - - if x == 20 { - slice = slice.push_back(20); - } - - slice = slice.push_back(15); - - if x != 20 { - slice = slice.push_back(50); - } - - slice = slice.push_back(60); - assert(slice.len() == 11); - assert(slice[x] == 50); - assert(slice[slice.len() - 4] == 30); - assert(slice[slice.len() - 3] == 15); - assert(slice[slice.len() - 2] == 50); - assert(slice[slice.len() - 1] == 60); -} - -fn dynamic_slice_merge_push_then_pop(mut slice: [Field], x: Field, y: Field) { - if x != y { - slice[x] = 5; - slice = slice.push_back(y); - slice = slice.push_back(x); - assert(slice.len() == 7); - - let (popped_slice, elem) = slice.pop_back(); - assert(slice.len() == 7); - assert(elem == x); - slice = popped_slice; - } else { - slice = slice.push_back(x); - } - - slice = slice.push_back(30); - assert(slice.len() == 7); - - if x == 20 { - slice = slice.push_back(20); - } - - let (slice, elem) = slice.pop_back(); - assert(elem == 30); - - let (_, elem) = slice.pop_back(); - assert(elem == y); -} - diff --git a/tooling/nargo_cli/tests/execution_success/slice_struct_field/src/main.nr b/tooling/nargo_cli/tests/execution_success/slice_struct_field/src/main.nr deleted file mode 100644 index a8ef5db063a..00000000000 --- a/tooling/nargo_cli/tests/execution_success/slice_struct_field/src/main.nr +++ /dev/null @@ -1,184 +0,0 @@ -struct FooParent { - parent_arr: [Field; 3], - foos: [Foo], -} - -struct Bar { - inner: [Field; 3], -} - -struct Foo { - a: Field, - b: [Field], - bar: Bar, -} - -fn main(y : pub Field) { - let mut b_one = [2, 3, 20]; - b_one = b_one.push_back(20); - let foo_one = Foo { a: 1, b: b_one, bar: Bar { inner: [100, 101, 102] } }; - - let mut b_two = [5, 6, 21]; - b_two = b_two.push_back(21); - let foo_two = Foo { a: 4, b: b_two, bar: Bar { inner: [103, 104, 105] } }; - - let foo_three = Foo { a: 7, b: [8, 9, 22], bar: Bar { inner: [106, 107, 108] } }; - let foo_four = Foo { a: 10, b: [11, 12, 23], bar: Bar { inner: [109, 110, 111] } }; - - let mut x = [foo_one, foo_two]; - x = x.push_back(foo_three); - x = x.push_back(foo_four); - - assert(x[y - 3].a == 1); - let struct_slice = x[y - 3].b; - for i in 0..4 { - assert(struct_slice[i] == b_one[i]); - } - - assert(x[y - 2].a == 4); - let struct_slice = x[y - 2].b; - for i in 0..4 { - assert(struct_slice[i] == b_two[i]); - } - - assert(x[y - 1].a == 7); - let struct_slice = x[y - 1].b; - assert(struct_slice[0] == 8); - assert(struct_slice[1] == 9); - assert(struct_slice[2] == 22); - - assert(x[y].a == 10); - let struct_slice = x[y].b; - assert(struct_slice[0] == 11); - assert(struct_slice[1] == 12); - assert(struct_slice[2] == 23); - assert(x[y].bar.inner == [109, 110, 111]); - - 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]); - assert(x[y].bar.inner == [109, 110, 111]); - // Check that switching the lhs and rhs is still valid - assert([109, 110, 111] == x[y].bar.inner); - - // TODO: Enable merging nested slices - // if y != 2 { - // x[y].a = 50; - // } else { - // x[y].a = 100; - // } - // assert(x[3].a == 50); - // if y == 2 { - // x[y - 1].b = [50, 51, 52]; - // } else { - // x[y - 1].b = [100, 101, 102]; - // } - // assert(x[2].b[0] == 100); - // assert(x[2].b[1] == 101); - // assert(x[2].b[2] == 102); - - let q = x.push_back(foo_four); - let foo_parent_one = FooParent { parent_arr: [0, 1, 2], foos: x }; - let foo_parent_two = FooParent { parent_arr: [3, 4, 5], foos: q }; - let mut foo_parents = [foo_parent_one]; - foo_parents = foo_parents.push_back(foo_parent_two); - - // TODO: make a separate test for compile time - // foo_parents[1].foos.push_back(foo_four); - - // TODO: Merging nested slices is broken - // if y == 3 { - // foo_parents[y - 2].foos[y - 1].b[y - 1] = 5000; - // } else { - // foo_parents[y - 2].foos[y - 1].b[y - 1] = 1000; - // } - - assert(foo_parents[y - 2].foos[y - 2].b[y - 1] == 21); - foo_parents[y - 2].foos[y - 2].b[y - 1] = 5000; - assert(foo_parents[y - 2].foos[y - 2].b[y - 1] == 5000); - - let b_array = foo_parents[y - 2].foos[y - 3].b; - assert(foo_parents[y - 2].foos[y - 3].a == 1); - assert(b_array[0] == 2); - assert(b_array[1] == 3); - assert(b_array[2] == 20); - assert(b_array[3] == 20); - - let b_array = foo_parents[y - 2].foos[y - 2].b; - assert(foo_parents[y - 2].foos[y - 2].a == 4); - assert(b_array[0] == 5); - assert(b_array[1] == 6); - assert(b_array[2] == 5000); - assert(b_array[3] == 21); - - assert(foo_parents[y - 2].foos[y - 1].a == 7); - foo_parents[y - 2].foos[y - 1].a = 50; - - let b_array = foo_parents[y - 2].foos[y - 1].b; - assert(b_array[2] == 22); - assert(b_array.len() == 3); - - // Test setting a nested array with non-dynamic - let x = [5, 6, 5000, 21, 100, 101].as_slice(); - foo_parents[y - 2].foos[y - 1].b = x; - - assert(foo_parents[y - 2].foos[y - 1].b.len() == 6); - assert(foo_parents[y - 2].foos[y - 1].b[4] == 100); - assert(foo_parents[y - 2].foos[y - 1].b[5] == 101); - - test_basic_intrinsics_nested_slices(foo_parents, y); - // TODO(#3364): still have to enable slice intrinsics on dynamic nested slices - // assert(foo_parents[y - 2].foos.len() == 5); - // foo_parents[y - 2].foos = foo_parents[y - 2].foos.push_back(foo_four); - // assert(foo_parents[y - 2].foos.len() == 6); - - let b_array = foo_parents[y - 2].foos[y - 1].b; - assert(b_array[0] == 5); - assert(b_array[1] == 6); - assert(b_array[2] == 5000); - assert(b_array[3] == 21); - - let b_array = foo_parents[y - 2].foos[y].b; - assert(foo_parents[y - 2].foos[y].a == 10); - assert(b_array[0] == 11); - assert(b_array[1] == 12); - assert(b_array[2] == 23); - - assert(foo_parents[y - 2].foos[y - 3].bar.inner == [100, 101, 102]); - assert(foo_parents[y - 2].foos[y - 2].bar.inner == [103, 104, 105]); - assert(foo_parents[y - 2].foos[y - 1].bar.inner == [106, 107, 108]); - assert(foo_parents[y - 2].foos[y].bar.inner == [109, 110, 111]); -} - -fn test_basic_intrinsics_nested_slices(mut foo_parents: [FooParent], y: Field) { - foo_parents[y - 2].foos[y - 1].b = foo_parents[y - 2].foos[y - 1].b.push_back(500); - assert(foo_parents[y - 2].foos[y - 1].b.len() == 7); - assert(foo_parents[y - 2].foos[y - 1].b[6] == 500); - - let (popped_slice, last_elem) = foo_parents[y - 2].foos[y - 1].b.pop_back(); - foo_parents[y - 2].foos[y - 1].b = popped_slice; - assert(foo_parents[y - 2].foos[y - 1].b.len() == 6); - assert(last_elem == 500); - - foo_parents[y - 2].foos[y - 1].b = foo_parents[y - 2].foos[y - 1].b.push_front(11); - assert(foo_parents[y - 2].foos[y - 1].b.len() == 7); - assert(foo_parents[y - 2].foos[y - 1].b[0] == 11); - - let (first_elem, rest_of_slice) = foo_parents[y - 2].foos[y - 1].b.pop_front(); - foo_parents[y - 2].foos[y - 1].b = rest_of_slice; - assert(foo_parents[y - 2].foos[y - 1].b.len() == 6); - assert(first_elem == 11); - - foo_parents[y - 2].foos[y - 1].b = foo_parents[y - 2].foos[y - 1].b.insert(2, 20); - assert(foo_parents[y - 2].foos[y - 1].b.len() == 7); - assert(foo_parents[y - 2].foos[y - 1].b[y - 1] == 20); - assert(foo_parents[y - 2].foos[y - 1].b[y] == 5000); - assert(foo_parents[y - 2].foos[y - 1].b[6] == 101); - - let (rest_of_slice, removed_elem) = foo_parents[y - 2].foos[y - 1].b.remove(3); - foo_parents[y - 2].foos[y - 1].b = rest_of_slice; - assert(removed_elem == 5000); - assert(foo_parents[y - 2].foos[y - 1].b.len() == 6); - assert(foo_parents[y - 2].foos[y - 1].b[2] == 20); - assert(foo_parents[y - 2].foos[y - 1].b[3] == 21); -} \ No newline at end of file diff --git a/tooling/nargo_cli/tests/execution_success/slices/src/main.nr b/tooling/nargo_cli/tests/execution_success/slices/src/main.nr deleted file mode 100644 index 25c2ac27157..00000000000 --- a/tooling/nargo_cli/tests/execution_success/slices/src/main.nr +++ /dev/null @@ -1,302 +0,0 @@ -use dep::std::slice; -use dep::std; - -fn main(x : Field, y : pub Field) { - let mut slice = [0; 2]; - assert(slice[0] == 0); - assert(slice[0] != 1); - slice[0] = x; - assert(slice[0] == x); - - let slice_plus_10 = slice.push_back(y); - assert(slice_plus_10[2] == 10); - assert(slice_plus_10[2] != 8); - assert(slice_plus_10.len() == 3); - - let mut new_slice = []; - for i in 0..5 { - new_slice = new_slice.push_back(i); - } - assert(new_slice.len() == 5); - - new_slice = new_slice.push_front(20); - assert(new_slice[0] == 20); - assert(new_slice.len() == 6); - - let (popped_slice, last_elem) = new_slice.pop_back(); - assert(last_elem == 4); - assert(popped_slice.len() == 5); - - let (first_elem, rest_of_slice) = popped_slice.pop_front(); - assert(first_elem == 20); - assert(rest_of_slice.len() == 4); - - new_slice = rest_of_slice.insert(2, 100); - assert(new_slice[2] == 100); - assert(new_slice[4] == 3); - assert(new_slice.len() == 5); - - let (remove_slice, removed_elem) = new_slice.remove(3); - assert(removed_elem == 2); - assert(remove_slice[3] == 3); - assert(remove_slice.len() == 4); - - let append = [1, 2].append([3, 4, 5]); - assert(append.len() == 5); - assert(append[0] == 1); - assert(append[4] == 5); - - regression_2083(); - // The parameters to this function must come from witness values (inputs to main) - regression_merge_slices(x, y); - regression_2370(); -} - -// Ensure that slices of struct/tuple values work. -fn regression_2083() { - let y = [(1, 2)]; - let y = y.push_back((3, 4)); // [(1, 2), (3, 4)] - let y = y.push_back((5, 6)); // [(1, 2), (3, 4), (5, 6)] - assert(y[2].1 == 6); - - 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)] - assert(y[0].1 == 13); - assert(y[1].1 == 56); - assert(y[2].0 == 10); - - let (y, x) = y.remove(2); // [(12, 13), (55, 56), (1, 2), (3, 4), (5, 6)] - assert(y[2].0 == 1); - assert(x.0 == 10); - assert(x.1 == 11); - - let (x, y) = y.pop_front(); // [(55, 56), (1, 2), (3, 4), (5, 6)] - assert(y[0].0 == 55); - assert(x.0 == 12); - assert(x.1 == 13); - - let (y, x) = y.pop_back(); // [(55, 56), (1, 2), (3, 4)] - assert(y.len() == 3); - assert(x.0 == 5); - assert(x.1 == 6); -} - -// The parameters to this function must come from witness values (inputs to main) -fn regression_merge_slices(x: Field, y: Field) { - merge_slices_if(x, y); - merge_slices_else(x); -} - -fn merge_slices_if(x: Field, y: Field) { - let slice = merge_slices_return(x, y); - assert(slice.len() == 3); - assert(slice[2] == 10); - - let slice = merge_slices_mutate(x, y); - assert(slice.len() == 4); - assert(slice[3] == 5); - - let slice = merge_slices_mutate_in_loop(x, y); - assert(slice.len() == 7); - assert(slice[6] == 4); - - let slice = merge_slices_mutate_two_ifs(x, y); - assert(slice.len() == 6); - assert(slice[3] == 5); - assert(slice[4] == 15); - assert(slice[5] == 30); - - let slice = merge_slices_mutate_between_ifs(x, y); - assert(slice.len() == 8); - assert(slice[3] == 5); - assert(slice[4] == 30); - assert(slice[5] == 15); - assert(slice[6] == 50); - assert(slice[7] == 60); - - merge_slices_push_then_pop(x, y); - - let slice = merge_slices_push_then_insert(x, y); - assert(slice.len() == 7); - assert(slice[1] == 50); - assert(slice[2] == 0); - assert(slice[5] == 30); - assert(slice[6] == 100); - - let slice = merge_slices_remove_between_ifs(x, y); - assert(slice.len() == 5); -} - -fn merge_slices_else(x: Field) { - let slice = merge_slices_return(x, 5); - assert(slice[0] == 0); - assert(slice[1] == 0); - assert(slice.len() == 2); - - let slice = merge_slices_mutate(x, 5); - assert(slice[2] == 5); - assert(slice.len() == 3); - - let slice = merge_slices_mutate_in_loop(x, 5); - assert(slice[2] == 5); - assert(slice.len() == 3); -} - -// 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 } -} - -// Test mutating a slice inside of an if statement -fn merge_slices_mutate(x: Field, y: Field) -> [Field] { - let mut slice = [0; 2]; - if x != y { - slice = slice.push_back(y); - slice = slice.push_back(x); - } else { - slice = slice.push_back(x); - } - slice -} - -// Test mutating a slice inside of a loop in an if statement -fn merge_slices_mutate_in_loop(x: Field, y: Field) -> [Field] { - let mut slice = [0; 2]; - if x != y { - for i in 0..5 { - slice = slice.push_back(i); - } - } else { - slice = slice.push_back(x); - } - slice -} - -fn merge_slices_mutate_two_ifs(x: Field, y: Field) -> [Field] { - let mut slice = [0; 2]; - if x != y { - slice = slice.push_back(y); - slice = slice.push_back(x); - } else { - slice = slice.push_back(x); - } - - if x == 20 { - slice = slice.push_back(20); - } - - slice = slice.push_back(15); - slice = slice.push_back(30); - - slice -} - -fn merge_slices_mutate_between_ifs(x: Field, y: Field) -> [Field] { - let mut slice = [0; 2]; - if x != y { - slice = slice.push_back(y); - slice = slice.push_back(x); - } else { - slice = slice.push_back(x); - } - - slice = slice.push_back(30); - - if x == 20 { - slice = slice.push_back(20); - } - - slice = slice.push_back(15); - - if x != 20 { - slice = slice.push_back(50); - } - - slice = slice.push_back(60); - - slice -} - -fn merge_slices_push_then_pop(x: Field, y: Field) { - let mut slice = [0; 2]; - if x != y { - slice = slice.push_back(y); - slice = slice.push_back(x); - } else { - slice = slice.push_back(x); - } - - slice = slice.push_back(30); - - if x == 20 { - slice = slice.push_back(20); - } - - let (slice, elem) = slice.pop_back(); - assert(slice.len() == 4); - assert(elem == 30); - - let (slice, elem) = slice.pop_back(); - assert(slice.len() == 3); - assert(elem == x); -} - -fn merge_slices_push_then_insert(x: Field, y: Field) -> [Field] { - let mut slice = [0; 2]; - if x != y { - slice = slice.push_back(y); - slice = slice.push_back(x); - } else { - slice = slice.push_back(x); - } - - slice = slice.push_back(30); - - if x == 20 { - slice = slice.push_back(20); - slice = slice.push_back(15); - } - - slice = slice.insert(1, 50); - - // Test that we can use slice insert the same as slice push back - slice = slice.insert(6, 100); - - slice -} - -fn merge_slices_remove_between_ifs(x: Field, y: Field) -> [Field] { - let mut slice = [0; 2]; - if x != y { - slice = slice.push_back(y); - slice = slice.push_back(x); - } else { - slice = slice.push_back(x); - } - - let (mut slice, elem) = slice.remove(2); - assert(elem == y); - - if x == 20 { - slice = slice.push_back(20); - } - - slice = slice.push_back(15); - - if x != 20 { - slice = slice.push_back(50); - } - - slice -} - -// Previously, we'd get a type error when trying to assign an array of a different size to -// an existing array variable. Now, we infer the variable must be a slice. -fn regression_2370() { - let mut slice = []; - slice = [1, 2, 3]; -} diff --git a/tooling/nargo_cli/tests/execution_success/strings/src/main.nr b/tooling/nargo_cli/tests/execution_success/strings/src/main.nr deleted file mode 100644 index e3b5a5e1197..00000000000 --- a/tooling/nargo_cli/tests/execution_success/strings/src/main.nr +++ /dev/null @@ -1,74 +0,0 @@ -use dep::std; - -// Test global string literals -global HELLO_WORLD = "hello world"; - -fn main(message : pub str<11>, y : Field, hex_as_string : str<4>, hex_as_field : Field) { - let mut bad_message = "hello world"; - - assert(message == "hello world"); - assert(message == HELLO_WORLD); - let x = 10; - let z = x * 5; - std::println(10); - - std::println(z); // x * 5 in println not yet supported - std::println(x); - - let array = [1, 2, 3, 5, 8]; - assert(y == 5); // Change to y != 5 to see how the later print statements are not called - std::println(array); - - bad_message = "hell\0\"world"; - std::println(bad_message); - assert(message != bad_message); - - let hash = std::hash::pedersen_commitment([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); -} - -#[test] -fn test_prints_strings() { - let message = "hello world!"; - - std::println(message); - std::println("goodbye world"); -} - -#[test] -fn test_prints_array() { - let array = [1, 2, 3, 5, 8]; - - let s = Test { a: 1, b: 2, c: [3, 4] }; - std::println(s); - - std::println(array); - - let hash = std::hash::pedersen_commitment(array); - std::println(hash); -} - -fn failed_constraint(hex_as_field: Field) { - // TODO(#2116): Note that `println` will not work if a failed constraint can be - // evaluated at compile time. - // When this method is called from a test method or with constant values - // a `Failed constraint` compile error will be caught before this `println` - // is executed as the input will be a constant. - std::println(hex_as_field); - assert(hex_as_field != 0x41); -} - -#[test] -fn test_failed_constraint() { - failed_constraint(0x41); -} - -struct Test { - a: Field, - b: Field, - c: [Field; 2], -} diff --git a/tooling/nargo_cli/tests/execution_success/struct/src/main.nr b/tooling/nargo_cli/tests/execution_success/struct/src/main.nr deleted file mode 100644 index 745815227d9..00000000000 --- a/tooling/nargo_cli/tests/execution_success/struct/src/main.nr +++ /dev/null @@ -1,77 +0,0 @@ -struct Foo { - bar: Field, - array: [Field; 2], -} - -struct Pair { - first: Foo, - second: Field, -} - -impl Foo { - fn default(x: Field,y: Field) -> Self { - Self { bar: 0, array: [x,y] } - } -} - -impl Pair { - fn foo(p: Self) -> Foo { - p.first - } - - fn bar(self) -> Field { - self.foo().bar - } -} - -struct Nested { - a: Field, - b: Field -} -struct MyStruct { - my_bool: bool, - my_int: u32, - 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 } }; - (my_struct, a_bool) -} - -struct Animal { - legs: Field, - eyes: u8, -} - -fn get_dog() -> Animal { - let dog = Animal { legs: 4, eyes: 2 }; - dog -} - -struct Unit; - -fn main(x: Field, y: Field) { - let unit = Unit {}; - - let first = Foo::default(x, y); - let p = Pair { first, second: 1 }; - - assert(p.bar() == x); - assert(p.second == y); - assert(p.first.array[0] != p.first.array[1]); - - // Nested structs - 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); - assert(struct_from_tuple.my_nest.a == 0); - - // Regression test for issue #670 - let Animal { legs, eyes } = get_dog(); - let six = legs + eyes as Field; - - assert(six == 6); - - let Animal { legs: _, eyes: _ } = get_dog(); -} diff --git a/tooling/nargo_cli/tests/execution_success/struct_fields_ordering/src/main.nr b/tooling/nargo_cli/tests/execution_success/struct_fields_ordering/src/main.nr deleted file mode 100644 index 5d4aa7c5a1e..00000000000 --- a/tooling/nargo_cli/tests/execution_success/struct_fields_ordering/src/main.nr +++ /dev/null @@ -1,12 +0,0 @@ -// Note that fields are not in alphabetical order. -// We want to check that this ordering is maintained -struct myStruct { - foo: u32, - bar: Field, -} - -fn main(y : pub myStruct) { - assert(y.foo == 5); - assert(y.bar == 7); -} - diff --git a/tooling/nargo_cli/tests/execution_success/struct_inputs/src/main.nr b/tooling/nargo_cli/tests/execution_success/struct_inputs/src/main.nr deleted file mode 100644 index 96684d01a1d..00000000000 --- a/tooling/nargo_cli/tests/execution_success/struct_inputs/src/main.nr +++ /dev/null @@ -1,34 +0,0 @@ -mod foo; - -struct myStruct { - foo: u32, - bar: Field, - message: str<5>, -} - -fn main(x : Field, y : pub myStruct, z: pub foo::bar::barStruct, a: pub foo::fooStruct) -> pub Field { - let struct_from_bar = foo::bar::barStruct { val: 1, array: [0, 1], message: "hello" }; - - check_inner_struct(a, z); - - for i in 0 .. struct_from_bar.array.len() { - assert(struct_from_bar.array[i] == z.array[i]); - } - assert(z.val == struct_from_bar.val); - - assert((struct_from_bar.val * x) == x); - - assert(x != y.bar); - - assert(y.message == "hello"); - assert(a.bar_struct.message == struct_from_bar.message); - - a.bar_struct.array[1] -} - -fn check_inner_struct(a: foo::fooStruct, z: foo::bar::barStruct) { - assert(a.bar_struct.val == z.val); - for i in 0.. a.bar_struct.array.len() { - assert(a.bar_struct.array[i] == z.array[i]); - } -} 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 deleted file mode 100644 index 025738c42e6..00000000000 --- a/tooling/nargo_cli/tests/execution_success/to_be_bytes/src/main.nr +++ /dev/null @@ -1,12 +0,0 @@ -fn main(x : Field) -> pub [u8; 31] { - // The result of this byte array will be big-endian - let byte_array = x.to_be_bytes(31); - let mut bytes = [0; 31]; - for i in 0..31 { - bytes[i] = byte_array[i]; - } - if (bytes[30] != 60) | (bytes[29] != 33) | (bytes[28] != 31) { - assert(false); - } - bytes -} diff --git a/tooling/nargo_cli/tests/execution_success/to_bytes_consistent/src/main.nr b/tooling/nargo_cli/tests/execution_success/to_bytes_consistent/src/main.nr deleted file mode 100644 index 270491e132d..00000000000 --- a/tooling/nargo_cli/tests/execution_success/to_bytes_consistent/src/main.nr +++ /dev/null @@ -1,14 +0,0 @@ -// This test aims to check that we have consistent behavior -// between a `to_be_bytes` call (which is radix decomposition under the hood) -// with constant inputs or with witness inputs. - -// x = 2040124 -fn main(x : Field) { - let byte_array = x.to_be_bytes(31); - let x_as_constant = 2040124; - let constant_byte_array = x_as_constant.to_be_bytes(31); - assert(constant_byte_array.len() == byte_array.len()); - for i in 0..constant_byte_array.len() { - assert(constant_byte_array[i] == byte_array[i]); - } -} 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 deleted file mode 100644 index 6825d7f5562..00000000000 --- a/tooling/nargo_cli/tests/execution_success/to_bytes_integration/src/main.nr +++ /dev/null @@ -1,25 +0,0 @@ -use dep::std; - -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]); - assert(le_byte_array[2] == be_byte_array[28]); - - let z = 0 - 1; - let p_bytes = std::field::modulus_le_bytes(); - let z_bytes = z.to_le_bytes(32); - assert(p_bytes[10] == z_bytes[10]); - assert(p_bytes[0] == z_bytes[0] as u8 + 1 as u8); - - let p_bits = std::field::modulus_le_bits(); - let z_bits = z.to_le_bits(std::field::modulus_num_bits() as u32); - assert(z_bits[0] == 0); - assert(p_bits[100] == z_bits[100]); - - a.to_le_bits(std::field::modulus_num_bits() as u32); -} diff --git a/tooling/nargo_cli/tests/execution_success/to_le_bytes/src/main.nr b/tooling/nargo_cli/tests/execution_success/to_le_bytes/src/main.nr deleted file mode 100644 index 7155122b372..00000000000 --- a/tooling/nargo_cli/tests/execution_success/to_le_bytes/src/main.nr +++ /dev/null @@ -1,11 +0,0 @@ -fn main(x : Field) -> pub [u8; 31] { - // The result of this byte array will be little-endian - let byte_array = x.to_le_bytes(31); - assert(byte_array.len() == 31); - - let mut bytes = [0; 31]; - for i in 0..31 { - bytes[i] = byte_array[i]; - } - bytes -} diff --git a/tooling/nargo_cli/tests/execution_success/trait_as_return_type/src/main.nr b/tooling/nargo_cli/tests/execution_success/trait_as_return_type/src/main.nr deleted file mode 100644 index e84fe15aba7..00000000000 --- a/tooling/nargo_cli/tests/execution_success/trait_as_return_type/src/main.nr +++ /dev/null @@ -1,55 +0,0 @@ -trait SomeTrait { - fn magic_number(self) -> Field; -} - -struct A {} -struct B {} -struct C { - x: Field -} - - -impl SomeTrait for A { - fn magic_number(self) -> Field { - 2 - } -} - -impl SomeTrait for B { - fn magic_number(self) -> Field { - 4 - } -} - -impl SomeTrait for C { - fn magic_number(self) -> Field { - self.x - } -} - - - -fn factory_a() -> impl SomeTrait { - A {} -} - -fn factory_b() -> impl SomeTrait { - B {} -} - -fn factory_c(x: Field) -> impl SomeTrait { - C {x:x} -} - -// x = 15 -fn main(x: u32) { - let a = factory_a(); - let b = B {}; - let b2 = factory_b(); - assert(a.magic_number() == 2); - assert(b.magic_number() == 4); - assert(b2.magic_number() == 4); - let c = factory_c(10); - assert(c.magic_number() == 10); - assert(factory_c(13).magic_number() == 13); -} \ No newline at end of file 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 deleted file mode 100644 index e01e243a78f..00000000000 --- a/tooling/nargo_cli/tests/execution_success/trait_impl_base_type/src/main.nr +++ /dev/null @@ -1,118 +0,0 @@ -trait Fieldable { - fn to_field(self) -> Field; -} - -impl Fieldable for u32 { - fn to_field(self) -> Field { - let res = self as Field; - res * 3 - } -} - - -impl Fieldable for [u32; 3] { - fn to_field(self) -> Field { - let res = self[0] + self[1] + self[2]; - res as Field - } -} - -impl Fieldable for bool { - fn to_field(self) -> Field { - if self { - 14 - } else { - 3 - } - } -} - -impl Fieldable for (u32, bool) { - fn to_field(self) -> Field { - if self.1 { - self.0 as Field - } else { - 32 - } - } -} - -impl Fieldable for Field { - fn to_field(self) -> Field { - self - } -} - -impl Fieldable for str<6> { - fn to_field(self) -> Field { - 6 - } -} - -impl Fieldable for () { - fn to_field(self) -> Field { - 0 - } -} - -type Point2D = [Field; 2]; -type Point2DAlias = Point2D; - -impl Fieldable for Point2DAlias { - fn to_field(self) -> Field { - self[0] + self[1] - } -} - -impl Fieldable for fmtstr<14, (Field, Field)> { - fn to_field(self) -> Field { - 52 - } -} - -impl Fieldable for fn(u32) -> u32 { - fn to_field(self) -> Field { - self(10) as Field - } -} - -fn some_func(x: u32) -> u32 { - x * 2 - 3 -} - - -impl Fieldable for u64 { - fn to_field(self) -> Field { - 66 as Field - } -} - -// x = 15 -fn main(x: u32) { - assert(x.to_field() == 15); - let arr: [u32; 3] = [3, 5, 8]; - assert(arr.to_field() == 16); - let b_true = 2 == 2; - assert(b_true.to_field() == 14); - let b_false = 2 == 3; - assert(b_false.to_field() == 3); - let f = 13 as Field; - assert(f.to_field() == 13); - let k_true = (12 as u32, true); - assert(k_true.to_field() == 12); - let k_false = (11 as u32, false); - assert(k_false.to_field() == 32); - let m = "String"; - assert(m.to_field() == 6); - let unit = (); - assert(unit.to_field() == 0); - let point: Point2DAlias = [2, 3]; - assert(point.to_field() == 5); - let i: Field = 2; - let j: Field = 6; - assert(f"i: {i}, j: {j}".to_field() == 52); - assert(some_func.to_field() == 17); - - let mut y = 0 as u64; - assert(y.to_field() == 66); -} diff --git a/tooling/nargo_cli/tests/execution_success/traits_in_crates_1/src/main.nr b/tooling/nargo_cli/tests/execution_success/traits_in_crates_1/src/main.nr deleted file mode 100644 index cb6416f7732..00000000000 --- a/tooling/nargo_cli/tests/execution_success/traits_in_crates_1/src/main.nr +++ /dev/null @@ -1,5 +0,0 @@ -fn main(x : Field, y : pub Field) { - let mut V = dep::crate2::MyStruct { Q: x }; - V.Add10(); - assert(V.Q == y); -} diff --git a/tooling/nargo_cli/tests/execution_success/traits_in_crates_2/src/main.nr b/tooling/nargo_cli/tests/execution_success/traits_in_crates_2/src/main.nr deleted file mode 100644 index cb6416f7732..00000000000 --- a/tooling/nargo_cli/tests/execution_success/traits_in_crates_2/src/main.nr +++ /dev/null @@ -1,5 +0,0 @@ -fn main(x : Field, y : pub Field) { - let mut V = dep::crate2::MyStruct { Q: x }; - V.Add10(); - assert(V.Q == y); -} 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 deleted file mode 100644 index 64621411c84..00000000000 --- a/tooling/nargo_cli/tests/execution_success/tuple_inputs/src/main.nr +++ /dev/null @@ -1,35 +0,0 @@ -struct Bar { - inner: [Field; 3], -} - -struct Foo { - a: Field, - b: [Field; 3], - bar: Bar, -} - -fn main(pair : (Field, Field), x: [(u8, u8, u8); 2], struct_pair: (Foo, Bar)) -> pub (Field, u8) { - let mut start_val = 0; - for i in 0..2 { - assert(x[i].0 == start_val); - assert(x[i].1 == start_val + 1); - assert(x[i].2 == start_val + 2); - start_val += 3; - } - - assert(struct_pair.0.a == 1); - assert(struct_pair.0.b == [2, 3, 20]); - assert(struct_pair.0.bar.inner == [100, 101, 102]); - assert(struct_pair.1.inner == [103, 104, 105]); - - let (u, v) = if pair.0 as u32 < 1 { - (pair.0, pair.0 + 1) - } else { - (pair.0 + 1, pair.0) - }; - - assert(u == pair.0 + 1); - assert(v == pair.0); - - (u, v as u8) -} diff --git a/tooling/nargo_cli/tests/execution_success/tuples/src/main.nr b/tooling/nargo_cli/tests/execution_success/tuples/src/main.nr deleted file mode 100644 index b6f6d695f2c..00000000000 --- a/tooling/nargo_cli/tests/execution_success/tuples/src/main.nr +++ /dev/null @@ -1,23 +0,0 @@ -fn main(x: Field, y: Field) { - let pair = (x, y); - assert(pair.0 == 1); - assert(pair.1 == 0); - - let (a, b) = if true { (0, 1) } else { (2, 3) }; - 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); - assert(v == x); - - // Test mutating tuples - let mut mutable = ((0, 0), 1, 2, 3); - mutable.0 = (x, y); - mutable.2 = 7; - assert(mutable.0.0 == 1); - assert(mutable.0.1 == 0); - assert(mutable.1 == 1); - assert(mutable.2 == 7); - assert(mutable.3 == 3); -} 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 deleted file mode 100644 index c9db6d81f90..00000000000 --- a/tooling/nargo_cli/tests/execution_success/type_aliases/src/main.nr +++ /dev/null @@ -1,34 +0,0 @@ -type Foo = [T; 2]; - -type Bar = Field; - -type One = (A, B); -type Two = One; -type Three = Two; - -struct MyStruct { - foo: Bar, -} - -fn main(x : [Field; 2]) { - let a: Foo = [1, 2]; - assert(a[0] != x[0]); - - let b: Bar = 2; - assert(x[0] == b); - - let c: u8 = 1; - let d: u32 = 2; - let e: Three = (c, d); - assert(e.0 == 1); - - let s = MyStruct { foo: 10 }; - assert(s.foo == 10); - - let _regression2502: Regression2502Alias = Regression2502 {}; -} - -// An ICE was occurring if a type alias referred to a struct before it was initialized -// during name resolution. The fix was to initialize structs during def collection instead. -type Regression2502Alias = Regression2502; -struct Regression2502 {} diff --git a/tooling/nargo_cli/tests/execution_success/workspace/crates/a/src/main.nr b/tooling/nargo_cli/tests/execution_success/workspace/crates/a/src/main.nr deleted file mode 100644 index 550e5034a7b..00000000000 --- a/tooling/nargo_cli/tests/execution_success/workspace/crates/a/src/main.nr +++ /dev/null @@ -1,3 +0,0 @@ -fn main(x : Field, y : pub Field) { - assert(x == y); -} diff --git a/tooling/nargo_cli/tests/execution_success/workspace/crates/b/src/main.nr b/tooling/nargo_cli/tests/execution_success/workspace/crates/b/src/main.nr deleted file mode 100644 index 6e170de75fc..00000000000 --- a/tooling/nargo_cli/tests/execution_success/workspace/crates/b/src/main.nr +++ /dev/null @@ -1,3 +0,0 @@ -fn main(x : Field, y : pub Field) { - assert(x != y); -} diff --git a/tooling/nargo_cli/tests/execution_success/workspace_default_member/a/src/main.nr b/tooling/nargo_cli/tests/execution_success/workspace_default_member/a/src/main.nr deleted file mode 100644 index 550e5034a7b..00000000000 --- a/tooling/nargo_cli/tests/execution_success/workspace_default_member/a/src/main.nr +++ /dev/null @@ -1,3 +0,0 @@ -fn main(x : Field, y : pub Field) { - assert(x == y); -} diff --git a/tooling/nargo_cli/tests/execution_success/workspace_default_member/b/src/main.nr b/tooling/nargo_cli/tests/execution_success/workspace_default_member/b/src/main.nr deleted file mode 100644 index 6e170de75fc..00000000000 --- a/tooling/nargo_cli/tests/execution_success/workspace_default_member/b/src/main.nr +++ /dev/null @@ -1,3 +0,0 @@ -fn main(x : Field, y : pub Field) { - assert(x != y); -} diff --git a/tooling/nargo_cli/tests/execution_success/xor/src/main.nr b/tooling/nargo_cli/tests/execution_success/xor/src/main.nr deleted file mode 100644 index d4261aa786a..00000000000 --- a/tooling/nargo_cli/tests/execution_success/xor/src/main.nr +++ /dev/null @@ -1,5 +0,0 @@ -fn main(x : u32, y : pub u32) { - let m = x ^ y; - - assert(m != 10); -} 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 deleted file mode 100644 index 6c8dbda5149..00000000000 --- a/tooling/nargo_cli/tests/noir_test_failure/should_fail_mismatch/src/main.nr +++ /dev/null @@ -1,16 +0,0 @@ -#[test(should_fail_with = "Not equal")] -fn test_different_string() { - assert_eq(0, 1, "Different string"); -} - -// The assert message has a space -#[test(should_fail_with = "Not equal")] -fn test_with_extra_space() { - assert_eq(0, 1, "Not equal "); -} - -// The assert message has a space -#[test(should_fail_with = "Not equal")] -fn test_runtime_mismatch() { - assert_eq(dep::std::hash::pedersen_commitment([27])[0], 0, "Not equal "); -} diff --git a/tooling/nargo_cli/tests/rebuild.sh b/tooling/nargo_cli/tests/rebuild.sh deleted file mode 100755 index d89402fb1cf..00000000000 --- a/tooling/nargo_cli/tests/rebuild.sh +++ /dev/null @@ -1,56 +0,0 @@ -#!/bin/bash -set -e - -excluded_dirs=("workspace" "workspace_default_member") - -current_dir=$(pwd) -base_path="$current_dir/execution_success" - -# Clear the acir_artifacts directory of any existing artifacts -rm -rf $current_dir/acir_artifacts -mkdir -p $current_dir/acir_artifacts - -# Loop over every directory -for dir in $base_path/*; do - if [[ ! -d $dir ]]; then - continue - fi - - dir_name=$(basename "$dir") - - if [[ ! " ${excluded_dirs[@]} " =~ " ${dir_name} " ]]; then - if [[ ! -d "$current_dir/acir_artifacts/$dir_name" ]]; then - mkdir -p $current_dir/acir_artifacts/$dir_name - fi - - cd $dir - if [ -d ./target/ ]; then - rm -r ./target/ - fi - nargo compile && nargo execute witness - - # Rename witness.tr to witness.gz - if [ -f ./target/witness.tr ]; then - mv ./target/witness.tr ./target/witness.gz - fi - - # Extract bytecode field from JSON, base64 decode it, and save it to the target directory - if [ -f ./target/${dir_name}.json ]; then - jq -r '.bytecode' ./target/${dir_name}.json | base64 -d > ./target/acir.gz - fi - - # Delete the JSON file after extracting bytecode field - rm ./target/${dir_name}.json - - # 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 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 -done diff --git a/tooling/nargo_cli/tests/test_libraries/bin_dep/src/main.nr b/tooling/nargo_cli/tests/test_libraries/bin_dep/src/main.nr deleted file mode 100644 index d469d3aafcb..00000000000 --- a/tooling/nargo_cli/tests/test_libraries/bin_dep/src/main.nr +++ /dev/null @@ -1,3 +0,0 @@ -fn call_dep1_then_dep2(x : Field, y : Field) { - assert(x == y); -} diff --git a/tooling/nargo_cli/tests/test_libraries/diamond_deps_1/src/lib.nr b/tooling/nargo_cli/tests/test_libraries/diamond_deps_1/src/lib.nr deleted file mode 100644 index d63b835c8a7..00000000000 --- a/tooling/nargo_cli/tests/test_libraries/diamond_deps_1/src/lib.nr +++ /dev/null @@ -1,5 +0,0 @@ -use dep::dep2::call_dep2; - -pub fn call_dep1_then_dep2(x : Field, y : Field) -> Field { - call_dep2(x, y) -} 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 deleted file mode 100644 index b0d09c70648..00000000000 --- a/tooling/nargo_cli/tests/test_libraries/diamond_deps_2/src/lib.nr +++ /dev/null @@ -1,5 +0,0 @@ -global RESOLVE_THIS = 3; - -pub fn call_dep2(x : Field, y : Field) -> Field { - x + y -} diff --git a/tooling/nargo_fmt/Cargo.toml b/tooling/nargo_fmt/Cargo.toml index 921c9893ab5..374413ac9f2 100644 --- a/tooling/nargo_fmt/Cargo.toml +++ b/tooling/nargo_fmt/Cargo.toml @@ -13,4 +13,4 @@ toml.workspace = true thiserror.workspace = true [dev-dependencies] -similar-asserts = "1.5.0" +similar-asserts.workspace = true diff --git a/tooling/nargo_fmt/build.rs b/tooling/nargo_fmt/build.rs index 3da07715e4e..c356b403ae5 100644 --- a/tooling/nargo_fmt/build.rs +++ b/tooling/nargo_fmt/build.rs @@ -46,8 +46,8 @@ fn generate_formatter_tests(test_file: &mut File, test_data_dir: &Path) { .collect::>() .join("\n"); - let output_source_path = outputs_dir.join(file_name); - let output_source = std::fs::read_to_string(output_source_path).unwrap(); + let output_source_path = outputs_dir.join(file_name).display().to_string(); + let output_source = std::fs::read_to_string(output_source_path.clone()).unwrap(); write!( test_file, @@ -58,12 +58,14 @@ fn format_{test_name}() {{ let expected_output = r#"{output_source}"#; - let (parsed_module, errors) = noirc_frontend::parse_program(&input); - assert!(errors.is_empty()); + let (parsed_module, _errors) = noirc_frontend::parse_program(&input); let config = nargo_fmt::Config::of("{config}").unwrap(); let fmt_text = nargo_fmt::format(&input, parsed_module, &config); + if std::env::var("UPDATE_EXPECT").is_ok() {{ + std::fs::write("{output_source_path}", fmt_text.clone()).unwrap(); + }} similar_asserts::assert_eq!(fmt_text, expected_output); }} diff --git a/tooling/nargo_fmt/src/config.rs b/tooling/nargo_fmt/src/config.rs index c026b1576c6..2bb5d97c0af 100644 --- a/tooling/nargo_fmt/src/config.rs +++ b/tooling/nargo_fmt/src/config.rs @@ -48,6 +48,7 @@ config! { error_on_lost_comment: bool, true, "Error if unable to get comments"; 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"; + fn_call_width: usize, 60, "Maximum width of the args of a function call before falling back to vertical formatting"; single_line_if_else_max_width: usize, 50, "Maximum line length for single line if-else expressions"; } diff --git a/tooling/nargo_fmt/src/lib.rs b/tooling/nargo_fmt/src/lib.rs index 39e978aa306..d731934c3c3 100644 --- a/tooling/nargo_fmt/src/lib.rs +++ b/tooling/nargo_fmt/src/lib.rs @@ -20,6 +20,7 @@ /// in both placement and content during the formatting process. mod config; pub mod errors; +mod rewrite; mod utils; mod visitor; diff --git a/tooling/nargo_fmt/src/rewrite.rs b/tooling/nargo_fmt/src/rewrite.rs new file mode 100644 index 00000000000..6a95eba8759 --- /dev/null +++ b/tooling/nargo_fmt/src/rewrite.rs @@ -0,0 +1,11 @@ +mod array; +mod expr; +mod infix; +mod parenthesized; +mod typ; + +pub(crate) use array::rewrite as array; +pub(crate) use expr::{rewrite as expr, rewrite_sub_expr as sub_expr}; +pub(crate) use infix::rewrite as infix; +pub(crate) use parenthesized::rewrite as parenthesized; +pub(crate) use typ::rewrite as typ; diff --git a/tooling/nargo_fmt/src/rewrite/array.rs b/tooling/nargo_fmt/src/rewrite/array.rs new file mode 100644 index 00000000000..fc5b240f83e --- /dev/null +++ b/tooling/nargo_fmt/src/rewrite/array.rs @@ -0,0 +1,85 @@ +use noirc_frontend::{hir::resolution::errors::Span, token::Token, Expression}; + +use crate::{ + utils::{Expr, FindToken}, + visitor::{expr::NewlineMode, FmtVisitor}, +}; + +pub(crate) fn rewrite(mut visitor: FmtVisitor, array: Vec, array_span: Span) -> String { + let pattern: &[_] = &[' ', '\t']; + + visitor.indent.block_indent(visitor.config); + let nested_indent = visitor.shape(); + + let indent_str = nested_indent.indent.to_string(); + + let mut last_position = array_span.start() + 1; + let end_position = array_span.end() - 1; + + let mut items = array.into_iter().peekable(); + + let mut result = Vec::new(); + while let Some(item) = items.next() { + let item_span = item.span; + + let start: u32 = last_position; + let end = item_span.start(); + + let leading = visitor.slice(start..end).trim_matches(pattern); + let item = super::sub_expr(&visitor, visitor.shape(), item); + let next_start = items.peek().map_or(end_position, |expr| expr.span.start()); + let trailing = visitor.slice(item_span.end()..next_start); + let offset = trailing + .find_token(Token::Comma) + .map(|span| span.end() as usize) + .unwrap_or(trailing.len()); + let trailing = trailing[..offset].trim_end_matches(',').trim_matches(pattern); + last_position = item_span.end() + offset as u32; + + let (leading, _) = visitor.format_comment_in_block(leading); + let (trailing, _) = visitor.format_comment_in_block(trailing); + + result.push(Expr { leading, value: item, trailing, different_line: false }); + } + + let slice = visitor.slice(last_position..end_position); + let (comment, _) = visitor.format_comment_in_block(slice); + result.push(Expr { + leading: "".into(), + value: "".into(), + trailing: comment, + different_line: false, + }); + + visitor.indent.block_unindent(visitor.config); + + let mut items_str = String::new(); + let mut items = result.into_iter().peekable(); + while let Some(next) = items.next() { + items_str.push_str(&next.leading); + if next.leading.contains('\n') && !next.value.is_empty() { + items_str.push_str(&indent_str); + } + items_str.push_str(&next.value); + items_str.push_str(&next.trailing); + + if let Some(item) = items.peek() { + if !item.value.is_empty() { + items_str.push(','); + } + + if !item.leading.contains('\n') && !next.value.is_empty() { + items_str.push(' '); + } + } + } + + crate::visitor::expr::wrap_exprs( + "[", + "]", + items_str.trim().into(), + nested_indent, + visitor.shape(), + NewlineMode::IfContainsNewLineAndWidth, + ) +} diff --git a/tooling/nargo_fmt/src/rewrite/expr.rs b/tooling/nargo_fmt/src/rewrite/expr.rs new file mode 100644 index 00000000000..3c46319c1aa --- /dev/null +++ b/tooling/nargo_fmt/src/rewrite/expr.rs @@ -0,0 +1,155 @@ +use noirc_frontend::{token::Token, ArrayLiteral, Expression, ExpressionKind, Literal, UnaryOp}; + +use crate::visitor::{ + expr::{format_brackets, format_parens, NewlineMode}, + ExpressionType, FmtVisitor, Indent, Shape, +}; + +pub(crate) fn rewrite_sub_expr( + visitor: &FmtVisitor, + shape: Shape, + expression: Expression, +) -> String { + rewrite(visitor, expression, ExpressionType::SubExpression, shape) +} + +pub(crate) fn rewrite( + visitor: &FmtVisitor, + Expression { kind, span }: Expression, + expr_type: ExpressionType, + shape: Shape, +) -> String { + match kind { + ExpressionKind::Block(block) => { + let mut visitor = visitor.fork(); + visitor.visit_block(block, span); + visitor.finish() + } + ExpressionKind::Prefix(prefix) => { + let op = match prefix.operator { + UnaryOp::Minus => "-", + UnaryOp::Not => "!", + UnaryOp::MutableReference => "&mut ", + UnaryOp::Dereference { implicitly_added } => { + if implicitly_added { + "" + } else { + "*" + } + } + }; + + format!("{op}{}", rewrite_sub_expr(visitor, shape, prefix.rhs)) + } + ExpressionKind::Cast(cast) => { + format!("{} as {}", rewrite_sub_expr(visitor, shape, cast.lhs), cast.r#type) + } + kind @ ExpressionKind::Infix(_) => { + super::infix(visitor.fork(), Expression { kind, span }, shape) + } + ExpressionKind::Call(call_expr) => { + let args_span = + visitor.span_before(call_expr.func.span.end()..span.end(), Token::LeftParen); + + let callee = rewrite_sub_expr(visitor, shape, *call_expr.func); + let args = format_parens( + visitor.config.fn_call_width.into(), + visitor.fork(), + shape, + false, + call_expr.arguments, + args_span, + true, + NewlineMode::IfContainsNewLineAndWidth, + ); + + format!("{callee}{args}") + } + ExpressionKind::MethodCall(method_call_expr) => { + let args_span = visitor.span_before( + method_call_expr.method_name.span().end()..span.end(), + Token::LeftParen, + ); + + let object = rewrite_sub_expr(visitor, shape, method_call_expr.object); + let method = method_call_expr.method_name.to_string(); + let args = format_parens( + visitor.config.fn_call_width.into(), + visitor.fork(), + shape, + false, + method_call_expr.arguments, + args_span, + true, + NewlineMode::IfContainsNewLineAndWidth, + ); + + format!("{object}.{method}{args}") + } + ExpressionKind::MemberAccess(member_access_expr) => { + let lhs_str = rewrite_sub_expr(visitor, shape, member_access_expr.lhs); + format!("{}.{}", lhs_str, member_access_expr.rhs) + } + ExpressionKind::Index(index_expr) => { + let index_span = visitor + .span_before(index_expr.collection.span.end()..span.end(), Token::LeftBracket); + + let collection = rewrite_sub_expr(visitor, shape, index_expr.collection); + let index = format_brackets(visitor.fork(), false, vec![index_expr.index], index_span); + + format!("{collection}{index}") + } + ExpressionKind::Tuple(exprs) => format_parens( + None, + visitor.fork(), + shape, + exprs.len() == 1, + exprs, + span, + true, + NewlineMode::Normal, + ), + ExpressionKind::Literal(literal) => match literal { + Literal::Integer(_) + | Literal::Bool(_) + | Literal::Str(_) + | Literal::RawStr(..) + | Literal::FmtStr(_) => visitor.slice(span).to_string(), + Literal::Array(ArrayLiteral::Repeated { repeated_element, length }) => { + let repeated = rewrite_sub_expr(visitor, shape, *repeated_element); + let length = rewrite_sub_expr(visitor, shape, *length); + + format!("[{repeated}; {length}]") + } + Literal::Array(ArrayLiteral::Standard(exprs)) => { + super::array(visitor.fork(), exprs, span) + } + Literal::Unit => "()".to_string(), + }, + ExpressionKind::Parenthesized(sub_expr) => { + super::parenthesized(visitor, shape, span, *sub_expr) + } + ExpressionKind::Constructor(constructor) => { + let type_name = visitor.slice(span.start()..constructor.type_name.span().end()); + let fields_span = visitor + .span_before(constructor.type_name.span().end()..span.end(), Token::LeftBrace); + + visitor.format_struct_lit(type_name, fields_span, *constructor) + } + ExpressionKind::If(if_expr) => { + let allow_single_line = expr_type == ExpressionType::SubExpression; + + if allow_single_line { + let mut visitor = visitor.fork(); + visitor.indent = Indent::default(); + if let Some(line) = visitor.format_if_single_line(*if_expr.clone()) { + return line; + } + } + + visitor.format_if(*if_expr) + } + ExpressionKind::Lambda(_) | ExpressionKind::Variable(_) => visitor.slice(span).to_string(), + ExpressionKind::Error => unreachable!(), + } +} diff --git a/tooling/nargo_fmt/src/rewrite/infix.rs b/tooling/nargo_fmt/src/rewrite/infix.rs new file mode 100644 index 00000000000..15f5fe23aae --- /dev/null +++ b/tooling/nargo_fmt/src/rewrite/infix.rs @@ -0,0 +1,113 @@ +use std::iter::zip; + +use noirc_frontend::{Expression, ExpressionKind}; + +use crate::{ + rewrite, + utils::{first_line_width, is_single_line}, + visitor::{FmtVisitor, Shape}, +}; + +pub(crate) fn rewrite(visitor: FmtVisitor, expr: Expression, shape: Shape) -> String { + match flatten(visitor.fork(), &expr) { + Some((exprs, separators)) => rewrite_single_line(shape, &exprs, &separators) + .unwrap_or_else(|| rewrite_multiline(visitor, &exprs, &separators)), + None => { + let ExpressionKind::Infix(infix) = expr.kind else { unreachable!() }; + + format!( + "{} {} {}", + rewrite::sub_expr(&visitor, shape, infix.lhs), + infix.operator.contents.as_string(), + rewrite::sub_expr(&visitor, shape, infix.rhs) + ) + } + } +} + +fn rewrite_single_line(shape: Shape, exprs: &[String], separators: &[String]) -> Option { + let mut result = String::new(); + + for (rewrite, separator) in zip(exprs, separators) { + if !is_single_line(rewrite) || result.len() > shape.width { + return None; + } + + result.push_str(rewrite); + result.push(' '); + result.push_str(separator); + result.push(' '); + } + + let last = exprs.last().unwrap(); + result.push_str(last); + + if first_line_width(&result) > shape.width { + return None; + } + + result.into() +} + +fn rewrite_multiline(visitor: FmtVisitor, exprs: &[String], separators: &[String]) -> String { + let mut visitor = visitor.fork(); + visitor.indent.block_indent(visitor.config); + let indent_str = visitor.indent.to_string_with_newline(); + + let mut result = exprs[0].clone(); + + for (rewrite, separator) in exprs[1..].iter().zip(separators.iter()) { + result.push_str(&indent_str); + result.push_str(separator); + result.push(' '); + result.push_str(rewrite); + } + + result +} + +pub(crate) fn flatten( + mut visitor: FmtVisitor, + mut node: &Expression, +) -> Option<(Vec, Vec)> { + let top_operator = match node.kind { + ExpressionKind::Infix(ref infix) => infix.operator.contents, + _ => return None, + }; + + let mut result = Vec::new(); + + let mut stack: Vec<&Expression> = Vec::new(); + let mut separators = Vec::new(); + + loop { + match &node.kind { + ExpressionKind::Infix(infix) if top_operator == infix.operator.contents => { + stack.push(node); + node = &infix.lhs; + } + _ => { + let rewrite = if result.is_empty() { + rewrite::sub_expr(&visitor, visitor.shape(), node.clone()) + } else { + visitor.indent.block_indent(visitor.config); + rewrite::sub_expr(&visitor, visitor.shape(), node.clone()) + }; + + result.push(rewrite); + + let Some(pop) = stack.pop() else { break; }; + + match &pop.kind { + ExpressionKind::Infix(infix) => { + separators.push(infix.operator.contents.to_string()); + node = &infix.rhs; + } + _ => unreachable!(), + } + } + } + } + + (result, separators).into() +} diff --git a/tooling/nargo_fmt/src/rewrite/parenthesized.rs b/tooling/nargo_fmt/src/rewrite/parenthesized.rs new file mode 100644 index 00000000000..3926b52cb73 --- /dev/null +++ b/tooling/nargo_fmt/src/rewrite/parenthesized.rs @@ -0,0 +1,67 @@ +use noirc_frontend::{hir::resolution::errors::Span, Expression, ExpressionKind}; + +use crate::visitor::{FmtVisitor, Shape}; + +pub(crate) fn rewrite( + visitor: &FmtVisitor<'_>, + shape: Shape, + mut span: Span, + mut sub_expr: Expression, +) -> String { + let remove_nested_parens = visitor.config.remove_nested_parens; + + let mut leading; + let mut trailing; + + loop { + let leading_span = span.start() + 1..sub_expr.span.start(); + let trailing_span = sub_expr.span.end()..span.end() - 1; + + leading = visitor.format_comment(leading_span.into()); + trailing = visitor.format_comment(trailing_span.into()); + + if let ExpressionKind::Parenthesized(ref sub_sub_expr) = sub_expr.kind { + if remove_nested_parens && leading.is_empty() && trailing.is_empty() { + span = sub_expr.span; + sub_expr = *sub_sub_expr.clone(); + continue; + } + } + + break; + } + + if !leading.contains("//") && !trailing.contains("//") { + let sub_expr = super::sub_expr(visitor, shape, sub_expr); + format!("({leading}{sub_expr}{trailing})") + } else { + let mut visitor = visitor.fork(); + + let indent = visitor.indent.to_string_with_newline(); + visitor.indent.block_indent(visitor.config); + let nested_indent = visitor.indent.to_string_with_newline(); + + let sub_expr = super::sub_expr(&visitor, shape, sub_expr); + + let mut result = String::new(); + result.push('('); + + if !leading.is_empty() { + result.push_str(&nested_indent); + result.push_str(&leading); + } + + result.push_str(&nested_indent); + result.push_str(&sub_expr); + + if !trailing.is_empty() { + result.push_str(&nested_indent); + result.push_str(&trailing); + } + + result.push_str(&indent); + result.push(')'); + + result + } +} diff --git a/tooling/nargo_fmt/src/rewrite/typ.rs b/tooling/nargo_fmt/src/rewrite/typ.rs new file mode 100644 index 00000000000..4c6411e92b8 --- /dev/null +++ b/tooling/nargo_fmt/src/rewrite/typ.rs @@ -0,0 +1,70 @@ +use noirc_frontend::{UnresolvedType, UnresolvedTypeData}; + +use crate::{ + utils::span_is_empty, + visitor::{FmtVisitor, Shape}, +}; + +pub(crate) fn rewrite(visitor: &FmtVisitor, _shape: Shape, typ: UnresolvedType) -> String { + match typ.typ { + UnresolvedTypeData::Array(length, element) => { + let typ = rewrite(visitor, _shape, *element); + if let Some(length) = length { + let length = visitor.slice(length.span()); + format!("[{typ}; {length}]") + } else { + format!("[{typ}]") + } + } + UnresolvedTypeData::Parenthesized(typ) => { + let typ = rewrite(visitor, _shape, *typ); + format!("({typ})") + } + UnresolvedTypeData::MutableReference(typ) => { + let typ = rewrite(visitor, _shape, *typ); + format!("&mut {typ}") + } + UnresolvedTypeData::Tuple(mut types) => { + if types.len() == 1 { + let typ = types.pop().unwrap(); + let typ = rewrite(visitor, _shape, typ); + + format!("({typ},)") + } else { + let types: Vec<_> = + types.into_iter().map(|typ| rewrite(visitor, _shape, typ)).collect(); + let types = types.join(", "); + format!("({types})") + } + } + UnresolvedTypeData::Function(args, return_type, env) => { + let env = if span_is_empty(env.span.unwrap()) { + "".into() + } else { + let ty = rewrite(visitor, _shape, *env); + format!("[{ty}]") + }; + + let args = args + .into_iter() + .map(|arg| rewrite(visitor, _shape, arg)) + .collect::>() + .join(", "); + + let return_type = rewrite(visitor, _shape, *return_type); + + format!("fn{env}({args}) -> {return_type}") + } + UnresolvedTypeData::Unspecified => todo!(), + UnresolvedTypeData::FieldElement + | UnresolvedTypeData::Integer(_, _) + | UnresolvedTypeData::Bool + | UnresolvedTypeData::Named(_, _) + | UnresolvedTypeData::Unit + | UnresolvedTypeData::Expression(_) + | UnresolvedTypeData::String(_) + | UnresolvedTypeData::FormatString(_, _) + | UnresolvedTypeData::TraitAsType(_, _) => visitor.slice(typ.span.unwrap()).into(), + UnresolvedTypeData::Error => unreachable!(), + } +} diff --git a/tooling/nargo_fmt/src/utils.rs b/tooling/nargo_fmt/src/utils.rs index 7bb40956b2e..4fbc1e8f85b 100644 --- a/tooling/nargo_fmt/src/utils.rs +++ b/tooling/nargo_fmt/src/utils.rs @@ -1,8 +1,9 @@ -use crate::visitor::FmtVisitor; +use crate::rewrite; +use crate::visitor::{FmtVisitor, Shape}; use noirc_frontend::hir::resolution::errors::Span; use noirc_frontend::lexer::Lexer; use noirc_frontend::token::Token; -use noirc_frontend::{Expression, Ident}; +use noirc_frontend::{Expression, Ident, Param, Visibility}; pub(crate) fn changed_comment_content(original: &str, new: &str) -> bool { comments(original).ne(comments(new)) @@ -40,15 +41,22 @@ impl Expr { pub(crate) struct Exprs<'me, T> { pub(crate) visitor: &'me FmtVisitor<'me>, + shape: Shape, pub(crate) elements: std::iter::Peekable>, pub(crate) last_position: u32, pub(crate) end_position: u32, } impl<'me, T: Item> Exprs<'me, T> { - pub(crate) fn new(visitor: &'me FmtVisitor<'me>, span: Span, elements: Vec) -> Self { + pub(crate) fn new( + visitor: &'me FmtVisitor<'me>, + shape: Shape, + span: Span, + elements: Vec, + ) -> Self { Self { visitor, + shape, last_position: span.start() + 1, /*(*/ end_position: span.end() - 1, /*)*/ elements: elements.into_iter().peekable(), @@ -70,7 +78,7 @@ impl Iterator for Exprs<'_, T> { let next_start = self.elements.peek().map_or(self.end_position, |expr| expr.start()); let (leading, different_line) = self.leading(start, end); - let expr = element.format(self.visitor); + let expr = element.format(self.visitor, self.shape); let trailing = self.trailing(element_span.end(), next_start, is_last); Expr { leading, value: expr, trailing, different_line }.into() @@ -112,7 +120,7 @@ impl<'me, T> Exprs<'me, T> { pub(crate) trait FindToken { fn find_token(&self, token: Token) -> Option; - fn find_token_with(&self, f: impl Fn(&Token) -> bool) -> Option; + fn find_token_with(&self, f: impl Fn(&Token) -> bool) -> Option; } impl FindToken for str { @@ -120,11 +128,11 @@ impl FindToken for str { 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 { + fn find_token_with(&self, f: impl Fn(&Token) -> bool) -> Option { Lexer::new(self) .skip_comments(false) .flatten() - .find_map(|spanned| f(spanned.token()).then(|| spanned.to_span().end())) + .find_map(|spanned| f(spanned.token()).then(|| spanned.to_span())) } } @@ -134,7 +142,7 @@ pub(crate) fn find_comment_end(slice: &str, is_last: bool) -> usize { .find_token_with(|token| { matches!(token, Token::LineComment(_, _) | Token::BlockComment(_, _)) }) - .map(|index| index as usize) + .map(|index| index.end() as usize) .unwrap_or(slice.len()) } @@ -189,10 +197,14 @@ fn comment_len(comment: &str) -> usize { } } +pub(crate) fn count_newlines(slice: &str) -> usize { + bytecount::count(slice.as_bytes(), b'\n') +} + pub(crate) trait Item { fn span(&self) -> Span; - fn format(self, visitor: &FmtVisitor) -> String; + fn format(self, visitor: &FmtVisitor, shape: Shape) -> String; fn start(&self) -> u32 { self.span().start() @@ -208,8 +220,8 @@ impl Item for Expression { self.span } - fn format(self, visitor: &FmtVisitor) -> String { - visitor.format_sub_expr(self) + fn format(self, visitor: &FmtVisitor, shape: Shape) -> String { + rewrite::sub_expr(visitor, shape, self) } } @@ -219,11 +231,11 @@ impl Item for (Ident, Expression) { (name.span().start()..value.span.end()).into() } - fn format(self, visitor: &FmtVisitor) -> String { + fn format(self, visitor: &FmtVisitor, shape: Shape) -> String { let (name, expr) = self; let name = name.0.contents; - let expr = visitor.format_sub_expr(expr); + let expr = rewrite::sub_expr(visitor, shape, expr); if name == expr { name @@ -232,3 +244,58 @@ impl Item for (Ident, Expression) { } } } + +impl Item for Param { + fn span(&self) -> Span { + self.span + } + + fn format(self, visitor: &FmtVisitor, shape: Shape) -> String { + let visibility = match self.visibility { + Visibility::Public => "pub ", + Visibility::Private => "", + }; + let pattern = visitor.slice(self.pattern.span()); + let ty = rewrite::typ(visitor, shape, self.typ); + + format!("{pattern}: {visibility}{ty}") + } +} + +impl Item for Ident { + fn span(&self) -> Span { + self.span() + } + + fn format(self, visitor: &FmtVisitor, _shape: Shape) -> String { + visitor.slice(self.span()).into() + } +} + +pub(crate) fn first_line_width(exprs: &str) -> usize { + exprs.lines().next().map_or(0, |line: &str| line.chars().count()) +} + +pub(crate) fn last_line_width(s: &str) -> usize { + s.rsplit('\n').next().unwrap_or("").chars().count() +} + +pub(crate) fn is_single_line(s: &str) -> bool { + !s.chars().any(|c| c == '\n') +} + +pub(crate) fn last_line_contains_single_line_comment(s: &str) -> bool { + s.lines().last().map_or(false, |line| line.contains("//")) +} + +pub(crate) fn last_line_used_width(s: &str, offset: usize) -> usize { + if s.contains('\n') { + last_line_width(s) + } else { + offset + s.chars().count() + } +} + +pub(crate) fn span_is_empty(span: Span) -> bool { + span.start() == span.end() +} diff --git a/tooling/nargo_fmt/src/visitor.rs b/tooling/nargo_fmt/src/visitor.rs index fd156074ec3..85989db79d8 100644 --- a/tooling/nargo_fmt/src/visitor.rs +++ b/tooling/nargo_fmt/src/visitor.rs @@ -1,4 +1,4 @@ -mod expr; +pub(crate) mod expr; mod item; mod stmt; @@ -10,16 +10,18 @@ use crate::{ }; pub(crate) struct FmtVisitor<'me> { - config: &'me Config, + ignore_next_node: bool, + pub(crate) config: &'me Config, buffer: String, pub(crate) source: &'me str, - indent: Indent, + pub(crate) indent: Indent, last_position: u32, } impl<'me> FmtVisitor<'me> { pub(crate) fn new(source: &'me str, config: &'me Config) -> Self { Self { + ignore_next_node: false, buffer: String::new(), config, source, @@ -28,12 +30,16 @@ impl<'me> FmtVisitor<'me> { } } + pub(crate) fn budget(&self, used_width: usize) -> usize { + self.config.max_width.saturating_sub(used_width) + } + pub(crate) fn slice(&self, span: impl Into) -> &'me str { let span = span.into(); &self.source[span.start() as usize..span.end() as usize] } - fn span_after(&self, span: impl Into, token: Token) -> Span { + pub(crate) fn span_after(&self, span: impl Into, token: Token) -> Span { let span = span.into(); let slice = self.slice(span); @@ -42,7 +48,7 @@ impl<'me> FmtVisitor<'me> { (span.start() + offset..span.end()).into() } - fn span_before(&self, span: impl Into, token: Token) -> Span { + pub(crate) fn span_before(&self, span: impl Into, token: Token) -> Span { let span = span.into(); let slice = self.slice(span); @@ -51,7 +57,7 @@ impl<'me> FmtVisitor<'me> { (span.start() + offset..span.end()).into() } - fn shape(&self) -> Shape { + pub(crate) fn shape(&self) -> Shape { Shape { width: self.config.max_width.saturating_sub(self.indent.width()), indent: self.indent, @@ -61,6 +67,7 @@ impl<'me> FmtVisitor<'me> { pub(crate) fn fork(&self) -> Self { Self { buffer: String::new(), + ignore_next_node: self.ignore_next_node, config: self.config, source: self.source, last_position: self.last_position, @@ -72,18 +79,29 @@ impl<'me> FmtVisitor<'me> { self.buffer } - fn with_indent(&mut self, f: impl FnOnce(&mut Self) -> T) -> T { - self.indent.block_indent(self.config); - let ret = f(self); - self.indent.block_unindent(self.config); - ret - } - fn at_start(&self) -> bool { self.buffer.is_empty() } fn push_str(&mut self, s: &str) { + let comments = Lexer::new(s).skip_comments(false).flatten().flat_map(|token| { + if let Token::LineComment(content, _) | Token::BlockComment(content, _) = + token.into_token() + { + let content = content.trim(); + content.strip_prefix("noir-fmt:").map(ToOwned::to_owned) + } else { + None + } + }); + + for comment in comments { + match comment.as_str() { + "ignore" => self.ignore_next_node = true, + this => unreachable!("unknown settings {this}"), + } + } + self.buffer.push_str(s); } @@ -96,14 +114,19 @@ impl<'me> FmtVisitor<'me> { panic!("not formatted because a comment would be lost: {rewrite:?}"); } - let rewrite = if changed_comment_content { original.to_string() } else { rewrite }; - self.format_missing_indent(span.start(), true); + + let rewrite = if changed_comment_content || std::mem::take(&mut self.ignore_next_node) { + original.to_string() + } else { + rewrite + }; + self.push_str(&rewrite); - } - fn format_missing(&mut self, end: u32) { - self.format_missing_inner(end, |this, slice, _| this.push_str(slice)); + if rewrite.starts_with('{') && rewrite.ends_with('}') { + self.ignore_next_node = false; + } } #[track_caller] @@ -111,7 +134,7 @@ impl<'me> FmtVisitor<'me> { self.format_missing_inner(end, |this, last_slice, slice| { this.push_str(last_slice.trim_end()); - if last_slice == slice && !this.at_start() { + if (last_slice == slice && !this.at_start()) || this.buffer.ends_with("*/") { this.push_str("\n"); } @@ -144,49 +167,54 @@ impl<'me> FmtVisitor<'me> { self.push_vertical_spaces(slice); process_last_slice(self, "", slice); } else { - 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() { + if result.trim().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); + let last_snippet = &slice[last_end as usize..]; + self.push_str(&result); + process_last_slice(self, last_snippet, &result); } } } - fn format_comment_in_block(&mut self, slice: &str) -> (String, u32) { + pub(crate) 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'); + let comments = Lexer::new(slice).skip_comments(false).skip_whitespaces(false).flatten(); + + let indent = self.indent.to_string(); + for comment in comments { + let span = comment.to_span(); + + match comment.token() { + Token::LineComment(_, _) | Token::BlockComment(_, _) => { + let comment = &slice[span.start() as usize..span.end() as usize]; + if result.ends_with('\n') { + result.push_str(&indent); + } else if !self.at_start() { + result.push(' '); + } + result.push_str(comment); + } + Token::Whitespace(whitespaces) => { + let mut visitor = self.fork(); + if whitespaces.contains('\n') { + visitor.push_vertical_spaces(whitespaces.trim_matches(' ')); + result.push_str(&visitor.finish()); + } + } + _ => {} } } - (result, last_end) + (result, slice.len() as u32) } fn push_vertical_spaces(&mut self, slice: &str) { let newline_upper_bound = 2; let newline_lower_bound = 1; - let mut newline_count = bytecount::count(slice.as_bytes(), b'\n'); + let mut newline_count = utils::count_newlines(slice); let offset = self.buffer.chars().rev().take_while(|c| *c == '\n').count(); if newline_count + offset > newline_upper_bound { @@ -220,37 +248,39 @@ impl<'me> FmtVisitor<'me> { } #[derive(Clone, Copy, Debug, Default)] -struct Indent { +pub(crate) struct Indent { block_indent: usize, } impl Indent { - fn width(&self) -> usize { + pub(crate) fn width(&self) -> usize { self.block_indent } - fn block_indent(&mut self, config: &Config) { + #[track_caller] + pub(crate) fn block_indent(&mut self, config: &Config) { self.block_indent += config.tab_spaces; } - fn block_unindent(&mut self, config: &Config) { + #[track_caller] + pub(crate) fn block_unindent(&mut self, config: &Config) { self.block_indent -= config.tab_spaces; } - fn to_string_with_newline(self) -> String { + pub(crate) fn to_string_with_newline(self) -> String { "\n".to_string() + &self.to_string() } #[allow(clippy::inherent_to_string)] - fn to_string(self) -> String { + pub(crate) fn to_string(self) -> String { " ".repeat(self.block_indent) } } #[derive(Clone, Copy, Debug)] -struct Shape { - width: usize, - indent: Indent, +pub(crate) struct Shape { + pub(crate) width: usize, + pub(crate) indent: Indent, } #[derive(PartialEq, Eq, Debug)] diff --git a/tooling/nargo_fmt/src/visitor/expr.rs b/tooling/nargo_fmt/src/visitor/expr.rs index b7f59526701..586d9583e32 100644 --- a/tooling/nargo_fmt/src/visitor/expr.rs +++ b/tooling/nargo_fmt/src/visitor/expr.rs @@ -1,203 +1,26 @@ use noirc_frontend::{ - hir::resolution::errors::Span, token::Token, ArrayLiteral, BlockExpression, - ConstructorExpression, Expression, ExpressionKind, IfExpression, Literal, Statement, - StatementKind, UnaryOp, + hir::resolution::errors::Span, lexer::Lexer, token::Token, BlockExpression, + ConstructorExpression, Expression, ExpressionKind, IfExpression, Statement, StatementKind, }; -use super::{ExpressionType, FmtVisitor, Indent, Shape}; +use super::{ExpressionType, FmtVisitor, Shape}; use crate::{ - utils::{self, Expr, FindToken, Item}, + rewrite, + utils::{self, first_line_width, Expr, FindToken, Item}, Config, }; impl FmtVisitor<'_> { pub(crate) fn visit_expr(&mut self, expr: Expression, expr_type: ExpressionType) { let span = expr.span; - let rewrite = self.format_expr(expr, expr_type); + let rewrite = rewrite::expr(self, expr, expr_type, self.shape()); self.push_rewrite(rewrite, span); self.last_position = span.end(); } - 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); - visitor.buffer - } - ExpressionKind::Prefix(prefix) => { - let op = match prefix.operator { - UnaryOp::Minus => "-", - UnaryOp::Not => "!", - UnaryOp::MutableReference => "&mut ", - UnaryOp::Dereference { implicitly_added } => { - if implicitly_added { - "" - } else { - "*" - } - } - }; - - format!("{op}{}", self.format_sub_expr(prefix.rhs)) - } - ExpressionKind::Cast(cast) => { - format!("{} as {}", self.format_sub_expr(cast.lhs), cast.r#type) - } - ExpressionKind::Infix(infix) => { - format!( - "{} {} {}", - self.format_sub_expr(infix.lhs), - infix.operator.contents.as_string(), - 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_sub_expr(*call_expr.func); - let args = format_parens(self.fork(), false, call_expr.arguments, args_span); - - format!("{callee}{args}") - } - ExpressionKind::MethodCall(method_call_expr) => { - let args_span = self.span_before( - method_call_expr.method_name.span().end()..span.end(), - Token::LeftParen, - ); - - 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_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_sub_expr(index_expr.collection); - let index = format_brackets(self.fork(), false, vec![index_expr.index], index_span); - - format!("{collection}{index}") - } - ExpressionKind::Tuple(exprs) => { - format_parens(self.fork(), exprs.len() == 1, exprs, span) - } - ExpressionKind::Literal(literal) => match literal { - Literal::Integer(_) | Literal::Bool(_) | Literal::Str(_) | Literal::FmtStr(_) => { - self.slice(span).to_string() - } - Literal::Array(ArrayLiteral::Repeated { repeated_element, length }) => { - let repeated = self.format_sub_expr(*repeated_element); - let length = self.format_sub_expr(*length); - - format!("[{repeated}; {length}]") - } - Literal::Array(ArrayLiteral::Standard(exprs)) => { - format_brackets(self.fork(), false, exprs, span) - } - Literal::Unit => "()".to_string(), - }, - ExpressionKind::Parenthesized(mut sub_expr) => { - let remove_nested_parens = self.config.remove_nested_parens; - - let mut leading; - let mut trailing; - - loop { - let leading_span = span.start() + 1..sub_expr.span.start(); - let trailing_span = sub_expr.span.end()..span.end() - 1; - - leading = self.format_comment(leading_span.into()); - trailing = self.format_comment(trailing_span.into()); - - if let ExpressionKind::Parenthesized(ref sub_sub_expr) = sub_expr.kind { - if remove_nested_parens && leading.is_empty() && trailing.is_empty() { - span = sub_expr.span; - sub_expr = sub_sub_expr.clone(); - continue; - } - } - - break; - } - - if !leading.contains("//") && !trailing.contains("//") { - let sub_expr = self.format_sub_expr(*sub_expr); - format!("({leading}{sub_expr}{trailing})") - } else { - let mut visitor = self.fork(); - - let indent = visitor.indent.to_string_with_newline(); - visitor.indent.block_indent(self.config); - let nested_indent = visitor.indent.to_string_with_newline(); - - let sub_expr = visitor.format_sub_expr(*sub_expr); - - let mut result = String::new(); - result.push('('); - - if !leading.is_empty() { - result.push_str(&nested_indent); - result.push_str(&leading); - } - - result.push_str(&nested_indent); - result.push_str(&sub_expr); - - if !trailing.is_empty() { - result.push_str(&nested_indent); - result.push_str(&trailing); - } - - result.push_str(&indent); - result.push(')'); - - result - } - } - ExpressionKind::Constructor(constructor) => { - 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) - } - 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); + pub(crate) fn format_if(&self, if_expr: IfExpression) -> String { + let condition_str = rewrite::sub_expr(self, self.shape(), if_expr.condition); + let consequence_str = rewrite::sub_expr(self, self.shape(), if_expr.consequence); let mut result = format!("if {condition_str} {consequence_str}"); @@ -207,7 +30,7 @@ impl FmtVisitor<'_> { { self.format_if(*if_expr) } else { - self.format_expr(alternative, ExpressionType::Statement) + rewrite::expr(self, alternative, ExpressionType::Statement, self.shape()) }; result.push_str(" else "); @@ -217,9 +40,10 @@ impl FmtVisitor<'_> { 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)?); + pub(crate) fn format_if_single_line(&self, if_expr: IfExpression) -> Option { + let condition_str = rewrite::sub_expr(self, self.shape(), if_expr.condition); + let consequence_str = + rewrite::sub_expr(self, self.shape(), extract_simple_expr(if_expr.consequence)?); let if_str = if let Some(alternative) = if_expr.alternative { let alternative_str = if let Some(ExpressionKind::If(_)) = @@ -227,7 +51,12 @@ impl FmtVisitor<'_> { { return None; } else { - self.format_expr(extract_simple_expr(alternative)?, ExpressionType::Statement) + rewrite::expr( + self, + extract_simple_expr(alternative)?, + ExpressionType::Statement, + self.shape(), + ) }; format!("if {} {{ {} }} else {{ {} }}", condition_str, consequence_str, alternative_str) @@ -238,7 +67,7 @@ impl FmtVisitor<'_> { (if_str.len() <= self.config.single_line_if_else_max_width).then_some(if_str) } - fn format_struct_lit( + pub(crate) fn format_struct_lit( &self, type_name: &str, fields_span: Span, @@ -252,13 +81,15 @@ impl FmtVisitor<'_> { let nested_indent = visitor.shape(); let exprs: Vec<_> = - utils::Exprs::new(&visitor, fields_span, constructor.fields).collect(); + utils::Exprs::new(&visitor, nested_indent, fields_span, constructor.fields) + .collect(); let exprs = format_exprs( visitor.config, Tactic::HorizontalVertical, false, exprs, nested_indent, + true, ); visitor.indent.block_unindent(visitor.config); @@ -290,17 +121,13 @@ impl FmtVisitor<'_> { self.trim_spaces_after_opening_brace(&block.0); - self.with_indent(|this| { - this.visit_stmts(block.0); - }); + self.indent.block_indent(self.config); - let slice = self.slice(self.last_position..block_span.end() - 1).trim_end(); - self.push_str(slice); + self.visit_stmts(block.0); + let span = (self.last_position..block_span.end() - 1).into(); + self.close_block(span); self.last_position = block_span.end(); - - self.push_str(&self.indent.to_string_with_newline()); - self.push_str("}"); } fn trim_spaces_after_opening_brace(&mut self, block: &[Statement]) { @@ -315,49 +142,85 @@ impl FmtVisitor<'_> { 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() + + if comment_str.is_empty() { + self.push_str("{}"); } else { + self.push_str("{"); self.indent.block_indent(self.config); - 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 = self.indent.to_string(); - format!("{{\n{comment_str}\n{close_indent}}}") + self.close_block(block_span); }; + self.last_position = block_span.end(); - self.push_str(&block_str); + } + + pub(crate) fn close_block(&mut self, span: Span) { + let slice = self.slice(span); + + for spanned in Lexer::new(slice).skip_comments(false).flatten() { + match spanned.token() { + Token::LineComment(_, _) | Token::BlockComment(_, _) => { + let token_span = spanned.to_span(); + + let offset = token_span.start(); + let sub_slice = &slice[token_span.start() as usize..token_span.end() as usize]; + + let span_in_between = span.start()..span.start() + offset; + let slice_in_between = self.slice(span_in_between); + let comment_on_same_line = !slice_in_between.contains('\n'); + + if comment_on_same_line { + self.push_str(" "); + self.push_str(sub_slice); + } else { + self.push_str(&self.indent.to_string_with_newline()); + self.push_str(sub_slice); + } + } + _ => {} + } + } + + self.indent.block_unindent(self.config); + self.push_str(&self.indent.to_string_with_newline()); + self.push_str("}"); } } -fn format_expr_seq( +// TODO: fixme +#[allow(clippy::too_many_arguments)] +pub(crate) fn format_seq( + shape: Shape, prefix: &str, suffix: &str, - mut visitor: FmtVisitor, + visitor: FmtVisitor, trailing_comma: bool, exprs: Vec, span: Span, tactic: Tactic, + mode: NewlineMode, + reduce: bool, ) -> String { - visitor.indent.block_indent(visitor.config); + let mut nested_indent = shape; + let shape = shape; - let nested_indent = visitor.shape(); - let exprs: Vec<_> = utils::Exprs::new(&visitor, span, exprs).collect(); - let exprs = format_exprs(visitor.config, tactic, trailing_comma, exprs, nested_indent); + nested_indent.indent.block_indent(visitor.config); - visitor.indent.block_unindent(visitor.config); + let exprs: Vec<_> = utils::Exprs::new(&visitor, nested_indent, span, exprs).collect(); + let exprs = format_exprs(visitor.config, tactic, trailing_comma, exprs, nested_indent, reduce); - wrap_exprs(prefix, suffix, exprs, nested_indent, visitor.shape()) + wrap_exprs(prefix, suffix, exprs, nested_indent, shape, mode) } -fn format_brackets( +pub(crate) fn format_brackets( visitor: FmtVisitor, trailing_comma: bool, exprs: Vec, span: Span, ) -> String { let array_width = visitor.config.array_width; - format_expr_seq( + format_seq( + visitor.shape(), "[", "]", visitor, @@ -365,16 +228,25 @@ fn format_brackets( exprs, span, Tactic::LimitedHorizontalVertical(array_width), + NewlineMode::Normal, + false, ) } -fn format_parens( +// TODO: fixme +#[allow(clippy::too_many_arguments)] +pub(crate) fn format_parens( + max_width: Option, visitor: FmtVisitor, + shape: Shape, trailing_comma: bool, exprs: Vec, span: Span, + reduce: bool, + mode: NewlineMode, ) -> String { - format_expr_seq("(", ")", visitor, trailing_comma, exprs, span, Tactic::Horizontal) + let tactic = max_width.map(Tactic::LimitedHorizontalVertical).unwrap_or(Tactic::Horizontal); + format_seq(shape, "(", ")", visitor, trailing_comma, exprs, span, tactic, mode, reduce) } fn format_exprs( @@ -383,11 +255,12 @@ fn format_exprs( trailing_comma: bool, exprs: Vec, shape: Shape, + reduce: bool, ) -> String { let mut result = String::new(); let indent_str = shape.indent.to_string(); - let tactic = tactic.definitive(&exprs, config.short_array_element_width_threshold); + let tactic = tactic.definitive(&exprs, config.short_array_element_width_threshold, reduce); let mut exprs = exprs.into_iter().enumerate().peekable(); let mut line_len = 0; let mut prev_expr_trailing_comment = false; @@ -460,16 +333,36 @@ fn format_exprs( result } -fn wrap_exprs( +#[derive(PartialEq, Eq)] +pub(crate) enum NewlineMode { + IfContainsNewLine, + IfContainsNewLineAndWidth, + Normal, +} + +pub(crate) fn wrap_exprs( prefix: &str, suffix: &str, exprs: String, nested_shape: Shape, shape: Shape, + newline_mode: NewlineMode, ) -> String { - let first_line_width = exprs.lines().next().map_or(0, |line| line.chars().count()); + let mut force_one_line = if newline_mode == NewlineMode::IfContainsNewLine { + true + } else { + first_line_width(&exprs) <= shape.width + }; + + if matches!( + newline_mode, + NewlineMode::IfContainsNewLine | NewlineMode::IfContainsNewLineAndWidth + ) && force_one_line + { + force_one_line = !exprs.contains('\n'); + } - if first_line_width <= shape.width { + if force_one_line { let allow_trailing_newline = exprs .lines() .last() @@ -492,8 +385,8 @@ fn wrap_exprs( } } -#[derive(PartialEq, Eq)] -enum Tactic { +#[derive(PartialEq, Eq, Clone, Copy)] +pub(crate) enum Tactic { Horizontal, HorizontalVertical, LimitedHorizontalVertical(usize), @@ -504,7 +397,8 @@ impl Tactic { fn definitive( self, exprs: &[Expr], - short_array_element_width_threshold: usize, + short_width_threshold: usize, + reduce: bool, ) -> DefinitiveTactic { let tactic = || { let has_single_line_comment = exprs.iter().any(|item| { @@ -538,7 +432,12 @@ impl Tactic { } }; - tactic().reduce(exprs, short_array_element_width_threshold) + let definitive_tactic = tactic(); + if reduce { + definitive_tactic.reduce(exprs, short_width_threshold) + } else { + definitive_tactic + } } } diff --git a/tooling/nargo_fmt/src/visitor/item.rs b/tooling/nargo_fmt/src/visitor/item.rs index 96821fb4f9d..eb2086168ba 100644 --- a/tooling/nargo_fmt/src/visitor/item.rs +++ b/tooling/nargo_fmt/src/visitor/item.rs @@ -1,14 +1,140 @@ use noirc_frontend::{ + hir::resolution::errors::Span, parser::{Item, ItemKind}, - token::Token, - NoirFunction, ParsedModule, + token::{Keyword, Token}, + Distinctness, NoirFunction, ParsedModule, Visibility, +}; + +use crate::{ + rewrite, + utils::{last_line_contains_single_line_comment, last_line_used_width, FindToken}, + visitor::expr::{format_seq, NewlineMode}, +}; + +use super::{ + expr::Tactic::{HorizontalVertical, LimitedHorizontalVertical}, + Shape, }; impl super::FmtVisitor<'_> { fn format_fn_before_block(&self, func: NoirFunction, start: u32) -> (String, bool) { - let slice = self.slice(start..func.span().start()); - let force_brace_newline = slice.contains("//"); - (slice.trim_end().to_string(), force_brace_newline) + let name_span = func.name_ident().span(); + let func_span = func.span(); + + let mut result = self.slice(start..name_span.end()).to_owned(); + + let params_open = + self.span_before(name_span.end()..func_span.start(), Token::LeftParen).start(); + + let last_span = if func.parameters().is_empty() { + params_open..func_span.start() + } else { + func.parameters().last().unwrap().span.end()..func_span.start() + }; + + let params_end = self.span_after(last_span, Token::RightParen).start(); + + let params_span = params_open..params_end; + let return_type_span = func.return_type().span; + let return_type = self.format_return_type(return_type_span, &func, func_span, params_end); + let parameters = func.def.parameters; + + if !func.def.generics.is_empty() { + let full_span = name_span.end()..params_open; + let start = name_span.end(); + let end = self.span_after(full_span, Token::Greater).start(); + + let generics = func.def.generics; + let span = (start..end).into(); + let generics = format_seq( + self.shape(), + "<", + ">", + self.fork(), + false, + generics, + span, + HorizontalVertical, + NewlineMode::IfContainsNewLine, + false, + ); + + result.push_str(&generics); + } + + let parameters = if parameters.is_empty() { + self.slice(params_span).into() + } else { + let fn_start = result + .find_token_with(|token| { + matches!(token, Token::Keyword(Keyword::Fn | Keyword::Unconstrained)) + }) + .unwrap() + .start(); + + let slice = self.slice(fn_start..result.len() as u32); + let indent = self.indent; + let used_width = last_line_used_width(slice, indent.width()); + let overhead = if return_type.is_empty() { 2 } else { 3 }; // 2 = `()`, 3 = `() ` + let one_line_budget = self.budget(used_width + return_type.len() + overhead); + let shape = Shape { width: one_line_budget, indent }; + + let tactic = LimitedHorizontalVertical(one_line_budget); + + format_seq( + shape, + "(", + ")", + self.fork(), + false, + parameters, + params_span.into(), + tactic, + NewlineMode::IfContainsNewLine, + false, + ) + }; + + result.push_str(¶meters); + result.push_str(&return_type); + + let maybe_comment = self.slice(params_end..func_span.start()); + + (result.trim_end().to_string(), last_line_contains_single_line_comment(maybe_comment)) + } + + fn format_return_type( + &self, + return_type_span: Option, + func: &NoirFunction, + func_span: Span, + params_end: u32, + ) -> String { + let mut result = String::new(); + + if let Some(span) = return_type_span { + result.push_str(" -> "); + + if let Distinctness::Distinct = func.def.return_distinctness { + result.push_str("distinct "); + } + + if let Visibility::Public = func.def.return_visibility { + result.push_str("pub "); + } + + let typ = rewrite::typ(self, self.shape(), func.return_type()); + result.push_str(&typ); + + let slice = self.slice(span.end()..func_span.start()); + if !slice.trim().is_empty() { + result.push_str(slice); + } + } else { + result.push_str(self.slice(params_end..func_span.start())); + } + + result } pub(crate) fn visit_file(&mut self, module: ParsedModule) { @@ -20,49 +146,49 @@ impl super::FmtVisitor<'_> { for Item { kind, span } in module.items { match kind { ItemKind::Function(func) => { + self.format_missing_indent(span.start(), true); + + if std::mem::take(&mut self.ignore_next_node) { + self.push_str(self.slice(span)); + self.last_position = span.end(); + continue; + } + let (fn_before_block, force_brace_newline) = self.format_fn_before_block(func.clone(), span.start()); - 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); } ItemKind::Submodules(module) => { - let name = module.name; + self.format_missing_indent(span.start(), true); - self.format_missing(span.start()); + if std::mem::take(&mut self.ignore_next_node) { + self.push_str(self.slice(span)); + self.last_position = span.end(); + continue; + } + let name = module.name; 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} ")); + self.push_str(&format!("{keyword} {name} ")); if module.contents.items.is_empty() { self.visit_empty_block((after_brace - 1..span.end()).into()); + continue; } 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.indent.block_indent(self.config); + self.visit_module(module.contents); } + self.close_block((self.last_position..span.end() - 1).into()); self.last_position = span.end(); } ItemKind::Import(_) diff --git a/tooling/nargo_fmt/src/visitor/stmt.rs b/tooling/nargo_fmt/src/visitor/stmt.rs index b6dd67323fa..800a8656ef3 100644 --- a/tooling/nargo_fmt/src/visitor/stmt.rs +++ b/tooling/nargo_fmt/src/visitor/stmt.rs @@ -4,7 +4,9 @@ use noirc_frontend::{ ConstrainKind, ConstrainStatement, ExpressionKind, ForRange, Statement, StatementKind, }; -use super::ExpressionType; +use crate::{rewrite, visitor::expr::wrap_exprs}; + +use super::{expr::NewlineMode, ExpressionType}; impl super::FmtVisitor<'_> { pub(crate) fn visit_stmts(&mut self, stmts: Vec) { @@ -25,36 +27,57 @@ impl super::FmtVisitor<'_> { 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, ExpressionType::SubExpression); + + let expr_str = rewrite::sub_expr(self, self.shape(), let_stmt.expression); self.push_rewrite(format!("{let_str} {expr_str};"), span); } StatementKind::Constrain(ConstrainStatement(expr, message, kind)) => { + let mut nested_shape = self.shape(); + let shape = nested_shape; + + nested_shape.indent.block_indent(self.config); + let message = message.map_or(String::new(), |message| format!(", \"{message}\"")); - let constrain = match kind { + + let (callee, args) = match kind { ConstrainKind::Assert => { - let assertion = self.format_sub_expr(expr); + let assertion = rewrite::sub_expr(self, nested_shape, expr); + let args = format!("{assertion}{message}"); - format!("assert({assertion}{message});") + ("assert", args) } 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); + let lhs = rewrite::sub_expr(self, nested_shape, infix.lhs); + let rhs = rewrite::sub_expr(self, nested_shape, infix.rhs); + + let args = format!("{lhs}, {rhs}{message}"); - format!("assert_eq({lhs}, {rhs}{message});") + ("assert_eq", args) } else { unreachable!() } } ConstrainKind::Constrain => { - let expr = self.format_sub_expr(expr); - format!("constrain {expr};") + let expr = rewrite::sub_expr(self, self.shape(), expr); + let constrain = format!("constrain {expr};"); + self.push_rewrite(constrain, span); + return; } }; + let args = wrap_exprs( + "(", + ")", + args, + nested_shape, + shape, + NewlineMode::IfContainsNewLineAndWidth, + ); + let constrain = format!("{callee}{args};"); + self.push_rewrite(constrain, span); } StatementKind::For(for_stmt) => { @@ -62,12 +85,12 @@ impl super::FmtVisitor<'_> { let range = match for_stmt.range { ForRange::Range(start, end) => format!( "{}..{}", - self.format_sub_expr(start), - self.format_sub_expr(end) + rewrite::sub_expr(self, self.shape(), start), + rewrite::sub_expr(self, self.shape(), end) ), - ForRange::Array(array) => self.format_sub_expr(array), + ForRange::Array(array) => rewrite::sub_expr(self, self.shape(), array), }; - let block = self.format_sub_expr(for_stmt.block); + let block = rewrite::sub_expr(self, self.shape(), for_stmt.block); let result = format!("for {identifier} in {range} {block}"); self.push_rewrite(result, span); diff --git a/tooling/nargo_fmt/tests/expected/array.nr b/tooling/nargo_fmt/tests/expected/array.nr index fdf81d3595c..3341afb31a5 100644 --- a/tooling/nargo_fmt/tests/expected/array.nr +++ b/tooling/nargo_fmt/tests/expected/array.nr @@ -1,42 +1,38 @@ fn big_array() { - [1, - 10, - 100, - 1000, - 10000, - 100000, - 1000000, - 10000000, - 100000000, - 1000000000, - 10000000000, - 100000000000, - 1000000000000, - 10000000000000, - 100000000000000, - 1000000000000000, - 10000000000000000, - 100000000000000000, - 1000000000000000000, + [ + 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000, 100000000000, 1000000000000, 10000000000000, 100000000000000, 1000000000000000, 10000000000000000, 100000000000000000, 1000000000000000000, 10000000000000000000, 100000000000000000000, 1000000000000000000000, 10000000000000000000000, 100000000000000000000000, - 1000000000000000000000000]; + 1000000000000000000000000 + ]; - [1, 10]; + [ + 1, + 10 + ]; - [// hello! - 1, 10]; + [ + // hello! + 1, + 10 + ]; - [// hello! - 1, // asd - 10]; + [ + // hello! + 1, + // asd + 10 + ]; - [// hello! - 1, // asd - 10// asdasd + [ + // hello! + 1, + // asd + 10 + // asdasd ]; [[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]], [[13, 14, 15], [16, 17, 18]]]; diff --git a/tooling/nargo_fmt/tests/expected/call.nr b/tooling/nargo_fmt/tests/expected/call.nr index 8f627ed1223..de78d7c4edb 100644 --- a/tooling/nargo_fmt/tests/expected/call.nr +++ b/tooling/nargo_fmt/tests/expected/call.nr @@ -1,23 +1,33 @@ fn foo() { my_function(10, some_value, another_func(20, 30)); - outer_function(some_function(), // Original inner function call + outer_function( + some_function(), // Original inner function call another_function() // Original inner function call ); - outer_function(some_function(), // Original inner function call + outer_function( + some_function(), // Original inner function call another_function() // Original inner function call ); - my_function(// Comment + my_function( + // Comment some_value, /* Multiline Comment */ - another_func(20, 30)); + another_func(20, 30) + ); - my_function(some_function(10, "arg1", another_function()), another_func(20, some_function(), 30)); + my_function( + some_function(10, "arg1", another_function()), + another_func(20, some_function(), 30) + ); - outer_function(some_function(), another_function(some_function(), some_value)); + outer_function( + some_function(), + another_function(some_function(), some_value) + ); assert_eq(x, y); @@ -28,4 +38,13 @@ fn foo() { assert(x, "message"); assert(x == y); + + assert( + p4_affine.eq( + Gaffine::new( + 6890855772600357754907169075114257697580319025794532037257385534741338397365, + 4338620300185947561074059802482547481416142213883829469920100239455078257889 + ) + ) + ); } diff --git a/tooling/nargo_fmt/tests/expected/comment.nr b/tooling/nargo_fmt/tests/expected/comment.nr index b6ac52a236a..fae425acfd0 100644 --- a/tooling/nargo_fmt/tests/expected/comment.nr +++ b/tooling/nargo_fmt/tests/expected/comment.nr @@ -3,8 +3,8 @@ fn comment1() { } // random comment -fn comment2() { - // Test + +fn comment2() { // Test } fn comment3() // some comment diff --git a/tooling/nargo_fmt/tests/expected/contract.nr b/tooling/nargo_fmt/tests/expected/contract.nr index 54fe6ff1419..d288b1af7eb 100644 --- a/tooling/nargo_fmt/tests/expected/contract.nr +++ b/tooling/nargo_fmt/tests/expected/contract.nr @@ -57,7 +57,11 @@ contract Benchmarking { 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]); + 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. @@ -66,8 +70,16 @@ contract Benchmarking { 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] { + 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) } } + +// Uses the token bridge contract, which tells which input token we need to talk to and handles the exit funds to L1 +contract Uniswap {} diff --git a/tooling/nargo_fmt/tests/expected/expr.nr b/tooling/nargo_fmt/tests/expected/expr.nr index c0047e90175..03a26835ee3 100644 --- a/tooling/nargo_fmt/tests/expected/expr.nr +++ b/tooling/nargo_fmt/tests/expected/expr.nr @@ -2,8 +2,7 @@ fn qux() { {} - { - /* a block with a comment */ + { /* a block with a comment */ } {} { @@ -72,6 +71,22 @@ fn test() { 34 } +fn line() { + 42; // 42 +} + +fn line() { + 42; // 42 + 42; + // hello +} + +fn line() { + 42; // 42 + // 42 + // hello +} + fn parenthesized() { value + (x as Field) } @@ -86,9 +101,9 @@ fn parenthesized() { fn parenthesized() { value + ( - // line - x as Field - ) + // line + x as Field + ) } fn constructor() { diff --git a/tooling/nargo_fmt/tests/expected/fn.nr b/tooling/nargo_fmt/tests/expected/fn.nr index 89484dc83c2..0088dba6a8f 100644 --- a/tooling/nargo_fmt/tests/expected/fn.nr +++ b/tooling/nargo_fmt/tests/expected/fn.nr @@ -1 +1,63 @@ fn main(x: pub u8, y: u8) {} + +fn main(x: pub u8, y: u8) -> pub Field {} + +fn main(x: A, y: B) -> pub Field where A: Eq, B: Eq {} + +fn main() +// hello +{} + +fn main( + // hello +) {} + +fn main( + // hello + unit: () +) {} + +fn main() where T: Eq {} + +fn main( + tape: [Field; TAPE_LEN], + initial_registers: [Field; REGISTER_COUNT], + initial_memory: [Field; MEM_COUNT], + initial_program_counter: Field, + initial_call_stack: [Field; MAX_CALL_STACK], + initial_call_stack_pointer: u64 +) -> pub ExecutionResult {} + +fn apply_binary_field_op( + lhs: RegisterIndex, + rhs: RegisterIndex, + result: RegisterIndex, + op: u8, + registers: &mut Registers +) -> bool {} + +fn main() -> distinct pub [Field; 2] {} + +fn ret_normal_lambda1() -> ((fn() -> Field)) {} + +fn ret_normal_lambda1() -> fn() -> Field {} + +fn ret_closure1() -> fn[(Field,)]() -> Field {} + +fn ret_closure2() -> fn[(Field, Field)]() -> Field {} + +fn ret_closure3() -> fn[(u32, u64)]() -> u64 {} + +fn make_counter() -> fn[(&mut Field,)]() -> Field {} + +fn get_some(generator: fn[Env]() -> Field) -> [Field; 5] {} + +fn main( + message: [u8; 10], + message_field: Field, + pub_key_x: Field, + pub_key_y: Field, + signature: [u8; 64] +) {} + +pub fn from_baz(x: [Field; crate::foo::MAGIC_NUMBER]) {} diff --git a/tooling/nargo_fmt/tests/expected/global.nr b/tooling/nargo_fmt/tests/expected/global.nr new file mode 100644 index 00000000000..e73cf96ccbe --- /dev/null +++ b/tooling/nargo_fmt/tests/expected/global.nr @@ -0,0 +1,9 @@ +//! Super module :] + +// super global variable +global answer = 42; + +// Super module :] + +// super global variable +global answer = 42; diff --git a/tooling/nargo_fmt/tests/expected/ignore.nr b/tooling/nargo_fmt/tests/expected/ignore.nr new file mode 100644 index 00000000000..1c84e178f34 --- /dev/null +++ b/tooling/nargo_fmt/tests/expected/ignore.nr @@ -0,0 +1,26 @@ +fn main() { + // noir-fmt:ignore + assert( x != y ); + assert(x != y); + { + // noir-fmt:ignore + }; + assert(x != y); +} +// noir-fmt:ignore +fn main() { +1; +2; +3; +} +// noir-fmt:ignore +mod items { +fn hello() {} +} + +fn mk_array() { + // noir-fmt:ignore + let array = [1, + ]; + let array = [1]; +} diff --git a/tooling/nargo_fmt/tests/expected/infix.nr b/tooling/nargo_fmt/tests/expected/infix.nr index 0688781ba48..228dfdf68c4 100644 --- a/tooling/nargo_fmt/tests/expected/infix.nr +++ b/tooling/nargo_fmt/tests/expected/infix.nr @@ -7,3 +7,14 @@ fn foo() { 40/*test*/ + 2 == 42; 40 + 2/*test*/ == 42; } + +fn big() { + assert( + bjj_affine.contains(bjj_affine.gen) + & bjj_affine.contains(p1_affine) + & bjj_affine.contains(p2_affine) + & bjj_affine.contains(p3_affine) + & bjj_affine.contains(p4_affine) + & bjj_affine.contains(p5_affine) + ); +} diff --git a/tooling/nargo_fmt/tests/expected/let.nr b/tooling/nargo_fmt/tests/expected/let.nr index 1c7afc8df5f..c57801155a0 100644 --- a/tooling/nargo_fmt/tests/expected/let.nr +++ b/tooling/nargo_fmt/tests/expected/let.nr @@ -1,20 +1,21 @@ //@error_on_lost_comment=false fn let_() { - let fn_call = my_function(some_function(10, "arg1", another_function()), another_func(20, some_function(), 30)); + let fn_call = my_function( + some_function(10, "arg1", another_function()), + another_func(20, some_function(), 30) + ); let array = [[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]], [[13, 14, 15], [16, 17, 18]]]; - let padded_sha256_hash: [u8; 259] = [// Padded hash - 209, 50, 135, 178, 4, 155, 190, 229, 228, 111, 61, 174, 8, 49, 48, 116, 90, 226, 77, 7, 111, - 27, 19, 113, 154, 48, 138, 136, 138, 15, 230, 132, 32, 4, 0, 5, 1, 2, 4, 3, 101, 1, 72, 134, - 96, 9, 6, 13, 48, 49, 48, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 1, 0, + let padded_sha256_hash: [u8; 259] = [ + // Padded hash + 209, 50, 135, 178, 4, 155, 190, 229, 228, 111, 61, 174, 8, 49, 48, 116, 90, 226, 77, 7, 111, 27, 19, 113, 154, 48, 138, 136, 138, 15, 230, 132, 32, 4, 0, 5, 1, 2, 4, 3, 101, 1, 72, 134, 96, 9, 6, 13, 48, 49, + 48, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 1, 0, // Rest is padded with 0s until max bytes - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0]; + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + ]; let a = BigUint56 { limbs: [ diff --git a/tooling/nargo_fmt/tests/expected/parens.nr b/tooling/nargo_fmt/tests/expected/parens.nr index aeba660f898..e6c4f91879c 100644 --- a/tooling/nargo_fmt/tests/expected/parens.nr +++ b/tooling/nargo_fmt/tests/expected/parens.nr @@ -1,4 +1,4 @@ -fn main(x : u64, y : pub u64) { +fn main(x: u64, y: pub u64) { ( // 1 diff --git a/tooling/nargo_fmt/tests/expected/single_fn.nr b/tooling/nargo_fmt/tests/expected/single_fn.nr new file mode 100644 index 00000000000..b02291fc50d --- /dev/null +++ b/tooling/nargo_fmt/tests/expected/single_fn.nr @@ -0,0 +1,3 @@ +fn main() { + // hello +} diff --git a/tooling/nargo_fmt/tests/expected/single_mod.nr b/tooling/nargo_fmt/tests/expected/single_mod.nr new file mode 100644 index 00000000000..3c04a12d34b --- /dev/null +++ b/tooling/nargo_fmt/tests/expected/single_mod.nr @@ -0,0 +1 @@ +mod hello {} diff --git a/tooling/nargo_fmt/tests/expected/struct.nr b/tooling/nargo_fmt/tests/expected/struct.nr index 6734dec68a6..cf1795892d2 100644 --- a/tooling/nargo_fmt/tests/expected/struct.nr +++ b/tooling/nargo_fmt/tests/expected/struct.nr @@ -33,7 +33,7 @@ struct MyStruct { my_int: u32, my_nest: Nested, } -fn test_struct_in_tuple(a_bool : bool,x:Field, y:Field) -> (MyStruct, bool) { +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 } }; (my_struct, a_bool) } diff --git a/tooling/nargo_fmt/tests/input/call.nr b/tooling/nargo_fmt/tests/input/call.nr index 24e61c806cc..2e9a612e6d8 100644 --- a/tooling/nargo_fmt/tests/input/call.nr +++ b/tooling/nargo_fmt/tests/input/call.nr @@ -42,4 +42,6 @@ fn foo() { assert( x, "message" ); assert( x == y ); + + assert(p4_affine.eq(Gaffine::new(6890855772600357754907169075114257697580319025794532037257385534741338397365, 4338620300185947561074059802482547481416142213883829469920100239455078257889))); } diff --git a/tooling/nargo_fmt/tests/input/contract.nr b/tooling/nargo_fmt/tests/input/contract.nr index 54fe6ff1419..6bc5c552110 100644 --- a/tooling/nargo_fmt/tests/input/contract.nr +++ b/tooling/nargo_fmt/tests/input/contract.nr @@ -71,3 +71,6 @@ contract Benchmarking { note_utils::compute_note_hash_and_nullifier(ValueNoteMethods, note_header, preimage) } } + +// Uses the token bridge contract, which tells which input token we need to talk to and handles the exit funds to L1 +contract Uniswap {} diff --git a/tooling/nargo_fmt/tests/input/expr.nr b/tooling/nargo_fmt/tests/input/expr.nr index 28ba9cb0585..b4edcbbed5f 100644 --- a/tooling/nargo_fmt/tests/input/expr.nr +++ b/tooling/nargo_fmt/tests/input/expr.nr @@ -81,6 +81,24 @@ fn test() { 34 } +fn line() { + 42; // 42 +} + + +fn line() { + 42;// 42 + 42; +// hello +} + + +fn line() { + 42;// 42 + // 42 +// hello +} + fn parenthesized() { value + ( x as Field ) } diff --git a/tooling/nargo_fmt/tests/input/fn.nr b/tooling/nargo_fmt/tests/input/fn.nr index 89484dc83c2..26ff5933802 100644 --- a/tooling/nargo_fmt/tests/input/fn.nr +++ b/tooling/nargo_fmt/tests/input/fn.nr @@ -1 +1,46 @@ fn main(x: pub u8, y: u8) {} + +fn main(x: pub u8, y: u8) -> pub Field {} + +fn main(x: A, y: B) -> pub Field where A: Eq, B: Eq {} + +fn main() +// hello +{} + +fn main( + // hello +) {} + +fn main( + // hello + unit: () +) {} + +fn main() where T: Eq {} + +fn main(tape: [Field; TAPE_LEN], initial_registers: [Field; REGISTER_COUNT], initial_memory: [Field; MEM_COUNT], initial_program_counter: Field, initial_call_stack: [Field; MAX_CALL_STACK], initial_call_stack_pointer: u64) -> pub ExecutionResult {} + +fn apply_binary_field_op(lhs: RegisterIndex, rhs: RegisterIndex, result: RegisterIndex, op: u8, registers: &mut Registers) -> bool {} + +fn main() -> distinct pub [Field;2] {} + +fn ret_normal_lambda1() -> ((fn() -> Field)) {} + +fn ret_normal_lambda1() -> fn() -> Field {} + +fn ret_closure1() -> fn[(Field,)]() -> Field {} + +fn ret_closure2() -> fn[(Field,Field)]() -> Field {} + +fn ret_closure3() -> fn[(u32,u64)]() -> u64 {} + +fn make_counter() -> fn[(&mut Field,)]() -> Field {} + +fn get_some(generator: fn[Env]() -> Field) -> [Field;5] {} + +fn main( + message: [u8; 10], message_field: Field, pub_key_x: Field, pub_key_y: Field, signature: [u8; 64] +) {} + +pub fn from_baz(x: [Field; crate::foo::MAGIC_NUMBER]) {} diff --git a/tooling/nargo_fmt/tests/input/global.nr b/tooling/nargo_fmt/tests/input/global.nr new file mode 100644 index 00000000000..bf023c61805 --- /dev/null +++ b/tooling/nargo_fmt/tests/input/global.nr @@ -0,0 +1,17 @@ +//! Super module :] + +// super global variable +global answer = 42; + +// Super module :] + + + + + + + + + +// super global variable +global answer = 42; diff --git a/tooling/nargo_fmt/tests/input/ignore.nr b/tooling/nargo_fmt/tests/input/ignore.nr new file mode 100644 index 00000000000..e095d2e9f1f --- /dev/null +++ b/tooling/nargo_fmt/tests/input/ignore.nr @@ -0,0 +1,27 @@ +fn main() { + // noir-fmt:ignore + assert( x != y ); + assert( x != y ); + { + // noir-fmt:ignore + }; + assert( x != y ); +} +// noir-fmt:ignore +fn main() { +1; +2; +3; +} +// noir-fmt:ignore +mod items { +fn hello() {} +} + +fn mk_array() { + // noir-fmt:ignore + let array = [1, + ]; + let array = [1, + ]; +} diff --git a/tooling/nargo_fmt/tests/input/infix.nr b/tooling/nargo_fmt/tests/input/infix.nr index b16ccd02982..059e58c6b64 100644 --- a/tooling/nargo_fmt/tests/input/infix.nr +++ b/tooling/nargo_fmt/tests/input/infix.nr @@ -7,3 +7,7 @@ fn foo() { 40/*test*/ + 2 == 42; 40 + 2/*test*/ == 42; } + +fn big() { + assert(bjj_affine.contains(bjj_affine.gen) & bjj_affine.contains(p1_affine) & bjj_affine.contains(p2_affine) & bjj_affine.contains(p3_affine) & bjj_affine.contains(p4_affine) & bjj_affine.contains(p5_affine)); +} diff --git a/tooling/nargo_fmt/tests/input/single_fn.nr b/tooling/nargo_fmt/tests/input/single_fn.nr new file mode 100644 index 00000000000..c7ee3bbd3aa --- /dev/null +++ b/tooling/nargo_fmt/tests/input/single_fn.nr @@ -0,0 +1,3 @@ +fn main() { + // hello +} \ No newline at end of file diff --git a/tooling/nargo_fmt/tests/input/single_mod.nr b/tooling/nargo_fmt/tests/input/single_mod.nr new file mode 100644 index 00000000000..f02dbfb5464 --- /dev/null +++ b/tooling/nargo_fmt/tests/input/single_mod.nr @@ -0,0 +1 @@ +mod hello {} \ No newline at end of file diff --git a/tooling/nargo_toml/src/errors.rs b/tooling/nargo_toml/src/errors.rs index fdbdc317bf9..da976e1b185 100644 --- a/tooling/nargo_toml/src/errors.rs +++ b/tooling/nargo_toml/src/errors.rs @@ -71,9 +71,10 @@ pub enum ManifestError { SemverError(SemverError), } +#[allow(clippy::enum_variant_names)] #[derive(Error, Debug, PartialEq, Eq, Clone)] pub enum SemverError { - #[error("Incompatible compiler version in package {package_name}. Required compiler version is {required_compiler_version} but the compiler version is {compiler_version_found}.\n Update the compiler_version field in Nargo.toml to >={compiler_version_found} or compile this project with version {compiler_version_found}")] + #[error("Incompatible compiler version in package {package_name}. Required compiler version is {required_compiler_version} but the compiler version is {compiler_version_found}.\n Update the compiler_version field in Nargo.toml to >={required_compiler_version} or compile this project with version {required_compiler_version}")] IncompatibleVersion { package_name: CrateName, required_compiler_version: String, @@ -81,4 +82,6 @@ pub enum SemverError { }, #[error("Could not parse the required compiler version for package {package_name} in Nargo.toml. Error: {error}")] CouldNotParseRequiredVersion { package_name: String, error: String }, + #[error("Could not parse the package version for package {package_name} in Nargo.toml. Error: {error}")] + CouldNotParsePackageVersion { package_name: String, error: String }, } diff --git a/tooling/nargo_toml/src/lib.rs b/tooling/nargo_toml/src/lib.rs index 223ed2da081..141cb3411b3 100644 --- a/tooling/nargo_toml/src/lib.rs +++ b/tooling/nargo_toml/src/lib.rs @@ -8,6 +8,7 @@ use std::{ path::{Component, Path, PathBuf}, }; +use errors::SemverError; use fm::{NormalizePath, FILE_EXTENSION}; use nargo::{ package::{Dependency, Package, PackageType}, @@ -99,7 +100,7 @@ struct PackageConfig { impl PackageConfig { fn resolve_to_package(&self, root_dir: &Path) -> Result { - let name = if let Some(name) = &self.package.name { + let name: CrateName = if let Some(name) = &self.package.name { name.parse().map_err(|_| ManifestError::InvalidPackageName { toml: root_dir.join("Nargo.toml"), name: name.into(), @@ -163,7 +164,18 @@ impl PackageConfig { } }; + // If there is a package version, ensure that it is semver compatible + if let Some(version) = &self.package.version { + semver::parse_semver_compatible_version(version).map_err(|err| { + ManifestError::SemverError(SemverError::CouldNotParsePackageVersion { + package_name: name.to_string(), + error: err.to_string(), + }) + })?; + } + Ok(Package { + version: self.package.version.clone(), compiler_required_version: self.package.compiler_version.clone(), root_dir: root_dir.to_path_buf(), entry_path, @@ -225,6 +237,7 @@ struct WorkspaceConfig { #[derive(Default, Debug, Deserialize, Clone)] struct PackageMetadata { name: Option, + version: Option, #[serde(alias = "type")] package_type: Option, entry: Option, diff --git a/tooling/nargo_toml/src/semver.rs b/tooling/nargo_toml/src/semver.rs index de722f06bd8..6acc68afa47 100644 --- a/tooling/nargo_toml/src/semver.rs +++ b/tooling/nargo_toml/src/semver.rs @@ -3,14 +3,19 @@ use nargo::{ package::{Dependency, Package}, workspace::Workspace, }; -use semver::{Version, VersionReq}; +use semver::{Error, Version, VersionReq}; + +// Parse a semver compatible version string +pub(crate) fn parse_semver_compatible_version(version: &str) -> Result { + Version::parse(version) +} // Check that all of the packages in the workspace are compatible with the current compiler version pub(crate) fn semver_check_workspace( workspace: Workspace, current_compiler_version: String, ) -> Result<(), ManifestError> { - let version = Version::parse(¤t_compiler_version) + let version = parse_semver_compatible_version(¤t_compiler_version) .expect("The compiler version is not a valid semver version"); for package in &workspace.members { semver_check_package(package, &version).map_err(ManifestError::SemverError)?; @@ -83,6 +88,7 @@ mod tests { entry_path: PathBuf::new(), name: CrateName::from_str("test").unwrap(), dependencies: BTreeMap::new(), + version: Some("1.0".to_string()), }; if let Err(err) = semver_check_package(&package, &compiler_version) { panic!("semver check should have passed. compiler version is 0.1.0 and required version from the package is 0.1.0\n error: {err:?}") @@ -113,6 +119,7 @@ mod tests { entry_path: PathBuf::new(), name: CrateName::from_str("test").unwrap(), dependencies: BTreeMap::new(), + version: Some("1.0".to_string()), }; let valid_dependency = Package { @@ -122,6 +129,7 @@ mod tests { entry_path: PathBuf::new(), name: CrateName::from_str("good_dependency").unwrap(), dependencies: BTreeMap::new(), + version: Some("1.0".to_string()), }; let invalid_dependency = Package { compiler_required_version: Some("0.2.0".to_string()), @@ -130,6 +138,7 @@ mod tests { entry_path: PathBuf::new(), name: CrateName::from_str("bad_dependency").unwrap(), dependencies: BTreeMap::new(), + version: Some("1.0".to_string()), }; package.dependencies.insert( @@ -169,6 +178,7 @@ mod tests { entry_path: PathBuf::new(), name: CrateName::from_str("test").unwrap(), dependencies: BTreeMap::new(), + version: Some("1.0".to_string()), }; if let Err(err) = semver_check_package(&package, &compiler_version) { @@ -187,6 +197,7 @@ mod tests { entry_path: PathBuf::new(), name: CrateName::from_str("test").unwrap(), dependencies: BTreeMap::new(), + version: Some("1.0".to_string()), }; if let Err(err) = semver_check_package(&package, &compiler_version) { diff --git a/tooling/noir_codegen/.gitignore b/tooling/noir_codegen/.gitignore index 721d05448d6..15ea344d453 100644 --- a/tooling/noir_codegen/.gitignore +++ b/tooling/noir_codegen/.gitignore @@ -2,3 +2,4 @@ crs lib !test/*/target +test/codegen diff --git a/tooling/noir_codegen/package.json b/tooling/noir_codegen/package.json index f041679fef5..9879991f6e7 100644 --- a/tooling/noir_codegen/package.json +++ b/tooling/noir_codegen/package.json @@ -3,7 +3,7 @@ "collaborators": [ "The Noir Team " ], - "version": "0.19.0", + "version": "0.19.4", "packageManager": "yarn@3.5.1", "license": "(MIT OR Apache-2.0)", "type": "module", @@ -26,8 +26,10 @@ "scripts": { "dev": "tsc-multi --watch", "build": "tsc", - "test": "ts-node --esm src/main.ts ./test/assert_lt/target/** --out-dir ./test/codegen && yarn test:node && rm -rf ./test/codegen", + "test": "yarn test:codegen && yarn test:node && yarn test:clean", + "test:codegen": "ts-node --esm src/main.ts ./test/assert_lt/target/** --out-dir ./test/codegen", "test:node": "mocha --timeout 25000 --exit --config ./.mocharc.json", + "test:clean": "rm -rf ./test/codegen", "prettier": "prettier 'src/**/*.ts'", "prettier:fix": "prettier --write 'src/**/*.ts' 'test/**/*.ts'", "lint": "NODE_NO_WARNINGS=1 eslint . --ext .ts --ignore-path ./.eslintignore --max-warnings 0", diff --git a/tooling/noir_codegen/src/index.ts b/tooling/noir_codegen/src/index.ts index 1f8d2d183d4..8d45b76bd7d 100644 --- a/tooling/noir_codegen/src/index.ts +++ b/tooling/noir_codegen/src/index.ts @@ -1,24 +1,56 @@ import { CompiledCircuit } from '@noir-lang/types'; +import { PrimitiveTypesUsed, generateTsInterface } from './noir_types.js'; -const codegenImports = `import { InputMap, InputValue } from "@noir-lang/noirc_abi" -import { Noir } from "@noir-lang/noir_js"`; +// TODO: reenable this. See `abiTypeToTs` for reasoning. +// export type FixedLengthArray = L extends 0 ? never[]: T[] & { length: L }; + +const codegenPrelude = `/* Autogenerated file, do not edit! */ + +/* eslint-disable */ + +import { Noir, InputMap, CompiledCircuit } from "@noir-lang/noir_js" +`; const codegenFunction = ( name: string, compiled_program: CompiledCircuit, -) => `export async function ${name}(args: InputMap): Promise { - const program = new Noir(${JSON.stringify(compiled_program)}); + function_signature: { inputs: [string, string][]; returnValue: string | null }, +) => { + const args = function_signature.inputs.map(([name]) => `${name}`).join(', '); + const args_with_types = function_signature.inputs.map(([name, type]) => `${name}: ${type}`).join(', '); + + return ` +export const ${name}_circuit: CompiledCircuit = ${JSON.stringify(compiled_program)}; + +export async function ${name}(${args_with_types}): Promise<${function_signature.returnValue}> { + const program = new Noir(${name}_circuit); + const args: InputMap = { ${args} }; const { returnValue } = await program.execute(args); - return returnValue; + return returnValue as ${function_signature.returnValue}; }`; +}; export const codegen = (programs: [string, CompiledCircuit][]): string => { - const results = [codegenImports]; + let results = [codegenPrelude]; + const primitiveTypeMap = new Map(); + + const functions: string[] = []; for (const [name, program] of programs) { - results.push(codegenFunction(name, stripUnwantedFields(program))); + const [types_string, function_sig] = generateTsInterface(program.abi, primitiveTypeMap); + functions.push(types_string); + functions.push('\n'); + functions.push(codegenFunction(name, stripUnwantedFields(program), function_sig)); } - return results.join('\n\n'); + // Add the primitive Noir types that do not have a 1-1 mapping to TypeScript. + const primitiveTypeAliases: string[] = []; + for (const value of primitiveTypeMap.values()) { + primitiveTypeAliases.push(`export type ${value.aliasName} = ${value.tsType};`); + } + + results = results.concat(...primitiveTypeAliases, ...functions); + + return results.filter((val) => val !== '').join('\n'); }; // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/tooling/noir_codegen/src/noir_types.ts b/tooling/noir_codegen/src/noir_types.ts new file mode 100644 index 00000000000..d1a22a3e2da --- /dev/null +++ b/tooling/noir_codegen/src/noir_types.ts @@ -0,0 +1,189 @@ +import { AbiType, Abi } from '@noir-lang/noirc_abi'; + +/** + * Keep track off all of the Noir primitive types that were used. + * Most of these will not have a 1-1 definition in TypeScript, + * so we will need to generate type aliases for them. + * + * We want to generate type aliases + * for specific types that are used in the ABI. + * + * For example: + * - If `Field` is used we want to alias that + * with `number`. + * - If `u32` is used we want to alias that with `number` too. + */ +export type PrimitiveTypesUsed = { + /** + * The name of the type alias that we will generate. + */ + aliasName: string; + /** + * The TypeScript type that we will alias to. + */ + tsType: string; +}; + +/** + * Typescript does not allow us to check for equality of non-primitive types + * easily, so we create a addIfUnique function that will only add an item + * to the map if it is not already there by using JSON.stringify. + * @param item - The item to add to the map. + */ +function addIfUnique(primitiveTypeMap: Map, item: PrimitiveTypesUsed) { + const key = JSON.stringify(item); + if (!primitiveTypeMap.has(key)) { + primitiveTypeMap.set(key, item); + } +} + +/** + * Converts an ABI type to a TypeScript type. + * @param type - The ABI type to convert. + * @returns The typescript code to define the type. + */ +function abiTypeToTs(type: AbiType, primitiveTypeMap: Map): string { + switch (type.kind) { + case 'field': + addIfUnique(primitiveTypeMap, { aliasName: 'Field', tsType: 'string' }); + return 'Field'; + case 'integer': { + const typeName = type.sign === 'signed' ? `i${type.width}` : `u${type.width}`; + // Javascript cannot safely represent the full range of Noir's integer types as numbers. + // `Number.MAX_SAFE_INTEGER == 2**53 - 1` so we disallow passing numbers to types which may exceed this. + // 52 has been chosen as the cutoff rather than 53 for safety. + const tsType = type.width <= 52 ? `string | number` : `string`; + + addIfUnique(primitiveTypeMap, { aliasName: typeName, tsType }); + return typeName; + } + case 'boolean': + return `boolean`; + case 'array': + // We can't force the usage of fixed length arrays as this currently throws errors in TS. + // The array would need to be `as const` to support this whereas that's unlikely to happen in user code. + // return `FixedLengthArray<${abiTypeToTs(type.type, primitiveTypeMap)}, ${type.length}>`; + return `${abiTypeToTs(type.type, primitiveTypeMap)}[]`; + case 'string': + // We could enforce that literals are the correct length but not generally. + // This would run into similar problems to above. + return `string`; + case 'struct': + return getLastComponentOfPath(type.path); + case 'tuple': { + const field_types = type.fields.map((field) => abiTypeToTs(field, primitiveTypeMap)); + return `[${field_types.join(', ')}]`; + } + default: + throw new Error(`Unknown ABI type ${JSON.stringify(type)}`); + } +} + +/** + * Returns the last component of a path, e.g. "foo::bar::baz" -\> "baz" + * Note: that if we have a path such as "Baz", we will return "Baz". + * + * Since these paths corresponds to structs, we can assume that we + * cannot have "foo::bar::". + * + * We also make the assumption that since these paths are coming from + * Noir, then we will not have two paths that look like this: + * - foo::bar::Baz + * - cat::dog::Baz + * ie the last component of the path (struct name) is enough to uniquely identify + * the whole path. + * + * TODO: We should double check this assumption when we use type aliases, + * I expect that `foo::bar::Baz as Dog` would effectively give `foo::bar::Dog` + * @param str - The path to get the last component of. + * @returns The last component of the path. + */ +function getLastComponentOfPath(str: string): string { + const parts = str.split('::'); + const lastPart = parts[parts.length - 1]; + return lastPart; +} + +/** + * Generates TypeScript interfaces for the structs used in the ABI. + * @param type - The ABI type to generate the interface for. + * @param output - The set of structs that we have already generated bindings for. + * @returns The TypeScript code to define the struct. + */ +function generateStructInterfaces( + type: AbiType, + output: Set, + primitiveTypeMap: Map, +): string { + let result = ''; + + // Edge case to handle the array of structs case. + if (type.kind === 'array' && type.type.kind === 'struct' && !output.has(getLastComponentOfPath(type.type.path))) { + result += generateStructInterfaces(type.type, output, primitiveTypeMap); + } + if (type.kind !== 'struct') return result; + + // List of structs encountered while viewing this type that we need to generate + // bindings for. + const typesEncountered = new Set(); + + // Codegen the struct and then its fields, so that the structs fields + // are defined before the struct itself. + let codeGeneratedStruct = ''; + let codeGeneratedStructFields = ''; + + const structName = getLastComponentOfPath(type.path); + if (!output.has(structName)) { + codeGeneratedStruct += `export type ${structName} = {\n`; + for (const field of type.fields) { + codeGeneratedStruct += ` ${field.name}: ${abiTypeToTs(field.type, primitiveTypeMap)};\n`; + typesEncountered.add(field.type); + } + codeGeneratedStruct += `};`; + output.add(structName); + + // Generate code for the encountered structs in the field above + for (const type of typesEncountered) { + codeGeneratedStructFields += generateStructInterfaces(type, output, primitiveTypeMap); + } + } + + return codeGeneratedStructFields + '\n' + codeGeneratedStruct; +} + +/** + * Generates a TypeScript interface for the ABI. + * @param abiObj - The ABI to generate the interface for. + * @returns The TypeScript code to define the interface. + */ +export function generateTsInterface( + abiObj: Abi, + primitiveTypeMap: Map, +): [string, { inputs: [string, string][]; returnValue: string | null }] { + let result = ``; + const outputStructs = new Set(); + + // Define structs for composite types + for (const param of abiObj.parameters) { + result += generateStructInterfaces(param.type, outputStructs, primitiveTypeMap); + } + + // Generating Return type, if it exists + if (abiObj.return_type != null) { + result += generateStructInterfaces(abiObj.return_type, outputStructs, primitiveTypeMap); + } + + return [result, getTsFunctionSignature(abiObj, primitiveTypeMap)]; +} + +function getTsFunctionSignature( + abi: Abi, + primitiveTypeMap: Map, +): { inputs: [string, string][]; returnValue: string | null } { + const inputs: [string, string][] = abi.parameters.map((param) => [ + param.name, + abiTypeToTs(param.type, primitiveTypeMap), + ]); + const returnValue = abi.return_type ? abiTypeToTs(abi.return_type, primitiveTypeMap) : null; + return { inputs, returnValue }; +} diff --git a/tooling/noir_codegen/test/assert_lt/src/main.nr b/tooling/noir_codegen/test/assert_lt/src/main.nr index 67b8c84bdcd..3b3e04ddece 100644 --- a/tooling/noir_codegen/test/assert_lt/src/main.nr +++ b/tooling/noir_codegen/test/assert_lt/src/main.nr @@ -1,4 +1,19 @@ -fn main(x : u64, y : pub u64) -> pub u64 { +struct MyStruct { + foo: bool, + bar: [str<5>; 3], +} + +fn main( + x: u64, + y: pub u64, + array: [u8; 5], + my_struct: MyStruct, + string: str<5> +) -> pub (u64, u64, MyStruct) { + assert(array.len() == 5); + assert(my_struct.foo); + assert(string == "12345"); + assert(x < y); - x + y + (x + y, 3, my_struct) } diff --git a/tooling/noir_codegen/test/assert_lt/target/assert_lt.json b/tooling/noir_codegen/test/assert_lt/target/assert_lt.json index 3b2b1b2c5a1..6d928a26d43 100644 --- a/tooling/noir_codegen/test/assert_lt/target/assert_lt.json +++ b/tooling/noir_codegen/test/assert_lt/target/assert_lt.json @@ -1 +1 @@ -{"hash":13834844072603749544,"backend":"acvm-backend-barretenberg","abi":{"parameters":[{"name":"x","type":{"kind":"integer","sign":"unsigned","width":64},"visibility":"private"},{"name":"y","type":{"kind":"integer","sign":"unsigned","width":64},"visibility":"public"}],"param_witnesses":{"x":[1],"y":[2]},"return_type":{"kind":"integer","sign":"unsigned","width":64},"return_witnesses":[12]},"bytecode":"H4sIAAAAAAAA/+1WUW6DMAx1QksZoGr72jUcAiX8VbvJ0Oj9j7ChJpKbtXw0NpvUWkImUXixn53w3gDgHc6mfh7t/ZGMtR9TU96HeYuHtp36ZjLWfGIzjK7DthsPzjjTue6rcdZOrnX9MA49Dqa1kzl1gz3h2bL7sTDCMhmJbylmTDOT8WEhjXfjH/DcB8u8zwVygWifmL/9lTnWzSWKsxHA3QJf00vlveWvERJIUU4x0eb86aEJppljVox9oO+Py8QTV1Jnw6a85t7vSL8pwvN89j7gd88o8q79Gr2wRt3AeSFz4XvRSyokl5MAtSfgGO2ZCewdsDibLRVrDzIXTMxfqiLIGXPeMdY1gb/Fg8+tznJY50eSGmfB2DNrqciCD+tCRc4X5FNFJmIWnkhu3BL+t4qc8y75aySqIkvGOP9CRWKaGQ0ydUrsgUUVWXlfw4OpyAouVWQN66pITDPDqSJfQaZxuVVkxZhzzVgLTv5uHbDwXhN+vwGywklHPBQAAA=="} \ No newline at end of file +{"noir_version":"0.19.3+e9322d14070fa444d77ee5c43c905dd86a67c6e3","hash":9449934793688855780,"backend":"acvm-backend-barretenberg","abi":{"parameters":[{"name":"x","type":{"kind":"integer","sign":"unsigned","width":64},"visibility":"private"},{"name":"y","type":{"kind":"integer","sign":"unsigned","width":64},"visibility":"public"},{"name":"array","type":{"kind":"array","length":5,"type":{"kind":"integer","sign":"unsigned","width":8}},"visibility":"private"},{"name":"my_struct","type":{"kind":"struct","path":"MyStruct","fields":[{"name":"foo","type":{"kind":"boolean"}},{"name":"bar","type":{"kind":"array","length":3,"type":{"kind":"string","length":5}}}]},"visibility":"private"},{"name":"string","type":{"kind":"string","length":5},"visibility":"private"}],"param_witnesses":{"array":[{"start":3,"end":8}],"my_struct":[{"start":8,"end":24}],"string":[{"start":24,"end":29}],"x":[{"start":1,"end":2}],"y":[{"start":2,"end":3}]},"return_type":{"kind":"tuple","fields":[{"kind":"integer","sign":"unsigned","width":64},{"kind":"integer","sign":"unsigned","width":64},{"kind":"struct","path":"MyStruct","fields":[{"name":"foo","type":{"kind":"boolean"}},{"name":"bar","type":{"kind":"array","length":3,"type":{"kind":"string","length":5}}}]}]},"return_witnesses":[31,32,33,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23]},"bytecode":"H4sIAAAAAAAA/81XbU/CMBDu5hv4gopvvGw49JOJH1q2wfaN+E+AddFEgzGL/H250Go5dInumnhJ0z2jXJ9er7s+t4yxe7YyZ9lc1Y8N7CK8tWw1A28jvIPwLsJ7Cus5mfIPxquZqBlzmX5DPowiORpIEYoJH6TTJOZRPB0mIhFxEmeDJAxlEiWjdJqOeCqiUIo8TsNcOa7RceQ6DnUUl32EDxA+RPgI4QbCxwifIHyKcBPhM4TPEb5A+BLhK4RbCLcR7iDcRdhjX3mjzUb+jIlyxibPFgFPmYNlVnm2yXjOcps8O3Q8pU2eXTqemU2eHh3PGdQbl22aS8zZYXRn3/07L4FffLN0Mt9mXH3V99iqhuu80GOgzj+wzZxxjGdXjXFLxjg/+Kkb7/T/G8bvVRe/EQxzciqfvgok9QXEp+P4eQHpGT61bRHHw9ahqurrhjCeZfH7JU+OeAqfcM09wn2tEL/SD9x/Pjdl+8yr2do54dVMUJ6Ta0b/3TF92tr3gI53aJNnn3DfuwZHyE8o2FDIQYBr0Q1FFoQmiEsQlCAiociCWASBCKIQhCCIPxB8IPJA2IGYA9EBF3q4LMNcHlsv/E31XGUOyI1g2fpsvfDfqd5T/aQo5MtrERTzYJJlweKpeAzm7/Itf54vPgBYg2KL1RAAAA=="} \ No newline at end of file diff --git a/tooling/noir_codegen/test/index.test.ts b/tooling/noir_codegen/test/index.test.ts index 702ba1f9cbb..48199c13a67 100644 --- a/tooling/noir_codegen/test/index.test.ts +++ b/tooling/noir_codegen/test/index.test.ts @@ -1,11 +1,16 @@ import { expect } from 'chai'; -import { assert_lt } from './codegen/index.js'; +import { assert_lt, MyStruct, u64 } from './codegen/index.js'; it('codegens a callable function', async () => { - const result = await assert_lt({ - x: '2', - y: '3', - }); + const [sum, constant, struct]: [u64, u64, MyStruct] = await assert_lt( + '2', + '3', + [0, 0, 0, 0, 0], + { foo: true, bar: ['12345', '12345', '12345'] }, + '12345', + ); - expect(result).to.be.eq('0x05'); + expect(sum).to.be.eq('0x05'); + expect(constant).to.be.eq('0x03'); + expect(struct).to.be.deep.eq({ foo: true, bar: ['12345', '12345', '12345'] }); }); diff --git a/tooling/noir_js/package.json b/tooling/noir_js/package.json index 16f63133b84..7818e689181 100644 --- a/tooling/noir_js/package.json +++ b/tooling/noir_js/package.json @@ -3,7 +3,7 @@ "collaborators": [ "The Noir Team " ], - "version": "0.19.0", + "version": "0.19.4", "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 35a3e716b6b..bacb391a464 100644 --- a/tooling/noir_js/src/index.ts +++ b/tooling/noir_js/src/index.ts @@ -1,6 +1,6 @@ import * as acvm from '@noir-lang/acvm_js'; import * as abi from '@noir-lang/noirc_abi'; -export { acvm, abi }; +import { CompiledCircuit, ProofData } from '@noir-lang/types'; export { ecdsa_secp256r1_verify, @@ -11,6 +11,14 @@ export { xor, and, } from '@noir-lang/acvm_js'; + +export { InputMap } from '@noir-lang/noirc_abi'; export { WitnessMap, ForeignCallHandler, ForeignCallInput, ForeignCallOutput } from '@noir-lang/acvm_js'; export { Noir } from './program.js'; + +/** @ignore */ +export { acvm, abi }; + +// type exports for typedoc +export { CompiledCircuit, ProofData }; diff --git a/tooling/noir_js/src/program.ts b/tooling/noir_js/src/program.ts index cfd7a715dfe..711413bbc84 100644 --- a/tooling/noir_js/src/program.ts +++ b/tooling/noir_js/src/program.ts @@ -10,6 +10,7 @@ export class Noir { private backend?: Backend, ) {} + /** @ignore */ async init(): Promise { // If these are available, then we are in the // web environment. For the node environment, this @@ -19,6 +20,17 @@ export class Noir { } } + /** + * + * @description + * Destroys the underlying backend instance. + * + * @example + * ```typescript + * await noir.destroy(); + * ``` + * + */ async destroy(): Promise { await this.backend?.destroy(); } @@ -29,6 +41,15 @@ export class Noir { } // Initial inputs to your program + /** + * @description + * Allows to execute a circuit to get its witness and return value. + * + * @example + * ```typescript + * async execute(inputs) + * ``` + */ async execute( inputs: InputMap, foreignCallHandler?: ForeignCallHandler, @@ -39,12 +60,34 @@ export class Noir { return { witness: compressWitness(witness), returnValue }; } - // Initial inputs to your program + /** + * + * @description + * Generates a witness and a proof given an object as input. + * + * @example + * ```typescript + * async generateFinalproof(input) + * ``` + * + */ async generateFinalProof(inputs: InputMap): Promise { const { witness } = await this.execute(inputs); return this.getBackend().generateFinalProof(witness); } + /** + * + * @description + * Instantiates the verification key and verifies a proof. + * + * + * @example + * ```typescript + * async verifyFinalProof(proof) + * ``` + * + */ async verifyFinalProof(proofData: ProofData): Promise { return this.getBackend().verifyFinalProof(proofData); } diff --git a/tooling/noir_js/test/node/e2e.test.ts b/tooling/noir_js/test/node/e2e.test.ts index f7c3ea37a1a..33d64377b06 100644 --- a/tooling/noir_js/test/node/e2e.test.ts +++ b/tooling/noir_js/test/node/e2e.test.ts @@ -69,19 +69,7 @@ it('end-to-end proof creation and verification (inner)', async () => { expect(isValid).to.be.true; }); -// The "real" workflow will involve a prover and a verifier on different systems. -// -// We cannot do this in our tests because they will panic with: -// `unreachable` -// -// This happens when we we create a proof with one barretenberg instance and -// try to verify it with another. -// -// If this bug is fixed, we can remove this test and split barretenberg into -// a prover and verifier class to more accurately reflect what happens in production. -// -// If its not fixable, we can leave it in as documentation of this behavior. -it('[BUG] -- bb.js unreachable (different instance) ', async () => { +it('end-to-end proving and verification with different instances', async () => { // Noir.Js part const inputs = { x: '2', @@ -97,16 +85,9 @@ it('[BUG] -- bb.js unreachable (different instance) ', async () => { const proof = await prover.generateFinalProof(witness); - try { - const verifier = new Backend(assert_lt_program); - await verifier.verifyFinalProof(proof); - expect.fail( - 'bb.js currently returns a bug when we try to verify a proof with a different Barretenberg instance that created it.', - ); - } catch (error) { - const knownError = error as Error; - expect(knownError.message).to.contain('unreachable'); - } + const verifier = new Backend(assert_lt_program); + const proof_is_valid = await verifier.verifyFinalProof(proof); + expect(proof_is_valid).to.be.true; }); // This bug occurs when we use the same backend to create an inner proof and then an outer proof 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 67b8c84bdcd..0698cbce4a8 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 { +fn main(x: u64, y: pub u64) -> pub u64 { assert(x < y); x + y } diff --git a/tooling/noir_js/tsconfig.json b/tooling/noir_js/tsconfig.json index 1e0fdea09c7..0dbc5204556 100644 --- a/tooling/noir_js/tsconfig.json +++ b/tooling/noir_js/tsconfig.json @@ -2,7 +2,6 @@ "compilerOptions": { "target": "esnext", "declaration": true, - "emitDeclarationOnly": false, "module": "ESNext", "moduleResolution": "node", "outDir": "./lib", @@ -12,5 +11,10 @@ "noImplicitAny": false, }, "include": ["src/**/*.ts"], - "exclude": ["node_modules"] -} \ No newline at end of file + "exclude": ["node_modules"], + "references": [ + { + "path": "../noir_js_types" + }, + ] +} diff --git a/tooling/noir_js_backend_barretenberg/.mocharc.json b/tooling/noir_js_backend_barretenberg/.mocharc.json new file mode 100644 index 00000000000..e1023f56327 --- /dev/null +++ b/tooling/noir_js_backend_barretenberg/.mocharc.json @@ -0,0 +1,11 @@ +{ + "require": "ts-node/register", + "loader": "ts-node/esm", + "extensions": [ + "ts", + "cjs" + ], + "spec": [ + "test/**/*.test.ts*" + ] +} diff --git a/tooling/noir_js_backend_barretenberg/package.json b/tooling/noir_js_backend_barretenberg/package.json index d13f89a2a97..baf8c0f58fd 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.19.0", + "version": "0.19.4", "packageManager": "yarn@3.5.1", "license": "(MIT OR Apache-2.0)", "type": "module", @@ -25,6 +25,7 @@ "generate:package": "bash ./fixup.sh", "build": "yarn clean && tsc && tsc -p ./tsconfig.cjs.json && yarn generate:package", "clean": "rm -rf ./lib", + "test": "mocha --timeout 25000 --exit --config ./.mocharc.json", "prettier": "prettier 'src/**/*.ts'", "prettier:fix": "prettier --write 'src/**/*.ts' 'test/**/*.ts'", "nightly:version": "jq --arg new_version \"-$(git rev-parse --short HEAD)$1\" '.version = .version + $new_version' package.json > package-tmp.json && mv package-tmp.json package.json", @@ -32,16 +33,19 @@ "lint": "NODE_NO_WARNINGS=1 eslint . --ext .ts --ignore-path ./.eslintignore --max-warnings 0" }, "dependencies": { - "@aztec/bb.js": "0.12.0", + "@aztec/bb.js": "0.16.0", "@noir-lang/types": "workspace:*", "fflate": "^0.8.0" }, "devDependencies": { "@types/node": "^20.6.2", "@types/prettier": "^3", + "chai": "^4.3.8", "eslint": "^8.50.0", "eslint-plugin-prettier": "^5.0.0", + "mocha": "^10.2.0", "prettier": "3.0.3", + "ts-node": "^10.9.1", "typescript": "5.1.5" } } diff --git a/tooling/noir_js_backend_barretenberg/src/index.ts b/tooling/noir_js_backend_barretenberg/src/index.ts index 11c56a7384d..100418debd0 100644 --- a/tooling/noir_js_backend_barretenberg/src/index.ts +++ b/tooling/noir_js_backend_barretenberg/src/index.ts @@ -3,6 +3,9 @@ import { decompressSync as gunzip } from 'fflate'; import { acirToUint8Array } from './serialize.js'; import { Backend, CompiledCircuit, ProofData } from '@noir-lang/types'; import { BackendOptions } from './types.js'; +import { deflattenPublicInputs, flattenPublicInputsAsArray } from './public_inputs.js'; + +export { flattenPublicInputs } from './public_inputs.js'; // This is the number of bytes in a UltraPlonk proof // minus the public inputs. @@ -18,13 +21,14 @@ export class BarretenbergBackend implements Backend { private acirUncompressedBytecode: Uint8Array; constructor( - acirCircuit: CompiledCircuit, + private acirCircuit: CompiledCircuit, private options: BackendOptions = { threads: 1 }, ) { const acirBytecodeBase64 = acirCircuit.bytecode; this.acirUncompressedBytecode = acirToUint8Array(acirBytecodeBase64); } + /** @ignore */ async instantiate(): Promise { if (!this.api) { // eslint-disable-next-line @typescript-eslint/ban-ts-comment @@ -38,6 +42,7 @@ export class BarretenbergBackend implements Backend { await api.srsInitSrs(new RawBuffer(crs.getG1Data()), crs.numPoints, new RawBuffer(crs.getG2Data())); this.acirComposer = await api.acirNewAcirComposer(subgroupSize); + await api.acirInitProvingKey(this.acirComposer, this.acirUncompressedBytecode); this.api = api; } } @@ -63,11 +68,20 @@ export class BarretenbergBackend implements Backend { // We set `makeEasyToVerifyInCircuit` to true, which will tell the backend to // generate the proof using components that will make the proof // easier to verify in a circuit. + + /** + * + * @example + * ```typescript + * const intermediateProof = await backend.generateIntermediateProof(witness); + * ``` + */ async generateIntermediateProof(witness: Uint8Array): Promise { const makeEasyToVerifyInCircuit = true; return this.generateProof(witness, makeEasyToVerifyInCircuit); } + /** @ignore */ async generateProof(compressedWitness: Uint8Array, makeEasyToVerifyInCircuit: boolean): Promise { await this.instantiate(); const proofWithPublicInputs = await this.api.acirCreateProof( @@ -80,16 +94,8 @@ export class BarretenbergBackend implements Backend { const splitIndex = proofWithPublicInputs.length - numBytesInProofWithoutPublicInputs; const publicInputsConcatenated = proofWithPublicInputs.slice(0, splitIndex); - - const publicInputSize = 32; - const publicInputs: Uint8Array[] = []; - - for (let i = 0; i < publicInputsConcatenated.length; i += publicInputSize) { - const publicInput = publicInputsConcatenated.slice(i, i + publicInputSize); - publicInputs.push(publicInput); - } - const proof = proofWithPublicInputs.slice(splitIndex); + const publicInputs = deflattenPublicInputs(publicInputsConcatenated, this.acirCircuit.abi); return { proof, publicInputs }; } @@ -103,6 +109,14 @@ export class BarretenbergBackend implements Backend { // method. // // The number of public inputs denotes how many public inputs are in the inner proof. + + /** + * + * @example + * ```typescript + * const artifacts = await backend.generateIntermediateProofArtifacts(proof, numOfPublicInputs); + * ``` + */ async generateIntermediateProofArtifacts( proofData: ProofData, numOfPublicInputs = 0, @@ -136,12 +150,20 @@ export class BarretenbergBackend implements Backend { return verified; } + /** + * + * @example + * ```typescript + * const isValidIntermediate = await backend.verifyIntermediateProof(proof); + * ``` + */ async verifyIntermediateProof(proofData: ProofData): Promise { const proof = reconstructProofWithPublicInputs(proofData); const makeEasyToVerifyInCircuit = true; return this.verifyProof(proof, makeEasyToVerifyInCircuit); } + /** @ignore */ async verifyProof(proof: Uint8Array, makeEasyToVerifyInCircuit: boolean): Promise { await this.instantiate(); await this.api.acirInitVerificationKey(this.acirComposer); @@ -158,7 +180,7 @@ export class BarretenbergBackend implements Backend { function reconstructProofWithPublicInputs(proofData: ProofData): Uint8Array { // Flatten publicInputs - const publicInputsConcatenated = flattenUint8Arrays(proofData.publicInputs); + const publicInputsConcatenated = flattenPublicInputsAsArray(proofData.publicInputs); // Concatenate publicInputs and proof const proofWithPublicInputs = Uint8Array.from([...publicInputsConcatenated, ...proofData.proof]); @@ -166,15 +188,5 @@ function reconstructProofWithPublicInputs(proofData: ProofData): Uint8Array { return proofWithPublicInputs; } -function flattenUint8Arrays(arrays: Uint8Array[]): Uint8Array { - const totalLength = arrays.reduce((acc, val) => acc + val.length, 0); - const result = new Uint8Array(totalLength); - - let offset = 0; - for (const arr of arrays) { - result.set(arr, offset); - offset += arr.length; - } - - return result; -} +// typedoc exports +export { Backend, BackendOptions, CompiledCircuit, ProofData }; diff --git a/tooling/noir_js_backend_barretenberg/src/public_inputs.ts b/tooling/noir_js_backend_barretenberg/src/public_inputs.ts new file mode 100644 index 00000000000..37bc5b13012 --- /dev/null +++ b/tooling/noir_js_backend_barretenberg/src/public_inputs.ts @@ -0,0 +1,89 @@ +import { Abi, WitnessMap } from '@noir-lang/types'; + +export function flattenPublicInputs(publicInputs: WitnessMap): string[] { + const publicInputIndices = [...publicInputs.keys()].sort((a, b) => a - b); + const flattenedPublicInputs = publicInputIndices.map((index) => publicInputs.get(index) as string); + return flattenedPublicInputs; +} + +export function flattenPublicInputsAsArray(publicInputs: WitnessMap): Uint8Array { + const flatPublicInputs = flattenPublicInputs(publicInputs); + const flattenedPublicInputs = flatPublicInputs.map(hexToUint8Array); + return flattenUint8Arrays(flattenedPublicInputs); +} + +export function deflattenPublicInputs(flattenedPublicInputs: Uint8Array, abi: Abi): WitnessMap { + const publicInputSize = 32; + const chunkedFlattenedPublicInputs: Uint8Array[] = []; + + for (let i = 0; i < flattenedPublicInputs.length; i += publicInputSize) { + const publicInput = flattenedPublicInputs.slice(i, i + publicInputSize); + chunkedFlattenedPublicInputs.push(publicInput); + } + + const return_value_witnesses = abi.return_witnesses; + const public_parameters = abi.parameters.filter((param) => param.visibility === 'public'); + const public_parameter_witnesses: number[] = public_parameters.flatMap((param) => + abi.param_witnesses[param.name].flatMap((witness_range) => + Array.from({ length: witness_range.end - witness_range.start }, (_, i) => witness_range.start + i), + ), + ); + + // We now have an array of witness indices which have been deduplicated and sorted in ascending order. + // The elements of this array should correspond to the elements of `flattenedPublicInputs` so that we can build up a `WitnessMap`. + const public_input_witnesses = [...new Set(public_parameter_witnesses.concat(return_value_witnesses))].sort( + (a, b) => a - b, + ); + + const publicInputs: WitnessMap = new Map(); + public_input_witnesses.forEach((witness_index, index) => { + const witness_value = uint8ArrayToHex(chunkedFlattenedPublicInputs[index]); + publicInputs.set(witness_index, witness_value); + }); + + return publicInputs; +} + +function flattenUint8Arrays(arrays: Uint8Array[]): Uint8Array { + const totalLength = arrays.reduce((acc, val) => acc + val.length, 0); + const result = new Uint8Array(totalLength); + + let offset = 0; + for (const arr of arrays) { + result.set(arr, offset); + offset += arr.length; + } + + return result; +} + +function uint8ArrayToHex(buffer: Uint8Array): string { + const hex: string[] = []; + + buffer.forEach(function (i) { + let h = i.toString(16); + if (h.length % 2) { + h = '0' + h; + } + hex.push(h); + }); + + return '0x' + hex.join(''); +} + +function hexToUint8Array(hex: string): Uint8Array { + const sanitised_hex = BigInt(hex).toString(16).padStart(64, '0'); + + const len = sanitised_hex.length / 2; + const u8 = new Uint8Array(len); + + let i = 0; + let j = 0; + while (i < len) { + u8[i] = parseInt(sanitised_hex.slice(j, j + 2), 16); + i += 1; + j += 2; + } + + return u8; +} diff --git a/tooling/noir_js_backend_barretenberg/src/types.ts b/tooling/noir_js_backend_barretenberg/src/types.ts index b88a942d986..041e36fdf91 100644 --- a/tooling/noir_js_backend_barretenberg/src/types.ts +++ b/tooling/noir_js_backend_barretenberg/src/types.ts @@ -1,3 +1,8 @@ +/** + * @description + * An options object, currently only used to specify the number of threads to use. + */ export type BackendOptions = { + /** @description Number of threads */ threads: number; }; diff --git a/tooling/noir_js_backend_barretenberg/test/public_input_deflattening.test.ts b/tooling/noir_js_backend_barretenberg/test/public_input_deflattening.test.ts new file mode 100644 index 00000000000..98189eaed5f --- /dev/null +++ b/tooling/noir_js_backend_barretenberg/test/public_input_deflattening.test.ts @@ -0,0 +1,93 @@ +import { Abi } from '@noir-lang/types'; +import { expect } from 'chai'; +import { flattenPublicInputsAsArray, deflattenPublicInputs, flattenPublicInputs } from '../src/public_inputs.js'; + +const abi: Abi = { + parameters: [ + { + name: 'array_with_returned_element', + type: { + kind: 'array', + type: { + kind: 'field', + }, + length: 10, + }, + visibility: 'private', + }, + { + name: 'pub_field', + type: { + kind: 'field', + }, + visibility: 'public', + }, + ], + param_witnesses: { + array_with_returned_element: [ + { + start: 1, + end: 11, + }, + ], + pub_field: [ + { + start: 11, + end: 12, + }, + ], + }, + return_type: { + kind: 'tuple', + fields: [ + { + kind: 'field', + }, + { + kind: 'field', + }, + { + kind: 'field', + }, + ], + }, + return_witnesses: [2, 13, 13], +}; + +it('flattens a witness map in order of its witness indices', async () => { + // Note that these are not in ascending order. This means that if we read from `witness_map` in insertion order + // then the witness values will be sorted incorrectly. + const public_input_indices = [2, 13, 11]; + + const witness_map = new Map( + public_input_indices.map((witness_index) => [ + witness_index, + '0x' + BigInt(witness_index).toString(16).padStart(64, '0'), + ]), + ); + + const flattened_public_inputs = flattenPublicInputs(witness_map); + expect(flattened_public_inputs).to.be.deep.eq([ + '0x0000000000000000000000000000000000000000000000000000000000000002', + '0x000000000000000000000000000000000000000000000000000000000000000b', + '0x000000000000000000000000000000000000000000000000000000000000000d', + ]); +}); + +it('recovers the original witness map when deflattening a public input array', async () => { + // Note that these are not in ascending order. This means that if we read from `witness_map` in insertion order + // then the witness values will be sorted incorrectly. + const public_input_indices = [2, 13, 11]; + + const witness_map = new Map( + public_input_indices.map((witness_index) => [ + witness_index, + '0x' + BigInt(witness_index).toString(16).padStart(64, '0'), + ]), + ); + + const flattened_public_inputs = flattenPublicInputsAsArray(witness_map); + const deflattened_public_inputs = deflattenPublicInputs(flattened_public_inputs, abi); + + expect(deflattened_public_inputs).to.be.deep.eq(witness_map); +}); diff --git a/tooling/noir_js_backend_barretenberg/tsconfig.json b/tooling/noir_js_backend_barretenberg/tsconfig.json index 393fa38f583..1e28c044bba 100644 --- a/tooling/noir_js_backend_barretenberg/tsconfig.json +++ b/tooling/noir_js_backend_barretenberg/tsconfig.json @@ -12,5 +12,10 @@ "noImplicitAny": false, }, "include": ["src/**/*.ts"], - "exclude": ["node_modules"] + "exclude": ["node_modules"], + "references": [ + { + "path": "../noir_js_types" + } + ] } diff --git a/tooling/noir_js_types/.gitignore b/tooling/noir_js_types/.gitignore index 7951405f85a..e92523b6247 100644 --- a/tooling/noir_js_types/.gitignore +++ b/tooling/noir_js_types/.gitignore @@ -1 +1,3 @@ -lib \ No newline at end of file +lib + +*.tsbuildinfo diff --git a/tooling/noir_js_types/package.json b/tooling/noir_js_types/package.json index 8daf73d5841..991052f1ad9 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.19.0", + "version": "0.19.4", "license": "(MIT OR Apache-2.0)", "files": [ "lib", @@ -19,7 +19,8 @@ "build": "yarn run build:cjs && yarn run build:esm", "nightly:version": "jq --arg new_version \"-$(git rev-parse --short HEAD)$1\" '.version = .version + $new_version' package.json > package-tmp.json && mv package-tmp.json package.json", "publish": "echo 📡 publishing `$npm_package_name` && yarn npm publish", - "lint": "NODE_NO_WARNINGS=1 eslint . --ext .ts --ignore-path ./.eslintignore --max-warnings 0" + "lint": "NODE_NO_WARNINGS=1 eslint . --ext .ts --ignore-path ./.eslintignore --max-warnings 0", + "clean": "rm -rf ./lib" }, "exports": { ".": { diff --git a/tooling/noir_js_types/src/types.ts b/tooling/noir_js_types/src/types.ts index f534ec9a920..b997d92425d 100644 --- a/tooling/noir_js_types/src/types.ts +++ b/tooling/noir_js_types/src/types.ts @@ -1,25 +1,62 @@ -import { Abi } from '@noir-lang/noirc_abi'; +import { Abi, WitnessMap } from '@noir-lang/noirc_abi'; + +export { Abi, WitnessMap } from '@noir-lang/noirc_abi'; export interface Backend { - // Generate an outer proof. This is the proof for the circuit which will verify - // inner proofs and or can be seen as the proof created for regular circuits. + /** + * @description Generates a final proof (not meant to be verified in another circuit) */ generateFinalProof(decompressedWitness: Uint8Array): Promise; - // Generates an inner proof. This is the proof that will be verified - // in another circuit. + /** + * @description Generates an intermediate proof (meant to be verified in another circuit) */ generateIntermediateProof(decompressedWitness: Uint8Array): Promise; + /** + * + * @description Retrieves the artifacts from a proof in the Field format + */ + generateIntermediateProofArtifacts( + proofData: ProofData, + numOfPublicInputs: number, + ): Promise<{ + /** @description An array of Fields containing the proof */ + proofAsFields: string[]; + /** @description An array of Fields containing the verification key */ + vkAsFields: string[]; + /** @description A Field containing the verification key hash */ + vkHash: string; + }>; + + /** + * @description Verifies a final proof */ verifyFinalProof(proofData: ProofData): Promise; + + /** @description Verifies an intermediate proof */ verifyIntermediateProof(proofData: ProofData): Promise; + + /** + * @description Destroys the backend */ destroy(): Promise; } +/** + * @description + * The representation of a proof + * */ export type ProofData = { - publicInputs: Uint8Array[]; + /** @description Public inputs of a proof */ + publicInputs: WitnessMap; + /** @description An byte array representing the proof */ proof: Uint8Array; }; +/** + * @description + * The representation of a compiled circuit + * */ export type CompiledCircuit = { + /** @description The bytecode of the circuit */ bytecode: string; + /** @description ABI representation of the circuit */ abi: Abi; }; diff --git a/tooling/noir_js_types/tsconfig.json b/tooling/noir_js_types/tsconfig.json index 1fe2a46abaa..0d5441cc5a2 100644 --- a/tooling/noir_js_types/tsconfig.json +++ b/tooling/noir_js_types/tsconfig.json @@ -1,12 +1,13 @@ { "compilerOptions": { + "composite": true, "declaration": true, "module": "ESNext", "moduleResolution": "node", "outDir": "lib/esm", "target": "ES2020", - "rootDir": "./src" + "rootDir": "./src", }, "include": ["src/**/*.ts"], "exclude": ["node_modules"] -} \ No newline at end of file +} diff --git a/tooling/noirc_abi_wasm/build.sh b/tooling/noirc_abi_wasm/build.sh index 37f2fd0a5a9..24af149bcea 100755 --- a/tooling/noirc_abi_wasm/build.sh +++ b/tooling/noirc_abi_wasm/build.sh @@ -34,7 +34,7 @@ export CARGO_TARGET_DIR=$self_path/target rm -rf $self_path/outputs >/dev/null 2>&1 rm -rf $self_path/result >/dev/null 2>&1 -if [ -v out ]; then +if [ -n "$out" ]; then echo "Will install package to $out (defined outside installPhase.sh script)" else export out="$self_path/outputs/out" diff --git a/tooling/noirc_abi_wasm/package.json b/tooling/noirc_abi_wasm/package.json index 4f57e9ea684..f1c68df8804 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.19.0", + "version": "0.19.4", "license": "(MIT OR Apache-2.0)", "files": [ "nodejs", diff --git a/tooling/noirc_abi_wasm/src/lib.rs b/tooling/noirc_abi_wasm/src/lib.rs index 734ca1ff40e..a3d829dd40f 100644 --- a/tooling/noirc_abi_wasm/src/lib.rs +++ b/tooling/noirc_abi_wasm/src/lib.rs @@ -56,7 +56,7 @@ export type AbiType = { kind: "integer", sign: Sign, width: number } | { kind: "array", length: number, type: AbiType } | { kind: "tuple", fields: AbiType[] } | - { kind: "struct", path: string, fields: [string, AbiType][] }; + { kind: "struct", path: string, fields: { name: string, type: AbiType }[] }; export type AbiParameter = { name: string, diff --git a/tooling/readme.md b/tooling/readme.md new file mode 100644 index 00000000000..20d1b560b5b --- /dev/null +++ b/tooling/readme.md @@ -0,0 +1,15 @@ +# Structure + +Below we briefly describe the purpose of each tool-related crate in this repository. + +## nargo + +This is the default package manager used by Noir. One may draw similarities to Rusts' Cargo. + +## nargo_fmt + +This is the default formatter used by Noir, analogous to Rust's rustfmt. + +## lsp + +This is the platform agnostic implementation of Noir's Language Server. It implements the various features supported, but doesn't bind to any particular transport. Binding to a transport must be done when consuming the crate. diff --git a/yarn.lock b/yarn.lock index f423ba8b384..b21d1256eff 100644 --- a/yarn.lock +++ b/yarn.lock @@ -221,9 +221,9 @@ __metadata: languageName: node linkType: hard -"@aztec/bb.js@npm:0.12.0": - version: 0.12.0 - resolution: "@aztec/bb.js@npm:0.12.0" +"@aztec/bb.js@npm:0.16.0": + version: 0.16.0 + resolution: "@aztec/bb.js@npm:0.16.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: d9d57b893b9b1c61949cb9bd911d4b7e1ece34965ccb9e122b39cd4e2edac9f06858abbe05c23f66141c9a74ff4861a354bfc5d62e5dcf772cfe8d1c783e2562 + checksum: 5f68b4ad16284a3a871e0ad21fea05aed670383bc639c9d07ab3bf9b7a9d15cc8a4e5cda404a9290775ad5023924739543a8aac37d602892dd1fb5087521970b languageName: node linkType: hard @@ -2281,7 +2281,7 @@ __metadata: languageName: node linkType: hard -"@docusaurus/types@npm:2.4.3": +"@docusaurus/types@npm:2.4.3, @docusaurus/types@npm:^2.4.1": version: 2.4.3 resolution: "@docusaurus/types@npm:2.4.3" dependencies: @@ -3434,14 +3434,17 @@ __metadata: version: 0.0.0-use.local resolution: "@noir-lang/backend_barretenberg@workspace:tooling/noir_js_backend_barretenberg" dependencies: - "@aztec/bb.js": 0.12.0 + "@aztec/bb.js": 0.16.0 "@noir-lang/types": "workspace:*" "@types/node": ^20.6.2 "@types/prettier": ^3 + chai: ^4.3.8 eslint: ^8.50.0 eslint-plugin-prettier: ^5.0.0 fflate: ^0.8.0 + mocha: ^10.2.0 prettier: 3.0.3 + ts-node: ^10.9.1 typescript: 5.1.5 languageName: unknown linkType: soft @@ -5915,6 +5918,13 @@ __metadata: languageName: node linkType: hard +"ansi-sequence-parser@npm:^1.1.0": + version: 1.1.1 + resolution: "ansi-sequence-parser@npm:1.1.1" + checksum: ead5b15c596e8e85ca02951a844366c6776769dcc9fd1bd3a0db11bb21364554822c6a439877fb599e7e1ffa0b5f039f1e5501423950457f3dcb2f480c30b188 + languageName: node + linkType: hard + "ansi-styles@npm:^3.2.1": version: 3.2.1 resolution: "ansi-styles@npm:3.2.1" @@ -8283,14 +8293,21 @@ __metadata: "@docusaurus/preset-classic": ^2.4.0 "@easyops-cn/docusaurus-search-local": ^0.35.0 "@mdx-js/react": ^1.6.22 + "@noir-lang/noir_js": "workspace:*" axios: ^1.4.0 clsx: ^1.2.1 + docusaurus-plugin-typedoc: 1.0.0-next.18 hast-util-is-element: ^1.1.0 prism-react-renderer: ^1.3.5 react: ^17.0.2 react-dom: ^17.0.2 rehype-katex: ^5.0.0 remark-math: ^3.0.1 + typedoc: ^0.25.0 + typedoc-plugin-frontmatter: ^0.0.2 + typedoc-plugin-markdown: 4.0.0-next.25 + typedoc-plugin-merge-modules: ^5.1.0 + typescript: ^5.2.2 languageName: unknown linkType: soft @@ -8303,6 +8320,17 @@ __metadata: languageName: node linkType: hard +"docusaurus-plugin-typedoc@npm:1.0.0-next.18": + version: 1.0.0-next.18 + resolution: "docusaurus-plugin-typedoc@npm:1.0.0-next.18" + dependencies: + "@docusaurus/types": ^2.4.1 + peerDependencies: + typedoc-plugin-markdown: ">=4.0.0-next.24" + checksum: a501e3bd1cc5b33d215a1b71b018a34d4aa5bd98f39580ab114a127d3f555078443d6b690119c0adb17fb8867ba4131382ad5d14dbaa7919715020cdfaf6b9c4 + languageName: node + linkType: hard + "dom-converter@npm:^0.2.0": version: 0.2.0 resolution: "dom-converter@npm:0.2.0" @@ -11459,6 +11487,13 @@ __metadata: languageName: node linkType: hard +"jsonc-parser@npm:^3.2.0": + version: 3.2.0 + resolution: "jsonc-parser@npm:3.2.0" + checksum: 946dd9a5f326b745aa326d48a7257e3f4a4b62c5e98ec8e49fa2bdd8d96cef7e6febf1399f5c7016114fd1f68a1c62c6138826d5d90bc650448e3cf0951c53c7 + languageName: node + linkType: hard + "jsonfile@npm:^2.1.0": version: 2.4.0 resolution: "jsonfile@npm:2.4.0" @@ -12140,6 +12175,15 @@ __metadata: languageName: node linkType: hard +"marked@npm:^4.3.0": + version: 4.3.0 + resolution: "marked@npm:4.3.0" + bin: + marked: bin/marked.js + checksum: 0db6817893952c3ec710eb9ceafb8468bf5ae38cb0f92b7b083baa13d70b19774674be04db5b817681fa7c5c6a088f61300815e4dd75a59696f4716ad69f6260 + languageName: node + linkType: hard + "marky@npm:^1.2.2": version: 1.2.5 resolution: "marky@npm:1.2.5" @@ -12425,7 +12469,7 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:^9.0.1": +"minimatch@npm:^9.0.1, minimatch@npm:^9.0.3": version: 9.0.3 resolution: "minimatch@npm:9.0.3" dependencies: @@ -15471,6 +15515,18 @@ __metadata: languageName: node linkType: hard +"shiki@npm:^0.14.1": + version: 0.14.5 + resolution: "shiki@npm:0.14.5" + dependencies: + ansi-sequence-parser: ^1.1.0 + jsonc-parser: ^3.2.0 + vscode-oniguruma: ^1.7.0 + vscode-textmate: ^8.0.0 + checksum: 41d847817cfc9bb6d8bf190316896698d250303656546446659cc02caed8dcc171b10cd113bb5da82425b51d0032e87aafcdc36c3dd61dadc123170b438da736 + languageName: node + linkType: hard + "side-channel@npm:^1.0.4": version: 1.0.4 resolution: "side-channel@npm:1.0.4" @@ -16511,6 +16567,49 @@ __metadata: languageName: node linkType: hard +"typedoc-plugin-frontmatter@npm:^0.0.2": + version: 0.0.2 + resolution: "typedoc-plugin-frontmatter@npm:0.0.2" + dependencies: + yaml: ^2.2.2 + checksum: 44cbdb82e3fd8f4eb89cdf54783b5b07b03a57edc7bda85a48280edba73f401a2f5439cbba97426dd79e9584c410244af5dd20d5d7281c27d67d61675fa7aaef + languageName: node + linkType: hard + +"typedoc-plugin-markdown@npm:4.0.0-next.25": + version: 4.0.0-next.25 + resolution: "typedoc-plugin-markdown@npm:4.0.0-next.25" + peerDependencies: + typedoc: ">=0.25.0" + checksum: 284e2ce44446faf8db5fc54dfa84e7cd819cfc60bbed12ffb781cb24a166ba6b3a220c542990a3eb14aefff33f1d7c8322378297551f55476155cf532fbbb807 + languageName: node + linkType: hard + +"typedoc-plugin-merge-modules@npm:^5.1.0": + version: 5.1.0 + resolution: "typedoc-plugin-merge-modules@npm:5.1.0" + peerDependencies: + typedoc: 0.24.x || 0.25.x + checksum: f01d825a1c6b73c29faaf515e76076931b16bcc5762c8e9b56d18a7eca6d450bd3691012e96bc3a09ce05f29aef90744e93e187171c561ef0a3c2c1fe116803f + languageName: node + linkType: hard + +"typedoc@npm:^0.25.0": + version: 0.25.2 + resolution: "typedoc@npm:0.25.2" + dependencies: + lunr: ^2.3.9 + marked: ^4.3.0 + minimatch: ^9.0.3 + shiki: ^0.14.1 + peerDependencies: + typescript: 4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x + bin: + typedoc: bin/typedoc + checksum: 5b6e24bae7498bb542aaba495378ed5a3e13c76eb04a1ae95b506f76bda4d517847101fb05a7eab3f6b79357d1e2ac6f4747d39792395329b72e463f7effda65 + languageName: node + linkType: hard + "typescript@npm:4.9.4": version: 4.9.4 resolution: "typescript@npm:4.9.4" @@ -17054,6 +17153,20 @@ __metadata: languageName: node linkType: hard +"vscode-oniguruma@npm:^1.7.0": + version: 1.7.0 + resolution: "vscode-oniguruma@npm:1.7.0" + checksum: 53519d91d90593e6fb080260892e87d447e9b200c4964d766772b5053f5699066539d92100f77f1302c91e8fc5d9c772fbe40fe4c90f3d411a96d5a9b1e63f42 + languageName: node + linkType: hard + +"vscode-textmate@npm:^8.0.0": + version: 8.0.0 + resolution: "vscode-textmate@npm:8.0.0" + checksum: 127780dfea89559d70b8326df6ec344cfd701312dd7f3f591a718693812b7852c30b6715e3cfc8b3200a4e2515b4c96f0843c0eacc0a3020969b5de262c2a4bb + languageName: node + linkType: hard + "wait-on@npm:^6.0.1": version: 6.0.1 resolution: "wait-on@npm:6.0.1" @@ -17595,6 +17708,13 @@ __metadata: languageName: node linkType: hard +"yaml@npm:^2.2.2": + version: 2.3.3 + resolution: "yaml@npm:2.3.3" + checksum: cdfd132e7e0259f948929efe8835923df05c013c273c02bb7a2de9b46ac3af53c2778a35b32c7c0f877cc355dc9340ed564018c0242bfbb1278c2a3e53a0e99e + languageName: node + linkType: hard + "yargs-parser@npm:20.2.4": version: 20.2.4 resolution: "yargs-parser@npm:20.2.4"