diff --git a/.github/critical_libraries_status/.gitignore b/.github/critical_libraries_status/.gitignore new file mode 100644 index 00000000000..38a3cf9caf1 --- /dev/null +++ b/.github/critical_libraries_status/.gitignore @@ -0,0 +1,3 @@ +.actual.jsonl +.actual.jsonl.jq +.failures.jsonl.jq \ No newline at end of file diff --git a/.github/critical_libraries_status/noir-lang/noir_bigcurve/.failures.jsonl.does_not_compile b/.github/critical_libraries_status/noir-lang/noir_bigcurve/.failures.jsonl similarity index 100% rename from .github/critical_libraries_status/noir-lang/noir_bigcurve/.failures.jsonl.does_not_compile rename to .github/critical_libraries_status/noir-lang/noir_bigcurve/.failures.jsonl diff --git a/.github/critical_libraries_status/noir-lang/sparse_array/.failures.jsonl.does_not_compile b/.github/critical_libraries_status/noir-lang/noir_check_shuffle/.failures.jsonl similarity index 100% rename from .github/critical_libraries_status/noir-lang/sparse_array/.failures.jsonl.does_not_compile rename to .github/critical_libraries_status/noir-lang/noir_check_shuffle/.failures.jsonl diff --git a/.github/critical_libraries_status/noir-lang/sparse_array/.failures.jsonl b/.github/critical_libraries_status/noir-lang/sparse_array/.failures.jsonl new file mode 100644 index 00000000000..e69de29bb2d diff --git a/.github/workflows/formatting.yml b/.github/workflows/formatting.yml index b132ba6f938..4e836ef2493 100644 --- a/.github/workflows/formatting.yml +++ b/.github/workflows/formatting.yml @@ -143,3 +143,29 @@ jobs: - name: Format test suite working-directory: ./test_programs run: ./format.sh check + + # This is a job which depends on all test jobs and reports the overall status. + # This allows us to add/remove test jobs without having to update the required workflows. + formatting-end: + name: Formatting End + runs-on: ubuntu-22.04 + # We want this job to always run (even if the dependant jobs fail) as we want this job to fail rather than skipping. + if: ${{ always() }} + needs: + - clippy + - rustfmt + - eslint + - nargo_fmt + + steps: + - name: Report overall success + run: | + if [[ $FAIL == true ]]; then + exit 1 + else + exit 0 + fi + env: + # We treat any skipped or failing jobs as a failure for the workflow as a whole. + FAIL: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') || contains(needs.*.result, 'skipped') }} + diff --git a/.github/workflows/reports.yml b/.github/workflows/reports.yml index b18ea731358..2268c48d16c 100644 --- a/.github/workflows/reports.yml +++ b/.github/workflows/reports.yml @@ -116,8 +116,19 @@ jobs: - name: Generate Brillig bytecode size report working-directory: ./test_programs run: | - ./gates_report_brillig.sh - mv gates_report_brillig.json ../gates_report_brillig.json + mkdir ./reports + + ./gates_report_brillig.sh 9223372036854775807 + jq '.programs |= map(.package_name |= (. + "_inliner_max"))' gates_report_brillig.json > ./reports/gates_report_brillig_inliner_max.json + + ./gates_report_brillig.sh 0 + jq '.programs |= map(.package_name |= (. + "_inliner_zero"))' gates_report_brillig.json > ./reports/gates_report_brillig_inliner_zero.json + + ./gates_report_brillig.sh -9223372036854775808 + jq '.programs |= map(.package_name |= (. + "_inliner_min"))' gates_report_brillig.json > ./reports/gates_report_brillig_inliner_min.json + + # Merge all reports + jq -s '{ programs: map(.programs) | add }' ./reports/* > ../gates_report_brillig.json - name: Compare Brillig bytecode size reports id: brillig_bytecode_diff @@ -165,8 +176,19 @@ jobs: - name: Generate Brillig execution report working-directory: ./test_programs run: | - ./gates_report_brillig_execution.sh - mv gates_report_brillig_execution.json ../gates_report_brillig_execution.json + mkdir ./reports + + ./gates_report_brillig_execution.sh 9223372036854775807 + jq '.programs |= map(.package_name |= (. + "_inliner_max"))' gates_report_brillig_execution.json > ./reports/gates_report_brillig_execution_inliner_max.json + + ./gates_report_brillig_execution.sh 0 + jq '.programs |= map(.package_name |= (. + "_inliner_zero"))' gates_report_brillig_execution.json > ./reports/gates_report_brillig_execution_inliner_zero.json + + ./gates_report_brillig_execution.sh -9223372036854775808 + jq '.programs |= map(.package_name |= (. + "_inliner_min"))' gates_report_brillig_execution.json > ./reports/gates_report_brillig_execution_inliner_min.json + + # Merge all reports + jq -s '{ programs: map(.programs) | add }' ./reports/* > ../gates_report_brillig_execution.json - name: Compare Brillig execution reports id: brillig_execution_diff @@ -293,21 +315,24 @@ jobs: external_repo_compilation_and_execution_report: needs: [build-nargo] - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 timeout-minutes: 15 strategy: fail-fast: false matrix: include: - # - project: { repo: AztecProtocol/aztec-packages, path: noir-projects/noir-contracts, is_library: true } - - project: { repo: AztecProtocol/aztec-packages, path: noir-projects/noir-protocol-circuits/crates/private-kernel-inner, take_average: true } - - project: { repo: AztecProtocol/aztec-packages, path: noir-projects/noir-protocol-circuits/crates/private-kernel-tail, take_average: true } - - project: { repo: AztecProtocol/aztec-packages, path: noir-projects/noir-protocol-circuits/crates/private-kernel-reset, take_average: true } - - project: { repo: AztecProtocol/aztec-packages, path: noir-projects/noir-protocol-circuits/crates/rollup-root, take_average: true } - #- project: { repo: AztecProtocol/aztec-packages, path: noir-projects/noir-protocol-circuits/crates/rollup-block-root } - - project: { repo: AztecProtocol/aztec-packages, path: noir-projects/noir-protocol-circuits/crates/rollup-block-merge, take_average: true } - - project: { repo: AztecProtocol/aztec-packages, path: noir-projects/noir-protocol-circuits/crates/rollup-base-private, take_average: true } - - project: { repo: AztecProtocol/aztec-packages, path: noir-projects/noir-protocol-circuits/crates/rollup-base-public, take_average: true } + # - project: { repo: AztecProtocol/aztec-packages, path: noir-projects/noir-contracts, cannot_execute: true } + - project: { repo: AztecProtocol/aztec-packages, path: noir-projects/noir-protocol-circuits/crates/private-kernel-inner, num_runs: 5 } + - project: { repo: AztecProtocol/aztec-packages, path: noir-projects/noir-protocol-circuits/crates/private-kernel-tail, num_runs: 5 } + - project: { repo: AztecProtocol/aztec-packages, path: noir-projects/noir-protocol-circuits/crates/private-kernel-reset, num_runs: 5 } + - project: { repo: AztecProtocol/aztec-packages, path: noir-projects/noir-protocol-circuits/crates/rollup-base-private, num_runs: 5 } + - project: { repo: AztecProtocol/aztec-packages, path: noir-projects/noir-protocol-circuits/crates/rollup-base-public, num_runs: 5 } + - project: { repo: AztecProtocol/aztec-packages, path: noir-projects/noir-protocol-circuits/crates/rollup-block-merge, num_runs: 5 } + - project: { repo: AztecProtocol/aztec-packages, path: noir-projects/noir-protocol-circuits/crates/rollup-block-root-empty, num_runs: 5, cannot_execute: true } + - project: { repo: AztecProtocol/aztec-packages, path: noir-projects/noir-protocol-circuits/crates/rollup-block-root-single-tx, num_runs: 1, flags: "--skip-brillig-constraints-check --skip-underconstrained-check", cannot_execute: true } + - project: { repo: AztecProtocol/aztec-packages, path: noir-projects/noir-protocol-circuits/crates/rollup-block-root, num_runs: 1, flags: "--skip-brillig-constraints-check --skip-underconstrained-check" } + - project: { repo: AztecProtocol/aztec-packages, path: noir-projects/noir-protocol-circuits/crates/rollup-merge, num_runs: 5 } + - project: { repo: AztecProtocol/aztec-packages, path: noir-projects/noir-protocol-circuits/crates/rollup-root, num_runs: 5 } name: External repo compilation and execution reports - ${{ matrix.project.repo }}/${{ matrix.project.path }} steps: @@ -341,41 +366,24 @@ jobs: path: test-repo ref: ${{ matrix.project.ref }} - - name: Generate compilation report without averages - working-directory: ./test-repo/${{ matrix.project.path }} - if: ${{ !matrix.project.take_average }} - run: | - mv /home/runner/work/noir/noir/scripts/test_programs/compilation_report.sh ./compilation_report.sh - touch parse_time.sh - chmod +x parse_time.sh - cp /home/runner/work/noir/noir/scripts/test_programs/parse_time.sh ./parse_time.sh - ./compilation_report.sh 1 - - - name: Generate compilation report with averages + - name: Generate compilation report working-directory: ./test-repo/${{ matrix.project.path }} - if: ${{ matrix.project.take_average }} run: | mv /home/runner/work/noir/noir/scripts/test_programs/compilation_report.sh ./compilation_report.sh touch parse_time.sh chmod +x parse_time.sh cp /home/runner/work/noir/noir/scripts/test_programs/parse_time.sh ./parse_time.sh - ./compilation_report.sh 1 1 - - - name: Generate execution report without averages - working-directory: ./test-repo/${{ matrix.project.path }} - if: ${{ !matrix.project.is_library && !matrix.project.take_average }} - run: | - mv /home/runner/work/noir/noir/scripts/test_programs/execution_report.sh ./execution_report.sh - mv /home/runner/work/noir/noir/scripts/test_programs/parse_time.sh ./parse_time.sh - ./execution_report.sh 1 + ./compilation_report.sh 1 ${{ matrix.project.num_runs }} + env: + FLAGS: ${{ matrix.project.flags }} - - name: Generate execution report with averages + - name: Generate execution report working-directory: ./test-repo/${{ matrix.project.path }} - if: ${{ !matrix.project.is_library && matrix.project.take_average }} + if: ${{ !matrix.project.cannot_execute }} run: | mv /home/runner/work/noir/noir/scripts/test_programs/execution_report.sh ./execution_report.sh mv /home/runner/work/noir/noir/scripts/test_programs/parse_time.sh ./parse_time.sh - ./execution_report.sh 1 1 + ./execution_report.sh 1 ${{ matrix.project.num_runs }} - name: Move compilation report id: compilation_report @@ -389,7 +397,7 @@ jobs: - name: Move execution report id: execution_report shell: bash - if: ${{ !matrix.project.is_library }} + if: ${{ !matrix.project.cannot_execute }} run: | PACKAGE_NAME=${{ matrix.project.path }} PACKAGE_NAME=$(basename $PACKAGE_NAME) @@ -412,53 +420,9 @@ jobs: retention-days: 3 overwrite: true - upload_compilation_report: - name: Upload compilation report - needs: [generate_compilation_and_execution_report, external_repo_compilation_and_execution_report] - # We want this job to run even if one variation of the matrix in `external_repo_compilation_and_execution_report` fails - if: always() - runs-on: ubuntu-latest - permissions: - pull-requests: write - - steps: - - uses: actions/checkout@v4 - - - name: Download initial compilation report - uses: actions/download-artifact@v4 - with: - name: in_progress_compilation_report - - - name: Download matrix compilation reports - uses: actions/download-artifact@v4 - with: - pattern: compilation_report_* - path: ./reports - - - name: Merge compilation reports using jq - run: | - mv ./.github/scripts/merge-bench-reports.sh merge-bench-reports.sh - ./merge-bench-reports.sh compilation_report - - - name: Parse compilation report - id: compilation_report - uses: noir-lang/noir-bench-report@6ba151d7795042c4ff51864fbeb13c0a6a79246c - with: - report: compilation_report.json - header: | - Compilation Report - memory_report: false - - - name: Add memory report to sticky comment - if: github.event_name == 'pull_request' || github.event_name == 'pull_request_target' - uses: marocchino/sticky-pull-request-comment@v2 - with: - header: compilation - message: ${{ steps.compilation_report.outputs.markdown }} - external_repo_memory_report: needs: [build-nargo] - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 timeout-minutes: 30 strategy: fail-fast: false @@ -467,10 +431,16 @@ jobs: # TODO: Bring this report back under a flag. The `noir-contracts` report takes just under 30 minutes. # - project: { repo: AztecProtocol/aztec-packages, path: noir-projects/noir-contracts } - project: { repo: AztecProtocol/aztec-packages, path: noir-projects/noir-protocol-circuits/crates/private-kernel-inner } + - project: { repo: AztecProtocol/aztec-packages, path: noir-projects/noir-protocol-circuits/crates/private-kernel-tail } - project: { repo: AztecProtocol/aztec-packages, path: noir-projects/noir-protocol-circuits/crates/private-kernel-reset } - - project: { repo: AztecProtocol/aztec-packages, path: noir-projects/noir-protocol-circuits/crates/private-kernel-tail } - project: { repo: AztecProtocol/aztec-packages, path: noir-projects/noir-protocol-circuits/crates/rollup-base-private } - project: { repo: AztecProtocol/aztec-packages, path: noir-projects/noir-protocol-circuits/crates/rollup-base-public } + - project: { repo: AztecProtocol/aztec-packages, path: noir-projects/noir-protocol-circuits/crates/rollup-block-merge } + - project: { repo: AztecProtocol/aztec-packages, path: noir-projects/noir-protocol-circuits/crates/rollup-block-root-empty, cannot_execute: true } + - project: { repo: AztecProtocol/aztec-packages, path: noir-projects/noir-protocol-circuits/crates/rollup-block-root-single-tx, flags: "--skip-brillig-constraints-check --skip-underconstrained-check", cannot_execute: true } + - project: { repo: AztecProtocol/aztec-packages, path: noir-projects/noir-protocol-circuits/crates/rollup-block-root, flags: "--skip-brillig-constraints-check --skip-underconstrained-check" } + - project: { repo: AztecProtocol/aztec-packages, path: noir-projects/noir-protocol-circuits/crates/rollup-merge } + - project: { repo: AztecProtocol/aztec-packages, path: noir-projects/noir-protocol-circuits/crates/rollup-root } name: External repo memory report - ${{ matrix.project.repo }}/${{ matrix.project.path }} steps: @@ -493,6 +463,7 @@ jobs: path: scripts sparse-checkout: | test_programs/memory_report.sh + test_programs/parse_memory.sh sparse-checkout-cone-mode: false - name: Checkout @@ -506,12 +477,16 @@ jobs: working-directory: ./test-repo/${{ matrix.project.path }} run: | mv /home/runner/work/noir/noir/scripts/test_programs/memory_report.sh ./memory_report.sh + mv /home/runner/work/noir/noir/scripts/test_programs/parse_memory.sh ./parse_memory.sh ./memory_report.sh 1 # Rename the memory report as the execution report is about to write to the same file cp memory_report.json compilation_memory_report.json + env: + FLAGS: ${{ matrix.project.flags }} - name: Generate execution memory report working-directory: ./test-repo/${{ matrix.project.path }} + if: ${{ !matrix.project.cannot_execute }} run: | ./memory_report.sh 1 1 @@ -534,6 +509,7 @@ jobs: - name: Move execution report id: execution_mem_report + if: ${{ !matrix.project.cannot_execute }} shell: bash run: | PACKAGE_NAME=${{ matrix.project.path }} @@ -549,14 +525,67 @@ jobs: retention-days: 3 overwrite: true + upload_compilation_report: + name: Upload compilation report + needs: [generate_compilation_and_execution_report, external_repo_compilation_and_execution_report] + # We want this job to run even if one variation of the matrix in `external_repo_compilation_and_execution_report` fails + if: always() + runs-on: ubuntu-22.04 + permissions: + pull-requests: write + # deployments permission to deploy GitHub pages website + deployments: write + # contents permission to update benchmark contents in gh-pages branch + contents: write + + steps: + - uses: actions/checkout@v4 + + - name: Download initial compilation report + uses: actions/download-artifact@v4 + with: + name: in_progress_compilation_report + + - name: Download matrix compilation reports + uses: actions/download-artifact@v4 + with: + pattern: compilation_report_* + path: ./reports + + - name: Merge compilation reports using jq + run: | + mv ./.github/scripts/merge-bench-reports.sh merge-bench-reports.sh + ./merge-bench-reports.sh compilation_report + jq ".compilation_reports | map({name: .artifact_name, value: (.time[:-1] | tonumber), unit: \"s\"}) " ./compilation_report.json > time_bench.json + + - name: Store benchmark result + continue-on-error: true + uses: benchmark-action/github-action-benchmark@4de1bed97a47495fc4c5404952da0499e31f5c29 + with: + name: "Compilation Time" + tool: "customSmallerIsBetter" + output-file-path: ./time_bench.json + github-token: ${{ secrets.GITHUB_TOKEN }} + # We want this to only run on master to avoid garbage data from PRs being added. + auto-push: ${{ github.ref == 'refs/heads/master' }} + alert-threshold: "120%" + comment-on-alert: true + fail-on-alert: false + alert-comment-cc-users: "@TomAFrench" + max-items-in-chart: 50 + upload_compilation_memory_report: name: Upload compilation memory report needs: [generate_memory_report, external_repo_memory_report] # We want this job to run even if one variation of the matrix in `external_repo_memory_report` fails if: always() - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 permissions: pull-requests: write + # deployments permission to deploy GitHub pages website + deployments: write + # contents permission to update benchmark contents in gh-pages branch + contents: write steps: - uses: actions/checkout@v4 @@ -576,31 +605,36 @@ jobs: run: | mv ./.github/scripts/merge-bench-reports.sh merge-bench-reports.sh ./merge-bench-reports.sh memory_report - - - name: Parse compilation memory report - id: compilation_mem_report - uses: noir-lang/noir-bench-report@6ba151d7795042c4ff51864fbeb13c0a6a79246c - with: - report: memory_report.json - header: | - Compilation Memory Report - memory_report: true - - - name: Add memory report to sticky comment - if: github.event_name == 'pull_request' || github.event_name == 'pull_request_target' - uses: marocchino/sticky-pull-request-comment@v2 - with: - header: compilation_memory - message: ${{ steps.compilation_mem_report.outputs.markdown }} + jq ".memory_reports | map({name: .artifact_name, value: (.peak_memory | tonumber), unit: \"MB\"}) " ./memory_report.json > memory_bench.json + + - name: Store benchmark result + continue-on-error: true + uses: benchmark-action/github-action-benchmark@4de1bed97a47495fc4c5404952da0499e31f5c29 + with: + name: "Compilation Memory" + tool: "customSmallerIsBetter" + output-file-path: ./memory_bench.json + github-token: ${{ secrets.GITHUB_TOKEN }} + # We want this to only run on master to avoid garbage data from PRs being added. + auto-push: ${{ github.ref == 'refs/heads/master' }} + alert-threshold: "120%" + comment-on-alert: true + fail-on-alert: false + alert-comment-cc-users: "@TomAFrench" + max-items-in-chart: 50 upload_execution_memory_report: name: Upload execution memory report needs: [generate_memory_report, external_repo_memory_report] # We want this job to run even if one variation of the matrix in `external_repo_memory_report` fails if: always() - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 permissions: pull-requests: write + # deployments permission to deploy GitHub pages website + deployments: write + # contents permission to update benchmark contents in gh-pages branch + contents: write steps: - uses: actions/checkout@v4 @@ -622,31 +656,37 @@ jobs: ./merge-bench-reports.sh memory_report # Rename the memory report as to not clash with the compilation memory report file name cp memory_report.json execution_memory_report.json + jq ".memory_reports | map({name: .artifact_name, value: (.peak_memory | tonumber), unit: \"MB\"}) " ./execution_memory_report.json > memory_bench.json - - name: Parse execution memory report - id: execution_mem_report - uses: noir-lang/noir-bench-report@6ba151d7795042c4ff51864fbeb13c0a6a79246c + - name: Store benchmark result + continue-on-error: true + uses: benchmark-action/github-action-benchmark@4de1bed97a47495fc4c5404952da0499e31f5c29 with: - report: execution_memory_report.json - header: | - Execution Memory Report - memory_report: true + name: "Execution Memory" + tool: "customSmallerIsBetter" + output-file-path: ./memory_bench.json + github-token: ${{ secrets.GITHUB_TOKEN }} + # We want this to only run on master to avoid garbage data from PRs being added. + auto-push: ${{ github.ref == 'refs/heads/master' }} + alert-threshold: "120%" + comment-on-alert: true + fail-on-alert: false + alert-comment-cc-users: "@TomAFrench" + max-items-in-chart: 50 - - name: Add execution memory report to sticky comment - if: github.event_name == 'pull_request' || github.event_name == 'pull_request_target' - uses: marocchino/sticky-pull-request-comment@v2 - with: - header: execution_memory - message: ${{ steps.execution_mem_report.outputs.markdown }} upload_execution_report: name: Upload execution report needs: [generate_compilation_and_execution_report, external_repo_compilation_and_execution_report] # We want this job to run even if one variation of the matrix in `external_repo_compilation_and_execution_report` fails if: always() - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 permissions: pull-requests: write + # deployments permission to deploy GitHub pages website + deployments: write + # contents permission to update benchmark contents in gh-pages branch + contents: write steps: - uses: actions/checkout@v4 @@ -666,20 +706,20 @@ jobs: run: | mv ./.github/scripts/merge-bench-reports.sh merge-bench-reports.sh ./merge-bench-reports.sh execution_report - - - name: Parse execution report - id: execution_report - uses: noir-lang/noir-bench-report@6ba151d7795042c4ff51864fbeb13c0a6a79246c - with: - report: execution_report.json - header: | - Execution Report - execution_report: true - - - name: Add memory report to sticky comment - if: github.event_name == 'pull_request' || github.event_name == 'pull_request_target' - uses: marocchino/sticky-pull-request-comment@v2 - with: - header: execution_time - message: ${{ steps.execution_report.outputs.markdown }} - + jq ".execution_reports | map({name: .artifact_name, value: (.time[:-1] | tonumber), unit: \"s\"}) " ./execution_report.json > time_bench.json + + - name: Store benchmark result + continue-on-error: true + uses: benchmark-action/github-action-benchmark@4de1bed97a47495fc4c5404952da0499e31f5c29 + with: + name: "Execution Time" + tool: "customSmallerIsBetter" + output-file-path: ./time_bench.json + github-token: ${{ secrets.GITHUB_TOKEN }} + # We want this to only run on master to avoid garbage data from PRs being added. + auto-push: ${{ github.ref == 'refs/heads/master' }} + alert-threshold: "120%" + comment-on-alert: true + fail-on-alert: false + alert-comment-cc-users: "@TomAFrench" + max-items-in-chart: 50 diff --git a/CRITICAL_NOIR_LIBRARIES b/CRITICAL_NOIR_LIBRARIES index c753b76a4fc..7637d9ac6df 100644 --- a/CRITICAL_NOIR_LIBRARIES +++ b/CRITICAL_NOIR_LIBRARIES @@ -1,3 +1,4 @@ +https://github.com/noir-lang/noir_check_shuffle https://github.com/noir-lang/ec https://github.com/noir-lang/eddsa https://github.com/noir-lang/mimc diff --git a/Cargo.lock b/Cargo.lock index 4d3e2cbd86d..e82d47d690a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -644,6 +644,12 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "binary-merge" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597bb81c80a54b6a4381b23faba8d7774b144c94cbd1d6fe3f1329bd776554ab" + [[package]] name = "bincode" version = "1.3.3" @@ -1764,9 +1770,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "foldhash" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" [[package]] name = "form_urlencoded" @@ -2519,6 +2525,15 @@ dependencies = [ "libc", ] +[[package]] +name = "inplace-vec-builder" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf64c2edc8226891a71f127587a2861b132d2b942310843814d5001d99a1d307" +dependencies = [ + "smallvec", +] + [[package]] name = "is-terminal" version = "0.4.13" @@ -3366,6 +3381,7 @@ dependencies = [ "thiserror", "tracing", "tracing-test", + "vec-collections", ] [[package]] @@ -4807,6 +4823,12 @@ dependencies = [ "sha1", ] +[[package]] +name = "sorted-iter" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bceb57dc07c92cdae60f5b27b3fa92ecaaa42fe36c55e22dbfb0b44893e0b1f7" + [[package]] name = "spin" version = "0.9.8" @@ -5497,6 +5519,21 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +[[package]] +name = "vec-collections" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c9965c8f2ffed1dbcd16cafe18a009642f540fa22661c6cfd6309ddb02e4982" +dependencies = [ + "binary-merge", + "inplace-vec-builder", + "lazy_static", + "num-traits", + "serde", + "smallvec", + "sorted-iter", +] + [[package]] name = "version_check" version = "0.9.5" diff --git a/acvm-repo/acvm/src/compiler/mod.rs b/acvm-repo/acvm/src/compiler/mod.rs index daedd77c4a0..ee84f7bf60b 100644 --- a/acvm-repo/acvm/src/compiler/mod.rs +++ b/acvm-repo/acvm/src/compiler/mod.rs @@ -16,10 +16,6 @@ pub use simulator::CircuitSimulator; use transformers::transform_internal; pub use transformers::{transform, MIN_EXPRESSION_WIDTH}; -/// We need multiple passes to stabilize the output. -/// The value was determined by running tests. -const MAX_OPTIMIZER_PASSES: usize = 3; - /// This module moves and decomposes acir opcodes. The transformation map allows consumers of this module to map /// metadata they had about the opcodes to the new opcode structure generated after the transformation. #[derive(Debug)] @@ -84,41 +80,10 @@ pub fn compile( ) -> (Circuit, AcirTransformationMap) { let acir_opcode_positions = (0..acir.opcodes.len()).collect::>(); - if MAX_OPTIMIZER_PASSES == 0 { - return (acir, AcirTransformationMap::new(&acir_opcode_positions)); - } - - let mut pass = 0; - let mut prev_opcodes_hash = fxhash::hash64(&acir.opcodes); - let mut prev_acir = acir; - let mut prev_acir_opcode_positions = acir_opcode_positions; - - // For most test programs it would be enough to only loop `transform_internal`, - // but some of them don't stabilize unless we also repeat the backend agnostic optimizations. - let (mut acir, acir_opcode_positions) = loop { - let (acir, acir_opcode_positions) = - optimize_internal(prev_acir, prev_acir_opcode_positions); - - // Stop if we have already done at least one transform and an extra optimization changed nothing. - if pass > 0 && prev_opcodes_hash == fxhash::hash64(&acir.opcodes) { - break (acir, acir_opcode_positions); - } - - let (acir, acir_opcode_positions) = - transform_internal(acir, expression_width, acir_opcode_positions); - - let opcodes_hash = fxhash::hash64(&acir.opcodes); - - // Stop if the output hasn't change in this loop or we went too long. - if pass == MAX_OPTIMIZER_PASSES - 1 || prev_opcodes_hash == opcodes_hash { - break (acir, acir_opcode_positions); - } + let (acir, acir_opcode_positions) = optimize_internal(acir, acir_opcode_positions); - pass += 1; - prev_acir = acir; - prev_opcodes_hash = opcodes_hash; - prev_acir_opcode_positions = acir_opcode_positions; - }; + let (mut acir, acir_opcode_positions) = + transform_internal(acir, expression_width, acir_opcode_positions); let transformation_map = AcirTransformationMap::new(&acir_opcode_positions); acir.assert_messages = transform_assert_messages(acir.assert_messages, &transformation_map); diff --git a/acvm-repo/acvm/src/compiler/optimizers/unused_memory.rs b/acvm-repo/acvm/src/compiler/optimizers/unused_memory.rs index 1963430210f..1325a3b03cd 100644 --- a/acvm-repo/acvm/src/compiler/optimizers/unused_memory.rs +++ b/acvm-repo/acvm/src/compiler/optimizers/unused_memory.rs @@ -1,4 +1,4 @@ -use acir::circuit::{opcodes::BlockId, Circuit, Opcode}; +use acir::circuit::{brillig::BrilligInputs, opcodes::BlockId, Circuit, Opcode}; use std::collections::HashSet; /// `UnusedMemoryOptimizer` will remove initializations of memory blocks which are unused. @@ -29,6 +29,13 @@ impl UnusedMemoryOptimizer { Opcode::MemoryOp { block_id, .. } => { unused_memory_initialization.remove(block_id); } + Opcode::BrilligCall { inputs, .. } => { + for input in inputs { + if let BrilligInputs::MemoryArray(block) = input { + unused_memory_initialization.remove(block); + } + } + } _ => (), } } diff --git a/acvm-repo/acvm/src/compiler/transformers/mod.rs b/acvm-repo/acvm/src/compiler/transformers/mod.rs index a499aec1b30..77a5d2da34e 100644 --- a/acvm-repo/acvm/src/compiler/transformers/mod.rs +++ b/acvm-repo/acvm/src/compiler/transformers/mod.rs @@ -14,12 +14,17 @@ mod csat; pub(crate) use csat::CSatTransformer; pub use csat::MIN_EXPRESSION_WIDTH; +use tracing::info; use super::{ - optimizers::MergeExpressionsOptimizer, transform_assert_messages, AcirTransformationMap, - MAX_OPTIMIZER_PASSES, + optimizers::{MergeExpressionsOptimizer, RangeOptimizer}, + transform_assert_messages, AcirTransformationMap, }; +/// We need multiple passes to stabilize the output. +/// The value was determined by running tests. +const MAX_TRANSFORMER_PASSES: usize = 3; + /// Applies [`ProofSystemCompiler`][crate::ProofSystemCompiler] specific optimizations to a [`Circuit`]. pub fn transform( acir: Circuit, @@ -50,12 +55,18 @@ pub(super) fn transform_internal( expression_width: ExpressionWidth, mut acir_opcode_positions: Vec, ) -> (Circuit, Vec) { + if acir.opcodes.len() == 1 && matches!(acir.opcodes[0], Opcode::BrilligCall { .. }) { + info!("Program is fully unconstrained, skipping transformation pass"); + return (acir, acir_opcode_positions); + } + // Allow multiple passes until we have stable output. let mut prev_opcodes_hash = fxhash::hash64(&acir.opcodes); // For most test programs it would be enough to loop here, but some of them // don't stabilize unless we also repeat the backend agnostic optimizations. - for _ in 0..MAX_OPTIMIZER_PASSES { + for _ in 0..MAX_TRANSFORMER_PASSES { + info!("Number of opcodes {}", acir.opcodes.len()); let (new_acir, new_acir_opcode_positions) = transform_internal_once(acir, expression_width, acir_opcode_positions); @@ -217,6 +228,12 @@ fn transform_internal_once( ..acir }; + // The `MergeOptimizer` can merge two witnesses which have range opcodes applied to them + // so we run the `RangeOptimizer` afterwards to clear these up. + let range_optimizer = RangeOptimizer::new(acir); + let (acir, new_acir_opcode_positions) = + range_optimizer.replace_redundant_ranges(new_acir_opcode_positions); + (acir, new_acir_opcode_positions) } diff --git a/compiler/noirc_driver/src/lib.rs b/compiler/noirc_driver/src/lib.rs index 87da9cd658e..a7e7e2d4e2f 100644 --- a/compiler/noirc_driver/src/lib.rs +++ b/compiler/noirc_driver/src/lib.rs @@ -73,7 +73,11 @@ pub struct CompileOptions { /// Only show SSA passes whose name contains the provided string. /// This setting takes precedence over `show_ssa` if it's not empty. #[arg(long, hide = true)] - pub show_ssa_pass_name: Option, + pub show_ssa_pass: Option, + + /// Only show the SSA and ACIR for the contract function with a given name. + #[arg(long, hide = true)] + pub show_contract_fn: Option, /// Emit the unoptimized SSA IR to file. /// The IR will be dumped into the workspace target directory, @@ -442,6 +446,11 @@ pub fn compile_contract( if options.print_acir { for contract_function in &compiled_contract.functions { + if let Some(ref name) = options.show_contract_fn { + if name != &contract_function.name { + continue; + } + } println!( "Compiled ACIR for {}::{} (unoptimized):", compiled_contract.name, contract_function.name @@ -486,7 +495,15 @@ fn compile_contract_inner( continue; } - let function = match compile_no_check(context, options, function_id, None, true) { + let mut options = options.clone(); + + if let Some(ref name_filter) = options.show_contract_fn { + let show = name == *name_filter; + options.show_ssa &= show; + options.show_ssa_pass = options.show_ssa_pass.filter(|_| show); + }; + + let function = match compile_no_check(context, &options, function_id, None, true) { Ok(function) => function, Err(new_error) => { errors.push(FileDiagnostic::from(new_error)); @@ -642,7 +659,7 @@ pub fn compile_no_check( let return_visibility = program.return_visibility; let ssa_evaluator_options = noirc_evaluator::ssa::SsaEvaluatorOptions { - ssa_logging: match &options.show_ssa_pass_name { + ssa_logging: match &options.show_ssa_pass { Some(string) => SsaLogging::Contains(string.clone()), None => { if options.show_ssa { diff --git a/compiler/noirc_evaluator/Cargo.toml b/compiler/noirc_evaluator/Cargo.toml index 15531fafff7..3e30fa6673d 100644 --- a/compiler/noirc_evaluator/Cargo.toml +++ b/compiler/noirc_evaluator/Cargo.toml @@ -29,6 +29,7 @@ chrono = "0.4.37" rayon.workspace = true cfg-if.workspace = true smallvec = { version = "1.13.2", features = ["serde"] } +vec-collections = "0.4.3" [dev-dependencies] proptest.workspace = true diff --git a/compiler/noirc_evaluator/src/acir/acir_variable.rs b/compiler/noirc_evaluator/src/acir/acir_variable.rs index cf6b1fcc7f7..41e2c2dad1e 100644 --- a/compiler/noirc_evaluator/src/acir/acir_variable.rs +++ b/compiler/noirc_evaluator/src/acir/acir_variable.rs @@ -541,6 +541,29 @@ impl> AcirContext { Ok(()) } + /// Constrains the `lhs` and `rhs` to be non-equal. + /// + /// This is done by asserting the existence of an inverse for the value `lhs - rhs`. + /// The constraint `(lhs - rhs) * inverse == 1` will only be satisfiable if `lhs` and `rhs` are non-equal. + pub(crate) fn assert_neq_var( + &mut self, + lhs: AcirVar, + rhs: AcirVar, + assert_message: Option>, + ) -> Result<(), RuntimeError> { + let diff_var = self.sub_var(lhs, rhs)?; + + let one = self.add_constant(F::one()); + let _ = self.inv_var(diff_var, one)?; + if let Some(payload) = assert_message { + self.acir_ir + .assertion_payloads + .insert(self.acir_ir.last_acir_opcode_location(), payload); + } + + Ok(()) + } + pub(crate) fn vars_to_expressions_or_memory( &self, values: &[AcirValue], diff --git a/compiler/noirc_evaluator/src/acir/generated_acir.rs b/compiler/noirc_evaluator/src/acir/generated_acir.rs index a2b161688c0..14ceac62461 100644 --- a/compiler/noirc_evaluator/src/acir/generated_acir.rs +++ b/compiler/noirc_evaluator/src/acir/generated_acir.rs @@ -479,7 +479,7 @@ impl GeneratedAcir { pub(crate) fn is_equal(&mut self, lhs: &Expression, rhs: &Expression) -> Witness { let t = lhs - rhs; - self.is_zero(&t) + self.is_zero(t) } /// Returns a `Witness` that is constrained to be: @@ -534,36 +534,32 @@ impl GeneratedAcir { /// By setting `z` to be `0`, we can make `y` equal to `1`. /// This is easily observed: `y = 1 - t * 0` /// Now since `y` is one, this means that `t` needs to be zero, or else `y * t == 0` will fail. - fn is_zero(&mut self, t_expr: &Expression) -> Witness { - // We're checking for equality with zero so we can negate the expression without changing the result. - // This is useful as it will sometimes allow us to simplify an expression down to a witness. - let t_witness = if let Some(witness) = t_expr.to_witness() { - witness + fn is_zero(&mut self, t_expr: Expression) -> Witness { + // We're going to be multiplying this expression by two different witnesses in a second so we want to + // ensure that this expression only contains a single witness. We can tolerate coefficients and constant terms however. + let linear_t = if t_expr.is_degree_one_univariate() { + t_expr } else { - let negated_expr = t_expr * -F::one(); - self.get_or_create_witness(&negated_expr) + Expression::::from(self.get_or_create_witness(&t_expr)) }; // Call the inversion directive, since we do not apply a constraint // the prover can choose anything here. - let z = self.brillig_inverse(t_witness.into()); + let z = self.brillig_inverse(linear_t.clone()); + let z_expr = Expression::::from(z); let y = self.next_witness_index(); + let y_expr = Expression::::from(y); // Add constraint y == 1 - tz => y + tz - 1 == 0 - let y_is_boolean_constraint = Expression { - mul_terms: vec![(F::one(), t_witness, z)], - linear_combinations: vec![(F::one(), y)], - q_c: -F::one(), - }; + let mut y_is_boolean_constraint = + (&z_expr * &linear_t).expect("multiplying two linear expressions"); + y_is_boolean_constraint.push_addition_term(F::one(), y); + let y_is_boolean_constraint = y_is_boolean_constraint - F::one(); self.assert_is_zero(y_is_boolean_constraint); // Add constraint that y * t == 0; - let ty_zero_constraint = Expression { - mul_terms: vec![(F::one(), t_witness, y)], - linear_combinations: vec![], - q_c: F::zero(), - }; + let ty_zero_constraint = (&y_expr * &linear_t).expect("multiplying two linear expressions"); self.assert_is_zero(ty_zero_constraint); y diff --git a/compiler/noirc_evaluator/src/acir/mod.rs b/compiler/noirc_evaluator/src/acir/mod.rs index e57f5d18830..a3c44e055b4 100644 --- a/compiler/noirc_evaluator/src/acir/mod.rs +++ b/compiler/noirc_evaluator/src/acir/mod.rs @@ -723,6 +723,47 @@ impl<'a> Context<'a> { self.acir_context.assert_eq_var(lhs, rhs, assert_payload)?; } + Instruction::ConstrainNotEqual(lhs, rhs, assert_message) => { + let lhs = self.convert_numeric_value(*lhs, dfg)?; + let rhs = self.convert_numeric_value(*rhs, dfg)?; + + let assert_payload = if let Some(error) = assert_message { + match error { + ConstrainError::StaticString(string) => Some( + self.acir_context.generate_assertion_message_payload(string.clone()), + ), + ConstrainError::Dynamic(error_selector, is_string_type, values) => { + if let Some(constant_string) = try_to_extract_string_from_error_payload( + *is_string_type, + values, + dfg, + ) { + Some( + self.acir_context + .generate_assertion_message_payload(constant_string), + ) + } else { + let acir_vars: Vec<_> = values + .iter() + .map(|value| self.convert_value(*value, dfg)) + .collect(); + + let expressions_or_memory = + self.acir_context.vars_to_expressions_or_memory(&acir_vars)?; + + Some(AssertionPayload { + error_selector: error_selector.as_u64(), + payload: expressions_or_memory, + }) + } + } + } + } else { + None + }; + + self.acir_context.assert_neq_var(lhs, rhs, assert_payload)?; + } Instruction::Cast(value_id, _) => { let acir_var = self.convert_numeric_value(*value_id, dfg)?; self.define_result_var(dfg, instruction_id, acir_var); @@ -1028,8 +1069,7 @@ impl<'a> Context<'a> { // Ensure that array id is fully resolved. let array = dfg.resolve(array); - let array_id = dfg.resolve(array); - let array_typ = dfg.type_of_value(array_id); + let array_typ = dfg.type_of_value(array); // Compiler sanity checks assert!(!array_typ.is_nested_slice(), "ICE: Nested slice type has reached ACIR generation"); let (Type::Array(_, _) | Type::Slice(_)) = &array_typ else { @@ -1084,15 +1124,7 @@ impl<'a> Context<'a> { index: ValueId, store_value: Option, ) -> Result { - let array_id = dfg.resolve(array); - let array_typ = dfg.type_of_value(array_id); - // Compiler sanity checks - assert!(!array_typ.is_nested_slice(), "ICE: Nested slice type has reached ACIR generation"); - let (Type::Array(_, _) | Type::Slice(_)) = &array_typ else { - unreachable!("ICE: expected array or slice type"); - }; - - match self.convert_value(array_id, dfg) { + match self.convert_value(array, dfg) { AcirValue::Var(acir_var, _) => { Err(RuntimeError::InternalError(InternalError::Unexpected { expected: "an array value".to_string(), @@ -1892,6 +1924,9 @@ impl<'a> Context<'a> { Value::Instruction { .. } | Value::Param { .. } => { unreachable!("ICE: Should have been in cache {value_id} {value:?}") } + Value::Global(_) => { + unreachable!("ICE: All globals should have been inlined"); + } }; self.ssa_values.insert(value_id, acir_value.clone()); acir_value @@ -1949,9 +1984,9 @@ impl<'a> Context<'a> { let bit_count = binary_type.bit_size::(); let num_type = binary_type.to_numeric_type(); let result = match binary.operator { - BinaryOp::Add => self.acir_context.add_var(lhs, rhs), - BinaryOp::Sub => self.acir_context.sub_var(lhs, rhs), - BinaryOp::Mul => self.acir_context.mul_var(lhs, rhs), + BinaryOp::Add { .. } => self.acir_context.add_var(lhs, rhs), + BinaryOp::Sub { .. } => self.acir_context.sub_var(lhs, rhs), + BinaryOp::Mul { .. } => self.acir_context.mul_var(lhs, rhs), BinaryOp::Div => self.acir_context.div_var( lhs, rhs, @@ -2070,7 +2105,7 @@ impl<'a> Context<'a> { Value::Instruction { instruction, .. } => { if matches!( &dfg[*instruction], - Instruction::Binary(Binary { operator: BinaryOp::Sub, .. }) + Instruction::Binary(Binary { operator: BinaryOp::Sub { .. }, .. }) ) { // Subtractions must first have the integer modulus added before truncation can be // applied. This is done in order to prevent underflow. @@ -2187,45 +2222,41 @@ impl<'a> Context<'a> { Intrinsic::AsSlice => { let slice_contents = arguments[0]; let slice_typ = dfg.type_of_value(slice_contents); - let block_id = self.ensure_array_is_initialized(slice_contents, dfg)?; assert!(!slice_typ.is_nested_slice(), "ICE: Nested slice used in ACIR generation"); - let result_block_id = self.block_id(&result_ids[1]); let acir_value = self.convert_value(slice_contents, dfg); + let (slice_length, result) = match acir_value { + AcirValue::Var(_, _) => { + unreachable!("ICE: cannot call `as_slice` on non-array type") + } + array @ AcirValue::Array(_) => { + let array_len = if !slice_typ.contains_slice_element() { + slice_typ.flattened_size() as usize + } else { + self.flattened_slice_size(slice_contents, dfg) + }; + (array_len, array) + } + AcirValue::DynamicArray(source_array) => { + let result_block_id = self.block_id(&result_ids[1]); + self.copy_dynamic_array( + source_array.block_id, + result_block_id, + source_array.len, + )?; - let array_len = if !slice_typ.contains_slice_element() { - slice_typ.flattened_size() as usize - } else { - self.flattened_slice_size(slice_contents, dfg) - }; - let slice_length = self.acir_context.add_constant(array_len); - self.copy_dynamic_array(block_id, result_block_id, array_len)?; + let array = AcirValue::DynamicArray(AcirDynamicArray { + block_id: result_block_id, + len: source_array.len, + value_types: source_array.value_types, + element_type_sizes: source_array.element_type_sizes, + }); - let element_type_sizes = if !can_omit_element_sizes_array(&slice_typ) { - Some(self.init_element_type_sizes_array( - &slice_typ, - slice_contents, - Some(&acir_value), - dfg, - )?) - } else { - None + (source_array.len, array) + } }; - let value_types = self.convert_value(slice_contents, dfg).flat_numeric_types(); - assert!( - array_len == value_types.len(), - "AsSlice: unexpected length difference: {:?} != {:?}", - array_len, - value_types.len() - ); - - let result = AcirValue::DynamicArray(AcirDynamicArray { - block_id: result_block_id, - len: value_types.len(), - value_types, - element_type_sizes, - }); + let slice_length = self.acir_context.add_constant(slice_length); Ok(vec![AcirValue::Var(slice_length, AcirType::field()), result]) } Intrinsic::SlicePushBack => { @@ -2865,7 +2896,7 @@ mod test { use std::collections::BTreeMap; use crate::{ - acir::BrilligStdlibFunc, + acir::{BrilligStdlibFunc, Function}, brillig::Brillig, ssa::{ function_builder::FunctionBuilder, @@ -3159,7 +3190,11 @@ mod test { let func_with_nested_call_v1 = builder.add_parameter(Type::field()); let two = builder.field_constant(2u128); - let v0_plus_two = builder.insert_binary(func_with_nested_call_v0, BinaryOp::Add, two); + let v0_plus_two = builder.insert_binary( + func_with_nested_call_v0, + BinaryOp::Add { unchecked: false }, + two, + ); let foo_id = Id::test_new(2); let foo_call = builder.import_function(foo_id); @@ -3293,7 +3328,8 @@ mod test { build_basic_foo_with_return(&mut builder, foo_id, true, InlineType::default()); build_basic_foo_with_return(&mut builder, bar_id, true, InlineType::default()); - let ssa = builder.finish(); + let mut ssa = builder.finish(); + ssa.globals = Function::new("globals".to_owned(), ssa.main_id); let brillig = ssa.to_brillig(false); let (acir_functions, brillig_functions, _, _) = ssa @@ -3431,7 +3467,8 @@ mod test { build_basic_foo_with_return(&mut builder, foo_id, true, InlineType::default()); - let ssa = builder.finish(); + let mut ssa = builder.finish(); + ssa.globals = Function::new("globals".to_owned(), ssa.main_id); // We need to generate Brillig artifacts for the regular Brillig function and pass them to the ACIR generation pass. let brillig = ssa.to_brillig(false); println!("{}", ssa); @@ -3520,7 +3557,8 @@ mod test { // Build an ACIR function which has the same logic as the Brillig function above build_basic_foo_with_return(&mut builder, bar_id, false, InlineType::Fold); - let ssa = builder.finish(); + let mut ssa = builder.finish(); + ssa.globals = Function::new("globals".to_owned(), ssa.main_id); // We need to generate Brillig artifacts for the regular Brillig function and pass them to the ACIR generation pass. let brillig = ssa.to_brillig(false); println!("{}", ssa); @@ -3658,4 +3696,35 @@ mod test { } } } + + #[test] + fn does_not_generate_memory_blocks_without_dynamic_accesses() { + let src = " + acir(inline) fn main f0 { + b0(v0: [Field; 2]): + v2, v3 = call as_slice(v0) -> (u32, [Field]) + call f1(u32 2, v3) + v7 = array_get v0, index u32 0 -> Field + constrain v7 == Field 0 + return + } + + brillig(inline) fn foo f1 { + b0(v0: u32, v1: [Field]): + return + } + "; + let ssa = Ssa::from_str(src).unwrap(); + let brillig = ssa.to_brillig(false); + + let (acir_functions, _brillig_functions, _, _) = ssa + .into_acir(&brillig, ExpressionWidth::default()) + .expect("Should compile manually written SSA into ACIR"); + + assert_eq!(acir_functions.len(), 1); + + // Check that no memory opcodes were emitted. + let main = &acir_functions[0]; + assert!(!main.opcodes().iter().any(|opcode| matches!(opcode, Opcode::MemoryOp { .. }))); + } } diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen.rs index 5a81c79ae0d..b51a3445a1b 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen.rs @@ -2,11 +2,13 @@ pub(crate) mod brillig_black_box; pub(crate) mod brillig_block; pub(crate) mod brillig_block_variables; pub(crate) mod brillig_fn; +pub(crate) mod brillig_globals; pub(crate) mod brillig_slice_ops; mod constant_allocation; mod variable_liveness; use acvm::FieldElement; +use fxhash::FxHashMap as HashMap; use self::{brillig_block::BrilligBlock, brillig_fn::FunctionContext}; use super::{ @@ -14,7 +16,7 @@ use super::{ artifact::{BrilligArtifact, BrilligParameter, GeneratedBrillig, Label}, BrilligContext, }, - Brillig, + Brillig, BrilligVariable, ValueId, }; use crate::{ errors::InternalError, @@ -25,6 +27,7 @@ use crate::{ pub(crate) fn convert_ssa_function( func: &Function, enable_debug_trace: bool, + globals: &HashMap, ) -> BrilligArtifact { let mut brillig_context = BrilligContext::new(enable_debug_trace); @@ -35,7 +38,13 @@ pub(crate) fn convert_ssa_function( brillig_context.call_check_max_stack_depth_procedure(); for block in function_context.blocks.clone() { - BrilligBlock::compile(&mut function_context, &mut brillig_context, block, &func.dfg); + BrilligBlock::compile( + &mut function_context, + &mut brillig_context, + block, + &func.dfg, + globals, + ); } let mut artifact = brillig_context.artifact(); @@ -53,6 +62,7 @@ pub(crate) fn gen_brillig_for( arguments, FunctionContext::return_values(func), func.id(), + true, ); entry_point.name = func.name().to_string(); 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 31c99bf433e..698d4cd05be 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -3,7 +3,7 @@ use crate::brillig::brillig_ir::brillig_variable::{ type_to_heap_value_type, BrilligArray, BrilligVariable, SingleAddrVariable, }; -use crate::brillig::brillig_ir::registers::Stack; +use crate::brillig::brillig_ir::registers::RegisterAllocator; use crate::brillig::brillig_ir::{ BrilligBinaryOp, BrilligContext, ReservedRegisters, BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, }; @@ -32,28 +32,41 @@ use super::brillig_fn::FunctionContext; use super::constant_allocation::InstructionLocation; /// Generate the compilation artifacts for compiling a function into brillig bytecode. -pub(crate) struct BrilligBlock<'block> { +pub(crate) struct BrilligBlock<'block, Registers: RegisterAllocator> { pub(crate) function_context: &'block mut FunctionContext, /// The basic block that is being converted pub(crate) block_id: BasicBlockId, /// Context for creating brillig opcodes - pub(crate) brillig_context: &'block mut BrilligContext, + pub(crate) brillig_context: &'block mut BrilligContext, /// Tracks the available variable during the codegen of the block pub(crate) variables: BlockVariables, /// For each instruction, the set of values that are not used anymore after it. pub(crate) last_uses: HashMap>, + + pub(crate) globals: &'block HashMap, + + pub(crate) building_globals: bool, } -impl<'block> BrilligBlock<'block> { +impl<'block, Registers: RegisterAllocator> BrilligBlock<'block, Registers> { /// Converts an SSA Basic block into a sequence of Brillig opcodes pub(crate) fn compile( function_context: &'block mut FunctionContext, - brillig_context: &'block mut BrilligContext, + brillig_context: &'block mut BrilligContext, block_id: BasicBlockId, dfg: &DataFlowGraph, + globals: &'block HashMap, ) { let live_in = function_context.liveness.get_live_in(&block_id); - let variables = BlockVariables::new(live_in.clone()); + + let mut live_in_no_globals = HashSet::default(); + for value in live_in { + if !dfg.is_global(*value) { + live_in_no_globals.insert(*value); + } + } + + let variables = BlockVariables::new(live_in_no_globals); brillig_context.set_allocated_registers( variables @@ -64,12 +77,44 @@ impl<'block> BrilligBlock<'block> { ); let last_uses = function_context.liveness.get_last_uses(&block_id).clone(); - let mut brillig_block = - BrilligBlock { function_context, block_id, brillig_context, variables, last_uses }; + let mut brillig_block = BrilligBlock { + function_context, + block_id, + brillig_context, + variables, + last_uses, + globals, + building_globals: false, + }; brillig_block.convert_block(dfg); } + pub(crate) fn compile_globals( + &mut self, + globals: &DataFlowGraph, + used_globals: &HashSet, + ) { + for (id, value) in globals.values_iter() { + if !used_globals.contains(&id) { + continue; + } + match value { + Value::NumericConstant { .. } => { + self.convert_ssa_value(id, globals); + } + Value::Instruction { instruction, .. } => { + self.convert_ssa_instruction(*instruction, globals); + } + _ => { + panic!( + "Expected either an instruction or a numeric constant for a global value" + ) + } + } + } + } + fn convert_block(&mut self, dfg: &DataFlowGraph) { // Add a label for this block let block_label = self.create_block_label_for_current_function(self.block_id); @@ -199,7 +244,11 @@ impl<'block> BrilligBlock<'block> { } /// Converts an SSA instruction into a sequence of Brillig opcodes. - fn convert_ssa_instruction(&mut self, instruction_id: InstructionId, dfg: &DataFlowGraph) { + pub(crate) fn convert_ssa_instruction( + &mut self, + instruction_id: InstructionId, + dfg: &DataFlowGraph, + ) { let instruction = &dfg[instruction_id]; self.brillig_context.set_call_stack(dfg.get_instruction_call_stack(instruction_id)); @@ -279,6 +328,10 @@ impl<'block> BrilligBlock<'block> { self.brillig_context.deallocate_single_addr(condition); } } + Instruction::ConstrainNotEqual(..) => { + unreachable!("only implemented in ACIR") + } + Instruction::Allocate => { let result_value = dfg.instruction_results(instruction_id)[0]; let pointer = self.variables.define_single_addr_variable( @@ -646,7 +699,10 @@ impl<'block> BrilligBlock<'block> { } } } - Value::Instruction { .. } | Value::Param { .. } | Value::NumericConstant { .. } => { + Value::Instruction { .. } + | Value::Param { .. } + | Value::NumericConstant { .. } + | Value::Global(_) => { unreachable!("unsupported function call type {:?}", dfg[*func]) } }, @@ -795,7 +851,7 @@ impl<'block> BrilligBlock<'block> { self.brillig_context.deallocate_register(rc_register); } Instruction::EnableSideEffectsIf { .. } => { - todo!("enable_side_effects not supported by brillig") + unreachable!("enable_side_effects not supported by brillig") } Instruction::IfElse { .. } => { unreachable!("IfElse instructions should not be possible in brillig") @@ -840,18 +896,24 @@ impl<'block> BrilligBlock<'block> { Instruction::Noop => (), }; - let dead_variables = self - .last_uses - .get(&instruction_id) - .expect("Last uses for instruction should have been computed"); - - for dead_variable in dead_variables { - self.variables.remove_variable( - dead_variable, - self.function_context, - self.brillig_context, - ); + if !self.building_globals { + let dead_variables = self + .last_uses + .get(&instruction_id) + .expect("Last uses for instruction should have been computed"); + + for dead_variable in dead_variables { + // Globals are reserved throughout the entirety of the program + if !dfg.is_global(*dead_variable) { + self.variables.remove_variable( + dead_variable, + self.function_context, + self.brillig_context, + ); + } + } } + self.brillig_context.set_call_stack(CallStack::new()); } @@ -1318,9 +1380,9 @@ impl<'block> BrilligBlock<'block> { BrilligBinaryOp::Modulo } } - BinaryOp::Add => BrilligBinaryOp::Add, - BinaryOp::Sub => BrilligBinaryOp::Sub, - BinaryOp::Mul => BrilligBinaryOp::Mul, + BinaryOp::Add { .. } => BrilligBinaryOp::Add, + BinaryOp::Sub { .. } => BrilligBinaryOp::Sub, + BinaryOp::Mul { .. } => BrilligBinaryOp::Mul, BinaryOp::Eq => BrilligBinaryOp::Equals, BinaryOp::Lt => { if is_signed { @@ -1552,22 +1614,38 @@ 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) -> BrilligVariable { + pub(crate) fn convert_ssa_value( + &mut self, + value_id: ValueId, + dfg: &DataFlowGraph, + ) -> BrilligVariable { let value_id = dfg.resolve(value_id); let value = &dfg[value_id]; match value { + Value::Global(_) => { + unreachable!("Expected global value to be resolve to its inner value"); + } Value::Param { .. } | Value::Instruction { .. } => { // All block parameters and instruction results should have already been // converted to registers so we fetch from the cache. - - self.variables.get_allocation(self.function_context, value_id, dfg) + if dfg.is_global(value_id) { + *self.globals.get(&value_id).unwrap_or_else(|| { + panic!("ICE: Global value not found in cache {value_id}") + }) + } else { + self.variables.get_allocation(self.function_context, value_id, dfg) + } } Value::NumericConstant { constant, .. } => { // Constants might have been converted previously or not, so we get or create and // (re)initialize the value inside. if self.variables.is_allocated(&value_id) { self.variables.get_allocation(self.function_context, value_id, dfg) + } else if dfg.is_global(value_id) { + *self.globals.get(&value_id).unwrap_or_else(|| { + panic!("ICE: Global value not found in cache {value_id}") + }) } else { let new_variable = self.variables.define_variable( self.function_context, 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 bf0a1bc7347..4cf8e921483 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 @@ -7,7 +7,7 @@ use crate::{ get_bit_size_from_ssa_type, BrilligArray, BrilligVariable, BrilligVector, SingleAddrVariable, }, - registers::{RegisterAllocator, Stack}, + registers::RegisterAllocator, BrilligContext, }, ssa::ir::{ @@ -48,10 +48,10 @@ impl BlockVariables { } /// For a given SSA value id, define the variable and return the corresponding cached allocation. - pub(crate) fn define_variable( + pub(crate) fn define_variable( &mut self, function_context: &mut FunctionContext, - brillig_context: &mut BrilligContext, + brillig_context: &mut BrilligContext, value_id: ValueId, dfg: &DataFlowGraph, ) -> BrilligVariable { @@ -68,10 +68,10 @@ impl BlockVariables { } /// Defines a variable that fits in a single register and returns the allocated register. - pub(crate) fn define_single_addr_variable( + pub(crate) fn define_single_addr_variable( &mut self, function_context: &mut FunctionContext, - brillig_context: &mut BrilligContext, + brillig_context: &mut BrilligContext, value: ValueId, dfg: &DataFlowGraph, ) -> SingleAddrVariable { @@ -80,11 +80,11 @@ impl BlockVariables { } /// Removes a variable so it's not used anymore within this block. - pub(crate) fn remove_variable( + pub(crate) fn remove_variable( &mut self, value_id: &ValueId, function_context: &mut FunctionContext, - brillig_context: &mut BrilligContext, + brillig_context: &mut BrilligContext, ) { assert!(self.available_variables.remove(value_id), "ICE: Variable is not available"); let variable = function_context @@ -133,6 +133,14 @@ pub(crate) fn allocate_value( ) -> BrilligVariable { let typ = dfg.type_of_value(value_id); + allocate_value_with_type(brillig_context, typ) +} + +/// For a given value_id, allocates the necessary registers to hold it. +pub(crate) fn allocate_value_with_type( + brillig_context: &mut BrilligContext, + typ: Type, +) -> BrilligVariable { match typ { Type::Numeric(_) | Type::Reference(_) | Type::Function => { BrilligVariable::SingleAddr(SingleAddrVariable { diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_globals.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_globals.rs new file mode 100644 index 00000000000..99c8ee0fded --- /dev/null +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_globals.rs @@ -0,0 +1,39 @@ +use acvm::FieldElement; +use fxhash::{FxHashMap as HashMap, FxHashSet as HashSet}; + +use super::{ + BrilligArtifact, BrilligBlock, BrilligVariable, Function, FunctionContext, Label, ValueId, +}; +use crate::brillig::{brillig_ir::BrilligContext, DataFlowGraph}; + +pub(crate) fn convert_ssa_globals( + enable_debug_trace: bool, + globals: &Function, + used_globals: &HashSet, +) -> (BrilligArtifact, HashMap) { + let mut brillig_context = BrilligContext::new_for_global_init(enable_debug_trace); + // The global space does not have globals itself + let empty_globals = HashMap::default(); + // We can use any ID here as this context is only going to be used for globals which does not differentiate + // by functions and blocks. The only Label that should be used in the globals context is `Label::globals_init()` + let mut function_context = FunctionContext::new(globals); + brillig_context.enter_context(Label::globals_init()); + + let block_id = DataFlowGraph::default().make_block(); + let mut brillig_block = BrilligBlock { + function_context: &mut function_context, + block_id, + brillig_context: &mut brillig_context, + variables: Default::default(), + last_uses: HashMap::default(), + globals: &empty_globals, + building_globals: true, + }; + + brillig_block.compile_globals(&globals.dfg, used_globals); + + brillig_context.return_instruction(); + + let artifact = brillig_context.artifact(); + (artifact, function_context.ssa_value_allocations) +} 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 26c7151bf07..1ec2d165b12 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 @@ -2,12 +2,13 @@ use acvm::acir::brillig::MemoryAddress; use crate::brillig::brillig_ir::{ brillig_variable::{BrilligVariable, BrilligVector, SingleAddrVariable}, + registers::RegisterAllocator, BrilligBinaryOp, }; use super::brillig_block::BrilligBlock; -impl<'block> BrilligBlock<'block> { +impl<'block, Registers: RegisterAllocator> BrilligBlock<'block, Registers> { fn write_variables(&mut self, write_pointer: MemoryAddress, variables: &[BrilligVariable]) { for (index, variable) in variables.iter().enumerate() { self.brillig_context.store_instruction(write_pointer, variable.extract_register()); @@ -159,6 +160,7 @@ mod tests { use std::vec; use acvm::FieldElement; + use fxhash::FxHashMap as HashMap; use noirc_frontend::monomorphization::ast::InlineType; use crate::brillig::brillig_gen::brillig_block::BrilligBlock; @@ -173,6 +175,7 @@ mod tests { create_and_run_vm, create_context, create_entry_point_bytecode, }; use crate::brillig::brillig_ir::{BrilligContext, BRILLIG_MEMORY_ADDRESSING_BIT_SIZE}; + use crate::brillig::ValueId; use crate::ssa::function_builder::FunctionBuilder; use crate::ssa::ir::function::RuntimeType; use crate::ssa::ir::map::Id; @@ -193,7 +196,8 @@ mod tests { fn create_brillig_block<'a>( function_context: &'a mut FunctionContext, brillig_context: &'a mut BrilligContext, - ) -> BrilligBlock<'a> { + globals: &'a HashMap, + ) -> BrilligBlock<'a, Stack> { let variables = BlockVariables::default(); BrilligBlock { function_context, @@ -201,6 +205,8 @@ mod tests { brillig_context, variables, last_uses: Default::default(), + globals, + building_globals: false, } } @@ -242,7 +248,9 @@ mod tests { // Allocate the results let target_vector = BrilligVector { pointer: context.allocate_register() }; - let mut block = create_brillig_block(&mut function_context, &mut context); + let brillig_globals = HashMap::default(); + let mut block = + create_brillig_block(&mut function_context, &mut context, &brillig_globals); if push_back { block.slice_push_back_operation( @@ -358,7 +366,9 @@ mod tests { bit_size: BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, }; - let mut block = create_brillig_block(&mut function_context, &mut context); + let brillig_globals = HashMap::default(); + let mut block = + create_brillig_block(&mut function_context, &mut context, &brillig_globals); if pop_back { block.slice_pop_back_operation( @@ -464,7 +474,9 @@ mod tests { // Allocate the results let target_vector = BrilligVector { pointer: context.allocate_register() }; - let mut block = create_brillig_block(&mut function_context, &mut context); + let brillig_globals = HashMap::default(); + let mut block = + create_brillig_block(&mut function_context, &mut context, &brillig_globals); block.slice_insert_operation( target_vector, @@ -604,7 +616,9 @@ mod tests { bit_size: BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, }; - let mut block = create_brillig_block(&mut function_context, &mut context); + let brillig_globals = HashMap::default(); + let mut block = + create_brillig_block(&mut function_context, &mut context, &brillig_globals); block.slice_remove_operation( target_vector, diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/constant_allocation.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/constant_allocation.rs index 61ca20be2f5..64741393dd7 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/constant_allocation.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/constant_allocation.rs @@ -22,6 +22,7 @@ pub(crate) enum InstructionLocation { Terminator, } +#[derive(Default)] pub(crate) struct ConstantAllocation { constant_usage: HashMap>>, allocation_points: HashMap>>, 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 d6851a9ecf9..37a63466119 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/variable_liveness.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/variable_liveness.rs @@ -53,9 +53,10 @@ pub(crate) fn collect_variables_of_value( let value = &dfg[value_id]; match value { - Value::Instruction { .. } | Value::Param { .. } | Value::NumericConstant { .. } => { - Some(value_id) - } + Value::Instruction { .. } + | Value::Param { .. } + | Value::NumericConstant { .. } + | Value::Global(_) => Some(value_id), // Functions are not variables in a defunctionalized SSA. Only constant function values should appear. Value::ForeignFunction(_) | Value::Function(_) | Value::Intrinsic(..) => None, } @@ -113,6 +114,7 @@ fn compute_used_before_def( type LastUses = HashMap; /// A struct representing the liveness of variables throughout a function. +#[derive(Default)] pub(crate) struct VariableLiveness { cfg: ControlFlowGraph, post_order: PostOrder, @@ -382,14 +384,14 @@ mod test { builder.switch_to_block(b2); let twenty_seven = builder.field_constant(27u128); - let v7 = builder.insert_binary(v0, BinaryOp::Add, twenty_seven); + let v7 = builder.insert_binary(v0, BinaryOp::Add { unchecked: false }, twenty_seven); builder.insert_store(v3, v7); builder.terminate_with_jmp(b3, vec![]); builder.switch_to_block(b1); - let v6 = builder.insert_binary(v1, BinaryOp::Add, twenty_seven); + let v6 = builder.insert_binary(v1, BinaryOp::Add { unchecked: false }, twenty_seven); builder.insert_store(v3, v6); builder.terminate_with_jmp(b3, vec![]); @@ -501,7 +503,7 @@ mod test { builder.switch_to_block(b2); - let v6 = builder.insert_binary(v4, BinaryOp::Mul, v4); + let v6 = builder.insert_binary(v4, BinaryOp::Mul { unchecked: false }, v4); builder.terminate_with_jmp(b4, vec![v0]); @@ -526,7 +528,7 @@ mod test { let v12 = builder.insert_load(v3, Type::field()); - let v13 = builder.insert_binary(v12, BinaryOp::Add, v6); + let v13 = builder.insert_binary(v12, BinaryOp::Add { unchecked: false }, v6); builder.insert_store(v3, v13); @@ -535,13 +537,13 @@ mod test { builder.switch_to_block(b8); let one = builder.field_constant(1u128); - let v15 = builder.insert_binary(v7, BinaryOp::Add, one); + let v15 = builder.insert_binary(v7, BinaryOp::Add { unchecked: false }, one); builder.terminate_with_jmp(b4, vec![v15]); builder.switch_to_block(b6); - let v16 = builder.insert_binary(v4, BinaryOp::Add, one); + let v16 = builder.insert_binary(v4, BinaryOp::Add { unchecked: false }, one); builder.terminate_with_jmp(b1, vec![v16]); diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir.rs index 55e12c993fa..06f61948337 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir.rs @@ -37,7 +37,7 @@ use acvm::{ }; use debug_show::DebugShow; -use super::ProcedureId; +use super::{GlobalSpace, ProcedureId}; /// The Brillig VM does not apply a limit to the memory address space, /// As a convention, we take use 32 bits. This means that we assume that @@ -110,7 +110,9 @@ impl BrilligContext { can_call_procedures: true, } } +} +impl BrilligContext { /// Splits a two's complement signed integer in the sign bit and the absolute value. /// For example, -6 i8 (11111010) is split to 00000110 (6, absolute value) and 1 (is_negative). pub(crate) fn absolute_value( @@ -213,6 +215,21 @@ impl BrilligContext { } } +/// Special brillig context to codegen global values initialization +impl BrilligContext { + pub(crate) fn new_for_global_init(enable_debug_trace: bool) -> BrilligContext { + BrilligContext { + obj: BrilligArtifact::default(), + registers: GlobalSpace::new(), + context_label: Label::globals_init(), + current_section: 0, + next_section: 1, + debug_show: DebugShow::new(enable_debug_trace), + can_call_procedures: false, + } + } +} + impl BrilligContext { /// Adds a brillig instruction to the brillig byte code fn push_opcode(&mut self, opcode: BrilligOpcode) { @@ -299,8 +316,12 @@ pub(crate) mod tests { returns: Vec, ) -> GeneratedBrillig { let artifact = context.artifact(); - let mut entry_point_artifact = - BrilligContext::new_entry_point_artifact(arguments, returns, FunctionId::test_new(0)); + let mut entry_point_artifact = BrilligContext::new_entry_point_artifact( + arguments, + returns, + FunctionId::test_new(0), + false, + ); entry_point_artifact.link_with(&artifact); while let Some(unresolved_fn_label) = entry_point_artifact.first_unresolved_function_call() { diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs index 3654a95a03f..4c48675d1e7 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs @@ -75,6 +75,8 @@ pub(crate) enum LabelType { Function(FunctionId, Option), /// Labels for intrinsic procedures Procedure(ProcedureId), + /// Label for initialization of globals + GlobalInit, } impl std::fmt::Display for LabelType { @@ -89,6 +91,7 @@ impl std::fmt::Display for LabelType { } LabelType::Entrypoint => write!(f, "Entrypoint"), LabelType::Procedure(procedure_id) => write!(f, "Procedure({:?})", procedure_id), + LabelType::GlobalInit => write!(f, "Globals Initialization"), } } } @@ -123,6 +126,10 @@ impl Label { pub(crate) fn procedure(procedure_id: ProcedureId) -> Self { Label { label_type: LabelType::Procedure(procedure_id), section: None } } + + pub(crate) fn globals_init() -> Self { + Label { label_type: LabelType::GlobalInit, section: None } + } } impl std::fmt::Display for Label { diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_calls.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_calls.rs index da310873cff..4da3aa4d6d2 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_calls.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_calls.rs @@ -9,7 +9,8 @@ use super::{ BrilligBinaryOp, BrilligContext, ReservedRegisters, }; -impl BrilligContext { +impl BrilligContext { + // impl BrilligContext { pub(crate) fn codegen_call( &mut self, func_id: FunctionId, @@ -17,7 +18,7 @@ impl BrilligContext { returns: &[BrilligVariable], ) { let stack_size_register = SingleAddrVariable::new_usize(self.allocate_register()); - let previous_stack_pointer = self.registers.empty_stack_start(); + let previous_stack_pointer = self.registers.empty_registers_start(); let stack_size = previous_stack_pointer.unwrap_relative(); // Write the stack size self.const_instruction(stack_size_register, stack_size.into()); 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 2dbee48b277..b84a15db4ad 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs @@ -15,6 +15,7 @@ use acvm::acir::{ pub(crate) const MAX_STACK_SIZE: usize = 16 * MAX_STACK_FRAME_SIZE; pub(crate) const MAX_STACK_FRAME_SIZE: usize = 2048; pub(crate) const MAX_SCRATCH_SPACE: usize = 64; +pub(crate) const MAX_GLOBAL_SPACE: usize = 16384; impl BrilligContext { /// Creates an entry point artifact that will jump to the function label provided. @@ -22,11 +23,16 @@ impl BrilligContext { arguments: Vec, return_parameters: Vec, target_function: FunctionId, + globals_init: bool, ) -> BrilligArtifact { let mut context = BrilligContext::new(false); context.codegen_entry_point(&arguments, &return_parameters); + if globals_init { + context.add_globals_init_instruction(); + } + context.add_external_call_instruction(target_function); context.codegen_exit_point(&arguments, &return_parameters); @@ -34,7 +40,7 @@ impl BrilligContext { } fn calldata_start_offset() -> usize { - ReservedRegisters::len() + MAX_STACK_SIZE + MAX_SCRATCH_SPACE + ReservedRegisters::len() + MAX_STACK_SIZE + MAX_SCRATCH_SPACE + MAX_GLOBAL_SPACE } fn return_data_start_offset(calldata_size: usize) -> usize { diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/instructions.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/instructions.rs index 2bf5364414c..d67da423d44 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/instructions.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/instructions.rs @@ -200,6 +200,13 @@ impl BrilligContext< self.obj.add_unresolved_external_call(BrilligOpcode::Call { location: 0 }, proc_label); } + pub(super) fn add_globals_init_instruction(&mut self) { + let globals_init_label = Label::globals_init(); + self.debug_show.add_external_call_instruction(globals_init_label.to_string()); + self.obj + .add_unresolved_external_call(BrilligOpcode::Call { location: 0 }, globals_init_label); + } + /// Adds a unresolved `Jump` instruction to the bytecode. pub(crate) fn jump_instruction(&mut self, target_label: Label) { self.debug_show.jump_instruction(target_label.to_string()); diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/registers.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/registers.rs index dd7766f40aa..b83c03b297a 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/registers.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/registers.rs @@ -7,7 +7,7 @@ use crate::brillig::brillig_ir::entry_point::MAX_STACK_SIZE; use super::{ brillig_variable::SingleAddrVariable, - entry_point::{MAX_SCRATCH_SPACE, MAX_STACK_FRAME_SIZE}, + entry_point::{MAX_GLOBAL_SPACE, MAX_SCRATCH_SPACE, MAX_STACK_FRAME_SIZE}, BrilligContext, ReservedRegisters, }; @@ -24,6 +24,8 @@ pub(crate) trait RegisterAllocator { fn ensure_register_is_allocated(&mut self, register: MemoryAddress); /// Creates a new register context from a set of registers allocated previously. fn from_preallocated_registers(preallocated_registers: Vec) -> Self; + /// Finds the first register that is available based upon the deallocation list + fn empty_registers_start(&self) -> MemoryAddress; } /// Every brillig stack frame/call context has its own view of register space. @@ -41,10 +43,6 @@ impl Stack { let offset = register.unwrap_relative(); offset >= Self::start() && offset < Self::end() } - - pub(crate) fn empty_stack_start(&self) -> MemoryAddress { - MemoryAddress::relative(self.storage.empty_registers_start(Self::start())) - } } impl RegisterAllocator for Stack { @@ -83,6 +81,10 @@ impl RegisterAllocator for Stack { ), } } + + fn empty_registers_start(&self) -> MemoryAddress { + MemoryAddress::relative(self.storage.empty_registers_start(Self::start())) + } } /// Procedure arguments and returns are passed through scratch space. @@ -109,7 +111,7 @@ impl RegisterAllocator for ScratchSpace { } fn end() -> usize { - ReservedRegisters::len() + MAX_STACK_SIZE + MAX_SCRATCH_SPACE + Self::start() + MAX_SCRATCH_SPACE } fn ensure_register_is_allocated(&mut self, register: MemoryAddress) { @@ -139,6 +141,70 @@ impl RegisterAllocator for ScratchSpace { ), } } + + fn empty_registers_start(&self) -> MemoryAddress { + MemoryAddress::direct(self.storage.empty_registers_start(Self::start())) + } +} + +/// Globals have a separate memory space +/// This memory space is initialized once at the beginning of a program +/// and is read-only. +pub(crate) struct GlobalSpace { + storage: DeallocationListAllocator, +} + +impl GlobalSpace { + pub(crate) fn new() -> Self { + Self { storage: DeallocationListAllocator::new(Self::start()) } + } + + fn is_within_bounds(register: MemoryAddress) -> bool { + let index = register.unwrap_direct(); + index >= Self::start() && index < Self::end() + } +} + +impl RegisterAllocator for GlobalSpace { + fn start() -> usize { + ScratchSpace::end() + } + + fn end() -> usize { + Self::start() + MAX_GLOBAL_SPACE + } + + fn allocate_register(&mut self) -> MemoryAddress { + let allocated = MemoryAddress::direct(self.storage.allocate_register()); + assert!(Self::is_within_bounds(allocated), "Global space too deep"); + allocated + } + + fn deallocate_register(&mut self, register_index: MemoryAddress) { + self.storage.deallocate_register(register_index.unwrap_direct()); + } + + fn ensure_register_is_allocated(&mut self, register: MemoryAddress) { + assert!(Self::is_within_bounds(register), "Register out of global space bounds"); + self.storage.ensure_register_is_allocated(register.unwrap_direct()); + } + + fn from_preallocated_registers(preallocated_registers: Vec) -> Self { + for register in &preallocated_registers { + assert!(Self::is_within_bounds(*register), "Register out of global space bounds"); + } + + Self { + storage: DeallocationListAllocator::from_preallocated_registers( + Self::start(), + vecmap(preallocated_registers, |r| r.unwrap_direct()), + ), + } + } + + fn empty_registers_start(&self) -> MemoryAddress { + MemoryAddress::direct(self.storage.empty_registers_start(Self::start())) + } } struct DeallocationListAllocator { diff --git a/compiler/noirc_evaluator/src/brillig/mod.rs b/compiler/noirc_evaluator/src/brillig/mod.rs index cb8c35cd8e0..3d96a855aa0 100644 --- a/compiler/noirc_evaluator/src/brillig/mod.rs +++ b/compiler/noirc_evaluator/src/brillig/mod.rs @@ -2,7 +2,8 @@ pub(crate) mod brillig_gen; pub(crate) mod brillig_ir; use acvm::FieldElement; -use brillig_ir::artifact::LabelType; +use brillig_gen::brillig_globals::convert_ssa_globals; +use brillig_ir::{artifact::LabelType, brillig_variable::BrilligVariable, registers::GlobalSpace}; use self::{ brillig_gen::convert_ssa_function, @@ -12,7 +13,11 @@ use self::{ }, }; use crate::ssa::{ - ir::function::{Function, FunctionId}, + ir::{ + dfg::DataFlowGraph, + function::{Function, FunctionId}, + value::ValueId, + }, ssa_gen::Ssa, }; use fxhash::FxHashMap as HashMap; @@ -26,12 +31,18 @@ pub use self::brillig_ir::procedures::ProcedureId; pub struct Brillig { /// Maps SSA function labels to their brillig artifact ssa_function_to_brillig: HashMap>, + globals: BrilligArtifact, } impl Brillig { /// Compiles a function into brillig and store the compilation artifacts - pub(crate) fn compile(&mut self, func: &Function, enable_debug_trace: bool) { - let obj = convert_ssa_function(func, enable_debug_trace); + pub(crate) fn compile( + &mut self, + func: &Function, + enable_debug_trace: bool, + globals: &HashMap, + ) { + let obj = convert_ssa_function(func, enable_debug_trace, globals); self.ssa_function_to_brillig.insert(func.id(), obj); } @@ -46,6 +57,7 @@ impl Brillig { } // Procedures are compiled as needed LabelType::Procedure(procedure_id) => Some(Cow::Owned(compile_procedure(procedure_id))), + LabelType::GlobalInit => Some(Cow::Borrowed(&self.globals)), _ => unreachable!("ICE: Expected a function or procedure label"), } } @@ -71,9 +83,14 @@ impl Ssa { .collect::>(); let mut brillig = Brillig::default(); + + let (artifact, brillig_globals) = + convert_ssa_globals(enable_debug_trace, &self.globals, &self.used_global_values); + brillig.globals = artifact; + for brillig_function_id in brillig_reachable_function_ids { let func = &self.functions[&brillig_function_id]; - brillig.compile(func, enable_debug_trace); + brillig.compile(func, enable_debug_trace, &brillig_globals); } brillig diff --git a/compiler/noirc_evaluator/src/errors.rs b/compiler/noirc_evaluator/src/errors.rs index 1e484f8af59..94c0e0554a4 100644 --- a/compiler/noirc_evaluator/src/errors.rs +++ b/compiler/noirc_evaluator/src/errors.rs @@ -49,8 +49,8 @@ pub enum RuntimeError { StaticAssertDynamicMessage { call_stack: CallStack }, #[error("Argument is dynamic")] StaticAssertDynamicPredicate { call_stack: CallStack }, - #[error("Argument is false")] - StaticAssertFailed { call_stack: CallStack }, + #[error("{message}")] + StaticAssertFailed { message: String, call_stack: CallStack }, #[error("Nested slices, i.e. slices within an array or slice, are not supported")] NestedSlice { call_stack: CallStack }, #[error("Big Integer modulus do no match")] @@ -165,7 +165,7 @@ impl RuntimeError { | RuntimeError::AssertConstantFailed { call_stack } | RuntimeError::StaticAssertDynamicMessage { call_stack } | RuntimeError::StaticAssertDynamicPredicate { call_stack } - | RuntimeError::StaticAssertFailed { call_stack } + | RuntimeError::StaticAssertFailed { call_stack, .. } | RuntimeError::IntegerOutOfBounds { call_stack, .. } | RuntimeError::UnsupportedIntegerSize { call_stack, .. } | RuntimeError::InvalidBlackBoxInputBitSize { call_stack, .. } diff --git a/compiler/noirc_evaluator/src/ssa.rs b/compiler/noirc_evaluator/src/ssa.rs index 206fe8d9084..12ea04daebd 100644 --- a/compiler/noirc_evaluator/src/ssa.rs +++ b/compiler/noirc_evaluator/src/ssa.rs @@ -150,15 +150,19 @@ pub(crate) fn optimize_into_acir( /// Run all SSA passes. fn optimize_all(builder: SsaBuilder, options: &SsaEvaluatorOptions) -> Result { Ok(builder - .run_pass(Ssa::remove_unreachable_functions, "Removing Unreachable Functions") + .run_pass(Ssa::remove_unreachable_functions, "Removing Unreachable Functions (1st)") .run_pass(Ssa::defunctionalize, "Defunctionalization") .run_pass(Ssa::remove_paired_rc, "Removing Paired rc_inc & rc_decs") + .run_pass( + |ssa| ssa.preprocess_functions(options.inliner_aggressiveness), + "Preprocessing Functions", + ) .run_pass(|ssa| ssa.inline_functions(options.inliner_aggressiveness), "Inlining (1st)") // Run mem2reg with the CFG separated into blocks .run_pass(Ssa::mem2reg, "Mem2Reg (1st)") .run_pass(Ssa::simplify_cfg, "Simplifying (1st)") .run_pass(Ssa::as_slice_optimization, "`as_slice` optimization") - .run_pass(Ssa::remove_unreachable_functions, "Removing Unreachable Functions") + .run_pass(Ssa::remove_unreachable_functions, "Removing Unreachable Functions (2nd)") .try_run_pass( Ssa::evaluate_static_assert_and_assert_constant, "`static_assert` and `assert_constant`", @@ -186,8 +190,9 @@ fn optimize_all(builder: SsaBuilder, options: &SsaEvaluatorOptions) -> Result Ssa { diff --git a/compiler/noirc_evaluator/src/ssa/checks/check_for_underconstrained_values.rs b/compiler/noirc_evaluator/src/ssa/checks/check_for_underconstrained_values.rs index d61dd27d02a..f44f726bfc7 100644 --- a/compiler/noirc_evaluator/src/ssa/checks/check_for_underconstrained_values.rs +++ b/compiler/noirc_evaluator/src/ssa/checks/check_for_underconstrained_values.rs @@ -267,7 +267,8 @@ impl DependencyContext { } // Check the constrain instruction arguments against those // involved in Brillig calls, remove covered calls - Instruction::Constrain(value_id1, value_id2, _) => { + Instruction::Constrain(value_id1, value_id2, _) + | Instruction::ConstrainNotEqual(value_id1, value_id2, _) => { self.clear_constrained( &[function.dfg.resolve(*value_id1), function.dfg.resolve(*value_id2)], function, @@ -332,7 +333,8 @@ impl DependencyContext { } Value::Instruction { .. } | Value::NumericConstant { .. } - | Value::Param { .. } => { + | Value::Param { .. } + | Value::Global(_) => { panic!( "calling non-function value with ID {func_id} in function {}", function.name() @@ -554,6 +556,7 @@ impl Context { | Instruction::Binary(..) | Instruction::Cast(..) | Instruction::Constrain(..) + | Instruction::ConstrainNotEqual(..) | Instruction::IfElse { .. } | Instruction::Load { .. } | Instruction::Not(..) @@ -618,7 +621,8 @@ impl Context { } Value::Instruction { .. } | Value::NumericConstant { .. } - | Value::Param { .. } => { + | Value::Param { .. } + | Value::Global(_) => { panic!("At the point we are running disconnect there shouldn't be any other values as arguments") } } diff --git a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs index d08d5339237..154d485b143 100644 --- a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs @@ -19,7 +19,7 @@ use super::{ ir::{ basic_block::BasicBlock, call_stack::{CallStack, CallStackId}, - dfg::InsertInstructionResult, + dfg::{GlobalsGraph, InsertInstructionResult}, function::RuntimeType, instruction::{ConstrainError, InstructionId, Intrinsic}, types::NumericType, @@ -73,6 +73,13 @@ impl FunctionBuilder { self.current_function.set_runtime(runtime); } + pub(crate) fn set_globals(&mut self, globals: Arc) { + for (_, value) in globals.values_iter() { + self.current_function.dfg.make_global(value.get_type().into_owned()); + } + self.current_function.set_globals(globals); + } + /// Finish the current function and create a new function. /// /// A FunctionBuilder can always only work on one function at a time, so care @@ -251,14 +258,6 @@ impl FunctionBuilder { operator: BinaryOp, rhs: ValueId, ) -> ValueId { - let lhs_type = self.type_of_value(lhs); - let rhs_type = self.type_of_value(rhs); - if operator != BinaryOp::Shl && operator != BinaryOp::Shr { - assert_eq!( - lhs_type, rhs_type, - "ICE - Binary instruction operands must have the same type" - ); - } let instruction = Instruction::Binary(Binary { lhs, rhs, operator }); self.insert_instruction(instruction, None).first() } diff --git a/compiler/noirc_evaluator/src/ssa/ir/cfg.rs b/compiler/noirc_evaluator/src/ssa/ir/cfg.rs index 788b1a7d302..5253c68c72c 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/cfg.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/cfg.rs @@ -18,7 +18,7 @@ struct CfgNode { pub(crate) successors: BTreeSet, } -#[derive(Clone)] +#[derive(Clone, Default)] /// The Control Flow Graph maintains a mapping of blocks to their predecessors /// and successors where predecessors are basic blocks and successors are /// basic blocks. diff --git a/compiler/noirc_evaluator/src/ssa/ir/dfg.rs b/compiler/noirc_evaluator/src/ssa/ir/dfg.rs index 8425e4d5e56..83b8f2a57ff 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/dfg.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/dfg.rs @@ -1,4 +1,4 @@ -use std::borrow::Cow; +use std::{borrow::Cow, sync::Arc}; use crate::ssa::{function_builder::data_bus::DataBus, ir::instruction::SimplifyResult}; @@ -102,6 +102,31 @@ pub(crate) struct DataFlowGraph { #[serde(skip)] pub(crate) data_bus: DataBus, + + pub(crate) globals: Arc, +} + +/// The GlobalsGraph contains the actual global data. +/// Global data is expected to only be numeric constants or array constants (which are represented by Instruction::MakeArray). +/// The global's data will shared across functions and should be accessible inside of a function's DataFlowGraph. +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub(crate) struct GlobalsGraph { + /// Storage for all of the global values + values: DenseMap, + /// All of the instructions in the global value space. + /// These are expected to all be Instruction::MakeArray + instructions: DenseMap, +} + +impl GlobalsGraph { + pub(crate) fn from_dfg(dfg: DataFlowGraph) -> Self { + Self { values: dfg.values, instructions: dfg.instructions } + } + + /// Iterate over every Value in this DFG in no particular order, including unused Values + pub(crate) fn values_iter(&self) -> impl DoubleEndedIterator { + self.values.iter() + } } impl DataFlowGraph { @@ -148,12 +173,12 @@ impl DataFlowGraph { /// The pairs are order by id, which is not guaranteed to be meaningful. pub(crate) fn basic_blocks_iter( &self, - ) -> impl ExactSizeIterator { + ) -> impl DoubleEndedIterator { self.blocks.iter() } /// Iterate over every Value in this DFG in no particular order, including unused Values - pub(crate) fn values_iter(&self) -> impl ExactSizeIterator { + pub(crate) fn values_iter(&self) -> impl DoubleEndedIterator { self.values.iter() } @@ -181,7 +206,16 @@ impl DataFlowGraph { /// Check if the function runtime would simply ignore this instruction. pub(crate) fn is_handled_by_runtime(&self, instruction: &Instruction) -> bool { - !(self.runtime().is_acir() && instruction.is_brillig_only()) + match self.runtime() { + RuntimeType::Acir(_) => !matches!( + instruction, + Instruction::IncrementRc { .. } | Instruction::DecrementRc { .. } + ), + RuntimeType::Brillig(_) => !matches!( + instruction, + Instruction::EnableSideEffectsIf { .. } | Instruction::IfElse { .. } + ), + } } fn insert_instruction_without_simplification( @@ -205,7 +239,7 @@ impl DataFlowGraph { call_stack: CallStackId, ) -> InsertInstructionResult { if !self.is_handled_by_runtime(&instruction_data) { - return InsertInstructionResult::InstructionRemoved; + panic!("Attempted to insert instruction not handled by runtime: {instruction_data:?}"); } let id = self.insert_instruction_without_simplification( @@ -226,10 +260,29 @@ impl DataFlowGraph { block: BasicBlockId, ctrl_typevars: Option>, call_stack: CallStackId, + ) -> InsertInstructionResult { + self.insert_instruction_and_results_if_simplified( + instruction, + block, + ctrl_typevars, + call_stack, + None, + ) + } + + /// Simplifies a potentially existing instruction and inserts it only if it changed. + pub(crate) fn insert_instruction_and_results_if_simplified( + &mut self, + instruction: Instruction, + block: BasicBlockId, + ctrl_typevars: Option>, + call_stack: CallStackId, + existing_id: Option, ) -> InsertInstructionResult { if !self.is_handled_by_runtime(&instruction) { - return InsertInstructionResult::InstructionRemoved; + panic!("Attempted to insert instruction not handled by runtime: {instruction:?}"); } + match instruction.simplify(self, block, ctrl_typevars.clone(), call_stack) { SimplifyResult::SimplifiedTo(simplification) => { InsertInstructionResult::SimplifiedTo(simplification) @@ -241,7 +294,20 @@ impl DataFlowGraph { result @ (SimplifyResult::SimplifiedToInstruction(_) | SimplifyResult::SimplifiedToInstructionMultiple(_) | SimplifyResult::None) => { - let mut instructions = result.instructions().unwrap_or(vec![instruction]); + let instructions = result.instructions(); + if instructions.is_none() { + if let Some(id) = existing_id { + if self[id] == instruction { + // Just (re)insert into the block, no need to redefine. + self.blocks[block].insert_instruction(id); + return InsertInstructionResult::Results( + id, + self.instruction_results(id), + ); + } + } + } + let mut instructions = instructions.unwrap_or(vec![instruction]); assert!(!instructions.is_empty(), "`SimplifyResult::SimplifiedToInstructionMultiple` must not return empty vector"); if instructions.len() > 1 { @@ -325,6 +391,10 @@ impl DataFlowGraph { id } + pub(crate) fn make_global(&mut self, typ: Type) -> ValueId { + self.values.insert(Value::Global(typ)) + } + /// Gets or creates a ValueId for the given FunctionId. pub(crate) fn import_function(&mut self, function: FunctionId) -> ValueId { if let Some(existing) = self.functions.get(&function) { @@ -497,7 +567,7 @@ impl DataFlowGraph { &self, value: ValueId, ) -> Option<(FieldElement, NumericType)> { - match &self.values[self.resolve(value)] { + match &self[self.resolve(value)] { Value::NumericConstant { constant, typ } => Some((*constant, *typ)), _ => None, } @@ -506,13 +576,15 @@ impl DataFlowGraph { /// Returns the Value::Array associated with this ValueId if it refers to an array constant. /// Otherwise, this returns None. pub(crate) fn get_array_constant(&self, value: ValueId) -> Option<(im::Vector, Type)> { - match &self.values[self.resolve(value)] { - Value::Instruction { instruction, .. } => match &self.instructions[*instruction] { + let value = self.resolve(value); + if let Some(instruction) = self.get_local_or_global_instruction(value) { + match instruction { Instruction::MakeArray { elements, typ } => Some((elements.clone(), typ.clone())), _ => None, - }, + } + } else { // Arrays are shared, so cloning them is cheap - _ => None, + None } } @@ -525,6 +597,24 @@ impl DataFlowGraph { } } + /// If this value points to an array of constant bytes, returns a string + /// consisting of those bytes if they form a valid UTF-8 string. + pub(crate) fn get_string(&self, value: ValueId) -> Option { + let (value_ids, _typ) = self.get_array_constant(value)?; + + let mut bytes = Vec::new(); + for value_id in value_ids { + let field_value = self.get_numeric_constant(value_id)?; + let u64_value = field_value.try_to_u64()?; + if u64_value > 255 { + return None; + }; + let byte = u64_value as u8; + bytes.push(byte); + } + String::from_utf8(bytes).ok() + } + /// A constant index less than the array length is safe pub(crate) fn is_safe_index(&self, index: ValueId, array: ValueId) -> bool { #[allow(clippy::match_like_matches_macro)] @@ -587,14 +677,23 @@ impl DataFlowGraph { /// True if the given ValueId refers to a (recursively) constant value pub(crate) fn is_constant(&self, argument: ValueId) -> bool { - match &self[self.resolve(argument)] { + let argument = self.resolve(argument); + match &self[argument] { Value::Param { .. } => false, - Value::Instruction { instruction, .. } => match &self[*instruction] { - Instruction::MakeArray { elements, .. } => { - elements.iter().all(|element| self.is_constant(*element)) + Value::Instruction { .. } => { + let Some(instruction) = self.get_local_or_global_instruction(argument) else { + return false; + }; + match &instruction { + Instruction::MakeArray { elements, .. } => { + elements.iter().all(|element| self.is_constant(*element)) + } + _ => false, } - _ => false, - }, + } + Value::Global(_) => { + unreachable!("The global value should have been indexed from the global space"); + } _ => true, } } @@ -607,6 +706,29 @@ impl DataFlowGraph { false } } + + pub(crate) fn is_global(&self, value: ValueId) -> bool { + matches!(self.values[value], Value::Global(_)) + } + + /// Uses value information to determine whether an instruction is from + /// this function's DFG or the global space's DFG. + pub(crate) fn get_local_or_global_instruction(&self, value: ValueId) -> Option<&Instruction> { + match &self[value] { + Value::Instruction { instruction, .. } => { + let instruction = if self.is_global(value) { + let instruction = &self.globals[*instruction]; + // We expect to only have MakeArray instructions in the global space + assert!(matches!(instruction, Instruction::MakeArray { .. })); + instruction + } else { + &self[*instruction] + }; + Some(instruction) + } + _ => None, + } + } } impl std::ops::Index for DataFlowGraph { @@ -625,7 +747,11 @@ impl std::ops::IndexMut for DataFlowGraph { impl std::ops::Index for DataFlowGraph { type Output = Value; fn index(&self, id: ValueId) -> &Self::Output { - &self.values[id] + let value = &self.values[id]; + if matches!(value, Value::Global(_)) { + return &self.globals[id]; + } + value } } @@ -643,6 +769,20 @@ impl std::ops::IndexMut for DataFlowGraph { } } +impl std::ops::Index for GlobalsGraph { + type Output = Value; + fn index(&self, id: ValueId) -> &Self::Output { + &self.values[id] + } +} + +impl std::ops::Index for GlobalsGraph { + type Output = Instruction; + fn index(&self, id: InstructionId) -> &Self::Output { + &self.instructions[id] + } +} + // The result of calling DataFlowGraph::insert_instruction can // be a list of results or a single ValueId if the instruction was simplified // to an existing value. diff --git a/compiler/noirc_evaluator/src/ssa/ir/dom.rs b/compiler/noirc_evaluator/src/ssa/ir/dom.rs index ff54bf3b6ed..3dde6240e18 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/dom.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/dom.rs @@ -39,6 +39,7 @@ impl DominatorTreeNode { } /// The dominator tree for a single function. +#[derive(Default)] pub(crate) struct DominatorTree { /// The nodes of the dominator tree /// diff --git a/compiler/noirc_evaluator/src/ssa/ir/function.rs b/compiler/noirc_evaluator/src/ssa/ir/function.rs index 109c2a59781..b21a84d16dc 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/function.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/function.rs @@ -1,17 +1,18 @@ use std::collections::BTreeSet; +use std::sync::Arc; use iter_extended::vecmap; use noirc_frontend::monomorphization::ast::InlineType; use serde::{Deserialize, Serialize}; use super::basic_block::BasicBlockId; -use super::dfg::DataFlowGraph; +use super::dfg::{DataFlowGraph, GlobalsGraph}; use super::instruction::TerminatorInstruction; use super::map::Id; use super::types::Type; use super::value::ValueId; -#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)] +#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Serialize, Deserialize, PartialOrd, Ord)] pub(crate) enum RuntimeType { // A noir function, to be compiled in ACIR and executed by ACVM Acir(InlineType), @@ -76,7 +77,7 @@ pub(crate) struct Function { /// Name of the function for debugging only name: String, - id: FunctionId, + id: Option, /// The DataFlowGraph holds the majority of data pertaining to the function /// including its blocks, instructions, and values. @@ -90,20 +91,29 @@ impl Function { pub(crate) fn new(name: String, id: FunctionId) -> Self { let mut dfg = DataFlowGraph::default(); let entry_block = dfg.make_block(); - Self { name, id, entry_block, dfg } + Self { name, id: Some(id), entry_block, dfg } + } + + /// Globals are generated using the same codegen process as functions. + /// To avoid a recursive global context we should create a pseudo function to mock a globals context. + pub(crate) fn new_for_globals() -> Self { + let mut dfg = DataFlowGraph::default(); + let entry_block = dfg.make_block(); + Self { name: "globals".to_owned(), id: None, entry_block, dfg } } /// Creates a new function as a clone of the one passed in with the passed in id. pub(crate) fn clone_with_id(id: FunctionId, another: &Function) -> Self { let dfg = another.dfg.clone(); let entry_block = another.entry_block; - Self { name: another.name.clone(), id, entry_block, dfg } + Self { name: another.name.clone(), id: Some(id), entry_block, dfg } } /// Takes the signature (function name & runtime) from a function but does not copy the body. pub(crate) fn clone_signature(id: FunctionId, another: &Function) -> Self { let mut new_function = Function::new(another.name.clone(), id); new_function.set_runtime(another.runtime()); + new_function.set_globals(another.dfg.globals.clone()); new_function } @@ -115,7 +125,7 @@ impl Function { /// The id of the function. pub(crate) fn id(&self) -> FunctionId { - self.id + self.id.expect("FunctionId should be initialized") } /// Runtime type of the function. @@ -128,6 +138,10 @@ impl Function { self.dfg.set_runtime(runtime); } + pub(crate) fn set_globals(&mut self, globals: Arc) { + self.dfg.globals = globals; + } + pub(crate) fn is_no_predicates(&self) -> bool { match self.runtime() { RuntimeType::Acir(inline_type) => matches!(inline_type, InlineType::NoPredicates), @@ -228,12 +242,6 @@ pub(crate) struct Signature { pub(crate) returns: Vec, } -impl std::fmt::Display for Function { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - super::printer::display_function(self, f) - } -} - #[test] fn sign_smoke() { let mut signature = Signature::default(); diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs index 17cde96cddc..5806e62bf95 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs @@ -10,7 +10,7 @@ use fxhash::FxHasher64; use iter_extended::vecmap; use noirc_frontend::hir_def::types::Type as HirType; -use crate::ssa::{ir::function::RuntimeType, opt::flatten_cfg::value_merger::ValueMerger}; +use crate::ssa::opt::flatten_cfg::value_merger::ValueMerger; use super::{ basic_block::BasicBlockId, @@ -256,6 +256,9 @@ pub(crate) enum Instruction { /// Constrains two values to be equal to one another. Constrain(ValueId, ValueId, Option), + /// Constrains two values to not be equal to one another. + ConstrainNotEqual(ValueId, ValueId, Option), + /// Range constrain `value` to `max_bit_size` RangeCheck { value: ValueId, max_bit_size: u32, assert_message: Option }, @@ -364,6 +367,7 @@ impl Instruction { InstructionResultType::Operand(*value) } Instruction::Constrain(..) + | Instruction::ConstrainNotEqual(..) | Instruction::Store { .. } | Instruction::IncrementRc { .. } | Instruction::DecrementRc { .. } @@ -405,17 +409,22 @@ impl Instruction { }, // These can fail. - Constrain(..) | RangeCheck { .. } => true, + Constrain(..) | ConstrainNotEqual(..) | RangeCheck { .. } => true, // This should never be side-effectful MakeArray { .. } | Noop => false, // Some binary math can overflow or underflow Binary(binary) => match binary.operator { - BinaryOp::Add | BinaryOp::Sub | BinaryOp::Mul | BinaryOp::Div | BinaryOp::Mod => { - true - } - BinaryOp::Eq + BinaryOp::Add { unchecked: false } + | BinaryOp::Sub { unchecked: false } + | BinaryOp::Mul { unchecked: false } + | BinaryOp::Div + | BinaryOp::Mod => true, + BinaryOp::Add { unchecked: true } + | BinaryOp::Sub { unchecked: true } + | BinaryOp::Mul { unchecked: true } + | BinaryOp::Eq | BinaryOp::Lt | BinaryOp::And | BinaryOp::Or @@ -467,7 +476,7 @@ impl Instruction { }, // We can deduplicate these instructions if we know the predicate is also the same. - Constrain(..) | RangeCheck { .. } => deduplicate_with_predicate, + Constrain(..) | ConstrainNotEqual(..) | RangeCheck { .. } => deduplicate_with_predicate, // Noop instructions can always be deduplicated, although they're more likely to be // removed entirely. @@ -497,7 +506,7 @@ impl Instruction { } } - pub(crate) fn can_eliminate_if_unused(&self, function: &Function) -> bool { + pub(crate) fn can_eliminate_if_unused(&self, function: &Function, flattened: bool) -> bool { use Instruction::*; match self { Binary(binary) => { @@ -530,11 +539,11 @@ impl Instruction { // pass where this check is done, but does mean that we cannot perform mem2reg // after the DIE pass. Store { .. } => { - matches!(function.runtime(), RuntimeType::Acir(_)) - && function.reachable_blocks().len() == 1 + flattened && function.runtime().is_acir() && function.reachable_blocks().len() == 1 } Constrain(..) + | ConstrainNotEqual(..) | EnableSideEffectsIf { .. } | IncrementRc { .. } | DecrementRc { .. } @@ -566,16 +575,19 @@ impl Instruction { match self { Instruction::Binary(binary) => { match binary.operator { - BinaryOp::Add - | BinaryOp::Sub - | BinaryOp::Mul + BinaryOp::Add { unchecked: false } + | BinaryOp::Sub { unchecked: false } + | BinaryOp::Mul { unchecked: false } | BinaryOp::Div | BinaryOp::Mod => { // Some binary math can overflow or underflow, but this is only the case // for unsigned types (here we assume the type of binary.lhs is the same) dfg.type_of_value(binary.rhs).is_unsigned() } - BinaryOp::Eq + BinaryOp::Add { unchecked: true } + | BinaryOp::Sub { unchecked: true } + | BinaryOp::Mul { unchecked: true } + | BinaryOp::Eq | BinaryOp::Lt | BinaryOp::And | BinaryOp::Or @@ -602,6 +614,7 @@ impl Instruction { Instruction::Cast(_, _) | Instruction::Not(_) | Instruction::Truncate { .. } + | Instruction::ConstrainNotEqual(..) | Instruction::Constrain(_, _, _) | Instruction::RangeCheck { .. } | Instruction::Allocate @@ -648,6 +661,22 @@ impl Instruction { }); Instruction::Constrain(lhs, rhs, assert_message) } + Instruction::ConstrainNotEqual(lhs, rhs, assert_message) => { + // Must map the `lhs` and `rhs` first as the value `f` is moved with the closure + let lhs = f(*lhs); + let rhs = f(*rhs); + let assert_message = assert_message.as_ref().map(|error| match error { + ConstrainError::Dynamic(selector, is_string, payload_values) => { + ConstrainError::Dynamic( + *selector, + *is_string, + payload_values.iter().map(|&value| f(value)).collect(), + ) + } + _ => error.clone(), + }); + Instruction::ConstrainNotEqual(lhs, rhs, assert_message) + } Instruction::Call { func, arguments } => Instruction::Call { func: f(*func), arguments: vecmap(arguments.iter().copied(), f), @@ -706,7 +735,8 @@ impl Instruction { Instruction::Truncate { value, bit_size: _, max_bit_size: _ } => { *value = f(*value); } - Instruction::Constrain(lhs, rhs, assert_message) => { + Instruction::Constrain(lhs, rhs, assert_message) + | Instruction::ConstrainNotEqual(lhs, rhs, assert_message) => { *lhs = f(*lhs); *rhs = f(*rhs); if let Some(ConstrainError::Dynamic(_, _, payload_values)) = assert_message { @@ -778,7 +808,8 @@ impl Instruction { | Instruction::Load { address: value } => { f(*value); } - Instruction::Constrain(lhs, rhs, assert_error) => { + Instruction::Constrain(lhs, rhs, assert_error) + | Instruction::ConstrainNotEqual(lhs, rhs, assert_error) => { f(*lhs); f(*rhs); if let Some(ConstrainError::Dynamic(_, _, values)) = assert_error.as_ref() { @@ -870,6 +901,7 @@ impl Instruction { SimplifiedToInstructionMultiple(constraints) } } + Instruction::ConstrainNotEqual(..) => None, Instruction::ArrayGet { array, index } => { if let Some(index) = dfg.get_numeric_constant(*index) { try_optimize_array_get_from_previous_set(dfg, *array, index) @@ -1058,12 +1090,6 @@ impl Instruction { Instruction::Noop => Remove, } } - - /// Some instructions are only to be used in Brillig and should be eliminated - /// after runtime separation, never to be be reintroduced in an ACIR runtime. - pub(crate) fn is_brillig_only(&self) -> bool { - matches!(self, Instruction::IncrementRc { .. } | Instruction::DecrementRc { .. }) - } } /// Given a chain of operations like: @@ -1091,28 +1117,27 @@ fn try_optimize_array_get_from_previous_set( // Arbitrary number of maximum tries just to prevent this optimization from taking too long. let max_tries = 5; for _ in 0..max_tries { - match &dfg[array_id] { - Value::Instruction { instruction, .. } => { - match &dfg[*instruction] { - Instruction::ArraySet { array, index, value, .. } => { - if let Some(constant) = dfg.get_numeric_constant(*index) { - if constant == target_index { - return SimplifyResult::SimplifiedTo(*value); - } - - array_id = *array; // recur - } else { - return SimplifyResult::None; + if let Some(instruction) = dfg.get_local_or_global_instruction(array_id) { + match instruction { + Instruction::ArraySet { array, index, value, .. } => { + if let Some(constant) = dfg.get_numeric_constant(*index) { + if constant == target_index { + return SimplifyResult::SimplifiedTo(*value); } + + array_id = *array; // recur + } else { + return SimplifyResult::None; } - Instruction::MakeArray { elements: array, typ: _ } => { - elements = Some(array.clone()); - break; - } - _ => return SimplifyResult::None, } + Instruction::MakeArray { elements: array, typ: _ } => { + elements = Some(array.clone()); + break; + } + _ => return SimplifyResult::None, } - _ => return SimplifyResult::None, + } else { + return SimplifyResult::None; } } diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction/binary.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction/binary.rs index 28e58e2cbb1..df1e8f537da 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction/binary.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction/binary.rs @@ -15,11 +15,11 @@ use super::{ #[derive(Debug, PartialEq, Eq, Hash, Copy, Clone, Serialize, Deserialize)] pub(crate) enum BinaryOp { /// Addition of lhs + rhs. - Add, + Add { unchecked: bool }, /// Subtraction of lhs - rhs. - Sub, + Sub { unchecked: bool }, /// Multiplication of lhs * rhs. - Mul, + Mul { unchecked: bool }, /// Division of lhs / rhs. Div, /// Modulus of lhs % rhs. @@ -48,9 +48,12 @@ pub(crate) enum BinaryOp { impl std::fmt::Display for BinaryOp { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - BinaryOp::Add => write!(f, "add"), - BinaryOp::Sub => write!(f, "sub"), - BinaryOp::Mul => write!(f, "mul"), + BinaryOp::Add { unchecked: false } => write!(f, "add"), + BinaryOp::Add { unchecked: true } => write!(f, "unchecked_add"), + BinaryOp::Sub { unchecked: false } => write!(f, "sub"), + BinaryOp::Sub { unchecked: true } => write!(f, "unchecked_sub"), + BinaryOp::Mul { unchecked: false } => write!(f, "mul"), + BinaryOp::Mul { unchecked: true } => write!(f, "unchecked_mul"), BinaryOp::Div => write!(f, "div"), BinaryOp::Eq => write!(f, "eq"), BinaryOp::Mod => write!(f, "mod"), @@ -86,28 +89,61 @@ impl Binary { /// Try to simplify this binary instruction, returning the new value if possible. pub(super) fn simplify(&self, dfg: &mut DataFlowGraph) -> SimplifyResult { - let lhs = dfg.get_numeric_constant(self.lhs); - let rhs = dfg.get_numeric_constant(self.rhs); - let operand_type = dfg.type_of_value(self.lhs).unwrap_numeric(); + let lhs_value = dfg.get_numeric_constant(self.lhs); + let rhs_value = dfg.get_numeric_constant(self.rhs); + + let lhs_type = dfg.type_of_value(self.lhs).unwrap_numeric(); + let rhs_type = dfg.type_of_value(self.rhs).unwrap_numeric(); + + let operator = self.operator; + if operator != BinaryOp::Shl && operator != BinaryOp::Shr { + assert_eq!( + lhs_type, rhs_type, + "ICE - Binary instruction operands must have the same type" + ); + } + + let operator = if lhs_type == NumericType::NativeField { + // Unchecked operations between fields or bools don't make sense, so we convert those to non-unchecked + // to reduce noise and confusion in the generated SSA. + match operator { + BinaryOp::Add { unchecked: true } => BinaryOp::Add { unchecked: false }, + BinaryOp::Sub { unchecked: true } => BinaryOp::Sub { unchecked: false }, + BinaryOp::Mul { unchecked: true } => BinaryOp::Mul { unchecked: false }, + _ => operator, + } + } else if lhs_type == NumericType::bool() { + // Unchecked mul between bools doesn't make sense, so we convert that to non-unchecked + if let BinaryOp::Mul { unchecked: true } = operator { + BinaryOp::Mul { unchecked: false } + } else { + operator + } + } else { + operator + }; + + // We never return `SimplifyResult::None` here because `operator` might have changed. + let simplified = Instruction::Binary(Binary { lhs: self.lhs, rhs: self.rhs, operator }); - if let (Some(lhs), Some(rhs)) = (lhs, rhs) { - return match eval_constant_binary_op(lhs, rhs, self.operator, operand_type) { + if let (Some(lhs), Some(rhs)) = (lhs_value, rhs_value) { + return match eval_constant_binary_op(lhs, rhs, operator, lhs_type) { Some((result, result_type)) => { let value = dfg.make_constant(result, result_type); SimplifyResult::SimplifiedTo(value) } - None => SimplifyResult::None, + None => SimplifyResult::SimplifiedToInstruction(simplified), }; } - let lhs_is_zero = lhs.map_or(false, |lhs| lhs.is_zero()); - let rhs_is_zero = rhs.map_or(false, |rhs| rhs.is_zero()); + let lhs_is_zero = lhs_value.map_or(false, |lhs| lhs.is_zero()); + let rhs_is_zero = rhs_value.map_or(false, |rhs| rhs.is_zero()); - let lhs_is_one = lhs.map_or(false, |lhs| lhs.is_one()); - let rhs_is_one = rhs.map_or(false, |rhs| rhs.is_one()); + let lhs_is_one = lhs_value.map_or(false, |lhs| lhs.is_one()); + let rhs_is_one = rhs_value.map_or(false, |rhs| rhs.is_one()); match self.operator { - BinaryOp::Add => { + BinaryOp::Add { .. } => { if lhs_is_zero { return SimplifyResult::SimplifiedTo(self.rhs); } @@ -115,12 +151,12 @@ impl Binary { return SimplifyResult::SimplifiedTo(self.lhs); } } - BinaryOp::Sub => { + BinaryOp::Sub { .. } => { if rhs_is_zero { return SimplifyResult::SimplifiedTo(self.lhs); } } - BinaryOp::Mul => { + BinaryOp::Mul { .. } => { if lhs_is_one { return SimplifyResult::SimplifiedTo(self.rhs); } @@ -128,7 +164,7 @@ impl Binary { return SimplifyResult::SimplifiedTo(self.lhs); } if lhs_is_zero || rhs_is_zero { - let zero = dfg.make_constant(FieldElement::zero(), operand_type); + let zero = dfg.make_constant(FieldElement::zero(), lhs_type); return SimplifyResult::SimplifiedTo(zero); } if dfg.get_value_max_num_bits(self.lhs) == 1 { @@ -141,7 +177,7 @@ impl Binary { if let Instruction::Binary(Binary { lhs, rhs, operator }) = dfg[*instruction] { - if operator == BinaryOp::Mul + if matches!(operator, BinaryOp::Mul { .. }) && (dfg.resolve(self.lhs) == dfg.resolve(lhs) || dfg.resolve(self.lhs) == dfg.resolve(rhs)) { @@ -156,7 +192,7 @@ impl Binary { if let Instruction::Binary(Binary { lhs, rhs, operator }) = dfg[*instruction] { - if operator == BinaryOp::Mul + if matches!(operator, BinaryOp::Mul { .. }) && (dfg.resolve(self.rhs) == dfg.resolve(lhs) || dfg.resolve(self.rhs) == dfg.resolve(rhs)) { @@ -173,13 +209,13 @@ impl Binary { } BinaryOp::Mod => { if rhs_is_one { - let zero = dfg.make_constant(FieldElement::zero(), operand_type); + let zero = dfg.make_constant(FieldElement::zero(), lhs_type); return SimplifyResult::SimplifiedTo(zero); } - if operand_type.is_unsigned() { + if lhs_type.is_unsigned() { // lhs % 2**bit_size is equivalent to truncating `lhs` to `bit_size` bits. // We then convert to a truncation for consistency, allowing more optimizations. - if let Some(modulus) = rhs { + if let Some(modulus) = rhs_value { let modulus = modulus.to_u128(); if modulus.is_power_of_two() { let bit_size = modulus.ilog2(); @@ -187,7 +223,7 @@ impl Binary { Instruction::Truncate { value: self.lhs, bit_size, - max_bit_size: operand_type.bit_size(), + max_bit_size: lhs_type.bit_size(), }, ); } @@ -200,7 +236,7 @@ impl Binary { return SimplifyResult::SimplifiedTo(one); } - if operand_type == NumericType::bool() { + if lhs_type == NumericType::bool() { // Simplify forms of `(boolean == true)` into `boolean` if lhs_is_one { return SimplifyResult::SimplifiedTo(self.rhs); @@ -222,13 +258,13 @@ impl Binary { let zero = dfg.make_constant(FieldElement::zero(), NumericType::bool()); return SimplifyResult::SimplifiedTo(zero); } - if operand_type.is_unsigned() { + if lhs_type.is_unsigned() { if rhs_is_zero { // Unsigned values cannot be less than zero. let zero = dfg.make_constant(FieldElement::zero(), NumericType::bool()); return SimplifyResult::SimplifiedTo(zero); } else if rhs_is_one { - let zero = dfg.make_constant(FieldElement::zero(), operand_type); + let zero = dfg.make_constant(FieldElement::zero(), lhs_type); return SimplifyResult::SimplifiedToInstruction(Instruction::binary( BinaryOp::Eq, self.lhs, @@ -239,35 +275,37 @@ impl Binary { } BinaryOp::And => { if lhs_is_zero || rhs_is_zero { - let zero = dfg.make_constant(FieldElement::zero(), operand_type); + let zero = dfg.make_constant(FieldElement::zero(), lhs_type); return SimplifyResult::SimplifiedTo(zero); } if dfg.resolve(self.lhs) == dfg.resolve(self.rhs) { return SimplifyResult::SimplifiedTo(self.lhs); } - if operand_type == NumericType::bool() { + if lhs_type == NumericType::bool() { // Boolean AND is equivalent to multiplication, which is a cheaper operation. - let instruction = Instruction::binary(BinaryOp::Mul, self.lhs, self.rhs); + // (mul unchecked because these are bools so it doesn't matter really) + let instruction = + Instruction::binary(BinaryOp::Mul { unchecked: true }, self.lhs, self.rhs); return SimplifyResult::SimplifiedToInstruction(instruction); } - if operand_type.is_unsigned() { + if lhs_type.is_unsigned() { // It's common in other programming languages to truncate values to a certain bit size using // a bitwise AND with a bit mask. However this operation is quite inefficient inside a snark. // // We then replace this bitwise operation with an equivalent truncation instruction. - match (lhs, rhs) { + match (lhs_value, rhs_value) { (Some(bitmask), None) | (None, Some(bitmask)) => { // This substitution requires the bitmask to retain all of the lower bits. // The bitmask must then be one less than a power of 2. let bitmask_plus_one = bitmask.to_u128() + 1; if bitmask_plus_one.is_power_of_two() { - let value = if lhs.is_some() { self.rhs } else { self.lhs }; + let value = if lhs_value.is_some() { self.rhs } else { self.lhs }; let num_bits = bitmask_plus_one.ilog2(); return SimplifyResult::SimplifiedToInstruction( Instruction::Truncate { value, bit_size: num_bits, - max_bit_size: operand_type.bit_size(), + max_bit_size: lhs_type.bit_size(), }, ); } @@ -284,8 +322,8 @@ impl Binary { if rhs_is_zero { return SimplifyResult::SimplifiedTo(self.lhs); } - if operand_type == NumericType::bool() && (lhs_is_one || rhs_is_one) { - let one = dfg.make_constant(FieldElement::one(), operand_type); + if lhs_type == NumericType::bool() && (lhs_is_one || rhs_is_one) { + let one = dfg.make_constant(FieldElement::one(), lhs_type); return SimplifyResult::SimplifiedTo(one); } if dfg.resolve(self.lhs) == dfg.resolve(self.rhs) { @@ -300,24 +338,24 @@ impl Binary { return SimplifyResult::SimplifiedTo(self.lhs); } if dfg.resolve(self.lhs) == dfg.resolve(self.rhs) { - let zero = dfg.make_constant(FieldElement::zero(), operand_type); + let zero = dfg.make_constant(FieldElement::zero(), lhs_type); return SimplifyResult::SimplifiedTo(zero); } } - BinaryOp::Shl => return SimplifyResult::None, + BinaryOp::Shl => return SimplifyResult::SimplifiedToInstruction(simplified), BinaryOp::Shr => { // Bit shifts by constants can be treated as divisions. - if let Some(rhs_const) = rhs { - if rhs_const >= FieldElement::from(operand_type.bit_size() as u128) { + if let Some(rhs_const) = rhs_value { + if rhs_const >= FieldElement::from(lhs_type.bit_size() as u128) { // Shifting by the full width of the operand type, any `lhs` goes to zero. - let zero = dfg.make_constant(FieldElement::zero(), operand_type); + let zero = dfg.make_constant(FieldElement::zero(), lhs_type); return SimplifyResult::SimplifiedTo(zero); } - return SimplifyResult::None; + return SimplifyResult::SimplifiedToInstruction(simplified); } } }; - SimplifyResult::None + SimplifyResult::SimplifiedToInstruction(simplified) } /// Check if unsigned overflow is possible, and if so return some message to be used if it fails. @@ -331,14 +369,14 @@ impl Binary { let max_rhs_bits = dfg.get_value_max_num_bits(self.rhs); let msg = match self.operator { - BinaryOp::Add => { + BinaryOp::Add { unchecked: false } => { if std::cmp::max(max_lhs_bits, max_rhs_bits) < bit_size { // `lhs` and `rhs` have both been casted up from smaller types and so cannot overflow. return None; } "attempt to add with overflow" } - BinaryOp::Sub => { + BinaryOp::Sub { unchecked: false } => { if dfg.is_constant(self.lhs) && max_lhs_bits > max_rhs_bits { // `lhs` is a fixed constant and `rhs` is restricted such that `lhs - rhs > 0` // Note strict inequality as `rhs > lhs` while `max_lhs_bits == max_rhs_bits` is possible. @@ -346,7 +384,7 @@ impl Binary { } "attempt to subtract with overflow" } - BinaryOp::Mul => { + BinaryOp::Mul { unchecked: false } => { if bit_size == 1 || max_lhs_bits + max_rhs_bits <= bit_size || max_lhs_bits == 1 @@ -470,9 +508,9 @@ fn truncate(int: u128, bit_size: u32) -> u128 { impl BinaryOp { fn get_field_function(self) -> Option FieldElement> { match self { - BinaryOp::Add => Some(std::ops::Add::add), - BinaryOp::Sub => Some(std::ops::Sub::sub), - BinaryOp::Mul => Some(std::ops::Mul::mul), + BinaryOp::Add { .. } => Some(std::ops::Add::add), + BinaryOp::Sub { .. } => Some(std::ops::Sub::sub), + BinaryOp::Mul { .. } => Some(std::ops::Mul::mul), BinaryOp::Div => Some(std::ops::Div::div), BinaryOp::Eq => Some(|x, y| (x == y).into()), BinaryOp::Lt => Some(|x, y| (x < y).into()), @@ -488,9 +526,9 @@ impl BinaryOp { fn get_u128_function(self) -> fn(u128, u128) -> Option { match self { - BinaryOp::Add => u128::checked_add, - BinaryOp::Sub => u128::checked_sub, - BinaryOp::Mul => u128::checked_mul, + BinaryOp::Add { .. } => u128::checked_add, + BinaryOp::Sub { .. } => u128::checked_sub, + BinaryOp::Mul { .. } => u128::checked_mul, BinaryOp::Div => u128::checked_div, BinaryOp::Mod => u128::checked_rem, BinaryOp::And => |x, y| Some(x & y), @@ -505,9 +543,9 @@ impl BinaryOp { fn get_i128_function(self) -> fn(i128, i128) -> Option { match self { - BinaryOp::Add => i128::checked_add, - BinaryOp::Sub => i128::checked_sub, - BinaryOp::Mul => i128::checked_mul, + BinaryOp::Add { .. } => i128::checked_add, + BinaryOp::Sub { .. } => i128::checked_sub, + BinaryOp::Mul { .. } => i128::checked_mul, BinaryOp::Div => i128::checked_div, BinaryOp::Mod => i128::checked_rem, BinaryOp::And => |x, y| Some(x & y), diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs index f1ba21f1c1d..992c633ffcd 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs @@ -142,8 +142,7 @@ pub(super) fn simplify_call( slice.push_back(*elem); } - let new_slice_length = - update_slice_length(arguments[0], dfg, BinaryOp::Add, block); + let new_slice_length = increment_slice_length(arguments[0], dfg, block); let new_slice = make_array(dfg, slice, element_type, block, call_stack); return SimplifyResult::SimplifiedToMultiple(vec![new_slice_length, new_slice]); @@ -161,7 +160,7 @@ pub(super) fn simplify_call( slice.push_front(*elem); } - let new_slice_length = update_slice_length(arguments[0], dfg, BinaryOp::Add, block); + let new_slice_length = increment_slice_length(arguments[0], dfg, block); let new_slice = make_array(dfg, slice, element_type, block, call_stack); SimplifyResult::SimplifiedToMultiple(vec![new_slice_length, new_slice]) @@ -201,7 +200,7 @@ pub(super) fn simplify_call( slice.pop_front().expect("There are no elements in this slice to be removed") }); - let new_slice_length = update_slice_length(arguments[0], dfg, BinaryOp::Sub, block); + let new_slice_length = decrement_slice_length(arguments[0], dfg, block); results.push(new_slice_length); @@ -234,7 +233,7 @@ pub(super) fn simplify_call( index += 1; } - let new_slice_length = update_slice_length(arguments[0], dfg, BinaryOp::Add, block); + let new_slice_length = increment_slice_length(arguments[0], dfg, block); let new_slice = make_array(dfg, slice, typ, block, call_stack); SimplifyResult::SimplifiedToMultiple(vec![new_slice_length, new_slice]) @@ -272,7 +271,7 @@ pub(super) fn simplify_call( let new_slice = make_array(dfg, slice, typ, block, call_stack); results.insert(0, new_slice); - let new_slice_length = update_slice_length(arguments[0], dfg, BinaryOp::Sub, block); + let new_slice_length = decrement_slice_length(arguments[0], dfg, block); results.insert(0, new_slice_length); @@ -388,6 +387,22 @@ fn update_slice_length( dfg.insert_instruction_and_results(instruction, block, None, call_stack).first() } +fn increment_slice_length( + slice_len: ValueId, + dfg: &mut DataFlowGraph, + block: BasicBlockId, +) -> ValueId { + update_slice_length(slice_len, dfg, BinaryOp::Add { unchecked: false }, block) +} + +fn decrement_slice_length( + slice_len: ValueId, + dfg: &mut DataFlowGraph, + block: BasicBlockId, +) -> ValueId { + update_slice_length(slice_len, dfg, BinaryOp::Sub { unchecked: true }, block) +} + fn simplify_slice_push_back( mut slice: im::Vector, element_type: Type, @@ -408,7 +423,7 @@ fn simplify_slice_push_back( .insert_instruction_and_results(len_not_equals_capacity_instr, block, None, call_stack) .first(); - let new_slice_length = update_slice_length(arguments[0], dfg, BinaryOp::Add, block); + let new_slice_length = increment_slice_length(arguments[0], dfg, block); for elem in &arguments[2..] { slice.push_back(*elem); @@ -457,14 +472,17 @@ fn simplify_slice_pop_back( let element_count = element_types.len(); let mut results = VecDeque::with_capacity(element_count + 1); - let new_slice_length = update_slice_length(arguments[0], dfg, BinaryOp::Sub, block); + let new_slice_length = decrement_slice_length(arguments[0], dfg, block); let element_size = dfg.make_constant((element_count as u128).into(), NumericType::length_type()); - let flattened_len_instr = Instruction::binary(BinaryOp::Mul, arguments[0], element_size); + // Compute the flattened length doing an unchecked mul + // (it shouldn't overflow because it would have overflowed before when the slice was created) + let flattened_len_instr = + Instruction::binary(BinaryOp::Mul { unchecked: true }, arguments[0], element_size); let mut flattened_len = dfg.insert_instruction_and_results(flattened_len_instr, block, None, call_stack).first(); - flattened_len = update_slice_length(flattened_len, dfg, BinaryOp::Sub, block); + flattened_len = decrement_slice_length(flattened_len, dfg, block); // We must pop multiple elements in the case of a slice of tuples // Iterating through element types in reverse here since we're popping from the end @@ -478,7 +496,7 @@ fn simplify_slice_pop_back( .first(); results.push_front(get_last_elem); - flattened_len = update_slice_length(flattened_len, dfg, BinaryOp::Sub, block); + flattened_len = decrement_slice_length(flattened_len, dfg, block); } results.push_front(arguments[1]); diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction/constrain.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction/constrain.rs index 5ae6a642a57..a3881419a83 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction/constrain.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction/constrain.rs @@ -44,7 +44,7 @@ pub(super) fn decompose_constrain( vec![Instruction::Constrain(lhs, rhs, msg.clone())] } - Instruction::Binary(Binary { lhs, rhs, operator: BinaryOp::Mul }) + Instruction::Binary(Binary { lhs, rhs, operator: BinaryOp::Mul { .. } }) if constant.is_one() && dfg.type_of_value(lhs) == Type::bool() => { // Replace an equality assertion on a boolean multiplication diff --git a/compiler/noirc_evaluator/src/ssa/ir/map.rs b/compiler/noirc_evaluator/src/ssa/ir/map.rs index 1d637309191..b6da107957c 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/map.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/map.rs @@ -190,7 +190,7 @@ impl DenseMap { /// Gets an iterator to a reference to each element in the dense map paired with its id. /// /// The id-element pairs are ordered by the numeric values of the ids. - pub(crate) fn iter(&self) -> impl ExactSizeIterator, &T)> { + pub(crate) fn iter(&self) -> impl DoubleEndedIterator, &T)> { let ids_iter = (0..self.storage.len() as u32).map(|idx| Id::new(idx)); ids_iter.zip(self.storage.iter()) } diff --git a/compiler/noirc_evaluator/src/ssa/ir/post_order.rs b/compiler/noirc_evaluator/src/ssa/ir/post_order.rs index 398ce887b96..08f195e53d1 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/post_order.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/post_order.rs @@ -13,6 +13,7 @@ enum Visit { Last, } +#[derive(Default)] pub(crate) struct PostOrder(Vec); impl PostOrder { diff --git a/compiler/noirc_evaluator/src/ssa/ir/printer.rs b/compiler/noirc_evaluator/src/ssa/ir/printer.rs index 9bf26b8414d..88bee0799a3 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/printer.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/printer.rs @@ -1,11 +1,14 @@ //! This file is for pretty-printing the SSA IR in a human-readable form for debugging. -use std::fmt::{Formatter, Result}; +use std::fmt::{Display, Formatter, Result}; use acvm::acir::AcirField; use im::Vector; use iter_extended::vecmap; -use crate::ssa::ir::types::{NumericType, Type}; +use crate::ssa::{ + ir::types::{NumericType, Type}, + Ssa, +}; use super::{ basic_block::BasicBlockId, @@ -15,8 +18,42 @@ use super::{ value::{Value, ValueId}, }; +impl Display for Ssa { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + for (id, global_value) in self.globals.dfg.values_iter() { + match global_value { + Value::NumericConstant { constant, typ } => { + writeln!(f, "g{} = {typ} {constant}", id.to_u32())?; + } + Value::Instruction { instruction, .. } => { + display_instruction(&self.globals.dfg, *instruction, true, f)?; + } + Value::Global(_) => { + panic!("Value::Global should only be in the function dfg"); + } + _ => panic!("Expected only numeric constant or instruction"), + }; + } + + if self.globals.dfg.values_iter().next().is_some() { + writeln!(f)?; + } + + for function in self.functions.values() { + writeln!(f, "{function}")?; + } + Ok(()) + } +} + +impl Display for Function { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + display_function(self, f) + } +} + /// Helper function for Function's Display impl to pretty-print the function with the given formatter. -pub(crate) fn display_function(function: &Function, f: &mut Formatter) -> Result { +fn display_function(function: &Function, f: &mut Formatter) -> Result { writeln!(f, "{} fn {} {} {{", function.runtime(), function.name(), function.id())?; for block_id in function.reachable_blocks() { display_block(&function.dfg, block_id, f)?; @@ -25,17 +62,13 @@ pub(crate) fn display_function(function: &Function, f: &mut Formatter) -> Result } /// Display a single block. This will not display the block's successors. -pub(crate) fn display_block( - dfg: &DataFlowGraph, - block_id: BasicBlockId, - f: &mut Formatter, -) -> Result { +fn display_block(dfg: &DataFlowGraph, block_id: BasicBlockId, f: &mut Formatter) -> Result { let block = &dfg[block_id]; writeln!(f, " {}({}):", block_id, value_list_with_types(dfg, block.parameters()))?; for instruction in block.instructions() { - display_instruction(dfg, *instruction, f)?; + display_instruction(dfg, *instruction, false, f)?; } display_terminator(dfg, block.terminator(), f) @@ -52,7 +85,16 @@ fn value(dfg: &DataFlowGraph, id: ValueId) -> String { Value::Function(id) => id.to_string(), Value::Intrinsic(intrinsic) => intrinsic.to_string(), Value::ForeignFunction(function) => function.clone(), - Value::Param { .. } | Value::Instruction { .. } => id.to_string(), + Value::Param { .. } | Value::Instruction { .. } => { + if dfg.is_global(id) { + format!("g{}", id.to_u32()) + } else { + id.to_string() + } + } + Value::Global(_) => { + format!("g{}", id.to_u32()) + } } } @@ -72,7 +114,7 @@ fn value_list(dfg: &DataFlowGraph, values: &[ValueId]) -> String { } /// Display a terminator instruction -pub(crate) fn display_terminator( +fn display_terminator( dfg: &DataFlowGraph, terminator: Option<&TerminatorInstruction>, f: &mut Formatter, @@ -107,26 +149,34 @@ pub(crate) fn display_terminator( } /// Display an arbitrary instruction -pub(crate) fn display_instruction( +fn display_instruction( dfg: &DataFlowGraph, instruction: InstructionId, + in_global_space: bool, f: &mut Formatter, ) -> Result { - // instructions are always indented within a function - write!(f, " ")?; + if !in_global_space { + // instructions are always indented within a function + write!(f, " ")?; + } let results = dfg.instruction_results(instruction); if !results.is_empty() { - write!(f, "{} = ", value_list(dfg, results))?; + let mut value_list = value_list(dfg, results); + if in_global_space { + value_list = value_list.replace('v', "g"); + } + write!(f, "{} = ", value_list)?; } - display_instruction_inner(dfg, &dfg[instruction], results, f) + display_instruction_inner(dfg, &dfg[instruction], results, in_global_space, f) } fn display_instruction_inner( dfg: &DataFlowGraph, instruction: &Instruction, results: &[ValueId], + in_global_space: bool, f: &mut Formatter, ) -> Result { let show = |id| value(dfg, id); @@ -149,6 +199,14 @@ fn display_instruction_inner( writeln!(f) } } + Instruction::ConstrainNotEqual(lhs, rhs, error) => { + write!(f, "constrain {} != {}", show(*lhs), show(*rhs))?; + if let Some(error) = error { + display_constrain_error(dfg, error, f) + } else { + writeln!(f) + } + } Instruction::Call { func, arguments } => { let arguments = value_list(dfg, arguments); writeln!(f, "call {}({}){}", show(*func), arguments, result_types(dfg, results)) @@ -229,7 +287,11 @@ fn display_instruction_inner( if i != 0 { write!(f, ", ")?; } - write!(f, "{}", show(*element))?; + let mut value = show(*element); + if in_global_space { + value = value.replace('v', "g"); + } + write!(f, "{}", value)?; } writeln!(f, "] : {typ}") @@ -274,22 +336,11 @@ pub(crate) fn try_to_extract_string_from_error_payload( values: &[ValueId], dfg: &DataFlowGraph, ) -> Option { - (is_string_type && (values.len() == 1)) - .then_some(()) - .and_then(|()| { - let (values, _) = &dfg.get_array_constant(values[0])?; - let values = values.iter().map(|value_id| dfg.get_numeric_constant(*value_id)); - values.collect::>>() - }) - .map(|fields| { - fields - .iter() - .map(|field| { - let as_u8 = field.try_to_u64().unwrap_or_default() as u8; - as_u8 as char - }) - .collect() - }) + if is_string_type && values.len() == 1 { + dfg.get_string(values[0]) + } else { + None + } } fn display_constrain_error( diff --git a/compiler/noirc_evaluator/src/ssa/ir/value.rs b/compiler/noirc_evaluator/src/ssa/ir/value.rs index 39c63e3efcd..53f87a260c3 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/value.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/value.rs @@ -53,6 +53,9 @@ pub(crate) enum Value { /// ForeignFunction's always have the type Type::Function and have similar semantics to Function, /// other than generating different backend operations and being only accessible through Brillig. ForeignFunction(String), + + /// This Value indicates we have a reserved slot that needs to be accessed in a separate global context + Global(Type), } impl Value { @@ -64,6 +67,7 @@ impl Value { Value::Function { .. } | Value::Intrinsic { .. } | Value::ForeignFunction { .. } => { Cow::Owned(Type::Function) } + Value::Global(typ) => Cow::Borrowed(typ), } } } diff --git a/compiler/noirc_evaluator/src/ssa/opt/assert_constant.rs b/compiler/noirc_evaluator/src/ssa/opt/assert_constant.rs index 6936c7ad542..192c0f59344 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/assert_constant.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/assert_constant.rs @@ -122,7 +122,11 @@ fn evaluate_static_assert( } else { let call_stack = function.dfg.get_instruction_call_stack(instruction); if function.dfg.is_constant(arguments[0]) { - Err(RuntimeError::StaticAssertFailed { call_stack }) + let message = function + .dfg + .get_string(arguments[1]) + .expect("Expected second argument to be a string"); + Err(RuntimeError::StaticAssertFailed { message, call_stack }) } else { Err(RuntimeError::StaticAssertDynamicPredicate { call_stack }) } diff --git a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs index db249f3bc3a..e8cae7da5b5 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs @@ -307,6 +307,7 @@ impl<'brillig> Context<'brillig> { let old_results = dfg.instruction_results(id).to_vec(); // If a copy of this instruction exists earlier in the block, then reuse the previous results. + let runtime_is_brillig = dfg.runtime().is_brillig(); if let Some(cache_result) = self.get_cached(dfg, dom, &instruction, *side_effects_enabled_var, block) { @@ -314,7 +315,7 @@ impl<'brillig> Context<'brillig> { CacheResult::Cached(cached) => { // We track whether we may mutate MakeArray instructions before we deduplicate // them but we still need to issue an extra inc_rc in case they're mutated afterward. - if matches!(instruction, Instruction::MakeArray { .. }) { + if runtime_is_brillig && matches!(instruction, Instruction::MakeArray { .. }) { let value = *cached.last().unwrap(); let inc_rc = Instruction::IncrementRc { value }; let call_stack = dfg.get_instruction_call_stack_id(id); @@ -422,14 +423,18 @@ impl<'brillig> Context<'brillig> { .then(|| vecmap(old_results, |result| dfg.type_of_value(*result))); let call_stack = dfg.get_instruction_call_stack_id(id); - let new_results = - match dfg.insert_instruction_and_results(instruction, block, ctrl_typevars, call_stack) - { - InsertInstructionResult::SimplifiedTo(new_result) => vec![new_result], - InsertInstructionResult::SimplifiedToMultiple(new_results) => new_results, - InsertInstructionResult::Results(_, new_results) => new_results.to_vec(), - InsertInstructionResult::InstructionRemoved => vec![], - }; + let new_results = match dfg.insert_instruction_and_results_if_simplified( + instruction, + block, + ctrl_typevars, + call_stack, + Some(id), + ) { + InsertInstructionResult::SimplifiedTo(new_result) => vec![new_result], + InsertInstructionResult::SimplifiedToMultiple(new_results) => new_results, + InsertInstructionResult::Results(_, new_results) => new_results.to_vec(), + InsertInstructionResult::InstructionRemoved => vec![], + }; // Optimizations while inserting the instruction should not change the number of results. assert_eq!(old_results.len(), new_results.len()); @@ -560,7 +565,7 @@ impl<'brillig> Context<'brillig> { ); match evaluation_result { - EvaluationResult::NotABrilligCall | EvaluationResult::CannotEvaluate(_) => None, + EvaluationResult::NotABrilligCall | EvaluationResult::CannotEvaluate => None, EvaluationResult::Evaluated(memory_values) => { let mut memory_index = 0; let new_results = vecmap(old_results, |old_result| { @@ -601,14 +606,14 @@ impl<'brillig> Context<'brillig> { }; if !arguments.iter().all(|argument| dfg.is_constant(*argument)) { - return EvaluationResult::CannotEvaluate(*func_id); + return EvaluationResult::CannotEvaluate; } let mut brillig_arguments = Vec::new(); for argument in arguments { let typ = dfg.type_of_value(*argument); let Some(parameter) = type_to_brillig_parameter(&typ) else { - return EvaluationResult::CannotEvaluate(*func_id); + return EvaluationResult::CannotEvaluate; }; brillig_arguments.push(parameter); } @@ -617,12 +622,12 @@ impl<'brillig> Context<'brillig> { for return_id in func.returns().iter() { let typ = func.dfg.type_of_value(*return_id); if type_to_brillig_parameter(&typ).is_none() { - return EvaluationResult::CannotEvaluate(*func_id); + return EvaluationResult::CannotEvaluate; } } let Ok(generated_brillig) = gen_brillig_for(func, brillig_arguments, brillig) else { - return EvaluationResult::CannotEvaluate(*func_id); + return EvaluationResult::CannotEvaluate; }; let mut calldata = Vec::new(); @@ -639,7 +644,7 @@ impl<'brillig> Context<'brillig> { VM::new(calldata, bytecode, foreign_call_results, &black_box_solver, profiling_active); let vm_status: VMStatus<_> = vm.process_opcodes(); let VMStatus::Finished { return_data_offset, return_data_size } = vm_status else { - return EvaluationResult::CannotEvaluate(*func_id); + return EvaluationResult::CannotEvaluate; }; let memory = @@ -771,7 +776,7 @@ enum EvaluationResult { NotABrilligCall, /// The instruction was a call to a brillig function, but we couldn't evaluate it. /// This can occur in the situation where the brillig function reaches a "trap" or a foreign call opcode. - CannotEvaluate(FunctionId), + CannotEvaluate, /// The instruction was a call to a brillig function and we were able to evaluate it, /// returning evaluation memory values. Evaluated(Vec>), @@ -1565,7 +1570,6 @@ mod test { v10 = mul v0, v9 // attaching `c` to `a` v11 = call to_be_radix(v10, u32 256) -> [u8; 1] // calling `to_radix(c * a)` inc_rc v11 - enable_side_effects v2 // side effect var for `c` shifted down by removal return } "; @@ -1580,7 +1584,6 @@ mod test { v7 = mul v0, v6 v8 = call to_be_radix(v7, u32 256) -> [u8; 1] inc_rc v8 - enable_side_effects v2 return } "; diff --git a/compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs b/compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs index 7d7798fd30a..4afddbef41a 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs @@ -8,12 +8,13 @@ use std::collections::{BTreeMap, BTreeSet, HashSet}; use acvm::FieldElement; use iter_extended::vecmap; +use noirc_frontend::monomorphization::ast::InlineType; use crate::ssa::{ function_builder::FunctionBuilder, ir::{ basic_block::BasicBlockId, - function::{Function, FunctionId, Signature}, + function::{Function, FunctionId, RuntimeType, Signature}, instruction::{BinaryOp, Instruction}, types::{NumericType, Type}, value::{Value, ValueId}, @@ -43,12 +44,15 @@ struct ApplyFunction { dispatches_to_multiple_functions: bool, } +type Variants = BTreeMap<(Signature, RuntimeType), Vec>; +type ApplyFunctions = HashMap<(Signature, RuntimeType), ApplyFunction>; + /// Performs defunctionalization on all functions /// This is done by changing all functions as value to be a number (FieldElement) /// And creating apply functions that dispatch to the correct target by runtime comparisons with constants #[derive(Debug, Clone)] struct DefunctionalizationContext { - apply_functions: HashMap, + apply_functions: ApplyFunctions, } impl Ssa { @@ -104,7 +108,7 @@ impl DefunctionalizationContext { }; // Find the correct apply function - let apply_function = self.get_apply_function(&signature); + let apply_function = self.get_apply_function(signature, func.runtime()); // Replace the instruction with a call to apply let apply_function_value_id = func.dfg.import_function(apply_function.id); @@ -152,19 +156,21 @@ impl DefunctionalizationContext { } /// Returns the apply function for the given signature - fn get_apply_function(&self, signature: &Signature) -> ApplyFunction { - *self.apply_functions.get(signature).expect("Could not find apply function") + fn get_apply_function(&self, signature: Signature, runtime: RuntimeType) -> ApplyFunction { + *self.apply_functions.get(&(signature, runtime)).expect("Could not find apply function") } } /// Collects all functions used as values that can be called by their signatures -fn find_variants(ssa: &Ssa) -> BTreeMap> { - let mut dynamic_dispatches: BTreeSet = BTreeSet::new(); +fn find_variants(ssa: &Ssa) -> Variants { + let mut dynamic_dispatches: BTreeSet<(Signature, RuntimeType)> = BTreeSet::new(); let mut functions_as_values: BTreeSet = BTreeSet::new(); for function in ssa.functions.values() { functions_as_values.extend(find_functions_as_values(function)); - dynamic_dispatches.extend(find_dynamic_dispatches(function)); + dynamic_dispatches.extend( + find_dynamic_dispatches(function).into_iter().map(|sig| (sig, function.runtime())), + ); } let mut signature_to_functions_as_value: BTreeMap> = BTreeMap::new(); @@ -174,16 +180,12 @@ fn find_variants(ssa: &Ssa) -> BTreeMap> { signature_to_functions_as_value.entry(signature).or_default().push(function_id); } - let mut variants = BTreeMap::new(); + let mut variants: Variants = BTreeMap::new(); - for dispatch_signature in dynamic_dispatches { - let mut target_fns = vec![]; - for (target_signature, functions) in &signature_to_functions_as_value { - if &dispatch_signature == target_signature { - target_fns.extend(functions); - } - } - variants.insert(dispatch_signature, target_fns); + for (dispatch_signature, caller_runtime) in dynamic_dispatches { + let target_fns = + signature_to_functions_as_value.get(&dispatch_signature).cloned().unwrap_or_default(); + variants.insert((dispatch_signature, caller_runtime), target_fns); } variants @@ -247,10 +249,10 @@ fn find_dynamic_dispatches(func: &Function) -> BTreeSet { fn create_apply_functions( ssa: &mut Ssa, - variants_map: BTreeMap>, -) -> HashMap { + variants_map: BTreeMap<(Signature, RuntimeType), Vec>, +) -> ApplyFunctions { let mut apply_functions = HashMap::default(); - for (signature, variants) in variants_map.into_iter() { + for ((signature, runtime), variants) in variants_map.into_iter() { assert!( !variants.is_empty(), "ICE: at least one variant should exist for a dynamic call {signature:?}" @@ -258,11 +260,12 @@ fn create_apply_functions( let dispatches_to_multiple_functions = variants.len() > 1; let id = if dispatches_to_multiple_functions { - create_apply_function(ssa, signature.clone(), variants) + create_apply_function(ssa, signature.clone(), runtime, variants) } else { variants[0] }; - apply_functions.insert(signature, ApplyFunction { id, dispatches_to_multiple_functions }); + apply_functions + .insert((signature, runtime), ApplyFunction { id, dispatches_to_multiple_functions }); } apply_functions } @@ -275,11 +278,22 @@ fn function_id_to_field(function_id: FunctionId) -> FieldElement { fn create_apply_function( ssa: &mut Ssa, signature: Signature, + caller_runtime: RuntimeType, function_ids: Vec, ) -> FunctionId { assert!(!function_ids.is_empty()); + let globals = ssa.functions[&function_ids[0]].dfg.globals.clone(); ssa.add_fn(|id| { let mut function_builder = FunctionBuilder::new("apply".to_string(), id); + function_builder.set_globals(globals); + + // We want to push for apply functions to be inlined more aggressively; + // they are expected to be optimized away by constants visible at the call site. + let runtime = match caller_runtime { + RuntimeType::Acir(_) => RuntimeType::Acir(InlineType::InlineAlways), + RuntimeType::Brillig(_) => RuntimeType::Brillig(InlineType::InlineAlways), + }; + function_builder.set_runtime(runtime); let target_id = function_builder.add_parameter(Type::field()); let params_ids = vecmap(signature.params, |typ| function_builder.add_parameter(typ)); @@ -337,22 +351,156 @@ fn create_apply_function( }) } -/// Crates a return block, if no previous return exists, it will create a final return -/// Else, it will create a bypass return block that points to the previous return block +/// If no previous return target exists, it will create a final return, +/// otherwise returns the existing return block to jump to. fn build_return_block( builder: &mut FunctionBuilder, previous_block: BasicBlockId, passed_types: &[Type], target: Option, ) -> BasicBlockId { + if let Some(return_block) = target { + return return_block; + } let return_block = builder.insert_block(); builder.switch_to_block(return_block); - let params = vecmap(passed_types, |typ| builder.add_block_parameter(return_block, typ.clone())); - match target { - None => builder.terminate_with_return(params), - Some(target) => builder.terminate_with_jmp(target, params), - } + builder.terminate_with_return(params); builder.switch_to_block(previous_block); return_block } + +#[cfg(test)] +mod tests { + use crate::ssa::opt::assert_normalized_ssa_equals; + + use super::Ssa; + + #[test] + fn apply_inherits_caller_runtime() { + // Extracted from `execution_success/brillig_fns_as_values` with `--force-brillig` + let src = " + brillig(inline) fn main f0 { + b0(v0: u32): + v3 = call f1(f2, v0) -> u32 + v5 = add v0, u32 1 + v6 = eq v3, v5 + constrain v3 == v5 + v9 = call f1(f3, v0) -> u32 + v10 = add v0, u32 1 + v11 = eq v9, v10 + constrain v9 == v10 + return + } + brillig(inline) fn wrapper f1 { + b0(v0: function, v1: u32): + v2 = call v0(v1) -> u32 + return v2 + } + brillig(inline) fn increment f2 { + b0(v0: u32): + v2 = add v0, u32 1 + return v2 + } + brillig(inline) fn increment_acir f3 { + b0(v0: u32): + v2 = add v0, u32 1 + return v2 + } + "; + + let ssa = Ssa::from_str(src).unwrap(); + let ssa = ssa.defunctionalize(); + + let expected = " + brillig(inline) fn main f0 { + b0(v0: u32): + v3 = call f1(Field 2, v0) -> u32 + v5 = add v0, u32 1 + v6 = eq v3, v5 + constrain v3 == v5 + v9 = call f1(Field 3, v0) -> u32 + v10 = add v0, u32 1 + v11 = eq v9, v10 + constrain v9 == v10 + return + } + brillig(inline) fn wrapper f1 { + b0(v0: Field, v1: u32): + v3 = call f4(v0, v1) -> u32 + return v3 + } + brillig(inline) fn increment f2 { + b0(v0: u32): + v2 = add v0, u32 1 + return v2 + } + brillig(inline) fn increment_acir f3 { + b0(v0: u32): + v2 = add v0, u32 1 + return v2 + } + brillig(inline_always) fn apply f4 { + b0(v0: Field, v1: u32): + v4 = eq v0, Field 2 + jmpif v4 then: b2, else: b1 + b1(): + constrain v0 == Field 3 + v7 = call f3(v1) -> u32 + jmp b3(v7) + b2(): + v9 = call f2(v1) -> u32 + jmp b3(v9) + b3(v2: u32): + return v2 + } + "; + assert_normalized_ssa_equals(ssa, expected); + } + + #[test] + fn apply_created_per_caller_runtime() { + let src = " + acir(inline) fn main f0 { + b0(v0: u32): + v3 = call f1(f2, v0) -> u32 + v5 = add v0, u32 1 + v6 = eq v3, v5 + constrain v3 == v5 + v9 = call f4(f3, v0) -> u32 + v10 = add v0, u32 1 + v11 = eq v9, v10 + constrain v9 == v10 + return + } + brillig(inline) fn wrapper f1 { + b0(v0: function, v1: u32): + v2 = call v0(v1) -> u32 + return v2 + } + acir(inline) fn wrapper_acir f4 { + b0(v0: function, v1: u32): + v2 = call v0(v1) -> u32 + return v2 + } + brillig(inline) fn increment f2 { + b0(v0: u32): + v2 = add v0, u32 1 + return v2 + } + acir(inline) fn increment_acir f3 { + b0(v0: u32): + v2 = add v0, u32 1 + return v2 + } + "; + + let ssa = Ssa::from_str(src).unwrap(); + let ssa = ssa.defunctionalize(); + + let applies = ssa.functions.values().filter(|f| f.name() == "apply").collect::>(); + assert_eq!(applies.len(), 2); + assert!(applies.iter().any(|f| f.runtime().is_acir())); + assert!(applies.iter().any(|f| f.runtime().is_brillig())); + } +} diff --git a/compiler/noirc_evaluator/src/ssa/opt/die.rs b/compiler/noirc_evaluator/src/ssa/opt/die.rs index 7fdcb4c26c2..3b5537aceb4 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/die.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/die.rs @@ -22,9 +22,33 @@ use super::rc::{pop_rc_for, RcInstruction}; impl Ssa { /// Performs Dead Instruction Elimination (DIE) to remove any instructions with /// unused results. + /// + /// This step should come after the flattening of the CFG and mem2reg. #[tracing::instrument(level = "trace", skip(self))] - pub(crate) fn dead_instruction_elimination(mut self) -> Ssa { - self.functions.par_iter_mut().for_each(|(_, func)| func.dead_instruction_elimination(true)); + pub(crate) fn dead_instruction_elimination(self) -> Ssa { + self.dead_instruction_elimination_inner(true) + } + + fn dead_instruction_elimination_inner(mut self, flattened: bool) -> Ssa { + let mut used_global_values: HashSet<_> = self + .functions + .par_iter_mut() + .flat_map(|(_, func)| func.dead_instruction_elimination(true, flattened)) + .collect(); + + // Check which globals are used across all functions + for (id, value) in self.globals.dfg.values_iter().rev() { + if used_global_values.contains(&id) { + if let Value::Instruction { instruction, .. } = &value { + let instruction = &self.globals.dfg[*instruction]; + instruction.for_each_value(|value_id| { + used_global_values.insert(value_id); + }); + } + } + } + + self.used_global_values = used_global_values; self } @@ -37,8 +61,16 @@ impl Function { /// instructions that reference results from an instruction in another block are evaluated first. /// If we did not iterate blocks in this order we could not safely say whether or not the results /// of its instructions are needed elsewhere. - pub(crate) fn dead_instruction_elimination(&mut self, insert_out_of_bounds_checks: bool) { - let mut context = Context::default(); + /// + /// Returns the set of globals that were used in this function. + /// After processing all functions, the union of these sets enables determining the unused globals. + pub(crate) fn dead_instruction_elimination( + &mut self, + insert_out_of_bounds_checks: bool, + flattened: bool, + ) -> HashSet { + let mut context = Context { flattened, ..Default::default() }; + for call_data in &self.dfg.data_bus.call_data { context.mark_used_instruction_results(&self.dfg, call_data.array_id); } @@ -58,11 +90,12 @@ impl Function { // instructions (we don't want to remove those checks, or instructions that are // dependencies of those checks) if inserted_out_of_bounds_checks { - self.dead_instruction_elimination(false); - return; + return self.dead_instruction_elimination(false, flattened); } context.remove_rc_instructions(&mut self.dfg); + + context.used_values.into_iter().filter(|value| self.dfg.is_global(*value)).collect() } } @@ -76,6 +109,11 @@ struct Context { /// they technically contain side-effects but we still want to remove them if their /// `value` parameter is not used elsewhere. rc_instructions: Vec<(InstructionId, BasicBlockId)>, + + /// The elimination of certain unused instructions assumes that the DIE pass runs after + /// the flattening of the CFG, but if that's not the case then we should not eliminate + /// them just yet. + flattened: bool, } impl Context { @@ -172,7 +210,7 @@ impl Context { fn is_unused(&self, instruction_id: InstructionId, function: &Function) -> bool { let instruction = &function.dfg[instruction_id]; - if instruction.can_eliminate_if_unused(function) { + if instruction.can_eliminate_if_unused(function, self.flattened) { let results = function.dfg.instruction_results(instruction_id); results.iter().all(|result| !self.used_values.contains(result)) } else if let Instruction::Call { func, arguments } = instruction { @@ -195,15 +233,17 @@ impl Context { /// Inspects a value and marks all instruction results as used. fn mark_used_instruction_results(&mut self, dfg: &DataFlowGraph, value_id: ValueId) { let value_id = dfg.resolve(value_id); - if matches!(&dfg[value_id], Value::Instruction { .. } | Value::Param { .. }) { + if matches!(&dfg[value_id], Value::Instruction { .. } | Value::Param { .. }) + || dfg.is_global(value_id) + { self.used_values.insert(value_id); } } - fn remove_rc_instructions(self, dfg: &mut DataFlowGraph) { + fn remove_rc_instructions(&self, dfg: &mut DataFlowGraph) { let unused_rc_values_by_block: HashMap> = - self.rc_instructions.into_iter().fold(HashMap::default(), |mut acc, (rc, block)| { - let value = match &dfg[rc] { + self.rc_instructions.iter().fold(HashMap::default(), |mut acc, (rc, block)| { + let value = match &dfg[*rc] { Instruction::IncrementRc { value } => *value, Instruction::DecrementRc { value } => *value, other => { @@ -214,7 +254,7 @@ impl Context { }; if !self.used_values.contains(&value) { - acc.entry(block).or_default().insert(rc); + acc.entry(*block).or_default().insert(*rc); } acc }); @@ -356,15 +396,16 @@ impl Context { ) -> bool { use Instruction::*; if let IncrementRc { value } | DecrementRc { value } = instruction { - if let Value::Instruction { instruction, .. } = &dfg[*value] { - return match &dfg[*instruction] { - MakeArray { .. } => true, - Call { func, .. } => { - matches!(&dfg[*func], Value::Intrinsic(_) | Value::ForeignFunction(_)) - } - _ => false, - }; - } + let Some(instruction) = dfg.get_local_or_global_instruction(*value) else { + return false; + }; + return match instruction { + MakeArray { .. } => true, + Call { func, .. } => { + matches!(&dfg[*func], Value::Intrinsic(_) | Value::ForeignFunction(_)) + } + _ => false, + }; } false } @@ -507,16 +548,18 @@ fn apply_side_effects( let casted_condition = dfg.insert_instruction_and_results(cast, block_id, None, call_stack); let casted_condition = casted_condition.first(); + // Unchecked mul because the side effects var is always 0 or 1 let lhs = dfg.insert_instruction_and_results( - Instruction::binary(BinaryOp::Mul, lhs, casted_condition), + Instruction::binary(BinaryOp::Mul { unchecked: true }, lhs, casted_condition), block_id, None, call_stack, ); let lhs = lhs.first(); + // Unchecked mul because the side effects var is always 0 or 1 let rhs = dfg.insert_instruction_and_results( - Instruction::binary(BinaryOp::Mul, rhs, casted_condition), + Instruction::binary(BinaryOp::Mul { unchecked: true }, rhs, casted_condition), block_id, None, call_stack, @@ -939,4 +982,53 @@ mod test { let ssa = ssa.dead_instruction_elimination(); assert_normalized_ssa_equals(ssa, src); } + + #[test] + fn do_not_remove_mutable_reference_params() { + let src = " + acir(inline) fn main f0 { + b0(v0: Field, v1: Field): + v2 = allocate -> &mut Field + store v0 at v2 + call f1(v2) + v4 = load v2 -> Field + v5 = eq v4, v1 + constrain v4 == v1 + return + } + acir(inline) fn Add10 f1 { + b0(v0: &mut Field): + v1 = load v0 -> Field + v2 = load v0 -> Field + v4 = add v2, Field 10 + store v4 at v0 + return + } + "; + + let ssa = Ssa::from_str(src).unwrap(); + + // Even though these ACIR functions only have 1 block, we have not inlined and flattened anything yet. + let ssa = ssa.dead_instruction_elimination_inner(false); + + let expected = " + acir(inline) fn main f0 { + b0(v0: Field, v1: Field): + v2 = allocate -> &mut Field + store v0 at v2 + call f1(v2) + v4 = load v2 -> Field + constrain v4 == v1 + return + } + acir(inline) fn Add10 f1 { + b0(v0: &mut Field): + v1 = load v0 -> Field + v3 = add v1, Field 10 + store v3 at v0 + return + } + "; + assert_normalized_ssa_equals(ssa, expected); + } } diff --git a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs index 748867c7409..76f8495c009 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs @@ -654,20 +654,10 @@ impl<'f> Context<'f> { // Replace constraint `lhs == rhs` with `condition * lhs == condition * rhs`. // Condition needs to be cast to argument type in order to multiply them together. - let argument_type = self.inserter.function.dfg.type_of_value(lhs); - - let cast = Instruction::Cast(condition, argument_type.unwrap_numeric()); - let casted_condition = self.insert_instruction(cast, call_stack); - - let lhs = self.insert_instruction( - Instruction::binary(BinaryOp::Mul, lhs, casted_condition), - call_stack, - ); - let rhs = self.insert_instruction( - Instruction::binary(BinaryOp::Mul, rhs, casted_condition), - call_stack, - ); - + let casted_condition = + self.cast_condition_to_value_type(condition, lhs, call_stack); + let lhs = self.mul_by_condition(lhs, casted_condition, call_stack); + let rhs = self.mul_by_condition(rhs, casted_condition, call_stack); Instruction::Constrain(lhs, rhs, message) } Instruction::Store { address, value } => { @@ -700,28 +690,18 @@ impl<'f> Context<'f> { // 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 cast = Instruction::Cast(condition, argument_type.unwrap_numeric()); - let casted_condition = self.insert_instruction(cast, call_stack); - - let value = self.insert_instruction( - Instruction::binary(BinaryOp::Mul, value, casted_condition), - call_stack, - ); + let casted_condition = + self.cast_condition_to_value_type(condition, value, call_stack); + let value = self.mul_by_condition(value, casted_condition, call_stack); Instruction::RangeCheck { value, max_bit_size, assert_message } } Instruction::Call { func, mut arguments } => match self.inserter.function.dfg[func] { Value::Intrinsic(Intrinsic::ToBits(_) | Intrinsic::ToRadix(_)) => { let field = arguments[0]; - let argument_type = self.inserter.function.dfg.type_of_value(field); - - let cast = Instruction::Cast(condition, argument_type.unwrap_numeric()); - let casted_condition = self.insert_instruction(cast, call_stack); - let field = self.insert_instruction( - Instruction::binary(BinaryOp::Mul, field, casted_condition), - call_stack, - ); + let casted_condition = + self.cast_condition_to_value_type(condition, field, call_stack); + let field = self.mul_by_condition(field, casted_condition, call_stack); arguments[0] = field; @@ -765,6 +745,30 @@ impl<'f> Context<'f> { } } + fn cast_condition_to_value_type( + &mut self, + condition: ValueId, + value: ValueId, + call_stack: CallStackId, + ) -> ValueId { + let argument_type = self.inserter.function.dfg.type_of_value(value); + let cast = Instruction::Cast(condition, argument_type.unwrap_numeric()); + self.insert_instruction(cast, call_stack) + } + + fn mul_by_condition( + &mut self, + value: ValueId, + condition: ValueId, + call_stack: CallStackId, + ) -> ValueId { + // Unchecked mul because the condition is always 0 or 1 + self.insert_instruction( + Instruction::binary(BinaryOp::Mul { unchecked: true }, value, condition), + call_stack, + ) + } + /// When a MSM is done under a predicate, we need to apply the predicate /// to the is_infinity property of the input points in order to ensure /// that the points will be on the curve no matter what. @@ -797,11 +801,11 @@ impl<'f> Context<'f> { // Computes: if condition { var } else { 1 } fn var_or_one(&mut self, var: ValueId, condition: ValueId, call_stack: CallStackId) -> ValueId { - let field = - self.insert_instruction(Instruction::binary(BinaryOp::Mul, var, condition), call_stack); + let field = self.mul_by_condition(var, condition, call_stack); let not_condition = self.not_instruction(condition, call_stack); + // Unchecked add because of the values is guaranteed to be 0 self.insert_instruction( - Instruction::binary(BinaryOp::Add, field, not_condition), + Instruction::binary(BinaryOp::Add { unchecked: true }, field, not_condition), call_stack, ) } @@ -1279,15 +1283,15 @@ mod test { v12 = not v5 v13 = cast v4 as u8 v14 = cast v12 as u8 - v15 = mul v13, v10 - v16 = mul v14, v11 - v17 = add v15, v16 + v15 = unchecked_mul v13, v10 + v16 = unchecked_mul v14, v11 + v17 = unchecked_add v15, v16 store v17 at v6 enable_side_effects v12 v18 = load v6 -> u8 v19 = cast v12 as u8 v20 = cast v4 as u8 - v21 = mul v20, v18 + v21 = unchecked_mul v20, v18 store v21 at v6 enable_side_effects u1 1 constrain v5 == u1 1 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 df351d6c0cd..f4638cf85e4 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 @@ -121,13 +121,18 @@ impl<'a> ValueMerger<'a> { let else_condition = dfg.insert_instruction_and_results(cast, block, None, call_stack).first(); - let mul = Instruction::binary(BinaryOp::Mul, then_condition, then_value); + // Unchecked mul because `then_condition` will be 1 or 0 + let mul = + Instruction::binary(BinaryOp::Mul { unchecked: true }, then_condition, then_value); let then_value = dfg.insert_instruction_and_results(mul, block, None, call_stack).first(); - let mul = Instruction::binary(BinaryOp::Mul, else_condition, else_value); + // Unchecked mul because `else_condition` will be 1 or 0 + let mul = + Instruction::binary(BinaryOp::Mul { unchecked: true }, else_condition, else_value); let else_value = dfg.insert_instruction_and_results(mul, block, None, call_stack).first(); - let add = Instruction::binary(BinaryOp::Add, then_value, else_value); + // Unchecked add because one of the values will always be 0 + let add = Instruction::binary(BinaryOp::Add { unchecked: true }, then_value, else_value); dfg.insert_instruction_and_results(add, block, None, call_stack).first() } diff --git a/compiler/noirc_evaluator/src/ssa/opt/inlining.rs b/compiler/noirc_evaluator/src/ssa/opt/inlining.rs index 36955480728..c3b771d9102 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/inlining.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/inlining.rs @@ -2,9 +2,10 @@ //! The purpose of this pass is to inline the instructions of each function call //! within the function caller. If all function calls are known, there will only //! be a single function remaining when the pass finishes. -use std::collections::{BTreeSet, HashSet, VecDeque}; +use std::collections::{BTreeMap, BTreeSet, HashSet, VecDeque}; use acvm::acir::AcirField; +use im::HashMap; use iter_extended::{btree_map, vecmap}; use crate::ssa::{ @@ -19,7 +20,6 @@ use crate::ssa::{ }, ssa_gen::Ssa, }; -use fxhash::FxHashMap as HashMap; /// An arbitrary limit to the maximum number of recursive call /// frames at any point in time. @@ -46,35 +46,85 @@ impl Ssa { /// This step should run after runtime separation, since it relies on the runtime of the called functions being final. #[tracing::instrument(level = "trace", skip(self))] pub(crate) fn inline_functions(self, aggressiveness: i64) -> Ssa { - Self::inline_functions_inner(self, aggressiveness, false) + let inline_infos = compute_inline_infos(&self, false, aggressiveness); + Self::inline_functions_inner(self, &inline_infos, false) } - // Run the inlining pass where functions marked with `InlineType::NoPredicates` as not entry points + /// Run the inlining pass where functions marked with `InlineType::NoPredicates` as not entry points pub(crate) fn inline_functions_with_no_predicates(self, aggressiveness: i64) -> Ssa { - Self::inline_functions_inner(self, aggressiveness, true) + let inline_infos = compute_inline_infos(&self, true, aggressiveness); + Self::inline_functions_inner(self, &inline_infos, true) } fn inline_functions_inner( mut self, - aggressiveness: i64, + inline_infos: &InlineInfos, inline_no_predicates_functions: bool, ) -> Ssa { - let inline_sources = - get_functions_to_inline_into(&self, inline_no_predicates_functions, aggressiveness); - self.functions = btree_map(&inline_sources, |entry_point| { - let new_function = InlineContext::new( - &self, - *entry_point, - inline_no_predicates_functions, - inline_sources.clone(), - ) - .inline_all(&self); - (*entry_point, new_function) + let inline_targets = + inline_infos.iter().filter_map(|(id, info)| info.is_inline_target().then_some(*id)); + + // NOTE: Functions are processed independently of each other, with the final mapping replacing the original, + // instead of inlining the "leaf" functions, moving up towards the entry point. + self.functions = btree_map(inline_targets, |entry_point| { + let function = &self.functions[&entry_point]; + let new_function = + function.inlined(&self, inline_no_predicates_functions, inline_infos); + (entry_point, new_function) }); self } } +impl Function { + /// Create a new function which has the functions called by this one inlined into its body. + pub(super) fn inlined( + &self, + ssa: &Ssa, + inline_no_predicates_functions: bool, + inline_infos: &InlineInfos, + ) -> Function { + let caller_runtime = self.runtime(); + + let should_inline_call = + |_context: &PerFunctionContext, ssa: &Ssa, called_func_id: FunctionId| -> bool { + // Do not inline self-recursive functions on the top level. + // Inlining a self-recursive function works when there is something to inline into + // by importing all the recursive blocks, but for the entry function there is no wrapper. + if called_func_id == self.id() { + return false; + } + let callee = &ssa.functions[&called_func_id]; + + match callee.runtime() { + RuntimeType::Acir(inline_type) => { + // If the called function is acir, we inline if it's not an entry point + + // If we have not already finished the flattening pass, functions marked + // to not have predicates should be preserved. + let preserve_function = + !inline_no_predicates_functions && callee.is_no_predicates(); + + !inline_type.is_entry_point() && !preserve_function + } + RuntimeType::Brillig(_) => { + if caller_runtime.is_acir() { + // We never inline a brillig function into an ACIR function. + return false; + } + // We inline inline if the function called wasn't ruled out as too costly or recursive. + inline_infos + .get(&called_func_id) + .map(|info| info.should_inline) + .unwrap_or_default() + } + } + }; + + InlineContext::new(ssa, self.id()).inline_all(ssa, &should_inline_call) + } +} + /// The context for the function inlining pass. /// /// This works using an internal FunctionBuilder to build a new main function from scratch. @@ -88,16 +138,6 @@ struct InlineContext { // The FunctionId of the entry point function we're inlining into in the old, unmodified Ssa. entry_point: FunctionId, - - /// Whether the inlining pass should inline any functions marked with [`InlineType::NoPredicates`] - /// or whether these should be preserved as entrypoint functions. - /// - /// This is done as we delay inlining of functions with the attribute `#[no_predicates]` until after - /// the control flow graph has been flattened. - inline_no_predicates_functions: bool, - - // These are the functions of the program that we shouldn't inline. - functions_not_to_inline: BTreeSet, } /// The per-function inlining context contains information that is only valid for one function. @@ -129,9 +169,13 @@ struct PerFunctionContext<'function> { /// True if we're currently working on the entry point function. inlining_entry: bool, + + globals: &'function Function, } /// Utility function to find out the direct calls of a function. +/// +/// Returns the function IDs from all `Call` instructions without deduplication. fn called_functions_vec(func: &Function) -> Vec { let mut called_function_ids = Vec::new(); for block_id in func.reachable_blocks() { @@ -149,32 +193,61 @@ fn called_functions_vec(func: &Function) -> Vec { called_function_ids } -/// Utility function to find out the deduplicated direct calls of a function. +/// Utility function to find out the deduplicated direct calls made from a function. fn called_functions(func: &Function) -> BTreeSet { called_functions_vec(func).into_iter().collect() } +/// Information about a function to aid the decision about whether to inline it or not. +/// The final decision depends on what we're inlining it into. +#[derive(Default, Debug)] +pub(super) struct InlineInfo { + is_brillig_entry_point: bool, + is_acir_entry_point: bool, + is_recursive: bool, + should_inline: bool, + weight: i64, + cost: i64, +} + +impl InlineInfo { + /// Functions which are to be retained, not inlined. + pub(super) fn is_inline_target(&self) -> bool { + self.is_brillig_entry_point + || self.is_acir_entry_point + || self.is_recursive + || !self.should_inline + } +} + +type InlineInfos = BTreeMap; + /// The functions we should inline into (and that should be left in the final program) are: /// - main /// - Any Brillig function called from Acir /// - Some Brillig functions depending on aggressiveness and some metrics /// - Any Acir functions with a [fold inline type][InlineType::Fold], -fn get_functions_to_inline_into( +/// +/// The returned `InlineInfos` won't have every function in it, only the ones which the algorithm visited. +pub(super) fn compute_inline_infos( ssa: &Ssa, inline_no_predicates_functions: bool, aggressiveness: i64, -) -> BTreeSet { - let mut brillig_entry_points = BTreeSet::default(); - let mut acir_entry_points = BTreeSet::default(); - - if matches!(ssa.main().runtime(), RuntimeType::Brillig(_)) { - brillig_entry_points.insert(ssa.main_id); - } else { - acir_entry_points.insert(ssa.main_id); - } +) -> InlineInfos { + let mut inline_infos = InlineInfos::default(); + + inline_infos.insert( + ssa.main_id, + InlineInfo { + is_acir_entry_point: ssa.main().runtime().is_acir(), + is_brillig_entry_point: ssa.main().runtime().is_brillig(), + ..Default::default() + }, + ); + // Handle ACIR functions. for (func_id, function) in ssa.functions.iter() { - if matches!(function.runtime(), RuntimeType::Brillig(_)) { + if function.runtime().is_brillig() { continue; } @@ -182,83 +255,216 @@ fn get_functions_to_inline_into( // to not have predicates should be preserved. let preserve_function = !inline_no_predicates_functions && function.is_no_predicates(); if function.runtime().is_entry_point() || preserve_function { - acir_entry_points.insert(*func_id); + inline_infos.entry(*func_id).or_default().is_acir_entry_point = true; } - for called_function_id in called_functions(function) { - if matches!(ssa.functions[&called_function_id].runtime(), RuntimeType::Brillig(_)) { - brillig_entry_points.insert(called_function_id); + // Any Brillig function called from ACIR is an entry into the Brillig VM. + for called_func_id in called_functions(function) { + if ssa.functions[&called_func_id].runtime().is_brillig() { + inline_infos.entry(called_func_id).or_default().is_brillig_entry_point = true; } } } - let times_called = compute_times_called(ssa); + let callers = compute_callers(ssa); + let times_called = compute_times_called(&callers); - let brillig_functions_to_retain: BTreeSet<_> = compute_functions_to_retain( + mark_brillig_functions_to_retain( ssa, - &brillig_entry_points, - ×_called, inline_no_predicates_functions, aggressiveness, + ×_called, + &mut inline_infos, ); - acir_entry_points - .into_iter() - .chain(brillig_entry_points) - .chain(brillig_functions_to_retain) + inline_infos +} + +/// Compute the time each function is called from any other function. +fn compute_times_called( + callers: &BTreeMap>, +) -> HashMap { + callers + .iter() + .map(|(callee, callers)| { + let total_calls = callers.values().sum(); + (*callee, total_calls) + }) .collect() } -fn compute_times_called(ssa: &Ssa) -> HashMap { +/// Compute for each function the set of functions that call it, and how many times they do so. +fn compute_callers(ssa: &Ssa) -> BTreeMap> { ssa.functions .iter() - .flat_map(|(_caller_id, function)| { - let called_functions_vec = called_functions_vec(function); - called_functions_vec.into_iter() + .flat_map(|(caller_id, function)| { + let called_functions = called_functions_vec(function); + called_functions.into_iter().map(|callee_id| (*caller_id, callee_id)) }) - .chain(std::iter::once(ssa.main_id)) - .fold(HashMap::default(), |mut map, func_id| { - *map.entry(func_id).or_insert(0) += 1; - map + .fold( + // Make sure an entry exists even for ones that don't get called. + ssa.functions.keys().map(|id| (*id, BTreeMap::new())).collect(), + |mut acc, (caller_id, callee_id)| { + let callers = acc.entry(callee_id).or_default(); + *callers.entry(caller_id).or_default() += 1; + acc + }, + ) +} + +/// Compute for each function the set of functions called by it, and how many times it does so. +fn compute_callees(ssa: &Ssa) -> BTreeMap> { + ssa.functions + .iter() + .flat_map(|(caller_id, function)| { + let called_functions = called_functions_vec(function); + called_functions.into_iter().map(|callee_id| (*caller_id, callee_id)) }) + .fold( + // Make sure an entry exists even for ones that don't call anything. + ssa.functions.keys().map(|id| (*id, BTreeMap::new())).collect(), + |mut acc, (caller_id, callee_id)| { + let callees = acc.entry(caller_id).or_default(); + *callees.entry(callee_id).or_default() += 1; + acc + }, + ) +} + +/// Compute something like a topological order of the functions, starting with the ones +/// that do not call any other functions, going towards the entry points. When cycles +/// are detected, take the one which are called by the most to break the ties. +/// +/// This can be used to simplify the most often called functions first. +/// +/// Returns the functions paired with their own as well as transitive weight, +/// which accumulates the weight of all the functions they call, as well as own. +pub(super) fn compute_bottom_up_order(ssa: &Ssa) -> Vec<(FunctionId, (usize, usize))> { + let mut order = Vec::new(); + let mut visited = HashSet::new(); + + // Call graph which we'll repeatedly prune to find the "leaves". + let mut callees = compute_callees(ssa); + let callers = compute_callers(ssa); + + // Number of times a function is called, used to break cycles in the call graph by popping the next candidate. + let mut times_called = compute_times_called(&callers).into_iter().collect::>(); + times_called.sort_by_key(|(id, cnt)| { + // Sort by called the *least* by others, as these are less likely to cut the graph when removed. + let called_desc = -(*cnt as i64); + // Sort entries first (last to be popped). + let is_entry_asc = -called_desc.signum(); + // Finally break ties by ID. + (is_entry_asc, called_desc, *id) + }); + + // Start with the weight of the functions in isolation, then accumulate as we pop off the ones they call. + let own_weights = ssa + .functions + .iter() + .map(|(id, f)| (*id, compute_function_own_weight(f))) + .collect::>(); + let mut weights = own_weights.clone(); + + // Seed the queue with functions that don't call anything. + let mut queue = callees + .iter() + .filter_map(|(id, callees)| callees.is_empty().then_some(*id)) + .collect::>(); + + loop { + while let Some(id) = queue.pop_front() { + // Pull the current weight of yet-to-be emitted callees (a nod to mutual recursion). + for (callee, cnt) in &callees[&id] { + if *callee != id { + weights[&id] = weights[&id].saturating_add(cnt.saturating_mul(weights[callee])); + } + } + // Own weight plus the weights accumulated from callees. + let weight = weights[&id]; + let own_weight = own_weights[&id]; + + // Emit the function. + order.push((id, (own_weight, weight))); + visited.insert(id); + + // Update the callers of this function. + for (caller, cnt) in &callers[&id] { + // Update the weight of the caller with the weight of this function. + weights[caller] = weights[caller].saturating_add(cnt.saturating_mul(weight)); + // Remove this function from the callees of the caller. + let callees = callees.get_mut(caller).unwrap(); + callees.remove(&id); + // If the caller doesn't call any other function, enqueue it, + // unless it's the entry function, which is never called by anything, so it should be last. + if callees.is_empty() && !visited.contains(caller) && !callers[caller].is_empty() { + queue.push_back(*caller); + } + } + } + // If we ran out of the queue, maybe there is a cycle; take the next most called function. + while let Some((id, _)) = times_called.pop() { + if !visited.contains(&id) { + queue.push_back(id); + break; + } + } + if times_called.is_empty() && queue.is_empty() { + assert_eq!(order.len(), callers.len()); + return order; + } + } } -fn should_retain_recursive( +/// Traverse the call graph starting from a given function, marking function to be retained if they are: +/// * recursive functions, or +/// * the cost of inlining outweighs the cost of not doing so +fn mark_functions_to_retain_recursive( ssa: &Ssa, - func: FunctionId, - times_called: &HashMap, - should_retain_function: &mut HashMap, - mut explored_functions: im::HashSet, inline_no_predicates_functions: bool, aggressiveness: i64, + times_called: &HashMap, + inline_infos: &mut InlineInfos, + mut explored_functions: im::HashSet, + func: FunctionId, ) { - // We have already decided on this function - if should_retain_function.get(&func).is_some() { + // Check if we have set any of the fields this method touches. + let decided = |inline_infos: &InlineInfos| { + inline_infos + .get(&func) + .map(|info| info.is_recursive || info.should_inline || info.weight != 0) + .unwrap_or_default() + }; + + // Check if we have already decided on this function + if decided(inline_infos) { return; } - // Recursive, this function won't be inlined + + // If recursive, this function won't be inlined if explored_functions.contains(&func) { - should_retain_function.insert(func, (true, 0)); + inline_infos.entry(func).or_default().is_recursive = true; return; } explored_functions.insert(func); - // Decide on dependencies first - let called_functions = called_functions(&ssa.functions[&func]); - for function in called_functions.iter() { - should_retain_recursive( + // Decide on dependencies first, so we know their weight. + let called_functions = called_functions_vec(&ssa.functions[&func]); + for callee in &called_functions { + mark_functions_to_retain_recursive( ssa, - *function, - times_called, - should_retain_function, - explored_functions.clone(), inline_no_predicates_functions, aggressiveness, + times_called, + inline_infos, + explored_functions.clone(), + *callee, ); } + // We could have decided on this function while deciding on dependencies - // If the function is recursive - if should_retain_function.get(&func).is_some() { + // if the function is recursive. + if decided(inline_infos) { return; } @@ -266,13 +472,18 @@ fn should_retain_recursive( // We compute the weight (roughly the number of instructions) of the function after inlining // And the interface cost of the function (the inherent cost at the callsite, roughly the number of args and returns) // We then can compute an approximation of the cost of inlining vs the cost of retaining the function - // We do this computation using saturating i64s to avoid overflows - let inlined_function_weights: i64 = called_functions.iter().fold(0, |acc, called_function| { - let (should_retain, weight) = should_retain_function[called_function]; - if should_retain { - acc + // We do this computation using saturating i64s to avoid overflows, + // and because we want to calculate a difference which can be negative. + + // Total weight of functions called by this one, unless we decided not to inline them. + // Callees which appear multiple times would be inlined multiple times. + let inlined_function_weights: i64 = called_functions.iter().fold(0, |acc, callee| { + let info = &inline_infos[callee]; + // If the callee is not going to be inlined then we can ignore its cost. + if info.should_inline { + acc.saturating_add(info.weight) } else { - acc.saturating_add(weight) + acc } }); @@ -285,54 +496,50 @@ fn should_retain_recursive( let inline_cost = times_called.saturating_mul(this_function_weight); let retain_cost = times_called.saturating_mul(interface_cost) + this_function_weight; + let net_cost = inline_cost.saturating_sub(retain_cost); let runtime = ssa.functions[&func].runtime(); // We inline if the aggressiveness is higher than inline cost minus the retain cost // If aggressiveness is infinite, we'll always inline // If aggressiveness is 0, we'll inline when the inline cost is lower than the retain cost // If aggressiveness is minus infinity, we'll never inline (other than in the mandatory cases) - let should_inline = ((inline_cost.saturating_sub(retain_cost)) < aggressiveness) + let should_inline = (net_cost < aggressiveness) || runtime.is_inline_always() || (runtime.is_no_predicates() && inline_no_predicates_functions); - should_retain_function.insert(func, (!should_inline, this_function_weight)); + let info = inline_infos.entry(func).or_default(); + info.should_inline = should_inline; + info.weight = this_function_weight; + info.cost = net_cost; } -fn compute_functions_to_retain( +/// Mark Brillig functions that should not be inlined because they are recursive or expensive. +fn mark_brillig_functions_to_retain( ssa: &Ssa, - entry_points: &BTreeSet, - times_called: &HashMap, inline_no_predicates_functions: bool, aggressiveness: i64, -) -> BTreeSet { - let mut should_retain_function = HashMap::default(); + times_called: &HashMap, + inline_infos: &mut BTreeMap, +) { + let brillig_entry_points = inline_infos + .iter() + .filter_map(|(id, info)| info.is_brillig_entry_point.then_some(*id)) + .collect::>(); - for entry_point in entry_points.iter() { - should_retain_recursive( + for entry_point in brillig_entry_points { + mark_functions_to_retain_recursive( ssa, - *entry_point, - times_called, - &mut should_retain_function, - im::HashSet::default(), inline_no_predicates_functions, aggressiveness, + times_called, + inline_infos, + im::HashSet::default(), + entry_point, ); } - - should_retain_function - .into_iter() - .filter_map( - |(func_id, (should_retain, _))| { - if should_retain { - Some(func_id) - } else { - None - } - }, - ) - .collect() } +/// Compute a weight of a function based on the number of instructions in its reachable blocks. fn compute_function_own_weight(func: &Function) -> usize { let mut weight = 0; for block_id in func.reachable_blocks() { @@ -343,6 +550,7 @@ fn compute_function_own_weight(func: &Function) -> usize { weight } +/// Compute interface cost of a function based on the number of inputs and outputs. fn compute_function_interface_cost(func: &Function) -> usize { func.parameters().len() + func.returns().len() } @@ -353,32 +561,30 @@ impl InlineContext { /// The function being inlined into will always be the main function, although it is /// actually a copy that is created in case the original main is still needed from a function /// that could not be inlined calling it. - fn new( - ssa: &Ssa, - entry_point: FunctionId, - inline_no_predicates_functions: bool, - functions_not_to_inline: BTreeSet, - ) -> InlineContext { + fn new(ssa: &Ssa, entry_point: FunctionId) -> Self { let source = &ssa.functions[&entry_point]; let mut builder = FunctionBuilder::new(source.name().to_owned(), entry_point); builder.set_runtime(source.runtime()); - Self { - builder, - recursion_level: 0, - entry_point, - call_stack: CallStackId::root(), - inline_no_predicates_functions, - functions_not_to_inline, - } + builder.current_function.set_globals(source.dfg.globals.clone()); + + Self { builder, recursion_level: 0, entry_point, call_stack: CallStackId::root() } } /// Start inlining the entry point function and all functions reachable from it. - fn inline_all(mut self, ssa: &Ssa) -> Function { + fn inline_all( + mut self, + ssa: &Ssa, + should_inline_call: &impl Fn(&PerFunctionContext, &Ssa, FunctionId) -> bool, + ) -> Function { let entry_point = &ssa.functions[&self.entry_point]; - let mut context = PerFunctionContext::new(&mut self, entry_point); + let mut context = PerFunctionContext::new(&mut self, entry_point, &ssa.globals); context.inlining_entry = true; + for (_, value) in entry_point.dfg.globals.values_iter() { + context.context.builder.current_function.dfg.make_global(value.get_type().into_owned()); + } + // The entry block is already inserted so we have to add it to context.blocks and add // its parameters here. Failing to do so would cause context.translate_block() to add // a fresh block for the entry block rather than use the existing one. @@ -392,7 +598,7 @@ impl InlineContext { } context.blocks.insert(context.source_function.entry_block(), entry_block); - context.inline_blocks(ssa); + context.inline_blocks(ssa, should_inline_call); // translate databus values let databus = entry_point.dfg.data_bus.map_values(|t| context.translate_value(t)); @@ -411,6 +617,7 @@ impl InlineContext { ssa: &Ssa, id: FunctionId, arguments: &[ValueId], + should_inline_call: &impl Fn(&PerFunctionContext, &Ssa, FunctionId) -> bool, ) -> Vec { self.recursion_level += 1; @@ -418,11 +625,11 @@ impl InlineContext { if self.recursion_level > RECURSION_LIMIT { panic!( - "Attempted to recur more than {RECURSION_LIMIT} times during inlining function '{}': {}", source_function.name(), source_function + "Attempted to recur more than {RECURSION_LIMIT} times during inlining function '{}':\n{}", source_function.name(), source_function ); } - let mut context = PerFunctionContext::new(self, source_function); + let mut context = PerFunctionContext::new(self, source_function, &ssa.globals); let parameters = source_function.parameters(); assert_eq!(parameters.len(), arguments.len()); @@ -431,7 +638,7 @@ impl InlineContext { let current_block = context.context.builder.current_block(); context.blocks.insert(source_function.entry_block(), current_block); - let return_values = context.inline_blocks(ssa); + let return_values = context.inline_blocks(ssa, should_inline_call); self.recursion_level -= 1; return_values } @@ -442,13 +649,18 @@ impl<'function> PerFunctionContext<'function> { /// The value and block mappings for this context are initially empty except /// for containing the mapping between parameters in the source_function and /// the arguments of the destination function. - fn new(context: &'function mut InlineContext, source_function: &'function Function) -> Self { + fn new( + context: &'function mut InlineContext, + source_function: &'function Function, + globals: &'function Function, + ) -> Self { Self { context, source_function, blocks: HashMap::default(), values: HashMap::default(), inlining_entry: false, + globals, } } @@ -464,20 +676,48 @@ impl<'function> PerFunctionContext<'function> { } let new_value = match &self.source_function.dfg[id] { - value @ Value::Instruction { .. } => { + value @ Value::Instruction { instruction, .. } => { + if self.source_function.dfg.is_global(id) { + if self.context.builder.current_function.dfg.runtime().is_acir() { + let Instruction::MakeArray { elements, typ } = + &self.globals.dfg[*instruction] + else { + panic!("Only expect Instruction::MakeArray for a global"); + }; + let elements = elements + .iter() + .map(|element| self.translate_value(*element)) + .collect::>(); + return self.context.builder.insert_make_array(elements, typ.clone()); + } else { + return id; + } + } unreachable!("All Value::Instructions should already be known during inlining after creating the original inlined instruction. Unknown value {id} = {value:?}") } value @ Value::Param { .. } => { unreachable!("All Value::Params should already be known from previous calls to translate_block. Unknown value {id} = {value:?}") } Value::NumericConstant { constant, typ } => { - self.context.builder.numeric_constant(*constant, *typ) + // The dfg indexes a global's inner value directly, so we need to check here + // whether we have a global. + // We also only keep a global and do not inline it in a Brillig runtime. + if self.source_function.dfg.is_global(id) + && self.context.builder.current_function.dfg.runtime().is_brillig() + { + id + } else { + self.context.builder.numeric_constant(*constant, *typ) + } } Value::Function(function) => self.context.builder.import_function(*function), Value::Intrinsic(intrinsic) => self.context.builder.import_intrinsic_id(*intrinsic), Value::ForeignFunction(function) => { self.context.builder.import_foreign_function(function) } + Value::Global(_) => { + panic!("Expected a global to be resolved to its inner value"); + } }; self.values.insert(id, new_value); @@ -534,7 +774,11 @@ impl<'function> PerFunctionContext<'function> { } /// Inline all reachable blocks within the source_function into the destination function. - fn inline_blocks(&mut self, ssa: &Ssa) -> Vec { + fn inline_blocks( + &mut self, + ssa: &Ssa, + should_inline_call: &impl Fn(&PerFunctionContext, &Ssa, FunctionId) -> bool, + ) -> Vec { let mut seen_blocks = HashSet::new(); let mut block_queue = VecDeque::new(); block_queue.push_back(self.source_function.entry_block()); @@ -551,7 +795,7 @@ impl<'function> PerFunctionContext<'function> { self.context.builder.switch_to_block(translated_block_id); seen_blocks.insert(source_block_id); - self.inline_block_instructions(ssa, source_block_id); + self.inline_block_instructions(ssa, source_block_id, should_inline_call); if let Some((block, values)) = self.handle_terminator_instruction(source_block_id, &mut block_queue) @@ -596,7 +840,12 @@ impl<'function> PerFunctionContext<'function> { /// Inline each instruction in the given block into the function being inlined into. /// This may recurse if it finds another function to inline if a call instruction is within this block. - fn inline_block_instructions(&mut self, ssa: &Ssa, block_id: BasicBlockId) { + fn inline_block_instructions( + &mut self, + ssa: &Ssa, + block_id: BasicBlockId, + should_inline_call: &impl Fn(&PerFunctionContext, &Ssa, FunctionId) -> bool, + ) { let mut side_effects_enabled: Option = None; let block = &self.source_function.dfg[block_id]; @@ -604,8 +853,8 @@ impl<'function> PerFunctionContext<'function> { match &self.source_function.dfg[*id] { Instruction::Call { func, arguments } => match self.get_function(*func) { Some(func_id) => { - if self.should_inline_call(ssa, func_id) { - self.inline_function(ssa, *id, func_id, arguments); + if should_inline_call(self, ssa, func_id) { + self.inline_function(ssa, *id, func_id, arguments, should_inline_call); // This is only relevant during handling functions with `InlineType::NoPredicates` as these // can pollute the function they're being inlined into with `Instruction::EnabledSideEffects`, @@ -633,24 +882,6 @@ impl<'function> PerFunctionContext<'function> { } } - fn should_inline_call(&self, ssa: &Ssa, called_func_id: FunctionId) -> bool { - let function = &ssa.functions[&called_func_id]; - - if let RuntimeType::Acir(inline_type) = function.runtime() { - // If the called function is acir, we inline if it's not an entry point - - // If we have not already finished the flattening pass, functions marked - // to not have predicates should be preserved. - let preserve_function = - !self.context.inline_no_predicates_functions && function.is_no_predicates(); - !inline_type.is_entry_point() && !preserve_function - } else { - // If the called function is brillig, we inline only if it's into brillig and the function is not recursive - matches!(ssa.functions[&self.context.entry_point].runtime(), RuntimeType::Brillig(_)) - && !self.context.functions_not_to_inline.contains(&called_func_id) - } - } - /// Inline a function call and remember the inlined return values in the values map fn inline_function( &mut self, @@ -658,6 +889,7 @@ impl<'function> PerFunctionContext<'function> { call_id: InstructionId, function: FunctionId, arguments: &[ValueId], + should_inline_call: &impl Fn(&PerFunctionContext, &Ssa, FunctionId) -> bool, ) { let old_results = self.source_function.dfg.instruction_results(call_id); let arguments = vecmap(arguments, |arg| self.translate_value(*arg)); @@ -673,7 +905,8 @@ impl<'function> PerFunctionContext<'function> { .extend_call_stack(self.context.call_stack, &call_stack); self.context.call_stack = new_call_stack; - let new_results = self.context.inline_function(ssa, function, &arguments); + let new_results = + self.context.inline_function(ssa, function, &arguments, should_inline_call); self.context.call_stack = self .context .builder @@ -846,6 +1079,8 @@ impl<'function> PerFunctionContext<'function> { #[cfg(test)] mod test { + use std::cmp::max; + use acvm::{acir::AcirField, FieldElement}; use noirc_frontend::monomorphization::ast::InlineType; @@ -858,8 +1093,11 @@ mod test { map::Id, types::{NumericType, Type}, }, + Ssa, }; + use super::compute_bottom_up_order; + #[test] fn basic_inlining() { // fn foo { @@ -936,7 +1174,8 @@ mod test { // Compiling square f1 builder.new_function("square".into(), square_id, InlineType::default()); let square_v0 = builder.add_parameter(Type::field()); - let square_v2 = builder.insert_binary(square_v0, BinaryOp::Mul, square_v0); + let square_v2 = + builder.insert_binary(square_v0, BinaryOp::Mul { unchecked: false }, square_v0); builder.terminate_with_return(vec![square_v2]); // Compiling id1 f2 @@ -1001,9 +1240,9 @@ mod test { builder.switch_to_block(b2); let factorial_id = builder.import_function(factorial_id); - let v2 = builder.insert_binary(v0, BinaryOp::Sub, one); + let v2 = builder.insert_binary(v0, BinaryOp::Sub { unchecked: false }, one); let v3 = builder.insert_call(factorial_id, vec![v2], vec![Type::field()])[0]; - let v4 = builder.insert_binary(v0, BinaryOp::Mul, v3); + let v4 = builder.insert_binary(v0, BinaryOp::Mul { unchecked: false }, v3); builder.terminate_with_return(vec![v4]); let ssa = builder.finish(); @@ -1129,26 +1368,25 @@ mod test { #[test] #[should_panic( - expected = "Attempted to recur more than 1000 times during inlining function 'main': acir(inline) fn main f0 {" + expected = "Attempted to recur more than 1000 times during inlining function 'foo':\nacir(inline) fn foo f1 {" )] fn unconditional_recursion() { - // fn main f1 { - // b0(): - // call f1() - // return - // } - let main_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("main".into(), main_id); - - let main = builder.import_function(main_id); - let results = builder.insert_call(main, Vec::new(), vec![]).to_vec(); - builder.terminate_with_return(results); - - let ssa = builder.finish(); - assert_eq!(ssa.functions.len(), 1); + let src = " + acir(inline) fn main f0 { + b0(): + call f1() + return + } + acir(inline) fn foo f1 { + b0(): + call f1() + return + } + "; + let ssa = Ssa::from_str(src).unwrap(); + assert_eq!(ssa.functions.len(), 2); - let inlined = ssa.inline_functions(i64::MAX); - assert_eq!(inlined.functions.len(), 0); + let _ = ssa.inline_functions(i64::MAX); } #[test] @@ -1238,4 +1476,95 @@ mod test { // No inlining has happened assert_eq!(inlined.functions.len(), 2); } + + #[test] + fn bottom_up_order_and_weights() { + let src = " + brillig(inline) fn main f0 { + b0(v0: u32, v1: u1): + v3 = call f2(v0) -> u1 + v4 = eq v3, v1 + constrain v3 == v1 + return + } + brillig(inline) fn is_even f1 { + b0(v0: u32): + v3 = eq v0, u32 0 + jmpif v3 then: b2, else: b1 + b1(): + v5 = call f3(v0) -> u32 + v7 = call f2(v5) -> u1 + jmp b3(v7) + b2(): + jmp b3(u1 1) + b3(v1: u1): + return v1 + } + brillig(inline) fn is_odd f2 { + b0(v0: u32): + v3 = eq v0, u32 0 + jmpif v3 then: b2, else: b1 + b1(): + v5 = call f3(v0) -> u32 + v7 = call f1(v5) -> u1 + jmp b3(v7) + b2(): + jmp b3(u1 0) + b3(v1: u1): + return v1 + } + brillig(inline) fn decrement f3 { + b0(v0: u32): + v2 = sub v0, u32 1 + return v2 + } + "; + // main + // | + // V + // is_odd <-> is_even + // | | + // V V + // decrement + + let ssa = Ssa::from_str(src).unwrap(); + let order = compute_bottom_up_order(&ssa); + + assert_eq!(order.len(), 4); + let (ids, ws): (Vec<_>, Vec<_>) = order.into_iter().map(|(id, w)| (id.to_u32(), w)).unzip(); + let (ows, tws): (Vec<_>, Vec<_>) = ws.into_iter().unzip(); + + // Check order + assert_eq!(ids[0], 3, "decrement: first, it doesn't call anything"); + assert_eq!(ids[1], 1, "is_even: called by is_odd; removing first avoids cutting the graph"); + assert_eq!(ids[2], 2, "is_odd: called by is_odd and main"); + assert_eq!(ids[3], 0, "main: last, it's the entry"); + + // Check own weights + assert_eq!(ows, [2, 7, 7, 4]); + + // Check transitive weights + assert_eq!(tws[0], ows[0], "decrement"); + assert_eq!( + tws[1], + ows[1] + // own + tws[0] + // pushed from decrement + (ows[2] + tws[0]), // pulled from is_odd at the time is_even is emitted + "is_even" + ); + assert_eq!( + tws[2], + ows[2] + // own + tws[0] + // pushed from decrement + tws[1], // pushed from is_even + "is_odd" + ); + assert_eq!( + tws[3], + ows[3] + // own + tws[2], // pushed from is_odd + "main" + ); + assert!(tws[3] > max(tws[1], tws[2]), "ideally 'main' has the most weight"); + } } diff --git a/compiler/noirc_evaluator/src/ssa/opt/loop_invariant.rs b/compiler/noirc_evaluator/src/ssa/opt/loop_invariant.rs index c1f2a6b88e5..224916c95e9 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/loop_invariant.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/loop_invariant.rs @@ -36,7 +36,7 @@ impl Ssa { } impl Function { - fn loop_invariant_code_motion(&mut self) { + pub(super) fn loop_invariant_code_motion(&mut self) { Loops::find_all(self).hoist_loop_invariants(self); } } @@ -112,10 +112,12 @@ impl<'f> LoopInvariantContext<'f> { // If we are hoisting a MakeArray instruction, // we need to issue an extra inc_rc in case they are mutated afterward. - if matches!( - self.inserter.function.dfg[instruction_id], - Instruction::MakeArray { .. } - ) { + if self.inserter.function.runtime().is_brillig() + && matches!( + self.inserter.function.dfg[instruction_id], + Instruction::MakeArray { .. } + ) + { let result = self.inserter.function.dfg.instruction_results(instruction_id)[0]; let inc_rc = Instruction::IncrementRc { value: result }; @@ -233,7 +235,7 @@ impl<'f> LoopInvariantContext<'f> { } } Instruction::Binary(binary) => { - if !matches!(binary.operator, BinaryOp::Add | BinaryOp::Mul) { + if !matches!(binary.operator, BinaryOp::Add { .. } | BinaryOp::Mul { .. }) { return false; } diff --git a/compiler/noirc_evaluator/src/ssa/opt/make_constrain_not_equal.rs b/compiler/noirc_evaluator/src/ssa/opt/make_constrain_not_equal.rs new file mode 100644 index 00000000000..21f536eba2d --- /dev/null +++ b/compiler/noirc_evaluator/src/ssa/opt/make_constrain_not_equal.rs @@ -0,0 +1,72 @@ +use acvm::AcirField; + +use crate::ssa::{ + ir::{ + function::Function, + instruction::{Binary, BinaryOp, Instruction}, + value::Value, + }, + ssa_gen::Ssa, +}; + +impl Ssa { + /// A simple SSA pass to go through each [`Instruction::Constrain`], determine whether it's asserting + /// two values are not equal, and if so replace it with a [`Instruction::ConstrainNotEqual`]. + /// + /// Note that this pass must be placed after CFG flattening as the flattening pass cannot + /// handle this instruction. + #[tracing::instrument(level = "trace", skip(self))] + pub(crate) fn make_constrain_not_equal_instructions(mut self) -> Ssa { + for function in self.functions.values_mut() { + function.make_constrain_not_equal(); + } + self + } +} + +impl Function { + pub(crate) fn make_constrain_not_equal(&mut self) { + if !self.runtime().is_acir() { + return; + } + + for block in self.reachable_blocks() { + let instructions = self.dfg[block].instructions().to_vec(); + + for instruction in instructions { + let constrain_ne: Instruction = match &self.dfg[instruction] { + Instruction::Constrain(lhs, rhs, msg) => { + if self + .dfg + .get_numeric_constant(*rhs) + .map_or(false, |constant| constant.is_zero()) + { + if let Value::Instruction { instruction, .. } = + &self.dfg[self.dfg.resolve(*lhs)] + { + if let Instruction::Binary(Binary { + lhs, + rhs, + operator: BinaryOp::Eq, + .. + }) = self.dfg[*instruction] + { + Instruction::ConstrainNotEqual(lhs, rhs, msg.clone()) + } else { + continue; + } + } else { + continue; + } + } else { + continue; + } + } + _ => continue, + }; + + self.dfg[instruction] = constrain_ne; + } + } + } +} diff --git a/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs b/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs index 29ddfff323d..ce76825877a 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs @@ -79,6 +79,7 @@ mod block; use std::collections::{BTreeMap, BTreeSet}; use fxhash::{FxHashMap as HashMap, FxHashSet as HashSet}; +use vec_collections::VecSet; use crate::ssa::{ ir::{ @@ -619,7 +620,7 @@ impl<'f> PerFunctionContext<'f> { // then those parameters also alias each other. // We save parameters with repeat arguments to later mark those // parameters as aliasing one another. - let mut arg_set: HashMap> = HashMap::default(); + let mut arg_set = HashMap::default(); // Add an alias for each reference parameter for (parameter, argument) in destination_parameters.iter().zip(arguments) { @@ -632,7 +633,8 @@ impl<'f> PerFunctionContext<'f> { aliases.insert(*parameter); // Check if we have seen the same argument - let seen_parameters = arg_set.entry(argument).or_default(); + let seen_parameters = + arg_set.entry(argument).or_insert_with(VecSet::empty); // Add the current parameter to the parameters we have seen for this argument. // The previous parameters and the current one alias one another. seen_parameters.insert(*parameter); @@ -1002,7 +1004,7 @@ mod tests { let two = builder.field_constant(2u128); builder.insert_store(v5, two); let one = builder.field_constant(1u128); - let v3_plus_one = builder.insert_binary(v3, BinaryOp::Add, one); + let v3_plus_one = builder.insert_binary(v3, BinaryOp::Add { unchecked: false }, one); builder.terminate_with_jmp(b1, vec![v3_plus_one]); builder.switch_to_block(b3); diff --git a/compiler/noirc_evaluator/src/ssa/opt/mem2reg/alias_set.rs b/compiler/noirc_evaluator/src/ssa/opt/mem2reg/alias_set.rs index e32eaa70186..443b21cfd15 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/mem2reg/alias_set.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/mem2reg/alias_set.rs @@ -1,4 +1,4 @@ -use std::collections::BTreeSet; +use vec_collections::{AbstractVecSet, VecSet}; use crate::ssa::ir::value::ValueId; @@ -10,7 +10,7 @@ use crate::ssa::ir::value::ValueId; /// "unknown which aliases this may refer to" - `None`. #[derive(Debug, Default, Clone)] pub(super) struct AliasSet { - aliases: Option>, + aliases: Option>, } impl AliasSet { @@ -19,12 +19,10 @@ impl AliasSet { } pub(super) fn known(value: ValueId) -> AliasSet { - let mut aliases = BTreeSet::new(); - aliases.insert(value); - Self { aliases: Some(aliases) } + Self { aliases: Some(VecSet::single(value)) } } - pub(super) fn known_multiple(values: BTreeSet) -> AliasSet { + pub(super) fn known_multiple(values: VecSet<[ValueId; 1]>) -> AliasSet { Self { aliases: Some(values) } } @@ -32,7 +30,7 @@ impl AliasSet { /// particular value will be known to be zero, which is distinct from being unknown and /// possibly referring to any alias. pub(super) fn known_empty() -> AliasSet { - Self { aliases: Some(BTreeSet::new()) } + Self { aliases: Some(VecSet::empty()) } } pub(super) fn is_unknown(&self) -> bool { @@ -44,19 +42,33 @@ impl AliasSet { pub(super) fn single_alias(&self) -> Option { self.aliases .as_ref() - .and_then(|aliases| (aliases.len() == 1).then(|| *aliases.first().unwrap())) + .and_then(|aliases| (aliases.len() == 1).then(|| *aliases.iter().next().unwrap())) } /// Unify this alias set with another. The result of this set is empty if either set is empty. /// Otherwise, it is the union of both alias sets. pub(super) fn unify(&mut self, other: &Self) { if let (Some(self_aliases), Some(other_aliases)) = (&mut self.aliases, &other.aliases) { - self_aliases.extend(other_aliases); + self_aliases.extend(other_aliases.iter().cloned()); } else { self.aliases = None; } } + /// Returns true if calling `unify` would change something in this alias set. + /// + /// This is an optimization to avoid having to look up an entry ready to be modified in the [Block](crate::ssa::opt::mem2reg::block::Block), + /// because doing so would involve calling `Arc::make_mut` which clones the entry, ready for modification. + pub(super) fn should_unify(&self, other: &Self) -> bool { + if let (Some(self_aliases), Some(other_aliases)) = (&self.aliases, &other.aliases) { + // `unify` would extend `self_aliases` with `other_aliases`, so if `other_aliases` is a subset, then nothing would happen. + !other_aliases.is_subset(self_aliases) + } else { + // `unify` would set `aliases` to `None`, so if it's not `Some`, then nothing would happen. + self.aliases.is_some() + } + } + /// Inserts a new alias into this set if it is not unknown pub(super) fn insert(&mut self, new_alias: ValueId) { if let Some(aliases) = &mut self.aliases { @@ -82,6 +94,6 @@ impl AliasSet { /// The ordering is arbitrary (by lowest ValueId) so this method should only be /// used when you need an arbitrary ValueId from the alias set. pub(super) fn first(&self) -> Option { - self.aliases.as_ref().and_then(|aliases| aliases.first().copied()) + self.aliases.as_ref().and_then(|aliases| aliases.iter().next().copied()) } } diff --git a/compiler/noirc_evaluator/src/ssa/opt/mem2reg/block.rs b/compiler/noirc_evaluator/src/ssa/opt/mem2reg/block.rs index f4265b2466d..91e27f07b8e 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/mem2reg/block.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/mem2reg/block.rs @@ -128,6 +128,12 @@ impl Block { } for (expression, new_aliases) in &other.aliases { + // If nothing would change, then don't call `.entry(...).and_modify(...)` as it involves creating more `Arc`s. + if let Some(aliases) = self.aliases.get(expression) { + if !aliases.should_unify(new_aliases) { + continue; + } + } let expression = expression.clone(); self.aliases diff --git a/compiler/noirc_evaluator/src/ssa/opt/mod.rs b/compiler/noirc_evaluator/src/ssa/opt/mod.rs index 1105e15c30e..44796e2531e 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/mod.rs @@ -14,8 +14,10 @@ pub(crate) mod flatten_cfg; mod hint; mod inlining; mod loop_invariant; +mod make_constrain_not_equal; mod mem2reg; mod normalize_value_ids; +mod preprocess_fns; mod rc; mod remove_bit_shifts; mod remove_enable_side_effects; diff --git a/compiler/noirc_evaluator/src/ssa/opt/normalize_value_ids.rs b/compiler/noirc_evaluator/src/ssa/opt/normalize_value_ids.rs index 56f69a912d4..b248f6734a9 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/normalize_value_ids.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/normalize_value_ids.rs @@ -72,6 +72,10 @@ impl Context { let new_function_id = self.new_ids.function_ids[&old_function.id()]; let new_function = &mut self.functions[new_function_id]; + for (_, value) in old_function.dfg.globals.values_iter() { + new_function.dfg.make_global(value.get_type().into_owned()); + } + let mut reachable_blocks = PostOrder::with_function(old_function).into_vec(); reachable_blocks.reverse(); @@ -166,6 +170,11 @@ impl IdMaps { old_value: ValueId, ) -> ValueId { let old_value = old_function.dfg.resolve(old_value); + if old_function.dfg.is_global(old_value) { + // Globals are computed at compile-time and thus are expected to be remain normalized + // between SSA passes + return old_value; + } match &old_function.dfg[old_value] { value @ Value::Instruction { instruction, .. } => { *self.values.get(&old_value).unwrap_or_else(|| { @@ -192,6 +201,9 @@ impl IdMaps { } Value::Intrinsic(intrinsic) => new_function.dfg.import_intrinsic(*intrinsic), Value::ForeignFunction(name) => new_function.dfg.import_foreign_function(name), + Value::Global(_) => { + unreachable!("Should have handled the global case already"); + }, } } } diff --git a/compiler/noirc_evaluator/src/ssa/opt/preprocess_fns.rs b/compiler/noirc_evaluator/src/ssa/opt/preprocess_fns.rs new file mode 100644 index 00000000000..439c2da5a2d --- /dev/null +++ b/compiler/noirc_evaluator/src/ssa/opt/preprocess_fns.rs @@ -0,0 +1,58 @@ +//! Pre-process functions before inlining them into others. + +use crate::ssa::Ssa; + +use super::inlining; + +impl Ssa { + /// Run pre-processing steps on functions in isolation. + pub(crate) fn preprocess_functions(mut self, aggressiveness: i64) -> Ssa { + // Bottom-up order, starting with the "leaf" functions, so we inline already optimized code into the ones that call them. + let bottom_up = inlining::compute_bottom_up_order(&self); + + // As a heuristic to avoid optimizing functions near the entry point, find a cutoff weight. + let total_weight = + bottom_up.iter().fold(0usize, |acc, (_, (_, w))| (acc.saturating_add(*w))); + let mean_weight = total_weight / bottom_up.len(); + let cutoff_weight = mean_weight; + + // Preliminary inlining decisions. + let inline_infos = inlining::compute_inline_infos(&self, false, aggressiveness); + + for (id, (own_weight, transitive_weight)) in bottom_up { + // Skip preprocessing heavy functions that gained most of their weight from transitive accumulation. + // These can be processed later by the regular SSA passes. + if transitive_weight >= cutoff_weight && transitive_weight > own_weight * 2 { + continue; + } + // Functions which are inline targets will be processed in later passes. + // Here we want to treat the functions which will be inlined into them. + if let Some(info) = inline_infos.get(&id) { + if info.is_inline_target() { + continue; + } + } + let function = &self.functions[&id]; + // Start with an inline pass. + let mut function = function.inlined(&self, false, &inline_infos); + // Help unrolling determine bounds. + function.as_slice_optimization(); + // Prepare for unrolling + function.loop_invariant_code_motion(); + // We might not be able to unroll all loops without fully inlining them, so ignore errors. + let _ = function.unroll_loops_iteratively(); + // Reduce the number of redundant stores/loads after unrolling + function.mem2reg(); + // Try to reduce the number of blocks. + function.simplify_function(); + // Remove leftover instructions. + function.dead_instruction_elimination(true, false); + + // Put it back into the SSA, so the next functions can pick it up. + self.functions.insert(id, function); + } + + // Remove any functions that have been inlined into others already. + self.remove_unreachable_functions() + } +} diff --git a/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs b/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs index 0af8fbb0b5e..e36be71aeea 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs @@ -133,15 +133,23 @@ impl Context<'_> { let predicate = self.insert_cast(overflow, typ); let pow = self.pow(base, rhs); let pow = self.insert_cast(pow, typ); - (FieldElement::max_num_bits(), self.insert_binary(predicate, BinaryOp::Mul, pow)) + + // Unchecked mul because `predicate` will be 1 or 0 + ( + FieldElement::max_num_bits(), + self.insert_binary(predicate, BinaryOp::Mul { unchecked: true }, pow), + ) }; if max_bit <= bit_size { - self.insert_binary(lhs, BinaryOp::Mul, pow) + // Unchecked mul as it can't overflow + self.insert_binary(lhs, BinaryOp::Mul { unchecked: true }, pow) } else { let lhs_field = self.insert_cast(lhs, NumericType::NativeField); let pow_field = self.insert_cast(pow, NumericType::NativeField); - let result = self.insert_binary(lhs_field, BinaryOp::Mul, pow_field); + // Unchecked mul as this is a wrapping operation that we later truncate + let result = + self.insert_binary(lhs_field, BinaryOp::Mul { unchecked: true }, pow_field); let result = self.insert_truncate(result, bit_size, max_bit); self.insert_cast(result, typ) } @@ -159,6 +167,7 @@ impl Context<'_> { let lhs_typ = self.function.dfg.type_of_value(lhs).unwrap_numeric(); let base = self.field_constant(FieldElement::from(2_u128)); let pow = self.pow(base, rhs); + let pow = self.insert_cast(pow, lhs_typ); if lhs_typ.is_unsigned() { // unsigned right bit shift is just a normal division self.insert_binary(lhs, BinaryOp::Div, pow) @@ -169,14 +178,29 @@ impl Context<'_> { let lhs_sign_as_field = self.insert_cast(lhs_sign, NumericType::NativeField); let lhs_as_field = self.insert_cast(lhs, NumericType::NativeField); // For negative numbers, convert to 1-complement using wrapping addition of a + 1 - let one_complement = self.insert_binary(lhs_sign_as_field, BinaryOp::Add, lhs_as_field); + // Unchecked add as these are fields + let one_complement = self.insert_binary( + lhs_sign_as_field, + BinaryOp::Add { unchecked: true }, + lhs_as_field, + ); let one_complement = self.insert_truncate(one_complement, bit_size, bit_size + 1); let one_complement = self.insert_cast(one_complement, NumericType::signed(bit_size)); // Performs the division on the 1-complement (or the operand if positive) let shifted_complement = self.insert_binary(one_complement, BinaryOp::Div, pow); // Convert back to 2-complement representation if operand is negative let lhs_sign_as_int = self.insert_cast(lhs_sign, lhs_typ); - let shifted = self.insert_binary(shifted_complement, BinaryOp::Sub, lhs_sign_as_int); + + // The requirements for this to underflow are all of these: + // - lhs < 0 + // - ones_complement(lhs) / (2^rhs) == 0 + // As the upper bit is set for the ones complement of negative numbers we'd need 2^rhs + // to be larger than the lhs bitsize for this to overflow. + let shifted = self.insert_binary( + shifted_complement, + BinaryOp::Sub { unchecked: true }, + lhs_sign_as_int, + ); self.insert_truncate(shifted, bit_size, bit_size + 1) } } @@ -200,17 +224,18 @@ impl Context<'_> { let rhs_bits = rhs_bits[0]; let one = self.field_constant(FieldElement::one()); let mut r = one; + // All operations are unchecked as we're acting on Field types (which are always unchecked) for i in 1..bit_size + 1 { - let r_squared = self.insert_binary(r, BinaryOp::Mul, r); - let a = self.insert_binary(r_squared, BinaryOp::Mul, lhs); + let r_squared = self.insert_binary(r, BinaryOp::Mul { unchecked: true }, r); + let a = self.insert_binary(r_squared, BinaryOp::Mul { unchecked: true }, lhs); let idx = self.field_constant(FieldElement::from((bit_size - i) as i128)); let b = self.insert_array_get(rhs_bits, idx, Type::bool()); let not_b = self.insert_not(b); let b = self.insert_cast(b, NumericType::NativeField); let not_b = self.insert_cast(not_b, NumericType::NativeField); - let r1 = self.insert_binary(a, BinaryOp::Mul, b); - let r2 = self.insert_binary(r_squared, BinaryOp::Mul, not_b); - r = self.insert_binary(r1, BinaryOp::Add, r2); + let r1 = self.insert_binary(a, BinaryOp::Mul { unchecked: true }, b); + let r2 = self.insert_binary(r_squared, BinaryOp::Mul { unchecked: true }, not_b); + r = self.insert_binary(r1, BinaryOp::Add { unchecked: true }, r2); } r } else { diff --git a/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs b/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs index a22232ba49a..942fe67b5d5 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs @@ -126,7 +126,7 @@ impl Context { use Instruction::*; match instruction { Binary(binary) => match binary.operator { - BinaryOp::Add | BinaryOp::Sub | BinaryOp::Mul => { + BinaryOp::Add { .. } | BinaryOp::Sub { .. } | BinaryOp::Mul { .. } => { dfg.type_of_value(binary.lhs).is_unsigned() } BinaryOp::Div | BinaryOp::Mod => { @@ -143,6 +143,7 @@ impl Context { | Not(_) | Truncate { .. } | Constrain(..) + | ConstrainNotEqual(..) | RangeCheck { .. } | IfElse { .. } | IncrementRc { .. } @@ -239,13 +240,13 @@ mod test { let one = builder.numeric_constant(1u128, NumericType::bool()); builder.insert_enable_side_effects_if(one); - builder.insert_binary(v0, BinaryOp::Mul, two); + builder.insert_binary(v0, BinaryOp::Mul { unchecked: false }, two); builder.insert_enable_side_effects_if(one); - builder.insert_binary(v0, BinaryOp::Mul, two); + builder.insert_binary(v0, BinaryOp::Mul { unchecked: false }, two); builder.insert_enable_side_effects_if(one); - builder.insert_binary(v0, BinaryOp::Mul, two); + builder.insert_binary(v0, BinaryOp::Mul { unchecked: false }, two); builder.insert_enable_side_effects_if(one); - builder.insert_binary(v0, BinaryOp::Mul, two); + builder.insert_binary(v0, BinaryOp::Mul { unchecked: false }, two); builder.insert_enable_side_effects_if(one); let ssa = builder.finish(); @@ -275,7 +276,10 @@ mod test { assert_eq!(instructions.len(), 4); for instruction in instructions.iter().take(4) { - assert_eq!(&main.dfg[*instruction], &Instruction::binary(BinaryOp::Mul, v0, two)); + assert_eq!( + &main.dfg[*instruction], + &Instruction::binary(BinaryOp::Mul { unchecked: false }, v0, two) + ); } } } diff --git a/compiler/noirc_evaluator/src/ssa/opt/remove_unreachable.rs b/compiler/noirc_evaluator/src/ssa/opt/remove_unreachable.rs index 41023b5f376..9b80b3a4d23 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/remove_unreachable.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/remove_unreachable.rs @@ -19,9 +19,13 @@ impl Ssa { pub(crate) fn remove_unreachable_functions(mut self) -> Self { let mut used_functions = HashSet::default(); - for function_id in self.functions.keys() { - if self.is_entry_point(*function_id) { - collect_reachable_functions(&self, *function_id, &mut used_functions); + for (id, function) in self.functions.iter() { + // XXX: `self.is_entry_point(*id)` could leave Brillig functions that nobody calls in the SSA. + let is_entry_point = function.id() == self.main_id + || function.runtime().is_acir() && function.runtime().is_entry_point(); + + if is_entry_point { + collect_reachable_functions(&self, *id, &mut used_functions); } } @@ -78,3 +82,54 @@ fn used_functions(func: &Function) -> BTreeSet { used_function_ids } + +#[cfg(test)] +mod tests { + use crate::ssa::opt::assert_normalized_ssa_equals; + + use super::Ssa; + + #[test] + fn remove_unused_brillig() { + let src = " + brillig(inline) fn main f0 { + b0(v0: u32): + v2 = call f1(v0) -> u32 + v4 = add v0, u32 1 + v5 = eq v2, v4 + constrain v2 == v4 + return + } + brillig(inline) fn increment f1 { + b0(v0: u32): + v2 = add v0, u32 1 + return v2 + } + brillig(inline) fn increment_acir f2 { + b0(v0: u32): + v2 = add v0, u32 1 + return v2 + } + "; + + let ssa = Ssa::from_str(src).unwrap(); + let ssa = ssa.remove_unreachable_functions(); + + let expected = " + brillig(inline) fn main f0 { + b0(v0: u32): + v2 = call f1(v0) -> u32 + v4 = add v0, u32 1 + v5 = eq v2, v4 + constrain v2 == v4 + return + } + brillig(inline) fn increment f1 { + b0(v0: u32): + v2 = add v0, u32 1 + return v2 + } + "; + assert_normalized_ssa_equals(ssa, expected); + } +} diff --git a/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs b/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs index ab4256197b9..a6e5c96d638 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs @@ -24,7 +24,10 @@ use acvm::{acir::AcirField, FieldElement}; use im::HashSet; use crate::{ - brillig::brillig_gen::convert_ssa_function, + brillig::{ + brillig_gen::{brillig_globals::convert_ssa_globals, convert_ssa_function}, + brillig_ir::brillig_variable::BrilligVariable, + }, errors::RuntimeError, ssa::{ ir::{ @@ -54,41 +57,40 @@ impl Ssa { /// fewer SSA instructions, but that can still result in more Brillig opcodes. #[tracing::instrument(level = "trace", skip(self))] pub(crate) fn unroll_loops_iteratively( - mut self: Ssa, + mut self, max_bytecode_increase_percent: Option, ) -> Result { - for (_, function) in self.functions.iter_mut() { - // Take a snapshot of the function to compare byte size increase, - // but only if the setting indicates we have to, otherwise skip it. - let orig_func_and_max_incr_pct = max_bytecode_increase_percent - .filter(|_| function.runtime().is_brillig()) - .map(|max_incr_pct| (function.clone(), max_incr_pct)); - - // Try to unroll loops first: - let (mut has_unrolled, mut unroll_errors) = function.try_unroll_loops(); - - // Keep unrolling until no more errors are found - while !unroll_errors.is_empty() { - let prev_unroll_err_count = unroll_errors.len(); - - // Simplify the SSA before retrying - simplify_between_unrolls(function); - - // Unroll again - let (new_unrolled, new_errors) = function.try_unroll_loops(); - unroll_errors = new_errors; - has_unrolled |= new_unrolled; - - // If we didn't manage to unroll any more loops, exit - if unroll_errors.len() >= prev_unroll_err_count { - return Err(unroll_errors.swap_remove(0)); - } - } + let mut global_cache = None; + + for function in self.functions.values_mut() { + let is_brillig = function.runtime().is_brillig(); + + // Take a snapshot in case we have to restore it. + let orig_function = + (max_bytecode_increase_percent.is_some() && is_brillig).then(|| function.clone()); + + // We must be able to unroll ACIR loops at this point, so exit on failure to unroll. + let has_unrolled = function.unroll_loops_iteratively()?; + + // Check if the size increase is acceptable + // This is here now instead of in `Function::unroll_loops_iteratively` because we'd need + // more finessing to convince the borrow checker that it's okay to share a read-only reference + // to the globals and a mutable reference to the function at the same time, both part of the `Ssa`. + if has_unrolled && is_brillig { + if let Some(max_incr_pct) = max_bytecode_increase_percent { + if global_cache.is_none() { + // DIE is run at the end of our SSA optimizations, so we mark all globals as in use here. + let used_globals = + &self.globals.dfg.values_iter().map(|(id, _)| id).collect(); + let (_, brillig_globals) = + convert_ssa_globals(false, &self.globals, used_globals); + global_cache = Some(brillig_globals); + } + let brillig_globals = global_cache.as_ref().unwrap(); - if has_unrolled { - if let Some((orig_function, max_incr_pct)) = orig_func_and_max_incr_pct { - let new_size = brillig_bytecode_size(function); - let orig_size = brillig_bytecode_size(&orig_function); + let orig_function = orig_function.expect("took snapshot to compare"); + let new_size = brillig_bytecode_size(function, brillig_globals); + let orig_size = brillig_bytecode_size(&orig_function, brillig_globals); if !is_new_size_ok(orig_size, new_size, max_incr_pct) { *function = orig_function; } @@ -100,6 +102,38 @@ impl Ssa { } impl Function { + /// Try to unroll loops in the function. + /// + /// Returns an `Err` if it cannot be done, for example because the loop bounds + /// cannot be determined at compile time. This can happen during pre-processing, + /// but it should still leave the function in a partially unrolled, but valid state. + /// + /// If successful, returns a flag indicating whether any loops have been unrolled. + pub(super) fn unroll_loops_iteratively(&mut self) -> Result { + // Try to unroll loops first: + let (mut has_unrolled, mut unroll_errors) = self.try_unroll_loops(); + + // Keep unrolling until no more errors are found + while !unroll_errors.is_empty() { + let prev_unroll_err_count = unroll_errors.len(); + + // Simplify the SSA before retrying + simplify_between_unrolls(self); + + // Unroll again + let (new_unrolled, new_errors) = self.try_unroll_loops(); + unroll_errors = new_errors; + has_unrolled |= new_unrolled; + + // If we didn't manage to unroll any more loops, exit + if unroll_errors.len() >= prev_unroll_err_count { + return Err(unroll_errors.swap_remove(0)); + } + } + + Ok(has_unrolled) + } + // Loop unrolling in brillig can lead to a code explosion currently. // This can also be true for ACIR, but we have no alternative to unrolling in ACIR. // Brillig also generally prefers smaller code rather than faster code, @@ -310,11 +344,13 @@ impl Loop { // simplified to a simple jump. return None; } - assert_eq!( - instructions.len(), - 1, - "The header should just compare the induction variable and jump" - ); + + if instructions.len() != 1 { + // The header should just compare the induction variable and jump. + // If that's not the case, this might be a `loop` and not a `for` loop. + return None; + } + match &function.dfg[instructions[0]] { Instruction::Binary(Binary { lhs: _, operator: BinaryOp::Lt, rhs }) => { function.dfg.get_numeric_constant(*rhs) @@ -619,10 +655,16 @@ impl Loop { let header = &function.dfg[self.header]; let induction_var = header.parameters()[0]; - back.instructions().iter().filter(|instruction| { - let instruction = &function.dfg[**instruction]; - matches!(instruction, Instruction::Binary(Binary { lhs, operator: BinaryOp::Add, rhs: _ }) if *lhs == induction_var) - }).count() + back.instructions() + .iter() + .filter(|instruction| { + let instruction = &function.dfg[**instruction]; + matches!(instruction, + Instruction::Binary(Binary { lhs, operator: BinaryOp::Add { .. }, rhs: _ }) + if *lhs == induction_var + ) + }) + .count() } /// Decide if this loop is small enough that it can be inlined in a way that the number @@ -744,7 +786,13 @@ fn get_induction_variable(function: &Function, block: BasicBlockId) -> Result usize { +fn brillig_bytecode_size( + function: &Function, + globals: &HashMap, +) -> usize { // We need to do some SSA passes in order for the conversion to be able to go ahead, // otherwise we can hit `unreachable!()` instructions in `convert_ssa_instruction`. // Creating a clone so as not to modify the originals. @@ -984,9 +1035,9 @@ fn brillig_bytecode_size(function: &Function) -> usize { simplify_between_unrolls(&mut temp); // This is to try to prevent hitting ICE. - temp.dead_instruction_elimination(false); + temp.dead_instruction_elimination(false, true); - convert_ssa_function(&temp, false).byte_code.len() + convert_ssa_function(&temp, false, globals).byte_code.len() } /// Decide if the new bytecode size is acceptable, compared to the original. diff --git a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs index fcaaf74f533..e2eea234dc7 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs @@ -5,7 +5,9 @@ use acvm::acir::circuit::ErrorSelector; use crate::ssa::{ function_builder::FunctionBuilder, ir::{ - basic_block::BasicBlockId, function::FunctionId, instruction::ConstrainError, + basic_block::BasicBlockId, + function::{Function, FunctionId}, + instruction::ConstrainError, value::ValueId, }, }; @@ -24,7 +26,7 @@ impl ParsedSsa { struct Translator { builder: FunctionBuilder, - /// Maps function names to their IDs + /// Maps internal function names (e.g. "f1") to their IDs functions: HashMap, /// Maps block names to their IDs @@ -135,14 +137,14 @@ impl Translator { match block.terminator { ParsedTerminator::Jmp { destination, arguments } => { - let block_id = self.lookup_block(destination)?; + let block_id = self.lookup_block(&destination)?; let arguments = self.translate_values(arguments)?; self.builder.terminate_with_jmp(block_id, arguments); } ParsedTerminator::Jmpif { condition, then_block, else_block } => { let condition = self.translate_value(condition)?; - let then_destination = self.lookup_block(then_block)?; - let else_destination = self.lookup_block(else_block)?; + let then_destination = self.lookup_block(&then_block)?; + let else_destination = self.lookup_block(&else_block)?; self.builder.terminate_with_jmpif(condition, then_destination, else_destination); } ParsedTerminator::Return(values) => { @@ -187,8 +189,13 @@ impl Translator { let function_id = if let Some(id) = self.builder.import_intrinsic(&function.name) { id } else { - let function_id = self.lookup_function(function)?; - self.builder.import_function(function_id) + let maybe_func = + self.lookup_function(&function).map(|f| self.builder.import_function(f)); + + maybe_func.or_else(|e| { + // e.g. `v2 = call v0(v1) -> u32`, a lambda passed as a parameter + self.lookup_variable(&function).map_err(|_| e) + })? }; let arguments = self.translate_values(arguments)?; @@ -293,7 +300,14 @@ impl Translator { ParsedValue::NumericConstant { constant, typ } => { Ok(self.builder.numeric_constant(constant, typ.unwrap_numeric())) } - ParsedValue::Variable(identifier) => self.lookup_variable(identifier), + ParsedValue::Variable(identifier) => self.lookup_variable(&identifier).or_else(|e| { + self.lookup_function(&identifier) + .map(|f| { + // e.g. `v3 = call f1(f2, v0) -> u32` + self.builder.import_function(f) + }) + .map_err(|_| e) + }), } } @@ -314,27 +328,27 @@ impl Translator { Ok(()) } - fn lookup_variable(&mut self, identifier: Identifier) -> Result { + fn lookup_variable(&mut self, identifier: &Identifier) -> Result { if let Some(value_id) = self.variables[&self.current_function_id()].get(&identifier.name) { Ok(*value_id) } else { - Err(SsaError::UnknownVariable(identifier)) + Err(SsaError::UnknownVariable(identifier.clone())) } } - fn lookup_block(&mut self, identifier: Identifier) -> Result { + fn lookup_block(&mut self, identifier: &Identifier) -> Result { if let Some(block_id) = self.blocks[&self.current_function_id()].get(&identifier.name) { Ok(*block_id) } else { - Err(SsaError::UnknownBlock(identifier)) + Err(SsaError::UnknownBlock(identifier.clone())) } } - fn lookup_function(&mut self, identifier: Identifier) -> Result { + fn lookup_function(&mut self, identifier: &Identifier) -> Result { if let Some(function_id) = self.functions.get(&identifier.name) { Ok(*function_id) } else { - Err(SsaError::UnknownFunction(identifier)) + Err(SsaError::UnknownFunction(identifier.clone())) } } @@ -345,6 +359,8 @@ impl Translator { // that the SSA we parsed was printed by the `SsaBuilder`, which normalizes // before each print. ssa.normalize_ids(); + // Does not matter what ID we use here. + ssa.globals = Function::new("globals".to_owned(), ssa.main_id); ssa } diff --git a/compiler/noirc_evaluator/src/ssa/parser/mod.rs b/compiler/noirc_evaluator/src/ssa/parser/mod.rs index 96aef1f3c32..143ba511879 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/mod.rs @@ -286,9 +286,9 @@ impl<'a> Parser<'a> { fn eat_binary_op(&mut self) -> ParseResult> { let op = match self.token.token() { - Token::Keyword(Keyword::Add) => BinaryOp::Add, - Token::Keyword(Keyword::Sub) => BinaryOp::Sub, - Token::Keyword(Keyword::Mul) => BinaryOp::Mul, + Token::Keyword(Keyword::Add) => BinaryOp::Add { unchecked: false }, + Token::Keyword(Keyword::Sub) => BinaryOp::Sub { unchecked: false }, + Token::Keyword(Keyword::Mul) => BinaryOp::Mul { unchecked: false }, Token::Keyword(Keyword::Div) => BinaryOp::Div, Token::Keyword(Keyword::Eq) => BinaryOp::Eq, Token::Keyword(Keyword::Mod) => BinaryOp::Mod, @@ -298,6 +298,9 @@ impl<'a> Parser<'a> { Token::Keyword(Keyword::Xor) => BinaryOp::Xor, Token::Keyword(Keyword::Shl) => BinaryOp::Shl, Token::Keyword(Keyword::Shr) => BinaryOp::Shr, + Token::Keyword(Keyword::UncheckedAdd) => BinaryOp::Add { unchecked: true }, + Token::Keyword(Keyword::UncheckedSub) => BinaryOp::Sub { unchecked: true }, + Token::Keyword(Keyword::UncheckedMul) => BinaryOp::Mul { unchecked: true }, _ => return Ok(None), }; diff --git a/compiler/noirc_evaluator/src/ssa/parser/tests.rs b/compiler/noirc_evaluator/src/ssa/parser/tests.rs index b5aac13cfd8..8c24b2ec458 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/tests.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/tests.rs @@ -326,11 +326,27 @@ fn test_array_get_set_bug() { #[test] fn test_binary() { - for op in ["add", "sub", "mul", "div", "eq", "mod", "lt", "and", "or", "xor", "shl", "shr"] { + for op in [ + "add", + "sub", + "mul", + "div", + "eq", + "mod", + "lt", + "and", + "or", + "xor", + "shl", + "shr", + "unchecked_add", + "unchecked_sub", + "unchecked_mul", + ] { let src = format!( " acir(inline) fn main f0 {{ - b0(v0: Field, v1: Field): + b0(v0: u32, v1: u32): v2 = {op} v0, v1 return }} diff --git a/compiler/noirc_evaluator/src/ssa/parser/token.rs b/compiler/noirc_evaluator/src/ssa/parser/token.rs index 83a2a1d1ed2..eb09209466d 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/token.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/token.rs @@ -160,6 +160,9 @@ pub(crate) enum Keyword { Then, To, Truncate, + UncheckedAdd, + UncheckedSub, + UncheckedMul, Value, Xor, } @@ -217,6 +220,9 @@ impl Keyword { "then" => Keyword::Then, "to" => Keyword::To, "truncate" => Keyword::Truncate, + "unchecked_add" => Keyword::UncheckedAdd, + "unchecked_sub" => Keyword::UncheckedSub, + "unchecked_mul" => Keyword::UncheckedMul, "value" => Keyword::Value, "xor" => Keyword::Xor, _ => return None, @@ -278,6 +284,9 @@ impl Display for Keyword { Keyword::Then => write!(f, "then"), Keyword::To => write!(f, "to"), Keyword::Truncate => write!(f, "truncate"), + Keyword::UncheckedAdd => write!(f, "unchecked_add"), + Keyword::UncheckedSub => write!(f, "unchecked_sub"), + Keyword::UncheckedMul => write!(f, "unchecked_mul"), Keyword::Value => write!(f, "value"), Keyword::Xor => write!(f, "xor"), } diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs index e89d1d2a0c3..a845c5654b2 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs @@ -1,10 +1,11 @@ +use std::collections::BTreeMap; use std::sync::{Arc, Mutex, RwLock}; use acvm::{acir::AcirField, FieldElement}; use iter_extended::vecmap; use noirc_errors::Location; use noirc_frontend::ast::{BinaryOpKind, Signedness}; -use noirc_frontend::monomorphization::ast::{self, LocalId, Parameters}; +use noirc_frontend::monomorphization::ast::{self, GlobalId, InlineType, LocalId, Parameters}; use noirc_frontend::monomorphization::ast::{FuncId, Program}; use crate::errors::RuntimeError; @@ -19,6 +20,7 @@ use crate::ssa::ir::types::{NumericType, Type}; use crate::ssa::ir::value::ValueId; use super::value::{Tree, Value, Values}; +use super::GlobalsGraph; use fxhash::{FxHashMap as HashMap, FxHashSet as HashSet}; /// The FunctionContext is the main context object for translating a @@ -71,6 +73,14 @@ pub(super) struct SharedContext { /// Shared counter used to assign the ID of the next function function_counter: AtomicCounter, + /// A pseudo function that represents global values. + /// Globals are only concerned with the values and instructions (due to Instruction::MakeArray) + /// in a function's DataFlowGraph. However, in order to re-use various codegen methods + /// we need to use the same `Function` type. + pub(super) globals_context: Function, + + pub(super) globals: BTreeMap, + /// The entire monomorphized source program pub(super) program: Program, } @@ -78,7 +88,8 @@ pub(super) struct SharedContext { #[derive(Copy, Clone)] pub(super) struct Loop { pub(super) loop_entry: BasicBlockId, - pub(super) loop_index: ValueId, + /// The loop index will be `Some` for a `for` and `None` for a `loop` + pub(super) loop_index: Option, pub(super) loop_end: BasicBlockId, } @@ -100,6 +111,7 @@ impl<'a> FunctionContext<'a> { parameters: &Parameters, runtime: RuntimeType, shared_context: &'a SharedContext, + globals: GlobalsGraph, ) -> Self { let function_id = shared_context .pop_next_function_in_queue() @@ -108,6 +120,8 @@ impl<'a> FunctionContext<'a> { let mut builder = FunctionBuilder::new(function_name, function_id); builder.set_runtime(runtime); + builder.set_globals(Arc::new(globals)); + let definitions = HashMap::default(); let mut this = Self { definitions, builder, shared_context, loops: Vec::new() }; this.add_parameters_to_scope(parameters); @@ -121,11 +135,15 @@ impl<'a> FunctionContext<'a> { /// avoid calling new_function until the previous function is completely finished with ssa-gen. pub(super) fn new_function(&mut self, id: IrFunctionId, func: &ast::Function) { self.definitions.clear(); + + let globals = self.builder.current_function.dfg.globals.clone(); if func.unconstrained { self.builder.new_brillig_function(func.name.clone(), id, func.inline_type); } else { self.builder.new_function(func.name.clone(), id, func.inline_type); } + self.builder.set_globals(globals); + self.add_parameters_to_scope(&func.parameters); } @@ -317,12 +335,24 @@ impl<'a> FunctionContext<'a> { // We use unsafe casts here, this is fine as we're casting to a `field` type. let as_field = self.builder.insert_cast(input, NumericType::NativeField); let sign_field = self.builder.insert_cast(sign, NumericType::NativeField); - let positive_predicate = self.builder.insert_binary(sign_field, BinaryOp::Mul, as_field); - let two_complement = self.builder.insert_binary(bit_width, BinaryOp::Sub, as_field); + + // All of these operations are unchecked because they deal with fields + let positive_predicate = + self.builder.insert_binary(sign_field, BinaryOp::Mul { unchecked: true }, as_field); + let two_complement = + self.builder.insert_binary(bit_width, BinaryOp::Sub { unchecked: true }, as_field); let sign_not_field = self.builder.insert_cast(sign_not, NumericType::NativeField); - let negative_predicate = - self.builder.insert_binary(sign_not_field, BinaryOp::Mul, two_complement); - self.builder.insert_binary(positive_predicate, BinaryOp::Add, negative_predicate) + let negative_predicate = self.builder.insert_binary( + sign_not_field, + BinaryOp::Mul { unchecked: true }, + two_complement, + ); + // Unchecked addition because either `positive_predicate` or `negative_predicate` will be 0 + self.builder.insert_binary( + positive_predicate, + BinaryOp::Add { unchecked: true }, + negative_predicate, + ) } /// Insert constraints ensuring that the operation does not overflow the bit size of the result @@ -477,8 +507,12 @@ impl<'a> FunctionContext<'a> { //Check the result has the same sign as its inputs let result_sign = self.builder.insert_binary(result, BinaryOp::Lt, half_width); let sign_diff = self.builder.insert_binary(result_sign, BinaryOp::Eq, lhs_sign); - let sign_diff_with_predicate = - self.builder.insert_binary(sign_diff, BinaryOp::Mul, same_sign); + // Unchecked multiplication because boolean inputs + let sign_diff_with_predicate = self.builder.insert_binary( + sign_diff, + BinaryOp::Mul { unchecked: true }, + same_sign, + ); let overflow_check = Instruction::Constrain( sign_diff_with_predicate, same_sign, @@ -491,7 +525,9 @@ impl<'a> FunctionContext<'a> { // First we compute the absolute value of operands, and their product let lhs_abs = self.absolute_value_helper(lhs, lhs_sign, bit_size); let rhs_abs = self.absolute_value_helper(rhs, rhs_sign, bit_size); - let product_field = self.builder.insert_binary(lhs_abs, BinaryOp::Mul, rhs_abs); + // Unchecked mul because these are fields + let product_field = + self.builder.insert_binary(lhs_abs, BinaryOp::Mul { unchecked: true }, rhs_abs); // It must not already overflow the bit_size self.builder.set_location(location).insert_range_check( product_field, @@ -505,8 +541,12 @@ impl<'a> FunctionContext<'a> { let not_same = self.builder.insert_not(same_sign); let not_same_sign_field = self.insert_safe_cast(not_same, NumericType::unsigned(bit_size), location); - let positive_maximum_with_offset = - self.builder.insert_binary(half_width, BinaryOp::Add, not_same_sign_field); + // Unchecked add because adding 1 to half_width can't overflow + let positive_maximum_with_offset = self.builder.insert_binary( + half_width, + BinaryOp::Add { unchecked: true }, + not_same_sign_field, + ); let product_overflow_check = self.builder.insert_binary(product, BinaryOp::Lt, positive_maximum_with_offset); @@ -610,7 +650,8 @@ impl<'a> FunctionContext<'a> { if offset != 0 { let typ = self.builder.type_of_value(address).unwrap_numeric(); let offset = self.builder.numeric_constant(offset, typ); - address = self.builder.insert_binary(address, BinaryOp::Add, offset); + address = + self.builder.insert_binary(address, BinaryOp::Add { unchecked: true }, offset); } address } @@ -633,6 +674,10 @@ impl<'a> FunctionContext<'a> { self.definitions.get(&id).expect("lookup: variable not defined").clone() } + pub(super) fn lookup_global(&self, id: GlobalId) -> Values { + self.shared_context.globals.get(&id).expect("lookup_global: variable not defined").clone() + } + /// Extract the given field of the tuple. Panics if the given Values is not /// a Tree::Branch or does not have enough fields. pub(super) fn get_field(tuple: Values, field_index: usize) -> Values { @@ -868,14 +913,20 @@ impl<'a> FunctionContext<'a> { self.builder.numeric_constant(self.element_size(array), NumericType::length_type()); // The actual base index is the user's index * the array element type's size - let mut index = - self.builder.set_location(location).insert_binary(index, BinaryOp::Mul, element_size); + // Unchecked mul because we are reaching for an array element: if it overflows here + // it would have overflowed when creating the array. + let mut index = self.builder.set_location(location).insert_binary( + index, + BinaryOp::Mul { unchecked: true }, + element_size, + ); let one = self.builder.numeric_constant(FieldElement::one(), NumericType::length_type()); new_value.for_each(|value| { let value = value.eval(self); array = self.builder.insert_array_set(array, index, value); - index = self.builder.insert_binary(index, BinaryOp::Add, one); + // Unchecked add because this can't overflow (it would have overflowed when creating the array) + index = self.builder.insert_binary(index, BinaryOp::Add { unchecked: true }, one); }); array } @@ -960,13 +1011,8 @@ impl<'a> FunctionContext<'a> { } } - pub(crate) fn enter_loop( - &mut self, - loop_entry: BasicBlockId, - loop_index: ValueId, - loop_end: BasicBlockId, - ) { - self.loops.push(Loop { loop_entry, loop_index, loop_end }); + pub(crate) fn enter_loop(&mut self, loop_: Loop) { + self.loops.push(loop_); } pub(crate) fn exit_loop(&mut self) { @@ -1000,9 +1046,9 @@ fn operator_requires_swapped_operands(op: BinaryOpKind) -> bool { /// to represent the full operation correctly. fn convert_operator(op: BinaryOpKind) -> BinaryOp { match op { - BinaryOpKind::Add => BinaryOp::Add, - BinaryOpKind::Subtract => BinaryOp::Sub, - BinaryOpKind::Multiply => BinaryOp::Mul, + BinaryOpKind::Add => BinaryOp::Add { unchecked: false }, + BinaryOpKind::Subtract => BinaryOp::Sub { unchecked: false }, + BinaryOpKind::Multiply => BinaryOp::Mul { unchecked: false }, BinaryOpKind::Divide => BinaryOp::Div, BinaryOpKind::Modulo => BinaryOp::Mod, BinaryOpKind::Equal => BinaryOp::Eq, @@ -1022,11 +1068,46 @@ fn convert_operator(op: BinaryOpKind) -> BinaryOp { impl SharedContext { /// Create a new SharedContext for the given monomorphized program. pub(super) fn new(program: Program) -> Self { + let globals_shared_context = SharedContext::new_for_globals(); + + let globals_id = Program::global_space_id(); + + // Queue the function representing the globals space for compilation + globals_shared_context.get_or_queue_function(globals_id); + + let mut context = FunctionContext::new( + "globals".to_owned(), + &vec![], + RuntimeType::Brillig(InlineType::default()), + &globals_shared_context, + GlobalsGraph::default(), + ); + let mut globals = BTreeMap::default(); + for (id, global) in program.globals.iter() { + let values = context.codegen_expression(global).unwrap(); + globals.insert(*id, values); + } + Self { functions: Default::default(), function_queue: Default::default(), function_counter: Default::default(), program, + globals_context: context.builder.current_function, + globals, + } + } + + pub(super) fn new_for_globals() -> Self { + let globals_context = Function::new_for_globals(); + + Self { + functions: Default::default(), + function_queue: Default::default(), + function_counter: Default::default(), + program: Default::default(), + globals_context, + globals: Default::default(), } } diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs index 10b8129e8db..fbb2b306bdf 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -6,7 +6,7 @@ use acvm::AcirField; use noirc_frontend::token::FmtStrFragment; pub(crate) use program::Ssa; -use context::SharedContext; +use context::{Loop, SharedContext}; use iter_extended::{try_vecmap, vecmap}; use noirc_errors::Location; use noirc_frontend::ast::{UnaryOp, Visibility}; @@ -23,6 +23,7 @@ use self::{ value::{Tree, Values}, }; +use super::ir::dfg::GlobalsGraph; use super::ir::instruction::ErrorType; use super::ir::types::NumericType; use super::{ @@ -49,6 +50,8 @@ pub(crate) fn generate_ssa(program: Program) -> Result { let return_location = program.return_location; let context = SharedContext::new(program); + let globals = GlobalsGraph::from_dfg(context.globals_context.dfg.clone()); + let main_id = Program::main_id(); let main = context.program.main(); @@ -60,7 +63,7 @@ pub(crate) fn generate_ssa(program: Program) -> Result { RuntimeType::Acir(main.inline_type) }; let mut function_context = - FunctionContext::new(main.name.clone(), &main.parameters, main_runtime, &context); + FunctionContext::new(main.name.clone(), &main.parameters, main_runtime, &context, globals); // Generate the call_data bus from the relevant parameters. We create it *before* processing the function body let call_data = function_context.builder.call_data_bus(is_databus); @@ -121,7 +124,9 @@ pub(crate) fn generate_ssa(program: Program) -> Result { function_context.codegen_function_body(&function.body)?; } - Ok(function_context.builder.finish()) + let mut ssa = function_context.builder.finish(); + ssa.globals = context.globals_context; + Ok(ssa) } impl<'a> FunctionContext<'a> { @@ -147,6 +152,7 @@ impl<'a> FunctionContext<'a> { Expression::Index(index) => self.codegen_index(index), Expression::Cast(cast) => self.codegen_cast(cast), Expression::For(for_expr) => self.codegen_for(for_expr), + Expression::Loop(block) => self.codegen_loop(block), Expression::If(if_expr) => self.codegen_if(if_expr), Expression::Tuple(tuple) => self.codegen_tuple(tuple), Expression::ExtractTupleField(tuple, index) => { @@ -179,6 +185,7 @@ impl<'a> FunctionContext<'a> { fn codegen_ident_reference(&mut self, ident: &ast::Ident) -> Values { match &ident.definition { ast::Definition::Local(id) => self.lookup(*id), + ast::Definition::Global(id) => self.lookup_global(*id), ast::Definition::Function(id) => self.get_or_queue_function(*id), ast::Definition::Oracle(name) => self.builder.import_foreign_function(name).into(), ast::Definition::Builtin(name) | ast::Definition::LowLevel(name) => { @@ -446,8 +453,13 @@ impl<'a> FunctionContext<'a> { let type_size = Self::convert_type(element_type).size_of_type(); let type_size = self.builder.numeric_constant(type_size as u128, NumericType::length_type()); - let base_index = - self.builder.set_location(location).insert_binary(index, BinaryOp::Mul, type_size); + // This shouldn't overflow as we are reaching for an initial array offset + // (otherwise it would have overflowed when creating the array) + let base_index = self.builder.set_location(location).insert_binary( + index, + BinaryOp::Mul { unchecked: true }, + type_size, + ); let mut field_index = 0u128; Ok(Self::map_type(element_type, |typ| { @@ -520,6 +532,22 @@ impl<'a> FunctionContext<'a> { /// ... This is the current insert point after codegen_for finishes ... /// ``` fn codegen_for(&mut self, for_expr: &ast::For) -> Result { + self.builder.set_location(for_expr.start_range_location); + let start_index = self.codegen_non_tuple_expression(&for_expr.start_range)?; + + self.builder.set_location(for_expr.end_range_location); + let end_index = self.codegen_non_tuple_expression(&for_expr.end_range)?; + + if let (Some(start_constant), Some(end_constant)) = ( + self.builder.current_function.dfg.get_numeric_constant(start_index), + self.builder.current_function.dfg.get_numeric_constant(end_index), + ) { + // If we can determine that the loop contains zero iterations then there's no need to codegen the loop. + if start_constant >= end_constant { + return Ok(Self::unit_value()); + } + } + let loop_entry = self.builder.insert_block(); let loop_body = self.builder.insert_block(); let loop_end = self.builder.insert_block(); @@ -530,13 +558,7 @@ impl<'a> FunctionContext<'a> { // Remember the blocks and variable used in case there are break/continue instructions // within the loop which need to jump to them. - self.enter_loop(loop_entry, loop_index, loop_end); - - self.builder.set_location(for_expr.start_range_location); - let start_index = self.codegen_non_tuple_expression(&for_expr.start_range)?; - - self.builder.set_location(for_expr.end_range_location); - let end_index = self.codegen_non_tuple_expression(&for_expr.end_range)?; + self.enter_loop(Loop { loop_entry, loop_index: Some(loop_index), loop_end }); // Set the location of the initial jmp instruction to the start range. This is the location // used to issue an error if the start range cannot be determined at compile-time. @@ -566,6 +588,38 @@ impl<'a> FunctionContext<'a> { Ok(Self::unit_value()) } + /// Codegens a loop, creating three new blocks in the process. + /// The return value of a loop is always a unit literal. + /// + /// For example, the loop `loop { body }` is codegen'd as: + /// + /// ```text + /// br loop_body() + /// loop_body(): + /// v3 = ... codegen body ... + /// br loop_body() + /// loop_end(): + /// ... This is the current insert point after codegen_for finishes ... + /// ``` + fn codegen_loop(&mut self, block: &Expression) -> Result { + let loop_body = self.builder.insert_block(); + let loop_end = self.builder.insert_block(); + + self.enter_loop(Loop { loop_entry: loop_body, loop_index: None, loop_end }); + + self.builder.terminate_with_jmp(loop_body, vec![]); + + // Compile the loop body + self.builder.switch_to_block(loop_body); + self.codegen_expression(block)?; + self.builder.terminate_with_jmp(loop_body, vec![]); + + // Finish by switching to the end of the loop + self.builder.switch_to_block(loop_end); + self.exit_loop(); + Ok(Self::unit_value()) + } + /// Codegens an if expression, handling the case of what to do if there is no 'else'. /// /// For example, the expression `if cond { a } else { b }` is codegen'd as: @@ -702,7 +756,12 @@ impl<'a> FunctionContext<'a> { // We add one here in the case of a slice insert as a slice insert at the length of the slice // can be converted to a slice push back - let len_plus_one = self.builder.insert_binary(arguments[0], BinaryOp::Add, one); + // This is unchecked as the slice length could be u32::max + let len_plus_one = self.builder.insert_binary( + arguments[0], + BinaryOp::Add { unchecked: false }, + one, + ); self.codegen_slice_access_check(arguments[2], Some(len_plus_one)); } @@ -826,8 +885,12 @@ impl<'a> FunctionContext<'a> { let loop_ = self.current_loop(); // Must remember to increment i before jumping - let new_loop_index = self.make_offset(loop_.loop_index, 1); - self.builder.terminate_with_jmp(loop_.loop_entry, vec![new_loop_index]); + if let Some(loop_index) = loop_.loop_index { + let new_loop_index = self.make_offset(loop_index, 1); + self.builder.terminate_with_jmp(loop_.loop_entry, vec![new_loop_index]); + } else { + self.builder.terminate_with_jmp(loop_.loop_entry, vec![]); + } Self::unit_value() } } diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/program.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/program.rs index 98cdc0adad9..7cd5c5c3990 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/program.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/program.rs @@ -1,6 +1,7 @@ -use std::{collections::BTreeMap, fmt::Display}; +use std::collections::BTreeMap; use acvm::acir::circuit::ErrorSelector; +use fxhash::FxHashSet as HashSet; use iter_extended::btree_map; use serde::{Deserialize, Serialize}; use serde_with::serde_as; @@ -11,12 +12,16 @@ use crate::ssa::ir::{ }; use noirc_frontend::hir_def::types::Type as HirType; +use super::ValueId; + /// Contains the entire SSA representation of the program. #[serde_as] #[derive(Serialize, Deserialize)] pub(crate) struct Ssa { #[serde_as(as = "Vec<(_, _)>")] pub(crate) functions: BTreeMap, + pub(crate) globals: Function, + pub(crate) used_global_values: HashSet, pub(crate) main_id: FunctionId, #[serde(skip)] pub(crate) next_id: AtomicCounter, @@ -53,6 +58,12 @@ impl Ssa { next_id: AtomicCounter::starting_after(max_id), entry_point_to_generated_index: BTreeMap::new(), error_selector_to_type: error_types, + // These fields should be set afterwards as globals are generated + // outside of the FunctionBuilder, which is where the `Ssa` is instantiated. + globals: Function::new_for_globals(), + // This field is set only after running DIE and is utilized + // for optimizing implementation of globals post-SSA. + used_global_values: HashSet::default(), } } @@ -99,15 +110,6 @@ impl Ssa { } } -impl Display for Ssa { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - for function in self.functions.values() { - writeln!(f, "{function}")?; - } - Ok(()) - } -} - #[cfg(test)] mod test { use crate::ssa::ir::map::Id; @@ -129,8 +131,8 @@ mod test { let one = builder.field_constant(1u128); let three = builder.field_constant(3u128); - let v1 = builder.insert_binary(v0, BinaryOp::Add, one); - let v2 = builder.insert_binary(v1, BinaryOp::Mul, three); + let v1 = builder.insert_binary(v0, BinaryOp::Add { unchecked: false }, one); + let v2 = builder.insert_binary(v1, BinaryOp::Mul { unchecked: false }, three); builder.terminate_with_return(vec![v2]); let ssa = builder.finish(); diff --git a/compiler/noirc_frontend/src/ast/enumeration.rs b/compiler/noirc_frontend/src/ast/enumeration.rs new file mode 100644 index 00000000000..eeeb823b9fc --- /dev/null +++ b/compiler/noirc_frontend/src/ast/enumeration.rs @@ -0,0 +1,50 @@ +use std::fmt::Display; + +use crate::ast::{Ident, UnresolvedGenerics, UnresolvedType}; +use crate::token::SecondaryAttribute; + +use iter_extended::vecmap; +use noirc_errors::Span; + +use super::{Documented, ItemVisibility}; + +/// Ast node for an enum +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct NoirEnumeration { + pub name: Ident, + pub attributes: Vec, + pub visibility: ItemVisibility, + pub generics: UnresolvedGenerics, + pub variants: Vec>, + pub span: Span, +} + +impl NoirEnumeration { + pub fn is_abi(&self) -> bool { + self.attributes.iter().any(|attr| attr.is_abi()) + } +} + +/// We only support variants of the form `Name(A, B, ...)` currently. +/// Enum variants like `Name { a: A, b: B, .. }` will be implemented later +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct EnumVariant { + pub name: Ident, + pub parameters: Vec, +} + +impl Display for NoirEnumeration { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let generics = vecmap(&self.generics, |generic| generic.to_string()); + let generics = if generics.is_empty() { "".into() } else { generics.join(", ") }; + + writeln!(f, "enum {}{} {{", self.name, generics)?; + + for variant in self.variants.iter() { + let parameters = vecmap(&variant.item.parameters, ToString::to_string).join(", "); + writeln!(f, " {}({}),", variant.item.name, parameters)?; + } + + write!(f, "}}") + } +} diff --git a/compiler/noirc_frontend/src/ast/mod.rs b/compiler/noirc_frontend/src/ast/mod.rs index 35e57cd4528..33f504437c0 100644 --- a/compiler/noirc_frontend/src/ast/mod.rs +++ b/compiler/noirc_frontend/src/ast/mod.rs @@ -5,6 +5,7 @@ //! Noir's Ast is produced by the parser and taken as input to name resolution, //! where it is converted into the Hir (defined in the hir_def module). mod docs; +mod enumeration; mod expression; mod function; mod statement; @@ -24,6 +25,7 @@ use proptest_derive::Arbitrary; use acvm::FieldElement; pub use docs::*; +pub use enumeration::*; use noirc_errors::Span; use serde::{Deserialize, Serialize}; pub use statement::*; @@ -580,12 +582,13 @@ impl std::fmt::Display for ItemVisibility { } } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Default)] /// Represents whether the parameter is public or known only to the prover. pub enum Visibility { Public, // Constants are not allowed in the ABI for main at the moment. // Constant, + #[default] Private, /// DataBus is public input handled as private input. We use the fact that return values are properly computed by the program to avoid having them as public inputs /// it is useful for recursion and is handled by the proving system. diff --git a/compiler/noirc_frontend/src/ast/statement.rs b/compiler/noirc_frontend/src/ast/statement.rs index bd65fc33377..3a70ff33a35 100644 --- a/compiler/noirc_frontend/src/ast/statement.rs +++ b/compiler/noirc_frontend/src/ast/statement.rs @@ -12,6 +12,7 @@ use super::{ }; use crate::ast::UnresolvedTypeData; use crate::elaborator::types::SELF_TYPE_NAME; +use crate::elaborator::Turbofish; use crate::lexer::token::SpannedToken; use crate::node_interner::{ InternedExpressionKind, InternedPattern, InternedStatementKind, NodeInterner, @@ -45,6 +46,7 @@ pub enum StatementKind { Expression(Expression), Assign(AssignStatement), For(ForLoopStatement), + Loop(Expression), Break, Continue, /// This statement should be executed at compile-time @@ -106,6 +108,9 @@ impl StatementKind { // A semicolon on a for loop is optional and does nothing StatementKind::For(_) => self, + // A semicolon on a loop is optional and does nothing + StatementKind::Loop(..) => self, + // No semicolon needed for a resolved statement StatementKind::Interned(_) => self, @@ -531,6 +536,12 @@ impl PathSegment { pub fn turbofish_span(&self) -> Span { Span::from(self.ident.span().end()..self.span.end()) } + + pub fn turbofish(&self) -> Option { + self.generics + .as_ref() + .map(|generics| Turbofish { span: self.turbofish_span(), generics: generics.clone() }) + } } impl From for PathSegment { @@ -961,6 +972,7 @@ impl Display for StatementKind { StatementKind::Expression(expression) => expression.fmt(f), StatementKind::Assign(assign) => assign.fmt(f), StatementKind::For(for_loop) => for_loop.fmt(f), + StatementKind::Loop(block) => write!(f, "loop {}", block), StatementKind::Break => write!(f, "break"), StatementKind::Continue => write!(f, "continue"), StatementKind::Comptime(statement) => write!(f, "comptime {}", statement.kind), diff --git a/compiler/noirc_frontend/src/ast/visitor.rs b/compiler/noirc_frontend/src/ast/visitor.rs index 2f60532980a..5c4781df7a5 100644 --- a/compiler/noirc_frontend/src/ast/visitor.rs +++ b/compiler/noirc_frontend/src/ast/visitor.rs @@ -21,15 +21,17 @@ use crate::{ }; use super::{ - ForBounds, FunctionReturnType, GenericTypeArgs, IntegerBitSize, ItemVisibility, Pattern, - Signedness, TraitBound, TraitImplItemKind, TypePath, UnresolvedGenerics, - UnresolvedTraitConstraint, UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression, + ForBounds, FunctionReturnType, GenericTypeArgs, IntegerBitSize, ItemVisibility, + NoirEnumeration, Pattern, Signedness, TraitBound, TraitImplItemKind, TypePath, + UnresolvedGenerics, UnresolvedTraitConstraint, UnresolvedType, UnresolvedTypeData, + UnresolvedTypeExpression, }; #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum AttributeTarget { Module, Struct, + Enum, Trait, Function, Let, @@ -142,6 +144,10 @@ pub trait Visitor { true } + fn visit_noir_enum(&mut self, _: &NoirEnumeration, _: Span) -> bool { + true + } + fn visit_noir_type_alias(&mut self, _: &NoirTypeAlias, _: Span) -> bool { true } @@ -296,6 +302,10 @@ pub trait Visitor { true } + fn visit_loop_statement(&mut self, _: &Expression) -> bool { + true + } + fn visit_comptime_statement(&mut self, _: &Statement) -> bool { true } @@ -523,6 +533,7 @@ impl Item { } ItemKind::TypeAlias(noir_type_alias) => noir_type_alias.accept(self.span, visitor), ItemKind::Struct(noir_struct) => noir_struct.accept(self.span, visitor), + ItemKind::Enum(noir_enum) => noir_enum.accept(self.span, visitor), ItemKind::ModuleDecl(module_declaration) => { module_declaration.accept(self.span, visitor); } @@ -771,6 +782,26 @@ impl NoirStruct { } } +impl NoirEnumeration { + pub fn accept(&self, span: Span, visitor: &mut impl Visitor) { + if visitor.visit_noir_enum(self, span) { + self.accept_children(visitor); + } + } + + pub fn accept_children(&self, visitor: &mut impl Visitor) { + for attribute in &self.attributes { + attribute.accept(AttributeTarget::Enum, visitor); + } + + for variant in &self.variants { + for parameter in &variant.item.parameters { + parameter.accept(visitor); + } + } + } +} + impl NoirTypeAlias { pub fn accept(&self, span: Span, visitor: &mut impl Visitor) { if visitor.visit_noir_type_alias(self, span) { @@ -1104,6 +1135,11 @@ impl Statement { StatementKind::For(for_loop_statement) => { for_loop_statement.accept(visitor); } + StatementKind::Loop(block) => { + if visitor.visit_loop_statement(block) { + block.accept(visitor); + } + } StatementKind::Comptime(statement) => { if visitor.visit_comptime_statement(statement) { statement.accept(visitor); diff --git a/compiler/noirc_frontend/src/elaborator/comptime.rs b/compiler/noirc_frontend/src/elaborator/comptime.rs index d88bb62e871..9f5eef6e785 100644 --- a/compiler/noirc_frontend/src/elaborator/comptime.rs +++ b/compiler/noirc_frontend/src/elaborator/comptime.rs @@ -442,7 +442,21 @@ impl<'context> Elaborator<'context> { self.crate_id, &mut self.errors, ) { - generated_items.types.insert(type_id, the_struct); + generated_items.structs.insert(type_id, the_struct); + } + } + ItemKind::Enum(enum_def) => { + if let Some((type_id, the_enum)) = dc_mod::collect_enum( + self.interner, + self.def_maps.get_mut(&self.crate_id).unwrap(), + self.usage_tracker, + Documented::new(enum_def, item.doc_comments), + self.file, + self.local_module, + self.crate_id, + &mut self.errors, + ) { + generated_items.enums.insert(type_id, the_enum); } } ItemKind::Impl(r#impl) => { diff --git a/compiler/noirc_frontend/src/elaborator/expressions.rs b/compiler/noirc_frontend/src/elaborator/expressions.rs index 1b3bc645cbc..ef2ae9c4df0 100644 --- a/compiler/noirc_frontend/src/elaborator/expressions.rs +++ b/compiler/noirc_frontend/src/elaborator/expressions.rs @@ -52,7 +52,7 @@ impl<'context> Elaborator<'context> { ExpressionKind::If(if_) => self.elaborate_if(*if_), ExpressionKind::Variable(variable) => return self.elaborate_variable(variable), ExpressionKind::Tuple(tuple) => self.elaborate_tuple(tuple), - ExpressionKind::Lambda(lambda) => self.elaborate_lambda(*lambda), + ExpressionKind::Lambda(lambda) => self.elaborate_lambda(*lambda, None), ExpressionKind::Parenthesized(expr) => return self.elaborate_expression(*expr), ExpressionKind::Quote(quote) => self.elaborate_quote(quote, expr.span), ExpressionKind::Comptime(comptime, _) => { @@ -387,17 +387,28 @@ impl<'context> Elaborator<'context> { fn elaborate_call(&mut self, call: CallExpression, span: Span) -> (HirExpression, Type) { let (func, func_type) = self.elaborate_expression(*call.func); + let func_arg_types = + if let Type::Function(args, _, _, _) = &func_type { Some(args) } else { None }; let mut arguments = Vec::with_capacity(call.arguments.len()); - let args = vecmap(call.arguments, |arg| { + let args = vecmap(call.arguments.into_iter().enumerate(), |(arg_index, arg)| { let span = arg.span; + let expected_type = func_arg_types.and_then(|args| args.get(arg_index)); let (arg, typ) = if call.is_macro_call { - self.elaborate_in_comptime_context(|this| this.elaborate_expression(arg)) + self.elaborate_in_comptime_context(|this| { + this.elaborate_expression_with_type(arg, expected_type) + }) } else { - self.elaborate_expression(arg) + self.elaborate_expression_with_type(arg, expected_type) }; + // Try to unify this argument type against the function's argument type + // so that a potential lambda following this argument can have more concrete types. + if let Some(expected_type) = expected_type { + let _ = expected_type.unify(&typ); + } + arguments.push(arg); (typ, arg, span) }); @@ -458,6 +469,32 @@ impl<'context> Elaborator<'context> { None }; + let call_span = Span::from(object_span.start()..method_name_span.end()); + let location = Location::new(call_span, self.file); + + let (function_id, function_name) = method_ref.clone().into_function_id_and_name( + object_type.clone(), + generics.clone(), + location, + self.interner, + ); + + let func_type = + self.type_check_variable(function_name.clone(), function_id, generics.clone()); + self.interner.push_expr_type(function_id, func_type.clone()); + + let func_arg_types = + if let Type::Function(args, _, _, _) = &func_type { Some(args) } else { None }; + + // Try to unify the object type with the first argument of the function. + // The reason to do this is that many methods that take a lambda will yield `self` or part of `self` + // as a parameter. By unifying `self` with the first argument we'll potentially get more + // concrete types in the arguments that are function types, which will later be passed as + // lambda parameter hints. + if let Some(first_arg_type) = func_arg_types.and_then(|args| args.first()) { + let _ = first_arg_type.unify(&object_type); + } + // These arguments will be given to the desugared function call. // Compared to the method arguments, they also contain the object. let mut function_args = Vec::with_capacity(method_call.arguments.len() + 1); @@ -465,17 +502,22 @@ impl<'context> Elaborator<'context> { function_args.push((object_type.clone(), object, object_span)); - for arg in method_call.arguments { + for (arg_index, arg) in method_call.arguments.into_iter().enumerate() { let span = arg.span; - let (arg, typ) = self.elaborate_expression(arg); + let expected_type = func_arg_types.and_then(|args| args.get(arg_index + 1)); + let (arg, typ) = self.elaborate_expression_with_type(arg, expected_type); + + // Try to unify this argument type against the function's argument type + // so that a potential lambda following this argument can have more concrete types. + if let Some(expected_type) = expected_type { + let _ = expected_type.unify(&typ); + } + arguments.push(arg); function_args.push((typ, arg, span)); } - let call_span = Span::from(object_span.start()..method_name_span.end()); - let location = Location::new(call_span, self.file); let method = method_call.method_name; - let turbofish_generics = generics.clone(); let is_macro_call = method_call.is_macro_call; let method_call = HirMethodCallExpression { method, object, arguments, location, generics }; @@ -485,18 +527,9 @@ impl<'context> Elaborator<'context> { // Desugar the method call into a normal, resolved function call // so that the backend doesn't need to worry about methods // TODO: update object_type here? - let ((function_id, function_name), function_call) = method_call.into_function_call( - method_ref, - object_type, - is_macro_call, - location, - self.interner, - ); - let func_type = - self.type_check_variable(function_name, function_id, turbofish_generics); - - self.interner.push_expr_type(function_id, func_type.clone()); + let function_call = + method_call.into_function_call(function_id, is_macro_call, location); self.interner .add_function_reference(func_id, Location::new(method_name_span, self.file)); @@ -520,6 +553,26 @@ impl<'context> Elaborator<'context> { } } + /// Elaborates an expression knowing that it has to match a given type. + fn elaborate_expression_with_type( + &mut self, + arg: Expression, + typ: Option<&Type>, + ) -> (ExprId, Type) { + let ExpressionKind::Lambda(lambda) = arg.kind else { + return self.elaborate_expression(arg); + }; + + let span = arg.span; + let type_hint = + if let Some(Type::Function(func_args, _, _, _)) = typ { Some(func_args) } else { None }; + let (hir_expr, typ) = self.elaborate_lambda(*lambda, type_hint); + let id = self.interner.push_expr(hir_expr); + self.interner.push_expr_location(id, span, self.file); + self.interner.push_expr_type(id, typ.clone()); + (id, typ) + } + fn check_method_call_visibility(&mut self, func_id: FuncId, object_type: &Type, name: &Ident) { if !method_call_is_visible( object_type, @@ -775,7 +828,10 @@ impl<'context> Elaborator<'context> { span, }, }; - self.push_trait_constraint(constraint, expr_id); + self.push_trait_constraint( + constraint, expr_id, + true, // this constraint should lead to choosing a trait impl + ); self.type_check_operator_method(expr_id, trait_id, operand_type, span); } typ @@ -843,19 +899,38 @@ impl<'context> Elaborator<'context> { (HirExpression::Tuple(element_ids), Type::Tuple(element_types)) } - fn elaborate_lambda(&mut self, lambda: Lambda) -> (HirExpression, Type) { + /// For elaborating a lambda we might get `parameters_type_hints`. These come from a potential + /// call that has this lambda as the argument. + /// The parameter type hints will be the types of the function type corresponding to the lambda argument. + fn elaborate_lambda( + &mut self, + lambda: Lambda, + parameters_type_hints: Option<&Vec>, + ) -> (HirExpression, Type) { self.push_scope(); let scope_index = self.scopes.current_scope_index(); self.lambda_stack.push(LambdaContext { captures: Vec::new(), scope_index }); let mut arg_types = Vec::with_capacity(lambda.parameters.len()); - let parameters = vecmap(lambda.parameters, |(pattern, typ)| { - let parameter = DefinitionKind::Local(None); - let typ = self.resolve_inferred_type(typ); - arg_types.push(typ.clone()); - (self.elaborate_pattern(pattern, typ.clone(), parameter, true), typ) - }); + let parameters = + vecmap(lambda.parameters.into_iter().enumerate(), |(index, (pattern, typ))| { + let parameter = DefinitionKind::Local(None); + let typ = if let UnresolvedTypeData::Unspecified = typ.typ { + if let Some(parameter_type_hint) = + parameters_type_hints.and_then(|hints| hints.get(index)) + { + parameter_type_hint.clone() + } else { + self.interner.next_type_variable_with_kind(Kind::Any) + } + } else { + self.resolve_type(typ) + }; + + arg_types.push(typ.clone()); + (self.elaborate_pattern(pattern, typ.clone(), parameter, true), typ) + }); let return_type = self.resolve_inferred_type(lambda.return_type); let body_span = lambda.body.span; diff --git a/compiler/noirc_frontend/src/elaborator/lints.rs b/compiler/noirc_frontend/src/elaborator/lints.rs index d3b776bea24..82a5e19054b 100644 --- a/compiler/noirc_frontend/src/elaborator/lints.rs +++ b/compiler/noirc_frontend/src/elaborator/lints.rs @@ -282,6 +282,7 @@ fn can_return_without_recursing(interner: &NodeInterner, func_id: FuncId, expr_i HirStatement::Semi(e) => check(e), // Rust doesn't seem to check the for loop body (it's bounds might mean it's never called). HirStatement::For(e) => check(e.start_range) && check(e.end_range), + HirStatement::Loop(e) => check(e), HirStatement::Constrain(_) | HirStatement::Comptime(_) | HirStatement::Break diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index aeee417789e..0e8850b6543 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -3,10 +3,6 @@ use std::{ rc::Rc, }; -use crate::{ - ast::ItemVisibility, graph::CrateGraph, hir_def::traits::ResolvedTraitBound, - node_interner::GlobalValue, usage_tracker::UsageTracker, StructField, StructType, TypeBindings, -}; use crate::{ ast::{ BlockExpression, FunctionKind, GenericTypeArgs, Ident, NoirFunction, NoirStruct, Param, @@ -41,6 +37,14 @@ use crate::{ token::SecondaryAttribute, Shared, Type, TypeVariable, }; +use crate::{ + ast::{ItemVisibility, UnresolvedType}, + graph::CrateGraph, + hir_def::traits::ResolvedTraitBound, + node_interner::GlobalValue, + usage_tracker::UsageTracker, + StructField, StructType, TypeBindings, +}; mod comptime; mod expressions; @@ -57,6 +61,7 @@ mod unquote; use fm::FileId; use iter_extended::vecmap; use noirc_errors::{Location, Span, Spanned}; +pub use path_resolution::Turbofish; use path_resolution::{PathResolution, PathResolutionItem}; use types::bind_ordered_generics; @@ -197,7 +202,11 @@ struct FunctionContext { /// 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)>, + /// The `select impl` bool indicates whether, after verifying the trait constraint, + /// the resulting trait impl should be the one used for a call (sometimes trait + /// constraints are verified but there's no call associated with them, like in the + /// case of checking generic arguments) + trait_constraints: Vec<(TraitConstraint, ExprId, bool /* select impl */)>, } impl<'context> Elaborator<'context> { @@ -311,11 +320,11 @@ impl<'context> Elaborator<'context> { } // Must resolve structs before we resolve globals. - self.collect_struct_definitions(&items.types); + self.collect_struct_definitions(&items.structs); self.define_function_metas(&mut items.functions, &mut items.impls, &mut items.trait_impls); - self.collect_traits(&items.traits); + self.collect_traits(&mut items.traits); // Before we resolve any function symbols we must go through our impls and // re-collect the methods within into their proper module. This cannot be @@ -341,7 +350,7 @@ impl<'context> Elaborator<'context> { // since the generated items are checked beforehand as well. self.run_attributes( &items.traits, - &items.types, + &items.structs, &items.functions, &items.module_attributes, ); @@ -354,6 +363,7 @@ impl<'context> Elaborator<'context> { self.current_trait = Some(trait_id); self.elaborate_functions(unresolved_trait.fns_with_default_impl); } + self.current_trait = None; for impls in items.impls.into_values() { @@ -475,7 +485,7 @@ impl<'context> Elaborator<'context> { self.add_existing_variable_to_scope(name, parameter.clone(), warn_if_unused); } - self.add_trait_constraints_to_scope(&func_meta); + self.add_trait_constraints_to_scope(&func_meta.trait_constraints, func_meta.location.span); let (hir_func, body_type) = match kind { FunctionKind::Builtin @@ -501,7 +511,7 @@ impl<'context> Elaborator<'context> { // when multiple impls are available. Instead we default first to choose the Field or u64 impl. self.check_and_pop_function_context(); - self.remove_trait_constraints_from_scope(&func_meta); + self.remove_trait_constraints_from_scope(&func_meta.trait_constraints); let func_scope_tree = self.scopes.end_function(); @@ -549,7 +559,7 @@ impl<'context> Elaborator<'context> { } } - for (mut constraint, expr_id) in context.trait_constraints { + for (mut constraint, expr_id, select_impl) in context.trait_constraints { let span = self.interner.expr_span(&expr_id); if matches!(&constraint.typ, Type::MutableReference(_)) { @@ -564,6 +574,7 @@ impl<'context> Elaborator<'context> { &constraint.trait_bound.trait_generics.ordered, &constraint.trait_bound.trait_generics.named, expr_id, + select_impl, span, ); } @@ -733,8 +744,13 @@ impl<'context> Elaborator<'context> { None } - /// TODO: This is currently only respected for generic free functions - /// there's a bunch of other places where trait constraints can pop up + /// Resolve the given trait constraints and add them to scope as we go. + /// This second step is necessary to resolve subsequent constraints such + /// as `::Bar: Eq` which may lookup an impl which was assumed + /// by a previous constraint. + /// + /// If these constraints are unwanted afterward they should be manually + /// removed from the interner. fn resolve_trait_constraints( &mut self, where_clause: &[UnresolvedTraitConstraint], @@ -745,12 +761,102 @@ impl<'context> Elaborator<'context> { .collect() } - pub fn resolve_trait_constraint( + /// Expands any traits in a where clause to mention all associated types if they were + /// elided by the user. See `add_missing_named_generics` for more detail. + /// + /// Returns all newly created generics to be added to this function/trait/impl. + fn desugar_trait_constraints( + &mut self, + where_clause: &mut [UnresolvedTraitConstraint], + ) -> Vec { + where_clause + .iter_mut() + .flat_map(|constraint| { + self.add_missing_named_generics(&constraint.typ, &mut constraint.trait_bound) + }) + .collect() + } + + /// For each associated type that isn't mentioned in a trait bound, this adds + /// the type as an implicit generic to the where clause and returns the newly + /// created generics in a vector to add to the function/trait/impl later. + /// For example, this will turn a function using a trait with 2 associated types: + /// + /// `fn foo() where T: Foo { ... }` + /// + /// into: + /// `fn foo() where T: Foo { ... }` + /// + /// with a vector of `` returned so that the caller can then modify the function to: + /// `fn foo() where T: Foo { ... }` + fn add_missing_named_generics( + &mut self, + object: &UnresolvedType, + bound: &mut TraitBound, + ) -> Vec { + let mut added_generics = Vec::new(); + + let Ok(item) = self.resolve_path_or_error(bound.trait_path.clone()) else { + return Vec::new(); + }; + + let PathResolutionItem::Trait(trait_id) = item else { + return Vec::new(); + }; + + let the_trait = self.get_trait_mut(trait_id); + + if the_trait.associated_types.len() > bound.trait_generics.named_args.len() { + let trait_name = the_trait.name.to_string(); + + for associated_type in &the_trait.associated_types.clone() { + if !bound + .trait_generics + .named_args + .iter() + .any(|(name, _)| name.0.contents == *associated_type.name.as_ref()) + { + // This generic isn't contained in the bound's named arguments, + // so add it by creating a fresh type variable. + let new_generic_id = self.interner.next_type_variable_id(); + let kind = associated_type.type_var.kind(); + let type_var = TypeVariable::unbound(new_generic_id, kind); + + let span = bound.trait_path.span; + let name = format!("<{object} as {trait_name}>::{}", associated_type.name); + let name = Rc::new(name); + let typ = Type::NamedGeneric(type_var.clone(), name.clone()); + let typ = self.interner.push_quoted_type(typ); + let typ = UnresolvedTypeData::Resolved(typ).with_span(span); + let ident = Ident::new(associated_type.name.as_ref().clone(), span); + + bound.trait_generics.named_args.push((ident, typ)); + added_generics.push(ResolvedGeneric { name, span, type_var }); + } + } + } + + added_generics + } + + /// Resolves a trait constraint and adds it to scope as an assumed impl. + /// This second step is necessary to resolve subsequent constraints such + /// as `::Bar: Eq` which may lookup an impl which was assumed + /// by a previous constraint. + fn resolve_trait_constraint( &mut self, constraint: &UnresolvedTraitConstraint, ) -> Option { let typ = self.resolve_type(constraint.typ.clone()); let trait_bound = self.resolve_trait_bound(&constraint.trait_bound)?; + + self.add_trait_bound_to_scope( + constraint.trait_bound.trait_path.span, + &typ, + &trait_bound, + trait_bound.trait_id, + ); + Some(TraitConstraint { typ, trait_bound }) } @@ -800,10 +906,13 @@ impl<'context> Elaborator<'context> { let has_inline_attribute = has_no_predicates_attribute || should_fold; let is_pub_allowed = self.pub_allowed(func, in_contract); self.add_generics(&func.def.generics); + let mut generics = vecmap(&self.generics, |generic| generic.type_var.clone()); + + let new_generics = self.desugar_trait_constraints(&mut func.def.where_clause); + generics.extend(new_generics.into_iter().map(|generic| generic.type_var)); let mut trait_constraints = self.resolve_trait_constraints(&func.def.where_clause); - let mut generics = vecmap(&self.generics, |generic| generic.type_var.clone()); let mut parameters = Vec::new(); let mut parameter_types = Vec::new(); let mut parameter_idents = Vec::new(); @@ -874,6 +983,9 @@ impl<'context> Elaborator<'context> { None }; + // Remove the traits assumed by `resolve_trait_constraints` from scope + self.remove_trait_constraints_from_scope(&trait_constraints); + let meta = FuncMeta { name: name_ident, kind: func.kind, @@ -932,7 +1044,7 @@ impl<'context> Elaborator<'context> { Type::MutableReference(typ) => { self.mark_type_as_used(typ); } - Type::InfixExpr(left, _op, right) => { + Type::InfixExpr(left, _op, right, _) => { self.mark_type_as_used(left); self.mark_type_as_used(right); } @@ -1013,10 +1125,10 @@ impl<'context> Elaborator<'context> { } } - fn add_trait_constraints_to_scope(&mut self, func_meta: &FuncMeta) { - for constraint in &func_meta.trait_constraints { + fn add_trait_constraints_to_scope(&mut self, constraints: &[TraitConstraint], span: Span) { + for constraint in constraints { self.add_trait_bound_to_scope( - func_meta, + span, &constraint.typ, &constraint.trait_bound, constraint.trait_bound.trait_id, @@ -1030,7 +1142,7 @@ impl<'context> Elaborator<'context> { let self_type = self.self_type.clone().expect("Expected a self type if there's a current trait"); self.add_trait_bound_to_scope( - func_meta, + span, &self_type, &constraint.trait_bound, constraint.trait_bound.trait_id, @@ -1038,8 +1150,8 @@ impl<'context> Elaborator<'context> { } } - fn remove_trait_constraints_from_scope(&mut self, func_meta: &FuncMeta) { - for constraint in &func_meta.trait_constraints { + fn remove_trait_constraints_from_scope(&mut self, constraints: &[TraitConstraint]) { + for constraint in constraints { self.interner .remove_assumed_trait_implementations_for_trait(constraint.trait_bound.trait_id); } @@ -1052,7 +1164,7 @@ impl<'context> Elaborator<'context> { fn add_trait_bound_to_scope( &mut self, - func_meta: &FuncMeta, + span: Span, object: &Type, trait_bound: &ResolvedTraitBound, starting_trait_id: TraitId, @@ -1064,7 +1176,6 @@ impl<'context> Elaborator<'context> { if let Some(the_trait) = self.interner.try_get_trait(trait_id) { let trait_name = the_trait.name.to_string(); let typ = object.clone(); - let span = func_meta.location.span; self.push_err(TypeCheckError::UnneededTraitConstraint { trait_name, typ, span }); } } @@ -1081,12 +1192,7 @@ impl<'context> Elaborator<'context> { let parent_trait_bound = self.instantiate_parent_trait_bound(trait_bound, &parent_trait_bound); - self.add_trait_bound_to_scope( - func_meta, - object, - &parent_trait_bound, - starting_trait_id, - ); + self.add_trait_bound_to_scope(span, object, &parent_trait_bound, starting_trait_id); } } } @@ -1316,15 +1422,22 @@ impl<'context> Elaborator<'context> { self.generics = trait_impl.resolved_generics.clone(); let where_clause = self.resolve_trait_constraints(&trait_impl.where_clause); + self.remove_trait_constraints_from_scope(&where_clause); self.collect_trait_impl_methods(trait_id, trait_impl, &where_clause); let span = trait_impl.object_type.span; self.declare_methods_on_struct(Some(trait_id), &mut trait_impl.methods, span); + let trait_visibility = self.interner.get_trait(trait_id).visibility; + let methods = trait_impl.methods.function_ids(); for func_id in &methods { self.interner.set_function_trait(*func_id, self_type.clone(), trait_id); + + // A trait impl method has the same visibility as its trait + let modifiers = self.interner.function_modifiers_mut(func_id); + modifiers.visibility = trait_visibility; } let trait_generics = trait_impl.resolved_trait_generics.clone(); @@ -1576,7 +1689,7 @@ impl<'context> Elaborator<'context> { Type::MutableReference(typ) | Type::Array(_, typ) | Type::Slice(typ) => { self.check_type_is_not_more_private_then_item(name, visibility, typ, span); } - Type::InfixExpr(left, _op, right) => { + Type::InfixExpr(left, _op, right, _) => { self.check_type_is_not_more_private_then_item(name, visibility, left, span); self.check_type_is_not_more_private_then_item(name, visibility, right, span); } @@ -1805,6 +1918,17 @@ impl<'context> Elaborator<'context> { self.add_generics(&trait_impl.generics); trait_impl.resolved_generics = self.generics.clone(); + let new_generics = self.desugar_trait_constraints(&mut trait_impl.where_clause); + for new_generic in new_generics { + trait_impl.resolved_generics.push(new_generic.clone()); + self.generics.push(new_generic); + } + + // We need to resolve the where clause before any associated types to be + // able to resolve trait as type syntax, eg. `` in case there + // is a where constraint for `T: Foo`. + let constraints = self.resolve_trait_constraints(&trait_impl.where_clause); + for (_, _, method) in trait_impl.methods.functions.iter_mut() { // Attach any trait constraints on the impl to the function method.def.where_clause.append(&mut trait_impl.where_clause.clone()); @@ -1817,17 +1941,20 @@ impl<'context> Elaborator<'context> { let impl_id = self.interner.next_trait_impl_id(); self.current_trait_impl = Some(impl_id); - // Fetch trait constraints here + let path_span = trait_impl.trait_path.span; let (ordered_generics, named_generics) = trait_impl .trait_id .map(|trait_id| { - self.resolve_type_args(trait_generics, trait_id, trait_impl.trait_path.span) + // Check for missing generics & associated types for the trait being implemented + self.resolve_trait_args_from_trait_impl(trait_generics, trait_id, path_span) }) .unwrap_or_default(); trait_impl.resolved_trait_generics = ordered_generics; self.interner.set_associated_types_for_impl(impl_id, named_generics); + self.remove_trait_constraints_from_scope(&constraints); + let self_type = self.resolve_type(unresolved_type); self.self_type = Some(self_type.clone()); trait_impl.methods.self_type = Some(self_type); diff --git a/compiler/noirc_frontend/src/elaborator/path_resolution.rs b/compiler/noirc_frontend/src/elaborator/path_resolution.rs index 010c5305fb3..120ead52883 100644 --- a/compiler/noirc_frontend/src/elaborator/path_resolution.rs +++ b/compiler/noirc_frontend/src/elaborator/path_resolution.rs @@ -79,7 +79,7 @@ pub struct Turbofish { /// Any item that can appear before the last segment in a path. #[derive(Debug)] enum IntermediatePathResolutionItem { - Module(ModuleId), + Module, Struct(StructId, Option), TypeAlias(TypeAliasId, Option), Trait(TraitId, Option), @@ -180,7 +180,7 @@ impl<'context> Elaborator<'context> { let mut current_module_id = starting_module; let mut current_module = self.get_module(starting_module); - let mut intermediate_item = IntermediatePathResolutionItem::Module(current_module_id); + let mut intermediate_item = IntermediatePathResolutionItem::Module; let first_segment = &path.segments.first().expect("ice: could not fetch first segment").ident; @@ -222,18 +222,12 @@ impl<'context> Elaborator<'context> { }); } - (id, false, IntermediatePathResolutionItem::Module(id)) + (id, false, IntermediatePathResolutionItem::Module) } ModuleDefId::TypeId(id) => ( id.module_id(), true, - IntermediatePathResolutionItem::Struct( - id, - last_segment_generics.as_ref().map(|generics| Turbofish { - generics: generics.clone(), - span: last_segment.turbofish_span(), - }), - ), + IntermediatePathResolutionItem::Struct(id, last_segment.turbofish()), ), ModuleDefId::TypeAliasId(id) => { let type_alias = self.interner.get_type_alias(id); @@ -244,25 +238,13 @@ impl<'context> Elaborator<'context> { ( module_id, true, - IntermediatePathResolutionItem::TypeAlias( - id, - last_segment_generics.as_ref().map(|generics| Turbofish { - generics: generics.clone(), - span: last_segment.turbofish_span(), - }), - ), + IntermediatePathResolutionItem::TypeAlias(id, last_segment.turbofish()), ) } ModuleDefId::TraitId(id) => ( id.0, false, - IntermediatePathResolutionItem::Trait( - id, - last_segment_generics.as_ref().map(|generics| Turbofish { - generics: generics.clone(), - span: last_segment.turbofish_span(), - }), - ), + IntermediatePathResolutionItem::Trait(id, last_segment.turbofish()), ), ModuleDefId::FunctionId(_) => panic!("functions cannot be in the type namespace"), ModuleDefId::GlobalId(_) => panic!("globals cannot be in the type namespace"), @@ -460,9 +442,7 @@ fn merge_intermediate_path_resolution_item_with_module_def_id( ModuleDefId::TraitId(trait_id) => PathResolutionItem::Trait(trait_id), ModuleDefId::GlobalId(global_id) => PathResolutionItem::Global(global_id), ModuleDefId::FunctionId(func_id) => match intermediate_item { - IntermediatePathResolutionItem::Module(_) => { - PathResolutionItem::ModuleFunction(func_id) - } + IntermediatePathResolutionItem::Module => PathResolutionItem::ModuleFunction(func_id), IntermediatePathResolutionItem::Struct(struct_id, generics) => { PathResolutionItem::StructFunction(struct_id, generics, func_id) } diff --git a/compiler/noirc_frontend/src/elaborator/patterns.rs b/compiler/noirc_frontend/src/elaborator/patterns.rs index 242f5f0b496..6a672866d7e 100644 --- a/compiler/noirc_frontend/src/elaborator/patterns.rs +++ b/compiler/noirc_frontend/src/elaborator/patterns.rs @@ -751,7 +751,11 @@ impl<'context> Elaborator<'context> { let function = self.interner.function_meta(&function); for mut constraint in function.trait_constraints.clone() { constraint.apply_bindings(&bindings); - self.push_trait_constraint(constraint, expr_id); + + self.push_trait_constraint( + constraint, expr_id, + false, // This constraint shouldn't lead to choosing a trait impl method + ); } } } @@ -767,7 +771,11 @@ impl<'context> Elaborator<'context> { // Currently only one impl can be selected per expr_id, so this // constraint needs to be pushed after any other constraints so // that monomorphization can resolve this trait method to the correct impl. - self.push_trait_constraint(method.constraint, expr_id); + self.push_trait_constraint( + method.constraint, + expr_id, + true, // this constraint should lead to choosing a trait impl method + ); } } diff --git a/compiler/noirc_frontend/src/elaborator/statements.rs b/compiler/noirc_frontend/src/elaborator/statements.rs index 8a46d85d563..a01b24c2f0f 100644 --- a/compiler/noirc_frontend/src/elaborator/statements.rs +++ b/compiler/noirc_frontend/src/elaborator/statements.rs @@ -33,6 +33,7 @@ impl<'context> Elaborator<'context> { StatementKind::Constrain(constrain) => self.elaborate_constrain(constrain), StatementKind::Assign(assign) => self.elaborate_assign(assign), StatementKind::For(for_stmt) => self.elaborate_for(for_stmt), + StatementKind::Loop(block) => self.elaborate_loop(block, statement.span), StatementKind::Break => self.elaborate_jump(true, statement.span), StatementKind::Continue => self.elaborate_jump(false, statement.span), StatementKind::Comptime(statement) => self.elaborate_comptime_statement(*statement), @@ -268,6 +269,29 @@ impl<'context> Elaborator<'context> { (statement, Type::Unit) } + pub(super) fn elaborate_loop( + &mut self, + block: Expression, + span: noirc_errors::Span, + ) -> (HirStatement, Type) { + let in_constrained_function = self.in_constrained_function(); + if in_constrained_function { + self.push_err(ResolverError::LoopInConstrainedFn { span }); + } + + self.nested_loops += 1; + self.push_scope(); + + let (block, _block_type) = self.elaborate_expression(block); + + self.pop_scope(); + self.nested_loops -= 1; + + let statement = HirStatement::Loop(block); + + (statement, Type::Unit) + } + fn elaborate_jump(&mut self, is_break: bool, span: noirc_errors::Span) -> (HirStatement, Type) { let in_constrained_function = self.in_constrained_function(); diff --git a/compiler/noirc_frontend/src/elaborator/traits.rs b/compiler/noirc_frontend/src/elaborator/traits.rs index a10939dde16..bbc1214de9e 100644 --- a/compiler/noirc_frontend/src/elaborator/traits.rs +++ b/compiler/noirc_frontend/src/elaborator/traits.rs @@ -21,7 +21,7 @@ use crate::{ use super::Elaborator; impl<'context> Elaborator<'context> { - pub fn collect_traits(&mut self, traits: &BTreeMap) { + pub fn collect_traits(&mut self, traits: &mut BTreeMap) { for (trait_id, unresolved_trait) in traits { self.local_module = unresolved_trait.module_id; @@ -39,8 +39,13 @@ impl<'context> Elaborator<'context> { &resolved_generics, ); + let new_generics = + this.desugar_trait_constraints(&mut unresolved_trait.trait_def.where_clause); + this.generics.extend(new_generics); + let where_clause = this.resolve_trait_constraints(&unresolved_trait.trait_def.where_clause); + this.remove_trait_constraints_from_scope(&where_clause); // Each associated type in this trait is also an implicit generic for associated_type in &this.interner.get_trait(*trait_id).associated_types { @@ -56,6 +61,7 @@ impl<'context> Elaborator<'context> { this.interner.update_trait(*trait_id, |trait_def| { trait_def.set_trait_bounds(resolved_trait_bounds); trait_def.set_where_clause(where_clause); + trait_def.set_visibility(unresolved_trait.trait_def.visibility); }); let methods = this.resolve_trait_methods(*trait_id, unresolved_trait); @@ -126,7 +132,7 @@ impl<'context> Elaborator<'context> { let func_id = unresolved_trait.method_ids[&name.0.contents]; let mut where_clause = where_clause.to_vec(); - // Attach any trait constraints on the trait to the function + // Attach any trait constraints on the trait to the function, where_clause.extend(unresolved_trait.trait_def.where_clause.clone()); this.resolve_trait_function( diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 72e46d36c2c..36427997843 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -310,11 +310,32 @@ impl<'context> Elaborator<'context> { } } + /// Identical to `resolve_type_args` but does not allow + /// associated types to be elided since trait impls must specify them. + pub(super) fn resolve_trait_args_from_trait_impl( + &mut self, + args: GenericTypeArgs, + item: TraitId, + span: Span, + ) -> (Vec, Vec) { + self.resolve_type_args_inner(args, item, span, false) + } + pub(super) fn resolve_type_args( + &mut self, + args: GenericTypeArgs, + item: impl Generic, + span: Span, + ) -> (Vec, Vec) { + self.resolve_type_args_inner(args, item, span, true) + } + + pub(super) fn resolve_type_args_inner( &mut self, mut args: GenericTypeArgs, item: impl Generic, span: Span, + allow_implicit_named_args: bool, ) -> (Vec, Vec) { let expected_kinds = item.generics(self.interner); @@ -336,7 +357,12 @@ impl<'context> Elaborator<'context> { let mut associated = Vec::new(); if item.accepts_named_type_args() { - associated = self.resolve_associated_type_args(args.named_args, item, span); + associated = self.resolve_associated_type_args( + args.named_args, + item, + span, + allow_implicit_named_args, + ); } else if !args.named_args.is_empty() { let item_kind = item.item_kind(); self.push_err(ResolverError::NamedTypeArgs { span, item_kind }); @@ -350,6 +376,7 @@ impl<'context> Elaborator<'context> { args: Vec<(Ident, UnresolvedType)>, item: impl Generic, span: Span, + allow_implicit_named_args: bool, ) -> Vec { let mut seen_args = HashMap::default(); let mut required_args = item.named_generics(self.interner); @@ -379,11 +406,19 @@ impl<'context> Elaborator<'context> { resolved.push(NamedType { name, typ }); } - // Anything that hasn't been removed yet is missing + // Anything that hasn't been removed yet is missing. + // Fill it in to avoid a panic if we allow named args to be elided, otherwise error. for generic in required_args { - let item = item.item_name(self.interner); let name = generic.name.clone(); - self.push_err(TypeCheckError::MissingNamedTypeArg { item, span, name }); + + if allow_implicit_named_args { + let name = Ident::new(name.as_ref().clone(), span); + let typ = self.interner.next_type_variable(); + resolved.push(NamedType { name, typ }); + } else { + let item = item.item_name(self.interner); + self.push_err(TypeCheckError::MissingNamedTypeArg { item, span, name }); + } } resolved @@ -500,7 +535,7 @@ impl<'context> Elaborator<'context> { } } (lhs, rhs) => { - let infix = Type::InfixExpr(Box::new(lhs), op, Box::new(rhs)); + let infix = Type::infix_expr(Box::new(lhs), op, Box::new(rhs)); Type::CheckedCast { from: Box::new(infix.clone()), to: Box::new(infix) } .canonicalize() } @@ -649,6 +684,60 @@ impl<'context> Elaborator<'context> { None } + /// This resolves a method in the form `Struct::method` where `method` is a trait method + fn resolve_struct_trait_method(&mut self, path: &Path) -> Option { + if path.segments.len() < 2 { + return None; + } + + let mut path = path.clone(); + let span = path.span(); + let last_segment = path.pop(); + let before_last_segment = path.last_segment(); + + let path_resolution = self.resolve_path(path).ok()?; + let PathResolutionItem::Struct(struct_id) = path_resolution.item else { + return None; + }; + + let struct_type = self.get_struct(struct_id); + let generics = struct_type.borrow().instantiate(self.interner); + let typ = Type::Struct(struct_type, generics); + let method_name = &last_segment.ident.0.contents; + + // If we can find a method on the struct, this is definitely not a trait method + if self.interner.lookup_direct_method(&typ, method_name, false).is_some() { + return None; + } + + let trait_methods = self.interner.lookup_trait_methods(&typ, method_name, false); + if trait_methods.is_empty() { + return None; + } + + let (hir_method_reference, error) = + self.get_trait_method_in_scope(&trait_methods, method_name, last_segment.span); + let hir_method_reference = hir_method_reference?; + let func_id = hir_method_reference.func_id(self.interner)?; + let HirMethodReference::TraitMethodId(trait_method_id, _, _) = hir_method_reference else { + return None; + }; + + let trait_id = trait_method_id.trait_id; + let trait_ = self.interner.get_trait(trait_id); + let mut constraint = trait_.as_constraint(span); + constraint.typ = typ; + + let method = TraitMethod { method_id: trait_method_id, constraint, assumed: false }; + let turbofish = before_last_segment.turbofish(); + let item = PathResolutionItem::TraitFunction(trait_id, turbofish, func_id); + let mut errors = path_resolution.errors; + if let Some(error) = error { + errors.push(error); + } + Some(TraitPathResolution { method, item: Some(item), errors }) + } + // Try to resolve the given trait method path. // // Returns the trait method, trait constraint, and whether the impl is assumed to exist by a where clause or not @@ -660,6 +749,7 @@ impl<'context> Elaborator<'context> { self.resolve_trait_static_method_by_self(path) .or_else(|| self.resolve_trait_static_method(path)) .or_else(|| self.resolve_trait_method_by_named_generic(path)) + .or_else(|| self.resolve_struct_trait_method(path)) } pub(super) fn unify( @@ -1421,6 +1511,19 @@ impl<'context> Elaborator<'context> { method_name: &str, span: Span, ) -> Option { + let (method, error) = self.get_trait_method_in_scope(trait_methods, method_name, span); + if let Some(error) = error { + self.push_err(error); + } + method + } + + fn get_trait_method_in_scope( + &mut self, + trait_methods: &[(FuncId, TraitId)], + method_name: &str, + span: Span, + ) -> (Option, Option) { let module_id = self.module_id(); let module_data = self.get_module(module_id); @@ -1434,12 +1537,8 @@ impl<'context> Elaborator<'context> { .filter_map(|trait_id| { let trait_ = self.interner.get_trait(*trait_id); let trait_name = &trait_.name; - let Some(map) = module_data.scope().types().get(trait_name) else { - return None; - }; - let Some(imported_item) = map.get(&None) else { - return None; - }; + let map = module_data.scope().types().get(trait_name)?; + let imported_item = map.get(&None)?; if imported_item.0 == ModuleDefId::TraitId(*trait_id) { Some((*trait_id, trait_name)) } else { @@ -1458,28 +1557,24 @@ impl<'context> Elaborator<'context> { let trait_id = *traits.iter().next().unwrap(); let trait_ = self.interner.get_trait(trait_id); let trait_name = self.fully_qualified_trait_path(trait_); - - self.push_err(PathResolutionError::TraitMethodNotInScope { + let method = + self.trait_hir_method_reference(trait_id, trait_methods, method_name, span); + let error = PathResolutionError::TraitMethodNotInScope { ident: Ident::new(method_name.into(), span), trait_name, - }); - - return Some(self.trait_hir_method_reference( - trait_id, - trait_methods, - method_name, - span, - )); + }; + return (Some(method), Some(error)); } else { let traits = vecmap(traits, |trait_id| { let trait_ = self.interner.get_trait(trait_id); self.fully_qualified_trait_path(trait_) }); - self.push_err(PathResolutionError::UnresolvedWithPossibleTraitsToImport { + let method = None; + let error = PathResolutionError::UnresolvedWithPossibleTraitsToImport { ident: Ident::new(method_name.into(), span), traits, - }); - return None; + }; + return (method, Some(error)); } } @@ -1488,15 +1583,18 @@ impl<'context> Elaborator<'context> { let trait_ = self.interner.get_trait(trait_id); self.fully_qualified_trait_path(trait_) }); - self.push_err(PathResolutionError::MultipleTraitsInScope { + let method = None; + let error = PathResolutionError::MultipleTraitsInScope { ident: Ident::new(method_name.into(), span), traits, - }); - return None; + }; + return (method, Some(error)); } let trait_id = traits_in_scope[0].0; - Some(self.trait_hir_method_reference(trait_id, trait_methods, method_name, span)) + let method = self.trait_hir_method_reference(trait_id, trait_methods, method_name, span); + let error = None; + (Some(method), error) } fn trait_hir_method_reference( @@ -1514,7 +1612,7 @@ impl<'context> Elaborator<'context> { // Return a TraitMethodId with unbound generics. These will later be bound by the type-checker. let trait_ = self.interner.get_trait(trait_id); - let generics = trait_.as_constraint(span).trait_bound.trait_generics; + let generics = trait_.get_trait_generics(span); let trait_method_id = trait_.find_method(method_name).unwrap(); HirMethodReference::TraitMethodId(trait_method_id, generics, false) } @@ -1814,6 +1912,7 @@ impl<'context> Elaborator<'context> { (expr_span, empty_function) } + #[allow(clippy::too_many_arguments)] pub fn verify_trait_constraint( &mut self, object_type: &Type, @@ -1821,6 +1920,7 @@ impl<'context> Elaborator<'context> { trait_generics: &[Type], associated_types: &[NamedType], function_ident_id: ExprId, + select_impl: bool, span: Span, ) { match self.interner.lookup_trait_implementation( @@ -1830,7 +1930,9 @@ impl<'context> Elaborator<'context> { associated_types, ) { Ok(impl_kind) => { - self.interner.select_impl_for_expression(function_ident_id, impl_kind); + if select_impl { + self.interner.select_impl_for_expression(function_ident_id, impl_kind); + } } Err(error) => self.push_trait_constraint_error(object_type, error, span), } @@ -1904,10 +2006,15 @@ impl<'context> Elaborator<'context> { /// Push a trait constraint into the current FunctionContext to be solved if needed /// at the end of the earlier of either the current function or the current comptime scope. - pub fn push_trait_constraint(&mut self, constraint: TraitConstraint, expr_id: ExprId) { + pub fn push_trait_constraint( + &mut self, + constraint: TraitConstraint, + expr_id: ExprId, + select_impl: bool, + ) { let context = self.function_context.last_mut(); let context = context.expect("The function_context stack should always be non-empty"); - context.trait_constraints.push((constraint, expr_id)); + context.trait_constraints.push((constraint, expr_id, select_impl)); } pub fn bind_generics_from_trait_constraint( @@ -1977,7 +2084,7 @@ pub(crate) fn bind_named_generics( args: &[NamedType], bindings: &mut TypeBindings, ) { - assert_eq!(params.len(), args.len()); + assert!(args.len() <= params.len()); for arg in args { let i = params @@ -1988,6 +2095,10 @@ pub(crate) fn bind_named_generics( let param = params.swap_remove(i); bind_generic(¶m, &arg.typ, bindings); } + + for unbound_param in params { + bind_generic(&unbound_param, &Type::Error, bindings); + } } fn bind_generic(param: &ResolvedGeneric, arg: &Type, bindings: &mut TypeBindings) { diff --git a/compiler/noirc_frontend/src/hir/comptime/display.rs b/compiler/noirc_frontend/src/hir/comptime/display.rs index 29d1448f07e..ccdfdf00e72 100644 --- a/compiler/noirc_frontend/src/hir/comptime/display.rs +++ b/compiler/noirc_frontend/src/hir/comptime/display.rs @@ -732,6 +732,9 @@ fn remove_interned_in_statement_kind( block: remove_interned_in_expression(interner, for_loop.block), ..for_loop }), + StatementKind::Loop(block) => { + StatementKind::Loop(remove_interned_in_expression(interner, block)) + } StatementKind::Comptime(statement) => { StatementKind::Comptime(Box::new(remove_interned_in_statement(interner, *statement))) } diff --git a/compiler/noirc_frontend/src/hir/comptime/errors.rs b/compiler/noirc_frontend/src/hir/comptime/errors.rs index 6ff918328a1..e9a615f2c59 100644 --- a/compiler/noirc_frontend/src/hir/comptime/errors.rs +++ b/compiler/noirc_frontend/src/hir/comptime/errors.rs @@ -246,6 +246,9 @@ pub enum InterpreterError { GlobalsDependencyCycle { location: Location, }, + LoopHaltedForUiResponsiveness { + location: Location, + }, // These cases are not errors, they are just used to prevent us from running more code // until the loop can be resumed properly. These cases will never be displayed to users. @@ -323,7 +326,8 @@ impl InterpreterError { | InterpreterError::CannotSetFunctionBody { location, .. } | InterpreterError::UnknownArrayLength { location, .. } | InterpreterError::CannotInterpretFormatStringWithErrors { location } - | InterpreterError::GlobalsDependencyCycle { location } => *location, + | InterpreterError::GlobalsDependencyCycle { location } + | InterpreterError::LoopHaltedForUiResponsiveness { location } => *location, InterpreterError::FailedToParseMacro { error, file, .. } => { Location::new(error.span(), *file) @@ -683,6 +687,13 @@ impl<'a> From<&'a InterpreterError> for CustomDiagnostic { let secondary = String::new(); CustomDiagnostic::simple_error(msg, secondary, location.span) } + InterpreterError::LoopHaltedForUiResponsiveness { location } => { + let msg = "This loop took too much time to execute so it was halted for UI responsiveness" + .to_string(); + let secondary = + "This error doesn't happen in normal executions of `nargo`".to_string(); + CustomDiagnostic::simple_warning(msg, secondary, location.span) + } } } } diff --git a/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs b/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs index 9338c0fc37f..71a462c9066 100644 --- a/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs +++ b/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs @@ -59,6 +59,7 @@ impl HirStatement { block: for_stmt.block.to_display_ast(interner), span, }), + HirStatement::Loop(block) => StatementKind::Loop(block.to_display_ast(interner)), HirStatement::Break => StatementKind::Break, HirStatement::Continue => StatementKind::Continue, HirStatement::Expression(expr) => { @@ -359,7 +360,7 @@ impl Type { Type::Constant(..) => panic!("Type::Constant where a type was expected: {self:?}"), Type::Quoted(quoted_type) => UnresolvedTypeData::Quoted(*quoted_type), Type::Error => UnresolvedTypeData::Error, - Type::InfixExpr(lhs, op, rhs) => { + Type::InfixExpr(lhs, op, rhs, _) => { let lhs = Box::new(lhs.to_type_expression()); let rhs = Box::new(rhs.to_type_expression()); let span = Span::default(); diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index 13a9400bd2e..7eafdf1e71f 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -1424,6 +1424,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { let (mut lhs, lhs_is_negative) = match evaluated_lhs { Value::Field(value) => (value, false), + Value::U1(value) => ((value as u128).into(), false), Value::U8(value) => ((value as u128).into(), false), Value::U16(value) => ((value as u128).into(), false), Value::U32(value) => ((value as u128).into(), false), @@ -1549,6 +1550,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { HirStatement::Constrain(constrain) => self.evaluate_constrain(constrain), HirStatement::Assign(assign) => self.evaluate_assign(assign), HirStatement::For(for_) => self.evaluate_for(for_), + HirStatement::Loop(expression) => self.evaluate_loop(expression), HirStatement::Break => self.evaluate_break(statement), HirStatement::Continue => self.evaluate_continue(statement), HirStatement::Expression(expression) => self.evaluate(expression), @@ -1722,22 +1724,68 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { let (end, _) = get_index(self, for_.end_range)?; let was_in_loop = std::mem::replace(&mut self.in_loop, true); + let mut result = Ok(Value::Unit); + for i in start..end { self.push_scope(); self.current_scope_mut().insert(for_.identifier.id, make_value(i)); - match self.evaluate(for_.block) { - Ok(_) => (), - Err(InterpreterError::Break) => break, - Err(InterpreterError::Continue) => continue, - Err(other) => return Err(other), + let must_break = match self.evaluate(for_.block) { + Ok(_) => false, + Err(InterpreterError::Break) => true, + Err(InterpreterError::Continue) => false, + Err(error) => { + result = Err(error); + true + } + }; + + self.pop_scope(); + + if must_break { + break; } + } + + self.in_loop = was_in_loop; + result + } + + fn evaluate_loop(&mut self, expr: ExprId) -> IResult { + let was_in_loop = std::mem::replace(&mut self.in_loop, true); + let in_lsp = self.elaborator.interner.is_in_lsp_mode(); + let mut counter = 0; + let mut result = Ok(Value::Unit); + + loop { + self.push_scope(); + + let must_break = match self.evaluate(expr) { + Ok(_) => false, + Err(InterpreterError::Break) => true, + Err(InterpreterError::Continue) => false, + Err(error) => { + result = Err(error); + true + } + }; self.pop_scope(); + + if must_break { + break; + } + + counter += 1; + if in_lsp && counter == 10_000 { + let location = self.elaborator.interner.expr_location(&expr); + result = Err(InterpreterError::LoopHaltedForUiResponsiveness { location }); + break; + } } self.in_loop = was_in_loop; - Ok(Value::Unit) + result } fn evaluate_break(&mut self, id: StmtId) -> IResult { diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 730bcd1be6c..3506b63919c 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -181,8 +181,13 @@ impl<'local, 'context> Interpreter<'local, 'context> { "struct_def_add_generic" => struct_def_add_generic(interner, arguments, location), "struct_def_as_type" => struct_def_as_type(interner, arguments, location), "struct_def_eq" => struct_def_eq(arguments, location), - "struct_def_fields" => struct_def_fields(interner, arguments, location), - "struct_def_generics" => struct_def_generics(interner, arguments, location), + "struct_def_fields" => struct_def_fields(interner, arguments, location, call_stack), + "struct_def_fields_as_written" => { + struct_def_fields_as_written(interner, arguments, location) + } + "struct_def_generics" => { + struct_def_generics(interner, arguments, return_type, location) + } "struct_def_has_named_attribute" => { struct_def_has_named_attribute(interner, arguments, location) } @@ -442,10 +447,11 @@ fn struct_def_as_type( Ok(Value::Type(Type::Struct(struct_def_rc, generics))) } -/// fn generics(self) -> [Type] +/// fn generics(self) -> [(Type, Option)] fn struct_def_generics( interner: &NodeInterner, arguments: Vec<(Value, Location)>, + return_type: Type, location: Location, ) -> IResult { let argument = check_one_argument(arguments, location)?; @@ -453,11 +459,38 @@ fn struct_def_generics( let struct_def = interner.get_struct(struct_id); let struct_def = struct_def.borrow(); - let generics = - struct_def.generics.iter().map(|generic| Value::Type(generic.clone().as_named_generic())); + let expected = Type::Slice(Box::new(Type::Tuple(vec![ + Type::Quoted(QuotedType::Type), + interner.next_type_variable(), // Option + ]))); + + let actual = return_type.clone(); - let typ = Type::Slice(Box::new(Type::Quoted(QuotedType::Type))); - Ok(Value::Slice(generics.collect(), typ)) + let slice_item_type = match return_type { + Type::Slice(item_type) => *item_type, + _ => return Err(InterpreterError::TypeMismatch { expected, actual, location }), + }; + + let option_typ = match &slice_item_type { + Type::Tuple(types) if types.len() == 2 => types[1].clone(), + _ => return Err(InterpreterError::TypeMismatch { expected, actual, location }), + }; + + let generics: IResult<_> = struct_def + .generics + .iter() + .map(|generic| -> IResult { + let generic_as_named = generic.clone().as_named_generic(); + let numeric_type = match generic_as_named.kind() { + Kind::Numeric(numeric_type) => Some(Value::Type(*numeric_type)), + _ => None, + }; + let numeric_type = option(option_typ.clone(), numeric_type, location.span)?; + Ok(Value::Tuple(vec![Value::Type(generic_as_named), numeric_type])) + }) + .collect(); + + Ok(Value::Slice(generics?, slice_item_type)) } fn struct_def_hash(arguments: Vec<(Value, Location)>, location: Location) -> IResult { @@ -482,12 +515,57 @@ fn struct_def_has_named_attribute( Ok(Value::Bool(has_named_attribute(&name, interner.struct_attributes(&struct_id)))) } -/// fn fields(self) -> [(Quoted, Type)] -/// Returns (name, type) pairs of each field of this StructDefinition +/// fn fields(self, generic_args: [Type]) -> [(Quoted, Type)] +/// Returns (name, type) pairs of each field of this StructDefinition. +/// Applies the given generic arguments to each field. fn struct_def_fields( interner: &mut NodeInterner, arguments: Vec<(Value, Location)>, location: Location, + call_stack: &im::Vector, +) -> IResult { + let (typ, generic_args) = check_two_arguments(arguments, location)?; + let struct_id = get_struct(typ)?; + let struct_def = interner.get_struct(struct_id); + let struct_def = struct_def.borrow(); + + let args_location = generic_args.1; + let generic_args = get_slice(interner, generic_args)?.0; + let generic_args = try_vecmap(generic_args, |arg| get_type((arg, args_location)))?; + + let actual = generic_args.len(); + let expected = struct_def.generics.len(); + if actual != expected { + let s = if expected == 1 { "" } else { "s" }; + let was_were = if actual == 1 { "was" } else { "were" }; + let message = Some(format!("`StructDefinition::fields` expected {expected} generic{s} for `{}` but {actual} {was_were} given", struct_def.name)); + let location = args_location; + let call_stack = call_stack.clone(); + return Err(InterpreterError::FailingConstraint { message, location, call_stack }); + } + + let mut fields = im::Vector::new(); + + for (field_name, field_type) in struct_def.get_fields(&generic_args) { + let name = Value::Quoted(Rc::new(vec![Token::Ident(field_name)])); + fields.push_back(Value::Tuple(vec![name, Value::Type(field_type)])); + } + + let typ = Type::Slice(Box::new(Type::Tuple(vec![ + Type::Quoted(QuotedType::Quoted), + Type::Quoted(QuotedType::Type), + ]))); + Ok(Value::Slice(fields, typ)) +} + +/// fn fields_as_written(self) -> [(Quoted, Type)] +/// Returns (name, type) pairs of each field of this StructDefinition. +/// +/// Note that any generic arguments won't be applied: if you need them to be, use `fields`. +fn struct_def_fields_as_written( + interner: &mut NodeInterner, + arguments: Vec<(Value, Location)>, + location: Location, ) -> IResult { let argument = check_one_argument(arguments, location)?; let struct_id = get_struct(argument)?; @@ -822,10 +900,10 @@ fn to_le_radix( let value = get_field(value)?; let radix = get_u32(radix)?; - let limb_count = if let Type::Array(length, _) = return_type { + let (limb_count, element_type) = if let Type::Array(length, element_type) = return_type { if let Type::Constant(limb_count, kind) = *length { if kind.unifies(&Kind::u32()) { - limb_count + (limb_count, element_type) } else { return Err(InterpreterError::TypeAnnotationsNeededForMethodCall { location }); } @@ -836,14 +914,29 @@ fn to_le_radix( return Err(InterpreterError::TypeAnnotationsNeededForMethodCall { location }); }; + let return_type_is_bits = + *element_type == Type::Integer(Signedness::Unsigned, IntegerBitSize::One); + // Decompose the integer into its radix digits in little endian form. let decomposed_integer = compute_to_radix_le(value, radix); - let decomposed_integer = - vecmap(0..limb_count.to_u128() as usize, |i| match decomposed_integer.get(i) { - Some(digit) => Value::U8(*digit), - None => Value::U8(0), - }); - let result_type = byte_array_type(decomposed_integer.len()); + let decomposed_integer = vecmap(0..limb_count.to_u128() as usize, |i| { + let digit = match decomposed_integer.get(i) { + Some(digit) => *digit, + None => 0, + }; + // The only built-ins that use these either return `[u1; N]` or `[u8; N]` + if return_type_is_bits { + Value::U1(digit != 0) + } else { + Value::U8(digit) + } + }); + + let result_type = Type::Array( + Box::new(Type::Constant(decomposed_integer.len().into(), Kind::u32())), + element_type, + ); + Ok(Value::Array(decomposed_integer.into(), result_type)) } diff --git a/compiler/noirc_frontend/src/hir/comptime/value.rs b/compiler/noirc_frontend/src/hir/comptime/value.rs index b5496507e80..77933ba9361 100644 --- a/compiler/noirc_frontend/src/hir/comptime/value.rs +++ b/compiler/noirc_frontend/src/hir/comptime/value.rs @@ -521,6 +521,10 @@ impl Value { ) } + pub(crate) fn is_closure(&self) -> bool { + matches!(self, Value::Closure(..)) + } + /// Converts any non-negative `Value` into a `FieldElement`. /// Returns `None` for negative integers and non-integral `Value`s. pub(crate) fn to_field_element(&self) -> Option { 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 9081cb1cccd..10866f4b309 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs @@ -3,7 +3,7 @@ use super::errors::{DefCollectorErrorKind, DuplicateType}; use crate::elaborator::Elaborator; use crate::graph::CrateId; use crate::hir::comptime::InterpreterError; -use crate::hir::def_map::{CrateDefMap, LocalModuleId, ModuleId}; +use crate::hir::def_map::{CrateDefMap, LocalModuleId, ModuleDefId, ModuleId}; use crate::hir::resolution::errors::ResolverError; use crate::hir::type_check::TypeCheckError; use crate::locations::ReferencesTracker; @@ -14,7 +14,7 @@ use crate::{Generics, Type}; use crate::hir::resolution::import::{resolve_import, ImportDirective}; use crate::hir::Context; -use crate::ast::Expression; +use crate::ast::{Expression, NoirEnumeration}; use crate::node_interner::{ FuncId, GlobalId, ModuleAttributes, NodeInterner, ReferenceId, StructId, TraitId, TraitImplId, TypeAliasId, @@ -64,6 +64,12 @@ pub struct UnresolvedStruct { pub struct_def: NoirStruct, } +pub struct UnresolvedEnum { + pub file_id: FileId, + pub module_id: LocalModuleId, + pub enum_def: NoirEnumeration, +} + #[derive(Clone)] pub struct UnresolvedTrait { pub file_id: FileId, @@ -141,7 +147,8 @@ pub struct DefCollector { #[derive(Default)] pub struct CollectedItems { pub functions: Vec, - pub(crate) types: BTreeMap, + pub(crate) structs: BTreeMap, + pub(crate) enums: BTreeMap, pub(crate) type_aliases: BTreeMap, pub(crate) traits: BTreeMap, pub globals: Vec, @@ -153,7 +160,7 @@ pub struct CollectedItems { impl CollectedItems { pub fn is_empty(&self) -> bool { self.functions.is_empty() - && self.types.is_empty() + && self.structs.is_empty() && self.type_aliases.is_empty() && self.traits.is_empty() && self.globals.is_empty() @@ -254,7 +261,8 @@ impl DefCollector { imports: vec![], items: CollectedItems { functions: vec![], - types: BTreeMap::new(), + structs: BTreeMap::new(), + enums: BTreeMap::new(), type_aliases: BTreeMap::new(), traits: BTreeMap::new(), impls: HashMap::default(), @@ -412,13 +420,24 @@ impl DefCollector { visibility, ); - if visibility != ItemVisibility::Private { + if context.def_interner.is_in_lsp_mode() + && visibility != ItemVisibility::Private + { context.def_interner.register_name_for_auto_import( name.to_string(), module_def_id, visibility, Some(defining_module), ); + + if let ModuleDefId::TraitId(trait_id) = module_def_id { + context.def_interner.add_trait_reexport( + trait_id, + defining_module, + name.clone(), + visibility, + ); + } } } 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 ec13cb2db15..41234980942 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs @@ -12,8 +12,9 @@ use rustc_hash::FxHashMap as HashMap; use crate::ast::{ Documented, Expression, FunctionDefinition, Ident, ItemVisibility, LetStatement, - ModuleDeclaration, NoirFunction, NoirStruct, NoirTrait, NoirTraitImpl, NoirTypeAlias, Pattern, - TraitImplItemKind, TraitItem, TypeImpl, UnresolvedType, UnresolvedTypeData, + ModuleDeclaration, NoirEnumeration, NoirFunction, NoirStruct, NoirTrait, NoirTraitImpl, + NoirTypeAlias, Pattern, TraitImplItemKind, TraitItem, TypeImpl, UnresolvedType, + UnresolvedTypeData, }; use crate::hir::resolution::errors::ResolverError; use crate::node_interner::{ModuleAttributes, NodeInterner, ReferenceId, StructId}; @@ -27,8 +28,8 @@ use crate::{ }; use crate::{Generics, Kind, ResolvedGeneric, Type, TypeVariable}; -use super::dc_crate::CollectedItems; use super::dc_crate::ModuleAttribute; +use super::dc_crate::{CollectedItems, UnresolvedEnum}; use super::{ dc_crate::{ CompilationError, DefCollector, UnresolvedFunctions, UnresolvedGlobal, UnresolvedTraitImpl, @@ -91,7 +92,7 @@ pub fn collect_defs( errors.extend(collector.collect_traits(context, ast.traits, crate_id)); - errors.extend(collector.collect_structs(context, ast.types, crate_id)); + errors.extend(collector.collect_structs(context, ast.structs, crate_id)); errors.extend(collector.collect_type_aliases(context, ast.type_aliases, crate_id)); @@ -317,7 +318,7 @@ impl<'a> ModCollector<'a> { krate, &mut definition_errors, ) { - self.def_collector.items.types.insert(id, the_struct); + self.def_collector.items.structs.insert(id, the_struct); } } definition_errors @@ -1078,6 +1079,20 @@ pub fn collect_struct( Some((id, unresolved)) } +#[allow(clippy::too_many_arguments)] +pub fn collect_enum( + _interner: &mut NodeInterner, + _def_map: &mut CrateDefMap, + _usage_tracker: &mut UsageTracker, + _enum_definition: Documented, + _file_id: FileId, + _module_id: LocalModuleId, + _krate: CrateId, + _definition_errors: &mut [(CompilationError, FileId)], +) -> Option<(StructId, UnresolvedEnum)> { + todo!("Implement collect_enum") +} + pub fn collect_impl( interner: &mut NodeInterner, items: &mut CollectedItems, @@ -1228,9 +1243,10 @@ pub(crate) fn collect_trait_impl_items( for item in std::mem::take(&mut trait_impl.items) { match item.item.kind { TraitImplItemKind::Function(mut impl_method) => { - // Regardless of what visibility was on the source code, treat it as public - // (a warning is produced during parsing for this) - impl_method.def.visibility = ItemVisibility::Public; + // Set the impl method visibility as temporarily private. + // Eventually when we find out what trait is this impl for we'll set it + // to the trait's visibility. + impl_method.def.visibility = ItemVisibility::Private; let func_id = interner.push_empty_fn(); let location = Location::new(impl_method.span(), file_id); diff --git a/compiler/noirc_frontend/src/hir/resolution/errors.rs b/compiler/noirc_frontend/src/hir/resolution/errors.rs index a4b3c1b9c07..77ba76a0595 100644 --- a/compiler/noirc_frontend/src/hir/resolution/errors.rs +++ b/compiler/noirc_frontend/src/hir/resolution/errors.rs @@ -98,6 +98,8 @@ pub enum ResolverError { DependencyCycle { span: Span, item: String, cycle: String }, #[error("break/continue are only allowed in unconstrained functions")] JumpInConstrainedFn { is_break: bool, span: Span }, + #[error("loop is only allowed in unconstrained functions")] + LoopInConstrainedFn { span: Span }, #[error("break/continue are only allowed within loops")] JumpOutsideLoop { is_break: bool, span: Span }, #[error("Only `comptime` globals can be mutable")] @@ -170,6 +172,8 @@ pub enum ResolverError { span: Span, missing_trait_location: Location, }, + #[error("`loop` statements are not yet implemented")] + LoopNotYetSupported { span: Span }, } impl ResolverError { @@ -432,6 +436,13 @@ impl<'a> From<&'a ResolverError> for Diagnostic { *span, ) }, + ResolverError::LoopInConstrainedFn { span } => { + Diagnostic::simple_error( + "loop is only allowed in unconstrained functions".into(), + "Constrained code must always have a known number of loop iterations".into(), + *span, + ) + }, ResolverError::JumpOutsideLoop { is_break, span } => { let item = if *is_break { "break" } else { "continue" }; Diagnostic::simple_error( @@ -642,6 +653,13 @@ impl<'a> From<&'a ResolverError> for Diagnostic { diagnostic.add_secondary_with_file(format!("required by this bound in `{impl_trait}"), missing_trait_location.span, missing_trait_location.file); diagnostic }, + ResolverError::LoopNotYetSupported { span } => { + Diagnostic::simple_error( + "`loop` statements are not yet implemented".to_string(), + String::new(), + *span) + + } } } } diff --git a/compiler/noirc_frontend/src/hir/resolution/visibility.rs b/compiler/noirc_frontend/src/hir/resolution/visibility.rs index 5e6dffa56e4..557f799df89 100644 --- a/compiler/noirc_frontend/src/hir/resolution/visibility.rs +++ b/compiler/noirc_frontend/src/hir/resolution/visibility.rs @@ -138,18 +138,29 @@ pub fn method_call_is_visible( ItemVisibility::Public => true, ItemVisibility::PublicCrate | ItemVisibility::Private => { let func_meta = interner.function_meta(&func_id); - if let Some(struct_id) = func_meta.struct_id { - return struct_member_is_visible( - struct_id, + + if let Some(trait_id) = func_meta.trait_id { + return trait_member_is_visible( + trait_id, modifiers.visibility, current_module, def_maps, ); } - if let Some(trait_id) = func_meta.trait_id { + if let Some(trait_impl_id) = func_meta.trait_impl { + let trait_impl = interner.get_trait_implementation(trait_impl_id); return trait_member_is_visible( - trait_id, + trait_impl.borrow().trait_id, + modifiers.visibility, + current_module, + def_maps, + ); + } + + if let Some(struct_id) = func_meta.struct_id { + return struct_member_is_visible( + struct_id, modifiers.visibility, current_module, def_maps, diff --git a/compiler/noirc_frontend/src/hir/type_check/errors.rs b/compiler/noirc_frontend/src/hir/type_check/errors.rs index 16422e0ef8b..d29e1aa4339 100644 --- a/compiler/noirc_frontend/src/hir/type_check/errors.rs +++ b/compiler/noirc_frontend/src/hir/type_check/errors.rs @@ -496,10 +496,10 @@ impl<'a> From<&'a TypeCheckError> for Diagnostic { Diagnostic::simple_error(msg.to_string(), "".to_string(), *span) }, TypeCheckError::Unsafe { span } => { - Diagnostic::simple_warning(error.to_string(), String::new(), *span) + Diagnostic::simple_error(error.to_string(), String::new(), *span) } TypeCheckError::UnsafeFn { span } => { - Diagnostic::simple_warning(error.to_string(), String::new(), *span) + Diagnostic::simple_error(error.to_string(), String::new(), *span) } TypeCheckError::UnspecifiedType { span } => { Diagnostic::simple_error(error.to_string(), String::new(), *span) diff --git a/compiler/noirc_frontend/src/hir_def/expr.rs b/compiler/noirc_frontend/src/hir_def/expr.rs index 9b3bf4962bb..5ac228a56d6 100644 --- a/compiler/noirc_frontend/src/hir_def/expr.rs +++ b/compiler/noirc_frontend/src/hir_def/expr.rs @@ -225,24 +225,15 @@ impl HirMethodReference { } } } -} -impl HirMethodCallExpression { - /// Converts a method call into a function call - /// - /// Returns ((func_var_id, func_var), call_expr) - pub fn into_function_call( - mut self, - method: HirMethodReference, + pub fn into_function_id_and_name( + self, object_type: Type, - is_macro_call: bool, + generics: Option>, location: Location, interner: &mut NodeInterner, - ) -> ((ExprId, HirIdent), HirCallExpression) { - let mut arguments = vec![self.object]; - arguments.append(&mut self.arguments); - - let (id, impl_kind) = match method { + ) -> (ExprId, HirIdent) { + let (id, impl_kind) = match self { HirMethodReference::FuncId(func_id) => { (interner.function_definition_id(func_id), ImplKind::NotATraitMethod) } @@ -261,10 +252,22 @@ impl HirMethodCallExpression { } }; let func_var = HirIdent { location, id, impl_kind }; - let func = interner.push_expr(HirExpression::Ident(func_var.clone(), self.generics)); + let func = interner.push_expr(HirExpression::Ident(func_var.clone(), generics)); interner.push_expr_location(func, location.span, location.file); - let expr = HirCallExpression { func, arguments, location, is_macro_call }; - ((func, func_var), expr) + (func, func_var) + } +} + +impl HirMethodCallExpression { + pub fn into_function_call( + mut self, + func: ExprId, + is_macro_call: bool, + location: Location, + ) -> HirCallExpression { + let mut arguments = vec![self.object]; + arguments.append(&mut self.arguments); + HirCallExpression { func, arguments, location, is_macro_call } } } diff --git a/compiler/noirc_frontend/src/hir_def/stmt.rs b/compiler/noirc_frontend/src/hir_def/stmt.rs index c42b8230290..8a580e735b1 100644 --- a/compiler/noirc_frontend/src/hir_def/stmt.rs +++ b/compiler/noirc_frontend/src/hir_def/stmt.rs @@ -16,6 +16,7 @@ pub enum HirStatement { Constrain(HirConstrainStatement), Assign(HirAssignStatement), For(HirForStatement), + Loop(ExprId), Break, Continue, Expression(ExprId), diff --git a/compiler/noirc_frontend/src/hir_def/traits.rs b/compiler/noirc_frontend/src/hir_def/traits.rs index 6fd3c4f7a24..a80c25492a3 100644 --- a/compiler/noirc_frontend/src/hir_def/traits.rs +++ b/compiler/noirc_frontend/src/hir_def/traits.rs @@ -1,7 +1,7 @@ use iter_extended::vecmap; use rustc_hash::FxHashMap as HashMap; -use crate::ast::{Ident, NoirFunction}; +use crate::ast::{Ident, ItemVisibility, NoirFunction}; use crate::hir::type_check::generics::TraitGenerics; use crate::ResolvedGeneric; use crate::{ @@ -66,6 +66,7 @@ pub struct Trait { pub name: Ident, pub generics: Generics, pub location: Location, + pub visibility: ItemVisibility, /// When resolving the types of Trait elements, all references to `Self` resolve /// to this TypeVariable. Then when we check if the types of trait impl elements @@ -160,6 +161,10 @@ impl Trait { self.where_clause = where_clause; } + pub fn set_visibility(&mut self, visibility: ItemVisibility) { + self.visibility = visibility; + } + pub fn find_method(&self, name: &str) -> Option { for (idx, method) in self.methods.iter().enumerate() { if &method.name == name { @@ -181,22 +186,22 @@ impl Trait { (ordered, named) } - /// Returns a TraitConstraint for this trait using Self as the object - /// type and the uninstantiated generics for any trait generics. - pub fn as_constraint(&self, span: Span) -> TraitConstraint { + pub fn get_trait_generics(&self, span: Span) -> TraitGenerics { let ordered = vecmap(&self.generics, |generic| generic.clone().as_named_generic()); let named = vecmap(&self.associated_types, |generic| { let name = Ident::new(generic.name.to_string(), span); NamedType { name, typ: generic.clone().as_named_generic() } }); + TraitGenerics { ordered, named } + } + /// Returns a TraitConstraint for this trait using Self as the object + /// type and the uninstantiated generics for any trait generics. + pub fn as_constraint(&self, span: Span) -> TraitConstraint { + let trait_generics = self.get_trait_generics(span); TraitConstraint { typ: Type::TypeVariable(self.self_type_typevar.clone()), - trait_bound: ResolvedTraitBound { - trait_generics: TraitGenerics { ordered, named }, - trait_id: self.id, - span, - }, + trait_bound: ResolvedTraitBound { trait_generics, trait_id: self.id, span }, } } } diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index c0dbf6f9500..4eeec314917 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -97,10 +97,7 @@ pub enum Type { /// A cast (to, from) that's checked at monomorphization. /// /// Simplifications on arithmetic generics are only allowed on the LHS. - CheckedCast { - from: Box, - to: Box, - }, + CheckedCast { from: Box, to: Box }, /// A functions with arguments, a return type and environment. /// the environment should be `Unit` by default, @@ -132,7 +129,13 @@ pub enum Type { /// The type of quoted code in macros. This is always a comptime-only type Quoted(QuotedType), - InfixExpr(Box, BinaryTypeOperator, Box), + /// An infix expression in the form `lhs * rhs`. + /// + /// The `inversion` bool keeps track of whether this expression came from + /// an expression like `4 = a / b` which was transformed to `a = 4 / b` + /// so that if at some point a infix expression `b * (4 / b)` is created, + /// it could be simplified back to `4`. + InfixExpr(Box, BinaryTypeOperator, Box, bool /* inversion */), /// The result of some type error. Remembering type errors as their own type variant lets /// us avoid issuing repeat type errors for the same item. For example, a lambda with @@ -312,6 +315,7 @@ pub enum QuotedType { Type, TypedExpr, StructDefinition, + EnumDefinition, TraitConstraint, TraitDefinition, TraitImpl, @@ -905,7 +909,7 @@ impl std::fmt::Display for Type { write!(f, "&mut {element}") } Type::Quoted(quoted) => write!(f, "{}", quoted), - Type::InfixExpr(lhs, op, rhs) => { + Type::InfixExpr(lhs, op, rhs, _) => { let this = self.canonicalize_checked(); // Prevent infinite recursion @@ -955,6 +959,7 @@ impl std::fmt::Display for QuotedType { QuotedType::Type => write!(f, "Type"), QuotedType::TypedExpr => write!(f, "TypedExpr"), QuotedType::StructDefinition => write!(f, "StructDefinition"), + QuotedType::EnumDefinition => write!(f, "EnumDefinition"), QuotedType::TraitDefinition => write!(f, "TraitDefinition"), QuotedType::TraitConstraint => write!(f, "TraitConstraint"), QuotedType::TraitImpl => write!(f, "TraitImpl"), @@ -1146,7 +1151,7 @@ impl Type { .into_iter() .all(|(_, field)| field.is_valid_for_program_input()), - Type::InfixExpr(lhs, _, rhs) => { + Type::InfixExpr(lhs, _, rhs, _) => { lhs.is_valid_for_program_input() && rhs.is_valid_for_program_input() } } @@ -1307,7 +1312,7 @@ impl Type { TypeBinding::Bound(ref typ) => typ.kind(), TypeBinding::Unbound(_, ref type_var_kind) => type_var_kind.clone(), }, - Type::InfixExpr(lhs, _op, rhs) => lhs.infix_kind(rhs), + Type::InfixExpr(lhs, _op, rhs, _) => lhs.infix_kind(rhs), Type::Alias(def, generics) => def.borrow().get_type(generics).kind(), // This is a concrete FieldElement, not an IntegerOrField Type::FieldElement @@ -1340,6 +1345,48 @@ impl Type { } } + /// Creates an `InfixExpr`. + pub fn infix_expr(lhs: Box, op: BinaryTypeOperator, rhs: Box) -> Type { + Self::new_infix_expr(lhs, op, rhs, false) + } + + /// Creates an `InfixExpr` that results from the compiler trying to unify something like + /// `4 = a * b` into `a = 4 / b` (where `4 / b` is the "inverted" expression). + pub fn inverted_infix_expr(lhs: Box, op: BinaryTypeOperator, rhs: Box) -> Type { + Self::new_infix_expr(lhs, op, rhs, true) + } + + pub fn new_infix_expr( + lhs: Box, + op: BinaryTypeOperator, + rhs: Box, + inversion: bool, + ) -> Type { + // If an InfixExpr like this is tried to be created: + // + // a * (b / a) + // + // where `b / a` resulted from the compiler creating an inverted InfixExpr from a previous + // unification (that is, the compiler had `b = a / y` and ended up doing `y = b / a` where + // `y` is `rhs` here) then we can simplify this to just `b` because there wasn't an actual + // division in the original expression, so multiplying it back is just going back to the + // original `y` + if let Type::InfixExpr(rhs_lhs, rhs_op, rhs_rhs, true) = &*rhs { + if op.approx_inverse() == Some(*rhs_op) && lhs == *rhs_rhs { + return *rhs_lhs.clone(); + } + } + + // Same thing but on the other side. + if let Type::InfixExpr(lhs_lhs, lhs_op, lhs_rhs, true) = &*lhs { + if op.approx_inverse() == Some(*lhs_op) && rhs == *lhs_rhs { + return *lhs_lhs.clone(); + } + } + + Self::InfixExpr(lhs, op, rhs, inversion) + } + /// Returns the number of field elements required to represent the type once encoded. pub fn field_count(&self, location: &Location) -> u32 { match self { @@ -1697,7 +1744,7 @@ impl Type { elem_a.try_unify(elem_b, bindings) } - (InfixExpr(lhs_a, op_a, rhs_a), InfixExpr(lhs_b, op_b, rhs_b)) => { + (InfixExpr(lhs_a, op_a, rhs_a, _), InfixExpr(lhs_b, op_b, rhs_b, _)) => { if op_a == op_b { // We need to preserve the original bindings since if syntactic equality // fails we fall back to other equality strategies. @@ -1724,14 +1771,15 @@ impl Type { } else { Err(UnificationError) } - } else if let InfixExpr(lhs, op, rhs) = other { + } else if let InfixExpr(lhs, op, rhs, _) = other { if let Some(inverse) = op.approx_inverse() { // Handle cases like `4 = a + b` by trying to solve to `a = 4 - b` - let new_type = InfixExpr( + let new_type = Type::inverted_infix_expr( Box::new(Constant(*value, kind.clone())), inverse, rhs.clone(), ); + new_type.try_unify(lhs, bindings)?; Ok(()) } else { @@ -1937,7 +1985,7 @@ impl Type { }) } } - Type::InfixExpr(lhs, op, rhs) => { + Type::InfixExpr(lhs, op, rhs, _) => { let infix_kind = lhs.infix_kind(&rhs); if kind.unifies(&infix_kind) { let lhs_value = lhs.evaluate_to_field_element_helper( @@ -2267,10 +2315,10 @@ impl Type { }); Type::TraitAsType(*s, name.clone(), TraitGenerics { ordered, named }) } - Type::InfixExpr(lhs, op, rhs) => { + Type::InfixExpr(lhs, op, rhs, inversion) => { let lhs = lhs.substitute_helper(type_bindings, substitute_bound_typevars); let rhs = rhs.substitute_helper(type_bindings, substitute_bound_typevars); - Type::InfixExpr(Box::new(lhs), *op, Box::new(rhs)) + Type::InfixExpr(Box::new(lhs), *op, Box::new(rhs), *inversion) } Type::FieldElement @@ -2320,7 +2368,7 @@ impl Type { || env.occurs(target_id) } Type::MutableReference(element) => element.occurs(target_id), - Type::InfixExpr(lhs, _op, rhs) => lhs.occurs(target_id) || rhs.occurs(target_id), + Type::InfixExpr(lhs, _op, rhs, _) => lhs.occurs(target_id) || rhs.occurs(target_id), Type::FieldElement | Type::Integer(_, _) @@ -2389,10 +2437,10 @@ impl Type { }); TraitAsType(*s, name.clone(), TraitGenerics { ordered, named }) } - InfixExpr(lhs, op, rhs) => { + InfixExpr(lhs, op, rhs, inversion) => { let lhs = lhs.follow_bindings(); let rhs = rhs.follow_bindings(); - InfixExpr(Box::new(lhs), *op, Box::new(rhs)) + InfixExpr(Box::new(lhs), *op, Box::new(rhs), *inversion) } // Expect that this function should only be called on instantiated types @@ -2502,7 +2550,7 @@ impl Type { } Type::MutableReference(elem) => elem.replace_named_generics_with_type_variables(), Type::Forall(_, typ) => typ.replace_named_generics_with_type_variables(), - Type::InfixExpr(lhs, _op, rhs) => { + Type::InfixExpr(lhs, _op, rhs, _) => { lhs.replace_named_generics_with_type_variables(); rhs.replace_named_generics_with_type_variables(); } @@ -2544,7 +2592,7 @@ impl Type { TypeBinding::Unbound(_, kind) => kind.integral_maximum_size(), }, Type::MutableReference(typ) => typ.integral_maximum_size(), - Type::InfixExpr(lhs, _op, rhs) => lhs.infix_kind(rhs).integral_maximum_size(), + Type::InfixExpr(lhs, _op, rhs, _) => lhs.infix_kind(rhs).integral_maximum_size(), Type::Constant(_, kind) => kind.integral_maximum_size(), Type::Array(..) @@ -2827,7 +2875,7 @@ impl std::fmt::Debug for Type { write!(f, "&mut {element:?}") } Type::Quoted(quoted) => write!(f, "{}", quoted), - Type::InfixExpr(lhs, op, rhs) => write!(f, "({lhs:?} {op} {rhs:?})"), + Type::InfixExpr(lhs, op, rhs, _) => write!(f, "({lhs:?} {op} {rhs:?})"), } } } @@ -2913,7 +2961,7 @@ impl std::hash::Hash for Type { Type::CheckedCast { to, .. } => to.hash(state), Type::Constant(value, _) => value.hash(state), Type::Quoted(typ) => typ.hash(state), - Type::InfixExpr(lhs, op, rhs) => { + Type::InfixExpr(lhs, op, rhs, _) => { lhs.hash(state); op.hash(state); rhs.hash(state); @@ -2982,7 +3030,7 @@ impl PartialEq for Type { lhs == rhs && lhs_kind == rhs_kind } (Quoted(lhs), Quoted(rhs)) => lhs == rhs, - (InfixExpr(l_lhs, l_op, l_rhs), InfixExpr(r_lhs, r_op, r_rhs)) => { + (InfixExpr(l_lhs, l_op, l_rhs, _), InfixExpr(r_lhs, r_op, r_rhs, _)) => { l_lhs == r_lhs && l_op == r_op && l_rhs == r_rhs } // Special case: we consider unbound named generics and type variables to be equal to each diff --git a/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs b/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs index 8cdf6f5502c..5750365c62d 100644 --- a/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs +++ b/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs @@ -58,7 +58,7 @@ impl Type { run_simplifications: bool, ) -> Type { match self.follow_bindings() { - Type::InfixExpr(lhs, op, rhs) => { + Type::InfixExpr(lhs, op, rhs, inversion) => { let kind = lhs.infix_kind(&rhs); let dummy_span = Span::default(); // evaluate_to_field_element also calls canonicalize so if we just called @@ -76,7 +76,7 @@ impl Type { let rhs = rhs.canonicalize_helper(found_checked_cast, run_simplifications); if !run_simplifications { - return Type::InfixExpr(Box::new(lhs), op, Box::new(rhs)); + return Type::InfixExpr(Box::new(lhs), op, Box::new(rhs), inversion); } if let Some(result) = Self::try_simplify_non_constants_in_lhs(&lhs, op, &rhs) { @@ -97,7 +97,7 @@ impl Type { return Self::sort_commutative(&lhs, op, &rhs); } - Type::InfixExpr(Box::new(lhs), op, Box::new(rhs)) + Type::InfixExpr(Box::new(lhs), op, Box::new(rhs), inversion) } Type::CheckedCast { from, to } => { let inner_found_checked_cast = true; @@ -131,7 +131,7 @@ impl Type { // Push each non-constant term to `sorted` to sort them. Recur on InfixExprs with the same operator. while let Some(item) = queue.pop() { match item.canonicalize_unchecked() { - Type::InfixExpr(lhs_inner, new_op, rhs_inner) if new_op == op => { + Type::InfixExpr(lhs_inner, new_op, rhs_inner, _) if new_op == op => { queue.push(*lhs_inner); queue.push(*rhs_inner); } @@ -157,18 +157,18 @@ impl Type { // - 1 since `typ` already is set to the first instance for _ in 0..first_type_count - 1 { - typ = Type::InfixExpr(Box::new(typ), op, Box::new(first.0.clone())); + typ = Type::infix_expr(Box::new(typ), op, Box::new(first.0.clone())); } for (rhs, rhs_count) in sorted { for _ in 0..rhs_count { - typ = Type::InfixExpr(Box::new(typ), op, Box::new(rhs.clone())); + typ = Type::infix_expr(Box::new(typ), op, Box::new(rhs.clone())); } } if constant != zero_value { let constant = Type::Constant(constant, lhs.infix_kind(rhs)); - typ = Type::InfixExpr(Box::new(typ), op, Box::new(constant)); + typ = Type::infix_expr(Box::new(typ), op, Box::new(constant)); } typ @@ -192,11 +192,11 @@ impl Type { match lhs.follow_bindings() { Type::CheckedCast { from, to } => { // Apply operation directly to `from` while attempting simplification to `to`. - let from = Type::InfixExpr(from, op, Box::new(rhs.clone())); + let from = Type::infix_expr(from, op, Box::new(rhs.clone())); let to = Self::try_simplify_non_constants_in_lhs(&to, op, rhs)?; Some(Type::CheckedCast { from: Box::new(from), to: Box::new(to) }) } - Type::InfixExpr(l_lhs, l_op, l_rhs) => { + Type::InfixExpr(l_lhs, l_op, l_rhs, _) => { // Note that this is exact, syntactic equality, not unification. // `rhs` is expected to already be in canonical form. if l_op.approx_inverse() != Some(op) @@ -229,11 +229,11 @@ impl Type { match rhs.follow_bindings() { Type::CheckedCast { from, to } => { // Apply operation directly to `from` while attempting simplification to `to`. - let from = Type::InfixExpr(Box::new(lhs.clone()), op, from); + let from = Type::infix_expr(Box::new(lhs.clone()), op, from); let to = Self::try_simplify_non_constants_in_rhs(lhs, op, &to)?; Some(Type::CheckedCast { from: Box::new(from), to: Box::new(to) }) } - Type::InfixExpr(r_lhs, r_op, r_rhs) => { + Type::InfixExpr(r_lhs, r_op, r_rhs, _) => { // `N / (M * N)` should be simplified to `1 / M`, but we only handle // simplifying to `M` in this function. if op == BinaryTypeOperator::Division && r_op == BinaryTypeOperator::Multiplication @@ -268,7 +268,7 @@ impl Type { let dummy_span = Span::default(); let rhs = rhs.evaluate_to_field_element(&kind, dummy_span).ok()?; - let Type::InfixExpr(l_type, l_op, l_rhs) = lhs.follow_bindings() else { + let Type::InfixExpr(l_type, l_op, l_rhs, _) = lhs.follow_bindings() else { return None; }; @@ -302,7 +302,7 @@ impl Type { let result = op.function(l_const, r_const, &lhs.infix_kind(rhs), dummy_span).ok()?; let constant = Type::Constant(result, lhs.infix_kind(rhs)); - Some(Type::InfixExpr(l_type, l_op, Box::new(constant))) + Some(Type::infix_expr(l_type, l_op, Box::new(constant))) } (Multiplication, Division) => { // We need to ensure the result divides evenly to preserve integer division semantics @@ -317,7 +317,7 @@ impl Type { let result = op.function(l_const, r_const, &lhs.infix_kind(rhs), dummy_span).ok()?; let constant = Box::new(Type::Constant(result, lhs.infix_kind(rhs))); - Some(Type::InfixExpr(l_type, l_op, constant)) + Some(Type::infix_expr(l_type, l_op, constant)) } } _ => None, @@ -331,13 +331,14 @@ impl Type { other: &Type, bindings: &mut TypeBindings, ) -> Result<(), UnificationError> { - if let Type::InfixExpr(lhs_a, op_a, rhs_a) = self { + if let Type::InfixExpr(lhs_a, op_a, rhs_a, _) = self { if let Some(inverse) = op_a.approx_inverse() { let kind = lhs_a.infix_kind(rhs_a); let dummy_span = Span::default(); if let Ok(rhs_a_value) = rhs_a.evaluate_to_field_element(&kind, dummy_span) { let rhs_a = Box::new(Type::Constant(rhs_a_value, kind)); - let new_other = Type::InfixExpr(Box::new(other.clone()), inverse, rhs_a); + let new_other = + Type::inverted_infix_expr(Box::new(other.clone()), inverse, rhs_a); let mut tmp_bindings = bindings.clone(); if lhs_a.try_unify(&new_other, &mut tmp_bindings).is_ok() { @@ -348,13 +349,14 @@ impl Type { } } - if let Type::InfixExpr(lhs_b, op_b, rhs_b) = other { + if let Type::InfixExpr(lhs_b, op_b, rhs_b, inversion) = other { if let Some(inverse) = op_b.approx_inverse() { let kind = lhs_b.infix_kind(rhs_b); let dummy_span = Span::default(); if let Ok(rhs_b_value) = rhs_b.evaluate_to_field_element(&kind, dummy_span) { let rhs_b = Box::new(Type::Constant(rhs_b_value, kind)); - let new_self = Type::InfixExpr(Box::new(self.clone()), inverse, rhs_b); + let new_self = + Type::InfixExpr(Box::new(self.clone()), inverse, rhs_b, !inversion); let mut tmp_bindings = bindings.clone(); if new_self.try_unify(lhs_b, &mut tmp_bindings).is_ok() { @@ -384,7 +386,7 @@ mod tests { TypeVariable::unbound(TypeVariableId(0), Kind::u32()), std::rc::Rc::new("N".to_owned()), ); - let n_minus_one = Type::InfixExpr( + let n_minus_one = Type::infix_expr( Box::new(n.clone()), BinaryTypeOperator::Subtraction, Box::new(Type::Constant(FieldElement::one(), Kind::u32())), @@ -392,7 +394,7 @@ mod tests { let checked_cast_n_minus_one = Type::CheckedCast { from: Box::new(n_minus_one.clone()), to: Box::new(n_minus_one) }; - let n_minus_one_plus_one = Type::InfixExpr( + let n_minus_one_plus_one = Type::infix_expr( Box::new(checked_cast_n_minus_one.clone()), BinaryTypeOperator::Addition, Box::new(Type::Constant(FieldElement::one(), Kind::u32())), @@ -405,7 +407,7 @@ mod tests { // We also want to check that if the `CheckedCast` is on the RHS then we'll still be able to canonicalize // the expression `1 + (N - 1)` to `N`. - let one_plus_n_minus_one = Type::InfixExpr( + let one_plus_n_minus_one = Type::infix_expr( Box::new(Type::Constant(FieldElement::one(), Kind::u32())), BinaryTypeOperator::Addition, Box::new(checked_cast_n_minus_one), @@ -423,13 +425,13 @@ mod tests { let x_type = Type::TypeVariable(x_var.clone()); let one = Type::Constant(FieldElement::one(), field_element_kind.clone()); - let lhs = Type::InfixExpr( + let lhs = Type::infix_expr( Box::new(x_type.clone()), BinaryTypeOperator::Addition, Box::new(one.clone()), ); let rhs = - Type::InfixExpr(Box::new(one), BinaryTypeOperator::Addition, Box::new(x_type.clone())); + Type::infix_expr(Box::new(one), BinaryTypeOperator::Addition, Box::new(x_type.clone())); // canonicalize let lhs = lhs.canonicalize(); @@ -546,7 +548,7 @@ mod proptests { 10, // We put up to 10 items per collection |inner| { (inner.clone(), any::(), inner) - .prop_map(|(lhs, op, rhs)| Type::InfixExpr(Box::new(lhs), op, Box::new(rhs))) + .prop_map(|(lhs, op, rhs)| Type::infix_expr(Box::new(lhs), op, Box::new(rhs))) }, ) } diff --git a/compiler/noirc_frontend/src/lexer/token.rs b/compiler/noirc_frontend/src/lexer/token.rs index 8df831bbaab..7d11b97ca16 100644 --- a/compiler/noirc_frontend/src/lexer/token.rs +++ b/compiler/noirc_frontend/src/lexer/token.rs @@ -1020,6 +1020,7 @@ pub enum Keyword { Dep, Else, Enum, + EnumDefinition, Expr, Field, Fn, @@ -1031,6 +1032,7 @@ pub enum Keyword { Impl, In, Let, + Loop, Match, Mod, Module, @@ -1079,6 +1081,7 @@ impl fmt::Display for Keyword { Keyword::Dep => write!(f, "dep"), Keyword::Else => write!(f, "else"), Keyword::Enum => write!(f, "enum"), + Keyword::EnumDefinition => write!(f, "EnumDefinition"), Keyword::Expr => write!(f, "Expr"), Keyword::Field => write!(f, "Field"), Keyword::Fn => write!(f, "fn"), @@ -1090,6 +1093,7 @@ impl fmt::Display for Keyword { Keyword::Impl => write!(f, "impl"), Keyword::In => write!(f, "in"), Keyword::Let => write!(f, "let"), + Keyword::Loop => write!(f, "loop"), Keyword::Match => write!(f, "match"), Keyword::Mod => write!(f, "mod"), Keyword::Module => write!(f, "Module"), @@ -1141,6 +1145,7 @@ impl Keyword { "dep" => Keyword::Dep, "else" => Keyword::Else, "enum" => Keyword::Enum, + "EnumDefinition" => Keyword::EnumDefinition, "Expr" => Keyword::Expr, "Field" => Keyword::Field, "fn" => Keyword::Fn, @@ -1152,6 +1157,7 @@ impl Keyword { "impl" => Keyword::Impl, "in" => Keyword::In, "let" => Keyword::Let, + "loop" => Keyword::Loop, "match" => Keyword::Match, "mod" => Keyword::Mod, "Module" => Keyword::Module, diff --git a/compiler/noirc_frontend/src/monomorphization/ast.rs b/compiler/noirc_frontend/src/monomorphization/ast.rs index c9ae3438e42..621eb30e4f8 100644 --- a/compiler/noirc_frontend/src/monomorphization/ast.rs +++ b/compiler/noirc_frontend/src/monomorphization/ast.rs @@ -1,4 +1,4 @@ -use std::fmt::Display; +use std::{collections::BTreeMap, fmt::Display}; use acvm::FieldElement; use iter_extended::vecmap; @@ -36,6 +36,7 @@ pub enum Expression { Index(Index), Cast(Cast), For(For), + Loop(Box), If(If), Tuple(Vec), ExtractTupleField(Box, usize), @@ -59,6 +60,7 @@ impl Expression { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Definition { Local(LocalId), + Global(GlobalId), Function(FuncId), Builtin(String), LowLevel(String), @@ -71,6 +73,10 @@ pub enum Definition { #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct LocalId(pub u32); +/// A function ID corresponds directly to an index of `Program::globals` +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)] +pub struct GlobalId(pub u32); + /// A function ID corresponds directly to an index of `Program::functions` #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct FuncId(pub u32); @@ -222,7 +228,9 @@ pub type Parameters = Vec<(LocalId, /*mutable:*/ bool, /*name:*/ String, Type)>; /// Represents how an Acir function should be inlined. /// This type is only relevant for ACIR functions as we do not inline any Brillig functions -#[derive(Default, Clone, Copy, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)] +#[derive( + Default, Clone, Copy, PartialEq, Eq, Debug, Hash, Serialize, Deserialize, PartialOrd, Ord, +)] pub enum InlineType { /// The most basic entry point can expect all its functions to be inlined. /// All function calls are expected to be inlined into a single ACIR. @@ -322,13 +330,14 @@ impl Type { } } -#[derive(Debug, Clone, Hash)] +#[derive(Debug, Clone, Hash, Default)] pub struct Program { pub functions: Vec, pub function_signatures: Vec, pub main_function_signature: FunctionSignature, pub return_location: Option, pub return_visibility: Visibility, + pub globals: BTreeMap, pub debug_variables: DebugVariables, pub debug_functions: DebugFunctions, pub debug_types: DebugTypes, @@ -342,6 +351,7 @@ impl Program { main_function_signature: FunctionSignature, return_location: Option, return_visibility: Visibility, + globals: BTreeMap, debug_variables: DebugVariables, debug_functions: DebugFunctions, debug_types: DebugTypes, @@ -352,6 +362,7 @@ impl Program { main_function_signature, return_location, return_visibility, + globals, debug_variables, debug_functions, debug_types, @@ -370,6 +381,13 @@ impl Program { FuncId(0) } + /// Globals are expected to be generated within a different context than + /// all other functions in the program. Thus, the globals space has the same + /// ID as `main`, although we should never expect a clash in these IDs. + pub fn global_space_id() -> FuncId { + FuncId(0) + } + /// Takes a function body by replacing it with `false` and /// returning the previous value pub fn take_function_body(&mut self, function: FuncId) -> Expression { diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index c3a32f979d4..191c3937e4b 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -25,6 +25,7 @@ use crate::{ Kind, Type, TypeBinding, TypeBindings, }; use acvm::{acir::AcirField, FieldElement}; +use ast::GlobalId; use fxhash::FxHashMap as HashMap; use iter_extended::{btree_map, try_vecmap, vecmap}; use noirc_errors::Location; @@ -70,6 +71,11 @@ struct Monomorphizer<'interner> { /// confuse users. locals: HashMap, + /// Globals are keyed by their unique ID because they are never duplicated during monomorphization. + globals: HashMap, + + finished_globals: HashMap, + /// Queue of functions to monomorphize next each item in the queue is a tuple of: /// (old_id, new_monomorphized_id, any type bindings to apply, the trait method if old_id is from a trait impl, is_unconstrained, location) queue: VecDeque<( @@ -91,6 +97,7 @@ struct Monomorphizer<'interner> { lambda_envs_stack: Vec, next_local_id: u32, + next_global_id: u32, next_function_id: u32, is_range_loop: bool, @@ -175,14 +182,18 @@ pub fn monomorphize_debug( let functions = vecmap(monomorphizer.finished_functions, |(_, f)| f); let FuncMeta { return_visibility, .. } = monomorphizer.interner.function_meta(&main); + let globals = monomorphizer.finished_globals.into_iter().collect::>(); + let (debug_variables, debug_functions, debug_types) = monomorphizer.debug_type_tracker.extract_vars_and_types(); + let program = Program::new( functions, func_sigs, function_sig, monomorphizer.return_location, *return_visibility, + globals, debug_variables, debug_functions, debug_types, @@ -195,9 +206,12 @@ impl<'interner> Monomorphizer<'interner> { Monomorphizer { functions: HashMap::default(), locals: HashMap::default(), + globals: HashMap::default(), + finished_globals: HashMap::default(), queue: VecDeque::new(), finished_functions: BTreeMap::new(), next_local_id: 0, + next_global_id: 0, next_function_id: 0, interner, lambda_envs_stack: Vec::new(), @@ -220,6 +234,12 @@ impl<'interner> Monomorphizer<'interner> { ast::FuncId(id) } + fn next_global_id(&mut self) -> GlobalId { + let id = self.next_global_id; + self.next_global_id += 1; + GlobalId(id) + } + fn lookup_local(&mut self, id: node_interner::DefinitionId) -> Option { self.locals.get(&id).copied().map(Definition::Local) } @@ -675,6 +695,10 @@ impl<'interner> Monomorphizer<'interner> { block, })) } + HirStatement::Loop(block) => { + let block = Box::new(self.expr(block)?); + Ok(ast::Expression::Loop(block)) + } HirStatement::Expression(expr) => self.expr(expr), HirStatement::Semi(expr) => { self.expr(expr).map(|expr| ast::Expression::Semi(Box::new(expr))) @@ -927,17 +951,7 @@ impl<'interner> Monomorphizer<'interner> { } } DefinitionKind::Global(global_id) => { - let global = self.interner.get_global(*global_id); - - let expr = if let GlobalValue::Resolved(value) = global.value.clone() { - value - .into_hir_expression(self.interner, global.location) - .map_err(MonomorphizationError::InterpreterError)? - } else { - unreachable!("All global values should be resolved at compile time and before monomorphization"); - }; - - self.expr(expr)? + self.global_ident(*global_id, definition.name.clone(), &typ, ident.location)? } DefinitionKind::Local(_) => match self.lookup_captured_expr(ident.id) { Some(expr) => expr, @@ -984,6 +998,66 @@ impl<'interner> Monomorphizer<'interner> { Ok(ident) } + fn global_ident( + &mut self, + global_id: node_interner::GlobalId, + name: String, + typ: &HirType, + location: Location, + ) -> Result { + let global = self.interner.get_global(global_id); + let id = global.id; + let expr = if let Some(seen_global) = self.globals.get(&id) { + let typ = Self::convert_type(typ, location)?; + let ident = ast::Ident { + location: Some(location), + definition: Definition::Global(*seen_global), + mutable: false, + name, + typ, + }; + ast::Expression::Ident(ident) + } else { + let (expr, is_closure) = if let GlobalValue::Resolved(value) = global.value.clone() { + let is_closure = value.is_closure(); + let expr = value + .into_hir_expression(self.interner, global.location) + .map_err(MonomorphizationError::InterpreterError)?; + (expr, is_closure) + } else { + unreachable!("All global values should be resolved at compile time and before monomorphization"); + }; + + let expr = self.expr(expr)?; + + // Globals are meant to be computed at compile time and are stored in their own context to be shared across functions. + // Closures are defined as normal functions among all SSA functions and later need to be defunctionalized. + // Thus, this means we would have to re-define any global closures. + // The effect of defunctionalization would be the same if we were redefining a global closure or a local closure + // just with an extra step of indirection through a global variable. + // For simplicity, we chose to instead inline closures at their callsite as we do not expect + // placing a closure in the global context to change the final result of the program. + if !is_closure { + let new_id = self.next_global_id(); + self.globals.insert(id, new_id); + + self.finished_globals.insert(new_id, expr); + let typ = Self::convert_type(typ, location)?; + let ident = ast::Ident { + location: Some(location), + definition: Definition::Global(new_id), + mutable: false, + name, + typ, + }; + ast::Expression::Ident(ident) + } else { + expr + } + }; + Ok(expr) + } + /// Convert a non-tuple/struct type to a monomorphized type fn convert_type(typ: &HirType, location: Location) -> Result { let typ = typ.follow_bindings_shallow(); @@ -1239,7 +1313,7 @@ impl<'interner> Monomorphizer<'interner> { } HirType::MutableReference(element) => Self::check_type(element, location), - HirType::InfixExpr(lhs, _, rhs) => { + HirType::InfixExpr(lhs, _, rhs, _) => { Self::check_type(lhs, location)?; Self::check_type(rhs, location) } diff --git a/compiler/noirc_frontend/src/monomorphization/printer.rs b/compiler/noirc_frontend/src/monomorphization/printer.rs index 9c1072a4117..665f4dcd371 100644 --- a/compiler/noirc_frontend/src/monomorphization/printer.rs +++ b/compiler/noirc_frontend/src/monomorphization/printer.rs @@ -49,6 +49,7 @@ impl AstPrinter { write!(f, " as {})", cast.r#type) } Expression::For(for_expr) => self.print_for(for_expr, f), + Expression::Loop(block) => self.print_loop(block, f), Expression::If(if_expr) => self.print_if(if_expr, f), Expression::Tuple(tuple) => self.print_tuple(tuple, f), Expression::ExtractTupleField(expr, index) => { @@ -209,6 +210,15 @@ impl AstPrinter { write!(f, "}}") } + fn print_loop(&mut self, block: &Expression, f: &mut Formatter) -> Result<(), std::fmt::Error> { + write!(f, "loop {{")?; + self.indent_level += 1; + self.print_expr_expect_block(block, f)?; + self.indent_level -= 1; + self.next_line(f)?; + write!(f, "}}") + } + fn print_if( &mut self, if_expr: &super::ast::If, @@ -293,6 +303,7 @@ impl Display for Definition { fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { match self { Definition::Local(id) => write!(f, "l{}", id.0), + Definition::Global(id) => write!(f, "g{}", id.0), Definition::Function(id) => write!(f, "f{}", id), Definition::Builtin(name) => write!(f, "{name}"), Definition::LowLevel(name) => write!(f, "{name}"), diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index a2b1d7fc8f0..431bed3b604 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -267,6 +267,11 @@ pub struct NodeInterner { /// Captures the documentation comments for each module, struct, trait, function, etc. pub(crate) doc_comments: HashMap>, + + /// Only for LSP: a map of trait ID to each module that pub or pub(crate) exports it. + /// In LSP this is used to offer importing the trait via one of these exports if + /// the trait is not visible where it's defined. + trait_reexports: HashMap>, } /// A dependency in the dependency graph may be a type or a definition. @@ -680,6 +685,7 @@ impl Default for NodeInterner { comptime_scopes: vec![HashMap::default()], trait_impl_associated_types: HashMap::default(), doc_comments: HashMap::default(), + trait_reexports: HashMap::default(), } } } @@ -729,6 +735,7 @@ impl NodeInterner { crate_id: unresolved_trait.crate_id, location: Location::new(unresolved_trait.trait_def.span, unresolved_trait.file_id), generics, + visibility: ItemVisibility::Private, self_type_typevar: TypeVariable::unbound(self.next_type_variable_id(), Kind::Normal), methods: Vec::new(), method_ids: unresolved_trait.method_ids.clone(), @@ -1715,8 +1722,18 @@ impl NodeInterner { let instantiated_object_type = object_type.substitute(&substitutions); let trait_generics = &trait_impl.borrow().trait_generics; + + // Replace any associated types with fresh type variables so that we match + // any existing impl regardless of associated types if one already exists. + // E.g. if we already have an `impl Foo for Baz`, we should + // reject `impl Foo for Baz` if it were to be added. let associated_types = self.get_associated_types_for_impl(impl_id); + let associated_types = vecmap(associated_types, |named| { + let typ = self.next_type_variable(); + NamedType { name: named.name.clone(), typ } + }); + // 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' @@ -1725,7 +1742,7 @@ impl NodeInterner { &instantiated_object_type, trait_id, trait_generics, - associated_types, + &associated_types, ) { let existing_impl = self.get_trait_implementation(existing); let existing_impl = existing_impl.borrow(); @@ -2255,6 +2272,20 @@ impl NodeInterner { _ => None, } } + + pub fn add_trait_reexport( + &mut self, + trait_id: TraitId, + module_id: ModuleId, + name: Ident, + visibility: ItemVisibility, + ) { + self.trait_reexports.entry(trait_id).or_default().push((module_id, name, visibility)); + } + + pub fn get_trait_reexports(&self, trait_id: TraitId) -> &[(ModuleId, Ident, ItemVisibility)] { + self.trait_reexports.get(&trait_id).map_or(&[], |exports| exports) + } } impl Methods { @@ -2268,31 +2299,26 @@ impl Methods { } /// Iterate through each method, starting with the direct methods - pub fn iter(&self) -> impl Iterator)> + '_ { - let trait_impl_methods = self.trait_impl_methods.iter().map(|m| (m.method, &m.typ)); - let direct = self.direct.iter().copied().map(|func_id| { - let typ: &Option = &None; - (func_id, typ) - }); + pub fn iter(&self) -> impl Iterator, Option)> { + let trait_impl_methods = + self.trait_impl_methods.iter().map(|m| (m.method, m.typ.as_ref(), Some(m.trait_id))); + let direct = self.direct.iter().copied().map(|func_id| (func_id, None, None)); direct.chain(trait_impl_methods) } - /// Select the 1 matching method with an object type matching `typ` - pub fn find_matching_method( - &self, - typ: &Type, + pub fn find_matching_methods<'a>( + &'a self, + typ: &'a Type, has_self_param: bool, - interner: &NodeInterner, - ) -> Option { - // When adding methods we always check they do not overlap, so there should be - // at most 1 matching method in this list. - for (method, method_type) in self.iter() { + interner: &'a NodeInterner, + ) -> impl Iterator)> + 'a { + self.iter().filter_map(move |(method, method_type, trait_id)| { if Self::method_matches(typ, has_self_param, method, method_type, interner) { - return Some(method); + Some((method, trait_id)) + } else { + None } - } - - None + }) } pub fn find_direct_method( @@ -2302,7 +2328,7 @@ impl Methods { interner: &NodeInterner, ) -> Option { for method in &self.direct { - if Self::method_matches(typ, has_self_param, *method, &None, interner) { + if Self::method_matches(typ, has_self_param, *method, None, interner) { return Some(*method); } } @@ -2320,7 +2346,7 @@ impl Methods { for trait_impl_method in &self.trait_impl_methods { let method = trait_impl_method.method; - let method_type = &trait_impl_method.typ; + let method_type = trait_impl_method.typ.as_ref(); let trait_id = trait_impl_method.trait_id; if Self::method_matches(typ, has_self_param, method, method_type, interner) { @@ -2335,7 +2361,7 @@ impl Methods { typ: &Type, has_self_param: bool, method: FuncId, - method_type: &Option, + method_type: Option<&Type>, interner: &NodeInterner, ) -> bool { match interner.function_meta(&method).typ.instantiate(interner).0 { diff --git a/compiler/noirc_frontend/src/parser/errors.rs b/compiler/noirc_frontend/src/parser/errors.rs index f44f109e1ce..508ed33857e 100644 --- a/compiler/noirc_frontend/src/parser/errors.rs +++ b/compiler/noirc_frontend/src/parser/errors.rs @@ -83,8 +83,8 @@ pub enum ParserErrorReason { "Multiple primary attributes found. Only one function attribute is allowed per function" )] MultipleFunctionAttributesFound, - #[error("A function attribute cannot be placed on a struct")] - NoFunctionAttributesAllowedOnStruct, + #[error("A function attribute cannot be placed on a struct or enum")] + NoFunctionAttributesAllowedOnType, #[error("Assert statements can only accept string literals")] AssertMessageNotString, #[error("Integer bit size {0} isn't supported")] diff --git a/compiler/noirc_frontend/src/parser/mod.rs b/compiler/noirc_frontend/src/parser/mod.rs index 17c156476a7..c433adbfdfb 100644 --- a/compiler/noirc_frontend/src/parser/mod.rs +++ b/compiler/noirc_frontend/src/parser/mod.rs @@ -13,7 +13,8 @@ mod parser; use crate::ast::{ Documented, Ident, ImportStatement, ItemVisibility, LetStatement, ModuleDeclaration, - NoirFunction, NoirStruct, NoirTrait, NoirTraitImpl, NoirTypeAlias, TypeImpl, UseTree, + NoirEnumeration, NoirFunction, NoirStruct, NoirTrait, NoirTraitImpl, NoirTypeAlias, TypeImpl, + UseTree, }; use crate::token::SecondaryAttribute; @@ -26,7 +27,8 @@ pub use parser::{parse_program, Parser, StatementOrExpressionOrLValue}; pub struct SortedModule { pub imports: Vec, pub functions: Vec>, - pub types: Vec>, + pub structs: Vec>, + pub enums: Vec>, pub traits: Vec>, pub trait_impls: Vec, pub impls: Vec, @@ -57,7 +59,7 @@ impl std::fmt::Display for SortedModule { write!(f, "{global_const}")?; } - for type_ in &self.types { + for type_ in &self.structs { write!(f, "{type_}")?; } @@ -96,7 +98,8 @@ impl ParsedModule { match item.kind { ItemKind::Import(import, visibility) => module.push_import(import, visibility), ItemKind::Function(func) => module.push_function(func, item.doc_comments), - ItemKind::Struct(typ) => module.push_type(typ, item.doc_comments), + ItemKind::Struct(typ) => module.push_struct(typ, item.doc_comments), + ItemKind::Enum(typ) => module.push_enum(typ, item.doc_comments), ItemKind::Trait(noir_trait) => module.push_trait(noir_trait, item.doc_comments), ItemKind::TraitImpl(trait_impl) => module.push_trait_impl(trait_impl), ItemKind::Impl(r#impl) => module.push_impl(r#impl), @@ -134,6 +137,7 @@ pub enum ItemKind { Import(UseTree, ItemVisibility), Function(NoirFunction), Struct(NoirStruct), + Enum(NoirEnumeration), Trait(NoirTrait), TraitImpl(NoirTraitImpl), Impl(TypeImpl), @@ -147,6 +151,7 @@ pub enum ItemKind { impl std::fmt::Display for ItemKind { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { + ItemKind::Enum(e) => e.fmt(f), ItemKind::Function(fun) => fun.fmt(f), ItemKind::ModuleDecl(m) => m.fmt(f), ItemKind::Import(tree, visibility) => { @@ -222,8 +227,12 @@ impl SortedModule { self.functions.push(Documented::new(func, doc_comments)); } - fn push_type(&mut self, typ: NoirStruct, doc_comments: Vec) { - self.types.push(Documented::new(typ, doc_comments)); + fn push_struct(&mut self, typ: NoirStruct, doc_comments: Vec) { + self.structs.push(Documented::new(typ, doc_comments)); + } + + fn push_enum(&mut self, typ: NoirEnumeration, doc_comments: Vec) { + self.enums.push(Documented::new(typ, doc_comments)); } fn push_trait(&mut self, noir_trait: NoirTrait, doc_comments: Vec) { diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 05f8ae3c2bb..e554248fb03 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -13,6 +13,7 @@ use super::{labels::ParsingRuleLabel, ParsedModule, ParserError, ParserErrorReas mod arguments; mod attributes; mod doc_comments; +mod enums; mod expression; mod function; mod generics; @@ -191,14 +192,10 @@ impl<'a> Parser<'a> { fn read_token_internal(&mut self) -> SpannedToken { loop { - let token = self.tokens.next(); - if let Some(token) = token { - match token { - Ok(token) => return token, - Err(lexer_error) => self.errors.push(lexer_error.into()), - } - } else { - return eof_spanned_token(); + match self.tokens.next() { + Some(Ok(token)) => return token, + Some(Err(lexer_error)) => self.errors.push(lexer_error.into()), + None => return eof_spanned_token(), } } } diff --git a/compiler/noirc_frontend/src/parser/parser/attributes.rs b/compiler/noirc_frontend/src/parser/parser/attributes.rs index 12cb37edb4b..e32e7d3cb23 100644 --- a/compiler/noirc_frontend/src/parser/parser/attributes.rs +++ b/compiler/noirc_frontend/src/parser/parser/attributes.rs @@ -92,7 +92,7 @@ impl<'a> Parser<'a> { .into_iter() .filter_map(|(attribute, span)| match attribute { Attribute::Function(..) => { - self.push_error(ParserErrorReason::NoFunctionAttributesAllowedOnStruct, span); + self.push_error(ParserErrorReason::NoFunctionAttributesAllowedOnType, span); None } Attribute::Secondary(attr) => Some(attr), diff --git a/compiler/noirc_frontend/src/parser/parser/enums.rs b/compiler/noirc_frontend/src/parser/parser/enums.rs new file mode 100644 index 00000000000..f95c0f8f72b --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/enums.rs @@ -0,0 +1,265 @@ +use noirc_errors::Span; + +use crate::{ + ast::{Documented, EnumVariant, Ident, ItemVisibility, NoirEnumeration, UnresolvedGenerics}, + parser::ParserErrorReason, + token::{Attribute, SecondaryAttribute, Token}, +}; + +use super::{ + parse_many::{separated_by_comma_until_right_brace, separated_by_comma_until_right_paren}, + Parser, +}; + +impl<'a> Parser<'a> { + /// Enum = 'enum' identifier Generics '{' EnumVariant* '}' + /// + /// EnumField = OuterDocComments identifier ':' Type + pub(crate) fn parse_enum( + &mut self, + attributes: Vec<(Attribute, Span)>, + visibility: ItemVisibility, + start_span: Span, + ) -> NoirEnumeration { + let attributes = self.validate_secondary_attributes(attributes); + + self.push_error(ParserErrorReason::ExperimentalFeature("Enums"), start_span); + + let Some(name) = self.eat_ident() else { + self.expected_identifier(); + return self.empty_enum( + Ident::default(), + attributes, + visibility, + Vec::new(), + start_span, + ); + }; + + let generics = self.parse_generics(); + + if !self.eat_left_brace() { + self.expected_token(Token::LeftBrace); + return self.empty_enum(name, attributes, visibility, generics, start_span); + } + + let comma_separated = separated_by_comma_until_right_brace(); + let variants = self.parse_many("enum variants", comma_separated, Self::parse_enum_variant); + + NoirEnumeration { + name, + attributes, + visibility, + generics, + variants, + span: self.span_since(start_span), + } + } + + fn parse_enum_variant(&mut self) -> Option> { + let mut doc_comments; + let name; + + // Loop until we find an identifier, skipping anything that's not one + loop { + let doc_comments_start_span = self.current_token_span; + doc_comments = self.parse_outer_doc_comments(); + + if let Some(ident) = self.eat_ident() { + name = ident; + break; + } + + if !doc_comments.is_empty() { + self.push_error( + ParserErrorReason::DocCommentDoesNotDocumentAnything, + self.span_since(doc_comments_start_span), + ); + } + + // Though we do have to stop at EOF + if self.at_eof() { + self.expected_token(Token::RightBrace); + return None; + } + + // Or if we find a right brace + if self.at(Token::RightBrace) { + return None; + } + + self.expected_identifier(); + self.bump(); + } + + let mut parameters = Vec::new(); + + if self.eat_left_paren() { + let comma_separated = separated_by_comma_until_right_paren(); + parameters = self.parse_many("variant parameters", comma_separated, Self::parse_type); + } + + Some(Documented::new(EnumVariant { name, parameters }, doc_comments)) + } + + fn empty_enum( + &self, + name: Ident, + attributes: Vec, + visibility: ItemVisibility, + generics: UnresolvedGenerics, + start_span: Span, + ) -> NoirEnumeration { + NoirEnumeration { + name, + attributes, + visibility, + generics, + variants: Vec::new(), + span: self.span_since(start_span), + } + } +} + +#[cfg(test)] +mod tests { + use crate::{ + ast::{IntegerBitSize, NoirEnumeration, Signedness, UnresolvedGeneric, UnresolvedTypeData}, + parser::{ + parser::{ + parse_program, + tests::{expect_no_errors, get_source_with_error_span}, + }, + ItemKind, ParserErrorReason, + }, + }; + + fn parse_enum_no_errors(src: &str) -> NoirEnumeration { + let (mut module, errors) = parse_program(src); + expect_no_errors(&errors); + assert_eq!(module.items.len(), 1); + let item = module.items.remove(0); + let ItemKind::Enum(noir_enum) = item.kind else { + panic!("Expected enum"); + }; + noir_enum + } + + #[test] + fn parse_empty_enum() { + let src = "enum Foo {}"; + let noir_enum = parse_enum_no_errors(src); + assert_eq!("Foo", noir_enum.name.to_string()); + assert!(noir_enum.variants.is_empty()); + assert!(noir_enum.generics.is_empty()); + } + + #[test] + fn parse_empty_enum_with_generics() { + let src = "enum Foo {}"; + let mut noir_enum = parse_enum_no_errors(src); + assert_eq!("Foo", noir_enum.name.to_string()); + assert!(noir_enum.variants.is_empty()); + assert_eq!(noir_enum.generics.len(), 2); + + let generic = noir_enum.generics.remove(0); + let UnresolvedGeneric::Variable(ident) = generic else { + panic!("Expected generic variable"); + }; + assert_eq!("A", ident.to_string()); + + let generic = noir_enum.generics.remove(0); + let UnresolvedGeneric::Numeric { ident, typ } = generic else { + panic!("Expected generic numeric"); + }; + assert_eq!("B", ident.to_string()); + assert_eq!( + typ.typ, + UnresolvedTypeData::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo) + ); + } + + #[test] + fn parse_enum_with_variants() { + let src = "enum Foo { X(i32), y(Field, u32), Z }"; + let mut noir_enum = parse_enum_no_errors(src); + assert_eq!("Foo", noir_enum.name.to_string()); + assert_eq!(noir_enum.variants.len(), 3); + + let variant = noir_enum.variants.remove(0).item; + assert_eq!("X", variant.name.to_string()); + assert!(matches!( + variant.parameters[0].typ, + UnresolvedTypeData::Integer(Signedness::Signed, IntegerBitSize::ThirtyTwo) + )); + + let variant = noir_enum.variants.remove(0).item; + assert_eq!("y", variant.name.to_string()); + assert!(matches!(variant.parameters[0].typ, UnresolvedTypeData::FieldElement)); + assert!(matches!(variant.parameters[1].typ, UnresolvedTypeData::Integer(..))); + + let variant = noir_enum.variants.remove(0).item; + assert_eq!("Z", variant.name.to_string()); + assert_eq!(variant.parameters.len(), 0); + } + + #[test] + fn parse_empty_enum_with_doc_comments() { + let src = "/// Hello\nenum Foo {}"; + let (module, errors) = parse_program(src); + expect_no_errors(&errors); + assert_eq!(module.items.len(), 1); + let item = &module.items[0]; + assert_eq!(item.doc_comments.len(), 1); + let ItemKind::Enum(noir_enum) = &item.kind else { + panic!("Expected enum"); + }; + assert_eq!("Foo", noir_enum.name.to_string()); + } + + #[test] + fn parse_unclosed_enum() { + let src = "enum Foo {"; + let (module, errors) = parse_program(src); + assert_eq!(errors.len(), 2); + assert_eq!(module.items.len(), 1); + let item = &module.items[0]; + let ItemKind::Enum(noir_enum) = &item.kind else { + panic!("Expected enum"); + }; + assert_eq!("Foo", noir_enum.name.to_string()); + } + + #[test] + fn parse_error_no_function_attributes_allowed_on_enum() { + let src = " + #[test] enum Foo {} + ^^^^^^^ + "; + let (src, _) = get_source_with_error_span(src); + let (_, errors) = parse_program(&src); + let reason = errors[0].reason().unwrap(); + assert!(matches!(reason, ParserErrorReason::NoFunctionAttributesAllowedOnType)); + } + + #[test] + fn recovers_on_non_field() { + let src = " + enum Foo { 42 X(i32) } + ^^ + "; + let (src, _) = get_source_with_error_span(src); + let (module, errors) = parse_program(&src); + + assert_eq!(module.items.len(), 1); + let item = &module.items[0]; + let ItemKind::Enum(noir_enum) = &item.kind else { + panic!("Expected enum"); + }; + assert_eq!("Foo", noir_enum.name.to_string()); + assert_eq!(noir_enum.variants.len(), 1); + + let error = &errors[1]; + assert_eq!(error.to_string(), "Expected an identifier but found '42'"); + } +} diff --git a/compiler/noirc_frontend/src/parser/parser/item.rs b/compiler/noirc_frontend/src/parser/parser/item.rs index ce712b559d8..d928d8e82d3 100644 --- a/compiler/noirc_frontend/src/parser/parser/item.rs +++ b/compiler/noirc_frontend/src/parser/parser/item.rs @@ -107,6 +107,7 @@ impl<'a> Parser<'a> { /// ( Use /// | ModOrContract /// | Struct + /// | Enum /// | Impl /// | Trait /// | Global @@ -148,6 +149,16 @@ impl<'a> Parser<'a> { ))]; } + if self.eat_keyword(Keyword::Enum) { + self.comptime_mutable_and_unconstrained_not_applicable(modifiers); + + return vec![ItemKind::Enum(self.parse_enum( + attributes, + modifiers.visibility, + start_span, + ))]; + } + if self.eat_keyword(Keyword::Impl) { self.comptime_mutable_and_unconstrained_not_applicable(modifiers); diff --git a/compiler/noirc_frontend/src/parser/parser/statement.rs b/compiler/noirc_frontend/src/parser/parser/statement.rs index 315f3b9f958..8c6d18d90ef 100644 --- a/compiler/noirc_frontend/src/parser/parser/statement.rs +++ b/compiler/noirc_frontend/src/parser/parser/statement.rs @@ -92,6 +92,7 @@ impl<'a> Parser<'a> { /// | ConstrainStatement /// | ComptimeStatement /// | ForStatement + /// | LoopStatement /// | IfStatement /// | BlockStatement /// | AssignStatement @@ -156,6 +157,10 @@ impl<'a> Parser<'a> { return Some(StatementKind::For(for_loop)); } + if let Some(block) = self.parse_loop() { + return Some(StatementKind::Loop(block)); + } + if let Some(kind) = self.parse_if_expr() { return Some(StatementKind::Expression(Expression { kind, @@ -287,6 +292,29 @@ impl<'a> Parser<'a> { Some(ForLoopStatement { identifier, range, block, span: self.span_since(start_span) }) } + /// LoopStatement = 'loop' Block + fn parse_loop(&mut self) -> Option { + let start_span = self.current_token_span; + if !self.eat_keyword(Keyword::Loop) { + return None; + } + + self.push_error(ParserErrorReason::ExperimentalFeature("loops"), start_span); + + let block_start_span = self.current_token_span; + let block = if let Some(block) = self.parse_block() { + Expression { + kind: ExpressionKind::Block(block), + span: self.span_since(block_start_span), + } + } else { + self.expected_token(Token::LeftBrace); + Expression { kind: ExpressionKind::Error, span: self.span_since(block_start_span) } + }; + + Some(block) + } + /// ForRange /// = ExpressionExceptConstructor /// | ExpressionExceptConstructor '..' ExpressionExceptConstructor @@ -790,4 +818,32 @@ mod tests { assert!(statement.is_none()); assert_eq!(parser.errors.len(), 2); } + + #[test] + fn parses_empty_loop() { + let src = "loop { }"; + let mut parser = Parser::for_str(src); + let statement = parser.parse_statement_or_error(); + let StatementKind::Loop(block) = statement.kind else { + panic!("Expected loop"); + }; + let ExpressionKind::Block(block) = block.kind else { + panic!("Expected block"); + }; + assert!(block.statements.is_empty()); + } + + #[test] + fn parses_loop_with_statements() { + let src = "loop { 1; 2 }"; + let mut parser = Parser::for_str(src); + let statement = parser.parse_statement_or_error(); + let StatementKind::Loop(block) = statement.kind else { + panic!("Expected loop"); + }; + let ExpressionKind::Block(block) = block.kind else { + panic!("Expected block"); + }; + assert_eq!(block.statements.len(), 2); + } } diff --git a/compiler/noirc_frontend/src/parser/parser/structs.rs b/compiler/noirc_frontend/src/parser/parser/structs.rs index da8ac64e021..b066565e680 100644 --- a/compiler/noirc_frontend/src/parser/parser/structs.rs +++ b/compiler/noirc_frontend/src/parser/parser/structs.rs @@ -251,7 +251,7 @@ mod tests { let (src, span) = get_source_with_error_span(src); let (_, errors) = parse_program(&src); let reason = get_single_error_reason(&errors, span); - assert!(matches!(reason, ParserErrorReason::NoFunctionAttributesAllowedOnStruct)); + assert!(matches!(reason, ParserErrorReason::NoFunctionAttributesAllowedOnType)); } #[test] diff --git a/compiler/noirc_frontend/src/parser/parser/tests.rs b/compiler/noirc_frontend/src/parser/parser/tests.rs index ea8b1fc638d..7308458e948 100644 --- a/compiler/noirc_frontend/src/parser/parser/tests.rs +++ b/compiler/noirc_frontend/src/parser/parser/tests.rs @@ -44,7 +44,7 @@ pub(super) fn get_single_error_reason( } pub(super) fn expect_no_errors(errors: &[ParserError]) { - if errors.is_empty() { + if errors.is_empty() || errors.iter().all(|error| error.is_warning()) { return; } diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index 884db763698..f325c7e60ca 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -208,6 +208,9 @@ impl<'a> Parser<'a> { if self.eat_keyword(Keyword::StructDefinition) { return Some(UnresolvedTypeData::Quoted(QuotedType::StructDefinition)); } + if self.eat_keyword(Keyword::EnumDefinition) { + return Some(UnresolvedTypeData::Quoted(QuotedType::EnumDefinition)); + } if self.eat_keyword(Keyword::TraitConstraint) { return Some(UnresolvedTypeData::Quoted(QuotedType::TraitConstraint)); } diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index c1b86b5dcf7..087e34fcc64 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -903,6 +903,7 @@ fn find_lambda_captures(stmts: &[StmtId], interner: &NodeInterner, result: &mut HirStatement::Constrain(constr_stmt) => constr_stmt.0, HirStatement::Semi(semi_expr) => semi_expr, HirStatement::For(for_loop) => for_loop.block, + HirStatement::Loop(block) => block, HirStatement::Error => panic!("Invalid HirStatement!"), HirStatement::Break => panic!("Unexpected break"), HirStatement::Continue => panic!("Unexpected continue"), @@ -3015,13 +3016,13 @@ fn do_not_eagerly_error_on_cast_on_type_variable() { #[test] fn error_on_cast_over_type_variable() { let src = r#" - pub fn foo(x: T, f: fn(T) -> U) -> U { + pub fn foo(f: fn(T) -> U, x: T, ) -> U { f(x) } fn main() { let x = "a"; - let _: Field = foo(x, |x| x as Field); + let _: Field = foo(|x| x as Field, x); } "#; @@ -3456,6 +3457,11 @@ fn arithmetic_generics_rounding_fail_on_struct() { #[test] fn unconditional_recursion_fail() { + // These examples are self recursive top level functions, which would actually + // not be inlined in the SSA (there is nothing to inline into but self), so it + // wouldn't panic due to infinite recursion, but the errors asserted here + // come from the compilation checks, which does static analysis to catch the + // problem before it even has a chance to cause a panic. let srcs = vec![ r#" fn main() { @@ -3955,3 +3961,115 @@ fn mutable_self_call() { "#; assert_no_errors(src); } + +#[test] +fn checks_visibility_of_trait_related_to_trait_impl_on_method_call() { + let src = r#" + mod moo { + pub struct Bar {} + } + + trait Foo { + fn foo(self); + } + + impl Foo for moo::Bar { + fn foo(self) {} + } + + fn main() { + let bar = moo::Bar {}; + bar.foo(); + } + "#; + assert_no_errors(src); +} + +#[test] +fn infers_lambda_argument_from_method_call_function_type() { + let src = r#" + struct Foo { + value: Field, + } + + impl Foo { + fn foo(self) -> Field { + self.value + } + } + + struct Box { + value: T, + } + + impl Box { + fn map(self, f: fn(T) -> U) -> Box { + Box { value: f(self.value) } + } + } + + fn main() { + let box = Box { value: Foo { value: 1 } }; + let _ = box.map(|foo| foo.foo()); + } + "#; + assert_no_errors(src); +} + +#[test] +fn infers_lambda_argument_from_call_function_type() { + let src = r#" + struct Foo { + value: Field, + } + + fn call(f: fn(Foo) -> Field) -> Field { + f(Foo { value: 1 }) + } + + fn main() { + let _ = call(|foo| foo.value); + } + "#; + assert_no_errors(src); +} + +#[test] +fn infers_lambda_argument_from_call_function_type_in_generic_call() { + let src = r#" + struct Foo { + value: Field, + } + + fn call(t: T, f: fn(T) -> Field) -> Field { + f(t) + } + + fn main() { + let _ = call(Foo { value: 1 }, |foo| foo.value); + } + "#; + assert_no_errors(src); +} + +#[test] +fn regression_7088() { + // A test for code that initially broke when implementing inferring + // lambda parameter types from the function type related to the call + // the lambda is in (PR #7088). + let src = r#" + struct U60Repr {} + + impl U60Repr { + fn new(_: [Field; N * NumFieldSegments]) -> Self { + U60Repr {} + } + } + + fn main() { + let input: [Field; 6] = [0; 6]; + let _: U60Repr<3, 6> = U60Repr::new(input); + } + "#; + assert_no_errors(src); +} diff --git a/compiler/noirc_frontend/src/tests/traits.rs b/compiler/noirc_frontend/src/tests/traits.rs index 3a55fc2b67d..eba9889a8e1 100644 --- a/compiler/noirc_frontend/src/tests/traits.rs +++ b/compiler/noirc_frontend/src/tests/traits.rs @@ -1,4 +1,5 @@ use crate::hir::def_collector::dc_crate::CompilationError; +use crate::hir::def_collector::errors::DefCollectorErrorKind; use crate::hir::resolution::errors::ResolverError; use crate::hir::resolution::import::PathResolutionError; use crate::hir::type_check::TypeCheckError; @@ -1236,3 +1237,160 @@ fn warns_if_trait_is_not_in_scope_for_generic_function_call_and_there_is_only_on assert_eq!(ident.to_string(), "foo"); assert_eq!(trait_name, "private_mod::Foo"); } + +#[test] +fn error_on_duplicate_impl_with_associated_type() { + let src = r#" + trait Foo { + type Bar; + } + + impl Foo for i32 { + type Bar = u32; + } + + impl Foo for i32 { + type Bar = u8; + } + + fn main() {} + "#; + + // Expect "Impl for type `i32` overlaps with existing impl" + // and "Previous impl defined here" + let errors = get_program_errors(src); + assert_eq!(errors.len(), 2); + + use CompilationError::DefinitionError; + use DefCollectorErrorKind::*; + assert!(matches!(&errors[0].0, DefinitionError(OverlappingImpl { .. }))); + assert!(matches!(&errors[1].0, DefinitionError(OverlappingImplNote { .. }))); +} + +#[test] +fn error_on_duplicate_impl_with_associated_constant() { + let src = r#" + trait Foo { + let Bar: u32; + } + + impl Foo for i32 { + let Bar = 5; + } + + impl Foo for i32 { + let Bar = 6; + } + + fn main() {} + "#; + + // Expect "Impl for type `i32` overlaps with existing impl" + // and "Previous impl defined here" + let errors = get_program_errors(src); + assert_eq!(errors.len(), 2); + + use CompilationError::DefinitionError; + use DefCollectorErrorKind::*; + assert!(matches!(&errors[0].0, DefinitionError(OverlappingImpl { .. }))); + assert!(matches!(&errors[1].0, DefinitionError(OverlappingImplNote { .. }))); +} + +// See https://github.com/noir-lang/noir/issues/6530 +#[test] +fn regression_6530() { + let src = r#" + pub trait From { + fn from(input: T) -> Self; + } + + pub trait Into { + fn into(self) -> T; + } + + impl Into for U + where + T: From, + { + fn into(self) -> T { + T::from(self) + } + } + + struct Foo { + inner: Field, + } + + impl Into for Foo { + fn into(self) -> Field { + self.inner + } + } + + fn main() { + let foo = Foo { inner: 0 }; + + // This works: + let _: Field = Into::::into(foo); + + // This was failing with 'No matching impl': + let _: Field = foo.into(); + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 0); +} + +#[test] +fn calls_trait_method_using_struct_name_when_multiple_impls_exist() { + let src = r#" + trait From2 { + fn from2(input: T) -> Self; + } + struct U60Repr {} + impl From2<[Field; 3]> for U60Repr { + fn from2(_: [Field; 3]) -> Self { + U60Repr {} + } + } + impl From2 for U60Repr { + fn from2(_: Field) -> Self { + U60Repr {} + } + } + fn main() { + let _ = U60Repr::from2([1, 2, 3]); + let _ = U60Repr::from2(1); + } + "#; + assert_no_errors(src); +} + +#[test] +fn calls_trait_method_using_struct_name_when_multiple_impls_exist_and_errors_turbofish() { + let src = r#" + trait From2 { + fn from2(input: T) -> Self; + } + struct U60Repr {} + impl From2<[Field; 3]> for U60Repr { + fn from2(_: [Field; 3]) -> Self { + U60Repr {} + } + } + impl From2 for U60Repr { + fn from2(_: Field) -> Self { + U60Repr {} + } + } + fn main() { + let _ = U60Repr::::from2([1, 2, 3]); + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::TypeMismatch { .. }) + )); +} diff --git a/cspell.json b/cspell.json index ed9f7427c6f..25a0cc91f52 100644 --- a/cspell.json +++ b/cspell.json @@ -35,6 +35,7 @@ "bunx", "bytecount", "cachix", + "callees", "callsite", "callsites", "callstack", @@ -204,6 +205,7 @@ "Secpr", "signedness", "signorecello", + "signum", "smallvec", "smol", "splitn", diff --git a/docs/docs/explainers/explainer-writing-noir.md b/docs/docs/explainers/explainer-writing-noir.md index e8d27c85ce2..a15f3170c60 100644 --- a/docs/docs/explainers/explainer-writing-noir.md +++ b/docs/docs/explainers/explainer-writing-noir.md @@ -26,9 +26,9 @@ The equivalent optimization task when writing zk circuits is affectionately refe ### Coding for circuits - a paradigm shift -In zero knowledge cryptography, code is compiled to "circuits" consisting of arithmetic gates, and gate count is the significant cost. Depending on the proving system this is linearly proportionate to proving time, and so from a product point this should be kept as low as possible. +In zero knowledge cryptography, code is compiled to "circuits" consisting of arithmetic gates, and gate count is the significant cost. Depending on the proving system this is linearly proportionate to proof size and proving time, so from a product point of view this should be kept as low as possible. -Whilst writing efficient code for web apps and Solidity has a few key differences, writing efficient circuits have a different set of considerations. It is a bit of a paradigm shift, like writing code for GPUs for the first time... +Whilst writing efficient code for web apps and Solidity have some differences, writing efficient circuits have a different set of considerations. It is a bit of a paradigm shift, like writing code for GPUs for the first time... For example, drawing a circle at (0, 0) of radius `r`: - For a single CPU thread, @@ -57,7 +57,7 @@ For those coming from a primarily web app background, this article will explain ## Translating from Rust -For some applications using Noir, existing code might be a convenient starting point to then proceed to optimize the gate count of. +Programs written in anything from pseudo code to C, can be translated into Noir. A Rust program written for execution can be readily ported to Noir thanks to the similarities in syntax. :::note Many valuable functions and algorithms have been written in more established languages (C/C++), and converted to modern ones (like Rust). @@ -93,23 +93,42 @@ A Noir program compiles to an Abstract Circuit Intermediate Representation which :::tip The command `nargo info` shows the programs circuit size, and is useful to compare the value of changes made. -You can dig deeper and use the `--print-acir` param to take a closer look at individual ACIR opcodes, and the proving backend to see its gate count (eg for barretenberg, `bb gates -b ./target/program.json`). +You can dig deeper and use the `--print-acir` param to take a closer look at individual ACIR opcodes, and the proving backend to see its gate count (eg for barretenberg, the `bb` binary has a `gates` option). ::: -### Use the `Field` type +### Numerical types -Since the native type of values in circuits are `Field`s, using them for variables in Noir means less gates converting them under the hood. -Some things to be mindful of when using a Field type for a regular integer value: -- A variable of type `Field` can be cast `as` an integer type (eg `u8`, `u64`) - - Note: this retains only the bits of the integer type. Eg a Field value of 260 as a `u8` becomes 4 -- For Field types arithmetic operations meaningfully overflow/underflow, yet for integer types they are checked according to their size -- Comparisons and bitwise operations do not exist for `Field`s, cast to an appropriately sized integer type when you need to +As mentioned earlier Noir has many familiar integer types (eg `i8`, `u64`). Ideally after bringing a program into Noir, proving/verifying of its execution just works where needed: client/server side, on an evm, or on the Aztec network. + +A program optimized for execution may leverage the binary representations of integers, reducing the number of clock cycles, and thus time of execution. +The cryptography in a proving backend makes use of a `Field` type, and leveraging this lower level type correctly can reduce gate count, and thus proof size and proving time. + +In some instances simply replacing the integer type with a `Field` could save on some range checks (and hence gates). +Note: when casting a `Field` to an integer type, the value is converted based on the integer binary representation. Eg a Field variable with a value of 260 `as u8` becomes 4 + +### `Field`s for efficiency + +`Field` types have their own underlying representation that is efficient for cryptography, which is different to binary representations efficient for CPUs. So, mathematically speaking, things like bitwise operations do not directly translate to fields. That said, the same outcome can be achieved if wanting to use the Field type as a number with lower overhead. + +For instance shift (`<<`) and or (`|`) work seamlessly with integer types (bit-packing `u8`'s into a `u16`): +``` + high as u16 << 8 | low as u16 +``` + +More efficiently with `Field` types, the equivalent is: +``` + low.assert_max_bit_size::<8>(); // ensure Field values could be represented as 8 bit numbers + high.assert_max_bit_size::<8>(); + (high * 2.pow_32(8) + low) +``` +(Note, the power of two can instead be a constant (256) or global evaluated at compile time) + +The first snippet is good for compatibility when using existing code, converting to the latter can help optimize frequently used functions. :::tip -Where possible, use `Field` type for values. Using smaller value types, and bit-packing strategies, will result in MORE gates +Where possible, use the `Field` type for values. Writing code with smaller value types and bit-packing strategies will result in MORE gates ::: - ### Use Arithmetic over non-arithmetic operations Since circuits are made of arithmetic gates, the cost of arithmetic operations tends to be one gate. Whereas for procedural code, they represent several clock cycles. diff --git a/docs/docs/getting_started/quick_start.md b/docs/docs/getting_started/quick_start.md index c980b5e7ffc..7b4524f6b8e 100644 --- a/docs/docs/getting_started/quick_start.md +++ b/docs/docs/getting_started/quick_start.md @@ -68,6 +68,7 @@ We can now use `nargo` to generate a _Prover.toml_ file, where our input values ```sh cd hello_world nargo check +``` Let's feed some valid values into this file: @@ -113,7 +114,7 @@ bb verify -k ./target/vk -p ./target/proof Notice that in order to verify a proof, the verifier knows nothing but the circuit, which is compiled and used to generate the verification key. This is obviously quite important: private inputs remain private. -As for the public inputs, you may have noticed they haven't been specified. This behavior varies with each particular backend, but barretenberg typically attaches them to the proof. You can see them by parsing and splitting it. For example for if your public inputs are 32 bytes: +As for the public inputs, you may have noticed they haven't been specified. This behavior varies with each particular backend, but barretenberg typically attaches them to the proof. You can see them by parsing and splitting it. For example if your public inputs are 32 bytes: ```bash head -c 32 ./target/proof | od -An -v -t x1 | tr -d $' \n' diff --git a/docs/docs/noir/concepts/control_flow.md b/docs/docs/noir/concepts/control_flow.md index b365bb22728..a11db545e32 100644 --- a/docs/docs/noir/concepts/control_flow.md +++ b/docs/docs/noir/concepts/control_flow.md @@ -29,10 +29,9 @@ if a == 0 { assert(x == 2); ``` -## Loops +## For loops -Noir has one kind of loop: the `for` loop. `for` loops allow you to repeat a block of code multiple -times. +`for` loops allow you to repeat a block of code multiple times. The following block of code between the braces is run 10 times. @@ -48,7 +47,7 @@ The index for loops is of type `u64`. ### Break and Continue -In unconstrained code, `break` and `continue` are also allowed in `for` loops. These are only allowed +In unconstrained code, `break` and `continue` are also allowed in `for` and `loop` loops. These are only allowed in unconstrained code since normal constrained code requires that Noir knows exactly how many iterations a loop may have. `break` and `continue` can be used like so: @@ -77,3 +76,22 @@ above, `continue` will jump to `println("Iteration start")` when used. Note that The iteration variable `i` is still increased by one as normal when `continue` is used. `break` and `continue` cannot currently be used to jump out of more than a single loop at a time. + +## Loops + +In unconstrained code, `loop` is allowed for loops that end with a `break`. This is only allowed +in unconstrained code since normal constrained code requires that Noir knows exactly how many iterations +a loop may have. + +```rust +let mut i = 10 +loop { + println(i); + i -= 1; + + if i == 0 { + break; + } +} +``` + diff --git a/docs/docs/noir/concepts/data_types/slices.mdx b/docs/docs/noir/concepts/data_types/slices.mdx index cfee564a302..e8091c62dd8 100644 --- a/docs/docs/noir/concepts/data_types/slices.mdx +++ b/docs/docs/noir/concepts/data_types/slices.mdx @@ -57,7 +57,7 @@ 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. +Returns a new slice 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 @@ -75,7 +75,7 @@ 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. +Returns a tuple of two items, the first element of the slice and the rest of the slice. ```rust fn pop_front(_self: Self) -> (T, Self) @@ -91,7 +91,7 @@ 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. +Returns a tuple of two items, the beginning of the slice with the last element omitted and the last element. ```rust fn pop_back(_self: Self) -> (Self, T) diff --git a/docs/docs/noir/concepts/generics.md b/docs/docs/noir/concepts/generics.md index 8925666aa20..bece5896a60 100644 --- a/docs/docs/noir/concepts/generics.md +++ b/docs/docs/noir/concepts/generics.md @@ -36,6 +36,7 @@ impl BigInt { 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); @@ -96,7 +97,17 @@ fn main() { // We can use first_element_is_equal for arrays of any type // as long as we have an Eq impl for the types we pass in let array = [MyStruct::new(), MyStruct::new()]; - assert(array_eq(array, array, MyStruct::eq)); + assert(first_element_is_equal(array, array)); +} + +struct MyStruct { + foo: Field +} + +impl MyStruct { + fn new() -> Self { + MyStruct { foo: 0 } + } } impl Eq for MyStruct { @@ -152,9 +163,9 @@ fn example() { // there is no matching impl for `u32: MyTrait`. // // Substituting the `10` on the left hand side of this assert - // with `10 as u32` would also fail with a type mismatch as we + // with `10 as u32` would fail with a type mismatch as we // are expecting a `Field` from the right hand side. - assert(10 as u32 == foo.generic_method::()); + assert(10 == foo.generic_method::()); } ``` diff --git a/docs/docs/noir/concepts/traits.md b/docs/docs/noir/concepts/traits.md index 1b232dcf8ab..13818ecffac 100644 --- a/docs/docs/noir/concepts/traits.md +++ b/docs/docs/noir/concepts/traits.md @@ -252,36 +252,33 @@ impl MyTrait for Field { Since associated constants can also be used in a type position, its values are limited to only other expression kinds allowed in numeric generics. -Note that currently all associated types and constants must be explicitly specified in a trait constraint. -If we leave out any, we'll get an error that we're missing one: +When writing a trait constraint, you can specify all associated types and constants explicitly if +you wish: ```rust -// Error! Constraint is missing associated constant for `Bar` -fn foo(x: T) where T: MyTrait { +fn foo(x: T) where T: MyTrait { ... } ``` -Because all associated types and constants must be explicitly specified, they are essentially named generics, -although this is set to change in the future. Future versions of Noir will allow users to elide associated types -in trait constraints similar to Rust. When this is done, you may still refer to their value with the `::AssociatedType` -syntax: +Or you can also elide them since there should only be one `Foo` and `Bar` for a given implementation +of `MyTrait` for a type: ```rust -// Only valid in future versions of Noir: fn foo(x: T) where T: MyTrait { - let _: ::Foo = ...; + ... } ``` -The type as trait syntax is possible in Noir today but is less useful when each type must be explicitly specified anyway: +If you elide associated types, you can still refer to them via the type as trait syntax ``: ```rust -fn foo(x: T) where T: MyTrait { - // Works, but could just use F directly - let _: >::Foo = ...; - - let _: F = ...; +fn foo(x: T) where + T: MyTrait, + ::Foo: Default + Eq +{ + let foo_value: ::Foo = Default::default(); + assert_eq(foo_value, foo_value); } ``` diff --git a/docs/docs/noir/standard_library/meta/struct_def.md b/docs/docs/noir/standard_library/meta/struct_def.md index 535708e0353..18e722af31e 100644 --- a/docs/docs/noir/standard_library/meta/struct_def.md +++ b/docs/docs/noir/standard_library/meta/struct_def.md @@ -42,23 +42,28 @@ any generics, the generics are also included as-is. #include_code generics noir_stdlib/src/meta/struct_def.nr rust -Returns each generic on this struct. +Returns each generic on this struct. Each generic is represented as a tuple containing the type, +and an optional containing the numeric type if it's a numeric generic. Example: ``` #[example] -struct Foo { - bar: [T; 2], +struct Foo { + bar: [T; K], baz: Baz, } comptime fn example(foo: StructDefinition) { - assert_eq(foo.generics().len(), 2); + assert_eq(foo.generics().len(), 3); // Fails because `T` isn't in scope // let t = quote { T }.as_type(); - // assert_eq(foo.generics()[0], t); + // assert_eq(foo.generics()[0].0, t); + assert(foo.generics()[0].1.is_none()); + + // Last generic is numeric, so we have the numeric type available to us + assert(foo.generics()[2].1.is_some()); } ``` @@ -66,7 +71,18 @@ comptime fn example(foo: StructDefinition) { #include_code fields noir_stdlib/src/meta/struct_def.nr rust -Returns each field of this struct as a pair of (field name, field type). +Returns (name, type) pairs of each field in this struct. +Any generic types used in each field type is automatically substituted with the +provided generic arguments. + +### fields_as_written + +#include_code fields_as_written noir_stdlib/src/meta/struct_def.nr rust + +Returns (name, type) pairs of each field in this struct. Each type is as-is +with any generic arguments unchanged. Unless the field types are not needed, +users should generally prefer to use `StructDefinition::fields` over this +function if possible. ### has_named_attribute diff --git a/docs/docs/noir/standard_library/meta/trait_impl.md b/docs/docs/noir/standard_library/meta/trait_impl.md index 659c6aad719..9134bf548b0 100644 --- a/docs/docs/noir/standard_library/meta/trait_impl.md +++ b/docs/docs/noir/standard_library/meta/trait_impl.md @@ -25,8 +25,10 @@ comptime { let generics = my_impl.trait_generic_args(); assert_eq(generics.len(), 2); - assert_eq(generics[0], quote { i32 }.as_type()); - assert_eq(generics[1], quote { Field }.as_type()); + assert_eq(generics[0].0, quote { i32 }.as_type()); + assert(generics[0].1.is_none()); + assert_eq(generics[1].0, quote { Field }.as_type()); + assert(generics[1].1.is_none()); } ``` diff --git a/docs/docs/tutorials/noirjs_app.md b/docs/docs/tutorials/noirjs_app.md index 8967ee005ce..cc61fff8405 100644 --- a/docs/docs/tutorials/noirjs_app.md +++ b/docs/docs/tutorials/noirjs_app.md @@ -25,7 +25,7 @@ Let's go barebones. Doing the bare minimum is not only simple, but also allows y Barebones means we can immediately start with the dependencies even on an empty folder 😈: ```bash -bun i @noir-lang/noir_wasm@1.0.0-beta.0 @noir-lang/noir_js@1.0.0-beta.0 @aztec/bb.js@0.63.1 +bun i @noir-lang/noir_wasm@1.0.0-beta.1 @noir-lang/noir_js@1.0.0-beta.1 @aztec/bb.js@0.63.1 ``` Wait, what are these dependencies? @@ -36,7 +36,7 @@ Wait, what are these dependencies? :::info -In this guide, we will install versions pinned to 1.0.0-beta.0. These work with Barretenberg version 0.63.1, so we are using that one version too. Feel free to try with older or later versions, though! +In this guide, we will install versions pinned to 1.0.0-beta.1. These work with Barretenberg version 0.63.1, so we are using that one version too. Feel free to try with older or later versions, though! ::: @@ -50,7 +50,7 @@ It's not just you. We also enjoy syntax highlighting. [Check out the Language Se ::: -All you need is a `main.nr` and a `Nargo.toml` file. You can follow the [noirup](../getting_started/noir_installation.md) installation and just run `noirup -v 1.0.0-beta.0`, or just create them by hand: +All you need is a `main.nr` and a `Nargo.toml` file. You can follow the [noirup](../getting_started/noir_installation.md) installation and just run `noirup -v 1.0.0-beta.1`, or just create them by hand: ```bash mkdir -p circuit/src diff --git a/docs/package.json b/docs/package.json index 39807588eaa..fcfdd3c9c14 100644 --- a/docs/package.json +++ b/docs/package.json @@ -13,6 +13,7 @@ "version": "yarn version::stables && ./scripts/cut_version.sh" }, "dependencies": { + "@cookbookdev/docsbot": "^4.24.9", "@docusaurus/core": "^3.5.2", "@docusaurus/preset-classic": "^3.5.2", "@easyops-cn/docusaurus-search-local": "^0.35.0", diff --git a/docs/src/theme/SearchBar/index.js b/docs/src/theme/SearchBar/index.js new file mode 100644 index 00000000000..41dd822a352 --- /dev/null +++ b/docs/src/theme/SearchBar/index.js @@ -0,0 +1,14 @@ +import React from 'react'; +import SearchBar from '@theme-original/SearchBar'; +import AskCookbook from '@cookbookdev/docsbot/react' +import BrowserOnly from '@docusaurus/BrowserOnly'; +/** It's a public API key, so it's safe to expose it here */ +const COOKBOOK_PUBLIC_API_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI2NzVhMGVkODQ4MGI5OThmNzlhNjNhOTciLCJpYXQiOjE3MzM5NTUyODgsImV4cCI6MjA0OTUzMTI4OH0.NzQlH2sfhJkjvnYxoaRgSY6nRyNClxHg57n3-JueN9Q"; +export default function SearchBarWrapper(props) { + return ( + <> + + {() => } + + ); +} diff --git a/docs/versioned_docs/version-v0.32.0/explainers/explainer-oracle.md b/docs/versioned_docs/version-v0.32.0/explainers/explainer-oracle.md deleted file mode 100644 index 821e1f95c04..00000000000 --- a/docs/versioned_docs/version-v0.32.0/explainers/explainer-oracle.md +++ /dev/null @@ -1,57 +0,0 @@ ---- -title: Oracles -description: This guide provides an in-depth understanding of how Oracles work in Noir programming. Learn how to use outside calculations in your programs, constrain oracles, and understand their uses and limitations. -keywords: - - Noir Programming - - Oracles - - JSON-RPC - - Foreign Call Handlers - - Constrained Functions - - Blockchain Programming -sidebar_position: 1 ---- - -If you've seen "The Matrix" you may recall "The Oracle" as Gloria Foster smoking cigarettes and baking cookies. While she appears to "know things", she is actually providing a calculation of a pre-determined future. Noir Oracles are similar, in a way. They don't calculate the future (yet), but they allow you to use outside calculations in your programs. - -![matrix oracle prediction](@site/static/img/memes/matrix_oracle.jpeg) - -A Noir program is usually self-contained. You can pass certain inputs to it, and it will generate a deterministic output for those inputs. But what if you wanted to defer some calculation to an outside process or source? - -Oracles are functions that provide this feature. - -## Use cases - -An example usage for Oracles is proving something on-chain. For example, proving that the ETH-USDC quote was below a certain target at a certain block time. Or even making more complex proofs like proving the ownership of an NFT as an anonymous login method. - -Another interesting use case is to defer expensive calculations to be made outside of the Noir program, and then constraining the result; similar to the use of [unconstrained functions](../noir/concepts//unconstrained.md). - -In short, anything that can be constrained in a Noir program but needs to be fetched from an external source is a great candidate to be used in oracles. - -## Constraining oracles - -Just like in The Matrix, Oracles are powerful. But with great power, comes great responsibility. Just because you're using them in a Noir program doesn't mean they're true. Noir has no superpowers. If you want to prove that Portugal won the Euro Cup 2016, you're still relying on potentially untrusted information. - -To give a concrete example, Alice wants to login to the [NounsDAO](https://nouns.wtf/) forum with her username "noir_nouner" by proving she owns a noun without revealing her ethereum address. Her Noir program could have an oracle call like this: - -```rust -#[oracle(getNoun)] -unconstrained fn get_noun(address: Field) -> Field -``` - -This oracle could naively resolve with the number of Nouns she possesses. However, it is useless as a trusted source, as the oracle could resolve to anything Alice wants. In order to make this oracle call actually useful, Alice would need to constrain the response from the oracle, by proving her address and the noun count belongs to the state tree of the contract. - -In short, **Oracles don't prove anything. Your Noir program does.** - -:::danger - -If you don't constrain the return of your oracle, you could be clearly opening an attack vector on your Noir program. Make double-triple sure that the return of an oracle call is constrained! - -::: - -## How to use Oracles - -On CLI, Nargo resolves oracles by making JSON RPC calls, which means it would require an RPC node to be running. - -In JavaScript, NoirJS accepts and resolves arbitrary call handlers (that is, not limited to JSON) as long as they match the expected types the developer defines. Refer to [Foreign Call Handler](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) to learn more about NoirJS's call handling. - -If you want to build using oracles, follow through to the [oracle guide](../how_to/how-to-oracles.md) for a simple example on how to do that. diff --git a/docs/versioned_docs/version-v0.32.0/explainers/explainer-recursion.md b/docs/versioned_docs/version-v0.32.0/explainers/explainer-recursion.md deleted file mode 100644 index df8529ef4e0..00000000000 --- a/docs/versioned_docs/version-v0.32.0/explainers/explainer-recursion.md +++ /dev/null @@ -1,176 +0,0 @@ ---- -title: Recursive proofs -description: Explore the concept of recursive proofs in Zero-Knowledge programming. Understand how recursion works in Noir, a language for writing smart contracts on the EVM blockchain. Learn through practical examples like Alice and Bob's guessing game, Charlie's recursive merkle tree, and Daniel's reusable components. Discover how to use recursive proofs to optimize computational resources and improve efficiency. - -keywords: - [ - "Recursive Proofs", - "Zero-Knowledge Programming", - "Noir", - "EVM Blockchain", - "Smart Contracts", - "Recursion in Noir", - "Alice and Bob Guessing Game", - "Recursive Merkle Tree", - "Reusable Components", - "Optimizing Computational Resources", - "Improving Efficiency", - "Verification Key", - "Aggregation", - "Recursive zkSNARK schemes", - "PLONK", - "Proving and Verification Keys" - ] -sidebar_position: 1 -pagination_next: how_to/how-to-recursion ---- - -In programming, we tend to think of recursion as something calling itself. A classic example would be the calculation of the factorial of a number: - -```js -function factorial(n) { - if (n === 0 || n === 1) { - return 1; - } else { - return n * factorial(n - 1); - } -} -``` - -In this case, while `n` is not `1`, this function will keep calling itself until it hits the base case, bubbling up the result on the call stack: - -```md - Is `n` 1? <--------- - /\ / - / \ n = n -1 - / \ / - Yes No -------- -``` - -In Zero-Knowledge, recursion has some similarities. - -It is not a Noir function calling itself, but a proof being used as an input to another circuit. In short, you verify one proof *inside* another proof, returning the proof that both proofs are valid. - -This means that, given enough computational resources, you can prove the correctness of any arbitrary number of proofs in a single proof. This could be useful to design state channels (for which a common example would be [Bitcoin's Lightning Network](https://en.wikipedia.org/wiki/Lightning_Network)), to save on gas costs by settling one proof on-chain, or simply to make business logic less dependent on a consensus mechanism. - -## Examples - -Let us look at some of these examples - -### Alice and Bob - Guessing game - -Alice and Bob are friends, and they like guessing games. They want to play a guessing game online, but for that, they need a trusted third-party that knows both of their secrets and finishes the game once someone wins. - -So, they use zero-knowledge proofs. Alice tries to guess Bob's number, and Bob will generate a ZK proof stating whether she succeeded or failed. - -This ZK proof can go on a smart contract, revealing the winner and even giving prizes. However, this means every turn needs to be verified on-chain. This incurs some cost and waiting time that may simply make the game too expensive or time-consuming to be worth it. - -As a solution, Alice proposes the following: "what if Bob generates his proof, and instead of sending it on-chain, I verify it *within* my own proof before playing my own turn?". - -She can then generate a proof that she verified his proof, and so on. - -```md - Did you fail? <-------------------------- - / \ / - / \ n = n -1 - / \ / - Yes No / - | | / - | | / - | You win / - | / - | / -Generate proof of that / - + / - my own guess ---------------- -``` - -### Charlie - Recursive merkle tree - -Charlie is a concerned citizen, and wants to be sure his vote in an election is accounted for. He votes with a ZK proof, but he has no way of knowing that his ZK proof was included in the total vote count! - -If the vote collector puts all of the votes into a [Merkle tree](https://en.wikipedia.org/wiki/Merkle_tree), everyone can prove the verification of two proofs within one proof, as such: - -```md - abcd - __________|______________ - | | - ab cd - _____|_____ ______|______ - | | | | - alice bob charlie daniel -``` - -Doing this recursively allows us to arrive on a final proof `abcd` which if true, verifies the correctness of all the votes. - -### Daniel - Reusable components - -Daniel has a big circuit and a big headache. A part of his circuit is a setup phase that finishes with some assertions that need to be made. But that section alone takes most of the proving time, and is largely independent of the rest of the circuit. - -He might find it more efficient to generate a proof for that setup phase separately, and verify that proof recursively in the actual business logic section of his circuit. This will allow for parallelization of both proofs, which results in a considerable speedup. - -## What params do I need - -As you can see in the [recursion reference](noir/standard_library/recursion.mdx), a simple recursive proof requires: - -- The proof to verify -- The Verification Key of the circuit that generated the proof -- A hash of this verification key, as it's needed for some backends -- The public inputs for the proof - -:::info - -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. - -So, taking the example of Alice and Bob and their guessing game: - -- Alice makes her guess. Her proof is *not* recursive: it doesn't verify any proof within it! It's just a standard `assert(x != y)` circuit -- Bob verifies Alice's proof and makes his own guess. In this circuit, he doesn't exactly *prove* the verification of Alice's proof. Instead, he *aggregates* his proof to Alice's proof. The actual verification is done when the full proof is verified, for example when using `nargo verify` or through the verifier smart contract. - -We can imagine recursive proofs a [relay race](https://en.wikipedia.org/wiki/Relay_race). The first runner doesn't have to receive the baton from anyone else, as he/she already starts with it. But when his/her turn is over, the next runner needs to receive it, run a bit more, and pass it along. Even though every runner could theoretically verify the baton mid-run (why not? 🏃🔍), only at the end of the race does the referee verify that the whole race is valid. - -::: - -## Some architecture - -As with everything in computer science, there's no one-size-fits all. But there are some patterns that could help understanding and implementing them. To give three examples: - -### Adding some logic to a proof verification - -This would be an approach for something like our guessing game, where proofs are sent back and forth and are verified by each opponent. This circuit would be divided in two sections: - -- A `recursive verification` section, which would be just the call to `std::verify_proof`, and that would be skipped on the first move (since there's no proof to verify) -- A `guessing` section, which is basically the logic part where the actual guessing happens - -In such a situation, and assuming Alice is first, she would skip the first part and try to guess Bob's number. Bob would then verify her proof on the first section of his run, and try to guess Alice's number on the second part, and so on. - -### Aggregating proofs - -In some one-way interaction situations, recursion would allow for aggregation of simple proofs that don't need to be immediately verified on-chain or elsewhere. - -To give a practical example, a barman wouldn't need to verify a "proof-of-age" on-chain every time he serves alcohol to a customer. Instead, the architecture would comprise two circuits: - -- A `main`, non-recursive circuit with some logic -- A `recursive` circuit meant to verify two proofs in one proof - -The customer's proofs would be intermediate, and made on their phones, and the barman could just verify them locally. He would then aggregate them into a final proof sent on-chain (or elsewhere) at the end of the day. - -### Recursively verifying different circuits - -Nothing prevents you from verifying different circuits in a recursive proof, for example: - -- A `circuit1` circuit -- A `circuit2` circuit -- A `recursive` circuit - -In this example, a regulator could verify that taxes were paid for a specific purchase by aggregating both a `payer` circuit (proving that a purchase was made and taxes were paid), and a `receipt` circuit (proving that the payment was received) - -## How fast is it - -At the time of writing, verifying recursive proofs is surprisingly fast. This is because most of the time is spent on generating the verification key that will be used to generate the next proof. So you are able to cache the verification key and reuse it later. - -Currently, Noir JS packages don't expose the functionality of loading proving and verification keys, but that feature exists in the underlying `bb.js` package. - -## How can I try it - -Learn more about using recursion in Nargo and NoirJS in the [how-to guide](../how_to/how-to-recursion.md) and see a full example in [noir-examples](https://github.com/noir-lang/noir-examples). diff --git a/docs/versioned_docs/version-v0.32.0/getting_started/_category_.json b/docs/versioned_docs/version-v0.32.0/getting_started/_category_.json deleted file mode 100644 index 5d694210bbf..00000000000 --- a/docs/versioned_docs/version-v0.32.0/getting_started/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "position": 0, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.32.0/getting_started/backend/_category_.json b/docs/versioned_docs/version-v0.32.0/getting_started/backend/_category_.json deleted file mode 100644 index b82e92beb0c..00000000000 --- a/docs/versioned_docs/version-v0.32.0/getting_started/backend/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "position": 1, - "label": "Install Proving Backend", - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.32.0/getting_started/backend/index.md b/docs/versioned_docs/version-v0.32.0/getting_started/backend/index.md deleted file mode 100644 index 7192d954877..00000000000 --- a/docs/versioned_docs/version-v0.32.0/getting_started/backend/index.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: Proving Backend Installation -description: Proving backends offer command line tools for proving and verifying Noir programs. This page describes how to install `bb` as an example. -keywords: [ - Proving - Backend - Barretenberg - bb - bbup - Installation - Terminal - Command - CLI - Version -] -pagination_next: getting_started/hello_noir/index ---- - -Proving backends each provide their own tools for working with Noir programs, providing functionality like proof generation, proof verification, and verifier smart contract generation. - -For the latest information on tooling provided by each proving backend, installation instructions, Noir version compatibility... you may refer to the proving backends' own documentation. - -You can find the full list of proving backends compatible with Noir in [Awesome Noir](https://github.com/noir-lang/awesome-noir/?tab=readme-ov-file#proving-backends). - -## Example: Installing `bb` - -`bb` is the CLI tool provided by the [Barretenberg proving backend](https://github.com/AztecProtocol/barretenberg) developed by Aztec Labs. - -You can find the instructions for installation in [`bb`'s documentation](https://github.com/AztecProtocol/aztec-packages/blob/master/barretenberg/cpp/src/barretenberg/bb/readme.md#installation). - -Once installed, we are ready to start working on [our first Noir program](../hello_noir/index.md). diff --git a/docs/versioned_docs/version-v0.32.0/getting_started/hello_noir/_category_.json b/docs/versioned_docs/version-v0.32.0/getting_started/hello_noir/_category_.json deleted file mode 100644 index 976a2325de0..00000000000 --- a/docs/versioned_docs/version-v0.32.0/getting_started/hello_noir/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "position": 2, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.32.0/getting_started/hello_noir/index.md b/docs/versioned_docs/version-v0.32.0/getting_started/hello_noir/index.md deleted file mode 100644 index 3baae217eb3..00000000000 --- a/docs/versioned_docs/version-v0.32.0/getting_started/hello_noir/index.md +++ /dev/null @@ -1,152 +0,0 @@ ---- -title: Creating 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, - ] -sidebar_position: 1 - ---- - -Now that we have installed Nargo and a proving backend, it is time to make our first hello world program! - -### 1. Create a new project directory - -Noir code can live anywhere on your computer. Let us create a _projects_ folder in the home -directory to house our first Noir program. - -Create the directory and change directory into it by running: - -```sh -mkdir ~/projects -cd ~/projects -``` - -## Nargo - -Nargo provides the ability to initiate and execute Noir projects. Read the [Nargo installation](../installation/index.md) section to learn more about Nargo and how to install it. - -### 2. Create a new Noir project - -Now that we are in the projects directory, create a new Nargo project by running: - -```sh -nargo new hello_world -``` - -`hello_world` can be any arbitrary project name, we are simply using `hello_world` for demonstration. - -In production, it is common practice to name the project folder, `circuits`, for clarity amongst other folders in the codebase (like: `contracts`, `scripts`, and `test`). - -A `hello_world` folder would be created. Similar to Rust, the folder houses _src/main.nr_ and -_Nargo.toml_ which contain 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](../../noir/concepts/data_types/index.md) 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](../../noir/concepts/comments.md) chapter. - -### 3. 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 -``` - -A _Prover.toml_ file will be generated in your project directory, to allow specifying input values to the program. - -### 4. Execute the Noir program - -Now that the project is set up, we can execute our Noir program. - -Fill in input values for execution in the _Prover.toml_ file. For example: - -```toml -x = "1" -y = "2" -``` - -Execute your Noir program: - -```sh -nargo execute witness-name -``` - -The witness corresponding to this execution will then be written to the file `./target/witness-name.gz`. - -The command also automatically compiles your Noir program if it was not already / was edited, which you may notice the compiled artifacts being written to the file `./target/hello_world.json`. - -## Proving Backend - -Proving backends provide the ability to generate and verify proofs of executing Noir programs, following Noir's tooling that compiles and executes the programs. Read the [proving backend installation](../backend/index.md) section to learn more about proving backends and how to install them. - -Barretenberg is used as an example here to demonstrate how proving and verifying could be implemented and used. Read the [`bb` installation](../backend/index.md#example-installing-bb) section for how to install Barretenberg's CLI tool; refer to [`bb`'s documentation](https://github.com/AztecProtocol/aztec-packages/blob/master/barretenberg/cpp/src/barretenberg/bb/readme.md) for full details about the tool. - -### 5. Prove an execution of the Noir program - -Using Barretenberg as an example, prove the valid execution of your Noir program running: - -```sh -bb prove -b ./target/hello_world.json -w ./target/witness-name.gz -o ./target/proof -``` - -The proof generated will then be written to the file `./target/proof`. - -### 6. Verify the execution proof - -Once a proof is generated, we can verify correct execution of our Noir program by verifying the proof file. - -Using Barretenberg as an example, compute the verification key for the Noir program by running: - -```sh -bb write_vk -b ./target/hello_world.json -o ./target/vk -``` - -And verify your proof by running: - -```sh -bb verify -k ./target/vk -p ./target/proof -``` - -If successful, the verification will complete in silence; if unsuccessful, the command will trigger logging of the corresponding error. - -Congratulations, you have now created and verified a proof for your very first Noir program! - -In the [next section](./project_breakdown.md), we will go into more detail on each step performed. diff --git a/docs/versioned_docs/version-v0.32.0/getting_started/hello_noir/project_breakdown.md b/docs/versioned_docs/version-v0.32.0/getting_started/hello_noir/project_breakdown.md deleted file mode 100644 index 96e653f6c08..00000000000 --- a/docs/versioned_docs/version-v0.32.0/getting_started/hello_noir/project_breakdown.md +++ /dev/null @@ -1,159 +0,0 @@ ---- -title: Project Breakdown -description: - Learn about the anatomy of a Nargo project, including the purpose of the Prover TOML - file, and how to prove and verify your program. -keywords: - [Nargo, Nargo project, Prover.toml, proof verification, private asset transfer] -sidebar_position: 2 ---- - -This section breaks down our hello world program from 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 - - 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. - -### 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 = "noir_starter" -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](../../noir/modules_packages_crates/workspaces.md) will look a bit different. For example: - -```toml -[workspace] -members = ["crates/a", "crates/b"] -default-member = "crates/a" -``` - -#### Package section - -The package section defines 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) -- `expression_width` (optional) - Sets the default backend expression width. This field will override the default backend expression width specified by the Noir compiler (currently set to width 4). - -#### Dependencies section - -This is where you will specify any dependencies for your project. See the [Dependencies page](../../noir/modules_packages_crates/dependencies.md) 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 that the condition to be satisfied (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 the inputs to the Noir program (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 execute` is executed, nargo will execute the Noir program using the inputs specified in `Prover.toml`, aborting if it finds that these do not satisfy the constraints defined by `main`. In this example, `x` and `y` must satisfy the inequality constraint `assert(x != y)`. - -If an output name is specified such as `nargo execute foo`, the witness generated by this execution will be written to `./target/foo.gz`. This can then be used to generate a proof of the execution. - -#### 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 execution by using the `--prover-name` or `-p` flags. - -This command looks for proof inputs in the default **Prover.toml** and generates the witness and saves it at `./target/foo.gz`: - -```bash -nargo execute foo -``` - -This command looks for proof inputs in the custom **OtherProver.toml** and generates the witness and saves it at `./target/bar.gz`: - -```bash -nargo execute -p OtherProver bar -``` - -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.32.0/getting_started/installation/_category_.json b/docs/versioned_docs/version-v0.32.0/getting_started/installation/_category_.json deleted file mode 100644 index 0c02fb5d4d7..00000000000 --- a/docs/versioned_docs/version-v0.32.0/getting_started/installation/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "position": 0, - "label": "Install Nargo", - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.32.0/getting_started/installation/index.md b/docs/versioned_docs/version-v0.32.0/getting_started/installation/index.md deleted file mode 100644 index 53ea9c7891c..00000000000 --- a/docs/versioned_docs/version-v0.32.0/getting_started/installation/index.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -title: Nargo Installation -description: - nargo is a command line tool for interacting with Noir programs. This page is a quick guide on how to install Nargo through the most common and easy method, noirup -keywords: [ - Nargo - Noir - Rust - Cargo - Noirup - Installation - Terminal Commands - Version Check - Nightlies - Specific Versions - Branches - Noirup Repository -] -pagination_next: getting_started/hello_noir/index ---- - -`nargo` is a tool for working with Noir programs on the CLI, providing you with the ability to start new projects, compile, execute and test Noir programs from the terminal. - -The name is inspired by Rust's package manager `cargo`; and similar to Rust's `rustup`, Noir also has an easy installation script `noirup`. - -## Installing Noirup - -Open a terminal on your machine, and write: - -```bash -curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash -``` - -Close the terminal, open another one, and run - -```bash -noirup -``` - -Done. That's it. 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. - -Now we're ready to start working on [our first Noir program!](../hello_noir/index.md) diff --git a/docs/versioned_docs/version-v0.32.0/getting_started/installation/other_install_methods.md b/docs/versioned_docs/version-v0.32.0/getting_started/installation/other_install_methods.md deleted file mode 100644 index 3634723562b..00000000000 --- a/docs/versioned_docs/version-v0.32.0/getting_started/installation/other_install_methods.md +++ /dev/null @@ -1,102 +0,0 @@ ---- -title: Alternative Installations -description: There are different ways to install Nargo, the one-stop shop and command-line tool for developing Noir programs. This guide explains how to specify which version to install when using noirup, and using WSL for windows. -keywords: [ - Installation - Nargo - Noirup - Binaries - Compiling from Source - WSL for Windows - macOS - Linux - Nix - Direnv - Uninstalling Nargo - ] -sidebar_position: 1 ---- - -## Encouraged Installation Method: Noirup - -Noirup is the endorsed method for installing Nargo, streamlining the process of fetching binaries or compiling from source. It supports a range of options to cater to your specific needs, from nightly builds and specific versions to compiling from various sources. - -### Installing Noirup - -First, ensure you have `noirup` installed: - -```sh -curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash -``` - -### Fetching Binaries - -With `noirup`, you can easily switch between different Nargo versions, including nightly builds: - -- **Nightly Version**: Install the latest nightly build. - - ```sh - noirup --version nightly - ``` - -- **Specific Version**: Install a specific version of Nargo. - ```sh - noirup --version - ``` - -### Compiling from Source - -`noirup` also enables compiling Nargo from various sources: - -- **From a Specific Branch**: Install from the latest commit on a branch. - - ```sh - noirup --branch - ``` - -- **From a Fork**: Install from the main branch of a fork. - - ```sh - noirup --repo - ``` - -- **From a Specific Branch in a Fork**: Install from a specific branch in a fork. - - ```sh - noirup --repo --branch - ``` - -- **From a Specific Pull Request**: Install from a specific PR. - - ```sh - noirup --pr - ``` - -- **From a Specific Commit**: Install from a specific commit. - - ```sh - noirup -C - ``` - -- **From Local Source**: Compile and install from a local directory. - ```sh - noirup --path ./path/to/local/source - ``` - -## Installation on Windows - -The default backend for Noir (Barretenberg) doesn't provide Windows binaries at this time. For that reason, Noir cannot be installed natively. 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](#encouraged-installation-method-noirup). - -## Uninstalling Nargo - -If you installed Nargo with `noirup`, you can uninstall Nargo by removing the files in `~/.nargo`, `~/nargo`, and `~/noir_cache`. This ensures that all installed binaries, configurations, and cache related to Nargo are fully removed from your system. - -```bash -rm -r ~/.nargo -rm -r ~/nargo -rm -r ~/noir_cache -``` diff --git a/docs/versioned_docs/version-v0.32.0/getting_started/tooling/noir_codegen.md b/docs/versioned_docs/version-v0.32.0/getting_started/tooling/noir_codegen.md deleted file mode 100644 index f7505bef7ab..00000000000 --- a/docs/versioned_docs/version-v0.32.0/getting_started/tooling/noir_codegen.md +++ /dev/null @@ -1,114 +0,0 @@ ---- -title: Noir Codegen for TypeScript -description: Learn how to use Noir codegen to generate TypeScript bindings -keywords: [Nargo, Noir, compile, TypeScript] -sidebar_position: 3 ---- - -When using TypeScript, it is extra work to interpret Noir program outputs in a type-safe way. Third party libraries may exist for popular Noir programs, but they are either hard to find or unmaintained. - -Now you can generate TypeScript bindings for your Noir programs in two steps: -1. Exporting Noir functions using `nargo export` -2. Using the TypeScript module `noir_codegen` to generate TypeScript binding - -**Note:** you can only export functions from a Noir *library* (not binary or contract program types). - -## Installation - -### Your TypeScript project - -If you don't already have a TypeScript project you can add the module with `yarn` (or `npm`), then initialize it: - -```bash -yarn add typescript -D -npx tsc --init -``` - -### Add TypeScript module - `noir_codegen` - -The following command will add the module to your project's devDependencies: - -```bash -yarn add @noir-lang/noir_codegen -D -``` - -### Nargo library -Make sure you have Nargo, v0.25.0 or greater, installed. If you don't, follow the [installation guide](../installation/index.md). - -If you're in a new project, make a `circuits` folder and create a new Noir library: - -```bash -mkdir circuits && cd circuits -nargo new --lib myNoirLib -``` - -## Usage - -### Export ABI of specified functions - -First go to the `.nr` files in your Noir library, and add the `#[export]` macro to each function that you want to use in TypeScript. - -```rust -#[export] -fn your_function(... -``` - -From your Noir library (where `Nargo.toml` is), run the following command: - -```bash -nargo export -``` - -You will now have an `export` directory with a .json file per exported function. - -You can also specify the directory of Noir programs using `--program-dir`, for example: - -```bash -nargo export --program-dir=./circuits/myNoirLib -``` - -### Generate TypeScript bindings from exported functions - -To use the `noir-codegen` package we added to the TypeScript project: - -```bash -yarn noir-codegen ./export/your_function.json -``` - -This creates an `exports` directory with an `index.ts` file containing all exported functions. - -**Note:** adding `--out-dir` allows you to specify an output dir for your TypeScript bindings to go. Eg: - -```bash -yarn noir-codegen ./export/*.json --out-dir ./path/to/output/dir -``` - -## Example .nr function to .ts output - -Consider a Noir library with this function: - -```rust -#[export] -fn not_equal(x: Field, y: Field) -> bool { - x != y -} -``` - -After the export and codegen steps, you should have an `index.ts` like: - -```typescript -export type Field = string; - - -export const is_equal_circuit: CompiledCircuit = -{"abi":{"parameters":[{"name":"x","type":{"kind":"field"},"visibility":"private"},{"name":"y","type":{"kind":"field"},"visibility":"private"}],"return_type":{"abi_type":{"kind":"boolean"},"visibility":"private"}},"bytecode":"H4sIAAAAAAAA/7WUMQ7DIAxFQ0Krrr2JjSGYLVcpKrn/CaqqDQN12WK+hPBgmWd/wEyHbF1SS923uhOs3pfoChI+wKXMAXzIKyNj4PB0TFTYc0w5RUjoqeAeEu1wqK0F54RGkWvW44LPzExnlkbMEs4JNZmN8PxS42uHv82T8a3Jeyn2Ks+VLPcO558HmyLMCDOXAXXtpPt4R/Rt9T36ss6dS9HGPx/eG17nGegKBQAA"}; - -export async function is_equal(x: Field, y: Field, foreignCallHandler?: ForeignCallHandler): Promise { - const program = new Noir(is_equal_circuit); - const args: InputMap = { x, y }; - const { returnValue } = await program.execute(args, foreignCallHandler); - return returnValue as boolean; -} -``` - -Now the `is_equal()` function and relevant types are readily available for use in TypeScript. diff --git a/docs/versioned_docs/version-v0.32.0/how_to/_category_.json b/docs/versioned_docs/version-v0.32.0/how_to/_category_.json deleted file mode 100644 index 23b560f610b..00000000000 --- a/docs/versioned_docs/version-v0.32.0/how_to/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "position": 1, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.32.0/how_to/debugger/_category_.json b/docs/versioned_docs/version-v0.32.0/how_to/debugger/_category_.json deleted file mode 100644 index cc2cbb1c253..00000000000 --- a/docs/versioned_docs/version-v0.32.0/how_to/debugger/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "Debugging", - "position": 5, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.32.0/how_to/debugger/debugging_with_the_repl.md b/docs/versioned_docs/version-v0.32.0/how_to/debugger/debugging_with_the_repl.md deleted file mode 100644 index 1d64dae3f37..00000000000 --- a/docs/versioned_docs/version-v0.32.0/how_to/debugger/debugging_with_the_repl.md +++ /dev/null @@ -1,164 +0,0 @@ ---- -title: Using the REPL Debugger -description: - Step-by-step guide on how to debug your Noir circuits with the REPL Debugger. -keywords: - [ - Nargo, - Noir CLI, - Noir Debugger, - REPL, - ] -sidebar_position: 1 ---- - -#### Pre-requisites - -In order to use the REPL debugger, first you need to install recent enough versions of Nargo and vscode-noir. - -## Debugging a simple circuit - -Let's debug a simple circuit: - -```rust -fn main(x : Field, y : pub Field) { - assert(x != y); -} -``` - -To start the REPL debugger, using a terminal, go to a Noir circuit's home directory. Then: - -`$ nargo debug` - -You should be seeing this in your terminal: - -``` -[main] Starting debugger -At ~/noir-examples/recursion/circuits/main/src/main.nr:1:9 - 1 -> fn main(x : Field, y : pub Field) { - 2 assert(x != y); - 3 } -> -``` - -The debugger displays the current Noir code location, and it is now waiting for us to drive it. - -Let's first take a look at the available commands. For that we'll use the `help` command. - -``` -> help -Available commands: - - opcodes display ACIR opcodes - into step into to the next opcode - next step until a new source location is reached - out step until a new source location is reached - and the current stack frame is finished - break LOCATION:OpcodeLocation add a breakpoint at an opcode location - over step until a new source location is reached - without diving into function calls - restart restart the debugging session - delete LOCATION:OpcodeLocation delete breakpoint at an opcode location - witness show witness map - witness index:u32 display a single witness from the witness map - witness index:u32 value:String update a witness with the given value - memset index:usize value:String update a memory cell with the given - value - continue continue execution until the end of the - program - vars show variable values available at this point - in execution - stacktrace display the current stack trace - memory show memory (valid when executing unconstrained code) - step step to the next ACIR opcode - -Other commands: - - help Show this help message - quit Quit repl - -``` - -Some commands operate only for unconstrained functions, such as `memory` and `memset`. If you try to use them while execution is paused at an ACIR opcode, the debugger will simply inform you that you are not executing unconstrained code: - -``` -> memory -Unconstrained VM memory not available -> -``` - -Before continuing, we can take a look at the initial witness map: - -``` -> witness -_0 = 1 -_1 = 2 -> -``` - -Cool, since `x==1`, `y==2`, and we want to check that `x != y`, our circuit should succeed. At this point we could intervene and use the witness setter command to change one of the witnesses. Let's set `y=3`, then back to 2, so we don't affect the expected result: - -``` -> witness -_0 = 1 -_1 = 2 -> witness 1 3 -_1 = 3 -> witness -_0 = 1 -_1 = 3 -> witness 1 2 -_1 = 2 -> witness -_0 = 1 -_1 = 2 -> -``` - -Now we can inspect the current state of local variables. For that we use the `vars` command. - -``` -> vars -> -``` - -We currently have no vars in context, since we are at the entry point of the program. Let's use `next` to execute until the next point in the program. - -``` -> vars -> next -At ~/noir-examples/recursion/circuits/main/src/main.nr:1:20 - 1 -> fn main(x : Field, y : pub Field) { - 2 assert(x != y); - 3 } -> vars -x:Field = 0x01 -``` - -As a result of stepping, the variable `x`, whose initial value comes from the witness map, is now in context and returned by `vars`. - -``` -> next - 1 fn main(x : Field, y : pub Field) { - 2 -> assert(x != y); - 3 } -> vars -y:Field = 0x02 -x:Field = 0x01 -``` - -Stepping again we can finally see both variables and their values. And now we can see that the next assertion should succeed. - -Let's continue to the end: - -``` -> continue -(Continuing execution...) -Finished execution -> q -[main] Circuit witness successfully solved -``` - -Upon quitting the debugger after a solved circuit, the resulting circuit witness gets saved, equivalent to what would happen if we had run the same circuit with `nargo execute`. - -We just went through the basics of debugging using Noir REPL debugger. For a comprehensive reference, check out [the reference page](../../reference/debugger/debugger_repl.md). diff --git a/docs/versioned_docs/version-v0.32.0/how_to/debugger/debugging_with_vs_code.md b/docs/versioned_docs/version-v0.32.0/how_to/debugger/debugging_with_vs_code.md deleted file mode 100644 index a5858c1a5eb..00000000000 --- a/docs/versioned_docs/version-v0.32.0/how_to/debugger/debugging_with_vs_code.md +++ /dev/null @@ -1,68 +0,0 @@ ---- -title: Using the VS Code Debugger -description: - Step by step guide on how to debug your Noir circuits with the VS Code Debugger configuration and features. -keywords: - [ - Nargo, - Noir CLI, - Noir Debugger, - VS Code, - IDE, - ] -sidebar_position: 0 ---- - -This guide will show you how to use VS Code with the vscode-noir extension to debug a Noir project. - -#### Pre-requisites - -- Nargo -- vscode-noir -- A Noir project with a `Nargo.toml`, `Prover.toml` and at least one Noir (`.nr`) containing an entry point function (typically `main`). - -## Running the debugger - -The easiest way to start debugging is to open the file you want to debug, and press `F5`. This will cause the debugger to launch, using your `Prover.toml` file as input. - -You should see something like this: - -![Debugger launched](@site/static/img/debugger/1-started.png) - -Let's inspect the state of the program. For that, we open VS Code's _Debug pane_. Look for this icon: - -![Debug pane icon](@site/static/img/debugger/2-icon.png) - -You will now see two categories of variables: Locals and Witness Map. - -![Debug pane expanded](@site/static/img/debugger/3-debug-pane.png) - -1. **Locals**: variables of your program. At this point in execution this section is empty, but as we step through the code it will get populated by `x`, `result`, `digest`, etc. - -2. **Witness map**: these are initially populated from your project's `Prover.toml` file. In this example, they will be used to populate `x` and `result` at the beginning of the `main` function. - -Most of the time you will probably be focusing mostly on locals, as they represent the high level state of your program. - -You might be interested in inspecting the witness map in case you are trying to solve a really low level issue in the compiler or runtime itself, so this concerns mostly advanced or niche users. - -Let's step through the program, by using the debugger buttons or their corresponding keyboard shortcuts. - -![Debugger buttons](@site/static/img/debugger/4-debugger-buttons.png) - -Now we can see in the variables pane that there's values for `digest`, `result` and `x`. - -![Inspecting locals](@site/static/img/debugger/5-assert.png) - -We can also inspect the values of variables by directly hovering on them on the code. - -![Hover locals](@site/static/img/debugger/6-hover.png) - -Let's set a break point at the `keccak256` function, so we can continue execution up to the point when it's first invoked without having to go one step at a time. - -We just need to click the to the right of the line number 18. Once the breakpoint appears, we can click the `continue` button or use its corresponding keyboard shortcut (`F5` by default). - -![Breakpoint](@site/static/img/debugger/7-break.png) - -Now we are debugging the `keccak256` function, notice the _Call Stack pane_ at the lower right. This lets us inspect the current call stack of our process. - -That covers most of the current debugger functionalities. Check out [the reference](../../reference/debugger/debugger_vscode.md) for more details on how to configure the debugger. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.32.0/how_to/how-to-oracles.md b/docs/versioned_docs/version-v0.32.0/how_to/how-to-oracles.md deleted file mode 100644 index 94a89710e66..00000000000 --- a/docs/versioned_docs/version-v0.32.0/how_to/how-to-oracles.md +++ /dev/null @@ -1,269 +0,0 @@ ---- -title: How to use Oracles -description: Learn how to use oracles in your Noir program with examples in both Nargo and NoirJS. This guide also covers writing a JSON RPC server and providing custom foreign call handlers for NoirJS. -keywords: - - Noir Programming - - Oracles - - Nargo - - NoirJS - - JSON RPC Server - - Foreign Call Handlers -sidebar_position: 1 ---- - -This guide shows you how to use oracles in your Noir program. For the sake of clarity, it assumes that: - -- You have read the [explainer on Oracles](../explainers/explainer-oracle.md) and are comfortable with the concept. -- You have a Noir program to add oracles to. You can create one using the [vite-hardhat starter](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat) as a boilerplate. -- You understand the concept of a JSON-RPC server. Visit the [JSON-RPC website](https://www.jsonrpc.org/) if you need a refresher. -- You are comfortable with server-side JavaScript (e.g. Node.js, managing packages, etc.). - -## Rundown - -This guide has 3 major steps: - -1. How to modify our Noir program to make use of oracle calls as unconstrained functions -2. How to write a JSON RPC Server to resolve these oracle calls with Nargo -3. How to use them in Nargo and how to provide a custom resolver in NoirJS - -## Step 1 - Modify your Noir program - -An oracle is defined in a Noir program by defining two methods: - -- An unconstrained method - This tells the compiler that it is executing an [unconstrained functions](../noir/concepts//unconstrained.md). -- A decorated oracle method - This tells the compiler that this method is an RPC call. - -An example of an oracle that returns a `Field` would be: - -```rust -#[oracle(getSqrt)] -unconstrained fn sqrt(number: Field) -> Field { } - -unconstrained fn get_sqrt(number: Field) -> Field { - sqrt(number) -} -``` - -In this example, we're wrapping our oracle function in an unconstrained method, and decorating it with `oracle(getSqrt)`. We can then call the unconstrained function as we would call any other function: - -```rust -fn main(input: Field) { - let sqrt = get_sqrt(input); -} -``` - -In the next section, we will make this `getSqrt` (defined on the `sqrt` decorator) be a method of the RPC server Noir will use. - -:::danger - -As explained in the [Oracle Explainer](../explainers/explainer-oracle.md), this `main` function is unsafe unless you constrain its return value. For example: - -```rust -fn main(input: Field) { - let sqrt = get_sqrt(input); - assert(sqrt.pow_32(2) as u64 == input as u64); // <---- constrain the return of an oracle! -} -``` - -::: - -:::info - -Currently, oracles only work with single params or array params. For example: - -```rust -#[oracle(getSqrt)] -unconstrained fn sqrt([Field; 2]) -> [Field; 2] { } -``` - -::: - -## Step 2 - Write an RPC server - -Brillig will call *one* RPC server. Most likely you will have to write your own, and you can do it in whatever language you prefer. In this guide, we will do it in Javascript. - -Let's use the above example of an oracle that consumes an array with two `Field` and returns their square roots: - -```rust -#[oracle(getSqrt)] -unconstrained fn sqrt(input: [Field; 2]) -> [Field; 2] { } - -unconstrained fn get_sqrt(input: [Field; 2]) -> [Field; 2] { - sqrt(input) -} - -fn main(input: [Field; 2]) { - let sqrt = get_sqrt(input); - assert(sqrt[0].pow_32(2) as u64 == input[0] as u64); - assert(sqrt[1].pow_32(2) as u64 == input[1] as u64); -} -``` - -:::info - -Why square root? - -In general, computing square roots is computationally more expensive than multiplications, which takes a toll when speaking about ZK applications. In this case, instead of calculating the square root in Noir, we are using our oracle to offload that computation to be made in plain. In our circuit we can simply multiply the two values. - -::: - -Now, we should write the correspondent RPC server, starting with the [default JSON-RPC 2.0 boilerplate](https://www.npmjs.com/package/json-rpc-2.0#example): - -```js -import { JSONRPCServer } from "json-rpc-2.0"; -import express from "express"; -import bodyParser from "body-parser"; - -const app = express(); -app.use(bodyParser.json()); - -const server = new JSONRPCServer(); -app.post("/", (req, res) => { - const jsonRPCRequest = req.body; - server.receive(jsonRPCRequest).then((jsonRPCResponse) => { - if (jsonRPCResponse) { - res.json(jsonRPCResponse); - } else { - res.sendStatus(204); - } - }); -}); - -app.listen(5555); -``` - -Now, we will add our `getSqrt` method, as expected by the `#[oracle(getSqrt)]` decorator in our Noir code. It maps through the params array and returns their square roots: - -```js -server.addMethod("resolve_foreign_call", async (params) => { - if (params[0].function !== "getSqrt") { - throw Error("Unexpected foreign call") - }; - const values = params[0].inputs[0].map((field) => { - return `${Math.sqrt(parseInt(field, 16))}`; - }); - return { values: [values] }; -}); -``` - -If you're using Typescript, the following types may be helpful in understanding the expected return value and making sure they're easy to follow: - -```js -export type ForeignCallSingle = string; - -export type ForeignCallArray = string[]; - -export type ForeignCallResult = { - values: (ForeignCallSingle | ForeignCallArray)[]; -}; -``` - -:::info Multidimensional Arrays - -If the Oracle function is returning an array containing other arrays, such as `[['1','2],['3','4']]`, you need to provide the values in JSON as flattened values. In the previous example, it would be `['1', '2', '3', '4']`. In the Noir program, the Oracle signature can use a nested type, the flattened values will be automatically converted to the nested type. - -::: - -## Step 3 - Usage with Nargo - -Using the [`nargo` CLI tool](../getting_started/installation/index.md), you can use oracles in the `nargo test` and `nargo execute` commands by passing a value to `--oracle-resolver`. For example: - -```bash -nargo test --oracle-resolver http://localhost:5555 -``` - -This tells `nargo` to use your RPC Server URL whenever it finds an oracle decorator. - -## Step 4 - Usage with NoirJS - -In a JS environment, an RPC server is not strictly necessary, as you may want to resolve your oracles without needing any JSON call at all. NoirJS simply expects that you pass a callback function when you generate proofs, and that callback function can be anything. - -For example, if your Noir program expects the host machine to provide CPU pseudo-randomness, you could simply pass it as the `foreignCallHandler`. You don't strictly need to create an RPC server to serve pseudo-randomness, as you may as well get it directly in your app: - -```js -const foreignCallHandler = (name, inputs) => crypto.randomBytes(16) // etc - -await noir.execute(inputs, foreignCallHandler) -``` - -As one can see, in NoirJS, the [`foreignCallHandler`](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) function simply means "a callback function that returns a value of type [`ForeignCallOutput`](../reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md). It doesn't have to be an RPC call like in the case for Nargo. - -:::tip - -Does this mean you don't have to write an RPC server like in [Step #2](#step-2---write-an-rpc-server)? - -You don't technically have to, but then how would you run `nargo test`? To use both `Nargo` and `NoirJS` in your development flow, you will have to write a JSON RPC server. - -::: - -In this case, let's make `foreignCallHandler` call the JSON RPC Server we created in [Step #2](#step-2---write-an-rpc-server), by making it a JSON RPC Client. - -For example, using the same `getSqrt` program in [Step #1](#step-1---modify-your-noir-program) (comments in the code): - -```js -import { JSONRPCClient } from "json-rpc-2.0"; - -// declaring the JSONRPCClient -const client = new JSONRPCClient((jsonRPCRequest) => { -// hitting the same JSON RPC Server we coded above - return fetch("http://localhost:5555", { - method: "POST", - headers: { - "content-type": "application/json", - }, - body: JSON.stringify(jsonRPCRequest), - }).then((response) => { - if (response.status === 200) { - return response - .json() - .then((jsonRPCResponse) => client.receive(jsonRPCResponse)); - } else if (jsonRPCRequest.id !== undefined) { - return Promise.reject(new Error(response.statusText)); - } - }); -}); - -// declaring a function that takes the name of the foreign call (getSqrt) and the inputs -const foreignCallHandler = async (name, input) => { - const inputs = input[0].map((i) => i.toString("hex")) - // notice that the "inputs" parameter contains *all* the inputs - // in this case we to make the RPC request with the first parameter "numbers", which would be input[0] - const oracleReturn = await client.request("resolve_foreign_call", [ - { - function: name, - inputs: [inputs] - }, - ]); - return [oracleReturn.values[0]]; -}; - -// the rest of your NoirJS code -const input = { input: [4, 16] }; -const { witness } = await noir.execute(input, foreignCallHandler); -``` - -:::tip - -If you're in a NoirJS environment running your RPC server together with a frontend app, you'll probably hit a familiar problem in full-stack development: requests being blocked by [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) policy. For development only, you can simply install and use the [`cors` npm package](https://www.npmjs.com/package/cors) to get around the problem: - -```bash -yarn add cors -``` - -and use it as a middleware: - -```js -import cors from "cors"; - -const app = express(); -app.use(cors()) -``` - -::: - -## Conclusion - -Hopefully by the end of this guide, you should be able to: - -- Write your own logic around Oracles and how to write a JSON RPC server to make them work with your Nargo commands. -- Provide custom foreign call handlers for NoirJS. diff --git a/docs/versioned_docs/version-v0.32.0/how_to/how-to-recursion.md b/docs/versioned_docs/version-v0.32.0/how_to/how-to-recursion.md deleted file mode 100644 index c8c4dc9f5b4..00000000000 --- a/docs/versioned_docs/version-v0.32.0/how_to/how-to-recursion.md +++ /dev/null @@ -1,180 +0,0 @@ ---- -title: How to use recursion on NoirJS -description: Learn how to implement recursion with NoirJS, a powerful tool for creating smart contracts on the EVM blockchain. This guide assumes familiarity with NoirJS, solidity verifiers, and the Barretenberg proving backend. Discover how to generate both final and intermediate proofs using `noir_js` and `backend_barretenberg`. -keywords: - [ - "NoirJS", - "EVM blockchain", - "smart contracts", - "recursion", - "solidity verifiers", - "Barretenberg backend", - "noir_js", - "backend_barretenberg", - "intermediate proofs", - "final proofs", - "nargo compile", - "json import", - "recursive circuit", - "recursive app" - ] -sidebar_position: 1 ---- - -This guide shows you how to use recursive proofs in your NoirJS app. For the sake of clarity, it is assumed that: - -- You already have a NoirJS app. If you don't, please visit the [NoirJS tutorial](../tutorials/noirjs_app.md) and the [reference](../reference/NoirJS/noir_js/index.md). -- You are familiar with what are recursive proofs and you have read the [recursion explainer](../explainers/explainer-recursion.md) -- You already built a recursive circuit following [the reference](../noir/standard_library/recursion.mdx), and understand how it works. - -It is also assumed that you're not using `noir_wasm` for compilation, and instead you've used [`nargo compile`](../reference/nargo_commands.md) to generate the `json` you're now importing into your project. However, the guide should work just the same if you're using `noir_wasm`. - -:::info - -As you've read in the [explainer](../explainers/explainer-recursion.md), a recursive proof is an intermediate proof. This means that it doesn't necessarily generate the final step that makes it verifiable in a smart contract. However, it is easy to verify within another circuit. - -While "standard" usage of NoirJS packages abstracts final proofs, it currently lacks the necessary interface to abstract away intermediate proofs. This means that these proofs need to be created by using the backend directly. - -In short: - -- `noir_js` generates *only* final proofs -- `backend_barretenberg` generates both types of proofs - -::: - -In a standard recursive app, you're also dealing with at least two circuits. For the purpose of this guide, we will assume the following: - -- `main`: a circuit of type `assert(x != y)`, where `main` is marked with a `#[recursive]` attribute. This attribute states that the backend should generate proofs that are friendly for verification within another circuit. -- `recursive`: a circuit that verifies `main` - -For a full example of how recursive proofs work, please refer to the [noir-examples](https://github.com/noir-lang/noir-examples) repository. We will *not* be using it as a reference for this guide. - -## Step 1: Setup - -In a common NoirJS app, you need to instantiate a backend with something like `const backend = new Backend(circuit)`. Then you feed it to the `noir_js` interface. - -For recursion, this doesn't happen, and the only need for `noir_js` is only to `execute` a circuit and get its witness and return value. Everything else is not interfaced, so it needs to happen on the `backend` object. - -It is also recommended that you instantiate the backend with as many threads as possible, to allow for maximum concurrency: - -```js -const backend = new Backend(circuit, { threads: 8 }) -``` - -:::tip -You can use the [`os.cpus()`](https://nodejs.org/api/os.html#oscpus) object in `nodejs` or [`navigator.hardwareConcurrency`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/hardwareConcurrency) on the browser to make the most out of those glorious cpu cores -::: - -## Step 2: Generating the witness and the proof for `main` - -After instantiating the backend, you should also instantiate `noir_js`. We will use it to execute the circuit and get the witness. - -```js -const noir = new Noir(circuit) -const { witness } = noir.execute(input) -``` - -With this witness, you are now able to generate the intermediate proof for the main circuit: - -```js -const { proof, publicInputs } = await backend.generateProof(witness) -``` - -:::warning - -Always keep in mind what is actually happening on your development process, otherwise you'll quickly become confused about what circuit we are actually running and why! - -In this case, you can imagine that Alice (running the `main` circuit) is proving something to Bob (running the `recursive` circuit), and Bob is verifying her proof within his proof. - -With this in mind, it becomes clear that our intermediate proof is the one *meant to be verified within another circuit*, so it must be Alice's. Actually, the only final proof in this theoretical scenario would be the last one, sent on-chain. - -::: - -## Step 3 - Verification and proof artifacts - -Optionally, you are able to verify the intermediate proof: - -```js -const verified = await backend.verifyProof({ proof, publicInputs }) -``` - -This can be useful to make sure our intermediate proof was correctly generated. But the real goal is to do it within another circuit. For that, we need to generate recursive proof artifacts that will be passed to the circuit that is verifying the proof we just generated. Instead of passing the proof and verification key as a byte array, we pass them as fields which makes it cheaper to verify in a circuit: - -```js -const { proofAsFields, vkAsFields, vkHash } = await backend.generateRecursiveProofArtifacts( { publicInputs, proof }, publicInputsCount) -``` - -This call takes the public inputs and the proof, but also the public inputs count. While this is easily retrievable by simply counting the `publicInputs` length, the backend interface doesn't currently abstract it away. - -:::info - -The `proofAsFields` has a constant size `[Field; 93]` and verification keys in Barretenberg are always `[Field; 114]`. - -::: - -:::warning - -One common mistake is to forget *who* makes this call. - -In a situation where Alice is generating the `main` proof, if she generates the proof artifacts and sends them to Bob, which gladly takes them as true, this would mean Alice could prove anything! - -Instead, Bob needs to make sure *he* extracts the proof artifacts, using his own instance of the `main` circuit backend. This way, Alice has to provide a valid proof for the correct `main` circuit. - -::: - -## Step 4 - Recursive proof generation - -With the artifacts, generating a recursive proof is no different from a normal proof. You simply use the `backend` (with the recursive circuit) to generate it: - -```js -const recursiveInputs = { - verification_key: vkAsFields, // array of length 114 - proof: proofAsFields, // array of length 93 + size of public inputs - publicInputs: [mainInput.y], // using the example above, where `y` is the only public input - key_hash: vkHash, -} - -const { witness, returnValue } = noir.execute(recursiveInputs) // we're executing the recursive circuit now! -const { proof, publicInputs } = backend.generateProof(witness) -const verified = backend.verifyProof({ proof, publicInputs }) -``` - -You can obviously chain this proof into another proof. In fact, if you're using recursive proofs, you're probably interested of using them this way! - -:::tip - -Managing circuits and "who does what" can be confusing. To make sure your naming is consistent, you can keep them in an object. For example: - -```js -const circuits = { - main: mainJSON, - recursive: recursiveJSON -} -const backends = { - main: new BarretenbergBackend(circuits.main), - recursive: new BarretenbergBackend(circuits.recursive) -} -const noir_programs = { - main: new Noir(circuits.main), - recursive: new Noir(circuits.recursive) -} -``` - -This allows you to neatly call exactly the method you want without conflicting names: - -```js -// Alice runs this 👇 -const { witness: mainWitness } = await noir_programs.main.execute(input) -const proof = await backends.main.generateProof(mainWitness) - -// Bob runs this 👇 -const verified = await backends.main.verifyProof(proof) -const { proofAsFields, vkAsFields, vkHash } = await backends.main.generateRecursiveProofArtifacts( - proof, - numPublicInputs, -); -const { witness: recursiveWitness } = await noir_programs.recursive.execute(recursiveInputs) -const recursiveProof = await backends.recursive.generateProof(recursiveWitness); -``` - -::: diff --git a/docs/versioned_docs/version-v0.32.0/how_to/how-to-solidity-verifier.md b/docs/versioned_docs/version-v0.32.0/how_to/how-to-solidity-verifier.md deleted file mode 100644 index a8169595b3d..00000000000 --- a/docs/versioned_docs/version-v0.32.0/how_to/how-to-solidity-verifier.md +++ /dev/null @@ -1,259 +0,0 @@ ---- -title: Generate a 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, - ] -sidebar_position: 0 -pagination_next: tutorials/noirjs_app ---- - -Noir has the ability to generate a verifier contract in Solidity, which can be deployed in many EVM-compatible blockchains such as Ethereum. - -This allows for a powerful feature set, as one can make use of the conciseness and the privacy provided by Noir in an immutable ledger. Applications can range from simple P2P guessing games, to complex private DeFi interactions. - -This guide shows you how to generate a Solidity Verifier and deploy it on the [Remix IDE](https://remix.ethereum.org/). It is assumed that: - -- You are comfortable with the Solidity programming language and understand how contracts are deployed on the Ethereum network -- You have Noir installed and you have a Noir program. If you don't, [get started](../getting_started/installation/index.md) with Nargo and the example Hello Noir circuit -- You are comfortable navigating RemixIDE. If you aren't or you need a refresher, you can find some video tutorials [here](https://www.youtube.com/channel/UCjTUPyFEr2xDGN6Cg8nKDaA) that could help you. - -## Rundown - -Generating a Solidity Verifier contract is actually a one-command process. However, compiling it and deploying it can have some caveats. Here's the rundown of this guide: - -1. How to generate a solidity smart contract -2. How to compile the smart contract in the RemixIDE -3. How to deploy it to a testnet - -## Step 1 - Generate a contract - -This is by far the most straightforward step. Just run: - -```sh -nargo compile -``` - -This will compile your source code into a Noir build artifact to be stored in the `./target` directory, you can then generate the smart contract using the commands: - -```sh -# Here we pass the path to the newly generated Noir artifact. -bb write_vk -b ./target/.json -bb contract -``` - -replacing `` with the name of your Noir project. A new `contract` folder would then be generated in your project directory, containing the Solidity -file `contract.sol`. It can be deployed to any EVM blockchain acting as a verifier smart contract. - -You can find more information about `bb` and the default Noir proving backend on [this page](../getting_started/hello_noir/index.md#proving-backend). - -:::info - -It is possible to generate 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 generation of verifier contracts, for the time being these are only in Solidity. -::: - -## Step 2 - Compiling - -We will mostly skip the details of RemixIDE, as the UI can change from version to version. For now, we can just open -Remix and create a blank workspace. - -![Create Workspace](@site/static/img/how-tos/solidity_verifier_1.png) - -We will create a new file to contain the contract Nargo generated, and copy-paste its content. - -:::warning - -You'll likely see a warning advising you to not trust pasted code. While it is an important warning, it is irrelevant in the context of this guide and can be ignored. We will not be deploying anywhere near a mainnet. - -::: - -To compile our the verifier, we can navigate to the compilation tab: - -![Compilation Tab](@site/static/img/how-tos/solidity_verifier_2.png) - -Remix should automatically match a suitable compiler version. However, hitting the "Compile" button will most likely generate a "Stack too deep" error: - -![Stack too deep](@site/static/img/how-tos/solidity_verifier_3.png) - -This is due to the verify function needing to put many variables on the stack, but enabling the optimizer resolves the issue. To do this, let's open the "Advanced Configurations" tab and enable optimization. The default 200 runs will suffice. - -:::info - -This time we will see a warning about an unused function parameter. This is expected, as the `verify` function doesn't use the `_proof` parameter inside a solidity block, it is loaded from calldata and used in assembly. - -::: - -![Compilation success](@site/static/img/how-tos/solidity_verifier_4.png) - -## Step 3 - Deploying - -At this point we should have a compiled contract ready to deploy. If we navigate to the deploy section in Remix, we will see many different environments we can deploy to. The steps to deploy on each environment would be out-of-scope for this guide, so we will just use the default Remix VM. - -Looking closely, we will notice that our "Solidity Verifier" is actually three contracts working together: - -- An `UltraVerificationKey` library which simply stores the verification key for our circuit. -- An abstract contract `BaseUltraVerifier` containing most of the verifying logic. -- A main `UltraVerifier` contract that inherits from the Base and uses the Key contract. - -Remix will take care of the dependencies for us so we can simply deploy the UltraVerifier contract by selecting it and hitting "deploy": - -![Deploying UltraVerifier](@site/static/img/how-tos/solidity_verifier_5.png) - -A contract will show up in the "Deployed Contracts" section, where we can retrieve the Verification Key Hash. This is particularly useful for double-checking that the deployer contract is the correct one. - -:::note - -Why "UltraVerifier"? - -To be precise, the Noir compiler (`nargo`) doesn't generate the verifier contract directly. It compiles the Noir code into an intermediate language (ACIR), which is then executed by the backend. So it is the backend that returns the verifier smart contract, not Noir. - -In this case, the Barretenberg Backend uses the UltraPlonk proving system, hence the "UltraVerifier" name. - -::: - -## Step 4 - Verifying - -To verify a proof using the Solidity verifier contract, we call the `verify` function in this extended contract: - -```solidity -function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external view returns (bool) -``` - -When using the default example in the [Hello Noir](../getting_started/hello_noir/index.md) guide, the easiest way to confirm that the verifier contract is doing its job is by calling the `verify` function via remix with the required parameters. Note that the public inputs must be passed in separately to the rest of the proof so we must split the proof as returned from `bb`. - -First generate a proof with `bb` at the location `./proof` using the steps in [get started](../getting_started/hello_noir/index.md), this proof is in a binary format but we want to convert it into a hex string to pass into Remix, this can be done with the - -```bash -# This value must be changed to match the number of public inputs (including return values!) in your program. -NUM_PUBLIC_INPUTS=1 -PUBLIC_INPUT_BYTES=32*NUM_PUBLIC_INPUTS -HEX_PUBLIC_INPUTS=$(head -c $PUBLIC_INPUT_BYTES ./proof | od -An -v -t x1 | tr -d $' \n') -HEX_PROOF=$(tail -c +$(($PUBLIC_INPUT_BYTES + 1)) ./proof | od -An -v -t x1 | tr -d $' \n') - -echo "Public inputs:" -echo $HEX_PUBLIC_INPUTS - -echo "Proof:" -echo "0x$HEX_PROOF" -``` - -Remix expects that the public inputs will be split into an array of `bytes32` values so `HEX_PUBLIC_INPUTS` needs to be split up into 32 byte chunks which are prefixed with `0x` accordingly. - -A programmatic example of how the `verify` function is called can be seen 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"); -``` - -:::info[Return Values] - -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. - -For example, if you have Noir program like this: - -```rust -fn main( - // Public inputs - pubkey_x: pub Field, - pubkey_y: pub Field, - // Private inputs - priv_key: Field, -) -> pub Field -``` - -the `verify` function will expect the public inputs array (second function parameter) to be of length 3, the two inputs and the return value. - -Passing only two inputs will result in an error such as `PUBLIC_INPUT_COUNT_INVALID(3, 2)`. - -In this case, the inputs parameter to `verify` would be an array ordered as `[pubkey_x, pubkey_y, return`. - -::: - -:::tip[Structs] - -You can pass structs to the verifier contract. They will be flattened so that the array of inputs is 1-dimensional array. - -For example, 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) { - //... -} -``` - -The order of these inputs would be flattened to: `[x, nested.t1.val1, nested.t1.val2, nested.is_true, y]` - -::: - -The other function you can call is our entrypoint `verify` function, as defined above. - -:::tip - -It's worth noticing that the `verify` function is actually a `view` function. A `view` function does not alter the blockchain state, so it doesn't need to be distributed (i.e. it will run only on the executing node), and therefore doesn't cost any gas. - -This can be particularly useful in some situations. If Alice generated a proof and wants Bob to verify its correctness, Bob doesn't need to run Nargo, NoirJS, or any Noir specific infrastructure. He can simply make a call to the blockchain with the proof and verify it is correct without paying any gas. - -It would be incorrect to say that a Noir proof verification costs any gas at all. However, most of the time the result of `verify` is used to modify state (for example, to update a balance, a game state, etc). In that case the whole network needs to execute it, which does incur gas costs (calldata and execution, but not storage). - -::: - -## A Note on EVM chains - -Noir proof verification requires the ecMul, ecAdd and ecPairing precompiles. Not all EVM chains support EC Pairings, notably some of the ZK-EVMs. This means that you won't be able to use the verifier contract in all of them. You can find an incomplete list of which EVM chains support these precompiles [here](https://www.evmdiff.com/features?feature=precompiles). - -For example, chains like `zkSync ERA` and `Polygon zkEVM` do not currently support these precompiles, so proof verification via Solidity verifier contracts won't work. Here's a quick list of EVM chains that have been tested and are known to work: - -- Optimism -- Arbitrum -- Polygon PoS -- Scroll -- Celo -- BSC -- Blast L2 -- Avalanche C-Chain -- Mode -- Linea -- Moonbeam - -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. - -## What's next - -Now that you know how to call a Noir Solidity Verifier on a smart contract using Remix, you should be comfortable with using it with some programmatic frameworks, such as [hardhat](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat) and [foundry](https://github.com/noir-lang/noir-starter/tree/main/with-foundry). - -You can find other tools, examples, boilerplates and libraries in the [awesome-noir](https://github.com/noir-lang/awesome-noir) repository. - -You should also be ready to write and deploy your first NoirJS app and start generating proofs on websites, phones, and NodeJS environments! Head on to the [NoirJS tutorial](../tutorials/noirjs_app.md) to learn how to do that. diff --git a/docs/versioned_docs/version-v0.32.0/how_to/merkle-proof.mdx b/docs/versioned_docs/version-v0.32.0/how_to/merkle-proof.mdx deleted file mode 100644 index 0a128adb2de..00000000000 --- a/docs/versioned_docs/version-v0.32.0/how_to/merkle-proof.mdx +++ /dev/null @@ -1,48 +0,0 @@ ---- -title: Prove Merkle Tree 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] -sidebar_position: 4 ---- - -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 - -fn main(message : [Field; 62], index : Field, hashpath : [Field; 40], root : Field) { - let leaf = std::hash::hash_to_field(message.as_slice()); - 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.as_slice()); -``` - -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.32.0/how_to/using-devcontainers.mdx b/docs/versioned_docs/version-v0.32.0/how_to/using-devcontainers.mdx deleted file mode 100644 index 727ec6ca667..00000000000 --- a/docs/versioned_docs/version-v0.32.0/how_to/using-devcontainers.mdx +++ /dev/null @@ -1,110 +0,0 @@ ---- -title: Developer Containers and Codespaces -description: "Learn how to set up a devcontainer in your GitHub repository for a seamless coding experience with Codespaces. Follow our easy 8-step guide to create your own Noir environment without installing Nargo locally." -keywords: ["Devcontainer", "Codespaces", "GitHub", "Noir Environment", "Docker Image", "Development Environment", "Remote Coding", "GitHub Codespaces", "Noir Programming", "Nargo", "VSCode Extensions", "Noirup"] -sidebar_position: 1 ---- - -Adding a developer container configuration file to your Noir project is one of the easiest way to unlock coding in browser. - -## What's a devcontainer after all? - -A [Developer Container](https://containers.dev/) (devcontainer for short) is a Docker image that comes preloaded with tools, extensions, and other tools you need to quickly get started or continue a project, without having to install Nargo locally. Think of it as a development environment in a box. - -There are many advantages to this: - -- It's platform and architecture agnostic -- You don't need to have an IDE installed, or Nargo, or use a terminal at all -- It's safer for using on a public machine or public network - -One of the best ways of using devcontainers is... not using your machine at all, for maximum control, performance, and ease of use. -Enter Codespaces. - -## Codespaces - -If a devcontainer is just a Docker image, then what stops you from provisioning a `p3dn.24xlarge` AWS EC2 instance with 92 vCPUs and 768 GiB RAM and using it to prove your 10-gate SNARK proof? - -Nothing! Except perhaps the 30-40$ per hour it will cost you. - -The problem is that provisioning takes time, and I bet you don't want to see the AWS console every time you want to code something real quick. - -Fortunately, there's an easy and free way to get a decent remote machine ready and loaded in less than 2 minutes: Codespaces. [Codespaces is a Github feature](https://github.com/features/codespaces) that allows you to code in a remote machine by using devcontainers, and it's pretty cool: - -- You can start coding Noir in less than a minute -- It uses the resources of a remote machine, so you can code on your grandma's phone if needed be -- It makes it easy to share work with your frens -- It's fully reusable, you can stop and restart whenever you need to - -:::info - -Don't take out your wallet just yet. Free GitHub accounts get about [15-60 hours of coding](https://github.com/features/codespaces) for free per month, depending on the size of your provisioned machine. - -::: - -## Tell me it's _actually_ easy - -It is! - -Github comes with a default codespace and you can use it to code your own devcontainer. That's exactly what we will be doing in this guide. - - - -8 simple steps: - -#### 1. Create a new repository on GitHub. - -#### 2. Click "Start coding with Codespaces". This will use the default image. - -#### 3. Create a folder called `.devcontainer` in the root of your repository. - -#### 4. Create a Dockerfile in that folder, and paste the following code: - -```docker -FROM --platform=linux/amd64 node:lts-bookworm-slim -SHELL ["/bin/bash", "-c"] -RUN apt update && apt install -y curl bash git tar gzip libc++-dev -RUN curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash -ENV PATH="/root/.nargo/bin:$PATH" -RUN noirup -ENTRYPOINT ["nargo"] -``` -#### 5. Create a file called `devcontainer.json` in the same folder, and paste the following code: - -```json -{ - "name": "Noir on Codespaces", - "build": { - "context": ".", - "dockerfile": "Dockerfile" - }, - "customizations": { - "vscode": { - "extensions": ["noir-lang.vscode-noir"] - } - } -} -``` -#### 6. Commit and push your changes - -This will pull the new image and build it, so it could take a minute or so - -#### 8. Done! -Just wait for the build to finish, and there's your easy Noir environment. - - -Refer to [noir-starter](https://github.com/noir-lang/noir-starter/) as an example of how devcontainers can be used together with codespaces. - - - -## How do I use it? - -Using the codespace is obviously much easier than setting it up. -Just navigate to your repository and click "Code" -> "Open with Codespaces". It should take a few seconds to load, and you're ready to go. - -:::info - -If you really like the experience, you can add a badge to your readme, links to existing codespaces, and more. -Check out the [official docs](https://docs.github.com/en/codespaces/setting-up-your-project-for-codespaces/setting-up-your-repository/facilitating-quick-creation-and-resumption-of-codespaces) for more info. diff --git a/docs/versioned_docs/version-v0.32.0/index.mdx b/docs/versioned_docs/version-v0.32.0/index.mdx deleted file mode 100644 index a6bd306f91d..00000000000 --- a/docs/versioned_docs/version-v0.32.0/index.mdx +++ /dev/null @@ -1,67 +0,0 @@ ---- -title: Noir Lang -hide_title: true -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] -sidebar_position: 0 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -Noir Logo - -Noir is an open-source Domain-Specific Language for safe and seamless construction of privacy-preserving Zero-Knowledge programs, requiring no previous knowledge on the underlying mathematics or cryptography. - -ZK programs are programs that can generate short proofs of statements without revealing all inputs to the statements. You can read more about Zero-Knowledge Proofs [here](https://dev.to/spalladino/a-beginners-intro-to-coding-zero-knowledge-proofs-c56). - -## What's new about Noir? - -Noir works differently from most ZK languages by taking a two-pronged path. First, it compiles the program to an adaptable intermediate language known as ACIR. From there, depending on a given project's needs, ACIR can be further compiled into an arithmetic circuit for integration with the proving backend. - -:::info - -Noir is backend agnostic, which means it makes no assumptions on which proving backend powers the ZK proof. Being the language that powers [Aztec Contracts](https://docs.aztec.network/developers/contracts/main), it defaults to Aztec's Barretenberg proving backend. - -However, the ACIR output can be transformed to be compatible with other PLONK-based backends, or into a [rank-1 constraint system](https://www.rareskills.io/post/rank-1-constraint-system) suitable for backends such as Arkwork's Marlin. - -::: - -## Who is Noir for? - -Noir can be used both in complex cloud-based backends and in user's smartphones, requiring no knowledge on the underlying math or cryptography. From authorization systems that keep a password in the user's device, to complex on-chain verification of recursive proofs, Noir is designed to abstract away complexity without any significant overhead. Here are some examples of situations where Noir can be used: - - - - Noir Logo - - Aztec Contracts leverage Noir to allow for the storage and execution of private information. Writing an Aztec Contract is as easy as writing Noir, and Aztec developers can easily interact with the network storage and execution through the [Aztec.nr](https://docs.aztec.network/developers/contracts/main) library. - - - Soliditry Verifier Example - Noir can auto-generate Solidity verifier contracts that verify Noir proofs. This allows for non-interactive verification of proofs containing private information in an immutable system. This feature powers a multitude of use-case scenarios, from P2P chess tournaments, to [Aztec Layer-2 Blockchain](https://docs.aztec.network/) - - - Aztec Labs developed NoirJS, an easy interface to generate and verify Noir proofs in a Javascript environment. This allows for Noir to be used in webpages, mobile apps, games, and any other environment supporting JS execution in a standalone manner. - - - - -## Libraries - -Noir is meant to be easy to extend by simply importing Noir libraries just like in Rust. -The [awesome-noir repo](https://github.com/noir-lang/awesome-noir#libraries) is a collection of libraries developed by the Noir community. -Writing a new library is easy and makes code be composable and easy to reuse. See the section on [dependencies](noir/modules_packages_crates/dependencies.md) for more information. diff --git a/docs/versioned_docs/version-v0.32.0/migration_notes.md b/docs/versioned_docs/version-v0.32.0/migration_notes.md deleted file mode 100644 index 6bd740024e5..00000000000 --- a/docs/versioned_docs/version-v0.32.0/migration_notes.md +++ /dev/null @@ -1,105 +0,0 @@ ---- -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. - -### `backend encountered an error: libc++.so.1` - -Depending on your OS, you may encounter the following error when running `nargo prove` for the first time: - -```text -The backend encountered an error: "/home/codespace/.nargo/backends/acvm-backend-barretenberg/backend_binary: error while loading shared libraries: libc++.so.1: cannot open shared object file: No such file or directory\n" -``` - -Install the `libc++-dev` library with: - -```bash -sudo apt install libc++-dev -``` - -## ≥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](noir/concepts/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 your 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 file is running [this bash script](https://github.com/noir-lang/barretenberg-js-binary/blob/master/run-bb-js.sh), 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.32.0/noir/concepts/_category_.json b/docs/versioned_docs/version-v0.32.0/noir/concepts/_category_.json deleted file mode 100644 index 7da08f8a8c5..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/concepts/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "Concepts", - "position": 0, - "collapsible": true, - "collapsed": true -} \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.32.0/noir/concepts/assert.md b/docs/versioned_docs/version-v0.32.0/noir/concepts/assert.md deleted file mode 100644 index 2132de42072..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/concepts/assert.md +++ /dev/null @@ -1,78 +0,0 @@ ---- -title: Assert Function -description: - Learn about the `assert` and `static_assert` functions 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 or compile-time, respectively. -keywords: [Noir programming language, assert statement, predicate expression, comparison expression] -sidebar_position: 4 ---- - -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); -} -``` - -> 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`. - -You can optionally provide a message to be logged when the assertion fails: - -```rust -assert(x == y, "x and y are not equal"); -``` - -Aside string literals, the optional message can be a format string or any other type supported as input for Noir's [print](../standard_library/logging.md) functions. This feature lets you incorporate runtime variables into your failed assertion logs: - -```rust -assert(x == y, f"Expected x == y, but got {x} == {y}"); -``` - -Using a variable as an assertion message directly: - -```rust -struct myStruct { - myField: Field -} - -let s = myStruct { myField: y }; -assert(s.myField == x, s); -``` - -There is also a special `static_assert` function that behaves like `assert`, -but that runs at compile-time. - -```rust -fn main(xs: [Field; 3]) { - let x = 2 + 2; - let y = 4; - static_assert(x == y, "expected 2 + 2 to equal 4"); - - // This passes since the length of `xs` is known at compile-time - static_assert(xs.len() == 3, "expected the input to have 3 elements"); -} -``` - -This function fails when passed a dynamic (run-time) argument: - -```rust -fn main(x : Field, y : Field) { - // this fails because `x` is not known at compile-time - static_assert(x == 2, "expected x to be known at compile-time and equal to 2"); - - let mut example_slice = &[]; - if y == 4 { - example_slice = example_slice.push_back(0); - } - - // This fails because the length of `example_slice` is not known at - // compile-time - let error_message = "expected an empty slice, known at compile-time"; - static_assert(example_slice.len() == 0, error_message); -} -``` - diff --git a/docs/versioned_docs/version-v0.32.0/noir/concepts/comments.md b/docs/versioned_docs/version-v0.32.0/noir/concepts/comments.md deleted file mode 100644 index b51a85f5c94..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/concepts/comments.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -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] -sidebar_position: 10 ---- - -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.32.0/noir/concepts/control_flow.md b/docs/versioned_docs/version-v0.32.0/noir/concepts/control_flow.md deleted file mode 100644 index 045d3c3a5f5..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/concepts/control_flow.md +++ /dev/null @@ -1,77 +0,0 @@ ---- -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] -sidebar_position: 2 ---- - -## 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); -``` - -## 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`. - -### Break and Continue - -In unconstrained code, `break` and `continue` are also allowed in `for` loops. These are only allowed -in unconstrained code since normal constrained code requires that Noir knows exactly how many iterations -a loop may have. `break` and `continue` can be used like so: - -```rust -for i in 0 .. 10 { - println("Iteration start") - - if i == 2 { - continue; - } - - if i == 5 { - break; - } - - println(i); -} -println("Loop end") -``` - -When used, `break` will end the current loop early and jump to the statement after the for loop. In the example -above, the `break` will stop the loop and jump to the `println("Loop end")`. - -`continue` will stop the current iteration of the loop, and jump to the start of the next iteration. In the example -above, `continue` will jump to `println("Iteration start")` when used. Note that the loop continues as normal after this. -The iteration variable `i` is still increased by one as normal when `continue` is used. - -`break` and `continue` cannot currently be used to jump out of more than a single loop at a time. diff --git a/docs/versioned_docs/version-v0.32.0/noir/concepts/data_bus.mdx b/docs/versioned_docs/version-v0.32.0/noir/concepts/data_bus.mdx deleted file mode 100644 index e55e58622ce..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/concepts/data_bus.mdx +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: Data Bus -sidebar_position: 13 ---- -import Experimental from '@site/src/components/Notes/_experimental.mdx'; - - - -The data bus is an optimization that the backend can use to make recursion more efficient. -In order to use it, you must define some inputs of the program entry points (usually the `main()` -function) with the `call_data` modifier, and the return values with the `return_data` modifier. -These modifiers are incompatible with `pub` and `mut` modifiers. - -## Example - -```rust -fn main(mut x: u32, y: call_data u32, z: call_data [u32;4] ) -> return_data u32 { - let a = z[x]; - a+y -} -``` - -As a result, both call_data and return_data will be treated as private inputs and encapsulated into a read-only array each, for the backend to process. diff --git a/docs/versioned_docs/version-v0.32.0/noir/concepts/data_types/_category_.json b/docs/versioned_docs/version-v0.32.0/noir/concepts/data_types/_category_.json deleted file mode 100644 index 5d694210bbf..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/concepts/data_types/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "position": 0, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.32.0/noir/concepts/data_types/arrays.md b/docs/versioned_docs/version-v0.32.0/noir/concepts/data_types/arrays.md deleted file mode 100644 index d26f6dff070..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/concepts/data_types/arrays.md +++ /dev/null @@ -1,253 +0,0 @@ ---- -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, - ] -sidebar_position: 4 ---- - -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.mdx), 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]; -``` - -However, multidimensional slices are not supported. For example, the following code will error at compile time: - -```rust -let slice : [[Field]] = &[]; -``` - -## 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. -Each of these functions are located within the generic impl `impl [T; N] {`. -So anywhere `self` appears, it refers to the variable `self: [T; N]`. - -### len - -Returns the length of an array - -```rust -fn len(self) -> 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(self) -> [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(self, 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(self, 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(self, 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 the starting element. - -```rust -fn reduce(self, 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(self, 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(self, 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.32.0/noir/concepts/data_types/booleans.md b/docs/versioned_docs/version-v0.32.0/noir/concepts/data_types/booleans.md deleted file mode 100644 index 2507af710e7..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/concepts/data_types/booleans.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -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, - ] -sidebar_position: 2 ---- - - -The `bool` type in Noir has two possible values: `true` and `false`: - -```rust -fn main() { - let t = true; - let f: bool = false; -} -``` - -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.md) and -[Assert Function](../assert.md) sections. diff --git a/docs/versioned_docs/version-v0.32.0/noir/concepts/data_types/fields.md b/docs/versioned_docs/version-v0.32.0/noir/concepts/data_types/fields.md deleted file mode 100644 index a10a4810788..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/concepts/data_types/fields.md +++ /dev/null @@ -1,192 +0,0 @@ ---- -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, - ] -sidebar_position: 0 ---- - -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] -``` - -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] -``` - -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); -} -``` - -### assert_max_bit_size - -Adds a constraint to specify that the field can be represented with `bit_size` number of bits - -```rust -fn assert_max_bit_size(self, bit_size: u32) -``` - -example: - -```rust -fn main() { - let field = 2 - field.assert_max_bit_size(32); -} -``` - -### 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 -``` - - -### lt - -Returns true if the field is less than the other field - -```rust -pub fn lt(self, another: Field) -> bool -``` diff --git a/docs/versioned_docs/version-v0.32.0/noir/concepts/data_types/function_types.md b/docs/versioned_docs/version-v0.32.0/noir/concepts/data_types/function_types.md deleted file mode 100644 index f6121af17e2..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/concepts/data_types/function_types.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: Function types -sidebar_position: 10 ---- - -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](../lambdas.md) for more details. diff --git a/docs/versioned_docs/version-v0.32.0/noir/concepts/data_types/index.md b/docs/versioned_docs/version-v0.32.0/noir/concepts/data_types/index.md deleted file mode 100644 index 3eadb2dc8a4..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/concepts/data_types/index.md +++ /dev/null @@ -1,118 +0,0 @@ ---- -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](../generics.md): - -```rust -type Id = Size; - -fn main() { - let id: Id = 1; - let zero: u32 = 0; - assert(zero + 1 == id); -} -``` - -Type aliases can even refer to other aliases. An error will be issued if they form a cycle: - -```rust -// Ok! -type A = B; -type B = Field; - -type Bad1 = Bad2; - -// error: Dependency cycle found -type Bad2 = Bad1; -// ^^^^^^^^^^^ 'Bad2' recursively depends on itself: Bad2 -> Bad1 -> Bad2 -``` - -## Wildcard Type -Noir can usually infer the type of the variable from the context, so specifying the type of a variable is only required when it cannot be inferred. However, specifying a complex type can be tedious, especially when it has multiple generic arguments. Often some of the generic types can be inferred from the context, and Noir only needs a hint to properly infer the other types. We can partially specify a variable's type by using `_` as a marker, indicating where we still want the compiler to infer the type. - -```rust -let a: [_; 4] = foo(b); -``` - - -### BigInt - -You can achieve BigInt functionality using the [Noir BigInt](https://github.com/shuklaayush/noir-bigint) library. diff --git a/docs/versioned_docs/version-v0.32.0/noir/concepts/data_types/integers.md b/docs/versioned_docs/version-v0.32.0/noir/concepts/data_types/integers.md deleted file mode 100644 index a1d59bf3166..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/concepts/data_types/integers.md +++ /dev/null @@ -1,156 +0,0 @@ ---- -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] -sidebar_position: 1 ---- - -An integer type is a range constrained field type. -The Noir frontend supports both unsigned and signed integer types. -The allowed sizes are 1, 8, 16, 32 and 64 bits. - -:::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\\$). - -## 128 bits Unsigned Integers - -The built-in structure `U128` allows you to use 128-bit unsigned integers almost like a native integer type. However, there are some differences to keep in mind: -- You cannot cast between a native integer and `U128` -- There is a higher performance cost when using `U128`, compared to a native type. - -Conversion between unsigned integer types and U128 are done through the use of `from_integer` and `to_integer` functions. `from_integer` also accepts the `Field` type as input. - -```rust -fn main() { - let x = U128::from_integer(23); - let y = U128::from_hex("0x7"); - let z = x + y; - assert(z.to_integer() == 30); -} -``` - -`U128` is implemented with two 64 bits limbs, representing the low and high bits, which explains the performance cost. You should expect `U128` to be twice more costly for addition and four times more costly for multiplication. -You can construct a U128 from its limbs: -```rust -fn main(x: u64, y: u64) { - let x = U128::from_u64s_be(x,y); - assert(z.hi == x as Field); - assert(z.lo == y as Field); -} -``` - -Note that the limbs are stored as Field elements in order to avoid unnecessary conversions. -Apart from this, most operations will work as usual: - -```rust -fn main(x: U128, y: U128) { - // multiplication - let c = x * y; - // addition and subtraction - let c = c - x + y; - // division - let c = x / y; - // bit operation; - let c = x & y | y; - // bit shift - let c = x << y; - // comparisons; - let c = x < y; - let c = x == y; -} -``` - -## 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 execute -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 - -fn main(x: u8, y: u8) -> pub u8 { - std::wrapping_add(x, y) -} -``` diff --git a/docs/versioned_docs/version-v0.32.0/noir/concepts/data_types/references.md b/docs/versioned_docs/version-v0.32.0/noir/concepts/data_types/references.md deleted file mode 100644 index a5293d11cfb..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/concepts/data_types/references.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: References -sidebar_position: 9 ---- - -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.32.0/noir/concepts/data_types/slices.mdx b/docs/versioned_docs/version-v0.32.0/noir/concepts/data_types/slices.mdx deleted file mode 100644 index 95da2030843..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/concepts/data_types/slices.mdx +++ /dev/null @@ -1,358 +0,0 @@ ---- -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] -sidebar_position: 5 ---- - -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 -fn main() -> pub u32 { - let mut slice: [Field] = &[0; 2]; - - let mut new_slice = slice.push_back(6); - new_slice.len() -} -``` - -To write a slice literal, use a preceeding ampersand as in: `&[0; 2]` or -`&[1, 2, 3]`. - -It is important to note that slices are not references to arrays. In Noir, -`&[..]` is more similar to an immutable, growable vector. - -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); -``` - -### len - -Returns the length of a slice - -```rust -fn len(self) -> Field -``` - -Example: - -```rust -fn main() { - let slice = &[42, 42]; - assert(slice.len() == 2); -} -``` - -### as_array - -Converts this slice into an array. - -Make sure to specify the size of the resulting array. -Panics if the resulting array length is different than the slice's length. - -```rust -fn as_array(self) -> [T; N] -``` - -Example: - -```rust -fn main() { - let slice = &[5, 6]; - - // Always specify the length of the resulting array! - let array: [Field; 2] = slice.as_array(); - - assert(array[0] == slice[0]); - assert(array[1] == slice[1]); -} -``` - -### map - -Applies a function to each element of the slice, returning a new slice containing the mapped elements. - -```rust -fn map(self, f: fn[Env](T) -> U) -> [U] -``` - -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 slice, returning the final accumulated value. The first -parameter is the initial value. - -```rust -fn fold(self, mut accumulator: U, f: fn[Env](U, T) -> U) -> U -``` - -This is a left fold, so the given function will be applied to the accumulator and first element of -the slice, 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 slice = &[2, 2, 2, 2, 2]; - let folded = slice.fold(0, |a, b| a + b); - assert(folded == 10); -} - -``` - -### reduce - -Same as fold, but uses the first element as the starting element. - -```rust -fn reduce(self, f: fn[Env](T, T) -> T) -> T -``` - -example: - -```rust -fn main() { - let slice = &[2, 2, 2, 2, 2]; - let reduced = slice.reduce(|a, b| a + b); - assert(reduced == 10); -} -``` - -### filter - -Returns a new slice containing only elements for which the given predicate returns true. - -```rust -fn filter(self, f: fn[Env](T) -> bool) -> Self -``` - -example: - -```rust -fn main() { - let slice = &[1, 2, 3, 4, 5]; - let odds = slice.filter(|x| x % 2 == 1); - assert_eq(odds, &[1, 3, 5]); -} -``` - -### join - -Flatten each element in the slice into one value, separated by `separator`. - -Note that although slices implement `Append`, `join` cannot be used on slice -elements since nested slices are prohibited. - -```rust -fn join(self, separator: T) -> T where T: Append -``` - -example: - -```rust -struct Accumulator { - total: Field, -} - -// "Append" two accumulators by adding them -impl Append for Accumulator { - fn empty() -> Self { - Self { total: 0 } - } - - fn append(self, other: Self) -> Self { - Self { total: self.total + other.total } - } -} - -fn main() { - let slice = &[1, 2, 3, 4, 5].map(|total| Accumulator { total }); - - let result = slice.join(Accumulator::empty()); - assert_eq(result, Accumulator { total: 15 }); - - // We can use a non-empty separator to insert additional elements to sum: - let separator = Accumulator { total: 10 }; - let result = slice.join(separator); - assert_eq(result, Accumulator { total: 55 }); -} -``` - -### all - -Returns true if all the elements satisfy the given predicate - -```rust -fn all(self, predicate: fn[Env](T) -> bool) -> bool -``` - -example: - -```rust -fn main() { - let slice = &[2, 2, 2, 2, 2]; - let all = slice.all(|a| a == 2); - assert(all); -} -``` - -### any - -Returns true if any of the elements satisfy the given predicate - -```rust -fn any(self, predicate: fn[Env](T) -> bool) -> bool -``` - -example: - -```rust -fn main() { - let slice = &[2, 2, 2, 2, 5]; - let any = slice.any(|a| a == 5); - assert(any); -} - -``` diff --git a/docs/versioned_docs/version-v0.32.0/noir/concepts/data_types/strings.md b/docs/versioned_docs/version-v0.32.0/noir/concepts/data_types/strings.md deleted file mode 100644 index 1fdee42425e..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/concepts/data_types/strings.md +++ /dev/null @@ -1,79 +0,0 @@ ---- -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, - ] -sidebar_position: 3 ---- - - -The string type is a fixed length value defined with `str`. - -You can use strings in `assert()` functions or print them with -`println()`. See more about [Logging](../../standard_library/logging.md). - -```rust - -fn main(message : pub str<11>, hex_as_string : str<4>) { - 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" -``` - -## 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/versioned_docs/version-v0.32.0/noir/concepts/data_types/structs.md b/docs/versioned_docs/version-v0.32.0/noir/concepts/data_types/structs.md deleted file mode 100644 index dbf68c99813..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/concepts/data_types/structs.md +++ /dev/null @@ -1,70 +0,0 @@ ---- -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, - ] -sidebar_position: 8 ---- - -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.32.0/noir/concepts/data_types/tuples.md b/docs/versioned_docs/version-v0.32.0/noir/concepts/data_types/tuples.md deleted file mode 100644 index 2ec5c9c4113..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/concepts/data_types/tuples.md +++ /dev/null @@ -1,48 +0,0 @@ ---- -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, - ] -sidebar_position: 7 ---- - -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.32.0/noir/concepts/functions.md b/docs/versioned_docs/version-v0.32.0/noir/concepts/functions.md deleted file mode 100644 index f656cdfd97a..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/concepts/functions.md +++ /dev/null @@ -1,226 +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] -sidebar_position: 1 ---- - -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](../../tooling/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](./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](./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](./unconstrained.md) and [NoirJS](../../reference/NoirJS/noir_js/index.md) for more details. -- **test**: mark the function as unit tests. See [Tests](../../tooling/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.32.0/noir/concepts/generics.md b/docs/versioned_docs/version-v0.32.0/noir/concepts/generics.md deleted file mode 100644 index 0c1c27a2221..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/concepts/generics.md +++ /dev/null @@ -1,106 +0,0 @@ ---- -title: Generics -description: Learn how to use Generics in Noir -keywords: [Noir, Rust, generics, functions, structs] -sidebar_position: 7 ---- - -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 print(self) { - for _i in 0 .. self.count { - println(self.value); - } - } -} - -fn main() { - let repeated = RepeatedValue { value: "Hello!", count: 2 }; - 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 - -Since a generic type `T` can represent any type, how can we call functions on the underlying type? -In other words, how can we go from "any type `T`" to "any type `T` that has certain methods available?" - -This is what [traits](../concepts/traits.md) are for in Noir. Here's an example of a function generic over -any type `T` that implements the `Eq` trait for equality: - -```rust -fn first_element_is_equal(array1: [T; N], array2: [T; N]) -> bool - where T: Eq -{ - if (array1.len() == 0) | (array2.len() == 0) { - true - } else { - array1[0] == array2[0] - } -} - -fn main() { - assert(first_element_is_equal([1, 2, 3], [1, 5, 6])); - - // We can use first_element_is_equal for arrays of any type - // as long as we have an Eq impl for the types we pass in - let array = [MyStruct::new(), MyStruct::new()]; - assert(array_eq(array, array, MyStruct::eq)); -} - -impl Eq for MyStruct { - fn eq(self, other: MyStruct) -> bool { - self.foo == other.foo - } -} -``` - -You can find more details on traits and trait implementations on the [traits page](../concepts/traits.md). diff --git a/docs/versioned_docs/version-v0.32.0/noir/concepts/globals.md b/docs/versioned_docs/version-v0.32.0/noir/concepts/globals.md deleted file mode 100644 index 063a3d89248..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/concepts/globals.md +++ /dev/null @@ -1,72 +0,0 @@ ---- -title: Global Variables -description: - Learn about global variables in Noir. Discover how - to declare, modify, and use them in your programs. -keywords: [noir programming language, globals, global variables, constants] -sidebar_position: 8 ---- - -## Globals - - -Noir supports global variables. The global's type can be inferred by the compiler entirely: - -```rust -global N = 5; // Same as `global N: Field = 5` - -global TUPLE = (3, 2); - -fn main() { - assert(N == 5); - assert(N == TUPLE.0 + TUPLE.1); -} -``` - -:::info - -Globals can be defined as any expression, so long as they don't depend on themselves - otherwise there would be a dependency cycle! For example: - -```rust -global T = foo(T); // dependency error -``` - -::: - - -If they are initialized to a literal integer, globals can be used to specify an array's length: - -```rust -global N: Field = 2; - -fn main(y : [Field; N]) { - assert(y[0] == y[1]) -} -``` - -A global from another module can be imported or referenced externally like any other name: - -```rust -global N = 20; - -fn main() { - assert(my_submodule::N != N); -} - -mod my_submodule { - global N: Field = 10; -} -``` - -When a global is used, Noir replaces the name with its definition on each occurrence. -This means globals defined using function calls will repeat the call each time they're used: - -```rust -global RESULT = foo(); - -fn foo() -> [Field; 100] { ... } -``` - -This is usually fine since Noir will generally optimize any function call that does not -refer to a program input into a constant. It should be kept in mind however, if the called -function performs side-effects like `println`, as these will still occur on each use. diff --git a/docs/versioned_docs/version-v0.32.0/noir/concepts/lambdas.md b/docs/versioned_docs/version-v0.32.0/noir/concepts/lambdas.md deleted file mode 100644 index be3c7e0b5ca..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/concepts/lambdas.md +++ /dev/null @@ -1,81 +0,0 @@ ---- -title: Lambdas -description: Learn how to use anonymous functions in Noir programming language. -keywords: [Noir programming language, lambda, closure, function, anonymous function] -sidebar_position: 9 ---- - -## 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.32.0/noir/concepts/mutability.md b/docs/versioned_docs/version-v0.32.0/noir/concepts/mutability.md deleted file mode 100644 index fdeef6a87c5..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/concepts/mutability.md +++ /dev/null @@ -1,121 +0,0 @@ ---- -title: Mutability -description: - Learn about mutable variables in Noir. Discover how - to declare, modify, and use them in your programs. -keywords: [noir programming language, mutability in noir, mutable variables] -sidebar_position: 8 ---- - -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; -} -``` - -## Non-local mutability - -Non-local mutability can be achieved through the mutable reference type `&mut T`: - -```rust -fn set_to_zero(x: &mut Field) { - *x = 0; -} - -fn main() { - let mut y = 42; - set_to_zero(&mut y); - assert(*y == 0); -} -``` - -When creating a mutable reference, the original variable being referred to (`y` in this -example) must also be mutable. Since mutable references are a reference type, they must -be explicitly dereferenced via `*` to retrieve the underlying value. Note that this yields -a copy of the value, so mutating this copy will not change the original value behind the -reference: - -```rust -fn main() { - let mut x = 1; - let x_ref = &mut x; - - let mut y = *x_ref; - let y_ref = &mut y; - - x = 2; - *x_ref = 3; - - y = 4; - *y_ref = 5; - - assert(x == 3); - assert(*x_ref == 3); - assert(y == 5); - assert(*y_ref == 5); -} -``` - -Note that types in Noir are actually deeply immutable so the copy that occurs when -dereferencing is only a conceptual copy - no additional constraints will occur. - -Mutable references can also be stored within structs. Note that there is also -no lifetime parameter on these unlike rust. This is because the allocated memory -always lasts the entire program - as if it were an array of one element. - -```rust -struct Foo { - x: &mut Field -} - -impl Foo { - fn incr(mut self) { - *self.x += 1; - } -} - -fn main() { - let foo = Foo { x: &mut 0 }; - foo.incr(); - assert(*foo.x == 1); -} -``` - -In general, you should avoid non-local & shared mutability unless it is needed. Sticking -to only local mutability will improve readability and potentially improve compiler optimizations as well. diff --git a/docs/versioned_docs/version-v0.32.0/noir/concepts/ops.md b/docs/versioned_docs/version-v0.32.0/noir/concepts/ops.md deleted file mode 100644 index c35c36c38a9..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/concepts/ops.md +++ /dev/null @@ -1,98 +0,0 @@ ---- -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, - ] -sidebar_position: 3 ---- - -# 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, shift must be u8 | -| >> | Right shift an integer by another integer amount | Types must be integer, shift must be u8 | -| ! | 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 identically 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.32.0/noir/concepts/oracles.mdx b/docs/versioned_docs/version-v0.32.0/noir/concepts/oracles.mdx deleted file mode 100644 index 77a2ac1550a..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/concepts/oracles.mdx +++ /dev/null @@ -1,29 +0,0 @@ ---- -title: Oracles -description: Dive into how Noir supports Oracles via RPC calls, and learn how to declare an Oracle in Noir with our comprehensive guide. -keywords: - - Noir - - Oracles - - RPC Calls - - Unconstrained Functions - - Programming - - Blockchain -sidebar_position: 6 ---- - -import Experimental from '@site/src/components/Notes/_experimental.mdx'; - - - -Noir has support for Oracles via RPC calls. This means Noir will make an RPC call and use the return value for proof generation. - -Since Oracles are not resolved by Noir, they are [`unconstrained` functions](./unconstrained.md) - -You can declare an Oracle through the `#[oracle()]` flag. Example: - -```rust -#[oracle(get_number_sequence)] -unconstrained fn get_number_sequence(_size: Field) -> [Field] {} -``` - -The timeout for when using an external RPC oracle resolver can be set with the `NARGO_FOREIGN_CALL_TIMEOUT` environment variable. This timeout is in units of milliseconds. diff --git a/docs/versioned_docs/version-v0.32.0/noir/concepts/shadowing.md b/docs/versioned_docs/version-v0.32.0/noir/concepts/shadowing.md deleted file mode 100644 index 5ce6130d201..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/concepts/shadowing.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -title: Shadowing -sidebar_position: 12 ---- - -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.32.0/noir/concepts/traits.md b/docs/versioned_docs/version-v0.32.0/noir/concepts/traits.md deleted file mode 100644 index 51305b38c16..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/concepts/traits.md +++ /dev/null @@ -1,405 +0,0 @@ ---- -title: Traits -description: - Traits in Noir can be used to abstract out a common interface for functions across - several data types. -keywords: [noir programming language, traits, interfaces, generic, protocol] -sidebar_position: 14 ---- - -## Overview - -Traits in Noir are a useful abstraction similar to interfaces or protocols in other languages. Each trait defines -the interface of several methods contained within the trait. Types can then implement this trait by providing -implementations for these methods. For example in the program: - -```rust -struct Rectangle { - width: Field, - height: Field, -} - -impl Rectangle { - fn area(self) -> Field { - self.width * self.height - } -} - -fn log_area(r: Rectangle) { - println(r.area()); -} -``` - -We have a function `log_area` to log the area of a `Rectangle`. Now how should we change the program if we want this -function to work on `Triangle`s as well?: - -```rust -struct Triangle { - width: Field, - height: Field, -} - -impl Triangle { - fn area(self) -> Field { - self.width * self.height / 2 - } -} -``` - -Making `log_area` generic over all types `T` would be invalid since not all types have an `area` method. Instead, we can -introduce a new `Area` trait and make `log_area` generic over all types `T` that implement `Area`: - -```rust -trait Area { - fn area(self) -> Field; -} - -fn log_area(shape: T) where T: Area { - println(shape.area()); -} -``` - -We also need to explicitly implement `Area` for `Rectangle` and `Triangle`. We can do that by changing their existing -impls slightly. Note that the parameter types and return type of each of our `area` methods must match those defined -by the `Area` trait. - -```rust -impl Area for Rectangle { - fn area(self) -> Field { - self.width * self.height - } -} - -impl Area for Triangle { - fn area(self) -> Field { - self.width * self.height / 2 - } -} -``` - -Now we have a working program that is generic over any type of Shape that is used! Others can even use this program -as a library with their own types - such as `Circle` - as long as they also implement `Area` for these types. - -## Where Clauses - -As seen in `log_area` above, when we want to create a function or method that is generic over any type that implements -a trait, we can add a where clause to the generic function. - -```rust -fn log_area(shape: T) where T: Area { - println(shape.area()); -} -``` - -It is also possible to apply multiple trait constraints on the same variable at once by combining traits with the `+` -operator. Similarly, we can have multiple trait constraints by separating each with a comma: - -```rust -fn foo(elements: [T], thing: U) where - T: Default + Add + Eq, - U: Bar, -{ - let mut sum = T::default(); - - for element in elements { - sum += element; - } - - if sum == T::default() { - thing.bar(); - } -} -``` - -## Generic Implementations - -You can add generics to a trait implementation by adding the generic list after the `impl` keyword: - -```rust -trait Second { - fn second(self) -> Field; -} - -impl Second for (T, Field) { - fn second(self) -> Field { - self.1 - } -} -``` - -You can also implement a trait for every type this way: - -```rust -trait Debug { - fn debug(self); -} - -impl Debug for T { - fn debug(self) { - println(self); - } -} - -fn main() { - 1.debug(); -} -``` - -### Generic Trait Implementations With Where Clauses - -Where clauses can be placed on trait implementations themselves to restrict generics in a similar way. -For example, while `impl Foo for T` implements the trait `Foo` for every type, `impl Foo for T where T: Bar` -will implement `Foo` only for types that also implement `Bar`. This is often used for implementing generic types. -For example, here is the implementation for array equality: - -```rust -impl Eq for [T; N] where T: Eq { - // Test if two arrays have the same elements. - // Because both arrays must have length N, we know their lengths already match. - fn eq(self, other: Self) -> bool { - let mut result = true; - - for i in 0 .. self.len() { - // The T: Eq constraint is needed to call == on the array elements here - result &= self[i] == other[i]; - } - - result - } -} -``` - -Where clauses can also be placed on struct implementations. -For example, here is a method utilizing a generic type that implements the equality trait. - -```rust -struct Foo { - a: u32, - b: T, -} - -impl Foo where T: Eq { - fn eq(self, other: Self) -> bool { - (self.a == other.a) & self.b.eq(other.b) - } -} -``` - -## Generic Traits - -Traits themselves can also be generic by placing the generic arguments after the trait name. These generics are in -scope of every item within the trait. - -```rust -trait Into { - // Convert `self` to type `T` - fn into(self) -> T; -} -``` - -When implementing generic traits the generic arguments of the trait must be specified. This is also true anytime -when referencing a generic trait (e.g. in a `where` clause). - -```rust -struct MyStruct { - array: [Field; 2], -} - -impl Into<[Field; 2]> for MyStruct { - fn into(self) -> [Field; 2] { - self.array - } -} - -fn as_array(x: T) -> [Field; 2] - where T: Into<[Field; 2]> -{ - x.into() -} - -fn main() { - let array = [1, 2]; - let my_struct = MyStruct { array }; - - assert_eq(as_array(my_struct), array); -} -``` - -## Trait Methods With No `self` - -A trait can contain any number of methods, each of which have access to the `Self` type which represents each type -that eventually implements the trait. Similarly, the `self` variable is available as well but is not required to be used. -For example, we can define a trait to create a default value for a type. This trait will need to return the `Self` type -but doesn't need to take any parameters: - -```rust -trait Default { - fn default() -> Self; -} -``` - -Implementing this trait can be done similarly to any other trait: - -```rust -impl Default for Field { - fn default() -> Field { - 0 - } -} - -struct MyType {} - -impl Default for MyType { - fn default() -> Field { - MyType {} - } -} -``` - -However, since there is no `self` parameter, we cannot call it via the method call syntax `object.method()`. -Instead, we'll need to refer to the function directly. This can be done either by referring to the -specific impl `MyType::default()` or referring to the trait itself `Default::default()`. In the later -case, type inference determines the impl that is selected. - -```rust -let my_struct = MyStruct::default(); - -let x: Field = Default::default(); -let result = x + Default::default(); -``` - -:::warning - -```rust -let _ = Default::default(); -``` - -If type inference cannot select which impl to use because of an ambiguous `Self` type, an impl will be -arbitrarily selected. This occurs most often when the result of a trait function call with no parameters -is unused. To avoid this, when calling a trait function with no `self` or `Self` parameters or return type, -always refer to it via the implementation type's namespace - e.g. `MyType::default()`. -This is set to change to an error in future Noir versions. - -::: - -## Default Method Implementations - -A trait can also have default implementations of its methods by giving a body to the desired functions. -Note that this body must be valid for all types that may implement the trait. As a result, the only -valid operations on `self` will be operations valid for any type or other operations on the trait itself. - -```rust -trait Numeric { - fn add(self, other: Self) -> Self; - - // Default implementation of double is (self + self) - fn double(self) -> Self { - self.add(self) - } -} -``` - -When implementing a trait with default functions, a type may choose to implement only the required functions: - -```rust -impl Numeric for Field { - fn add(self, other: Field) -> Field { - self + other - } -} -``` - -Or it may implement the optional methods as well: - -```rust -impl Numeric for u32 { - fn add(self, other: u32) -> u32 { - self + other - } - - fn double(self) -> u32 { - self * 2 - } -} -``` - -## Impl Specialization - -When implementing traits for a generic type it is possible to implement the trait for only a certain combination -of generics. This can be either as an optimization or because those specific generics are required to implement the trait. - -```rust -trait Sub { - fn sub(self, other: Self) -> Self; -} - -struct NonZero { - value: T, -} - -impl Sub for NonZero { - fn sub(self, other: Self) -> Self { - let value = self.value - other.value; - assert(value != 0); - NonZero { value } - } -} -``` - -## Overlapping Implementations - -Overlapping implementations are disallowed by Noir to ensure Noir's decision on which impl to select is never ambiguous. -This means if a trait `Foo` is already implemented -by a type `Bar` for all `T`, then we cannot also have a separate impl for `Bar` (or any other -type argument). Similarly, if there is an impl for all `T` such as `impl Debug for T`, we cannot create -any more impls to `Debug` for other types since it would be ambiguous which impl to choose for any given -method call. - -```rust -trait Trait {} - -// Previous impl defined here -impl Trait for (A, B) {} - -// error: Impl for type `(Field, Field)` overlaps with existing impl -impl Trait for (Field, Field) {} -``` - -## Trait Coherence - -Another restriction on trait implementations is coherence. This restriction ensures other crates cannot create -impls that may overlap with other impls, even if several unrelated crates are used as dependencies in the same -program. - -The coherence restriction is: to implement a trait, either the trait itself or the object type must be declared -in the crate the impl is in. - -In practice this often comes up when using types provided by libraries. If a library provides a type `Foo` that does -not implement a trait in the standard library such as `Default`, you may not `impl Default for Foo` in your own crate. -While restrictive, this prevents later issues or silent changes in the program if the `Foo` library later added its -own impl for `Default`. If you are a user of the `Foo` library in this scenario and need a trait not implemented by the -library your choices are to either submit a patch to the library or use the newtype pattern. - -### The Newtype Pattern - -The newtype pattern gets around the coherence restriction by creating a new wrapper type around the library type -that we cannot create `impl`s for. Since the new wrapper type is defined in our current crate, we can create -impls for any trait we need on it. - -```rust -struct Wrapper { - foo: some_library::Foo, -} - -impl Default for Wrapper { - fn default() -> Wrapper { - Wrapper { - foo: some_library::Foo::new(), - } - } -} -``` - -Since we have an impl for our own type, the behavior of this code will not change even if `some_library` is updated -to provide its own `impl Default for Foo`. The downside of this pattern is that it requires extra wrapping and -unwrapping of values when converting to and from the `Wrapper` and `Foo` types. diff --git a/docs/versioned_docs/version-v0.32.0/noir/concepts/unconstrained.md b/docs/versioned_docs/version-v0.32.0/noir/concepts/unconstrained.md deleted file mode 100644 index 96f824c5e42..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/concepts/unconstrained.md +++ /dev/null @@ -1,99 +0,0 @@ ---- -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] -sidebar_position: 5 ---- - -Unconstrained functions are functions which do not constrain any of the included computation and allow for non-deterministic 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 AND 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. - -## Break and Continue - -In addition to loops over runtime bounds, `break` and `continue` are also available in unconstrained code. See [break and continue](../concepts/control_flow.md#break-and-continue) diff --git a/docs/versioned_docs/version-v0.32.0/noir/modules_packages_crates/_category_.json b/docs/versioned_docs/version-v0.32.0/noir/modules_packages_crates/_category_.json deleted file mode 100644 index 1debcfe7675..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/modules_packages_crates/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "Modules, Packages and Crates", - "position": 2, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.32.0/noir/modules_packages_crates/crates_and_packages.md b/docs/versioned_docs/version-v0.32.0/noir/modules_packages_crates/crates_and_packages.md deleted file mode 100644 index 95ee9f52ab2..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/modules_packages_crates/crates_and_packages.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -title: Crates and Packages -description: Learn how to use Crates and Packages in your Noir project -keywords: [Nargo, dependencies, package management, crates, package] -sidebar_position: 0 ---- - -## 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/noir-projects/noir-contracts/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.32.0/noir/modules_packages_crates/dependencies.md b/docs/versioned_docs/version-v0.32.0/noir/modules_packages_crates/dependencies.md deleted file mode 100644 index 24e02de08fe..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/modules_packages_crates/dependencies.md +++ /dev/null @@ -1,124 +0,0 @@ ---- -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] -sidebar_position: 1 ---- - -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 = "noir-contracts/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 -└── lib_a - ├── Nargo.toml - └── src - └── lib.nr -``` - -Inside of the binary crate, you can specify: - -```toml -# Nargo.toml - -[dependencies] -lib_a = { path = "../lib_a" } -``` - -## 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 lib_a referenced above: - -```rust -use ecrecover; -use lib_a; -``` - -You can also import only the specific parts of dependency that you want to use, like so: - -```rust -use std::hash::sha256; -use std::scalar_mul::fixed_base_embedded_curve; -``` - -Lastly, as demonstrated in the -[elliptic curve example](../standard_library/cryptographic_primitives/ec_primitives.md#examples), you -can import multiple items in the same line by enclosing them in curly braces: - -```rust -use std::ec::tecurve::affine::{Curve, Point}; -``` - -We don't have a way to consume libraries from inside a [workspace](./workspaces.md) 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 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.32.0/noir/modules_packages_crates/modules.md b/docs/versioned_docs/version-v0.32.0/noir/modules_packages_crates/modules.md deleted file mode 100644 index 16b6307d2fd..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/modules_packages_crates/modules.md +++ /dev/null @@ -1,185 +0,0 @@ ---- -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] -sidebar_position: 2 ---- - -Noir's module system follows the same convention as the _newer_ version of Rust's module system. - -## Purpose of Modules - -Modules are used to organize 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 - -``` - -The module filename may also be the name of the module as a directory with the contents in a -file named `mod.nr` within that directory. The above example can alternatively be expressed like this: - -Filename : `src/main.nr` - -```rust -mod foo; - -fn main() { - foo::hello_world(); -} -``` - -Filename : `src/foo/mod.nr` - -```rust -fn from_foo() {} -``` - -Note that it's an error to have both files `src/foo.nr` and `src/foo/mod.nr` in the filesystem. - -### 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 -``` - -Similar to importing a module in the crate root, modules can be placed in a `mod.nr` file, like this: - -Filename : `src/main.nr` - -```rust -mod foo; - -fn main() { - foo::from_foo(); -} -``` - -Filename : `src/foo/mod.nr` - -```rust -mod bar; -fn from_foo() {} -``` - -Filename : `src/foo/bar/mod.nr` - -```rust -fn from_bar() {} -``` - -### Referencing a parent module - -Given a submodule, you can refer to its parent module using the `super` keyword. - -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 -// Same as bar::from_foo -use super::from_foo; - -fn from_bar() { - from_foo(); // invokes super::from_foo(), which is bar::from_foo() - super::from_foo(); // also invokes bar::from_foo() -} -``` \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.32.0/noir/modules_packages_crates/workspaces.md b/docs/versioned_docs/version-v0.32.0/noir/modules_packages_crates/workspaces.md deleted file mode 100644 index 513497f12bf..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/modules_packages_crates/workspaces.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -title: Workspaces -sidebar_position: 3 ---- - -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 -│ │ └── Prover.toml -│ │ └── src -│ │ └── main.nr -│ └── b -│ ├── Nargo.toml -│ └── Prover.toml -│ └── src -│ └── main.nr -│ -└── Nargo.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.32.0/noir/standard_library/_category_.json b/docs/versioned_docs/version-v0.32.0/noir/standard_library/_category_.json deleted file mode 100644 index af04c0933fd..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/standard_library/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "Standard Library", - "position": 1, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.32.0/noir/standard_library/bigint.md b/docs/versioned_docs/version-v0.32.0/noir/standard_library/bigint.md deleted file mode 100644 index 2bfdeec6631..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/standard_library/bigint.md +++ /dev/null @@ -1,122 +0,0 @@ ---- -title: Big Integers -description: How to use big integers from Noir standard library -keywords: - [ - Big Integer, - Noir programming language, - Noir libraries, - ] ---- - -The BigInt module in the standard library exposes some class of integers which do not fit (well) into a Noir native field. It implements modulo arithmetic, modulo a 'big' prime number. - -:::note - -The module can currently be considered as `Field`s with fixed modulo sizes used by a set of elliptic curves, in addition to just the native curve. [More work](https://github.com/noir-lang/noir/issues/510) is needed to achieve arbitrarily sized big integers. - -::: - -Currently 6 classes of integers (i.e 'big' prime numbers) are available in the module, namely: - -- BN254 Fq: Bn254Fq -- BN254 Fr: Bn254Fr -- Secp256k1 Fq: Secpk1Fq -- Secp256k1 Fr: Secpk1Fr -- Secp256r1 Fr: Secpr1Fr -- Secp256r1 Fq: Secpr1Fq - -Where XXX Fq and XXX Fr denote respectively the order of the base and scalar field of the (usual) elliptic curve XXX. -For instance the big integer 'Secpk1Fq' in the standard library refers to integers modulo $2^{256}-2^{32}-977$. - -Feel free to explore the source code for the other primes: - -```rust title="big_int_definition" showLineNumbers -struct BigInt { - pointer: u32, - modulus: u32, -} -``` -> Source code: noir_stdlib/src/bigint.nr#L14-L19 - - -## Example usage - -A common use-case is when constructing a big integer from its bytes representation, and performing arithmetic operations on it: - -```rust title="big_int_example" showLineNumbers -fn big_int_example(x: u8, y: u8) { - let a = Secpk1Fq::from_le_bytes(&[x, y, 0, 45, 2]); - let b = Secpk1Fq::from_le_bytes(&[y, x, 9]); - let c = (a + b) * b / a; - let d = c.to_le_bytes(); - println(d[0]); -} -``` -> Source code: test_programs/execution_success/bigint/src/main.nr#L70-L78 - - -## Methods - -The available operations for each big integer are: - -### from_le_bytes - -Construct a big integer from its little-endian bytes representation. Example: - -```rust - // Construct a big integer from a slice of bytes - let a = Secpk1Fq::from_le_bytes(&[x, y, 0, 45, 2]); - // Construct a big integer from an array of 32 bytes - let a = Secpk1Fq::from_le_bytes_32([1;32]); - ``` - -Sure, here's the formatted version of the remaining methods: - -### to_le_bytes - -Return the little-endian bytes representation of a big integer. Example: - -```rust -let bytes = a.to_le_bytes(); -``` - -### add - -Add two big integers. Example: - -```rust -let sum = a + b; -``` - -### sub - -Subtract two big integers. Example: - -```rust -let difference = a - b; -``` - -### mul - -Multiply two big integers. Example: - -```rust -let product = a * b; -``` - -### div - -Divide two big integers. Note that division is field division and not euclidean division. Example: - -```rust -let quotient = a / b; -``` - -### eq - -Compare two big integers. Example: - -```rust -let are_equal = a == b; -``` diff --git a/docs/versioned_docs/version-v0.32.0/noir/standard_library/black_box_fns.md b/docs/versioned_docs/version-v0.32.0/noir/standard_library/black_box_fns.md deleted file mode 100644 index d6079ab182c..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/standard_library/black_box_fns.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -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. - -The ACVM spec defines a set of blackbox functions which backends will be expected to implement. This allows backends to use optimized implementations of these constraints if they have them, however they may also fallback to less efficient naive implementations if not. - -## Function list - -Here is a list of the current black box functions: - -- [AES128](./cryptographic_primitives/ciphers.mdx#aes128) -- [SHA256](./cryptographic_primitives/hashes.mdx#sha256) -- [Schnorr signature verification](./cryptographic_primitives/schnorr.mdx) -- [Blake2s](./cryptographic_primitives/hashes.mdx#blake2s) -- [Blake3](./cryptographic_primitives/hashes.mdx#blake3) -- [Pedersen Hash](./cryptographic_primitives/hashes.mdx#pedersen_hash) -- [Pedersen Commitment](./cryptographic_primitives/hashes.mdx#pedersen_commitment) -- [ECDSA signature verification](./cryptographic_primitives/ecdsa_sig_verification.mdx) -- [Embedded curve operations (MSM, addition, ...)](./cryptographic_primitives/embedded_curve_ops.mdx) -- AND -- XOR -- RANGE -- [Keccak256](./cryptographic_primitives/hashes.mdx#keccak256) -- [Recursive proof verification](./recursion.mdx) - -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. - -You can view the black box functions defined in the ACVM code [here](https://github.com/noir-lang/noir/blob/master/acvm-repo/acir/src/circuit/black_box_functions.rs). diff --git a/docs/versioned_docs/version-v0.32.0/noir/standard_library/bn254.md b/docs/versioned_docs/version-v0.32.0/noir/standard_library/bn254.md deleted file mode 100644 index 3294f005dbb..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/standard_library/bn254.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -title: Bn254 Field Library ---- - -Noir provides a module in standard library with some optimized functions for bn254 Fr in `std::field::bn254`. - -## decompose - -```rust -fn decompose(x: Field) -> (Field, Field) {} -``` - -Decomposes a single field into two fields, low and high. The low field contains the lower 16 bytes of the input field and the high field contains the upper 16 bytes of the input field. Both field results are range checked to 128 bits. - - -## assert_gt - -```rust -fn assert_gt(a: Field, b: Field) {} -``` - -Asserts that a > b. This will generate less constraints than using `assert(gt(a, b))`. - -## assert_lt - -```rust -fn assert_lt(a: Field, b: Field) {} -``` - -Asserts that a < b. This will generate less constraints than using `assert(lt(a, b))`. - -## gt - -```rust -fn gt(a: Field, b: Field) -> bool {} -``` - -Returns true if a > b. - -## lt - -```rust -fn lt(a: Field, b: Field) -> bool {} -``` - -Returns true if a < b. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.32.0/noir/standard_library/containers/boundedvec.md b/docs/versioned_docs/version-v0.32.0/noir/standard_library/containers/boundedvec.md deleted file mode 100644 index 604d84d5ba4..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/standard_library/containers/boundedvec.md +++ /dev/null @@ -1,419 +0,0 @@ ---- -title: Bounded Vectors -keywords: [noir, vector, bounded vector, slice] -sidebar_position: 1 ---- - -A `BoundedVec` is a growable storage similar to a `Vec` except that it -is bounded with a maximum possible length. Unlike `Vec`, `BoundedVec` is not implemented -via slices and thus is not subject to the same restrictions slices are (notably, nested -slices - and thus nested vectors as well - are disallowed). - -Since a BoundedVec is backed by a normal array under the hood, growing the BoundedVec by -pushing an additional element is also more efficient - the length only needs to be increased -by one. - -For these reasons `BoundedVec` should generally be preferred over `Vec` when there -is a reasonable maximum bound that can be placed on the vector. - -Example: - -```rust -let mut vector: BoundedVec = BoundedVec::new(); -for i in 0..5 { - vector.push(i); -} -assert(vector.len() == 5); -assert(vector.max_len() == 10); -``` - -## Methods - -### new - -```rust -pub fn new() -> Self -``` - -Creates a new, empty vector of length zero. - -Since this container is backed by an array internally, it still needs an initial value -to give each element. To resolve this, each element is zeroed internally. This value -is guaranteed to be inaccessible unless `get_unchecked` is used. - -Example: - -```rust -let empty_vector: BoundedVec = BoundedVec::new(); -assert(empty_vector.len() == 0); -``` - -Note that whenever calling `new` the maximum length of the vector should always be specified -via a type signature: - -```rust title="new_example" showLineNumbers -fn foo() -> BoundedVec { - // Ok! MaxLen is specified with a type annotation - let v1: BoundedVec = BoundedVec::new(); - let v2 = BoundedVec::new(); - - // Ok! MaxLen is known from the type of foo's return value - v2 -} - -fn bad() { - let mut v3 = BoundedVec::new(); - - // Not Ok! We don't know if v3's MaxLen is at least 1, and the compiler often infers 0 by default. - v3.push(5); -} -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L11-L27 - - -This defaulting of `MaxLen` (and numeric generics in general) to zero may change in future noir versions -but for now make sure to use type annotations when using bounded vectors. Otherwise, you will receive a constraint failure at runtime when the vec is pushed to. - -### get - -```rust -pub fn get(self, index: u64) -> T { -``` - -Retrieves an element from the vector at the given index, starting from zero. - -If the given index is equal to or greater than the length of the vector, this -will issue a constraint failure. - -Example: - -```rust -fn foo(v: BoundedVec) { - let first = v.get(0); - let last = v.get(v.len() - 1); - assert(first != last); -} -``` - -### get_unchecked - -```rust -pub fn get_unchecked(self, index: u64) -> T { -``` - -Retrieves an element from the vector at the given index, starting from zero, without -performing a bounds check. - -Since this function does not perform a bounds check on length before accessing the element, -it is unsafe! Use at your own risk! - -Example: - -```rust title="get_unchecked_example" showLineNumbers -fn sum_of_first_three(v: BoundedVec) -> u32 { - // Always ensure the length is larger than the largest - // index passed to get_unchecked - assert(v.len() > 2); - let first = v.get_unchecked(0); - let second = v.get_unchecked(1); - let third = v.get_unchecked(2); - first + second + third -} -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L54-L64 - - -### set - -```rust -pub fn set(&mut self: Self, index: u64, value: T) { -``` - -Writes an element to the vector at the given index, starting from zero. - -If the given index is equal to or greater than the length of the vector, this will issue a constraint failure. - -Example: - -```rust -fn foo(v: BoundedVec) { - let first = v.get(0); - assert(first != 42); - v.set(0, 42); - let new_first = v.get(0); - assert(new_first == 42); -} -``` - -### set_unchecked - -```rust -pub fn set_unchecked(&mut self: Self, index: u64, value: T) -> T { -``` - -Writes an element to the vector at the given index, starting from zero, without performing a bounds check. - -Since this function does not perform a bounds check on length before accessing the element, it is unsafe! Use at your own risk! - -Example: - -```rust title="set_unchecked_example" showLineNumbers -fn set_unchecked_example() { - let mut vec: BoundedVec = BoundedVec::new(); - vec.extend_from_array([1, 2]); - - // Here we're safely writing within the valid range of `vec` - // `vec` now has the value [42, 2] - vec.set_unchecked(0, 42); - - // We can then safely read this value back out of `vec`. - // Notice that we use the checked version of `get` which would prevent reading unsafe values. - assert_eq(vec.get(0), 42); - - // We've now written past the end of `vec`. - // As this index is still within the maximum potential length of `v`, - // it won't cause a constraint failure. - vec.set_unchecked(2, 42); - println(vec); - - // This will write past the end of the maximum potential length of `vec`, - // it will then trigger a constraint failure. - vec.set_unchecked(5, 42); - println(vec); -} -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L67-L91 - - - -### push - -```rust -pub fn push(&mut self, elem: T) { -``` - -Pushes an element to the end of the vector. This increases the length -of the vector by one. - -Panics if the new length of the vector will be greater than the max length. - -Example: - -```rust title="bounded-vec-push-example" showLineNumbers -let mut v: BoundedVec = BoundedVec::new(); - - v.push(1); - v.push(2); - - // Panics with failed assertion "push out of bounds" - v.push(3); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L95-L103 - - -### pop - -```rust -pub fn pop(&mut self) -> T -``` - -Pops the element at the end of the vector. This will decrease the length -of the vector by one. - -Panics if the vector is empty. - -Example: - -```rust title="bounded-vec-pop-example" showLineNumbers -let mut v: BoundedVec = BoundedVec::new(); - v.push(1); - v.push(2); - - let two = v.pop(); - let one = v.pop(); - - assert(two == 2); - assert(one == 1); - // error: cannot pop from an empty vector - // let _ = v.pop(); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L108-L120 - - -### len - -```rust -pub fn len(self) -> u64 { -``` - -Returns the current length of this vector - -Example: - -```rust title="bounded-vec-len-example" showLineNumbers -let mut v: BoundedVec = BoundedVec::new(); - assert(v.len() == 0); - - v.push(100); - assert(v.len() == 1); - - v.push(200); - v.push(300); - v.push(400); - assert(v.len() == 4); - - let _ = v.pop(); - let _ = v.pop(); - assert(v.len() == 2); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L125-L140 - - -### max_len - -```rust -pub fn max_len(_self: BoundedVec) -> u64 { -``` - -Returns the maximum length of this vector. This is always -equal to the `MaxLen` parameter this vector was initialized with. - -Example: - -```rust title="bounded-vec-max-len-example" showLineNumbers -let mut v: BoundedVec = BoundedVec::new(); - - assert(v.max_len() == 5); - v.push(10); - assert(v.max_len() == 5); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L145-L151 - - -### storage - -```rust -pub fn storage(self) -> [T; MaxLen] { -``` - -Returns the internal array within this vector. -Since arrays in Noir are immutable, mutating the returned storage array will not mutate -the storage held internally by this vector. - -Note that uninitialized elements may be zeroed out! - -Example: - -```rust title="bounded-vec-storage-example" showLineNumbers -let mut v: BoundedVec = BoundedVec::new(); - - assert(v.storage() == [0, 0, 0, 0, 0]); - - v.push(57); - assert(v.storage() == [57, 0, 0, 0, 0]); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L156-L163 - - -### extend_from_array - -```rust -pub fn extend_from_array(&mut self, array: [T; Len]) -``` - -Pushes each element from the given array to this vector. - -Panics if pushing each element would cause the length of this vector -to exceed the maximum length. - -Example: - -```rust title="bounded-vec-extend-from-array-example" showLineNumbers -let mut vec: BoundedVec = BoundedVec::new(); - vec.extend_from_array([2, 4]); - - assert(vec.len == 2); - assert(vec.get(0) == 2); - assert(vec.get(1) == 4); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L168-L175 - - -### extend_from_bounded_vec - -```rust -pub fn extend_from_bounded_vec(&mut self, vec: BoundedVec) -``` - -Pushes each element from the other vector to this vector. The length of -the other vector is left unchanged. - -Panics if pushing each element would cause the length of this vector -to exceed the maximum length. - -Example: - -```rust title="bounded-vec-extend-from-bounded-vec-example" showLineNumbers -let mut v1: BoundedVec = BoundedVec::new(); - let mut v2: BoundedVec = BoundedVec::new(); - - v2.extend_from_array([1, 2, 3]); - v1.extend_from_bounded_vec(v2); - - assert(v1.storage() == [1, 2, 3, 0, 0]); - assert(v2.storage() == [1, 2, 3, 0, 0, 0, 0]); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L180-L189 - - -### from_array - -```rust -pub fn from_array(array: [T; Len]) -> Self -``` - -Creates a new vector, populating it with values derived from an array input. -The maximum length of the vector is determined based on the type signature. - -Example: -```rust -let bounded_vec: BoundedVec = BoundedVec::from_array([1, 2, 3]) -``` - -### map - -```rust -pub fn map(self, f: fn[Env](T) -> U) -> BoundedVec -``` - -Creates a new vector of equal size by calling a closure on each element in this vector. - -Example: - -```rust title="bounded-vec-map-example" showLineNumbers -let vec: BoundedVec = BoundedVec::from_array([1, 2, 3, 4]); - let result = vec.map(|value| value * 2); -``` -> Source code: noir_stdlib/src/collections/bounded_vec.nr#L205-L208 - - -### any - -```rust -pub fn any(self, predicate: fn[Env](T) -> bool) -> bool -``` - -Returns true if the given predicate returns true for any element -in this vector. - -Example: - -```rust title="bounded-vec-any-example" showLineNumbers -let mut v: BoundedVec = BoundedVec::new(); - v.extend_from_array([2, 4, 6]); - - let all_even = !v.any(|elem: u32| elem % 2 != 0); - assert(all_even); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L256-L262 - diff --git a/docs/versioned_docs/version-v0.32.0/noir/standard_library/containers/hashmap.md b/docs/versioned_docs/version-v0.32.0/noir/standard_library/containers/hashmap.md deleted file mode 100644 index 8c50c7e774c..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/standard_library/containers/hashmap.md +++ /dev/null @@ -1,570 +0,0 @@ ---- -title: HashMap -keywords: [noir, map, hash, hashmap] -sidebar_position: 1 ---- - -`HashMap` is used to efficiently store and look up key-value pairs. - -`HashMap` is a bounded type which can store anywhere from zero to `MaxLen` total elements. -Note that due to hash collisions, the actual maximum number of elements stored by any particular -hashmap is likely lower than `MaxLen`. This is true even with cryptographic hash functions since -every hash value will be performed modulo `MaxLen`. - -When creating `HashMap`s, the `MaxLen` generic should always be specified if it is not already -known. Otherwise, the compiler may infer a different value for `MaxLen` (such as zero), which -will likely change the result of the program. This behavior is set to become an error in future -versions instead. - -Example: - -```rust -// Create a mapping from Fields to u32s with a maximum length of 12 -// using a poseidon2 hasher -use std::hash::poseidon2::Poseidon2Hasher; -let mut map: HashMap> = HashMap::default(); - -map.insert(1, 2); -map.insert(3, 4); - -let two = map.get(1).unwrap(); -``` - -## Methods - -### default - -```rust title="default" showLineNumbers -impl Default for HashMap -where - B: BuildHasher + Default, - H: Hasher + Default -{ - fn default() -> Self { -``` -> Source code: noir_stdlib/src/collections/map.nr#L462-L469 - - -Creates a fresh, empty HashMap. - -When using this function, always make sure to specify the maximum size of the hash map. - -This is the same `default` from the `Default` implementation given further below. It is -repeated here for convenience since it is the recommended way to create a hashmap. - -Example: - -```rust title="default_example" showLineNumbers -let hashmap: HashMap> = HashMap::default(); - assert(hashmap.is_empty()); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L201-L204 - - -Because `HashMap` has so many generic arguments that are likely to be the same throughout -your program, it may be helpful to create a type alias: - -```rust title="type_alias" showLineNumbers -type MyMap = HashMap>; -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L195-L197 - - -### with_hasher - -```rust title="with_hasher" showLineNumbers -pub fn with_hasher(_build_hasher: B) -> Self - where - B: BuildHasher { -``` -> Source code: noir_stdlib/src/collections/map.nr#L82-L86 - - -Creates a hashmap with an existing `BuildHasher`. This can be used to ensure multiple -hashmaps are created with the same hasher instance. - -Example: - -```rust title="with_hasher_example" showLineNumbers -let my_hasher: BuildHasherDefault = Default::default(); - let hashmap: HashMap> = HashMap::with_hasher(my_hasher); - assert(hashmap.is_empty()); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L206-L210 - - -### get - -```rust title="get" showLineNumbers -pub fn get( - self, - key: K - ) -> Option - where - K: Eq + Hash, - B: BuildHasher, - H: Hasher { -``` -> Source code: noir_stdlib/src/collections/map.nr#L278-L287 - - -Retrieves a value from the hashmap, returning `Option::none()` if it was not found. - -Example: - -```rust title="get_example" showLineNumbers -fn get_example(map: HashMap>) { - let x = map.get(12); - - if x.is_some() { - assert(x.unwrap() == 42); - } -} -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L298-L306 - - -### insert - -```rust title="insert" showLineNumbers -pub fn insert( - &mut self, - key: K, - value: V - ) - where - K: Eq + Hash, - B: BuildHasher, - H: Hasher { -``` -> Source code: noir_stdlib/src/collections/map.nr#L313-L323 - - -Inserts a new key-value pair into the map. If the key was already in the map, its -previous value will be overridden with the newly provided one. - -Example: - -```rust title="insert_example" showLineNumbers -let mut map: HashMap> = HashMap::default(); - map.insert(12, 42); - assert(map.len() == 1); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L212-L216 - - -### remove - -```rust title="remove" showLineNumbers -pub fn remove( - &mut self, - key: K - ) - where - K: Eq + Hash, - B: BuildHasher, - H: Hasher { -``` -> Source code: noir_stdlib/src/collections/map.nr#L356-L365 - - -Removes the given key-value pair from the map. If the key was not already present -in the map, this does nothing. - -Example: - -```rust title="remove_example" showLineNumbers -map.remove(12); - assert(map.is_empty()); - - // If a key was not present in the map, remove does nothing - map.remove(12); - assert(map.is_empty()); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L220-L227 - - -### is_empty - -```rust title="is_empty" showLineNumbers -pub fn is_empty(self) -> bool { -``` -> Source code: noir_stdlib/src/collections/map.nr#L115-L117 - - -True if the length of the hash map is empty. - -Example: - -```rust title="is_empty_example" showLineNumbers -assert(map.is_empty()); - - map.insert(1, 2); - assert(!map.is_empty()); - - map.remove(1); - assert(map.is_empty()); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L229-L237 - - -### len - -```rust title="len" showLineNumbers -pub fn len(self) -> u32 { -``` -> Source code: noir_stdlib/src/collections/map.nr#L264-L266 - - -Returns the current length of this hash map. - -Example: - -```rust title="len_example" showLineNumbers -// This is equivalent to checking map.is_empty() - assert(map.len() == 0); - - map.insert(1, 2); - map.insert(3, 4); - map.insert(5, 6); - assert(map.len() == 3); - - // 3 was already present as a key in the hash map, so the length is unchanged - map.insert(3, 7); - assert(map.len() == 3); - - map.remove(1); - assert(map.len() == 2); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L239-L254 - - -### capacity - -```rust title="capacity" showLineNumbers -pub fn capacity(_self: Self) -> u32 { -``` -> Source code: noir_stdlib/src/collections/map.nr#L271-L273 - - -Returns the maximum capacity of this hashmap. This is always equal to the capacity -specified in the hashmap's type. - -Unlike hashmaps in general purpose programming languages, hashmaps in Noir have a -static capacity that does not increase as the map grows larger. Thus, this capacity -is also the maximum possible element count that can be inserted into the hashmap. -Due to hash collisions (modulo the hashmap length), it is likely the actual maximum -element count will be lower than the full capacity. - -Example: - -```rust title="capacity_example" showLineNumbers -let empty_map: HashMap> = HashMap::default(); - assert(empty_map.len() == 0); - assert(empty_map.capacity() == 42); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L256-L260 - - -### clear - -```rust title="clear" showLineNumbers -pub fn clear(&mut self) { -``` -> Source code: noir_stdlib/src/collections/map.nr#L93-L95 - - -Clears the hashmap, removing all key-value pairs from it. - -Example: - -```rust title="clear_example" showLineNumbers -assert(!map.is_empty()); - map.clear(); - assert(map.is_empty()); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L262-L266 - - -### contains_key - -```rust title="contains_key" showLineNumbers -pub fn contains_key( - self, - key: K - ) -> bool - where - K: Hash + Eq, - B: BuildHasher, - H: Hasher { -``` -> Source code: noir_stdlib/src/collections/map.nr#L101-L110 - - -True if the hashmap contains the given key. Unlike `get`, this will not also return -the value associated with the key. - -Example: - -```rust title="contains_key_example" showLineNumbers -if map.contains_key(7) { - let value = map.get(7); - assert(value.is_some()); - } else { - println("No value for key 7!"); - } -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L268-L275 - - -### entries - -```rust title="entries" showLineNumbers -pub fn entries(self) -> BoundedVec<(K, V), N> { -``` -> Source code: noir_stdlib/src/collections/map.nr#L123-L125 - - -Returns a vector of each key-value pair present in the hashmap. - -The length of the returned vector is always equal to the length of the hashmap. - -Example: - -```rust title="entries_example" showLineNumbers -let entries = map.entries(); - - // The length of a hashmap may not be compile-time known, so we - // need to loop over its capacity instead - for i in 0..map.capacity() { - if i < entries.len() { - let (key, value) = entries.get(i); - println(f"{key} -> {value}"); - } - } -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L309-L320 - - -### keys - -```rust title="keys" showLineNumbers -pub fn keys(self) -> BoundedVec { -``` -> Source code: noir_stdlib/src/collections/map.nr#L144-L146 - - -Returns a vector of each key present in the hashmap. - -The length of the returned vector is always equal to the length of the hashmap. - -Example: - -```rust title="keys_example" showLineNumbers -let keys = map.keys(); - - for i in 0..keys.max_len() { - if i < keys.len() { - let key = keys.get_unchecked(i); - let value = map.get(key).unwrap_unchecked(); - println(f"{key} -> {value}"); - } - } -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L322-L332 - - -### values - -```rust title="values" showLineNumbers -pub fn values(self) -> BoundedVec { -``` -> Source code: noir_stdlib/src/collections/map.nr#L164-L166 - - -Returns a vector of each value present in the hashmap. - -The length of the returned vector is always equal to the length of the hashmap. - -Example: - -```rust title="values_example" showLineNumbers -let values = map.values(); - - for i in 0..values.max_len() { - if i < values.len() { - let value = values.get_unchecked(i); - println(f"Found value {value}"); - } - } -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L334-L343 - - -### iter_mut - -```rust title="iter_mut" showLineNumbers -pub fn iter_mut( - &mut self, - f: fn(K, V) -> (K, V) - ) - where - K: Eq + Hash, - B: BuildHasher, - H: Hasher { -``` -> Source code: noir_stdlib/src/collections/map.nr#L183-L192 - - -Iterates through each key-value pair of the HashMap, setting each key-value pair to the -result returned from the given function. - -Note that since keys can be mutated, the HashMap needs to be rebuilt as it is iterated -through. If this is not desired, use `iter_values_mut` if only values need to be mutated, -or `entries` if neither keys nor values need to be mutated. - -The iteration order is left unspecified. As a result, if two keys are mutated to become -equal, which of the two values that will be present for the key in the resulting map is also unspecified. - -Example: - -```rust title="iter_mut_example" showLineNumbers -// Add 1 to each key in the map, and double the value associated with that key. - map.iter_mut(|k, v| (k + 1, v * 2)); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L347-L350 - - -### iter_keys_mut - -```rust title="iter_keys_mut" showLineNumbers -pub fn iter_keys_mut( - &mut self, - f: fn(K) -> K - ) - where - K: Eq + Hash, - B: BuildHasher, - H: Hasher { -``` -> Source code: noir_stdlib/src/collections/map.nr#L208-L217 - - -Iterates through the HashMap, mutating each key to the result returned from -the given function. - -Note that since keys can be mutated, the HashMap needs to be rebuilt as it is iterated -through. If only iteration is desired and the keys are not intended to be mutated, -prefer using `entries` instead. - -The iteration order is left unspecified. As a result, if two keys are mutated to become -equal, which of the two values that will be present for the key in the resulting map is also unspecified. - -Example: - -```rust title="iter_keys_mut_example" showLineNumbers -// Double each key, leaving the value associated with that key untouched - map.iter_keys_mut(|k| k * 2); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L352-L355 - - -### iter_values_mut - -```rust title="iter_values_mut" showLineNumbers -pub fn iter_values_mut(&mut self, f: fn(V) -> V) { -``` -> Source code: noir_stdlib/src/collections/map.nr#L233-L235 - - -Iterates through the HashMap, applying the given function to each value and mutating the -value to equal the result. This function is more efficient than `iter_mut` and `iter_keys_mut` -because the keys are untouched and the underlying hashmap thus does not need to be reordered. - -Example: - -```rust title="iter_values_mut_example" showLineNumbers -// Halve each value - map.iter_values_mut(|v| v / 2); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L357-L360 - - -### retain - -```rust title="retain" showLineNumbers -pub fn retain(&mut self, f: fn(K, V) -> bool) { -``` -> Source code: noir_stdlib/src/collections/map.nr#L247-L249 - - -Retains only the key-value pairs for which the given function returns true. -Any key-value pairs for which the function returns false will be removed from the map. - -Example: - -```rust title="retain_example" showLineNumbers -map.retain(|k, v| (k != 0) & (v != 0)); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L280-L282 - - -## Trait Implementations - -### default - -```rust title="default" showLineNumbers -impl Default for HashMap -where - B: BuildHasher + Default, - H: Hasher + Default -{ - fn default() -> Self { -``` -> Source code: noir_stdlib/src/collections/map.nr#L462-L469 - - -Constructs an empty HashMap. - -Example: - -```rust title="default_example" showLineNumbers -let hashmap: HashMap> = HashMap::default(); - assert(hashmap.is_empty()); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L201-L204 - - -### eq - -```rust title="eq" showLineNumbers -impl Eq for HashMap -where - K: Eq + Hash, - V: Eq, - B: BuildHasher, - H: Hasher -{ - fn eq(self, other: HashMap) -> bool { -``` -> Source code: noir_stdlib/src/collections/map.nr#L426-L435 - - -Checks if two HashMaps are equal. - -Example: - -```rust title="eq_example" showLineNumbers -let mut map1: HashMap> = HashMap::default(); - let mut map2: HashMap> = HashMap::default(); - - map1.insert(1, 2); - map1.insert(3, 4); - - map2.insert(3, 4); - map2.insert(1, 2); - - assert(map1 == map2); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L284-L295 - diff --git a/docs/versioned_docs/version-v0.32.0/noir/standard_library/containers/index.md b/docs/versioned_docs/version-v0.32.0/noir/standard_library/containers/index.md deleted file mode 100644 index ea84c6d5c21..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/standard_library/containers/index.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Containers -description: Container types provided by Noir's standard library for storing and retrieving data -keywords: [containers, data types, vec, hashmap] ---- diff --git a/docs/versioned_docs/version-v0.32.0/noir/standard_library/containers/vec.mdx b/docs/versioned_docs/version-v0.32.0/noir/standard_library/containers/vec.mdx deleted file mode 100644 index 475011922f8..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/standard_library/containers/vec.mdx +++ /dev/null @@ -1,170 +0,0 @@ ---- -title: Vectors -description: Delve into the Vec 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] -sidebar_position: 6 ---- - -import Experimental from '@site/src/components/Notes/_experimental.mdx'; - - - -A vector is a collection type similar to Rust's `Vec` type. In Noir, it is a convenient way to use slices as mutable arrays. - -Example: - -```rust -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 -``` - -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 -``` - -Example: - -```rust -let slice: [Field] = &[1, 2, 3]; -let vector_from_slice = Vec::from_slice(slice); -assert(vector_from_slice.len() == 3); -``` - -### len - -Returns the number of elements in the vector. - -```rust -pub fn len(self) -> Field -``` - -Example: - -```rust -let empty_vector: Vec = Vec::new(); -assert(empty_vector.len() == 0); -``` - -### 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 -``` - -Example: - -```rust -let vector: Vec = Vec::from_slice(&[10, 20, 30]); -assert(vector.get(1) == 20); -``` - -### set - -```rust -pub fn set(&mut self: Self, index: u64, value: T) { -``` - -Writes an element to the vector at the given index, starting from zero. - -Panics if the index points beyond the vector's end. - -Example: - -```rust -let vector: Vec = Vec::from_slice(&[10, 20, 30]); -assert(vector.get(1) == 20); -vector.set(1, 42); -assert(vector.get(1) == 42); -``` - -### 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) -``` - -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 -``` - -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) -``` - -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 -``` - -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); -``` diff --git a/docs/versioned_docs/version-v0.32.0/noir/standard_library/cryptographic_primitives/_category_.json b/docs/versioned_docs/version-v0.32.0/noir/standard_library/cryptographic_primitives/_category_.json deleted file mode 100644 index 5d694210bbf..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/standard_library/cryptographic_primitives/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "position": 0, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.32.0/noir/standard_library/cryptographic_primitives/ciphers.mdx b/docs/versioned_docs/version-v0.32.0/noir/standard_library/cryptographic_primitives/ciphers.mdx deleted file mode 100644 index d6a5e1a79eb..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/standard_library/cryptographic_primitives/ciphers.mdx +++ /dev/null @@ -1,32 +0,0 @@ ---- -title: Ciphers -description: - Learn about the implemented ciphers ready to use for any Noir project -keywords: - [ciphers, Noir project, aes128, encrypt] -sidebar_position: 0 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; - -## aes128 - -Given a plaintext as an array of bytes, returns the corresponding aes128 ciphertext (CBC mode). Input padding is automatically performed using PKCS#7, so that the output length is `input.len() + (16 - input.len() % 16)`. - -```rust title="aes128" showLineNumbers -pub fn aes128_encrypt(input: [u8; N], iv: [u8; 16], key: [u8; 16]) -> [u8] {} -``` -> Source code: noir_stdlib/src/aes128.nr#L2-L4 - - -```rust -fn main() { - let input: [u8; 4] = [0, 12, 3, 15] // Random bytes, will be padded to 16 bytes. - let iv: [u8; 16] = [0; 16]; // Initialisation vector - let key: [u8; 16] = [0; 16] // AES key - let ciphertext = std::aes128::aes128_encrypt(inputs.as_bytes(), iv.as_bytes(), key.as_bytes()); // In this case, the output length will be 16 bytes. -} -``` - - - \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.32.0/noir/standard_library/cryptographic_primitives/ec_primitives.md b/docs/versioned_docs/version-v0.32.0/noir/standard_library/cryptographic_primitives/ec_primitives.md deleted file mode 100644 index f262d8160d6..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/standard_library/cryptographic_primitives/ec_primitives.md +++ /dev/null @@ -1,102 +0,0 @@ ---- -title: Elliptic Curve Primitives -keywords: [cryptographic primitives, Noir project] -sidebar_position: 4 ---- - -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/mod.nr`](https://github.com/noir-lang/noir/blob/master/noir_stdlib/src/ec/mod.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/mod.nr)). - -## Examples - -The -[ec_baby_jubjub test](https://github.com/noir-lang/noir/blob/master/test_programs/compile_success_empty/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 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.32.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx b/docs/versioned_docs/version-v0.32.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx deleted file mode 100644 index 4c22e70e8de..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx +++ /dev/null @@ -1,98 +0,0 @@ ---- -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] -sidebar_position: 3 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; - -Noir supports ECDSA signatures verification over the secp256k1 and secp256r1 curves. - -## ecdsa_secp256k1::verify_signature - -Verifier for ECDSA Secp256k1 signatures. -See ecdsa_secp256k1::verify_signature_slice for a version that accepts slices directly. - -```rust title="ecdsa_secp256k1" showLineNumbers -pub fn verify_signature( - public_key_x: [u8; 32], - public_key_y: [u8; 32], - signature: [u8; 64], - message_hash: [u8; N] -) -> bool -``` -> Source code: noir_stdlib/src/ecdsa_secp256k1.nr#L2-L9 - - -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_secp256k1::verify_signature_slice - -Verifier for ECDSA Secp256k1 signatures where the message is a slice. - -```rust title="ecdsa_secp256k1_slice" showLineNumbers -pub fn verify_signature_slice( - public_key_x: [u8; 32], - public_key_y: [u8; 32], - signature: [u8; 64], - message_hash: [u8] -) -> bool -``` -> Source code: noir_stdlib/src/ecdsa_secp256k1.nr#L13-L20 - - - - -## ecdsa_secp256r1::verify_signature - -Verifier for ECDSA Secp256r1 signatures. -See ecdsa_secp256r1::verify_signature_slice for a version that accepts slices directly. - -```rust title="ecdsa_secp256r1" showLineNumbers -pub fn verify_signature( - public_key_x: [u8; 32], - public_key_y: [u8; 32], - signature: [u8; 64], - message_hash: [u8; N] -) -> bool -``` -> Source code: noir_stdlib/src/ecdsa_secp256r1.nr#L2-L9 - - -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); -} -``` - - - -## ecdsa_secp256r1::verify_signature - -Verifier for ECDSA Secp256r1 signatures where the message is a slice. - -```rust title="ecdsa_secp256r1_slice" showLineNumbers -pub fn verify_signature_slice( - public_key_x: [u8; 32], - public_key_y: [u8; 32], - signature: [u8; 64], - message_hash: [u8] -) -> bool -``` -> Source code: noir_stdlib/src/ecdsa_secp256r1.nr#L13-L20 - - - diff --git a/docs/versioned_docs/version-v0.32.0/noir/standard_library/cryptographic_primitives/eddsa.mdx b/docs/versioned_docs/version-v0.32.0/noir/standard_library/cryptographic_primitives/eddsa.mdx deleted file mode 100644 index ef4386052eb..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/standard_library/cryptographic_primitives/eddsa.mdx +++ /dev/null @@ -1,37 +0,0 @@ ---- -title: EdDSA Verification -description: Learn about the cryptographic primitives regarding EdDSA -keywords: [cryptographic primitives, Noir project, eddsa, signatures] -sidebar_position: 5 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; - -## 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 -``` - -It is also possible to specify the hash algorithm used for the signature by using the `eddsa_verify` function by passing a type implementing the Hasher trait with the turbofish operator. -For instance, if you want to use Poseidon2 instead, you can do the following: -```rust -use std::hash::poseidon2::Poseidon2Hasher; - -eddsa_verify::(pub_key_a.x, pub_key_a.y, s_a, r8_a.x, r8_a.y, msg); -``` - - - -## eddsa::eddsa_to_pub - -Private to public key conversion. - -Returns `(pub_key_x, pub_key_y)` - -```rust -fn eddsa_to_pub(secret : Field) -> (Field, Field) -``` - diff --git a/docs/versioned_docs/version-v0.32.0/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx b/docs/versioned_docs/version-v0.32.0/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx deleted file mode 100644 index 68d033e9d60..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx +++ /dev/null @@ -1,98 +0,0 @@ ---- -title: Scalar multiplication -description: See how you can perform scalar multiplication in Noir -keywords: [cryptographic primitives, Noir project, scalar multiplication] -sidebar_position: 1 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; - -The following functions perform operations over the embedded curve whose coordinates are defined by the configured noir field. -For the BN254 scalar field, this is BabyJubJub or Grumpkin. - -:::note -Suffixes `_low` and `_high` denote low and high limbs of a scalar. -::: - -## embedded_curve_ops::multi_scalar_mul - -Performs multi scalar multiplication over the embedded curve. -The function accepts arbitrary amount of point-scalar pairs on the input, it multiplies the individual pairs over -the curve and returns a sum of the resulting points. - -Points represented as x and y coordinates [x1, y1, x2, y2, ...], scalars as low and high limbs [low1, high1, low2, high2, ...]. - -```rust title="multi_scalar_mul" showLineNumbers -pub fn multi_scalar_mul( - points: [EmbeddedCurvePoint; N], - scalars: [EmbeddedCurveScalar; N] -) -> [Field; 3] -``` -> Source code: noir_stdlib/src/embedded_curve_ops.nr#L92-L97 - - -example - -```rust -fn main(point_x: Field, point_y: Field, scalar_low: Field, scalar_high: Field) { - let point = std::embedded_curve_ops::multi_scalar_mul([point_x, point_y], [scalar_low, scalar_high]); - println(point); -} -``` - -## embedded_curve_ops::fixed_base_scalar_mul - -Performs fixed base scalar multiplication over the embedded curve (multiplies input scalar with a generator point). -The function accepts a single scalar on the input represented as 2 fields. - -```rust title="fixed_base_scalar_mul" showLineNumbers -pub fn fixed_base_scalar_mul( - scalar_low: Field, - scalar_high: Field -) -> [Field; 3] -``` -> Source code: noir_stdlib/src/embedded_curve_ops.nr#L103-L108 - - -example - -```rust -fn main(scalar_low: Field, scalar_high: Field) { - let point = std::embedded_curve_ops::fixed_base_scalar_mul(scalar_low, scalar_high); - println(point); -} -``` - -## embedded_curve_ops::embedded_curve_add - -Adds two points on the embedded curve. -This function takes two `EmbeddedCurvePoint` structures as parameters, representing points on the curve, and returns a new `EmbeddedCurvePoint` structure that represents their sum. - -### Parameters: -- `point1` (`EmbeddedCurvePoint`): The first point to add. -- `point2` (`EmbeddedCurvePoint`): The second point to add. - -### Returns: -- `EmbeddedCurvePoint`: The resulting point after the addition of `point1` and `point2`. - -```rust title="embedded_curve_add" showLineNumbers -fn embedded_curve_add( - point1: EmbeddedCurvePoint, - point2: EmbeddedCurvePoint -) -> EmbeddedCurvePoint -``` -> Source code: noir_stdlib/src/embedded_curve_ops.nr#L117-L122 - - -example - -```rust -fn main() { - let point1 = EmbeddedCurvePoint { x: 1, y: 2 }; - let point2 = EmbeddedCurvePoint { x: 3, y: 4 }; - let result = std::embedded_curve_ops::embedded_curve_add(point1, point2); - println!("Resulting Point: ({}, {})", result.x, result.y); -} -``` - - diff --git a/docs/versioned_docs/version-v0.32.0/noir/standard_library/cryptographic_primitives/hashes.mdx b/docs/versioned_docs/version-v0.32.0/noir/standard_library/cryptographic_primitives/hashes.mdx deleted file mode 100644 index ddcfbb2175f..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/standard_library/cryptographic_primitives/hashes.mdx +++ /dev/null @@ -1,253 +0,0 @@ ---- -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] -sidebar_position: 0 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; - -## sha256 - -Given an array of bytes, returns the resulting sha256 hash. -Specify a message_size to hash only the first `message_size` bytes of the input. - -```rust title="sha256" showLineNumbers -pub fn sha256(input: [u8; N]) -> [u8; 32] -``` -> Source code: noir_stdlib/src/hash/mod.nr#L13-L15 - - -example: -```rust title="sha256_var" showLineNumbers -let digest = std::hash::sha256_var([x as u8], 1); -``` -> Source code: test_programs/execution_success/sha256/src/main.nr#L16-L18 - - -```rust -fn main() { - let x = [163, 117, 178, 149]; // some random bytes - let hash = std::sha256::sha256_var(x, 4); -} -``` - - - - -## blake2s - -Given an array of bytes, returns an array with the Blake2 hash - -```rust title="blake2s" showLineNumbers -pub fn blake2s(input: [u8; N]) -> [u8; 32] -``` -> Source code: noir_stdlib/src/hash/mod.nr#L19-L21 - - -example: - -```rust -fn main() { - let x = [163, 117, 178, 149]; // some random bytes - let hash = std::hash::blake2s(x); -} -``` - - - -## blake3 - -Given an array of bytes, returns an array with the Blake3 hash - -```rust title="blake3" showLineNumbers -pub fn blake3(input: [u8; N]) -> [u8; 32] -``` -> Source code: noir_stdlib/src/hash/mod.nr#L25-L27 - - -example: - -```rust -fn main() { - let x = [163, 117, 178, 149]; // some random bytes - let hash = std::hash::blake3(x); -} -``` - - - -## pedersen_hash - -Given an array of Fields, returns the Pedersen hash. - -```rust title="pedersen_hash" showLineNumbers -pub fn pedersen_hash(input: [Field; N]) -> Field -``` -> Source code: noir_stdlib/src/hash/mod.nr#L61-L63 - - -example: - -```rust title="pedersen-hash" showLineNumbers -fn main(x: Field, y: Field, expected_hash: Field) { - let hash = std::hash::pedersen_hash([x, y]); - assert_eq(hash, expected_hash); -} -``` -> Source code: test_programs/execution_success/pedersen_hash/src/main.nr#L1-L7 - - - - -## pedersen_commitment - -Given an array of Fields, returns the Pedersen commitment. - -```rust title="pedersen_commitment" showLineNumbers -pub fn pedersen_commitment(input: [Field; N]) -> EmbeddedCurvePoint { -``` -> Source code: noir_stdlib/src/hash/mod.nr#L30-L32 - - -example: - -```rust title="pedersen-commitment" showLineNumbers -fn main(x: Field, y: Field, expected_commitment: std::embedded_curve_ops::EmbeddedCurvePoint) { - let commitment = std::hash::pedersen_commitment([x, y]); - assert_eq(commitment.x, expected_commitment.x); - assert_eq(commitment.y, expected_commitment.y); -} -``` -> Source code: test_programs/execution_success/pedersen_commitment/src/main.nr#L1-L8 - - - - -## 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 title="keccak256" showLineNumbers -pub fn keccak256(input: [u8; N], message_size: u32) -> [u8; 32] -``` -> Source code: noir_stdlib/src/hash/mod.nr#L104-L106 - - -example: - -```rust title="keccak256" showLineNumbers -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 message 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); -} -``` -> Source code: test_programs/execution_success/keccak256/src/main.nr#L1-L21 - - - - -## 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 title="poseidon" showLineNumbers -use std::hash::poseidon; - -fn main(x1: [Field; 2], y1: pub Field, x2: [Field; 4], y2: pub Field) { - let hash1 = poseidon::bn254::hash_2(x1); - assert(hash1 == y1); - - let hash2 = poseidon::bn254::hash_4(x2); - assert(hash2 == y2); -} -``` -> Source code: test_programs/execution_success/poseidon_bn254_hash/src/main.nr#L1-L11 - - -## poseidon 2 - -Given an array of Fields, returns a new Field with the Poseidon2 Hash. Contrary to the Poseidon -function, there is only one hash and you can specify a message_size to hash only the first -`message_size` bytes of the input, - -```rust -// example for hashing the first three elements of the input -Poseidon2::hash(input, 3); -``` - -example: - -```rust title="poseidon2" showLineNumbers -use std::hash::poseidon2; - -fn main(inputs: [Field; 4], expected_hash: Field) { - let hash = poseidon2::Poseidon2::hash(inputs, inputs.len()); - assert_eq(hash, expected_hash); -} -``` -> Source code: test_programs/execution_success/poseidon2/src/main.nr#L1-L8 - - -## 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]) -> 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.32.0/noir/standard_library/cryptographic_primitives/index.md b/docs/versioned_docs/version-v0.32.0/noir/standard_library/cryptographic_primitives/index.md deleted file mode 100644 index 650f30165d5..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/standard_library/cryptographic_primitives/index.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -title: Cryptographic Primitives -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.32.0/noir/standard_library/cryptographic_primitives/schnorr.mdx b/docs/versioned_docs/version-v0.32.0/noir/standard_library/cryptographic_primitives/schnorr.mdx deleted file mode 100644 index 00e7f257612..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/standard_library/cryptographic_primitives/schnorr.mdx +++ /dev/null @@ -1,64 +0,0 @@ ---- -title: Schnorr Signatures -description: Learn how you can verify Schnorr signatures using Noir -keywords: [cryptographic primitives, Noir project, schnorr, signatures] -sidebar_position: 2 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; - -## schnorr::verify_signature - -Verifier for Schnorr signatures over the embedded curve (for BN254 it is Grumpkin). -See schnorr::verify_signature_slice for a version that works directly on slices. - -```rust title="schnorr_verify" showLineNumbers -pub fn verify_signature( - public_key_x: Field, - public_key_y: Field, - signature: [u8; 64], - message: [u8; N] -) -> bool -``` -> Source code: noir_stdlib/src/schnorr.nr#L2-L9 - - -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() -); - -... -``` - - - -## schnorr::verify_signature_slice - -Verifier for Schnorr signatures over the embedded curve (for BN254 it is Grumpkin) -where the message is a slice. - -```rust title="schnorr_verify_slice" showLineNumbers -pub fn verify_signature_slice( - public_key_x: Field, - public_key_y: Field, - signature: [u8; 64], - message: [u8] -) -> bool -``` -> Source code: noir_stdlib/src/schnorr.nr#L13-L20 - - - diff --git a/docs/versioned_docs/version-v0.32.0/noir/standard_library/is_unconstrained.md b/docs/versioned_docs/version-v0.32.0/noir/standard_library/is_unconstrained.md deleted file mode 100644 index 51bb1bda8f1..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/standard_library/is_unconstrained.md +++ /dev/null @@ -1,69 +0,0 @@ ---- -title: Is Unconstrained Function -description: - The is_unconstrained function returns wether the context at that point of the program is unconstrained or not. -keywords: - [ - unconstrained - ] ---- - -It's very common for functions in circuits to take unconstrained hints of an expensive computation and then verify it. This is done by running the hint in an unconstrained context and then verifying the result in a constrained context. - -When a function is marked as unconstrained, any subsequent functions that it calls will also be run in an unconstrained context. However, if we are implementing a library function, other users might call it within an unconstrained context or a constrained one. Generally, in an unconstrained context we prefer just computing the result instead of taking a hint of it and verifying it, since that'd mean doing the same computation twice: - -```rust - -fn my_expensive_computation(){ - ... -} - -unconstrained fn my_expensive_computation_hint(){ - my_expensive_computation() -} - -pub fn external_interface(){ - my_expensive_computation_hint(); - // verify my_expensive_computation: If external_interface is called from unconstrained, this is redundant - ... -} - -``` - -In order to improve the performance in an unconstrained context you can use the function at `std::runtime::is_unconstrained() -> bool`: - - -```rust -use dep::std::runtime::is_unconstrained; - -fn my_expensive_computation(){ - ... -} - -unconstrained fn my_expensive_computation_hint(){ - my_expensive_computation() -} - -pub fn external_interface(){ - if is_unconstrained() { - my_expensive_computation(); - } else { - my_expensive_computation_hint(); - // verify my_expensive_computation - ... - } -} - -``` - -The is_unconstrained result is resolved at compile time, so in unconstrained contexts the compiler removes the else branch, and in constrained contexts the compiler removes the if branch, reducing the amount of compute necessary to run external_interface. - -Note that using `is_unconstrained` in a `comptime` context will also return `true`: - -``` -fn main() { - comptime { - assert(is_unconstrained()); - } -} -``` diff --git a/docs/versioned_docs/version-v0.32.0/noir/standard_library/logging.md b/docs/versioned_docs/version-v0.32.0/noir/standard_library/logging.md deleted file mode 100644 index db75ef9f86f..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/standard_library/logging.md +++ /dev/null @@ -1,78 +0,0 @@ ---- -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, - print statement, - debugging in noir, - noir std library, - logging tutorial, - basic logging in noir, - noir logging implementation, - noir debugging techniques, - rust, - ] ---- - -The standard library provides two familiar statements you can use: `println` and `print`. Despite being a limited implementation of rust's `println!` and `print!` macros, these constructs can be useful for debugging. - -You can print the output of both statements in your Noir code by using the `nargo execute` command or the `--show-output` flag when using `nargo test` (provided there are print statements in your tests). - -It is recommended to use `nargo execute` if you want to debug failing constraints with `println` or `print` 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). Neither `println`, nor `print` are callable for failed constraints caught at compile time. - -Both `print` and `println` are generic functions which can work on integers, fields, strings, and even structs or expressions. Note however, that slices are currently unsupported. For example: - -```rust -struct Person { - age: Field, - height: Field, -} - -fn main(age: Field, height: Field) { - let person = Person { - age: age, - height: height, - }; - println(person); - println(age + height); - println("Hello world!"); -} -``` - -You can print different types in the same statement (including strings) with a type called `fmtstr`. It can be specified in the same way as a normal string, just prepended with an "f" character: - -```rust - let fmt_str = f"i: {i}, j: {j}"; - println(fmt_str); - - let s = myStruct { y: x, x: y }; - println(s); - - println(f"i: {i}, s: {s}"); - - println(x); - println([x, y]); - - let foo = fooStruct { my_struct: s, foo: 15 }; - println(f"s: {s}, foo: {foo}"); - - println(15); // prints 0x0f, implicit Field - println(-1 as u8); // prints 255 - println(-1 as i8); // prints -1 -``` - -Examples shown above are interchangeable between the two `print` statements: - -```rust -let person = Person { age : age, height : height }; - -println(person); -print(person); - -println("Hello world!"); // Prints with a newline at the end of the input -print("Hello world!"); // Prints the input and keeps cursor on the same line -``` diff --git a/docs/versioned_docs/version-v0.32.0/noir/standard_library/merkle_trees.md b/docs/versioned_docs/version-v0.32.0/noir/standard_library/merkle_trees.md deleted file mode 100644 index 6a9ebf72ada..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/standard_library/merkle_trees.md +++ /dev/null @@ -1,58 +0,0 @@ ---- -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/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.as_slice()); - 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.32.0/noir/standard_library/options.md b/docs/versioned_docs/version-v0.32.0/noir/standard_library/options.md deleted file mode 100644 index a1bd4e1de5f..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/standard_library/options.md +++ /dev/null @@ -1,101 +0,0 @@ ---- -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), -} -``` - -The `Option` type, already imported into your Noir program, can be used directly: - -```rust -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. - -### expect - -Asserts `self.is_some()` with a provided custom message and returns the contained `Some` value. The custom message is expected to be a format string. - -### 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.32.0/noir/standard_library/recursion.mdx b/docs/versioned_docs/version-v0.32.0/noir/standard_library/recursion.mdx deleted file mode 100644 index 8fdb8e8f514..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/standard_library/recursion.mdx +++ /dev/null @@ -1,85 +0,0 @@ ---- -title: Recursive Proofs -description: Learn about how to write recursive proofs in Noir. -keywords: [recursion, recursive proofs, verification_key, verify_proof] ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; - -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. - -Read [the explainer on recursion](../../explainers/explainer-recursion.md) to know more about this function and the [guide on how to use it.](../../how_to/how-to-recursion.md) - -## The `#[recursive]` Attribute - -In Noir, the `#[recursive]` attribute is used to indicate that a circuit is designed for recursive proof generation. When applied, it informs the compiler and the tooling that the circuit should be compiled in a way that makes its proofs suitable for recursive verification. This attribute eliminates the need for manual flagging of recursion at the tooling level, streamlining the proof generation process for recursive circuits. - -### Example usage with `#[recursive]` - -```rust -#[recursive] -fn main(x: Field, y: pub Field) { - assert(x == y, "x and y are not equal"); -} - -// This marks the circuit as recursion-friendly and indicates that proofs generated from this circuit -// are intended for recursive verification. -``` - -By incorporating this attribute directly in the circuit's definition, tooling like Nargo and NoirJS can automatically execute recursive-specific duties for Noir programs (e.g. recursive-friendly proof artifact generation) without additional flags or configurations. - -## Verifying Recursive Proofs - -```rust -#[foreign(recursive_aggregation)] -pub fn verify_proof(verification_key: [Field], proof: [Field], public_inputs: [Field], key_hash: Field) {} -``` - - - -## Example usage - -```rust - -fn main( - verification_key : [Field; 114], - proof : [Field; 93], - public_inputs : [Field; 1], - key_hash : Field, - proof_b : [Field; 93], -) { - std::verify_proof( - verification_key.as_slice(), - proof.as_slice(), - public_inputs.as_slice(), - key_hash - ); - - std::verify_proof( - verification_key.as_slice(), - proof_b.as_slice(), - public_inputs.as_slice(), - key_hash - ); -} -``` - -You can see a full example of recursive proofs in [this example recursion demo repo](https://github.com/noir-lang/noir-examples/tree/master/recursion). - -## 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. - -### `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. diff --git a/docs/versioned_docs/version-v0.32.0/noir/standard_library/traits.md b/docs/versioned_docs/version-v0.32.0/noir/standard_library/traits.md deleted file mode 100644 index 2ce8360bc83..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/standard_library/traits.md +++ /dev/null @@ -1,501 +0,0 @@ ---- -title: Traits -description: Noir's stdlib provides a few commonly used traits. -keywords: [traits, trait, interface, protocol, default, add, eq] ---- - -## `std::default` - -### `std::default::Default` - -```rust title="default-trait" showLineNumbers -trait Default { - fn default() -> Self; -} -``` -> Source code: noir_stdlib/src/default.nr#L1-L5 - - -Constructs a default value of a type. - -Implementations: -```rust -impl Default for Field { .. } - -impl Default for i8 { .. } -impl Default for i16 { .. } -impl Default for i32 { .. } -impl Default for i64 { .. } - -impl Default for u8 { .. } -impl Default for u16 { .. } -impl Default for u32 { .. } -impl Default for u64 { .. } - -impl Default for () { .. } -impl Default for bool { .. } - -impl Default for [T; N] - where T: Default { .. } - -impl Default for [T] { .. } - -impl Default for (A, B) - where A: Default, B: Default { .. } - -impl Default for (A, B, C) - where A: Default, B: Default, C: Default { .. } - -impl Default for (A, B, C, D) - where A: Default, B: Default, C: Default, D: Default { .. } - -impl Default for (A, B, C, D, E) - where A: Default, B: Default, C: Default, D: Default, E: Default { .. } -``` - -For primitive integer types, the return value of `default` is `0`. Container -types such as arrays are filled with default values of their element type, -except slices whose length is unknown and thus defaulted to zero. - ---- - -## `std::convert` - -### `std::convert::From` - -```rust title="from-trait" showLineNumbers -trait From { - fn from(input: T) -> Self; -} -``` -> Source code: noir_stdlib/src/convert.nr#L1-L5 - - -The `From` trait defines how to convert from a given type `T` to the type on which the trait is implemented. - -The Noir standard library provides a number of implementations of `From` between primitive types. -```rust title="from-impls" showLineNumbers -// Unsigned integers - -impl From for u32 { fn from(value: u8) -> u32 { value as u32 } } - -impl From for u64 { fn from(value: u8) -> u64 { value as u64 } } -impl From for u64 { fn from(value: u32) -> u64 { value as u64 } } - -impl From for Field { fn from(value: u8) -> Field { value as Field } } -impl From for Field { fn from(value: u32) -> Field { value as Field } } -impl From for Field { fn from(value: u64) -> Field { value as Field } } - -// Signed integers - -impl From for i32 { fn from(value: i8) -> i32 { value as i32 } } - -impl From for i64 { fn from(value: i8) -> i64 { value as i64 } } -impl From for i64 { fn from(value: i32) -> i64 { value as i64 } } - -// Booleans -impl From for u8 { fn from(value: bool) -> u8 { value as u8 } } -impl From for u32 { fn from(value: bool) -> u32 { value as u32 } } -impl From for u64 { fn from(value: bool) -> u64 { value as u64 } } -impl From for i8 { fn from(value: bool) -> i8 { value as i8 } } -impl From for i32 { fn from(value: bool) -> i32 { value as i32 } } -impl From for i64 { fn from(value: bool) -> i64 { value as i64 } } -impl From for Field { fn from(value: bool) -> Field { value as Field } } -``` -> Source code: noir_stdlib/src/convert.nr#L25-L52 - - -#### When to implement `From` - -As a general rule of thumb, `From` may be implemented in the [situations where it would be suitable in Rust](https://doc.rust-lang.org/std/convert/trait.From.html#when-to-implement-from): - -- The conversion is *infallible*: Noir does not provide an equivalent to Rust's `TryFrom`, if the conversion can fail then provide a named method instead. -- The conversion is *lossless*: semantically, it should not lose or discard information. For example, `u32: From` can losslessly convert any `u16` into a valid `u32` such that the original `u16` can be recovered. On the other hand, `u16: From` should not be implemented as `2**16` is a `u32` which cannot be losslessly converted into a `u16`. -- The conversion is *value-preserving*: the conceptual kind and meaning of the resulting value is the same, even though the Noir type and technical representation might be different. While it's possible to infallibly and losslessly convert a `u8` into a `str<2>` hex representation, `4u8` and `"04"` are too different for `str<2>: From` to be implemented. -- The conversion is *obvious*: it's the only reasonable conversion between the two types. If there's ambiguity on how to convert between them such that the same input could potentially map to two different values then a named method should be used. For instance rather than implementing `U128: From<[u8; 16]>`, the methods `U128::from_le_bytes` and `U128::from_be_bytes` are used as otherwise the endianness of the array would be ambiguous, resulting in two potential values of `U128` from the same byte array. - -One additional recommendation specific to Noir is: -- The conversion is *efficient*: it's relatively cheap to convert between the two types. Due to being a ZK DSL, it's more important to avoid unnecessary computation compared to Rust. If the implementation of `From` would encourage users to perform unnecessary conversion, resulting in additional proving time, then it may be preferable to expose functionality such that this conversion may be avoided. - -### `std::convert::Into` - -The `Into` trait is defined as the reciprocal of `From`. It should be easy to convince yourself that if we can convert to type `A` from type `B`, then it's possible to convert type `B` into type `A`. - -For this reason, implementing `From` on a type will automatically generate a matching `Into` implementation. One should always prefer implementing `From` over `Into` as implementing `Into` will not generate a matching `From` implementation. - -```rust title="into-trait" showLineNumbers -trait Into { - fn into(self) -> T; -} - -impl Into for U where T: From { - fn into(self) -> T { - T::from(self) - } -} -``` -> Source code: noir_stdlib/src/convert.nr#L13-L23 - - -`Into` is most useful when passing function arguments where the types don't quite match up with what the function expects. In this case, the compiler has enough type information to perform the necessary conversion by just appending `.into()` onto the arguments in question. - ---- - -## `std::cmp` - -### `std::cmp::Eq` - -```rust title="eq-trait" showLineNumbers -trait Eq { - fn eq(self, other: Self) -> bool; -} -``` -> Source code: noir_stdlib/src/cmp.nr#L1-L5 - - -Returns `true` if `self` is equal to `other`. Implementing this trait on a type -allows the type to be used with `==` and `!=`. - -Implementations: -```rust -impl Eq for Field { .. } - -impl Eq for i8 { .. } -impl Eq for i16 { .. } -impl Eq for i32 { .. } -impl Eq for i64 { .. } - -impl Eq for u8 { .. } -impl Eq for u16 { .. } -impl Eq for u32 { .. } -impl Eq for u64 { .. } - -impl Eq for () { .. } -impl Eq for bool { .. } - -impl Eq for [T; N] - where T: Eq { .. } - -impl Eq for [T] - where T: Eq { .. } - -impl Eq for (A, B) - where A: Eq, B: Eq { .. } - -impl Eq for (A, B, C) - where A: Eq, B: Eq, C: Eq { .. } - -impl Eq for (A, B, C, D) - where A: Eq, B: Eq, C: Eq, D: Eq { .. } - -impl Eq for (A, B, C, D, E) - where A: Eq, B: Eq, C: Eq, D: Eq, E: Eq { .. } -``` - -### `std::cmp::Ord` - -```rust title="ord-trait" showLineNumbers -trait Ord { - fn cmp(self, other: Self) -> Ordering; -} -``` -> Source code: noir_stdlib/src/cmp.nr#L102-L106 - - -`a.cmp(b)` compares two values returning `Ordering::less()` if `a < b`, -`Ordering::equal()` if `a == b`, or `Ordering::greater()` if `a > b`. -Implementing this trait on a type allows `<`, `<=`, `>`, and `>=` to be -used on values of the type. - -`std::cmp` also provides `max` and `min` functions for any type which implements the `Ord` trait. - -Implementations: - -```rust -impl Ord for u8 { .. } -impl Ord for u16 { .. } -impl Ord for u32 { .. } -impl Ord for u64 { .. } - -impl Ord for i8 { .. } -impl Ord for i16 { .. } -impl Ord for i32 { .. } - -impl Ord for i64 { .. } - -impl Ord for () { .. } -impl Ord for bool { .. } - -impl Ord for [T; N] - where T: Ord { .. } - -impl Ord for [T] - where T: Ord { .. } - -impl Ord for (A, B) - where A: Ord, B: Ord { .. } - -impl Ord for (A, B, C) - where A: Ord, B: Ord, C: Ord { .. } - -impl Ord for (A, B, C, D) - where A: Ord, B: Ord, C: Ord, D: Ord { .. } - -impl Ord for (A, B, C, D, E) - where A: Ord, B: Ord, C: Ord, D: Ord, E: Ord { .. } -``` - ---- - -## `std::ops` - -### `std::ops::Add`, `std::ops::Sub`, `std::ops::Mul`, and `std::ops::Div` - -These traits abstract over addition, subtraction, multiplication, and division respectively. -Implementing these traits for a given type will also allow that type to be used with the corresponding operator -for that trait (`+` for Add, etc) in addition to the normal method names. - -```rust title="add-trait" showLineNumbers -trait Add { - fn add(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/arith.nr#L1-L5 - -```rust title="sub-trait" showLineNumbers -trait Sub { - fn sub(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/arith.nr#L19-L23 - -```rust title="mul-trait" showLineNumbers -trait Mul { - fn mul(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/arith.nr#L37-L41 - -```rust title="div-trait" showLineNumbers -trait Div { - fn div(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/arith.nr#L55-L59 - - -The implementations block below is given for the `Add` trait, but the same types that implement -`Add` also implement `Sub`, `Mul`, and `Div`. - -Implementations: -```rust -impl Add for Field { .. } - -impl Add for i8 { .. } -impl Add for i16 { .. } -impl Add for i32 { .. } -impl Add for i64 { .. } - -impl Add for u8 { .. } -impl Add for u16 { .. } -impl Add for u32 { .. } -impl Add for u64 { .. } -``` - -### `std::ops::Rem` - -```rust title="rem-trait" showLineNumbers -trait Rem{ - fn rem(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/arith.nr#L73-L77 - - -`Rem::rem(a, b)` is the remainder function returning the result of what is -left after dividing `a` and `b`. Implementing `Rem` allows the `%` operator -to be used with the implementation type. - -Unlike other numeric traits, `Rem` is not implemented for `Field`. - -Implementations: -```rust -impl Rem for u8 { fn rem(self, other: u8) -> u8 { self % other } } -impl Rem for u16 { fn rem(self, other: u16) -> u16 { self % other } } -impl Rem for u32 { fn rem(self, other: u32) -> u32 { self % other } } -impl Rem for u64 { fn rem(self, other: u64) -> u64 { self % other } } - -impl Rem for i8 { fn rem(self, other: i8) -> i8 { self % other } } -impl Rem for i16 { fn rem(self, other: i16) -> i16 { self % other } } -impl Rem for i32 { fn rem(self, other: i32) -> i32 { self % other } } -impl Rem for i64 { fn rem(self, other: i64) -> i64 { self % other } } -``` - -### `std::ops::Neg` - -```rust title="neg-trait" showLineNumbers -trait Neg { - fn neg(self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/arith.nr#L89-L93 - - -`Neg::neg` is equivalent to the unary negation operator `-`. - -Implementations: -```rust title="neg-trait-impls" showLineNumbers -impl Neg for Field { fn neg(self) -> Field { -self } } - -impl Neg for i8 { fn neg(self) -> i8 { -self } } -impl Neg for i16 { fn neg(self) -> i16 { -self } } -impl Neg for i32 { fn neg(self) -> i32 { -self } } -impl Neg for i64 { fn neg(self) -> i64 { -self } } -``` -> Source code: noir_stdlib/src/ops/arith.nr#L95-L102 - - -### `std::ops::Not` - -```rust title="not-trait" showLineNumbers -trait Not { - fn not(self: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/bit.nr#L1-L5 - - -`Not::not` is equivalent to the unary bitwise NOT operator `!`. - -Implementations: -```rust title="not-trait-impls" showLineNumbers -impl Not for bool { fn not(self) -> bool { !self } } - -impl Not for u64 { fn not(self) -> u64 { !self } } -impl Not for u32 { fn not(self) -> u32 { !self } } -impl Not for u16 { fn not(self) -> u16 { !self } } -impl Not for u8 { fn not(self) -> u8 { !self } } -impl Not for u1 { fn not(self) -> u1 { !self } } - -impl Not for i8 { fn not(self) -> i8 { !self } } -impl Not for i16 { fn not(self) -> i16 { !self } } -impl Not for i32 { fn not(self) -> i32 { !self } } -impl Not for i64 { fn not(self) -> i64 { !self } } -``` -> Source code: noir_stdlib/src/ops/bit.nr#L7-L20 - - -### `std::ops::{ BitOr, BitAnd, BitXor }` - -```rust title="bitor-trait" showLineNumbers -trait BitOr { - fn bitor(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/bit.nr#L22-L26 - -```rust title="bitand-trait" showLineNumbers -trait BitAnd { - fn bitand(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/bit.nr#L40-L44 - -```rust title="bitxor-trait" showLineNumbers -trait BitXor { - fn bitxor(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/bit.nr#L58-L62 - - -Traits for the bitwise operations `|`, `&`, and `^`. - -Implementing `BitOr`, `BitAnd` or `BitXor` for a type allows the `|`, `&`, or `^` operator respectively -to be used with the type. - -The implementations block below is given for the `BitOr` trait, but the same types that implement -`BitOr` also implement `BitAnd` and `BitXor`. - -Implementations: -```rust -impl BitOr for bool { fn bitor(self, other: bool) -> bool { self | other } } - -impl BitOr for u8 { fn bitor(self, other: u8) -> u8 { self | other } } -impl BitOr for u16 { fn bitor(self, other: u16) -> u16 { self | other } } -impl BitOr for u32 { fn bitor(self, other: u32) -> u32 { self | other } } -impl BitOr for u64 { fn bitor(self, other: u64) -> u64 { self | other } } - -impl BitOr for i8 { fn bitor(self, other: i8) -> i8 { self | other } } -impl BitOr for i16 { fn bitor(self, other: i16) -> i16 { self | other } } -impl BitOr for i32 { fn bitor(self, other: i32) -> i32 { self | other } } -impl BitOr for i64 { fn bitor(self, other: i64) -> i64 { self | other } } -``` - -### `std::ops::{ Shl, Shr }` - -```rust title="shl-trait" showLineNumbers -trait Shl { - fn shl(self, other: u8) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/bit.nr#L76-L80 - -```rust title="shr-trait" showLineNumbers -trait Shr { - fn shr(self, other: u8) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/bit.nr#L93-L97 - - -Traits for a bit shift left and bit shift right. - -Implementing `Shl` for a type allows the left shift operator (`<<`) to be used with the implementation type. -Similarly, implementing `Shr` allows the right shift operator (`>>`) to be used with the type. - -Note that bit shifting is not currently implemented for signed types. - -The implementations block below is given for the `Shl` trait, but the same types that implement -`Shl` also implement `Shr`. - -Implementations: -```rust -impl Shl for u8 { fn shl(self, other: u8) -> u8 { self << other } } -impl Shl for u16 { fn shl(self, other: u16) -> u16 { self << other } } -impl Shl for u32 { fn shl(self, other: u32) -> u32 { self << other } } -impl Shl for u64 { fn shl(self, other: u64) -> u64 { self << other } } -``` - ---- - -## `std::append` - -### `std::append::Append` - -`Append` can abstract over types that can be appended to - usually container types: - -```rust title="append-trait" showLineNumbers -trait Append { - fn empty() -> Self; - fn append(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/append.nr#L9-L14 - - -`Append` requires two methods: - -- `empty`: Constructs an empty value of `Self`. -- `append`: Append two values together, returning the result. - -Additionally, it is expected that for any implementation: - -- `T::empty().append(x) == x` -- `x.append(T::empty()) == x` - -Implementations: -```rust -impl Append for [T] -impl Append for Quoted -``` diff --git a/docs/versioned_docs/version-v0.32.0/noir/standard_library/zeroed.md b/docs/versioned_docs/version-v0.32.0/noir/standard_library/zeroed.md deleted file mode 100644 index f450fecdd36..00000000000 --- a/docs/versioned_docs/version-v0.32.0/noir/standard_library/zeroed.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -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 -- Slice -- String -- Tuple -- Function - -Using it on other types could result in unexpected behavior. diff --git a/docs/versioned_docs/version-v0.32.0/reference/NoirJS/backend_barretenberg/.nojekyll b/docs/versioned_docs/version-v0.32.0/reference/NoirJS/backend_barretenberg/.nojekyll deleted file mode 100644 index e2ac6616add..00000000000 --- a/docs/versioned_docs/version-v0.32.0/reference/NoirJS/backend_barretenberg/.nojekyll +++ /dev/null @@ -1 +0,0 @@ -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.32.0/reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend.md b/docs/versioned_docs/version-v0.32.0/reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend.md deleted file mode 100644 index 42f065f4a4e..00000000000 --- a/docs/versioned_docs/version-v0.32.0/reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend.md +++ /dev/null @@ -1,141 +0,0 @@ -# BarretenbergBackend - -## Implements - -- [`Backend`](../index.md#backend) -- [`Backend`](../index.md#backend) - -## Constructors - -### new BarretenbergBackend(acirCircuit, options) - -```ts -new BarretenbergBackend(acirCircuit, options): BarretenbergBackend -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `acirCircuit` | `CompiledCircuit` | -| `options` | [`BackendOptions`](../type-aliases/BackendOptions.md) | - -#### Returns - -[`BarretenbergBackend`](BarretenbergBackend.md) - -## Properties - -| Property | Type | Description | -| :------ | :------ | :------ | -| `acirComposer` | `any` | - | -| `acirUncompressedBytecode` | `Uint8Array` | - | -| `api` | `Barretenberg` | - | -| `options` | [`BackendOptions`](../type-aliases/BackendOptions.md) | - | - -## Methods - -### destroy() - -```ts -destroy(): Promise -``` - -#### Returns - -`Promise`\<`void`\> - -*** - -### generateProof() - -```ts -generateProof(compressedWitness): Promise -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `compressedWitness` | `Uint8Array` | - -#### Returns - -`Promise`\<`ProofData`\> - -#### Description - -Generates a proof - -*** - -### generateRecursiveProofArtifacts() - -```ts -generateRecursiveProofArtifacts(proofData, numOfPublicInputs): Promise -``` - -Generates artifacts that will be passed to a circuit that will verify this proof. - -Instead of passing the proof and verification key as a byte array, we pass them -as fields which makes it cheaper to verify in a circuit. - -The proof that is passed here will have been created using a circuit -that has the #[recursive] attribute on its `main` method. - -The number of public inputs denotes how many public inputs are in the inner proof. - -#### Parameters - -| Parameter | Type | Default value | -| :------ | :------ | :------ | -| `proofData` | `ProofData` | `undefined` | -| `numOfPublicInputs` | `number` | `0` | - -#### Returns - -`Promise`\<`object`\> - -#### Example - -```typescript -const artifacts = await backend.generateRecursiveProofArtifacts(proof, numOfPublicInputs); -``` - -*** - -### getVerificationKey() - -```ts -getVerificationKey(): Promise -``` - -#### Returns - -`Promise`\<`Uint8Array`\> - -*** - -### verifyProof() - -```ts -verifyProof(proofData): Promise -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `proofData` | `ProofData` | - -#### Returns - -`Promise`\<`boolean`\> - -#### Description - -Verifies 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.32.0/reference/NoirJS/backend_barretenberg/classes/BarretenbergVerifier.md b/docs/versioned_docs/version-v0.32.0/reference/NoirJS/backend_barretenberg/classes/BarretenbergVerifier.md deleted file mode 100644 index 500276ea748..00000000000 --- a/docs/versioned_docs/version-v0.32.0/reference/NoirJS/backend_barretenberg/classes/BarretenbergVerifier.md +++ /dev/null @@ -1,58 +0,0 @@ -# BarretenbergVerifier - -## Constructors - -### new BarretenbergVerifier(options) - -```ts -new BarretenbergVerifier(options): BarretenbergVerifier -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `options` | [`BackendOptions`](../type-aliases/BackendOptions.md) | - -#### Returns - -[`BarretenbergVerifier`](BarretenbergVerifier.md) - -## Methods - -### destroy() - -```ts -destroy(): Promise -``` - -#### Returns - -`Promise`\<`void`\> - -*** - -### verifyProof() - -```ts -verifyProof(proofData, verificationKey): Promise -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `proofData` | `ProofData` | -| `verificationKey` | `Uint8Array` | - -#### Returns - -`Promise`\<`boolean`\> - -#### Description - -Verifies 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.32.0/reference/NoirJS/backend_barretenberg/index.md b/docs/versioned_docs/version-v0.32.0/reference/NoirJS/backend_barretenberg/index.md deleted file mode 100644 index 14dfac681d4..00000000000 --- a/docs/versioned_docs/version-v0.32.0/reference/NoirJS/backend_barretenberg/index.md +++ /dev/null @@ -1,40 +0,0 @@ -# backend_barretenberg - -## Exports - -### Classes - -| Class | Description | -| :------ | :------ | -| [BarretenbergBackend](classes/BarretenbergBackend.md) | - | -| [BarretenbergVerifier](classes/BarretenbergVerifier.md) | - | - -### Type Aliases - -| Type alias | Description | -| :------ | :------ | -| [BackendOptions](type-aliases/BackendOptions.md) | - | - -## References - -### CompiledCircuit - -Renames and re-exports [Backend](index.md#backend) - -*** - -### ProofData - -Renames and re-exports [Backend](index.md#backend) - -## Variables - -### Backend - -```ts -Backend: any; -``` - -*** - -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.32.0/reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions.md b/docs/versioned_docs/version-v0.32.0/reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions.md deleted file mode 100644 index b49a479f4f4..00000000000 --- a/docs/versioned_docs/version-v0.32.0/reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions.md +++ /dev/null @@ -1,21 +0,0 @@ -# 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 | -| :------ | :------ | :------ | -| `memory` | `object` | - | -| `memory.maximum` | `number` | - | -| `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.32.0/reference/NoirJS/backend_barretenberg/typedoc-sidebar.cjs b/docs/versioned_docs/version-v0.32.0/reference/NoirJS/backend_barretenberg/typedoc-sidebar.cjs deleted file mode 100644 index d7d5128f9e3..00000000000 --- a/docs/versioned_docs/version-v0.32.0/reference/NoirJS/backend_barretenberg/typedoc-sidebar.cjs +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-check -/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ -const typedocSidebar = { items: [{"type":"category","label":"Classes","items":[{"type":"doc","id":"reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend","label":"BarretenbergBackend"},{"type":"doc","id":"reference/NoirJS/backend_barretenberg/classes/BarretenbergVerifier","label":"BarretenbergVerifier"}]},{"type":"category","label":"Type Aliases","items":[{"type":"doc","id":"reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions","label":"BackendOptions"}]}]}; -module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.32.0/reference/NoirJS/noir_js/.nojekyll b/docs/versioned_docs/version-v0.32.0/reference/NoirJS/noir_js/.nojekyll deleted file mode 100644 index e2ac6616add..00000000000 --- a/docs/versioned_docs/version-v0.32.0/reference/NoirJS/noir_js/.nojekyll +++ /dev/null @@ -1 +0,0 @@ -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.32.0/reference/NoirJS/noir_js/classes/Noir.md b/docs/versioned_docs/version-v0.32.0/reference/NoirJS/noir_js/classes/Noir.md deleted file mode 100644 index ead255bc504..00000000000 --- a/docs/versioned_docs/version-v0.32.0/reference/NoirJS/noir_js/classes/Noir.md +++ /dev/null @@ -1,52 +0,0 @@ -# Noir - -## Constructors - -### new Noir(circuit) - -```ts -new Noir(circuit): Noir -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `circuit` | `CompiledCircuit` | - -#### Returns - -[`Noir`](Noir.md) - -## Methods - -### 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) -``` - -*** - -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.32.0/reference/NoirJS/noir_js/functions/and.md b/docs/versioned_docs/version-v0.32.0/reference/NoirJS/noir_js/functions/and.md deleted file mode 100644 index c783283e396..00000000000 --- a/docs/versioned_docs/version-v0.32.0/reference/NoirJS/noir_js/functions/and.md +++ /dev/null @@ -1,22 +0,0 @@ -# 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.32.0/reference/NoirJS/noir_js/functions/blake2s256.md b/docs/versioned_docs/version-v0.32.0/reference/NoirJS/noir_js/functions/blake2s256.md deleted file mode 100644 index 7882d0da8d5..00000000000 --- a/docs/versioned_docs/version-v0.32.0/reference/NoirJS/noir_js/functions/blake2s256.md +++ /dev/null @@ -1,21 +0,0 @@ -# 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.32.0/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md b/docs/versioned_docs/version-v0.32.0/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md deleted file mode 100644 index 5e3cd53e9d3..00000000000 --- a/docs/versioned_docs/version-v0.32.0/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md +++ /dev/null @@ -1,28 +0,0 @@ -# ecdsa\_secp256k1\_verify() - -```ts -ecdsa_secp256k1_verify( - hashed_msg, - public_key_x_bytes, - public_key_y_bytes, - signature): boolean -``` - -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.32.0/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md b/docs/versioned_docs/version-v0.32.0/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md deleted file mode 100644 index 0b20ff68957..00000000000 --- a/docs/versioned_docs/version-v0.32.0/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md +++ /dev/null @@ -1,28 +0,0 @@ -# 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.32.0/reference/NoirJS/noir_js/functions/keccak256.md b/docs/versioned_docs/version-v0.32.0/reference/NoirJS/noir_js/functions/keccak256.md deleted file mode 100644 index d10f155ce86..00000000000 --- a/docs/versioned_docs/version-v0.32.0/reference/NoirJS/noir_js/functions/keccak256.md +++ /dev/null @@ -1,21 +0,0 @@ -# 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.32.0/reference/NoirJS/noir_js/functions/sha256.md b/docs/versioned_docs/version-v0.32.0/reference/NoirJS/noir_js/functions/sha256.md deleted file mode 100644 index 6ba4ecac022..00000000000 --- a/docs/versioned_docs/version-v0.32.0/reference/NoirJS/noir_js/functions/sha256.md +++ /dev/null @@ -1,21 +0,0 @@ -# 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.32.0/reference/NoirJS/noir_js/functions/xor.md b/docs/versioned_docs/version-v0.32.0/reference/NoirJS/noir_js/functions/xor.md deleted file mode 100644 index 8d762b895d3..00000000000 --- a/docs/versioned_docs/version-v0.32.0/reference/NoirJS/noir_js/functions/xor.md +++ /dev/null @@ -1,22 +0,0 @@ -# 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.32.0/reference/NoirJS/noir_js/index.md b/docs/versioned_docs/version-v0.32.0/reference/NoirJS/noir_js/index.md deleted file mode 100644 index 166508f7124..00000000000 --- a/docs/versioned_docs/version-v0.32.0/reference/NoirJS/noir_js/index.md +++ /dev/null @@ -1,49 +0,0 @@ -# noir_js - -## Exports - -### Classes - -| Class | Description | -| :------ | :------ | -| [Noir](classes/Noir.md) | - | - -### Type Aliases - -| Type alias | Description | -| :------ | :------ | -| [ErrorWithPayload](type-aliases/ErrorWithPayload.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) | - | -| [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) | Verifies a ECDSA signature over the secp256k1 curve. | -| [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` | - -## References - -### CompiledCircuit - -Renames and re-exports [InputMap](index.md#inputmap) - -## Variables - -### InputMap - -```ts -InputMap: any; -``` - -*** - -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.32.0/reference/NoirJS/noir_js/type-aliases/ErrorWithPayload.md b/docs/versioned_docs/version-v0.32.0/reference/NoirJS/noir_js/type-aliases/ErrorWithPayload.md deleted file mode 100644 index e8c2f4aef3d..00000000000 --- a/docs/versioned_docs/version-v0.32.0/reference/NoirJS/noir_js/type-aliases/ErrorWithPayload.md +++ /dev/null @@ -1,15 +0,0 @@ -# ErrorWithPayload - -```ts -type ErrorWithPayload: ExecutionError & object; -``` - -## Type declaration - -| Member | Type | Description | -| :------ | :------ | :------ | -| `decodedAssertionPayload` | `any` | - | - -*** - -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.32.0/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md b/docs/versioned_docs/version-v0.32.0/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md deleted file mode 100644 index 812b8b16481..00000000000 --- a/docs/versioned_docs/version-v0.32.0/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md +++ /dev/null @@ -1,24 +0,0 @@ -# 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.32.0/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md b/docs/versioned_docs/version-v0.32.0/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md deleted file mode 100644 index dd95809186a..00000000000 --- a/docs/versioned_docs/version-v0.32.0/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md +++ /dev/null @@ -1,9 +0,0 @@ -# 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.32.0/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md b/docs/versioned_docs/version-v0.32.0/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md deleted file mode 100644 index b71fb78a946..00000000000 --- a/docs/versioned_docs/version-v0.32.0/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md +++ /dev/null @@ -1,9 +0,0 @@ -# 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.32.0/reference/NoirJS/noir_js/type-aliases/WitnessMap.md b/docs/versioned_docs/version-v0.32.0/reference/NoirJS/noir_js/type-aliases/WitnessMap.md deleted file mode 100644 index 258c46f9d0c..00000000000 --- a/docs/versioned_docs/version-v0.32.0/reference/NoirJS/noir_js/type-aliases/WitnessMap.md +++ /dev/null @@ -1,9 +0,0 @@ -# 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.32.0/reference/NoirJS/noir_js/typedoc-sidebar.cjs b/docs/versioned_docs/version-v0.32.0/reference/NoirJS/noir_js/typedoc-sidebar.cjs deleted file mode 100644 index b3156097df6..00000000000 --- a/docs/versioned_docs/version-v0.32.0/reference/NoirJS/noir_js/typedoc-sidebar.cjs +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-check -/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ -const typedocSidebar = { items: [{"type":"category","label":"Classes","items":[{"type":"doc","id":"reference/NoirJS/noir_js/classes/Noir","label":"Noir"}]},{"type":"category","label":"Type Aliases","items":[{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ErrorWithPayload","label":"ErrorWithPayload"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ForeignCallHandler","label":"ForeignCallHandler"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ForeignCallInput","label":"ForeignCallInput"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ForeignCallOutput","label":"ForeignCallOutput"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/WitnessMap","label":"WitnessMap"}]},{"type":"category","label":"Functions","items":[{"type":"doc","id":"reference/NoirJS/noir_js/functions/and","label":"and"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/blake2s256","label":"blake2s256"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify","label":"ecdsa_secp256k1_verify"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify","label":"ecdsa_secp256r1_verify"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/keccak256","label":"keccak256"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/sha256","label":"sha256"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/xor","label":"xor"}]}]}; -module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.32.0/reference/NoirJS/noir_wasm/.nojekyll b/docs/versioned_docs/version-v0.32.0/reference/NoirJS/noir_wasm/.nojekyll deleted file mode 100644 index e2ac6616add..00000000000 --- a/docs/versioned_docs/version-v0.32.0/reference/NoirJS/noir_wasm/.nojekyll +++ /dev/null @@ -1 +0,0 @@ -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.32.0/reference/NoirJS/noir_wasm/functions/compile.md b/docs/versioned_docs/version-v0.32.0/reference/NoirJS/noir_wasm/functions/compile.md deleted file mode 100644 index 6faf763b37f..00000000000 --- a/docs/versioned_docs/version-v0.32.0/reference/NoirJS/noir_wasm/functions/compile.md +++ /dev/null @@ -1,51 +0,0 @@ -# compile() - -```ts -compile( - fileManager, - projectPath?, - logFn?, -debugLogFn?): Promise -``` - -Compiles a Noir project - -## Parameters - -| Parameter | Type | Description | -| :------ | :------ | :------ | -| `fileManager` | `FileManager` | The file manager to use | -| `projectPath`? | `string` | The path to the project inside the file manager. Defaults to the root of the file manager | -| `logFn`? | `LogFn` | A logging function. If not provided, console.log will be used | -| `debugLogFn`? | `LogFn` | A debug logging function. If not provided, logFn will be used | - -## Returns - -`Promise`\<[`ProgramCompilationArtifacts`](../index.md#programcompilationartifacts)\> - -## Example - -```typescript -// Node.js - -import { compile_program, createFileManager } from '@noir-lang/noir_wasm'; - -const fm = createFileManager(myProjectPath); -const myCompiledCode = await compile_program(fm); -``` - -```typescript -// Browser - -import { compile_program, createFileManager } from '@noir-lang/noir_wasm'; - -const fm = createFileManager('/'); -for (const path of files) { - await fm.writeFile(path, await getFileAsStream(path)); -} -const myCompiledCode = await compile_program(fm); -``` - -*** - -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.32.0/reference/NoirJS/noir_wasm/functions/compile_contract.md b/docs/versioned_docs/version-v0.32.0/reference/NoirJS/noir_wasm/functions/compile_contract.md deleted file mode 100644 index 7d0b39a43ef..00000000000 --- a/docs/versioned_docs/version-v0.32.0/reference/NoirJS/noir_wasm/functions/compile_contract.md +++ /dev/null @@ -1,51 +0,0 @@ -# compile\_contract() - -```ts -compile_contract( - fileManager, - projectPath?, - logFn?, -debugLogFn?): Promise -``` - -Compiles a Noir project - -## Parameters - -| Parameter | Type | Description | -| :------ | :------ | :------ | -| `fileManager` | `FileManager` | The file manager to use | -| `projectPath`? | `string` | The path to the project inside the file manager. Defaults to the root of the file manager | -| `logFn`? | `LogFn` | A logging function. If not provided, console.log will be used | -| `debugLogFn`? | `LogFn` | A debug logging function. If not provided, logFn will be used | - -## Returns - -`Promise`\<[`ContractCompilationArtifacts`](../index.md#contractcompilationartifacts)\> - -## Example - -```typescript -// Node.js - -import { compile_contract, createFileManager } from '@noir-lang/noir_wasm'; - -const fm = createFileManager(myProjectPath); -const myCompiledCode = await compile_contract(fm); -``` - -```typescript -// Browser - -import { compile_contract, createFileManager } from '@noir-lang/noir_wasm'; - -const fm = createFileManager('/'); -for (const path of files) { - await fm.writeFile(path, await getFileAsStream(path)); -} -const myCompiledCode = await compile_contract(fm); -``` - -*** - -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.32.0/reference/NoirJS/noir_wasm/functions/createFileManager.md b/docs/versioned_docs/version-v0.32.0/reference/NoirJS/noir_wasm/functions/createFileManager.md deleted file mode 100644 index 7e65c1d69c7..00000000000 --- a/docs/versioned_docs/version-v0.32.0/reference/NoirJS/noir_wasm/functions/createFileManager.md +++ /dev/null @@ -1,21 +0,0 @@ -# createFileManager() - -```ts -createFileManager(dataDir): FileManager -``` - -Creates a new FileManager instance based on fs in node and memfs in the browser (via webpack alias) - -## Parameters - -| Parameter | Type | Description | -| :------ | :------ | :------ | -| `dataDir` | `string` | root of the file system | - -## Returns - -`FileManager` - -*** - -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.32.0/reference/NoirJS/noir_wasm/functions/inflateDebugSymbols.md b/docs/versioned_docs/version-v0.32.0/reference/NoirJS/noir_wasm/functions/inflateDebugSymbols.md deleted file mode 100644 index fcea9275341..00000000000 --- a/docs/versioned_docs/version-v0.32.0/reference/NoirJS/noir_wasm/functions/inflateDebugSymbols.md +++ /dev/null @@ -1,21 +0,0 @@ -# inflateDebugSymbols() - -```ts -inflateDebugSymbols(debugSymbols): any -``` - -Decompresses and decodes the debug symbols - -## Parameters - -| Parameter | Type | Description | -| :------ | :------ | :------ | -| `debugSymbols` | `string` | The base64 encoded debug symbols | - -## Returns - -`any` - -*** - -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.32.0/reference/NoirJS/noir_wasm/index.md b/docs/versioned_docs/version-v0.32.0/reference/NoirJS/noir_wasm/index.md deleted file mode 100644 index b6e0f9d1bc0..00000000000 --- a/docs/versioned_docs/version-v0.32.0/reference/NoirJS/noir_wasm/index.md +++ /dev/null @@ -1,49 +0,0 @@ -# noir_wasm - -## Exports - -### Functions - -| Function | Description | -| :------ | :------ | -| [compile](functions/compile.md) | Compiles a Noir project | -| [compile\_contract](functions/compile_contract.md) | Compiles a Noir project | -| [createFileManager](functions/createFileManager.md) | Creates a new FileManager instance based on fs in node and memfs in the browser (via webpack alias) | -| [inflateDebugSymbols](functions/inflateDebugSymbols.md) | Decompresses and decodes the debug symbols | - -## References - -### compile\_program - -Renames and re-exports [compile](functions/compile.md) - -## Interfaces - -### ContractCompilationArtifacts - -The compilation artifacts of a given contract. - -#### Properties - -| Property | Type | Description | -| :------ | :------ | :------ | -| `contract` | `ContractArtifact` | The compiled contract. | -| `warnings` | `unknown`[] | Compilation warnings. | - -*** - -### ProgramCompilationArtifacts - -The compilation artifacts of a given program. - -#### Properties - -| Property | Type | Description | -| :------ | :------ | :------ | -| `name` | `string` | not part of the compilation output, injected later | -| `program` | `ProgramArtifact` | The compiled contract. | -| `warnings` | `unknown`[] | Compilation warnings. | - -*** - -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.32.0/reference/NoirJS/noir_wasm/typedoc-sidebar.cjs b/docs/versioned_docs/version-v0.32.0/reference/NoirJS/noir_wasm/typedoc-sidebar.cjs deleted file mode 100644 index e0870710349..00000000000 --- a/docs/versioned_docs/version-v0.32.0/reference/NoirJS/noir_wasm/typedoc-sidebar.cjs +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-check -/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ -const typedocSidebar = { items: [{"type":"doc","id":"reference/NoirJS/noir_wasm/index","label":"API"},{"type":"category","label":"Functions","items":[{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/compile","label":"compile"},{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/compile_contract","label":"compile_contract"},{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/createFileManager","label":"createFileManager"},{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/inflateDebugSymbols","label":"inflateDebugSymbols"}]}]}; -module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.32.0/reference/_category_.json b/docs/versioned_docs/version-v0.32.0/reference/_category_.json deleted file mode 100644 index 5b6a20a609a..00000000000 --- a/docs/versioned_docs/version-v0.32.0/reference/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "position": 4, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.32.0/reference/debugger/_category_.json b/docs/versioned_docs/version-v0.32.0/reference/debugger/_category_.json deleted file mode 100644 index 27869205ad3..00000000000 --- a/docs/versioned_docs/version-v0.32.0/reference/debugger/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "Debugger", - "position": 1, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.32.0/reference/debugger/debugger_known_limitations.md b/docs/versioned_docs/version-v0.32.0/reference/debugger/debugger_known_limitations.md deleted file mode 100644 index 936d416ac4b..00000000000 --- a/docs/versioned_docs/version-v0.32.0/reference/debugger/debugger_known_limitations.md +++ /dev/null @@ -1,59 +0,0 @@ ---- -title: Known limitations -description: - An overview of known limitations of the current version of the Noir debugger -keywords: - [ - Nargo, - Noir Debugger, - VS Code, - ] -sidebar_position: 2 ---- - -# Debugger Known Limitations - -There are currently some limits to what the debugger can observe. - -## Mutable references - -The debugger is currently blind to any state mutated via a mutable reference. For example, in: - -``` -let mut x = 1; -let y = &mut x; -*y = 2; -``` - -The update on `x` will not be observed by the debugger. That means, when running `vars` from the debugger REPL, or inspecting the _local variables_ pane in the VS Code debugger, `x` will appear with value 1 despite having executed `*y = 2;`. - -## Variables of type function or mutable references are opaque - -When inspecting variables, any variable of type `Function` or `MutableReference` will render its value as `<>` or `<>`. - -## Debugger instrumentation affects resulting ACIR - -In order to make the state of local variables observable, the debugger compiles Noir circuits interleaving foreign calls that track any mutations to them. While this works (except in the cases described above) and doesn't introduce any behavior changes, it does as a side effect produce bigger bytecode. In particular, when running the command `opcodes` on the REPL debugger, you will notice Unconstrained VM blocks that look like this: - -``` -... -5 BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [], q_c: 2 }), Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(2))], q_c: 0 })] - | outputs=[] - 5.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } - 5.1 | Mov { destination: RegisterIndex(3), source: RegisterIndex(1) } - 5.2 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } - 5.3 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } - 5.4 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } - 5.5 | Mov { destination: RegisterIndex(3), source: RegisterIndex(3) } - 5.6 | Call { location: 8 } - 5.7 | Stop - 5.8 | ForeignCall { function: "__debug_var_assign", destinations: [], inputs: [RegisterIndex(RegisterIndex(2)), RegisterIndex(RegisterIndex(3))] } -... -``` - -If you are interested in debugging/inspecting compiled ACIR without these synthetic changes, you can invoke the REPL debugger with the `--skip-instrumentation` flag or launch the VS Code debugger with the `skipConfiguration` property set to true in its launch configuration. You can find more details about those in the [Debugger REPL reference](debugger_repl.md) and the [VS Code Debugger reference](debugger_vscode.md). - -:::note -Skipping debugger instrumentation means you won't be able to inspect values of local variables. -::: - diff --git a/docs/versioned_docs/version-v0.32.0/reference/debugger/debugger_repl.md b/docs/versioned_docs/version-v0.32.0/reference/debugger/debugger_repl.md deleted file mode 100644 index 46e2011304e..00000000000 --- a/docs/versioned_docs/version-v0.32.0/reference/debugger/debugger_repl.md +++ /dev/null @@ -1,360 +0,0 @@ ---- -title: REPL Debugger -description: - Noir Debugger REPL options and commands. -keywords: - [ - Nargo, - Noir CLI, - Noir Debugger, - REPL, - ] -sidebar_position: 1 ---- - -## Running the REPL debugger - -`nargo debug [OPTIONS] [WITNESS_NAME]` - -Runs the Noir REPL debugger. If a `WITNESS_NAME` is provided the debugger writes the resulting execution witness to a `WITNESS_NAME` 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 debug | -| `--print-acir` | Display the ACIR for compiled circuit | -| `--deny-warnings` | Treat all warnings as errors | -| `--silence-warnings` | Suppress warnings | -| `-h, --help` | Print help | - -None of these options are required. - -:::note -Since the debugger starts by compiling the target package, all Noir compiler options are also available. Check out the [compiler reference](../nargo_commands.md#nargo-compile) to learn more about the compiler options. -::: - -## REPL commands - -Once the debugger is running, it accepts the following commands. - -#### `help` (h) - -Displays the menu of available commands. - -``` -> help -Available commands: - - opcodes display ACIR opcodes - into step into to the next opcode - next step until a new source location is reached - out step until a new source location is reached - and the current stack frame is finished - break LOCATION:OpcodeLocation add a breakpoint at an opcode location - over step until a new source location is reached - without diving into function calls - restart restart the debugging session - delete LOCATION:OpcodeLocation delete breakpoint at an opcode location - witness show witness map - witness index:u32 display a single witness from the witness map - witness index:u32 value:String update a witness with the given value - memset index:usize value:String update a memory cell with the given - value - continue continue execution until the end of the - program - vars show variable values available at this point - in execution - stacktrace display the current stack trace - memory show memory (valid when executing unconstrained code) value - step step to the next ACIR opcode - -Other commands: - - help Show this help message - quit Quit repl - -``` - -### Stepping through programs - -#### `next` (n) - -Step until the next Noir source code location. While other commands, such as [`into`](#into-i) and [`step`](#step-s), allow for finer grained control of the program's execution at the opcode level, `next` is source code centric. For example: - -``` -3 ... -4 fn main(x: u32) { -5 assert(entry_point(x) == 2); -6 swap_entry_point(x, x + 1); -7 -> assert(deep_entry_point(x) == 4); -8 multiple_values_entry_point(x); -9 } -``` - - -Using `next` here would cause the debugger to jump to the definition of `deep_entry_point` (if available). - -If you want to step over `deep_entry_point` and go straight to line 8, use [the `over` command](#over) instead. - -#### `over` - -Step until the next source code location, without diving into function calls. For example: - -``` -3 ... -4 fn main(x: u32) { -5 assert(entry_point(x) == 2); -6 swap_entry_point(x, x + 1); -7 -> assert(deep_entry_point(x) == 4); -8 multiple_values_entry_point(x); -9 } -``` - - -Using `over` here would cause the debugger to execute until line 8 (`multiple_values_entry_point(x);`). - -If you want to step into `deep_entry_point` instead, use [the `next` command](#next-n). - -#### `out` - -Step until the end of the current function call. For example: - -``` - 3 ... - 4 fn main(x: u32) { - 5 assert(entry_point(x) == 2); - 6 swap_entry_point(x, x + 1); - 7 -> assert(deep_entry_point(x) == 4); - 8 multiple_values_entry_point(x); - 9 } - 10 - 11 unconstrained fn returns_multiple_values(x: u32) -> (u32, u32, u32, u32) { - 12 ... - ... - 55 - 56 unconstrained fn deep_entry_point(x: u32) -> u32 { - 57 -> level_1(x + 1) - 58 } - -``` - -Running `out` here will resume execution until line 8. - -#### `step` (s) - -Skips to the next ACIR code. A compiled Noir program is a sequence of ACIR opcodes. However, an unconstrained VM opcode denotes the start of an unconstrained code block, to be executed by the unconstrained VM. For example (redacted for brevity): - -``` -0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] -1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] - 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } - 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } - 1.2 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } - 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } - 1.4 | Call { location: 7 } - ... - 1.43 | Return -2 EXPR [ (1, _1) -2 ] -``` - -The `->` here shows the debugger paused at an ACIR opcode: `BRILLIG`, at index 1, which denotes an unconstrained code block is about to start. - -Using the `step` command at this point would result in the debugger stopping at ACIR opcode 2, `EXPR`, skipping unconstrained computation steps. - -Use [the `into` command](#into-i) instead if you want to follow unconstrained computation step by step. - -#### `into` (i) - -Steps into the next opcode. A compiled Noir program is a sequence of ACIR opcodes. However, a BRILLIG opcode denotes the start of an unconstrained code block, to be executed by the unconstrained VM. For example (redacted for brevity): - -``` -0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] -1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] - 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } - 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } - 1.2 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } - 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } - 1.4 | Call { location: 7 } - ... - 1.43 | Return -2 EXPR [ (1, _1) -2 ] -``` - -The `->` here shows the debugger paused at an ACIR opcode: `BRILLIG`, at index 1, which denotes an unconstrained code block is about to start. - -Using the `into` command at this point would result in the debugger stopping at opcode 1.0, `Mov ...`, allowing the debugger user to follow unconstrained computation step by step. - -Use [the `step` command](#step-s) instead if you want to skip to the next ACIR code directly. - -#### `continue` (c) - -Continues execution until the next breakpoint, or the end of the program. - -#### `restart` (res) - -Interrupts execution, and restarts a new debugging session from scratch. - -#### `opcodes` (o) - -Display the program's ACIR opcode sequence. For example: - -``` -0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] -1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] - 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } - 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } - 1.2 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } - 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } - 1.4 | Call { location: 7 } - ... - 1.43 | Return -2 EXPR [ (1, _1) -2 ] -``` - -### Breakpoints - -#### `break [Opcode]` (or shorthand `b [Opcode]`) - -Sets a breakpoint on the specified opcode index. To get a list of the program opcode numbers, see [the `opcode` command](#opcodes-o). For example: - -``` -0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] -1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] - 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } - 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } - 1.2 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } - 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } - 1.4 | Call { location: 7 } - ... - 1.43 | Return -2 EXPR [ (1, _1) -2 ] -``` - -In this example, issuing a `break 1.2` command adds break on opcode 1.2, as denoted by the `*` character: - -``` -0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] -1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] - 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } - 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } - 1.2 | * Const { destination: RegisterIndex(1), value: Value { inner: 0 } } - 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } - 1.4 | Call { location: 7 } - ... - 1.43 | Return -2 EXPR [ (1, _1) -2 ] -``` - -Running [the `continue` command](#continue-c) at this point would cause the debugger to execute the program until opcode 1.2. - -#### `delete [Opcode]` (or shorthand `d [Opcode]`) - -Deletes a breakpoint at an opcode location. Usage is analogous to [the `break` command](#). - -### Variable inspection - -#### vars - -Show variable values available at this point in execution. - -:::note -The ability to inspect variable values from the debugger depends on compilation to be run in a special debug instrumentation mode. This instrumentation weaves variable tracing code with the original source code. - -So variable value inspection comes at the expense of making the resulting ACIR bytecode bigger and harder to understand and optimize. - -If you find this compromise unacceptable, you can run the debugger with the flag `--skip-debug-instrumentation`. This will compile your circuit without any additional debug information, so the resulting ACIR bytecode will be identical to the one produced by standard Noir compilation. However, if you opt for this, the `vars` command will not be available while debugging. -::: - - -### Stacktrace - -#### `stacktrace` - -Displays the current stack trace. - - -### Witness map - -#### `witness` (w) - -Show witness map. For example: - -``` -_0 = 0 -_1 = 2 -_2 = 1 -``` - -#### `witness [Witness Index]` - -Display a single witness from the witness map. For example: - -``` -> witness 1 -_1 = 2 -``` - -#### `witness [Witness Index] [New value]` - -Overwrite the given index with a new value. For example: - -``` -> witness 1 3 -_1 = 3 -``` - - -### Unconstrained VM memory - -#### `memory` - -Show unconstrained VM memory state. For example: - -``` -> memory -At opcode 1.13: Store { destination_pointer: RegisterIndex(0), source: RegisterIndex(3) } -... -> registers -0 = 0 -1 = 10 -2 = 0 -3 = 1 -4 = 1 -5 = 2³² -6 = 1 -> into -At opcode 1.14: Const { destination: RegisterIndex(5), value: Value { inner: 1 } } -... -> memory -0 = 1 -> -``` - -In the example above: we start with clean memory, then step through a `Store` opcode which stores the value of register 3 (1) into the memory address stored in register 0 (0). Thus now `memory` shows memory address 0 contains value 1. - -:::note -This command is only functional while the debugger is executing unconstrained code. -::: - -#### `memset [Memory address] [New value]` - -Update a memory cell with the given value. For example: - -``` -> memory -0 = 1 -> memset 0 2 -> memory -0 = 2 -> memset 1 4 -> memory -0 = 2 -1 = 4 -> -``` - -:::note -This command is only functional while the debugger is executing unconstrained code. -::: \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.32.0/reference/debugger/debugger_vscode.md b/docs/versioned_docs/version-v0.32.0/reference/debugger/debugger_vscode.md deleted file mode 100644 index c027332b3b0..00000000000 --- a/docs/versioned_docs/version-v0.32.0/reference/debugger/debugger_vscode.md +++ /dev/null @@ -1,82 +0,0 @@ ---- -title: VS Code Debugger -description: - VS Code Debugger configuration and features. -keywords: - [ - Nargo, - Noir CLI, - Noir Debugger, - VS Code, - IDE, - ] -sidebar_position: 0 ---- - -# VS Code Noir Debugger Reference - -The Noir debugger enabled by the vscode-noir extension ships with default settings such that the most common scenario should run without any additional configuration steps. - -These defaults can nevertheless be overridden by defining a launch configuration file. This page provides a reference for the properties you can override via a launch configuration file, as well as documenting the Nargo `dap` command, which is a dependency of the VS Code Noir debugger. - - -## Creating and editing launch configuration files - -To create a launch configuration file from VS Code, open the _debug pane_, and click on _create a launch.json file_. - -![Creating a launch configuration file](@site/static/img/debugger/ref1-create-launch.png) - -A `launch.json` file will be created, populated with basic defaults. - -### Noir Debugger launch.json properties - -#### projectFolder - -_String, optional._ - -Absolute path to the Nargo project to debug. By default, it is dynamically determined by looking for the nearest `Nargo.toml` file to the active file at the moment of launching the debugger. - -#### proverName - -_String, optional._ - -Name of the prover input to use. Defaults to `Prover`, which looks for a file named `Prover.toml` at the `projectFolder`. - -#### generateAcir - -_Boolean, optional._ - -If true, generate ACIR opcodes instead of unconstrained opcodes which will be closer to release binaries but less convenient for debugging. Defaults to `false`. - -#### skipInstrumentation - -_Boolean, optional._ - -Skips variables debugging instrumentation of code, making debugging less convenient but the resulting binary smaller and closer to production. Defaults to `false`. - -:::note -Skipping instrumentation causes the debugger to be unable to inspect local variables. -::: - -## `nargo dap [OPTIONS]` - -When run without any option flags, it starts the Nargo Debug Adapter Protocol server, which acts as the debugging backend for the VS Code Noir Debugger. - -All option flags are related to preflight checks. The Debug Adapter Protocol specifies how errors are to be informed from a running DAP server, but it doesn't specify mechanisms to communicate server initialization errors between the DAP server and its client IDE. - -Thus `nargo dap` ships with a _preflight check_ mode. If flag `--preflight-check` and the rest of the `--preflight-*` flags are provided, Nargo will run the same initialization routine except it will not start the DAP server. - -`vscode-noir` will then run `nargo dap` in preflight check mode first before a debugging session starts. If the preflight check ends in error, vscode-noir will present stderr and stdout output from this process through its own Output pane in VS Code. This makes it possible for users to diagnose what pieces of configuration might be wrong or missing in case of initialization errors. - -If the preflight check succeeds, `vscode-noir` proceeds to start the DAP server normally but running `nargo dap` without any additional flags. - -### Options - -| Option | Description | -| --------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | -| `--preflight-check` | If present, dap runs in preflight check mode. | -| `--preflight-project-folder ` | Absolute path to the project to debug for preflight check. | -| `--preflight-prover-name ` | Name of prover file to use for preflight check | -| `--preflight-generate-acir` | Optional. If present, compile in ACIR mode while running preflight check. | -| `--preflight-skip-instrumentation` | Optional. If present, compile without introducing debug instrumentation while running preflight check. | -| `-h, --help` | Print help. | diff --git a/docs/versioned_docs/version-v0.32.0/reference/nargo_commands.md b/docs/versioned_docs/version-v0.32.0/reference/nargo_commands.md deleted file mode 100644 index 2d6defc5a44..00000000000 --- a/docs/versioned_docs/version-v0.32.0/reference/nargo_commands.md +++ /dev/null @@ -1,271 +0,0 @@ ---- -title: Nargo -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, - ] -sidebar_position: 0 ---- - -# Command-Line Help for `nargo` - -This document contains the help content for the `nargo` command-line program. - -**Command Overview:** - -* [`nargo`↴](#nargo) -* [`nargo check`↴](#nargo-check) -* [`nargo fmt`↴](#nargo-fmt) -* [`nargo compile`↴](#nargo-compile) -* [`nargo new`↴](#nargo-new) -* [`nargo init`↴](#nargo-init) -* [`nargo execute`↴](#nargo-execute) -* [`nargo debug`↴](#nargo-debug) -* [`nargo test`↴](#nargo-test) -* [`nargo info`↴](#nargo-info) -* [`nargo lsp`↴](#nargo-lsp) - -## `nargo` - -Noir's package manager - -**Usage:** `nargo ` - -###### **Subcommands:** - -* `check` — Checks the constraint system for errors -* `fmt` — Format the Noir files in a workspace -* `compile` — Compile the program and its secret execution trace into ACIR format -* `new` — Create a Noir project in a new directory -* `init` — Create a Noir project in the current directory -* `execute` — Executes a circuit to calculate its return value -* `debug` — Executes a circuit in debug mode -* `test` — Run the tests for this program -* `info` — Provides detailed information on each of a program's function (represented by a single circuit) -* `lsp` — Starts the Noir LSP server - -###### **Options:** - - - - -## `nargo check` - -Checks the constraint system for errors - -**Usage:** `nargo check [OPTIONS]` - -###### **Options:** - -* `--package ` — The name of the package to check -* `--workspace` — Check all packages in the workspace -* `--overwrite` — Force overwrite of existing files -* `--expression-width ` — Specify the backend expression width that should be targeted -* `--force` — Force a full recompilation -* `--print-acir` — Display the ACIR for compiled circuit -* `--deny-warnings` — Treat all warnings as errors -* `--silence-warnings` — Suppress warnings -* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" - - - -## `nargo fmt` - -Format the Noir files in a workspace - -**Usage:** `nargo fmt [OPTIONS]` - -###### **Options:** - -* `--check` — Run noirfmt in check mode - - - -## `nargo compile` - -Compile the program and its secret execution trace into ACIR format - -**Usage:** `nargo compile [OPTIONS]` - -###### **Options:** - -* `--package ` — The name of the package to compile -* `--workspace` — Compile all packages in the workspace -* `--expression-width ` — Specify the backend expression width that should be targeted -* `--force` — Force a full recompilation -* `--print-acir` — Display the ACIR for compiled circuit -* `--deny-warnings` — Treat all warnings as errors -* `--silence-warnings` — Suppress warnings -* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" - - - -## `nargo new` - -Create a Noir project in a new directory - -**Usage:** `nargo new [OPTIONS] ` - -###### **Arguments:** - -* `` — The path to save the new project - -###### **Options:** - -* `--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 - - - -## `nargo init` - -Create a Noir project in the current directory - -**Usage:** `nargo init [OPTIONS]` - -###### **Options:** - -* `--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 - - - -## `nargo execute` - -Executes a circuit to calculate its return value - -**Usage:** `nargo execute [OPTIONS] [WITNESS_NAME]` - -###### **Arguments:** - -* `` — Write the execution witness to named file - -###### **Options:** - -* `-p`, `--prover-name ` — The name of the toml file which contains the inputs for the prover - - Default value: `Prover` -* `--package ` — The name of the package to execute -* `--workspace` — Execute all packages in the workspace -* `--expression-width ` — Specify the backend expression width that should be targeted -* `--force` — Force a full recompilation -* `--print-acir` — Display the ACIR for compiled circuit -* `--deny-warnings` — Treat all warnings as errors -* `--silence-warnings` — Suppress warnings -* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" -* `--oracle-resolver ` — JSON RPC url to solve oracle calls - - - -## `nargo debug` - -Executes a circuit in debug mode - -**Usage:** `nargo debug [OPTIONS] [WITNESS_NAME]` - -###### **Arguments:** - -* `` — Write the execution witness to named file - -###### **Options:** - -* `-p`, `--prover-name ` — The name of the toml file which contains the inputs for the prover - - Default value: `Prover` -* `--package ` — The name of the package to execute -* `--expression-width ` — Specify the backend expression width that should be targeted -* `--force` — Force a full recompilation -* `--print-acir` — Display the ACIR for compiled circuit -* `--deny-warnings` — Treat all warnings as errors -* `--silence-warnings` — Suppress warnings -* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" -* `--acir-mode` — Force ACIR output (disabling instrumentation) -* `--skip-instrumentation ` — Disable vars debug instrumentation (enabled by default) - - Possible values: `true`, `false` - - - - -## `nargo test` - -Run the tests for this program - -**Usage:** `nargo test [OPTIONS] [TEST_NAME]` - -###### **Arguments:** - -* `` — If given, only tests with names containing this string will be run - -###### **Options:** - -* `--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 -* `--expression-width ` — Specify the backend expression width that should be targeted -* `--force` — Force a full recompilation -* `--print-acir` — Display the ACIR for compiled circuit -* `--deny-warnings` — Treat all warnings as errors -* `--silence-warnings` — Suppress warnings -* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" -* `--oracle-resolver ` — JSON RPC url to solve oracle calls - - - -## `nargo info` - -Provides detailed information on each of a program's function (represented by a single circuit) - -Current information provided per circuit: 1. The number of ACIR opcodes 2. Counts the final number gates in the circuit used by a backend - -**Usage:** `nargo info [OPTIONS]` - -###### **Options:** - -* `--package ` — The name of the package to detail -* `--workspace` — Detail all packages in the workspace -* `--expression-width ` — Specify the backend expression width that should be targeted -* `--force` — Force a full recompilation -* `--print-acir` — Display the ACIR for compiled circuit -* `--deny-warnings` — Treat all warnings as errors -* `--silence-warnings` — Suppress warnings -* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" - - - -## `nargo lsp` - -Starts the Noir LSP server - -Starts an LSP server which allows IDEs such as VS Code to display diagnostics in Noir source. - -VS Code Noir Language Support: https://marketplace.visualstudio.com/items?itemName=noir-lang.vscode-noir - -**Usage:** `nargo lsp` - - - -
- - - This document was generated automatically by - clap-markdown. - - diff --git a/docs/versioned_docs/version-v0.32.0/tooling/debugger.md b/docs/versioned_docs/version-v0.32.0/tooling/debugger.md deleted file mode 100644 index 9b7565ba9ff..00000000000 --- a/docs/versioned_docs/version-v0.32.0/tooling/debugger.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: Debugger -description: Learn about the Noir Debugger, in its REPL or VS Code versions. -keywords: [Nargo, VSCode, Visual Studio Code, REPL, Debugger] -sidebar_position: 2 ---- - -# Noir Debugger - -There are currently two ways of debugging Noir programs: - -1. From VS 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). -2. Via the REPL debugger, which ships with Nargo. - -In order to use either version of the debugger, you will need to install recent enough versions of Noir, [Nargo](../getting_started/installation/index.md) and vscode-noir: - -- Noir & Nargo ≥0.28.0 -- Noir's VS Code extension ≥0.0.11 - -:::info -At the moment, the debugger supports debugging binary projects, but not contracts. -::: - -We cover the VS Code Noir debugger more in depth in [its VS Code debugger how-to guide](../how_to/debugger/debugging_with_vs_code.md) and [the reference](../reference/debugger/debugger_vscode.md). - -The REPL debugger is discussed at length in [the REPL debugger how-to guide](../how_to/debugger/debugging_with_the_repl.md) and [the reference](../reference/debugger/debugger_repl.md). diff --git a/docs/versioned_docs/version-v0.32.0/tooling/language_server.md b/docs/versioned_docs/version-v0.32.0/tooling/language_server.md deleted file mode 100644 index 81e0356ef8a..00000000000 --- a/docs/versioned_docs/version-v0.32.0/tooling/language_server.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -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] -sidebar_position: 0 ---- - -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.32.0/tooling/testing.md b/docs/versioned_docs/version-v0.32.0/tooling/testing.md deleted file mode 100644 index 866677da567..00000000000 --- a/docs/versioned_docs/version-v0.32.0/tooling/testing.md +++ /dev/null @@ -1,79 +0,0 @@ ---- -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] -sidebar_position: 1 ---- - -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 all -the constraints 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); -} -``` - -The string given to `should_fail_with` doesn't need to exactly match the failure reason, it just needs to be a substring of it: - -```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 = "airspeed velocity")] -fn test_bridgekeeper() { - main(32); -} -``` \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.32.0/tutorials/noirjs_app.md b/docs/versioned_docs/version-v0.32.0/tutorials/noirjs_app.md deleted file mode 100644 index 8c23b639f12..00000000000 --- a/docs/versioned_docs/version-v0.32.0/tutorials/noirjs_app.md +++ /dev/null @@ -1,348 +0,0 @@ ---- -title: Building a web app with NoirJS -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, app] -sidebar_position: 0 -pagination_next: noir/concepts/data_types/index ---- - -NoirJS is a set of packages meant to work both in a browser and a server environment. In this tutorial, we will build a simple web app using them. From here, you should get an idea on how to proceed with your own Noir projects! - -You can find the complete app code for this guide [here](https://github.com/noir-lang/tiny-noirjs-app). - -## Setup - -:::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.31.x matches `noir_js@0.31.x`, etc. - -In this guide, we will be pinned to 0.31.0. - -::: - -Before we start, we want to make sure we have Node, Nargo and the Barretenberg proving system (`bb`) installed. - -We start by opening a terminal and executing `node --version`. If we don't get an output like `v20.10.0`, that means node is not installed. Let's do that by following the handy [nvm guide](https://github.com/nvm-sh/nvm?tab=readme-ov-file#install--update-script). - -As for `Nargo`, we can follow the [Nargo guide](../getting_started/installation/index.md) to install it. If you're lazy, just paste this on a terminal and run `noirup`: - -```sh -curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash -``` - -Follow the instructions on [this page](https://github.com/AztecProtocol/aztec-packages/tree/master/barretenberg/cpp/src/barretenberg/bb#installation) to install `bb`. -Version 0.41.0 is compatible with `nargo` version 0.31.0, which you can install with `bbup -v 0.41.0` once `bbup` is installed. - -Easy enough. Onwards! - -## Our project - -ZK is a powerful technology. An app that doesn't reveal one of the inputs to _anyone_ is almost unbelievable, yet Noir makes it as easy as a single line of code. - -In fact, it's so simple that it comes nicely packaged in `nargo`. Let's do that! - -### Nargo - -Run: - -```bash -nargo new circuit -``` - -And... That's about it. Your program is ready to be compiled and run. - -To compile, let's `cd` into the `circuit` folder to enter our project, and call: - -```bash -nargo compile -``` - -This compiles our circuit into `json` format and add it to a new `target` folder. - -:::info - -At this point in the tutorial, your folder structure should look like this: - -```tree -. -└── circuit <---- our working directory - ├── Nargo.toml - ├── src - │ └── main.nr - └── target - └── circuit.json -``` - -::: - -### Node and Vite - -If you want to explore Nargo, feel free to go on a side-quest now and follow the steps in the -[getting started](../getting_started/hello_noir/index.md) guide. However, we want our app to run on the browser, so we need Vite. - -Vite is a powerful tool to generate static websites. While it provides all kinds of features, let's just go barebones with some good old vanilla JS. - -To do this this, go back to the previous folder (`cd ..`) and create a new vite project by running `npm create vite` and choosing "Vanilla" and "Javascript". - -A wild `vite-project` directory should now appear in your root folder! Let's not waste any time and dive right in: - -```bash -cd vite-project -``` - -### Setting Up Vite and Configuring the Project - -Before we proceed with any coding, let's get our environment tailored for Noir. We'll start by laying down the foundations with a `vite.config.js` file. This little piece of configuration is our secret sauce for making sure everything meshes well with the NoirJS libraries and other special setups we might need, like handling WebAssembly modules. Here’s how you get that going: - -#### Creating the vite.config.js - -In your freshly minted `vite-project` folder, create a new file named `vite.config.js` and open it in your code editor. Paste the following to set the stage: - -```javascript -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 { - build: { - target: 'esnext', - rollupOptions: { - external: ['@aztec/bb.js'] - } - }, - optimizeDeps: { - esbuildOptions: { - target: 'esnext' - } - }, - plugins: [ - copy({ - targets: [{ src: 'node_modules/**/*.wasm', dest: 'node_modules/.vite/dist' }], - copySync: true, - hook: 'buildStart', - }), - command === 'serve' ? wasmContentTypePlugin : [], - ], - }; - } - - return {}; -}); -``` - -#### Install Dependencies - -Now that our stage is set, install the necessary NoirJS packages along with our other dependencies: - -```bash -npm install && npm install @noir-lang/backend_barretenberg@0.31.0 @noir-lang/noir_js@0.31.0 -npm install rollup-plugin-copy --save-dev -``` - -:::info - -At this point in the tutorial, your folder structure should look like this: - -```tree -. -└── circuit - └── ...etc... -└── vite-project <---- our working directory - └── ...etc... -``` - -::: - -#### Some cleanup - -`npx create vite` is amazing but it creates a bunch of files we don't really need for our simple example. Actually, let's just delete everything except for `vite.config.js`, `index.html`, `main.js` and `package.json`. I feel lighter already. - -![my heart is ready for you, noir.js](@site/static/img/memes/titanic.jpeg) - -## HTML - -Our app won't run like this, of course. We need some working HTML, at least. Let's open our broken-hearted `index.html` and replace everything with this code snippet: - -```html - - - - - - -

Noir app

-
- - -
-
-

Logs

-

Proof

-
- - -``` - -It _could_ be a beautiful UI... Depending on which universe you live in. - -## Some good old vanilla Javascript - -Our love for Noir needs undivided attention, so let's just open `main.js` and delete everything (this is where the romantic scenery becomes a bit creepy). - -Start by pasting in this boilerplate code: - -```js -function display(container, msg) { - const c = document.getElementById(container); - const p = document.createElement('p'); - p.textContent = msg; - c.appendChild(p); -} - -document.getElementById('submitGuess').addEventListener('click', async () => { - try { - // here's where love happens - } catch (err) { - display('logs', 'Oh 💔 Wrong guess'); - } -}); -``` - -The display function doesn't do much. We're simply manipulating our website to see stuff happening. For example, if the proof fails, it will simply log a broken heart 😢 - -:::info - -At this point in the tutorial, your folder structure should look like this: - -```tree -. -└── circuit - └── ...same as above -└── vite-project - ├── vite.config.js - ├── main.js - ├── package.json - └── index.html -``` - -You'll see other files and folders showing up (like `package-lock.json`, `node_modules`) but you shouldn't have to care about those. - -::: - -## Some NoirJS - -We're starting with the good stuff now. If you've compiled the circuit as described above, you should have a `json` file we want to import at the very top of our `main.js` file: - -```ts -import circuit from '../circuit/target/circuit.json'; -``` - -[Noir is backend-agnostic](../index.mdx#whats-new-about-noir). We write Noir, but we also need a proving backend. That's why we need to import and instantiate the two dependencies we installed above: `BarretenbergBackend` and `Noir`. Let's import them right below: - -```js -import { BarretenbergBackend, BarretenbergVerifier as Verifier } from '@noir-lang/backend_barretenberg'; -import { Noir } from '@noir-lang/noir_js'; -``` - -And instantiate them inside our try-catch block: - -```ts -// try { -const backend = new BarretenbergBackend(circuit); -const noir = new Noir(circuit); -// } -``` - -:::note - -For the remainder of the tutorial, everything will be happening inside the `try` block - -::: - -## Our app - -Now for the app itself. We're capturing whatever is in the input when people press the submit button. Just add this: - -```js -const x = parseInt(document.getElementById('guessInput').value); -const input = { x, y: 2 }; -``` - -Now we're ready to prove stuff! Let's feed some inputs to our circuit and calculate the proof: - -```js -await setup(); // let's squeeze our wasm inits here - -display('logs', 'Generating proof... ⌛'); -const { witness } = await noir.execute(input); -const proof = await backend.generateProof(witness); -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 run dev`. If it doesn't open a browser for you, just visit `localhost:5173`. You should now see the worst UI ever, with an ugly input. - -![Getting Started 0](@site/static/img/noir_getting_started_1.png) - -Now, our circuit says `fn main(x: Field, y: pub Field)`. This means only the `y` value is public, and it's hardcoded above: `input = { x, y: 2 }`. In other words, you won't need to send your secret`x` to the verifier! - -By inputting any number other than 2 in the input box and clicking "submit", you should get a valid proof. Otherwise the proof won't even generate correctly. By the way, if you're human, you shouldn't be able to understand anything on the "proof" box. That's OK. We like you, human ❤️. - -## Verifying - -Time to celebrate, yes! But we shouldn't trust machines so blindly. Let's add these lines to see our proof being verified: - -```js -display('logs', 'Verifying proof... ⌛'); -const isValid = await backend.verifyProof(proof); - -// or to cache and use the verification key: -// const verificationKey = await backend.getVerificationKey(); -// const verifier = new Verifier(); -// const isValid = await verifier.verifyProof(proof, verificationKey); - -if (isValid) display('logs', 'Verifying proof... ✅'); -``` - -You have successfully generated a client-side Noir web app! - -![coded app without math knowledge](@site/static/img/memes/flextape.jpeg) - -## 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/vite-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.33.0/explainers/cspell.json b/docs/versioned_docs/version-v0.33.0/explainers/cspell.json deleted file mode 100644 index c60b0a597b1..00000000000 --- a/docs/versioned_docs/version-v0.33.0/explainers/cspell.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "words": [ - "Cryptdoku" - ] -} diff --git a/docs/versioned_docs/version-v0.33.0/explainers/explainer-oracle.md b/docs/versioned_docs/version-v0.33.0/explainers/explainer-oracle.md deleted file mode 100644 index 821e1f95c04..00000000000 --- a/docs/versioned_docs/version-v0.33.0/explainers/explainer-oracle.md +++ /dev/null @@ -1,57 +0,0 @@ ---- -title: Oracles -description: This guide provides an in-depth understanding of how Oracles work in Noir programming. Learn how to use outside calculations in your programs, constrain oracles, and understand their uses and limitations. -keywords: - - Noir Programming - - Oracles - - JSON-RPC - - Foreign Call Handlers - - Constrained Functions - - Blockchain Programming -sidebar_position: 1 ---- - -If you've seen "The Matrix" you may recall "The Oracle" as Gloria Foster smoking cigarettes and baking cookies. While she appears to "know things", she is actually providing a calculation of a pre-determined future. Noir Oracles are similar, in a way. They don't calculate the future (yet), but they allow you to use outside calculations in your programs. - -![matrix oracle prediction](@site/static/img/memes/matrix_oracle.jpeg) - -A Noir program is usually self-contained. You can pass certain inputs to it, and it will generate a deterministic output for those inputs. But what if you wanted to defer some calculation to an outside process or source? - -Oracles are functions that provide this feature. - -## Use cases - -An example usage for Oracles is proving something on-chain. For example, proving that the ETH-USDC quote was below a certain target at a certain block time. Or even making more complex proofs like proving the ownership of an NFT as an anonymous login method. - -Another interesting use case is to defer expensive calculations to be made outside of the Noir program, and then constraining the result; similar to the use of [unconstrained functions](../noir/concepts//unconstrained.md). - -In short, anything that can be constrained in a Noir program but needs to be fetched from an external source is a great candidate to be used in oracles. - -## Constraining oracles - -Just like in The Matrix, Oracles are powerful. But with great power, comes great responsibility. Just because you're using them in a Noir program doesn't mean they're true. Noir has no superpowers. If you want to prove that Portugal won the Euro Cup 2016, you're still relying on potentially untrusted information. - -To give a concrete example, Alice wants to login to the [NounsDAO](https://nouns.wtf/) forum with her username "noir_nouner" by proving she owns a noun without revealing her ethereum address. Her Noir program could have an oracle call like this: - -```rust -#[oracle(getNoun)] -unconstrained fn get_noun(address: Field) -> Field -``` - -This oracle could naively resolve with the number of Nouns she possesses. However, it is useless as a trusted source, as the oracle could resolve to anything Alice wants. In order to make this oracle call actually useful, Alice would need to constrain the response from the oracle, by proving her address and the noun count belongs to the state tree of the contract. - -In short, **Oracles don't prove anything. Your Noir program does.** - -:::danger - -If you don't constrain the return of your oracle, you could be clearly opening an attack vector on your Noir program. Make double-triple sure that the return of an oracle call is constrained! - -::: - -## How to use Oracles - -On CLI, Nargo resolves oracles by making JSON RPC calls, which means it would require an RPC node to be running. - -In JavaScript, NoirJS accepts and resolves arbitrary call handlers (that is, not limited to JSON) as long as they match the expected types the developer defines. Refer to [Foreign Call Handler](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) to learn more about NoirJS's call handling. - -If you want to build using oracles, follow through to the [oracle guide](../how_to/how-to-oracles.md) for a simple example on how to do that. diff --git a/docs/versioned_docs/version-v0.33.0/explainers/explainer-recursion.md b/docs/versioned_docs/version-v0.33.0/explainers/explainer-recursion.md deleted file mode 100644 index df8529ef4e0..00000000000 --- a/docs/versioned_docs/version-v0.33.0/explainers/explainer-recursion.md +++ /dev/null @@ -1,176 +0,0 @@ ---- -title: Recursive proofs -description: Explore the concept of recursive proofs in Zero-Knowledge programming. Understand how recursion works in Noir, a language for writing smart contracts on the EVM blockchain. Learn through practical examples like Alice and Bob's guessing game, Charlie's recursive merkle tree, and Daniel's reusable components. Discover how to use recursive proofs to optimize computational resources and improve efficiency. - -keywords: - [ - "Recursive Proofs", - "Zero-Knowledge Programming", - "Noir", - "EVM Blockchain", - "Smart Contracts", - "Recursion in Noir", - "Alice and Bob Guessing Game", - "Recursive Merkle Tree", - "Reusable Components", - "Optimizing Computational Resources", - "Improving Efficiency", - "Verification Key", - "Aggregation", - "Recursive zkSNARK schemes", - "PLONK", - "Proving and Verification Keys" - ] -sidebar_position: 1 -pagination_next: how_to/how-to-recursion ---- - -In programming, we tend to think of recursion as something calling itself. A classic example would be the calculation of the factorial of a number: - -```js -function factorial(n) { - if (n === 0 || n === 1) { - return 1; - } else { - return n * factorial(n - 1); - } -} -``` - -In this case, while `n` is not `1`, this function will keep calling itself until it hits the base case, bubbling up the result on the call stack: - -```md - Is `n` 1? <--------- - /\ / - / \ n = n -1 - / \ / - Yes No -------- -``` - -In Zero-Knowledge, recursion has some similarities. - -It is not a Noir function calling itself, but a proof being used as an input to another circuit. In short, you verify one proof *inside* another proof, returning the proof that both proofs are valid. - -This means that, given enough computational resources, you can prove the correctness of any arbitrary number of proofs in a single proof. This could be useful to design state channels (for which a common example would be [Bitcoin's Lightning Network](https://en.wikipedia.org/wiki/Lightning_Network)), to save on gas costs by settling one proof on-chain, or simply to make business logic less dependent on a consensus mechanism. - -## Examples - -Let us look at some of these examples - -### Alice and Bob - Guessing game - -Alice and Bob are friends, and they like guessing games. They want to play a guessing game online, but for that, they need a trusted third-party that knows both of their secrets and finishes the game once someone wins. - -So, they use zero-knowledge proofs. Alice tries to guess Bob's number, and Bob will generate a ZK proof stating whether she succeeded or failed. - -This ZK proof can go on a smart contract, revealing the winner and even giving prizes. However, this means every turn needs to be verified on-chain. This incurs some cost and waiting time that may simply make the game too expensive or time-consuming to be worth it. - -As a solution, Alice proposes the following: "what if Bob generates his proof, and instead of sending it on-chain, I verify it *within* my own proof before playing my own turn?". - -She can then generate a proof that she verified his proof, and so on. - -```md - Did you fail? <-------------------------- - / \ / - / \ n = n -1 - / \ / - Yes No / - | | / - | | / - | You win / - | / - | / -Generate proof of that / - + / - my own guess ---------------- -``` - -### Charlie - Recursive merkle tree - -Charlie is a concerned citizen, and wants to be sure his vote in an election is accounted for. He votes with a ZK proof, but he has no way of knowing that his ZK proof was included in the total vote count! - -If the vote collector puts all of the votes into a [Merkle tree](https://en.wikipedia.org/wiki/Merkle_tree), everyone can prove the verification of two proofs within one proof, as such: - -```md - abcd - __________|______________ - | | - ab cd - _____|_____ ______|______ - | | | | - alice bob charlie daniel -``` - -Doing this recursively allows us to arrive on a final proof `abcd` which if true, verifies the correctness of all the votes. - -### Daniel - Reusable components - -Daniel has a big circuit and a big headache. A part of his circuit is a setup phase that finishes with some assertions that need to be made. But that section alone takes most of the proving time, and is largely independent of the rest of the circuit. - -He might find it more efficient to generate a proof for that setup phase separately, and verify that proof recursively in the actual business logic section of his circuit. This will allow for parallelization of both proofs, which results in a considerable speedup. - -## What params do I need - -As you can see in the [recursion reference](noir/standard_library/recursion.mdx), a simple recursive proof requires: - -- The proof to verify -- The Verification Key of the circuit that generated the proof -- A hash of this verification key, as it's needed for some backends -- The public inputs for the proof - -:::info - -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. - -So, taking the example of Alice and Bob and their guessing game: - -- Alice makes her guess. Her proof is *not* recursive: it doesn't verify any proof within it! It's just a standard `assert(x != y)` circuit -- Bob verifies Alice's proof and makes his own guess. In this circuit, he doesn't exactly *prove* the verification of Alice's proof. Instead, he *aggregates* his proof to Alice's proof. The actual verification is done when the full proof is verified, for example when using `nargo verify` or through the verifier smart contract. - -We can imagine recursive proofs a [relay race](https://en.wikipedia.org/wiki/Relay_race). The first runner doesn't have to receive the baton from anyone else, as he/she already starts with it. But when his/her turn is over, the next runner needs to receive it, run a bit more, and pass it along. Even though every runner could theoretically verify the baton mid-run (why not? 🏃🔍), only at the end of the race does the referee verify that the whole race is valid. - -::: - -## Some architecture - -As with everything in computer science, there's no one-size-fits all. But there are some patterns that could help understanding and implementing them. To give three examples: - -### Adding some logic to a proof verification - -This would be an approach for something like our guessing game, where proofs are sent back and forth and are verified by each opponent. This circuit would be divided in two sections: - -- A `recursive verification` section, which would be just the call to `std::verify_proof`, and that would be skipped on the first move (since there's no proof to verify) -- A `guessing` section, which is basically the logic part where the actual guessing happens - -In such a situation, and assuming Alice is first, she would skip the first part and try to guess Bob's number. Bob would then verify her proof on the first section of his run, and try to guess Alice's number on the second part, and so on. - -### Aggregating proofs - -In some one-way interaction situations, recursion would allow for aggregation of simple proofs that don't need to be immediately verified on-chain or elsewhere. - -To give a practical example, a barman wouldn't need to verify a "proof-of-age" on-chain every time he serves alcohol to a customer. Instead, the architecture would comprise two circuits: - -- A `main`, non-recursive circuit with some logic -- A `recursive` circuit meant to verify two proofs in one proof - -The customer's proofs would be intermediate, and made on their phones, and the barman could just verify them locally. He would then aggregate them into a final proof sent on-chain (or elsewhere) at the end of the day. - -### Recursively verifying different circuits - -Nothing prevents you from verifying different circuits in a recursive proof, for example: - -- A `circuit1` circuit -- A `circuit2` circuit -- A `recursive` circuit - -In this example, a regulator could verify that taxes were paid for a specific purchase by aggregating both a `payer` circuit (proving that a purchase was made and taxes were paid), and a `receipt` circuit (proving that the payment was received) - -## How fast is it - -At the time of writing, verifying recursive proofs is surprisingly fast. This is because most of the time is spent on generating the verification key that will be used to generate the next proof. So you are able to cache the verification key and reuse it later. - -Currently, Noir JS packages don't expose the functionality of loading proving and verification keys, but that feature exists in the underlying `bb.js` package. - -## How can I try it - -Learn more about using recursion in Nargo and NoirJS in the [how-to guide](../how_to/how-to-recursion.md) and see a full example in [noir-examples](https://github.com/noir-lang/noir-examples). diff --git a/docs/versioned_docs/version-v0.33.0/explainers/explainer-writing-noir.md b/docs/versioned_docs/version-v0.33.0/explainers/explainer-writing-noir.md deleted file mode 100644 index c8a42c379e6..00000000000 --- a/docs/versioned_docs/version-v0.33.0/explainers/explainer-writing-noir.md +++ /dev/null @@ -1,173 +0,0 @@ ---- -title: Writing Performant Noir -description: Understand new considerations when writing Noir -keywords: [Noir, programming, rust] -tags: [Optimization] -sidebar_position: 0 ---- - - -This article intends to set you up with key concepts essential for writing more viable applications that use zero knowledge proofs, namely around efficient circuits. - -## Context - 'Efficient' is subjective - -When writing a web application for a performant computer with high-speed internet connection, writing efficient code sometimes is seen as an afterthought only if needed. Large multiplications running at the innermost of nested loops may not even be on a dev's radar. -When writing firmware for a battery-powered microcontroller, you think of cpu cycles as rations to keep within a product's power budget. - -> Code is written to create applications that perform specific tasks within specific constraints - -And these constraints differ depending on where the compiled code is execute. - -### The Ethereum Virtual Machine (EVM) - -In scenarios where extremely low gas costs are required for an Ethereum application to be viable/competitive, Ethereum smart contract developers get into what is colloquially known as: "*gas golfing*". Finding the lowest execution cost of their compiled code (EVM bytecode) to achieve a specific task. - -The equivalent optimization task when writing zk circuits is affectionately referred to as "*gate golfing*", finding the lowest gate representation of the compiled Noir code. - -### Coding for circuits - a paradigm shift - -In zero knowledge cryptography, code is compiled to "circuits" consisting of arithmetic gates, and gate count is the significant cost. Depending on the proving system this is linearly proportionate to proving time, and so from a product point this should be kept as low as possible. - -Whilst writing efficient code for web apps and Solidity has a few key differences, writing efficient circuits have a different set of considerations. It is a bit of a paradigm shift, like writing code for GPUs for the first time... - -For example, drawing a circle at (0, 0) of radius `r`: -- For a single CPU thread, -``` -for theta in 0..2*pi { - let x = r * cos(theta); - let y = r * sin(theta); - draw(x, y); -} // note: would do 0 - pi/2 and draw +ve/-ve x and y. -``` - -- For GPUs (simultaneous parallel calls with x, y across image), -``` -if (x^2 + y^2 = r^2) { - draw(x, y); -} -``` - -([Related](https://www.youtube.com/watch?v=-P28LKWTzrI)) - -Whilst this CPU -> GPU does not translate to circuits exactly, it is intended to exemplify the difference in intuition when coding for different machine capabilities/constraints. - -### Context Takeaway - -For those coming from a primarily web app background, this article will explain what you need to consider when writing circuits. Furthermore, for those experienced writing efficient machine code, prepare to shift what you think is efficient 😬 - -## Translating from Rust - -For some applications using Noir, existing code might be a convenient starting point to then proceed to optimize the gate count of. - -:::note -Many valuable functions and algorithms have been written in more established languages (C/C++), and converted to modern ones (like Rust). -::: - -Fortunately for Noir developers, when needing a particular function a Rust implementation can be readily compiled into Noir with some key changes. While the compiler does a decent amount of optimizations, it won't be able to change code that has been optimized for clock-cycles into code optimized for arithmetic gates. - -A few things to do when converting Rust code to Noir: -- `println!` is not a macro, use `println` function (same for `assert_eq`) -- No early `return` in function. Use constrain via assertion instead -- No passing by reference. Remove `&` operator to pass by value (copy) -- No boolean operators (`&&`, `||`). Use bitwise operators (`&`, `|`) with boolean values -- No type `usize`. Use types `u8`, `u32`, `u64`, ... -- `main` return must be public, `pub` -- No `const`, use `global` -- Noir's LSP is your friend, so error message should be informative enough to resolve syntax issues. - -## Writing efficient Noir for performant products - -The following points help refine our understanding over time. - -:::note -A Noir program makes a statement that can be verified. -::: - -It compiles to a structure that represents the calculation, and can assert results within the calculation at any stage (via the `constrain` keyword). - -A Noir program compiles to an Abstract Circuit Intermediate Representation which is: - - A tree structure - - Leaves (inputs) are the `Field` type - - Nodes contain arithmetic operations to combine them (gates) - - The root is the final result (return value) - -:::tip -The command `nargo info` shows the programs circuit size, and is useful to compare the value of changes made. -You can dig deeper and use the `--print-acir` param to take a closer look at individual ACIR opcodes, and the proving backend to see its gate count (eg for barretenberg, `bb gates -b ./target/program.json`). -::: - -### Use the `Field` type - -Since the native type of values in circuits are `Field`s, using them for variables in Noir means less gates converting them under the hood. - -:::tip -Where possible, use `Field` type for values. Using smaller value types, and bit-packing strategies, will result in MORE gates -::: - -**Note:** Need to remain mindful of overflow. Types with less bits may be used to limit the range of possible values prior to a calculation. - -### Use Arithmetic over non-arithmetic operations - -Since circuits are made of arithmetic gates, the cost of arithmetic operations tends to be one gate. Whereas for procedural code, they represent several clock cycles. - -Inversely, non-arithmetic operators are achieved with multiple gates, vs 1 clock cycle for procedural code. - -| (cost\op) | arithmetic
(`*`, `+`) | bit-wise ops
(eg `<`, `\|`, `>>`) | -| - | - | - | -| **cycles** | 10+ | 1 | -| **gates** | 1 | 10+ | - -Bit-wise operations (e.g. bit shifts `<<` and `>>`), albeit commonly used in general programming and especially for clock cycle optimizations, are on the contrary expensive in gates when performed within circuits. - -Translate away from bit shifts when writing constrained functions for the best performance. - -On the flip side, feel free to use bit shifts in unconstrained functions and tests if necessary, as they are executed outside of circuits and does not induce performance hits. - -### Use static over dynamic values - -Another general theme that manifests in different ways is that static reads are represented with less gates than dynamic ones. - -Reading from read-only memory (ROM) adds less gates than random-access memory (RAM), 2 vs ~3.25 due to the additional bounds checks. Arrays of fixed length (albeit used at a lower capacity), will generate less gates than dynamic storage. - -Related to this, if an index used to access an array is not known at compile time (ie unknown until run time), then ROM will be converted to RAM, expanding the gate count. - -:::tip -Use arrays and indices that are known at compile time where possible. -Using `assert_constant(i);` before an index, `i`, is used in an array will give a compile error if `i` is NOT known at compile time. -::: - -### Leverage unconstrained execution - -Constrained verification can leverage unconstrained execution, this is especially useful for operations that are represented by many gates. -Use an [unconstrained function](../noir/concepts/unconstrained.md) to perform gate-heavy calculations, then verify and constrain the result. - -Eg division generates more gates than multiplication, so calculating the quotient in an unconstrained function then constraining the product for the quotient and divisor (+ any remainder) equals the dividend will be more efficient. - -Use ` if is_unconstrained() { /`, to conditionally execute code if being called in an unconstrained vs constrained way. - -## Advanced - -Unless you're well into the depth of gate optimization, this advanced section can be ignored. - -### Combine arithmetic operations - -A Noir program can be honed further by combining arithmetic operators in a way that makes the most of each constraint of the backend proving system. This is in scenarios where the backend might not be doing this perfectly. - -Eg Barretenberg backend (current default for Noir) is a width-4 PLONKish constraint system -$ w_1*w_2*q_m + w_1*q_1 + w_2*q_2 + w_3*q_3 + w_4*q_4 + q_c = 0 $ - -Here we see there is one occurrence of witness 1 and 2 ($w_1$, $w_2$) being multiplied together, with addition to witnesses 1-4 ($w_1$ .. $w_4$) multiplied by 4 corresponding circuit constants ($q_1$ .. $q_4$) (plus a final circuit constant, $q_c$). - -Use `nargo info --print-acir`, to inspect the ACIR opcodes (and the proving system for gates), and it may present opportunities to amend the order of operations and reduce the number of constraints. - -#### Variable as witness vs expression - -If you've come this far and really know what you're doing at the equation level, a temporary lever (that will become unnecessary/useless over time) is: `std::as_witness`. This informs the compiler to save a variable as a witness not an expression. - -The compiler will mostly be correct and optimal, but this may help some near term edge cases that are yet to optimize. -Note: When used incorrectly it will create **less** efficient circuits (higher gate count). - -## References -- Guillaume's ["`Cryptdoku`" talk](https://www.youtube.com/watch?v=MrQyzuogxgg) (Jun'23) -- Tips from Tom, Jake and Zac. -- [Idiomatic Noir](https://www.vlayer.xyz/blog/idiomatic-noir-part-1-collections) blog post diff --git a/docs/versioned_docs/version-v0.33.0/getting_started/_category_.json b/docs/versioned_docs/version-v0.33.0/getting_started/_category_.json deleted file mode 100644 index 5d694210bbf..00000000000 --- a/docs/versioned_docs/version-v0.33.0/getting_started/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "position": 0, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.33.0/getting_started/backend/_category_.json b/docs/versioned_docs/version-v0.33.0/getting_started/backend/_category_.json deleted file mode 100644 index b82e92beb0c..00000000000 --- a/docs/versioned_docs/version-v0.33.0/getting_started/backend/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "position": 1, - "label": "Install Proving Backend", - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.33.0/getting_started/backend/index.md b/docs/versioned_docs/version-v0.33.0/getting_started/backend/index.md deleted file mode 100644 index 7192d954877..00000000000 --- a/docs/versioned_docs/version-v0.33.0/getting_started/backend/index.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: Proving Backend Installation -description: Proving backends offer command line tools for proving and verifying Noir programs. This page describes how to install `bb` as an example. -keywords: [ - Proving - Backend - Barretenberg - bb - bbup - Installation - Terminal - Command - CLI - Version -] -pagination_next: getting_started/hello_noir/index ---- - -Proving backends each provide their own tools for working with Noir programs, providing functionality like proof generation, proof verification, and verifier smart contract generation. - -For the latest information on tooling provided by each proving backend, installation instructions, Noir version compatibility... you may refer to the proving backends' own documentation. - -You can find the full list of proving backends compatible with Noir in [Awesome Noir](https://github.com/noir-lang/awesome-noir/?tab=readme-ov-file#proving-backends). - -## Example: Installing `bb` - -`bb` is the CLI tool provided by the [Barretenberg proving backend](https://github.com/AztecProtocol/barretenberg) developed by Aztec Labs. - -You can find the instructions for installation in [`bb`'s documentation](https://github.com/AztecProtocol/aztec-packages/blob/master/barretenberg/cpp/src/barretenberg/bb/readme.md#installation). - -Once installed, we are ready to start working on [our first Noir program](../hello_noir/index.md). diff --git a/docs/versioned_docs/version-v0.33.0/getting_started/hello_noir/_category_.json b/docs/versioned_docs/version-v0.33.0/getting_started/hello_noir/_category_.json deleted file mode 100644 index 976a2325de0..00000000000 --- a/docs/versioned_docs/version-v0.33.0/getting_started/hello_noir/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "position": 2, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.33.0/getting_started/hello_noir/index.md b/docs/versioned_docs/version-v0.33.0/getting_started/hello_noir/index.md deleted file mode 100644 index 3baae217eb3..00000000000 --- a/docs/versioned_docs/version-v0.33.0/getting_started/hello_noir/index.md +++ /dev/null @@ -1,152 +0,0 @@ ---- -title: Creating 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, - ] -sidebar_position: 1 - ---- - -Now that we have installed Nargo and a proving backend, it is time to make our first hello world program! - -### 1. Create a new project directory - -Noir code can live anywhere on your computer. Let us create a _projects_ folder in the home -directory to house our first Noir program. - -Create the directory and change directory into it by running: - -```sh -mkdir ~/projects -cd ~/projects -``` - -## Nargo - -Nargo provides the ability to initiate and execute Noir projects. Read the [Nargo installation](../installation/index.md) section to learn more about Nargo and how to install it. - -### 2. Create a new Noir project - -Now that we are in the projects directory, create a new Nargo project by running: - -```sh -nargo new hello_world -``` - -`hello_world` can be any arbitrary project name, we are simply using `hello_world` for demonstration. - -In production, it is common practice to name the project folder, `circuits`, for clarity amongst other folders in the codebase (like: `contracts`, `scripts`, and `test`). - -A `hello_world` folder would be created. Similar to Rust, the folder houses _src/main.nr_ and -_Nargo.toml_ which contain 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](../../noir/concepts/data_types/index.md) 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](../../noir/concepts/comments.md) chapter. - -### 3. 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 -``` - -A _Prover.toml_ file will be generated in your project directory, to allow specifying input values to the program. - -### 4. Execute the Noir program - -Now that the project is set up, we can execute our Noir program. - -Fill in input values for execution in the _Prover.toml_ file. For example: - -```toml -x = "1" -y = "2" -``` - -Execute your Noir program: - -```sh -nargo execute witness-name -``` - -The witness corresponding to this execution will then be written to the file `./target/witness-name.gz`. - -The command also automatically compiles your Noir program if it was not already / was edited, which you may notice the compiled artifacts being written to the file `./target/hello_world.json`. - -## Proving Backend - -Proving backends provide the ability to generate and verify proofs of executing Noir programs, following Noir's tooling that compiles and executes the programs. Read the [proving backend installation](../backend/index.md) section to learn more about proving backends and how to install them. - -Barretenberg is used as an example here to demonstrate how proving and verifying could be implemented and used. Read the [`bb` installation](../backend/index.md#example-installing-bb) section for how to install Barretenberg's CLI tool; refer to [`bb`'s documentation](https://github.com/AztecProtocol/aztec-packages/blob/master/barretenberg/cpp/src/barretenberg/bb/readme.md) for full details about the tool. - -### 5. Prove an execution of the Noir program - -Using Barretenberg as an example, prove the valid execution of your Noir program running: - -```sh -bb prove -b ./target/hello_world.json -w ./target/witness-name.gz -o ./target/proof -``` - -The proof generated will then be written to the file `./target/proof`. - -### 6. Verify the execution proof - -Once a proof is generated, we can verify correct execution of our Noir program by verifying the proof file. - -Using Barretenberg as an example, compute the verification key for the Noir program by running: - -```sh -bb write_vk -b ./target/hello_world.json -o ./target/vk -``` - -And verify your proof by running: - -```sh -bb verify -k ./target/vk -p ./target/proof -``` - -If successful, the verification will complete in silence; if unsuccessful, the command will trigger logging of the corresponding error. - -Congratulations, you have now created and verified a proof for your very first Noir program! - -In the [next section](./project_breakdown.md), we will go into more detail on each step performed. diff --git a/docs/versioned_docs/version-v0.33.0/getting_started/hello_noir/project_breakdown.md b/docs/versioned_docs/version-v0.33.0/getting_started/hello_noir/project_breakdown.md deleted file mode 100644 index 96e653f6c08..00000000000 --- a/docs/versioned_docs/version-v0.33.0/getting_started/hello_noir/project_breakdown.md +++ /dev/null @@ -1,159 +0,0 @@ ---- -title: Project Breakdown -description: - Learn about the anatomy of a Nargo project, including the purpose of the Prover TOML - file, and how to prove and verify your program. -keywords: - [Nargo, Nargo project, Prover.toml, proof verification, private asset transfer] -sidebar_position: 2 ---- - -This section breaks down our hello world program from 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 - - 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. - -### 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 = "noir_starter" -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](../../noir/modules_packages_crates/workspaces.md) will look a bit different. For example: - -```toml -[workspace] -members = ["crates/a", "crates/b"] -default-member = "crates/a" -``` - -#### Package section - -The package section defines 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) -- `expression_width` (optional) - Sets the default backend expression width. This field will override the default backend expression width specified by the Noir compiler (currently set to width 4). - -#### Dependencies section - -This is where you will specify any dependencies for your project. See the [Dependencies page](../../noir/modules_packages_crates/dependencies.md) 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 that the condition to be satisfied (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 the inputs to the Noir program (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 execute` is executed, nargo will execute the Noir program using the inputs specified in `Prover.toml`, aborting if it finds that these do not satisfy the constraints defined by `main`. In this example, `x` and `y` must satisfy the inequality constraint `assert(x != y)`. - -If an output name is specified such as `nargo execute foo`, the witness generated by this execution will be written to `./target/foo.gz`. This can then be used to generate a proof of the execution. - -#### 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 execution by using the `--prover-name` or `-p` flags. - -This command looks for proof inputs in the default **Prover.toml** and generates the witness and saves it at `./target/foo.gz`: - -```bash -nargo execute foo -``` - -This command looks for proof inputs in the custom **OtherProver.toml** and generates the witness and saves it at `./target/bar.gz`: - -```bash -nargo execute -p OtherProver bar -``` - -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.33.0/getting_started/installation/_category_.json b/docs/versioned_docs/version-v0.33.0/getting_started/installation/_category_.json deleted file mode 100644 index 0c02fb5d4d7..00000000000 --- a/docs/versioned_docs/version-v0.33.0/getting_started/installation/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "position": 0, - "label": "Install Nargo", - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.33.0/getting_started/installation/index.md b/docs/versioned_docs/version-v0.33.0/getting_started/installation/index.md deleted file mode 100644 index 53ea9c7891c..00000000000 --- a/docs/versioned_docs/version-v0.33.0/getting_started/installation/index.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -title: Nargo Installation -description: - nargo is a command line tool for interacting with Noir programs. This page is a quick guide on how to install Nargo through the most common and easy method, noirup -keywords: [ - Nargo - Noir - Rust - Cargo - Noirup - Installation - Terminal Commands - Version Check - Nightlies - Specific Versions - Branches - Noirup Repository -] -pagination_next: getting_started/hello_noir/index ---- - -`nargo` is a tool for working with Noir programs on the CLI, providing you with the ability to start new projects, compile, execute and test Noir programs from the terminal. - -The name is inspired by Rust's package manager `cargo`; and similar to Rust's `rustup`, Noir also has an easy installation script `noirup`. - -## Installing Noirup - -Open a terminal on your machine, and write: - -```bash -curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash -``` - -Close the terminal, open another one, and run - -```bash -noirup -``` - -Done. That's it. 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. - -Now we're ready to start working on [our first Noir program!](../hello_noir/index.md) diff --git a/docs/versioned_docs/version-v0.33.0/getting_started/installation/other_install_methods.md b/docs/versioned_docs/version-v0.33.0/getting_started/installation/other_install_methods.md deleted file mode 100644 index 3634723562b..00000000000 --- a/docs/versioned_docs/version-v0.33.0/getting_started/installation/other_install_methods.md +++ /dev/null @@ -1,102 +0,0 @@ ---- -title: Alternative Installations -description: There are different ways to install Nargo, the one-stop shop and command-line tool for developing Noir programs. This guide explains how to specify which version to install when using noirup, and using WSL for windows. -keywords: [ - Installation - Nargo - Noirup - Binaries - Compiling from Source - WSL for Windows - macOS - Linux - Nix - Direnv - Uninstalling Nargo - ] -sidebar_position: 1 ---- - -## Encouraged Installation Method: Noirup - -Noirup is the endorsed method for installing Nargo, streamlining the process of fetching binaries or compiling from source. It supports a range of options to cater to your specific needs, from nightly builds and specific versions to compiling from various sources. - -### Installing Noirup - -First, ensure you have `noirup` installed: - -```sh -curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash -``` - -### Fetching Binaries - -With `noirup`, you can easily switch between different Nargo versions, including nightly builds: - -- **Nightly Version**: Install the latest nightly build. - - ```sh - noirup --version nightly - ``` - -- **Specific Version**: Install a specific version of Nargo. - ```sh - noirup --version - ``` - -### Compiling from Source - -`noirup` also enables compiling Nargo from various sources: - -- **From a Specific Branch**: Install from the latest commit on a branch. - - ```sh - noirup --branch - ``` - -- **From a Fork**: Install from the main branch of a fork. - - ```sh - noirup --repo - ``` - -- **From a Specific Branch in a Fork**: Install from a specific branch in a fork. - - ```sh - noirup --repo --branch - ``` - -- **From a Specific Pull Request**: Install from a specific PR. - - ```sh - noirup --pr - ``` - -- **From a Specific Commit**: Install from a specific commit. - - ```sh - noirup -C - ``` - -- **From Local Source**: Compile and install from a local directory. - ```sh - noirup --path ./path/to/local/source - ``` - -## Installation on Windows - -The default backend for Noir (Barretenberg) doesn't provide Windows binaries at this time. For that reason, Noir cannot be installed natively. 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](#encouraged-installation-method-noirup). - -## Uninstalling Nargo - -If you installed Nargo with `noirup`, you can uninstall Nargo by removing the files in `~/.nargo`, `~/nargo`, and `~/noir_cache`. This ensures that all installed binaries, configurations, and cache related to Nargo are fully removed from your system. - -```bash -rm -r ~/.nargo -rm -r ~/nargo -rm -r ~/noir_cache -``` diff --git a/docs/versioned_docs/version-v0.33.0/how_to/_category_.json b/docs/versioned_docs/version-v0.33.0/how_to/_category_.json deleted file mode 100644 index 23b560f610b..00000000000 --- a/docs/versioned_docs/version-v0.33.0/how_to/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "position": 1, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.33.0/how_to/debugger/_category_.json b/docs/versioned_docs/version-v0.33.0/how_to/debugger/_category_.json deleted file mode 100644 index cc2cbb1c253..00000000000 --- a/docs/versioned_docs/version-v0.33.0/how_to/debugger/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "Debugging", - "position": 5, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.33.0/how_to/debugger/debugging_with_the_repl.md b/docs/versioned_docs/version-v0.33.0/how_to/debugger/debugging_with_the_repl.md deleted file mode 100644 index 1d64dae3f37..00000000000 --- a/docs/versioned_docs/version-v0.33.0/how_to/debugger/debugging_with_the_repl.md +++ /dev/null @@ -1,164 +0,0 @@ ---- -title: Using the REPL Debugger -description: - Step-by-step guide on how to debug your Noir circuits with the REPL Debugger. -keywords: - [ - Nargo, - Noir CLI, - Noir Debugger, - REPL, - ] -sidebar_position: 1 ---- - -#### Pre-requisites - -In order to use the REPL debugger, first you need to install recent enough versions of Nargo and vscode-noir. - -## Debugging a simple circuit - -Let's debug a simple circuit: - -```rust -fn main(x : Field, y : pub Field) { - assert(x != y); -} -``` - -To start the REPL debugger, using a terminal, go to a Noir circuit's home directory. Then: - -`$ nargo debug` - -You should be seeing this in your terminal: - -``` -[main] Starting debugger -At ~/noir-examples/recursion/circuits/main/src/main.nr:1:9 - 1 -> fn main(x : Field, y : pub Field) { - 2 assert(x != y); - 3 } -> -``` - -The debugger displays the current Noir code location, and it is now waiting for us to drive it. - -Let's first take a look at the available commands. For that we'll use the `help` command. - -``` -> help -Available commands: - - opcodes display ACIR opcodes - into step into to the next opcode - next step until a new source location is reached - out step until a new source location is reached - and the current stack frame is finished - break LOCATION:OpcodeLocation add a breakpoint at an opcode location - over step until a new source location is reached - without diving into function calls - restart restart the debugging session - delete LOCATION:OpcodeLocation delete breakpoint at an opcode location - witness show witness map - witness index:u32 display a single witness from the witness map - witness index:u32 value:String update a witness with the given value - memset index:usize value:String update a memory cell with the given - value - continue continue execution until the end of the - program - vars show variable values available at this point - in execution - stacktrace display the current stack trace - memory show memory (valid when executing unconstrained code) - step step to the next ACIR opcode - -Other commands: - - help Show this help message - quit Quit repl - -``` - -Some commands operate only for unconstrained functions, such as `memory` and `memset`. If you try to use them while execution is paused at an ACIR opcode, the debugger will simply inform you that you are not executing unconstrained code: - -``` -> memory -Unconstrained VM memory not available -> -``` - -Before continuing, we can take a look at the initial witness map: - -``` -> witness -_0 = 1 -_1 = 2 -> -``` - -Cool, since `x==1`, `y==2`, and we want to check that `x != y`, our circuit should succeed. At this point we could intervene and use the witness setter command to change one of the witnesses. Let's set `y=3`, then back to 2, so we don't affect the expected result: - -``` -> witness -_0 = 1 -_1 = 2 -> witness 1 3 -_1 = 3 -> witness -_0 = 1 -_1 = 3 -> witness 1 2 -_1 = 2 -> witness -_0 = 1 -_1 = 2 -> -``` - -Now we can inspect the current state of local variables. For that we use the `vars` command. - -``` -> vars -> -``` - -We currently have no vars in context, since we are at the entry point of the program. Let's use `next` to execute until the next point in the program. - -``` -> vars -> next -At ~/noir-examples/recursion/circuits/main/src/main.nr:1:20 - 1 -> fn main(x : Field, y : pub Field) { - 2 assert(x != y); - 3 } -> vars -x:Field = 0x01 -``` - -As a result of stepping, the variable `x`, whose initial value comes from the witness map, is now in context and returned by `vars`. - -``` -> next - 1 fn main(x : Field, y : pub Field) { - 2 -> assert(x != y); - 3 } -> vars -y:Field = 0x02 -x:Field = 0x01 -``` - -Stepping again we can finally see both variables and their values. And now we can see that the next assertion should succeed. - -Let's continue to the end: - -``` -> continue -(Continuing execution...) -Finished execution -> q -[main] Circuit witness successfully solved -``` - -Upon quitting the debugger after a solved circuit, the resulting circuit witness gets saved, equivalent to what would happen if we had run the same circuit with `nargo execute`. - -We just went through the basics of debugging using Noir REPL debugger. For a comprehensive reference, check out [the reference page](../../reference/debugger/debugger_repl.md). diff --git a/docs/versioned_docs/version-v0.33.0/how_to/debugger/debugging_with_vs_code.md b/docs/versioned_docs/version-v0.33.0/how_to/debugger/debugging_with_vs_code.md deleted file mode 100644 index a5858c1a5eb..00000000000 --- a/docs/versioned_docs/version-v0.33.0/how_to/debugger/debugging_with_vs_code.md +++ /dev/null @@ -1,68 +0,0 @@ ---- -title: Using the VS Code Debugger -description: - Step by step guide on how to debug your Noir circuits with the VS Code Debugger configuration and features. -keywords: - [ - Nargo, - Noir CLI, - Noir Debugger, - VS Code, - IDE, - ] -sidebar_position: 0 ---- - -This guide will show you how to use VS Code with the vscode-noir extension to debug a Noir project. - -#### Pre-requisites - -- Nargo -- vscode-noir -- A Noir project with a `Nargo.toml`, `Prover.toml` and at least one Noir (`.nr`) containing an entry point function (typically `main`). - -## Running the debugger - -The easiest way to start debugging is to open the file you want to debug, and press `F5`. This will cause the debugger to launch, using your `Prover.toml` file as input. - -You should see something like this: - -![Debugger launched](@site/static/img/debugger/1-started.png) - -Let's inspect the state of the program. For that, we open VS Code's _Debug pane_. Look for this icon: - -![Debug pane icon](@site/static/img/debugger/2-icon.png) - -You will now see two categories of variables: Locals and Witness Map. - -![Debug pane expanded](@site/static/img/debugger/3-debug-pane.png) - -1. **Locals**: variables of your program. At this point in execution this section is empty, but as we step through the code it will get populated by `x`, `result`, `digest`, etc. - -2. **Witness map**: these are initially populated from your project's `Prover.toml` file. In this example, they will be used to populate `x` and `result` at the beginning of the `main` function. - -Most of the time you will probably be focusing mostly on locals, as they represent the high level state of your program. - -You might be interested in inspecting the witness map in case you are trying to solve a really low level issue in the compiler or runtime itself, so this concerns mostly advanced or niche users. - -Let's step through the program, by using the debugger buttons or their corresponding keyboard shortcuts. - -![Debugger buttons](@site/static/img/debugger/4-debugger-buttons.png) - -Now we can see in the variables pane that there's values for `digest`, `result` and `x`. - -![Inspecting locals](@site/static/img/debugger/5-assert.png) - -We can also inspect the values of variables by directly hovering on them on the code. - -![Hover locals](@site/static/img/debugger/6-hover.png) - -Let's set a break point at the `keccak256` function, so we can continue execution up to the point when it's first invoked without having to go one step at a time. - -We just need to click the to the right of the line number 18. Once the breakpoint appears, we can click the `continue` button or use its corresponding keyboard shortcut (`F5` by default). - -![Breakpoint](@site/static/img/debugger/7-break.png) - -Now we are debugging the `keccak256` function, notice the _Call Stack pane_ at the lower right. This lets us inspect the current call stack of our process. - -That covers most of the current debugger functionalities. Check out [the reference](../../reference/debugger/debugger_vscode.md) for more details on how to configure the debugger. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.33.0/how_to/how-to-oracles.md b/docs/versioned_docs/version-v0.33.0/how_to/how-to-oracles.md deleted file mode 100644 index 392dd8b452e..00000000000 --- a/docs/versioned_docs/version-v0.33.0/how_to/how-to-oracles.md +++ /dev/null @@ -1,275 +0,0 @@ ---- -title: How to use Oracles -description: Learn how to use oracles in your Noir program with examples in both Nargo and NoirJS. This guide also covers writing a JSON RPC server and providing custom foreign call handlers for NoirJS. -keywords: - - Noir Programming - - Oracles - - Nargo - - NoirJS - - JSON RPC Server - - Foreign Call Handlers -sidebar_position: 1 ---- - -This guide shows you how to use oracles in your Noir program. For the sake of clarity, it assumes that: - -- You have read the [explainer on Oracles](../explainers/explainer-oracle.md) and are comfortable with the concept. -- You have a Noir program to add oracles to. You can create one using the [vite-hardhat starter](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat) as a boilerplate. -- You understand the concept of a JSON-RPC server. Visit the [JSON-RPC website](https://www.jsonrpc.org/) if you need a refresher. -- You are comfortable with server-side JavaScript (e.g. Node.js, managing packages, etc.). - -## Rundown - -This guide has 3 major steps: - -1. How to modify our Noir program to make use of oracle calls as unconstrained functions -2. How to write a JSON RPC Server to resolve these oracle calls with Nargo -3. How to use them in Nargo and how to provide a custom resolver in NoirJS - -## Step 1 - Modify your Noir program - -An oracle is defined in a Noir program by defining two methods: - -- An unconstrained method - This tells the compiler that it is executing an [unconstrained functions](../noir/concepts//unconstrained.md). -- A decorated oracle method - This tells the compiler that this method is an RPC call. - -An example of an oracle that returns a `Field` would be: - -```rust -#[oracle(getSqrt)] -unconstrained fn sqrt(number: Field) -> Field { } - -unconstrained fn get_sqrt(number: Field) -> Field { - sqrt(number) -} -``` - -In this example, we're wrapping our oracle function in an unconstrained method, and decorating it with `oracle(getSqrt)`. We can then call the unconstrained function as we would call any other function: - -```rust -fn main(input: Field) { - let sqrt = get_sqrt(input); -} -``` - -In the next section, we will make this `getSqrt` (defined on the `sqrt` decorator) be a method of the RPC server Noir will use. - -:::danger - -As explained in the [Oracle Explainer](../explainers/explainer-oracle.md), this `main` function is unsafe unless you constrain its return value. For example: - -```rust -fn main(input: Field) { - let sqrt = get_sqrt(input); - assert(sqrt.pow_32(2) as u64 == input as u64); // <---- constrain the return of an oracle! -} -``` - -::: - -:::info - -Currently, oracles only work with single params or array params. For example: - -```rust -#[oracle(getSqrt)] -unconstrained fn sqrt([Field; 2]) -> [Field; 2] { } -``` - -::: - -## Step 2 - Write an RPC server - -Brillig will call *one* RPC server. Most likely you will have to write your own, and you can do it in whatever language you prefer. In this guide, we will do it in Javascript. - -Let's use the above example of an oracle that consumes an array with two `Field` and returns their square roots: - -```rust -#[oracle(getSqrt)] -unconstrained fn sqrt(input: [Field; 2]) -> [Field; 2] { } - -unconstrained fn get_sqrt(input: [Field; 2]) -> [Field; 2] { - sqrt(input) -} - -fn main(input: [Field; 2]) { - let sqrt = get_sqrt(input); - assert(sqrt[0].pow_32(2) as u64 == input[0] as u64); - assert(sqrt[1].pow_32(2) as u64 == input[1] as u64); -} - -#[test] -fn test() { - let input = [4, 16]; - main(input); -} -``` - -:::info - -Why square root? - -In general, computing square roots is computationally more expensive than multiplications, which takes a toll when speaking about ZK applications. In this case, instead of calculating the square root in Noir, we are using our oracle to offload that computation to be made in plain. In our circuit we can simply multiply the two values. - -::: - -Now, we should write the correspondent RPC server, starting with the [default JSON-RPC 2.0 boilerplate](https://www.npmjs.com/package/json-rpc-2.0#example): - -```js -import { JSONRPCServer } from "json-rpc-2.0"; -import express from "express"; -import bodyParser from "body-parser"; - -const app = express(); -app.use(bodyParser.json()); - -const server = new JSONRPCServer(); -app.post("/", (req, res) => { - const jsonRPCRequest = req.body; - server.receive(jsonRPCRequest).then((jsonRPCResponse) => { - if (jsonRPCResponse) { - res.json(jsonRPCResponse); - } else { - res.sendStatus(204); - } - }); -}); - -app.listen(5555); -``` - -Now, we will add our `getSqrt` method, as expected by the `#[oracle(getSqrt)]` decorator in our Noir code. It maps through the params array and returns their square roots: - -```js -server.addMethod("resolve_foreign_call", async (params) => { - if (params[0].function !== "getSqrt") { - throw Error("Unexpected foreign call") - }; - const values = params[0].inputs[0].map((field) => { - return `${Math.sqrt(parseInt(field, 16))}`; - }); - return { values: [values] }; -}); -``` - -If you're using Typescript, the following types may be helpful in understanding the expected return value and making sure they're easy to follow: - -```js -export type ForeignCallSingle = string; - -export type ForeignCallArray = string[]; - -export type ForeignCallResult = { - values: (ForeignCallSingle | ForeignCallArray)[]; -}; -``` - -:::info Multidimensional Arrays - -If the Oracle function is returning an array containing other arrays, such as `[['1','2],['3','4']]`, you need to provide the values in JSON as flattened values. In the previous example, it would be `['1', '2', '3', '4']`. In the Noir program, the Oracle signature can use a nested type, the flattened values will be automatically converted to the nested type. - -::: - -## Step 3 - Usage with Nargo - -Using the [`nargo` CLI tool](../getting_started/installation/index.md), you can use oracles in the `nargo test` and `nargo execute` commands by passing a value to `--oracle-resolver`. For example: - -```bash -nargo test --oracle-resolver http://localhost:5555 -``` - -This tells `nargo` to use your RPC Server URL whenever it finds an oracle decorator. - -## Step 4 - Usage with NoirJS - -In a JS environment, an RPC server is not strictly necessary, as you may want to resolve your oracles without needing any JSON call at all. NoirJS simply expects that you pass a callback function when you generate proofs, and that callback function can be anything. - -For example, if your Noir program expects the host machine to provide CPU pseudo-randomness, you could simply pass it as the `foreignCallHandler`. You don't strictly need to create an RPC server to serve pseudo-randomness, as you may as well get it directly in your app: - -```js -const foreignCallHandler = (name, inputs) => crypto.randomBytes(16) // etc - -await noir.execute(inputs, foreignCallHandler) -``` - -As one can see, in NoirJS, the [`foreignCallHandler`](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) function simply means "a callback function that returns a value of type [`ForeignCallOutput`](../reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md). It doesn't have to be an RPC call like in the case for Nargo. - -:::tip - -Does this mean you don't have to write an RPC server like in [Step #2](#step-2---write-an-rpc-server)? - -You don't technically have to, but then how would you run `nargo test`? To use both `Nargo` and `NoirJS` in your development flow, you will have to write a JSON RPC server. - -::: - -In this case, let's make `foreignCallHandler` call the JSON RPC Server we created in [Step #2](#step-2---write-an-rpc-server), by making it a JSON RPC Client. - -For example, using the same `getSqrt` program in [Step #1](#step-1---modify-your-noir-program) (comments in the code): - -```js -import { JSONRPCClient } from "json-rpc-2.0"; - -// declaring the JSONRPCClient -const client = new JSONRPCClient((jsonRPCRequest) => { -// hitting the same JSON RPC Server we coded above - return fetch("http://localhost:5555", { - method: "POST", - headers: { - "content-type": "application/json", - }, - body: JSON.stringify(jsonRPCRequest), - }).then((response) => { - if (response.status === 200) { - return response - .json() - .then((jsonRPCResponse) => client.receive(jsonRPCResponse)); - } else if (jsonRPCRequest.id !== undefined) { - return Promise.reject(new Error(response.statusText)); - } - }); -}); - -// declaring a function that takes the name of the foreign call (getSqrt) and the inputs -const foreignCallHandler = async (name, input) => { - const inputs = input[0].map((i) => i.toString("hex")) - // notice that the "inputs" parameter contains *all* the inputs - // in this case we to make the RPC request with the first parameter "numbers", which would be input[0] - const oracleReturn = await client.request("resolve_foreign_call", [ - { - function: name, - inputs: [inputs] - }, - ]); - return [oracleReturn.values[0]]; -}; - -// the rest of your NoirJS code -const input = { input: [4, 16] }; -const { witness } = await noir.execute(input, foreignCallHandler); -``` - -:::tip - -If you're in a NoirJS environment running your RPC server together with a frontend app, you'll probably hit a familiar problem in full-stack development: requests being blocked by [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) policy. For development only, you can simply install and use the [`cors` npm package](https://www.npmjs.com/package/cors) to get around the problem: - -```bash -yarn add cors -``` - -and use it as a middleware: - -```js -import cors from "cors"; - -const app = express(); -app.use(cors()) -``` - -::: - -## Conclusion - -Hopefully by the end of this guide, you should be able to: - -- Write your own logic around Oracles and how to write a JSON RPC server to make them work with your Nargo commands. -- Provide custom foreign call handlers for NoirJS. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.33.0/how_to/how-to-recursion.md b/docs/versioned_docs/version-v0.33.0/how_to/how-to-recursion.md deleted file mode 100644 index c8c4dc9f5b4..00000000000 --- a/docs/versioned_docs/version-v0.33.0/how_to/how-to-recursion.md +++ /dev/null @@ -1,180 +0,0 @@ ---- -title: How to use recursion on NoirJS -description: Learn how to implement recursion with NoirJS, a powerful tool for creating smart contracts on the EVM blockchain. This guide assumes familiarity with NoirJS, solidity verifiers, and the Barretenberg proving backend. Discover how to generate both final and intermediate proofs using `noir_js` and `backend_barretenberg`. -keywords: - [ - "NoirJS", - "EVM blockchain", - "smart contracts", - "recursion", - "solidity verifiers", - "Barretenberg backend", - "noir_js", - "backend_barretenberg", - "intermediate proofs", - "final proofs", - "nargo compile", - "json import", - "recursive circuit", - "recursive app" - ] -sidebar_position: 1 ---- - -This guide shows you how to use recursive proofs in your NoirJS app. For the sake of clarity, it is assumed that: - -- You already have a NoirJS app. If you don't, please visit the [NoirJS tutorial](../tutorials/noirjs_app.md) and the [reference](../reference/NoirJS/noir_js/index.md). -- You are familiar with what are recursive proofs and you have read the [recursion explainer](../explainers/explainer-recursion.md) -- You already built a recursive circuit following [the reference](../noir/standard_library/recursion.mdx), and understand how it works. - -It is also assumed that you're not using `noir_wasm` for compilation, and instead you've used [`nargo compile`](../reference/nargo_commands.md) to generate the `json` you're now importing into your project. However, the guide should work just the same if you're using `noir_wasm`. - -:::info - -As you've read in the [explainer](../explainers/explainer-recursion.md), a recursive proof is an intermediate proof. This means that it doesn't necessarily generate the final step that makes it verifiable in a smart contract. However, it is easy to verify within another circuit. - -While "standard" usage of NoirJS packages abstracts final proofs, it currently lacks the necessary interface to abstract away intermediate proofs. This means that these proofs need to be created by using the backend directly. - -In short: - -- `noir_js` generates *only* final proofs -- `backend_barretenberg` generates both types of proofs - -::: - -In a standard recursive app, you're also dealing with at least two circuits. For the purpose of this guide, we will assume the following: - -- `main`: a circuit of type `assert(x != y)`, where `main` is marked with a `#[recursive]` attribute. This attribute states that the backend should generate proofs that are friendly for verification within another circuit. -- `recursive`: a circuit that verifies `main` - -For a full example of how recursive proofs work, please refer to the [noir-examples](https://github.com/noir-lang/noir-examples) repository. We will *not* be using it as a reference for this guide. - -## Step 1: Setup - -In a common NoirJS app, you need to instantiate a backend with something like `const backend = new Backend(circuit)`. Then you feed it to the `noir_js` interface. - -For recursion, this doesn't happen, and the only need for `noir_js` is only to `execute` a circuit and get its witness and return value. Everything else is not interfaced, so it needs to happen on the `backend` object. - -It is also recommended that you instantiate the backend with as many threads as possible, to allow for maximum concurrency: - -```js -const backend = new Backend(circuit, { threads: 8 }) -``` - -:::tip -You can use the [`os.cpus()`](https://nodejs.org/api/os.html#oscpus) object in `nodejs` or [`navigator.hardwareConcurrency`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/hardwareConcurrency) on the browser to make the most out of those glorious cpu cores -::: - -## Step 2: Generating the witness and the proof for `main` - -After instantiating the backend, you should also instantiate `noir_js`. We will use it to execute the circuit and get the witness. - -```js -const noir = new Noir(circuit) -const { witness } = noir.execute(input) -``` - -With this witness, you are now able to generate the intermediate proof for the main circuit: - -```js -const { proof, publicInputs } = await backend.generateProof(witness) -``` - -:::warning - -Always keep in mind what is actually happening on your development process, otherwise you'll quickly become confused about what circuit we are actually running and why! - -In this case, you can imagine that Alice (running the `main` circuit) is proving something to Bob (running the `recursive` circuit), and Bob is verifying her proof within his proof. - -With this in mind, it becomes clear that our intermediate proof is the one *meant to be verified within another circuit*, so it must be Alice's. Actually, the only final proof in this theoretical scenario would be the last one, sent on-chain. - -::: - -## Step 3 - Verification and proof artifacts - -Optionally, you are able to verify the intermediate proof: - -```js -const verified = await backend.verifyProof({ proof, publicInputs }) -``` - -This can be useful to make sure our intermediate proof was correctly generated. But the real goal is to do it within another circuit. For that, we need to generate recursive proof artifacts that will be passed to the circuit that is verifying the proof we just generated. Instead of passing the proof and verification key as a byte array, we pass them as fields which makes it cheaper to verify in a circuit: - -```js -const { proofAsFields, vkAsFields, vkHash } = await backend.generateRecursiveProofArtifacts( { publicInputs, proof }, publicInputsCount) -``` - -This call takes the public inputs and the proof, but also the public inputs count. While this is easily retrievable by simply counting the `publicInputs` length, the backend interface doesn't currently abstract it away. - -:::info - -The `proofAsFields` has a constant size `[Field; 93]` and verification keys in Barretenberg are always `[Field; 114]`. - -::: - -:::warning - -One common mistake is to forget *who* makes this call. - -In a situation where Alice is generating the `main` proof, if she generates the proof artifacts and sends them to Bob, which gladly takes them as true, this would mean Alice could prove anything! - -Instead, Bob needs to make sure *he* extracts the proof artifacts, using his own instance of the `main` circuit backend. This way, Alice has to provide a valid proof for the correct `main` circuit. - -::: - -## Step 4 - Recursive proof generation - -With the artifacts, generating a recursive proof is no different from a normal proof. You simply use the `backend` (with the recursive circuit) to generate it: - -```js -const recursiveInputs = { - verification_key: vkAsFields, // array of length 114 - proof: proofAsFields, // array of length 93 + size of public inputs - publicInputs: [mainInput.y], // using the example above, where `y` is the only public input - key_hash: vkHash, -} - -const { witness, returnValue } = noir.execute(recursiveInputs) // we're executing the recursive circuit now! -const { proof, publicInputs } = backend.generateProof(witness) -const verified = backend.verifyProof({ proof, publicInputs }) -``` - -You can obviously chain this proof into another proof. In fact, if you're using recursive proofs, you're probably interested of using them this way! - -:::tip - -Managing circuits and "who does what" can be confusing. To make sure your naming is consistent, you can keep them in an object. For example: - -```js -const circuits = { - main: mainJSON, - recursive: recursiveJSON -} -const backends = { - main: new BarretenbergBackend(circuits.main), - recursive: new BarretenbergBackend(circuits.recursive) -} -const noir_programs = { - main: new Noir(circuits.main), - recursive: new Noir(circuits.recursive) -} -``` - -This allows you to neatly call exactly the method you want without conflicting names: - -```js -// Alice runs this 👇 -const { witness: mainWitness } = await noir_programs.main.execute(input) -const proof = await backends.main.generateProof(mainWitness) - -// Bob runs this 👇 -const verified = await backends.main.verifyProof(proof) -const { proofAsFields, vkAsFields, vkHash } = await backends.main.generateRecursiveProofArtifacts( - proof, - numPublicInputs, -); -const { witness: recursiveWitness } = await noir_programs.recursive.execute(recursiveInputs) -const recursiveProof = await backends.recursive.generateProof(recursiveWitness); -``` - -::: diff --git a/docs/versioned_docs/version-v0.33.0/how_to/how-to-solidity-verifier.md b/docs/versioned_docs/version-v0.33.0/how_to/how-to-solidity-verifier.md deleted file mode 100644 index 063538f1fc2..00000000000 --- a/docs/versioned_docs/version-v0.33.0/how_to/how-to-solidity-verifier.md +++ /dev/null @@ -1,253 +0,0 @@ ---- -title: Generate a 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, - ] -sidebar_position: 0 -pagination_next: tutorials/noirjs_app ---- - -Noir has the ability to generate a verifier contract in Solidity, which can be deployed in many EVM-compatible blockchains such as Ethereum. - -This allows for a powerful feature set, as one can make use of the conciseness and the privacy provided by Noir in an immutable ledger. Applications can range from simple P2P guessing games, to complex private DeFi interactions. - -This guide shows you how to generate a Solidity Verifier and deploy it on the [Remix IDE](https://remix.ethereum.org/). It is assumed that: - -- You are comfortable with the Solidity programming language and understand how contracts are deployed on the Ethereum network -- You have Noir installed and you have a Noir program. If you don't, [get started](../getting_started/installation/index.md) with Nargo and the example Hello Noir circuit -- You are comfortable navigating RemixIDE. If you aren't or you need a refresher, you can find some video tutorials [here](https://www.youtube.com/channel/UCjTUPyFEr2xDGN6Cg8nKDaA) that could help you. - -## Rundown - -Generating a Solidity Verifier contract is actually a one-command process. However, compiling it and deploying it can have some caveats. Here's the rundown of this guide: - -1. How to generate a solidity smart contract -2. How to compile the smart contract in the RemixIDE -3. How to deploy it to a testnet - -## Step 1 - Generate a contract - -This is by far the most straightforward step. Just run: - -```sh -nargo compile -``` - -This will compile your source code into a Noir build artifact to be stored in the `./target` directory, you can then generate the smart contract using the commands: - -```sh -# Here we pass the path to the newly generated Noir artifact. -bb write_vk -b ./target/.json -bb contract -``` - -replacing `` with the name of your Noir project. A new `contract` folder would then be generated in your project directory, containing the Solidity -file `contract.sol`. It can be deployed to any EVM blockchain acting as a verifier smart contract. - -You can find more information about `bb` and the default Noir proving backend on [this page](../getting_started/hello_noir/index.md#proving-backend). - -:::info - -It is possible to generate 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 generation of verifier contracts, for the time being these are only in Solidity. -::: - -## Step 2 - Compiling - -We will mostly skip the details of RemixIDE, as the UI can change from version to version. For now, we can just open -Remix and create a blank workspace. - -![Create Workspace](@site/static/img/how-tos/solidity_verifier_1.png) - -We will create a new file to contain the contract Nargo generated, and copy-paste its content. - -:::warning - -You'll likely see a warning advising you to not trust pasted code. While it is an important warning, it is irrelevant in the context of this guide and can be ignored. We will not be deploying anywhere near a mainnet. - -::: - -To compile our the verifier, we can navigate to the compilation tab: - -![Compilation Tab](@site/static/img/how-tos/solidity_verifier_2.png) - -Remix should automatically match a suitable compiler version. However, hitting the "Compile" button will most likely generate a "Stack too deep" error: - -![Stack too deep](@site/static/img/how-tos/solidity_verifier_3.png) - -This is due to the verify function needing to put many variables on the stack, but enabling the optimizer resolves the issue. To do this, let's open the "Advanced Configurations" tab and enable optimization. The default 200 runs will suffice. - -:::info - -This time we will see a warning about an unused function parameter. This is expected, as the `verify` function doesn't use the `_proof` parameter inside a solidity block, it is loaded from calldata and used in assembly. - -::: - -![Compilation success](@site/static/img/how-tos/solidity_verifier_4.png) - -## Step 3 - Deploying - -At this point we should have a compiled contract ready to deploy. If we navigate to the deploy section in Remix, we will see many different environments we can deploy to. The steps to deploy on each environment would be out-of-scope for this guide, so we will just use the default Remix VM. - -Looking closely, we will notice that our "Solidity Verifier" is actually three contracts working together: - -- An `UltraVerificationKey` library which simply stores the verification key for our circuit. -- An abstract contract `BaseUltraVerifier` containing most of the verifying logic. -- A main `UltraVerifier` contract that inherits from the Base and uses the Key contract. - -Remix will take care of the dependencies for us so we can simply deploy the UltraVerifier contract by selecting it and hitting "deploy": - -![Deploying UltraVerifier](@site/static/img/how-tos/solidity_verifier_5.png) - -A contract will show up in the "Deployed Contracts" section, where we can retrieve the Verification Key Hash. This is particularly useful for double-checking that the deployer contract is the correct one. - -:::note - -Why "UltraVerifier"? - -To be precise, the Noir compiler (`nargo`) doesn't generate the verifier contract directly. It compiles the Noir code into an intermediate language (ACIR), which is then executed by the backend. So it is the backend that returns the verifier smart contract, not Noir. - -In this case, the Barretenberg Backend uses the UltraPlonk proving system, hence the "UltraVerifier" name. - -::: - -## Step 4 - Verifying - -To verify a proof using the Solidity verifier contract, we call the `verify` function in this extended contract: - -```solidity -function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external view returns (bool) -``` - -When using the default example in the [Hello Noir](../getting_started/hello_noir/index.md) guide, the easiest way to confirm that the verifier contract is doing its job is by calling the `verify` function via remix with the required parameters. Note that the public inputs must be passed in separately to the rest of the proof so we must split the proof as returned from `bb`. - -First generate a proof with `bb` at the location `./proof` using the steps in [get started](../getting_started/hello_noir/index.md), this proof is in a binary format but we want to convert it into a hex string to pass into Remix, this can be done with the - -```bash -# This value must be changed to match the number of public inputs (including return values!) in your program. -NUM_PUBLIC_INPUTS=1 -PUBLIC_INPUT_BYTES=32*NUM_PUBLIC_INPUTS -HEX_PUBLIC_INPUTS=$(head -c $PUBLIC_INPUT_BYTES ./proof | od -An -v -t x1 | tr -d $' \n') -HEX_PROOF=$(tail -c +$(($PUBLIC_INPUT_BYTES + 1)) ./proof | od -An -v -t x1 | tr -d $' \n') - -echo "Public inputs:" -echo $HEX_PUBLIC_INPUTS - -echo "Proof:" -echo "0x$HEX_PROOF" -``` - -Remix expects that the public inputs will be split into an array of `bytes32` values so `HEX_PUBLIC_INPUTS` needs to be split up into 32 byte chunks which are prefixed with `0x` accordingly. - -A programmatic example of how the `verify` function is called can be seen 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"); -``` - -:::info[Return Values] - -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. - -For example, if you have Noir program like this: - -```rust -fn main( - // Public inputs - pubkey_x: pub Field, - pubkey_y: pub Field, - // Private inputs - priv_key: Field, -) -> pub Field -``` - -the `verify` function will expect the public inputs array (second function parameter) to be of length 3, the two inputs and the return value. - -Passing only two inputs will result in an error such as `PUBLIC_INPUT_COUNT_INVALID(3, 2)`. - -In this case, the inputs parameter to `verify` would be an array ordered as `[pubkey_x, pubkey_y, return`. - -::: - -:::tip[Structs] - -You can pass structs to the verifier contract. They will be flattened so that the array of inputs is 1-dimensional array. - -For example, 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) { - //... -} -``` - -The order of these inputs would be flattened to: `[x, nested.t1.val1, nested.t1.val2, nested.is_true, y]` - -::: - -The other function you can call is our entrypoint `verify` function, as defined above. - -:::tip - -It's worth noticing that the `verify` function is actually a `view` function. A `view` function does not alter the blockchain state, so it doesn't need to be distributed (i.e. it will run only on the executing node), and therefore doesn't cost any gas. - -This can be particularly useful in some situations. If Alice generated a proof and wants Bob to verify its correctness, Bob doesn't need to run Nargo, NoirJS, or any Noir specific infrastructure. He can simply make a call to the blockchain with the proof and verify it is correct without paying any gas. - -It would be incorrect to say that a Noir proof verification costs any gas at all. However, most of the time the result of `verify` is used to modify state (for example, to update a balance, a game state, etc). In that case the whole network needs to execute it, which does incur gas costs (calldata and execution, but not storage). - -::: - -## A Note on EVM chains - -Noir proof verification requires the ecMul, ecAdd and ecPairing precompiles. Not all EVM chains support EC Pairings, notably some of the ZK-EVMs. This means that you won't be able to use the verifier contract in all of them. You can find an incomplete list of which EVM chains support these precompiles [here](https://www.evmdiff.com/features?feature=precompiles). - -For example, chains like `zkSync ERA` and `Polygon zkEVM` do not currently support these precompiles, so proof verification via Solidity verifier contracts won't work. Here's a quick list of EVM chains that have been tested and are known to work: - -- Optimism -- Arbitrum -- Polygon PoS -- Scroll -- Celo - -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. - -## What's next - -Now that you know how to call a Noir Solidity Verifier on a smart contract using Remix, you should be comfortable with using it with some programmatic frameworks, such as [hardhat](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat) and [foundry](https://github.com/noir-lang/noir-starter/tree/main/with-foundry). - -You can find other tools, examples, boilerplates and libraries in the [awesome-noir](https://github.com/noir-lang/awesome-noir) repository. - -You should also be ready to write and deploy your first NoirJS app and start generating proofs on websites, phones, and NodeJS environments! Head on to the [NoirJS tutorial](../tutorials/noirjs_app.md) to learn how to do that. diff --git a/docs/versioned_docs/version-v0.33.0/how_to/merkle-proof.mdx b/docs/versioned_docs/version-v0.33.0/how_to/merkle-proof.mdx deleted file mode 100644 index 0a128adb2de..00000000000 --- a/docs/versioned_docs/version-v0.33.0/how_to/merkle-proof.mdx +++ /dev/null @@ -1,48 +0,0 @@ ---- -title: Prove Merkle Tree 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] -sidebar_position: 4 ---- - -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 - -fn main(message : [Field; 62], index : Field, hashpath : [Field; 40], root : Field) { - let leaf = std::hash::hash_to_field(message.as_slice()); - 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.as_slice()); -``` - -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.33.0/how_to/using-devcontainers.mdx b/docs/versioned_docs/version-v0.33.0/how_to/using-devcontainers.mdx deleted file mode 100644 index 727ec6ca667..00000000000 --- a/docs/versioned_docs/version-v0.33.0/how_to/using-devcontainers.mdx +++ /dev/null @@ -1,110 +0,0 @@ ---- -title: Developer Containers and Codespaces -description: "Learn how to set up a devcontainer in your GitHub repository for a seamless coding experience with Codespaces. Follow our easy 8-step guide to create your own Noir environment without installing Nargo locally." -keywords: ["Devcontainer", "Codespaces", "GitHub", "Noir Environment", "Docker Image", "Development Environment", "Remote Coding", "GitHub Codespaces", "Noir Programming", "Nargo", "VSCode Extensions", "Noirup"] -sidebar_position: 1 ---- - -Adding a developer container configuration file to your Noir project is one of the easiest way to unlock coding in browser. - -## What's a devcontainer after all? - -A [Developer Container](https://containers.dev/) (devcontainer for short) is a Docker image that comes preloaded with tools, extensions, and other tools you need to quickly get started or continue a project, without having to install Nargo locally. Think of it as a development environment in a box. - -There are many advantages to this: - -- It's platform and architecture agnostic -- You don't need to have an IDE installed, or Nargo, or use a terminal at all -- It's safer for using on a public machine or public network - -One of the best ways of using devcontainers is... not using your machine at all, for maximum control, performance, and ease of use. -Enter Codespaces. - -## Codespaces - -If a devcontainer is just a Docker image, then what stops you from provisioning a `p3dn.24xlarge` AWS EC2 instance with 92 vCPUs and 768 GiB RAM and using it to prove your 10-gate SNARK proof? - -Nothing! Except perhaps the 30-40$ per hour it will cost you. - -The problem is that provisioning takes time, and I bet you don't want to see the AWS console every time you want to code something real quick. - -Fortunately, there's an easy and free way to get a decent remote machine ready and loaded in less than 2 minutes: Codespaces. [Codespaces is a Github feature](https://github.com/features/codespaces) that allows you to code in a remote machine by using devcontainers, and it's pretty cool: - -- You can start coding Noir in less than a minute -- It uses the resources of a remote machine, so you can code on your grandma's phone if needed be -- It makes it easy to share work with your frens -- It's fully reusable, you can stop and restart whenever you need to - -:::info - -Don't take out your wallet just yet. Free GitHub accounts get about [15-60 hours of coding](https://github.com/features/codespaces) for free per month, depending on the size of your provisioned machine. - -::: - -## Tell me it's _actually_ easy - -It is! - -Github comes with a default codespace and you can use it to code your own devcontainer. That's exactly what we will be doing in this guide. - - - -8 simple steps: - -#### 1. Create a new repository on GitHub. - -#### 2. Click "Start coding with Codespaces". This will use the default image. - -#### 3. Create a folder called `.devcontainer` in the root of your repository. - -#### 4. Create a Dockerfile in that folder, and paste the following code: - -```docker -FROM --platform=linux/amd64 node:lts-bookworm-slim -SHELL ["/bin/bash", "-c"] -RUN apt update && apt install -y curl bash git tar gzip libc++-dev -RUN curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash -ENV PATH="/root/.nargo/bin:$PATH" -RUN noirup -ENTRYPOINT ["nargo"] -``` -#### 5. Create a file called `devcontainer.json` in the same folder, and paste the following code: - -```json -{ - "name": "Noir on Codespaces", - "build": { - "context": ".", - "dockerfile": "Dockerfile" - }, - "customizations": { - "vscode": { - "extensions": ["noir-lang.vscode-noir"] - } - } -} -``` -#### 6. Commit and push your changes - -This will pull the new image and build it, so it could take a minute or so - -#### 8. Done! -Just wait for the build to finish, and there's your easy Noir environment. - - -Refer to [noir-starter](https://github.com/noir-lang/noir-starter/) as an example of how devcontainers can be used together with codespaces. - - - -## How do I use it? - -Using the codespace is obviously much easier than setting it up. -Just navigate to your repository and click "Code" -> "Open with Codespaces". It should take a few seconds to load, and you're ready to go. - -:::info - -If you really like the experience, you can add a badge to your readme, links to existing codespaces, and more. -Check out the [official docs](https://docs.github.com/en/codespaces/setting-up-your-project-for-codespaces/setting-up-your-repository/facilitating-quick-creation-and-resumption-of-codespaces) for more info. diff --git a/docs/versioned_docs/version-v0.33.0/index.mdx b/docs/versioned_docs/version-v0.33.0/index.mdx deleted file mode 100644 index 9ed9662b0b9..00000000000 --- a/docs/versioned_docs/version-v0.33.0/index.mdx +++ /dev/null @@ -1,67 +0,0 @@ ---- -title: Noir Lang -hide_title: true -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] -sidebar_position: 0 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -Noir Logo - -Noir is an open-source Domain-Specific Language for safe and seamless construction of privacy-preserving Zero-Knowledge programs, requiring no previous knowledge on the underlying mathematics or cryptography. - -ZK programs are programs that can generate short proofs of statements without revealing all inputs to the statements. You can read more about Zero-Knowledge Proofs [here](https://dev.to/spalladino/a-beginners-intro-to-coding-zero-knowledge-proofs-c56). - -## What's new about Noir? - -Noir works differently from most ZK languages by taking a two-pronged path. First, it compiles the program to an adaptable intermediate language known as ACIR. From there, depending on a given project's needs, ACIR can be further compiled into an arithmetic circuit for integration with the proving backend. - -:::info - -Noir is backend agnostic, which means it makes no assumptions on which proving backend powers the ZK proof. Being the language that powers [Aztec Contracts](https://docs.aztec.network/developers/contracts/main), it defaults to Aztec's Barretenberg proving backend. - -However, the ACIR output can be transformed to be compatible with other PLONK-based backends, or into a [rank-1 constraint system](https://www.rareskills.io/post/rank-1-constraint-system) suitable for backends such as Arkwork's Marlin. - -::: - -## Who is Noir for? - -Noir can be used both in complex cloud-based backends and in user's smartphones, requiring no knowledge on the underlying math or cryptography. From authorization systems that keep a password in the user's device, to complex on-chain verification of recursive proofs, Noir is designed to abstract away complexity without any significant overhead. Here are some examples of situations where Noir can be used: - - - - Noir Logo - - Aztec Contracts leverage Noir to allow for the storage and execution of private information. Writing an Aztec Contract is as easy as writing Noir, and Aztec developers can easily interact with the network storage and execution through the [Aztec.nr](https://docs.aztec.network/developers/contracts/main) library. - - - Solidity Verifier Example - Noir can auto-generate Solidity verifier contracts that verify Noir proofs. This allows for non-interactive verification of proofs containing private information in an immutable system. This feature powers a multitude of use-case scenarios, from P2P chess tournaments, to [Aztec Layer-2 Blockchain](https://docs.aztec.network/) - - - Aztec Labs developed NoirJS, an easy interface to generate and verify Noir proofs in a Javascript environment. This allows for Noir to be used in webpages, mobile apps, games, and any other environment supporting JS execution in a standalone manner. - - - - -## Libraries - -Noir is meant to be easy to extend by simply importing Noir libraries just like in Rust. -The [awesome-noir repo](https://github.com/noir-lang/awesome-noir#libraries) is a collection of libraries developed by the Noir community. -Writing a new library is easy and makes code be composable and easy to reuse. See the section on [dependencies](noir/modules_packages_crates/dependencies.md) for more information. diff --git a/docs/versioned_docs/version-v0.33.0/migration_notes.md b/docs/versioned_docs/version-v0.33.0/migration_notes.md deleted file mode 100644 index 6bd740024e5..00000000000 --- a/docs/versioned_docs/version-v0.33.0/migration_notes.md +++ /dev/null @@ -1,105 +0,0 @@ ---- -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. - -### `backend encountered an error: libc++.so.1` - -Depending on your OS, you may encounter the following error when running `nargo prove` for the first time: - -```text -The backend encountered an error: "/home/codespace/.nargo/backends/acvm-backend-barretenberg/backend_binary: error while loading shared libraries: libc++.so.1: cannot open shared object file: No such file or directory\n" -``` - -Install the `libc++-dev` library with: - -```bash -sudo apt install libc++-dev -``` - -## ≥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](noir/concepts/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 your 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 file is running [this bash script](https://github.com/noir-lang/barretenberg-js-binary/blob/master/run-bb-js.sh), 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.33.0/noir/concepts/_category_.json b/docs/versioned_docs/version-v0.33.0/noir/concepts/_category_.json deleted file mode 100644 index 7da08f8a8c5..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/concepts/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "Concepts", - "position": 0, - "collapsible": true, - "collapsed": true -} \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.33.0/noir/concepts/assert.md b/docs/versioned_docs/version-v0.33.0/noir/concepts/assert.md deleted file mode 100644 index 2132de42072..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/concepts/assert.md +++ /dev/null @@ -1,78 +0,0 @@ ---- -title: Assert Function -description: - Learn about the `assert` and `static_assert` functions 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 or compile-time, respectively. -keywords: [Noir programming language, assert statement, predicate expression, comparison expression] -sidebar_position: 4 ---- - -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); -} -``` - -> 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`. - -You can optionally provide a message to be logged when the assertion fails: - -```rust -assert(x == y, "x and y are not equal"); -``` - -Aside string literals, the optional message can be a format string or any other type supported as input for Noir's [print](../standard_library/logging.md) functions. This feature lets you incorporate runtime variables into your failed assertion logs: - -```rust -assert(x == y, f"Expected x == y, but got {x} == {y}"); -``` - -Using a variable as an assertion message directly: - -```rust -struct myStruct { - myField: Field -} - -let s = myStruct { myField: y }; -assert(s.myField == x, s); -``` - -There is also a special `static_assert` function that behaves like `assert`, -but that runs at compile-time. - -```rust -fn main(xs: [Field; 3]) { - let x = 2 + 2; - let y = 4; - static_assert(x == y, "expected 2 + 2 to equal 4"); - - // This passes since the length of `xs` is known at compile-time - static_assert(xs.len() == 3, "expected the input to have 3 elements"); -} -``` - -This function fails when passed a dynamic (run-time) argument: - -```rust -fn main(x : Field, y : Field) { - // this fails because `x` is not known at compile-time - static_assert(x == 2, "expected x to be known at compile-time and equal to 2"); - - let mut example_slice = &[]; - if y == 4 { - example_slice = example_slice.push_back(0); - } - - // This fails because the length of `example_slice` is not known at - // compile-time - let error_message = "expected an empty slice, known at compile-time"; - static_assert(example_slice.len() == 0, error_message); -} -``` - diff --git a/docs/versioned_docs/version-v0.33.0/noir/concepts/comments.md b/docs/versioned_docs/version-v0.33.0/noir/concepts/comments.md deleted file mode 100644 index b51a85f5c94..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/concepts/comments.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -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] -sidebar_position: 10 ---- - -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.33.0/noir/concepts/control_flow.md b/docs/versioned_docs/version-v0.33.0/noir/concepts/control_flow.md deleted file mode 100644 index 045d3c3a5f5..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/concepts/control_flow.md +++ /dev/null @@ -1,77 +0,0 @@ ---- -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] -sidebar_position: 2 ---- - -## 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); -``` - -## 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`. - -### Break and Continue - -In unconstrained code, `break` and `continue` are also allowed in `for` loops. These are only allowed -in unconstrained code since normal constrained code requires that Noir knows exactly how many iterations -a loop may have. `break` and `continue` can be used like so: - -```rust -for i in 0 .. 10 { - println("Iteration start") - - if i == 2 { - continue; - } - - if i == 5 { - break; - } - - println(i); -} -println("Loop end") -``` - -When used, `break` will end the current loop early and jump to the statement after the for loop. In the example -above, the `break` will stop the loop and jump to the `println("Loop end")`. - -`continue` will stop the current iteration of the loop, and jump to the start of the next iteration. In the example -above, `continue` will jump to `println("Iteration start")` when used. Note that the loop continues as normal after this. -The iteration variable `i` is still increased by one as normal when `continue` is used. - -`break` and `continue` cannot currently be used to jump out of more than a single loop at a time. diff --git a/docs/versioned_docs/version-v0.33.0/noir/concepts/data_bus.mdx b/docs/versioned_docs/version-v0.33.0/noir/concepts/data_bus.mdx deleted file mode 100644 index e55e58622ce..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/concepts/data_bus.mdx +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: Data Bus -sidebar_position: 13 ---- -import Experimental from '@site/src/components/Notes/_experimental.mdx'; - - - -The data bus is an optimization that the backend can use to make recursion more efficient. -In order to use it, you must define some inputs of the program entry points (usually the `main()` -function) with the `call_data` modifier, and the return values with the `return_data` modifier. -These modifiers are incompatible with `pub` and `mut` modifiers. - -## Example - -```rust -fn main(mut x: u32, y: call_data u32, z: call_data [u32;4] ) -> return_data u32 { - let a = z[x]; - a+y -} -``` - -As a result, both call_data and return_data will be treated as private inputs and encapsulated into a read-only array each, for the backend to process. diff --git a/docs/versioned_docs/version-v0.33.0/noir/concepts/data_types/_category_.json b/docs/versioned_docs/version-v0.33.0/noir/concepts/data_types/_category_.json deleted file mode 100644 index 5d694210bbf..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/concepts/data_types/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "position": 0, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.33.0/noir/concepts/data_types/arrays.md b/docs/versioned_docs/version-v0.33.0/noir/concepts/data_types/arrays.md deleted file mode 100644 index d26f6dff070..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/concepts/data_types/arrays.md +++ /dev/null @@ -1,253 +0,0 @@ ---- -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, - ] -sidebar_position: 4 ---- - -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.mdx), 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]; -``` - -However, multidimensional slices are not supported. For example, the following code will error at compile time: - -```rust -let slice : [[Field]] = &[]; -``` - -## 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. -Each of these functions are located within the generic impl `impl [T; N] {`. -So anywhere `self` appears, it refers to the variable `self: [T; N]`. - -### len - -Returns the length of an array - -```rust -fn len(self) -> 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(self) -> [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(self, 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(self, 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(self, 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 the starting element. - -```rust -fn reduce(self, 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(self, 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(self, 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.33.0/noir/concepts/data_types/booleans.md b/docs/versioned_docs/version-v0.33.0/noir/concepts/data_types/booleans.md deleted file mode 100644 index 2507af710e7..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/concepts/data_types/booleans.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -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, - ] -sidebar_position: 2 ---- - - -The `bool` type in Noir has two possible values: `true` and `false`: - -```rust -fn main() { - let t = true; - let f: bool = false; -} -``` - -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.md) and -[Assert Function](../assert.md) sections. diff --git a/docs/versioned_docs/version-v0.33.0/noir/concepts/data_types/fields.md b/docs/versioned_docs/version-v0.33.0/noir/concepts/data_types/fields.md deleted file mode 100644 index a10a4810788..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/concepts/data_types/fields.md +++ /dev/null @@ -1,192 +0,0 @@ ---- -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, - ] -sidebar_position: 0 ---- - -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] -``` - -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] -``` - -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); -} -``` - -### assert_max_bit_size - -Adds a constraint to specify that the field can be represented with `bit_size` number of bits - -```rust -fn assert_max_bit_size(self, bit_size: u32) -``` - -example: - -```rust -fn main() { - let field = 2 - field.assert_max_bit_size(32); -} -``` - -### 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 -``` - - -### lt - -Returns true if the field is less than the other field - -```rust -pub fn lt(self, another: Field) -> bool -``` diff --git a/docs/versioned_docs/version-v0.33.0/noir/concepts/data_types/function_types.md b/docs/versioned_docs/version-v0.33.0/noir/concepts/data_types/function_types.md deleted file mode 100644 index f6121af17e2..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/concepts/data_types/function_types.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: Function types -sidebar_position: 10 ---- - -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](../lambdas.md) for more details. diff --git a/docs/versioned_docs/version-v0.33.0/noir/concepts/data_types/index.md b/docs/versioned_docs/version-v0.33.0/noir/concepts/data_types/index.md deleted file mode 100644 index 3eadb2dc8a4..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/concepts/data_types/index.md +++ /dev/null @@ -1,118 +0,0 @@ ---- -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](../generics.md): - -```rust -type Id = Size; - -fn main() { - let id: Id = 1; - let zero: u32 = 0; - assert(zero + 1 == id); -} -``` - -Type aliases can even refer to other aliases. An error will be issued if they form a cycle: - -```rust -// Ok! -type A = B; -type B = Field; - -type Bad1 = Bad2; - -// error: Dependency cycle found -type Bad2 = Bad1; -// ^^^^^^^^^^^ 'Bad2' recursively depends on itself: Bad2 -> Bad1 -> Bad2 -``` - -## Wildcard Type -Noir can usually infer the type of the variable from the context, so specifying the type of a variable is only required when it cannot be inferred. However, specifying a complex type can be tedious, especially when it has multiple generic arguments. Often some of the generic types can be inferred from the context, and Noir only needs a hint to properly infer the other types. We can partially specify a variable's type by using `_` as a marker, indicating where we still want the compiler to infer the type. - -```rust -let a: [_; 4] = foo(b); -``` - - -### BigInt - -You can achieve BigInt functionality using the [Noir BigInt](https://github.com/shuklaayush/noir-bigint) library. diff --git a/docs/versioned_docs/version-v0.33.0/noir/concepts/data_types/integers.md b/docs/versioned_docs/version-v0.33.0/noir/concepts/data_types/integers.md deleted file mode 100644 index a1d59bf3166..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/concepts/data_types/integers.md +++ /dev/null @@ -1,156 +0,0 @@ ---- -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] -sidebar_position: 1 ---- - -An integer type is a range constrained field type. -The Noir frontend supports both unsigned and signed integer types. -The allowed sizes are 1, 8, 16, 32 and 64 bits. - -:::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\\$). - -## 128 bits Unsigned Integers - -The built-in structure `U128` allows you to use 128-bit unsigned integers almost like a native integer type. However, there are some differences to keep in mind: -- You cannot cast between a native integer and `U128` -- There is a higher performance cost when using `U128`, compared to a native type. - -Conversion between unsigned integer types and U128 are done through the use of `from_integer` and `to_integer` functions. `from_integer` also accepts the `Field` type as input. - -```rust -fn main() { - let x = U128::from_integer(23); - let y = U128::from_hex("0x7"); - let z = x + y; - assert(z.to_integer() == 30); -} -``` - -`U128` is implemented with two 64 bits limbs, representing the low and high bits, which explains the performance cost. You should expect `U128` to be twice more costly for addition and four times more costly for multiplication. -You can construct a U128 from its limbs: -```rust -fn main(x: u64, y: u64) { - let x = U128::from_u64s_be(x,y); - assert(z.hi == x as Field); - assert(z.lo == y as Field); -} -``` - -Note that the limbs are stored as Field elements in order to avoid unnecessary conversions. -Apart from this, most operations will work as usual: - -```rust -fn main(x: U128, y: U128) { - // multiplication - let c = x * y; - // addition and subtraction - let c = c - x + y; - // division - let c = x / y; - // bit operation; - let c = x & y | y; - // bit shift - let c = x << y; - // comparisons; - let c = x < y; - let c = x == y; -} -``` - -## 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 execute -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 - -fn main(x: u8, y: u8) -> pub u8 { - std::wrapping_add(x, y) -} -``` diff --git a/docs/versioned_docs/version-v0.33.0/noir/concepts/data_types/references.md b/docs/versioned_docs/version-v0.33.0/noir/concepts/data_types/references.md deleted file mode 100644 index a5293d11cfb..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/concepts/data_types/references.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: References -sidebar_position: 9 ---- - -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.33.0/noir/concepts/data_types/slices.mdx b/docs/versioned_docs/version-v0.33.0/noir/concepts/data_types/slices.mdx deleted file mode 100644 index 95da2030843..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/concepts/data_types/slices.mdx +++ /dev/null @@ -1,358 +0,0 @@ ---- -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] -sidebar_position: 5 ---- - -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 -fn main() -> pub u32 { - let mut slice: [Field] = &[0; 2]; - - let mut new_slice = slice.push_back(6); - new_slice.len() -} -``` - -To write a slice literal, use a preceeding ampersand as in: `&[0; 2]` or -`&[1, 2, 3]`. - -It is important to note that slices are not references to arrays. In Noir, -`&[..]` is more similar to an immutable, growable vector. - -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); -``` - -### len - -Returns the length of a slice - -```rust -fn len(self) -> Field -``` - -Example: - -```rust -fn main() { - let slice = &[42, 42]; - assert(slice.len() == 2); -} -``` - -### as_array - -Converts this slice into an array. - -Make sure to specify the size of the resulting array. -Panics if the resulting array length is different than the slice's length. - -```rust -fn as_array(self) -> [T; N] -``` - -Example: - -```rust -fn main() { - let slice = &[5, 6]; - - // Always specify the length of the resulting array! - let array: [Field; 2] = slice.as_array(); - - assert(array[0] == slice[0]); - assert(array[1] == slice[1]); -} -``` - -### map - -Applies a function to each element of the slice, returning a new slice containing the mapped elements. - -```rust -fn map(self, f: fn[Env](T) -> U) -> [U] -``` - -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 slice, returning the final accumulated value. The first -parameter is the initial value. - -```rust -fn fold(self, mut accumulator: U, f: fn[Env](U, T) -> U) -> U -``` - -This is a left fold, so the given function will be applied to the accumulator and first element of -the slice, 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 slice = &[2, 2, 2, 2, 2]; - let folded = slice.fold(0, |a, b| a + b); - assert(folded == 10); -} - -``` - -### reduce - -Same as fold, but uses the first element as the starting element. - -```rust -fn reduce(self, f: fn[Env](T, T) -> T) -> T -``` - -example: - -```rust -fn main() { - let slice = &[2, 2, 2, 2, 2]; - let reduced = slice.reduce(|a, b| a + b); - assert(reduced == 10); -} -``` - -### filter - -Returns a new slice containing only elements for which the given predicate returns true. - -```rust -fn filter(self, f: fn[Env](T) -> bool) -> Self -``` - -example: - -```rust -fn main() { - let slice = &[1, 2, 3, 4, 5]; - let odds = slice.filter(|x| x % 2 == 1); - assert_eq(odds, &[1, 3, 5]); -} -``` - -### join - -Flatten each element in the slice into one value, separated by `separator`. - -Note that although slices implement `Append`, `join` cannot be used on slice -elements since nested slices are prohibited. - -```rust -fn join(self, separator: T) -> T where T: Append -``` - -example: - -```rust -struct Accumulator { - total: Field, -} - -// "Append" two accumulators by adding them -impl Append for Accumulator { - fn empty() -> Self { - Self { total: 0 } - } - - fn append(self, other: Self) -> Self { - Self { total: self.total + other.total } - } -} - -fn main() { - let slice = &[1, 2, 3, 4, 5].map(|total| Accumulator { total }); - - let result = slice.join(Accumulator::empty()); - assert_eq(result, Accumulator { total: 15 }); - - // We can use a non-empty separator to insert additional elements to sum: - let separator = Accumulator { total: 10 }; - let result = slice.join(separator); - assert_eq(result, Accumulator { total: 55 }); -} -``` - -### all - -Returns true if all the elements satisfy the given predicate - -```rust -fn all(self, predicate: fn[Env](T) -> bool) -> bool -``` - -example: - -```rust -fn main() { - let slice = &[2, 2, 2, 2, 2]; - let all = slice.all(|a| a == 2); - assert(all); -} -``` - -### any - -Returns true if any of the elements satisfy the given predicate - -```rust -fn any(self, predicate: fn[Env](T) -> bool) -> bool -``` - -example: - -```rust -fn main() { - let slice = &[2, 2, 2, 2, 5]; - let any = slice.any(|a| a == 5); - assert(any); -} - -``` diff --git a/docs/versioned_docs/version-v0.33.0/noir/concepts/data_types/strings.md b/docs/versioned_docs/version-v0.33.0/noir/concepts/data_types/strings.md deleted file mode 100644 index 1fdee42425e..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/concepts/data_types/strings.md +++ /dev/null @@ -1,79 +0,0 @@ ---- -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, - ] -sidebar_position: 3 ---- - - -The string type is a fixed length value defined with `str`. - -You can use strings in `assert()` functions or print them with -`println()`. See more about [Logging](../../standard_library/logging.md). - -```rust - -fn main(message : pub str<11>, hex_as_string : str<4>) { - 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" -``` - -## 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/versioned_docs/version-v0.33.0/noir/concepts/data_types/structs.md b/docs/versioned_docs/version-v0.33.0/noir/concepts/data_types/structs.md deleted file mode 100644 index dbf68c99813..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/concepts/data_types/structs.md +++ /dev/null @@ -1,70 +0,0 @@ ---- -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, - ] -sidebar_position: 8 ---- - -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.33.0/noir/concepts/data_types/tuples.md b/docs/versioned_docs/version-v0.33.0/noir/concepts/data_types/tuples.md deleted file mode 100644 index 2ec5c9c4113..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/concepts/data_types/tuples.md +++ /dev/null @@ -1,48 +0,0 @@ ---- -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, - ] -sidebar_position: 7 ---- - -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.33.0/noir/concepts/functions.md b/docs/versioned_docs/version-v0.33.0/noir/concepts/functions.md deleted file mode 100644 index f656cdfd97a..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/concepts/functions.md +++ /dev/null @@ -1,226 +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] -sidebar_position: 1 ---- - -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](../../tooling/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](./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](./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](./unconstrained.md) and [NoirJS](../../reference/NoirJS/noir_js/index.md) for more details. -- **test**: mark the function as unit tests. See [Tests](../../tooling/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.33.0/noir/concepts/generics.md b/docs/versioned_docs/version-v0.33.0/noir/concepts/generics.md deleted file mode 100644 index 3e416eee093..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/concepts/generics.md +++ /dev/null @@ -1,163 +0,0 @@ ---- -title: Generics -description: Learn how to use Generics in Noir -keywords: [Noir, Rust, generics, functions, structs] -sidebar_position: 7 ---- - -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 print(self) { - for _i in 0 .. self.count { - println(self.value); - } - } -} - -fn main() { - let repeated = RepeatedValue { value: "Hello!", count: 2 }; - 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 - -Since a generic type `T` can represent any type, how can we call functions on the underlying type? -In other words, how can we go from "any type `T`" to "any type `T` that has certain methods available?" - -This is what [traits](../concepts/traits.md) are for in Noir. Here's an example of a function generic over -any type `T` that implements the `Eq` trait for equality: - -```rust -fn first_element_is_equal(array1: [T; N], array2: [T; N]) -> bool - where T: Eq -{ - if (array1.len() == 0) | (array2.len() == 0) { - true - } else { - array1[0] == array2[0] - } -} - -fn main() { - assert(first_element_is_equal([1, 2, 3], [1, 5, 6])); - - // We can use first_element_is_equal for arrays of any type - // as long as we have an Eq impl for the types we pass in - let array = [MyStruct::new(), MyStruct::new()]; - assert(array_eq(array, array, MyStruct::eq)); -} - -impl Eq for MyStruct { - fn eq(self, other: MyStruct) -> bool { - self.foo == other.foo - } -} -``` - -You can find more details on traits and trait implementations on the [traits page](../concepts/traits.md). - -## Manually Specifying Generics with the Turbofish Operator - -There are times when the compiler cannot reasonably infer what type should be used for a generic, or when the developer themselves may want to manually distinguish generic type parameters. This is where the `::<>` turbofish operator comes into play. - -The `::<>` operator can follow a variable or path and can be used to manually specify generic arguments within the angle brackets. -The name "turbofish" comes from that `::<>` looks like a little fish. - -Examples: -```rust -fn main() { - let mut slice = []; - slice = slice.push_back(1); - slice = slice.push_back(2); - // Without turbofish a type annotation would be needed on the left hand side - let array = slice.as_array::<2>(); -} -``` -```rust -fn double() -> u32 { - N * 2 -} -fn example() { - assert(double::<9>() == 18); - assert(double::<7 + 8>() == 30); -} -``` -```rust -trait MyTrait { - fn ten() -> Self; -} - -impl MyTrait for Field { - fn ten() -> Self { 10 } -} - -struct Foo { - inner: T -} - -impl Foo { - fn generic_method(_self: Self) -> U where U: MyTrait { - U::ten() - } -} - -fn example() { - let foo: Foo = Foo { inner: 1 }; - // Using a type other than `Field` here (e.g. u32) would fail as - // there is no matching impl for `u32: MyTrait`. - // - // Substituting the `10` on the left hand side of this assert - // with `10 as u32` would also fail with a type mismatch as we - // are expecting a `Field` from the right hand side. - assert(10 as u32 == foo.generic_method::()); -} -``` diff --git a/docs/versioned_docs/version-v0.33.0/noir/concepts/globals.md b/docs/versioned_docs/version-v0.33.0/noir/concepts/globals.md deleted file mode 100644 index 063a3d89248..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/concepts/globals.md +++ /dev/null @@ -1,72 +0,0 @@ ---- -title: Global Variables -description: - Learn about global variables in Noir. Discover how - to declare, modify, and use them in your programs. -keywords: [noir programming language, globals, global variables, constants] -sidebar_position: 8 ---- - -## Globals - - -Noir supports global variables. The global's type can be inferred by the compiler entirely: - -```rust -global N = 5; // Same as `global N: Field = 5` - -global TUPLE = (3, 2); - -fn main() { - assert(N == 5); - assert(N == TUPLE.0 + TUPLE.1); -} -``` - -:::info - -Globals can be defined as any expression, so long as they don't depend on themselves - otherwise there would be a dependency cycle! For example: - -```rust -global T = foo(T); // dependency error -``` - -::: - - -If they are initialized to a literal integer, globals can be used to specify an array's length: - -```rust -global N: Field = 2; - -fn main(y : [Field; N]) { - assert(y[0] == y[1]) -} -``` - -A global from another module can be imported or referenced externally like any other name: - -```rust -global N = 20; - -fn main() { - assert(my_submodule::N != N); -} - -mod my_submodule { - global N: Field = 10; -} -``` - -When a global is used, Noir replaces the name with its definition on each occurrence. -This means globals defined using function calls will repeat the call each time they're used: - -```rust -global RESULT = foo(); - -fn foo() -> [Field; 100] { ... } -``` - -This is usually fine since Noir will generally optimize any function call that does not -refer to a program input into a constant. It should be kept in mind however, if the called -function performs side-effects like `println`, as these will still occur on each use. diff --git a/docs/versioned_docs/version-v0.33.0/noir/concepts/lambdas.md b/docs/versioned_docs/version-v0.33.0/noir/concepts/lambdas.md deleted file mode 100644 index be3c7e0b5ca..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/concepts/lambdas.md +++ /dev/null @@ -1,81 +0,0 @@ ---- -title: Lambdas -description: Learn how to use anonymous functions in Noir programming language. -keywords: [Noir programming language, lambda, closure, function, anonymous function] -sidebar_position: 9 ---- - -## 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.33.0/noir/concepts/mutability.md b/docs/versioned_docs/version-v0.33.0/noir/concepts/mutability.md deleted file mode 100644 index fdeef6a87c5..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/concepts/mutability.md +++ /dev/null @@ -1,121 +0,0 @@ ---- -title: Mutability -description: - Learn about mutable variables in Noir. Discover how - to declare, modify, and use them in your programs. -keywords: [noir programming language, mutability in noir, mutable variables] -sidebar_position: 8 ---- - -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; -} -``` - -## Non-local mutability - -Non-local mutability can be achieved through the mutable reference type `&mut T`: - -```rust -fn set_to_zero(x: &mut Field) { - *x = 0; -} - -fn main() { - let mut y = 42; - set_to_zero(&mut y); - assert(*y == 0); -} -``` - -When creating a mutable reference, the original variable being referred to (`y` in this -example) must also be mutable. Since mutable references are a reference type, they must -be explicitly dereferenced via `*` to retrieve the underlying value. Note that this yields -a copy of the value, so mutating this copy will not change the original value behind the -reference: - -```rust -fn main() { - let mut x = 1; - let x_ref = &mut x; - - let mut y = *x_ref; - let y_ref = &mut y; - - x = 2; - *x_ref = 3; - - y = 4; - *y_ref = 5; - - assert(x == 3); - assert(*x_ref == 3); - assert(y == 5); - assert(*y_ref == 5); -} -``` - -Note that types in Noir are actually deeply immutable so the copy that occurs when -dereferencing is only a conceptual copy - no additional constraints will occur. - -Mutable references can also be stored within structs. Note that there is also -no lifetime parameter on these unlike rust. This is because the allocated memory -always lasts the entire program - as if it were an array of one element. - -```rust -struct Foo { - x: &mut Field -} - -impl Foo { - fn incr(mut self) { - *self.x += 1; - } -} - -fn main() { - let foo = Foo { x: &mut 0 }; - foo.incr(); - assert(*foo.x == 1); -} -``` - -In general, you should avoid non-local & shared mutability unless it is needed. Sticking -to only local mutability will improve readability and potentially improve compiler optimizations as well. diff --git a/docs/versioned_docs/version-v0.33.0/noir/concepts/ops.md b/docs/versioned_docs/version-v0.33.0/noir/concepts/ops.md deleted file mode 100644 index c35c36c38a9..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/concepts/ops.md +++ /dev/null @@ -1,98 +0,0 @@ ---- -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, - ] -sidebar_position: 3 ---- - -# 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, shift must be u8 | -| >> | Right shift an integer by another integer amount | Types must be integer, shift must be u8 | -| ! | 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 identically 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.33.0/noir/concepts/oracles.mdx b/docs/versioned_docs/version-v0.33.0/noir/concepts/oracles.mdx deleted file mode 100644 index 77a2ac1550a..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/concepts/oracles.mdx +++ /dev/null @@ -1,29 +0,0 @@ ---- -title: Oracles -description: Dive into how Noir supports Oracles via RPC calls, and learn how to declare an Oracle in Noir with our comprehensive guide. -keywords: - - Noir - - Oracles - - RPC Calls - - Unconstrained Functions - - Programming - - Blockchain -sidebar_position: 6 ---- - -import Experimental from '@site/src/components/Notes/_experimental.mdx'; - - - -Noir has support for Oracles via RPC calls. This means Noir will make an RPC call and use the return value for proof generation. - -Since Oracles are not resolved by Noir, they are [`unconstrained` functions](./unconstrained.md) - -You can declare an Oracle through the `#[oracle()]` flag. Example: - -```rust -#[oracle(get_number_sequence)] -unconstrained fn get_number_sequence(_size: Field) -> [Field] {} -``` - -The timeout for when using an external RPC oracle resolver can be set with the `NARGO_FOREIGN_CALL_TIMEOUT` environment variable. This timeout is in units of milliseconds. diff --git a/docs/versioned_docs/version-v0.33.0/noir/concepts/shadowing.md b/docs/versioned_docs/version-v0.33.0/noir/concepts/shadowing.md deleted file mode 100644 index 5ce6130d201..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/concepts/shadowing.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -title: Shadowing -sidebar_position: 12 ---- - -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.33.0/noir/concepts/traits.md b/docs/versioned_docs/version-v0.33.0/noir/concepts/traits.md deleted file mode 100644 index 51305b38c16..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/concepts/traits.md +++ /dev/null @@ -1,405 +0,0 @@ ---- -title: Traits -description: - Traits in Noir can be used to abstract out a common interface for functions across - several data types. -keywords: [noir programming language, traits, interfaces, generic, protocol] -sidebar_position: 14 ---- - -## Overview - -Traits in Noir are a useful abstraction similar to interfaces or protocols in other languages. Each trait defines -the interface of several methods contained within the trait. Types can then implement this trait by providing -implementations for these methods. For example in the program: - -```rust -struct Rectangle { - width: Field, - height: Field, -} - -impl Rectangle { - fn area(self) -> Field { - self.width * self.height - } -} - -fn log_area(r: Rectangle) { - println(r.area()); -} -``` - -We have a function `log_area` to log the area of a `Rectangle`. Now how should we change the program if we want this -function to work on `Triangle`s as well?: - -```rust -struct Triangle { - width: Field, - height: Field, -} - -impl Triangle { - fn area(self) -> Field { - self.width * self.height / 2 - } -} -``` - -Making `log_area` generic over all types `T` would be invalid since not all types have an `area` method. Instead, we can -introduce a new `Area` trait and make `log_area` generic over all types `T` that implement `Area`: - -```rust -trait Area { - fn area(self) -> Field; -} - -fn log_area(shape: T) where T: Area { - println(shape.area()); -} -``` - -We also need to explicitly implement `Area` for `Rectangle` and `Triangle`. We can do that by changing their existing -impls slightly. Note that the parameter types and return type of each of our `area` methods must match those defined -by the `Area` trait. - -```rust -impl Area for Rectangle { - fn area(self) -> Field { - self.width * self.height - } -} - -impl Area for Triangle { - fn area(self) -> Field { - self.width * self.height / 2 - } -} -``` - -Now we have a working program that is generic over any type of Shape that is used! Others can even use this program -as a library with their own types - such as `Circle` - as long as they also implement `Area` for these types. - -## Where Clauses - -As seen in `log_area` above, when we want to create a function or method that is generic over any type that implements -a trait, we can add a where clause to the generic function. - -```rust -fn log_area(shape: T) where T: Area { - println(shape.area()); -} -``` - -It is also possible to apply multiple trait constraints on the same variable at once by combining traits with the `+` -operator. Similarly, we can have multiple trait constraints by separating each with a comma: - -```rust -fn foo(elements: [T], thing: U) where - T: Default + Add + Eq, - U: Bar, -{ - let mut sum = T::default(); - - for element in elements { - sum += element; - } - - if sum == T::default() { - thing.bar(); - } -} -``` - -## Generic Implementations - -You can add generics to a trait implementation by adding the generic list after the `impl` keyword: - -```rust -trait Second { - fn second(self) -> Field; -} - -impl Second for (T, Field) { - fn second(self) -> Field { - self.1 - } -} -``` - -You can also implement a trait for every type this way: - -```rust -trait Debug { - fn debug(self); -} - -impl Debug for T { - fn debug(self) { - println(self); - } -} - -fn main() { - 1.debug(); -} -``` - -### Generic Trait Implementations With Where Clauses - -Where clauses can be placed on trait implementations themselves to restrict generics in a similar way. -For example, while `impl Foo for T` implements the trait `Foo` for every type, `impl Foo for T where T: Bar` -will implement `Foo` only for types that also implement `Bar`. This is often used for implementing generic types. -For example, here is the implementation for array equality: - -```rust -impl Eq for [T; N] where T: Eq { - // Test if two arrays have the same elements. - // Because both arrays must have length N, we know their lengths already match. - fn eq(self, other: Self) -> bool { - let mut result = true; - - for i in 0 .. self.len() { - // The T: Eq constraint is needed to call == on the array elements here - result &= self[i] == other[i]; - } - - result - } -} -``` - -Where clauses can also be placed on struct implementations. -For example, here is a method utilizing a generic type that implements the equality trait. - -```rust -struct Foo { - a: u32, - b: T, -} - -impl Foo where T: Eq { - fn eq(self, other: Self) -> bool { - (self.a == other.a) & self.b.eq(other.b) - } -} -``` - -## Generic Traits - -Traits themselves can also be generic by placing the generic arguments after the trait name. These generics are in -scope of every item within the trait. - -```rust -trait Into { - // Convert `self` to type `T` - fn into(self) -> T; -} -``` - -When implementing generic traits the generic arguments of the trait must be specified. This is also true anytime -when referencing a generic trait (e.g. in a `where` clause). - -```rust -struct MyStruct { - array: [Field; 2], -} - -impl Into<[Field; 2]> for MyStruct { - fn into(self) -> [Field; 2] { - self.array - } -} - -fn as_array(x: T) -> [Field; 2] - where T: Into<[Field; 2]> -{ - x.into() -} - -fn main() { - let array = [1, 2]; - let my_struct = MyStruct { array }; - - assert_eq(as_array(my_struct), array); -} -``` - -## Trait Methods With No `self` - -A trait can contain any number of methods, each of which have access to the `Self` type which represents each type -that eventually implements the trait. Similarly, the `self` variable is available as well but is not required to be used. -For example, we can define a trait to create a default value for a type. This trait will need to return the `Self` type -but doesn't need to take any parameters: - -```rust -trait Default { - fn default() -> Self; -} -``` - -Implementing this trait can be done similarly to any other trait: - -```rust -impl Default for Field { - fn default() -> Field { - 0 - } -} - -struct MyType {} - -impl Default for MyType { - fn default() -> Field { - MyType {} - } -} -``` - -However, since there is no `self` parameter, we cannot call it via the method call syntax `object.method()`. -Instead, we'll need to refer to the function directly. This can be done either by referring to the -specific impl `MyType::default()` or referring to the trait itself `Default::default()`. In the later -case, type inference determines the impl that is selected. - -```rust -let my_struct = MyStruct::default(); - -let x: Field = Default::default(); -let result = x + Default::default(); -``` - -:::warning - -```rust -let _ = Default::default(); -``` - -If type inference cannot select which impl to use because of an ambiguous `Self` type, an impl will be -arbitrarily selected. This occurs most often when the result of a trait function call with no parameters -is unused. To avoid this, when calling a trait function with no `self` or `Self` parameters or return type, -always refer to it via the implementation type's namespace - e.g. `MyType::default()`. -This is set to change to an error in future Noir versions. - -::: - -## Default Method Implementations - -A trait can also have default implementations of its methods by giving a body to the desired functions. -Note that this body must be valid for all types that may implement the trait. As a result, the only -valid operations on `self` will be operations valid for any type or other operations on the trait itself. - -```rust -trait Numeric { - fn add(self, other: Self) -> Self; - - // Default implementation of double is (self + self) - fn double(self) -> Self { - self.add(self) - } -} -``` - -When implementing a trait with default functions, a type may choose to implement only the required functions: - -```rust -impl Numeric for Field { - fn add(self, other: Field) -> Field { - self + other - } -} -``` - -Or it may implement the optional methods as well: - -```rust -impl Numeric for u32 { - fn add(self, other: u32) -> u32 { - self + other - } - - fn double(self) -> u32 { - self * 2 - } -} -``` - -## Impl Specialization - -When implementing traits for a generic type it is possible to implement the trait for only a certain combination -of generics. This can be either as an optimization or because those specific generics are required to implement the trait. - -```rust -trait Sub { - fn sub(self, other: Self) -> Self; -} - -struct NonZero { - value: T, -} - -impl Sub for NonZero { - fn sub(self, other: Self) -> Self { - let value = self.value - other.value; - assert(value != 0); - NonZero { value } - } -} -``` - -## Overlapping Implementations - -Overlapping implementations are disallowed by Noir to ensure Noir's decision on which impl to select is never ambiguous. -This means if a trait `Foo` is already implemented -by a type `Bar` for all `T`, then we cannot also have a separate impl for `Bar` (or any other -type argument). Similarly, if there is an impl for all `T` such as `impl Debug for T`, we cannot create -any more impls to `Debug` for other types since it would be ambiguous which impl to choose for any given -method call. - -```rust -trait Trait {} - -// Previous impl defined here -impl Trait for (A, B) {} - -// error: Impl for type `(Field, Field)` overlaps with existing impl -impl Trait for (Field, Field) {} -``` - -## Trait Coherence - -Another restriction on trait implementations is coherence. This restriction ensures other crates cannot create -impls that may overlap with other impls, even if several unrelated crates are used as dependencies in the same -program. - -The coherence restriction is: to implement a trait, either the trait itself or the object type must be declared -in the crate the impl is in. - -In practice this often comes up when using types provided by libraries. If a library provides a type `Foo` that does -not implement a trait in the standard library such as `Default`, you may not `impl Default for Foo` in your own crate. -While restrictive, this prevents later issues or silent changes in the program if the `Foo` library later added its -own impl for `Default`. If you are a user of the `Foo` library in this scenario and need a trait not implemented by the -library your choices are to either submit a patch to the library or use the newtype pattern. - -### The Newtype Pattern - -The newtype pattern gets around the coherence restriction by creating a new wrapper type around the library type -that we cannot create `impl`s for. Since the new wrapper type is defined in our current crate, we can create -impls for any trait we need on it. - -```rust -struct Wrapper { - foo: some_library::Foo, -} - -impl Default for Wrapper { - fn default() -> Wrapper { - Wrapper { - foo: some_library::Foo::new(), - } - } -} -``` - -Since we have an impl for our own type, the behavior of this code will not change even if `some_library` is updated -to provide its own `impl Default for Foo`. The downside of this pattern is that it requires extra wrapping and -unwrapping of values when converting to and from the `Wrapper` and `Foo` types. diff --git a/docs/versioned_docs/version-v0.33.0/noir/concepts/unconstrained.md b/docs/versioned_docs/version-v0.33.0/noir/concepts/unconstrained.md deleted file mode 100644 index 96f824c5e42..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/concepts/unconstrained.md +++ /dev/null @@ -1,99 +0,0 @@ ---- -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] -sidebar_position: 5 ---- - -Unconstrained functions are functions which do not constrain any of the included computation and allow for non-deterministic 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 AND 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. - -## Break and Continue - -In addition to loops over runtime bounds, `break` and `continue` are also available in unconstrained code. See [break and continue](../concepts/control_flow.md#break-and-continue) diff --git a/docs/versioned_docs/version-v0.33.0/noir/modules_packages_crates/_category_.json b/docs/versioned_docs/version-v0.33.0/noir/modules_packages_crates/_category_.json deleted file mode 100644 index 1debcfe7675..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/modules_packages_crates/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "Modules, Packages and Crates", - "position": 2, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.33.0/noir/modules_packages_crates/crates_and_packages.md b/docs/versioned_docs/version-v0.33.0/noir/modules_packages_crates/crates_and_packages.md deleted file mode 100644 index 95ee9f52ab2..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/modules_packages_crates/crates_and_packages.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -title: Crates and Packages -description: Learn how to use Crates and Packages in your Noir project -keywords: [Nargo, dependencies, package management, crates, package] -sidebar_position: 0 ---- - -## 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/noir-projects/noir-contracts/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.33.0/noir/modules_packages_crates/dependencies.md b/docs/versioned_docs/version-v0.33.0/noir/modules_packages_crates/dependencies.md deleted file mode 100644 index 24e02de08fe..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/modules_packages_crates/dependencies.md +++ /dev/null @@ -1,124 +0,0 @@ ---- -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] -sidebar_position: 1 ---- - -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 = "noir-contracts/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 -└── lib_a - ├── Nargo.toml - └── src - └── lib.nr -``` - -Inside of the binary crate, you can specify: - -```toml -# Nargo.toml - -[dependencies] -lib_a = { path = "../lib_a" } -``` - -## 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 lib_a referenced above: - -```rust -use ecrecover; -use lib_a; -``` - -You can also import only the specific parts of dependency that you want to use, like so: - -```rust -use std::hash::sha256; -use std::scalar_mul::fixed_base_embedded_curve; -``` - -Lastly, as demonstrated in the -[elliptic curve example](../standard_library/cryptographic_primitives/ec_primitives.md#examples), you -can import multiple items in the same line by enclosing them in curly braces: - -```rust -use std::ec::tecurve::affine::{Curve, Point}; -``` - -We don't have a way to consume libraries from inside a [workspace](./workspaces.md) 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 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.33.0/noir/modules_packages_crates/modules.md b/docs/versioned_docs/version-v0.33.0/noir/modules_packages_crates/modules.md deleted file mode 100644 index 16b6307d2fd..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/modules_packages_crates/modules.md +++ /dev/null @@ -1,185 +0,0 @@ ---- -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] -sidebar_position: 2 ---- - -Noir's module system follows the same convention as the _newer_ version of Rust's module system. - -## Purpose of Modules - -Modules are used to organize 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 - -``` - -The module filename may also be the name of the module as a directory with the contents in a -file named `mod.nr` within that directory. The above example can alternatively be expressed like this: - -Filename : `src/main.nr` - -```rust -mod foo; - -fn main() { - foo::hello_world(); -} -``` - -Filename : `src/foo/mod.nr` - -```rust -fn from_foo() {} -``` - -Note that it's an error to have both files `src/foo.nr` and `src/foo/mod.nr` in the filesystem. - -### 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 -``` - -Similar to importing a module in the crate root, modules can be placed in a `mod.nr` file, like this: - -Filename : `src/main.nr` - -```rust -mod foo; - -fn main() { - foo::from_foo(); -} -``` - -Filename : `src/foo/mod.nr` - -```rust -mod bar; -fn from_foo() {} -``` - -Filename : `src/foo/bar/mod.nr` - -```rust -fn from_bar() {} -``` - -### Referencing a parent module - -Given a submodule, you can refer to its parent module using the `super` keyword. - -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 -// Same as bar::from_foo -use super::from_foo; - -fn from_bar() { - from_foo(); // invokes super::from_foo(), which is bar::from_foo() - super::from_foo(); // also invokes bar::from_foo() -} -``` \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.33.0/noir/modules_packages_crates/workspaces.md b/docs/versioned_docs/version-v0.33.0/noir/modules_packages_crates/workspaces.md deleted file mode 100644 index 513497f12bf..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/modules_packages_crates/workspaces.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -title: Workspaces -sidebar_position: 3 ---- - -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 -│ │ └── Prover.toml -│ │ └── src -│ │ └── main.nr -│ └── b -│ ├── Nargo.toml -│ └── Prover.toml -│ └── src -│ └── main.nr -│ -└── Nargo.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.33.0/noir/standard_library/_category_.json b/docs/versioned_docs/version-v0.33.0/noir/standard_library/_category_.json deleted file mode 100644 index af04c0933fd..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/standard_library/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "Standard Library", - "position": 1, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.33.0/noir/standard_library/bigint.md b/docs/versioned_docs/version-v0.33.0/noir/standard_library/bigint.md deleted file mode 100644 index 2bfdeec6631..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/standard_library/bigint.md +++ /dev/null @@ -1,122 +0,0 @@ ---- -title: Big Integers -description: How to use big integers from Noir standard library -keywords: - [ - Big Integer, - Noir programming language, - Noir libraries, - ] ---- - -The BigInt module in the standard library exposes some class of integers which do not fit (well) into a Noir native field. It implements modulo arithmetic, modulo a 'big' prime number. - -:::note - -The module can currently be considered as `Field`s with fixed modulo sizes used by a set of elliptic curves, in addition to just the native curve. [More work](https://github.com/noir-lang/noir/issues/510) is needed to achieve arbitrarily sized big integers. - -::: - -Currently 6 classes of integers (i.e 'big' prime numbers) are available in the module, namely: - -- BN254 Fq: Bn254Fq -- BN254 Fr: Bn254Fr -- Secp256k1 Fq: Secpk1Fq -- Secp256k1 Fr: Secpk1Fr -- Secp256r1 Fr: Secpr1Fr -- Secp256r1 Fq: Secpr1Fq - -Where XXX Fq and XXX Fr denote respectively the order of the base and scalar field of the (usual) elliptic curve XXX. -For instance the big integer 'Secpk1Fq' in the standard library refers to integers modulo $2^{256}-2^{32}-977$. - -Feel free to explore the source code for the other primes: - -```rust title="big_int_definition" showLineNumbers -struct BigInt { - pointer: u32, - modulus: u32, -} -``` -> Source code: noir_stdlib/src/bigint.nr#L14-L19 - - -## Example usage - -A common use-case is when constructing a big integer from its bytes representation, and performing arithmetic operations on it: - -```rust title="big_int_example" showLineNumbers -fn big_int_example(x: u8, y: u8) { - let a = Secpk1Fq::from_le_bytes(&[x, y, 0, 45, 2]); - let b = Secpk1Fq::from_le_bytes(&[y, x, 9]); - let c = (a + b) * b / a; - let d = c.to_le_bytes(); - println(d[0]); -} -``` -> Source code: test_programs/execution_success/bigint/src/main.nr#L70-L78 - - -## Methods - -The available operations for each big integer are: - -### from_le_bytes - -Construct a big integer from its little-endian bytes representation. Example: - -```rust - // Construct a big integer from a slice of bytes - let a = Secpk1Fq::from_le_bytes(&[x, y, 0, 45, 2]); - // Construct a big integer from an array of 32 bytes - let a = Secpk1Fq::from_le_bytes_32([1;32]); - ``` - -Sure, here's the formatted version of the remaining methods: - -### to_le_bytes - -Return the little-endian bytes representation of a big integer. Example: - -```rust -let bytes = a.to_le_bytes(); -``` - -### add - -Add two big integers. Example: - -```rust -let sum = a + b; -``` - -### sub - -Subtract two big integers. Example: - -```rust -let difference = a - b; -``` - -### mul - -Multiply two big integers. Example: - -```rust -let product = a * b; -``` - -### div - -Divide two big integers. Note that division is field division and not euclidean division. Example: - -```rust -let quotient = a / b; -``` - -### eq - -Compare two big integers. Example: - -```rust -let are_equal = a == b; -``` diff --git a/docs/versioned_docs/version-v0.33.0/noir/standard_library/black_box_fns.md b/docs/versioned_docs/version-v0.33.0/noir/standard_library/black_box_fns.md deleted file mode 100644 index d6079ab182c..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/standard_library/black_box_fns.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -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. - -The ACVM spec defines a set of blackbox functions which backends will be expected to implement. This allows backends to use optimized implementations of these constraints if they have them, however they may also fallback to less efficient naive implementations if not. - -## Function list - -Here is a list of the current black box functions: - -- [AES128](./cryptographic_primitives/ciphers.mdx#aes128) -- [SHA256](./cryptographic_primitives/hashes.mdx#sha256) -- [Schnorr signature verification](./cryptographic_primitives/schnorr.mdx) -- [Blake2s](./cryptographic_primitives/hashes.mdx#blake2s) -- [Blake3](./cryptographic_primitives/hashes.mdx#blake3) -- [Pedersen Hash](./cryptographic_primitives/hashes.mdx#pedersen_hash) -- [Pedersen Commitment](./cryptographic_primitives/hashes.mdx#pedersen_commitment) -- [ECDSA signature verification](./cryptographic_primitives/ecdsa_sig_verification.mdx) -- [Embedded curve operations (MSM, addition, ...)](./cryptographic_primitives/embedded_curve_ops.mdx) -- AND -- XOR -- RANGE -- [Keccak256](./cryptographic_primitives/hashes.mdx#keccak256) -- [Recursive proof verification](./recursion.mdx) - -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. - -You can view the black box functions defined in the ACVM code [here](https://github.com/noir-lang/noir/blob/master/acvm-repo/acir/src/circuit/black_box_functions.rs). diff --git a/docs/versioned_docs/version-v0.33.0/noir/standard_library/bn254.md b/docs/versioned_docs/version-v0.33.0/noir/standard_library/bn254.md deleted file mode 100644 index 3294f005dbb..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/standard_library/bn254.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -title: Bn254 Field Library ---- - -Noir provides a module in standard library with some optimized functions for bn254 Fr in `std::field::bn254`. - -## decompose - -```rust -fn decompose(x: Field) -> (Field, Field) {} -``` - -Decomposes a single field into two fields, low and high. The low field contains the lower 16 bytes of the input field and the high field contains the upper 16 bytes of the input field. Both field results are range checked to 128 bits. - - -## assert_gt - -```rust -fn assert_gt(a: Field, b: Field) {} -``` - -Asserts that a > b. This will generate less constraints than using `assert(gt(a, b))`. - -## assert_lt - -```rust -fn assert_lt(a: Field, b: Field) {} -``` - -Asserts that a < b. This will generate less constraints than using `assert(lt(a, b))`. - -## gt - -```rust -fn gt(a: Field, b: Field) -> bool {} -``` - -Returns true if a > b. - -## lt - -```rust -fn lt(a: Field, b: Field) -> bool {} -``` - -Returns true if a < b. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.33.0/noir/standard_library/containers/boundedvec.md b/docs/versioned_docs/version-v0.33.0/noir/standard_library/containers/boundedvec.md deleted file mode 100644 index 604d84d5ba4..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/standard_library/containers/boundedvec.md +++ /dev/null @@ -1,419 +0,0 @@ ---- -title: Bounded Vectors -keywords: [noir, vector, bounded vector, slice] -sidebar_position: 1 ---- - -A `BoundedVec` is a growable storage similar to a `Vec` except that it -is bounded with a maximum possible length. Unlike `Vec`, `BoundedVec` is not implemented -via slices and thus is not subject to the same restrictions slices are (notably, nested -slices - and thus nested vectors as well - are disallowed). - -Since a BoundedVec is backed by a normal array under the hood, growing the BoundedVec by -pushing an additional element is also more efficient - the length only needs to be increased -by one. - -For these reasons `BoundedVec` should generally be preferred over `Vec` when there -is a reasonable maximum bound that can be placed on the vector. - -Example: - -```rust -let mut vector: BoundedVec = BoundedVec::new(); -for i in 0..5 { - vector.push(i); -} -assert(vector.len() == 5); -assert(vector.max_len() == 10); -``` - -## Methods - -### new - -```rust -pub fn new() -> Self -``` - -Creates a new, empty vector of length zero. - -Since this container is backed by an array internally, it still needs an initial value -to give each element. To resolve this, each element is zeroed internally. This value -is guaranteed to be inaccessible unless `get_unchecked` is used. - -Example: - -```rust -let empty_vector: BoundedVec = BoundedVec::new(); -assert(empty_vector.len() == 0); -``` - -Note that whenever calling `new` the maximum length of the vector should always be specified -via a type signature: - -```rust title="new_example" showLineNumbers -fn foo() -> BoundedVec { - // Ok! MaxLen is specified with a type annotation - let v1: BoundedVec = BoundedVec::new(); - let v2 = BoundedVec::new(); - - // Ok! MaxLen is known from the type of foo's return value - v2 -} - -fn bad() { - let mut v3 = BoundedVec::new(); - - // Not Ok! We don't know if v3's MaxLen is at least 1, and the compiler often infers 0 by default. - v3.push(5); -} -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L11-L27 - - -This defaulting of `MaxLen` (and numeric generics in general) to zero may change in future noir versions -but for now make sure to use type annotations when using bounded vectors. Otherwise, you will receive a constraint failure at runtime when the vec is pushed to. - -### get - -```rust -pub fn get(self, index: u64) -> T { -``` - -Retrieves an element from the vector at the given index, starting from zero. - -If the given index is equal to or greater than the length of the vector, this -will issue a constraint failure. - -Example: - -```rust -fn foo(v: BoundedVec) { - let first = v.get(0); - let last = v.get(v.len() - 1); - assert(first != last); -} -``` - -### get_unchecked - -```rust -pub fn get_unchecked(self, index: u64) -> T { -``` - -Retrieves an element from the vector at the given index, starting from zero, without -performing a bounds check. - -Since this function does not perform a bounds check on length before accessing the element, -it is unsafe! Use at your own risk! - -Example: - -```rust title="get_unchecked_example" showLineNumbers -fn sum_of_first_three(v: BoundedVec) -> u32 { - // Always ensure the length is larger than the largest - // index passed to get_unchecked - assert(v.len() > 2); - let first = v.get_unchecked(0); - let second = v.get_unchecked(1); - let third = v.get_unchecked(2); - first + second + third -} -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L54-L64 - - -### set - -```rust -pub fn set(&mut self: Self, index: u64, value: T) { -``` - -Writes an element to the vector at the given index, starting from zero. - -If the given index is equal to or greater than the length of the vector, this will issue a constraint failure. - -Example: - -```rust -fn foo(v: BoundedVec) { - let first = v.get(0); - assert(first != 42); - v.set(0, 42); - let new_first = v.get(0); - assert(new_first == 42); -} -``` - -### set_unchecked - -```rust -pub fn set_unchecked(&mut self: Self, index: u64, value: T) -> T { -``` - -Writes an element to the vector at the given index, starting from zero, without performing a bounds check. - -Since this function does not perform a bounds check on length before accessing the element, it is unsafe! Use at your own risk! - -Example: - -```rust title="set_unchecked_example" showLineNumbers -fn set_unchecked_example() { - let mut vec: BoundedVec = BoundedVec::new(); - vec.extend_from_array([1, 2]); - - // Here we're safely writing within the valid range of `vec` - // `vec` now has the value [42, 2] - vec.set_unchecked(0, 42); - - // We can then safely read this value back out of `vec`. - // Notice that we use the checked version of `get` which would prevent reading unsafe values. - assert_eq(vec.get(0), 42); - - // We've now written past the end of `vec`. - // As this index is still within the maximum potential length of `v`, - // it won't cause a constraint failure. - vec.set_unchecked(2, 42); - println(vec); - - // This will write past the end of the maximum potential length of `vec`, - // it will then trigger a constraint failure. - vec.set_unchecked(5, 42); - println(vec); -} -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L67-L91 - - - -### push - -```rust -pub fn push(&mut self, elem: T) { -``` - -Pushes an element to the end of the vector. This increases the length -of the vector by one. - -Panics if the new length of the vector will be greater than the max length. - -Example: - -```rust title="bounded-vec-push-example" showLineNumbers -let mut v: BoundedVec = BoundedVec::new(); - - v.push(1); - v.push(2); - - // Panics with failed assertion "push out of bounds" - v.push(3); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L95-L103 - - -### pop - -```rust -pub fn pop(&mut self) -> T -``` - -Pops the element at the end of the vector. This will decrease the length -of the vector by one. - -Panics if the vector is empty. - -Example: - -```rust title="bounded-vec-pop-example" showLineNumbers -let mut v: BoundedVec = BoundedVec::new(); - v.push(1); - v.push(2); - - let two = v.pop(); - let one = v.pop(); - - assert(two == 2); - assert(one == 1); - // error: cannot pop from an empty vector - // let _ = v.pop(); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L108-L120 - - -### len - -```rust -pub fn len(self) -> u64 { -``` - -Returns the current length of this vector - -Example: - -```rust title="bounded-vec-len-example" showLineNumbers -let mut v: BoundedVec = BoundedVec::new(); - assert(v.len() == 0); - - v.push(100); - assert(v.len() == 1); - - v.push(200); - v.push(300); - v.push(400); - assert(v.len() == 4); - - let _ = v.pop(); - let _ = v.pop(); - assert(v.len() == 2); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L125-L140 - - -### max_len - -```rust -pub fn max_len(_self: BoundedVec) -> u64 { -``` - -Returns the maximum length of this vector. This is always -equal to the `MaxLen` parameter this vector was initialized with. - -Example: - -```rust title="bounded-vec-max-len-example" showLineNumbers -let mut v: BoundedVec = BoundedVec::new(); - - assert(v.max_len() == 5); - v.push(10); - assert(v.max_len() == 5); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L145-L151 - - -### storage - -```rust -pub fn storage(self) -> [T; MaxLen] { -``` - -Returns the internal array within this vector. -Since arrays in Noir are immutable, mutating the returned storage array will not mutate -the storage held internally by this vector. - -Note that uninitialized elements may be zeroed out! - -Example: - -```rust title="bounded-vec-storage-example" showLineNumbers -let mut v: BoundedVec = BoundedVec::new(); - - assert(v.storage() == [0, 0, 0, 0, 0]); - - v.push(57); - assert(v.storage() == [57, 0, 0, 0, 0]); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L156-L163 - - -### extend_from_array - -```rust -pub fn extend_from_array(&mut self, array: [T; Len]) -``` - -Pushes each element from the given array to this vector. - -Panics if pushing each element would cause the length of this vector -to exceed the maximum length. - -Example: - -```rust title="bounded-vec-extend-from-array-example" showLineNumbers -let mut vec: BoundedVec = BoundedVec::new(); - vec.extend_from_array([2, 4]); - - assert(vec.len == 2); - assert(vec.get(0) == 2); - assert(vec.get(1) == 4); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L168-L175 - - -### extend_from_bounded_vec - -```rust -pub fn extend_from_bounded_vec(&mut self, vec: BoundedVec) -``` - -Pushes each element from the other vector to this vector. The length of -the other vector is left unchanged. - -Panics if pushing each element would cause the length of this vector -to exceed the maximum length. - -Example: - -```rust title="bounded-vec-extend-from-bounded-vec-example" showLineNumbers -let mut v1: BoundedVec = BoundedVec::new(); - let mut v2: BoundedVec = BoundedVec::new(); - - v2.extend_from_array([1, 2, 3]); - v1.extend_from_bounded_vec(v2); - - assert(v1.storage() == [1, 2, 3, 0, 0]); - assert(v2.storage() == [1, 2, 3, 0, 0, 0, 0]); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L180-L189 - - -### from_array - -```rust -pub fn from_array(array: [T; Len]) -> Self -``` - -Creates a new vector, populating it with values derived from an array input. -The maximum length of the vector is determined based on the type signature. - -Example: -```rust -let bounded_vec: BoundedVec = BoundedVec::from_array([1, 2, 3]) -``` - -### map - -```rust -pub fn map(self, f: fn[Env](T) -> U) -> BoundedVec -``` - -Creates a new vector of equal size by calling a closure on each element in this vector. - -Example: - -```rust title="bounded-vec-map-example" showLineNumbers -let vec: BoundedVec = BoundedVec::from_array([1, 2, 3, 4]); - let result = vec.map(|value| value * 2); -``` -> Source code: noir_stdlib/src/collections/bounded_vec.nr#L205-L208 - - -### any - -```rust -pub fn any(self, predicate: fn[Env](T) -> bool) -> bool -``` - -Returns true if the given predicate returns true for any element -in this vector. - -Example: - -```rust title="bounded-vec-any-example" showLineNumbers -let mut v: BoundedVec = BoundedVec::new(); - v.extend_from_array([2, 4, 6]); - - let all_even = !v.any(|elem: u32| elem % 2 != 0); - assert(all_even); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L256-L262 - diff --git a/docs/versioned_docs/version-v0.33.0/noir/standard_library/containers/hashmap.md b/docs/versioned_docs/version-v0.33.0/noir/standard_library/containers/hashmap.md deleted file mode 100644 index 8c50c7e774c..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/standard_library/containers/hashmap.md +++ /dev/null @@ -1,570 +0,0 @@ ---- -title: HashMap -keywords: [noir, map, hash, hashmap] -sidebar_position: 1 ---- - -`HashMap` is used to efficiently store and look up key-value pairs. - -`HashMap` is a bounded type which can store anywhere from zero to `MaxLen` total elements. -Note that due to hash collisions, the actual maximum number of elements stored by any particular -hashmap is likely lower than `MaxLen`. This is true even with cryptographic hash functions since -every hash value will be performed modulo `MaxLen`. - -When creating `HashMap`s, the `MaxLen` generic should always be specified if it is not already -known. Otherwise, the compiler may infer a different value for `MaxLen` (such as zero), which -will likely change the result of the program. This behavior is set to become an error in future -versions instead. - -Example: - -```rust -// Create a mapping from Fields to u32s with a maximum length of 12 -// using a poseidon2 hasher -use std::hash::poseidon2::Poseidon2Hasher; -let mut map: HashMap> = HashMap::default(); - -map.insert(1, 2); -map.insert(3, 4); - -let two = map.get(1).unwrap(); -``` - -## Methods - -### default - -```rust title="default" showLineNumbers -impl Default for HashMap -where - B: BuildHasher + Default, - H: Hasher + Default -{ - fn default() -> Self { -``` -> Source code: noir_stdlib/src/collections/map.nr#L462-L469 - - -Creates a fresh, empty HashMap. - -When using this function, always make sure to specify the maximum size of the hash map. - -This is the same `default` from the `Default` implementation given further below. It is -repeated here for convenience since it is the recommended way to create a hashmap. - -Example: - -```rust title="default_example" showLineNumbers -let hashmap: HashMap> = HashMap::default(); - assert(hashmap.is_empty()); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L201-L204 - - -Because `HashMap` has so many generic arguments that are likely to be the same throughout -your program, it may be helpful to create a type alias: - -```rust title="type_alias" showLineNumbers -type MyMap = HashMap>; -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L195-L197 - - -### with_hasher - -```rust title="with_hasher" showLineNumbers -pub fn with_hasher(_build_hasher: B) -> Self - where - B: BuildHasher { -``` -> Source code: noir_stdlib/src/collections/map.nr#L82-L86 - - -Creates a hashmap with an existing `BuildHasher`. This can be used to ensure multiple -hashmaps are created with the same hasher instance. - -Example: - -```rust title="with_hasher_example" showLineNumbers -let my_hasher: BuildHasherDefault = Default::default(); - let hashmap: HashMap> = HashMap::with_hasher(my_hasher); - assert(hashmap.is_empty()); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L206-L210 - - -### get - -```rust title="get" showLineNumbers -pub fn get( - self, - key: K - ) -> Option - where - K: Eq + Hash, - B: BuildHasher, - H: Hasher { -``` -> Source code: noir_stdlib/src/collections/map.nr#L278-L287 - - -Retrieves a value from the hashmap, returning `Option::none()` if it was not found. - -Example: - -```rust title="get_example" showLineNumbers -fn get_example(map: HashMap>) { - let x = map.get(12); - - if x.is_some() { - assert(x.unwrap() == 42); - } -} -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L298-L306 - - -### insert - -```rust title="insert" showLineNumbers -pub fn insert( - &mut self, - key: K, - value: V - ) - where - K: Eq + Hash, - B: BuildHasher, - H: Hasher { -``` -> Source code: noir_stdlib/src/collections/map.nr#L313-L323 - - -Inserts a new key-value pair into the map. If the key was already in the map, its -previous value will be overridden with the newly provided one. - -Example: - -```rust title="insert_example" showLineNumbers -let mut map: HashMap> = HashMap::default(); - map.insert(12, 42); - assert(map.len() == 1); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L212-L216 - - -### remove - -```rust title="remove" showLineNumbers -pub fn remove( - &mut self, - key: K - ) - where - K: Eq + Hash, - B: BuildHasher, - H: Hasher { -``` -> Source code: noir_stdlib/src/collections/map.nr#L356-L365 - - -Removes the given key-value pair from the map. If the key was not already present -in the map, this does nothing. - -Example: - -```rust title="remove_example" showLineNumbers -map.remove(12); - assert(map.is_empty()); - - // If a key was not present in the map, remove does nothing - map.remove(12); - assert(map.is_empty()); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L220-L227 - - -### is_empty - -```rust title="is_empty" showLineNumbers -pub fn is_empty(self) -> bool { -``` -> Source code: noir_stdlib/src/collections/map.nr#L115-L117 - - -True if the length of the hash map is empty. - -Example: - -```rust title="is_empty_example" showLineNumbers -assert(map.is_empty()); - - map.insert(1, 2); - assert(!map.is_empty()); - - map.remove(1); - assert(map.is_empty()); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L229-L237 - - -### len - -```rust title="len" showLineNumbers -pub fn len(self) -> u32 { -``` -> Source code: noir_stdlib/src/collections/map.nr#L264-L266 - - -Returns the current length of this hash map. - -Example: - -```rust title="len_example" showLineNumbers -// This is equivalent to checking map.is_empty() - assert(map.len() == 0); - - map.insert(1, 2); - map.insert(3, 4); - map.insert(5, 6); - assert(map.len() == 3); - - // 3 was already present as a key in the hash map, so the length is unchanged - map.insert(3, 7); - assert(map.len() == 3); - - map.remove(1); - assert(map.len() == 2); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L239-L254 - - -### capacity - -```rust title="capacity" showLineNumbers -pub fn capacity(_self: Self) -> u32 { -``` -> Source code: noir_stdlib/src/collections/map.nr#L271-L273 - - -Returns the maximum capacity of this hashmap. This is always equal to the capacity -specified in the hashmap's type. - -Unlike hashmaps in general purpose programming languages, hashmaps in Noir have a -static capacity that does not increase as the map grows larger. Thus, this capacity -is also the maximum possible element count that can be inserted into the hashmap. -Due to hash collisions (modulo the hashmap length), it is likely the actual maximum -element count will be lower than the full capacity. - -Example: - -```rust title="capacity_example" showLineNumbers -let empty_map: HashMap> = HashMap::default(); - assert(empty_map.len() == 0); - assert(empty_map.capacity() == 42); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L256-L260 - - -### clear - -```rust title="clear" showLineNumbers -pub fn clear(&mut self) { -``` -> Source code: noir_stdlib/src/collections/map.nr#L93-L95 - - -Clears the hashmap, removing all key-value pairs from it. - -Example: - -```rust title="clear_example" showLineNumbers -assert(!map.is_empty()); - map.clear(); - assert(map.is_empty()); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L262-L266 - - -### contains_key - -```rust title="contains_key" showLineNumbers -pub fn contains_key( - self, - key: K - ) -> bool - where - K: Hash + Eq, - B: BuildHasher, - H: Hasher { -``` -> Source code: noir_stdlib/src/collections/map.nr#L101-L110 - - -True if the hashmap contains the given key. Unlike `get`, this will not also return -the value associated with the key. - -Example: - -```rust title="contains_key_example" showLineNumbers -if map.contains_key(7) { - let value = map.get(7); - assert(value.is_some()); - } else { - println("No value for key 7!"); - } -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L268-L275 - - -### entries - -```rust title="entries" showLineNumbers -pub fn entries(self) -> BoundedVec<(K, V), N> { -``` -> Source code: noir_stdlib/src/collections/map.nr#L123-L125 - - -Returns a vector of each key-value pair present in the hashmap. - -The length of the returned vector is always equal to the length of the hashmap. - -Example: - -```rust title="entries_example" showLineNumbers -let entries = map.entries(); - - // The length of a hashmap may not be compile-time known, so we - // need to loop over its capacity instead - for i in 0..map.capacity() { - if i < entries.len() { - let (key, value) = entries.get(i); - println(f"{key} -> {value}"); - } - } -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L309-L320 - - -### keys - -```rust title="keys" showLineNumbers -pub fn keys(self) -> BoundedVec { -``` -> Source code: noir_stdlib/src/collections/map.nr#L144-L146 - - -Returns a vector of each key present in the hashmap. - -The length of the returned vector is always equal to the length of the hashmap. - -Example: - -```rust title="keys_example" showLineNumbers -let keys = map.keys(); - - for i in 0..keys.max_len() { - if i < keys.len() { - let key = keys.get_unchecked(i); - let value = map.get(key).unwrap_unchecked(); - println(f"{key} -> {value}"); - } - } -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L322-L332 - - -### values - -```rust title="values" showLineNumbers -pub fn values(self) -> BoundedVec { -``` -> Source code: noir_stdlib/src/collections/map.nr#L164-L166 - - -Returns a vector of each value present in the hashmap. - -The length of the returned vector is always equal to the length of the hashmap. - -Example: - -```rust title="values_example" showLineNumbers -let values = map.values(); - - for i in 0..values.max_len() { - if i < values.len() { - let value = values.get_unchecked(i); - println(f"Found value {value}"); - } - } -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L334-L343 - - -### iter_mut - -```rust title="iter_mut" showLineNumbers -pub fn iter_mut( - &mut self, - f: fn(K, V) -> (K, V) - ) - where - K: Eq + Hash, - B: BuildHasher, - H: Hasher { -``` -> Source code: noir_stdlib/src/collections/map.nr#L183-L192 - - -Iterates through each key-value pair of the HashMap, setting each key-value pair to the -result returned from the given function. - -Note that since keys can be mutated, the HashMap needs to be rebuilt as it is iterated -through. If this is not desired, use `iter_values_mut` if only values need to be mutated, -or `entries` if neither keys nor values need to be mutated. - -The iteration order is left unspecified. As a result, if two keys are mutated to become -equal, which of the two values that will be present for the key in the resulting map is also unspecified. - -Example: - -```rust title="iter_mut_example" showLineNumbers -// Add 1 to each key in the map, and double the value associated with that key. - map.iter_mut(|k, v| (k + 1, v * 2)); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L347-L350 - - -### iter_keys_mut - -```rust title="iter_keys_mut" showLineNumbers -pub fn iter_keys_mut( - &mut self, - f: fn(K) -> K - ) - where - K: Eq + Hash, - B: BuildHasher, - H: Hasher { -``` -> Source code: noir_stdlib/src/collections/map.nr#L208-L217 - - -Iterates through the HashMap, mutating each key to the result returned from -the given function. - -Note that since keys can be mutated, the HashMap needs to be rebuilt as it is iterated -through. If only iteration is desired and the keys are not intended to be mutated, -prefer using `entries` instead. - -The iteration order is left unspecified. As a result, if two keys are mutated to become -equal, which of the two values that will be present for the key in the resulting map is also unspecified. - -Example: - -```rust title="iter_keys_mut_example" showLineNumbers -// Double each key, leaving the value associated with that key untouched - map.iter_keys_mut(|k| k * 2); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L352-L355 - - -### iter_values_mut - -```rust title="iter_values_mut" showLineNumbers -pub fn iter_values_mut(&mut self, f: fn(V) -> V) { -``` -> Source code: noir_stdlib/src/collections/map.nr#L233-L235 - - -Iterates through the HashMap, applying the given function to each value and mutating the -value to equal the result. This function is more efficient than `iter_mut` and `iter_keys_mut` -because the keys are untouched and the underlying hashmap thus does not need to be reordered. - -Example: - -```rust title="iter_values_mut_example" showLineNumbers -// Halve each value - map.iter_values_mut(|v| v / 2); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L357-L360 - - -### retain - -```rust title="retain" showLineNumbers -pub fn retain(&mut self, f: fn(K, V) -> bool) { -``` -> Source code: noir_stdlib/src/collections/map.nr#L247-L249 - - -Retains only the key-value pairs for which the given function returns true. -Any key-value pairs for which the function returns false will be removed from the map. - -Example: - -```rust title="retain_example" showLineNumbers -map.retain(|k, v| (k != 0) & (v != 0)); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L280-L282 - - -## Trait Implementations - -### default - -```rust title="default" showLineNumbers -impl Default for HashMap -where - B: BuildHasher + Default, - H: Hasher + Default -{ - fn default() -> Self { -``` -> Source code: noir_stdlib/src/collections/map.nr#L462-L469 - - -Constructs an empty HashMap. - -Example: - -```rust title="default_example" showLineNumbers -let hashmap: HashMap> = HashMap::default(); - assert(hashmap.is_empty()); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L201-L204 - - -### eq - -```rust title="eq" showLineNumbers -impl Eq for HashMap -where - K: Eq + Hash, - V: Eq, - B: BuildHasher, - H: Hasher -{ - fn eq(self, other: HashMap) -> bool { -``` -> Source code: noir_stdlib/src/collections/map.nr#L426-L435 - - -Checks if two HashMaps are equal. - -Example: - -```rust title="eq_example" showLineNumbers -let mut map1: HashMap> = HashMap::default(); - let mut map2: HashMap> = HashMap::default(); - - map1.insert(1, 2); - map1.insert(3, 4); - - map2.insert(3, 4); - map2.insert(1, 2); - - assert(map1 == map2); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L284-L295 - diff --git a/docs/versioned_docs/version-v0.33.0/noir/standard_library/containers/index.md b/docs/versioned_docs/version-v0.33.0/noir/standard_library/containers/index.md deleted file mode 100644 index ea84c6d5c21..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/standard_library/containers/index.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Containers -description: Container types provided by Noir's standard library for storing and retrieving data -keywords: [containers, data types, vec, hashmap] ---- diff --git a/docs/versioned_docs/version-v0.33.0/noir/standard_library/containers/vec.mdx b/docs/versioned_docs/version-v0.33.0/noir/standard_library/containers/vec.mdx deleted file mode 100644 index 475011922f8..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/standard_library/containers/vec.mdx +++ /dev/null @@ -1,170 +0,0 @@ ---- -title: Vectors -description: Delve into the Vec 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] -sidebar_position: 6 ---- - -import Experimental from '@site/src/components/Notes/_experimental.mdx'; - - - -A vector is a collection type similar to Rust's `Vec` type. In Noir, it is a convenient way to use slices as mutable arrays. - -Example: - -```rust -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 -``` - -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 -``` - -Example: - -```rust -let slice: [Field] = &[1, 2, 3]; -let vector_from_slice = Vec::from_slice(slice); -assert(vector_from_slice.len() == 3); -``` - -### len - -Returns the number of elements in the vector. - -```rust -pub fn len(self) -> Field -``` - -Example: - -```rust -let empty_vector: Vec = Vec::new(); -assert(empty_vector.len() == 0); -``` - -### 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 -``` - -Example: - -```rust -let vector: Vec = Vec::from_slice(&[10, 20, 30]); -assert(vector.get(1) == 20); -``` - -### set - -```rust -pub fn set(&mut self: Self, index: u64, value: T) { -``` - -Writes an element to the vector at the given index, starting from zero. - -Panics if the index points beyond the vector's end. - -Example: - -```rust -let vector: Vec = Vec::from_slice(&[10, 20, 30]); -assert(vector.get(1) == 20); -vector.set(1, 42); -assert(vector.get(1) == 42); -``` - -### 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) -``` - -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 -``` - -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) -``` - -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 -``` - -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); -``` diff --git a/docs/versioned_docs/version-v0.33.0/noir/standard_library/cryptographic_primitives/_category_.json b/docs/versioned_docs/version-v0.33.0/noir/standard_library/cryptographic_primitives/_category_.json deleted file mode 100644 index 5d694210bbf..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/standard_library/cryptographic_primitives/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "position": 0, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.33.0/noir/standard_library/cryptographic_primitives/ciphers.mdx b/docs/versioned_docs/version-v0.33.0/noir/standard_library/cryptographic_primitives/ciphers.mdx deleted file mode 100644 index d6a5e1a79eb..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/standard_library/cryptographic_primitives/ciphers.mdx +++ /dev/null @@ -1,32 +0,0 @@ ---- -title: Ciphers -description: - Learn about the implemented ciphers ready to use for any Noir project -keywords: - [ciphers, Noir project, aes128, encrypt] -sidebar_position: 0 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; - -## aes128 - -Given a plaintext as an array of bytes, returns the corresponding aes128 ciphertext (CBC mode). Input padding is automatically performed using PKCS#7, so that the output length is `input.len() + (16 - input.len() % 16)`. - -```rust title="aes128" showLineNumbers -pub fn aes128_encrypt(input: [u8; N], iv: [u8; 16], key: [u8; 16]) -> [u8] {} -``` -> Source code: noir_stdlib/src/aes128.nr#L2-L4 - - -```rust -fn main() { - let input: [u8; 4] = [0, 12, 3, 15] // Random bytes, will be padded to 16 bytes. - let iv: [u8; 16] = [0; 16]; // Initialisation vector - let key: [u8; 16] = [0; 16] // AES key - let ciphertext = std::aes128::aes128_encrypt(inputs.as_bytes(), iv.as_bytes(), key.as_bytes()); // In this case, the output length will be 16 bytes. -} -``` - - - \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.33.0/noir/standard_library/cryptographic_primitives/ec_primitives.md b/docs/versioned_docs/version-v0.33.0/noir/standard_library/cryptographic_primitives/ec_primitives.md deleted file mode 100644 index f262d8160d6..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/standard_library/cryptographic_primitives/ec_primitives.md +++ /dev/null @@ -1,102 +0,0 @@ ---- -title: Elliptic Curve Primitives -keywords: [cryptographic primitives, Noir project] -sidebar_position: 4 ---- - -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/mod.nr`](https://github.com/noir-lang/noir/blob/master/noir_stdlib/src/ec/mod.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/mod.nr)). - -## Examples - -The -[ec_baby_jubjub test](https://github.com/noir-lang/noir/blob/master/test_programs/compile_success_empty/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 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.33.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx b/docs/versioned_docs/version-v0.33.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx deleted file mode 100644 index 4c22e70e8de..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx +++ /dev/null @@ -1,98 +0,0 @@ ---- -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] -sidebar_position: 3 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; - -Noir supports ECDSA signatures verification over the secp256k1 and secp256r1 curves. - -## ecdsa_secp256k1::verify_signature - -Verifier for ECDSA Secp256k1 signatures. -See ecdsa_secp256k1::verify_signature_slice for a version that accepts slices directly. - -```rust title="ecdsa_secp256k1" showLineNumbers -pub fn verify_signature( - public_key_x: [u8; 32], - public_key_y: [u8; 32], - signature: [u8; 64], - message_hash: [u8; N] -) -> bool -``` -> Source code: noir_stdlib/src/ecdsa_secp256k1.nr#L2-L9 - - -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_secp256k1::verify_signature_slice - -Verifier for ECDSA Secp256k1 signatures where the message is a slice. - -```rust title="ecdsa_secp256k1_slice" showLineNumbers -pub fn verify_signature_slice( - public_key_x: [u8; 32], - public_key_y: [u8; 32], - signature: [u8; 64], - message_hash: [u8] -) -> bool -``` -> Source code: noir_stdlib/src/ecdsa_secp256k1.nr#L13-L20 - - - - -## ecdsa_secp256r1::verify_signature - -Verifier for ECDSA Secp256r1 signatures. -See ecdsa_secp256r1::verify_signature_slice for a version that accepts slices directly. - -```rust title="ecdsa_secp256r1" showLineNumbers -pub fn verify_signature( - public_key_x: [u8; 32], - public_key_y: [u8; 32], - signature: [u8; 64], - message_hash: [u8; N] -) -> bool -``` -> Source code: noir_stdlib/src/ecdsa_secp256r1.nr#L2-L9 - - -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); -} -``` - - - -## ecdsa_secp256r1::verify_signature - -Verifier for ECDSA Secp256r1 signatures where the message is a slice. - -```rust title="ecdsa_secp256r1_slice" showLineNumbers -pub fn verify_signature_slice( - public_key_x: [u8; 32], - public_key_y: [u8; 32], - signature: [u8; 64], - message_hash: [u8] -) -> bool -``` -> Source code: noir_stdlib/src/ecdsa_secp256r1.nr#L13-L20 - - - diff --git a/docs/versioned_docs/version-v0.33.0/noir/standard_library/cryptographic_primitives/eddsa.mdx b/docs/versioned_docs/version-v0.33.0/noir/standard_library/cryptographic_primitives/eddsa.mdx deleted file mode 100644 index b283de693c8..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/standard_library/cryptographic_primitives/eddsa.mdx +++ /dev/null @@ -1,37 +0,0 @@ ---- -title: EdDSA Verification -description: Learn about the cryptographic primitives regarding EdDSA -keywords: [cryptographic primitives, Noir project, eddsa, signatures] -sidebar_position: 5 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; - -## 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 -``` - -It is also possible to specify the hash algorithm used for the signature by using the `eddsa_verify` function by passing a type implementing the Hasher trait with the turbofish operator. -For instance, if you want to use Poseidon2 instead, you can do the following: -```rust -use std::hash::poseidon2::Poseidon2Hasher; - -eddsa_verify::(pub_key_a.x, pub_key_a.y, s_a, r8_a.x, r8_a.y, msg); -``` - - - -## eddsa::eddsa_to_pub - -Private to public key conversion. - -Returns `(pub_key_x, pub_key_y)` - -```rust -fn eddsa_to_pub(secret : Field) -> (Field, Field) -``` - diff --git a/docs/versioned_docs/version-v0.33.0/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx b/docs/versioned_docs/version-v0.33.0/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx deleted file mode 100644 index 69e0265c81a..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx +++ /dev/null @@ -1,95 +0,0 @@ ---- -title: Scalar multiplication -description: See how you can perform scalar multiplication in Noir -keywords: [cryptographic primitives, Noir project, scalar multiplication] -sidebar_position: 1 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; - -The following functions perform operations over the embedded curve whose coordinates are defined by the configured noir field. -For the BN254 scalar field, this is BabyJubJub or Grumpkin. - -:::note -Suffixes `_low` and `_high` denote low and high limbs of a scalar. -::: - -## embedded_curve_ops::multi_scalar_mul - -Performs multi scalar multiplication over the embedded curve. -The function accepts arbitrary amount of point-scalar pairs on the input, it multiplies the individual pairs over -the curve and returns a sum of the resulting points. - -Points represented as x and y coordinates [x1, y1, x2, y2, ...], scalars as low and high limbs [low1, high1, low2, high2, ...]. - -```rust title="multi_scalar_mul" showLineNumbers -pub fn multi_scalar_mul( - points: [EmbeddedCurvePoint; N], - scalars: [EmbeddedCurveScalar; N] -) -> EmbeddedCurvePoint -``` -> Source code: noir_stdlib/src/embedded_curve_ops.nr#L91-L96 - - -example - -```rust -fn main(point_x: Field, point_y: Field, scalar_low: Field, scalar_high: Field) { - let point = std::embedded_curve_ops::multi_scalar_mul([point_x, point_y], [scalar_low, scalar_high]); - println(point); -} -``` - -## embedded_curve_ops::fixed_base_scalar_mul - -Performs fixed base scalar multiplication over the embedded curve (multiplies input scalar with a generator point). -The function accepts a single scalar on the input represented as 2 fields. - -```rust title="fixed_base_scalar_mul" showLineNumbers -pub fn fixed_base_scalar_mul(scalar: EmbeddedCurveScalar) -> EmbeddedCurvePoint -``` -> Source code: noir_stdlib/src/embedded_curve_ops.nr#L108-L110 - - -example - -```rust -fn main(scalar_low: Field, scalar_high: Field) { - let point = std::embedded_curve_ops::fixed_base_scalar_mul(scalar_low, scalar_high); - println(point); -} -``` - -## embedded_curve_ops::embedded_curve_add - -Adds two points on the embedded curve. -This function takes two `EmbeddedCurvePoint` structures as parameters, representing points on the curve, and returns a new `EmbeddedCurvePoint` structure that represents their sum. - -### Parameters: -- `point1` (`EmbeddedCurvePoint`): The first point to add. -- `point2` (`EmbeddedCurvePoint`): The second point to add. - -### Returns: -- `EmbeddedCurvePoint`: The resulting point after the addition of `point1` and `point2`. - -```rust title="embedded_curve_add" showLineNumbers -fn embedded_curve_add( - point1: EmbeddedCurvePoint, - point2: EmbeddedCurvePoint -) -> EmbeddedCurvePoint -``` -> Source code: noir_stdlib/src/embedded_curve_ops.nr#L118-L123 - - -example - -```rust -fn main() { - let point1 = EmbeddedCurvePoint { x: 1, y: 2 }; - let point2 = EmbeddedCurvePoint { x: 3, y: 4 }; - let result = std::embedded_curve_ops::embedded_curve_add(point1, point2); - println!("Resulting Point: ({}, {})", result.x, result.y); -} -``` - - diff --git a/docs/versioned_docs/version-v0.33.0/noir/standard_library/cryptographic_primitives/hashes.mdx b/docs/versioned_docs/version-v0.33.0/noir/standard_library/cryptographic_primitives/hashes.mdx deleted file mode 100644 index 797ff8cc22c..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/standard_library/cryptographic_primitives/hashes.mdx +++ /dev/null @@ -1,253 +0,0 @@ ---- -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] -sidebar_position: 0 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; - -## sha256 - -Given an array of bytes, returns the resulting sha256 hash. -Specify a message_size to hash only the first `message_size` bytes of the input. - -```rust title="sha256" showLineNumbers -pub fn sha256(input: [u8; N]) -> [u8; 32] -``` -> Source code: noir_stdlib/src/hash/mod.nr#L14-L16 - - -example: -```rust title="sha256_var" showLineNumbers -let digest = std::hash::sha256_var([x as u8], 1); -``` -> Source code: test_programs/execution_success/sha256/src/main.nr#L16-L18 - - -```rust -fn main() { - let x = [163, 117, 178, 149]; // some random bytes - let hash = std::sha256::sha256_var(x, 4); -} -``` - - - - -## blake2s - -Given an array of bytes, returns an array with the Blake2 hash - -```rust title="blake2s" showLineNumbers -pub fn blake2s(input: [u8; N]) -> [u8; 32] -``` -> Source code: noir_stdlib/src/hash/mod.nr#L20-L22 - - -example: - -```rust -fn main() { - let x = [163, 117, 178, 149]; // some random bytes - let hash = std::hash::blake2s(x); -} -``` - - - -## blake3 - -Given an array of bytes, returns an array with the Blake3 hash - -```rust title="blake3" showLineNumbers -pub fn blake3(input: [u8; N]) -> [u8; 32] -``` -> Source code: noir_stdlib/src/hash/mod.nr#L26-L28 - - -example: - -```rust -fn main() { - let x = [163, 117, 178, 149]; // some random bytes - let hash = std::hash::blake3(x); -} -``` - - - -## pedersen_hash - -Given an array of Fields, returns the Pedersen hash. - -```rust title="pedersen_hash" showLineNumbers -pub fn pedersen_hash(input: [Field; N]) -> Field -``` -> Source code: noir_stdlib/src/hash/mod.nr#L79-L81 - - -example: - -```rust title="pedersen-hash" showLineNumbers -fn main(x: Field, y: Field, expected_hash: Field) { - let hash = std::hash::pedersen_hash([x, y]); - assert_eq(hash, expected_hash); -} -``` -> Source code: test_programs/execution_success/pedersen_hash/src/main.nr#L1-L7 - - - - -## pedersen_commitment - -Given an array of Fields, returns the Pedersen commitment. - -```rust title="pedersen_commitment" showLineNumbers -pub fn pedersen_commitment(input: [Field; N]) -> EmbeddedCurvePoint { -``` -> Source code: noir_stdlib/src/hash/mod.nr#L31-L33 - - -example: - -```rust title="pedersen-commitment" showLineNumbers -fn main(x: Field, y: Field, expected_commitment: std::embedded_curve_ops::EmbeddedCurvePoint) { - let commitment = std::hash::pedersen_commitment([x, y]); - assert_eq(commitment.x, expected_commitment.x); - assert_eq(commitment.y, expected_commitment.y); -} -``` -> Source code: test_programs/execution_success/pedersen_commitment/src/main.nr#L1-L8 - - - - -## 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 title="keccak256" showLineNumbers -pub fn keccak256(input: [u8; N], message_size: u32) -> [u8; 32] -``` -> Source code: noir_stdlib/src/hash/mod.nr#L128-L130 - - -example: - -```rust title="keccak256" showLineNumbers -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 message 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); -} -``` -> Source code: test_programs/execution_success/keccak256/src/main.nr#L1-L21 - - - - -## 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 title="poseidon" showLineNumbers -use std::hash::poseidon; - -fn main(x1: [Field; 2], y1: pub Field, x2: [Field; 4], y2: pub Field) { - let hash1 = poseidon::bn254::hash_2(x1); - assert(hash1 == y1); - - let hash2 = poseidon::bn254::hash_4(x2); - assert(hash2 == y2); -} -``` -> Source code: test_programs/execution_success/poseidon_bn254_hash/src/main.nr#L1-L11 - - -## poseidon 2 - -Given an array of Fields, returns a new Field with the Poseidon2 Hash. Contrary to the Poseidon -function, there is only one hash and you can specify a message_size to hash only the first -`message_size` bytes of the input, - -```rust -// example for hashing the first three elements of the input -Poseidon2::hash(input, 3); -``` - -example: - -```rust title="poseidon2" showLineNumbers -use std::hash::poseidon2; - -fn main(inputs: [Field; 4], expected_hash: Field) { - let hash = poseidon2::Poseidon2::hash(inputs, inputs.len()); - assert_eq(hash, expected_hash); -} -``` -> Source code: test_programs/execution_success/poseidon2/src/main.nr#L1-L8 - - -## 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]) -> 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.33.0/noir/standard_library/cryptographic_primitives/index.md b/docs/versioned_docs/version-v0.33.0/noir/standard_library/cryptographic_primitives/index.md deleted file mode 100644 index 650f30165d5..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/standard_library/cryptographic_primitives/index.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -title: Cryptographic Primitives -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.33.0/noir/standard_library/cryptographic_primitives/schnorr.mdx b/docs/versioned_docs/version-v0.33.0/noir/standard_library/cryptographic_primitives/schnorr.mdx deleted file mode 100644 index 00e7f257612..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/standard_library/cryptographic_primitives/schnorr.mdx +++ /dev/null @@ -1,64 +0,0 @@ ---- -title: Schnorr Signatures -description: Learn how you can verify Schnorr signatures using Noir -keywords: [cryptographic primitives, Noir project, schnorr, signatures] -sidebar_position: 2 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; - -## schnorr::verify_signature - -Verifier for Schnorr signatures over the embedded curve (for BN254 it is Grumpkin). -See schnorr::verify_signature_slice for a version that works directly on slices. - -```rust title="schnorr_verify" showLineNumbers -pub fn verify_signature( - public_key_x: Field, - public_key_y: Field, - signature: [u8; 64], - message: [u8; N] -) -> bool -``` -> Source code: noir_stdlib/src/schnorr.nr#L2-L9 - - -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() -); - -... -``` - - - -## schnorr::verify_signature_slice - -Verifier for Schnorr signatures over the embedded curve (for BN254 it is Grumpkin) -where the message is a slice. - -```rust title="schnorr_verify_slice" showLineNumbers -pub fn verify_signature_slice( - public_key_x: Field, - public_key_y: Field, - signature: [u8; 64], - message: [u8] -) -> bool -``` -> Source code: noir_stdlib/src/schnorr.nr#L13-L20 - - - diff --git a/docs/versioned_docs/version-v0.33.0/noir/standard_library/is_unconstrained.md b/docs/versioned_docs/version-v0.33.0/noir/standard_library/is_unconstrained.md deleted file mode 100644 index 51bb1bda8f1..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/standard_library/is_unconstrained.md +++ /dev/null @@ -1,69 +0,0 @@ ---- -title: Is Unconstrained Function -description: - The is_unconstrained function returns wether the context at that point of the program is unconstrained or not. -keywords: - [ - unconstrained - ] ---- - -It's very common for functions in circuits to take unconstrained hints of an expensive computation and then verify it. This is done by running the hint in an unconstrained context and then verifying the result in a constrained context. - -When a function is marked as unconstrained, any subsequent functions that it calls will also be run in an unconstrained context. However, if we are implementing a library function, other users might call it within an unconstrained context or a constrained one. Generally, in an unconstrained context we prefer just computing the result instead of taking a hint of it and verifying it, since that'd mean doing the same computation twice: - -```rust - -fn my_expensive_computation(){ - ... -} - -unconstrained fn my_expensive_computation_hint(){ - my_expensive_computation() -} - -pub fn external_interface(){ - my_expensive_computation_hint(); - // verify my_expensive_computation: If external_interface is called from unconstrained, this is redundant - ... -} - -``` - -In order to improve the performance in an unconstrained context you can use the function at `std::runtime::is_unconstrained() -> bool`: - - -```rust -use dep::std::runtime::is_unconstrained; - -fn my_expensive_computation(){ - ... -} - -unconstrained fn my_expensive_computation_hint(){ - my_expensive_computation() -} - -pub fn external_interface(){ - if is_unconstrained() { - my_expensive_computation(); - } else { - my_expensive_computation_hint(); - // verify my_expensive_computation - ... - } -} - -``` - -The is_unconstrained result is resolved at compile time, so in unconstrained contexts the compiler removes the else branch, and in constrained contexts the compiler removes the if branch, reducing the amount of compute necessary to run external_interface. - -Note that using `is_unconstrained` in a `comptime` context will also return `true`: - -``` -fn main() { - comptime { - assert(is_unconstrained()); - } -} -``` diff --git a/docs/versioned_docs/version-v0.33.0/noir/standard_library/logging.md b/docs/versioned_docs/version-v0.33.0/noir/standard_library/logging.md deleted file mode 100644 index db75ef9f86f..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/standard_library/logging.md +++ /dev/null @@ -1,78 +0,0 @@ ---- -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, - print statement, - debugging in noir, - noir std library, - logging tutorial, - basic logging in noir, - noir logging implementation, - noir debugging techniques, - rust, - ] ---- - -The standard library provides two familiar statements you can use: `println` and `print`. Despite being a limited implementation of rust's `println!` and `print!` macros, these constructs can be useful for debugging. - -You can print the output of both statements in your Noir code by using the `nargo execute` command or the `--show-output` flag when using `nargo test` (provided there are print statements in your tests). - -It is recommended to use `nargo execute` if you want to debug failing constraints with `println` or `print` 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). Neither `println`, nor `print` are callable for failed constraints caught at compile time. - -Both `print` and `println` are generic functions which can work on integers, fields, strings, and even structs or expressions. Note however, that slices are currently unsupported. For example: - -```rust -struct Person { - age: Field, - height: Field, -} - -fn main(age: Field, height: Field) { - let person = Person { - age: age, - height: height, - }; - println(person); - println(age + height); - println("Hello world!"); -} -``` - -You can print different types in the same statement (including strings) with a type called `fmtstr`. It can be specified in the same way as a normal string, just prepended with an "f" character: - -```rust - let fmt_str = f"i: {i}, j: {j}"; - println(fmt_str); - - let s = myStruct { y: x, x: y }; - println(s); - - println(f"i: {i}, s: {s}"); - - println(x); - println([x, y]); - - let foo = fooStruct { my_struct: s, foo: 15 }; - println(f"s: {s}, foo: {foo}"); - - println(15); // prints 0x0f, implicit Field - println(-1 as u8); // prints 255 - println(-1 as i8); // prints -1 -``` - -Examples shown above are interchangeable between the two `print` statements: - -```rust -let person = Person { age : age, height : height }; - -println(person); -print(person); - -println("Hello world!"); // Prints with a newline at the end of the input -print("Hello world!"); // Prints the input and keeps cursor on the same line -``` diff --git a/docs/versioned_docs/version-v0.33.0/noir/standard_library/merkle_trees.md b/docs/versioned_docs/version-v0.33.0/noir/standard_library/merkle_trees.md deleted file mode 100644 index 6a9ebf72ada..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/standard_library/merkle_trees.md +++ /dev/null @@ -1,58 +0,0 @@ ---- -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/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.as_slice()); - 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.33.0/noir/standard_library/options.md b/docs/versioned_docs/version-v0.33.0/noir/standard_library/options.md deleted file mode 100644 index a1bd4e1de5f..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/standard_library/options.md +++ /dev/null @@ -1,101 +0,0 @@ ---- -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), -} -``` - -The `Option` type, already imported into your Noir program, can be used directly: - -```rust -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. - -### expect - -Asserts `self.is_some()` with a provided custom message and returns the contained `Some` value. The custom message is expected to be a format string. - -### 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.33.0/noir/standard_library/recursion.mdx b/docs/versioned_docs/version-v0.33.0/noir/standard_library/recursion.mdx deleted file mode 100644 index 60414a2fa51..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/standard_library/recursion.mdx +++ /dev/null @@ -1,85 +0,0 @@ ---- -title: Recursive Proofs -description: Learn about how to write recursive proofs in Noir. -keywords: [recursion, recursive proofs, verification_key, verify_proof] ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; - -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. - -Read [the explainer on recursion](../../explainers/explainer-recursion.md) to know more about this function and the [guide on how to use it.](../../how_to/how-to-recursion.md) - -## The `#[recursive]` Attribute - -In Noir, the `#[recursive]` attribute is used to indicate that a circuit is designed for recursive proof generation. When applied, it informs the compiler and the tooling that the circuit should be compiled in a way that makes its proofs suitable for recursive verification. This attribute eliminates the need for manual flagging of recursion at the tooling level, streamlining the proof generation process for recursive circuits. - -### Example usage with `#[recursive]` - -```rust -#[recursive] -fn main(x: Field, y: pub Field) { - assert(x == y, "x and y are not equal"); -} - -// This marks the circuit as recursion-friendly and indicates that proofs generated from this circuit -// are intended for recursive verification. -``` - -By incorporating this attribute directly in the circuit's definition, tooling like Nargo and NoirJS can automatically execute recursive-specific duties for Noir programs (e.g. recursive-friendly proof artifact generation) without additional flags or configurations. - -## Verifying Recursive Proofs - -```rust -#[foreign(recursive_aggregation)] -pub fn verify_proof(verification_key: [Field], proof: [Field], public_inputs: [Field], key_hash: Field) {} -``` - - - -## Example usage - -```rust - -fn main( - verification_key : [Field; 114], - proof : [Field; 93], - public_inputs : [Field; 1], - key_hash : Field, - proof_b : [Field; 93], -) { - std::verify_proof( - verification_key, - proof, - public_inputs, - key_hash - ); - - std::verify_proof( - verification_key, - proof_b, - public_inputs, - key_hash - ); -} -``` - -You can see a full example of recursive proofs in [this example recursion demo repo](https://github.com/noir-lang/noir-examples/tree/master/recursion). - -## 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. - -### `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. diff --git a/docs/versioned_docs/version-v0.33.0/noir/standard_library/traits.md b/docs/versioned_docs/version-v0.33.0/noir/standard_library/traits.md deleted file mode 100644 index 850cc129e73..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/standard_library/traits.md +++ /dev/null @@ -1,501 +0,0 @@ ---- -title: Traits -description: Noir's stdlib provides a few commonly used traits. -keywords: [traits, trait, interface, protocol, default, add, eq] ---- - -## `std::default` - -### `std::default::Default` - -```rust title="default-trait" showLineNumbers -trait Default { - fn default() -> Self; -} -``` -> Source code: noir_stdlib/src/default.nr#L4-L8 - - -Constructs a default value of a type. - -Implementations: -```rust -impl Default for Field { .. } - -impl Default for i8 { .. } -impl Default for i16 { .. } -impl Default for i32 { .. } -impl Default for i64 { .. } - -impl Default for u8 { .. } -impl Default for u16 { .. } -impl Default for u32 { .. } -impl Default for u64 { .. } - -impl Default for () { .. } -impl Default for bool { .. } - -impl Default for [T; N] - where T: Default { .. } - -impl Default for [T] { .. } - -impl Default for (A, B) - where A: Default, B: Default { .. } - -impl Default for (A, B, C) - where A: Default, B: Default, C: Default { .. } - -impl Default for (A, B, C, D) - where A: Default, B: Default, C: Default, D: Default { .. } - -impl Default for (A, B, C, D, E) - where A: Default, B: Default, C: Default, D: Default, E: Default { .. } -``` - -For primitive integer types, the return value of `default` is `0`. Container -types such as arrays are filled with default values of their element type, -except slices whose length is unknown and thus defaulted to zero. - ---- - -## `std::convert` - -### `std::convert::From` - -```rust title="from-trait" showLineNumbers -trait From { - fn from(input: T) -> Self; -} -``` -> Source code: noir_stdlib/src/convert.nr#L1-L5 - - -The `From` trait defines how to convert from a given type `T` to the type on which the trait is implemented. - -The Noir standard library provides a number of implementations of `From` between primitive types. -```rust title="from-impls" showLineNumbers -// Unsigned integers - -impl From for u32 { fn from(value: u8) -> u32 { value as u32 } } - -impl From for u64 { fn from(value: u8) -> u64 { value as u64 } } -impl From for u64 { fn from(value: u32) -> u64 { value as u64 } } - -impl From for Field { fn from(value: u8) -> Field { value as Field } } -impl From for Field { fn from(value: u32) -> Field { value as Field } } -impl From for Field { fn from(value: u64) -> Field { value as Field } } - -// Signed integers - -impl From for i32 { fn from(value: i8) -> i32 { value as i32 } } - -impl From for i64 { fn from(value: i8) -> i64 { value as i64 } } -impl From for i64 { fn from(value: i32) -> i64 { value as i64 } } - -// Booleans -impl From for u8 { fn from(value: bool) -> u8 { value as u8 } } -impl From for u32 { fn from(value: bool) -> u32 { value as u32 } } -impl From for u64 { fn from(value: bool) -> u64 { value as u64 } } -impl From for i8 { fn from(value: bool) -> i8 { value as i8 } } -impl From for i32 { fn from(value: bool) -> i32 { value as i32 } } -impl From for i64 { fn from(value: bool) -> i64 { value as i64 } } -impl From for Field { fn from(value: bool) -> Field { value as Field } } -``` -> Source code: noir_stdlib/src/convert.nr#L25-L52 - - -#### When to implement `From` - -As a general rule of thumb, `From` may be implemented in the [situations where it would be suitable in Rust](https://doc.rust-lang.org/std/convert/trait.From.html#when-to-implement-from): - -- The conversion is *infallible*: Noir does not provide an equivalent to Rust's `TryFrom`, if the conversion can fail then provide a named method instead. -- The conversion is *lossless*: semantically, it should not lose or discard information. For example, `u32: From` can losslessly convert any `u16` into a valid `u32` such that the original `u16` can be recovered. On the other hand, `u16: From` should not be implemented as `2**16` is a `u32` which cannot be losslessly converted into a `u16`. -- The conversion is *value-preserving*: the conceptual kind and meaning of the resulting value is the same, even though the Noir type and technical representation might be different. While it's possible to infallibly and losslessly convert a `u8` into a `str<2>` hex representation, `4u8` and `"04"` are too different for `str<2>: From` to be implemented. -- The conversion is *obvious*: it's the only reasonable conversion between the two types. If there's ambiguity on how to convert between them such that the same input could potentially map to two different values then a named method should be used. For instance rather than implementing `U128: From<[u8; 16]>`, the methods `U128::from_le_bytes` and `U128::from_be_bytes` are used as otherwise the endianness of the array would be ambiguous, resulting in two potential values of `U128` from the same byte array. - -One additional recommendation specific to Noir is: -- The conversion is *efficient*: it's relatively cheap to convert between the two types. Due to being a ZK DSL, it's more important to avoid unnecessary computation compared to Rust. If the implementation of `From` would encourage users to perform unnecessary conversion, resulting in additional proving time, then it may be preferable to expose functionality such that this conversion may be avoided. - -### `std::convert::Into` - -The `Into` trait is defined as the reciprocal of `From`. It should be easy to convince yourself that if we can convert to type `A` from type `B`, then it's possible to convert type `B` into type `A`. - -For this reason, implementing `From` on a type will automatically generate a matching `Into` implementation. One should always prefer implementing `From` over `Into` as implementing `Into` will not generate a matching `From` implementation. - -```rust title="into-trait" showLineNumbers -trait Into { - fn into(self) -> T; -} - -impl Into for U where T: From { - fn into(self) -> T { - T::from(self) - } -} -``` -> Source code: noir_stdlib/src/convert.nr#L13-L23 - - -`Into` is most useful when passing function arguments where the types don't quite match up with what the function expects. In this case, the compiler has enough type information to perform the necessary conversion by just appending `.into()` onto the arguments in question. - ---- - -## `std::cmp` - -### `std::cmp::Eq` - -```rust title="eq-trait" showLineNumbers -trait Eq { - fn eq(self, other: Self) -> bool; -} -``` -> Source code: noir_stdlib/src/cmp.nr#L4-L8 - - -Returns `true` if `self` is equal to `other`. Implementing this trait on a type -allows the type to be used with `==` and `!=`. - -Implementations: -```rust -impl Eq for Field { .. } - -impl Eq for i8 { .. } -impl Eq for i16 { .. } -impl Eq for i32 { .. } -impl Eq for i64 { .. } - -impl Eq for u8 { .. } -impl Eq for u16 { .. } -impl Eq for u32 { .. } -impl Eq for u64 { .. } - -impl Eq for () { .. } -impl Eq for bool { .. } - -impl Eq for [T; N] - where T: Eq { .. } - -impl Eq for [T] - where T: Eq { .. } - -impl Eq for (A, B) - where A: Eq, B: Eq { .. } - -impl Eq for (A, B, C) - where A: Eq, B: Eq, C: Eq { .. } - -impl Eq for (A, B, C, D) - where A: Eq, B: Eq, C: Eq, D: Eq { .. } - -impl Eq for (A, B, C, D, E) - where A: Eq, B: Eq, C: Eq, D: Eq, E: Eq { .. } -``` - -### `std::cmp::Ord` - -```rust title="ord-trait" showLineNumbers -trait Ord { - fn cmp(self, other: Self) -> Ordering; -} -``` -> Source code: noir_stdlib/src/cmp.nr#L113-L117 - - -`a.cmp(b)` compares two values returning `Ordering::less()` if `a < b`, -`Ordering::equal()` if `a == b`, or `Ordering::greater()` if `a > b`. -Implementing this trait on a type allows `<`, `<=`, `>`, and `>=` to be -used on values of the type. - -`std::cmp` also provides `max` and `min` functions for any type which implements the `Ord` trait. - -Implementations: - -```rust -impl Ord for u8 { .. } -impl Ord for u16 { .. } -impl Ord for u32 { .. } -impl Ord for u64 { .. } - -impl Ord for i8 { .. } -impl Ord for i16 { .. } -impl Ord for i32 { .. } - -impl Ord for i64 { .. } - -impl Ord for () { .. } -impl Ord for bool { .. } - -impl Ord for [T; N] - where T: Ord { .. } - -impl Ord for [T] - where T: Ord { .. } - -impl Ord for (A, B) - where A: Ord, B: Ord { .. } - -impl Ord for (A, B, C) - where A: Ord, B: Ord, C: Ord { .. } - -impl Ord for (A, B, C, D) - where A: Ord, B: Ord, C: Ord, D: Ord { .. } - -impl Ord for (A, B, C, D, E) - where A: Ord, B: Ord, C: Ord, D: Ord, E: Ord { .. } -``` - ---- - -## `std::ops` - -### `std::ops::Add`, `std::ops::Sub`, `std::ops::Mul`, and `std::ops::Div` - -These traits abstract over addition, subtraction, multiplication, and division respectively. -Implementing these traits for a given type will also allow that type to be used with the corresponding operator -for that trait (`+` for Add, etc) in addition to the normal method names. - -```rust title="add-trait" showLineNumbers -trait Add { - fn add(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/arith.nr#L1-L5 - -```rust title="sub-trait" showLineNumbers -trait Sub { - fn sub(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/arith.nr#L19-L23 - -```rust title="mul-trait" showLineNumbers -trait Mul { - fn mul(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/arith.nr#L37-L41 - -```rust title="div-trait" showLineNumbers -trait Div { - fn div(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/arith.nr#L55-L59 - - -The implementations block below is given for the `Add` trait, but the same types that implement -`Add` also implement `Sub`, `Mul`, and `Div`. - -Implementations: -```rust -impl Add for Field { .. } - -impl Add for i8 { .. } -impl Add for i16 { .. } -impl Add for i32 { .. } -impl Add for i64 { .. } - -impl Add for u8 { .. } -impl Add for u16 { .. } -impl Add for u32 { .. } -impl Add for u64 { .. } -``` - -### `std::ops::Rem` - -```rust title="rem-trait" showLineNumbers -trait Rem{ - fn rem(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/arith.nr#L73-L77 - - -`Rem::rem(a, b)` is the remainder function returning the result of what is -left after dividing `a` and `b`. Implementing `Rem` allows the `%` operator -to be used with the implementation type. - -Unlike other numeric traits, `Rem` is not implemented for `Field`. - -Implementations: -```rust -impl Rem for u8 { fn rem(self, other: u8) -> u8 { self % other } } -impl Rem for u16 { fn rem(self, other: u16) -> u16 { self % other } } -impl Rem for u32 { fn rem(self, other: u32) -> u32 { self % other } } -impl Rem for u64 { fn rem(self, other: u64) -> u64 { self % other } } - -impl Rem for i8 { fn rem(self, other: i8) -> i8 { self % other } } -impl Rem for i16 { fn rem(self, other: i16) -> i16 { self % other } } -impl Rem for i32 { fn rem(self, other: i32) -> i32 { self % other } } -impl Rem for i64 { fn rem(self, other: i64) -> i64 { self % other } } -``` - -### `std::ops::Neg` - -```rust title="neg-trait" showLineNumbers -trait Neg { - fn neg(self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/arith.nr#L89-L93 - - -`Neg::neg` is equivalent to the unary negation operator `-`. - -Implementations: -```rust title="neg-trait-impls" showLineNumbers -impl Neg for Field { fn neg(self) -> Field { -self } } - -impl Neg for i8 { fn neg(self) -> i8 { -self } } -impl Neg for i16 { fn neg(self) -> i16 { -self } } -impl Neg for i32 { fn neg(self) -> i32 { -self } } -impl Neg for i64 { fn neg(self) -> i64 { -self } } -``` -> Source code: noir_stdlib/src/ops/arith.nr#L95-L102 - - -### `std::ops::Not` - -```rust title="not-trait" showLineNumbers -trait Not { - fn not(self: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/bit.nr#L1-L5 - - -`Not::not` is equivalent to the unary bitwise NOT operator `!`. - -Implementations: -```rust title="not-trait-impls" showLineNumbers -impl Not for bool { fn not(self) -> bool { !self } } - -impl Not for u64 { fn not(self) -> u64 { !self } } -impl Not for u32 { fn not(self) -> u32 { !self } } -impl Not for u16 { fn not(self) -> u16 { !self } } -impl Not for u8 { fn not(self) -> u8 { !self } } -impl Not for u1 { fn not(self) -> u1 { !self } } - -impl Not for i8 { fn not(self) -> i8 { !self } } -impl Not for i16 { fn not(self) -> i16 { !self } } -impl Not for i32 { fn not(self) -> i32 { !self } } -impl Not for i64 { fn not(self) -> i64 { !self } } -``` -> Source code: noir_stdlib/src/ops/bit.nr#L7-L20 - - -### `std::ops::{ BitOr, BitAnd, BitXor }` - -```rust title="bitor-trait" showLineNumbers -trait BitOr { - fn bitor(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/bit.nr#L22-L26 - -```rust title="bitand-trait" showLineNumbers -trait BitAnd { - fn bitand(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/bit.nr#L40-L44 - -```rust title="bitxor-trait" showLineNumbers -trait BitXor { - fn bitxor(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/bit.nr#L58-L62 - - -Traits for the bitwise operations `|`, `&`, and `^`. - -Implementing `BitOr`, `BitAnd` or `BitXor` for a type allows the `|`, `&`, or `^` operator respectively -to be used with the type. - -The implementations block below is given for the `BitOr` trait, but the same types that implement -`BitOr` also implement `BitAnd` and `BitXor`. - -Implementations: -```rust -impl BitOr for bool { fn bitor(self, other: bool) -> bool { self | other } } - -impl BitOr for u8 { fn bitor(self, other: u8) -> u8 { self | other } } -impl BitOr for u16 { fn bitor(self, other: u16) -> u16 { self | other } } -impl BitOr for u32 { fn bitor(self, other: u32) -> u32 { self | other } } -impl BitOr for u64 { fn bitor(self, other: u64) -> u64 { self | other } } - -impl BitOr for i8 { fn bitor(self, other: i8) -> i8 { self | other } } -impl BitOr for i16 { fn bitor(self, other: i16) -> i16 { self | other } } -impl BitOr for i32 { fn bitor(self, other: i32) -> i32 { self | other } } -impl BitOr for i64 { fn bitor(self, other: i64) -> i64 { self | other } } -``` - -### `std::ops::{ Shl, Shr }` - -```rust title="shl-trait" showLineNumbers -trait Shl { - fn shl(self, other: u8) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/bit.nr#L76-L80 - -```rust title="shr-trait" showLineNumbers -trait Shr { - fn shr(self, other: u8) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/bit.nr#L93-L97 - - -Traits for a bit shift left and bit shift right. - -Implementing `Shl` for a type allows the left shift operator (`<<`) to be used with the implementation type. -Similarly, implementing `Shr` allows the right shift operator (`>>`) to be used with the type. - -Note that bit shifting is not currently implemented for signed types. - -The implementations block below is given for the `Shl` trait, but the same types that implement -`Shl` also implement `Shr`. - -Implementations: -```rust -impl Shl for u8 { fn shl(self, other: u8) -> u8 { self << other } } -impl Shl for u16 { fn shl(self, other: u16) -> u16 { self << other } } -impl Shl for u32 { fn shl(self, other: u32) -> u32 { self << other } } -impl Shl for u64 { fn shl(self, other: u64) -> u64 { self << other } } -``` - ---- - -## `std::append` - -### `std::append::Append` - -`Append` can abstract over types that can be appended to - usually container types: - -```rust title="append-trait" showLineNumbers -trait Append { - fn empty() -> Self; - fn append(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/append.nr#L9-L14 - - -`Append` requires two methods: - -- `empty`: Constructs an empty value of `Self`. -- `append`: Append two values together, returning the result. - -Additionally, it is expected that for any implementation: - -- `T::empty().append(x) == x` -- `x.append(T::empty()) == x` - -Implementations: -```rust -impl Append for [T] -impl Append for Quoted -``` diff --git a/docs/versioned_docs/version-v0.33.0/noir/standard_library/zeroed.md b/docs/versioned_docs/version-v0.33.0/noir/standard_library/zeroed.md deleted file mode 100644 index f450fecdd36..00000000000 --- a/docs/versioned_docs/version-v0.33.0/noir/standard_library/zeroed.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -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 -- Slice -- String -- Tuple -- Function - -Using it on other types could result in unexpected behavior. diff --git a/docs/versioned_docs/version-v0.33.0/reference/NoirJS/backend_barretenberg/.nojekyll b/docs/versioned_docs/version-v0.33.0/reference/NoirJS/backend_barretenberg/.nojekyll deleted file mode 100644 index e2ac6616add..00000000000 --- a/docs/versioned_docs/version-v0.33.0/reference/NoirJS/backend_barretenberg/.nojekyll +++ /dev/null @@ -1 +0,0 @@ -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.33.0/reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend.md b/docs/versioned_docs/version-v0.33.0/reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend.md deleted file mode 100644 index 42f065f4a4e..00000000000 --- a/docs/versioned_docs/version-v0.33.0/reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend.md +++ /dev/null @@ -1,141 +0,0 @@ -# BarretenbergBackend - -## Implements - -- [`Backend`](../index.md#backend) -- [`Backend`](../index.md#backend) - -## Constructors - -### new BarretenbergBackend(acirCircuit, options) - -```ts -new BarretenbergBackend(acirCircuit, options): BarretenbergBackend -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `acirCircuit` | `CompiledCircuit` | -| `options` | [`BackendOptions`](../type-aliases/BackendOptions.md) | - -#### Returns - -[`BarretenbergBackend`](BarretenbergBackend.md) - -## Properties - -| Property | Type | Description | -| :------ | :------ | :------ | -| `acirComposer` | `any` | - | -| `acirUncompressedBytecode` | `Uint8Array` | - | -| `api` | `Barretenberg` | - | -| `options` | [`BackendOptions`](../type-aliases/BackendOptions.md) | - | - -## Methods - -### destroy() - -```ts -destroy(): Promise -``` - -#### Returns - -`Promise`\<`void`\> - -*** - -### generateProof() - -```ts -generateProof(compressedWitness): Promise -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `compressedWitness` | `Uint8Array` | - -#### Returns - -`Promise`\<`ProofData`\> - -#### Description - -Generates a proof - -*** - -### generateRecursiveProofArtifacts() - -```ts -generateRecursiveProofArtifacts(proofData, numOfPublicInputs): Promise -``` - -Generates artifacts that will be passed to a circuit that will verify this proof. - -Instead of passing the proof and verification key as a byte array, we pass them -as fields which makes it cheaper to verify in a circuit. - -The proof that is passed here will have been created using a circuit -that has the #[recursive] attribute on its `main` method. - -The number of public inputs denotes how many public inputs are in the inner proof. - -#### Parameters - -| Parameter | Type | Default value | -| :------ | :------ | :------ | -| `proofData` | `ProofData` | `undefined` | -| `numOfPublicInputs` | `number` | `0` | - -#### Returns - -`Promise`\<`object`\> - -#### Example - -```typescript -const artifacts = await backend.generateRecursiveProofArtifacts(proof, numOfPublicInputs); -``` - -*** - -### getVerificationKey() - -```ts -getVerificationKey(): Promise -``` - -#### Returns - -`Promise`\<`Uint8Array`\> - -*** - -### verifyProof() - -```ts -verifyProof(proofData): Promise -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `proofData` | `ProofData` | - -#### Returns - -`Promise`\<`boolean`\> - -#### Description - -Verifies 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.33.0/reference/NoirJS/backend_barretenberg/classes/BarretenbergVerifier.md b/docs/versioned_docs/version-v0.33.0/reference/NoirJS/backend_barretenberg/classes/BarretenbergVerifier.md deleted file mode 100644 index 500276ea748..00000000000 --- a/docs/versioned_docs/version-v0.33.0/reference/NoirJS/backend_barretenberg/classes/BarretenbergVerifier.md +++ /dev/null @@ -1,58 +0,0 @@ -# BarretenbergVerifier - -## Constructors - -### new BarretenbergVerifier(options) - -```ts -new BarretenbergVerifier(options): BarretenbergVerifier -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `options` | [`BackendOptions`](../type-aliases/BackendOptions.md) | - -#### Returns - -[`BarretenbergVerifier`](BarretenbergVerifier.md) - -## Methods - -### destroy() - -```ts -destroy(): Promise -``` - -#### Returns - -`Promise`\<`void`\> - -*** - -### verifyProof() - -```ts -verifyProof(proofData, verificationKey): Promise -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `proofData` | `ProofData` | -| `verificationKey` | `Uint8Array` | - -#### Returns - -`Promise`\<`boolean`\> - -#### Description - -Verifies 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.33.0/reference/NoirJS/backend_barretenberg/classes/UltraHonkBackend.md b/docs/versioned_docs/version-v0.33.0/reference/NoirJS/backend_barretenberg/classes/UltraHonkBackend.md deleted file mode 100644 index be1cd9ad465..00000000000 --- a/docs/versioned_docs/version-v0.33.0/reference/NoirJS/backend_barretenberg/classes/UltraHonkBackend.md +++ /dev/null @@ -1,116 +0,0 @@ -# UltraHonkBackend - -## Implements - -- [`Backend`](../index.md#backend) -- [`Backend`](../index.md#backend) - -## Constructors - -### new UltraHonkBackend(acirCircuit, options) - -```ts -new UltraHonkBackend(acirCircuit, options): UltraHonkBackend -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `acirCircuit` | `CompiledCircuit` | -| `options` | [`BackendOptions`](../type-aliases/BackendOptions.md) | - -#### Returns - -[`UltraHonkBackend`](UltraHonkBackend.md) - -## Properties - -| Property | Type | Description | -| :------ | :------ | :------ | -| `acirUncompressedBytecode` | `Uint8Array` | - | -| `api` | `Barretenberg` | - | -| `options` | [`BackendOptions`](../type-aliases/BackendOptions.md) | - | - -## Methods - -### destroy() - -```ts -destroy(): Promise -``` - -#### Returns - -`Promise`\<`void`\> - -*** - -### generateProof() - -```ts -generateProof(decompressedWitness): Promise -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `decompressedWitness` | `Uint8Array` | - -#### Returns - -`Promise`\<`ProofData`\> - -*** - -### generateRecursiveProofArtifacts() - -```ts -generateRecursiveProofArtifacts(_proofData, _numOfPublicInputs): Promise -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `_proofData` | `ProofData` | -| `_numOfPublicInputs` | `number` | - -#### Returns - -`Promise`\<`object`\> - -*** - -### getVerificationKey() - -```ts -getVerificationKey(): Promise -``` - -#### Returns - -`Promise`\<`Uint8Array`\> - -*** - -### verifyProof() - -```ts -verifyProof(proofData): Promise -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `proofData` | `ProofData` | - -#### Returns - -`Promise`\<`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.33.0/reference/NoirJS/backend_barretenberg/classes/UltraHonkVerifier.md b/docs/versioned_docs/version-v0.33.0/reference/NoirJS/backend_barretenberg/classes/UltraHonkVerifier.md deleted file mode 100644 index aee9460153f..00000000000 --- a/docs/versioned_docs/version-v0.33.0/reference/NoirJS/backend_barretenberg/classes/UltraHonkVerifier.md +++ /dev/null @@ -1,58 +0,0 @@ -# UltraHonkVerifier - -## Constructors - -### new UltraHonkVerifier(options) - -```ts -new UltraHonkVerifier(options): UltraHonkVerifier -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `options` | [`BackendOptions`](../type-aliases/BackendOptions.md) | - -#### Returns - -[`UltraHonkVerifier`](UltraHonkVerifier.md) - -## Methods - -### destroy() - -```ts -destroy(): Promise -``` - -#### Returns - -`Promise`\<`void`\> - -*** - -### verifyProof() - -```ts -verifyProof(proofData, verificationKey): Promise -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `proofData` | `ProofData` | -| `verificationKey` | `Uint8Array` | - -#### Returns - -`Promise`\<`boolean`\> - -#### Description - -Verifies 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.33.0/reference/NoirJS/backend_barretenberg/index.md b/docs/versioned_docs/version-v0.33.0/reference/NoirJS/backend_barretenberg/index.md deleted file mode 100644 index 4699e16dee6..00000000000 --- a/docs/versioned_docs/version-v0.33.0/reference/NoirJS/backend_barretenberg/index.md +++ /dev/null @@ -1,42 +0,0 @@ -# backend_barretenberg - -## Exports - -### Classes - -| Class | Description | -| :------ | :------ | -| [BarretenbergBackend](classes/BarretenbergBackend.md) | - | -| [BarretenbergVerifier](classes/BarretenbergVerifier.md) | - | -| [UltraHonkBackend](classes/UltraHonkBackend.md) | - | -| [UltraHonkVerifier](classes/UltraHonkVerifier.md) | - | - -### Type Aliases - -| Type alias | Description | -| :------ | :------ | -| [BackendOptions](type-aliases/BackendOptions.md) | - | - -## References - -### CompiledCircuit - -Renames and re-exports [Backend](index.md#backend) - -*** - -### ProofData - -Renames and re-exports [Backend](index.md#backend) - -## Variables - -### Backend - -```ts -Backend: any; -``` - -*** - -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.33.0/reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions.md b/docs/versioned_docs/version-v0.33.0/reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions.md deleted file mode 100644 index b49a479f4f4..00000000000 --- a/docs/versioned_docs/version-v0.33.0/reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions.md +++ /dev/null @@ -1,21 +0,0 @@ -# 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 | -| :------ | :------ | :------ | -| `memory` | `object` | - | -| `memory.maximum` | `number` | - | -| `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.33.0/reference/NoirJS/backend_barretenberg/typedoc-sidebar.cjs b/docs/versioned_docs/version-v0.33.0/reference/NoirJS/backend_barretenberg/typedoc-sidebar.cjs deleted file mode 100644 index 8ecf05c0163..00000000000 --- a/docs/versioned_docs/version-v0.33.0/reference/NoirJS/backend_barretenberg/typedoc-sidebar.cjs +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-check -/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ -const typedocSidebar = { items: [{"type":"category","label":"Classes","items":[{"type":"doc","id":"reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend","label":"BarretenbergBackend"},{"type":"doc","id":"reference/NoirJS/backend_barretenberg/classes/BarretenbergVerifier","label":"BarretenbergVerifier"},{"type":"doc","id":"reference/NoirJS/backend_barretenberg/classes/UltraHonkBackend","label":"UltraHonkBackend"},{"type":"doc","id":"reference/NoirJS/backend_barretenberg/classes/UltraHonkVerifier","label":"UltraHonkVerifier"}]},{"type":"category","label":"Type Aliases","items":[{"type":"doc","id":"reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions","label":"BackendOptions"}]}]}; -module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.33.0/reference/NoirJS/noir_js/.nojekyll b/docs/versioned_docs/version-v0.33.0/reference/NoirJS/noir_js/.nojekyll deleted file mode 100644 index e2ac6616add..00000000000 --- a/docs/versioned_docs/version-v0.33.0/reference/NoirJS/noir_js/.nojekyll +++ /dev/null @@ -1 +0,0 @@ -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.33.0/reference/NoirJS/noir_js/classes/Noir.md b/docs/versioned_docs/version-v0.33.0/reference/NoirJS/noir_js/classes/Noir.md deleted file mode 100644 index ead255bc504..00000000000 --- a/docs/versioned_docs/version-v0.33.0/reference/NoirJS/noir_js/classes/Noir.md +++ /dev/null @@ -1,52 +0,0 @@ -# Noir - -## Constructors - -### new Noir(circuit) - -```ts -new Noir(circuit): Noir -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `circuit` | `CompiledCircuit` | - -#### Returns - -[`Noir`](Noir.md) - -## Methods - -### 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) -``` - -*** - -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.33.0/reference/NoirJS/noir_js/functions/and.md b/docs/versioned_docs/version-v0.33.0/reference/NoirJS/noir_js/functions/and.md deleted file mode 100644 index c783283e396..00000000000 --- a/docs/versioned_docs/version-v0.33.0/reference/NoirJS/noir_js/functions/and.md +++ /dev/null @@ -1,22 +0,0 @@ -# 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.33.0/reference/NoirJS/noir_js/functions/blake2s256.md b/docs/versioned_docs/version-v0.33.0/reference/NoirJS/noir_js/functions/blake2s256.md deleted file mode 100644 index 7882d0da8d5..00000000000 --- a/docs/versioned_docs/version-v0.33.0/reference/NoirJS/noir_js/functions/blake2s256.md +++ /dev/null @@ -1,21 +0,0 @@ -# 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.33.0/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md b/docs/versioned_docs/version-v0.33.0/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md deleted file mode 100644 index 5e3cd53e9d3..00000000000 --- a/docs/versioned_docs/version-v0.33.0/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md +++ /dev/null @@ -1,28 +0,0 @@ -# ecdsa\_secp256k1\_verify() - -```ts -ecdsa_secp256k1_verify( - hashed_msg, - public_key_x_bytes, - public_key_y_bytes, - signature): boolean -``` - -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.33.0/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md b/docs/versioned_docs/version-v0.33.0/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md deleted file mode 100644 index 0b20ff68957..00000000000 --- a/docs/versioned_docs/version-v0.33.0/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md +++ /dev/null @@ -1,28 +0,0 @@ -# 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.33.0/reference/NoirJS/noir_js/functions/keccak256.md b/docs/versioned_docs/version-v0.33.0/reference/NoirJS/noir_js/functions/keccak256.md deleted file mode 100644 index d10f155ce86..00000000000 --- a/docs/versioned_docs/version-v0.33.0/reference/NoirJS/noir_js/functions/keccak256.md +++ /dev/null @@ -1,21 +0,0 @@ -# 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.33.0/reference/NoirJS/noir_js/functions/sha256.md b/docs/versioned_docs/version-v0.33.0/reference/NoirJS/noir_js/functions/sha256.md deleted file mode 100644 index 6ba4ecac022..00000000000 --- a/docs/versioned_docs/version-v0.33.0/reference/NoirJS/noir_js/functions/sha256.md +++ /dev/null @@ -1,21 +0,0 @@ -# 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.33.0/reference/NoirJS/noir_js/functions/xor.md b/docs/versioned_docs/version-v0.33.0/reference/NoirJS/noir_js/functions/xor.md deleted file mode 100644 index 8d762b895d3..00000000000 --- a/docs/versioned_docs/version-v0.33.0/reference/NoirJS/noir_js/functions/xor.md +++ /dev/null @@ -1,22 +0,0 @@ -# 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.33.0/reference/NoirJS/noir_js/index.md b/docs/versioned_docs/version-v0.33.0/reference/NoirJS/noir_js/index.md deleted file mode 100644 index 166508f7124..00000000000 --- a/docs/versioned_docs/version-v0.33.0/reference/NoirJS/noir_js/index.md +++ /dev/null @@ -1,49 +0,0 @@ -# noir_js - -## Exports - -### Classes - -| Class | Description | -| :------ | :------ | -| [Noir](classes/Noir.md) | - | - -### Type Aliases - -| Type alias | Description | -| :------ | :------ | -| [ErrorWithPayload](type-aliases/ErrorWithPayload.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) | - | -| [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) | Verifies a ECDSA signature over the secp256k1 curve. | -| [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` | - -## References - -### CompiledCircuit - -Renames and re-exports [InputMap](index.md#inputmap) - -## Variables - -### InputMap - -```ts -InputMap: any; -``` - -*** - -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.33.0/reference/NoirJS/noir_js/type-aliases/ErrorWithPayload.md b/docs/versioned_docs/version-v0.33.0/reference/NoirJS/noir_js/type-aliases/ErrorWithPayload.md deleted file mode 100644 index e8c2f4aef3d..00000000000 --- a/docs/versioned_docs/version-v0.33.0/reference/NoirJS/noir_js/type-aliases/ErrorWithPayload.md +++ /dev/null @@ -1,15 +0,0 @@ -# ErrorWithPayload - -```ts -type ErrorWithPayload: ExecutionError & object; -``` - -## Type declaration - -| Member | Type | Description | -| :------ | :------ | :------ | -| `decodedAssertionPayload` | `any` | - | - -*** - -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.33.0/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md b/docs/versioned_docs/version-v0.33.0/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md deleted file mode 100644 index 812b8b16481..00000000000 --- a/docs/versioned_docs/version-v0.33.0/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md +++ /dev/null @@ -1,24 +0,0 @@ -# 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.33.0/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md b/docs/versioned_docs/version-v0.33.0/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md deleted file mode 100644 index dd95809186a..00000000000 --- a/docs/versioned_docs/version-v0.33.0/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md +++ /dev/null @@ -1,9 +0,0 @@ -# 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.33.0/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md b/docs/versioned_docs/version-v0.33.0/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md deleted file mode 100644 index b71fb78a946..00000000000 --- a/docs/versioned_docs/version-v0.33.0/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md +++ /dev/null @@ -1,9 +0,0 @@ -# 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.33.0/reference/NoirJS/noir_js/type-aliases/WitnessMap.md b/docs/versioned_docs/version-v0.33.0/reference/NoirJS/noir_js/type-aliases/WitnessMap.md deleted file mode 100644 index 258c46f9d0c..00000000000 --- a/docs/versioned_docs/version-v0.33.0/reference/NoirJS/noir_js/type-aliases/WitnessMap.md +++ /dev/null @@ -1,9 +0,0 @@ -# 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.33.0/reference/NoirJS/noir_js/typedoc-sidebar.cjs b/docs/versioned_docs/version-v0.33.0/reference/NoirJS/noir_js/typedoc-sidebar.cjs deleted file mode 100644 index b3156097df6..00000000000 --- a/docs/versioned_docs/version-v0.33.0/reference/NoirJS/noir_js/typedoc-sidebar.cjs +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-check -/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ -const typedocSidebar = { items: [{"type":"category","label":"Classes","items":[{"type":"doc","id":"reference/NoirJS/noir_js/classes/Noir","label":"Noir"}]},{"type":"category","label":"Type Aliases","items":[{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ErrorWithPayload","label":"ErrorWithPayload"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ForeignCallHandler","label":"ForeignCallHandler"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ForeignCallInput","label":"ForeignCallInput"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ForeignCallOutput","label":"ForeignCallOutput"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/WitnessMap","label":"WitnessMap"}]},{"type":"category","label":"Functions","items":[{"type":"doc","id":"reference/NoirJS/noir_js/functions/and","label":"and"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/blake2s256","label":"blake2s256"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify","label":"ecdsa_secp256k1_verify"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify","label":"ecdsa_secp256r1_verify"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/keccak256","label":"keccak256"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/sha256","label":"sha256"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/xor","label":"xor"}]}]}; -module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.33.0/reference/NoirJS/noir_wasm/.nojekyll b/docs/versioned_docs/version-v0.33.0/reference/NoirJS/noir_wasm/.nojekyll deleted file mode 100644 index e2ac6616add..00000000000 --- a/docs/versioned_docs/version-v0.33.0/reference/NoirJS/noir_wasm/.nojekyll +++ /dev/null @@ -1 +0,0 @@ -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.33.0/reference/NoirJS/noir_wasm/functions/compile.md b/docs/versioned_docs/version-v0.33.0/reference/NoirJS/noir_wasm/functions/compile.md deleted file mode 100644 index 6faf763b37f..00000000000 --- a/docs/versioned_docs/version-v0.33.0/reference/NoirJS/noir_wasm/functions/compile.md +++ /dev/null @@ -1,51 +0,0 @@ -# compile() - -```ts -compile( - fileManager, - projectPath?, - logFn?, -debugLogFn?): Promise -``` - -Compiles a Noir project - -## Parameters - -| Parameter | Type | Description | -| :------ | :------ | :------ | -| `fileManager` | `FileManager` | The file manager to use | -| `projectPath`? | `string` | The path to the project inside the file manager. Defaults to the root of the file manager | -| `logFn`? | `LogFn` | A logging function. If not provided, console.log will be used | -| `debugLogFn`? | `LogFn` | A debug logging function. If not provided, logFn will be used | - -## Returns - -`Promise`\<[`ProgramCompilationArtifacts`](../index.md#programcompilationartifacts)\> - -## Example - -```typescript -// Node.js - -import { compile_program, createFileManager } from '@noir-lang/noir_wasm'; - -const fm = createFileManager(myProjectPath); -const myCompiledCode = await compile_program(fm); -``` - -```typescript -// Browser - -import { compile_program, createFileManager } from '@noir-lang/noir_wasm'; - -const fm = createFileManager('/'); -for (const path of files) { - await fm.writeFile(path, await getFileAsStream(path)); -} -const myCompiledCode = await compile_program(fm); -``` - -*** - -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.33.0/reference/NoirJS/noir_wasm/functions/compile_contract.md b/docs/versioned_docs/version-v0.33.0/reference/NoirJS/noir_wasm/functions/compile_contract.md deleted file mode 100644 index 7d0b39a43ef..00000000000 --- a/docs/versioned_docs/version-v0.33.0/reference/NoirJS/noir_wasm/functions/compile_contract.md +++ /dev/null @@ -1,51 +0,0 @@ -# compile\_contract() - -```ts -compile_contract( - fileManager, - projectPath?, - logFn?, -debugLogFn?): Promise -``` - -Compiles a Noir project - -## Parameters - -| Parameter | Type | Description | -| :------ | :------ | :------ | -| `fileManager` | `FileManager` | The file manager to use | -| `projectPath`? | `string` | The path to the project inside the file manager. Defaults to the root of the file manager | -| `logFn`? | `LogFn` | A logging function. If not provided, console.log will be used | -| `debugLogFn`? | `LogFn` | A debug logging function. If not provided, logFn will be used | - -## Returns - -`Promise`\<[`ContractCompilationArtifacts`](../index.md#contractcompilationartifacts)\> - -## Example - -```typescript -// Node.js - -import { compile_contract, createFileManager } from '@noir-lang/noir_wasm'; - -const fm = createFileManager(myProjectPath); -const myCompiledCode = await compile_contract(fm); -``` - -```typescript -// Browser - -import { compile_contract, createFileManager } from '@noir-lang/noir_wasm'; - -const fm = createFileManager('/'); -for (const path of files) { - await fm.writeFile(path, await getFileAsStream(path)); -} -const myCompiledCode = await compile_contract(fm); -``` - -*** - -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.33.0/reference/NoirJS/noir_wasm/functions/createFileManager.md b/docs/versioned_docs/version-v0.33.0/reference/NoirJS/noir_wasm/functions/createFileManager.md deleted file mode 100644 index 7e65c1d69c7..00000000000 --- a/docs/versioned_docs/version-v0.33.0/reference/NoirJS/noir_wasm/functions/createFileManager.md +++ /dev/null @@ -1,21 +0,0 @@ -# createFileManager() - -```ts -createFileManager(dataDir): FileManager -``` - -Creates a new FileManager instance based on fs in node and memfs in the browser (via webpack alias) - -## Parameters - -| Parameter | Type | Description | -| :------ | :------ | :------ | -| `dataDir` | `string` | root of the file system | - -## Returns - -`FileManager` - -*** - -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.33.0/reference/NoirJS/noir_wasm/functions/inflateDebugSymbols.md b/docs/versioned_docs/version-v0.33.0/reference/NoirJS/noir_wasm/functions/inflateDebugSymbols.md deleted file mode 100644 index fcea9275341..00000000000 --- a/docs/versioned_docs/version-v0.33.0/reference/NoirJS/noir_wasm/functions/inflateDebugSymbols.md +++ /dev/null @@ -1,21 +0,0 @@ -# inflateDebugSymbols() - -```ts -inflateDebugSymbols(debugSymbols): any -``` - -Decompresses and decodes the debug symbols - -## Parameters - -| Parameter | Type | Description | -| :------ | :------ | :------ | -| `debugSymbols` | `string` | The base64 encoded debug symbols | - -## Returns - -`any` - -*** - -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.33.0/reference/NoirJS/noir_wasm/index.md b/docs/versioned_docs/version-v0.33.0/reference/NoirJS/noir_wasm/index.md deleted file mode 100644 index b6e0f9d1bc0..00000000000 --- a/docs/versioned_docs/version-v0.33.0/reference/NoirJS/noir_wasm/index.md +++ /dev/null @@ -1,49 +0,0 @@ -# noir_wasm - -## Exports - -### Functions - -| Function | Description | -| :------ | :------ | -| [compile](functions/compile.md) | Compiles a Noir project | -| [compile\_contract](functions/compile_contract.md) | Compiles a Noir project | -| [createFileManager](functions/createFileManager.md) | Creates a new FileManager instance based on fs in node and memfs in the browser (via webpack alias) | -| [inflateDebugSymbols](functions/inflateDebugSymbols.md) | Decompresses and decodes the debug symbols | - -## References - -### compile\_program - -Renames and re-exports [compile](functions/compile.md) - -## Interfaces - -### ContractCompilationArtifacts - -The compilation artifacts of a given contract. - -#### Properties - -| Property | Type | Description | -| :------ | :------ | :------ | -| `contract` | `ContractArtifact` | The compiled contract. | -| `warnings` | `unknown`[] | Compilation warnings. | - -*** - -### ProgramCompilationArtifacts - -The compilation artifacts of a given program. - -#### Properties - -| Property | Type | Description | -| :------ | :------ | :------ | -| `name` | `string` | not part of the compilation output, injected later | -| `program` | `ProgramArtifact` | The compiled contract. | -| `warnings` | `unknown`[] | Compilation warnings. | - -*** - -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.33.0/reference/NoirJS/noir_wasm/typedoc-sidebar.cjs b/docs/versioned_docs/version-v0.33.0/reference/NoirJS/noir_wasm/typedoc-sidebar.cjs deleted file mode 100644 index e0870710349..00000000000 --- a/docs/versioned_docs/version-v0.33.0/reference/NoirJS/noir_wasm/typedoc-sidebar.cjs +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-check -/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ -const typedocSidebar = { items: [{"type":"doc","id":"reference/NoirJS/noir_wasm/index","label":"API"},{"type":"category","label":"Functions","items":[{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/compile","label":"compile"},{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/compile_contract","label":"compile_contract"},{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/createFileManager","label":"createFileManager"},{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/inflateDebugSymbols","label":"inflateDebugSymbols"}]}]}; -module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.33.0/reference/_category_.json b/docs/versioned_docs/version-v0.33.0/reference/_category_.json deleted file mode 100644 index 5b6a20a609a..00000000000 --- a/docs/versioned_docs/version-v0.33.0/reference/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "position": 4, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.33.0/reference/debugger/_category_.json b/docs/versioned_docs/version-v0.33.0/reference/debugger/_category_.json deleted file mode 100644 index 27869205ad3..00000000000 --- a/docs/versioned_docs/version-v0.33.0/reference/debugger/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "Debugger", - "position": 1, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.33.0/reference/debugger/debugger_known_limitations.md b/docs/versioned_docs/version-v0.33.0/reference/debugger/debugger_known_limitations.md deleted file mode 100644 index 936d416ac4b..00000000000 --- a/docs/versioned_docs/version-v0.33.0/reference/debugger/debugger_known_limitations.md +++ /dev/null @@ -1,59 +0,0 @@ ---- -title: Known limitations -description: - An overview of known limitations of the current version of the Noir debugger -keywords: - [ - Nargo, - Noir Debugger, - VS Code, - ] -sidebar_position: 2 ---- - -# Debugger Known Limitations - -There are currently some limits to what the debugger can observe. - -## Mutable references - -The debugger is currently blind to any state mutated via a mutable reference. For example, in: - -``` -let mut x = 1; -let y = &mut x; -*y = 2; -``` - -The update on `x` will not be observed by the debugger. That means, when running `vars` from the debugger REPL, or inspecting the _local variables_ pane in the VS Code debugger, `x` will appear with value 1 despite having executed `*y = 2;`. - -## Variables of type function or mutable references are opaque - -When inspecting variables, any variable of type `Function` or `MutableReference` will render its value as `<>` or `<>`. - -## Debugger instrumentation affects resulting ACIR - -In order to make the state of local variables observable, the debugger compiles Noir circuits interleaving foreign calls that track any mutations to them. While this works (except in the cases described above) and doesn't introduce any behavior changes, it does as a side effect produce bigger bytecode. In particular, when running the command `opcodes` on the REPL debugger, you will notice Unconstrained VM blocks that look like this: - -``` -... -5 BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [], q_c: 2 }), Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(2))], q_c: 0 })] - | outputs=[] - 5.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } - 5.1 | Mov { destination: RegisterIndex(3), source: RegisterIndex(1) } - 5.2 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } - 5.3 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } - 5.4 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } - 5.5 | Mov { destination: RegisterIndex(3), source: RegisterIndex(3) } - 5.6 | Call { location: 8 } - 5.7 | Stop - 5.8 | ForeignCall { function: "__debug_var_assign", destinations: [], inputs: [RegisterIndex(RegisterIndex(2)), RegisterIndex(RegisterIndex(3))] } -... -``` - -If you are interested in debugging/inspecting compiled ACIR without these synthetic changes, you can invoke the REPL debugger with the `--skip-instrumentation` flag or launch the VS Code debugger with the `skipConfiguration` property set to true in its launch configuration. You can find more details about those in the [Debugger REPL reference](debugger_repl.md) and the [VS Code Debugger reference](debugger_vscode.md). - -:::note -Skipping debugger instrumentation means you won't be able to inspect values of local variables. -::: - diff --git a/docs/versioned_docs/version-v0.33.0/reference/debugger/debugger_repl.md b/docs/versioned_docs/version-v0.33.0/reference/debugger/debugger_repl.md deleted file mode 100644 index 46e2011304e..00000000000 --- a/docs/versioned_docs/version-v0.33.0/reference/debugger/debugger_repl.md +++ /dev/null @@ -1,360 +0,0 @@ ---- -title: REPL Debugger -description: - Noir Debugger REPL options and commands. -keywords: - [ - Nargo, - Noir CLI, - Noir Debugger, - REPL, - ] -sidebar_position: 1 ---- - -## Running the REPL debugger - -`nargo debug [OPTIONS] [WITNESS_NAME]` - -Runs the Noir REPL debugger. If a `WITNESS_NAME` is provided the debugger writes the resulting execution witness to a `WITNESS_NAME` 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 debug | -| `--print-acir` | Display the ACIR for compiled circuit | -| `--deny-warnings` | Treat all warnings as errors | -| `--silence-warnings` | Suppress warnings | -| `-h, --help` | Print help | - -None of these options are required. - -:::note -Since the debugger starts by compiling the target package, all Noir compiler options are also available. Check out the [compiler reference](../nargo_commands.md#nargo-compile) to learn more about the compiler options. -::: - -## REPL commands - -Once the debugger is running, it accepts the following commands. - -#### `help` (h) - -Displays the menu of available commands. - -``` -> help -Available commands: - - opcodes display ACIR opcodes - into step into to the next opcode - next step until a new source location is reached - out step until a new source location is reached - and the current stack frame is finished - break LOCATION:OpcodeLocation add a breakpoint at an opcode location - over step until a new source location is reached - without diving into function calls - restart restart the debugging session - delete LOCATION:OpcodeLocation delete breakpoint at an opcode location - witness show witness map - witness index:u32 display a single witness from the witness map - witness index:u32 value:String update a witness with the given value - memset index:usize value:String update a memory cell with the given - value - continue continue execution until the end of the - program - vars show variable values available at this point - in execution - stacktrace display the current stack trace - memory show memory (valid when executing unconstrained code) value - step step to the next ACIR opcode - -Other commands: - - help Show this help message - quit Quit repl - -``` - -### Stepping through programs - -#### `next` (n) - -Step until the next Noir source code location. While other commands, such as [`into`](#into-i) and [`step`](#step-s), allow for finer grained control of the program's execution at the opcode level, `next` is source code centric. For example: - -``` -3 ... -4 fn main(x: u32) { -5 assert(entry_point(x) == 2); -6 swap_entry_point(x, x + 1); -7 -> assert(deep_entry_point(x) == 4); -8 multiple_values_entry_point(x); -9 } -``` - - -Using `next` here would cause the debugger to jump to the definition of `deep_entry_point` (if available). - -If you want to step over `deep_entry_point` and go straight to line 8, use [the `over` command](#over) instead. - -#### `over` - -Step until the next source code location, without diving into function calls. For example: - -``` -3 ... -4 fn main(x: u32) { -5 assert(entry_point(x) == 2); -6 swap_entry_point(x, x + 1); -7 -> assert(deep_entry_point(x) == 4); -8 multiple_values_entry_point(x); -9 } -``` - - -Using `over` here would cause the debugger to execute until line 8 (`multiple_values_entry_point(x);`). - -If you want to step into `deep_entry_point` instead, use [the `next` command](#next-n). - -#### `out` - -Step until the end of the current function call. For example: - -``` - 3 ... - 4 fn main(x: u32) { - 5 assert(entry_point(x) == 2); - 6 swap_entry_point(x, x + 1); - 7 -> assert(deep_entry_point(x) == 4); - 8 multiple_values_entry_point(x); - 9 } - 10 - 11 unconstrained fn returns_multiple_values(x: u32) -> (u32, u32, u32, u32) { - 12 ... - ... - 55 - 56 unconstrained fn deep_entry_point(x: u32) -> u32 { - 57 -> level_1(x + 1) - 58 } - -``` - -Running `out` here will resume execution until line 8. - -#### `step` (s) - -Skips to the next ACIR code. A compiled Noir program is a sequence of ACIR opcodes. However, an unconstrained VM opcode denotes the start of an unconstrained code block, to be executed by the unconstrained VM. For example (redacted for brevity): - -``` -0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] -1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] - 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } - 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } - 1.2 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } - 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } - 1.4 | Call { location: 7 } - ... - 1.43 | Return -2 EXPR [ (1, _1) -2 ] -``` - -The `->` here shows the debugger paused at an ACIR opcode: `BRILLIG`, at index 1, which denotes an unconstrained code block is about to start. - -Using the `step` command at this point would result in the debugger stopping at ACIR opcode 2, `EXPR`, skipping unconstrained computation steps. - -Use [the `into` command](#into-i) instead if you want to follow unconstrained computation step by step. - -#### `into` (i) - -Steps into the next opcode. A compiled Noir program is a sequence of ACIR opcodes. However, a BRILLIG opcode denotes the start of an unconstrained code block, to be executed by the unconstrained VM. For example (redacted for brevity): - -``` -0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] -1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] - 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } - 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } - 1.2 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } - 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } - 1.4 | Call { location: 7 } - ... - 1.43 | Return -2 EXPR [ (1, _1) -2 ] -``` - -The `->` here shows the debugger paused at an ACIR opcode: `BRILLIG`, at index 1, which denotes an unconstrained code block is about to start. - -Using the `into` command at this point would result in the debugger stopping at opcode 1.0, `Mov ...`, allowing the debugger user to follow unconstrained computation step by step. - -Use [the `step` command](#step-s) instead if you want to skip to the next ACIR code directly. - -#### `continue` (c) - -Continues execution until the next breakpoint, or the end of the program. - -#### `restart` (res) - -Interrupts execution, and restarts a new debugging session from scratch. - -#### `opcodes` (o) - -Display the program's ACIR opcode sequence. For example: - -``` -0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] -1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] - 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } - 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } - 1.2 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } - 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } - 1.4 | Call { location: 7 } - ... - 1.43 | Return -2 EXPR [ (1, _1) -2 ] -``` - -### Breakpoints - -#### `break [Opcode]` (or shorthand `b [Opcode]`) - -Sets a breakpoint on the specified opcode index. To get a list of the program opcode numbers, see [the `opcode` command](#opcodes-o). For example: - -``` -0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] -1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] - 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } - 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } - 1.2 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } - 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } - 1.4 | Call { location: 7 } - ... - 1.43 | Return -2 EXPR [ (1, _1) -2 ] -``` - -In this example, issuing a `break 1.2` command adds break on opcode 1.2, as denoted by the `*` character: - -``` -0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] -1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] - 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } - 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } - 1.2 | * Const { destination: RegisterIndex(1), value: Value { inner: 0 } } - 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } - 1.4 | Call { location: 7 } - ... - 1.43 | Return -2 EXPR [ (1, _1) -2 ] -``` - -Running [the `continue` command](#continue-c) at this point would cause the debugger to execute the program until opcode 1.2. - -#### `delete [Opcode]` (or shorthand `d [Opcode]`) - -Deletes a breakpoint at an opcode location. Usage is analogous to [the `break` command](#). - -### Variable inspection - -#### vars - -Show variable values available at this point in execution. - -:::note -The ability to inspect variable values from the debugger depends on compilation to be run in a special debug instrumentation mode. This instrumentation weaves variable tracing code with the original source code. - -So variable value inspection comes at the expense of making the resulting ACIR bytecode bigger and harder to understand and optimize. - -If you find this compromise unacceptable, you can run the debugger with the flag `--skip-debug-instrumentation`. This will compile your circuit without any additional debug information, so the resulting ACIR bytecode will be identical to the one produced by standard Noir compilation. However, if you opt for this, the `vars` command will not be available while debugging. -::: - - -### Stacktrace - -#### `stacktrace` - -Displays the current stack trace. - - -### Witness map - -#### `witness` (w) - -Show witness map. For example: - -``` -_0 = 0 -_1 = 2 -_2 = 1 -``` - -#### `witness [Witness Index]` - -Display a single witness from the witness map. For example: - -``` -> witness 1 -_1 = 2 -``` - -#### `witness [Witness Index] [New value]` - -Overwrite the given index with a new value. For example: - -``` -> witness 1 3 -_1 = 3 -``` - - -### Unconstrained VM memory - -#### `memory` - -Show unconstrained VM memory state. For example: - -``` -> memory -At opcode 1.13: Store { destination_pointer: RegisterIndex(0), source: RegisterIndex(3) } -... -> registers -0 = 0 -1 = 10 -2 = 0 -3 = 1 -4 = 1 -5 = 2³² -6 = 1 -> into -At opcode 1.14: Const { destination: RegisterIndex(5), value: Value { inner: 1 } } -... -> memory -0 = 1 -> -``` - -In the example above: we start with clean memory, then step through a `Store` opcode which stores the value of register 3 (1) into the memory address stored in register 0 (0). Thus now `memory` shows memory address 0 contains value 1. - -:::note -This command is only functional while the debugger is executing unconstrained code. -::: - -#### `memset [Memory address] [New value]` - -Update a memory cell with the given value. For example: - -``` -> memory -0 = 1 -> memset 0 2 -> memory -0 = 2 -> memset 1 4 -> memory -0 = 2 -1 = 4 -> -``` - -:::note -This command is only functional while the debugger is executing unconstrained code. -::: \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.33.0/reference/debugger/debugger_vscode.md b/docs/versioned_docs/version-v0.33.0/reference/debugger/debugger_vscode.md deleted file mode 100644 index c027332b3b0..00000000000 --- a/docs/versioned_docs/version-v0.33.0/reference/debugger/debugger_vscode.md +++ /dev/null @@ -1,82 +0,0 @@ ---- -title: VS Code Debugger -description: - VS Code Debugger configuration and features. -keywords: - [ - Nargo, - Noir CLI, - Noir Debugger, - VS Code, - IDE, - ] -sidebar_position: 0 ---- - -# VS Code Noir Debugger Reference - -The Noir debugger enabled by the vscode-noir extension ships with default settings such that the most common scenario should run without any additional configuration steps. - -These defaults can nevertheless be overridden by defining a launch configuration file. This page provides a reference for the properties you can override via a launch configuration file, as well as documenting the Nargo `dap` command, which is a dependency of the VS Code Noir debugger. - - -## Creating and editing launch configuration files - -To create a launch configuration file from VS Code, open the _debug pane_, and click on _create a launch.json file_. - -![Creating a launch configuration file](@site/static/img/debugger/ref1-create-launch.png) - -A `launch.json` file will be created, populated with basic defaults. - -### Noir Debugger launch.json properties - -#### projectFolder - -_String, optional._ - -Absolute path to the Nargo project to debug. By default, it is dynamically determined by looking for the nearest `Nargo.toml` file to the active file at the moment of launching the debugger. - -#### proverName - -_String, optional._ - -Name of the prover input to use. Defaults to `Prover`, which looks for a file named `Prover.toml` at the `projectFolder`. - -#### generateAcir - -_Boolean, optional._ - -If true, generate ACIR opcodes instead of unconstrained opcodes which will be closer to release binaries but less convenient for debugging. Defaults to `false`. - -#### skipInstrumentation - -_Boolean, optional._ - -Skips variables debugging instrumentation of code, making debugging less convenient but the resulting binary smaller and closer to production. Defaults to `false`. - -:::note -Skipping instrumentation causes the debugger to be unable to inspect local variables. -::: - -## `nargo dap [OPTIONS]` - -When run without any option flags, it starts the Nargo Debug Adapter Protocol server, which acts as the debugging backend for the VS Code Noir Debugger. - -All option flags are related to preflight checks. The Debug Adapter Protocol specifies how errors are to be informed from a running DAP server, but it doesn't specify mechanisms to communicate server initialization errors between the DAP server and its client IDE. - -Thus `nargo dap` ships with a _preflight check_ mode. If flag `--preflight-check` and the rest of the `--preflight-*` flags are provided, Nargo will run the same initialization routine except it will not start the DAP server. - -`vscode-noir` will then run `nargo dap` in preflight check mode first before a debugging session starts. If the preflight check ends in error, vscode-noir will present stderr and stdout output from this process through its own Output pane in VS Code. This makes it possible for users to diagnose what pieces of configuration might be wrong or missing in case of initialization errors. - -If the preflight check succeeds, `vscode-noir` proceeds to start the DAP server normally but running `nargo dap` without any additional flags. - -### Options - -| Option | Description | -| --------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | -| `--preflight-check` | If present, dap runs in preflight check mode. | -| `--preflight-project-folder ` | Absolute path to the project to debug for preflight check. | -| `--preflight-prover-name ` | Name of prover file to use for preflight check | -| `--preflight-generate-acir` | Optional. If present, compile in ACIR mode while running preflight check. | -| `--preflight-skip-instrumentation` | Optional. If present, compile without introducing debug instrumentation while running preflight check. | -| `-h, --help` | Print help. | diff --git a/docs/versioned_docs/version-v0.33.0/reference/nargo_commands.md b/docs/versioned_docs/version-v0.33.0/reference/nargo_commands.md deleted file mode 100644 index 8384788fa81..00000000000 --- a/docs/versioned_docs/version-v0.33.0/reference/nargo_commands.md +++ /dev/null @@ -1,289 +0,0 @@ ---- -title: Nargo -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, - ] -sidebar_position: 0 ---- - -# Command-Line Help for `nargo` - -This document contains the help content for the `nargo` command-line program. - -**Command Overview:** - -* [`nargo`↴](#nargo) -* [`nargo check`↴](#nargo-check) -* [`nargo fmt`↴](#nargo-fmt) -* [`nargo compile`↴](#nargo-compile) -* [`nargo new`↴](#nargo-new) -* [`nargo init`↴](#nargo-init) -* [`nargo execute`↴](#nargo-execute) -* [`nargo debug`↴](#nargo-debug) -* [`nargo test`↴](#nargo-test) -* [`nargo info`↴](#nargo-info) -* [`nargo lsp`↴](#nargo-lsp) - -## `nargo` - -Noir's package manager - -**Usage:** `nargo ` - -###### **Subcommands:** - -* `check` — Checks the constraint system for errors -* `fmt` — Format the Noir files in a workspace -* `compile` — Compile the program and its secret execution trace into ACIR format -* `new` — Create a Noir project in a new directory -* `init` — Create a Noir project in the current directory -* `execute` — Executes a circuit to calculate its return value -* `debug` — Executes a circuit in debug mode -* `test` — Run the tests for this program -* `info` — Provides detailed information on each of a program's function (represented by a single circuit) -* `lsp` — Starts the Noir LSP server - -###### **Options:** - - - - -## `nargo check` - -Checks the constraint system for errors - -**Usage:** `nargo check [OPTIONS]` - -###### **Options:** - -* `--package ` — The name of the package to check -* `--workspace` — Check all packages in the workspace -* `--overwrite` — Force overwrite of existing files -* `--expression-width ` — Specify the backend expression width that should be targeted -* `--bounded-codegen` — Generate ACIR with the target backend expression width. The default is to generate ACIR without a bound and split expressions after code generation. Activating this flag can sometimes provide optimizations for certain programs - - Default value: `false` -* `--force` — Force a full recompilation -* `--print-acir` — Display the ACIR for compiled circuit -* `--deny-warnings` — Treat all warnings as errors -* `--silence-warnings` — Suppress warnings -* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" - - - -## `nargo fmt` - -Format the Noir files in a workspace - -**Usage:** `nargo fmt [OPTIONS]` - -###### **Options:** - -* `--check` — Run noirfmt in check mode - - - -## `nargo compile` - -Compile the program and its secret execution trace into ACIR format - -**Usage:** `nargo compile [OPTIONS]` - -###### **Options:** - -* `--package ` — The name of the package to compile -* `--workspace` — Compile all packages in the workspace -* `--expression-width ` — Specify the backend expression width that should be targeted -* `--bounded-codegen` — Generate ACIR with the target backend expression width. The default is to generate ACIR without a bound and split expressions after code generation. Activating this flag can sometimes provide optimizations for certain programs - - Default value: `false` -* `--force` — Force a full recompilation -* `--print-acir` — Display the ACIR for compiled circuit -* `--deny-warnings` — Treat all warnings as errors -* `--silence-warnings` — Suppress warnings -* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" - - - -## `nargo new` - -Create a Noir project in a new directory - -**Usage:** `nargo new [OPTIONS] ` - -###### **Arguments:** - -* `` — The path to save the new project - -###### **Options:** - -* `--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 - - - -## `nargo init` - -Create a Noir project in the current directory - -**Usage:** `nargo init [OPTIONS]` - -###### **Options:** - -* `--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 - - - -## `nargo execute` - -Executes a circuit to calculate its return value - -**Usage:** `nargo execute [OPTIONS] [WITNESS_NAME]` - -###### **Arguments:** - -* `` — Write the execution witness to named file - -###### **Options:** - -* `-p`, `--prover-name ` — The name of the toml file which contains the inputs for the prover - - Default value: `Prover` -* `--package ` — The name of the package to execute -* `--workspace` — Execute all packages in the workspace -* `--expression-width ` — Specify the backend expression width that should be targeted -* `--bounded-codegen` — Generate ACIR with the target backend expression width. The default is to generate ACIR without a bound and split expressions after code generation. Activating this flag can sometimes provide optimizations for certain programs - - Default value: `false` -* `--force` — Force a full recompilation -* `--print-acir` — Display the ACIR for compiled circuit -* `--deny-warnings` — Treat all warnings as errors -* `--silence-warnings` — Suppress warnings -* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" -* `--oracle-resolver ` — JSON RPC url to solve oracle calls - - - -## `nargo debug` - -Executes a circuit in debug mode - -**Usage:** `nargo debug [OPTIONS] [WITNESS_NAME]` - -###### **Arguments:** - -* `` — Write the execution witness to named file - -###### **Options:** - -* `-p`, `--prover-name ` — The name of the toml file which contains the inputs for the prover - - Default value: `Prover` -* `--package ` — The name of the package to execute -* `--expression-width ` — Specify the backend expression width that should be targeted -* `--bounded-codegen` — Generate ACIR with the target backend expression width. The default is to generate ACIR without a bound and split expressions after code generation. Activating this flag can sometimes provide optimizations for certain programs - - Default value: `false` -* `--force` — Force a full recompilation -* `--print-acir` — Display the ACIR for compiled circuit -* `--deny-warnings` — Treat all warnings as errors -* `--silence-warnings` — Suppress warnings -* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" -* `--acir-mode` — Force ACIR output (disabling instrumentation) -* `--skip-instrumentation ` — Disable vars debug instrumentation (enabled by default) - - Possible values: `true`, `false` - - - - -## `nargo test` - -Run the tests for this program - -**Usage:** `nargo test [OPTIONS] [TEST_NAME]` - -###### **Arguments:** - -* `` — If given, only tests with names containing this string will be run - -###### **Options:** - -* `--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 -* `--expression-width ` — Specify the backend expression width that should be targeted -* `--bounded-codegen` — Generate ACIR with the target backend expression width. The default is to generate ACIR without a bound and split expressions after code generation. Activating this flag can sometimes provide optimizations for certain programs - - Default value: `false` -* `--force` — Force a full recompilation -* `--print-acir` — Display the ACIR for compiled circuit -* `--deny-warnings` — Treat all warnings as errors -* `--silence-warnings` — Suppress warnings -* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" -* `--oracle-resolver ` — JSON RPC url to solve oracle calls - - - -## `nargo info` - -Provides detailed information on each of a program's function (represented by a single circuit) - -Current information provided per circuit: 1. The number of ACIR opcodes 2. Counts the final number gates in the circuit used by a backend - -**Usage:** `nargo info [OPTIONS]` - -###### **Options:** - -* `--package ` — The name of the package to detail -* `--workspace` — Detail all packages in the workspace -* `--expression-width ` — Specify the backend expression width that should be targeted -* `--bounded-codegen` — Generate ACIR with the target backend expression width. The default is to generate ACIR without a bound and split expressions after code generation. Activating this flag can sometimes provide optimizations for certain programs - - Default value: `false` -* `--force` — Force a full recompilation -* `--print-acir` — Display the ACIR for compiled circuit -* `--deny-warnings` — Treat all warnings as errors -* `--silence-warnings` — Suppress warnings -* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" - - - -## `nargo lsp` - -Starts the Noir LSP server - -Starts an LSP server which allows IDEs such as VS Code to display diagnostics in Noir source. - -VS Code Noir Language Support: https://marketplace.visualstudio.com/items?itemName=noir-lang.vscode-noir - -**Usage:** `nargo lsp` - - - -
- - - This document was generated automatically by - clap-markdown. - - diff --git a/docs/versioned_docs/version-v0.33.0/reference/noir_codegen.md b/docs/versioned_docs/version-v0.33.0/reference/noir_codegen.md deleted file mode 100644 index db8f07dc22e..00000000000 --- a/docs/versioned_docs/version-v0.33.0/reference/noir_codegen.md +++ /dev/null @@ -1,114 +0,0 @@ ---- -title: Noir Codegen for TypeScript -description: Learn how to use Noir codegen to generate TypeScript bindings -keywords: [Nargo, Noir, compile, TypeScript] -sidebar_position: 3 ---- - -When using TypeScript, it is extra work to interpret Noir program outputs in a type-safe way. Third party libraries may exist for popular Noir programs, but they are either hard to find or unmaintained. - -Now you can generate TypeScript bindings for your Noir programs in two steps: -1. Exporting Noir functions using `nargo export` -2. Using the TypeScript module `noir_codegen` to generate TypeScript binding - -**Note:** you can only export functions from a Noir *library* (not binary or contract program types). - -## Installation - -### Your TypeScript project - -If you don't already have a TypeScript project you can add the module with `yarn` (or `npm`), then initialize it: - -```bash -yarn add typescript -D -npx tsc --init -``` - -### Add TypeScript module - `noir_codegen` - -The following command will add the module to your project's devDependencies: - -```bash -yarn add @noir-lang/noir_codegen -D -``` - -### Nargo library -Make sure you have Nargo, v0.25.0 or greater, installed. If you don't, follow the [installation guide](../getting_started/installation/index.md). - -If you're in a new project, make a `circuits` folder and create a new Noir library: - -```bash -mkdir circuits && cd circuits -nargo new --lib myNoirLib -``` - -## Usage - -### Export ABI of specified functions - -First go to the `.nr` files in your Noir library, and add the `#[export]` macro to each function that you want to use in TypeScript. - -```rust -#[export] -fn your_function(... -``` - -From your Noir library (where `Nargo.toml` is), run the following command: - -```bash -nargo export -``` - -You will now have an `export` directory with a .json file per exported function. - -You can also specify the directory of Noir programs using `--program-dir`, for example: - -```bash -nargo export --program-dir=./circuits/myNoirLib -``` - -### Generate TypeScript bindings from exported functions - -To use the `noir-codegen` package we added to the TypeScript project: - -```bash -yarn noir-codegen ./export/your_function.json -``` - -This creates an `exports` directory with an `index.ts` file containing all exported functions. - -**Note:** adding `--out-dir` allows you to specify an output dir for your TypeScript bindings to go. Eg: - -```bash -yarn noir-codegen ./export/*.json --out-dir ./path/to/output/dir -``` - -## Example .nr function to .ts output - -Consider a Noir library with this function: - -```rust -#[export] -fn not_equal(x: Field, y: Field) -> bool { - x != y -} -``` - -After the export and codegen steps, you should have an `index.ts` like: - -```typescript -export type Field = string; - - -export const is_equal_circuit: CompiledCircuit = -{"abi":{"parameters":[{"name":"x","type":{"kind":"field"},"visibility":"private"},{"name":"y","type":{"kind":"field"},"visibility":"private"}],"return_type":{"abi_type":{"kind":"boolean"},"visibility":"private"}},"bytecode":"H4sIAAAAAAAA/7WUMQ7DIAxFQ0Krrr2JjSGYLVcpKrn/CaqqDQN12WK+hPBgmWd/wEyHbF1SS923uhOs3pfoChI+wKXMAXzIKyNj4PB0TFTYc0w5RUjoqeAeEu1wqK0F54RGkWvW44LPzExnlkbMEs4JNZmN8PxS42uHv82T8a3Jeyn2Ks+VLPcO558HmyLMCDOXAXXtpPt4R/Rt9T36ss6dS9HGPx/eG17nGegKBQAA"}; - -export async function is_equal(x: Field, y: Field, foreignCallHandler?: ForeignCallHandler): Promise { - const program = new Noir(is_equal_circuit); - const args: InputMap = { x, y }; - const { returnValue } = await program.execute(args, foreignCallHandler); - return returnValue as boolean; -} -``` - -Now the `is_equal()` function and relevant types are readily available for use in TypeScript. diff --git a/docs/versioned_docs/version-v0.33.0/tooling/debugger.md b/docs/versioned_docs/version-v0.33.0/tooling/debugger.md deleted file mode 100644 index 9b7565ba9ff..00000000000 --- a/docs/versioned_docs/version-v0.33.0/tooling/debugger.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: Debugger -description: Learn about the Noir Debugger, in its REPL or VS Code versions. -keywords: [Nargo, VSCode, Visual Studio Code, REPL, Debugger] -sidebar_position: 2 ---- - -# Noir Debugger - -There are currently two ways of debugging Noir programs: - -1. From VS 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). -2. Via the REPL debugger, which ships with Nargo. - -In order to use either version of the debugger, you will need to install recent enough versions of Noir, [Nargo](../getting_started/installation/index.md) and vscode-noir: - -- Noir & Nargo ≥0.28.0 -- Noir's VS Code extension ≥0.0.11 - -:::info -At the moment, the debugger supports debugging binary projects, but not contracts. -::: - -We cover the VS Code Noir debugger more in depth in [its VS Code debugger how-to guide](../how_to/debugger/debugging_with_vs_code.md) and [the reference](../reference/debugger/debugger_vscode.md). - -The REPL debugger is discussed at length in [the REPL debugger how-to guide](../how_to/debugger/debugging_with_the_repl.md) and [the reference](../reference/debugger/debugger_repl.md). diff --git a/docs/versioned_docs/version-v0.33.0/tooling/language_server.md b/docs/versioned_docs/version-v0.33.0/tooling/language_server.md deleted file mode 100644 index 81e0356ef8a..00000000000 --- a/docs/versioned_docs/version-v0.33.0/tooling/language_server.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -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] -sidebar_position: 0 ---- - -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.33.0/tooling/testing.md b/docs/versioned_docs/version-v0.33.0/tooling/testing.md deleted file mode 100644 index 866677da567..00000000000 --- a/docs/versioned_docs/version-v0.33.0/tooling/testing.md +++ /dev/null @@ -1,79 +0,0 @@ ---- -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] -sidebar_position: 1 ---- - -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 all -the constraints 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); -} -``` - -The string given to `should_fail_with` doesn't need to exactly match the failure reason, it just needs to be a substring of it: - -```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 = "airspeed velocity")] -fn test_bridgekeeper() { - main(32); -} -``` \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.33.0/tutorials/noirjs_app.md b/docs/versioned_docs/version-v0.33.0/tutorials/noirjs_app.md deleted file mode 100644 index eac28168445..00000000000 --- a/docs/versioned_docs/version-v0.33.0/tutorials/noirjs_app.md +++ /dev/null @@ -1,362 +0,0 @@ ---- -title: Building a web app with NoirJS -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, app] -sidebar_position: 0 -pagination_next: noir/concepts/data_types/index ---- - -NoirJS is a set of packages meant to work both in a browser and a server environment. In this tutorial, we will build a simple web app using them. From here, you should get an idea on how to proceed with your own Noir projects! - -You can find the complete app code for this guide [here](https://github.com/noir-lang/tiny-noirjs-app). - -## Setup - -:::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.31.x matches `noir_js@0.31.x`, etc. - -In this guide, we will be pinned to 0.31.0. - -::: - -Before we start, we want to make sure we have Node, Nargo and the Barretenberg proving system (`bb`) installed. - -We start by opening a terminal and executing `node --version`. If we don't get an output like `v20.10.0`, that means node is not installed. Let's do that by following the handy [nvm guide](https://github.com/nvm-sh/nvm?tab=readme-ov-file#install--update-script). - -As for `Nargo`, we can follow the [Nargo guide](../getting_started/installation/index.md) to install it. If you're lazy, just paste this on a terminal and run `noirup`: - -```sh -curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash -``` - -Follow the instructions on [this page](https://github.com/AztecProtocol/aztec-packages/tree/master/barretenberg/cpp/src/barretenberg/bb#installation) to install `bb`. -Version 0.41.0 is compatible with `nargo` version 0.31.0, which you can install with `bbup -v 0.41.0` once `bbup` is installed. - -Easy enough. Onwards! - -## Our project - -ZK is a powerful technology. An app that doesn't reveal one of the inputs to _anyone_ is almost unbelievable, yet Noir makes it as easy as a single line of code. - -In fact, it's so simple that it comes nicely packaged in `nargo`. Let's do that! - -### Nargo - -Run: - -```bash -nargo new circuit -``` - -And... That's about it. Your program is ready to be compiled and run. - -To compile, let's `cd` into the `circuit` folder to enter our project, and call: - -```bash -nargo compile -``` - -This compiles our circuit into `json` format and add it to a new `target` folder. - -:::info - -At this point in the tutorial, your folder structure should look like this: - -```tree -. -└── circuit <---- our working directory - ├── Nargo.toml - ├── src - │ └── main.nr - └── target - └── circuit.json -``` - -::: - -### Node and Vite - -If you want to explore Nargo, feel free to go on a side-quest now and follow the steps in the -[getting started](../getting_started/hello_noir/index.md) guide. However, we want our app to run on the browser, so we need Vite. - -Vite is a powerful tool to generate static websites. While it provides all kinds of features, let's just go barebones with some good old vanilla JS. - -To do this this, go back to the previous folder (`cd ..`) and create a new vite project by running `npm create vite` and choosing "Vanilla" and "Javascript". - -A wild `vite-project` directory should now appear in your root folder! Let's not waste any time and dive right in: - -```bash -cd vite-project -``` - -### Setting Up Vite and Configuring the Project - -Before we proceed with any coding, let's get our environment tailored for Noir. We'll start by laying down the foundations with a `vite.config.js` file. This little piece of configuration is our secret sauce for making sure everything meshes well with the NoirJS libraries and other special setups we might need, like handling WebAssembly modules. Here’s how you get that going: - -#### Creating the vite.config.js - -In your freshly minted `vite-project` folder, create a new file named `vite.config.js` and open it in your code editor. Paste the following to set the stage: - -```javascript -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 { - build: { - target: 'esnext', - rollupOptions: { - external: ['@aztec/bb.js'] - } - }, - optimizeDeps: { - esbuildOptions: { - target: 'esnext' - } - }, - plugins: [ - copy({ - targets: [{ src: 'node_modules/**/*.wasm', dest: 'node_modules/.vite/dist' }], - copySync: true, - hook: 'buildStart', - }), - command === 'serve' ? wasmContentTypePlugin : [], - ], - }; - } - - return {}; -}); -``` - -#### Install Dependencies - -Now that our stage is set, install the necessary NoirJS packages along with our other dependencies: - -```bash -npm install && npm install @noir-lang/backend_barretenberg@0.31.0 @noir-lang/noir_js@0.31.0 -npm install rollup-plugin-copy --save-dev -``` - -:::info - -At this point in the tutorial, your folder structure should look like this: - -```tree -. -└── circuit - └── ...etc... -└── vite-project <---- our working directory - └── ...etc... -``` - -::: - -#### Some cleanup - -`npx create vite` is amazing but it creates a bunch of files we don't really need for our simple example. Actually, let's just delete everything except for `vite.config.js`, `index.html`, `main.js` and `package.json`. I feel lighter already. - -![my heart is ready for you, noir.js](@site/static/img/memes/titanic.jpeg) - -## HTML - -Our app won't run like this, of course. We need some working HTML, at least. Let's open our broken-hearted `index.html` and replace everything with this code snippet: - -```html - - - - - - -

Noir app

-
- - -
-
-

Logs

-

Proof

-
- - -``` - -It _could_ be a beautiful UI... Depending on which universe you live in. - -## Some good old vanilla Javascript - -Our love for Noir needs undivided attention, so let's just open `main.js` and delete everything (this is where the romantic scenery becomes a bit creepy). - -Start by pasting in this boilerplate code: - -```js -function display(container, msg) { - const c = document.getElementById(container); - const p = document.createElement('p'); - p.textContent = msg; - c.appendChild(p); -} - -document.getElementById('submitGuess').addEventListener('click', async () => { - try { - // here's where love happens - } catch (err) { - display('logs', 'Oh 💔 Wrong guess'); - } -}); -``` - -The display function doesn't do much. We're simply manipulating our website to see stuff happening. For example, if the proof fails, it will simply log a broken heart 😢 - -:::info - -At this point in the tutorial, your folder structure should look like this: - -```tree -. -└── circuit - └── ...same as above -└── vite-project - ├── vite.config.js - ├── main.js - ├── package.json - └── index.html -``` - -You'll see other files and folders showing up (like `package-lock.json`, `node_modules`) but you shouldn't have to care about those. - -::: - -## Some NoirJS - -We're starting with the good stuff now. If you've compiled the circuit as described above, you should have a `json` file we want to import at the very top of our `main.js` file: - -```ts -import circuit from '../circuit/target/circuit.json'; -``` - -[Noir is backend-agnostic](../index.mdx#whats-new-about-noir). We write Noir, but we also need a proving backend. That's why we need to import and instantiate the two dependencies we installed above: `BarretenbergBackend` and `Noir`. Let's import them right below: - -```js -import { BarretenbergBackend, BarretenbergVerifier as Verifier } from '@noir-lang/backend_barretenberg'; -import { Noir } from '@noir-lang/noir_js'; -``` - -And instantiate them inside our try-catch block: - -```ts -// try { -const backend = new BarretenbergBackend(circuit); -const noir = new Noir(circuit); -// } -``` - -:::note - -For the remainder of the tutorial, everything will be happening inside the `try` block - -::: - -## Our app - -Now for the app itself. We're capturing whatever is in the input when people press the submit button. Just add this: - -```js -const x = parseInt(document.getElementById('guessInput').value); -const input = { x, y: 2 }; -``` - -Now we're ready to prove stuff! Let's feed some inputs to our circuit and calculate the proof: - -```js -await setup(); // let's squeeze our wasm inits here - -display('logs', 'Generating proof... ⌛'); -const { witness } = await noir.execute(input); -const proof = await backend.generateProof(witness); -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 run dev`. If it doesn't open a browser for you, just visit `localhost:5173`. You should now see the worst UI ever, with an ugly input. - -![Getting Started 0](@site/static/img/noir_getting_started_1.png) - -Now, our circuit says `fn main(x: Field, y: pub Field)`. This means only the `y` value is public, and it's hardcoded above: `input = { x, y: 2 }`. In other words, you won't need to send your secret`x` to the verifier! - -By inputting any number other than 2 in the input box and clicking "submit", you should get a valid proof. Otherwise the proof won't even generate correctly. By the way, if you're human, you shouldn't be able to understand anything on the "proof" box. That's OK. We like you, human ❤️. - -## Verifying - -Time to celebrate, yes! But we shouldn't trust machines so blindly. Let's add these lines to see our proof being verified: - -```js -display('logs', 'Verifying proof... ⌛'); -const isValid = await backend.verifyProof(proof); - -// or to cache and use the verification key: -// const verificationKey = await backend.getVerificationKey(); -// const verifier = new Verifier(); -// const isValid = await verifier.verifyProof(proof, verificationKey); - -if (isValid) display('logs', 'Verifying proof... ✅'); -``` - -You have successfully generated a client-side Noir web app! - -![coded app without math knowledge](@site/static/img/memes/flextape.jpeg) - -## 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/vite-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. - -## UltraHonk Backend - -Barretenberg has recently exposed a new UltraHonk backend. We can use UltraHonk in NoirJS after version 0.33.0. Everything will be the same as the tutorial above, except that the class we need to import will change: -```js -import { UltraHonkBackend, UltraHonkVerifier as Verifier } from '@noir-lang/backend_barretenberg'; -``` -The backend will then be instantiated as such: -```js -const backend = new UltraHonkBackend(circuit); -``` -Then all the commands to prove and verify your circuit will be same. - -The only feature currently unsupported with UltraHonk are [recursive proofs](../explainers/explainer-recursion.md). \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.34.0/explainers/cspell.json b/docs/versioned_docs/version-v0.34.0/explainers/cspell.json deleted file mode 100644 index c60b0a597b1..00000000000 --- a/docs/versioned_docs/version-v0.34.0/explainers/cspell.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "words": [ - "Cryptdoku" - ] -} diff --git a/docs/versioned_docs/version-v0.34.0/explainers/explainer-oracle.md b/docs/versioned_docs/version-v0.34.0/explainers/explainer-oracle.md deleted file mode 100644 index 821e1f95c04..00000000000 --- a/docs/versioned_docs/version-v0.34.0/explainers/explainer-oracle.md +++ /dev/null @@ -1,57 +0,0 @@ ---- -title: Oracles -description: This guide provides an in-depth understanding of how Oracles work in Noir programming. Learn how to use outside calculations in your programs, constrain oracles, and understand their uses and limitations. -keywords: - - Noir Programming - - Oracles - - JSON-RPC - - Foreign Call Handlers - - Constrained Functions - - Blockchain Programming -sidebar_position: 1 ---- - -If you've seen "The Matrix" you may recall "The Oracle" as Gloria Foster smoking cigarettes and baking cookies. While she appears to "know things", she is actually providing a calculation of a pre-determined future. Noir Oracles are similar, in a way. They don't calculate the future (yet), but they allow you to use outside calculations in your programs. - -![matrix oracle prediction](@site/static/img/memes/matrix_oracle.jpeg) - -A Noir program is usually self-contained. You can pass certain inputs to it, and it will generate a deterministic output for those inputs. But what if you wanted to defer some calculation to an outside process or source? - -Oracles are functions that provide this feature. - -## Use cases - -An example usage for Oracles is proving something on-chain. For example, proving that the ETH-USDC quote was below a certain target at a certain block time. Or even making more complex proofs like proving the ownership of an NFT as an anonymous login method. - -Another interesting use case is to defer expensive calculations to be made outside of the Noir program, and then constraining the result; similar to the use of [unconstrained functions](../noir/concepts//unconstrained.md). - -In short, anything that can be constrained in a Noir program but needs to be fetched from an external source is a great candidate to be used in oracles. - -## Constraining oracles - -Just like in The Matrix, Oracles are powerful. But with great power, comes great responsibility. Just because you're using them in a Noir program doesn't mean they're true. Noir has no superpowers. If you want to prove that Portugal won the Euro Cup 2016, you're still relying on potentially untrusted information. - -To give a concrete example, Alice wants to login to the [NounsDAO](https://nouns.wtf/) forum with her username "noir_nouner" by proving she owns a noun without revealing her ethereum address. Her Noir program could have an oracle call like this: - -```rust -#[oracle(getNoun)] -unconstrained fn get_noun(address: Field) -> Field -``` - -This oracle could naively resolve with the number of Nouns she possesses. However, it is useless as a trusted source, as the oracle could resolve to anything Alice wants. In order to make this oracle call actually useful, Alice would need to constrain the response from the oracle, by proving her address and the noun count belongs to the state tree of the contract. - -In short, **Oracles don't prove anything. Your Noir program does.** - -:::danger - -If you don't constrain the return of your oracle, you could be clearly opening an attack vector on your Noir program. Make double-triple sure that the return of an oracle call is constrained! - -::: - -## How to use Oracles - -On CLI, Nargo resolves oracles by making JSON RPC calls, which means it would require an RPC node to be running. - -In JavaScript, NoirJS accepts and resolves arbitrary call handlers (that is, not limited to JSON) as long as they match the expected types the developer defines. Refer to [Foreign Call Handler](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) to learn more about NoirJS's call handling. - -If you want to build using oracles, follow through to the [oracle guide](../how_to/how-to-oracles.md) for a simple example on how to do that. diff --git a/docs/versioned_docs/version-v0.34.0/explainers/explainer-recursion.md b/docs/versioned_docs/version-v0.34.0/explainers/explainer-recursion.md deleted file mode 100644 index df8529ef4e0..00000000000 --- a/docs/versioned_docs/version-v0.34.0/explainers/explainer-recursion.md +++ /dev/null @@ -1,176 +0,0 @@ ---- -title: Recursive proofs -description: Explore the concept of recursive proofs in Zero-Knowledge programming. Understand how recursion works in Noir, a language for writing smart contracts on the EVM blockchain. Learn through practical examples like Alice and Bob's guessing game, Charlie's recursive merkle tree, and Daniel's reusable components. Discover how to use recursive proofs to optimize computational resources and improve efficiency. - -keywords: - [ - "Recursive Proofs", - "Zero-Knowledge Programming", - "Noir", - "EVM Blockchain", - "Smart Contracts", - "Recursion in Noir", - "Alice and Bob Guessing Game", - "Recursive Merkle Tree", - "Reusable Components", - "Optimizing Computational Resources", - "Improving Efficiency", - "Verification Key", - "Aggregation", - "Recursive zkSNARK schemes", - "PLONK", - "Proving and Verification Keys" - ] -sidebar_position: 1 -pagination_next: how_to/how-to-recursion ---- - -In programming, we tend to think of recursion as something calling itself. A classic example would be the calculation of the factorial of a number: - -```js -function factorial(n) { - if (n === 0 || n === 1) { - return 1; - } else { - return n * factorial(n - 1); - } -} -``` - -In this case, while `n` is not `1`, this function will keep calling itself until it hits the base case, bubbling up the result on the call stack: - -```md - Is `n` 1? <--------- - /\ / - / \ n = n -1 - / \ / - Yes No -------- -``` - -In Zero-Knowledge, recursion has some similarities. - -It is not a Noir function calling itself, but a proof being used as an input to another circuit. In short, you verify one proof *inside* another proof, returning the proof that both proofs are valid. - -This means that, given enough computational resources, you can prove the correctness of any arbitrary number of proofs in a single proof. This could be useful to design state channels (for which a common example would be [Bitcoin's Lightning Network](https://en.wikipedia.org/wiki/Lightning_Network)), to save on gas costs by settling one proof on-chain, or simply to make business logic less dependent on a consensus mechanism. - -## Examples - -Let us look at some of these examples - -### Alice and Bob - Guessing game - -Alice and Bob are friends, and they like guessing games. They want to play a guessing game online, but for that, they need a trusted third-party that knows both of their secrets and finishes the game once someone wins. - -So, they use zero-knowledge proofs. Alice tries to guess Bob's number, and Bob will generate a ZK proof stating whether she succeeded or failed. - -This ZK proof can go on a smart contract, revealing the winner and even giving prizes. However, this means every turn needs to be verified on-chain. This incurs some cost and waiting time that may simply make the game too expensive or time-consuming to be worth it. - -As a solution, Alice proposes the following: "what if Bob generates his proof, and instead of sending it on-chain, I verify it *within* my own proof before playing my own turn?". - -She can then generate a proof that she verified his proof, and so on. - -```md - Did you fail? <-------------------------- - / \ / - / \ n = n -1 - / \ / - Yes No / - | | / - | | / - | You win / - | / - | / -Generate proof of that / - + / - my own guess ---------------- -``` - -### Charlie - Recursive merkle tree - -Charlie is a concerned citizen, and wants to be sure his vote in an election is accounted for. He votes with a ZK proof, but he has no way of knowing that his ZK proof was included in the total vote count! - -If the vote collector puts all of the votes into a [Merkle tree](https://en.wikipedia.org/wiki/Merkle_tree), everyone can prove the verification of two proofs within one proof, as such: - -```md - abcd - __________|______________ - | | - ab cd - _____|_____ ______|______ - | | | | - alice bob charlie daniel -``` - -Doing this recursively allows us to arrive on a final proof `abcd` which if true, verifies the correctness of all the votes. - -### Daniel - Reusable components - -Daniel has a big circuit and a big headache. A part of his circuit is a setup phase that finishes with some assertions that need to be made. But that section alone takes most of the proving time, and is largely independent of the rest of the circuit. - -He might find it more efficient to generate a proof for that setup phase separately, and verify that proof recursively in the actual business logic section of his circuit. This will allow for parallelization of both proofs, which results in a considerable speedup. - -## What params do I need - -As you can see in the [recursion reference](noir/standard_library/recursion.mdx), a simple recursive proof requires: - -- The proof to verify -- The Verification Key of the circuit that generated the proof -- A hash of this verification key, as it's needed for some backends -- The public inputs for the proof - -:::info - -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. - -So, taking the example of Alice and Bob and their guessing game: - -- Alice makes her guess. Her proof is *not* recursive: it doesn't verify any proof within it! It's just a standard `assert(x != y)` circuit -- Bob verifies Alice's proof and makes his own guess. In this circuit, he doesn't exactly *prove* the verification of Alice's proof. Instead, he *aggregates* his proof to Alice's proof. The actual verification is done when the full proof is verified, for example when using `nargo verify` or through the verifier smart contract. - -We can imagine recursive proofs a [relay race](https://en.wikipedia.org/wiki/Relay_race). The first runner doesn't have to receive the baton from anyone else, as he/she already starts with it. But when his/her turn is over, the next runner needs to receive it, run a bit more, and pass it along. Even though every runner could theoretically verify the baton mid-run (why not? 🏃🔍), only at the end of the race does the referee verify that the whole race is valid. - -::: - -## Some architecture - -As with everything in computer science, there's no one-size-fits all. But there are some patterns that could help understanding and implementing them. To give three examples: - -### Adding some logic to a proof verification - -This would be an approach for something like our guessing game, where proofs are sent back and forth and are verified by each opponent. This circuit would be divided in two sections: - -- A `recursive verification` section, which would be just the call to `std::verify_proof`, and that would be skipped on the first move (since there's no proof to verify) -- A `guessing` section, which is basically the logic part where the actual guessing happens - -In such a situation, and assuming Alice is first, she would skip the first part and try to guess Bob's number. Bob would then verify her proof on the first section of his run, and try to guess Alice's number on the second part, and so on. - -### Aggregating proofs - -In some one-way interaction situations, recursion would allow for aggregation of simple proofs that don't need to be immediately verified on-chain or elsewhere. - -To give a practical example, a barman wouldn't need to verify a "proof-of-age" on-chain every time he serves alcohol to a customer. Instead, the architecture would comprise two circuits: - -- A `main`, non-recursive circuit with some logic -- A `recursive` circuit meant to verify two proofs in one proof - -The customer's proofs would be intermediate, and made on their phones, and the barman could just verify them locally. He would then aggregate them into a final proof sent on-chain (or elsewhere) at the end of the day. - -### Recursively verifying different circuits - -Nothing prevents you from verifying different circuits in a recursive proof, for example: - -- A `circuit1` circuit -- A `circuit2` circuit -- A `recursive` circuit - -In this example, a regulator could verify that taxes were paid for a specific purchase by aggregating both a `payer` circuit (proving that a purchase was made and taxes were paid), and a `receipt` circuit (proving that the payment was received) - -## How fast is it - -At the time of writing, verifying recursive proofs is surprisingly fast. This is because most of the time is spent on generating the verification key that will be used to generate the next proof. So you are able to cache the verification key and reuse it later. - -Currently, Noir JS packages don't expose the functionality of loading proving and verification keys, but that feature exists in the underlying `bb.js` package. - -## How can I try it - -Learn more about using recursion in Nargo and NoirJS in the [how-to guide](../how_to/how-to-recursion.md) and see a full example in [noir-examples](https://github.com/noir-lang/noir-examples). diff --git a/docs/versioned_docs/version-v0.34.0/explainers/explainer-writing-noir.md b/docs/versioned_docs/version-v0.34.0/explainers/explainer-writing-noir.md deleted file mode 100644 index 3ef6e014a2f..00000000000 --- a/docs/versioned_docs/version-v0.34.0/explainers/explainer-writing-noir.md +++ /dev/null @@ -1,177 +0,0 @@ ---- -title: Writing Performant Noir -description: Understand new considerations when writing Noir -keywords: [Noir, programming, rust] -tags: [Optimization] -sidebar_position: 0 ---- - - -This article intends to set you up with key concepts essential for writing more viable applications that use zero knowledge proofs, namely around efficient circuits. - -## Context - 'Efficient' is subjective - -When writing a web application for a performant computer with high-speed internet connection, writing efficient code sometimes is seen as an afterthought only if needed. Large multiplications running at the innermost of nested loops may not even be on a dev's radar. -When writing firmware for a battery-powered microcontroller, you think of cpu cycles as rations to keep within a product's power budget. - -> Code is written to create applications that perform specific tasks within specific constraints - -And these constraints differ depending on where the compiled code is execute. - -### The Ethereum Virtual Machine (EVM) - -In scenarios where extremely low gas costs are required for an Ethereum application to be viable/competitive, Ethereum smart contract developers get into what is colloquially known as: "*gas golfing*". Finding the lowest execution cost of their compiled code (EVM bytecode) to achieve a specific task. - -The equivalent optimization task when writing zk circuits is affectionately referred to as "*gate golfing*", finding the lowest gate representation of the compiled Noir code. - -### Coding for circuits - a paradigm shift - -In zero knowledge cryptography, code is compiled to "circuits" consisting of arithmetic gates, and gate count is the significant cost. Depending on the proving system this is linearly proportionate to proving time, and so from a product point this should be kept as low as possible. - -Whilst writing efficient code for web apps and Solidity has a few key differences, writing efficient circuits have a different set of considerations. It is a bit of a paradigm shift, like writing code for GPUs for the first time... - -For example, drawing a circle at (0, 0) of radius `r`: -- For a single CPU thread, -``` -for theta in 0..2*pi { - let x = r * cos(theta); - let y = r * sin(theta); - draw(x, y); -} // note: would do 0 - pi/2 and draw +ve/-ve x and y. -``` - -- For GPUs (simultaneous parallel calls with x, y across image), -``` -if (x^2 + y^2 = r^2) { - draw(x, y); -} -``` - -([Related](https://www.youtube.com/watch?v=-P28LKWTzrI)) - -Whilst this CPU -> GPU does not translate to circuits exactly, it is intended to exemplify the difference in intuition when coding for different machine capabilities/constraints. - -### Context Takeaway - -For those coming from a primarily web app background, this article will explain what you need to consider when writing circuits. Furthermore, for those experienced writing efficient machine code, prepare to shift what you think is efficient 😬 - -## Translating from Rust - -For some applications using Noir, existing code might be a convenient starting point to then proceed to optimize the gate count of. - -:::note -Many valuable functions and algorithms have been written in more established languages (C/C++), and converted to modern ones (like Rust). -::: - -Fortunately for Noir developers, when needing a particular function a Rust implementation can be readily compiled into Noir with some key changes. While the compiler does a decent amount of optimizations, it won't be able to change code that has been optimized for clock-cycles into code optimized for arithmetic gates. - -A few things to do when converting Rust code to Noir: -- `println!` is not a macro, use `println` function (same for `assert_eq`) -- No early `return` in function. Use constrain via assertion instead -- No passing by reference. Remove `&` operator to pass by value (copy) -- No boolean operators (`&&`, `||`). Use bitwise operators (`&`, `|`) with boolean values -- No type `usize`. Use types `u8`, `u32`, `u64`, ... -- `main` return must be public, `pub` -- No `const`, use `global` -- Noir's LSP is your friend, so error message should be informative enough to resolve syntax issues. - -## Writing efficient Noir for performant products - -The following points help refine our understanding over time. - -:::note -A Noir program makes a statement that can be verified. -::: - -It compiles to a structure that represents the calculation, and can assert results within the calculation at any stage (via the `constrain` keyword). - -A Noir program compiles to an Abstract Circuit Intermediate Representation which is: - - Conceptually a tree structure - - Leaves (inputs) are the `Field` type - - Nodes contain arithmetic operations to combine them (gates) - - The root is the final result (return value) - -:::tip -The command `nargo info` shows the programs circuit size, and is useful to compare the value of changes made. -You can dig deeper and use the `--print-acir` param to take a closer look at individual ACIR opcodes, and the proving backend to see its gate count (eg for barretenberg, `bb gates -b ./target/program.json`). -::: - -### Use the `Field` type - -Since the native type of values in circuits are `Field`s, using them for variables in Noir means less gates converting them under the hood. -Some things to be mindful of when using a Field type for a regular integer value: -- A variable of type `Field` can be cast `as` an integer type (eg `u8`, `u64`) - - Note: this retains only the bits of the integer type. Eg a Field value of 260 as a `u8` becomes 4 -- For Field types arithmetic operations meaningfully overflow/underflow, yet for integer types they are checked according to their size -- Comparisons and bitwise operations do not exist for `Field`s, cast to an appropriately sized integer type when you need to - -:::tip -Where possible, use `Field` type for values. Using smaller value types, and bit-packing strategies, will result in MORE gates -::: - - -### Use Arithmetic over non-arithmetic operations - -Since circuits are made of arithmetic gates, the cost of arithmetic operations tends to be one gate. Whereas for procedural code, they represent several clock cycles. - -Inversely, non-arithmetic operators are achieved with multiple gates, vs 1 clock cycle for procedural code. - -| (cost\op) | arithmetic
(`*`, `+`) | bit-wise ops
(eg `<`, `\|`, `>>`) | -| - | - | - | -| **cycles** | 10+ | 1 | -| **gates** | 1 | 10+ | - -Bit-wise operations (e.g. bit shifts `<<` and `>>`), albeit commonly used in general programming and especially for clock cycle optimizations, are on the contrary expensive in gates when performed within circuits. - -Translate away from bit shifts when writing constrained functions for the best performance. - -On the flip side, feel free to use bit shifts in unconstrained functions and tests if necessary, as they are executed outside of circuits and does not induce performance hits. - -### Use static over dynamic values - -Another general theme that manifests in different ways is that static reads are represented with less gates than dynamic ones. - -Reading from read-only memory (ROM) adds less gates than random-access memory (RAM), 2 vs ~3.25 due to the additional bounds checks. Arrays of fixed length (albeit used at a lower capacity), will generate less gates than dynamic storage. - -Related to this, if an index used to access an array is not known at compile time (ie unknown until run time), then ROM will be converted to RAM, expanding the gate count. - -:::tip -Use arrays and indices that are known at compile time where possible. -Using `assert_constant(i);` before an index, `i`, is used in an array will give a compile error if `i` is NOT known at compile time. -::: - -### Leverage unconstrained execution - -Constrained verification can leverage unconstrained execution, this is especially useful for operations that are represented by many gates. -Use an [unconstrained function](../noir/concepts/unconstrained.md) to perform gate-heavy calculations, then verify and constrain the result. - -Eg division generates more gates than multiplication, so calculating the quotient in an unconstrained function then constraining the product for the quotient and divisor (+ any remainder) equals the dividend will be more efficient. - -Use ` if is_unconstrained() { /`, to conditionally execute code if being called in an unconstrained vs constrained way. - -## Advanced - -Unless you're well into the depth of gate optimization, this advanced section can be ignored. - -### Combine arithmetic operations - -A Noir program can be honed further by combining arithmetic operators in a way that makes the most of each constraint of the backend proving system. This is in scenarios where the backend might not be doing this perfectly. - -Eg Barretenberg backend (current default for Noir) is a width-4 PLONKish constraint system -$ w_1*w_2*q_m + w_1*q_1 + w_2*q_2 + w_3*q_3 + w_4*q_4 + q_c = 0 $ - -Here we see there is one occurrence of witness 1 and 2 ($w_1$, $w_2$) being multiplied together, with addition to witnesses 1-4 ($w_1$ .. $w_4$) multiplied by 4 corresponding circuit constants ($q_1$ .. $q_4$) (plus a final circuit constant, $q_c$). - -Use `nargo info --print-acir`, to inspect the ACIR opcodes (and the proving system for gates), and it may present opportunities to amend the order of operations and reduce the number of constraints. - -#### Variable as witness vs expression - -If you've come this far and really know what you're doing at the equation level, a temporary lever (that will become unnecessary/useless over time) is: `std::as_witness`. This informs the compiler to save a variable as a witness not an expression. - -The compiler will mostly be correct and optimal, but this may help some near term edge cases that are yet to optimize. -Note: When used incorrectly it will create **less** efficient circuits (higher gate count). - -## References -- Guillaume's ["`Cryptdoku`" talk](https://www.youtube.com/watch?v=MrQyzuogxgg) (Jun'23) -- Tips from Tom, Jake and Zac. -- [Idiomatic Noir](https://www.vlayer.xyz/blog/idiomatic-noir-part-1-collections) blog post diff --git a/docs/versioned_docs/version-v0.34.0/getting_started/_category_.json b/docs/versioned_docs/version-v0.34.0/getting_started/_category_.json deleted file mode 100644 index 5d694210bbf..00000000000 --- a/docs/versioned_docs/version-v0.34.0/getting_started/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "position": 0, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.34.0/getting_started/backend/_category_.json b/docs/versioned_docs/version-v0.34.0/getting_started/backend/_category_.json deleted file mode 100644 index b82e92beb0c..00000000000 --- a/docs/versioned_docs/version-v0.34.0/getting_started/backend/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "position": 1, - "label": "Install Proving Backend", - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.34.0/getting_started/backend/index.md b/docs/versioned_docs/version-v0.34.0/getting_started/backend/index.md deleted file mode 100644 index 7192d954877..00000000000 --- a/docs/versioned_docs/version-v0.34.0/getting_started/backend/index.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: Proving Backend Installation -description: Proving backends offer command line tools for proving and verifying Noir programs. This page describes how to install `bb` as an example. -keywords: [ - Proving - Backend - Barretenberg - bb - bbup - Installation - Terminal - Command - CLI - Version -] -pagination_next: getting_started/hello_noir/index ---- - -Proving backends each provide their own tools for working with Noir programs, providing functionality like proof generation, proof verification, and verifier smart contract generation. - -For the latest information on tooling provided by each proving backend, installation instructions, Noir version compatibility... you may refer to the proving backends' own documentation. - -You can find the full list of proving backends compatible with Noir in [Awesome Noir](https://github.com/noir-lang/awesome-noir/?tab=readme-ov-file#proving-backends). - -## Example: Installing `bb` - -`bb` is the CLI tool provided by the [Barretenberg proving backend](https://github.com/AztecProtocol/barretenberg) developed by Aztec Labs. - -You can find the instructions for installation in [`bb`'s documentation](https://github.com/AztecProtocol/aztec-packages/blob/master/barretenberg/cpp/src/barretenberg/bb/readme.md#installation). - -Once installed, we are ready to start working on [our first Noir program](../hello_noir/index.md). diff --git a/docs/versioned_docs/version-v0.34.0/getting_started/hello_noir/_category_.json b/docs/versioned_docs/version-v0.34.0/getting_started/hello_noir/_category_.json deleted file mode 100644 index 976a2325de0..00000000000 --- a/docs/versioned_docs/version-v0.34.0/getting_started/hello_noir/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "position": 2, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.34.0/getting_started/hello_noir/index.md b/docs/versioned_docs/version-v0.34.0/getting_started/hello_noir/index.md deleted file mode 100644 index 6760e54aad1..00000000000 --- a/docs/versioned_docs/version-v0.34.0/getting_started/hello_noir/index.md +++ /dev/null @@ -1,157 +0,0 @@ ---- -title: Creating 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, - ] -sidebar_position: 1 - ---- - -Now that we have installed Nargo and a proving backend, it is time to make our first hello world program! - -### 1. Create a new project directory - -Noir code can live anywhere on your computer. Let us create a _projects_ folder in the home -directory to house our first Noir program. - -Create the directory and change directory into it by running: - -```sh -mkdir ~/projects -cd ~/projects -``` - -## Nargo - -Nargo provides the ability to initiate and execute Noir projects. Read the [Nargo installation](../installation/index.md) section to learn more about Nargo and how to install it. - -### 2. Create a new Noir project - -Now that we are in the projects directory, create a new Nargo project by running: - -```sh -nargo new hello_world -``` - -`hello_world` can be any arbitrary project name, we are simply using `hello_world` for demonstration. - -In production, it is common practice to name the project folder, `circuits`, for clarity amongst other folders in the codebase (like: `contracts`, `scripts`, and `test`). - -A `hello_world` folder would be created. Similar to Rust, the folder houses _src/main.nr_ and -_Nargo.toml_ which contain 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](../../noir/concepts/data_types/index.md) 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](../../noir/concepts/comments.md) chapter. - -### 3. 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 -``` - -A _Prover.toml_ file will be generated in your project directory, to allow specifying input values to the program. - -### 4. Execute the Noir program - -Now that the project is set up, we can execute our Noir program. - -Fill in input values for execution in the _Prover.toml_ file. For example: - -```toml -x = "1" -y = "2" -``` - -Execute your Noir program: - -```sh -nargo execute witness-name -``` - -The witness corresponding to this execution will then be written to the file `./target/witness-name.gz`. - -The command also automatically compiles your Noir program if it was not already / was edited, which you may notice the compiled artifacts being written to the file `./target/hello_world.json`. - -## Proving Backend - -Proving backends provide the ability to generate and verify proofs of executing Noir programs, following Noir's tooling that compiles and executes the programs. Read the [proving backend installation](../backend/index.md) section to learn more about proving backends and how to install them. - -Barretenberg is used as an example here to demonstrate how proving and verifying could be implemented and used. Read the [`bb` installation](../backend/index.md#example-installing-bb) section for how to install Barretenberg's CLI tool; refer to [`bb`'s documentation](https://github.com/AztecProtocol/aztec-packages/blob/master/barretenberg/cpp/src/barretenberg/bb/readme.md) for full details about the tool. - -### 5. Prove an execution of the Noir program - -Using Barretenberg as an example, prove the valid execution of your Noir program running: - -```sh -bb prove -b ./target/hello_world.json -w ./target/witness-name.gz -o ./target/proof -``` - -The proof generated will then be written to the file `./target/proof`. - -:::tip -Since the params for `nargo` and `bb` often specify multiple filenames to read from or write to, remember to check each command is referring to the desired filenames. -Or for greater certainty, delete the target folder and go through each step again (compile, witness, prove, ...) to ensure files generated in past commands are being referenced in future ones. -::: - -### 6. Verify the execution proof - -Once a proof is generated, we can verify correct execution of our Noir program by verifying the proof file. - -Using Barretenberg as an example, compute the verification key for the Noir program by running: - -```sh -bb write_vk -b ./target/hello_world.json -o ./target/vk -``` - -And verify your proof by running: - -```sh -bb verify -k ./target/vk -p ./target/proof -``` - -If successful, the verification will complete in silence; if unsuccessful, the command will trigger logging of the corresponding error. - -Congratulations, you have now created and verified a proof for your very first Noir program! - -In the [next section](./project_breakdown.md), we will go into more detail on each step performed. diff --git a/docs/versioned_docs/version-v0.34.0/getting_started/hello_noir/project_breakdown.md b/docs/versioned_docs/version-v0.34.0/getting_started/hello_noir/project_breakdown.md deleted file mode 100644 index 96e653f6c08..00000000000 --- a/docs/versioned_docs/version-v0.34.0/getting_started/hello_noir/project_breakdown.md +++ /dev/null @@ -1,159 +0,0 @@ ---- -title: Project Breakdown -description: - Learn about the anatomy of a Nargo project, including the purpose of the Prover TOML - file, and how to prove and verify your program. -keywords: - [Nargo, Nargo project, Prover.toml, proof verification, private asset transfer] -sidebar_position: 2 ---- - -This section breaks down our hello world program from 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 - - 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. - -### 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 = "noir_starter" -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](../../noir/modules_packages_crates/workspaces.md) will look a bit different. For example: - -```toml -[workspace] -members = ["crates/a", "crates/b"] -default-member = "crates/a" -``` - -#### Package section - -The package section defines 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) -- `expression_width` (optional) - Sets the default backend expression width. This field will override the default backend expression width specified by the Noir compiler (currently set to width 4). - -#### Dependencies section - -This is where you will specify any dependencies for your project. See the [Dependencies page](../../noir/modules_packages_crates/dependencies.md) 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 that the condition to be satisfied (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 the inputs to the Noir program (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 execute` is executed, nargo will execute the Noir program using the inputs specified in `Prover.toml`, aborting if it finds that these do not satisfy the constraints defined by `main`. In this example, `x` and `y` must satisfy the inequality constraint `assert(x != y)`. - -If an output name is specified such as `nargo execute foo`, the witness generated by this execution will be written to `./target/foo.gz`. This can then be used to generate a proof of the execution. - -#### 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 execution by using the `--prover-name` or `-p` flags. - -This command looks for proof inputs in the default **Prover.toml** and generates the witness and saves it at `./target/foo.gz`: - -```bash -nargo execute foo -``` - -This command looks for proof inputs in the custom **OtherProver.toml** and generates the witness and saves it at `./target/bar.gz`: - -```bash -nargo execute -p OtherProver bar -``` - -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.34.0/getting_started/installation/_category_.json b/docs/versioned_docs/version-v0.34.0/getting_started/installation/_category_.json deleted file mode 100644 index 0c02fb5d4d7..00000000000 --- a/docs/versioned_docs/version-v0.34.0/getting_started/installation/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "position": 0, - "label": "Install Nargo", - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.34.0/getting_started/installation/index.md b/docs/versioned_docs/version-v0.34.0/getting_started/installation/index.md deleted file mode 100644 index 53ea9c7891c..00000000000 --- a/docs/versioned_docs/version-v0.34.0/getting_started/installation/index.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -title: Nargo Installation -description: - nargo is a command line tool for interacting with Noir programs. This page is a quick guide on how to install Nargo through the most common and easy method, noirup -keywords: [ - Nargo - Noir - Rust - Cargo - Noirup - Installation - Terminal Commands - Version Check - Nightlies - Specific Versions - Branches - Noirup Repository -] -pagination_next: getting_started/hello_noir/index ---- - -`nargo` is a tool for working with Noir programs on the CLI, providing you with the ability to start new projects, compile, execute and test Noir programs from the terminal. - -The name is inspired by Rust's package manager `cargo`; and similar to Rust's `rustup`, Noir also has an easy installation script `noirup`. - -## Installing Noirup - -Open a terminal on your machine, and write: - -```bash -curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash -``` - -Close the terminal, open another one, and run - -```bash -noirup -``` - -Done. That's it. 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. - -Now we're ready to start working on [our first Noir program!](../hello_noir/index.md) diff --git a/docs/versioned_docs/version-v0.34.0/getting_started/installation/other_install_methods.md b/docs/versioned_docs/version-v0.34.0/getting_started/installation/other_install_methods.md deleted file mode 100644 index 3634723562b..00000000000 --- a/docs/versioned_docs/version-v0.34.0/getting_started/installation/other_install_methods.md +++ /dev/null @@ -1,102 +0,0 @@ ---- -title: Alternative Installations -description: There are different ways to install Nargo, the one-stop shop and command-line tool for developing Noir programs. This guide explains how to specify which version to install when using noirup, and using WSL for windows. -keywords: [ - Installation - Nargo - Noirup - Binaries - Compiling from Source - WSL for Windows - macOS - Linux - Nix - Direnv - Uninstalling Nargo - ] -sidebar_position: 1 ---- - -## Encouraged Installation Method: Noirup - -Noirup is the endorsed method for installing Nargo, streamlining the process of fetching binaries or compiling from source. It supports a range of options to cater to your specific needs, from nightly builds and specific versions to compiling from various sources. - -### Installing Noirup - -First, ensure you have `noirup` installed: - -```sh -curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash -``` - -### Fetching Binaries - -With `noirup`, you can easily switch between different Nargo versions, including nightly builds: - -- **Nightly Version**: Install the latest nightly build. - - ```sh - noirup --version nightly - ``` - -- **Specific Version**: Install a specific version of Nargo. - ```sh - noirup --version - ``` - -### Compiling from Source - -`noirup` also enables compiling Nargo from various sources: - -- **From a Specific Branch**: Install from the latest commit on a branch. - - ```sh - noirup --branch - ``` - -- **From a Fork**: Install from the main branch of a fork. - - ```sh - noirup --repo - ``` - -- **From a Specific Branch in a Fork**: Install from a specific branch in a fork. - - ```sh - noirup --repo --branch - ``` - -- **From a Specific Pull Request**: Install from a specific PR. - - ```sh - noirup --pr - ``` - -- **From a Specific Commit**: Install from a specific commit. - - ```sh - noirup -C - ``` - -- **From Local Source**: Compile and install from a local directory. - ```sh - noirup --path ./path/to/local/source - ``` - -## Installation on Windows - -The default backend for Noir (Barretenberg) doesn't provide Windows binaries at this time. For that reason, Noir cannot be installed natively. 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](#encouraged-installation-method-noirup). - -## Uninstalling Nargo - -If you installed Nargo with `noirup`, you can uninstall Nargo by removing the files in `~/.nargo`, `~/nargo`, and `~/noir_cache`. This ensures that all installed binaries, configurations, and cache related to Nargo are fully removed from your system. - -```bash -rm -r ~/.nargo -rm -r ~/nargo -rm -r ~/noir_cache -``` diff --git a/docs/versioned_docs/version-v0.34.0/how_to/_category_.json b/docs/versioned_docs/version-v0.34.0/how_to/_category_.json deleted file mode 100644 index 23b560f610b..00000000000 --- a/docs/versioned_docs/version-v0.34.0/how_to/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "position": 1, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.34.0/how_to/debugger/_category_.json b/docs/versioned_docs/version-v0.34.0/how_to/debugger/_category_.json deleted file mode 100644 index cc2cbb1c253..00000000000 --- a/docs/versioned_docs/version-v0.34.0/how_to/debugger/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "Debugging", - "position": 5, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.34.0/how_to/debugger/debugging_with_the_repl.md b/docs/versioned_docs/version-v0.34.0/how_to/debugger/debugging_with_the_repl.md deleted file mode 100644 index 1d64dae3f37..00000000000 --- a/docs/versioned_docs/version-v0.34.0/how_to/debugger/debugging_with_the_repl.md +++ /dev/null @@ -1,164 +0,0 @@ ---- -title: Using the REPL Debugger -description: - Step-by-step guide on how to debug your Noir circuits with the REPL Debugger. -keywords: - [ - Nargo, - Noir CLI, - Noir Debugger, - REPL, - ] -sidebar_position: 1 ---- - -#### Pre-requisites - -In order to use the REPL debugger, first you need to install recent enough versions of Nargo and vscode-noir. - -## Debugging a simple circuit - -Let's debug a simple circuit: - -```rust -fn main(x : Field, y : pub Field) { - assert(x != y); -} -``` - -To start the REPL debugger, using a terminal, go to a Noir circuit's home directory. Then: - -`$ nargo debug` - -You should be seeing this in your terminal: - -``` -[main] Starting debugger -At ~/noir-examples/recursion/circuits/main/src/main.nr:1:9 - 1 -> fn main(x : Field, y : pub Field) { - 2 assert(x != y); - 3 } -> -``` - -The debugger displays the current Noir code location, and it is now waiting for us to drive it. - -Let's first take a look at the available commands. For that we'll use the `help` command. - -``` -> help -Available commands: - - opcodes display ACIR opcodes - into step into to the next opcode - next step until a new source location is reached - out step until a new source location is reached - and the current stack frame is finished - break LOCATION:OpcodeLocation add a breakpoint at an opcode location - over step until a new source location is reached - without diving into function calls - restart restart the debugging session - delete LOCATION:OpcodeLocation delete breakpoint at an opcode location - witness show witness map - witness index:u32 display a single witness from the witness map - witness index:u32 value:String update a witness with the given value - memset index:usize value:String update a memory cell with the given - value - continue continue execution until the end of the - program - vars show variable values available at this point - in execution - stacktrace display the current stack trace - memory show memory (valid when executing unconstrained code) - step step to the next ACIR opcode - -Other commands: - - help Show this help message - quit Quit repl - -``` - -Some commands operate only for unconstrained functions, such as `memory` and `memset`. If you try to use them while execution is paused at an ACIR opcode, the debugger will simply inform you that you are not executing unconstrained code: - -``` -> memory -Unconstrained VM memory not available -> -``` - -Before continuing, we can take a look at the initial witness map: - -``` -> witness -_0 = 1 -_1 = 2 -> -``` - -Cool, since `x==1`, `y==2`, and we want to check that `x != y`, our circuit should succeed. At this point we could intervene and use the witness setter command to change one of the witnesses. Let's set `y=3`, then back to 2, so we don't affect the expected result: - -``` -> witness -_0 = 1 -_1 = 2 -> witness 1 3 -_1 = 3 -> witness -_0 = 1 -_1 = 3 -> witness 1 2 -_1 = 2 -> witness -_0 = 1 -_1 = 2 -> -``` - -Now we can inspect the current state of local variables. For that we use the `vars` command. - -``` -> vars -> -``` - -We currently have no vars in context, since we are at the entry point of the program. Let's use `next` to execute until the next point in the program. - -``` -> vars -> next -At ~/noir-examples/recursion/circuits/main/src/main.nr:1:20 - 1 -> fn main(x : Field, y : pub Field) { - 2 assert(x != y); - 3 } -> vars -x:Field = 0x01 -``` - -As a result of stepping, the variable `x`, whose initial value comes from the witness map, is now in context and returned by `vars`. - -``` -> next - 1 fn main(x : Field, y : pub Field) { - 2 -> assert(x != y); - 3 } -> vars -y:Field = 0x02 -x:Field = 0x01 -``` - -Stepping again we can finally see both variables and their values. And now we can see that the next assertion should succeed. - -Let's continue to the end: - -``` -> continue -(Continuing execution...) -Finished execution -> q -[main] Circuit witness successfully solved -``` - -Upon quitting the debugger after a solved circuit, the resulting circuit witness gets saved, equivalent to what would happen if we had run the same circuit with `nargo execute`. - -We just went through the basics of debugging using Noir REPL debugger. For a comprehensive reference, check out [the reference page](../../reference/debugger/debugger_repl.md). diff --git a/docs/versioned_docs/version-v0.34.0/how_to/debugger/debugging_with_vs_code.md b/docs/versioned_docs/version-v0.34.0/how_to/debugger/debugging_with_vs_code.md deleted file mode 100644 index a5858c1a5eb..00000000000 --- a/docs/versioned_docs/version-v0.34.0/how_to/debugger/debugging_with_vs_code.md +++ /dev/null @@ -1,68 +0,0 @@ ---- -title: Using the VS Code Debugger -description: - Step by step guide on how to debug your Noir circuits with the VS Code Debugger configuration and features. -keywords: - [ - Nargo, - Noir CLI, - Noir Debugger, - VS Code, - IDE, - ] -sidebar_position: 0 ---- - -This guide will show you how to use VS Code with the vscode-noir extension to debug a Noir project. - -#### Pre-requisites - -- Nargo -- vscode-noir -- A Noir project with a `Nargo.toml`, `Prover.toml` and at least one Noir (`.nr`) containing an entry point function (typically `main`). - -## Running the debugger - -The easiest way to start debugging is to open the file you want to debug, and press `F5`. This will cause the debugger to launch, using your `Prover.toml` file as input. - -You should see something like this: - -![Debugger launched](@site/static/img/debugger/1-started.png) - -Let's inspect the state of the program. For that, we open VS Code's _Debug pane_. Look for this icon: - -![Debug pane icon](@site/static/img/debugger/2-icon.png) - -You will now see two categories of variables: Locals and Witness Map. - -![Debug pane expanded](@site/static/img/debugger/3-debug-pane.png) - -1. **Locals**: variables of your program. At this point in execution this section is empty, but as we step through the code it will get populated by `x`, `result`, `digest`, etc. - -2. **Witness map**: these are initially populated from your project's `Prover.toml` file. In this example, they will be used to populate `x` and `result` at the beginning of the `main` function. - -Most of the time you will probably be focusing mostly on locals, as they represent the high level state of your program. - -You might be interested in inspecting the witness map in case you are trying to solve a really low level issue in the compiler or runtime itself, so this concerns mostly advanced or niche users. - -Let's step through the program, by using the debugger buttons or their corresponding keyboard shortcuts. - -![Debugger buttons](@site/static/img/debugger/4-debugger-buttons.png) - -Now we can see in the variables pane that there's values for `digest`, `result` and `x`. - -![Inspecting locals](@site/static/img/debugger/5-assert.png) - -We can also inspect the values of variables by directly hovering on them on the code. - -![Hover locals](@site/static/img/debugger/6-hover.png) - -Let's set a break point at the `keccak256` function, so we can continue execution up to the point when it's first invoked without having to go one step at a time. - -We just need to click the to the right of the line number 18. Once the breakpoint appears, we can click the `continue` button or use its corresponding keyboard shortcut (`F5` by default). - -![Breakpoint](@site/static/img/debugger/7-break.png) - -Now we are debugging the `keccak256` function, notice the _Call Stack pane_ at the lower right. This lets us inspect the current call stack of our process. - -That covers most of the current debugger functionalities. Check out [the reference](../../reference/debugger/debugger_vscode.md) for more details on how to configure the debugger. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.34.0/how_to/how-to-oracles.md b/docs/versioned_docs/version-v0.34.0/how_to/how-to-oracles.md deleted file mode 100644 index 392dd8b452e..00000000000 --- a/docs/versioned_docs/version-v0.34.0/how_to/how-to-oracles.md +++ /dev/null @@ -1,275 +0,0 @@ ---- -title: How to use Oracles -description: Learn how to use oracles in your Noir program with examples in both Nargo and NoirJS. This guide also covers writing a JSON RPC server and providing custom foreign call handlers for NoirJS. -keywords: - - Noir Programming - - Oracles - - Nargo - - NoirJS - - JSON RPC Server - - Foreign Call Handlers -sidebar_position: 1 ---- - -This guide shows you how to use oracles in your Noir program. For the sake of clarity, it assumes that: - -- You have read the [explainer on Oracles](../explainers/explainer-oracle.md) and are comfortable with the concept. -- You have a Noir program to add oracles to. You can create one using the [vite-hardhat starter](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat) as a boilerplate. -- You understand the concept of a JSON-RPC server. Visit the [JSON-RPC website](https://www.jsonrpc.org/) if you need a refresher. -- You are comfortable with server-side JavaScript (e.g. Node.js, managing packages, etc.). - -## Rundown - -This guide has 3 major steps: - -1. How to modify our Noir program to make use of oracle calls as unconstrained functions -2. How to write a JSON RPC Server to resolve these oracle calls with Nargo -3. How to use them in Nargo and how to provide a custom resolver in NoirJS - -## Step 1 - Modify your Noir program - -An oracle is defined in a Noir program by defining two methods: - -- An unconstrained method - This tells the compiler that it is executing an [unconstrained functions](../noir/concepts//unconstrained.md). -- A decorated oracle method - This tells the compiler that this method is an RPC call. - -An example of an oracle that returns a `Field` would be: - -```rust -#[oracle(getSqrt)] -unconstrained fn sqrt(number: Field) -> Field { } - -unconstrained fn get_sqrt(number: Field) -> Field { - sqrt(number) -} -``` - -In this example, we're wrapping our oracle function in an unconstrained method, and decorating it with `oracle(getSqrt)`. We can then call the unconstrained function as we would call any other function: - -```rust -fn main(input: Field) { - let sqrt = get_sqrt(input); -} -``` - -In the next section, we will make this `getSqrt` (defined on the `sqrt` decorator) be a method of the RPC server Noir will use. - -:::danger - -As explained in the [Oracle Explainer](../explainers/explainer-oracle.md), this `main` function is unsafe unless you constrain its return value. For example: - -```rust -fn main(input: Field) { - let sqrt = get_sqrt(input); - assert(sqrt.pow_32(2) as u64 == input as u64); // <---- constrain the return of an oracle! -} -``` - -::: - -:::info - -Currently, oracles only work with single params or array params. For example: - -```rust -#[oracle(getSqrt)] -unconstrained fn sqrt([Field; 2]) -> [Field; 2] { } -``` - -::: - -## Step 2 - Write an RPC server - -Brillig will call *one* RPC server. Most likely you will have to write your own, and you can do it in whatever language you prefer. In this guide, we will do it in Javascript. - -Let's use the above example of an oracle that consumes an array with two `Field` and returns their square roots: - -```rust -#[oracle(getSqrt)] -unconstrained fn sqrt(input: [Field; 2]) -> [Field; 2] { } - -unconstrained fn get_sqrt(input: [Field; 2]) -> [Field; 2] { - sqrt(input) -} - -fn main(input: [Field; 2]) { - let sqrt = get_sqrt(input); - assert(sqrt[0].pow_32(2) as u64 == input[0] as u64); - assert(sqrt[1].pow_32(2) as u64 == input[1] as u64); -} - -#[test] -fn test() { - let input = [4, 16]; - main(input); -} -``` - -:::info - -Why square root? - -In general, computing square roots is computationally more expensive than multiplications, which takes a toll when speaking about ZK applications. In this case, instead of calculating the square root in Noir, we are using our oracle to offload that computation to be made in plain. In our circuit we can simply multiply the two values. - -::: - -Now, we should write the correspondent RPC server, starting with the [default JSON-RPC 2.0 boilerplate](https://www.npmjs.com/package/json-rpc-2.0#example): - -```js -import { JSONRPCServer } from "json-rpc-2.0"; -import express from "express"; -import bodyParser from "body-parser"; - -const app = express(); -app.use(bodyParser.json()); - -const server = new JSONRPCServer(); -app.post("/", (req, res) => { - const jsonRPCRequest = req.body; - server.receive(jsonRPCRequest).then((jsonRPCResponse) => { - if (jsonRPCResponse) { - res.json(jsonRPCResponse); - } else { - res.sendStatus(204); - } - }); -}); - -app.listen(5555); -``` - -Now, we will add our `getSqrt` method, as expected by the `#[oracle(getSqrt)]` decorator in our Noir code. It maps through the params array and returns their square roots: - -```js -server.addMethod("resolve_foreign_call", async (params) => { - if (params[0].function !== "getSqrt") { - throw Error("Unexpected foreign call") - }; - const values = params[0].inputs[0].map((field) => { - return `${Math.sqrt(parseInt(field, 16))}`; - }); - return { values: [values] }; -}); -``` - -If you're using Typescript, the following types may be helpful in understanding the expected return value and making sure they're easy to follow: - -```js -export type ForeignCallSingle = string; - -export type ForeignCallArray = string[]; - -export type ForeignCallResult = { - values: (ForeignCallSingle | ForeignCallArray)[]; -}; -``` - -:::info Multidimensional Arrays - -If the Oracle function is returning an array containing other arrays, such as `[['1','2],['3','4']]`, you need to provide the values in JSON as flattened values. In the previous example, it would be `['1', '2', '3', '4']`. In the Noir program, the Oracle signature can use a nested type, the flattened values will be automatically converted to the nested type. - -::: - -## Step 3 - Usage with Nargo - -Using the [`nargo` CLI tool](../getting_started/installation/index.md), you can use oracles in the `nargo test` and `nargo execute` commands by passing a value to `--oracle-resolver`. For example: - -```bash -nargo test --oracle-resolver http://localhost:5555 -``` - -This tells `nargo` to use your RPC Server URL whenever it finds an oracle decorator. - -## Step 4 - Usage with NoirJS - -In a JS environment, an RPC server is not strictly necessary, as you may want to resolve your oracles without needing any JSON call at all. NoirJS simply expects that you pass a callback function when you generate proofs, and that callback function can be anything. - -For example, if your Noir program expects the host machine to provide CPU pseudo-randomness, you could simply pass it as the `foreignCallHandler`. You don't strictly need to create an RPC server to serve pseudo-randomness, as you may as well get it directly in your app: - -```js -const foreignCallHandler = (name, inputs) => crypto.randomBytes(16) // etc - -await noir.execute(inputs, foreignCallHandler) -``` - -As one can see, in NoirJS, the [`foreignCallHandler`](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) function simply means "a callback function that returns a value of type [`ForeignCallOutput`](../reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md). It doesn't have to be an RPC call like in the case for Nargo. - -:::tip - -Does this mean you don't have to write an RPC server like in [Step #2](#step-2---write-an-rpc-server)? - -You don't technically have to, but then how would you run `nargo test`? To use both `Nargo` and `NoirJS` in your development flow, you will have to write a JSON RPC server. - -::: - -In this case, let's make `foreignCallHandler` call the JSON RPC Server we created in [Step #2](#step-2---write-an-rpc-server), by making it a JSON RPC Client. - -For example, using the same `getSqrt` program in [Step #1](#step-1---modify-your-noir-program) (comments in the code): - -```js -import { JSONRPCClient } from "json-rpc-2.0"; - -// declaring the JSONRPCClient -const client = new JSONRPCClient((jsonRPCRequest) => { -// hitting the same JSON RPC Server we coded above - return fetch("http://localhost:5555", { - method: "POST", - headers: { - "content-type": "application/json", - }, - body: JSON.stringify(jsonRPCRequest), - }).then((response) => { - if (response.status === 200) { - return response - .json() - .then((jsonRPCResponse) => client.receive(jsonRPCResponse)); - } else if (jsonRPCRequest.id !== undefined) { - return Promise.reject(new Error(response.statusText)); - } - }); -}); - -// declaring a function that takes the name of the foreign call (getSqrt) and the inputs -const foreignCallHandler = async (name, input) => { - const inputs = input[0].map((i) => i.toString("hex")) - // notice that the "inputs" parameter contains *all* the inputs - // in this case we to make the RPC request with the first parameter "numbers", which would be input[0] - const oracleReturn = await client.request("resolve_foreign_call", [ - { - function: name, - inputs: [inputs] - }, - ]); - return [oracleReturn.values[0]]; -}; - -// the rest of your NoirJS code -const input = { input: [4, 16] }; -const { witness } = await noir.execute(input, foreignCallHandler); -``` - -:::tip - -If you're in a NoirJS environment running your RPC server together with a frontend app, you'll probably hit a familiar problem in full-stack development: requests being blocked by [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) policy. For development only, you can simply install and use the [`cors` npm package](https://www.npmjs.com/package/cors) to get around the problem: - -```bash -yarn add cors -``` - -and use it as a middleware: - -```js -import cors from "cors"; - -const app = express(); -app.use(cors()) -``` - -::: - -## Conclusion - -Hopefully by the end of this guide, you should be able to: - -- Write your own logic around Oracles and how to write a JSON RPC server to make them work with your Nargo commands. -- Provide custom foreign call handlers for NoirJS. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.34.0/how_to/how-to-recursion.md b/docs/versioned_docs/version-v0.34.0/how_to/how-to-recursion.md deleted file mode 100644 index c8c4dc9f5b4..00000000000 --- a/docs/versioned_docs/version-v0.34.0/how_to/how-to-recursion.md +++ /dev/null @@ -1,180 +0,0 @@ ---- -title: How to use recursion on NoirJS -description: Learn how to implement recursion with NoirJS, a powerful tool for creating smart contracts on the EVM blockchain. This guide assumes familiarity with NoirJS, solidity verifiers, and the Barretenberg proving backend. Discover how to generate both final and intermediate proofs using `noir_js` and `backend_barretenberg`. -keywords: - [ - "NoirJS", - "EVM blockchain", - "smart contracts", - "recursion", - "solidity verifiers", - "Barretenberg backend", - "noir_js", - "backend_barretenberg", - "intermediate proofs", - "final proofs", - "nargo compile", - "json import", - "recursive circuit", - "recursive app" - ] -sidebar_position: 1 ---- - -This guide shows you how to use recursive proofs in your NoirJS app. For the sake of clarity, it is assumed that: - -- You already have a NoirJS app. If you don't, please visit the [NoirJS tutorial](../tutorials/noirjs_app.md) and the [reference](../reference/NoirJS/noir_js/index.md). -- You are familiar with what are recursive proofs and you have read the [recursion explainer](../explainers/explainer-recursion.md) -- You already built a recursive circuit following [the reference](../noir/standard_library/recursion.mdx), and understand how it works. - -It is also assumed that you're not using `noir_wasm` for compilation, and instead you've used [`nargo compile`](../reference/nargo_commands.md) to generate the `json` you're now importing into your project. However, the guide should work just the same if you're using `noir_wasm`. - -:::info - -As you've read in the [explainer](../explainers/explainer-recursion.md), a recursive proof is an intermediate proof. This means that it doesn't necessarily generate the final step that makes it verifiable in a smart contract. However, it is easy to verify within another circuit. - -While "standard" usage of NoirJS packages abstracts final proofs, it currently lacks the necessary interface to abstract away intermediate proofs. This means that these proofs need to be created by using the backend directly. - -In short: - -- `noir_js` generates *only* final proofs -- `backend_barretenberg` generates both types of proofs - -::: - -In a standard recursive app, you're also dealing with at least two circuits. For the purpose of this guide, we will assume the following: - -- `main`: a circuit of type `assert(x != y)`, where `main` is marked with a `#[recursive]` attribute. This attribute states that the backend should generate proofs that are friendly for verification within another circuit. -- `recursive`: a circuit that verifies `main` - -For a full example of how recursive proofs work, please refer to the [noir-examples](https://github.com/noir-lang/noir-examples) repository. We will *not* be using it as a reference for this guide. - -## Step 1: Setup - -In a common NoirJS app, you need to instantiate a backend with something like `const backend = new Backend(circuit)`. Then you feed it to the `noir_js` interface. - -For recursion, this doesn't happen, and the only need for `noir_js` is only to `execute` a circuit and get its witness and return value. Everything else is not interfaced, so it needs to happen on the `backend` object. - -It is also recommended that you instantiate the backend with as many threads as possible, to allow for maximum concurrency: - -```js -const backend = new Backend(circuit, { threads: 8 }) -``` - -:::tip -You can use the [`os.cpus()`](https://nodejs.org/api/os.html#oscpus) object in `nodejs` or [`navigator.hardwareConcurrency`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/hardwareConcurrency) on the browser to make the most out of those glorious cpu cores -::: - -## Step 2: Generating the witness and the proof for `main` - -After instantiating the backend, you should also instantiate `noir_js`. We will use it to execute the circuit and get the witness. - -```js -const noir = new Noir(circuit) -const { witness } = noir.execute(input) -``` - -With this witness, you are now able to generate the intermediate proof for the main circuit: - -```js -const { proof, publicInputs } = await backend.generateProof(witness) -``` - -:::warning - -Always keep in mind what is actually happening on your development process, otherwise you'll quickly become confused about what circuit we are actually running and why! - -In this case, you can imagine that Alice (running the `main` circuit) is proving something to Bob (running the `recursive` circuit), and Bob is verifying her proof within his proof. - -With this in mind, it becomes clear that our intermediate proof is the one *meant to be verified within another circuit*, so it must be Alice's. Actually, the only final proof in this theoretical scenario would be the last one, sent on-chain. - -::: - -## Step 3 - Verification and proof artifacts - -Optionally, you are able to verify the intermediate proof: - -```js -const verified = await backend.verifyProof({ proof, publicInputs }) -``` - -This can be useful to make sure our intermediate proof was correctly generated. But the real goal is to do it within another circuit. For that, we need to generate recursive proof artifacts that will be passed to the circuit that is verifying the proof we just generated. Instead of passing the proof and verification key as a byte array, we pass them as fields which makes it cheaper to verify in a circuit: - -```js -const { proofAsFields, vkAsFields, vkHash } = await backend.generateRecursiveProofArtifacts( { publicInputs, proof }, publicInputsCount) -``` - -This call takes the public inputs and the proof, but also the public inputs count. While this is easily retrievable by simply counting the `publicInputs` length, the backend interface doesn't currently abstract it away. - -:::info - -The `proofAsFields` has a constant size `[Field; 93]` and verification keys in Barretenberg are always `[Field; 114]`. - -::: - -:::warning - -One common mistake is to forget *who* makes this call. - -In a situation where Alice is generating the `main` proof, if she generates the proof artifacts and sends them to Bob, which gladly takes them as true, this would mean Alice could prove anything! - -Instead, Bob needs to make sure *he* extracts the proof artifacts, using his own instance of the `main` circuit backend. This way, Alice has to provide a valid proof for the correct `main` circuit. - -::: - -## Step 4 - Recursive proof generation - -With the artifacts, generating a recursive proof is no different from a normal proof. You simply use the `backend` (with the recursive circuit) to generate it: - -```js -const recursiveInputs = { - verification_key: vkAsFields, // array of length 114 - proof: proofAsFields, // array of length 93 + size of public inputs - publicInputs: [mainInput.y], // using the example above, where `y` is the only public input - key_hash: vkHash, -} - -const { witness, returnValue } = noir.execute(recursiveInputs) // we're executing the recursive circuit now! -const { proof, publicInputs } = backend.generateProof(witness) -const verified = backend.verifyProof({ proof, publicInputs }) -``` - -You can obviously chain this proof into another proof. In fact, if you're using recursive proofs, you're probably interested of using them this way! - -:::tip - -Managing circuits and "who does what" can be confusing. To make sure your naming is consistent, you can keep them in an object. For example: - -```js -const circuits = { - main: mainJSON, - recursive: recursiveJSON -} -const backends = { - main: new BarretenbergBackend(circuits.main), - recursive: new BarretenbergBackend(circuits.recursive) -} -const noir_programs = { - main: new Noir(circuits.main), - recursive: new Noir(circuits.recursive) -} -``` - -This allows you to neatly call exactly the method you want without conflicting names: - -```js -// Alice runs this 👇 -const { witness: mainWitness } = await noir_programs.main.execute(input) -const proof = await backends.main.generateProof(mainWitness) - -// Bob runs this 👇 -const verified = await backends.main.verifyProof(proof) -const { proofAsFields, vkAsFields, vkHash } = await backends.main.generateRecursiveProofArtifacts( - proof, - numPublicInputs, -); -const { witness: recursiveWitness } = await noir_programs.recursive.execute(recursiveInputs) -const recursiveProof = await backends.recursive.generateProof(recursiveWitness); -``` - -::: diff --git a/docs/versioned_docs/version-v0.34.0/how_to/how-to-solidity-verifier.md b/docs/versioned_docs/version-v0.34.0/how_to/how-to-solidity-verifier.md deleted file mode 100644 index 3bb96c66795..00000000000 --- a/docs/versioned_docs/version-v0.34.0/how_to/how-to-solidity-verifier.md +++ /dev/null @@ -1,257 +0,0 @@ ---- -title: Generate a 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, - ] -sidebar_position: 0 -pagination_next: tutorials/noirjs_app ---- - -Noir has the ability to generate a verifier contract in Solidity, which can be deployed in many EVM-compatible blockchains such as Ethereum. - -This allows for a powerful feature set, as one can make use of the conciseness and the privacy provided by Noir in an immutable ledger. Applications can range from simple P2P guessing games, to complex private DeFi interactions. - -This guide shows you how to generate a Solidity Verifier and deploy it on the [Remix IDE](https://remix.ethereum.org/). It is assumed that: - -- You are comfortable with the Solidity programming language and understand how contracts are deployed on the Ethereum network -- You have Noir installed and you have a Noir program. If you don't, [get started](../getting_started/installation/index.md) with Nargo and the example Hello Noir circuit -- You are comfortable navigating RemixIDE. If you aren't or you need a refresher, you can find some video tutorials [here](https://www.youtube.com/channel/UCjTUPyFEr2xDGN6Cg8nKDaA) that could help you. - -## Rundown - -Generating a Solidity Verifier contract is actually a one-command process. However, compiling it and deploying it can have some caveats. Here's the rundown of this guide: - -1. How to generate a solidity smart contract -2. How to compile the smart contract in the RemixIDE -3. How to deploy it to a testnet - -## Step 1 - Generate a contract - -This is by far the most straightforward step. Just run: - -```sh -nargo compile -``` - -This will compile your source code into a Noir build artifact to be stored in the `./target` directory, you can then generate the smart contract using the commands: - -```sh -# Here we pass the path to the newly generated Noir artifact. -bb write_vk -b ./target/.json -bb contract -``` - -replacing `` with the name of your Noir project. A new `contract` folder would then be generated in your project directory, containing the Solidity -file `contract.sol`. It can be deployed to any EVM blockchain acting as a verifier smart contract. - -:::info - -It is possible to generate 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 generation of verifier contracts, for the time being these are only in Solidity. -::: - -## Step 2 - Compiling - -We will mostly skip the details of RemixIDE, as the UI can change from version to version. For now, we can just open -Remix and create a blank workspace. - -![Create Workspace](@site/static/img/how-tos/solidity_verifier_1.png) - -We will create a new file to contain the contract Nargo generated, and copy-paste its content. - -:::warning - -You'll likely see a warning advising you to not trust pasted code. While it is an important warning, it is irrelevant in the context of this guide and can be ignored. We will not be deploying anywhere near a mainnet. - -::: - -To compile our the verifier, we can navigate to the compilation tab: - -![Compilation Tab](@site/static/img/how-tos/solidity_verifier_2.png) - -Remix should automatically match a suitable compiler version. However, hitting the "Compile" button will most likely generate a "Stack too deep" error: - -![Stack too deep](@site/static/img/how-tos/solidity_verifier_3.png) - -This is due to the verify function needing to put many variables on the stack, but enabling the optimizer resolves the issue. To do this, let's open the "Advanced Configurations" tab and enable optimization. The default 200 runs will suffice. - -:::info - -This time we will see a warning about an unused function parameter. This is expected, as the `verify` function doesn't use the `_proof` parameter inside a solidity block, it is loaded from calldata and used in assembly. - -::: - -![Compilation success](@site/static/img/how-tos/solidity_verifier_4.png) - -## Step 3 - Deploying - -At this point we should have a compiled contract ready to deploy. If we navigate to the deploy section in Remix, we will see many different environments we can deploy to. The steps to deploy on each environment would be out-of-scope for this guide, so we will just use the default Remix VM. - -Looking closely, we will notice that our "Solidity Verifier" is actually three contracts working together: - -- An `UltraVerificationKey` library which simply stores the verification key for our circuit. -- An abstract contract `BaseUltraVerifier` containing most of the verifying logic. -- A main `UltraVerifier` contract that inherits from the Base and uses the Key contract. - -Remix will take care of the dependencies for us so we can simply deploy the UltraVerifier contract by selecting it and hitting "deploy": - -![Deploying UltraVerifier](@site/static/img/how-tos/solidity_verifier_5.png) - -A contract will show up in the "Deployed Contracts" section, where we can retrieve the Verification Key Hash. This is particularly useful for double-checking that the deployer contract is the correct one. - -:::note - -Why "UltraVerifier"? - -To be precise, the Noir compiler (`nargo`) doesn't generate the verifier contract directly. It compiles the Noir code into an intermediate language (ACIR), which is then executed by the backend. So it is the backend that returns the verifier smart contract, not Noir. - -In this case, the Barretenberg Backend uses the UltraPlonk proving system, hence the "UltraVerifier" name. - -::: - -## Step 4 - Verifying - -To verify a proof using the Solidity verifier contract, we call the `verify` function in this extended contract: - -```solidity -function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external view returns (bool) -``` - -When using the default example in the [Hello Noir](../getting_started/hello_noir/index.md) guide, the easiest way to confirm that the verifier contract is doing its job is by calling the `verify` function via remix with the required parameters. Note that the public inputs must be passed in separately to the rest of the proof so we must split the proof as returned from `bb`. - -First generate a proof with `bb` at the location `./proof` using the steps in [get started](../getting_started/hello_noir/index.md), this proof is in a binary format but we want to convert it into a hex string to pass into Remix, this can be done with the - -```bash -# This value must be changed to match the number of public inputs (including return values!) in your program. -NUM_PUBLIC_INPUTS=1 -PUBLIC_INPUT_BYTES=32*NUM_PUBLIC_INPUTS -HEX_PUBLIC_INPUTS=$(head -c $PUBLIC_INPUT_BYTES ./proof | od -An -v -t x1 | tr -d $' \n') -HEX_PROOF=$(tail -c +$(($PUBLIC_INPUT_BYTES + 1)) ./proof | od -An -v -t x1 | tr -d $' \n') - -echo "Public inputs:" -echo $HEX_PUBLIC_INPUTS - -echo "Proof:" -echo "0x$HEX_PROOF" -``` - -Remix expects that the public inputs will be split into an array of `bytes32` values so `HEX_PUBLIC_INPUTS` needs to be split up into 32 byte chunks which are prefixed with `0x` accordingly. - -A programmatic example of how the `verify` function is called can be seen 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"); -``` - -:::info[Return Values] - -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. - -For example, if you have Noir program like this: - -```rust -fn main( - // Public inputs - pubkey_x: pub Field, - pubkey_y: pub Field, - // Private inputs - priv_key: Field, -) -> pub Field -``` - -the `verify` function will expect the public inputs array (second function parameter) to be of length 3, the two inputs and the return value. - -Passing only two inputs will result in an error such as `PUBLIC_INPUT_COUNT_INVALID(3, 2)`. - -In this case, the inputs parameter to `verify` would be an array ordered as `[pubkey_x, pubkey_y, return`. - -::: - -:::tip[Structs] - -You can pass structs to the verifier contract. They will be flattened so that the array of inputs is 1-dimensional array. - -For example, 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) { - //... -} -``` - -The order of these inputs would be flattened to: `[x, nested.t1.val1, nested.t1.val2, nested.is_true, y]` - -::: - -The other function you can call is our entrypoint `verify` function, as defined above. - -:::tip - -It's worth noticing that the `verify` function is actually a `view` function. A `view` function does not alter the blockchain state, so it doesn't need to be distributed (i.e. it will run only on the executing node), and therefore doesn't cost any gas. - -This can be particularly useful in some situations. If Alice generated a proof and wants Bob to verify its correctness, Bob doesn't need to run Nargo, NoirJS, or any Noir specific infrastructure. He can simply make a call to the blockchain with the proof and verify it is correct without paying any gas. - -It would be incorrect to say that a Noir proof verification costs any gas at all. However, most of the time the result of `verify` is used to modify state (for example, to update a balance, a game state, etc). In that case the whole network needs to execute it, which does incur gas costs (calldata and execution, but not storage). - -::: - -## A Note on EVM chains - -Noir proof verification requires the ecMul, ecAdd and ecPairing precompiles. Not all EVM chains support EC Pairings, notably some of the ZK-EVMs. This means that you won't be able to use the verifier contract in all of them. You can find an incomplete list of which EVM chains support these precompiles [here](https://www.evmdiff.com/features?feature=precompiles). - -For example, chains like `zkSync ERA` and `Polygon zkEVM` do not currently support these precompiles, so proof verification via Solidity verifier contracts won't work. Here's a quick list of EVM chains that have been tested and are known to work: - -- Optimism -- Arbitrum -- Polygon PoS -- Scroll -- Celo -- BSC -- Blast L2 -- Avalanche C-Chain -- Mode -- Linea -- Moonbeam - -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. - -## What's next - -Now that you know how to call a Noir Solidity Verifier on a smart contract using Remix, you should be comfortable with using it with some programmatic frameworks, such as [hardhat](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat) and [foundry](https://github.com/noir-lang/noir-starter/tree/main/with-foundry). - -You can find other tools, examples, boilerplates and libraries in the [awesome-noir](https://github.com/noir-lang/awesome-noir) repository. - -You should also be ready to write and deploy your first NoirJS app and start generating proofs on websites, phones, and NodeJS environments! Head on to the [NoirJS tutorial](../tutorials/noirjs_app.md) to learn how to do that. diff --git a/docs/versioned_docs/version-v0.34.0/how_to/merkle-proof.mdx b/docs/versioned_docs/version-v0.34.0/how_to/merkle-proof.mdx deleted file mode 100644 index 0a128adb2de..00000000000 --- a/docs/versioned_docs/version-v0.34.0/how_to/merkle-proof.mdx +++ /dev/null @@ -1,48 +0,0 @@ ---- -title: Prove Merkle Tree 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] -sidebar_position: 4 ---- - -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 - -fn main(message : [Field; 62], index : Field, hashpath : [Field; 40], root : Field) { - let leaf = std::hash::hash_to_field(message.as_slice()); - 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.as_slice()); -``` - -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.34.0/how_to/using-devcontainers.mdx b/docs/versioned_docs/version-v0.34.0/how_to/using-devcontainers.mdx deleted file mode 100644 index 727ec6ca667..00000000000 --- a/docs/versioned_docs/version-v0.34.0/how_to/using-devcontainers.mdx +++ /dev/null @@ -1,110 +0,0 @@ ---- -title: Developer Containers and Codespaces -description: "Learn how to set up a devcontainer in your GitHub repository for a seamless coding experience with Codespaces. Follow our easy 8-step guide to create your own Noir environment without installing Nargo locally." -keywords: ["Devcontainer", "Codespaces", "GitHub", "Noir Environment", "Docker Image", "Development Environment", "Remote Coding", "GitHub Codespaces", "Noir Programming", "Nargo", "VSCode Extensions", "Noirup"] -sidebar_position: 1 ---- - -Adding a developer container configuration file to your Noir project is one of the easiest way to unlock coding in browser. - -## What's a devcontainer after all? - -A [Developer Container](https://containers.dev/) (devcontainer for short) is a Docker image that comes preloaded with tools, extensions, and other tools you need to quickly get started or continue a project, without having to install Nargo locally. Think of it as a development environment in a box. - -There are many advantages to this: - -- It's platform and architecture agnostic -- You don't need to have an IDE installed, or Nargo, or use a terminal at all -- It's safer for using on a public machine or public network - -One of the best ways of using devcontainers is... not using your machine at all, for maximum control, performance, and ease of use. -Enter Codespaces. - -## Codespaces - -If a devcontainer is just a Docker image, then what stops you from provisioning a `p3dn.24xlarge` AWS EC2 instance with 92 vCPUs and 768 GiB RAM and using it to prove your 10-gate SNARK proof? - -Nothing! Except perhaps the 30-40$ per hour it will cost you. - -The problem is that provisioning takes time, and I bet you don't want to see the AWS console every time you want to code something real quick. - -Fortunately, there's an easy and free way to get a decent remote machine ready and loaded in less than 2 minutes: Codespaces. [Codespaces is a Github feature](https://github.com/features/codespaces) that allows you to code in a remote machine by using devcontainers, and it's pretty cool: - -- You can start coding Noir in less than a minute -- It uses the resources of a remote machine, so you can code on your grandma's phone if needed be -- It makes it easy to share work with your frens -- It's fully reusable, you can stop and restart whenever you need to - -:::info - -Don't take out your wallet just yet. Free GitHub accounts get about [15-60 hours of coding](https://github.com/features/codespaces) for free per month, depending on the size of your provisioned machine. - -::: - -## Tell me it's _actually_ easy - -It is! - -Github comes with a default codespace and you can use it to code your own devcontainer. That's exactly what we will be doing in this guide. - - - -8 simple steps: - -#### 1. Create a new repository on GitHub. - -#### 2. Click "Start coding with Codespaces". This will use the default image. - -#### 3. Create a folder called `.devcontainer` in the root of your repository. - -#### 4. Create a Dockerfile in that folder, and paste the following code: - -```docker -FROM --platform=linux/amd64 node:lts-bookworm-slim -SHELL ["/bin/bash", "-c"] -RUN apt update && apt install -y curl bash git tar gzip libc++-dev -RUN curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash -ENV PATH="/root/.nargo/bin:$PATH" -RUN noirup -ENTRYPOINT ["nargo"] -``` -#### 5. Create a file called `devcontainer.json` in the same folder, and paste the following code: - -```json -{ - "name": "Noir on Codespaces", - "build": { - "context": ".", - "dockerfile": "Dockerfile" - }, - "customizations": { - "vscode": { - "extensions": ["noir-lang.vscode-noir"] - } - } -} -``` -#### 6. Commit and push your changes - -This will pull the new image and build it, so it could take a minute or so - -#### 8. Done! -Just wait for the build to finish, and there's your easy Noir environment. - - -Refer to [noir-starter](https://github.com/noir-lang/noir-starter/) as an example of how devcontainers can be used together with codespaces. - - - -## How do I use it? - -Using the codespace is obviously much easier than setting it up. -Just navigate to your repository and click "Code" -> "Open with Codespaces". It should take a few seconds to load, and you're ready to go. - -:::info - -If you really like the experience, you can add a badge to your readme, links to existing codespaces, and more. -Check out the [official docs](https://docs.github.com/en/codespaces/setting-up-your-project-for-codespaces/setting-up-your-repository/facilitating-quick-creation-and-resumption-of-codespaces) for more info. diff --git a/docs/versioned_docs/version-v0.34.0/index.mdx b/docs/versioned_docs/version-v0.34.0/index.mdx deleted file mode 100644 index a6bd306f91d..00000000000 --- a/docs/versioned_docs/version-v0.34.0/index.mdx +++ /dev/null @@ -1,67 +0,0 @@ ---- -title: Noir Lang -hide_title: true -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] -sidebar_position: 0 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -Noir Logo - -Noir is an open-source Domain-Specific Language for safe and seamless construction of privacy-preserving Zero-Knowledge programs, requiring no previous knowledge on the underlying mathematics or cryptography. - -ZK programs are programs that can generate short proofs of statements without revealing all inputs to the statements. You can read more about Zero-Knowledge Proofs [here](https://dev.to/spalladino/a-beginners-intro-to-coding-zero-knowledge-proofs-c56). - -## What's new about Noir? - -Noir works differently from most ZK languages by taking a two-pronged path. First, it compiles the program to an adaptable intermediate language known as ACIR. From there, depending on a given project's needs, ACIR can be further compiled into an arithmetic circuit for integration with the proving backend. - -:::info - -Noir is backend agnostic, which means it makes no assumptions on which proving backend powers the ZK proof. Being the language that powers [Aztec Contracts](https://docs.aztec.network/developers/contracts/main), it defaults to Aztec's Barretenberg proving backend. - -However, the ACIR output can be transformed to be compatible with other PLONK-based backends, or into a [rank-1 constraint system](https://www.rareskills.io/post/rank-1-constraint-system) suitable for backends such as Arkwork's Marlin. - -::: - -## Who is Noir for? - -Noir can be used both in complex cloud-based backends and in user's smartphones, requiring no knowledge on the underlying math or cryptography. From authorization systems that keep a password in the user's device, to complex on-chain verification of recursive proofs, Noir is designed to abstract away complexity without any significant overhead. Here are some examples of situations where Noir can be used: - - - - Noir Logo - - Aztec Contracts leverage Noir to allow for the storage and execution of private information. Writing an Aztec Contract is as easy as writing Noir, and Aztec developers can easily interact with the network storage and execution through the [Aztec.nr](https://docs.aztec.network/developers/contracts/main) library. - - - Soliditry Verifier Example - Noir can auto-generate Solidity verifier contracts that verify Noir proofs. This allows for non-interactive verification of proofs containing private information in an immutable system. This feature powers a multitude of use-case scenarios, from P2P chess tournaments, to [Aztec Layer-2 Blockchain](https://docs.aztec.network/) - - - Aztec Labs developed NoirJS, an easy interface to generate and verify Noir proofs in a Javascript environment. This allows for Noir to be used in webpages, mobile apps, games, and any other environment supporting JS execution in a standalone manner. - - - - -## Libraries - -Noir is meant to be easy to extend by simply importing Noir libraries just like in Rust. -The [awesome-noir repo](https://github.com/noir-lang/awesome-noir#libraries) is a collection of libraries developed by the Noir community. -Writing a new library is easy and makes code be composable and easy to reuse. See the section on [dependencies](noir/modules_packages_crates/dependencies.md) for more information. diff --git a/docs/versioned_docs/version-v0.34.0/migration_notes.md b/docs/versioned_docs/version-v0.34.0/migration_notes.md deleted file mode 100644 index 6bd740024e5..00000000000 --- a/docs/versioned_docs/version-v0.34.0/migration_notes.md +++ /dev/null @@ -1,105 +0,0 @@ ---- -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. - -### `backend encountered an error: libc++.so.1` - -Depending on your OS, you may encounter the following error when running `nargo prove` for the first time: - -```text -The backend encountered an error: "/home/codespace/.nargo/backends/acvm-backend-barretenberg/backend_binary: error while loading shared libraries: libc++.so.1: cannot open shared object file: No such file or directory\n" -``` - -Install the `libc++-dev` library with: - -```bash -sudo apt install libc++-dev -``` - -## ≥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](noir/concepts/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 your 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 file is running [this bash script](https://github.com/noir-lang/barretenberg-js-binary/blob/master/run-bb-js.sh), 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.34.0/noir/concepts/_category_.json b/docs/versioned_docs/version-v0.34.0/noir/concepts/_category_.json deleted file mode 100644 index 7da08f8a8c5..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/concepts/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "Concepts", - "position": 0, - "collapsible": true, - "collapsed": true -} \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.34.0/noir/concepts/assert.md b/docs/versioned_docs/version-v0.34.0/noir/concepts/assert.md deleted file mode 100644 index 2132de42072..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/concepts/assert.md +++ /dev/null @@ -1,78 +0,0 @@ ---- -title: Assert Function -description: - Learn about the `assert` and `static_assert` functions 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 or compile-time, respectively. -keywords: [Noir programming language, assert statement, predicate expression, comparison expression] -sidebar_position: 4 ---- - -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); -} -``` - -> 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`. - -You can optionally provide a message to be logged when the assertion fails: - -```rust -assert(x == y, "x and y are not equal"); -``` - -Aside string literals, the optional message can be a format string or any other type supported as input for Noir's [print](../standard_library/logging.md) functions. This feature lets you incorporate runtime variables into your failed assertion logs: - -```rust -assert(x == y, f"Expected x == y, but got {x} == {y}"); -``` - -Using a variable as an assertion message directly: - -```rust -struct myStruct { - myField: Field -} - -let s = myStruct { myField: y }; -assert(s.myField == x, s); -``` - -There is also a special `static_assert` function that behaves like `assert`, -but that runs at compile-time. - -```rust -fn main(xs: [Field; 3]) { - let x = 2 + 2; - let y = 4; - static_assert(x == y, "expected 2 + 2 to equal 4"); - - // This passes since the length of `xs` is known at compile-time - static_assert(xs.len() == 3, "expected the input to have 3 elements"); -} -``` - -This function fails when passed a dynamic (run-time) argument: - -```rust -fn main(x : Field, y : Field) { - // this fails because `x` is not known at compile-time - static_assert(x == 2, "expected x to be known at compile-time and equal to 2"); - - let mut example_slice = &[]; - if y == 4 { - example_slice = example_slice.push_back(0); - } - - // This fails because the length of `example_slice` is not known at - // compile-time - let error_message = "expected an empty slice, known at compile-time"; - static_assert(example_slice.len() == 0, error_message); -} -``` - diff --git a/docs/versioned_docs/version-v0.34.0/noir/concepts/comments.md b/docs/versioned_docs/version-v0.34.0/noir/concepts/comments.md deleted file mode 100644 index b51a85f5c94..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/concepts/comments.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -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] -sidebar_position: 10 ---- - -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.34.0/noir/concepts/comptime.md b/docs/versioned_docs/version-v0.34.0/noir/concepts/comptime.md deleted file mode 100644 index 6f91fde9bd8..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/concepts/comptime.md +++ /dev/null @@ -1,424 +0,0 @@ ---- -title: Compile-time Code & Metaprogramming -description: Learn how to use metaprogramming in Noir to create macros or derive your own traits -keywords: [Noir, comptime, compile-time, metaprogramming, macros, quote, unquote] -sidebar_position: 15 ---- - -# Overview - -Metaprogramming in Noir is comprised of three parts: -1. `comptime` code -2. Quoting and unquoting -3. The metaprogramming API in `std::meta` - -Each of these are explained in more detail in the next sections but the wide picture is that -`comptime` allows us to write code which runs at compile-time. In this `comptime` code we -can quote and unquote snippets of the program, manipulate them, and insert them in other -parts of the program. Comptime functions which do this are said to be macros. Additionally, -there's a compile-time API of built-in types and functions provided by the compiler which allows -for greater analysis and modification of programs. - ---- - -# Comptime - -`comptime` is a new keyword in Noir which marks an item as executing or existing at compile-time. It can be used in several ways: - -- `comptime fn` to define functions which execute exclusively during compile-time. -- `comptime global` to define a global variable which is evaluated at compile-time. - - Unlike runtime globals, `comptime global`s can be mutable. -- `comptime { ... }` to execute a block of statements during compile-time. -- `comptime let` to define a variable whose value is evaluated at compile-time. -- `comptime for` to run a for loop at compile-time. Syntax sugar for `comptime { for .. }`. - -## Scoping - -Note that while in a `comptime` context, any runtime variables _local to the current function_ are never visible. - -## Evaluating - -Evaluation rules of `comptime` follows the normal unconstrained evaluation rules for other Noir code. There are a few things to note though: - -- Certain built-in functions may not be available, although more may be added over time. -- Evaluation order of global items is currently unspecified. For example, given the following two functions we can't guarantee -which `println` will execute first. The ordering of the two printouts will be arbitrary, but should be stable across multiple compilations with the same `nargo` version as long as the program is also unchanged. - -```rust -fn one() { - comptime { println("one"); } -} - -fn two() { - comptime { println("two"); } -} -``` - -- Since evaluation order is unspecified, care should be taken when using mutable globals so that they do not rely on a particular ordering. -For example, using globals to generate unique ids should be fine but relying on certain ids always being produced (especially after edits to the program) should be avoided. -- Although most ordering of globals is unspecified, two are: - - Dependencies of a crate will always be evaluated before the dependent crate. - - Any annotations on a function will be run before the function itself is resolved. This is to allow the annotation to modify the function if necessary. Note that if the - function itself was called at compile-time previously, it will already be resolved and cannot be modified. To prevent accidentally calling functions you wish to modify - at compile-time, it may be helpful to sort your `comptime` annotation functions into a different crate along with any dependencies they require. - -## Lowering - -When a `comptime` value is used in runtime code it must be lowered into a runtime value. This means replacing the expression with the literal that it evaluated to. For example, the code: - -```rust -struct Foo { array: [Field; 2], len: u32 } - -fn main() { - println(comptime { - let mut foo = std::mem::zeroed::(); - foo.array[0] = 4; - foo.len = 1; - foo - }); -} -``` - -will be converted to the following after `comptime` expressions are evaluated: - -```rust -struct Foo { array: [Field; 2], len: u32 } - -fn main() { - println(Foo { array: [4, 0], len: 1 }); -} -``` - -Not all types of values can be lowered. For example, `Type`s and `TypeDefinition`s (among other types) cannot be lowered at all. - -```rust -fn main() { - // There's nothing we could inline here to create a Type value at runtime - // let _ = get_type!(); -} - -comptime fn get_type() -> Type { ... } -``` - ---- - -# (Quasi) Quote - -Macros in Noir are `comptime` functions which return code as a value which is inserted into the call site when it is lowered there. -A code value in this case is of type `Quoted` and can be created by a `quote { ... }` expression. -More specifically, the code value `quote` creates is a token stream - a representation of source code as a series of words, numbers, string literals, or operators. -For example, the expression `quote { Hi "there reader"! }` would quote three tokens: the word "hi", the string "there reader", and an exclamation mark. -You'll note that snippets that would otherwise be invalid syntax can still be quoted. - -When a `Quoted` value is used in runtime code, it is lowered into a `quote { ... }` expression. Since this expression is only valid -in compile-time code however, we'd get an error if we tried this. Instead, we can use macro insertion to insert each token into the -program at that point, and parse it as an expression. To do this, we have to add a `!` after the function name returning the `Quoted` value. -If the value was created locally and there is no function returning it, `std::meta::unquote!(_)` can be used instead. -Calling such a function at compile-time without `!` will just return the `Quoted` value to be further manipulated. For example: - -```rust title="quote-example" showLineNumbers -comptime fn quote_one() -> Quoted { - quote { 1 } - } - - #[test] - fn returning_versus_macro_insertion() { - comptime - { - // let _a: Quoted = quote { 1 }; - let _a: Quoted = quote_one(); - - // let _b: i32 = 1; - let _b: i32 = quote_one!(); - } - } -``` -> Source code: noir_stdlib/src/meta/mod.nr#L120-L136 - - -For those familiar with quoting from other languages (primarily lisps), Noir's `quote` is actually a _quasiquote_. -This means we can escape the quoting by using the unquote operator to splice values in the middle of quoted code. - -# Unquote - -The unquote operator `$` is usable within a `quote` expression. -It takes a variable as an argument, evaluates the variable, and splices the resulting value into the quoted token stream at that point. For example, - -```rust -comptime { - let x = 1 + 2; - let y = quote { $x + 4 }; -} -``` - -The value of `y` above will be the token stream containing `3`, `+`, and `4`. We can also use this to combine `Quoted` values into larger token streams: - -```rust -comptime { - let x = quote { 1 + 2 }; - let y = quote { $x + 4 }; -} -``` - -The value of `y` above is now the token stream containing five tokens: `1 + 2 + 4`. - -Note that to unquote something, a variable name _must_ follow the `$` operator in a token stream. -If it is an expression (even a parenthesized one), it will do nothing. Most likely a parse error will be given when the macro is later unquoted. - -Unquoting can also be avoided by escaping the `$` with a backslash: - -``` -comptime { - let x = quote { 1 + 2 }; - - // y contains the four tokens: `$x + 4` - let y = quote { \$x + 4 }; -} -``` - ---- - -# Annotations - -Annotations provide a way to run a `comptime` function on an item in the program. -When you use an annotation, the function with the same name will be called with that item as an argument: - -```rust -#[my_struct_annotation] -struct Foo {} - -comptime fn my_struct_annotation(s: StructDefinition) { - println("Called my_struct_annotation!"); -} - -#[my_function_annotation] -fn foo() {} - -comptime fn my_function_annotation(f: FunctionDefinition) { - println("Called my_function_annotation!"); -} -``` - -Anything returned from one of these functions will be inserted at top-level along with the original item. -Note that expressions are not valid at top-level so you'll get an error trying to return `3` or similar just as if you tried to write a program containing `3; struct Foo {}`. -You can insert other top-level items such as trait impls, structs, or functions this way though. -For example, this is the mechanism used to insert additional trait implementations into the program when deriving a trait impl from a struct: - -```rust title="derive-field-count-example" showLineNumbers -trait FieldCount { - fn field_count() -> u32; - } - - #[derive_field_count] - struct Bar { x: Field, y: [Field; 2] } - - comptime fn derive_field_count(s: StructDefinition) -> Quoted { - let typ = s.as_type(); - let field_count = s.fields().len(); - quote { - impl FieldCount for $typ { - fn field_count() -> u32 { - $field_count - } - } - } - } -``` -> Source code: noir_stdlib/src/meta/mod.nr#L138-L157 - - -## Calling annotations with additional arguments - -Arguments may optionally be given to annotations. -When this is done, these additional arguments are passed to the annotation function after the item argument. - -```rust title="annotation-arguments-example" showLineNumbers -#[assert_field_is_type(quote { i32 }.as_type())] - struct MyStruct { my_field: i32 } - - comptime fn assert_field_is_type(s: StructDefinition, typ: Type) { - // Assert the first field in `s` has type `typ` - let fields = s.fields(); - assert_eq(fields[0].1, typ); - } -``` -> Source code: noir_stdlib/src/meta/mod.nr#L159-L168 - - -We can also take any number of arguments by adding the `varargs` annotation: - -```rust title="annotation-varargs-example" showLineNumbers -#[assert_three_args(1, 2, 3)] - struct MyOtherStruct { my_other_field: u32 } - - #[varargs] - comptime fn assert_three_args(_s: StructDefinition, args: [Field]) { - assert_eq(args.len(), 3); - } -``` -> Source code: noir_stdlib/src/meta/mod.nr#L170-L178 - - ---- - -# Comptime API - -Although `comptime`, `quote`, and unquoting provide a flexible base for writing macros, -Noir's true metaprogramming ability comes from being able to interact with the compiler through a compile-time API. -This API can be accessed through built-in functions in `std::meta` as well as on methods of several `comptime` types. - -The following is an incomplete list of some `comptime` types along with some useful methods on them. - -- `Quoted`: A token stream -- `Type`: The type of a Noir type - - `fn implements(self, constraint: TraitConstraint) -> bool` - - Returns true if `self` implements the given trait constraint -- `Expr`: A syntactically valid expression. Can be used to recur on a program's parse tree to inspect how it is structured. - - Methods: - - `fn as_function_call(self) -> Option<(Expr, [Expr])>` - - If this is a function call expression, return `(function, arguments)` - - `fn as_block(self) -> Option<[Expr]>` - - If this is a block, return each statement in the block -- `FunctionDefinition`: A function definition - - Methods: - - `fn parameters(self) -> [(Quoted, Type)]` - - Returns a slice of `(name, type)` pairs for each parameter -- `StructDefinition`: A struct definition - - Methods: - - `fn as_type(self) -> Type` - - Returns this `StructDefinition` as a `Type`. Any generics are kept as-is - - `fn generics(self) -> [Quoted]` - - Return the name of each generic on this struct - - `fn fields(self) -> [(Quoted, Type)]` - - Return the name and type of each field -- `TraitConstraint`: A trait constraint such as `From` -- `TypedExpr`: A type-checked expression. -- `UnresolvedType`: A syntactic notation that refers to a Noir type that hasn't been resolved yet - -There are many more functions available by exploring the `std::meta` module and its submodules. -Using these methods is the key to writing powerful metaprogramming libraries. - ---- - -# Example: Derive - -Using all of the above, we can write a `derive` macro that behaves similarly to Rust's but is not built into the language. -From the user's perspective it will look like this: - -```rust -// Example usage -#[derive(Default, Eq, Ord)] -struct MyStruct { my_field: u32 } -``` - -To implement `derive` we'll have to create a `comptime` function that accepts -a variable amount of traits. - -```rust title="derive_example" showLineNumbers -// These are needed for the unconstrained hashmap we're using to store derive functions -use crate::collections::umap::UHashMap; -use crate::hash::BuildHasherDefault; -use crate::hash::poseidon2::Poseidon2Hasher; - -// A derive function is one that given a struct definition can -// create us a quoted trait impl from it. -type DeriveFunction = fn(StructDefinition) -> Quoted; - -// We'll keep a global HANDLERS map to keep track of the derive handler for each trait -comptime mut global HANDLERS: UHashMap> = UHashMap::default(); - -// Given a struct and a slice of traits to derive, create trait impls for each. -// This function is as simple as iterating over the slice, checking if we have a trait -// handler registered for the given trait, calling it, and appending the result. -#[varargs] -pub comptime fn derive(s: StructDefinition, traits: [TraitDefinition]) -> Quoted { - let mut result = quote {}; - - for trait_to_derive in traits { - let handler = unsafe { - HANDLERS.get(trait_to_derive) - }; - assert(handler.is_some(), f"No derive function registered for `{trait_to_derive}`"); - - let trait_impl = handler.unwrap()(s); - result = quote { $result $trait_impl }; - } - - result -} -``` -> Source code: noir_stdlib/src/meta/mod.nr#L30-L64 - - -Registering a derive function could be done as follows: - -```rust title="derive_via" showLineNumbers -// To register a handler for a trait, just add it to our handlers map -pub comptime fn derive_via(t: TraitDefinition, f: DeriveFunction) { - HANDLERS.insert(t, f); -} -``` -> Source code: noir_stdlib/src/meta/mod.nr#L66-L73 - - -```rust title="big-derive-usage-example" showLineNumbers -// Finally, to register a handler we call the above function as an annotation - // with our handler function. - #[derive_via(derive_do_nothing)] - trait DoNothing { - fn do_nothing(self); - } - - comptime fn derive_do_nothing(s: StructDefinition) -> Quoted { - // This is simplified since we don't handle generics or where clauses! - // In a real example we'd likely also need to introduce each of - // `s.generics()` as well as a trait constraint for each generic - // to ensure they also implement the trait. - let typ = s.as_type(); - quote { - impl DoNothing for $typ { - fn do_nothing(self) { - // Traits can't tell us what to do - println("something"); - } - } - } - } - - // Since `DoNothing` is a simple trait which: - // 1. Only has one method - // 2. Does not have any generics on the trait itself - // We can use `std::meta::make_trait_impl` to help us out. - // This helper function will generate our impl for us along with any - // necessary where clauses and still provides a flexible interface - // for us to work on each field on the struct. - comptime fn derive_do_nothing_alt(s: StructDefinition) -> Quoted { - let trait_name = quote { DoNothing }; - let method_signature = quote { fn do_nothing(self) }; - - // Call `do_nothing` recursively on each field in the struct - let for_each_field = |field_name| quote { self.$field_name.do_nothing(); }; - - // Some traits like Eq want to join each field expression with something like `&`. - // We don't need that here - let join_fields_with = quote {}; - - // The body function is a spot to insert any extra setup/teardown needed. - // We'll insert our println here. Since we recur on each field, we should see - // one println for the struct itself, followed by a println for every field (recursively). - let body = |body| quote { - println("something"); - $body - }; - crate::meta::make_trait_impl( - s, - trait_name, - method_signature, - for_each_field, - join_fields_with, - body - ) - } -``` -> Source code: noir_stdlib/src/meta/mod.nr#L180-L238 - diff --git a/docs/versioned_docs/version-v0.34.0/noir/concepts/control_flow.md b/docs/versioned_docs/version-v0.34.0/noir/concepts/control_flow.md deleted file mode 100644 index 045d3c3a5f5..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/concepts/control_flow.md +++ /dev/null @@ -1,77 +0,0 @@ ---- -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] -sidebar_position: 2 ---- - -## 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); -``` - -## 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`. - -### Break and Continue - -In unconstrained code, `break` and `continue` are also allowed in `for` loops. These are only allowed -in unconstrained code since normal constrained code requires that Noir knows exactly how many iterations -a loop may have. `break` and `continue` can be used like so: - -```rust -for i in 0 .. 10 { - println("Iteration start") - - if i == 2 { - continue; - } - - if i == 5 { - break; - } - - println(i); -} -println("Loop end") -``` - -When used, `break` will end the current loop early and jump to the statement after the for loop. In the example -above, the `break` will stop the loop and jump to the `println("Loop end")`. - -`continue` will stop the current iteration of the loop, and jump to the start of the next iteration. In the example -above, `continue` will jump to `println("Iteration start")` when used. Note that the loop continues as normal after this. -The iteration variable `i` is still increased by one as normal when `continue` is used. - -`break` and `continue` cannot currently be used to jump out of more than a single loop at a time. diff --git a/docs/versioned_docs/version-v0.34.0/noir/concepts/data_bus.mdx b/docs/versioned_docs/version-v0.34.0/noir/concepts/data_bus.mdx deleted file mode 100644 index e55e58622ce..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/concepts/data_bus.mdx +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: Data Bus -sidebar_position: 13 ---- -import Experimental from '@site/src/components/Notes/_experimental.mdx'; - - - -The data bus is an optimization that the backend can use to make recursion more efficient. -In order to use it, you must define some inputs of the program entry points (usually the `main()` -function) with the `call_data` modifier, and the return values with the `return_data` modifier. -These modifiers are incompatible with `pub` and `mut` modifiers. - -## Example - -```rust -fn main(mut x: u32, y: call_data u32, z: call_data [u32;4] ) -> return_data u32 { - let a = z[x]; - a+y -} -``` - -As a result, both call_data and return_data will be treated as private inputs and encapsulated into a read-only array each, for the backend to process. diff --git a/docs/versioned_docs/version-v0.34.0/noir/concepts/data_types/_category_.json b/docs/versioned_docs/version-v0.34.0/noir/concepts/data_types/_category_.json deleted file mode 100644 index 5d694210bbf..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/concepts/data_types/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "position": 0, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.34.0/noir/concepts/data_types/arrays.md b/docs/versioned_docs/version-v0.34.0/noir/concepts/data_types/arrays.md deleted file mode 100644 index d0d8eb70aa6..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/concepts/data_types/arrays.md +++ /dev/null @@ -1,275 +0,0 @@ ---- -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, - ] -sidebar_position: 4 ---- - -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.mdx), 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]; -``` - -However, multidimensional slices are not supported. For example, the following code will error at compile time: - -```rust -let slice : [[Field]] = &[]; -``` - -## 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. -Each of these functions are located within the generic impl `impl [T; N] {`. -So anywhere `self` appears, it refers to the variable `self: [T; N]`. - -### len - -Returns the length of an array - -```rust -fn len(self) -> 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(self) -> [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. The ordering function must return true if the first argument should be sorted to be before the second argument or is equal to the second argument. - -Using this method with an operator like `<` that does not return `true` for equal values will result in an assertion failure for arrays with equal elements. - -```rust -fn sort_via(self, 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(self, 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(self, 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 the starting element. - -```rust -fn reduce(self, 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(self, 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(self, 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); -} - -``` - -### as_str_unchecked - -Converts a byte array of type `[u8; N]` to a string. Note that this performs no UTF-8 validation - -the given array is interpreted as-is as a string. - -```rust -impl [u8; N] { - pub fn as_str_unchecked(self) -> str -} -``` - -example: - -```rust -fn main() { - let hi = [104, 105].as_str_unchecked(); - assert_eq(hi, "hi"); -} -``` diff --git a/docs/versioned_docs/version-v0.34.0/noir/concepts/data_types/booleans.md b/docs/versioned_docs/version-v0.34.0/noir/concepts/data_types/booleans.md deleted file mode 100644 index 2507af710e7..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/concepts/data_types/booleans.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -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, - ] -sidebar_position: 2 ---- - - -The `bool` type in Noir has two possible values: `true` and `false`: - -```rust -fn main() { - let t = true; - let f: bool = false; -} -``` - -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.md) and -[Assert Function](../assert.md) sections. diff --git a/docs/versioned_docs/version-v0.34.0/noir/concepts/data_types/fields.md b/docs/versioned_docs/version-v0.34.0/noir/concepts/data_types/fields.md deleted file mode 100644 index 23367a32451..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/concepts/data_types/fields.md +++ /dev/null @@ -1,240 +0,0 @@ ---- -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, - ] -sidebar_position: 0 ---- - -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 title="to_le_bits" showLineNumbers -pub fn to_le_bits(self: Self) -> [u1; N] {} -``` -> Source code: noir_stdlib/src/field/mod.nr#L32-L34 - - -example: - -```rust title="to_le_bits_example" showLineNumbers -fn test_to_le_bits() { - let field = 2; - let bits: [u1; 8] = field.to_le_bits(); - assert_eq(bits, [0, 1, 0, 0, 0, 0, 0, 0]); - } -``` -> Source code: noir_stdlib/src/field/mod.nr#L196-L202 - - - -### to_be_bits - -Transforms the field into an array of bits, Big Endian. - -```rust title="to_be_bits" showLineNumbers -pub fn to_be_bits(self: Self) -> [u1; N] {} -``` -> Source code: noir_stdlib/src/field/mod.nr#L48-L50 - - -example: - -```rust title="to_be_bits_example" showLineNumbers -fn test_to_be_bits() { - let field = 2; - let bits: [u1; 8] = field.to_be_bits(); - assert_eq(bits, [0, 0, 0, 0, 0, 0, 1, 0]); - } -``` -> Source code: noir_stdlib/src/field/mod.nr#L187-L193 - - - -### to_le_bytes - -Transforms into an array of bytes, Little Endian - -```rust title="to_le_bytes" showLineNumbers -pub fn to_le_bytes(self: Self) -> [u8; N] { - self.to_le_radix(256) - } -``` -> Source code: noir_stdlib/src/field/mod.nr#L63-L67 - - -example: - -```rust title="to_le_bytes_example" showLineNumbers -fn test_to_le_bytes() { - let field = 2; - let bits: [u8; 8] = field.to_le_bytes(); - assert_eq(bits, [2, 0, 0, 0, 0, 0, 0, 0]); - } -``` -> Source code: noir_stdlib/src/field/mod.nr#L214-L220 - - -### to_be_bytes - -Transforms into an array of bytes, Big Endian - -```rust title="to_be_bytes" showLineNumbers -pub fn to_be_bytes(self: Self) -> [u8; N] { - self.to_be_radix(256) - } -``` -> Source code: noir_stdlib/src/field/mod.nr#L80-L84 - - -example: - -```rust title="to_be_bytes_example" showLineNumbers -fn test_to_be_bytes() { - let field = 2; - let bits: [u8; 8] = field.to_be_bytes(); - assert_eq(bits, [0, 0, 0, 0, 0, 0, 0, 2]); - } -``` -> Source code: noir_stdlib/src/field/mod.nr#L205-L211 - - - -### to_le_radix - -Decomposes into an array over the specified base, Little Endian - -```rust title="to_le_radix" showLineNumbers -pub fn to_le_radix(self: Self, radix: u32) -> [u8; N] { - crate::assert_constant(radix); - self.__to_le_radix(radix) - } -``` -> Source code: noir_stdlib/src/field/mod.nr#L86-L91 - - - -example: - -```rust title="to_le_radix_example" showLineNumbers -fn test_to_le_radix() { - let field = 2; - let bits: [u8; 8] = field.to_le_radix(256); - assert_eq(bits, [2, 0, 0, 0, 0, 0, 0, 0]); - } -``` -> Source code: noir_stdlib/src/field/mod.nr#L232-L238 - - - -### to_be_radix - -Decomposes into an array over the specified base, Big Endian - -```rust title="to_be_radix" showLineNumbers -pub fn to_be_radix(self: Self, radix: u32) -> [u8; N] { - crate::assert_constant(radix); - self.__to_be_radix(radix) - } -``` -> Source code: noir_stdlib/src/field/mod.nr#L93-L98 - - -example: - -```rust title="to_be_radix_example" showLineNumbers -fn test_to_be_radix() { - let field = 2; - let bits: [u8; 8] = field.to_be_radix(256); - assert_eq(bits, [0, 0, 0, 0, 0, 0, 0, 2]); - } -``` -> Source code: noir_stdlib/src/field/mod.nr#L223-L229 - - - -### 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); -} -``` - -### assert_max_bit_size - -Adds a constraint to specify that the field can be represented with `bit_size` number of bits - -```rust title="assert_max_bit_size" showLineNumbers -pub fn assert_max_bit_size(self, bit_size: u32) { -``` -> Source code: noir_stdlib/src/field/mod.nr#L9-L11 - - -example: - -```rust -fn main() { - let field = 2 - field.assert_max_bit_size(32); -} -``` - -### 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 -``` - - -### lt - -Returns true if the field is less than the other field - -```rust -pub fn lt(self, another: Field) -> bool -``` diff --git a/docs/versioned_docs/version-v0.34.0/noir/concepts/data_types/function_types.md b/docs/versioned_docs/version-v0.34.0/noir/concepts/data_types/function_types.md deleted file mode 100644 index f6121af17e2..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/concepts/data_types/function_types.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: Function types -sidebar_position: 10 ---- - -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](../lambdas.md) for more details. diff --git a/docs/versioned_docs/version-v0.34.0/noir/concepts/data_types/index.md b/docs/versioned_docs/version-v0.34.0/noir/concepts/data_types/index.md deleted file mode 100644 index 3eadb2dc8a4..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/concepts/data_types/index.md +++ /dev/null @@ -1,118 +0,0 @@ ---- -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](../generics.md): - -```rust -type Id = Size; - -fn main() { - let id: Id = 1; - let zero: u32 = 0; - assert(zero + 1 == id); -} -``` - -Type aliases can even refer to other aliases. An error will be issued if they form a cycle: - -```rust -// Ok! -type A = B; -type B = Field; - -type Bad1 = Bad2; - -// error: Dependency cycle found -type Bad2 = Bad1; -// ^^^^^^^^^^^ 'Bad2' recursively depends on itself: Bad2 -> Bad1 -> Bad2 -``` - -## Wildcard Type -Noir can usually infer the type of the variable from the context, so specifying the type of a variable is only required when it cannot be inferred. However, specifying a complex type can be tedious, especially when it has multiple generic arguments. Often some of the generic types can be inferred from the context, and Noir only needs a hint to properly infer the other types. We can partially specify a variable's type by using `_` as a marker, indicating where we still want the compiler to infer the type. - -```rust -let a: [_; 4] = foo(b); -``` - - -### BigInt - -You can achieve BigInt functionality using the [Noir BigInt](https://github.com/shuklaayush/noir-bigint) library. diff --git a/docs/versioned_docs/version-v0.34.0/noir/concepts/data_types/integers.md b/docs/versioned_docs/version-v0.34.0/noir/concepts/data_types/integers.md deleted file mode 100644 index a1d59bf3166..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/concepts/data_types/integers.md +++ /dev/null @@ -1,156 +0,0 @@ ---- -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] -sidebar_position: 1 ---- - -An integer type is a range constrained field type. -The Noir frontend supports both unsigned and signed integer types. -The allowed sizes are 1, 8, 16, 32 and 64 bits. - -:::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\\$). - -## 128 bits Unsigned Integers - -The built-in structure `U128` allows you to use 128-bit unsigned integers almost like a native integer type. However, there are some differences to keep in mind: -- You cannot cast between a native integer and `U128` -- There is a higher performance cost when using `U128`, compared to a native type. - -Conversion between unsigned integer types and U128 are done through the use of `from_integer` and `to_integer` functions. `from_integer` also accepts the `Field` type as input. - -```rust -fn main() { - let x = U128::from_integer(23); - let y = U128::from_hex("0x7"); - let z = x + y; - assert(z.to_integer() == 30); -} -``` - -`U128` is implemented with two 64 bits limbs, representing the low and high bits, which explains the performance cost. You should expect `U128` to be twice more costly for addition and four times more costly for multiplication. -You can construct a U128 from its limbs: -```rust -fn main(x: u64, y: u64) { - let x = U128::from_u64s_be(x,y); - assert(z.hi == x as Field); - assert(z.lo == y as Field); -} -``` - -Note that the limbs are stored as Field elements in order to avoid unnecessary conversions. -Apart from this, most operations will work as usual: - -```rust -fn main(x: U128, y: U128) { - // multiplication - let c = x * y; - // addition and subtraction - let c = c - x + y; - // division - let c = x / y; - // bit operation; - let c = x & y | y; - // bit shift - let c = x << y; - // comparisons; - let c = x < y; - let c = x == y; -} -``` - -## 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 execute -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 - -fn main(x: u8, y: u8) -> pub u8 { - std::wrapping_add(x, y) -} -``` diff --git a/docs/versioned_docs/version-v0.34.0/noir/concepts/data_types/references.md b/docs/versioned_docs/version-v0.34.0/noir/concepts/data_types/references.md deleted file mode 100644 index a5293d11cfb..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/concepts/data_types/references.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: References -sidebar_position: 9 ---- - -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.34.0/noir/concepts/data_types/slices.mdx b/docs/versioned_docs/version-v0.34.0/noir/concepts/data_types/slices.mdx deleted file mode 100644 index a0c87c29259..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/concepts/data_types/slices.mdx +++ /dev/null @@ -1,358 +0,0 @@ ---- -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] -sidebar_position: 5 ---- - -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 -fn main() -> pub u32 { - let mut slice: [Field] = &[0; 2]; - - let mut new_slice = slice.push_back(6); - new_slice.len() -} -``` - -To write a slice literal, use a preceding ampersand as in: `&[0; 2]` or -`&[1, 2, 3]`. - -It is important to note that slices are not references to arrays. In Noir, -`&[..]` is more similar to an immutable, growable vector. - -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); -``` - -### len - -Returns the length of a slice - -```rust -fn len(self) -> Field -``` - -Example: - -```rust -fn main() { - let slice = &[42, 42]; - assert(slice.len() == 2); -} -``` - -### as_array - -Converts this slice into an array. - -Make sure to specify the size of the resulting array. -Panics if the resulting array length is different than the slice's length. - -```rust -fn as_array(self) -> [T; N] -``` - -Example: - -```rust -fn main() { - let slice = &[5, 6]; - - // Always specify the length of the resulting array! - let array: [Field; 2] = slice.as_array(); - - assert(array[0] == slice[0]); - assert(array[1] == slice[1]); -} -``` - -### map - -Applies a function to each element of the slice, returning a new slice containing the mapped elements. - -```rust -fn map(self, f: fn[Env](T) -> U) -> [U] -``` - -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 slice, returning the final accumulated value. The first -parameter is the initial value. - -```rust -fn fold(self, mut accumulator: U, f: fn[Env](U, T) -> U) -> U -``` - -This is a left fold, so the given function will be applied to the accumulator and first element of -the slice, 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 slice = &[2, 2, 2, 2, 2]; - let folded = slice.fold(0, |a, b| a + b); - assert(folded == 10); -} - -``` - -### reduce - -Same as fold, but uses the first element as the starting element. - -```rust -fn reduce(self, f: fn[Env](T, T) -> T) -> T -``` - -example: - -```rust -fn main() { - let slice = &[2, 2, 2, 2, 2]; - let reduced = slice.reduce(|a, b| a + b); - assert(reduced == 10); -} -``` - -### filter - -Returns a new slice containing only elements for which the given predicate returns true. - -```rust -fn filter(self, f: fn[Env](T) -> bool) -> Self -``` - -example: - -```rust -fn main() { - let slice = &[1, 2, 3, 4, 5]; - let odds = slice.filter(|x| x % 2 == 1); - assert_eq(odds, &[1, 3, 5]); -} -``` - -### join - -Flatten each element in the slice into one value, separated by `separator`. - -Note that although slices implement `Append`, `join` cannot be used on slice -elements since nested slices are prohibited. - -```rust -fn join(self, separator: T) -> T where T: Append -``` - -example: - -```rust -struct Accumulator { - total: Field, -} - -// "Append" two accumulators by adding them -impl Append for Accumulator { - fn empty() -> Self { - Self { total: 0 } - } - - fn append(self, other: Self) -> Self { - Self { total: self.total + other.total } - } -} - -fn main() { - let slice = &[1, 2, 3, 4, 5].map(|total| Accumulator { total }); - - let result = slice.join(Accumulator::empty()); - assert_eq(result, Accumulator { total: 15 }); - - // We can use a non-empty separator to insert additional elements to sum: - let separator = Accumulator { total: 10 }; - let result = slice.join(separator); - assert_eq(result, Accumulator { total: 55 }); -} -``` - -### all - -Returns true if all the elements satisfy the given predicate - -```rust -fn all(self, predicate: fn[Env](T) -> bool) -> bool -``` - -example: - -```rust -fn main() { - let slice = &[2, 2, 2, 2, 2]; - let all = slice.all(|a| a == 2); - assert(all); -} -``` - -### any - -Returns true if any of the elements satisfy the given predicate - -```rust -fn any(self, predicate: fn[Env](T) -> bool) -> bool -``` - -example: - -```rust -fn main() { - let slice = &[2, 2, 2, 2, 5]; - let any = slice.any(|a| a == 5); - assert(any); -} - -``` diff --git a/docs/versioned_docs/version-v0.34.0/noir/concepts/data_types/strings.md b/docs/versioned_docs/version-v0.34.0/noir/concepts/data_types/strings.md deleted file mode 100644 index 1fdee42425e..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/concepts/data_types/strings.md +++ /dev/null @@ -1,79 +0,0 @@ ---- -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, - ] -sidebar_position: 3 ---- - - -The string type is a fixed length value defined with `str`. - -You can use strings in `assert()` functions or print them with -`println()`. See more about [Logging](../../standard_library/logging.md). - -```rust - -fn main(message : pub str<11>, hex_as_string : str<4>) { - 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" -``` - -## 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/versioned_docs/version-v0.34.0/noir/concepts/data_types/structs.md b/docs/versioned_docs/version-v0.34.0/noir/concepts/data_types/structs.md deleted file mode 100644 index dbf68c99813..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/concepts/data_types/structs.md +++ /dev/null @@ -1,70 +0,0 @@ ---- -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, - ] -sidebar_position: 8 ---- - -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.34.0/noir/concepts/data_types/tuples.md b/docs/versioned_docs/version-v0.34.0/noir/concepts/data_types/tuples.md deleted file mode 100644 index 2ec5c9c4113..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/concepts/data_types/tuples.md +++ /dev/null @@ -1,48 +0,0 @@ ---- -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, - ] -sidebar_position: 7 ---- - -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.34.0/noir/concepts/functions.md b/docs/versioned_docs/version-v0.34.0/noir/concepts/functions.md deleted file mode 100644 index f656cdfd97a..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/concepts/functions.md +++ /dev/null @@ -1,226 +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] -sidebar_position: 1 ---- - -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](../../tooling/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](./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](./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](./unconstrained.md) and [NoirJS](../../reference/NoirJS/noir_js/index.md) for more details. -- **test**: mark the function as unit tests. See [Tests](../../tooling/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.34.0/noir/concepts/generics.md b/docs/versioned_docs/version-v0.34.0/noir/concepts/generics.md deleted file mode 100644 index 1f313cef58e..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/concepts/generics.md +++ /dev/null @@ -1,258 +0,0 @@ ---- -title: Generics -description: Learn how to use Generics in Noir -keywords: [Noir, Rust, generics, functions, structs] -sidebar_position: 7 ---- - -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 print(self) { - for _i in 0 .. self.count { - println(self.value); - } - } -} - -fn main() { - let repeated = RepeatedValue { value: "Hello!", count: 2 }; - repeated.print(); -} -``` - -The `print` function will print `Hello!` an arbitrary number of times, twice in this case. - -## Numeric Generics - -If we want to be generic over array lengths (which are type-level integers), we can use numeric -generics. Using these looks similar to using regular generics, but introducing them into scope -requires declaring them with `let MyGenericName: IntegerType`. This can be done anywhere a normal -generic is declared. Instead of types, these generics resolve to integers at compile-time. -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 - -Since a generic type `T` can represent any type, how can we call functions on the underlying type? -In other words, how can we go from "any type `T`" to "any type `T` that has certain methods available?" - -This is what [traits](../concepts/traits.md) are for in Noir. Here's an example of a function generic over -any type `T` that implements the `Eq` trait for equality: - -```rust -fn first_element_is_equal(array1: [T; N], array2: [T; N]) -> bool - where T: Eq -{ - if (array1.len() == 0) | (array2.len() == 0) { - true - } else { - array1[0] == array2[0] - } -} - -fn main() { - assert(first_element_is_equal([1, 2, 3], [1, 5, 6])); - - // We can use first_element_is_equal for arrays of any type - // as long as we have an Eq impl for the types we pass in - let array = [MyStruct::new(), MyStruct::new()]; - assert(array_eq(array, array, MyStruct::eq)); -} - -impl Eq for MyStruct { - fn eq(self, other: MyStruct) -> bool { - self.foo == other.foo - } -} -``` - -You can find more details on traits and trait implementations on the [traits page](../concepts/traits.md). - -## Manually Specifying Generics with the Turbofish Operator - -There are times when the compiler cannot reasonably infer what type should be used for a generic, or when the developer themselves may want to manually distinguish generic type parameters. This is where the `::<>` turbofish operator comes into play. - -The `::<>` operator can follow a variable or path and can be used to manually specify generic arguments within the angle brackets. -The name "turbofish" comes from that `::<>` looks like a little fish. - -Examples: -```rust -fn main() { - let mut slice = []; - slice = slice.push_back(1); - slice = slice.push_back(2); - // Without turbofish a type annotation would be needed on the left hand side - let array = slice.as_array::<2>(); -} -``` -```rust -fn double() -> u32 { - N * 2 -} -fn example() { - assert(double::<9>() == 18); - assert(double::<7 + 8>() == 30); -} -``` -```rust -trait MyTrait { - fn ten() -> Self; -} - -impl MyTrait for Field { - fn ten() -> Self { 10 } -} - -struct Foo { - inner: T -} - -impl Foo { - fn generic_method(_self: Self) -> U where U: MyTrait { - U::ten() - } -} - -fn example() { - let foo: Foo = Foo { inner: 1 }; - // Using a type other than `Field` here (e.g. u32) would fail as - // there is no matching impl for `u32: MyTrait`. - // - // Substituting the `10` on the left hand side of this assert - // with `10 as u32` would also fail with a type mismatch as we - // are expecting a `Field` from the right hand side. - assert(10 as u32 == foo.generic_method::()); -} -``` - -## Arithmetic Generics - -In addition to numeric generics, Noir also allows a limited form of arithmetic on generics. -When you have a numeric generic such as `N`, you can use the following operators on it in a -type position: `+`, `-`, `*`, `/`, and `%`. - -Note that type checking arithmetic generics is a best effort guess from the compiler and there -are many cases of types that are equal that the compiler may not see as such. For example, -we know that `T * (N + M)` should be equal to `T*N + T*M` but the compiler does not currently -apply the distributive law and thus sees these as different types. - -Even with this limitation though, the compiler can handle common cases decently well: - -```rust -trait Serialize { - fn serialize(self) -> [Field; N]; -} - -impl Serialize<1> for Field { - fn serialize(self) -> [Field; 1] { - [self] - } -} - -impl Serialize for [T; N] - where T: Serialize { .. } - -impl Serialize for (T, U) - where T: Serialize, U: Serialize { .. } - -fn main() { - let data = (1, [2, 3, 4]); - assert_eq(data.serialize().len(), 4); -} -``` - -Note that if there is any over or underflow the types will fail to unify: - -```rust title="underflow-example" showLineNumbers -fn pop(array: [Field; N]) -> [Field; N - 1] { - let mut result: [Field; N - 1] = std::mem::zeroed(); - for i in 0..N - 1 { - result[i] = array[i]; - } - result -} - -fn main() { - // error: Could not determine array length `(0 - 1)` - pop([]); -} -``` -> Source code: test_programs/compile_failure/arithmetic_generics_underflow/src/main.nr#L1-L14 - - -This also applies if there is underflow in an intermediate calculation: - -```rust title="intermediate-underflow-example" showLineNumbers -fn main() { - // From main it looks like there's nothing sketchy going on - seems_fine([]); -} - -// Since `seems_fine` says it can receive and return any length N -fn seems_fine(array: [Field; N]) -> [Field; N] { - // But inside `seems_fine` we pop from the array which - // requires the length to be greater than zero. - - // error: Could not determine array length `(0 - 1)` - push_zero(pop(array)) -} - -fn pop(array: [Field; N]) -> [Field; N - 1] { - let mut result: [Field; N - 1] = std::mem::zeroed(); - for i in 0..N - 1 { - result[i] = array[i]; - } - result -} - -fn push_zero(array: [Field; N]) -> [Field; N + 1] { - let mut result: [Field; N + 1] = std::mem::zeroed(); - for i in 0..N { - result[i] = array[i]; - } - // index N is already zeroed - result -} -``` -> Source code: test_programs/compile_failure/arithmetic_generics_intermediate_underflow/src/main.nr#L1-L32 - diff --git a/docs/versioned_docs/version-v0.34.0/noir/concepts/globals.md b/docs/versioned_docs/version-v0.34.0/noir/concepts/globals.md deleted file mode 100644 index 063a3d89248..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/concepts/globals.md +++ /dev/null @@ -1,72 +0,0 @@ ---- -title: Global Variables -description: - Learn about global variables in Noir. Discover how - to declare, modify, and use them in your programs. -keywords: [noir programming language, globals, global variables, constants] -sidebar_position: 8 ---- - -## Globals - - -Noir supports global variables. The global's type can be inferred by the compiler entirely: - -```rust -global N = 5; // Same as `global N: Field = 5` - -global TUPLE = (3, 2); - -fn main() { - assert(N == 5); - assert(N == TUPLE.0 + TUPLE.1); -} -``` - -:::info - -Globals can be defined as any expression, so long as they don't depend on themselves - otherwise there would be a dependency cycle! For example: - -```rust -global T = foo(T); // dependency error -``` - -::: - - -If they are initialized to a literal integer, globals can be used to specify an array's length: - -```rust -global N: Field = 2; - -fn main(y : [Field; N]) { - assert(y[0] == y[1]) -} -``` - -A global from another module can be imported or referenced externally like any other name: - -```rust -global N = 20; - -fn main() { - assert(my_submodule::N != N); -} - -mod my_submodule { - global N: Field = 10; -} -``` - -When a global is used, Noir replaces the name with its definition on each occurrence. -This means globals defined using function calls will repeat the call each time they're used: - -```rust -global RESULT = foo(); - -fn foo() -> [Field; 100] { ... } -``` - -This is usually fine since Noir will generally optimize any function call that does not -refer to a program input into a constant. It should be kept in mind however, if the called -function performs side-effects like `println`, as these will still occur on each use. diff --git a/docs/versioned_docs/version-v0.34.0/noir/concepts/lambdas.md b/docs/versioned_docs/version-v0.34.0/noir/concepts/lambdas.md deleted file mode 100644 index be3c7e0b5ca..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/concepts/lambdas.md +++ /dev/null @@ -1,81 +0,0 @@ ---- -title: Lambdas -description: Learn how to use anonymous functions in Noir programming language. -keywords: [Noir programming language, lambda, closure, function, anonymous function] -sidebar_position: 9 ---- - -## 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.34.0/noir/concepts/mutability.md b/docs/versioned_docs/version-v0.34.0/noir/concepts/mutability.md deleted file mode 100644 index fdeef6a87c5..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/concepts/mutability.md +++ /dev/null @@ -1,121 +0,0 @@ ---- -title: Mutability -description: - Learn about mutable variables in Noir. Discover how - to declare, modify, and use them in your programs. -keywords: [noir programming language, mutability in noir, mutable variables] -sidebar_position: 8 ---- - -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; -} -``` - -## Non-local mutability - -Non-local mutability can be achieved through the mutable reference type `&mut T`: - -```rust -fn set_to_zero(x: &mut Field) { - *x = 0; -} - -fn main() { - let mut y = 42; - set_to_zero(&mut y); - assert(*y == 0); -} -``` - -When creating a mutable reference, the original variable being referred to (`y` in this -example) must also be mutable. Since mutable references are a reference type, they must -be explicitly dereferenced via `*` to retrieve the underlying value. Note that this yields -a copy of the value, so mutating this copy will not change the original value behind the -reference: - -```rust -fn main() { - let mut x = 1; - let x_ref = &mut x; - - let mut y = *x_ref; - let y_ref = &mut y; - - x = 2; - *x_ref = 3; - - y = 4; - *y_ref = 5; - - assert(x == 3); - assert(*x_ref == 3); - assert(y == 5); - assert(*y_ref == 5); -} -``` - -Note that types in Noir are actually deeply immutable so the copy that occurs when -dereferencing is only a conceptual copy - no additional constraints will occur. - -Mutable references can also be stored within structs. Note that there is also -no lifetime parameter on these unlike rust. This is because the allocated memory -always lasts the entire program - as if it were an array of one element. - -```rust -struct Foo { - x: &mut Field -} - -impl Foo { - fn incr(mut self) { - *self.x += 1; - } -} - -fn main() { - let foo = Foo { x: &mut 0 }; - foo.incr(); - assert(*foo.x == 1); -} -``` - -In general, you should avoid non-local & shared mutability unless it is needed. Sticking -to only local mutability will improve readability and potentially improve compiler optimizations as well. diff --git a/docs/versioned_docs/version-v0.34.0/noir/concepts/ops.md b/docs/versioned_docs/version-v0.34.0/noir/concepts/ops.md deleted file mode 100644 index c35c36c38a9..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/concepts/ops.md +++ /dev/null @@ -1,98 +0,0 @@ ---- -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, - ] -sidebar_position: 3 ---- - -# 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, shift must be u8 | -| >> | Right shift an integer by another integer amount | Types must be integer, shift must be u8 | -| ! | 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 identically 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.34.0/noir/concepts/oracles.mdx b/docs/versioned_docs/version-v0.34.0/noir/concepts/oracles.mdx deleted file mode 100644 index 77a2ac1550a..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/concepts/oracles.mdx +++ /dev/null @@ -1,29 +0,0 @@ ---- -title: Oracles -description: Dive into how Noir supports Oracles via RPC calls, and learn how to declare an Oracle in Noir with our comprehensive guide. -keywords: - - Noir - - Oracles - - RPC Calls - - Unconstrained Functions - - Programming - - Blockchain -sidebar_position: 6 ---- - -import Experimental from '@site/src/components/Notes/_experimental.mdx'; - - - -Noir has support for Oracles via RPC calls. This means Noir will make an RPC call and use the return value for proof generation. - -Since Oracles are not resolved by Noir, they are [`unconstrained` functions](./unconstrained.md) - -You can declare an Oracle through the `#[oracle()]` flag. Example: - -```rust -#[oracle(get_number_sequence)] -unconstrained fn get_number_sequence(_size: Field) -> [Field] {} -``` - -The timeout for when using an external RPC oracle resolver can be set with the `NARGO_FOREIGN_CALL_TIMEOUT` environment variable. This timeout is in units of milliseconds. diff --git a/docs/versioned_docs/version-v0.34.0/noir/concepts/shadowing.md b/docs/versioned_docs/version-v0.34.0/noir/concepts/shadowing.md deleted file mode 100644 index 5ce6130d201..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/concepts/shadowing.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -title: Shadowing -sidebar_position: 12 ---- - -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.34.0/noir/concepts/traits.md b/docs/versioned_docs/version-v0.34.0/noir/concepts/traits.md deleted file mode 100644 index 597c62c737c..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/concepts/traits.md +++ /dev/null @@ -1,465 +0,0 @@ ---- -title: Traits -description: - Traits in Noir can be used to abstract out a common interface for functions across - several data types. -keywords: [noir programming language, traits, interfaces, generic, protocol] -sidebar_position: 14 ---- - -## Overview - -Traits in Noir are a useful abstraction similar to interfaces or protocols in other languages. Each trait defines -the interface of several methods contained within the trait. Types can then implement this trait by providing -implementations for these methods. For example in the program: - -```rust -struct Rectangle { - width: Field, - height: Field, -} - -impl Rectangle { - fn area(self) -> Field { - self.width * self.height - } -} - -fn log_area(r: Rectangle) { - println(r.area()); -} -``` - -We have a function `log_area` to log the area of a `Rectangle`. Now how should we change the program if we want this -function to work on `Triangle`s as well?: - -```rust -struct Triangle { - width: Field, - height: Field, -} - -impl Triangle { - fn area(self) -> Field { - self.width * self.height / 2 - } -} -``` - -Making `log_area` generic over all types `T` would be invalid since not all types have an `area` method. Instead, we can -introduce a new `Area` trait and make `log_area` generic over all types `T` that implement `Area`: - -```rust -trait Area { - fn area(self) -> Field; -} - -fn log_area(shape: T) where T: Area { - println(shape.area()); -} -``` - -We also need to explicitly implement `Area` for `Rectangle` and `Triangle`. We can do that by changing their existing -impls slightly. Note that the parameter types and return type of each of our `area` methods must match those defined -by the `Area` trait. - -```rust -impl Area for Rectangle { - fn area(self) -> Field { - self.width * self.height - } -} - -impl Area for Triangle { - fn area(self) -> Field { - self.width * self.height / 2 - } -} -``` - -Now we have a working program that is generic over any type of Shape that is used! Others can even use this program -as a library with their own types - such as `Circle` - as long as they also implement `Area` for these types. - -## Where Clauses - -As seen in `log_area` above, when we want to create a function or method that is generic over any type that implements -a trait, we can add a where clause to the generic function. - -```rust -fn log_area(shape: T) where T: Area { - println(shape.area()); -} -``` - -It is also possible to apply multiple trait constraints on the same variable at once by combining traits with the `+` -operator. Similarly, we can have multiple trait constraints by separating each with a comma: - -```rust -fn foo(elements: [T], thing: U) where - T: Default + Add + Eq, - U: Bar, -{ - let mut sum = T::default(); - - for element in elements { - sum += element; - } - - if sum == T::default() { - thing.bar(); - } -} -``` - -## Generic Implementations - -You can add generics to a trait implementation by adding the generic list after the `impl` keyword: - -```rust -trait Second { - fn second(self) -> Field; -} - -impl Second for (T, Field) { - fn second(self) -> Field { - self.1 - } -} -``` - -You can also implement a trait for every type this way: - -```rust -trait Debug { - fn debug(self); -} - -impl Debug for T { - fn debug(self) { - println(self); - } -} - -fn main() { - 1.debug(); -} -``` - -### Generic Trait Implementations With Where Clauses - -Where clauses can be placed on trait implementations themselves to restrict generics in a similar way. -For example, while `impl Foo for T` implements the trait `Foo` for every type, `impl Foo for T where T: Bar` -will implement `Foo` only for types that also implement `Bar`. This is often used for implementing generic types. -For example, here is the implementation for array equality: - -```rust -impl Eq for [T; N] where T: Eq { - // Test if two arrays have the same elements. - // Because both arrays must have length N, we know their lengths already match. - fn eq(self, other: Self) -> bool { - let mut result = true; - - for i in 0 .. self.len() { - // The T: Eq constraint is needed to call == on the array elements here - result &= self[i] == other[i]; - } - - result - } -} -``` - -Where clauses can also be placed on struct implementations. -For example, here is a method utilizing a generic type that implements the equality trait. - -```rust -struct Foo { - a: u32, - b: T, -} - -impl Foo where T: Eq { - fn eq(self, other: Self) -> bool { - (self.a == other.a) & self.b.eq(other.b) - } -} -``` - -## Generic Traits - -Traits themselves can also be generic by placing the generic arguments after the trait name. These generics are in -scope of every item within the trait. - -```rust -trait Into { - // Convert `self` to type `T` - fn into(self) -> T; -} -``` - -When implementing generic traits the generic arguments of the trait must be specified. This is also true anytime -when referencing a generic trait (e.g. in a `where` clause). - -```rust -struct MyStruct { - array: [Field; 2], -} - -impl Into<[Field; 2]> for MyStruct { - fn into(self) -> [Field; 2] { - self.array - } -} - -fn as_array(x: T) -> [Field; 2] - where T: Into<[Field; 2]> -{ - x.into() -} - -fn main() { - let array = [1, 2]; - let my_struct = MyStruct { array }; - - assert_eq(as_array(my_struct), array); -} -``` - -### Associated Types and Constants - -Traits also support associated types and constraints which can be thought of as additional generics that are referred to by name. - -Here's an example of a trait with an associated type `Foo` and a constant `Bar`: - -```rust -trait MyTrait { - type Foo; - - let Bar: u32; -} -``` - -Now when we're implementing `MyTrait` we also have to provide values for `Foo` and `Bar`: - -```rust -impl MyTrait for Field { - type Foo = i32; - - let Bar: u32 = 11; -} -``` - -Since associated constants can also be used in a type position, its values are limited to only other -expression kinds allowed in numeric generics. - -Note that currently all associated types and constants must be explicitly specified in a trait constraint. -If we leave out any, we'll get an error that we're missing one: - -```rust -// Error! Constraint is missing associated constant for `Bar` -fn foo(x: T) where T: MyTrait { - ... -} -``` - -Because all associated types and constants must be explicitly specified, they are essentially named generics, -although this is set to change in the future. Future versions of Noir will allow users to elide associated types -in trait constraints similar to Rust. When this is done, you may still refer to their value with the `::AssociatedType` -syntax: - -```rust -// Only valid in future versions of Noir: -fn foo(x: T) where T: MyTrait { - let _: ::Foo = ...; -} -``` - -The type as trait syntax is possible in Noir today but is less useful when each type must be explicitly specified anyway: - -```rust -fn foo(x: T) where T: MyTrait { - // Works, but could just use F directly - let _: >::Foo = ...; - - let _: F = ...; -} -``` - -## Trait Methods With No `self` - -A trait can contain any number of methods, each of which have access to the `Self` type which represents each type -that eventually implements the trait. Similarly, the `self` variable is available as well but is not required to be used. -For example, we can define a trait to create a default value for a type. This trait will need to return the `Self` type -but doesn't need to take any parameters: - -```rust -trait Default { - fn default() -> Self; -} -``` - -Implementing this trait can be done similarly to any other trait: - -```rust -impl Default for Field { - fn default() -> Field { - 0 - } -} - -struct MyType {} - -impl Default for MyType { - fn default() -> Field { - MyType {} - } -} -``` - -However, since there is no `self` parameter, we cannot call it via the method call syntax `object.method()`. -Instead, we'll need to refer to the function directly. This can be done either by referring to the -specific impl `MyType::default()` or referring to the trait itself `Default::default()`. In the later -case, type inference determines the impl that is selected. - -```rust -let my_struct = MyStruct::default(); - -let x: Field = Default::default(); -let result = x + Default::default(); -``` - -:::warning - -```rust -let _ = Default::default(); -``` - -If type inference cannot select which impl to use because of an ambiguous `Self` type, an impl will be -arbitrarily selected. This occurs most often when the result of a trait function call with no parameters -is unused. To avoid this, when calling a trait function with no `self` or `Self` parameters or return type, -always refer to it via the implementation type's namespace - e.g. `MyType::default()`. -This is set to change to an error in future Noir versions. - -::: - -## Default Method Implementations - -A trait can also have default implementations of its methods by giving a body to the desired functions. -Note that this body must be valid for all types that may implement the trait. As a result, the only -valid operations on `self` will be operations valid for any type or other operations on the trait itself. - -```rust -trait Numeric { - fn add(self, other: Self) -> Self; - - // Default implementation of double is (self + self) - fn double(self) -> Self { - self.add(self) - } -} -``` - -When implementing a trait with default functions, a type may choose to implement only the required functions: - -```rust -impl Numeric for Field { - fn add(self, other: Field) -> Field { - self + other - } -} -``` - -Or it may implement the optional methods as well: - -```rust -impl Numeric for u32 { - fn add(self, other: u32) -> u32 { - self + other - } - - fn double(self) -> u32 { - self * 2 - } -} -``` - -## Impl Specialization - -When implementing traits for a generic type it is possible to implement the trait for only a certain combination -of generics. This can be either as an optimization or because those specific generics are required to implement the trait. - -```rust -trait Sub { - fn sub(self, other: Self) -> Self; -} - -struct NonZero { - value: T, -} - -impl Sub for NonZero { - fn sub(self, other: Self) -> Self { - let value = self.value - other.value; - assert(value != 0); - NonZero { value } - } -} -``` - -## Overlapping Implementations - -Overlapping implementations are disallowed by Noir to ensure Noir's decision on which impl to select is never ambiguous. -This means if a trait `Foo` is already implemented -by a type `Bar` for all `T`, then we cannot also have a separate impl for `Bar` (or any other -type argument). Similarly, if there is an impl for all `T` such as `impl Debug for T`, we cannot create -any more impls to `Debug` for other types since it would be ambiguous which impl to choose for any given -method call. - -```rust -trait Trait {} - -// Previous impl defined here -impl Trait for (A, B) {} - -// error: Impl for type `(Field, Field)` overlaps with existing impl -impl Trait for (Field, Field) {} -``` - -## Trait Coherence - -Another restriction on trait implementations is coherence. This restriction ensures other crates cannot create -impls that may overlap with other impls, even if several unrelated crates are used as dependencies in the same -program. - -The coherence restriction is: to implement a trait, either the trait itself or the object type must be declared -in the crate the impl is in. - -In practice this often comes up when using types provided by libraries. If a library provides a type `Foo` that does -not implement a trait in the standard library such as `Default`, you may not `impl Default for Foo` in your own crate. -While restrictive, this prevents later issues or silent changes in the program if the `Foo` library later added its -own impl for `Default`. If you are a user of the `Foo` library in this scenario and need a trait not implemented by the -library your choices are to either submit a patch to the library or use the newtype pattern. - -### The Newtype Pattern - -The newtype pattern gets around the coherence restriction by creating a new wrapper type around the library type -that we cannot create `impl`s for. Since the new wrapper type is defined in our current crate, we can create -impls for any trait we need on it. - -```rust -struct Wrapper { - foo: some_library::Foo, -} - -impl Default for Wrapper { - fn default() -> Wrapper { - Wrapper { - foo: some_library::Foo::new(), - } - } -} -``` - -Since we have an impl for our own type, the behavior of this code will not change even if `some_library` is updated -to provide its own `impl Default for Foo`. The downside of this pattern is that it requires extra wrapping and -unwrapping of values when converting to and from the `Wrapper` and `Foo` types. diff --git a/docs/versioned_docs/version-v0.34.0/noir/concepts/unconstrained.md b/docs/versioned_docs/version-v0.34.0/noir/concepts/unconstrained.md deleted file mode 100644 index b5221b8d2dd..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/concepts/unconstrained.md +++ /dev/null @@ -1,104 +0,0 @@ ---- -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] -sidebar_position: 5 ---- - -Unconstrained functions are functions which do not constrain any of the included computation and allow for non-deterministic 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 AND 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 = unsafe { - 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). - -Note that in order to invoke unconstrained functions we need to wrap them in an `unsafe` block, -to make it clear that the call is unconstrained. - -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. - -## Break and Continue - -In addition to loops over runtime bounds, `break` and `continue` are also available in unconstrained code. See [break and continue](../concepts/control_flow.md#break-and-continue) diff --git a/docs/versioned_docs/version-v0.34.0/noir/modules_packages_crates/_category_.json b/docs/versioned_docs/version-v0.34.0/noir/modules_packages_crates/_category_.json deleted file mode 100644 index 1debcfe7675..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/modules_packages_crates/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "Modules, Packages and Crates", - "position": 2, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.34.0/noir/modules_packages_crates/crates_and_packages.md b/docs/versioned_docs/version-v0.34.0/noir/modules_packages_crates/crates_and_packages.md deleted file mode 100644 index 95ee9f52ab2..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/modules_packages_crates/crates_and_packages.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -title: Crates and Packages -description: Learn how to use Crates and Packages in your Noir project -keywords: [Nargo, dependencies, package management, crates, package] -sidebar_position: 0 ---- - -## 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/noir-projects/noir-contracts/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.34.0/noir/modules_packages_crates/dependencies.md b/docs/versioned_docs/version-v0.34.0/noir/modules_packages_crates/dependencies.md deleted file mode 100644 index 24e02de08fe..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/modules_packages_crates/dependencies.md +++ /dev/null @@ -1,124 +0,0 @@ ---- -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] -sidebar_position: 1 ---- - -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 = "noir-contracts/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 -└── lib_a - ├── Nargo.toml - └── src - └── lib.nr -``` - -Inside of the binary crate, you can specify: - -```toml -# Nargo.toml - -[dependencies] -lib_a = { path = "../lib_a" } -``` - -## 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 lib_a referenced above: - -```rust -use ecrecover; -use lib_a; -``` - -You can also import only the specific parts of dependency that you want to use, like so: - -```rust -use std::hash::sha256; -use std::scalar_mul::fixed_base_embedded_curve; -``` - -Lastly, as demonstrated in the -[elliptic curve example](../standard_library/cryptographic_primitives/ec_primitives.md#examples), you -can import multiple items in the same line by enclosing them in curly braces: - -```rust -use std::ec::tecurve::affine::{Curve, Point}; -``` - -We don't have a way to consume libraries from inside a [workspace](./workspaces.md) 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 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.34.0/noir/modules_packages_crates/modules.md b/docs/versioned_docs/version-v0.34.0/noir/modules_packages_crates/modules.md deleted file mode 100644 index d21b009be3b..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/modules_packages_crates/modules.md +++ /dev/null @@ -1,211 +0,0 @@ ---- -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] -sidebar_position: 2 ---- - -Noir's module system follows the same convention as the _newer_ version of Rust's module system. - -## Purpose of Modules - -Modules are used to organize 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 - -``` - -The module filename may also be the name of the module as a directory with the contents in a -file named `mod.nr` within that directory. The above example can alternatively be expressed like this: - -Filename : `src/main.nr` - -```rust -mod foo; - -fn main() { - foo::hello_world(); -} -``` - -Filename : `src/foo/mod.nr` - -```rust -fn from_foo() {} -``` - -Note that it's an error to have both files `src/foo.nr` and `src/foo/mod.nr` in the filesystem. - -### 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 -``` - -Similar to importing a module in the crate root, modules can be placed in a `mod.nr` file, like this: - -Filename : `src/main.nr` - -```rust -mod foo; - -fn main() { - foo::from_foo(); -} -``` - -Filename : `src/foo/mod.nr` - -```rust -mod bar; -fn from_foo() {} -``` - -Filename : `src/foo/bar/mod.nr` - -```rust -fn from_bar() {} -``` - -### Referencing a parent module - -Given a submodule, you can refer to its parent module using the `super` keyword. - -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 -// Same as bar::from_foo -use super::from_foo; - -fn from_bar() { - from_foo(); // invokes super::from_foo(), which is bar::from_foo() - super::from_foo(); // also invokes bar::from_foo() -} -``` - -### `use` visibility - -`use` declarations are private to the containing module, by default. However, like functions, -they can be marked as `pub` or `pub(crate)`. Such a use declaration serves to _re-export_ a name. -A public `use` declaration can therefore redirect some public name to a different target definition: -even a definition with a private canonical path, inside a different module. - -An example of re-exporting: - -```rust -mod some_module { - pub use foo::{bar, baz}; - mod foo { - pub fn bar() {} - pub fn baz() {} - } -} - -fn main() { - some_module::bar(); - some_module::baz(); -} -``` - -In this example, the module `some_module` re-exports two public names defined in `foo`. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.34.0/noir/modules_packages_crates/workspaces.md b/docs/versioned_docs/version-v0.34.0/noir/modules_packages_crates/workspaces.md deleted file mode 100644 index 513497f12bf..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/modules_packages_crates/workspaces.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -title: Workspaces -sidebar_position: 3 ---- - -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 -│ │ └── Prover.toml -│ │ └── src -│ │ └── main.nr -│ └── b -│ ├── Nargo.toml -│ └── Prover.toml -│ └── src -│ └── main.nr -│ -└── Nargo.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.34.0/noir/standard_library/_category_.json b/docs/versioned_docs/version-v0.34.0/noir/standard_library/_category_.json deleted file mode 100644 index af04c0933fd..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/standard_library/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "Standard Library", - "position": 1, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.34.0/noir/standard_library/bigint.md b/docs/versioned_docs/version-v0.34.0/noir/standard_library/bigint.md deleted file mode 100644 index bcbb6b0d252..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/standard_library/bigint.md +++ /dev/null @@ -1,127 +0,0 @@ ---- -title: Big Integers -description: How to use big integers from Noir standard library -keywords: - [ - Big Integer, - Noir programming language, - Noir libraries, - ] ---- - -The BigInt module in the standard library exposes some class of integers which do not fit (well) into a Noir native field. It implements modulo arithmetic, modulo a 'big' prime number. - -:::note - -The module can currently be considered as `Field`s with fixed modulo sizes used by a set of elliptic curves, in addition to just the native curve. [More work](https://github.com/noir-lang/noir/issues/510) is needed to achieve arbitrarily sized big integers. - -:::note - -`nargo` can be built with `--profile release-pedantic` to enable extra overflow checks which may affect `BigInt` results in some cases. -Consider the [`noir-bignum`](https://github.com/noir-lang/noir-bignum) library for an optimized alternative approach. - -::: - -Currently 6 classes of integers (i.e 'big' prime numbers) are available in the module, namely: - -- BN254 Fq: Bn254Fq -- BN254 Fr: Bn254Fr -- Secp256k1 Fq: Secpk1Fq -- Secp256k1 Fr: Secpk1Fr -- Secp256r1 Fr: Secpr1Fr -- Secp256r1 Fq: Secpr1Fq - -Where XXX Fq and XXX Fr denote respectively the order of the base and scalar field of the (usual) elliptic curve XXX. -For instance the big integer 'Secpk1Fq' in the standard library refers to integers modulo $2^{256}-2^{32}-977$. - -Feel free to explore the source code for the other primes: - -```rust title="big_int_definition" showLineNumbers -struct BigInt { - pointer: u32, - modulus: u32, -} -``` -> Source code: noir_stdlib/src/bigint.nr#L14-L19 - - -## Example usage - -A common use-case is when constructing a big integer from its bytes representation, and performing arithmetic operations on it: - -```rust title="big_int_example" showLineNumbers -fn big_int_example(x: u8, y: u8) { - let a = Secpk1Fq::from_le_bytes(&[x, y, 0, 45, 2]); - let b = Secpk1Fq::from_le_bytes(&[y, x, 9]); - let c = (a + b) * b / a; - let d = c.to_le_bytes(); - println(d[0]); -} -``` -> Source code: test_programs/execution_success/bigint/src/main.nr#L72-L80 - - -## Methods - -The available operations for each big integer are: - -### from_le_bytes - -Construct a big integer from its little-endian bytes representation. Example: - -```rust - // Construct a big integer from a slice of bytes - let a = Secpk1Fq::from_le_bytes(&[x, y, 0, 45, 2]); - // Construct a big integer from an array of 32 bytes - let a = Secpk1Fq::from_le_bytes_32([1;32]); - ``` - -Sure, here's the formatted version of the remaining methods: - -### to_le_bytes - -Return the little-endian bytes representation of a big integer. Example: - -```rust -let bytes = a.to_le_bytes(); -``` - -### add - -Add two big integers. Example: - -```rust -let sum = a + b; -``` - -### sub - -Subtract two big integers. Example: - -```rust -let difference = a - b; -``` - -### mul - -Multiply two big integers. Example: - -```rust -let product = a * b; -``` - -### div - -Divide two big integers. Note that division is field division and not euclidean division. Example: - -```rust -let quotient = a / b; -``` - -### eq - -Compare two big integers. Example: - -```rust -let are_equal = a == b; -``` diff --git a/docs/versioned_docs/version-v0.34.0/noir/standard_library/black_box_fns.md b/docs/versioned_docs/version-v0.34.0/noir/standard_library/black_box_fns.md deleted file mode 100644 index d6079ab182c..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/standard_library/black_box_fns.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -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. - -The ACVM spec defines a set of blackbox functions which backends will be expected to implement. This allows backends to use optimized implementations of these constraints if they have them, however they may also fallback to less efficient naive implementations if not. - -## Function list - -Here is a list of the current black box functions: - -- [AES128](./cryptographic_primitives/ciphers.mdx#aes128) -- [SHA256](./cryptographic_primitives/hashes.mdx#sha256) -- [Schnorr signature verification](./cryptographic_primitives/schnorr.mdx) -- [Blake2s](./cryptographic_primitives/hashes.mdx#blake2s) -- [Blake3](./cryptographic_primitives/hashes.mdx#blake3) -- [Pedersen Hash](./cryptographic_primitives/hashes.mdx#pedersen_hash) -- [Pedersen Commitment](./cryptographic_primitives/hashes.mdx#pedersen_commitment) -- [ECDSA signature verification](./cryptographic_primitives/ecdsa_sig_verification.mdx) -- [Embedded curve operations (MSM, addition, ...)](./cryptographic_primitives/embedded_curve_ops.mdx) -- AND -- XOR -- RANGE -- [Keccak256](./cryptographic_primitives/hashes.mdx#keccak256) -- [Recursive proof verification](./recursion.mdx) - -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. - -You can view the black box functions defined in the ACVM code [here](https://github.com/noir-lang/noir/blob/master/acvm-repo/acir/src/circuit/black_box_functions.rs). diff --git a/docs/versioned_docs/version-v0.34.0/noir/standard_library/bn254.md b/docs/versioned_docs/version-v0.34.0/noir/standard_library/bn254.md deleted file mode 100644 index 3294f005dbb..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/standard_library/bn254.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -title: Bn254 Field Library ---- - -Noir provides a module in standard library with some optimized functions for bn254 Fr in `std::field::bn254`. - -## decompose - -```rust -fn decompose(x: Field) -> (Field, Field) {} -``` - -Decomposes a single field into two fields, low and high. The low field contains the lower 16 bytes of the input field and the high field contains the upper 16 bytes of the input field. Both field results are range checked to 128 bits. - - -## assert_gt - -```rust -fn assert_gt(a: Field, b: Field) {} -``` - -Asserts that a > b. This will generate less constraints than using `assert(gt(a, b))`. - -## assert_lt - -```rust -fn assert_lt(a: Field, b: Field) {} -``` - -Asserts that a < b. This will generate less constraints than using `assert(lt(a, b))`. - -## gt - -```rust -fn gt(a: Field, b: Field) -> bool {} -``` - -Returns true if a > b. - -## lt - -```rust -fn lt(a: Field, b: Field) -> bool {} -``` - -Returns true if a < b. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.34.0/noir/standard_library/containers/boundedvec.md b/docs/versioned_docs/version-v0.34.0/noir/standard_library/containers/boundedvec.md deleted file mode 100644 index 7463ca092f1..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/standard_library/containers/boundedvec.md +++ /dev/null @@ -1,419 +0,0 @@ ---- -title: Bounded Vectors -keywords: [noir, vector, bounded vector, slice] -sidebar_position: 1 ---- - -A `BoundedVec` is a growable storage similar to a `Vec` except that it -is bounded with a maximum possible length. Unlike `Vec`, `BoundedVec` is not implemented -via slices and thus is not subject to the same restrictions slices are (notably, nested -slices - and thus nested vectors as well - are disallowed). - -Since a BoundedVec is backed by a normal array under the hood, growing the BoundedVec by -pushing an additional element is also more efficient - the length only needs to be increased -by one. - -For these reasons `BoundedVec` should generally be preferred over `Vec` when there -is a reasonable maximum bound that can be placed on the vector. - -Example: - -```rust -let mut vector: BoundedVec = BoundedVec::new(); -for i in 0..5 { - vector.push(i); -} -assert(vector.len() == 5); -assert(vector.max_len() == 10); -``` - -## Methods - -### new - -```rust -pub fn new() -> Self -``` - -Creates a new, empty vector of length zero. - -Since this container is backed by an array internally, it still needs an initial value -to give each element. To resolve this, each element is zeroed internally. This value -is guaranteed to be inaccessible unless `get_unchecked` is used. - -Example: - -```rust -let empty_vector: BoundedVec = BoundedVec::new(); -assert(empty_vector.len() == 0); -``` - -Note that whenever calling `new` the maximum length of the vector should always be specified -via a type signature: - -```rust title="new_example" showLineNumbers -fn good() -> BoundedVec { - // Ok! MaxLen is specified with a type annotation - let v1: BoundedVec = BoundedVec::new(); - let v2 = BoundedVec::new(); - - // Ok! MaxLen is known from the type of `good`'s return value - v2 -} - -fn bad() { - // Error: Type annotation needed - // The compiler can't infer `MaxLen` from this code. - let mut v3 = BoundedVec::new(); - v3.push(5); -} -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L11-L27 - - -This defaulting of `MaxLen` (and numeric generics in general) to zero may change in future noir versions -but for now make sure to use type annotations when using bounded vectors. Otherwise, you will receive a constraint failure at runtime when the vec is pushed to. - -### get - -```rust -pub fn get(self, index: u64) -> T { -``` - -Retrieves an element from the vector at the given index, starting from zero. - -If the given index is equal to or greater than the length of the vector, this -will issue a constraint failure. - -Example: - -```rust -fn foo(v: BoundedVec) { - let first = v.get(0); - let last = v.get(v.len() - 1); - assert(first != last); -} -``` - -### get_unchecked - -```rust -pub fn get_unchecked(self, index: u64) -> T { -``` - -Retrieves an element from the vector at the given index, starting from zero, without -performing a bounds check. - -Since this function does not perform a bounds check on length before accessing the element, -it is unsafe! Use at your own risk! - -Example: - -```rust title="get_unchecked_example" showLineNumbers -fn sum_of_first_three(v: BoundedVec) -> u32 { - // Always ensure the length is larger than the largest - // index passed to get_unchecked - assert(v.len() > 2); - let first = v.get_unchecked(0); - let second = v.get_unchecked(1); - let third = v.get_unchecked(2); - first + second + third -} -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L54-L64 - - -### set - -```rust -pub fn set(&mut self: Self, index: u64, value: T) { -``` - -Writes an element to the vector at the given index, starting from zero. - -If the given index is equal to or greater than the length of the vector, this will issue a constraint failure. - -Example: - -```rust -fn foo(v: BoundedVec) { - let first = v.get(0); - assert(first != 42); - v.set(0, 42); - let new_first = v.get(0); - assert(new_first == 42); -} -``` - -### set_unchecked - -```rust -pub fn set_unchecked(&mut self: Self, index: u64, value: T) -> T { -``` - -Writes an element to the vector at the given index, starting from zero, without performing a bounds check. - -Since this function does not perform a bounds check on length before accessing the element, it is unsafe! Use at your own risk! - -Example: - -```rust title="set_unchecked_example" showLineNumbers -fn set_unchecked_example() { - let mut vec: BoundedVec = BoundedVec::new(); - vec.extend_from_array([1, 2]); - - // Here we're safely writing within the valid range of `vec` - // `vec` now has the value [42, 2] - vec.set_unchecked(0, 42); - - // We can then safely read this value back out of `vec`. - // Notice that we use the checked version of `get` which would prevent reading unsafe values. - assert_eq(vec.get(0), 42); - - // We've now written past the end of `vec`. - // As this index is still within the maximum potential length of `v`, - // it won't cause a constraint failure. - vec.set_unchecked(2, 42); - println(vec); - - // This will write past the end of the maximum potential length of `vec`, - // it will then trigger a constraint failure. - vec.set_unchecked(5, 42); - println(vec); -} -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L67-L91 - - - -### push - -```rust -pub fn push(&mut self, elem: T) { -``` - -Pushes an element to the end of the vector. This increases the length -of the vector by one. - -Panics if the new length of the vector will be greater than the max length. - -Example: - -```rust title="bounded-vec-push-example" showLineNumbers -let mut v: BoundedVec = BoundedVec::new(); - - v.push(1); - v.push(2); - - // Panics with failed assertion "push out of bounds" - v.push(3); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L95-L103 - - -### pop - -```rust -pub fn pop(&mut self) -> T -``` - -Pops the element at the end of the vector. This will decrease the length -of the vector by one. - -Panics if the vector is empty. - -Example: - -```rust title="bounded-vec-pop-example" showLineNumbers -let mut v: BoundedVec = BoundedVec::new(); - v.push(1); - v.push(2); - - let two = v.pop(); - let one = v.pop(); - - assert(two == 2); - assert(one == 1); - // error: cannot pop from an empty vector - // let _ = v.pop(); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L108-L120 - - -### len - -```rust -pub fn len(self) -> u64 { -``` - -Returns the current length of this vector - -Example: - -```rust title="bounded-vec-len-example" showLineNumbers -let mut v: BoundedVec = BoundedVec::new(); - assert(v.len() == 0); - - v.push(100); - assert(v.len() == 1); - - v.push(200); - v.push(300); - v.push(400); - assert(v.len() == 4); - - let _ = v.pop(); - let _ = v.pop(); - assert(v.len() == 2); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L125-L140 - - -### max_len - -```rust -pub fn max_len(_self: BoundedVec) -> u64 { -``` - -Returns the maximum length of this vector. This is always -equal to the `MaxLen` parameter this vector was initialized with. - -Example: - -```rust title="bounded-vec-max-len-example" showLineNumbers -let mut v: BoundedVec = BoundedVec::new(); - - assert(v.max_len() == 5); - v.push(10); - assert(v.max_len() == 5); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L145-L151 - - -### storage - -```rust -pub fn storage(self) -> [T; MaxLen] { -``` - -Returns the internal array within this vector. -Since arrays in Noir are immutable, mutating the returned storage array will not mutate -the storage held internally by this vector. - -Note that uninitialized elements may be zeroed out! - -Example: - -```rust title="bounded-vec-storage-example" showLineNumbers -let mut v: BoundedVec = BoundedVec::new(); - - assert(v.storage() == [0, 0, 0, 0, 0]); - - v.push(57); - assert(v.storage() == [57, 0, 0, 0, 0]); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L156-L163 - - -### extend_from_array - -```rust -pub fn extend_from_array(&mut self, array: [T; Len]) -``` - -Pushes each element from the given array to this vector. - -Panics if pushing each element would cause the length of this vector -to exceed the maximum length. - -Example: - -```rust title="bounded-vec-extend-from-array-example" showLineNumbers -let mut vec: BoundedVec = BoundedVec::new(); - vec.extend_from_array([2, 4]); - - assert(vec.len == 2); - assert(vec.get(0) == 2); - assert(vec.get(1) == 4); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L168-L175 - - -### extend_from_bounded_vec - -```rust -pub fn extend_from_bounded_vec(&mut self, vec: BoundedVec) -``` - -Pushes each element from the other vector to this vector. The length of -the other vector is left unchanged. - -Panics if pushing each element would cause the length of this vector -to exceed the maximum length. - -Example: - -```rust title="bounded-vec-extend-from-bounded-vec-example" showLineNumbers -let mut v1: BoundedVec = BoundedVec::new(); - let mut v2: BoundedVec = BoundedVec::new(); - - v2.extend_from_array([1, 2, 3]); - v1.extend_from_bounded_vec(v2); - - assert(v1.storage() == [1, 2, 3, 0, 0]); - assert(v2.storage() == [1, 2, 3, 0, 0, 0, 0]); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L180-L189 - - -### from_array - -```rust -pub fn from_array(array: [T; Len]) -> Self -``` - -Creates a new vector, populating it with values derived from an array input. -The maximum length of the vector is determined based on the type signature. - -Example: -```rust -let bounded_vec: BoundedVec = BoundedVec::from_array([1, 2, 3]) -``` - -### map - -```rust -pub fn map(self, f: fn[Env](T) -> U) -> BoundedVec -``` - -Creates a new vector of equal size by calling a closure on each element in this vector. - -Example: - -```rust title="bounded-vec-map-example" showLineNumbers -let vec: BoundedVec = BoundedVec::from_array([1, 2, 3, 4]); - let result = vec.map(|value| value * 2); -``` -> Source code: noir_stdlib/src/collections/bounded_vec.nr#L493-L496 - - -### any - -```rust -pub fn any(self, predicate: fn[Env](T) -> bool) -> bool -``` - -Returns true if the given predicate returns true for any element -in this vector. - -Example: - -```rust title="bounded-vec-any-example" showLineNumbers -let mut v: BoundedVec = BoundedVec::new(); - v.extend_from_array([2, 4, 6]); - - let all_even = !v.any(|elem: u32| elem % 2 != 0); - assert(all_even); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L256-L262 - diff --git a/docs/versioned_docs/version-v0.34.0/noir/standard_library/containers/hashmap.md b/docs/versioned_docs/version-v0.34.0/noir/standard_library/containers/hashmap.md deleted file mode 100644 index 39f1ae9b32c..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/standard_library/containers/hashmap.md +++ /dev/null @@ -1,594 +0,0 @@ ---- -title: HashMap -keywords: [noir, map, hash, hashmap] -sidebar_position: 1 ---- - -`HashMap` is used to efficiently store and look up key-value pairs. - -`HashMap` is a bounded type which can store anywhere from zero to `MaxLen` total elements. -Note that due to hash collisions, the actual maximum number of elements stored by any particular -hashmap is likely lower than `MaxLen`. This is true even with cryptographic hash functions since -every hash value will be performed modulo `MaxLen`. - -Example: - -```rust -// Create a mapping from Fields to u32s with a maximum length of 12 -// using a poseidon2 hasher -use std::hash::poseidon2::Poseidon2Hasher; -let mut map: HashMap> = HashMap::default(); - -map.insert(1, 2); -map.insert(3, 4); - -let two = map.get(1).unwrap(); -``` - -## Methods - -### default - -```rust title="default" showLineNumbers -impl Default for HashMap -where - B: BuildHasher + Default, - H: Hasher + Default { - /// Constructs an empty HashMap. - /// - /// Example: - /// - /// ```noir - /// let hashmap: HashMap> = HashMap::default(); - /// assert(hashmap.is_empty()); - /// ``` - fn default() -> Self { -``` -> Source code: noir_stdlib/src/collections/map.nr#L694-L708 - - -Creates a fresh, empty HashMap. - -When using this function, always make sure to specify the maximum size of the hash map. - -This is the same `default` from the `Default` implementation given further below. It is -repeated here for convenience since it is the recommended way to create a hashmap. - -Example: - -```rust title="default_example" showLineNumbers -let hashmap: HashMap> = HashMap::default(); - assert(hashmap.is_empty()); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L201-L204 - - -Because `HashMap` has so many generic arguments that are likely to be the same throughout -your program, it may be helpful to create a type alias: - -```rust title="type_alias" showLineNumbers -type MyMap = HashMap>; -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L195-L197 - - -### with_hasher - -```rust title="with_hasher" showLineNumbers -pub fn with_hasher(_build_hasher: B) -> Self - where - B: BuildHasher { -``` -> Source code: noir_stdlib/src/collections/map.nr#L103-L107 - - -Creates a hashmap with an existing `BuildHasher`. This can be used to ensure multiple -hashmaps are created with the same hasher instance. - -Example: - -```rust title="with_hasher_example" showLineNumbers -let my_hasher: BuildHasherDefault = Default::default(); - let hashmap: HashMap> = HashMap::with_hasher(my_hasher); - assert(hashmap.is_empty()); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L206-L210 - - -### get - -```rust title="get" showLineNumbers -pub fn get( - self, - key: K - ) -> Option - where - K: Eq + Hash, - B: BuildHasher, - H: Hasher { -``` -> Source code: noir_stdlib/src/collections/map.nr#L470-L479 - - -Retrieves a value from the hashmap, returning `Option::none()` if it was not found. - -Example: - -```rust title="get_example" showLineNumbers -fn get_example(map: HashMap>) { - let x = map.get(12); - - if x.is_some() { - assert(x.unwrap() == 42); - } -} -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L298-L306 - - -### insert - -```rust title="insert" showLineNumbers -pub fn insert( - &mut self, - key: K, - value: V - ) - where - K: Eq + Hash, - B: BuildHasher, - H: Hasher { -``` -> Source code: noir_stdlib/src/collections/map.nr#L514-L524 - - -Inserts a new key-value pair into the map. If the key was already in the map, its -previous value will be overridden with the newly provided one. - -Example: - -```rust title="insert_example" showLineNumbers -let mut map: HashMap> = HashMap::default(); - map.insert(12, 42); - assert(map.len() == 1); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L212-L216 - - -### remove - -```rust title="remove" showLineNumbers -pub fn remove( - &mut self, - key: K - ) - where - K: Eq + Hash, - B: BuildHasher, - H: Hasher { -``` -> Source code: noir_stdlib/src/collections/map.nr#L573-L582 - - -Removes the given key-value pair from the map. If the key was not already present -in the map, this does nothing. - -Example: - -```rust title="remove_example" showLineNumbers -map.remove(12); - assert(map.is_empty()); - - // If a key was not present in the map, remove does nothing - map.remove(12); - assert(map.is_empty()); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L220-L227 - - -### is_empty - -```rust title="is_empty" showLineNumbers -pub fn is_empty(self) -> bool { -``` -> Source code: noir_stdlib/src/collections/map.nr#L168-L170 - - -True if the length of the hash map is empty. - -Example: - -```rust title="is_empty_example" showLineNumbers -assert(map.is_empty()); - - map.insert(1, 2); - assert(!map.is_empty()); - - map.remove(1); - assert(map.is_empty()); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L229-L237 - - -### len - -```rust title="len" showLineNumbers -pub fn len(self) -> u32 { -``` -> Source code: noir_stdlib/src/collections/map.nr#L429-L431 - - -Returns the current length of this hash map. - -Example: - -```rust title="len_example" showLineNumbers -// This is equivalent to checking map.is_empty() - assert(map.len() == 0); - - map.insert(1, 2); - map.insert(3, 4); - map.insert(5, 6); - assert(map.len() == 3); - - // 3 was already present as a key in the hash map, so the length is unchanged - map.insert(3, 7); - assert(map.len() == 3); - - map.remove(1); - assert(map.len() == 2); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L239-L254 - - -### capacity - -```rust title="capacity" showLineNumbers -pub fn capacity(_self: Self) -> u32 { -``` -> Source code: noir_stdlib/src/collections/map.nr#L451-L453 - - -Returns the maximum capacity of this hashmap. This is always equal to the capacity -specified in the hashmap's type. - -Unlike hashmaps in general purpose programming languages, hashmaps in Noir have a -static capacity that does not increase as the map grows larger. Thus, this capacity -is also the maximum possible element count that can be inserted into the hashmap. -Due to hash collisions (modulo the hashmap length), it is likely the actual maximum -element count will be lower than the full capacity. - -Example: - -```rust title="capacity_example" showLineNumbers -let empty_map: HashMap> = HashMap::default(); - assert(empty_map.len() == 0); - assert(empty_map.capacity() == 42); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L256-L260 - - -### clear - -```rust title="clear" showLineNumbers -pub fn clear(&mut self) { -``` -> Source code: noir_stdlib/src/collections/map.nr#L122-L124 - - -Clears the hashmap, removing all key-value pairs from it. - -Example: - -```rust title="clear_example" showLineNumbers -assert(!map.is_empty()); - map.clear(); - assert(map.is_empty()); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L262-L266 - - -### contains_key - -```rust title="contains_key" showLineNumbers -pub fn contains_key( - self, - key: K - ) -> bool - where - K: Hash + Eq, - B: BuildHasher, - H: Hasher { -``` -> Source code: noir_stdlib/src/collections/map.nr#L142-L151 - - -True if the hashmap contains the given key. Unlike `get`, this will not also return -the value associated with the key. - -Example: - -```rust title="contains_key_example" showLineNumbers -if map.contains_key(7) { - let value = map.get(7); - assert(value.is_some()); - } else { - println("No value for key 7!"); - } -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L268-L275 - - -### entries - -```rust title="entries" showLineNumbers -pub fn entries(self) -> BoundedVec<(K, V), N> { -``` -> Source code: noir_stdlib/src/collections/map.nr#L192-L194 - - -Returns a vector of each key-value pair present in the hashmap. - -The length of the returned vector is always equal to the length of the hashmap. - -Example: - -```rust title="entries_example" showLineNumbers -let entries = map.entries(); - - // The length of a hashmap may not be compile-time known, so we - // need to loop over its capacity instead - for i in 0..map.capacity() { - if i < entries.len() { - let (key, value) = entries.get(i); - println(f"{key} -> {value}"); - } - } -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L309-L320 - - -### keys - -```rust title="keys" showLineNumbers -pub fn keys(self) -> BoundedVec { -``` -> Source code: noir_stdlib/src/collections/map.nr#L228-L230 - - -Returns a vector of each key present in the hashmap. - -The length of the returned vector is always equal to the length of the hashmap. - -Example: - -```rust title="keys_example" showLineNumbers -let keys = map.keys(); - - for i in 0..keys.max_len() { - if i < keys.len() { - let key = keys.get_unchecked(i); - let value = map.get(key).unwrap_unchecked(); - println(f"{key} -> {value}"); - } - } -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L322-L332 - - -### values - -```rust title="values" showLineNumbers -pub fn values(self) -> BoundedVec { -``` -> Source code: noir_stdlib/src/collections/map.nr#L262-L264 - - -Returns a vector of each value present in the hashmap. - -The length of the returned vector is always equal to the length of the hashmap. - -Example: - -```rust title="values_example" showLineNumbers -let values = map.values(); - - for i in 0..values.max_len() { - if i < values.len() { - let value = values.get_unchecked(i); - println(f"Found value {value}"); - } - } -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L334-L343 - - -### iter_mut - -```rust title="iter_mut" showLineNumbers -pub fn iter_mut( - &mut self, - f: fn(K, V) -> (K, V) - ) - where - K: Eq + Hash, - B: BuildHasher, - H: Hasher { -``` -> Source code: noir_stdlib/src/collections/map.nr#L298-L307 - - -Iterates through each key-value pair of the HashMap, setting each key-value pair to the -result returned from the given function. - -Note that since keys can be mutated, the HashMap needs to be rebuilt as it is iterated -through. If this is not desired, use `iter_values_mut` if only values need to be mutated, -or `entries` if neither keys nor values need to be mutated. - -The iteration order is left unspecified. As a result, if two keys are mutated to become -equal, which of the two values that will be present for the key in the resulting map is also unspecified. - -Example: - -```rust title="iter_mut_example" showLineNumbers -// Add 1 to each key in the map, and double the value associated with that key. - map.iter_mut(|k, v| (k + 1, v * 2)); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L347-L350 - - -### iter_keys_mut - -```rust title="iter_keys_mut" showLineNumbers -pub fn iter_keys_mut( - &mut self, - f: fn(K) -> K - ) - where - K: Eq + Hash, - B: BuildHasher, - H: Hasher { -``` -> Source code: noir_stdlib/src/collections/map.nr#L338-L347 - - -Iterates through the HashMap, mutating each key to the result returned from -the given function. - -Note that since keys can be mutated, the HashMap needs to be rebuilt as it is iterated -through. If only iteration is desired and the keys are not intended to be mutated, -prefer using `entries` instead. - -The iteration order is left unspecified. As a result, if two keys are mutated to become -equal, which of the two values that will be present for the key in the resulting map is also unspecified. - -Example: - -```rust title="iter_keys_mut_example" showLineNumbers -// Double each key, leaving the value associated with that key untouched - map.iter_keys_mut(|k| k * 2); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L352-L355 - - -### iter_values_mut - -```rust title="iter_values_mut" showLineNumbers -pub fn iter_values_mut(&mut self, f: fn(V) -> V) { -``` -> Source code: noir_stdlib/src/collections/map.nr#L372-L374 - - -Iterates through the HashMap, applying the given function to each value and mutating the -value to equal the result. This function is more efficient than `iter_mut` and `iter_keys_mut` -because the keys are untouched and the underlying hashmap thus does not need to be reordered. - -Example: - -```rust title="iter_values_mut_example" showLineNumbers -// Halve each value - map.iter_values_mut(|v| v / 2); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L357-L360 - - -### retain - -```rust title="retain" showLineNumbers -pub fn retain(&mut self, f: fn(K, V) -> bool) { -``` -> Source code: noir_stdlib/src/collections/map.nr#L393-L395 - - -Retains only the key-value pairs for which the given function returns true. -Any key-value pairs for which the function returns false will be removed from the map. - -Example: - -```rust title="retain_example" showLineNumbers -map.retain(|k, v| (k != 0) & (v != 0)); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L280-L282 - - -## Trait Implementations - -### default - -```rust title="default" showLineNumbers -impl Default for HashMap -where - B: BuildHasher + Default, - H: Hasher + Default { - /// Constructs an empty HashMap. - /// - /// Example: - /// - /// ```noir - /// let hashmap: HashMap> = HashMap::default(); - /// assert(hashmap.is_empty()); - /// ``` - fn default() -> Self { -``` -> Source code: noir_stdlib/src/collections/map.nr#L694-L708 - - -Constructs an empty HashMap. - -Example: - -```rust title="default_example" showLineNumbers -let hashmap: HashMap> = HashMap::default(); - assert(hashmap.is_empty()); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L201-L204 - - -### eq - -```rust title="eq" showLineNumbers -impl Eq for HashMap -where - K: Eq + Hash, - V: Eq, - B: BuildHasher, - H: Hasher { - /// Checks if two HashMaps are equal. - /// - /// Example: - /// - /// ```noir - /// let mut map1: HashMap> = HashMap::default(); - /// let mut map2: HashMap> = HashMap::default(); - /// - /// map1.insert(1, 2); - /// map1.insert(3, 4); - /// - /// map2.insert(3, 4); - /// map2.insert(1, 2); - /// - /// assert(map1 == map2); - /// ``` - fn eq(self, other: HashMap) -> bool { -``` -> Source code: noir_stdlib/src/collections/map.nr#L643-L667 - - -Checks if two HashMaps are equal. - -Example: - -```rust title="eq_example" showLineNumbers -let mut map1: HashMap> = HashMap::default(); - let mut map2: HashMap> = HashMap::default(); - - map1.insert(1, 2); - map1.insert(3, 4); - - map2.insert(3, 4); - map2.insert(1, 2); - - assert(map1 == map2); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L284-L295 - diff --git a/docs/versioned_docs/version-v0.34.0/noir/standard_library/containers/index.md b/docs/versioned_docs/version-v0.34.0/noir/standard_library/containers/index.md deleted file mode 100644 index ea84c6d5c21..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/standard_library/containers/index.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Containers -description: Container types provided by Noir's standard library for storing and retrieving data -keywords: [containers, data types, vec, hashmap] ---- diff --git a/docs/versioned_docs/version-v0.34.0/noir/standard_library/containers/vec.mdx b/docs/versioned_docs/version-v0.34.0/noir/standard_library/containers/vec.mdx deleted file mode 100644 index 475011922f8..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/standard_library/containers/vec.mdx +++ /dev/null @@ -1,170 +0,0 @@ ---- -title: Vectors -description: Delve into the Vec 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] -sidebar_position: 6 ---- - -import Experimental from '@site/src/components/Notes/_experimental.mdx'; - - - -A vector is a collection type similar to Rust's `Vec` type. In Noir, it is a convenient way to use slices as mutable arrays. - -Example: - -```rust -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 -``` - -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 -``` - -Example: - -```rust -let slice: [Field] = &[1, 2, 3]; -let vector_from_slice = Vec::from_slice(slice); -assert(vector_from_slice.len() == 3); -``` - -### len - -Returns the number of elements in the vector. - -```rust -pub fn len(self) -> Field -``` - -Example: - -```rust -let empty_vector: Vec = Vec::new(); -assert(empty_vector.len() == 0); -``` - -### 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 -``` - -Example: - -```rust -let vector: Vec = Vec::from_slice(&[10, 20, 30]); -assert(vector.get(1) == 20); -``` - -### set - -```rust -pub fn set(&mut self: Self, index: u64, value: T) { -``` - -Writes an element to the vector at the given index, starting from zero. - -Panics if the index points beyond the vector's end. - -Example: - -```rust -let vector: Vec = Vec::from_slice(&[10, 20, 30]); -assert(vector.get(1) == 20); -vector.set(1, 42); -assert(vector.get(1) == 42); -``` - -### 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) -``` - -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 -``` - -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) -``` - -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 -``` - -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); -``` diff --git a/docs/versioned_docs/version-v0.34.0/noir/standard_library/cryptographic_primitives/_category_.json b/docs/versioned_docs/version-v0.34.0/noir/standard_library/cryptographic_primitives/_category_.json deleted file mode 100644 index 5d694210bbf..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/standard_library/cryptographic_primitives/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "position": 0, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.34.0/noir/standard_library/cryptographic_primitives/ciphers.mdx b/docs/versioned_docs/version-v0.34.0/noir/standard_library/cryptographic_primitives/ciphers.mdx deleted file mode 100644 index d6a5e1a79eb..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/standard_library/cryptographic_primitives/ciphers.mdx +++ /dev/null @@ -1,32 +0,0 @@ ---- -title: Ciphers -description: - Learn about the implemented ciphers ready to use for any Noir project -keywords: - [ciphers, Noir project, aes128, encrypt] -sidebar_position: 0 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; - -## aes128 - -Given a plaintext as an array of bytes, returns the corresponding aes128 ciphertext (CBC mode). Input padding is automatically performed using PKCS#7, so that the output length is `input.len() + (16 - input.len() % 16)`. - -```rust title="aes128" showLineNumbers -pub fn aes128_encrypt(input: [u8; N], iv: [u8; 16], key: [u8; 16]) -> [u8] {} -``` -> Source code: noir_stdlib/src/aes128.nr#L2-L4 - - -```rust -fn main() { - let input: [u8; 4] = [0, 12, 3, 15] // Random bytes, will be padded to 16 bytes. - let iv: [u8; 16] = [0; 16]; // Initialisation vector - let key: [u8; 16] = [0; 16] // AES key - let ciphertext = std::aes128::aes128_encrypt(inputs.as_bytes(), iv.as_bytes(), key.as_bytes()); // In this case, the output length will be 16 bytes. -} -``` - - - \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.34.0/noir/standard_library/cryptographic_primitives/ec_primitives.md b/docs/versioned_docs/version-v0.34.0/noir/standard_library/cryptographic_primitives/ec_primitives.md deleted file mode 100644 index f262d8160d6..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/standard_library/cryptographic_primitives/ec_primitives.md +++ /dev/null @@ -1,102 +0,0 @@ ---- -title: Elliptic Curve Primitives -keywords: [cryptographic primitives, Noir project] -sidebar_position: 4 ---- - -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/mod.nr`](https://github.com/noir-lang/noir/blob/master/noir_stdlib/src/ec/mod.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/mod.nr)). - -## Examples - -The -[ec_baby_jubjub test](https://github.com/noir-lang/noir/blob/master/test_programs/compile_success_empty/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 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.34.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx b/docs/versioned_docs/version-v0.34.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx deleted file mode 100644 index 4c22e70e8de..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx +++ /dev/null @@ -1,98 +0,0 @@ ---- -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] -sidebar_position: 3 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; - -Noir supports ECDSA signatures verification over the secp256k1 and secp256r1 curves. - -## ecdsa_secp256k1::verify_signature - -Verifier for ECDSA Secp256k1 signatures. -See ecdsa_secp256k1::verify_signature_slice for a version that accepts slices directly. - -```rust title="ecdsa_secp256k1" showLineNumbers -pub fn verify_signature( - public_key_x: [u8; 32], - public_key_y: [u8; 32], - signature: [u8; 64], - message_hash: [u8; N] -) -> bool -``` -> Source code: noir_stdlib/src/ecdsa_secp256k1.nr#L2-L9 - - -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_secp256k1::verify_signature_slice - -Verifier for ECDSA Secp256k1 signatures where the message is a slice. - -```rust title="ecdsa_secp256k1_slice" showLineNumbers -pub fn verify_signature_slice( - public_key_x: [u8; 32], - public_key_y: [u8; 32], - signature: [u8; 64], - message_hash: [u8] -) -> bool -``` -> Source code: noir_stdlib/src/ecdsa_secp256k1.nr#L13-L20 - - - - -## ecdsa_secp256r1::verify_signature - -Verifier for ECDSA Secp256r1 signatures. -See ecdsa_secp256r1::verify_signature_slice for a version that accepts slices directly. - -```rust title="ecdsa_secp256r1" showLineNumbers -pub fn verify_signature( - public_key_x: [u8; 32], - public_key_y: [u8; 32], - signature: [u8; 64], - message_hash: [u8; N] -) -> bool -``` -> Source code: noir_stdlib/src/ecdsa_secp256r1.nr#L2-L9 - - -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); -} -``` - - - -## ecdsa_secp256r1::verify_signature - -Verifier for ECDSA Secp256r1 signatures where the message is a slice. - -```rust title="ecdsa_secp256r1_slice" showLineNumbers -pub fn verify_signature_slice( - public_key_x: [u8; 32], - public_key_y: [u8; 32], - signature: [u8; 64], - message_hash: [u8] -) -> bool -``` -> Source code: noir_stdlib/src/ecdsa_secp256r1.nr#L13-L20 - - - diff --git a/docs/versioned_docs/version-v0.34.0/noir/standard_library/cryptographic_primitives/eddsa.mdx b/docs/versioned_docs/version-v0.34.0/noir/standard_library/cryptographic_primitives/eddsa.mdx deleted file mode 100644 index b283de693c8..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/standard_library/cryptographic_primitives/eddsa.mdx +++ /dev/null @@ -1,37 +0,0 @@ ---- -title: EdDSA Verification -description: Learn about the cryptographic primitives regarding EdDSA -keywords: [cryptographic primitives, Noir project, eddsa, signatures] -sidebar_position: 5 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; - -## 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 -``` - -It is also possible to specify the hash algorithm used for the signature by using the `eddsa_verify` function by passing a type implementing the Hasher trait with the turbofish operator. -For instance, if you want to use Poseidon2 instead, you can do the following: -```rust -use std::hash::poseidon2::Poseidon2Hasher; - -eddsa_verify::(pub_key_a.x, pub_key_a.y, s_a, r8_a.x, r8_a.y, msg); -``` - - - -## eddsa::eddsa_to_pub - -Private to public key conversion. - -Returns `(pub_key_x, pub_key_y)` - -```rust -fn eddsa_to_pub(secret : Field) -> (Field, Field) -``` - diff --git a/docs/versioned_docs/version-v0.34.0/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx b/docs/versioned_docs/version-v0.34.0/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx deleted file mode 100644 index 2da1e34f008..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx +++ /dev/null @@ -1,95 +0,0 @@ ---- -title: Scalar multiplication -description: See how you can perform scalar multiplication in Noir -keywords: [cryptographic primitives, Noir project, scalar multiplication] -sidebar_position: 1 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; - -The following functions perform operations over the embedded curve whose coordinates are defined by the configured noir field. -For the BN254 scalar field, this is BabyJubJub or Grumpkin. - -:::note -Suffixes `_low` and `_high` denote low and high limbs of a scalar. -::: - -## embedded_curve_ops::multi_scalar_mul - -Performs multi scalar multiplication over the embedded curve. -The function accepts arbitrary amount of point-scalar pairs on the input, it multiplies the individual pairs over -the curve and returns a sum of the resulting points. - -Points represented as x and y coordinates [x1, y1, x2, y2, ...], scalars as low and high limbs [low1, high1, low2, high2, ...]. - -```rust title="multi_scalar_mul" showLineNumbers -pub fn multi_scalar_mul( - points: [EmbeddedCurvePoint; N], - scalars: [EmbeddedCurveScalar; N] -) -> EmbeddedCurvePoint -``` -> Source code: noir_stdlib/src/embedded_curve_ops.nr#L88-L93 - - -example - -```rust -fn main(point_x: Field, point_y: Field, scalar_low: Field, scalar_high: Field) { - let point = std::embedded_curve_ops::multi_scalar_mul([point_x, point_y], [scalar_low, scalar_high]); - println(point); -} -``` - -## embedded_curve_ops::fixed_base_scalar_mul - -Performs fixed base scalar multiplication over the embedded curve (multiplies input scalar with a generator point). -The function accepts a single scalar on the input represented as 2 fields. - -```rust title="fixed_base_scalar_mul" showLineNumbers -pub fn fixed_base_scalar_mul(scalar: EmbeddedCurveScalar) -> EmbeddedCurvePoint -``` -> Source code: noir_stdlib/src/embedded_curve_ops.nr#L105-L107 - - -example - -```rust -fn main(scalar_low: Field, scalar_high: Field) { - let point = std::embedded_curve_ops::fixed_base_scalar_mul(scalar_low, scalar_high); - println(point); -} -``` - -## embedded_curve_ops::embedded_curve_add - -Adds two points on the embedded curve. -This function takes two `EmbeddedCurvePoint` structures as parameters, representing points on the curve, and returns a new `EmbeddedCurvePoint` structure that represents their sum. - -### Parameters: -- `point1` (`EmbeddedCurvePoint`): The first point to add. -- `point2` (`EmbeddedCurvePoint`): The second point to add. - -### Returns: -- `EmbeddedCurvePoint`: The resulting point after the addition of `point1` and `point2`. - -```rust title="embedded_curve_add" showLineNumbers -fn embedded_curve_add( - point1: EmbeddedCurvePoint, - point2: EmbeddedCurvePoint -) -> EmbeddedCurvePoint -``` -> Source code: noir_stdlib/src/embedded_curve_ops.nr#L115-L120 - - -example - -```rust -fn main() { - let point1 = EmbeddedCurvePoint { x: 1, y: 2 }; - let point2 = EmbeddedCurvePoint { x: 3, y: 4 }; - let result = std::embedded_curve_ops::embedded_curve_add(point1, point2); - println!("Resulting Point: ({}, {})", result.x, result.y); -} -``` - - diff --git a/docs/versioned_docs/version-v0.34.0/noir/standard_library/cryptographic_primitives/hashes.mdx b/docs/versioned_docs/version-v0.34.0/noir/standard_library/cryptographic_primitives/hashes.mdx deleted file mode 100644 index d6640d26f25..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/standard_library/cryptographic_primitives/hashes.mdx +++ /dev/null @@ -1,253 +0,0 @@ ---- -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] -sidebar_position: 0 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; - -## sha256 - -Given an array of bytes, returns the resulting sha256 hash. -Specify a message_size to hash only the first `message_size` bytes of the input. - -```rust title="sha256" showLineNumbers -pub fn sha256(input: [u8; N]) -> [u8; 32] -``` -> Source code: noir_stdlib/src/hash/sha256.nr#L8-L10 - - -example: -```rust title="sha256_var" showLineNumbers -let digest = std::hash::sha256_var([x as u8], 1); -``` -> Source code: test_programs/execution_success/sha256/src/main.nr#L16-L18 - - -```rust -fn main() { - let x = [163, 117, 178, 149]; // some random bytes - let hash = std::sha256::sha256_var(x, 4); -} -``` - - - - -## blake2s - -Given an array of bytes, returns an array with the Blake2 hash - -```rust title="blake2s" showLineNumbers -pub fn blake2s(input: [u8; N]) -> [u8; 32] -``` -> Source code: noir_stdlib/src/hash/mod.nr#L18-L20 - - -example: - -```rust -fn main() { - let x = [163, 117, 178, 149]; // some random bytes - let hash = std::hash::blake2s(x); -} -``` - - - -## blake3 - -Given an array of bytes, returns an array with the Blake3 hash - -```rust title="blake3" showLineNumbers -pub fn blake3(input: [u8; N]) -> [u8; 32] -``` -> Source code: noir_stdlib/src/hash/mod.nr#L24-L26 - - -example: - -```rust -fn main() { - let x = [163, 117, 178, 149]; // some random bytes - let hash = std::hash::blake3(x); -} -``` - - - -## pedersen_hash - -Given an array of Fields, returns the Pedersen hash. - -```rust title="pedersen_hash" showLineNumbers -pub fn pedersen_hash(input: [Field; N]) -> Field -``` -> Source code: noir_stdlib/src/hash/mod.nr#L77-L79 - - -example: - -```rust title="pedersen-hash" showLineNumbers -fn main(x: Field, y: Field, expected_hash: Field) { - let hash = std::hash::pedersen_hash([x, y]); - assert_eq(hash, expected_hash); -} -``` -> Source code: test_programs/execution_success/pedersen_hash/src/main.nr#L1-L7 - - - - -## pedersen_commitment - -Given an array of Fields, returns the Pedersen commitment. - -```rust title="pedersen_commitment" showLineNumbers -pub fn pedersen_commitment(input: [Field; N]) -> EmbeddedCurvePoint { -``` -> Source code: noir_stdlib/src/hash/mod.nr#L29-L31 - - -example: - -```rust title="pedersen-commitment" showLineNumbers -fn main(x: Field, y: Field, expected_commitment: std::embedded_curve_ops::EmbeddedCurvePoint) { - let commitment = std::hash::pedersen_commitment([x, y]); - assert_eq(commitment.x, expected_commitment.x); - assert_eq(commitment.y, expected_commitment.y); -} -``` -> Source code: test_programs/execution_success/pedersen_commitment/src/main.nr#L1-L8 - - - - -## 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 title="keccak256" showLineNumbers -pub fn keccak256(input: [u8; N], message_size: u32) -> [u8; 32] -``` -> Source code: noir_stdlib/src/hash/mod.nr#L128-L130 - - -example: - -```rust title="keccak256" showLineNumbers -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 message 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); -} -``` -> Source code: test_programs/execution_success/keccak256/src/main.nr#L1-L21 - - - - -## 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 title="poseidon" showLineNumbers -use std::hash::poseidon; - -fn main(x1: [Field; 2], y1: pub Field, x2: [Field; 4], y2: pub Field) { - let hash1 = poseidon::bn254::hash_2(x1); - assert(hash1 == y1); - - let hash2 = poseidon::bn254::hash_4(x2); - assert(hash2 == y2); -} -``` -> Source code: test_programs/execution_success/poseidon_bn254_hash/src/main.nr#L1-L11 - - -## poseidon 2 - -Given an array of Fields, returns a new Field with the Poseidon2 Hash. Contrary to the Poseidon -function, there is only one hash and you can specify a message_size to hash only the first -`message_size` bytes of the input, - -```rust -// example for hashing the first three elements of the input -Poseidon2::hash(input, 3); -``` - -example: - -```rust title="poseidon2" showLineNumbers -use std::hash::poseidon2; - -fn main(inputs: [Field; 4], expected_hash: Field) { - let hash = poseidon2::Poseidon2::hash(inputs, inputs.len()); - assert_eq(hash, expected_hash); -} -``` -> Source code: test_programs/execution_success/poseidon2/src/main.nr#L1-L8 - - -## 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]) -> 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.34.0/noir/standard_library/cryptographic_primitives/index.md b/docs/versioned_docs/version-v0.34.0/noir/standard_library/cryptographic_primitives/index.md deleted file mode 100644 index 650f30165d5..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/standard_library/cryptographic_primitives/index.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -title: Cryptographic Primitives -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.34.0/noir/standard_library/cryptographic_primitives/schnorr.mdx b/docs/versioned_docs/version-v0.34.0/noir/standard_library/cryptographic_primitives/schnorr.mdx deleted file mode 100644 index 00e7f257612..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/standard_library/cryptographic_primitives/schnorr.mdx +++ /dev/null @@ -1,64 +0,0 @@ ---- -title: Schnorr Signatures -description: Learn how you can verify Schnorr signatures using Noir -keywords: [cryptographic primitives, Noir project, schnorr, signatures] -sidebar_position: 2 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; - -## schnorr::verify_signature - -Verifier for Schnorr signatures over the embedded curve (for BN254 it is Grumpkin). -See schnorr::verify_signature_slice for a version that works directly on slices. - -```rust title="schnorr_verify" showLineNumbers -pub fn verify_signature( - public_key_x: Field, - public_key_y: Field, - signature: [u8; 64], - message: [u8; N] -) -> bool -``` -> Source code: noir_stdlib/src/schnorr.nr#L2-L9 - - -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() -); - -... -``` - - - -## schnorr::verify_signature_slice - -Verifier for Schnorr signatures over the embedded curve (for BN254 it is Grumpkin) -where the message is a slice. - -```rust title="schnorr_verify_slice" showLineNumbers -pub fn verify_signature_slice( - public_key_x: Field, - public_key_y: Field, - signature: [u8; 64], - message: [u8] -) -> bool -``` -> Source code: noir_stdlib/src/schnorr.nr#L13-L20 - - - diff --git a/docs/versioned_docs/version-v0.34.0/noir/standard_library/fmtstr.md b/docs/versioned_docs/version-v0.34.0/noir/standard_library/fmtstr.md deleted file mode 100644 index 65a7da9996d..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/standard_library/fmtstr.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -title: fmtstr ---- - -`fmtstr` is the type resulting from using format string (`f"..."`). - -## Methods - -### quoted_contents - -```rust title="quoted_contents" showLineNumbers -comptime fn quoted_contents(self) -> Quoted {} -``` -> Source code: noir_stdlib/src/meta/format_string.nr#L3-L5 - - -Returns the format string contents (that is, without the leading and trailing double quotes) as a `Quoted` value. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.34.0/noir/standard_library/is_unconstrained.md b/docs/versioned_docs/version-v0.34.0/noir/standard_library/is_unconstrained.md deleted file mode 100644 index 51bb1bda8f1..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/standard_library/is_unconstrained.md +++ /dev/null @@ -1,69 +0,0 @@ ---- -title: Is Unconstrained Function -description: - The is_unconstrained function returns wether the context at that point of the program is unconstrained or not. -keywords: - [ - unconstrained - ] ---- - -It's very common for functions in circuits to take unconstrained hints of an expensive computation and then verify it. This is done by running the hint in an unconstrained context and then verifying the result in a constrained context. - -When a function is marked as unconstrained, any subsequent functions that it calls will also be run in an unconstrained context. However, if we are implementing a library function, other users might call it within an unconstrained context or a constrained one. Generally, in an unconstrained context we prefer just computing the result instead of taking a hint of it and verifying it, since that'd mean doing the same computation twice: - -```rust - -fn my_expensive_computation(){ - ... -} - -unconstrained fn my_expensive_computation_hint(){ - my_expensive_computation() -} - -pub fn external_interface(){ - my_expensive_computation_hint(); - // verify my_expensive_computation: If external_interface is called from unconstrained, this is redundant - ... -} - -``` - -In order to improve the performance in an unconstrained context you can use the function at `std::runtime::is_unconstrained() -> bool`: - - -```rust -use dep::std::runtime::is_unconstrained; - -fn my_expensive_computation(){ - ... -} - -unconstrained fn my_expensive_computation_hint(){ - my_expensive_computation() -} - -pub fn external_interface(){ - if is_unconstrained() { - my_expensive_computation(); - } else { - my_expensive_computation_hint(); - // verify my_expensive_computation - ... - } -} - -``` - -The is_unconstrained result is resolved at compile time, so in unconstrained contexts the compiler removes the else branch, and in constrained contexts the compiler removes the if branch, reducing the amount of compute necessary to run external_interface. - -Note that using `is_unconstrained` in a `comptime` context will also return `true`: - -``` -fn main() { - comptime { - assert(is_unconstrained()); - } -} -``` diff --git a/docs/versioned_docs/version-v0.34.0/noir/standard_library/logging.md b/docs/versioned_docs/version-v0.34.0/noir/standard_library/logging.md deleted file mode 100644 index db75ef9f86f..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/standard_library/logging.md +++ /dev/null @@ -1,78 +0,0 @@ ---- -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, - print statement, - debugging in noir, - noir std library, - logging tutorial, - basic logging in noir, - noir logging implementation, - noir debugging techniques, - rust, - ] ---- - -The standard library provides two familiar statements you can use: `println` and `print`. Despite being a limited implementation of rust's `println!` and `print!` macros, these constructs can be useful for debugging. - -You can print the output of both statements in your Noir code by using the `nargo execute` command or the `--show-output` flag when using `nargo test` (provided there are print statements in your tests). - -It is recommended to use `nargo execute` if you want to debug failing constraints with `println` or `print` 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). Neither `println`, nor `print` are callable for failed constraints caught at compile time. - -Both `print` and `println` are generic functions which can work on integers, fields, strings, and even structs or expressions. Note however, that slices are currently unsupported. For example: - -```rust -struct Person { - age: Field, - height: Field, -} - -fn main(age: Field, height: Field) { - let person = Person { - age: age, - height: height, - }; - println(person); - println(age + height); - println("Hello world!"); -} -``` - -You can print different types in the same statement (including strings) with a type called `fmtstr`. It can be specified in the same way as a normal string, just prepended with an "f" character: - -```rust - let fmt_str = f"i: {i}, j: {j}"; - println(fmt_str); - - let s = myStruct { y: x, x: y }; - println(s); - - println(f"i: {i}, s: {s}"); - - println(x); - println([x, y]); - - let foo = fooStruct { my_struct: s, foo: 15 }; - println(f"s: {s}, foo: {foo}"); - - println(15); // prints 0x0f, implicit Field - println(-1 as u8); // prints 255 - println(-1 as i8); // prints -1 -``` - -Examples shown above are interchangeable between the two `print` statements: - -```rust -let person = Person { age : age, height : height }; - -println(person); -print(person); - -println("Hello world!"); // Prints with a newline at the end of the input -print("Hello world!"); // Prints the input and keeps cursor on the same line -``` diff --git a/docs/versioned_docs/version-v0.34.0/noir/standard_library/merkle_trees.md b/docs/versioned_docs/version-v0.34.0/noir/standard_library/merkle_trees.md deleted file mode 100644 index 6a9ebf72ada..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/standard_library/merkle_trees.md +++ /dev/null @@ -1,58 +0,0 @@ ---- -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/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.as_slice()); - 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.34.0/noir/standard_library/meta/expr.md b/docs/versioned_docs/version-v0.34.0/noir/standard_library/meta/expr.md deleted file mode 100644 index f226c1eebb3..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/standard_library/meta/expr.md +++ /dev/null @@ -1,323 +0,0 @@ ---- -title: Expr ---- - -`std::meta::expr` contains methods on the built-in `Expr` type for quoted, syntactically valid expressions. - -## Methods - -### as_array - -```rust title="as_array" showLineNumbers -comptime fn as_array(self) -> Option<[Expr]> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L7-L9 - - -If this expression is an array, this returns a slice of each element in the array. - -### as_assert - -```rust title="as_assert" showLineNumbers -comptime fn as_assert(self) -> Option<(Expr, Option)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L12-L14 - - -If this expression is an assert, this returns the assert expression and the optional message. - -### as_assert_eq - -```rust title="as_assert_eq" showLineNumbers -comptime fn as_assert_eq(self) -> Option<(Expr, Expr, Option)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L17-L19 - - -If this expression is an assert_eq, this returns the left-hand-side and right-hand-side -expressions, together with the optional message. - -### as_assign - -```rust title="as_assign" showLineNumbers -comptime fn as_assign(self) -> Option<(Expr, Expr)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L22-L24 - - -If this expression is an assignment, this returns a tuple with the left hand side -and right hand side in order. - -### as_binary_op - -```rust title="as_binary_op" showLineNumbers -comptime fn as_binary_op(self) -> Option<(Expr, BinaryOp, Expr)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L32-L34 - - -If this expression is a binary operator operation ` `, -return the left-hand side, operator, and the right-hand side of the operation. - -### as_block - -```rust title="as_block" showLineNumbers -comptime fn as_block(self) -> Option<[Expr]> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L37-L39 - - -If this expression is a block `{ stmt1; stmt2; ...; stmtN }`, return -a slice containing each statement. - -### as_bool - -```rust title="as_bool" showLineNumbers -comptime fn as_bool(self) -> Option {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L42-L44 - - -If this expression is a boolean literal, return that literal. - -### as_comptime - -```rust title="as_comptime" showLineNumbers -comptime fn as_comptime(self) -> Option<[Expr]> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L50-L52 - - -If this expression is a `comptime { stmt1; stmt2; ...; stmtN }` block, -return each statement in the block. - -### as_function_call - -```rust title="as_function_call" showLineNumbers -comptime fn as_function_call(self) -> Option<(Expr, [Expr])> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L55-L57 - - -If this expression is a function call `foo(arg1, ..., argN)`, return -the function and a slice of each argument. - -### as_if - -```rust title="as_if" showLineNumbers -comptime fn as_if(self) -> Option<(Expr, Expr, Option)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L60-L62 - - -If this expression is an `if condition { then_branch } else { else_branch }`, -return the condition, then branch, and else branch. If there is no else branch, -`None` is returned for that branch instead. - -### as_index - -```rust title="as_index" showLineNumbers -comptime fn as_index(self) -> Option<(Expr, Expr)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L65-L67 - - -If this expression is an index into an array `array[index]`, return the -array and the index. - -### as_integer - -```rust title="as_integer" showLineNumbers -comptime fn as_integer(self) -> Option<(Field, bool)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L27-L29 - - -If this expression is an integer literal, return the integer as a field -as well as whether the integer is negative (true) or not (false). - -### as_let - -```rust title="as_let" showLineNumbers -comptime fn as_let(self) -> Option<(Expr, Option, Expr)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L70-L72 - - -If this expression is a let statement, returns the let pattern as an `Expr`, -the optional type annotation, and the assigned expression. - -### as_member_access - -```rust title="as_member_access" showLineNumbers -comptime fn as_member_access(self) -> Option<(Expr, Quoted)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L75-L77 - - -If this expression is a member access `foo.bar`, return the struct/tuple -expression and the field. The field will be represented as a quoted value. - -### as_method_call - -```rust title="as_method_call" showLineNumbers -comptime fn as_method_call(self) -> Option<(Expr, Quoted, [UnresolvedType], [Expr])> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L80-L82 - - -If this expression is a method call `foo.bar::(arg1, ..., argN)`, return -the receiver, method name, a slice of each generic argument, and a slice of each argument. - -### as_repeated_element_array - -```rust title="as_repeated_element_array" showLineNumbers -comptime fn as_repeated_element_array(self) -> Option<(Expr, Expr)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L85-L87 - - -If this expression is a repeated element array `[elem; length]`, return -the repeated element and the length expressions. - -### as_repeated_element_slice - -```rust title="as_repeated_element_slice" showLineNumbers -comptime fn as_repeated_element_slice(self) -> Option<(Expr, Expr)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L90-L92 - - -If this expression is a repeated element slice `[elem; length]`, return -the repeated element and the length expressions. - -### as_slice - -```rust title="as_slice" showLineNumbers -comptime fn as_slice(self) -> Option<[Expr]> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L95-L97 - - -If this expression is a slice literal `&[elem1, ..., elemN]`, -return each element of the slice. - -### as_tuple - -```rust title="as_tuple" showLineNumbers -comptime fn as_tuple(self) -> Option<[Expr]> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L100-L102 - - -If this expression is a tuple `(field1, ..., fieldN)`, -return each element of the tuple. - -### as_unary_op - -```rust title="as_unary_op" showLineNumbers -comptime fn as_unary_op(self) -> Option<(UnaryOp, Expr)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L105-L107 - - -If this expression is a unary operation ` `, -return the unary operator as well as the right-hand side expression. - -### as_unsafe - -```rust title="as_unsafe" showLineNumbers -comptime fn as_unsafe(self) -> Option<[Expr]> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L110-L112 - - -If this expression is an `unsafe { stmt1; ...; stmtN }` block, -return each statement inside in a slice. - -### has_semicolon - -```rust title="has_semicolon" showLineNumbers -comptime fn has_semicolon(self) -> bool {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L115-L117 - - -`true` if this expression is trailed by a semicolon. E.g. - -``` -comptime { - let expr1 = quote { 1 + 2 }.as_expr().unwrap(); - let expr2 = quote { 1 + 2; }.as_expr().unwrap(); - - assert(expr1.as_binary_op().is_some()); - assert(expr2.as_binary_op().is_some()); - - assert(!expr1.has_semicolon()); - assert(expr2.has_semicolon()); -} -``` - -### is_break - -```rust title="is_break" showLineNumbers -comptime fn is_break(self) -> bool {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L120-L122 - - -`true` if this expression is `break`. - -### is_continue - -```rust title="is_continue" showLineNumbers -comptime fn is_continue(self) -> bool {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L125-L127 - - -`true` if this expression is `continue`. - -### modify - -```rust title="modify" showLineNumbers -comptime fn modify(self, f: fn[Env](Expr) -> Option) -> Expr { -``` -> Source code: noir_stdlib/src/meta/expr.nr#L129-L131 - - -Applies a mapping function to this expression and to all of its sub-expressions. -`f` will be applied to each sub-expression first, then applied to the expression itself. - -This happens recursively for every expression within `self`. - -For example, calling `modify` on `(&[1], &[2, 3])` with an `f` that returns `Option::some` -for expressions that are integers, doubling them, would return `(&[2], &[4, 6])`. - -### quoted - -```rust title="quoted" showLineNumbers -comptime fn quoted(self) -> Quoted { -``` -> Source code: noir_stdlib/src/meta/expr.nr#L161-L163 - - -Returns this expression as a `Quoted` value. It's the same as `quote { $self }`. - -### resolve - -```rust title="resolve" showLineNumbers -comptime fn resolve(self, in_function: Option) -> TypedExpr {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L168-L170 - - -Resolves and type-checks this expression and returns the result as a `TypedExpr`. - -The `in_function` argument specifies where the expression is resolved: -- If it's `none`, the expression is resolved in the function where `resolve` was called -- If it's `some`, the expression is resolved in the given function - -If any names used by this expression are not in scope or if there are any type errors, -this will give compiler errors as if the expression was written directly into -the current `comptime` function. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.34.0/noir/standard_library/meta/function_def.md b/docs/versioned_docs/version-v0.34.0/noir/standard_library/meta/function_def.md deleted file mode 100644 index e490af68f7f..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/standard_library/meta/function_def.md +++ /dev/null @@ -1,166 +0,0 @@ ---- -title: FunctionDefinition ---- - -`std::meta::function_def` contains methods on the built-in `FunctionDefinition` type representing -a function definition in the source program. - -## Methods - -### add_attribute - -```rust title="add_attribute" showLineNumbers -comptime fn add_attribute(self, attribute: str) {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L3-L5 - - -Adds an attribute to the function. This is only valid -on functions in the current crate which have not yet been resolved. -This means any functions called at compile-time are invalid targets for this method. - -### body - -```rust title="body" showLineNumbers -comptime fn body(self) -> Expr {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L8-L10 - - -Returns the body of the function as an expression. This is only valid -on functions in the current crate which have not yet been resolved. -This means any functions called at compile-time are invalid targets for this method. - -### has_named_attribute - -```rust title="has_named_attribute" showLineNumbers -comptime fn has_named_attribute(self, name: str) -> bool {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L13-L15 - - -Returns true if this function has a custom attribute with the given name. - -### is_unconstrained - -```rust title="is_unconstrained" showLineNumbers -comptime fn is_unconstrained(self) -> bool {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L18-L20 - - -Returns true if this function is unconstrained. - -### module - -```rust title="module" showLineNumbers -comptime fn module(self) -> Module {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L23-L25 - - -Returns the module where the function is defined. - -### name - -```rust title="name" showLineNumbers -comptime fn name(self) -> Quoted {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L28-L30 - - -Returns the name of the function. - -### parameters - -```rust title="parameters" showLineNumbers -comptime fn parameters(self) -> [(Quoted, Type)] {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L33-L35 - - -Returns each parameter of the function as a tuple of (parameter pattern, parameter type). - -### return_type - -```rust title="return_type" showLineNumbers -comptime fn return_type(self) -> Type {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L38-L40 - - -The return type of the function. - -### set_body - -```rust title="set_body" showLineNumbers -comptime fn set_body(self, body: Expr) {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L43-L45 - - -Mutate the function body to a new expression. This is only valid -on functions in the current crate which have not yet been resolved. -This means any functions called at compile-time are invalid targets for this method. - -### set_parameters - -```rust title="set_parameters" showLineNumbers -comptime fn set_parameters(self, parameters: [(Quoted, Type)]) {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L48-L50 - - -Mutates the function's parameters to a new set of parameters. This is only valid -on functions in the current crate which have not yet been resolved. -This means any functions called at compile-time are invalid targets for this method. - -Expects a slice of (parameter pattern, parameter type) for each parameter. Requires -each parameter pattern to be a syntactically valid parameter. - -### set_return_type - -```rust title="set_return_type" showLineNumbers -comptime fn set_return_type(self, return_type: Type) {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L53-L55 - - -Mutates the function's return type to a new type. This is only valid -on functions in the current crate which have not yet been resolved. -This means any functions called at compile-time are invalid targets for this method. - -### set_return_public - -```rust title="set_return_public" showLineNumbers -comptime fn set_return_public(self, public: bool) {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L58-L60 - - -Mutates the function's return visibility to public (if `true` is given) or private (if `false` is given). -This is only valid on functions in the current crate which have not yet been resolved. -This means any functions called at compile-time are invalid targets for this method. - -### set_unconstrained - -```rust title="set_unconstrained" showLineNumbers -comptime fn set_unconstrained(self, value: bool) {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L63-L65 - - -Mutates the function to be unconstrained (if `true` is given) or not (if `false` is given). -This is only valid on functions in the current crate which have not yet been resolved. -This means any functions called at compile-time are invalid targets for this method. - -## Trait Implementations - -```rust -impl Eq for FunctionDefinition -impl Hash for FunctionDefinition -``` - -Note that each function is assigned a unique ID internally and this is what is used for -equality and hashing. So even functions with identical signatures and bodies may not -be equal in this sense if they were originally different items in the source program. diff --git a/docs/versioned_docs/version-v0.34.0/noir/standard_library/meta/index.md b/docs/versioned_docs/version-v0.34.0/noir/standard_library/meta/index.md deleted file mode 100644 index 73d6fe52285..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/standard_library/meta/index.md +++ /dev/null @@ -1,210 +0,0 @@ ---- -title: Metaprogramming -description: Noir's Metaprogramming API -keywords: [metaprogramming, comptime, macros, macro, quote, unquote] ---- - -`std::meta` is the entry point for Noir's metaprogramming API. This consists of `comptime` functions -and types used for inspecting and modifying Noir programs. - -## Functions - -### type_of - -```rust title="type_of" showLineNumbers -pub comptime fn type_of(x: T) -> Type {} -``` -> Source code: noir_stdlib/src/meta/mod.nr#L26-L28 - - -Returns the type of a variable at compile-time. - -Example: -```rust -comptime { - let x: i32 = 1; - let x_type: Type = std::meta::type_of(x); - - assert_eq(x_type, quote { i32 }.as_type()); -} -``` - -### unquote - -```rust title="unquote" showLineNumbers -pub comptime fn unquote(code: Quoted) -> Quoted { -``` -> Source code: noir_stdlib/src/meta/mod.nr#L18-L20 - - -Unquotes the passed-in token stream where this function was called. - -Example: -```rust -comptime { - let code = quote { 1 + 2 }; - - // let x = 1 + 2; - let x = unquote!(code); -} -``` - -### derive - -```rust title="derive" showLineNumbers -#[varargs] -pub comptime fn derive(s: StructDefinition, traits: [TraitDefinition]) -> Quoted { -``` -> Source code: noir_stdlib/src/meta/mod.nr#L46-L49 - - -Attribute placed on struct definitions. - -Creates a trait impl for each trait passed in as an argument. -To do this, the trait must have a derive handler registered -with `derive_via` beforehand. The traits in the stdlib that -can be derived this way are `Eq`, `Ord`, `Default`, and `Hash`. - -Example: -```rust -#[derive(Eq, Default)] -struct Foo { - x: i32, - y: T, -} - -fn main() { - let foo1 = Foo::default(); - let foo2 = Foo { x: 0, y: &[0] }; - assert_eq(foo1, foo2); -} -``` - -### derive_via - -```rust title="derive_via_signature" showLineNumbers -pub comptime fn derive_via(t: TraitDefinition, f: DeriveFunction) { -``` -> Source code: noir_stdlib/src/meta/mod.nr#L68-L70 - - -Attribute placed on trait definitions. - -Registers a function to create impls for the given trait -when the trait is used in a `derive` call. Users may use -this to register their own functions to enable their traits -to be derived by `derive`. - -Because this function requires a function as an argument which -should produce a trait impl for any given struct, users may find -it helpful to use a function like `std::meta::make_trait_impl` to -help creating these impls. - -Example: -```rust -#[derive_via(derive_do_nothing)] -trait DoNothing { - fn do_nothing(self); -} - -comptime fn derive_do_nothing(s: StructDefinition) -> Quoted { - let typ = s.as_type(); - quote { - impl DoNothing for $typ { - fn do_nothing(self) { - println("Nothing"); - } - } - } -} -``` - -As another example, `derive_eq` in the stdlib is used to derive the `Eq` -trait for any struct. It makes use of `make_trait_impl` to do this: - -```rust title="derive_eq" showLineNumbers -comptime fn derive_eq(s: StructDefinition) -> Quoted { - let signature = quote { fn eq(_self: Self, _other: Self) -> bool }; - let for_each_field = |name| quote { (_self.$name == _other.$name) }; - let body = |fields| { - if s.fields().len() == 0 { - quote { true } - } else { - fields - } - }; - crate::meta::make_trait_impl(s, quote { Eq }, signature, for_each_field, quote { & }, body) -} -``` -> Source code: noir_stdlib/src/cmp.nr#L10-L23 - - -### make_trait_impl - -```rust title="make_trait_impl" showLineNumbers -pub comptime fn make_trait_impl( - s: StructDefinition, - trait_name: Quoted, - function_signature: Quoted, - for_each_field: fn[Env1](Quoted) -> Quoted, - join_fields_with: Quoted, - body: fn[Env2](Quoted) -> Quoted -) -> Quoted { -``` -> Source code: noir_stdlib/src/meta/mod.nr#L87-L96 - - -A helper function to more easily create trait impls while deriving traits. - -Note that this function only works for traits which: -1. Have only one method -2. Have no generics on the trait itself. - - E.g. Using this on a trait such as `trait Foo { ... }` will result in the - generated impl incorrectly missing the `T` generic. - -If your trait fits these criteria then `make_trait_impl` is likely the easiest -way to write your derive handler. The arguments are as follows: - -- `s`: The struct to make the impl for -- `trait_name`: The name of the trait to derive. E.g. `quote { Eq }`. -- `function_signature`: The signature of the trait method to derive. E.g. `fn eq(self, other: Self) -> bool`. -- `for_each_field`: An operation to be performed on each field. E.g. `|name| quote { (self.$name == other.$name) }`. -- `join_fields_with`: A separator to join each result of `for_each_field` with. - E.g. `quote { & }`. You can also use an empty `quote {}` for no separator. -- `body`: The result of the field operations are passed into this function for any final processing. - This is the place to insert any setup/teardown code the trait requires. If the trait doesn't require - any such code, you can return the body as-is: `|body| body`. - -Example deriving `Hash`: - -```rust title="derive_hash" showLineNumbers -comptime fn derive_hash(s: StructDefinition) -> Quoted { - let name = quote { Hash }; - let signature = quote { fn hash(_self: Self, _state: &mut H) where H: std::hash::Hasher }; - let for_each_field = |name| quote { _self.$name.hash(_state); }; - crate::meta::make_trait_impl(s, name, signature, for_each_field, quote {}, |fields| fields) -} -``` -> Source code: noir_stdlib/src/hash/mod.nr#L147-L154 - - -Example deriving `Ord`: - -```rust title="derive_ord" showLineNumbers -comptime fn derive_ord(s: StructDefinition) -> Quoted { - let signature = quote { fn cmp(_self: Self, _other: Self) -> std::cmp::Ordering }; - let for_each_field = |name| quote { - if result == std::cmp::Ordering::equal() { - result = _self.$name.cmp(_other.$name); - } - }; - let body = |fields| quote { - let mut result = std::cmp::Ordering::equal(); - $fields - result - }; - crate::meta::make_trait_impl(s, quote { Ord }, signature, for_each_field, quote {}, body) -} -``` -> Source code: noir_stdlib/src/cmp.nr#L181-L196 - diff --git a/docs/versioned_docs/version-v0.34.0/noir/standard_library/meta/module.md b/docs/versioned_docs/version-v0.34.0/noir/standard_library/meta/module.md deleted file mode 100644 index efd3e61e125..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/standard_library/meta/module.md +++ /dev/null @@ -1,82 +0,0 @@ ---- -title: Module ---- - -`std::meta::module` contains methods on the built-in `Module` type which represents a module in the source program. -Note that this type represents a module generally, it isn't limited to only `mod my_submodule { ... }` -declarations in the source program. - -## Methods - -### add_item - -```rust title="add_item" showLineNumbers -comptime fn add_item(self, item: Quoted) {} -``` -> Source code: noir_stdlib/src/meta/module.nr#L3-L5 - - -Adds a top-level item (a function, a struct, a global, etc.) to the module. -Adding multiple items in one go is also valid if the `Quoted` value has multiple items in it. -Note that the items are type-checked as if they are inside the module they are being added to. - -### functions - -```rust title="functions" showLineNumbers -comptime fn functions(self) -> [FunctionDefinition] {} -``` -> Source code: noir_stdlib/src/meta/module.nr#L18-L20 - - -Returns each function defined in the module. - -### has_named_attribute - -```rust title="has_named_attribute" showLineNumbers -comptime fn has_named_attribute(self, name: str) -> bool {} -``` -> Source code: noir_stdlib/src/meta/module.nr#L8-L10 - - -Returns true if this module has a custom attribute with the given name. - -### is_contract - -```rust title="is_contract" showLineNumbers -comptime fn is_contract(self) -> bool {} -``` -> Source code: noir_stdlib/src/meta/module.nr#L13-L15 - - -`true` if this module is a contract module (was declared via `contract foo { ... }`). - -### name - -```rust title="name" showLineNumbers -comptime fn name(self) -> Quoted {} -``` -> Source code: noir_stdlib/src/meta/module.nr#L28-L30 - - -Returns the name of the module. - -### structs - -```rust title="structs" showLineNumbers -comptime fn structs(self) -> [StructDefinition] {} -``` -> Source code: noir_stdlib/src/meta/module.nr#L23-L25 - - -Returns each struct defined in the module. - -## Trait Implementations - -```rust -impl Eq for Module -impl Hash for Module -``` - -Note that each module is assigned a unique ID internally and this is what is used for -equality and hashing. So even modules with identical names and contents may not -be equal in this sense if they were originally different items in the source program. diff --git a/docs/versioned_docs/version-v0.34.0/noir/standard_library/meta/op.md b/docs/versioned_docs/version-v0.34.0/noir/standard_library/meta/op.md deleted file mode 100644 index 18d1f0768fd..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/standard_library/meta/op.md +++ /dev/null @@ -1,244 +0,0 @@ ---- -title: UnaryOp and BinaryOp ---- - -`std::meta::op` contains the `UnaryOp` and `BinaryOp` types as well as methods on them. -These types are used to represent a unary or binary operator respectively in Noir source code. - -## Types - -### UnaryOp - -Represents a unary operator. One of `-`, `!`, `&mut`, or `*`. - -### Methods - -#### is_minus - -```rust title="is_minus" showLineNumbers -pub fn is_minus(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L7-L9 - - -Returns `true` if this operator is `-`. - -#### is_not - -```rust title="is_not" showLineNumbers -pub fn is_not(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L13-L15 - - -`true` if this operator is `!` - -#### is_mutable_reference - -```rust title="is_mutable_reference" showLineNumbers -pub fn is_mutable_reference(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L19-L21 - - -`true` if this operator is `&mut` - -#### is_dereference - -```rust title="is_dereference" showLineNumbers -pub fn is_dereference(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L25-L27 - - -`true` if this operator is `*` - -#### quoted - -```rust title="unary_quoted" showLineNumbers -pub comptime fn quoted(self) -> Quoted { -``` -> Source code: noir_stdlib/src/meta/op.nr#L31-L33 - - -Returns this operator as a `Quoted` value. - -### Trait Implementations - -```rust -impl Eq for UnaryOp -impl Hash for UnaryOp -``` - -### BinaryOp - -Represents a binary operator. One of `+`, `-`, `*`, `/`, `%`, `==`, `!=`, `<`, `<=`, `>`, `>=`, `&`, `|`, `^`, `>>`, or `<<`. - -### Methods - -#### is_add - -```rust title="is_add" showLineNumbers -pub fn is_add(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L55-L57 - - -`true` if this operator is `+` - -#### is_subtract - -```rust title="is_subtract" showLineNumbers -pub fn is_subtract(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L61-L63 - - -`true` if this operator is `-` - -#### is_multiply - -```rust title="is_multiply" showLineNumbers -pub fn is_multiply(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L67-L69 - - -`true` if this operator is `*` - -#### is_divide - -```rust title="is_divide" showLineNumbers -pub fn is_divide(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L73-L75 - - -`true` if this operator is `/` - -#### is_modulo - -```rust title="is_modulo" showLineNumbers -pub fn is_modulo(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L145-L147 - - -`true` if this operator is `%` - -#### is_equal - -```rust title="is_equal" showLineNumbers -pub fn is_equal(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L79-L81 - - -`true` if this operator is `==` - -#### is_not_equal - -```rust title="is_not_equal" showLineNumbers -pub fn is_not_equal(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L85-L87 - - -`true` if this operator is `!=` - -#### is_less_than - -```rust title="is_less_than" showLineNumbers -pub fn is_less_than(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L91-L93 - - -`true` if this operator is `<` - -#### is_less_than_or_equal - -```rust title="is_less_than_or_equal" showLineNumbers -pub fn is_less_than_or_equal(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L97-L99 - - -`true` if this operator is `<=` - -#### is_greater_than - -```rust title="is_greater_than" showLineNumbers -pub fn is_greater_than(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L103-L105 - - -`true` if this operator is `>` - -#### is_greater_than_or_equal - -```rust title="is_greater_than_or_equal" showLineNumbers -pub fn is_greater_than_or_equal(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L109-L111 - - -`true` if this operator is `>=` - -#### is_and - -```rust title="is_and" showLineNumbers -pub fn is_and(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L115-L117 - - -`true` if this operator is `&` - -#### is_or - -```rust title="is_or" showLineNumbers -pub fn is_or(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L121-L123 - - -`true` if this operator is `|` - -#### is_shift_right - -```rust title="is_shift_right" showLineNumbers -pub fn is_shift_right(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L133-L135 - - -`true` if this operator is `>>` - -#### is_shift_left - -```rust title="is_shift_right" showLineNumbers -pub fn is_shift_right(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L133-L135 - - -`true` if this operator is `<<` - -#### quoted - -```rust title="binary_quoted" showLineNumbers -pub comptime fn quoted(self) -> Quoted { -``` -> Source code: noir_stdlib/src/meta/op.nr#L151-L153 - - -Returns this operator as a `Quoted` value. - -### Trait Implementations - -```rust -impl Eq for BinaryOp -impl Hash for BinaryOp -``` diff --git a/docs/versioned_docs/version-v0.34.0/noir/standard_library/meta/quoted.md b/docs/versioned_docs/version-v0.34.0/noir/standard_library/meta/quoted.md deleted file mode 100644 index 8fc188b25da..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/standard_library/meta/quoted.md +++ /dev/null @@ -1,138 +0,0 @@ ---- -title: Quoted ---- - -`std::meta::quoted` contains methods on the built-in `Quoted` type which represents -quoted token streams and is the result of the `quote { ... }` expression. - -## Methods - -### as_expr - -```rust title="as_expr" showLineNumbers -comptime fn as_expr(self) -> Option {} -``` -> Source code: noir_stdlib/src/meta/quoted.nr#L6-L8 - - -Parses the quoted token stream as an expression. Returns `Option::none()` if -the expression failed to parse. - -Example: - -```rust title="as_expr_example" showLineNumbers -#[test] - fn test_expr_as_function_call() { - comptime - { - let expr = quote { foo(42) }.as_expr().unwrap(); - let (_function, args) = expr.as_function_call().unwrap(); - assert_eq(args.len(), 1); - assert_eq(args[0].as_integer().unwrap(), (42, false)); - } - } -``` -> Source code: test_programs/noir_test_success/comptime_expr/src/main.nr#L331-L342 - - -### as_module - -```rust title="as_module" showLineNumbers -comptime fn as_module(self) -> Option {} -``` -> Source code: noir_stdlib/src/meta/quoted.nr#L11-L13 - - -Interprets this token stream as a module path leading to the name of a module. -Returns `Option::none()` if the module isn't found or this token stream cannot be parsed as a path. - -Example: - -```rust title="as_module_example" showLineNumbers -mod baz { - mod qux {} -} - -#[test] -fn as_module_test() { - comptime - { - let my_mod = quote { baz::qux }.as_module().unwrap(); - assert_eq(my_mod.name(), quote { qux }); - } -} -``` -> Source code: test_programs/compile_success_empty/comptime_module/src/main.nr#L102-L115 - - -### as_trait_constraint - -```rust title="as_trait_constraint" showLineNumbers -comptime fn as_trait_constraint(self) -> TraitConstraint {} -``` -> Source code: noir_stdlib/src/meta/quoted.nr#L16-L18 - - -Interprets this token stream as a trait constraint (without an object type). -Note that this function panics instead of returning `Option::none()` if the token -stream does not parse and resolve to a valid trait constraint. - -Example: - -```rust title="implements_example" showLineNumbers -fn function_with_where(_x: T) where T: SomeTrait { - comptime - { - let t = quote { T }.as_type(); - let some_trait_i32 = quote { SomeTrait }.as_trait_constraint(); - assert(t.implements(some_trait_i32)); - - assert(t.get_trait_impl(some_trait_i32).is_none()); - } -} -``` -> Source code: test_programs/compile_success_empty/comptime_type/src/main.nr#L154-L165 - - -### as_type - -```rust title="as_type" showLineNumbers -comptime fn as_type(self) -> Type {} -``` -> Source code: noir_stdlib/src/meta/quoted.nr#L21-L23 - - -Interprets this token stream as a resolved type. Panics if the token -stream doesn't parse to a type or if the type isn't a valid type in scope. - -```rust title="implements_example" showLineNumbers -fn function_with_where(_x: T) where T: SomeTrait { - comptime - { - let t = quote { T }.as_type(); - let some_trait_i32 = quote { SomeTrait }.as_trait_constraint(); - assert(t.implements(some_trait_i32)); - - assert(t.get_trait_impl(some_trait_i32).is_none()); - } -} -``` -> Source code: test_programs/compile_success_empty/comptime_type/src/main.nr#L154-L165 - - -### tokens - -```rust title="tokens" showLineNumbers -comptime fn tokens(self) -> [Quoted] {} -``` -> Source code: noir_stdlib/src/meta/quoted.nr#L26-L28 - - -Returns a slice of the individual tokens that form this token stream. - -## Trait Implementations - -```rust -impl Eq for Quoted -impl Hash for Quoted -``` diff --git a/docs/versioned_docs/version-v0.34.0/noir/standard_library/meta/struct_def.md b/docs/versioned_docs/version-v0.34.0/noir/standard_library/meta/struct_def.md deleted file mode 100644 index 212c636d12a..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/standard_library/meta/struct_def.md +++ /dev/null @@ -1,177 +0,0 @@ ---- -title: StructDefinition ---- - -`std::meta::struct_def` contains methods on the built-in `StructDefinition` type. -This type corresponds to `struct Name { field1: Type1, ... }` items in the source program. - -## Methods - -### add_attribute - -```rust title="add_attribute" showLineNumbers -comptime fn add_attribute(self, attribute: str) {} -``` -> Source code: noir_stdlib/src/meta/struct_def.nr#L3-L5 - - -Adds an attribute to the struct. - -### add_generic - -```rust title="add_generic" showLineNumbers -comptime fn add_generic(self, generic_name: str) -> Type {} -``` -> Source code: noir_stdlib/src/meta/struct_def.nr#L8-L10 - - -Adds an generic to the struct. Returns the new generic type. -Errors if the given generic name isn't a single identifier or if -the struct already has a generic with the same name. - -This method should be used carefully, if there is existing code referring -to the struct type it may be checked before this function is called and -see the struct with the original number of generics. This method should -thus be preferred to use on code generated from other macros and structs -that are not used in function signatures. - -Example: - -```rust title="add-generic-example" showLineNumbers -comptime fn add_generic(s: StructDefinition) { - assert_eq(s.generics().len(), 0); - let new_generic = s.add_generic("T"); - - let generics = s.generics(); - assert_eq(generics.len(), 1); - assert_eq(generics[0], new_generic); - } -``` -> Source code: test_programs/compile_success_empty/comptime_struct_definition/src/main.nr#L38-L47 - - -### as_type - -```rust title="as_type" showLineNumbers -comptime fn as_type(self) -> Type {} -``` -> Source code: noir_stdlib/src/meta/struct_def.nr#L15-L17 - - -Returns this struct as a type in the source program. If this struct has -any generics, the generics are also included as-is. - -### generics - -```rust title="generics" showLineNumbers -comptime fn generics(self) -> [Type] {} -``` -> Source code: noir_stdlib/src/meta/struct_def.nr#L26-L28 - - -Returns each generic on this struct. - -Example: - -``` -#[example] -struct Foo { - bar: [T; 2], - baz: Baz, -} - -comptime fn example(foo: StructDefinition) { - assert_eq(foo.generics().len(), 2); - - // Fails because `T` isn't in scope - // let t = quote { T }.as_type(); - // assert_eq(foo.generics()[0], t); -} -``` - -### fields - -```rust title="fields" showLineNumbers -comptime fn fields(self) -> [(Quoted, Type)] {} -``` -> Source code: noir_stdlib/src/meta/struct_def.nr#L33-L35 - - -Returns each field of this struct as a pair of (field name, field type). - -### has_named_attribute - -```rust title="has_named_attribute" showLineNumbers -comptime fn has_named_attribute(self, name: str) -> bool {} -``` -> Source code: noir_stdlib/src/meta/struct_def.nr#L20-L22 - - -Returns true if this struct has a custom attribute with the given name. - -### module - -```rust title="module" showLineNumbers -comptime fn module(self) -> Module {} -``` -> Source code: noir_stdlib/src/meta/struct_def.nr#L38-L40 - - -Returns the module where the struct is defined. - -### name - -```rust title="name" showLineNumbers -comptime fn name(self) -> Quoted {} -``` -> Source code: noir_stdlib/src/meta/struct_def.nr#L43-L45 - - -Returns the name of this struct - -Note that the returned quoted value will be just the struct name, it will -not be the full path to the struct, nor will it include any generics. - -### set_fields - -```rust title="set_fields" showLineNumbers -comptime fn set_fields(self, new_fields: [(Quoted, Type)]) {} -``` -> Source code: noir_stdlib/src/meta/struct_def.nr#L52-L54 - - -Sets the fields of this struct to the given fields list where each element -is a pair of the field's name and the field's type. Expects each field name -to be a single identifier. Note that this will override any previous fields -on this struct. If those should be preserved, use `.fields()` to retrieve the -current fields on the struct type and append the new fields from there. - -Example: - -```rust -// Change this struct to: -// struct Foo { -// a: u32, -// b: i8, -// } -#[mangle_fields] -struct Foo { x: Field } - -comptime fn mangle_fields(s: StructDefinition) { - s.set_fields(&[ - (quote { a }, quote { u32 }.as_type()), - (quote { b }, quote { i8 }.as_type()), - ]); -} -``` - -## Trait Implementations - -```rust -impl Eq for StructDefinition -impl Hash for StructDefinition -``` - -Note that each struct is assigned a unique ID internally and this is what is used for -equality and hashing. So even structs with identical generics and fields may not -be equal in this sense if they were originally different items in the source program. diff --git a/docs/versioned_docs/version-v0.34.0/noir/standard_library/meta/trait_constraint.md b/docs/versioned_docs/version-v0.34.0/noir/standard_library/meta/trait_constraint.md deleted file mode 100644 index 3106f732b5a..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/standard_library/meta/trait_constraint.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -title: TraitConstraint ---- - -`std::meta::trait_constraint` contains methods on the built-in `TraitConstraint` type which represents -a trait constraint that can be used to search for a trait implementation. This is similar -syntactically to just the trait itself, but can also contain generic arguments. E.g. `Eq`, `Default`, -`BuildHasher`. - -This type currently has no public methods but it can be used alongside `Type` in `implements` or `get_trait_impl`. - -## Trait Implementations - -```rust -impl Eq for TraitConstraint -impl Hash for TraitConstraint -``` diff --git a/docs/versioned_docs/version-v0.34.0/noir/standard_library/meta/trait_def.md b/docs/versioned_docs/version-v0.34.0/noir/standard_library/meta/trait_def.md deleted file mode 100644 index a1f363d46ff..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/standard_library/meta/trait_def.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: TraitDefinition ---- - -`std::meta::trait_def` contains methods on the built-in `TraitDefinition` type. This type -represents trait definitions such as `trait Foo { .. }` at the top-level of a program. - -## Methods - -### as_trait_constraint - -```rust title="as_trait_constraint" showLineNumbers -comptime fn as_trait_constraint(_self: Self) -> TraitConstraint {} -``` -> Source code: noir_stdlib/src/meta/trait_def.nr#L6-L8 - - -Converts this trait into a trait constraint. If there are any generics on this -trait, they will be kept as-is without instantiating or replacing them. - -## Trait Implementations - -```rust -impl Eq for TraitDefinition -impl Hash for TraitDefinition -``` diff --git a/docs/versioned_docs/version-v0.34.0/noir/standard_library/meta/trait_impl.md b/docs/versioned_docs/version-v0.34.0/noir/standard_library/meta/trait_impl.md deleted file mode 100644 index 66d31ed2560..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/standard_library/meta/trait_impl.md +++ /dev/null @@ -1,60 +0,0 @@ ---- -title: TraitImpl ---- - -`std::meta::trait_impl` contains methods on the built-in `TraitImpl` type which represents a trait -implementation such as `impl Foo for Bar { ... }`. - -## Methods - -### trait_generic_args - -```rust title="trait_generic_args" showLineNumbers -comptime fn trait_generic_args(self) -> [Type] {} -``` -> Source code: noir_stdlib/src/meta/trait_impl.nr#L3-L5 - - -Returns any generic arguments on the trait of this trait implementation, if any. - -```rs -impl Foo for Bar { ... } - -comptime { - let bar_type = quote { Bar }.as_type(); - let foo = quote { Foo }.as_trait_constraint(); - - let my_impl: TraitImpl = bar_type.get_trait_impl(foo).unwrap(); - - let generics = my_impl.trait_generic_args(); - assert_eq(generics.len(), 2); - - assert_eq(generics[0], quote { i32 }.as_type()); - assert_eq(generics[1], quote { Field }.as_type()); -} -``` - -### methods - -```rust title="methods" showLineNumbers -comptime fn methods(self) -> [FunctionDefinition] {} -``` -> Source code: noir_stdlib/src/meta/trait_impl.nr#L8-L10 - - -Returns each method in this trait impl. - -Example: - -```rs -comptime { - let i32_type = quote { i32 }.as_type(); - let eq = quote { Eq }.as_trait_constraint(); - - let impl_eq_for_i32: TraitImpl = i32_type.get_trait_impl(eq).unwrap(); - let methods = impl_eq_for_i32.methods(); - - assert_eq(methods.len(), 1); - assert_eq(methods[0].name(), quote { eq }); -} -``` diff --git a/docs/versioned_docs/version-v0.34.0/noir/standard_library/meta/typ.md b/docs/versioned_docs/version-v0.34.0/noir/standard_library/meta/typ.md deleted file mode 100644 index 6c9f4b8d087..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/standard_library/meta/typ.md +++ /dev/null @@ -1,239 +0,0 @@ ---- -title: Type ---- - -`std::meta::typ` contains methods on the built-in `Type` type used for representing -a type in the source program. - -## Functions - -```rust title="fresh_type_variable" showLineNumbers -pub comptime fn fresh_type_variable() -> Type {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L5-L7 - - -Creates and returns an unbound type variable. This is a special kind of type internal -to type checking which will type check with any other type. When it is type checked -against another type it will also be set to that type. For example, if `a` is a type -variable and we have the type equality `(a, i32) = (u8, i32)`, the compiler will set -`a` equal to `u8`. - -Unbound type variables will often be rendered as `_` while printing them. Bound type -variables will appear as the type they are bound to. - -This can be used in conjunction with functions which internally perform type checks -such as `Type::implements` or `Type::get_trait_impl` to potentially grab some of the types used. - -Note that calling `Type::implements` or `Type::get_trait_impl` on a type variable will always -fail. - -Example: - -```rust title="serialize-setup" showLineNumbers -trait Serialize {} - -impl Serialize<1> for Field {} - -impl Serialize for [T; N] - where T: Serialize {} - -impl Serialize for (T, U) - where T: Serialize, U: Serialize {} -``` -> Source code: test_programs/compile_success_empty/comptime_type/src/main.nr#L20-L30 - -```rust title="fresh-type-variable-example" showLineNumbers -let typevar1 = std::meta::typ::fresh_type_variable(); - let constraint = quote { Serialize<$typevar1> }.as_trait_constraint(); - let field_type = quote { Field }.as_type(); - - // Search for a trait impl (binding typevar1 to 1 when the impl is found): - assert(field_type.implements(constraint)); - - // typevar1 should be bound to the "1" generic now: - assert_eq(typevar1.as_constant().unwrap(), 1); - - // If we want to do the same with a different type, we need to - // create a new type variable now that `typevar1` is bound - let typevar2 = std::meta::typ::fresh_type_variable(); - let constraint = quote { Serialize<$typevar2> }.as_trait_constraint(); - let array_type = quote { [(Field, Field); 5] }.as_type(); - assert(array_type.implements(constraint)); - - // Now typevar2 should be bound to the serialized pair size 2 times the array length 5 - assert_eq(typevar2.as_constant().unwrap(), 10); -``` -> Source code: test_programs/compile_success_empty/comptime_type/src/main.nr#L130-L150 - - -## Methods - -### as_array - -```rust title="as_array" showLineNumbers -comptime fn as_array(self) -> Option<(Type, Type)> {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L11-L13 - - -If this type is an array, return a pair of (element type, size type). - -Example: - -```rust -comptime { - let array_type = quote { [Field; 3] }.as_type(); - let (field_type, three_type) = array_type.as_array().unwrap(); - - assert(field_type.is_field()); - assert_eq(three_type.as_constant().unwrap(), 3); -} -``` - -### as_constant - -```rust title="as_constant" showLineNumbers -comptime fn as_constant(self) -> Option {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L16-L18 - - -If this type is a constant integer (such as the `3` in the array type `[Field; 3]`), -return the numeric constant. - -### as_integer - -```rust title="as_integer" showLineNumbers -comptime fn as_integer(self) -> Option<(bool, u8)> {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L21-L23 - - -If this is an integer type, return a boolean which is `true` -if the type is signed, as well as the number of bits of this integer type. - -### as_slice - -```rust title="as_slice" showLineNumbers -comptime fn as_slice(self) -> Option {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L26-L28 - - -If this is a slice type, return the element type of the slice. - -### as_str - -```rust title="as_str" showLineNumbers -comptime fn as_str(self) -> Option {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L31-L33 - - -If this is a `str` type, returns the length `N` as a type. - -### as_struct - -```rust title="as_struct" showLineNumbers -comptime fn as_struct(self) -> Option<(StructDefinition, [Type])> {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L36-L38 - - -If this is a struct type, returns the struct in addition to -any generic arguments on this type. - -### as_tuple - -```rust title="as_tuple" showLineNumbers -comptime fn as_tuple(self) -> Option<[Type]> {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L41-L43 - - -If this is a tuple type, returns each element type of the tuple. - -### get_trait_impl - -```rust title="get_trait_impl" showLineNumbers -comptime fn get_trait_impl(self, constraint: TraitConstraint) -> Option {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L46-L48 - - -Retrieves the trait implementation that implements the given -trait constraint for this type. If the trait constraint is not -found, `None` is returned. Note that since the concrete trait implementation -for a trait constraint specified from a `where` clause is unknown, -this function will return `None` in these cases. If you only want to know -whether a type implements a trait, use `implements` instead. - -Example: - -```rust -comptime { - let field_type = quote { Field }.as_type(); - let default = quote { Default }.as_trait_constraint(); - - let the_impl: TraitImpl = field_type.get_trait_impl(default).unwrap(); - assert(the_impl.methods().len(), 1); -} -``` - -### implements - -```rust title="implements" showLineNumbers -comptime fn implements(self, constraint: TraitConstraint) -> bool {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L51-L53 - - -`true` if this type implements the given trait. Note that unlike -`get_trait_impl` this will also return true for any `where` constraints -in scope. - -Example: - -```rust -fn foo() where T: Default { - comptime { - let field_type = quote { Field }.as_type(); - let default = quote { Default }.as_trait_constraint(); - assert(field_type.implements(default)); - - let t = quote { T }.as_type(); - assert(t.implements(default)); - } -} -``` - -### is_bool - -```rust title="is_bool" showLineNumbers -comptime fn is_bool(self) -> bool {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L56-L58 - - -`true` if this type is `bool`. - -### is_field - -```rust title="is_field" showLineNumbers -comptime fn is_field(self) -> bool {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L61-L63 - - -`true` if this type is `Field`. - -## Trait Implementations - -```rust -impl Eq for Type -impl Hash for Type -``` -Note that this is syntactic equality, this is not the same as whether two types will type check -to be the same type. Unless type inference or generics are being used however, users should not -typically have to worry about this distinction unless `std::meta::typ::fresh_type_variable` is used. diff --git a/docs/versioned_docs/version-v0.34.0/noir/standard_library/meta/typed_expr.md b/docs/versioned_docs/version-v0.34.0/noir/standard_library/meta/typed_expr.md deleted file mode 100644 index 1ee71c8b064..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/standard_library/meta/typed_expr.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -title: TypedExpr ---- - -`std::meta::typed_expr` contains methods on the built-in `TypedExpr` type for resolved and type-checked expressions. - -## Methods - -### get_type - -```rust title="as_function_definition" showLineNumbers -comptime fn as_function_definition(self) -> Option {} -``` -> Source code: noir_stdlib/src/meta/typed_expr.nr#L7-L9 - - -If this expression refers to a function definitions, returns it. Otherwise returns `Option::none()`. - -### get_type - -```rust title="get_type" showLineNumbers -comptime fn get_type(self) -> Option {} -``` -> Source code: noir_stdlib/src/meta/typed_expr.nr#L13-L15 - - -Returns the type of the expression, or `Option::none()` if there were errors when the expression was previously resolved. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.34.0/noir/standard_library/meta/unresolved_type.md b/docs/versioned_docs/version-v0.34.0/noir/standard_library/meta/unresolved_type.md deleted file mode 100644 index d6f2b1494bb..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/standard_library/meta/unresolved_type.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -title: UnresolvedType ---- - -`std::meta::unresolved_type` contains methods on the built-in `UnresolvedType` type for the syntax of types. - -## Methods - -### is_field - -```rust title="is_field" showLineNumbers -comptime fn is_field(self) -> bool {} -``` -> Source code: noir_stdlib/src/meta/unresolved_type.nr#L3-L5 - - -Returns true if this type refers to the Field type. diff --git a/docs/versioned_docs/version-v0.34.0/noir/standard_library/options.md b/docs/versioned_docs/version-v0.34.0/noir/standard_library/options.md deleted file mode 100644 index a1bd4e1de5f..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/standard_library/options.md +++ /dev/null @@ -1,101 +0,0 @@ ---- -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), -} -``` - -The `Option` type, already imported into your Noir program, can be used directly: - -```rust -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. - -### expect - -Asserts `self.is_some()` with a provided custom message and returns the contained `Some` value. The custom message is expected to be a format string. - -### 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.34.0/noir/standard_library/recursion.mdx b/docs/versioned_docs/version-v0.34.0/noir/standard_library/recursion.mdx deleted file mode 100644 index 60414a2fa51..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/standard_library/recursion.mdx +++ /dev/null @@ -1,85 +0,0 @@ ---- -title: Recursive Proofs -description: Learn about how to write recursive proofs in Noir. -keywords: [recursion, recursive proofs, verification_key, verify_proof] ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; - -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. - -Read [the explainer on recursion](../../explainers/explainer-recursion.md) to know more about this function and the [guide on how to use it.](../../how_to/how-to-recursion.md) - -## The `#[recursive]` Attribute - -In Noir, the `#[recursive]` attribute is used to indicate that a circuit is designed for recursive proof generation. When applied, it informs the compiler and the tooling that the circuit should be compiled in a way that makes its proofs suitable for recursive verification. This attribute eliminates the need for manual flagging of recursion at the tooling level, streamlining the proof generation process for recursive circuits. - -### Example usage with `#[recursive]` - -```rust -#[recursive] -fn main(x: Field, y: pub Field) { - assert(x == y, "x and y are not equal"); -} - -// This marks the circuit as recursion-friendly and indicates that proofs generated from this circuit -// are intended for recursive verification. -``` - -By incorporating this attribute directly in the circuit's definition, tooling like Nargo and NoirJS can automatically execute recursive-specific duties for Noir programs (e.g. recursive-friendly proof artifact generation) without additional flags or configurations. - -## Verifying Recursive Proofs - -```rust -#[foreign(recursive_aggregation)] -pub fn verify_proof(verification_key: [Field], proof: [Field], public_inputs: [Field], key_hash: Field) {} -``` - - - -## Example usage - -```rust - -fn main( - verification_key : [Field; 114], - proof : [Field; 93], - public_inputs : [Field; 1], - key_hash : Field, - proof_b : [Field; 93], -) { - std::verify_proof( - verification_key, - proof, - public_inputs, - key_hash - ); - - std::verify_proof( - verification_key, - proof_b, - public_inputs, - key_hash - ); -} -``` - -You can see a full example of recursive proofs in [this example recursion demo repo](https://github.com/noir-lang/noir-examples/tree/master/recursion). - -## 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. - -### `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. diff --git a/docs/versioned_docs/version-v0.34.0/noir/standard_library/traits.md b/docs/versioned_docs/version-v0.34.0/noir/standard_library/traits.md deleted file mode 100644 index 0629623a15a..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/standard_library/traits.md +++ /dev/null @@ -1,625 +0,0 @@ ---- -title: Traits -description: Noir's stdlib provides a few commonly used traits. -keywords: [traits, trait, interface, protocol, default, add, eq] ---- - -## `std::default` - -### `std::default::Default` - -```rust title="default-trait" showLineNumbers -trait Default { - fn default() -> Self; -} -``` -> Source code: noir_stdlib/src/default.nr#L4-L8 - - -Constructs a default value of a type. - -Implementations: -```rust -impl Default for Field { .. } - -impl Default for i8 { .. } -impl Default for i16 { .. } -impl Default for i32 { .. } -impl Default for i64 { .. } - -impl Default for u8 { .. } -impl Default for u16 { .. } -impl Default for u32 { .. } -impl Default for u64 { .. } - -impl Default for () { .. } -impl Default for bool { .. } - -impl Default for [T; N] - where T: Default { .. } - -impl Default for [T] { .. } - -impl Default for (A, B) - where A: Default, B: Default { .. } - -impl Default for (A, B, C) - where A: Default, B: Default, C: Default { .. } - -impl Default for (A, B, C, D) - where A: Default, B: Default, C: Default, D: Default { .. } - -impl Default for (A, B, C, D, E) - where A: Default, B: Default, C: Default, D: Default, E: Default { .. } -``` - -For primitive integer types, the return value of `default` is `0`. Container -types such as arrays are filled with default values of their element type, -except slices whose length is unknown and thus defaulted to zero. - ---- - -## `std::convert` - -### `std::convert::From` - -```rust title="from-trait" showLineNumbers -trait From { - fn from(input: T) -> Self; -} -``` -> Source code: noir_stdlib/src/convert.nr#L1-L5 - - -The `From` trait defines how to convert from a given type `T` to the type on which the trait is implemented. - -The Noir standard library provides a number of implementations of `From` between primitive types. -```rust title="from-impls" showLineNumbers -// Unsigned integers - -impl From for u32 { - fn from(value: u8) -> u32 { - value as u32 - } -} - -impl From for u64 { - fn from(value: u8) -> u64 { - value as u64 - } -} -impl From for u64 { - fn from(value: u32) -> u64 { - value as u64 - } -} - -impl From for Field { - fn from(value: u8) -> Field { - value as Field - } -} -impl From for Field { - fn from(value: u32) -> Field { - value as Field - } -} -impl From for Field { - fn from(value: u64) -> Field { - value as Field - } -} - -// Signed integers - -impl From for i32 { - fn from(value: i8) -> i32 { - value as i32 - } -} - -impl From for i64 { - fn from(value: i8) -> i64 { - value as i64 - } -} -impl From for i64 { - fn from(value: i32) -> i64 { - value as i64 - } -} - -// Booleans -impl From for u8 { - fn from(value: bool) -> u8 { - value as u8 - } -} -impl From for u32 { - fn from(value: bool) -> u32 { - value as u32 - } -} -impl From for u64 { - fn from(value: bool) -> u64 { - value as u64 - } -} -impl From for i8 { - fn from(value: bool) -> i8 { - value as i8 - } -} -impl From for i32 { - fn from(value: bool) -> i32 { - value as i32 - } -} -impl From for i64 { - fn from(value: bool) -> i64 { - value as i64 - } -} -impl From for Field { - fn from(value: bool) -> Field { - value as Field - } -} -``` -> Source code: noir_stdlib/src/convert.nr#L25-L116 - - -#### When to implement `From` - -As a general rule of thumb, `From` may be implemented in the [situations where it would be suitable in Rust](https://doc.rust-lang.org/std/convert/trait.From.html#when-to-implement-from): - -- The conversion is *infallible*: Noir does not provide an equivalent to Rust's `TryFrom`, if the conversion can fail then provide a named method instead. -- The conversion is *lossless*: semantically, it should not lose or discard information. For example, `u32: From` can losslessly convert any `u16` into a valid `u32` such that the original `u16` can be recovered. On the other hand, `u16: From` should not be implemented as `2**16` is a `u32` which cannot be losslessly converted into a `u16`. -- The conversion is *value-preserving*: the conceptual kind and meaning of the resulting value is the same, even though the Noir type and technical representation might be different. While it's possible to infallibly and losslessly convert a `u8` into a `str<2>` hex representation, `4u8` and `"04"` are too different for `str<2>: From` to be implemented. -- The conversion is *obvious*: it's the only reasonable conversion between the two types. If there's ambiguity on how to convert between them such that the same input could potentially map to two different values then a named method should be used. For instance rather than implementing `U128: From<[u8; 16]>`, the methods `U128::from_le_bytes` and `U128::from_be_bytes` are used as otherwise the endianness of the array would be ambiguous, resulting in two potential values of `U128` from the same byte array. - -One additional recommendation specific to Noir is: -- The conversion is *efficient*: it's relatively cheap to convert between the two types. Due to being a ZK DSL, it's more important to avoid unnecessary computation compared to Rust. If the implementation of `From` would encourage users to perform unnecessary conversion, resulting in additional proving time, then it may be preferable to expose functionality such that this conversion may be avoided. - -### `std::convert::Into` - -The `Into` trait is defined as the reciprocal of `From`. It should be easy to convince yourself that if we can convert to type `A` from type `B`, then it's possible to convert type `B` into type `A`. - -For this reason, implementing `From` on a type will automatically generate a matching `Into` implementation. One should always prefer implementing `From` over `Into` as implementing `Into` will not generate a matching `From` implementation. - -```rust title="into-trait" showLineNumbers -trait Into { - fn into(self) -> T; -} - -impl Into for U where T: From { - fn into(self) -> T { - T::from(self) - } -} -``` -> Source code: noir_stdlib/src/convert.nr#L13-L23 - - -`Into` is most useful when passing function arguments where the types don't quite match up with what the function expects. In this case, the compiler has enough type information to perform the necessary conversion by just appending `.into()` onto the arguments in question. - ---- - -## `std::cmp` - -### `std::cmp::Eq` - -```rust title="eq-trait" showLineNumbers -trait Eq { - fn eq(self, other: Self) -> bool; -} -``` -> Source code: noir_stdlib/src/cmp.nr#L4-L8 - - -Returns `true` if `self` is equal to `other`. Implementing this trait on a type -allows the type to be used with `==` and `!=`. - -Implementations: -```rust -impl Eq for Field { .. } - -impl Eq for i8 { .. } -impl Eq for i16 { .. } -impl Eq for i32 { .. } -impl Eq for i64 { .. } - -impl Eq for u8 { .. } -impl Eq for u16 { .. } -impl Eq for u32 { .. } -impl Eq for u64 { .. } - -impl Eq for () { .. } -impl Eq for bool { .. } - -impl Eq for [T; N] - where T: Eq { .. } - -impl Eq for [T] - where T: Eq { .. } - -impl Eq for (A, B) - where A: Eq, B: Eq { .. } - -impl Eq for (A, B, C) - where A: Eq, B: Eq, C: Eq { .. } - -impl Eq for (A, B, C, D) - where A: Eq, B: Eq, C: Eq, D: Eq { .. } - -impl Eq for (A, B, C, D, E) - where A: Eq, B: Eq, C: Eq, D: Eq, E: Eq { .. } -``` - -### `std::cmp::Ord` - -```rust title="ord-trait" showLineNumbers -trait Ord { - fn cmp(self, other: Self) -> Ordering; -} -``` -> Source code: noir_stdlib/src/cmp.nr#L175-L179 - - -`a.cmp(b)` compares two values returning `Ordering::less()` if `a < b`, -`Ordering::equal()` if `a == b`, or `Ordering::greater()` if `a > b`. -Implementing this trait on a type allows `<`, `<=`, `>`, and `>=` to be -used on values of the type. - -`std::cmp` also provides `max` and `min` functions for any type which implements the `Ord` trait. - -Implementations: - -```rust -impl Ord for u8 { .. } -impl Ord for u16 { .. } -impl Ord for u32 { .. } -impl Ord for u64 { .. } - -impl Ord for i8 { .. } -impl Ord for i16 { .. } -impl Ord for i32 { .. } - -impl Ord for i64 { .. } - -impl Ord for () { .. } -impl Ord for bool { .. } - -impl Ord for [T; N] - where T: Ord { .. } - -impl Ord for [T] - where T: Ord { .. } - -impl Ord for (A, B) - where A: Ord, B: Ord { .. } - -impl Ord for (A, B, C) - where A: Ord, B: Ord, C: Ord { .. } - -impl Ord for (A, B, C, D) - where A: Ord, B: Ord, C: Ord, D: Ord { .. } - -impl Ord for (A, B, C, D, E) - where A: Ord, B: Ord, C: Ord, D: Ord, E: Ord { .. } -``` - ---- - -## `std::ops` - -### `std::ops::Add`, `std::ops::Sub`, `std::ops::Mul`, and `std::ops::Div` - -These traits abstract over addition, subtraction, multiplication, and division respectively. -Implementing these traits for a given type will also allow that type to be used with the corresponding operator -for that trait (`+` for Add, etc) in addition to the normal method names. - -```rust title="add-trait" showLineNumbers -trait Add { - fn add(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/arith.nr#L1-L5 - -```rust title="sub-trait" showLineNumbers -trait Sub { - fn sub(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/arith.nr#L60-L64 - -```rust title="mul-trait" showLineNumbers -trait Mul { - fn mul(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/arith.nr#L119-L123 - -```rust title="div-trait" showLineNumbers -trait Div { - fn div(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/arith.nr#L178-L182 - - -The implementations block below is given for the `Add` trait, but the same types that implement -`Add` also implement `Sub`, `Mul`, and `Div`. - -Implementations: -```rust -impl Add for Field { .. } - -impl Add for i8 { .. } -impl Add for i16 { .. } -impl Add for i32 { .. } -impl Add for i64 { .. } - -impl Add for u8 { .. } -impl Add for u16 { .. } -impl Add for u32 { .. } -impl Add for u64 { .. } -``` - -### `std::ops::Rem` - -```rust title="rem-trait" showLineNumbers -trait Rem{ - fn rem(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/arith.nr#L237-L241 - - -`Rem::rem(a, b)` is the remainder function returning the result of what is -left after dividing `a` and `b`. Implementing `Rem` allows the `%` operator -to be used with the implementation type. - -Unlike other numeric traits, `Rem` is not implemented for `Field`. - -Implementations: -```rust -impl Rem for u8 { fn rem(self, other: u8) -> u8 { self % other } } -impl Rem for u16 { fn rem(self, other: u16) -> u16 { self % other } } -impl Rem for u32 { fn rem(self, other: u32) -> u32 { self % other } } -impl Rem for u64 { fn rem(self, other: u64) -> u64 { self % other } } - -impl Rem for i8 { fn rem(self, other: i8) -> i8 { self % other } } -impl Rem for i16 { fn rem(self, other: i16) -> i16 { self % other } } -impl Rem for i32 { fn rem(self, other: i32) -> i32 { self % other } } -impl Rem for i64 { fn rem(self, other: i64) -> i64 { self % other } } -``` - -### `std::ops::Neg` - -```rust title="neg-trait" showLineNumbers -trait Neg { - fn neg(self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/arith.nr#L290-L294 - - -`Neg::neg` is equivalent to the unary negation operator `-`. - -Implementations: -```rust title="neg-trait-impls" showLineNumbers -impl Neg for Field { - fn neg(self) -> Field { - -self - } -} - -impl Neg for i8 { - fn neg(self) -> i8 { - -self - } -} -impl Neg for i16 { - fn neg(self) -> i16 { - -self - } -} -impl Neg for i32 { - fn neg(self) -> i32 { - -self - } -} -impl Neg for i64 { - fn neg(self) -> i64 { - -self - } -} -``` -> Source code: noir_stdlib/src/ops/arith.nr#L296-L323 - - -### `std::ops::Not` - -```rust title="not-trait" showLineNumbers -trait Not { - fn not(self: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/bit.nr#L1-L5 - - -`Not::not` is equivalent to the unary bitwise NOT operator `!`. - -Implementations: -```rust title="not-trait-impls" showLineNumbers -impl Not for bool { - fn not(self) -> bool { - !self - } -} - -impl Not for u64 { - fn not(self) -> u64 { - !self - } -} -impl Not for u32 { - fn not(self) -> u32 { - !self - } -} -impl Not for u16 { - fn not(self) -> u16 { - !self - } -} -impl Not for u8 { - fn not(self) -> u8 { - !self - } -} -impl Not for u1 { - fn not(self) -> u1 { - !self - } -} - -impl Not for i8 { - fn not(self) -> i8 { - !self - } -} -impl Not for i16 { - fn not(self) -> i16 { - !self - } -} -impl Not for i32 { - fn not(self) -> i32 { - !self - } -} -impl Not for i64 { - fn not(self) -> i64 { - !self - } -} -``` -> Source code: noir_stdlib/src/ops/bit.nr#L7-L60 - - -### `std::ops::{ BitOr, BitAnd, BitXor }` - -```rust title="bitor-trait" showLineNumbers -trait BitOr { - fn bitor(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/bit.nr#L62-L66 - -```rust title="bitand-trait" showLineNumbers -trait BitAnd { - fn bitand(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/bit.nr#L121-L125 - -```rust title="bitxor-trait" showLineNumbers -trait BitXor { - fn bitxor(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/bit.nr#L180-L184 - - -Traits for the bitwise operations `|`, `&`, and `^`. - -Implementing `BitOr`, `BitAnd` or `BitXor` for a type allows the `|`, `&`, or `^` operator respectively -to be used with the type. - -The implementations block below is given for the `BitOr` trait, but the same types that implement -`BitOr` also implement `BitAnd` and `BitXor`. - -Implementations: -```rust -impl BitOr for bool { fn bitor(self, other: bool) -> bool { self | other } } - -impl BitOr for u8 { fn bitor(self, other: u8) -> u8 { self | other } } -impl BitOr for u16 { fn bitor(self, other: u16) -> u16 { self | other } } -impl BitOr for u32 { fn bitor(self, other: u32) -> u32 { self | other } } -impl BitOr for u64 { fn bitor(self, other: u64) -> u64 { self | other } } - -impl BitOr for i8 { fn bitor(self, other: i8) -> i8 { self | other } } -impl BitOr for i16 { fn bitor(self, other: i16) -> i16 { self | other } } -impl BitOr for i32 { fn bitor(self, other: i32) -> i32 { self | other } } -impl BitOr for i64 { fn bitor(self, other: i64) -> i64 { self | other } } -``` - -### `std::ops::{ Shl, Shr }` - -```rust title="shl-trait" showLineNumbers -trait Shl { - fn shl(self, other: u8) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/bit.nr#L239-L243 - -```rust title="shr-trait" showLineNumbers -trait Shr { - fn shr(self, other: u8) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/bit.nr#L292-L296 - - -Traits for a bit shift left and bit shift right. - -Implementing `Shl` for a type allows the left shift operator (`<<`) to be used with the implementation type. -Similarly, implementing `Shr` allows the right shift operator (`>>`) to be used with the type. - -Note that bit shifting is not currently implemented for signed types. - -The implementations block below is given for the `Shl` trait, but the same types that implement -`Shl` also implement `Shr`. - -Implementations: -```rust -impl Shl for u8 { fn shl(self, other: u8) -> u8 { self << other } } -impl Shl for u16 { fn shl(self, other: u16) -> u16 { self << other } } -impl Shl for u32 { fn shl(self, other: u32) -> u32 { self << other } } -impl Shl for u64 { fn shl(self, other: u64) -> u64 { self << other } } -``` - ---- - -## `std::append` - -### `std::append::Append` - -`Append` can abstract over types that can be appended to - usually container types: - -```rust title="append-trait" showLineNumbers -trait Append { - fn empty() -> Self; - fn append(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/append.nr#L9-L14 - - -`Append` requires two methods: - -- `empty`: Constructs an empty value of `Self`. -- `append`: Append two values together, returning the result. - -Additionally, it is expected that for any implementation: - -- `T::empty().append(x) == x` -- `x.append(T::empty()) == x` - -Implementations: -```rust -impl Append for [T] -impl Append for Quoted -``` diff --git a/docs/versioned_docs/version-v0.34.0/noir/standard_library/zeroed.md b/docs/versioned_docs/version-v0.34.0/noir/standard_library/zeroed.md deleted file mode 100644 index f450fecdd36..00000000000 --- a/docs/versioned_docs/version-v0.34.0/noir/standard_library/zeroed.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -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 -- Slice -- String -- Tuple -- Function - -Using it on other types could result in unexpected behavior. diff --git a/docs/versioned_docs/version-v0.34.0/reference/NoirJS/backend_barretenberg/.nojekyll b/docs/versioned_docs/version-v0.34.0/reference/NoirJS/backend_barretenberg/.nojekyll deleted file mode 100644 index e2ac6616add..00000000000 --- a/docs/versioned_docs/version-v0.34.0/reference/NoirJS/backend_barretenberg/.nojekyll +++ /dev/null @@ -1 +0,0 @@ -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.34.0/reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend.md b/docs/versioned_docs/version-v0.34.0/reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend.md deleted file mode 100644 index 42f065f4a4e..00000000000 --- a/docs/versioned_docs/version-v0.34.0/reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend.md +++ /dev/null @@ -1,141 +0,0 @@ -# BarretenbergBackend - -## Implements - -- [`Backend`](../index.md#backend) -- [`Backend`](../index.md#backend) - -## Constructors - -### new BarretenbergBackend(acirCircuit, options) - -```ts -new BarretenbergBackend(acirCircuit, options): BarretenbergBackend -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `acirCircuit` | `CompiledCircuit` | -| `options` | [`BackendOptions`](../type-aliases/BackendOptions.md) | - -#### Returns - -[`BarretenbergBackend`](BarretenbergBackend.md) - -## Properties - -| Property | Type | Description | -| :------ | :------ | :------ | -| `acirComposer` | `any` | - | -| `acirUncompressedBytecode` | `Uint8Array` | - | -| `api` | `Barretenberg` | - | -| `options` | [`BackendOptions`](../type-aliases/BackendOptions.md) | - | - -## Methods - -### destroy() - -```ts -destroy(): Promise -``` - -#### Returns - -`Promise`\<`void`\> - -*** - -### generateProof() - -```ts -generateProof(compressedWitness): Promise -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `compressedWitness` | `Uint8Array` | - -#### Returns - -`Promise`\<`ProofData`\> - -#### Description - -Generates a proof - -*** - -### generateRecursiveProofArtifacts() - -```ts -generateRecursiveProofArtifacts(proofData, numOfPublicInputs): Promise -``` - -Generates artifacts that will be passed to a circuit that will verify this proof. - -Instead of passing the proof and verification key as a byte array, we pass them -as fields which makes it cheaper to verify in a circuit. - -The proof that is passed here will have been created using a circuit -that has the #[recursive] attribute on its `main` method. - -The number of public inputs denotes how many public inputs are in the inner proof. - -#### Parameters - -| Parameter | Type | Default value | -| :------ | :------ | :------ | -| `proofData` | `ProofData` | `undefined` | -| `numOfPublicInputs` | `number` | `0` | - -#### Returns - -`Promise`\<`object`\> - -#### Example - -```typescript -const artifacts = await backend.generateRecursiveProofArtifacts(proof, numOfPublicInputs); -``` - -*** - -### getVerificationKey() - -```ts -getVerificationKey(): Promise -``` - -#### Returns - -`Promise`\<`Uint8Array`\> - -*** - -### verifyProof() - -```ts -verifyProof(proofData): Promise -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `proofData` | `ProofData` | - -#### Returns - -`Promise`\<`boolean`\> - -#### Description - -Verifies 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.34.0/reference/NoirJS/backend_barretenberg/classes/BarretenbergVerifier.md b/docs/versioned_docs/version-v0.34.0/reference/NoirJS/backend_barretenberg/classes/BarretenbergVerifier.md deleted file mode 100644 index 500276ea748..00000000000 --- a/docs/versioned_docs/version-v0.34.0/reference/NoirJS/backend_barretenberg/classes/BarretenbergVerifier.md +++ /dev/null @@ -1,58 +0,0 @@ -# BarretenbergVerifier - -## Constructors - -### new BarretenbergVerifier(options) - -```ts -new BarretenbergVerifier(options): BarretenbergVerifier -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `options` | [`BackendOptions`](../type-aliases/BackendOptions.md) | - -#### Returns - -[`BarretenbergVerifier`](BarretenbergVerifier.md) - -## Methods - -### destroy() - -```ts -destroy(): Promise -``` - -#### Returns - -`Promise`\<`void`\> - -*** - -### verifyProof() - -```ts -verifyProof(proofData, verificationKey): Promise -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `proofData` | `ProofData` | -| `verificationKey` | `Uint8Array` | - -#### Returns - -`Promise`\<`boolean`\> - -#### Description - -Verifies 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.34.0/reference/NoirJS/backend_barretenberg/classes/UltraHonkBackend.md b/docs/versioned_docs/version-v0.34.0/reference/NoirJS/backend_barretenberg/classes/UltraHonkBackend.md deleted file mode 100644 index be1cd9ad465..00000000000 --- a/docs/versioned_docs/version-v0.34.0/reference/NoirJS/backend_barretenberg/classes/UltraHonkBackend.md +++ /dev/null @@ -1,116 +0,0 @@ -# UltraHonkBackend - -## Implements - -- [`Backend`](../index.md#backend) -- [`Backend`](../index.md#backend) - -## Constructors - -### new UltraHonkBackend(acirCircuit, options) - -```ts -new UltraHonkBackend(acirCircuit, options): UltraHonkBackend -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `acirCircuit` | `CompiledCircuit` | -| `options` | [`BackendOptions`](../type-aliases/BackendOptions.md) | - -#### Returns - -[`UltraHonkBackend`](UltraHonkBackend.md) - -## Properties - -| Property | Type | Description | -| :------ | :------ | :------ | -| `acirUncompressedBytecode` | `Uint8Array` | - | -| `api` | `Barretenberg` | - | -| `options` | [`BackendOptions`](../type-aliases/BackendOptions.md) | - | - -## Methods - -### destroy() - -```ts -destroy(): Promise -``` - -#### Returns - -`Promise`\<`void`\> - -*** - -### generateProof() - -```ts -generateProof(decompressedWitness): Promise -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `decompressedWitness` | `Uint8Array` | - -#### Returns - -`Promise`\<`ProofData`\> - -*** - -### generateRecursiveProofArtifacts() - -```ts -generateRecursiveProofArtifacts(_proofData, _numOfPublicInputs): Promise -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `_proofData` | `ProofData` | -| `_numOfPublicInputs` | `number` | - -#### Returns - -`Promise`\<`object`\> - -*** - -### getVerificationKey() - -```ts -getVerificationKey(): Promise -``` - -#### Returns - -`Promise`\<`Uint8Array`\> - -*** - -### verifyProof() - -```ts -verifyProof(proofData): Promise -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `proofData` | `ProofData` | - -#### Returns - -`Promise`\<`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.34.0/reference/NoirJS/backend_barretenberg/classes/UltraHonkVerifier.md b/docs/versioned_docs/version-v0.34.0/reference/NoirJS/backend_barretenberg/classes/UltraHonkVerifier.md deleted file mode 100644 index aee9460153f..00000000000 --- a/docs/versioned_docs/version-v0.34.0/reference/NoirJS/backend_barretenberg/classes/UltraHonkVerifier.md +++ /dev/null @@ -1,58 +0,0 @@ -# UltraHonkVerifier - -## Constructors - -### new UltraHonkVerifier(options) - -```ts -new UltraHonkVerifier(options): UltraHonkVerifier -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `options` | [`BackendOptions`](../type-aliases/BackendOptions.md) | - -#### Returns - -[`UltraHonkVerifier`](UltraHonkVerifier.md) - -## Methods - -### destroy() - -```ts -destroy(): Promise -``` - -#### Returns - -`Promise`\<`void`\> - -*** - -### verifyProof() - -```ts -verifyProof(proofData, verificationKey): Promise -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `proofData` | `ProofData` | -| `verificationKey` | `Uint8Array` | - -#### Returns - -`Promise`\<`boolean`\> - -#### Description - -Verifies 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.34.0/reference/NoirJS/backend_barretenberg/index.md b/docs/versioned_docs/version-v0.34.0/reference/NoirJS/backend_barretenberg/index.md deleted file mode 100644 index 4699e16dee6..00000000000 --- a/docs/versioned_docs/version-v0.34.0/reference/NoirJS/backend_barretenberg/index.md +++ /dev/null @@ -1,42 +0,0 @@ -# backend_barretenberg - -## Exports - -### Classes - -| Class | Description | -| :------ | :------ | -| [BarretenbergBackend](classes/BarretenbergBackend.md) | - | -| [BarretenbergVerifier](classes/BarretenbergVerifier.md) | - | -| [UltraHonkBackend](classes/UltraHonkBackend.md) | - | -| [UltraHonkVerifier](classes/UltraHonkVerifier.md) | - | - -### Type Aliases - -| Type alias | Description | -| :------ | :------ | -| [BackendOptions](type-aliases/BackendOptions.md) | - | - -## References - -### CompiledCircuit - -Renames and re-exports [Backend](index.md#backend) - -*** - -### ProofData - -Renames and re-exports [Backend](index.md#backend) - -## Variables - -### Backend - -```ts -Backend: any; -``` - -*** - -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.34.0/reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions.md b/docs/versioned_docs/version-v0.34.0/reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions.md deleted file mode 100644 index b49a479f4f4..00000000000 --- a/docs/versioned_docs/version-v0.34.0/reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions.md +++ /dev/null @@ -1,21 +0,0 @@ -# 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 | -| :------ | :------ | :------ | -| `memory` | `object` | - | -| `memory.maximum` | `number` | - | -| `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.34.0/reference/NoirJS/backend_barretenberg/typedoc-sidebar.cjs b/docs/versioned_docs/version-v0.34.0/reference/NoirJS/backend_barretenberg/typedoc-sidebar.cjs deleted file mode 100644 index 8ecf05c0163..00000000000 --- a/docs/versioned_docs/version-v0.34.0/reference/NoirJS/backend_barretenberg/typedoc-sidebar.cjs +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-check -/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ -const typedocSidebar = { items: [{"type":"category","label":"Classes","items":[{"type":"doc","id":"reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend","label":"BarretenbergBackend"},{"type":"doc","id":"reference/NoirJS/backend_barretenberg/classes/BarretenbergVerifier","label":"BarretenbergVerifier"},{"type":"doc","id":"reference/NoirJS/backend_barretenberg/classes/UltraHonkBackend","label":"UltraHonkBackend"},{"type":"doc","id":"reference/NoirJS/backend_barretenberg/classes/UltraHonkVerifier","label":"UltraHonkVerifier"}]},{"type":"category","label":"Type Aliases","items":[{"type":"doc","id":"reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions","label":"BackendOptions"}]}]}; -module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.34.0/reference/NoirJS/noir_js/.nojekyll b/docs/versioned_docs/version-v0.34.0/reference/NoirJS/noir_js/.nojekyll deleted file mode 100644 index e2ac6616add..00000000000 --- a/docs/versioned_docs/version-v0.34.0/reference/NoirJS/noir_js/.nojekyll +++ /dev/null @@ -1 +0,0 @@ -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.34.0/reference/NoirJS/noir_js/classes/Noir.md b/docs/versioned_docs/version-v0.34.0/reference/NoirJS/noir_js/classes/Noir.md deleted file mode 100644 index ead255bc504..00000000000 --- a/docs/versioned_docs/version-v0.34.0/reference/NoirJS/noir_js/classes/Noir.md +++ /dev/null @@ -1,52 +0,0 @@ -# Noir - -## Constructors - -### new Noir(circuit) - -```ts -new Noir(circuit): Noir -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `circuit` | `CompiledCircuit` | - -#### Returns - -[`Noir`](Noir.md) - -## Methods - -### 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) -``` - -*** - -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.34.0/reference/NoirJS/noir_js/functions/and.md b/docs/versioned_docs/version-v0.34.0/reference/NoirJS/noir_js/functions/and.md deleted file mode 100644 index c783283e396..00000000000 --- a/docs/versioned_docs/version-v0.34.0/reference/NoirJS/noir_js/functions/and.md +++ /dev/null @@ -1,22 +0,0 @@ -# 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.34.0/reference/NoirJS/noir_js/functions/blake2s256.md b/docs/versioned_docs/version-v0.34.0/reference/NoirJS/noir_js/functions/blake2s256.md deleted file mode 100644 index 7882d0da8d5..00000000000 --- a/docs/versioned_docs/version-v0.34.0/reference/NoirJS/noir_js/functions/blake2s256.md +++ /dev/null @@ -1,21 +0,0 @@ -# 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.34.0/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md b/docs/versioned_docs/version-v0.34.0/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md deleted file mode 100644 index 5e3cd53e9d3..00000000000 --- a/docs/versioned_docs/version-v0.34.0/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md +++ /dev/null @@ -1,28 +0,0 @@ -# ecdsa\_secp256k1\_verify() - -```ts -ecdsa_secp256k1_verify( - hashed_msg, - public_key_x_bytes, - public_key_y_bytes, - signature): boolean -``` - -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.34.0/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md b/docs/versioned_docs/version-v0.34.0/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md deleted file mode 100644 index 0b20ff68957..00000000000 --- a/docs/versioned_docs/version-v0.34.0/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md +++ /dev/null @@ -1,28 +0,0 @@ -# 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.34.0/reference/NoirJS/noir_js/functions/keccak256.md b/docs/versioned_docs/version-v0.34.0/reference/NoirJS/noir_js/functions/keccak256.md deleted file mode 100644 index d10f155ce86..00000000000 --- a/docs/versioned_docs/version-v0.34.0/reference/NoirJS/noir_js/functions/keccak256.md +++ /dev/null @@ -1,21 +0,0 @@ -# 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.34.0/reference/NoirJS/noir_js/functions/sha256.md b/docs/versioned_docs/version-v0.34.0/reference/NoirJS/noir_js/functions/sha256.md deleted file mode 100644 index 6ba4ecac022..00000000000 --- a/docs/versioned_docs/version-v0.34.0/reference/NoirJS/noir_js/functions/sha256.md +++ /dev/null @@ -1,21 +0,0 @@ -# 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.34.0/reference/NoirJS/noir_js/functions/xor.md b/docs/versioned_docs/version-v0.34.0/reference/NoirJS/noir_js/functions/xor.md deleted file mode 100644 index 8d762b895d3..00000000000 --- a/docs/versioned_docs/version-v0.34.0/reference/NoirJS/noir_js/functions/xor.md +++ /dev/null @@ -1,22 +0,0 @@ -# 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.34.0/reference/NoirJS/noir_js/index.md b/docs/versioned_docs/version-v0.34.0/reference/NoirJS/noir_js/index.md deleted file mode 100644 index 166508f7124..00000000000 --- a/docs/versioned_docs/version-v0.34.0/reference/NoirJS/noir_js/index.md +++ /dev/null @@ -1,49 +0,0 @@ -# noir_js - -## Exports - -### Classes - -| Class | Description | -| :------ | :------ | -| [Noir](classes/Noir.md) | - | - -### Type Aliases - -| Type alias | Description | -| :------ | :------ | -| [ErrorWithPayload](type-aliases/ErrorWithPayload.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) | - | -| [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) | Verifies a ECDSA signature over the secp256k1 curve. | -| [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` | - -## References - -### CompiledCircuit - -Renames and re-exports [InputMap](index.md#inputmap) - -## Variables - -### InputMap - -```ts -InputMap: any; -``` - -*** - -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.34.0/reference/NoirJS/noir_js/type-aliases/ErrorWithPayload.md b/docs/versioned_docs/version-v0.34.0/reference/NoirJS/noir_js/type-aliases/ErrorWithPayload.md deleted file mode 100644 index e8c2f4aef3d..00000000000 --- a/docs/versioned_docs/version-v0.34.0/reference/NoirJS/noir_js/type-aliases/ErrorWithPayload.md +++ /dev/null @@ -1,15 +0,0 @@ -# ErrorWithPayload - -```ts -type ErrorWithPayload: ExecutionError & object; -``` - -## Type declaration - -| Member | Type | Description | -| :------ | :------ | :------ | -| `decodedAssertionPayload` | `any` | - | - -*** - -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.34.0/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md b/docs/versioned_docs/version-v0.34.0/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md deleted file mode 100644 index 812b8b16481..00000000000 --- a/docs/versioned_docs/version-v0.34.0/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md +++ /dev/null @@ -1,24 +0,0 @@ -# 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.34.0/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md b/docs/versioned_docs/version-v0.34.0/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md deleted file mode 100644 index dd95809186a..00000000000 --- a/docs/versioned_docs/version-v0.34.0/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md +++ /dev/null @@ -1,9 +0,0 @@ -# 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.34.0/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md b/docs/versioned_docs/version-v0.34.0/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md deleted file mode 100644 index b71fb78a946..00000000000 --- a/docs/versioned_docs/version-v0.34.0/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md +++ /dev/null @@ -1,9 +0,0 @@ -# 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.34.0/reference/NoirJS/noir_js/type-aliases/WitnessMap.md b/docs/versioned_docs/version-v0.34.0/reference/NoirJS/noir_js/type-aliases/WitnessMap.md deleted file mode 100644 index 258c46f9d0c..00000000000 --- a/docs/versioned_docs/version-v0.34.0/reference/NoirJS/noir_js/type-aliases/WitnessMap.md +++ /dev/null @@ -1,9 +0,0 @@ -# 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.34.0/reference/NoirJS/noir_js/typedoc-sidebar.cjs b/docs/versioned_docs/version-v0.34.0/reference/NoirJS/noir_js/typedoc-sidebar.cjs deleted file mode 100644 index b3156097df6..00000000000 --- a/docs/versioned_docs/version-v0.34.0/reference/NoirJS/noir_js/typedoc-sidebar.cjs +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-check -/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ -const typedocSidebar = { items: [{"type":"category","label":"Classes","items":[{"type":"doc","id":"reference/NoirJS/noir_js/classes/Noir","label":"Noir"}]},{"type":"category","label":"Type Aliases","items":[{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ErrorWithPayload","label":"ErrorWithPayload"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ForeignCallHandler","label":"ForeignCallHandler"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ForeignCallInput","label":"ForeignCallInput"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ForeignCallOutput","label":"ForeignCallOutput"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/WitnessMap","label":"WitnessMap"}]},{"type":"category","label":"Functions","items":[{"type":"doc","id":"reference/NoirJS/noir_js/functions/and","label":"and"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/blake2s256","label":"blake2s256"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify","label":"ecdsa_secp256k1_verify"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify","label":"ecdsa_secp256r1_verify"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/keccak256","label":"keccak256"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/sha256","label":"sha256"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/xor","label":"xor"}]}]}; -module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.34.0/reference/NoirJS/noir_wasm/.nojekyll b/docs/versioned_docs/version-v0.34.0/reference/NoirJS/noir_wasm/.nojekyll deleted file mode 100644 index e2ac6616add..00000000000 --- a/docs/versioned_docs/version-v0.34.0/reference/NoirJS/noir_wasm/.nojekyll +++ /dev/null @@ -1 +0,0 @@ -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.34.0/reference/NoirJS/noir_wasm/functions/compile.md b/docs/versioned_docs/version-v0.34.0/reference/NoirJS/noir_wasm/functions/compile.md deleted file mode 100644 index 6faf763b37f..00000000000 --- a/docs/versioned_docs/version-v0.34.0/reference/NoirJS/noir_wasm/functions/compile.md +++ /dev/null @@ -1,51 +0,0 @@ -# compile() - -```ts -compile( - fileManager, - projectPath?, - logFn?, -debugLogFn?): Promise -``` - -Compiles a Noir project - -## Parameters - -| Parameter | Type | Description | -| :------ | :------ | :------ | -| `fileManager` | `FileManager` | The file manager to use | -| `projectPath`? | `string` | The path to the project inside the file manager. Defaults to the root of the file manager | -| `logFn`? | `LogFn` | A logging function. If not provided, console.log will be used | -| `debugLogFn`? | `LogFn` | A debug logging function. If not provided, logFn will be used | - -## Returns - -`Promise`\<[`ProgramCompilationArtifacts`](../index.md#programcompilationartifacts)\> - -## Example - -```typescript -// Node.js - -import { compile_program, createFileManager } from '@noir-lang/noir_wasm'; - -const fm = createFileManager(myProjectPath); -const myCompiledCode = await compile_program(fm); -``` - -```typescript -// Browser - -import { compile_program, createFileManager } from '@noir-lang/noir_wasm'; - -const fm = createFileManager('/'); -for (const path of files) { - await fm.writeFile(path, await getFileAsStream(path)); -} -const myCompiledCode = await compile_program(fm); -``` - -*** - -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.34.0/reference/NoirJS/noir_wasm/functions/compile_contract.md b/docs/versioned_docs/version-v0.34.0/reference/NoirJS/noir_wasm/functions/compile_contract.md deleted file mode 100644 index 7d0b39a43ef..00000000000 --- a/docs/versioned_docs/version-v0.34.0/reference/NoirJS/noir_wasm/functions/compile_contract.md +++ /dev/null @@ -1,51 +0,0 @@ -# compile\_contract() - -```ts -compile_contract( - fileManager, - projectPath?, - logFn?, -debugLogFn?): Promise -``` - -Compiles a Noir project - -## Parameters - -| Parameter | Type | Description | -| :------ | :------ | :------ | -| `fileManager` | `FileManager` | The file manager to use | -| `projectPath`? | `string` | The path to the project inside the file manager. Defaults to the root of the file manager | -| `logFn`? | `LogFn` | A logging function. If not provided, console.log will be used | -| `debugLogFn`? | `LogFn` | A debug logging function. If not provided, logFn will be used | - -## Returns - -`Promise`\<[`ContractCompilationArtifacts`](../index.md#contractcompilationartifacts)\> - -## Example - -```typescript -// Node.js - -import { compile_contract, createFileManager } from '@noir-lang/noir_wasm'; - -const fm = createFileManager(myProjectPath); -const myCompiledCode = await compile_contract(fm); -``` - -```typescript -// Browser - -import { compile_contract, createFileManager } from '@noir-lang/noir_wasm'; - -const fm = createFileManager('/'); -for (const path of files) { - await fm.writeFile(path, await getFileAsStream(path)); -} -const myCompiledCode = await compile_contract(fm); -``` - -*** - -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.34.0/reference/NoirJS/noir_wasm/functions/createFileManager.md b/docs/versioned_docs/version-v0.34.0/reference/NoirJS/noir_wasm/functions/createFileManager.md deleted file mode 100644 index 7e65c1d69c7..00000000000 --- a/docs/versioned_docs/version-v0.34.0/reference/NoirJS/noir_wasm/functions/createFileManager.md +++ /dev/null @@ -1,21 +0,0 @@ -# createFileManager() - -```ts -createFileManager(dataDir): FileManager -``` - -Creates a new FileManager instance based on fs in node and memfs in the browser (via webpack alias) - -## Parameters - -| Parameter | Type | Description | -| :------ | :------ | :------ | -| `dataDir` | `string` | root of the file system | - -## Returns - -`FileManager` - -*** - -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.34.0/reference/NoirJS/noir_wasm/functions/inflateDebugSymbols.md b/docs/versioned_docs/version-v0.34.0/reference/NoirJS/noir_wasm/functions/inflateDebugSymbols.md deleted file mode 100644 index fcea9275341..00000000000 --- a/docs/versioned_docs/version-v0.34.0/reference/NoirJS/noir_wasm/functions/inflateDebugSymbols.md +++ /dev/null @@ -1,21 +0,0 @@ -# inflateDebugSymbols() - -```ts -inflateDebugSymbols(debugSymbols): any -``` - -Decompresses and decodes the debug symbols - -## Parameters - -| Parameter | Type | Description | -| :------ | :------ | :------ | -| `debugSymbols` | `string` | The base64 encoded debug symbols | - -## Returns - -`any` - -*** - -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.34.0/reference/NoirJS/noir_wasm/index.md b/docs/versioned_docs/version-v0.34.0/reference/NoirJS/noir_wasm/index.md deleted file mode 100644 index b6e0f9d1bc0..00000000000 --- a/docs/versioned_docs/version-v0.34.0/reference/NoirJS/noir_wasm/index.md +++ /dev/null @@ -1,49 +0,0 @@ -# noir_wasm - -## Exports - -### Functions - -| Function | Description | -| :------ | :------ | -| [compile](functions/compile.md) | Compiles a Noir project | -| [compile\_contract](functions/compile_contract.md) | Compiles a Noir project | -| [createFileManager](functions/createFileManager.md) | Creates a new FileManager instance based on fs in node and memfs in the browser (via webpack alias) | -| [inflateDebugSymbols](functions/inflateDebugSymbols.md) | Decompresses and decodes the debug symbols | - -## References - -### compile\_program - -Renames and re-exports [compile](functions/compile.md) - -## Interfaces - -### ContractCompilationArtifacts - -The compilation artifacts of a given contract. - -#### Properties - -| Property | Type | Description | -| :------ | :------ | :------ | -| `contract` | `ContractArtifact` | The compiled contract. | -| `warnings` | `unknown`[] | Compilation warnings. | - -*** - -### ProgramCompilationArtifacts - -The compilation artifacts of a given program. - -#### Properties - -| Property | Type | Description | -| :------ | :------ | :------ | -| `name` | `string` | not part of the compilation output, injected later | -| `program` | `ProgramArtifact` | The compiled contract. | -| `warnings` | `unknown`[] | Compilation warnings. | - -*** - -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.34.0/reference/NoirJS/noir_wasm/typedoc-sidebar.cjs b/docs/versioned_docs/version-v0.34.0/reference/NoirJS/noir_wasm/typedoc-sidebar.cjs deleted file mode 100644 index e0870710349..00000000000 --- a/docs/versioned_docs/version-v0.34.0/reference/NoirJS/noir_wasm/typedoc-sidebar.cjs +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-check -/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ -const typedocSidebar = { items: [{"type":"doc","id":"reference/NoirJS/noir_wasm/index","label":"API"},{"type":"category","label":"Functions","items":[{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/compile","label":"compile"},{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/compile_contract","label":"compile_contract"},{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/createFileManager","label":"createFileManager"},{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/inflateDebugSymbols","label":"inflateDebugSymbols"}]}]}; -module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.34.0/reference/_category_.json b/docs/versioned_docs/version-v0.34.0/reference/_category_.json deleted file mode 100644 index 5b6a20a609a..00000000000 --- a/docs/versioned_docs/version-v0.34.0/reference/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "position": 4, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.34.0/reference/debugger/_category_.json b/docs/versioned_docs/version-v0.34.0/reference/debugger/_category_.json deleted file mode 100644 index 27869205ad3..00000000000 --- a/docs/versioned_docs/version-v0.34.0/reference/debugger/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "Debugger", - "position": 1, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.34.0/reference/debugger/debugger_known_limitations.md b/docs/versioned_docs/version-v0.34.0/reference/debugger/debugger_known_limitations.md deleted file mode 100644 index 936d416ac4b..00000000000 --- a/docs/versioned_docs/version-v0.34.0/reference/debugger/debugger_known_limitations.md +++ /dev/null @@ -1,59 +0,0 @@ ---- -title: Known limitations -description: - An overview of known limitations of the current version of the Noir debugger -keywords: - [ - Nargo, - Noir Debugger, - VS Code, - ] -sidebar_position: 2 ---- - -# Debugger Known Limitations - -There are currently some limits to what the debugger can observe. - -## Mutable references - -The debugger is currently blind to any state mutated via a mutable reference. For example, in: - -``` -let mut x = 1; -let y = &mut x; -*y = 2; -``` - -The update on `x` will not be observed by the debugger. That means, when running `vars` from the debugger REPL, or inspecting the _local variables_ pane in the VS Code debugger, `x` will appear with value 1 despite having executed `*y = 2;`. - -## Variables of type function or mutable references are opaque - -When inspecting variables, any variable of type `Function` or `MutableReference` will render its value as `<>` or `<>`. - -## Debugger instrumentation affects resulting ACIR - -In order to make the state of local variables observable, the debugger compiles Noir circuits interleaving foreign calls that track any mutations to them. While this works (except in the cases described above) and doesn't introduce any behavior changes, it does as a side effect produce bigger bytecode. In particular, when running the command `opcodes` on the REPL debugger, you will notice Unconstrained VM blocks that look like this: - -``` -... -5 BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [], q_c: 2 }), Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(2))], q_c: 0 })] - | outputs=[] - 5.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } - 5.1 | Mov { destination: RegisterIndex(3), source: RegisterIndex(1) } - 5.2 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } - 5.3 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } - 5.4 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } - 5.5 | Mov { destination: RegisterIndex(3), source: RegisterIndex(3) } - 5.6 | Call { location: 8 } - 5.7 | Stop - 5.8 | ForeignCall { function: "__debug_var_assign", destinations: [], inputs: [RegisterIndex(RegisterIndex(2)), RegisterIndex(RegisterIndex(3))] } -... -``` - -If you are interested in debugging/inspecting compiled ACIR without these synthetic changes, you can invoke the REPL debugger with the `--skip-instrumentation` flag or launch the VS Code debugger with the `skipConfiguration` property set to true in its launch configuration. You can find more details about those in the [Debugger REPL reference](debugger_repl.md) and the [VS Code Debugger reference](debugger_vscode.md). - -:::note -Skipping debugger instrumentation means you won't be able to inspect values of local variables. -::: - diff --git a/docs/versioned_docs/version-v0.34.0/reference/debugger/debugger_repl.md b/docs/versioned_docs/version-v0.34.0/reference/debugger/debugger_repl.md deleted file mode 100644 index 46e2011304e..00000000000 --- a/docs/versioned_docs/version-v0.34.0/reference/debugger/debugger_repl.md +++ /dev/null @@ -1,360 +0,0 @@ ---- -title: REPL Debugger -description: - Noir Debugger REPL options and commands. -keywords: - [ - Nargo, - Noir CLI, - Noir Debugger, - REPL, - ] -sidebar_position: 1 ---- - -## Running the REPL debugger - -`nargo debug [OPTIONS] [WITNESS_NAME]` - -Runs the Noir REPL debugger. If a `WITNESS_NAME` is provided the debugger writes the resulting execution witness to a `WITNESS_NAME` 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 debug | -| `--print-acir` | Display the ACIR for compiled circuit | -| `--deny-warnings` | Treat all warnings as errors | -| `--silence-warnings` | Suppress warnings | -| `-h, --help` | Print help | - -None of these options are required. - -:::note -Since the debugger starts by compiling the target package, all Noir compiler options are also available. Check out the [compiler reference](../nargo_commands.md#nargo-compile) to learn more about the compiler options. -::: - -## REPL commands - -Once the debugger is running, it accepts the following commands. - -#### `help` (h) - -Displays the menu of available commands. - -``` -> help -Available commands: - - opcodes display ACIR opcodes - into step into to the next opcode - next step until a new source location is reached - out step until a new source location is reached - and the current stack frame is finished - break LOCATION:OpcodeLocation add a breakpoint at an opcode location - over step until a new source location is reached - without diving into function calls - restart restart the debugging session - delete LOCATION:OpcodeLocation delete breakpoint at an opcode location - witness show witness map - witness index:u32 display a single witness from the witness map - witness index:u32 value:String update a witness with the given value - memset index:usize value:String update a memory cell with the given - value - continue continue execution until the end of the - program - vars show variable values available at this point - in execution - stacktrace display the current stack trace - memory show memory (valid when executing unconstrained code) value - step step to the next ACIR opcode - -Other commands: - - help Show this help message - quit Quit repl - -``` - -### Stepping through programs - -#### `next` (n) - -Step until the next Noir source code location. While other commands, such as [`into`](#into-i) and [`step`](#step-s), allow for finer grained control of the program's execution at the opcode level, `next` is source code centric. For example: - -``` -3 ... -4 fn main(x: u32) { -5 assert(entry_point(x) == 2); -6 swap_entry_point(x, x + 1); -7 -> assert(deep_entry_point(x) == 4); -8 multiple_values_entry_point(x); -9 } -``` - - -Using `next` here would cause the debugger to jump to the definition of `deep_entry_point` (if available). - -If you want to step over `deep_entry_point` and go straight to line 8, use [the `over` command](#over) instead. - -#### `over` - -Step until the next source code location, without diving into function calls. For example: - -``` -3 ... -4 fn main(x: u32) { -5 assert(entry_point(x) == 2); -6 swap_entry_point(x, x + 1); -7 -> assert(deep_entry_point(x) == 4); -8 multiple_values_entry_point(x); -9 } -``` - - -Using `over` here would cause the debugger to execute until line 8 (`multiple_values_entry_point(x);`). - -If you want to step into `deep_entry_point` instead, use [the `next` command](#next-n). - -#### `out` - -Step until the end of the current function call. For example: - -``` - 3 ... - 4 fn main(x: u32) { - 5 assert(entry_point(x) == 2); - 6 swap_entry_point(x, x + 1); - 7 -> assert(deep_entry_point(x) == 4); - 8 multiple_values_entry_point(x); - 9 } - 10 - 11 unconstrained fn returns_multiple_values(x: u32) -> (u32, u32, u32, u32) { - 12 ... - ... - 55 - 56 unconstrained fn deep_entry_point(x: u32) -> u32 { - 57 -> level_1(x + 1) - 58 } - -``` - -Running `out` here will resume execution until line 8. - -#### `step` (s) - -Skips to the next ACIR code. A compiled Noir program is a sequence of ACIR opcodes. However, an unconstrained VM opcode denotes the start of an unconstrained code block, to be executed by the unconstrained VM. For example (redacted for brevity): - -``` -0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] -1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] - 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } - 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } - 1.2 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } - 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } - 1.4 | Call { location: 7 } - ... - 1.43 | Return -2 EXPR [ (1, _1) -2 ] -``` - -The `->` here shows the debugger paused at an ACIR opcode: `BRILLIG`, at index 1, which denotes an unconstrained code block is about to start. - -Using the `step` command at this point would result in the debugger stopping at ACIR opcode 2, `EXPR`, skipping unconstrained computation steps. - -Use [the `into` command](#into-i) instead if you want to follow unconstrained computation step by step. - -#### `into` (i) - -Steps into the next opcode. A compiled Noir program is a sequence of ACIR opcodes. However, a BRILLIG opcode denotes the start of an unconstrained code block, to be executed by the unconstrained VM. For example (redacted for brevity): - -``` -0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] -1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] - 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } - 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } - 1.2 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } - 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } - 1.4 | Call { location: 7 } - ... - 1.43 | Return -2 EXPR [ (1, _1) -2 ] -``` - -The `->` here shows the debugger paused at an ACIR opcode: `BRILLIG`, at index 1, which denotes an unconstrained code block is about to start. - -Using the `into` command at this point would result in the debugger stopping at opcode 1.0, `Mov ...`, allowing the debugger user to follow unconstrained computation step by step. - -Use [the `step` command](#step-s) instead if you want to skip to the next ACIR code directly. - -#### `continue` (c) - -Continues execution until the next breakpoint, or the end of the program. - -#### `restart` (res) - -Interrupts execution, and restarts a new debugging session from scratch. - -#### `opcodes` (o) - -Display the program's ACIR opcode sequence. For example: - -``` -0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] -1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] - 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } - 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } - 1.2 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } - 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } - 1.4 | Call { location: 7 } - ... - 1.43 | Return -2 EXPR [ (1, _1) -2 ] -``` - -### Breakpoints - -#### `break [Opcode]` (or shorthand `b [Opcode]`) - -Sets a breakpoint on the specified opcode index. To get a list of the program opcode numbers, see [the `opcode` command](#opcodes-o). For example: - -``` -0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] -1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] - 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } - 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } - 1.2 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } - 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } - 1.4 | Call { location: 7 } - ... - 1.43 | Return -2 EXPR [ (1, _1) -2 ] -``` - -In this example, issuing a `break 1.2` command adds break on opcode 1.2, as denoted by the `*` character: - -``` -0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] -1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] - 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } - 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } - 1.2 | * Const { destination: RegisterIndex(1), value: Value { inner: 0 } } - 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } - 1.4 | Call { location: 7 } - ... - 1.43 | Return -2 EXPR [ (1, _1) -2 ] -``` - -Running [the `continue` command](#continue-c) at this point would cause the debugger to execute the program until opcode 1.2. - -#### `delete [Opcode]` (or shorthand `d [Opcode]`) - -Deletes a breakpoint at an opcode location. Usage is analogous to [the `break` command](#). - -### Variable inspection - -#### vars - -Show variable values available at this point in execution. - -:::note -The ability to inspect variable values from the debugger depends on compilation to be run in a special debug instrumentation mode. This instrumentation weaves variable tracing code with the original source code. - -So variable value inspection comes at the expense of making the resulting ACIR bytecode bigger and harder to understand and optimize. - -If you find this compromise unacceptable, you can run the debugger with the flag `--skip-debug-instrumentation`. This will compile your circuit without any additional debug information, so the resulting ACIR bytecode will be identical to the one produced by standard Noir compilation. However, if you opt for this, the `vars` command will not be available while debugging. -::: - - -### Stacktrace - -#### `stacktrace` - -Displays the current stack trace. - - -### Witness map - -#### `witness` (w) - -Show witness map. For example: - -``` -_0 = 0 -_1 = 2 -_2 = 1 -``` - -#### `witness [Witness Index]` - -Display a single witness from the witness map. For example: - -``` -> witness 1 -_1 = 2 -``` - -#### `witness [Witness Index] [New value]` - -Overwrite the given index with a new value. For example: - -``` -> witness 1 3 -_1 = 3 -``` - - -### Unconstrained VM memory - -#### `memory` - -Show unconstrained VM memory state. For example: - -``` -> memory -At opcode 1.13: Store { destination_pointer: RegisterIndex(0), source: RegisterIndex(3) } -... -> registers -0 = 0 -1 = 10 -2 = 0 -3 = 1 -4 = 1 -5 = 2³² -6 = 1 -> into -At opcode 1.14: Const { destination: RegisterIndex(5), value: Value { inner: 1 } } -... -> memory -0 = 1 -> -``` - -In the example above: we start with clean memory, then step through a `Store` opcode which stores the value of register 3 (1) into the memory address stored in register 0 (0). Thus now `memory` shows memory address 0 contains value 1. - -:::note -This command is only functional while the debugger is executing unconstrained code. -::: - -#### `memset [Memory address] [New value]` - -Update a memory cell with the given value. For example: - -``` -> memory -0 = 1 -> memset 0 2 -> memory -0 = 2 -> memset 1 4 -> memory -0 = 2 -1 = 4 -> -``` - -:::note -This command is only functional while the debugger is executing unconstrained code. -::: \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.34.0/reference/debugger/debugger_vscode.md b/docs/versioned_docs/version-v0.34.0/reference/debugger/debugger_vscode.md deleted file mode 100644 index c027332b3b0..00000000000 --- a/docs/versioned_docs/version-v0.34.0/reference/debugger/debugger_vscode.md +++ /dev/null @@ -1,82 +0,0 @@ ---- -title: VS Code Debugger -description: - VS Code Debugger configuration and features. -keywords: - [ - Nargo, - Noir CLI, - Noir Debugger, - VS Code, - IDE, - ] -sidebar_position: 0 ---- - -# VS Code Noir Debugger Reference - -The Noir debugger enabled by the vscode-noir extension ships with default settings such that the most common scenario should run without any additional configuration steps. - -These defaults can nevertheless be overridden by defining a launch configuration file. This page provides a reference for the properties you can override via a launch configuration file, as well as documenting the Nargo `dap` command, which is a dependency of the VS Code Noir debugger. - - -## Creating and editing launch configuration files - -To create a launch configuration file from VS Code, open the _debug pane_, and click on _create a launch.json file_. - -![Creating a launch configuration file](@site/static/img/debugger/ref1-create-launch.png) - -A `launch.json` file will be created, populated with basic defaults. - -### Noir Debugger launch.json properties - -#### projectFolder - -_String, optional._ - -Absolute path to the Nargo project to debug. By default, it is dynamically determined by looking for the nearest `Nargo.toml` file to the active file at the moment of launching the debugger. - -#### proverName - -_String, optional._ - -Name of the prover input to use. Defaults to `Prover`, which looks for a file named `Prover.toml` at the `projectFolder`. - -#### generateAcir - -_Boolean, optional._ - -If true, generate ACIR opcodes instead of unconstrained opcodes which will be closer to release binaries but less convenient for debugging. Defaults to `false`. - -#### skipInstrumentation - -_Boolean, optional._ - -Skips variables debugging instrumentation of code, making debugging less convenient but the resulting binary smaller and closer to production. Defaults to `false`. - -:::note -Skipping instrumentation causes the debugger to be unable to inspect local variables. -::: - -## `nargo dap [OPTIONS]` - -When run without any option flags, it starts the Nargo Debug Adapter Protocol server, which acts as the debugging backend for the VS Code Noir Debugger. - -All option flags are related to preflight checks. The Debug Adapter Protocol specifies how errors are to be informed from a running DAP server, but it doesn't specify mechanisms to communicate server initialization errors between the DAP server and its client IDE. - -Thus `nargo dap` ships with a _preflight check_ mode. If flag `--preflight-check` and the rest of the `--preflight-*` flags are provided, Nargo will run the same initialization routine except it will not start the DAP server. - -`vscode-noir` will then run `nargo dap` in preflight check mode first before a debugging session starts. If the preflight check ends in error, vscode-noir will present stderr and stdout output from this process through its own Output pane in VS Code. This makes it possible for users to diagnose what pieces of configuration might be wrong or missing in case of initialization errors. - -If the preflight check succeeds, `vscode-noir` proceeds to start the DAP server normally but running `nargo dap` without any additional flags. - -### Options - -| Option | Description | -| --------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | -| `--preflight-check` | If present, dap runs in preflight check mode. | -| `--preflight-project-folder ` | Absolute path to the project to debug for preflight check. | -| `--preflight-prover-name ` | Name of prover file to use for preflight check | -| `--preflight-generate-acir` | Optional. If present, compile in ACIR mode while running preflight check. | -| `--preflight-skip-instrumentation` | Optional. If present, compile without introducing debug instrumentation while running preflight check. | -| `-h, --help` | Print help. | diff --git a/docs/versioned_docs/version-v0.34.0/reference/nargo_commands.md b/docs/versioned_docs/version-v0.34.0/reference/nargo_commands.md deleted file mode 100644 index b46ad4cda98..00000000000 --- a/docs/versioned_docs/version-v0.34.0/reference/nargo_commands.md +++ /dev/null @@ -1,295 +0,0 @@ ---- -title: Nargo -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, - ] -sidebar_position: 0 ---- - -# Command-Line Help for `nargo` - -This document contains the help content for the `nargo` command-line program. - -**Command Overview:** - -* [`nargo`↴](#nargo) -* [`nargo check`↴](#nargo-check) -* [`nargo fmt`↴](#nargo-fmt) -* [`nargo compile`↴](#nargo-compile) -* [`nargo new`↴](#nargo-new) -* [`nargo init`↴](#nargo-init) -* [`nargo execute`↴](#nargo-execute) -* [`nargo debug`↴](#nargo-debug) -* [`nargo test`↴](#nargo-test) -* [`nargo info`↴](#nargo-info) -* [`nargo lsp`↴](#nargo-lsp) - -## `nargo` - -Noir's package manager - -**Usage:** `nargo ` - -###### **Subcommands:** - -* `check` — Checks the constraint system for errors -* `fmt` — Format the Noir files in a workspace -* `compile` — Compile the program and its secret execution trace into ACIR format -* `new` — Create a Noir project in a new directory -* `init` — Create a Noir project in the current directory -* `execute` — Executes a circuit to calculate its return value -* `debug` — Executes a circuit in debug mode -* `test` — Run the tests for this program -* `info` — Provides detailed information on each of a program's function (represented by a single circuit) -* `lsp` — Starts the Noir LSP server - -###### **Options:** - - - - -## `nargo check` - -Checks the constraint system for errors - -**Usage:** `nargo check [OPTIONS]` - -###### **Options:** - -* `--package ` — The name of the package to check -* `--workspace` — Check all packages in the workspace -* `--overwrite` — Force overwrite of existing files -* `--expression-width ` — Specify the backend expression width that should be targeted -* `--bounded-codegen` — Generate ACIR with the target backend expression width. The default is to generate ACIR without a bound and split expressions after code generation. Activating this flag can sometimes provide optimizations for certain programs - - Default value: `false` -* `--force` — Force a full recompilation -* `--print-acir` — Display the ACIR for compiled circuit -* `--deny-warnings` — Treat all warnings as errors -* `--silence-warnings` — Suppress warnings -* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" -* `--skip-underconstrained-check` — Flag to turn off the compiler check for under constrained values. Warning: This can improve compilation speed but can also lead to correctness errors. This check should always be run on production code - - - -## `nargo fmt` - -Format the Noir files in a workspace - -**Usage:** `nargo fmt [OPTIONS]` - -###### **Options:** - -* `--check` — Run noirfmt in check mode - - - -## `nargo compile` - -Compile the program and its secret execution trace into ACIR format - -**Usage:** `nargo compile [OPTIONS]` - -###### **Options:** - -* `--package ` — The name of the package to compile -* `--workspace` — Compile all packages in the workspace -* `--expression-width ` — Specify the backend expression width that should be targeted -* `--bounded-codegen` — Generate ACIR with the target backend expression width. The default is to generate ACIR without a bound and split expressions after code generation. Activating this flag can sometimes provide optimizations for certain programs - - Default value: `false` -* `--force` — Force a full recompilation -* `--print-acir` — Display the ACIR for compiled circuit -* `--deny-warnings` — Treat all warnings as errors -* `--silence-warnings` — Suppress warnings -* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" -* `--skip-underconstrained-check` — Flag to turn off the compiler check for under constrained values. Warning: This can improve compilation speed but can also lead to correctness errors. This check should always be run on production code - - - -## `nargo new` - -Create a Noir project in a new directory - -**Usage:** `nargo new [OPTIONS] ` - -###### **Arguments:** - -* `` — The path to save the new project - -###### **Options:** - -* `--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 - - - -## `nargo init` - -Create a Noir project in the current directory - -**Usage:** `nargo init [OPTIONS]` - -###### **Options:** - -* `--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 - - - -## `nargo execute` - -Executes a circuit to calculate its return value - -**Usage:** `nargo execute [OPTIONS] [WITNESS_NAME]` - -###### **Arguments:** - -* `` — Write the execution witness to named file - -###### **Options:** - -* `-p`, `--prover-name ` — The name of the toml file which contains the inputs for the prover - - Default value: `Prover` -* `--package ` — The name of the package to execute -* `--workspace` — Execute all packages in the workspace -* `--expression-width ` — Specify the backend expression width that should be targeted -* `--bounded-codegen` — Generate ACIR with the target backend expression width. The default is to generate ACIR without a bound and split expressions after code generation. Activating this flag can sometimes provide optimizations for certain programs - - Default value: `false` -* `--force` — Force a full recompilation -* `--print-acir` — Display the ACIR for compiled circuit -* `--deny-warnings` — Treat all warnings as errors -* `--silence-warnings` — Suppress warnings -* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" -* `--skip-underconstrained-check` — Flag to turn off the compiler check for under constrained values. Warning: This can improve compilation speed but can also lead to correctness errors. This check should always be run on production code -* `--oracle-resolver ` — JSON RPC url to solve oracle calls - - - -## `nargo debug` - -Executes a circuit in debug mode - -**Usage:** `nargo debug [OPTIONS] [WITNESS_NAME]` - -###### **Arguments:** - -* `` — Write the execution witness to named file - -###### **Options:** - -* `-p`, `--prover-name ` — The name of the toml file which contains the inputs for the prover - - Default value: `Prover` -* `--package ` — The name of the package to execute -* `--expression-width ` — Specify the backend expression width that should be targeted -* `--bounded-codegen` — Generate ACIR with the target backend expression width. The default is to generate ACIR without a bound and split expressions after code generation. Activating this flag can sometimes provide optimizations for certain programs - - Default value: `false` -* `--force` — Force a full recompilation -* `--print-acir` — Display the ACIR for compiled circuit -* `--deny-warnings` — Treat all warnings as errors -* `--silence-warnings` — Suppress warnings -* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" -* `--skip-underconstrained-check` — Flag to turn off the compiler check for under constrained values. Warning: This can improve compilation speed but can also lead to correctness errors. This check should always be run on production code -* `--acir-mode` — Force ACIR output (disabling instrumentation) -* `--skip-instrumentation ` — Disable vars debug instrumentation (enabled by default) - - Possible values: `true`, `false` - - - - -## `nargo test` - -Run the tests for this program - -**Usage:** `nargo test [OPTIONS] [TEST_NAME]` - -###### **Arguments:** - -* `` — If given, only tests with names containing this string will be run - -###### **Options:** - -* `--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 -* `--expression-width ` — Specify the backend expression width that should be targeted -* `--bounded-codegen` — Generate ACIR with the target backend expression width. The default is to generate ACIR without a bound and split expressions after code generation. Activating this flag can sometimes provide optimizations for certain programs - - Default value: `false` -* `--force` — Force a full recompilation -* `--print-acir` — Display the ACIR for compiled circuit -* `--deny-warnings` — Treat all warnings as errors -* `--silence-warnings` — Suppress warnings -* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" -* `--skip-underconstrained-check` — Flag to turn off the compiler check for under constrained values. Warning: This can improve compilation speed but can also lead to correctness errors. This check should always be run on production code -* `--oracle-resolver ` — JSON RPC url to solve oracle calls - - - -## `nargo info` - -Provides detailed information on each of a program's function (represented by a single circuit) - -Current information provided per circuit: 1. The number of ACIR opcodes 2. Counts the final number gates in the circuit used by a backend - -**Usage:** `nargo info [OPTIONS]` - -###### **Options:** - -* `--package ` — The name of the package to detail -* `--workspace` — Detail all packages in the workspace -* `--expression-width ` — Specify the backend expression width that should be targeted -* `--bounded-codegen` — Generate ACIR with the target backend expression width. The default is to generate ACIR without a bound and split expressions after code generation. Activating this flag can sometimes provide optimizations for certain programs - - Default value: `false` -* `--force` — Force a full recompilation -* `--print-acir` — Display the ACIR for compiled circuit -* `--deny-warnings` — Treat all warnings as errors -* `--silence-warnings` — Suppress warnings -* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" -* `--skip-underconstrained-check` — Flag to turn off the compiler check for under constrained values. Warning: This can improve compilation speed but can also lead to correctness errors. This check should always be run on production code - - - -## `nargo lsp` - -Starts the Noir LSP server - -Starts an LSP server which allows IDEs such as VS Code to display diagnostics in Noir source. - -VS Code Noir Language Support: https://marketplace.visualstudio.com/items?itemName=noir-lang.vscode-noir - -**Usage:** `nargo lsp` - - - -
- - - This document was generated automatically by - clap-markdown. - - diff --git a/docs/versioned_docs/version-v0.34.0/reference/noir_codegen.md b/docs/versioned_docs/version-v0.34.0/reference/noir_codegen.md deleted file mode 100644 index db8f07dc22e..00000000000 --- a/docs/versioned_docs/version-v0.34.0/reference/noir_codegen.md +++ /dev/null @@ -1,114 +0,0 @@ ---- -title: Noir Codegen for TypeScript -description: Learn how to use Noir codegen to generate TypeScript bindings -keywords: [Nargo, Noir, compile, TypeScript] -sidebar_position: 3 ---- - -When using TypeScript, it is extra work to interpret Noir program outputs in a type-safe way. Third party libraries may exist for popular Noir programs, but they are either hard to find or unmaintained. - -Now you can generate TypeScript bindings for your Noir programs in two steps: -1. Exporting Noir functions using `nargo export` -2. Using the TypeScript module `noir_codegen` to generate TypeScript binding - -**Note:** you can only export functions from a Noir *library* (not binary or contract program types). - -## Installation - -### Your TypeScript project - -If you don't already have a TypeScript project you can add the module with `yarn` (or `npm`), then initialize it: - -```bash -yarn add typescript -D -npx tsc --init -``` - -### Add TypeScript module - `noir_codegen` - -The following command will add the module to your project's devDependencies: - -```bash -yarn add @noir-lang/noir_codegen -D -``` - -### Nargo library -Make sure you have Nargo, v0.25.0 or greater, installed. If you don't, follow the [installation guide](../getting_started/installation/index.md). - -If you're in a new project, make a `circuits` folder and create a new Noir library: - -```bash -mkdir circuits && cd circuits -nargo new --lib myNoirLib -``` - -## Usage - -### Export ABI of specified functions - -First go to the `.nr` files in your Noir library, and add the `#[export]` macro to each function that you want to use in TypeScript. - -```rust -#[export] -fn your_function(... -``` - -From your Noir library (where `Nargo.toml` is), run the following command: - -```bash -nargo export -``` - -You will now have an `export` directory with a .json file per exported function. - -You can also specify the directory of Noir programs using `--program-dir`, for example: - -```bash -nargo export --program-dir=./circuits/myNoirLib -``` - -### Generate TypeScript bindings from exported functions - -To use the `noir-codegen` package we added to the TypeScript project: - -```bash -yarn noir-codegen ./export/your_function.json -``` - -This creates an `exports` directory with an `index.ts` file containing all exported functions. - -**Note:** adding `--out-dir` allows you to specify an output dir for your TypeScript bindings to go. Eg: - -```bash -yarn noir-codegen ./export/*.json --out-dir ./path/to/output/dir -``` - -## Example .nr function to .ts output - -Consider a Noir library with this function: - -```rust -#[export] -fn not_equal(x: Field, y: Field) -> bool { - x != y -} -``` - -After the export and codegen steps, you should have an `index.ts` like: - -```typescript -export type Field = string; - - -export const is_equal_circuit: CompiledCircuit = -{"abi":{"parameters":[{"name":"x","type":{"kind":"field"},"visibility":"private"},{"name":"y","type":{"kind":"field"},"visibility":"private"}],"return_type":{"abi_type":{"kind":"boolean"},"visibility":"private"}},"bytecode":"H4sIAAAAAAAA/7WUMQ7DIAxFQ0Krrr2JjSGYLVcpKrn/CaqqDQN12WK+hPBgmWd/wEyHbF1SS923uhOs3pfoChI+wKXMAXzIKyNj4PB0TFTYc0w5RUjoqeAeEu1wqK0F54RGkWvW44LPzExnlkbMEs4JNZmN8PxS42uHv82T8a3Jeyn2Ks+VLPcO558HmyLMCDOXAXXtpPt4R/Rt9T36ss6dS9HGPx/eG17nGegKBQAA"}; - -export async function is_equal(x: Field, y: Field, foreignCallHandler?: ForeignCallHandler): Promise { - const program = new Noir(is_equal_circuit); - const args: InputMap = { x, y }; - const { returnValue } = await program.execute(args, foreignCallHandler); - return returnValue as boolean; -} -``` - -Now the `is_equal()` function and relevant types are readily available for use in TypeScript. diff --git a/docs/versioned_docs/version-v0.34.0/tooling/debugger.md b/docs/versioned_docs/version-v0.34.0/tooling/debugger.md deleted file mode 100644 index 9b7565ba9ff..00000000000 --- a/docs/versioned_docs/version-v0.34.0/tooling/debugger.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: Debugger -description: Learn about the Noir Debugger, in its REPL or VS Code versions. -keywords: [Nargo, VSCode, Visual Studio Code, REPL, Debugger] -sidebar_position: 2 ---- - -# Noir Debugger - -There are currently two ways of debugging Noir programs: - -1. From VS 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). -2. Via the REPL debugger, which ships with Nargo. - -In order to use either version of the debugger, you will need to install recent enough versions of Noir, [Nargo](../getting_started/installation/index.md) and vscode-noir: - -- Noir & Nargo ≥0.28.0 -- Noir's VS Code extension ≥0.0.11 - -:::info -At the moment, the debugger supports debugging binary projects, but not contracts. -::: - -We cover the VS Code Noir debugger more in depth in [its VS Code debugger how-to guide](../how_to/debugger/debugging_with_vs_code.md) and [the reference](../reference/debugger/debugger_vscode.md). - -The REPL debugger is discussed at length in [the REPL debugger how-to guide](../how_to/debugger/debugging_with_the_repl.md) and [the reference](../reference/debugger/debugger_repl.md). diff --git a/docs/versioned_docs/version-v0.34.0/tooling/language_server.md b/docs/versioned_docs/version-v0.34.0/tooling/language_server.md deleted file mode 100644 index 81e0356ef8a..00000000000 --- a/docs/versioned_docs/version-v0.34.0/tooling/language_server.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -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] -sidebar_position: 0 ---- - -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.34.0/tooling/testing.md b/docs/versioned_docs/version-v0.34.0/tooling/testing.md deleted file mode 100644 index 866677da567..00000000000 --- a/docs/versioned_docs/version-v0.34.0/tooling/testing.md +++ /dev/null @@ -1,79 +0,0 @@ ---- -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] -sidebar_position: 1 ---- - -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 all -the constraints 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); -} -``` - -The string given to `should_fail_with` doesn't need to exactly match the failure reason, it just needs to be a substring of it: - -```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 = "airspeed velocity")] -fn test_bridgekeeper() { - main(32); -} -``` \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.34.0/tutorials/noirjs_app.md b/docs/versioned_docs/version-v0.34.0/tutorials/noirjs_app.md deleted file mode 100644 index eac28168445..00000000000 --- a/docs/versioned_docs/version-v0.34.0/tutorials/noirjs_app.md +++ /dev/null @@ -1,362 +0,0 @@ ---- -title: Building a web app with NoirJS -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, app] -sidebar_position: 0 -pagination_next: noir/concepts/data_types/index ---- - -NoirJS is a set of packages meant to work both in a browser and a server environment. In this tutorial, we will build a simple web app using them. From here, you should get an idea on how to proceed with your own Noir projects! - -You can find the complete app code for this guide [here](https://github.com/noir-lang/tiny-noirjs-app). - -## Setup - -:::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.31.x matches `noir_js@0.31.x`, etc. - -In this guide, we will be pinned to 0.31.0. - -::: - -Before we start, we want to make sure we have Node, Nargo and the Barretenberg proving system (`bb`) installed. - -We start by opening a terminal and executing `node --version`. If we don't get an output like `v20.10.0`, that means node is not installed. Let's do that by following the handy [nvm guide](https://github.com/nvm-sh/nvm?tab=readme-ov-file#install--update-script). - -As for `Nargo`, we can follow the [Nargo guide](../getting_started/installation/index.md) to install it. If you're lazy, just paste this on a terminal and run `noirup`: - -```sh -curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash -``` - -Follow the instructions on [this page](https://github.com/AztecProtocol/aztec-packages/tree/master/barretenberg/cpp/src/barretenberg/bb#installation) to install `bb`. -Version 0.41.0 is compatible with `nargo` version 0.31.0, which you can install with `bbup -v 0.41.0` once `bbup` is installed. - -Easy enough. Onwards! - -## Our project - -ZK is a powerful technology. An app that doesn't reveal one of the inputs to _anyone_ is almost unbelievable, yet Noir makes it as easy as a single line of code. - -In fact, it's so simple that it comes nicely packaged in `nargo`. Let's do that! - -### Nargo - -Run: - -```bash -nargo new circuit -``` - -And... That's about it. Your program is ready to be compiled and run. - -To compile, let's `cd` into the `circuit` folder to enter our project, and call: - -```bash -nargo compile -``` - -This compiles our circuit into `json` format and add it to a new `target` folder. - -:::info - -At this point in the tutorial, your folder structure should look like this: - -```tree -. -└── circuit <---- our working directory - ├── Nargo.toml - ├── src - │ └── main.nr - └── target - └── circuit.json -``` - -::: - -### Node and Vite - -If you want to explore Nargo, feel free to go on a side-quest now and follow the steps in the -[getting started](../getting_started/hello_noir/index.md) guide. However, we want our app to run on the browser, so we need Vite. - -Vite is a powerful tool to generate static websites. While it provides all kinds of features, let's just go barebones with some good old vanilla JS. - -To do this this, go back to the previous folder (`cd ..`) and create a new vite project by running `npm create vite` and choosing "Vanilla" and "Javascript". - -A wild `vite-project` directory should now appear in your root folder! Let's not waste any time and dive right in: - -```bash -cd vite-project -``` - -### Setting Up Vite and Configuring the Project - -Before we proceed with any coding, let's get our environment tailored for Noir. We'll start by laying down the foundations with a `vite.config.js` file. This little piece of configuration is our secret sauce for making sure everything meshes well with the NoirJS libraries and other special setups we might need, like handling WebAssembly modules. Here’s how you get that going: - -#### Creating the vite.config.js - -In your freshly minted `vite-project` folder, create a new file named `vite.config.js` and open it in your code editor. Paste the following to set the stage: - -```javascript -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 { - build: { - target: 'esnext', - rollupOptions: { - external: ['@aztec/bb.js'] - } - }, - optimizeDeps: { - esbuildOptions: { - target: 'esnext' - } - }, - plugins: [ - copy({ - targets: [{ src: 'node_modules/**/*.wasm', dest: 'node_modules/.vite/dist' }], - copySync: true, - hook: 'buildStart', - }), - command === 'serve' ? wasmContentTypePlugin : [], - ], - }; - } - - return {}; -}); -``` - -#### Install Dependencies - -Now that our stage is set, install the necessary NoirJS packages along with our other dependencies: - -```bash -npm install && npm install @noir-lang/backend_barretenberg@0.31.0 @noir-lang/noir_js@0.31.0 -npm install rollup-plugin-copy --save-dev -``` - -:::info - -At this point in the tutorial, your folder structure should look like this: - -```tree -. -└── circuit - └── ...etc... -└── vite-project <---- our working directory - └── ...etc... -``` - -::: - -#### Some cleanup - -`npx create vite` is amazing but it creates a bunch of files we don't really need for our simple example. Actually, let's just delete everything except for `vite.config.js`, `index.html`, `main.js` and `package.json`. I feel lighter already. - -![my heart is ready for you, noir.js](@site/static/img/memes/titanic.jpeg) - -## HTML - -Our app won't run like this, of course. We need some working HTML, at least. Let's open our broken-hearted `index.html` and replace everything with this code snippet: - -```html - - - - - - -

Noir app

-
- - -
-
-

Logs

-

Proof

-
- - -``` - -It _could_ be a beautiful UI... Depending on which universe you live in. - -## Some good old vanilla Javascript - -Our love for Noir needs undivided attention, so let's just open `main.js` and delete everything (this is where the romantic scenery becomes a bit creepy). - -Start by pasting in this boilerplate code: - -```js -function display(container, msg) { - const c = document.getElementById(container); - const p = document.createElement('p'); - p.textContent = msg; - c.appendChild(p); -} - -document.getElementById('submitGuess').addEventListener('click', async () => { - try { - // here's where love happens - } catch (err) { - display('logs', 'Oh 💔 Wrong guess'); - } -}); -``` - -The display function doesn't do much. We're simply manipulating our website to see stuff happening. For example, if the proof fails, it will simply log a broken heart 😢 - -:::info - -At this point in the tutorial, your folder structure should look like this: - -```tree -. -└── circuit - └── ...same as above -└── vite-project - ├── vite.config.js - ├── main.js - ├── package.json - └── index.html -``` - -You'll see other files and folders showing up (like `package-lock.json`, `node_modules`) but you shouldn't have to care about those. - -::: - -## Some NoirJS - -We're starting with the good stuff now. If you've compiled the circuit as described above, you should have a `json` file we want to import at the very top of our `main.js` file: - -```ts -import circuit from '../circuit/target/circuit.json'; -``` - -[Noir is backend-agnostic](../index.mdx#whats-new-about-noir). We write Noir, but we also need a proving backend. That's why we need to import and instantiate the two dependencies we installed above: `BarretenbergBackend` and `Noir`. Let's import them right below: - -```js -import { BarretenbergBackend, BarretenbergVerifier as Verifier } from '@noir-lang/backend_barretenberg'; -import { Noir } from '@noir-lang/noir_js'; -``` - -And instantiate them inside our try-catch block: - -```ts -// try { -const backend = new BarretenbergBackend(circuit); -const noir = new Noir(circuit); -// } -``` - -:::note - -For the remainder of the tutorial, everything will be happening inside the `try` block - -::: - -## Our app - -Now for the app itself. We're capturing whatever is in the input when people press the submit button. Just add this: - -```js -const x = parseInt(document.getElementById('guessInput').value); -const input = { x, y: 2 }; -``` - -Now we're ready to prove stuff! Let's feed some inputs to our circuit and calculate the proof: - -```js -await setup(); // let's squeeze our wasm inits here - -display('logs', 'Generating proof... ⌛'); -const { witness } = await noir.execute(input); -const proof = await backend.generateProof(witness); -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 run dev`. If it doesn't open a browser for you, just visit `localhost:5173`. You should now see the worst UI ever, with an ugly input. - -![Getting Started 0](@site/static/img/noir_getting_started_1.png) - -Now, our circuit says `fn main(x: Field, y: pub Field)`. This means only the `y` value is public, and it's hardcoded above: `input = { x, y: 2 }`. In other words, you won't need to send your secret`x` to the verifier! - -By inputting any number other than 2 in the input box and clicking "submit", you should get a valid proof. Otherwise the proof won't even generate correctly. By the way, if you're human, you shouldn't be able to understand anything on the "proof" box. That's OK. We like you, human ❤️. - -## Verifying - -Time to celebrate, yes! But we shouldn't trust machines so blindly. Let's add these lines to see our proof being verified: - -```js -display('logs', 'Verifying proof... ⌛'); -const isValid = await backend.verifyProof(proof); - -// or to cache and use the verification key: -// const verificationKey = await backend.getVerificationKey(); -// const verifier = new Verifier(); -// const isValid = await verifier.verifyProof(proof, verificationKey); - -if (isValid) display('logs', 'Verifying proof... ✅'); -``` - -You have successfully generated a client-side Noir web app! - -![coded app without math knowledge](@site/static/img/memes/flextape.jpeg) - -## 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/vite-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. - -## UltraHonk Backend - -Barretenberg has recently exposed a new UltraHonk backend. We can use UltraHonk in NoirJS after version 0.33.0. Everything will be the same as the tutorial above, except that the class we need to import will change: -```js -import { UltraHonkBackend, UltraHonkVerifier as Verifier } from '@noir-lang/backend_barretenberg'; -``` -The backend will then be instantiated as such: -```js -const backend = new UltraHonkBackend(circuit); -``` -Then all the commands to prove and verify your circuit will be same. - -The only feature currently unsupported with UltraHonk are [recursive proofs](../explainers/explainer-recursion.md). \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.35.0/explainers/cspell.json b/docs/versioned_docs/version-v0.35.0/explainers/cspell.json deleted file mode 100644 index c60b0a597b1..00000000000 --- a/docs/versioned_docs/version-v0.35.0/explainers/cspell.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "words": [ - "Cryptdoku" - ] -} diff --git a/docs/versioned_docs/version-v0.35.0/explainers/explainer-oracle.md b/docs/versioned_docs/version-v0.35.0/explainers/explainer-oracle.md deleted file mode 100644 index 821e1f95c04..00000000000 --- a/docs/versioned_docs/version-v0.35.0/explainers/explainer-oracle.md +++ /dev/null @@ -1,57 +0,0 @@ ---- -title: Oracles -description: This guide provides an in-depth understanding of how Oracles work in Noir programming. Learn how to use outside calculations in your programs, constrain oracles, and understand their uses and limitations. -keywords: - - Noir Programming - - Oracles - - JSON-RPC - - Foreign Call Handlers - - Constrained Functions - - Blockchain Programming -sidebar_position: 1 ---- - -If you've seen "The Matrix" you may recall "The Oracle" as Gloria Foster smoking cigarettes and baking cookies. While she appears to "know things", she is actually providing a calculation of a pre-determined future. Noir Oracles are similar, in a way. They don't calculate the future (yet), but they allow you to use outside calculations in your programs. - -![matrix oracle prediction](@site/static/img/memes/matrix_oracle.jpeg) - -A Noir program is usually self-contained. You can pass certain inputs to it, and it will generate a deterministic output for those inputs. But what if you wanted to defer some calculation to an outside process or source? - -Oracles are functions that provide this feature. - -## Use cases - -An example usage for Oracles is proving something on-chain. For example, proving that the ETH-USDC quote was below a certain target at a certain block time. Or even making more complex proofs like proving the ownership of an NFT as an anonymous login method. - -Another interesting use case is to defer expensive calculations to be made outside of the Noir program, and then constraining the result; similar to the use of [unconstrained functions](../noir/concepts//unconstrained.md). - -In short, anything that can be constrained in a Noir program but needs to be fetched from an external source is a great candidate to be used in oracles. - -## Constraining oracles - -Just like in The Matrix, Oracles are powerful. But with great power, comes great responsibility. Just because you're using them in a Noir program doesn't mean they're true. Noir has no superpowers. If you want to prove that Portugal won the Euro Cup 2016, you're still relying on potentially untrusted information. - -To give a concrete example, Alice wants to login to the [NounsDAO](https://nouns.wtf/) forum with her username "noir_nouner" by proving she owns a noun without revealing her ethereum address. Her Noir program could have an oracle call like this: - -```rust -#[oracle(getNoun)] -unconstrained fn get_noun(address: Field) -> Field -``` - -This oracle could naively resolve with the number of Nouns she possesses. However, it is useless as a trusted source, as the oracle could resolve to anything Alice wants. In order to make this oracle call actually useful, Alice would need to constrain the response from the oracle, by proving her address and the noun count belongs to the state tree of the contract. - -In short, **Oracles don't prove anything. Your Noir program does.** - -:::danger - -If you don't constrain the return of your oracle, you could be clearly opening an attack vector on your Noir program. Make double-triple sure that the return of an oracle call is constrained! - -::: - -## How to use Oracles - -On CLI, Nargo resolves oracles by making JSON RPC calls, which means it would require an RPC node to be running. - -In JavaScript, NoirJS accepts and resolves arbitrary call handlers (that is, not limited to JSON) as long as they match the expected types the developer defines. Refer to [Foreign Call Handler](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) to learn more about NoirJS's call handling. - -If you want to build using oracles, follow through to the [oracle guide](../how_to/how-to-oracles.md) for a simple example on how to do that. diff --git a/docs/versioned_docs/version-v0.35.0/explainers/explainer-recursion.md b/docs/versioned_docs/version-v0.35.0/explainers/explainer-recursion.md deleted file mode 100644 index df8529ef4e0..00000000000 --- a/docs/versioned_docs/version-v0.35.0/explainers/explainer-recursion.md +++ /dev/null @@ -1,176 +0,0 @@ ---- -title: Recursive proofs -description: Explore the concept of recursive proofs in Zero-Knowledge programming. Understand how recursion works in Noir, a language for writing smart contracts on the EVM blockchain. Learn through practical examples like Alice and Bob's guessing game, Charlie's recursive merkle tree, and Daniel's reusable components. Discover how to use recursive proofs to optimize computational resources and improve efficiency. - -keywords: - [ - "Recursive Proofs", - "Zero-Knowledge Programming", - "Noir", - "EVM Blockchain", - "Smart Contracts", - "Recursion in Noir", - "Alice and Bob Guessing Game", - "Recursive Merkle Tree", - "Reusable Components", - "Optimizing Computational Resources", - "Improving Efficiency", - "Verification Key", - "Aggregation", - "Recursive zkSNARK schemes", - "PLONK", - "Proving and Verification Keys" - ] -sidebar_position: 1 -pagination_next: how_to/how-to-recursion ---- - -In programming, we tend to think of recursion as something calling itself. A classic example would be the calculation of the factorial of a number: - -```js -function factorial(n) { - if (n === 0 || n === 1) { - return 1; - } else { - return n * factorial(n - 1); - } -} -``` - -In this case, while `n` is not `1`, this function will keep calling itself until it hits the base case, bubbling up the result on the call stack: - -```md - Is `n` 1? <--------- - /\ / - / \ n = n -1 - / \ / - Yes No -------- -``` - -In Zero-Knowledge, recursion has some similarities. - -It is not a Noir function calling itself, but a proof being used as an input to another circuit. In short, you verify one proof *inside* another proof, returning the proof that both proofs are valid. - -This means that, given enough computational resources, you can prove the correctness of any arbitrary number of proofs in a single proof. This could be useful to design state channels (for which a common example would be [Bitcoin's Lightning Network](https://en.wikipedia.org/wiki/Lightning_Network)), to save on gas costs by settling one proof on-chain, or simply to make business logic less dependent on a consensus mechanism. - -## Examples - -Let us look at some of these examples - -### Alice and Bob - Guessing game - -Alice and Bob are friends, and they like guessing games. They want to play a guessing game online, but for that, they need a trusted third-party that knows both of their secrets and finishes the game once someone wins. - -So, they use zero-knowledge proofs. Alice tries to guess Bob's number, and Bob will generate a ZK proof stating whether she succeeded or failed. - -This ZK proof can go on a smart contract, revealing the winner and even giving prizes. However, this means every turn needs to be verified on-chain. This incurs some cost and waiting time that may simply make the game too expensive or time-consuming to be worth it. - -As a solution, Alice proposes the following: "what if Bob generates his proof, and instead of sending it on-chain, I verify it *within* my own proof before playing my own turn?". - -She can then generate a proof that she verified his proof, and so on. - -```md - Did you fail? <-------------------------- - / \ / - / \ n = n -1 - / \ / - Yes No / - | | / - | | / - | You win / - | / - | / -Generate proof of that / - + / - my own guess ---------------- -``` - -### Charlie - Recursive merkle tree - -Charlie is a concerned citizen, and wants to be sure his vote in an election is accounted for. He votes with a ZK proof, but he has no way of knowing that his ZK proof was included in the total vote count! - -If the vote collector puts all of the votes into a [Merkle tree](https://en.wikipedia.org/wiki/Merkle_tree), everyone can prove the verification of two proofs within one proof, as such: - -```md - abcd - __________|______________ - | | - ab cd - _____|_____ ______|______ - | | | | - alice bob charlie daniel -``` - -Doing this recursively allows us to arrive on a final proof `abcd` which if true, verifies the correctness of all the votes. - -### Daniel - Reusable components - -Daniel has a big circuit and a big headache. A part of his circuit is a setup phase that finishes with some assertions that need to be made. But that section alone takes most of the proving time, and is largely independent of the rest of the circuit. - -He might find it more efficient to generate a proof for that setup phase separately, and verify that proof recursively in the actual business logic section of his circuit. This will allow for parallelization of both proofs, which results in a considerable speedup. - -## What params do I need - -As you can see in the [recursion reference](noir/standard_library/recursion.mdx), a simple recursive proof requires: - -- The proof to verify -- The Verification Key of the circuit that generated the proof -- A hash of this verification key, as it's needed for some backends -- The public inputs for the proof - -:::info - -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. - -So, taking the example of Alice and Bob and their guessing game: - -- Alice makes her guess. Her proof is *not* recursive: it doesn't verify any proof within it! It's just a standard `assert(x != y)` circuit -- Bob verifies Alice's proof and makes his own guess. In this circuit, he doesn't exactly *prove* the verification of Alice's proof. Instead, he *aggregates* his proof to Alice's proof. The actual verification is done when the full proof is verified, for example when using `nargo verify` or through the verifier smart contract. - -We can imagine recursive proofs a [relay race](https://en.wikipedia.org/wiki/Relay_race). The first runner doesn't have to receive the baton from anyone else, as he/she already starts with it. But when his/her turn is over, the next runner needs to receive it, run a bit more, and pass it along. Even though every runner could theoretically verify the baton mid-run (why not? 🏃🔍), only at the end of the race does the referee verify that the whole race is valid. - -::: - -## Some architecture - -As with everything in computer science, there's no one-size-fits all. But there are some patterns that could help understanding and implementing them. To give three examples: - -### Adding some logic to a proof verification - -This would be an approach for something like our guessing game, where proofs are sent back and forth and are verified by each opponent. This circuit would be divided in two sections: - -- A `recursive verification` section, which would be just the call to `std::verify_proof`, and that would be skipped on the first move (since there's no proof to verify) -- A `guessing` section, which is basically the logic part where the actual guessing happens - -In such a situation, and assuming Alice is first, she would skip the first part and try to guess Bob's number. Bob would then verify her proof on the first section of his run, and try to guess Alice's number on the second part, and so on. - -### Aggregating proofs - -In some one-way interaction situations, recursion would allow for aggregation of simple proofs that don't need to be immediately verified on-chain or elsewhere. - -To give a practical example, a barman wouldn't need to verify a "proof-of-age" on-chain every time he serves alcohol to a customer. Instead, the architecture would comprise two circuits: - -- A `main`, non-recursive circuit with some logic -- A `recursive` circuit meant to verify two proofs in one proof - -The customer's proofs would be intermediate, and made on their phones, and the barman could just verify them locally. He would then aggregate them into a final proof sent on-chain (or elsewhere) at the end of the day. - -### Recursively verifying different circuits - -Nothing prevents you from verifying different circuits in a recursive proof, for example: - -- A `circuit1` circuit -- A `circuit2` circuit -- A `recursive` circuit - -In this example, a regulator could verify that taxes were paid for a specific purchase by aggregating both a `payer` circuit (proving that a purchase was made and taxes were paid), and a `receipt` circuit (proving that the payment was received) - -## How fast is it - -At the time of writing, verifying recursive proofs is surprisingly fast. This is because most of the time is spent on generating the verification key that will be used to generate the next proof. So you are able to cache the verification key and reuse it later. - -Currently, Noir JS packages don't expose the functionality of loading proving and verification keys, but that feature exists in the underlying `bb.js` package. - -## How can I try it - -Learn more about using recursion in Nargo and NoirJS in the [how-to guide](../how_to/how-to-recursion.md) and see a full example in [noir-examples](https://github.com/noir-lang/noir-examples). diff --git a/docs/versioned_docs/version-v0.35.0/explainers/explainer-writing-noir.md b/docs/versioned_docs/version-v0.35.0/explainers/explainer-writing-noir.md deleted file mode 100644 index 3ef6e014a2f..00000000000 --- a/docs/versioned_docs/version-v0.35.0/explainers/explainer-writing-noir.md +++ /dev/null @@ -1,177 +0,0 @@ ---- -title: Writing Performant Noir -description: Understand new considerations when writing Noir -keywords: [Noir, programming, rust] -tags: [Optimization] -sidebar_position: 0 ---- - - -This article intends to set you up with key concepts essential for writing more viable applications that use zero knowledge proofs, namely around efficient circuits. - -## Context - 'Efficient' is subjective - -When writing a web application for a performant computer with high-speed internet connection, writing efficient code sometimes is seen as an afterthought only if needed. Large multiplications running at the innermost of nested loops may not even be on a dev's radar. -When writing firmware for a battery-powered microcontroller, you think of cpu cycles as rations to keep within a product's power budget. - -> Code is written to create applications that perform specific tasks within specific constraints - -And these constraints differ depending on where the compiled code is execute. - -### The Ethereum Virtual Machine (EVM) - -In scenarios where extremely low gas costs are required for an Ethereum application to be viable/competitive, Ethereum smart contract developers get into what is colloquially known as: "*gas golfing*". Finding the lowest execution cost of their compiled code (EVM bytecode) to achieve a specific task. - -The equivalent optimization task when writing zk circuits is affectionately referred to as "*gate golfing*", finding the lowest gate representation of the compiled Noir code. - -### Coding for circuits - a paradigm shift - -In zero knowledge cryptography, code is compiled to "circuits" consisting of arithmetic gates, and gate count is the significant cost. Depending on the proving system this is linearly proportionate to proving time, and so from a product point this should be kept as low as possible. - -Whilst writing efficient code for web apps and Solidity has a few key differences, writing efficient circuits have a different set of considerations. It is a bit of a paradigm shift, like writing code for GPUs for the first time... - -For example, drawing a circle at (0, 0) of radius `r`: -- For a single CPU thread, -``` -for theta in 0..2*pi { - let x = r * cos(theta); - let y = r * sin(theta); - draw(x, y); -} // note: would do 0 - pi/2 and draw +ve/-ve x and y. -``` - -- For GPUs (simultaneous parallel calls with x, y across image), -``` -if (x^2 + y^2 = r^2) { - draw(x, y); -} -``` - -([Related](https://www.youtube.com/watch?v=-P28LKWTzrI)) - -Whilst this CPU -> GPU does not translate to circuits exactly, it is intended to exemplify the difference in intuition when coding for different machine capabilities/constraints. - -### Context Takeaway - -For those coming from a primarily web app background, this article will explain what you need to consider when writing circuits. Furthermore, for those experienced writing efficient machine code, prepare to shift what you think is efficient 😬 - -## Translating from Rust - -For some applications using Noir, existing code might be a convenient starting point to then proceed to optimize the gate count of. - -:::note -Many valuable functions and algorithms have been written in more established languages (C/C++), and converted to modern ones (like Rust). -::: - -Fortunately for Noir developers, when needing a particular function a Rust implementation can be readily compiled into Noir with some key changes. While the compiler does a decent amount of optimizations, it won't be able to change code that has been optimized for clock-cycles into code optimized for arithmetic gates. - -A few things to do when converting Rust code to Noir: -- `println!` is not a macro, use `println` function (same for `assert_eq`) -- No early `return` in function. Use constrain via assertion instead -- No passing by reference. Remove `&` operator to pass by value (copy) -- No boolean operators (`&&`, `||`). Use bitwise operators (`&`, `|`) with boolean values -- No type `usize`. Use types `u8`, `u32`, `u64`, ... -- `main` return must be public, `pub` -- No `const`, use `global` -- Noir's LSP is your friend, so error message should be informative enough to resolve syntax issues. - -## Writing efficient Noir for performant products - -The following points help refine our understanding over time. - -:::note -A Noir program makes a statement that can be verified. -::: - -It compiles to a structure that represents the calculation, and can assert results within the calculation at any stage (via the `constrain` keyword). - -A Noir program compiles to an Abstract Circuit Intermediate Representation which is: - - Conceptually a tree structure - - Leaves (inputs) are the `Field` type - - Nodes contain arithmetic operations to combine them (gates) - - The root is the final result (return value) - -:::tip -The command `nargo info` shows the programs circuit size, and is useful to compare the value of changes made. -You can dig deeper and use the `--print-acir` param to take a closer look at individual ACIR opcodes, and the proving backend to see its gate count (eg for barretenberg, `bb gates -b ./target/program.json`). -::: - -### Use the `Field` type - -Since the native type of values in circuits are `Field`s, using them for variables in Noir means less gates converting them under the hood. -Some things to be mindful of when using a Field type for a regular integer value: -- A variable of type `Field` can be cast `as` an integer type (eg `u8`, `u64`) - - Note: this retains only the bits of the integer type. Eg a Field value of 260 as a `u8` becomes 4 -- For Field types arithmetic operations meaningfully overflow/underflow, yet for integer types they are checked according to their size -- Comparisons and bitwise operations do not exist for `Field`s, cast to an appropriately sized integer type when you need to - -:::tip -Where possible, use `Field` type for values. Using smaller value types, and bit-packing strategies, will result in MORE gates -::: - - -### Use Arithmetic over non-arithmetic operations - -Since circuits are made of arithmetic gates, the cost of arithmetic operations tends to be one gate. Whereas for procedural code, they represent several clock cycles. - -Inversely, non-arithmetic operators are achieved with multiple gates, vs 1 clock cycle for procedural code. - -| (cost\op) | arithmetic
(`*`, `+`) | bit-wise ops
(eg `<`, `\|`, `>>`) | -| - | - | - | -| **cycles** | 10+ | 1 | -| **gates** | 1 | 10+ | - -Bit-wise operations (e.g. bit shifts `<<` and `>>`), albeit commonly used in general programming and especially for clock cycle optimizations, are on the contrary expensive in gates when performed within circuits. - -Translate away from bit shifts when writing constrained functions for the best performance. - -On the flip side, feel free to use bit shifts in unconstrained functions and tests if necessary, as they are executed outside of circuits and does not induce performance hits. - -### Use static over dynamic values - -Another general theme that manifests in different ways is that static reads are represented with less gates than dynamic ones. - -Reading from read-only memory (ROM) adds less gates than random-access memory (RAM), 2 vs ~3.25 due to the additional bounds checks. Arrays of fixed length (albeit used at a lower capacity), will generate less gates than dynamic storage. - -Related to this, if an index used to access an array is not known at compile time (ie unknown until run time), then ROM will be converted to RAM, expanding the gate count. - -:::tip -Use arrays and indices that are known at compile time where possible. -Using `assert_constant(i);` before an index, `i`, is used in an array will give a compile error if `i` is NOT known at compile time. -::: - -### Leverage unconstrained execution - -Constrained verification can leverage unconstrained execution, this is especially useful for operations that are represented by many gates. -Use an [unconstrained function](../noir/concepts/unconstrained.md) to perform gate-heavy calculations, then verify and constrain the result. - -Eg division generates more gates than multiplication, so calculating the quotient in an unconstrained function then constraining the product for the quotient and divisor (+ any remainder) equals the dividend will be more efficient. - -Use ` if is_unconstrained() { /`, to conditionally execute code if being called in an unconstrained vs constrained way. - -## Advanced - -Unless you're well into the depth of gate optimization, this advanced section can be ignored. - -### Combine arithmetic operations - -A Noir program can be honed further by combining arithmetic operators in a way that makes the most of each constraint of the backend proving system. This is in scenarios where the backend might not be doing this perfectly. - -Eg Barretenberg backend (current default for Noir) is a width-4 PLONKish constraint system -$ w_1*w_2*q_m + w_1*q_1 + w_2*q_2 + w_3*q_3 + w_4*q_4 + q_c = 0 $ - -Here we see there is one occurrence of witness 1 and 2 ($w_1$, $w_2$) being multiplied together, with addition to witnesses 1-4 ($w_1$ .. $w_4$) multiplied by 4 corresponding circuit constants ($q_1$ .. $q_4$) (plus a final circuit constant, $q_c$). - -Use `nargo info --print-acir`, to inspect the ACIR opcodes (and the proving system for gates), and it may present opportunities to amend the order of operations and reduce the number of constraints. - -#### Variable as witness vs expression - -If you've come this far and really know what you're doing at the equation level, a temporary lever (that will become unnecessary/useless over time) is: `std::as_witness`. This informs the compiler to save a variable as a witness not an expression. - -The compiler will mostly be correct and optimal, but this may help some near term edge cases that are yet to optimize. -Note: When used incorrectly it will create **less** efficient circuits (higher gate count). - -## References -- Guillaume's ["`Cryptdoku`" talk](https://www.youtube.com/watch?v=MrQyzuogxgg) (Jun'23) -- Tips from Tom, Jake and Zac. -- [Idiomatic Noir](https://www.vlayer.xyz/blog/idiomatic-noir-part-1-collections) blog post diff --git a/docs/versioned_docs/version-v0.35.0/getting_started/_category_.json b/docs/versioned_docs/version-v0.35.0/getting_started/_category_.json deleted file mode 100644 index 5d694210bbf..00000000000 --- a/docs/versioned_docs/version-v0.35.0/getting_started/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "position": 0, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.35.0/getting_started/backend/_category_.json b/docs/versioned_docs/version-v0.35.0/getting_started/backend/_category_.json deleted file mode 100644 index b82e92beb0c..00000000000 --- a/docs/versioned_docs/version-v0.35.0/getting_started/backend/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "position": 1, - "label": "Install Proving Backend", - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.35.0/getting_started/backend/index.md b/docs/versioned_docs/version-v0.35.0/getting_started/backend/index.md deleted file mode 100644 index 7192d954877..00000000000 --- a/docs/versioned_docs/version-v0.35.0/getting_started/backend/index.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: Proving Backend Installation -description: Proving backends offer command line tools for proving and verifying Noir programs. This page describes how to install `bb` as an example. -keywords: [ - Proving - Backend - Barretenberg - bb - bbup - Installation - Terminal - Command - CLI - Version -] -pagination_next: getting_started/hello_noir/index ---- - -Proving backends each provide their own tools for working with Noir programs, providing functionality like proof generation, proof verification, and verifier smart contract generation. - -For the latest information on tooling provided by each proving backend, installation instructions, Noir version compatibility... you may refer to the proving backends' own documentation. - -You can find the full list of proving backends compatible with Noir in [Awesome Noir](https://github.com/noir-lang/awesome-noir/?tab=readme-ov-file#proving-backends). - -## Example: Installing `bb` - -`bb` is the CLI tool provided by the [Barretenberg proving backend](https://github.com/AztecProtocol/barretenberg) developed by Aztec Labs. - -You can find the instructions for installation in [`bb`'s documentation](https://github.com/AztecProtocol/aztec-packages/blob/master/barretenberg/cpp/src/barretenberg/bb/readme.md#installation). - -Once installed, we are ready to start working on [our first Noir program](../hello_noir/index.md). diff --git a/docs/versioned_docs/version-v0.35.0/getting_started/hello_noir/_category_.json b/docs/versioned_docs/version-v0.35.0/getting_started/hello_noir/_category_.json deleted file mode 100644 index 976a2325de0..00000000000 --- a/docs/versioned_docs/version-v0.35.0/getting_started/hello_noir/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "position": 2, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.35.0/getting_started/hello_noir/index.md b/docs/versioned_docs/version-v0.35.0/getting_started/hello_noir/index.md deleted file mode 100644 index 6760e54aad1..00000000000 --- a/docs/versioned_docs/version-v0.35.0/getting_started/hello_noir/index.md +++ /dev/null @@ -1,157 +0,0 @@ ---- -title: Creating 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, - ] -sidebar_position: 1 - ---- - -Now that we have installed Nargo and a proving backend, it is time to make our first hello world program! - -### 1. Create a new project directory - -Noir code can live anywhere on your computer. Let us create a _projects_ folder in the home -directory to house our first Noir program. - -Create the directory and change directory into it by running: - -```sh -mkdir ~/projects -cd ~/projects -``` - -## Nargo - -Nargo provides the ability to initiate and execute Noir projects. Read the [Nargo installation](../installation/index.md) section to learn more about Nargo and how to install it. - -### 2. Create a new Noir project - -Now that we are in the projects directory, create a new Nargo project by running: - -```sh -nargo new hello_world -``` - -`hello_world` can be any arbitrary project name, we are simply using `hello_world` for demonstration. - -In production, it is common practice to name the project folder, `circuits`, for clarity amongst other folders in the codebase (like: `contracts`, `scripts`, and `test`). - -A `hello_world` folder would be created. Similar to Rust, the folder houses _src/main.nr_ and -_Nargo.toml_ which contain 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](../../noir/concepts/data_types/index.md) 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](../../noir/concepts/comments.md) chapter. - -### 3. 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 -``` - -A _Prover.toml_ file will be generated in your project directory, to allow specifying input values to the program. - -### 4. Execute the Noir program - -Now that the project is set up, we can execute our Noir program. - -Fill in input values for execution in the _Prover.toml_ file. For example: - -```toml -x = "1" -y = "2" -``` - -Execute your Noir program: - -```sh -nargo execute witness-name -``` - -The witness corresponding to this execution will then be written to the file `./target/witness-name.gz`. - -The command also automatically compiles your Noir program if it was not already / was edited, which you may notice the compiled artifacts being written to the file `./target/hello_world.json`. - -## Proving Backend - -Proving backends provide the ability to generate and verify proofs of executing Noir programs, following Noir's tooling that compiles and executes the programs. Read the [proving backend installation](../backend/index.md) section to learn more about proving backends and how to install them. - -Barretenberg is used as an example here to demonstrate how proving and verifying could be implemented and used. Read the [`bb` installation](../backend/index.md#example-installing-bb) section for how to install Barretenberg's CLI tool; refer to [`bb`'s documentation](https://github.com/AztecProtocol/aztec-packages/blob/master/barretenberg/cpp/src/barretenberg/bb/readme.md) for full details about the tool. - -### 5. Prove an execution of the Noir program - -Using Barretenberg as an example, prove the valid execution of your Noir program running: - -```sh -bb prove -b ./target/hello_world.json -w ./target/witness-name.gz -o ./target/proof -``` - -The proof generated will then be written to the file `./target/proof`. - -:::tip -Since the params for `nargo` and `bb` often specify multiple filenames to read from or write to, remember to check each command is referring to the desired filenames. -Or for greater certainty, delete the target folder and go through each step again (compile, witness, prove, ...) to ensure files generated in past commands are being referenced in future ones. -::: - -### 6. Verify the execution proof - -Once a proof is generated, we can verify correct execution of our Noir program by verifying the proof file. - -Using Barretenberg as an example, compute the verification key for the Noir program by running: - -```sh -bb write_vk -b ./target/hello_world.json -o ./target/vk -``` - -And verify your proof by running: - -```sh -bb verify -k ./target/vk -p ./target/proof -``` - -If successful, the verification will complete in silence; if unsuccessful, the command will trigger logging of the corresponding error. - -Congratulations, you have now created and verified a proof for your very first Noir program! - -In the [next section](./project_breakdown.md), we will go into more detail on each step performed. diff --git a/docs/versioned_docs/version-v0.35.0/getting_started/hello_noir/project_breakdown.md b/docs/versioned_docs/version-v0.35.0/getting_started/hello_noir/project_breakdown.md deleted file mode 100644 index 96e653f6c08..00000000000 --- a/docs/versioned_docs/version-v0.35.0/getting_started/hello_noir/project_breakdown.md +++ /dev/null @@ -1,159 +0,0 @@ ---- -title: Project Breakdown -description: - Learn about the anatomy of a Nargo project, including the purpose of the Prover TOML - file, and how to prove and verify your program. -keywords: - [Nargo, Nargo project, Prover.toml, proof verification, private asset transfer] -sidebar_position: 2 ---- - -This section breaks down our hello world program from 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 - - 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. - -### 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 = "noir_starter" -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](../../noir/modules_packages_crates/workspaces.md) will look a bit different. For example: - -```toml -[workspace] -members = ["crates/a", "crates/b"] -default-member = "crates/a" -``` - -#### Package section - -The package section defines 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) -- `expression_width` (optional) - Sets the default backend expression width. This field will override the default backend expression width specified by the Noir compiler (currently set to width 4). - -#### Dependencies section - -This is where you will specify any dependencies for your project. See the [Dependencies page](../../noir/modules_packages_crates/dependencies.md) 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 that the condition to be satisfied (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 the inputs to the Noir program (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 execute` is executed, nargo will execute the Noir program using the inputs specified in `Prover.toml`, aborting if it finds that these do not satisfy the constraints defined by `main`. In this example, `x` and `y` must satisfy the inequality constraint `assert(x != y)`. - -If an output name is specified such as `nargo execute foo`, the witness generated by this execution will be written to `./target/foo.gz`. This can then be used to generate a proof of the execution. - -#### 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 execution by using the `--prover-name` or `-p` flags. - -This command looks for proof inputs in the default **Prover.toml** and generates the witness and saves it at `./target/foo.gz`: - -```bash -nargo execute foo -``` - -This command looks for proof inputs in the custom **OtherProver.toml** and generates the witness and saves it at `./target/bar.gz`: - -```bash -nargo execute -p OtherProver bar -``` - -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.35.0/getting_started/installation/_category_.json b/docs/versioned_docs/version-v0.35.0/getting_started/installation/_category_.json deleted file mode 100644 index 0c02fb5d4d7..00000000000 --- a/docs/versioned_docs/version-v0.35.0/getting_started/installation/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "position": 0, - "label": "Install Nargo", - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.35.0/getting_started/installation/index.md b/docs/versioned_docs/version-v0.35.0/getting_started/installation/index.md deleted file mode 100644 index 53ea9c7891c..00000000000 --- a/docs/versioned_docs/version-v0.35.0/getting_started/installation/index.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -title: Nargo Installation -description: - nargo is a command line tool for interacting with Noir programs. This page is a quick guide on how to install Nargo through the most common and easy method, noirup -keywords: [ - Nargo - Noir - Rust - Cargo - Noirup - Installation - Terminal Commands - Version Check - Nightlies - Specific Versions - Branches - Noirup Repository -] -pagination_next: getting_started/hello_noir/index ---- - -`nargo` is a tool for working with Noir programs on the CLI, providing you with the ability to start new projects, compile, execute and test Noir programs from the terminal. - -The name is inspired by Rust's package manager `cargo`; and similar to Rust's `rustup`, Noir also has an easy installation script `noirup`. - -## Installing Noirup - -Open a terminal on your machine, and write: - -```bash -curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash -``` - -Close the terminal, open another one, and run - -```bash -noirup -``` - -Done. That's it. 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. - -Now we're ready to start working on [our first Noir program!](../hello_noir/index.md) diff --git a/docs/versioned_docs/version-v0.35.0/getting_started/installation/other_install_methods.md b/docs/versioned_docs/version-v0.35.0/getting_started/installation/other_install_methods.md deleted file mode 100644 index 3634723562b..00000000000 --- a/docs/versioned_docs/version-v0.35.0/getting_started/installation/other_install_methods.md +++ /dev/null @@ -1,102 +0,0 @@ ---- -title: Alternative Installations -description: There are different ways to install Nargo, the one-stop shop and command-line tool for developing Noir programs. This guide explains how to specify which version to install when using noirup, and using WSL for windows. -keywords: [ - Installation - Nargo - Noirup - Binaries - Compiling from Source - WSL for Windows - macOS - Linux - Nix - Direnv - Uninstalling Nargo - ] -sidebar_position: 1 ---- - -## Encouraged Installation Method: Noirup - -Noirup is the endorsed method for installing Nargo, streamlining the process of fetching binaries or compiling from source. It supports a range of options to cater to your specific needs, from nightly builds and specific versions to compiling from various sources. - -### Installing Noirup - -First, ensure you have `noirup` installed: - -```sh -curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash -``` - -### Fetching Binaries - -With `noirup`, you can easily switch between different Nargo versions, including nightly builds: - -- **Nightly Version**: Install the latest nightly build. - - ```sh - noirup --version nightly - ``` - -- **Specific Version**: Install a specific version of Nargo. - ```sh - noirup --version - ``` - -### Compiling from Source - -`noirup` also enables compiling Nargo from various sources: - -- **From a Specific Branch**: Install from the latest commit on a branch. - - ```sh - noirup --branch - ``` - -- **From a Fork**: Install from the main branch of a fork. - - ```sh - noirup --repo - ``` - -- **From a Specific Branch in a Fork**: Install from a specific branch in a fork. - - ```sh - noirup --repo --branch - ``` - -- **From a Specific Pull Request**: Install from a specific PR. - - ```sh - noirup --pr - ``` - -- **From a Specific Commit**: Install from a specific commit. - - ```sh - noirup -C - ``` - -- **From Local Source**: Compile and install from a local directory. - ```sh - noirup --path ./path/to/local/source - ``` - -## Installation on Windows - -The default backend for Noir (Barretenberg) doesn't provide Windows binaries at this time. For that reason, Noir cannot be installed natively. 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](#encouraged-installation-method-noirup). - -## Uninstalling Nargo - -If you installed Nargo with `noirup`, you can uninstall Nargo by removing the files in `~/.nargo`, `~/nargo`, and `~/noir_cache`. This ensures that all installed binaries, configurations, and cache related to Nargo are fully removed from your system. - -```bash -rm -r ~/.nargo -rm -r ~/nargo -rm -r ~/noir_cache -``` diff --git a/docs/versioned_docs/version-v0.35.0/how_to/_category_.json b/docs/versioned_docs/version-v0.35.0/how_to/_category_.json deleted file mode 100644 index 23b560f610b..00000000000 --- a/docs/versioned_docs/version-v0.35.0/how_to/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "position": 1, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.35.0/how_to/debugger/_category_.json b/docs/versioned_docs/version-v0.35.0/how_to/debugger/_category_.json deleted file mode 100644 index cc2cbb1c253..00000000000 --- a/docs/versioned_docs/version-v0.35.0/how_to/debugger/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "Debugging", - "position": 5, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.35.0/how_to/debugger/debugging_with_the_repl.md b/docs/versioned_docs/version-v0.35.0/how_to/debugger/debugging_with_the_repl.md deleted file mode 100644 index 1d64dae3f37..00000000000 --- a/docs/versioned_docs/version-v0.35.0/how_to/debugger/debugging_with_the_repl.md +++ /dev/null @@ -1,164 +0,0 @@ ---- -title: Using the REPL Debugger -description: - Step-by-step guide on how to debug your Noir circuits with the REPL Debugger. -keywords: - [ - Nargo, - Noir CLI, - Noir Debugger, - REPL, - ] -sidebar_position: 1 ---- - -#### Pre-requisites - -In order to use the REPL debugger, first you need to install recent enough versions of Nargo and vscode-noir. - -## Debugging a simple circuit - -Let's debug a simple circuit: - -```rust -fn main(x : Field, y : pub Field) { - assert(x != y); -} -``` - -To start the REPL debugger, using a terminal, go to a Noir circuit's home directory. Then: - -`$ nargo debug` - -You should be seeing this in your terminal: - -``` -[main] Starting debugger -At ~/noir-examples/recursion/circuits/main/src/main.nr:1:9 - 1 -> fn main(x : Field, y : pub Field) { - 2 assert(x != y); - 3 } -> -``` - -The debugger displays the current Noir code location, and it is now waiting for us to drive it. - -Let's first take a look at the available commands. For that we'll use the `help` command. - -``` -> help -Available commands: - - opcodes display ACIR opcodes - into step into to the next opcode - next step until a new source location is reached - out step until a new source location is reached - and the current stack frame is finished - break LOCATION:OpcodeLocation add a breakpoint at an opcode location - over step until a new source location is reached - without diving into function calls - restart restart the debugging session - delete LOCATION:OpcodeLocation delete breakpoint at an opcode location - witness show witness map - witness index:u32 display a single witness from the witness map - witness index:u32 value:String update a witness with the given value - memset index:usize value:String update a memory cell with the given - value - continue continue execution until the end of the - program - vars show variable values available at this point - in execution - stacktrace display the current stack trace - memory show memory (valid when executing unconstrained code) - step step to the next ACIR opcode - -Other commands: - - help Show this help message - quit Quit repl - -``` - -Some commands operate only for unconstrained functions, such as `memory` and `memset`. If you try to use them while execution is paused at an ACIR opcode, the debugger will simply inform you that you are not executing unconstrained code: - -``` -> memory -Unconstrained VM memory not available -> -``` - -Before continuing, we can take a look at the initial witness map: - -``` -> witness -_0 = 1 -_1 = 2 -> -``` - -Cool, since `x==1`, `y==2`, and we want to check that `x != y`, our circuit should succeed. At this point we could intervene and use the witness setter command to change one of the witnesses. Let's set `y=3`, then back to 2, so we don't affect the expected result: - -``` -> witness -_0 = 1 -_1 = 2 -> witness 1 3 -_1 = 3 -> witness -_0 = 1 -_1 = 3 -> witness 1 2 -_1 = 2 -> witness -_0 = 1 -_1 = 2 -> -``` - -Now we can inspect the current state of local variables. For that we use the `vars` command. - -``` -> vars -> -``` - -We currently have no vars in context, since we are at the entry point of the program. Let's use `next` to execute until the next point in the program. - -``` -> vars -> next -At ~/noir-examples/recursion/circuits/main/src/main.nr:1:20 - 1 -> fn main(x : Field, y : pub Field) { - 2 assert(x != y); - 3 } -> vars -x:Field = 0x01 -``` - -As a result of stepping, the variable `x`, whose initial value comes from the witness map, is now in context and returned by `vars`. - -``` -> next - 1 fn main(x : Field, y : pub Field) { - 2 -> assert(x != y); - 3 } -> vars -y:Field = 0x02 -x:Field = 0x01 -``` - -Stepping again we can finally see both variables and their values. And now we can see that the next assertion should succeed. - -Let's continue to the end: - -``` -> continue -(Continuing execution...) -Finished execution -> q -[main] Circuit witness successfully solved -``` - -Upon quitting the debugger after a solved circuit, the resulting circuit witness gets saved, equivalent to what would happen if we had run the same circuit with `nargo execute`. - -We just went through the basics of debugging using Noir REPL debugger. For a comprehensive reference, check out [the reference page](../../reference/debugger/debugger_repl.md). diff --git a/docs/versioned_docs/version-v0.35.0/how_to/debugger/debugging_with_vs_code.md b/docs/versioned_docs/version-v0.35.0/how_to/debugger/debugging_with_vs_code.md deleted file mode 100644 index a5858c1a5eb..00000000000 --- a/docs/versioned_docs/version-v0.35.0/how_to/debugger/debugging_with_vs_code.md +++ /dev/null @@ -1,68 +0,0 @@ ---- -title: Using the VS Code Debugger -description: - Step by step guide on how to debug your Noir circuits with the VS Code Debugger configuration and features. -keywords: - [ - Nargo, - Noir CLI, - Noir Debugger, - VS Code, - IDE, - ] -sidebar_position: 0 ---- - -This guide will show you how to use VS Code with the vscode-noir extension to debug a Noir project. - -#### Pre-requisites - -- Nargo -- vscode-noir -- A Noir project with a `Nargo.toml`, `Prover.toml` and at least one Noir (`.nr`) containing an entry point function (typically `main`). - -## Running the debugger - -The easiest way to start debugging is to open the file you want to debug, and press `F5`. This will cause the debugger to launch, using your `Prover.toml` file as input. - -You should see something like this: - -![Debugger launched](@site/static/img/debugger/1-started.png) - -Let's inspect the state of the program. For that, we open VS Code's _Debug pane_. Look for this icon: - -![Debug pane icon](@site/static/img/debugger/2-icon.png) - -You will now see two categories of variables: Locals and Witness Map. - -![Debug pane expanded](@site/static/img/debugger/3-debug-pane.png) - -1. **Locals**: variables of your program. At this point in execution this section is empty, but as we step through the code it will get populated by `x`, `result`, `digest`, etc. - -2. **Witness map**: these are initially populated from your project's `Prover.toml` file. In this example, they will be used to populate `x` and `result` at the beginning of the `main` function. - -Most of the time you will probably be focusing mostly on locals, as they represent the high level state of your program. - -You might be interested in inspecting the witness map in case you are trying to solve a really low level issue in the compiler or runtime itself, so this concerns mostly advanced or niche users. - -Let's step through the program, by using the debugger buttons or their corresponding keyboard shortcuts. - -![Debugger buttons](@site/static/img/debugger/4-debugger-buttons.png) - -Now we can see in the variables pane that there's values for `digest`, `result` and `x`. - -![Inspecting locals](@site/static/img/debugger/5-assert.png) - -We can also inspect the values of variables by directly hovering on them on the code. - -![Hover locals](@site/static/img/debugger/6-hover.png) - -Let's set a break point at the `keccak256` function, so we can continue execution up to the point when it's first invoked without having to go one step at a time. - -We just need to click the to the right of the line number 18. Once the breakpoint appears, we can click the `continue` button or use its corresponding keyboard shortcut (`F5` by default). - -![Breakpoint](@site/static/img/debugger/7-break.png) - -Now we are debugging the `keccak256` function, notice the _Call Stack pane_ at the lower right. This lets us inspect the current call stack of our process. - -That covers most of the current debugger functionalities. Check out [the reference](../../reference/debugger/debugger_vscode.md) for more details on how to configure the debugger. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.35.0/how_to/how-to-oracles.md b/docs/versioned_docs/version-v0.35.0/how_to/how-to-oracles.md deleted file mode 100644 index 392dd8b452e..00000000000 --- a/docs/versioned_docs/version-v0.35.0/how_to/how-to-oracles.md +++ /dev/null @@ -1,275 +0,0 @@ ---- -title: How to use Oracles -description: Learn how to use oracles in your Noir program with examples in both Nargo and NoirJS. This guide also covers writing a JSON RPC server and providing custom foreign call handlers for NoirJS. -keywords: - - Noir Programming - - Oracles - - Nargo - - NoirJS - - JSON RPC Server - - Foreign Call Handlers -sidebar_position: 1 ---- - -This guide shows you how to use oracles in your Noir program. For the sake of clarity, it assumes that: - -- You have read the [explainer on Oracles](../explainers/explainer-oracle.md) and are comfortable with the concept. -- You have a Noir program to add oracles to. You can create one using the [vite-hardhat starter](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat) as a boilerplate. -- You understand the concept of a JSON-RPC server. Visit the [JSON-RPC website](https://www.jsonrpc.org/) if you need a refresher. -- You are comfortable with server-side JavaScript (e.g. Node.js, managing packages, etc.). - -## Rundown - -This guide has 3 major steps: - -1. How to modify our Noir program to make use of oracle calls as unconstrained functions -2. How to write a JSON RPC Server to resolve these oracle calls with Nargo -3. How to use them in Nargo and how to provide a custom resolver in NoirJS - -## Step 1 - Modify your Noir program - -An oracle is defined in a Noir program by defining two methods: - -- An unconstrained method - This tells the compiler that it is executing an [unconstrained functions](../noir/concepts//unconstrained.md). -- A decorated oracle method - This tells the compiler that this method is an RPC call. - -An example of an oracle that returns a `Field` would be: - -```rust -#[oracle(getSqrt)] -unconstrained fn sqrt(number: Field) -> Field { } - -unconstrained fn get_sqrt(number: Field) -> Field { - sqrt(number) -} -``` - -In this example, we're wrapping our oracle function in an unconstrained method, and decorating it with `oracle(getSqrt)`. We can then call the unconstrained function as we would call any other function: - -```rust -fn main(input: Field) { - let sqrt = get_sqrt(input); -} -``` - -In the next section, we will make this `getSqrt` (defined on the `sqrt` decorator) be a method of the RPC server Noir will use. - -:::danger - -As explained in the [Oracle Explainer](../explainers/explainer-oracle.md), this `main` function is unsafe unless you constrain its return value. For example: - -```rust -fn main(input: Field) { - let sqrt = get_sqrt(input); - assert(sqrt.pow_32(2) as u64 == input as u64); // <---- constrain the return of an oracle! -} -``` - -::: - -:::info - -Currently, oracles only work with single params or array params. For example: - -```rust -#[oracle(getSqrt)] -unconstrained fn sqrt([Field; 2]) -> [Field; 2] { } -``` - -::: - -## Step 2 - Write an RPC server - -Brillig will call *one* RPC server. Most likely you will have to write your own, and you can do it in whatever language you prefer. In this guide, we will do it in Javascript. - -Let's use the above example of an oracle that consumes an array with two `Field` and returns their square roots: - -```rust -#[oracle(getSqrt)] -unconstrained fn sqrt(input: [Field; 2]) -> [Field; 2] { } - -unconstrained fn get_sqrt(input: [Field; 2]) -> [Field; 2] { - sqrt(input) -} - -fn main(input: [Field; 2]) { - let sqrt = get_sqrt(input); - assert(sqrt[0].pow_32(2) as u64 == input[0] as u64); - assert(sqrt[1].pow_32(2) as u64 == input[1] as u64); -} - -#[test] -fn test() { - let input = [4, 16]; - main(input); -} -``` - -:::info - -Why square root? - -In general, computing square roots is computationally more expensive than multiplications, which takes a toll when speaking about ZK applications. In this case, instead of calculating the square root in Noir, we are using our oracle to offload that computation to be made in plain. In our circuit we can simply multiply the two values. - -::: - -Now, we should write the correspondent RPC server, starting with the [default JSON-RPC 2.0 boilerplate](https://www.npmjs.com/package/json-rpc-2.0#example): - -```js -import { JSONRPCServer } from "json-rpc-2.0"; -import express from "express"; -import bodyParser from "body-parser"; - -const app = express(); -app.use(bodyParser.json()); - -const server = new JSONRPCServer(); -app.post("/", (req, res) => { - const jsonRPCRequest = req.body; - server.receive(jsonRPCRequest).then((jsonRPCResponse) => { - if (jsonRPCResponse) { - res.json(jsonRPCResponse); - } else { - res.sendStatus(204); - } - }); -}); - -app.listen(5555); -``` - -Now, we will add our `getSqrt` method, as expected by the `#[oracle(getSqrt)]` decorator in our Noir code. It maps through the params array and returns their square roots: - -```js -server.addMethod("resolve_foreign_call", async (params) => { - if (params[0].function !== "getSqrt") { - throw Error("Unexpected foreign call") - }; - const values = params[0].inputs[0].map((field) => { - return `${Math.sqrt(parseInt(field, 16))}`; - }); - return { values: [values] }; -}); -``` - -If you're using Typescript, the following types may be helpful in understanding the expected return value and making sure they're easy to follow: - -```js -export type ForeignCallSingle = string; - -export type ForeignCallArray = string[]; - -export type ForeignCallResult = { - values: (ForeignCallSingle | ForeignCallArray)[]; -}; -``` - -:::info Multidimensional Arrays - -If the Oracle function is returning an array containing other arrays, such as `[['1','2],['3','4']]`, you need to provide the values in JSON as flattened values. In the previous example, it would be `['1', '2', '3', '4']`. In the Noir program, the Oracle signature can use a nested type, the flattened values will be automatically converted to the nested type. - -::: - -## Step 3 - Usage with Nargo - -Using the [`nargo` CLI tool](../getting_started/installation/index.md), you can use oracles in the `nargo test` and `nargo execute` commands by passing a value to `--oracle-resolver`. For example: - -```bash -nargo test --oracle-resolver http://localhost:5555 -``` - -This tells `nargo` to use your RPC Server URL whenever it finds an oracle decorator. - -## Step 4 - Usage with NoirJS - -In a JS environment, an RPC server is not strictly necessary, as you may want to resolve your oracles without needing any JSON call at all. NoirJS simply expects that you pass a callback function when you generate proofs, and that callback function can be anything. - -For example, if your Noir program expects the host machine to provide CPU pseudo-randomness, you could simply pass it as the `foreignCallHandler`. You don't strictly need to create an RPC server to serve pseudo-randomness, as you may as well get it directly in your app: - -```js -const foreignCallHandler = (name, inputs) => crypto.randomBytes(16) // etc - -await noir.execute(inputs, foreignCallHandler) -``` - -As one can see, in NoirJS, the [`foreignCallHandler`](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) function simply means "a callback function that returns a value of type [`ForeignCallOutput`](../reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md). It doesn't have to be an RPC call like in the case for Nargo. - -:::tip - -Does this mean you don't have to write an RPC server like in [Step #2](#step-2---write-an-rpc-server)? - -You don't technically have to, but then how would you run `nargo test`? To use both `Nargo` and `NoirJS` in your development flow, you will have to write a JSON RPC server. - -::: - -In this case, let's make `foreignCallHandler` call the JSON RPC Server we created in [Step #2](#step-2---write-an-rpc-server), by making it a JSON RPC Client. - -For example, using the same `getSqrt` program in [Step #1](#step-1---modify-your-noir-program) (comments in the code): - -```js -import { JSONRPCClient } from "json-rpc-2.0"; - -// declaring the JSONRPCClient -const client = new JSONRPCClient((jsonRPCRequest) => { -// hitting the same JSON RPC Server we coded above - return fetch("http://localhost:5555", { - method: "POST", - headers: { - "content-type": "application/json", - }, - body: JSON.stringify(jsonRPCRequest), - }).then((response) => { - if (response.status === 200) { - return response - .json() - .then((jsonRPCResponse) => client.receive(jsonRPCResponse)); - } else if (jsonRPCRequest.id !== undefined) { - return Promise.reject(new Error(response.statusText)); - } - }); -}); - -// declaring a function that takes the name of the foreign call (getSqrt) and the inputs -const foreignCallHandler = async (name, input) => { - const inputs = input[0].map((i) => i.toString("hex")) - // notice that the "inputs" parameter contains *all* the inputs - // in this case we to make the RPC request with the first parameter "numbers", which would be input[0] - const oracleReturn = await client.request("resolve_foreign_call", [ - { - function: name, - inputs: [inputs] - }, - ]); - return [oracleReturn.values[0]]; -}; - -// the rest of your NoirJS code -const input = { input: [4, 16] }; -const { witness } = await noir.execute(input, foreignCallHandler); -``` - -:::tip - -If you're in a NoirJS environment running your RPC server together with a frontend app, you'll probably hit a familiar problem in full-stack development: requests being blocked by [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) policy. For development only, you can simply install and use the [`cors` npm package](https://www.npmjs.com/package/cors) to get around the problem: - -```bash -yarn add cors -``` - -and use it as a middleware: - -```js -import cors from "cors"; - -const app = express(); -app.use(cors()) -``` - -::: - -## Conclusion - -Hopefully by the end of this guide, you should be able to: - -- Write your own logic around Oracles and how to write a JSON RPC server to make them work with your Nargo commands. -- Provide custom foreign call handlers for NoirJS. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.35.0/how_to/how-to-recursion.md b/docs/versioned_docs/version-v0.35.0/how_to/how-to-recursion.md deleted file mode 100644 index c8c4dc9f5b4..00000000000 --- a/docs/versioned_docs/version-v0.35.0/how_to/how-to-recursion.md +++ /dev/null @@ -1,180 +0,0 @@ ---- -title: How to use recursion on NoirJS -description: Learn how to implement recursion with NoirJS, a powerful tool for creating smart contracts on the EVM blockchain. This guide assumes familiarity with NoirJS, solidity verifiers, and the Barretenberg proving backend. Discover how to generate both final and intermediate proofs using `noir_js` and `backend_barretenberg`. -keywords: - [ - "NoirJS", - "EVM blockchain", - "smart contracts", - "recursion", - "solidity verifiers", - "Barretenberg backend", - "noir_js", - "backend_barretenberg", - "intermediate proofs", - "final proofs", - "nargo compile", - "json import", - "recursive circuit", - "recursive app" - ] -sidebar_position: 1 ---- - -This guide shows you how to use recursive proofs in your NoirJS app. For the sake of clarity, it is assumed that: - -- You already have a NoirJS app. If you don't, please visit the [NoirJS tutorial](../tutorials/noirjs_app.md) and the [reference](../reference/NoirJS/noir_js/index.md). -- You are familiar with what are recursive proofs and you have read the [recursion explainer](../explainers/explainer-recursion.md) -- You already built a recursive circuit following [the reference](../noir/standard_library/recursion.mdx), and understand how it works. - -It is also assumed that you're not using `noir_wasm` for compilation, and instead you've used [`nargo compile`](../reference/nargo_commands.md) to generate the `json` you're now importing into your project. However, the guide should work just the same if you're using `noir_wasm`. - -:::info - -As you've read in the [explainer](../explainers/explainer-recursion.md), a recursive proof is an intermediate proof. This means that it doesn't necessarily generate the final step that makes it verifiable in a smart contract. However, it is easy to verify within another circuit. - -While "standard" usage of NoirJS packages abstracts final proofs, it currently lacks the necessary interface to abstract away intermediate proofs. This means that these proofs need to be created by using the backend directly. - -In short: - -- `noir_js` generates *only* final proofs -- `backend_barretenberg` generates both types of proofs - -::: - -In a standard recursive app, you're also dealing with at least two circuits. For the purpose of this guide, we will assume the following: - -- `main`: a circuit of type `assert(x != y)`, where `main` is marked with a `#[recursive]` attribute. This attribute states that the backend should generate proofs that are friendly for verification within another circuit. -- `recursive`: a circuit that verifies `main` - -For a full example of how recursive proofs work, please refer to the [noir-examples](https://github.com/noir-lang/noir-examples) repository. We will *not* be using it as a reference for this guide. - -## Step 1: Setup - -In a common NoirJS app, you need to instantiate a backend with something like `const backend = new Backend(circuit)`. Then you feed it to the `noir_js` interface. - -For recursion, this doesn't happen, and the only need for `noir_js` is only to `execute` a circuit and get its witness and return value. Everything else is not interfaced, so it needs to happen on the `backend` object. - -It is also recommended that you instantiate the backend with as many threads as possible, to allow for maximum concurrency: - -```js -const backend = new Backend(circuit, { threads: 8 }) -``` - -:::tip -You can use the [`os.cpus()`](https://nodejs.org/api/os.html#oscpus) object in `nodejs` or [`navigator.hardwareConcurrency`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/hardwareConcurrency) on the browser to make the most out of those glorious cpu cores -::: - -## Step 2: Generating the witness and the proof for `main` - -After instantiating the backend, you should also instantiate `noir_js`. We will use it to execute the circuit and get the witness. - -```js -const noir = new Noir(circuit) -const { witness } = noir.execute(input) -``` - -With this witness, you are now able to generate the intermediate proof for the main circuit: - -```js -const { proof, publicInputs } = await backend.generateProof(witness) -``` - -:::warning - -Always keep in mind what is actually happening on your development process, otherwise you'll quickly become confused about what circuit we are actually running and why! - -In this case, you can imagine that Alice (running the `main` circuit) is proving something to Bob (running the `recursive` circuit), and Bob is verifying her proof within his proof. - -With this in mind, it becomes clear that our intermediate proof is the one *meant to be verified within another circuit*, so it must be Alice's. Actually, the only final proof in this theoretical scenario would be the last one, sent on-chain. - -::: - -## Step 3 - Verification and proof artifacts - -Optionally, you are able to verify the intermediate proof: - -```js -const verified = await backend.verifyProof({ proof, publicInputs }) -``` - -This can be useful to make sure our intermediate proof was correctly generated. But the real goal is to do it within another circuit. For that, we need to generate recursive proof artifacts that will be passed to the circuit that is verifying the proof we just generated. Instead of passing the proof and verification key as a byte array, we pass them as fields which makes it cheaper to verify in a circuit: - -```js -const { proofAsFields, vkAsFields, vkHash } = await backend.generateRecursiveProofArtifacts( { publicInputs, proof }, publicInputsCount) -``` - -This call takes the public inputs and the proof, but also the public inputs count. While this is easily retrievable by simply counting the `publicInputs` length, the backend interface doesn't currently abstract it away. - -:::info - -The `proofAsFields` has a constant size `[Field; 93]` and verification keys in Barretenberg are always `[Field; 114]`. - -::: - -:::warning - -One common mistake is to forget *who* makes this call. - -In a situation where Alice is generating the `main` proof, if she generates the proof artifacts and sends them to Bob, which gladly takes them as true, this would mean Alice could prove anything! - -Instead, Bob needs to make sure *he* extracts the proof artifacts, using his own instance of the `main` circuit backend. This way, Alice has to provide a valid proof for the correct `main` circuit. - -::: - -## Step 4 - Recursive proof generation - -With the artifacts, generating a recursive proof is no different from a normal proof. You simply use the `backend` (with the recursive circuit) to generate it: - -```js -const recursiveInputs = { - verification_key: vkAsFields, // array of length 114 - proof: proofAsFields, // array of length 93 + size of public inputs - publicInputs: [mainInput.y], // using the example above, where `y` is the only public input - key_hash: vkHash, -} - -const { witness, returnValue } = noir.execute(recursiveInputs) // we're executing the recursive circuit now! -const { proof, publicInputs } = backend.generateProof(witness) -const verified = backend.verifyProof({ proof, publicInputs }) -``` - -You can obviously chain this proof into another proof. In fact, if you're using recursive proofs, you're probably interested of using them this way! - -:::tip - -Managing circuits and "who does what" can be confusing. To make sure your naming is consistent, you can keep them in an object. For example: - -```js -const circuits = { - main: mainJSON, - recursive: recursiveJSON -} -const backends = { - main: new BarretenbergBackend(circuits.main), - recursive: new BarretenbergBackend(circuits.recursive) -} -const noir_programs = { - main: new Noir(circuits.main), - recursive: new Noir(circuits.recursive) -} -``` - -This allows you to neatly call exactly the method you want without conflicting names: - -```js -// Alice runs this 👇 -const { witness: mainWitness } = await noir_programs.main.execute(input) -const proof = await backends.main.generateProof(mainWitness) - -// Bob runs this 👇 -const verified = await backends.main.verifyProof(proof) -const { proofAsFields, vkAsFields, vkHash } = await backends.main.generateRecursiveProofArtifacts( - proof, - numPublicInputs, -); -const { witness: recursiveWitness } = await noir_programs.recursive.execute(recursiveInputs) -const recursiveProof = await backends.recursive.generateProof(recursiveWitness); -``` - -::: diff --git a/docs/versioned_docs/version-v0.35.0/how_to/how-to-solidity-verifier.md b/docs/versioned_docs/version-v0.35.0/how_to/how-to-solidity-verifier.md deleted file mode 100644 index a8169595b3d..00000000000 --- a/docs/versioned_docs/version-v0.35.0/how_to/how-to-solidity-verifier.md +++ /dev/null @@ -1,259 +0,0 @@ ---- -title: Generate a 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, - ] -sidebar_position: 0 -pagination_next: tutorials/noirjs_app ---- - -Noir has the ability to generate a verifier contract in Solidity, which can be deployed in many EVM-compatible blockchains such as Ethereum. - -This allows for a powerful feature set, as one can make use of the conciseness and the privacy provided by Noir in an immutable ledger. Applications can range from simple P2P guessing games, to complex private DeFi interactions. - -This guide shows you how to generate a Solidity Verifier and deploy it on the [Remix IDE](https://remix.ethereum.org/). It is assumed that: - -- You are comfortable with the Solidity programming language and understand how contracts are deployed on the Ethereum network -- You have Noir installed and you have a Noir program. If you don't, [get started](../getting_started/installation/index.md) with Nargo and the example Hello Noir circuit -- You are comfortable navigating RemixIDE. If you aren't or you need a refresher, you can find some video tutorials [here](https://www.youtube.com/channel/UCjTUPyFEr2xDGN6Cg8nKDaA) that could help you. - -## Rundown - -Generating a Solidity Verifier contract is actually a one-command process. However, compiling it and deploying it can have some caveats. Here's the rundown of this guide: - -1. How to generate a solidity smart contract -2. How to compile the smart contract in the RemixIDE -3. How to deploy it to a testnet - -## Step 1 - Generate a contract - -This is by far the most straightforward step. Just run: - -```sh -nargo compile -``` - -This will compile your source code into a Noir build artifact to be stored in the `./target` directory, you can then generate the smart contract using the commands: - -```sh -# Here we pass the path to the newly generated Noir artifact. -bb write_vk -b ./target/.json -bb contract -``` - -replacing `` with the name of your Noir project. A new `contract` folder would then be generated in your project directory, containing the Solidity -file `contract.sol`. It can be deployed to any EVM blockchain acting as a verifier smart contract. - -You can find more information about `bb` and the default Noir proving backend on [this page](../getting_started/hello_noir/index.md#proving-backend). - -:::info - -It is possible to generate 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 generation of verifier contracts, for the time being these are only in Solidity. -::: - -## Step 2 - Compiling - -We will mostly skip the details of RemixIDE, as the UI can change from version to version. For now, we can just open -Remix and create a blank workspace. - -![Create Workspace](@site/static/img/how-tos/solidity_verifier_1.png) - -We will create a new file to contain the contract Nargo generated, and copy-paste its content. - -:::warning - -You'll likely see a warning advising you to not trust pasted code. While it is an important warning, it is irrelevant in the context of this guide and can be ignored. We will not be deploying anywhere near a mainnet. - -::: - -To compile our the verifier, we can navigate to the compilation tab: - -![Compilation Tab](@site/static/img/how-tos/solidity_verifier_2.png) - -Remix should automatically match a suitable compiler version. However, hitting the "Compile" button will most likely generate a "Stack too deep" error: - -![Stack too deep](@site/static/img/how-tos/solidity_verifier_3.png) - -This is due to the verify function needing to put many variables on the stack, but enabling the optimizer resolves the issue. To do this, let's open the "Advanced Configurations" tab and enable optimization. The default 200 runs will suffice. - -:::info - -This time we will see a warning about an unused function parameter. This is expected, as the `verify` function doesn't use the `_proof` parameter inside a solidity block, it is loaded from calldata and used in assembly. - -::: - -![Compilation success](@site/static/img/how-tos/solidity_verifier_4.png) - -## Step 3 - Deploying - -At this point we should have a compiled contract ready to deploy. If we navigate to the deploy section in Remix, we will see many different environments we can deploy to. The steps to deploy on each environment would be out-of-scope for this guide, so we will just use the default Remix VM. - -Looking closely, we will notice that our "Solidity Verifier" is actually three contracts working together: - -- An `UltraVerificationKey` library which simply stores the verification key for our circuit. -- An abstract contract `BaseUltraVerifier` containing most of the verifying logic. -- A main `UltraVerifier` contract that inherits from the Base and uses the Key contract. - -Remix will take care of the dependencies for us so we can simply deploy the UltraVerifier contract by selecting it and hitting "deploy": - -![Deploying UltraVerifier](@site/static/img/how-tos/solidity_verifier_5.png) - -A contract will show up in the "Deployed Contracts" section, where we can retrieve the Verification Key Hash. This is particularly useful for double-checking that the deployer contract is the correct one. - -:::note - -Why "UltraVerifier"? - -To be precise, the Noir compiler (`nargo`) doesn't generate the verifier contract directly. It compiles the Noir code into an intermediate language (ACIR), which is then executed by the backend. So it is the backend that returns the verifier smart contract, not Noir. - -In this case, the Barretenberg Backend uses the UltraPlonk proving system, hence the "UltraVerifier" name. - -::: - -## Step 4 - Verifying - -To verify a proof using the Solidity verifier contract, we call the `verify` function in this extended contract: - -```solidity -function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external view returns (bool) -``` - -When using the default example in the [Hello Noir](../getting_started/hello_noir/index.md) guide, the easiest way to confirm that the verifier contract is doing its job is by calling the `verify` function via remix with the required parameters. Note that the public inputs must be passed in separately to the rest of the proof so we must split the proof as returned from `bb`. - -First generate a proof with `bb` at the location `./proof` using the steps in [get started](../getting_started/hello_noir/index.md), this proof is in a binary format but we want to convert it into a hex string to pass into Remix, this can be done with the - -```bash -# This value must be changed to match the number of public inputs (including return values!) in your program. -NUM_PUBLIC_INPUTS=1 -PUBLIC_INPUT_BYTES=32*NUM_PUBLIC_INPUTS -HEX_PUBLIC_INPUTS=$(head -c $PUBLIC_INPUT_BYTES ./proof | od -An -v -t x1 | tr -d $' \n') -HEX_PROOF=$(tail -c +$(($PUBLIC_INPUT_BYTES + 1)) ./proof | od -An -v -t x1 | tr -d $' \n') - -echo "Public inputs:" -echo $HEX_PUBLIC_INPUTS - -echo "Proof:" -echo "0x$HEX_PROOF" -``` - -Remix expects that the public inputs will be split into an array of `bytes32` values so `HEX_PUBLIC_INPUTS` needs to be split up into 32 byte chunks which are prefixed with `0x` accordingly. - -A programmatic example of how the `verify` function is called can be seen 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"); -``` - -:::info[Return Values] - -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. - -For example, if you have Noir program like this: - -```rust -fn main( - // Public inputs - pubkey_x: pub Field, - pubkey_y: pub Field, - // Private inputs - priv_key: Field, -) -> pub Field -``` - -the `verify` function will expect the public inputs array (second function parameter) to be of length 3, the two inputs and the return value. - -Passing only two inputs will result in an error such as `PUBLIC_INPUT_COUNT_INVALID(3, 2)`. - -In this case, the inputs parameter to `verify` would be an array ordered as `[pubkey_x, pubkey_y, return`. - -::: - -:::tip[Structs] - -You can pass structs to the verifier contract. They will be flattened so that the array of inputs is 1-dimensional array. - -For example, 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) { - //... -} -``` - -The order of these inputs would be flattened to: `[x, nested.t1.val1, nested.t1.val2, nested.is_true, y]` - -::: - -The other function you can call is our entrypoint `verify` function, as defined above. - -:::tip - -It's worth noticing that the `verify` function is actually a `view` function. A `view` function does not alter the blockchain state, so it doesn't need to be distributed (i.e. it will run only on the executing node), and therefore doesn't cost any gas. - -This can be particularly useful in some situations. If Alice generated a proof and wants Bob to verify its correctness, Bob doesn't need to run Nargo, NoirJS, or any Noir specific infrastructure. He can simply make a call to the blockchain with the proof and verify it is correct without paying any gas. - -It would be incorrect to say that a Noir proof verification costs any gas at all. However, most of the time the result of `verify` is used to modify state (for example, to update a balance, a game state, etc). In that case the whole network needs to execute it, which does incur gas costs (calldata and execution, but not storage). - -::: - -## A Note on EVM chains - -Noir proof verification requires the ecMul, ecAdd and ecPairing precompiles. Not all EVM chains support EC Pairings, notably some of the ZK-EVMs. This means that you won't be able to use the verifier contract in all of them. You can find an incomplete list of which EVM chains support these precompiles [here](https://www.evmdiff.com/features?feature=precompiles). - -For example, chains like `zkSync ERA` and `Polygon zkEVM` do not currently support these precompiles, so proof verification via Solidity verifier contracts won't work. Here's a quick list of EVM chains that have been tested and are known to work: - -- Optimism -- Arbitrum -- Polygon PoS -- Scroll -- Celo -- BSC -- Blast L2 -- Avalanche C-Chain -- Mode -- Linea -- Moonbeam - -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. - -## What's next - -Now that you know how to call a Noir Solidity Verifier on a smart contract using Remix, you should be comfortable with using it with some programmatic frameworks, such as [hardhat](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat) and [foundry](https://github.com/noir-lang/noir-starter/tree/main/with-foundry). - -You can find other tools, examples, boilerplates and libraries in the [awesome-noir](https://github.com/noir-lang/awesome-noir) repository. - -You should also be ready to write and deploy your first NoirJS app and start generating proofs on websites, phones, and NodeJS environments! Head on to the [NoirJS tutorial](../tutorials/noirjs_app.md) to learn how to do that. diff --git a/docs/versioned_docs/version-v0.35.0/how_to/merkle-proof.mdx b/docs/versioned_docs/version-v0.35.0/how_to/merkle-proof.mdx deleted file mode 100644 index 0a128adb2de..00000000000 --- a/docs/versioned_docs/version-v0.35.0/how_to/merkle-proof.mdx +++ /dev/null @@ -1,48 +0,0 @@ ---- -title: Prove Merkle Tree 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] -sidebar_position: 4 ---- - -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 - -fn main(message : [Field; 62], index : Field, hashpath : [Field; 40], root : Field) { - let leaf = std::hash::hash_to_field(message.as_slice()); - 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.as_slice()); -``` - -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.35.0/how_to/using-devcontainers.mdx b/docs/versioned_docs/version-v0.35.0/how_to/using-devcontainers.mdx deleted file mode 100644 index 727ec6ca667..00000000000 --- a/docs/versioned_docs/version-v0.35.0/how_to/using-devcontainers.mdx +++ /dev/null @@ -1,110 +0,0 @@ ---- -title: Developer Containers and Codespaces -description: "Learn how to set up a devcontainer in your GitHub repository for a seamless coding experience with Codespaces. Follow our easy 8-step guide to create your own Noir environment without installing Nargo locally." -keywords: ["Devcontainer", "Codespaces", "GitHub", "Noir Environment", "Docker Image", "Development Environment", "Remote Coding", "GitHub Codespaces", "Noir Programming", "Nargo", "VSCode Extensions", "Noirup"] -sidebar_position: 1 ---- - -Adding a developer container configuration file to your Noir project is one of the easiest way to unlock coding in browser. - -## What's a devcontainer after all? - -A [Developer Container](https://containers.dev/) (devcontainer for short) is a Docker image that comes preloaded with tools, extensions, and other tools you need to quickly get started or continue a project, without having to install Nargo locally. Think of it as a development environment in a box. - -There are many advantages to this: - -- It's platform and architecture agnostic -- You don't need to have an IDE installed, or Nargo, or use a terminal at all -- It's safer for using on a public machine or public network - -One of the best ways of using devcontainers is... not using your machine at all, for maximum control, performance, and ease of use. -Enter Codespaces. - -## Codespaces - -If a devcontainer is just a Docker image, then what stops you from provisioning a `p3dn.24xlarge` AWS EC2 instance with 92 vCPUs and 768 GiB RAM and using it to prove your 10-gate SNARK proof? - -Nothing! Except perhaps the 30-40$ per hour it will cost you. - -The problem is that provisioning takes time, and I bet you don't want to see the AWS console every time you want to code something real quick. - -Fortunately, there's an easy and free way to get a decent remote machine ready and loaded in less than 2 minutes: Codespaces. [Codespaces is a Github feature](https://github.com/features/codespaces) that allows you to code in a remote machine by using devcontainers, and it's pretty cool: - -- You can start coding Noir in less than a minute -- It uses the resources of a remote machine, so you can code on your grandma's phone if needed be -- It makes it easy to share work with your frens -- It's fully reusable, you can stop and restart whenever you need to - -:::info - -Don't take out your wallet just yet. Free GitHub accounts get about [15-60 hours of coding](https://github.com/features/codespaces) for free per month, depending on the size of your provisioned machine. - -::: - -## Tell me it's _actually_ easy - -It is! - -Github comes with a default codespace and you can use it to code your own devcontainer. That's exactly what we will be doing in this guide. - - - -8 simple steps: - -#### 1. Create a new repository on GitHub. - -#### 2. Click "Start coding with Codespaces". This will use the default image. - -#### 3. Create a folder called `.devcontainer` in the root of your repository. - -#### 4. Create a Dockerfile in that folder, and paste the following code: - -```docker -FROM --platform=linux/amd64 node:lts-bookworm-slim -SHELL ["/bin/bash", "-c"] -RUN apt update && apt install -y curl bash git tar gzip libc++-dev -RUN curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash -ENV PATH="/root/.nargo/bin:$PATH" -RUN noirup -ENTRYPOINT ["nargo"] -``` -#### 5. Create a file called `devcontainer.json` in the same folder, and paste the following code: - -```json -{ - "name": "Noir on Codespaces", - "build": { - "context": ".", - "dockerfile": "Dockerfile" - }, - "customizations": { - "vscode": { - "extensions": ["noir-lang.vscode-noir"] - } - } -} -``` -#### 6. Commit and push your changes - -This will pull the new image and build it, so it could take a minute or so - -#### 8. Done! -Just wait for the build to finish, and there's your easy Noir environment. - - -Refer to [noir-starter](https://github.com/noir-lang/noir-starter/) as an example of how devcontainers can be used together with codespaces. - - - -## How do I use it? - -Using the codespace is obviously much easier than setting it up. -Just navigate to your repository and click "Code" -> "Open with Codespaces". It should take a few seconds to load, and you're ready to go. - -:::info - -If you really like the experience, you can add a badge to your readme, links to existing codespaces, and more. -Check out the [official docs](https://docs.github.com/en/codespaces/setting-up-your-project-for-codespaces/setting-up-your-repository/facilitating-quick-creation-and-resumption-of-codespaces) for more info. diff --git a/docs/versioned_docs/version-v0.35.0/index.mdx b/docs/versioned_docs/version-v0.35.0/index.mdx deleted file mode 100644 index a6bd306f91d..00000000000 --- a/docs/versioned_docs/version-v0.35.0/index.mdx +++ /dev/null @@ -1,67 +0,0 @@ ---- -title: Noir Lang -hide_title: true -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] -sidebar_position: 0 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -Noir Logo - -Noir is an open-source Domain-Specific Language for safe and seamless construction of privacy-preserving Zero-Knowledge programs, requiring no previous knowledge on the underlying mathematics or cryptography. - -ZK programs are programs that can generate short proofs of statements without revealing all inputs to the statements. You can read more about Zero-Knowledge Proofs [here](https://dev.to/spalladino/a-beginners-intro-to-coding-zero-knowledge-proofs-c56). - -## What's new about Noir? - -Noir works differently from most ZK languages by taking a two-pronged path. First, it compiles the program to an adaptable intermediate language known as ACIR. From there, depending on a given project's needs, ACIR can be further compiled into an arithmetic circuit for integration with the proving backend. - -:::info - -Noir is backend agnostic, which means it makes no assumptions on which proving backend powers the ZK proof. Being the language that powers [Aztec Contracts](https://docs.aztec.network/developers/contracts/main), it defaults to Aztec's Barretenberg proving backend. - -However, the ACIR output can be transformed to be compatible with other PLONK-based backends, or into a [rank-1 constraint system](https://www.rareskills.io/post/rank-1-constraint-system) suitable for backends such as Arkwork's Marlin. - -::: - -## Who is Noir for? - -Noir can be used both in complex cloud-based backends and in user's smartphones, requiring no knowledge on the underlying math or cryptography. From authorization systems that keep a password in the user's device, to complex on-chain verification of recursive proofs, Noir is designed to abstract away complexity without any significant overhead. Here are some examples of situations where Noir can be used: - - - - Noir Logo - - Aztec Contracts leverage Noir to allow for the storage and execution of private information. Writing an Aztec Contract is as easy as writing Noir, and Aztec developers can easily interact with the network storage and execution through the [Aztec.nr](https://docs.aztec.network/developers/contracts/main) library. - - - Soliditry Verifier Example - Noir can auto-generate Solidity verifier contracts that verify Noir proofs. This allows for non-interactive verification of proofs containing private information in an immutable system. This feature powers a multitude of use-case scenarios, from P2P chess tournaments, to [Aztec Layer-2 Blockchain](https://docs.aztec.network/) - - - Aztec Labs developed NoirJS, an easy interface to generate and verify Noir proofs in a Javascript environment. This allows for Noir to be used in webpages, mobile apps, games, and any other environment supporting JS execution in a standalone manner. - - - - -## Libraries - -Noir is meant to be easy to extend by simply importing Noir libraries just like in Rust. -The [awesome-noir repo](https://github.com/noir-lang/awesome-noir#libraries) is a collection of libraries developed by the Noir community. -Writing a new library is easy and makes code be composable and easy to reuse. See the section on [dependencies](noir/modules_packages_crates/dependencies.md) for more information. diff --git a/docs/versioned_docs/version-v0.35.0/migration_notes.md b/docs/versioned_docs/version-v0.35.0/migration_notes.md deleted file mode 100644 index 6bd740024e5..00000000000 --- a/docs/versioned_docs/version-v0.35.0/migration_notes.md +++ /dev/null @@ -1,105 +0,0 @@ ---- -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. - -### `backend encountered an error: libc++.so.1` - -Depending on your OS, you may encounter the following error when running `nargo prove` for the first time: - -```text -The backend encountered an error: "/home/codespace/.nargo/backends/acvm-backend-barretenberg/backend_binary: error while loading shared libraries: libc++.so.1: cannot open shared object file: No such file or directory\n" -``` - -Install the `libc++-dev` library with: - -```bash -sudo apt install libc++-dev -``` - -## ≥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](noir/concepts/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 your 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 file is running [this bash script](https://github.com/noir-lang/barretenberg-js-binary/blob/master/run-bb-js.sh), 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.35.0/noir/concepts/_category_.json b/docs/versioned_docs/version-v0.35.0/noir/concepts/_category_.json deleted file mode 100644 index 7da08f8a8c5..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/concepts/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "Concepts", - "position": 0, - "collapsible": true, - "collapsed": true -} \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.35.0/noir/concepts/assert.md b/docs/versioned_docs/version-v0.35.0/noir/concepts/assert.md deleted file mode 100644 index 2132de42072..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/concepts/assert.md +++ /dev/null @@ -1,78 +0,0 @@ ---- -title: Assert Function -description: - Learn about the `assert` and `static_assert` functions 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 or compile-time, respectively. -keywords: [Noir programming language, assert statement, predicate expression, comparison expression] -sidebar_position: 4 ---- - -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); -} -``` - -> 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`. - -You can optionally provide a message to be logged when the assertion fails: - -```rust -assert(x == y, "x and y are not equal"); -``` - -Aside string literals, the optional message can be a format string or any other type supported as input for Noir's [print](../standard_library/logging.md) functions. This feature lets you incorporate runtime variables into your failed assertion logs: - -```rust -assert(x == y, f"Expected x == y, but got {x} == {y}"); -``` - -Using a variable as an assertion message directly: - -```rust -struct myStruct { - myField: Field -} - -let s = myStruct { myField: y }; -assert(s.myField == x, s); -``` - -There is also a special `static_assert` function that behaves like `assert`, -but that runs at compile-time. - -```rust -fn main(xs: [Field; 3]) { - let x = 2 + 2; - let y = 4; - static_assert(x == y, "expected 2 + 2 to equal 4"); - - // This passes since the length of `xs` is known at compile-time - static_assert(xs.len() == 3, "expected the input to have 3 elements"); -} -``` - -This function fails when passed a dynamic (run-time) argument: - -```rust -fn main(x : Field, y : Field) { - // this fails because `x` is not known at compile-time - static_assert(x == 2, "expected x to be known at compile-time and equal to 2"); - - let mut example_slice = &[]; - if y == 4 { - example_slice = example_slice.push_back(0); - } - - // This fails because the length of `example_slice` is not known at - // compile-time - let error_message = "expected an empty slice, known at compile-time"; - static_assert(example_slice.len() == 0, error_message); -} -``` - diff --git a/docs/versioned_docs/version-v0.35.0/noir/concepts/comments.md b/docs/versioned_docs/version-v0.35.0/noir/concepts/comments.md deleted file mode 100644 index b51a85f5c94..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/concepts/comments.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -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] -sidebar_position: 10 ---- - -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.35.0/noir/concepts/comptime.md b/docs/versioned_docs/version-v0.35.0/noir/concepts/comptime.md deleted file mode 100644 index 50d7e686083..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/concepts/comptime.md +++ /dev/null @@ -1,440 +0,0 @@ ---- -title: Compile-time Code & Metaprogramming -description: Learn how to use metaprogramming in Noir to create macros or derive your own traits -keywords: [Noir, comptime, compile-time, metaprogramming, macros, quote, unquote] -sidebar_position: 15 ---- - -# Overview - -Metaprogramming in Noir is comprised of three parts: -1. `comptime` code -2. Quoting and unquoting -3. The metaprogramming API in `std::meta` - -Each of these are explained in more detail in the next sections but the wide picture is that -`comptime` allows us to write code which runs at compile-time. In this `comptime` code we -can quote and unquote snippets of the program, manipulate them, and insert them in other -parts of the program. Comptime functions which do this are said to be macros. Additionally, -there's a compile-time API of built-in types and functions provided by the compiler which allows -for greater analysis and modification of programs. - ---- - -# Comptime - -`comptime` is a new keyword in Noir which marks an item as executing or existing at compile-time. It can be used in several ways: - -- `comptime fn` to define functions which execute exclusively during compile-time. -- `comptime global` to define a global variable which is evaluated at compile-time. - - Unlike runtime globals, `comptime global`s can be mutable. -- `comptime { ... }` to execute a block of statements during compile-time. -- `comptime let` to define a variable whose value is evaluated at compile-time. -- `comptime for` to run a for loop at compile-time. Syntax sugar for `comptime { for .. }`. - -## Scoping - -Note that while in a `comptime` context, any runtime variables _local to the current function_ are never visible. - -## Evaluating - -Evaluation rules of `comptime` follows the normal unconstrained evaluation rules for other Noir code. There are a few things to note though: - -- Certain built-in functions may not be available, although more may be added over time. -- Evaluation order of global items is currently unspecified. For example, given the following two functions we can't guarantee -which `println` will execute first. The ordering of the two printouts will be arbitrary, but should be stable across multiple compilations with the same `nargo` version as long as the program is also unchanged. - -```rust -fn one() { - comptime { println("one"); } -} - -fn two() { - comptime { println("two"); } -} -``` - -- Since evaluation order is unspecified, care should be taken when using mutable globals so that they do not rely on a particular ordering. -For example, using globals to generate unique ids should be fine but relying on certain ids always being produced (especially after edits to the program) should be avoided. -- Although most ordering of globals is unspecified, two are: - - Dependencies of a crate will always be evaluated before the dependent crate. - - Any annotations on a function will be run before the function itself is resolved. This is to allow the annotation to modify the function if necessary. Note that if the - function itself was called at compile-time previously, it will already be resolved and cannot be modified. To prevent accidentally calling functions you wish to modify - at compile-time, it may be helpful to sort your `comptime` annotation functions into a different crate along with any dependencies they require. - -## Lowering - -When a `comptime` value is used in runtime code it must be lowered into a runtime value. This means replacing the expression with the literal that it evaluated to. For example, the code: - -```rust -struct Foo { array: [Field; 2], len: u32 } - -fn main() { - println(comptime { - let mut foo = std::mem::zeroed::(); - foo.array[0] = 4; - foo.len = 1; - foo - }); -} -``` - -will be converted to the following after `comptime` expressions are evaluated: - -```rust -struct Foo { array: [Field; 2], len: u32 } - -fn main() { - println(Foo { array: [4, 0], len: 1 }); -} -``` - -Not all types of values can be lowered. For example, `Type`s and `TypeDefinition`s (among other types) cannot be lowered at all. - -```rust -fn main() { - // There's nothing we could inline here to create a Type value at runtime - // let _ = get_type!(); -} - -comptime fn get_type() -> Type { ... } -``` - ---- - -# (Quasi) Quote - -Macros in Noir are `comptime` functions which return code as a value which is inserted into the call site when it is lowered there. -A code value in this case is of type `Quoted` and can be created by a `quote { ... }` expression. -More specifically, the code value `quote` creates is a token stream - a representation of source code as a series of words, numbers, string literals, or operators. -For example, the expression `quote { Hi "there reader"! }` would quote three tokens: the word "hi", the string "there reader", and an exclamation mark. -You'll note that snippets that would otherwise be invalid syntax can still be quoted. - -When a `Quoted` value is used in runtime code, it is lowered into a `quote { ... }` expression. Since this expression is only valid -in compile-time code however, we'd get an error if we tried this. Instead, we can use macro insertion to insert each token into the -program at that point, and parse it as an expression. To do this, we have to add a `!` after the function name returning the `Quoted` value. -If the value was created locally and there is no function returning it, `std::meta::unquote!(_)` can be used instead. -Calling such a function at compile-time without `!` will just return the `Quoted` value to be further manipulated. For example: - -```rust title="quote-example" showLineNumbers -comptime fn quote_one() -> Quoted { - quote { 1 } - } - - #[test] - fn returning_versus_macro_insertion() { - comptime - { - // let _a: Quoted = quote { 1 }; - let _a: Quoted = quote_one(); - - // let _b: Field = 1; - let _b: Field = quote_one!(); - - // Since integers default to fields, if we - // want a different type we have to explicitly cast - // let _c: i32 = 1 as i32; - let _c: i32 = quote_one!() as i32; - } - } -``` -> Source code: noir_stdlib/src/meta/mod.nr#L121-L142 - - -For those familiar with quoting from other languages (primarily lisps), Noir's `quote` is actually a _quasiquote_. -This means we can escape the quoting by using the unquote operator to splice values in the middle of quoted code. - -# Unquote - -The unquote operator `$` is usable within a `quote` expression. -It takes a variable as an argument, evaluates the variable, and splices the resulting value into the quoted token stream at that point. For example, - -```rust -comptime { - let x = 1 + 2; - let y = quote { $x + 4 }; -} -``` - -The value of `y` above will be the token stream containing `3`, `+`, and `4`. We can also use this to combine `Quoted` values into larger token streams: - -```rust -comptime { - let x = quote { 1 + 2 }; - let y = quote { $x + 4 }; -} -``` - -The value of `y` above is now the token stream containing five tokens: `1 + 2 + 4`. - -Note that to unquote something, a variable name _must_ follow the `$` operator in a token stream. -If it is an expression (even a parenthesized one), it will do nothing. Most likely a parse error will be given when the macro is later unquoted. - -Unquoting can also be avoided by escaping the `$` with a backslash: - -``` -comptime { - let x = quote { 1 + 2 }; - - // y contains the four tokens: `$x + 4` - let y = quote { \$x + 4 }; -} -``` - ---- - -# Annotations - -Annotations provide a way to run a `comptime` function on an item in the program. -When you use an annotation, the function with the same name will be called with that item as an argument: - -```rust -#[my_struct_annotation] -struct Foo {} - -comptime fn my_struct_annotation(s: StructDefinition) { - println("Called my_struct_annotation!"); -} - -#[my_function_annotation] -fn foo() {} - -comptime fn my_function_annotation(f: FunctionDefinition) { - println("Called my_function_annotation!"); -} -``` - -Anything returned from one of these functions will be inserted at top-level along with the original item. -Note that expressions are not valid at top-level so you'll get an error trying to return `3` or similar just as if you tried to write a program containing `3; struct Foo {}`. -You can insert other top-level items such as trait impls, structs, or functions this way though. -For example, this is the mechanism used to insert additional trait implementations into the program when deriving a trait impl from a struct: - -```rust title="derive-field-count-example" showLineNumbers -trait FieldCount { - fn field_count() -> u32; - } - - #[derive_field_count] - struct Bar { x: Field, y: [Field; 2] } - - comptime fn derive_field_count(s: StructDefinition) -> Quoted { - let typ = s.as_type(); - let field_count = s.fields().len(); - quote { - impl FieldCount for $typ { - fn field_count() -> u32 { - $field_count - } - } - } - } -``` -> Source code: noir_stdlib/src/meta/mod.nr#L144-L163 - - -## Calling annotations with additional arguments - -Arguments may optionally be given to annotations. -When this is done, these additional arguments are passed to the annotation function after the item argument. - -```rust title="annotation-arguments-example" showLineNumbers -#[assert_field_is_type(quote { i32 }.as_type())] - struct MyStruct { my_field: i32 } - - comptime fn assert_field_is_type(s: StructDefinition, typ: Type) { - // Assert the first field in `s` has type `typ` - let fields = s.fields(); - assert_eq(fields[0].1, typ); - } -``` -> Source code: noir_stdlib/src/meta/mod.nr#L165-L174 - - -We can also take any number of arguments by adding the `varargs` annotation: - -```rust title="annotation-varargs-example" showLineNumbers -#[assert_three_args(1, 2, 3)] - struct MyOtherStruct { my_other_field: u32 } - - #[varargs] - comptime fn assert_three_args(_s: StructDefinition, args: [Field]) { - assert_eq(args.len(), 3); - } -``` -> Source code: noir_stdlib/src/meta/mod.nr#L176-L184 - - ---- - -# Comptime API - -Although `comptime`, `quote`, and unquoting provide a flexible base for writing macros, -Noir's true metaprogramming ability comes from being able to interact with the compiler through a compile-time API. -This API can be accessed through built-in functions in `std::meta` as well as on methods of several `comptime` types. - -The following is an incomplete list of some `comptime` types along with some useful methods on them. - -- `Quoted`: A token stream -- `Type`: The type of a Noir type - - `fn implements(self, constraint: TraitConstraint) -> bool` - - Returns true if `self` implements the given trait constraint -- `Expr`: A syntactically valid expression. Can be used to recur on a program's parse tree to inspect how it is structured. - - Methods: - - `fn as_function_call(self) -> Option<(Expr, [Expr])>` - - If this is a function call expression, return `(function, arguments)` - - `fn as_block(self) -> Option<[Expr]>` - - If this is a block, return each statement in the block -- `FunctionDefinition`: A function definition - - Methods: - - `fn parameters(self) -> [(Quoted, Type)]` - - Returns a slice of `(name, type)` pairs for each parameter -- `StructDefinition`: A struct definition - - Methods: - - `fn as_type(self) -> Type` - - Returns this `StructDefinition` as a `Type`. Any generics are kept as-is - - `fn generics(self) -> [Quoted]` - - Return the name of each generic on this struct - - `fn fields(self) -> [(Quoted, Type)]` - - Return the name and type of each field -- `TraitConstraint`: A trait constraint such as `From` -- `TypedExpr`: A type-checked expression. -- `UnresolvedType`: A syntactic notation that refers to a Noir type that hasn't been resolved yet - -There are many more functions available by exploring the `std::meta` module and its submodules. -Using these methods is the key to writing powerful metaprogramming libraries. - -## `#[use_callers_scope]` - -Since certain functions such as `Quoted::as_type`, `Expression::as_type`, or `Quoted::as_trait_constraint` will attempt -to resolve their contents in a particular scope - it can be useful to change the scope they resolve in. By default -these functions will resolve in the current function's scope which is usually the attribute function they are called in. -If you're working on a library however, this may be a completely different module or crate to the item you're trying to -use the attribute on. If you want to be able to use `Quoted::as_type` to refer to types local to the caller's scope for -example, you can annotate your attribute function with `#[use_callers_scope]`. This will ensure your attribute, and any -closures it uses, can refer to anything in the caller's scope. `#[use_callers_scope]` also works recursively. So if both -your attribute function and a helper function it calls use it, then they can both refer to the same original caller. - ---- - -# Example: Derive - -Using all of the above, we can write a `derive` macro that behaves similarly to Rust's but is not built into the language. -From the user's perspective it will look like this: - -```rust -// Example usage -#[derive(Default, Eq, Ord)] -struct MyStruct { my_field: u32 } -``` - -To implement `derive` we'll have to create a `comptime` function that accepts -a variable amount of traits. - -```rust title="derive_example" showLineNumbers -// These are needed for the unconstrained hashmap we're using to store derive functions -use crate::collections::umap::UHashMap; -use crate::hash::BuildHasherDefault; -use crate::hash::poseidon2::Poseidon2Hasher; - -// A derive function is one that given a struct definition can -// create us a quoted trait impl from it. -pub type DeriveFunction = fn(StructDefinition) -> Quoted; - -// We'll keep a global HANDLERS map to keep track of the derive handler for each trait -comptime mut global HANDLERS: UHashMap> = UHashMap::default(); - -// Given a struct and a slice of traits to derive, create trait impls for each. -// This function is as simple as iterating over the slice, checking if we have a trait -// handler registered for the given trait, calling it, and appending the result. -#[varargs] -pub comptime fn derive(s: StructDefinition, traits: [TraitDefinition]) -> Quoted { - let mut result = quote {}; - - for trait_to_derive in traits { - let handler = unsafe { - HANDLERS.get(trait_to_derive) - }; - assert(handler.is_some(), f"No derive function registered for `{trait_to_derive}`"); - - let trait_impl = handler.unwrap()(s); - result = quote { $result $trait_impl }; - } - - result -} -``` -> Source code: noir_stdlib/src/meta/mod.nr#L31-L65 - - -Registering a derive function could be done as follows: - -```rust title="derive_via" showLineNumbers -// To register a handler for a trait, just add it to our handlers map -pub comptime fn derive_via(t: TraitDefinition, f: DeriveFunction) { - HANDLERS.insert(t, f); -} -``` -> Source code: noir_stdlib/src/meta/mod.nr#L67-L74 - - -```rust title="big-derive-usage-example" showLineNumbers -// Finally, to register a handler we call the above function as an annotation - // with our handler function. - #[derive_via(derive_do_nothing)] - trait DoNothing { - fn do_nothing(self); - } - - comptime fn derive_do_nothing(s: StructDefinition) -> Quoted { - // This is simplified since we don't handle generics or where clauses! - // In a real example we'd likely also need to introduce each of - // `s.generics()` as well as a trait constraint for each generic - // to ensure they also implement the trait. - let typ = s.as_type(); - quote { - impl DoNothing for $typ { - fn do_nothing(self) { - // Traits can't tell us what to do - println("something"); - } - } - } - } - - // Since `DoNothing` is a simple trait which: - // 1. Only has one method - // 2. Does not have any generics on the trait itself - // We can use `std::meta::make_trait_impl` to help us out. - // This helper function will generate our impl for us along with any - // necessary where clauses and still provides a flexible interface - // for us to work on each field on the struct. - comptime fn derive_do_nothing_alt(s: StructDefinition) -> Quoted { - let trait_name = quote { DoNothing }; - let method_signature = quote { fn do_nothing(self) }; - - // Call `do_nothing` recursively on each field in the struct - let for_each_field = |field_name| quote { self.$field_name.do_nothing(); }; - - // Some traits like Eq want to join each field expression with something like `&`. - // We don't need that here - let join_fields_with = quote {}; - - // The body function is a spot to insert any extra setup/teardown needed. - // We'll insert our println here. Since we recur on each field, we should see - // one println for the struct itself, followed by a println for every field (recursively). - let body = |body| quote { - println("something"); - $body - }; - crate::meta::make_trait_impl( - s, - trait_name, - method_signature, - for_each_field, - join_fields_with, - body - ) - } -``` -> Source code: noir_stdlib/src/meta/mod.nr#L186-L244 - diff --git a/docs/versioned_docs/version-v0.35.0/noir/concepts/control_flow.md b/docs/versioned_docs/version-v0.35.0/noir/concepts/control_flow.md deleted file mode 100644 index 045d3c3a5f5..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/concepts/control_flow.md +++ /dev/null @@ -1,77 +0,0 @@ ---- -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] -sidebar_position: 2 ---- - -## 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); -``` - -## 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`. - -### Break and Continue - -In unconstrained code, `break` and `continue` are also allowed in `for` loops. These are only allowed -in unconstrained code since normal constrained code requires that Noir knows exactly how many iterations -a loop may have. `break` and `continue` can be used like so: - -```rust -for i in 0 .. 10 { - println("Iteration start") - - if i == 2 { - continue; - } - - if i == 5 { - break; - } - - println(i); -} -println("Loop end") -``` - -When used, `break` will end the current loop early and jump to the statement after the for loop. In the example -above, the `break` will stop the loop and jump to the `println("Loop end")`. - -`continue` will stop the current iteration of the loop, and jump to the start of the next iteration. In the example -above, `continue` will jump to `println("Iteration start")` when used. Note that the loop continues as normal after this. -The iteration variable `i` is still increased by one as normal when `continue` is used. - -`break` and `continue` cannot currently be used to jump out of more than a single loop at a time. diff --git a/docs/versioned_docs/version-v0.35.0/noir/concepts/data_bus.mdx b/docs/versioned_docs/version-v0.35.0/noir/concepts/data_bus.mdx deleted file mode 100644 index e55e58622ce..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/concepts/data_bus.mdx +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: Data Bus -sidebar_position: 13 ---- -import Experimental from '@site/src/components/Notes/_experimental.mdx'; - - - -The data bus is an optimization that the backend can use to make recursion more efficient. -In order to use it, you must define some inputs of the program entry points (usually the `main()` -function) with the `call_data` modifier, and the return values with the `return_data` modifier. -These modifiers are incompatible with `pub` and `mut` modifiers. - -## Example - -```rust -fn main(mut x: u32, y: call_data u32, z: call_data [u32;4] ) -> return_data u32 { - let a = z[x]; - a+y -} -``` - -As a result, both call_data and return_data will be treated as private inputs and encapsulated into a read-only array each, for the backend to process. diff --git a/docs/versioned_docs/version-v0.35.0/noir/concepts/data_types/_category_.json b/docs/versioned_docs/version-v0.35.0/noir/concepts/data_types/_category_.json deleted file mode 100644 index 5d694210bbf..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/concepts/data_types/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "position": 0, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.35.0/noir/concepts/data_types/arrays.md b/docs/versioned_docs/version-v0.35.0/noir/concepts/data_types/arrays.md deleted file mode 100644 index bb68e60fe53..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/concepts/data_types/arrays.md +++ /dev/null @@ -1,274 +0,0 @@ ---- -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, - ] -sidebar_position: 4 ---- - -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.mdx), 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]; -``` - -However, multidimensional slices are not supported. For example, the following code will error at compile time: - -```rust -let slice : [[Field]] = &[]; -``` - -## 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. -Each of these functions are located within the generic impl `impl [T; N] {`. -So anywhere `self` appears, it refers to the variable `self: [T; N]`. - -### len - -Returns the length of an array - -```rust -fn len(self) -> 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(self) -> [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. The ordering function must return true if the first argument should be sorted to be before the second argument or is equal to the second argument. - -Using this method with an operator like `<` that does not return `true` for equal values will result in an assertion failure for arrays with equal elements. - -```rust -fn sort_via(self, 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(self, 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(self, 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 the starting element. - -```rust -fn reduce(self, 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(self, 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(self, 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); -} -``` - -### as_str_unchecked - -Converts a byte array of type `[u8; N]` to a string. Note that this performs no UTF-8 validation - -the given array is interpreted as-is as a string. - -```rust -impl [u8; N] { - pub fn as_str_unchecked(self) -> str -} -``` - -example: - -```rust -fn main() { - let hi = [104, 105].as_str_unchecked(); - assert_eq(hi, "hi"); -} -``` diff --git a/docs/versioned_docs/version-v0.35.0/noir/concepts/data_types/booleans.md b/docs/versioned_docs/version-v0.35.0/noir/concepts/data_types/booleans.md deleted file mode 100644 index 2507af710e7..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/concepts/data_types/booleans.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -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, - ] -sidebar_position: 2 ---- - - -The `bool` type in Noir has two possible values: `true` and `false`: - -```rust -fn main() { - let t = true; - let f: bool = false; -} -``` - -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.md) and -[Assert Function](../assert.md) sections. diff --git a/docs/versioned_docs/version-v0.35.0/noir/concepts/data_types/fields.md b/docs/versioned_docs/version-v0.35.0/noir/concepts/data_types/fields.md deleted file mode 100644 index 814b996facb..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/concepts/data_types/fields.md +++ /dev/null @@ -1,240 +0,0 @@ ---- -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, - ] -sidebar_position: 0 ---- - -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 title="to_le_bits" showLineNumbers -pub fn to_le_bits(self: Self) -> [u1; N] {} -``` -> Source code: noir_stdlib/src/field/mod.nr#L33-L35 - - -example: - -```rust title="to_le_bits_example" showLineNumbers -fn test_to_le_bits() { - let field = 2; - let bits: [u1; 8] = field.to_le_bits(); - assert_eq(bits, [0, 1, 0, 0, 0, 0, 0, 0]); - } -``` -> Source code: noir_stdlib/src/field/mod.nr#L255-L261 - - - -### to_be_bits - -Transforms the field into an array of bits, Big Endian. - -```rust title="to_be_bits" showLineNumbers -pub fn to_be_bits(self: Self) -> [u1; N] {} -``` -> Source code: noir_stdlib/src/field/mod.nr#L49-L51 - - -example: - -```rust title="to_be_bits_example" showLineNumbers -fn test_to_be_bits() { - let field = 2; - let bits: [u1; 8] = field.to_be_bits(); - assert_eq(bits, [0, 0, 0, 0, 0, 0, 1, 0]); - } -``` -> Source code: noir_stdlib/src/field/mod.nr#L246-L252 - - - -### to_le_bytes - -Transforms into an array of bytes, Little Endian - -```rust title="to_le_bytes" showLineNumbers -pub fn to_le_bytes(self: Self) -> [u8; N] { -``` -> Source code: noir_stdlib/src/field/mod.nr#L62-L64 - - -example: - -```rust title="to_le_bytes_example" showLineNumbers -fn test_to_le_bytes() { - let field = 2; - let bits: [u8; 8] = field.to_le_bytes(); - assert_eq(bits, [2, 0, 0, 0, 0, 0, 0, 0]); - assert_eq(Field::from_le_bytes::<8>(bits), field); - } -``` -> Source code: noir_stdlib/src/field/mod.nr#L274-L281 - - -### to_be_bytes - -Transforms into an array of bytes, Big Endian - -```rust title="to_be_bytes" showLineNumbers -pub fn to_be_bytes(self: Self) -> [u8; N] { -``` -> Source code: noir_stdlib/src/field/mod.nr#L95-L97 - - -example: - -```rust title="to_be_bytes_example" showLineNumbers -fn test_to_be_bytes() { - let field = 2; - let bits: [u8; 8] = field.to_be_bytes(); - assert_eq(bits, [0, 0, 0, 0, 0, 0, 0, 2]); - assert_eq(Field::from_be_bytes::<8>(bits), field); - } -``` -> Source code: noir_stdlib/src/field/mod.nr#L264-L271 - - - -### to_le_radix - -Decomposes into an array over the specified base, Little Endian - -```rust title="to_le_radix" showLineNumbers -pub fn to_le_radix(self: Self, radix: u32) -> [u8; N] { - crate::assert_constant(radix); - self.__to_le_radix(radix) - } -``` -> Source code: noir_stdlib/src/field/mod.nr#L119-L124 - - - -example: - -```rust title="to_le_radix_example" showLineNumbers -fn test_to_le_radix() { - let field = 2; - let bits: [u8; 8] = field.to_le_radix(256); - assert_eq(bits, [2, 0, 0, 0, 0, 0, 0, 0]); - assert_eq(Field::from_le_bytes::<8>(bits), field); - } -``` -> Source code: noir_stdlib/src/field/mod.nr#L294-L301 - - - -### to_be_radix - -Decomposes into an array over the specified base, Big Endian - -```rust title="to_be_radix" showLineNumbers -pub fn to_be_radix(self: Self, radix: u32) -> [u8; N] { - crate::assert_constant(radix); - self.__to_be_radix(radix) - } -``` -> Source code: noir_stdlib/src/field/mod.nr#L126-L131 - - -example: - -```rust title="to_be_radix_example" showLineNumbers -fn test_to_be_radix() { - let field = 2; - let bits: [u8; 8] = field.to_be_radix(256); - assert_eq(bits, [0, 0, 0, 0, 0, 0, 0, 2]); - assert_eq(Field::from_be_bytes::<8>(bits), field); - } -``` -> Source code: noir_stdlib/src/field/mod.nr#L284-L291 - - - -### 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); -} -``` - -### assert_max_bit_size - -Adds a constraint to specify that the field can be represented with `bit_size` number of bits - -```rust title="assert_max_bit_size" showLineNumbers -pub fn assert_max_bit_size(self, bit_size: u32) { -``` -> Source code: noir_stdlib/src/field/mod.nr#L10-L12 - - -example: - -```rust -fn main() { - let field = 2 - field.assert_max_bit_size(32); -} -``` - -### 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 -``` - - -### lt - -Returns true if the field is less than the other field - -```rust -pub fn lt(self, another: Field) -> bool -``` diff --git a/docs/versioned_docs/version-v0.35.0/noir/concepts/data_types/function_types.md b/docs/versioned_docs/version-v0.35.0/noir/concepts/data_types/function_types.md deleted file mode 100644 index f6121af17e2..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/concepts/data_types/function_types.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: Function types -sidebar_position: 10 ---- - -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](../lambdas.md) for more details. diff --git a/docs/versioned_docs/version-v0.35.0/noir/concepts/data_types/index.md b/docs/versioned_docs/version-v0.35.0/noir/concepts/data_types/index.md deleted file mode 100644 index 11f51e2b65a..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/concepts/data_types/index.md +++ /dev/null @@ -1,126 +0,0 @@ ---- -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](../generics.md): - -```rust -type Id = Size; - -fn main() { - let id: Id = 1; - let zero: u32 = 0; - assert(zero + 1 == id); -} -``` - -Type aliases can even refer to other aliases. An error will be issued if they form a cycle: - -```rust -// Ok! -type A = B; -type B = Field; - -type Bad1 = Bad2; - -// error: Dependency cycle found -type Bad2 = Bad1; -// ^^^^^^^^^^^ 'Bad2' recursively depends on itself: Bad2 -> Bad1 -> Bad2 -``` - -By default, like functions, type aliases are private to the module the exist in. You can use `pub` -to make the type alias public or `pub(crate)` to make it public to just its crate: - -```rust -// This type alias is now public -pub type Id = u8; -``` - -## Wildcard Type -Noir can usually infer the type of the variable from the context, so specifying the type of a variable is only required when it cannot be inferred. However, specifying a complex type can be tedious, especially when it has multiple generic arguments. Often some of the generic types can be inferred from the context, and Noir only needs a hint to properly infer the other types. We can partially specify a variable's type by using `_` as a marker, indicating where we still want the compiler to infer the type. - -```rust -let a: [_; 4] = foo(b); -``` - - -### BigInt - -You can achieve BigInt functionality using the [Noir BigInt](https://github.com/shuklaayush/noir-bigint) library. diff --git a/docs/versioned_docs/version-v0.35.0/noir/concepts/data_types/integers.md b/docs/versioned_docs/version-v0.35.0/noir/concepts/data_types/integers.md deleted file mode 100644 index a1d59bf3166..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/concepts/data_types/integers.md +++ /dev/null @@ -1,156 +0,0 @@ ---- -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] -sidebar_position: 1 ---- - -An integer type is a range constrained field type. -The Noir frontend supports both unsigned and signed integer types. -The allowed sizes are 1, 8, 16, 32 and 64 bits. - -:::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\\$). - -## 128 bits Unsigned Integers - -The built-in structure `U128` allows you to use 128-bit unsigned integers almost like a native integer type. However, there are some differences to keep in mind: -- You cannot cast between a native integer and `U128` -- There is a higher performance cost when using `U128`, compared to a native type. - -Conversion between unsigned integer types and U128 are done through the use of `from_integer` and `to_integer` functions. `from_integer` also accepts the `Field` type as input. - -```rust -fn main() { - let x = U128::from_integer(23); - let y = U128::from_hex("0x7"); - let z = x + y; - assert(z.to_integer() == 30); -} -``` - -`U128` is implemented with two 64 bits limbs, representing the low and high bits, which explains the performance cost. You should expect `U128` to be twice more costly for addition and four times more costly for multiplication. -You can construct a U128 from its limbs: -```rust -fn main(x: u64, y: u64) { - let x = U128::from_u64s_be(x,y); - assert(z.hi == x as Field); - assert(z.lo == y as Field); -} -``` - -Note that the limbs are stored as Field elements in order to avoid unnecessary conversions. -Apart from this, most operations will work as usual: - -```rust -fn main(x: U128, y: U128) { - // multiplication - let c = x * y; - // addition and subtraction - let c = c - x + y; - // division - let c = x / y; - // bit operation; - let c = x & y | y; - // bit shift - let c = x << y; - // comparisons; - let c = x < y; - let c = x == y; -} -``` - -## 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 execute -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 - -fn main(x: u8, y: u8) -> pub u8 { - std::wrapping_add(x, y) -} -``` diff --git a/docs/versioned_docs/version-v0.35.0/noir/concepts/data_types/references.md b/docs/versioned_docs/version-v0.35.0/noir/concepts/data_types/references.md deleted file mode 100644 index a5293d11cfb..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/concepts/data_types/references.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: References -sidebar_position: 9 ---- - -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.35.0/noir/concepts/data_types/slices.mdx b/docs/versioned_docs/version-v0.35.0/noir/concepts/data_types/slices.mdx deleted file mode 100644 index cfee564a302..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/concepts/data_types/slices.mdx +++ /dev/null @@ -1,358 +0,0 @@ ---- -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] -sidebar_position: 5 ---- - -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 -fn main() -> pub u32 { - let mut slice: [Field] = &[0; 2]; - - let mut new_slice = slice.push_back(6); - new_slice.len() -} -``` - -To write a slice literal, use a preceding ampersand as in: `&[0; 2]` or -`&[1, 2, 3]`. - -It is important to note that slices are not references to arrays. In Noir, -`&[..]` is more similar to an immutable, growable vector. - -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); -``` - -### len - -Returns the length of a slice - -```rust -fn len(self) -> Field -``` - -Example: - -```rust -fn main() { - let slice = &[42, 42]; - assert(slice.len() == 2); -} -``` - -### as_array - -Converts this slice into an array. - -Make sure to specify the size of the resulting array. -Panics if the resulting array length is different than the slice's length. - -```rust -fn as_array(self) -> [T; N] -``` - -Example: - -```rust -fn main() { - let slice = &[5, 6]; - - // Always specify the length of the resulting array! - let array: [Field; 2] = slice.as_array(); - - assert(array[0] == slice[0]); - assert(array[1] == slice[1]); -} -``` - -### map - -Applies a function to each element of the slice, returning a new slice containing the mapped elements. - -```rust -fn map(self, f: fn[Env](T) -> U) -> [U] -``` - -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 slice, returning the final accumulated value. The first -parameter is the initial value. - -```rust -fn fold(self, mut accumulator: U, f: fn[Env](U, T) -> U) -> U -``` - -This is a left fold, so the given function will be applied to the accumulator and first element of -the slice, 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 slice = &[2, 2, 2, 2, 2]; - let folded = slice.fold(0, |a, b| a + b); - assert(folded == 10); -} - -``` - -### reduce - -Same as fold, but uses the first element as the starting element. - -```rust -fn reduce(self, f: fn[Env](T, T) -> T) -> T -``` - -example: - -```rust -fn main() { - let slice = &[2, 2, 2, 2, 2]; - let reduced = slice.reduce(|a, b| a + b); - assert(reduced == 10); -} -``` - -### filter - -Returns a new slice containing only elements for which the given predicate returns true. - -```rust -fn filter(self, f: fn[Env](T) -> bool) -> Self -``` - -example: - -```rust -fn main() { - let slice = &[1, 2, 3, 4, 5]; - let odds = slice.filter(|x| x % 2 == 1); - assert_eq(odds, &[1, 3, 5]); -} -``` - -### join - -Flatten each element in the slice into one value, separated by `separator`. - -Note that although slices implement `Append`, `join` cannot be used on slice -elements since nested slices are prohibited. - -```rust -fn join(self, separator: T) -> T where T: Append -``` - -example: - -```rust -struct Accumulator { - total: Field, -} - -// "Append" two accumulators by adding them -impl Append for Accumulator { - fn empty() -> Self { - Self { total: 0 } - } - - fn append(self, other: Self) -> Self { - Self { total: self.total + other.total } - } -} - -fn main() { - let slice = &[1, 2, 3, 4, 5].map(|total| Accumulator { total }); - - let result = slice.join(Accumulator::empty()); - assert_eq(result, Accumulator { total: 15 }); - - // We can use a non-empty separator to insert additional elements to sum: - let separator = Accumulator { total: 10 }; - let result = slice.join(separator); - assert_eq(result, Accumulator { total: 55 }); -} -``` - -### all - -Returns true if all the elements satisfy the given predicate - -```rust -fn all(self, predicate: fn[Env](T) -> bool) -> bool -``` - -example: - -```rust -fn main() { - let slice = &[2, 2, 2, 2, 2]; - let all = slice.all(|a| a == 2); - assert(all); -} -``` - -### any - -Returns true if any of the elements satisfy the given predicate - -```rust -fn any(self, predicate: fn[Env](T) -> bool) -> bool -``` - -example: - -```rust -fn main() { - let slice = &[2, 2, 2, 2, 5]; - let any = slice.any(|a| a == 5); - assert(any); -} - -``` diff --git a/docs/versioned_docs/version-v0.35.0/noir/concepts/data_types/strings.md b/docs/versioned_docs/version-v0.35.0/noir/concepts/data_types/strings.md deleted file mode 100644 index 1fdee42425e..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/concepts/data_types/strings.md +++ /dev/null @@ -1,79 +0,0 @@ ---- -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, - ] -sidebar_position: 3 ---- - - -The string type is a fixed length value defined with `str`. - -You can use strings in `assert()` functions or print them with -`println()`. See more about [Logging](../../standard_library/logging.md). - -```rust - -fn main(message : pub str<11>, hex_as_string : str<4>) { - 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" -``` - -## 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/versioned_docs/version-v0.35.0/noir/concepts/data_types/structs.md b/docs/versioned_docs/version-v0.35.0/noir/concepts/data_types/structs.md deleted file mode 100644 index e529347f27d..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/concepts/data_types/structs.md +++ /dev/null @@ -1,82 +0,0 @@ ---- -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, - ] -sidebar_position: 8 ---- - -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. - -By default, like functions, structs are private to the module the exist in. You can use `pub` -to make the struct public or `pub(crate)` to make it public to just its crate: - -```rust -// This struct is now public -pub struct Animal { - hands: Field, - legs: Field, - eyes: u8, -} -``` \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.35.0/noir/concepts/data_types/tuples.md b/docs/versioned_docs/version-v0.35.0/noir/concepts/data_types/tuples.md deleted file mode 100644 index 2ec5c9c4113..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/concepts/data_types/tuples.md +++ /dev/null @@ -1,48 +0,0 @@ ---- -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, - ] -sidebar_position: 7 ---- - -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.35.0/noir/concepts/functions.md b/docs/versioned_docs/version-v0.35.0/noir/concepts/functions.md deleted file mode 100644 index f656cdfd97a..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/concepts/functions.md +++ /dev/null @@ -1,226 +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] -sidebar_position: 1 ---- - -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](../../tooling/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](./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](./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](./unconstrained.md) and [NoirJS](../../reference/NoirJS/noir_js/index.md) for more details. -- **test**: mark the function as unit tests. See [Tests](../../tooling/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.35.0/noir/concepts/generics.md b/docs/versioned_docs/version-v0.35.0/noir/concepts/generics.md deleted file mode 100644 index c180a0ce7e6..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/concepts/generics.md +++ /dev/null @@ -1,251 +0,0 @@ ---- -title: Generics -description: Learn how to use Generics in Noir -keywords: [Noir, Rust, generics, functions, structs] -sidebar_position: 7 ---- - -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 -} -``` - -## Numeric Generics - -If we want to be generic over array lengths (which are type-level integers), we can use numeric -generics. Using these looks similar to using regular generics, but introducing them into scope -requires declaring them with `let MyGenericName: IntegerType`. This can be done anywhere a normal -generic is declared. Instead of types, these generics resolve to integers at compile-time. -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 - } -} -``` - -## 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 print(self) { - for _i in 0 .. self.count { - println(self.value); - } - } -} - -fn main() { - let repeated = RepeatedValue { value: "Hello!", count: 2 }; - repeated.print(); -} -``` - -The `print` function will print `Hello!` an arbitrary number of times, twice in this case. - -## Calling functions on generic parameters - -Since a generic type `T` can represent any type, how can we call functions on the underlying type? -In other words, how can we go from "any type `T`" to "any type `T` that has certain methods available?" - -This is what [traits](../concepts/traits.md) are for in Noir. Here's an example of a function generic over -any type `T` that implements the `Eq` trait for equality: - -```rust -fn first_element_is_equal(array1: [T; N], array2: [T; N]) -> bool - where T: Eq -{ - if (array1.len() == 0) | (array2.len() == 0) { - true - } else { - array1[0] == array2[0] - } -} - -fn main() { - assert(first_element_is_equal([1, 2, 3], [1, 5, 6])); - - // We can use first_element_is_equal for arrays of any type - // as long as we have an Eq impl for the types we pass in - let array = [MyStruct::new(), MyStruct::new()]; - assert(array_eq(array, array, MyStruct::eq)); -} - -impl Eq for MyStruct { - fn eq(self, other: MyStruct) -> bool { - self.foo == other.foo - } -} -``` - -You can find more details on traits and trait implementations on the [traits page](../concepts/traits.md). - -## Manually Specifying Generics with the Turbofish Operator - -There are times when the compiler cannot reasonably infer what type should be used for a generic, or when the developer themselves may want to manually distinguish generic type parameters. This is where the `::<>` turbofish operator comes into play. - -The `::<>` operator can follow a variable or path and can be used to manually specify generic arguments within the angle brackets. -The name "turbofish" comes from that `::<>` looks like a little fish. - -Examples: -```rust -fn main() { - let mut slice = []; - slice = slice.push_back(1); - slice = slice.push_back(2); - // Without turbofish a type annotation would be needed on the left hand side - let array = slice.as_array::<2>(); -} -``` - - -```rust -trait MyTrait { - fn ten() -> Self; -} - -impl MyTrait for Field { - fn ten() -> Self { 10 } -} - -struct Foo { - inner: T -} - -impl Foo { - fn generic_method(_self: Self) -> U where U: MyTrait { - U::ten() - } -} - -fn example() { - let foo: Foo = Foo { inner: 1 }; - // Using a type other than `Field` here (e.g. u32) would fail as - // there is no matching impl for `u32: MyTrait`. - // - // Substituting the `10` on the left hand side of this assert - // with `10 as u32` would also fail with a type mismatch as we - // are expecting a `Field` from the right hand side. - assert(10 as u32 == foo.generic_method::()); -} -``` - -## Arithmetic Generics - -In addition to numeric generics, Noir also allows a limited form of arithmetic on generics. -When you have a numeric generic such as `N`, you can use the following operators on it in a -type position: `+`, `-`, `*`, `/`, and `%`. - -Note that type checking arithmetic generics is a best effort guess from the compiler and there -are many cases of types that are equal that the compiler may not see as such. For example, -we know that `T * (N + M)` should be equal to `T*N + T*M` but the compiler does not currently -apply the distributive law and thus sees these as different types. - -Even with this limitation though, the compiler can handle common cases decently well: - -```rust -trait Serialize { - fn serialize(self) -> [Field; N]; -} - -impl Serialize<1> for Field { - fn serialize(self) -> [Field; 1] { - [self] - } -} - -impl Serialize for [T; N] - where T: Serialize { .. } - -impl Serialize for (T, U) - where T: Serialize, U: Serialize { .. } - -fn main() { - let data = (1, [2, 3, 4]); - assert_eq(data.serialize().len(), 4); -} -``` - -Note that if there is any over or underflow the types will fail to unify: - -```rust title="underflow-example" showLineNumbers -fn pop(array: [Field; N]) -> [Field; N - 1] { - let mut result: [Field; N - 1] = std::mem::zeroed(); - for i in 0..N - 1 { - result[i] = array[i]; - } - result -} - -fn main() { - // error: Could not determine array length `(0 - 1)` - pop([]); -} -``` -> Source code: test_programs/compile_failure/arithmetic_generics_underflow/src/main.nr#L1-L14 - - -This also applies if there is underflow in an intermediate calculation: - -```rust title="intermediate-underflow-example" showLineNumbers -fn main() { - // From main it looks like there's nothing sketchy going on - seems_fine([]); -} - -// Since `seems_fine` says it can receive and return any length N -fn seems_fine(array: [Field; N]) -> [Field; N] { - // But inside `seems_fine` we pop from the array which - // requires the length to be greater than zero. - - // error: Could not determine array length `(0 - 1)` - push_zero(pop(array)) -} - -fn pop(array: [Field; N]) -> [Field; N - 1] { - let mut result: [Field; N - 1] = std::mem::zeroed(); - for i in 0..N - 1 { - result[i] = array[i]; - } - result -} - -fn push_zero(array: [Field; N]) -> [Field; N + 1] { - let mut result: [Field; N + 1] = std::mem::zeroed(); - for i in 0..N { - result[i] = array[i]; - } - // index N is already zeroed - result -} -``` -> Source code: test_programs/compile_failure/arithmetic_generics_intermediate_underflow/src/main.nr#L1-L32 - diff --git a/docs/versioned_docs/version-v0.35.0/noir/concepts/globals.md b/docs/versioned_docs/version-v0.35.0/noir/concepts/globals.md deleted file mode 100644 index 1145c55dfc7..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/concepts/globals.md +++ /dev/null @@ -1,82 +0,0 @@ ---- -title: Global Variables -description: - Learn about global variables in Noir. Discover how - to declare, modify, and use them in your programs. -keywords: [noir programming language, globals, global variables, constants] -sidebar_position: 8 ---- - -## Globals - - -Noir supports global variables. The global's type can be inferred by the compiler entirely: - -```rust -global N = 5; // Same as `global N: Field = 5` - -global TUPLE = (3, 2); - -fn main() { - assert(N == 5); - assert(N == TUPLE.0 + TUPLE.1); -} -``` - -:::info - -Globals can be defined as any expression, so long as they don't depend on themselves - otherwise there would be a dependency cycle! For example: - -```rust -global T = foo(T); // dependency error -``` - -::: - - -If they are initialized to a literal integer, globals can be used to specify an array's length: - -```rust -global N: u32 = 2; - -fn main(y : [Field; N]) { - assert(y[0] == y[1]) -} -``` - -A global from another module can be imported or referenced externally like any other name: - -```rust -global N = 20; - -fn main() { - assert(my_submodule::N != N); -} - -mod my_submodule { - global N: Field = 10; -} -``` - -When a global is used, Noir replaces the name with its definition on each occurrence. -This means globals defined using function calls will repeat the call each time they're used: - -```rust -global RESULT = foo(); - -fn foo() -> [Field; 100] { ... } -``` - -This is usually fine since Noir will generally optimize any function call that does not -refer to a program input into a constant. It should be kept in mind however, if the called -function performs side-effects like `println`, as these will still occur on each use. - -### Visibility - -By default, like functions, globals are private to the module the exist in. You can use `pub` -to make the global public or `pub(crate)` to make it public to just its crate: - -```rust -// This global is now public -pub global N = 5; -``` \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.35.0/noir/concepts/lambdas.md b/docs/versioned_docs/version-v0.35.0/noir/concepts/lambdas.md deleted file mode 100644 index be3c7e0b5ca..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/concepts/lambdas.md +++ /dev/null @@ -1,81 +0,0 @@ ---- -title: Lambdas -description: Learn how to use anonymous functions in Noir programming language. -keywords: [Noir programming language, lambda, closure, function, anonymous function] -sidebar_position: 9 ---- - -## 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.35.0/noir/concepts/mutability.md b/docs/versioned_docs/version-v0.35.0/noir/concepts/mutability.md deleted file mode 100644 index fdeef6a87c5..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/concepts/mutability.md +++ /dev/null @@ -1,121 +0,0 @@ ---- -title: Mutability -description: - Learn about mutable variables in Noir. Discover how - to declare, modify, and use them in your programs. -keywords: [noir programming language, mutability in noir, mutable variables] -sidebar_position: 8 ---- - -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; -} -``` - -## Non-local mutability - -Non-local mutability can be achieved through the mutable reference type `&mut T`: - -```rust -fn set_to_zero(x: &mut Field) { - *x = 0; -} - -fn main() { - let mut y = 42; - set_to_zero(&mut y); - assert(*y == 0); -} -``` - -When creating a mutable reference, the original variable being referred to (`y` in this -example) must also be mutable. Since mutable references are a reference type, they must -be explicitly dereferenced via `*` to retrieve the underlying value. Note that this yields -a copy of the value, so mutating this copy will not change the original value behind the -reference: - -```rust -fn main() { - let mut x = 1; - let x_ref = &mut x; - - let mut y = *x_ref; - let y_ref = &mut y; - - x = 2; - *x_ref = 3; - - y = 4; - *y_ref = 5; - - assert(x == 3); - assert(*x_ref == 3); - assert(y == 5); - assert(*y_ref == 5); -} -``` - -Note that types in Noir are actually deeply immutable so the copy that occurs when -dereferencing is only a conceptual copy - no additional constraints will occur. - -Mutable references can also be stored within structs. Note that there is also -no lifetime parameter on these unlike rust. This is because the allocated memory -always lasts the entire program - as if it were an array of one element. - -```rust -struct Foo { - x: &mut Field -} - -impl Foo { - fn incr(mut self) { - *self.x += 1; - } -} - -fn main() { - let foo = Foo { x: &mut 0 }; - foo.incr(); - assert(*foo.x == 1); -} -``` - -In general, you should avoid non-local & shared mutability unless it is needed. Sticking -to only local mutability will improve readability and potentially improve compiler optimizations as well. diff --git a/docs/versioned_docs/version-v0.35.0/noir/concepts/ops.md b/docs/versioned_docs/version-v0.35.0/noir/concepts/ops.md deleted file mode 100644 index c35c36c38a9..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/concepts/ops.md +++ /dev/null @@ -1,98 +0,0 @@ ---- -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, - ] -sidebar_position: 3 ---- - -# 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, shift must be u8 | -| >> | Right shift an integer by another integer amount | Types must be integer, shift must be u8 | -| ! | 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 identically 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.35.0/noir/concepts/oracles.mdx b/docs/versioned_docs/version-v0.35.0/noir/concepts/oracles.mdx deleted file mode 100644 index 77a2ac1550a..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/concepts/oracles.mdx +++ /dev/null @@ -1,29 +0,0 @@ ---- -title: Oracles -description: Dive into how Noir supports Oracles via RPC calls, and learn how to declare an Oracle in Noir with our comprehensive guide. -keywords: - - Noir - - Oracles - - RPC Calls - - Unconstrained Functions - - Programming - - Blockchain -sidebar_position: 6 ---- - -import Experimental from '@site/src/components/Notes/_experimental.mdx'; - - - -Noir has support for Oracles via RPC calls. This means Noir will make an RPC call and use the return value for proof generation. - -Since Oracles are not resolved by Noir, they are [`unconstrained` functions](./unconstrained.md) - -You can declare an Oracle through the `#[oracle()]` flag. Example: - -```rust -#[oracle(get_number_sequence)] -unconstrained fn get_number_sequence(_size: Field) -> [Field] {} -``` - -The timeout for when using an external RPC oracle resolver can be set with the `NARGO_FOREIGN_CALL_TIMEOUT` environment variable. This timeout is in units of milliseconds. diff --git a/docs/versioned_docs/version-v0.35.0/noir/concepts/shadowing.md b/docs/versioned_docs/version-v0.35.0/noir/concepts/shadowing.md deleted file mode 100644 index 5ce6130d201..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/concepts/shadowing.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -title: Shadowing -sidebar_position: 12 ---- - -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.35.0/noir/concepts/traits.md b/docs/versioned_docs/version-v0.35.0/noir/concepts/traits.md deleted file mode 100644 index 5d07e0c68f0..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/concepts/traits.md +++ /dev/null @@ -1,475 +0,0 @@ ---- -title: Traits -description: - Traits in Noir can be used to abstract out a common interface for functions across - several data types. -keywords: [noir programming language, traits, interfaces, generic, protocol] -sidebar_position: 14 ---- - -## Overview - -Traits in Noir are a useful abstraction similar to interfaces or protocols in other languages. Each trait defines -the interface of several methods contained within the trait. Types can then implement this trait by providing -implementations for these methods. For example in the program: - -```rust -struct Rectangle { - width: Field, - height: Field, -} - -impl Rectangle { - fn area(self) -> Field { - self.width * self.height - } -} - -fn log_area(r: Rectangle) { - println(r.area()); -} -``` - -We have a function `log_area` to log the area of a `Rectangle`. Now how should we change the program if we want this -function to work on `Triangle`s as well?: - -```rust -struct Triangle { - width: Field, - height: Field, -} - -impl Triangle { - fn area(self) -> Field { - self.width * self.height / 2 - } -} -``` - -Making `log_area` generic over all types `T` would be invalid since not all types have an `area` method. Instead, we can -introduce a new `Area` trait and make `log_area` generic over all types `T` that implement `Area`: - -```rust -trait Area { - fn area(self) -> Field; -} - -fn log_area(shape: T) where T: Area { - println(shape.area()); -} -``` - -We also need to explicitly implement `Area` for `Rectangle` and `Triangle`. We can do that by changing their existing -impls slightly. Note that the parameter types and return type of each of our `area` methods must match those defined -by the `Area` trait. - -```rust -impl Area for Rectangle { - fn area(self) -> Field { - self.width * self.height - } -} - -impl Area for Triangle { - fn area(self) -> Field { - self.width * self.height / 2 - } -} -``` - -Now we have a working program that is generic over any type of Shape that is used! Others can even use this program -as a library with their own types - such as `Circle` - as long as they also implement `Area` for these types. - -## Where Clauses - -As seen in `log_area` above, when we want to create a function or method that is generic over any type that implements -a trait, we can add a where clause to the generic function. - -```rust -fn log_area(shape: T) where T: Area { - println(shape.area()); -} -``` - -It is also possible to apply multiple trait constraints on the same variable at once by combining traits with the `+` -operator. Similarly, we can have multiple trait constraints by separating each with a comma: - -```rust -fn foo(elements: [T], thing: U) where - T: Default + Add + Eq, - U: Bar, -{ - let mut sum = T::default(); - - for element in elements { - sum += element; - } - - if sum == T::default() { - thing.bar(); - } -} -``` - -## Generic Implementations - -You can add generics to a trait implementation by adding the generic list after the `impl` keyword: - -```rust -trait Second { - fn second(self) -> Field; -} - -impl Second for (T, Field) { - fn second(self) -> Field { - self.1 - } -} -``` - -You can also implement a trait for every type this way: - -```rust -trait Debug { - fn debug(self); -} - -impl Debug for T { - fn debug(self) { - println(self); - } -} - -fn main() { - 1.debug(); -} -``` - -### Generic Trait Implementations With Where Clauses - -Where clauses can be placed on trait implementations themselves to restrict generics in a similar way. -For example, while `impl Foo for T` implements the trait `Foo` for every type, `impl Foo for T where T: Bar` -will implement `Foo` only for types that also implement `Bar`. This is often used for implementing generic types. -For example, here is the implementation for array equality: - -```rust -impl Eq for [T; let N: u32] where T: Eq { - // Test if two arrays have the same elements. - // Because both arrays must have length N, we know their lengths already match. - fn eq(self, other: Self) -> bool { - let mut result = true; - - for i in 0 .. self.len() { - // The T: Eq constraint is needed to call == on the array elements here - result &= self[i] == other[i]; - } - - result - } -} -``` - -Where clauses can also be placed on struct implementations. -For example, here is a method utilizing a generic type that implements the equality trait. - -```rust -struct Foo { - a: u32, - b: T, -} - -impl Foo where T: Eq { - fn eq(self, other: Self) -> bool { - (self.a == other.a) & self.b.eq(other.b) - } -} -``` - -## Generic Traits - -Traits themselves can also be generic by placing the generic arguments after the trait name. These generics are in -scope of every item within the trait. - -```rust -trait Into { - // Convert `self` to type `T` - fn into(self) -> T; -} -``` - -When implementing generic traits the generic arguments of the trait must be specified. This is also true anytime -when referencing a generic trait (e.g. in a `where` clause). - -```rust -struct MyStruct { - array: [Field; 2], -} - -impl Into<[Field; 2]> for MyStruct { - fn into(self) -> [Field; 2] { - self.array - } -} - -fn as_array(x: T) -> [Field; 2] - where T: Into<[Field; 2]> -{ - x.into() -} - -fn main() { - let array = [1, 2]; - let my_struct = MyStruct { array }; - - assert_eq(as_array(my_struct), array); -} -``` - -### Associated Types and Constants - -Traits also support associated types and constraints which can be thought of as additional generics that are referred to by name. - -Here's an example of a trait with an associated type `Foo` and a constant `Bar`: - -```rust -trait MyTrait { - type Foo; - - let Bar: u32; -} -``` - -Now when we're implementing `MyTrait` we also have to provide values for `Foo` and `Bar`: - -```rust -impl MyTrait for Field { - type Foo = i32; - - let Bar: u32 = 11; -} -``` - -Since associated constants can also be used in a type position, its values are limited to only other -expression kinds allowed in numeric generics. - -Note that currently all associated types and constants must be explicitly specified in a trait constraint. -If we leave out any, we'll get an error that we're missing one: - -```rust -// Error! Constraint is missing associated constant for `Bar` -fn foo(x: T) where T: MyTrait { - ... -} -``` - -Because all associated types and constants must be explicitly specified, they are essentially named generics, -although this is set to change in the future. Future versions of Noir will allow users to elide associated types -in trait constraints similar to Rust. When this is done, you may still refer to their value with the `::AssociatedType` -syntax: - -```rust -// Only valid in future versions of Noir: -fn foo(x: T) where T: MyTrait { - let _: ::Foo = ...; -} -``` - -The type as trait syntax is possible in Noir today but is less useful when each type must be explicitly specified anyway: - -```rust -fn foo(x: T) where T: MyTrait { - // Works, but could just use F directly - let _: >::Foo = ...; - - let _: F = ...; -} -``` - -## Trait Methods With No `self` - -A trait can contain any number of methods, each of which have access to the `Self` type which represents each type -that eventually implements the trait. Similarly, the `self` variable is available as well but is not required to be used. -For example, we can define a trait to create a default value for a type. This trait will need to return the `Self` type -but doesn't need to take any parameters: - -```rust -trait Default { - fn default() -> Self; -} -``` - -Implementing this trait can be done similarly to any other trait: - -```rust -impl Default for Field { - fn default() -> Field { - 0 - } -} - -struct MyType {} - -impl Default for MyType { - fn default() -> Field { - MyType {} - } -} -``` - -However, since there is no `self` parameter, we cannot call it via the method call syntax `object.method()`. -Instead, we'll need to refer to the function directly. This can be done either by referring to the -specific impl `MyType::default()` or referring to the trait itself `Default::default()`. In the later -case, type inference determines the impl that is selected. - -```rust -let my_struct = MyStruct::default(); - -let x: Field = Default::default(); -let result = x + Default::default(); -``` - -:::warning - -```rust -let _ = Default::default(); -``` - -If type inference cannot select which impl to use because of an ambiguous `Self` type, an impl will be -arbitrarily selected. This occurs most often when the result of a trait function call with no parameters -is unused. To avoid this, when calling a trait function with no `self` or `Self` parameters or return type, -always refer to it via the implementation type's namespace - e.g. `MyType::default()`. -This is set to change to an error in future Noir versions. - -::: - -## Default Method Implementations - -A trait can also have default implementations of its methods by giving a body to the desired functions. -Note that this body must be valid for all types that may implement the trait. As a result, the only -valid operations on `self` will be operations valid for any type or other operations on the trait itself. - -```rust -trait Numeric { - fn add(self, other: Self) -> Self; - - // Default implementation of double is (self + self) - fn double(self) -> Self { - self.add(self) - } -} -``` - -When implementing a trait with default functions, a type may choose to implement only the required functions: - -```rust -impl Numeric for Field { - fn add(self, other: Field) -> Field { - self + other - } -} -``` - -Or it may implement the optional methods as well: - -```rust -impl Numeric for u32 { - fn add(self, other: u32) -> u32 { - self + other - } - - fn double(self) -> u32 { - self * 2 - } -} -``` - -## Impl Specialization - -When implementing traits for a generic type it is possible to implement the trait for only a certain combination -of generics. This can be either as an optimization or because those specific generics are required to implement the trait. - -```rust -trait Sub { - fn sub(self, other: Self) -> Self; -} - -struct NonZero { - value: T, -} - -impl Sub for NonZero { - fn sub(self, other: Self) -> Self { - let value = self.value - other.value; - assert(value != 0); - NonZero { value } - } -} -``` - -## Overlapping Implementations - -Overlapping implementations are disallowed by Noir to ensure Noir's decision on which impl to select is never ambiguous. -This means if a trait `Foo` is already implemented -by a type `Bar` for all `T`, then we cannot also have a separate impl for `Bar` (or any other -type argument). Similarly, if there is an impl for all `T` such as `impl Debug for T`, we cannot create -any more impls to `Debug` for other types since it would be ambiguous which impl to choose for any given -method call. - -```rust -trait Trait {} - -// Previous impl defined here -impl Trait for (A, B) {} - -// error: Impl for type `(Field, Field)` overlaps with existing impl -impl Trait for (Field, Field) {} -``` - -## Trait Coherence - -Another restriction on trait implementations is coherence. This restriction ensures other crates cannot create -impls that may overlap with other impls, even if several unrelated crates are used as dependencies in the same -program. - -The coherence restriction is: to implement a trait, either the trait itself or the object type must be declared -in the crate the impl is in. - -In practice this often comes up when using types provided by libraries. If a library provides a type `Foo` that does -not implement a trait in the standard library such as `Default`, you may not `impl Default for Foo` in your own crate. -While restrictive, this prevents later issues or silent changes in the program if the `Foo` library later added its -own impl for `Default`. If you are a user of the `Foo` library in this scenario and need a trait not implemented by the -library your choices are to either submit a patch to the library or use the newtype pattern. - -### The Newtype Pattern - -The newtype pattern gets around the coherence restriction by creating a new wrapper type around the library type -that we cannot create `impl`s for. Since the new wrapper type is defined in our current crate, we can create -impls for any trait we need on it. - -```rust -struct Wrapper { - foo: some_library::Foo, -} - -impl Default for Wrapper { - fn default() -> Wrapper { - Wrapper { - foo: some_library::Foo::new(), - } - } -} -``` - -Since we have an impl for our own type, the behavior of this code will not change even if `some_library` is updated -to provide its own `impl Default for Foo`. The downside of this pattern is that it requires extra wrapping and -unwrapping of values when converting to and from the `Wrapper` and `Foo` types. - -### Visibility - -By default, like functions, traits are private to the module the exist in. You can use `pub` -to make the trait public or `pub(crate)` to make it public to just its crate: - -```rust -// This trait is now public -pub trait Trait {} -``` \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.35.0/noir/concepts/unconstrained.md b/docs/versioned_docs/version-v0.35.0/noir/concepts/unconstrained.md deleted file mode 100644 index b5221b8d2dd..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/concepts/unconstrained.md +++ /dev/null @@ -1,104 +0,0 @@ ---- -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] -sidebar_position: 5 ---- - -Unconstrained functions are functions which do not constrain any of the included computation and allow for non-deterministic 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 AND 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 = unsafe { - 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). - -Note that in order to invoke unconstrained functions we need to wrap them in an `unsafe` block, -to make it clear that the call is unconstrained. - -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. - -## Break and Continue - -In addition to loops over runtime bounds, `break` and `continue` are also available in unconstrained code. See [break and continue](../concepts/control_flow.md#break-and-continue) diff --git a/docs/versioned_docs/version-v0.35.0/noir/modules_packages_crates/_category_.json b/docs/versioned_docs/version-v0.35.0/noir/modules_packages_crates/_category_.json deleted file mode 100644 index 1debcfe7675..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/modules_packages_crates/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "Modules, Packages and Crates", - "position": 2, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.35.0/noir/modules_packages_crates/crates_and_packages.md b/docs/versioned_docs/version-v0.35.0/noir/modules_packages_crates/crates_and_packages.md deleted file mode 100644 index 95ee9f52ab2..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/modules_packages_crates/crates_and_packages.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -title: Crates and Packages -description: Learn how to use Crates and Packages in your Noir project -keywords: [Nargo, dependencies, package management, crates, package] -sidebar_position: 0 ---- - -## 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/noir-projects/noir-contracts/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.35.0/noir/modules_packages_crates/dependencies.md b/docs/versioned_docs/version-v0.35.0/noir/modules_packages_crates/dependencies.md deleted file mode 100644 index 24e02de08fe..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/modules_packages_crates/dependencies.md +++ /dev/null @@ -1,124 +0,0 @@ ---- -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] -sidebar_position: 1 ---- - -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 = "noir-contracts/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 -└── lib_a - ├── Nargo.toml - └── src - └── lib.nr -``` - -Inside of the binary crate, you can specify: - -```toml -# Nargo.toml - -[dependencies] -lib_a = { path = "../lib_a" } -``` - -## 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 lib_a referenced above: - -```rust -use ecrecover; -use lib_a; -``` - -You can also import only the specific parts of dependency that you want to use, like so: - -```rust -use std::hash::sha256; -use std::scalar_mul::fixed_base_embedded_curve; -``` - -Lastly, as demonstrated in the -[elliptic curve example](../standard_library/cryptographic_primitives/ec_primitives.md#examples), you -can import multiple items in the same line by enclosing them in curly braces: - -```rust -use std::ec::tecurve::affine::{Curve, Point}; -``` - -We don't have a way to consume libraries from inside a [workspace](./workspaces.md) 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 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.35.0/noir/modules_packages_crates/modules.md b/docs/versioned_docs/version-v0.35.0/noir/modules_packages_crates/modules.md deleted file mode 100644 index 05399c38b4c..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/modules_packages_crates/modules.md +++ /dev/null @@ -1,221 +0,0 @@ ---- -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] -sidebar_position: 2 ---- - -Noir's module system follows the same convention as the _newer_ version of Rust's module system. - -## Purpose of Modules - -Modules are used to organize 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 - -``` - -The module filename may also be the name of the module as a directory with the contents in a -file named `mod.nr` within that directory. The above example can alternatively be expressed like this: - -Filename : `src/main.nr` - -```rust -mod foo; - -fn main() { - foo::hello_world(); -} -``` - -Filename : `src/foo/mod.nr` - -```rust -fn from_foo() {} -``` - -Note that it's an error to have both files `src/foo.nr` and `src/foo/mod.nr` in the filesystem. - -### 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 -``` - -Similar to importing a module in the crate root, modules can be placed in a `mod.nr` file, like this: - -Filename : `src/main.nr` - -```rust -mod foo; - -fn main() { - foo::from_foo(); -} -``` - -Filename : `src/foo/mod.nr` - -```rust -mod bar; -fn from_foo() {} -``` - -Filename : `src/foo/bar/mod.nr` - -```rust -fn from_bar() {} -``` - -### Referencing a parent module - -Given a submodule, you can refer to its parent module using the `super` keyword. - -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 -// Same as bar::from_foo -use super::from_foo; - -fn from_bar() { - from_foo(); // invokes super::from_foo(), which is bar::from_foo() - super::from_foo(); // also invokes bar::from_foo() -} -``` - -### `use` visibility - -`use` declarations are private to the containing module, by default. However, like functions, -they can be marked as `pub` or `pub(crate)`. Such a use declaration serves to _re-export_ a name. -A public `use` declaration can therefore redirect some public name to a different target definition: -even a definition with a private canonical path, inside a different module. - -An example of re-exporting: - -```rust -mod some_module { - pub use foo::{bar, baz}; - mod foo { - pub fn bar() {} - pub fn baz() {} - } -} - -fn main() { - some_module::bar(); - some_module::baz(); -} -``` - -In this example, the module `some_module` re-exports two public names defined in `foo`. - -### Visibility - -By default, like functions, modules are private to the module (or crate) the exist in. You can use `pub` -to make the module public or `pub(crate)` to make it public to just its crate: - -```rust -// This module is now public and can be seen by other crates. -pub mod foo; -``` \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.35.0/noir/modules_packages_crates/workspaces.md b/docs/versioned_docs/version-v0.35.0/noir/modules_packages_crates/workspaces.md deleted file mode 100644 index 513497f12bf..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/modules_packages_crates/workspaces.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -title: Workspaces -sidebar_position: 3 ---- - -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 -│ │ └── Prover.toml -│ │ └── src -│ │ └── main.nr -│ └── b -│ ├── Nargo.toml -│ └── Prover.toml -│ └── src -│ └── main.nr -│ -└── Nargo.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.35.0/noir/standard_library/_category_.json b/docs/versioned_docs/version-v0.35.0/noir/standard_library/_category_.json deleted file mode 100644 index af04c0933fd..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/standard_library/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "Standard Library", - "position": 1, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.35.0/noir/standard_library/bigint.md b/docs/versioned_docs/version-v0.35.0/noir/standard_library/bigint.md deleted file mode 100644 index b16fed4ed96..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/standard_library/bigint.md +++ /dev/null @@ -1,127 +0,0 @@ ---- -title: Big Integers -description: How to use big integers from Noir standard library -keywords: - [ - Big Integer, - Noir programming language, - Noir libraries, - ] ---- - -The BigInt module in the standard library exposes some class of integers which do not fit (well) into a Noir native field. It implements modulo arithmetic, modulo a 'big' prime number. - -:::note - -The module can currently be considered as `Field`s with fixed modulo sizes used by a set of elliptic curves, in addition to just the native curve. [More work](https://github.com/noir-lang/noir/issues/510) is needed to achieve arbitrarily sized big integers. - -:::note - -`nargo` can be built with `--profile release-pedantic` to enable extra overflow checks which may affect `BigInt` results in some cases. -Consider the [`noir-bignum`](https://github.com/noir-lang/noir-bignum) library for an optimized alternative approach. - -::: - -Currently 6 classes of integers (i.e 'big' prime numbers) are available in the module, namely: - -- BN254 Fq: Bn254Fq -- BN254 Fr: Bn254Fr -- Secp256k1 Fq: Secpk1Fq -- Secp256k1 Fr: Secpk1Fr -- Secp256r1 Fr: Secpr1Fr -- Secp256r1 Fq: Secpr1Fq - -Where XXX Fq and XXX Fr denote respectively the order of the base and scalar field of the (usual) elliptic curve XXX. -For instance the big integer 'Secpk1Fq' in the standard library refers to integers modulo $2^{256}-2^{32}-977$. - -Feel free to explore the source code for the other primes: - -```rust title="big_int_definition" showLineNumbers -pub struct BigInt { - pointer: u32, - modulus: u32, -} -``` -> Source code: noir_stdlib/src/bigint.nr#L14-L19 - - -## Example usage - -A common use-case is when constructing a big integer from its bytes representation, and performing arithmetic operations on it: - -```rust title="big_int_example" showLineNumbers -fn big_int_example(x: u8, y: u8) { - let a = Secpk1Fq::from_le_bytes(&[x, y, 0, 45, 2]); - let b = Secpk1Fq::from_le_bytes(&[y, x, 9]); - let c = (a + b) * b / a; - let d = c.to_le_bytes(); - println(d[0]); -} -``` -> Source code: test_programs/execution_success/bigint/src/main.nr#L72-L80 - - -## Methods - -The available operations for each big integer are: - -### from_le_bytes - -Construct a big integer from its little-endian bytes representation. Example: - -```rust - // Construct a big integer from a slice of bytes - let a = Secpk1Fq::from_le_bytes(&[x, y, 0, 45, 2]); - // Construct a big integer from an array of 32 bytes - let a = Secpk1Fq::from_le_bytes_32([1;32]); - ``` - -Sure, here's the formatted version of the remaining methods: - -### to_le_bytes - -Return the little-endian bytes representation of a big integer. Example: - -```rust -let bytes = a.to_le_bytes(); -``` - -### add - -Add two big integers. Example: - -```rust -let sum = a + b; -``` - -### sub - -Subtract two big integers. Example: - -```rust -let difference = a - b; -``` - -### mul - -Multiply two big integers. Example: - -```rust -let product = a * b; -``` - -### div - -Divide two big integers. Note that division is field division and not euclidean division. Example: - -```rust -let quotient = a / b; -``` - -### eq - -Compare two big integers. Example: - -```rust -let are_equal = a == b; -``` diff --git a/docs/versioned_docs/version-v0.35.0/noir/standard_library/black_box_fns.md b/docs/versioned_docs/version-v0.35.0/noir/standard_library/black_box_fns.md deleted file mode 100644 index d6079ab182c..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/standard_library/black_box_fns.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -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. - -The ACVM spec defines a set of blackbox functions which backends will be expected to implement. This allows backends to use optimized implementations of these constraints if they have them, however they may also fallback to less efficient naive implementations if not. - -## Function list - -Here is a list of the current black box functions: - -- [AES128](./cryptographic_primitives/ciphers.mdx#aes128) -- [SHA256](./cryptographic_primitives/hashes.mdx#sha256) -- [Schnorr signature verification](./cryptographic_primitives/schnorr.mdx) -- [Blake2s](./cryptographic_primitives/hashes.mdx#blake2s) -- [Blake3](./cryptographic_primitives/hashes.mdx#blake3) -- [Pedersen Hash](./cryptographic_primitives/hashes.mdx#pedersen_hash) -- [Pedersen Commitment](./cryptographic_primitives/hashes.mdx#pedersen_commitment) -- [ECDSA signature verification](./cryptographic_primitives/ecdsa_sig_verification.mdx) -- [Embedded curve operations (MSM, addition, ...)](./cryptographic_primitives/embedded_curve_ops.mdx) -- AND -- XOR -- RANGE -- [Keccak256](./cryptographic_primitives/hashes.mdx#keccak256) -- [Recursive proof verification](./recursion.mdx) - -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. - -You can view the black box functions defined in the ACVM code [here](https://github.com/noir-lang/noir/blob/master/acvm-repo/acir/src/circuit/black_box_functions.rs). diff --git a/docs/versioned_docs/version-v0.35.0/noir/standard_library/bn254.md b/docs/versioned_docs/version-v0.35.0/noir/standard_library/bn254.md deleted file mode 100644 index 3294f005dbb..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/standard_library/bn254.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -title: Bn254 Field Library ---- - -Noir provides a module in standard library with some optimized functions for bn254 Fr in `std::field::bn254`. - -## decompose - -```rust -fn decompose(x: Field) -> (Field, Field) {} -``` - -Decomposes a single field into two fields, low and high. The low field contains the lower 16 bytes of the input field and the high field contains the upper 16 bytes of the input field. Both field results are range checked to 128 bits. - - -## assert_gt - -```rust -fn assert_gt(a: Field, b: Field) {} -``` - -Asserts that a > b. This will generate less constraints than using `assert(gt(a, b))`. - -## assert_lt - -```rust -fn assert_lt(a: Field, b: Field) {} -``` - -Asserts that a < b. This will generate less constraints than using `assert(lt(a, b))`. - -## gt - -```rust -fn gt(a: Field, b: Field) -> bool {} -``` - -Returns true if a > b. - -## lt - -```rust -fn lt(a: Field, b: Field) -> bool {} -``` - -Returns true if a < b. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.35.0/noir/standard_library/containers/boundedvec.md b/docs/versioned_docs/version-v0.35.0/noir/standard_library/containers/boundedvec.md deleted file mode 100644 index b20b49f863c..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/standard_library/containers/boundedvec.md +++ /dev/null @@ -1,419 +0,0 @@ ---- -title: Bounded Vectors -keywords: [noir, vector, bounded vector, slice] -sidebar_position: 1 ---- - -A `BoundedVec` is a growable storage similar to a `Vec` except that it -is bounded with a maximum possible length. Unlike `Vec`, `BoundedVec` is not implemented -via slices and thus is not subject to the same restrictions slices are (notably, nested -slices - and thus nested vectors as well - are disallowed). - -Since a BoundedVec is backed by a normal array under the hood, growing the BoundedVec by -pushing an additional element is also more efficient - the length only needs to be increased -by one. - -For these reasons `BoundedVec` should generally be preferred over `Vec` when there -is a reasonable maximum bound that can be placed on the vector. - -Example: - -```rust -let mut vector: BoundedVec = BoundedVec::new(); -for i in 0..5 { - vector.push(i); -} -assert(vector.len() == 5); -assert(vector.max_len() == 10); -``` - -## Methods - -### new - -```rust -pub fn new() -> Self -``` - -Creates a new, empty vector of length zero. - -Since this container is backed by an array internally, it still needs an initial value -to give each element. To resolve this, each element is zeroed internally. This value -is guaranteed to be inaccessible unless `get_unchecked` is used. - -Example: - -```rust -let empty_vector: BoundedVec = BoundedVec::new(); -assert(empty_vector.len() == 0); -``` - -Note that whenever calling `new` the maximum length of the vector should always be specified -via a type signature: - -```rust title="new_example" showLineNumbers -fn good() -> BoundedVec { - // Ok! MaxLen is specified with a type annotation - let v1: BoundedVec = BoundedVec::new(); - let v2 = BoundedVec::new(); - - // Ok! MaxLen is known from the type of `good`'s return value - v2 -} - -fn bad() { - // Error: Type annotation needed - // The compiler can't infer `MaxLen` from this code. - let mut v3 = BoundedVec::new(); - v3.push(5); -} -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L11-L27 - - -This defaulting of `MaxLen` (and numeric generics in general) to zero may change in future noir versions -but for now make sure to use type annotations when using bounded vectors. Otherwise, you will receive a constraint failure at runtime when the vec is pushed to. - -### get - -```rust -pub fn get(self, index: u64) -> T { -``` - -Retrieves an element from the vector at the given index, starting from zero. - -If the given index is equal to or greater than the length of the vector, this -will issue a constraint failure. - -Example: - -```rust -fn foo(v: BoundedVec) { - let first = v.get(0); - let last = v.get(v.len() - 1); - assert(first != last); -} -``` - -### get_unchecked - -```rust -pub fn get_unchecked(self, index: u64) -> T { -``` - -Retrieves an element from the vector at the given index, starting from zero, without -performing a bounds check. - -Since this function does not perform a bounds check on length before accessing the element, -it is unsafe! Use at your own risk! - -Example: - -```rust title="get_unchecked_example" showLineNumbers -fn sum_of_first_three(v: BoundedVec) -> u32 { - // Always ensure the length is larger than the largest - // index passed to get_unchecked - assert(v.len() > 2); - let first = v.get_unchecked(0); - let second = v.get_unchecked(1); - let third = v.get_unchecked(2); - first + second + third -} -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L54-L64 - - -### set - -```rust -pub fn set(&mut self: Self, index: u64, value: T) { -``` - -Writes an element to the vector at the given index, starting from zero. - -If the given index is equal to or greater than the length of the vector, this will issue a constraint failure. - -Example: - -```rust -fn foo(v: BoundedVec) { - let first = v.get(0); - assert(first != 42); - v.set(0, 42); - let new_first = v.get(0); - assert(new_first == 42); -} -``` - -### set_unchecked - -```rust -pub fn set_unchecked(&mut self: Self, index: u64, value: T) -> T { -``` - -Writes an element to the vector at the given index, starting from zero, without performing a bounds check. - -Since this function does not perform a bounds check on length before accessing the element, it is unsafe! Use at your own risk! - -Example: - -```rust title="set_unchecked_example" showLineNumbers -fn set_unchecked_example() { - let mut vec: BoundedVec = BoundedVec::new(); - vec.extend_from_array([1, 2]); - - // Here we're safely writing within the valid range of `vec` - // `vec` now has the value [42, 2] - vec.set_unchecked(0, 42); - - // We can then safely read this value back out of `vec`. - // Notice that we use the checked version of `get` which would prevent reading unsafe values. - assert_eq(vec.get(0), 42); - - // We've now written past the end of `vec`. - // As this index is still within the maximum potential length of `v`, - // it won't cause a constraint failure. - vec.set_unchecked(2, 42); - println(vec); - - // This will write past the end of the maximum potential length of `vec`, - // it will then trigger a constraint failure. - vec.set_unchecked(5, 42); - println(vec); -} -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L67-L91 - - - -### push - -```rust -pub fn push(&mut self, elem: T) { -``` - -Pushes an element to the end of the vector. This increases the length -of the vector by one. - -Panics if the new length of the vector will be greater than the max length. - -Example: - -```rust title="bounded-vec-push-example" showLineNumbers -let mut v: BoundedVec = BoundedVec::new(); - - v.push(1); - v.push(2); - - // Panics with failed assertion "push out of bounds" - v.push(3); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L95-L103 - - -### pop - -```rust -pub fn pop(&mut self) -> T -``` - -Pops the element at the end of the vector. This will decrease the length -of the vector by one. - -Panics if the vector is empty. - -Example: - -```rust title="bounded-vec-pop-example" showLineNumbers -let mut v: BoundedVec = BoundedVec::new(); - v.push(1); - v.push(2); - - let two = v.pop(); - let one = v.pop(); - - assert(two == 2); - assert(one == 1); - // error: cannot pop from an empty vector - // let _ = v.pop(); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L108-L120 - - -### len - -```rust -pub fn len(self) -> u64 { -``` - -Returns the current length of this vector - -Example: - -```rust title="bounded-vec-len-example" showLineNumbers -let mut v: BoundedVec = BoundedVec::new(); - assert(v.len() == 0); - - v.push(100); - assert(v.len() == 1); - - v.push(200); - v.push(300); - v.push(400); - assert(v.len() == 4); - - let _ = v.pop(); - let _ = v.pop(); - assert(v.len() == 2); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L125-L140 - - -### max_len - -```rust -pub fn max_len(_self: BoundedVec) -> u64 { -``` - -Returns the maximum length of this vector. This is always -equal to the `MaxLen` parameter this vector was initialized with. - -Example: - -```rust title="bounded-vec-max-len-example" showLineNumbers -let mut v: BoundedVec = BoundedVec::new(); - - assert(v.max_len() == 5); - v.push(10); - assert(v.max_len() == 5); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L145-L151 - - -### storage - -```rust -pub fn storage(self) -> [T; MaxLen] { -``` - -Returns the internal array within this vector. -Since arrays in Noir are immutable, mutating the returned storage array will not mutate -the storage held internally by this vector. - -Note that uninitialized elements may be zeroed out! - -Example: - -```rust title="bounded-vec-storage-example" showLineNumbers -let mut v: BoundedVec = BoundedVec::new(); - - assert(v.storage() == [0, 0, 0, 0, 0]); - - v.push(57); - assert(v.storage() == [57, 0, 0, 0, 0]); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L156-L163 - - -### extend_from_array - -```rust -pub fn extend_from_array(&mut self, array: [T; Len]) -``` - -Pushes each element from the given array to this vector. - -Panics if pushing each element would cause the length of this vector -to exceed the maximum length. - -Example: - -```rust title="bounded-vec-extend-from-array-example" showLineNumbers -let mut vec: BoundedVec = BoundedVec::new(); - vec.extend_from_array([2, 4]); - - assert(vec.len == 2); - assert(vec.get(0) == 2); - assert(vec.get(1) == 4); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L168-L175 - - -### extend_from_bounded_vec - -```rust -pub fn extend_from_bounded_vec(&mut self, vec: BoundedVec) -``` - -Pushes each element from the other vector to this vector. The length of -the other vector is left unchanged. - -Panics if pushing each element would cause the length of this vector -to exceed the maximum length. - -Example: - -```rust title="bounded-vec-extend-from-bounded-vec-example" showLineNumbers -let mut v1: BoundedVec = BoundedVec::new(); - let mut v2: BoundedVec = BoundedVec::new(); - - v2.extend_from_array([1, 2, 3]); - v1.extend_from_bounded_vec(v2); - - assert(v1.storage() == [1, 2, 3, 0, 0]); - assert(v2.storage() == [1, 2, 3, 0, 0, 0, 0]); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L180-L189 - - -### from_array - -```rust -pub fn from_array(array: [T; Len]) -> Self -``` - -Creates a new vector, populating it with values derived from an array input. -The maximum length of the vector is determined based on the type signature. - -Example: -```rust -let bounded_vec: BoundedVec = BoundedVec::from_array([1, 2, 3]) -``` - -### map - -```rust -pub fn map(self, f: fn[Env](T) -> U) -> BoundedVec -``` - -Creates a new vector of equal size by calling a closure on each element in this vector. - -Example: - -```rust title="bounded-vec-map-example" showLineNumbers -let vec: BoundedVec = BoundedVec::from_array([1, 2, 3, 4]); - let result = vec.map(|value| value * 2); -``` -> Source code: noir_stdlib/src/collections/bounded_vec.nr#L493-L496 - - -### any - -```rust -pub fn any(self, predicate: fn[Env](T) -> bool) -> bool -``` - -Returns true if the given predicate returns true for any element -in this vector. - -Example: - -```rust title="bounded-vec-any-example" showLineNumbers -let mut v: BoundedVec = BoundedVec::new(); - v.extend_from_array([2, 4, 6]); - - let all_even = !v.any(|elem: u32| elem % 2 != 0); - assert(all_even); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L256-L262 - diff --git a/docs/versioned_docs/version-v0.35.0/noir/standard_library/containers/hashmap.md b/docs/versioned_docs/version-v0.35.0/noir/standard_library/containers/hashmap.md deleted file mode 100644 index 39f1ae9b32c..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/standard_library/containers/hashmap.md +++ /dev/null @@ -1,594 +0,0 @@ ---- -title: HashMap -keywords: [noir, map, hash, hashmap] -sidebar_position: 1 ---- - -`HashMap` is used to efficiently store and look up key-value pairs. - -`HashMap` is a bounded type which can store anywhere from zero to `MaxLen` total elements. -Note that due to hash collisions, the actual maximum number of elements stored by any particular -hashmap is likely lower than `MaxLen`. This is true even with cryptographic hash functions since -every hash value will be performed modulo `MaxLen`. - -Example: - -```rust -// Create a mapping from Fields to u32s with a maximum length of 12 -// using a poseidon2 hasher -use std::hash::poseidon2::Poseidon2Hasher; -let mut map: HashMap> = HashMap::default(); - -map.insert(1, 2); -map.insert(3, 4); - -let two = map.get(1).unwrap(); -``` - -## Methods - -### default - -```rust title="default" showLineNumbers -impl Default for HashMap -where - B: BuildHasher + Default, - H: Hasher + Default { - /// Constructs an empty HashMap. - /// - /// Example: - /// - /// ```noir - /// let hashmap: HashMap> = HashMap::default(); - /// assert(hashmap.is_empty()); - /// ``` - fn default() -> Self { -``` -> Source code: noir_stdlib/src/collections/map.nr#L694-L708 - - -Creates a fresh, empty HashMap. - -When using this function, always make sure to specify the maximum size of the hash map. - -This is the same `default` from the `Default` implementation given further below. It is -repeated here for convenience since it is the recommended way to create a hashmap. - -Example: - -```rust title="default_example" showLineNumbers -let hashmap: HashMap> = HashMap::default(); - assert(hashmap.is_empty()); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L201-L204 - - -Because `HashMap` has so many generic arguments that are likely to be the same throughout -your program, it may be helpful to create a type alias: - -```rust title="type_alias" showLineNumbers -type MyMap = HashMap>; -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L195-L197 - - -### with_hasher - -```rust title="with_hasher" showLineNumbers -pub fn with_hasher(_build_hasher: B) -> Self - where - B: BuildHasher { -``` -> Source code: noir_stdlib/src/collections/map.nr#L103-L107 - - -Creates a hashmap with an existing `BuildHasher`. This can be used to ensure multiple -hashmaps are created with the same hasher instance. - -Example: - -```rust title="with_hasher_example" showLineNumbers -let my_hasher: BuildHasherDefault = Default::default(); - let hashmap: HashMap> = HashMap::with_hasher(my_hasher); - assert(hashmap.is_empty()); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L206-L210 - - -### get - -```rust title="get" showLineNumbers -pub fn get( - self, - key: K - ) -> Option - where - K: Eq + Hash, - B: BuildHasher, - H: Hasher { -``` -> Source code: noir_stdlib/src/collections/map.nr#L470-L479 - - -Retrieves a value from the hashmap, returning `Option::none()` if it was not found. - -Example: - -```rust title="get_example" showLineNumbers -fn get_example(map: HashMap>) { - let x = map.get(12); - - if x.is_some() { - assert(x.unwrap() == 42); - } -} -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L298-L306 - - -### insert - -```rust title="insert" showLineNumbers -pub fn insert( - &mut self, - key: K, - value: V - ) - where - K: Eq + Hash, - B: BuildHasher, - H: Hasher { -``` -> Source code: noir_stdlib/src/collections/map.nr#L514-L524 - - -Inserts a new key-value pair into the map. If the key was already in the map, its -previous value will be overridden with the newly provided one. - -Example: - -```rust title="insert_example" showLineNumbers -let mut map: HashMap> = HashMap::default(); - map.insert(12, 42); - assert(map.len() == 1); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L212-L216 - - -### remove - -```rust title="remove" showLineNumbers -pub fn remove( - &mut self, - key: K - ) - where - K: Eq + Hash, - B: BuildHasher, - H: Hasher { -``` -> Source code: noir_stdlib/src/collections/map.nr#L573-L582 - - -Removes the given key-value pair from the map. If the key was not already present -in the map, this does nothing. - -Example: - -```rust title="remove_example" showLineNumbers -map.remove(12); - assert(map.is_empty()); - - // If a key was not present in the map, remove does nothing - map.remove(12); - assert(map.is_empty()); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L220-L227 - - -### is_empty - -```rust title="is_empty" showLineNumbers -pub fn is_empty(self) -> bool { -``` -> Source code: noir_stdlib/src/collections/map.nr#L168-L170 - - -True if the length of the hash map is empty. - -Example: - -```rust title="is_empty_example" showLineNumbers -assert(map.is_empty()); - - map.insert(1, 2); - assert(!map.is_empty()); - - map.remove(1); - assert(map.is_empty()); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L229-L237 - - -### len - -```rust title="len" showLineNumbers -pub fn len(self) -> u32 { -``` -> Source code: noir_stdlib/src/collections/map.nr#L429-L431 - - -Returns the current length of this hash map. - -Example: - -```rust title="len_example" showLineNumbers -// This is equivalent to checking map.is_empty() - assert(map.len() == 0); - - map.insert(1, 2); - map.insert(3, 4); - map.insert(5, 6); - assert(map.len() == 3); - - // 3 was already present as a key in the hash map, so the length is unchanged - map.insert(3, 7); - assert(map.len() == 3); - - map.remove(1); - assert(map.len() == 2); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L239-L254 - - -### capacity - -```rust title="capacity" showLineNumbers -pub fn capacity(_self: Self) -> u32 { -``` -> Source code: noir_stdlib/src/collections/map.nr#L451-L453 - - -Returns the maximum capacity of this hashmap. This is always equal to the capacity -specified in the hashmap's type. - -Unlike hashmaps in general purpose programming languages, hashmaps in Noir have a -static capacity that does not increase as the map grows larger. Thus, this capacity -is also the maximum possible element count that can be inserted into the hashmap. -Due to hash collisions (modulo the hashmap length), it is likely the actual maximum -element count will be lower than the full capacity. - -Example: - -```rust title="capacity_example" showLineNumbers -let empty_map: HashMap> = HashMap::default(); - assert(empty_map.len() == 0); - assert(empty_map.capacity() == 42); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L256-L260 - - -### clear - -```rust title="clear" showLineNumbers -pub fn clear(&mut self) { -``` -> Source code: noir_stdlib/src/collections/map.nr#L122-L124 - - -Clears the hashmap, removing all key-value pairs from it. - -Example: - -```rust title="clear_example" showLineNumbers -assert(!map.is_empty()); - map.clear(); - assert(map.is_empty()); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L262-L266 - - -### contains_key - -```rust title="contains_key" showLineNumbers -pub fn contains_key( - self, - key: K - ) -> bool - where - K: Hash + Eq, - B: BuildHasher, - H: Hasher { -``` -> Source code: noir_stdlib/src/collections/map.nr#L142-L151 - - -True if the hashmap contains the given key. Unlike `get`, this will not also return -the value associated with the key. - -Example: - -```rust title="contains_key_example" showLineNumbers -if map.contains_key(7) { - let value = map.get(7); - assert(value.is_some()); - } else { - println("No value for key 7!"); - } -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L268-L275 - - -### entries - -```rust title="entries" showLineNumbers -pub fn entries(self) -> BoundedVec<(K, V), N> { -``` -> Source code: noir_stdlib/src/collections/map.nr#L192-L194 - - -Returns a vector of each key-value pair present in the hashmap. - -The length of the returned vector is always equal to the length of the hashmap. - -Example: - -```rust title="entries_example" showLineNumbers -let entries = map.entries(); - - // The length of a hashmap may not be compile-time known, so we - // need to loop over its capacity instead - for i in 0..map.capacity() { - if i < entries.len() { - let (key, value) = entries.get(i); - println(f"{key} -> {value}"); - } - } -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L309-L320 - - -### keys - -```rust title="keys" showLineNumbers -pub fn keys(self) -> BoundedVec { -``` -> Source code: noir_stdlib/src/collections/map.nr#L228-L230 - - -Returns a vector of each key present in the hashmap. - -The length of the returned vector is always equal to the length of the hashmap. - -Example: - -```rust title="keys_example" showLineNumbers -let keys = map.keys(); - - for i in 0..keys.max_len() { - if i < keys.len() { - let key = keys.get_unchecked(i); - let value = map.get(key).unwrap_unchecked(); - println(f"{key} -> {value}"); - } - } -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L322-L332 - - -### values - -```rust title="values" showLineNumbers -pub fn values(self) -> BoundedVec { -``` -> Source code: noir_stdlib/src/collections/map.nr#L262-L264 - - -Returns a vector of each value present in the hashmap. - -The length of the returned vector is always equal to the length of the hashmap. - -Example: - -```rust title="values_example" showLineNumbers -let values = map.values(); - - for i in 0..values.max_len() { - if i < values.len() { - let value = values.get_unchecked(i); - println(f"Found value {value}"); - } - } -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L334-L343 - - -### iter_mut - -```rust title="iter_mut" showLineNumbers -pub fn iter_mut( - &mut self, - f: fn(K, V) -> (K, V) - ) - where - K: Eq + Hash, - B: BuildHasher, - H: Hasher { -``` -> Source code: noir_stdlib/src/collections/map.nr#L298-L307 - - -Iterates through each key-value pair of the HashMap, setting each key-value pair to the -result returned from the given function. - -Note that since keys can be mutated, the HashMap needs to be rebuilt as it is iterated -through. If this is not desired, use `iter_values_mut` if only values need to be mutated, -or `entries` if neither keys nor values need to be mutated. - -The iteration order is left unspecified. As a result, if two keys are mutated to become -equal, which of the two values that will be present for the key in the resulting map is also unspecified. - -Example: - -```rust title="iter_mut_example" showLineNumbers -// Add 1 to each key in the map, and double the value associated with that key. - map.iter_mut(|k, v| (k + 1, v * 2)); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L347-L350 - - -### iter_keys_mut - -```rust title="iter_keys_mut" showLineNumbers -pub fn iter_keys_mut( - &mut self, - f: fn(K) -> K - ) - where - K: Eq + Hash, - B: BuildHasher, - H: Hasher { -``` -> Source code: noir_stdlib/src/collections/map.nr#L338-L347 - - -Iterates through the HashMap, mutating each key to the result returned from -the given function. - -Note that since keys can be mutated, the HashMap needs to be rebuilt as it is iterated -through. If only iteration is desired and the keys are not intended to be mutated, -prefer using `entries` instead. - -The iteration order is left unspecified. As a result, if two keys are mutated to become -equal, which of the two values that will be present for the key in the resulting map is also unspecified. - -Example: - -```rust title="iter_keys_mut_example" showLineNumbers -// Double each key, leaving the value associated with that key untouched - map.iter_keys_mut(|k| k * 2); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L352-L355 - - -### iter_values_mut - -```rust title="iter_values_mut" showLineNumbers -pub fn iter_values_mut(&mut self, f: fn(V) -> V) { -``` -> Source code: noir_stdlib/src/collections/map.nr#L372-L374 - - -Iterates through the HashMap, applying the given function to each value and mutating the -value to equal the result. This function is more efficient than `iter_mut` and `iter_keys_mut` -because the keys are untouched and the underlying hashmap thus does not need to be reordered. - -Example: - -```rust title="iter_values_mut_example" showLineNumbers -// Halve each value - map.iter_values_mut(|v| v / 2); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L357-L360 - - -### retain - -```rust title="retain" showLineNumbers -pub fn retain(&mut self, f: fn(K, V) -> bool) { -``` -> Source code: noir_stdlib/src/collections/map.nr#L393-L395 - - -Retains only the key-value pairs for which the given function returns true. -Any key-value pairs for which the function returns false will be removed from the map. - -Example: - -```rust title="retain_example" showLineNumbers -map.retain(|k, v| (k != 0) & (v != 0)); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L280-L282 - - -## Trait Implementations - -### default - -```rust title="default" showLineNumbers -impl Default for HashMap -where - B: BuildHasher + Default, - H: Hasher + Default { - /// Constructs an empty HashMap. - /// - /// Example: - /// - /// ```noir - /// let hashmap: HashMap> = HashMap::default(); - /// assert(hashmap.is_empty()); - /// ``` - fn default() -> Self { -``` -> Source code: noir_stdlib/src/collections/map.nr#L694-L708 - - -Constructs an empty HashMap. - -Example: - -```rust title="default_example" showLineNumbers -let hashmap: HashMap> = HashMap::default(); - assert(hashmap.is_empty()); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L201-L204 - - -### eq - -```rust title="eq" showLineNumbers -impl Eq for HashMap -where - K: Eq + Hash, - V: Eq, - B: BuildHasher, - H: Hasher { - /// Checks if two HashMaps are equal. - /// - /// Example: - /// - /// ```noir - /// let mut map1: HashMap> = HashMap::default(); - /// let mut map2: HashMap> = HashMap::default(); - /// - /// map1.insert(1, 2); - /// map1.insert(3, 4); - /// - /// map2.insert(3, 4); - /// map2.insert(1, 2); - /// - /// assert(map1 == map2); - /// ``` - fn eq(self, other: HashMap) -> bool { -``` -> Source code: noir_stdlib/src/collections/map.nr#L643-L667 - - -Checks if two HashMaps are equal. - -Example: - -```rust title="eq_example" showLineNumbers -let mut map1: HashMap> = HashMap::default(); - let mut map2: HashMap> = HashMap::default(); - - map1.insert(1, 2); - map1.insert(3, 4); - - map2.insert(3, 4); - map2.insert(1, 2); - - assert(map1 == map2); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L284-L295 - diff --git a/docs/versioned_docs/version-v0.35.0/noir/standard_library/containers/index.md b/docs/versioned_docs/version-v0.35.0/noir/standard_library/containers/index.md deleted file mode 100644 index ea84c6d5c21..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/standard_library/containers/index.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Containers -description: Container types provided by Noir's standard library for storing and retrieving data -keywords: [containers, data types, vec, hashmap] ---- diff --git a/docs/versioned_docs/version-v0.35.0/noir/standard_library/containers/vec.mdx b/docs/versioned_docs/version-v0.35.0/noir/standard_library/containers/vec.mdx deleted file mode 100644 index 475011922f8..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/standard_library/containers/vec.mdx +++ /dev/null @@ -1,170 +0,0 @@ ---- -title: Vectors -description: Delve into the Vec 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] -sidebar_position: 6 ---- - -import Experimental from '@site/src/components/Notes/_experimental.mdx'; - - - -A vector is a collection type similar to Rust's `Vec` type. In Noir, it is a convenient way to use slices as mutable arrays. - -Example: - -```rust -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 -``` - -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 -``` - -Example: - -```rust -let slice: [Field] = &[1, 2, 3]; -let vector_from_slice = Vec::from_slice(slice); -assert(vector_from_slice.len() == 3); -``` - -### len - -Returns the number of elements in the vector. - -```rust -pub fn len(self) -> Field -``` - -Example: - -```rust -let empty_vector: Vec = Vec::new(); -assert(empty_vector.len() == 0); -``` - -### 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 -``` - -Example: - -```rust -let vector: Vec = Vec::from_slice(&[10, 20, 30]); -assert(vector.get(1) == 20); -``` - -### set - -```rust -pub fn set(&mut self: Self, index: u64, value: T) { -``` - -Writes an element to the vector at the given index, starting from zero. - -Panics if the index points beyond the vector's end. - -Example: - -```rust -let vector: Vec = Vec::from_slice(&[10, 20, 30]); -assert(vector.get(1) == 20); -vector.set(1, 42); -assert(vector.get(1) == 42); -``` - -### 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) -``` - -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 -``` - -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) -``` - -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 -``` - -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); -``` diff --git a/docs/versioned_docs/version-v0.35.0/noir/standard_library/cryptographic_primitives/_category_.json b/docs/versioned_docs/version-v0.35.0/noir/standard_library/cryptographic_primitives/_category_.json deleted file mode 100644 index 5d694210bbf..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/standard_library/cryptographic_primitives/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "position": 0, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.35.0/noir/standard_library/cryptographic_primitives/ciphers.mdx b/docs/versioned_docs/version-v0.35.0/noir/standard_library/cryptographic_primitives/ciphers.mdx deleted file mode 100644 index d6a5e1a79eb..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/standard_library/cryptographic_primitives/ciphers.mdx +++ /dev/null @@ -1,32 +0,0 @@ ---- -title: Ciphers -description: - Learn about the implemented ciphers ready to use for any Noir project -keywords: - [ciphers, Noir project, aes128, encrypt] -sidebar_position: 0 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; - -## aes128 - -Given a plaintext as an array of bytes, returns the corresponding aes128 ciphertext (CBC mode). Input padding is automatically performed using PKCS#7, so that the output length is `input.len() + (16 - input.len() % 16)`. - -```rust title="aes128" showLineNumbers -pub fn aes128_encrypt(input: [u8; N], iv: [u8; 16], key: [u8; 16]) -> [u8] {} -``` -> Source code: noir_stdlib/src/aes128.nr#L2-L4 - - -```rust -fn main() { - let input: [u8; 4] = [0, 12, 3, 15] // Random bytes, will be padded to 16 bytes. - let iv: [u8; 16] = [0; 16]; // Initialisation vector - let key: [u8; 16] = [0; 16] // AES key - let ciphertext = std::aes128::aes128_encrypt(inputs.as_bytes(), iv.as_bytes(), key.as_bytes()); // In this case, the output length will be 16 bytes. -} -``` - - - \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.35.0/noir/standard_library/cryptographic_primitives/ec_primitives.md b/docs/versioned_docs/version-v0.35.0/noir/standard_library/cryptographic_primitives/ec_primitives.md deleted file mode 100644 index f262d8160d6..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/standard_library/cryptographic_primitives/ec_primitives.md +++ /dev/null @@ -1,102 +0,0 @@ ---- -title: Elliptic Curve Primitives -keywords: [cryptographic primitives, Noir project] -sidebar_position: 4 ---- - -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/mod.nr`](https://github.com/noir-lang/noir/blob/master/noir_stdlib/src/ec/mod.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/mod.nr)). - -## Examples - -The -[ec_baby_jubjub test](https://github.com/noir-lang/noir/blob/master/test_programs/compile_success_empty/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 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.35.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx b/docs/versioned_docs/version-v0.35.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx deleted file mode 100644 index 12bf5ae8aaf..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx +++ /dev/null @@ -1,98 +0,0 @@ ---- -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] -sidebar_position: 3 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; - -Noir supports ECDSA signatures verification over the secp256k1 and secp256r1 curves. - -## ecdsa_secp256k1::verify_signature - -Verifier for ECDSA Secp256k1 signatures. -See ecdsa_secp256k1::verify_signature_slice for a version that accepts slices directly. - -```rust title="ecdsa_secp256k1" showLineNumbers -pub fn verify_signature( - public_key_x: [u8; 32], - public_key_y: [u8; 32], - signature: [u8; 64], - message_hash: [u8; N] -) -> bool -``` -> Source code: noir_stdlib/src/ecdsa_secp256k1.nr#L2-L9 - - -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_secp256k1::verify_signature_slice - -Verifier for ECDSA Secp256k1 signatures where the message is a slice. - -```rust title="ecdsa_secp256k1_slice" showLineNumbers -pub fn verify_signature_slice( - public_key_x: [u8; 32], - public_key_y: [u8; 32], - signature: [u8; 64], - message_hash: [u8] -) -> bool -``` -> Source code: noir_stdlib/src/ecdsa_secp256k1.nr#L13-L20 - - - - -## ecdsa_secp256r1::verify_signature - -Verifier for ECDSA Secp256r1 signatures. -See ecdsa_secp256r1::verify_signature_slice for a version that accepts slices directly. - -```rust title="ecdsa_secp256r1" showLineNumbers -pub fn verify_signature( - public_key_x: [u8; 32], - public_key_y: [u8; 32], - signature: [u8; 64], - message_hash: [u8; N] -) -> bool -``` -> Source code: noir_stdlib/src/ecdsa_secp256r1.nr#L2-L9 - - -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); -} -``` - - - -## ecdsa_secp256r1::verify_signature - -Verifier for ECDSA Secp256r1 signatures where the message is a slice. - -```rust title="ecdsa_secp256r1_slice" showLineNumbers -pub fn verify_signature_slice( - public_key_x: [u8; 32], - public_key_y: [u8; 32], - signature: [u8; 64], - message_hash: [u8] -) -> bool -``` -> Source code: noir_stdlib/src/ecdsa_secp256r1.nr#L13-L20 - - - diff --git a/docs/versioned_docs/version-v0.35.0/noir/standard_library/cryptographic_primitives/eddsa.mdx b/docs/versioned_docs/version-v0.35.0/noir/standard_library/cryptographic_primitives/eddsa.mdx deleted file mode 100644 index b283de693c8..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/standard_library/cryptographic_primitives/eddsa.mdx +++ /dev/null @@ -1,37 +0,0 @@ ---- -title: EdDSA Verification -description: Learn about the cryptographic primitives regarding EdDSA -keywords: [cryptographic primitives, Noir project, eddsa, signatures] -sidebar_position: 5 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; - -## 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 -``` - -It is also possible to specify the hash algorithm used for the signature by using the `eddsa_verify` function by passing a type implementing the Hasher trait with the turbofish operator. -For instance, if you want to use Poseidon2 instead, you can do the following: -```rust -use std::hash::poseidon2::Poseidon2Hasher; - -eddsa_verify::(pub_key_a.x, pub_key_a.y, s_a, r8_a.x, r8_a.y, msg); -``` - - - -## eddsa::eddsa_to_pub - -Private to public key conversion. - -Returns `(pub_key_x, pub_key_y)` - -```rust -fn eddsa_to_pub(secret : Field) -> (Field, Field) -``` - diff --git a/docs/versioned_docs/version-v0.35.0/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx b/docs/versioned_docs/version-v0.35.0/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx deleted file mode 100644 index 9507f16322c..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx +++ /dev/null @@ -1,92 +0,0 @@ ---- -title: Scalar multiplication -description: See how you can perform scalar multiplication in Noir -keywords: [cryptographic primitives, Noir project, scalar multiplication] -sidebar_position: 1 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; - -The following functions perform operations over the embedded curve whose coordinates are defined by the configured noir field. -For the BN254 scalar field, this is BabyJubJub or Grumpkin. - -:::note -Suffixes `_low` and `_high` denote low and high limbs of a scalar. -::: - -## embedded_curve_ops::multi_scalar_mul - -Performs multi scalar multiplication over the embedded curve. -The function accepts arbitrary amount of point-scalar pairs on the input, it multiplies the individual pairs over -the curve and returns a sum of the resulting points. - -Points represented as x and y coordinates [x1, y1, x2, y2, ...], scalars as low and high limbs [low1, high1, low2, high2, ...]. - -```rust title="multi_scalar_mul" showLineNumbers -pub fn multi_scalar_mul( - points: [EmbeddedCurvePoint; N], - scalars: [EmbeddedCurveScalar; N] -) -> EmbeddedCurvePoint -``` -> Source code: noir_stdlib/src/embedded_curve_ops.nr#L103-L108 - - -example - -```rust -fn main(point_x: Field, point_y: Field, scalar_low: Field, scalar_high: Field) { - let point = std::embedded_curve_ops::multi_scalar_mul([point_x, point_y], [scalar_low, scalar_high]); - println(point); -} -``` - -## embedded_curve_ops::fixed_base_scalar_mul - -Performs fixed base scalar multiplication over the embedded curve (multiplies input scalar with a generator point). -The function accepts a single scalar on the input represented as 2 fields. - -```rust title="fixed_base_scalar_mul" showLineNumbers -pub fn fixed_base_scalar_mul(scalar: EmbeddedCurveScalar) -> EmbeddedCurvePoint -``` -> Source code: noir_stdlib/src/embedded_curve_ops.nr#L120-L122 - - -example - -```rust -fn main(scalar_low: Field, scalar_high: Field) { - let point = std::embedded_curve_ops::fixed_base_scalar_mul(scalar_low, scalar_high); - println(point); -} -``` - -## embedded_curve_ops::embedded_curve_add - -Adds two points on the embedded curve. -This function takes two `EmbeddedCurvePoint` structures as parameters, representing points on the curve, and returns a new `EmbeddedCurvePoint` structure that represents their sum. - -### Parameters: -- `point1` (`EmbeddedCurvePoint`): The first point to add. -- `point2` (`EmbeddedCurvePoint`): The second point to add. - -### Returns: -- `EmbeddedCurvePoint`: The resulting point after the addition of `point1` and `point2`. - -```rust title="embedded_curve_add" showLineNumbers -pub fn embedded_curve_add(point1: EmbeddedCurvePoint, point2: EmbeddedCurvePoint) -> EmbeddedCurvePoint { -``` -> Source code: noir_stdlib/src/embedded_curve_ops.nr#L132-L134 - - -example - -```rust -fn main() { - let point1 = EmbeddedCurvePoint { x: 1, y: 2 }; - let point2 = EmbeddedCurvePoint { x: 3, y: 4 }; - let result = std::embedded_curve_ops::embedded_curve_add(point1, point2); - println!("Resulting Point: ({}, {})", result.x, result.y); -} -``` - - diff --git a/docs/versioned_docs/version-v0.35.0/noir/standard_library/cryptographic_primitives/hashes.mdx b/docs/versioned_docs/version-v0.35.0/noir/standard_library/cryptographic_primitives/hashes.mdx deleted file mode 100644 index 33601bae22b..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/standard_library/cryptographic_primitives/hashes.mdx +++ /dev/null @@ -1,253 +0,0 @@ ---- -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] -sidebar_position: 0 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; - -## sha256 - -Given an array of bytes, returns the resulting sha256 hash. -Specify a message_size to hash only the first `message_size` bytes of the input. - -```rust title="sha256" showLineNumbers -pub fn sha256(input: [u8; N]) -> [u8; 32] -``` -> Source code: noir_stdlib/src/hash/sha256.nr#L7-L9 - - -example: -```rust title="sha256_var" showLineNumbers -let digest = std::hash::sha256_var([x as u8], 1); -``` -> Source code: test_programs/execution_success/sha256/src/main.nr#L16-L18 - - -```rust -fn main() { - let x = [163, 117, 178, 149]; // some random bytes - let hash = std::sha256::sha256_var(x, 4); -} -``` - - - - -## blake2s - -Given an array of bytes, returns an array with the Blake2 hash - -```rust title="blake2s" showLineNumbers -pub fn blake2s(input: [u8; N]) -> [u8; 32] -``` -> Source code: noir_stdlib/src/hash/mod.nr#L18-L20 - - -example: - -```rust -fn main() { - let x = [163, 117, 178, 149]; // some random bytes - let hash = std::hash::blake2s(x); -} -``` - - - -## blake3 - -Given an array of bytes, returns an array with the Blake3 hash - -```rust title="blake3" showLineNumbers -pub fn blake3(input: [u8; N]) -> [u8; 32] -``` -> Source code: noir_stdlib/src/hash/mod.nr#L24-L26 - - -example: - -```rust -fn main() { - let x = [163, 117, 178, 149]; // some random bytes - let hash = std::hash::blake3(x); -} -``` - - - -## pedersen_hash - -Given an array of Fields, returns the Pedersen hash. - -```rust title="pedersen_hash" showLineNumbers -pub fn pedersen_hash(input: [Field; N]) -> Field -``` -> Source code: noir_stdlib/src/hash/mod.nr#L77-L79 - - -example: - -```rust title="pedersen-hash" showLineNumbers -fn main(x: Field, y: Field, expected_hash: Field) { - let hash = std::hash::pedersen_hash([x, y]); - assert_eq(hash, expected_hash); -} -``` -> Source code: test_programs/execution_success/pedersen_hash/src/main.nr#L1-L7 - - - - -## pedersen_commitment - -Given an array of Fields, returns the Pedersen commitment. - -```rust title="pedersen_commitment" showLineNumbers -pub fn pedersen_commitment(input: [Field; N]) -> EmbeddedCurvePoint { -``` -> Source code: noir_stdlib/src/hash/mod.nr#L29-L31 - - -example: - -```rust title="pedersen-commitment" showLineNumbers -fn main(x: Field, y: Field, expected_commitment: std::embedded_curve_ops::EmbeddedCurvePoint) { - let commitment = std::hash::pedersen_commitment([x, y]); - assert_eq(commitment.x, expected_commitment.x); - assert_eq(commitment.y, expected_commitment.y); -} -``` -> Source code: test_programs/execution_success/pedersen_commitment/src/main.nr#L1-L8 - - - - -## 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 title="keccak256" showLineNumbers -pub fn keccak256(input: [u8; N], message_size: u32) -> [u8; 32] -``` -> Source code: noir_stdlib/src/hash/mod.nr#L125-L127 - - -example: - -```rust title="keccak256" showLineNumbers -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 message 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); -} -``` -> Source code: test_programs/execution_success/keccak256/src/main.nr#L1-L21 - - - - -## 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 title="poseidon" showLineNumbers -use std::hash::poseidon; - -fn main(x1: [Field; 2], y1: pub Field, x2: [Field; 4], y2: pub Field) { - let hash1 = poseidon::bn254::hash_2(x1); - assert(hash1 == y1); - - let hash2 = poseidon::bn254::hash_4(x2); - assert(hash2 == y2); -} -``` -> Source code: test_programs/execution_success/poseidon_bn254_hash/src/main.nr#L1-L11 - - -## poseidon 2 - -Given an array of Fields, returns a new Field with the Poseidon2 Hash. Contrary to the Poseidon -function, there is only one hash and you can specify a message_size to hash only the first -`message_size` bytes of the input, - -```rust -// example for hashing the first three elements of the input -Poseidon2::hash(input, 3); -``` - -example: - -```rust title="poseidon2" showLineNumbers -use std::hash::poseidon2; - -fn main(inputs: [Field; 4], expected_hash: Field) { - let hash = poseidon2::Poseidon2::hash(inputs, inputs.len()); - assert_eq(hash, expected_hash); -} -``` -> Source code: test_programs/execution_success/poseidon2/src/main.nr#L1-L8 - - -## 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]) -> 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.35.0/noir/standard_library/cryptographic_primitives/index.md b/docs/versioned_docs/version-v0.35.0/noir/standard_library/cryptographic_primitives/index.md deleted file mode 100644 index 650f30165d5..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/standard_library/cryptographic_primitives/index.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -title: Cryptographic Primitives -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.35.0/noir/standard_library/cryptographic_primitives/schnorr.mdx b/docs/versioned_docs/version-v0.35.0/noir/standard_library/cryptographic_primitives/schnorr.mdx deleted file mode 100644 index 0058c0c0c98..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/standard_library/cryptographic_primitives/schnorr.mdx +++ /dev/null @@ -1,64 +0,0 @@ ---- -title: Schnorr Signatures -description: Learn how you can verify Schnorr signatures using Noir -keywords: [cryptographic primitives, Noir project, schnorr, signatures] -sidebar_position: 2 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; - -## schnorr::verify_signature - -Verifier for Schnorr signatures over the embedded curve (for BN254 it is Grumpkin). -See schnorr::verify_signature_slice for a version that works directly on slices. - -```rust title="schnorr_verify" showLineNumbers -pub fn verify_signature( - public_key_x: Field, - public_key_y: Field, - signature: [u8; 64], - message: [u8; N] -) -> bool -``` -> Source code: noir_stdlib/src/schnorr.nr#L4-L11 - - -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() -); - -... -``` - - - -## schnorr::verify_signature_slice - -Verifier for Schnorr signatures over the embedded curve (for BN254 it is Grumpkin) -where the message is a slice. - -```rust title="schnorr_verify_slice" showLineNumbers -pub fn verify_signature_slice( - public_key_x: Field, - public_key_y: Field, - signature: [u8; 64], - message: [u8] -) -> bool -``` -> Source code: noir_stdlib/src/schnorr.nr#L15-L22 - - - diff --git a/docs/versioned_docs/version-v0.35.0/noir/standard_library/fmtstr.md b/docs/versioned_docs/version-v0.35.0/noir/standard_library/fmtstr.md deleted file mode 100644 index 65a7da9996d..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/standard_library/fmtstr.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -title: fmtstr ---- - -`fmtstr` is the type resulting from using format string (`f"..."`). - -## Methods - -### quoted_contents - -```rust title="quoted_contents" showLineNumbers -comptime fn quoted_contents(self) -> Quoted {} -``` -> Source code: noir_stdlib/src/meta/format_string.nr#L3-L5 - - -Returns the format string contents (that is, without the leading and trailing double quotes) as a `Quoted` value. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.35.0/noir/standard_library/is_unconstrained.md b/docs/versioned_docs/version-v0.35.0/noir/standard_library/is_unconstrained.md deleted file mode 100644 index 51bb1bda8f1..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/standard_library/is_unconstrained.md +++ /dev/null @@ -1,69 +0,0 @@ ---- -title: Is Unconstrained Function -description: - The is_unconstrained function returns wether the context at that point of the program is unconstrained or not. -keywords: - [ - unconstrained - ] ---- - -It's very common for functions in circuits to take unconstrained hints of an expensive computation and then verify it. This is done by running the hint in an unconstrained context and then verifying the result in a constrained context. - -When a function is marked as unconstrained, any subsequent functions that it calls will also be run in an unconstrained context. However, if we are implementing a library function, other users might call it within an unconstrained context or a constrained one. Generally, in an unconstrained context we prefer just computing the result instead of taking a hint of it and verifying it, since that'd mean doing the same computation twice: - -```rust - -fn my_expensive_computation(){ - ... -} - -unconstrained fn my_expensive_computation_hint(){ - my_expensive_computation() -} - -pub fn external_interface(){ - my_expensive_computation_hint(); - // verify my_expensive_computation: If external_interface is called from unconstrained, this is redundant - ... -} - -``` - -In order to improve the performance in an unconstrained context you can use the function at `std::runtime::is_unconstrained() -> bool`: - - -```rust -use dep::std::runtime::is_unconstrained; - -fn my_expensive_computation(){ - ... -} - -unconstrained fn my_expensive_computation_hint(){ - my_expensive_computation() -} - -pub fn external_interface(){ - if is_unconstrained() { - my_expensive_computation(); - } else { - my_expensive_computation_hint(); - // verify my_expensive_computation - ... - } -} - -``` - -The is_unconstrained result is resolved at compile time, so in unconstrained contexts the compiler removes the else branch, and in constrained contexts the compiler removes the if branch, reducing the amount of compute necessary to run external_interface. - -Note that using `is_unconstrained` in a `comptime` context will also return `true`: - -``` -fn main() { - comptime { - assert(is_unconstrained()); - } -} -``` diff --git a/docs/versioned_docs/version-v0.35.0/noir/standard_library/logging.md b/docs/versioned_docs/version-v0.35.0/noir/standard_library/logging.md deleted file mode 100644 index db75ef9f86f..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/standard_library/logging.md +++ /dev/null @@ -1,78 +0,0 @@ ---- -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, - print statement, - debugging in noir, - noir std library, - logging tutorial, - basic logging in noir, - noir logging implementation, - noir debugging techniques, - rust, - ] ---- - -The standard library provides two familiar statements you can use: `println` and `print`. Despite being a limited implementation of rust's `println!` and `print!` macros, these constructs can be useful for debugging. - -You can print the output of both statements in your Noir code by using the `nargo execute` command or the `--show-output` flag when using `nargo test` (provided there are print statements in your tests). - -It is recommended to use `nargo execute` if you want to debug failing constraints with `println` or `print` 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). Neither `println`, nor `print` are callable for failed constraints caught at compile time. - -Both `print` and `println` are generic functions which can work on integers, fields, strings, and even structs or expressions. Note however, that slices are currently unsupported. For example: - -```rust -struct Person { - age: Field, - height: Field, -} - -fn main(age: Field, height: Field) { - let person = Person { - age: age, - height: height, - }; - println(person); - println(age + height); - println("Hello world!"); -} -``` - -You can print different types in the same statement (including strings) with a type called `fmtstr`. It can be specified in the same way as a normal string, just prepended with an "f" character: - -```rust - let fmt_str = f"i: {i}, j: {j}"; - println(fmt_str); - - let s = myStruct { y: x, x: y }; - println(s); - - println(f"i: {i}, s: {s}"); - - println(x); - println([x, y]); - - let foo = fooStruct { my_struct: s, foo: 15 }; - println(f"s: {s}, foo: {foo}"); - - println(15); // prints 0x0f, implicit Field - println(-1 as u8); // prints 255 - println(-1 as i8); // prints -1 -``` - -Examples shown above are interchangeable between the two `print` statements: - -```rust -let person = Person { age : age, height : height }; - -println(person); -print(person); - -println("Hello world!"); // Prints with a newline at the end of the input -print("Hello world!"); // Prints the input and keeps cursor on the same line -``` diff --git a/docs/versioned_docs/version-v0.35.0/noir/standard_library/merkle_trees.md b/docs/versioned_docs/version-v0.35.0/noir/standard_library/merkle_trees.md deleted file mode 100644 index 6a9ebf72ada..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/standard_library/merkle_trees.md +++ /dev/null @@ -1,58 +0,0 @@ ---- -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/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.as_slice()); - 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.35.0/noir/standard_library/meta/ctstring.md b/docs/versioned_docs/version-v0.35.0/noir/standard_library/meta/ctstring.md deleted file mode 100644 index 0be9bbced4e..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/standard_library/meta/ctstring.md +++ /dev/null @@ -1,100 +0,0 @@ ---- -title: CtString ---- - -`std::meta::ctstring` contains methods on the built-in `CtString` type which is -a compile-time, dynamically-sized string type. Compared to `str` and `fmtstr`, -`CtString` is useful because its size does not need to be specified in its type. This -can be used for formatting items at compile-time or general string handling in `comptime` -code. - -Since `fmtstr`s can be converted into `CtString`s, you can make use of their formatting -abilities in CtStrings by formatting in `fmtstr`s then converting the result to a CtString -afterward. - -## Traits - -### AsCtString - -```rust title="as-ctstring" showLineNumbers -pub trait AsCtString { - comptime fn as_ctstring(self) -> CtString; -} -``` -> Source code: noir_stdlib/src/meta/ctstring.nr#L43-L47 - - -Converts an object into a compile-time string. - -Implementations: - -```rust -impl AsCtString for str { ... } -impl AsCtString for fmtstr { ... } -``` - -## Methods - -### new - -```rust title="new" showLineNumbers -comptime fn new() -> Self { -``` -> Source code: noir_stdlib/src/meta/ctstring.nr#L4-L6 - - -Creates an empty `CtString`. - -### append_str - -```rust title="append_str" showLineNumbers -comptime fn append_str(self, s: str) -> Self { -``` -> Source code: noir_stdlib/src/meta/ctstring.nr#L11-L13 - - -Returns a new CtString with the given str appended onto the end. - -### append_fmtstr - -```rust title="append_fmtstr" showLineNumbers -comptime fn append_fmtstr(self, s: fmtstr) -> Self { -``` -> Source code: noir_stdlib/src/meta/ctstring.nr#L17-L19 - - -Returns a new CtString with the given fmtstr appended onto the end. - -### as_quoted_str - -```rust title="as_quoted_str" showLineNumbers -comptime fn as_quoted_str(self) -> Quoted { -``` -> Source code: noir_stdlib/src/meta/ctstring.nr#L26-L28 - - -Returns a quoted string literal from this string's contents. - -There is no direct conversion from a `CtString` to a `str` since -the size would not be known. To get around this, this function can -be used in combination with macro insertion (`!`) to insert this string -literal at this function's call site. - -Example: - -```rust title="as_quoted_str_example" showLineNumbers -let my_ctstring = "foo bar".as_ctstring(); - let my_str = my_ctstring.as_quoted_str!(); - - assert_eq(crate::meta::type_of(my_str), quote { str<7> }.as_type()); -``` -> Source code: noir_stdlib/src/meta/ctstring.nr#L90-L95 - - -## Trait Implementations - -```rust -impl Eq for CtString -impl Hash for CtString -impl Append for CtString -``` diff --git a/docs/versioned_docs/version-v0.35.0/noir/standard_library/meta/expr.md b/docs/versioned_docs/version-v0.35.0/noir/standard_library/meta/expr.md deleted file mode 100644 index 1da32b0bc09..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/standard_library/meta/expr.md +++ /dev/null @@ -1,378 +0,0 @@ ---- -title: Expr ---- - -`std::meta::expr` contains methods on the built-in `Expr` type for quoted, syntactically valid expressions. - -## Methods - -### as_array - -```rust title="as_array" showLineNumbers -comptime fn as_array(self) -> Option<[Expr]> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L10-L12 - - -If this expression is an array, this returns a slice of each element in the array. - -### as_assert - -```rust title="as_assert" showLineNumbers -comptime fn as_assert(self) -> Option<(Expr, Option)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L16-L18 - - -If this expression is an assert, this returns the assert expression and the optional message. - -### as_assert_eq - -```rust title="as_assert_eq" showLineNumbers -comptime fn as_assert_eq(self) -> Option<(Expr, Expr, Option)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L23-L25 - - -If this expression is an assert_eq, this returns the left-hand-side and right-hand-side -expressions, together with the optional message. - -### as_assign - -```rust title="as_assign" showLineNumbers -comptime fn as_assign(self) -> Option<(Expr, Expr)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L30-L32 - - -If this expression is an assignment, this returns a tuple with the left hand side -and right hand side in order. - -### as_binary_op - -```rust title="as_binary_op" showLineNumbers -comptime fn as_binary_op(self) -> Option<(Expr, BinaryOp, Expr)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L37-L39 - - -If this expression is a binary operator operation ` `, -return the left-hand side, operator, and the right-hand side of the operation. - -### as_block - -```rust title="as_block" showLineNumbers -comptime fn as_block(self) -> Option<[Expr]> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L44-L46 - - -If this expression is a block `{ stmt1; stmt2; ...; stmtN }`, return -a slice containing each statement. - -### as_bool - -```rust title="as_bool" showLineNumbers -comptime fn as_bool(self) -> Option {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L50-L52 - - -If this expression is a boolean literal, return that literal. - -### as_cast - -```rust title="as_cast" showLineNumbers -#[builtin(expr_as_cast)] - comptime fn as_cast(self) -> Option<(Expr, UnresolvedType)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L56-L59 - - -If this expression is a cast expression (`expr as type`), returns the casted -expression and the type to cast to. - -### as_comptime - -```rust title="as_comptime" showLineNumbers -comptime fn as_comptime(self) -> Option<[Expr]> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L64-L66 - - -If this expression is a `comptime { stmt1; stmt2; ...; stmtN }` block, -return each statement in the block. - -### as_constructor - -```rust title="as_constructor" showLineNumbers -comptime fn as_constructor(self) -> Option<(UnresolvedType, [(Quoted, Expr)])> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L71-L73 - - -If this expression is a constructor `Type { field1: expr1, ..., fieldN: exprN }`, -return the type and the fields. - -### as_for - -```rust title="as_for" showLineNumbers -comptime fn as_for(self) -> Option<(Quoted, Expr, Expr)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L78-L80 - - -If this expression is a for statement over a single expression, return the identifier, -the expression and the for loop body. - -### as_for_range - -```rust title="as_for" showLineNumbers -comptime fn as_for(self) -> Option<(Quoted, Expr, Expr)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L78-L80 - - -If this expression is a for statement over a range, return the identifier, -the range start, the range end and the for loop body. - -### as_function_call - -```rust title="as_function_call" showLineNumbers -comptime fn as_function_call(self) -> Option<(Expr, [Expr])> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L92-L94 - - -If this expression is a function call `foo(arg1, ..., argN)`, return -the function and a slice of each argument. - -### as_if - -```rust title="as_if" showLineNumbers -comptime fn as_if(self) -> Option<(Expr, Expr, Option)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L100-L102 - - -If this expression is an `if condition { then_branch } else { else_branch }`, -return the condition, then branch, and else branch. If there is no else branch, -`None` is returned for that branch instead. - -### as_index - -```rust title="as_index" showLineNumbers -comptime fn as_index(self) -> Option<(Expr, Expr)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L107-L109 - - -If this expression is an index into an array `array[index]`, return the -array and the index. - -### as_integer - -```rust title="as_integer" showLineNumbers -comptime fn as_integer(self) -> Option<(Field, bool)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L114-L116 - - -If this expression is an integer literal, return the integer as a field -as well as whether the integer is negative (true) or not (false). - -### as_lambda - -```rust title="as_lambda" showLineNumbers -comptime fn as_lambda(self) -> Option<([(Expr, Option)], Option, Expr)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L120-L122 - - -If this expression is a lambda, returns the parameters, return type and body. - -### as_let - -```rust title="as_let" showLineNumbers -comptime fn as_let(self) -> Option<(Expr, Option, Expr)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L127-L129 - - -If this expression is a let statement, returns the let pattern as an `Expr`, -the optional type annotation, and the assigned expression. - -### as_member_access - -```rust title="as_member_access" showLineNumbers -comptime fn as_member_access(self) -> Option<(Expr, Quoted)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L134-L136 - - -If this expression is a member access `foo.bar`, return the struct/tuple -expression and the field. The field will be represented as a quoted value. - -### as_method_call - -```rust title="as_method_call" showLineNumbers -comptime fn as_method_call(self) -> Option<(Expr, Quoted, [UnresolvedType], [Expr])> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L141-L143 - - -If this expression is a method call `foo.bar::(arg1, ..., argN)`, return -the receiver, method name, a slice of each generic argument, and a slice of each argument. - -### as_repeated_element_array - -```rust title="as_repeated_element_array" showLineNumbers -comptime fn as_repeated_element_array(self) -> Option<(Expr, Expr)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L148-L150 - - -If this expression is a repeated element array `[elem; length]`, return -the repeated element and the length expressions. - -### as_repeated_element_slice - -```rust title="as_repeated_element_slice" showLineNumbers -comptime fn as_repeated_element_slice(self) -> Option<(Expr, Expr)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L155-L157 - - -If this expression is a repeated element slice `[elem; length]`, return -the repeated element and the length expressions. - -### as_slice - -```rust title="as_slice" showLineNumbers -comptime fn as_slice(self) -> Option<[Expr]> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L162-L164 - - -If this expression is a slice literal `&[elem1, ..., elemN]`, -return each element of the slice. - -### as_tuple - -```rust title="as_tuple" showLineNumbers -comptime fn as_tuple(self) -> Option<[Expr]> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L169-L171 - - -If this expression is a tuple `(field1, ..., fieldN)`, -return each element of the tuple. - -### as_unary_op - -```rust title="as_unary_op" showLineNumbers -comptime fn as_unary_op(self) -> Option<(UnaryOp, Expr)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L176-L178 - - -If this expression is a unary operation ` `, -return the unary operator as well as the right-hand side expression. - -### as_unsafe - -```rust title="as_unsafe" showLineNumbers -comptime fn as_unsafe(self) -> Option<[Expr]> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L183-L185 - - -If this expression is an `unsafe { stmt1; ...; stmtN }` block, -return each statement inside in a slice. - -### has_semicolon - -```rust title="has_semicolon" showLineNumbers -comptime fn has_semicolon(self) -> bool {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L204-L206 - - -`true` if this expression is trailed by a semicolon. E.g. - -``` -comptime { - let expr1 = quote { 1 + 2 }.as_expr().unwrap(); - let expr2 = quote { 1 + 2; }.as_expr().unwrap(); - - assert(expr1.as_binary_op().is_some()); - assert(expr2.as_binary_op().is_some()); - - assert(!expr1.has_semicolon()); - assert(expr2.has_semicolon()); -} -``` - -### is_break - -```rust title="is_break" showLineNumbers -comptime fn is_break(self) -> bool {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L210-L212 - - -`true` if this expression is `break`. - -### is_continue - -```rust title="is_continue" showLineNumbers -comptime fn is_continue(self) -> bool {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L216-L218 - - -`true` if this expression is `continue`. - -### modify - -```rust title="modify" showLineNumbers -comptime fn modify(self, f: fn[Env](Expr) -> Option) -> Expr { -``` -> Source code: noir_stdlib/src/meta/expr.nr#L227-L229 - - -Applies a mapping function to this expression and to all of its sub-expressions. -`f` will be applied to each sub-expression first, then applied to the expression itself. - -This happens recursively for every expression within `self`. - -For example, calling `modify` on `(&[1], &[2, 3])` with an `f` that returns `Option::some` -for expressions that are integers, doubling them, would return `(&[2], &[4, 6])`. - -### quoted - -```rust title="quoted" showLineNumbers -comptime fn quoted(self) -> Quoted { -``` -> Source code: noir_stdlib/src/meta/expr.nr#L264-L266 - - -Returns this expression as a `Quoted` value. It's the same as `quote { $self }`. - -### resolve - -```rust title="resolve" showLineNumbers -comptime fn resolve(self, in_function: Option) -> TypedExpr {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L280-L282 - - -Resolves and type-checks this expression and returns the result as a `TypedExpr`. - -The `in_function` argument specifies where the expression is resolved: -- If it's `none`, the expression is resolved in the function where `resolve` was called -- If it's `some`, the expression is resolved in the given function - -If any names used by this expression are not in scope or if there are any type errors, -this will give compiler errors as if the expression was written directly into -the current `comptime` function. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.35.0/noir/standard_library/meta/function_def.md b/docs/versioned_docs/version-v0.35.0/noir/standard_library/meta/function_def.md deleted file mode 100644 index e490af68f7f..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/standard_library/meta/function_def.md +++ /dev/null @@ -1,166 +0,0 @@ ---- -title: FunctionDefinition ---- - -`std::meta::function_def` contains methods on the built-in `FunctionDefinition` type representing -a function definition in the source program. - -## Methods - -### add_attribute - -```rust title="add_attribute" showLineNumbers -comptime fn add_attribute(self, attribute: str) {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L3-L5 - - -Adds an attribute to the function. This is only valid -on functions in the current crate which have not yet been resolved. -This means any functions called at compile-time are invalid targets for this method. - -### body - -```rust title="body" showLineNumbers -comptime fn body(self) -> Expr {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L8-L10 - - -Returns the body of the function as an expression. This is only valid -on functions in the current crate which have not yet been resolved. -This means any functions called at compile-time are invalid targets for this method. - -### has_named_attribute - -```rust title="has_named_attribute" showLineNumbers -comptime fn has_named_attribute(self, name: str) -> bool {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L13-L15 - - -Returns true if this function has a custom attribute with the given name. - -### is_unconstrained - -```rust title="is_unconstrained" showLineNumbers -comptime fn is_unconstrained(self) -> bool {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L18-L20 - - -Returns true if this function is unconstrained. - -### module - -```rust title="module" showLineNumbers -comptime fn module(self) -> Module {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L23-L25 - - -Returns the module where the function is defined. - -### name - -```rust title="name" showLineNumbers -comptime fn name(self) -> Quoted {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L28-L30 - - -Returns the name of the function. - -### parameters - -```rust title="parameters" showLineNumbers -comptime fn parameters(self) -> [(Quoted, Type)] {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L33-L35 - - -Returns each parameter of the function as a tuple of (parameter pattern, parameter type). - -### return_type - -```rust title="return_type" showLineNumbers -comptime fn return_type(self) -> Type {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L38-L40 - - -The return type of the function. - -### set_body - -```rust title="set_body" showLineNumbers -comptime fn set_body(self, body: Expr) {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L43-L45 - - -Mutate the function body to a new expression. This is only valid -on functions in the current crate which have not yet been resolved. -This means any functions called at compile-time are invalid targets for this method. - -### set_parameters - -```rust title="set_parameters" showLineNumbers -comptime fn set_parameters(self, parameters: [(Quoted, Type)]) {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L48-L50 - - -Mutates the function's parameters to a new set of parameters. This is only valid -on functions in the current crate which have not yet been resolved. -This means any functions called at compile-time are invalid targets for this method. - -Expects a slice of (parameter pattern, parameter type) for each parameter. Requires -each parameter pattern to be a syntactically valid parameter. - -### set_return_type - -```rust title="set_return_type" showLineNumbers -comptime fn set_return_type(self, return_type: Type) {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L53-L55 - - -Mutates the function's return type to a new type. This is only valid -on functions in the current crate which have not yet been resolved. -This means any functions called at compile-time are invalid targets for this method. - -### set_return_public - -```rust title="set_return_public" showLineNumbers -comptime fn set_return_public(self, public: bool) {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L58-L60 - - -Mutates the function's return visibility to public (if `true` is given) or private (if `false` is given). -This is only valid on functions in the current crate which have not yet been resolved. -This means any functions called at compile-time are invalid targets for this method. - -### set_unconstrained - -```rust title="set_unconstrained" showLineNumbers -comptime fn set_unconstrained(self, value: bool) {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L63-L65 - - -Mutates the function to be unconstrained (if `true` is given) or not (if `false` is given). -This is only valid on functions in the current crate which have not yet been resolved. -This means any functions called at compile-time are invalid targets for this method. - -## Trait Implementations - -```rust -impl Eq for FunctionDefinition -impl Hash for FunctionDefinition -``` - -Note that each function is assigned a unique ID internally and this is what is used for -equality and hashing. So even functions with identical signatures and bodies may not -be equal in this sense if they were originally different items in the source program. diff --git a/docs/versioned_docs/version-v0.35.0/noir/standard_library/meta/index.md b/docs/versioned_docs/version-v0.35.0/noir/standard_library/meta/index.md deleted file mode 100644 index f81c89e05ae..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/standard_library/meta/index.md +++ /dev/null @@ -1,210 +0,0 @@ ---- -title: Metaprogramming -description: Noir's Metaprogramming API -keywords: [metaprogramming, comptime, macros, macro, quote, unquote] ---- - -`std::meta` is the entry point for Noir's metaprogramming API. This consists of `comptime` functions -and types used for inspecting and modifying Noir programs. - -## Functions - -### type_of - -```rust title="type_of" showLineNumbers -pub comptime fn type_of(x: T) -> Type {} -``` -> Source code: noir_stdlib/src/meta/mod.nr#L27-L29 - - -Returns the type of a variable at compile-time. - -Example: -```rust -comptime { - let x: i32 = 1; - let x_type: Type = std::meta::type_of(x); - - assert_eq(x_type, quote { i32 }.as_type()); -} -``` - -### unquote - -```rust title="unquote" showLineNumbers -pub comptime fn unquote(code: Quoted) -> Quoted { -``` -> Source code: noir_stdlib/src/meta/mod.nr#L19-L21 - - -Unquotes the passed-in token stream where this function was called. - -Example: -```rust -comptime { - let code = quote { 1 + 2 }; - - // let x = 1 + 2; - let x = unquote!(code); -} -``` - -### derive - -```rust title="derive" showLineNumbers -#[varargs] -pub comptime fn derive(s: StructDefinition, traits: [TraitDefinition]) -> Quoted { -``` -> Source code: noir_stdlib/src/meta/mod.nr#L47-L50 - - -Attribute placed on struct definitions. - -Creates a trait impl for each trait passed in as an argument. -To do this, the trait must have a derive handler registered -with `derive_via` beforehand. The traits in the stdlib that -can be derived this way are `Eq`, `Ord`, `Default`, and `Hash`. - -Example: -```rust -#[derive(Eq, Default)] -struct Foo { - x: i32, - y: T, -} - -fn main() { - let foo1 = Foo::default(); - let foo2 = Foo { x: 0, y: &[0] }; - assert_eq(foo1, foo2); -} -``` - -### derive_via - -```rust title="derive_via_signature" showLineNumbers -pub comptime fn derive_via(t: TraitDefinition, f: DeriveFunction) { -``` -> Source code: noir_stdlib/src/meta/mod.nr#L69-L71 - - -Attribute placed on trait definitions. - -Registers a function to create impls for the given trait -when the trait is used in a `derive` call. Users may use -this to register their own functions to enable their traits -to be derived by `derive`. - -Because this function requires a function as an argument which -should produce a trait impl for any given struct, users may find -it helpful to use a function like `std::meta::make_trait_impl` to -help creating these impls. - -Example: -```rust -#[derive_via(derive_do_nothing)] -trait DoNothing { - fn do_nothing(self); -} - -comptime fn derive_do_nothing(s: StructDefinition) -> Quoted { - let typ = s.as_type(); - quote { - impl DoNothing for $typ { - fn do_nothing(self) { - println("Nothing"); - } - } - } -} -``` - -As another example, `derive_eq` in the stdlib is used to derive the `Eq` -trait for any struct. It makes use of `make_trait_impl` to do this: - -```rust title="derive_eq" showLineNumbers -comptime fn derive_eq(s: StructDefinition) -> Quoted { - let signature = quote { fn eq(_self: Self, _other: Self) -> bool }; - let for_each_field = |name| quote { (_self.$name == _other.$name) }; - let body = |fields| { - if s.fields().len() == 0 { - quote { true } - } else { - fields - } - }; - crate::meta::make_trait_impl(s, quote { Eq }, signature, for_each_field, quote { & }, body) -} -``` -> Source code: noir_stdlib/src/cmp.nr#L10-L23 - - -### make_trait_impl - -```rust title="make_trait_impl" showLineNumbers -pub comptime fn make_trait_impl( - s: StructDefinition, - trait_name: Quoted, - function_signature: Quoted, - for_each_field: fn[Env1](Quoted) -> Quoted, - join_fields_with: Quoted, - body: fn[Env2](Quoted) -> Quoted -) -> Quoted { -``` -> Source code: noir_stdlib/src/meta/mod.nr#L88-L97 - - -A helper function to more easily create trait impls while deriving traits. - -Note that this function only works for traits which: -1. Have only one method -2. Have no generics on the trait itself. - - E.g. Using this on a trait such as `trait Foo { ... }` will result in the - generated impl incorrectly missing the `T` generic. - -If your trait fits these criteria then `make_trait_impl` is likely the easiest -way to write your derive handler. The arguments are as follows: - -- `s`: The struct to make the impl for -- `trait_name`: The name of the trait to derive. E.g. `quote { Eq }`. -- `function_signature`: The signature of the trait method to derive. E.g. `fn eq(self, other: Self) -> bool`. -- `for_each_field`: An operation to be performed on each field. E.g. `|name| quote { (self.$name == other.$name) }`. -- `join_fields_with`: A separator to join each result of `for_each_field` with. - E.g. `quote { & }`. You can also use an empty `quote {}` for no separator. -- `body`: The result of the field operations are passed into this function for any final processing. - This is the place to insert any setup/teardown code the trait requires. If the trait doesn't require - any such code, you can return the body as-is: `|body| body`. - -Example deriving `Hash`: - -```rust title="derive_hash" showLineNumbers -comptime fn derive_hash(s: StructDefinition) -> Quoted { - let name = quote { Hash }; - let signature = quote { fn hash(_self: Self, _state: &mut H) where H: std::hash::Hasher }; - let for_each_field = |name| quote { _self.$name.hash(_state); }; - crate::meta::make_trait_impl(s, name, signature, for_each_field, quote {}, |fields| fields) -} -``` -> Source code: noir_stdlib/src/hash/mod.nr#L144-L151 - - -Example deriving `Ord`: - -```rust title="derive_ord" showLineNumbers -comptime fn derive_ord(s: StructDefinition) -> Quoted { - let signature = quote { fn cmp(_self: Self, _other: Self) -> std::cmp::Ordering }; - let for_each_field = |name| quote { - if result == std::cmp::Ordering::equal() { - result = _self.$name.cmp(_other.$name); - } - }; - let body = |fields| quote { - let mut result = std::cmp::Ordering::equal(); - $fields - result - }; - crate::meta::make_trait_impl(s, quote { Ord }, signature, for_each_field, quote {}, body) -} -``` -> Source code: noir_stdlib/src/cmp.nr#L181-L196 - diff --git a/docs/versioned_docs/version-v0.35.0/noir/standard_library/meta/module.md b/docs/versioned_docs/version-v0.35.0/noir/standard_library/meta/module.md deleted file mode 100644 index efd3e61e125..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/standard_library/meta/module.md +++ /dev/null @@ -1,82 +0,0 @@ ---- -title: Module ---- - -`std::meta::module` contains methods on the built-in `Module` type which represents a module in the source program. -Note that this type represents a module generally, it isn't limited to only `mod my_submodule { ... }` -declarations in the source program. - -## Methods - -### add_item - -```rust title="add_item" showLineNumbers -comptime fn add_item(self, item: Quoted) {} -``` -> Source code: noir_stdlib/src/meta/module.nr#L3-L5 - - -Adds a top-level item (a function, a struct, a global, etc.) to the module. -Adding multiple items in one go is also valid if the `Quoted` value has multiple items in it. -Note that the items are type-checked as if they are inside the module they are being added to. - -### functions - -```rust title="functions" showLineNumbers -comptime fn functions(self) -> [FunctionDefinition] {} -``` -> Source code: noir_stdlib/src/meta/module.nr#L18-L20 - - -Returns each function defined in the module. - -### has_named_attribute - -```rust title="has_named_attribute" showLineNumbers -comptime fn has_named_attribute(self, name: str) -> bool {} -``` -> Source code: noir_stdlib/src/meta/module.nr#L8-L10 - - -Returns true if this module has a custom attribute with the given name. - -### is_contract - -```rust title="is_contract" showLineNumbers -comptime fn is_contract(self) -> bool {} -``` -> Source code: noir_stdlib/src/meta/module.nr#L13-L15 - - -`true` if this module is a contract module (was declared via `contract foo { ... }`). - -### name - -```rust title="name" showLineNumbers -comptime fn name(self) -> Quoted {} -``` -> Source code: noir_stdlib/src/meta/module.nr#L28-L30 - - -Returns the name of the module. - -### structs - -```rust title="structs" showLineNumbers -comptime fn structs(self) -> [StructDefinition] {} -``` -> Source code: noir_stdlib/src/meta/module.nr#L23-L25 - - -Returns each struct defined in the module. - -## Trait Implementations - -```rust -impl Eq for Module -impl Hash for Module -``` - -Note that each module is assigned a unique ID internally and this is what is used for -equality and hashing. So even modules with identical names and contents may not -be equal in this sense if they were originally different items in the source program. diff --git a/docs/versioned_docs/version-v0.35.0/noir/standard_library/meta/op.md b/docs/versioned_docs/version-v0.35.0/noir/standard_library/meta/op.md deleted file mode 100644 index 18d1f0768fd..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/standard_library/meta/op.md +++ /dev/null @@ -1,244 +0,0 @@ ---- -title: UnaryOp and BinaryOp ---- - -`std::meta::op` contains the `UnaryOp` and `BinaryOp` types as well as methods on them. -These types are used to represent a unary or binary operator respectively in Noir source code. - -## Types - -### UnaryOp - -Represents a unary operator. One of `-`, `!`, `&mut`, or `*`. - -### Methods - -#### is_minus - -```rust title="is_minus" showLineNumbers -pub fn is_minus(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L7-L9 - - -Returns `true` if this operator is `-`. - -#### is_not - -```rust title="is_not" showLineNumbers -pub fn is_not(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L13-L15 - - -`true` if this operator is `!` - -#### is_mutable_reference - -```rust title="is_mutable_reference" showLineNumbers -pub fn is_mutable_reference(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L19-L21 - - -`true` if this operator is `&mut` - -#### is_dereference - -```rust title="is_dereference" showLineNumbers -pub fn is_dereference(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L25-L27 - - -`true` if this operator is `*` - -#### quoted - -```rust title="unary_quoted" showLineNumbers -pub comptime fn quoted(self) -> Quoted { -``` -> Source code: noir_stdlib/src/meta/op.nr#L31-L33 - - -Returns this operator as a `Quoted` value. - -### Trait Implementations - -```rust -impl Eq for UnaryOp -impl Hash for UnaryOp -``` - -### BinaryOp - -Represents a binary operator. One of `+`, `-`, `*`, `/`, `%`, `==`, `!=`, `<`, `<=`, `>`, `>=`, `&`, `|`, `^`, `>>`, or `<<`. - -### Methods - -#### is_add - -```rust title="is_add" showLineNumbers -pub fn is_add(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L55-L57 - - -`true` if this operator is `+` - -#### is_subtract - -```rust title="is_subtract" showLineNumbers -pub fn is_subtract(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L61-L63 - - -`true` if this operator is `-` - -#### is_multiply - -```rust title="is_multiply" showLineNumbers -pub fn is_multiply(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L67-L69 - - -`true` if this operator is `*` - -#### is_divide - -```rust title="is_divide" showLineNumbers -pub fn is_divide(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L73-L75 - - -`true` if this operator is `/` - -#### is_modulo - -```rust title="is_modulo" showLineNumbers -pub fn is_modulo(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L145-L147 - - -`true` if this operator is `%` - -#### is_equal - -```rust title="is_equal" showLineNumbers -pub fn is_equal(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L79-L81 - - -`true` if this operator is `==` - -#### is_not_equal - -```rust title="is_not_equal" showLineNumbers -pub fn is_not_equal(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L85-L87 - - -`true` if this operator is `!=` - -#### is_less_than - -```rust title="is_less_than" showLineNumbers -pub fn is_less_than(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L91-L93 - - -`true` if this operator is `<` - -#### is_less_than_or_equal - -```rust title="is_less_than_or_equal" showLineNumbers -pub fn is_less_than_or_equal(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L97-L99 - - -`true` if this operator is `<=` - -#### is_greater_than - -```rust title="is_greater_than" showLineNumbers -pub fn is_greater_than(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L103-L105 - - -`true` if this operator is `>` - -#### is_greater_than_or_equal - -```rust title="is_greater_than_or_equal" showLineNumbers -pub fn is_greater_than_or_equal(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L109-L111 - - -`true` if this operator is `>=` - -#### is_and - -```rust title="is_and" showLineNumbers -pub fn is_and(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L115-L117 - - -`true` if this operator is `&` - -#### is_or - -```rust title="is_or" showLineNumbers -pub fn is_or(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L121-L123 - - -`true` if this operator is `|` - -#### is_shift_right - -```rust title="is_shift_right" showLineNumbers -pub fn is_shift_right(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L133-L135 - - -`true` if this operator is `>>` - -#### is_shift_left - -```rust title="is_shift_right" showLineNumbers -pub fn is_shift_right(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L133-L135 - - -`true` if this operator is `<<` - -#### quoted - -```rust title="binary_quoted" showLineNumbers -pub comptime fn quoted(self) -> Quoted { -``` -> Source code: noir_stdlib/src/meta/op.nr#L151-L153 - - -Returns this operator as a `Quoted` value. - -### Trait Implementations - -```rust -impl Eq for BinaryOp -impl Hash for BinaryOp -``` diff --git a/docs/versioned_docs/version-v0.35.0/noir/standard_library/meta/quoted.md b/docs/versioned_docs/version-v0.35.0/noir/standard_library/meta/quoted.md deleted file mode 100644 index cb779bca98f..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/standard_library/meta/quoted.md +++ /dev/null @@ -1,138 +0,0 @@ ---- -title: Quoted ---- - -`std::meta::quoted` contains methods on the built-in `Quoted` type which represents -quoted token streams and is the result of the `quote { ... }` expression. - -## Methods - -### as_expr - -```rust title="as_expr" showLineNumbers -comptime fn as_expr(self) -> Option {} -``` -> Source code: noir_stdlib/src/meta/quoted.nr#L6-L8 - - -Parses the quoted token stream as an expression. Returns `Option::none()` if -the expression failed to parse. - -Example: - -```rust title="as_expr_example" showLineNumbers -#[test] - fn test_expr_as_function_call() { - comptime - { - let expr = quote { foo(42) }.as_expr().unwrap(); - let (_function, args) = expr.as_function_call().unwrap(); - assert_eq(args.len(), 1); - assert_eq(args[0].as_integer().unwrap(), (42, false)); - } - } -``` -> Source code: test_programs/noir_test_success/comptime_expr/src/main.nr#L360-L371 - - -### as_module - -```rust title="as_module" showLineNumbers -comptime fn as_module(self) -> Option {} -``` -> Source code: noir_stdlib/src/meta/quoted.nr#L11-L13 - - -Interprets this token stream as a module path leading to the name of a module. -Returns `Option::none()` if the module isn't found or this token stream cannot be parsed as a path. - -Example: - -```rust title="as_module_example" showLineNumbers -mod baz { - pub mod qux {} -} - -#[test] -fn as_module_test() { - comptime - { - let my_mod = quote { baz::qux }.as_module().unwrap(); - assert_eq(my_mod.name(), quote { qux }); - } -} -``` -> Source code: test_programs/compile_success_empty/comptime_module/src/main.nr#L116-L129 - - -### as_trait_constraint - -```rust title="as_trait_constraint" showLineNumbers -comptime fn as_trait_constraint(self) -> TraitConstraint {} -``` -> Source code: noir_stdlib/src/meta/quoted.nr#L16-L18 - - -Interprets this token stream as a trait constraint (without an object type). -Note that this function panics instead of returning `Option::none()` if the token -stream does not parse and resolve to a valid trait constraint. - -Example: - -```rust title="implements_example" showLineNumbers -fn function_with_where(_x: T) where T: SomeTrait { - comptime - { - let t = quote { T }.as_type(); - let some_trait_i32 = quote { SomeTrait }.as_trait_constraint(); - assert(t.implements(some_trait_i32)); - - assert(t.get_trait_impl(some_trait_i32).is_none()); - } -} -``` -> Source code: test_programs/compile_success_empty/comptime_type/src/main.nr#L154-L165 - - -### as_type - -```rust title="as_type" showLineNumbers -comptime fn as_type(self) -> Type {} -``` -> Source code: noir_stdlib/src/meta/quoted.nr#L21-L23 - - -Interprets this token stream as a resolved type. Panics if the token -stream doesn't parse to a type or if the type isn't a valid type in scope. - -```rust title="implements_example" showLineNumbers -fn function_with_where(_x: T) where T: SomeTrait { - comptime - { - let t = quote { T }.as_type(); - let some_trait_i32 = quote { SomeTrait }.as_trait_constraint(); - assert(t.implements(some_trait_i32)); - - assert(t.get_trait_impl(some_trait_i32).is_none()); - } -} -``` -> Source code: test_programs/compile_success_empty/comptime_type/src/main.nr#L154-L165 - - -### tokens - -```rust title="tokens" showLineNumbers -comptime fn tokens(self) -> [Quoted] {} -``` -> Source code: noir_stdlib/src/meta/quoted.nr#L26-L28 - - -Returns a slice of the individual tokens that form this token stream. - -## Trait Implementations - -```rust -impl Eq for Quoted -impl Hash for Quoted -``` diff --git a/docs/versioned_docs/version-v0.35.0/noir/standard_library/meta/struct_def.md b/docs/versioned_docs/version-v0.35.0/noir/standard_library/meta/struct_def.md deleted file mode 100644 index 212c636d12a..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/standard_library/meta/struct_def.md +++ /dev/null @@ -1,177 +0,0 @@ ---- -title: StructDefinition ---- - -`std::meta::struct_def` contains methods on the built-in `StructDefinition` type. -This type corresponds to `struct Name { field1: Type1, ... }` items in the source program. - -## Methods - -### add_attribute - -```rust title="add_attribute" showLineNumbers -comptime fn add_attribute(self, attribute: str) {} -``` -> Source code: noir_stdlib/src/meta/struct_def.nr#L3-L5 - - -Adds an attribute to the struct. - -### add_generic - -```rust title="add_generic" showLineNumbers -comptime fn add_generic(self, generic_name: str) -> Type {} -``` -> Source code: noir_stdlib/src/meta/struct_def.nr#L8-L10 - - -Adds an generic to the struct. Returns the new generic type. -Errors if the given generic name isn't a single identifier or if -the struct already has a generic with the same name. - -This method should be used carefully, if there is existing code referring -to the struct type it may be checked before this function is called and -see the struct with the original number of generics. This method should -thus be preferred to use on code generated from other macros and structs -that are not used in function signatures. - -Example: - -```rust title="add-generic-example" showLineNumbers -comptime fn add_generic(s: StructDefinition) { - assert_eq(s.generics().len(), 0); - let new_generic = s.add_generic("T"); - - let generics = s.generics(); - assert_eq(generics.len(), 1); - assert_eq(generics[0], new_generic); - } -``` -> Source code: test_programs/compile_success_empty/comptime_struct_definition/src/main.nr#L38-L47 - - -### as_type - -```rust title="as_type" showLineNumbers -comptime fn as_type(self) -> Type {} -``` -> Source code: noir_stdlib/src/meta/struct_def.nr#L15-L17 - - -Returns this struct as a type in the source program. If this struct has -any generics, the generics are also included as-is. - -### generics - -```rust title="generics" showLineNumbers -comptime fn generics(self) -> [Type] {} -``` -> Source code: noir_stdlib/src/meta/struct_def.nr#L26-L28 - - -Returns each generic on this struct. - -Example: - -``` -#[example] -struct Foo { - bar: [T; 2], - baz: Baz, -} - -comptime fn example(foo: StructDefinition) { - assert_eq(foo.generics().len(), 2); - - // Fails because `T` isn't in scope - // let t = quote { T }.as_type(); - // assert_eq(foo.generics()[0], t); -} -``` - -### fields - -```rust title="fields" showLineNumbers -comptime fn fields(self) -> [(Quoted, Type)] {} -``` -> Source code: noir_stdlib/src/meta/struct_def.nr#L33-L35 - - -Returns each field of this struct as a pair of (field name, field type). - -### has_named_attribute - -```rust title="has_named_attribute" showLineNumbers -comptime fn has_named_attribute(self, name: str) -> bool {} -``` -> Source code: noir_stdlib/src/meta/struct_def.nr#L20-L22 - - -Returns true if this struct has a custom attribute with the given name. - -### module - -```rust title="module" showLineNumbers -comptime fn module(self) -> Module {} -``` -> Source code: noir_stdlib/src/meta/struct_def.nr#L38-L40 - - -Returns the module where the struct is defined. - -### name - -```rust title="name" showLineNumbers -comptime fn name(self) -> Quoted {} -``` -> Source code: noir_stdlib/src/meta/struct_def.nr#L43-L45 - - -Returns the name of this struct - -Note that the returned quoted value will be just the struct name, it will -not be the full path to the struct, nor will it include any generics. - -### set_fields - -```rust title="set_fields" showLineNumbers -comptime fn set_fields(self, new_fields: [(Quoted, Type)]) {} -``` -> Source code: noir_stdlib/src/meta/struct_def.nr#L52-L54 - - -Sets the fields of this struct to the given fields list where each element -is a pair of the field's name and the field's type. Expects each field name -to be a single identifier. Note that this will override any previous fields -on this struct. If those should be preserved, use `.fields()` to retrieve the -current fields on the struct type and append the new fields from there. - -Example: - -```rust -// Change this struct to: -// struct Foo { -// a: u32, -// b: i8, -// } -#[mangle_fields] -struct Foo { x: Field } - -comptime fn mangle_fields(s: StructDefinition) { - s.set_fields(&[ - (quote { a }, quote { u32 }.as_type()), - (quote { b }, quote { i8 }.as_type()), - ]); -} -``` - -## Trait Implementations - -```rust -impl Eq for StructDefinition -impl Hash for StructDefinition -``` - -Note that each struct is assigned a unique ID internally and this is what is used for -equality and hashing. So even structs with identical generics and fields may not -be equal in this sense if they were originally different items in the source program. diff --git a/docs/versioned_docs/version-v0.35.0/noir/standard_library/meta/trait_constraint.md b/docs/versioned_docs/version-v0.35.0/noir/standard_library/meta/trait_constraint.md deleted file mode 100644 index 3106f732b5a..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/standard_library/meta/trait_constraint.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -title: TraitConstraint ---- - -`std::meta::trait_constraint` contains methods on the built-in `TraitConstraint` type which represents -a trait constraint that can be used to search for a trait implementation. This is similar -syntactically to just the trait itself, but can also contain generic arguments. E.g. `Eq`, `Default`, -`BuildHasher`. - -This type currently has no public methods but it can be used alongside `Type` in `implements` or `get_trait_impl`. - -## Trait Implementations - -```rust -impl Eq for TraitConstraint -impl Hash for TraitConstraint -``` diff --git a/docs/versioned_docs/version-v0.35.0/noir/standard_library/meta/trait_def.md b/docs/versioned_docs/version-v0.35.0/noir/standard_library/meta/trait_def.md deleted file mode 100644 index a1f363d46ff..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/standard_library/meta/trait_def.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: TraitDefinition ---- - -`std::meta::trait_def` contains methods on the built-in `TraitDefinition` type. This type -represents trait definitions such as `trait Foo { .. }` at the top-level of a program. - -## Methods - -### as_trait_constraint - -```rust title="as_trait_constraint" showLineNumbers -comptime fn as_trait_constraint(_self: Self) -> TraitConstraint {} -``` -> Source code: noir_stdlib/src/meta/trait_def.nr#L6-L8 - - -Converts this trait into a trait constraint. If there are any generics on this -trait, they will be kept as-is without instantiating or replacing them. - -## Trait Implementations - -```rust -impl Eq for TraitDefinition -impl Hash for TraitDefinition -``` diff --git a/docs/versioned_docs/version-v0.35.0/noir/standard_library/meta/trait_impl.md b/docs/versioned_docs/version-v0.35.0/noir/standard_library/meta/trait_impl.md deleted file mode 100644 index 66d31ed2560..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/standard_library/meta/trait_impl.md +++ /dev/null @@ -1,60 +0,0 @@ ---- -title: TraitImpl ---- - -`std::meta::trait_impl` contains methods on the built-in `TraitImpl` type which represents a trait -implementation such as `impl Foo for Bar { ... }`. - -## Methods - -### trait_generic_args - -```rust title="trait_generic_args" showLineNumbers -comptime fn trait_generic_args(self) -> [Type] {} -``` -> Source code: noir_stdlib/src/meta/trait_impl.nr#L3-L5 - - -Returns any generic arguments on the trait of this trait implementation, if any. - -```rs -impl Foo for Bar { ... } - -comptime { - let bar_type = quote { Bar }.as_type(); - let foo = quote { Foo }.as_trait_constraint(); - - let my_impl: TraitImpl = bar_type.get_trait_impl(foo).unwrap(); - - let generics = my_impl.trait_generic_args(); - assert_eq(generics.len(), 2); - - assert_eq(generics[0], quote { i32 }.as_type()); - assert_eq(generics[1], quote { Field }.as_type()); -} -``` - -### methods - -```rust title="methods" showLineNumbers -comptime fn methods(self) -> [FunctionDefinition] {} -``` -> Source code: noir_stdlib/src/meta/trait_impl.nr#L8-L10 - - -Returns each method in this trait impl. - -Example: - -```rs -comptime { - let i32_type = quote { i32 }.as_type(); - let eq = quote { Eq }.as_trait_constraint(); - - let impl_eq_for_i32: TraitImpl = i32_type.get_trait_impl(eq).unwrap(); - let methods = impl_eq_for_i32.methods(); - - assert_eq(methods.len(), 1); - assert_eq(methods[0].name(), quote { eq }); -} -``` diff --git a/docs/versioned_docs/version-v0.35.0/noir/standard_library/meta/typ.md b/docs/versioned_docs/version-v0.35.0/noir/standard_library/meta/typ.md deleted file mode 100644 index 6c9f4b8d087..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/standard_library/meta/typ.md +++ /dev/null @@ -1,239 +0,0 @@ ---- -title: Type ---- - -`std::meta::typ` contains methods on the built-in `Type` type used for representing -a type in the source program. - -## Functions - -```rust title="fresh_type_variable" showLineNumbers -pub comptime fn fresh_type_variable() -> Type {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L5-L7 - - -Creates and returns an unbound type variable. This is a special kind of type internal -to type checking which will type check with any other type. When it is type checked -against another type it will also be set to that type. For example, if `a` is a type -variable and we have the type equality `(a, i32) = (u8, i32)`, the compiler will set -`a` equal to `u8`. - -Unbound type variables will often be rendered as `_` while printing them. Bound type -variables will appear as the type they are bound to. - -This can be used in conjunction with functions which internally perform type checks -such as `Type::implements` or `Type::get_trait_impl` to potentially grab some of the types used. - -Note that calling `Type::implements` or `Type::get_trait_impl` on a type variable will always -fail. - -Example: - -```rust title="serialize-setup" showLineNumbers -trait Serialize {} - -impl Serialize<1> for Field {} - -impl Serialize for [T; N] - where T: Serialize {} - -impl Serialize for (T, U) - where T: Serialize, U: Serialize {} -``` -> Source code: test_programs/compile_success_empty/comptime_type/src/main.nr#L20-L30 - -```rust title="fresh-type-variable-example" showLineNumbers -let typevar1 = std::meta::typ::fresh_type_variable(); - let constraint = quote { Serialize<$typevar1> }.as_trait_constraint(); - let field_type = quote { Field }.as_type(); - - // Search for a trait impl (binding typevar1 to 1 when the impl is found): - assert(field_type.implements(constraint)); - - // typevar1 should be bound to the "1" generic now: - assert_eq(typevar1.as_constant().unwrap(), 1); - - // If we want to do the same with a different type, we need to - // create a new type variable now that `typevar1` is bound - let typevar2 = std::meta::typ::fresh_type_variable(); - let constraint = quote { Serialize<$typevar2> }.as_trait_constraint(); - let array_type = quote { [(Field, Field); 5] }.as_type(); - assert(array_type.implements(constraint)); - - // Now typevar2 should be bound to the serialized pair size 2 times the array length 5 - assert_eq(typevar2.as_constant().unwrap(), 10); -``` -> Source code: test_programs/compile_success_empty/comptime_type/src/main.nr#L130-L150 - - -## Methods - -### as_array - -```rust title="as_array" showLineNumbers -comptime fn as_array(self) -> Option<(Type, Type)> {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L11-L13 - - -If this type is an array, return a pair of (element type, size type). - -Example: - -```rust -comptime { - let array_type = quote { [Field; 3] }.as_type(); - let (field_type, three_type) = array_type.as_array().unwrap(); - - assert(field_type.is_field()); - assert_eq(three_type.as_constant().unwrap(), 3); -} -``` - -### as_constant - -```rust title="as_constant" showLineNumbers -comptime fn as_constant(self) -> Option {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L16-L18 - - -If this type is a constant integer (such as the `3` in the array type `[Field; 3]`), -return the numeric constant. - -### as_integer - -```rust title="as_integer" showLineNumbers -comptime fn as_integer(self) -> Option<(bool, u8)> {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L21-L23 - - -If this is an integer type, return a boolean which is `true` -if the type is signed, as well as the number of bits of this integer type. - -### as_slice - -```rust title="as_slice" showLineNumbers -comptime fn as_slice(self) -> Option {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L26-L28 - - -If this is a slice type, return the element type of the slice. - -### as_str - -```rust title="as_str" showLineNumbers -comptime fn as_str(self) -> Option {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L31-L33 - - -If this is a `str` type, returns the length `N` as a type. - -### as_struct - -```rust title="as_struct" showLineNumbers -comptime fn as_struct(self) -> Option<(StructDefinition, [Type])> {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L36-L38 - - -If this is a struct type, returns the struct in addition to -any generic arguments on this type. - -### as_tuple - -```rust title="as_tuple" showLineNumbers -comptime fn as_tuple(self) -> Option<[Type]> {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L41-L43 - - -If this is a tuple type, returns each element type of the tuple. - -### get_trait_impl - -```rust title="get_trait_impl" showLineNumbers -comptime fn get_trait_impl(self, constraint: TraitConstraint) -> Option {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L46-L48 - - -Retrieves the trait implementation that implements the given -trait constraint for this type. If the trait constraint is not -found, `None` is returned. Note that since the concrete trait implementation -for a trait constraint specified from a `where` clause is unknown, -this function will return `None` in these cases. If you only want to know -whether a type implements a trait, use `implements` instead. - -Example: - -```rust -comptime { - let field_type = quote { Field }.as_type(); - let default = quote { Default }.as_trait_constraint(); - - let the_impl: TraitImpl = field_type.get_trait_impl(default).unwrap(); - assert(the_impl.methods().len(), 1); -} -``` - -### implements - -```rust title="implements" showLineNumbers -comptime fn implements(self, constraint: TraitConstraint) -> bool {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L51-L53 - - -`true` if this type implements the given trait. Note that unlike -`get_trait_impl` this will also return true for any `where` constraints -in scope. - -Example: - -```rust -fn foo() where T: Default { - comptime { - let field_type = quote { Field }.as_type(); - let default = quote { Default }.as_trait_constraint(); - assert(field_type.implements(default)); - - let t = quote { T }.as_type(); - assert(t.implements(default)); - } -} -``` - -### is_bool - -```rust title="is_bool" showLineNumbers -comptime fn is_bool(self) -> bool {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L56-L58 - - -`true` if this type is `bool`. - -### is_field - -```rust title="is_field" showLineNumbers -comptime fn is_field(self) -> bool {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L61-L63 - - -`true` if this type is `Field`. - -## Trait Implementations - -```rust -impl Eq for Type -impl Hash for Type -``` -Note that this is syntactic equality, this is not the same as whether two types will type check -to be the same type. Unless type inference or generics are being used however, users should not -typically have to worry about this distinction unless `std::meta::typ::fresh_type_variable` is used. diff --git a/docs/versioned_docs/version-v0.35.0/noir/standard_library/meta/typed_expr.md b/docs/versioned_docs/version-v0.35.0/noir/standard_library/meta/typed_expr.md deleted file mode 100644 index 1ee71c8b064..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/standard_library/meta/typed_expr.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -title: TypedExpr ---- - -`std::meta::typed_expr` contains methods on the built-in `TypedExpr` type for resolved and type-checked expressions. - -## Methods - -### get_type - -```rust title="as_function_definition" showLineNumbers -comptime fn as_function_definition(self) -> Option {} -``` -> Source code: noir_stdlib/src/meta/typed_expr.nr#L7-L9 - - -If this expression refers to a function definitions, returns it. Otherwise returns `Option::none()`. - -### get_type - -```rust title="get_type" showLineNumbers -comptime fn get_type(self) -> Option {} -``` -> Source code: noir_stdlib/src/meta/typed_expr.nr#L13-L15 - - -Returns the type of the expression, or `Option::none()` if there were errors when the expression was previously resolved. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.35.0/noir/standard_library/meta/unresolved_type.md b/docs/versioned_docs/version-v0.35.0/noir/standard_library/meta/unresolved_type.md deleted file mode 100644 index d6f2b1494bb..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/standard_library/meta/unresolved_type.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -title: UnresolvedType ---- - -`std::meta::unresolved_type` contains methods on the built-in `UnresolvedType` type for the syntax of types. - -## Methods - -### is_field - -```rust title="is_field" showLineNumbers -comptime fn is_field(self) -> bool {} -``` -> Source code: noir_stdlib/src/meta/unresolved_type.nr#L3-L5 - - -Returns true if this type refers to the Field type. diff --git a/docs/versioned_docs/version-v0.35.0/noir/standard_library/options.md b/docs/versioned_docs/version-v0.35.0/noir/standard_library/options.md deleted file mode 100644 index a1bd4e1de5f..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/standard_library/options.md +++ /dev/null @@ -1,101 +0,0 @@ ---- -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), -} -``` - -The `Option` type, already imported into your Noir program, can be used directly: - -```rust -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. - -### expect - -Asserts `self.is_some()` with a provided custom message and returns the contained `Some` value. The custom message is expected to be a format string. - -### 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.35.0/noir/standard_library/recursion.mdx b/docs/versioned_docs/version-v0.35.0/noir/standard_library/recursion.mdx deleted file mode 100644 index 60414a2fa51..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/standard_library/recursion.mdx +++ /dev/null @@ -1,85 +0,0 @@ ---- -title: Recursive Proofs -description: Learn about how to write recursive proofs in Noir. -keywords: [recursion, recursive proofs, verification_key, verify_proof] ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; - -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. - -Read [the explainer on recursion](../../explainers/explainer-recursion.md) to know more about this function and the [guide on how to use it.](../../how_to/how-to-recursion.md) - -## The `#[recursive]` Attribute - -In Noir, the `#[recursive]` attribute is used to indicate that a circuit is designed for recursive proof generation. When applied, it informs the compiler and the tooling that the circuit should be compiled in a way that makes its proofs suitable for recursive verification. This attribute eliminates the need for manual flagging of recursion at the tooling level, streamlining the proof generation process for recursive circuits. - -### Example usage with `#[recursive]` - -```rust -#[recursive] -fn main(x: Field, y: pub Field) { - assert(x == y, "x and y are not equal"); -} - -// This marks the circuit as recursion-friendly and indicates that proofs generated from this circuit -// are intended for recursive verification. -``` - -By incorporating this attribute directly in the circuit's definition, tooling like Nargo and NoirJS can automatically execute recursive-specific duties for Noir programs (e.g. recursive-friendly proof artifact generation) without additional flags or configurations. - -## Verifying Recursive Proofs - -```rust -#[foreign(recursive_aggregation)] -pub fn verify_proof(verification_key: [Field], proof: [Field], public_inputs: [Field], key_hash: Field) {} -``` - - - -## Example usage - -```rust - -fn main( - verification_key : [Field; 114], - proof : [Field; 93], - public_inputs : [Field; 1], - key_hash : Field, - proof_b : [Field; 93], -) { - std::verify_proof( - verification_key, - proof, - public_inputs, - key_hash - ); - - std::verify_proof( - verification_key, - proof_b, - public_inputs, - key_hash - ); -} -``` - -You can see a full example of recursive proofs in [this example recursion demo repo](https://github.com/noir-lang/noir-examples/tree/master/recursion). - -## 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. - -### `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. diff --git a/docs/versioned_docs/version-v0.35.0/noir/standard_library/traits.md b/docs/versioned_docs/version-v0.35.0/noir/standard_library/traits.md deleted file mode 100644 index 74ed6276234..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/standard_library/traits.md +++ /dev/null @@ -1,625 +0,0 @@ ---- -title: Traits -description: Noir's stdlib provides a few commonly used traits. -keywords: [traits, trait, interface, protocol, default, add, eq] ---- - -## `std::default` - -### `std::default::Default` - -```rust title="default-trait" showLineNumbers -pub trait Default { - fn default() -> Self; -} -``` -> Source code: noir_stdlib/src/default.nr#L4-L8 - - -Constructs a default value of a type. - -Implementations: -```rust -impl Default for Field { .. } - -impl Default for i8 { .. } -impl Default for i16 { .. } -impl Default for i32 { .. } -impl Default for i64 { .. } - -impl Default for u8 { .. } -impl Default for u16 { .. } -impl Default for u32 { .. } -impl Default for u64 { .. } - -impl Default for () { .. } -impl Default for bool { .. } - -impl Default for [T; N] - where T: Default { .. } - -impl Default for [T] { .. } - -impl Default for (A, B) - where A: Default, B: Default { .. } - -impl Default for (A, B, C) - where A: Default, B: Default, C: Default { .. } - -impl Default for (A, B, C, D) - where A: Default, B: Default, C: Default, D: Default { .. } - -impl Default for (A, B, C, D, E) - where A: Default, B: Default, C: Default, D: Default, E: Default { .. } -``` - -For primitive integer types, the return value of `default` is `0`. Container -types such as arrays are filled with default values of their element type, -except slices whose length is unknown and thus defaulted to zero. - ---- - -## `std::convert` - -### `std::convert::From` - -```rust title="from-trait" showLineNumbers -pub trait From { - fn from(input: T) -> Self; -} -``` -> Source code: noir_stdlib/src/convert.nr#L1-L5 - - -The `From` trait defines how to convert from a given type `T` to the type on which the trait is implemented. - -The Noir standard library provides a number of implementations of `From` between primitive types. -```rust title="from-impls" showLineNumbers -// Unsigned integers - -impl From for u32 { - fn from(value: u8) -> u32 { - value as u32 - } -} - -impl From for u64 { - fn from(value: u8) -> u64 { - value as u64 - } -} -impl From for u64 { - fn from(value: u32) -> u64 { - value as u64 - } -} - -impl From for Field { - fn from(value: u8) -> Field { - value as Field - } -} -impl From for Field { - fn from(value: u32) -> Field { - value as Field - } -} -impl From for Field { - fn from(value: u64) -> Field { - value as Field - } -} - -// Signed integers - -impl From for i32 { - fn from(value: i8) -> i32 { - value as i32 - } -} - -impl From for i64 { - fn from(value: i8) -> i64 { - value as i64 - } -} -impl From for i64 { - fn from(value: i32) -> i64 { - value as i64 - } -} - -// Booleans -impl From for u8 { - fn from(value: bool) -> u8 { - value as u8 - } -} -impl From for u32 { - fn from(value: bool) -> u32 { - value as u32 - } -} -impl From for u64 { - fn from(value: bool) -> u64 { - value as u64 - } -} -impl From for i8 { - fn from(value: bool) -> i8 { - value as i8 - } -} -impl From for i32 { - fn from(value: bool) -> i32 { - value as i32 - } -} -impl From for i64 { - fn from(value: bool) -> i64 { - value as i64 - } -} -impl From for Field { - fn from(value: bool) -> Field { - value as Field - } -} -``` -> Source code: noir_stdlib/src/convert.nr#L25-L116 - - -#### When to implement `From` - -As a general rule of thumb, `From` may be implemented in the [situations where it would be suitable in Rust](https://doc.rust-lang.org/std/convert/trait.From.html#when-to-implement-from): - -- The conversion is *infallible*: Noir does not provide an equivalent to Rust's `TryFrom`, if the conversion can fail then provide a named method instead. -- The conversion is *lossless*: semantically, it should not lose or discard information. For example, `u32: From` can losslessly convert any `u16` into a valid `u32` such that the original `u16` can be recovered. On the other hand, `u16: From` should not be implemented as `2**16` is a `u32` which cannot be losslessly converted into a `u16`. -- The conversion is *value-preserving*: the conceptual kind and meaning of the resulting value is the same, even though the Noir type and technical representation might be different. While it's possible to infallibly and losslessly convert a `u8` into a `str<2>` hex representation, `4u8` and `"04"` are too different for `str<2>: From` to be implemented. -- The conversion is *obvious*: it's the only reasonable conversion between the two types. If there's ambiguity on how to convert between them such that the same input could potentially map to two different values then a named method should be used. For instance rather than implementing `U128: From<[u8; 16]>`, the methods `U128::from_le_bytes` and `U128::from_be_bytes` are used as otherwise the endianness of the array would be ambiguous, resulting in two potential values of `U128` from the same byte array. - -One additional recommendation specific to Noir is: -- The conversion is *efficient*: it's relatively cheap to convert between the two types. Due to being a ZK DSL, it's more important to avoid unnecessary computation compared to Rust. If the implementation of `From` would encourage users to perform unnecessary conversion, resulting in additional proving time, then it may be preferable to expose functionality such that this conversion may be avoided. - -### `std::convert::Into` - -The `Into` trait is defined as the reciprocal of `From`. It should be easy to convince yourself that if we can convert to type `A` from type `B`, then it's possible to convert type `B` into type `A`. - -For this reason, implementing `From` on a type will automatically generate a matching `Into` implementation. One should always prefer implementing `From` over `Into` as implementing `Into` will not generate a matching `From` implementation. - -```rust title="into-trait" showLineNumbers -pub trait Into { - fn into(self) -> T; -} - -impl Into for U where T: From { - fn into(self) -> T { - T::from(self) - } -} -``` -> Source code: noir_stdlib/src/convert.nr#L13-L23 - - -`Into` is most useful when passing function arguments where the types don't quite match up with what the function expects. In this case, the compiler has enough type information to perform the necessary conversion by just appending `.into()` onto the arguments in question. - ---- - -## `std::cmp` - -### `std::cmp::Eq` - -```rust title="eq-trait" showLineNumbers -pub trait Eq { - fn eq(self, other: Self) -> bool; -} -``` -> Source code: noir_stdlib/src/cmp.nr#L4-L8 - - -Returns `true` if `self` is equal to `other`. Implementing this trait on a type -allows the type to be used with `==` and `!=`. - -Implementations: -```rust -impl Eq for Field { .. } - -impl Eq for i8 { .. } -impl Eq for i16 { .. } -impl Eq for i32 { .. } -impl Eq for i64 { .. } - -impl Eq for u8 { .. } -impl Eq for u16 { .. } -impl Eq for u32 { .. } -impl Eq for u64 { .. } - -impl Eq for () { .. } -impl Eq for bool { .. } - -impl Eq for [T; N] - where T: Eq { .. } - -impl Eq for [T] - where T: Eq { .. } - -impl Eq for (A, B) - where A: Eq, B: Eq { .. } - -impl Eq for (A, B, C) - where A: Eq, B: Eq, C: Eq { .. } - -impl Eq for (A, B, C, D) - where A: Eq, B: Eq, C: Eq, D: Eq { .. } - -impl Eq for (A, B, C, D, E) - where A: Eq, B: Eq, C: Eq, D: Eq, E: Eq { .. } -``` - -### `std::cmp::Ord` - -```rust title="ord-trait" showLineNumbers -pub trait Ord { - fn cmp(self, other: Self) -> Ordering; -} -``` -> Source code: noir_stdlib/src/cmp.nr#L175-L179 - - -`a.cmp(b)` compares two values returning `Ordering::less()` if `a < b`, -`Ordering::equal()` if `a == b`, or `Ordering::greater()` if `a > b`. -Implementing this trait on a type allows `<`, `<=`, `>`, and `>=` to be -used on values of the type. - -`std::cmp` also provides `max` and `min` functions for any type which implements the `Ord` trait. - -Implementations: - -```rust -impl Ord for u8 { .. } -impl Ord for u16 { .. } -impl Ord for u32 { .. } -impl Ord for u64 { .. } - -impl Ord for i8 { .. } -impl Ord for i16 { .. } -impl Ord for i32 { .. } - -impl Ord for i64 { .. } - -impl Ord for () { .. } -impl Ord for bool { .. } - -impl Ord for [T; N] - where T: Ord { .. } - -impl Ord for [T] - where T: Ord { .. } - -impl Ord for (A, B) - where A: Ord, B: Ord { .. } - -impl Ord for (A, B, C) - where A: Ord, B: Ord, C: Ord { .. } - -impl Ord for (A, B, C, D) - where A: Ord, B: Ord, C: Ord, D: Ord { .. } - -impl Ord for (A, B, C, D, E) - where A: Ord, B: Ord, C: Ord, D: Ord, E: Ord { .. } -``` - ---- - -## `std::ops` - -### `std::ops::Add`, `std::ops::Sub`, `std::ops::Mul`, and `std::ops::Div` - -These traits abstract over addition, subtraction, multiplication, and division respectively. -Implementing these traits for a given type will also allow that type to be used with the corresponding operator -for that trait (`+` for Add, etc) in addition to the normal method names. - -```rust title="add-trait" showLineNumbers -pub trait Add { - fn add(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/arith.nr#L1-L5 - -```rust title="sub-trait" showLineNumbers -pub trait Sub { - fn sub(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/arith.nr#L60-L64 - -```rust title="mul-trait" showLineNumbers -pub trait Mul { - fn mul(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/arith.nr#L119-L123 - -```rust title="div-trait" showLineNumbers -pub trait Div { - fn div(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/arith.nr#L178-L182 - - -The implementations block below is given for the `Add` trait, but the same types that implement -`Add` also implement `Sub`, `Mul`, and `Div`. - -Implementations: -```rust -impl Add for Field { .. } - -impl Add for i8 { .. } -impl Add for i16 { .. } -impl Add for i32 { .. } -impl Add for i64 { .. } - -impl Add for u8 { .. } -impl Add for u16 { .. } -impl Add for u32 { .. } -impl Add for u64 { .. } -``` - -### `std::ops::Rem` - -```rust title="rem-trait" showLineNumbers -pub trait Rem { - fn rem(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/arith.nr#L237-L241 - - -`Rem::rem(a, b)` is the remainder function returning the result of what is -left after dividing `a` and `b`. Implementing `Rem` allows the `%` operator -to be used with the implementation type. - -Unlike other numeric traits, `Rem` is not implemented for `Field`. - -Implementations: -```rust -impl Rem for u8 { fn rem(self, other: u8) -> u8 { self % other } } -impl Rem for u16 { fn rem(self, other: u16) -> u16 { self % other } } -impl Rem for u32 { fn rem(self, other: u32) -> u32 { self % other } } -impl Rem for u64 { fn rem(self, other: u64) -> u64 { self % other } } - -impl Rem for i8 { fn rem(self, other: i8) -> i8 { self % other } } -impl Rem for i16 { fn rem(self, other: i16) -> i16 { self % other } } -impl Rem for i32 { fn rem(self, other: i32) -> i32 { self % other } } -impl Rem for i64 { fn rem(self, other: i64) -> i64 { self % other } } -``` - -### `std::ops::Neg` - -```rust title="neg-trait" showLineNumbers -pub trait Neg { - fn neg(self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/arith.nr#L290-L294 - - -`Neg::neg` is equivalent to the unary negation operator `-`. - -Implementations: -```rust title="neg-trait-impls" showLineNumbers -impl Neg for Field { - fn neg(self) -> Field { - -self - } -} - -impl Neg for i8 { - fn neg(self) -> i8 { - -self - } -} -impl Neg for i16 { - fn neg(self) -> i16 { - -self - } -} -impl Neg for i32 { - fn neg(self) -> i32 { - -self - } -} -impl Neg for i64 { - fn neg(self) -> i64 { - -self - } -} -``` -> Source code: noir_stdlib/src/ops/arith.nr#L296-L323 - - -### `std::ops::Not` - -```rust title="not-trait" showLineNumbers -pub trait Not { - fn not(self: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/bit.nr#L1-L5 - - -`Not::not` is equivalent to the unary bitwise NOT operator `!`. - -Implementations: -```rust title="not-trait-impls" showLineNumbers -impl Not for bool { - fn not(self) -> bool { - !self - } -} - -impl Not for u64 { - fn not(self) -> u64 { - !self - } -} -impl Not for u32 { - fn not(self) -> u32 { - !self - } -} -impl Not for u16 { - fn not(self) -> u16 { - !self - } -} -impl Not for u8 { - fn not(self) -> u8 { - !self - } -} -impl Not for u1 { - fn not(self) -> u1 { - !self - } -} - -impl Not for i8 { - fn not(self) -> i8 { - !self - } -} -impl Not for i16 { - fn not(self) -> i16 { - !self - } -} -impl Not for i32 { - fn not(self) -> i32 { - !self - } -} -impl Not for i64 { - fn not(self) -> i64 { - !self - } -} -``` -> Source code: noir_stdlib/src/ops/bit.nr#L7-L60 - - -### `std::ops::{ BitOr, BitAnd, BitXor }` - -```rust title="bitor-trait" showLineNumbers -pub trait BitOr { - fn bitor(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/bit.nr#L62-L66 - -```rust title="bitand-trait" showLineNumbers -pub trait BitAnd { - fn bitand(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/bit.nr#L121-L125 - -```rust title="bitxor-trait" showLineNumbers -pub trait BitXor { - fn bitxor(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/bit.nr#L180-L184 - - -Traits for the bitwise operations `|`, `&`, and `^`. - -Implementing `BitOr`, `BitAnd` or `BitXor` for a type allows the `|`, `&`, or `^` operator respectively -to be used with the type. - -The implementations block below is given for the `BitOr` trait, but the same types that implement -`BitOr` also implement `BitAnd` and `BitXor`. - -Implementations: -```rust -impl BitOr for bool { fn bitor(self, other: bool) -> bool { self | other } } - -impl BitOr for u8 { fn bitor(self, other: u8) -> u8 { self | other } } -impl BitOr for u16 { fn bitor(self, other: u16) -> u16 { self | other } } -impl BitOr for u32 { fn bitor(self, other: u32) -> u32 { self | other } } -impl BitOr for u64 { fn bitor(self, other: u64) -> u64 { self | other } } - -impl BitOr for i8 { fn bitor(self, other: i8) -> i8 { self | other } } -impl BitOr for i16 { fn bitor(self, other: i16) -> i16 { self | other } } -impl BitOr for i32 { fn bitor(self, other: i32) -> i32 { self | other } } -impl BitOr for i64 { fn bitor(self, other: i64) -> i64 { self | other } } -``` - -### `std::ops::{ Shl, Shr }` - -```rust title="shl-trait" showLineNumbers -pub trait Shl { - fn shl(self, other: u8) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/bit.nr#L239-L243 - -```rust title="shr-trait" showLineNumbers -pub trait Shr { - fn shr(self, other: u8) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/bit.nr#L292-L296 - - -Traits for a bit shift left and bit shift right. - -Implementing `Shl` for a type allows the left shift operator (`<<`) to be used with the implementation type. -Similarly, implementing `Shr` allows the right shift operator (`>>`) to be used with the type. - -Note that bit shifting is not currently implemented for signed types. - -The implementations block below is given for the `Shl` trait, but the same types that implement -`Shl` also implement `Shr`. - -Implementations: -```rust -impl Shl for u8 { fn shl(self, other: u8) -> u8 { self << other } } -impl Shl for u16 { fn shl(self, other: u16) -> u16 { self << other } } -impl Shl for u32 { fn shl(self, other: u32) -> u32 { self << other } } -impl Shl for u64 { fn shl(self, other: u64) -> u64 { self << other } } -``` - ---- - -## `std::append` - -### `std::append::Append` - -`Append` can abstract over types that can be appended to - usually container types: - -```rust title="append-trait" showLineNumbers -pub trait Append { - fn empty() -> Self; - fn append(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/append.nr#L9-L14 - - -`Append` requires two methods: - -- `empty`: Constructs an empty value of `Self`. -- `append`: Append two values together, returning the result. - -Additionally, it is expected that for any implementation: - -- `T::empty().append(x) == x` -- `x.append(T::empty()) == x` - -Implementations: -```rust -impl Append for [T] -impl Append for Quoted -``` diff --git a/docs/versioned_docs/version-v0.35.0/noir/standard_library/zeroed.md b/docs/versioned_docs/version-v0.35.0/noir/standard_library/zeroed.md deleted file mode 100644 index f450fecdd36..00000000000 --- a/docs/versioned_docs/version-v0.35.0/noir/standard_library/zeroed.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -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 -- Slice -- String -- Tuple -- Function - -Using it on other types could result in unexpected behavior. diff --git a/docs/versioned_docs/version-v0.35.0/reference/NoirJS/backend_barretenberg/.nojekyll b/docs/versioned_docs/version-v0.35.0/reference/NoirJS/backend_barretenberg/.nojekyll deleted file mode 100644 index e2ac6616add..00000000000 --- a/docs/versioned_docs/version-v0.35.0/reference/NoirJS/backend_barretenberg/.nojekyll +++ /dev/null @@ -1 +0,0 @@ -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.35.0/reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend.md b/docs/versioned_docs/version-v0.35.0/reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend.md deleted file mode 100644 index 252d72a0b71..00000000000 --- a/docs/versioned_docs/version-v0.35.0/reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend.md +++ /dev/null @@ -1,138 +0,0 @@ -# BarretenbergBackend - -## Implements - -- [`Backend`](../index.md#backend) -- [`Backend`](../index.md#backend) - -## Constructors - -### new BarretenbergBackend(acirCircuit, options) - -```ts -new BarretenbergBackend(acirCircuit, options): BarretenbergBackend -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `acirCircuit` | `CompiledCircuit` | -| `options` | [`BackendOptions`](../type-aliases/BackendOptions.md) | - -#### Returns - -[`BarretenbergBackend`](BarretenbergBackend.md) - -## Properties - -| Property | Type | Description | -| :------ | :------ | :------ | -| `backend` | `UltraPlonkBackend` | - | - -## Methods - -### destroy() - -```ts -destroy(): Promise -``` - -#### Returns - -`Promise`\<`void`\> - -*** - -### generateProof() - -```ts -generateProof(compressedWitness): Promise -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `compressedWitness` | `Uint8Array` | - -#### Returns - -`Promise`\<`ProofData`\> - -#### Description - -Generates a proof - -*** - -### generateRecursiveProofArtifacts() - -```ts -generateRecursiveProofArtifacts(proofData, numOfPublicInputs): Promise -``` - -Generates artifacts that will be passed to a circuit that will verify this proof. - -Instead of passing the proof and verification key as a byte array, we pass them -as fields which makes it cheaper to verify in a circuit. - -The proof that is passed here will have been created using a circuit -that has the #[recursive] attribute on its `main` method. - -The number of public inputs denotes how many public inputs are in the inner proof. - -#### Parameters - -| Parameter | Type | Default value | -| :------ | :------ | :------ | -| `proofData` | `ProofData` | `undefined` | -| `numOfPublicInputs` | `number` | `0` | - -#### Returns - -`Promise`\<`object`\> - -#### Example - -```typescript -const artifacts = await backend.generateRecursiveProofArtifacts(proof, numOfPublicInputs); -``` - -*** - -### getVerificationKey() - -```ts -getVerificationKey(): Promise -``` - -#### Returns - -`Promise`\<`Uint8Array`\> - -*** - -### verifyProof() - -```ts -verifyProof(proofData): Promise -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `proofData` | `ProofData` | - -#### Returns - -`Promise`\<`boolean`\> - -#### Description - -Verifies 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.35.0/reference/NoirJS/backend_barretenberg/classes/BarretenbergVerifier.md b/docs/versioned_docs/version-v0.35.0/reference/NoirJS/backend_barretenberg/classes/BarretenbergVerifier.md deleted file mode 100644 index 500276ea748..00000000000 --- a/docs/versioned_docs/version-v0.35.0/reference/NoirJS/backend_barretenberg/classes/BarretenbergVerifier.md +++ /dev/null @@ -1,58 +0,0 @@ -# BarretenbergVerifier - -## Constructors - -### new BarretenbergVerifier(options) - -```ts -new BarretenbergVerifier(options): BarretenbergVerifier -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `options` | [`BackendOptions`](../type-aliases/BackendOptions.md) | - -#### Returns - -[`BarretenbergVerifier`](BarretenbergVerifier.md) - -## Methods - -### destroy() - -```ts -destroy(): Promise -``` - -#### Returns - -`Promise`\<`void`\> - -*** - -### verifyProof() - -```ts -verifyProof(proofData, verificationKey): Promise -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `proofData` | `ProofData` | -| `verificationKey` | `Uint8Array` | - -#### Returns - -`Promise`\<`boolean`\> - -#### Description - -Verifies 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.35.0/reference/NoirJS/backend_barretenberg/classes/UltraHonkBackend.md b/docs/versioned_docs/version-v0.35.0/reference/NoirJS/backend_barretenberg/classes/UltraHonkBackend.md deleted file mode 100644 index 204aaa18db6..00000000000 --- a/docs/versioned_docs/version-v0.35.0/reference/NoirJS/backend_barretenberg/classes/UltraHonkBackend.md +++ /dev/null @@ -1,114 +0,0 @@ -# UltraHonkBackend - -## Implements - -- [`Backend`](../index.md#backend) -- [`Backend`](../index.md#backend) - -## Constructors - -### new UltraHonkBackend(acirCircuit, options) - -```ts -new UltraHonkBackend(acirCircuit, options): UltraHonkBackend -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `acirCircuit` | `CompiledCircuit` | -| `options` | [`BackendOptions`](../type-aliases/BackendOptions.md) | - -#### Returns - -[`UltraHonkBackend`](UltraHonkBackend.md) - -## Properties - -| Property | Type | Description | -| :------ | :------ | :------ | -| `backend` | `UltraHonkBackend` | - | - -## Methods - -### destroy() - -```ts -destroy(): Promise -``` - -#### Returns - -`Promise`\<`void`\> - -*** - -### generateProof() - -```ts -generateProof(compressedWitness): Promise -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `compressedWitness` | `Uint8Array` | - -#### Returns - -`Promise`\<`ProofData`\> - -*** - -### generateRecursiveProofArtifacts() - -```ts -generateRecursiveProofArtifacts(proofData, numOfPublicInputs): Promise -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `proofData` | `ProofData` | -| `numOfPublicInputs` | `number` | - -#### Returns - -`Promise`\<`object`\> - -*** - -### getVerificationKey() - -```ts -getVerificationKey(): Promise -``` - -#### Returns - -`Promise`\<`Uint8Array`\> - -*** - -### verifyProof() - -```ts -verifyProof(proofData): Promise -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `proofData` | `ProofData` | - -#### Returns - -`Promise`\<`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.35.0/reference/NoirJS/backend_barretenberg/classes/UltraHonkVerifier.md b/docs/versioned_docs/version-v0.35.0/reference/NoirJS/backend_barretenberg/classes/UltraHonkVerifier.md deleted file mode 100644 index aee9460153f..00000000000 --- a/docs/versioned_docs/version-v0.35.0/reference/NoirJS/backend_barretenberg/classes/UltraHonkVerifier.md +++ /dev/null @@ -1,58 +0,0 @@ -# UltraHonkVerifier - -## Constructors - -### new UltraHonkVerifier(options) - -```ts -new UltraHonkVerifier(options): UltraHonkVerifier -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `options` | [`BackendOptions`](../type-aliases/BackendOptions.md) | - -#### Returns - -[`UltraHonkVerifier`](UltraHonkVerifier.md) - -## Methods - -### destroy() - -```ts -destroy(): Promise -``` - -#### Returns - -`Promise`\<`void`\> - -*** - -### verifyProof() - -```ts -verifyProof(proofData, verificationKey): Promise -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `proofData` | `ProofData` | -| `verificationKey` | `Uint8Array` | - -#### Returns - -`Promise`\<`boolean`\> - -#### Description - -Verifies 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.35.0/reference/NoirJS/backend_barretenberg/index.md b/docs/versioned_docs/version-v0.35.0/reference/NoirJS/backend_barretenberg/index.md deleted file mode 100644 index 4699e16dee6..00000000000 --- a/docs/versioned_docs/version-v0.35.0/reference/NoirJS/backend_barretenberg/index.md +++ /dev/null @@ -1,42 +0,0 @@ -# backend_barretenberg - -## Exports - -### Classes - -| Class | Description | -| :------ | :------ | -| [BarretenbergBackend](classes/BarretenbergBackend.md) | - | -| [BarretenbergVerifier](classes/BarretenbergVerifier.md) | - | -| [UltraHonkBackend](classes/UltraHonkBackend.md) | - | -| [UltraHonkVerifier](classes/UltraHonkVerifier.md) | - | - -### Type Aliases - -| Type alias | Description | -| :------ | :------ | -| [BackendOptions](type-aliases/BackendOptions.md) | - | - -## References - -### CompiledCircuit - -Renames and re-exports [Backend](index.md#backend) - -*** - -### ProofData - -Renames and re-exports [Backend](index.md#backend) - -## Variables - -### Backend - -```ts -Backend: any; -``` - -*** - -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.35.0/reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions.md b/docs/versioned_docs/version-v0.35.0/reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions.md deleted file mode 100644 index 99ade2ce6f7..00000000000 --- a/docs/versioned_docs/version-v0.35.0/reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions.md +++ /dev/null @@ -1,18 +0,0 @@ -# BackendOptions - -```ts -type BackendOptions: object; -``` - -## Type declaration - -| Member | Type | Description | -| :------ | :------ | :------ | -| `memory` | `object` | - | -| `memory.initial` | `number` | - | -| `memory.maximum` | `number` | - | -| `threads` | `number` | - | - -*** - -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.35.0/reference/NoirJS/backend_barretenberg/typedoc-sidebar.cjs b/docs/versioned_docs/version-v0.35.0/reference/NoirJS/backend_barretenberg/typedoc-sidebar.cjs deleted file mode 100644 index 8ecf05c0163..00000000000 --- a/docs/versioned_docs/version-v0.35.0/reference/NoirJS/backend_barretenberg/typedoc-sidebar.cjs +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-check -/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ -const typedocSidebar = { items: [{"type":"category","label":"Classes","items":[{"type":"doc","id":"reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend","label":"BarretenbergBackend"},{"type":"doc","id":"reference/NoirJS/backend_barretenberg/classes/BarretenbergVerifier","label":"BarretenbergVerifier"},{"type":"doc","id":"reference/NoirJS/backend_barretenberg/classes/UltraHonkBackend","label":"UltraHonkBackend"},{"type":"doc","id":"reference/NoirJS/backend_barretenberg/classes/UltraHonkVerifier","label":"UltraHonkVerifier"}]},{"type":"category","label":"Type Aliases","items":[{"type":"doc","id":"reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions","label":"BackendOptions"}]}]}; -module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.35.0/reference/NoirJS/noir_js/.nojekyll b/docs/versioned_docs/version-v0.35.0/reference/NoirJS/noir_js/.nojekyll deleted file mode 100644 index e2ac6616add..00000000000 --- a/docs/versioned_docs/version-v0.35.0/reference/NoirJS/noir_js/.nojekyll +++ /dev/null @@ -1 +0,0 @@ -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.35.0/reference/NoirJS/noir_js/classes/Noir.md b/docs/versioned_docs/version-v0.35.0/reference/NoirJS/noir_js/classes/Noir.md deleted file mode 100644 index ead255bc504..00000000000 --- a/docs/versioned_docs/version-v0.35.0/reference/NoirJS/noir_js/classes/Noir.md +++ /dev/null @@ -1,52 +0,0 @@ -# Noir - -## Constructors - -### new Noir(circuit) - -```ts -new Noir(circuit): Noir -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `circuit` | `CompiledCircuit` | - -#### Returns - -[`Noir`](Noir.md) - -## Methods - -### 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) -``` - -*** - -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.35.0/reference/NoirJS/noir_js/functions/and.md b/docs/versioned_docs/version-v0.35.0/reference/NoirJS/noir_js/functions/and.md deleted file mode 100644 index c783283e396..00000000000 --- a/docs/versioned_docs/version-v0.35.0/reference/NoirJS/noir_js/functions/and.md +++ /dev/null @@ -1,22 +0,0 @@ -# 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.35.0/reference/NoirJS/noir_js/functions/blake2s256.md b/docs/versioned_docs/version-v0.35.0/reference/NoirJS/noir_js/functions/blake2s256.md deleted file mode 100644 index 7882d0da8d5..00000000000 --- a/docs/versioned_docs/version-v0.35.0/reference/NoirJS/noir_js/functions/blake2s256.md +++ /dev/null @@ -1,21 +0,0 @@ -# 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.35.0/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md b/docs/versioned_docs/version-v0.35.0/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md deleted file mode 100644 index 5e3cd53e9d3..00000000000 --- a/docs/versioned_docs/version-v0.35.0/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md +++ /dev/null @@ -1,28 +0,0 @@ -# ecdsa\_secp256k1\_verify() - -```ts -ecdsa_secp256k1_verify( - hashed_msg, - public_key_x_bytes, - public_key_y_bytes, - signature): boolean -``` - -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.35.0/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md b/docs/versioned_docs/version-v0.35.0/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md deleted file mode 100644 index 0b20ff68957..00000000000 --- a/docs/versioned_docs/version-v0.35.0/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md +++ /dev/null @@ -1,28 +0,0 @@ -# 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.35.0/reference/NoirJS/noir_js/functions/keccak256.md b/docs/versioned_docs/version-v0.35.0/reference/NoirJS/noir_js/functions/keccak256.md deleted file mode 100644 index d10f155ce86..00000000000 --- a/docs/versioned_docs/version-v0.35.0/reference/NoirJS/noir_js/functions/keccak256.md +++ /dev/null @@ -1,21 +0,0 @@ -# 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.35.0/reference/NoirJS/noir_js/functions/xor.md b/docs/versioned_docs/version-v0.35.0/reference/NoirJS/noir_js/functions/xor.md deleted file mode 100644 index 8d762b895d3..00000000000 --- a/docs/versioned_docs/version-v0.35.0/reference/NoirJS/noir_js/functions/xor.md +++ /dev/null @@ -1,22 +0,0 @@ -# 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.35.0/reference/NoirJS/noir_js/index.md b/docs/versioned_docs/version-v0.35.0/reference/NoirJS/noir_js/index.md deleted file mode 100644 index b65a44865ee..00000000000 --- a/docs/versioned_docs/version-v0.35.0/reference/NoirJS/noir_js/index.md +++ /dev/null @@ -1,48 +0,0 @@ -# noir_js - -## Exports - -### Classes - -| Class | Description | -| :------ | :------ | -| [Noir](classes/Noir.md) | - | - -### Type Aliases - -| Type alias | Description | -| :------ | :------ | -| [ErrorWithPayload](type-aliases/ErrorWithPayload.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) | - | -| [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) | Verifies a ECDSA signature over the secp256k1 curve. | -| [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 | -| [xor](functions/xor.md) | Performs a bitwise XOR operation between `lhs` and `rhs` | - -## References - -### CompiledCircuit - -Renames and re-exports [InputMap](index.md#inputmap) - -## Variables - -### InputMap - -```ts -InputMap: any; -``` - -*** - -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.35.0/reference/NoirJS/noir_js/type-aliases/ErrorWithPayload.md b/docs/versioned_docs/version-v0.35.0/reference/NoirJS/noir_js/type-aliases/ErrorWithPayload.md deleted file mode 100644 index e8c2f4aef3d..00000000000 --- a/docs/versioned_docs/version-v0.35.0/reference/NoirJS/noir_js/type-aliases/ErrorWithPayload.md +++ /dev/null @@ -1,15 +0,0 @@ -# ErrorWithPayload - -```ts -type ErrorWithPayload: ExecutionError & object; -``` - -## Type declaration - -| Member | Type | Description | -| :------ | :------ | :------ | -| `decodedAssertionPayload` | `any` | - | - -*** - -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.35.0/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md b/docs/versioned_docs/version-v0.35.0/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md deleted file mode 100644 index 812b8b16481..00000000000 --- a/docs/versioned_docs/version-v0.35.0/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md +++ /dev/null @@ -1,24 +0,0 @@ -# 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.35.0/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md b/docs/versioned_docs/version-v0.35.0/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md deleted file mode 100644 index dd95809186a..00000000000 --- a/docs/versioned_docs/version-v0.35.0/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md +++ /dev/null @@ -1,9 +0,0 @@ -# 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.35.0/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md b/docs/versioned_docs/version-v0.35.0/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md deleted file mode 100644 index b71fb78a946..00000000000 --- a/docs/versioned_docs/version-v0.35.0/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md +++ /dev/null @@ -1,9 +0,0 @@ -# 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.35.0/reference/NoirJS/noir_js/type-aliases/WitnessMap.md b/docs/versioned_docs/version-v0.35.0/reference/NoirJS/noir_js/type-aliases/WitnessMap.md deleted file mode 100644 index 258c46f9d0c..00000000000 --- a/docs/versioned_docs/version-v0.35.0/reference/NoirJS/noir_js/type-aliases/WitnessMap.md +++ /dev/null @@ -1,9 +0,0 @@ -# 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.35.0/reference/NoirJS/noir_js/typedoc-sidebar.cjs b/docs/versioned_docs/version-v0.35.0/reference/NoirJS/noir_js/typedoc-sidebar.cjs deleted file mode 100644 index 9c7fc4a353f..00000000000 --- a/docs/versioned_docs/version-v0.35.0/reference/NoirJS/noir_js/typedoc-sidebar.cjs +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-check -/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ -const typedocSidebar = { items: [{"type":"category","label":"Classes","items":[{"type":"doc","id":"reference/NoirJS/noir_js/classes/Noir","label":"Noir"}]},{"type":"category","label":"Type Aliases","items":[{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ErrorWithPayload","label":"ErrorWithPayload"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ForeignCallHandler","label":"ForeignCallHandler"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ForeignCallInput","label":"ForeignCallInput"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ForeignCallOutput","label":"ForeignCallOutput"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/WitnessMap","label":"WitnessMap"}]},{"type":"category","label":"Functions","items":[{"type":"doc","id":"reference/NoirJS/noir_js/functions/and","label":"and"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/blake2s256","label":"blake2s256"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify","label":"ecdsa_secp256k1_verify"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify","label":"ecdsa_secp256r1_verify"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/keccak256","label":"keccak256"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/xor","label":"xor"}]}]}; -module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.35.0/reference/NoirJS/noir_wasm/.nojekyll b/docs/versioned_docs/version-v0.35.0/reference/NoirJS/noir_wasm/.nojekyll deleted file mode 100644 index e2ac6616add..00000000000 --- a/docs/versioned_docs/version-v0.35.0/reference/NoirJS/noir_wasm/.nojekyll +++ /dev/null @@ -1 +0,0 @@ -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.35.0/reference/NoirJS/noir_wasm/functions/compile.md b/docs/versioned_docs/version-v0.35.0/reference/NoirJS/noir_wasm/functions/compile.md deleted file mode 100644 index 6faf763b37f..00000000000 --- a/docs/versioned_docs/version-v0.35.0/reference/NoirJS/noir_wasm/functions/compile.md +++ /dev/null @@ -1,51 +0,0 @@ -# compile() - -```ts -compile( - fileManager, - projectPath?, - logFn?, -debugLogFn?): Promise -``` - -Compiles a Noir project - -## Parameters - -| Parameter | Type | Description | -| :------ | :------ | :------ | -| `fileManager` | `FileManager` | The file manager to use | -| `projectPath`? | `string` | The path to the project inside the file manager. Defaults to the root of the file manager | -| `logFn`? | `LogFn` | A logging function. If not provided, console.log will be used | -| `debugLogFn`? | `LogFn` | A debug logging function. If not provided, logFn will be used | - -## Returns - -`Promise`\<[`ProgramCompilationArtifacts`](../index.md#programcompilationartifacts)\> - -## Example - -```typescript -// Node.js - -import { compile_program, createFileManager } from '@noir-lang/noir_wasm'; - -const fm = createFileManager(myProjectPath); -const myCompiledCode = await compile_program(fm); -``` - -```typescript -// Browser - -import { compile_program, createFileManager } from '@noir-lang/noir_wasm'; - -const fm = createFileManager('/'); -for (const path of files) { - await fm.writeFile(path, await getFileAsStream(path)); -} -const myCompiledCode = await compile_program(fm); -``` - -*** - -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.35.0/reference/NoirJS/noir_wasm/functions/compile_contract.md b/docs/versioned_docs/version-v0.35.0/reference/NoirJS/noir_wasm/functions/compile_contract.md deleted file mode 100644 index 7d0b39a43ef..00000000000 --- a/docs/versioned_docs/version-v0.35.0/reference/NoirJS/noir_wasm/functions/compile_contract.md +++ /dev/null @@ -1,51 +0,0 @@ -# compile\_contract() - -```ts -compile_contract( - fileManager, - projectPath?, - logFn?, -debugLogFn?): Promise -``` - -Compiles a Noir project - -## Parameters - -| Parameter | Type | Description | -| :------ | :------ | :------ | -| `fileManager` | `FileManager` | The file manager to use | -| `projectPath`? | `string` | The path to the project inside the file manager. Defaults to the root of the file manager | -| `logFn`? | `LogFn` | A logging function. If not provided, console.log will be used | -| `debugLogFn`? | `LogFn` | A debug logging function. If not provided, logFn will be used | - -## Returns - -`Promise`\<[`ContractCompilationArtifacts`](../index.md#contractcompilationartifacts)\> - -## Example - -```typescript -// Node.js - -import { compile_contract, createFileManager } from '@noir-lang/noir_wasm'; - -const fm = createFileManager(myProjectPath); -const myCompiledCode = await compile_contract(fm); -``` - -```typescript -// Browser - -import { compile_contract, createFileManager } from '@noir-lang/noir_wasm'; - -const fm = createFileManager('/'); -for (const path of files) { - await fm.writeFile(path, await getFileAsStream(path)); -} -const myCompiledCode = await compile_contract(fm); -``` - -*** - -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.35.0/reference/NoirJS/noir_wasm/functions/createFileManager.md b/docs/versioned_docs/version-v0.35.0/reference/NoirJS/noir_wasm/functions/createFileManager.md deleted file mode 100644 index 7e65c1d69c7..00000000000 --- a/docs/versioned_docs/version-v0.35.0/reference/NoirJS/noir_wasm/functions/createFileManager.md +++ /dev/null @@ -1,21 +0,0 @@ -# createFileManager() - -```ts -createFileManager(dataDir): FileManager -``` - -Creates a new FileManager instance based on fs in node and memfs in the browser (via webpack alias) - -## Parameters - -| Parameter | Type | Description | -| :------ | :------ | :------ | -| `dataDir` | `string` | root of the file system | - -## Returns - -`FileManager` - -*** - -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.35.0/reference/NoirJS/noir_wasm/functions/inflateDebugSymbols.md b/docs/versioned_docs/version-v0.35.0/reference/NoirJS/noir_wasm/functions/inflateDebugSymbols.md deleted file mode 100644 index fcea9275341..00000000000 --- a/docs/versioned_docs/version-v0.35.0/reference/NoirJS/noir_wasm/functions/inflateDebugSymbols.md +++ /dev/null @@ -1,21 +0,0 @@ -# inflateDebugSymbols() - -```ts -inflateDebugSymbols(debugSymbols): any -``` - -Decompresses and decodes the debug symbols - -## Parameters - -| Parameter | Type | Description | -| :------ | :------ | :------ | -| `debugSymbols` | `string` | The base64 encoded debug symbols | - -## Returns - -`any` - -*** - -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.35.0/reference/NoirJS/noir_wasm/index.md b/docs/versioned_docs/version-v0.35.0/reference/NoirJS/noir_wasm/index.md deleted file mode 100644 index b6e0f9d1bc0..00000000000 --- a/docs/versioned_docs/version-v0.35.0/reference/NoirJS/noir_wasm/index.md +++ /dev/null @@ -1,49 +0,0 @@ -# noir_wasm - -## Exports - -### Functions - -| Function | Description | -| :------ | :------ | -| [compile](functions/compile.md) | Compiles a Noir project | -| [compile\_contract](functions/compile_contract.md) | Compiles a Noir project | -| [createFileManager](functions/createFileManager.md) | Creates a new FileManager instance based on fs in node and memfs in the browser (via webpack alias) | -| [inflateDebugSymbols](functions/inflateDebugSymbols.md) | Decompresses and decodes the debug symbols | - -## References - -### compile\_program - -Renames and re-exports [compile](functions/compile.md) - -## Interfaces - -### ContractCompilationArtifacts - -The compilation artifacts of a given contract. - -#### Properties - -| Property | Type | Description | -| :------ | :------ | :------ | -| `contract` | `ContractArtifact` | The compiled contract. | -| `warnings` | `unknown`[] | Compilation warnings. | - -*** - -### ProgramCompilationArtifacts - -The compilation artifacts of a given program. - -#### Properties - -| Property | Type | Description | -| :------ | :------ | :------ | -| `name` | `string` | not part of the compilation output, injected later | -| `program` | `ProgramArtifact` | The compiled contract. | -| `warnings` | `unknown`[] | Compilation warnings. | - -*** - -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.35.0/reference/NoirJS/noir_wasm/typedoc-sidebar.cjs b/docs/versioned_docs/version-v0.35.0/reference/NoirJS/noir_wasm/typedoc-sidebar.cjs deleted file mode 100644 index e0870710349..00000000000 --- a/docs/versioned_docs/version-v0.35.0/reference/NoirJS/noir_wasm/typedoc-sidebar.cjs +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-check -/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ -const typedocSidebar = { items: [{"type":"doc","id":"reference/NoirJS/noir_wasm/index","label":"API"},{"type":"category","label":"Functions","items":[{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/compile","label":"compile"},{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/compile_contract","label":"compile_contract"},{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/createFileManager","label":"createFileManager"},{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/inflateDebugSymbols","label":"inflateDebugSymbols"}]}]}; -module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.35.0/reference/_category_.json b/docs/versioned_docs/version-v0.35.0/reference/_category_.json deleted file mode 100644 index 5b6a20a609a..00000000000 --- a/docs/versioned_docs/version-v0.35.0/reference/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "position": 4, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.35.0/reference/debugger/_category_.json b/docs/versioned_docs/version-v0.35.0/reference/debugger/_category_.json deleted file mode 100644 index 27869205ad3..00000000000 --- a/docs/versioned_docs/version-v0.35.0/reference/debugger/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "Debugger", - "position": 1, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.35.0/reference/debugger/debugger_known_limitations.md b/docs/versioned_docs/version-v0.35.0/reference/debugger/debugger_known_limitations.md deleted file mode 100644 index 936d416ac4b..00000000000 --- a/docs/versioned_docs/version-v0.35.0/reference/debugger/debugger_known_limitations.md +++ /dev/null @@ -1,59 +0,0 @@ ---- -title: Known limitations -description: - An overview of known limitations of the current version of the Noir debugger -keywords: - [ - Nargo, - Noir Debugger, - VS Code, - ] -sidebar_position: 2 ---- - -# Debugger Known Limitations - -There are currently some limits to what the debugger can observe. - -## Mutable references - -The debugger is currently blind to any state mutated via a mutable reference. For example, in: - -``` -let mut x = 1; -let y = &mut x; -*y = 2; -``` - -The update on `x` will not be observed by the debugger. That means, when running `vars` from the debugger REPL, or inspecting the _local variables_ pane in the VS Code debugger, `x` will appear with value 1 despite having executed `*y = 2;`. - -## Variables of type function or mutable references are opaque - -When inspecting variables, any variable of type `Function` or `MutableReference` will render its value as `<>` or `<>`. - -## Debugger instrumentation affects resulting ACIR - -In order to make the state of local variables observable, the debugger compiles Noir circuits interleaving foreign calls that track any mutations to them. While this works (except in the cases described above) and doesn't introduce any behavior changes, it does as a side effect produce bigger bytecode. In particular, when running the command `opcodes` on the REPL debugger, you will notice Unconstrained VM blocks that look like this: - -``` -... -5 BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [], q_c: 2 }), Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(2))], q_c: 0 })] - | outputs=[] - 5.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } - 5.1 | Mov { destination: RegisterIndex(3), source: RegisterIndex(1) } - 5.2 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } - 5.3 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } - 5.4 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } - 5.5 | Mov { destination: RegisterIndex(3), source: RegisterIndex(3) } - 5.6 | Call { location: 8 } - 5.7 | Stop - 5.8 | ForeignCall { function: "__debug_var_assign", destinations: [], inputs: [RegisterIndex(RegisterIndex(2)), RegisterIndex(RegisterIndex(3))] } -... -``` - -If you are interested in debugging/inspecting compiled ACIR without these synthetic changes, you can invoke the REPL debugger with the `--skip-instrumentation` flag or launch the VS Code debugger with the `skipConfiguration` property set to true in its launch configuration. You can find more details about those in the [Debugger REPL reference](debugger_repl.md) and the [VS Code Debugger reference](debugger_vscode.md). - -:::note -Skipping debugger instrumentation means you won't be able to inspect values of local variables. -::: - diff --git a/docs/versioned_docs/version-v0.35.0/reference/debugger/debugger_repl.md b/docs/versioned_docs/version-v0.35.0/reference/debugger/debugger_repl.md deleted file mode 100644 index 46e2011304e..00000000000 --- a/docs/versioned_docs/version-v0.35.0/reference/debugger/debugger_repl.md +++ /dev/null @@ -1,360 +0,0 @@ ---- -title: REPL Debugger -description: - Noir Debugger REPL options and commands. -keywords: - [ - Nargo, - Noir CLI, - Noir Debugger, - REPL, - ] -sidebar_position: 1 ---- - -## Running the REPL debugger - -`nargo debug [OPTIONS] [WITNESS_NAME]` - -Runs the Noir REPL debugger. If a `WITNESS_NAME` is provided the debugger writes the resulting execution witness to a `WITNESS_NAME` 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 debug | -| `--print-acir` | Display the ACIR for compiled circuit | -| `--deny-warnings` | Treat all warnings as errors | -| `--silence-warnings` | Suppress warnings | -| `-h, --help` | Print help | - -None of these options are required. - -:::note -Since the debugger starts by compiling the target package, all Noir compiler options are also available. Check out the [compiler reference](../nargo_commands.md#nargo-compile) to learn more about the compiler options. -::: - -## REPL commands - -Once the debugger is running, it accepts the following commands. - -#### `help` (h) - -Displays the menu of available commands. - -``` -> help -Available commands: - - opcodes display ACIR opcodes - into step into to the next opcode - next step until a new source location is reached - out step until a new source location is reached - and the current stack frame is finished - break LOCATION:OpcodeLocation add a breakpoint at an opcode location - over step until a new source location is reached - without diving into function calls - restart restart the debugging session - delete LOCATION:OpcodeLocation delete breakpoint at an opcode location - witness show witness map - witness index:u32 display a single witness from the witness map - witness index:u32 value:String update a witness with the given value - memset index:usize value:String update a memory cell with the given - value - continue continue execution until the end of the - program - vars show variable values available at this point - in execution - stacktrace display the current stack trace - memory show memory (valid when executing unconstrained code) value - step step to the next ACIR opcode - -Other commands: - - help Show this help message - quit Quit repl - -``` - -### Stepping through programs - -#### `next` (n) - -Step until the next Noir source code location. While other commands, such as [`into`](#into-i) and [`step`](#step-s), allow for finer grained control of the program's execution at the opcode level, `next` is source code centric. For example: - -``` -3 ... -4 fn main(x: u32) { -5 assert(entry_point(x) == 2); -6 swap_entry_point(x, x + 1); -7 -> assert(deep_entry_point(x) == 4); -8 multiple_values_entry_point(x); -9 } -``` - - -Using `next` here would cause the debugger to jump to the definition of `deep_entry_point` (if available). - -If you want to step over `deep_entry_point` and go straight to line 8, use [the `over` command](#over) instead. - -#### `over` - -Step until the next source code location, without diving into function calls. For example: - -``` -3 ... -4 fn main(x: u32) { -5 assert(entry_point(x) == 2); -6 swap_entry_point(x, x + 1); -7 -> assert(deep_entry_point(x) == 4); -8 multiple_values_entry_point(x); -9 } -``` - - -Using `over` here would cause the debugger to execute until line 8 (`multiple_values_entry_point(x);`). - -If you want to step into `deep_entry_point` instead, use [the `next` command](#next-n). - -#### `out` - -Step until the end of the current function call. For example: - -``` - 3 ... - 4 fn main(x: u32) { - 5 assert(entry_point(x) == 2); - 6 swap_entry_point(x, x + 1); - 7 -> assert(deep_entry_point(x) == 4); - 8 multiple_values_entry_point(x); - 9 } - 10 - 11 unconstrained fn returns_multiple_values(x: u32) -> (u32, u32, u32, u32) { - 12 ... - ... - 55 - 56 unconstrained fn deep_entry_point(x: u32) -> u32 { - 57 -> level_1(x + 1) - 58 } - -``` - -Running `out` here will resume execution until line 8. - -#### `step` (s) - -Skips to the next ACIR code. A compiled Noir program is a sequence of ACIR opcodes. However, an unconstrained VM opcode denotes the start of an unconstrained code block, to be executed by the unconstrained VM. For example (redacted for brevity): - -``` -0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] -1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] - 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } - 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } - 1.2 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } - 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } - 1.4 | Call { location: 7 } - ... - 1.43 | Return -2 EXPR [ (1, _1) -2 ] -``` - -The `->` here shows the debugger paused at an ACIR opcode: `BRILLIG`, at index 1, which denotes an unconstrained code block is about to start. - -Using the `step` command at this point would result in the debugger stopping at ACIR opcode 2, `EXPR`, skipping unconstrained computation steps. - -Use [the `into` command](#into-i) instead if you want to follow unconstrained computation step by step. - -#### `into` (i) - -Steps into the next opcode. A compiled Noir program is a sequence of ACIR opcodes. However, a BRILLIG opcode denotes the start of an unconstrained code block, to be executed by the unconstrained VM. For example (redacted for brevity): - -``` -0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] -1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] - 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } - 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } - 1.2 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } - 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } - 1.4 | Call { location: 7 } - ... - 1.43 | Return -2 EXPR [ (1, _1) -2 ] -``` - -The `->` here shows the debugger paused at an ACIR opcode: `BRILLIG`, at index 1, which denotes an unconstrained code block is about to start. - -Using the `into` command at this point would result in the debugger stopping at opcode 1.0, `Mov ...`, allowing the debugger user to follow unconstrained computation step by step. - -Use [the `step` command](#step-s) instead if you want to skip to the next ACIR code directly. - -#### `continue` (c) - -Continues execution until the next breakpoint, or the end of the program. - -#### `restart` (res) - -Interrupts execution, and restarts a new debugging session from scratch. - -#### `opcodes` (o) - -Display the program's ACIR opcode sequence. For example: - -``` -0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] -1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] - 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } - 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } - 1.2 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } - 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } - 1.4 | Call { location: 7 } - ... - 1.43 | Return -2 EXPR [ (1, _1) -2 ] -``` - -### Breakpoints - -#### `break [Opcode]` (or shorthand `b [Opcode]`) - -Sets a breakpoint on the specified opcode index. To get a list of the program opcode numbers, see [the `opcode` command](#opcodes-o). For example: - -``` -0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] -1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] - 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } - 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } - 1.2 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } - 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } - 1.4 | Call { location: 7 } - ... - 1.43 | Return -2 EXPR [ (1, _1) -2 ] -``` - -In this example, issuing a `break 1.2` command adds break on opcode 1.2, as denoted by the `*` character: - -``` -0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] -1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] - 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } - 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } - 1.2 | * Const { destination: RegisterIndex(1), value: Value { inner: 0 } } - 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } - 1.4 | Call { location: 7 } - ... - 1.43 | Return -2 EXPR [ (1, _1) -2 ] -``` - -Running [the `continue` command](#continue-c) at this point would cause the debugger to execute the program until opcode 1.2. - -#### `delete [Opcode]` (or shorthand `d [Opcode]`) - -Deletes a breakpoint at an opcode location. Usage is analogous to [the `break` command](#). - -### Variable inspection - -#### vars - -Show variable values available at this point in execution. - -:::note -The ability to inspect variable values from the debugger depends on compilation to be run in a special debug instrumentation mode. This instrumentation weaves variable tracing code with the original source code. - -So variable value inspection comes at the expense of making the resulting ACIR bytecode bigger and harder to understand and optimize. - -If you find this compromise unacceptable, you can run the debugger with the flag `--skip-debug-instrumentation`. This will compile your circuit without any additional debug information, so the resulting ACIR bytecode will be identical to the one produced by standard Noir compilation. However, if you opt for this, the `vars` command will not be available while debugging. -::: - - -### Stacktrace - -#### `stacktrace` - -Displays the current stack trace. - - -### Witness map - -#### `witness` (w) - -Show witness map. For example: - -``` -_0 = 0 -_1 = 2 -_2 = 1 -``` - -#### `witness [Witness Index]` - -Display a single witness from the witness map. For example: - -``` -> witness 1 -_1 = 2 -``` - -#### `witness [Witness Index] [New value]` - -Overwrite the given index with a new value. For example: - -``` -> witness 1 3 -_1 = 3 -``` - - -### Unconstrained VM memory - -#### `memory` - -Show unconstrained VM memory state. For example: - -``` -> memory -At opcode 1.13: Store { destination_pointer: RegisterIndex(0), source: RegisterIndex(3) } -... -> registers -0 = 0 -1 = 10 -2 = 0 -3 = 1 -4 = 1 -5 = 2³² -6 = 1 -> into -At opcode 1.14: Const { destination: RegisterIndex(5), value: Value { inner: 1 } } -... -> memory -0 = 1 -> -``` - -In the example above: we start with clean memory, then step through a `Store` opcode which stores the value of register 3 (1) into the memory address stored in register 0 (0). Thus now `memory` shows memory address 0 contains value 1. - -:::note -This command is only functional while the debugger is executing unconstrained code. -::: - -#### `memset [Memory address] [New value]` - -Update a memory cell with the given value. For example: - -``` -> memory -0 = 1 -> memset 0 2 -> memory -0 = 2 -> memset 1 4 -> memory -0 = 2 -1 = 4 -> -``` - -:::note -This command is only functional while the debugger is executing unconstrained code. -::: \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.35.0/reference/debugger/debugger_vscode.md b/docs/versioned_docs/version-v0.35.0/reference/debugger/debugger_vscode.md deleted file mode 100644 index c027332b3b0..00000000000 --- a/docs/versioned_docs/version-v0.35.0/reference/debugger/debugger_vscode.md +++ /dev/null @@ -1,82 +0,0 @@ ---- -title: VS Code Debugger -description: - VS Code Debugger configuration and features. -keywords: - [ - Nargo, - Noir CLI, - Noir Debugger, - VS Code, - IDE, - ] -sidebar_position: 0 ---- - -# VS Code Noir Debugger Reference - -The Noir debugger enabled by the vscode-noir extension ships with default settings such that the most common scenario should run without any additional configuration steps. - -These defaults can nevertheless be overridden by defining a launch configuration file. This page provides a reference for the properties you can override via a launch configuration file, as well as documenting the Nargo `dap` command, which is a dependency of the VS Code Noir debugger. - - -## Creating and editing launch configuration files - -To create a launch configuration file from VS Code, open the _debug pane_, and click on _create a launch.json file_. - -![Creating a launch configuration file](@site/static/img/debugger/ref1-create-launch.png) - -A `launch.json` file will be created, populated with basic defaults. - -### Noir Debugger launch.json properties - -#### projectFolder - -_String, optional._ - -Absolute path to the Nargo project to debug. By default, it is dynamically determined by looking for the nearest `Nargo.toml` file to the active file at the moment of launching the debugger. - -#### proverName - -_String, optional._ - -Name of the prover input to use. Defaults to `Prover`, which looks for a file named `Prover.toml` at the `projectFolder`. - -#### generateAcir - -_Boolean, optional._ - -If true, generate ACIR opcodes instead of unconstrained opcodes which will be closer to release binaries but less convenient for debugging. Defaults to `false`. - -#### skipInstrumentation - -_Boolean, optional._ - -Skips variables debugging instrumentation of code, making debugging less convenient but the resulting binary smaller and closer to production. Defaults to `false`. - -:::note -Skipping instrumentation causes the debugger to be unable to inspect local variables. -::: - -## `nargo dap [OPTIONS]` - -When run without any option flags, it starts the Nargo Debug Adapter Protocol server, which acts as the debugging backend for the VS Code Noir Debugger. - -All option flags are related to preflight checks. The Debug Adapter Protocol specifies how errors are to be informed from a running DAP server, but it doesn't specify mechanisms to communicate server initialization errors between the DAP server and its client IDE. - -Thus `nargo dap` ships with a _preflight check_ mode. If flag `--preflight-check` and the rest of the `--preflight-*` flags are provided, Nargo will run the same initialization routine except it will not start the DAP server. - -`vscode-noir` will then run `nargo dap` in preflight check mode first before a debugging session starts. If the preflight check ends in error, vscode-noir will present stderr and stdout output from this process through its own Output pane in VS Code. This makes it possible for users to diagnose what pieces of configuration might be wrong or missing in case of initialization errors. - -If the preflight check succeeds, `vscode-noir` proceeds to start the DAP server normally but running `nargo dap` without any additional flags. - -### Options - -| Option | Description | -| --------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | -| `--preflight-check` | If present, dap runs in preflight check mode. | -| `--preflight-project-folder ` | Absolute path to the project to debug for preflight check. | -| `--preflight-prover-name ` | Name of prover file to use for preflight check | -| `--preflight-generate-acir` | Optional. If present, compile in ACIR mode while running preflight check. | -| `--preflight-skip-instrumentation` | Optional. If present, compile without introducing debug instrumentation while running preflight check. | -| `-h, --help` | Print help. | diff --git a/docs/versioned_docs/version-v0.35.0/reference/nargo_commands.md b/docs/versioned_docs/version-v0.35.0/reference/nargo_commands.md deleted file mode 100644 index db1884afee2..00000000000 --- a/docs/versioned_docs/version-v0.35.0/reference/nargo_commands.md +++ /dev/null @@ -1,297 +0,0 @@ ---- -title: Nargo -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, - ] -sidebar_position: 0 ---- - -# Command-Line Help for `nargo` - -This document contains the help content for the `nargo` command-line program. - -**Command Overview:** - -* [`nargo`↴](#nargo) -* [`nargo check`↴](#nargo-check) -* [`nargo fmt`↴](#nargo-fmt) -* [`nargo compile`↴](#nargo-compile) -* [`nargo new`↴](#nargo-new) -* [`nargo init`↴](#nargo-init) -* [`nargo execute`↴](#nargo-execute) -* [`nargo debug`↴](#nargo-debug) -* [`nargo test`↴](#nargo-test) -* [`nargo info`↴](#nargo-info) -* [`nargo lsp`↴](#nargo-lsp) - -## `nargo` - -Noir's package manager - -**Usage:** `nargo ` - -###### **Subcommands:** - -* `check` — Checks the constraint system for errors -* `fmt` — Format the Noir files in a workspace -* `compile` — Compile the program and its secret execution trace into ACIR format -* `new` — Create a Noir project in a new directory -* `init` — Create a Noir project in the current directory -* `execute` — Executes a circuit to calculate its return value -* `debug` — Executes a circuit in debug mode -* `test` — Run the tests for this program -* `info` — Provides detailed information on each of a program's function (represented by a single circuit) -* `lsp` — Starts the Noir LSP server - -###### **Options:** - - - - -## `nargo check` - -Checks the constraint system for errors - -**Usage:** `nargo check [OPTIONS]` - -###### **Options:** - -* `--package ` — The name of the package to check -* `--workspace` — Check all packages in the workspace -* `--overwrite` — Force overwrite of existing files -* `--expression-width ` — Specify the backend expression width that should be targeted -* `--bounded-codegen` — Generate ACIR with the target backend expression width. The default is to generate ACIR without a bound and split expressions after code generation. Activating this flag can sometimes provide optimizations for certain programs - - Default value: `false` -* `--force` — Force a full recompilation -* `--print-acir` — Display the ACIR for compiled circuit -* `--deny-warnings` — Treat all warnings as errors -* `--silence-warnings` — Suppress warnings -* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" -* `--skip-underconstrained-check` — Flag to turn off the compiler check for under constrained values. Warning: This can improve compilation speed but can also lead to correctness errors. This check should always be run on production code - - - -## `nargo fmt` - -Format the Noir files in a workspace - -**Usage:** `nargo fmt [OPTIONS]` - -###### **Options:** - -* `--check` — Run noirfmt in check mode - - - -## `nargo compile` - -Compile the program and its secret execution trace into ACIR format - -**Usage:** `nargo compile [OPTIONS]` - -###### **Options:** - -* `--package ` — The name of the package to compile -* `--workspace` — Compile all packages in the workspace -* `--expression-width ` — Specify the backend expression width that should be targeted -* `--bounded-codegen` — Generate ACIR with the target backend expression width. The default is to generate ACIR without a bound and split expressions after code generation. Activating this flag can sometimes provide optimizations for certain programs - - Default value: `false` -* `--force` — Force a full recompilation -* `--print-acir` — Display the ACIR for compiled circuit -* `--deny-warnings` — Treat all warnings as errors -* `--silence-warnings` — Suppress warnings -* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" -* `--skip-underconstrained-check` — Flag to turn off the compiler check for under constrained values. Warning: This can improve compilation speed but can also lead to correctness errors. This check should always be run on production code - - - -## `nargo new` - -Create a Noir project in a new directory - -**Usage:** `nargo new [OPTIONS] ` - -###### **Arguments:** - -* `` — The path to save the new project - -###### **Options:** - -* `--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 - - - -## `nargo init` - -Create a Noir project in the current directory - -**Usage:** `nargo init [OPTIONS]` - -###### **Options:** - -* `--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 - - - -## `nargo execute` - -Executes a circuit to calculate its return value - -**Usage:** `nargo execute [OPTIONS] [WITNESS_NAME]` - -###### **Arguments:** - -* `` — Write the execution witness to named file - -Defaults to the name of the package being executed. - -###### **Options:** - -* `-p`, `--prover-name ` — The name of the toml file which contains the inputs for the prover - - Default value: `Prover` -* `--package ` — The name of the package to execute -* `--workspace` — Execute all packages in the workspace -* `--expression-width ` — Specify the backend expression width that should be targeted -* `--bounded-codegen` — Generate ACIR with the target backend expression width. The default is to generate ACIR without a bound and split expressions after code generation. Activating this flag can sometimes provide optimizations for certain programs - - Default value: `false` -* `--force` — Force a full recompilation -* `--print-acir` — Display the ACIR for compiled circuit -* `--deny-warnings` — Treat all warnings as errors -* `--silence-warnings` — Suppress warnings -* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" -* `--skip-underconstrained-check` — Flag to turn off the compiler check for under constrained values. Warning: This can improve compilation speed but can also lead to correctness errors. This check should always be run on production code -* `--oracle-resolver ` — JSON RPC url to solve oracle calls - - - -## `nargo debug` - -Executes a circuit in debug mode - -**Usage:** `nargo debug [OPTIONS] [WITNESS_NAME]` - -###### **Arguments:** - -* `` — Write the execution witness to named file - -###### **Options:** - -* `-p`, `--prover-name ` — The name of the toml file which contains the inputs for the prover - - Default value: `Prover` -* `--package ` — The name of the package to execute -* `--expression-width ` — Specify the backend expression width that should be targeted -* `--bounded-codegen` — Generate ACIR with the target backend expression width. The default is to generate ACIR without a bound and split expressions after code generation. Activating this flag can sometimes provide optimizations for certain programs - - Default value: `false` -* `--force` — Force a full recompilation -* `--print-acir` — Display the ACIR for compiled circuit -* `--deny-warnings` — Treat all warnings as errors -* `--silence-warnings` — Suppress warnings -* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" -* `--skip-underconstrained-check` — Flag to turn off the compiler check for under constrained values. Warning: This can improve compilation speed but can also lead to correctness errors. This check should always be run on production code -* `--acir-mode` — Force ACIR output (disabling instrumentation) -* `--skip-instrumentation ` — Disable vars debug instrumentation (enabled by default) - - Possible values: `true`, `false` - - - - -## `nargo test` - -Run the tests for this program - -**Usage:** `nargo test [OPTIONS] [TEST_NAME]` - -###### **Arguments:** - -* `` — If given, only tests with names containing this string will be run - -###### **Options:** - -* `--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 -* `--expression-width ` — Specify the backend expression width that should be targeted -* `--bounded-codegen` — Generate ACIR with the target backend expression width. The default is to generate ACIR without a bound and split expressions after code generation. Activating this flag can sometimes provide optimizations for certain programs - - Default value: `false` -* `--force` — Force a full recompilation -* `--print-acir` — Display the ACIR for compiled circuit -* `--deny-warnings` — Treat all warnings as errors -* `--silence-warnings` — Suppress warnings -* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" -* `--skip-underconstrained-check` — Flag to turn off the compiler check for under constrained values. Warning: This can improve compilation speed but can also lead to correctness errors. This check should always be run on production code -* `--oracle-resolver ` — JSON RPC url to solve oracle calls - - - -## `nargo info` - -Provides detailed information on each of a program's function (represented by a single circuit) - -Current information provided per circuit: 1. The number of ACIR opcodes 2. Counts the final number gates in the circuit used by a backend - -**Usage:** `nargo info [OPTIONS]` - -###### **Options:** - -* `--package ` — The name of the package to detail -* `--workspace` — Detail all packages in the workspace -* `--expression-width ` — Specify the backend expression width that should be targeted -* `--bounded-codegen` — Generate ACIR with the target backend expression width. The default is to generate ACIR without a bound and split expressions after code generation. Activating this flag can sometimes provide optimizations for certain programs - - Default value: `false` -* `--force` — Force a full recompilation -* `--print-acir` — Display the ACIR for compiled circuit -* `--deny-warnings` — Treat all warnings as errors -* `--silence-warnings` — Suppress warnings -* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" -* `--skip-underconstrained-check` — Flag to turn off the compiler check for under constrained values. Warning: This can improve compilation speed but can also lead to correctness errors. This check should always be run on production code - - - -## `nargo lsp` - -Starts the Noir LSP server - -Starts an LSP server which allows IDEs such as VS Code to display diagnostics in Noir source. - -VS Code Noir Language Support: https://marketplace.visualstudio.com/items?itemName=noir-lang.vscode-noir - -**Usage:** `nargo lsp` - - - -
- - - This document was generated automatically by - clap-markdown. - - diff --git a/docs/versioned_docs/version-v0.35.0/reference/noir_codegen.md b/docs/versioned_docs/version-v0.35.0/reference/noir_codegen.md deleted file mode 100644 index db8f07dc22e..00000000000 --- a/docs/versioned_docs/version-v0.35.0/reference/noir_codegen.md +++ /dev/null @@ -1,114 +0,0 @@ ---- -title: Noir Codegen for TypeScript -description: Learn how to use Noir codegen to generate TypeScript bindings -keywords: [Nargo, Noir, compile, TypeScript] -sidebar_position: 3 ---- - -When using TypeScript, it is extra work to interpret Noir program outputs in a type-safe way. Third party libraries may exist for popular Noir programs, but they are either hard to find or unmaintained. - -Now you can generate TypeScript bindings for your Noir programs in two steps: -1. Exporting Noir functions using `nargo export` -2. Using the TypeScript module `noir_codegen` to generate TypeScript binding - -**Note:** you can only export functions from a Noir *library* (not binary or contract program types). - -## Installation - -### Your TypeScript project - -If you don't already have a TypeScript project you can add the module with `yarn` (or `npm`), then initialize it: - -```bash -yarn add typescript -D -npx tsc --init -``` - -### Add TypeScript module - `noir_codegen` - -The following command will add the module to your project's devDependencies: - -```bash -yarn add @noir-lang/noir_codegen -D -``` - -### Nargo library -Make sure you have Nargo, v0.25.0 or greater, installed. If you don't, follow the [installation guide](../getting_started/installation/index.md). - -If you're in a new project, make a `circuits` folder and create a new Noir library: - -```bash -mkdir circuits && cd circuits -nargo new --lib myNoirLib -``` - -## Usage - -### Export ABI of specified functions - -First go to the `.nr` files in your Noir library, and add the `#[export]` macro to each function that you want to use in TypeScript. - -```rust -#[export] -fn your_function(... -``` - -From your Noir library (where `Nargo.toml` is), run the following command: - -```bash -nargo export -``` - -You will now have an `export` directory with a .json file per exported function. - -You can also specify the directory of Noir programs using `--program-dir`, for example: - -```bash -nargo export --program-dir=./circuits/myNoirLib -``` - -### Generate TypeScript bindings from exported functions - -To use the `noir-codegen` package we added to the TypeScript project: - -```bash -yarn noir-codegen ./export/your_function.json -``` - -This creates an `exports` directory with an `index.ts` file containing all exported functions. - -**Note:** adding `--out-dir` allows you to specify an output dir for your TypeScript bindings to go. Eg: - -```bash -yarn noir-codegen ./export/*.json --out-dir ./path/to/output/dir -``` - -## Example .nr function to .ts output - -Consider a Noir library with this function: - -```rust -#[export] -fn not_equal(x: Field, y: Field) -> bool { - x != y -} -``` - -After the export and codegen steps, you should have an `index.ts` like: - -```typescript -export type Field = string; - - -export const is_equal_circuit: CompiledCircuit = -{"abi":{"parameters":[{"name":"x","type":{"kind":"field"},"visibility":"private"},{"name":"y","type":{"kind":"field"},"visibility":"private"}],"return_type":{"abi_type":{"kind":"boolean"},"visibility":"private"}},"bytecode":"H4sIAAAAAAAA/7WUMQ7DIAxFQ0Krrr2JjSGYLVcpKrn/CaqqDQN12WK+hPBgmWd/wEyHbF1SS923uhOs3pfoChI+wKXMAXzIKyNj4PB0TFTYc0w5RUjoqeAeEu1wqK0F54RGkWvW44LPzExnlkbMEs4JNZmN8PxS42uHv82T8a3Jeyn2Ks+VLPcO558HmyLMCDOXAXXtpPt4R/Rt9T36ss6dS9HGPx/eG17nGegKBQAA"}; - -export async function is_equal(x: Field, y: Field, foreignCallHandler?: ForeignCallHandler): Promise { - const program = new Noir(is_equal_circuit); - const args: InputMap = { x, y }; - const { returnValue } = await program.execute(args, foreignCallHandler); - return returnValue as boolean; -} -``` - -Now the `is_equal()` function and relevant types are readily available for use in TypeScript. diff --git a/docs/versioned_docs/version-v0.35.0/tooling/debugger.md b/docs/versioned_docs/version-v0.35.0/tooling/debugger.md deleted file mode 100644 index 9b7565ba9ff..00000000000 --- a/docs/versioned_docs/version-v0.35.0/tooling/debugger.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: Debugger -description: Learn about the Noir Debugger, in its REPL or VS Code versions. -keywords: [Nargo, VSCode, Visual Studio Code, REPL, Debugger] -sidebar_position: 2 ---- - -# Noir Debugger - -There are currently two ways of debugging Noir programs: - -1. From VS 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). -2. Via the REPL debugger, which ships with Nargo. - -In order to use either version of the debugger, you will need to install recent enough versions of Noir, [Nargo](../getting_started/installation/index.md) and vscode-noir: - -- Noir & Nargo ≥0.28.0 -- Noir's VS Code extension ≥0.0.11 - -:::info -At the moment, the debugger supports debugging binary projects, but not contracts. -::: - -We cover the VS Code Noir debugger more in depth in [its VS Code debugger how-to guide](../how_to/debugger/debugging_with_vs_code.md) and [the reference](../reference/debugger/debugger_vscode.md). - -The REPL debugger is discussed at length in [the REPL debugger how-to guide](../how_to/debugger/debugging_with_the_repl.md) and [the reference](../reference/debugger/debugger_repl.md). diff --git a/docs/versioned_docs/version-v0.35.0/tooling/language_server.md b/docs/versioned_docs/version-v0.35.0/tooling/language_server.md deleted file mode 100644 index 81e0356ef8a..00000000000 --- a/docs/versioned_docs/version-v0.35.0/tooling/language_server.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -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] -sidebar_position: 0 ---- - -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.35.0/tooling/testing.md b/docs/versioned_docs/version-v0.35.0/tooling/testing.md deleted file mode 100644 index 866677da567..00000000000 --- a/docs/versioned_docs/version-v0.35.0/tooling/testing.md +++ /dev/null @@ -1,79 +0,0 @@ ---- -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] -sidebar_position: 1 ---- - -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 all -the constraints 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); -} -``` - -The string given to `should_fail_with` doesn't need to exactly match the failure reason, it just needs to be a substring of it: - -```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 = "airspeed velocity")] -fn test_bridgekeeper() { - main(32); -} -``` \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.35.0/tutorials/noirjs_app.md b/docs/versioned_docs/version-v0.35.0/tutorials/noirjs_app.md deleted file mode 100644 index eac28168445..00000000000 --- a/docs/versioned_docs/version-v0.35.0/tutorials/noirjs_app.md +++ /dev/null @@ -1,362 +0,0 @@ ---- -title: Building a web app with NoirJS -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, app] -sidebar_position: 0 -pagination_next: noir/concepts/data_types/index ---- - -NoirJS is a set of packages meant to work both in a browser and a server environment. In this tutorial, we will build a simple web app using them. From here, you should get an idea on how to proceed with your own Noir projects! - -You can find the complete app code for this guide [here](https://github.com/noir-lang/tiny-noirjs-app). - -## Setup - -:::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.31.x matches `noir_js@0.31.x`, etc. - -In this guide, we will be pinned to 0.31.0. - -::: - -Before we start, we want to make sure we have Node, Nargo and the Barretenberg proving system (`bb`) installed. - -We start by opening a terminal and executing `node --version`. If we don't get an output like `v20.10.0`, that means node is not installed. Let's do that by following the handy [nvm guide](https://github.com/nvm-sh/nvm?tab=readme-ov-file#install--update-script). - -As for `Nargo`, we can follow the [Nargo guide](../getting_started/installation/index.md) to install it. If you're lazy, just paste this on a terminal and run `noirup`: - -```sh -curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash -``` - -Follow the instructions on [this page](https://github.com/AztecProtocol/aztec-packages/tree/master/barretenberg/cpp/src/barretenberg/bb#installation) to install `bb`. -Version 0.41.0 is compatible with `nargo` version 0.31.0, which you can install with `bbup -v 0.41.0` once `bbup` is installed. - -Easy enough. Onwards! - -## Our project - -ZK is a powerful technology. An app that doesn't reveal one of the inputs to _anyone_ is almost unbelievable, yet Noir makes it as easy as a single line of code. - -In fact, it's so simple that it comes nicely packaged in `nargo`. Let's do that! - -### Nargo - -Run: - -```bash -nargo new circuit -``` - -And... That's about it. Your program is ready to be compiled and run. - -To compile, let's `cd` into the `circuit` folder to enter our project, and call: - -```bash -nargo compile -``` - -This compiles our circuit into `json` format and add it to a new `target` folder. - -:::info - -At this point in the tutorial, your folder structure should look like this: - -```tree -. -└── circuit <---- our working directory - ├── Nargo.toml - ├── src - │ └── main.nr - └── target - └── circuit.json -``` - -::: - -### Node and Vite - -If you want to explore Nargo, feel free to go on a side-quest now and follow the steps in the -[getting started](../getting_started/hello_noir/index.md) guide. However, we want our app to run on the browser, so we need Vite. - -Vite is a powerful tool to generate static websites. While it provides all kinds of features, let's just go barebones with some good old vanilla JS. - -To do this this, go back to the previous folder (`cd ..`) and create a new vite project by running `npm create vite` and choosing "Vanilla" and "Javascript". - -A wild `vite-project` directory should now appear in your root folder! Let's not waste any time and dive right in: - -```bash -cd vite-project -``` - -### Setting Up Vite and Configuring the Project - -Before we proceed with any coding, let's get our environment tailored for Noir. We'll start by laying down the foundations with a `vite.config.js` file. This little piece of configuration is our secret sauce for making sure everything meshes well with the NoirJS libraries and other special setups we might need, like handling WebAssembly modules. Here’s how you get that going: - -#### Creating the vite.config.js - -In your freshly minted `vite-project` folder, create a new file named `vite.config.js` and open it in your code editor. Paste the following to set the stage: - -```javascript -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 { - build: { - target: 'esnext', - rollupOptions: { - external: ['@aztec/bb.js'] - } - }, - optimizeDeps: { - esbuildOptions: { - target: 'esnext' - } - }, - plugins: [ - copy({ - targets: [{ src: 'node_modules/**/*.wasm', dest: 'node_modules/.vite/dist' }], - copySync: true, - hook: 'buildStart', - }), - command === 'serve' ? wasmContentTypePlugin : [], - ], - }; - } - - return {}; -}); -``` - -#### Install Dependencies - -Now that our stage is set, install the necessary NoirJS packages along with our other dependencies: - -```bash -npm install && npm install @noir-lang/backend_barretenberg@0.31.0 @noir-lang/noir_js@0.31.0 -npm install rollup-plugin-copy --save-dev -``` - -:::info - -At this point in the tutorial, your folder structure should look like this: - -```tree -. -└── circuit - └── ...etc... -└── vite-project <---- our working directory - └── ...etc... -``` - -::: - -#### Some cleanup - -`npx create vite` is amazing but it creates a bunch of files we don't really need for our simple example. Actually, let's just delete everything except for `vite.config.js`, `index.html`, `main.js` and `package.json`. I feel lighter already. - -![my heart is ready for you, noir.js](@site/static/img/memes/titanic.jpeg) - -## HTML - -Our app won't run like this, of course. We need some working HTML, at least. Let's open our broken-hearted `index.html` and replace everything with this code snippet: - -```html - - - - - - -

Noir app

-
- - -
-
-

Logs

-

Proof

-
- - -``` - -It _could_ be a beautiful UI... Depending on which universe you live in. - -## Some good old vanilla Javascript - -Our love for Noir needs undivided attention, so let's just open `main.js` and delete everything (this is where the romantic scenery becomes a bit creepy). - -Start by pasting in this boilerplate code: - -```js -function display(container, msg) { - const c = document.getElementById(container); - const p = document.createElement('p'); - p.textContent = msg; - c.appendChild(p); -} - -document.getElementById('submitGuess').addEventListener('click', async () => { - try { - // here's where love happens - } catch (err) { - display('logs', 'Oh 💔 Wrong guess'); - } -}); -``` - -The display function doesn't do much. We're simply manipulating our website to see stuff happening. For example, if the proof fails, it will simply log a broken heart 😢 - -:::info - -At this point in the tutorial, your folder structure should look like this: - -```tree -. -└── circuit - └── ...same as above -└── vite-project - ├── vite.config.js - ├── main.js - ├── package.json - └── index.html -``` - -You'll see other files and folders showing up (like `package-lock.json`, `node_modules`) but you shouldn't have to care about those. - -::: - -## Some NoirJS - -We're starting with the good stuff now. If you've compiled the circuit as described above, you should have a `json` file we want to import at the very top of our `main.js` file: - -```ts -import circuit from '../circuit/target/circuit.json'; -``` - -[Noir is backend-agnostic](../index.mdx#whats-new-about-noir). We write Noir, but we also need a proving backend. That's why we need to import and instantiate the two dependencies we installed above: `BarretenbergBackend` and `Noir`. Let's import them right below: - -```js -import { BarretenbergBackend, BarretenbergVerifier as Verifier } from '@noir-lang/backend_barretenberg'; -import { Noir } from '@noir-lang/noir_js'; -``` - -And instantiate them inside our try-catch block: - -```ts -// try { -const backend = new BarretenbergBackend(circuit); -const noir = new Noir(circuit); -// } -``` - -:::note - -For the remainder of the tutorial, everything will be happening inside the `try` block - -::: - -## Our app - -Now for the app itself. We're capturing whatever is in the input when people press the submit button. Just add this: - -```js -const x = parseInt(document.getElementById('guessInput').value); -const input = { x, y: 2 }; -``` - -Now we're ready to prove stuff! Let's feed some inputs to our circuit and calculate the proof: - -```js -await setup(); // let's squeeze our wasm inits here - -display('logs', 'Generating proof... ⌛'); -const { witness } = await noir.execute(input); -const proof = await backend.generateProof(witness); -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 run dev`. If it doesn't open a browser for you, just visit `localhost:5173`. You should now see the worst UI ever, with an ugly input. - -![Getting Started 0](@site/static/img/noir_getting_started_1.png) - -Now, our circuit says `fn main(x: Field, y: pub Field)`. This means only the `y` value is public, and it's hardcoded above: `input = { x, y: 2 }`. In other words, you won't need to send your secret`x` to the verifier! - -By inputting any number other than 2 in the input box and clicking "submit", you should get a valid proof. Otherwise the proof won't even generate correctly. By the way, if you're human, you shouldn't be able to understand anything on the "proof" box. That's OK. We like you, human ❤️. - -## Verifying - -Time to celebrate, yes! But we shouldn't trust machines so blindly. Let's add these lines to see our proof being verified: - -```js -display('logs', 'Verifying proof... ⌛'); -const isValid = await backend.verifyProof(proof); - -// or to cache and use the verification key: -// const verificationKey = await backend.getVerificationKey(); -// const verifier = new Verifier(); -// const isValid = await verifier.verifyProof(proof, verificationKey); - -if (isValid) display('logs', 'Verifying proof... ✅'); -``` - -You have successfully generated a client-side Noir web app! - -![coded app without math knowledge](@site/static/img/memes/flextape.jpeg) - -## 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/vite-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. - -## UltraHonk Backend - -Barretenberg has recently exposed a new UltraHonk backend. We can use UltraHonk in NoirJS after version 0.33.0. Everything will be the same as the tutorial above, except that the class we need to import will change: -```js -import { UltraHonkBackend, UltraHonkVerifier as Verifier } from '@noir-lang/backend_barretenberg'; -``` -The backend will then be instantiated as such: -```js -const backend = new UltraHonkBackend(circuit); -``` -Then all the commands to prove and verify your circuit will be same. - -The only feature currently unsupported with UltraHonk are [recursive proofs](../explainers/explainer-recursion.md). \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.37.0/explainers/cspell.json b/docs/versioned_docs/version-v0.37.0/explainers/cspell.json deleted file mode 100644 index c60b0a597b1..00000000000 --- a/docs/versioned_docs/version-v0.37.0/explainers/cspell.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "words": [ - "Cryptdoku" - ] -} diff --git a/docs/versioned_docs/version-v0.37.0/explainers/explainer-oracle.md b/docs/versioned_docs/version-v0.37.0/explainers/explainer-oracle.md deleted file mode 100644 index 821e1f95c04..00000000000 --- a/docs/versioned_docs/version-v0.37.0/explainers/explainer-oracle.md +++ /dev/null @@ -1,57 +0,0 @@ ---- -title: Oracles -description: This guide provides an in-depth understanding of how Oracles work in Noir programming. Learn how to use outside calculations in your programs, constrain oracles, and understand their uses and limitations. -keywords: - - Noir Programming - - Oracles - - JSON-RPC - - Foreign Call Handlers - - Constrained Functions - - Blockchain Programming -sidebar_position: 1 ---- - -If you've seen "The Matrix" you may recall "The Oracle" as Gloria Foster smoking cigarettes and baking cookies. While she appears to "know things", she is actually providing a calculation of a pre-determined future. Noir Oracles are similar, in a way. They don't calculate the future (yet), but they allow you to use outside calculations in your programs. - -![matrix oracle prediction](@site/static/img/memes/matrix_oracle.jpeg) - -A Noir program is usually self-contained. You can pass certain inputs to it, and it will generate a deterministic output for those inputs. But what if you wanted to defer some calculation to an outside process or source? - -Oracles are functions that provide this feature. - -## Use cases - -An example usage for Oracles is proving something on-chain. For example, proving that the ETH-USDC quote was below a certain target at a certain block time. Or even making more complex proofs like proving the ownership of an NFT as an anonymous login method. - -Another interesting use case is to defer expensive calculations to be made outside of the Noir program, and then constraining the result; similar to the use of [unconstrained functions](../noir/concepts//unconstrained.md). - -In short, anything that can be constrained in a Noir program but needs to be fetched from an external source is a great candidate to be used in oracles. - -## Constraining oracles - -Just like in The Matrix, Oracles are powerful. But with great power, comes great responsibility. Just because you're using them in a Noir program doesn't mean they're true. Noir has no superpowers. If you want to prove that Portugal won the Euro Cup 2016, you're still relying on potentially untrusted information. - -To give a concrete example, Alice wants to login to the [NounsDAO](https://nouns.wtf/) forum with her username "noir_nouner" by proving she owns a noun without revealing her ethereum address. Her Noir program could have an oracle call like this: - -```rust -#[oracle(getNoun)] -unconstrained fn get_noun(address: Field) -> Field -``` - -This oracle could naively resolve with the number of Nouns she possesses. However, it is useless as a trusted source, as the oracle could resolve to anything Alice wants. In order to make this oracle call actually useful, Alice would need to constrain the response from the oracle, by proving her address and the noun count belongs to the state tree of the contract. - -In short, **Oracles don't prove anything. Your Noir program does.** - -:::danger - -If you don't constrain the return of your oracle, you could be clearly opening an attack vector on your Noir program. Make double-triple sure that the return of an oracle call is constrained! - -::: - -## How to use Oracles - -On CLI, Nargo resolves oracles by making JSON RPC calls, which means it would require an RPC node to be running. - -In JavaScript, NoirJS accepts and resolves arbitrary call handlers (that is, not limited to JSON) as long as they match the expected types the developer defines. Refer to [Foreign Call Handler](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) to learn more about NoirJS's call handling. - -If you want to build using oracles, follow through to the [oracle guide](../how_to/how-to-oracles.md) for a simple example on how to do that. diff --git a/docs/versioned_docs/version-v0.37.0/explainers/explainer-recursion.md b/docs/versioned_docs/version-v0.37.0/explainers/explainer-recursion.md deleted file mode 100644 index df8529ef4e0..00000000000 --- a/docs/versioned_docs/version-v0.37.0/explainers/explainer-recursion.md +++ /dev/null @@ -1,176 +0,0 @@ ---- -title: Recursive proofs -description: Explore the concept of recursive proofs in Zero-Knowledge programming. Understand how recursion works in Noir, a language for writing smart contracts on the EVM blockchain. Learn through practical examples like Alice and Bob's guessing game, Charlie's recursive merkle tree, and Daniel's reusable components. Discover how to use recursive proofs to optimize computational resources and improve efficiency. - -keywords: - [ - "Recursive Proofs", - "Zero-Knowledge Programming", - "Noir", - "EVM Blockchain", - "Smart Contracts", - "Recursion in Noir", - "Alice and Bob Guessing Game", - "Recursive Merkle Tree", - "Reusable Components", - "Optimizing Computational Resources", - "Improving Efficiency", - "Verification Key", - "Aggregation", - "Recursive zkSNARK schemes", - "PLONK", - "Proving and Verification Keys" - ] -sidebar_position: 1 -pagination_next: how_to/how-to-recursion ---- - -In programming, we tend to think of recursion as something calling itself. A classic example would be the calculation of the factorial of a number: - -```js -function factorial(n) { - if (n === 0 || n === 1) { - return 1; - } else { - return n * factorial(n - 1); - } -} -``` - -In this case, while `n` is not `1`, this function will keep calling itself until it hits the base case, bubbling up the result on the call stack: - -```md - Is `n` 1? <--------- - /\ / - / \ n = n -1 - / \ / - Yes No -------- -``` - -In Zero-Knowledge, recursion has some similarities. - -It is not a Noir function calling itself, but a proof being used as an input to another circuit. In short, you verify one proof *inside* another proof, returning the proof that both proofs are valid. - -This means that, given enough computational resources, you can prove the correctness of any arbitrary number of proofs in a single proof. This could be useful to design state channels (for which a common example would be [Bitcoin's Lightning Network](https://en.wikipedia.org/wiki/Lightning_Network)), to save on gas costs by settling one proof on-chain, or simply to make business logic less dependent on a consensus mechanism. - -## Examples - -Let us look at some of these examples - -### Alice and Bob - Guessing game - -Alice and Bob are friends, and they like guessing games. They want to play a guessing game online, but for that, they need a trusted third-party that knows both of their secrets and finishes the game once someone wins. - -So, they use zero-knowledge proofs. Alice tries to guess Bob's number, and Bob will generate a ZK proof stating whether she succeeded or failed. - -This ZK proof can go on a smart contract, revealing the winner and even giving prizes. However, this means every turn needs to be verified on-chain. This incurs some cost and waiting time that may simply make the game too expensive or time-consuming to be worth it. - -As a solution, Alice proposes the following: "what if Bob generates his proof, and instead of sending it on-chain, I verify it *within* my own proof before playing my own turn?". - -She can then generate a proof that she verified his proof, and so on. - -```md - Did you fail? <-------------------------- - / \ / - / \ n = n -1 - / \ / - Yes No / - | | / - | | / - | You win / - | / - | / -Generate proof of that / - + / - my own guess ---------------- -``` - -### Charlie - Recursive merkle tree - -Charlie is a concerned citizen, and wants to be sure his vote in an election is accounted for. He votes with a ZK proof, but he has no way of knowing that his ZK proof was included in the total vote count! - -If the vote collector puts all of the votes into a [Merkle tree](https://en.wikipedia.org/wiki/Merkle_tree), everyone can prove the verification of two proofs within one proof, as such: - -```md - abcd - __________|______________ - | | - ab cd - _____|_____ ______|______ - | | | | - alice bob charlie daniel -``` - -Doing this recursively allows us to arrive on a final proof `abcd` which if true, verifies the correctness of all the votes. - -### Daniel - Reusable components - -Daniel has a big circuit and a big headache. A part of his circuit is a setup phase that finishes with some assertions that need to be made. But that section alone takes most of the proving time, and is largely independent of the rest of the circuit. - -He might find it more efficient to generate a proof for that setup phase separately, and verify that proof recursively in the actual business logic section of his circuit. This will allow for parallelization of both proofs, which results in a considerable speedup. - -## What params do I need - -As you can see in the [recursion reference](noir/standard_library/recursion.mdx), a simple recursive proof requires: - -- The proof to verify -- The Verification Key of the circuit that generated the proof -- A hash of this verification key, as it's needed for some backends -- The public inputs for the proof - -:::info - -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. - -So, taking the example of Alice and Bob and their guessing game: - -- Alice makes her guess. Her proof is *not* recursive: it doesn't verify any proof within it! It's just a standard `assert(x != y)` circuit -- Bob verifies Alice's proof and makes his own guess. In this circuit, he doesn't exactly *prove* the verification of Alice's proof. Instead, he *aggregates* his proof to Alice's proof. The actual verification is done when the full proof is verified, for example when using `nargo verify` or through the verifier smart contract. - -We can imagine recursive proofs a [relay race](https://en.wikipedia.org/wiki/Relay_race). The first runner doesn't have to receive the baton from anyone else, as he/she already starts with it. But when his/her turn is over, the next runner needs to receive it, run a bit more, and pass it along. Even though every runner could theoretically verify the baton mid-run (why not? 🏃🔍), only at the end of the race does the referee verify that the whole race is valid. - -::: - -## Some architecture - -As with everything in computer science, there's no one-size-fits all. But there are some patterns that could help understanding and implementing them. To give three examples: - -### Adding some logic to a proof verification - -This would be an approach for something like our guessing game, where proofs are sent back and forth and are verified by each opponent. This circuit would be divided in two sections: - -- A `recursive verification` section, which would be just the call to `std::verify_proof`, and that would be skipped on the first move (since there's no proof to verify) -- A `guessing` section, which is basically the logic part where the actual guessing happens - -In such a situation, and assuming Alice is first, she would skip the first part and try to guess Bob's number. Bob would then verify her proof on the first section of his run, and try to guess Alice's number on the second part, and so on. - -### Aggregating proofs - -In some one-way interaction situations, recursion would allow for aggregation of simple proofs that don't need to be immediately verified on-chain or elsewhere. - -To give a practical example, a barman wouldn't need to verify a "proof-of-age" on-chain every time he serves alcohol to a customer. Instead, the architecture would comprise two circuits: - -- A `main`, non-recursive circuit with some logic -- A `recursive` circuit meant to verify two proofs in one proof - -The customer's proofs would be intermediate, and made on their phones, and the barman could just verify them locally. He would then aggregate them into a final proof sent on-chain (or elsewhere) at the end of the day. - -### Recursively verifying different circuits - -Nothing prevents you from verifying different circuits in a recursive proof, for example: - -- A `circuit1` circuit -- A `circuit2` circuit -- A `recursive` circuit - -In this example, a regulator could verify that taxes were paid for a specific purchase by aggregating both a `payer` circuit (proving that a purchase was made and taxes were paid), and a `receipt` circuit (proving that the payment was received) - -## How fast is it - -At the time of writing, verifying recursive proofs is surprisingly fast. This is because most of the time is spent on generating the verification key that will be used to generate the next proof. So you are able to cache the verification key and reuse it later. - -Currently, Noir JS packages don't expose the functionality of loading proving and verification keys, but that feature exists in the underlying `bb.js` package. - -## How can I try it - -Learn more about using recursion in Nargo and NoirJS in the [how-to guide](../how_to/how-to-recursion.md) and see a full example in [noir-examples](https://github.com/noir-lang/noir-examples). diff --git a/docs/versioned_docs/version-v0.37.0/explainers/explainer-writing-noir.md b/docs/versioned_docs/version-v0.37.0/explainers/explainer-writing-noir.md deleted file mode 100644 index 3ce4245dc45..00000000000 --- a/docs/versioned_docs/version-v0.37.0/explainers/explainer-writing-noir.md +++ /dev/null @@ -1,177 +0,0 @@ ---- -title: Thinking in Circuits -description: Considerations when writing Noir programs -keywords: [Noir, programming, rust] -tags: [Optimization] -sidebar_position: 0 ---- - - -This article intends to set you up with key concepts essential for writing more viable applications that use zero knowledge proofs, namely around efficient circuits. - -## Context - 'Efficient' is subjective - -When writing a web application for a performant computer with high-speed internet connection, writing efficient code sometimes is seen as an afterthought only if needed. Large multiplications running at the innermost of nested loops may not even be on a dev's radar. -When writing firmware for a battery-powered microcontroller, you think of cpu cycles as rations to keep within a product's power budget. - -> Code is written to create applications that perform specific tasks within specific constraints - -And these constraints differ depending on where the compiled code is execute. - -### The Ethereum Virtual Machine (EVM) - -In scenarios where extremely low gas costs are required for an Ethereum application to be viable/competitive, Ethereum smart contract developers get into what is colloquially known as: "*gas golfing*". Finding the lowest execution cost of their compiled code (EVM bytecode) to achieve a specific task. - -The equivalent optimization task when writing zk circuits is affectionately referred to as "*gate golfing*", finding the lowest gate representation of the compiled Noir code. - -### Coding for circuits - a paradigm shift - -In zero knowledge cryptography, code is compiled to "circuits" consisting of arithmetic gates, and gate count is the significant cost. Depending on the proving system this is linearly proportionate to proving time, and so from a product point this should be kept as low as possible. - -Whilst writing efficient code for web apps and Solidity has a few key differences, writing efficient circuits have a different set of considerations. It is a bit of a paradigm shift, like writing code for GPUs for the first time... - -For example, drawing a circle at (0, 0) of radius `r`: -- For a single CPU thread, -``` -for theta in 0..2*pi { - let x = r * cos(theta); - let y = r * sin(theta); - draw(x, y); -} // note: would do 0 - pi/2 and draw +ve/-ve x and y. -``` - -- For GPUs (simultaneous parallel calls with x, y across image), -``` -if (x^2 + y^2 = r^2) { - draw(x, y); -} -``` - -([Related](https://www.youtube.com/watch?v=-P28LKWTzrI)) - -Whilst this CPU -> GPU does not translate to circuits exactly, it is intended to exemplify the difference in intuition when coding for different machine capabilities/constraints. - -### Context Takeaway - -For those coming from a primarily web app background, this article will explain what you need to consider when writing circuits. Furthermore, for those experienced writing efficient machine code, prepare to shift what you think is efficient 😬 - -## Translating from Rust - -For some applications using Noir, existing code might be a convenient starting point to then proceed to optimize the gate count of. - -:::note -Many valuable functions and algorithms have been written in more established languages (C/C++), and converted to modern ones (like Rust). -::: - -Fortunately for Noir developers, when needing a particular function a Rust implementation can be readily compiled into Noir with some key changes. While the compiler does a decent amount of optimizations, it won't be able to change code that has been optimized for clock-cycles into code optimized for arithmetic gates. - -A few things to do when converting Rust code to Noir: -- `println!` is not a macro, use `println` function (same for `assert_eq`) -- No early `return` in function. Use constrain via assertion instead -- No passing by reference. Remove `&` operator to pass by value (copy) -- No boolean operators (`&&`, `||`). Use bitwise operators (`&`, `|`) with boolean values -- No type `usize`. Use types `u8`, `u32`, `u64`, ... -- `main` return must be public, `pub` -- No `const`, use `global` -- Noir's LSP is your friend, so error message should be informative enough to resolve syntax issues. - -## Writing efficient Noir for performant products - -The following points help refine our understanding over time. - -:::note -A Noir program makes a statement that can be verified. -::: - -It compiles to a structure that represents the calculation, and can assert results within the calculation at any stage (via the `constrain` keyword). - -A Noir program compiles to an Abstract Circuit Intermediate Representation which is: - - Conceptually a tree structure - - Leaves (inputs) are the `Field` type - - Nodes contain arithmetic operations to combine them (gates) - - The root is the final result (return value) - -:::tip -The command `nargo info` shows the programs circuit size, and is useful to compare the value of changes made. -You can dig deeper and use the `--print-acir` param to take a closer look at individual ACIR opcodes, and the proving backend to see its gate count (eg for barretenberg, `bb gates -b ./target/program.json`). -::: - -### Use the `Field` type - -Since the native type of values in circuits are `Field`s, using them for variables in Noir means less gates converting them under the hood. -Some things to be mindful of when using a Field type for a regular integer value: -- A variable of type `Field` can be cast `as` an integer type (eg `u8`, `u64`) - - Note: this retains only the bits of the integer type. Eg a Field value of 260 as a `u8` becomes 4 -- For Field types arithmetic operations meaningfully overflow/underflow, yet for integer types they are checked according to their size -- Comparisons and bitwise operations do not exist for `Field`s, cast to an appropriately sized integer type when you need to - -:::tip -Where possible, use `Field` type for values. Using smaller value types, and bit-packing strategies, will result in MORE gates -::: - - -### Use Arithmetic over non-arithmetic operations - -Since circuits are made of arithmetic gates, the cost of arithmetic operations tends to be one gate. Whereas for procedural code, they represent several clock cycles. - -Inversely, non-arithmetic operators are achieved with multiple gates, vs 1 clock cycle for procedural code. - -| (cost\op) | arithmetic
(`*`, `+`) | bit-wise ops
(eg `<`, `\|`, `>>`) | -| - | - | - | -| **cycles** | 10+ | 1 | -| **gates** | 1 | 10+ | - -Bit-wise operations (e.g. bit shifts `<<` and `>>`), albeit commonly used in general programming and especially for clock cycle optimizations, are on the contrary expensive in gates when performed within circuits. - -Translate away from bit shifts when writing constrained functions for the best performance. - -On the flip side, feel free to use bit shifts in unconstrained functions and tests if necessary, as they are executed outside of circuits and does not induce performance hits. - -### Use static over dynamic values - -Another general theme that manifests in different ways is that static reads are represented with less gates than dynamic ones. - -Reading from read-only memory (ROM) adds less gates than random-access memory (RAM), 2 vs ~3.25 due to the additional bounds checks. Arrays of fixed length (albeit used at a lower capacity), will generate less gates than dynamic storage. - -Related to this, if an index used to access an array is not known at compile time (ie unknown until run time), then ROM will be converted to RAM, expanding the gate count. - -:::tip -Use arrays and indices that are known at compile time where possible. -Using `assert_constant(i);` before an index, `i`, is used in an array will give a compile error if `i` is NOT known at compile time. -::: - -### Leverage unconstrained execution - -Constrained verification can leverage unconstrained execution, this is especially useful for operations that are represented by many gates. -Use an [unconstrained function](../noir/concepts/unconstrained.md) to perform gate-heavy calculations, then verify and constrain the result. - -Eg division generates more gates than multiplication, so calculating the quotient in an unconstrained function then constraining the product for the quotient and divisor (+ any remainder) equals the dividend will be more efficient. - -Use ` if is_unconstrained() { /`, to conditionally execute code if being called in an unconstrained vs constrained way. - -## Advanced - -Unless you're well into the depth of gate optimization, this advanced section can be ignored. - -### Combine arithmetic operations - -A Noir program can be honed further by combining arithmetic operators in a way that makes the most of each constraint of the backend proving system. This is in scenarios where the backend might not be doing this perfectly. - -Eg Barretenberg backend (current default for Noir) is a width-4 PLONKish constraint system -$ w_1*w_2*q_m + w_1*q_1 + w_2*q_2 + w_3*q_3 + w_4*q_4 + q_c = 0 $ - -Here we see there is one occurrence of witness 1 and 2 ($w_1$, $w_2$) being multiplied together, with addition to witnesses 1-4 ($w_1$ .. $w_4$) multiplied by 4 corresponding circuit constants ($q_1$ .. $q_4$) (plus a final circuit constant, $q_c$). - -Use `nargo info --print-acir`, to inspect the ACIR opcodes (and the proving system for gates), and it may present opportunities to amend the order of operations and reduce the number of constraints. - -#### Variable as witness vs expression - -If you've come this far and really know what you're doing at the equation level, a temporary lever (that will become unnecessary/useless over time) is: `std::as_witness`. This informs the compiler to save a variable as a witness not an expression. - -The compiler will mostly be correct and optimal, but this may help some near term edge cases that are yet to optimize. -Note: When used incorrectly it will create **less** efficient circuits (higher gate count). - -## References -- Guillaume's ["`Cryptdoku`" talk](https://www.youtube.com/watch?v=MrQyzuogxgg) (Jun'23) -- Tips from Tom, Jake and Zac. -- [Idiomatic Noir](https://www.vlayer.xyz/blog/idiomatic-noir-part-1-collections) blog post diff --git a/docs/versioned_docs/version-v0.37.0/getting_started/noir_installation.md b/docs/versioned_docs/version-v0.37.0/getting_started/noir_installation.md deleted file mode 100644 index f92fd8dea38..00000000000 --- a/docs/versioned_docs/version-v0.37.0/getting_started/noir_installation.md +++ /dev/null @@ -1,102 +0,0 @@ ---- -title: Standalone Noir Installation -description: There are different ways to install Nargo, the one-stop shop and command-line tool for developing Noir programs. This guide explains how to specify which version to install when using noirup, and using WSL for windows. -keywords: [ - Installation - Nargo - Noirup - Binaries - Compiling from Source - WSL for Windows - macOS - Linux - Nix - Direnv - Uninstalling Nargo - ] -sidebar_position: 2 ---- - -Noirup is the endorsed method for installing Nargo, streamlining the process of fetching binaries or compiling from source. It supports a range of options to cater to your specific needs, from nightly builds and specific versions to compiling from various sources. - -### Installing Noirup - -First, ensure you have `noirup` installed: - -```sh -curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash -``` - -### Fetching Binaries - -With `noirup`, you can easily switch between different Nargo versions, including nightly builds: - -- **Nightly Version**: Install the latest nightly build. - - ```sh - noirup --version nightly - ``` - -- **Specific Version**: Install a specific version of Nargo. - - ```sh - noirup --version - ``` - -### Compiling from Source - -`noirup` also enables compiling Nargo from various sources: - -- **From a Specific Branch**: Install from the latest commit on a branch. - - ```sh - noirup --branch - ``` - -- **From a Fork**: Install from the main branch of a fork. - - ```sh - noirup --repo - ``` - -- **From a Specific Branch in a Fork**: Install from a specific branch in a fork. - - ```sh - noirup --repo --branch - ``` - -- **From a Specific Pull Request**: Install from a specific PR. - - ```sh - noirup --pr - ``` - -- **From a Specific Commit**: Install from a specific commit. - - ```sh - noirup -C - ``` - -- **From Local Source**: Compile and install from a local directory. - - ```sh - noirup --path ./path/to/local/source - ``` - -## Installation on Windows - -The default backend for Noir (Barretenberg) doesn't provide Windows binaries at this time. For that reason, Noir cannot be installed natively. 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](#installing-noirup). - -## Uninstalling Nargo - -If you installed Nargo with `noirup`, you can uninstall Nargo by removing the files in `~/.nargo`, `~/nargo`, and `~/noir_cache`. This ensures that all installed binaries, configurations, and cache related to Nargo are fully removed from your system. - -```bash -rm -r ~/.nargo -rm -r ~/nargo -rm -r ~/noir_cache -``` diff --git a/docs/versioned_docs/version-v0.37.0/getting_started/project_breakdown.md b/docs/versioned_docs/version-v0.37.0/getting_started/project_breakdown.md deleted file mode 100644 index e442e377040..00000000000 --- a/docs/versioned_docs/version-v0.37.0/getting_started/project_breakdown.md +++ /dev/null @@ -1,159 +0,0 @@ ---- -title: Project Breakdown -description: - Learn about the anatomy of a Nargo project, including the purpose of the Prover TOML - file, and how to prove and verify your program. -keywords: - [Nargo, Nargo project, Prover.toml, proof verification, private asset transfer] -sidebar_position: 1 ---- - -This section breaks down our hello world program from 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 - - 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. - -### 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 = "noir_starter" -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](../noir/modules_packages_crates/workspaces.md) will look a bit different. For example: - -```toml -[workspace] -members = ["crates/a", "crates/b"] -default-member = "crates/a" -``` - -#### Package section - -The package section defines 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) -- `expression_width` (optional) - Sets the default backend expression width. This field will override the default backend expression width specified by the Noir compiler (currently set to width 4). - -#### Dependencies section - -This is where you will specify any dependencies for your project. See the [Dependencies page](../noir/modules_packages_crates/dependencies.md) 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 that the condition to be satisfied (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 the inputs to the Noir program (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 execute` is executed, nargo will execute the Noir program using the inputs specified in `Prover.toml`, aborting if it finds that these do not satisfy the constraints defined by `main`. In this example, `x` and `y` must satisfy the inequality constraint `assert(x != y)`. - -If an output name is specified such as `nargo execute foo`, the witness generated by this execution will be written to `./target/foo.gz`. This can then be used to generate a proof of the execution. - -#### 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 execution by using the `--prover-name` or `-p` flags. - -This command looks for proof inputs in the default **Prover.toml** and generates the witness and saves it at `./target/foo.gz`: - -```bash -nargo execute foo -``` - -This command looks for proof inputs in the custom **OtherProver.toml** and generates the witness and saves it at `./target/bar.gz`: - -```bash -nargo execute -p OtherProver bar -``` - -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.37.0/getting_started/quick_start.md b/docs/versioned_docs/version-v0.37.0/getting_started/quick_start.md deleted file mode 100644 index 73ef62f6523..00000000000 --- a/docs/versioned_docs/version-v0.37.0/getting_started/quick_start.md +++ /dev/null @@ -1,124 +0,0 @@ ---- -title: Quick Start -tags: [] -sidebar_position: 0 ---- - -## Installation - -### Noir - -The easiest way to develop with Noir is using Nargo the CLI tool. It provides you the ability to start new projects, compile, execute and test Noir programs from the terminal. - -You can use `noirup` the installation script to quickly install and update Nargo: - -```bash -curl -L https://raw.githubusercontent.com/noir-lang/noirup/refs/heads/main/install | bash -noirup -``` - -### Proving backend - -After installing Noir, we install a proving backend to work with our Noir programs. - -Proving backends provide you the abilities to generate proofs, verify proofs, generate smart contracts and more for your Noir programs. - -Different proving backends provide different tools for working with Noir programs, here we will use the [Barretenberg proving backend](https://github.com/AztecProtocol/aztec-packages/tree/master/barretenberg) developed by Aztec Labs as an example. - -You can use the `bbup` installation script to quickly install and update BB, Barretenberg's CLI tool: - -You can find the full list of proving backends compatible with Noir in Awesome Noir. - -```bash -curl -L https://raw.githubusercontent.com/AztecProtocol/aztec-packages/refs/heads/master/barretenberg/bbup/install | bash -bbup -``` - -For the full list of proving backends compatible with Noir, visit [Awesome Noir](https://github.com/noir-lang/awesome-noir/?tab=readme-ov-file#proving-backends). - -## Nargo - -Nargo provides the ability to initiate and execute Noir projects. Let's initialize the traditional `hello_world`: - -```sh -nargo new hello_world -``` - -Two files will be created. - -- `src/main.nr` contains a simple boilerplate circuit -- `Nargo.toml` contains environmental options, such as name, author, dependencies, and others. - -Glancing at _main.nr_ , we can see that inputs in Noir are private by default, but can be labeled public using the keyword `pub`. This means that we will _assert_ that we know a value `x` which is different from `y` without revealing `x`: - -```rust -fn main(x : Field, y : pub Field) { - assert(x != y); -} -``` - -To learn more about private and public values, check the [Data Types](../noir/concepts/data_types/index.md) section. - -### Compiling and executing - -We can now use `nargo` to generate a _Prover.toml_ file, where our input values will be specified: - -```sh -cd hello_world -nargo check - -Let's feed some valid values into this file: - -```toml -x = "1" -y = "2" -``` - -We're now ready to compile and execute our Noir program. By default the `nargo execute` command will do both, and generate the `witness` that we need to feed to our proving backend: - -```sh -nargo execute -``` - -The witness corresponding to this execution will then be written to the file _./target/witness-name.gz_. - -The command also automatically compiles your Noir program if it was not already / was edited, which you may notice the compiled artifacts being written to the file _./target/hello_world.json_. - -With circuit compiled and witness generated, we're ready to prove. - -## Proving backend - -Different proving backends may provide different tools and commands to work with Noir programs. Here Barretenberg's `bb` CLI tool is used as an example: - -```sh -bb prove -b ./target/hello_world.json -w ./target/hello_world.gz -o ./target/proof -``` - -:::tip - -Naming can be confusing, specially as you pass them to the `bb` commands. If unsure, it won't hurt to delete the target folder and start anew to make sure you're using the most recent versions of the compiled circuit and witness. - -::: - -The proof is now generated in the `target` folder. To verify it we first need to compute the verification key from the compiled circuit, and use it to verify: - -```sh -bb write_vk -b ./target/hello_world.json -o ./target/vk -bb verify -k ./target/vk -p ./target/proof -``` - -:::info - -Notice that in order to verify a proof, the verifier knows nothing but the circuit, which is compiled and used to generate the verification key. This is obviously quite important: private inputs remain private. - -As for the public inputs, you may have noticed they haven't been specified. This behavior varies with each particular backend, but barretenberg typically attaches them to the proof. You can see them by parsing and splitting it. For example for if your public inputs are 32 bytes: - -```bash -head -c 32 ./target/proof | od -An -v -t x1 | tr -d $' \n' -``` - -::: - -Congratulations, you have now created and verified a proof for your very first Noir program! - -In the [next section](./project_breakdown.md), we will go into more detail on each step performed. diff --git a/docs/versioned_docs/version-v0.37.0/how_to/_category_.json b/docs/versioned_docs/version-v0.37.0/how_to/_category_.json deleted file mode 100644 index 23b560f610b..00000000000 --- a/docs/versioned_docs/version-v0.37.0/how_to/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "position": 1, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.37.0/how_to/debugger/_category_.json b/docs/versioned_docs/version-v0.37.0/how_to/debugger/_category_.json deleted file mode 100644 index cc2cbb1c253..00000000000 --- a/docs/versioned_docs/version-v0.37.0/how_to/debugger/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "Debugging", - "position": 5, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.37.0/how_to/debugger/debugging_with_the_repl.md b/docs/versioned_docs/version-v0.37.0/how_to/debugger/debugging_with_the_repl.md deleted file mode 100644 index 1d64dae3f37..00000000000 --- a/docs/versioned_docs/version-v0.37.0/how_to/debugger/debugging_with_the_repl.md +++ /dev/null @@ -1,164 +0,0 @@ ---- -title: Using the REPL Debugger -description: - Step-by-step guide on how to debug your Noir circuits with the REPL Debugger. -keywords: - [ - Nargo, - Noir CLI, - Noir Debugger, - REPL, - ] -sidebar_position: 1 ---- - -#### Pre-requisites - -In order to use the REPL debugger, first you need to install recent enough versions of Nargo and vscode-noir. - -## Debugging a simple circuit - -Let's debug a simple circuit: - -```rust -fn main(x : Field, y : pub Field) { - assert(x != y); -} -``` - -To start the REPL debugger, using a terminal, go to a Noir circuit's home directory. Then: - -`$ nargo debug` - -You should be seeing this in your terminal: - -``` -[main] Starting debugger -At ~/noir-examples/recursion/circuits/main/src/main.nr:1:9 - 1 -> fn main(x : Field, y : pub Field) { - 2 assert(x != y); - 3 } -> -``` - -The debugger displays the current Noir code location, and it is now waiting for us to drive it. - -Let's first take a look at the available commands. For that we'll use the `help` command. - -``` -> help -Available commands: - - opcodes display ACIR opcodes - into step into to the next opcode - next step until a new source location is reached - out step until a new source location is reached - and the current stack frame is finished - break LOCATION:OpcodeLocation add a breakpoint at an opcode location - over step until a new source location is reached - without diving into function calls - restart restart the debugging session - delete LOCATION:OpcodeLocation delete breakpoint at an opcode location - witness show witness map - witness index:u32 display a single witness from the witness map - witness index:u32 value:String update a witness with the given value - memset index:usize value:String update a memory cell with the given - value - continue continue execution until the end of the - program - vars show variable values available at this point - in execution - stacktrace display the current stack trace - memory show memory (valid when executing unconstrained code) - step step to the next ACIR opcode - -Other commands: - - help Show this help message - quit Quit repl - -``` - -Some commands operate only for unconstrained functions, such as `memory` and `memset`. If you try to use them while execution is paused at an ACIR opcode, the debugger will simply inform you that you are not executing unconstrained code: - -``` -> memory -Unconstrained VM memory not available -> -``` - -Before continuing, we can take a look at the initial witness map: - -``` -> witness -_0 = 1 -_1 = 2 -> -``` - -Cool, since `x==1`, `y==2`, and we want to check that `x != y`, our circuit should succeed. At this point we could intervene and use the witness setter command to change one of the witnesses. Let's set `y=3`, then back to 2, so we don't affect the expected result: - -``` -> witness -_0 = 1 -_1 = 2 -> witness 1 3 -_1 = 3 -> witness -_0 = 1 -_1 = 3 -> witness 1 2 -_1 = 2 -> witness -_0 = 1 -_1 = 2 -> -``` - -Now we can inspect the current state of local variables. For that we use the `vars` command. - -``` -> vars -> -``` - -We currently have no vars in context, since we are at the entry point of the program. Let's use `next` to execute until the next point in the program. - -``` -> vars -> next -At ~/noir-examples/recursion/circuits/main/src/main.nr:1:20 - 1 -> fn main(x : Field, y : pub Field) { - 2 assert(x != y); - 3 } -> vars -x:Field = 0x01 -``` - -As a result of stepping, the variable `x`, whose initial value comes from the witness map, is now in context and returned by `vars`. - -``` -> next - 1 fn main(x : Field, y : pub Field) { - 2 -> assert(x != y); - 3 } -> vars -y:Field = 0x02 -x:Field = 0x01 -``` - -Stepping again we can finally see both variables and their values. And now we can see that the next assertion should succeed. - -Let's continue to the end: - -``` -> continue -(Continuing execution...) -Finished execution -> q -[main] Circuit witness successfully solved -``` - -Upon quitting the debugger after a solved circuit, the resulting circuit witness gets saved, equivalent to what would happen if we had run the same circuit with `nargo execute`. - -We just went through the basics of debugging using Noir REPL debugger. For a comprehensive reference, check out [the reference page](../../reference/debugger/debugger_repl.md). diff --git a/docs/versioned_docs/version-v0.37.0/how_to/debugger/debugging_with_vs_code.md b/docs/versioned_docs/version-v0.37.0/how_to/debugger/debugging_with_vs_code.md deleted file mode 100644 index a5858c1a5eb..00000000000 --- a/docs/versioned_docs/version-v0.37.0/how_to/debugger/debugging_with_vs_code.md +++ /dev/null @@ -1,68 +0,0 @@ ---- -title: Using the VS Code Debugger -description: - Step by step guide on how to debug your Noir circuits with the VS Code Debugger configuration and features. -keywords: - [ - Nargo, - Noir CLI, - Noir Debugger, - VS Code, - IDE, - ] -sidebar_position: 0 ---- - -This guide will show you how to use VS Code with the vscode-noir extension to debug a Noir project. - -#### Pre-requisites - -- Nargo -- vscode-noir -- A Noir project with a `Nargo.toml`, `Prover.toml` and at least one Noir (`.nr`) containing an entry point function (typically `main`). - -## Running the debugger - -The easiest way to start debugging is to open the file you want to debug, and press `F5`. This will cause the debugger to launch, using your `Prover.toml` file as input. - -You should see something like this: - -![Debugger launched](@site/static/img/debugger/1-started.png) - -Let's inspect the state of the program. For that, we open VS Code's _Debug pane_. Look for this icon: - -![Debug pane icon](@site/static/img/debugger/2-icon.png) - -You will now see two categories of variables: Locals and Witness Map. - -![Debug pane expanded](@site/static/img/debugger/3-debug-pane.png) - -1. **Locals**: variables of your program. At this point in execution this section is empty, but as we step through the code it will get populated by `x`, `result`, `digest`, etc. - -2. **Witness map**: these are initially populated from your project's `Prover.toml` file. In this example, they will be used to populate `x` and `result` at the beginning of the `main` function. - -Most of the time you will probably be focusing mostly on locals, as they represent the high level state of your program. - -You might be interested in inspecting the witness map in case you are trying to solve a really low level issue in the compiler or runtime itself, so this concerns mostly advanced or niche users. - -Let's step through the program, by using the debugger buttons or their corresponding keyboard shortcuts. - -![Debugger buttons](@site/static/img/debugger/4-debugger-buttons.png) - -Now we can see in the variables pane that there's values for `digest`, `result` and `x`. - -![Inspecting locals](@site/static/img/debugger/5-assert.png) - -We can also inspect the values of variables by directly hovering on them on the code. - -![Hover locals](@site/static/img/debugger/6-hover.png) - -Let's set a break point at the `keccak256` function, so we can continue execution up to the point when it's first invoked without having to go one step at a time. - -We just need to click the to the right of the line number 18. Once the breakpoint appears, we can click the `continue` button or use its corresponding keyboard shortcut (`F5` by default). - -![Breakpoint](@site/static/img/debugger/7-break.png) - -Now we are debugging the `keccak256` function, notice the _Call Stack pane_ at the lower right. This lets us inspect the current call stack of our process. - -That covers most of the current debugger functionalities. Check out [the reference](../../reference/debugger/debugger_vscode.md) for more details on how to configure the debugger. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.37.0/how_to/how-to-oracles.md b/docs/versioned_docs/version-v0.37.0/how_to/how-to-oracles.md deleted file mode 100644 index 08ce6ee5476..00000000000 --- a/docs/versioned_docs/version-v0.37.0/how_to/how-to-oracles.md +++ /dev/null @@ -1,275 +0,0 @@ ---- -title: How to use Oracles -description: Learn how to use oracles in your Noir program with examples in both Nargo and NoirJS. This guide also covers writing a JSON RPC server and providing custom foreign call handlers for NoirJS. -keywords: - - Noir Programming - - Oracles - - Nargo - - NoirJS - - JSON RPC Server - - Foreign Call Handlers -sidebar_position: 1 ---- - -This guide shows you how to use oracles in your Noir program. For the sake of clarity, it assumes that: - -- You have read the [explainer on Oracles](../explainers/explainer-oracle.md) and are comfortable with the concept. -- You have a Noir program to add oracles to. You can create one using the [vite-hardhat starter](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat) as a boilerplate. -- You understand the concept of a JSON-RPC server. Visit the [JSON-RPC website](https://www.jsonrpc.org/) if you need a refresher. -- You are comfortable with server-side JavaScript (e.g. Node.js, managing packages, etc.). - -## Rundown - -This guide has 3 major steps: - -1. How to modify our Noir program to make use of oracle calls as unconstrained functions -2. How to write a JSON RPC Server to resolve these oracle calls with Nargo -3. How to use them in Nargo and how to provide a custom resolver in NoirJS - -## Step 1 - Modify your Noir program - -An oracle is defined in a Noir program by defining two methods: - -- An unconstrained method - This tells the compiler that it is executing an [unconstrained functions](../noir/concepts//unconstrained.md). -- A decorated oracle method - This tells the compiler that this method is an RPC call. - -An example of an oracle that returns a `Field` would be: - -```rust -#[oracle(getSqrt)] -unconstrained fn sqrt(number: Field) -> Field { } - -unconstrained fn get_sqrt(number: Field) -> Field { - sqrt(number) -} -``` - -In this example, we're wrapping our oracle function in an unconstrained method, and decorating it with `oracle(getSqrt)`. We can then call the unconstrained function as we would call any other function: - -```rust -fn main(input: Field) { - let sqrt = get_sqrt(input); -} -``` - -In the next section, we will make this `getSqrt` (defined on the `sqrt` decorator) be a method of the RPC server Noir will use. - -:::danger - -As explained in the [Oracle Explainer](../explainers/explainer-oracle.md), this `main` function is unsafe unless you constrain its return value. For example: - -```rust -fn main(input: Field) { - let sqrt = get_sqrt(input); - assert(sqrt.pow_32(2) as u64 == input as u64); // <---- constrain the return of an oracle! -} -``` - -::: - -:::info - -Currently, oracles only work with single params or array params. For example: - -```rust -#[oracle(getSqrt)] -unconstrained fn sqrt([Field; 2]) -> [Field; 2] { } -``` - -::: - -## Step 2 - Write an RPC server - -Brillig will call *one* RPC server. Most likely you will have to write your own, and you can do it in whatever language you prefer. In this guide, we will do it in Javascript. - -Let's use the above example of an oracle that consumes an array with two `Field` and returns their square roots: - -```rust -#[oracle(getSqrt)] -unconstrained fn sqrt(input: [Field; 2]) -> [Field; 2] { } - -unconstrained fn get_sqrt(input: [Field; 2]) -> [Field; 2] { - sqrt(input) -} - -fn main(input: [Field; 2]) { - let sqrt = get_sqrt(input); - assert(sqrt[0].pow_32(2) as u64 == input[0] as u64); - assert(sqrt[1].pow_32(2) as u64 == input[1] as u64); -} - -#[test] -fn test() { - let input = [4, 16]; - main(input); -} -``` - -:::info - -Why square root? - -In general, computing square roots is computationally more expensive than multiplications, which takes a toll when speaking about ZK applications. In this case, instead of calculating the square root in Noir, we are using our oracle to offload that computation to be made in plain. In our circuit we can simply multiply the two values. - -::: - -Now, we should write the correspondent RPC server, starting with the [default JSON-RPC 2.0 boilerplate](https://www.npmjs.com/package/json-rpc-2.0#example): - -```js -import { JSONRPCServer } from "json-rpc-2.0"; -import express from "express"; -import bodyParser from "body-parser"; - -const app = express(); -app.use(bodyParser.json()); - -const server = new JSONRPCServer(); -app.post("/", (req, res) => { - const jsonRPCRequest = req.body; - server.receive(jsonRPCRequest).then((jsonRPCResponse) => { - if (jsonRPCResponse) { - res.json(jsonRPCResponse); - } else { - res.sendStatus(204); - } - }); -}); - -app.listen(5555); -``` - -Now, we will add our `getSqrt` method, as expected by the `#[oracle(getSqrt)]` decorator in our Noir code. It maps through the params array and returns their square roots: - -```js -server.addMethod("resolve_foreign_call", async (params) => { - if (params[0].function !== "getSqrt") { - throw Error("Unexpected foreign call") - }; - const values = params[0].inputs[0].map((field) => { - return `${Math.sqrt(parseInt(field, 16))}`; - }); - return { values: [values] }; -}); -``` - -If you're using Typescript, the following types may be helpful in understanding the expected return value and making sure they're easy to follow: - -```js -export type ForeignCallSingle = string; - -export type ForeignCallArray = string[]; - -export type ForeignCallResult = { - values: (ForeignCallSingle | ForeignCallArray)[]; -}; -``` - -:::info Multidimensional Arrays - -If the Oracle function is returning an array containing other arrays, such as `[['1','2],['3','4']]`, you need to provide the values in JSON as flattened values. In the previous example, it would be `['1', '2', '3', '4']`. In the Noir program, the Oracle signature can use a nested type, the flattened values will be automatically converted to the nested type. - -::: - -## Step 3 - Usage with Nargo - -Using the [`nargo` CLI tool](../reference/nargo_commands.md), you can use oracles in the `nargo test` and `nargo execute` commands by passing a value to `--oracle-resolver`. For example: - -```bash -nargo test --oracle-resolver http://localhost:5555 -``` - -This tells `nargo` to use your RPC Server URL whenever it finds an oracle decorator. - -## Step 4 - Usage with NoirJS - -In a JS environment, an RPC server is not strictly necessary, as you may want to resolve your oracles without needing any JSON call at all. NoirJS simply expects that you pass a callback function when you generate proofs, and that callback function can be anything. - -For example, if your Noir program expects the host machine to provide CPU pseudo-randomness, you could simply pass it as the `foreignCallHandler`. You don't strictly need to create an RPC server to serve pseudo-randomness, as you may as well get it directly in your app: - -```js -const foreignCallHandler = (name, inputs) => crypto.randomBytes(16) // etc - -await noir.execute(inputs, foreignCallHandler) -``` - -As one can see, in NoirJS, the [`foreignCallHandler`](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) function simply means "a callback function that returns a value of type [`ForeignCallOutput`](../reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md). It doesn't have to be an RPC call like in the case for Nargo. - -:::tip - -Does this mean you don't have to write an RPC server like in [Step #2](#step-2---write-an-rpc-server)? - -You don't technically have to, but then how would you run `nargo test`? To use both `Nargo` and `NoirJS` in your development flow, you will have to write a JSON RPC server. - -::: - -In this case, let's make `foreignCallHandler` call the JSON RPC Server we created in [Step #2](#step-2---write-an-rpc-server), by making it a JSON RPC Client. - -For example, using the same `getSqrt` program in [Step #1](#step-1---modify-your-noir-program) (comments in the code): - -```js -import { JSONRPCClient } from "json-rpc-2.0"; - -// declaring the JSONRPCClient -const client = new JSONRPCClient((jsonRPCRequest) => { -// hitting the same JSON RPC Server we coded above - return fetch("http://localhost:5555", { - method: "POST", - headers: { - "content-type": "application/json", - }, - body: JSON.stringify(jsonRPCRequest), - }).then((response) => { - if (response.status === 200) { - return response - .json() - .then((jsonRPCResponse) => client.receive(jsonRPCResponse)); - } else if (jsonRPCRequest.id !== undefined) { - return Promise.reject(new Error(response.statusText)); - } - }); -}); - -// declaring a function that takes the name of the foreign call (getSqrt) and the inputs -const foreignCallHandler = async (name, input) => { - const inputs = input[0].map((i) => i.toString("hex")) - // notice that the "inputs" parameter contains *all* the inputs - // in this case we to make the RPC request with the first parameter "numbers", which would be input[0] - const oracleReturn = await client.request("resolve_foreign_call", [ - { - function: name, - inputs: [inputs] - }, - ]); - return [oracleReturn.values[0]]; -}; - -// the rest of your NoirJS code -const input = { input: [4, 16] }; -const { witness } = await noir.execute(input, foreignCallHandler); -``` - -:::tip - -If you're in a NoirJS environment running your RPC server together with a frontend app, you'll probably hit a familiar problem in full-stack development: requests being blocked by [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) policy. For development only, you can simply install and use the [`cors` npm package](https://www.npmjs.com/package/cors) to get around the problem: - -```bash -yarn add cors -``` - -and use it as a middleware: - -```js -import cors from "cors"; - -const app = express(); -app.use(cors()) -``` - -::: - -## Conclusion - -Hopefully by the end of this guide, you should be able to: - -- Write your own logic around Oracles and how to write a JSON RPC server to make them work with your Nargo commands. -- Provide custom foreign call handlers for NoirJS. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.37.0/how_to/how-to-recursion.md b/docs/versioned_docs/version-v0.37.0/how_to/how-to-recursion.md deleted file mode 100644 index fac79a9cf49..00000000000 --- a/docs/versioned_docs/version-v0.37.0/how_to/how-to-recursion.md +++ /dev/null @@ -1,172 +0,0 @@ ---- -title: How to use recursion on NoirJS -description: Learn how to implement recursion with NoirJS, a powerful tool for creating smart contracts on the EVM blockchain. This guide assumes familiarity with NoirJS, solidity verifiers, and the Barretenberg proving backend. Discover how to generate both final and intermediate proofs using `noir_js` and `bb.js`. -keywords: - [ - "NoirJS", - "EVM blockchain", - "smart contracts", - "recursion", - "solidity verifiers", - "Barretenberg backend", - "noir_js", - "intermediate proofs", - "final proofs", - "nargo compile", - "json import", - "recursive circuit", - "recursive app" - ] -sidebar_position: 1 ---- - -This guide shows you how to use recursive proofs in your NoirJS app. For the sake of clarity, it is assumed that: - -- You already have a NoirJS app. If you don't, please visit the [NoirJS tutorial](../tutorials/noirjs_app.md) and the [reference](../reference/NoirJS/noir_js/index.md). -- You are familiar with what are recursive proofs and you have read the [recursion explainer](../explainers/explainer-recursion.md) -- You already built a recursive circuit following [the reference](../noir/standard_library/recursion.mdx), and understand how it works. - -It is also assumed that you're not using `noir_wasm` for compilation, and instead you've used [`nargo compile`](../reference/nargo_commands.md) to generate the `json` you're now importing into your project. However, the guide should work just the same if you're using `noir_wasm`. - -:::info - -As you've read in the [explainer](../explainers/explainer-recursion.md), a recursive proof is an intermediate proof. This means that it doesn't necessarily generate the final step that makes it verifiable in a smart contract. However, it is easy to verify within another circuit. - -::: - -In a standard recursive app, you're also dealing with at least two circuits. For the purpose of this guide, we will assume the following: - -- `main`: a circuit of type `assert(x != y)`, where `main` is marked with a `#[recursive]` attribute. This attribute states that the backend should generate proofs that are friendly for verification within another circuit. -- `recursive`: a circuit that verifies `main` - -For a full example of how recursive proofs work, please refer to the [noir-examples](https://github.com/noir-lang/noir-examples) repository. We will *not* be using it as a reference for this guide. - -## Step 1: Setup - -In a common NoirJS app, you need to instantiate a backend with something like `const backend = new Backend(circuit)`. Then you feed it to the `noir_js` interface. - -For recursion, this doesn't happen, and the only need for `noir_js` is only to `execute` a circuit and get its witness and return value. Everything else is not interfaced, so it needs to happen on the `backend` object. - -It is also recommended that you instantiate the backend with as many threads as possible, to allow for maximum concurrency: - -```js -const backend = new Backend(circuit, { threads: 8 }) -``` - -:::tip -You can use the [`os.cpus()`](https://nodejs.org/api/os.html#oscpus) object in `nodejs` or [`navigator.hardwareConcurrency`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/hardwareConcurrency) on the browser to make the most out of those glorious cpu cores -::: - -## Step 2: Generating the witness and the proof for `main` - -After instantiating the backend, you should also instantiate `noir_js`. We will use it to execute the circuit and get the witness. - -```js -const noir = new Noir(circuit) -const { witness } = noir.execute(input) -``` - -With this witness, you are now able to generate the intermediate proof for the main circuit: - -```js -const { proof, publicInputs } = await backend.generateProof(witness) -``` - -:::warning - -Always keep in mind what is actually happening on your development process, otherwise you'll quickly become confused about what circuit we are actually running and why! - -In this case, you can imagine that Alice (running the `main` circuit) is proving something to Bob (running the `recursive` circuit), and Bob is verifying her proof within his proof. - -With this in mind, it becomes clear that our intermediate proof is the one *meant to be verified within another circuit*, so it must be Alice's. Actually, the only final proof in this theoretical scenario would be the last one, sent on-chain. - -::: - -## Step 3 - Verification and proof artifacts - -Optionally, you are able to verify the intermediate proof: - -```js -const verified = await backend.verifyProof({ proof, publicInputs }) -``` - -This can be useful to make sure our intermediate proof was correctly generated. But the real goal is to do it within another circuit. For that, we need to generate recursive proof artifacts that will be passed to the circuit that is verifying the proof we just generated. Instead of passing the proof and verification key as a byte array, we pass them as fields which makes it cheaper to verify in a circuit: - -```js -const { proofAsFields, vkAsFields, vkHash } = await backend.generateRecursiveProofArtifacts( { publicInputs, proof }, publicInputsCount) -``` - -This call takes the public inputs and the proof, but also the public inputs count. While this is easily retrievable by simply counting the `publicInputs` length, the backend interface doesn't currently abstract it away. - -:::info - -The `proofAsFields` has a constant size `[Field; 93]` and verification keys in Barretenberg are always `[Field; 114]`. - -::: - -:::warning - -One common mistake is to forget *who* makes this call. - -In a situation where Alice is generating the `main` proof, if she generates the proof artifacts and sends them to Bob, which gladly takes them as true, this would mean Alice could prove anything! - -Instead, Bob needs to make sure *he* extracts the proof artifacts, using his own instance of the `main` circuit backend. This way, Alice has to provide a valid proof for the correct `main` circuit. - -::: - -## Step 4 - Recursive proof generation - -With the artifacts, generating a recursive proof is no different from a normal proof. You simply use the `backend` (with the recursive circuit) to generate it: - -```js -const recursiveInputs = { - verification_key: vkAsFields, // array of length 114 - proof: proofAsFields, // array of length 93 + size of public inputs - publicInputs: [mainInput.y], // using the example above, where `y` is the only public input - key_hash: vkHash, -} - -const { witness, returnValue } = noir.execute(recursiveInputs) // we're executing the recursive circuit now! -const { proof, publicInputs } = backend.generateProof(witness) -const verified = backend.verifyProof({ proof, publicInputs }) -``` - -You can obviously chain this proof into another proof. In fact, if you're using recursive proofs, you're probably interested of using them this way! - -:::tip - -Managing circuits and "who does what" can be confusing. To make sure your naming is consistent, you can keep them in an object. For example: - -```js -const circuits = { - main: mainJSON, - recursive: recursiveJSON -} -const backends = { - main: new BarretenbergBackend(circuits.main), - recursive: new BarretenbergBackend(circuits.recursive) -} -const noir_programs = { - main: new Noir(circuits.main), - recursive: new Noir(circuits.recursive) -} -``` - -This allows you to neatly call exactly the method you want without conflicting names: - -```js -// Alice runs this 👇 -const { witness: mainWitness } = await noir_programs.main.execute(input) -const proof = await backends.main.generateProof(mainWitness) - -// Bob runs this 👇 -const verified = await backends.main.verifyProof(proof) -const { proofAsFields, vkAsFields, vkHash } = await backends.main.generateRecursiveProofArtifacts( - proof, - numPublicInputs, -); -const { witness: recursiveWitness } = await noir_programs.recursive.execute(recursiveInputs) -const recursiveProof = await backends.recursive.generateProof(recursiveWitness); -``` - -::: diff --git a/docs/versioned_docs/version-v0.37.0/how_to/how-to-solidity-verifier.md b/docs/versioned_docs/version-v0.37.0/how_to/how-to-solidity-verifier.md deleted file mode 100644 index 2cc0f8e57ce..00000000000 --- a/docs/versioned_docs/version-v0.37.0/how_to/how-to-solidity-verifier.md +++ /dev/null @@ -1,259 +0,0 @@ ---- -title: Generate a 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, - ] -sidebar_position: 0 -pagination_next: tutorials/noirjs_app ---- - -Noir has the ability to generate a verifier contract in Solidity, which can be deployed in many EVM-compatible blockchains such as Ethereum. - -This allows for a powerful feature set, as one can make use of the conciseness and the privacy provided by Noir in an immutable ledger. Applications can range from simple P2P guessing games, to complex private DeFi interactions. - -This guide shows you how to generate a Solidity Verifier and deploy it on the [Remix IDE](https://remix.ethereum.org/). It is assumed that: - -- You are comfortable with the Solidity programming language and understand how contracts are deployed on the Ethereum network -- You have Noir installed and you have a Noir program. If you don't, [get started](../getting_started/quick_start.md) with Nargo and the example Hello Noir circuit -- You are comfortable navigating RemixIDE. If you aren't or you need a refresher, you can find some video tutorials [here](https://www.youtube.com/channel/UCjTUPyFEr2xDGN6Cg8nKDaA) that could help you. - -## Rundown - -Generating a Solidity Verifier contract is actually a one-command process. However, compiling it and deploying it can have some caveats. Here's the rundown of this guide: - -1. How to generate a solidity smart contract -2. How to compile the smart contract in the RemixIDE -3. How to deploy it to a testnet - -## Step 1 - Generate a contract - -This is by far the most straightforward step. Just run: - -```sh -nargo compile -``` - -This will compile your source code into a Noir build artifact to be stored in the `./target` directory, you can then generate the smart contract using the commands: - -```sh -# Here we pass the path to the newly generated Noir artifact. -bb write_vk -b ./target/.json -bb contract -``` - -replacing `` with the name of your Noir project. A new `contract` folder would then be generated in your project directory, containing the Solidity -file `contract.sol`. It can be deployed to any EVM blockchain acting as a verifier smart contract. - -You can find more information about `bb` and the default Noir proving backend on [this page](../getting_started/quick_start.md#proving-backend). - -:::info - -It is possible to generate 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 generation of verifier contracts, for the time being these are only in Solidity. -::: - -## Step 2 - Compiling - -We will mostly skip the details of RemixIDE, as the UI can change from version to version. For now, we can just open -Remix and create a blank workspace. - -![Create Workspace](@site/static/img/how-tos/solidity_verifier_1.png) - -We will create a new file to contain the contract Nargo generated, and copy-paste its content. - -:::warning - -You'll likely see a warning advising you to not trust pasted code. While it is an important warning, it is irrelevant in the context of this guide and can be ignored. We will not be deploying anywhere near a mainnet. - -::: - -To compile our the verifier, we can navigate to the compilation tab: - -![Compilation Tab](@site/static/img/how-tos/solidity_verifier_2.png) - -Remix should automatically match a suitable compiler version. However, hitting the "Compile" button will most likely generate a "Stack too deep" error: - -![Stack too deep](@site/static/img/how-tos/solidity_verifier_3.png) - -This is due to the verify function needing to put many variables on the stack, but enabling the optimizer resolves the issue. To do this, let's open the "Advanced Configurations" tab and enable optimization. The default 200 runs will suffice. - -:::info - -This time we will see a warning about an unused function parameter. This is expected, as the `verify` function doesn't use the `_proof` parameter inside a solidity block, it is loaded from calldata and used in assembly. - -::: - -![Compilation success](@site/static/img/how-tos/solidity_verifier_4.png) - -## Step 3 - Deploying - -At this point we should have a compiled contract ready to deploy. If we navigate to the deploy section in Remix, we will see many different environments we can deploy to. The steps to deploy on each environment would be out-of-scope for this guide, so we will just use the default Remix VM. - -Looking closely, we will notice that our "Solidity Verifier" is actually three contracts working together: - -- An `UltraVerificationKey` library which simply stores the verification key for our circuit. -- An abstract contract `BaseUltraVerifier` containing most of the verifying logic. -- A main `UltraVerifier` contract that inherits from the Base and uses the Key contract. - -Remix will take care of the dependencies for us so we can simply deploy the UltraVerifier contract by selecting it and hitting "deploy": - -![Deploying UltraVerifier](@site/static/img/how-tos/solidity_verifier_5.png) - -A contract will show up in the "Deployed Contracts" section, where we can retrieve the Verification Key Hash. This is particularly useful for double-checking that the deployer contract is the correct one. - -:::note - -Why "UltraVerifier"? - -To be precise, the Noir compiler (`nargo`) doesn't generate the verifier contract directly. It compiles the Noir code into an intermediate language (ACIR), which is then executed by the backend. So it is the backend that returns the verifier smart contract, not Noir. - -In this case, the Barretenberg Backend uses the UltraPlonk proving system, hence the "UltraVerifier" name. - -::: - -## Step 4 - Verifying - -To verify a proof using the Solidity verifier contract, we call the `verify` function in this extended contract: - -```solidity -function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external view returns (bool) -``` - -When using the default example in the [Hello Noir](../getting_started/quick_start.md) guide, the easiest way to confirm that the verifier contract is doing its job is by calling the `verify` function via remix with the required parameters. Note that the public inputs must be passed in separately to the rest of the proof so we must split the proof as returned from `bb`. - -First generate a proof with `bb` at the location `./proof` using the steps in [get started](../getting_started/quick_start.md), this proof is in a binary format but we want to convert it into a hex string to pass into Remix, this can be done with the - -```bash -# This value must be changed to match the number of public inputs (including return values!) in your program. -NUM_PUBLIC_INPUTS=1 -PUBLIC_INPUT_BYTES=32*NUM_PUBLIC_INPUTS -HEX_PUBLIC_INPUTS=$(head -c $PUBLIC_INPUT_BYTES ./proof | od -An -v -t x1 | tr -d $' \n') -HEX_PROOF=$(tail -c +$(($PUBLIC_INPUT_BYTES + 1)) ./proof | od -An -v -t x1 | tr -d $' \n') - -echo "Public inputs:" -echo $HEX_PUBLIC_INPUTS - -echo "Proof:" -echo "0x$HEX_PROOF" -``` - -Remix expects that the public inputs will be split into an array of `bytes32` values so `HEX_PUBLIC_INPUTS` needs to be split up into 32 byte chunks which are prefixed with `0x` accordingly. - -A programmatic example of how the `verify` function is called can be seen 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"); -``` - -:::info[Return Values] - -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. - -For example, if you have Noir program like this: - -```rust -fn main( - // Public inputs - pubkey_x: pub Field, - pubkey_y: pub Field, - // Private inputs - priv_key: Field, -) -> pub Field -``` - -the `verify` function will expect the public inputs array (second function parameter) to be of length 3, the two inputs and the return value. - -Passing only two inputs will result in an error such as `PUBLIC_INPUT_COUNT_INVALID(3, 2)`. - -In this case, the inputs parameter to `verify` would be an array ordered as `[pubkey_x, pubkey_y, return`. - -::: - -:::tip[Structs] - -You can pass structs to the verifier contract. They will be flattened so that the array of inputs is 1-dimensional array. - -For example, 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) { - //... -} -``` - -The order of these inputs would be flattened to: `[x, nested.t1.val1, nested.t1.val2, nested.is_true, y]` - -::: - -The other function you can call is our entrypoint `verify` function, as defined above. - -:::tip - -It's worth noticing that the `verify` function is actually a `view` function. A `view` function does not alter the blockchain state, so it doesn't need to be distributed (i.e. it will run only on the executing node), and therefore doesn't cost any gas. - -This can be particularly useful in some situations. If Alice generated a proof and wants Bob to verify its correctness, Bob doesn't need to run Nargo, NoirJS, or any Noir specific infrastructure. He can simply make a call to the blockchain with the proof and verify it is correct without paying any gas. - -It would be incorrect to say that a Noir proof verification costs any gas at all. However, most of the time the result of `verify` is used to modify state (for example, to update a balance, a game state, etc). In that case the whole network needs to execute it, which does incur gas costs (calldata and execution, but not storage). - -::: - -## A Note on EVM chains - -Noir proof verification requires the ecMul, ecAdd and ecPairing precompiles. Not all EVM chains support EC Pairings, notably some of the ZK-EVMs. This means that you won't be able to use the verifier contract in all of them. You can find an incomplete list of which EVM chains support these precompiles [here](https://www.evmdiff.com/features?feature=precompiles). - -For example, chains like `zkSync ERA` and `Polygon zkEVM` do not currently support these precompiles, so proof verification via Solidity verifier contracts won't work. Here's a quick list of EVM chains that have been tested and are known to work: - -- Optimism -- Arbitrum -- Polygon PoS -- Scroll -- Celo -- BSC -- Blast L2 -- Avalanche C-Chain -- Mode -- Linea -- Moonbeam - -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. - -## What's next - -Now that you know how to call a Noir Solidity Verifier on a smart contract using Remix, you should be comfortable with using it with some programmatic frameworks, such as [hardhat](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat) and [foundry](https://github.com/noir-lang/noir-starter/tree/main/with-foundry). - -You can find other tools, examples, boilerplates and libraries in the [awesome-noir](https://github.com/noir-lang/awesome-noir) repository. - -You should also be ready to write and deploy your first NoirJS app and start generating proofs on websites, phones, and NodeJS environments! Head on to the [NoirJS tutorial](../tutorials/noirjs_app.md) to learn how to do that. diff --git a/docs/versioned_docs/version-v0.37.0/how_to/merkle-proof.mdx b/docs/versioned_docs/version-v0.37.0/how_to/merkle-proof.mdx deleted file mode 100644 index 0a128adb2de..00000000000 --- a/docs/versioned_docs/version-v0.37.0/how_to/merkle-proof.mdx +++ /dev/null @@ -1,48 +0,0 @@ ---- -title: Prove Merkle Tree 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] -sidebar_position: 4 ---- - -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 - -fn main(message : [Field; 62], index : Field, hashpath : [Field; 40], root : Field) { - let leaf = std::hash::hash_to_field(message.as_slice()); - 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.as_slice()); -``` - -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.37.0/how_to/using-devcontainers.mdx b/docs/versioned_docs/version-v0.37.0/how_to/using-devcontainers.mdx deleted file mode 100644 index 727ec6ca667..00000000000 --- a/docs/versioned_docs/version-v0.37.0/how_to/using-devcontainers.mdx +++ /dev/null @@ -1,110 +0,0 @@ ---- -title: Developer Containers and Codespaces -description: "Learn how to set up a devcontainer in your GitHub repository for a seamless coding experience with Codespaces. Follow our easy 8-step guide to create your own Noir environment without installing Nargo locally." -keywords: ["Devcontainer", "Codespaces", "GitHub", "Noir Environment", "Docker Image", "Development Environment", "Remote Coding", "GitHub Codespaces", "Noir Programming", "Nargo", "VSCode Extensions", "Noirup"] -sidebar_position: 1 ---- - -Adding a developer container configuration file to your Noir project is one of the easiest way to unlock coding in browser. - -## What's a devcontainer after all? - -A [Developer Container](https://containers.dev/) (devcontainer for short) is a Docker image that comes preloaded with tools, extensions, and other tools you need to quickly get started or continue a project, without having to install Nargo locally. Think of it as a development environment in a box. - -There are many advantages to this: - -- It's platform and architecture agnostic -- You don't need to have an IDE installed, or Nargo, or use a terminal at all -- It's safer for using on a public machine or public network - -One of the best ways of using devcontainers is... not using your machine at all, for maximum control, performance, and ease of use. -Enter Codespaces. - -## Codespaces - -If a devcontainer is just a Docker image, then what stops you from provisioning a `p3dn.24xlarge` AWS EC2 instance with 92 vCPUs and 768 GiB RAM and using it to prove your 10-gate SNARK proof? - -Nothing! Except perhaps the 30-40$ per hour it will cost you. - -The problem is that provisioning takes time, and I bet you don't want to see the AWS console every time you want to code something real quick. - -Fortunately, there's an easy and free way to get a decent remote machine ready and loaded in less than 2 minutes: Codespaces. [Codespaces is a Github feature](https://github.com/features/codespaces) that allows you to code in a remote machine by using devcontainers, and it's pretty cool: - -- You can start coding Noir in less than a minute -- It uses the resources of a remote machine, so you can code on your grandma's phone if needed be -- It makes it easy to share work with your frens -- It's fully reusable, you can stop and restart whenever you need to - -:::info - -Don't take out your wallet just yet. Free GitHub accounts get about [15-60 hours of coding](https://github.com/features/codespaces) for free per month, depending on the size of your provisioned machine. - -::: - -## Tell me it's _actually_ easy - -It is! - -Github comes with a default codespace and you can use it to code your own devcontainer. That's exactly what we will be doing in this guide. - - - -8 simple steps: - -#### 1. Create a new repository on GitHub. - -#### 2. Click "Start coding with Codespaces". This will use the default image. - -#### 3. Create a folder called `.devcontainer` in the root of your repository. - -#### 4. Create a Dockerfile in that folder, and paste the following code: - -```docker -FROM --platform=linux/amd64 node:lts-bookworm-slim -SHELL ["/bin/bash", "-c"] -RUN apt update && apt install -y curl bash git tar gzip libc++-dev -RUN curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash -ENV PATH="/root/.nargo/bin:$PATH" -RUN noirup -ENTRYPOINT ["nargo"] -``` -#### 5. Create a file called `devcontainer.json` in the same folder, and paste the following code: - -```json -{ - "name": "Noir on Codespaces", - "build": { - "context": ".", - "dockerfile": "Dockerfile" - }, - "customizations": { - "vscode": { - "extensions": ["noir-lang.vscode-noir"] - } - } -} -``` -#### 6. Commit and push your changes - -This will pull the new image and build it, so it could take a minute or so - -#### 8. Done! -Just wait for the build to finish, and there's your easy Noir environment. - - -Refer to [noir-starter](https://github.com/noir-lang/noir-starter/) as an example of how devcontainers can be used together with codespaces. - - - -## How do I use it? - -Using the codespace is obviously much easier than setting it up. -Just navigate to your repository and click "Code" -> "Open with Codespaces". It should take a few seconds to load, and you're ready to go. - -:::info - -If you really like the experience, you can add a badge to your readme, links to existing codespaces, and more. -Check out the [official docs](https://docs.github.com/en/codespaces/setting-up-your-project-for-codespaces/setting-up-your-repository/facilitating-quick-creation-and-resumption-of-codespaces) for more info. diff --git a/docs/versioned_docs/version-v0.37.0/index.mdx b/docs/versioned_docs/version-v0.37.0/index.mdx deleted file mode 100644 index 5c116a73b3f..00000000000 --- a/docs/versioned_docs/version-v0.37.0/index.mdx +++ /dev/null @@ -1,76 +0,0 @@ ---- -title: Noir Lang -hide_title: true -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] -sidebar_position: 0 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; -import ThemedImage from '@theme/ThemedImage'; -import useBaseUrl from '@docusaurus/useBaseUrl'; - - - -Noir is an open-source Domain-Specific Language for safe and seamless construction of privacy-preserving Zero-Knowledge programs, requiring no previous knowledge on the underlying mathematics or cryptography. - -ZK programs are programs that can generate short proofs of statements without revealing all inputs to the statements. You can read more about Zero-Knowledge Proofs [here](https://dev.to/spalladino/a-beginners-intro-to-coding-zero-knowledge-proofs-c56). - -## What's new about Noir? - -Noir works differently from most ZK languages by taking a two-pronged path. First, it compiles the program to an adaptable intermediate language known as ACIR. From there, depending on a given project's needs, ACIR can be further compiled into an arithmetic circuit for integration with the proving backend. - -:::info - -Noir is backend agnostic, which means it makes no assumptions on which proving backend powers the ZK proof. Being the language that powers [Aztec Contracts](https://docs.aztec.network/developers/contracts/main), it defaults to Aztec's Barretenberg proving backend. - -However, the ACIR output can be transformed to be compatible with other PLONK-based backends, or into a [rank-1 constraint system](https://www.rareskills.io/post/rank-1-constraint-system) suitable for backends such as Arkwork's Marlin. - -::: - -## Who is Noir for? - -Noir can be used both in complex cloud-based backends and in user's smartphones, requiring no knowledge on the underlying math or cryptography. From authorization systems that keep a password in the user's device, to complex on-chain verification of recursive proofs, Noir is designed to abstract away complexity without any significant overhead. Here are some examples of situations where Noir can be used: - - - - Noir Logo - - Aztec Contracts leverage Noir to allow for the storage and execution of private information. Writing an Aztec Contract is as easy as writing Noir, and Aztec developers can easily interact with the network storage and execution through the [Aztec.nr](https://docs.aztec.network/developers/contracts/main) library. - - - Soliditry Verifier Example - Noir can auto-generate Solidity verifier contracts that verify Noir proofs. This allows for non-interactive verification of proofs containing private information in an immutable system. This feature powers a multitude of use-case scenarios, from P2P chess tournaments, to [Aztec Layer-2 Blockchain](https://docs.aztec.network/) - - - Aztec Labs developed NoirJS, an easy interface to generate and verify Noir proofs in a Javascript environment. This allows for Noir to be used in webpages, mobile apps, games, and any other environment supporting JS execution in a standalone manner. - - - - -## Libraries - -Noir is meant to be easy to extend by simply importing Noir libraries just like in Rust. -The [awesome-noir repo](https://github.com/noir-lang/awesome-noir#libraries) is a collection of libraries developed by the Noir community. -Writing a new library is easy and makes code be composable and easy to reuse. See the section on [dependencies](noir/modules_packages_crates/dependencies.md) for more information. diff --git a/docs/versioned_docs/version-v0.37.0/migration_notes.md b/docs/versioned_docs/version-v0.37.0/migration_notes.md deleted file mode 100644 index 6bd740024e5..00000000000 --- a/docs/versioned_docs/version-v0.37.0/migration_notes.md +++ /dev/null @@ -1,105 +0,0 @@ ---- -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. - -### `backend encountered an error: libc++.so.1` - -Depending on your OS, you may encounter the following error when running `nargo prove` for the first time: - -```text -The backend encountered an error: "/home/codespace/.nargo/backends/acvm-backend-barretenberg/backend_binary: error while loading shared libraries: libc++.so.1: cannot open shared object file: No such file or directory\n" -``` - -Install the `libc++-dev` library with: - -```bash -sudo apt install libc++-dev -``` - -## ≥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](noir/concepts/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 your 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 file is running [this bash script](https://github.com/noir-lang/barretenberg-js-binary/blob/master/run-bb-js.sh), 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.37.0/noir/concepts/_category_.json b/docs/versioned_docs/version-v0.37.0/noir/concepts/_category_.json deleted file mode 100644 index 7da08f8a8c5..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/concepts/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "Concepts", - "position": 0, - "collapsible": true, - "collapsed": true -} \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.37.0/noir/concepts/assert.md b/docs/versioned_docs/version-v0.37.0/noir/concepts/assert.md deleted file mode 100644 index 2132de42072..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/concepts/assert.md +++ /dev/null @@ -1,78 +0,0 @@ ---- -title: Assert Function -description: - Learn about the `assert` and `static_assert` functions 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 or compile-time, respectively. -keywords: [Noir programming language, assert statement, predicate expression, comparison expression] -sidebar_position: 4 ---- - -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); -} -``` - -> 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`. - -You can optionally provide a message to be logged when the assertion fails: - -```rust -assert(x == y, "x and y are not equal"); -``` - -Aside string literals, the optional message can be a format string or any other type supported as input for Noir's [print](../standard_library/logging.md) functions. This feature lets you incorporate runtime variables into your failed assertion logs: - -```rust -assert(x == y, f"Expected x == y, but got {x} == {y}"); -``` - -Using a variable as an assertion message directly: - -```rust -struct myStruct { - myField: Field -} - -let s = myStruct { myField: y }; -assert(s.myField == x, s); -``` - -There is also a special `static_assert` function that behaves like `assert`, -but that runs at compile-time. - -```rust -fn main(xs: [Field; 3]) { - let x = 2 + 2; - let y = 4; - static_assert(x == y, "expected 2 + 2 to equal 4"); - - // This passes since the length of `xs` is known at compile-time - static_assert(xs.len() == 3, "expected the input to have 3 elements"); -} -``` - -This function fails when passed a dynamic (run-time) argument: - -```rust -fn main(x : Field, y : Field) { - // this fails because `x` is not known at compile-time - static_assert(x == 2, "expected x to be known at compile-time and equal to 2"); - - let mut example_slice = &[]; - if y == 4 { - example_slice = example_slice.push_back(0); - } - - // This fails because the length of `example_slice` is not known at - // compile-time - let error_message = "expected an empty slice, known at compile-time"; - static_assert(example_slice.len() == 0, error_message); -} -``` - diff --git a/docs/versioned_docs/version-v0.37.0/noir/concepts/comments.md b/docs/versioned_docs/version-v0.37.0/noir/concepts/comments.md deleted file mode 100644 index b51a85f5c94..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/concepts/comments.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -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] -sidebar_position: 10 ---- - -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.37.0/noir/concepts/comptime.md b/docs/versioned_docs/version-v0.37.0/noir/concepts/comptime.md deleted file mode 100644 index 2ceb030c7e1..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/concepts/comptime.md +++ /dev/null @@ -1,445 +0,0 @@ ---- -title: Compile-time Code & Metaprogramming -description: Learn how to use metaprogramming in Noir to create macros or derive your own traits -keywords: [Noir, comptime, compile-time, metaprogramming, macros, quote, unquote] -sidebar_position: 15 ---- - -## Overview - -Metaprogramming in Noir is comprised of three parts: -1. `comptime` code -2. Quoting and unquoting -3. The metaprogramming API in `std::meta` - -Each of these are explained in more detail in the next sections but the wide picture is that -`comptime` allows us to write code which runs at compile-time. In this `comptime` code we -can quote and unquote snippets of the program, manipulate them, and insert them in other -parts of the program. Comptime functions which do this are said to be macros. Additionally, -there's a compile-time API of built-in types and functions provided by the compiler which allows -for greater analysis and modification of programs. - ---- - -## Comptime - -`comptime` is a new keyword in Noir which marks an item as executing or existing at compile-time. It can be used in several ways: - -- `comptime fn` to define functions which execute exclusively during compile-time. -- `comptime global` to define a global variable which is evaluated at compile-time. - - Unlike runtime globals, `comptime global`s can be mutable. -- `comptime { ... }` to execute a block of statements during compile-time. -- `comptime let` to define a variable whose value is evaluated at compile-time. -- `comptime for` to run a for loop at compile-time. Syntax sugar for `comptime { for .. }`. - -### Scoping - -Note that while in a `comptime` context, any runtime variables _local to the current function_ are never visible. - -### Evaluating - -Evaluation rules of `comptime` follows the normal unconstrained evaluation rules for other Noir code. There are a few things to note though: - -- Certain built-in functions may not be available, although more may be added over time. -- Evaluation order of global items is currently unspecified. For example, given the following two functions we can't guarantee -which `println` will execute first. The ordering of the two printouts will be arbitrary, but should be stable across multiple compilations with the same `nargo` version as long as the program is also unchanged. - -```rust -fn one() { - comptime { println("one"); } -} - -fn two() { - comptime { println("two"); } -} -``` - -- Since evaluation order is unspecified, care should be taken when using mutable globals so that they do not rely on a particular ordering. -For example, using globals to generate unique ids should be fine but relying on certain ids always being produced (especially after edits to the program) should be avoided. -- Although most ordering of globals is unspecified, two are: - - Dependencies of a crate will always be evaluated before the dependent crate. - - Any annotations on a function will be run before the function itself is resolved. This is to allow the annotation to modify the function if necessary. Note that if the - function itself was called at compile-time previously, it will already be resolved and cannot be modified. To prevent accidentally calling functions you wish to modify - at compile-time, it may be helpful to sort your `comptime` annotation functions into a different crate along with any dependencies they require. - -### Lowering - -When a `comptime` value is used in runtime code it must be lowered into a runtime value. This means replacing the expression with the literal that it evaluated to. For example, the code: - -```rust -struct Foo { array: [Field; 2], len: u32 } - -fn main() { - println(comptime { - let mut foo = std::mem::zeroed::(); - foo.array[0] = 4; - foo.len = 1; - foo - }); -} -``` - -will be converted to the following after `comptime` expressions are evaluated: - -```rust -struct Foo { array: [Field; 2], len: u32 } - -fn main() { - println(Foo { array: [4, 0], len: 1 }); -} -``` - -Not all types of values can be lowered. For example, `Type`s and `TypeDefinition`s (among other types) cannot be lowered at all. - -```rust -fn main() { - // There's nothing we could inline here to create a Type value at runtime - // let _ = get_type!(); -} - -comptime fn get_type() -> Type { ... } -``` - ---- - -## (Quasi) Quote - -Macros in Noir are `comptime` functions which return code as a value which is inserted into the call site when it is lowered there. -A code value in this case is of type `Quoted` and can be created by a `quote { ... }` expression. -More specifically, the code value `quote` creates is a token stream - a representation of source code as a series of words, numbers, string literals, or operators. -For example, the expression `quote { Hi "there reader"! }` would quote three tokens: the word "hi", the string "there reader", and an exclamation mark. -You'll note that snippets that would otherwise be invalid syntax can still be quoted. - -When a `Quoted` value is used in runtime code, it is lowered into a `quote { ... }` expression. Since this expression is only valid -in compile-time code however, we'd get an error if we tried this. Instead, we can use macro insertion to insert each token into the -program at that point, and parse it as an expression. To do this, we have to add a `!` after the function name returning the `Quoted` value. -If the value was created locally and there is no function returning it, `std::meta::unquote!(_)` can be used instead. -Calling such a function at compile-time without `!` will just return the `Quoted` value to be further manipulated. For example: - -```rust title="quote-example" showLineNumbers -comptime fn quote_one() -> Quoted { - quote { 1 } - } - - #[test] - fn returning_versus_macro_insertion() { - comptime { - // let _a: Quoted = quote { 1 }; - let _a: Quoted = quote_one(); - - // let _b: Field = 1; - let _b: Field = quote_one!(); - - // Since integers default to fields, if we - // want a different type we have to explicitly cast - // let _c: i32 = 1 as i32; - let _c: i32 = quote_one!() as i32; - } - } -``` -> Source code: noir_stdlib/src/meta/mod.nr#L120-L140 - - -For those familiar with quoting from other languages (primarily lisps), Noir's `quote` is actually a _quasiquote_. -This means we can escape the quoting by using the unquote operator to splice values in the middle of quoted code. - -## Unquote - -The unquote operator `$` is usable within a `quote` expression. -It takes a variable as an argument, evaluates the variable, and splices the resulting value into the quoted token stream at that point. For example, - -```rust -comptime { - let x = 1 + 2; - let y = quote { $x + 4 }; -} -``` - -The value of `y` above will be the token stream containing `3`, `+`, and `4`. We can also use this to combine `Quoted` values into larger token streams: - -```rust -comptime { - let x = quote { 1 + 2 }; - let y = quote { $x + 4 }; -} -``` - -The value of `y` above is now the token stream containing five tokens: `1 + 2 + 4`. - -Note that to unquote something, a variable name _must_ follow the `$` operator in a token stream. -If it is an expression (even a parenthesized one), it will do nothing. Most likely a parse error will be given when the macro is later unquoted. - -Unquoting can also be avoided by escaping the `$` with a backslash: - -``` -comptime { - let x = quote { 1 + 2 }; - - // y contains the four tokens: `$x + 4` - let y = quote { \$x + 4 }; -} -``` - ---- - -## Annotations - -Annotations provide a way to run a `comptime` function on an item in the program. -When you use an annotation, the function with the same name will be called with that item as an argument: - -```rust -#[my_struct_annotation] -struct Foo {} - -comptime fn my_struct_annotation(s: StructDefinition) { - println("Called my_struct_annotation!"); -} - -#[my_function_annotation] -fn foo() {} - -comptime fn my_function_annotation(f: FunctionDefinition) { - println("Called my_function_annotation!"); -} -``` - -Anything returned from one of these functions will be inserted at top-level along with the original item. -Note that expressions are not valid at top-level so you'll get an error trying to return `3` or similar just as if you tried to write a program containing `3; struct Foo {}`. -You can insert other top-level items such as trait impls, structs, or functions this way though. -For example, this is the mechanism used to insert additional trait implementations into the program when deriving a trait impl from a struct: - -```rust title="derive-field-count-example" showLineNumbers -trait FieldCount { - fn field_count() -> u32; - } - - #[derive_field_count] - struct Bar { - x: Field, - y: [Field; 2], - } - - comptime fn derive_field_count(s: StructDefinition) -> Quoted { - let typ = s.as_type(); - let field_count = s.fields().len(); - quote { - impl FieldCount for $typ { - fn field_count() -> u32 { - $field_count - } - } - } - } -``` -> Source code: noir_stdlib/src/meta/mod.nr#L142-L164 - - -### Calling annotations with additional arguments - -Arguments may optionally be given to annotations. -When this is done, these additional arguments are passed to the annotation function after the item argument. - -```rust title="annotation-arguments-example" showLineNumbers -#[assert_field_is_type(quote { i32 }.as_type())] - struct MyStruct { - my_field: i32, - } - - comptime fn assert_field_is_type(s: StructDefinition, typ: Type) { - // Assert the first field in `s` has type `typ` - let fields = s.fields(); - assert_eq(fields[0].1, typ); - } -``` -> Source code: noir_stdlib/src/meta/mod.nr#L166-L177 - - -We can also take any number of arguments by adding the `varargs` annotation: - -```rust title="annotation-varargs-example" showLineNumbers -#[assert_three_args(1, 2, 3)] - struct MyOtherStruct { - my_other_field: u32, - } - - #[varargs] - comptime fn assert_three_args(_s: StructDefinition, args: [Field]) { - assert_eq(args.len(), 3); - } -``` -> Source code: noir_stdlib/src/meta/mod.nr#L179-L189 - - ---- - -## Comptime API - -Although `comptime`, `quote`, and unquoting provide a flexible base for writing macros, -Noir's true metaprogramming ability comes from being able to interact with the compiler through a compile-time API. -This API can be accessed through built-in functions in `std::meta` as well as on methods of several `comptime` types. - -The following is an incomplete list of some `comptime` types along with some useful methods on them. You can see more in the standard library [Metaprogramming section](../standard_library/meta). - -- `Quoted`: A token stream -- `Type`: The type of a Noir type - - `fn implements(self, constraint: TraitConstraint) -> bool` - - Returns true if `self` implements the given trait constraint -- `Expr`: A syntactically valid expression. Can be used to recur on a program's parse tree to inspect how it is structured. - - Methods: - - `fn as_function_call(self) -> Option<(Expr, [Expr])>` - - If this is a function call expression, return `(function, arguments)` - - `fn as_block(self) -> Option<[Expr]>` - - If this is a block, return each statement in the block -- `FunctionDefinition`: A function definition - - Methods: - - `fn parameters(self) -> [(Quoted, Type)]` - - Returns a slice of `(name, type)` pairs for each parameter -- `StructDefinition`: A struct definition - - Methods: - - `fn as_type(self) -> Type` - - Returns this `StructDefinition` as a `Type`. Any generics are kept as-is - - `fn generics(self) -> [Quoted]` - - Return the name of each generic on this struct - - `fn fields(self) -> [(Quoted, Type)]` - - Return the name and type of each field -- `TraitConstraint`: A trait constraint such as `From` -- `TypedExpr`: A type-checked expression. -- `UnresolvedType`: A syntactic notation that refers to a Noir type that hasn't been resolved yet - -There are many more functions available by exploring the `std::meta` module and its submodules. -Using these methods is the key to writing powerful metaprogramming libraries. - -### `#[use_callers_scope]` - -Since certain functions such as `Quoted::as_type`, `Expression::as_type`, or `Quoted::as_trait_constraint` will attempt -to resolve their contents in a particular scope - it can be useful to change the scope they resolve in. By default -these functions will resolve in the current function's scope which is usually the attribute function they are called in. -If you're working on a library however, this may be a completely different module or crate to the item you're trying to -use the attribute on. If you want to be able to use `Quoted::as_type` to refer to types local to the caller's scope for -example, you can annotate your attribute function with `#[use_callers_scope]`. This will ensure your attribute, and any -closures it uses, can refer to anything in the caller's scope. `#[use_callers_scope]` also works recursively. So if both -your attribute function and a helper function it calls use it, then they can both refer to the same original caller. - ---- - -## Example: Derive - -Using all of the above, we can write a `derive` macro that behaves similarly to Rust's but is not built into the language. -From the user's perspective it will look like this: - -```rust -// Example usage -#[derive(Default, Eq, Ord)] -struct MyStruct { my_field: u32 } -``` - -To implement `derive` we'll have to create a `comptime` function that accepts -a variable amount of traits. - -```rust title="derive_example" showLineNumbers -// These are needed for the unconstrained hashmap we're using to store derive functions -use crate::collections::umap::UHashMap; -use crate::hash::BuildHasherDefault; -use crate::hash::poseidon2::Poseidon2Hasher; - -// A derive function is one that given a struct definition can -// create us a quoted trait impl from it. -pub type DeriveFunction = fn(StructDefinition) -> Quoted; - -// We'll keep a global HANDLERS map to keep track of the derive handler for each trait -comptime mut global HANDLERS: UHashMap> = - UHashMap::default(); - -// Given a struct and a slice of traits to derive, create trait impls for each. -// This function is as simple as iterating over the slice, checking if we have a trait -// handler registered for the given trait, calling it, and appending the result. -#[varargs] -pub comptime fn derive(s: StructDefinition, traits: [TraitDefinition]) -> Quoted { - let mut result = quote {}; - - for trait_to_derive in traits { - let handler = unsafe { HANDLERS.get(trait_to_derive) }; - assert(handler.is_some(), f"No derive function registered for `{trait_to_derive}`"); - - let trait_impl = handler.unwrap()(s); - result = quote { $result $trait_impl }; - } - - result -} -``` -> Source code: noir_stdlib/src/meta/mod.nr#L31-L64 - - -Registering a derive function could be done as follows: - -```rust title="derive_via" showLineNumbers -// To register a handler for a trait, just add it to our handlers map -pub comptime fn derive_via(t: TraitDefinition, f: DeriveFunction) { - HANDLERS.insert(t, f); -} -``` -> Source code: noir_stdlib/src/meta/mod.nr#L66-L73 - - -```rust title="big-derive-usage-example" showLineNumbers -// Finally, to register a handler we call the above function as an annotation - // with our handler function. - #[derive_via(derive_do_nothing)] - trait DoNothing { - fn do_nothing(self); - } - - comptime fn derive_do_nothing(s: StructDefinition) -> Quoted { - // This is simplified since we don't handle generics or where clauses! - // In a real example we'd likely also need to introduce each of - // `s.generics()` as well as a trait constraint for each generic - // to ensure they also implement the trait. - let typ = s.as_type(); - quote { - impl DoNothing for $typ { - fn do_nothing(self) { - // Traits can't tell us what to do - println("something"); - } - } - } - } - - // Since `DoNothing` is a simple trait which: - // 1. Only has one method - // 2. Does not have any generics on the trait itself - // We can use `std::meta::make_trait_impl` to help us out. - // This helper function will generate our impl for us along with any - // necessary where clauses and still provides a flexible interface - // for us to work on each field on the struct. - comptime fn derive_do_nothing_alt(s: StructDefinition) -> Quoted { - let trait_name = quote { DoNothing }; - let method_signature = quote { fn do_nothing(self) }; - - // Call `do_nothing` recursively on each field in the struct - let for_each_field = |field_name| quote { self.$field_name.do_nothing(); }; - - // Some traits like Eq want to join each field expression with something like `&`. - // We don't need that here - let join_fields_with = quote {}; - - // The body function is a spot to insert any extra setup/teardown needed. - // We'll insert our println here. Since we recur on each field, we should see - // one println for the struct itself, followed by a println for every field (recursively). - let body = |body| quote { - println("something"); - $body - }; - crate::meta::make_trait_impl( - s, - trait_name, - method_signature, - for_each_field, - join_fields_with, - body, - ) - } -``` -> Source code: noir_stdlib/src/meta/mod.nr#L191-L249 - diff --git a/docs/versioned_docs/version-v0.37.0/noir/concepts/control_flow.md b/docs/versioned_docs/version-v0.37.0/noir/concepts/control_flow.md deleted file mode 100644 index b365bb22728..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/concepts/control_flow.md +++ /dev/null @@ -1,79 +0,0 @@ ---- -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] -sidebar_position: 2 ---- - -## 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); -``` - -## 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 -} -``` - -Alternatively, `start..=end` can be used for a range that is inclusive on both ends. - -The index for loops is of type `u64`. - -### Break and Continue - -In unconstrained code, `break` and `continue` are also allowed in `for` loops. These are only allowed -in unconstrained code since normal constrained code requires that Noir knows exactly how many iterations -a loop may have. `break` and `continue` can be used like so: - -```rust -for i in 0 .. 10 { - println("Iteration start") - - if i == 2 { - continue; - } - - if i == 5 { - break; - } - - println(i); -} -println("Loop end") -``` - -When used, `break` will end the current loop early and jump to the statement after the for loop. In the example -above, the `break` will stop the loop and jump to the `println("Loop end")`. - -`continue` will stop the current iteration of the loop, and jump to the start of the next iteration. In the example -above, `continue` will jump to `println("Iteration start")` when used. Note that the loop continues as normal after this. -The iteration variable `i` is still increased by one as normal when `continue` is used. - -`break` and `continue` cannot currently be used to jump out of more than a single loop at a time. diff --git a/docs/versioned_docs/version-v0.37.0/noir/concepts/data_bus.mdx b/docs/versioned_docs/version-v0.37.0/noir/concepts/data_bus.mdx deleted file mode 100644 index e55e58622ce..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/concepts/data_bus.mdx +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: Data Bus -sidebar_position: 13 ---- -import Experimental from '@site/src/components/Notes/_experimental.mdx'; - - - -The data bus is an optimization that the backend can use to make recursion more efficient. -In order to use it, you must define some inputs of the program entry points (usually the `main()` -function) with the `call_data` modifier, and the return values with the `return_data` modifier. -These modifiers are incompatible with `pub` and `mut` modifiers. - -## Example - -```rust -fn main(mut x: u32, y: call_data u32, z: call_data [u32;4] ) -> return_data u32 { - let a = z[x]; - a+y -} -``` - -As a result, both call_data and return_data will be treated as private inputs and encapsulated into a read-only array each, for the backend to process. diff --git a/docs/versioned_docs/version-v0.37.0/noir/concepts/data_types/_category_.json b/docs/versioned_docs/version-v0.37.0/noir/concepts/data_types/_category_.json deleted file mode 100644 index 5d694210bbf..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/concepts/data_types/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "position": 0, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.37.0/noir/concepts/data_types/arrays.md b/docs/versioned_docs/version-v0.37.0/noir/concepts/data_types/arrays.md deleted file mode 100644 index 289145a8c4d..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/concepts/data_types/arrays.md +++ /dev/null @@ -1,276 +0,0 @@ ---- -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, - ] -sidebar_position: 4 ---- - -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.mdx), 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]; -``` - -However, multidimensional slices are not supported. For example, the following code will error at compile time: - -```rust -let slice : [[Field]] = &[]; -``` - -## 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. -Each of these functions are located within the generic impl `impl [T; N] {`. -So anywhere `self` appears, it refers to the variable `self: [T; N]`. - -### len - -Returns the length of an array - -```rust -fn len(self) -> 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(self) -> [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. The ordering function must return true if the first argument should be sorted to be before the second argument or is equal to the second argument. - -Using this method with an operator like `<` that does not return `true` for equal values will result in an assertion failure for arrays with equal elements. - -```rust -fn sort_via(self, 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(self, 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(self, 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 the starting element. - -Requires `self` to be non-empty. - -```rust -fn reduce(self, 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(self, 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(self, 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); -} -``` - -### as_str_unchecked - -Converts a byte array of type `[u8; N]` to a string. Note that this performs no UTF-8 validation - -the given array is interpreted as-is as a string. - -```rust -impl [u8; N] { - pub fn as_str_unchecked(self) -> str -} -``` - -example: - -```rust -fn main() { - let hi = [104, 105].as_str_unchecked(); - assert_eq(hi, "hi"); -} -``` diff --git a/docs/versioned_docs/version-v0.37.0/noir/concepts/data_types/booleans.md b/docs/versioned_docs/version-v0.37.0/noir/concepts/data_types/booleans.md deleted file mode 100644 index 2507af710e7..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/concepts/data_types/booleans.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -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, - ] -sidebar_position: 2 ---- - - -The `bool` type in Noir has two possible values: `true` and `false`: - -```rust -fn main() { - let t = true; - let f: bool = false; -} -``` - -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.md) and -[Assert Function](../assert.md) sections. diff --git a/docs/versioned_docs/version-v0.37.0/noir/concepts/data_types/fields.md b/docs/versioned_docs/version-v0.37.0/noir/concepts/data_types/fields.md deleted file mode 100644 index b9b56f7ecc3..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/concepts/data_types/fields.md +++ /dev/null @@ -1,246 +0,0 @@ ---- -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, - ] -sidebar_position: 0 ---- - -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 title="to_le_bits" showLineNumbers -pub fn to_le_bits(self: Self) -> [u1; N] {} -``` -> Source code: noir_stdlib/src/field/mod.nr#L32-L34 - - -example: - -```rust title="to_le_bits_example" showLineNumbers -fn test_to_le_bits() { - let field = 2; - let bits: [u1; 8] = field.to_le_bits(); - assert_eq(bits, [0, 1, 0, 0, 0, 0, 0, 0]); - } -``` -> Source code: noir_stdlib/src/field/mod.nr#L276-L282 - - - -### to_be_bits - -Transforms the field into an array of bits, Big Endian. - -```rust title="to_be_bits" showLineNumbers -pub fn to_be_bits(self: Self) -> [u1; N] {} -``` -> Source code: noir_stdlib/src/field/mod.nr#L48-L50 - - -example: - -```rust title="to_be_bits_example" showLineNumbers -fn test_to_be_bits() { - let field = 2; - let bits: [u1; 8] = field.to_be_bits(); - assert_eq(bits, [0, 0, 0, 0, 0, 0, 1, 0]); - } -``` -> Source code: noir_stdlib/src/field/mod.nr#L267-L273 - - - -### to_le_bytes - -Transforms into an array of bytes, Little Endian - -```rust title="to_le_bytes" showLineNumbers -pub fn to_le_bytes(self: Self) -> [u8; N] { -``` -> Source code: noir_stdlib/src/field/mod.nr#L61-L63 - - -example: - -```rust title="to_le_bytes_example" showLineNumbers -fn test_to_le_bytes() { - let field = 2; - let bytes: [u8; 8] = field.to_le_bytes(); - assert_eq(bytes, [2, 0, 0, 0, 0, 0, 0, 0]); - assert_eq(Field::from_le_bytes::<8>(bytes), field); - } -``` -> Source code: noir_stdlib/src/field/mod.nr#L295-L302 - - -### to_be_bytes - -Transforms into an array of bytes, Big Endian - -```rust title="to_be_bytes" showLineNumbers -pub fn to_be_bytes(self: Self) -> [u8; N] { -``` -> Source code: noir_stdlib/src/field/mod.nr#L94-L96 - - -example: - -```rust title="to_be_bytes_example" showLineNumbers -fn test_to_be_bytes() { - let field = 2; - let bytes: [u8; 8] = field.to_be_bytes(); - assert_eq(bytes, [0, 0, 0, 0, 0, 0, 0, 2]); - assert_eq(Field::from_be_bytes::<8>(bytes), field); - } -``` -> Source code: noir_stdlib/src/field/mod.nr#L285-L292 - - - -### to_le_radix - -Decomposes into an array over the specified base, Little Endian - -```rust title="to_le_radix" showLineNumbers -pub fn to_le_radix(self: Self, radix: u32) -> [u8; N] { - // Brillig does not need an immediate radix - if !crate::runtime::is_unconstrained() { - crate::assert_constant(radix); - } - self.__to_le_radix(radix) - } -``` -> Source code: noir_stdlib/src/field/mod.nr#L118-L126 - - - -example: - -```rust title="to_le_radix_example" showLineNumbers -fn test_to_le_radix() { - let field = 2; - let bytes: [u8; 8] = field.to_le_radix(256); - assert_eq(bytes, [2, 0, 0, 0, 0, 0, 0, 0]); - assert_eq(Field::from_le_bytes::<8>(bytes), field); - } -``` -> Source code: noir_stdlib/src/field/mod.nr#L315-L322 - - - -### to_be_radix - -Decomposes into an array over the specified base, Big Endian - -```rust title="to_be_radix" showLineNumbers -pub fn to_be_radix(self: Self, radix: u32) -> [u8; N] { - // Brillig does not need an immediate radix - if !crate::runtime::is_unconstrained() { - crate::assert_constant(radix); - } - self.__to_be_radix(radix) - } -``` -> Source code: noir_stdlib/src/field/mod.nr#L128-L136 - - -example: - -```rust title="to_be_radix_example" showLineNumbers -fn test_to_be_radix() { - let field = 2; - let bytes: [u8; 8] = field.to_be_radix(256); - assert_eq(bytes, [0, 0, 0, 0, 0, 0, 0, 2]); - assert_eq(Field::from_be_bytes::<8>(bytes), field); - } -``` -> Source code: noir_stdlib/src/field/mod.nr#L305-L312 - - - -### 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); -} -``` - -### assert_max_bit_size - -Adds a constraint to specify that the field can be represented with `bit_size` number of bits - -```rust title="assert_max_bit_size" showLineNumbers -pub fn assert_max_bit_size(self) { -``` -> Source code: noir_stdlib/src/field/mod.nr#L10-L12 - - -example: - -```rust -fn main() { - let field = 2 - field.assert_max_bit_size(32); -} -``` - -### 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 -``` - - -### lt - -Returns true if the field is less than the other field - -```rust -pub fn lt(self, another: Field) -> bool -``` diff --git a/docs/versioned_docs/version-v0.37.0/noir/concepts/data_types/function_types.md b/docs/versioned_docs/version-v0.37.0/noir/concepts/data_types/function_types.md deleted file mode 100644 index f6121af17e2..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/concepts/data_types/function_types.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: Function types -sidebar_position: 10 ---- - -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](../lambdas.md) for more details. diff --git a/docs/versioned_docs/version-v0.37.0/noir/concepts/data_types/index.md b/docs/versioned_docs/version-v0.37.0/noir/concepts/data_types/index.md deleted file mode 100644 index 0f2db2b2d75..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/concepts/data_types/index.md +++ /dev/null @@ -1,126 +0,0 @@ ---- -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](../generics.md): - -```rust -type Id = Size; - -fn main() { - let id: Id = 1; - let zero: u32 = 0; - assert(zero + 1 == id); -} -``` - -Type aliases can even refer to other aliases. An error will be issued if they form a cycle: - -```rust -// Ok! -type A = B; -type B = Field; - -type Bad1 = Bad2; - -// error: Dependency cycle found -type Bad2 = Bad1; -// ^^^^^^^^^^^ 'Bad2' recursively depends on itself: Bad2 -> Bad1 -> Bad2 -``` - -By default, like functions, type aliases are private to the module they exist in. You can use `pub` -to make the type alias public or `pub(crate)` to make it public to just its crate: - -```rust -// This type alias is now public -pub type Id = u8; -``` - -## Wildcard Type -Noir can usually infer the type of the variable from the context, so specifying the type of a variable is only required when it cannot be inferred. However, specifying a complex type can be tedious, especially when it has multiple generic arguments. Often some of the generic types can be inferred from the context, and Noir only needs a hint to properly infer the other types. We can partially specify a variable's type by using `_` as a marker, indicating where we still want the compiler to infer the type. - -```rust -let a: [_; 4] = foo(b); -``` - - -### BigInt - -You can achieve BigInt functionality using the [Noir BigInt](https://github.com/shuklaayush/noir-bigint) library. diff --git a/docs/versioned_docs/version-v0.37.0/noir/concepts/data_types/integers.md b/docs/versioned_docs/version-v0.37.0/noir/concepts/data_types/integers.md deleted file mode 100644 index a1d59bf3166..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/concepts/data_types/integers.md +++ /dev/null @@ -1,156 +0,0 @@ ---- -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] -sidebar_position: 1 ---- - -An integer type is a range constrained field type. -The Noir frontend supports both unsigned and signed integer types. -The allowed sizes are 1, 8, 16, 32 and 64 bits. - -:::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\\$). - -## 128 bits Unsigned Integers - -The built-in structure `U128` allows you to use 128-bit unsigned integers almost like a native integer type. However, there are some differences to keep in mind: -- You cannot cast between a native integer and `U128` -- There is a higher performance cost when using `U128`, compared to a native type. - -Conversion between unsigned integer types and U128 are done through the use of `from_integer` and `to_integer` functions. `from_integer` also accepts the `Field` type as input. - -```rust -fn main() { - let x = U128::from_integer(23); - let y = U128::from_hex("0x7"); - let z = x + y; - assert(z.to_integer() == 30); -} -``` - -`U128` is implemented with two 64 bits limbs, representing the low and high bits, which explains the performance cost. You should expect `U128` to be twice more costly for addition and four times more costly for multiplication. -You can construct a U128 from its limbs: -```rust -fn main(x: u64, y: u64) { - let x = U128::from_u64s_be(x,y); - assert(z.hi == x as Field); - assert(z.lo == y as Field); -} -``` - -Note that the limbs are stored as Field elements in order to avoid unnecessary conversions. -Apart from this, most operations will work as usual: - -```rust -fn main(x: U128, y: U128) { - // multiplication - let c = x * y; - // addition and subtraction - let c = c - x + y; - // division - let c = x / y; - // bit operation; - let c = x & y | y; - // bit shift - let c = x << y; - // comparisons; - let c = x < y; - let c = x == y; -} -``` - -## 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 execute -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 - -fn main(x: u8, y: u8) -> pub u8 { - std::wrapping_add(x, y) -} -``` diff --git a/docs/versioned_docs/version-v0.37.0/noir/concepts/data_types/references.md b/docs/versioned_docs/version-v0.37.0/noir/concepts/data_types/references.md deleted file mode 100644 index a5293d11cfb..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/concepts/data_types/references.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: References -sidebar_position: 9 ---- - -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.37.0/noir/concepts/data_types/slices.mdx b/docs/versioned_docs/version-v0.37.0/noir/concepts/data_types/slices.mdx deleted file mode 100644 index cfee564a302..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/concepts/data_types/slices.mdx +++ /dev/null @@ -1,358 +0,0 @@ ---- -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] -sidebar_position: 5 ---- - -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 -fn main() -> pub u32 { - let mut slice: [Field] = &[0; 2]; - - let mut new_slice = slice.push_back(6); - new_slice.len() -} -``` - -To write a slice literal, use a preceding ampersand as in: `&[0; 2]` or -`&[1, 2, 3]`. - -It is important to note that slices are not references to arrays. In Noir, -`&[..]` is more similar to an immutable, growable vector. - -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); -``` - -### len - -Returns the length of a slice - -```rust -fn len(self) -> Field -``` - -Example: - -```rust -fn main() { - let slice = &[42, 42]; - assert(slice.len() == 2); -} -``` - -### as_array - -Converts this slice into an array. - -Make sure to specify the size of the resulting array. -Panics if the resulting array length is different than the slice's length. - -```rust -fn as_array(self) -> [T; N] -``` - -Example: - -```rust -fn main() { - let slice = &[5, 6]; - - // Always specify the length of the resulting array! - let array: [Field; 2] = slice.as_array(); - - assert(array[0] == slice[0]); - assert(array[1] == slice[1]); -} -``` - -### map - -Applies a function to each element of the slice, returning a new slice containing the mapped elements. - -```rust -fn map(self, f: fn[Env](T) -> U) -> [U] -``` - -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 slice, returning the final accumulated value. The first -parameter is the initial value. - -```rust -fn fold(self, mut accumulator: U, f: fn[Env](U, T) -> U) -> U -``` - -This is a left fold, so the given function will be applied to the accumulator and first element of -the slice, 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 slice = &[2, 2, 2, 2, 2]; - let folded = slice.fold(0, |a, b| a + b); - assert(folded == 10); -} - -``` - -### reduce - -Same as fold, but uses the first element as the starting element. - -```rust -fn reduce(self, f: fn[Env](T, T) -> T) -> T -``` - -example: - -```rust -fn main() { - let slice = &[2, 2, 2, 2, 2]; - let reduced = slice.reduce(|a, b| a + b); - assert(reduced == 10); -} -``` - -### filter - -Returns a new slice containing only elements for which the given predicate returns true. - -```rust -fn filter(self, f: fn[Env](T) -> bool) -> Self -``` - -example: - -```rust -fn main() { - let slice = &[1, 2, 3, 4, 5]; - let odds = slice.filter(|x| x % 2 == 1); - assert_eq(odds, &[1, 3, 5]); -} -``` - -### join - -Flatten each element in the slice into one value, separated by `separator`. - -Note that although slices implement `Append`, `join` cannot be used on slice -elements since nested slices are prohibited. - -```rust -fn join(self, separator: T) -> T where T: Append -``` - -example: - -```rust -struct Accumulator { - total: Field, -} - -// "Append" two accumulators by adding them -impl Append for Accumulator { - fn empty() -> Self { - Self { total: 0 } - } - - fn append(self, other: Self) -> Self { - Self { total: self.total + other.total } - } -} - -fn main() { - let slice = &[1, 2, 3, 4, 5].map(|total| Accumulator { total }); - - let result = slice.join(Accumulator::empty()); - assert_eq(result, Accumulator { total: 15 }); - - // We can use a non-empty separator to insert additional elements to sum: - let separator = Accumulator { total: 10 }; - let result = slice.join(separator); - assert_eq(result, Accumulator { total: 55 }); -} -``` - -### all - -Returns true if all the elements satisfy the given predicate - -```rust -fn all(self, predicate: fn[Env](T) -> bool) -> bool -``` - -example: - -```rust -fn main() { - let slice = &[2, 2, 2, 2, 2]; - let all = slice.all(|a| a == 2); - assert(all); -} -``` - -### any - -Returns true if any of the elements satisfy the given predicate - -```rust -fn any(self, predicate: fn[Env](T) -> bool) -> bool -``` - -example: - -```rust -fn main() { - let slice = &[2, 2, 2, 2, 5]; - let any = slice.any(|a| a == 5); - assert(any); -} - -``` diff --git a/docs/versioned_docs/version-v0.37.0/noir/concepts/data_types/strings.md b/docs/versioned_docs/version-v0.37.0/noir/concepts/data_types/strings.md deleted file mode 100644 index 1fdee42425e..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/concepts/data_types/strings.md +++ /dev/null @@ -1,79 +0,0 @@ ---- -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, - ] -sidebar_position: 3 ---- - - -The string type is a fixed length value defined with `str`. - -You can use strings in `assert()` functions or print them with -`println()`. See more about [Logging](../../standard_library/logging.md). - -```rust - -fn main(message : pub str<11>, hex_as_string : str<4>) { - 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" -``` - -## 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/versioned_docs/version-v0.37.0/noir/concepts/data_types/structs.md b/docs/versioned_docs/version-v0.37.0/noir/concepts/data_types/structs.md deleted file mode 100644 index 29951ae843a..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/concepts/data_types/structs.md +++ /dev/null @@ -1,96 +0,0 @@ ---- -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, - ] -sidebar_position: 8 ---- - -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. - -### Visibility - -By default, like functions, structs are private to the module they exist in. You can use `pub` -to make the struct public or `pub(crate)` to make it public to just its crate: - -```rust -// This struct is now public -pub struct Animal { - hands: Field, - legs: Field, - eyes: u8, -} -``` - -The same applies to struct fields: by default they are private to the module they exist in, -but they can be made `pub` or `pub(crate)`: - -```rust -// This struct is now public -pub struct Animal { - hands: Field, // private to its module - pub(crate) legs: Field, // accessible from the entire crate - pub eyes: u8, // accessible from anywhere -} -``` \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.37.0/noir/concepts/data_types/tuples.md b/docs/versioned_docs/version-v0.37.0/noir/concepts/data_types/tuples.md deleted file mode 100644 index 2ec5c9c4113..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/concepts/data_types/tuples.md +++ /dev/null @@ -1,48 +0,0 @@ ---- -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, - ] -sidebar_position: 7 ---- - -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.37.0/noir/concepts/functions.md b/docs/versioned_docs/version-v0.37.0/noir/concepts/functions.md deleted file mode 100644 index f656cdfd97a..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/concepts/functions.md +++ /dev/null @@ -1,226 +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] -sidebar_position: 1 ---- - -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](../../tooling/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](./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](./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](./unconstrained.md) and [NoirJS](../../reference/NoirJS/noir_js/index.md) for more details. -- **test**: mark the function as unit tests. See [Tests](../../tooling/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.37.0/noir/concepts/generics.md b/docs/versioned_docs/version-v0.37.0/noir/concepts/generics.md deleted file mode 100644 index c180a0ce7e6..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/concepts/generics.md +++ /dev/null @@ -1,251 +0,0 @@ ---- -title: Generics -description: Learn how to use Generics in Noir -keywords: [Noir, Rust, generics, functions, structs] -sidebar_position: 7 ---- - -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 -} -``` - -## Numeric Generics - -If we want to be generic over array lengths (which are type-level integers), we can use numeric -generics. Using these looks similar to using regular generics, but introducing them into scope -requires declaring them with `let MyGenericName: IntegerType`. This can be done anywhere a normal -generic is declared. Instead of types, these generics resolve to integers at compile-time. -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 - } -} -``` - -## 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 print(self) { - for _i in 0 .. self.count { - println(self.value); - } - } -} - -fn main() { - let repeated = RepeatedValue { value: "Hello!", count: 2 }; - repeated.print(); -} -``` - -The `print` function will print `Hello!` an arbitrary number of times, twice in this case. - -## Calling functions on generic parameters - -Since a generic type `T` can represent any type, how can we call functions on the underlying type? -In other words, how can we go from "any type `T`" to "any type `T` that has certain methods available?" - -This is what [traits](../concepts/traits.md) are for in Noir. Here's an example of a function generic over -any type `T` that implements the `Eq` trait for equality: - -```rust -fn first_element_is_equal(array1: [T; N], array2: [T; N]) -> bool - where T: Eq -{ - if (array1.len() == 0) | (array2.len() == 0) { - true - } else { - array1[0] == array2[0] - } -} - -fn main() { - assert(first_element_is_equal([1, 2, 3], [1, 5, 6])); - - // We can use first_element_is_equal for arrays of any type - // as long as we have an Eq impl for the types we pass in - let array = [MyStruct::new(), MyStruct::new()]; - assert(array_eq(array, array, MyStruct::eq)); -} - -impl Eq for MyStruct { - fn eq(self, other: MyStruct) -> bool { - self.foo == other.foo - } -} -``` - -You can find more details on traits and trait implementations on the [traits page](../concepts/traits.md). - -## Manually Specifying Generics with the Turbofish Operator - -There are times when the compiler cannot reasonably infer what type should be used for a generic, or when the developer themselves may want to manually distinguish generic type parameters. This is where the `::<>` turbofish operator comes into play. - -The `::<>` operator can follow a variable or path and can be used to manually specify generic arguments within the angle brackets. -The name "turbofish" comes from that `::<>` looks like a little fish. - -Examples: -```rust -fn main() { - let mut slice = []; - slice = slice.push_back(1); - slice = slice.push_back(2); - // Without turbofish a type annotation would be needed on the left hand side - let array = slice.as_array::<2>(); -} -``` - - -```rust -trait MyTrait { - fn ten() -> Self; -} - -impl MyTrait for Field { - fn ten() -> Self { 10 } -} - -struct Foo { - inner: T -} - -impl Foo { - fn generic_method(_self: Self) -> U where U: MyTrait { - U::ten() - } -} - -fn example() { - let foo: Foo = Foo { inner: 1 }; - // Using a type other than `Field` here (e.g. u32) would fail as - // there is no matching impl for `u32: MyTrait`. - // - // Substituting the `10` on the left hand side of this assert - // with `10 as u32` would also fail with a type mismatch as we - // are expecting a `Field` from the right hand side. - assert(10 as u32 == foo.generic_method::()); -} -``` - -## Arithmetic Generics - -In addition to numeric generics, Noir also allows a limited form of arithmetic on generics. -When you have a numeric generic such as `N`, you can use the following operators on it in a -type position: `+`, `-`, `*`, `/`, and `%`. - -Note that type checking arithmetic generics is a best effort guess from the compiler and there -are many cases of types that are equal that the compiler may not see as such. For example, -we know that `T * (N + M)` should be equal to `T*N + T*M` but the compiler does not currently -apply the distributive law and thus sees these as different types. - -Even with this limitation though, the compiler can handle common cases decently well: - -```rust -trait Serialize { - fn serialize(self) -> [Field; N]; -} - -impl Serialize<1> for Field { - fn serialize(self) -> [Field; 1] { - [self] - } -} - -impl Serialize for [T; N] - where T: Serialize { .. } - -impl Serialize for (T, U) - where T: Serialize, U: Serialize { .. } - -fn main() { - let data = (1, [2, 3, 4]); - assert_eq(data.serialize().len(), 4); -} -``` - -Note that if there is any over or underflow the types will fail to unify: - -```rust title="underflow-example" showLineNumbers -fn pop(array: [Field; N]) -> [Field; N - 1] { - let mut result: [Field; N - 1] = std::mem::zeroed(); - for i in 0..N - 1 { - result[i] = array[i]; - } - result -} - -fn main() { - // error: Could not determine array length `(0 - 1)` - pop([]); -} -``` -> Source code: test_programs/compile_failure/arithmetic_generics_underflow/src/main.nr#L1-L14 - - -This also applies if there is underflow in an intermediate calculation: - -```rust title="intermediate-underflow-example" showLineNumbers -fn main() { - // From main it looks like there's nothing sketchy going on - seems_fine([]); -} - -// Since `seems_fine` says it can receive and return any length N -fn seems_fine(array: [Field; N]) -> [Field; N] { - // But inside `seems_fine` we pop from the array which - // requires the length to be greater than zero. - - // error: Could not determine array length `(0 - 1)` - push_zero(pop(array)) -} - -fn pop(array: [Field; N]) -> [Field; N - 1] { - let mut result: [Field; N - 1] = std::mem::zeroed(); - for i in 0..N - 1 { - result[i] = array[i]; - } - result -} - -fn push_zero(array: [Field; N]) -> [Field; N + 1] { - let mut result: [Field; N + 1] = std::mem::zeroed(); - for i in 0..N { - result[i] = array[i]; - } - // index N is already zeroed - result -} -``` -> Source code: test_programs/compile_failure/arithmetic_generics_intermediate_underflow/src/main.nr#L1-L32 - diff --git a/docs/versioned_docs/version-v0.37.0/noir/concepts/globals.md b/docs/versioned_docs/version-v0.37.0/noir/concepts/globals.md deleted file mode 100644 index 6b8314399a2..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/concepts/globals.md +++ /dev/null @@ -1,82 +0,0 @@ ---- -title: Global Variables -description: - Learn about global variables in Noir. Discover how - to declare, modify, and use them in your programs. -keywords: [noir programming language, globals, global variables, constants] -sidebar_position: 8 ---- - -## Globals - - -Noir supports global variables. The global's type can be inferred by the compiler entirely: - -```rust -global N = 5; // Same as `global N: Field = 5` - -global TUPLE = (3, 2); - -fn main() { - assert(N == 5); - assert(N == TUPLE.0 + TUPLE.1); -} -``` - -:::info - -Globals can be defined as any expression, so long as they don't depend on themselves - otherwise there would be a dependency cycle! For example: - -```rust -global T = foo(T); // dependency error -``` - -::: - - -If they are initialized to a literal integer, globals can be used to specify an array's length: - -```rust -global N: u32 = 2; - -fn main(y : [Field; N]) { - assert(y[0] == y[1]) -} -``` - -A global from another module can be imported or referenced externally like any other name: - -```rust -global N = 20; - -fn main() { - assert(my_submodule::N != N); -} - -mod my_submodule { - global N: Field = 10; -} -``` - -When a global is used, Noir replaces the name with its definition on each occurrence. -This means globals defined using function calls will repeat the call each time they're used: - -```rust -global RESULT = foo(); - -fn foo() -> [Field; 100] { ... } -``` - -This is usually fine since Noir will generally optimize any function call that does not -refer to a program input into a constant. It should be kept in mind however, if the called -function performs side-effects like `println`, as these will still occur on each use. - -### Visibility - -By default, like functions, globals are private to the module they exist in. You can use `pub` -to make the global public or `pub(crate)` to make it public to just its crate: - -```rust -// This global is now public -pub global N = 5; -``` \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.37.0/noir/concepts/lambdas.md b/docs/versioned_docs/version-v0.37.0/noir/concepts/lambdas.md deleted file mode 100644 index be3c7e0b5ca..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/concepts/lambdas.md +++ /dev/null @@ -1,81 +0,0 @@ ---- -title: Lambdas -description: Learn how to use anonymous functions in Noir programming language. -keywords: [Noir programming language, lambda, closure, function, anonymous function] -sidebar_position: 9 ---- - -## 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.37.0/noir/concepts/mutability.md b/docs/versioned_docs/version-v0.37.0/noir/concepts/mutability.md deleted file mode 100644 index fdeef6a87c5..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/concepts/mutability.md +++ /dev/null @@ -1,121 +0,0 @@ ---- -title: Mutability -description: - Learn about mutable variables in Noir. Discover how - to declare, modify, and use them in your programs. -keywords: [noir programming language, mutability in noir, mutable variables] -sidebar_position: 8 ---- - -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; -} -``` - -## Non-local mutability - -Non-local mutability can be achieved through the mutable reference type `&mut T`: - -```rust -fn set_to_zero(x: &mut Field) { - *x = 0; -} - -fn main() { - let mut y = 42; - set_to_zero(&mut y); - assert(*y == 0); -} -``` - -When creating a mutable reference, the original variable being referred to (`y` in this -example) must also be mutable. Since mutable references are a reference type, they must -be explicitly dereferenced via `*` to retrieve the underlying value. Note that this yields -a copy of the value, so mutating this copy will not change the original value behind the -reference: - -```rust -fn main() { - let mut x = 1; - let x_ref = &mut x; - - let mut y = *x_ref; - let y_ref = &mut y; - - x = 2; - *x_ref = 3; - - y = 4; - *y_ref = 5; - - assert(x == 3); - assert(*x_ref == 3); - assert(y == 5); - assert(*y_ref == 5); -} -``` - -Note that types in Noir are actually deeply immutable so the copy that occurs when -dereferencing is only a conceptual copy - no additional constraints will occur. - -Mutable references can also be stored within structs. Note that there is also -no lifetime parameter on these unlike rust. This is because the allocated memory -always lasts the entire program - as if it were an array of one element. - -```rust -struct Foo { - x: &mut Field -} - -impl Foo { - fn incr(mut self) { - *self.x += 1; - } -} - -fn main() { - let foo = Foo { x: &mut 0 }; - foo.incr(); - assert(*foo.x == 1); -} -``` - -In general, you should avoid non-local & shared mutability unless it is needed. Sticking -to only local mutability will improve readability and potentially improve compiler optimizations as well. diff --git a/docs/versioned_docs/version-v0.37.0/noir/concepts/ops.md b/docs/versioned_docs/version-v0.37.0/noir/concepts/ops.md deleted file mode 100644 index c35c36c38a9..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/concepts/ops.md +++ /dev/null @@ -1,98 +0,0 @@ ---- -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, - ] -sidebar_position: 3 ---- - -# 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, shift must be u8 | -| >> | Right shift an integer by another integer amount | Types must be integer, shift must be u8 | -| ! | 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 identically 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.37.0/noir/concepts/oracles.mdx b/docs/versioned_docs/version-v0.37.0/noir/concepts/oracles.mdx deleted file mode 100644 index 77a2ac1550a..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/concepts/oracles.mdx +++ /dev/null @@ -1,29 +0,0 @@ ---- -title: Oracles -description: Dive into how Noir supports Oracles via RPC calls, and learn how to declare an Oracle in Noir with our comprehensive guide. -keywords: - - Noir - - Oracles - - RPC Calls - - Unconstrained Functions - - Programming - - Blockchain -sidebar_position: 6 ---- - -import Experimental from '@site/src/components/Notes/_experimental.mdx'; - - - -Noir has support for Oracles via RPC calls. This means Noir will make an RPC call and use the return value for proof generation. - -Since Oracles are not resolved by Noir, they are [`unconstrained` functions](./unconstrained.md) - -You can declare an Oracle through the `#[oracle()]` flag. Example: - -```rust -#[oracle(get_number_sequence)] -unconstrained fn get_number_sequence(_size: Field) -> [Field] {} -``` - -The timeout for when using an external RPC oracle resolver can be set with the `NARGO_FOREIGN_CALL_TIMEOUT` environment variable. This timeout is in units of milliseconds. diff --git a/docs/versioned_docs/version-v0.37.0/noir/concepts/shadowing.md b/docs/versioned_docs/version-v0.37.0/noir/concepts/shadowing.md deleted file mode 100644 index 5ce6130d201..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/concepts/shadowing.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -title: Shadowing -sidebar_position: 12 ---- - -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.37.0/noir/concepts/traits.md b/docs/versioned_docs/version-v0.37.0/noir/concepts/traits.md deleted file mode 100644 index 9da00a77587..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/concepts/traits.md +++ /dev/null @@ -1,501 +0,0 @@ ---- -title: Traits -description: - Traits in Noir can be used to abstract out a common interface for functions across - several data types. -keywords: [noir programming language, traits, interfaces, generic, protocol] -sidebar_position: 14 ---- - -## Overview - -Traits in Noir are a useful abstraction similar to interfaces or protocols in other languages. Each trait defines -the interface of several methods contained within the trait. Types can then implement this trait by providing -implementations for these methods. For example in the program: - -```rust -struct Rectangle { - width: Field, - height: Field, -} - -impl Rectangle { - fn area(self) -> Field { - self.width * self.height - } -} - -fn log_area(r: Rectangle) { - println(r.area()); -} -``` - -We have a function `log_area` to log the area of a `Rectangle`. Now how should we change the program if we want this -function to work on `Triangle`s as well?: - -```rust -struct Triangle { - width: Field, - height: Field, -} - -impl Triangle { - fn area(self) -> Field { - self.width * self.height / 2 - } -} -``` - -Making `log_area` generic over all types `T` would be invalid since not all types have an `area` method. Instead, we can -introduce a new `Area` trait and make `log_area` generic over all types `T` that implement `Area`: - -```rust -trait Area { - fn area(self) -> Field; -} - -fn log_area(shape: T) where T: Area { - println(shape.area()); -} -``` - -We also need to explicitly implement `Area` for `Rectangle` and `Triangle`. We can do that by changing their existing -impls slightly. Note that the parameter types and return type of each of our `area` methods must match those defined -by the `Area` trait. - -```rust -impl Area for Rectangle { - fn area(self) -> Field { - self.width * self.height - } -} - -impl Area for Triangle { - fn area(self) -> Field { - self.width * self.height / 2 - } -} -``` - -Now we have a working program that is generic over any type of Shape that is used! Others can even use this program -as a library with their own types - such as `Circle` - as long as they also implement `Area` for these types. - -## Where Clauses - -As seen in `log_area` above, when we want to create a function or method that is generic over any type that implements -a trait, we can add a where clause to the generic function. - -```rust -fn log_area(shape: T) where T: Area { - println(shape.area()); -} -``` - -It is also possible to apply multiple trait constraints on the same variable at once by combining traits with the `+` -operator. Similarly, we can have multiple trait constraints by separating each with a comma: - -```rust -fn foo(elements: [T], thing: U) where - T: Default + Add + Eq, - U: Bar, -{ - let mut sum = T::default(); - - for element in elements { - sum += element; - } - - if sum == T::default() { - thing.bar(); - } -} -``` - -## Generic Implementations - -You can add generics to a trait implementation by adding the generic list after the `impl` keyword: - -```rust -trait Second { - fn second(self) -> Field; -} - -impl Second for (T, Field) { - fn second(self) -> Field { - self.1 - } -} -``` - -You can also implement a trait for every type this way: - -```rust -trait Debug { - fn debug(self); -} - -impl Debug for T { - fn debug(self) { - println(self); - } -} - -fn main() { - 1.debug(); -} -``` - -### Generic Trait Implementations With Where Clauses - -Where clauses can be placed on trait implementations themselves to restrict generics in a similar way. -For example, while `impl Foo for T` implements the trait `Foo` for every type, `impl Foo for T where T: Bar` -will implement `Foo` only for types that also implement `Bar`. This is often used for implementing generic types. -For example, here is the implementation for array equality: - -```rust -impl Eq for [T; let N: u32] where T: Eq { - // Test if two arrays have the same elements. - // Because both arrays must have length N, we know their lengths already match. - fn eq(self, other: Self) -> bool { - let mut result = true; - - for i in 0 .. self.len() { - // The T: Eq constraint is needed to call == on the array elements here - result &= self[i] == other[i]; - } - - result - } -} -``` - -Where clauses can also be placed on struct implementations. -For example, here is a method utilizing a generic type that implements the equality trait. - -```rust -struct Foo { - a: u32, - b: T, -} - -impl Foo where T: Eq { - fn eq(self, other: Self) -> bool { - (self.a == other.a) & self.b.eq(other.b) - } -} -``` - -## Generic Traits - -Traits themselves can also be generic by placing the generic arguments after the trait name. These generics are in -scope of every item within the trait. - -```rust -trait Into { - // Convert `self` to type `T` - fn into(self) -> T; -} -``` - -When implementing generic traits the generic arguments of the trait must be specified. This is also true anytime -when referencing a generic trait (e.g. in a `where` clause). - -```rust -struct MyStruct { - array: [Field; 2], -} - -impl Into<[Field; 2]> for MyStruct { - fn into(self) -> [Field; 2] { - self.array - } -} - -fn as_array(x: T) -> [Field; 2] - where T: Into<[Field; 2]> -{ - x.into() -} - -fn main() { - let array = [1, 2]; - let my_struct = MyStruct { array }; - - assert_eq(as_array(my_struct), array); -} -``` - -### Associated Types and Constants - -Traits also support associated types and constraints which can be thought of as additional generics that are referred to by name. - -Here's an example of a trait with an associated type `Foo` and a constant `Bar`: - -```rust -trait MyTrait { - type Foo; - - let Bar: u32; -} -``` - -Now when we're implementing `MyTrait` we also have to provide values for `Foo` and `Bar`: - -```rust -impl MyTrait for Field { - type Foo = i32; - - let Bar: u32 = 11; -} -``` - -Since associated constants can also be used in a type position, its values are limited to only other -expression kinds allowed in numeric generics. - -Note that currently all associated types and constants must be explicitly specified in a trait constraint. -If we leave out any, we'll get an error that we're missing one: - -```rust -// Error! Constraint is missing associated constant for `Bar` -fn foo(x: T) where T: MyTrait { - ... -} -``` - -Because all associated types and constants must be explicitly specified, they are essentially named generics, -although this is set to change in the future. Future versions of Noir will allow users to elide associated types -in trait constraints similar to Rust. When this is done, you may still refer to their value with the `::AssociatedType` -syntax: - -```rust -// Only valid in future versions of Noir: -fn foo(x: T) where T: MyTrait { - let _: ::Foo = ...; -} -``` - -The type as trait syntax is possible in Noir today but is less useful when each type must be explicitly specified anyway: - -```rust -fn foo(x: T) where T: MyTrait { - // Works, but could just use F directly - let _: >::Foo = ...; - - let _: F = ...; -} -``` - -## Trait Methods With No `self` - -A trait can contain any number of methods, each of which have access to the `Self` type which represents each type -that eventually implements the trait. Similarly, the `self` variable is available as well but is not required to be used. -For example, we can define a trait to create a default value for a type. This trait will need to return the `Self` type -but doesn't need to take any parameters: - -```rust -trait Default { - fn default() -> Self; -} -``` - -Implementing this trait can be done similarly to any other trait: - -```rust -impl Default for Field { - fn default() -> Field { - 0 - } -} - -struct MyType {} - -impl Default for MyType { - fn default() -> Field { - MyType {} - } -} -``` - -However, since there is no `self` parameter, we cannot call it via the method call syntax `object.method()`. -Instead, we'll need to refer to the function directly. This can be done either by referring to the -specific impl `MyType::default()` or referring to the trait itself `Default::default()`. In the later -case, type inference determines the impl that is selected. - -```rust -let my_struct = MyStruct::default(); - -let x: Field = Default::default(); -let result = x + Default::default(); -``` - -:::warning - -```rust -let _ = Default::default(); -``` - -If type inference cannot select which impl to use because of an ambiguous `Self` type, an impl will be -arbitrarily selected. This occurs most often when the result of a trait function call with no parameters -is unused. To avoid this, when calling a trait function with no `self` or `Self` parameters or return type, -always refer to it via the implementation type's namespace - e.g. `MyType::default()`. -This is set to change to an error in future Noir versions. - -::: - -## Default Method Implementations - -A trait can also have default implementations of its methods by giving a body to the desired functions. -Note that this body must be valid for all types that may implement the trait. As a result, the only -valid operations on `self` will be operations valid for any type or other operations on the trait itself. - -```rust -trait Numeric { - fn add(self, other: Self) -> Self; - - // Default implementation of double is (self + self) - fn double(self) -> Self { - self.add(self) - } -} -``` - -When implementing a trait with default functions, a type may choose to implement only the required functions: - -```rust -impl Numeric for Field { - fn add(self, other: Field) -> Field { - self + other - } -} -``` - -Or it may implement the optional methods as well: - -```rust -impl Numeric for u32 { - fn add(self, other: u32) -> u32 { - self + other - } - - fn double(self) -> u32 { - self * 2 - } -} -``` - -## Impl Specialization - -When implementing traits for a generic type it is possible to implement the trait for only a certain combination -of generics. This can be either as an optimization or because those specific generics are required to implement the trait. - -```rust -trait Sub { - fn sub(self, other: Self) -> Self; -} - -struct NonZero { - value: T, -} - -impl Sub for NonZero { - fn sub(self, other: Self) -> Self { - let value = self.value - other.value; - assert(value != 0); - NonZero { value } - } -} -``` - -## Overlapping Implementations - -Overlapping implementations are disallowed by Noir to ensure Noir's decision on which impl to select is never ambiguous. -This means if a trait `Foo` is already implemented -by a type `Bar` for all `T`, then we cannot also have a separate impl for `Bar` (or any other -type argument). Similarly, if there is an impl for all `T` such as `impl Debug for T`, we cannot create -any more impls to `Debug` for other types since it would be ambiguous which impl to choose for any given -method call. - -```rust -trait Trait {} - -// Previous impl defined here -impl Trait for (A, B) {} - -// error: Impl for type `(Field, Field)` overlaps with existing impl -impl Trait for (Field, Field) {} -``` - -## Trait Coherence - -Another restriction on trait implementations is coherence. This restriction ensures other crates cannot create -impls that may overlap with other impls, even if several unrelated crates are used as dependencies in the same -program. - -The coherence restriction is: to implement a trait, either the trait itself or the object type must be declared -in the crate the impl is in. - -In practice this often comes up when using types provided by libraries. If a library provides a type `Foo` that does -not implement a trait in the standard library such as `Default`, you may not `impl Default for Foo` in your own crate. -While restrictive, this prevents later issues or silent changes in the program if the `Foo` library later added its -own impl for `Default`. If you are a user of the `Foo` library in this scenario and need a trait not implemented by the -library your choices are to either submit a patch to the library or use the newtype pattern. - -### The Newtype Pattern - -The newtype pattern gets around the coherence restriction by creating a new wrapper type around the library type -that we cannot create `impl`s for. Since the new wrapper type is defined in our current crate, we can create -impls for any trait we need on it. - -```rust -struct Wrapper { - foo: some_library::Foo, -} - -impl Default for Wrapper { - fn default() -> Wrapper { - Wrapper { - foo: some_library::Foo::new(), - } - } -} -``` - -Since we have an impl for our own type, the behavior of this code will not change even if `some_library` is updated -to provide its own `impl Default for Foo`. The downside of this pattern is that it requires extra wrapping and -unwrapping of values when converting to and from the `Wrapper` and `Foo` types. - -### Trait Inheritance - -Sometimes, you might need one trait to use another trait’s functionality (like "inheritance" in some other languages). In this case, you can specify this relationship by listing any child traits after the parent trait's name and a colon. Now, whenever the parent trait is implemented it will require the child traits to be implemented as well. A parent trait is also called a "super trait." - -```rust -trait Person { - fn name(self) -> String; -} - -// Person is a supertrait of Student. -// Implementing Student requires you to also impl Person. -trait Student: Person { - fn university(self) -> String; -} - -trait Programmer { - fn fav_language(self) -> String; -} - -// CompSciStudent (computer science student) is a subtrait of both Programmer -// and Student. Implementing CompSciStudent requires you to impl both supertraits. -trait CompSciStudent: Programmer + Student { - fn git_username(self) -> String; -} -``` - -### Visibility - -By default, like functions, traits are private to the module they exist in. You can use `pub` -to make the trait public or `pub(crate)` to make it public to just its crate: - -```rust -// This trait is now public -pub trait Trait {} -``` \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.37.0/noir/concepts/unconstrained.md b/docs/versioned_docs/version-v0.37.0/noir/concepts/unconstrained.md deleted file mode 100644 index b5221b8d2dd..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/concepts/unconstrained.md +++ /dev/null @@ -1,104 +0,0 @@ ---- -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] -sidebar_position: 5 ---- - -Unconstrained functions are functions which do not constrain any of the included computation and allow for non-deterministic 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 AND 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 = unsafe { - 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). - -Note that in order to invoke unconstrained functions we need to wrap them in an `unsafe` block, -to make it clear that the call is unconstrained. - -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. - -## Break and Continue - -In addition to loops over runtime bounds, `break` and `continue` are also available in unconstrained code. See [break and continue](../concepts/control_flow.md#break-and-continue) diff --git a/docs/versioned_docs/version-v0.37.0/noir/modules_packages_crates/_category_.json b/docs/versioned_docs/version-v0.37.0/noir/modules_packages_crates/_category_.json deleted file mode 100644 index 1debcfe7675..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/modules_packages_crates/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "Modules, Packages and Crates", - "position": 2, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.37.0/noir/modules_packages_crates/crates_and_packages.md b/docs/versioned_docs/version-v0.37.0/noir/modules_packages_crates/crates_and_packages.md deleted file mode 100644 index 95ee9f52ab2..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/modules_packages_crates/crates_and_packages.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -title: Crates and Packages -description: Learn how to use Crates and Packages in your Noir project -keywords: [Nargo, dependencies, package management, crates, package] -sidebar_position: 0 ---- - -## 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/noir-projects/noir-contracts/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.37.0/noir/modules_packages_crates/dependencies.md b/docs/versioned_docs/version-v0.37.0/noir/modules_packages_crates/dependencies.md deleted file mode 100644 index 24e02de08fe..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/modules_packages_crates/dependencies.md +++ /dev/null @@ -1,124 +0,0 @@ ---- -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] -sidebar_position: 1 ---- - -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 = "noir-contracts/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 -└── lib_a - ├── Nargo.toml - └── src - └── lib.nr -``` - -Inside of the binary crate, you can specify: - -```toml -# Nargo.toml - -[dependencies] -lib_a = { path = "../lib_a" } -``` - -## 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 lib_a referenced above: - -```rust -use ecrecover; -use lib_a; -``` - -You can also import only the specific parts of dependency that you want to use, like so: - -```rust -use std::hash::sha256; -use std::scalar_mul::fixed_base_embedded_curve; -``` - -Lastly, as demonstrated in the -[elliptic curve example](../standard_library/cryptographic_primitives/ec_primitives.md#examples), you -can import multiple items in the same line by enclosing them in curly braces: - -```rust -use std::ec::tecurve::affine::{Curve, Point}; -``` - -We don't have a way to consume libraries from inside a [workspace](./workspaces.md) 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 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.37.0/noir/modules_packages_crates/modules.md b/docs/versioned_docs/version-v0.37.0/noir/modules_packages_crates/modules.md deleted file mode 100644 index 14aa1f0579a..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/modules_packages_crates/modules.md +++ /dev/null @@ -1,221 +0,0 @@ ---- -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] -sidebar_position: 2 ---- - -Noir's module system follows the same convention as the _newer_ version of Rust's module system. - -## Purpose of Modules - -Modules are used to organize 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 - -``` - -The module filename may also be the name of the module as a directory with the contents in a -file named `mod.nr` within that directory. The above example can alternatively be expressed like this: - -Filename : `src/main.nr` - -```rust -mod foo; - -fn main() { - foo::hello_world(); -} -``` - -Filename : `src/foo/mod.nr` - -```rust -fn from_foo() {} -``` - -Note that it's an error to have both files `src/foo.nr` and `src/foo/mod.nr` in the filesystem. - -### 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 -``` - -Similar to importing a module in the crate root, modules can be placed in a `mod.nr` file, like this: - -Filename : `src/main.nr` - -```rust -mod foo; - -fn main() { - foo::from_foo(); -} -``` - -Filename : `src/foo/mod.nr` - -```rust -mod bar; -fn from_foo() {} -``` - -Filename : `src/foo/bar/mod.nr` - -```rust -fn from_bar() {} -``` - -### Referencing a parent module - -Given a submodule, you can refer to its parent module using the `super` keyword. - -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 -// Same as bar::from_foo -use super::from_foo; - -fn from_bar() { - from_foo(); // invokes super::from_foo(), which is bar::from_foo() - super::from_foo(); // also invokes bar::from_foo() -} -``` - -### `use` visibility - -`use` declarations are private to the containing module, by default. However, like functions, -they can be marked as `pub` or `pub(crate)`. Such a use declaration serves to _re-export_ a name. -A public `use` declaration can therefore redirect some public name to a different target definition: -even a definition with a private canonical path, inside a different module. - -An example of re-exporting: - -```rust -mod some_module { - pub use foo::{bar, baz}; - mod foo { - pub fn bar() {} - pub fn baz() {} - } -} - -fn main() { - some_module::bar(); - some_module::baz(); -} -``` - -In this example, the module `some_module` re-exports two public names defined in `foo`. - -### Visibility - -By default, like functions, modules are private to the module (or crate) they exist in. You can use `pub` -to make the module public or `pub(crate)` to make it public to just its crate: - -```rust -// This module is now public and can be seen by other crates. -pub mod foo; -``` \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.37.0/noir/modules_packages_crates/workspaces.md b/docs/versioned_docs/version-v0.37.0/noir/modules_packages_crates/workspaces.md deleted file mode 100644 index 513497f12bf..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/modules_packages_crates/workspaces.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -title: Workspaces -sidebar_position: 3 ---- - -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 -│ │ └── Prover.toml -│ │ └── src -│ │ └── main.nr -│ └── b -│ ├── Nargo.toml -│ └── Prover.toml -│ └── src -│ └── main.nr -│ -└── Nargo.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.37.0/noir/standard_library/_category_.json b/docs/versioned_docs/version-v0.37.0/noir/standard_library/_category_.json deleted file mode 100644 index af04c0933fd..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/standard_library/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "Standard Library", - "position": 1, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.37.0/noir/standard_library/bigint.md b/docs/versioned_docs/version-v0.37.0/noir/standard_library/bigint.md deleted file mode 100644 index 05c3011634f..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/standard_library/bigint.md +++ /dev/null @@ -1,127 +0,0 @@ ---- -title: Big Integers -description: How to use big integers from Noir standard library -keywords: - [ - Big Integer, - Noir programming language, - Noir libraries, - ] ---- - -The BigInt module in the standard library exposes some class of integers which do not fit (well) into a Noir native field. It implements modulo arithmetic, modulo a 'big' prime number. - -:::note - -The module can currently be considered as `Field`s with fixed modulo sizes used by a set of elliptic curves, in addition to just the native curve. [More work](https://github.com/noir-lang/noir/issues/510) is needed to achieve arbitrarily sized big integers. - -:::note - -`nargo` can be built with `--profile release-pedantic` to enable extra overflow checks which may affect `BigInt` results in some cases. -Consider the [`noir-bignum`](https://github.com/noir-lang/noir-bignum) library for an optimized alternative approach. - -::: - -Currently 6 classes of integers (i.e 'big' prime numbers) are available in the module, namely: - -- BN254 Fq: Bn254Fq -- BN254 Fr: Bn254Fr -- Secp256k1 Fq: Secpk1Fq -- Secp256k1 Fr: Secpk1Fr -- Secp256r1 Fr: Secpr1Fr -- Secp256r1 Fq: Secpr1Fq - -Where XXX Fq and XXX Fr denote respectively the order of the base and scalar field of the (usual) elliptic curve XXX. -For instance the big integer 'Secpk1Fq' in the standard library refers to integers modulo $2^{256}-2^{32}-977$. - -Feel free to explore the source code for the other primes: - -```rust title="big_int_definition" showLineNumbers -pub struct BigInt { - pointer: u32, - modulus: u32, -} -``` -> Source code: noir_stdlib/src/bigint.nr#L28-L33 - - -## Example usage - -A common use-case is when constructing a big integer from its bytes representation, and performing arithmetic operations on it: - -```rust title="big_int_example" showLineNumbers -fn big_int_example(x: u8, y: u8) { - let a = Secpk1Fq::from_le_bytes(&[x, y, 0, 45, 2]); - let b = Secpk1Fq::from_le_bytes(&[y, x, 9]); - let c = (a + b) * b / a; - let d = c.to_le_bytes(); - println(d[0]); -} -``` -> Source code: test_programs/execution_success/bigint/src/main.nr#L74-L82 - - -## Methods - -The available operations for each big integer are: - -### from_le_bytes - -Construct a big integer from its little-endian bytes representation. Example: - -```rust - // Construct a big integer from a slice of bytes - let a = Secpk1Fq::from_le_bytes(&[x, y, 0, 45, 2]); - // Construct a big integer from an array of 32 bytes - let a = Secpk1Fq::from_le_bytes_32([1;32]); - ``` - -Sure, here's the formatted version of the remaining methods: - -### to_le_bytes - -Return the little-endian bytes representation of a big integer. Example: - -```rust -let bytes = a.to_le_bytes(); -``` - -### add - -Add two big integers. Example: - -```rust -let sum = a + b; -``` - -### sub - -Subtract two big integers. Example: - -```rust -let difference = a - b; -``` - -### mul - -Multiply two big integers. Example: - -```rust -let product = a * b; -``` - -### div - -Divide two big integers. Note that division is field division and not euclidean division. Example: - -```rust -let quotient = a / b; -``` - -### eq - -Compare two big integers. Example: - -```rust -let are_equal = a == b; -``` diff --git a/docs/versioned_docs/version-v0.37.0/noir/standard_library/black_box_fns.md b/docs/versioned_docs/version-v0.37.0/noir/standard_library/black_box_fns.md deleted file mode 100644 index d6079ab182c..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/standard_library/black_box_fns.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -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. - -The ACVM spec defines a set of blackbox functions which backends will be expected to implement. This allows backends to use optimized implementations of these constraints if they have them, however they may also fallback to less efficient naive implementations if not. - -## Function list - -Here is a list of the current black box functions: - -- [AES128](./cryptographic_primitives/ciphers.mdx#aes128) -- [SHA256](./cryptographic_primitives/hashes.mdx#sha256) -- [Schnorr signature verification](./cryptographic_primitives/schnorr.mdx) -- [Blake2s](./cryptographic_primitives/hashes.mdx#blake2s) -- [Blake3](./cryptographic_primitives/hashes.mdx#blake3) -- [Pedersen Hash](./cryptographic_primitives/hashes.mdx#pedersen_hash) -- [Pedersen Commitment](./cryptographic_primitives/hashes.mdx#pedersen_commitment) -- [ECDSA signature verification](./cryptographic_primitives/ecdsa_sig_verification.mdx) -- [Embedded curve operations (MSM, addition, ...)](./cryptographic_primitives/embedded_curve_ops.mdx) -- AND -- XOR -- RANGE -- [Keccak256](./cryptographic_primitives/hashes.mdx#keccak256) -- [Recursive proof verification](./recursion.mdx) - -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. - -You can view the black box functions defined in the ACVM code [here](https://github.com/noir-lang/noir/blob/master/acvm-repo/acir/src/circuit/black_box_functions.rs). diff --git a/docs/versioned_docs/version-v0.37.0/noir/standard_library/bn254.md b/docs/versioned_docs/version-v0.37.0/noir/standard_library/bn254.md deleted file mode 100644 index 3294f005dbb..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/standard_library/bn254.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -title: Bn254 Field Library ---- - -Noir provides a module in standard library with some optimized functions for bn254 Fr in `std::field::bn254`. - -## decompose - -```rust -fn decompose(x: Field) -> (Field, Field) {} -``` - -Decomposes a single field into two fields, low and high. The low field contains the lower 16 bytes of the input field and the high field contains the upper 16 bytes of the input field. Both field results are range checked to 128 bits. - - -## assert_gt - -```rust -fn assert_gt(a: Field, b: Field) {} -``` - -Asserts that a > b. This will generate less constraints than using `assert(gt(a, b))`. - -## assert_lt - -```rust -fn assert_lt(a: Field, b: Field) {} -``` - -Asserts that a < b. This will generate less constraints than using `assert(lt(a, b))`. - -## gt - -```rust -fn gt(a: Field, b: Field) -> bool {} -``` - -Returns true if a > b. - -## lt - -```rust -fn lt(a: Field, b: Field) -> bool {} -``` - -Returns true if a < b. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.37.0/noir/standard_library/containers/boundedvec.md b/docs/versioned_docs/version-v0.37.0/noir/standard_library/containers/boundedvec.md deleted file mode 100644 index 509b214bf3a..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/standard_library/containers/boundedvec.md +++ /dev/null @@ -1,419 +0,0 @@ ---- -title: Bounded Vectors -keywords: [noir, vector, bounded vector, slice] -sidebar_position: 1 ---- - -A `BoundedVec` is a growable storage similar to a `Vec` except that it -is bounded with a maximum possible length. Unlike `Vec`, `BoundedVec` is not implemented -via slices and thus is not subject to the same restrictions slices are (notably, nested -slices - and thus nested vectors as well - are disallowed). - -Since a BoundedVec is backed by a normal array under the hood, growing the BoundedVec by -pushing an additional element is also more efficient - the length only needs to be increased -by one. - -For these reasons `BoundedVec` should generally be preferred over `Vec` when there -is a reasonable maximum bound that can be placed on the vector. - -Example: - -```rust -let mut vector: BoundedVec = BoundedVec::new(); -for i in 0..5 { - vector.push(i); -} -assert(vector.len() == 5); -assert(vector.max_len() == 10); -``` - -## Methods - -### new - -```rust -pub fn new() -> Self -``` - -Creates a new, empty vector of length zero. - -Since this container is backed by an array internally, it still needs an initial value -to give each element. To resolve this, each element is zeroed internally. This value -is guaranteed to be inaccessible unless `get_unchecked` is used. - -Example: - -```rust -let empty_vector: BoundedVec = BoundedVec::new(); -assert(empty_vector.len() == 0); -``` - -Note that whenever calling `new` the maximum length of the vector should always be specified -via a type signature: - -```rust title="new_example" showLineNumbers -fn good() -> BoundedVec { - // Ok! MaxLen is specified with a type annotation - let v1: BoundedVec = BoundedVec::new(); - let v2 = BoundedVec::new(); - - // Ok! MaxLen is known from the type of `good`'s return value - v2 -} - -fn bad() { - // Error: Type annotation needed - // The compiler can't infer `MaxLen` from this code. - let mut v3 = BoundedVec::new(); - v3.push(5); -} -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L11-L27 - - -This defaulting of `MaxLen` (and numeric generics in general) to zero may change in future noir versions -but for now make sure to use type annotations when using bounded vectors. Otherwise, you will receive a constraint failure at runtime when the vec is pushed to. - -### get - -```rust -pub fn get(self, index: u64) -> T { -``` - -Retrieves an element from the vector at the given index, starting from zero. - -If the given index is equal to or greater than the length of the vector, this -will issue a constraint failure. - -Example: - -```rust -fn foo(v: BoundedVec) { - let first = v.get(0); - let last = v.get(v.len() - 1); - assert(first != last); -} -``` - -### get_unchecked - -```rust -pub fn get_unchecked(self, index: u64) -> T { -``` - -Retrieves an element from the vector at the given index, starting from zero, without -performing a bounds check. - -Since this function does not perform a bounds check on length before accessing the element, -it is unsafe! Use at your own risk! - -Example: - -```rust title="get_unchecked_example" showLineNumbers -fn sum_of_first_three(v: BoundedVec) -> u32 { - // Always ensure the length is larger than the largest - // index passed to get_unchecked - assert(v.len() > 2); - let first = v.get_unchecked(0); - let second = v.get_unchecked(1); - let third = v.get_unchecked(2); - first + second + third -} -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L54-L64 - - -### set - -```rust -pub fn set(&mut self: Self, index: u64, value: T) { -``` - -Writes an element to the vector at the given index, starting from zero. - -If the given index is equal to or greater than the length of the vector, this will issue a constraint failure. - -Example: - -```rust -fn foo(v: BoundedVec) { - let first = v.get(0); - assert(first != 42); - v.set(0, 42); - let new_first = v.get(0); - assert(new_first == 42); -} -``` - -### set_unchecked - -```rust -pub fn set_unchecked(&mut self: Self, index: u64, value: T) -> T { -``` - -Writes an element to the vector at the given index, starting from zero, without performing a bounds check. - -Since this function does not perform a bounds check on length before accessing the element, it is unsafe! Use at your own risk! - -Example: - -```rust title="set_unchecked_example" showLineNumbers -fn set_unchecked_example() { - let mut vec: BoundedVec = BoundedVec::new(); - vec.extend_from_array([1, 2]); - - // Here we're safely writing within the valid range of `vec` - // `vec` now has the value [42, 2] - vec.set_unchecked(0, 42); - - // We can then safely read this value back out of `vec`. - // Notice that we use the checked version of `get` which would prevent reading unsafe values. - assert_eq(vec.get(0), 42); - - // We've now written past the end of `vec`. - // As this index is still within the maximum potential length of `v`, - // it won't cause a constraint failure. - vec.set_unchecked(2, 42); - println(vec); - - // This will write past the end of the maximum potential length of `vec`, - // it will then trigger a constraint failure. - vec.set_unchecked(5, 42); - println(vec); -} -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L67-L91 - - - -### push - -```rust -pub fn push(&mut self, elem: T) { -``` - -Pushes an element to the end of the vector. This increases the length -of the vector by one. - -Panics if the new length of the vector will be greater than the max length. - -Example: - -```rust title="bounded-vec-push-example" showLineNumbers -let mut v: BoundedVec = BoundedVec::new(); - - v.push(1); - v.push(2); - - // Panics with failed assertion "push out of bounds" - v.push(3); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L95-L103 - - -### pop - -```rust -pub fn pop(&mut self) -> T -``` - -Pops the element at the end of the vector. This will decrease the length -of the vector by one. - -Panics if the vector is empty. - -Example: - -```rust title="bounded-vec-pop-example" showLineNumbers -let mut v: BoundedVec = BoundedVec::new(); - v.push(1); - v.push(2); - - let two = v.pop(); - let one = v.pop(); - - assert(two == 2); - assert(one == 1); - // error: cannot pop from an empty vector - // let _ = v.pop(); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L108-L120 - - -### len - -```rust -pub fn len(self) -> u64 { -``` - -Returns the current length of this vector - -Example: - -```rust title="bounded-vec-len-example" showLineNumbers -let mut v: BoundedVec = BoundedVec::new(); - assert(v.len() == 0); - - v.push(100); - assert(v.len() == 1); - - v.push(200); - v.push(300); - v.push(400); - assert(v.len() == 4); - - let _ = v.pop(); - let _ = v.pop(); - assert(v.len() == 2); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L125-L140 - - -### max_len - -```rust -pub fn max_len(_self: BoundedVec) -> u64 { -``` - -Returns the maximum length of this vector. This is always -equal to the `MaxLen` parameter this vector was initialized with. - -Example: - -```rust title="bounded-vec-max-len-example" showLineNumbers -let mut v: BoundedVec = BoundedVec::new(); - - assert(v.max_len() == 5); - v.push(10); - assert(v.max_len() == 5); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L145-L151 - - -### storage - -```rust -pub fn storage(self) -> [T; MaxLen] { -``` - -Returns the internal array within this vector. -Since arrays in Noir are immutable, mutating the returned storage array will not mutate -the storage held internally by this vector. - -Note that uninitialized elements may be zeroed out! - -Example: - -```rust title="bounded-vec-storage-example" showLineNumbers -let mut v: BoundedVec = BoundedVec::new(); - - assert(v.storage() == [0, 0, 0, 0, 0]); - - v.push(57); - assert(v.storage() == [57, 0, 0, 0, 0]); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L156-L163 - - -### extend_from_array - -```rust -pub fn extend_from_array(&mut self, array: [T; Len]) -``` - -Pushes each element from the given array to this vector. - -Panics if pushing each element would cause the length of this vector -to exceed the maximum length. - -Example: - -```rust title="bounded-vec-extend-from-array-example" showLineNumbers -let mut vec: BoundedVec = BoundedVec::new(); - vec.extend_from_array([2, 4]); - - assert(vec.len == 2); - assert(vec.get(0) == 2); - assert(vec.get(1) == 4); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L168-L175 - - -### extend_from_bounded_vec - -```rust -pub fn extend_from_bounded_vec(&mut self, vec: BoundedVec) -``` - -Pushes each element from the other vector to this vector. The length of -the other vector is left unchanged. - -Panics if pushing each element would cause the length of this vector -to exceed the maximum length. - -Example: - -```rust title="bounded-vec-extend-from-bounded-vec-example" showLineNumbers -let mut v1: BoundedVec = BoundedVec::new(); - let mut v2: BoundedVec = BoundedVec::new(); - - v2.extend_from_array([1, 2, 3]); - v1.extend_from_bounded_vec(v2); - - assert(v1.storage() == [1, 2, 3, 0, 0]); - assert(v2.storage() == [1, 2, 3, 0, 0, 0, 0]); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L180-L189 - - -### from_array - -```rust -pub fn from_array(array: [T; Len]) -> Self -``` - -Creates a new vector, populating it with values derived from an array input. -The maximum length of the vector is determined based on the type signature. - -Example: -```rust -let bounded_vec: BoundedVec = BoundedVec::from_array([1, 2, 3]) -``` - -### map - -```rust -pub fn map(self, f: fn[Env](T) -> U) -> BoundedVec -``` - -Creates a new vector of equal size by calling a closure on each element in this vector. - -Example: - -```rust title="bounded-vec-map-example" showLineNumbers -let vec: BoundedVec = BoundedVec::from_array([1, 2, 3, 4]); - let result = vec.map(|value| value * 2); -``` -> Source code: noir_stdlib/src/collections/bounded_vec.nr#L495-L498 - - -### any - -```rust -pub fn any(self, predicate: fn[Env](T) -> bool) -> bool -``` - -Returns true if the given predicate returns true for any element -in this vector. - -Example: - -```rust title="bounded-vec-any-example" showLineNumbers -let mut v: BoundedVec = BoundedVec::new(); - v.extend_from_array([2, 4, 6]); - - let all_even = !v.any(|elem: u32| elem % 2 != 0); - assert(all_even); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L256-L262 - diff --git a/docs/versioned_docs/version-v0.37.0/noir/standard_library/containers/hashmap.md b/docs/versioned_docs/version-v0.37.0/noir/standard_library/containers/hashmap.md deleted file mode 100644 index 395cc312705..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/standard_library/containers/hashmap.md +++ /dev/null @@ -1,587 +0,0 @@ ---- -title: HashMap -keywords: [noir, map, hash, hashmap] -sidebar_position: 1 ---- - -`HashMap` is used to efficiently store and look up key-value pairs. - -`HashMap` is a bounded type which can store anywhere from zero to `MaxLen` total elements. -Note that due to hash collisions, the actual maximum number of elements stored by any particular -hashmap is likely lower than `MaxLen`. This is true even with cryptographic hash functions since -every hash value will be performed modulo `MaxLen`. - -Example: - -```rust -// Create a mapping from Fields to u32s with a maximum length of 12 -// using a poseidon2 hasher -use std::hash::poseidon2::Poseidon2Hasher; -let mut map: HashMap> = HashMap::default(); - -map.insert(1, 2); -map.insert(3, 4); - -let two = map.get(1).unwrap(); -``` - -## Methods - -### default - -```rust title="default" showLineNumbers -impl Default for HashMap -where - B: BuildHasher + Default, - H: Hasher + Default, -{ - /// Constructs an empty HashMap. - /// - /// Example: - /// - /// ```noir - /// let hashmap: HashMap> = HashMap::default(); - /// assert(hashmap.is_empty()); - /// ``` - fn default() -> Self { -``` -> Source code: noir_stdlib/src/collections/map.nr#L681-L696 - - -Creates a fresh, empty HashMap. - -When using this function, always make sure to specify the maximum size of the hash map. - -This is the same `default` from the `Default` implementation given further below. It is -repeated here for convenience since it is the recommended way to create a hashmap. - -Example: - -```rust title="default_example" showLineNumbers -let hashmap: HashMap> = HashMap::default(); - assert(hashmap.is_empty()); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L207-L210 - - -Because `HashMap` has so many generic arguments that are likely to be the same throughout -your program, it may be helpful to create a type alias: - -```rust title="type_alias" showLineNumbers -type MyMap = HashMap>; -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L201-L203 - - -### with_hasher - -```rust title="with_hasher" showLineNumbers -pub fn with_hasher(_build_hasher: B) -> Self - where - B: BuildHasher, - { -``` -> Source code: noir_stdlib/src/collections/map.nr#L103-L108 - - -Creates a hashmap with an existing `BuildHasher`. This can be used to ensure multiple -hashmaps are created with the same hasher instance. - -Example: - -```rust title="with_hasher_example" showLineNumbers -let my_hasher: BuildHasherDefault = Default::default(); - let hashmap: HashMap> = - HashMap::with_hasher(my_hasher); - assert(hashmap.is_empty()); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L211-L216 - - -### get - -```rust title="get" showLineNumbers -pub fn get(self, key: K) -> Option - where - K: Eq + Hash, - B: BuildHasher, - H: Hasher, - { -``` -> Source code: noir_stdlib/src/collections/map.nr#L465-L472 - - -Retrieves a value from the hashmap, returning `Option::none()` if it was not found. - -Example: - -```rust title="get_example" showLineNumbers -fn get_example(map: HashMap>) { - let x = map.get(12); - - if x.is_some() { - assert(x.unwrap() == 42); - } -} -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L296-L304 - - -### insert - -```rust title="insert" showLineNumbers -pub fn insert(&mut self, key: K, value: V) - where - K: Eq + Hash, - B: BuildHasher, - H: Hasher, - { -``` -> Source code: noir_stdlib/src/collections/map.nr#L507-L514 - - -Inserts a new key-value pair into the map. If the key was already in the map, its -previous value will be overridden with the newly provided one. - -Example: - -```rust title="insert_example" showLineNumbers -let mut map: HashMap> = HashMap::default(); - map.insert(12, 42); - assert(map.len() == 1); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L217-L221 - - -### remove - -```rust title="remove" showLineNumbers -pub fn remove(&mut self, key: K) - where - K: Eq + Hash, - B: BuildHasher, - H: Hasher, - { -``` -> Source code: noir_stdlib/src/collections/map.nr#L563-L570 - - -Removes the given key-value pair from the map. If the key was not already present -in the map, this does nothing. - -Example: - -```rust title="remove_example" showLineNumbers -map.remove(12); - assert(map.is_empty()); - - // If a key was not present in the map, remove does nothing - map.remove(12); - assert(map.is_empty()); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L224-L231 - - -### is_empty - -```rust title="is_empty" showLineNumbers -pub fn is_empty(self) -> bool { -``` -> Source code: noir_stdlib/src/collections/map.nr#L167-L169 - - -True if the length of the hash map is empty. - -Example: - -```rust title="is_empty_example" showLineNumbers -assert(map.is_empty()); - - map.insert(1, 2); - assert(!map.is_empty()); - - map.remove(1); - assert(map.is_empty()); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L232-L240 - - -### len - -```rust title="len" showLineNumbers -pub fn len(self) -> u32 { -``` -> Source code: noir_stdlib/src/collections/map.nr#L424-L426 - - -Returns the current length of this hash map. - -Example: - -```rust title="len_example" showLineNumbers -// This is equivalent to checking map.is_empty() - assert(map.len() == 0); - - map.insert(1, 2); - map.insert(3, 4); - map.insert(5, 6); - assert(map.len() == 3); - - // 3 was already present as a key in the hash map, so the length is unchanged - map.insert(3, 7); - assert(map.len() == 3); - - map.remove(1); - assert(map.len() == 2); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L241-L256 - - -### capacity - -```rust title="capacity" showLineNumbers -pub fn capacity(_self: Self) -> u32 { -``` -> Source code: noir_stdlib/src/collections/map.nr#L446-L448 - - -Returns the maximum capacity of this hashmap. This is always equal to the capacity -specified in the hashmap's type. - -Unlike hashmaps in general purpose programming languages, hashmaps in Noir have a -static capacity that does not increase as the map grows larger. Thus, this capacity -is also the maximum possible element count that can be inserted into the hashmap. -Due to hash collisions (modulo the hashmap length), it is likely the actual maximum -element count will be lower than the full capacity. - -Example: - -```rust title="capacity_example" showLineNumbers -let empty_map: HashMap> = - HashMap::default(); - assert(empty_map.len() == 0); - assert(empty_map.capacity() == 42); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L257-L262 - - -### clear - -```rust title="clear" showLineNumbers -pub fn clear(&mut self) { -``` -> Source code: noir_stdlib/src/collections/map.nr#L123-L125 - - -Clears the hashmap, removing all key-value pairs from it. - -Example: - -```rust title="clear_example" showLineNumbers -assert(!map.is_empty()); - map.clear(); - assert(map.is_empty()); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L263-L267 - - -### contains_key - -```rust title="contains_key" showLineNumbers -pub fn contains_key(self, key: K) -> bool - where - K: Hash + Eq, - B: BuildHasher, - H: Hasher, - { -``` -> Source code: noir_stdlib/src/collections/map.nr#L143-L150 - - -True if the hashmap contains the given key. Unlike `get`, this will not also return -the value associated with the key. - -Example: - -```rust title="contains_key_example" showLineNumbers -if map.contains_key(7) { - let value = map.get(7); - assert(value.is_some()); - } else { - println("No value for key 7!"); - } -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L268-L275 - - -### entries - -```rust title="entries" showLineNumbers -pub fn entries(self) -> BoundedVec<(K, V), N> { -``` -> Source code: noir_stdlib/src/collections/map.nr#L191-L193 - - -Returns a vector of each key-value pair present in the hashmap. - -The length of the returned vector is always equal to the length of the hashmap. - -Example: - -```rust title="entries_example" showLineNumbers -let entries = map.entries(); - - // The length of a hashmap may not be compile-time known, so we - // need to loop over its capacity instead - for i in 0..map.capacity() { - if i < entries.len() { - let (key, value) = entries.get(i); - println(f"{key} -> {value}"); - } - } -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L307-L318 - - -### keys - -```rust title="keys" showLineNumbers -pub fn keys(self) -> BoundedVec { -``` -> Source code: noir_stdlib/src/collections/map.nr#L227-L229 - - -Returns a vector of each key present in the hashmap. - -The length of the returned vector is always equal to the length of the hashmap. - -Example: - -```rust title="keys_example" showLineNumbers -let keys = map.keys(); - - for i in 0..keys.max_len() { - if i < keys.len() { - let key = keys.get_unchecked(i); - let value = map.get(key).unwrap_unchecked(); - println(f"{key} -> {value}"); - } - } -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L319-L329 - - -### values - -```rust title="values" showLineNumbers -pub fn values(self) -> BoundedVec { -``` -> Source code: noir_stdlib/src/collections/map.nr#L262-L264 - - -Returns a vector of each value present in the hashmap. - -The length of the returned vector is always equal to the length of the hashmap. - -Example: - -```rust title="values_example" showLineNumbers -let values = map.values(); - - for i in 0..values.max_len() { - if i < values.len() { - let value = values.get_unchecked(i); - println(f"Found value {value}"); - } - } -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L330-L339 - - -### iter_mut - -```rust title="iter_mut" showLineNumbers -pub fn iter_mut(&mut self, f: fn(K, V) -> (K, V)) - where - K: Eq + Hash, - B: BuildHasher, - H: Hasher, - { -``` -> Source code: noir_stdlib/src/collections/map.nr#L297-L304 - - -Iterates through each key-value pair of the HashMap, setting each key-value pair to the -result returned from the given function. - -Note that since keys can be mutated, the HashMap needs to be rebuilt as it is iterated -through. If this is not desired, use `iter_values_mut` if only values need to be mutated, -or `entries` if neither keys nor values need to be mutated. - -The iteration order is left unspecified. As a result, if two keys are mutated to become -equal, which of the two values that will be present for the key in the resulting map is also unspecified. - -Example: - -```rust title="iter_mut_example" showLineNumbers -// Add 1 to each key in the map, and double the value associated with that key. - map.iter_mut(|k, v| (k + 1, v * 2)); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L343-L346 - - -### iter_keys_mut - -```rust title="iter_keys_mut" showLineNumbers -pub fn iter_keys_mut(&mut self, f: fn(K) -> K) - where - K: Eq + Hash, - B: BuildHasher, - H: Hasher, - { -``` -> Source code: noir_stdlib/src/collections/map.nr#L335-L342 - - -Iterates through the HashMap, mutating each key to the result returned from -the given function. - -Note that since keys can be mutated, the HashMap needs to be rebuilt as it is iterated -through. If only iteration is desired and the keys are not intended to be mutated, -prefer using `entries` instead. - -The iteration order is left unspecified. As a result, if two keys are mutated to become -equal, which of the two values that will be present for the key in the resulting map is also unspecified. - -Example: - -```rust title="iter_keys_mut_example" showLineNumbers -// Double each key, leaving the value associated with that key untouched - map.iter_keys_mut(|k| k * 2); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L347-L350 - - -### iter_values_mut - -```rust title="iter_values_mut" showLineNumbers -pub fn iter_values_mut(&mut self, f: fn(V) -> V) { -``` -> Source code: noir_stdlib/src/collections/map.nr#L367-L369 - - -Iterates through the HashMap, applying the given function to each value and mutating the -value to equal the result. This function is more efficient than `iter_mut` and `iter_keys_mut` -because the keys are untouched and the underlying hashmap thus does not need to be reordered. - -Example: - -```rust title="iter_values_mut_example" showLineNumbers -// Halve each value - map.iter_values_mut(|v| v / 2); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L351-L354 - - -### retain - -```rust title="retain" showLineNumbers -pub fn retain(&mut self, f: fn(K, V) -> bool) { -``` -> Source code: noir_stdlib/src/collections/map.nr#L388-L390 - - -Retains only the key-value pairs for which the given function returns true. -Any key-value pairs for which the function returns false will be removed from the map. - -Example: - -```rust title="retain_example" showLineNumbers -map.retain(|k, v| (k != 0) & (v != 0)); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L279-L281 - - -## Trait Implementations - -### default - -```rust title="default" showLineNumbers -impl Default for HashMap -where - B: BuildHasher + Default, - H: Hasher + Default, -{ - /// Constructs an empty HashMap. - /// - /// Example: - /// - /// ```noir - /// let hashmap: HashMap> = HashMap::default(); - /// assert(hashmap.is_empty()); - /// ``` - fn default() -> Self { -``` -> Source code: noir_stdlib/src/collections/map.nr#L681-L696 - - -Constructs an empty HashMap. - -Example: - -```rust title="default_example" showLineNumbers -let hashmap: HashMap> = HashMap::default(); - assert(hashmap.is_empty()); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L207-L210 - - -### eq - -```rust title="eq" showLineNumbers -impl Eq for HashMap -where - K: Eq + Hash, - V: Eq, - B: BuildHasher, - H: Hasher, -{ - /// Checks if two HashMaps are equal. - /// - /// Example: - /// - /// ```noir - /// let mut map1: HashMap> = HashMap::default(); - /// let mut map2: HashMap> = HashMap::default(); - /// - /// map1.insert(1, 2); - /// map1.insert(3, 4); - /// - /// map2.insert(3, 4); - /// map2.insert(1, 2); - /// - /// assert(map1 == map2); - /// ``` - fn eq(self, other: HashMap) -> bool { -``` -> Source code: noir_stdlib/src/collections/map.nr#L629-L654 - - -Checks if two HashMaps are equal. - -Example: - -```rust title="eq_example" showLineNumbers -let mut map1: HashMap> = HashMap::default(); - let mut map2: HashMap> = HashMap::default(); - - map1.insert(1, 2); - map1.insert(3, 4); - - map2.insert(3, 4); - map2.insert(1, 2); - - assert(map1 == map2); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L282-L293 - diff --git a/docs/versioned_docs/version-v0.37.0/noir/standard_library/containers/index.md b/docs/versioned_docs/version-v0.37.0/noir/standard_library/containers/index.md deleted file mode 100644 index ea84c6d5c21..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/standard_library/containers/index.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Containers -description: Container types provided by Noir's standard library for storing and retrieving data -keywords: [containers, data types, vec, hashmap] ---- diff --git a/docs/versioned_docs/version-v0.37.0/noir/standard_library/containers/vec.mdx b/docs/versioned_docs/version-v0.37.0/noir/standard_library/containers/vec.mdx deleted file mode 100644 index 475011922f8..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/standard_library/containers/vec.mdx +++ /dev/null @@ -1,170 +0,0 @@ ---- -title: Vectors -description: Delve into the Vec 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] -sidebar_position: 6 ---- - -import Experimental from '@site/src/components/Notes/_experimental.mdx'; - - - -A vector is a collection type similar to Rust's `Vec` type. In Noir, it is a convenient way to use slices as mutable arrays. - -Example: - -```rust -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 -``` - -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 -``` - -Example: - -```rust -let slice: [Field] = &[1, 2, 3]; -let vector_from_slice = Vec::from_slice(slice); -assert(vector_from_slice.len() == 3); -``` - -### len - -Returns the number of elements in the vector. - -```rust -pub fn len(self) -> Field -``` - -Example: - -```rust -let empty_vector: Vec = Vec::new(); -assert(empty_vector.len() == 0); -``` - -### 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 -``` - -Example: - -```rust -let vector: Vec = Vec::from_slice(&[10, 20, 30]); -assert(vector.get(1) == 20); -``` - -### set - -```rust -pub fn set(&mut self: Self, index: u64, value: T) { -``` - -Writes an element to the vector at the given index, starting from zero. - -Panics if the index points beyond the vector's end. - -Example: - -```rust -let vector: Vec = Vec::from_slice(&[10, 20, 30]); -assert(vector.get(1) == 20); -vector.set(1, 42); -assert(vector.get(1) == 42); -``` - -### 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) -``` - -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 -``` - -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) -``` - -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 -``` - -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); -``` diff --git a/docs/versioned_docs/version-v0.37.0/noir/standard_library/cryptographic_primitives/_category_.json b/docs/versioned_docs/version-v0.37.0/noir/standard_library/cryptographic_primitives/_category_.json deleted file mode 100644 index 5d694210bbf..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/standard_library/cryptographic_primitives/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "position": 0, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.37.0/noir/standard_library/cryptographic_primitives/ciphers.mdx b/docs/versioned_docs/version-v0.37.0/noir/standard_library/cryptographic_primitives/ciphers.mdx deleted file mode 100644 index d6a5e1a79eb..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/standard_library/cryptographic_primitives/ciphers.mdx +++ /dev/null @@ -1,32 +0,0 @@ ---- -title: Ciphers -description: - Learn about the implemented ciphers ready to use for any Noir project -keywords: - [ciphers, Noir project, aes128, encrypt] -sidebar_position: 0 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; - -## aes128 - -Given a plaintext as an array of bytes, returns the corresponding aes128 ciphertext (CBC mode). Input padding is automatically performed using PKCS#7, so that the output length is `input.len() + (16 - input.len() % 16)`. - -```rust title="aes128" showLineNumbers -pub fn aes128_encrypt(input: [u8; N], iv: [u8; 16], key: [u8; 16]) -> [u8] {} -``` -> Source code: noir_stdlib/src/aes128.nr#L2-L4 - - -```rust -fn main() { - let input: [u8; 4] = [0, 12, 3, 15] // Random bytes, will be padded to 16 bytes. - let iv: [u8; 16] = [0; 16]; // Initialisation vector - let key: [u8; 16] = [0; 16] // AES key - let ciphertext = std::aes128::aes128_encrypt(inputs.as_bytes(), iv.as_bytes(), key.as_bytes()); // In this case, the output length will be 16 bytes. -} -``` - - - \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.37.0/noir/standard_library/cryptographic_primitives/ec_primitives.md b/docs/versioned_docs/version-v0.37.0/noir/standard_library/cryptographic_primitives/ec_primitives.md deleted file mode 100644 index f262d8160d6..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/standard_library/cryptographic_primitives/ec_primitives.md +++ /dev/null @@ -1,102 +0,0 @@ ---- -title: Elliptic Curve Primitives -keywords: [cryptographic primitives, Noir project] -sidebar_position: 4 ---- - -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/mod.nr`](https://github.com/noir-lang/noir/blob/master/noir_stdlib/src/ec/mod.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/mod.nr)). - -## Examples - -The -[ec_baby_jubjub test](https://github.com/noir-lang/noir/blob/master/test_programs/compile_success_empty/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 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.37.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx b/docs/versioned_docs/version-v0.37.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx deleted file mode 100644 index 8d96027b42c..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx +++ /dev/null @@ -1,98 +0,0 @@ ---- -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] -sidebar_position: 3 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; - -Noir supports ECDSA signatures verification over the secp256k1 and secp256r1 curves. - -## ecdsa_secp256k1::verify_signature - -Verifier for ECDSA Secp256k1 signatures. -See ecdsa_secp256k1::verify_signature_slice for a version that accepts slices directly. - -```rust title="ecdsa_secp256k1" showLineNumbers -pub fn verify_signature( - public_key_x: [u8; 32], - public_key_y: [u8; 32], - signature: [u8; 64], - message_hash: [u8; N], -) -> bool -``` -> Source code: noir_stdlib/src/ecdsa_secp256k1.nr#L2-L9 - - -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_secp256k1::verify_signature_slice - -Verifier for ECDSA Secp256k1 signatures where the message is a slice. - -```rust title="ecdsa_secp256k1_slice" showLineNumbers -pub fn verify_signature_slice( - public_key_x: [u8; 32], - public_key_y: [u8; 32], - signature: [u8; 64], - message_hash: [u8], -) -> bool -``` -> Source code: noir_stdlib/src/ecdsa_secp256k1.nr#L13-L20 - - - - -## ecdsa_secp256r1::verify_signature - -Verifier for ECDSA Secp256r1 signatures. -See ecdsa_secp256r1::verify_signature_slice for a version that accepts slices directly. - -```rust title="ecdsa_secp256r1" showLineNumbers -pub fn verify_signature( - public_key_x: [u8; 32], - public_key_y: [u8; 32], - signature: [u8; 64], - message_hash: [u8; N], -) -> bool -``` -> Source code: noir_stdlib/src/ecdsa_secp256r1.nr#L2-L9 - - -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); -} -``` - - - -## ecdsa_secp256r1::verify_signature - -Verifier for ECDSA Secp256r1 signatures where the message is a slice. - -```rust title="ecdsa_secp256r1_slice" showLineNumbers -pub fn verify_signature_slice( - public_key_x: [u8; 32], - public_key_y: [u8; 32], - signature: [u8; 64], - message_hash: [u8], -) -> bool -``` -> Source code: noir_stdlib/src/ecdsa_secp256r1.nr#L13-L20 - - - diff --git a/docs/versioned_docs/version-v0.37.0/noir/standard_library/cryptographic_primitives/eddsa.mdx b/docs/versioned_docs/version-v0.37.0/noir/standard_library/cryptographic_primitives/eddsa.mdx deleted file mode 100644 index b283de693c8..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/standard_library/cryptographic_primitives/eddsa.mdx +++ /dev/null @@ -1,37 +0,0 @@ ---- -title: EdDSA Verification -description: Learn about the cryptographic primitives regarding EdDSA -keywords: [cryptographic primitives, Noir project, eddsa, signatures] -sidebar_position: 5 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; - -## 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 -``` - -It is also possible to specify the hash algorithm used for the signature by using the `eddsa_verify` function by passing a type implementing the Hasher trait with the turbofish operator. -For instance, if you want to use Poseidon2 instead, you can do the following: -```rust -use std::hash::poseidon2::Poseidon2Hasher; - -eddsa_verify::(pub_key_a.x, pub_key_a.y, s_a, r8_a.x, r8_a.y, msg); -``` - - - -## eddsa::eddsa_to_pub - -Private to public key conversion. - -Returns `(pub_key_x, pub_key_y)` - -```rust -fn eddsa_to_pub(secret : Field) -> (Field, Field) -``` - diff --git a/docs/versioned_docs/version-v0.37.0/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx b/docs/versioned_docs/version-v0.37.0/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx deleted file mode 100644 index 482a36932b9..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx +++ /dev/null @@ -1,95 +0,0 @@ ---- -title: Scalar multiplication -description: See how you can perform scalar multiplication in Noir -keywords: [cryptographic primitives, Noir project, scalar multiplication] -sidebar_position: 1 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; - -The following functions perform operations over the embedded curve whose coordinates are defined by the configured noir field. -For the BN254 scalar field, this is BabyJubJub or Grumpkin. - -:::note -Suffixes `_low` and `_high` denote low and high limbs of a scalar. -::: - -## embedded_curve_ops::multi_scalar_mul - -Performs multi scalar multiplication over the embedded curve. -The function accepts arbitrary amount of point-scalar pairs on the input, it multiplies the individual pairs over -the curve and returns a sum of the resulting points. - -Points represented as x and y coordinates [x1, y1, x2, y2, ...], scalars as low and high limbs [low1, high1, low2, high2, ...]. - -```rust title="multi_scalar_mul" showLineNumbers -pub fn multi_scalar_mul( - points: [EmbeddedCurvePoint; N], - scalars: [EmbeddedCurveScalar; N], -) -> EmbeddedCurvePoint -``` -> Source code: noir_stdlib/src/embedded_curve_ops.nr#L103-L108 - - -example - -```rust -fn main(point_x: Field, point_y: Field, scalar_low: Field, scalar_high: Field) { - let point = std::embedded_curve_ops::multi_scalar_mul([point_x, point_y], [scalar_low, scalar_high]); - println(point); -} -``` - -## embedded_curve_ops::fixed_base_scalar_mul - -Performs fixed base scalar multiplication over the embedded curve (multiplies input scalar with a generator point). -The function accepts a single scalar on the input represented as 2 fields. - -```rust title="fixed_base_scalar_mul" showLineNumbers -pub fn fixed_base_scalar_mul(scalar: EmbeddedCurveScalar) -> EmbeddedCurvePoint -``` -> Source code: noir_stdlib/src/embedded_curve_ops.nr#L120-L122 - - -example - -```rust -fn main(scalar_low: Field, scalar_high: Field) { - let point = std::embedded_curve_ops::fixed_base_scalar_mul(scalar_low, scalar_high); - println(point); -} -``` - -## embedded_curve_ops::embedded_curve_add - -Adds two points on the embedded curve. -This function takes two `EmbeddedCurvePoint` structures as parameters, representing points on the curve, and returns a new `EmbeddedCurvePoint` structure that represents their sum. - -### Parameters: -- `point1` (`EmbeddedCurvePoint`): The first point to add. -- `point2` (`EmbeddedCurvePoint`): The second point to add. - -### Returns: -- `EmbeddedCurvePoint`: The resulting point after the addition of `point1` and `point2`. - -```rust title="embedded_curve_add" showLineNumbers -pub fn embedded_curve_add( - point1: EmbeddedCurvePoint, - point2: EmbeddedCurvePoint, -) -> EmbeddedCurvePoint { -``` -> Source code: noir_stdlib/src/embedded_curve_ops.nr#L136-L141 - - -example - -```rust -fn main() { - let point1 = EmbeddedCurvePoint { x: 1, y: 2 }; - let point2 = EmbeddedCurvePoint { x: 3, y: 4 }; - let result = std::embedded_curve_ops::embedded_curve_add(point1, point2); - println!("Resulting Point: ({}, {})", result.x, result.y); -} -``` - - diff --git a/docs/versioned_docs/version-v0.37.0/noir/standard_library/cryptographic_primitives/hashes.mdx b/docs/versioned_docs/version-v0.37.0/noir/standard_library/cryptographic_primitives/hashes.mdx deleted file mode 100644 index 541a1971561..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/standard_library/cryptographic_primitives/hashes.mdx +++ /dev/null @@ -1,227 +0,0 @@ ---- -title: Hash methods -description: - Learn about the cryptographic primitives ready to use for any Noir project, including sha256, - blake2s and pedersen -keywords: - [cryptographic primitives, Noir project, sha256, blake2s, pedersen, hash] -sidebar_position: 0 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; - -## sha256 - -Given an array of bytes, returns the resulting sha256 hash. -Specify a message_size to hash only the first `message_size` bytes of the input. - -```rust title="sha256" showLineNumbers -pub fn sha256(input: [u8; N]) -> HASH -``` -> Source code: noir_stdlib/src/hash/sha256.nr#L47-L49 - - -example: -```rust title="sha256_var" showLineNumbers -let digest = std::hash::sha256_var([x as u8], 1); -``` -> Source code: test_programs/execution_success/sha256/src/main.nr#L15-L17 - - -```rust -fn main() { - let x = [163, 117, 178, 149]; // some random bytes - let hash = std::sha256::sha256_var(x, 4); -} -``` - - - - -## blake2s - -Given an array of bytes, returns an array with the Blake2 hash - -```rust title="blake2s" showLineNumbers -pub fn blake2s(input: [u8; N]) -> [u8; 32] -``` -> Source code: noir_stdlib/src/hash/mod.nr#L18-L20 - - -example: - -```rust -fn main() { - let x = [163, 117, 178, 149]; // some random bytes - let hash = std::hash::blake2s(x); -} -``` - - - -## blake3 - -Given an array of bytes, returns an array with the Blake3 hash - -```rust title="blake3" showLineNumbers -pub fn blake3(input: [u8; N]) -> [u8; 32] -``` -> Source code: noir_stdlib/src/hash/mod.nr#L24-L26 - - -example: - -```rust -fn main() { - let x = [163, 117, 178, 149]; // some random bytes - let hash = std::hash::blake3(x); -} -``` - - - -## pedersen_hash - -Given an array of Fields, returns the Pedersen hash. - -```rust title="pedersen_hash" showLineNumbers -pub fn pedersen_hash(input: [Field; N]) -> Field -``` -> Source code: noir_stdlib/src/hash/mod.nr#L49-L51 - - -example: - -```rust title="pedersen-hash" showLineNumbers -fn main(x: Field, y: Field, expected_hash: Field) { - let hash = std::hash::pedersen_hash([x, y]); - assert_eq(hash, expected_hash); -} -``` -> Source code: test_programs/execution_success/pedersen_hash/src/main.nr#L1-L6 - - - - -## pedersen_commitment - -Given an array of Fields, returns the Pedersen commitment. - -```rust title="pedersen_commitment" showLineNumbers -pub fn pedersen_commitment(input: [Field; N]) -> EmbeddedCurvePoint { -``` -> Source code: noir_stdlib/src/hash/mod.nr#L29-L31 - - -example: - -```rust title="pedersen-commitment" showLineNumbers -fn main(x: Field, y: Field, expected_commitment: std::embedded_curve_ops::EmbeddedCurvePoint) { - let commitment = std::hash::pedersen_commitment([x, y]); - assert_eq(commitment.x, expected_commitment.x); - assert_eq(commitment.y, expected_commitment.y); -} -``` -> Source code: test_programs/execution_success/pedersen_commitment/src/main.nr#L1-L7 - - - - -## 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 title="keccak256" showLineNumbers -pub fn keccak256(input: [u8; N], message_size: u32) -> [u8; 32] -``` -> Source code: noir_stdlib/src/hash/mod.nr#L116-L118 - - -example: - -```rust title="keccak256" showLineNumbers -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 message 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); -} -``` -> Source code: test_programs/execution_success/keccak256/src/main.nr#L1-L20 - - - - -## 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 title="poseidon" showLineNumbers -use std::hash::poseidon; - -fn main(x1: [Field; 2], y1: pub Field, x2: [Field; 4], y2: pub Field) { - let hash1 = poseidon::bn254::hash_2(x1); - assert(hash1 == y1); - - let hash2 = poseidon::bn254::hash_4(x2); - assert(hash2 == y2); -} -``` -> Source code: test_programs/execution_success/poseidon_bn254_hash/src/main.nr#L1-L11 - - -## poseidon 2 - -Given an array of Fields, returns a new Field with the Poseidon2 Hash. Contrary to the Poseidon -function, there is only one hash and you can specify a message_size to hash only the first -`message_size` bytes of the input, - -```rust -// example for hashing the first three elements of the input -Poseidon2::hash(input, 3); -``` - -example: - -```rust title="poseidon2" showLineNumbers -use std::hash::poseidon2; - -fn main(inputs: [Field; 4], expected_hash: Field) { - let hash = poseidon2::Poseidon2::hash(inputs, inputs.len()); - assert_eq(hash, expected_hash); -} -``` -> Source code: test_programs/execution_success/poseidon2/src/main.nr#L1-L8 - - -## hash_to_field - -```rust -fn hash_to_field(_input : [Field]) -> 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.37.0/noir/standard_library/cryptographic_primitives/index.md b/docs/versioned_docs/version-v0.37.0/noir/standard_library/cryptographic_primitives/index.md deleted file mode 100644 index 650f30165d5..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/standard_library/cryptographic_primitives/index.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -title: Cryptographic Primitives -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.37.0/noir/standard_library/cryptographic_primitives/schnorr.mdx b/docs/versioned_docs/version-v0.37.0/noir/standard_library/cryptographic_primitives/schnorr.mdx deleted file mode 100644 index 030452645c5..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/standard_library/cryptographic_primitives/schnorr.mdx +++ /dev/null @@ -1,64 +0,0 @@ ---- -title: Schnorr Signatures -description: Learn how you can verify Schnorr signatures using Noir -keywords: [cryptographic primitives, Noir project, schnorr, signatures] -sidebar_position: 2 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; - -## schnorr::verify_signature - -Verifier for Schnorr signatures over the embedded curve (for BN254 it is Grumpkin). -See schnorr::verify_signature_slice for a version that works directly on slices. - -```rust title="schnorr_verify" showLineNumbers -pub fn verify_signature( - public_key_x: Field, - public_key_y: Field, - signature: [u8; 64], - message: [u8; N], -) -> bool -``` -> Source code: noir_stdlib/src/schnorr.nr#L4-L11 - - -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() -); - -... -``` - - - -## schnorr::verify_signature_slice - -Verifier for Schnorr signatures over the embedded curve (for BN254 it is Grumpkin) -where the message is a slice. - -```rust title="schnorr_verify_slice" showLineNumbers -pub fn verify_signature_slice( - public_key_x: Field, - public_key_y: Field, - signature: [u8; 64], - message: [u8], -) -> bool -``` -> Source code: noir_stdlib/src/schnorr.nr#L15-L22 - - - diff --git a/docs/versioned_docs/version-v0.37.0/noir/standard_library/fmtstr.md b/docs/versioned_docs/version-v0.37.0/noir/standard_library/fmtstr.md deleted file mode 100644 index 19809d60261..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/standard_library/fmtstr.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -title: fmtstr ---- - -`fmtstr` is the type resulting from using format string (`f"..."`). - -## Methods - -### quoted_contents - -```rust title="quoted_contents" showLineNumbers -pub comptime fn quoted_contents(self) -> Quoted {} -``` -> Source code: noir_stdlib/src/meta/format_string.nr#L3-L5 - - -Returns the format string contents (that is, without the leading and trailing double quotes) as a `Quoted` value. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.37.0/noir/standard_library/is_unconstrained.md b/docs/versioned_docs/version-v0.37.0/noir/standard_library/is_unconstrained.md deleted file mode 100644 index 51bb1bda8f1..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/standard_library/is_unconstrained.md +++ /dev/null @@ -1,69 +0,0 @@ ---- -title: Is Unconstrained Function -description: - The is_unconstrained function returns wether the context at that point of the program is unconstrained or not. -keywords: - [ - unconstrained - ] ---- - -It's very common for functions in circuits to take unconstrained hints of an expensive computation and then verify it. This is done by running the hint in an unconstrained context and then verifying the result in a constrained context. - -When a function is marked as unconstrained, any subsequent functions that it calls will also be run in an unconstrained context. However, if we are implementing a library function, other users might call it within an unconstrained context or a constrained one. Generally, in an unconstrained context we prefer just computing the result instead of taking a hint of it and verifying it, since that'd mean doing the same computation twice: - -```rust - -fn my_expensive_computation(){ - ... -} - -unconstrained fn my_expensive_computation_hint(){ - my_expensive_computation() -} - -pub fn external_interface(){ - my_expensive_computation_hint(); - // verify my_expensive_computation: If external_interface is called from unconstrained, this is redundant - ... -} - -``` - -In order to improve the performance in an unconstrained context you can use the function at `std::runtime::is_unconstrained() -> bool`: - - -```rust -use dep::std::runtime::is_unconstrained; - -fn my_expensive_computation(){ - ... -} - -unconstrained fn my_expensive_computation_hint(){ - my_expensive_computation() -} - -pub fn external_interface(){ - if is_unconstrained() { - my_expensive_computation(); - } else { - my_expensive_computation_hint(); - // verify my_expensive_computation - ... - } -} - -``` - -The is_unconstrained result is resolved at compile time, so in unconstrained contexts the compiler removes the else branch, and in constrained contexts the compiler removes the if branch, reducing the amount of compute necessary to run external_interface. - -Note that using `is_unconstrained` in a `comptime` context will also return `true`: - -``` -fn main() { - comptime { - assert(is_unconstrained()); - } -} -``` diff --git a/docs/versioned_docs/version-v0.37.0/noir/standard_library/logging.md b/docs/versioned_docs/version-v0.37.0/noir/standard_library/logging.md deleted file mode 100644 index db75ef9f86f..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/standard_library/logging.md +++ /dev/null @@ -1,78 +0,0 @@ ---- -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, - print statement, - debugging in noir, - noir std library, - logging tutorial, - basic logging in noir, - noir logging implementation, - noir debugging techniques, - rust, - ] ---- - -The standard library provides two familiar statements you can use: `println` and `print`. Despite being a limited implementation of rust's `println!` and `print!` macros, these constructs can be useful for debugging. - -You can print the output of both statements in your Noir code by using the `nargo execute` command or the `--show-output` flag when using `nargo test` (provided there are print statements in your tests). - -It is recommended to use `nargo execute` if you want to debug failing constraints with `println` or `print` 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). Neither `println`, nor `print` are callable for failed constraints caught at compile time. - -Both `print` and `println` are generic functions which can work on integers, fields, strings, and even structs or expressions. Note however, that slices are currently unsupported. For example: - -```rust -struct Person { - age: Field, - height: Field, -} - -fn main(age: Field, height: Field) { - let person = Person { - age: age, - height: height, - }; - println(person); - println(age + height); - println("Hello world!"); -} -``` - -You can print different types in the same statement (including strings) with a type called `fmtstr`. It can be specified in the same way as a normal string, just prepended with an "f" character: - -```rust - let fmt_str = f"i: {i}, j: {j}"; - println(fmt_str); - - let s = myStruct { y: x, x: y }; - println(s); - - println(f"i: {i}, s: {s}"); - - println(x); - println([x, y]); - - let foo = fooStruct { my_struct: s, foo: 15 }; - println(f"s: {s}, foo: {foo}"); - - println(15); // prints 0x0f, implicit Field - println(-1 as u8); // prints 255 - println(-1 as i8); // prints -1 -``` - -Examples shown above are interchangeable between the two `print` statements: - -```rust -let person = Person { age : age, height : height }; - -println(person); -print(person); - -println("Hello world!"); // Prints with a newline at the end of the input -print("Hello world!"); // Prints the input and keeps cursor on the same line -``` diff --git a/docs/versioned_docs/version-v0.37.0/noir/standard_library/mem.md b/docs/versioned_docs/version-v0.37.0/noir/standard_library/mem.md deleted file mode 100644 index 95d36ac2a72..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/standard_library/mem.md +++ /dev/null @@ -1,52 +0,0 @@ ---- -title: Memory Module -description: - This module contains functions which manipulate memory in a low-level way -keywords: - [ - mem, memory, zeroed, transmute, checked_transmute - ] ---- - -# `std::mem::zeroed` - -```rust -fn zeroed() -> T -``` - -Returns 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. - -This function currently supports the following types: - -- Field -- Bool -- Uint -- Array -- Slice -- String -- Tuple -- Functions - -Using it on other types could result in unexpected behavior. - -# `std::mem::checked_transmute` - -```rust -fn checked_transmute(value: T) -> U -``` - -Transmutes a value of one type into the same value but with a new type `U`. - -This function is safe to use since both types are asserted to be equal later during compilation after the concrete values for generic types become known. -This function is useful for cases where the compiler may fails a type check that is expected to pass where -a user knows the two types to be equal. For example, when using arithmetic generics there are cases the compiler -does not see as equal, such as `[Field; N*(A + B)]` and `[Field; N*A + N*B]`, which users may know to be equal. -In these cases, `checked_transmute` can be used to cast the value to the desired type while also preserving safety -by checking this equality once `N`, `A`, `B` are fully resolved. - -Note that since this safety check is performed after type checking rather than during, no error is issued if the function -containing `checked_transmute` is never called. diff --git a/docs/versioned_docs/version-v0.37.0/noir/standard_library/merkle_trees.md b/docs/versioned_docs/version-v0.37.0/noir/standard_library/merkle_trees.md deleted file mode 100644 index 6a9ebf72ada..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/standard_library/merkle_trees.md +++ /dev/null @@ -1,58 +0,0 @@ ---- -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/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.as_slice()); - 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.37.0/noir/standard_library/meta/ctstring.md b/docs/versioned_docs/version-v0.37.0/noir/standard_library/meta/ctstring.md deleted file mode 100644 index b76f873ca03..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/standard_library/meta/ctstring.md +++ /dev/null @@ -1,100 +0,0 @@ ---- -title: CtString ---- - -`std::meta::ctstring` contains methods on the built-in `CtString` type which is -a compile-time, dynamically-sized string type. Compared to `str` and `fmtstr`, -`CtString` is useful because its size does not need to be specified in its type. This -can be used for formatting items at compile-time or general string handling in `comptime` -code. - -Since `fmtstr`s can be converted into `CtString`s, you can make use of their formatting -abilities in CtStrings by formatting in `fmtstr`s then converting the result to a CtString -afterward. - -## Traits - -### AsCtString - -```rust title="as-ctstring" showLineNumbers -pub trait AsCtString { - comptime fn as_ctstring(self) -> CtString; -} -``` -> Source code: noir_stdlib/src/meta/ctstring.nr#L43-L47 - - -Converts an object into a compile-time string. - -Implementations: - -```rust -impl AsCtString for str { ... } -impl AsCtString for fmtstr { ... } -``` - -## Methods - -### new - -```rust title="new" showLineNumbers -pub comptime fn new() -> Self { -``` -> Source code: noir_stdlib/src/meta/ctstring.nr#L4-L6 - - -Creates an empty `CtString`. - -### append_str - -```rust title="append_str" showLineNumbers -pub comptime fn append_str(self, s: str) -> Self { -``` -> Source code: noir_stdlib/src/meta/ctstring.nr#L11-L13 - - -Returns a new CtString with the given str appended onto the end. - -### append_fmtstr - -```rust title="append_fmtstr" showLineNumbers -pub comptime fn append_fmtstr(self, s: fmtstr) -> Self { -``` -> Source code: noir_stdlib/src/meta/ctstring.nr#L17-L19 - - -Returns a new CtString with the given fmtstr appended onto the end. - -### as_quoted_str - -```rust title="as_quoted_str" showLineNumbers -pub comptime fn as_quoted_str(self) -> Quoted { -``` -> Source code: noir_stdlib/src/meta/ctstring.nr#L26-L28 - - -Returns a quoted string literal from this string's contents. - -There is no direct conversion from a `CtString` to a `str` since -the size would not be known. To get around this, this function can -be used in combination with macro insertion (`!`) to insert this string -literal at this function's call site. - -Example: - -```rust title="as_quoted_str_example" showLineNumbers -let my_ctstring = "foo bar".as_ctstring(); - let my_str = my_ctstring.as_quoted_str!(); - - assert_eq(crate::meta::type_of(my_str), quote { str<7> }.as_type()); -``` -> Source code: noir_stdlib/src/meta/ctstring.nr#L92-L97 - - -## Trait Implementations - -```rust -impl Eq for CtString -impl Hash for CtString -impl Append for CtString -``` diff --git a/docs/versioned_docs/version-v0.37.0/noir/standard_library/meta/expr.md b/docs/versioned_docs/version-v0.37.0/noir/standard_library/meta/expr.md deleted file mode 100644 index b6d395c6700..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/standard_library/meta/expr.md +++ /dev/null @@ -1,380 +0,0 @@ ---- -title: Expr ---- - -`std::meta::expr` contains methods on the built-in `Expr` type for quoted, syntactically valid expressions. - -## Methods - -### as_array - -```rust title="as_array" showLineNumbers -pub comptime fn as_array(self) -> Option<[Expr]> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L10-L12 - - -If this expression is an array, this returns a slice of each element in the array. - -### as_assert - -```rust title="as_assert" showLineNumbers -pub comptime fn as_assert(self) -> Option<(Expr, Option)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L16-L18 - - -If this expression is an assert, this returns the assert expression and the optional message. - -### as_assert_eq - -```rust title="as_assert_eq" showLineNumbers -pub comptime fn as_assert_eq(self) -> Option<(Expr, Expr, Option)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L23-L25 - - -If this expression is an assert_eq, this returns the left-hand-side and right-hand-side -expressions, together with the optional message. - -### as_assign - -```rust title="as_assign" showLineNumbers -pub comptime fn as_assign(self) -> Option<(Expr, Expr)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L30-L32 - - -If this expression is an assignment, this returns a tuple with the left hand side -and right hand side in order. - -### as_binary_op - -```rust title="as_binary_op" showLineNumbers -pub comptime fn as_binary_op(self) -> Option<(Expr, BinaryOp, Expr)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L37-L39 - - -If this expression is a binary operator operation ` `, -return the left-hand side, operator, and the right-hand side of the operation. - -### as_block - -```rust title="as_block" showLineNumbers -pub comptime fn as_block(self) -> Option<[Expr]> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L44-L46 - - -If this expression is a block `{ stmt1; stmt2; ...; stmtN }`, return -a slice containing each statement. - -### as_bool - -```rust title="as_bool" showLineNumbers -pub comptime fn as_bool(self) -> Option {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L50-L52 - - -If this expression is a boolean literal, return that literal. - -### as_cast - -```rust title="as_cast" showLineNumbers -#[builtin(expr_as_cast)] - pub comptime fn as_cast(self) -> Option<(Expr, UnresolvedType)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L56-L59 - - -If this expression is a cast expression (`expr as type`), returns the casted -expression and the type to cast to. - -### as_comptime - -```rust title="as_comptime" showLineNumbers -pub comptime fn as_comptime(self) -> Option<[Expr]> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L64-L66 - - -If this expression is a `comptime { stmt1; stmt2; ...; stmtN }` block, -return each statement in the block. - -### as_constructor - -```rust title="as_constructor" showLineNumbers -pub comptime fn as_constructor(self) -> Option<(UnresolvedType, [(Quoted, Expr)])> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L71-L73 - - -If this expression is a constructor `Type { field1: expr1, ..., fieldN: exprN }`, -return the type and the fields. - -### as_for - -```rust title="as_for" showLineNumbers -pub comptime fn as_for(self) -> Option<(Quoted, Expr, Expr)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L78-L80 - - -If this expression is a for statement over a single expression, return the identifier, -the expression and the for loop body. - -### as_for_range - -```rust title="as_for" showLineNumbers -pub comptime fn as_for(self) -> Option<(Quoted, Expr, Expr)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L78-L80 - - -If this expression is a for statement over a range, return the identifier, -the range start, the range end and the for loop body. - -### as_function_call - -```rust title="as_function_call" showLineNumbers -pub comptime fn as_function_call(self) -> Option<(Expr, [Expr])> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L92-L94 - - -If this expression is a function call `foo(arg1, ..., argN)`, return -the function and a slice of each argument. - -### as_if - -```rust title="as_if" showLineNumbers -pub comptime fn as_if(self) -> Option<(Expr, Expr, Option)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L100-L102 - - -If this expression is an `if condition { then_branch } else { else_branch }`, -return the condition, then branch, and else branch. If there is no else branch, -`None` is returned for that branch instead. - -### as_index - -```rust title="as_index" showLineNumbers -pub comptime fn as_index(self) -> Option<(Expr, Expr)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L107-L109 - - -If this expression is an index into an array `array[index]`, return the -array and the index. - -### as_integer - -```rust title="as_integer" showLineNumbers -pub comptime fn as_integer(self) -> Option<(Field, bool)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L114-L116 - - -If this expression is an integer literal, return the integer as a field -as well as whether the integer is negative (true) or not (false). - -### as_lambda - -```rust title="as_lambda" showLineNumbers -pub comptime fn as_lambda( - self, - ) -> Option<([(Expr, Option)], Option, Expr)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L120-L124 - - -If this expression is a lambda, returns the parameters, return type and body. - -### as_let - -```rust title="as_let" showLineNumbers -pub comptime fn as_let(self) -> Option<(Expr, Option, Expr)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L129-L131 - - -If this expression is a let statement, returns the let pattern as an `Expr`, -the optional type annotation, and the assigned expression. - -### as_member_access - -```rust title="as_member_access" showLineNumbers -pub comptime fn as_member_access(self) -> Option<(Expr, Quoted)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L136-L138 - - -If this expression is a member access `foo.bar`, return the struct/tuple -expression and the field. The field will be represented as a quoted value. - -### as_method_call - -```rust title="as_method_call" showLineNumbers -pub comptime fn as_method_call(self) -> Option<(Expr, Quoted, [UnresolvedType], [Expr])> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L143-L145 - - -If this expression is a method call `foo.bar::(arg1, ..., argN)`, return -the receiver, method name, a slice of each generic argument, and a slice of each argument. - -### as_repeated_element_array - -```rust title="as_repeated_element_array" showLineNumbers -pub comptime fn as_repeated_element_array(self) -> Option<(Expr, Expr)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L150-L152 - - -If this expression is a repeated element array `[elem; length]`, return -the repeated element and the length expressions. - -### as_repeated_element_slice - -```rust title="as_repeated_element_slice" showLineNumbers -pub comptime fn as_repeated_element_slice(self) -> Option<(Expr, Expr)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L157-L159 - - -If this expression is a repeated element slice `[elem; length]`, return -the repeated element and the length expressions. - -### as_slice - -```rust title="as_slice" showLineNumbers -pub comptime fn as_slice(self) -> Option<[Expr]> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L164-L166 - - -If this expression is a slice literal `&[elem1, ..., elemN]`, -return each element of the slice. - -### as_tuple - -```rust title="as_tuple" showLineNumbers -pub comptime fn as_tuple(self) -> Option<[Expr]> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L171-L173 - - -If this expression is a tuple `(field1, ..., fieldN)`, -return each element of the tuple. - -### as_unary_op - -```rust title="as_unary_op" showLineNumbers -pub comptime fn as_unary_op(self) -> Option<(UnaryOp, Expr)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L178-L180 - - -If this expression is a unary operation ` `, -return the unary operator as well as the right-hand side expression. - -### as_unsafe - -```rust title="as_unsafe" showLineNumbers -pub comptime fn as_unsafe(self) -> Option<[Expr]> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L185-L187 - - -If this expression is an `unsafe { stmt1; ...; stmtN }` block, -return each statement inside in a slice. - -### has_semicolon - -```rust title="has_semicolon" showLineNumbers -pub comptime fn has_semicolon(self) -> bool {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L206-L208 - - -`true` if this expression is trailed by a semicolon. E.g. - -``` -comptime { - let expr1 = quote { 1 + 2 }.as_expr().unwrap(); - let expr2 = quote { 1 + 2; }.as_expr().unwrap(); - - assert(expr1.as_binary_op().is_some()); - assert(expr2.as_binary_op().is_some()); - - assert(!expr1.has_semicolon()); - assert(expr2.has_semicolon()); -} -``` - -### is_break - -```rust title="is_break" showLineNumbers -pub comptime fn is_break(self) -> bool {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L212-L214 - - -`true` if this expression is `break`. - -### is_continue - -```rust title="is_continue" showLineNumbers -pub comptime fn is_continue(self) -> bool {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L218-L220 - - -`true` if this expression is `continue`. - -### modify - -```rust title="modify" showLineNumbers -pub comptime fn modify(self, f: fn[Env](Expr) -> Option) -> Expr { -``` -> Source code: noir_stdlib/src/meta/expr.nr#L229-L231 - - -Applies a mapping function to this expression and to all of its sub-expressions. -`f` will be applied to each sub-expression first, then applied to the expression itself. - -This happens recursively for every expression within `self`. - -For example, calling `modify` on `(&[1], &[2, 3])` with an `f` that returns `Option::some` -for expressions that are integers, doubling them, would return `(&[2], &[4, 6])`. - -### quoted - -```rust title="quoted" showLineNumbers -pub comptime fn quoted(self) -> Quoted { -``` -> Source code: noir_stdlib/src/meta/expr.nr#L266-L268 - - -Returns this expression as a `Quoted` value. It's the same as `quote { $self }`. - -### resolve - -```rust title="resolve" showLineNumbers -pub comptime fn resolve(self, in_function: Option) -> TypedExpr {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L282-L284 - - -Resolves and type-checks this expression and returns the result as a `TypedExpr`. - -The `in_function` argument specifies where the expression is resolved: -- If it's `none`, the expression is resolved in the function where `resolve` was called -- If it's `some`, the expression is resolved in the given function - -If any names used by this expression are not in scope or if there are any type errors, -this will give compiler errors as if the expression was written directly into -the current `comptime` function. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.37.0/noir/standard_library/meta/function_def.md b/docs/versioned_docs/version-v0.37.0/noir/standard_library/meta/function_def.md deleted file mode 100644 index b7f2ebdb889..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/standard_library/meta/function_def.md +++ /dev/null @@ -1,166 +0,0 @@ ---- -title: FunctionDefinition ---- - -`std::meta::function_def` contains methods on the built-in `FunctionDefinition` type representing -a function definition in the source program. - -## Methods - -### add_attribute - -```rust title="add_attribute" showLineNumbers -pub comptime fn add_attribute(self, attribute: str) {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L3-L5 - - -Adds an attribute to the function. This is only valid -on functions in the current crate which have not yet been resolved. -This means any functions called at compile-time are invalid targets for this method. - -### body - -```rust title="body" showLineNumbers -pub comptime fn body(self) -> Expr {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L8-L10 - - -Returns the body of the function as an expression. This is only valid -on functions in the current crate which have not yet been resolved. -This means any functions called at compile-time are invalid targets for this method. - -### has_named_attribute - -```rust title="has_named_attribute" showLineNumbers -pub comptime fn has_named_attribute(self, name: str) -> bool {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L13-L15 - - -Returns true if this function has a custom attribute with the given name. - -### is_unconstrained - -```rust title="is_unconstrained" showLineNumbers -pub comptime fn is_unconstrained(self) -> bool {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L18-L20 - - -Returns true if this function is unconstrained. - -### module - -```rust title="module" showLineNumbers -pub comptime fn module(self) -> Module {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L23-L25 - - -Returns the module where the function is defined. - -### name - -```rust title="name" showLineNumbers -pub comptime fn name(self) -> Quoted {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L28-L30 - - -Returns the name of the function. - -### parameters - -```rust title="parameters" showLineNumbers -pub comptime fn parameters(self) -> [(Quoted, Type)] {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L33-L35 - - -Returns each parameter of the function as a tuple of (parameter pattern, parameter type). - -### return_type - -```rust title="return_type" showLineNumbers -pub comptime fn return_type(self) -> Type {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L38-L40 - - -The return type of the function. - -### set_body - -```rust title="set_body" showLineNumbers -pub comptime fn set_body(self, body: Expr) {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L43-L45 - - -Mutate the function body to a new expression. This is only valid -on functions in the current crate which have not yet been resolved. -This means any functions called at compile-time are invalid targets for this method. - -### set_parameters - -```rust title="set_parameters" showLineNumbers -pub comptime fn set_parameters(self, parameters: [(Quoted, Type)]) {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L48-L50 - - -Mutates the function's parameters to a new set of parameters. This is only valid -on functions in the current crate which have not yet been resolved. -This means any functions called at compile-time are invalid targets for this method. - -Expects a slice of (parameter pattern, parameter type) for each parameter. Requires -each parameter pattern to be a syntactically valid parameter. - -### set_return_type - -```rust title="set_return_type" showLineNumbers -pub comptime fn set_return_type(self, return_type: Type) {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L53-L55 - - -Mutates the function's return type to a new type. This is only valid -on functions in the current crate which have not yet been resolved. -This means any functions called at compile-time are invalid targets for this method. - -### set_return_public - -```rust title="set_return_public" showLineNumbers -pub comptime fn set_return_public(self, public: bool) {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L58-L60 - - -Mutates the function's return visibility to public (if `true` is given) or private (if `false` is given). -This is only valid on functions in the current crate which have not yet been resolved. -This means any functions called at compile-time are invalid targets for this method. - -### set_unconstrained - -```rust title="set_unconstrained" showLineNumbers -pub comptime fn set_unconstrained(self, value: bool) {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L66-L68 - - -Mutates the function to be unconstrained (if `true` is given) or not (if `false` is given). -This is only valid on functions in the current crate which have not yet been resolved. -This means any functions called at compile-time are invalid targets for this method. - -## Trait Implementations - -```rust -impl Eq for FunctionDefinition -impl Hash for FunctionDefinition -``` - -Note that each function is assigned a unique ID internally and this is what is used for -equality and hashing. So even functions with identical signatures and bodies may not -be equal in this sense if they were originally different items in the source program. diff --git a/docs/versioned_docs/version-v0.37.0/noir/standard_library/meta/index.md b/docs/versioned_docs/version-v0.37.0/noir/standard_library/meta/index.md deleted file mode 100644 index 14544c07442..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/standard_library/meta/index.md +++ /dev/null @@ -1,224 +0,0 @@ ---- -title: Metaprogramming -description: Noir's Metaprogramming API -keywords: [metaprogramming, comptime, macros, macro, quote, unquote] ---- - -`std::meta` is the entry point for Noir's metaprogramming API. This consists of `comptime` functions -and types used for inspecting and modifying Noir programs. - -## Functions - -### type_of - -```rust title="type_of" showLineNumbers -pub comptime fn type_of(x: T) -> Type {} -``` -> Source code: noir_stdlib/src/meta/mod.nr#L27-L29 - - -Returns the type of a variable at compile-time. - -Example: -```rust -comptime { - let x: i32 = 1; - let x_type: Type = std::meta::type_of(x); - - assert_eq(x_type, quote { i32 }.as_type()); -} -``` - -### unquote - -```rust title="unquote" showLineNumbers -pub comptime fn unquote(code: Quoted) -> Quoted { -``` -> Source code: noir_stdlib/src/meta/mod.nr#L19-L21 - - -Unquotes the passed-in token stream where this function was called. - -Example: -```rust -comptime { - let code = quote { 1 + 2 }; - - // let x = 1 + 2; - let x = unquote!(code); -} -``` - -### derive - -```rust title="derive" showLineNumbers -#[varargs] -pub comptime fn derive(s: StructDefinition, traits: [TraitDefinition]) -> Quoted { -``` -> Source code: noir_stdlib/src/meta/mod.nr#L48-L51 - - -Attribute placed on struct definitions. - -Creates a trait impl for each trait passed in as an argument. -To do this, the trait must have a derive handler registered -with `derive_via` beforehand. The traits in the stdlib that -can be derived this way are `Eq`, `Ord`, `Default`, and `Hash`. - -Example: -```rust -#[derive(Eq, Default)] -struct Foo { - x: i32, - y: T, -} - -fn main() { - let foo1 = Foo::default(); - let foo2 = Foo { x: 0, y: &[0] }; - assert_eq(foo1, foo2); -} -``` - -### derive_via - -```rust title="derive_via_signature" showLineNumbers -pub comptime fn derive_via(t: TraitDefinition, f: DeriveFunction) { -``` -> Source code: noir_stdlib/src/meta/mod.nr#L68-L70 - - -Attribute placed on trait definitions. - -Registers a function to create impls for the given trait -when the trait is used in a `derive` call. Users may use -this to register their own functions to enable their traits -to be derived by `derive`. - -Because this function requires a function as an argument which -should produce a trait impl for any given struct, users may find -it helpful to use a function like `std::meta::make_trait_impl` to -help creating these impls. - -Example: -```rust -#[derive_via(derive_do_nothing)] -trait DoNothing { - fn do_nothing(self); -} - -comptime fn derive_do_nothing(s: StructDefinition) -> Quoted { - let typ = s.as_type(); - quote { - impl DoNothing for $typ { - fn do_nothing(self) { - println("Nothing"); - } - } - } -} -``` - -As another example, `derive_eq` in the stdlib is used to derive the `Eq` -trait for any struct. It makes use of `make_trait_impl` to do this: - -```rust title="derive_eq" showLineNumbers -comptime fn derive_eq(s: StructDefinition) -> Quoted { - let signature = quote { fn eq(_self: Self, _other: Self) -> bool }; - let for_each_field = |name| quote { (_self.$name == _other.$name) }; - let body = |fields| { - if s.fields().len() == 0 { - quote { true } - } else { - fields - } - }; - crate::meta::make_trait_impl( - s, - quote { Eq }, - signature, - for_each_field, - quote { & }, - body, - ) -} -``` -> Source code: noir_stdlib/src/cmp.nr#L10-L30 - - -### make_trait_impl - -```rust title="make_trait_impl" showLineNumbers -pub comptime fn make_trait_impl( - s: StructDefinition, - trait_name: Quoted, - function_signature: Quoted, - for_each_field: fn[Env1](Quoted) -> Quoted, - join_fields_with: Quoted, - body: fn[Env2](Quoted) -> Quoted, -) -> Quoted { -``` -> Source code: noir_stdlib/src/meta/mod.nr#L87-L96 - - -A helper function to more easily create trait impls while deriving traits. - -Note that this function only works for traits which: -1. Have only one method -2. Have no generics on the trait itself. - - E.g. Using this on a trait such as `trait Foo { ... }` will result in the - generated impl incorrectly missing the `T` generic. - -If your trait fits these criteria then `make_trait_impl` is likely the easiest -way to write your derive handler. The arguments are as follows: - -- `s`: The struct to make the impl for -- `trait_name`: The name of the trait to derive. E.g. `quote { Eq }`. -- `function_signature`: The signature of the trait method to derive. E.g. `fn eq(self, other: Self) -> bool`. -- `for_each_field`: An operation to be performed on each field. E.g. `|name| quote { (self.$name == other.$name) }`. -- `join_fields_with`: A separator to join each result of `for_each_field` with. - E.g. `quote { & }`. You can also use an empty `quote {}` for no separator. -- `body`: The result of the field operations are passed into this function for any final processing. - This is the place to insert any setup/teardown code the trait requires. If the trait doesn't require - any such code, you can return the body as-is: `|body| body`. - -Example deriving `Hash`: - -```rust title="derive_hash" showLineNumbers -comptime fn derive_hash(s: StructDefinition) -> Quoted { - let name = quote { Hash }; - let signature = quote { fn hash(_self: Self, _state: &mut H) where H: std::hash::Hasher }; - let for_each_field = |name| quote { _self.$name.hash(_state); }; - crate::meta::make_trait_impl( - s, - name, - signature, - for_each_field, - quote {}, - |fields| fields, - ) -} -``` -> Source code: noir_stdlib/src/hash/mod.nr#L137-L151 - - -Example deriving `Ord`: - -```rust title="derive_ord" showLineNumbers -comptime fn derive_ord(s: StructDefinition) -> Quoted { - let signature = quote { fn cmp(_self: Self, _other: Self) -> std::cmp::Ordering }; - let for_each_field = |name| quote { - if result == std::cmp::Ordering::equal() { - result = _self.$name.cmp(_other.$name); - } - }; - let body = |fields| quote { - let mut result = std::cmp::Ordering::equal(); - $fields - result - }; - crate::meta::make_trait_impl(s, quote { Ord }, signature, for_each_field, quote {}, body) -} -``` -> Source code: noir_stdlib/src/cmp.nr#L216-L231 - diff --git a/docs/versioned_docs/version-v0.37.0/noir/standard_library/meta/module.md b/docs/versioned_docs/version-v0.37.0/noir/standard_library/meta/module.md deleted file mode 100644 index f47231972b7..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/standard_library/meta/module.md +++ /dev/null @@ -1,82 +0,0 @@ ---- -title: Module ---- - -`std::meta::module` contains methods on the built-in `Module` type which represents a module in the source program. -Note that this type represents a module generally, it isn't limited to only `mod my_submodule { ... }` -declarations in the source program. - -## Methods - -### add_item - -```rust title="add_item" showLineNumbers -pub comptime fn add_item(self, item: Quoted) {} -``` -> Source code: noir_stdlib/src/meta/module.nr#L3-L5 - - -Adds a top-level item (a function, a struct, a global, etc.) to the module. -Adding multiple items in one go is also valid if the `Quoted` value has multiple items in it. -Note that the items are type-checked as if they are inside the module they are being added to. - -### functions - -```rust title="functions" showLineNumbers -pub comptime fn functions(self) -> [FunctionDefinition] {} -``` -> Source code: noir_stdlib/src/meta/module.nr#L18-L20 - - -Returns each function defined in the module. - -### has_named_attribute - -```rust title="has_named_attribute" showLineNumbers -pub comptime fn has_named_attribute(self, name: str) -> bool {} -``` -> Source code: noir_stdlib/src/meta/module.nr#L8-L10 - - -Returns true if this module has a custom attribute with the given name. - -### is_contract - -```rust title="is_contract" showLineNumbers -pub comptime fn is_contract(self) -> bool {} -``` -> Source code: noir_stdlib/src/meta/module.nr#L13-L15 - - -`true` if this module is a contract module (was declared via `contract foo { ... }`). - -### name - -```rust title="name" showLineNumbers -pub comptime fn name(self) -> Quoted {} -``` -> Source code: noir_stdlib/src/meta/module.nr#L28-L30 - - -Returns the name of the module. - -### structs - -```rust title="structs" showLineNumbers -pub comptime fn structs(self) -> [StructDefinition] {} -``` -> Source code: noir_stdlib/src/meta/module.nr#L23-L25 - - -Returns each struct defined in the module. - -## Trait Implementations - -```rust -impl Eq for Module -impl Hash for Module -``` - -Note that each module is assigned a unique ID internally and this is what is used for -equality and hashing. So even modules with identical names and contents may not -be equal in this sense if they were originally different items in the source program. diff --git a/docs/versioned_docs/version-v0.37.0/noir/standard_library/meta/op.md b/docs/versioned_docs/version-v0.37.0/noir/standard_library/meta/op.md deleted file mode 100644 index 03ea49ad8ec..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/standard_library/meta/op.md +++ /dev/null @@ -1,244 +0,0 @@ ---- -title: UnaryOp and BinaryOp ---- - -`std::meta::op` contains the `UnaryOp` and `BinaryOp` types as well as methods on them. -These types are used to represent a unary or binary operator respectively in Noir source code. - -## Types - -### UnaryOp - -Represents a unary operator. One of `-`, `!`, `&mut`, or `*`. - -### Methods - -#### is_minus - -```rust title="is_minus" showLineNumbers -pub fn is_minus(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L24-L26 - - -Returns `true` if this operator is `-`. - -#### is_not - -```rust title="is_not" showLineNumbers -pub fn is_not(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L30-L32 - - -`true` if this operator is `!` - -#### is_mutable_reference - -```rust title="is_mutable_reference" showLineNumbers -pub fn is_mutable_reference(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L36-L38 - - -`true` if this operator is `&mut` - -#### is_dereference - -```rust title="is_dereference" showLineNumbers -pub fn is_dereference(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L42-L44 - - -`true` if this operator is `*` - -#### quoted - -```rust title="unary_quoted" showLineNumbers -pub comptime fn quoted(self) -> Quoted { -``` -> Source code: noir_stdlib/src/meta/op.nr#L48-L50 - - -Returns this operator as a `Quoted` value. - -### Trait Implementations - -```rust -impl Eq for UnaryOp -impl Hash for UnaryOp -``` - -### BinaryOp - -Represents a binary operator. One of `+`, `-`, `*`, `/`, `%`, `==`, `!=`, `<`, `<=`, `>`, `>=`, `&`, `|`, `^`, `>>`, or `<<`. - -### Methods - -#### is_add - -```rust title="is_add" showLineNumbers -pub fn is_add(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L86-L88 - - -`true` if this operator is `+` - -#### is_subtract - -```rust title="is_subtract" showLineNumbers -pub fn is_subtract(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L92-L94 - - -`true` if this operator is `-` - -#### is_multiply - -```rust title="is_multiply" showLineNumbers -pub fn is_multiply(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L98-L100 - - -`true` if this operator is `*` - -#### is_divide - -```rust title="is_divide" showLineNumbers -pub fn is_divide(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L104-L106 - - -`true` if this operator is `/` - -#### is_modulo - -```rust title="is_modulo" showLineNumbers -pub fn is_modulo(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L176-L178 - - -`true` if this operator is `%` - -#### is_equal - -```rust title="is_equal" showLineNumbers -pub fn is_equal(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L110-L112 - - -`true` if this operator is `==` - -#### is_not_equal - -```rust title="is_not_equal" showLineNumbers -pub fn is_not_equal(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L116-L118 - - -`true` if this operator is `!=` - -#### is_less_than - -```rust title="is_less_than" showLineNumbers -pub fn is_less_than(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L122-L124 - - -`true` if this operator is `<` - -#### is_less_than_or_equal - -```rust title="is_less_than_or_equal" showLineNumbers -pub fn is_less_than_or_equal(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L128-L130 - - -`true` if this operator is `<=` - -#### is_greater_than - -```rust title="is_greater_than" showLineNumbers -pub fn is_greater_than(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L134-L136 - - -`true` if this operator is `>` - -#### is_greater_than_or_equal - -```rust title="is_greater_than_or_equal" showLineNumbers -pub fn is_greater_than_or_equal(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L140-L142 - - -`true` if this operator is `>=` - -#### is_and - -```rust title="is_and" showLineNumbers -pub fn is_and(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L146-L148 - - -`true` if this operator is `&` - -#### is_or - -```rust title="is_or" showLineNumbers -pub fn is_or(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L152-L154 - - -`true` if this operator is `|` - -#### is_shift_right - -```rust title="is_shift_right" showLineNumbers -pub fn is_shift_right(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L164-L166 - - -`true` if this operator is `>>` - -#### is_shift_left - -```rust title="is_shift_right" showLineNumbers -pub fn is_shift_right(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L164-L166 - - -`true` if this operator is `<<` - -#### quoted - -```rust title="binary_quoted" showLineNumbers -pub comptime fn quoted(self) -> Quoted { -``` -> Source code: noir_stdlib/src/meta/op.nr#L182-L184 - - -Returns this operator as a `Quoted` value. - -### Trait Implementations - -```rust -impl Eq for BinaryOp -impl Hash for BinaryOp -``` diff --git a/docs/versioned_docs/version-v0.37.0/noir/standard_library/meta/quoted.md b/docs/versioned_docs/version-v0.37.0/noir/standard_library/meta/quoted.md deleted file mode 100644 index d7acf23bc07..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/standard_library/meta/quoted.md +++ /dev/null @@ -1,141 +0,0 @@ ---- -title: Quoted ---- - -`std::meta::quoted` contains methods on the built-in `Quoted` type which represents -quoted token streams and is the result of the `quote { ... }` expression. - -## Methods - -### as_expr - -```rust title="as_expr" showLineNumbers -pub comptime fn as_expr(self) -> Option {} -``` -> Source code: noir_stdlib/src/meta/quoted.nr#L6-L8 - - -Parses the quoted token stream as an expression. Returns `Option::none()` if -the expression failed to parse. - -Example: - -```rust title="as_expr_example" showLineNumbers -#[test] - fn test_expr_as_function_call() { - comptime - { - let expr = quote { foo(42) }.as_expr().unwrap(); - let (_function, args) = expr.as_function_call().unwrap(); - assert_eq(args.len(), 1); - assert_eq(args[0].as_integer().unwrap(), (42, false)); - } - } -``` -> Source code: test_programs/noir_test_success/comptime_expr/src/main.nr#L360-L371 - - -### as_module - -```rust title="as_module" showLineNumbers -pub comptime fn as_module(self) -> Option {} -``` -> Source code: noir_stdlib/src/meta/quoted.nr#L11-L13 - - -Interprets this token stream as a module path leading to the name of a module. -Returns `Option::none()` if the module isn't found or this token stream cannot be parsed as a path. - -Example: - -```rust title="as_module_example" showLineNumbers -mod baz { - pub mod qux {} -} - -#[test] -fn as_module_test() { - comptime { - let my_mod = quote { baz::qux }.as_module().unwrap(); - assert_eq(my_mod.name(), quote { qux }); - } -} -``` -> Source code: test_programs/compile_success_empty/comptime_module/src/main.nr#L115-L127 - - -### as_trait_constraint - -```rust title="as_trait_constraint" showLineNumbers -pub comptime fn as_trait_constraint(self) -> TraitConstraint {} -``` -> Source code: noir_stdlib/src/meta/quoted.nr#L16-L18 - - -Interprets this token stream as a trait constraint (without an object type). -Note that this function panics instead of returning `Option::none()` if the token -stream does not parse and resolve to a valid trait constraint. - -Example: - -```rust title="implements_example" showLineNumbers -pub fn function_with_where(_x: T) -where - T: SomeTrait, -{ - comptime { - let t = quote { T }.as_type(); - let some_trait_i32 = quote { SomeTrait }.as_trait_constraint(); - assert(t.implements(some_trait_i32)); - - assert(t.get_trait_impl(some_trait_i32).is_none()); - } -} -``` -> Source code: test_programs/compile_success_empty/comptime_type/src/main.nr#L160-L173 - - -### as_type - -```rust title="as_type" showLineNumbers -pub comptime fn as_type(self) -> Type {} -``` -> Source code: noir_stdlib/src/meta/quoted.nr#L21-L23 - - -Interprets this token stream as a resolved type. Panics if the token -stream doesn't parse to a type or if the type isn't a valid type in scope. - -```rust title="implements_example" showLineNumbers -pub fn function_with_where(_x: T) -where - T: SomeTrait, -{ - comptime { - let t = quote { T }.as_type(); - let some_trait_i32 = quote { SomeTrait }.as_trait_constraint(); - assert(t.implements(some_trait_i32)); - - assert(t.get_trait_impl(some_trait_i32).is_none()); - } -} -``` -> Source code: test_programs/compile_success_empty/comptime_type/src/main.nr#L160-L173 - - -### tokens - -```rust title="tokens" showLineNumbers -pub comptime fn tokens(self) -> [Quoted] {} -``` -> Source code: noir_stdlib/src/meta/quoted.nr#L26-L28 - - -Returns a slice of the individual tokens that form this token stream. - -## Trait Implementations - -```rust -impl Eq for Quoted -impl Hash for Quoted -``` diff --git a/docs/versioned_docs/version-v0.37.0/noir/standard_library/meta/struct_def.md b/docs/versioned_docs/version-v0.37.0/noir/standard_library/meta/struct_def.md deleted file mode 100644 index fd609942f4e..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/standard_library/meta/struct_def.md +++ /dev/null @@ -1,177 +0,0 @@ ---- -title: StructDefinition ---- - -`std::meta::struct_def` contains methods on the built-in `StructDefinition` type. -This type corresponds to `struct Name { field1: Type1, ... }` items in the source program. - -## Methods - -### add_attribute - -```rust title="add_attribute" showLineNumbers -pub comptime fn add_attribute(self, attribute: str) {} -``` -> Source code: noir_stdlib/src/meta/struct_def.nr#L3-L5 - - -Adds an attribute to the struct. - -### add_generic - -```rust title="add_generic" showLineNumbers -pub comptime fn add_generic(self, generic_name: str) -> Type {} -``` -> Source code: noir_stdlib/src/meta/struct_def.nr#L8-L10 - - -Adds an generic to the struct. Returns the new generic type. -Errors if the given generic name isn't a single identifier or if -the struct already has a generic with the same name. - -This method should be used carefully, if there is existing code referring -to the struct type it may be checked before this function is called and -see the struct with the original number of generics. This method should -thus be preferred to use on code generated from other macros and structs -that are not used in function signatures. - -Example: - -```rust title="add-generic-example" showLineNumbers -comptime fn add_generic(s: StructDefinition) { - assert_eq(s.generics().len(), 0); - let new_generic = s.add_generic("T"); - - let generics = s.generics(); - assert_eq(generics.len(), 1); - assert_eq(generics[0], new_generic); - } -``` -> Source code: test_programs/compile_success_empty/comptime_struct_definition/src/main.nr#L35-L44 - - -### as_type - -```rust title="as_type" showLineNumbers -pub comptime fn as_type(self) -> Type {} -``` -> Source code: noir_stdlib/src/meta/struct_def.nr#L15-L17 - - -Returns this struct as a type in the source program. If this struct has -any generics, the generics are also included as-is. - -### generics - -```rust title="generics" showLineNumbers -pub comptime fn generics(self) -> [Type] {} -``` -> Source code: noir_stdlib/src/meta/struct_def.nr#L26-L28 - - -Returns each generic on this struct. - -Example: - -``` -#[example] -struct Foo { - bar: [T; 2], - baz: Baz, -} - -comptime fn example(foo: StructDefinition) { - assert_eq(foo.generics().len(), 2); - - // Fails because `T` isn't in scope - // let t = quote { T }.as_type(); - // assert_eq(foo.generics()[0], t); -} -``` - -### fields - -```rust title="fields" showLineNumbers -pub comptime fn fields(self) -> [(Quoted, Type)] {} -``` -> Source code: noir_stdlib/src/meta/struct_def.nr#L33-L35 - - -Returns each field of this struct as a pair of (field name, field type). - -### has_named_attribute - -```rust title="has_named_attribute" showLineNumbers -pub comptime fn has_named_attribute(self, name: str) -> bool {} -``` -> Source code: noir_stdlib/src/meta/struct_def.nr#L20-L22 - - -Returns true if this struct has a custom attribute with the given name. - -### module - -```rust title="module" showLineNumbers -pub comptime fn module(self) -> Module {} -``` -> Source code: noir_stdlib/src/meta/struct_def.nr#L38-L40 - - -Returns the module where the struct is defined. - -### name - -```rust title="name" showLineNumbers -pub comptime fn name(self) -> Quoted {} -``` -> Source code: noir_stdlib/src/meta/struct_def.nr#L43-L45 - - -Returns the name of this struct - -Note that the returned quoted value will be just the struct name, it will -not be the full path to the struct, nor will it include any generics. - -### set_fields - -```rust title="set_fields" showLineNumbers -pub comptime fn set_fields(self, new_fields: [(Quoted, Type)]) {} -``` -> Source code: noir_stdlib/src/meta/struct_def.nr#L52-L54 - - -Sets the fields of this struct to the given fields list where each element -is a pair of the field's name and the field's type. Expects each field name -to be a single identifier. Note that this will override any previous fields -on this struct. If those should be preserved, use `.fields()` to retrieve the -current fields on the struct type and append the new fields from there. - -Example: - -```rust -// Change this struct to: -// struct Foo { -// a: u32, -// b: i8, -// } -#[mangle_fields] -struct Foo { x: Field } - -comptime fn mangle_fields(s: StructDefinition) { - s.set_fields(&[ - (quote { a }, quote { u32 }.as_type()), - (quote { b }, quote { i8 }.as_type()), - ]); -} -``` - -## Trait Implementations - -```rust -impl Eq for StructDefinition -impl Hash for StructDefinition -``` - -Note that each struct is assigned a unique ID internally and this is what is used for -equality and hashing. So even structs with identical generics and fields may not -be equal in this sense if they were originally different items in the source program. diff --git a/docs/versioned_docs/version-v0.37.0/noir/standard_library/meta/trait_constraint.md b/docs/versioned_docs/version-v0.37.0/noir/standard_library/meta/trait_constraint.md deleted file mode 100644 index 3106f732b5a..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/standard_library/meta/trait_constraint.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -title: TraitConstraint ---- - -`std::meta::trait_constraint` contains methods on the built-in `TraitConstraint` type which represents -a trait constraint that can be used to search for a trait implementation. This is similar -syntactically to just the trait itself, but can also contain generic arguments. E.g. `Eq`, `Default`, -`BuildHasher`. - -This type currently has no public methods but it can be used alongside `Type` in `implements` or `get_trait_impl`. - -## Trait Implementations - -```rust -impl Eq for TraitConstraint -impl Hash for TraitConstraint -``` diff --git a/docs/versioned_docs/version-v0.37.0/noir/standard_library/meta/trait_def.md b/docs/versioned_docs/version-v0.37.0/noir/standard_library/meta/trait_def.md deleted file mode 100644 index e661d3af7f1..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/standard_library/meta/trait_def.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: TraitDefinition ---- - -`std::meta::trait_def` contains methods on the built-in `TraitDefinition` type. This type -represents trait definitions such as `trait Foo { .. }` at the top-level of a program. - -## Methods - -### as_trait_constraint - -```rust title="as_trait_constraint" showLineNumbers -pub comptime fn as_trait_constraint(_self: Self) -> TraitConstraint {} -``` -> Source code: noir_stdlib/src/meta/trait_def.nr#L6-L8 - - -Converts this trait into a trait constraint. If there are any generics on this -trait, they will be kept as-is without instantiating or replacing them. - -## Trait Implementations - -```rust -impl Eq for TraitDefinition -impl Hash for TraitDefinition -``` diff --git a/docs/versioned_docs/version-v0.37.0/noir/standard_library/meta/trait_impl.md b/docs/versioned_docs/version-v0.37.0/noir/standard_library/meta/trait_impl.md deleted file mode 100644 index a527617c1e6..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/standard_library/meta/trait_impl.md +++ /dev/null @@ -1,60 +0,0 @@ ---- -title: TraitImpl ---- - -`std::meta::trait_impl` contains methods on the built-in `TraitImpl` type which represents a trait -implementation such as `impl Foo for Bar { ... }`. - -## Methods - -### trait_generic_args - -```rust title="trait_generic_args" showLineNumbers -pub comptime fn trait_generic_args(self) -> [Type] {} -``` -> Source code: noir_stdlib/src/meta/trait_impl.nr#L3-L5 - - -Returns any generic arguments on the trait of this trait implementation, if any. - -```rs -impl Foo for Bar { ... } - -comptime { - let bar_type = quote { Bar }.as_type(); - let foo = quote { Foo }.as_trait_constraint(); - - let my_impl: TraitImpl = bar_type.get_trait_impl(foo).unwrap(); - - let generics = my_impl.trait_generic_args(); - assert_eq(generics.len(), 2); - - assert_eq(generics[0], quote { i32 }.as_type()); - assert_eq(generics[1], quote { Field }.as_type()); -} -``` - -### methods - -```rust title="methods" showLineNumbers -pub comptime fn methods(self) -> [FunctionDefinition] {} -``` -> Source code: noir_stdlib/src/meta/trait_impl.nr#L8-L10 - - -Returns each method in this trait impl. - -Example: - -```rs -comptime { - let i32_type = quote { i32 }.as_type(); - let eq = quote { Eq }.as_trait_constraint(); - - let impl_eq_for_i32: TraitImpl = i32_type.get_trait_impl(eq).unwrap(); - let methods = impl_eq_for_i32.methods(); - - assert_eq(methods.len(), 1); - assert_eq(methods[0].name(), quote { eq }); -} -``` diff --git a/docs/versioned_docs/version-v0.37.0/noir/standard_library/meta/typ.md b/docs/versioned_docs/version-v0.37.0/noir/standard_library/meta/typ.md deleted file mode 100644 index 90222c222f5..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/standard_library/meta/typ.md +++ /dev/null @@ -1,264 +0,0 @@ ---- -title: Type ---- - -`std::meta::typ` contains methods on the built-in `Type` type used for representing -a type in the source program. - -## Functions - -```rust title="fresh_type_variable" showLineNumbers -pub comptime fn fresh_type_variable() -> Type {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L57-L59 - - -Creates and returns an unbound type variable. This is a special kind of type internal -to type checking which will type check with any other type. When it is type checked -against another type it will also be set to that type. For example, if `a` is a type -variable and we have the type equality `(a, i32) = (u8, i32)`, the compiler will set -`a` equal to `u8`. - -Unbound type variables will often be rendered as `_` while printing them. Bound type -variables will appear as the type they are bound to. - -This can be used in conjunction with functions which internally perform type checks -such as `Type::implements` or `Type::get_trait_impl` to potentially grab some of the types used. - -Note that calling `Type::implements` or `Type::get_trait_impl` on a type variable will always -fail. - -Example: - -```rust title="serialize-setup" showLineNumbers -trait Serialize {} - -impl Serialize<1> for Field {} - -impl Serialize for [T; N] -where - T: Serialize, -{} - -impl Serialize for (T, U) -where - T: Serialize, - U: Serialize, -{} -``` -> Source code: test_programs/compile_success_empty/comptime_type/src/main.nr#L14-L29 - -```rust title="fresh-type-variable-example" showLineNumbers -let typevar1 = std::meta::typ::fresh_type_variable(); - let constraint = quote { Serialize<$typevar1> }.as_trait_constraint(); - let field_type = quote { Field }.as_type(); - - // Search for a trait impl (binding typevar1 to 1 when the impl is found): - assert(field_type.implements(constraint)); - - // typevar1 should be bound to the "1" generic now: - assert_eq(typevar1.as_constant().unwrap(), 1); - - // If we want to do the same with a different type, we need to - // create a new type variable now that `typevar1` is bound - let typevar2 = std::meta::typ::fresh_type_variable(); - let constraint = quote { Serialize<$typevar2> }.as_trait_constraint(); - let array_type = quote { [(Field, Field); 5] }.as_type(); - assert(array_type.implements(constraint)); - - // Now typevar2 should be bound to the serialized pair size 2 times the array length 5 - assert_eq(typevar2.as_constant().unwrap(), 10); -``` -> Source code: test_programs/compile_success_empty/comptime_type/src/main.nr#L129-L149 - - -## Methods - -### as_array - -```rust title="as_array" showLineNumbers -pub comptime fn as_array(self) -> Option<(Type, Type)> {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L76-L78 - - -If this type is an array, return a pair of (element type, size type). - -Example: - -```rust -comptime { - let array_type = quote { [Field; 3] }.as_type(); - let (field_type, three_type) = array_type.as_array().unwrap(); - - assert(field_type.is_field()); - assert_eq(three_type.as_constant().unwrap(), 3); -} -``` - -### as_constant - -```rust title="as_constant" showLineNumbers -pub comptime fn as_constant(self) -> Option {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L83-L85 - - -If this type is a constant integer (such as the `3` in the array type `[Field; 3]`), -return the numeric constant. - -### as_integer - -```rust title="as_integer" showLineNumbers -pub comptime fn as_integer(self) -> Option<(bool, u8)> {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L90-L92 - - -If this is an integer type, return a boolean which is `true` -if the type is signed, as well as the number of bits of this integer type. - -### as_mutable_reference - -```rust title="as_mutable_reference" showLineNumbers -comptime fn as_mutable_reference(self) -> Option {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L96-L98 - - -If this is a mutable reference type `&mut T`, returns the mutable type `T`. - -### as_slice - -```rust title="as_slice" showLineNumbers -pub comptime fn as_slice(self) -> Option {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L102-L104 - - -If this is a slice type, return the element type of the slice. - -### as_str - -```rust title="as_str" showLineNumbers -pub comptime fn as_str(self) -> Option {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L108-L110 - - -If this is a `str` type, returns the length `N` as a type. - -### as_struct - -```rust title="as_struct" showLineNumbers -pub comptime fn as_struct(self) -> Option<(StructDefinition, [Type])> {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L114-L116 - - -If this is a struct type, returns the struct in addition to -any generic arguments on this type. - -### as_tuple - -```rust title="as_tuple" showLineNumbers -pub comptime fn as_tuple(self) -> Option<[Type]> {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L120-L122 - - -If this is a tuple type, returns each element type of the tuple. - -### get_trait_impl - -```rust title="get_trait_impl" showLineNumbers -pub comptime fn get_trait_impl(self, constraint: TraitConstraint) -> Option {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L143-L145 - - -Retrieves the trait implementation that implements the given -trait constraint for this type. If the trait constraint is not -found, `None` is returned. Note that since the concrete trait implementation -for a trait constraint specified from a `where` clause is unknown, -this function will return `None` in these cases. If you only want to know -whether a type implements a trait, use `implements` instead. - -Example: - -```rust -comptime { - let field_type = quote { Field }.as_type(); - let default = quote { Default }.as_trait_constraint(); - - let the_impl: TraitImpl = field_type.get_trait_impl(default).unwrap(); - assert(the_impl.methods().len(), 1); -} -``` - -### implements - -```rust title="implements" showLineNumbers -pub comptime fn implements(self, constraint: TraitConstraint) -> bool {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L166-L168 - - -`true` if this type implements the given trait. Note that unlike -`get_trait_impl` this will also return true for any `where` constraints -in scope. - -Example: - -```rust -fn foo() where T: Default { - comptime { - let field_type = quote { Field }.as_type(); - let default = quote { Default }.as_trait_constraint(); - assert(field_type.implements(default)); - - let t = quote { T }.as_type(); - assert(t.implements(default)); - } -} -``` - -### is_bool - -```rust title="is_bool" showLineNumbers -pub comptime fn is_bool(self) -> bool {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L172-L174 - - -`true` if this type is `bool`. - -### is_field - -```rust title="is_field" showLineNumbers -pub comptime fn is_field(self) -> bool {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L178-L180 - - -`true` if this type is `Field`. - -### is_unit - -```rust title="is_unit" showLineNumbers -comptime fn is_unit(self) -> bool {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L184-L186 - - -`true` if this type is the unit `()` type. - -## Trait Implementations - -```rust -impl Eq for Type -impl Hash for Type -``` -Note that this is syntactic equality, this is not the same as whether two types will type check -to be the same type. Unless type inference or generics are being used however, users should not -typically have to worry about this distinction unless `std::meta::typ::fresh_type_variable` is used. diff --git a/docs/versioned_docs/version-v0.37.0/noir/standard_library/meta/typed_expr.md b/docs/versioned_docs/version-v0.37.0/noir/standard_library/meta/typed_expr.md deleted file mode 100644 index 0db7dbfef61..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/standard_library/meta/typed_expr.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -title: TypedExpr ---- - -`std::meta::typed_expr` contains methods on the built-in `TypedExpr` type for resolved and type-checked expressions. - -## Methods - -### get_type - -```rust title="as_function_definition" showLineNumbers -pub comptime fn as_function_definition(self) -> Option {} -``` -> Source code: noir_stdlib/src/meta/typed_expr.nr#L7-L9 - - -If this expression refers to a function definitions, returns it. Otherwise returns `Option::none()`. - -### get_type - -```rust title="get_type" showLineNumbers -pub comptime fn get_type(self) -> Option {} -``` -> Source code: noir_stdlib/src/meta/typed_expr.nr#L13-L15 - - -Returns the type of the expression, or `Option::none()` if there were errors when the expression was previously resolved. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.37.0/noir/standard_library/meta/unresolved_type.md b/docs/versioned_docs/version-v0.37.0/noir/standard_library/meta/unresolved_type.md deleted file mode 100644 index 2826ec5ec0f..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/standard_library/meta/unresolved_type.md +++ /dev/null @@ -1,57 +0,0 @@ ---- -title: UnresolvedType ---- - -`std::meta::unresolved_type` contains methods on the built-in `UnresolvedType` type for the syntax of types. - -## Methods - -### as_mutable_reference - -```rust title="as_mutable_reference" showLineNumbers -comptime fn as_mutable_reference(self) -> Option {} -``` -> Source code: noir_stdlib/src/meta/unresolved_type.nr#L8-L10 - - -If this is a mutable reference type `&mut T`, returns the mutable type `T`. - -### as_slice - -```rust title="as_slice" showLineNumbers -comptime fn as_slice(self) -> Option {} -``` -> Source code: noir_stdlib/src/meta/unresolved_type.nr#L14-L16 - - -If this is a slice `&[T]`, returns the element type `T`. - -### is_bool - -```rust title="is_bool" showLineNumbers -comptime fn is_bool(self) -> bool {} -``` -> Source code: noir_stdlib/src/meta/unresolved_type.nr#L20-L22 - - -Returns `true` if this type is `bool`. - -### is_field - -```rust title="is_field" showLineNumbers -pub comptime fn is_field(self) -> bool {} -``` -> Source code: noir_stdlib/src/meta/unresolved_type.nr#L26-L28 - - -Returns true if this type refers to the Field type. - -### is_unit - -```rust title="is_unit" showLineNumbers -comptime fn is_unit(self) -> bool {} -``` -> Source code: noir_stdlib/src/meta/unresolved_type.nr#L32-L34 - - -Returns true if this type is the unit `()` type. diff --git a/docs/versioned_docs/version-v0.37.0/noir/standard_library/options.md b/docs/versioned_docs/version-v0.37.0/noir/standard_library/options.md deleted file mode 100644 index a1bd4e1de5f..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/standard_library/options.md +++ /dev/null @@ -1,101 +0,0 @@ ---- -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), -} -``` - -The `Option` type, already imported into your Noir program, can be used directly: - -```rust -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. - -### expect - -Asserts `self.is_some()` with a provided custom message and returns the contained `Some` value. The custom message is expected to be a format string. - -### 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.37.0/noir/standard_library/recursion.mdx b/docs/versioned_docs/version-v0.37.0/noir/standard_library/recursion.mdx deleted file mode 100644 index 60414a2fa51..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/standard_library/recursion.mdx +++ /dev/null @@ -1,85 +0,0 @@ ---- -title: Recursive Proofs -description: Learn about how to write recursive proofs in Noir. -keywords: [recursion, recursive proofs, verification_key, verify_proof] ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; - -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. - -Read [the explainer on recursion](../../explainers/explainer-recursion.md) to know more about this function and the [guide on how to use it.](../../how_to/how-to-recursion.md) - -## The `#[recursive]` Attribute - -In Noir, the `#[recursive]` attribute is used to indicate that a circuit is designed for recursive proof generation. When applied, it informs the compiler and the tooling that the circuit should be compiled in a way that makes its proofs suitable for recursive verification. This attribute eliminates the need for manual flagging of recursion at the tooling level, streamlining the proof generation process for recursive circuits. - -### Example usage with `#[recursive]` - -```rust -#[recursive] -fn main(x: Field, y: pub Field) { - assert(x == y, "x and y are not equal"); -} - -// This marks the circuit as recursion-friendly and indicates that proofs generated from this circuit -// are intended for recursive verification. -``` - -By incorporating this attribute directly in the circuit's definition, tooling like Nargo and NoirJS can automatically execute recursive-specific duties for Noir programs (e.g. recursive-friendly proof artifact generation) without additional flags or configurations. - -## Verifying Recursive Proofs - -```rust -#[foreign(recursive_aggregation)] -pub fn verify_proof(verification_key: [Field], proof: [Field], public_inputs: [Field], key_hash: Field) {} -``` - - - -## Example usage - -```rust - -fn main( - verification_key : [Field; 114], - proof : [Field; 93], - public_inputs : [Field; 1], - key_hash : Field, - proof_b : [Field; 93], -) { - std::verify_proof( - verification_key, - proof, - public_inputs, - key_hash - ); - - std::verify_proof( - verification_key, - proof_b, - public_inputs, - key_hash - ); -} -``` - -You can see a full example of recursive proofs in [this example recursion demo repo](https://github.com/noir-lang/noir-examples/tree/master/recursion). - -## 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. - -### `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. diff --git a/docs/versioned_docs/version-v0.37.0/noir/standard_library/traits.md b/docs/versioned_docs/version-v0.37.0/noir/standard_library/traits.md deleted file mode 100644 index ee20f9cd949..00000000000 --- a/docs/versioned_docs/version-v0.37.0/noir/standard_library/traits.md +++ /dev/null @@ -1,628 +0,0 @@ ---- -title: Traits -description: Noir's stdlib provides a few commonly used traits. -keywords: [traits, trait, interface, protocol, default, add, eq] ---- - -## `std::default` - -### `std::default::Default` - -```rust title="default-trait" showLineNumbers -pub trait Default { - fn default() -> Self; -} -``` -> Source code: noir_stdlib/src/default.nr#L4-L8 - - -Constructs a default value of a type. - -Implementations: -```rust -impl Default for Field { .. } - -impl Default for i8 { .. } -impl Default for i16 { .. } -impl Default for i32 { .. } -impl Default for i64 { .. } - -impl Default for u8 { .. } -impl Default for u16 { .. } -impl Default for u32 { .. } -impl Default for u64 { .. } - -impl Default for () { .. } -impl Default for bool { .. } - -impl Default for [T; N] - where T: Default { .. } - -impl Default for [T] { .. } - -impl Default for (A, B) - where A: Default, B: Default { .. } - -impl Default for (A, B, C) - where A: Default, B: Default, C: Default { .. } - -impl Default for (A, B, C, D) - where A: Default, B: Default, C: Default, D: Default { .. } - -impl Default for (A, B, C, D, E) - where A: Default, B: Default, C: Default, D: Default, E: Default { .. } -``` - -For primitive integer types, the return value of `default` is `0`. Container -types such as arrays are filled with default values of their element type, -except slices whose length is unknown and thus defaulted to zero. - ---- - -## `std::convert` - -### `std::convert::From` - -```rust title="from-trait" showLineNumbers -pub trait From { - fn from(input: T) -> Self; -} -``` -> Source code: noir_stdlib/src/convert.nr#L1-L5 - - -The `From` trait defines how to convert from a given type `T` to the type on which the trait is implemented. - -The Noir standard library provides a number of implementations of `From` between primitive types. -```rust title="from-impls" showLineNumbers -// Unsigned integers - -impl From for u32 { - fn from(value: u8) -> u32 { - value as u32 - } -} - -impl From for u64 { - fn from(value: u8) -> u64 { - value as u64 - } -} -impl From for u64 { - fn from(value: u32) -> u64 { - value as u64 - } -} - -impl From for Field { - fn from(value: u8) -> Field { - value as Field - } -} -impl From for Field { - fn from(value: u32) -> Field { - value as Field - } -} -impl From for Field { - fn from(value: u64) -> Field { - value as Field - } -} - -// Signed integers - -impl From for i32 { - fn from(value: i8) -> i32 { - value as i32 - } -} - -impl From for i64 { - fn from(value: i8) -> i64 { - value as i64 - } -} -impl From for i64 { - fn from(value: i32) -> i64 { - value as i64 - } -} - -// Booleans -impl From for u8 { - fn from(value: bool) -> u8 { - value as u8 - } -} -impl From for u32 { - fn from(value: bool) -> u32 { - value as u32 - } -} -impl From for u64 { - fn from(value: bool) -> u64 { - value as u64 - } -} -impl From for i8 { - fn from(value: bool) -> i8 { - value as i8 - } -} -impl From for i32 { - fn from(value: bool) -> i32 { - value as i32 - } -} -impl From for i64 { - fn from(value: bool) -> i64 { - value as i64 - } -} -impl From for Field { - fn from(value: bool) -> Field { - value as Field - } -} -``` -> Source code: noir_stdlib/src/convert.nr#L28-L119 - - -#### When to implement `From` - -As a general rule of thumb, `From` may be implemented in the [situations where it would be suitable in Rust](https://doc.rust-lang.org/std/convert/trait.From.html#when-to-implement-from): - -- The conversion is *infallible*: Noir does not provide an equivalent to Rust's `TryFrom`, if the conversion can fail then provide a named method instead. -- The conversion is *lossless*: semantically, it should not lose or discard information. For example, `u32: From` can losslessly convert any `u16` into a valid `u32` such that the original `u16` can be recovered. On the other hand, `u16: From` should not be implemented as `2**16` is a `u32` which cannot be losslessly converted into a `u16`. -- The conversion is *value-preserving*: the conceptual kind and meaning of the resulting value is the same, even though the Noir type and technical representation might be different. While it's possible to infallibly and losslessly convert a `u8` into a `str<2>` hex representation, `4u8` and `"04"` are too different for `str<2>: From` to be implemented. -- The conversion is *obvious*: it's the only reasonable conversion between the two types. If there's ambiguity on how to convert between them such that the same input could potentially map to two different values then a named method should be used. For instance rather than implementing `U128: From<[u8; 16]>`, the methods `U128::from_le_bytes` and `U128::from_be_bytes` are used as otherwise the endianness of the array would be ambiguous, resulting in two potential values of `U128` from the same byte array. - -One additional recommendation specific to Noir is: -- The conversion is *efficient*: it's relatively cheap to convert between the two types. Due to being a ZK DSL, it's more important to avoid unnecessary computation compared to Rust. If the implementation of `From` would encourage users to perform unnecessary conversion, resulting in additional proving time, then it may be preferable to expose functionality such that this conversion may be avoided. - -### `std::convert::Into` - -The `Into` trait is defined as the reciprocal of `From`. It should be easy to convince yourself that if we can convert to type `A` from type `B`, then it's possible to convert type `B` into type `A`. - -For this reason, implementing `From` on a type will automatically generate a matching `Into` implementation. One should always prefer implementing `From` over `Into` as implementing `Into` will not generate a matching `From` implementation. - -```rust title="into-trait" showLineNumbers -pub trait Into { - fn into(self) -> T; -} - -impl Into for U -where - T: From, -{ - fn into(self) -> T { - T::from(self) - } -} -``` -> Source code: noir_stdlib/src/convert.nr#L13-L26 - - -`Into` is most useful when passing function arguments where the types don't quite match up with what the function expects. In this case, the compiler has enough type information to perform the necessary conversion by just appending `.into()` onto the arguments in question. - ---- - -## `std::cmp` - -### `std::cmp::Eq` - -```rust title="eq-trait" showLineNumbers -pub trait Eq { - fn eq(self, other: Self) -> bool; -} -``` -> Source code: noir_stdlib/src/cmp.nr#L4-L8 - - -Returns `true` if `self` is equal to `other`. Implementing this trait on a type -allows the type to be used with `==` and `!=`. - -Implementations: -```rust -impl Eq for Field { .. } - -impl Eq for i8 { .. } -impl Eq for i16 { .. } -impl Eq for i32 { .. } -impl Eq for i64 { .. } - -impl Eq for u8 { .. } -impl Eq for u16 { .. } -impl Eq for u32 { .. } -impl Eq for u64 { .. } - -impl Eq for () { .. } -impl Eq for bool { .. } - -impl Eq for [T; N] - where T: Eq { .. } - -impl Eq for [T] - where T: Eq { .. } - -impl Eq for (A, B) - where A: Eq, B: Eq { .. } - -impl Eq for (A, B, C) - where A: Eq, B: Eq, C: Eq { .. } - -impl Eq for (A, B, C, D) - where A: Eq, B: Eq, C: Eq, D: Eq { .. } - -impl Eq for (A, B, C, D, E) - where A: Eq, B: Eq, C: Eq, D: Eq, E: Eq { .. } -``` - -### `std::cmp::Ord` - -```rust title="ord-trait" showLineNumbers -pub trait Ord { - fn cmp(self, other: Self) -> Ordering; -} -``` -> Source code: noir_stdlib/src/cmp.nr#L210-L214 - - -`a.cmp(b)` compares two values returning `Ordering::less()` if `a < b`, -`Ordering::equal()` if `a == b`, or `Ordering::greater()` if `a > b`. -Implementing this trait on a type allows `<`, `<=`, `>`, and `>=` to be -used on values of the type. - -`std::cmp` also provides `max` and `min` functions for any type which implements the `Ord` trait. - -Implementations: - -```rust -impl Ord for u8 { .. } -impl Ord for u16 { .. } -impl Ord for u32 { .. } -impl Ord for u64 { .. } - -impl Ord for i8 { .. } -impl Ord for i16 { .. } -impl Ord for i32 { .. } - -impl Ord for i64 { .. } - -impl Ord for () { .. } -impl Ord for bool { .. } - -impl Ord for [T; N] - where T: Ord { .. } - -impl Ord for [T] - where T: Ord { .. } - -impl Ord for (A, B) - where A: Ord, B: Ord { .. } - -impl Ord for (A, B, C) - where A: Ord, B: Ord, C: Ord { .. } - -impl Ord for (A, B, C, D) - where A: Ord, B: Ord, C: Ord, D: Ord { .. } - -impl Ord for (A, B, C, D, E) - where A: Ord, B: Ord, C: Ord, D: Ord, E: Ord { .. } -``` - ---- - -## `std::ops` - -### `std::ops::Add`, `std::ops::Sub`, `std::ops::Mul`, and `std::ops::Div` - -These traits abstract over addition, subtraction, multiplication, and division respectively. -Implementing these traits for a given type will also allow that type to be used with the corresponding operator -for that trait (`+` for Add, etc) in addition to the normal method names. - -```rust title="add-trait" showLineNumbers -pub trait Add { - fn add(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/arith.nr#L1-L5 - -```rust title="sub-trait" showLineNumbers -pub trait Sub { - fn sub(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/arith.nr#L60-L64 - -```rust title="mul-trait" showLineNumbers -pub trait Mul { - fn mul(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/arith.nr#L119-L123 - -```rust title="div-trait" showLineNumbers -pub trait Div { - fn div(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/arith.nr#L178-L182 - - -The implementations block below is given for the `Add` trait, but the same types that implement -`Add` also implement `Sub`, `Mul`, and `Div`. - -Implementations: -```rust -impl Add for Field { .. } - -impl Add for i8 { .. } -impl Add for i16 { .. } -impl Add for i32 { .. } -impl Add for i64 { .. } - -impl Add for u8 { .. } -impl Add for u16 { .. } -impl Add for u32 { .. } -impl Add for u64 { .. } -``` - -### `std::ops::Rem` - -```rust title="rem-trait" showLineNumbers -pub trait Rem { - fn rem(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/arith.nr#L237-L241 - - -`Rem::rem(a, b)` is the remainder function returning the result of what is -left after dividing `a` and `b`. Implementing `Rem` allows the `%` operator -to be used with the implementation type. - -Unlike other numeric traits, `Rem` is not implemented for `Field`. - -Implementations: -```rust -impl Rem for u8 { fn rem(self, other: u8) -> u8 { self % other } } -impl Rem for u16 { fn rem(self, other: u16) -> u16 { self % other } } -impl Rem for u32 { fn rem(self, other: u32) -> u32 { self % other } } -impl Rem for u64 { fn rem(self, other: u64) -> u64 { self % other } } - -impl Rem for i8 { fn rem(self, other: i8) -> i8 { self % other } } -impl Rem for i16 { fn rem(self, other: i16) -> i16 { self % other } } -impl Rem for i32 { fn rem(self, other: i32) -> i32 { self % other } } -impl Rem for i64 { fn rem(self, other: i64) -> i64 { self % other } } -``` - -### `std::ops::Neg` - -```rust title="neg-trait" showLineNumbers -pub trait Neg { - fn neg(self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/arith.nr#L290-L294 - - -`Neg::neg` is equivalent to the unary negation operator `-`. - -Implementations: -```rust title="neg-trait-impls" showLineNumbers -impl Neg for Field { - fn neg(self) -> Field { - -self - } -} - -impl Neg for i8 { - fn neg(self) -> i8 { - -self - } -} -impl Neg for i16 { - fn neg(self) -> i16 { - -self - } -} -impl Neg for i32 { - fn neg(self) -> i32 { - -self - } -} -impl Neg for i64 { - fn neg(self) -> i64 { - -self - } -} -``` -> Source code: noir_stdlib/src/ops/arith.nr#L296-L323 - - -### `std::ops::Not` - -```rust title="not-trait" showLineNumbers -pub trait Not { - fn not(self: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/bit.nr#L1-L5 - - -`Not::not` is equivalent to the unary bitwise NOT operator `!`. - -Implementations: -```rust title="not-trait-impls" showLineNumbers -impl Not for bool { - fn not(self) -> bool { - !self - } -} - -impl Not for u64 { - fn not(self) -> u64 { - !self - } -} -impl Not for u32 { - fn not(self) -> u32 { - !self - } -} -impl Not for u16 { - fn not(self) -> u16 { - !self - } -} -impl Not for u8 { - fn not(self) -> u8 { - !self - } -} -impl Not for u1 { - fn not(self) -> u1 { - !self - } -} - -impl Not for i8 { - fn not(self) -> i8 { - !self - } -} -impl Not for i16 { - fn not(self) -> i16 { - !self - } -} -impl Not for i32 { - fn not(self) -> i32 { - !self - } -} -impl Not for i64 { - fn not(self) -> i64 { - !self - } -} -``` -> Source code: noir_stdlib/src/ops/bit.nr#L7-L60 - - -### `std::ops::{ BitOr, BitAnd, BitXor }` - -```rust title="bitor-trait" showLineNumbers -pub trait BitOr { - fn bitor(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/bit.nr#L62-L66 - -```rust title="bitand-trait" showLineNumbers -pub trait BitAnd { - fn bitand(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/bit.nr#L121-L125 - -```rust title="bitxor-trait" showLineNumbers -pub trait BitXor { - fn bitxor(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/bit.nr#L180-L184 - - -Traits for the bitwise operations `|`, `&`, and `^`. - -Implementing `BitOr`, `BitAnd` or `BitXor` for a type allows the `|`, `&`, or `^` operator respectively -to be used with the type. - -The implementations block below is given for the `BitOr` trait, but the same types that implement -`BitOr` also implement `BitAnd` and `BitXor`. - -Implementations: -```rust -impl BitOr for bool { fn bitor(self, other: bool) -> bool { self | other } } - -impl BitOr for u8 { fn bitor(self, other: u8) -> u8 { self | other } } -impl BitOr for u16 { fn bitor(self, other: u16) -> u16 { self | other } } -impl BitOr for u32 { fn bitor(self, other: u32) -> u32 { self | other } } -impl BitOr for u64 { fn bitor(self, other: u64) -> u64 { self | other } } - -impl BitOr for i8 { fn bitor(self, other: i8) -> i8 { self | other } } -impl BitOr for i16 { fn bitor(self, other: i16) -> i16 { self | other } } -impl BitOr for i32 { fn bitor(self, other: i32) -> i32 { self | other } } -impl BitOr for i64 { fn bitor(self, other: i64) -> i64 { self | other } } -``` - -### `std::ops::{ Shl, Shr }` - -```rust title="shl-trait" showLineNumbers -pub trait Shl { - fn shl(self, other: u8) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/bit.nr#L239-L243 - -```rust title="shr-trait" showLineNumbers -pub trait Shr { - fn shr(self, other: u8) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/bit.nr#L292-L296 - - -Traits for a bit shift left and bit shift right. - -Implementing `Shl` for a type allows the left shift operator (`<<`) to be used with the implementation type. -Similarly, implementing `Shr` allows the right shift operator (`>>`) to be used with the type. - -Note that bit shifting is not currently implemented for signed types. - -The implementations block below is given for the `Shl` trait, but the same types that implement -`Shl` also implement `Shr`. - -Implementations: -```rust -impl Shl for u8 { fn shl(self, other: u8) -> u8 { self << other } } -impl Shl for u16 { fn shl(self, other: u16) -> u16 { self << other } } -impl Shl for u32 { fn shl(self, other: u32) -> u32 { self << other } } -impl Shl for u64 { fn shl(self, other: u64) -> u64 { self << other } } -``` - ---- - -## `std::append` - -### `std::append::Append` - -`Append` can abstract over types that can be appended to - usually container types: - -```rust title="append-trait" showLineNumbers -pub trait Append { - fn empty() -> Self; - fn append(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/append.nr#L9-L14 - - -`Append` requires two methods: - -- `empty`: Constructs an empty value of `Self`. -- `append`: Append two values together, returning the result. - -Additionally, it is expected that for any implementation: - -- `T::empty().append(x) == x` -- `x.append(T::empty()) == x` - -Implementations: -```rust -impl Append for [T] -impl Append for Quoted -``` diff --git a/docs/versioned_docs/version-v0.37.0/reference/NoirJS/noir_js/.nojekyll b/docs/versioned_docs/version-v0.37.0/reference/NoirJS/noir_js/.nojekyll deleted file mode 100644 index e2ac6616add..00000000000 --- a/docs/versioned_docs/version-v0.37.0/reference/NoirJS/noir_js/.nojekyll +++ /dev/null @@ -1 +0,0 @@ -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.37.0/reference/NoirJS/noir_js/classes/Noir.md b/docs/versioned_docs/version-v0.37.0/reference/NoirJS/noir_js/classes/Noir.md deleted file mode 100644 index ead255bc504..00000000000 --- a/docs/versioned_docs/version-v0.37.0/reference/NoirJS/noir_js/classes/Noir.md +++ /dev/null @@ -1,52 +0,0 @@ -# Noir - -## Constructors - -### new Noir(circuit) - -```ts -new Noir(circuit): Noir -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `circuit` | `CompiledCircuit` | - -#### Returns - -[`Noir`](Noir.md) - -## Methods - -### 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) -``` - -*** - -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.37.0/reference/NoirJS/noir_js/functions/and.md b/docs/versioned_docs/version-v0.37.0/reference/NoirJS/noir_js/functions/and.md deleted file mode 100644 index c783283e396..00000000000 --- a/docs/versioned_docs/version-v0.37.0/reference/NoirJS/noir_js/functions/and.md +++ /dev/null @@ -1,22 +0,0 @@ -# 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.37.0/reference/NoirJS/noir_js/functions/blake2s256.md b/docs/versioned_docs/version-v0.37.0/reference/NoirJS/noir_js/functions/blake2s256.md deleted file mode 100644 index 7882d0da8d5..00000000000 --- a/docs/versioned_docs/version-v0.37.0/reference/NoirJS/noir_js/functions/blake2s256.md +++ /dev/null @@ -1,21 +0,0 @@ -# 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.37.0/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md b/docs/versioned_docs/version-v0.37.0/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md deleted file mode 100644 index 5e3cd53e9d3..00000000000 --- a/docs/versioned_docs/version-v0.37.0/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md +++ /dev/null @@ -1,28 +0,0 @@ -# ecdsa\_secp256k1\_verify() - -```ts -ecdsa_secp256k1_verify( - hashed_msg, - public_key_x_bytes, - public_key_y_bytes, - signature): boolean -``` - -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.37.0/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md b/docs/versioned_docs/version-v0.37.0/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md deleted file mode 100644 index 0b20ff68957..00000000000 --- a/docs/versioned_docs/version-v0.37.0/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md +++ /dev/null @@ -1,28 +0,0 @@ -# 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.37.0/reference/NoirJS/noir_js/functions/xor.md b/docs/versioned_docs/version-v0.37.0/reference/NoirJS/noir_js/functions/xor.md deleted file mode 100644 index 8d762b895d3..00000000000 --- a/docs/versioned_docs/version-v0.37.0/reference/NoirJS/noir_js/functions/xor.md +++ /dev/null @@ -1,22 +0,0 @@ -# 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.37.0/reference/NoirJS/noir_js/index.md b/docs/versioned_docs/version-v0.37.0/reference/NoirJS/noir_js/index.md deleted file mode 100644 index 4de7a696991..00000000000 --- a/docs/versioned_docs/version-v0.37.0/reference/NoirJS/noir_js/index.md +++ /dev/null @@ -1,47 +0,0 @@ -# noir_js - -## Exports - -### Classes - -| Class | Description | -| :------ | :------ | -| [Noir](classes/Noir.md) | - | - -### Type Aliases - -| Type alias | Description | -| :------ | :------ | -| [ErrorWithPayload](type-aliases/ErrorWithPayload.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) | - | -| [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) | Verifies a ECDSA signature over the secp256k1 curve. | -| [ecdsa\_secp256r1\_verify](functions/ecdsa_secp256r1_verify.md) | Verifies a ECDSA signature over the secp256r1 curve. | -| [xor](functions/xor.md) | Performs a bitwise XOR operation between `lhs` and `rhs` | - -## References - -### CompiledCircuit - -Renames and re-exports [InputMap](index.md#inputmap) - -## Variables - -### InputMap - -```ts -InputMap: any; -``` - -*** - -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.37.0/reference/NoirJS/noir_js/type-aliases/ErrorWithPayload.md b/docs/versioned_docs/version-v0.37.0/reference/NoirJS/noir_js/type-aliases/ErrorWithPayload.md deleted file mode 100644 index e8c2f4aef3d..00000000000 --- a/docs/versioned_docs/version-v0.37.0/reference/NoirJS/noir_js/type-aliases/ErrorWithPayload.md +++ /dev/null @@ -1,15 +0,0 @@ -# ErrorWithPayload - -```ts -type ErrorWithPayload: ExecutionError & object; -``` - -## Type declaration - -| Member | Type | Description | -| :------ | :------ | :------ | -| `decodedAssertionPayload` | `any` | - | - -*** - -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.37.0/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md b/docs/versioned_docs/version-v0.37.0/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md deleted file mode 100644 index 812b8b16481..00000000000 --- a/docs/versioned_docs/version-v0.37.0/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md +++ /dev/null @@ -1,24 +0,0 @@ -# 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.37.0/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md b/docs/versioned_docs/version-v0.37.0/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md deleted file mode 100644 index dd95809186a..00000000000 --- a/docs/versioned_docs/version-v0.37.0/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md +++ /dev/null @@ -1,9 +0,0 @@ -# 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.37.0/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md b/docs/versioned_docs/version-v0.37.0/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md deleted file mode 100644 index b71fb78a946..00000000000 --- a/docs/versioned_docs/version-v0.37.0/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md +++ /dev/null @@ -1,9 +0,0 @@ -# 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.37.0/reference/NoirJS/noir_js/type-aliases/WitnessMap.md b/docs/versioned_docs/version-v0.37.0/reference/NoirJS/noir_js/type-aliases/WitnessMap.md deleted file mode 100644 index 258c46f9d0c..00000000000 --- a/docs/versioned_docs/version-v0.37.0/reference/NoirJS/noir_js/type-aliases/WitnessMap.md +++ /dev/null @@ -1,9 +0,0 @@ -# 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.37.0/reference/NoirJS/noir_js/typedoc-sidebar.cjs b/docs/versioned_docs/version-v0.37.0/reference/NoirJS/noir_js/typedoc-sidebar.cjs deleted file mode 100644 index 4796b5abaa8..00000000000 --- a/docs/versioned_docs/version-v0.37.0/reference/NoirJS/noir_js/typedoc-sidebar.cjs +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-check -/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ -const typedocSidebar = { items: [{"type":"category","label":"Classes","items":[{"type":"doc","id":"reference/NoirJS/noir_js/classes/Noir","label":"Noir"}]},{"type":"category","label":"Type Aliases","items":[{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ErrorWithPayload","label":"ErrorWithPayload"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ForeignCallHandler","label":"ForeignCallHandler"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ForeignCallInput","label":"ForeignCallInput"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ForeignCallOutput","label":"ForeignCallOutput"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/WitnessMap","label":"WitnessMap"}]},{"type":"category","label":"Functions","items":[{"type":"doc","id":"reference/NoirJS/noir_js/functions/and","label":"and"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/blake2s256","label":"blake2s256"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify","label":"ecdsa_secp256k1_verify"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify","label":"ecdsa_secp256r1_verify"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/xor","label":"xor"}]}]}; -module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.37.0/reference/NoirJS/noir_wasm/.nojekyll b/docs/versioned_docs/version-v0.37.0/reference/NoirJS/noir_wasm/.nojekyll deleted file mode 100644 index e2ac6616add..00000000000 --- a/docs/versioned_docs/version-v0.37.0/reference/NoirJS/noir_wasm/.nojekyll +++ /dev/null @@ -1 +0,0 @@ -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.37.0/reference/NoirJS/noir_wasm/functions/compile.md b/docs/versioned_docs/version-v0.37.0/reference/NoirJS/noir_wasm/functions/compile.md deleted file mode 100644 index 6faf763b37f..00000000000 --- a/docs/versioned_docs/version-v0.37.0/reference/NoirJS/noir_wasm/functions/compile.md +++ /dev/null @@ -1,51 +0,0 @@ -# compile() - -```ts -compile( - fileManager, - projectPath?, - logFn?, -debugLogFn?): Promise -``` - -Compiles a Noir project - -## Parameters - -| Parameter | Type | Description | -| :------ | :------ | :------ | -| `fileManager` | `FileManager` | The file manager to use | -| `projectPath`? | `string` | The path to the project inside the file manager. Defaults to the root of the file manager | -| `logFn`? | `LogFn` | A logging function. If not provided, console.log will be used | -| `debugLogFn`? | `LogFn` | A debug logging function. If not provided, logFn will be used | - -## Returns - -`Promise`\<[`ProgramCompilationArtifacts`](../index.md#programcompilationartifacts)\> - -## Example - -```typescript -// Node.js - -import { compile_program, createFileManager } from '@noir-lang/noir_wasm'; - -const fm = createFileManager(myProjectPath); -const myCompiledCode = await compile_program(fm); -``` - -```typescript -// Browser - -import { compile_program, createFileManager } from '@noir-lang/noir_wasm'; - -const fm = createFileManager('/'); -for (const path of files) { - await fm.writeFile(path, await getFileAsStream(path)); -} -const myCompiledCode = await compile_program(fm); -``` - -*** - -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.37.0/reference/NoirJS/noir_wasm/functions/compile_contract.md b/docs/versioned_docs/version-v0.37.0/reference/NoirJS/noir_wasm/functions/compile_contract.md deleted file mode 100644 index 7d0b39a43ef..00000000000 --- a/docs/versioned_docs/version-v0.37.0/reference/NoirJS/noir_wasm/functions/compile_contract.md +++ /dev/null @@ -1,51 +0,0 @@ -# compile\_contract() - -```ts -compile_contract( - fileManager, - projectPath?, - logFn?, -debugLogFn?): Promise -``` - -Compiles a Noir project - -## Parameters - -| Parameter | Type | Description | -| :------ | :------ | :------ | -| `fileManager` | `FileManager` | The file manager to use | -| `projectPath`? | `string` | The path to the project inside the file manager. Defaults to the root of the file manager | -| `logFn`? | `LogFn` | A logging function. If not provided, console.log will be used | -| `debugLogFn`? | `LogFn` | A debug logging function. If not provided, logFn will be used | - -## Returns - -`Promise`\<[`ContractCompilationArtifacts`](../index.md#contractcompilationartifacts)\> - -## Example - -```typescript -// Node.js - -import { compile_contract, createFileManager } from '@noir-lang/noir_wasm'; - -const fm = createFileManager(myProjectPath); -const myCompiledCode = await compile_contract(fm); -``` - -```typescript -// Browser - -import { compile_contract, createFileManager } from '@noir-lang/noir_wasm'; - -const fm = createFileManager('/'); -for (const path of files) { - await fm.writeFile(path, await getFileAsStream(path)); -} -const myCompiledCode = await compile_contract(fm); -``` - -*** - -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.37.0/reference/NoirJS/noir_wasm/functions/createFileManager.md b/docs/versioned_docs/version-v0.37.0/reference/NoirJS/noir_wasm/functions/createFileManager.md deleted file mode 100644 index 7e65c1d69c7..00000000000 --- a/docs/versioned_docs/version-v0.37.0/reference/NoirJS/noir_wasm/functions/createFileManager.md +++ /dev/null @@ -1,21 +0,0 @@ -# createFileManager() - -```ts -createFileManager(dataDir): FileManager -``` - -Creates a new FileManager instance based on fs in node and memfs in the browser (via webpack alias) - -## Parameters - -| Parameter | Type | Description | -| :------ | :------ | :------ | -| `dataDir` | `string` | root of the file system | - -## Returns - -`FileManager` - -*** - -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.37.0/reference/NoirJS/noir_wasm/functions/inflateDebugSymbols.md b/docs/versioned_docs/version-v0.37.0/reference/NoirJS/noir_wasm/functions/inflateDebugSymbols.md deleted file mode 100644 index fcea9275341..00000000000 --- a/docs/versioned_docs/version-v0.37.0/reference/NoirJS/noir_wasm/functions/inflateDebugSymbols.md +++ /dev/null @@ -1,21 +0,0 @@ -# inflateDebugSymbols() - -```ts -inflateDebugSymbols(debugSymbols): any -``` - -Decompresses and decodes the debug symbols - -## Parameters - -| Parameter | Type | Description | -| :------ | :------ | :------ | -| `debugSymbols` | `string` | The base64 encoded debug symbols | - -## Returns - -`any` - -*** - -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.37.0/reference/NoirJS/noir_wasm/index.md b/docs/versioned_docs/version-v0.37.0/reference/NoirJS/noir_wasm/index.md deleted file mode 100644 index b6e0f9d1bc0..00000000000 --- a/docs/versioned_docs/version-v0.37.0/reference/NoirJS/noir_wasm/index.md +++ /dev/null @@ -1,49 +0,0 @@ -# noir_wasm - -## Exports - -### Functions - -| Function | Description | -| :------ | :------ | -| [compile](functions/compile.md) | Compiles a Noir project | -| [compile\_contract](functions/compile_contract.md) | Compiles a Noir project | -| [createFileManager](functions/createFileManager.md) | Creates a new FileManager instance based on fs in node and memfs in the browser (via webpack alias) | -| [inflateDebugSymbols](functions/inflateDebugSymbols.md) | Decompresses and decodes the debug symbols | - -## References - -### compile\_program - -Renames and re-exports [compile](functions/compile.md) - -## Interfaces - -### ContractCompilationArtifacts - -The compilation artifacts of a given contract. - -#### Properties - -| Property | Type | Description | -| :------ | :------ | :------ | -| `contract` | `ContractArtifact` | The compiled contract. | -| `warnings` | `unknown`[] | Compilation warnings. | - -*** - -### ProgramCompilationArtifacts - -The compilation artifacts of a given program. - -#### Properties - -| Property | Type | Description | -| :------ | :------ | :------ | -| `name` | `string` | not part of the compilation output, injected later | -| `program` | `ProgramArtifact` | The compiled contract. | -| `warnings` | `unknown`[] | Compilation warnings. | - -*** - -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.37.0/reference/NoirJS/noir_wasm/typedoc-sidebar.cjs b/docs/versioned_docs/version-v0.37.0/reference/NoirJS/noir_wasm/typedoc-sidebar.cjs deleted file mode 100644 index e0870710349..00000000000 --- a/docs/versioned_docs/version-v0.37.0/reference/NoirJS/noir_wasm/typedoc-sidebar.cjs +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-check -/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ -const typedocSidebar = { items: [{"type":"doc","id":"reference/NoirJS/noir_wasm/index","label":"API"},{"type":"category","label":"Functions","items":[{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/compile","label":"compile"},{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/compile_contract","label":"compile_contract"},{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/createFileManager","label":"createFileManager"},{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/inflateDebugSymbols","label":"inflateDebugSymbols"}]}]}; -module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.37.0/reference/_category_.json b/docs/versioned_docs/version-v0.37.0/reference/_category_.json deleted file mode 100644 index 5b6a20a609a..00000000000 --- a/docs/versioned_docs/version-v0.37.0/reference/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "position": 4, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.37.0/reference/debugger/_category_.json b/docs/versioned_docs/version-v0.37.0/reference/debugger/_category_.json deleted file mode 100644 index 27869205ad3..00000000000 --- a/docs/versioned_docs/version-v0.37.0/reference/debugger/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "Debugger", - "position": 1, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.37.0/reference/debugger/debugger_known_limitations.md b/docs/versioned_docs/version-v0.37.0/reference/debugger/debugger_known_limitations.md deleted file mode 100644 index 936d416ac4b..00000000000 --- a/docs/versioned_docs/version-v0.37.0/reference/debugger/debugger_known_limitations.md +++ /dev/null @@ -1,59 +0,0 @@ ---- -title: Known limitations -description: - An overview of known limitations of the current version of the Noir debugger -keywords: - [ - Nargo, - Noir Debugger, - VS Code, - ] -sidebar_position: 2 ---- - -# Debugger Known Limitations - -There are currently some limits to what the debugger can observe. - -## Mutable references - -The debugger is currently blind to any state mutated via a mutable reference. For example, in: - -``` -let mut x = 1; -let y = &mut x; -*y = 2; -``` - -The update on `x` will not be observed by the debugger. That means, when running `vars` from the debugger REPL, or inspecting the _local variables_ pane in the VS Code debugger, `x` will appear with value 1 despite having executed `*y = 2;`. - -## Variables of type function or mutable references are opaque - -When inspecting variables, any variable of type `Function` or `MutableReference` will render its value as `<>` or `<>`. - -## Debugger instrumentation affects resulting ACIR - -In order to make the state of local variables observable, the debugger compiles Noir circuits interleaving foreign calls that track any mutations to them. While this works (except in the cases described above) and doesn't introduce any behavior changes, it does as a side effect produce bigger bytecode. In particular, when running the command `opcodes` on the REPL debugger, you will notice Unconstrained VM blocks that look like this: - -``` -... -5 BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [], q_c: 2 }), Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(2))], q_c: 0 })] - | outputs=[] - 5.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } - 5.1 | Mov { destination: RegisterIndex(3), source: RegisterIndex(1) } - 5.2 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } - 5.3 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } - 5.4 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } - 5.5 | Mov { destination: RegisterIndex(3), source: RegisterIndex(3) } - 5.6 | Call { location: 8 } - 5.7 | Stop - 5.8 | ForeignCall { function: "__debug_var_assign", destinations: [], inputs: [RegisterIndex(RegisterIndex(2)), RegisterIndex(RegisterIndex(3))] } -... -``` - -If you are interested in debugging/inspecting compiled ACIR without these synthetic changes, you can invoke the REPL debugger with the `--skip-instrumentation` flag or launch the VS Code debugger with the `skipConfiguration` property set to true in its launch configuration. You can find more details about those in the [Debugger REPL reference](debugger_repl.md) and the [VS Code Debugger reference](debugger_vscode.md). - -:::note -Skipping debugger instrumentation means you won't be able to inspect values of local variables. -::: - diff --git a/docs/versioned_docs/version-v0.37.0/reference/debugger/debugger_repl.md b/docs/versioned_docs/version-v0.37.0/reference/debugger/debugger_repl.md deleted file mode 100644 index 46e2011304e..00000000000 --- a/docs/versioned_docs/version-v0.37.0/reference/debugger/debugger_repl.md +++ /dev/null @@ -1,360 +0,0 @@ ---- -title: REPL Debugger -description: - Noir Debugger REPL options and commands. -keywords: - [ - Nargo, - Noir CLI, - Noir Debugger, - REPL, - ] -sidebar_position: 1 ---- - -## Running the REPL debugger - -`nargo debug [OPTIONS] [WITNESS_NAME]` - -Runs the Noir REPL debugger. If a `WITNESS_NAME` is provided the debugger writes the resulting execution witness to a `WITNESS_NAME` 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 debug | -| `--print-acir` | Display the ACIR for compiled circuit | -| `--deny-warnings` | Treat all warnings as errors | -| `--silence-warnings` | Suppress warnings | -| `-h, --help` | Print help | - -None of these options are required. - -:::note -Since the debugger starts by compiling the target package, all Noir compiler options are also available. Check out the [compiler reference](../nargo_commands.md#nargo-compile) to learn more about the compiler options. -::: - -## REPL commands - -Once the debugger is running, it accepts the following commands. - -#### `help` (h) - -Displays the menu of available commands. - -``` -> help -Available commands: - - opcodes display ACIR opcodes - into step into to the next opcode - next step until a new source location is reached - out step until a new source location is reached - and the current stack frame is finished - break LOCATION:OpcodeLocation add a breakpoint at an opcode location - over step until a new source location is reached - without diving into function calls - restart restart the debugging session - delete LOCATION:OpcodeLocation delete breakpoint at an opcode location - witness show witness map - witness index:u32 display a single witness from the witness map - witness index:u32 value:String update a witness with the given value - memset index:usize value:String update a memory cell with the given - value - continue continue execution until the end of the - program - vars show variable values available at this point - in execution - stacktrace display the current stack trace - memory show memory (valid when executing unconstrained code) value - step step to the next ACIR opcode - -Other commands: - - help Show this help message - quit Quit repl - -``` - -### Stepping through programs - -#### `next` (n) - -Step until the next Noir source code location. While other commands, such as [`into`](#into-i) and [`step`](#step-s), allow for finer grained control of the program's execution at the opcode level, `next` is source code centric. For example: - -``` -3 ... -4 fn main(x: u32) { -5 assert(entry_point(x) == 2); -6 swap_entry_point(x, x + 1); -7 -> assert(deep_entry_point(x) == 4); -8 multiple_values_entry_point(x); -9 } -``` - - -Using `next` here would cause the debugger to jump to the definition of `deep_entry_point` (if available). - -If you want to step over `deep_entry_point` and go straight to line 8, use [the `over` command](#over) instead. - -#### `over` - -Step until the next source code location, without diving into function calls. For example: - -``` -3 ... -4 fn main(x: u32) { -5 assert(entry_point(x) == 2); -6 swap_entry_point(x, x + 1); -7 -> assert(deep_entry_point(x) == 4); -8 multiple_values_entry_point(x); -9 } -``` - - -Using `over` here would cause the debugger to execute until line 8 (`multiple_values_entry_point(x);`). - -If you want to step into `deep_entry_point` instead, use [the `next` command](#next-n). - -#### `out` - -Step until the end of the current function call. For example: - -``` - 3 ... - 4 fn main(x: u32) { - 5 assert(entry_point(x) == 2); - 6 swap_entry_point(x, x + 1); - 7 -> assert(deep_entry_point(x) == 4); - 8 multiple_values_entry_point(x); - 9 } - 10 - 11 unconstrained fn returns_multiple_values(x: u32) -> (u32, u32, u32, u32) { - 12 ... - ... - 55 - 56 unconstrained fn deep_entry_point(x: u32) -> u32 { - 57 -> level_1(x + 1) - 58 } - -``` - -Running `out` here will resume execution until line 8. - -#### `step` (s) - -Skips to the next ACIR code. A compiled Noir program is a sequence of ACIR opcodes. However, an unconstrained VM opcode denotes the start of an unconstrained code block, to be executed by the unconstrained VM. For example (redacted for brevity): - -``` -0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] -1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] - 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } - 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } - 1.2 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } - 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } - 1.4 | Call { location: 7 } - ... - 1.43 | Return -2 EXPR [ (1, _1) -2 ] -``` - -The `->` here shows the debugger paused at an ACIR opcode: `BRILLIG`, at index 1, which denotes an unconstrained code block is about to start. - -Using the `step` command at this point would result in the debugger stopping at ACIR opcode 2, `EXPR`, skipping unconstrained computation steps. - -Use [the `into` command](#into-i) instead if you want to follow unconstrained computation step by step. - -#### `into` (i) - -Steps into the next opcode. A compiled Noir program is a sequence of ACIR opcodes. However, a BRILLIG opcode denotes the start of an unconstrained code block, to be executed by the unconstrained VM. For example (redacted for brevity): - -``` -0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] -1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] - 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } - 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } - 1.2 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } - 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } - 1.4 | Call { location: 7 } - ... - 1.43 | Return -2 EXPR [ (1, _1) -2 ] -``` - -The `->` here shows the debugger paused at an ACIR opcode: `BRILLIG`, at index 1, which denotes an unconstrained code block is about to start. - -Using the `into` command at this point would result in the debugger stopping at opcode 1.0, `Mov ...`, allowing the debugger user to follow unconstrained computation step by step. - -Use [the `step` command](#step-s) instead if you want to skip to the next ACIR code directly. - -#### `continue` (c) - -Continues execution until the next breakpoint, or the end of the program. - -#### `restart` (res) - -Interrupts execution, and restarts a new debugging session from scratch. - -#### `opcodes` (o) - -Display the program's ACIR opcode sequence. For example: - -``` -0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] -1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] - 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } - 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } - 1.2 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } - 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } - 1.4 | Call { location: 7 } - ... - 1.43 | Return -2 EXPR [ (1, _1) -2 ] -``` - -### Breakpoints - -#### `break [Opcode]` (or shorthand `b [Opcode]`) - -Sets a breakpoint on the specified opcode index. To get a list of the program opcode numbers, see [the `opcode` command](#opcodes-o). For example: - -``` -0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] -1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] - 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } - 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } - 1.2 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } - 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } - 1.4 | Call { location: 7 } - ... - 1.43 | Return -2 EXPR [ (1, _1) -2 ] -``` - -In this example, issuing a `break 1.2` command adds break on opcode 1.2, as denoted by the `*` character: - -``` -0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] -1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] - 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } - 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } - 1.2 | * Const { destination: RegisterIndex(1), value: Value { inner: 0 } } - 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } - 1.4 | Call { location: 7 } - ... - 1.43 | Return -2 EXPR [ (1, _1) -2 ] -``` - -Running [the `continue` command](#continue-c) at this point would cause the debugger to execute the program until opcode 1.2. - -#### `delete [Opcode]` (or shorthand `d [Opcode]`) - -Deletes a breakpoint at an opcode location. Usage is analogous to [the `break` command](#). - -### Variable inspection - -#### vars - -Show variable values available at this point in execution. - -:::note -The ability to inspect variable values from the debugger depends on compilation to be run in a special debug instrumentation mode. This instrumentation weaves variable tracing code with the original source code. - -So variable value inspection comes at the expense of making the resulting ACIR bytecode bigger and harder to understand and optimize. - -If you find this compromise unacceptable, you can run the debugger with the flag `--skip-debug-instrumentation`. This will compile your circuit without any additional debug information, so the resulting ACIR bytecode will be identical to the one produced by standard Noir compilation. However, if you opt for this, the `vars` command will not be available while debugging. -::: - - -### Stacktrace - -#### `stacktrace` - -Displays the current stack trace. - - -### Witness map - -#### `witness` (w) - -Show witness map. For example: - -``` -_0 = 0 -_1 = 2 -_2 = 1 -``` - -#### `witness [Witness Index]` - -Display a single witness from the witness map. For example: - -``` -> witness 1 -_1 = 2 -``` - -#### `witness [Witness Index] [New value]` - -Overwrite the given index with a new value. For example: - -``` -> witness 1 3 -_1 = 3 -``` - - -### Unconstrained VM memory - -#### `memory` - -Show unconstrained VM memory state. For example: - -``` -> memory -At opcode 1.13: Store { destination_pointer: RegisterIndex(0), source: RegisterIndex(3) } -... -> registers -0 = 0 -1 = 10 -2 = 0 -3 = 1 -4 = 1 -5 = 2³² -6 = 1 -> into -At opcode 1.14: Const { destination: RegisterIndex(5), value: Value { inner: 1 } } -... -> memory -0 = 1 -> -``` - -In the example above: we start with clean memory, then step through a `Store` opcode which stores the value of register 3 (1) into the memory address stored in register 0 (0). Thus now `memory` shows memory address 0 contains value 1. - -:::note -This command is only functional while the debugger is executing unconstrained code. -::: - -#### `memset [Memory address] [New value]` - -Update a memory cell with the given value. For example: - -``` -> memory -0 = 1 -> memset 0 2 -> memory -0 = 2 -> memset 1 4 -> memory -0 = 2 -1 = 4 -> -``` - -:::note -This command is only functional while the debugger is executing unconstrained code. -::: \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.37.0/reference/debugger/debugger_vscode.md b/docs/versioned_docs/version-v0.37.0/reference/debugger/debugger_vscode.md deleted file mode 100644 index c027332b3b0..00000000000 --- a/docs/versioned_docs/version-v0.37.0/reference/debugger/debugger_vscode.md +++ /dev/null @@ -1,82 +0,0 @@ ---- -title: VS Code Debugger -description: - VS Code Debugger configuration and features. -keywords: - [ - Nargo, - Noir CLI, - Noir Debugger, - VS Code, - IDE, - ] -sidebar_position: 0 ---- - -# VS Code Noir Debugger Reference - -The Noir debugger enabled by the vscode-noir extension ships with default settings such that the most common scenario should run without any additional configuration steps. - -These defaults can nevertheless be overridden by defining a launch configuration file. This page provides a reference for the properties you can override via a launch configuration file, as well as documenting the Nargo `dap` command, which is a dependency of the VS Code Noir debugger. - - -## Creating and editing launch configuration files - -To create a launch configuration file from VS Code, open the _debug pane_, and click on _create a launch.json file_. - -![Creating a launch configuration file](@site/static/img/debugger/ref1-create-launch.png) - -A `launch.json` file will be created, populated with basic defaults. - -### Noir Debugger launch.json properties - -#### projectFolder - -_String, optional._ - -Absolute path to the Nargo project to debug. By default, it is dynamically determined by looking for the nearest `Nargo.toml` file to the active file at the moment of launching the debugger. - -#### proverName - -_String, optional._ - -Name of the prover input to use. Defaults to `Prover`, which looks for a file named `Prover.toml` at the `projectFolder`. - -#### generateAcir - -_Boolean, optional._ - -If true, generate ACIR opcodes instead of unconstrained opcodes which will be closer to release binaries but less convenient for debugging. Defaults to `false`. - -#### skipInstrumentation - -_Boolean, optional._ - -Skips variables debugging instrumentation of code, making debugging less convenient but the resulting binary smaller and closer to production. Defaults to `false`. - -:::note -Skipping instrumentation causes the debugger to be unable to inspect local variables. -::: - -## `nargo dap [OPTIONS]` - -When run without any option flags, it starts the Nargo Debug Adapter Protocol server, which acts as the debugging backend for the VS Code Noir Debugger. - -All option flags are related to preflight checks. The Debug Adapter Protocol specifies how errors are to be informed from a running DAP server, but it doesn't specify mechanisms to communicate server initialization errors between the DAP server and its client IDE. - -Thus `nargo dap` ships with a _preflight check_ mode. If flag `--preflight-check` and the rest of the `--preflight-*` flags are provided, Nargo will run the same initialization routine except it will not start the DAP server. - -`vscode-noir` will then run `nargo dap` in preflight check mode first before a debugging session starts. If the preflight check ends in error, vscode-noir will present stderr and stdout output from this process through its own Output pane in VS Code. This makes it possible for users to diagnose what pieces of configuration might be wrong or missing in case of initialization errors. - -If the preflight check succeeds, `vscode-noir` proceeds to start the DAP server normally but running `nargo dap` without any additional flags. - -### Options - -| Option | Description | -| --------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | -| `--preflight-check` | If present, dap runs in preflight check mode. | -| `--preflight-project-folder ` | Absolute path to the project to debug for preflight check. | -| `--preflight-prover-name ` | Name of prover file to use for preflight check | -| `--preflight-generate-acir` | Optional. If present, compile in ACIR mode while running preflight check. | -| `--preflight-skip-instrumentation` | Optional. If present, compile without introducing debug instrumentation while running preflight check. | -| `-h, --help` | Print help. | diff --git a/docs/versioned_docs/version-v0.37.0/reference/nargo_commands.md b/docs/versioned_docs/version-v0.37.0/reference/nargo_commands.md deleted file mode 100644 index 44fe8a2e62c..00000000000 --- a/docs/versioned_docs/version-v0.37.0/reference/nargo_commands.md +++ /dev/null @@ -1,301 +0,0 @@ ---- -title: Nargo -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, - ] -sidebar_position: 0 ---- - -# Command-Line Help for `nargo` - -This document contains the help content for the `nargo` command-line program. - -**Command Overview:** - -* [`nargo`↴](#nargo) -* [`nargo check`↴](#nargo-check) -* [`nargo fmt`↴](#nargo-fmt) -* [`nargo compile`↴](#nargo-compile) -* [`nargo new`↴](#nargo-new) -* [`nargo init`↴](#nargo-init) -* [`nargo execute`↴](#nargo-execute) -* [`nargo debug`↴](#nargo-debug) -* [`nargo test`↴](#nargo-test) -* [`nargo info`↴](#nargo-info) -* [`nargo lsp`↴](#nargo-lsp) - -## `nargo` - -Noir's package manager - -**Usage:** `nargo ` - -###### **Subcommands:** - -* `check` — Checks the constraint system for errors -* `fmt` — Format the Noir files in a workspace -* `compile` — Compile the program and its secret execution trace into ACIR format -* `new` — Create a Noir project in a new directory -* `init` — Create a Noir project in the current directory -* `execute` — Executes a circuit to calculate its return value -* `debug` — Executes a circuit in debug mode -* `test` — Run the tests for this program -* `info` — Provides detailed information on each of a program's function (represented by a single circuit) -* `lsp` — Starts the Noir LSP server - -###### **Options:** - - - - -## `nargo check` - -Checks the constraint system for errors - -**Usage:** `nargo check [OPTIONS]` - -###### **Options:** - -* `--package ` — The name of the package to check -* `--workspace` — Check all packages in the workspace -* `--overwrite` — Force overwrite of existing files -* `--expression-width ` — Specify the backend expression width that should be targeted -* `--bounded-codegen` — Generate ACIR with the target backend expression width. The default is to generate ACIR without a bound and split expressions after code generation. Activating this flag can sometimes provide optimizations for certain programs - - Default value: `false` -* `--force` — Force a full recompilation -* `--print-acir` — Display the ACIR for compiled circuit -* `--deny-warnings` — Treat all warnings as errors -* `--silence-warnings` — Suppress warnings -* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" -* `--skip-underconstrained-check` — Flag to turn off the compiler check for under constrained values. Warning: This can improve compilation speed but can also lead to correctness errors. This check should always be run on production code - - - -## `nargo fmt` - -Format the Noir files in a workspace - -**Usage:** `nargo fmt [OPTIONS]` - -###### **Options:** - -* `--check` — Run noirfmt in check mode - - - -## `nargo compile` - -Compile the program and its secret execution trace into ACIR format - -**Usage:** `nargo compile [OPTIONS]` - -###### **Options:** - -* `--package ` — The name of the package to compile -* `--workspace` — Compile all packages in the workspace -* `--expression-width ` — Specify the backend expression width that should be targeted -* `--bounded-codegen` — Generate ACIR with the target backend expression width. The default is to generate ACIR without a bound and split expressions after code generation. Activating this flag can sometimes provide optimizations for certain programs - - Default value: `false` -* `--force` — Force a full recompilation -* `--print-acir` — Display the ACIR for compiled circuit -* `--deny-warnings` — Treat all warnings as errors -* `--silence-warnings` — Suppress warnings -* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" -* `--skip-underconstrained-check` — Flag to turn off the compiler check for under constrained values. Warning: This can improve compilation speed but can also lead to correctness errors. This check should always be run on production code - - - -## `nargo new` - -Create a Noir project in a new directory - -**Usage:** `nargo new [OPTIONS] ` - -###### **Arguments:** - -* `` — The path to save the new project - -###### **Options:** - -* `--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 - - - -## `nargo init` - -Create a Noir project in the current directory - -**Usage:** `nargo init [OPTIONS]` - -###### **Options:** - -* `--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 - - - -## `nargo execute` - -Executes a circuit to calculate its return value - -**Usage:** `nargo execute [OPTIONS] [WITNESS_NAME]` - -###### **Arguments:** - -* `` — Write the execution witness to named file - -Defaults to the name of the package being executed. - -###### **Options:** - -* `-p`, `--prover-name ` — The name of the toml file which contains the inputs for the prover - - Default value: `Prover` -* `--package ` — The name of the package to execute -* `--workspace` — Execute all packages in the workspace -* `--expression-width ` — Specify the backend expression width that should be targeted -* `--bounded-codegen` — Generate ACIR with the target backend expression width. The default is to generate ACIR without a bound and split expressions after code generation. Activating this flag can sometimes provide optimizations for certain programs - - Default value: `false` -* `--force` — Force a full recompilation -* `--print-acir` — Display the ACIR for compiled circuit -* `--deny-warnings` — Treat all warnings as errors -* `--silence-warnings` — Suppress warnings -* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" -* `--skip-underconstrained-check` — Flag to turn off the compiler check for under constrained values. Warning: This can improve compilation speed but can also lead to correctness errors. This check should always be run on production code -* `--oracle-resolver ` — JSON RPC url to solve oracle calls - - - -## `nargo debug` - -Executes a circuit in debug mode - -**Usage:** `nargo debug [OPTIONS] [WITNESS_NAME]` - -###### **Arguments:** - -* `` — Write the execution witness to named file - -###### **Options:** - -* `-p`, `--prover-name ` — The name of the toml file which contains the inputs for the prover - - Default value: `Prover` -* `--package ` — The name of the package to execute -* `--expression-width ` — Specify the backend expression width that should be targeted -* `--bounded-codegen` — Generate ACIR with the target backend expression width. The default is to generate ACIR without a bound and split expressions after code generation. Activating this flag can sometimes provide optimizations for certain programs - - Default value: `false` -* `--force` — Force a full recompilation -* `--print-acir` — Display the ACIR for compiled circuit -* `--deny-warnings` — Treat all warnings as errors -* `--silence-warnings` — Suppress warnings -* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" -* `--skip-underconstrained-check` — Flag to turn off the compiler check for under constrained values. Warning: This can improve compilation speed but can also lead to correctness errors. This check should always be run on production code -* `--acir-mode` — Force ACIR output (disabling instrumentation) -* `--skip-instrumentation ` — Disable vars debug instrumentation (enabled by default) - - Possible values: `true`, `false` - - - - -## `nargo test` - -Run the tests for this program - -**Usage:** `nargo test [OPTIONS] [TEST_NAME]` - -###### **Arguments:** - -* `` — If given, only tests with names containing this string will be run - -###### **Options:** - -* `--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 -* `--expression-width ` — Specify the backend expression width that should be targeted -* `--bounded-codegen` — Generate ACIR with the target backend expression width. The default is to generate ACIR without a bound and split expressions after code generation. Activating this flag can sometimes provide optimizations for certain programs - - Default value: `false` -* `--force` — Force a full recompilation -* `--print-acir` — Display the ACIR for compiled circuit -* `--deny-warnings` — Treat all warnings as errors -* `--silence-warnings` — Suppress warnings -* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" -* `--skip-underconstrained-check` — Flag to turn off the compiler check for under constrained values. Warning: This can improve compilation speed but can also lead to correctness errors. This check should always be run on production code -* `--oracle-resolver ` — JSON RPC url to solve oracle calls - - - -## `nargo info` - -Provides detailed information on each of a program's function (represented by a single circuit) - -Current information provided per circuit: 1. The number of ACIR opcodes 2. Counts the final number gates in the circuit used by a backend - -**Usage:** `nargo info [OPTIONS]` - -###### **Options:** - -* `--package ` — The name of the package to detail -* `--workspace` — Detail all packages in the workspace -* `--profile-execution` -* `-p`, `--prover-name ` — The name of the toml file which contains the inputs for the prover - - Default value: `Prover` -* `--expression-width ` — Specify the backend expression width that should be targeted -* `--bounded-codegen` — Generate ACIR with the target backend expression width. The default is to generate ACIR without a bound and split expressions after code generation. Activating this flag can sometimes provide optimizations for certain programs - - Default value: `false` -* `--force` — Force a full recompilation -* `--print-acir` — Display the ACIR for compiled circuit -* `--deny-warnings` — Treat all warnings as errors -* `--silence-warnings` — Suppress warnings -* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" -* `--skip-underconstrained-check` — Flag to turn off the compiler check for under constrained values. Warning: This can improve compilation speed but can also lead to correctness errors. This check should always be run on production code - - - -## `nargo lsp` - -Starts the Noir LSP server - -Starts an LSP server which allows IDEs such as VS Code to display diagnostics in Noir source. - -VS Code Noir Language Support: https://marketplace.visualstudio.com/items?itemName=noir-lang.vscode-noir - -**Usage:** `nargo lsp` - - - -
- - - This document was generated automatically by - clap-markdown. - - diff --git a/docs/versioned_docs/version-v0.37.0/reference/noir_codegen.md b/docs/versioned_docs/version-v0.37.0/reference/noir_codegen.md deleted file mode 100644 index e4c362f9610..00000000000 --- a/docs/versioned_docs/version-v0.37.0/reference/noir_codegen.md +++ /dev/null @@ -1,116 +0,0 @@ ---- -title: Noir Codegen for TypeScript -description: Learn how to use Noir codegen to generate TypeScript bindings -keywords: [Nargo, Noir, compile, TypeScript] -sidebar_position: 3 ---- - -When using TypeScript, it is extra work to interpret Noir program outputs in a type-safe way. Third party libraries may exist for popular Noir programs, but they are either hard to find or unmaintained. - -Now you can generate TypeScript bindings for your Noir programs in two steps: - -1. Exporting Noir functions using `nargo export` -2. Using the TypeScript module `noir_codegen` to generate TypeScript binding - -**Note:** you can only export functions from a Noir *library* (not binary or contract program types). - -## Installation - -### Your TypeScript project - -If you don't already have a TypeScript project you can add the module with `yarn` (or `npm`), then initialize it: - -```bash -yarn add typescript -D -npx tsc --init -``` - -### Add TypeScript module - `noir_codegen` - -The following command will add the module to your project's devDependencies: - -```bash -yarn add @noir-lang/noir_codegen -D -``` - -### Nargo library - -Make sure you have Nargo, v0.25.0 or greater, installed. If you don't, follow the [installation guide](../getting_started/noir_installation.md). - -If you're in a new project, make a `circuits` folder and create a new Noir library: - -```bash -mkdir circuits && cd circuits -nargo new --lib myNoirLib -``` - -## Usage - -### Export ABI of specified functions - -First go to the `.nr` files in your Noir library, and add the `#[export]` macro to each function that you want to use in TypeScript. - -```rust -#[export] -fn your_function(... -``` - -From your Noir library (where `Nargo.toml` is), run the following command: - -```bash -nargo export -``` - -You will now have an `export` directory with a .json file per exported function. - -You can also specify the directory of Noir programs using `--program-dir`, for example: - -```bash -nargo export --program-dir=./circuits/myNoirLib -``` - -### Generate TypeScript bindings from exported functions - -To use the `noir-codegen` package we added to the TypeScript project: - -```bash -yarn noir-codegen ./export/your_function.json -``` - -This creates an `exports` directory with an `index.ts` file containing all exported functions. - -**Note:** adding `--out-dir` allows you to specify an output dir for your TypeScript bindings to go. Eg: - -```bash -yarn noir-codegen ./export/*.json --out-dir ./path/to/output/dir -``` - -## Example .nr function to .ts output - -Consider a Noir library with this function: - -```rust -#[export] -fn not_equal(x: Field, y: Field) -> bool { - x != y -} -``` - -After the export and codegen steps, you should have an `index.ts` like: - -```typescript -export type Field = string; - - -export const is_equal_circuit: CompiledCircuit = -{"abi":{"parameters":[{"name":"x","type":{"kind":"field"},"visibility":"private"},{"name":"y","type":{"kind":"field"},"visibility":"private"}],"return_type":{"abi_type":{"kind":"boolean"},"visibility":"private"}},"bytecode":"H4sIAAAAAAAA/7WUMQ7DIAxFQ0Krrr2JjSGYLVcpKrn/CaqqDQN12WK+hPBgmWd/wEyHbF1SS923uhOs3pfoChI+wKXMAXzIKyNj4PB0TFTYc0w5RUjoqeAeEu1wqK0F54RGkWvW44LPzExnlkbMEs4JNZmN8PxS42uHv82T8a3Jeyn2Ks+VLPcO558HmyLMCDOXAXXtpPt4R/Rt9T36ss6dS9HGPx/eG17nGegKBQAA"}; - -export async function is_equal(x: Field, y: Field, foreignCallHandler?: ForeignCallHandler): Promise { - const program = new Noir(is_equal_circuit); - const args: InputMap = { x, y }; - const { returnValue } = await program.execute(args, foreignCallHandler); - return returnValue as boolean; -} -``` - -Now the `is_equal()` function and relevant types are readily available for use in TypeScript. diff --git a/docs/versioned_docs/version-v0.37.0/tooling/debugger.md b/docs/versioned_docs/version-v0.37.0/tooling/debugger.md deleted file mode 100644 index 200b5fc423a..00000000000 --- a/docs/versioned_docs/version-v0.37.0/tooling/debugger.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: Debugger -description: Learn about the Noir Debugger, in its REPL or VS Code versions. -keywords: [Nargo, VSCode, Visual Studio Code, REPL, Debugger] -sidebar_position: 2 ---- - -# Noir Debugger - -There are currently two ways of debugging Noir programs: - -1. From VS 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). -2. Via the REPL debugger, which ships with Nargo. - -In order to use either version of the debugger, you will need to install recent enough versions of Noir, [Nargo](../getting_started/noir_installation.md) and vscode-noir: - -- Noir & Nargo ≥0.28.0 -- Noir's VS Code extension ≥0.0.11 - -:::info -At the moment, the debugger supports debugging binary projects, but not contracts. -::: - -We cover the VS Code Noir debugger more in depth in [its VS Code debugger how-to guide](../how_to/debugger/debugging_with_vs_code.md) and [the reference](../reference/debugger/debugger_vscode.md). - -The REPL debugger is discussed at length in [the REPL debugger how-to guide](../how_to/debugger/debugging_with_the_repl.md) and [the reference](../reference/debugger/debugger_repl.md). diff --git a/docs/versioned_docs/version-v0.37.0/tooling/language_server.md b/docs/versioned_docs/version-v0.37.0/tooling/language_server.md deleted file mode 100644 index 81e0356ef8a..00000000000 --- a/docs/versioned_docs/version-v0.37.0/tooling/language_server.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -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] -sidebar_position: 0 ---- - -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.37.0/tooling/testing.md b/docs/versioned_docs/version-v0.37.0/tooling/testing.md deleted file mode 100644 index 866677da567..00000000000 --- a/docs/versioned_docs/version-v0.37.0/tooling/testing.md +++ /dev/null @@ -1,79 +0,0 @@ ---- -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] -sidebar_position: 1 ---- - -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 all -the constraints 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); -} -``` - -The string given to `should_fail_with` doesn't need to exactly match the failure reason, it just needs to be a substring of it: - -```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 = "airspeed velocity")] -fn test_bridgekeeper() { - main(32); -} -``` \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.37.0/tutorials/noirjs_app.md b/docs/versioned_docs/version-v0.37.0/tutorials/noirjs_app.md deleted file mode 100644 index 6e69ea0bbed..00000000000 --- a/docs/versioned_docs/version-v0.37.0/tutorials/noirjs_app.md +++ /dev/null @@ -1,366 +0,0 @@ ---- -title: Building a web app with NoirJS -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, app] -sidebar_position: 0 -pagination_next: noir/concepts/data_types/index ---- - -NoirJS is a set of packages meant to work both in a browser and a server environment. In this tutorial, we will build a simple web app using them. From here, you should get an idea on how to proceed with your own Noir projects! - -You can find the complete app code for this guide [here](https://github.com/noir-lang/tiny-noirjs-app). - -## Setup - -:::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.31.x matches `noir_js@0.31.x`, etc. - -In this guide, we will be pinned to 0.31.0. - -::: - -Before we start, we want to make sure we have Node, Nargo and the Barretenberg proving system (`bb`) installed. - -We start by opening a terminal and executing `node --version`. If we don't get an output like `v20.10.0`, that means node is not installed. Let's do that by following the handy [nvm guide](https://github.com/nvm-sh/nvm?tab=readme-ov-file#install--update-script). - -As for `Nargo`, we can follow the [Nargo guide](../getting_started/quick_start.md) to install it. If you're lazy, just paste this on a terminal and run `noirup`: - -```sh -curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash -``` - -Follow the instructions on [this page](https://github.com/AztecProtocol/aztec-packages/tree/master/barretenberg/cpp/src/barretenberg/bb#installation) to install `bb`. -Version 0.41.0 is compatible with `nargo` version 0.31.0, which you can install with `bbup -v 0.41.0` once `bbup` is installed. - -Easy enough. Onwards! - -## Our project - -ZK is a powerful technology. An app that doesn't reveal one of the inputs to _anyone_ is almost unbelievable, yet Noir makes it as easy as a single line of code. - -In fact, it's so simple that it comes nicely packaged in `nargo`. Let's do that! - -### Nargo - -Run: - -```bash -nargo new circuit -``` - -And... That's about it. Your program is ready to be compiled and run. - -To compile, let's `cd` into the `circuit` folder to enter our project, and call: - -```bash -nargo compile -``` - -This compiles our circuit into `json` format and add it to a new `target` folder. - -:::info - -At this point in the tutorial, your folder structure should look like this: - -```tree -. -└── circuit <---- our working directory - ├── Nargo.toml - ├── src - │ └── main.nr - └── target - └── circuit.json -``` - -::: - -### Node and Vite - -If you want to explore Nargo, feel free to go on a side-quest now and follow the steps in the -[getting started](../getting_started/quick_start.md) guide. However, we want our app to run on the browser, so we need Vite. - -Vite is a powerful tool to generate static websites. While it provides all kinds of features, let's just go barebones with some good old vanilla JS. - -To do this this, go back to the previous folder (`cd ..`) and create a new vite project by running `npm create vite` and choosing "Vanilla" and "Javascript". - -A wild `vite-project` directory should now appear in your root folder! Let's not waste any time and dive right in: - -```bash -cd vite-project -``` - -### Setting Up Vite and Configuring the Project - -Before we proceed with any coding, let's get our environment tailored for Noir. We'll start by laying down the foundations with a `vite.config.js` file. This little piece of configuration is our secret sauce for making sure everything meshes well with the NoirJS libraries and other special setups we might need, like handling WebAssembly modules. Here’s how you get that going: - -#### Creating the vite.config.js - -In your freshly minted `vite-project` folder, create a new file named `vite.config.js` and open it in your code editor. Paste the following to set the stage: - -```javascript -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 { - build: { - target: 'esnext', - rollupOptions: { - external: ['@aztec/bb.js'] - } - }, - optimizeDeps: { - esbuildOptions: { - target: 'esnext' - } - }, - plugins: [ - copy({ - targets: [{ src: 'node_modules/**/*.wasm', dest: 'node_modules/.vite/dist' }], - copySync: true, - hook: 'buildStart', - }), - command === 'serve' ? wasmContentTypePlugin : [], - ], - }; - } - - return {}; -}); -``` - -#### Install Dependencies - -Now that our stage is set, install the necessary NoirJS packages along with our other dependencies: - -```bash -npm install && npm install @noir-lang/backend_barretenberg@0.31.0 @noir-lang/noir_js@0.31.0 -npm install rollup-plugin-copy --save-dev -``` - -:::info - -At this point in the tutorial, your folder structure should look like this: - -```tree -. -└── circuit - └── ...etc... -└── vite-project <---- our working directory - └── ...etc... -``` - -::: - -#### Some cleanup - -`npx create vite` is amazing but it creates a bunch of files we don't really need for our simple example. Actually, let's just delete everything except for `vite.config.js`, `index.html`, `main.js` and `package.json`. I feel lighter already. - -![my heart is ready for you, noir.js](@site/static/img/memes/titanic.jpeg) - -## HTML - -Our app won't run like this, of course. We need some working HTML, at least. Let's open our broken-hearted `index.html` and replace everything with this code snippet: - -```html - - - - - - -

Noir app

-
- - -
-
-

Logs

-

Proof

-
- - -``` - -It _could_ be a beautiful UI... Depending on which universe you live in. - -## Some good old vanilla Javascript - -Our love for Noir needs undivided attention, so let's just open `main.js` and delete everything (this is where the romantic scenery becomes a bit creepy). - -Start by pasting in this boilerplate code: - -```js -function display(container, msg) { - const c = document.getElementById(container); - const p = document.createElement('p'); - p.textContent = msg; - c.appendChild(p); -} - -document.getElementById('submitGuess').addEventListener('click', async () => { - try { - // here's where love happens - } catch (err) { - display('logs', 'Oh 💔 Wrong guess'); - } -}); -``` - -The display function doesn't do much. We're simply manipulating our website to see stuff happening. For example, if the proof fails, it will simply log a broken heart 😢 - -:::info - -At this point in the tutorial, your folder structure should look like this: - -```tree -. -└── circuit - └── ...same as above -└── vite-project - ├── vite.config.js - ├── main.js - ├── package.json - └── index.html -``` - -You'll see other files and folders showing up (like `package-lock.json`, `node_modules`) but you shouldn't have to care about those. - -::: - -## Some NoirJS - -We're starting with the good stuff now. If you've compiled the circuit as described above, you should have a `json` file we want to import at the very top of our `main.js` file: - -```ts -import circuit from '../circuit/target/circuit.json'; -``` - -[Noir is backend-agnostic](../index.mdx#whats-new-about-noir). We write Noir, but we also need a proving backend. That's why we need to import and instantiate the two dependencies we installed above: `BarretenbergBackend` and `Noir`. Let's import them right below: - -```js -import { BarretenbergBackend, BarretenbergVerifier as Verifier } from '@noir-lang/backend_barretenberg'; -import { Noir } from '@noir-lang/noir_js'; -``` - -And instantiate them inside our try-catch block: - -```ts -// try { -const backend = new BarretenbergBackend(circuit); -const noir = new Noir(circuit); -// } -``` - -:::note - -For the remainder of the tutorial, everything will be happening inside the `try` block - -::: - -## Our app - -Now for the app itself. We're capturing whatever is in the input when people press the submit button. Just add this: - -```js -const x = parseInt(document.getElementById('guessInput').value); -const input = { x, y: 2 }; -``` - -Now we're ready to prove stuff! Let's feed some inputs to our circuit and calculate the proof: - -```js -await setup(); // let's squeeze our wasm inits here - -display('logs', 'Generating proof... ⌛'); -const { witness } = await noir.execute(input); -const proof = await backend.generateProof(witness); -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 run dev`. If it doesn't open a browser for you, just visit `localhost:5173`. You should now see the worst UI ever, with an ugly input. - -![Getting Started 0](@site/static/img/noir_getting_started_1.png) - -Now, our circuit says `fn main(x: Field, y: pub Field)`. This means only the `y` value is public, and it's hardcoded above: `input = { x, y: 2 }`. In other words, you won't need to send your secret`x` to the verifier! - -By inputting any number other than 2 in the input box and clicking "submit", you should get a valid proof. Otherwise the proof won't even generate correctly. By the way, if you're human, you shouldn't be able to understand anything on the "proof" box. That's OK. We like you, human ❤️. - -## Verifying - -Time to celebrate, yes! But we shouldn't trust machines so blindly. Let's add these lines to see our proof being verified: - -```js -display('logs', 'Verifying proof... ⌛'); -const isValid = await backend.verifyProof(proof); - -// or to cache and use the verification key: -// const verificationKey = await backend.getVerificationKey(); -// const verifier = new Verifier(); -// const isValid = await verifier.verifyProof(proof, verificationKey); - -if (isValid) display('logs', 'Verifying proof... ✅'); -``` - -You have successfully generated a client-side Noir web app! - -![coded app without math knowledge](@site/static/img/memes/flextape.jpeg) - -## 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/vite-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. - -## UltraHonk Backend - -Barretenberg has recently exposed a new UltraHonk backend. We can use UltraHonk in NoirJS after version 0.33.0. Everything will be the same as the tutorial above, except that the class we need to import will change: - -```js -import { UltraHonkBackend, UltraHonkVerifier as Verifier } from '@noir-lang/backend_barretenberg'; -``` - -The backend will then be instantiated as such: - -```js -const backend = new UltraHonkBackend(circuit); -``` - -Then all the commands to prove and verify your circuit will be same. - -The only feature currently unsupported with UltraHonk are [recursive proofs](../explainers/explainer-recursion.md). diff --git a/docs/versioned_docs/version-v0.38.0/explainers/cspell.json b/docs/versioned_docs/version-v0.38.0/explainers/cspell.json deleted file mode 100644 index c60b0a597b1..00000000000 --- a/docs/versioned_docs/version-v0.38.0/explainers/cspell.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "words": [ - "Cryptdoku" - ] -} diff --git a/docs/versioned_docs/version-v0.38.0/explainers/explainer-oracle.md b/docs/versioned_docs/version-v0.38.0/explainers/explainer-oracle.md deleted file mode 100644 index 821e1f95c04..00000000000 --- a/docs/versioned_docs/version-v0.38.0/explainers/explainer-oracle.md +++ /dev/null @@ -1,57 +0,0 @@ ---- -title: Oracles -description: This guide provides an in-depth understanding of how Oracles work in Noir programming. Learn how to use outside calculations in your programs, constrain oracles, and understand their uses and limitations. -keywords: - - Noir Programming - - Oracles - - JSON-RPC - - Foreign Call Handlers - - Constrained Functions - - Blockchain Programming -sidebar_position: 1 ---- - -If you've seen "The Matrix" you may recall "The Oracle" as Gloria Foster smoking cigarettes and baking cookies. While she appears to "know things", she is actually providing a calculation of a pre-determined future. Noir Oracles are similar, in a way. They don't calculate the future (yet), but they allow you to use outside calculations in your programs. - -![matrix oracle prediction](@site/static/img/memes/matrix_oracle.jpeg) - -A Noir program is usually self-contained. You can pass certain inputs to it, and it will generate a deterministic output for those inputs. But what if you wanted to defer some calculation to an outside process or source? - -Oracles are functions that provide this feature. - -## Use cases - -An example usage for Oracles is proving something on-chain. For example, proving that the ETH-USDC quote was below a certain target at a certain block time. Or even making more complex proofs like proving the ownership of an NFT as an anonymous login method. - -Another interesting use case is to defer expensive calculations to be made outside of the Noir program, and then constraining the result; similar to the use of [unconstrained functions](../noir/concepts//unconstrained.md). - -In short, anything that can be constrained in a Noir program but needs to be fetched from an external source is a great candidate to be used in oracles. - -## Constraining oracles - -Just like in The Matrix, Oracles are powerful. But with great power, comes great responsibility. Just because you're using them in a Noir program doesn't mean they're true. Noir has no superpowers. If you want to prove that Portugal won the Euro Cup 2016, you're still relying on potentially untrusted information. - -To give a concrete example, Alice wants to login to the [NounsDAO](https://nouns.wtf/) forum with her username "noir_nouner" by proving she owns a noun without revealing her ethereum address. Her Noir program could have an oracle call like this: - -```rust -#[oracle(getNoun)] -unconstrained fn get_noun(address: Field) -> Field -``` - -This oracle could naively resolve with the number of Nouns she possesses. However, it is useless as a trusted source, as the oracle could resolve to anything Alice wants. In order to make this oracle call actually useful, Alice would need to constrain the response from the oracle, by proving her address and the noun count belongs to the state tree of the contract. - -In short, **Oracles don't prove anything. Your Noir program does.** - -:::danger - -If you don't constrain the return of your oracle, you could be clearly opening an attack vector on your Noir program. Make double-triple sure that the return of an oracle call is constrained! - -::: - -## How to use Oracles - -On CLI, Nargo resolves oracles by making JSON RPC calls, which means it would require an RPC node to be running. - -In JavaScript, NoirJS accepts and resolves arbitrary call handlers (that is, not limited to JSON) as long as they match the expected types the developer defines. Refer to [Foreign Call Handler](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) to learn more about NoirJS's call handling. - -If you want to build using oracles, follow through to the [oracle guide](../how_to/how-to-oracles.md) for a simple example on how to do that. diff --git a/docs/versioned_docs/version-v0.38.0/explainers/explainer-recursion.md b/docs/versioned_docs/version-v0.38.0/explainers/explainer-recursion.md deleted file mode 100644 index df8529ef4e0..00000000000 --- a/docs/versioned_docs/version-v0.38.0/explainers/explainer-recursion.md +++ /dev/null @@ -1,176 +0,0 @@ ---- -title: Recursive proofs -description: Explore the concept of recursive proofs in Zero-Knowledge programming. Understand how recursion works in Noir, a language for writing smart contracts on the EVM blockchain. Learn through practical examples like Alice and Bob's guessing game, Charlie's recursive merkle tree, and Daniel's reusable components. Discover how to use recursive proofs to optimize computational resources and improve efficiency. - -keywords: - [ - "Recursive Proofs", - "Zero-Knowledge Programming", - "Noir", - "EVM Blockchain", - "Smart Contracts", - "Recursion in Noir", - "Alice and Bob Guessing Game", - "Recursive Merkle Tree", - "Reusable Components", - "Optimizing Computational Resources", - "Improving Efficiency", - "Verification Key", - "Aggregation", - "Recursive zkSNARK schemes", - "PLONK", - "Proving and Verification Keys" - ] -sidebar_position: 1 -pagination_next: how_to/how-to-recursion ---- - -In programming, we tend to think of recursion as something calling itself. A classic example would be the calculation of the factorial of a number: - -```js -function factorial(n) { - if (n === 0 || n === 1) { - return 1; - } else { - return n * factorial(n - 1); - } -} -``` - -In this case, while `n` is not `1`, this function will keep calling itself until it hits the base case, bubbling up the result on the call stack: - -```md - Is `n` 1? <--------- - /\ / - / \ n = n -1 - / \ / - Yes No -------- -``` - -In Zero-Knowledge, recursion has some similarities. - -It is not a Noir function calling itself, but a proof being used as an input to another circuit. In short, you verify one proof *inside* another proof, returning the proof that both proofs are valid. - -This means that, given enough computational resources, you can prove the correctness of any arbitrary number of proofs in a single proof. This could be useful to design state channels (for which a common example would be [Bitcoin's Lightning Network](https://en.wikipedia.org/wiki/Lightning_Network)), to save on gas costs by settling one proof on-chain, or simply to make business logic less dependent on a consensus mechanism. - -## Examples - -Let us look at some of these examples - -### Alice and Bob - Guessing game - -Alice and Bob are friends, and they like guessing games. They want to play a guessing game online, but for that, they need a trusted third-party that knows both of their secrets and finishes the game once someone wins. - -So, they use zero-knowledge proofs. Alice tries to guess Bob's number, and Bob will generate a ZK proof stating whether she succeeded or failed. - -This ZK proof can go on a smart contract, revealing the winner and even giving prizes. However, this means every turn needs to be verified on-chain. This incurs some cost and waiting time that may simply make the game too expensive or time-consuming to be worth it. - -As a solution, Alice proposes the following: "what if Bob generates his proof, and instead of sending it on-chain, I verify it *within* my own proof before playing my own turn?". - -She can then generate a proof that she verified his proof, and so on. - -```md - Did you fail? <-------------------------- - / \ / - / \ n = n -1 - / \ / - Yes No / - | | / - | | / - | You win / - | / - | / -Generate proof of that / - + / - my own guess ---------------- -``` - -### Charlie - Recursive merkle tree - -Charlie is a concerned citizen, and wants to be sure his vote in an election is accounted for. He votes with a ZK proof, but he has no way of knowing that his ZK proof was included in the total vote count! - -If the vote collector puts all of the votes into a [Merkle tree](https://en.wikipedia.org/wiki/Merkle_tree), everyone can prove the verification of two proofs within one proof, as such: - -```md - abcd - __________|______________ - | | - ab cd - _____|_____ ______|______ - | | | | - alice bob charlie daniel -``` - -Doing this recursively allows us to arrive on a final proof `abcd` which if true, verifies the correctness of all the votes. - -### Daniel - Reusable components - -Daniel has a big circuit and a big headache. A part of his circuit is a setup phase that finishes with some assertions that need to be made. But that section alone takes most of the proving time, and is largely independent of the rest of the circuit. - -He might find it more efficient to generate a proof for that setup phase separately, and verify that proof recursively in the actual business logic section of his circuit. This will allow for parallelization of both proofs, which results in a considerable speedup. - -## What params do I need - -As you can see in the [recursion reference](noir/standard_library/recursion.mdx), a simple recursive proof requires: - -- The proof to verify -- The Verification Key of the circuit that generated the proof -- A hash of this verification key, as it's needed for some backends -- The public inputs for the proof - -:::info - -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. - -So, taking the example of Alice and Bob and their guessing game: - -- Alice makes her guess. Her proof is *not* recursive: it doesn't verify any proof within it! It's just a standard `assert(x != y)` circuit -- Bob verifies Alice's proof and makes his own guess. In this circuit, he doesn't exactly *prove* the verification of Alice's proof. Instead, he *aggregates* his proof to Alice's proof. The actual verification is done when the full proof is verified, for example when using `nargo verify` or through the verifier smart contract. - -We can imagine recursive proofs a [relay race](https://en.wikipedia.org/wiki/Relay_race). The first runner doesn't have to receive the baton from anyone else, as he/she already starts with it. But when his/her turn is over, the next runner needs to receive it, run a bit more, and pass it along. Even though every runner could theoretically verify the baton mid-run (why not? 🏃🔍), only at the end of the race does the referee verify that the whole race is valid. - -::: - -## Some architecture - -As with everything in computer science, there's no one-size-fits all. But there are some patterns that could help understanding and implementing them. To give three examples: - -### Adding some logic to a proof verification - -This would be an approach for something like our guessing game, where proofs are sent back and forth and are verified by each opponent. This circuit would be divided in two sections: - -- A `recursive verification` section, which would be just the call to `std::verify_proof`, and that would be skipped on the first move (since there's no proof to verify) -- A `guessing` section, which is basically the logic part where the actual guessing happens - -In such a situation, and assuming Alice is first, she would skip the first part and try to guess Bob's number. Bob would then verify her proof on the first section of his run, and try to guess Alice's number on the second part, and so on. - -### Aggregating proofs - -In some one-way interaction situations, recursion would allow for aggregation of simple proofs that don't need to be immediately verified on-chain or elsewhere. - -To give a practical example, a barman wouldn't need to verify a "proof-of-age" on-chain every time he serves alcohol to a customer. Instead, the architecture would comprise two circuits: - -- A `main`, non-recursive circuit with some logic -- A `recursive` circuit meant to verify two proofs in one proof - -The customer's proofs would be intermediate, and made on their phones, and the barman could just verify them locally. He would then aggregate them into a final proof sent on-chain (or elsewhere) at the end of the day. - -### Recursively verifying different circuits - -Nothing prevents you from verifying different circuits in a recursive proof, for example: - -- A `circuit1` circuit -- A `circuit2` circuit -- A `recursive` circuit - -In this example, a regulator could verify that taxes were paid for a specific purchase by aggregating both a `payer` circuit (proving that a purchase was made and taxes were paid), and a `receipt` circuit (proving that the payment was received) - -## How fast is it - -At the time of writing, verifying recursive proofs is surprisingly fast. This is because most of the time is spent on generating the verification key that will be used to generate the next proof. So you are able to cache the verification key and reuse it later. - -Currently, Noir JS packages don't expose the functionality of loading proving and verification keys, but that feature exists in the underlying `bb.js` package. - -## How can I try it - -Learn more about using recursion in Nargo and NoirJS in the [how-to guide](../how_to/how-to-recursion.md) and see a full example in [noir-examples](https://github.com/noir-lang/noir-examples). diff --git a/docs/versioned_docs/version-v0.38.0/explainers/explainer-writing-noir.md b/docs/versioned_docs/version-v0.38.0/explainers/explainer-writing-noir.md deleted file mode 100644 index 3ce4245dc45..00000000000 --- a/docs/versioned_docs/version-v0.38.0/explainers/explainer-writing-noir.md +++ /dev/null @@ -1,177 +0,0 @@ ---- -title: Thinking in Circuits -description: Considerations when writing Noir programs -keywords: [Noir, programming, rust] -tags: [Optimization] -sidebar_position: 0 ---- - - -This article intends to set you up with key concepts essential for writing more viable applications that use zero knowledge proofs, namely around efficient circuits. - -## Context - 'Efficient' is subjective - -When writing a web application for a performant computer with high-speed internet connection, writing efficient code sometimes is seen as an afterthought only if needed. Large multiplications running at the innermost of nested loops may not even be on a dev's radar. -When writing firmware for a battery-powered microcontroller, you think of cpu cycles as rations to keep within a product's power budget. - -> Code is written to create applications that perform specific tasks within specific constraints - -And these constraints differ depending on where the compiled code is execute. - -### The Ethereum Virtual Machine (EVM) - -In scenarios where extremely low gas costs are required for an Ethereum application to be viable/competitive, Ethereum smart contract developers get into what is colloquially known as: "*gas golfing*". Finding the lowest execution cost of their compiled code (EVM bytecode) to achieve a specific task. - -The equivalent optimization task when writing zk circuits is affectionately referred to as "*gate golfing*", finding the lowest gate representation of the compiled Noir code. - -### Coding for circuits - a paradigm shift - -In zero knowledge cryptography, code is compiled to "circuits" consisting of arithmetic gates, and gate count is the significant cost. Depending on the proving system this is linearly proportionate to proving time, and so from a product point this should be kept as low as possible. - -Whilst writing efficient code for web apps and Solidity has a few key differences, writing efficient circuits have a different set of considerations. It is a bit of a paradigm shift, like writing code for GPUs for the first time... - -For example, drawing a circle at (0, 0) of radius `r`: -- For a single CPU thread, -``` -for theta in 0..2*pi { - let x = r * cos(theta); - let y = r * sin(theta); - draw(x, y); -} // note: would do 0 - pi/2 and draw +ve/-ve x and y. -``` - -- For GPUs (simultaneous parallel calls with x, y across image), -``` -if (x^2 + y^2 = r^2) { - draw(x, y); -} -``` - -([Related](https://www.youtube.com/watch?v=-P28LKWTzrI)) - -Whilst this CPU -> GPU does not translate to circuits exactly, it is intended to exemplify the difference in intuition when coding for different machine capabilities/constraints. - -### Context Takeaway - -For those coming from a primarily web app background, this article will explain what you need to consider when writing circuits. Furthermore, for those experienced writing efficient machine code, prepare to shift what you think is efficient 😬 - -## Translating from Rust - -For some applications using Noir, existing code might be a convenient starting point to then proceed to optimize the gate count of. - -:::note -Many valuable functions and algorithms have been written in more established languages (C/C++), and converted to modern ones (like Rust). -::: - -Fortunately for Noir developers, when needing a particular function a Rust implementation can be readily compiled into Noir with some key changes. While the compiler does a decent amount of optimizations, it won't be able to change code that has been optimized for clock-cycles into code optimized for arithmetic gates. - -A few things to do when converting Rust code to Noir: -- `println!` is not a macro, use `println` function (same for `assert_eq`) -- No early `return` in function. Use constrain via assertion instead -- No passing by reference. Remove `&` operator to pass by value (copy) -- No boolean operators (`&&`, `||`). Use bitwise operators (`&`, `|`) with boolean values -- No type `usize`. Use types `u8`, `u32`, `u64`, ... -- `main` return must be public, `pub` -- No `const`, use `global` -- Noir's LSP is your friend, so error message should be informative enough to resolve syntax issues. - -## Writing efficient Noir for performant products - -The following points help refine our understanding over time. - -:::note -A Noir program makes a statement that can be verified. -::: - -It compiles to a structure that represents the calculation, and can assert results within the calculation at any stage (via the `constrain` keyword). - -A Noir program compiles to an Abstract Circuit Intermediate Representation which is: - - Conceptually a tree structure - - Leaves (inputs) are the `Field` type - - Nodes contain arithmetic operations to combine them (gates) - - The root is the final result (return value) - -:::tip -The command `nargo info` shows the programs circuit size, and is useful to compare the value of changes made. -You can dig deeper and use the `--print-acir` param to take a closer look at individual ACIR opcodes, and the proving backend to see its gate count (eg for barretenberg, `bb gates -b ./target/program.json`). -::: - -### Use the `Field` type - -Since the native type of values in circuits are `Field`s, using them for variables in Noir means less gates converting them under the hood. -Some things to be mindful of when using a Field type for a regular integer value: -- A variable of type `Field` can be cast `as` an integer type (eg `u8`, `u64`) - - Note: this retains only the bits of the integer type. Eg a Field value of 260 as a `u8` becomes 4 -- For Field types arithmetic operations meaningfully overflow/underflow, yet for integer types they are checked according to their size -- Comparisons and bitwise operations do not exist for `Field`s, cast to an appropriately sized integer type when you need to - -:::tip -Where possible, use `Field` type for values. Using smaller value types, and bit-packing strategies, will result in MORE gates -::: - - -### Use Arithmetic over non-arithmetic operations - -Since circuits are made of arithmetic gates, the cost of arithmetic operations tends to be one gate. Whereas for procedural code, they represent several clock cycles. - -Inversely, non-arithmetic operators are achieved with multiple gates, vs 1 clock cycle for procedural code. - -| (cost\op) | arithmetic
(`*`, `+`) | bit-wise ops
(eg `<`, `\|`, `>>`) | -| - | - | - | -| **cycles** | 10+ | 1 | -| **gates** | 1 | 10+ | - -Bit-wise operations (e.g. bit shifts `<<` and `>>`), albeit commonly used in general programming and especially for clock cycle optimizations, are on the contrary expensive in gates when performed within circuits. - -Translate away from bit shifts when writing constrained functions for the best performance. - -On the flip side, feel free to use bit shifts in unconstrained functions and tests if necessary, as they are executed outside of circuits and does not induce performance hits. - -### Use static over dynamic values - -Another general theme that manifests in different ways is that static reads are represented with less gates than dynamic ones. - -Reading from read-only memory (ROM) adds less gates than random-access memory (RAM), 2 vs ~3.25 due to the additional bounds checks. Arrays of fixed length (albeit used at a lower capacity), will generate less gates than dynamic storage. - -Related to this, if an index used to access an array is not known at compile time (ie unknown until run time), then ROM will be converted to RAM, expanding the gate count. - -:::tip -Use arrays and indices that are known at compile time where possible. -Using `assert_constant(i);` before an index, `i`, is used in an array will give a compile error if `i` is NOT known at compile time. -::: - -### Leverage unconstrained execution - -Constrained verification can leverage unconstrained execution, this is especially useful for operations that are represented by many gates. -Use an [unconstrained function](../noir/concepts/unconstrained.md) to perform gate-heavy calculations, then verify and constrain the result. - -Eg division generates more gates than multiplication, so calculating the quotient in an unconstrained function then constraining the product for the quotient and divisor (+ any remainder) equals the dividend will be more efficient. - -Use ` if is_unconstrained() { /`, to conditionally execute code if being called in an unconstrained vs constrained way. - -## Advanced - -Unless you're well into the depth of gate optimization, this advanced section can be ignored. - -### Combine arithmetic operations - -A Noir program can be honed further by combining arithmetic operators in a way that makes the most of each constraint of the backend proving system. This is in scenarios where the backend might not be doing this perfectly. - -Eg Barretenberg backend (current default for Noir) is a width-4 PLONKish constraint system -$ w_1*w_2*q_m + w_1*q_1 + w_2*q_2 + w_3*q_3 + w_4*q_4 + q_c = 0 $ - -Here we see there is one occurrence of witness 1 and 2 ($w_1$, $w_2$) being multiplied together, with addition to witnesses 1-4 ($w_1$ .. $w_4$) multiplied by 4 corresponding circuit constants ($q_1$ .. $q_4$) (plus a final circuit constant, $q_c$). - -Use `nargo info --print-acir`, to inspect the ACIR opcodes (and the proving system for gates), and it may present opportunities to amend the order of operations and reduce the number of constraints. - -#### Variable as witness vs expression - -If you've come this far and really know what you're doing at the equation level, a temporary lever (that will become unnecessary/useless over time) is: `std::as_witness`. This informs the compiler to save a variable as a witness not an expression. - -The compiler will mostly be correct and optimal, but this may help some near term edge cases that are yet to optimize. -Note: When used incorrectly it will create **less** efficient circuits (higher gate count). - -## References -- Guillaume's ["`Cryptdoku`" talk](https://www.youtube.com/watch?v=MrQyzuogxgg) (Jun'23) -- Tips from Tom, Jake and Zac. -- [Idiomatic Noir](https://www.vlayer.xyz/blog/idiomatic-noir-part-1-collections) blog post diff --git a/docs/versioned_docs/version-v0.38.0/getting_started/noir_installation.md b/docs/versioned_docs/version-v0.38.0/getting_started/noir_installation.md deleted file mode 100644 index a5c7e649278..00000000000 --- a/docs/versioned_docs/version-v0.38.0/getting_started/noir_installation.md +++ /dev/null @@ -1,106 +0,0 @@ ---- -title: Standalone Noir Installation -description: There are different ways to install Nargo, the one-stop shop and command-line tool for developing Noir programs. This guide explains how to specify which version to install when using noirup, and using WSL for windows. -keywords: [ - Installation - Nargo - Noirup - Binaries - Compiling from Source - WSL for Windows - macOS - Linux - Nix - Direnv - Uninstalling Nargo - ] -sidebar_position: 2 ---- - -Noirup is the endorsed method for installing Nargo, streamlining the process of fetching binaries or compiling from source. It supports a range of options to cater to your specific needs, from nightly builds and specific versions to compiling from various sources. - -### Installing Noirup - -First, ensure you have `noirup` installed: - -```sh -curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash -``` - -### Fetching Binaries - -With `noirup`, you can easily switch between different Nargo versions, including nightly builds: - -- **Nightly Version**: Install the latest nightly build. - - ```sh - noirup --version nightly - ``` - -- **Specific Version**: Install a specific version of Nargo. - - ```sh - noirup --version - ``` - -### Compiling from Source - -`noirup` also enables compiling Nargo from various sources: - -- **From a Specific Branch**: Install from the latest commit on a branch. - - ```sh - noirup --branch - ``` - -- **From a Fork**: Install from the main branch of a fork. - - ```sh - noirup --repo - ``` - -- **From a Specific Branch in a Fork**: Install from a specific branch in a fork. - - ```sh - noirup --repo --branch - ``` - -- **From a Specific Pull Request**: Install from a specific PR. - - ```sh - noirup --pr - ``` - -- **From a Specific Commit**: Install from a specific commit. - - ```sh - noirup -C - ``` - -- **From Local Source**: Compile and install from a local directory. - - ```sh - noirup --path ./path/to/local/source - ``` - -## Installation on Windows - -The default backend for Noir (Barretenberg) doesn't provide Windows binaries at this time. For that reason, Noir cannot be installed natively. 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](#installing-noirup). - -## Setting up shell completions - -Once `nargo` is installed, you can [set up shell completions for it](setting_up_shell_completions). - -## Uninstalling Nargo - -If you installed Nargo with `noirup`, you can uninstall Nargo by removing the files in `~/.nargo`, `~/nargo`, and `~/noir_cache`. This ensures that all installed binaries, configurations, and cache related to Nargo are fully removed from your system. - -```bash -rm -r ~/.nargo -rm -r ~/nargo -rm -r ~/noir_cache -``` diff --git a/docs/versioned_docs/version-v0.38.0/getting_started/project_breakdown.md b/docs/versioned_docs/version-v0.38.0/getting_started/project_breakdown.md deleted file mode 100644 index e442e377040..00000000000 --- a/docs/versioned_docs/version-v0.38.0/getting_started/project_breakdown.md +++ /dev/null @@ -1,159 +0,0 @@ ---- -title: Project Breakdown -description: - Learn about the anatomy of a Nargo project, including the purpose of the Prover TOML - file, and how to prove and verify your program. -keywords: - [Nargo, Nargo project, Prover.toml, proof verification, private asset transfer] -sidebar_position: 1 ---- - -This section breaks down our hello world program from 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 - - 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. - -### 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 = "noir_starter" -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](../noir/modules_packages_crates/workspaces.md) will look a bit different. For example: - -```toml -[workspace] -members = ["crates/a", "crates/b"] -default-member = "crates/a" -``` - -#### Package section - -The package section defines 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) -- `expression_width` (optional) - Sets the default backend expression width. This field will override the default backend expression width specified by the Noir compiler (currently set to width 4). - -#### Dependencies section - -This is where you will specify any dependencies for your project. See the [Dependencies page](../noir/modules_packages_crates/dependencies.md) 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 that the condition to be satisfied (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 the inputs to the Noir program (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 execute` is executed, nargo will execute the Noir program using the inputs specified in `Prover.toml`, aborting if it finds that these do not satisfy the constraints defined by `main`. In this example, `x` and `y` must satisfy the inequality constraint `assert(x != y)`. - -If an output name is specified such as `nargo execute foo`, the witness generated by this execution will be written to `./target/foo.gz`. This can then be used to generate a proof of the execution. - -#### 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 execution by using the `--prover-name` or `-p` flags. - -This command looks for proof inputs in the default **Prover.toml** and generates the witness and saves it at `./target/foo.gz`: - -```bash -nargo execute foo -``` - -This command looks for proof inputs in the custom **OtherProver.toml** and generates the witness and saves it at `./target/bar.gz`: - -```bash -nargo execute -p OtherProver bar -``` - -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.38.0/getting_started/quick_start.md b/docs/versioned_docs/version-v0.38.0/getting_started/quick_start.md deleted file mode 100644 index 7deeae12fd9..00000000000 --- a/docs/versioned_docs/version-v0.38.0/getting_started/quick_start.md +++ /dev/null @@ -1,126 +0,0 @@ ---- -title: Quick Start -tags: [] -sidebar_position: 0 ---- - -## Installation - -### Noir - -The easiest way to develop with Noir is using Nargo the CLI tool. It provides you the ability to start new projects, compile, execute and test Noir programs from the terminal. - -You can use `noirup` the installation script to quickly install and update Nargo: - -```bash -curl -L https://raw.githubusercontent.com/noir-lang/noirup/refs/heads/main/install | bash -noirup -``` - -Once installed, you can [set up shell completions for the `nargo` command](setting_up_shell_completions). - -### Proving backend - -After installing Noir, we install a proving backend to work with our Noir programs. - -Proving backends provide you the abilities to generate proofs, verify proofs, generate smart contracts and more for your Noir programs. - -Different proving backends provide different tools for working with Noir programs, here we will use the [Barretenberg proving backend](https://github.com/AztecProtocol/aztec-packages/tree/master/barretenberg) developed by Aztec Labs as an example. - -You can use the `bbup` installation script to quickly install and update BB, Barretenberg's CLI tool: - -You can find the full list of proving backends compatible with Noir in Awesome Noir. - -```bash -curl -L https://raw.githubusercontent.com/AztecProtocol/aztec-packages/refs/heads/master/barretenberg/bbup/install | bash -bbup -``` - -For the full list of proving backends compatible with Noir, visit [Awesome Noir](https://github.com/noir-lang/awesome-noir/?tab=readme-ov-file#proving-backends). - -## Nargo - -Nargo provides the ability to initiate and execute Noir projects. Let's initialize the traditional `hello_world`: - -```sh -nargo new hello_world -``` - -Two files will be created. - -- `src/main.nr` contains a simple boilerplate circuit -- `Nargo.toml` contains environmental options, such as name, author, dependencies, and others. - -Glancing at _main.nr_ , we can see that inputs in Noir are private by default, but can be labeled public using the keyword `pub`. This means that we will _assert_ that we know a value `x` which is different from `y` without revealing `x`: - -```rust -fn main(x : Field, y : pub Field) { - assert(x != y); -} -``` - -To learn more about private and public values, check the [Data Types](../noir/concepts/data_types/index.md) section. - -### Compiling and executing - -We can now use `nargo` to generate a _Prover.toml_ file, where our input values will be specified: - -```sh -cd hello_world -nargo check - -Let's feed some valid values into this file: - -```toml -x = "1" -y = "2" -``` - -We're now ready to compile and execute our Noir program. By default the `nargo execute` command will do both, and generate the `witness` that we need to feed to our proving backend: - -```sh -nargo execute -``` - -The witness corresponding to this execution will then be written to the file _./target/witness-name.gz_. - -The command also automatically compiles your Noir program if it was not already / was edited, which you may notice the compiled artifacts being written to the file _./target/hello_world.json_. - -With circuit compiled and witness generated, we're ready to prove. - -## Proving backend - -Different proving backends may provide different tools and commands to work with Noir programs. Here Barretenberg's `bb` CLI tool is used as an example: - -```sh -bb prove -b ./target/hello_world.json -w ./target/hello_world.gz -o ./target/proof -``` - -:::tip - -Naming can be confusing, specially as you pass them to the `bb` commands. If unsure, it won't hurt to delete the target folder and start anew to make sure you're using the most recent versions of the compiled circuit and witness. - -::: - -The proof is now generated in the `target` folder. To verify it we first need to compute the verification key from the compiled circuit, and use it to verify: - -```sh -bb write_vk -b ./target/hello_world.json -o ./target/vk -bb verify -k ./target/vk -p ./target/proof -``` - -:::info - -Notice that in order to verify a proof, the verifier knows nothing but the circuit, which is compiled and used to generate the verification key. This is obviously quite important: private inputs remain private. - -As for the public inputs, you may have noticed they haven't been specified. This behavior varies with each particular backend, but barretenberg typically attaches them to the proof. You can see them by parsing and splitting it. For example for if your public inputs are 32 bytes: - -```bash -head -c 32 ./target/proof | od -An -v -t x1 | tr -d $' \n' -``` - -::: - -Congratulations, you have now created and verified a proof for your very first Noir program! - -In the [next section](./project_breakdown.md), we will go into more detail on each step performed. diff --git a/docs/versioned_docs/version-v0.38.0/getting_started/setting_up_shell_completions.md b/docs/versioned_docs/version-v0.38.0/getting_started/setting_up_shell_completions.md deleted file mode 100644 index 0447321cbab..00000000000 --- a/docs/versioned_docs/version-v0.38.0/getting_started/setting_up_shell_completions.md +++ /dev/null @@ -1,87 +0,0 @@ ---- -title: Setting up shell completions -tags: [] -sidebar_position: 3 ---- - -The `nargo` binary provides a command to generate shell completions: - -```bash -nargo generate-completion-script [shell] -``` - -where `shell` must be one of `bash`, `elvish`, `fish`, `powershell`, and `zsh`. - -Below we explain how to install them in some popular shells. - -## Installing Zsh Completions - -If you have `oh-my-zsh` installed, you might already have a directory of automatically loading completion scripts — `.oh-my-zsh/completions`. -If not, first create it: - -```bash -mkdir -p ~/.oh-my-zsh/completions` -``` - -Then copy the completion script to that directory: - -```bash -nargo generate-completion-script zsh > ~/.oh-my-zsh/completions/_nargo -``` - -Without `oh-my-zsh`, you’ll need to add a path for completion scripts to your function path, and turn on completion script auto-loading. -First, add these lines to `~/.zshrc`: - -```bash -fpath=(~/.zsh/completions $fpath) -autoload -U compinit -compinit -``` - -Next, create a directory at `~/.zsh/completions`: - -```bash -mkdir -p ~/.zsh/completions -``` - -Then copy the completion script to that directory: - -```bash -nargo generate-completion-script zsh > ~/.zsh/completions/_nargo -``` - -## Installing Bash Completions - -If you have [bash-completion](https://github.com/scop/bash-completion) installed, you can just copy the completion script to the `/usr/local/etc/bash_completion.d` directory: - -```bash -nargo generate-completion-script bash > /usr/local/etc/bash_completion.d/nargo -``` - -Without `bash-completion`, you’ll need to source the completion script directly. -First create a directory such as `~/.bash_completions/`: - -```bash -mkdir ~/.bash_completions/ -``` - -Copy the completion script to that directory: - -```bash -nargo generate-completion-script bash > ~/.bash_completions/nargo.bash -``` - -Then add the following line to `~/.bash_profile` or `~/.bashrc`: - - -```bash -source ~/.bash_completions/nargo.bash -``` - -## Installing Fish Completions - -Copy the completion script to any path listed in the environment variable `$fish_completion_path`. For example, a typical location is `~/.config/fish/completions/nargo.fish`: - -```bash -nargo generate-completion-script fish > ~/.config/fish/completions/nargo.fish -``` diff --git a/docs/versioned_docs/version-v0.38.0/how_to/_category_.json b/docs/versioned_docs/version-v0.38.0/how_to/_category_.json deleted file mode 100644 index 23b560f610b..00000000000 --- a/docs/versioned_docs/version-v0.38.0/how_to/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "position": 1, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.38.0/how_to/debugger/_category_.json b/docs/versioned_docs/version-v0.38.0/how_to/debugger/_category_.json deleted file mode 100644 index cc2cbb1c253..00000000000 --- a/docs/versioned_docs/version-v0.38.0/how_to/debugger/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "Debugging", - "position": 5, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.38.0/how_to/debugger/debugging_with_the_repl.md b/docs/versioned_docs/version-v0.38.0/how_to/debugger/debugging_with_the_repl.md deleted file mode 100644 index 1d64dae3f37..00000000000 --- a/docs/versioned_docs/version-v0.38.0/how_to/debugger/debugging_with_the_repl.md +++ /dev/null @@ -1,164 +0,0 @@ ---- -title: Using the REPL Debugger -description: - Step-by-step guide on how to debug your Noir circuits with the REPL Debugger. -keywords: - [ - Nargo, - Noir CLI, - Noir Debugger, - REPL, - ] -sidebar_position: 1 ---- - -#### Pre-requisites - -In order to use the REPL debugger, first you need to install recent enough versions of Nargo and vscode-noir. - -## Debugging a simple circuit - -Let's debug a simple circuit: - -```rust -fn main(x : Field, y : pub Field) { - assert(x != y); -} -``` - -To start the REPL debugger, using a terminal, go to a Noir circuit's home directory. Then: - -`$ nargo debug` - -You should be seeing this in your terminal: - -``` -[main] Starting debugger -At ~/noir-examples/recursion/circuits/main/src/main.nr:1:9 - 1 -> fn main(x : Field, y : pub Field) { - 2 assert(x != y); - 3 } -> -``` - -The debugger displays the current Noir code location, and it is now waiting for us to drive it. - -Let's first take a look at the available commands. For that we'll use the `help` command. - -``` -> help -Available commands: - - opcodes display ACIR opcodes - into step into to the next opcode - next step until a new source location is reached - out step until a new source location is reached - and the current stack frame is finished - break LOCATION:OpcodeLocation add a breakpoint at an opcode location - over step until a new source location is reached - without diving into function calls - restart restart the debugging session - delete LOCATION:OpcodeLocation delete breakpoint at an opcode location - witness show witness map - witness index:u32 display a single witness from the witness map - witness index:u32 value:String update a witness with the given value - memset index:usize value:String update a memory cell with the given - value - continue continue execution until the end of the - program - vars show variable values available at this point - in execution - stacktrace display the current stack trace - memory show memory (valid when executing unconstrained code) - step step to the next ACIR opcode - -Other commands: - - help Show this help message - quit Quit repl - -``` - -Some commands operate only for unconstrained functions, such as `memory` and `memset`. If you try to use them while execution is paused at an ACIR opcode, the debugger will simply inform you that you are not executing unconstrained code: - -``` -> memory -Unconstrained VM memory not available -> -``` - -Before continuing, we can take a look at the initial witness map: - -``` -> witness -_0 = 1 -_1 = 2 -> -``` - -Cool, since `x==1`, `y==2`, and we want to check that `x != y`, our circuit should succeed. At this point we could intervene and use the witness setter command to change one of the witnesses. Let's set `y=3`, then back to 2, so we don't affect the expected result: - -``` -> witness -_0 = 1 -_1 = 2 -> witness 1 3 -_1 = 3 -> witness -_0 = 1 -_1 = 3 -> witness 1 2 -_1 = 2 -> witness -_0 = 1 -_1 = 2 -> -``` - -Now we can inspect the current state of local variables. For that we use the `vars` command. - -``` -> vars -> -``` - -We currently have no vars in context, since we are at the entry point of the program. Let's use `next` to execute until the next point in the program. - -``` -> vars -> next -At ~/noir-examples/recursion/circuits/main/src/main.nr:1:20 - 1 -> fn main(x : Field, y : pub Field) { - 2 assert(x != y); - 3 } -> vars -x:Field = 0x01 -``` - -As a result of stepping, the variable `x`, whose initial value comes from the witness map, is now in context and returned by `vars`. - -``` -> next - 1 fn main(x : Field, y : pub Field) { - 2 -> assert(x != y); - 3 } -> vars -y:Field = 0x02 -x:Field = 0x01 -``` - -Stepping again we can finally see both variables and their values. And now we can see that the next assertion should succeed. - -Let's continue to the end: - -``` -> continue -(Continuing execution...) -Finished execution -> q -[main] Circuit witness successfully solved -``` - -Upon quitting the debugger after a solved circuit, the resulting circuit witness gets saved, equivalent to what would happen if we had run the same circuit with `nargo execute`. - -We just went through the basics of debugging using Noir REPL debugger. For a comprehensive reference, check out [the reference page](../../reference/debugger/debugger_repl.md). diff --git a/docs/versioned_docs/version-v0.38.0/how_to/debugger/debugging_with_vs_code.md b/docs/versioned_docs/version-v0.38.0/how_to/debugger/debugging_with_vs_code.md deleted file mode 100644 index a5858c1a5eb..00000000000 --- a/docs/versioned_docs/version-v0.38.0/how_to/debugger/debugging_with_vs_code.md +++ /dev/null @@ -1,68 +0,0 @@ ---- -title: Using the VS Code Debugger -description: - Step by step guide on how to debug your Noir circuits with the VS Code Debugger configuration and features. -keywords: - [ - Nargo, - Noir CLI, - Noir Debugger, - VS Code, - IDE, - ] -sidebar_position: 0 ---- - -This guide will show you how to use VS Code with the vscode-noir extension to debug a Noir project. - -#### Pre-requisites - -- Nargo -- vscode-noir -- A Noir project with a `Nargo.toml`, `Prover.toml` and at least one Noir (`.nr`) containing an entry point function (typically `main`). - -## Running the debugger - -The easiest way to start debugging is to open the file you want to debug, and press `F5`. This will cause the debugger to launch, using your `Prover.toml` file as input. - -You should see something like this: - -![Debugger launched](@site/static/img/debugger/1-started.png) - -Let's inspect the state of the program. For that, we open VS Code's _Debug pane_. Look for this icon: - -![Debug pane icon](@site/static/img/debugger/2-icon.png) - -You will now see two categories of variables: Locals and Witness Map. - -![Debug pane expanded](@site/static/img/debugger/3-debug-pane.png) - -1. **Locals**: variables of your program. At this point in execution this section is empty, but as we step through the code it will get populated by `x`, `result`, `digest`, etc. - -2. **Witness map**: these are initially populated from your project's `Prover.toml` file. In this example, they will be used to populate `x` and `result` at the beginning of the `main` function. - -Most of the time you will probably be focusing mostly on locals, as they represent the high level state of your program. - -You might be interested in inspecting the witness map in case you are trying to solve a really low level issue in the compiler or runtime itself, so this concerns mostly advanced or niche users. - -Let's step through the program, by using the debugger buttons or their corresponding keyboard shortcuts. - -![Debugger buttons](@site/static/img/debugger/4-debugger-buttons.png) - -Now we can see in the variables pane that there's values for `digest`, `result` and `x`. - -![Inspecting locals](@site/static/img/debugger/5-assert.png) - -We can also inspect the values of variables by directly hovering on them on the code. - -![Hover locals](@site/static/img/debugger/6-hover.png) - -Let's set a break point at the `keccak256` function, so we can continue execution up to the point when it's first invoked without having to go one step at a time. - -We just need to click the to the right of the line number 18. Once the breakpoint appears, we can click the `continue` button or use its corresponding keyboard shortcut (`F5` by default). - -![Breakpoint](@site/static/img/debugger/7-break.png) - -Now we are debugging the `keccak256` function, notice the _Call Stack pane_ at the lower right. This lets us inspect the current call stack of our process. - -That covers most of the current debugger functionalities. Check out [the reference](../../reference/debugger/debugger_vscode.md) for more details on how to configure the debugger. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.38.0/how_to/how-to-oracles.md b/docs/versioned_docs/version-v0.38.0/how_to/how-to-oracles.md deleted file mode 100644 index 08ce6ee5476..00000000000 --- a/docs/versioned_docs/version-v0.38.0/how_to/how-to-oracles.md +++ /dev/null @@ -1,275 +0,0 @@ ---- -title: How to use Oracles -description: Learn how to use oracles in your Noir program with examples in both Nargo and NoirJS. This guide also covers writing a JSON RPC server and providing custom foreign call handlers for NoirJS. -keywords: - - Noir Programming - - Oracles - - Nargo - - NoirJS - - JSON RPC Server - - Foreign Call Handlers -sidebar_position: 1 ---- - -This guide shows you how to use oracles in your Noir program. For the sake of clarity, it assumes that: - -- You have read the [explainer on Oracles](../explainers/explainer-oracle.md) and are comfortable with the concept. -- You have a Noir program to add oracles to. You can create one using the [vite-hardhat starter](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat) as a boilerplate. -- You understand the concept of a JSON-RPC server. Visit the [JSON-RPC website](https://www.jsonrpc.org/) if you need a refresher. -- You are comfortable with server-side JavaScript (e.g. Node.js, managing packages, etc.). - -## Rundown - -This guide has 3 major steps: - -1. How to modify our Noir program to make use of oracle calls as unconstrained functions -2. How to write a JSON RPC Server to resolve these oracle calls with Nargo -3. How to use them in Nargo and how to provide a custom resolver in NoirJS - -## Step 1 - Modify your Noir program - -An oracle is defined in a Noir program by defining two methods: - -- An unconstrained method - This tells the compiler that it is executing an [unconstrained functions](../noir/concepts//unconstrained.md). -- A decorated oracle method - This tells the compiler that this method is an RPC call. - -An example of an oracle that returns a `Field` would be: - -```rust -#[oracle(getSqrt)] -unconstrained fn sqrt(number: Field) -> Field { } - -unconstrained fn get_sqrt(number: Field) -> Field { - sqrt(number) -} -``` - -In this example, we're wrapping our oracle function in an unconstrained method, and decorating it with `oracle(getSqrt)`. We can then call the unconstrained function as we would call any other function: - -```rust -fn main(input: Field) { - let sqrt = get_sqrt(input); -} -``` - -In the next section, we will make this `getSqrt` (defined on the `sqrt` decorator) be a method of the RPC server Noir will use. - -:::danger - -As explained in the [Oracle Explainer](../explainers/explainer-oracle.md), this `main` function is unsafe unless you constrain its return value. For example: - -```rust -fn main(input: Field) { - let sqrt = get_sqrt(input); - assert(sqrt.pow_32(2) as u64 == input as u64); // <---- constrain the return of an oracle! -} -``` - -::: - -:::info - -Currently, oracles only work with single params or array params. For example: - -```rust -#[oracle(getSqrt)] -unconstrained fn sqrt([Field; 2]) -> [Field; 2] { } -``` - -::: - -## Step 2 - Write an RPC server - -Brillig will call *one* RPC server. Most likely you will have to write your own, and you can do it in whatever language you prefer. In this guide, we will do it in Javascript. - -Let's use the above example of an oracle that consumes an array with two `Field` and returns their square roots: - -```rust -#[oracle(getSqrt)] -unconstrained fn sqrt(input: [Field; 2]) -> [Field; 2] { } - -unconstrained fn get_sqrt(input: [Field; 2]) -> [Field; 2] { - sqrt(input) -} - -fn main(input: [Field; 2]) { - let sqrt = get_sqrt(input); - assert(sqrt[0].pow_32(2) as u64 == input[0] as u64); - assert(sqrt[1].pow_32(2) as u64 == input[1] as u64); -} - -#[test] -fn test() { - let input = [4, 16]; - main(input); -} -``` - -:::info - -Why square root? - -In general, computing square roots is computationally more expensive than multiplications, which takes a toll when speaking about ZK applications. In this case, instead of calculating the square root in Noir, we are using our oracle to offload that computation to be made in plain. In our circuit we can simply multiply the two values. - -::: - -Now, we should write the correspondent RPC server, starting with the [default JSON-RPC 2.0 boilerplate](https://www.npmjs.com/package/json-rpc-2.0#example): - -```js -import { JSONRPCServer } from "json-rpc-2.0"; -import express from "express"; -import bodyParser from "body-parser"; - -const app = express(); -app.use(bodyParser.json()); - -const server = new JSONRPCServer(); -app.post("/", (req, res) => { - const jsonRPCRequest = req.body; - server.receive(jsonRPCRequest).then((jsonRPCResponse) => { - if (jsonRPCResponse) { - res.json(jsonRPCResponse); - } else { - res.sendStatus(204); - } - }); -}); - -app.listen(5555); -``` - -Now, we will add our `getSqrt` method, as expected by the `#[oracle(getSqrt)]` decorator in our Noir code. It maps through the params array and returns their square roots: - -```js -server.addMethod("resolve_foreign_call", async (params) => { - if (params[0].function !== "getSqrt") { - throw Error("Unexpected foreign call") - }; - const values = params[0].inputs[0].map((field) => { - return `${Math.sqrt(parseInt(field, 16))}`; - }); - return { values: [values] }; -}); -``` - -If you're using Typescript, the following types may be helpful in understanding the expected return value and making sure they're easy to follow: - -```js -export type ForeignCallSingle = string; - -export type ForeignCallArray = string[]; - -export type ForeignCallResult = { - values: (ForeignCallSingle | ForeignCallArray)[]; -}; -``` - -:::info Multidimensional Arrays - -If the Oracle function is returning an array containing other arrays, such as `[['1','2],['3','4']]`, you need to provide the values in JSON as flattened values. In the previous example, it would be `['1', '2', '3', '4']`. In the Noir program, the Oracle signature can use a nested type, the flattened values will be automatically converted to the nested type. - -::: - -## Step 3 - Usage with Nargo - -Using the [`nargo` CLI tool](../reference/nargo_commands.md), you can use oracles in the `nargo test` and `nargo execute` commands by passing a value to `--oracle-resolver`. For example: - -```bash -nargo test --oracle-resolver http://localhost:5555 -``` - -This tells `nargo` to use your RPC Server URL whenever it finds an oracle decorator. - -## Step 4 - Usage with NoirJS - -In a JS environment, an RPC server is not strictly necessary, as you may want to resolve your oracles without needing any JSON call at all. NoirJS simply expects that you pass a callback function when you generate proofs, and that callback function can be anything. - -For example, if your Noir program expects the host machine to provide CPU pseudo-randomness, you could simply pass it as the `foreignCallHandler`. You don't strictly need to create an RPC server to serve pseudo-randomness, as you may as well get it directly in your app: - -```js -const foreignCallHandler = (name, inputs) => crypto.randomBytes(16) // etc - -await noir.execute(inputs, foreignCallHandler) -``` - -As one can see, in NoirJS, the [`foreignCallHandler`](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) function simply means "a callback function that returns a value of type [`ForeignCallOutput`](../reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md). It doesn't have to be an RPC call like in the case for Nargo. - -:::tip - -Does this mean you don't have to write an RPC server like in [Step #2](#step-2---write-an-rpc-server)? - -You don't technically have to, but then how would you run `nargo test`? To use both `Nargo` and `NoirJS` in your development flow, you will have to write a JSON RPC server. - -::: - -In this case, let's make `foreignCallHandler` call the JSON RPC Server we created in [Step #2](#step-2---write-an-rpc-server), by making it a JSON RPC Client. - -For example, using the same `getSqrt` program in [Step #1](#step-1---modify-your-noir-program) (comments in the code): - -```js -import { JSONRPCClient } from "json-rpc-2.0"; - -// declaring the JSONRPCClient -const client = new JSONRPCClient((jsonRPCRequest) => { -// hitting the same JSON RPC Server we coded above - return fetch("http://localhost:5555", { - method: "POST", - headers: { - "content-type": "application/json", - }, - body: JSON.stringify(jsonRPCRequest), - }).then((response) => { - if (response.status === 200) { - return response - .json() - .then((jsonRPCResponse) => client.receive(jsonRPCResponse)); - } else if (jsonRPCRequest.id !== undefined) { - return Promise.reject(new Error(response.statusText)); - } - }); -}); - -// declaring a function that takes the name of the foreign call (getSqrt) and the inputs -const foreignCallHandler = async (name, input) => { - const inputs = input[0].map((i) => i.toString("hex")) - // notice that the "inputs" parameter contains *all* the inputs - // in this case we to make the RPC request with the first parameter "numbers", which would be input[0] - const oracleReturn = await client.request("resolve_foreign_call", [ - { - function: name, - inputs: [inputs] - }, - ]); - return [oracleReturn.values[0]]; -}; - -// the rest of your NoirJS code -const input = { input: [4, 16] }; -const { witness } = await noir.execute(input, foreignCallHandler); -``` - -:::tip - -If you're in a NoirJS environment running your RPC server together with a frontend app, you'll probably hit a familiar problem in full-stack development: requests being blocked by [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) policy. For development only, you can simply install and use the [`cors` npm package](https://www.npmjs.com/package/cors) to get around the problem: - -```bash -yarn add cors -``` - -and use it as a middleware: - -```js -import cors from "cors"; - -const app = express(); -app.use(cors()) -``` - -::: - -## Conclusion - -Hopefully by the end of this guide, you should be able to: - -- Write your own logic around Oracles and how to write a JSON RPC server to make them work with your Nargo commands. -- Provide custom foreign call handlers for NoirJS. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.38.0/how_to/how-to-recursion.md b/docs/versioned_docs/version-v0.38.0/how_to/how-to-recursion.md deleted file mode 100644 index fac79a9cf49..00000000000 --- a/docs/versioned_docs/version-v0.38.0/how_to/how-to-recursion.md +++ /dev/null @@ -1,172 +0,0 @@ ---- -title: How to use recursion on NoirJS -description: Learn how to implement recursion with NoirJS, a powerful tool for creating smart contracts on the EVM blockchain. This guide assumes familiarity with NoirJS, solidity verifiers, and the Barretenberg proving backend. Discover how to generate both final and intermediate proofs using `noir_js` and `bb.js`. -keywords: - [ - "NoirJS", - "EVM blockchain", - "smart contracts", - "recursion", - "solidity verifiers", - "Barretenberg backend", - "noir_js", - "intermediate proofs", - "final proofs", - "nargo compile", - "json import", - "recursive circuit", - "recursive app" - ] -sidebar_position: 1 ---- - -This guide shows you how to use recursive proofs in your NoirJS app. For the sake of clarity, it is assumed that: - -- You already have a NoirJS app. If you don't, please visit the [NoirJS tutorial](../tutorials/noirjs_app.md) and the [reference](../reference/NoirJS/noir_js/index.md). -- You are familiar with what are recursive proofs and you have read the [recursion explainer](../explainers/explainer-recursion.md) -- You already built a recursive circuit following [the reference](../noir/standard_library/recursion.mdx), and understand how it works. - -It is also assumed that you're not using `noir_wasm` for compilation, and instead you've used [`nargo compile`](../reference/nargo_commands.md) to generate the `json` you're now importing into your project. However, the guide should work just the same if you're using `noir_wasm`. - -:::info - -As you've read in the [explainer](../explainers/explainer-recursion.md), a recursive proof is an intermediate proof. This means that it doesn't necessarily generate the final step that makes it verifiable in a smart contract. However, it is easy to verify within another circuit. - -::: - -In a standard recursive app, you're also dealing with at least two circuits. For the purpose of this guide, we will assume the following: - -- `main`: a circuit of type `assert(x != y)`, where `main` is marked with a `#[recursive]` attribute. This attribute states that the backend should generate proofs that are friendly for verification within another circuit. -- `recursive`: a circuit that verifies `main` - -For a full example of how recursive proofs work, please refer to the [noir-examples](https://github.com/noir-lang/noir-examples) repository. We will *not* be using it as a reference for this guide. - -## Step 1: Setup - -In a common NoirJS app, you need to instantiate a backend with something like `const backend = new Backend(circuit)`. Then you feed it to the `noir_js` interface. - -For recursion, this doesn't happen, and the only need for `noir_js` is only to `execute` a circuit and get its witness and return value. Everything else is not interfaced, so it needs to happen on the `backend` object. - -It is also recommended that you instantiate the backend with as many threads as possible, to allow for maximum concurrency: - -```js -const backend = new Backend(circuit, { threads: 8 }) -``` - -:::tip -You can use the [`os.cpus()`](https://nodejs.org/api/os.html#oscpus) object in `nodejs` or [`navigator.hardwareConcurrency`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/hardwareConcurrency) on the browser to make the most out of those glorious cpu cores -::: - -## Step 2: Generating the witness and the proof for `main` - -After instantiating the backend, you should also instantiate `noir_js`. We will use it to execute the circuit and get the witness. - -```js -const noir = new Noir(circuit) -const { witness } = noir.execute(input) -``` - -With this witness, you are now able to generate the intermediate proof for the main circuit: - -```js -const { proof, publicInputs } = await backend.generateProof(witness) -``` - -:::warning - -Always keep in mind what is actually happening on your development process, otherwise you'll quickly become confused about what circuit we are actually running and why! - -In this case, you can imagine that Alice (running the `main` circuit) is proving something to Bob (running the `recursive` circuit), and Bob is verifying her proof within his proof. - -With this in mind, it becomes clear that our intermediate proof is the one *meant to be verified within another circuit*, so it must be Alice's. Actually, the only final proof in this theoretical scenario would be the last one, sent on-chain. - -::: - -## Step 3 - Verification and proof artifacts - -Optionally, you are able to verify the intermediate proof: - -```js -const verified = await backend.verifyProof({ proof, publicInputs }) -``` - -This can be useful to make sure our intermediate proof was correctly generated. But the real goal is to do it within another circuit. For that, we need to generate recursive proof artifacts that will be passed to the circuit that is verifying the proof we just generated. Instead of passing the proof and verification key as a byte array, we pass them as fields which makes it cheaper to verify in a circuit: - -```js -const { proofAsFields, vkAsFields, vkHash } = await backend.generateRecursiveProofArtifacts( { publicInputs, proof }, publicInputsCount) -``` - -This call takes the public inputs and the proof, but also the public inputs count. While this is easily retrievable by simply counting the `publicInputs` length, the backend interface doesn't currently abstract it away. - -:::info - -The `proofAsFields` has a constant size `[Field; 93]` and verification keys in Barretenberg are always `[Field; 114]`. - -::: - -:::warning - -One common mistake is to forget *who* makes this call. - -In a situation where Alice is generating the `main` proof, if she generates the proof artifacts and sends them to Bob, which gladly takes them as true, this would mean Alice could prove anything! - -Instead, Bob needs to make sure *he* extracts the proof artifacts, using his own instance of the `main` circuit backend. This way, Alice has to provide a valid proof for the correct `main` circuit. - -::: - -## Step 4 - Recursive proof generation - -With the artifacts, generating a recursive proof is no different from a normal proof. You simply use the `backend` (with the recursive circuit) to generate it: - -```js -const recursiveInputs = { - verification_key: vkAsFields, // array of length 114 - proof: proofAsFields, // array of length 93 + size of public inputs - publicInputs: [mainInput.y], // using the example above, where `y` is the only public input - key_hash: vkHash, -} - -const { witness, returnValue } = noir.execute(recursiveInputs) // we're executing the recursive circuit now! -const { proof, publicInputs } = backend.generateProof(witness) -const verified = backend.verifyProof({ proof, publicInputs }) -``` - -You can obviously chain this proof into another proof. In fact, if you're using recursive proofs, you're probably interested of using them this way! - -:::tip - -Managing circuits and "who does what" can be confusing. To make sure your naming is consistent, you can keep them in an object. For example: - -```js -const circuits = { - main: mainJSON, - recursive: recursiveJSON -} -const backends = { - main: new BarretenbergBackend(circuits.main), - recursive: new BarretenbergBackend(circuits.recursive) -} -const noir_programs = { - main: new Noir(circuits.main), - recursive: new Noir(circuits.recursive) -} -``` - -This allows you to neatly call exactly the method you want without conflicting names: - -```js -// Alice runs this 👇 -const { witness: mainWitness } = await noir_programs.main.execute(input) -const proof = await backends.main.generateProof(mainWitness) - -// Bob runs this 👇 -const verified = await backends.main.verifyProof(proof) -const { proofAsFields, vkAsFields, vkHash } = await backends.main.generateRecursiveProofArtifacts( - proof, - numPublicInputs, -); -const { witness: recursiveWitness } = await noir_programs.recursive.execute(recursiveInputs) -const recursiveProof = await backends.recursive.generateProof(recursiveWitness); -``` - -::: diff --git a/docs/versioned_docs/version-v0.38.0/how_to/merkle-proof.mdx b/docs/versioned_docs/version-v0.38.0/how_to/merkle-proof.mdx deleted file mode 100644 index 0a128adb2de..00000000000 --- a/docs/versioned_docs/version-v0.38.0/how_to/merkle-proof.mdx +++ /dev/null @@ -1,48 +0,0 @@ ---- -title: Prove Merkle Tree 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] -sidebar_position: 4 ---- - -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 - -fn main(message : [Field; 62], index : Field, hashpath : [Field; 40], root : Field) { - let leaf = std::hash::hash_to_field(message.as_slice()); - 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.as_slice()); -``` - -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.38.0/how_to/using-devcontainers.mdx b/docs/versioned_docs/version-v0.38.0/how_to/using-devcontainers.mdx deleted file mode 100644 index 727ec6ca667..00000000000 --- a/docs/versioned_docs/version-v0.38.0/how_to/using-devcontainers.mdx +++ /dev/null @@ -1,110 +0,0 @@ ---- -title: Developer Containers and Codespaces -description: "Learn how to set up a devcontainer in your GitHub repository for a seamless coding experience with Codespaces. Follow our easy 8-step guide to create your own Noir environment without installing Nargo locally." -keywords: ["Devcontainer", "Codespaces", "GitHub", "Noir Environment", "Docker Image", "Development Environment", "Remote Coding", "GitHub Codespaces", "Noir Programming", "Nargo", "VSCode Extensions", "Noirup"] -sidebar_position: 1 ---- - -Adding a developer container configuration file to your Noir project is one of the easiest way to unlock coding in browser. - -## What's a devcontainer after all? - -A [Developer Container](https://containers.dev/) (devcontainer for short) is a Docker image that comes preloaded with tools, extensions, and other tools you need to quickly get started or continue a project, without having to install Nargo locally. Think of it as a development environment in a box. - -There are many advantages to this: - -- It's platform and architecture agnostic -- You don't need to have an IDE installed, or Nargo, or use a terminal at all -- It's safer for using on a public machine or public network - -One of the best ways of using devcontainers is... not using your machine at all, for maximum control, performance, and ease of use. -Enter Codespaces. - -## Codespaces - -If a devcontainer is just a Docker image, then what stops you from provisioning a `p3dn.24xlarge` AWS EC2 instance with 92 vCPUs and 768 GiB RAM and using it to prove your 10-gate SNARK proof? - -Nothing! Except perhaps the 30-40$ per hour it will cost you. - -The problem is that provisioning takes time, and I bet you don't want to see the AWS console every time you want to code something real quick. - -Fortunately, there's an easy and free way to get a decent remote machine ready and loaded in less than 2 minutes: Codespaces. [Codespaces is a Github feature](https://github.com/features/codespaces) that allows you to code in a remote machine by using devcontainers, and it's pretty cool: - -- You can start coding Noir in less than a minute -- It uses the resources of a remote machine, so you can code on your grandma's phone if needed be -- It makes it easy to share work with your frens -- It's fully reusable, you can stop and restart whenever you need to - -:::info - -Don't take out your wallet just yet. Free GitHub accounts get about [15-60 hours of coding](https://github.com/features/codespaces) for free per month, depending on the size of your provisioned machine. - -::: - -## Tell me it's _actually_ easy - -It is! - -Github comes with a default codespace and you can use it to code your own devcontainer. That's exactly what we will be doing in this guide. - - - -8 simple steps: - -#### 1. Create a new repository on GitHub. - -#### 2. Click "Start coding with Codespaces". This will use the default image. - -#### 3. Create a folder called `.devcontainer` in the root of your repository. - -#### 4. Create a Dockerfile in that folder, and paste the following code: - -```docker -FROM --platform=linux/amd64 node:lts-bookworm-slim -SHELL ["/bin/bash", "-c"] -RUN apt update && apt install -y curl bash git tar gzip libc++-dev -RUN curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash -ENV PATH="/root/.nargo/bin:$PATH" -RUN noirup -ENTRYPOINT ["nargo"] -``` -#### 5. Create a file called `devcontainer.json` in the same folder, and paste the following code: - -```json -{ - "name": "Noir on Codespaces", - "build": { - "context": ".", - "dockerfile": "Dockerfile" - }, - "customizations": { - "vscode": { - "extensions": ["noir-lang.vscode-noir"] - } - } -} -``` -#### 6. Commit and push your changes - -This will pull the new image and build it, so it could take a minute or so - -#### 8. Done! -Just wait for the build to finish, and there's your easy Noir environment. - - -Refer to [noir-starter](https://github.com/noir-lang/noir-starter/) as an example of how devcontainers can be used together with codespaces. - - - -## How do I use it? - -Using the codespace is obviously much easier than setting it up. -Just navigate to your repository and click "Code" -> "Open with Codespaces". It should take a few seconds to load, and you're ready to go. - -:::info - -If you really like the experience, you can add a badge to your readme, links to existing codespaces, and more. -Check out the [official docs](https://docs.github.com/en/codespaces/setting-up-your-project-for-codespaces/setting-up-your-repository/facilitating-quick-creation-and-resumption-of-codespaces) for more info. diff --git a/docs/versioned_docs/version-v0.38.0/index.mdx b/docs/versioned_docs/version-v0.38.0/index.mdx deleted file mode 100644 index 5c116a73b3f..00000000000 --- a/docs/versioned_docs/version-v0.38.0/index.mdx +++ /dev/null @@ -1,76 +0,0 @@ ---- -title: Noir Lang -hide_title: true -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] -sidebar_position: 0 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; -import ThemedImage from '@theme/ThemedImage'; -import useBaseUrl from '@docusaurus/useBaseUrl'; - - - -Noir is an open-source Domain-Specific Language for safe and seamless construction of privacy-preserving Zero-Knowledge programs, requiring no previous knowledge on the underlying mathematics or cryptography. - -ZK programs are programs that can generate short proofs of statements without revealing all inputs to the statements. You can read more about Zero-Knowledge Proofs [here](https://dev.to/spalladino/a-beginners-intro-to-coding-zero-knowledge-proofs-c56). - -## What's new about Noir? - -Noir works differently from most ZK languages by taking a two-pronged path. First, it compiles the program to an adaptable intermediate language known as ACIR. From there, depending on a given project's needs, ACIR can be further compiled into an arithmetic circuit for integration with the proving backend. - -:::info - -Noir is backend agnostic, which means it makes no assumptions on which proving backend powers the ZK proof. Being the language that powers [Aztec Contracts](https://docs.aztec.network/developers/contracts/main), it defaults to Aztec's Barretenberg proving backend. - -However, the ACIR output can be transformed to be compatible with other PLONK-based backends, or into a [rank-1 constraint system](https://www.rareskills.io/post/rank-1-constraint-system) suitable for backends such as Arkwork's Marlin. - -::: - -## Who is Noir for? - -Noir can be used both in complex cloud-based backends and in user's smartphones, requiring no knowledge on the underlying math or cryptography. From authorization systems that keep a password in the user's device, to complex on-chain verification of recursive proofs, Noir is designed to abstract away complexity without any significant overhead. Here are some examples of situations where Noir can be used: - - - - Noir Logo - - Aztec Contracts leverage Noir to allow for the storage and execution of private information. Writing an Aztec Contract is as easy as writing Noir, and Aztec developers can easily interact with the network storage and execution through the [Aztec.nr](https://docs.aztec.network/developers/contracts/main) library. - - - Soliditry Verifier Example - Noir can auto-generate Solidity verifier contracts that verify Noir proofs. This allows for non-interactive verification of proofs containing private information in an immutable system. This feature powers a multitude of use-case scenarios, from P2P chess tournaments, to [Aztec Layer-2 Blockchain](https://docs.aztec.network/) - - - Aztec Labs developed NoirJS, an easy interface to generate and verify Noir proofs in a Javascript environment. This allows for Noir to be used in webpages, mobile apps, games, and any other environment supporting JS execution in a standalone manner. - - - - -## Libraries - -Noir is meant to be easy to extend by simply importing Noir libraries just like in Rust. -The [awesome-noir repo](https://github.com/noir-lang/awesome-noir#libraries) is a collection of libraries developed by the Noir community. -Writing a new library is easy and makes code be composable and easy to reuse. See the section on [dependencies](noir/modules_packages_crates/dependencies.md) for more information. diff --git a/docs/versioned_docs/version-v0.38.0/migration_notes.md b/docs/versioned_docs/version-v0.38.0/migration_notes.md deleted file mode 100644 index 6bd740024e5..00000000000 --- a/docs/versioned_docs/version-v0.38.0/migration_notes.md +++ /dev/null @@ -1,105 +0,0 @@ ---- -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. - -### `backend encountered an error: libc++.so.1` - -Depending on your OS, you may encounter the following error when running `nargo prove` for the first time: - -```text -The backend encountered an error: "/home/codespace/.nargo/backends/acvm-backend-barretenberg/backend_binary: error while loading shared libraries: libc++.so.1: cannot open shared object file: No such file or directory\n" -``` - -Install the `libc++-dev` library with: - -```bash -sudo apt install libc++-dev -``` - -## ≥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](noir/concepts/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 your 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 file is running [this bash script](https://github.com/noir-lang/barretenberg-js-binary/blob/master/run-bb-js.sh), 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.38.0/noir/concepts/_category_.json b/docs/versioned_docs/version-v0.38.0/noir/concepts/_category_.json deleted file mode 100644 index 7da08f8a8c5..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/concepts/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "Concepts", - "position": 0, - "collapsible": true, - "collapsed": true -} \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.38.0/noir/concepts/assert.md b/docs/versioned_docs/version-v0.38.0/noir/concepts/assert.md deleted file mode 100644 index 2132de42072..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/concepts/assert.md +++ /dev/null @@ -1,78 +0,0 @@ ---- -title: Assert Function -description: - Learn about the `assert` and `static_assert` functions 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 or compile-time, respectively. -keywords: [Noir programming language, assert statement, predicate expression, comparison expression] -sidebar_position: 4 ---- - -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); -} -``` - -> 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`. - -You can optionally provide a message to be logged when the assertion fails: - -```rust -assert(x == y, "x and y are not equal"); -``` - -Aside string literals, the optional message can be a format string or any other type supported as input for Noir's [print](../standard_library/logging.md) functions. This feature lets you incorporate runtime variables into your failed assertion logs: - -```rust -assert(x == y, f"Expected x == y, but got {x} == {y}"); -``` - -Using a variable as an assertion message directly: - -```rust -struct myStruct { - myField: Field -} - -let s = myStruct { myField: y }; -assert(s.myField == x, s); -``` - -There is also a special `static_assert` function that behaves like `assert`, -but that runs at compile-time. - -```rust -fn main(xs: [Field; 3]) { - let x = 2 + 2; - let y = 4; - static_assert(x == y, "expected 2 + 2 to equal 4"); - - // This passes since the length of `xs` is known at compile-time - static_assert(xs.len() == 3, "expected the input to have 3 elements"); -} -``` - -This function fails when passed a dynamic (run-time) argument: - -```rust -fn main(x : Field, y : Field) { - // this fails because `x` is not known at compile-time - static_assert(x == 2, "expected x to be known at compile-time and equal to 2"); - - let mut example_slice = &[]; - if y == 4 { - example_slice = example_slice.push_back(0); - } - - // This fails because the length of `example_slice` is not known at - // compile-time - let error_message = "expected an empty slice, known at compile-time"; - static_assert(example_slice.len() == 0, error_message); -} -``` - diff --git a/docs/versioned_docs/version-v0.38.0/noir/concepts/comments.md b/docs/versioned_docs/version-v0.38.0/noir/concepts/comments.md deleted file mode 100644 index b51a85f5c94..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/concepts/comments.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -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] -sidebar_position: 10 ---- - -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.38.0/noir/concepts/comptime.md b/docs/versioned_docs/version-v0.38.0/noir/concepts/comptime.md deleted file mode 100644 index 2ceb030c7e1..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/concepts/comptime.md +++ /dev/null @@ -1,445 +0,0 @@ ---- -title: Compile-time Code & Metaprogramming -description: Learn how to use metaprogramming in Noir to create macros or derive your own traits -keywords: [Noir, comptime, compile-time, metaprogramming, macros, quote, unquote] -sidebar_position: 15 ---- - -## Overview - -Metaprogramming in Noir is comprised of three parts: -1. `comptime` code -2. Quoting and unquoting -3. The metaprogramming API in `std::meta` - -Each of these are explained in more detail in the next sections but the wide picture is that -`comptime` allows us to write code which runs at compile-time. In this `comptime` code we -can quote and unquote snippets of the program, manipulate them, and insert them in other -parts of the program. Comptime functions which do this are said to be macros. Additionally, -there's a compile-time API of built-in types and functions provided by the compiler which allows -for greater analysis and modification of programs. - ---- - -## Comptime - -`comptime` is a new keyword in Noir which marks an item as executing or existing at compile-time. It can be used in several ways: - -- `comptime fn` to define functions which execute exclusively during compile-time. -- `comptime global` to define a global variable which is evaluated at compile-time. - - Unlike runtime globals, `comptime global`s can be mutable. -- `comptime { ... }` to execute a block of statements during compile-time. -- `comptime let` to define a variable whose value is evaluated at compile-time. -- `comptime for` to run a for loop at compile-time. Syntax sugar for `comptime { for .. }`. - -### Scoping - -Note that while in a `comptime` context, any runtime variables _local to the current function_ are never visible. - -### Evaluating - -Evaluation rules of `comptime` follows the normal unconstrained evaluation rules for other Noir code. There are a few things to note though: - -- Certain built-in functions may not be available, although more may be added over time. -- Evaluation order of global items is currently unspecified. For example, given the following two functions we can't guarantee -which `println` will execute first. The ordering of the two printouts will be arbitrary, but should be stable across multiple compilations with the same `nargo` version as long as the program is also unchanged. - -```rust -fn one() { - comptime { println("one"); } -} - -fn two() { - comptime { println("two"); } -} -``` - -- Since evaluation order is unspecified, care should be taken when using mutable globals so that they do not rely on a particular ordering. -For example, using globals to generate unique ids should be fine but relying on certain ids always being produced (especially after edits to the program) should be avoided. -- Although most ordering of globals is unspecified, two are: - - Dependencies of a crate will always be evaluated before the dependent crate. - - Any annotations on a function will be run before the function itself is resolved. This is to allow the annotation to modify the function if necessary. Note that if the - function itself was called at compile-time previously, it will already be resolved and cannot be modified. To prevent accidentally calling functions you wish to modify - at compile-time, it may be helpful to sort your `comptime` annotation functions into a different crate along with any dependencies they require. - -### Lowering - -When a `comptime` value is used in runtime code it must be lowered into a runtime value. This means replacing the expression with the literal that it evaluated to. For example, the code: - -```rust -struct Foo { array: [Field; 2], len: u32 } - -fn main() { - println(comptime { - let mut foo = std::mem::zeroed::(); - foo.array[0] = 4; - foo.len = 1; - foo - }); -} -``` - -will be converted to the following after `comptime` expressions are evaluated: - -```rust -struct Foo { array: [Field; 2], len: u32 } - -fn main() { - println(Foo { array: [4, 0], len: 1 }); -} -``` - -Not all types of values can be lowered. For example, `Type`s and `TypeDefinition`s (among other types) cannot be lowered at all. - -```rust -fn main() { - // There's nothing we could inline here to create a Type value at runtime - // let _ = get_type!(); -} - -comptime fn get_type() -> Type { ... } -``` - ---- - -## (Quasi) Quote - -Macros in Noir are `comptime` functions which return code as a value which is inserted into the call site when it is lowered there. -A code value in this case is of type `Quoted` and can be created by a `quote { ... }` expression. -More specifically, the code value `quote` creates is a token stream - a representation of source code as a series of words, numbers, string literals, or operators. -For example, the expression `quote { Hi "there reader"! }` would quote three tokens: the word "hi", the string "there reader", and an exclamation mark. -You'll note that snippets that would otherwise be invalid syntax can still be quoted. - -When a `Quoted` value is used in runtime code, it is lowered into a `quote { ... }` expression. Since this expression is only valid -in compile-time code however, we'd get an error if we tried this. Instead, we can use macro insertion to insert each token into the -program at that point, and parse it as an expression. To do this, we have to add a `!` after the function name returning the `Quoted` value. -If the value was created locally and there is no function returning it, `std::meta::unquote!(_)` can be used instead. -Calling such a function at compile-time without `!` will just return the `Quoted` value to be further manipulated. For example: - -```rust title="quote-example" showLineNumbers -comptime fn quote_one() -> Quoted { - quote { 1 } - } - - #[test] - fn returning_versus_macro_insertion() { - comptime { - // let _a: Quoted = quote { 1 }; - let _a: Quoted = quote_one(); - - // let _b: Field = 1; - let _b: Field = quote_one!(); - - // Since integers default to fields, if we - // want a different type we have to explicitly cast - // let _c: i32 = 1 as i32; - let _c: i32 = quote_one!() as i32; - } - } -``` -> Source code: noir_stdlib/src/meta/mod.nr#L120-L140 - - -For those familiar with quoting from other languages (primarily lisps), Noir's `quote` is actually a _quasiquote_. -This means we can escape the quoting by using the unquote operator to splice values in the middle of quoted code. - -## Unquote - -The unquote operator `$` is usable within a `quote` expression. -It takes a variable as an argument, evaluates the variable, and splices the resulting value into the quoted token stream at that point. For example, - -```rust -comptime { - let x = 1 + 2; - let y = quote { $x + 4 }; -} -``` - -The value of `y` above will be the token stream containing `3`, `+`, and `4`. We can also use this to combine `Quoted` values into larger token streams: - -```rust -comptime { - let x = quote { 1 + 2 }; - let y = quote { $x + 4 }; -} -``` - -The value of `y` above is now the token stream containing five tokens: `1 + 2 + 4`. - -Note that to unquote something, a variable name _must_ follow the `$` operator in a token stream. -If it is an expression (even a parenthesized one), it will do nothing. Most likely a parse error will be given when the macro is later unquoted. - -Unquoting can also be avoided by escaping the `$` with a backslash: - -``` -comptime { - let x = quote { 1 + 2 }; - - // y contains the four tokens: `$x + 4` - let y = quote { \$x + 4 }; -} -``` - ---- - -## Annotations - -Annotations provide a way to run a `comptime` function on an item in the program. -When you use an annotation, the function with the same name will be called with that item as an argument: - -```rust -#[my_struct_annotation] -struct Foo {} - -comptime fn my_struct_annotation(s: StructDefinition) { - println("Called my_struct_annotation!"); -} - -#[my_function_annotation] -fn foo() {} - -comptime fn my_function_annotation(f: FunctionDefinition) { - println("Called my_function_annotation!"); -} -``` - -Anything returned from one of these functions will be inserted at top-level along with the original item. -Note that expressions are not valid at top-level so you'll get an error trying to return `3` or similar just as if you tried to write a program containing `3; struct Foo {}`. -You can insert other top-level items such as trait impls, structs, or functions this way though. -For example, this is the mechanism used to insert additional trait implementations into the program when deriving a trait impl from a struct: - -```rust title="derive-field-count-example" showLineNumbers -trait FieldCount { - fn field_count() -> u32; - } - - #[derive_field_count] - struct Bar { - x: Field, - y: [Field; 2], - } - - comptime fn derive_field_count(s: StructDefinition) -> Quoted { - let typ = s.as_type(); - let field_count = s.fields().len(); - quote { - impl FieldCount for $typ { - fn field_count() -> u32 { - $field_count - } - } - } - } -``` -> Source code: noir_stdlib/src/meta/mod.nr#L142-L164 - - -### Calling annotations with additional arguments - -Arguments may optionally be given to annotations. -When this is done, these additional arguments are passed to the annotation function after the item argument. - -```rust title="annotation-arguments-example" showLineNumbers -#[assert_field_is_type(quote { i32 }.as_type())] - struct MyStruct { - my_field: i32, - } - - comptime fn assert_field_is_type(s: StructDefinition, typ: Type) { - // Assert the first field in `s` has type `typ` - let fields = s.fields(); - assert_eq(fields[0].1, typ); - } -``` -> Source code: noir_stdlib/src/meta/mod.nr#L166-L177 - - -We can also take any number of arguments by adding the `varargs` annotation: - -```rust title="annotation-varargs-example" showLineNumbers -#[assert_three_args(1, 2, 3)] - struct MyOtherStruct { - my_other_field: u32, - } - - #[varargs] - comptime fn assert_three_args(_s: StructDefinition, args: [Field]) { - assert_eq(args.len(), 3); - } -``` -> Source code: noir_stdlib/src/meta/mod.nr#L179-L189 - - ---- - -## Comptime API - -Although `comptime`, `quote`, and unquoting provide a flexible base for writing macros, -Noir's true metaprogramming ability comes from being able to interact with the compiler through a compile-time API. -This API can be accessed through built-in functions in `std::meta` as well as on methods of several `comptime` types. - -The following is an incomplete list of some `comptime` types along with some useful methods on them. You can see more in the standard library [Metaprogramming section](../standard_library/meta). - -- `Quoted`: A token stream -- `Type`: The type of a Noir type - - `fn implements(self, constraint: TraitConstraint) -> bool` - - Returns true if `self` implements the given trait constraint -- `Expr`: A syntactically valid expression. Can be used to recur on a program's parse tree to inspect how it is structured. - - Methods: - - `fn as_function_call(self) -> Option<(Expr, [Expr])>` - - If this is a function call expression, return `(function, arguments)` - - `fn as_block(self) -> Option<[Expr]>` - - If this is a block, return each statement in the block -- `FunctionDefinition`: A function definition - - Methods: - - `fn parameters(self) -> [(Quoted, Type)]` - - Returns a slice of `(name, type)` pairs for each parameter -- `StructDefinition`: A struct definition - - Methods: - - `fn as_type(self) -> Type` - - Returns this `StructDefinition` as a `Type`. Any generics are kept as-is - - `fn generics(self) -> [Quoted]` - - Return the name of each generic on this struct - - `fn fields(self) -> [(Quoted, Type)]` - - Return the name and type of each field -- `TraitConstraint`: A trait constraint such as `From` -- `TypedExpr`: A type-checked expression. -- `UnresolvedType`: A syntactic notation that refers to a Noir type that hasn't been resolved yet - -There are many more functions available by exploring the `std::meta` module and its submodules. -Using these methods is the key to writing powerful metaprogramming libraries. - -### `#[use_callers_scope]` - -Since certain functions such as `Quoted::as_type`, `Expression::as_type`, or `Quoted::as_trait_constraint` will attempt -to resolve their contents in a particular scope - it can be useful to change the scope they resolve in. By default -these functions will resolve in the current function's scope which is usually the attribute function they are called in. -If you're working on a library however, this may be a completely different module or crate to the item you're trying to -use the attribute on. If you want to be able to use `Quoted::as_type` to refer to types local to the caller's scope for -example, you can annotate your attribute function with `#[use_callers_scope]`. This will ensure your attribute, and any -closures it uses, can refer to anything in the caller's scope. `#[use_callers_scope]` also works recursively. So if both -your attribute function and a helper function it calls use it, then they can both refer to the same original caller. - ---- - -## Example: Derive - -Using all of the above, we can write a `derive` macro that behaves similarly to Rust's but is not built into the language. -From the user's perspective it will look like this: - -```rust -// Example usage -#[derive(Default, Eq, Ord)] -struct MyStruct { my_field: u32 } -``` - -To implement `derive` we'll have to create a `comptime` function that accepts -a variable amount of traits. - -```rust title="derive_example" showLineNumbers -// These are needed for the unconstrained hashmap we're using to store derive functions -use crate::collections::umap::UHashMap; -use crate::hash::BuildHasherDefault; -use crate::hash::poseidon2::Poseidon2Hasher; - -// A derive function is one that given a struct definition can -// create us a quoted trait impl from it. -pub type DeriveFunction = fn(StructDefinition) -> Quoted; - -// We'll keep a global HANDLERS map to keep track of the derive handler for each trait -comptime mut global HANDLERS: UHashMap> = - UHashMap::default(); - -// Given a struct and a slice of traits to derive, create trait impls for each. -// This function is as simple as iterating over the slice, checking if we have a trait -// handler registered for the given trait, calling it, and appending the result. -#[varargs] -pub comptime fn derive(s: StructDefinition, traits: [TraitDefinition]) -> Quoted { - let mut result = quote {}; - - for trait_to_derive in traits { - let handler = unsafe { HANDLERS.get(trait_to_derive) }; - assert(handler.is_some(), f"No derive function registered for `{trait_to_derive}`"); - - let trait_impl = handler.unwrap()(s); - result = quote { $result $trait_impl }; - } - - result -} -``` -> Source code: noir_stdlib/src/meta/mod.nr#L31-L64 - - -Registering a derive function could be done as follows: - -```rust title="derive_via" showLineNumbers -// To register a handler for a trait, just add it to our handlers map -pub comptime fn derive_via(t: TraitDefinition, f: DeriveFunction) { - HANDLERS.insert(t, f); -} -``` -> Source code: noir_stdlib/src/meta/mod.nr#L66-L73 - - -```rust title="big-derive-usage-example" showLineNumbers -// Finally, to register a handler we call the above function as an annotation - // with our handler function. - #[derive_via(derive_do_nothing)] - trait DoNothing { - fn do_nothing(self); - } - - comptime fn derive_do_nothing(s: StructDefinition) -> Quoted { - // This is simplified since we don't handle generics or where clauses! - // In a real example we'd likely also need to introduce each of - // `s.generics()` as well as a trait constraint for each generic - // to ensure they also implement the trait. - let typ = s.as_type(); - quote { - impl DoNothing for $typ { - fn do_nothing(self) { - // Traits can't tell us what to do - println("something"); - } - } - } - } - - // Since `DoNothing` is a simple trait which: - // 1. Only has one method - // 2. Does not have any generics on the trait itself - // We can use `std::meta::make_trait_impl` to help us out. - // This helper function will generate our impl for us along with any - // necessary where clauses and still provides a flexible interface - // for us to work on each field on the struct. - comptime fn derive_do_nothing_alt(s: StructDefinition) -> Quoted { - let trait_name = quote { DoNothing }; - let method_signature = quote { fn do_nothing(self) }; - - // Call `do_nothing` recursively on each field in the struct - let for_each_field = |field_name| quote { self.$field_name.do_nothing(); }; - - // Some traits like Eq want to join each field expression with something like `&`. - // We don't need that here - let join_fields_with = quote {}; - - // The body function is a spot to insert any extra setup/teardown needed. - // We'll insert our println here. Since we recur on each field, we should see - // one println for the struct itself, followed by a println for every field (recursively). - let body = |body| quote { - println("something"); - $body - }; - crate::meta::make_trait_impl( - s, - trait_name, - method_signature, - for_each_field, - join_fields_with, - body, - ) - } -``` -> Source code: noir_stdlib/src/meta/mod.nr#L191-L249 - diff --git a/docs/versioned_docs/version-v0.38.0/noir/concepts/control_flow.md b/docs/versioned_docs/version-v0.38.0/noir/concepts/control_flow.md deleted file mode 100644 index b365bb22728..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/concepts/control_flow.md +++ /dev/null @@ -1,79 +0,0 @@ ---- -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] -sidebar_position: 2 ---- - -## 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); -``` - -## 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 -} -``` - -Alternatively, `start..=end` can be used for a range that is inclusive on both ends. - -The index for loops is of type `u64`. - -### Break and Continue - -In unconstrained code, `break` and `continue` are also allowed in `for` loops. These are only allowed -in unconstrained code since normal constrained code requires that Noir knows exactly how many iterations -a loop may have. `break` and `continue` can be used like so: - -```rust -for i in 0 .. 10 { - println("Iteration start") - - if i == 2 { - continue; - } - - if i == 5 { - break; - } - - println(i); -} -println("Loop end") -``` - -When used, `break` will end the current loop early and jump to the statement after the for loop. In the example -above, the `break` will stop the loop and jump to the `println("Loop end")`. - -`continue` will stop the current iteration of the loop, and jump to the start of the next iteration. In the example -above, `continue` will jump to `println("Iteration start")` when used. Note that the loop continues as normal after this. -The iteration variable `i` is still increased by one as normal when `continue` is used. - -`break` and `continue` cannot currently be used to jump out of more than a single loop at a time. diff --git a/docs/versioned_docs/version-v0.38.0/noir/concepts/data_bus.mdx b/docs/versioned_docs/version-v0.38.0/noir/concepts/data_bus.mdx deleted file mode 100644 index e55e58622ce..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/concepts/data_bus.mdx +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: Data Bus -sidebar_position: 13 ---- -import Experimental from '@site/src/components/Notes/_experimental.mdx'; - - - -The data bus is an optimization that the backend can use to make recursion more efficient. -In order to use it, you must define some inputs of the program entry points (usually the `main()` -function) with the `call_data` modifier, and the return values with the `return_data` modifier. -These modifiers are incompatible with `pub` and `mut` modifiers. - -## Example - -```rust -fn main(mut x: u32, y: call_data u32, z: call_data [u32;4] ) -> return_data u32 { - let a = z[x]; - a+y -} -``` - -As a result, both call_data and return_data will be treated as private inputs and encapsulated into a read-only array each, for the backend to process. diff --git a/docs/versioned_docs/version-v0.38.0/noir/concepts/data_types/_category_.json b/docs/versioned_docs/version-v0.38.0/noir/concepts/data_types/_category_.json deleted file mode 100644 index 5d694210bbf..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/concepts/data_types/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "position": 0, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.38.0/noir/concepts/data_types/arrays.md b/docs/versioned_docs/version-v0.38.0/noir/concepts/data_types/arrays.md deleted file mode 100644 index 289145a8c4d..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/concepts/data_types/arrays.md +++ /dev/null @@ -1,276 +0,0 @@ ---- -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, - ] -sidebar_position: 4 ---- - -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.mdx), 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]; -``` - -However, multidimensional slices are not supported. For example, the following code will error at compile time: - -```rust -let slice : [[Field]] = &[]; -``` - -## 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. -Each of these functions are located within the generic impl `impl [T; N] {`. -So anywhere `self` appears, it refers to the variable `self: [T; N]`. - -### len - -Returns the length of an array - -```rust -fn len(self) -> 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(self) -> [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. The ordering function must return true if the first argument should be sorted to be before the second argument or is equal to the second argument. - -Using this method with an operator like `<` that does not return `true` for equal values will result in an assertion failure for arrays with equal elements. - -```rust -fn sort_via(self, 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(self, 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(self, 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 the starting element. - -Requires `self` to be non-empty. - -```rust -fn reduce(self, 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(self, 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(self, 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); -} -``` - -### as_str_unchecked - -Converts a byte array of type `[u8; N]` to a string. Note that this performs no UTF-8 validation - -the given array is interpreted as-is as a string. - -```rust -impl [u8; N] { - pub fn as_str_unchecked(self) -> str -} -``` - -example: - -```rust -fn main() { - let hi = [104, 105].as_str_unchecked(); - assert_eq(hi, "hi"); -} -``` diff --git a/docs/versioned_docs/version-v0.38.0/noir/concepts/data_types/booleans.md b/docs/versioned_docs/version-v0.38.0/noir/concepts/data_types/booleans.md deleted file mode 100644 index 2507af710e7..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/concepts/data_types/booleans.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -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, - ] -sidebar_position: 2 ---- - - -The `bool` type in Noir has two possible values: `true` and `false`: - -```rust -fn main() { - let t = true; - let f: bool = false; -} -``` - -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.md) and -[Assert Function](../assert.md) sections. diff --git a/docs/versioned_docs/version-v0.38.0/noir/concepts/data_types/fields.md b/docs/versioned_docs/version-v0.38.0/noir/concepts/data_types/fields.md deleted file mode 100644 index b9b56f7ecc3..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/concepts/data_types/fields.md +++ /dev/null @@ -1,246 +0,0 @@ ---- -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, - ] -sidebar_position: 0 ---- - -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 title="to_le_bits" showLineNumbers -pub fn to_le_bits(self: Self) -> [u1; N] {} -``` -> Source code: noir_stdlib/src/field/mod.nr#L32-L34 - - -example: - -```rust title="to_le_bits_example" showLineNumbers -fn test_to_le_bits() { - let field = 2; - let bits: [u1; 8] = field.to_le_bits(); - assert_eq(bits, [0, 1, 0, 0, 0, 0, 0, 0]); - } -``` -> Source code: noir_stdlib/src/field/mod.nr#L276-L282 - - - -### to_be_bits - -Transforms the field into an array of bits, Big Endian. - -```rust title="to_be_bits" showLineNumbers -pub fn to_be_bits(self: Self) -> [u1; N] {} -``` -> Source code: noir_stdlib/src/field/mod.nr#L48-L50 - - -example: - -```rust title="to_be_bits_example" showLineNumbers -fn test_to_be_bits() { - let field = 2; - let bits: [u1; 8] = field.to_be_bits(); - assert_eq(bits, [0, 0, 0, 0, 0, 0, 1, 0]); - } -``` -> Source code: noir_stdlib/src/field/mod.nr#L267-L273 - - - -### to_le_bytes - -Transforms into an array of bytes, Little Endian - -```rust title="to_le_bytes" showLineNumbers -pub fn to_le_bytes(self: Self) -> [u8; N] { -``` -> Source code: noir_stdlib/src/field/mod.nr#L61-L63 - - -example: - -```rust title="to_le_bytes_example" showLineNumbers -fn test_to_le_bytes() { - let field = 2; - let bytes: [u8; 8] = field.to_le_bytes(); - assert_eq(bytes, [2, 0, 0, 0, 0, 0, 0, 0]); - assert_eq(Field::from_le_bytes::<8>(bytes), field); - } -``` -> Source code: noir_stdlib/src/field/mod.nr#L295-L302 - - -### to_be_bytes - -Transforms into an array of bytes, Big Endian - -```rust title="to_be_bytes" showLineNumbers -pub fn to_be_bytes(self: Self) -> [u8; N] { -``` -> Source code: noir_stdlib/src/field/mod.nr#L94-L96 - - -example: - -```rust title="to_be_bytes_example" showLineNumbers -fn test_to_be_bytes() { - let field = 2; - let bytes: [u8; 8] = field.to_be_bytes(); - assert_eq(bytes, [0, 0, 0, 0, 0, 0, 0, 2]); - assert_eq(Field::from_be_bytes::<8>(bytes), field); - } -``` -> Source code: noir_stdlib/src/field/mod.nr#L285-L292 - - - -### to_le_radix - -Decomposes into an array over the specified base, Little Endian - -```rust title="to_le_radix" showLineNumbers -pub fn to_le_radix(self: Self, radix: u32) -> [u8; N] { - // Brillig does not need an immediate radix - if !crate::runtime::is_unconstrained() { - crate::assert_constant(radix); - } - self.__to_le_radix(radix) - } -``` -> Source code: noir_stdlib/src/field/mod.nr#L118-L126 - - - -example: - -```rust title="to_le_radix_example" showLineNumbers -fn test_to_le_radix() { - let field = 2; - let bytes: [u8; 8] = field.to_le_radix(256); - assert_eq(bytes, [2, 0, 0, 0, 0, 0, 0, 0]); - assert_eq(Field::from_le_bytes::<8>(bytes), field); - } -``` -> Source code: noir_stdlib/src/field/mod.nr#L315-L322 - - - -### to_be_radix - -Decomposes into an array over the specified base, Big Endian - -```rust title="to_be_radix" showLineNumbers -pub fn to_be_radix(self: Self, radix: u32) -> [u8; N] { - // Brillig does not need an immediate radix - if !crate::runtime::is_unconstrained() { - crate::assert_constant(radix); - } - self.__to_be_radix(radix) - } -``` -> Source code: noir_stdlib/src/field/mod.nr#L128-L136 - - -example: - -```rust title="to_be_radix_example" showLineNumbers -fn test_to_be_radix() { - let field = 2; - let bytes: [u8; 8] = field.to_be_radix(256); - assert_eq(bytes, [0, 0, 0, 0, 0, 0, 0, 2]); - assert_eq(Field::from_be_bytes::<8>(bytes), field); - } -``` -> Source code: noir_stdlib/src/field/mod.nr#L305-L312 - - - -### 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); -} -``` - -### assert_max_bit_size - -Adds a constraint to specify that the field can be represented with `bit_size` number of bits - -```rust title="assert_max_bit_size" showLineNumbers -pub fn assert_max_bit_size(self) { -``` -> Source code: noir_stdlib/src/field/mod.nr#L10-L12 - - -example: - -```rust -fn main() { - let field = 2 - field.assert_max_bit_size(32); -} -``` - -### 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 -``` - - -### lt - -Returns true if the field is less than the other field - -```rust -pub fn lt(self, another: Field) -> bool -``` diff --git a/docs/versioned_docs/version-v0.38.0/noir/concepts/data_types/function_types.md b/docs/versioned_docs/version-v0.38.0/noir/concepts/data_types/function_types.md deleted file mode 100644 index f6121af17e2..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/concepts/data_types/function_types.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: Function types -sidebar_position: 10 ---- - -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](../lambdas.md) for more details. diff --git a/docs/versioned_docs/version-v0.38.0/noir/concepts/data_types/index.md b/docs/versioned_docs/version-v0.38.0/noir/concepts/data_types/index.md deleted file mode 100644 index 0f2db2b2d75..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/concepts/data_types/index.md +++ /dev/null @@ -1,126 +0,0 @@ ---- -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](../generics.md): - -```rust -type Id = Size; - -fn main() { - let id: Id = 1; - let zero: u32 = 0; - assert(zero + 1 == id); -} -``` - -Type aliases can even refer to other aliases. An error will be issued if they form a cycle: - -```rust -// Ok! -type A = B; -type B = Field; - -type Bad1 = Bad2; - -// error: Dependency cycle found -type Bad2 = Bad1; -// ^^^^^^^^^^^ 'Bad2' recursively depends on itself: Bad2 -> Bad1 -> Bad2 -``` - -By default, like functions, type aliases are private to the module they exist in. You can use `pub` -to make the type alias public or `pub(crate)` to make it public to just its crate: - -```rust -// This type alias is now public -pub type Id = u8; -``` - -## Wildcard Type -Noir can usually infer the type of the variable from the context, so specifying the type of a variable is only required when it cannot be inferred. However, specifying a complex type can be tedious, especially when it has multiple generic arguments. Often some of the generic types can be inferred from the context, and Noir only needs a hint to properly infer the other types. We can partially specify a variable's type by using `_` as a marker, indicating where we still want the compiler to infer the type. - -```rust -let a: [_; 4] = foo(b); -``` - - -### BigInt - -You can achieve BigInt functionality using the [Noir BigInt](https://github.com/shuklaayush/noir-bigint) library. diff --git a/docs/versioned_docs/version-v0.38.0/noir/concepts/data_types/integers.md b/docs/versioned_docs/version-v0.38.0/noir/concepts/data_types/integers.md deleted file mode 100644 index a1d59bf3166..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/concepts/data_types/integers.md +++ /dev/null @@ -1,156 +0,0 @@ ---- -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] -sidebar_position: 1 ---- - -An integer type is a range constrained field type. -The Noir frontend supports both unsigned and signed integer types. -The allowed sizes are 1, 8, 16, 32 and 64 bits. - -:::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\\$). - -## 128 bits Unsigned Integers - -The built-in structure `U128` allows you to use 128-bit unsigned integers almost like a native integer type. However, there are some differences to keep in mind: -- You cannot cast between a native integer and `U128` -- There is a higher performance cost when using `U128`, compared to a native type. - -Conversion between unsigned integer types and U128 are done through the use of `from_integer` and `to_integer` functions. `from_integer` also accepts the `Field` type as input. - -```rust -fn main() { - let x = U128::from_integer(23); - let y = U128::from_hex("0x7"); - let z = x + y; - assert(z.to_integer() == 30); -} -``` - -`U128` is implemented with two 64 bits limbs, representing the low and high bits, which explains the performance cost. You should expect `U128` to be twice more costly for addition and four times more costly for multiplication. -You can construct a U128 from its limbs: -```rust -fn main(x: u64, y: u64) { - let x = U128::from_u64s_be(x,y); - assert(z.hi == x as Field); - assert(z.lo == y as Field); -} -``` - -Note that the limbs are stored as Field elements in order to avoid unnecessary conversions. -Apart from this, most operations will work as usual: - -```rust -fn main(x: U128, y: U128) { - // multiplication - let c = x * y; - // addition and subtraction - let c = c - x + y; - // division - let c = x / y; - // bit operation; - let c = x & y | y; - // bit shift - let c = x << y; - // comparisons; - let c = x < y; - let c = x == y; -} -``` - -## 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 execute -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 - -fn main(x: u8, y: u8) -> pub u8 { - std::wrapping_add(x, y) -} -``` diff --git a/docs/versioned_docs/version-v0.38.0/noir/concepts/data_types/references.md b/docs/versioned_docs/version-v0.38.0/noir/concepts/data_types/references.md deleted file mode 100644 index a5293d11cfb..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/concepts/data_types/references.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: References -sidebar_position: 9 ---- - -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.38.0/noir/concepts/data_types/slices.mdx b/docs/versioned_docs/version-v0.38.0/noir/concepts/data_types/slices.mdx deleted file mode 100644 index cfee564a302..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/concepts/data_types/slices.mdx +++ /dev/null @@ -1,358 +0,0 @@ ---- -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] -sidebar_position: 5 ---- - -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 -fn main() -> pub u32 { - let mut slice: [Field] = &[0; 2]; - - let mut new_slice = slice.push_back(6); - new_slice.len() -} -``` - -To write a slice literal, use a preceding ampersand as in: `&[0; 2]` or -`&[1, 2, 3]`. - -It is important to note that slices are not references to arrays. In Noir, -`&[..]` is more similar to an immutable, growable vector. - -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); -``` - -### len - -Returns the length of a slice - -```rust -fn len(self) -> Field -``` - -Example: - -```rust -fn main() { - let slice = &[42, 42]; - assert(slice.len() == 2); -} -``` - -### as_array - -Converts this slice into an array. - -Make sure to specify the size of the resulting array. -Panics if the resulting array length is different than the slice's length. - -```rust -fn as_array(self) -> [T; N] -``` - -Example: - -```rust -fn main() { - let slice = &[5, 6]; - - // Always specify the length of the resulting array! - let array: [Field; 2] = slice.as_array(); - - assert(array[0] == slice[0]); - assert(array[1] == slice[1]); -} -``` - -### map - -Applies a function to each element of the slice, returning a new slice containing the mapped elements. - -```rust -fn map(self, f: fn[Env](T) -> U) -> [U] -``` - -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 slice, returning the final accumulated value. The first -parameter is the initial value. - -```rust -fn fold(self, mut accumulator: U, f: fn[Env](U, T) -> U) -> U -``` - -This is a left fold, so the given function will be applied to the accumulator and first element of -the slice, 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 slice = &[2, 2, 2, 2, 2]; - let folded = slice.fold(0, |a, b| a + b); - assert(folded == 10); -} - -``` - -### reduce - -Same as fold, but uses the first element as the starting element. - -```rust -fn reduce(self, f: fn[Env](T, T) -> T) -> T -``` - -example: - -```rust -fn main() { - let slice = &[2, 2, 2, 2, 2]; - let reduced = slice.reduce(|a, b| a + b); - assert(reduced == 10); -} -``` - -### filter - -Returns a new slice containing only elements for which the given predicate returns true. - -```rust -fn filter(self, f: fn[Env](T) -> bool) -> Self -``` - -example: - -```rust -fn main() { - let slice = &[1, 2, 3, 4, 5]; - let odds = slice.filter(|x| x % 2 == 1); - assert_eq(odds, &[1, 3, 5]); -} -``` - -### join - -Flatten each element in the slice into one value, separated by `separator`. - -Note that although slices implement `Append`, `join` cannot be used on slice -elements since nested slices are prohibited. - -```rust -fn join(self, separator: T) -> T where T: Append -``` - -example: - -```rust -struct Accumulator { - total: Field, -} - -// "Append" two accumulators by adding them -impl Append for Accumulator { - fn empty() -> Self { - Self { total: 0 } - } - - fn append(self, other: Self) -> Self { - Self { total: self.total + other.total } - } -} - -fn main() { - let slice = &[1, 2, 3, 4, 5].map(|total| Accumulator { total }); - - let result = slice.join(Accumulator::empty()); - assert_eq(result, Accumulator { total: 15 }); - - // We can use a non-empty separator to insert additional elements to sum: - let separator = Accumulator { total: 10 }; - let result = slice.join(separator); - assert_eq(result, Accumulator { total: 55 }); -} -``` - -### all - -Returns true if all the elements satisfy the given predicate - -```rust -fn all(self, predicate: fn[Env](T) -> bool) -> bool -``` - -example: - -```rust -fn main() { - let slice = &[2, 2, 2, 2, 2]; - let all = slice.all(|a| a == 2); - assert(all); -} -``` - -### any - -Returns true if any of the elements satisfy the given predicate - -```rust -fn any(self, predicate: fn[Env](T) -> bool) -> bool -``` - -example: - -```rust -fn main() { - let slice = &[2, 2, 2, 2, 5]; - let any = slice.any(|a| a == 5); - assert(any); -} - -``` diff --git a/docs/versioned_docs/version-v0.38.0/noir/concepts/data_types/strings.md b/docs/versioned_docs/version-v0.38.0/noir/concepts/data_types/strings.md deleted file mode 100644 index 1fdee42425e..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/concepts/data_types/strings.md +++ /dev/null @@ -1,79 +0,0 @@ ---- -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, - ] -sidebar_position: 3 ---- - - -The string type is a fixed length value defined with `str`. - -You can use strings in `assert()` functions or print them with -`println()`. See more about [Logging](../../standard_library/logging.md). - -```rust - -fn main(message : pub str<11>, hex_as_string : str<4>) { - 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" -``` - -## 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/versioned_docs/version-v0.38.0/noir/concepts/data_types/structs.md b/docs/versioned_docs/version-v0.38.0/noir/concepts/data_types/structs.md deleted file mode 100644 index 29951ae843a..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/concepts/data_types/structs.md +++ /dev/null @@ -1,96 +0,0 @@ ---- -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, - ] -sidebar_position: 8 ---- - -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. - -### Visibility - -By default, like functions, structs are private to the module they exist in. You can use `pub` -to make the struct public or `pub(crate)` to make it public to just its crate: - -```rust -// This struct is now public -pub struct Animal { - hands: Field, - legs: Field, - eyes: u8, -} -``` - -The same applies to struct fields: by default they are private to the module they exist in, -but they can be made `pub` or `pub(crate)`: - -```rust -// This struct is now public -pub struct Animal { - hands: Field, // private to its module - pub(crate) legs: Field, // accessible from the entire crate - pub eyes: u8, // accessible from anywhere -} -``` \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.38.0/noir/concepts/data_types/tuples.md b/docs/versioned_docs/version-v0.38.0/noir/concepts/data_types/tuples.md deleted file mode 100644 index 2ec5c9c4113..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/concepts/data_types/tuples.md +++ /dev/null @@ -1,48 +0,0 @@ ---- -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, - ] -sidebar_position: 7 ---- - -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.38.0/noir/concepts/functions.md b/docs/versioned_docs/version-v0.38.0/noir/concepts/functions.md deleted file mode 100644 index f656cdfd97a..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/concepts/functions.md +++ /dev/null @@ -1,226 +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] -sidebar_position: 1 ---- - -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](../../tooling/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](./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](./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](./unconstrained.md) and [NoirJS](../../reference/NoirJS/noir_js/index.md) for more details. -- **test**: mark the function as unit tests. See [Tests](../../tooling/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.38.0/noir/concepts/generics.md b/docs/versioned_docs/version-v0.38.0/noir/concepts/generics.md deleted file mode 100644 index c180a0ce7e6..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/concepts/generics.md +++ /dev/null @@ -1,251 +0,0 @@ ---- -title: Generics -description: Learn how to use Generics in Noir -keywords: [Noir, Rust, generics, functions, structs] -sidebar_position: 7 ---- - -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 -} -``` - -## Numeric Generics - -If we want to be generic over array lengths (which are type-level integers), we can use numeric -generics. Using these looks similar to using regular generics, but introducing them into scope -requires declaring them with `let MyGenericName: IntegerType`. This can be done anywhere a normal -generic is declared. Instead of types, these generics resolve to integers at compile-time. -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 - } -} -``` - -## 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 print(self) { - for _i in 0 .. self.count { - println(self.value); - } - } -} - -fn main() { - let repeated = RepeatedValue { value: "Hello!", count: 2 }; - repeated.print(); -} -``` - -The `print` function will print `Hello!` an arbitrary number of times, twice in this case. - -## Calling functions on generic parameters - -Since a generic type `T` can represent any type, how can we call functions on the underlying type? -In other words, how can we go from "any type `T`" to "any type `T` that has certain methods available?" - -This is what [traits](../concepts/traits.md) are for in Noir. Here's an example of a function generic over -any type `T` that implements the `Eq` trait for equality: - -```rust -fn first_element_is_equal(array1: [T; N], array2: [T; N]) -> bool - where T: Eq -{ - if (array1.len() == 0) | (array2.len() == 0) { - true - } else { - array1[0] == array2[0] - } -} - -fn main() { - assert(first_element_is_equal([1, 2, 3], [1, 5, 6])); - - // We can use first_element_is_equal for arrays of any type - // as long as we have an Eq impl for the types we pass in - let array = [MyStruct::new(), MyStruct::new()]; - assert(array_eq(array, array, MyStruct::eq)); -} - -impl Eq for MyStruct { - fn eq(self, other: MyStruct) -> bool { - self.foo == other.foo - } -} -``` - -You can find more details on traits and trait implementations on the [traits page](../concepts/traits.md). - -## Manually Specifying Generics with the Turbofish Operator - -There are times when the compiler cannot reasonably infer what type should be used for a generic, or when the developer themselves may want to manually distinguish generic type parameters. This is where the `::<>` turbofish operator comes into play. - -The `::<>` operator can follow a variable or path and can be used to manually specify generic arguments within the angle brackets. -The name "turbofish" comes from that `::<>` looks like a little fish. - -Examples: -```rust -fn main() { - let mut slice = []; - slice = slice.push_back(1); - slice = slice.push_back(2); - // Without turbofish a type annotation would be needed on the left hand side - let array = slice.as_array::<2>(); -} -``` - - -```rust -trait MyTrait { - fn ten() -> Self; -} - -impl MyTrait for Field { - fn ten() -> Self { 10 } -} - -struct Foo { - inner: T -} - -impl Foo { - fn generic_method(_self: Self) -> U where U: MyTrait { - U::ten() - } -} - -fn example() { - let foo: Foo = Foo { inner: 1 }; - // Using a type other than `Field` here (e.g. u32) would fail as - // there is no matching impl for `u32: MyTrait`. - // - // Substituting the `10` on the left hand side of this assert - // with `10 as u32` would also fail with a type mismatch as we - // are expecting a `Field` from the right hand side. - assert(10 as u32 == foo.generic_method::()); -} -``` - -## Arithmetic Generics - -In addition to numeric generics, Noir also allows a limited form of arithmetic on generics. -When you have a numeric generic such as `N`, you can use the following operators on it in a -type position: `+`, `-`, `*`, `/`, and `%`. - -Note that type checking arithmetic generics is a best effort guess from the compiler and there -are many cases of types that are equal that the compiler may not see as such. For example, -we know that `T * (N + M)` should be equal to `T*N + T*M` but the compiler does not currently -apply the distributive law and thus sees these as different types. - -Even with this limitation though, the compiler can handle common cases decently well: - -```rust -trait Serialize { - fn serialize(self) -> [Field; N]; -} - -impl Serialize<1> for Field { - fn serialize(self) -> [Field; 1] { - [self] - } -} - -impl Serialize for [T; N] - where T: Serialize { .. } - -impl Serialize for (T, U) - where T: Serialize, U: Serialize { .. } - -fn main() { - let data = (1, [2, 3, 4]); - assert_eq(data.serialize().len(), 4); -} -``` - -Note that if there is any over or underflow the types will fail to unify: - -```rust title="underflow-example" showLineNumbers -fn pop(array: [Field; N]) -> [Field; N - 1] { - let mut result: [Field; N - 1] = std::mem::zeroed(); - for i in 0..N - 1 { - result[i] = array[i]; - } - result -} - -fn main() { - // error: Could not determine array length `(0 - 1)` - pop([]); -} -``` -> Source code: test_programs/compile_failure/arithmetic_generics_underflow/src/main.nr#L1-L14 - - -This also applies if there is underflow in an intermediate calculation: - -```rust title="intermediate-underflow-example" showLineNumbers -fn main() { - // From main it looks like there's nothing sketchy going on - seems_fine([]); -} - -// Since `seems_fine` says it can receive and return any length N -fn seems_fine(array: [Field; N]) -> [Field; N] { - // But inside `seems_fine` we pop from the array which - // requires the length to be greater than zero. - - // error: Could not determine array length `(0 - 1)` - push_zero(pop(array)) -} - -fn pop(array: [Field; N]) -> [Field; N - 1] { - let mut result: [Field; N - 1] = std::mem::zeroed(); - for i in 0..N - 1 { - result[i] = array[i]; - } - result -} - -fn push_zero(array: [Field; N]) -> [Field; N + 1] { - let mut result: [Field; N + 1] = std::mem::zeroed(); - for i in 0..N { - result[i] = array[i]; - } - // index N is already zeroed - result -} -``` -> Source code: test_programs/compile_failure/arithmetic_generics_intermediate_underflow/src/main.nr#L1-L32 - diff --git a/docs/versioned_docs/version-v0.38.0/noir/concepts/globals.md b/docs/versioned_docs/version-v0.38.0/noir/concepts/globals.md deleted file mode 100644 index 6b8314399a2..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/concepts/globals.md +++ /dev/null @@ -1,82 +0,0 @@ ---- -title: Global Variables -description: - Learn about global variables in Noir. Discover how - to declare, modify, and use them in your programs. -keywords: [noir programming language, globals, global variables, constants] -sidebar_position: 8 ---- - -## Globals - - -Noir supports global variables. The global's type can be inferred by the compiler entirely: - -```rust -global N = 5; // Same as `global N: Field = 5` - -global TUPLE = (3, 2); - -fn main() { - assert(N == 5); - assert(N == TUPLE.0 + TUPLE.1); -} -``` - -:::info - -Globals can be defined as any expression, so long as they don't depend on themselves - otherwise there would be a dependency cycle! For example: - -```rust -global T = foo(T); // dependency error -``` - -::: - - -If they are initialized to a literal integer, globals can be used to specify an array's length: - -```rust -global N: u32 = 2; - -fn main(y : [Field; N]) { - assert(y[0] == y[1]) -} -``` - -A global from another module can be imported or referenced externally like any other name: - -```rust -global N = 20; - -fn main() { - assert(my_submodule::N != N); -} - -mod my_submodule { - global N: Field = 10; -} -``` - -When a global is used, Noir replaces the name with its definition on each occurrence. -This means globals defined using function calls will repeat the call each time they're used: - -```rust -global RESULT = foo(); - -fn foo() -> [Field; 100] { ... } -``` - -This is usually fine since Noir will generally optimize any function call that does not -refer to a program input into a constant. It should be kept in mind however, if the called -function performs side-effects like `println`, as these will still occur on each use. - -### Visibility - -By default, like functions, globals are private to the module they exist in. You can use `pub` -to make the global public or `pub(crate)` to make it public to just its crate: - -```rust -// This global is now public -pub global N = 5; -``` \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.38.0/noir/concepts/lambdas.md b/docs/versioned_docs/version-v0.38.0/noir/concepts/lambdas.md deleted file mode 100644 index be3c7e0b5ca..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/concepts/lambdas.md +++ /dev/null @@ -1,81 +0,0 @@ ---- -title: Lambdas -description: Learn how to use anonymous functions in Noir programming language. -keywords: [Noir programming language, lambda, closure, function, anonymous function] -sidebar_position: 9 ---- - -## 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.38.0/noir/concepts/mutability.md b/docs/versioned_docs/version-v0.38.0/noir/concepts/mutability.md deleted file mode 100644 index fdeef6a87c5..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/concepts/mutability.md +++ /dev/null @@ -1,121 +0,0 @@ ---- -title: Mutability -description: - Learn about mutable variables in Noir. Discover how - to declare, modify, and use them in your programs. -keywords: [noir programming language, mutability in noir, mutable variables] -sidebar_position: 8 ---- - -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; -} -``` - -## Non-local mutability - -Non-local mutability can be achieved through the mutable reference type `&mut T`: - -```rust -fn set_to_zero(x: &mut Field) { - *x = 0; -} - -fn main() { - let mut y = 42; - set_to_zero(&mut y); - assert(*y == 0); -} -``` - -When creating a mutable reference, the original variable being referred to (`y` in this -example) must also be mutable. Since mutable references are a reference type, they must -be explicitly dereferenced via `*` to retrieve the underlying value. Note that this yields -a copy of the value, so mutating this copy will not change the original value behind the -reference: - -```rust -fn main() { - let mut x = 1; - let x_ref = &mut x; - - let mut y = *x_ref; - let y_ref = &mut y; - - x = 2; - *x_ref = 3; - - y = 4; - *y_ref = 5; - - assert(x == 3); - assert(*x_ref == 3); - assert(y == 5); - assert(*y_ref == 5); -} -``` - -Note that types in Noir are actually deeply immutable so the copy that occurs when -dereferencing is only a conceptual copy - no additional constraints will occur. - -Mutable references can also be stored within structs. Note that there is also -no lifetime parameter on these unlike rust. This is because the allocated memory -always lasts the entire program - as if it were an array of one element. - -```rust -struct Foo { - x: &mut Field -} - -impl Foo { - fn incr(mut self) { - *self.x += 1; - } -} - -fn main() { - let foo = Foo { x: &mut 0 }; - foo.incr(); - assert(*foo.x == 1); -} -``` - -In general, you should avoid non-local & shared mutability unless it is needed. Sticking -to only local mutability will improve readability and potentially improve compiler optimizations as well. diff --git a/docs/versioned_docs/version-v0.38.0/noir/concepts/ops.md b/docs/versioned_docs/version-v0.38.0/noir/concepts/ops.md deleted file mode 100644 index c35c36c38a9..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/concepts/ops.md +++ /dev/null @@ -1,98 +0,0 @@ ---- -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, - ] -sidebar_position: 3 ---- - -# 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, shift must be u8 | -| >> | Right shift an integer by another integer amount | Types must be integer, shift must be u8 | -| ! | 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 identically 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.38.0/noir/concepts/oracles.mdx b/docs/versioned_docs/version-v0.38.0/noir/concepts/oracles.mdx deleted file mode 100644 index 77a2ac1550a..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/concepts/oracles.mdx +++ /dev/null @@ -1,29 +0,0 @@ ---- -title: Oracles -description: Dive into how Noir supports Oracles via RPC calls, and learn how to declare an Oracle in Noir with our comprehensive guide. -keywords: - - Noir - - Oracles - - RPC Calls - - Unconstrained Functions - - Programming - - Blockchain -sidebar_position: 6 ---- - -import Experimental from '@site/src/components/Notes/_experimental.mdx'; - - - -Noir has support for Oracles via RPC calls. This means Noir will make an RPC call and use the return value for proof generation. - -Since Oracles are not resolved by Noir, they are [`unconstrained` functions](./unconstrained.md) - -You can declare an Oracle through the `#[oracle()]` flag. Example: - -```rust -#[oracle(get_number_sequence)] -unconstrained fn get_number_sequence(_size: Field) -> [Field] {} -``` - -The timeout for when using an external RPC oracle resolver can be set with the `NARGO_FOREIGN_CALL_TIMEOUT` environment variable. This timeout is in units of milliseconds. diff --git a/docs/versioned_docs/version-v0.38.0/noir/concepts/shadowing.md b/docs/versioned_docs/version-v0.38.0/noir/concepts/shadowing.md deleted file mode 100644 index 5ce6130d201..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/concepts/shadowing.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -title: Shadowing -sidebar_position: 12 ---- - -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.38.0/noir/concepts/traits.md b/docs/versioned_docs/version-v0.38.0/noir/concepts/traits.md deleted file mode 100644 index 9da00a77587..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/concepts/traits.md +++ /dev/null @@ -1,501 +0,0 @@ ---- -title: Traits -description: - Traits in Noir can be used to abstract out a common interface for functions across - several data types. -keywords: [noir programming language, traits, interfaces, generic, protocol] -sidebar_position: 14 ---- - -## Overview - -Traits in Noir are a useful abstraction similar to interfaces or protocols in other languages. Each trait defines -the interface of several methods contained within the trait. Types can then implement this trait by providing -implementations for these methods. For example in the program: - -```rust -struct Rectangle { - width: Field, - height: Field, -} - -impl Rectangle { - fn area(self) -> Field { - self.width * self.height - } -} - -fn log_area(r: Rectangle) { - println(r.area()); -} -``` - -We have a function `log_area` to log the area of a `Rectangle`. Now how should we change the program if we want this -function to work on `Triangle`s as well?: - -```rust -struct Triangle { - width: Field, - height: Field, -} - -impl Triangle { - fn area(self) -> Field { - self.width * self.height / 2 - } -} -``` - -Making `log_area` generic over all types `T` would be invalid since not all types have an `area` method. Instead, we can -introduce a new `Area` trait and make `log_area` generic over all types `T` that implement `Area`: - -```rust -trait Area { - fn area(self) -> Field; -} - -fn log_area(shape: T) where T: Area { - println(shape.area()); -} -``` - -We also need to explicitly implement `Area` for `Rectangle` and `Triangle`. We can do that by changing their existing -impls slightly. Note that the parameter types and return type of each of our `area` methods must match those defined -by the `Area` trait. - -```rust -impl Area for Rectangle { - fn area(self) -> Field { - self.width * self.height - } -} - -impl Area for Triangle { - fn area(self) -> Field { - self.width * self.height / 2 - } -} -``` - -Now we have a working program that is generic over any type of Shape that is used! Others can even use this program -as a library with their own types - such as `Circle` - as long as they also implement `Area` for these types. - -## Where Clauses - -As seen in `log_area` above, when we want to create a function or method that is generic over any type that implements -a trait, we can add a where clause to the generic function. - -```rust -fn log_area(shape: T) where T: Area { - println(shape.area()); -} -``` - -It is also possible to apply multiple trait constraints on the same variable at once by combining traits with the `+` -operator. Similarly, we can have multiple trait constraints by separating each with a comma: - -```rust -fn foo(elements: [T], thing: U) where - T: Default + Add + Eq, - U: Bar, -{ - let mut sum = T::default(); - - for element in elements { - sum += element; - } - - if sum == T::default() { - thing.bar(); - } -} -``` - -## Generic Implementations - -You can add generics to a trait implementation by adding the generic list after the `impl` keyword: - -```rust -trait Second { - fn second(self) -> Field; -} - -impl Second for (T, Field) { - fn second(self) -> Field { - self.1 - } -} -``` - -You can also implement a trait for every type this way: - -```rust -trait Debug { - fn debug(self); -} - -impl Debug for T { - fn debug(self) { - println(self); - } -} - -fn main() { - 1.debug(); -} -``` - -### Generic Trait Implementations With Where Clauses - -Where clauses can be placed on trait implementations themselves to restrict generics in a similar way. -For example, while `impl Foo for T` implements the trait `Foo` for every type, `impl Foo for T where T: Bar` -will implement `Foo` only for types that also implement `Bar`. This is often used for implementing generic types. -For example, here is the implementation for array equality: - -```rust -impl Eq for [T; let N: u32] where T: Eq { - // Test if two arrays have the same elements. - // Because both arrays must have length N, we know their lengths already match. - fn eq(self, other: Self) -> bool { - let mut result = true; - - for i in 0 .. self.len() { - // The T: Eq constraint is needed to call == on the array elements here - result &= self[i] == other[i]; - } - - result - } -} -``` - -Where clauses can also be placed on struct implementations. -For example, here is a method utilizing a generic type that implements the equality trait. - -```rust -struct Foo { - a: u32, - b: T, -} - -impl Foo where T: Eq { - fn eq(self, other: Self) -> bool { - (self.a == other.a) & self.b.eq(other.b) - } -} -``` - -## Generic Traits - -Traits themselves can also be generic by placing the generic arguments after the trait name. These generics are in -scope of every item within the trait. - -```rust -trait Into { - // Convert `self` to type `T` - fn into(self) -> T; -} -``` - -When implementing generic traits the generic arguments of the trait must be specified. This is also true anytime -when referencing a generic trait (e.g. in a `where` clause). - -```rust -struct MyStruct { - array: [Field; 2], -} - -impl Into<[Field; 2]> for MyStruct { - fn into(self) -> [Field; 2] { - self.array - } -} - -fn as_array(x: T) -> [Field; 2] - where T: Into<[Field; 2]> -{ - x.into() -} - -fn main() { - let array = [1, 2]; - let my_struct = MyStruct { array }; - - assert_eq(as_array(my_struct), array); -} -``` - -### Associated Types and Constants - -Traits also support associated types and constraints which can be thought of as additional generics that are referred to by name. - -Here's an example of a trait with an associated type `Foo` and a constant `Bar`: - -```rust -trait MyTrait { - type Foo; - - let Bar: u32; -} -``` - -Now when we're implementing `MyTrait` we also have to provide values for `Foo` and `Bar`: - -```rust -impl MyTrait for Field { - type Foo = i32; - - let Bar: u32 = 11; -} -``` - -Since associated constants can also be used in a type position, its values are limited to only other -expression kinds allowed in numeric generics. - -Note that currently all associated types and constants must be explicitly specified in a trait constraint. -If we leave out any, we'll get an error that we're missing one: - -```rust -// Error! Constraint is missing associated constant for `Bar` -fn foo(x: T) where T: MyTrait { - ... -} -``` - -Because all associated types and constants must be explicitly specified, they are essentially named generics, -although this is set to change in the future. Future versions of Noir will allow users to elide associated types -in trait constraints similar to Rust. When this is done, you may still refer to their value with the `::AssociatedType` -syntax: - -```rust -// Only valid in future versions of Noir: -fn foo(x: T) where T: MyTrait { - let _: ::Foo = ...; -} -``` - -The type as trait syntax is possible in Noir today but is less useful when each type must be explicitly specified anyway: - -```rust -fn foo(x: T) where T: MyTrait { - // Works, but could just use F directly - let _: >::Foo = ...; - - let _: F = ...; -} -``` - -## Trait Methods With No `self` - -A trait can contain any number of methods, each of which have access to the `Self` type which represents each type -that eventually implements the trait. Similarly, the `self` variable is available as well but is not required to be used. -For example, we can define a trait to create a default value for a type. This trait will need to return the `Self` type -but doesn't need to take any parameters: - -```rust -trait Default { - fn default() -> Self; -} -``` - -Implementing this trait can be done similarly to any other trait: - -```rust -impl Default for Field { - fn default() -> Field { - 0 - } -} - -struct MyType {} - -impl Default for MyType { - fn default() -> Field { - MyType {} - } -} -``` - -However, since there is no `self` parameter, we cannot call it via the method call syntax `object.method()`. -Instead, we'll need to refer to the function directly. This can be done either by referring to the -specific impl `MyType::default()` or referring to the trait itself `Default::default()`. In the later -case, type inference determines the impl that is selected. - -```rust -let my_struct = MyStruct::default(); - -let x: Field = Default::default(); -let result = x + Default::default(); -``` - -:::warning - -```rust -let _ = Default::default(); -``` - -If type inference cannot select which impl to use because of an ambiguous `Self` type, an impl will be -arbitrarily selected. This occurs most often when the result of a trait function call with no parameters -is unused. To avoid this, when calling a trait function with no `self` or `Self` parameters or return type, -always refer to it via the implementation type's namespace - e.g. `MyType::default()`. -This is set to change to an error in future Noir versions. - -::: - -## Default Method Implementations - -A trait can also have default implementations of its methods by giving a body to the desired functions. -Note that this body must be valid for all types that may implement the trait. As a result, the only -valid operations on `self` will be operations valid for any type or other operations on the trait itself. - -```rust -trait Numeric { - fn add(self, other: Self) -> Self; - - // Default implementation of double is (self + self) - fn double(self) -> Self { - self.add(self) - } -} -``` - -When implementing a trait with default functions, a type may choose to implement only the required functions: - -```rust -impl Numeric for Field { - fn add(self, other: Field) -> Field { - self + other - } -} -``` - -Or it may implement the optional methods as well: - -```rust -impl Numeric for u32 { - fn add(self, other: u32) -> u32 { - self + other - } - - fn double(self) -> u32 { - self * 2 - } -} -``` - -## Impl Specialization - -When implementing traits for a generic type it is possible to implement the trait for only a certain combination -of generics. This can be either as an optimization or because those specific generics are required to implement the trait. - -```rust -trait Sub { - fn sub(self, other: Self) -> Self; -} - -struct NonZero { - value: T, -} - -impl Sub for NonZero { - fn sub(self, other: Self) -> Self { - let value = self.value - other.value; - assert(value != 0); - NonZero { value } - } -} -``` - -## Overlapping Implementations - -Overlapping implementations are disallowed by Noir to ensure Noir's decision on which impl to select is never ambiguous. -This means if a trait `Foo` is already implemented -by a type `Bar` for all `T`, then we cannot also have a separate impl for `Bar` (or any other -type argument). Similarly, if there is an impl for all `T` such as `impl Debug for T`, we cannot create -any more impls to `Debug` for other types since it would be ambiguous which impl to choose for any given -method call. - -```rust -trait Trait {} - -// Previous impl defined here -impl Trait for (A, B) {} - -// error: Impl for type `(Field, Field)` overlaps with existing impl -impl Trait for (Field, Field) {} -``` - -## Trait Coherence - -Another restriction on trait implementations is coherence. This restriction ensures other crates cannot create -impls that may overlap with other impls, even if several unrelated crates are used as dependencies in the same -program. - -The coherence restriction is: to implement a trait, either the trait itself or the object type must be declared -in the crate the impl is in. - -In practice this often comes up when using types provided by libraries. If a library provides a type `Foo` that does -not implement a trait in the standard library such as `Default`, you may not `impl Default for Foo` in your own crate. -While restrictive, this prevents later issues or silent changes in the program if the `Foo` library later added its -own impl for `Default`. If you are a user of the `Foo` library in this scenario and need a trait not implemented by the -library your choices are to either submit a patch to the library or use the newtype pattern. - -### The Newtype Pattern - -The newtype pattern gets around the coherence restriction by creating a new wrapper type around the library type -that we cannot create `impl`s for. Since the new wrapper type is defined in our current crate, we can create -impls for any trait we need on it. - -```rust -struct Wrapper { - foo: some_library::Foo, -} - -impl Default for Wrapper { - fn default() -> Wrapper { - Wrapper { - foo: some_library::Foo::new(), - } - } -} -``` - -Since we have an impl for our own type, the behavior of this code will not change even if `some_library` is updated -to provide its own `impl Default for Foo`. The downside of this pattern is that it requires extra wrapping and -unwrapping of values when converting to and from the `Wrapper` and `Foo` types. - -### Trait Inheritance - -Sometimes, you might need one trait to use another trait’s functionality (like "inheritance" in some other languages). In this case, you can specify this relationship by listing any child traits after the parent trait's name and a colon. Now, whenever the parent trait is implemented it will require the child traits to be implemented as well. A parent trait is also called a "super trait." - -```rust -trait Person { - fn name(self) -> String; -} - -// Person is a supertrait of Student. -// Implementing Student requires you to also impl Person. -trait Student: Person { - fn university(self) -> String; -} - -trait Programmer { - fn fav_language(self) -> String; -} - -// CompSciStudent (computer science student) is a subtrait of both Programmer -// and Student. Implementing CompSciStudent requires you to impl both supertraits. -trait CompSciStudent: Programmer + Student { - fn git_username(self) -> String; -} -``` - -### Visibility - -By default, like functions, traits are private to the module they exist in. You can use `pub` -to make the trait public or `pub(crate)` to make it public to just its crate: - -```rust -// This trait is now public -pub trait Trait {} -``` \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.38.0/noir/concepts/unconstrained.md b/docs/versioned_docs/version-v0.38.0/noir/concepts/unconstrained.md deleted file mode 100644 index b5221b8d2dd..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/concepts/unconstrained.md +++ /dev/null @@ -1,104 +0,0 @@ ---- -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] -sidebar_position: 5 ---- - -Unconstrained functions are functions which do not constrain any of the included computation and allow for non-deterministic 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 AND 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 = unsafe { - 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). - -Note that in order to invoke unconstrained functions we need to wrap them in an `unsafe` block, -to make it clear that the call is unconstrained. - -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. - -## Break and Continue - -In addition to loops over runtime bounds, `break` and `continue` are also available in unconstrained code. See [break and continue](../concepts/control_flow.md#break-and-continue) diff --git a/docs/versioned_docs/version-v0.38.0/noir/modules_packages_crates/_category_.json b/docs/versioned_docs/version-v0.38.0/noir/modules_packages_crates/_category_.json deleted file mode 100644 index 1debcfe7675..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/modules_packages_crates/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "Modules, Packages and Crates", - "position": 2, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.38.0/noir/modules_packages_crates/crates_and_packages.md b/docs/versioned_docs/version-v0.38.0/noir/modules_packages_crates/crates_and_packages.md deleted file mode 100644 index 95ee9f52ab2..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/modules_packages_crates/crates_and_packages.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -title: Crates and Packages -description: Learn how to use Crates and Packages in your Noir project -keywords: [Nargo, dependencies, package management, crates, package] -sidebar_position: 0 ---- - -## 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/noir-projects/noir-contracts/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.38.0/noir/modules_packages_crates/dependencies.md b/docs/versioned_docs/version-v0.38.0/noir/modules_packages_crates/dependencies.md deleted file mode 100644 index 24e02de08fe..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/modules_packages_crates/dependencies.md +++ /dev/null @@ -1,124 +0,0 @@ ---- -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] -sidebar_position: 1 ---- - -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 = "noir-contracts/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 -└── lib_a - ├── Nargo.toml - └── src - └── lib.nr -``` - -Inside of the binary crate, you can specify: - -```toml -# Nargo.toml - -[dependencies] -lib_a = { path = "../lib_a" } -``` - -## 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 lib_a referenced above: - -```rust -use ecrecover; -use lib_a; -``` - -You can also import only the specific parts of dependency that you want to use, like so: - -```rust -use std::hash::sha256; -use std::scalar_mul::fixed_base_embedded_curve; -``` - -Lastly, as demonstrated in the -[elliptic curve example](../standard_library/cryptographic_primitives/ec_primitives.md#examples), you -can import multiple items in the same line by enclosing them in curly braces: - -```rust -use std::ec::tecurve::affine::{Curve, Point}; -``` - -We don't have a way to consume libraries from inside a [workspace](./workspaces.md) 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 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.38.0/noir/modules_packages_crates/modules.md b/docs/versioned_docs/version-v0.38.0/noir/modules_packages_crates/modules.md deleted file mode 100644 index 14aa1f0579a..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/modules_packages_crates/modules.md +++ /dev/null @@ -1,221 +0,0 @@ ---- -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] -sidebar_position: 2 ---- - -Noir's module system follows the same convention as the _newer_ version of Rust's module system. - -## Purpose of Modules - -Modules are used to organize 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 - -``` - -The module filename may also be the name of the module as a directory with the contents in a -file named `mod.nr` within that directory. The above example can alternatively be expressed like this: - -Filename : `src/main.nr` - -```rust -mod foo; - -fn main() { - foo::hello_world(); -} -``` - -Filename : `src/foo/mod.nr` - -```rust -fn from_foo() {} -``` - -Note that it's an error to have both files `src/foo.nr` and `src/foo/mod.nr` in the filesystem. - -### 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 -``` - -Similar to importing a module in the crate root, modules can be placed in a `mod.nr` file, like this: - -Filename : `src/main.nr` - -```rust -mod foo; - -fn main() { - foo::from_foo(); -} -``` - -Filename : `src/foo/mod.nr` - -```rust -mod bar; -fn from_foo() {} -``` - -Filename : `src/foo/bar/mod.nr` - -```rust -fn from_bar() {} -``` - -### Referencing a parent module - -Given a submodule, you can refer to its parent module using the `super` keyword. - -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 -// Same as bar::from_foo -use super::from_foo; - -fn from_bar() { - from_foo(); // invokes super::from_foo(), which is bar::from_foo() - super::from_foo(); // also invokes bar::from_foo() -} -``` - -### `use` visibility - -`use` declarations are private to the containing module, by default. However, like functions, -they can be marked as `pub` or `pub(crate)`. Such a use declaration serves to _re-export_ a name. -A public `use` declaration can therefore redirect some public name to a different target definition: -even a definition with a private canonical path, inside a different module. - -An example of re-exporting: - -```rust -mod some_module { - pub use foo::{bar, baz}; - mod foo { - pub fn bar() {} - pub fn baz() {} - } -} - -fn main() { - some_module::bar(); - some_module::baz(); -} -``` - -In this example, the module `some_module` re-exports two public names defined in `foo`. - -### Visibility - -By default, like functions, modules are private to the module (or crate) they exist in. You can use `pub` -to make the module public or `pub(crate)` to make it public to just its crate: - -```rust -// This module is now public and can be seen by other crates. -pub mod foo; -``` \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.38.0/noir/modules_packages_crates/workspaces.md b/docs/versioned_docs/version-v0.38.0/noir/modules_packages_crates/workspaces.md deleted file mode 100644 index 513497f12bf..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/modules_packages_crates/workspaces.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -title: Workspaces -sidebar_position: 3 ---- - -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 -│ │ └── Prover.toml -│ │ └── src -│ │ └── main.nr -│ └── b -│ ├── Nargo.toml -│ └── Prover.toml -│ └── src -│ └── main.nr -│ -└── Nargo.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.38.0/noir/standard_library/_category_.json b/docs/versioned_docs/version-v0.38.0/noir/standard_library/_category_.json deleted file mode 100644 index af04c0933fd..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/standard_library/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "Standard Library", - "position": 1, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.38.0/noir/standard_library/bigint.md b/docs/versioned_docs/version-v0.38.0/noir/standard_library/bigint.md deleted file mode 100644 index 05c3011634f..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/standard_library/bigint.md +++ /dev/null @@ -1,127 +0,0 @@ ---- -title: Big Integers -description: How to use big integers from Noir standard library -keywords: - [ - Big Integer, - Noir programming language, - Noir libraries, - ] ---- - -The BigInt module in the standard library exposes some class of integers which do not fit (well) into a Noir native field. It implements modulo arithmetic, modulo a 'big' prime number. - -:::note - -The module can currently be considered as `Field`s with fixed modulo sizes used by a set of elliptic curves, in addition to just the native curve. [More work](https://github.com/noir-lang/noir/issues/510) is needed to achieve arbitrarily sized big integers. - -:::note - -`nargo` can be built with `--profile release-pedantic` to enable extra overflow checks which may affect `BigInt` results in some cases. -Consider the [`noir-bignum`](https://github.com/noir-lang/noir-bignum) library for an optimized alternative approach. - -::: - -Currently 6 classes of integers (i.e 'big' prime numbers) are available in the module, namely: - -- BN254 Fq: Bn254Fq -- BN254 Fr: Bn254Fr -- Secp256k1 Fq: Secpk1Fq -- Secp256k1 Fr: Secpk1Fr -- Secp256r1 Fr: Secpr1Fr -- Secp256r1 Fq: Secpr1Fq - -Where XXX Fq and XXX Fr denote respectively the order of the base and scalar field of the (usual) elliptic curve XXX. -For instance the big integer 'Secpk1Fq' in the standard library refers to integers modulo $2^{256}-2^{32}-977$. - -Feel free to explore the source code for the other primes: - -```rust title="big_int_definition" showLineNumbers -pub struct BigInt { - pointer: u32, - modulus: u32, -} -``` -> Source code: noir_stdlib/src/bigint.nr#L28-L33 - - -## Example usage - -A common use-case is when constructing a big integer from its bytes representation, and performing arithmetic operations on it: - -```rust title="big_int_example" showLineNumbers -fn big_int_example(x: u8, y: u8) { - let a = Secpk1Fq::from_le_bytes(&[x, y, 0, 45, 2]); - let b = Secpk1Fq::from_le_bytes(&[y, x, 9]); - let c = (a + b) * b / a; - let d = c.to_le_bytes(); - println(d[0]); -} -``` -> Source code: test_programs/execution_success/bigint/src/main.nr#L74-L82 - - -## Methods - -The available operations for each big integer are: - -### from_le_bytes - -Construct a big integer from its little-endian bytes representation. Example: - -```rust - // Construct a big integer from a slice of bytes - let a = Secpk1Fq::from_le_bytes(&[x, y, 0, 45, 2]); - // Construct a big integer from an array of 32 bytes - let a = Secpk1Fq::from_le_bytes_32([1;32]); - ``` - -Sure, here's the formatted version of the remaining methods: - -### to_le_bytes - -Return the little-endian bytes representation of a big integer. Example: - -```rust -let bytes = a.to_le_bytes(); -``` - -### add - -Add two big integers. Example: - -```rust -let sum = a + b; -``` - -### sub - -Subtract two big integers. Example: - -```rust -let difference = a - b; -``` - -### mul - -Multiply two big integers. Example: - -```rust -let product = a * b; -``` - -### div - -Divide two big integers. Note that division is field division and not euclidean division. Example: - -```rust -let quotient = a / b; -``` - -### eq - -Compare two big integers. Example: - -```rust -let are_equal = a == b; -``` diff --git a/docs/versioned_docs/version-v0.38.0/noir/standard_library/black_box_fns.md b/docs/versioned_docs/version-v0.38.0/noir/standard_library/black_box_fns.md deleted file mode 100644 index d6079ab182c..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/standard_library/black_box_fns.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -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. - -The ACVM spec defines a set of blackbox functions which backends will be expected to implement. This allows backends to use optimized implementations of these constraints if they have them, however they may also fallback to less efficient naive implementations if not. - -## Function list - -Here is a list of the current black box functions: - -- [AES128](./cryptographic_primitives/ciphers.mdx#aes128) -- [SHA256](./cryptographic_primitives/hashes.mdx#sha256) -- [Schnorr signature verification](./cryptographic_primitives/schnorr.mdx) -- [Blake2s](./cryptographic_primitives/hashes.mdx#blake2s) -- [Blake3](./cryptographic_primitives/hashes.mdx#blake3) -- [Pedersen Hash](./cryptographic_primitives/hashes.mdx#pedersen_hash) -- [Pedersen Commitment](./cryptographic_primitives/hashes.mdx#pedersen_commitment) -- [ECDSA signature verification](./cryptographic_primitives/ecdsa_sig_verification.mdx) -- [Embedded curve operations (MSM, addition, ...)](./cryptographic_primitives/embedded_curve_ops.mdx) -- AND -- XOR -- RANGE -- [Keccak256](./cryptographic_primitives/hashes.mdx#keccak256) -- [Recursive proof verification](./recursion.mdx) - -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. - -You can view the black box functions defined in the ACVM code [here](https://github.com/noir-lang/noir/blob/master/acvm-repo/acir/src/circuit/black_box_functions.rs). diff --git a/docs/versioned_docs/version-v0.38.0/noir/standard_library/bn254.md b/docs/versioned_docs/version-v0.38.0/noir/standard_library/bn254.md deleted file mode 100644 index 3294f005dbb..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/standard_library/bn254.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -title: Bn254 Field Library ---- - -Noir provides a module in standard library with some optimized functions for bn254 Fr in `std::field::bn254`. - -## decompose - -```rust -fn decompose(x: Field) -> (Field, Field) {} -``` - -Decomposes a single field into two fields, low and high. The low field contains the lower 16 bytes of the input field and the high field contains the upper 16 bytes of the input field. Both field results are range checked to 128 bits. - - -## assert_gt - -```rust -fn assert_gt(a: Field, b: Field) {} -``` - -Asserts that a > b. This will generate less constraints than using `assert(gt(a, b))`. - -## assert_lt - -```rust -fn assert_lt(a: Field, b: Field) {} -``` - -Asserts that a < b. This will generate less constraints than using `assert(lt(a, b))`. - -## gt - -```rust -fn gt(a: Field, b: Field) -> bool {} -``` - -Returns true if a > b. - -## lt - -```rust -fn lt(a: Field, b: Field) -> bool {} -``` - -Returns true if a < b. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.38.0/noir/standard_library/containers/boundedvec.md b/docs/versioned_docs/version-v0.38.0/noir/standard_library/containers/boundedvec.md deleted file mode 100644 index 509b214bf3a..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/standard_library/containers/boundedvec.md +++ /dev/null @@ -1,419 +0,0 @@ ---- -title: Bounded Vectors -keywords: [noir, vector, bounded vector, slice] -sidebar_position: 1 ---- - -A `BoundedVec` is a growable storage similar to a `Vec` except that it -is bounded with a maximum possible length. Unlike `Vec`, `BoundedVec` is not implemented -via slices and thus is not subject to the same restrictions slices are (notably, nested -slices - and thus nested vectors as well - are disallowed). - -Since a BoundedVec is backed by a normal array under the hood, growing the BoundedVec by -pushing an additional element is also more efficient - the length only needs to be increased -by one. - -For these reasons `BoundedVec` should generally be preferred over `Vec` when there -is a reasonable maximum bound that can be placed on the vector. - -Example: - -```rust -let mut vector: BoundedVec = BoundedVec::new(); -for i in 0..5 { - vector.push(i); -} -assert(vector.len() == 5); -assert(vector.max_len() == 10); -``` - -## Methods - -### new - -```rust -pub fn new() -> Self -``` - -Creates a new, empty vector of length zero. - -Since this container is backed by an array internally, it still needs an initial value -to give each element. To resolve this, each element is zeroed internally. This value -is guaranteed to be inaccessible unless `get_unchecked` is used. - -Example: - -```rust -let empty_vector: BoundedVec = BoundedVec::new(); -assert(empty_vector.len() == 0); -``` - -Note that whenever calling `new` the maximum length of the vector should always be specified -via a type signature: - -```rust title="new_example" showLineNumbers -fn good() -> BoundedVec { - // Ok! MaxLen is specified with a type annotation - let v1: BoundedVec = BoundedVec::new(); - let v2 = BoundedVec::new(); - - // Ok! MaxLen is known from the type of `good`'s return value - v2 -} - -fn bad() { - // Error: Type annotation needed - // The compiler can't infer `MaxLen` from this code. - let mut v3 = BoundedVec::new(); - v3.push(5); -} -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L11-L27 - - -This defaulting of `MaxLen` (and numeric generics in general) to zero may change in future noir versions -but for now make sure to use type annotations when using bounded vectors. Otherwise, you will receive a constraint failure at runtime when the vec is pushed to. - -### get - -```rust -pub fn get(self, index: u64) -> T { -``` - -Retrieves an element from the vector at the given index, starting from zero. - -If the given index is equal to or greater than the length of the vector, this -will issue a constraint failure. - -Example: - -```rust -fn foo(v: BoundedVec) { - let first = v.get(0); - let last = v.get(v.len() - 1); - assert(first != last); -} -``` - -### get_unchecked - -```rust -pub fn get_unchecked(self, index: u64) -> T { -``` - -Retrieves an element from the vector at the given index, starting from zero, without -performing a bounds check. - -Since this function does not perform a bounds check on length before accessing the element, -it is unsafe! Use at your own risk! - -Example: - -```rust title="get_unchecked_example" showLineNumbers -fn sum_of_first_three(v: BoundedVec) -> u32 { - // Always ensure the length is larger than the largest - // index passed to get_unchecked - assert(v.len() > 2); - let first = v.get_unchecked(0); - let second = v.get_unchecked(1); - let third = v.get_unchecked(2); - first + second + third -} -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L54-L64 - - -### set - -```rust -pub fn set(&mut self: Self, index: u64, value: T) { -``` - -Writes an element to the vector at the given index, starting from zero. - -If the given index is equal to or greater than the length of the vector, this will issue a constraint failure. - -Example: - -```rust -fn foo(v: BoundedVec) { - let first = v.get(0); - assert(first != 42); - v.set(0, 42); - let new_first = v.get(0); - assert(new_first == 42); -} -``` - -### set_unchecked - -```rust -pub fn set_unchecked(&mut self: Self, index: u64, value: T) -> T { -``` - -Writes an element to the vector at the given index, starting from zero, without performing a bounds check. - -Since this function does not perform a bounds check on length before accessing the element, it is unsafe! Use at your own risk! - -Example: - -```rust title="set_unchecked_example" showLineNumbers -fn set_unchecked_example() { - let mut vec: BoundedVec = BoundedVec::new(); - vec.extend_from_array([1, 2]); - - // Here we're safely writing within the valid range of `vec` - // `vec` now has the value [42, 2] - vec.set_unchecked(0, 42); - - // We can then safely read this value back out of `vec`. - // Notice that we use the checked version of `get` which would prevent reading unsafe values. - assert_eq(vec.get(0), 42); - - // We've now written past the end of `vec`. - // As this index is still within the maximum potential length of `v`, - // it won't cause a constraint failure. - vec.set_unchecked(2, 42); - println(vec); - - // This will write past the end of the maximum potential length of `vec`, - // it will then trigger a constraint failure. - vec.set_unchecked(5, 42); - println(vec); -} -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L67-L91 - - - -### push - -```rust -pub fn push(&mut self, elem: T) { -``` - -Pushes an element to the end of the vector. This increases the length -of the vector by one. - -Panics if the new length of the vector will be greater than the max length. - -Example: - -```rust title="bounded-vec-push-example" showLineNumbers -let mut v: BoundedVec = BoundedVec::new(); - - v.push(1); - v.push(2); - - // Panics with failed assertion "push out of bounds" - v.push(3); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L95-L103 - - -### pop - -```rust -pub fn pop(&mut self) -> T -``` - -Pops the element at the end of the vector. This will decrease the length -of the vector by one. - -Panics if the vector is empty. - -Example: - -```rust title="bounded-vec-pop-example" showLineNumbers -let mut v: BoundedVec = BoundedVec::new(); - v.push(1); - v.push(2); - - let two = v.pop(); - let one = v.pop(); - - assert(two == 2); - assert(one == 1); - // error: cannot pop from an empty vector - // let _ = v.pop(); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L108-L120 - - -### len - -```rust -pub fn len(self) -> u64 { -``` - -Returns the current length of this vector - -Example: - -```rust title="bounded-vec-len-example" showLineNumbers -let mut v: BoundedVec = BoundedVec::new(); - assert(v.len() == 0); - - v.push(100); - assert(v.len() == 1); - - v.push(200); - v.push(300); - v.push(400); - assert(v.len() == 4); - - let _ = v.pop(); - let _ = v.pop(); - assert(v.len() == 2); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L125-L140 - - -### max_len - -```rust -pub fn max_len(_self: BoundedVec) -> u64 { -``` - -Returns the maximum length of this vector. This is always -equal to the `MaxLen` parameter this vector was initialized with. - -Example: - -```rust title="bounded-vec-max-len-example" showLineNumbers -let mut v: BoundedVec = BoundedVec::new(); - - assert(v.max_len() == 5); - v.push(10); - assert(v.max_len() == 5); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L145-L151 - - -### storage - -```rust -pub fn storage(self) -> [T; MaxLen] { -``` - -Returns the internal array within this vector. -Since arrays in Noir are immutable, mutating the returned storage array will not mutate -the storage held internally by this vector. - -Note that uninitialized elements may be zeroed out! - -Example: - -```rust title="bounded-vec-storage-example" showLineNumbers -let mut v: BoundedVec = BoundedVec::new(); - - assert(v.storage() == [0, 0, 0, 0, 0]); - - v.push(57); - assert(v.storage() == [57, 0, 0, 0, 0]); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L156-L163 - - -### extend_from_array - -```rust -pub fn extend_from_array(&mut self, array: [T; Len]) -``` - -Pushes each element from the given array to this vector. - -Panics if pushing each element would cause the length of this vector -to exceed the maximum length. - -Example: - -```rust title="bounded-vec-extend-from-array-example" showLineNumbers -let mut vec: BoundedVec = BoundedVec::new(); - vec.extend_from_array([2, 4]); - - assert(vec.len == 2); - assert(vec.get(0) == 2); - assert(vec.get(1) == 4); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L168-L175 - - -### extend_from_bounded_vec - -```rust -pub fn extend_from_bounded_vec(&mut self, vec: BoundedVec) -``` - -Pushes each element from the other vector to this vector. The length of -the other vector is left unchanged. - -Panics if pushing each element would cause the length of this vector -to exceed the maximum length. - -Example: - -```rust title="bounded-vec-extend-from-bounded-vec-example" showLineNumbers -let mut v1: BoundedVec = BoundedVec::new(); - let mut v2: BoundedVec = BoundedVec::new(); - - v2.extend_from_array([1, 2, 3]); - v1.extend_from_bounded_vec(v2); - - assert(v1.storage() == [1, 2, 3, 0, 0]); - assert(v2.storage() == [1, 2, 3, 0, 0, 0, 0]); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L180-L189 - - -### from_array - -```rust -pub fn from_array(array: [T; Len]) -> Self -``` - -Creates a new vector, populating it with values derived from an array input. -The maximum length of the vector is determined based on the type signature. - -Example: -```rust -let bounded_vec: BoundedVec = BoundedVec::from_array([1, 2, 3]) -``` - -### map - -```rust -pub fn map(self, f: fn[Env](T) -> U) -> BoundedVec -``` - -Creates a new vector of equal size by calling a closure on each element in this vector. - -Example: - -```rust title="bounded-vec-map-example" showLineNumbers -let vec: BoundedVec = BoundedVec::from_array([1, 2, 3, 4]); - let result = vec.map(|value| value * 2); -``` -> Source code: noir_stdlib/src/collections/bounded_vec.nr#L495-L498 - - -### any - -```rust -pub fn any(self, predicate: fn[Env](T) -> bool) -> bool -``` - -Returns true if the given predicate returns true for any element -in this vector. - -Example: - -```rust title="bounded-vec-any-example" showLineNumbers -let mut v: BoundedVec = BoundedVec::new(); - v.extend_from_array([2, 4, 6]); - - let all_even = !v.any(|elem: u32| elem % 2 != 0); - assert(all_even); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L256-L262 - diff --git a/docs/versioned_docs/version-v0.38.0/noir/standard_library/containers/hashmap.md b/docs/versioned_docs/version-v0.38.0/noir/standard_library/containers/hashmap.md deleted file mode 100644 index 395cc312705..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/standard_library/containers/hashmap.md +++ /dev/null @@ -1,587 +0,0 @@ ---- -title: HashMap -keywords: [noir, map, hash, hashmap] -sidebar_position: 1 ---- - -`HashMap` is used to efficiently store and look up key-value pairs. - -`HashMap` is a bounded type which can store anywhere from zero to `MaxLen` total elements. -Note that due to hash collisions, the actual maximum number of elements stored by any particular -hashmap is likely lower than `MaxLen`. This is true even with cryptographic hash functions since -every hash value will be performed modulo `MaxLen`. - -Example: - -```rust -// Create a mapping from Fields to u32s with a maximum length of 12 -// using a poseidon2 hasher -use std::hash::poseidon2::Poseidon2Hasher; -let mut map: HashMap> = HashMap::default(); - -map.insert(1, 2); -map.insert(3, 4); - -let two = map.get(1).unwrap(); -``` - -## Methods - -### default - -```rust title="default" showLineNumbers -impl Default for HashMap -where - B: BuildHasher + Default, - H: Hasher + Default, -{ - /// Constructs an empty HashMap. - /// - /// Example: - /// - /// ```noir - /// let hashmap: HashMap> = HashMap::default(); - /// assert(hashmap.is_empty()); - /// ``` - fn default() -> Self { -``` -> Source code: noir_stdlib/src/collections/map.nr#L681-L696 - - -Creates a fresh, empty HashMap. - -When using this function, always make sure to specify the maximum size of the hash map. - -This is the same `default` from the `Default` implementation given further below. It is -repeated here for convenience since it is the recommended way to create a hashmap. - -Example: - -```rust title="default_example" showLineNumbers -let hashmap: HashMap> = HashMap::default(); - assert(hashmap.is_empty()); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L207-L210 - - -Because `HashMap` has so many generic arguments that are likely to be the same throughout -your program, it may be helpful to create a type alias: - -```rust title="type_alias" showLineNumbers -type MyMap = HashMap>; -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L201-L203 - - -### with_hasher - -```rust title="with_hasher" showLineNumbers -pub fn with_hasher(_build_hasher: B) -> Self - where - B: BuildHasher, - { -``` -> Source code: noir_stdlib/src/collections/map.nr#L103-L108 - - -Creates a hashmap with an existing `BuildHasher`. This can be used to ensure multiple -hashmaps are created with the same hasher instance. - -Example: - -```rust title="with_hasher_example" showLineNumbers -let my_hasher: BuildHasherDefault = Default::default(); - let hashmap: HashMap> = - HashMap::with_hasher(my_hasher); - assert(hashmap.is_empty()); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L211-L216 - - -### get - -```rust title="get" showLineNumbers -pub fn get(self, key: K) -> Option - where - K: Eq + Hash, - B: BuildHasher, - H: Hasher, - { -``` -> Source code: noir_stdlib/src/collections/map.nr#L465-L472 - - -Retrieves a value from the hashmap, returning `Option::none()` if it was not found. - -Example: - -```rust title="get_example" showLineNumbers -fn get_example(map: HashMap>) { - let x = map.get(12); - - if x.is_some() { - assert(x.unwrap() == 42); - } -} -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L296-L304 - - -### insert - -```rust title="insert" showLineNumbers -pub fn insert(&mut self, key: K, value: V) - where - K: Eq + Hash, - B: BuildHasher, - H: Hasher, - { -``` -> Source code: noir_stdlib/src/collections/map.nr#L507-L514 - - -Inserts a new key-value pair into the map. If the key was already in the map, its -previous value will be overridden with the newly provided one. - -Example: - -```rust title="insert_example" showLineNumbers -let mut map: HashMap> = HashMap::default(); - map.insert(12, 42); - assert(map.len() == 1); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L217-L221 - - -### remove - -```rust title="remove" showLineNumbers -pub fn remove(&mut self, key: K) - where - K: Eq + Hash, - B: BuildHasher, - H: Hasher, - { -``` -> Source code: noir_stdlib/src/collections/map.nr#L563-L570 - - -Removes the given key-value pair from the map. If the key was not already present -in the map, this does nothing. - -Example: - -```rust title="remove_example" showLineNumbers -map.remove(12); - assert(map.is_empty()); - - // If a key was not present in the map, remove does nothing - map.remove(12); - assert(map.is_empty()); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L224-L231 - - -### is_empty - -```rust title="is_empty" showLineNumbers -pub fn is_empty(self) -> bool { -``` -> Source code: noir_stdlib/src/collections/map.nr#L167-L169 - - -True if the length of the hash map is empty. - -Example: - -```rust title="is_empty_example" showLineNumbers -assert(map.is_empty()); - - map.insert(1, 2); - assert(!map.is_empty()); - - map.remove(1); - assert(map.is_empty()); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L232-L240 - - -### len - -```rust title="len" showLineNumbers -pub fn len(self) -> u32 { -``` -> Source code: noir_stdlib/src/collections/map.nr#L424-L426 - - -Returns the current length of this hash map. - -Example: - -```rust title="len_example" showLineNumbers -// This is equivalent to checking map.is_empty() - assert(map.len() == 0); - - map.insert(1, 2); - map.insert(3, 4); - map.insert(5, 6); - assert(map.len() == 3); - - // 3 was already present as a key in the hash map, so the length is unchanged - map.insert(3, 7); - assert(map.len() == 3); - - map.remove(1); - assert(map.len() == 2); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L241-L256 - - -### capacity - -```rust title="capacity" showLineNumbers -pub fn capacity(_self: Self) -> u32 { -``` -> Source code: noir_stdlib/src/collections/map.nr#L446-L448 - - -Returns the maximum capacity of this hashmap. This is always equal to the capacity -specified in the hashmap's type. - -Unlike hashmaps in general purpose programming languages, hashmaps in Noir have a -static capacity that does not increase as the map grows larger. Thus, this capacity -is also the maximum possible element count that can be inserted into the hashmap. -Due to hash collisions (modulo the hashmap length), it is likely the actual maximum -element count will be lower than the full capacity. - -Example: - -```rust title="capacity_example" showLineNumbers -let empty_map: HashMap> = - HashMap::default(); - assert(empty_map.len() == 0); - assert(empty_map.capacity() == 42); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L257-L262 - - -### clear - -```rust title="clear" showLineNumbers -pub fn clear(&mut self) { -``` -> Source code: noir_stdlib/src/collections/map.nr#L123-L125 - - -Clears the hashmap, removing all key-value pairs from it. - -Example: - -```rust title="clear_example" showLineNumbers -assert(!map.is_empty()); - map.clear(); - assert(map.is_empty()); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L263-L267 - - -### contains_key - -```rust title="contains_key" showLineNumbers -pub fn contains_key(self, key: K) -> bool - where - K: Hash + Eq, - B: BuildHasher, - H: Hasher, - { -``` -> Source code: noir_stdlib/src/collections/map.nr#L143-L150 - - -True if the hashmap contains the given key. Unlike `get`, this will not also return -the value associated with the key. - -Example: - -```rust title="contains_key_example" showLineNumbers -if map.contains_key(7) { - let value = map.get(7); - assert(value.is_some()); - } else { - println("No value for key 7!"); - } -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L268-L275 - - -### entries - -```rust title="entries" showLineNumbers -pub fn entries(self) -> BoundedVec<(K, V), N> { -``` -> Source code: noir_stdlib/src/collections/map.nr#L191-L193 - - -Returns a vector of each key-value pair present in the hashmap. - -The length of the returned vector is always equal to the length of the hashmap. - -Example: - -```rust title="entries_example" showLineNumbers -let entries = map.entries(); - - // The length of a hashmap may not be compile-time known, so we - // need to loop over its capacity instead - for i in 0..map.capacity() { - if i < entries.len() { - let (key, value) = entries.get(i); - println(f"{key} -> {value}"); - } - } -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L307-L318 - - -### keys - -```rust title="keys" showLineNumbers -pub fn keys(self) -> BoundedVec { -``` -> Source code: noir_stdlib/src/collections/map.nr#L227-L229 - - -Returns a vector of each key present in the hashmap. - -The length of the returned vector is always equal to the length of the hashmap. - -Example: - -```rust title="keys_example" showLineNumbers -let keys = map.keys(); - - for i in 0..keys.max_len() { - if i < keys.len() { - let key = keys.get_unchecked(i); - let value = map.get(key).unwrap_unchecked(); - println(f"{key} -> {value}"); - } - } -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L319-L329 - - -### values - -```rust title="values" showLineNumbers -pub fn values(self) -> BoundedVec { -``` -> Source code: noir_stdlib/src/collections/map.nr#L262-L264 - - -Returns a vector of each value present in the hashmap. - -The length of the returned vector is always equal to the length of the hashmap. - -Example: - -```rust title="values_example" showLineNumbers -let values = map.values(); - - for i in 0..values.max_len() { - if i < values.len() { - let value = values.get_unchecked(i); - println(f"Found value {value}"); - } - } -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L330-L339 - - -### iter_mut - -```rust title="iter_mut" showLineNumbers -pub fn iter_mut(&mut self, f: fn(K, V) -> (K, V)) - where - K: Eq + Hash, - B: BuildHasher, - H: Hasher, - { -``` -> Source code: noir_stdlib/src/collections/map.nr#L297-L304 - - -Iterates through each key-value pair of the HashMap, setting each key-value pair to the -result returned from the given function. - -Note that since keys can be mutated, the HashMap needs to be rebuilt as it is iterated -through. If this is not desired, use `iter_values_mut` if only values need to be mutated, -or `entries` if neither keys nor values need to be mutated. - -The iteration order is left unspecified. As a result, if two keys are mutated to become -equal, which of the two values that will be present for the key in the resulting map is also unspecified. - -Example: - -```rust title="iter_mut_example" showLineNumbers -// Add 1 to each key in the map, and double the value associated with that key. - map.iter_mut(|k, v| (k + 1, v * 2)); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L343-L346 - - -### iter_keys_mut - -```rust title="iter_keys_mut" showLineNumbers -pub fn iter_keys_mut(&mut self, f: fn(K) -> K) - where - K: Eq + Hash, - B: BuildHasher, - H: Hasher, - { -``` -> Source code: noir_stdlib/src/collections/map.nr#L335-L342 - - -Iterates through the HashMap, mutating each key to the result returned from -the given function. - -Note that since keys can be mutated, the HashMap needs to be rebuilt as it is iterated -through. If only iteration is desired and the keys are not intended to be mutated, -prefer using `entries` instead. - -The iteration order is left unspecified. As a result, if two keys are mutated to become -equal, which of the two values that will be present for the key in the resulting map is also unspecified. - -Example: - -```rust title="iter_keys_mut_example" showLineNumbers -// Double each key, leaving the value associated with that key untouched - map.iter_keys_mut(|k| k * 2); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L347-L350 - - -### iter_values_mut - -```rust title="iter_values_mut" showLineNumbers -pub fn iter_values_mut(&mut self, f: fn(V) -> V) { -``` -> Source code: noir_stdlib/src/collections/map.nr#L367-L369 - - -Iterates through the HashMap, applying the given function to each value and mutating the -value to equal the result. This function is more efficient than `iter_mut` and `iter_keys_mut` -because the keys are untouched and the underlying hashmap thus does not need to be reordered. - -Example: - -```rust title="iter_values_mut_example" showLineNumbers -// Halve each value - map.iter_values_mut(|v| v / 2); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L351-L354 - - -### retain - -```rust title="retain" showLineNumbers -pub fn retain(&mut self, f: fn(K, V) -> bool) { -``` -> Source code: noir_stdlib/src/collections/map.nr#L388-L390 - - -Retains only the key-value pairs for which the given function returns true. -Any key-value pairs for which the function returns false will be removed from the map. - -Example: - -```rust title="retain_example" showLineNumbers -map.retain(|k, v| (k != 0) & (v != 0)); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L279-L281 - - -## Trait Implementations - -### default - -```rust title="default" showLineNumbers -impl Default for HashMap -where - B: BuildHasher + Default, - H: Hasher + Default, -{ - /// Constructs an empty HashMap. - /// - /// Example: - /// - /// ```noir - /// let hashmap: HashMap> = HashMap::default(); - /// assert(hashmap.is_empty()); - /// ``` - fn default() -> Self { -``` -> Source code: noir_stdlib/src/collections/map.nr#L681-L696 - - -Constructs an empty HashMap. - -Example: - -```rust title="default_example" showLineNumbers -let hashmap: HashMap> = HashMap::default(); - assert(hashmap.is_empty()); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L207-L210 - - -### eq - -```rust title="eq" showLineNumbers -impl Eq for HashMap -where - K: Eq + Hash, - V: Eq, - B: BuildHasher, - H: Hasher, -{ - /// Checks if two HashMaps are equal. - /// - /// Example: - /// - /// ```noir - /// let mut map1: HashMap> = HashMap::default(); - /// let mut map2: HashMap> = HashMap::default(); - /// - /// map1.insert(1, 2); - /// map1.insert(3, 4); - /// - /// map2.insert(3, 4); - /// map2.insert(1, 2); - /// - /// assert(map1 == map2); - /// ``` - fn eq(self, other: HashMap) -> bool { -``` -> Source code: noir_stdlib/src/collections/map.nr#L629-L654 - - -Checks if two HashMaps are equal. - -Example: - -```rust title="eq_example" showLineNumbers -let mut map1: HashMap> = HashMap::default(); - let mut map2: HashMap> = HashMap::default(); - - map1.insert(1, 2); - map1.insert(3, 4); - - map2.insert(3, 4); - map2.insert(1, 2); - - assert(map1 == map2); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L282-L293 - diff --git a/docs/versioned_docs/version-v0.38.0/noir/standard_library/containers/index.md b/docs/versioned_docs/version-v0.38.0/noir/standard_library/containers/index.md deleted file mode 100644 index ea84c6d5c21..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/standard_library/containers/index.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Containers -description: Container types provided by Noir's standard library for storing and retrieving data -keywords: [containers, data types, vec, hashmap] ---- diff --git a/docs/versioned_docs/version-v0.38.0/noir/standard_library/containers/vec.mdx b/docs/versioned_docs/version-v0.38.0/noir/standard_library/containers/vec.mdx deleted file mode 100644 index 475011922f8..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/standard_library/containers/vec.mdx +++ /dev/null @@ -1,170 +0,0 @@ ---- -title: Vectors -description: Delve into the Vec 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] -sidebar_position: 6 ---- - -import Experimental from '@site/src/components/Notes/_experimental.mdx'; - - - -A vector is a collection type similar to Rust's `Vec` type. In Noir, it is a convenient way to use slices as mutable arrays. - -Example: - -```rust -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 -``` - -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 -``` - -Example: - -```rust -let slice: [Field] = &[1, 2, 3]; -let vector_from_slice = Vec::from_slice(slice); -assert(vector_from_slice.len() == 3); -``` - -### len - -Returns the number of elements in the vector. - -```rust -pub fn len(self) -> Field -``` - -Example: - -```rust -let empty_vector: Vec = Vec::new(); -assert(empty_vector.len() == 0); -``` - -### 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 -``` - -Example: - -```rust -let vector: Vec = Vec::from_slice(&[10, 20, 30]); -assert(vector.get(1) == 20); -``` - -### set - -```rust -pub fn set(&mut self: Self, index: u64, value: T) { -``` - -Writes an element to the vector at the given index, starting from zero. - -Panics if the index points beyond the vector's end. - -Example: - -```rust -let vector: Vec = Vec::from_slice(&[10, 20, 30]); -assert(vector.get(1) == 20); -vector.set(1, 42); -assert(vector.get(1) == 42); -``` - -### 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) -``` - -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 -``` - -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) -``` - -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 -``` - -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); -``` diff --git a/docs/versioned_docs/version-v0.38.0/noir/standard_library/cryptographic_primitives/_category_.json b/docs/versioned_docs/version-v0.38.0/noir/standard_library/cryptographic_primitives/_category_.json deleted file mode 100644 index 5d694210bbf..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/standard_library/cryptographic_primitives/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "position": 0, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.38.0/noir/standard_library/cryptographic_primitives/ciphers.mdx b/docs/versioned_docs/version-v0.38.0/noir/standard_library/cryptographic_primitives/ciphers.mdx deleted file mode 100644 index d6a5e1a79eb..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/standard_library/cryptographic_primitives/ciphers.mdx +++ /dev/null @@ -1,32 +0,0 @@ ---- -title: Ciphers -description: - Learn about the implemented ciphers ready to use for any Noir project -keywords: - [ciphers, Noir project, aes128, encrypt] -sidebar_position: 0 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; - -## aes128 - -Given a plaintext as an array of bytes, returns the corresponding aes128 ciphertext (CBC mode). Input padding is automatically performed using PKCS#7, so that the output length is `input.len() + (16 - input.len() % 16)`. - -```rust title="aes128" showLineNumbers -pub fn aes128_encrypt(input: [u8; N], iv: [u8; 16], key: [u8; 16]) -> [u8] {} -``` -> Source code: noir_stdlib/src/aes128.nr#L2-L4 - - -```rust -fn main() { - let input: [u8; 4] = [0, 12, 3, 15] // Random bytes, will be padded to 16 bytes. - let iv: [u8; 16] = [0; 16]; // Initialisation vector - let key: [u8; 16] = [0; 16] // AES key - let ciphertext = std::aes128::aes128_encrypt(inputs.as_bytes(), iv.as_bytes(), key.as_bytes()); // In this case, the output length will be 16 bytes. -} -``` - - - \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.38.0/noir/standard_library/cryptographic_primitives/ec_primitives.md b/docs/versioned_docs/version-v0.38.0/noir/standard_library/cryptographic_primitives/ec_primitives.md deleted file mode 100644 index f262d8160d6..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/standard_library/cryptographic_primitives/ec_primitives.md +++ /dev/null @@ -1,102 +0,0 @@ ---- -title: Elliptic Curve Primitives -keywords: [cryptographic primitives, Noir project] -sidebar_position: 4 ---- - -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/mod.nr`](https://github.com/noir-lang/noir/blob/master/noir_stdlib/src/ec/mod.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/mod.nr)). - -## Examples - -The -[ec_baby_jubjub test](https://github.com/noir-lang/noir/blob/master/test_programs/compile_success_empty/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 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.38.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx b/docs/versioned_docs/version-v0.38.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx deleted file mode 100644 index 8d96027b42c..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx +++ /dev/null @@ -1,98 +0,0 @@ ---- -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] -sidebar_position: 3 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; - -Noir supports ECDSA signatures verification over the secp256k1 and secp256r1 curves. - -## ecdsa_secp256k1::verify_signature - -Verifier for ECDSA Secp256k1 signatures. -See ecdsa_secp256k1::verify_signature_slice for a version that accepts slices directly. - -```rust title="ecdsa_secp256k1" showLineNumbers -pub fn verify_signature( - public_key_x: [u8; 32], - public_key_y: [u8; 32], - signature: [u8; 64], - message_hash: [u8; N], -) -> bool -``` -> Source code: noir_stdlib/src/ecdsa_secp256k1.nr#L2-L9 - - -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_secp256k1::verify_signature_slice - -Verifier for ECDSA Secp256k1 signatures where the message is a slice. - -```rust title="ecdsa_secp256k1_slice" showLineNumbers -pub fn verify_signature_slice( - public_key_x: [u8; 32], - public_key_y: [u8; 32], - signature: [u8; 64], - message_hash: [u8], -) -> bool -``` -> Source code: noir_stdlib/src/ecdsa_secp256k1.nr#L13-L20 - - - - -## ecdsa_secp256r1::verify_signature - -Verifier for ECDSA Secp256r1 signatures. -See ecdsa_secp256r1::verify_signature_slice for a version that accepts slices directly. - -```rust title="ecdsa_secp256r1" showLineNumbers -pub fn verify_signature( - public_key_x: [u8; 32], - public_key_y: [u8; 32], - signature: [u8; 64], - message_hash: [u8; N], -) -> bool -``` -> Source code: noir_stdlib/src/ecdsa_secp256r1.nr#L2-L9 - - -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); -} -``` - - - -## ecdsa_secp256r1::verify_signature - -Verifier for ECDSA Secp256r1 signatures where the message is a slice. - -```rust title="ecdsa_secp256r1_slice" showLineNumbers -pub fn verify_signature_slice( - public_key_x: [u8; 32], - public_key_y: [u8; 32], - signature: [u8; 64], - message_hash: [u8], -) -> bool -``` -> Source code: noir_stdlib/src/ecdsa_secp256r1.nr#L13-L20 - - - diff --git a/docs/versioned_docs/version-v0.38.0/noir/standard_library/cryptographic_primitives/eddsa.mdx b/docs/versioned_docs/version-v0.38.0/noir/standard_library/cryptographic_primitives/eddsa.mdx deleted file mode 100644 index b283de693c8..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/standard_library/cryptographic_primitives/eddsa.mdx +++ /dev/null @@ -1,37 +0,0 @@ ---- -title: EdDSA Verification -description: Learn about the cryptographic primitives regarding EdDSA -keywords: [cryptographic primitives, Noir project, eddsa, signatures] -sidebar_position: 5 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; - -## 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 -``` - -It is also possible to specify the hash algorithm used for the signature by using the `eddsa_verify` function by passing a type implementing the Hasher trait with the turbofish operator. -For instance, if you want to use Poseidon2 instead, you can do the following: -```rust -use std::hash::poseidon2::Poseidon2Hasher; - -eddsa_verify::(pub_key_a.x, pub_key_a.y, s_a, r8_a.x, r8_a.y, msg); -``` - - - -## eddsa::eddsa_to_pub - -Private to public key conversion. - -Returns `(pub_key_x, pub_key_y)` - -```rust -fn eddsa_to_pub(secret : Field) -> (Field, Field) -``` - diff --git a/docs/versioned_docs/version-v0.38.0/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx b/docs/versioned_docs/version-v0.38.0/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx deleted file mode 100644 index 482a36932b9..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx +++ /dev/null @@ -1,95 +0,0 @@ ---- -title: Scalar multiplication -description: See how you can perform scalar multiplication in Noir -keywords: [cryptographic primitives, Noir project, scalar multiplication] -sidebar_position: 1 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; - -The following functions perform operations over the embedded curve whose coordinates are defined by the configured noir field. -For the BN254 scalar field, this is BabyJubJub or Grumpkin. - -:::note -Suffixes `_low` and `_high` denote low and high limbs of a scalar. -::: - -## embedded_curve_ops::multi_scalar_mul - -Performs multi scalar multiplication over the embedded curve. -The function accepts arbitrary amount of point-scalar pairs on the input, it multiplies the individual pairs over -the curve and returns a sum of the resulting points. - -Points represented as x and y coordinates [x1, y1, x2, y2, ...], scalars as low and high limbs [low1, high1, low2, high2, ...]. - -```rust title="multi_scalar_mul" showLineNumbers -pub fn multi_scalar_mul( - points: [EmbeddedCurvePoint; N], - scalars: [EmbeddedCurveScalar; N], -) -> EmbeddedCurvePoint -``` -> Source code: noir_stdlib/src/embedded_curve_ops.nr#L103-L108 - - -example - -```rust -fn main(point_x: Field, point_y: Field, scalar_low: Field, scalar_high: Field) { - let point = std::embedded_curve_ops::multi_scalar_mul([point_x, point_y], [scalar_low, scalar_high]); - println(point); -} -``` - -## embedded_curve_ops::fixed_base_scalar_mul - -Performs fixed base scalar multiplication over the embedded curve (multiplies input scalar with a generator point). -The function accepts a single scalar on the input represented as 2 fields. - -```rust title="fixed_base_scalar_mul" showLineNumbers -pub fn fixed_base_scalar_mul(scalar: EmbeddedCurveScalar) -> EmbeddedCurvePoint -``` -> Source code: noir_stdlib/src/embedded_curve_ops.nr#L120-L122 - - -example - -```rust -fn main(scalar_low: Field, scalar_high: Field) { - let point = std::embedded_curve_ops::fixed_base_scalar_mul(scalar_low, scalar_high); - println(point); -} -``` - -## embedded_curve_ops::embedded_curve_add - -Adds two points on the embedded curve. -This function takes two `EmbeddedCurvePoint` structures as parameters, representing points on the curve, and returns a new `EmbeddedCurvePoint` structure that represents their sum. - -### Parameters: -- `point1` (`EmbeddedCurvePoint`): The first point to add. -- `point2` (`EmbeddedCurvePoint`): The second point to add. - -### Returns: -- `EmbeddedCurvePoint`: The resulting point after the addition of `point1` and `point2`. - -```rust title="embedded_curve_add" showLineNumbers -pub fn embedded_curve_add( - point1: EmbeddedCurvePoint, - point2: EmbeddedCurvePoint, -) -> EmbeddedCurvePoint { -``` -> Source code: noir_stdlib/src/embedded_curve_ops.nr#L136-L141 - - -example - -```rust -fn main() { - let point1 = EmbeddedCurvePoint { x: 1, y: 2 }; - let point2 = EmbeddedCurvePoint { x: 3, y: 4 }; - let result = std::embedded_curve_ops::embedded_curve_add(point1, point2); - println!("Resulting Point: ({}, {})", result.x, result.y); -} -``` - - diff --git a/docs/versioned_docs/version-v0.38.0/noir/standard_library/cryptographic_primitives/hashes.mdx b/docs/versioned_docs/version-v0.38.0/noir/standard_library/cryptographic_primitives/hashes.mdx deleted file mode 100644 index 541a1971561..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/standard_library/cryptographic_primitives/hashes.mdx +++ /dev/null @@ -1,227 +0,0 @@ ---- -title: Hash methods -description: - Learn about the cryptographic primitives ready to use for any Noir project, including sha256, - blake2s and pedersen -keywords: - [cryptographic primitives, Noir project, sha256, blake2s, pedersen, hash] -sidebar_position: 0 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; - -## sha256 - -Given an array of bytes, returns the resulting sha256 hash. -Specify a message_size to hash only the first `message_size` bytes of the input. - -```rust title="sha256" showLineNumbers -pub fn sha256(input: [u8; N]) -> HASH -``` -> Source code: noir_stdlib/src/hash/sha256.nr#L47-L49 - - -example: -```rust title="sha256_var" showLineNumbers -let digest = std::hash::sha256_var([x as u8], 1); -``` -> Source code: test_programs/execution_success/sha256/src/main.nr#L15-L17 - - -```rust -fn main() { - let x = [163, 117, 178, 149]; // some random bytes - let hash = std::sha256::sha256_var(x, 4); -} -``` - - - - -## blake2s - -Given an array of bytes, returns an array with the Blake2 hash - -```rust title="blake2s" showLineNumbers -pub fn blake2s(input: [u8; N]) -> [u8; 32] -``` -> Source code: noir_stdlib/src/hash/mod.nr#L18-L20 - - -example: - -```rust -fn main() { - let x = [163, 117, 178, 149]; // some random bytes - let hash = std::hash::blake2s(x); -} -``` - - - -## blake3 - -Given an array of bytes, returns an array with the Blake3 hash - -```rust title="blake3" showLineNumbers -pub fn blake3(input: [u8; N]) -> [u8; 32] -``` -> Source code: noir_stdlib/src/hash/mod.nr#L24-L26 - - -example: - -```rust -fn main() { - let x = [163, 117, 178, 149]; // some random bytes - let hash = std::hash::blake3(x); -} -``` - - - -## pedersen_hash - -Given an array of Fields, returns the Pedersen hash. - -```rust title="pedersen_hash" showLineNumbers -pub fn pedersen_hash(input: [Field; N]) -> Field -``` -> Source code: noir_stdlib/src/hash/mod.nr#L49-L51 - - -example: - -```rust title="pedersen-hash" showLineNumbers -fn main(x: Field, y: Field, expected_hash: Field) { - let hash = std::hash::pedersen_hash([x, y]); - assert_eq(hash, expected_hash); -} -``` -> Source code: test_programs/execution_success/pedersen_hash/src/main.nr#L1-L6 - - - - -## pedersen_commitment - -Given an array of Fields, returns the Pedersen commitment. - -```rust title="pedersen_commitment" showLineNumbers -pub fn pedersen_commitment(input: [Field; N]) -> EmbeddedCurvePoint { -``` -> Source code: noir_stdlib/src/hash/mod.nr#L29-L31 - - -example: - -```rust title="pedersen-commitment" showLineNumbers -fn main(x: Field, y: Field, expected_commitment: std::embedded_curve_ops::EmbeddedCurvePoint) { - let commitment = std::hash::pedersen_commitment([x, y]); - assert_eq(commitment.x, expected_commitment.x); - assert_eq(commitment.y, expected_commitment.y); -} -``` -> Source code: test_programs/execution_success/pedersen_commitment/src/main.nr#L1-L7 - - - - -## 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 title="keccak256" showLineNumbers -pub fn keccak256(input: [u8; N], message_size: u32) -> [u8; 32] -``` -> Source code: noir_stdlib/src/hash/mod.nr#L116-L118 - - -example: - -```rust title="keccak256" showLineNumbers -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 message 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); -} -``` -> Source code: test_programs/execution_success/keccak256/src/main.nr#L1-L20 - - - - -## 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 title="poseidon" showLineNumbers -use std::hash::poseidon; - -fn main(x1: [Field; 2], y1: pub Field, x2: [Field; 4], y2: pub Field) { - let hash1 = poseidon::bn254::hash_2(x1); - assert(hash1 == y1); - - let hash2 = poseidon::bn254::hash_4(x2); - assert(hash2 == y2); -} -``` -> Source code: test_programs/execution_success/poseidon_bn254_hash/src/main.nr#L1-L11 - - -## poseidon 2 - -Given an array of Fields, returns a new Field with the Poseidon2 Hash. Contrary to the Poseidon -function, there is only one hash and you can specify a message_size to hash only the first -`message_size` bytes of the input, - -```rust -// example for hashing the first three elements of the input -Poseidon2::hash(input, 3); -``` - -example: - -```rust title="poseidon2" showLineNumbers -use std::hash::poseidon2; - -fn main(inputs: [Field; 4], expected_hash: Field) { - let hash = poseidon2::Poseidon2::hash(inputs, inputs.len()); - assert_eq(hash, expected_hash); -} -``` -> Source code: test_programs/execution_success/poseidon2/src/main.nr#L1-L8 - - -## hash_to_field - -```rust -fn hash_to_field(_input : [Field]) -> 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.38.0/noir/standard_library/cryptographic_primitives/index.md b/docs/versioned_docs/version-v0.38.0/noir/standard_library/cryptographic_primitives/index.md deleted file mode 100644 index 650f30165d5..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/standard_library/cryptographic_primitives/index.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -title: Cryptographic Primitives -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.38.0/noir/standard_library/cryptographic_primitives/schnorr.mdx b/docs/versioned_docs/version-v0.38.0/noir/standard_library/cryptographic_primitives/schnorr.mdx deleted file mode 100644 index 030452645c5..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/standard_library/cryptographic_primitives/schnorr.mdx +++ /dev/null @@ -1,64 +0,0 @@ ---- -title: Schnorr Signatures -description: Learn how you can verify Schnorr signatures using Noir -keywords: [cryptographic primitives, Noir project, schnorr, signatures] -sidebar_position: 2 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; - -## schnorr::verify_signature - -Verifier for Schnorr signatures over the embedded curve (for BN254 it is Grumpkin). -See schnorr::verify_signature_slice for a version that works directly on slices. - -```rust title="schnorr_verify" showLineNumbers -pub fn verify_signature( - public_key_x: Field, - public_key_y: Field, - signature: [u8; 64], - message: [u8; N], -) -> bool -``` -> Source code: noir_stdlib/src/schnorr.nr#L4-L11 - - -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() -); - -... -``` - - - -## schnorr::verify_signature_slice - -Verifier for Schnorr signatures over the embedded curve (for BN254 it is Grumpkin) -where the message is a slice. - -```rust title="schnorr_verify_slice" showLineNumbers -pub fn verify_signature_slice( - public_key_x: Field, - public_key_y: Field, - signature: [u8; 64], - message: [u8], -) -> bool -``` -> Source code: noir_stdlib/src/schnorr.nr#L15-L22 - - - diff --git a/docs/versioned_docs/version-v0.38.0/noir/standard_library/fmtstr.md b/docs/versioned_docs/version-v0.38.0/noir/standard_library/fmtstr.md deleted file mode 100644 index 19809d60261..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/standard_library/fmtstr.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -title: fmtstr ---- - -`fmtstr` is the type resulting from using format string (`f"..."`). - -## Methods - -### quoted_contents - -```rust title="quoted_contents" showLineNumbers -pub comptime fn quoted_contents(self) -> Quoted {} -``` -> Source code: noir_stdlib/src/meta/format_string.nr#L3-L5 - - -Returns the format string contents (that is, without the leading and trailing double quotes) as a `Quoted` value. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.38.0/noir/standard_library/is_unconstrained.md b/docs/versioned_docs/version-v0.38.0/noir/standard_library/is_unconstrained.md deleted file mode 100644 index 51bb1bda8f1..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/standard_library/is_unconstrained.md +++ /dev/null @@ -1,69 +0,0 @@ ---- -title: Is Unconstrained Function -description: - The is_unconstrained function returns wether the context at that point of the program is unconstrained or not. -keywords: - [ - unconstrained - ] ---- - -It's very common for functions in circuits to take unconstrained hints of an expensive computation and then verify it. This is done by running the hint in an unconstrained context and then verifying the result in a constrained context. - -When a function is marked as unconstrained, any subsequent functions that it calls will also be run in an unconstrained context. However, if we are implementing a library function, other users might call it within an unconstrained context or a constrained one. Generally, in an unconstrained context we prefer just computing the result instead of taking a hint of it and verifying it, since that'd mean doing the same computation twice: - -```rust - -fn my_expensive_computation(){ - ... -} - -unconstrained fn my_expensive_computation_hint(){ - my_expensive_computation() -} - -pub fn external_interface(){ - my_expensive_computation_hint(); - // verify my_expensive_computation: If external_interface is called from unconstrained, this is redundant - ... -} - -``` - -In order to improve the performance in an unconstrained context you can use the function at `std::runtime::is_unconstrained() -> bool`: - - -```rust -use dep::std::runtime::is_unconstrained; - -fn my_expensive_computation(){ - ... -} - -unconstrained fn my_expensive_computation_hint(){ - my_expensive_computation() -} - -pub fn external_interface(){ - if is_unconstrained() { - my_expensive_computation(); - } else { - my_expensive_computation_hint(); - // verify my_expensive_computation - ... - } -} - -``` - -The is_unconstrained result is resolved at compile time, so in unconstrained contexts the compiler removes the else branch, and in constrained contexts the compiler removes the if branch, reducing the amount of compute necessary to run external_interface. - -Note that using `is_unconstrained` in a `comptime` context will also return `true`: - -``` -fn main() { - comptime { - assert(is_unconstrained()); - } -} -``` diff --git a/docs/versioned_docs/version-v0.38.0/noir/standard_library/logging.md b/docs/versioned_docs/version-v0.38.0/noir/standard_library/logging.md deleted file mode 100644 index db75ef9f86f..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/standard_library/logging.md +++ /dev/null @@ -1,78 +0,0 @@ ---- -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, - print statement, - debugging in noir, - noir std library, - logging tutorial, - basic logging in noir, - noir logging implementation, - noir debugging techniques, - rust, - ] ---- - -The standard library provides two familiar statements you can use: `println` and `print`. Despite being a limited implementation of rust's `println!` and `print!` macros, these constructs can be useful for debugging. - -You can print the output of both statements in your Noir code by using the `nargo execute` command or the `--show-output` flag when using `nargo test` (provided there are print statements in your tests). - -It is recommended to use `nargo execute` if you want to debug failing constraints with `println` or `print` 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). Neither `println`, nor `print` are callable for failed constraints caught at compile time. - -Both `print` and `println` are generic functions which can work on integers, fields, strings, and even structs or expressions. Note however, that slices are currently unsupported. For example: - -```rust -struct Person { - age: Field, - height: Field, -} - -fn main(age: Field, height: Field) { - let person = Person { - age: age, - height: height, - }; - println(person); - println(age + height); - println("Hello world!"); -} -``` - -You can print different types in the same statement (including strings) with a type called `fmtstr`. It can be specified in the same way as a normal string, just prepended with an "f" character: - -```rust - let fmt_str = f"i: {i}, j: {j}"; - println(fmt_str); - - let s = myStruct { y: x, x: y }; - println(s); - - println(f"i: {i}, s: {s}"); - - println(x); - println([x, y]); - - let foo = fooStruct { my_struct: s, foo: 15 }; - println(f"s: {s}, foo: {foo}"); - - println(15); // prints 0x0f, implicit Field - println(-1 as u8); // prints 255 - println(-1 as i8); // prints -1 -``` - -Examples shown above are interchangeable between the two `print` statements: - -```rust -let person = Person { age : age, height : height }; - -println(person); -print(person); - -println("Hello world!"); // Prints with a newline at the end of the input -print("Hello world!"); // Prints the input and keeps cursor on the same line -``` diff --git a/docs/versioned_docs/version-v0.38.0/noir/standard_library/mem.md b/docs/versioned_docs/version-v0.38.0/noir/standard_library/mem.md deleted file mode 100644 index 95d36ac2a72..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/standard_library/mem.md +++ /dev/null @@ -1,52 +0,0 @@ ---- -title: Memory Module -description: - This module contains functions which manipulate memory in a low-level way -keywords: - [ - mem, memory, zeroed, transmute, checked_transmute - ] ---- - -# `std::mem::zeroed` - -```rust -fn zeroed() -> T -``` - -Returns 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. - -This function currently supports the following types: - -- Field -- Bool -- Uint -- Array -- Slice -- String -- Tuple -- Functions - -Using it on other types could result in unexpected behavior. - -# `std::mem::checked_transmute` - -```rust -fn checked_transmute(value: T) -> U -``` - -Transmutes a value of one type into the same value but with a new type `U`. - -This function is safe to use since both types are asserted to be equal later during compilation after the concrete values for generic types become known. -This function is useful for cases where the compiler may fails a type check that is expected to pass where -a user knows the two types to be equal. For example, when using arithmetic generics there are cases the compiler -does not see as equal, such as `[Field; N*(A + B)]` and `[Field; N*A + N*B]`, which users may know to be equal. -In these cases, `checked_transmute` can be used to cast the value to the desired type while also preserving safety -by checking this equality once `N`, `A`, `B` are fully resolved. - -Note that since this safety check is performed after type checking rather than during, no error is issued if the function -containing `checked_transmute` is never called. diff --git a/docs/versioned_docs/version-v0.38.0/noir/standard_library/merkle_trees.md b/docs/versioned_docs/version-v0.38.0/noir/standard_library/merkle_trees.md deleted file mode 100644 index 6a9ebf72ada..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/standard_library/merkle_trees.md +++ /dev/null @@ -1,58 +0,0 @@ ---- -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/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.as_slice()); - 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.38.0/noir/standard_library/meta/ctstring.md b/docs/versioned_docs/version-v0.38.0/noir/standard_library/meta/ctstring.md deleted file mode 100644 index b76f873ca03..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/standard_library/meta/ctstring.md +++ /dev/null @@ -1,100 +0,0 @@ ---- -title: CtString ---- - -`std::meta::ctstring` contains methods on the built-in `CtString` type which is -a compile-time, dynamically-sized string type. Compared to `str` and `fmtstr`, -`CtString` is useful because its size does not need to be specified in its type. This -can be used for formatting items at compile-time or general string handling in `comptime` -code. - -Since `fmtstr`s can be converted into `CtString`s, you can make use of their formatting -abilities in CtStrings by formatting in `fmtstr`s then converting the result to a CtString -afterward. - -## Traits - -### AsCtString - -```rust title="as-ctstring" showLineNumbers -pub trait AsCtString { - comptime fn as_ctstring(self) -> CtString; -} -``` -> Source code: noir_stdlib/src/meta/ctstring.nr#L43-L47 - - -Converts an object into a compile-time string. - -Implementations: - -```rust -impl AsCtString for str { ... } -impl AsCtString for fmtstr { ... } -``` - -## Methods - -### new - -```rust title="new" showLineNumbers -pub comptime fn new() -> Self { -``` -> Source code: noir_stdlib/src/meta/ctstring.nr#L4-L6 - - -Creates an empty `CtString`. - -### append_str - -```rust title="append_str" showLineNumbers -pub comptime fn append_str(self, s: str) -> Self { -``` -> Source code: noir_stdlib/src/meta/ctstring.nr#L11-L13 - - -Returns a new CtString with the given str appended onto the end. - -### append_fmtstr - -```rust title="append_fmtstr" showLineNumbers -pub comptime fn append_fmtstr(self, s: fmtstr) -> Self { -``` -> Source code: noir_stdlib/src/meta/ctstring.nr#L17-L19 - - -Returns a new CtString with the given fmtstr appended onto the end. - -### as_quoted_str - -```rust title="as_quoted_str" showLineNumbers -pub comptime fn as_quoted_str(self) -> Quoted { -``` -> Source code: noir_stdlib/src/meta/ctstring.nr#L26-L28 - - -Returns a quoted string literal from this string's contents. - -There is no direct conversion from a `CtString` to a `str` since -the size would not be known. To get around this, this function can -be used in combination with macro insertion (`!`) to insert this string -literal at this function's call site. - -Example: - -```rust title="as_quoted_str_example" showLineNumbers -let my_ctstring = "foo bar".as_ctstring(); - let my_str = my_ctstring.as_quoted_str!(); - - assert_eq(crate::meta::type_of(my_str), quote { str<7> }.as_type()); -``` -> Source code: noir_stdlib/src/meta/ctstring.nr#L92-L97 - - -## Trait Implementations - -```rust -impl Eq for CtString -impl Hash for CtString -impl Append for CtString -``` diff --git a/docs/versioned_docs/version-v0.38.0/noir/standard_library/meta/expr.md b/docs/versioned_docs/version-v0.38.0/noir/standard_library/meta/expr.md deleted file mode 100644 index b6d395c6700..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/standard_library/meta/expr.md +++ /dev/null @@ -1,380 +0,0 @@ ---- -title: Expr ---- - -`std::meta::expr` contains methods on the built-in `Expr` type for quoted, syntactically valid expressions. - -## Methods - -### as_array - -```rust title="as_array" showLineNumbers -pub comptime fn as_array(self) -> Option<[Expr]> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L10-L12 - - -If this expression is an array, this returns a slice of each element in the array. - -### as_assert - -```rust title="as_assert" showLineNumbers -pub comptime fn as_assert(self) -> Option<(Expr, Option)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L16-L18 - - -If this expression is an assert, this returns the assert expression and the optional message. - -### as_assert_eq - -```rust title="as_assert_eq" showLineNumbers -pub comptime fn as_assert_eq(self) -> Option<(Expr, Expr, Option)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L23-L25 - - -If this expression is an assert_eq, this returns the left-hand-side and right-hand-side -expressions, together with the optional message. - -### as_assign - -```rust title="as_assign" showLineNumbers -pub comptime fn as_assign(self) -> Option<(Expr, Expr)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L30-L32 - - -If this expression is an assignment, this returns a tuple with the left hand side -and right hand side in order. - -### as_binary_op - -```rust title="as_binary_op" showLineNumbers -pub comptime fn as_binary_op(self) -> Option<(Expr, BinaryOp, Expr)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L37-L39 - - -If this expression is a binary operator operation ` `, -return the left-hand side, operator, and the right-hand side of the operation. - -### as_block - -```rust title="as_block" showLineNumbers -pub comptime fn as_block(self) -> Option<[Expr]> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L44-L46 - - -If this expression is a block `{ stmt1; stmt2; ...; stmtN }`, return -a slice containing each statement. - -### as_bool - -```rust title="as_bool" showLineNumbers -pub comptime fn as_bool(self) -> Option {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L50-L52 - - -If this expression is a boolean literal, return that literal. - -### as_cast - -```rust title="as_cast" showLineNumbers -#[builtin(expr_as_cast)] - pub comptime fn as_cast(self) -> Option<(Expr, UnresolvedType)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L56-L59 - - -If this expression is a cast expression (`expr as type`), returns the casted -expression and the type to cast to. - -### as_comptime - -```rust title="as_comptime" showLineNumbers -pub comptime fn as_comptime(self) -> Option<[Expr]> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L64-L66 - - -If this expression is a `comptime { stmt1; stmt2; ...; stmtN }` block, -return each statement in the block. - -### as_constructor - -```rust title="as_constructor" showLineNumbers -pub comptime fn as_constructor(self) -> Option<(UnresolvedType, [(Quoted, Expr)])> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L71-L73 - - -If this expression is a constructor `Type { field1: expr1, ..., fieldN: exprN }`, -return the type and the fields. - -### as_for - -```rust title="as_for" showLineNumbers -pub comptime fn as_for(self) -> Option<(Quoted, Expr, Expr)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L78-L80 - - -If this expression is a for statement over a single expression, return the identifier, -the expression and the for loop body. - -### as_for_range - -```rust title="as_for" showLineNumbers -pub comptime fn as_for(self) -> Option<(Quoted, Expr, Expr)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L78-L80 - - -If this expression is a for statement over a range, return the identifier, -the range start, the range end and the for loop body. - -### as_function_call - -```rust title="as_function_call" showLineNumbers -pub comptime fn as_function_call(self) -> Option<(Expr, [Expr])> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L92-L94 - - -If this expression is a function call `foo(arg1, ..., argN)`, return -the function and a slice of each argument. - -### as_if - -```rust title="as_if" showLineNumbers -pub comptime fn as_if(self) -> Option<(Expr, Expr, Option)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L100-L102 - - -If this expression is an `if condition { then_branch } else { else_branch }`, -return the condition, then branch, and else branch. If there is no else branch, -`None` is returned for that branch instead. - -### as_index - -```rust title="as_index" showLineNumbers -pub comptime fn as_index(self) -> Option<(Expr, Expr)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L107-L109 - - -If this expression is an index into an array `array[index]`, return the -array and the index. - -### as_integer - -```rust title="as_integer" showLineNumbers -pub comptime fn as_integer(self) -> Option<(Field, bool)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L114-L116 - - -If this expression is an integer literal, return the integer as a field -as well as whether the integer is negative (true) or not (false). - -### as_lambda - -```rust title="as_lambda" showLineNumbers -pub comptime fn as_lambda( - self, - ) -> Option<([(Expr, Option)], Option, Expr)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L120-L124 - - -If this expression is a lambda, returns the parameters, return type and body. - -### as_let - -```rust title="as_let" showLineNumbers -pub comptime fn as_let(self) -> Option<(Expr, Option, Expr)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L129-L131 - - -If this expression is a let statement, returns the let pattern as an `Expr`, -the optional type annotation, and the assigned expression. - -### as_member_access - -```rust title="as_member_access" showLineNumbers -pub comptime fn as_member_access(self) -> Option<(Expr, Quoted)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L136-L138 - - -If this expression is a member access `foo.bar`, return the struct/tuple -expression and the field. The field will be represented as a quoted value. - -### as_method_call - -```rust title="as_method_call" showLineNumbers -pub comptime fn as_method_call(self) -> Option<(Expr, Quoted, [UnresolvedType], [Expr])> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L143-L145 - - -If this expression is a method call `foo.bar::(arg1, ..., argN)`, return -the receiver, method name, a slice of each generic argument, and a slice of each argument. - -### as_repeated_element_array - -```rust title="as_repeated_element_array" showLineNumbers -pub comptime fn as_repeated_element_array(self) -> Option<(Expr, Expr)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L150-L152 - - -If this expression is a repeated element array `[elem; length]`, return -the repeated element and the length expressions. - -### as_repeated_element_slice - -```rust title="as_repeated_element_slice" showLineNumbers -pub comptime fn as_repeated_element_slice(self) -> Option<(Expr, Expr)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L157-L159 - - -If this expression is a repeated element slice `[elem; length]`, return -the repeated element and the length expressions. - -### as_slice - -```rust title="as_slice" showLineNumbers -pub comptime fn as_slice(self) -> Option<[Expr]> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L164-L166 - - -If this expression is a slice literal `&[elem1, ..., elemN]`, -return each element of the slice. - -### as_tuple - -```rust title="as_tuple" showLineNumbers -pub comptime fn as_tuple(self) -> Option<[Expr]> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L171-L173 - - -If this expression is a tuple `(field1, ..., fieldN)`, -return each element of the tuple. - -### as_unary_op - -```rust title="as_unary_op" showLineNumbers -pub comptime fn as_unary_op(self) -> Option<(UnaryOp, Expr)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L178-L180 - - -If this expression is a unary operation ` `, -return the unary operator as well as the right-hand side expression. - -### as_unsafe - -```rust title="as_unsafe" showLineNumbers -pub comptime fn as_unsafe(self) -> Option<[Expr]> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L185-L187 - - -If this expression is an `unsafe { stmt1; ...; stmtN }` block, -return each statement inside in a slice. - -### has_semicolon - -```rust title="has_semicolon" showLineNumbers -pub comptime fn has_semicolon(self) -> bool {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L206-L208 - - -`true` if this expression is trailed by a semicolon. E.g. - -``` -comptime { - let expr1 = quote { 1 + 2 }.as_expr().unwrap(); - let expr2 = quote { 1 + 2; }.as_expr().unwrap(); - - assert(expr1.as_binary_op().is_some()); - assert(expr2.as_binary_op().is_some()); - - assert(!expr1.has_semicolon()); - assert(expr2.has_semicolon()); -} -``` - -### is_break - -```rust title="is_break" showLineNumbers -pub comptime fn is_break(self) -> bool {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L212-L214 - - -`true` if this expression is `break`. - -### is_continue - -```rust title="is_continue" showLineNumbers -pub comptime fn is_continue(self) -> bool {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L218-L220 - - -`true` if this expression is `continue`. - -### modify - -```rust title="modify" showLineNumbers -pub comptime fn modify(self, f: fn[Env](Expr) -> Option) -> Expr { -``` -> Source code: noir_stdlib/src/meta/expr.nr#L229-L231 - - -Applies a mapping function to this expression and to all of its sub-expressions. -`f` will be applied to each sub-expression first, then applied to the expression itself. - -This happens recursively for every expression within `self`. - -For example, calling `modify` on `(&[1], &[2, 3])` with an `f` that returns `Option::some` -for expressions that are integers, doubling them, would return `(&[2], &[4, 6])`. - -### quoted - -```rust title="quoted" showLineNumbers -pub comptime fn quoted(self) -> Quoted { -``` -> Source code: noir_stdlib/src/meta/expr.nr#L266-L268 - - -Returns this expression as a `Quoted` value. It's the same as `quote { $self }`. - -### resolve - -```rust title="resolve" showLineNumbers -pub comptime fn resolve(self, in_function: Option) -> TypedExpr {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L282-L284 - - -Resolves and type-checks this expression and returns the result as a `TypedExpr`. - -The `in_function` argument specifies where the expression is resolved: -- If it's `none`, the expression is resolved in the function where `resolve` was called -- If it's `some`, the expression is resolved in the given function - -If any names used by this expression are not in scope or if there are any type errors, -this will give compiler errors as if the expression was written directly into -the current `comptime` function. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.38.0/noir/standard_library/meta/function_def.md b/docs/versioned_docs/version-v0.38.0/noir/standard_library/meta/function_def.md deleted file mode 100644 index b7f2ebdb889..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/standard_library/meta/function_def.md +++ /dev/null @@ -1,166 +0,0 @@ ---- -title: FunctionDefinition ---- - -`std::meta::function_def` contains methods on the built-in `FunctionDefinition` type representing -a function definition in the source program. - -## Methods - -### add_attribute - -```rust title="add_attribute" showLineNumbers -pub comptime fn add_attribute(self, attribute: str) {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L3-L5 - - -Adds an attribute to the function. This is only valid -on functions in the current crate which have not yet been resolved. -This means any functions called at compile-time are invalid targets for this method. - -### body - -```rust title="body" showLineNumbers -pub comptime fn body(self) -> Expr {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L8-L10 - - -Returns the body of the function as an expression. This is only valid -on functions in the current crate which have not yet been resolved. -This means any functions called at compile-time are invalid targets for this method. - -### has_named_attribute - -```rust title="has_named_attribute" showLineNumbers -pub comptime fn has_named_attribute(self, name: str) -> bool {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L13-L15 - - -Returns true if this function has a custom attribute with the given name. - -### is_unconstrained - -```rust title="is_unconstrained" showLineNumbers -pub comptime fn is_unconstrained(self) -> bool {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L18-L20 - - -Returns true if this function is unconstrained. - -### module - -```rust title="module" showLineNumbers -pub comptime fn module(self) -> Module {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L23-L25 - - -Returns the module where the function is defined. - -### name - -```rust title="name" showLineNumbers -pub comptime fn name(self) -> Quoted {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L28-L30 - - -Returns the name of the function. - -### parameters - -```rust title="parameters" showLineNumbers -pub comptime fn parameters(self) -> [(Quoted, Type)] {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L33-L35 - - -Returns each parameter of the function as a tuple of (parameter pattern, parameter type). - -### return_type - -```rust title="return_type" showLineNumbers -pub comptime fn return_type(self) -> Type {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L38-L40 - - -The return type of the function. - -### set_body - -```rust title="set_body" showLineNumbers -pub comptime fn set_body(self, body: Expr) {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L43-L45 - - -Mutate the function body to a new expression. This is only valid -on functions in the current crate which have not yet been resolved. -This means any functions called at compile-time are invalid targets for this method. - -### set_parameters - -```rust title="set_parameters" showLineNumbers -pub comptime fn set_parameters(self, parameters: [(Quoted, Type)]) {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L48-L50 - - -Mutates the function's parameters to a new set of parameters. This is only valid -on functions in the current crate which have not yet been resolved. -This means any functions called at compile-time are invalid targets for this method. - -Expects a slice of (parameter pattern, parameter type) for each parameter. Requires -each parameter pattern to be a syntactically valid parameter. - -### set_return_type - -```rust title="set_return_type" showLineNumbers -pub comptime fn set_return_type(self, return_type: Type) {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L53-L55 - - -Mutates the function's return type to a new type. This is only valid -on functions in the current crate which have not yet been resolved. -This means any functions called at compile-time are invalid targets for this method. - -### set_return_public - -```rust title="set_return_public" showLineNumbers -pub comptime fn set_return_public(self, public: bool) {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L58-L60 - - -Mutates the function's return visibility to public (if `true` is given) or private (if `false` is given). -This is only valid on functions in the current crate which have not yet been resolved. -This means any functions called at compile-time are invalid targets for this method. - -### set_unconstrained - -```rust title="set_unconstrained" showLineNumbers -pub comptime fn set_unconstrained(self, value: bool) {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L66-L68 - - -Mutates the function to be unconstrained (if `true` is given) or not (if `false` is given). -This is only valid on functions in the current crate which have not yet been resolved. -This means any functions called at compile-time are invalid targets for this method. - -## Trait Implementations - -```rust -impl Eq for FunctionDefinition -impl Hash for FunctionDefinition -``` - -Note that each function is assigned a unique ID internally and this is what is used for -equality and hashing. So even functions with identical signatures and bodies may not -be equal in this sense if they were originally different items in the source program. diff --git a/docs/versioned_docs/version-v0.38.0/noir/standard_library/meta/index.md b/docs/versioned_docs/version-v0.38.0/noir/standard_library/meta/index.md deleted file mode 100644 index 14544c07442..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/standard_library/meta/index.md +++ /dev/null @@ -1,224 +0,0 @@ ---- -title: Metaprogramming -description: Noir's Metaprogramming API -keywords: [metaprogramming, comptime, macros, macro, quote, unquote] ---- - -`std::meta` is the entry point for Noir's metaprogramming API. This consists of `comptime` functions -and types used for inspecting and modifying Noir programs. - -## Functions - -### type_of - -```rust title="type_of" showLineNumbers -pub comptime fn type_of(x: T) -> Type {} -``` -> Source code: noir_stdlib/src/meta/mod.nr#L27-L29 - - -Returns the type of a variable at compile-time. - -Example: -```rust -comptime { - let x: i32 = 1; - let x_type: Type = std::meta::type_of(x); - - assert_eq(x_type, quote { i32 }.as_type()); -} -``` - -### unquote - -```rust title="unquote" showLineNumbers -pub comptime fn unquote(code: Quoted) -> Quoted { -``` -> Source code: noir_stdlib/src/meta/mod.nr#L19-L21 - - -Unquotes the passed-in token stream where this function was called. - -Example: -```rust -comptime { - let code = quote { 1 + 2 }; - - // let x = 1 + 2; - let x = unquote!(code); -} -``` - -### derive - -```rust title="derive" showLineNumbers -#[varargs] -pub comptime fn derive(s: StructDefinition, traits: [TraitDefinition]) -> Quoted { -``` -> Source code: noir_stdlib/src/meta/mod.nr#L48-L51 - - -Attribute placed on struct definitions. - -Creates a trait impl for each trait passed in as an argument. -To do this, the trait must have a derive handler registered -with `derive_via` beforehand. The traits in the stdlib that -can be derived this way are `Eq`, `Ord`, `Default`, and `Hash`. - -Example: -```rust -#[derive(Eq, Default)] -struct Foo { - x: i32, - y: T, -} - -fn main() { - let foo1 = Foo::default(); - let foo2 = Foo { x: 0, y: &[0] }; - assert_eq(foo1, foo2); -} -``` - -### derive_via - -```rust title="derive_via_signature" showLineNumbers -pub comptime fn derive_via(t: TraitDefinition, f: DeriveFunction) { -``` -> Source code: noir_stdlib/src/meta/mod.nr#L68-L70 - - -Attribute placed on trait definitions. - -Registers a function to create impls for the given trait -when the trait is used in a `derive` call. Users may use -this to register their own functions to enable their traits -to be derived by `derive`. - -Because this function requires a function as an argument which -should produce a trait impl for any given struct, users may find -it helpful to use a function like `std::meta::make_trait_impl` to -help creating these impls. - -Example: -```rust -#[derive_via(derive_do_nothing)] -trait DoNothing { - fn do_nothing(self); -} - -comptime fn derive_do_nothing(s: StructDefinition) -> Quoted { - let typ = s.as_type(); - quote { - impl DoNothing for $typ { - fn do_nothing(self) { - println("Nothing"); - } - } - } -} -``` - -As another example, `derive_eq` in the stdlib is used to derive the `Eq` -trait for any struct. It makes use of `make_trait_impl` to do this: - -```rust title="derive_eq" showLineNumbers -comptime fn derive_eq(s: StructDefinition) -> Quoted { - let signature = quote { fn eq(_self: Self, _other: Self) -> bool }; - let for_each_field = |name| quote { (_self.$name == _other.$name) }; - let body = |fields| { - if s.fields().len() == 0 { - quote { true } - } else { - fields - } - }; - crate::meta::make_trait_impl( - s, - quote { Eq }, - signature, - for_each_field, - quote { & }, - body, - ) -} -``` -> Source code: noir_stdlib/src/cmp.nr#L10-L30 - - -### make_trait_impl - -```rust title="make_trait_impl" showLineNumbers -pub comptime fn make_trait_impl( - s: StructDefinition, - trait_name: Quoted, - function_signature: Quoted, - for_each_field: fn[Env1](Quoted) -> Quoted, - join_fields_with: Quoted, - body: fn[Env2](Quoted) -> Quoted, -) -> Quoted { -``` -> Source code: noir_stdlib/src/meta/mod.nr#L87-L96 - - -A helper function to more easily create trait impls while deriving traits. - -Note that this function only works for traits which: -1. Have only one method -2. Have no generics on the trait itself. - - E.g. Using this on a trait such as `trait Foo { ... }` will result in the - generated impl incorrectly missing the `T` generic. - -If your trait fits these criteria then `make_trait_impl` is likely the easiest -way to write your derive handler. The arguments are as follows: - -- `s`: The struct to make the impl for -- `trait_name`: The name of the trait to derive. E.g. `quote { Eq }`. -- `function_signature`: The signature of the trait method to derive. E.g. `fn eq(self, other: Self) -> bool`. -- `for_each_field`: An operation to be performed on each field. E.g. `|name| quote { (self.$name == other.$name) }`. -- `join_fields_with`: A separator to join each result of `for_each_field` with. - E.g. `quote { & }`. You can also use an empty `quote {}` for no separator. -- `body`: The result of the field operations are passed into this function for any final processing. - This is the place to insert any setup/teardown code the trait requires. If the trait doesn't require - any such code, you can return the body as-is: `|body| body`. - -Example deriving `Hash`: - -```rust title="derive_hash" showLineNumbers -comptime fn derive_hash(s: StructDefinition) -> Quoted { - let name = quote { Hash }; - let signature = quote { fn hash(_self: Self, _state: &mut H) where H: std::hash::Hasher }; - let for_each_field = |name| quote { _self.$name.hash(_state); }; - crate::meta::make_trait_impl( - s, - name, - signature, - for_each_field, - quote {}, - |fields| fields, - ) -} -``` -> Source code: noir_stdlib/src/hash/mod.nr#L137-L151 - - -Example deriving `Ord`: - -```rust title="derive_ord" showLineNumbers -comptime fn derive_ord(s: StructDefinition) -> Quoted { - let signature = quote { fn cmp(_self: Self, _other: Self) -> std::cmp::Ordering }; - let for_each_field = |name| quote { - if result == std::cmp::Ordering::equal() { - result = _self.$name.cmp(_other.$name); - } - }; - let body = |fields| quote { - let mut result = std::cmp::Ordering::equal(); - $fields - result - }; - crate::meta::make_trait_impl(s, quote { Ord }, signature, for_each_field, quote {}, body) -} -``` -> Source code: noir_stdlib/src/cmp.nr#L216-L231 - diff --git a/docs/versioned_docs/version-v0.38.0/noir/standard_library/meta/module.md b/docs/versioned_docs/version-v0.38.0/noir/standard_library/meta/module.md deleted file mode 100644 index f47231972b7..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/standard_library/meta/module.md +++ /dev/null @@ -1,82 +0,0 @@ ---- -title: Module ---- - -`std::meta::module` contains methods on the built-in `Module` type which represents a module in the source program. -Note that this type represents a module generally, it isn't limited to only `mod my_submodule { ... }` -declarations in the source program. - -## Methods - -### add_item - -```rust title="add_item" showLineNumbers -pub comptime fn add_item(self, item: Quoted) {} -``` -> Source code: noir_stdlib/src/meta/module.nr#L3-L5 - - -Adds a top-level item (a function, a struct, a global, etc.) to the module. -Adding multiple items in one go is also valid if the `Quoted` value has multiple items in it. -Note that the items are type-checked as if they are inside the module they are being added to. - -### functions - -```rust title="functions" showLineNumbers -pub comptime fn functions(self) -> [FunctionDefinition] {} -``` -> Source code: noir_stdlib/src/meta/module.nr#L18-L20 - - -Returns each function defined in the module. - -### has_named_attribute - -```rust title="has_named_attribute" showLineNumbers -pub comptime fn has_named_attribute(self, name: str) -> bool {} -``` -> Source code: noir_stdlib/src/meta/module.nr#L8-L10 - - -Returns true if this module has a custom attribute with the given name. - -### is_contract - -```rust title="is_contract" showLineNumbers -pub comptime fn is_contract(self) -> bool {} -``` -> Source code: noir_stdlib/src/meta/module.nr#L13-L15 - - -`true` if this module is a contract module (was declared via `contract foo { ... }`). - -### name - -```rust title="name" showLineNumbers -pub comptime fn name(self) -> Quoted {} -``` -> Source code: noir_stdlib/src/meta/module.nr#L28-L30 - - -Returns the name of the module. - -### structs - -```rust title="structs" showLineNumbers -pub comptime fn structs(self) -> [StructDefinition] {} -``` -> Source code: noir_stdlib/src/meta/module.nr#L23-L25 - - -Returns each struct defined in the module. - -## Trait Implementations - -```rust -impl Eq for Module -impl Hash for Module -``` - -Note that each module is assigned a unique ID internally and this is what is used for -equality and hashing. So even modules with identical names and contents may not -be equal in this sense if they were originally different items in the source program. diff --git a/docs/versioned_docs/version-v0.38.0/noir/standard_library/meta/op.md b/docs/versioned_docs/version-v0.38.0/noir/standard_library/meta/op.md deleted file mode 100644 index 03ea49ad8ec..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/standard_library/meta/op.md +++ /dev/null @@ -1,244 +0,0 @@ ---- -title: UnaryOp and BinaryOp ---- - -`std::meta::op` contains the `UnaryOp` and `BinaryOp` types as well as methods on them. -These types are used to represent a unary or binary operator respectively in Noir source code. - -## Types - -### UnaryOp - -Represents a unary operator. One of `-`, `!`, `&mut`, or `*`. - -### Methods - -#### is_minus - -```rust title="is_minus" showLineNumbers -pub fn is_minus(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L24-L26 - - -Returns `true` if this operator is `-`. - -#### is_not - -```rust title="is_not" showLineNumbers -pub fn is_not(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L30-L32 - - -`true` if this operator is `!` - -#### is_mutable_reference - -```rust title="is_mutable_reference" showLineNumbers -pub fn is_mutable_reference(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L36-L38 - - -`true` if this operator is `&mut` - -#### is_dereference - -```rust title="is_dereference" showLineNumbers -pub fn is_dereference(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L42-L44 - - -`true` if this operator is `*` - -#### quoted - -```rust title="unary_quoted" showLineNumbers -pub comptime fn quoted(self) -> Quoted { -``` -> Source code: noir_stdlib/src/meta/op.nr#L48-L50 - - -Returns this operator as a `Quoted` value. - -### Trait Implementations - -```rust -impl Eq for UnaryOp -impl Hash for UnaryOp -``` - -### BinaryOp - -Represents a binary operator. One of `+`, `-`, `*`, `/`, `%`, `==`, `!=`, `<`, `<=`, `>`, `>=`, `&`, `|`, `^`, `>>`, or `<<`. - -### Methods - -#### is_add - -```rust title="is_add" showLineNumbers -pub fn is_add(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L86-L88 - - -`true` if this operator is `+` - -#### is_subtract - -```rust title="is_subtract" showLineNumbers -pub fn is_subtract(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L92-L94 - - -`true` if this operator is `-` - -#### is_multiply - -```rust title="is_multiply" showLineNumbers -pub fn is_multiply(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L98-L100 - - -`true` if this operator is `*` - -#### is_divide - -```rust title="is_divide" showLineNumbers -pub fn is_divide(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L104-L106 - - -`true` if this operator is `/` - -#### is_modulo - -```rust title="is_modulo" showLineNumbers -pub fn is_modulo(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L176-L178 - - -`true` if this operator is `%` - -#### is_equal - -```rust title="is_equal" showLineNumbers -pub fn is_equal(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L110-L112 - - -`true` if this operator is `==` - -#### is_not_equal - -```rust title="is_not_equal" showLineNumbers -pub fn is_not_equal(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L116-L118 - - -`true` if this operator is `!=` - -#### is_less_than - -```rust title="is_less_than" showLineNumbers -pub fn is_less_than(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L122-L124 - - -`true` if this operator is `<` - -#### is_less_than_or_equal - -```rust title="is_less_than_or_equal" showLineNumbers -pub fn is_less_than_or_equal(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L128-L130 - - -`true` if this operator is `<=` - -#### is_greater_than - -```rust title="is_greater_than" showLineNumbers -pub fn is_greater_than(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L134-L136 - - -`true` if this operator is `>` - -#### is_greater_than_or_equal - -```rust title="is_greater_than_or_equal" showLineNumbers -pub fn is_greater_than_or_equal(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L140-L142 - - -`true` if this operator is `>=` - -#### is_and - -```rust title="is_and" showLineNumbers -pub fn is_and(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L146-L148 - - -`true` if this operator is `&` - -#### is_or - -```rust title="is_or" showLineNumbers -pub fn is_or(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L152-L154 - - -`true` if this operator is `|` - -#### is_shift_right - -```rust title="is_shift_right" showLineNumbers -pub fn is_shift_right(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L164-L166 - - -`true` if this operator is `>>` - -#### is_shift_left - -```rust title="is_shift_right" showLineNumbers -pub fn is_shift_right(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L164-L166 - - -`true` if this operator is `<<` - -#### quoted - -```rust title="binary_quoted" showLineNumbers -pub comptime fn quoted(self) -> Quoted { -``` -> Source code: noir_stdlib/src/meta/op.nr#L182-L184 - - -Returns this operator as a `Quoted` value. - -### Trait Implementations - -```rust -impl Eq for BinaryOp -impl Hash for BinaryOp -``` diff --git a/docs/versioned_docs/version-v0.38.0/noir/standard_library/meta/quoted.md b/docs/versioned_docs/version-v0.38.0/noir/standard_library/meta/quoted.md deleted file mode 100644 index d7acf23bc07..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/standard_library/meta/quoted.md +++ /dev/null @@ -1,141 +0,0 @@ ---- -title: Quoted ---- - -`std::meta::quoted` contains methods on the built-in `Quoted` type which represents -quoted token streams and is the result of the `quote { ... }` expression. - -## Methods - -### as_expr - -```rust title="as_expr" showLineNumbers -pub comptime fn as_expr(self) -> Option {} -``` -> Source code: noir_stdlib/src/meta/quoted.nr#L6-L8 - - -Parses the quoted token stream as an expression. Returns `Option::none()` if -the expression failed to parse. - -Example: - -```rust title="as_expr_example" showLineNumbers -#[test] - fn test_expr_as_function_call() { - comptime - { - let expr = quote { foo(42) }.as_expr().unwrap(); - let (_function, args) = expr.as_function_call().unwrap(); - assert_eq(args.len(), 1); - assert_eq(args[0].as_integer().unwrap(), (42, false)); - } - } -``` -> Source code: test_programs/noir_test_success/comptime_expr/src/main.nr#L360-L371 - - -### as_module - -```rust title="as_module" showLineNumbers -pub comptime fn as_module(self) -> Option {} -``` -> Source code: noir_stdlib/src/meta/quoted.nr#L11-L13 - - -Interprets this token stream as a module path leading to the name of a module. -Returns `Option::none()` if the module isn't found or this token stream cannot be parsed as a path. - -Example: - -```rust title="as_module_example" showLineNumbers -mod baz { - pub mod qux {} -} - -#[test] -fn as_module_test() { - comptime { - let my_mod = quote { baz::qux }.as_module().unwrap(); - assert_eq(my_mod.name(), quote { qux }); - } -} -``` -> Source code: test_programs/compile_success_empty/comptime_module/src/main.nr#L115-L127 - - -### as_trait_constraint - -```rust title="as_trait_constraint" showLineNumbers -pub comptime fn as_trait_constraint(self) -> TraitConstraint {} -``` -> Source code: noir_stdlib/src/meta/quoted.nr#L16-L18 - - -Interprets this token stream as a trait constraint (without an object type). -Note that this function panics instead of returning `Option::none()` if the token -stream does not parse and resolve to a valid trait constraint. - -Example: - -```rust title="implements_example" showLineNumbers -pub fn function_with_where(_x: T) -where - T: SomeTrait, -{ - comptime { - let t = quote { T }.as_type(); - let some_trait_i32 = quote { SomeTrait }.as_trait_constraint(); - assert(t.implements(some_trait_i32)); - - assert(t.get_trait_impl(some_trait_i32).is_none()); - } -} -``` -> Source code: test_programs/compile_success_empty/comptime_type/src/main.nr#L160-L173 - - -### as_type - -```rust title="as_type" showLineNumbers -pub comptime fn as_type(self) -> Type {} -``` -> Source code: noir_stdlib/src/meta/quoted.nr#L21-L23 - - -Interprets this token stream as a resolved type. Panics if the token -stream doesn't parse to a type or if the type isn't a valid type in scope. - -```rust title="implements_example" showLineNumbers -pub fn function_with_where(_x: T) -where - T: SomeTrait, -{ - comptime { - let t = quote { T }.as_type(); - let some_trait_i32 = quote { SomeTrait }.as_trait_constraint(); - assert(t.implements(some_trait_i32)); - - assert(t.get_trait_impl(some_trait_i32).is_none()); - } -} -``` -> Source code: test_programs/compile_success_empty/comptime_type/src/main.nr#L160-L173 - - -### tokens - -```rust title="tokens" showLineNumbers -pub comptime fn tokens(self) -> [Quoted] {} -``` -> Source code: noir_stdlib/src/meta/quoted.nr#L26-L28 - - -Returns a slice of the individual tokens that form this token stream. - -## Trait Implementations - -```rust -impl Eq for Quoted -impl Hash for Quoted -``` diff --git a/docs/versioned_docs/version-v0.38.0/noir/standard_library/meta/struct_def.md b/docs/versioned_docs/version-v0.38.0/noir/standard_library/meta/struct_def.md deleted file mode 100644 index fd609942f4e..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/standard_library/meta/struct_def.md +++ /dev/null @@ -1,177 +0,0 @@ ---- -title: StructDefinition ---- - -`std::meta::struct_def` contains methods on the built-in `StructDefinition` type. -This type corresponds to `struct Name { field1: Type1, ... }` items in the source program. - -## Methods - -### add_attribute - -```rust title="add_attribute" showLineNumbers -pub comptime fn add_attribute(self, attribute: str) {} -``` -> Source code: noir_stdlib/src/meta/struct_def.nr#L3-L5 - - -Adds an attribute to the struct. - -### add_generic - -```rust title="add_generic" showLineNumbers -pub comptime fn add_generic(self, generic_name: str) -> Type {} -``` -> Source code: noir_stdlib/src/meta/struct_def.nr#L8-L10 - - -Adds an generic to the struct. Returns the new generic type. -Errors if the given generic name isn't a single identifier or if -the struct already has a generic with the same name. - -This method should be used carefully, if there is existing code referring -to the struct type it may be checked before this function is called and -see the struct with the original number of generics. This method should -thus be preferred to use on code generated from other macros and structs -that are not used in function signatures. - -Example: - -```rust title="add-generic-example" showLineNumbers -comptime fn add_generic(s: StructDefinition) { - assert_eq(s.generics().len(), 0); - let new_generic = s.add_generic("T"); - - let generics = s.generics(); - assert_eq(generics.len(), 1); - assert_eq(generics[0], new_generic); - } -``` -> Source code: test_programs/compile_success_empty/comptime_struct_definition/src/main.nr#L35-L44 - - -### as_type - -```rust title="as_type" showLineNumbers -pub comptime fn as_type(self) -> Type {} -``` -> Source code: noir_stdlib/src/meta/struct_def.nr#L15-L17 - - -Returns this struct as a type in the source program. If this struct has -any generics, the generics are also included as-is. - -### generics - -```rust title="generics" showLineNumbers -pub comptime fn generics(self) -> [Type] {} -``` -> Source code: noir_stdlib/src/meta/struct_def.nr#L26-L28 - - -Returns each generic on this struct. - -Example: - -``` -#[example] -struct Foo { - bar: [T; 2], - baz: Baz, -} - -comptime fn example(foo: StructDefinition) { - assert_eq(foo.generics().len(), 2); - - // Fails because `T` isn't in scope - // let t = quote { T }.as_type(); - // assert_eq(foo.generics()[0], t); -} -``` - -### fields - -```rust title="fields" showLineNumbers -pub comptime fn fields(self) -> [(Quoted, Type)] {} -``` -> Source code: noir_stdlib/src/meta/struct_def.nr#L33-L35 - - -Returns each field of this struct as a pair of (field name, field type). - -### has_named_attribute - -```rust title="has_named_attribute" showLineNumbers -pub comptime fn has_named_attribute(self, name: str) -> bool {} -``` -> Source code: noir_stdlib/src/meta/struct_def.nr#L20-L22 - - -Returns true if this struct has a custom attribute with the given name. - -### module - -```rust title="module" showLineNumbers -pub comptime fn module(self) -> Module {} -``` -> Source code: noir_stdlib/src/meta/struct_def.nr#L38-L40 - - -Returns the module where the struct is defined. - -### name - -```rust title="name" showLineNumbers -pub comptime fn name(self) -> Quoted {} -``` -> Source code: noir_stdlib/src/meta/struct_def.nr#L43-L45 - - -Returns the name of this struct - -Note that the returned quoted value will be just the struct name, it will -not be the full path to the struct, nor will it include any generics. - -### set_fields - -```rust title="set_fields" showLineNumbers -pub comptime fn set_fields(self, new_fields: [(Quoted, Type)]) {} -``` -> Source code: noir_stdlib/src/meta/struct_def.nr#L52-L54 - - -Sets the fields of this struct to the given fields list where each element -is a pair of the field's name and the field's type. Expects each field name -to be a single identifier. Note that this will override any previous fields -on this struct. If those should be preserved, use `.fields()` to retrieve the -current fields on the struct type and append the new fields from there. - -Example: - -```rust -// Change this struct to: -// struct Foo { -// a: u32, -// b: i8, -// } -#[mangle_fields] -struct Foo { x: Field } - -comptime fn mangle_fields(s: StructDefinition) { - s.set_fields(&[ - (quote { a }, quote { u32 }.as_type()), - (quote { b }, quote { i8 }.as_type()), - ]); -} -``` - -## Trait Implementations - -```rust -impl Eq for StructDefinition -impl Hash for StructDefinition -``` - -Note that each struct is assigned a unique ID internally and this is what is used for -equality and hashing. So even structs with identical generics and fields may not -be equal in this sense if they were originally different items in the source program. diff --git a/docs/versioned_docs/version-v0.38.0/noir/standard_library/meta/trait_constraint.md b/docs/versioned_docs/version-v0.38.0/noir/standard_library/meta/trait_constraint.md deleted file mode 100644 index 3106f732b5a..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/standard_library/meta/trait_constraint.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -title: TraitConstraint ---- - -`std::meta::trait_constraint` contains methods on the built-in `TraitConstraint` type which represents -a trait constraint that can be used to search for a trait implementation. This is similar -syntactically to just the trait itself, but can also contain generic arguments. E.g. `Eq`, `Default`, -`BuildHasher`. - -This type currently has no public methods but it can be used alongside `Type` in `implements` or `get_trait_impl`. - -## Trait Implementations - -```rust -impl Eq for TraitConstraint -impl Hash for TraitConstraint -``` diff --git a/docs/versioned_docs/version-v0.38.0/noir/standard_library/meta/trait_def.md b/docs/versioned_docs/version-v0.38.0/noir/standard_library/meta/trait_def.md deleted file mode 100644 index e661d3af7f1..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/standard_library/meta/trait_def.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: TraitDefinition ---- - -`std::meta::trait_def` contains methods on the built-in `TraitDefinition` type. This type -represents trait definitions such as `trait Foo { .. }` at the top-level of a program. - -## Methods - -### as_trait_constraint - -```rust title="as_trait_constraint" showLineNumbers -pub comptime fn as_trait_constraint(_self: Self) -> TraitConstraint {} -``` -> Source code: noir_stdlib/src/meta/trait_def.nr#L6-L8 - - -Converts this trait into a trait constraint. If there are any generics on this -trait, they will be kept as-is without instantiating or replacing them. - -## Trait Implementations - -```rust -impl Eq for TraitDefinition -impl Hash for TraitDefinition -``` diff --git a/docs/versioned_docs/version-v0.38.0/noir/standard_library/meta/trait_impl.md b/docs/versioned_docs/version-v0.38.0/noir/standard_library/meta/trait_impl.md deleted file mode 100644 index a527617c1e6..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/standard_library/meta/trait_impl.md +++ /dev/null @@ -1,60 +0,0 @@ ---- -title: TraitImpl ---- - -`std::meta::trait_impl` contains methods on the built-in `TraitImpl` type which represents a trait -implementation such as `impl Foo for Bar { ... }`. - -## Methods - -### trait_generic_args - -```rust title="trait_generic_args" showLineNumbers -pub comptime fn trait_generic_args(self) -> [Type] {} -``` -> Source code: noir_stdlib/src/meta/trait_impl.nr#L3-L5 - - -Returns any generic arguments on the trait of this trait implementation, if any. - -```rs -impl Foo for Bar { ... } - -comptime { - let bar_type = quote { Bar }.as_type(); - let foo = quote { Foo }.as_trait_constraint(); - - let my_impl: TraitImpl = bar_type.get_trait_impl(foo).unwrap(); - - let generics = my_impl.trait_generic_args(); - assert_eq(generics.len(), 2); - - assert_eq(generics[0], quote { i32 }.as_type()); - assert_eq(generics[1], quote { Field }.as_type()); -} -``` - -### methods - -```rust title="methods" showLineNumbers -pub comptime fn methods(self) -> [FunctionDefinition] {} -``` -> Source code: noir_stdlib/src/meta/trait_impl.nr#L8-L10 - - -Returns each method in this trait impl. - -Example: - -```rs -comptime { - let i32_type = quote { i32 }.as_type(); - let eq = quote { Eq }.as_trait_constraint(); - - let impl_eq_for_i32: TraitImpl = i32_type.get_trait_impl(eq).unwrap(); - let methods = impl_eq_for_i32.methods(); - - assert_eq(methods.len(), 1); - assert_eq(methods[0].name(), quote { eq }); -} -``` diff --git a/docs/versioned_docs/version-v0.38.0/noir/standard_library/meta/typ.md b/docs/versioned_docs/version-v0.38.0/noir/standard_library/meta/typ.md deleted file mode 100644 index 90222c222f5..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/standard_library/meta/typ.md +++ /dev/null @@ -1,264 +0,0 @@ ---- -title: Type ---- - -`std::meta::typ` contains methods on the built-in `Type` type used for representing -a type in the source program. - -## Functions - -```rust title="fresh_type_variable" showLineNumbers -pub comptime fn fresh_type_variable() -> Type {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L57-L59 - - -Creates and returns an unbound type variable. This is a special kind of type internal -to type checking which will type check with any other type. When it is type checked -against another type it will also be set to that type. For example, if `a` is a type -variable and we have the type equality `(a, i32) = (u8, i32)`, the compiler will set -`a` equal to `u8`. - -Unbound type variables will often be rendered as `_` while printing them. Bound type -variables will appear as the type they are bound to. - -This can be used in conjunction with functions which internally perform type checks -such as `Type::implements` or `Type::get_trait_impl` to potentially grab some of the types used. - -Note that calling `Type::implements` or `Type::get_trait_impl` on a type variable will always -fail. - -Example: - -```rust title="serialize-setup" showLineNumbers -trait Serialize {} - -impl Serialize<1> for Field {} - -impl Serialize for [T; N] -where - T: Serialize, -{} - -impl Serialize for (T, U) -where - T: Serialize, - U: Serialize, -{} -``` -> Source code: test_programs/compile_success_empty/comptime_type/src/main.nr#L14-L29 - -```rust title="fresh-type-variable-example" showLineNumbers -let typevar1 = std::meta::typ::fresh_type_variable(); - let constraint = quote { Serialize<$typevar1> }.as_trait_constraint(); - let field_type = quote { Field }.as_type(); - - // Search for a trait impl (binding typevar1 to 1 when the impl is found): - assert(field_type.implements(constraint)); - - // typevar1 should be bound to the "1" generic now: - assert_eq(typevar1.as_constant().unwrap(), 1); - - // If we want to do the same with a different type, we need to - // create a new type variable now that `typevar1` is bound - let typevar2 = std::meta::typ::fresh_type_variable(); - let constraint = quote { Serialize<$typevar2> }.as_trait_constraint(); - let array_type = quote { [(Field, Field); 5] }.as_type(); - assert(array_type.implements(constraint)); - - // Now typevar2 should be bound to the serialized pair size 2 times the array length 5 - assert_eq(typevar2.as_constant().unwrap(), 10); -``` -> Source code: test_programs/compile_success_empty/comptime_type/src/main.nr#L129-L149 - - -## Methods - -### as_array - -```rust title="as_array" showLineNumbers -pub comptime fn as_array(self) -> Option<(Type, Type)> {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L76-L78 - - -If this type is an array, return a pair of (element type, size type). - -Example: - -```rust -comptime { - let array_type = quote { [Field; 3] }.as_type(); - let (field_type, three_type) = array_type.as_array().unwrap(); - - assert(field_type.is_field()); - assert_eq(three_type.as_constant().unwrap(), 3); -} -``` - -### as_constant - -```rust title="as_constant" showLineNumbers -pub comptime fn as_constant(self) -> Option {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L83-L85 - - -If this type is a constant integer (such as the `3` in the array type `[Field; 3]`), -return the numeric constant. - -### as_integer - -```rust title="as_integer" showLineNumbers -pub comptime fn as_integer(self) -> Option<(bool, u8)> {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L90-L92 - - -If this is an integer type, return a boolean which is `true` -if the type is signed, as well as the number of bits of this integer type. - -### as_mutable_reference - -```rust title="as_mutable_reference" showLineNumbers -comptime fn as_mutable_reference(self) -> Option {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L96-L98 - - -If this is a mutable reference type `&mut T`, returns the mutable type `T`. - -### as_slice - -```rust title="as_slice" showLineNumbers -pub comptime fn as_slice(self) -> Option {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L102-L104 - - -If this is a slice type, return the element type of the slice. - -### as_str - -```rust title="as_str" showLineNumbers -pub comptime fn as_str(self) -> Option {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L108-L110 - - -If this is a `str` type, returns the length `N` as a type. - -### as_struct - -```rust title="as_struct" showLineNumbers -pub comptime fn as_struct(self) -> Option<(StructDefinition, [Type])> {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L114-L116 - - -If this is a struct type, returns the struct in addition to -any generic arguments on this type. - -### as_tuple - -```rust title="as_tuple" showLineNumbers -pub comptime fn as_tuple(self) -> Option<[Type]> {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L120-L122 - - -If this is a tuple type, returns each element type of the tuple. - -### get_trait_impl - -```rust title="get_trait_impl" showLineNumbers -pub comptime fn get_trait_impl(self, constraint: TraitConstraint) -> Option {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L143-L145 - - -Retrieves the trait implementation that implements the given -trait constraint for this type. If the trait constraint is not -found, `None` is returned. Note that since the concrete trait implementation -for a trait constraint specified from a `where` clause is unknown, -this function will return `None` in these cases. If you only want to know -whether a type implements a trait, use `implements` instead. - -Example: - -```rust -comptime { - let field_type = quote { Field }.as_type(); - let default = quote { Default }.as_trait_constraint(); - - let the_impl: TraitImpl = field_type.get_trait_impl(default).unwrap(); - assert(the_impl.methods().len(), 1); -} -``` - -### implements - -```rust title="implements" showLineNumbers -pub comptime fn implements(self, constraint: TraitConstraint) -> bool {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L166-L168 - - -`true` if this type implements the given trait. Note that unlike -`get_trait_impl` this will also return true for any `where` constraints -in scope. - -Example: - -```rust -fn foo() where T: Default { - comptime { - let field_type = quote { Field }.as_type(); - let default = quote { Default }.as_trait_constraint(); - assert(field_type.implements(default)); - - let t = quote { T }.as_type(); - assert(t.implements(default)); - } -} -``` - -### is_bool - -```rust title="is_bool" showLineNumbers -pub comptime fn is_bool(self) -> bool {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L172-L174 - - -`true` if this type is `bool`. - -### is_field - -```rust title="is_field" showLineNumbers -pub comptime fn is_field(self) -> bool {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L178-L180 - - -`true` if this type is `Field`. - -### is_unit - -```rust title="is_unit" showLineNumbers -comptime fn is_unit(self) -> bool {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L184-L186 - - -`true` if this type is the unit `()` type. - -## Trait Implementations - -```rust -impl Eq for Type -impl Hash for Type -``` -Note that this is syntactic equality, this is not the same as whether two types will type check -to be the same type. Unless type inference or generics are being used however, users should not -typically have to worry about this distinction unless `std::meta::typ::fresh_type_variable` is used. diff --git a/docs/versioned_docs/version-v0.38.0/noir/standard_library/meta/typed_expr.md b/docs/versioned_docs/version-v0.38.0/noir/standard_library/meta/typed_expr.md deleted file mode 100644 index 0db7dbfef61..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/standard_library/meta/typed_expr.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -title: TypedExpr ---- - -`std::meta::typed_expr` contains methods on the built-in `TypedExpr` type for resolved and type-checked expressions. - -## Methods - -### get_type - -```rust title="as_function_definition" showLineNumbers -pub comptime fn as_function_definition(self) -> Option {} -``` -> Source code: noir_stdlib/src/meta/typed_expr.nr#L7-L9 - - -If this expression refers to a function definitions, returns it. Otherwise returns `Option::none()`. - -### get_type - -```rust title="get_type" showLineNumbers -pub comptime fn get_type(self) -> Option {} -``` -> Source code: noir_stdlib/src/meta/typed_expr.nr#L13-L15 - - -Returns the type of the expression, or `Option::none()` if there were errors when the expression was previously resolved. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.38.0/noir/standard_library/meta/unresolved_type.md b/docs/versioned_docs/version-v0.38.0/noir/standard_library/meta/unresolved_type.md deleted file mode 100644 index 2826ec5ec0f..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/standard_library/meta/unresolved_type.md +++ /dev/null @@ -1,57 +0,0 @@ ---- -title: UnresolvedType ---- - -`std::meta::unresolved_type` contains methods on the built-in `UnresolvedType` type for the syntax of types. - -## Methods - -### as_mutable_reference - -```rust title="as_mutable_reference" showLineNumbers -comptime fn as_mutable_reference(self) -> Option {} -``` -> Source code: noir_stdlib/src/meta/unresolved_type.nr#L8-L10 - - -If this is a mutable reference type `&mut T`, returns the mutable type `T`. - -### as_slice - -```rust title="as_slice" showLineNumbers -comptime fn as_slice(self) -> Option {} -``` -> Source code: noir_stdlib/src/meta/unresolved_type.nr#L14-L16 - - -If this is a slice `&[T]`, returns the element type `T`. - -### is_bool - -```rust title="is_bool" showLineNumbers -comptime fn is_bool(self) -> bool {} -``` -> Source code: noir_stdlib/src/meta/unresolved_type.nr#L20-L22 - - -Returns `true` if this type is `bool`. - -### is_field - -```rust title="is_field" showLineNumbers -pub comptime fn is_field(self) -> bool {} -``` -> Source code: noir_stdlib/src/meta/unresolved_type.nr#L26-L28 - - -Returns true if this type refers to the Field type. - -### is_unit - -```rust title="is_unit" showLineNumbers -comptime fn is_unit(self) -> bool {} -``` -> Source code: noir_stdlib/src/meta/unresolved_type.nr#L32-L34 - - -Returns true if this type is the unit `()` type. diff --git a/docs/versioned_docs/version-v0.38.0/noir/standard_library/options.md b/docs/versioned_docs/version-v0.38.0/noir/standard_library/options.md deleted file mode 100644 index a1bd4e1de5f..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/standard_library/options.md +++ /dev/null @@ -1,101 +0,0 @@ ---- -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), -} -``` - -The `Option` type, already imported into your Noir program, can be used directly: - -```rust -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. - -### expect - -Asserts `self.is_some()` with a provided custom message and returns the contained `Some` value. The custom message is expected to be a format string. - -### 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.38.0/noir/standard_library/recursion.mdx b/docs/versioned_docs/version-v0.38.0/noir/standard_library/recursion.mdx deleted file mode 100644 index 60414a2fa51..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/standard_library/recursion.mdx +++ /dev/null @@ -1,85 +0,0 @@ ---- -title: Recursive Proofs -description: Learn about how to write recursive proofs in Noir. -keywords: [recursion, recursive proofs, verification_key, verify_proof] ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; - -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. - -Read [the explainer on recursion](../../explainers/explainer-recursion.md) to know more about this function and the [guide on how to use it.](../../how_to/how-to-recursion.md) - -## The `#[recursive]` Attribute - -In Noir, the `#[recursive]` attribute is used to indicate that a circuit is designed for recursive proof generation. When applied, it informs the compiler and the tooling that the circuit should be compiled in a way that makes its proofs suitable for recursive verification. This attribute eliminates the need for manual flagging of recursion at the tooling level, streamlining the proof generation process for recursive circuits. - -### Example usage with `#[recursive]` - -```rust -#[recursive] -fn main(x: Field, y: pub Field) { - assert(x == y, "x and y are not equal"); -} - -// This marks the circuit as recursion-friendly and indicates that proofs generated from this circuit -// are intended for recursive verification. -``` - -By incorporating this attribute directly in the circuit's definition, tooling like Nargo and NoirJS can automatically execute recursive-specific duties for Noir programs (e.g. recursive-friendly proof artifact generation) without additional flags or configurations. - -## Verifying Recursive Proofs - -```rust -#[foreign(recursive_aggregation)] -pub fn verify_proof(verification_key: [Field], proof: [Field], public_inputs: [Field], key_hash: Field) {} -``` - - - -## Example usage - -```rust - -fn main( - verification_key : [Field; 114], - proof : [Field; 93], - public_inputs : [Field; 1], - key_hash : Field, - proof_b : [Field; 93], -) { - std::verify_proof( - verification_key, - proof, - public_inputs, - key_hash - ); - - std::verify_proof( - verification_key, - proof_b, - public_inputs, - key_hash - ); -} -``` - -You can see a full example of recursive proofs in [this example recursion demo repo](https://github.com/noir-lang/noir-examples/tree/master/recursion). - -## 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. - -### `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. diff --git a/docs/versioned_docs/version-v0.38.0/noir/standard_library/traits.md b/docs/versioned_docs/version-v0.38.0/noir/standard_library/traits.md deleted file mode 100644 index ee20f9cd949..00000000000 --- a/docs/versioned_docs/version-v0.38.0/noir/standard_library/traits.md +++ /dev/null @@ -1,628 +0,0 @@ ---- -title: Traits -description: Noir's stdlib provides a few commonly used traits. -keywords: [traits, trait, interface, protocol, default, add, eq] ---- - -## `std::default` - -### `std::default::Default` - -```rust title="default-trait" showLineNumbers -pub trait Default { - fn default() -> Self; -} -``` -> Source code: noir_stdlib/src/default.nr#L4-L8 - - -Constructs a default value of a type. - -Implementations: -```rust -impl Default for Field { .. } - -impl Default for i8 { .. } -impl Default for i16 { .. } -impl Default for i32 { .. } -impl Default for i64 { .. } - -impl Default for u8 { .. } -impl Default for u16 { .. } -impl Default for u32 { .. } -impl Default for u64 { .. } - -impl Default for () { .. } -impl Default for bool { .. } - -impl Default for [T; N] - where T: Default { .. } - -impl Default for [T] { .. } - -impl Default for (A, B) - where A: Default, B: Default { .. } - -impl Default for (A, B, C) - where A: Default, B: Default, C: Default { .. } - -impl Default for (A, B, C, D) - where A: Default, B: Default, C: Default, D: Default { .. } - -impl Default for (A, B, C, D, E) - where A: Default, B: Default, C: Default, D: Default, E: Default { .. } -``` - -For primitive integer types, the return value of `default` is `0`. Container -types such as arrays are filled with default values of their element type, -except slices whose length is unknown and thus defaulted to zero. - ---- - -## `std::convert` - -### `std::convert::From` - -```rust title="from-trait" showLineNumbers -pub trait From { - fn from(input: T) -> Self; -} -``` -> Source code: noir_stdlib/src/convert.nr#L1-L5 - - -The `From` trait defines how to convert from a given type `T` to the type on which the trait is implemented. - -The Noir standard library provides a number of implementations of `From` between primitive types. -```rust title="from-impls" showLineNumbers -// Unsigned integers - -impl From for u32 { - fn from(value: u8) -> u32 { - value as u32 - } -} - -impl From for u64 { - fn from(value: u8) -> u64 { - value as u64 - } -} -impl From for u64 { - fn from(value: u32) -> u64 { - value as u64 - } -} - -impl From for Field { - fn from(value: u8) -> Field { - value as Field - } -} -impl From for Field { - fn from(value: u32) -> Field { - value as Field - } -} -impl From for Field { - fn from(value: u64) -> Field { - value as Field - } -} - -// Signed integers - -impl From for i32 { - fn from(value: i8) -> i32 { - value as i32 - } -} - -impl From for i64 { - fn from(value: i8) -> i64 { - value as i64 - } -} -impl From for i64 { - fn from(value: i32) -> i64 { - value as i64 - } -} - -// Booleans -impl From for u8 { - fn from(value: bool) -> u8 { - value as u8 - } -} -impl From for u32 { - fn from(value: bool) -> u32 { - value as u32 - } -} -impl From for u64 { - fn from(value: bool) -> u64 { - value as u64 - } -} -impl From for i8 { - fn from(value: bool) -> i8 { - value as i8 - } -} -impl From for i32 { - fn from(value: bool) -> i32 { - value as i32 - } -} -impl From for i64 { - fn from(value: bool) -> i64 { - value as i64 - } -} -impl From for Field { - fn from(value: bool) -> Field { - value as Field - } -} -``` -> Source code: noir_stdlib/src/convert.nr#L28-L119 - - -#### When to implement `From` - -As a general rule of thumb, `From` may be implemented in the [situations where it would be suitable in Rust](https://doc.rust-lang.org/std/convert/trait.From.html#when-to-implement-from): - -- The conversion is *infallible*: Noir does not provide an equivalent to Rust's `TryFrom`, if the conversion can fail then provide a named method instead. -- The conversion is *lossless*: semantically, it should not lose or discard information. For example, `u32: From` can losslessly convert any `u16` into a valid `u32` such that the original `u16` can be recovered. On the other hand, `u16: From` should not be implemented as `2**16` is a `u32` which cannot be losslessly converted into a `u16`. -- The conversion is *value-preserving*: the conceptual kind and meaning of the resulting value is the same, even though the Noir type and technical representation might be different. While it's possible to infallibly and losslessly convert a `u8` into a `str<2>` hex representation, `4u8` and `"04"` are too different for `str<2>: From` to be implemented. -- The conversion is *obvious*: it's the only reasonable conversion between the two types. If there's ambiguity on how to convert between them such that the same input could potentially map to two different values then a named method should be used. For instance rather than implementing `U128: From<[u8; 16]>`, the methods `U128::from_le_bytes` and `U128::from_be_bytes` are used as otherwise the endianness of the array would be ambiguous, resulting in two potential values of `U128` from the same byte array. - -One additional recommendation specific to Noir is: -- The conversion is *efficient*: it's relatively cheap to convert between the two types. Due to being a ZK DSL, it's more important to avoid unnecessary computation compared to Rust. If the implementation of `From` would encourage users to perform unnecessary conversion, resulting in additional proving time, then it may be preferable to expose functionality such that this conversion may be avoided. - -### `std::convert::Into` - -The `Into` trait is defined as the reciprocal of `From`. It should be easy to convince yourself that if we can convert to type `A` from type `B`, then it's possible to convert type `B` into type `A`. - -For this reason, implementing `From` on a type will automatically generate a matching `Into` implementation. One should always prefer implementing `From` over `Into` as implementing `Into` will not generate a matching `From` implementation. - -```rust title="into-trait" showLineNumbers -pub trait Into { - fn into(self) -> T; -} - -impl Into for U -where - T: From, -{ - fn into(self) -> T { - T::from(self) - } -} -``` -> Source code: noir_stdlib/src/convert.nr#L13-L26 - - -`Into` is most useful when passing function arguments where the types don't quite match up with what the function expects. In this case, the compiler has enough type information to perform the necessary conversion by just appending `.into()` onto the arguments in question. - ---- - -## `std::cmp` - -### `std::cmp::Eq` - -```rust title="eq-trait" showLineNumbers -pub trait Eq { - fn eq(self, other: Self) -> bool; -} -``` -> Source code: noir_stdlib/src/cmp.nr#L4-L8 - - -Returns `true` if `self` is equal to `other`. Implementing this trait on a type -allows the type to be used with `==` and `!=`. - -Implementations: -```rust -impl Eq for Field { .. } - -impl Eq for i8 { .. } -impl Eq for i16 { .. } -impl Eq for i32 { .. } -impl Eq for i64 { .. } - -impl Eq for u8 { .. } -impl Eq for u16 { .. } -impl Eq for u32 { .. } -impl Eq for u64 { .. } - -impl Eq for () { .. } -impl Eq for bool { .. } - -impl Eq for [T; N] - where T: Eq { .. } - -impl Eq for [T] - where T: Eq { .. } - -impl Eq for (A, B) - where A: Eq, B: Eq { .. } - -impl Eq for (A, B, C) - where A: Eq, B: Eq, C: Eq { .. } - -impl Eq for (A, B, C, D) - where A: Eq, B: Eq, C: Eq, D: Eq { .. } - -impl Eq for (A, B, C, D, E) - where A: Eq, B: Eq, C: Eq, D: Eq, E: Eq { .. } -``` - -### `std::cmp::Ord` - -```rust title="ord-trait" showLineNumbers -pub trait Ord { - fn cmp(self, other: Self) -> Ordering; -} -``` -> Source code: noir_stdlib/src/cmp.nr#L210-L214 - - -`a.cmp(b)` compares two values returning `Ordering::less()` if `a < b`, -`Ordering::equal()` if `a == b`, or `Ordering::greater()` if `a > b`. -Implementing this trait on a type allows `<`, `<=`, `>`, and `>=` to be -used on values of the type. - -`std::cmp` also provides `max` and `min` functions for any type which implements the `Ord` trait. - -Implementations: - -```rust -impl Ord for u8 { .. } -impl Ord for u16 { .. } -impl Ord for u32 { .. } -impl Ord for u64 { .. } - -impl Ord for i8 { .. } -impl Ord for i16 { .. } -impl Ord for i32 { .. } - -impl Ord for i64 { .. } - -impl Ord for () { .. } -impl Ord for bool { .. } - -impl Ord for [T; N] - where T: Ord { .. } - -impl Ord for [T] - where T: Ord { .. } - -impl Ord for (A, B) - where A: Ord, B: Ord { .. } - -impl Ord for (A, B, C) - where A: Ord, B: Ord, C: Ord { .. } - -impl Ord for (A, B, C, D) - where A: Ord, B: Ord, C: Ord, D: Ord { .. } - -impl Ord for (A, B, C, D, E) - where A: Ord, B: Ord, C: Ord, D: Ord, E: Ord { .. } -``` - ---- - -## `std::ops` - -### `std::ops::Add`, `std::ops::Sub`, `std::ops::Mul`, and `std::ops::Div` - -These traits abstract over addition, subtraction, multiplication, and division respectively. -Implementing these traits for a given type will also allow that type to be used with the corresponding operator -for that trait (`+` for Add, etc) in addition to the normal method names. - -```rust title="add-trait" showLineNumbers -pub trait Add { - fn add(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/arith.nr#L1-L5 - -```rust title="sub-trait" showLineNumbers -pub trait Sub { - fn sub(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/arith.nr#L60-L64 - -```rust title="mul-trait" showLineNumbers -pub trait Mul { - fn mul(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/arith.nr#L119-L123 - -```rust title="div-trait" showLineNumbers -pub trait Div { - fn div(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/arith.nr#L178-L182 - - -The implementations block below is given for the `Add` trait, but the same types that implement -`Add` also implement `Sub`, `Mul`, and `Div`. - -Implementations: -```rust -impl Add for Field { .. } - -impl Add for i8 { .. } -impl Add for i16 { .. } -impl Add for i32 { .. } -impl Add for i64 { .. } - -impl Add for u8 { .. } -impl Add for u16 { .. } -impl Add for u32 { .. } -impl Add for u64 { .. } -``` - -### `std::ops::Rem` - -```rust title="rem-trait" showLineNumbers -pub trait Rem { - fn rem(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/arith.nr#L237-L241 - - -`Rem::rem(a, b)` is the remainder function returning the result of what is -left after dividing `a` and `b`. Implementing `Rem` allows the `%` operator -to be used with the implementation type. - -Unlike other numeric traits, `Rem` is not implemented for `Field`. - -Implementations: -```rust -impl Rem for u8 { fn rem(self, other: u8) -> u8 { self % other } } -impl Rem for u16 { fn rem(self, other: u16) -> u16 { self % other } } -impl Rem for u32 { fn rem(self, other: u32) -> u32 { self % other } } -impl Rem for u64 { fn rem(self, other: u64) -> u64 { self % other } } - -impl Rem for i8 { fn rem(self, other: i8) -> i8 { self % other } } -impl Rem for i16 { fn rem(self, other: i16) -> i16 { self % other } } -impl Rem for i32 { fn rem(self, other: i32) -> i32 { self % other } } -impl Rem for i64 { fn rem(self, other: i64) -> i64 { self % other } } -``` - -### `std::ops::Neg` - -```rust title="neg-trait" showLineNumbers -pub trait Neg { - fn neg(self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/arith.nr#L290-L294 - - -`Neg::neg` is equivalent to the unary negation operator `-`. - -Implementations: -```rust title="neg-trait-impls" showLineNumbers -impl Neg for Field { - fn neg(self) -> Field { - -self - } -} - -impl Neg for i8 { - fn neg(self) -> i8 { - -self - } -} -impl Neg for i16 { - fn neg(self) -> i16 { - -self - } -} -impl Neg for i32 { - fn neg(self) -> i32 { - -self - } -} -impl Neg for i64 { - fn neg(self) -> i64 { - -self - } -} -``` -> Source code: noir_stdlib/src/ops/arith.nr#L296-L323 - - -### `std::ops::Not` - -```rust title="not-trait" showLineNumbers -pub trait Not { - fn not(self: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/bit.nr#L1-L5 - - -`Not::not` is equivalent to the unary bitwise NOT operator `!`. - -Implementations: -```rust title="not-trait-impls" showLineNumbers -impl Not for bool { - fn not(self) -> bool { - !self - } -} - -impl Not for u64 { - fn not(self) -> u64 { - !self - } -} -impl Not for u32 { - fn not(self) -> u32 { - !self - } -} -impl Not for u16 { - fn not(self) -> u16 { - !self - } -} -impl Not for u8 { - fn not(self) -> u8 { - !self - } -} -impl Not for u1 { - fn not(self) -> u1 { - !self - } -} - -impl Not for i8 { - fn not(self) -> i8 { - !self - } -} -impl Not for i16 { - fn not(self) -> i16 { - !self - } -} -impl Not for i32 { - fn not(self) -> i32 { - !self - } -} -impl Not for i64 { - fn not(self) -> i64 { - !self - } -} -``` -> Source code: noir_stdlib/src/ops/bit.nr#L7-L60 - - -### `std::ops::{ BitOr, BitAnd, BitXor }` - -```rust title="bitor-trait" showLineNumbers -pub trait BitOr { - fn bitor(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/bit.nr#L62-L66 - -```rust title="bitand-trait" showLineNumbers -pub trait BitAnd { - fn bitand(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/bit.nr#L121-L125 - -```rust title="bitxor-trait" showLineNumbers -pub trait BitXor { - fn bitxor(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/bit.nr#L180-L184 - - -Traits for the bitwise operations `|`, `&`, and `^`. - -Implementing `BitOr`, `BitAnd` or `BitXor` for a type allows the `|`, `&`, or `^` operator respectively -to be used with the type. - -The implementations block below is given for the `BitOr` trait, but the same types that implement -`BitOr` also implement `BitAnd` and `BitXor`. - -Implementations: -```rust -impl BitOr for bool { fn bitor(self, other: bool) -> bool { self | other } } - -impl BitOr for u8 { fn bitor(self, other: u8) -> u8 { self | other } } -impl BitOr for u16 { fn bitor(self, other: u16) -> u16 { self | other } } -impl BitOr for u32 { fn bitor(self, other: u32) -> u32 { self | other } } -impl BitOr for u64 { fn bitor(self, other: u64) -> u64 { self | other } } - -impl BitOr for i8 { fn bitor(self, other: i8) -> i8 { self | other } } -impl BitOr for i16 { fn bitor(self, other: i16) -> i16 { self | other } } -impl BitOr for i32 { fn bitor(self, other: i32) -> i32 { self | other } } -impl BitOr for i64 { fn bitor(self, other: i64) -> i64 { self | other } } -``` - -### `std::ops::{ Shl, Shr }` - -```rust title="shl-trait" showLineNumbers -pub trait Shl { - fn shl(self, other: u8) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/bit.nr#L239-L243 - -```rust title="shr-trait" showLineNumbers -pub trait Shr { - fn shr(self, other: u8) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/bit.nr#L292-L296 - - -Traits for a bit shift left and bit shift right. - -Implementing `Shl` for a type allows the left shift operator (`<<`) to be used with the implementation type. -Similarly, implementing `Shr` allows the right shift operator (`>>`) to be used with the type. - -Note that bit shifting is not currently implemented for signed types. - -The implementations block below is given for the `Shl` trait, but the same types that implement -`Shl` also implement `Shr`. - -Implementations: -```rust -impl Shl for u8 { fn shl(self, other: u8) -> u8 { self << other } } -impl Shl for u16 { fn shl(self, other: u16) -> u16 { self << other } } -impl Shl for u32 { fn shl(self, other: u32) -> u32 { self << other } } -impl Shl for u64 { fn shl(self, other: u64) -> u64 { self << other } } -``` - ---- - -## `std::append` - -### `std::append::Append` - -`Append` can abstract over types that can be appended to - usually container types: - -```rust title="append-trait" showLineNumbers -pub trait Append { - fn empty() -> Self; - fn append(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/append.nr#L9-L14 - - -`Append` requires two methods: - -- `empty`: Constructs an empty value of `Self`. -- `append`: Append two values together, returning the result. - -Additionally, it is expected that for any implementation: - -- `T::empty().append(x) == x` -- `x.append(T::empty()) == x` - -Implementations: -```rust -impl Append for [T] -impl Append for Quoted -``` diff --git a/docs/versioned_docs/version-v0.38.0/reference/NoirJS/noir_js/.nojekyll b/docs/versioned_docs/version-v0.38.0/reference/NoirJS/noir_js/.nojekyll deleted file mode 100644 index e2ac6616add..00000000000 --- a/docs/versioned_docs/version-v0.38.0/reference/NoirJS/noir_js/.nojekyll +++ /dev/null @@ -1 +0,0 @@ -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.38.0/reference/NoirJS/noir_js/classes/Noir.md b/docs/versioned_docs/version-v0.38.0/reference/NoirJS/noir_js/classes/Noir.md deleted file mode 100644 index ead255bc504..00000000000 --- a/docs/versioned_docs/version-v0.38.0/reference/NoirJS/noir_js/classes/Noir.md +++ /dev/null @@ -1,52 +0,0 @@ -# Noir - -## Constructors - -### new Noir(circuit) - -```ts -new Noir(circuit): Noir -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `circuit` | `CompiledCircuit` | - -#### Returns - -[`Noir`](Noir.md) - -## Methods - -### 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) -``` - -*** - -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.38.0/reference/NoirJS/noir_js/functions/and.md b/docs/versioned_docs/version-v0.38.0/reference/NoirJS/noir_js/functions/and.md deleted file mode 100644 index c783283e396..00000000000 --- a/docs/versioned_docs/version-v0.38.0/reference/NoirJS/noir_js/functions/and.md +++ /dev/null @@ -1,22 +0,0 @@ -# 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.38.0/reference/NoirJS/noir_js/functions/blake2s256.md b/docs/versioned_docs/version-v0.38.0/reference/NoirJS/noir_js/functions/blake2s256.md deleted file mode 100644 index 7882d0da8d5..00000000000 --- a/docs/versioned_docs/version-v0.38.0/reference/NoirJS/noir_js/functions/blake2s256.md +++ /dev/null @@ -1,21 +0,0 @@ -# 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.38.0/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md b/docs/versioned_docs/version-v0.38.0/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md deleted file mode 100644 index 5e3cd53e9d3..00000000000 --- a/docs/versioned_docs/version-v0.38.0/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md +++ /dev/null @@ -1,28 +0,0 @@ -# ecdsa\_secp256k1\_verify() - -```ts -ecdsa_secp256k1_verify( - hashed_msg, - public_key_x_bytes, - public_key_y_bytes, - signature): boolean -``` - -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.38.0/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md b/docs/versioned_docs/version-v0.38.0/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md deleted file mode 100644 index 0b20ff68957..00000000000 --- a/docs/versioned_docs/version-v0.38.0/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md +++ /dev/null @@ -1,28 +0,0 @@ -# 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.38.0/reference/NoirJS/noir_js/functions/xor.md b/docs/versioned_docs/version-v0.38.0/reference/NoirJS/noir_js/functions/xor.md deleted file mode 100644 index 8d762b895d3..00000000000 --- a/docs/versioned_docs/version-v0.38.0/reference/NoirJS/noir_js/functions/xor.md +++ /dev/null @@ -1,22 +0,0 @@ -# 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.38.0/reference/NoirJS/noir_js/index.md b/docs/versioned_docs/version-v0.38.0/reference/NoirJS/noir_js/index.md deleted file mode 100644 index 4de7a696991..00000000000 --- a/docs/versioned_docs/version-v0.38.0/reference/NoirJS/noir_js/index.md +++ /dev/null @@ -1,47 +0,0 @@ -# noir_js - -## Exports - -### Classes - -| Class | Description | -| :------ | :------ | -| [Noir](classes/Noir.md) | - | - -### Type Aliases - -| Type alias | Description | -| :------ | :------ | -| [ErrorWithPayload](type-aliases/ErrorWithPayload.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) | - | -| [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) | Verifies a ECDSA signature over the secp256k1 curve. | -| [ecdsa\_secp256r1\_verify](functions/ecdsa_secp256r1_verify.md) | Verifies a ECDSA signature over the secp256r1 curve. | -| [xor](functions/xor.md) | Performs a bitwise XOR operation between `lhs` and `rhs` | - -## References - -### CompiledCircuit - -Renames and re-exports [InputMap](index.md#inputmap) - -## Variables - -### InputMap - -```ts -InputMap: any; -``` - -*** - -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.38.0/reference/NoirJS/noir_js/type-aliases/ErrorWithPayload.md b/docs/versioned_docs/version-v0.38.0/reference/NoirJS/noir_js/type-aliases/ErrorWithPayload.md deleted file mode 100644 index e8c2f4aef3d..00000000000 --- a/docs/versioned_docs/version-v0.38.0/reference/NoirJS/noir_js/type-aliases/ErrorWithPayload.md +++ /dev/null @@ -1,15 +0,0 @@ -# ErrorWithPayload - -```ts -type ErrorWithPayload: ExecutionError & object; -``` - -## Type declaration - -| Member | Type | Description | -| :------ | :------ | :------ | -| `decodedAssertionPayload` | `any` | - | - -*** - -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.38.0/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md b/docs/versioned_docs/version-v0.38.0/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md deleted file mode 100644 index 812b8b16481..00000000000 --- a/docs/versioned_docs/version-v0.38.0/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md +++ /dev/null @@ -1,24 +0,0 @@ -# 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.38.0/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md b/docs/versioned_docs/version-v0.38.0/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md deleted file mode 100644 index dd95809186a..00000000000 --- a/docs/versioned_docs/version-v0.38.0/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md +++ /dev/null @@ -1,9 +0,0 @@ -# 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.38.0/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md b/docs/versioned_docs/version-v0.38.0/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md deleted file mode 100644 index b71fb78a946..00000000000 --- a/docs/versioned_docs/version-v0.38.0/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md +++ /dev/null @@ -1,9 +0,0 @@ -# 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.38.0/reference/NoirJS/noir_js/type-aliases/WitnessMap.md b/docs/versioned_docs/version-v0.38.0/reference/NoirJS/noir_js/type-aliases/WitnessMap.md deleted file mode 100644 index 258c46f9d0c..00000000000 --- a/docs/versioned_docs/version-v0.38.0/reference/NoirJS/noir_js/type-aliases/WitnessMap.md +++ /dev/null @@ -1,9 +0,0 @@ -# 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.38.0/reference/NoirJS/noir_js/typedoc-sidebar.cjs b/docs/versioned_docs/version-v0.38.0/reference/NoirJS/noir_js/typedoc-sidebar.cjs deleted file mode 100644 index 4796b5abaa8..00000000000 --- a/docs/versioned_docs/version-v0.38.0/reference/NoirJS/noir_js/typedoc-sidebar.cjs +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-check -/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ -const typedocSidebar = { items: [{"type":"category","label":"Classes","items":[{"type":"doc","id":"reference/NoirJS/noir_js/classes/Noir","label":"Noir"}]},{"type":"category","label":"Type Aliases","items":[{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ErrorWithPayload","label":"ErrorWithPayload"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ForeignCallHandler","label":"ForeignCallHandler"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ForeignCallInput","label":"ForeignCallInput"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ForeignCallOutput","label":"ForeignCallOutput"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/WitnessMap","label":"WitnessMap"}]},{"type":"category","label":"Functions","items":[{"type":"doc","id":"reference/NoirJS/noir_js/functions/and","label":"and"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/blake2s256","label":"blake2s256"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify","label":"ecdsa_secp256k1_verify"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify","label":"ecdsa_secp256r1_verify"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/xor","label":"xor"}]}]}; -module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.38.0/reference/NoirJS/noir_wasm/.nojekyll b/docs/versioned_docs/version-v0.38.0/reference/NoirJS/noir_wasm/.nojekyll deleted file mode 100644 index e2ac6616add..00000000000 --- a/docs/versioned_docs/version-v0.38.0/reference/NoirJS/noir_wasm/.nojekyll +++ /dev/null @@ -1 +0,0 @@ -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.38.0/reference/NoirJS/noir_wasm/functions/compile.md b/docs/versioned_docs/version-v0.38.0/reference/NoirJS/noir_wasm/functions/compile.md deleted file mode 100644 index 6faf763b37f..00000000000 --- a/docs/versioned_docs/version-v0.38.0/reference/NoirJS/noir_wasm/functions/compile.md +++ /dev/null @@ -1,51 +0,0 @@ -# compile() - -```ts -compile( - fileManager, - projectPath?, - logFn?, -debugLogFn?): Promise -``` - -Compiles a Noir project - -## Parameters - -| Parameter | Type | Description | -| :------ | :------ | :------ | -| `fileManager` | `FileManager` | The file manager to use | -| `projectPath`? | `string` | The path to the project inside the file manager. Defaults to the root of the file manager | -| `logFn`? | `LogFn` | A logging function. If not provided, console.log will be used | -| `debugLogFn`? | `LogFn` | A debug logging function. If not provided, logFn will be used | - -## Returns - -`Promise`\<[`ProgramCompilationArtifacts`](../index.md#programcompilationartifacts)\> - -## Example - -```typescript -// Node.js - -import { compile_program, createFileManager } from '@noir-lang/noir_wasm'; - -const fm = createFileManager(myProjectPath); -const myCompiledCode = await compile_program(fm); -``` - -```typescript -// Browser - -import { compile_program, createFileManager } from '@noir-lang/noir_wasm'; - -const fm = createFileManager('/'); -for (const path of files) { - await fm.writeFile(path, await getFileAsStream(path)); -} -const myCompiledCode = await compile_program(fm); -``` - -*** - -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.38.0/reference/NoirJS/noir_wasm/functions/compile_contract.md b/docs/versioned_docs/version-v0.38.0/reference/NoirJS/noir_wasm/functions/compile_contract.md deleted file mode 100644 index 7d0b39a43ef..00000000000 --- a/docs/versioned_docs/version-v0.38.0/reference/NoirJS/noir_wasm/functions/compile_contract.md +++ /dev/null @@ -1,51 +0,0 @@ -# compile\_contract() - -```ts -compile_contract( - fileManager, - projectPath?, - logFn?, -debugLogFn?): Promise -``` - -Compiles a Noir project - -## Parameters - -| Parameter | Type | Description | -| :------ | :------ | :------ | -| `fileManager` | `FileManager` | The file manager to use | -| `projectPath`? | `string` | The path to the project inside the file manager. Defaults to the root of the file manager | -| `logFn`? | `LogFn` | A logging function. If not provided, console.log will be used | -| `debugLogFn`? | `LogFn` | A debug logging function. If not provided, logFn will be used | - -## Returns - -`Promise`\<[`ContractCompilationArtifacts`](../index.md#contractcompilationartifacts)\> - -## Example - -```typescript -// Node.js - -import { compile_contract, createFileManager } from '@noir-lang/noir_wasm'; - -const fm = createFileManager(myProjectPath); -const myCompiledCode = await compile_contract(fm); -``` - -```typescript -// Browser - -import { compile_contract, createFileManager } from '@noir-lang/noir_wasm'; - -const fm = createFileManager('/'); -for (const path of files) { - await fm.writeFile(path, await getFileAsStream(path)); -} -const myCompiledCode = await compile_contract(fm); -``` - -*** - -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.38.0/reference/NoirJS/noir_wasm/functions/createFileManager.md b/docs/versioned_docs/version-v0.38.0/reference/NoirJS/noir_wasm/functions/createFileManager.md deleted file mode 100644 index 7e65c1d69c7..00000000000 --- a/docs/versioned_docs/version-v0.38.0/reference/NoirJS/noir_wasm/functions/createFileManager.md +++ /dev/null @@ -1,21 +0,0 @@ -# createFileManager() - -```ts -createFileManager(dataDir): FileManager -``` - -Creates a new FileManager instance based on fs in node and memfs in the browser (via webpack alias) - -## Parameters - -| Parameter | Type | Description | -| :------ | :------ | :------ | -| `dataDir` | `string` | root of the file system | - -## Returns - -`FileManager` - -*** - -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.38.0/reference/NoirJS/noir_wasm/functions/inflateDebugSymbols.md b/docs/versioned_docs/version-v0.38.0/reference/NoirJS/noir_wasm/functions/inflateDebugSymbols.md deleted file mode 100644 index fcea9275341..00000000000 --- a/docs/versioned_docs/version-v0.38.0/reference/NoirJS/noir_wasm/functions/inflateDebugSymbols.md +++ /dev/null @@ -1,21 +0,0 @@ -# inflateDebugSymbols() - -```ts -inflateDebugSymbols(debugSymbols): any -``` - -Decompresses and decodes the debug symbols - -## Parameters - -| Parameter | Type | Description | -| :------ | :------ | :------ | -| `debugSymbols` | `string` | The base64 encoded debug symbols | - -## Returns - -`any` - -*** - -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.38.0/reference/NoirJS/noir_wasm/index.md b/docs/versioned_docs/version-v0.38.0/reference/NoirJS/noir_wasm/index.md deleted file mode 100644 index b6e0f9d1bc0..00000000000 --- a/docs/versioned_docs/version-v0.38.0/reference/NoirJS/noir_wasm/index.md +++ /dev/null @@ -1,49 +0,0 @@ -# noir_wasm - -## Exports - -### Functions - -| Function | Description | -| :------ | :------ | -| [compile](functions/compile.md) | Compiles a Noir project | -| [compile\_contract](functions/compile_contract.md) | Compiles a Noir project | -| [createFileManager](functions/createFileManager.md) | Creates a new FileManager instance based on fs in node and memfs in the browser (via webpack alias) | -| [inflateDebugSymbols](functions/inflateDebugSymbols.md) | Decompresses and decodes the debug symbols | - -## References - -### compile\_program - -Renames and re-exports [compile](functions/compile.md) - -## Interfaces - -### ContractCompilationArtifacts - -The compilation artifacts of a given contract. - -#### Properties - -| Property | Type | Description | -| :------ | :------ | :------ | -| `contract` | `ContractArtifact` | The compiled contract. | -| `warnings` | `unknown`[] | Compilation warnings. | - -*** - -### ProgramCompilationArtifacts - -The compilation artifacts of a given program. - -#### Properties - -| Property | Type | Description | -| :------ | :------ | :------ | -| `name` | `string` | not part of the compilation output, injected later | -| `program` | `ProgramArtifact` | The compiled contract. | -| `warnings` | `unknown`[] | Compilation warnings. | - -*** - -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.38.0/reference/NoirJS/noir_wasm/typedoc-sidebar.cjs b/docs/versioned_docs/version-v0.38.0/reference/NoirJS/noir_wasm/typedoc-sidebar.cjs deleted file mode 100644 index e0870710349..00000000000 --- a/docs/versioned_docs/version-v0.38.0/reference/NoirJS/noir_wasm/typedoc-sidebar.cjs +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-check -/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ -const typedocSidebar = { items: [{"type":"doc","id":"reference/NoirJS/noir_wasm/index","label":"API"},{"type":"category","label":"Functions","items":[{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/compile","label":"compile"},{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/compile_contract","label":"compile_contract"},{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/createFileManager","label":"createFileManager"},{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/inflateDebugSymbols","label":"inflateDebugSymbols"}]}]}; -module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.38.0/reference/_category_.json b/docs/versioned_docs/version-v0.38.0/reference/_category_.json deleted file mode 100644 index 5b6a20a609a..00000000000 --- a/docs/versioned_docs/version-v0.38.0/reference/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "position": 4, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.38.0/reference/debugger/_category_.json b/docs/versioned_docs/version-v0.38.0/reference/debugger/_category_.json deleted file mode 100644 index 27869205ad3..00000000000 --- a/docs/versioned_docs/version-v0.38.0/reference/debugger/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "Debugger", - "position": 1, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.38.0/reference/debugger/debugger_known_limitations.md b/docs/versioned_docs/version-v0.38.0/reference/debugger/debugger_known_limitations.md deleted file mode 100644 index 936d416ac4b..00000000000 --- a/docs/versioned_docs/version-v0.38.0/reference/debugger/debugger_known_limitations.md +++ /dev/null @@ -1,59 +0,0 @@ ---- -title: Known limitations -description: - An overview of known limitations of the current version of the Noir debugger -keywords: - [ - Nargo, - Noir Debugger, - VS Code, - ] -sidebar_position: 2 ---- - -# Debugger Known Limitations - -There are currently some limits to what the debugger can observe. - -## Mutable references - -The debugger is currently blind to any state mutated via a mutable reference. For example, in: - -``` -let mut x = 1; -let y = &mut x; -*y = 2; -``` - -The update on `x` will not be observed by the debugger. That means, when running `vars` from the debugger REPL, or inspecting the _local variables_ pane in the VS Code debugger, `x` will appear with value 1 despite having executed `*y = 2;`. - -## Variables of type function or mutable references are opaque - -When inspecting variables, any variable of type `Function` or `MutableReference` will render its value as `<>` or `<>`. - -## Debugger instrumentation affects resulting ACIR - -In order to make the state of local variables observable, the debugger compiles Noir circuits interleaving foreign calls that track any mutations to them. While this works (except in the cases described above) and doesn't introduce any behavior changes, it does as a side effect produce bigger bytecode. In particular, when running the command `opcodes` on the REPL debugger, you will notice Unconstrained VM blocks that look like this: - -``` -... -5 BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [], q_c: 2 }), Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(2))], q_c: 0 })] - | outputs=[] - 5.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } - 5.1 | Mov { destination: RegisterIndex(3), source: RegisterIndex(1) } - 5.2 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } - 5.3 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } - 5.4 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } - 5.5 | Mov { destination: RegisterIndex(3), source: RegisterIndex(3) } - 5.6 | Call { location: 8 } - 5.7 | Stop - 5.8 | ForeignCall { function: "__debug_var_assign", destinations: [], inputs: [RegisterIndex(RegisterIndex(2)), RegisterIndex(RegisterIndex(3))] } -... -``` - -If you are interested in debugging/inspecting compiled ACIR without these synthetic changes, you can invoke the REPL debugger with the `--skip-instrumentation` flag or launch the VS Code debugger with the `skipConfiguration` property set to true in its launch configuration. You can find more details about those in the [Debugger REPL reference](debugger_repl.md) and the [VS Code Debugger reference](debugger_vscode.md). - -:::note -Skipping debugger instrumentation means you won't be able to inspect values of local variables. -::: - diff --git a/docs/versioned_docs/version-v0.38.0/reference/debugger/debugger_repl.md b/docs/versioned_docs/version-v0.38.0/reference/debugger/debugger_repl.md deleted file mode 100644 index 46e2011304e..00000000000 --- a/docs/versioned_docs/version-v0.38.0/reference/debugger/debugger_repl.md +++ /dev/null @@ -1,360 +0,0 @@ ---- -title: REPL Debugger -description: - Noir Debugger REPL options and commands. -keywords: - [ - Nargo, - Noir CLI, - Noir Debugger, - REPL, - ] -sidebar_position: 1 ---- - -## Running the REPL debugger - -`nargo debug [OPTIONS] [WITNESS_NAME]` - -Runs the Noir REPL debugger. If a `WITNESS_NAME` is provided the debugger writes the resulting execution witness to a `WITNESS_NAME` 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 debug | -| `--print-acir` | Display the ACIR for compiled circuit | -| `--deny-warnings` | Treat all warnings as errors | -| `--silence-warnings` | Suppress warnings | -| `-h, --help` | Print help | - -None of these options are required. - -:::note -Since the debugger starts by compiling the target package, all Noir compiler options are also available. Check out the [compiler reference](../nargo_commands.md#nargo-compile) to learn more about the compiler options. -::: - -## REPL commands - -Once the debugger is running, it accepts the following commands. - -#### `help` (h) - -Displays the menu of available commands. - -``` -> help -Available commands: - - opcodes display ACIR opcodes - into step into to the next opcode - next step until a new source location is reached - out step until a new source location is reached - and the current stack frame is finished - break LOCATION:OpcodeLocation add a breakpoint at an opcode location - over step until a new source location is reached - without diving into function calls - restart restart the debugging session - delete LOCATION:OpcodeLocation delete breakpoint at an opcode location - witness show witness map - witness index:u32 display a single witness from the witness map - witness index:u32 value:String update a witness with the given value - memset index:usize value:String update a memory cell with the given - value - continue continue execution until the end of the - program - vars show variable values available at this point - in execution - stacktrace display the current stack trace - memory show memory (valid when executing unconstrained code) value - step step to the next ACIR opcode - -Other commands: - - help Show this help message - quit Quit repl - -``` - -### Stepping through programs - -#### `next` (n) - -Step until the next Noir source code location. While other commands, such as [`into`](#into-i) and [`step`](#step-s), allow for finer grained control of the program's execution at the opcode level, `next` is source code centric. For example: - -``` -3 ... -4 fn main(x: u32) { -5 assert(entry_point(x) == 2); -6 swap_entry_point(x, x + 1); -7 -> assert(deep_entry_point(x) == 4); -8 multiple_values_entry_point(x); -9 } -``` - - -Using `next` here would cause the debugger to jump to the definition of `deep_entry_point` (if available). - -If you want to step over `deep_entry_point` and go straight to line 8, use [the `over` command](#over) instead. - -#### `over` - -Step until the next source code location, without diving into function calls. For example: - -``` -3 ... -4 fn main(x: u32) { -5 assert(entry_point(x) == 2); -6 swap_entry_point(x, x + 1); -7 -> assert(deep_entry_point(x) == 4); -8 multiple_values_entry_point(x); -9 } -``` - - -Using `over` here would cause the debugger to execute until line 8 (`multiple_values_entry_point(x);`). - -If you want to step into `deep_entry_point` instead, use [the `next` command](#next-n). - -#### `out` - -Step until the end of the current function call. For example: - -``` - 3 ... - 4 fn main(x: u32) { - 5 assert(entry_point(x) == 2); - 6 swap_entry_point(x, x + 1); - 7 -> assert(deep_entry_point(x) == 4); - 8 multiple_values_entry_point(x); - 9 } - 10 - 11 unconstrained fn returns_multiple_values(x: u32) -> (u32, u32, u32, u32) { - 12 ... - ... - 55 - 56 unconstrained fn deep_entry_point(x: u32) -> u32 { - 57 -> level_1(x + 1) - 58 } - -``` - -Running `out` here will resume execution until line 8. - -#### `step` (s) - -Skips to the next ACIR code. A compiled Noir program is a sequence of ACIR opcodes. However, an unconstrained VM opcode denotes the start of an unconstrained code block, to be executed by the unconstrained VM. For example (redacted for brevity): - -``` -0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] -1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] - 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } - 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } - 1.2 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } - 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } - 1.4 | Call { location: 7 } - ... - 1.43 | Return -2 EXPR [ (1, _1) -2 ] -``` - -The `->` here shows the debugger paused at an ACIR opcode: `BRILLIG`, at index 1, which denotes an unconstrained code block is about to start. - -Using the `step` command at this point would result in the debugger stopping at ACIR opcode 2, `EXPR`, skipping unconstrained computation steps. - -Use [the `into` command](#into-i) instead if you want to follow unconstrained computation step by step. - -#### `into` (i) - -Steps into the next opcode. A compiled Noir program is a sequence of ACIR opcodes. However, a BRILLIG opcode denotes the start of an unconstrained code block, to be executed by the unconstrained VM. For example (redacted for brevity): - -``` -0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] -1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] - 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } - 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } - 1.2 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } - 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } - 1.4 | Call { location: 7 } - ... - 1.43 | Return -2 EXPR [ (1, _1) -2 ] -``` - -The `->` here shows the debugger paused at an ACIR opcode: `BRILLIG`, at index 1, which denotes an unconstrained code block is about to start. - -Using the `into` command at this point would result in the debugger stopping at opcode 1.0, `Mov ...`, allowing the debugger user to follow unconstrained computation step by step. - -Use [the `step` command](#step-s) instead if you want to skip to the next ACIR code directly. - -#### `continue` (c) - -Continues execution until the next breakpoint, or the end of the program. - -#### `restart` (res) - -Interrupts execution, and restarts a new debugging session from scratch. - -#### `opcodes` (o) - -Display the program's ACIR opcode sequence. For example: - -``` -0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] -1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] - 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } - 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } - 1.2 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } - 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } - 1.4 | Call { location: 7 } - ... - 1.43 | Return -2 EXPR [ (1, _1) -2 ] -``` - -### Breakpoints - -#### `break [Opcode]` (or shorthand `b [Opcode]`) - -Sets a breakpoint on the specified opcode index. To get a list of the program opcode numbers, see [the `opcode` command](#opcodes-o). For example: - -``` -0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] -1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] - 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } - 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } - 1.2 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } - 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } - 1.4 | Call { location: 7 } - ... - 1.43 | Return -2 EXPR [ (1, _1) -2 ] -``` - -In this example, issuing a `break 1.2` command adds break on opcode 1.2, as denoted by the `*` character: - -``` -0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] -1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] - 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } - 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } - 1.2 | * Const { destination: RegisterIndex(1), value: Value { inner: 0 } } - 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } - 1.4 | Call { location: 7 } - ... - 1.43 | Return -2 EXPR [ (1, _1) -2 ] -``` - -Running [the `continue` command](#continue-c) at this point would cause the debugger to execute the program until opcode 1.2. - -#### `delete [Opcode]` (or shorthand `d [Opcode]`) - -Deletes a breakpoint at an opcode location. Usage is analogous to [the `break` command](#). - -### Variable inspection - -#### vars - -Show variable values available at this point in execution. - -:::note -The ability to inspect variable values from the debugger depends on compilation to be run in a special debug instrumentation mode. This instrumentation weaves variable tracing code with the original source code. - -So variable value inspection comes at the expense of making the resulting ACIR bytecode bigger and harder to understand and optimize. - -If you find this compromise unacceptable, you can run the debugger with the flag `--skip-debug-instrumentation`. This will compile your circuit without any additional debug information, so the resulting ACIR bytecode will be identical to the one produced by standard Noir compilation. However, if you opt for this, the `vars` command will not be available while debugging. -::: - - -### Stacktrace - -#### `stacktrace` - -Displays the current stack trace. - - -### Witness map - -#### `witness` (w) - -Show witness map. For example: - -``` -_0 = 0 -_1 = 2 -_2 = 1 -``` - -#### `witness [Witness Index]` - -Display a single witness from the witness map. For example: - -``` -> witness 1 -_1 = 2 -``` - -#### `witness [Witness Index] [New value]` - -Overwrite the given index with a new value. For example: - -``` -> witness 1 3 -_1 = 3 -``` - - -### Unconstrained VM memory - -#### `memory` - -Show unconstrained VM memory state. For example: - -``` -> memory -At opcode 1.13: Store { destination_pointer: RegisterIndex(0), source: RegisterIndex(3) } -... -> registers -0 = 0 -1 = 10 -2 = 0 -3 = 1 -4 = 1 -5 = 2³² -6 = 1 -> into -At opcode 1.14: Const { destination: RegisterIndex(5), value: Value { inner: 1 } } -... -> memory -0 = 1 -> -``` - -In the example above: we start with clean memory, then step through a `Store` opcode which stores the value of register 3 (1) into the memory address stored in register 0 (0). Thus now `memory` shows memory address 0 contains value 1. - -:::note -This command is only functional while the debugger is executing unconstrained code. -::: - -#### `memset [Memory address] [New value]` - -Update a memory cell with the given value. For example: - -``` -> memory -0 = 1 -> memset 0 2 -> memory -0 = 2 -> memset 1 4 -> memory -0 = 2 -1 = 4 -> -``` - -:::note -This command is only functional while the debugger is executing unconstrained code. -::: \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.38.0/reference/debugger/debugger_vscode.md b/docs/versioned_docs/version-v0.38.0/reference/debugger/debugger_vscode.md deleted file mode 100644 index c027332b3b0..00000000000 --- a/docs/versioned_docs/version-v0.38.0/reference/debugger/debugger_vscode.md +++ /dev/null @@ -1,82 +0,0 @@ ---- -title: VS Code Debugger -description: - VS Code Debugger configuration and features. -keywords: - [ - Nargo, - Noir CLI, - Noir Debugger, - VS Code, - IDE, - ] -sidebar_position: 0 ---- - -# VS Code Noir Debugger Reference - -The Noir debugger enabled by the vscode-noir extension ships with default settings such that the most common scenario should run without any additional configuration steps. - -These defaults can nevertheless be overridden by defining a launch configuration file. This page provides a reference for the properties you can override via a launch configuration file, as well as documenting the Nargo `dap` command, which is a dependency of the VS Code Noir debugger. - - -## Creating and editing launch configuration files - -To create a launch configuration file from VS Code, open the _debug pane_, and click on _create a launch.json file_. - -![Creating a launch configuration file](@site/static/img/debugger/ref1-create-launch.png) - -A `launch.json` file will be created, populated with basic defaults. - -### Noir Debugger launch.json properties - -#### projectFolder - -_String, optional._ - -Absolute path to the Nargo project to debug. By default, it is dynamically determined by looking for the nearest `Nargo.toml` file to the active file at the moment of launching the debugger. - -#### proverName - -_String, optional._ - -Name of the prover input to use. Defaults to `Prover`, which looks for a file named `Prover.toml` at the `projectFolder`. - -#### generateAcir - -_Boolean, optional._ - -If true, generate ACIR opcodes instead of unconstrained opcodes which will be closer to release binaries but less convenient for debugging. Defaults to `false`. - -#### skipInstrumentation - -_Boolean, optional._ - -Skips variables debugging instrumentation of code, making debugging less convenient but the resulting binary smaller and closer to production. Defaults to `false`. - -:::note -Skipping instrumentation causes the debugger to be unable to inspect local variables. -::: - -## `nargo dap [OPTIONS]` - -When run without any option flags, it starts the Nargo Debug Adapter Protocol server, which acts as the debugging backend for the VS Code Noir Debugger. - -All option flags are related to preflight checks. The Debug Adapter Protocol specifies how errors are to be informed from a running DAP server, but it doesn't specify mechanisms to communicate server initialization errors between the DAP server and its client IDE. - -Thus `nargo dap` ships with a _preflight check_ mode. If flag `--preflight-check` and the rest of the `--preflight-*` flags are provided, Nargo will run the same initialization routine except it will not start the DAP server. - -`vscode-noir` will then run `nargo dap` in preflight check mode first before a debugging session starts. If the preflight check ends in error, vscode-noir will present stderr and stdout output from this process through its own Output pane in VS Code. This makes it possible for users to diagnose what pieces of configuration might be wrong or missing in case of initialization errors. - -If the preflight check succeeds, `vscode-noir` proceeds to start the DAP server normally but running `nargo dap` without any additional flags. - -### Options - -| Option | Description | -| --------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | -| `--preflight-check` | If present, dap runs in preflight check mode. | -| `--preflight-project-folder ` | Absolute path to the project to debug for preflight check. | -| `--preflight-prover-name ` | Name of prover file to use for preflight check | -| `--preflight-generate-acir` | Optional. If present, compile in ACIR mode while running preflight check. | -| `--preflight-skip-instrumentation` | Optional. If present, compile without introducing debug instrumentation while running preflight check. | -| `-h, --help` | Print help. | diff --git a/docs/versioned_docs/version-v0.38.0/reference/nargo_commands.md b/docs/versioned_docs/version-v0.38.0/reference/nargo_commands.md deleted file mode 100644 index 8842fad6647..00000000000 --- a/docs/versioned_docs/version-v0.38.0/reference/nargo_commands.md +++ /dev/null @@ -1,474 +0,0 @@ ---- -title: Nargo -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, - ] -sidebar_position: 0 ---- - -# Command-Line Help for `nargo` - -This document contains the help content for the `nargo` command-line program. - -**Command Overview:** - -* [`nargo`↴](#nargo) -* [`nargo check`↴](#nargo-check) -* [`nargo fmt`↴](#nargo-fmt) -* [`nargo compile`↴](#nargo-compile) -* [`nargo new`↴](#nargo-new) -* [`nargo init`↴](#nargo-init) -* [`nargo execute`↴](#nargo-execute) -* [`nargo debug`↴](#nargo-debug) -* [`nargo test`↴](#nargo-test) -* [`nargo info`↴](#nargo-info) -* [`nargo lsp`↴](#nargo-lsp) -* [`nargo generate-completion-script`↴](#nargo-generate-completion-script) - -## `nargo` - -Noir's package manager - -**Usage:** `nargo ` - -###### **Subcommands:** - -* `check` — Checks the constraint system for errors -* `fmt` — Format the Noir files in a workspace -* `compile` — Compile the program and its secret execution trace into ACIR format -* `new` — Create a Noir project in a new directory -* `init` — Create a Noir project in the current directory -* `execute` — Executes a circuit to calculate its return value -* `debug` — Executes a circuit in debug mode -* `test` — Run the tests for this program -* `info` — Provides detailed information on each of a program's function (represented by a single circuit) -* `lsp` — Starts the Noir LSP server -* `generate-completion-script` — Generates a shell completion script for your favorite shell - -###### **Options:** - - - - -## `nargo check` - -Checks the constraint system for errors - -**Usage:** `nargo check [OPTIONS]` - -###### **Options:** - -* `--package ` — The name of the package to check -* `--workspace` — Check all packages in the workspace - - Possible values: `true`, `false` - -* `--overwrite` — Force overwrite of existing files - - Possible values: `true`, `false` - -* `--expression-width ` — Specify the backend expression width that should be targeted -* `--bounded-codegen` — Generate ACIR with the target backend expression width. The default is to generate ACIR without a bound and split expressions after code generation. Activating this flag can sometimes provide optimizations for certain programs - - Default value: `false` - - Possible values: `true`, `false` - -* `--force` — Force a full recompilation - - Possible values: `true`, `false` - -* `--print-acir` — Display the ACIR for compiled circuit - - Possible values: `true`, `false` - -* `--deny-warnings` — Treat all warnings as errors - - Possible values: `true`, `false` - -* `--silence-warnings` — Suppress warnings - - Possible values: `true`, `false` - -* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" -* `--skip-underconstrained-check` — Flag to turn off the compiler check for under constrained values. Warning: This can improve compilation speed but can also lead to correctness errors. This check should always be run on production code - - Possible values: `true`, `false` - - - - -## `nargo fmt` - -Format the Noir files in a workspace - -**Usage:** `nargo fmt [OPTIONS]` - -###### **Options:** - -* `--check` — Run noirfmt in check mode - - Possible values: `true`, `false` - - - - -## `nargo compile` - -Compile the program and its secret execution trace into ACIR format - -**Usage:** `nargo compile [OPTIONS]` - -###### **Options:** - -* `--package ` — The name of the package to compile -* `--workspace` — Compile all packages in the workspace - - Possible values: `true`, `false` - -* `--expression-width ` — Specify the backend expression width that should be targeted -* `--bounded-codegen` — Generate ACIR with the target backend expression width. The default is to generate ACIR without a bound and split expressions after code generation. Activating this flag can sometimes provide optimizations for certain programs - - Default value: `false` - - Possible values: `true`, `false` - -* `--force` — Force a full recompilation - - Possible values: `true`, `false` - -* `--print-acir` — Display the ACIR for compiled circuit - - Possible values: `true`, `false` - -* `--deny-warnings` — Treat all warnings as errors - - Possible values: `true`, `false` - -* `--silence-warnings` — Suppress warnings - - Possible values: `true`, `false` - -* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" -* `--skip-underconstrained-check` — Flag to turn off the compiler check for under constrained values. Warning: This can improve compilation speed but can also lead to correctness errors. This check should always be run on production code - - Possible values: `true`, `false` - - - - -## `nargo new` - -Create a Noir project in a new directory - -**Usage:** `nargo new [OPTIONS] ` - -###### **Arguments:** - -* `` — The path to save the new project - -###### **Options:** - -* `--name ` — Name of the package [default: package directory name] -* `--lib` — Use a library template - - Possible values: `true`, `false` - -* `--bin` — Use a binary template [default] - - Possible values: `true`, `false` - -* `--contract` — Use a contract template - - Possible values: `true`, `false` - - - - -## `nargo init` - -Create a Noir project in the current directory - -**Usage:** `nargo init [OPTIONS]` - -###### **Options:** - -* `--name ` — Name of the package [default: current directory name] -* `--lib` — Use a library template - - Possible values: `true`, `false` - -* `--bin` — Use a binary template [default] - - Possible values: `true`, `false` - -* `--contract` — Use a contract template - - Possible values: `true`, `false` - - - - -## `nargo execute` - -Executes a circuit to calculate its return value - -**Usage:** `nargo execute [OPTIONS] [WITNESS_NAME]` - -###### **Arguments:** - -* `` — Write the execution witness to named file - -Defaults to the name of the package being executed. - -###### **Options:** - -* `-p`, `--prover-name ` — The name of the toml file which contains the inputs for the prover - - Default value: `Prover` -* `--package ` — The name of the package to execute -* `--workspace` — Execute all packages in the workspace - - Possible values: `true`, `false` - -* `--expression-width ` — Specify the backend expression width that should be targeted -* `--bounded-codegen` — Generate ACIR with the target backend expression width. The default is to generate ACIR without a bound and split expressions after code generation. Activating this flag can sometimes provide optimizations for certain programs - - Default value: `false` - - Possible values: `true`, `false` - -* `--force` — Force a full recompilation - - Possible values: `true`, `false` - -* `--print-acir` — Display the ACIR for compiled circuit - - Possible values: `true`, `false` - -* `--deny-warnings` — Treat all warnings as errors - - Possible values: `true`, `false` - -* `--silence-warnings` — Suppress warnings - - Possible values: `true`, `false` - -* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" -* `--skip-underconstrained-check` — Flag to turn off the compiler check for under constrained values. Warning: This can improve compilation speed but can also lead to correctness errors. This check should always be run on production code - - Possible values: `true`, `false` - -* `--oracle-resolver ` — JSON RPC url to solve oracle calls - - - -## `nargo debug` - -Executes a circuit in debug mode - -**Usage:** `nargo debug [OPTIONS] [WITNESS_NAME]` - -###### **Arguments:** - -* `` — Write the execution witness to named file - -###### **Options:** - -* `-p`, `--prover-name ` — The name of the toml file which contains the inputs for the prover - - Default value: `Prover` -* `--package ` — The name of the package to execute -* `--expression-width ` — Specify the backend expression width that should be targeted -* `--bounded-codegen` — Generate ACIR with the target backend expression width. The default is to generate ACIR without a bound and split expressions after code generation. Activating this flag can sometimes provide optimizations for certain programs - - Default value: `false` - - Possible values: `true`, `false` - -* `--force` — Force a full recompilation - - Possible values: `true`, `false` - -* `--print-acir` — Display the ACIR for compiled circuit - - Possible values: `true`, `false` - -* `--deny-warnings` — Treat all warnings as errors - - Possible values: `true`, `false` - -* `--silence-warnings` — Suppress warnings - - Possible values: `true`, `false` - -* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" -* `--skip-underconstrained-check` — Flag to turn off the compiler check for under constrained values. Warning: This can improve compilation speed but can also lead to correctness errors. This check should always be run on production code - - Possible values: `true`, `false` - -* `--acir-mode` — Force ACIR output (disabling instrumentation) - - Possible values: `true`, `false` - -* `--skip-instrumentation ` — Disable vars debug instrumentation (enabled by default) - - Possible values: `true`, `false` - - - - -## `nargo test` - -Run the tests for this program - -**Usage:** `nargo test [OPTIONS] [TEST_NAME]` - -###### **Arguments:** - -* `` — If given, only tests with names containing this string will be run - -###### **Options:** - -* `--show-output` — Display output of `println` statements - - Possible values: `true`, `false` - -* `--exact` — Only run tests that match exactly - - Possible values: `true`, `false` - -* `--package ` — The name of the package to test -* `--workspace` — Test all packages in the workspace - - Possible values: `true`, `false` - -* `--expression-width ` — Specify the backend expression width that should be targeted -* `--bounded-codegen` — Generate ACIR with the target backend expression width. The default is to generate ACIR without a bound and split expressions after code generation. Activating this flag can sometimes provide optimizations for certain programs - - Default value: `false` - - Possible values: `true`, `false` - -* `--force` — Force a full recompilation - - Possible values: `true`, `false` - -* `--print-acir` — Display the ACIR for compiled circuit - - Possible values: `true`, `false` - -* `--deny-warnings` — Treat all warnings as errors - - Possible values: `true`, `false` - -* `--silence-warnings` — Suppress warnings - - Possible values: `true`, `false` - -* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" -* `--skip-underconstrained-check` — Flag to turn off the compiler check for under constrained values. Warning: This can improve compilation speed but can also lead to correctness errors. This check should always be run on production code - - Possible values: `true`, `false` - -* `--oracle-resolver ` — JSON RPC url to solve oracle calls - - - -## `nargo info` - -Provides detailed information on each of a program's function (represented by a single circuit) - -Current information provided per circuit: 1. The number of ACIR opcodes 2. Counts the final number gates in the circuit used by a backend - -**Usage:** `nargo info [OPTIONS]` - -###### **Options:** - -* `--package ` — The name of the package to detail -* `--workspace` — Detail all packages in the workspace - - Possible values: `true`, `false` - -* `--profile-execution` - - Possible values: `true`, `false` - -* `-p`, `--prover-name ` — The name of the toml file which contains the inputs for the prover - - Default value: `Prover` -* `--expression-width ` — Specify the backend expression width that should be targeted -* `--bounded-codegen` — Generate ACIR with the target backend expression width. The default is to generate ACIR without a bound and split expressions after code generation. Activating this flag can sometimes provide optimizations for certain programs - - Default value: `false` - - Possible values: `true`, `false` - -* `--force` — Force a full recompilation - - Possible values: `true`, `false` - -* `--print-acir` — Display the ACIR for compiled circuit - - Possible values: `true`, `false` - -* `--deny-warnings` — Treat all warnings as errors - - Possible values: `true`, `false` - -* `--silence-warnings` — Suppress warnings - - Possible values: `true`, `false` - -* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" -* `--skip-underconstrained-check` — Flag to turn off the compiler check for under constrained values. Warning: This can improve compilation speed but can also lead to correctness errors. This check should always be run on production code - - Possible values: `true`, `false` - - - - -## `nargo lsp` - -Starts the Noir LSP server - -Starts an LSP server which allows IDEs such as VS Code to display diagnostics in Noir source. - -VS Code Noir Language Support: https://marketplace.visualstudio.com/items?itemName=noir-lang.vscode-noir - -**Usage:** `nargo lsp` - - - -## `nargo generate-completion-script` - -Generates a shell completion script for your favorite shell - -**Usage:** `nargo generate-completion-script ` - -###### **Arguments:** - -* `` — The shell to generate completions for. One of: bash, elvish, fish, powershell, zsh - - - -
- - - This document was generated automatically by - clap-markdown. - - diff --git a/docs/versioned_docs/version-v0.38.0/reference/noir_codegen.md b/docs/versioned_docs/version-v0.38.0/reference/noir_codegen.md deleted file mode 100644 index e4c362f9610..00000000000 --- a/docs/versioned_docs/version-v0.38.0/reference/noir_codegen.md +++ /dev/null @@ -1,116 +0,0 @@ ---- -title: Noir Codegen for TypeScript -description: Learn how to use Noir codegen to generate TypeScript bindings -keywords: [Nargo, Noir, compile, TypeScript] -sidebar_position: 3 ---- - -When using TypeScript, it is extra work to interpret Noir program outputs in a type-safe way. Third party libraries may exist for popular Noir programs, but they are either hard to find or unmaintained. - -Now you can generate TypeScript bindings for your Noir programs in two steps: - -1. Exporting Noir functions using `nargo export` -2. Using the TypeScript module `noir_codegen` to generate TypeScript binding - -**Note:** you can only export functions from a Noir *library* (not binary or contract program types). - -## Installation - -### Your TypeScript project - -If you don't already have a TypeScript project you can add the module with `yarn` (or `npm`), then initialize it: - -```bash -yarn add typescript -D -npx tsc --init -``` - -### Add TypeScript module - `noir_codegen` - -The following command will add the module to your project's devDependencies: - -```bash -yarn add @noir-lang/noir_codegen -D -``` - -### Nargo library - -Make sure you have Nargo, v0.25.0 or greater, installed. If you don't, follow the [installation guide](../getting_started/noir_installation.md). - -If you're in a new project, make a `circuits` folder and create a new Noir library: - -```bash -mkdir circuits && cd circuits -nargo new --lib myNoirLib -``` - -## Usage - -### Export ABI of specified functions - -First go to the `.nr` files in your Noir library, and add the `#[export]` macro to each function that you want to use in TypeScript. - -```rust -#[export] -fn your_function(... -``` - -From your Noir library (where `Nargo.toml` is), run the following command: - -```bash -nargo export -``` - -You will now have an `export` directory with a .json file per exported function. - -You can also specify the directory of Noir programs using `--program-dir`, for example: - -```bash -nargo export --program-dir=./circuits/myNoirLib -``` - -### Generate TypeScript bindings from exported functions - -To use the `noir-codegen` package we added to the TypeScript project: - -```bash -yarn noir-codegen ./export/your_function.json -``` - -This creates an `exports` directory with an `index.ts` file containing all exported functions. - -**Note:** adding `--out-dir` allows you to specify an output dir for your TypeScript bindings to go. Eg: - -```bash -yarn noir-codegen ./export/*.json --out-dir ./path/to/output/dir -``` - -## Example .nr function to .ts output - -Consider a Noir library with this function: - -```rust -#[export] -fn not_equal(x: Field, y: Field) -> bool { - x != y -} -``` - -After the export and codegen steps, you should have an `index.ts` like: - -```typescript -export type Field = string; - - -export const is_equal_circuit: CompiledCircuit = -{"abi":{"parameters":[{"name":"x","type":{"kind":"field"},"visibility":"private"},{"name":"y","type":{"kind":"field"},"visibility":"private"}],"return_type":{"abi_type":{"kind":"boolean"},"visibility":"private"}},"bytecode":"H4sIAAAAAAAA/7WUMQ7DIAxFQ0Krrr2JjSGYLVcpKrn/CaqqDQN12WK+hPBgmWd/wEyHbF1SS923uhOs3pfoChI+wKXMAXzIKyNj4PB0TFTYc0w5RUjoqeAeEu1wqK0F54RGkWvW44LPzExnlkbMEs4JNZmN8PxS42uHv82T8a3Jeyn2Ks+VLPcO558HmyLMCDOXAXXtpPt4R/Rt9T36ss6dS9HGPx/eG17nGegKBQAA"}; - -export async function is_equal(x: Field, y: Field, foreignCallHandler?: ForeignCallHandler): Promise { - const program = new Noir(is_equal_circuit); - const args: InputMap = { x, y }; - const { returnValue } = await program.execute(args, foreignCallHandler); - return returnValue as boolean; -} -``` - -Now the `is_equal()` function and relevant types are readily available for use in TypeScript. diff --git a/docs/versioned_docs/version-v0.38.0/tooling/debugger.md b/docs/versioned_docs/version-v0.38.0/tooling/debugger.md deleted file mode 100644 index 200b5fc423a..00000000000 --- a/docs/versioned_docs/version-v0.38.0/tooling/debugger.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: Debugger -description: Learn about the Noir Debugger, in its REPL or VS Code versions. -keywords: [Nargo, VSCode, Visual Studio Code, REPL, Debugger] -sidebar_position: 2 ---- - -# Noir Debugger - -There are currently two ways of debugging Noir programs: - -1. From VS 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). -2. Via the REPL debugger, which ships with Nargo. - -In order to use either version of the debugger, you will need to install recent enough versions of Noir, [Nargo](../getting_started/noir_installation.md) and vscode-noir: - -- Noir & Nargo ≥0.28.0 -- Noir's VS Code extension ≥0.0.11 - -:::info -At the moment, the debugger supports debugging binary projects, but not contracts. -::: - -We cover the VS Code Noir debugger more in depth in [its VS Code debugger how-to guide](../how_to/debugger/debugging_with_vs_code.md) and [the reference](../reference/debugger/debugger_vscode.md). - -The REPL debugger is discussed at length in [the REPL debugger how-to guide](../how_to/debugger/debugging_with_the_repl.md) and [the reference](../reference/debugger/debugger_repl.md). diff --git a/docs/versioned_docs/version-v0.38.0/tooling/language_server.md b/docs/versioned_docs/version-v0.38.0/tooling/language_server.md deleted file mode 100644 index 81e0356ef8a..00000000000 --- a/docs/versioned_docs/version-v0.38.0/tooling/language_server.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -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] -sidebar_position: 0 ---- - -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.38.0/tooling/testing.md b/docs/versioned_docs/version-v0.38.0/tooling/testing.md deleted file mode 100644 index 866677da567..00000000000 --- a/docs/versioned_docs/version-v0.38.0/tooling/testing.md +++ /dev/null @@ -1,79 +0,0 @@ ---- -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] -sidebar_position: 1 ---- - -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 all -the constraints 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); -} -``` - -The string given to `should_fail_with` doesn't need to exactly match the failure reason, it just needs to be a substring of it: - -```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 = "airspeed velocity")] -fn test_bridgekeeper() { - main(32); -} -``` \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.38.0/tutorials/noirjs_app.md b/docs/versioned_docs/version-v0.38.0/tutorials/noirjs_app.md deleted file mode 100644 index 6e69ea0bbed..00000000000 --- a/docs/versioned_docs/version-v0.38.0/tutorials/noirjs_app.md +++ /dev/null @@ -1,366 +0,0 @@ ---- -title: Building a web app with NoirJS -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, app] -sidebar_position: 0 -pagination_next: noir/concepts/data_types/index ---- - -NoirJS is a set of packages meant to work both in a browser and a server environment. In this tutorial, we will build a simple web app using them. From here, you should get an idea on how to proceed with your own Noir projects! - -You can find the complete app code for this guide [here](https://github.com/noir-lang/tiny-noirjs-app). - -## Setup - -:::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.31.x matches `noir_js@0.31.x`, etc. - -In this guide, we will be pinned to 0.31.0. - -::: - -Before we start, we want to make sure we have Node, Nargo and the Barretenberg proving system (`bb`) installed. - -We start by opening a terminal and executing `node --version`. If we don't get an output like `v20.10.0`, that means node is not installed. Let's do that by following the handy [nvm guide](https://github.com/nvm-sh/nvm?tab=readme-ov-file#install--update-script). - -As for `Nargo`, we can follow the [Nargo guide](../getting_started/quick_start.md) to install it. If you're lazy, just paste this on a terminal and run `noirup`: - -```sh -curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash -``` - -Follow the instructions on [this page](https://github.com/AztecProtocol/aztec-packages/tree/master/barretenberg/cpp/src/barretenberg/bb#installation) to install `bb`. -Version 0.41.0 is compatible with `nargo` version 0.31.0, which you can install with `bbup -v 0.41.0` once `bbup` is installed. - -Easy enough. Onwards! - -## Our project - -ZK is a powerful technology. An app that doesn't reveal one of the inputs to _anyone_ is almost unbelievable, yet Noir makes it as easy as a single line of code. - -In fact, it's so simple that it comes nicely packaged in `nargo`. Let's do that! - -### Nargo - -Run: - -```bash -nargo new circuit -``` - -And... That's about it. Your program is ready to be compiled and run. - -To compile, let's `cd` into the `circuit` folder to enter our project, and call: - -```bash -nargo compile -``` - -This compiles our circuit into `json` format and add it to a new `target` folder. - -:::info - -At this point in the tutorial, your folder structure should look like this: - -```tree -. -└── circuit <---- our working directory - ├── Nargo.toml - ├── src - │ └── main.nr - └── target - └── circuit.json -``` - -::: - -### Node and Vite - -If you want to explore Nargo, feel free to go on a side-quest now and follow the steps in the -[getting started](../getting_started/quick_start.md) guide. However, we want our app to run on the browser, so we need Vite. - -Vite is a powerful tool to generate static websites. While it provides all kinds of features, let's just go barebones with some good old vanilla JS. - -To do this this, go back to the previous folder (`cd ..`) and create a new vite project by running `npm create vite` and choosing "Vanilla" and "Javascript". - -A wild `vite-project` directory should now appear in your root folder! Let's not waste any time and dive right in: - -```bash -cd vite-project -``` - -### Setting Up Vite and Configuring the Project - -Before we proceed with any coding, let's get our environment tailored for Noir. We'll start by laying down the foundations with a `vite.config.js` file. This little piece of configuration is our secret sauce for making sure everything meshes well with the NoirJS libraries and other special setups we might need, like handling WebAssembly modules. Here’s how you get that going: - -#### Creating the vite.config.js - -In your freshly minted `vite-project` folder, create a new file named `vite.config.js` and open it in your code editor. Paste the following to set the stage: - -```javascript -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 { - build: { - target: 'esnext', - rollupOptions: { - external: ['@aztec/bb.js'] - } - }, - optimizeDeps: { - esbuildOptions: { - target: 'esnext' - } - }, - plugins: [ - copy({ - targets: [{ src: 'node_modules/**/*.wasm', dest: 'node_modules/.vite/dist' }], - copySync: true, - hook: 'buildStart', - }), - command === 'serve' ? wasmContentTypePlugin : [], - ], - }; - } - - return {}; -}); -``` - -#### Install Dependencies - -Now that our stage is set, install the necessary NoirJS packages along with our other dependencies: - -```bash -npm install && npm install @noir-lang/backend_barretenberg@0.31.0 @noir-lang/noir_js@0.31.0 -npm install rollup-plugin-copy --save-dev -``` - -:::info - -At this point in the tutorial, your folder structure should look like this: - -```tree -. -└── circuit - └── ...etc... -└── vite-project <---- our working directory - └── ...etc... -``` - -::: - -#### Some cleanup - -`npx create vite` is amazing but it creates a bunch of files we don't really need for our simple example. Actually, let's just delete everything except for `vite.config.js`, `index.html`, `main.js` and `package.json`. I feel lighter already. - -![my heart is ready for you, noir.js](@site/static/img/memes/titanic.jpeg) - -## HTML - -Our app won't run like this, of course. We need some working HTML, at least. Let's open our broken-hearted `index.html` and replace everything with this code snippet: - -```html - - - - - - -

Noir app

-
- - -
-
-

Logs

-

Proof

-
- - -``` - -It _could_ be a beautiful UI... Depending on which universe you live in. - -## Some good old vanilla Javascript - -Our love for Noir needs undivided attention, so let's just open `main.js` and delete everything (this is where the romantic scenery becomes a bit creepy). - -Start by pasting in this boilerplate code: - -```js -function display(container, msg) { - const c = document.getElementById(container); - const p = document.createElement('p'); - p.textContent = msg; - c.appendChild(p); -} - -document.getElementById('submitGuess').addEventListener('click', async () => { - try { - // here's where love happens - } catch (err) { - display('logs', 'Oh 💔 Wrong guess'); - } -}); -``` - -The display function doesn't do much. We're simply manipulating our website to see stuff happening. For example, if the proof fails, it will simply log a broken heart 😢 - -:::info - -At this point in the tutorial, your folder structure should look like this: - -```tree -. -└── circuit - └── ...same as above -└── vite-project - ├── vite.config.js - ├── main.js - ├── package.json - └── index.html -``` - -You'll see other files and folders showing up (like `package-lock.json`, `node_modules`) but you shouldn't have to care about those. - -::: - -## Some NoirJS - -We're starting with the good stuff now. If you've compiled the circuit as described above, you should have a `json` file we want to import at the very top of our `main.js` file: - -```ts -import circuit from '../circuit/target/circuit.json'; -``` - -[Noir is backend-agnostic](../index.mdx#whats-new-about-noir). We write Noir, but we also need a proving backend. That's why we need to import and instantiate the two dependencies we installed above: `BarretenbergBackend` and `Noir`. Let's import them right below: - -```js -import { BarretenbergBackend, BarretenbergVerifier as Verifier } from '@noir-lang/backend_barretenberg'; -import { Noir } from '@noir-lang/noir_js'; -``` - -And instantiate them inside our try-catch block: - -```ts -// try { -const backend = new BarretenbergBackend(circuit); -const noir = new Noir(circuit); -// } -``` - -:::note - -For the remainder of the tutorial, everything will be happening inside the `try` block - -::: - -## Our app - -Now for the app itself. We're capturing whatever is in the input when people press the submit button. Just add this: - -```js -const x = parseInt(document.getElementById('guessInput').value); -const input = { x, y: 2 }; -``` - -Now we're ready to prove stuff! Let's feed some inputs to our circuit and calculate the proof: - -```js -await setup(); // let's squeeze our wasm inits here - -display('logs', 'Generating proof... ⌛'); -const { witness } = await noir.execute(input); -const proof = await backend.generateProof(witness); -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 run dev`. If it doesn't open a browser for you, just visit `localhost:5173`. You should now see the worst UI ever, with an ugly input. - -![Getting Started 0](@site/static/img/noir_getting_started_1.png) - -Now, our circuit says `fn main(x: Field, y: pub Field)`. This means only the `y` value is public, and it's hardcoded above: `input = { x, y: 2 }`. In other words, you won't need to send your secret`x` to the verifier! - -By inputting any number other than 2 in the input box and clicking "submit", you should get a valid proof. Otherwise the proof won't even generate correctly. By the way, if you're human, you shouldn't be able to understand anything on the "proof" box. That's OK. We like you, human ❤️. - -## Verifying - -Time to celebrate, yes! But we shouldn't trust machines so blindly. Let's add these lines to see our proof being verified: - -```js -display('logs', 'Verifying proof... ⌛'); -const isValid = await backend.verifyProof(proof); - -// or to cache and use the verification key: -// const verificationKey = await backend.getVerificationKey(); -// const verifier = new Verifier(); -// const isValid = await verifier.verifyProof(proof, verificationKey); - -if (isValid) display('logs', 'Verifying proof... ✅'); -``` - -You have successfully generated a client-side Noir web app! - -![coded app without math knowledge](@site/static/img/memes/flextape.jpeg) - -## 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/vite-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. - -## UltraHonk Backend - -Barretenberg has recently exposed a new UltraHonk backend. We can use UltraHonk in NoirJS after version 0.33.0. Everything will be the same as the tutorial above, except that the class we need to import will change: - -```js -import { UltraHonkBackend, UltraHonkVerifier as Verifier } from '@noir-lang/backend_barretenberg'; -``` - -The backend will then be instantiated as such: - -```js -const backend = new UltraHonkBackend(circuit); -``` - -Then all the commands to prove and verify your circuit will be same. - -The only feature currently unsupported with UltraHonk are [recursive proofs](../explainers/explainer-recursion.md). diff --git a/docs/versioned_docs/version-v0.39.0/explainers/cspell.json b/docs/versioned_docs/version-v0.39.0/explainers/cspell.json deleted file mode 100644 index c60b0a597b1..00000000000 --- a/docs/versioned_docs/version-v0.39.0/explainers/cspell.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "words": [ - "Cryptdoku" - ] -} diff --git a/docs/versioned_docs/version-v0.39.0/explainers/explainer-oracle.md b/docs/versioned_docs/version-v0.39.0/explainers/explainer-oracle.md deleted file mode 100644 index 821e1f95c04..00000000000 --- a/docs/versioned_docs/version-v0.39.0/explainers/explainer-oracle.md +++ /dev/null @@ -1,57 +0,0 @@ ---- -title: Oracles -description: This guide provides an in-depth understanding of how Oracles work in Noir programming. Learn how to use outside calculations in your programs, constrain oracles, and understand their uses and limitations. -keywords: - - Noir Programming - - Oracles - - JSON-RPC - - Foreign Call Handlers - - Constrained Functions - - Blockchain Programming -sidebar_position: 1 ---- - -If you've seen "The Matrix" you may recall "The Oracle" as Gloria Foster smoking cigarettes and baking cookies. While she appears to "know things", she is actually providing a calculation of a pre-determined future. Noir Oracles are similar, in a way. They don't calculate the future (yet), but they allow you to use outside calculations in your programs. - -![matrix oracle prediction](@site/static/img/memes/matrix_oracle.jpeg) - -A Noir program is usually self-contained. You can pass certain inputs to it, and it will generate a deterministic output for those inputs. But what if you wanted to defer some calculation to an outside process or source? - -Oracles are functions that provide this feature. - -## Use cases - -An example usage for Oracles is proving something on-chain. For example, proving that the ETH-USDC quote was below a certain target at a certain block time. Or even making more complex proofs like proving the ownership of an NFT as an anonymous login method. - -Another interesting use case is to defer expensive calculations to be made outside of the Noir program, and then constraining the result; similar to the use of [unconstrained functions](../noir/concepts//unconstrained.md). - -In short, anything that can be constrained in a Noir program but needs to be fetched from an external source is a great candidate to be used in oracles. - -## Constraining oracles - -Just like in The Matrix, Oracles are powerful. But with great power, comes great responsibility. Just because you're using them in a Noir program doesn't mean they're true. Noir has no superpowers. If you want to prove that Portugal won the Euro Cup 2016, you're still relying on potentially untrusted information. - -To give a concrete example, Alice wants to login to the [NounsDAO](https://nouns.wtf/) forum with her username "noir_nouner" by proving she owns a noun without revealing her ethereum address. Her Noir program could have an oracle call like this: - -```rust -#[oracle(getNoun)] -unconstrained fn get_noun(address: Field) -> Field -``` - -This oracle could naively resolve with the number of Nouns she possesses. However, it is useless as a trusted source, as the oracle could resolve to anything Alice wants. In order to make this oracle call actually useful, Alice would need to constrain the response from the oracle, by proving her address and the noun count belongs to the state tree of the contract. - -In short, **Oracles don't prove anything. Your Noir program does.** - -:::danger - -If you don't constrain the return of your oracle, you could be clearly opening an attack vector on your Noir program. Make double-triple sure that the return of an oracle call is constrained! - -::: - -## How to use Oracles - -On CLI, Nargo resolves oracles by making JSON RPC calls, which means it would require an RPC node to be running. - -In JavaScript, NoirJS accepts and resolves arbitrary call handlers (that is, not limited to JSON) as long as they match the expected types the developer defines. Refer to [Foreign Call Handler](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) to learn more about NoirJS's call handling. - -If you want to build using oracles, follow through to the [oracle guide](../how_to/how-to-oracles.md) for a simple example on how to do that. diff --git a/docs/versioned_docs/version-v0.39.0/explainers/explainer-recursion.md b/docs/versioned_docs/version-v0.39.0/explainers/explainer-recursion.md deleted file mode 100644 index df8529ef4e0..00000000000 --- a/docs/versioned_docs/version-v0.39.0/explainers/explainer-recursion.md +++ /dev/null @@ -1,176 +0,0 @@ ---- -title: Recursive proofs -description: Explore the concept of recursive proofs in Zero-Knowledge programming. Understand how recursion works in Noir, a language for writing smart contracts on the EVM blockchain. Learn through practical examples like Alice and Bob's guessing game, Charlie's recursive merkle tree, and Daniel's reusable components. Discover how to use recursive proofs to optimize computational resources and improve efficiency. - -keywords: - [ - "Recursive Proofs", - "Zero-Knowledge Programming", - "Noir", - "EVM Blockchain", - "Smart Contracts", - "Recursion in Noir", - "Alice and Bob Guessing Game", - "Recursive Merkle Tree", - "Reusable Components", - "Optimizing Computational Resources", - "Improving Efficiency", - "Verification Key", - "Aggregation", - "Recursive zkSNARK schemes", - "PLONK", - "Proving and Verification Keys" - ] -sidebar_position: 1 -pagination_next: how_to/how-to-recursion ---- - -In programming, we tend to think of recursion as something calling itself. A classic example would be the calculation of the factorial of a number: - -```js -function factorial(n) { - if (n === 0 || n === 1) { - return 1; - } else { - return n * factorial(n - 1); - } -} -``` - -In this case, while `n` is not `1`, this function will keep calling itself until it hits the base case, bubbling up the result on the call stack: - -```md - Is `n` 1? <--------- - /\ / - / \ n = n -1 - / \ / - Yes No -------- -``` - -In Zero-Knowledge, recursion has some similarities. - -It is not a Noir function calling itself, but a proof being used as an input to another circuit. In short, you verify one proof *inside* another proof, returning the proof that both proofs are valid. - -This means that, given enough computational resources, you can prove the correctness of any arbitrary number of proofs in a single proof. This could be useful to design state channels (for which a common example would be [Bitcoin's Lightning Network](https://en.wikipedia.org/wiki/Lightning_Network)), to save on gas costs by settling one proof on-chain, or simply to make business logic less dependent on a consensus mechanism. - -## Examples - -Let us look at some of these examples - -### Alice and Bob - Guessing game - -Alice and Bob are friends, and they like guessing games. They want to play a guessing game online, but for that, they need a trusted third-party that knows both of their secrets and finishes the game once someone wins. - -So, they use zero-knowledge proofs. Alice tries to guess Bob's number, and Bob will generate a ZK proof stating whether she succeeded or failed. - -This ZK proof can go on a smart contract, revealing the winner and even giving prizes. However, this means every turn needs to be verified on-chain. This incurs some cost and waiting time that may simply make the game too expensive or time-consuming to be worth it. - -As a solution, Alice proposes the following: "what if Bob generates his proof, and instead of sending it on-chain, I verify it *within* my own proof before playing my own turn?". - -She can then generate a proof that she verified his proof, and so on. - -```md - Did you fail? <-------------------------- - / \ / - / \ n = n -1 - / \ / - Yes No / - | | / - | | / - | You win / - | / - | / -Generate proof of that / - + / - my own guess ---------------- -``` - -### Charlie - Recursive merkle tree - -Charlie is a concerned citizen, and wants to be sure his vote in an election is accounted for. He votes with a ZK proof, but he has no way of knowing that his ZK proof was included in the total vote count! - -If the vote collector puts all of the votes into a [Merkle tree](https://en.wikipedia.org/wiki/Merkle_tree), everyone can prove the verification of two proofs within one proof, as such: - -```md - abcd - __________|______________ - | | - ab cd - _____|_____ ______|______ - | | | | - alice bob charlie daniel -``` - -Doing this recursively allows us to arrive on a final proof `abcd` which if true, verifies the correctness of all the votes. - -### Daniel - Reusable components - -Daniel has a big circuit and a big headache. A part of his circuit is a setup phase that finishes with some assertions that need to be made. But that section alone takes most of the proving time, and is largely independent of the rest of the circuit. - -He might find it more efficient to generate a proof for that setup phase separately, and verify that proof recursively in the actual business logic section of his circuit. This will allow for parallelization of both proofs, which results in a considerable speedup. - -## What params do I need - -As you can see in the [recursion reference](noir/standard_library/recursion.mdx), a simple recursive proof requires: - -- The proof to verify -- The Verification Key of the circuit that generated the proof -- A hash of this verification key, as it's needed for some backends -- The public inputs for the proof - -:::info - -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. - -So, taking the example of Alice and Bob and their guessing game: - -- Alice makes her guess. Her proof is *not* recursive: it doesn't verify any proof within it! It's just a standard `assert(x != y)` circuit -- Bob verifies Alice's proof and makes his own guess. In this circuit, he doesn't exactly *prove* the verification of Alice's proof. Instead, he *aggregates* his proof to Alice's proof. The actual verification is done when the full proof is verified, for example when using `nargo verify` or through the verifier smart contract. - -We can imagine recursive proofs a [relay race](https://en.wikipedia.org/wiki/Relay_race). The first runner doesn't have to receive the baton from anyone else, as he/she already starts with it. But when his/her turn is over, the next runner needs to receive it, run a bit more, and pass it along. Even though every runner could theoretically verify the baton mid-run (why not? 🏃🔍), only at the end of the race does the referee verify that the whole race is valid. - -::: - -## Some architecture - -As with everything in computer science, there's no one-size-fits all. But there are some patterns that could help understanding and implementing them. To give three examples: - -### Adding some logic to a proof verification - -This would be an approach for something like our guessing game, where proofs are sent back and forth and are verified by each opponent. This circuit would be divided in two sections: - -- A `recursive verification` section, which would be just the call to `std::verify_proof`, and that would be skipped on the first move (since there's no proof to verify) -- A `guessing` section, which is basically the logic part where the actual guessing happens - -In such a situation, and assuming Alice is first, she would skip the first part and try to guess Bob's number. Bob would then verify her proof on the first section of his run, and try to guess Alice's number on the second part, and so on. - -### Aggregating proofs - -In some one-way interaction situations, recursion would allow for aggregation of simple proofs that don't need to be immediately verified on-chain or elsewhere. - -To give a practical example, a barman wouldn't need to verify a "proof-of-age" on-chain every time he serves alcohol to a customer. Instead, the architecture would comprise two circuits: - -- A `main`, non-recursive circuit with some logic -- A `recursive` circuit meant to verify two proofs in one proof - -The customer's proofs would be intermediate, and made on their phones, and the barman could just verify them locally. He would then aggregate them into a final proof sent on-chain (or elsewhere) at the end of the day. - -### Recursively verifying different circuits - -Nothing prevents you from verifying different circuits in a recursive proof, for example: - -- A `circuit1` circuit -- A `circuit2` circuit -- A `recursive` circuit - -In this example, a regulator could verify that taxes were paid for a specific purchase by aggregating both a `payer` circuit (proving that a purchase was made and taxes were paid), and a `receipt` circuit (proving that the payment was received) - -## How fast is it - -At the time of writing, verifying recursive proofs is surprisingly fast. This is because most of the time is spent on generating the verification key that will be used to generate the next proof. So you are able to cache the verification key and reuse it later. - -Currently, Noir JS packages don't expose the functionality of loading proving and verification keys, but that feature exists in the underlying `bb.js` package. - -## How can I try it - -Learn more about using recursion in Nargo and NoirJS in the [how-to guide](../how_to/how-to-recursion.md) and see a full example in [noir-examples](https://github.com/noir-lang/noir-examples). diff --git a/docs/versioned_docs/version-v0.39.0/explainers/explainer-writing-noir.md b/docs/versioned_docs/version-v0.39.0/explainers/explainer-writing-noir.md deleted file mode 100644 index 3ce4245dc45..00000000000 --- a/docs/versioned_docs/version-v0.39.0/explainers/explainer-writing-noir.md +++ /dev/null @@ -1,177 +0,0 @@ ---- -title: Thinking in Circuits -description: Considerations when writing Noir programs -keywords: [Noir, programming, rust] -tags: [Optimization] -sidebar_position: 0 ---- - - -This article intends to set you up with key concepts essential for writing more viable applications that use zero knowledge proofs, namely around efficient circuits. - -## Context - 'Efficient' is subjective - -When writing a web application for a performant computer with high-speed internet connection, writing efficient code sometimes is seen as an afterthought only if needed. Large multiplications running at the innermost of nested loops may not even be on a dev's radar. -When writing firmware for a battery-powered microcontroller, you think of cpu cycles as rations to keep within a product's power budget. - -> Code is written to create applications that perform specific tasks within specific constraints - -And these constraints differ depending on where the compiled code is execute. - -### The Ethereum Virtual Machine (EVM) - -In scenarios where extremely low gas costs are required for an Ethereum application to be viable/competitive, Ethereum smart contract developers get into what is colloquially known as: "*gas golfing*". Finding the lowest execution cost of their compiled code (EVM bytecode) to achieve a specific task. - -The equivalent optimization task when writing zk circuits is affectionately referred to as "*gate golfing*", finding the lowest gate representation of the compiled Noir code. - -### Coding for circuits - a paradigm shift - -In zero knowledge cryptography, code is compiled to "circuits" consisting of arithmetic gates, and gate count is the significant cost. Depending on the proving system this is linearly proportionate to proving time, and so from a product point this should be kept as low as possible. - -Whilst writing efficient code for web apps and Solidity has a few key differences, writing efficient circuits have a different set of considerations. It is a bit of a paradigm shift, like writing code for GPUs for the first time... - -For example, drawing a circle at (0, 0) of radius `r`: -- For a single CPU thread, -``` -for theta in 0..2*pi { - let x = r * cos(theta); - let y = r * sin(theta); - draw(x, y); -} // note: would do 0 - pi/2 and draw +ve/-ve x and y. -``` - -- For GPUs (simultaneous parallel calls with x, y across image), -``` -if (x^2 + y^2 = r^2) { - draw(x, y); -} -``` - -([Related](https://www.youtube.com/watch?v=-P28LKWTzrI)) - -Whilst this CPU -> GPU does not translate to circuits exactly, it is intended to exemplify the difference in intuition when coding for different machine capabilities/constraints. - -### Context Takeaway - -For those coming from a primarily web app background, this article will explain what you need to consider when writing circuits. Furthermore, for those experienced writing efficient machine code, prepare to shift what you think is efficient 😬 - -## Translating from Rust - -For some applications using Noir, existing code might be a convenient starting point to then proceed to optimize the gate count of. - -:::note -Many valuable functions and algorithms have been written in more established languages (C/C++), and converted to modern ones (like Rust). -::: - -Fortunately for Noir developers, when needing a particular function a Rust implementation can be readily compiled into Noir with some key changes. While the compiler does a decent amount of optimizations, it won't be able to change code that has been optimized for clock-cycles into code optimized for arithmetic gates. - -A few things to do when converting Rust code to Noir: -- `println!` is not a macro, use `println` function (same for `assert_eq`) -- No early `return` in function. Use constrain via assertion instead -- No passing by reference. Remove `&` operator to pass by value (copy) -- No boolean operators (`&&`, `||`). Use bitwise operators (`&`, `|`) with boolean values -- No type `usize`. Use types `u8`, `u32`, `u64`, ... -- `main` return must be public, `pub` -- No `const`, use `global` -- Noir's LSP is your friend, so error message should be informative enough to resolve syntax issues. - -## Writing efficient Noir for performant products - -The following points help refine our understanding over time. - -:::note -A Noir program makes a statement that can be verified. -::: - -It compiles to a structure that represents the calculation, and can assert results within the calculation at any stage (via the `constrain` keyword). - -A Noir program compiles to an Abstract Circuit Intermediate Representation which is: - - Conceptually a tree structure - - Leaves (inputs) are the `Field` type - - Nodes contain arithmetic operations to combine them (gates) - - The root is the final result (return value) - -:::tip -The command `nargo info` shows the programs circuit size, and is useful to compare the value of changes made. -You can dig deeper and use the `--print-acir` param to take a closer look at individual ACIR opcodes, and the proving backend to see its gate count (eg for barretenberg, `bb gates -b ./target/program.json`). -::: - -### Use the `Field` type - -Since the native type of values in circuits are `Field`s, using them for variables in Noir means less gates converting them under the hood. -Some things to be mindful of when using a Field type for a regular integer value: -- A variable of type `Field` can be cast `as` an integer type (eg `u8`, `u64`) - - Note: this retains only the bits of the integer type. Eg a Field value of 260 as a `u8` becomes 4 -- For Field types arithmetic operations meaningfully overflow/underflow, yet for integer types they are checked according to their size -- Comparisons and bitwise operations do not exist for `Field`s, cast to an appropriately sized integer type when you need to - -:::tip -Where possible, use `Field` type for values. Using smaller value types, and bit-packing strategies, will result in MORE gates -::: - - -### Use Arithmetic over non-arithmetic operations - -Since circuits are made of arithmetic gates, the cost of arithmetic operations tends to be one gate. Whereas for procedural code, they represent several clock cycles. - -Inversely, non-arithmetic operators are achieved with multiple gates, vs 1 clock cycle for procedural code. - -| (cost\op) | arithmetic
(`*`, `+`) | bit-wise ops
(eg `<`, `\|`, `>>`) | -| - | - | - | -| **cycles** | 10+ | 1 | -| **gates** | 1 | 10+ | - -Bit-wise operations (e.g. bit shifts `<<` and `>>`), albeit commonly used in general programming and especially for clock cycle optimizations, are on the contrary expensive in gates when performed within circuits. - -Translate away from bit shifts when writing constrained functions for the best performance. - -On the flip side, feel free to use bit shifts in unconstrained functions and tests if necessary, as they are executed outside of circuits and does not induce performance hits. - -### Use static over dynamic values - -Another general theme that manifests in different ways is that static reads are represented with less gates than dynamic ones. - -Reading from read-only memory (ROM) adds less gates than random-access memory (RAM), 2 vs ~3.25 due to the additional bounds checks. Arrays of fixed length (albeit used at a lower capacity), will generate less gates than dynamic storage. - -Related to this, if an index used to access an array is not known at compile time (ie unknown until run time), then ROM will be converted to RAM, expanding the gate count. - -:::tip -Use arrays and indices that are known at compile time where possible. -Using `assert_constant(i);` before an index, `i`, is used in an array will give a compile error if `i` is NOT known at compile time. -::: - -### Leverage unconstrained execution - -Constrained verification can leverage unconstrained execution, this is especially useful for operations that are represented by many gates. -Use an [unconstrained function](../noir/concepts/unconstrained.md) to perform gate-heavy calculations, then verify and constrain the result. - -Eg division generates more gates than multiplication, so calculating the quotient in an unconstrained function then constraining the product for the quotient and divisor (+ any remainder) equals the dividend will be more efficient. - -Use ` if is_unconstrained() { /`, to conditionally execute code if being called in an unconstrained vs constrained way. - -## Advanced - -Unless you're well into the depth of gate optimization, this advanced section can be ignored. - -### Combine arithmetic operations - -A Noir program can be honed further by combining arithmetic operators in a way that makes the most of each constraint of the backend proving system. This is in scenarios where the backend might not be doing this perfectly. - -Eg Barretenberg backend (current default for Noir) is a width-4 PLONKish constraint system -$ w_1*w_2*q_m + w_1*q_1 + w_2*q_2 + w_3*q_3 + w_4*q_4 + q_c = 0 $ - -Here we see there is one occurrence of witness 1 and 2 ($w_1$, $w_2$) being multiplied together, with addition to witnesses 1-4 ($w_1$ .. $w_4$) multiplied by 4 corresponding circuit constants ($q_1$ .. $q_4$) (plus a final circuit constant, $q_c$). - -Use `nargo info --print-acir`, to inspect the ACIR opcodes (and the proving system for gates), and it may present opportunities to amend the order of operations and reduce the number of constraints. - -#### Variable as witness vs expression - -If you've come this far and really know what you're doing at the equation level, a temporary lever (that will become unnecessary/useless over time) is: `std::as_witness`. This informs the compiler to save a variable as a witness not an expression. - -The compiler will mostly be correct and optimal, but this may help some near term edge cases that are yet to optimize. -Note: When used incorrectly it will create **less** efficient circuits (higher gate count). - -## References -- Guillaume's ["`Cryptdoku`" talk](https://www.youtube.com/watch?v=MrQyzuogxgg) (Jun'23) -- Tips from Tom, Jake and Zac. -- [Idiomatic Noir](https://www.vlayer.xyz/blog/idiomatic-noir-part-1-collections) blog post diff --git a/docs/versioned_docs/version-v0.39.0/getting_started/noir_installation.md b/docs/versioned_docs/version-v0.39.0/getting_started/noir_installation.md deleted file mode 100644 index a5c7e649278..00000000000 --- a/docs/versioned_docs/version-v0.39.0/getting_started/noir_installation.md +++ /dev/null @@ -1,106 +0,0 @@ ---- -title: Standalone Noir Installation -description: There are different ways to install Nargo, the one-stop shop and command-line tool for developing Noir programs. This guide explains how to specify which version to install when using noirup, and using WSL for windows. -keywords: [ - Installation - Nargo - Noirup - Binaries - Compiling from Source - WSL for Windows - macOS - Linux - Nix - Direnv - Uninstalling Nargo - ] -sidebar_position: 2 ---- - -Noirup is the endorsed method for installing Nargo, streamlining the process of fetching binaries or compiling from source. It supports a range of options to cater to your specific needs, from nightly builds and specific versions to compiling from various sources. - -### Installing Noirup - -First, ensure you have `noirup` installed: - -```sh -curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash -``` - -### Fetching Binaries - -With `noirup`, you can easily switch between different Nargo versions, including nightly builds: - -- **Nightly Version**: Install the latest nightly build. - - ```sh - noirup --version nightly - ``` - -- **Specific Version**: Install a specific version of Nargo. - - ```sh - noirup --version - ``` - -### Compiling from Source - -`noirup` also enables compiling Nargo from various sources: - -- **From a Specific Branch**: Install from the latest commit on a branch. - - ```sh - noirup --branch - ``` - -- **From a Fork**: Install from the main branch of a fork. - - ```sh - noirup --repo - ``` - -- **From a Specific Branch in a Fork**: Install from a specific branch in a fork. - - ```sh - noirup --repo --branch - ``` - -- **From a Specific Pull Request**: Install from a specific PR. - - ```sh - noirup --pr - ``` - -- **From a Specific Commit**: Install from a specific commit. - - ```sh - noirup -C - ``` - -- **From Local Source**: Compile and install from a local directory. - - ```sh - noirup --path ./path/to/local/source - ``` - -## Installation on Windows - -The default backend for Noir (Barretenberg) doesn't provide Windows binaries at this time. For that reason, Noir cannot be installed natively. 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](#installing-noirup). - -## Setting up shell completions - -Once `nargo` is installed, you can [set up shell completions for it](setting_up_shell_completions). - -## Uninstalling Nargo - -If you installed Nargo with `noirup`, you can uninstall Nargo by removing the files in `~/.nargo`, `~/nargo`, and `~/noir_cache`. This ensures that all installed binaries, configurations, and cache related to Nargo are fully removed from your system. - -```bash -rm -r ~/.nargo -rm -r ~/nargo -rm -r ~/noir_cache -``` diff --git a/docs/versioned_docs/version-v0.39.0/getting_started/project_breakdown.md b/docs/versioned_docs/version-v0.39.0/getting_started/project_breakdown.md deleted file mode 100644 index e442e377040..00000000000 --- a/docs/versioned_docs/version-v0.39.0/getting_started/project_breakdown.md +++ /dev/null @@ -1,159 +0,0 @@ ---- -title: Project Breakdown -description: - Learn about the anatomy of a Nargo project, including the purpose of the Prover TOML - file, and how to prove and verify your program. -keywords: - [Nargo, Nargo project, Prover.toml, proof verification, private asset transfer] -sidebar_position: 1 ---- - -This section breaks down our hello world program from 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 - - 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. - -### 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 = "noir_starter" -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](../noir/modules_packages_crates/workspaces.md) will look a bit different. For example: - -```toml -[workspace] -members = ["crates/a", "crates/b"] -default-member = "crates/a" -``` - -#### Package section - -The package section defines 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) -- `expression_width` (optional) - Sets the default backend expression width. This field will override the default backend expression width specified by the Noir compiler (currently set to width 4). - -#### Dependencies section - -This is where you will specify any dependencies for your project. See the [Dependencies page](../noir/modules_packages_crates/dependencies.md) 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 that the condition to be satisfied (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 the inputs to the Noir program (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 execute` is executed, nargo will execute the Noir program using the inputs specified in `Prover.toml`, aborting if it finds that these do not satisfy the constraints defined by `main`. In this example, `x` and `y` must satisfy the inequality constraint `assert(x != y)`. - -If an output name is specified such as `nargo execute foo`, the witness generated by this execution will be written to `./target/foo.gz`. This can then be used to generate a proof of the execution. - -#### 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 execution by using the `--prover-name` or `-p` flags. - -This command looks for proof inputs in the default **Prover.toml** and generates the witness and saves it at `./target/foo.gz`: - -```bash -nargo execute foo -``` - -This command looks for proof inputs in the custom **OtherProver.toml** and generates the witness and saves it at `./target/bar.gz`: - -```bash -nargo execute -p OtherProver bar -``` - -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.39.0/getting_started/quick_start.md b/docs/versioned_docs/version-v0.39.0/getting_started/quick_start.md deleted file mode 100644 index 7deeae12fd9..00000000000 --- a/docs/versioned_docs/version-v0.39.0/getting_started/quick_start.md +++ /dev/null @@ -1,126 +0,0 @@ ---- -title: Quick Start -tags: [] -sidebar_position: 0 ---- - -## Installation - -### Noir - -The easiest way to develop with Noir is using Nargo the CLI tool. It provides you the ability to start new projects, compile, execute and test Noir programs from the terminal. - -You can use `noirup` the installation script to quickly install and update Nargo: - -```bash -curl -L https://raw.githubusercontent.com/noir-lang/noirup/refs/heads/main/install | bash -noirup -``` - -Once installed, you can [set up shell completions for the `nargo` command](setting_up_shell_completions). - -### Proving backend - -After installing Noir, we install a proving backend to work with our Noir programs. - -Proving backends provide you the abilities to generate proofs, verify proofs, generate smart contracts and more for your Noir programs. - -Different proving backends provide different tools for working with Noir programs, here we will use the [Barretenberg proving backend](https://github.com/AztecProtocol/aztec-packages/tree/master/barretenberg) developed by Aztec Labs as an example. - -You can use the `bbup` installation script to quickly install and update BB, Barretenberg's CLI tool: - -You can find the full list of proving backends compatible with Noir in Awesome Noir. - -```bash -curl -L https://raw.githubusercontent.com/AztecProtocol/aztec-packages/refs/heads/master/barretenberg/bbup/install | bash -bbup -``` - -For the full list of proving backends compatible with Noir, visit [Awesome Noir](https://github.com/noir-lang/awesome-noir/?tab=readme-ov-file#proving-backends). - -## Nargo - -Nargo provides the ability to initiate and execute Noir projects. Let's initialize the traditional `hello_world`: - -```sh -nargo new hello_world -``` - -Two files will be created. - -- `src/main.nr` contains a simple boilerplate circuit -- `Nargo.toml` contains environmental options, such as name, author, dependencies, and others. - -Glancing at _main.nr_ , we can see that inputs in Noir are private by default, but can be labeled public using the keyword `pub`. This means that we will _assert_ that we know a value `x` which is different from `y` without revealing `x`: - -```rust -fn main(x : Field, y : pub Field) { - assert(x != y); -} -``` - -To learn more about private and public values, check the [Data Types](../noir/concepts/data_types/index.md) section. - -### Compiling and executing - -We can now use `nargo` to generate a _Prover.toml_ file, where our input values will be specified: - -```sh -cd hello_world -nargo check - -Let's feed some valid values into this file: - -```toml -x = "1" -y = "2" -``` - -We're now ready to compile and execute our Noir program. By default the `nargo execute` command will do both, and generate the `witness` that we need to feed to our proving backend: - -```sh -nargo execute -``` - -The witness corresponding to this execution will then be written to the file _./target/witness-name.gz_. - -The command also automatically compiles your Noir program if it was not already / was edited, which you may notice the compiled artifacts being written to the file _./target/hello_world.json_. - -With circuit compiled and witness generated, we're ready to prove. - -## Proving backend - -Different proving backends may provide different tools and commands to work with Noir programs. Here Barretenberg's `bb` CLI tool is used as an example: - -```sh -bb prove -b ./target/hello_world.json -w ./target/hello_world.gz -o ./target/proof -``` - -:::tip - -Naming can be confusing, specially as you pass them to the `bb` commands. If unsure, it won't hurt to delete the target folder and start anew to make sure you're using the most recent versions of the compiled circuit and witness. - -::: - -The proof is now generated in the `target` folder. To verify it we first need to compute the verification key from the compiled circuit, and use it to verify: - -```sh -bb write_vk -b ./target/hello_world.json -o ./target/vk -bb verify -k ./target/vk -p ./target/proof -``` - -:::info - -Notice that in order to verify a proof, the verifier knows nothing but the circuit, which is compiled and used to generate the verification key. This is obviously quite important: private inputs remain private. - -As for the public inputs, you may have noticed they haven't been specified. This behavior varies with each particular backend, but barretenberg typically attaches them to the proof. You can see them by parsing and splitting it. For example for if your public inputs are 32 bytes: - -```bash -head -c 32 ./target/proof | od -An -v -t x1 | tr -d $' \n' -``` - -::: - -Congratulations, you have now created and verified a proof for your very first Noir program! - -In the [next section](./project_breakdown.md), we will go into more detail on each step performed. diff --git a/docs/versioned_docs/version-v0.39.0/getting_started/setting_up_shell_completions.md b/docs/versioned_docs/version-v0.39.0/getting_started/setting_up_shell_completions.md deleted file mode 100644 index 0447321cbab..00000000000 --- a/docs/versioned_docs/version-v0.39.0/getting_started/setting_up_shell_completions.md +++ /dev/null @@ -1,87 +0,0 @@ ---- -title: Setting up shell completions -tags: [] -sidebar_position: 3 ---- - -The `nargo` binary provides a command to generate shell completions: - -```bash -nargo generate-completion-script [shell] -``` - -where `shell` must be one of `bash`, `elvish`, `fish`, `powershell`, and `zsh`. - -Below we explain how to install them in some popular shells. - -## Installing Zsh Completions - -If you have `oh-my-zsh` installed, you might already have a directory of automatically loading completion scripts — `.oh-my-zsh/completions`. -If not, first create it: - -```bash -mkdir -p ~/.oh-my-zsh/completions` -``` - -Then copy the completion script to that directory: - -```bash -nargo generate-completion-script zsh > ~/.oh-my-zsh/completions/_nargo -``` - -Without `oh-my-zsh`, you’ll need to add a path for completion scripts to your function path, and turn on completion script auto-loading. -First, add these lines to `~/.zshrc`: - -```bash -fpath=(~/.zsh/completions $fpath) -autoload -U compinit -compinit -``` - -Next, create a directory at `~/.zsh/completions`: - -```bash -mkdir -p ~/.zsh/completions -``` - -Then copy the completion script to that directory: - -```bash -nargo generate-completion-script zsh > ~/.zsh/completions/_nargo -``` - -## Installing Bash Completions - -If you have [bash-completion](https://github.com/scop/bash-completion) installed, you can just copy the completion script to the `/usr/local/etc/bash_completion.d` directory: - -```bash -nargo generate-completion-script bash > /usr/local/etc/bash_completion.d/nargo -``` - -Without `bash-completion`, you’ll need to source the completion script directly. -First create a directory such as `~/.bash_completions/`: - -```bash -mkdir ~/.bash_completions/ -``` - -Copy the completion script to that directory: - -```bash -nargo generate-completion-script bash > ~/.bash_completions/nargo.bash -``` - -Then add the following line to `~/.bash_profile` or `~/.bashrc`: - - -```bash -source ~/.bash_completions/nargo.bash -``` - -## Installing Fish Completions - -Copy the completion script to any path listed in the environment variable `$fish_completion_path`. For example, a typical location is `~/.config/fish/completions/nargo.fish`: - -```bash -nargo generate-completion-script fish > ~/.config/fish/completions/nargo.fish -``` diff --git a/docs/versioned_docs/version-v0.39.0/how_to/_category_.json b/docs/versioned_docs/version-v0.39.0/how_to/_category_.json deleted file mode 100644 index 23b560f610b..00000000000 --- a/docs/versioned_docs/version-v0.39.0/how_to/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "position": 1, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.39.0/how_to/debugger/_category_.json b/docs/versioned_docs/version-v0.39.0/how_to/debugger/_category_.json deleted file mode 100644 index cc2cbb1c253..00000000000 --- a/docs/versioned_docs/version-v0.39.0/how_to/debugger/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "Debugging", - "position": 5, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.39.0/how_to/debugger/debugging_with_the_repl.md b/docs/versioned_docs/version-v0.39.0/how_to/debugger/debugging_with_the_repl.md deleted file mode 100644 index 1d64dae3f37..00000000000 --- a/docs/versioned_docs/version-v0.39.0/how_to/debugger/debugging_with_the_repl.md +++ /dev/null @@ -1,164 +0,0 @@ ---- -title: Using the REPL Debugger -description: - Step-by-step guide on how to debug your Noir circuits with the REPL Debugger. -keywords: - [ - Nargo, - Noir CLI, - Noir Debugger, - REPL, - ] -sidebar_position: 1 ---- - -#### Pre-requisites - -In order to use the REPL debugger, first you need to install recent enough versions of Nargo and vscode-noir. - -## Debugging a simple circuit - -Let's debug a simple circuit: - -```rust -fn main(x : Field, y : pub Field) { - assert(x != y); -} -``` - -To start the REPL debugger, using a terminal, go to a Noir circuit's home directory. Then: - -`$ nargo debug` - -You should be seeing this in your terminal: - -``` -[main] Starting debugger -At ~/noir-examples/recursion/circuits/main/src/main.nr:1:9 - 1 -> fn main(x : Field, y : pub Field) { - 2 assert(x != y); - 3 } -> -``` - -The debugger displays the current Noir code location, and it is now waiting for us to drive it. - -Let's first take a look at the available commands. For that we'll use the `help` command. - -``` -> help -Available commands: - - opcodes display ACIR opcodes - into step into to the next opcode - next step until a new source location is reached - out step until a new source location is reached - and the current stack frame is finished - break LOCATION:OpcodeLocation add a breakpoint at an opcode location - over step until a new source location is reached - without diving into function calls - restart restart the debugging session - delete LOCATION:OpcodeLocation delete breakpoint at an opcode location - witness show witness map - witness index:u32 display a single witness from the witness map - witness index:u32 value:String update a witness with the given value - memset index:usize value:String update a memory cell with the given - value - continue continue execution until the end of the - program - vars show variable values available at this point - in execution - stacktrace display the current stack trace - memory show memory (valid when executing unconstrained code) - step step to the next ACIR opcode - -Other commands: - - help Show this help message - quit Quit repl - -``` - -Some commands operate only for unconstrained functions, such as `memory` and `memset`. If you try to use them while execution is paused at an ACIR opcode, the debugger will simply inform you that you are not executing unconstrained code: - -``` -> memory -Unconstrained VM memory not available -> -``` - -Before continuing, we can take a look at the initial witness map: - -``` -> witness -_0 = 1 -_1 = 2 -> -``` - -Cool, since `x==1`, `y==2`, and we want to check that `x != y`, our circuit should succeed. At this point we could intervene and use the witness setter command to change one of the witnesses. Let's set `y=3`, then back to 2, so we don't affect the expected result: - -``` -> witness -_0 = 1 -_1 = 2 -> witness 1 3 -_1 = 3 -> witness -_0 = 1 -_1 = 3 -> witness 1 2 -_1 = 2 -> witness -_0 = 1 -_1 = 2 -> -``` - -Now we can inspect the current state of local variables. For that we use the `vars` command. - -``` -> vars -> -``` - -We currently have no vars in context, since we are at the entry point of the program. Let's use `next` to execute until the next point in the program. - -``` -> vars -> next -At ~/noir-examples/recursion/circuits/main/src/main.nr:1:20 - 1 -> fn main(x : Field, y : pub Field) { - 2 assert(x != y); - 3 } -> vars -x:Field = 0x01 -``` - -As a result of stepping, the variable `x`, whose initial value comes from the witness map, is now in context and returned by `vars`. - -``` -> next - 1 fn main(x : Field, y : pub Field) { - 2 -> assert(x != y); - 3 } -> vars -y:Field = 0x02 -x:Field = 0x01 -``` - -Stepping again we can finally see both variables and their values. And now we can see that the next assertion should succeed. - -Let's continue to the end: - -``` -> continue -(Continuing execution...) -Finished execution -> q -[main] Circuit witness successfully solved -``` - -Upon quitting the debugger after a solved circuit, the resulting circuit witness gets saved, equivalent to what would happen if we had run the same circuit with `nargo execute`. - -We just went through the basics of debugging using Noir REPL debugger. For a comprehensive reference, check out [the reference page](../../reference/debugger/debugger_repl.md). diff --git a/docs/versioned_docs/version-v0.39.0/how_to/debugger/debugging_with_vs_code.md b/docs/versioned_docs/version-v0.39.0/how_to/debugger/debugging_with_vs_code.md deleted file mode 100644 index a5858c1a5eb..00000000000 --- a/docs/versioned_docs/version-v0.39.0/how_to/debugger/debugging_with_vs_code.md +++ /dev/null @@ -1,68 +0,0 @@ ---- -title: Using the VS Code Debugger -description: - Step by step guide on how to debug your Noir circuits with the VS Code Debugger configuration and features. -keywords: - [ - Nargo, - Noir CLI, - Noir Debugger, - VS Code, - IDE, - ] -sidebar_position: 0 ---- - -This guide will show you how to use VS Code with the vscode-noir extension to debug a Noir project. - -#### Pre-requisites - -- Nargo -- vscode-noir -- A Noir project with a `Nargo.toml`, `Prover.toml` and at least one Noir (`.nr`) containing an entry point function (typically `main`). - -## Running the debugger - -The easiest way to start debugging is to open the file you want to debug, and press `F5`. This will cause the debugger to launch, using your `Prover.toml` file as input. - -You should see something like this: - -![Debugger launched](@site/static/img/debugger/1-started.png) - -Let's inspect the state of the program. For that, we open VS Code's _Debug pane_. Look for this icon: - -![Debug pane icon](@site/static/img/debugger/2-icon.png) - -You will now see two categories of variables: Locals and Witness Map. - -![Debug pane expanded](@site/static/img/debugger/3-debug-pane.png) - -1. **Locals**: variables of your program. At this point in execution this section is empty, but as we step through the code it will get populated by `x`, `result`, `digest`, etc. - -2. **Witness map**: these are initially populated from your project's `Prover.toml` file. In this example, they will be used to populate `x` and `result` at the beginning of the `main` function. - -Most of the time you will probably be focusing mostly on locals, as they represent the high level state of your program. - -You might be interested in inspecting the witness map in case you are trying to solve a really low level issue in the compiler or runtime itself, so this concerns mostly advanced or niche users. - -Let's step through the program, by using the debugger buttons or their corresponding keyboard shortcuts. - -![Debugger buttons](@site/static/img/debugger/4-debugger-buttons.png) - -Now we can see in the variables pane that there's values for `digest`, `result` and `x`. - -![Inspecting locals](@site/static/img/debugger/5-assert.png) - -We can also inspect the values of variables by directly hovering on them on the code. - -![Hover locals](@site/static/img/debugger/6-hover.png) - -Let's set a break point at the `keccak256` function, so we can continue execution up to the point when it's first invoked without having to go one step at a time. - -We just need to click the to the right of the line number 18. Once the breakpoint appears, we can click the `continue` button or use its corresponding keyboard shortcut (`F5` by default). - -![Breakpoint](@site/static/img/debugger/7-break.png) - -Now we are debugging the `keccak256` function, notice the _Call Stack pane_ at the lower right. This lets us inspect the current call stack of our process. - -That covers most of the current debugger functionalities. Check out [the reference](../../reference/debugger/debugger_vscode.md) for more details on how to configure the debugger. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.39.0/how_to/how-to-oracles.md b/docs/versioned_docs/version-v0.39.0/how_to/how-to-oracles.md deleted file mode 100644 index 4763b7788d6..00000000000 --- a/docs/versioned_docs/version-v0.39.0/how_to/how-to-oracles.md +++ /dev/null @@ -1,275 +0,0 @@ ---- -title: How to use Oracles -description: Learn how to use oracles in your Noir program with examples in both Nargo and NoirJS. This guide also covers writing a JSON RPC server and providing custom foreign call handlers for NoirJS. -keywords: - - Noir Programming - - Oracles - - Nargo - - NoirJS - - JSON RPC Server - - Foreign Call Handlers -sidebar_position: 1 ---- - -This guide shows you how to use oracles in your Noir program. For the sake of clarity, it assumes that: - -- You have read the [explainer on Oracles](../explainers/explainer-oracle.md) and are comfortable with the concept. -- You have a Noir program to add oracles to. You can create one using the [vite-hardhat starter](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat) as a boilerplate. -- You understand the concept of a JSON-RPC server. Visit the [JSON-RPC website](https://www.jsonrpc.org/) if you need a refresher. -- You are comfortable with server-side JavaScript (e.g. Node.js, managing packages, etc.). - -## Rundown - -This guide has 3 major steps: - -1. How to modify our Noir program to make use of oracle calls as unconstrained functions -2. How to write a JSON RPC Server to resolve these oracle calls with Nargo -3. How to use them in Nargo and how to provide a custom resolver in NoirJS - -## Step 1 - Modify your Noir program - -An oracle is defined in a Noir program by defining two methods: - -- An unconstrained method - This tells the compiler that it is executing an [unconstrained functions](../noir/concepts//unconstrained.md). -- A decorated oracle method - This tells the compiler that this method is an RPC call. - -An example of an oracle that returns a `Field` would be: - -```rust -#[oracle(getSqrt)] -unconstrained fn sqrt(number: Field) -> Field { } - -unconstrained fn get_sqrt(number: Field) -> Field { - sqrt(number) -} -``` - -In this example, we're wrapping our oracle function in an unconstrained method, and decorating it with `oracle(getSqrt)`. We can then call the unconstrained function as we would call any other function: - -```rust -fn main(input: Field) { - let sqrt = get_sqrt(input); -} -``` - -In the next section, we will make this `getSqrt` (defined on the `sqrt` decorator) be a method of the RPC server Noir will use. - -:::danger - -As explained in the [Oracle Explainer](../explainers/explainer-oracle.md), this `main` function is unsafe unless you constrain its return value. For example: - -```rust -fn main(input: Field) { - let sqrt = get_sqrt(input); - assert(sqrt.pow_32(2) as u64 == input as u64); // <---- constrain the return of an oracle! -} -``` - -::: - -:::info - -Currently, oracles only work with single params or array params. For example: - -```rust -#[oracle(getSqrt)] -unconstrained fn sqrt([Field; 2]) -> [Field; 2] { } -``` - -::: - -## Step 2 - Write an RPC server - -Brillig will call *one* RPC server. Most likely you will have to write your own, and you can do it in whatever language you prefer. In this guide, we will do it in Javascript. - -Let's use the above example of an oracle that consumes an array with two `Field` and returns their square roots: - -```rust -#[oracle(getSqrt)] -unconstrained fn sqrt(input: [Field; 2]) -> [Field; 2] { } - -unconstrained fn get_sqrt(input: [Field; 2]) -> [Field; 2] { - sqrt(input) -} - -fn main(input: [Field; 2]) { - let sqrt = get_sqrt(input); - assert(sqrt[0].pow_32(2) as u64 == input[0] as u64); - assert(sqrt[1].pow_32(2) as u64 == input[1] as u64); -} - -#[test] -fn test() { - let input = [4, 16]; - main(input); -} -``` - -:::info - -Why square root? - -In general, computing square roots is computationally more expensive than multiplications, which takes a toll when speaking about ZK applications. In this case, instead of calculating the square root in Noir, we are using our oracle to offload that computation to be made in plain. In our circuit we can simply multiply the two values. - -::: - -Now, we should write the correspondent RPC server, starting with the [default JSON-RPC 2.0 boilerplate](https://www.npmjs.com/package/json-rpc-2.0#example): - -```js -import { JSONRPCServer } from "json-rpc-2.0"; -import express from "express"; -import bodyParser from "body-parser"; - -const app = express(); -app.use(bodyParser.json()); - -const server = new JSONRPCServer(); -app.post("/", (req, res) => { - const jsonRPCRequest = req.body; - server.receive(jsonRPCRequest).then((jsonRPCResponse) => { - if (jsonRPCResponse) { - res.json(jsonRPCResponse); - } else { - res.sendStatus(204); - } - }); -}); - -app.listen(5555); -``` - -Now, we will add our `getSqrt` method, as expected by the `#[oracle(getSqrt)]` decorator in our Noir code. It maps through the params array and returns their square roots: - -```js -server.addMethod("resolve_foreign_call", async (params) => { - if (params[0].function !== "getSqrt") { - throw Error("Unexpected foreign call") - }; - const values = params[0].inputs[0].map((field) => { - return `${Math.sqrt(parseInt(field, 16))}`; - }); - return { values: [values] }; -}); -``` - -If you're using Typescript, the following types may be helpful in understanding the expected return value and making sure they're easy to follow: - -```js -export type ForeignCallSingle = string; - -export type ForeignCallArray = string[]; - -export type ForeignCallResult = { - values: (ForeignCallSingle | ForeignCallArray)[]; -}; -``` - -:::info Multidimensional Arrays - -If the Oracle function is returning an array containing other arrays, such as `[['1','2],['3','4']]`, you need to provide the values in JSON as flattened values. In the previous example, it would be `['1', '2', '3', '4']`. In the Noir program, the Oracle signature can use a nested type, the flattened values will be automatically converted to the nested type. - -::: - -## Step 3 - Usage with Nargo - -Using the [`nargo` CLI tool](../reference/nargo_commands.md), you can use oracles in the `nargo test` and `nargo execute` commands by passing a value to `--oracle-resolver`. For example: - -```bash -nargo test --oracle-resolver http://localhost:5555 -``` - -This tells `nargo` to use your RPC Server URL whenever it finds an oracle decorator. - -## Step 4 - Usage with NoirJS - -In a JS environment, an RPC server is not strictly necessary, as you may want to resolve your oracles without needing any JSON call at all. NoirJS simply expects that you pass a callback function when you generate proofs, and that callback function can be anything. - -For example, if your Noir program expects the host machine to provide CPU pseudo-randomness, you could simply pass it as the `foreignCallHandler`. You don't strictly need to create an RPC server to serve pseudo-randomness, as you may as well get it directly in your app: - -```js -const foreignCallHandler = (name, inputs) => crypto.randomBytes(16) // etc - -await noir.execute(inputs, foreignCallHandler) -``` - -As one can see, in NoirJS, the [`foreignCallHandler`](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) function simply means "a callback function that returns a value of type [`ForeignCallOutput`](../reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md). It doesn't have to be an RPC call like in the case for Nargo. - -:::tip - -Does this mean you don't have to write an RPC server like in [Step #2](#step-2---write-an-rpc-server)? - -You don't technically have to, but then how would you run `nargo test`? To use both `Nargo` and `NoirJS` in your development flow, you will have to write a JSON RPC server. - -::: - -In this case, let's make `foreignCallHandler` call the JSON RPC Server we created in [Step #2](#step-2---write-an-rpc-server), by making it a JSON RPC Client. - -For example, using the same `getSqrt` program in [Step #1](#step-1---modify-your-noir-program) (comments in the code): - -```js -import { JSONRPCClient } from "json-rpc-2.0"; - -// declaring the JSONRPCClient -const client = new JSONRPCClient((jsonRPCRequest) => { -// hitting the same JSON RPC Server we coded above - return fetch("http://localhost:5555", { - method: "POST", - headers: { - "content-type": "application/json", - }, - body: JSON.stringify(jsonRPCRequest), - }).then((response) => { - if (response.status === 200) { - return response - .json() - .then((jsonRPCResponse) => client.receive(jsonRPCResponse)); - } else if (jsonRPCRequest.id !== undefined) { - return Promise.reject(new Error(response.statusText)); - } - }); -}); - -// declaring a function that takes the name of the foreign call (getSqrt) and the inputs -const foreignCallHandler = async (name, input) => { - const inputs = input[0].map((i) => i.toString("hex")) - // notice that the "inputs" parameter contains *all* the inputs - // in this case we to make the RPC request with the first parameter "numbers", which would be input[0] - const oracleReturn = await client.request("resolve_foreign_call", [ - { - function: name, - inputs: [inputs] - }, - ]); - return [oracleReturn.values[0]]; -}; - -// the rest of your NoirJS code -const input = { input: [4, 16] }; -const { witness } = await noir.execute(input, foreignCallHandler); -``` - -:::tip - -If you're in a NoirJS environment running your RPC server together with a frontend app, you'll probably hit a familiar problem in full-stack development: requests being blocked by [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) policy. For development only, you can simply install and use the [`cors` npm package](https://www.npmjs.com/package/cors) to get around the problem: - -```bash -yarn add cors -``` - -and use it as a middleware: - -```js -import cors from "cors"; - -const app = express(); -app.use(cors()) -``` - -::: - -## Conclusion - -Hopefully by the end of this guide, you should be able to: - -- Write your own logic around Oracles and how to write a JSON RPC server to make them work with your Nargo commands. -- Provide custom foreign call handlers for NoirJS. diff --git a/docs/versioned_docs/version-v0.39.0/how_to/how-to-recursion.md b/docs/versioned_docs/version-v0.39.0/how_to/how-to-recursion.md deleted file mode 100644 index 399e4d4b38a..00000000000 --- a/docs/versioned_docs/version-v0.39.0/how_to/how-to-recursion.md +++ /dev/null @@ -1,172 +0,0 @@ ---- -title: How to use recursion on NoirJS -description: Learn how to implement recursion with NoirJS, a powerful tool for creating smart contracts on the EVM blockchain. This guide assumes familiarity with NoirJS, solidity verifiers, and the Barretenberg proving backend. Discover how to generate both final and intermediate proofs using `noir_js` and `bb.js`. -keywords: - [ - "NoirJS", - "EVM blockchain", - "smart contracts", - "recursion", - "solidity verifiers", - "Barretenberg backend", - "noir_js", - "intermediate proofs", - "final proofs", - "nargo compile", - "json import", - "recursive circuit", - "recursive app" - ] -sidebar_position: 1 ---- - -This guide shows you how to use recursive proofs in your NoirJS app. For the sake of clarity, it is assumed that: - -- You already have a NoirJS app. If you don't, please visit the [NoirJS tutorial](../tutorials/noirjs_app.md) and the [reference](../reference/NoirJS/noir_js/index.md). -- You are familiar with what are recursive proofs and you have read the [recursion explainer](../explainers/explainer-recursion.md) -- You already built a recursive circuit following [the reference](../noir/standard_library/recursion.mdx), and understand how it works. - -It is also assumed that you're not using `noir_wasm` for compilation, and instead you've used [`nargo compile`](../reference/nargo_commands.md) to generate the `json` you're now importing into your project. However, the guide should work just the same if you're using `noir_wasm`. - -:::info - -As you've read in the [explainer](../explainers/explainer-recursion.md), a recursive proof is an intermediate proof. This means that it doesn't necessarily generate the final step that makes it verifiable in a smart contract. However, it is easy to verify within another circuit. - -::: - -In a standard recursive app, you're also dealing with at least two circuits. For the purpose of this guide, we will assume the following: - -- `main`: a circuit of type `assert(x != y)`, which we want to embed in another circuit recursively. For example when proving with the `bb` tool, we can use the `--recursive` CLI option to tell the backend that it should generate proofs that are friendly for verification within another circuit. -- `recursive`: a circuit that verifies `main` - -For a full example of how recursive proofs work, please refer to the [noir-examples](https://github.com/noir-lang/noir-examples) repository. We will *not* be using it as a reference for this guide. - -## Step 1: Setup - -In a common NoirJS app, you need to instantiate a backend with something like `const backend = new Backend(circuit)`. Then you feed it to the `noir_js` interface. - -For recursion, this doesn't happen, and the only need for `noir_js` is only to `execute` a circuit and get its witness and return value. Everything else is not interfaced, so it needs to happen on the `backend` object. - -It is also recommended that you instantiate the backend with as many threads as possible, to allow for maximum concurrency: - -```js -const backend = new UltraPlonkBackend(circuit, { threads: 8 }, { recursive: true }) -``` - -:::tip -You can use the [`os.cpus()`](https://nodejs.org/api/os.html#oscpus) object in `nodejs` or [`navigator.hardwareConcurrency`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/hardwareConcurrency) on the browser to make the most out of those glorious cpu cores -::: - -## Step 2: Generating the witness and the proof for `main` - -After instantiating the backend, you should also instantiate `noir_js`. We will use it to execute the circuit and get the witness. - -```js -const noir = new Noir(circuit) -const { witness } = noir.execute(input) -``` - -With this witness, you are now able to generate the intermediate proof for the main circuit: - -```js -const { proof, publicInputs } = await backend.generateProof(witness) -``` - -:::warning - -Always keep in mind what is actually happening on your development process, otherwise you'll quickly become confused about what circuit we are actually running and why! - -In this case, you can imagine that Alice (running the `main` circuit) is proving something to Bob (running the `recursive` circuit), and Bob is verifying her proof within his proof. - -With this in mind, it becomes clear that our intermediate proof is the one *meant to be verified within another circuit*, so it must be Alice's. Actually, the only final proof in this theoretical scenario would be the last one, sent on-chain. - -::: - -## Step 3 - Verification and proof artifacts - -Optionally, you are able to verify the intermediate proof: - -```js -const verified = await backend.verifyProof({ proof, publicInputs }) -``` - -This can be useful to make sure our intermediate proof was correctly generated. But the real goal is to do it within another circuit. For that, we need to generate recursive proof artifacts that will be passed to the circuit that is verifying the proof we just generated. Instead of passing the proof and verification key as a byte array, we pass them as fields which makes it cheaper to verify in a circuit: - -```js -const { proofAsFields, vkAsFields, vkHash } = await backend.generateRecursiveProofArtifacts( { publicInputs, proof }, publicInputsCount) -``` - -This call takes the public inputs and the proof, but also the public inputs count. While this is easily retrievable by simply counting the `publicInputs` length, the backend interface doesn't currently abstract it away. - -:::info - -The `proofAsFields` has a constant size `[Field; 93]` and verification keys in Barretenberg are always `[Field; 114]`. - -::: - -:::warning - -One common mistake is to forget *who* makes this call. - -In a situation where Alice is generating the `main` proof, if she generates the proof artifacts and sends them to Bob, which gladly takes them as true, this would mean Alice could prove anything! - -Instead, Bob needs to make sure *he* extracts the proof artifacts, using his own instance of the `main` circuit backend. This way, Alice has to provide a valid proof for the correct `main` circuit. - -::: - -## Step 4 - Recursive proof generation - -With the artifacts, generating a recursive proof is no different from a normal proof. You simply use the `backend` (with the recursive circuit) to generate it: - -```js -const recursiveInputs = { - verification_key: vkAsFields, // array of length 114 - proof: proofAsFields, // array of length 93 + size of public inputs - publicInputs: [mainInput.y], // using the example above, where `y` is the only public input - key_hash: vkHash, -} - -const { witness, returnValue } = noir.execute(recursiveInputs) // we're executing the recursive circuit now! -const { proof, publicInputs } = backend.generateProof(witness) -const verified = backend.verifyProof({ proof, publicInputs }) -``` - -You can obviously chain this proof into another proof. In fact, if you're using recursive proofs, you're probably interested of using them this way! - -:::tip - -Managing circuits and "who does what" can be confusing. To make sure your naming is consistent, you can keep them in an object. For example: - -```js -const circuits = { - main: mainJSON, - recursive: recursiveJSON -} -const backends = { - main: new BarretenbergBackend(circuits.main), - recursive: new BarretenbergBackend(circuits.recursive) -} -const noir_programs = { - main: new Noir(circuits.main), - recursive: new Noir(circuits.recursive) -} -``` - -This allows you to neatly call exactly the method you want without conflicting names: - -```js -// Alice runs this 👇 -const { witness: mainWitness } = await noir_programs.main.execute(input) -const proof = await backends.main.generateProof(mainWitness) - -// Bob runs this 👇 -const verified = await backends.main.verifyProof(proof) -const { proofAsFields, vkAsFields, vkHash } = await backends.main.generateRecursiveProofArtifacts( - proof, - numPublicInputs, -); -const { witness: recursiveWitness } = await noir_programs.recursive.execute(recursiveInputs) -const recursiveProof = await backends.recursive.generateProof(recursiveWitness); -``` - -::: diff --git a/docs/versioned_docs/version-v0.39.0/how_to/how-to-solidity-verifier.md b/docs/versioned_docs/version-v0.39.0/how_to/how-to-solidity-verifier.md deleted file mode 100644 index 2cc0f8e57ce..00000000000 --- a/docs/versioned_docs/version-v0.39.0/how_to/how-to-solidity-verifier.md +++ /dev/null @@ -1,259 +0,0 @@ ---- -title: Generate a 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, - ] -sidebar_position: 0 -pagination_next: tutorials/noirjs_app ---- - -Noir has the ability to generate a verifier contract in Solidity, which can be deployed in many EVM-compatible blockchains such as Ethereum. - -This allows for a powerful feature set, as one can make use of the conciseness and the privacy provided by Noir in an immutable ledger. Applications can range from simple P2P guessing games, to complex private DeFi interactions. - -This guide shows you how to generate a Solidity Verifier and deploy it on the [Remix IDE](https://remix.ethereum.org/). It is assumed that: - -- You are comfortable with the Solidity programming language and understand how contracts are deployed on the Ethereum network -- You have Noir installed and you have a Noir program. If you don't, [get started](../getting_started/quick_start.md) with Nargo and the example Hello Noir circuit -- You are comfortable navigating RemixIDE. If you aren't or you need a refresher, you can find some video tutorials [here](https://www.youtube.com/channel/UCjTUPyFEr2xDGN6Cg8nKDaA) that could help you. - -## Rundown - -Generating a Solidity Verifier contract is actually a one-command process. However, compiling it and deploying it can have some caveats. Here's the rundown of this guide: - -1. How to generate a solidity smart contract -2. How to compile the smart contract in the RemixIDE -3. How to deploy it to a testnet - -## Step 1 - Generate a contract - -This is by far the most straightforward step. Just run: - -```sh -nargo compile -``` - -This will compile your source code into a Noir build artifact to be stored in the `./target` directory, you can then generate the smart contract using the commands: - -```sh -# Here we pass the path to the newly generated Noir artifact. -bb write_vk -b ./target/.json -bb contract -``` - -replacing `` with the name of your Noir project. A new `contract` folder would then be generated in your project directory, containing the Solidity -file `contract.sol`. It can be deployed to any EVM blockchain acting as a verifier smart contract. - -You can find more information about `bb` and the default Noir proving backend on [this page](../getting_started/quick_start.md#proving-backend). - -:::info - -It is possible to generate 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 generation of verifier contracts, for the time being these are only in Solidity. -::: - -## Step 2 - Compiling - -We will mostly skip the details of RemixIDE, as the UI can change from version to version. For now, we can just open -Remix and create a blank workspace. - -![Create Workspace](@site/static/img/how-tos/solidity_verifier_1.png) - -We will create a new file to contain the contract Nargo generated, and copy-paste its content. - -:::warning - -You'll likely see a warning advising you to not trust pasted code. While it is an important warning, it is irrelevant in the context of this guide and can be ignored. We will not be deploying anywhere near a mainnet. - -::: - -To compile our the verifier, we can navigate to the compilation tab: - -![Compilation Tab](@site/static/img/how-tos/solidity_verifier_2.png) - -Remix should automatically match a suitable compiler version. However, hitting the "Compile" button will most likely generate a "Stack too deep" error: - -![Stack too deep](@site/static/img/how-tos/solidity_verifier_3.png) - -This is due to the verify function needing to put many variables on the stack, but enabling the optimizer resolves the issue. To do this, let's open the "Advanced Configurations" tab and enable optimization. The default 200 runs will suffice. - -:::info - -This time we will see a warning about an unused function parameter. This is expected, as the `verify` function doesn't use the `_proof` parameter inside a solidity block, it is loaded from calldata and used in assembly. - -::: - -![Compilation success](@site/static/img/how-tos/solidity_verifier_4.png) - -## Step 3 - Deploying - -At this point we should have a compiled contract ready to deploy. If we navigate to the deploy section in Remix, we will see many different environments we can deploy to. The steps to deploy on each environment would be out-of-scope for this guide, so we will just use the default Remix VM. - -Looking closely, we will notice that our "Solidity Verifier" is actually three contracts working together: - -- An `UltraVerificationKey` library which simply stores the verification key for our circuit. -- An abstract contract `BaseUltraVerifier` containing most of the verifying logic. -- A main `UltraVerifier` contract that inherits from the Base and uses the Key contract. - -Remix will take care of the dependencies for us so we can simply deploy the UltraVerifier contract by selecting it and hitting "deploy": - -![Deploying UltraVerifier](@site/static/img/how-tos/solidity_verifier_5.png) - -A contract will show up in the "Deployed Contracts" section, where we can retrieve the Verification Key Hash. This is particularly useful for double-checking that the deployer contract is the correct one. - -:::note - -Why "UltraVerifier"? - -To be precise, the Noir compiler (`nargo`) doesn't generate the verifier contract directly. It compiles the Noir code into an intermediate language (ACIR), which is then executed by the backend. So it is the backend that returns the verifier smart contract, not Noir. - -In this case, the Barretenberg Backend uses the UltraPlonk proving system, hence the "UltraVerifier" name. - -::: - -## Step 4 - Verifying - -To verify a proof using the Solidity verifier contract, we call the `verify` function in this extended contract: - -```solidity -function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external view returns (bool) -``` - -When using the default example in the [Hello Noir](../getting_started/quick_start.md) guide, the easiest way to confirm that the verifier contract is doing its job is by calling the `verify` function via remix with the required parameters. Note that the public inputs must be passed in separately to the rest of the proof so we must split the proof as returned from `bb`. - -First generate a proof with `bb` at the location `./proof` using the steps in [get started](../getting_started/quick_start.md), this proof is in a binary format but we want to convert it into a hex string to pass into Remix, this can be done with the - -```bash -# This value must be changed to match the number of public inputs (including return values!) in your program. -NUM_PUBLIC_INPUTS=1 -PUBLIC_INPUT_BYTES=32*NUM_PUBLIC_INPUTS -HEX_PUBLIC_INPUTS=$(head -c $PUBLIC_INPUT_BYTES ./proof | od -An -v -t x1 | tr -d $' \n') -HEX_PROOF=$(tail -c +$(($PUBLIC_INPUT_BYTES + 1)) ./proof | od -An -v -t x1 | tr -d $' \n') - -echo "Public inputs:" -echo $HEX_PUBLIC_INPUTS - -echo "Proof:" -echo "0x$HEX_PROOF" -``` - -Remix expects that the public inputs will be split into an array of `bytes32` values so `HEX_PUBLIC_INPUTS` needs to be split up into 32 byte chunks which are prefixed with `0x` accordingly. - -A programmatic example of how the `verify` function is called can be seen 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"); -``` - -:::info[Return Values] - -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. - -For example, if you have Noir program like this: - -```rust -fn main( - // Public inputs - pubkey_x: pub Field, - pubkey_y: pub Field, - // Private inputs - priv_key: Field, -) -> pub Field -``` - -the `verify` function will expect the public inputs array (second function parameter) to be of length 3, the two inputs and the return value. - -Passing only two inputs will result in an error such as `PUBLIC_INPUT_COUNT_INVALID(3, 2)`. - -In this case, the inputs parameter to `verify` would be an array ordered as `[pubkey_x, pubkey_y, return`. - -::: - -:::tip[Structs] - -You can pass structs to the verifier contract. They will be flattened so that the array of inputs is 1-dimensional array. - -For example, 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) { - //... -} -``` - -The order of these inputs would be flattened to: `[x, nested.t1.val1, nested.t1.val2, nested.is_true, y]` - -::: - -The other function you can call is our entrypoint `verify` function, as defined above. - -:::tip - -It's worth noticing that the `verify` function is actually a `view` function. A `view` function does not alter the blockchain state, so it doesn't need to be distributed (i.e. it will run only on the executing node), and therefore doesn't cost any gas. - -This can be particularly useful in some situations. If Alice generated a proof and wants Bob to verify its correctness, Bob doesn't need to run Nargo, NoirJS, or any Noir specific infrastructure. He can simply make a call to the blockchain with the proof and verify it is correct without paying any gas. - -It would be incorrect to say that a Noir proof verification costs any gas at all. However, most of the time the result of `verify` is used to modify state (for example, to update a balance, a game state, etc). In that case the whole network needs to execute it, which does incur gas costs (calldata and execution, but not storage). - -::: - -## A Note on EVM chains - -Noir proof verification requires the ecMul, ecAdd and ecPairing precompiles. Not all EVM chains support EC Pairings, notably some of the ZK-EVMs. This means that you won't be able to use the verifier contract in all of them. You can find an incomplete list of which EVM chains support these precompiles [here](https://www.evmdiff.com/features?feature=precompiles). - -For example, chains like `zkSync ERA` and `Polygon zkEVM` do not currently support these precompiles, so proof verification via Solidity verifier contracts won't work. Here's a quick list of EVM chains that have been tested and are known to work: - -- Optimism -- Arbitrum -- Polygon PoS -- Scroll -- Celo -- BSC -- Blast L2 -- Avalanche C-Chain -- Mode -- Linea -- Moonbeam - -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. - -## What's next - -Now that you know how to call a Noir Solidity Verifier on a smart contract using Remix, you should be comfortable with using it with some programmatic frameworks, such as [hardhat](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat) and [foundry](https://github.com/noir-lang/noir-starter/tree/main/with-foundry). - -You can find other tools, examples, boilerplates and libraries in the [awesome-noir](https://github.com/noir-lang/awesome-noir) repository. - -You should also be ready to write and deploy your first NoirJS app and start generating proofs on websites, phones, and NodeJS environments! Head on to the [NoirJS tutorial](../tutorials/noirjs_app.md) to learn how to do that. diff --git a/docs/versioned_docs/version-v0.39.0/how_to/merkle-proof.mdx b/docs/versioned_docs/version-v0.39.0/how_to/merkle-proof.mdx deleted file mode 100644 index 0a128adb2de..00000000000 --- a/docs/versioned_docs/version-v0.39.0/how_to/merkle-proof.mdx +++ /dev/null @@ -1,48 +0,0 @@ ---- -title: Prove Merkle Tree 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] -sidebar_position: 4 ---- - -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 - -fn main(message : [Field; 62], index : Field, hashpath : [Field; 40], root : Field) { - let leaf = std::hash::hash_to_field(message.as_slice()); - 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.as_slice()); -``` - -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.39.0/how_to/using-devcontainers.mdx b/docs/versioned_docs/version-v0.39.0/how_to/using-devcontainers.mdx deleted file mode 100644 index 727ec6ca667..00000000000 --- a/docs/versioned_docs/version-v0.39.0/how_to/using-devcontainers.mdx +++ /dev/null @@ -1,110 +0,0 @@ ---- -title: Developer Containers and Codespaces -description: "Learn how to set up a devcontainer in your GitHub repository for a seamless coding experience with Codespaces. Follow our easy 8-step guide to create your own Noir environment without installing Nargo locally." -keywords: ["Devcontainer", "Codespaces", "GitHub", "Noir Environment", "Docker Image", "Development Environment", "Remote Coding", "GitHub Codespaces", "Noir Programming", "Nargo", "VSCode Extensions", "Noirup"] -sidebar_position: 1 ---- - -Adding a developer container configuration file to your Noir project is one of the easiest way to unlock coding in browser. - -## What's a devcontainer after all? - -A [Developer Container](https://containers.dev/) (devcontainer for short) is a Docker image that comes preloaded with tools, extensions, and other tools you need to quickly get started or continue a project, without having to install Nargo locally. Think of it as a development environment in a box. - -There are many advantages to this: - -- It's platform and architecture agnostic -- You don't need to have an IDE installed, or Nargo, or use a terminal at all -- It's safer for using on a public machine or public network - -One of the best ways of using devcontainers is... not using your machine at all, for maximum control, performance, and ease of use. -Enter Codespaces. - -## Codespaces - -If a devcontainer is just a Docker image, then what stops you from provisioning a `p3dn.24xlarge` AWS EC2 instance with 92 vCPUs and 768 GiB RAM and using it to prove your 10-gate SNARK proof? - -Nothing! Except perhaps the 30-40$ per hour it will cost you. - -The problem is that provisioning takes time, and I bet you don't want to see the AWS console every time you want to code something real quick. - -Fortunately, there's an easy and free way to get a decent remote machine ready and loaded in less than 2 minutes: Codespaces. [Codespaces is a Github feature](https://github.com/features/codespaces) that allows you to code in a remote machine by using devcontainers, and it's pretty cool: - -- You can start coding Noir in less than a minute -- It uses the resources of a remote machine, so you can code on your grandma's phone if needed be -- It makes it easy to share work with your frens -- It's fully reusable, you can stop and restart whenever you need to - -:::info - -Don't take out your wallet just yet. Free GitHub accounts get about [15-60 hours of coding](https://github.com/features/codespaces) for free per month, depending on the size of your provisioned machine. - -::: - -## Tell me it's _actually_ easy - -It is! - -Github comes with a default codespace and you can use it to code your own devcontainer. That's exactly what we will be doing in this guide. - - - -8 simple steps: - -#### 1. Create a new repository on GitHub. - -#### 2. Click "Start coding with Codespaces". This will use the default image. - -#### 3. Create a folder called `.devcontainer` in the root of your repository. - -#### 4. Create a Dockerfile in that folder, and paste the following code: - -```docker -FROM --platform=linux/amd64 node:lts-bookworm-slim -SHELL ["/bin/bash", "-c"] -RUN apt update && apt install -y curl bash git tar gzip libc++-dev -RUN curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash -ENV PATH="/root/.nargo/bin:$PATH" -RUN noirup -ENTRYPOINT ["nargo"] -``` -#### 5. Create a file called `devcontainer.json` in the same folder, and paste the following code: - -```json -{ - "name": "Noir on Codespaces", - "build": { - "context": ".", - "dockerfile": "Dockerfile" - }, - "customizations": { - "vscode": { - "extensions": ["noir-lang.vscode-noir"] - } - } -} -``` -#### 6. Commit and push your changes - -This will pull the new image and build it, so it could take a minute or so - -#### 8. Done! -Just wait for the build to finish, and there's your easy Noir environment. - - -Refer to [noir-starter](https://github.com/noir-lang/noir-starter/) as an example of how devcontainers can be used together with codespaces. - - - -## How do I use it? - -Using the codespace is obviously much easier than setting it up. -Just navigate to your repository and click "Code" -> "Open with Codespaces". It should take a few seconds to load, and you're ready to go. - -:::info - -If you really like the experience, you can add a badge to your readme, links to existing codespaces, and more. -Check out the [official docs](https://docs.github.com/en/codespaces/setting-up-your-project-for-codespaces/setting-up-your-repository/facilitating-quick-creation-and-resumption-of-codespaces) for more info. diff --git a/docs/versioned_docs/version-v0.39.0/index.mdx b/docs/versioned_docs/version-v0.39.0/index.mdx deleted file mode 100644 index 5c116a73b3f..00000000000 --- a/docs/versioned_docs/version-v0.39.0/index.mdx +++ /dev/null @@ -1,76 +0,0 @@ ---- -title: Noir Lang -hide_title: true -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] -sidebar_position: 0 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; -import ThemedImage from '@theme/ThemedImage'; -import useBaseUrl from '@docusaurus/useBaseUrl'; - - - -Noir is an open-source Domain-Specific Language for safe and seamless construction of privacy-preserving Zero-Knowledge programs, requiring no previous knowledge on the underlying mathematics or cryptography. - -ZK programs are programs that can generate short proofs of statements without revealing all inputs to the statements. You can read more about Zero-Knowledge Proofs [here](https://dev.to/spalladino/a-beginners-intro-to-coding-zero-knowledge-proofs-c56). - -## What's new about Noir? - -Noir works differently from most ZK languages by taking a two-pronged path. First, it compiles the program to an adaptable intermediate language known as ACIR. From there, depending on a given project's needs, ACIR can be further compiled into an arithmetic circuit for integration with the proving backend. - -:::info - -Noir is backend agnostic, which means it makes no assumptions on which proving backend powers the ZK proof. Being the language that powers [Aztec Contracts](https://docs.aztec.network/developers/contracts/main), it defaults to Aztec's Barretenberg proving backend. - -However, the ACIR output can be transformed to be compatible with other PLONK-based backends, or into a [rank-1 constraint system](https://www.rareskills.io/post/rank-1-constraint-system) suitable for backends such as Arkwork's Marlin. - -::: - -## Who is Noir for? - -Noir can be used both in complex cloud-based backends and in user's smartphones, requiring no knowledge on the underlying math or cryptography. From authorization systems that keep a password in the user's device, to complex on-chain verification of recursive proofs, Noir is designed to abstract away complexity without any significant overhead. Here are some examples of situations where Noir can be used: - - - - Noir Logo - - Aztec Contracts leverage Noir to allow for the storage and execution of private information. Writing an Aztec Contract is as easy as writing Noir, and Aztec developers can easily interact with the network storage and execution through the [Aztec.nr](https://docs.aztec.network/developers/contracts/main) library. - - - Soliditry Verifier Example - Noir can auto-generate Solidity verifier contracts that verify Noir proofs. This allows for non-interactive verification of proofs containing private information in an immutable system. This feature powers a multitude of use-case scenarios, from P2P chess tournaments, to [Aztec Layer-2 Blockchain](https://docs.aztec.network/) - - - Aztec Labs developed NoirJS, an easy interface to generate and verify Noir proofs in a Javascript environment. This allows for Noir to be used in webpages, mobile apps, games, and any other environment supporting JS execution in a standalone manner. - - - - -## Libraries - -Noir is meant to be easy to extend by simply importing Noir libraries just like in Rust. -The [awesome-noir repo](https://github.com/noir-lang/awesome-noir#libraries) is a collection of libraries developed by the Noir community. -Writing a new library is easy and makes code be composable and easy to reuse. See the section on [dependencies](noir/modules_packages_crates/dependencies.md) for more information. diff --git a/docs/versioned_docs/version-v0.39.0/migration_notes.md b/docs/versioned_docs/version-v0.39.0/migration_notes.md deleted file mode 100644 index 6bd740024e5..00000000000 --- a/docs/versioned_docs/version-v0.39.0/migration_notes.md +++ /dev/null @@ -1,105 +0,0 @@ ---- -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. - -### `backend encountered an error: libc++.so.1` - -Depending on your OS, you may encounter the following error when running `nargo prove` for the first time: - -```text -The backend encountered an error: "/home/codespace/.nargo/backends/acvm-backend-barretenberg/backend_binary: error while loading shared libraries: libc++.so.1: cannot open shared object file: No such file or directory\n" -``` - -Install the `libc++-dev` library with: - -```bash -sudo apt install libc++-dev -``` - -## ≥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](noir/concepts/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 your 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 file is running [this bash script](https://github.com/noir-lang/barretenberg-js-binary/blob/master/run-bb-js.sh), 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.39.0/noir/concepts/_category_.json b/docs/versioned_docs/version-v0.39.0/noir/concepts/_category_.json deleted file mode 100644 index 7da08f8a8c5..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/concepts/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "Concepts", - "position": 0, - "collapsible": true, - "collapsed": true -} \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.39.0/noir/concepts/assert.md b/docs/versioned_docs/version-v0.39.0/noir/concepts/assert.md deleted file mode 100644 index 2132de42072..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/concepts/assert.md +++ /dev/null @@ -1,78 +0,0 @@ ---- -title: Assert Function -description: - Learn about the `assert` and `static_assert` functions 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 or compile-time, respectively. -keywords: [Noir programming language, assert statement, predicate expression, comparison expression] -sidebar_position: 4 ---- - -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); -} -``` - -> 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`. - -You can optionally provide a message to be logged when the assertion fails: - -```rust -assert(x == y, "x and y are not equal"); -``` - -Aside string literals, the optional message can be a format string or any other type supported as input for Noir's [print](../standard_library/logging.md) functions. This feature lets you incorporate runtime variables into your failed assertion logs: - -```rust -assert(x == y, f"Expected x == y, but got {x} == {y}"); -``` - -Using a variable as an assertion message directly: - -```rust -struct myStruct { - myField: Field -} - -let s = myStruct { myField: y }; -assert(s.myField == x, s); -``` - -There is also a special `static_assert` function that behaves like `assert`, -but that runs at compile-time. - -```rust -fn main(xs: [Field; 3]) { - let x = 2 + 2; - let y = 4; - static_assert(x == y, "expected 2 + 2 to equal 4"); - - // This passes since the length of `xs` is known at compile-time - static_assert(xs.len() == 3, "expected the input to have 3 elements"); -} -``` - -This function fails when passed a dynamic (run-time) argument: - -```rust -fn main(x : Field, y : Field) { - // this fails because `x` is not known at compile-time - static_assert(x == 2, "expected x to be known at compile-time and equal to 2"); - - let mut example_slice = &[]; - if y == 4 { - example_slice = example_slice.push_back(0); - } - - // This fails because the length of `example_slice` is not known at - // compile-time - let error_message = "expected an empty slice, known at compile-time"; - static_assert(example_slice.len() == 0, error_message); -} -``` - diff --git a/docs/versioned_docs/version-v0.39.0/noir/concepts/comments.md b/docs/versioned_docs/version-v0.39.0/noir/concepts/comments.md deleted file mode 100644 index b51a85f5c94..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/concepts/comments.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -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] -sidebar_position: 10 ---- - -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.39.0/noir/concepts/comptime.md b/docs/versioned_docs/version-v0.39.0/noir/concepts/comptime.md deleted file mode 100644 index 2ceb030c7e1..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/concepts/comptime.md +++ /dev/null @@ -1,445 +0,0 @@ ---- -title: Compile-time Code & Metaprogramming -description: Learn how to use metaprogramming in Noir to create macros or derive your own traits -keywords: [Noir, comptime, compile-time, metaprogramming, macros, quote, unquote] -sidebar_position: 15 ---- - -## Overview - -Metaprogramming in Noir is comprised of three parts: -1. `comptime` code -2. Quoting and unquoting -3. The metaprogramming API in `std::meta` - -Each of these are explained in more detail in the next sections but the wide picture is that -`comptime` allows us to write code which runs at compile-time. In this `comptime` code we -can quote and unquote snippets of the program, manipulate them, and insert them in other -parts of the program. Comptime functions which do this are said to be macros. Additionally, -there's a compile-time API of built-in types and functions provided by the compiler which allows -for greater analysis and modification of programs. - ---- - -## Comptime - -`comptime` is a new keyword in Noir which marks an item as executing or existing at compile-time. It can be used in several ways: - -- `comptime fn` to define functions which execute exclusively during compile-time. -- `comptime global` to define a global variable which is evaluated at compile-time. - - Unlike runtime globals, `comptime global`s can be mutable. -- `comptime { ... }` to execute a block of statements during compile-time. -- `comptime let` to define a variable whose value is evaluated at compile-time. -- `comptime for` to run a for loop at compile-time. Syntax sugar for `comptime { for .. }`. - -### Scoping - -Note that while in a `comptime` context, any runtime variables _local to the current function_ are never visible. - -### Evaluating - -Evaluation rules of `comptime` follows the normal unconstrained evaluation rules for other Noir code. There are a few things to note though: - -- Certain built-in functions may not be available, although more may be added over time. -- Evaluation order of global items is currently unspecified. For example, given the following two functions we can't guarantee -which `println` will execute first. The ordering of the two printouts will be arbitrary, but should be stable across multiple compilations with the same `nargo` version as long as the program is also unchanged. - -```rust -fn one() { - comptime { println("one"); } -} - -fn two() { - comptime { println("two"); } -} -``` - -- Since evaluation order is unspecified, care should be taken when using mutable globals so that they do not rely on a particular ordering. -For example, using globals to generate unique ids should be fine but relying on certain ids always being produced (especially after edits to the program) should be avoided. -- Although most ordering of globals is unspecified, two are: - - Dependencies of a crate will always be evaluated before the dependent crate. - - Any annotations on a function will be run before the function itself is resolved. This is to allow the annotation to modify the function if necessary. Note that if the - function itself was called at compile-time previously, it will already be resolved and cannot be modified. To prevent accidentally calling functions you wish to modify - at compile-time, it may be helpful to sort your `comptime` annotation functions into a different crate along with any dependencies they require. - -### Lowering - -When a `comptime` value is used in runtime code it must be lowered into a runtime value. This means replacing the expression with the literal that it evaluated to. For example, the code: - -```rust -struct Foo { array: [Field; 2], len: u32 } - -fn main() { - println(comptime { - let mut foo = std::mem::zeroed::(); - foo.array[0] = 4; - foo.len = 1; - foo - }); -} -``` - -will be converted to the following after `comptime` expressions are evaluated: - -```rust -struct Foo { array: [Field; 2], len: u32 } - -fn main() { - println(Foo { array: [4, 0], len: 1 }); -} -``` - -Not all types of values can be lowered. For example, `Type`s and `TypeDefinition`s (among other types) cannot be lowered at all. - -```rust -fn main() { - // There's nothing we could inline here to create a Type value at runtime - // let _ = get_type!(); -} - -comptime fn get_type() -> Type { ... } -``` - ---- - -## (Quasi) Quote - -Macros in Noir are `comptime` functions which return code as a value which is inserted into the call site when it is lowered there. -A code value in this case is of type `Quoted` and can be created by a `quote { ... }` expression. -More specifically, the code value `quote` creates is a token stream - a representation of source code as a series of words, numbers, string literals, or operators. -For example, the expression `quote { Hi "there reader"! }` would quote three tokens: the word "hi", the string "there reader", and an exclamation mark. -You'll note that snippets that would otherwise be invalid syntax can still be quoted. - -When a `Quoted` value is used in runtime code, it is lowered into a `quote { ... }` expression. Since this expression is only valid -in compile-time code however, we'd get an error if we tried this. Instead, we can use macro insertion to insert each token into the -program at that point, and parse it as an expression. To do this, we have to add a `!` after the function name returning the `Quoted` value. -If the value was created locally and there is no function returning it, `std::meta::unquote!(_)` can be used instead. -Calling such a function at compile-time without `!` will just return the `Quoted` value to be further manipulated. For example: - -```rust title="quote-example" showLineNumbers -comptime fn quote_one() -> Quoted { - quote { 1 } - } - - #[test] - fn returning_versus_macro_insertion() { - comptime { - // let _a: Quoted = quote { 1 }; - let _a: Quoted = quote_one(); - - // let _b: Field = 1; - let _b: Field = quote_one!(); - - // Since integers default to fields, if we - // want a different type we have to explicitly cast - // let _c: i32 = 1 as i32; - let _c: i32 = quote_one!() as i32; - } - } -``` -> Source code: noir_stdlib/src/meta/mod.nr#L120-L140 - - -For those familiar with quoting from other languages (primarily lisps), Noir's `quote` is actually a _quasiquote_. -This means we can escape the quoting by using the unquote operator to splice values in the middle of quoted code. - -## Unquote - -The unquote operator `$` is usable within a `quote` expression. -It takes a variable as an argument, evaluates the variable, and splices the resulting value into the quoted token stream at that point. For example, - -```rust -comptime { - let x = 1 + 2; - let y = quote { $x + 4 }; -} -``` - -The value of `y` above will be the token stream containing `3`, `+`, and `4`. We can also use this to combine `Quoted` values into larger token streams: - -```rust -comptime { - let x = quote { 1 + 2 }; - let y = quote { $x + 4 }; -} -``` - -The value of `y` above is now the token stream containing five tokens: `1 + 2 + 4`. - -Note that to unquote something, a variable name _must_ follow the `$` operator in a token stream. -If it is an expression (even a parenthesized one), it will do nothing. Most likely a parse error will be given when the macro is later unquoted. - -Unquoting can also be avoided by escaping the `$` with a backslash: - -``` -comptime { - let x = quote { 1 + 2 }; - - // y contains the four tokens: `$x + 4` - let y = quote { \$x + 4 }; -} -``` - ---- - -## Annotations - -Annotations provide a way to run a `comptime` function on an item in the program. -When you use an annotation, the function with the same name will be called with that item as an argument: - -```rust -#[my_struct_annotation] -struct Foo {} - -comptime fn my_struct_annotation(s: StructDefinition) { - println("Called my_struct_annotation!"); -} - -#[my_function_annotation] -fn foo() {} - -comptime fn my_function_annotation(f: FunctionDefinition) { - println("Called my_function_annotation!"); -} -``` - -Anything returned from one of these functions will be inserted at top-level along with the original item. -Note that expressions are not valid at top-level so you'll get an error trying to return `3` or similar just as if you tried to write a program containing `3; struct Foo {}`. -You can insert other top-level items such as trait impls, structs, or functions this way though. -For example, this is the mechanism used to insert additional trait implementations into the program when deriving a trait impl from a struct: - -```rust title="derive-field-count-example" showLineNumbers -trait FieldCount { - fn field_count() -> u32; - } - - #[derive_field_count] - struct Bar { - x: Field, - y: [Field; 2], - } - - comptime fn derive_field_count(s: StructDefinition) -> Quoted { - let typ = s.as_type(); - let field_count = s.fields().len(); - quote { - impl FieldCount for $typ { - fn field_count() -> u32 { - $field_count - } - } - } - } -``` -> Source code: noir_stdlib/src/meta/mod.nr#L142-L164 - - -### Calling annotations with additional arguments - -Arguments may optionally be given to annotations. -When this is done, these additional arguments are passed to the annotation function after the item argument. - -```rust title="annotation-arguments-example" showLineNumbers -#[assert_field_is_type(quote { i32 }.as_type())] - struct MyStruct { - my_field: i32, - } - - comptime fn assert_field_is_type(s: StructDefinition, typ: Type) { - // Assert the first field in `s` has type `typ` - let fields = s.fields(); - assert_eq(fields[0].1, typ); - } -``` -> Source code: noir_stdlib/src/meta/mod.nr#L166-L177 - - -We can also take any number of arguments by adding the `varargs` annotation: - -```rust title="annotation-varargs-example" showLineNumbers -#[assert_three_args(1, 2, 3)] - struct MyOtherStruct { - my_other_field: u32, - } - - #[varargs] - comptime fn assert_three_args(_s: StructDefinition, args: [Field]) { - assert_eq(args.len(), 3); - } -``` -> Source code: noir_stdlib/src/meta/mod.nr#L179-L189 - - ---- - -## Comptime API - -Although `comptime`, `quote`, and unquoting provide a flexible base for writing macros, -Noir's true metaprogramming ability comes from being able to interact with the compiler through a compile-time API. -This API can be accessed through built-in functions in `std::meta` as well as on methods of several `comptime` types. - -The following is an incomplete list of some `comptime` types along with some useful methods on them. You can see more in the standard library [Metaprogramming section](../standard_library/meta). - -- `Quoted`: A token stream -- `Type`: The type of a Noir type - - `fn implements(self, constraint: TraitConstraint) -> bool` - - Returns true if `self` implements the given trait constraint -- `Expr`: A syntactically valid expression. Can be used to recur on a program's parse tree to inspect how it is structured. - - Methods: - - `fn as_function_call(self) -> Option<(Expr, [Expr])>` - - If this is a function call expression, return `(function, arguments)` - - `fn as_block(self) -> Option<[Expr]>` - - If this is a block, return each statement in the block -- `FunctionDefinition`: A function definition - - Methods: - - `fn parameters(self) -> [(Quoted, Type)]` - - Returns a slice of `(name, type)` pairs for each parameter -- `StructDefinition`: A struct definition - - Methods: - - `fn as_type(self) -> Type` - - Returns this `StructDefinition` as a `Type`. Any generics are kept as-is - - `fn generics(self) -> [Quoted]` - - Return the name of each generic on this struct - - `fn fields(self) -> [(Quoted, Type)]` - - Return the name and type of each field -- `TraitConstraint`: A trait constraint such as `From` -- `TypedExpr`: A type-checked expression. -- `UnresolvedType`: A syntactic notation that refers to a Noir type that hasn't been resolved yet - -There are many more functions available by exploring the `std::meta` module and its submodules. -Using these methods is the key to writing powerful metaprogramming libraries. - -### `#[use_callers_scope]` - -Since certain functions such as `Quoted::as_type`, `Expression::as_type`, or `Quoted::as_trait_constraint` will attempt -to resolve their contents in a particular scope - it can be useful to change the scope they resolve in. By default -these functions will resolve in the current function's scope which is usually the attribute function they are called in. -If you're working on a library however, this may be a completely different module or crate to the item you're trying to -use the attribute on. If you want to be able to use `Quoted::as_type` to refer to types local to the caller's scope for -example, you can annotate your attribute function with `#[use_callers_scope]`. This will ensure your attribute, and any -closures it uses, can refer to anything in the caller's scope. `#[use_callers_scope]` also works recursively. So if both -your attribute function and a helper function it calls use it, then they can both refer to the same original caller. - ---- - -## Example: Derive - -Using all of the above, we can write a `derive` macro that behaves similarly to Rust's but is not built into the language. -From the user's perspective it will look like this: - -```rust -// Example usage -#[derive(Default, Eq, Ord)] -struct MyStruct { my_field: u32 } -``` - -To implement `derive` we'll have to create a `comptime` function that accepts -a variable amount of traits. - -```rust title="derive_example" showLineNumbers -// These are needed for the unconstrained hashmap we're using to store derive functions -use crate::collections::umap::UHashMap; -use crate::hash::BuildHasherDefault; -use crate::hash::poseidon2::Poseidon2Hasher; - -// A derive function is one that given a struct definition can -// create us a quoted trait impl from it. -pub type DeriveFunction = fn(StructDefinition) -> Quoted; - -// We'll keep a global HANDLERS map to keep track of the derive handler for each trait -comptime mut global HANDLERS: UHashMap> = - UHashMap::default(); - -// Given a struct and a slice of traits to derive, create trait impls for each. -// This function is as simple as iterating over the slice, checking if we have a trait -// handler registered for the given trait, calling it, and appending the result. -#[varargs] -pub comptime fn derive(s: StructDefinition, traits: [TraitDefinition]) -> Quoted { - let mut result = quote {}; - - for trait_to_derive in traits { - let handler = unsafe { HANDLERS.get(trait_to_derive) }; - assert(handler.is_some(), f"No derive function registered for `{trait_to_derive}`"); - - let trait_impl = handler.unwrap()(s); - result = quote { $result $trait_impl }; - } - - result -} -``` -> Source code: noir_stdlib/src/meta/mod.nr#L31-L64 - - -Registering a derive function could be done as follows: - -```rust title="derive_via" showLineNumbers -// To register a handler for a trait, just add it to our handlers map -pub comptime fn derive_via(t: TraitDefinition, f: DeriveFunction) { - HANDLERS.insert(t, f); -} -``` -> Source code: noir_stdlib/src/meta/mod.nr#L66-L73 - - -```rust title="big-derive-usage-example" showLineNumbers -// Finally, to register a handler we call the above function as an annotation - // with our handler function. - #[derive_via(derive_do_nothing)] - trait DoNothing { - fn do_nothing(self); - } - - comptime fn derive_do_nothing(s: StructDefinition) -> Quoted { - // This is simplified since we don't handle generics or where clauses! - // In a real example we'd likely also need to introduce each of - // `s.generics()` as well as a trait constraint for each generic - // to ensure they also implement the trait. - let typ = s.as_type(); - quote { - impl DoNothing for $typ { - fn do_nothing(self) { - // Traits can't tell us what to do - println("something"); - } - } - } - } - - // Since `DoNothing` is a simple trait which: - // 1. Only has one method - // 2. Does not have any generics on the trait itself - // We can use `std::meta::make_trait_impl` to help us out. - // This helper function will generate our impl for us along with any - // necessary where clauses and still provides a flexible interface - // for us to work on each field on the struct. - comptime fn derive_do_nothing_alt(s: StructDefinition) -> Quoted { - let trait_name = quote { DoNothing }; - let method_signature = quote { fn do_nothing(self) }; - - // Call `do_nothing` recursively on each field in the struct - let for_each_field = |field_name| quote { self.$field_name.do_nothing(); }; - - // Some traits like Eq want to join each field expression with something like `&`. - // We don't need that here - let join_fields_with = quote {}; - - // The body function is a spot to insert any extra setup/teardown needed. - // We'll insert our println here. Since we recur on each field, we should see - // one println for the struct itself, followed by a println for every field (recursively). - let body = |body| quote { - println("something"); - $body - }; - crate::meta::make_trait_impl( - s, - trait_name, - method_signature, - for_each_field, - join_fields_with, - body, - ) - } -``` -> Source code: noir_stdlib/src/meta/mod.nr#L191-L249 - diff --git a/docs/versioned_docs/version-v0.39.0/noir/concepts/control_flow.md b/docs/versioned_docs/version-v0.39.0/noir/concepts/control_flow.md deleted file mode 100644 index b365bb22728..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/concepts/control_flow.md +++ /dev/null @@ -1,79 +0,0 @@ ---- -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] -sidebar_position: 2 ---- - -## 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); -``` - -## 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 -} -``` - -Alternatively, `start..=end` can be used for a range that is inclusive on both ends. - -The index for loops is of type `u64`. - -### Break and Continue - -In unconstrained code, `break` and `continue` are also allowed in `for` loops. These are only allowed -in unconstrained code since normal constrained code requires that Noir knows exactly how many iterations -a loop may have. `break` and `continue` can be used like so: - -```rust -for i in 0 .. 10 { - println("Iteration start") - - if i == 2 { - continue; - } - - if i == 5 { - break; - } - - println(i); -} -println("Loop end") -``` - -When used, `break` will end the current loop early and jump to the statement after the for loop. In the example -above, the `break` will stop the loop and jump to the `println("Loop end")`. - -`continue` will stop the current iteration of the loop, and jump to the start of the next iteration. In the example -above, `continue` will jump to `println("Iteration start")` when used. Note that the loop continues as normal after this. -The iteration variable `i` is still increased by one as normal when `continue` is used. - -`break` and `continue` cannot currently be used to jump out of more than a single loop at a time. diff --git a/docs/versioned_docs/version-v0.39.0/noir/concepts/data_bus.mdx b/docs/versioned_docs/version-v0.39.0/noir/concepts/data_bus.mdx deleted file mode 100644 index e55e58622ce..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/concepts/data_bus.mdx +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: Data Bus -sidebar_position: 13 ---- -import Experimental from '@site/src/components/Notes/_experimental.mdx'; - - - -The data bus is an optimization that the backend can use to make recursion more efficient. -In order to use it, you must define some inputs of the program entry points (usually the `main()` -function) with the `call_data` modifier, and the return values with the `return_data` modifier. -These modifiers are incompatible with `pub` and `mut` modifiers. - -## Example - -```rust -fn main(mut x: u32, y: call_data u32, z: call_data [u32;4] ) -> return_data u32 { - let a = z[x]; - a+y -} -``` - -As a result, both call_data and return_data will be treated as private inputs and encapsulated into a read-only array each, for the backend to process. diff --git a/docs/versioned_docs/version-v0.39.0/noir/concepts/data_types/_category_.json b/docs/versioned_docs/version-v0.39.0/noir/concepts/data_types/_category_.json deleted file mode 100644 index 5d694210bbf..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/concepts/data_types/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "position": 0, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.39.0/noir/concepts/data_types/arrays.md b/docs/versioned_docs/version-v0.39.0/noir/concepts/data_types/arrays.md deleted file mode 100644 index 289145a8c4d..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/concepts/data_types/arrays.md +++ /dev/null @@ -1,276 +0,0 @@ ---- -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, - ] -sidebar_position: 4 ---- - -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.mdx), 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]; -``` - -However, multidimensional slices are not supported. For example, the following code will error at compile time: - -```rust -let slice : [[Field]] = &[]; -``` - -## 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. -Each of these functions are located within the generic impl `impl [T; N] {`. -So anywhere `self` appears, it refers to the variable `self: [T; N]`. - -### len - -Returns the length of an array - -```rust -fn len(self) -> 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(self) -> [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. The ordering function must return true if the first argument should be sorted to be before the second argument or is equal to the second argument. - -Using this method with an operator like `<` that does not return `true` for equal values will result in an assertion failure for arrays with equal elements. - -```rust -fn sort_via(self, 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(self, 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(self, 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 the starting element. - -Requires `self` to be non-empty. - -```rust -fn reduce(self, 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(self, 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(self, 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); -} -``` - -### as_str_unchecked - -Converts a byte array of type `[u8; N]` to a string. Note that this performs no UTF-8 validation - -the given array is interpreted as-is as a string. - -```rust -impl [u8; N] { - pub fn as_str_unchecked(self) -> str -} -``` - -example: - -```rust -fn main() { - let hi = [104, 105].as_str_unchecked(); - assert_eq(hi, "hi"); -} -``` diff --git a/docs/versioned_docs/version-v0.39.0/noir/concepts/data_types/booleans.md b/docs/versioned_docs/version-v0.39.0/noir/concepts/data_types/booleans.md deleted file mode 100644 index 2507af710e7..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/concepts/data_types/booleans.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -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, - ] -sidebar_position: 2 ---- - - -The `bool` type in Noir has two possible values: `true` and `false`: - -```rust -fn main() { - let t = true; - let f: bool = false; -} -``` - -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.md) and -[Assert Function](../assert.md) sections. diff --git a/docs/versioned_docs/version-v0.39.0/noir/concepts/data_types/fields.md b/docs/versioned_docs/version-v0.39.0/noir/concepts/data_types/fields.md deleted file mode 100644 index b9b56f7ecc3..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/concepts/data_types/fields.md +++ /dev/null @@ -1,246 +0,0 @@ ---- -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, - ] -sidebar_position: 0 ---- - -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 title="to_le_bits" showLineNumbers -pub fn to_le_bits(self: Self) -> [u1; N] {} -``` -> Source code: noir_stdlib/src/field/mod.nr#L32-L34 - - -example: - -```rust title="to_le_bits_example" showLineNumbers -fn test_to_le_bits() { - let field = 2; - let bits: [u1; 8] = field.to_le_bits(); - assert_eq(bits, [0, 1, 0, 0, 0, 0, 0, 0]); - } -``` -> Source code: noir_stdlib/src/field/mod.nr#L276-L282 - - - -### to_be_bits - -Transforms the field into an array of bits, Big Endian. - -```rust title="to_be_bits" showLineNumbers -pub fn to_be_bits(self: Self) -> [u1; N] {} -``` -> Source code: noir_stdlib/src/field/mod.nr#L48-L50 - - -example: - -```rust title="to_be_bits_example" showLineNumbers -fn test_to_be_bits() { - let field = 2; - let bits: [u1; 8] = field.to_be_bits(); - assert_eq(bits, [0, 0, 0, 0, 0, 0, 1, 0]); - } -``` -> Source code: noir_stdlib/src/field/mod.nr#L267-L273 - - - -### to_le_bytes - -Transforms into an array of bytes, Little Endian - -```rust title="to_le_bytes" showLineNumbers -pub fn to_le_bytes(self: Self) -> [u8; N] { -``` -> Source code: noir_stdlib/src/field/mod.nr#L61-L63 - - -example: - -```rust title="to_le_bytes_example" showLineNumbers -fn test_to_le_bytes() { - let field = 2; - let bytes: [u8; 8] = field.to_le_bytes(); - assert_eq(bytes, [2, 0, 0, 0, 0, 0, 0, 0]); - assert_eq(Field::from_le_bytes::<8>(bytes), field); - } -``` -> Source code: noir_stdlib/src/field/mod.nr#L295-L302 - - -### to_be_bytes - -Transforms into an array of bytes, Big Endian - -```rust title="to_be_bytes" showLineNumbers -pub fn to_be_bytes(self: Self) -> [u8; N] { -``` -> Source code: noir_stdlib/src/field/mod.nr#L94-L96 - - -example: - -```rust title="to_be_bytes_example" showLineNumbers -fn test_to_be_bytes() { - let field = 2; - let bytes: [u8; 8] = field.to_be_bytes(); - assert_eq(bytes, [0, 0, 0, 0, 0, 0, 0, 2]); - assert_eq(Field::from_be_bytes::<8>(bytes), field); - } -``` -> Source code: noir_stdlib/src/field/mod.nr#L285-L292 - - - -### to_le_radix - -Decomposes into an array over the specified base, Little Endian - -```rust title="to_le_radix" showLineNumbers -pub fn to_le_radix(self: Self, radix: u32) -> [u8; N] { - // Brillig does not need an immediate radix - if !crate::runtime::is_unconstrained() { - crate::assert_constant(radix); - } - self.__to_le_radix(radix) - } -``` -> Source code: noir_stdlib/src/field/mod.nr#L118-L126 - - - -example: - -```rust title="to_le_radix_example" showLineNumbers -fn test_to_le_radix() { - let field = 2; - let bytes: [u8; 8] = field.to_le_radix(256); - assert_eq(bytes, [2, 0, 0, 0, 0, 0, 0, 0]); - assert_eq(Field::from_le_bytes::<8>(bytes), field); - } -``` -> Source code: noir_stdlib/src/field/mod.nr#L315-L322 - - - -### to_be_radix - -Decomposes into an array over the specified base, Big Endian - -```rust title="to_be_radix" showLineNumbers -pub fn to_be_radix(self: Self, radix: u32) -> [u8; N] { - // Brillig does not need an immediate radix - if !crate::runtime::is_unconstrained() { - crate::assert_constant(radix); - } - self.__to_be_radix(radix) - } -``` -> Source code: noir_stdlib/src/field/mod.nr#L128-L136 - - -example: - -```rust title="to_be_radix_example" showLineNumbers -fn test_to_be_radix() { - let field = 2; - let bytes: [u8; 8] = field.to_be_radix(256); - assert_eq(bytes, [0, 0, 0, 0, 0, 0, 0, 2]); - assert_eq(Field::from_be_bytes::<8>(bytes), field); - } -``` -> Source code: noir_stdlib/src/field/mod.nr#L305-L312 - - - -### 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); -} -``` - -### assert_max_bit_size - -Adds a constraint to specify that the field can be represented with `bit_size` number of bits - -```rust title="assert_max_bit_size" showLineNumbers -pub fn assert_max_bit_size(self) { -``` -> Source code: noir_stdlib/src/field/mod.nr#L10-L12 - - -example: - -```rust -fn main() { - let field = 2 - field.assert_max_bit_size(32); -} -``` - -### 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 -``` - - -### lt - -Returns true if the field is less than the other field - -```rust -pub fn lt(self, another: Field) -> bool -``` diff --git a/docs/versioned_docs/version-v0.39.0/noir/concepts/data_types/function_types.md b/docs/versioned_docs/version-v0.39.0/noir/concepts/data_types/function_types.md deleted file mode 100644 index f6121af17e2..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/concepts/data_types/function_types.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: Function types -sidebar_position: 10 ---- - -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](../lambdas.md) for more details. diff --git a/docs/versioned_docs/version-v0.39.0/noir/concepts/data_types/index.md b/docs/versioned_docs/version-v0.39.0/noir/concepts/data_types/index.md deleted file mode 100644 index 0f2db2b2d75..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/concepts/data_types/index.md +++ /dev/null @@ -1,126 +0,0 @@ ---- -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](../generics.md): - -```rust -type Id = Size; - -fn main() { - let id: Id = 1; - let zero: u32 = 0; - assert(zero + 1 == id); -} -``` - -Type aliases can even refer to other aliases. An error will be issued if they form a cycle: - -```rust -// Ok! -type A = B; -type B = Field; - -type Bad1 = Bad2; - -// error: Dependency cycle found -type Bad2 = Bad1; -// ^^^^^^^^^^^ 'Bad2' recursively depends on itself: Bad2 -> Bad1 -> Bad2 -``` - -By default, like functions, type aliases are private to the module they exist in. You can use `pub` -to make the type alias public or `pub(crate)` to make it public to just its crate: - -```rust -// This type alias is now public -pub type Id = u8; -``` - -## Wildcard Type -Noir can usually infer the type of the variable from the context, so specifying the type of a variable is only required when it cannot be inferred. However, specifying a complex type can be tedious, especially when it has multiple generic arguments. Often some of the generic types can be inferred from the context, and Noir only needs a hint to properly infer the other types. We can partially specify a variable's type by using `_` as a marker, indicating where we still want the compiler to infer the type. - -```rust -let a: [_; 4] = foo(b); -``` - - -### BigInt - -You can achieve BigInt functionality using the [Noir BigInt](https://github.com/shuklaayush/noir-bigint) library. diff --git a/docs/versioned_docs/version-v0.39.0/noir/concepts/data_types/integers.md b/docs/versioned_docs/version-v0.39.0/noir/concepts/data_types/integers.md deleted file mode 100644 index a1d59bf3166..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/concepts/data_types/integers.md +++ /dev/null @@ -1,156 +0,0 @@ ---- -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] -sidebar_position: 1 ---- - -An integer type is a range constrained field type. -The Noir frontend supports both unsigned and signed integer types. -The allowed sizes are 1, 8, 16, 32 and 64 bits. - -:::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\\$). - -## 128 bits Unsigned Integers - -The built-in structure `U128` allows you to use 128-bit unsigned integers almost like a native integer type. However, there are some differences to keep in mind: -- You cannot cast between a native integer and `U128` -- There is a higher performance cost when using `U128`, compared to a native type. - -Conversion between unsigned integer types and U128 are done through the use of `from_integer` and `to_integer` functions. `from_integer` also accepts the `Field` type as input. - -```rust -fn main() { - let x = U128::from_integer(23); - let y = U128::from_hex("0x7"); - let z = x + y; - assert(z.to_integer() == 30); -} -``` - -`U128` is implemented with two 64 bits limbs, representing the low and high bits, which explains the performance cost. You should expect `U128` to be twice more costly for addition and four times more costly for multiplication. -You can construct a U128 from its limbs: -```rust -fn main(x: u64, y: u64) { - let x = U128::from_u64s_be(x,y); - assert(z.hi == x as Field); - assert(z.lo == y as Field); -} -``` - -Note that the limbs are stored as Field elements in order to avoid unnecessary conversions. -Apart from this, most operations will work as usual: - -```rust -fn main(x: U128, y: U128) { - // multiplication - let c = x * y; - // addition and subtraction - let c = c - x + y; - // division - let c = x / y; - // bit operation; - let c = x & y | y; - // bit shift - let c = x << y; - // comparisons; - let c = x < y; - let c = x == y; -} -``` - -## 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 execute -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 - -fn main(x: u8, y: u8) -> pub u8 { - std::wrapping_add(x, y) -} -``` diff --git a/docs/versioned_docs/version-v0.39.0/noir/concepts/data_types/references.md b/docs/versioned_docs/version-v0.39.0/noir/concepts/data_types/references.md deleted file mode 100644 index a5293d11cfb..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/concepts/data_types/references.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: References -sidebar_position: 9 ---- - -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.39.0/noir/concepts/data_types/slices.mdx b/docs/versioned_docs/version-v0.39.0/noir/concepts/data_types/slices.mdx deleted file mode 100644 index cfee564a302..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/concepts/data_types/slices.mdx +++ /dev/null @@ -1,358 +0,0 @@ ---- -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] -sidebar_position: 5 ---- - -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 -fn main() -> pub u32 { - let mut slice: [Field] = &[0; 2]; - - let mut new_slice = slice.push_back(6); - new_slice.len() -} -``` - -To write a slice literal, use a preceding ampersand as in: `&[0; 2]` or -`&[1, 2, 3]`. - -It is important to note that slices are not references to arrays. In Noir, -`&[..]` is more similar to an immutable, growable vector. - -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); -``` - -### len - -Returns the length of a slice - -```rust -fn len(self) -> Field -``` - -Example: - -```rust -fn main() { - let slice = &[42, 42]; - assert(slice.len() == 2); -} -``` - -### as_array - -Converts this slice into an array. - -Make sure to specify the size of the resulting array. -Panics if the resulting array length is different than the slice's length. - -```rust -fn as_array(self) -> [T; N] -``` - -Example: - -```rust -fn main() { - let slice = &[5, 6]; - - // Always specify the length of the resulting array! - let array: [Field; 2] = slice.as_array(); - - assert(array[0] == slice[0]); - assert(array[1] == slice[1]); -} -``` - -### map - -Applies a function to each element of the slice, returning a new slice containing the mapped elements. - -```rust -fn map(self, f: fn[Env](T) -> U) -> [U] -``` - -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 slice, returning the final accumulated value. The first -parameter is the initial value. - -```rust -fn fold(self, mut accumulator: U, f: fn[Env](U, T) -> U) -> U -``` - -This is a left fold, so the given function will be applied to the accumulator and first element of -the slice, 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 slice = &[2, 2, 2, 2, 2]; - let folded = slice.fold(0, |a, b| a + b); - assert(folded == 10); -} - -``` - -### reduce - -Same as fold, but uses the first element as the starting element. - -```rust -fn reduce(self, f: fn[Env](T, T) -> T) -> T -``` - -example: - -```rust -fn main() { - let slice = &[2, 2, 2, 2, 2]; - let reduced = slice.reduce(|a, b| a + b); - assert(reduced == 10); -} -``` - -### filter - -Returns a new slice containing only elements for which the given predicate returns true. - -```rust -fn filter(self, f: fn[Env](T) -> bool) -> Self -``` - -example: - -```rust -fn main() { - let slice = &[1, 2, 3, 4, 5]; - let odds = slice.filter(|x| x % 2 == 1); - assert_eq(odds, &[1, 3, 5]); -} -``` - -### join - -Flatten each element in the slice into one value, separated by `separator`. - -Note that although slices implement `Append`, `join` cannot be used on slice -elements since nested slices are prohibited. - -```rust -fn join(self, separator: T) -> T where T: Append -``` - -example: - -```rust -struct Accumulator { - total: Field, -} - -// "Append" two accumulators by adding them -impl Append for Accumulator { - fn empty() -> Self { - Self { total: 0 } - } - - fn append(self, other: Self) -> Self { - Self { total: self.total + other.total } - } -} - -fn main() { - let slice = &[1, 2, 3, 4, 5].map(|total| Accumulator { total }); - - let result = slice.join(Accumulator::empty()); - assert_eq(result, Accumulator { total: 15 }); - - // We can use a non-empty separator to insert additional elements to sum: - let separator = Accumulator { total: 10 }; - let result = slice.join(separator); - assert_eq(result, Accumulator { total: 55 }); -} -``` - -### all - -Returns true if all the elements satisfy the given predicate - -```rust -fn all(self, predicate: fn[Env](T) -> bool) -> bool -``` - -example: - -```rust -fn main() { - let slice = &[2, 2, 2, 2, 2]; - let all = slice.all(|a| a == 2); - assert(all); -} -``` - -### any - -Returns true if any of the elements satisfy the given predicate - -```rust -fn any(self, predicate: fn[Env](T) -> bool) -> bool -``` - -example: - -```rust -fn main() { - let slice = &[2, 2, 2, 2, 5]; - let any = slice.any(|a| a == 5); - assert(any); -} - -``` diff --git a/docs/versioned_docs/version-v0.39.0/noir/concepts/data_types/strings.md b/docs/versioned_docs/version-v0.39.0/noir/concepts/data_types/strings.md deleted file mode 100644 index 1fdee42425e..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/concepts/data_types/strings.md +++ /dev/null @@ -1,79 +0,0 @@ ---- -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, - ] -sidebar_position: 3 ---- - - -The string type is a fixed length value defined with `str`. - -You can use strings in `assert()` functions or print them with -`println()`. See more about [Logging](../../standard_library/logging.md). - -```rust - -fn main(message : pub str<11>, hex_as_string : str<4>) { - 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" -``` - -## 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/versioned_docs/version-v0.39.0/noir/concepts/data_types/structs.md b/docs/versioned_docs/version-v0.39.0/noir/concepts/data_types/structs.md deleted file mode 100644 index 29951ae843a..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/concepts/data_types/structs.md +++ /dev/null @@ -1,96 +0,0 @@ ---- -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, - ] -sidebar_position: 8 ---- - -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. - -### Visibility - -By default, like functions, structs are private to the module they exist in. You can use `pub` -to make the struct public or `pub(crate)` to make it public to just its crate: - -```rust -// This struct is now public -pub struct Animal { - hands: Field, - legs: Field, - eyes: u8, -} -``` - -The same applies to struct fields: by default they are private to the module they exist in, -but they can be made `pub` or `pub(crate)`: - -```rust -// This struct is now public -pub struct Animal { - hands: Field, // private to its module - pub(crate) legs: Field, // accessible from the entire crate - pub eyes: u8, // accessible from anywhere -} -``` \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.39.0/noir/concepts/data_types/tuples.md b/docs/versioned_docs/version-v0.39.0/noir/concepts/data_types/tuples.md deleted file mode 100644 index 2ec5c9c4113..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/concepts/data_types/tuples.md +++ /dev/null @@ -1,48 +0,0 @@ ---- -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, - ] -sidebar_position: 7 ---- - -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.39.0/noir/concepts/functions.md b/docs/versioned_docs/version-v0.39.0/noir/concepts/functions.md deleted file mode 100644 index f656cdfd97a..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/concepts/functions.md +++ /dev/null @@ -1,226 +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] -sidebar_position: 1 ---- - -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](../../tooling/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](./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](./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](./unconstrained.md) and [NoirJS](../../reference/NoirJS/noir_js/index.md) for more details. -- **test**: mark the function as unit tests. See [Tests](../../tooling/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.39.0/noir/concepts/generics.md b/docs/versioned_docs/version-v0.39.0/noir/concepts/generics.md deleted file mode 100644 index c180a0ce7e6..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/concepts/generics.md +++ /dev/null @@ -1,251 +0,0 @@ ---- -title: Generics -description: Learn how to use Generics in Noir -keywords: [Noir, Rust, generics, functions, structs] -sidebar_position: 7 ---- - -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 -} -``` - -## Numeric Generics - -If we want to be generic over array lengths (which are type-level integers), we can use numeric -generics. Using these looks similar to using regular generics, but introducing them into scope -requires declaring them with `let MyGenericName: IntegerType`. This can be done anywhere a normal -generic is declared. Instead of types, these generics resolve to integers at compile-time. -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 - } -} -``` - -## 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 print(self) { - for _i in 0 .. self.count { - println(self.value); - } - } -} - -fn main() { - let repeated = RepeatedValue { value: "Hello!", count: 2 }; - repeated.print(); -} -``` - -The `print` function will print `Hello!` an arbitrary number of times, twice in this case. - -## Calling functions on generic parameters - -Since a generic type `T` can represent any type, how can we call functions on the underlying type? -In other words, how can we go from "any type `T`" to "any type `T` that has certain methods available?" - -This is what [traits](../concepts/traits.md) are for in Noir. Here's an example of a function generic over -any type `T` that implements the `Eq` trait for equality: - -```rust -fn first_element_is_equal(array1: [T; N], array2: [T; N]) -> bool - where T: Eq -{ - if (array1.len() == 0) | (array2.len() == 0) { - true - } else { - array1[0] == array2[0] - } -} - -fn main() { - assert(first_element_is_equal([1, 2, 3], [1, 5, 6])); - - // We can use first_element_is_equal for arrays of any type - // as long as we have an Eq impl for the types we pass in - let array = [MyStruct::new(), MyStruct::new()]; - assert(array_eq(array, array, MyStruct::eq)); -} - -impl Eq for MyStruct { - fn eq(self, other: MyStruct) -> bool { - self.foo == other.foo - } -} -``` - -You can find more details on traits and trait implementations on the [traits page](../concepts/traits.md). - -## Manually Specifying Generics with the Turbofish Operator - -There are times when the compiler cannot reasonably infer what type should be used for a generic, or when the developer themselves may want to manually distinguish generic type parameters. This is where the `::<>` turbofish operator comes into play. - -The `::<>` operator can follow a variable or path and can be used to manually specify generic arguments within the angle brackets. -The name "turbofish" comes from that `::<>` looks like a little fish. - -Examples: -```rust -fn main() { - let mut slice = []; - slice = slice.push_back(1); - slice = slice.push_back(2); - // Without turbofish a type annotation would be needed on the left hand side - let array = slice.as_array::<2>(); -} -``` - - -```rust -trait MyTrait { - fn ten() -> Self; -} - -impl MyTrait for Field { - fn ten() -> Self { 10 } -} - -struct Foo { - inner: T -} - -impl Foo { - fn generic_method(_self: Self) -> U where U: MyTrait { - U::ten() - } -} - -fn example() { - let foo: Foo = Foo { inner: 1 }; - // Using a type other than `Field` here (e.g. u32) would fail as - // there is no matching impl for `u32: MyTrait`. - // - // Substituting the `10` on the left hand side of this assert - // with `10 as u32` would also fail with a type mismatch as we - // are expecting a `Field` from the right hand side. - assert(10 as u32 == foo.generic_method::()); -} -``` - -## Arithmetic Generics - -In addition to numeric generics, Noir also allows a limited form of arithmetic on generics. -When you have a numeric generic such as `N`, you can use the following operators on it in a -type position: `+`, `-`, `*`, `/`, and `%`. - -Note that type checking arithmetic generics is a best effort guess from the compiler and there -are many cases of types that are equal that the compiler may not see as such. For example, -we know that `T * (N + M)` should be equal to `T*N + T*M` but the compiler does not currently -apply the distributive law and thus sees these as different types. - -Even with this limitation though, the compiler can handle common cases decently well: - -```rust -trait Serialize { - fn serialize(self) -> [Field; N]; -} - -impl Serialize<1> for Field { - fn serialize(self) -> [Field; 1] { - [self] - } -} - -impl Serialize for [T; N] - where T: Serialize { .. } - -impl Serialize for (T, U) - where T: Serialize, U: Serialize { .. } - -fn main() { - let data = (1, [2, 3, 4]); - assert_eq(data.serialize().len(), 4); -} -``` - -Note that if there is any over or underflow the types will fail to unify: - -```rust title="underflow-example" showLineNumbers -fn pop(array: [Field; N]) -> [Field; N - 1] { - let mut result: [Field; N - 1] = std::mem::zeroed(); - for i in 0..N - 1 { - result[i] = array[i]; - } - result -} - -fn main() { - // error: Could not determine array length `(0 - 1)` - pop([]); -} -``` -> Source code: test_programs/compile_failure/arithmetic_generics_underflow/src/main.nr#L1-L14 - - -This also applies if there is underflow in an intermediate calculation: - -```rust title="intermediate-underflow-example" showLineNumbers -fn main() { - // From main it looks like there's nothing sketchy going on - seems_fine([]); -} - -// Since `seems_fine` says it can receive and return any length N -fn seems_fine(array: [Field; N]) -> [Field; N] { - // But inside `seems_fine` we pop from the array which - // requires the length to be greater than zero. - - // error: Could not determine array length `(0 - 1)` - push_zero(pop(array)) -} - -fn pop(array: [Field; N]) -> [Field; N - 1] { - let mut result: [Field; N - 1] = std::mem::zeroed(); - for i in 0..N - 1 { - result[i] = array[i]; - } - result -} - -fn push_zero(array: [Field; N]) -> [Field; N + 1] { - let mut result: [Field; N + 1] = std::mem::zeroed(); - for i in 0..N { - result[i] = array[i]; - } - // index N is already zeroed - result -} -``` -> Source code: test_programs/compile_failure/arithmetic_generics_intermediate_underflow/src/main.nr#L1-L32 - diff --git a/docs/versioned_docs/version-v0.39.0/noir/concepts/globals.md b/docs/versioned_docs/version-v0.39.0/noir/concepts/globals.md deleted file mode 100644 index 6b8314399a2..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/concepts/globals.md +++ /dev/null @@ -1,82 +0,0 @@ ---- -title: Global Variables -description: - Learn about global variables in Noir. Discover how - to declare, modify, and use them in your programs. -keywords: [noir programming language, globals, global variables, constants] -sidebar_position: 8 ---- - -## Globals - - -Noir supports global variables. The global's type can be inferred by the compiler entirely: - -```rust -global N = 5; // Same as `global N: Field = 5` - -global TUPLE = (3, 2); - -fn main() { - assert(N == 5); - assert(N == TUPLE.0 + TUPLE.1); -} -``` - -:::info - -Globals can be defined as any expression, so long as they don't depend on themselves - otherwise there would be a dependency cycle! For example: - -```rust -global T = foo(T); // dependency error -``` - -::: - - -If they are initialized to a literal integer, globals can be used to specify an array's length: - -```rust -global N: u32 = 2; - -fn main(y : [Field; N]) { - assert(y[0] == y[1]) -} -``` - -A global from another module can be imported or referenced externally like any other name: - -```rust -global N = 20; - -fn main() { - assert(my_submodule::N != N); -} - -mod my_submodule { - global N: Field = 10; -} -``` - -When a global is used, Noir replaces the name with its definition on each occurrence. -This means globals defined using function calls will repeat the call each time they're used: - -```rust -global RESULT = foo(); - -fn foo() -> [Field; 100] { ... } -``` - -This is usually fine since Noir will generally optimize any function call that does not -refer to a program input into a constant. It should be kept in mind however, if the called -function performs side-effects like `println`, as these will still occur on each use. - -### Visibility - -By default, like functions, globals are private to the module they exist in. You can use `pub` -to make the global public or `pub(crate)` to make it public to just its crate: - -```rust -// This global is now public -pub global N = 5; -``` \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.39.0/noir/concepts/lambdas.md b/docs/versioned_docs/version-v0.39.0/noir/concepts/lambdas.md deleted file mode 100644 index be3c7e0b5ca..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/concepts/lambdas.md +++ /dev/null @@ -1,81 +0,0 @@ ---- -title: Lambdas -description: Learn how to use anonymous functions in Noir programming language. -keywords: [Noir programming language, lambda, closure, function, anonymous function] -sidebar_position: 9 ---- - -## 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.39.0/noir/concepts/mutability.md b/docs/versioned_docs/version-v0.39.0/noir/concepts/mutability.md deleted file mode 100644 index fdeef6a87c5..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/concepts/mutability.md +++ /dev/null @@ -1,121 +0,0 @@ ---- -title: Mutability -description: - Learn about mutable variables in Noir. Discover how - to declare, modify, and use them in your programs. -keywords: [noir programming language, mutability in noir, mutable variables] -sidebar_position: 8 ---- - -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; -} -``` - -## Non-local mutability - -Non-local mutability can be achieved through the mutable reference type `&mut T`: - -```rust -fn set_to_zero(x: &mut Field) { - *x = 0; -} - -fn main() { - let mut y = 42; - set_to_zero(&mut y); - assert(*y == 0); -} -``` - -When creating a mutable reference, the original variable being referred to (`y` in this -example) must also be mutable. Since mutable references are a reference type, they must -be explicitly dereferenced via `*` to retrieve the underlying value. Note that this yields -a copy of the value, so mutating this copy will not change the original value behind the -reference: - -```rust -fn main() { - let mut x = 1; - let x_ref = &mut x; - - let mut y = *x_ref; - let y_ref = &mut y; - - x = 2; - *x_ref = 3; - - y = 4; - *y_ref = 5; - - assert(x == 3); - assert(*x_ref == 3); - assert(y == 5); - assert(*y_ref == 5); -} -``` - -Note that types in Noir are actually deeply immutable so the copy that occurs when -dereferencing is only a conceptual copy - no additional constraints will occur. - -Mutable references can also be stored within structs. Note that there is also -no lifetime parameter on these unlike rust. This is because the allocated memory -always lasts the entire program - as if it were an array of one element. - -```rust -struct Foo { - x: &mut Field -} - -impl Foo { - fn incr(mut self) { - *self.x += 1; - } -} - -fn main() { - let foo = Foo { x: &mut 0 }; - foo.incr(); - assert(*foo.x == 1); -} -``` - -In general, you should avoid non-local & shared mutability unless it is needed. Sticking -to only local mutability will improve readability and potentially improve compiler optimizations as well. diff --git a/docs/versioned_docs/version-v0.39.0/noir/concepts/ops.md b/docs/versioned_docs/version-v0.39.0/noir/concepts/ops.md deleted file mode 100644 index c35c36c38a9..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/concepts/ops.md +++ /dev/null @@ -1,98 +0,0 @@ ---- -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, - ] -sidebar_position: 3 ---- - -# 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, shift must be u8 | -| >> | Right shift an integer by another integer amount | Types must be integer, shift must be u8 | -| ! | 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 identically 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.39.0/noir/concepts/oracles.mdx b/docs/versioned_docs/version-v0.39.0/noir/concepts/oracles.mdx deleted file mode 100644 index 77a2ac1550a..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/concepts/oracles.mdx +++ /dev/null @@ -1,29 +0,0 @@ ---- -title: Oracles -description: Dive into how Noir supports Oracles via RPC calls, and learn how to declare an Oracle in Noir with our comprehensive guide. -keywords: - - Noir - - Oracles - - RPC Calls - - Unconstrained Functions - - Programming - - Blockchain -sidebar_position: 6 ---- - -import Experimental from '@site/src/components/Notes/_experimental.mdx'; - - - -Noir has support for Oracles via RPC calls. This means Noir will make an RPC call and use the return value for proof generation. - -Since Oracles are not resolved by Noir, they are [`unconstrained` functions](./unconstrained.md) - -You can declare an Oracle through the `#[oracle()]` flag. Example: - -```rust -#[oracle(get_number_sequence)] -unconstrained fn get_number_sequence(_size: Field) -> [Field] {} -``` - -The timeout for when using an external RPC oracle resolver can be set with the `NARGO_FOREIGN_CALL_TIMEOUT` environment variable. This timeout is in units of milliseconds. diff --git a/docs/versioned_docs/version-v0.39.0/noir/concepts/shadowing.md b/docs/versioned_docs/version-v0.39.0/noir/concepts/shadowing.md deleted file mode 100644 index 5ce6130d201..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/concepts/shadowing.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -title: Shadowing -sidebar_position: 12 ---- - -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.39.0/noir/concepts/traits.md b/docs/versioned_docs/version-v0.39.0/noir/concepts/traits.md deleted file mode 100644 index 9da00a77587..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/concepts/traits.md +++ /dev/null @@ -1,501 +0,0 @@ ---- -title: Traits -description: - Traits in Noir can be used to abstract out a common interface for functions across - several data types. -keywords: [noir programming language, traits, interfaces, generic, protocol] -sidebar_position: 14 ---- - -## Overview - -Traits in Noir are a useful abstraction similar to interfaces or protocols in other languages. Each trait defines -the interface of several methods contained within the trait. Types can then implement this trait by providing -implementations for these methods. For example in the program: - -```rust -struct Rectangle { - width: Field, - height: Field, -} - -impl Rectangle { - fn area(self) -> Field { - self.width * self.height - } -} - -fn log_area(r: Rectangle) { - println(r.area()); -} -``` - -We have a function `log_area` to log the area of a `Rectangle`. Now how should we change the program if we want this -function to work on `Triangle`s as well?: - -```rust -struct Triangle { - width: Field, - height: Field, -} - -impl Triangle { - fn area(self) -> Field { - self.width * self.height / 2 - } -} -``` - -Making `log_area` generic over all types `T` would be invalid since not all types have an `area` method. Instead, we can -introduce a new `Area` trait and make `log_area` generic over all types `T` that implement `Area`: - -```rust -trait Area { - fn area(self) -> Field; -} - -fn log_area(shape: T) where T: Area { - println(shape.area()); -} -``` - -We also need to explicitly implement `Area` for `Rectangle` and `Triangle`. We can do that by changing their existing -impls slightly. Note that the parameter types and return type of each of our `area` methods must match those defined -by the `Area` trait. - -```rust -impl Area for Rectangle { - fn area(self) -> Field { - self.width * self.height - } -} - -impl Area for Triangle { - fn area(self) -> Field { - self.width * self.height / 2 - } -} -``` - -Now we have a working program that is generic over any type of Shape that is used! Others can even use this program -as a library with their own types - such as `Circle` - as long as they also implement `Area` for these types. - -## Where Clauses - -As seen in `log_area` above, when we want to create a function or method that is generic over any type that implements -a trait, we can add a where clause to the generic function. - -```rust -fn log_area(shape: T) where T: Area { - println(shape.area()); -} -``` - -It is also possible to apply multiple trait constraints on the same variable at once by combining traits with the `+` -operator. Similarly, we can have multiple trait constraints by separating each with a comma: - -```rust -fn foo(elements: [T], thing: U) where - T: Default + Add + Eq, - U: Bar, -{ - let mut sum = T::default(); - - for element in elements { - sum += element; - } - - if sum == T::default() { - thing.bar(); - } -} -``` - -## Generic Implementations - -You can add generics to a trait implementation by adding the generic list after the `impl` keyword: - -```rust -trait Second { - fn second(self) -> Field; -} - -impl Second for (T, Field) { - fn second(self) -> Field { - self.1 - } -} -``` - -You can also implement a trait for every type this way: - -```rust -trait Debug { - fn debug(self); -} - -impl Debug for T { - fn debug(self) { - println(self); - } -} - -fn main() { - 1.debug(); -} -``` - -### Generic Trait Implementations With Where Clauses - -Where clauses can be placed on trait implementations themselves to restrict generics in a similar way. -For example, while `impl Foo for T` implements the trait `Foo` for every type, `impl Foo for T where T: Bar` -will implement `Foo` only for types that also implement `Bar`. This is often used for implementing generic types. -For example, here is the implementation for array equality: - -```rust -impl Eq for [T; let N: u32] where T: Eq { - // Test if two arrays have the same elements. - // Because both arrays must have length N, we know their lengths already match. - fn eq(self, other: Self) -> bool { - let mut result = true; - - for i in 0 .. self.len() { - // The T: Eq constraint is needed to call == on the array elements here - result &= self[i] == other[i]; - } - - result - } -} -``` - -Where clauses can also be placed on struct implementations. -For example, here is a method utilizing a generic type that implements the equality trait. - -```rust -struct Foo { - a: u32, - b: T, -} - -impl Foo where T: Eq { - fn eq(self, other: Self) -> bool { - (self.a == other.a) & self.b.eq(other.b) - } -} -``` - -## Generic Traits - -Traits themselves can also be generic by placing the generic arguments after the trait name. These generics are in -scope of every item within the trait. - -```rust -trait Into { - // Convert `self` to type `T` - fn into(self) -> T; -} -``` - -When implementing generic traits the generic arguments of the trait must be specified. This is also true anytime -when referencing a generic trait (e.g. in a `where` clause). - -```rust -struct MyStruct { - array: [Field; 2], -} - -impl Into<[Field; 2]> for MyStruct { - fn into(self) -> [Field; 2] { - self.array - } -} - -fn as_array(x: T) -> [Field; 2] - where T: Into<[Field; 2]> -{ - x.into() -} - -fn main() { - let array = [1, 2]; - let my_struct = MyStruct { array }; - - assert_eq(as_array(my_struct), array); -} -``` - -### Associated Types and Constants - -Traits also support associated types and constraints which can be thought of as additional generics that are referred to by name. - -Here's an example of a trait with an associated type `Foo` and a constant `Bar`: - -```rust -trait MyTrait { - type Foo; - - let Bar: u32; -} -``` - -Now when we're implementing `MyTrait` we also have to provide values for `Foo` and `Bar`: - -```rust -impl MyTrait for Field { - type Foo = i32; - - let Bar: u32 = 11; -} -``` - -Since associated constants can also be used in a type position, its values are limited to only other -expression kinds allowed in numeric generics. - -Note that currently all associated types and constants must be explicitly specified in a trait constraint. -If we leave out any, we'll get an error that we're missing one: - -```rust -// Error! Constraint is missing associated constant for `Bar` -fn foo(x: T) where T: MyTrait { - ... -} -``` - -Because all associated types and constants must be explicitly specified, they are essentially named generics, -although this is set to change in the future. Future versions of Noir will allow users to elide associated types -in trait constraints similar to Rust. When this is done, you may still refer to their value with the `::AssociatedType` -syntax: - -```rust -// Only valid in future versions of Noir: -fn foo(x: T) where T: MyTrait { - let _: ::Foo = ...; -} -``` - -The type as trait syntax is possible in Noir today but is less useful when each type must be explicitly specified anyway: - -```rust -fn foo(x: T) where T: MyTrait { - // Works, but could just use F directly - let _: >::Foo = ...; - - let _: F = ...; -} -``` - -## Trait Methods With No `self` - -A trait can contain any number of methods, each of which have access to the `Self` type which represents each type -that eventually implements the trait. Similarly, the `self` variable is available as well but is not required to be used. -For example, we can define a trait to create a default value for a type. This trait will need to return the `Self` type -but doesn't need to take any parameters: - -```rust -trait Default { - fn default() -> Self; -} -``` - -Implementing this trait can be done similarly to any other trait: - -```rust -impl Default for Field { - fn default() -> Field { - 0 - } -} - -struct MyType {} - -impl Default for MyType { - fn default() -> Field { - MyType {} - } -} -``` - -However, since there is no `self` parameter, we cannot call it via the method call syntax `object.method()`. -Instead, we'll need to refer to the function directly. This can be done either by referring to the -specific impl `MyType::default()` or referring to the trait itself `Default::default()`. In the later -case, type inference determines the impl that is selected. - -```rust -let my_struct = MyStruct::default(); - -let x: Field = Default::default(); -let result = x + Default::default(); -``` - -:::warning - -```rust -let _ = Default::default(); -``` - -If type inference cannot select which impl to use because of an ambiguous `Self` type, an impl will be -arbitrarily selected. This occurs most often when the result of a trait function call with no parameters -is unused. To avoid this, when calling a trait function with no `self` or `Self` parameters or return type, -always refer to it via the implementation type's namespace - e.g. `MyType::default()`. -This is set to change to an error in future Noir versions. - -::: - -## Default Method Implementations - -A trait can also have default implementations of its methods by giving a body to the desired functions. -Note that this body must be valid for all types that may implement the trait. As a result, the only -valid operations on `self` will be operations valid for any type or other operations on the trait itself. - -```rust -trait Numeric { - fn add(self, other: Self) -> Self; - - // Default implementation of double is (self + self) - fn double(self) -> Self { - self.add(self) - } -} -``` - -When implementing a trait with default functions, a type may choose to implement only the required functions: - -```rust -impl Numeric for Field { - fn add(self, other: Field) -> Field { - self + other - } -} -``` - -Or it may implement the optional methods as well: - -```rust -impl Numeric for u32 { - fn add(self, other: u32) -> u32 { - self + other - } - - fn double(self) -> u32 { - self * 2 - } -} -``` - -## Impl Specialization - -When implementing traits for a generic type it is possible to implement the trait for only a certain combination -of generics. This can be either as an optimization or because those specific generics are required to implement the trait. - -```rust -trait Sub { - fn sub(self, other: Self) -> Self; -} - -struct NonZero { - value: T, -} - -impl Sub for NonZero { - fn sub(self, other: Self) -> Self { - let value = self.value - other.value; - assert(value != 0); - NonZero { value } - } -} -``` - -## Overlapping Implementations - -Overlapping implementations are disallowed by Noir to ensure Noir's decision on which impl to select is never ambiguous. -This means if a trait `Foo` is already implemented -by a type `Bar` for all `T`, then we cannot also have a separate impl for `Bar` (or any other -type argument). Similarly, if there is an impl for all `T` such as `impl Debug for T`, we cannot create -any more impls to `Debug` for other types since it would be ambiguous which impl to choose for any given -method call. - -```rust -trait Trait {} - -// Previous impl defined here -impl Trait for (A, B) {} - -// error: Impl for type `(Field, Field)` overlaps with existing impl -impl Trait for (Field, Field) {} -``` - -## Trait Coherence - -Another restriction on trait implementations is coherence. This restriction ensures other crates cannot create -impls that may overlap with other impls, even if several unrelated crates are used as dependencies in the same -program. - -The coherence restriction is: to implement a trait, either the trait itself or the object type must be declared -in the crate the impl is in. - -In practice this often comes up when using types provided by libraries. If a library provides a type `Foo` that does -not implement a trait in the standard library such as `Default`, you may not `impl Default for Foo` in your own crate. -While restrictive, this prevents later issues or silent changes in the program if the `Foo` library later added its -own impl for `Default`. If you are a user of the `Foo` library in this scenario and need a trait not implemented by the -library your choices are to either submit a patch to the library or use the newtype pattern. - -### The Newtype Pattern - -The newtype pattern gets around the coherence restriction by creating a new wrapper type around the library type -that we cannot create `impl`s for. Since the new wrapper type is defined in our current crate, we can create -impls for any trait we need on it. - -```rust -struct Wrapper { - foo: some_library::Foo, -} - -impl Default for Wrapper { - fn default() -> Wrapper { - Wrapper { - foo: some_library::Foo::new(), - } - } -} -``` - -Since we have an impl for our own type, the behavior of this code will not change even if `some_library` is updated -to provide its own `impl Default for Foo`. The downside of this pattern is that it requires extra wrapping and -unwrapping of values when converting to and from the `Wrapper` and `Foo` types. - -### Trait Inheritance - -Sometimes, you might need one trait to use another trait’s functionality (like "inheritance" in some other languages). In this case, you can specify this relationship by listing any child traits after the parent trait's name and a colon. Now, whenever the parent trait is implemented it will require the child traits to be implemented as well. A parent trait is also called a "super trait." - -```rust -trait Person { - fn name(self) -> String; -} - -// Person is a supertrait of Student. -// Implementing Student requires you to also impl Person. -trait Student: Person { - fn university(self) -> String; -} - -trait Programmer { - fn fav_language(self) -> String; -} - -// CompSciStudent (computer science student) is a subtrait of both Programmer -// and Student. Implementing CompSciStudent requires you to impl both supertraits. -trait CompSciStudent: Programmer + Student { - fn git_username(self) -> String; -} -``` - -### Visibility - -By default, like functions, traits are private to the module they exist in. You can use `pub` -to make the trait public or `pub(crate)` to make it public to just its crate: - -```rust -// This trait is now public -pub trait Trait {} -``` \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.39.0/noir/concepts/unconstrained.md b/docs/versioned_docs/version-v0.39.0/noir/concepts/unconstrained.md deleted file mode 100644 index b5221b8d2dd..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/concepts/unconstrained.md +++ /dev/null @@ -1,104 +0,0 @@ ---- -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] -sidebar_position: 5 ---- - -Unconstrained functions are functions which do not constrain any of the included computation and allow for non-deterministic 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 AND 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 = unsafe { - 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). - -Note that in order to invoke unconstrained functions we need to wrap them in an `unsafe` block, -to make it clear that the call is unconstrained. - -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. - -## Break and Continue - -In addition to loops over runtime bounds, `break` and `continue` are also available in unconstrained code. See [break and continue](../concepts/control_flow.md#break-and-continue) diff --git a/docs/versioned_docs/version-v0.39.0/noir/modules_packages_crates/_category_.json b/docs/versioned_docs/version-v0.39.0/noir/modules_packages_crates/_category_.json deleted file mode 100644 index 1debcfe7675..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/modules_packages_crates/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "Modules, Packages and Crates", - "position": 2, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.39.0/noir/modules_packages_crates/crates_and_packages.md b/docs/versioned_docs/version-v0.39.0/noir/modules_packages_crates/crates_and_packages.md deleted file mode 100644 index 95ee9f52ab2..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/modules_packages_crates/crates_and_packages.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -title: Crates and Packages -description: Learn how to use Crates and Packages in your Noir project -keywords: [Nargo, dependencies, package management, crates, package] -sidebar_position: 0 ---- - -## 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/noir-projects/noir-contracts/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.39.0/noir/modules_packages_crates/dependencies.md b/docs/versioned_docs/version-v0.39.0/noir/modules_packages_crates/dependencies.md deleted file mode 100644 index 24e02de08fe..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/modules_packages_crates/dependencies.md +++ /dev/null @@ -1,124 +0,0 @@ ---- -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] -sidebar_position: 1 ---- - -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 = "noir-contracts/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 -└── lib_a - ├── Nargo.toml - └── src - └── lib.nr -``` - -Inside of the binary crate, you can specify: - -```toml -# Nargo.toml - -[dependencies] -lib_a = { path = "../lib_a" } -``` - -## 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 lib_a referenced above: - -```rust -use ecrecover; -use lib_a; -``` - -You can also import only the specific parts of dependency that you want to use, like so: - -```rust -use std::hash::sha256; -use std::scalar_mul::fixed_base_embedded_curve; -``` - -Lastly, as demonstrated in the -[elliptic curve example](../standard_library/cryptographic_primitives/ec_primitives.md#examples), you -can import multiple items in the same line by enclosing them in curly braces: - -```rust -use std::ec::tecurve::affine::{Curve, Point}; -``` - -We don't have a way to consume libraries from inside a [workspace](./workspaces.md) 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 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.39.0/noir/modules_packages_crates/modules.md b/docs/versioned_docs/version-v0.39.0/noir/modules_packages_crates/modules.md deleted file mode 100644 index 14aa1f0579a..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/modules_packages_crates/modules.md +++ /dev/null @@ -1,221 +0,0 @@ ---- -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] -sidebar_position: 2 ---- - -Noir's module system follows the same convention as the _newer_ version of Rust's module system. - -## Purpose of Modules - -Modules are used to organize 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 - -``` - -The module filename may also be the name of the module as a directory with the contents in a -file named `mod.nr` within that directory. The above example can alternatively be expressed like this: - -Filename : `src/main.nr` - -```rust -mod foo; - -fn main() { - foo::hello_world(); -} -``` - -Filename : `src/foo/mod.nr` - -```rust -fn from_foo() {} -``` - -Note that it's an error to have both files `src/foo.nr` and `src/foo/mod.nr` in the filesystem. - -### 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 -``` - -Similar to importing a module in the crate root, modules can be placed in a `mod.nr` file, like this: - -Filename : `src/main.nr` - -```rust -mod foo; - -fn main() { - foo::from_foo(); -} -``` - -Filename : `src/foo/mod.nr` - -```rust -mod bar; -fn from_foo() {} -``` - -Filename : `src/foo/bar/mod.nr` - -```rust -fn from_bar() {} -``` - -### Referencing a parent module - -Given a submodule, you can refer to its parent module using the `super` keyword. - -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 -// Same as bar::from_foo -use super::from_foo; - -fn from_bar() { - from_foo(); // invokes super::from_foo(), which is bar::from_foo() - super::from_foo(); // also invokes bar::from_foo() -} -``` - -### `use` visibility - -`use` declarations are private to the containing module, by default. However, like functions, -they can be marked as `pub` or `pub(crate)`. Such a use declaration serves to _re-export_ a name. -A public `use` declaration can therefore redirect some public name to a different target definition: -even a definition with a private canonical path, inside a different module. - -An example of re-exporting: - -```rust -mod some_module { - pub use foo::{bar, baz}; - mod foo { - pub fn bar() {} - pub fn baz() {} - } -} - -fn main() { - some_module::bar(); - some_module::baz(); -} -``` - -In this example, the module `some_module` re-exports two public names defined in `foo`. - -### Visibility - -By default, like functions, modules are private to the module (or crate) they exist in. You can use `pub` -to make the module public or `pub(crate)` to make it public to just its crate: - -```rust -// This module is now public and can be seen by other crates. -pub mod foo; -``` \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.39.0/noir/modules_packages_crates/workspaces.md b/docs/versioned_docs/version-v0.39.0/noir/modules_packages_crates/workspaces.md deleted file mode 100644 index 513497f12bf..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/modules_packages_crates/workspaces.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -title: Workspaces -sidebar_position: 3 ---- - -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 -│ │ └── Prover.toml -│ │ └── src -│ │ └── main.nr -│ └── b -│ ├── Nargo.toml -│ └── Prover.toml -│ └── src -│ └── main.nr -│ -└── Nargo.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.39.0/noir/standard_library/_category_.json b/docs/versioned_docs/version-v0.39.0/noir/standard_library/_category_.json deleted file mode 100644 index af04c0933fd..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/standard_library/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "Standard Library", - "position": 1, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.39.0/noir/standard_library/bigint.md b/docs/versioned_docs/version-v0.39.0/noir/standard_library/bigint.md deleted file mode 100644 index 05c3011634f..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/standard_library/bigint.md +++ /dev/null @@ -1,127 +0,0 @@ ---- -title: Big Integers -description: How to use big integers from Noir standard library -keywords: - [ - Big Integer, - Noir programming language, - Noir libraries, - ] ---- - -The BigInt module in the standard library exposes some class of integers which do not fit (well) into a Noir native field. It implements modulo arithmetic, modulo a 'big' prime number. - -:::note - -The module can currently be considered as `Field`s with fixed modulo sizes used by a set of elliptic curves, in addition to just the native curve. [More work](https://github.com/noir-lang/noir/issues/510) is needed to achieve arbitrarily sized big integers. - -:::note - -`nargo` can be built with `--profile release-pedantic` to enable extra overflow checks which may affect `BigInt` results in some cases. -Consider the [`noir-bignum`](https://github.com/noir-lang/noir-bignum) library for an optimized alternative approach. - -::: - -Currently 6 classes of integers (i.e 'big' prime numbers) are available in the module, namely: - -- BN254 Fq: Bn254Fq -- BN254 Fr: Bn254Fr -- Secp256k1 Fq: Secpk1Fq -- Secp256k1 Fr: Secpk1Fr -- Secp256r1 Fr: Secpr1Fr -- Secp256r1 Fq: Secpr1Fq - -Where XXX Fq and XXX Fr denote respectively the order of the base and scalar field of the (usual) elliptic curve XXX. -For instance the big integer 'Secpk1Fq' in the standard library refers to integers modulo $2^{256}-2^{32}-977$. - -Feel free to explore the source code for the other primes: - -```rust title="big_int_definition" showLineNumbers -pub struct BigInt { - pointer: u32, - modulus: u32, -} -``` -> Source code: noir_stdlib/src/bigint.nr#L28-L33 - - -## Example usage - -A common use-case is when constructing a big integer from its bytes representation, and performing arithmetic operations on it: - -```rust title="big_int_example" showLineNumbers -fn big_int_example(x: u8, y: u8) { - let a = Secpk1Fq::from_le_bytes(&[x, y, 0, 45, 2]); - let b = Secpk1Fq::from_le_bytes(&[y, x, 9]); - let c = (a + b) * b / a; - let d = c.to_le_bytes(); - println(d[0]); -} -``` -> Source code: test_programs/execution_success/bigint/src/main.nr#L74-L82 - - -## Methods - -The available operations for each big integer are: - -### from_le_bytes - -Construct a big integer from its little-endian bytes representation. Example: - -```rust - // Construct a big integer from a slice of bytes - let a = Secpk1Fq::from_le_bytes(&[x, y, 0, 45, 2]); - // Construct a big integer from an array of 32 bytes - let a = Secpk1Fq::from_le_bytes_32([1;32]); - ``` - -Sure, here's the formatted version of the remaining methods: - -### to_le_bytes - -Return the little-endian bytes representation of a big integer. Example: - -```rust -let bytes = a.to_le_bytes(); -``` - -### add - -Add two big integers. Example: - -```rust -let sum = a + b; -``` - -### sub - -Subtract two big integers. Example: - -```rust -let difference = a - b; -``` - -### mul - -Multiply two big integers. Example: - -```rust -let product = a * b; -``` - -### div - -Divide two big integers. Note that division is field division and not euclidean division. Example: - -```rust -let quotient = a / b; -``` - -### eq - -Compare two big integers. Example: - -```rust -let are_equal = a == b; -``` diff --git a/docs/versioned_docs/version-v0.39.0/noir/standard_library/black_box_fns.md b/docs/versioned_docs/version-v0.39.0/noir/standard_library/black_box_fns.md deleted file mode 100644 index d6079ab182c..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/standard_library/black_box_fns.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -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. - -The ACVM spec defines a set of blackbox functions which backends will be expected to implement. This allows backends to use optimized implementations of these constraints if they have them, however they may also fallback to less efficient naive implementations if not. - -## Function list - -Here is a list of the current black box functions: - -- [AES128](./cryptographic_primitives/ciphers.mdx#aes128) -- [SHA256](./cryptographic_primitives/hashes.mdx#sha256) -- [Schnorr signature verification](./cryptographic_primitives/schnorr.mdx) -- [Blake2s](./cryptographic_primitives/hashes.mdx#blake2s) -- [Blake3](./cryptographic_primitives/hashes.mdx#blake3) -- [Pedersen Hash](./cryptographic_primitives/hashes.mdx#pedersen_hash) -- [Pedersen Commitment](./cryptographic_primitives/hashes.mdx#pedersen_commitment) -- [ECDSA signature verification](./cryptographic_primitives/ecdsa_sig_verification.mdx) -- [Embedded curve operations (MSM, addition, ...)](./cryptographic_primitives/embedded_curve_ops.mdx) -- AND -- XOR -- RANGE -- [Keccak256](./cryptographic_primitives/hashes.mdx#keccak256) -- [Recursive proof verification](./recursion.mdx) - -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. - -You can view the black box functions defined in the ACVM code [here](https://github.com/noir-lang/noir/blob/master/acvm-repo/acir/src/circuit/black_box_functions.rs). diff --git a/docs/versioned_docs/version-v0.39.0/noir/standard_library/bn254.md b/docs/versioned_docs/version-v0.39.0/noir/standard_library/bn254.md deleted file mode 100644 index 3294f005dbb..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/standard_library/bn254.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -title: Bn254 Field Library ---- - -Noir provides a module in standard library with some optimized functions for bn254 Fr in `std::field::bn254`. - -## decompose - -```rust -fn decompose(x: Field) -> (Field, Field) {} -``` - -Decomposes a single field into two fields, low and high. The low field contains the lower 16 bytes of the input field and the high field contains the upper 16 bytes of the input field. Both field results are range checked to 128 bits. - - -## assert_gt - -```rust -fn assert_gt(a: Field, b: Field) {} -``` - -Asserts that a > b. This will generate less constraints than using `assert(gt(a, b))`. - -## assert_lt - -```rust -fn assert_lt(a: Field, b: Field) {} -``` - -Asserts that a < b. This will generate less constraints than using `assert(lt(a, b))`. - -## gt - -```rust -fn gt(a: Field, b: Field) -> bool {} -``` - -Returns true if a > b. - -## lt - -```rust -fn lt(a: Field, b: Field) -> bool {} -``` - -Returns true if a < b. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.39.0/noir/standard_library/containers/boundedvec.md b/docs/versioned_docs/version-v0.39.0/noir/standard_library/containers/boundedvec.md deleted file mode 100644 index 509b214bf3a..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/standard_library/containers/boundedvec.md +++ /dev/null @@ -1,419 +0,0 @@ ---- -title: Bounded Vectors -keywords: [noir, vector, bounded vector, slice] -sidebar_position: 1 ---- - -A `BoundedVec` is a growable storage similar to a `Vec` except that it -is bounded with a maximum possible length. Unlike `Vec`, `BoundedVec` is not implemented -via slices and thus is not subject to the same restrictions slices are (notably, nested -slices - and thus nested vectors as well - are disallowed). - -Since a BoundedVec is backed by a normal array under the hood, growing the BoundedVec by -pushing an additional element is also more efficient - the length only needs to be increased -by one. - -For these reasons `BoundedVec` should generally be preferred over `Vec` when there -is a reasonable maximum bound that can be placed on the vector. - -Example: - -```rust -let mut vector: BoundedVec = BoundedVec::new(); -for i in 0..5 { - vector.push(i); -} -assert(vector.len() == 5); -assert(vector.max_len() == 10); -``` - -## Methods - -### new - -```rust -pub fn new() -> Self -``` - -Creates a new, empty vector of length zero. - -Since this container is backed by an array internally, it still needs an initial value -to give each element. To resolve this, each element is zeroed internally. This value -is guaranteed to be inaccessible unless `get_unchecked` is used. - -Example: - -```rust -let empty_vector: BoundedVec = BoundedVec::new(); -assert(empty_vector.len() == 0); -``` - -Note that whenever calling `new` the maximum length of the vector should always be specified -via a type signature: - -```rust title="new_example" showLineNumbers -fn good() -> BoundedVec { - // Ok! MaxLen is specified with a type annotation - let v1: BoundedVec = BoundedVec::new(); - let v2 = BoundedVec::new(); - - // Ok! MaxLen is known from the type of `good`'s return value - v2 -} - -fn bad() { - // Error: Type annotation needed - // The compiler can't infer `MaxLen` from this code. - let mut v3 = BoundedVec::new(); - v3.push(5); -} -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L11-L27 - - -This defaulting of `MaxLen` (and numeric generics in general) to zero may change in future noir versions -but for now make sure to use type annotations when using bounded vectors. Otherwise, you will receive a constraint failure at runtime when the vec is pushed to. - -### get - -```rust -pub fn get(self, index: u64) -> T { -``` - -Retrieves an element from the vector at the given index, starting from zero. - -If the given index is equal to or greater than the length of the vector, this -will issue a constraint failure. - -Example: - -```rust -fn foo(v: BoundedVec) { - let first = v.get(0); - let last = v.get(v.len() - 1); - assert(first != last); -} -``` - -### get_unchecked - -```rust -pub fn get_unchecked(self, index: u64) -> T { -``` - -Retrieves an element from the vector at the given index, starting from zero, without -performing a bounds check. - -Since this function does not perform a bounds check on length before accessing the element, -it is unsafe! Use at your own risk! - -Example: - -```rust title="get_unchecked_example" showLineNumbers -fn sum_of_first_three(v: BoundedVec) -> u32 { - // Always ensure the length is larger than the largest - // index passed to get_unchecked - assert(v.len() > 2); - let first = v.get_unchecked(0); - let second = v.get_unchecked(1); - let third = v.get_unchecked(2); - first + second + third -} -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L54-L64 - - -### set - -```rust -pub fn set(&mut self: Self, index: u64, value: T) { -``` - -Writes an element to the vector at the given index, starting from zero. - -If the given index is equal to or greater than the length of the vector, this will issue a constraint failure. - -Example: - -```rust -fn foo(v: BoundedVec) { - let first = v.get(0); - assert(first != 42); - v.set(0, 42); - let new_first = v.get(0); - assert(new_first == 42); -} -``` - -### set_unchecked - -```rust -pub fn set_unchecked(&mut self: Self, index: u64, value: T) -> T { -``` - -Writes an element to the vector at the given index, starting from zero, without performing a bounds check. - -Since this function does not perform a bounds check on length before accessing the element, it is unsafe! Use at your own risk! - -Example: - -```rust title="set_unchecked_example" showLineNumbers -fn set_unchecked_example() { - let mut vec: BoundedVec = BoundedVec::new(); - vec.extend_from_array([1, 2]); - - // Here we're safely writing within the valid range of `vec` - // `vec` now has the value [42, 2] - vec.set_unchecked(0, 42); - - // We can then safely read this value back out of `vec`. - // Notice that we use the checked version of `get` which would prevent reading unsafe values. - assert_eq(vec.get(0), 42); - - // We've now written past the end of `vec`. - // As this index is still within the maximum potential length of `v`, - // it won't cause a constraint failure. - vec.set_unchecked(2, 42); - println(vec); - - // This will write past the end of the maximum potential length of `vec`, - // it will then trigger a constraint failure. - vec.set_unchecked(5, 42); - println(vec); -} -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L67-L91 - - - -### push - -```rust -pub fn push(&mut self, elem: T) { -``` - -Pushes an element to the end of the vector. This increases the length -of the vector by one. - -Panics if the new length of the vector will be greater than the max length. - -Example: - -```rust title="bounded-vec-push-example" showLineNumbers -let mut v: BoundedVec = BoundedVec::new(); - - v.push(1); - v.push(2); - - // Panics with failed assertion "push out of bounds" - v.push(3); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L95-L103 - - -### pop - -```rust -pub fn pop(&mut self) -> T -``` - -Pops the element at the end of the vector. This will decrease the length -of the vector by one. - -Panics if the vector is empty. - -Example: - -```rust title="bounded-vec-pop-example" showLineNumbers -let mut v: BoundedVec = BoundedVec::new(); - v.push(1); - v.push(2); - - let two = v.pop(); - let one = v.pop(); - - assert(two == 2); - assert(one == 1); - // error: cannot pop from an empty vector - // let _ = v.pop(); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L108-L120 - - -### len - -```rust -pub fn len(self) -> u64 { -``` - -Returns the current length of this vector - -Example: - -```rust title="bounded-vec-len-example" showLineNumbers -let mut v: BoundedVec = BoundedVec::new(); - assert(v.len() == 0); - - v.push(100); - assert(v.len() == 1); - - v.push(200); - v.push(300); - v.push(400); - assert(v.len() == 4); - - let _ = v.pop(); - let _ = v.pop(); - assert(v.len() == 2); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L125-L140 - - -### max_len - -```rust -pub fn max_len(_self: BoundedVec) -> u64 { -``` - -Returns the maximum length of this vector. This is always -equal to the `MaxLen` parameter this vector was initialized with. - -Example: - -```rust title="bounded-vec-max-len-example" showLineNumbers -let mut v: BoundedVec = BoundedVec::new(); - - assert(v.max_len() == 5); - v.push(10); - assert(v.max_len() == 5); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L145-L151 - - -### storage - -```rust -pub fn storage(self) -> [T; MaxLen] { -``` - -Returns the internal array within this vector. -Since arrays in Noir are immutable, mutating the returned storage array will not mutate -the storage held internally by this vector. - -Note that uninitialized elements may be zeroed out! - -Example: - -```rust title="bounded-vec-storage-example" showLineNumbers -let mut v: BoundedVec = BoundedVec::new(); - - assert(v.storage() == [0, 0, 0, 0, 0]); - - v.push(57); - assert(v.storage() == [57, 0, 0, 0, 0]); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L156-L163 - - -### extend_from_array - -```rust -pub fn extend_from_array(&mut self, array: [T; Len]) -``` - -Pushes each element from the given array to this vector. - -Panics if pushing each element would cause the length of this vector -to exceed the maximum length. - -Example: - -```rust title="bounded-vec-extend-from-array-example" showLineNumbers -let mut vec: BoundedVec = BoundedVec::new(); - vec.extend_from_array([2, 4]); - - assert(vec.len == 2); - assert(vec.get(0) == 2); - assert(vec.get(1) == 4); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L168-L175 - - -### extend_from_bounded_vec - -```rust -pub fn extend_from_bounded_vec(&mut self, vec: BoundedVec) -``` - -Pushes each element from the other vector to this vector. The length of -the other vector is left unchanged. - -Panics if pushing each element would cause the length of this vector -to exceed the maximum length. - -Example: - -```rust title="bounded-vec-extend-from-bounded-vec-example" showLineNumbers -let mut v1: BoundedVec = BoundedVec::new(); - let mut v2: BoundedVec = BoundedVec::new(); - - v2.extend_from_array([1, 2, 3]); - v1.extend_from_bounded_vec(v2); - - assert(v1.storage() == [1, 2, 3, 0, 0]); - assert(v2.storage() == [1, 2, 3, 0, 0, 0, 0]); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L180-L189 - - -### from_array - -```rust -pub fn from_array(array: [T; Len]) -> Self -``` - -Creates a new vector, populating it with values derived from an array input. -The maximum length of the vector is determined based on the type signature. - -Example: -```rust -let bounded_vec: BoundedVec = BoundedVec::from_array([1, 2, 3]) -``` - -### map - -```rust -pub fn map(self, f: fn[Env](T) -> U) -> BoundedVec -``` - -Creates a new vector of equal size by calling a closure on each element in this vector. - -Example: - -```rust title="bounded-vec-map-example" showLineNumbers -let vec: BoundedVec = BoundedVec::from_array([1, 2, 3, 4]); - let result = vec.map(|value| value * 2); -``` -> Source code: noir_stdlib/src/collections/bounded_vec.nr#L495-L498 - - -### any - -```rust -pub fn any(self, predicate: fn[Env](T) -> bool) -> bool -``` - -Returns true if the given predicate returns true for any element -in this vector. - -Example: - -```rust title="bounded-vec-any-example" showLineNumbers -let mut v: BoundedVec = BoundedVec::new(); - v.extend_from_array([2, 4, 6]); - - let all_even = !v.any(|elem: u32| elem % 2 != 0); - assert(all_even); -``` -> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L256-L262 - diff --git a/docs/versioned_docs/version-v0.39.0/noir/standard_library/containers/hashmap.md b/docs/versioned_docs/version-v0.39.0/noir/standard_library/containers/hashmap.md deleted file mode 100644 index 395cc312705..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/standard_library/containers/hashmap.md +++ /dev/null @@ -1,587 +0,0 @@ ---- -title: HashMap -keywords: [noir, map, hash, hashmap] -sidebar_position: 1 ---- - -`HashMap` is used to efficiently store and look up key-value pairs. - -`HashMap` is a bounded type which can store anywhere from zero to `MaxLen` total elements. -Note that due to hash collisions, the actual maximum number of elements stored by any particular -hashmap is likely lower than `MaxLen`. This is true even with cryptographic hash functions since -every hash value will be performed modulo `MaxLen`. - -Example: - -```rust -// Create a mapping from Fields to u32s with a maximum length of 12 -// using a poseidon2 hasher -use std::hash::poseidon2::Poseidon2Hasher; -let mut map: HashMap> = HashMap::default(); - -map.insert(1, 2); -map.insert(3, 4); - -let two = map.get(1).unwrap(); -``` - -## Methods - -### default - -```rust title="default" showLineNumbers -impl Default for HashMap -where - B: BuildHasher + Default, - H: Hasher + Default, -{ - /// Constructs an empty HashMap. - /// - /// Example: - /// - /// ```noir - /// let hashmap: HashMap> = HashMap::default(); - /// assert(hashmap.is_empty()); - /// ``` - fn default() -> Self { -``` -> Source code: noir_stdlib/src/collections/map.nr#L681-L696 - - -Creates a fresh, empty HashMap. - -When using this function, always make sure to specify the maximum size of the hash map. - -This is the same `default` from the `Default` implementation given further below. It is -repeated here for convenience since it is the recommended way to create a hashmap. - -Example: - -```rust title="default_example" showLineNumbers -let hashmap: HashMap> = HashMap::default(); - assert(hashmap.is_empty()); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L207-L210 - - -Because `HashMap` has so many generic arguments that are likely to be the same throughout -your program, it may be helpful to create a type alias: - -```rust title="type_alias" showLineNumbers -type MyMap = HashMap>; -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L201-L203 - - -### with_hasher - -```rust title="with_hasher" showLineNumbers -pub fn with_hasher(_build_hasher: B) -> Self - where - B: BuildHasher, - { -``` -> Source code: noir_stdlib/src/collections/map.nr#L103-L108 - - -Creates a hashmap with an existing `BuildHasher`. This can be used to ensure multiple -hashmaps are created with the same hasher instance. - -Example: - -```rust title="with_hasher_example" showLineNumbers -let my_hasher: BuildHasherDefault = Default::default(); - let hashmap: HashMap> = - HashMap::with_hasher(my_hasher); - assert(hashmap.is_empty()); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L211-L216 - - -### get - -```rust title="get" showLineNumbers -pub fn get(self, key: K) -> Option - where - K: Eq + Hash, - B: BuildHasher, - H: Hasher, - { -``` -> Source code: noir_stdlib/src/collections/map.nr#L465-L472 - - -Retrieves a value from the hashmap, returning `Option::none()` if it was not found. - -Example: - -```rust title="get_example" showLineNumbers -fn get_example(map: HashMap>) { - let x = map.get(12); - - if x.is_some() { - assert(x.unwrap() == 42); - } -} -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L296-L304 - - -### insert - -```rust title="insert" showLineNumbers -pub fn insert(&mut self, key: K, value: V) - where - K: Eq + Hash, - B: BuildHasher, - H: Hasher, - { -``` -> Source code: noir_stdlib/src/collections/map.nr#L507-L514 - - -Inserts a new key-value pair into the map. If the key was already in the map, its -previous value will be overridden with the newly provided one. - -Example: - -```rust title="insert_example" showLineNumbers -let mut map: HashMap> = HashMap::default(); - map.insert(12, 42); - assert(map.len() == 1); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L217-L221 - - -### remove - -```rust title="remove" showLineNumbers -pub fn remove(&mut self, key: K) - where - K: Eq + Hash, - B: BuildHasher, - H: Hasher, - { -``` -> Source code: noir_stdlib/src/collections/map.nr#L563-L570 - - -Removes the given key-value pair from the map. If the key was not already present -in the map, this does nothing. - -Example: - -```rust title="remove_example" showLineNumbers -map.remove(12); - assert(map.is_empty()); - - // If a key was not present in the map, remove does nothing - map.remove(12); - assert(map.is_empty()); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L224-L231 - - -### is_empty - -```rust title="is_empty" showLineNumbers -pub fn is_empty(self) -> bool { -``` -> Source code: noir_stdlib/src/collections/map.nr#L167-L169 - - -True if the length of the hash map is empty. - -Example: - -```rust title="is_empty_example" showLineNumbers -assert(map.is_empty()); - - map.insert(1, 2); - assert(!map.is_empty()); - - map.remove(1); - assert(map.is_empty()); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L232-L240 - - -### len - -```rust title="len" showLineNumbers -pub fn len(self) -> u32 { -``` -> Source code: noir_stdlib/src/collections/map.nr#L424-L426 - - -Returns the current length of this hash map. - -Example: - -```rust title="len_example" showLineNumbers -// This is equivalent to checking map.is_empty() - assert(map.len() == 0); - - map.insert(1, 2); - map.insert(3, 4); - map.insert(5, 6); - assert(map.len() == 3); - - // 3 was already present as a key in the hash map, so the length is unchanged - map.insert(3, 7); - assert(map.len() == 3); - - map.remove(1); - assert(map.len() == 2); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L241-L256 - - -### capacity - -```rust title="capacity" showLineNumbers -pub fn capacity(_self: Self) -> u32 { -``` -> Source code: noir_stdlib/src/collections/map.nr#L446-L448 - - -Returns the maximum capacity of this hashmap. This is always equal to the capacity -specified in the hashmap's type. - -Unlike hashmaps in general purpose programming languages, hashmaps in Noir have a -static capacity that does not increase as the map grows larger. Thus, this capacity -is also the maximum possible element count that can be inserted into the hashmap. -Due to hash collisions (modulo the hashmap length), it is likely the actual maximum -element count will be lower than the full capacity. - -Example: - -```rust title="capacity_example" showLineNumbers -let empty_map: HashMap> = - HashMap::default(); - assert(empty_map.len() == 0); - assert(empty_map.capacity() == 42); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L257-L262 - - -### clear - -```rust title="clear" showLineNumbers -pub fn clear(&mut self) { -``` -> Source code: noir_stdlib/src/collections/map.nr#L123-L125 - - -Clears the hashmap, removing all key-value pairs from it. - -Example: - -```rust title="clear_example" showLineNumbers -assert(!map.is_empty()); - map.clear(); - assert(map.is_empty()); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L263-L267 - - -### contains_key - -```rust title="contains_key" showLineNumbers -pub fn contains_key(self, key: K) -> bool - where - K: Hash + Eq, - B: BuildHasher, - H: Hasher, - { -``` -> Source code: noir_stdlib/src/collections/map.nr#L143-L150 - - -True if the hashmap contains the given key. Unlike `get`, this will not also return -the value associated with the key. - -Example: - -```rust title="contains_key_example" showLineNumbers -if map.contains_key(7) { - let value = map.get(7); - assert(value.is_some()); - } else { - println("No value for key 7!"); - } -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L268-L275 - - -### entries - -```rust title="entries" showLineNumbers -pub fn entries(self) -> BoundedVec<(K, V), N> { -``` -> Source code: noir_stdlib/src/collections/map.nr#L191-L193 - - -Returns a vector of each key-value pair present in the hashmap. - -The length of the returned vector is always equal to the length of the hashmap. - -Example: - -```rust title="entries_example" showLineNumbers -let entries = map.entries(); - - // The length of a hashmap may not be compile-time known, so we - // need to loop over its capacity instead - for i in 0..map.capacity() { - if i < entries.len() { - let (key, value) = entries.get(i); - println(f"{key} -> {value}"); - } - } -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L307-L318 - - -### keys - -```rust title="keys" showLineNumbers -pub fn keys(self) -> BoundedVec { -``` -> Source code: noir_stdlib/src/collections/map.nr#L227-L229 - - -Returns a vector of each key present in the hashmap. - -The length of the returned vector is always equal to the length of the hashmap. - -Example: - -```rust title="keys_example" showLineNumbers -let keys = map.keys(); - - for i in 0..keys.max_len() { - if i < keys.len() { - let key = keys.get_unchecked(i); - let value = map.get(key).unwrap_unchecked(); - println(f"{key} -> {value}"); - } - } -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L319-L329 - - -### values - -```rust title="values" showLineNumbers -pub fn values(self) -> BoundedVec { -``` -> Source code: noir_stdlib/src/collections/map.nr#L262-L264 - - -Returns a vector of each value present in the hashmap. - -The length of the returned vector is always equal to the length of the hashmap. - -Example: - -```rust title="values_example" showLineNumbers -let values = map.values(); - - for i in 0..values.max_len() { - if i < values.len() { - let value = values.get_unchecked(i); - println(f"Found value {value}"); - } - } -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L330-L339 - - -### iter_mut - -```rust title="iter_mut" showLineNumbers -pub fn iter_mut(&mut self, f: fn(K, V) -> (K, V)) - where - K: Eq + Hash, - B: BuildHasher, - H: Hasher, - { -``` -> Source code: noir_stdlib/src/collections/map.nr#L297-L304 - - -Iterates through each key-value pair of the HashMap, setting each key-value pair to the -result returned from the given function. - -Note that since keys can be mutated, the HashMap needs to be rebuilt as it is iterated -through. If this is not desired, use `iter_values_mut` if only values need to be mutated, -or `entries` if neither keys nor values need to be mutated. - -The iteration order is left unspecified. As a result, if two keys are mutated to become -equal, which of the two values that will be present for the key in the resulting map is also unspecified. - -Example: - -```rust title="iter_mut_example" showLineNumbers -// Add 1 to each key in the map, and double the value associated with that key. - map.iter_mut(|k, v| (k + 1, v * 2)); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L343-L346 - - -### iter_keys_mut - -```rust title="iter_keys_mut" showLineNumbers -pub fn iter_keys_mut(&mut self, f: fn(K) -> K) - where - K: Eq + Hash, - B: BuildHasher, - H: Hasher, - { -``` -> Source code: noir_stdlib/src/collections/map.nr#L335-L342 - - -Iterates through the HashMap, mutating each key to the result returned from -the given function. - -Note that since keys can be mutated, the HashMap needs to be rebuilt as it is iterated -through. If only iteration is desired and the keys are not intended to be mutated, -prefer using `entries` instead. - -The iteration order is left unspecified. As a result, if two keys are mutated to become -equal, which of the two values that will be present for the key in the resulting map is also unspecified. - -Example: - -```rust title="iter_keys_mut_example" showLineNumbers -// Double each key, leaving the value associated with that key untouched - map.iter_keys_mut(|k| k * 2); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L347-L350 - - -### iter_values_mut - -```rust title="iter_values_mut" showLineNumbers -pub fn iter_values_mut(&mut self, f: fn(V) -> V) { -``` -> Source code: noir_stdlib/src/collections/map.nr#L367-L369 - - -Iterates through the HashMap, applying the given function to each value and mutating the -value to equal the result. This function is more efficient than `iter_mut` and `iter_keys_mut` -because the keys are untouched and the underlying hashmap thus does not need to be reordered. - -Example: - -```rust title="iter_values_mut_example" showLineNumbers -// Halve each value - map.iter_values_mut(|v| v / 2); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L351-L354 - - -### retain - -```rust title="retain" showLineNumbers -pub fn retain(&mut self, f: fn(K, V) -> bool) { -``` -> Source code: noir_stdlib/src/collections/map.nr#L388-L390 - - -Retains only the key-value pairs for which the given function returns true. -Any key-value pairs for which the function returns false will be removed from the map. - -Example: - -```rust title="retain_example" showLineNumbers -map.retain(|k, v| (k != 0) & (v != 0)); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L279-L281 - - -## Trait Implementations - -### default - -```rust title="default" showLineNumbers -impl Default for HashMap -where - B: BuildHasher + Default, - H: Hasher + Default, -{ - /// Constructs an empty HashMap. - /// - /// Example: - /// - /// ```noir - /// let hashmap: HashMap> = HashMap::default(); - /// assert(hashmap.is_empty()); - /// ``` - fn default() -> Self { -``` -> Source code: noir_stdlib/src/collections/map.nr#L681-L696 - - -Constructs an empty HashMap. - -Example: - -```rust title="default_example" showLineNumbers -let hashmap: HashMap> = HashMap::default(); - assert(hashmap.is_empty()); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L207-L210 - - -### eq - -```rust title="eq" showLineNumbers -impl Eq for HashMap -where - K: Eq + Hash, - V: Eq, - B: BuildHasher, - H: Hasher, -{ - /// Checks if two HashMaps are equal. - /// - /// Example: - /// - /// ```noir - /// let mut map1: HashMap> = HashMap::default(); - /// let mut map2: HashMap> = HashMap::default(); - /// - /// map1.insert(1, 2); - /// map1.insert(3, 4); - /// - /// map2.insert(3, 4); - /// map2.insert(1, 2); - /// - /// assert(map1 == map2); - /// ``` - fn eq(self, other: HashMap) -> bool { -``` -> Source code: noir_stdlib/src/collections/map.nr#L629-L654 - - -Checks if two HashMaps are equal. - -Example: - -```rust title="eq_example" showLineNumbers -let mut map1: HashMap> = HashMap::default(); - let mut map2: HashMap> = HashMap::default(); - - map1.insert(1, 2); - map1.insert(3, 4); - - map2.insert(3, 4); - map2.insert(1, 2); - - assert(map1 == map2); -``` -> Source code: test_programs/execution_success/hashmap/src/main.nr#L282-L293 - diff --git a/docs/versioned_docs/version-v0.39.0/noir/standard_library/containers/index.md b/docs/versioned_docs/version-v0.39.0/noir/standard_library/containers/index.md deleted file mode 100644 index ea84c6d5c21..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/standard_library/containers/index.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Containers -description: Container types provided by Noir's standard library for storing and retrieving data -keywords: [containers, data types, vec, hashmap] ---- diff --git a/docs/versioned_docs/version-v0.39.0/noir/standard_library/containers/vec.mdx b/docs/versioned_docs/version-v0.39.0/noir/standard_library/containers/vec.mdx deleted file mode 100644 index 475011922f8..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/standard_library/containers/vec.mdx +++ /dev/null @@ -1,170 +0,0 @@ ---- -title: Vectors -description: Delve into the Vec 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] -sidebar_position: 6 ---- - -import Experimental from '@site/src/components/Notes/_experimental.mdx'; - - - -A vector is a collection type similar to Rust's `Vec` type. In Noir, it is a convenient way to use slices as mutable arrays. - -Example: - -```rust -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 -``` - -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 -``` - -Example: - -```rust -let slice: [Field] = &[1, 2, 3]; -let vector_from_slice = Vec::from_slice(slice); -assert(vector_from_slice.len() == 3); -``` - -### len - -Returns the number of elements in the vector. - -```rust -pub fn len(self) -> Field -``` - -Example: - -```rust -let empty_vector: Vec = Vec::new(); -assert(empty_vector.len() == 0); -``` - -### 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 -``` - -Example: - -```rust -let vector: Vec = Vec::from_slice(&[10, 20, 30]); -assert(vector.get(1) == 20); -``` - -### set - -```rust -pub fn set(&mut self: Self, index: u64, value: T) { -``` - -Writes an element to the vector at the given index, starting from zero. - -Panics if the index points beyond the vector's end. - -Example: - -```rust -let vector: Vec = Vec::from_slice(&[10, 20, 30]); -assert(vector.get(1) == 20); -vector.set(1, 42); -assert(vector.get(1) == 42); -``` - -### 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) -``` - -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 -``` - -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) -``` - -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 -``` - -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); -``` diff --git a/docs/versioned_docs/version-v0.39.0/noir/standard_library/cryptographic_primitives/_category_.json b/docs/versioned_docs/version-v0.39.0/noir/standard_library/cryptographic_primitives/_category_.json deleted file mode 100644 index 5d694210bbf..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/standard_library/cryptographic_primitives/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "position": 0, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.39.0/noir/standard_library/cryptographic_primitives/ciphers.mdx b/docs/versioned_docs/version-v0.39.0/noir/standard_library/cryptographic_primitives/ciphers.mdx deleted file mode 100644 index d6a5e1a79eb..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/standard_library/cryptographic_primitives/ciphers.mdx +++ /dev/null @@ -1,32 +0,0 @@ ---- -title: Ciphers -description: - Learn about the implemented ciphers ready to use for any Noir project -keywords: - [ciphers, Noir project, aes128, encrypt] -sidebar_position: 0 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; - -## aes128 - -Given a plaintext as an array of bytes, returns the corresponding aes128 ciphertext (CBC mode). Input padding is automatically performed using PKCS#7, so that the output length is `input.len() + (16 - input.len() % 16)`. - -```rust title="aes128" showLineNumbers -pub fn aes128_encrypt(input: [u8; N], iv: [u8; 16], key: [u8; 16]) -> [u8] {} -``` -> Source code: noir_stdlib/src/aes128.nr#L2-L4 - - -```rust -fn main() { - let input: [u8; 4] = [0, 12, 3, 15] // Random bytes, will be padded to 16 bytes. - let iv: [u8; 16] = [0; 16]; // Initialisation vector - let key: [u8; 16] = [0; 16] // AES key - let ciphertext = std::aes128::aes128_encrypt(inputs.as_bytes(), iv.as_bytes(), key.as_bytes()); // In this case, the output length will be 16 bytes. -} -``` - - - \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.39.0/noir/standard_library/cryptographic_primitives/ec_primitives.md b/docs/versioned_docs/version-v0.39.0/noir/standard_library/cryptographic_primitives/ec_primitives.md deleted file mode 100644 index f262d8160d6..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/standard_library/cryptographic_primitives/ec_primitives.md +++ /dev/null @@ -1,102 +0,0 @@ ---- -title: Elliptic Curve Primitives -keywords: [cryptographic primitives, Noir project] -sidebar_position: 4 ---- - -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/mod.nr`](https://github.com/noir-lang/noir/blob/master/noir_stdlib/src/ec/mod.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/mod.nr)). - -## Examples - -The -[ec_baby_jubjub test](https://github.com/noir-lang/noir/blob/master/test_programs/compile_success_empty/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 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.39.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx b/docs/versioned_docs/version-v0.39.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx deleted file mode 100644 index 8d96027b42c..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx +++ /dev/null @@ -1,98 +0,0 @@ ---- -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] -sidebar_position: 3 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; - -Noir supports ECDSA signatures verification over the secp256k1 and secp256r1 curves. - -## ecdsa_secp256k1::verify_signature - -Verifier for ECDSA Secp256k1 signatures. -See ecdsa_secp256k1::verify_signature_slice for a version that accepts slices directly. - -```rust title="ecdsa_secp256k1" showLineNumbers -pub fn verify_signature( - public_key_x: [u8; 32], - public_key_y: [u8; 32], - signature: [u8; 64], - message_hash: [u8; N], -) -> bool -``` -> Source code: noir_stdlib/src/ecdsa_secp256k1.nr#L2-L9 - - -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_secp256k1::verify_signature_slice - -Verifier for ECDSA Secp256k1 signatures where the message is a slice. - -```rust title="ecdsa_secp256k1_slice" showLineNumbers -pub fn verify_signature_slice( - public_key_x: [u8; 32], - public_key_y: [u8; 32], - signature: [u8; 64], - message_hash: [u8], -) -> bool -``` -> Source code: noir_stdlib/src/ecdsa_secp256k1.nr#L13-L20 - - - - -## ecdsa_secp256r1::verify_signature - -Verifier for ECDSA Secp256r1 signatures. -See ecdsa_secp256r1::verify_signature_slice for a version that accepts slices directly. - -```rust title="ecdsa_secp256r1" showLineNumbers -pub fn verify_signature( - public_key_x: [u8; 32], - public_key_y: [u8; 32], - signature: [u8; 64], - message_hash: [u8; N], -) -> bool -``` -> Source code: noir_stdlib/src/ecdsa_secp256r1.nr#L2-L9 - - -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); -} -``` - - - -## ecdsa_secp256r1::verify_signature - -Verifier for ECDSA Secp256r1 signatures where the message is a slice. - -```rust title="ecdsa_secp256r1_slice" showLineNumbers -pub fn verify_signature_slice( - public_key_x: [u8; 32], - public_key_y: [u8; 32], - signature: [u8; 64], - message_hash: [u8], -) -> bool -``` -> Source code: noir_stdlib/src/ecdsa_secp256r1.nr#L13-L20 - - - diff --git a/docs/versioned_docs/version-v0.39.0/noir/standard_library/cryptographic_primitives/eddsa.mdx b/docs/versioned_docs/version-v0.39.0/noir/standard_library/cryptographic_primitives/eddsa.mdx deleted file mode 100644 index b283de693c8..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/standard_library/cryptographic_primitives/eddsa.mdx +++ /dev/null @@ -1,37 +0,0 @@ ---- -title: EdDSA Verification -description: Learn about the cryptographic primitives regarding EdDSA -keywords: [cryptographic primitives, Noir project, eddsa, signatures] -sidebar_position: 5 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; - -## 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 -``` - -It is also possible to specify the hash algorithm used for the signature by using the `eddsa_verify` function by passing a type implementing the Hasher trait with the turbofish operator. -For instance, if you want to use Poseidon2 instead, you can do the following: -```rust -use std::hash::poseidon2::Poseidon2Hasher; - -eddsa_verify::(pub_key_a.x, pub_key_a.y, s_a, r8_a.x, r8_a.y, msg); -``` - - - -## eddsa::eddsa_to_pub - -Private to public key conversion. - -Returns `(pub_key_x, pub_key_y)` - -```rust -fn eddsa_to_pub(secret : Field) -> (Field, Field) -``` - diff --git a/docs/versioned_docs/version-v0.39.0/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx b/docs/versioned_docs/version-v0.39.0/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx deleted file mode 100644 index 482a36932b9..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx +++ /dev/null @@ -1,95 +0,0 @@ ---- -title: Scalar multiplication -description: See how you can perform scalar multiplication in Noir -keywords: [cryptographic primitives, Noir project, scalar multiplication] -sidebar_position: 1 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; - -The following functions perform operations over the embedded curve whose coordinates are defined by the configured noir field. -For the BN254 scalar field, this is BabyJubJub or Grumpkin. - -:::note -Suffixes `_low` and `_high` denote low and high limbs of a scalar. -::: - -## embedded_curve_ops::multi_scalar_mul - -Performs multi scalar multiplication over the embedded curve. -The function accepts arbitrary amount of point-scalar pairs on the input, it multiplies the individual pairs over -the curve and returns a sum of the resulting points. - -Points represented as x and y coordinates [x1, y1, x2, y2, ...], scalars as low and high limbs [low1, high1, low2, high2, ...]. - -```rust title="multi_scalar_mul" showLineNumbers -pub fn multi_scalar_mul( - points: [EmbeddedCurvePoint; N], - scalars: [EmbeddedCurveScalar; N], -) -> EmbeddedCurvePoint -``` -> Source code: noir_stdlib/src/embedded_curve_ops.nr#L103-L108 - - -example - -```rust -fn main(point_x: Field, point_y: Field, scalar_low: Field, scalar_high: Field) { - let point = std::embedded_curve_ops::multi_scalar_mul([point_x, point_y], [scalar_low, scalar_high]); - println(point); -} -``` - -## embedded_curve_ops::fixed_base_scalar_mul - -Performs fixed base scalar multiplication over the embedded curve (multiplies input scalar with a generator point). -The function accepts a single scalar on the input represented as 2 fields. - -```rust title="fixed_base_scalar_mul" showLineNumbers -pub fn fixed_base_scalar_mul(scalar: EmbeddedCurveScalar) -> EmbeddedCurvePoint -``` -> Source code: noir_stdlib/src/embedded_curve_ops.nr#L120-L122 - - -example - -```rust -fn main(scalar_low: Field, scalar_high: Field) { - let point = std::embedded_curve_ops::fixed_base_scalar_mul(scalar_low, scalar_high); - println(point); -} -``` - -## embedded_curve_ops::embedded_curve_add - -Adds two points on the embedded curve. -This function takes two `EmbeddedCurvePoint` structures as parameters, representing points on the curve, and returns a new `EmbeddedCurvePoint` structure that represents their sum. - -### Parameters: -- `point1` (`EmbeddedCurvePoint`): The first point to add. -- `point2` (`EmbeddedCurvePoint`): The second point to add. - -### Returns: -- `EmbeddedCurvePoint`: The resulting point after the addition of `point1` and `point2`. - -```rust title="embedded_curve_add" showLineNumbers -pub fn embedded_curve_add( - point1: EmbeddedCurvePoint, - point2: EmbeddedCurvePoint, -) -> EmbeddedCurvePoint { -``` -> Source code: noir_stdlib/src/embedded_curve_ops.nr#L136-L141 - - -example - -```rust -fn main() { - let point1 = EmbeddedCurvePoint { x: 1, y: 2 }; - let point2 = EmbeddedCurvePoint { x: 3, y: 4 }; - let result = std::embedded_curve_ops::embedded_curve_add(point1, point2); - println!("Resulting Point: ({}, {})", result.x, result.y); -} -``` - - diff --git a/docs/versioned_docs/version-v0.39.0/noir/standard_library/cryptographic_primitives/hashes.mdx b/docs/versioned_docs/version-v0.39.0/noir/standard_library/cryptographic_primitives/hashes.mdx deleted file mode 100644 index 541a1971561..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/standard_library/cryptographic_primitives/hashes.mdx +++ /dev/null @@ -1,227 +0,0 @@ ---- -title: Hash methods -description: - Learn about the cryptographic primitives ready to use for any Noir project, including sha256, - blake2s and pedersen -keywords: - [cryptographic primitives, Noir project, sha256, blake2s, pedersen, hash] -sidebar_position: 0 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; - -## sha256 - -Given an array of bytes, returns the resulting sha256 hash. -Specify a message_size to hash only the first `message_size` bytes of the input. - -```rust title="sha256" showLineNumbers -pub fn sha256(input: [u8; N]) -> HASH -``` -> Source code: noir_stdlib/src/hash/sha256.nr#L47-L49 - - -example: -```rust title="sha256_var" showLineNumbers -let digest = std::hash::sha256_var([x as u8], 1); -``` -> Source code: test_programs/execution_success/sha256/src/main.nr#L15-L17 - - -```rust -fn main() { - let x = [163, 117, 178, 149]; // some random bytes - let hash = std::sha256::sha256_var(x, 4); -} -``` - - - - -## blake2s - -Given an array of bytes, returns an array with the Blake2 hash - -```rust title="blake2s" showLineNumbers -pub fn blake2s(input: [u8; N]) -> [u8; 32] -``` -> Source code: noir_stdlib/src/hash/mod.nr#L18-L20 - - -example: - -```rust -fn main() { - let x = [163, 117, 178, 149]; // some random bytes - let hash = std::hash::blake2s(x); -} -``` - - - -## blake3 - -Given an array of bytes, returns an array with the Blake3 hash - -```rust title="blake3" showLineNumbers -pub fn blake3(input: [u8; N]) -> [u8; 32] -``` -> Source code: noir_stdlib/src/hash/mod.nr#L24-L26 - - -example: - -```rust -fn main() { - let x = [163, 117, 178, 149]; // some random bytes - let hash = std::hash::blake3(x); -} -``` - - - -## pedersen_hash - -Given an array of Fields, returns the Pedersen hash. - -```rust title="pedersen_hash" showLineNumbers -pub fn pedersen_hash(input: [Field; N]) -> Field -``` -> Source code: noir_stdlib/src/hash/mod.nr#L49-L51 - - -example: - -```rust title="pedersen-hash" showLineNumbers -fn main(x: Field, y: Field, expected_hash: Field) { - let hash = std::hash::pedersen_hash([x, y]); - assert_eq(hash, expected_hash); -} -``` -> Source code: test_programs/execution_success/pedersen_hash/src/main.nr#L1-L6 - - - - -## pedersen_commitment - -Given an array of Fields, returns the Pedersen commitment. - -```rust title="pedersen_commitment" showLineNumbers -pub fn pedersen_commitment(input: [Field; N]) -> EmbeddedCurvePoint { -``` -> Source code: noir_stdlib/src/hash/mod.nr#L29-L31 - - -example: - -```rust title="pedersen-commitment" showLineNumbers -fn main(x: Field, y: Field, expected_commitment: std::embedded_curve_ops::EmbeddedCurvePoint) { - let commitment = std::hash::pedersen_commitment([x, y]); - assert_eq(commitment.x, expected_commitment.x); - assert_eq(commitment.y, expected_commitment.y); -} -``` -> Source code: test_programs/execution_success/pedersen_commitment/src/main.nr#L1-L7 - - - - -## 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 title="keccak256" showLineNumbers -pub fn keccak256(input: [u8; N], message_size: u32) -> [u8; 32] -``` -> Source code: noir_stdlib/src/hash/mod.nr#L116-L118 - - -example: - -```rust title="keccak256" showLineNumbers -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 message 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); -} -``` -> Source code: test_programs/execution_success/keccak256/src/main.nr#L1-L20 - - - - -## 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 title="poseidon" showLineNumbers -use std::hash::poseidon; - -fn main(x1: [Field; 2], y1: pub Field, x2: [Field; 4], y2: pub Field) { - let hash1 = poseidon::bn254::hash_2(x1); - assert(hash1 == y1); - - let hash2 = poseidon::bn254::hash_4(x2); - assert(hash2 == y2); -} -``` -> Source code: test_programs/execution_success/poseidon_bn254_hash/src/main.nr#L1-L11 - - -## poseidon 2 - -Given an array of Fields, returns a new Field with the Poseidon2 Hash. Contrary to the Poseidon -function, there is only one hash and you can specify a message_size to hash only the first -`message_size` bytes of the input, - -```rust -// example for hashing the first three elements of the input -Poseidon2::hash(input, 3); -``` - -example: - -```rust title="poseidon2" showLineNumbers -use std::hash::poseidon2; - -fn main(inputs: [Field; 4], expected_hash: Field) { - let hash = poseidon2::Poseidon2::hash(inputs, inputs.len()); - assert_eq(hash, expected_hash); -} -``` -> Source code: test_programs/execution_success/poseidon2/src/main.nr#L1-L8 - - -## hash_to_field - -```rust -fn hash_to_field(_input : [Field]) -> 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.39.0/noir/standard_library/cryptographic_primitives/index.md b/docs/versioned_docs/version-v0.39.0/noir/standard_library/cryptographic_primitives/index.md deleted file mode 100644 index 650f30165d5..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/standard_library/cryptographic_primitives/index.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -title: Cryptographic Primitives -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.39.0/noir/standard_library/cryptographic_primitives/schnorr.mdx b/docs/versioned_docs/version-v0.39.0/noir/standard_library/cryptographic_primitives/schnorr.mdx deleted file mode 100644 index 030452645c5..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/standard_library/cryptographic_primitives/schnorr.mdx +++ /dev/null @@ -1,64 +0,0 @@ ---- -title: Schnorr Signatures -description: Learn how you can verify Schnorr signatures using Noir -keywords: [cryptographic primitives, Noir project, schnorr, signatures] -sidebar_position: 2 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; - -## schnorr::verify_signature - -Verifier for Schnorr signatures over the embedded curve (for BN254 it is Grumpkin). -See schnorr::verify_signature_slice for a version that works directly on slices. - -```rust title="schnorr_verify" showLineNumbers -pub fn verify_signature( - public_key_x: Field, - public_key_y: Field, - signature: [u8; 64], - message: [u8; N], -) -> bool -``` -> Source code: noir_stdlib/src/schnorr.nr#L4-L11 - - -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() -); - -... -``` - - - -## schnorr::verify_signature_slice - -Verifier for Schnorr signatures over the embedded curve (for BN254 it is Grumpkin) -where the message is a slice. - -```rust title="schnorr_verify_slice" showLineNumbers -pub fn verify_signature_slice( - public_key_x: Field, - public_key_y: Field, - signature: [u8; 64], - message: [u8], -) -> bool -``` -> Source code: noir_stdlib/src/schnorr.nr#L15-L22 - - - diff --git a/docs/versioned_docs/version-v0.39.0/noir/standard_library/fmtstr.md b/docs/versioned_docs/version-v0.39.0/noir/standard_library/fmtstr.md deleted file mode 100644 index 19809d60261..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/standard_library/fmtstr.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -title: fmtstr ---- - -`fmtstr` is the type resulting from using format string (`f"..."`). - -## Methods - -### quoted_contents - -```rust title="quoted_contents" showLineNumbers -pub comptime fn quoted_contents(self) -> Quoted {} -``` -> Source code: noir_stdlib/src/meta/format_string.nr#L3-L5 - - -Returns the format string contents (that is, without the leading and trailing double quotes) as a `Quoted` value. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.39.0/noir/standard_library/is_unconstrained.md b/docs/versioned_docs/version-v0.39.0/noir/standard_library/is_unconstrained.md deleted file mode 100644 index 51bb1bda8f1..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/standard_library/is_unconstrained.md +++ /dev/null @@ -1,69 +0,0 @@ ---- -title: Is Unconstrained Function -description: - The is_unconstrained function returns wether the context at that point of the program is unconstrained or not. -keywords: - [ - unconstrained - ] ---- - -It's very common for functions in circuits to take unconstrained hints of an expensive computation and then verify it. This is done by running the hint in an unconstrained context and then verifying the result in a constrained context. - -When a function is marked as unconstrained, any subsequent functions that it calls will also be run in an unconstrained context. However, if we are implementing a library function, other users might call it within an unconstrained context or a constrained one. Generally, in an unconstrained context we prefer just computing the result instead of taking a hint of it and verifying it, since that'd mean doing the same computation twice: - -```rust - -fn my_expensive_computation(){ - ... -} - -unconstrained fn my_expensive_computation_hint(){ - my_expensive_computation() -} - -pub fn external_interface(){ - my_expensive_computation_hint(); - // verify my_expensive_computation: If external_interface is called from unconstrained, this is redundant - ... -} - -``` - -In order to improve the performance in an unconstrained context you can use the function at `std::runtime::is_unconstrained() -> bool`: - - -```rust -use dep::std::runtime::is_unconstrained; - -fn my_expensive_computation(){ - ... -} - -unconstrained fn my_expensive_computation_hint(){ - my_expensive_computation() -} - -pub fn external_interface(){ - if is_unconstrained() { - my_expensive_computation(); - } else { - my_expensive_computation_hint(); - // verify my_expensive_computation - ... - } -} - -``` - -The is_unconstrained result is resolved at compile time, so in unconstrained contexts the compiler removes the else branch, and in constrained contexts the compiler removes the if branch, reducing the amount of compute necessary to run external_interface. - -Note that using `is_unconstrained` in a `comptime` context will also return `true`: - -``` -fn main() { - comptime { - assert(is_unconstrained()); - } -} -``` diff --git a/docs/versioned_docs/version-v0.39.0/noir/standard_library/logging.md b/docs/versioned_docs/version-v0.39.0/noir/standard_library/logging.md deleted file mode 100644 index db75ef9f86f..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/standard_library/logging.md +++ /dev/null @@ -1,78 +0,0 @@ ---- -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, - print statement, - debugging in noir, - noir std library, - logging tutorial, - basic logging in noir, - noir logging implementation, - noir debugging techniques, - rust, - ] ---- - -The standard library provides two familiar statements you can use: `println` and `print`. Despite being a limited implementation of rust's `println!` and `print!` macros, these constructs can be useful for debugging. - -You can print the output of both statements in your Noir code by using the `nargo execute` command or the `--show-output` flag when using `nargo test` (provided there are print statements in your tests). - -It is recommended to use `nargo execute` if you want to debug failing constraints with `println` or `print` 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). Neither `println`, nor `print` are callable for failed constraints caught at compile time. - -Both `print` and `println` are generic functions which can work on integers, fields, strings, and even structs or expressions. Note however, that slices are currently unsupported. For example: - -```rust -struct Person { - age: Field, - height: Field, -} - -fn main(age: Field, height: Field) { - let person = Person { - age: age, - height: height, - }; - println(person); - println(age + height); - println("Hello world!"); -} -``` - -You can print different types in the same statement (including strings) with a type called `fmtstr`. It can be specified in the same way as a normal string, just prepended with an "f" character: - -```rust - let fmt_str = f"i: {i}, j: {j}"; - println(fmt_str); - - let s = myStruct { y: x, x: y }; - println(s); - - println(f"i: {i}, s: {s}"); - - println(x); - println([x, y]); - - let foo = fooStruct { my_struct: s, foo: 15 }; - println(f"s: {s}, foo: {foo}"); - - println(15); // prints 0x0f, implicit Field - println(-1 as u8); // prints 255 - println(-1 as i8); // prints -1 -``` - -Examples shown above are interchangeable between the two `print` statements: - -```rust -let person = Person { age : age, height : height }; - -println(person); -print(person); - -println("Hello world!"); // Prints with a newline at the end of the input -print("Hello world!"); // Prints the input and keeps cursor on the same line -``` diff --git a/docs/versioned_docs/version-v0.39.0/noir/standard_library/mem.md b/docs/versioned_docs/version-v0.39.0/noir/standard_library/mem.md deleted file mode 100644 index 95d36ac2a72..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/standard_library/mem.md +++ /dev/null @@ -1,52 +0,0 @@ ---- -title: Memory Module -description: - This module contains functions which manipulate memory in a low-level way -keywords: - [ - mem, memory, zeroed, transmute, checked_transmute - ] ---- - -# `std::mem::zeroed` - -```rust -fn zeroed() -> T -``` - -Returns 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. - -This function currently supports the following types: - -- Field -- Bool -- Uint -- Array -- Slice -- String -- Tuple -- Functions - -Using it on other types could result in unexpected behavior. - -# `std::mem::checked_transmute` - -```rust -fn checked_transmute(value: T) -> U -``` - -Transmutes a value of one type into the same value but with a new type `U`. - -This function is safe to use since both types are asserted to be equal later during compilation after the concrete values for generic types become known. -This function is useful for cases where the compiler may fails a type check that is expected to pass where -a user knows the two types to be equal. For example, when using arithmetic generics there are cases the compiler -does not see as equal, such as `[Field; N*(A + B)]` and `[Field; N*A + N*B]`, which users may know to be equal. -In these cases, `checked_transmute` can be used to cast the value to the desired type while also preserving safety -by checking this equality once `N`, `A`, `B` are fully resolved. - -Note that since this safety check is performed after type checking rather than during, no error is issued if the function -containing `checked_transmute` is never called. diff --git a/docs/versioned_docs/version-v0.39.0/noir/standard_library/merkle_trees.md b/docs/versioned_docs/version-v0.39.0/noir/standard_library/merkle_trees.md deleted file mode 100644 index 6a9ebf72ada..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/standard_library/merkle_trees.md +++ /dev/null @@ -1,58 +0,0 @@ ---- -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/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.as_slice()); - 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.39.0/noir/standard_library/meta/ctstring.md b/docs/versioned_docs/version-v0.39.0/noir/standard_library/meta/ctstring.md deleted file mode 100644 index b76f873ca03..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/standard_library/meta/ctstring.md +++ /dev/null @@ -1,100 +0,0 @@ ---- -title: CtString ---- - -`std::meta::ctstring` contains methods on the built-in `CtString` type which is -a compile-time, dynamically-sized string type. Compared to `str` and `fmtstr`, -`CtString` is useful because its size does not need to be specified in its type. This -can be used for formatting items at compile-time or general string handling in `comptime` -code. - -Since `fmtstr`s can be converted into `CtString`s, you can make use of their formatting -abilities in CtStrings by formatting in `fmtstr`s then converting the result to a CtString -afterward. - -## Traits - -### AsCtString - -```rust title="as-ctstring" showLineNumbers -pub trait AsCtString { - comptime fn as_ctstring(self) -> CtString; -} -``` -> Source code: noir_stdlib/src/meta/ctstring.nr#L43-L47 - - -Converts an object into a compile-time string. - -Implementations: - -```rust -impl AsCtString for str { ... } -impl AsCtString for fmtstr { ... } -``` - -## Methods - -### new - -```rust title="new" showLineNumbers -pub comptime fn new() -> Self { -``` -> Source code: noir_stdlib/src/meta/ctstring.nr#L4-L6 - - -Creates an empty `CtString`. - -### append_str - -```rust title="append_str" showLineNumbers -pub comptime fn append_str(self, s: str) -> Self { -``` -> Source code: noir_stdlib/src/meta/ctstring.nr#L11-L13 - - -Returns a new CtString with the given str appended onto the end. - -### append_fmtstr - -```rust title="append_fmtstr" showLineNumbers -pub comptime fn append_fmtstr(self, s: fmtstr) -> Self { -``` -> Source code: noir_stdlib/src/meta/ctstring.nr#L17-L19 - - -Returns a new CtString with the given fmtstr appended onto the end. - -### as_quoted_str - -```rust title="as_quoted_str" showLineNumbers -pub comptime fn as_quoted_str(self) -> Quoted { -``` -> Source code: noir_stdlib/src/meta/ctstring.nr#L26-L28 - - -Returns a quoted string literal from this string's contents. - -There is no direct conversion from a `CtString` to a `str` since -the size would not be known. To get around this, this function can -be used in combination with macro insertion (`!`) to insert this string -literal at this function's call site. - -Example: - -```rust title="as_quoted_str_example" showLineNumbers -let my_ctstring = "foo bar".as_ctstring(); - let my_str = my_ctstring.as_quoted_str!(); - - assert_eq(crate::meta::type_of(my_str), quote { str<7> }.as_type()); -``` -> Source code: noir_stdlib/src/meta/ctstring.nr#L92-L97 - - -## Trait Implementations - -```rust -impl Eq for CtString -impl Hash for CtString -impl Append for CtString -``` diff --git a/docs/versioned_docs/version-v0.39.0/noir/standard_library/meta/expr.md b/docs/versioned_docs/version-v0.39.0/noir/standard_library/meta/expr.md deleted file mode 100644 index b6d395c6700..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/standard_library/meta/expr.md +++ /dev/null @@ -1,380 +0,0 @@ ---- -title: Expr ---- - -`std::meta::expr` contains methods on the built-in `Expr` type for quoted, syntactically valid expressions. - -## Methods - -### as_array - -```rust title="as_array" showLineNumbers -pub comptime fn as_array(self) -> Option<[Expr]> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L10-L12 - - -If this expression is an array, this returns a slice of each element in the array. - -### as_assert - -```rust title="as_assert" showLineNumbers -pub comptime fn as_assert(self) -> Option<(Expr, Option)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L16-L18 - - -If this expression is an assert, this returns the assert expression and the optional message. - -### as_assert_eq - -```rust title="as_assert_eq" showLineNumbers -pub comptime fn as_assert_eq(self) -> Option<(Expr, Expr, Option)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L23-L25 - - -If this expression is an assert_eq, this returns the left-hand-side and right-hand-side -expressions, together with the optional message. - -### as_assign - -```rust title="as_assign" showLineNumbers -pub comptime fn as_assign(self) -> Option<(Expr, Expr)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L30-L32 - - -If this expression is an assignment, this returns a tuple with the left hand side -and right hand side in order. - -### as_binary_op - -```rust title="as_binary_op" showLineNumbers -pub comptime fn as_binary_op(self) -> Option<(Expr, BinaryOp, Expr)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L37-L39 - - -If this expression is a binary operator operation ` `, -return the left-hand side, operator, and the right-hand side of the operation. - -### as_block - -```rust title="as_block" showLineNumbers -pub comptime fn as_block(self) -> Option<[Expr]> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L44-L46 - - -If this expression is a block `{ stmt1; stmt2; ...; stmtN }`, return -a slice containing each statement. - -### as_bool - -```rust title="as_bool" showLineNumbers -pub comptime fn as_bool(self) -> Option {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L50-L52 - - -If this expression is a boolean literal, return that literal. - -### as_cast - -```rust title="as_cast" showLineNumbers -#[builtin(expr_as_cast)] - pub comptime fn as_cast(self) -> Option<(Expr, UnresolvedType)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L56-L59 - - -If this expression is a cast expression (`expr as type`), returns the casted -expression and the type to cast to. - -### as_comptime - -```rust title="as_comptime" showLineNumbers -pub comptime fn as_comptime(self) -> Option<[Expr]> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L64-L66 - - -If this expression is a `comptime { stmt1; stmt2; ...; stmtN }` block, -return each statement in the block. - -### as_constructor - -```rust title="as_constructor" showLineNumbers -pub comptime fn as_constructor(self) -> Option<(UnresolvedType, [(Quoted, Expr)])> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L71-L73 - - -If this expression is a constructor `Type { field1: expr1, ..., fieldN: exprN }`, -return the type and the fields. - -### as_for - -```rust title="as_for" showLineNumbers -pub comptime fn as_for(self) -> Option<(Quoted, Expr, Expr)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L78-L80 - - -If this expression is a for statement over a single expression, return the identifier, -the expression and the for loop body. - -### as_for_range - -```rust title="as_for" showLineNumbers -pub comptime fn as_for(self) -> Option<(Quoted, Expr, Expr)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L78-L80 - - -If this expression is a for statement over a range, return the identifier, -the range start, the range end and the for loop body. - -### as_function_call - -```rust title="as_function_call" showLineNumbers -pub comptime fn as_function_call(self) -> Option<(Expr, [Expr])> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L92-L94 - - -If this expression is a function call `foo(arg1, ..., argN)`, return -the function and a slice of each argument. - -### as_if - -```rust title="as_if" showLineNumbers -pub comptime fn as_if(self) -> Option<(Expr, Expr, Option)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L100-L102 - - -If this expression is an `if condition { then_branch } else { else_branch }`, -return the condition, then branch, and else branch. If there is no else branch, -`None` is returned for that branch instead. - -### as_index - -```rust title="as_index" showLineNumbers -pub comptime fn as_index(self) -> Option<(Expr, Expr)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L107-L109 - - -If this expression is an index into an array `array[index]`, return the -array and the index. - -### as_integer - -```rust title="as_integer" showLineNumbers -pub comptime fn as_integer(self) -> Option<(Field, bool)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L114-L116 - - -If this expression is an integer literal, return the integer as a field -as well as whether the integer is negative (true) or not (false). - -### as_lambda - -```rust title="as_lambda" showLineNumbers -pub comptime fn as_lambda( - self, - ) -> Option<([(Expr, Option)], Option, Expr)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L120-L124 - - -If this expression is a lambda, returns the parameters, return type and body. - -### as_let - -```rust title="as_let" showLineNumbers -pub comptime fn as_let(self) -> Option<(Expr, Option, Expr)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L129-L131 - - -If this expression is a let statement, returns the let pattern as an `Expr`, -the optional type annotation, and the assigned expression. - -### as_member_access - -```rust title="as_member_access" showLineNumbers -pub comptime fn as_member_access(self) -> Option<(Expr, Quoted)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L136-L138 - - -If this expression is a member access `foo.bar`, return the struct/tuple -expression and the field. The field will be represented as a quoted value. - -### as_method_call - -```rust title="as_method_call" showLineNumbers -pub comptime fn as_method_call(self) -> Option<(Expr, Quoted, [UnresolvedType], [Expr])> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L143-L145 - - -If this expression is a method call `foo.bar::(arg1, ..., argN)`, return -the receiver, method name, a slice of each generic argument, and a slice of each argument. - -### as_repeated_element_array - -```rust title="as_repeated_element_array" showLineNumbers -pub comptime fn as_repeated_element_array(self) -> Option<(Expr, Expr)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L150-L152 - - -If this expression is a repeated element array `[elem; length]`, return -the repeated element and the length expressions. - -### as_repeated_element_slice - -```rust title="as_repeated_element_slice" showLineNumbers -pub comptime fn as_repeated_element_slice(self) -> Option<(Expr, Expr)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L157-L159 - - -If this expression is a repeated element slice `[elem; length]`, return -the repeated element and the length expressions. - -### as_slice - -```rust title="as_slice" showLineNumbers -pub comptime fn as_slice(self) -> Option<[Expr]> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L164-L166 - - -If this expression is a slice literal `&[elem1, ..., elemN]`, -return each element of the slice. - -### as_tuple - -```rust title="as_tuple" showLineNumbers -pub comptime fn as_tuple(self) -> Option<[Expr]> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L171-L173 - - -If this expression is a tuple `(field1, ..., fieldN)`, -return each element of the tuple. - -### as_unary_op - -```rust title="as_unary_op" showLineNumbers -pub comptime fn as_unary_op(self) -> Option<(UnaryOp, Expr)> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L178-L180 - - -If this expression is a unary operation ` `, -return the unary operator as well as the right-hand side expression. - -### as_unsafe - -```rust title="as_unsafe" showLineNumbers -pub comptime fn as_unsafe(self) -> Option<[Expr]> {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L185-L187 - - -If this expression is an `unsafe { stmt1; ...; stmtN }` block, -return each statement inside in a slice. - -### has_semicolon - -```rust title="has_semicolon" showLineNumbers -pub comptime fn has_semicolon(self) -> bool {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L206-L208 - - -`true` if this expression is trailed by a semicolon. E.g. - -``` -comptime { - let expr1 = quote { 1 + 2 }.as_expr().unwrap(); - let expr2 = quote { 1 + 2; }.as_expr().unwrap(); - - assert(expr1.as_binary_op().is_some()); - assert(expr2.as_binary_op().is_some()); - - assert(!expr1.has_semicolon()); - assert(expr2.has_semicolon()); -} -``` - -### is_break - -```rust title="is_break" showLineNumbers -pub comptime fn is_break(self) -> bool {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L212-L214 - - -`true` if this expression is `break`. - -### is_continue - -```rust title="is_continue" showLineNumbers -pub comptime fn is_continue(self) -> bool {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L218-L220 - - -`true` if this expression is `continue`. - -### modify - -```rust title="modify" showLineNumbers -pub comptime fn modify(self, f: fn[Env](Expr) -> Option) -> Expr { -``` -> Source code: noir_stdlib/src/meta/expr.nr#L229-L231 - - -Applies a mapping function to this expression and to all of its sub-expressions. -`f` will be applied to each sub-expression first, then applied to the expression itself. - -This happens recursively for every expression within `self`. - -For example, calling `modify` on `(&[1], &[2, 3])` with an `f` that returns `Option::some` -for expressions that are integers, doubling them, would return `(&[2], &[4, 6])`. - -### quoted - -```rust title="quoted" showLineNumbers -pub comptime fn quoted(self) -> Quoted { -``` -> Source code: noir_stdlib/src/meta/expr.nr#L266-L268 - - -Returns this expression as a `Quoted` value. It's the same as `quote { $self }`. - -### resolve - -```rust title="resolve" showLineNumbers -pub comptime fn resolve(self, in_function: Option) -> TypedExpr {} -``` -> Source code: noir_stdlib/src/meta/expr.nr#L282-L284 - - -Resolves and type-checks this expression and returns the result as a `TypedExpr`. - -The `in_function` argument specifies where the expression is resolved: -- If it's `none`, the expression is resolved in the function where `resolve` was called -- If it's `some`, the expression is resolved in the given function - -If any names used by this expression are not in scope or if there are any type errors, -this will give compiler errors as if the expression was written directly into -the current `comptime` function. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.39.0/noir/standard_library/meta/function_def.md b/docs/versioned_docs/version-v0.39.0/noir/standard_library/meta/function_def.md deleted file mode 100644 index b7f2ebdb889..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/standard_library/meta/function_def.md +++ /dev/null @@ -1,166 +0,0 @@ ---- -title: FunctionDefinition ---- - -`std::meta::function_def` contains methods on the built-in `FunctionDefinition` type representing -a function definition in the source program. - -## Methods - -### add_attribute - -```rust title="add_attribute" showLineNumbers -pub comptime fn add_attribute(self, attribute: str) {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L3-L5 - - -Adds an attribute to the function. This is only valid -on functions in the current crate which have not yet been resolved. -This means any functions called at compile-time are invalid targets for this method. - -### body - -```rust title="body" showLineNumbers -pub comptime fn body(self) -> Expr {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L8-L10 - - -Returns the body of the function as an expression. This is only valid -on functions in the current crate which have not yet been resolved. -This means any functions called at compile-time are invalid targets for this method. - -### has_named_attribute - -```rust title="has_named_attribute" showLineNumbers -pub comptime fn has_named_attribute(self, name: str) -> bool {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L13-L15 - - -Returns true if this function has a custom attribute with the given name. - -### is_unconstrained - -```rust title="is_unconstrained" showLineNumbers -pub comptime fn is_unconstrained(self) -> bool {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L18-L20 - - -Returns true if this function is unconstrained. - -### module - -```rust title="module" showLineNumbers -pub comptime fn module(self) -> Module {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L23-L25 - - -Returns the module where the function is defined. - -### name - -```rust title="name" showLineNumbers -pub comptime fn name(self) -> Quoted {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L28-L30 - - -Returns the name of the function. - -### parameters - -```rust title="parameters" showLineNumbers -pub comptime fn parameters(self) -> [(Quoted, Type)] {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L33-L35 - - -Returns each parameter of the function as a tuple of (parameter pattern, parameter type). - -### return_type - -```rust title="return_type" showLineNumbers -pub comptime fn return_type(self) -> Type {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L38-L40 - - -The return type of the function. - -### set_body - -```rust title="set_body" showLineNumbers -pub comptime fn set_body(self, body: Expr) {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L43-L45 - - -Mutate the function body to a new expression. This is only valid -on functions in the current crate which have not yet been resolved. -This means any functions called at compile-time are invalid targets for this method. - -### set_parameters - -```rust title="set_parameters" showLineNumbers -pub comptime fn set_parameters(self, parameters: [(Quoted, Type)]) {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L48-L50 - - -Mutates the function's parameters to a new set of parameters. This is only valid -on functions in the current crate which have not yet been resolved. -This means any functions called at compile-time are invalid targets for this method. - -Expects a slice of (parameter pattern, parameter type) for each parameter. Requires -each parameter pattern to be a syntactically valid parameter. - -### set_return_type - -```rust title="set_return_type" showLineNumbers -pub comptime fn set_return_type(self, return_type: Type) {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L53-L55 - - -Mutates the function's return type to a new type. This is only valid -on functions in the current crate which have not yet been resolved. -This means any functions called at compile-time are invalid targets for this method. - -### set_return_public - -```rust title="set_return_public" showLineNumbers -pub comptime fn set_return_public(self, public: bool) {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L58-L60 - - -Mutates the function's return visibility to public (if `true` is given) or private (if `false` is given). -This is only valid on functions in the current crate which have not yet been resolved. -This means any functions called at compile-time are invalid targets for this method. - -### set_unconstrained - -```rust title="set_unconstrained" showLineNumbers -pub comptime fn set_unconstrained(self, value: bool) {} -``` -> Source code: noir_stdlib/src/meta/function_def.nr#L66-L68 - - -Mutates the function to be unconstrained (if `true` is given) or not (if `false` is given). -This is only valid on functions in the current crate which have not yet been resolved. -This means any functions called at compile-time are invalid targets for this method. - -## Trait Implementations - -```rust -impl Eq for FunctionDefinition -impl Hash for FunctionDefinition -``` - -Note that each function is assigned a unique ID internally and this is what is used for -equality and hashing. So even functions with identical signatures and bodies may not -be equal in this sense if they were originally different items in the source program. diff --git a/docs/versioned_docs/version-v0.39.0/noir/standard_library/meta/index.md b/docs/versioned_docs/version-v0.39.0/noir/standard_library/meta/index.md deleted file mode 100644 index 14544c07442..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/standard_library/meta/index.md +++ /dev/null @@ -1,224 +0,0 @@ ---- -title: Metaprogramming -description: Noir's Metaprogramming API -keywords: [metaprogramming, comptime, macros, macro, quote, unquote] ---- - -`std::meta` is the entry point for Noir's metaprogramming API. This consists of `comptime` functions -and types used for inspecting and modifying Noir programs. - -## Functions - -### type_of - -```rust title="type_of" showLineNumbers -pub comptime fn type_of(x: T) -> Type {} -``` -> Source code: noir_stdlib/src/meta/mod.nr#L27-L29 - - -Returns the type of a variable at compile-time. - -Example: -```rust -comptime { - let x: i32 = 1; - let x_type: Type = std::meta::type_of(x); - - assert_eq(x_type, quote { i32 }.as_type()); -} -``` - -### unquote - -```rust title="unquote" showLineNumbers -pub comptime fn unquote(code: Quoted) -> Quoted { -``` -> Source code: noir_stdlib/src/meta/mod.nr#L19-L21 - - -Unquotes the passed-in token stream where this function was called. - -Example: -```rust -comptime { - let code = quote { 1 + 2 }; - - // let x = 1 + 2; - let x = unquote!(code); -} -``` - -### derive - -```rust title="derive" showLineNumbers -#[varargs] -pub comptime fn derive(s: StructDefinition, traits: [TraitDefinition]) -> Quoted { -``` -> Source code: noir_stdlib/src/meta/mod.nr#L48-L51 - - -Attribute placed on struct definitions. - -Creates a trait impl for each trait passed in as an argument. -To do this, the trait must have a derive handler registered -with `derive_via` beforehand. The traits in the stdlib that -can be derived this way are `Eq`, `Ord`, `Default`, and `Hash`. - -Example: -```rust -#[derive(Eq, Default)] -struct Foo { - x: i32, - y: T, -} - -fn main() { - let foo1 = Foo::default(); - let foo2 = Foo { x: 0, y: &[0] }; - assert_eq(foo1, foo2); -} -``` - -### derive_via - -```rust title="derive_via_signature" showLineNumbers -pub comptime fn derive_via(t: TraitDefinition, f: DeriveFunction) { -``` -> Source code: noir_stdlib/src/meta/mod.nr#L68-L70 - - -Attribute placed on trait definitions. - -Registers a function to create impls for the given trait -when the trait is used in a `derive` call. Users may use -this to register their own functions to enable their traits -to be derived by `derive`. - -Because this function requires a function as an argument which -should produce a trait impl for any given struct, users may find -it helpful to use a function like `std::meta::make_trait_impl` to -help creating these impls. - -Example: -```rust -#[derive_via(derive_do_nothing)] -trait DoNothing { - fn do_nothing(self); -} - -comptime fn derive_do_nothing(s: StructDefinition) -> Quoted { - let typ = s.as_type(); - quote { - impl DoNothing for $typ { - fn do_nothing(self) { - println("Nothing"); - } - } - } -} -``` - -As another example, `derive_eq` in the stdlib is used to derive the `Eq` -trait for any struct. It makes use of `make_trait_impl` to do this: - -```rust title="derive_eq" showLineNumbers -comptime fn derive_eq(s: StructDefinition) -> Quoted { - let signature = quote { fn eq(_self: Self, _other: Self) -> bool }; - let for_each_field = |name| quote { (_self.$name == _other.$name) }; - let body = |fields| { - if s.fields().len() == 0 { - quote { true } - } else { - fields - } - }; - crate::meta::make_trait_impl( - s, - quote { Eq }, - signature, - for_each_field, - quote { & }, - body, - ) -} -``` -> Source code: noir_stdlib/src/cmp.nr#L10-L30 - - -### make_trait_impl - -```rust title="make_trait_impl" showLineNumbers -pub comptime fn make_trait_impl( - s: StructDefinition, - trait_name: Quoted, - function_signature: Quoted, - for_each_field: fn[Env1](Quoted) -> Quoted, - join_fields_with: Quoted, - body: fn[Env2](Quoted) -> Quoted, -) -> Quoted { -``` -> Source code: noir_stdlib/src/meta/mod.nr#L87-L96 - - -A helper function to more easily create trait impls while deriving traits. - -Note that this function only works for traits which: -1. Have only one method -2. Have no generics on the trait itself. - - E.g. Using this on a trait such as `trait Foo { ... }` will result in the - generated impl incorrectly missing the `T` generic. - -If your trait fits these criteria then `make_trait_impl` is likely the easiest -way to write your derive handler. The arguments are as follows: - -- `s`: The struct to make the impl for -- `trait_name`: The name of the trait to derive. E.g. `quote { Eq }`. -- `function_signature`: The signature of the trait method to derive. E.g. `fn eq(self, other: Self) -> bool`. -- `for_each_field`: An operation to be performed on each field. E.g. `|name| quote { (self.$name == other.$name) }`. -- `join_fields_with`: A separator to join each result of `for_each_field` with. - E.g. `quote { & }`. You can also use an empty `quote {}` for no separator. -- `body`: The result of the field operations are passed into this function for any final processing. - This is the place to insert any setup/teardown code the trait requires. If the trait doesn't require - any such code, you can return the body as-is: `|body| body`. - -Example deriving `Hash`: - -```rust title="derive_hash" showLineNumbers -comptime fn derive_hash(s: StructDefinition) -> Quoted { - let name = quote { Hash }; - let signature = quote { fn hash(_self: Self, _state: &mut H) where H: std::hash::Hasher }; - let for_each_field = |name| quote { _self.$name.hash(_state); }; - crate::meta::make_trait_impl( - s, - name, - signature, - for_each_field, - quote {}, - |fields| fields, - ) -} -``` -> Source code: noir_stdlib/src/hash/mod.nr#L137-L151 - - -Example deriving `Ord`: - -```rust title="derive_ord" showLineNumbers -comptime fn derive_ord(s: StructDefinition) -> Quoted { - let signature = quote { fn cmp(_self: Self, _other: Self) -> std::cmp::Ordering }; - let for_each_field = |name| quote { - if result == std::cmp::Ordering::equal() { - result = _self.$name.cmp(_other.$name); - } - }; - let body = |fields| quote { - let mut result = std::cmp::Ordering::equal(); - $fields - result - }; - crate::meta::make_trait_impl(s, quote { Ord }, signature, for_each_field, quote {}, body) -} -``` -> Source code: noir_stdlib/src/cmp.nr#L216-L231 - diff --git a/docs/versioned_docs/version-v0.39.0/noir/standard_library/meta/module.md b/docs/versioned_docs/version-v0.39.0/noir/standard_library/meta/module.md deleted file mode 100644 index f47231972b7..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/standard_library/meta/module.md +++ /dev/null @@ -1,82 +0,0 @@ ---- -title: Module ---- - -`std::meta::module` contains methods on the built-in `Module` type which represents a module in the source program. -Note that this type represents a module generally, it isn't limited to only `mod my_submodule { ... }` -declarations in the source program. - -## Methods - -### add_item - -```rust title="add_item" showLineNumbers -pub comptime fn add_item(self, item: Quoted) {} -``` -> Source code: noir_stdlib/src/meta/module.nr#L3-L5 - - -Adds a top-level item (a function, a struct, a global, etc.) to the module. -Adding multiple items in one go is also valid if the `Quoted` value has multiple items in it. -Note that the items are type-checked as if they are inside the module they are being added to. - -### functions - -```rust title="functions" showLineNumbers -pub comptime fn functions(self) -> [FunctionDefinition] {} -``` -> Source code: noir_stdlib/src/meta/module.nr#L18-L20 - - -Returns each function defined in the module. - -### has_named_attribute - -```rust title="has_named_attribute" showLineNumbers -pub comptime fn has_named_attribute(self, name: str) -> bool {} -``` -> Source code: noir_stdlib/src/meta/module.nr#L8-L10 - - -Returns true if this module has a custom attribute with the given name. - -### is_contract - -```rust title="is_contract" showLineNumbers -pub comptime fn is_contract(self) -> bool {} -``` -> Source code: noir_stdlib/src/meta/module.nr#L13-L15 - - -`true` if this module is a contract module (was declared via `contract foo { ... }`). - -### name - -```rust title="name" showLineNumbers -pub comptime fn name(self) -> Quoted {} -``` -> Source code: noir_stdlib/src/meta/module.nr#L28-L30 - - -Returns the name of the module. - -### structs - -```rust title="structs" showLineNumbers -pub comptime fn structs(self) -> [StructDefinition] {} -``` -> Source code: noir_stdlib/src/meta/module.nr#L23-L25 - - -Returns each struct defined in the module. - -## Trait Implementations - -```rust -impl Eq for Module -impl Hash for Module -``` - -Note that each module is assigned a unique ID internally and this is what is used for -equality and hashing. So even modules with identical names and contents may not -be equal in this sense if they were originally different items in the source program. diff --git a/docs/versioned_docs/version-v0.39.0/noir/standard_library/meta/op.md b/docs/versioned_docs/version-v0.39.0/noir/standard_library/meta/op.md deleted file mode 100644 index 03ea49ad8ec..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/standard_library/meta/op.md +++ /dev/null @@ -1,244 +0,0 @@ ---- -title: UnaryOp and BinaryOp ---- - -`std::meta::op` contains the `UnaryOp` and `BinaryOp` types as well as methods on them. -These types are used to represent a unary or binary operator respectively in Noir source code. - -## Types - -### UnaryOp - -Represents a unary operator. One of `-`, `!`, `&mut`, or `*`. - -### Methods - -#### is_minus - -```rust title="is_minus" showLineNumbers -pub fn is_minus(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L24-L26 - - -Returns `true` if this operator is `-`. - -#### is_not - -```rust title="is_not" showLineNumbers -pub fn is_not(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L30-L32 - - -`true` if this operator is `!` - -#### is_mutable_reference - -```rust title="is_mutable_reference" showLineNumbers -pub fn is_mutable_reference(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L36-L38 - - -`true` if this operator is `&mut` - -#### is_dereference - -```rust title="is_dereference" showLineNumbers -pub fn is_dereference(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L42-L44 - - -`true` if this operator is `*` - -#### quoted - -```rust title="unary_quoted" showLineNumbers -pub comptime fn quoted(self) -> Quoted { -``` -> Source code: noir_stdlib/src/meta/op.nr#L48-L50 - - -Returns this operator as a `Quoted` value. - -### Trait Implementations - -```rust -impl Eq for UnaryOp -impl Hash for UnaryOp -``` - -### BinaryOp - -Represents a binary operator. One of `+`, `-`, `*`, `/`, `%`, `==`, `!=`, `<`, `<=`, `>`, `>=`, `&`, `|`, `^`, `>>`, or `<<`. - -### Methods - -#### is_add - -```rust title="is_add" showLineNumbers -pub fn is_add(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L86-L88 - - -`true` if this operator is `+` - -#### is_subtract - -```rust title="is_subtract" showLineNumbers -pub fn is_subtract(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L92-L94 - - -`true` if this operator is `-` - -#### is_multiply - -```rust title="is_multiply" showLineNumbers -pub fn is_multiply(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L98-L100 - - -`true` if this operator is `*` - -#### is_divide - -```rust title="is_divide" showLineNumbers -pub fn is_divide(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L104-L106 - - -`true` if this operator is `/` - -#### is_modulo - -```rust title="is_modulo" showLineNumbers -pub fn is_modulo(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L176-L178 - - -`true` if this operator is `%` - -#### is_equal - -```rust title="is_equal" showLineNumbers -pub fn is_equal(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L110-L112 - - -`true` if this operator is `==` - -#### is_not_equal - -```rust title="is_not_equal" showLineNumbers -pub fn is_not_equal(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L116-L118 - - -`true` if this operator is `!=` - -#### is_less_than - -```rust title="is_less_than" showLineNumbers -pub fn is_less_than(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L122-L124 - - -`true` if this operator is `<` - -#### is_less_than_or_equal - -```rust title="is_less_than_or_equal" showLineNumbers -pub fn is_less_than_or_equal(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L128-L130 - - -`true` if this operator is `<=` - -#### is_greater_than - -```rust title="is_greater_than" showLineNumbers -pub fn is_greater_than(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L134-L136 - - -`true` if this operator is `>` - -#### is_greater_than_or_equal - -```rust title="is_greater_than_or_equal" showLineNumbers -pub fn is_greater_than_or_equal(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L140-L142 - - -`true` if this operator is `>=` - -#### is_and - -```rust title="is_and" showLineNumbers -pub fn is_and(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L146-L148 - - -`true` if this operator is `&` - -#### is_or - -```rust title="is_or" showLineNumbers -pub fn is_or(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L152-L154 - - -`true` if this operator is `|` - -#### is_shift_right - -```rust title="is_shift_right" showLineNumbers -pub fn is_shift_right(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L164-L166 - - -`true` if this operator is `>>` - -#### is_shift_left - -```rust title="is_shift_right" showLineNumbers -pub fn is_shift_right(self) -> bool { -``` -> Source code: noir_stdlib/src/meta/op.nr#L164-L166 - - -`true` if this operator is `<<` - -#### quoted - -```rust title="binary_quoted" showLineNumbers -pub comptime fn quoted(self) -> Quoted { -``` -> Source code: noir_stdlib/src/meta/op.nr#L182-L184 - - -Returns this operator as a `Quoted` value. - -### Trait Implementations - -```rust -impl Eq for BinaryOp -impl Hash for BinaryOp -``` diff --git a/docs/versioned_docs/version-v0.39.0/noir/standard_library/meta/quoted.md b/docs/versioned_docs/version-v0.39.0/noir/standard_library/meta/quoted.md deleted file mode 100644 index d7acf23bc07..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/standard_library/meta/quoted.md +++ /dev/null @@ -1,141 +0,0 @@ ---- -title: Quoted ---- - -`std::meta::quoted` contains methods on the built-in `Quoted` type which represents -quoted token streams and is the result of the `quote { ... }` expression. - -## Methods - -### as_expr - -```rust title="as_expr" showLineNumbers -pub comptime fn as_expr(self) -> Option {} -``` -> Source code: noir_stdlib/src/meta/quoted.nr#L6-L8 - - -Parses the quoted token stream as an expression. Returns `Option::none()` if -the expression failed to parse. - -Example: - -```rust title="as_expr_example" showLineNumbers -#[test] - fn test_expr_as_function_call() { - comptime - { - let expr = quote { foo(42) }.as_expr().unwrap(); - let (_function, args) = expr.as_function_call().unwrap(); - assert_eq(args.len(), 1); - assert_eq(args[0].as_integer().unwrap(), (42, false)); - } - } -``` -> Source code: test_programs/noir_test_success/comptime_expr/src/main.nr#L360-L371 - - -### as_module - -```rust title="as_module" showLineNumbers -pub comptime fn as_module(self) -> Option {} -``` -> Source code: noir_stdlib/src/meta/quoted.nr#L11-L13 - - -Interprets this token stream as a module path leading to the name of a module. -Returns `Option::none()` if the module isn't found or this token stream cannot be parsed as a path. - -Example: - -```rust title="as_module_example" showLineNumbers -mod baz { - pub mod qux {} -} - -#[test] -fn as_module_test() { - comptime { - let my_mod = quote { baz::qux }.as_module().unwrap(); - assert_eq(my_mod.name(), quote { qux }); - } -} -``` -> Source code: test_programs/compile_success_empty/comptime_module/src/main.nr#L115-L127 - - -### as_trait_constraint - -```rust title="as_trait_constraint" showLineNumbers -pub comptime fn as_trait_constraint(self) -> TraitConstraint {} -``` -> Source code: noir_stdlib/src/meta/quoted.nr#L16-L18 - - -Interprets this token stream as a trait constraint (without an object type). -Note that this function panics instead of returning `Option::none()` if the token -stream does not parse and resolve to a valid trait constraint. - -Example: - -```rust title="implements_example" showLineNumbers -pub fn function_with_where(_x: T) -where - T: SomeTrait, -{ - comptime { - let t = quote { T }.as_type(); - let some_trait_i32 = quote { SomeTrait }.as_trait_constraint(); - assert(t.implements(some_trait_i32)); - - assert(t.get_trait_impl(some_trait_i32).is_none()); - } -} -``` -> Source code: test_programs/compile_success_empty/comptime_type/src/main.nr#L160-L173 - - -### as_type - -```rust title="as_type" showLineNumbers -pub comptime fn as_type(self) -> Type {} -``` -> Source code: noir_stdlib/src/meta/quoted.nr#L21-L23 - - -Interprets this token stream as a resolved type. Panics if the token -stream doesn't parse to a type or if the type isn't a valid type in scope. - -```rust title="implements_example" showLineNumbers -pub fn function_with_where(_x: T) -where - T: SomeTrait, -{ - comptime { - let t = quote { T }.as_type(); - let some_trait_i32 = quote { SomeTrait }.as_trait_constraint(); - assert(t.implements(some_trait_i32)); - - assert(t.get_trait_impl(some_trait_i32).is_none()); - } -} -``` -> Source code: test_programs/compile_success_empty/comptime_type/src/main.nr#L160-L173 - - -### tokens - -```rust title="tokens" showLineNumbers -pub comptime fn tokens(self) -> [Quoted] {} -``` -> Source code: noir_stdlib/src/meta/quoted.nr#L26-L28 - - -Returns a slice of the individual tokens that form this token stream. - -## Trait Implementations - -```rust -impl Eq for Quoted -impl Hash for Quoted -``` diff --git a/docs/versioned_docs/version-v0.39.0/noir/standard_library/meta/struct_def.md b/docs/versioned_docs/version-v0.39.0/noir/standard_library/meta/struct_def.md deleted file mode 100644 index fd609942f4e..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/standard_library/meta/struct_def.md +++ /dev/null @@ -1,177 +0,0 @@ ---- -title: StructDefinition ---- - -`std::meta::struct_def` contains methods on the built-in `StructDefinition` type. -This type corresponds to `struct Name { field1: Type1, ... }` items in the source program. - -## Methods - -### add_attribute - -```rust title="add_attribute" showLineNumbers -pub comptime fn add_attribute(self, attribute: str) {} -``` -> Source code: noir_stdlib/src/meta/struct_def.nr#L3-L5 - - -Adds an attribute to the struct. - -### add_generic - -```rust title="add_generic" showLineNumbers -pub comptime fn add_generic(self, generic_name: str) -> Type {} -``` -> Source code: noir_stdlib/src/meta/struct_def.nr#L8-L10 - - -Adds an generic to the struct. Returns the new generic type. -Errors if the given generic name isn't a single identifier or if -the struct already has a generic with the same name. - -This method should be used carefully, if there is existing code referring -to the struct type it may be checked before this function is called and -see the struct with the original number of generics. This method should -thus be preferred to use on code generated from other macros and structs -that are not used in function signatures. - -Example: - -```rust title="add-generic-example" showLineNumbers -comptime fn add_generic(s: StructDefinition) { - assert_eq(s.generics().len(), 0); - let new_generic = s.add_generic("T"); - - let generics = s.generics(); - assert_eq(generics.len(), 1); - assert_eq(generics[0], new_generic); - } -``` -> Source code: test_programs/compile_success_empty/comptime_struct_definition/src/main.nr#L35-L44 - - -### as_type - -```rust title="as_type" showLineNumbers -pub comptime fn as_type(self) -> Type {} -``` -> Source code: noir_stdlib/src/meta/struct_def.nr#L15-L17 - - -Returns this struct as a type in the source program. If this struct has -any generics, the generics are also included as-is. - -### generics - -```rust title="generics" showLineNumbers -pub comptime fn generics(self) -> [Type] {} -``` -> Source code: noir_stdlib/src/meta/struct_def.nr#L26-L28 - - -Returns each generic on this struct. - -Example: - -``` -#[example] -struct Foo { - bar: [T; 2], - baz: Baz, -} - -comptime fn example(foo: StructDefinition) { - assert_eq(foo.generics().len(), 2); - - // Fails because `T` isn't in scope - // let t = quote { T }.as_type(); - // assert_eq(foo.generics()[0], t); -} -``` - -### fields - -```rust title="fields" showLineNumbers -pub comptime fn fields(self) -> [(Quoted, Type)] {} -``` -> Source code: noir_stdlib/src/meta/struct_def.nr#L33-L35 - - -Returns each field of this struct as a pair of (field name, field type). - -### has_named_attribute - -```rust title="has_named_attribute" showLineNumbers -pub comptime fn has_named_attribute(self, name: str) -> bool {} -``` -> Source code: noir_stdlib/src/meta/struct_def.nr#L20-L22 - - -Returns true if this struct has a custom attribute with the given name. - -### module - -```rust title="module" showLineNumbers -pub comptime fn module(self) -> Module {} -``` -> Source code: noir_stdlib/src/meta/struct_def.nr#L38-L40 - - -Returns the module where the struct is defined. - -### name - -```rust title="name" showLineNumbers -pub comptime fn name(self) -> Quoted {} -``` -> Source code: noir_stdlib/src/meta/struct_def.nr#L43-L45 - - -Returns the name of this struct - -Note that the returned quoted value will be just the struct name, it will -not be the full path to the struct, nor will it include any generics. - -### set_fields - -```rust title="set_fields" showLineNumbers -pub comptime fn set_fields(self, new_fields: [(Quoted, Type)]) {} -``` -> Source code: noir_stdlib/src/meta/struct_def.nr#L52-L54 - - -Sets the fields of this struct to the given fields list where each element -is a pair of the field's name and the field's type. Expects each field name -to be a single identifier. Note that this will override any previous fields -on this struct. If those should be preserved, use `.fields()` to retrieve the -current fields on the struct type and append the new fields from there. - -Example: - -```rust -// Change this struct to: -// struct Foo { -// a: u32, -// b: i8, -// } -#[mangle_fields] -struct Foo { x: Field } - -comptime fn mangle_fields(s: StructDefinition) { - s.set_fields(&[ - (quote { a }, quote { u32 }.as_type()), - (quote { b }, quote { i8 }.as_type()), - ]); -} -``` - -## Trait Implementations - -```rust -impl Eq for StructDefinition -impl Hash for StructDefinition -``` - -Note that each struct is assigned a unique ID internally and this is what is used for -equality and hashing. So even structs with identical generics and fields may not -be equal in this sense if they were originally different items in the source program. diff --git a/docs/versioned_docs/version-v0.39.0/noir/standard_library/meta/trait_constraint.md b/docs/versioned_docs/version-v0.39.0/noir/standard_library/meta/trait_constraint.md deleted file mode 100644 index 3106f732b5a..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/standard_library/meta/trait_constraint.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -title: TraitConstraint ---- - -`std::meta::trait_constraint` contains methods on the built-in `TraitConstraint` type which represents -a trait constraint that can be used to search for a trait implementation. This is similar -syntactically to just the trait itself, but can also contain generic arguments. E.g. `Eq`, `Default`, -`BuildHasher`. - -This type currently has no public methods but it can be used alongside `Type` in `implements` or `get_trait_impl`. - -## Trait Implementations - -```rust -impl Eq for TraitConstraint -impl Hash for TraitConstraint -``` diff --git a/docs/versioned_docs/version-v0.39.0/noir/standard_library/meta/trait_def.md b/docs/versioned_docs/version-v0.39.0/noir/standard_library/meta/trait_def.md deleted file mode 100644 index e661d3af7f1..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/standard_library/meta/trait_def.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: TraitDefinition ---- - -`std::meta::trait_def` contains methods on the built-in `TraitDefinition` type. This type -represents trait definitions such as `trait Foo { .. }` at the top-level of a program. - -## Methods - -### as_trait_constraint - -```rust title="as_trait_constraint" showLineNumbers -pub comptime fn as_trait_constraint(_self: Self) -> TraitConstraint {} -``` -> Source code: noir_stdlib/src/meta/trait_def.nr#L6-L8 - - -Converts this trait into a trait constraint. If there are any generics on this -trait, they will be kept as-is without instantiating or replacing them. - -## Trait Implementations - -```rust -impl Eq for TraitDefinition -impl Hash for TraitDefinition -``` diff --git a/docs/versioned_docs/version-v0.39.0/noir/standard_library/meta/trait_impl.md b/docs/versioned_docs/version-v0.39.0/noir/standard_library/meta/trait_impl.md deleted file mode 100644 index a527617c1e6..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/standard_library/meta/trait_impl.md +++ /dev/null @@ -1,60 +0,0 @@ ---- -title: TraitImpl ---- - -`std::meta::trait_impl` contains methods on the built-in `TraitImpl` type which represents a trait -implementation such as `impl Foo for Bar { ... }`. - -## Methods - -### trait_generic_args - -```rust title="trait_generic_args" showLineNumbers -pub comptime fn trait_generic_args(self) -> [Type] {} -``` -> Source code: noir_stdlib/src/meta/trait_impl.nr#L3-L5 - - -Returns any generic arguments on the trait of this trait implementation, if any. - -```rs -impl Foo for Bar { ... } - -comptime { - let bar_type = quote { Bar }.as_type(); - let foo = quote { Foo }.as_trait_constraint(); - - let my_impl: TraitImpl = bar_type.get_trait_impl(foo).unwrap(); - - let generics = my_impl.trait_generic_args(); - assert_eq(generics.len(), 2); - - assert_eq(generics[0], quote { i32 }.as_type()); - assert_eq(generics[1], quote { Field }.as_type()); -} -``` - -### methods - -```rust title="methods" showLineNumbers -pub comptime fn methods(self) -> [FunctionDefinition] {} -``` -> Source code: noir_stdlib/src/meta/trait_impl.nr#L8-L10 - - -Returns each method in this trait impl. - -Example: - -```rs -comptime { - let i32_type = quote { i32 }.as_type(); - let eq = quote { Eq }.as_trait_constraint(); - - let impl_eq_for_i32: TraitImpl = i32_type.get_trait_impl(eq).unwrap(); - let methods = impl_eq_for_i32.methods(); - - assert_eq(methods.len(), 1); - assert_eq(methods[0].name(), quote { eq }); -} -``` diff --git a/docs/versioned_docs/version-v0.39.0/noir/standard_library/meta/typ.md b/docs/versioned_docs/version-v0.39.0/noir/standard_library/meta/typ.md deleted file mode 100644 index 90222c222f5..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/standard_library/meta/typ.md +++ /dev/null @@ -1,264 +0,0 @@ ---- -title: Type ---- - -`std::meta::typ` contains methods on the built-in `Type` type used for representing -a type in the source program. - -## Functions - -```rust title="fresh_type_variable" showLineNumbers -pub comptime fn fresh_type_variable() -> Type {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L57-L59 - - -Creates and returns an unbound type variable. This is a special kind of type internal -to type checking which will type check with any other type. When it is type checked -against another type it will also be set to that type. For example, if `a` is a type -variable and we have the type equality `(a, i32) = (u8, i32)`, the compiler will set -`a` equal to `u8`. - -Unbound type variables will often be rendered as `_` while printing them. Bound type -variables will appear as the type they are bound to. - -This can be used in conjunction with functions which internally perform type checks -such as `Type::implements` or `Type::get_trait_impl` to potentially grab some of the types used. - -Note that calling `Type::implements` or `Type::get_trait_impl` on a type variable will always -fail. - -Example: - -```rust title="serialize-setup" showLineNumbers -trait Serialize {} - -impl Serialize<1> for Field {} - -impl Serialize for [T; N] -where - T: Serialize, -{} - -impl Serialize for (T, U) -where - T: Serialize, - U: Serialize, -{} -``` -> Source code: test_programs/compile_success_empty/comptime_type/src/main.nr#L14-L29 - -```rust title="fresh-type-variable-example" showLineNumbers -let typevar1 = std::meta::typ::fresh_type_variable(); - let constraint = quote { Serialize<$typevar1> }.as_trait_constraint(); - let field_type = quote { Field }.as_type(); - - // Search for a trait impl (binding typevar1 to 1 when the impl is found): - assert(field_type.implements(constraint)); - - // typevar1 should be bound to the "1" generic now: - assert_eq(typevar1.as_constant().unwrap(), 1); - - // If we want to do the same with a different type, we need to - // create a new type variable now that `typevar1` is bound - let typevar2 = std::meta::typ::fresh_type_variable(); - let constraint = quote { Serialize<$typevar2> }.as_trait_constraint(); - let array_type = quote { [(Field, Field); 5] }.as_type(); - assert(array_type.implements(constraint)); - - // Now typevar2 should be bound to the serialized pair size 2 times the array length 5 - assert_eq(typevar2.as_constant().unwrap(), 10); -``` -> Source code: test_programs/compile_success_empty/comptime_type/src/main.nr#L129-L149 - - -## Methods - -### as_array - -```rust title="as_array" showLineNumbers -pub comptime fn as_array(self) -> Option<(Type, Type)> {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L76-L78 - - -If this type is an array, return a pair of (element type, size type). - -Example: - -```rust -comptime { - let array_type = quote { [Field; 3] }.as_type(); - let (field_type, three_type) = array_type.as_array().unwrap(); - - assert(field_type.is_field()); - assert_eq(three_type.as_constant().unwrap(), 3); -} -``` - -### as_constant - -```rust title="as_constant" showLineNumbers -pub comptime fn as_constant(self) -> Option {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L83-L85 - - -If this type is a constant integer (such as the `3` in the array type `[Field; 3]`), -return the numeric constant. - -### as_integer - -```rust title="as_integer" showLineNumbers -pub comptime fn as_integer(self) -> Option<(bool, u8)> {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L90-L92 - - -If this is an integer type, return a boolean which is `true` -if the type is signed, as well as the number of bits of this integer type. - -### as_mutable_reference - -```rust title="as_mutable_reference" showLineNumbers -comptime fn as_mutable_reference(self) -> Option {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L96-L98 - - -If this is a mutable reference type `&mut T`, returns the mutable type `T`. - -### as_slice - -```rust title="as_slice" showLineNumbers -pub comptime fn as_slice(self) -> Option {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L102-L104 - - -If this is a slice type, return the element type of the slice. - -### as_str - -```rust title="as_str" showLineNumbers -pub comptime fn as_str(self) -> Option {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L108-L110 - - -If this is a `str` type, returns the length `N` as a type. - -### as_struct - -```rust title="as_struct" showLineNumbers -pub comptime fn as_struct(self) -> Option<(StructDefinition, [Type])> {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L114-L116 - - -If this is a struct type, returns the struct in addition to -any generic arguments on this type. - -### as_tuple - -```rust title="as_tuple" showLineNumbers -pub comptime fn as_tuple(self) -> Option<[Type]> {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L120-L122 - - -If this is a tuple type, returns each element type of the tuple. - -### get_trait_impl - -```rust title="get_trait_impl" showLineNumbers -pub comptime fn get_trait_impl(self, constraint: TraitConstraint) -> Option {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L143-L145 - - -Retrieves the trait implementation that implements the given -trait constraint for this type. If the trait constraint is not -found, `None` is returned. Note that since the concrete trait implementation -for a trait constraint specified from a `where` clause is unknown, -this function will return `None` in these cases. If you only want to know -whether a type implements a trait, use `implements` instead. - -Example: - -```rust -comptime { - let field_type = quote { Field }.as_type(); - let default = quote { Default }.as_trait_constraint(); - - let the_impl: TraitImpl = field_type.get_trait_impl(default).unwrap(); - assert(the_impl.methods().len(), 1); -} -``` - -### implements - -```rust title="implements" showLineNumbers -pub comptime fn implements(self, constraint: TraitConstraint) -> bool {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L166-L168 - - -`true` if this type implements the given trait. Note that unlike -`get_trait_impl` this will also return true for any `where` constraints -in scope. - -Example: - -```rust -fn foo() where T: Default { - comptime { - let field_type = quote { Field }.as_type(); - let default = quote { Default }.as_trait_constraint(); - assert(field_type.implements(default)); - - let t = quote { T }.as_type(); - assert(t.implements(default)); - } -} -``` - -### is_bool - -```rust title="is_bool" showLineNumbers -pub comptime fn is_bool(self) -> bool {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L172-L174 - - -`true` if this type is `bool`. - -### is_field - -```rust title="is_field" showLineNumbers -pub comptime fn is_field(self) -> bool {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L178-L180 - - -`true` if this type is `Field`. - -### is_unit - -```rust title="is_unit" showLineNumbers -comptime fn is_unit(self) -> bool {} -``` -> Source code: noir_stdlib/src/meta/typ.nr#L184-L186 - - -`true` if this type is the unit `()` type. - -## Trait Implementations - -```rust -impl Eq for Type -impl Hash for Type -``` -Note that this is syntactic equality, this is not the same as whether two types will type check -to be the same type. Unless type inference or generics are being used however, users should not -typically have to worry about this distinction unless `std::meta::typ::fresh_type_variable` is used. diff --git a/docs/versioned_docs/version-v0.39.0/noir/standard_library/meta/typed_expr.md b/docs/versioned_docs/version-v0.39.0/noir/standard_library/meta/typed_expr.md deleted file mode 100644 index 0db7dbfef61..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/standard_library/meta/typed_expr.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -title: TypedExpr ---- - -`std::meta::typed_expr` contains methods on the built-in `TypedExpr` type for resolved and type-checked expressions. - -## Methods - -### get_type - -```rust title="as_function_definition" showLineNumbers -pub comptime fn as_function_definition(self) -> Option {} -``` -> Source code: noir_stdlib/src/meta/typed_expr.nr#L7-L9 - - -If this expression refers to a function definitions, returns it. Otherwise returns `Option::none()`. - -### get_type - -```rust title="get_type" showLineNumbers -pub comptime fn get_type(self) -> Option {} -``` -> Source code: noir_stdlib/src/meta/typed_expr.nr#L13-L15 - - -Returns the type of the expression, or `Option::none()` if there were errors when the expression was previously resolved. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.39.0/noir/standard_library/meta/unresolved_type.md b/docs/versioned_docs/version-v0.39.0/noir/standard_library/meta/unresolved_type.md deleted file mode 100644 index 2826ec5ec0f..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/standard_library/meta/unresolved_type.md +++ /dev/null @@ -1,57 +0,0 @@ ---- -title: UnresolvedType ---- - -`std::meta::unresolved_type` contains methods on the built-in `UnresolvedType` type for the syntax of types. - -## Methods - -### as_mutable_reference - -```rust title="as_mutable_reference" showLineNumbers -comptime fn as_mutable_reference(self) -> Option {} -``` -> Source code: noir_stdlib/src/meta/unresolved_type.nr#L8-L10 - - -If this is a mutable reference type `&mut T`, returns the mutable type `T`. - -### as_slice - -```rust title="as_slice" showLineNumbers -comptime fn as_slice(self) -> Option {} -``` -> Source code: noir_stdlib/src/meta/unresolved_type.nr#L14-L16 - - -If this is a slice `&[T]`, returns the element type `T`. - -### is_bool - -```rust title="is_bool" showLineNumbers -comptime fn is_bool(self) -> bool {} -``` -> Source code: noir_stdlib/src/meta/unresolved_type.nr#L20-L22 - - -Returns `true` if this type is `bool`. - -### is_field - -```rust title="is_field" showLineNumbers -pub comptime fn is_field(self) -> bool {} -``` -> Source code: noir_stdlib/src/meta/unresolved_type.nr#L26-L28 - - -Returns true if this type refers to the Field type. - -### is_unit - -```rust title="is_unit" showLineNumbers -comptime fn is_unit(self) -> bool {} -``` -> Source code: noir_stdlib/src/meta/unresolved_type.nr#L32-L34 - - -Returns true if this type is the unit `()` type. diff --git a/docs/versioned_docs/version-v0.39.0/noir/standard_library/options.md b/docs/versioned_docs/version-v0.39.0/noir/standard_library/options.md deleted file mode 100644 index a1bd4e1de5f..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/standard_library/options.md +++ /dev/null @@ -1,101 +0,0 @@ ---- -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), -} -``` - -The `Option` type, already imported into your Noir program, can be used directly: - -```rust -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. - -### expect - -Asserts `self.is_some()` with a provided custom message and returns the contained `Some` value. The custom message is expected to be a format string. - -### 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.39.0/noir/standard_library/recursion.mdx b/docs/versioned_docs/version-v0.39.0/noir/standard_library/recursion.mdx deleted file mode 100644 index fcb36278060..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/standard_library/recursion.mdx +++ /dev/null @@ -1,67 +0,0 @@ ---- -title: Recursive Proofs -description: Learn about how to write recursive proofs in Noir. -keywords: [recursion, recursive proofs, verification_key, verify_proof] ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; - -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. - -Read [the explainer on recursion](../../explainers/explainer-recursion.md) to know more about this function and the [guide on how to use it.](../../how_to/how-to-recursion.md) - -## Verifying Recursive Proofs - -```rust -#[foreign(recursive_aggregation)] -pub fn verify_proof(verification_key: [Field], proof: [Field], public_inputs: [Field], key_hash: Field) {} -``` - - - -## Example usage - -```rust - -fn main( - verification_key : [Field; 114], - proof : [Field; 93], - public_inputs : [Field; 1], - key_hash : Field, - proof_b : [Field; 93], -) { - std::verify_proof( - verification_key, - proof, - public_inputs, - key_hash - ); - - std::verify_proof( - verification_key, - proof_b, - public_inputs, - key_hash - ); -} -``` - -You can see a full example of recursive proofs in [this example recursion demo repo](https://github.com/noir-lang/noir-examples/tree/master/recursion). - -## 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. - -### `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. diff --git a/docs/versioned_docs/version-v0.39.0/noir/standard_library/traits.md b/docs/versioned_docs/version-v0.39.0/noir/standard_library/traits.md deleted file mode 100644 index ee20f9cd949..00000000000 --- a/docs/versioned_docs/version-v0.39.0/noir/standard_library/traits.md +++ /dev/null @@ -1,628 +0,0 @@ ---- -title: Traits -description: Noir's stdlib provides a few commonly used traits. -keywords: [traits, trait, interface, protocol, default, add, eq] ---- - -## `std::default` - -### `std::default::Default` - -```rust title="default-trait" showLineNumbers -pub trait Default { - fn default() -> Self; -} -``` -> Source code: noir_stdlib/src/default.nr#L4-L8 - - -Constructs a default value of a type. - -Implementations: -```rust -impl Default for Field { .. } - -impl Default for i8 { .. } -impl Default for i16 { .. } -impl Default for i32 { .. } -impl Default for i64 { .. } - -impl Default for u8 { .. } -impl Default for u16 { .. } -impl Default for u32 { .. } -impl Default for u64 { .. } - -impl Default for () { .. } -impl Default for bool { .. } - -impl Default for [T; N] - where T: Default { .. } - -impl Default for [T] { .. } - -impl Default for (A, B) - where A: Default, B: Default { .. } - -impl Default for (A, B, C) - where A: Default, B: Default, C: Default { .. } - -impl Default for (A, B, C, D) - where A: Default, B: Default, C: Default, D: Default { .. } - -impl Default for (A, B, C, D, E) - where A: Default, B: Default, C: Default, D: Default, E: Default { .. } -``` - -For primitive integer types, the return value of `default` is `0`. Container -types such as arrays are filled with default values of their element type, -except slices whose length is unknown and thus defaulted to zero. - ---- - -## `std::convert` - -### `std::convert::From` - -```rust title="from-trait" showLineNumbers -pub trait From { - fn from(input: T) -> Self; -} -``` -> Source code: noir_stdlib/src/convert.nr#L1-L5 - - -The `From` trait defines how to convert from a given type `T` to the type on which the trait is implemented. - -The Noir standard library provides a number of implementations of `From` between primitive types. -```rust title="from-impls" showLineNumbers -// Unsigned integers - -impl From for u32 { - fn from(value: u8) -> u32 { - value as u32 - } -} - -impl From for u64 { - fn from(value: u8) -> u64 { - value as u64 - } -} -impl From for u64 { - fn from(value: u32) -> u64 { - value as u64 - } -} - -impl From for Field { - fn from(value: u8) -> Field { - value as Field - } -} -impl From for Field { - fn from(value: u32) -> Field { - value as Field - } -} -impl From for Field { - fn from(value: u64) -> Field { - value as Field - } -} - -// Signed integers - -impl From for i32 { - fn from(value: i8) -> i32 { - value as i32 - } -} - -impl From for i64 { - fn from(value: i8) -> i64 { - value as i64 - } -} -impl From for i64 { - fn from(value: i32) -> i64 { - value as i64 - } -} - -// Booleans -impl From for u8 { - fn from(value: bool) -> u8 { - value as u8 - } -} -impl From for u32 { - fn from(value: bool) -> u32 { - value as u32 - } -} -impl From for u64 { - fn from(value: bool) -> u64 { - value as u64 - } -} -impl From for i8 { - fn from(value: bool) -> i8 { - value as i8 - } -} -impl From for i32 { - fn from(value: bool) -> i32 { - value as i32 - } -} -impl From for i64 { - fn from(value: bool) -> i64 { - value as i64 - } -} -impl From for Field { - fn from(value: bool) -> Field { - value as Field - } -} -``` -> Source code: noir_stdlib/src/convert.nr#L28-L119 - - -#### When to implement `From` - -As a general rule of thumb, `From` may be implemented in the [situations where it would be suitable in Rust](https://doc.rust-lang.org/std/convert/trait.From.html#when-to-implement-from): - -- The conversion is *infallible*: Noir does not provide an equivalent to Rust's `TryFrom`, if the conversion can fail then provide a named method instead. -- The conversion is *lossless*: semantically, it should not lose or discard information. For example, `u32: From` can losslessly convert any `u16` into a valid `u32` such that the original `u16` can be recovered. On the other hand, `u16: From` should not be implemented as `2**16` is a `u32` which cannot be losslessly converted into a `u16`. -- The conversion is *value-preserving*: the conceptual kind and meaning of the resulting value is the same, even though the Noir type and technical representation might be different. While it's possible to infallibly and losslessly convert a `u8` into a `str<2>` hex representation, `4u8` and `"04"` are too different for `str<2>: From` to be implemented. -- The conversion is *obvious*: it's the only reasonable conversion between the two types. If there's ambiguity on how to convert between them such that the same input could potentially map to two different values then a named method should be used. For instance rather than implementing `U128: From<[u8; 16]>`, the methods `U128::from_le_bytes` and `U128::from_be_bytes` are used as otherwise the endianness of the array would be ambiguous, resulting in two potential values of `U128` from the same byte array. - -One additional recommendation specific to Noir is: -- The conversion is *efficient*: it's relatively cheap to convert between the two types. Due to being a ZK DSL, it's more important to avoid unnecessary computation compared to Rust. If the implementation of `From` would encourage users to perform unnecessary conversion, resulting in additional proving time, then it may be preferable to expose functionality such that this conversion may be avoided. - -### `std::convert::Into` - -The `Into` trait is defined as the reciprocal of `From`. It should be easy to convince yourself that if we can convert to type `A` from type `B`, then it's possible to convert type `B` into type `A`. - -For this reason, implementing `From` on a type will automatically generate a matching `Into` implementation. One should always prefer implementing `From` over `Into` as implementing `Into` will not generate a matching `From` implementation. - -```rust title="into-trait" showLineNumbers -pub trait Into { - fn into(self) -> T; -} - -impl Into for U -where - T: From, -{ - fn into(self) -> T { - T::from(self) - } -} -``` -> Source code: noir_stdlib/src/convert.nr#L13-L26 - - -`Into` is most useful when passing function arguments where the types don't quite match up with what the function expects. In this case, the compiler has enough type information to perform the necessary conversion by just appending `.into()` onto the arguments in question. - ---- - -## `std::cmp` - -### `std::cmp::Eq` - -```rust title="eq-trait" showLineNumbers -pub trait Eq { - fn eq(self, other: Self) -> bool; -} -``` -> Source code: noir_stdlib/src/cmp.nr#L4-L8 - - -Returns `true` if `self` is equal to `other`. Implementing this trait on a type -allows the type to be used with `==` and `!=`. - -Implementations: -```rust -impl Eq for Field { .. } - -impl Eq for i8 { .. } -impl Eq for i16 { .. } -impl Eq for i32 { .. } -impl Eq for i64 { .. } - -impl Eq for u8 { .. } -impl Eq for u16 { .. } -impl Eq for u32 { .. } -impl Eq for u64 { .. } - -impl Eq for () { .. } -impl Eq for bool { .. } - -impl Eq for [T; N] - where T: Eq { .. } - -impl Eq for [T] - where T: Eq { .. } - -impl Eq for (A, B) - where A: Eq, B: Eq { .. } - -impl Eq for (A, B, C) - where A: Eq, B: Eq, C: Eq { .. } - -impl Eq for (A, B, C, D) - where A: Eq, B: Eq, C: Eq, D: Eq { .. } - -impl Eq for (A, B, C, D, E) - where A: Eq, B: Eq, C: Eq, D: Eq, E: Eq { .. } -``` - -### `std::cmp::Ord` - -```rust title="ord-trait" showLineNumbers -pub trait Ord { - fn cmp(self, other: Self) -> Ordering; -} -``` -> Source code: noir_stdlib/src/cmp.nr#L210-L214 - - -`a.cmp(b)` compares two values returning `Ordering::less()` if `a < b`, -`Ordering::equal()` if `a == b`, or `Ordering::greater()` if `a > b`. -Implementing this trait on a type allows `<`, `<=`, `>`, and `>=` to be -used on values of the type. - -`std::cmp` also provides `max` and `min` functions for any type which implements the `Ord` trait. - -Implementations: - -```rust -impl Ord for u8 { .. } -impl Ord for u16 { .. } -impl Ord for u32 { .. } -impl Ord for u64 { .. } - -impl Ord for i8 { .. } -impl Ord for i16 { .. } -impl Ord for i32 { .. } - -impl Ord for i64 { .. } - -impl Ord for () { .. } -impl Ord for bool { .. } - -impl Ord for [T; N] - where T: Ord { .. } - -impl Ord for [T] - where T: Ord { .. } - -impl Ord for (A, B) - where A: Ord, B: Ord { .. } - -impl Ord for (A, B, C) - where A: Ord, B: Ord, C: Ord { .. } - -impl Ord for (A, B, C, D) - where A: Ord, B: Ord, C: Ord, D: Ord { .. } - -impl Ord for (A, B, C, D, E) - where A: Ord, B: Ord, C: Ord, D: Ord, E: Ord { .. } -``` - ---- - -## `std::ops` - -### `std::ops::Add`, `std::ops::Sub`, `std::ops::Mul`, and `std::ops::Div` - -These traits abstract over addition, subtraction, multiplication, and division respectively. -Implementing these traits for a given type will also allow that type to be used with the corresponding operator -for that trait (`+` for Add, etc) in addition to the normal method names. - -```rust title="add-trait" showLineNumbers -pub trait Add { - fn add(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/arith.nr#L1-L5 - -```rust title="sub-trait" showLineNumbers -pub trait Sub { - fn sub(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/arith.nr#L60-L64 - -```rust title="mul-trait" showLineNumbers -pub trait Mul { - fn mul(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/arith.nr#L119-L123 - -```rust title="div-trait" showLineNumbers -pub trait Div { - fn div(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/arith.nr#L178-L182 - - -The implementations block below is given for the `Add` trait, but the same types that implement -`Add` also implement `Sub`, `Mul`, and `Div`. - -Implementations: -```rust -impl Add for Field { .. } - -impl Add for i8 { .. } -impl Add for i16 { .. } -impl Add for i32 { .. } -impl Add for i64 { .. } - -impl Add for u8 { .. } -impl Add for u16 { .. } -impl Add for u32 { .. } -impl Add for u64 { .. } -``` - -### `std::ops::Rem` - -```rust title="rem-trait" showLineNumbers -pub trait Rem { - fn rem(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/arith.nr#L237-L241 - - -`Rem::rem(a, b)` is the remainder function returning the result of what is -left after dividing `a` and `b`. Implementing `Rem` allows the `%` operator -to be used with the implementation type. - -Unlike other numeric traits, `Rem` is not implemented for `Field`. - -Implementations: -```rust -impl Rem for u8 { fn rem(self, other: u8) -> u8 { self % other } } -impl Rem for u16 { fn rem(self, other: u16) -> u16 { self % other } } -impl Rem for u32 { fn rem(self, other: u32) -> u32 { self % other } } -impl Rem for u64 { fn rem(self, other: u64) -> u64 { self % other } } - -impl Rem for i8 { fn rem(self, other: i8) -> i8 { self % other } } -impl Rem for i16 { fn rem(self, other: i16) -> i16 { self % other } } -impl Rem for i32 { fn rem(self, other: i32) -> i32 { self % other } } -impl Rem for i64 { fn rem(self, other: i64) -> i64 { self % other } } -``` - -### `std::ops::Neg` - -```rust title="neg-trait" showLineNumbers -pub trait Neg { - fn neg(self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/arith.nr#L290-L294 - - -`Neg::neg` is equivalent to the unary negation operator `-`. - -Implementations: -```rust title="neg-trait-impls" showLineNumbers -impl Neg for Field { - fn neg(self) -> Field { - -self - } -} - -impl Neg for i8 { - fn neg(self) -> i8 { - -self - } -} -impl Neg for i16 { - fn neg(self) -> i16 { - -self - } -} -impl Neg for i32 { - fn neg(self) -> i32 { - -self - } -} -impl Neg for i64 { - fn neg(self) -> i64 { - -self - } -} -``` -> Source code: noir_stdlib/src/ops/arith.nr#L296-L323 - - -### `std::ops::Not` - -```rust title="not-trait" showLineNumbers -pub trait Not { - fn not(self: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/bit.nr#L1-L5 - - -`Not::not` is equivalent to the unary bitwise NOT operator `!`. - -Implementations: -```rust title="not-trait-impls" showLineNumbers -impl Not for bool { - fn not(self) -> bool { - !self - } -} - -impl Not for u64 { - fn not(self) -> u64 { - !self - } -} -impl Not for u32 { - fn not(self) -> u32 { - !self - } -} -impl Not for u16 { - fn not(self) -> u16 { - !self - } -} -impl Not for u8 { - fn not(self) -> u8 { - !self - } -} -impl Not for u1 { - fn not(self) -> u1 { - !self - } -} - -impl Not for i8 { - fn not(self) -> i8 { - !self - } -} -impl Not for i16 { - fn not(self) -> i16 { - !self - } -} -impl Not for i32 { - fn not(self) -> i32 { - !self - } -} -impl Not for i64 { - fn not(self) -> i64 { - !self - } -} -``` -> Source code: noir_stdlib/src/ops/bit.nr#L7-L60 - - -### `std::ops::{ BitOr, BitAnd, BitXor }` - -```rust title="bitor-trait" showLineNumbers -pub trait BitOr { - fn bitor(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/bit.nr#L62-L66 - -```rust title="bitand-trait" showLineNumbers -pub trait BitAnd { - fn bitand(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/bit.nr#L121-L125 - -```rust title="bitxor-trait" showLineNumbers -pub trait BitXor { - fn bitxor(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/bit.nr#L180-L184 - - -Traits for the bitwise operations `|`, `&`, and `^`. - -Implementing `BitOr`, `BitAnd` or `BitXor` for a type allows the `|`, `&`, or `^` operator respectively -to be used with the type. - -The implementations block below is given for the `BitOr` trait, but the same types that implement -`BitOr` also implement `BitAnd` and `BitXor`. - -Implementations: -```rust -impl BitOr for bool { fn bitor(self, other: bool) -> bool { self | other } } - -impl BitOr for u8 { fn bitor(self, other: u8) -> u8 { self | other } } -impl BitOr for u16 { fn bitor(self, other: u16) -> u16 { self | other } } -impl BitOr for u32 { fn bitor(self, other: u32) -> u32 { self | other } } -impl BitOr for u64 { fn bitor(self, other: u64) -> u64 { self | other } } - -impl BitOr for i8 { fn bitor(self, other: i8) -> i8 { self | other } } -impl BitOr for i16 { fn bitor(self, other: i16) -> i16 { self | other } } -impl BitOr for i32 { fn bitor(self, other: i32) -> i32 { self | other } } -impl BitOr for i64 { fn bitor(self, other: i64) -> i64 { self | other } } -``` - -### `std::ops::{ Shl, Shr }` - -```rust title="shl-trait" showLineNumbers -pub trait Shl { - fn shl(self, other: u8) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/bit.nr#L239-L243 - -```rust title="shr-trait" showLineNumbers -pub trait Shr { - fn shr(self, other: u8) -> Self; -} -``` -> Source code: noir_stdlib/src/ops/bit.nr#L292-L296 - - -Traits for a bit shift left and bit shift right. - -Implementing `Shl` for a type allows the left shift operator (`<<`) to be used with the implementation type. -Similarly, implementing `Shr` allows the right shift operator (`>>`) to be used with the type. - -Note that bit shifting is not currently implemented for signed types. - -The implementations block below is given for the `Shl` trait, but the same types that implement -`Shl` also implement `Shr`. - -Implementations: -```rust -impl Shl for u8 { fn shl(self, other: u8) -> u8 { self << other } } -impl Shl for u16 { fn shl(self, other: u16) -> u16 { self << other } } -impl Shl for u32 { fn shl(self, other: u32) -> u32 { self << other } } -impl Shl for u64 { fn shl(self, other: u64) -> u64 { self << other } } -``` - ---- - -## `std::append` - -### `std::append::Append` - -`Append` can abstract over types that can be appended to - usually container types: - -```rust title="append-trait" showLineNumbers -pub trait Append { - fn empty() -> Self; - fn append(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/append.nr#L9-L14 - - -`Append` requires two methods: - -- `empty`: Constructs an empty value of `Self`. -- `append`: Append two values together, returning the result. - -Additionally, it is expected that for any implementation: - -- `T::empty().append(x) == x` -- `x.append(T::empty()) == x` - -Implementations: -```rust -impl Append for [T] -impl Append for Quoted -``` diff --git a/docs/versioned_docs/version-v0.39.0/reference/NoirJS/noir_js/.nojekyll b/docs/versioned_docs/version-v0.39.0/reference/NoirJS/noir_js/.nojekyll deleted file mode 100644 index e2ac6616add..00000000000 --- a/docs/versioned_docs/version-v0.39.0/reference/NoirJS/noir_js/.nojekyll +++ /dev/null @@ -1 +0,0 @@ -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.39.0/reference/NoirJS/noir_js/classes/Noir.md b/docs/versioned_docs/version-v0.39.0/reference/NoirJS/noir_js/classes/Noir.md deleted file mode 100644 index ead255bc504..00000000000 --- a/docs/versioned_docs/version-v0.39.0/reference/NoirJS/noir_js/classes/Noir.md +++ /dev/null @@ -1,52 +0,0 @@ -# Noir - -## Constructors - -### new Noir(circuit) - -```ts -new Noir(circuit): Noir -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `circuit` | `CompiledCircuit` | - -#### Returns - -[`Noir`](Noir.md) - -## Methods - -### 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) -``` - -*** - -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.39.0/reference/NoirJS/noir_js/functions/and.md b/docs/versioned_docs/version-v0.39.0/reference/NoirJS/noir_js/functions/and.md deleted file mode 100644 index c783283e396..00000000000 --- a/docs/versioned_docs/version-v0.39.0/reference/NoirJS/noir_js/functions/and.md +++ /dev/null @@ -1,22 +0,0 @@ -# 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.39.0/reference/NoirJS/noir_js/functions/blake2s256.md b/docs/versioned_docs/version-v0.39.0/reference/NoirJS/noir_js/functions/blake2s256.md deleted file mode 100644 index 7882d0da8d5..00000000000 --- a/docs/versioned_docs/version-v0.39.0/reference/NoirJS/noir_js/functions/blake2s256.md +++ /dev/null @@ -1,21 +0,0 @@ -# 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.39.0/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md b/docs/versioned_docs/version-v0.39.0/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md deleted file mode 100644 index 5e3cd53e9d3..00000000000 --- a/docs/versioned_docs/version-v0.39.0/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md +++ /dev/null @@ -1,28 +0,0 @@ -# ecdsa\_secp256k1\_verify() - -```ts -ecdsa_secp256k1_verify( - hashed_msg, - public_key_x_bytes, - public_key_y_bytes, - signature): boolean -``` - -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.39.0/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md b/docs/versioned_docs/version-v0.39.0/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md deleted file mode 100644 index 0b20ff68957..00000000000 --- a/docs/versioned_docs/version-v0.39.0/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md +++ /dev/null @@ -1,28 +0,0 @@ -# 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.39.0/reference/NoirJS/noir_js/functions/xor.md b/docs/versioned_docs/version-v0.39.0/reference/NoirJS/noir_js/functions/xor.md deleted file mode 100644 index 8d762b895d3..00000000000 --- a/docs/versioned_docs/version-v0.39.0/reference/NoirJS/noir_js/functions/xor.md +++ /dev/null @@ -1,22 +0,0 @@ -# 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.39.0/reference/NoirJS/noir_js/index.md b/docs/versioned_docs/version-v0.39.0/reference/NoirJS/noir_js/index.md deleted file mode 100644 index 4de7a696991..00000000000 --- a/docs/versioned_docs/version-v0.39.0/reference/NoirJS/noir_js/index.md +++ /dev/null @@ -1,47 +0,0 @@ -# noir_js - -## Exports - -### Classes - -| Class | Description | -| :------ | :------ | -| [Noir](classes/Noir.md) | - | - -### Type Aliases - -| Type alias | Description | -| :------ | :------ | -| [ErrorWithPayload](type-aliases/ErrorWithPayload.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) | - | -| [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) | Verifies a ECDSA signature over the secp256k1 curve. | -| [ecdsa\_secp256r1\_verify](functions/ecdsa_secp256r1_verify.md) | Verifies a ECDSA signature over the secp256r1 curve. | -| [xor](functions/xor.md) | Performs a bitwise XOR operation between `lhs` and `rhs` | - -## References - -### CompiledCircuit - -Renames and re-exports [InputMap](index.md#inputmap) - -## Variables - -### InputMap - -```ts -InputMap: any; -``` - -*** - -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.39.0/reference/NoirJS/noir_js/type-aliases/ErrorWithPayload.md b/docs/versioned_docs/version-v0.39.0/reference/NoirJS/noir_js/type-aliases/ErrorWithPayload.md deleted file mode 100644 index e8c2f4aef3d..00000000000 --- a/docs/versioned_docs/version-v0.39.0/reference/NoirJS/noir_js/type-aliases/ErrorWithPayload.md +++ /dev/null @@ -1,15 +0,0 @@ -# ErrorWithPayload - -```ts -type ErrorWithPayload: ExecutionError & object; -``` - -## Type declaration - -| Member | Type | Description | -| :------ | :------ | :------ | -| `decodedAssertionPayload` | `any` | - | - -*** - -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.39.0/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md b/docs/versioned_docs/version-v0.39.0/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md deleted file mode 100644 index 812b8b16481..00000000000 --- a/docs/versioned_docs/version-v0.39.0/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md +++ /dev/null @@ -1,24 +0,0 @@ -# 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.39.0/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md b/docs/versioned_docs/version-v0.39.0/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md deleted file mode 100644 index dd95809186a..00000000000 --- a/docs/versioned_docs/version-v0.39.0/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md +++ /dev/null @@ -1,9 +0,0 @@ -# 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.39.0/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md b/docs/versioned_docs/version-v0.39.0/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md deleted file mode 100644 index b71fb78a946..00000000000 --- a/docs/versioned_docs/version-v0.39.0/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md +++ /dev/null @@ -1,9 +0,0 @@ -# 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.39.0/reference/NoirJS/noir_js/type-aliases/WitnessMap.md b/docs/versioned_docs/version-v0.39.0/reference/NoirJS/noir_js/type-aliases/WitnessMap.md deleted file mode 100644 index 258c46f9d0c..00000000000 --- a/docs/versioned_docs/version-v0.39.0/reference/NoirJS/noir_js/type-aliases/WitnessMap.md +++ /dev/null @@ -1,9 +0,0 @@ -# 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.39.0/reference/NoirJS/noir_js/typedoc-sidebar.cjs b/docs/versioned_docs/version-v0.39.0/reference/NoirJS/noir_js/typedoc-sidebar.cjs deleted file mode 100644 index 4796b5abaa8..00000000000 --- a/docs/versioned_docs/version-v0.39.0/reference/NoirJS/noir_js/typedoc-sidebar.cjs +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-check -/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ -const typedocSidebar = { items: [{"type":"category","label":"Classes","items":[{"type":"doc","id":"reference/NoirJS/noir_js/classes/Noir","label":"Noir"}]},{"type":"category","label":"Type Aliases","items":[{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ErrorWithPayload","label":"ErrorWithPayload"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ForeignCallHandler","label":"ForeignCallHandler"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ForeignCallInput","label":"ForeignCallInput"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ForeignCallOutput","label":"ForeignCallOutput"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/WitnessMap","label":"WitnessMap"}]},{"type":"category","label":"Functions","items":[{"type":"doc","id":"reference/NoirJS/noir_js/functions/and","label":"and"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/blake2s256","label":"blake2s256"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify","label":"ecdsa_secp256k1_verify"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify","label":"ecdsa_secp256r1_verify"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/xor","label":"xor"}]}]}; -module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.39.0/reference/NoirJS/noir_wasm/.nojekyll b/docs/versioned_docs/version-v0.39.0/reference/NoirJS/noir_wasm/.nojekyll deleted file mode 100644 index e2ac6616add..00000000000 --- a/docs/versioned_docs/version-v0.39.0/reference/NoirJS/noir_wasm/.nojekyll +++ /dev/null @@ -1 +0,0 @@ -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.39.0/reference/NoirJS/noir_wasm/functions/compile.md b/docs/versioned_docs/version-v0.39.0/reference/NoirJS/noir_wasm/functions/compile.md deleted file mode 100644 index 6faf763b37f..00000000000 --- a/docs/versioned_docs/version-v0.39.0/reference/NoirJS/noir_wasm/functions/compile.md +++ /dev/null @@ -1,51 +0,0 @@ -# compile() - -```ts -compile( - fileManager, - projectPath?, - logFn?, -debugLogFn?): Promise -``` - -Compiles a Noir project - -## Parameters - -| Parameter | Type | Description | -| :------ | :------ | :------ | -| `fileManager` | `FileManager` | The file manager to use | -| `projectPath`? | `string` | The path to the project inside the file manager. Defaults to the root of the file manager | -| `logFn`? | `LogFn` | A logging function. If not provided, console.log will be used | -| `debugLogFn`? | `LogFn` | A debug logging function. If not provided, logFn will be used | - -## Returns - -`Promise`\<[`ProgramCompilationArtifacts`](../index.md#programcompilationartifacts)\> - -## Example - -```typescript -// Node.js - -import { compile_program, createFileManager } from '@noir-lang/noir_wasm'; - -const fm = createFileManager(myProjectPath); -const myCompiledCode = await compile_program(fm); -``` - -```typescript -// Browser - -import { compile_program, createFileManager } from '@noir-lang/noir_wasm'; - -const fm = createFileManager('/'); -for (const path of files) { - await fm.writeFile(path, await getFileAsStream(path)); -} -const myCompiledCode = await compile_program(fm); -``` - -*** - -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.39.0/reference/NoirJS/noir_wasm/functions/compile_contract.md b/docs/versioned_docs/version-v0.39.0/reference/NoirJS/noir_wasm/functions/compile_contract.md deleted file mode 100644 index 7d0b39a43ef..00000000000 --- a/docs/versioned_docs/version-v0.39.0/reference/NoirJS/noir_wasm/functions/compile_contract.md +++ /dev/null @@ -1,51 +0,0 @@ -# compile\_contract() - -```ts -compile_contract( - fileManager, - projectPath?, - logFn?, -debugLogFn?): Promise -``` - -Compiles a Noir project - -## Parameters - -| Parameter | Type | Description | -| :------ | :------ | :------ | -| `fileManager` | `FileManager` | The file manager to use | -| `projectPath`? | `string` | The path to the project inside the file manager. Defaults to the root of the file manager | -| `logFn`? | `LogFn` | A logging function. If not provided, console.log will be used | -| `debugLogFn`? | `LogFn` | A debug logging function. If not provided, logFn will be used | - -## Returns - -`Promise`\<[`ContractCompilationArtifacts`](../index.md#contractcompilationartifacts)\> - -## Example - -```typescript -// Node.js - -import { compile_contract, createFileManager } from '@noir-lang/noir_wasm'; - -const fm = createFileManager(myProjectPath); -const myCompiledCode = await compile_contract(fm); -``` - -```typescript -// Browser - -import { compile_contract, createFileManager } from '@noir-lang/noir_wasm'; - -const fm = createFileManager('/'); -for (const path of files) { - await fm.writeFile(path, await getFileAsStream(path)); -} -const myCompiledCode = await compile_contract(fm); -``` - -*** - -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.39.0/reference/NoirJS/noir_wasm/functions/createFileManager.md b/docs/versioned_docs/version-v0.39.0/reference/NoirJS/noir_wasm/functions/createFileManager.md deleted file mode 100644 index 7e65c1d69c7..00000000000 --- a/docs/versioned_docs/version-v0.39.0/reference/NoirJS/noir_wasm/functions/createFileManager.md +++ /dev/null @@ -1,21 +0,0 @@ -# createFileManager() - -```ts -createFileManager(dataDir): FileManager -``` - -Creates a new FileManager instance based on fs in node and memfs in the browser (via webpack alias) - -## Parameters - -| Parameter | Type | Description | -| :------ | :------ | :------ | -| `dataDir` | `string` | root of the file system | - -## Returns - -`FileManager` - -*** - -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.39.0/reference/NoirJS/noir_wasm/functions/inflateDebugSymbols.md b/docs/versioned_docs/version-v0.39.0/reference/NoirJS/noir_wasm/functions/inflateDebugSymbols.md deleted file mode 100644 index fcea9275341..00000000000 --- a/docs/versioned_docs/version-v0.39.0/reference/NoirJS/noir_wasm/functions/inflateDebugSymbols.md +++ /dev/null @@ -1,21 +0,0 @@ -# inflateDebugSymbols() - -```ts -inflateDebugSymbols(debugSymbols): any -``` - -Decompresses and decodes the debug symbols - -## Parameters - -| Parameter | Type | Description | -| :------ | :------ | :------ | -| `debugSymbols` | `string` | The base64 encoded debug symbols | - -## Returns - -`any` - -*** - -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.39.0/reference/NoirJS/noir_wasm/index.md b/docs/versioned_docs/version-v0.39.0/reference/NoirJS/noir_wasm/index.md deleted file mode 100644 index b6e0f9d1bc0..00000000000 --- a/docs/versioned_docs/version-v0.39.0/reference/NoirJS/noir_wasm/index.md +++ /dev/null @@ -1,49 +0,0 @@ -# noir_wasm - -## Exports - -### Functions - -| Function | Description | -| :------ | :------ | -| [compile](functions/compile.md) | Compiles a Noir project | -| [compile\_contract](functions/compile_contract.md) | Compiles a Noir project | -| [createFileManager](functions/createFileManager.md) | Creates a new FileManager instance based on fs in node and memfs in the browser (via webpack alias) | -| [inflateDebugSymbols](functions/inflateDebugSymbols.md) | Decompresses and decodes the debug symbols | - -## References - -### compile\_program - -Renames and re-exports [compile](functions/compile.md) - -## Interfaces - -### ContractCompilationArtifacts - -The compilation artifacts of a given contract. - -#### Properties - -| Property | Type | Description | -| :------ | :------ | :------ | -| `contract` | `ContractArtifact` | The compiled contract. | -| `warnings` | `unknown`[] | Compilation warnings. | - -*** - -### ProgramCompilationArtifacts - -The compilation artifacts of a given program. - -#### Properties - -| Property | Type | Description | -| :------ | :------ | :------ | -| `name` | `string` | not part of the compilation output, injected later | -| `program` | `ProgramArtifact` | The compiled contract. | -| `warnings` | `unknown`[] | Compilation warnings. | - -*** - -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.39.0/reference/NoirJS/noir_wasm/typedoc-sidebar.cjs b/docs/versioned_docs/version-v0.39.0/reference/NoirJS/noir_wasm/typedoc-sidebar.cjs deleted file mode 100644 index e0870710349..00000000000 --- a/docs/versioned_docs/version-v0.39.0/reference/NoirJS/noir_wasm/typedoc-sidebar.cjs +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-check -/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ -const typedocSidebar = { items: [{"type":"doc","id":"reference/NoirJS/noir_wasm/index","label":"API"},{"type":"category","label":"Functions","items":[{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/compile","label":"compile"},{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/compile_contract","label":"compile_contract"},{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/createFileManager","label":"createFileManager"},{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/inflateDebugSymbols","label":"inflateDebugSymbols"}]}]}; -module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.39.0/reference/_category_.json b/docs/versioned_docs/version-v0.39.0/reference/_category_.json deleted file mode 100644 index 5b6a20a609a..00000000000 --- a/docs/versioned_docs/version-v0.39.0/reference/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "position": 4, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.39.0/reference/debugger/_category_.json b/docs/versioned_docs/version-v0.39.0/reference/debugger/_category_.json deleted file mode 100644 index 27869205ad3..00000000000 --- a/docs/versioned_docs/version-v0.39.0/reference/debugger/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "Debugger", - "position": 1, - "collapsible": true, - "collapsed": true -} diff --git a/docs/versioned_docs/version-v0.39.0/reference/debugger/debugger_known_limitations.md b/docs/versioned_docs/version-v0.39.0/reference/debugger/debugger_known_limitations.md deleted file mode 100644 index 936d416ac4b..00000000000 --- a/docs/versioned_docs/version-v0.39.0/reference/debugger/debugger_known_limitations.md +++ /dev/null @@ -1,59 +0,0 @@ ---- -title: Known limitations -description: - An overview of known limitations of the current version of the Noir debugger -keywords: - [ - Nargo, - Noir Debugger, - VS Code, - ] -sidebar_position: 2 ---- - -# Debugger Known Limitations - -There are currently some limits to what the debugger can observe. - -## Mutable references - -The debugger is currently blind to any state mutated via a mutable reference. For example, in: - -``` -let mut x = 1; -let y = &mut x; -*y = 2; -``` - -The update on `x` will not be observed by the debugger. That means, when running `vars` from the debugger REPL, or inspecting the _local variables_ pane in the VS Code debugger, `x` will appear with value 1 despite having executed `*y = 2;`. - -## Variables of type function or mutable references are opaque - -When inspecting variables, any variable of type `Function` or `MutableReference` will render its value as `<>` or `<>`. - -## Debugger instrumentation affects resulting ACIR - -In order to make the state of local variables observable, the debugger compiles Noir circuits interleaving foreign calls that track any mutations to them. While this works (except in the cases described above) and doesn't introduce any behavior changes, it does as a side effect produce bigger bytecode. In particular, when running the command `opcodes` on the REPL debugger, you will notice Unconstrained VM blocks that look like this: - -``` -... -5 BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [], q_c: 2 }), Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(2))], q_c: 0 })] - | outputs=[] - 5.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } - 5.1 | Mov { destination: RegisterIndex(3), source: RegisterIndex(1) } - 5.2 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } - 5.3 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } - 5.4 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } - 5.5 | Mov { destination: RegisterIndex(3), source: RegisterIndex(3) } - 5.6 | Call { location: 8 } - 5.7 | Stop - 5.8 | ForeignCall { function: "__debug_var_assign", destinations: [], inputs: [RegisterIndex(RegisterIndex(2)), RegisterIndex(RegisterIndex(3))] } -... -``` - -If you are interested in debugging/inspecting compiled ACIR without these synthetic changes, you can invoke the REPL debugger with the `--skip-instrumentation` flag or launch the VS Code debugger with the `skipConfiguration` property set to true in its launch configuration. You can find more details about those in the [Debugger REPL reference](debugger_repl.md) and the [VS Code Debugger reference](debugger_vscode.md). - -:::note -Skipping debugger instrumentation means you won't be able to inspect values of local variables. -::: - diff --git a/docs/versioned_docs/version-v0.39.0/reference/debugger/debugger_repl.md b/docs/versioned_docs/version-v0.39.0/reference/debugger/debugger_repl.md deleted file mode 100644 index 46e2011304e..00000000000 --- a/docs/versioned_docs/version-v0.39.0/reference/debugger/debugger_repl.md +++ /dev/null @@ -1,360 +0,0 @@ ---- -title: REPL Debugger -description: - Noir Debugger REPL options and commands. -keywords: - [ - Nargo, - Noir CLI, - Noir Debugger, - REPL, - ] -sidebar_position: 1 ---- - -## Running the REPL debugger - -`nargo debug [OPTIONS] [WITNESS_NAME]` - -Runs the Noir REPL debugger. If a `WITNESS_NAME` is provided the debugger writes the resulting execution witness to a `WITNESS_NAME` 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 debug | -| `--print-acir` | Display the ACIR for compiled circuit | -| `--deny-warnings` | Treat all warnings as errors | -| `--silence-warnings` | Suppress warnings | -| `-h, --help` | Print help | - -None of these options are required. - -:::note -Since the debugger starts by compiling the target package, all Noir compiler options are also available. Check out the [compiler reference](../nargo_commands.md#nargo-compile) to learn more about the compiler options. -::: - -## REPL commands - -Once the debugger is running, it accepts the following commands. - -#### `help` (h) - -Displays the menu of available commands. - -``` -> help -Available commands: - - opcodes display ACIR opcodes - into step into to the next opcode - next step until a new source location is reached - out step until a new source location is reached - and the current stack frame is finished - break LOCATION:OpcodeLocation add a breakpoint at an opcode location - over step until a new source location is reached - without diving into function calls - restart restart the debugging session - delete LOCATION:OpcodeLocation delete breakpoint at an opcode location - witness show witness map - witness index:u32 display a single witness from the witness map - witness index:u32 value:String update a witness with the given value - memset index:usize value:String update a memory cell with the given - value - continue continue execution until the end of the - program - vars show variable values available at this point - in execution - stacktrace display the current stack trace - memory show memory (valid when executing unconstrained code) value - step step to the next ACIR opcode - -Other commands: - - help Show this help message - quit Quit repl - -``` - -### Stepping through programs - -#### `next` (n) - -Step until the next Noir source code location. While other commands, such as [`into`](#into-i) and [`step`](#step-s), allow for finer grained control of the program's execution at the opcode level, `next` is source code centric. For example: - -``` -3 ... -4 fn main(x: u32) { -5 assert(entry_point(x) == 2); -6 swap_entry_point(x, x + 1); -7 -> assert(deep_entry_point(x) == 4); -8 multiple_values_entry_point(x); -9 } -``` - - -Using `next` here would cause the debugger to jump to the definition of `deep_entry_point` (if available). - -If you want to step over `deep_entry_point` and go straight to line 8, use [the `over` command](#over) instead. - -#### `over` - -Step until the next source code location, without diving into function calls. For example: - -``` -3 ... -4 fn main(x: u32) { -5 assert(entry_point(x) == 2); -6 swap_entry_point(x, x + 1); -7 -> assert(deep_entry_point(x) == 4); -8 multiple_values_entry_point(x); -9 } -``` - - -Using `over` here would cause the debugger to execute until line 8 (`multiple_values_entry_point(x);`). - -If you want to step into `deep_entry_point` instead, use [the `next` command](#next-n). - -#### `out` - -Step until the end of the current function call. For example: - -``` - 3 ... - 4 fn main(x: u32) { - 5 assert(entry_point(x) == 2); - 6 swap_entry_point(x, x + 1); - 7 -> assert(deep_entry_point(x) == 4); - 8 multiple_values_entry_point(x); - 9 } - 10 - 11 unconstrained fn returns_multiple_values(x: u32) -> (u32, u32, u32, u32) { - 12 ... - ... - 55 - 56 unconstrained fn deep_entry_point(x: u32) -> u32 { - 57 -> level_1(x + 1) - 58 } - -``` - -Running `out` here will resume execution until line 8. - -#### `step` (s) - -Skips to the next ACIR code. A compiled Noir program is a sequence of ACIR opcodes. However, an unconstrained VM opcode denotes the start of an unconstrained code block, to be executed by the unconstrained VM. For example (redacted for brevity): - -``` -0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] -1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] - 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } - 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } - 1.2 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } - 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } - 1.4 | Call { location: 7 } - ... - 1.43 | Return -2 EXPR [ (1, _1) -2 ] -``` - -The `->` here shows the debugger paused at an ACIR opcode: `BRILLIG`, at index 1, which denotes an unconstrained code block is about to start. - -Using the `step` command at this point would result in the debugger stopping at ACIR opcode 2, `EXPR`, skipping unconstrained computation steps. - -Use [the `into` command](#into-i) instead if you want to follow unconstrained computation step by step. - -#### `into` (i) - -Steps into the next opcode. A compiled Noir program is a sequence of ACIR opcodes. However, a BRILLIG opcode denotes the start of an unconstrained code block, to be executed by the unconstrained VM. For example (redacted for brevity): - -``` -0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] -1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] - 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } - 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } - 1.2 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } - 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } - 1.4 | Call { location: 7 } - ... - 1.43 | Return -2 EXPR [ (1, _1) -2 ] -``` - -The `->` here shows the debugger paused at an ACIR opcode: `BRILLIG`, at index 1, which denotes an unconstrained code block is about to start. - -Using the `into` command at this point would result in the debugger stopping at opcode 1.0, `Mov ...`, allowing the debugger user to follow unconstrained computation step by step. - -Use [the `step` command](#step-s) instead if you want to skip to the next ACIR code directly. - -#### `continue` (c) - -Continues execution until the next breakpoint, or the end of the program. - -#### `restart` (res) - -Interrupts execution, and restarts a new debugging session from scratch. - -#### `opcodes` (o) - -Display the program's ACIR opcode sequence. For example: - -``` -0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] -1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] - 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } - 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } - 1.2 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } - 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } - 1.4 | Call { location: 7 } - ... - 1.43 | Return -2 EXPR [ (1, _1) -2 ] -``` - -### Breakpoints - -#### `break [Opcode]` (or shorthand `b [Opcode]`) - -Sets a breakpoint on the specified opcode index. To get a list of the program opcode numbers, see [the `opcode` command](#opcodes-o). For example: - -``` -0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] -1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] - 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } - 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } - 1.2 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } - 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } - 1.4 | Call { location: 7 } - ... - 1.43 | Return -2 EXPR [ (1, _1) -2 ] -``` - -In this example, issuing a `break 1.2` command adds break on opcode 1.2, as denoted by the `*` character: - -``` -0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] -1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] - 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } - 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } - 1.2 | * Const { destination: RegisterIndex(1), value: Value { inner: 0 } } - 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } - 1.4 | Call { location: 7 } - ... - 1.43 | Return -2 EXPR [ (1, _1) -2 ] -``` - -Running [the `continue` command](#continue-c) at this point would cause the debugger to execute the program until opcode 1.2. - -#### `delete [Opcode]` (or shorthand `d [Opcode]`) - -Deletes a breakpoint at an opcode location. Usage is analogous to [the `break` command](#). - -### Variable inspection - -#### vars - -Show variable values available at this point in execution. - -:::note -The ability to inspect variable values from the debugger depends on compilation to be run in a special debug instrumentation mode. This instrumentation weaves variable tracing code with the original source code. - -So variable value inspection comes at the expense of making the resulting ACIR bytecode bigger and harder to understand and optimize. - -If you find this compromise unacceptable, you can run the debugger with the flag `--skip-debug-instrumentation`. This will compile your circuit without any additional debug information, so the resulting ACIR bytecode will be identical to the one produced by standard Noir compilation. However, if you opt for this, the `vars` command will not be available while debugging. -::: - - -### Stacktrace - -#### `stacktrace` - -Displays the current stack trace. - - -### Witness map - -#### `witness` (w) - -Show witness map. For example: - -``` -_0 = 0 -_1 = 2 -_2 = 1 -``` - -#### `witness [Witness Index]` - -Display a single witness from the witness map. For example: - -``` -> witness 1 -_1 = 2 -``` - -#### `witness [Witness Index] [New value]` - -Overwrite the given index with a new value. For example: - -``` -> witness 1 3 -_1 = 3 -``` - - -### Unconstrained VM memory - -#### `memory` - -Show unconstrained VM memory state. For example: - -``` -> memory -At opcode 1.13: Store { destination_pointer: RegisterIndex(0), source: RegisterIndex(3) } -... -> registers -0 = 0 -1 = 10 -2 = 0 -3 = 1 -4 = 1 -5 = 2³² -6 = 1 -> into -At opcode 1.14: Const { destination: RegisterIndex(5), value: Value { inner: 1 } } -... -> memory -0 = 1 -> -``` - -In the example above: we start with clean memory, then step through a `Store` opcode which stores the value of register 3 (1) into the memory address stored in register 0 (0). Thus now `memory` shows memory address 0 contains value 1. - -:::note -This command is only functional while the debugger is executing unconstrained code. -::: - -#### `memset [Memory address] [New value]` - -Update a memory cell with the given value. For example: - -``` -> memory -0 = 1 -> memset 0 2 -> memory -0 = 2 -> memset 1 4 -> memory -0 = 2 -1 = 4 -> -``` - -:::note -This command is only functional while the debugger is executing unconstrained code. -::: \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.39.0/reference/debugger/debugger_vscode.md b/docs/versioned_docs/version-v0.39.0/reference/debugger/debugger_vscode.md deleted file mode 100644 index c027332b3b0..00000000000 --- a/docs/versioned_docs/version-v0.39.0/reference/debugger/debugger_vscode.md +++ /dev/null @@ -1,82 +0,0 @@ ---- -title: VS Code Debugger -description: - VS Code Debugger configuration and features. -keywords: - [ - Nargo, - Noir CLI, - Noir Debugger, - VS Code, - IDE, - ] -sidebar_position: 0 ---- - -# VS Code Noir Debugger Reference - -The Noir debugger enabled by the vscode-noir extension ships with default settings such that the most common scenario should run without any additional configuration steps. - -These defaults can nevertheless be overridden by defining a launch configuration file. This page provides a reference for the properties you can override via a launch configuration file, as well as documenting the Nargo `dap` command, which is a dependency of the VS Code Noir debugger. - - -## Creating and editing launch configuration files - -To create a launch configuration file from VS Code, open the _debug pane_, and click on _create a launch.json file_. - -![Creating a launch configuration file](@site/static/img/debugger/ref1-create-launch.png) - -A `launch.json` file will be created, populated with basic defaults. - -### Noir Debugger launch.json properties - -#### projectFolder - -_String, optional._ - -Absolute path to the Nargo project to debug. By default, it is dynamically determined by looking for the nearest `Nargo.toml` file to the active file at the moment of launching the debugger. - -#### proverName - -_String, optional._ - -Name of the prover input to use. Defaults to `Prover`, which looks for a file named `Prover.toml` at the `projectFolder`. - -#### generateAcir - -_Boolean, optional._ - -If true, generate ACIR opcodes instead of unconstrained opcodes which will be closer to release binaries but less convenient for debugging. Defaults to `false`. - -#### skipInstrumentation - -_Boolean, optional._ - -Skips variables debugging instrumentation of code, making debugging less convenient but the resulting binary smaller and closer to production. Defaults to `false`. - -:::note -Skipping instrumentation causes the debugger to be unable to inspect local variables. -::: - -## `nargo dap [OPTIONS]` - -When run without any option flags, it starts the Nargo Debug Adapter Protocol server, which acts as the debugging backend for the VS Code Noir Debugger. - -All option flags are related to preflight checks. The Debug Adapter Protocol specifies how errors are to be informed from a running DAP server, but it doesn't specify mechanisms to communicate server initialization errors between the DAP server and its client IDE. - -Thus `nargo dap` ships with a _preflight check_ mode. If flag `--preflight-check` and the rest of the `--preflight-*` flags are provided, Nargo will run the same initialization routine except it will not start the DAP server. - -`vscode-noir` will then run `nargo dap` in preflight check mode first before a debugging session starts. If the preflight check ends in error, vscode-noir will present stderr and stdout output from this process through its own Output pane in VS Code. This makes it possible for users to diagnose what pieces of configuration might be wrong or missing in case of initialization errors. - -If the preflight check succeeds, `vscode-noir` proceeds to start the DAP server normally but running `nargo dap` without any additional flags. - -### Options - -| Option | Description | -| --------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | -| `--preflight-check` | If present, dap runs in preflight check mode. | -| `--preflight-project-folder ` | Absolute path to the project to debug for preflight check. | -| `--preflight-prover-name ` | Name of prover file to use for preflight check | -| `--preflight-generate-acir` | Optional. If present, compile in ACIR mode while running preflight check. | -| `--preflight-skip-instrumentation` | Optional. If present, compile without introducing debug instrumentation while running preflight check. | -| `-h, --help` | Print help. | diff --git a/docs/versioned_docs/version-v0.39.0/reference/nargo_commands.md b/docs/versioned_docs/version-v0.39.0/reference/nargo_commands.md deleted file mode 100644 index 8842fad6647..00000000000 --- a/docs/versioned_docs/version-v0.39.0/reference/nargo_commands.md +++ /dev/null @@ -1,474 +0,0 @@ ---- -title: Nargo -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, - ] -sidebar_position: 0 ---- - -# Command-Line Help for `nargo` - -This document contains the help content for the `nargo` command-line program. - -**Command Overview:** - -* [`nargo`↴](#nargo) -* [`nargo check`↴](#nargo-check) -* [`nargo fmt`↴](#nargo-fmt) -* [`nargo compile`↴](#nargo-compile) -* [`nargo new`↴](#nargo-new) -* [`nargo init`↴](#nargo-init) -* [`nargo execute`↴](#nargo-execute) -* [`nargo debug`↴](#nargo-debug) -* [`nargo test`↴](#nargo-test) -* [`nargo info`↴](#nargo-info) -* [`nargo lsp`↴](#nargo-lsp) -* [`nargo generate-completion-script`↴](#nargo-generate-completion-script) - -## `nargo` - -Noir's package manager - -**Usage:** `nargo ` - -###### **Subcommands:** - -* `check` — Checks the constraint system for errors -* `fmt` — Format the Noir files in a workspace -* `compile` — Compile the program and its secret execution trace into ACIR format -* `new` — Create a Noir project in a new directory -* `init` — Create a Noir project in the current directory -* `execute` — Executes a circuit to calculate its return value -* `debug` — Executes a circuit in debug mode -* `test` — Run the tests for this program -* `info` — Provides detailed information on each of a program's function (represented by a single circuit) -* `lsp` — Starts the Noir LSP server -* `generate-completion-script` — Generates a shell completion script for your favorite shell - -###### **Options:** - - - - -## `nargo check` - -Checks the constraint system for errors - -**Usage:** `nargo check [OPTIONS]` - -###### **Options:** - -* `--package ` — The name of the package to check -* `--workspace` — Check all packages in the workspace - - Possible values: `true`, `false` - -* `--overwrite` — Force overwrite of existing files - - Possible values: `true`, `false` - -* `--expression-width ` — Specify the backend expression width that should be targeted -* `--bounded-codegen` — Generate ACIR with the target backend expression width. The default is to generate ACIR without a bound and split expressions after code generation. Activating this flag can sometimes provide optimizations for certain programs - - Default value: `false` - - Possible values: `true`, `false` - -* `--force` — Force a full recompilation - - Possible values: `true`, `false` - -* `--print-acir` — Display the ACIR for compiled circuit - - Possible values: `true`, `false` - -* `--deny-warnings` — Treat all warnings as errors - - Possible values: `true`, `false` - -* `--silence-warnings` — Suppress warnings - - Possible values: `true`, `false` - -* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" -* `--skip-underconstrained-check` — Flag to turn off the compiler check for under constrained values. Warning: This can improve compilation speed but can also lead to correctness errors. This check should always be run on production code - - Possible values: `true`, `false` - - - - -## `nargo fmt` - -Format the Noir files in a workspace - -**Usage:** `nargo fmt [OPTIONS]` - -###### **Options:** - -* `--check` — Run noirfmt in check mode - - Possible values: `true`, `false` - - - - -## `nargo compile` - -Compile the program and its secret execution trace into ACIR format - -**Usage:** `nargo compile [OPTIONS]` - -###### **Options:** - -* `--package ` — The name of the package to compile -* `--workspace` — Compile all packages in the workspace - - Possible values: `true`, `false` - -* `--expression-width ` — Specify the backend expression width that should be targeted -* `--bounded-codegen` — Generate ACIR with the target backend expression width. The default is to generate ACIR without a bound and split expressions after code generation. Activating this flag can sometimes provide optimizations for certain programs - - Default value: `false` - - Possible values: `true`, `false` - -* `--force` — Force a full recompilation - - Possible values: `true`, `false` - -* `--print-acir` — Display the ACIR for compiled circuit - - Possible values: `true`, `false` - -* `--deny-warnings` — Treat all warnings as errors - - Possible values: `true`, `false` - -* `--silence-warnings` — Suppress warnings - - Possible values: `true`, `false` - -* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" -* `--skip-underconstrained-check` — Flag to turn off the compiler check for under constrained values. Warning: This can improve compilation speed but can also lead to correctness errors. This check should always be run on production code - - Possible values: `true`, `false` - - - - -## `nargo new` - -Create a Noir project in a new directory - -**Usage:** `nargo new [OPTIONS] ` - -###### **Arguments:** - -* `` — The path to save the new project - -###### **Options:** - -* `--name ` — Name of the package [default: package directory name] -* `--lib` — Use a library template - - Possible values: `true`, `false` - -* `--bin` — Use a binary template [default] - - Possible values: `true`, `false` - -* `--contract` — Use a contract template - - Possible values: `true`, `false` - - - - -## `nargo init` - -Create a Noir project in the current directory - -**Usage:** `nargo init [OPTIONS]` - -###### **Options:** - -* `--name ` — Name of the package [default: current directory name] -* `--lib` — Use a library template - - Possible values: `true`, `false` - -* `--bin` — Use a binary template [default] - - Possible values: `true`, `false` - -* `--contract` — Use a contract template - - Possible values: `true`, `false` - - - - -## `nargo execute` - -Executes a circuit to calculate its return value - -**Usage:** `nargo execute [OPTIONS] [WITNESS_NAME]` - -###### **Arguments:** - -* `` — Write the execution witness to named file - -Defaults to the name of the package being executed. - -###### **Options:** - -* `-p`, `--prover-name ` — The name of the toml file which contains the inputs for the prover - - Default value: `Prover` -* `--package ` — The name of the package to execute -* `--workspace` — Execute all packages in the workspace - - Possible values: `true`, `false` - -* `--expression-width ` — Specify the backend expression width that should be targeted -* `--bounded-codegen` — Generate ACIR with the target backend expression width. The default is to generate ACIR without a bound and split expressions after code generation. Activating this flag can sometimes provide optimizations for certain programs - - Default value: `false` - - Possible values: `true`, `false` - -* `--force` — Force a full recompilation - - Possible values: `true`, `false` - -* `--print-acir` — Display the ACIR for compiled circuit - - Possible values: `true`, `false` - -* `--deny-warnings` — Treat all warnings as errors - - Possible values: `true`, `false` - -* `--silence-warnings` — Suppress warnings - - Possible values: `true`, `false` - -* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" -* `--skip-underconstrained-check` — Flag to turn off the compiler check for under constrained values. Warning: This can improve compilation speed but can also lead to correctness errors. This check should always be run on production code - - Possible values: `true`, `false` - -* `--oracle-resolver ` — JSON RPC url to solve oracle calls - - - -## `nargo debug` - -Executes a circuit in debug mode - -**Usage:** `nargo debug [OPTIONS] [WITNESS_NAME]` - -###### **Arguments:** - -* `` — Write the execution witness to named file - -###### **Options:** - -* `-p`, `--prover-name ` — The name of the toml file which contains the inputs for the prover - - Default value: `Prover` -* `--package ` — The name of the package to execute -* `--expression-width ` — Specify the backend expression width that should be targeted -* `--bounded-codegen` — Generate ACIR with the target backend expression width. The default is to generate ACIR without a bound and split expressions after code generation. Activating this flag can sometimes provide optimizations for certain programs - - Default value: `false` - - Possible values: `true`, `false` - -* `--force` — Force a full recompilation - - Possible values: `true`, `false` - -* `--print-acir` — Display the ACIR for compiled circuit - - Possible values: `true`, `false` - -* `--deny-warnings` — Treat all warnings as errors - - Possible values: `true`, `false` - -* `--silence-warnings` — Suppress warnings - - Possible values: `true`, `false` - -* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" -* `--skip-underconstrained-check` — Flag to turn off the compiler check for under constrained values. Warning: This can improve compilation speed but can also lead to correctness errors. This check should always be run on production code - - Possible values: `true`, `false` - -* `--acir-mode` — Force ACIR output (disabling instrumentation) - - Possible values: `true`, `false` - -* `--skip-instrumentation ` — Disable vars debug instrumentation (enabled by default) - - Possible values: `true`, `false` - - - - -## `nargo test` - -Run the tests for this program - -**Usage:** `nargo test [OPTIONS] [TEST_NAME]` - -###### **Arguments:** - -* `` — If given, only tests with names containing this string will be run - -###### **Options:** - -* `--show-output` — Display output of `println` statements - - Possible values: `true`, `false` - -* `--exact` — Only run tests that match exactly - - Possible values: `true`, `false` - -* `--package ` — The name of the package to test -* `--workspace` — Test all packages in the workspace - - Possible values: `true`, `false` - -* `--expression-width ` — Specify the backend expression width that should be targeted -* `--bounded-codegen` — Generate ACIR with the target backend expression width. The default is to generate ACIR without a bound and split expressions after code generation. Activating this flag can sometimes provide optimizations for certain programs - - Default value: `false` - - Possible values: `true`, `false` - -* `--force` — Force a full recompilation - - Possible values: `true`, `false` - -* `--print-acir` — Display the ACIR for compiled circuit - - Possible values: `true`, `false` - -* `--deny-warnings` — Treat all warnings as errors - - Possible values: `true`, `false` - -* `--silence-warnings` — Suppress warnings - - Possible values: `true`, `false` - -* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" -* `--skip-underconstrained-check` — Flag to turn off the compiler check for under constrained values. Warning: This can improve compilation speed but can also lead to correctness errors. This check should always be run on production code - - Possible values: `true`, `false` - -* `--oracle-resolver ` — JSON RPC url to solve oracle calls - - - -## `nargo info` - -Provides detailed information on each of a program's function (represented by a single circuit) - -Current information provided per circuit: 1. The number of ACIR opcodes 2. Counts the final number gates in the circuit used by a backend - -**Usage:** `nargo info [OPTIONS]` - -###### **Options:** - -* `--package ` — The name of the package to detail -* `--workspace` — Detail all packages in the workspace - - Possible values: `true`, `false` - -* `--profile-execution` - - Possible values: `true`, `false` - -* `-p`, `--prover-name ` — The name of the toml file which contains the inputs for the prover - - Default value: `Prover` -* `--expression-width ` — Specify the backend expression width that should be targeted -* `--bounded-codegen` — Generate ACIR with the target backend expression width. The default is to generate ACIR without a bound and split expressions after code generation. Activating this flag can sometimes provide optimizations for certain programs - - Default value: `false` - - Possible values: `true`, `false` - -* `--force` — Force a full recompilation - - Possible values: `true`, `false` - -* `--print-acir` — Display the ACIR for compiled circuit - - Possible values: `true`, `false` - -* `--deny-warnings` — Treat all warnings as errors - - Possible values: `true`, `false` - -* `--silence-warnings` — Suppress warnings - - Possible values: `true`, `false` - -* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" -* `--skip-underconstrained-check` — Flag to turn off the compiler check for under constrained values. Warning: This can improve compilation speed but can also lead to correctness errors. This check should always be run on production code - - Possible values: `true`, `false` - - - - -## `nargo lsp` - -Starts the Noir LSP server - -Starts an LSP server which allows IDEs such as VS Code to display diagnostics in Noir source. - -VS Code Noir Language Support: https://marketplace.visualstudio.com/items?itemName=noir-lang.vscode-noir - -**Usage:** `nargo lsp` - - - -## `nargo generate-completion-script` - -Generates a shell completion script for your favorite shell - -**Usage:** `nargo generate-completion-script ` - -###### **Arguments:** - -* `` — The shell to generate completions for. One of: bash, elvish, fish, powershell, zsh - - - -
- - - This document was generated automatically by - clap-markdown. - - diff --git a/docs/versioned_docs/version-v0.39.0/reference/noir_codegen.md b/docs/versioned_docs/version-v0.39.0/reference/noir_codegen.md deleted file mode 100644 index e4c362f9610..00000000000 --- a/docs/versioned_docs/version-v0.39.0/reference/noir_codegen.md +++ /dev/null @@ -1,116 +0,0 @@ ---- -title: Noir Codegen for TypeScript -description: Learn how to use Noir codegen to generate TypeScript bindings -keywords: [Nargo, Noir, compile, TypeScript] -sidebar_position: 3 ---- - -When using TypeScript, it is extra work to interpret Noir program outputs in a type-safe way. Third party libraries may exist for popular Noir programs, but they are either hard to find or unmaintained. - -Now you can generate TypeScript bindings for your Noir programs in two steps: - -1. Exporting Noir functions using `nargo export` -2. Using the TypeScript module `noir_codegen` to generate TypeScript binding - -**Note:** you can only export functions from a Noir *library* (not binary or contract program types). - -## Installation - -### Your TypeScript project - -If you don't already have a TypeScript project you can add the module with `yarn` (or `npm`), then initialize it: - -```bash -yarn add typescript -D -npx tsc --init -``` - -### Add TypeScript module - `noir_codegen` - -The following command will add the module to your project's devDependencies: - -```bash -yarn add @noir-lang/noir_codegen -D -``` - -### Nargo library - -Make sure you have Nargo, v0.25.0 or greater, installed. If you don't, follow the [installation guide](../getting_started/noir_installation.md). - -If you're in a new project, make a `circuits` folder and create a new Noir library: - -```bash -mkdir circuits && cd circuits -nargo new --lib myNoirLib -``` - -## Usage - -### Export ABI of specified functions - -First go to the `.nr` files in your Noir library, and add the `#[export]` macro to each function that you want to use in TypeScript. - -```rust -#[export] -fn your_function(... -``` - -From your Noir library (where `Nargo.toml` is), run the following command: - -```bash -nargo export -``` - -You will now have an `export` directory with a .json file per exported function. - -You can also specify the directory of Noir programs using `--program-dir`, for example: - -```bash -nargo export --program-dir=./circuits/myNoirLib -``` - -### Generate TypeScript bindings from exported functions - -To use the `noir-codegen` package we added to the TypeScript project: - -```bash -yarn noir-codegen ./export/your_function.json -``` - -This creates an `exports` directory with an `index.ts` file containing all exported functions. - -**Note:** adding `--out-dir` allows you to specify an output dir for your TypeScript bindings to go. Eg: - -```bash -yarn noir-codegen ./export/*.json --out-dir ./path/to/output/dir -``` - -## Example .nr function to .ts output - -Consider a Noir library with this function: - -```rust -#[export] -fn not_equal(x: Field, y: Field) -> bool { - x != y -} -``` - -After the export and codegen steps, you should have an `index.ts` like: - -```typescript -export type Field = string; - - -export const is_equal_circuit: CompiledCircuit = -{"abi":{"parameters":[{"name":"x","type":{"kind":"field"},"visibility":"private"},{"name":"y","type":{"kind":"field"},"visibility":"private"}],"return_type":{"abi_type":{"kind":"boolean"},"visibility":"private"}},"bytecode":"H4sIAAAAAAAA/7WUMQ7DIAxFQ0Krrr2JjSGYLVcpKrn/CaqqDQN12WK+hPBgmWd/wEyHbF1SS923uhOs3pfoChI+wKXMAXzIKyNj4PB0TFTYc0w5RUjoqeAeEu1wqK0F54RGkWvW44LPzExnlkbMEs4JNZmN8PxS42uHv82T8a3Jeyn2Ks+VLPcO558HmyLMCDOXAXXtpPt4R/Rt9T36ss6dS9HGPx/eG17nGegKBQAA"}; - -export async function is_equal(x: Field, y: Field, foreignCallHandler?: ForeignCallHandler): Promise { - const program = new Noir(is_equal_circuit); - const args: InputMap = { x, y }; - const { returnValue } = await program.execute(args, foreignCallHandler); - return returnValue as boolean; -} -``` - -Now the `is_equal()` function and relevant types are readily available for use in TypeScript. diff --git a/docs/versioned_docs/version-v0.39.0/tooling/debugger.md b/docs/versioned_docs/version-v0.39.0/tooling/debugger.md deleted file mode 100644 index 200b5fc423a..00000000000 --- a/docs/versioned_docs/version-v0.39.0/tooling/debugger.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: Debugger -description: Learn about the Noir Debugger, in its REPL or VS Code versions. -keywords: [Nargo, VSCode, Visual Studio Code, REPL, Debugger] -sidebar_position: 2 ---- - -# Noir Debugger - -There are currently two ways of debugging Noir programs: - -1. From VS 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). -2. Via the REPL debugger, which ships with Nargo. - -In order to use either version of the debugger, you will need to install recent enough versions of Noir, [Nargo](../getting_started/noir_installation.md) and vscode-noir: - -- Noir & Nargo ≥0.28.0 -- Noir's VS Code extension ≥0.0.11 - -:::info -At the moment, the debugger supports debugging binary projects, but not contracts. -::: - -We cover the VS Code Noir debugger more in depth in [its VS Code debugger how-to guide](../how_to/debugger/debugging_with_vs_code.md) and [the reference](../reference/debugger/debugger_vscode.md). - -The REPL debugger is discussed at length in [the REPL debugger how-to guide](../how_to/debugger/debugging_with_the_repl.md) and [the reference](../reference/debugger/debugger_repl.md). diff --git a/docs/versioned_docs/version-v0.39.0/tooling/language_server.md b/docs/versioned_docs/version-v0.39.0/tooling/language_server.md deleted file mode 100644 index 81e0356ef8a..00000000000 --- a/docs/versioned_docs/version-v0.39.0/tooling/language_server.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -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] -sidebar_position: 0 ---- - -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.39.0/tooling/testing.md b/docs/versioned_docs/version-v0.39.0/tooling/testing.md deleted file mode 100644 index 866677da567..00000000000 --- a/docs/versioned_docs/version-v0.39.0/tooling/testing.md +++ /dev/null @@ -1,79 +0,0 @@ ---- -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] -sidebar_position: 1 ---- - -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 all -the constraints 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); -} -``` - -The string given to `should_fail_with` doesn't need to exactly match the failure reason, it just needs to be a substring of it: - -```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 = "airspeed velocity")] -fn test_bridgekeeper() { - main(32); -} -``` \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.39.0/tutorials/noirjs_app.md b/docs/versioned_docs/version-v0.39.0/tutorials/noirjs_app.md deleted file mode 100644 index 6e69ea0bbed..00000000000 --- a/docs/versioned_docs/version-v0.39.0/tutorials/noirjs_app.md +++ /dev/null @@ -1,366 +0,0 @@ ---- -title: Building a web app with NoirJS -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, app] -sidebar_position: 0 -pagination_next: noir/concepts/data_types/index ---- - -NoirJS is a set of packages meant to work both in a browser and a server environment. In this tutorial, we will build a simple web app using them. From here, you should get an idea on how to proceed with your own Noir projects! - -You can find the complete app code for this guide [here](https://github.com/noir-lang/tiny-noirjs-app). - -## Setup - -:::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.31.x matches `noir_js@0.31.x`, etc. - -In this guide, we will be pinned to 0.31.0. - -::: - -Before we start, we want to make sure we have Node, Nargo and the Barretenberg proving system (`bb`) installed. - -We start by opening a terminal and executing `node --version`. If we don't get an output like `v20.10.0`, that means node is not installed. Let's do that by following the handy [nvm guide](https://github.com/nvm-sh/nvm?tab=readme-ov-file#install--update-script). - -As for `Nargo`, we can follow the [Nargo guide](../getting_started/quick_start.md) to install it. If you're lazy, just paste this on a terminal and run `noirup`: - -```sh -curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash -``` - -Follow the instructions on [this page](https://github.com/AztecProtocol/aztec-packages/tree/master/barretenberg/cpp/src/barretenberg/bb#installation) to install `bb`. -Version 0.41.0 is compatible with `nargo` version 0.31.0, which you can install with `bbup -v 0.41.0` once `bbup` is installed. - -Easy enough. Onwards! - -## Our project - -ZK is a powerful technology. An app that doesn't reveal one of the inputs to _anyone_ is almost unbelievable, yet Noir makes it as easy as a single line of code. - -In fact, it's so simple that it comes nicely packaged in `nargo`. Let's do that! - -### Nargo - -Run: - -```bash -nargo new circuit -``` - -And... That's about it. Your program is ready to be compiled and run. - -To compile, let's `cd` into the `circuit` folder to enter our project, and call: - -```bash -nargo compile -``` - -This compiles our circuit into `json` format and add it to a new `target` folder. - -:::info - -At this point in the tutorial, your folder structure should look like this: - -```tree -. -└── circuit <---- our working directory - ├── Nargo.toml - ├── src - │ └── main.nr - └── target - └── circuit.json -``` - -::: - -### Node and Vite - -If you want to explore Nargo, feel free to go on a side-quest now and follow the steps in the -[getting started](../getting_started/quick_start.md) guide. However, we want our app to run on the browser, so we need Vite. - -Vite is a powerful tool to generate static websites. While it provides all kinds of features, let's just go barebones with some good old vanilla JS. - -To do this this, go back to the previous folder (`cd ..`) and create a new vite project by running `npm create vite` and choosing "Vanilla" and "Javascript". - -A wild `vite-project` directory should now appear in your root folder! Let's not waste any time and dive right in: - -```bash -cd vite-project -``` - -### Setting Up Vite and Configuring the Project - -Before we proceed with any coding, let's get our environment tailored for Noir. We'll start by laying down the foundations with a `vite.config.js` file. This little piece of configuration is our secret sauce for making sure everything meshes well with the NoirJS libraries and other special setups we might need, like handling WebAssembly modules. Here’s how you get that going: - -#### Creating the vite.config.js - -In your freshly minted `vite-project` folder, create a new file named `vite.config.js` and open it in your code editor. Paste the following to set the stage: - -```javascript -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 { - build: { - target: 'esnext', - rollupOptions: { - external: ['@aztec/bb.js'] - } - }, - optimizeDeps: { - esbuildOptions: { - target: 'esnext' - } - }, - plugins: [ - copy({ - targets: [{ src: 'node_modules/**/*.wasm', dest: 'node_modules/.vite/dist' }], - copySync: true, - hook: 'buildStart', - }), - command === 'serve' ? wasmContentTypePlugin : [], - ], - }; - } - - return {}; -}); -``` - -#### Install Dependencies - -Now that our stage is set, install the necessary NoirJS packages along with our other dependencies: - -```bash -npm install && npm install @noir-lang/backend_barretenberg@0.31.0 @noir-lang/noir_js@0.31.0 -npm install rollup-plugin-copy --save-dev -``` - -:::info - -At this point in the tutorial, your folder structure should look like this: - -```tree -. -└── circuit - └── ...etc... -└── vite-project <---- our working directory - └── ...etc... -``` - -::: - -#### Some cleanup - -`npx create vite` is amazing but it creates a bunch of files we don't really need for our simple example. Actually, let's just delete everything except for `vite.config.js`, `index.html`, `main.js` and `package.json`. I feel lighter already. - -![my heart is ready for you, noir.js](@site/static/img/memes/titanic.jpeg) - -## HTML - -Our app won't run like this, of course. We need some working HTML, at least. Let's open our broken-hearted `index.html` and replace everything with this code snippet: - -```html - - - - - - -

Noir app

-
- - -
-
-

Logs

-

Proof

-
- - -``` - -It _could_ be a beautiful UI... Depending on which universe you live in. - -## Some good old vanilla Javascript - -Our love for Noir needs undivided attention, so let's just open `main.js` and delete everything (this is where the romantic scenery becomes a bit creepy). - -Start by pasting in this boilerplate code: - -```js -function display(container, msg) { - const c = document.getElementById(container); - const p = document.createElement('p'); - p.textContent = msg; - c.appendChild(p); -} - -document.getElementById('submitGuess').addEventListener('click', async () => { - try { - // here's where love happens - } catch (err) { - display('logs', 'Oh 💔 Wrong guess'); - } -}); -``` - -The display function doesn't do much. We're simply manipulating our website to see stuff happening. For example, if the proof fails, it will simply log a broken heart 😢 - -:::info - -At this point in the tutorial, your folder structure should look like this: - -```tree -. -└── circuit - └── ...same as above -└── vite-project - ├── vite.config.js - ├── main.js - ├── package.json - └── index.html -``` - -You'll see other files and folders showing up (like `package-lock.json`, `node_modules`) but you shouldn't have to care about those. - -::: - -## Some NoirJS - -We're starting with the good stuff now. If you've compiled the circuit as described above, you should have a `json` file we want to import at the very top of our `main.js` file: - -```ts -import circuit from '../circuit/target/circuit.json'; -``` - -[Noir is backend-agnostic](../index.mdx#whats-new-about-noir). We write Noir, but we also need a proving backend. That's why we need to import and instantiate the two dependencies we installed above: `BarretenbergBackend` and `Noir`. Let's import them right below: - -```js -import { BarretenbergBackend, BarretenbergVerifier as Verifier } from '@noir-lang/backend_barretenberg'; -import { Noir } from '@noir-lang/noir_js'; -``` - -And instantiate them inside our try-catch block: - -```ts -// try { -const backend = new BarretenbergBackend(circuit); -const noir = new Noir(circuit); -// } -``` - -:::note - -For the remainder of the tutorial, everything will be happening inside the `try` block - -::: - -## Our app - -Now for the app itself. We're capturing whatever is in the input when people press the submit button. Just add this: - -```js -const x = parseInt(document.getElementById('guessInput').value); -const input = { x, y: 2 }; -``` - -Now we're ready to prove stuff! Let's feed some inputs to our circuit and calculate the proof: - -```js -await setup(); // let's squeeze our wasm inits here - -display('logs', 'Generating proof... ⌛'); -const { witness } = await noir.execute(input); -const proof = await backend.generateProof(witness); -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 run dev`. If it doesn't open a browser for you, just visit `localhost:5173`. You should now see the worst UI ever, with an ugly input. - -![Getting Started 0](@site/static/img/noir_getting_started_1.png) - -Now, our circuit says `fn main(x: Field, y: pub Field)`. This means only the `y` value is public, and it's hardcoded above: `input = { x, y: 2 }`. In other words, you won't need to send your secret`x` to the verifier! - -By inputting any number other than 2 in the input box and clicking "submit", you should get a valid proof. Otherwise the proof won't even generate correctly. By the way, if you're human, you shouldn't be able to understand anything on the "proof" box. That's OK. We like you, human ❤️. - -## Verifying - -Time to celebrate, yes! But we shouldn't trust machines so blindly. Let's add these lines to see our proof being verified: - -```js -display('logs', 'Verifying proof... ⌛'); -const isValid = await backend.verifyProof(proof); - -// or to cache and use the verification key: -// const verificationKey = await backend.getVerificationKey(); -// const verifier = new Verifier(); -// const isValid = await verifier.verifyProof(proof, verificationKey); - -if (isValid) display('logs', 'Verifying proof... ✅'); -``` - -You have successfully generated a client-side Noir web app! - -![coded app without math knowledge](@site/static/img/memes/flextape.jpeg) - -## 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/vite-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. - -## UltraHonk Backend - -Barretenberg has recently exposed a new UltraHonk backend. We can use UltraHonk in NoirJS after version 0.33.0. Everything will be the same as the tutorial above, except that the class we need to import will change: - -```js -import { UltraHonkBackend, UltraHonkVerifier as Verifier } from '@noir-lang/backend_barretenberg'; -``` - -The backend will then be instantiated as such: - -```js -const backend = new UltraHonkBackend(circuit); -``` - -Then all the commands to prove and verify your circuit will be same. - -The only feature currently unsupported with UltraHonk are [recursive proofs](../explainers/explainer-recursion.md). diff --git a/docs/versioned_docs/version-v1.0.0-beta.0/how_to/how-to-solidity-verifier.md b/docs/versioned_docs/version-v1.0.0-beta.0/how_to/how-to-solidity-verifier.md deleted file mode 100644 index 2cc0f8e57ce..00000000000 --- a/docs/versioned_docs/version-v1.0.0-beta.0/how_to/how-to-solidity-verifier.md +++ /dev/null @@ -1,259 +0,0 @@ ---- -title: Generate a 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, - ] -sidebar_position: 0 -pagination_next: tutorials/noirjs_app ---- - -Noir has the ability to generate a verifier contract in Solidity, which can be deployed in many EVM-compatible blockchains such as Ethereum. - -This allows for a powerful feature set, as one can make use of the conciseness and the privacy provided by Noir in an immutable ledger. Applications can range from simple P2P guessing games, to complex private DeFi interactions. - -This guide shows you how to generate a Solidity Verifier and deploy it on the [Remix IDE](https://remix.ethereum.org/). It is assumed that: - -- You are comfortable with the Solidity programming language and understand how contracts are deployed on the Ethereum network -- You have Noir installed and you have a Noir program. If you don't, [get started](../getting_started/quick_start.md) with Nargo and the example Hello Noir circuit -- You are comfortable navigating RemixIDE. If you aren't or you need a refresher, you can find some video tutorials [here](https://www.youtube.com/channel/UCjTUPyFEr2xDGN6Cg8nKDaA) that could help you. - -## Rundown - -Generating a Solidity Verifier contract is actually a one-command process. However, compiling it and deploying it can have some caveats. Here's the rundown of this guide: - -1. How to generate a solidity smart contract -2. How to compile the smart contract in the RemixIDE -3. How to deploy it to a testnet - -## Step 1 - Generate a contract - -This is by far the most straightforward step. Just run: - -```sh -nargo compile -``` - -This will compile your source code into a Noir build artifact to be stored in the `./target` directory, you can then generate the smart contract using the commands: - -```sh -# Here we pass the path to the newly generated Noir artifact. -bb write_vk -b ./target/.json -bb contract -``` - -replacing `` with the name of your Noir project. A new `contract` folder would then be generated in your project directory, containing the Solidity -file `contract.sol`. It can be deployed to any EVM blockchain acting as a verifier smart contract. - -You can find more information about `bb` and the default Noir proving backend on [this page](../getting_started/quick_start.md#proving-backend). - -:::info - -It is possible to generate 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 generation of verifier contracts, for the time being these are only in Solidity. -::: - -## Step 2 - Compiling - -We will mostly skip the details of RemixIDE, as the UI can change from version to version. For now, we can just open -Remix and create a blank workspace. - -![Create Workspace](@site/static/img/how-tos/solidity_verifier_1.png) - -We will create a new file to contain the contract Nargo generated, and copy-paste its content. - -:::warning - -You'll likely see a warning advising you to not trust pasted code. While it is an important warning, it is irrelevant in the context of this guide and can be ignored. We will not be deploying anywhere near a mainnet. - -::: - -To compile our the verifier, we can navigate to the compilation tab: - -![Compilation Tab](@site/static/img/how-tos/solidity_verifier_2.png) - -Remix should automatically match a suitable compiler version. However, hitting the "Compile" button will most likely generate a "Stack too deep" error: - -![Stack too deep](@site/static/img/how-tos/solidity_verifier_3.png) - -This is due to the verify function needing to put many variables on the stack, but enabling the optimizer resolves the issue. To do this, let's open the "Advanced Configurations" tab and enable optimization. The default 200 runs will suffice. - -:::info - -This time we will see a warning about an unused function parameter. This is expected, as the `verify` function doesn't use the `_proof` parameter inside a solidity block, it is loaded from calldata and used in assembly. - -::: - -![Compilation success](@site/static/img/how-tos/solidity_verifier_4.png) - -## Step 3 - Deploying - -At this point we should have a compiled contract ready to deploy. If we navigate to the deploy section in Remix, we will see many different environments we can deploy to. The steps to deploy on each environment would be out-of-scope for this guide, so we will just use the default Remix VM. - -Looking closely, we will notice that our "Solidity Verifier" is actually three contracts working together: - -- An `UltraVerificationKey` library which simply stores the verification key for our circuit. -- An abstract contract `BaseUltraVerifier` containing most of the verifying logic. -- A main `UltraVerifier` contract that inherits from the Base and uses the Key contract. - -Remix will take care of the dependencies for us so we can simply deploy the UltraVerifier contract by selecting it and hitting "deploy": - -![Deploying UltraVerifier](@site/static/img/how-tos/solidity_verifier_5.png) - -A contract will show up in the "Deployed Contracts" section, where we can retrieve the Verification Key Hash. This is particularly useful for double-checking that the deployer contract is the correct one. - -:::note - -Why "UltraVerifier"? - -To be precise, the Noir compiler (`nargo`) doesn't generate the verifier contract directly. It compiles the Noir code into an intermediate language (ACIR), which is then executed by the backend. So it is the backend that returns the verifier smart contract, not Noir. - -In this case, the Barretenberg Backend uses the UltraPlonk proving system, hence the "UltraVerifier" name. - -::: - -## Step 4 - Verifying - -To verify a proof using the Solidity verifier contract, we call the `verify` function in this extended contract: - -```solidity -function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external view returns (bool) -``` - -When using the default example in the [Hello Noir](../getting_started/quick_start.md) guide, the easiest way to confirm that the verifier contract is doing its job is by calling the `verify` function via remix with the required parameters. Note that the public inputs must be passed in separately to the rest of the proof so we must split the proof as returned from `bb`. - -First generate a proof with `bb` at the location `./proof` using the steps in [get started](../getting_started/quick_start.md), this proof is in a binary format but we want to convert it into a hex string to pass into Remix, this can be done with the - -```bash -# This value must be changed to match the number of public inputs (including return values!) in your program. -NUM_PUBLIC_INPUTS=1 -PUBLIC_INPUT_BYTES=32*NUM_PUBLIC_INPUTS -HEX_PUBLIC_INPUTS=$(head -c $PUBLIC_INPUT_BYTES ./proof | od -An -v -t x1 | tr -d $' \n') -HEX_PROOF=$(tail -c +$(($PUBLIC_INPUT_BYTES + 1)) ./proof | od -An -v -t x1 | tr -d $' \n') - -echo "Public inputs:" -echo $HEX_PUBLIC_INPUTS - -echo "Proof:" -echo "0x$HEX_PROOF" -``` - -Remix expects that the public inputs will be split into an array of `bytes32` values so `HEX_PUBLIC_INPUTS` needs to be split up into 32 byte chunks which are prefixed with `0x` accordingly. - -A programmatic example of how the `verify` function is called can be seen 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"); -``` - -:::info[Return Values] - -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. - -For example, if you have Noir program like this: - -```rust -fn main( - // Public inputs - pubkey_x: pub Field, - pubkey_y: pub Field, - // Private inputs - priv_key: Field, -) -> pub Field -``` - -the `verify` function will expect the public inputs array (second function parameter) to be of length 3, the two inputs and the return value. - -Passing only two inputs will result in an error such as `PUBLIC_INPUT_COUNT_INVALID(3, 2)`. - -In this case, the inputs parameter to `verify` would be an array ordered as `[pubkey_x, pubkey_y, return`. - -::: - -:::tip[Structs] - -You can pass structs to the verifier contract. They will be flattened so that the array of inputs is 1-dimensional array. - -For example, 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) { - //... -} -``` - -The order of these inputs would be flattened to: `[x, nested.t1.val1, nested.t1.val2, nested.is_true, y]` - -::: - -The other function you can call is our entrypoint `verify` function, as defined above. - -:::tip - -It's worth noticing that the `verify` function is actually a `view` function. A `view` function does not alter the blockchain state, so it doesn't need to be distributed (i.e. it will run only on the executing node), and therefore doesn't cost any gas. - -This can be particularly useful in some situations. If Alice generated a proof and wants Bob to verify its correctness, Bob doesn't need to run Nargo, NoirJS, or any Noir specific infrastructure. He can simply make a call to the blockchain with the proof and verify it is correct without paying any gas. - -It would be incorrect to say that a Noir proof verification costs any gas at all. However, most of the time the result of `verify` is used to modify state (for example, to update a balance, a game state, etc). In that case the whole network needs to execute it, which does incur gas costs (calldata and execution, but not storage). - -::: - -## A Note on EVM chains - -Noir proof verification requires the ecMul, ecAdd and ecPairing precompiles. Not all EVM chains support EC Pairings, notably some of the ZK-EVMs. This means that you won't be able to use the verifier contract in all of them. You can find an incomplete list of which EVM chains support these precompiles [here](https://www.evmdiff.com/features?feature=precompiles). - -For example, chains like `zkSync ERA` and `Polygon zkEVM` do not currently support these precompiles, so proof verification via Solidity verifier contracts won't work. Here's a quick list of EVM chains that have been tested and are known to work: - -- Optimism -- Arbitrum -- Polygon PoS -- Scroll -- Celo -- BSC -- Blast L2 -- Avalanche C-Chain -- Mode -- Linea -- Moonbeam - -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. - -## What's next - -Now that you know how to call a Noir Solidity Verifier on a smart contract using Remix, you should be comfortable with using it with some programmatic frameworks, such as [hardhat](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat) and [foundry](https://github.com/noir-lang/noir-starter/tree/main/with-foundry). - -You can find other tools, examples, boilerplates and libraries in the [awesome-noir](https://github.com/noir-lang/awesome-noir) repository. - -You should also be ready to write and deploy your first NoirJS app and start generating proofs on websites, phones, and NodeJS environments! Head on to the [NoirJS tutorial](../tutorials/noirjs_app.md) to learn how to do that. diff --git a/docs/versioned_docs/version-v0.38.0/how_to/how-to-solidity-verifier.md b/docs/versioned_docs/version-v1.0.0-beta.0/how_to/how-to-solidity-verifier.mdx similarity index 64% rename from docs/versioned_docs/version-v0.38.0/how_to/how-to-solidity-verifier.md rename to docs/versioned_docs/version-v1.0.0-beta.0/how_to/how-to-solidity-verifier.mdx index 2cc0f8e57ce..da36b60920d 100644 --- a/docs/versioned_docs/version-v0.38.0/how_to/how-to-solidity-verifier.md +++ b/docs/versioned_docs/version-v1.0.0-beta.0/how_to/how-to-solidity-verifier.mdx @@ -20,24 +20,37 @@ sidebar_position: 0 pagination_next: tutorials/noirjs_app --- -Noir has the ability to generate a verifier contract in Solidity, which can be deployed in many EVM-compatible blockchains such as Ethereum. +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +Noir is universal. The witness and the compiled program can be fed into a proving backend such as Aztec's [Barretenberg](https://github.com/AztecProtocol/aztec-packages/tree/master/barretenberg), which can then generate a verifier contract for deployment on blockchains. This allows for a powerful feature set, as one can make use of the conciseness and the privacy provided by Noir in an immutable ledger. Applications can range from simple P2P guessing games, to complex private DeFi interactions. -This guide shows you how to generate a Solidity Verifier and deploy it on the [Remix IDE](https://remix.ethereum.org/). It is assumed that: +Although not strictly in the domain of Noir itself, this guide shows how to generate a Solidity Verifier with Barretenberg and deploy it on the [Remix IDE](https://remix.ethereum.org/). It is assumed that: +- You will be using Barretenberg as your proving backend +- You will be using an EVM blockchain to verify your proof - You are comfortable with the Solidity programming language and understand how contracts are deployed on the Ethereum network - You have Noir installed and you have a Noir program. If you don't, [get started](../getting_started/quick_start.md) with Nargo and the example Hello Noir circuit - You are comfortable navigating RemixIDE. If you aren't or you need a refresher, you can find some video tutorials [here](https://www.youtube.com/channel/UCjTUPyFEr2xDGN6Cg8nKDaA) that could help you. ## Rundown -Generating a Solidity Verifier contract is actually a one-command process. However, compiling it and deploying it can have some caveats. Here's the rundown of this guide: +Generating a Solidity Verifier with Barretenberg contract is actually a one-command process. However, compiling it and deploying it can have some caveats. Here's the rundown of this guide: 1. How to generate a solidity smart contract 2. How to compile the smart contract in the RemixIDE 3. How to deploy it to a testnet +:::info[Which proving system to use?] + +Barretenberg currently provides two provers: `UltraPlonk` and `UltraHonk`. In a nutshell, `UltraHonk` is faster and uses less RAM, but its verifier contract is much more expensive. `UltraPlonk` is optimized for on-chain verification, but proving is more expensive. + +In any case, we provide instructions for both. Choose your poison ☠️ + +::: + ## Step 1 - Generate a contract This is by far the most straightforward step. Just run: @@ -46,25 +59,31 @@ This is by far the most straightforward step. Just run: nargo compile ``` -This will compile your source code into a Noir build artifact to be stored in the `./target` directory, you can then generate the smart contract using the commands: +This will compile your source code into a Noir build artifact to be stored in the `./target` directory. From here on, it's Barretenberg's work. You can generate the smart contract using the commands: + + + + +```sh +bb write_vk_ultra_keccak_honk -b ./target/.json +bb contract_ultra_honk +``` + + + ```sh -# Here we pass the path to the newly generated Noir artifact. bb write_vk -b ./target/.json bb contract ``` -replacing `` with the name of your Noir project. A new `contract` folder would then be generated in your project directory, containing the Solidity -file `contract.sol`. It can be deployed to any EVM blockchain acting as a verifier smart contract. + + -You can find more information about `bb` and the default Noir proving backend on [this page](../getting_started/quick_start.md#proving-backend). +replacing `` with the name of your Noir project. A `Verifier.sol` contract is now in the target folder and can be deployed to any EVM blockchain acting as a verifier smart contract. -:::info - -It is possible to generate verifier contracts of Noir programs for other smart contract platforms as long as the proving backend supplies an implementation. +You can find more information about `bb` and the default Noir proving backend on [this page](../getting_started/quick_start.md#proving-backend). -Barretenberg, the default proving backend for Nargo, supports generation of verifier contracts, for the time being these are only in Solidity. -::: ## Step 2 - Compiling @@ -85,17 +104,12 @@ To compile our the verifier, we can navigate to the compilation tab: ![Compilation Tab](@site/static/img/how-tos/solidity_verifier_2.png) -Remix should automatically match a suitable compiler version. However, hitting the "Compile" button will most likely generate a "Stack too deep" error: +Remix should automatically match a suitable compiler version. However, hitting the "Compile" button will most likely tell you the contract is too big to deploy on mainnet, or complain about a stack too deep: -![Stack too deep](@site/static/img/how-tos/solidity_verifier_3.png) +![Contract code too big](@site/static/img/how-tos/solidity_verifier_6.png) +![Stack too deep](@site/static/img/how-tos/solidity_verifier_8.png) -This is due to the verify function needing to put many variables on the stack, but enabling the optimizer resolves the issue. To do this, let's open the "Advanced Configurations" tab and enable optimization. The default 200 runs will suffice. - -:::info - -This time we will see a warning about an unused function parameter. This is expected, as the `verify` function doesn't use the `_proof` parameter inside a solidity block, it is loaded from calldata and used in assembly. - -::: +To avoid this, you can just use some optimization. Open the "Advanced Configurations" tab and enable optimization. The default 200 runs will suffice. ![Compilation success](@site/static/img/how-tos/solidity_verifier_4.png) @@ -103,54 +117,81 @@ This time we will see a warning about an unused function parameter. This is expe At this point we should have a compiled contract ready to deploy. If we navigate to the deploy section in Remix, we will see many different environments we can deploy to. The steps to deploy on each environment would be out-of-scope for this guide, so we will just use the default Remix VM. -Looking closely, we will notice that our "Solidity Verifier" is actually three contracts working together: - -- An `UltraVerificationKey` library which simply stores the verification key for our circuit. -- An abstract contract `BaseUltraVerifier` containing most of the verifying logic. -- A main `UltraVerifier` contract that inherits from the Base and uses the Key contract. - -Remix will take care of the dependencies for us so we can simply deploy the UltraVerifier contract by selecting it and hitting "deploy": +Looking closely, we will notice that our "Solidity Verifier" is composed on multiple contracts working together. Remix will take care of the dependencies for us so we can simply deploy the Verifier contract by selecting it and hitting "deploy": -![Deploying UltraVerifier](@site/static/img/how-tos/solidity_verifier_5.png) + + -A contract will show up in the "Deployed Contracts" section, where we can retrieve the Verification Key Hash. This is particularly useful for double-checking that the deployer contract is the correct one. +![Deploying HonkVerifier](@site/static/img/how-tos/solidity_verifier_7.png) -:::note + + -Why "UltraVerifier"? +![Deploying PlonkVerifier](@site/static/img/how-tos/solidity_verifier_9.png) -To be precise, the Noir compiler (`nargo`) doesn't generate the verifier contract directly. It compiles the Noir code into an intermediate language (ACIR), which is then executed by the backend. So it is the backend that returns the verifier smart contract, not Noir. + + -In this case, the Barretenberg Backend uses the UltraPlonk proving system, hence the "UltraVerifier" name. - -::: +A contract will show up in the "Deployed Contracts" section. ## Step 4 - Verifying -To verify a proof using the Solidity verifier contract, we call the `verify` function in this extended contract: +To verify a proof using the Solidity verifier contract, we call the `verify` function: ```solidity function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external view returns (bool) ``` -When using the default example in the [Hello Noir](../getting_started/quick_start.md) guide, the easiest way to confirm that the verifier contract is doing its job is by calling the `verify` function via remix with the required parameters. Note that the public inputs must be passed in separately to the rest of the proof so we must split the proof as returned from `bb`. +First generate a proof with `bb`. We need a `Prover.toml` file for our inputs. Run: -First generate a proof with `bb` at the location `./proof` using the steps in [get started](../getting_started/quick_start.md), this proof is in a binary format but we want to convert it into a hex string to pass into Remix, this can be done with the +```bash +nargo check +``` + +This will generate a `Prover.toml` you can fill with the values you want to prove. We can now execute the circuit with `nargo` and then use the proving backend to prove: + + + + +```bash +nargo execute +bb prove_ultra_keccak_honk -b ./target/.json -w ./target/ -o ./target/proof +``` + +:::tip[Public inputs] +Barretenberg attaches the public inputs to the proof, which in this case it's not very useful. If you're up for some JS, `bb.js` has [a method for it](https://github.com/AztecProtocol/aztec-packages/blob/master/barretenberg/ts/src/proof/index.ts), but in the CLI you can use this ugly snippet: + +```bash +cat ./target/proof | od -An -v -t x1 | tr -d $' \n' | sed 's/^.\{8\}//' | (read hex; echo "${hex:0:192}${hex:256}") +``` + +Beautiful. This assumes a circuit with one public input (32 bytes, for Barretenberg). For more inputs, you can just increment `hex:256` with 32 more bytes for each public input. + +::: + + + ```bash -# This value must be changed to match the number of public inputs (including return values!) in your program. -NUM_PUBLIC_INPUTS=1 -PUBLIC_INPUT_BYTES=32*NUM_PUBLIC_INPUTS -HEX_PUBLIC_INPUTS=$(head -c $PUBLIC_INPUT_BYTES ./proof | od -An -v -t x1 | tr -d $' \n') -HEX_PROOF=$(tail -c +$(($PUBLIC_INPUT_BYTES + 1)) ./proof | od -An -v -t x1 | tr -d $' \n') +nargo execute +bb prove -b ./target/.json -w ./target/ -o ./target/proof +``` + -echo "Public inputs:" -echo $HEX_PUBLIC_INPUTS +:::tip[Public inputs] +Barretenberg attaches the public inputs to the proof, which in this case it's not very useful. If you're up for some JS, `bb.js` has [a method for it](https://github.com/AztecProtocol/aztec-packages/blob/master/barretenberg/ts/src/proof/index.ts), but in the CLI you can use this ugly snippet: -echo "Proof:" -echo "0x$HEX_PROOF" +```bash +tail -c +33 ./target/proof | od -An -v -t x1 | tr -d $' \n' ``` +Beautiful. This assumes a circuit with one public input (32 bytes, for Barretenberg). For more inputs, you can just add 32 more bytes for each public input to the `tail` command. + +::: + + + + Remix expects that the public inputs will be split into an array of `bytes32` values so `HEX_PUBLIC_INPUTS` needs to be split up into 32 byte chunks which are prefixed with `0x` accordingly. A programmatic example of how the `verify` function is called can be seen in the example zk voting application [here](https://github.com/noir-lang/noir-examples/blob/33e598c257e2402ea3a6b68dd4c5ad492bce1b0a/foundry-voting/src/zkVote.sol#L35): @@ -188,7 +229,7 @@ the `verify` function will expect the public inputs array (second function param Passing only two inputs will result in an error such as `PUBLIC_INPUT_COUNT_INVALID(3, 2)`. -In this case, the inputs parameter to `verify` would be an array ordered as `[pubkey_x, pubkey_y, return`. +In this case, the inputs parameter to `verify` would be an array ordered as `[pubkey_x, pubkey_y, return]`. ::: diff --git a/docs/versioned_docs/version-v1.0.0-beta.0/tutorials/noirjs_app.md b/docs/versioned_docs/version-v1.0.0-beta.0/tutorials/noirjs_app.md index 6e69ea0bbed..8967ee005ce 100644 --- a/docs/versioned_docs/version-v1.0.0-beta.0/tutorials/noirjs_app.md +++ b/docs/versioned_docs/version-v1.0.0-beta.0/tutorials/noirjs_app.md @@ -1,186 +1,91 @@ --- -title: Building a web app with NoirJS +title: Building a web app with Noir and Barretenberg 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, app] sidebar_position: 0 pagination_next: noir/concepts/data_types/index --- -NoirJS is a set of packages meant to work both in a browser and a server environment. In this tutorial, we will build a simple web app using them. From here, you should get an idea on how to proceed with your own Noir projects! +NoirJS is a Typescript package meant to work both in a browser and a server environment. -You can find the complete app code for this guide [here](https://github.com/noir-lang/tiny-noirjs-app). - -## Setup - -:::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.31.x matches `noir_js@0.31.x`, etc. - -In this guide, we will be pinned to 0.31.0. - -::: - -Before we start, we want to make sure we have Node, Nargo and the Barretenberg proving system (`bb`) installed. - -We start by opening a terminal and executing `node --version`. If we don't get an output like `v20.10.0`, that means node is not installed. Let's do that by following the handy [nvm guide](https://github.com/nvm-sh/nvm?tab=readme-ov-file#install--update-script). - -As for `Nargo`, we can follow the [Nargo guide](../getting_started/quick_start.md) to install it. If you're lazy, just paste this on a terminal and run `noirup`: - -```sh -curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash -``` +In this tutorial, we will combine NoirJS with Aztec's Barretenberg backend to build a simple web app. From here, you should get an idea on how to proceed with your own Noir projects! -Follow the instructions on [this page](https://github.com/AztecProtocol/aztec-packages/tree/master/barretenberg/cpp/src/barretenberg/bb#installation) to install `bb`. -Version 0.41.0 is compatible with `nargo` version 0.31.0, which you can install with `bbup -v 0.41.0` once `bbup` is installed. - -Easy enough. Onwards! - -## Our project - -ZK is a powerful technology. An app that doesn't reveal one of the inputs to _anyone_ is almost unbelievable, yet Noir makes it as easy as a single line of code. - -In fact, it's so simple that it comes nicely packaged in `nargo`. Let's do that! +You can find the complete app code for this guide [here](https://github.com/noir-lang/tiny-noirjs-app). -### Nargo +## Dependencies -Run: +Before we start, we want to make sure we have Node installed. For convenience (and speed), we can just install [Bun](https://bun.sh) as our package manager, and Node will work out-of-the-box: ```bash -nargo new circuit +curl -fsSL https://bun.sh/install | bash ``` -And... That's about it. Your program is ready to be compiled and run. +Let's go barebones. Doing the bare minimum is not only simple, but also allows you to easily adapt it to almost any frontend framework. -To compile, let's `cd` into the `circuit` folder to enter our project, and call: +Barebones means we can immediately start with the dependencies even on an empty folder 😈: ```bash -nargo compile +bun i @noir-lang/noir_wasm@1.0.0-beta.0 @noir-lang/noir_js@1.0.0-beta.0 @aztec/bb.js@0.63.1 ``` -This compiles our circuit into `json` format and add it to a new `target` folder. +Wait, what are these dependencies? -:::info +- `noir_wasm` is the `wasm` version of the Noir compiler. Although most developers prefer to use `nargo` for compiling, there's nothing wrong with `noir_wasm`. We like `noir_wasm`. +- `noir_js` is the main Noir package. It will execute our program, and generate the witness that will be sent to the backend. +- `bb.js` is the Typescript interface for Aztec's Barretenberg proving backend. It also uses the `wasm` version in order to run on the browser. -At this point in the tutorial, your folder structure should look like this: +:::info -```tree -. -└── circuit <---- our working directory - ├── Nargo.toml - ├── src - │ └── main.nr - └── target - └── circuit.json -``` +In this guide, we will install versions pinned to 1.0.0-beta.0. These work with Barretenberg version 0.63.1, so we are using that one version too. Feel free to try with older or later versions, though! ::: -### Node and Vite - -If you want to explore Nargo, feel free to go on a side-quest now and follow the steps in the -[getting started](../getting_started/quick_start.md) guide. However, we want our app to run on the browser, so we need Vite. +## Setting up our Noir program -Vite is a powerful tool to generate static websites. While it provides all kinds of features, let's just go barebones with some good old vanilla JS. +ZK is a powerful technology. An app that reveals computational correctness but doesn't reveal some of its inputs is almost unbelievable, yet Noir makes it as easy as a single line of code. -To do this this, go back to the previous folder (`cd ..`) and create a new vite project by running `npm create vite` and choosing "Vanilla" and "Javascript". +:::tip -A wild `vite-project` directory should now appear in your root folder! Let's not waste any time and dive right in: +It's not just you. We also enjoy syntax highlighting. [Check out the Language Server](../tooling/language_server.md) -```bash -cd vite-project -``` +::: -### Setting Up Vite and Configuring the Project - -Before we proceed with any coding, let's get our environment tailored for Noir. We'll start by laying down the foundations with a `vite.config.js` file. This little piece of configuration is our secret sauce for making sure everything meshes well with the NoirJS libraries and other special setups we might need, like handling WebAssembly modules. Here’s how you get that going: - -#### Creating the vite.config.js - -In your freshly minted `vite-project` folder, create a new file named `vite.config.js` and open it in your code editor. Paste the following to set the stage: - -```javascript -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(); - }); - }, -}; +All you need is a `main.nr` and a `Nargo.toml` file. You can follow the [noirup](../getting_started/noir_installation.md) installation and just run `noirup -v 1.0.0-beta.0`, or just create them by hand: -export default defineConfig(({ command }) => { - if (command === 'serve') { - return { - build: { - target: 'esnext', - rollupOptions: { - external: ['@aztec/bb.js'] - } - }, - optimizeDeps: { - esbuildOptions: { - target: 'esnext' - } - }, - plugins: [ - copy({ - targets: [{ src: 'node_modules/**/*.wasm', dest: 'node_modules/.vite/dist' }], - copySync: true, - hook: 'buildStart', - }), - command === 'serve' ? wasmContentTypePlugin : [], - ], - }; - } - - return {}; -}); +```bash +mkdir -p circuit/src +touch circuit/src/main.nr circuit/Nargo.toml ``` -#### Install Dependencies - -Now that our stage is set, install the necessary NoirJS packages along with our other dependencies: +To make our program interesting, let's give it a real use-case scenario: Bob wants to prove he is older than 18, without disclosing his age. Open `main.nr` and write: -```bash -npm install && npm install @noir-lang/backend_barretenberg@0.31.0 @noir-lang/noir_js@0.31.0 -npm install rollup-plugin-copy --save-dev +```rust +fn main(age: u8) { + assert(age >= 18); +} ``` -:::info - -At this point in the tutorial, your folder structure should look like this: +This program accepts a private input called age, and simply proves this number is higher than 18. But to run this code, we need to give the compiler a `Nargo.toml` with at least a name and a type: -```tree -. -└── circuit - └── ...etc... -└── vite-project <---- our working directory - └── ...etc... +```toml +[package] +name = "circuit" +type = "bin" ``` -::: +This is all that we need to get started with Noir. -#### Some cleanup +![my heart is ready for you, noir.js](@site/static/img/memes/titanic.jpeg) -`npx create vite` is amazing but it creates a bunch of files we don't really need for our simple example. Actually, let's just delete everything except for `vite.config.js`, `index.html`, `main.js` and `package.json`. I feel lighter already. +## Setting up our app -![my heart is ready for you, noir.js](@site/static/img/memes/titanic.jpeg) +Remember when apps only had one `html` and one `js` file? Well, that's enough for Noir webapps. Let's create them: -## HTML +```bash +touch index.html index.js +``` -Our app won't run like this, of course. We need some working HTML, at least. Let's open our broken-hearted `index.html` and replace everything with this code snippet: +And add something useful to our HTML file: ```html @@ -200,11 +105,11 @@ Our app won't run like this, of course. We need some working HTML, at least. Let - +

Noir app

- - + +

Logs

@@ -214,32 +119,26 @@ Our app won't run like this, of course. We need some working HTML, at least. Let ``` -It _could_ be a beautiful UI... Depending on which universe you live in. - -## Some good old vanilla Javascript - -Our love for Noir needs undivided attention, so let's just open `main.js` and delete everything (this is where the romantic scenery becomes a bit creepy). +It _could_ be a beautiful UI... Depending on which universe you live in. In any case, we're using some scary CSS to make two boxes that will show cool things on the screen. -Start by pasting in this boilerplate code: +As for the JS, real madmen could just `console.log` everything, but let's say we want to see things happening (the true initial purpose of JS... right?). Here's some boilerplate for that. Just paste it in `index.js`: ```js -function display(container, msg) { - const c = document.getElementById(container); - const p = document.createElement('p'); - p.textContent = msg; - c.appendChild(p); -} +const show = (id, content) => { + const container = document.getElementById(id); + container.appendChild(document.createTextNode(content)); + container.appendChild(document.createElement("br")); +}; -document.getElementById('submitGuess').addEventListener('click', async () => { - try { - // here's where love happens - } catch (err) { - display('logs', 'Oh 💔 Wrong guess'); - } +document.getElementById("submit").addEventListener("click", async () => { + try { + // noir goes here + } catch { + show("logs", "Oh 💔"); + } }); -``` -The display function doesn't do much. We're simply manipulating our website to see stuff happening. For example, if the proof fails, it will simply log a broken heart 😢 +``` :::info @@ -248,30 +147,56 @@ At this point in the tutorial, your folder structure should look like this: ```tree . └── circuit - └── ...same as above -└── vite-project - ├── vite.config.js - ├── main.js - ├── package.json - └── index.html + └── src + └── main.nr + Nargo.toml + index.js + package.json + index.html + ...etc ``` -You'll see other files and folders showing up (like `package-lock.json`, `node_modules`) but you shouldn't have to care about those. - ::: -## Some NoirJS +## Compile compile compile -We're starting with the good stuff now. If you've compiled the circuit as described above, you should have a `json` file we want to import at the very top of our `main.js` file: +Finally we're up for something cool. But before we can execute a Noir program, we need to compile it into ACIR: an abstract representation. Here's where `noir_wasm` comes in. -```ts -import circuit from '../circuit/target/circuit.json'; +`noir_wasm` expects a filesystem so it can resolve dependencies. While we could use the `public` folder, let's just import those using the nice `?url` syntax provided by vite. At the top of the file: + +```js +import { compile, createFileManager } from "@noir-lang/noir_wasm" + +import main from "./circuit/src/main.nr?url"; +import nargoToml from "./circuit/Nargo.toml?url"; ``` -[Noir is backend-agnostic](../index.mdx#whats-new-about-noir). We write Noir, but we also need a proving backend. That's why we need to import and instantiate the two dependencies we installed above: `BarretenbergBackend` and `Noir`. Let's import them right below: +Compiling on the browser is common enough that `createFileManager` already gives us a nice in-memory filesystem we can use. So all we need to compile is fetching these files, writing them to our filesystem, and compile. Add this function: ```js -import { BarretenbergBackend, BarretenbergVerifier as Verifier } from '@noir-lang/backend_barretenberg'; +export async function getCircuit() { + const fm = createFileManager("/"); + const { body } = await fetch(main); + const { body: nargoTomlBody } = await fetch(nargoToml); + + fm.writeFile("./src/main.nr", body); + fm.writeFile("./Nargo.toml", nargoTomlBody); + return await compile(fm); +} +``` + +:::tip + +As you can imagine, with `node` it's all conveniently easier since you get native access to `fs`... + +::: + +## Some more JS + +We're starting with the good stuff now. We want to execute our circuit to get the witness, and then feed that witness to Barretenberg. Luckily, both packages are quite easy to work with. Let's import them at the top of the file: + +```js +import { UltraHonkBackend } from '@aztec/bb.js'; import { Noir } from '@noir-lang/noir_js'; ``` @@ -279,88 +204,103 @@ And instantiate them inside our try-catch block: ```ts // try { -const backend = new BarretenbergBackend(circuit); -const noir = new Noir(circuit); +const { program } = await getCircuit(); +const noir = new Noir(program); +const backend = new UltraHonkBackend(program.bytecode); // } ``` -:::note +:::warning -For the remainder of the tutorial, everything will be happening inside the `try` block +WASMs are not always easy to work with. In our case, `vite` likes serving them with the wrong MIME type. There are different fixes but we found the easiest one is just YOLO instantiating the WASMs manually. Paste this at the top of the file, just below the other imports, and it will work just fine: + +```js +import initNoirC from "@noir-lang/noirc_abi"; +import initACVM from "@noir-lang/acvm_js"; +import acvm from "@noir-lang/acvm_js/web/acvm_js_bg.wasm?url"; +import noirc from "@noir-lang/noirc_abi/web/noirc_abi_wasm_bg.wasm?url"; +await Promise.all([initACVM(fetch(acvm)), initNoirC(fetch(noirc))]); +``` ::: -## Our app +## Executing and proving -Now for the app itself. We're capturing whatever is in the input when people press the submit button. Just add this: +Now for the app itself. We're capturing whatever is in the input when people press the submit button. Inside our `try` block, let's just grab that input and get its value. Noir will gladly execute it, and give us a witness: ```js -const x = parseInt(document.getElementById('guessInput').value); -const input = { x, y: 2 }; +const age = document.getElementById("age").value; +show("logs", "Generating witness... ⏳"); +const { witness } = await noir.execute({ age }); +show("logs", "Generated witness... ✅"); + ``` +:::note + +For the remainder of the tutorial, everything will be happening inside the `try` block + +::: + Now we're ready to prove stuff! Let's feed some inputs to our circuit and calculate the proof: ```js -await setup(); // let's squeeze our wasm inits here - -display('logs', 'Generating proof... ⌛'); -const { witness } = await noir.execute(input); +show("logs", "Generating proof... ⏳"); const proof = await backend.generateProof(witness); -display('logs', 'Generating proof... ✅'); -display('results', proof.proof); +show("logs", "Generated proof... ✅"); +show("results", proof.proof); ``` -You're probably eager to see stuff happening, so go and run your app now! +Our program is technically **done** . You're probably eager to see stuff happening! To serve this in a convenient way, we can use a bundler like `vite` by creating a `vite.config.js` file: + +```bash +touch vite.config.js +``` -From your terminal, run `npm run dev`. If it doesn't open a browser for you, just visit `localhost:5173`. You should now see the worst UI ever, with an ugly input. +`vite` helps us with a little catch: `bb.js` in particular uses top-level awaits which aren't supported everywhere. So we can add this to the `vite.config.js` to make the bundler optimize them: -![Getting Started 0](@site/static/img/noir_getting_started_1.png) +```js +export default { optimizeDeps: { esbuildOptions: { target: "esnext" } } }; +``` + +This should be enough for vite. We don't even need to install it, just run: + +```bash +bunx vite +``` -Now, our circuit says `fn main(x: Field, y: pub Field)`. This means only the `y` value is public, and it's hardcoded above: `input = { x, y: 2 }`. In other words, you won't need to send your secret`x` to the verifier! +If it doesn't open a browser for you, just visit `localhost:5173`. You should now see the worst UI ever, with an ugly input. -By inputting any number other than 2 in the input box and clicking "submit", you should get a valid proof. Otherwise the proof won't even generate correctly. By the way, if you're human, you shouldn't be able to understand anything on the "proof" box. That's OK. We like you, human ❤️. +![Noir Webapp UI](@site/static/img/tutorials/noirjs_webapp/webapp1.png) + +Now, our circuit requires a private input `fn main(age: u8)`, and fails if it is less than 18. Let's see if it works. Submit any number above 18 (as long as it fits in 8 bits) and you should get a valid proof. Otherwise the proof won't even generate correctly. + +By the way, if you're human, you shouldn't be able to understand anything on the "proof" box. That's OK. We like you, human ❤️. ## Verifying Time to celebrate, yes! But we shouldn't trust machines so blindly. Let's add these lines to see our proof being verified: ```js -display('logs', 'Verifying proof... ⌛'); +show('logs', 'Verifying proof... ⌛'); const isValid = await backend.verifyProof(proof); - -// or to cache and use the verification key: -// const verificationKey = await backend.getVerificationKey(); -// const verifier = new Verifier(); -// const isValid = await verifier.verifyProof(proof, verificationKey); - -if (isValid) display('logs', 'Verifying proof... ✅'); +show("logs", `Proof is ${isValid ? "valid" : "invalid"}... ✅`); ``` You have successfully generated a client-side Noir web app! ![coded app without math knowledge](@site/static/img/memes/flextape.jpeg) -## 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/vite-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. +## Next steps -## UltraHonk Backend +At this point, you have a working ZK app that works on the browser. Actually, it works on a mobile phone too! -Barretenberg has recently exposed a new UltraHonk backend. We can use UltraHonk in NoirJS after version 0.33.0. Everything will be the same as the tutorial above, except that the class we need to import will change: +If you want to continue learning by doing, here are some challenges for you: -```js -import { UltraHonkBackend, UltraHonkVerifier as Verifier } from '@noir-lang/backend_barretenberg'; -``` - -The backend will then be instantiated as such: - -```js -const backend = new UltraHonkBackend(circuit); -``` +- Install [nargo](https://noir-lang.org/docs/getting_started/noir_installation) and write [Noir tests](../tooling/testing) +- Change the circuit to accept a [public input](../noir/concepts/data_types/#private--public-types) as the cutoff age. It could be different depending on the purpose, for example! +- Enjoy Noir's Rust-like syntax and write a struct `Country` that implements a trait `MinAge` with a method `get_min_age`. Then, make a struct `Person` have an `u8` as its age and a country of type `Country`. You can pass a `person` in JS just like a JSON object `person: { age, country: { min_age: 18 }}` -Then all the commands to prove and verify your circuit will be same. +The world is your stage, just have fun with ZK! 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/vite-hardhat). The example shows how to calculate a proof in the browser and verify it with a deployed Solidity verifier contract from noirjs. -The only feature currently unsupported with UltraHonk are [recursive proofs](../explainers/explainer-recursion.md). +Check out other starters, tools, or just cool projects in the [awesome noir repository](https://github.com/noir-lang/awesome-noir). diff --git a/docs/versioned_sidebars/version-v0.32.0-sidebars.json b/docs/versioned_sidebars/version-v0.32.0-sidebars.json deleted file mode 100644 index b9ad026f69f..00000000000 --- a/docs/versioned_sidebars/version-v0.32.0-sidebars.json +++ /dev/null @@ -1,93 +0,0 @@ -{ - "sidebar": [ - { - "type": "doc", - "id": "index" - }, - { - "type": "category", - "label": "Getting Started", - "items": [ - { - "type": "autogenerated", - "dirName": "getting_started" - } - ] - }, - { - "type": "category", - "label": "The Noir Language", - "items": [ - { - "type": "autogenerated", - "dirName": "noir" - } - ] - }, - { - "type": "html", - "value": "
", - "defaultStyle": true - }, - { - "type": "category", - "label": "How To Guides", - "items": [ - { - "type": "autogenerated", - "dirName": "how_to" - } - ] - }, - { - "type": "category", - "label": "Explainers", - "items": [ - { - "type": "autogenerated", - "dirName": "explainers" - } - ] - }, - { - "type": "category", - "label": "Tutorials", - "items": [ - { - "type": "autogenerated", - "dirName": "tutorials" - } - ] - }, - { - "type": "category", - "label": "Reference", - "items": [ - { - "type": "autogenerated", - "dirName": "reference" - } - ] - }, - { - "type": "category", - "label": "Tooling", - "items": [ - { - "type": "autogenerated", - "dirName": "tooling" - } - ] - }, - { - "type": "html", - "value": "
", - "defaultStyle": true - }, - { - "type": "doc", - "id": "migration_notes", - "label": "Migration notes" - } - ] -} diff --git a/docs/versioned_sidebars/version-v0.33.0-sidebars.json b/docs/versioned_sidebars/version-v0.33.0-sidebars.json deleted file mode 100644 index b9ad026f69f..00000000000 --- a/docs/versioned_sidebars/version-v0.33.0-sidebars.json +++ /dev/null @@ -1,93 +0,0 @@ -{ - "sidebar": [ - { - "type": "doc", - "id": "index" - }, - { - "type": "category", - "label": "Getting Started", - "items": [ - { - "type": "autogenerated", - "dirName": "getting_started" - } - ] - }, - { - "type": "category", - "label": "The Noir Language", - "items": [ - { - "type": "autogenerated", - "dirName": "noir" - } - ] - }, - { - "type": "html", - "value": "
", - "defaultStyle": true - }, - { - "type": "category", - "label": "How To Guides", - "items": [ - { - "type": "autogenerated", - "dirName": "how_to" - } - ] - }, - { - "type": "category", - "label": "Explainers", - "items": [ - { - "type": "autogenerated", - "dirName": "explainers" - } - ] - }, - { - "type": "category", - "label": "Tutorials", - "items": [ - { - "type": "autogenerated", - "dirName": "tutorials" - } - ] - }, - { - "type": "category", - "label": "Reference", - "items": [ - { - "type": "autogenerated", - "dirName": "reference" - } - ] - }, - { - "type": "category", - "label": "Tooling", - "items": [ - { - "type": "autogenerated", - "dirName": "tooling" - } - ] - }, - { - "type": "html", - "value": "
", - "defaultStyle": true - }, - { - "type": "doc", - "id": "migration_notes", - "label": "Migration notes" - } - ] -} diff --git a/docs/versioned_sidebars/version-v0.34.0-sidebars.json b/docs/versioned_sidebars/version-v0.34.0-sidebars.json deleted file mode 100644 index b9ad026f69f..00000000000 --- a/docs/versioned_sidebars/version-v0.34.0-sidebars.json +++ /dev/null @@ -1,93 +0,0 @@ -{ - "sidebar": [ - { - "type": "doc", - "id": "index" - }, - { - "type": "category", - "label": "Getting Started", - "items": [ - { - "type": "autogenerated", - "dirName": "getting_started" - } - ] - }, - { - "type": "category", - "label": "The Noir Language", - "items": [ - { - "type": "autogenerated", - "dirName": "noir" - } - ] - }, - { - "type": "html", - "value": "
", - "defaultStyle": true - }, - { - "type": "category", - "label": "How To Guides", - "items": [ - { - "type": "autogenerated", - "dirName": "how_to" - } - ] - }, - { - "type": "category", - "label": "Explainers", - "items": [ - { - "type": "autogenerated", - "dirName": "explainers" - } - ] - }, - { - "type": "category", - "label": "Tutorials", - "items": [ - { - "type": "autogenerated", - "dirName": "tutorials" - } - ] - }, - { - "type": "category", - "label": "Reference", - "items": [ - { - "type": "autogenerated", - "dirName": "reference" - } - ] - }, - { - "type": "category", - "label": "Tooling", - "items": [ - { - "type": "autogenerated", - "dirName": "tooling" - } - ] - }, - { - "type": "html", - "value": "
", - "defaultStyle": true - }, - { - "type": "doc", - "id": "migration_notes", - "label": "Migration notes" - } - ] -} diff --git a/docs/versioned_sidebars/version-v0.35.0-sidebars.json b/docs/versioned_sidebars/version-v0.35.0-sidebars.json deleted file mode 100644 index b9ad026f69f..00000000000 --- a/docs/versioned_sidebars/version-v0.35.0-sidebars.json +++ /dev/null @@ -1,93 +0,0 @@ -{ - "sidebar": [ - { - "type": "doc", - "id": "index" - }, - { - "type": "category", - "label": "Getting Started", - "items": [ - { - "type": "autogenerated", - "dirName": "getting_started" - } - ] - }, - { - "type": "category", - "label": "The Noir Language", - "items": [ - { - "type": "autogenerated", - "dirName": "noir" - } - ] - }, - { - "type": "html", - "value": "
", - "defaultStyle": true - }, - { - "type": "category", - "label": "How To Guides", - "items": [ - { - "type": "autogenerated", - "dirName": "how_to" - } - ] - }, - { - "type": "category", - "label": "Explainers", - "items": [ - { - "type": "autogenerated", - "dirName": "explainers" - } - ] - }, - { - "type": "category", - "label": "Tutorials", - "items": [ - { - "type": "autogenerated", - "dirName": "tutorials" - } - ] - }, - { - "type": "category", - "label": "Reference", - "items": [ - { - "type": "autogenerated", - "dirName": "reference" - } - ] - }, - { - "type": "category", - "label": "Tooling", - "items": [ - { - "type": "autogenerated", - "dirName": "tooling" - } - ] - }, - { - "type": "html", - "value": "
", - "defaultStyle": true - }, - { - "type": "doc", - "id": "migration_notes", - "label": "Migration notes" - } - ] -} diff --git a/docs/versioned_sidebars/version-v0.37.0-sidebars.json b/docs/versioned_sidebars/version-v0.37.0-sidebars.json deleted file mode 100644 index b9ad026f69f..00000000000 --- a/docs/versioned_sidebars/version-v0.37.0-sidebars.json +++ /dev/null @@ -1,93 +0,0 @@ -{ - "sidebar": [ - { - "type": "doc", - "id": "index" - }, - { - "type": "category", - "label": "Getting Started", - "items": [ - { - "type": "autogenerated", - "dirName": "getting_started" - } - ] - }, - { - "type": "category", - "label": "The Noir Language", - "items": [ - { - "type": "autogenerated", - "dirName": "noir" - } - ] - }, - { - "type": "html", - "value": "
", - "defaultStyle": true - }, - { - "type": "category", - "label": "How To Guides", - "items": [ - { - "type": "autogenerated", - "dirName": "how_to" - } - ] - }, - { - "type": "category", - "label": "Explainers", - "items": [ - { - "type": "autogenerated", - "dirName": "explainers" - } - ] - }, - { - "type": "category", - "label": "Tutorials", - "items": [ - { - "type": "autogenerated", - "dirName": "tutorials" - } - ] - }, - { - "type": "category", - "label": "Reference", - "items": [ - { - "type": "autogenerated", - "dirName": "reference" - } - ] - }, - { - "type": "category", - "label": "Tooling", - "items": [ - { - "type": "autogenerated", - "dirName": "tooling" - } - ] - }, - { - "type": "html", - "value": "
", - "defaultStyle": true - }, - { - "type": "doc", - "id": "migration_notes", - "label": "Migration notes" - } - ] -} diff --git a/docs/versioned_sidebars/version-v0.38.0-sidebars.json b/docs/versioned_sidebars/version-v0.38.0-sidebars.json deleted file mode 100644 index b9ad026f69f..00000000000 --- a/docs/versioned_sidebars/version-v0.38.0-sidebars.json +++ /dev/null @@ -1,93 +0,0 @@ -{ - "sidebar": [ - { - "type": "doc", - "id": "index" - }, - { - "type": "category", - "label": "Getting Started", - "items": [ - { - "type": "autogenerated", - "dirName": "getting_started" - } - ] - }, - { - "type": "category", - "label": "The Noir Language", - "items": [ - { - "type": "autogenerated", - "dirName": "noir" - } - ] - }, - { - "type": "html", - "value": "
", - "defaultStyle": true - }, - { - "type": "category", - "label": "How To Guides", - "items": [ - { - "type": "autogenerated", - "dirName": "how_to" - } - ] - }, - { - "type": "category", - "label": "Explainers", - "items": [ - { - "type": "autogenerated", - "dirName": "explainers" - } - ] - }, - { - "type": "category", - "label": "Tutorials", - "items": [ - { - "type": "autogenerated", - "dirName": "tutorials" - } - ] - }, - { - "type": "category", - "label": "Reference", - "items": [ - { - "type": "autogenerated", - "dirName": "reference" - } - ] - }, - { - "type": "category", - "label": "Tooling", - "items": [ - { - "type": "autogenerated", - "dirName": "tooling" - } - ] - }, - { - "type": "html", - "value": "
", - "defaultStyle": true - }, - { - "type": "doc", - "id": "migration_notes", - "label": "Migration notes" - } - ] -} diff --git a/docs/versioned_sidebars/version-v0.39.0-sidebars.json b/docs/versioned_sidebars/version-v0.39.0-sidebars.json deleted file mode 100644 index b9ad026f69f..00000000000 --- a/docs/versioned_sidebars/version-v0.39.0-sidebars.json +++ /dev/null @@ -1,93 +0,0 @@ -{ - "sidebar": [ - { - "type": "doc", - "id": "index" - }, - { - "type": "category", - "label": "Getting Started", - "items": [ - { - "type": "autogenerated", - "dirName": "getting_started" - } - ] - }, - { - "type": "category", - "label": "The Noir Language", - "items": [ - { - "type": "autogenerated", - "dirName": "noir" - } - ] - }, - { - "type": "html", - "value": "
", - "defaultStyle": true - }, - { - "type": "category", - "label": "How To Guides", - "items": [ - { - "type": "autogenerated", - "dirName": "how_to" - } - ] - }, - { - "type": "category", - "label": "Explainers", - "items": [ - { - "type": "autogenerated", - "dirName": "explainers" - } - ] - }, - { - "type": "category", - "label": "Tutorials", - "items": [ - { - "type": "autogenerated", - "dirName": "tutorials" - } - ] - }, - { - "type": "category", - "label": "Reference", - "items": [ - { - "type": "autogenerated", - "dirName": "reference" - } - ] - }, - { - "type": "category", - "label": "Tooling", - "items": [ - { - "type": "autogenerated", - "dirName": "tooling" - } - ] - }, - { - "type": "html", - "value": "
", - "defaultStyle": true - }, - { - "type": "doc", - "id": "migration_notes", - "label": "Migration notes" - } - ] -} diff --git a/noir_stdlib/src/array/mod.nr b/noir_stdlib/src/array/mod.nr index 47dc3ca7bb9..85cc0580aae 100644 --- a/noir_stdlib/src/array/mod.nr +++ b/noir_stdlib/src/array/mod.nr @@ -157,7 +157,7 @@ where /// } /// ``` pub fn sort(self) -> Self { - self.sort_via(|a: T, b: T| a <= b) + self.sort_via(|a, b| a <= b) } } diff --git a/noir_stdlib/src/cmp.nr b/noir_stdlib/src/cmp.nr index ae515150a4d..7f19594c30e 100644 --- a/noir_stdlib/src/cmp.nr +++ b/noir_stdlib/src/cmp.nr @@ -12,7 +12,7 @@ comptime fn derive_eq(s: StructDefinition) -> Quoted { let signature = quote { fn eq(_self: Self, _other: Self) -> bool }; let for_each_field = |name| quote { (_self.$name == _other.$name) }; let body = |fields| { - if s.fields().len() == 0 { + if s.fields_as_written().len() == 0 { quote { true } } else { fields diff --git a/noir_stdlib/src/collections/bounded_vec.nr b/noir_stdlib/src/collections/bounded_vec.nr index 7aed5e6a0e4..a1befdd58ec 100644 --- a/noir_stdlib/src/collections/bounded_vec.nr +++ b/noir_stdlib/src/collections/bounded_vec.nr @@ -1,4 +1,4 @@ -use crate::{cmp::Eq, convert::From}; +use crate::{cmp::Eq, convert::From, runtime::is_unconstrained}; /// A `BoundedVec` is a growable storage similar to a `Vec` except that it /// is bounded with a maximum possible length. Unlike `Vec`, `BoundedVec` is not implemented @@ -320,12 +320,18 @@ impl BoundedVec { let new_len = self.len + append_len; assert(new_len <= MaxLen, "extend_from_bounded_vec out of bounds"); - let mut exceeded_len = false; - for i in 0..Len { - exceeded_len |= i == append_len; - if !exceeded_len { + if is_unconstrained() { + for i in 0..append_len { self.storage[self.len + i] = vec.get_unchecked(i); } + } else { + let mut exceeded_len = false; + for i in 0..Len { + exceeded_len |= i == append_len; + if !exceeded_len { + self.storage[self.len + i] = vec.get_unchecked(i); + } + } } self.len = new_len; } @@ -389,12 +395,19 @@ impl BoundedVec { /// ``` pub fn any(self, predicate: fn[Env](T) -> bool) -> bool { let mut ret = false; - let mut exceeded_len = false; - for i in 0..MaxLen { - exceeded_len |= i == self.len; - if !exceeded_len { + if is_unconstrained() { + for i in 0..self.len { ret |= predicate(self.storage[i]); } + } else { + let mut ret = false; + let mut exceeded_len = false; + for i in 0..MaxLen { + exceeded_len |= i == self.len; + if !exceeded_len { + ret |= predicate(self.storage[i]); + } + } } ret } @@ -413,11 +426,19 @@ impl BoundedVec { pub fn map(self, f: fn[Env](T) -> U) -> BoundedVec { let mut ret = BoundedVec::new(); ret.len = self.len(); - for i in 0..MaxLen { - if i < self.len() { + + if is_unconstrained() { + for i in 0..self.len() { ret.storage[i] = f(self.get_unchecked(i)); } + } else { + for i in 0..MaxLen { + if i < self.len() { + ret.storage[i] = f(self.get_unchecked(i)); + } + } } + ret } @@ -437,11 +458,19 @@ impl BoundedVec { pub fn from_parts(mut array: [T; MaxLen], len: u32) -> Self { assert(len <= MaxLen); let zeroed = crate::mem::zeroed(); - for i in 0..MaxLen { - if i >= len { + + if is_unconstrained() { + for i in len..MaxLen { array[i] = zeroed; } + } else { + for i in 0..MaxLen { + if i >= len { + array[i] = zeroed; + } + } } + BoundedVec { storage: array, len } } diff --git a/noir_stdlib/src/collections/umap.nr b/noir_stdlib/src/collections/umap.nr index fb15d532bc5..bcb9759b4db 100644 --- a/noir_stdlib/src/collections/umap.nr +++ b/noir_stdlib/src/collections/umap.nr @@ -114,8 +114,7 @@ impl UHashMap { { // docs:end:contains_key /// Safety: unconstrained context - unsafe { self.get(key) } - .is_some() + unsafe { self.get(key) }.is_some() } // Returns true if the map contains no elements. diff --git a/noir_stdlib/src/lib.nr b/noir_stdlib/src/lib.nr index 2e743822ffb..fb073516d29 100644 --- a/noir_stdlib/src/lib.nr +++ b/noir_stdlib/src/lib.nr @@ -120,3 +120,10 @@ where #[builtin(as_witness)] pub fn as_witness(x: Field) {} + +mod tests { + #[test(should_fail_with = "custom message")] + fn test_static_assert_custom_message() { + super::static_assert(1 == 2, "custom message"); + } +} diff --git a/noir_stdlib/src/meta/expr.nr b/noir_stdlib/src/meta/expr.nr index 7538b26dc44..a1663135c20 100644 --- a/noir_stdlib/src/meta/expr.nr +++ b/noir_stdlib/src/meta/expr.nr @@ -285,33 +285,31 @@ impl Expr { } comptime fn modify_array(expr: Expr, f: fn[Env](Expr) -> Option) -> Option { - expr.as_array().map(|exprs: [Expr]| { + expr.as_array().map(|exprs| { let exprs = modify_expressions(exprs, f); new_array(exprs) }) } comptime fn modify_assert(expr: Expr, f: fn[Env](Expr) -> Option) -> Option { - expr.as_assert().map(|expr: (Expr, Option)| { - let (predicate, msg) = expr; + expr.as_assert().map(|(predicate, msg)| { let predicate = predicate.modify(f); - let msg = msg.map(|msg: Expr| msg.modify(f)); + let msg = msg.map(|msg| msg.modify(f)); new_assert(predicate, msg) }) } comptime fn modify_assert_eq(expr: Expr, f: fn[Env](Expr) -> Option) -> Option { - expr.as_assert_eq().map(|expr: (Expr, Expr, Option)| { - let (lhs, rhs, msg) = expr; + expr.as_assert_eq().map(|(lhs, rhs, msg)| { let lhs = lhs.modify(f); let rhs = rhs.modify(f); - let msg = msg.map(|msg: Expr| msg.modify(f)); + let msg = msg.map(|msg| msg.modify(f)); new_assert_eq(lhs, rhs, msg) }) } comptime fn modify_assign(expr: Expr, f: fn[Env](Expr) -> Option) -> Option { - expr.as_assign().map(|expr: (Expr, Expr)| { + expr.as_assign().map(|expr| { let (lhs, rhs) = expr; let lhs = lhs.modify(f); let rhs = rhs.modify(f); @@ -320,8 +318,7 @@ comptime fn modify_assign(expr: Expr, f: fn[Env](Expr) -> Option) -> } comptime fn modify_binary_op(expr: Expr, f: fn[Env](Expr) -> Option) -> Option { - expr.as_binary_op().map(|expr: (Expr, BinaryOp, Expr)| { - let (lhs, op, rhs) = expr; + expr.as_binary_op().map(|(lhs, op, rhs)| { let lhs = lhs.modify(f); let rhs = rhs.modify(f); new_binary_op(lhs, op, rhs) @@ -329,34 +326,29 @@ comptime fn modify_binary_op(expr: Expr, f: fn[Env](Expr) -> Option) } comptime fn modify_block(expr: Expr, f: fn[Env](Expr) -> Option) -> Option { - expr.as_block().map(|exprs: [Expr]| { + expr.as_block().map(|exprs| { let exprs = modify_expressions(exprs, f); new_block(exprs) }) } comptime fn modify_cast(expr: Expr, f: fn[Env](Expr) -> Option) -> Option { - expr.as_cast().map(|expr: (Expr, UnresolvedType)| { - let (expr, typ) = expr; + expr.as_cast().map(|(expr, typ)| { let expr = expr.modify(f); new_cast(expr, typ) }) } comptime fn modify_comptime(expr: Expr, f: fn[Env](Expr) -> Option) -> Option { - expr.as_comptime().map(|exprs: [Expr]| { - let exprs = exprs.map(|expr: Expr| expr.modify(f)); + expr.as_comptime().map(|exprs| { + let exprs = exprs.map(|expr| expr.modify(f)); new_comptime(exprs) }) } comptime fn modify_constructor(expr: Expr, f: fn[Env](Expr) -> Option) -> Option { - expr.as_constructor().map(|expr: (UnresolvedType, [(Quoted, Expr)])| { - let (typ, fields) = expr; - let fields = fields.map(|field: (Quoted, Expr)| { - let (name, value) = field; - (name, value.modify(f)) - }); + expr.as_constructor().map(|(typ, fields)| { + let fields = fields.map(|(name, value)| (name, value.modify(f))); new_constructor(typ, fields) }) } @@ -365,27 +357,24 @@ comptime fn modify_function_call( expr: Expr, f: fn[Env](Expr) -> Option, ) -> Option { - expr.as_function_call().map(|expr: (Expr, [Expr])| { - let (function, arguments) = expr; + expr.as_function_call().map(|(function, arguments)| { let function = function.modify(f); - let arguments = arguments.map(|arg: Expr| arg.modify(f)); + let arguments = arguments.map(|arg| arg.modify(f)); new_function_call(function, arguments) }) } comptime fn modify_if(expr: Expr, f: fn[Env](Expr) -> Option) -> Option { - expr.as_if().map(|expr: (Expr, Expr, Option)| { - let (condition, consequence, alternative) = expr; + expr.as_if().map(|(condition, consequence, alternative)| { let condition = condition.modify(f); let consequence = consequence.modify(f); - let alternative = alternative.map(|alternative: Expr| alternative.modify(f)); + let alternative = alternative.map(|alternative| alternative.modify(f)); new_if(condition, consequence, alternative) }) } comptime fn modify_index(expr: Expr, f: fn[Env](Expr) -> Option) -> Option { - expr.as_index().map(|expr: (Expr, Expr)| { - let (object, index) = expr; + expr.as_index().map(|(object, index)| { let object = object.modify(f); let index = index.modify(f); new_index(object, index) @@ -393,8 +382,7 @@ comptime fn modify_index(expr: Expr, f: fn[Env](Expr) -> Option) -> O } comptime fn modify_for(expr: Expr, f: fn[Env](Expr) -> Option) -> Option { - expr.as_for().map(|expr: (Quoted, Expr, Expr)| { - let (identifier, array, body) = expr; + expr.as_for().map(|(identifier, array, body)| { let array = array.modify(f); let body = body.modify(f); new_for(identifier, array, body) @@ -402,8 +390,7 @@ comptime fn modify_for(expr: Expr, f: fn[Env](Expr) -> Option) -> Opt } comptime fn modify_for_range(expr: Expr, f: fn[Env](Expr) -> Option) -> Option { - expr.as_for_range().map(|expr: (Quoted, Expr, Expr, Expr)| { - let (identifier, from, to, body) = expr; + expr.as_for_range().map(|(identifier, from, to, body)| { let from = from.modify(f); let to = to.modify(f); let body = body.modify(f); @@ -412,18 +399,15 @@ comptime fn modify_for_range(expr: Expr, f: fn[Env](Expr) -> Option) } comptime fn modify_lambda(expr: Expr, f: fn[Env](Expr) -> Option) -> Option { - expr.as_lambda().map(|expr: ([(Expr, Option)], Option, Expr)| { - let (params, return_type, body) = expr; - let params = - params.map(|param: (Expr, Option)| (param.0.modify(f), param.1)); + expr.as_lambda().map(|(params, return_type, body)| { + let params = params.map(|(name, typ)| (name.modify(f), typ)); let body = body.modify(f); new_lambda(params, return_type, body) }) } comptime fn modify_let(expr: Expr, f: fn[Env](Expr) -> Option) -> Option { - expr.as_let().map(|expr: (Expr, Option, Expr)| { - let (pattern, typ, expr) = expr; + expr.as_let().map(|(pattern, typ, expr)| { let pattern = pattern.modify(f); let expr = expr.modify(f); new_let(pattern, typ, expr) @@ -434,18 +418,16 @@ comptime fn modify_member_access( expr: Expr, f: fn[Env](Expr) -> Option, ) -> Option { - expr.as_member_access().map(|expr: (Expr, Quoted)| { - let (object, name) = expr; + expr.as_member_access().map(|(object, name)| { let object = object.modify(f); new_member_access(object, name) }) } comptime fn modify_method_call(expr: Expr, f: fn[Env](Expr) -> Option) -> Option { - expr.as_method_call().map(|expr: (Expr, Quoted, [UnresolvedType], [Expr])| { - let (object, name, generics, arguments) = expr; + expr.as_method_call().map(|(object, name, generics, arguments)| { let object = object.modify(f); - let arguments = arguments.map(|arg: Expr| arg.modify(f)); + let arguments = arguments.map(|arg| arg.modify(f)); new_method_call(object, name, generics, arguments) }) } @@ -454,8 +436,7 @@ comptime fn modify_repeated_element_array( expr: Expr, f: fn[Env](Expr) -> Option, ) -> Option { - expr.as_repeated_element_array().map(|expr: (Expr, Expr)| { - let (expr, length) = expr; + expr.as_repeated_element_array().map(|(expr, length)| { let expr = expr.modify(f); let length = length.modify(f); new_repeated_element_array(expr, length) @@ -466,8 +447,7 @@ comptime fn modify_repeated_element_slice( expr: Expr, f: fn[Env](Expr) -> Option, ) -> Option { - expr.as_repeated_element_slice().map(|expr: (Expr, Expr)| { - let (expr, length) = expr; + expr.as_repeated_element_slice().map(|(expr, length)| { let expr = expr.modify(f); let length = length.modify(f); new_repeated_element_slice(expr, length) @@ -475,36 +455,35 @@ comptime fn modify_repeated_element_slice( } comptime fn modify_slice(expr: Expr, f: fn[Env](Expr) -> Option) -> Option { - expr.as_slice().map(|exprs: [Expr]| { + expr.as_slice().map(|exprs| { let exprs = modify_expressions(exprs, f); new_slice(exprs) }) } comptime fn modify_tuple(expr: Expr, f: fn[Env](Expr) -> Option) -> Option { - expr.as_tuple().map(|exprs: [Expr]| { + expr.as_tuple().map(|exprs| { let exprs = modify_expressions(exprs, f); new_tuple(exprs) }) } comptime fn modify_unary_op(expr: Expr, f: fn[Env](Expr) -> Option) -> Option { - expr.as_unary_op().map(|expr: (UnaryOp, Expr)| { - let (op, rhs) = expr; + expr.as_unary_op().map(|(op, rhs)| { let rhs = rhs.modify(f); new_unary_op(op, rhs) }) } comptime fn modify_unsafe(expr: Expr, f: fn[Env](Expr) -> Option) -> Option { - expr.as_unsafe().map(|exprs: [Expr]| { - let exprs = exprs.map(|expr: Expr| expr.modify(f)); + expr.as_unsafe().map(|exprs| { + let exprs = exprs.map(|expr| expr.modify(f)); new_unsafe(exprs) }) } comptime fn modify_expressions(exprs: [Expr], f: fn[Env](Expr) -> Option) -> [Expr] { - exprs.map(|expr: Expr| expr.modify(f)) + exprs.map(|expr| expr.modify(f)) } comptime fn new_array(exprs: [Expr]) -> Expr { @@ -554,12 +533,7 @@ comptime fn new_comptime(exprs: [Expr]) -> Expr { } comptime fn new_constructor(typ: UnresolvedType, fields: [(Quoted, Expr)]) -> Expr { - let fields = fields - .map(|field: (Quoted, Expr)| { - let (name, value) = field; - quote { $name: $value } - }) - .join(quote { , }); + let fields = fields.map(|(name, value)| quote { $name: $value }).join(quote { , }); quote { $typ { $fields }}.as_expr().unwrap() } @@ -590,8 +564,7 @@ comptime fn new_lambda( body: Expr, ) -> Expr { let params = params - .map(|param: (Expr, Option)| { - let (name, typ) = param; + .map(|(name, typ)| { if typ.is_some() { let typ = typ.unwrap(); quote { $name: $typ } @@ -678,5 +651,5 @@ comptime fn new_unsafe(exprs: [Expr]) -> Expr { } comptime fn join_expressions(exprs: [Expr], separator: Quoted) -> Quoted { - exprs.map(|expr: Expr| expr.quoted()).join(separator) + exprs.map(|expr| expr.quoted()).join(separator) } diff --git a/noir_stdlib/src/meta/mod.nr b/noir_stdlib/src/meta/mod.nr index f6a1f4838ee..35ba05ba74d 100644 --- a/noir_stdlib/src/meta/mod.nr +++ b/noir_stdlib/src/meta/mod.nr @@ -97,14 +97,22 @@ pub comptime fn make_trait_impl( ) -> Quoted { // docs:end:make_trait_impl let typ = s.as_type(); - let impl_generics = s.generics().map(|g| quote { $g }).join(quote {,}); - let where_clause = s.generics().map(|name| quote { $name: $trait_name }).join(quote {,}); + + let mut impl_generics = &[]; + let mut where_clause = &[]; + for g in s.generics() { + let (typ, numeric_type) = g; + impl_generics = impl_generics.push_back(quote { $typ }); + if numeric_type.is_none() { + where_clause = where_clause.push_back(quote { $typ: $trait_name }); + } + } + + let impl_generics = impl_generics.join(quote {, }); + let where_clause = where_clause.join(quote {, }); // `for_each_field(field1) $join_fields_with for_each_field(field2) $join_fields_with ...` - let fields = s.fields().map(|f: (Quoted, Type)| { - let name = f.0; - for_each_field(name) - }); + let fields = s.fields_as_written().map(|(name, _)| for_each_field(name)); let body = body(fields.join(join_fields_with)); quote { @@ -155,7 +163,7 @@ mod tests { comptime fn derive_field_count(s: StructDefinition) -> Quoted { let typ = s.as_type(); - let field_count = s.fields().len(); + let field_count = s.fields_as_written().len(); quote { impl FieldCount for $typ { fn field_count() -> u32 { @@ -174,7 +182,7 @@ mod tests { comptime fn assert_field_is_type(s: StructDefinition, typ: Type) { // Assert the first field in `s` has type `typ` - let fields = s.fields(); + let fields = s.fields([]); assert_eq(fields[0].1, typ); } // docs:end:annotation-arguments-example diff --git a/noir_stdlib/src/meta/struct_def.nr b/noir_stdlib/src/meta/struct_def.nr index ba5d0289e73..d561e326fbd 100644 --- a/noir_stdlib/src/meta/struct_def.nr +++ b/noir_stdlib/src/meta/struct_def.nr @@ -1,3 +1,5 @@ +use crate::option::Option; + impl StructDefinition { #[builtin(struct_def_add_attribute)] // docs:start:add_attribute @@ -21,19 +23,30 @@ impl StructDefinition { pub comptime fn has_named_attribute(self, name: str) -> bool {} // docs:end:has_named_attribute - /// Return each generic on this struct. + /// Return (type, option) pairs of each generic in this struct definition. + /// If a generic is numeric, the second element of the pair will contain the numeric type. #[builtin(struct_def_generics)] // docs:start:generics - pub comptime fn generics(self) -> [Type] {} + pub comptime fn generics(self) -> [(Type, Option)] {} // docs:end:generics - /// Returns (name, type) pairs of each field in this struct. Each type is as-is - /// with any generic arguments unchanged. + /// Returns (name, type) pairs of each field in this struct. + /// Any generic types used in each field type is automatically substituted with the + /// provided generic arguments. #[builtin(struct_def_fields)] // docs:start:fields - pub comptime fn fields(self) -> [(Quoted, Type)] {} + pub comptime fn fields(self, generic_args: [Type]) -> [(Quoted, Type)] {} // docs:end:fields + /// Returns (name, type) pairs of each field in this struct. Each type is as-is + /// with any generic arguments unchanged. Unless the field types are not needed, + /// users should generally prefer to use `StructDefinition::fields` over this + /// function if possible. + #[builtin(struct_def_fields_as_written)] + // docs:start:fields_as_written + pub comptime fn fields_as_written(self) -> [(Quoted, Type)] {} + // docs:end:fields_as_written + #[builtin(struct_def_module)] // docs:start:module pub comptime fn module(self) -> Module {} diff --git a/test_programs/compilation_report.sh b/test_programs/compilation_report.sh index df15ef00008..786dbd75fe8 100755 --- a/test_programs/compilation_report.sh +++ b/test_programs/compilation_report.sh @@ -18,6 +18,7 @@ fi ITER="1" NUM_ARTIFACTS=${#tests_to_profile[@]} +FLAGS=${FLAGS:- ""} for dir in ${tests_to_profile[@]}; do if [[ " ${excluded_dirs[@]} " =~ " ${dir} " ]]; then @@ -37,17 +38,11 @@ for dir in ${tests_to_profile[@]}; do PACKAGE_NAME=$(basename $current_dir) fi - NUM_RUNS=1 + NUM_RUNS=$2 TOTAL_TIME=0 - # Passing a second argument will take an average of five runs - # rather than - if [ "$2" == "1" ]; then - NUM_RUNS=5 - fi - for ((i = 1; i <= NUM_RUNS; i++)); do - NOIR_LOG=trace NARGO_LOG_DIR=./tmp nargo compile --force --silence-warnings + NOIR_LOG=trace NARGO_LOG_DIR=./tmp nargo compile --force --silence-warnings $FLAGS done TIMES=($(jq -r '. | select(.target == "nargo::cli" and .fields.message == "close") | .fields."time.busy"' ./tmp/*)) diff --git a/test_programs/compile_success_empty/associated_types_implicit/Nargo.toml b/test_programs/compile_success_empty/associated_types_implicit/Nargo.toml new file mode 100644 index 00000000000..6b54cbfca6a --- /dev/null +++ b/test_programs/compile_success_empty/associated_types_implicit/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "associated_types_implicit" +type = "bin" +authors = [""] + +[dependencies] diff --git a/test_programs/compile_success_empty/associated_types_implicit/Prover.toml b/test_programs/compile_success_empty/associated_types_implicit/Prover.toml new file mode 100644 index 00000000000..cab679b4b66 --- /dev/null +++ b/test_programs/compile_success_empty/associated_types_implicit/Prover.toml @@ -0,0 +1 @@ +a = [[0, 1], [2, 3]] diff --git a/test_programs/compile_success_empty/associated_types_implicit/src/main.nr b/test_programs/compile_success_empty/associated_types_implicit/src/main.nr new file mode 100644 index 00000000000..d04cef51868 --- /dev/null +++ b/test_programs/compile_success_empty/associated_types_implicit/src/main.nr @@ -0,0 +1,60 @@ +trait Foo { + type Bar; + + fn foo(self) -> Self::Bar; +} + +impl Foo for u64 { + type Bar = u8; + + fn foo(self) -> Self::Bar { + self as u8 + } +} + +fn main() { + // This currently requires a type annotation to find the impl + let three: u64 = 3; + call_foo(three); + + let x: Option> = Option::some(Option::some(0)); + let x_foo = x.foo(); + assert_eq(x_foo, x_foo); // ensure we don't need an additional type annotation for Bar here + + // The `as u8` is still necessary even though we know the object type, + // otherwise we try to search for `u64: Foo`. + // It seems the `Bar = u8` constraint is still there & checked for, but + // the defaulting of the polymorphic integer occurs first. + assert_eq(x.foo(), 0 as u8); +} + +// Ensure we can use `::Bar: Eq` in a function's where clause +fn call_foo(x: T) +where + T: Foo, + ::Bar: Eq, +{ + let y = x.foo(); + assert_eq(y, y); +} + +// Ensure we can use `::Bar: Eq` in a trait impl's where clause +impl Foo for Option +where + T: Foo, + ::Bar: Eq, +{ + type Bar = ::Bar; + + fn foo(self) -> Self::Bar { + self.unwrap().foo() + } +} + +// Ensure we can use `::Bar: Eq` in a trait's where clause +// TODO: Not working, see issue #7024 +// trait Baz where +// T: Foo, +// ::Bar: Eq {} +// +// impl Baz for u32 {} diff --git a/test_programs/compile_success_empty/comptime_struct_definition/src/main.nr b/test_programs/compile_success_empty/comptime_struct_definition/src/main.nr index 686ac26025d..41ba76171a8 100644 --- a/test_programs/compile_success_empty/comptime_struct_definition/src/main.nr +++ b/test_programs/compile_success_empty/comptime_struct_definition/src/main.nr @@ -12,7 +12,7 @@ pub struct I32AndField { comptime fn my_comptime_fn(typ: StructDefinition) { let _ = typ.as_type(); assert_eq(typ.generics().len(), 3); - assert_eq(typ.fields().len(), 2); + assert_eq(typ.fields_as_written().len(), 2); assert_eq(typ.name(), quote { MyType }); } @@ -39,9 +39,29 @@ mod foo { let generics = s.generics(); assert_eq(generics.len(), 1); - assert_eq(generics[0], new_generic); + let (typ, numeric) = generics[0]; + assert_eq(typ, new_generic); + assert(numeric.is_none()); } // docs:end:add-generic-example } -fn main() {} +fn main() { + comptime { + let typ = quote { MyType }.as_type(); + let (struct_def, generics) = typ.as_struct().unwrap(); + + let fields = struct_def.fields(generics); + assert_eq(fields.len(), 2); + + let (field1_name, field1_type) = fields[0]; + let (field2_name, field2_type) = fields[1]; + + assert_eq(field1_name, quote { field1 }); + assert_eq(field2_name, quote { field2 }); + + // Ensure .fields(generics) actually performs substitutions on generics + assert_eq(field1_type, quote { [i8; 10] }.as_type()); + assert_eq(field2_type, quote { (i16, i32) }.as_type()); + } +} diff --git a/test_programs/compile_success_empty/comptime_type/src/main.nr b/test_programs/compile_success_empty/comptime_type/src/main.nr index 887690cb6cb..6a2453ff0f2 100644 --- a/test_programs/compile_success_empty/comptime_type/src/main.nr +++ b/test_programs/compile_success_empty/comptime_type/src/main.nr @@ -100,7 +100,7 @@ fn main() { let foo = Foo { x: 0 }; let foo_type = type_of(foo); let (struct_definition, generics) = foo_type.as_struct().unwrap(); - let fields = struct_definition.fields(); + let fields = struct_definition.fields(generics); assert_eq(fields.len(), 1); assert_eq(generics.len(), 1); diff --git a/test_programs/compile_success_empty/derive_impl/src/main.nr b/test_programs/compile_success_empty/derive_impl/src/main.nr index 4396169235d..fe7a7140280 100644 --- a/test_programs/compile_success_empty/derive_impl/src/main.nr +++ b/test_programs/compile_success_empty/derive_impl/src/main.nr @@ -7,7 +7,7 @@ comptime fn derive_default(typ: StructDefinition) -> Quoted { ); let type_name = typ.as_type(); - let fields = typ.fields(); + let fields = typ.fields_as_written(); let fields = join(make_field_exprs(fields)); diff --git a/test_programs/compile_success_empty/eq_derivation_with_numeric_generics/Nargo.toml b/test_programs/compile_success_empty/eq_derivation_with_numeric_generics/Nargo.toml new file mode 100644 index 00000000000..14b4d5eff4a --- /dev/null +++ b/test_programs/compile_success_empty/eq_derivation_with_numeric_generics/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "eq_derivation_with_numeric_generics" +type = "bin" +authors = [""] + +[dependencies] diff --git a/test_programs/compile_success_empty/eq_derivation_with_numeric_generics/src/main.nr b/test_programs/compile_success_empty/eq_derivation_with_numeric_generics/src/main.nr new file mode 100644 index 00000000000..3307aeb15ad --- /dev/null +++ b/test_programs/compile_success_empty/eq_derivation_with_numeric_generics/src/main.nr @@ -0,0 +1,12 @@ +#[derive(Eq)] +struct Foo { + a: [Field; T], + b: u32, +} + +fn main() { + let foo = Foo { a: [0; 10], b: 27 }; + let bar = Foo { a: [0; 10], b: 28 }; + assert(foo != bar); +} + diff --git a/test_programs/compile_success_empty/inject_context_attribute/src/main.nr b/test_programs/compile_success_empty/inject_context_attribute/src/main.nr index 963d4cea969..e682ea34b23 100644 --- a/test_programs/compile_success_empty/inject_context_attribute/src/main.nr +++ b/test_programs/compile_success_empty/inject_context_attribute/src/main.nr @@ -40,19 +40,16 @@ comptime fn inject_context(f: FunctionDefinition) { } comptime fn mapping_function(expr: Expr, f: FunctionDefinition) -> Option { - expr.as_function_call().and_then(|func_call: (Expr, [Expr])| { - let (name, arguments) = func_call; - name.resolve(Option::some(f)).as_function_definition().and_then( - |function_definition: FunctionDefinition| { - if function_definition.has_named_attribute("inject_context") { - let arguments = arguments.push_front(quote { _context }.as_expr().unwrap()); - let arguments = arguments.map(|arg: Expr| arg.quoted()).join(quote { , }); - Option::some(quote { $name($arguments) }.as_expr().unwrap()) - } else { - Option::none() - } - }, - ) + expr.as_function_call().and_then(|(name, arguments)| { + name.resolve(Option::some(f)).as_function_definition().and_then(|function_definition| { + if function_definition.has_named_attribute("inject_context") { + let arguments = arguments.push_front(quote { _context }.as_expr().unwrap()); + let arguments = arguments.map(|arg| arg.quoted()).join(quote { , }); + Option::some(quote { $name($arguments) }.as_expr().unwrap()) + } else { + Option::none() + } + }) }) } diff --git a/test_programs/compile_success_empty/macros_in_comptime/src/main.nr b/test_programs/compile_success_empty/macros_in_comptime/src/main.nr index 0572192225c..112ed16c22a 100644 --- a/test_programs/compile_success_empty/macros_in_comptime/src/main.nr +++ b/test_programs/compile_success_empty/macros_in_comptime/src/main.nr @@ -33,8 +33,8 @@ comptime fn foo(x: Field) { break; } - let loop = quote { for _ in 0..0 { break; } }; - unquote!(loop); + let loop_ = quote { for _ in 0..0 { break; } }; + unquote!(loop_); } mod submodule { diff --git a/test_programs/compile_success_empty/regression_7038/Nargo.toml b/test_programs/compile_success_empty/regression_7038/Nargo.toml new file mode 100644 index 00000000000..3c874d4b6e8 --- /dev/null +++ b/test_programs/compile_success_empty/regression_7038/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "regression_7038" +type = "bin" +authors = [""] +compiler_version = ">=0.30.0" + +[dependencies] diff --git a/test_programs/compile_success_empty/regression_7038/src/main.nr b/test_programs/compile_success_empty/regression_7038/src/main.nr new file mode 100644 index 00000000000..793a3f60807 --- /dev/null +++ b/test_programs/compile_success_empty/regression_7038/src/main.nr @@ -0,0 +1,40 @@ +trait BigNumTrait {} + +pub struct MyBigNum; + +impl crate::BigNumTrait for MyBigNum {} + +trait CurveParamsTrait +where + BigNum: BigNumTrait, +{ + fn one(); +} + +pub struct BN254Params; +impl CurveParamsTrait for BN254Params { + + fn one() {} +} + +trait BigCurveTrait { + fn two(); +} + +pub struct BigCurve {} + +type BN254 = BigCurve; + +impl BigCurveTrait for BigCurve +where + BigNum: BigNumTrait, + CurveParams: CurveParamsTrait, +{ + fn two() { + let _ = CurveParams::one(); + } +} + +fn main() { + let _ = BN254::two(); +} diff --git a/test_programs/compile_success_empty/regression_7038_2/Nargo.toml b/test_programs/compile_success_empty/regression_7038_2/Nargo.toml new file mode 100644 index 00000000000..f4f23683eb8 --- /dev/null +++ b/test_programs/compile_success_empty/regression_7038_2/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "regression_7038_2" +type = "bin" +authors = [""] +compiler_version = ">=0.30.0" + +[dependencies] diff --git a/test_programs/compile_success_empty/regression_7038_2/src/main.nr b/test_programs/compile_success_empty/regression_7038_2/src/main.nr new file mode 100644 index 00000000000..6a116bb0722 --- /dev/null +++ b/test_programs/compile_success_empty/regression_7038_2/src/main.nr @@ -0,0 +1,39 @@ +trait BigNumTrait {} + +pub struct MyBigNum; + +impl crate::BigNumTrait for MyBigNum {} + +trait CurveParamsTrait +where + BigNum: BigNumTrait, +{ + // The difference between this and regression_7083 is that here + // this is a default method. + fn one() {} +} + +pub struct BN254Params; +impl CurveParamsTrait for BN254Params {} + +trait BigCurveTrait { + fn two(); +} + +pub struct BigCurve {} + +type BN254 = BigCurve; + +impl BigCurveTrait for BigCurve +where + BigNum: BigNumTrait, + CurveParams: CurveParamsTrait, +{ + fn two() { + let _ = CurveParams::one(); + } +} + +fn main() { + let _ = BN254::two(); +} diff --git a/test_programs/compile_success_empty/regression_7038_3/Nargo.toml b/test_programs/compile_success_empty/regression_7038_3/Nargo.toml new file mode 100644 index 00000000000..65bc946c559 --- /dev/null +++ b/test_programs/compile_success_empty/regression_7038_3/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "regression_7038_3" +type = "bin" +authors = [""] +compiler_version = ">=0.30.0" + +[dependencies] diff --git a/test_programs/compile_success_empty/regression_7038_3/src/main.nr b/test_programs/compile_success_empty/regression_7038_3/src/main.nr new file mode 100644 index 00000000000..1b6bf0b72d5 --- /dev/null +++ b/test_programs/compile_success_empty/regression_7038_3/src/main.nr @@ -0,0 +1,40 @@ +trait BigNumTrait {} + +pub struct MyBigNum; + +impl crate::BigNumTrait for MyBigNum {} + +trait CurveParamsTrait { + // The difference between this and regression_7038 and regression_7038_2 is that + // here the where clause is on the method, not the trait + fn one() + where + BigNum: BigNumTrait; +} + +pub struct BN254Params; +impl CurveParamsTrait for BN254Params { + fn one() {} +} + +trait BigCurveTrait { + fn two(); +} + +pub struct BigCurve {} + +type BN254 = BigCurve; + +impl BigCurveTrait for BigCurve +where + BigNum: BigNumTrait, + CurveParams: CurveParamsTrait, +{ + fn two() { + let _ = CurveParams::one(); + } +} + +fn main() { + let _ = BN254::two(); +} diff --git a/test_programs/compile_success_empty/regression_7038_4/Nargo.toml b/test_programs/compile_success_empty/regression_7038_4/Nargo.toml new file mode 100644 index 00000000000..435c8094c70 --- /dev/null +++ b/test_programs/compile_success_empty/regression_7038_4/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "regression_7038_4" +type = "bin" +authors = [""] +compiler_version = ">=0.30.0" + +[dependencies] diff --git a/test_programs/compile_success_empty/regression_7038_4/src/main.nr b/test_programs/compile_success_empty/regression_7038_4/src/main.nr new file mode 100644 index 00000000000..524f05a5022 --- /dev/null +++ b/test_programs/compile_success_empty/regression_7038_4/src/main.nr @@ -0,0 +1,29 @@ +// This program is a reduction of regression_7038_3 that led to a monomorphizer crash +trait BigNumTrait {} + +trait CurveParamsTrait { + fn one() + where + BigNum: BigNumTrait; +} + +pub struct MyBigNum; + +impl BigNumTrait for MyBigNum {} + +pub struct Params; +impl CurveParamsTrait for Params { + + fn one() {} +} + +fn foo() +where + C: CurveParamsTrait, +{ + let _ = C::one(); +} + +fn main() { + foo::(); +} diff --git a/test_programs/compile_success_empty/serialize/src/main.nr b/test_programs/compile_success_empty/serialize/src/main.nr index 66c79f9fc9d..a11fdf570d0 100644 --- a/test_programs/compile_success_empty/serialize/src/main.nr +++ b/test_programs/compile_success_empty/serialize/src/main.nr @@ -5,13 +5,12 @@ trait Serialize { fn serialize(self) -> [Field; Self::Size]; } -impl Serialize for (A, B) +impl Serialize for (A, B) where - A: Serialize, - B: Serialize, + A: Serialize, + B: Serialize, { - // let Size = ::Size + ::Size; - let Size: u32 = AS + BS; + let Size = ::Size + ::Size; fn serialize(self: Self) -> [Field; Self::Size] { let mut array: [Field; Self::Size] = std::mem::zeroed(); @@ -28,12 +27,11 @@ where } } -impl Serialize for [T; N] +impl Serialize for [T; N] where - T: Serialize, + T: Serialize, { - // let Size = ::Size * N; - let Size: u32 = TS * N; + let Size = ::Size * N; fn serialize(self: Self) -> [Field; Self::Size] { let mut array: [Field; Self::Size] = std::mem::zeroed(); diff --git a/test_programs/compile_success_empty/trait_generics/src/main.nr b/test_programs/compile_success_empty/trait_generics/src/main.nr index 08302ded68c..e8b57b6fe6f 100644 --- a/test_programs/compile_success_empty/trait_generics/src/main.nr +++ b/test_programs/compile_success_empty/trait_generics/src/main.nr @@ -24,7 +24,7 @@ where T: MyInto, { fn into(self) -> [U; N] { - self.map(|x: T| x.into()) + self.map(|x| x.into()) } } diff --git a/test_programs/compile_success_empty/unquote_struct/src/main.nr b/test_programs/compile_success_empty/unquote_struct/src/main.nr index d4ab275858c..12c683a94a8 100644 --- a/test_programs/compile_success_empty/unquote_struct/src/main.nr +++ b/test_programs/compile_success_empty/unquote_struct/src/main.nr @@ -10,14 +10,7 @@ fn foo(x: Field, y: u32) -> u32 { // Given a function, wrap its parameters in a struct definition comptime fn output_struct(f: FunctionDefinition) -> Quoted { - let fields = f - .parameters() - .map(|param: (Quoted, Type)| { - let name = param.0; - let typ = param.1; - quote { $name: $typ, } - }) - .join(quote {}); + let fields = f.parameters().map(|(name, typ)| quote { $name: $typ, }).join(quote {}); quote { struct Foo { $fields } diff --git a/test_programs/execution_failure/mocks_in_execution/Nargo.toml b/test_programs/execution_failure/mocks_in_execution/Nargo.toml new file mode 100644 index 00000000000..c83da9a3d0c --- /dev/null +++ b/test_programs/execution_failure/mocks_in_execution/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "mocks_in_execution" +type = "bin" +authors = [""] +compiler_version = ">=0.23.0" + +[dependencies] diff --git a/test_programs/execution_failure/mocks_in_execution/Prover.toml b/test_programs/execution_failure/mocks_in_execution/Prover.toml new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test_programs/execution_failure/mocks_in_execution/src/main.nr b/test_programs/execution_failure/mocks_in_execution/src/main.nr new file mode 100644 index 00000000000..b43068ab251 --- /dev/null +++ b/test_programs/execution_failure/mocks_in_execution/src/main.nr @@ -0,0 +1,5 @@ +fn main() { + // Trying to use a mock in `nargo execute` should fail. + let mock = unsafe { std::test::OracleMock::mock("foo") }; + assert_eq(mock.id, 0); +} diff --git a/test_programs/execution_report.sh b/test_programs/execution_report.sh index b929c367f7d..827b7806d37 100755 --- a/test_programs/execution_report.sh +++ b/test_programs/execution_report.sh @@ -46,12 +46,8 @@ for dir in ${tests_to_profile[@]}; do fi - NUM_RUNS=1 + NUM_RUNS=$2 TOTAL_TIME=0 - - if [ "$2" == "1" ]; then - NUM_RUNS=5 - fi for ((i = 1; i <= NUM_RUNS; i++)); do NOIR_LOG=trace NARGO_LOG_DIR=./tmp nargo execute --silence-warnings diff --git a/test_programs/execution_success/brillig_oracle/Prover.toml b/test_programs/execution_success/brillig_oracle/Prover.toml deleted file mode 100644 index 161f4fb62c0..00000000000 --- a/test_programs/execution_success/brillig_oracle/Prover.toml +++ /dev/null @@ -1,2 +0,0 @@ -_x = "10" - diff --git a/test_programs/execution_success/global_var_regression_simple/Nargo.toml b/test_programs/execution_success/global_var_regression_simple/Nargo.toml new file mode 100644 index 00000000000..50b4902de22 --- /dev/null +++ b/test_programs/execution_success/global_var_regression_simple/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "global_var_regression_simple" +type = "bin" +authors = [""] + +[dependencies] \ No newline at end of file diff --git a/test_programs/execution_success/global_var_regression_simple/Prover.toml b/test_programs/execution_success/global_var_regression_simple/Prover.toml new file mode 100644 index 00000000000..2c1854573a4 --- /dev/null +++ b/test_programs/execution_success/global_var_regression_simple/Prover.toml @@ -0,0 +1,2 @@ +x = 1 +y = 2 diff --git a/test_programs/execution_success/global_var_regression_simple/src/main.nr b/test_programs/execution_success/global_var_regression_simple/src/main.nr new file mode 100644 index 00000000000..b1bf753a73c --- /dev/null +++ b/test_programs/execution_success/global_var_regression_simple/src/main.nr @@ -0,0 +1,25 @@ +global EXPONENTIATE: [[Field; 2]; 2] = [[1, 1], [0, 0]]; + +fn main(x: Field, y: pub Field) { + let mut acc: Field = 0; + for i in 0..2 { + for j in 0..2 { + acc += EXPONENTIATE[i][j]; + } + } + assert(!acc.lt(x)); + assert(x != y); + + dummy_again(x, y); +} + +fn dummy_again(x: Field, y: Field) { + let mut acc: Field = 0; + for i in 0..2 { + for j in 0..2 { + acc += EXPONENTIATE[i][j]; + } + } + assert(!acc.lt(x)); + assert(x != y); +} diff --git a/test_programs/execution_success/hint_black_box/src/main.nr b/test_programs/execution_success/hint_black_box/src/main.nr index abceadb07ff..ed2dc2d3760 100644 --- a/test_programs/execution_success/hint_black_box/src/main.nr +++ b/test_programs/execution_success/hint_black_box/src/main.nr @@ -2,12 +2,12 @@ use std::hint::black_box; fn main(a: u32, b: u32) { // This version unrolls into a number of additions - assert_eq(loop(5, a), b); + assert_eq(loop_(5, a), b); // This version simplifies into a single `constraint 50 == b` - assert_eq(loop(5, 10), b); + assert_eq(loop_(5, 10), b); // This version should not simplify down to a single constraint, // it should treat 10 as opaque: - assert_eq(loop(5, black_box(10)), b); + assert_eq(loop_(5, black_box(10)), b); // Check array handling. let arr = [a, a, a, a, a]; @@ -54,7 +54,7 @@ fn main(a: u32, b: u32) { //assert_eq(c, b); } -fn loop(n: u32, k: u32) -> u32 { +fn loop_(n: u32, k: u32) -> u32 { let mut sum = 0; for _ in 0..n { sum = sum + k; diff --git a/test_programs/execution_success/loop/src/main.nr b/test_programs/execution_success/loop/src/main.nr index b3be4c4c3ff..3e1f24742ec 100644 --- a/test_programs/execution_success/loop/src/main.nr +++ b/test_programs/execution_success/loop/src/main.nr @@ -2,12 +2,12 @@ // // The features being tested is basic looping. fn main(six_as_u32: u32) { - assert_eq(loop(4), six_as_u32); + assert_eq(loop_excl(4), six_as_u32); assert_eq(loop_incl(3), six_as_u32); assert(plain_loop() == six_as_u32); } -fn loop(x: u32) -> u32 { +fn loop_excl(x: u32) -> u32 { let mut sum = 0; for i in 0..x { sum = sum + i; diff --git a/test_programs/execution_success/loop_invariant_regression/src/main.nr b/test_programs/execution_success/loop_invariant_regression/src/main.nr index c28ce063116..61f8b1bedba 100644 --- a/test_programs/execution_success/loop_invariant_regression/src/main.nr +++ b/test_programs/execution_success/loop_invariant_regression/src/main.nr @@ -1,11 +1,11 @@ // Tests a simple loop where we expect loop invariant instructions // to be hoisted to the loop's pre-header block. fn main(x: u32, y: u32) { - loop(4, x, y); + loop_(4, x, y); array_read_loop(4, x); } -fn loop(upper_bound: u32, x: u32, y: u32) { +fn loop_(upper_bound: u32, x: u32, y: u32) { for _ in 0..upper_bound { let mut z = x * y; z = z * x; diff --git a/test_programs/execution_success/loop_keyword/Nargo.toml b/test_programs/execution_success/loop_keyword/Nargo.toml new file mode 100644 index 00000000000..8189b407cd9 --- /dev/null +++ b/test_programs/execution_success/loop_keyword/Nargo.toml @@ -0,0 +1,5 @@ +[package] +name = "loop_keyword" +type = "bin" +authors = [""] +[dependencies] diff --git a/test_programs/execution_success/loop_keyword/src/main.nr b/test_programs/execution_success/loop_keyword/src/main.nr new file mode 100644 index 00000000000..b038ae22343 --- /dev/null +++ b/test_programs/execution_success/loop_keyword/src/main.nr @@ -0,0 +1,52 @@ +fn main() { + /// Safety: test code + unsafe { + check_loop(); + } + + check_comptime_loop(); +} + +unconstrained fn check_loop() { + let mut i = 0; + let mut sum = 0; + + loop { + if i == 4 { + break; + } + + if i == 2 { + i += 1; + continue; + } + + sum += i; + i += 1; + } + + assert_eq(sum, 1 + 3); +} + +fn check_comptime_loop() { + comptime { + let mut i = 0; + let mut sum = 0; + + loop { + if i == 4 { + break; + } + + if i == 2 { + i += 1; + continue; + } + + sum += i; + i += 1; + } + + assert_eq(sum, 1 + 3); + } +} diff --git a/test_programs/execution_success/regression_7062/Nargo.toml b/test_programs/execution_success/regression_7062/Nargo.toml new file mode 100644 index 00000000000..0e11219ad98 --- /dev/null +++ b/test_programs/execution_success/regression_7062/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "regression_7062" +type = "bin" +authors = [""] +compiler_version = ">=0.31.0" + +[dependencies] diff --git a/test_programs/execution_success/regression_7062/Prover.toml b/test_programs/execution_success/regression_7062/Prover.toml new file mode 100644 index 00000000000..08608e6b3ba --- /dev/null +++ b/test_programs/execution_success/regression_7062/Prover.toml @@ -0,0 +1,2 @@ +index = 1 +value = 1 diff --git a/test_programs/execution_success/regression_7062/src/main.nr b/test_programs/execution_success/regression_7062/src/main.nr new file mode 100644 index 00000000000..47e7593c0e6 --- /dev/null +++ b/test_programs/execution_success/regression_7062/src/main.nr @@ -0,0 +1,10 @@ +fn main(value: Field, index: u32) { + let mut args = &[0, 1]; + args[index] = value; + /// Safety: n/a + unsafe { store(args) }; + // Dummy test to remove the 'underconstraint bug' + assert(args[0] + args[1] != 0); +} + +pub unconstrained fn store(_: [Field]) {} diff --git a/test_programs/gates_report_brillig.sh b/test_programs/gates_report_brillig.sh index 7343857a3c5..3f2a4542735 100755 --- a/test_programs/gates_report_brillig.sh +++ b/test_programs/gates_report_brillig.sh @@ -28,6 +28,6 @@ done echo "]" >> Nargo.toml -nargo info --force-brillig --json | jq -r ".programs[].functions = []" > gates_report_brillig.json +nargo info --silence-warnings --force-brillig --json --inliner-aggressiveness $1 | jq -r ".programs[].functions = []" > gates_report_brillig.json rm Nargo.toml diff --git a/test_programs/gates_report_brillig_execution.sh b/test_programs/gates_report_brillig_execution.sh index 219fbb5c11a..c6a904f8b93 100755 --- a/test_programs/gates_report_brillig_execution.sh +++ b/test_programs/gates_report_brillig_execution.sh @@ -42,6 +42,6 @@ done echo "]" >> Nargo.toml -nargo info --profile-execution --json | jq -r ".programs[].functions = []" > gates_report_brillig_execution.json +nargo info --silence-warnings --profile-execution --json --inliner-aggressiveness $1 | jq -r ".programs[].functions = []" > gates_report_brillig_execution.json rm Nargo.toml diff --git a/test_programs/memory_report.sh b/test_programs/memory_report.sh index e501464c198..4ba7579b390 100755 --- a/test_programs/memory_report.sh +++ b/test_programs/memory_report.sh @@ -4,6 +4,8 @@ set -e sudo apt-get install heaptrack NARGO="nargo" +PARSE_MEMORY=$(realpath "$(dirname "$0")/parse_memory.sh") + # Tests to be profiled for memory report tests_to_profile=("keccak256" "workspace" "regression_4709" "ram_blowup_regression") @@ -19,6 +21,7 @@ fi FIRST="1" +FLAGS=${FLAGS:- ""} echo "{\"memory_reports\": [ " > memory_report.json for test_name in ${tests_to_profile[@]}; do @@ -35,12 +38,12 @@ for test_name in ${tests_to_profile[@]}; do test_name=$(basename $current_dir) fi - COMMAND="compile --force --silence-warnings" + COMMAND="compile --force --silence-warnings $FLAGS" if [ "$2" == "1" ]; then COMMAND="execute --silence-warnings" fi - heaptrack --output $current_dir/$test_name"_heap" $NARGO $COMMAND + heaptrack --output $current_dir/$test_name"_heap" $NARGO $COMMAND if test -f $current_dir/$test_name"_heap.gz"; then heaptrack --analyze $current_dir/$test_name"_heap.gz" > $current_dir/$test_name"_heap_analysis.txt" @@ -53,7 +56,8 @@ for test_name in ${tests_to_profile[@]}; do len=${#consumption}-30 peak=${consumption:30:len} rm $current_dir/$test_name"_heap_analysis.txt" - echo -e " {\n \"artifact_name\":\"$test_name\",\n \"peak_memory\":\"$peak\"\n }" >> $current_dir"/memory_report.json" + peak_memory=$($PARSE_MEMORY $peak) + echo -e " {\n \"artifact_name\":\"$test_name\",\n \"peak_memory\":\"$peak_memory\"\n }" >> $current_dir"/memory_report.json" done echo "]}" >> $current_dir"/memory_report.json" diff --git a/test_programs/execution_success/brillig_oracle/Nargo.toml b/test_programs/noir_test_success/brillig_oracle/Nargo.toml similarity index 100% rename from test_programs/execution_success/brillig_oracle/Nargo.toml rename to test_programs/noir_test_success/brillig_oracle/Nargo.toml diff --git a/test_programs/execution_success/brillig_oracle/src/main.nr b/test_programs/noir_test_success/brillig_oracle/src/main.nr similarity index 97% rename from test_programs/execution_success/brillig_oracle/src/main.nr rename to test_programs/noir_test_success/brillig_oracle/src/main.nr index cae174edb99..77dbeef9aa1 100644 --- a/test_programs/execution_success/brillig_oracle/src/main.nr +++ b/test_programs/noir_test_success/brillig_oracle/src/main.nr @@ -1,6 +1,11 @@ use std::slice; use std::test::OracleMock; +#[test] +fn test_main() { + main(10); +} + // Tests oracle usage in brillig/unconstrained functions fn main(_x: Field) { /// Safety: testing context @@ -46,4 +51,3 @@ unconstrained fn get_number_sequence_wrapper(size: Field) { assert(slice[i] == reversed_slice[19 - i]); } } - diff --git a/test_programs/noir_test_success/comptime_expr/src/main.nr b/test_programs/noir_test_success/comptime_expr/src/main.nr index 25910685e87..6efbc212cbe 100644 --- a/test_programs/noir_test_success/comptime_expr/src/main.nr +++ b/test_programs/noir_test_success/comptime_expr/src/main.nr @@ -761,8 +761,7 @@ mod tests { } comptime fn times_two(expr: Expr) -> Option { - expr.as_integer().and_then(|integer: (Field, bool)| { - let (value, _) = integer; + expr.as_integer().and_then(|(value, _)| { let value = value * 2; quote { $value }.as_expr() }) diff --git a/test_programs/parse_memory.sh b/test_programs/parse_memory.sh new file mode 100755 index 00000000000..8e125b13b23 --- /dev/null +++ b/test_programs/parse_memory.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +set -e + +# This script accepts a string representing the amount of memory allocated as outputted by `heaptrack` +# and standardizes it to be in terms of megabytes as `heaptrack` will report different units depending on the duration. + +DIGITS='([0-9]+(\.[0-9]+)?)' +KILOBYTES_REGEX=^${DIGITS}K$ +MEGABYTES_REGEX=^${DIGITS}M$ +GIGABYTES_REGEX=^${DIGITS}G$ + +if [[ $1 =~ $KILOBYTES_REGEX ]]; then + echo ${BASH_REMATCH[1]} 1000 | awk '{printf "%.3f\n", $1/$2}' +elif [[ $1 =~ $MEGABYTES_REGEX ]]; then + echo ${BASH_REMATCH[1]} | awk '{printf "%.3f\n", $1}' +elif [[ $1 =~ $GIGABYTES_REGEX ]]; then + echo ${BASH_REMATCH[1]} 1000 | awk '{printf "%.3f\n", $1*$2}' +else + echo "Could not parse memory: unrecognized format" 1>&2 + exit 1 +fi diff --git a/tooling/acvm_cli/src/cli/execute_cmd.rs b/tooling/acvm_cli/src/cli/execute_cmd.rs index 9fed8cbd497..e5d48073ca8 100644 --- a/tooling/acvm_cli/src/cli/execute_cmd.rs +++ b/tooling/acvm_cli/src/cli/execute_cmd.rs @@ -82,8 +82,12 @@ pub(crate) fn execute_program_from_witness( &program, inputs_map, &Bn254BlackBoxSolver(pedantic_solving), - &mut DefaultForeignCallBuilder { output: PrintOutput::Stdout, ..Default::default() } - .build(), + &mut DefaultForeignCallBuilder { + output: PrintOutput::Stdout, + enable_mocks: false, + ..Default::default() + } + .build(), ) .map_err(CliError::CircuitExecutionError) } diff --git a/tooling/lsp/src/requests/code_action.rs b/tooling/lsp/src/requests/code_action.rs index 38cc6bddf64..24ed327393d 100644 --- a/tooling/lsp/src/requests/code_action.rs +++ b/tooling/lsp/src/requests/code_action.rs @@ -33,6 +33,7 @@ use super::{process_request, to_lsp_location}; mod fill_struct_fields; mod implement_missing_members; mod import_or_qualify; +mod import_trait; mod remove_bang_from_call; mod remove_unused_import; mod tests; @@ -285,6 +286,8 @@ impl<'a> Visitor for CodeActionFinder<'a> { self.remove_bang_from_call(method_call.method_name.span()); } + self.import_trait_in_method_call(method_call); + true } } diff --git a/tooling/lsp/src/requests/code_action/import_trait.rs b/tooling/lsp/src/requests/code_action/import_trait.rs new file mode 100644 index 00000000000..dd500c334ab --- /dev/null +++ b/tooling/lsp/src/requests/code_action/import_trait.rs @@ -0,0 +1,411 @@ +use std::collections::HashSet; + +use noirc_errors::Location; +use noirc_frontend::{ + ast::MethodCallExpression, + hir::def_map::ModuleDefId, + node_interner::{ReferenceId, TraitId}, +}; + +use crate::{ + modules::{relative_module_full_path, relative_module_id_path}, + requests::TraitReexport, + use_segment_positions::{ + use_completion_item_additional_text_edits, UseCompletionItemAdditionTextEditsRequest, + }, + visibility::module_def_id_is_visible, +}; + +use super::CodeActionFinder; + +impl<'a> CodeActionFinder<'a> { + pub(super) fn import_trait_in_method_call(&mut self, method_call: &MethodCallExpression) { + // First see if the method name already points to a function. + let name_location = Location::new(method_call.method_name.span(), self.file); + if let Some(ReferenceId::Function(func_id)) = self.interner.find_referenced(name_location) { + // If yes, it could be that the compiler is issuing a warning because there's + // only one possible trait that the method could be coming from, but it's not imported + let func_meta = self.interner.function_meta(&func_id); + let Some(trait_impl_id) = func_meta.trait_impl else { + return; + }; + + let trait_impl = self.interner.get_trait_implementation(trait_impl_id); + let trait_id = trait_impl.borrow().trait_id; + self.import_trait(trait_id); + return; + } + + // Find out the type of the object + let object_location = Location::new(method_call.object.span, self.file); + let Some(typ) = self.interner.type_at_location(object_location) else { + return; + }; + + let trait_methods = + self.interner.lookup_trait_methods(&typ, &method_call.method_name.0.contents, true); + let trait_ids: HashSet<_> = trait_methods.iter().map(|(_, trait_id)| *trait_id).collect(); + + for trait_id in trait_ids { + self.import_trait(trait_id); + } + } + + fn import_trait(&mut self, trait_id: TraitId) { + // First check if the trait is visible + let trait_ = self.interner.get_trait(trait_id); + let visibility = trait_.visibility; + let module_def_id = ModuleDefId::TraitId(trait_id); + let mut trait_reexport = None; + + if !module_def_id_is_visible( + module_def_id, + self.module_id, + visibility, + None, + self.interner, + self.def_maps, + ) { + // If it's not, try to find a visible reexport of the trait + // that is visible from the current module + let Some((visible_module_id, name, _)) = + self.interner.get_trait_reexports(trait_id).iter().find( + |(module_id, _, visibility)| { + module_def_id_is_visible( + module_def_id, + self.module_id, + *visibility, + Some(*module_id), + self.interner, + self.def_maps, + ) + }, + ) + else { + return; + }; + trait_reexport = Some(TraitReexport { module_id: visible_module_id, name }); + } + + let trait_name = if let Some(trait_reexport) = &trait_reexport { + trait_reexport.name + } else { + &trait_.name + }; + + // Check if the trait is currently imported. If yes, no need to suggest anything + let module_data = + &self.def_maps[&self.module_id.krate].modules()[self.module_id.local_id.0]; + if !module_data.scope().find_name(trait_name).is_none() { + return; + } + + let module_def_id = ModuleDefId::TraitId(trait_id); + let current_module_parent_id = self.module_id.parent(self.def_maps); + let module_full_path = if let Some(trait_reexport) = &trait_reexport { + relative_module_id_path( + *trait_reexport.module_id, + &self.module_id, + current_module_parent_id, + self.interner, + ) + } else { + let Some(path) = relative_module_full_path( + module_def_id, + self.module_id, + current_module_parent_id, + self.interner, + ) else { + return; + }; + path + }; + + let full_path = format!("{}::{}", module_full_path, trait_name); + + let title = format!("Import {}", full_path); + + let text_edits = use_completion_item_additional_text_edits( + UseCompletionItemAdditionTextEditsRequest { + full_path: &full_path, + files: self.files, + file: self.file, + lines: &self.lines, + nesting: self.nesting, + auto_import_line: self.auto_import_line, + }, + &self.use_segment_positions, + ); + + let code_action = self.new_quick_fix_multiple_edits(title, text_edits); + self.code_actions.push(code_action); + } +} + +#[cfg(test)] +mod tests { + use tokio::test; + + use crate::requests::code_action::tests::assert_code_action; + + #[test] + async fn test_import_trait_in_method_call_when_one_option_but_not_in_scope() { + let title = "Import moo::Foo"; + + let src = r#"mod moo { + pub trait Foo { + fn foobar(self); + } + + impl Foo for Field { + fn foobar(self) {} + } +} + +fn main() { + let x: Field = 1; + x.foo>||||| CodeActionResponse { ) .await .expect("Could not execute on_code_action_request") - .unwrap() + .expect("Expected to get a CodeActionResponse, got None") } pub(crate) async fn assert_code_action(title: &str, src: &str, expected: &str) { diff --git a/tooling/lsp/src/requests/completion.rs b/tooling/lsp/src/requests/completion.rs index fd6ef60af82..a845fd4496f 100644 --- a/tooling/lsp/src/requests/completion.rs +++ b/tooling/lsp/src/requests/completion.rs @@ -40,10 +40,11 @@ use sort_text::underscore_sort_text; use crate::{ requests::to_lsp_location, trait_impl_method_stub_generator::TraitImplMethodStubGenerator, - use_segment_positions::UseSegmentPositions, utils, LspState, + use_segment_positions::UseSegmentPositions, utils, visibility::module_def_id_is_visible, + LspState, }; -use super::process_request; +use super::{process_request, TraitReexport}; mod auto_import; mod builtins; @@ -245,7 +246,7 @@ impl<'a> NodeFinder<'a> { let mut idents: Vec = Vec::new(); // Find in which ident we are in, and in which part of it - // (it could be that we are completting in the middle of an ident) + // (it could be that we are completing in the middle of an ident) for segment in &path.segments { let ident = &segment.ident; @@ -621,7 +622,7 @@ impl<'a> NodeFinder<'a> { | Type::Forall(_, _) | Type::Constant(..) | Type::Quoted(_) - | Type::InfixExpr(_, _, _) + | Type::InfixExpr(..) | Type::Error => (), } @@ -658,45 +659,82 @@ impl<'a> NodeFinder<'a> { let has_self_param = matches!(function_kind, FunctionKind::SelfType(..)); for (name, methods) in methods_by_name { - let Some(func_id) = - methods.find_matching_method(typ, has_self_param, self.interner).or_else(|| { - // Also try to find a method assuming typ is `&mut typ`: - // we want to suggest methods that take `&mut self` even though a variable might not - // be mutable, so a user can know they need to mark it as mutable. - let typ = Type::MutableReference(Box::new(typ.clone())); - methods.find_matching_method(&typ, has_self_param, self.interner) - }) - else { + if !name_matches(name, prefix) { continue; - }; - - if let Some(struct_id) = struct_id { - let modifiers = self.interner.function_modifiers(&func_id); - let visibility = modifiers.visibility; - if !struct_member_is_visible(struct_id, visibility, self.module_id, self.def_maps) { - continue; - } } - if is_primitive - && !method_call_is_visible( - typ, - func_id, - self.module_id, - self.interner, - self.def_maps, - ) + for (func_id, trait_id) in + methods.find_matching_methods(typ, has_self_param, self.interner) { - continue; - } + if let Some(struct_id) = struct_id { + let modifiers = self.interner.function_modifiers(&func_id); + let visibility = modifiers.visibility; + if !struct_member_is_visible( + struct_id, + visibility, + self.module_id, + self.def_maps, + ) { + continue; + } + } + + let mut trait_reexport = None; + + if let Some(trait_id) = trait_id { + let modifiers = self.interner.function_modifiers(&func_id); + let visibility = modifiers.visibility; + let module_def_id = ModuleDefId::TraitId(trait_id); + if !module_def_id_is_visible( + module_def_id, + self.module_id, + visibility, + None, // defining module + self.interner, + self.def_maps, + ) { + // Try to find a visible reexport of the trait + // that is visible from the current module + let Some((visible_module_id, name, _)) = + self.interner.get_trait_reexports(trait_id).iter().find( + |(module_id, _, visibility)| { + module_def_id_is_visible( + module_def_id, + self.module_id, + *visibility, + Some(*module_id), + self.interner, + self.def_maps, + ) + }, + ) + else { + continue; + }; + + trait_reexport = Some(TraitReexport { module_id: visible_module_id, name }); + } + } + + if is_primitive + && !method_call_is_visible( + typ, + func_id, + self.module_id, + self.interner, + self.def_maps, + ) + { + continue; + } - if name_matches(name, prefix) { let completion_items = self.function_completion_items( name, func_id, function_completion_kind, function_kind, None, // attribute first type + trait_id.map(|id| (id, trait_reexport.as_ref())), self_prefix, ); if !completion_items.is_empty() { @@ -749,6 +787,7 @@ impl<'a> NodeFinder<'a> { function_completion_kind, function_kind, None, // attribute first type + None, // trait_id (we are suggesting methods for `Trait::>|<` so no need to auto-import it) self_prefix, ); if !completion_items.is_empty() { diff --git a/tooling/lsp/src/requests/completion/builtins.rs b/tooling/lsp/src/requests/completion/builtins.rs index c0910e9005e..10267d4719b 100644 --- a/tooling/lsp/src/requests/completion/builtins.rs +++ b/tooling/lsp/src/requests/completion/builtins.rs @@ -91,6 +91,9 @@ impl<'a> NodeFinder<'a> { AttributeTarget::Struct => { self.suggest_one_argument_attributes(prefix, &["abi"]); } + AttributeTarget::Enum => { + self.suggest_one_argument_attributes(prefix, &["abi"]); + } AttributeTarget::Function => { let no_arguments_attributes = &[ "contract_library_method", @@ -156,6 +159,7 @@ pub(super) fn keyword_builtin_type(keyword: &Keyword) -> Option<&'static str> { match keyword { Keyword::Bool => Some("bool"), Keyword::CtString => Some("CtString"), + Keyword::EnumDefinition => Some("EnumDefinition"), Keyword::Expr => Some("Expr"), Keyword::Field => Some("Field"), Keyword::FunctionDefinition => Some("FunctionDefinition"), @@ -191,6 +195,7 @@ pub(super) fn keyword_builtin_type(keyword: &Keyword) -> Option<&'static str> { | Keyword::Impl | Keyword::In | Keyword::Let + | Keyword::Loop | Keyword::Match | Keyword::Mod | Keyword::Mut @@ -246,6 +251,7 @@ pub(super) fn keyword_builtin_function(keyword: &Keyword) -> Option Option NodeFinder<'a> { @@ -56,6 +63,7 @@ impl<'a> NodeFinder<'a> { match target { AttributeTarget::Module => Some(Type::Quoted(QuotedType::Module)), AttributeTarget::Struct => Some(Type::Quoted(QuotedType::StructDefinition)), + AttributeTarget::Enum => Some(Type::Quoted(QuotedType::EnumDefinition)), AttributeTarget::Trait => Some(Type::Quoted(QuotedType::TraitDefinition)), AttributeTarget::Function => Some(Type::Quoted(QuotedType::FunctionDefinition)), AttributeTarget::Let => { @@ -75,6 +83,7 @@ impl<'a> NodeFinder<'a> { function_completion_kind, function_kind, attribute_first_type.as_ref(), + None, // trait_id false, // self_prefix ), ModuleDefId::TypeId(struct_id) => vec![self.struct_completion_item(name, struct_id)], @@ -144,6 +153,7 @@ impl<'a> NodeFinder<'a> { self.completion_item_with_doc_comments(ReferenceId::Global(global_id), completion_item) } + #[allow(clippy::too_many_arguments)] pub(super) fn function_completion_items( &self, name: &String, @@ -151,6 +161,7 @@ impl<'a> NodeFinder<'a> { function_completion_kind: FunctionCompletionKind, function_kind: FunctionKind, attribute_first_type: Option<&Type>, + trait_info: Option<(TraitId, Option<&TraitReexport>)>, self_prefix: bool, ) -> Vec { let func_meta = self.interner.function_meta(&func_id); @@ -223,6 +234,7 @@ impl<'a> NodeFinder<'a> { function_completion_kind, function_kind, attribute_first_type, + trait_info, self_prefix, is_macro_call, ) @@ -265,6 +277,7 @@ impl<'a> NodeFinder<'a> { function_completion_kind: FunctionCompletionKind, function_kind: FunctionKind, attribute_first_type: Option<&Type>, + trait_info: Option<(TraitId, Option<&TraitReexport>)>, self_prefix: bool, is_macro_call: bool, ) -> CompletionItem { @@ -325,7 +338,7 @@ impl<'a> NodeFinder<'a> { completion_item }; - let completion_item = match function_completion_kind { + let mut completion_item = match function_completion_kind { FunctionCompletionKind::Name => completion_item, FunctionCompletionKind::NameAndParameters => { if has_arguments { @@ -336,9 +349,69 @@ impl<'a> NodeFinder<'a> { } }; + self.auto_import_trait_if_trait_method(func_id, trait_info, &mut completion_item); + self.completion_item_with_doc_comments(ReferenceId::Function(func_id), completion_item) } + fn auto_import_trait_if_trait_method( + &self, + func_id: FuncId, + trait_info: Option<(TraitId, Option<&TraitReexport>)>, + completion_item: &mut CompletionItem, + ) -> Option<()> { + // If this is a trait method, check if the trait is in scope + let (trait_id, trait_reexport) = trait_info?; + + let trait_name = if let Some(trait_reexport) = trait_reexport { + trait_reexport.name + } else { + let trait_ = self.interner.get_trait(trait_id); + &trait_.name + }; + + let module_data = + &self.def_maps[&self.module_id.krate].modules()[self.module_id.local_id.0]; + if !module_data.scope().find_name(trait_name).is_none() { + return None; + } + + // If not, automatically import it + let current_module_parent_id = self.module_id.parent(self.def_maps); + let module_full_path = if let Some(reexport_data) = trait_reexport { + relative_module_id_path( + *reexport_data.module_id, + &self.module_id, + current_module_parent_id, + self.interner, + ) + } else { + relative_module_full_path( + ModuleDefId::FunctionId(func_id), + self.module_id, + current_module_parent_id, + self.interner, + )? + }; + let full_path = format!("{}::{}", module_full_path, trait_name); + let mut label_details = completion_item.label_details.clone().unwrap(); + label_details.detail = Some(format!("(use {})", full_path)); + completion_item.label_details = Some(label_details); + completion_item.additional_text_edits = Some(use_completion_item_additional_text_edits( + UseCompletionItemAdditionTextEditsRequest { + full_path: &full_path, + files: self.files, + file: self.file, + lines: &self.lines, + nesting: self.nesting, + auto_import_line: self.auto_import_line, + }, + &self.use_segment_positions, + )); + + None + } + fn compute_function_insert_text( &self, func_meta: &FuncMeta, diff --git a/tooling/lsp/src/requests/completion/tests.rs b/tooling/lsp/src/requests/completion/tests.rs index 65157090767..8ff568e3c26 100644 --- a/tooling/lsp/src/requests/completion/tests.rs +++ b/tooling/lsp/src/requests/completion/tests.rs @@ -2879,4 +2879,202 @@ fn main() { let items = get_completions(src).await; assert_eq!(items.len(), 1); } + + #[test] + async fn test_does_not_suggest_trait_function_not_visible() { + let src = r#" + mod moo { + trait Foo { + fn foobar(); + } + + impl Foo for Field { + fn foobar() {} + } + } + + fn main() { + Field::fooba>|< + } + + "#; + assert_completion(src, vec![]).await; + } + + #[test] + async fn test_suggests_multiple_trait_methods() { + let src = r#" + mod moo { + pub trait Foo { + fn foobar(); + } + + impl Foo for Field { + fn foobar() {} + } + + pub trait Bar { + fn foobar(); + } + + impl Bar for Field { + fn foobar() {} + } + } + + fn main() { + Field::fooba>|< + } + + "#; + let items = get_completions(src).await; + assert_eq!(items.len(), 2); + } + + #[test] + async fn test_suggests_and_imports_trait_method_without_self() { + let src = r#" +mod moo { + pub trait Foo { + fn foobar(); + } + + impl Foo for Field { + fn foobar() {} + } +} + +fn main() { + Field::fooba>|< +} + "#; + let mut items = get_completions(src).await; + assert_eq!(items.len(), 1); + + let item = items.remove(0); + assert_eq!(item.label_details.unwrap().detail, Some("(use moo::Foo)".to_string())); + + let new_code = + apply_text_edits(&src.replace(">|<", ""), &item.additional_text_edits.unwrap()); + + let expected = r#"use moo::Foo; + +mod moo { + pub trait Foo { + fn foobar(); + } + + impl Foo for Field { + fn foobar() {} + } +} + +fn main() { + Field::fooba +} + "#; + assert_eq!(new_code, expected); + } + + #[test] + async fn test_suggests_and_imports_trait_method_with_self() { + let src = r#" +mod moo { + pub trait Foo { + fn foobar(self); + } + + impl Foo for Field { + fn foobar(self) {} + } +} + +fn main() { + let x: Field = 1; + x.fooba>|< +} + "#; + let mut items = get_completions(src).await; + assert_eq!(items.len(), 1); + + let item = items.remove(0); + assert_eq!(item.label_details.unwrap().detail, Some("(use moo::Foo)".to_string())); + + let new_code = + apply_text_edits(&src.replace(">|<", ""), &item.additional_text_edits.unwrap()); + + let expected = r#"use moo::Foo; + +mod moo { + pub trait Foo { + fn foobar(self); + } + + impl Foo for Field { + fn foobar(self) {} + } +} + +fn main() { + let x: Field = 1; + x.fooba +} + "#; + assert_eq!(new_code, expected); + } + + #[test] + async fn test_suggests_and_imports_trait_method_with_self_using_public_export() { + let src = r#" +mod moo { + mod nested { + pub trait Foo { + fn foobar(self); + } + + impl Foo for Field { + fn foobar(self) {} + } + } + + pub use nested::Foo as Bar; +} + +fn main() { + let x: Field = 1; + x.fooba>|< +} + "#; + let mut items = get_completions(src).await; + assert_eq!(items.len(), 1); + + let item = items.remove(0); + assert_eq!(item.label_details.unwrap().detail, Some("(use moo::Bar)".to_string())); + + let new_code = + apply_text_edits(&src.replace(">|<", ""), &item.additional_text_edits.unwrap()); + + let expected = r#"use moo::Bar; + +mod moo { + mod nested { + pub trait Foo { + fn foobar(self); + } + + impl Foo for Field { + fn foobar(self) {} + } + } + + pub use nested::Foo as Bar; +} + +fn main() { + let x: Field = 1; + x.fooba +} + "#; + assert_eq!(new_code, expected); + } } diff --git a/tooling/lsp/src/requests/hover.rs b/tooling/lsp/src/requests/hover.rs index 9da24fd1c8a..5d8c50fa47b 100644 --- a/tooling/lsp/src/requests/hover.rs +++ b/tooling/lsp/src/requests/hover.rs @@ -399,7 +399,10 @@ fn format_function(id: FuncId, args: &ProcessRequestCallbackArgs) -> String { } string.push_str(" "); - if func_modifiers.visibility != ItemVisibility::Private { + if func_modifiers.visibility != ItemVisibility::Private + && func_meta.trait_id.is_none() + && func_meta.trait_impl.is_none() + { string.push_str(&func_modifiers.visibility.to_string()); string.push(' '); } @@ -718,7 +721,7 @@ impl<'a> TypeLinksGatherer<'a> { self.gather_type_links(env); } Type::MutableReference(typ) => self.gather_type_links(typ), - Type::InfixExpr(lhs, _, rhs) => { + Type::InfixExpr(lhs, _, rhs, _) => { self.gather_type_links(lhs); self.gather_type_links(rhs); } @@ -1154,7 +1157,7 @@ mod hover_tests { assert!(hover_text.starts_with( " two impl Bar for Foo - pub fn bar_stuff(self)" + fn bar_stuff(self)" )); } diff --git a/tooling/lsp/src/requests/mod.rs b/tooling/lsp/src/requests/mod.rs index d81108c2ec5..80f4a167a04 100644 --- a/tooling/lsp/src/requests/mod.rs +++ b/tooling/lsp/src/requests/mod.rs @@ -16,8 +16,9 @@ use lsp_types::{ }; use nargo_fmt::Config; +use noirc_frontend::ast::Ident; use noirc_frontend::graph::CrateId; -use noirc_frontend::hir::def_map::CrateDefMap; +use noirc_frontend::hir::def_map::{CrateDefMap, ModuleId}; use noirc_frontend::parser::ParserError; use noirc_frontend::usage_tracker::UsageTracker; use noirc_frontend::{graph::Dependency, node_interner::NodeInterner}; @@ -619,6 +620,12 @@ pub(crate) fn find_all_references( .unwrap_or_default() } +/// Represents a trait reexported from a given module with a name. +pub(crate) struct TraitReexport<'a> { + pub(super) module_id: &'a ModuleId, + pub(super) name: &'a Ident, +} + #[cfg(test)] mod initialization { use acvm::blackbox_solver::StubbedBlackBoxSolver; diff --git a/tooling/lsp/src/requests/test_run.rs b/tooling/lsp/src/requests/test_run.rs index 1071866dfad..bd53526298e 100644 --- a/tooling/lsp/src/requests/test_run.rs +++ b/tooling/lsp/src/requests/test_run.rs @@ -93,6 +93,7 @@ fn on_test_run_request_inner( |output, base| { DefaultForeignCallBuilder { output, + enable_mocks: true, resolver_url: None, // NB without this the root and package don't do anything. root_path: Some(workspace.root_dir.clone()), package_name: Some(package.name.to_string()), diff --git a/tooling/lsp/src/trait_impl_method_stub_generator.rs b/tooling/lsp/src/trait_impl_method_stub_generator.rs index 2ae0d526f3e..eb1709e34d0 100644 --- a/tooling/lsp/src/trait_impl_method_stub_generator.rs +++ b/tooling/lsp/src/trait_impl_method_stub_generator.rs @@ -361,7 +361,7 @@ impl<'a> TraitImplMethodStubGenerator<'a> { Type::Forall(_, _) => { panic!("Shouldn't get a Type::Forall"); } - Type::InfixExpr(left, op, right) => { + Type::InfixExpr(left, op, right, _) => { self.append_type(left); self.string.push(' '); self.string.push_str(&op.to_string()); diff --git a/tooling/nargo/src/foreign_calls/default.rs b/tooling/nargo/src/foreign_calls/default.rs index ce4af3aa744..19928e89563 100644 --- a/tooling/nargo/src/foreign_calls/default.rs +++ b/tooling/nargo/src/foreign_calls/default.rs @@ -4,8 +4,8 @@ use serde::{Deserialize, Serialize}; use crate::PrintOutput; use super::{ - layers::{self, Layer, Layering}, - mocker::MockForeignCallExecutor, + layers::{self, Either, Layer, Layering}, + mocker::{DisabledMockForeignCallExecutor, MockForeignCallExecutor}, print::PrintForeignCallExecutor, ForeignCallExecutor, }; @@ -15,17 +15,38 @@ use super::rpc::RPCForeignCallExecutor; /// A builder for [DefaultForeignCallLayers] where we can enable fields based on feature flags, /// which is easier than providing different overrides for a `new` method. -#[derive(Default)] pub struct DefaultForeignCallBuilder<'a> { pub output: PrintOutput<'a>, + pub enable_mocks: bool, + #[cfg(feature = "rpc")] pub resolver_url: Option, + #[cfg(feature = "rpc")] pub root_path: Option, + #[cfg(feature = "rpc")] pub package_name: Option, } +impl<'a> Default for DefaultForeignCallBuilder<'a> { + fn default() -> Self { + Self { + output: PrintOutput::default(), + enable_mocks: true, + + #[cfg(feature = "rpc")] + resolver_url: None, + + #[cfg(feature = "rpc")] + root_path: None, + + #[cfg(feature = "rpc")] + package_name: None, + } + } +} + impl<'a> DefaultForeignCallBuilder<'a> { /// Override the output. pub fn with_output(mut self, output: PrintOutput<'a>) -> Self { @@ -33,6 +54,12 @@ impl<'a> DefaultForeignCallBuilder<'a> { self } + /// Enable or disable mocks. + pub fn with_mocks(mut self, enabled: bool) -> Self { + self.enable_mocks = enabled; + self + } + /// Compose the executor layers with [layers::Empty] as the default handler. pub fn build(self) -> DefaultForeignCallLayers<'a, layers::Empty, F> where @@ -69,7 +96,11 @@ impl<'a> DefaultForeignCallBuilder<'a> { }; executor - .add_layer(MockForeignCallExecutor::default()) + .add_layer(if self.enable_mocks { + Either::Left(MockForeignCallExecutor::default()) + } else { + Either::Right(DisabledMockForeignCallExecutor) + }) .add_layer(PrintForeignCallExecutor::new(self.output)) } } @@ -78,11 +109,16 @@ impl<'a> DefaultForeignCallBuilder<'a> { #[cfg(feature = "rpc")] pub type DefaultForeignCallLayers<'a, B, F> = Layer< PrintForeignCallExecutor<'a>, - Layer, Layer, B>>, + Layer< + Either, DisabledMockForeignCallExecutor>, + Layer, B>, + >, >; #[cfg(not(feature = "rpc"))] -pub type DefaultForeignCallLayers<'a, B, F> = - Layer, Layer, B>>; +pub type DefaultForeignCallLayers<'a, B, F> = Layer< + PrintForeignCallExecutor<'a>, + Layer, DisabledMockForeignCallExecutor>, B>, +>; /// Convenience constructor for code that used to create the executor this way. #[cfg(feature = "rpc")] @@ -106,6 +142,7 @@ impl DefaultForeignCallExecutor { { DefaultForeignCallBuilder { output, + enable_mocks: true, resolver_url: resolver_url.map(|s| s.to_string()), root_path, package_name, diff --git a/tooling/nargo/src/foreign_calls/layers.rs b/tooling/nargo/src/foreign_calls/layers.rs index 19f14c6f4a8..83145cacb44 100644 --- a/tooling/nargo/src/foreign_calls/layers.rs +++ b/tooling/nargo/src/foreign_calls/layers.rs @@ -121,6 +121,28 @@ impl Layering for T { } } +/// A case where we can have either this or that type of handler. +pub enum Either { + Left(L), + Right(R), +} + +impl ForeignCallExecutor for Either +where + L: ForeignCallExecutor, + R: ForeignCallExecutor, +{ + fn execute( + &mut self, + foreign_call: &ForeignCallWaitInfo, + ) -> Result, ForeignCallError> { + match self { + Either::Left(left) => left.execute(foreign_call), + Either::Right(right) => right.execute(foreign_call), + } + } +} + /// Support disabling a layer by making it optional. /// This way we can still have a known static type for a composition, /// because layers are always added, potentially wrapped in an `Option`. diff --git a/tooling/nargo/src/foreign_calls/mocker.rs b/tooling/nargo/src/foreign_calls/mocker.rs index b289e907cd7..41fac610052 100644 --- a/tooling/nargo/src/foreign_calls/mocker.rs +++ b/tooling/nargo/src/foreign_calls/mocker.rs @@ -1,5 +1,3 @@ -use std::marker::PhantomData; - use acvm::{ acir::brillig::{ForeignCallParam, ForeignCallResult}, pwg::ForeignCallWaitInfo, @@ -178,12 +176,9 @@ where } /// Handler that panics if any of the mock functions are called. -#[allow(dead_code)] // TODO: Make the mocker optional -pub(crate) struct DisabledMockForeignCallExecutor { - _field: PhantomData, -} +pub struct DisabledMockForeignCallExecutor; -impl ForeignCallExecutor for DisabledMockForeignCallExecutor { +impl ForeignCallExecutor for DisabledMockForeignCallExecutor { fn execute( &mut self, foreign_call: &ForeignCallWaitInfo, @@ -198,7 +193,8 @@ impl ForeignCallExecutor for DisabledMockForeignCallExecutor { | ForeignCall::ClearMock, ) = ForeignCall::lookup(foreign_call_name) { - panic!("unexpected mock call: {}", foreign_call.function) + // Returning an error instead of panicking so this can be tested. + return Err(ForeignCallError::Disabled(foreign_call.function.to_string())); } Err(ForeignCallError::NoHandler(foreign_call.function.clone())) } diff --git a/tooling/nargo/src/foreign_calls/mod.rs b/tooling/nargo/src/foreign_calls/mod.rs index 06fe42bfec9..f17a97cecd4 100644 --- a/tooling/nargo/src/foreign_calls/mod.rs +++ b/tooling/nargo/src/foreign_calls/mod.rs @@ -66,6 +66,9 @@ impl ForeignCall { #[derive(Debug, Error)] pub enum ForeignCallError { + #[error("Attempted to call disabled foreign call `{0}`")] + Disabled(String), + #[error("No handler could be found for foreign call `{0}`")] NoHandler(String), diff --git a/tooling/nargo_cli/src/cli/check_cmd.rs b/tooling/nargo_cli/src/cli/check_cmd.rs index c8695a8f626..949a19ca655 100644 --- a/tooling/nargo_cli/src/cli/check_cmd.rs +++ b/tooling/nargo_cli/src/cli/check_cmd.rs @@ -26,7 +26,7 @@ pub(crate) struct CheckCommand { /// Force overwrite of existing files #[clap(long = "overwrite")] - allow_overwrite: bool, + pub(super) allow_overwrite: bool, #[clap(flatten)] compile_options: CompileOptions, diff --git a/tooling/nargo_cli/src/cli/execute_cmd.rs b/tooling/nargo_cli/src/cli/execute_cmd.rs index 88e74340c71..cb471995be5 100644 --- a/tooling/nargo_cli/src/cli/execute_cmd.rs +++ b/tooling/nargo_cli/src/cli/execute_cmd.rs @@ -7,7 +7,7 @@ use clap::Args; use nargo::constants::PROVER_INPUT_FILE; use nargo::errors::try_to_diagnose_runtime_error; -use nargo::foreign_calls::DefaultForeignCallExecutor; +use nargo::foreign_calls::DefaultForeignCallBuilder; use nargo::package::Package; use nargo::PrintOutput; use nargo_toml::{get_package_manifest, resolve_workspace_from_toml}; @@ -150,12 +150,14 @@ pub(crate) fn execute_program( &compiled_program.program, initial_witness, &Bn254BlackBoxSolver(pedantic_solving), - &mut DefaultForeignCallExecutor::new( - PrintOutput::Stdout, - foreign_call_resolver_url, + &mut DefaultForeignCallBuilder { + output: PrintOutput::Stdout, + enable_mocks: false, + resolver_url: foreign_call_resolver_url.map(|s| s.to_string()), root_path, package_name, - ), + } + .build(), ); match solved_witness_stack_err { Ok(solved_witness_stack) => Ok(solved_witness_stack), diff --git a/tooling/nargo_cli/src/cli/mod.rs b/tooling/nargo_cli/src/cli/mod.rs index 0b725afcf4e..8a9e34b2730 100644 --- a/tooling/nargo_cli/src/cli/mod.rs +++ b/tooling/nargo_cli/src/cli/mod.rs @@ -224,10 +224,14 @@ fn command_dir(cmd: &NargoCommand, program_dir: &Path) -> Result Ok(Some(nargo_toml::find_root(program_dir, workspace)?)) } +/// Returns: +/// - `Some(true)` if an exclusive lock is needed +/// - `Some(false)` if an read lock is needed +/// - None if no lock is needed fn needs_lock(cmd: &NargoCommand) -> Option { match cmd { - NargoCommand::Check(..) - | NargoCommand::Compile(..) + NargoCommand::Check(check_command) => Some(check_command.allow_overwrite), + NargoCommand::Compile(..) | NargoCommand::Execute(..) | NargoCommand::Export(..) | NargoCommand::Info(..) => Some(true), diff --git a/tooling/nargo_cli/src/cli/test_cmd.rs b/tooling/nargo_cli/src/cli/test_cmd.rs index 43f1c306d88..9bf3ae9fedf 100644 --- a/tooling/nargo_cli/src/cli/test_cmd.rs +++ b/tooling/nargo_cli/src/cli/test_cmd.rs @@ -499,6 +499,7 @@ impl<'a> TestRunner<'a> { |output, base| { DefaultForeignCallBuilder { output, + enable_mocks: true, resolver_url: foreign_call_resolver_url.map(|s| s.to_string()), root_path: root_path.clone(), package_name: Some(package_name.clone()), diff --git a/tooling/nargo_cli/tests/stdlib-tests.rs b/tooling/nargo_cli/tests/stdlib-tests.rs index b162b282781..048de33f24c 100644 --- a/tooling/nargo_cli/tests/stdlib-tests.rs +++ b/tooling/nargo_cli/tests/stdlib-tests.rs @@ -92,7 +92,7 @@ fn run_stdlib_tests(force_brillig: bool, inliner_aggressiveness: i64) { PrintOutput::Stdout, &CompileOptions { force_brillig, inliner_aggressiveness, ..Default::default() }, |output, base| { - DefaultForeignCallBuilder { output, ..Default::default() }.build_with_base(base) + DefaultForeignCallBuilder::default().with_output(output).build_with_base(base) }, ); (test_name, status) diff --git a/tooling/nargo_fmt/src/formatter.rs b/tooling/nargo_fmt/src/formatter.rs index 4184ff288d7..2a8adb3fb28 100644 --- a/tooling/nargo_fmt/src/formatter.rs +++ b/tooling/nargo_fmt/src/formatter.rs @@ -14,6 +14,7 @@ mod attribute; mod buffer; mod comments_and_whitespace; mod doc_comments; +mod enums; mod expression; mod function; mod generics; diff --git a/tooling/nargo_fmt/src/formatter/enums.rs b/tooling/nargo_fmt/src/formatter/enums.rs new file mode 100644 index 00000000000..b596ec95c94 --- /dev/null +++ b/tooling/nargo_fmt/src/formatter/enums.rs @@ -0,0 +1,202 @@ +use noirc_frontend::{ + ast::NoirEnumeration, + token::{Keyword, Token}, +}; + +use super::Formatter; +use crate::chunks::ChunkGroup; + +impl<'a> Formatter<'a> { + pub(super) fn format_enum(&mut self, noir_enum: NoirEnumeration) { + self.format_secondary_attributes(noir_enum.attributes); + self.write_indentation(); + self.format_item_visibility(noir_enum.visibility); + self.write_keyword(Keyword::Enum); + self.write_space(); + self.write_identifier(noir_enum.name); + self.format_generics(noir_enum.generics); + self.skip_comments_and_whitespace(); + + // A case like `enum Foo;` + if self.is_at(Token::Semicolon) { + self.write_semicolon(); + return; + } + + // A case like `enum Foo { ... }` + self.write_space(); + self.write_left_brace(); + + if noir_enum.variants.is_empty() { + self.format_empty_block_contents(); + } else { + self.increase_indentation(); + self.write_line(); + + for (index, documented_variant) in noir_enum.variants.into_iter().enumerate() { + if index > 0 { + self.write_comma(); + self.write_line(); + } + + let doc_comments = documented_variant.doc_comments; + if !doc_comments.is_empty() { + self.format_outer_doc_comments(); + } + + let variant = documented_variant.item; + self.write_indentation(); + self.write_identifier(variant.name); + + if !variant.parameters.is_empty() { + self.write_token(Token::LeftParen); + for (i, parameter) in variant.parameters.into_iter().enumerate() { + if i != 0 { + self.write_comma(); + self.write_space(); + } + self.format_type(parameter); + } + self.write_token(Token::RightParen); + } else { + // Remove `()` from an empty `Variant()` + self.skip_comments_and_whitespace(); + if self.is_at(Token::LeftParen) { + self.bump(); + } + self.skip_comments_and_whitespace(); + if self.is_at(Token::RightParen) { + self.bump(); + } + } + } + + // Take the comment chunk so we can put it after a trailing comma we add, in case there's no comma + let mut group = ChunkGroup::new(); + let mut comments_and_whitespace_chunk = + self.chunk_formatter().skip_comments_and_whitespace_chunk(); + comments_and_whitespace_chunk.string = + comments_and_whitespace_chunk.string.trim_end().to_string(); + group.text(comments_and_whitespace_chunk); + + if self.is_at(Token::Comma) { + self.bump(); + } + self.write(","); + + self.format_chunk_group(group); + self.skip_comments_and_whitespace(); + + self.decrease_indentation(); + self.write_line(); + self.write_indentation(); + } + + self.write_right_brace(); + } +} + +#[cfg(test)] +mod tests { + use crate::assert_format; + + #[test] + fn format_empty_enum_with_generics() { + let src = " mod moo { enum Foo < A, B, let N : u32 > {} }"; + let expected = "mod moo { + enum Foo {} +} +"; + assert_format(src, expected); + } + + #[test] + fn format_enum_with_variants() { + let src = " mod moo { enum Foo { +// hello +/// comment + Variant ( Field , i32 ) , + // comment + Another ( ), + } }"; + let expected = "mod moo { + enum Foo { + // hello + /// comment + Variant(Field, i32), + // comment + Another, + } +} +"; + assert_format(src, expected); + } + + #[test] + fn format_enum_with_multiple_newlines() { + let src = " mod moo { + + + enum Foo { + + +X( Field) , + + +Y ( Field ) + + +} + + +}"; + let expected = "mod moo { + + enum Foo { + + X(Field), + + Y(Field), + } + +} +"; + assert_format(src, expected); + } + + #[test] + fn format_two_enums() { + let src = " enum Foo { } enum Bar {} + "; + let expected = "enum Foo {} +enum Bar {} +"; + assert_format(src, expected); + } + + #[test] + fn format_enum_field_without_trailing_comma_but_comment() { + let src = "enum Foo { + field(Field) // comment + }"; + let expected = "enum Foo { + field(Field), // comment +} +"; + assert_format(src, expected); + } + + #[test] + fn format_comment_after_last_enum_field() { + let src = "enum Foo { + field(Field) + /* comment */ + }"; + let expected = "enum Foo { + field(Field), + /* comment */ +} +"; + assert_format(src, expected); + } +} diff --git a/tooling/nargo_fmt/src/formatter/item.rs b/tooling/nargo_fmt/src/formatter/item.rs index 3365e52ec29..499acb8415c 100644 --- a/tooling/nargo_fmt/src/formatter/item.rs +++ b/tooling/nargo_fmt/src/formatter/item.rs @@ -63,6 +63,7 @@ impl<'a> Formatter<'a> { false, // skip visibility ), ItemKind::Struct(noir_struct) => self.format_struct(noir_struct), + ItemKind::Enum(noir_enum) => self.format_enum(noir_enum), ItemKind::Trait(noir_trait) => self.format_trait(noir_trait), ItemKind::TraitImpl(noir_trait_impl) => self.format_trait_impl(noir_trait_impl), ItemKind::Impl(type_impl) => self.format_impl(type_impl), diff --git a/tooling/nargo_fmt/src/formatter/statement.rs b/tooling/nargo_fmt/src/formatter/statement.rs index 1736197403c..983fb00db16 100644 --- a/tooling/nargo_fmt/src/formatter/statement.rs +++ b/tooling/nargo_fmt/src/formatter/statement.rs @@ -23,10 +23,17 @@ impl<'a, 'b> ChunkFormatter<'a, 'b> { // Now write any leading comment respecting multiple newlines after them group.leading_comment(self.chunk(|formatter| { + // Doc comments for a let statement could come before a potential non-doc comment if formatter.token.kind() == TokenKind::OuterDocComment { formatter.format_outer_doc_comments(); } + formatter.skip_comments_and_whitespace_writing_multiple_lines_if_found(); + + // Or doc comments could come after a potential non-doc comment + if formatter.token.kind() == TokenKind::OuterDocComment { + formatter.format_outer_doc_comments(); + } })); ignore_next |= self.ignore_next; @@ -68,6 +75,9 @@ impl<'a, 'b> ChunkFormatter<'a, 'b> { StatementKind::For(for_loop_statement) => { group.group(self.format_for_loop(for_loop_statement)); } + StatementKind::Loop(block) => { + group.group(self.format_loop(block)); + } StatementKind::Break => { group.text(self.chunk(|formatter| { formatter.write_keyword(Keyword::Break); @@ -270,6 +280,34 @@ impl<'a, 'b> ChunkFormatter<'a, 'b> { group } + fn format_loop(&mut self, block: Expression) -> ChunkGroup { + let mut group = ChunkGroup::new(); + + group.text(self.chunk(|formatter| { + formatter.write_keyword(Keyword::Loop); + })); + + group.space(self); + + let ExpressionKind::Block(block) = block.kind else { + panic!("Expected a block expression for loop body"); + }; + + group.group(self.format_block_expression( + block, true, // force multiple lines + )); + + // If there's a trailing semicolon, remove it + group.text(self.chunk(|formatter| { + formatter.skip_whitespace_if_it_is_not_a_newline(); + if formatter.is_at(Token::Semicolon) { + formatter.bump(); + } + })); + + group + } + fn format_comptime_statement(&mut self, statement: Statement) -> ChunkGroup { let mut group = ChunkGroup::new(); @@ -390,6 +428,21 @@ mod tests { assert_format(src, expected); } + #[test] + fn format_let_statement_with_unsafe_and_comment_before_it() { + let src = " fn foo() { + // Some comment + /// Safety: some doc + let x = unsafe { 1 } ; } "; + let expected = "fn foo() { + // Some comment + /// Safety: some doc + let x = unsafe { 1 }; +} +"; + assert_format(src, expected); + } + #[test] fn format_assign() { let src = " fn foo() { x = 2 ; } "; @@ -727,4 +780,27 @@ mod tests { let expected = src; assert_format_with_max_width(src, expected, " let x = 123456;".len()); } + + #[test] + fn format_empty_loop() { + let src = " fn foo() { loop { } } "; + let expected = "fn foo() { + loop {} +} +"; + assert_format(src, expected); + } + + #[test] + fn format_non_empty_loop() { + let src = " fn foo() { loop { 1 ; 2 } } "; + let expected = "fn foo() { + loop { + 1; + 2 + } +} +"; + assert_format(src, expected); + } } diff --git a/tooling/nargo_fmt/src/formatter/use_tree_merge.rs b/tooling/nargo_fmt/src/formatter/use_tree_merge.rs index 76fdcd0d0f8..a679e026435 100644 --- a/tooling/nargo_fmt/src/formatter/use_tree_merge.rs +++ b/tooling/nargo_fmt/src/formatter/use_tree_merge.rs @@ -179,7 +179,12 @@ impl Ord for Segment { if let (Segment::Plain(self_string), Segment::Plain(other_string)) = (self, other) { // Case-insensitive comparison for plain segments - self_string.to_lowercase().cmp(&other_string.to_lowercase()) + let ordering = self_string.to_lowercase().cmp(&other_string.to_lowercase()); + if ordering == Ordering::Equal { + self_string.cmp(other_string) + } else { + ordering + } } else { order_number_ordering } @@ -620,4 +625,10 @@ use std::merkle::compute_merkle_root; let expected = "use std::{as_witness, merkle::compute_merkle_root};\n"; assert_format(src, expected); } + + #[test] + fn does_not_merge_same_identifiers_if_equal_case_insensitive() { + let src = "use bigint::{BigNum, bignum::BigNumTrait};\n"; + assert_format(src, src); + } } diff --git a/yarn.lock b/yarn.lock index 77962512b08..44bff5a28c7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -353,6 +353,19 @@ __metadata: languageName: node linkType: hard +"@babel/generator@npm:^7.14.5": + version: 7.26.5 + resolution: "@babel/generator@npm:7.26.5" + dependencies: + "@babel/parser": ^7.26.5 + "@babel/types": ^7.26.5 + "@jridgewell/gen-mapping": ^0.3.5 + "@jridgewell/trace-mapping": ^0.3.25 + jsesc: ^3.0.2 + checksum: baa42a98cd01efa3ae3634a6caa81d0738e5e0bdba4efbf1ac735216c8d7cf6bdffeab69c468e6ab2063b07db402346113def4962719746756518432f83c53ba + languageName: node + linkType: hard + "@babel/generator@npm:^7.25.0, @babel/generator@npm:^7.25.6": version: 7.25.6 resolution: "@babel/generator@npm:7.25.6" @@ -765,6 +778,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-string-parser@npm:^7.25.9": + version: 7.25.9 + resolution: "@babel/helper-string-parser@npm:7.25.9" + checksum: 6435ee0849e101681c1849868278b5aee82686ba2c1e27280e5e8aca6233af6810d39f8e4e693d2f2a44a3728a6ccfd66f72d71826a94105b86b731697cdfa99 + languageName: node + linkType: hard + "@babel/helper-validator-identifier@npm:^7.22.20": version: 7.22.20 resolution: "@babel/helper-validator-identifier@npm:7.22.20" @@ -779,6 +799,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-validator-identifier@npm:^7.25.9": + version: 7.25.9 + resolution: "@babel/helper-validator-identifier@npm:7.25.9" + checksum: 5b85918cb1a92a7f3f508ea02699e8d2422fe17ea8e82acd445006c0ef7520fbf48e3dbcdaf7b0a1d571fc3a2715a29719e5226636cb6042e15fe6ed2a590944 + languageName: node + linkType: hard + "@babel/helper-validator-option@npm:^7.22.15, @babel/helper-validator-option@npm:^7.23.5": version: 7.23.5 resolution: "@babel/helper-validator-option@npm:7.23.5" @@ -879,6 +906,17 @@ __metadata: languageName: node linkType: hard +"@babel/parser@npm:^7.26.5": + version: 7.26.5 + resolution: "@babel/parser@npm:7.26.5" + dependencies: + "@babel/types": ^7.26.5 + bin: + parser: ./bin/babel-parser.js + checksum: 663aebf27c1dc04813e6c1d6e8e8fcb2954163ec297a95bdb3f1d0c2a0f04b504bddc09588fe4b176b43fad28c8a4b2914838a1edffdd426537a42f3ac644f1e + languageName: node + linkType: hard + "@babel/plugin-bugfix-firefox-class-in-computed-class-key@npm:^7.25.3": version: 7.25.3 resolution: "@babel/plugin-bugfix-firefox-class-in-computed-class-key@npm:7.25.3" @@ -2825,6 +2863,15 @@ __metadata: languageName: node linkType: hard +"@babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.20.13, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.8.7": + version: 7.26.0 + resolution: "@babel/runtime@npm:7.26.0" + dependencies: + regenerator-runtime: ^0.14.0 + checksum: c8e2c0504ab271b3467a261a8f119bf2603eb857a0d71e37791f4e3fae00f681365073cc79f141ddaa90c6077c60ba56448004ad5429d07ac73532be9f7cf28a + languageName: node + linkType: hard + "@babel/template@npm:^7.12.7, @babel/template@npm:^7.22.15": version: 7.22.15 resolution: "@babel/template@npm:7.22.15" @@ -2891,6 +2938,16 @@ __metadata: languageName: node linkType: hard +"@babel/types@npm:^7.14.5, @babel/types@npm:^7.20.7, @babel/types@npm:^7.26.5": + version: 7.26.5 + resolution: "@babel/types@npm:7.26.5" + dependencies: + "@babel/helper-string-parser": ^7.25.9 + "@babel/helper-validator-identifier": ^7.25.9 + checksum: 65dc14aa32ace22655c5edadeb99df80776c09cd93c105feaf49cc0583f3116aff0581b7eab630888c39ba61151f251c1399ec982b93585b0d1d1bf4a45b54f9 + languageName: node + linkType: hard + "@babel/types@npm:^7.21.3, @babel/types@npm:^7.24.7, @babel/types@npm:^7.24.8, @babel/types@npm:^7.25.0, @babel/types@npm:^7.25.2, @babel/types@npm:^7.25.6": version: 7.25.6 resolution: "@babel/types@npm:7.25.6" @@ -2909,6 +2966,87 @@ __metadata: languageName: node linkType: hard +"@cookbookdev/docsbot@npm:^4.24.9": + version: 4.24.9 + resolution: "@cookbookdev/docsbot@npm:4.24.9" + dependencies: + "@cookbookdev/sonner": 1.5.1 + "@headlessui/react": ^1.7.18 + "@headlessui/tailwindcss": ^0.2.0 + "@lingui/detect-locale": ^4.11.4 + "@lingui/macro": ^4.11.4 + "@lingui/react": ^4.11.4 + "@lingui/remote-loader": ^3.11.0 + "@monaco-editor/react": ^4.6.0 + "@radix-ui/react-avatar": ^1.0.4 + "@radix-ui/react-checkbox": ^1.0.4 + "@radix-ui/react-collapsible": ^1.0.3 + "@radix-ui/react-dialog": ^1.0.5 + "@radix-ui/react-dropdown-menu": ^2.0.6 + "@radix-ui/react-hover-card": ^1.0.7 + "@radix-ui/react-icons": ^1.3.0 + "@radix-ui/react-label": ^2.0.2 + "@radix-ui/react-popover": ^1.0.7 + "@radix-ui/react-scroll-area": ^1.0.5 + "@radix-ui/react-select": ^2.0.0 + "@radix-ui/react-separator": ^1.0.3 + "@radix-ui/react-slider": ^1.1.2 + "@radix-ui/react-slot": ^1.0.2 + "@radix-ui/react-tabs": ^1.0.4 + "@radix-ui/react-toast": ^1.1.5 + "@radix-ui/react-tooltip": ^1.0.7 + "@shikijs/rehype": ^1.12.1 + "@statsig/js-client": ^3.1.0 + "@statsig/react-bindings": ^3.1.0 + "@tailwindcss/line-clamp": ^0.4.4 + "@tanstack/react-table": ^8.11.7 + "@vercel/edge": ^1.1.1 + bcp-47: ^2.1.0 + can-dom-mutate: ^2.0.9 + clsx: ^2.1.0 + cmdk-react17: ^1.0.0 + color2k: ^2.0.3 + fflate: ^0.8.2 + file-saver: ^2.0.5 + framer-motion: ^6.5.1 + idb: ^8.0.0 + iso-639-1: ^3.1.3 + jszip: ^3.10.1 + nanoid: 3.3.7 + posthog-js: ^1.136.8 + prop-types: ^15.8.1 + react-complex-tree: ^2.3.7 + react-remark: ^2.1.0 + react-resizable-panels: 2.0.19 + recharts: ^2.12.4 + rehype-react: ^8.0.0 + remark-gfm: ^4.0.0 + remark-parse: ^11.0.0 + remark-rehype: ^11.1.0 + solc: ^0.8.25 + styled-components: ^6.1.8 + swr: ^2.2.5 + tailwind-merge: ^2.2.1 + tailwindcss-animate: ^1.0.7 + use-sync-external-store: ^1.2.0 + viem: 2.9.16 + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + checksum: d1264012469a410d7232c3fa82f2855a2d603806142746ea16c05c21fda54632475214cbe1a2c390ee001d37823277c9d4e2d821d6a826e3cd5acef5b1da1b66 + languageName: node + linkType: hard + +"@cookbookdev/sonner@npm:1.5.1": + version: 1.5.1 + resolution: "@cookbookdev/sonner@npm:1.5.1" + peerDependencies: + react: ^18.0.0 + react-dom: ^18.0.0 + checksum: 658774d5055f47e1ccb64f97e9affc149bba6042b15318cd861ce790550041c9a3c96605b773a7853e32af0980d37eabd6db1077d0b2a103495ea9d025d4ff4e + languageName: node + linkType: hard + "@cspell/cspell-bundled-dicts@npm:8.3.2": version: 8.3.2 resolution: "@cspell/cspell-bundled-dicts@npm:8.3.2" @@ -4257,6 +4395,45 @@ __metadata: languageName: node linkType: hard +"@emotion/is-prop-valid@npm:1.2.2": + version: 1.2.2 + resolution: "@emotion/is-prop-valid@npm:1.2.2" + dependencies: + "@emotion/memoize": ^0.8.1 + checksum: 61f6b128ea62b9f76b47955057d5d86fcbe2a6989d2cd1e583daac592901a950475a37d049b9f7a7c6aa8758a33b408735db759fdedfd1f629df0f85ab60ea25 + languageName: node + linkType: hard + +"@emotion/is-prop-valid@npm:^0.8.2": + version: 0.8.8 + resolution: "@emotion/is-prop-valid@npm:0.8.8" + dependencies: + "@emotion/memoize": 0.7.4 + checksum: bb7ec6d48c572c540e24e47cc94fc2f8dec2d6a342ae97bc9c8b6388d9b8d283862672172a1bb62d335c02662afe6291e10c71e9b8642664a8b43416cdceffac + languageName: node + linkType: hard + +"@emotion/memoize@npm:0.7.4": + version: 0.7.4 + resolution: "@emotion/memoize@npm:0.7.4" + checksum: 4e3920d4ec95995657a37beb43d3f4b7d89fed6caa2b173a4c04d10482d089d5c3ea50bbc96618d918b020f26ed6e9c4026bbd45433566576c1f7b056c3271dc + languageName: node + linkType: hard + +"@emotion/memoize@npm:^0.8.1": + version: 0.8.1 + resolution: "@emotion/memoize@npm:0.8.1" + checksum: a19cc01a29fcc97514948eaab4dc34d8272e934466ed87c07f157887406bc318000c69ae6f813a9001c6a225364df04249842a50e692ef7a9873335fbcc141b0 + languageName: node + linkType: hard + +"@emotion/unitless@npm:0.8.1": + version: 0.8.1 + resolution: "@emotion/unitless@npm:0.8.1" + checksum: 385e21d184d27853bb350999471f00e1429fa4e83182f46cd2c164985999d9b46d558dc8b9cc89975cb337831ce50c31ac2f33b15502e85c299892e67e7b4a88 + languageName: node + linkType: hard + "@esbuild/android-arm64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/android-arm64@npm:0.17.19" @@ -4836,6 +5013,44 @@ __metadata: languageName: node linkType: hard +"@floating-ui/core@npm:^1.6.0": + version: 1.6.9 + resolution: "@floating-ui/core@npm:1.6.9" + dependencies: + "@floating-ui/utils": ^0.2.9 + checksum: 21cbcac72a40172399570dedf0eb96e4f24b0d829980160e8d14edf08c2955ac6feffb7b94e1530c78fb7944635e52669c9257ad08570e0295efead3b5a9af91 + languageName: node + linkType: hard + +"@floating-ui/dom@npm:^1.0.0": + version: 1.6.13 + resolution: "@floating-ui/dom@npm:1.6.13" + dependencies: + "@floating-ui/core": ^1.6.0 + "@floating-ui/utils": ^0.2.9 + checksum: eabab9d860d3b5beab1c2d6936287efc4d9ab352de99062380589ef62870d59e8730397489c34a96657e128498001b5672330c4a9da0159fe8b2401ac59fe314 + languageName: node + linkType: hard + +"@floating-ui/react-dom@npm:^2.0.0": + version: 2.1.2 + resolution: "@floating-ui/react-dom@npm:2.1.2" + dependencies: + "@floating-ui/dom": ^1.0.0 + peerDependencies: + react: ">=16.8.0" + react-dom: ">=16.8.0" + checksum: 25bb031686e23062ed4222a8946e76b3f9021d40a48437bd747233c4964a766204b8a55f34fa8b259839af96e60db7c6e3714d81f1de06914294f90e86ffbc48 + languageName: node + linkType: hard + +"@floating-ui/utils@npm:^0.2.9": + version: 0.2.9 + resolution: "@floating-ui/utils@npm:0.2.9" + checksum: d518b80cec5a323e54a069a1dd99a20f8221a4853ed98ac16c75275a0cc22f75de4f8ac5b121b4f8990bd45da7ad1fb015b9a1e4bac27bb1cd62444af84e9784 + languageName: node + linkType: hard + "@hapi/hoek@npm:^9.0.0": version: 9.3.0 resolution: "@hapi/hoek@npm:9.3.0" @@ -4852,6 +5067,28 @@ __metadata: languageName: node linkType: hard +"@headlessui/react@npm:^1.7.18": + version: 1.7.19 + resolution: "@headlessui/react@npm:1.7.19" + dependencies: + "@tanstack/react-virtual": ^3.0.0-beta.60 + client-only: ^0.0.1 + peerDependencies: + react: ^16 || ^17 || ^18 + react-dom: ^16 || ^17 || ^18 + checksum: 2a343a5fcf1f45e870cc94613231b89a8da78114001ffafa4751a0eceae7569ff9237aff1f2aedfa6f6e53ee3bb9ba5e5d19ebf1878fee3ff4f3c733fddc1087 + languageName: node + linkType: hard + +"@headlessui/tailwindcss@npm:^0.2.0": + version: 0.2.1 + resolution: "@headlessui/tailwindcss@npm:0.2.1" + peerDependencies: + tailwindcss: ^3.0 + checksum: 5cc9b30af4348dd133ce7fe6b7bdae9bec681796480cbd91cc8d20cd8a0841169ac741ec04ded4235c520199d79775ab439489d869b2785cc58a94002193a6f0 + languageName: node + linkType: hard + "@humanwhocodes/config-array@npm:^0.11.14": version: 0.11.14 resolution: "@humanwhocodes/config-array@npm:0.11.14" @@ -5028,6 +5265,89 @@ __metadata: languageName: node linkType: hard +"@lingui/conf@npm:4.14.1": + version: 4.14.1 + resolution: "@lingui/conf@npm:4.14.1" + dependencies: + "@babel/runtime": ^7.20.13 + chalk: ^4.1.0 + cosmiconfig: ^8.0.0 + jest-validate: ^29.4.3 + jiti: ^1.17.1 + lodash.get: ^4.4.2 + checksum: 6108724647a85c2114fb5bd3212de16f56f1495bf94fffa3fb944a1187e7e30df63947628534bc9880483e3b290761287e317ec48d3c12fe90d654332d199b78 + languageName: node + linkType: hard + +"@lingui/core@npm:4.14.1": + version: 4.14.1 + resolution: "@lingui/core@npm:4.14.1" + dependencies: + "@babel/runtime": ^7.20.13 + "@lingui/message-utils": 4.14.1 + unraw: ^3.0.0 + checksum: 6bf82be81a84a7e5a34a5a7a3d9d503b4eca6d310655a306549e575a50aae3c6e6964022ab98229c7cbeef45ab368d5c6bc6a713abefc367f1f16b1d80e9d226 + languageName: node + linkType: hard + +"@lingui/detect-locale@npm:^4.11.4": + version: 4.14.1 + resolution: "@lingui/detect-locale@npm:4.14.1" + checksum: 5d2e06bb11a98634eb62dfaa587baa2aaaf6e5f4e577447eac67bacae37c7aa85fac835272080cc07bb07b27b96351ddc6bd630bec7fe852e346b2b5d281fb8d + languageName: node + linkType: hard + +"@lingui/macro@npm:^4.11.4": + version: 4.14.1 + resolution: "@lingui/macro@npm:4.14.1" + dependencies: + "@babel/runtime": ^7.20.13 + "@babel/types": ^7.20.7 + "@lingui/conf": 4.14.1 + "@lingui/core": 4.14.1 + "@lingui/message-utils": 4.14.1 + peerDependencies: + "@lingui/react": ^4.0.0 + babel-plugin-macros: 2 || 3 + checksum: f479ca66cea835044fb40170734aa99ff1b0e7c20b9efd4ec04a33d324df1b80b2c708ce12c24a6e839fdc737104170d674d21a54f679e6a4a81a23f3355c3aa + languageName: node + linkType: hard + +"@lingui/message-utils@npm:4.14.1": + version: 4.14.1 + resolution: "@lingui/message-utils@npm:4.14.1" + dependencies: + "@messageformat/parser": ^5.0.0 + js-sha256: ^0.10.1 + checksum: 1b699891b3e62e54cdf69fcdedb5c9c7bbe15e02798f44b617d999a3e15a85b38f49d43d47b335e983c85335b50fc2d220d562b1fd0c241608875241d13006ec + languageName: node + linkType: hard + +"@lingui/react@npm:^4.11.4": + version: 4.14.1 + resolution: "@lingui/react@npm:4.14.1" + dependencies: + "@babel/runtime": ^7.20.13 + "@lingui/core": 4.14.1 + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + checksum: b448eb9fc83415428b5cff501497993c2d0f40768f5ae10d9cf6e5d4ae6f711a08c573380165fda277c0a9805447d7b9aff901a33705abf1eaadde97d330adf4 + languageName: node + linkType: hard + +"@lingui/remote-loader@npm:^3.11.0": + version: 3.11.0 + resolution: "@lingui/remote-loader@npm:3.11.0" + dependencies: + "@babel/generator": ^7.14.5 + "@babel/types": ^7.14.5 + json5: ^2.2.0 + messageformat-parser: ^4.1.3 + ramda: ^0.27.1 + checksum: 714ac3c93f20a92d7c1e3f184adb28c8a6e77c614cbb159af0297fc2c962bbc1e644a68be23e5343f5e9c56487139f69e9c6694042647530e600d0fe9f85777f + languageName: node + linkType: hard + "@ltd/j-toml@npm:^1.38.0": version: 1.38.0 resolution: "@ltd/j-toml@npm:1.38.0" @@ -5035,6 +5355,15 @@ __metadata: languageName: node linkType: hard +"@mapbox/hast-util-table-cell-style@npm:^0.2.0": + version: 0.2.1 + resolution: "@mapbox/hast-util-table-cell-style@npm:0.2.1" + dependencies: + unist-util-visit: ^1.4.1 + checksum: d7151365d10cb8a02f08973064fd2619b4ce1dab4139c8c5e9ee21f9c364ecf5e2fae066136e815cf8035496070c6280bb5215b5bfe9c9cdc804b50025e94fa2 + languageName: node + linkType: hard + "@mdn/browser-compat-data@npm:^4.0.0": version: 4.2.1 resolution: "@mdn/browser-compat-data@npm:4.2.1" @@ -5119,6 +5448,15 @@ __metadata: languageName: node linkType: hard +"@messageformat/parser@npm:^5.0.0": + version: 5.1.1 + resolution: "@messageformat/parser@npm:5.1.1" + dependencies: + moo: ^0.5.1 + checksum: 530a53b1445709f3a0361c378892fe84a976a3977869dfb7e31580375f3ddb6e9fac933dfb4f1f214c22e716e8e52f6ada2c47c765c5f3d87ca4747f5076eeaa + languageName: node + linkType: hard + "@metamask/eth-sig-util@npm:^4.0.0": version: 4.0.1 resolution: "@metamask/eth-sig-util@npm:4.0.1" @@ -5132,7 +5470,96 @@ __metadata: languageName: node linkType: hard -"@noble/curves@npm:1.2.0": +"@monaco-editor/loader@npm:^1.4.0": + version: 1.4.0 + resolution: "@monaco-editor/loader@npm:1.4.0" + dependencies: + state-local: ^1.0.6 + peerDependencies: + monaco-editor: ">= 0.21.0 < 1" + checksum: 374ec0ea872ee15b33310e105a43217148161480d3955c5cece87d0f801754cd2c45a3f6c539a75da18a066c1615756fb87eaf1003f1df6a64a0cbce5d2c3749 + languageName: node + linkType: hard + +"@monaco-editor/react@npm:^4.6.0": + version: 4.6.0 + resolution: "@monaco-editor/react@npm:4.6.0" + dependencies: + "@monaco-editor/loader": ^1.4.0 + peerDependencies: + monaco-editor: ">= 0.25.0 < 1" + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + checksum: 9d44e76c5baad6db5f84c90a5540fbd3c9af691b97d76cf2a99b3c8273004d0efe44c2572d80e9d975c9af10022c21e4a66923924950a5201e82017c8b20428c + languageName: node + linkType: hard + +"@motionone/animation@npm:^10.12.0": + version: 10.18.0 + resolution: "@motionone/animation@npm:10.18.0" + dependencies: + "@motionone/easing": ^10.18.0 + "@motionone/types": ^10.17.1 + "@motionone/utils": ^10.18.0 + tslib: ^2.3.1 + checksum: 841cb9f4843a89e5e4560b9f960f52cbe78afc86f87c769f71e9edb3aadd53fb87982b7e11914428f228b29fd580756be531369c2ffac06432550afa4e87d1c3 + languageName: node + linkType: hard + +"@motionone/dom@npm:10.12.0": + version: 10.12.0 + resolution: "@motionone/dom@npm:10.12.0" + dependencies: + "@motionone/animation": ^10.12.0 + "@motionone/generators": ^10.12.0 + "@motionone/types": ^10.12.0 + "@motionone/utils": ^10.12.0 + hey-listen: ^1.0.8 + tslib: ^2.3.1 + checksum: 123356f28e44362c4f081aae3df22e576f46bfcb07e01257b2ac64a115668448f29b8de67e4b6e692c5407cffb78ffe7cf9fa1bc064007482bab5dd23a69d380 + languageName: node + linkType: hard + +"@motionone/easing@npm:^10.18.0": + version: 10.18.0 + resolution: "@motionone/easing@npm:10.18.0" + dependencies: + "@motionone/utils": ^10.18.0 + tslib: ^2.3.1 + checksum: 6bd37f7a9d5a88f868cc0ad6e47d2ba8d9fefd7da84fccfea7ed77ec08c2e6d1e42df88dda462665102a5cf03f748231a1a077de7054b5a8ccb0fbf36f61b1e7 + languageName: node + linkType: hard + +"@motionone/generators@npm:^10.12.0": + version: 10.18.0 + resolution: "@motionone/generators@npm:10.18.0" + dependencies: + "@motionone/types": ^10.17.1 + "@motionone/utils": ^10.18.0 + tslib: ^2.3.1 + checksum: 51a0e075681697b11d0771998cac8c76a745f00141502f81adb953896992b7f49478965e4afe696bc83361afaae8d2f1057d71c25b21035fe67258ff73764f1c + languageName: node + linkType: hard + +"@motionone/types@npm:^10.12.0, @motionone/types@npm:^10.17.1": + version: 10.17.1 + resolution: "@motionone/types@npm:10.17.1" + checksum: 3fa74db64e371e61a7f7669d7d541d11c9a8dd871032d59c69041e3b2e07a67ad2ed8767cb9273bac90eed4e1f76efc1f14c8673c2e9a288f6070ee0fef64a25 + languageName: node + linkType: hard + +"@motionone/utils@npm:^10.12.0, @motionone/utils@npm:^10.18.0": + version: 10.18.0 + resolution: "@motionone/utils@npm:10.18.0" + dependencies: + "@motionone/types": ^10.17.1 + hey-listen: ^1.0.8 + tslib: ^2.3.1 + checksum: a27f9afde693a0cbbbcb33962b12bbe40dd2cfa514b0732f3c7953c5ef4beed738e1e8172a2de89e3b9f74a253ef0a70d7f3efb730be97b77d7176a3ffacb67a + languageName: node + linkType: hard + +"@noble/curves@npm:1.2.0, @noble/curves@npm:~1.2.0": version: 1.2.0 resolution: "@noble/curves@npm:1.2.0" dependencies: @@ -5155,6 +5582,13 @@ __metadata: languageName: node linkType: hard +"@noble/hashes@npm:~1.3.0, @noble/hashes@npm:~1.3.2": + version: 1.3.3 + resolution: "@noble/hashes@npm:1.3.3" + checksum: 8a6496d1c0c64797339bc694ad06cdfaa0f9e56cd0c3f68ae3666cfb153a791a55deb0af9c653c7ed2db64d537aa3e3054629740d2f2338bb1dcb7ab60cd205b + languageName: node + linkType: hard + "@noble/secp256k1@npm:1.7.1, @noble/secp256k1@npm:~1.7.0": version: 1.7.1 resolution: "@noble/secp256k1@npm:1.7.1" @@ -5826,71 +6260,1234 @@ __metadata: languageName: node linkType: hard -"@rollup/plugin-node-resolve@npm:^15.0.1": - version: 15.2.3 - resolution: "@rollup/plugin-node-resolve@npm:15.2.3" +"@radix-ui/number@npm:1.1.0": + version: 1.1.0 + resolution: "@radix-ui/number@npm:1.1.0" + checksum: e4fc7483c19141c25dbaf3d140b75e2b7fed0bfa3ad969f4441f0266ed34b35413f57a35df7b025e2a977152bbe6131849d3444fc6f15a73345dfc2bfdc105fa + languageName: node + linkType: hard + +"@radix-ui/primitive@npm:1.0.1": + version: 1.0.1 + resolution: "@radix-ui/primitive@npm:1.0.1" dependencies: - "@rollup/pluginutils": ^5.0.1 - "@types/resolve": 1.20.2 - deepmerge: ^4.2.2 - is-builtin-module: ^3.2.1 - is-module: ^1.0.0 - resolve: ^1.22.1 - peerDependencies: - rollup: ^2.78.0||^3.0.0||^4.0.0 - peerDependenciesMeta: - rollup: - optional: true - checksum: 730f32c2f8fdddff07cf0fca86a5dac7c475605fb96930197a868c066e62eb6388c557545e4f7d99b7a283411754c9fbf98944ab086b6074e04fc1292e234aa8 + "@babel/runtime": ^7.13.10 + checksum: 2b93e161d3fdabe9a64919def7fa3ceaecf2848341e9211520c401181c9eaebb8451c630b066fad2256e5c639c95edc41de0ba59c40eff37e799918d019822d1 languageName: node linkType: hard -"@rollup/pluginutils@npm:^5.0.1": - version: 5.1.0 - resolution: "@rollup/pluginutils@npm:5.1.0" +"@radix-ui/primitive@npm:1.1.1": + version: 1.1.1 + resolution: "@radix-ui/primitive@npm:1.1.1" + checksum: d7e819177590108b74139809d52ec043c0962ae3513e947998be575fb13639c5c1c091896ddcf1d6a22a777d44ade59d22c2019ce9099607fc62a5de09c59707 + languageName: node + linkType: hard + +"@radix-ui/react-arrow@npm:1.1.1": + version: 1.1.1 + resolution: "@radix-ui/react-arrow@npm:1.1.1" dependencies: - "@types/estree": ^1.0.0 - estree-walker: ^2.0.2 - picomatch: ^2.3.1 + "@radix-ui/react-primitive": 2.0.1 peerDependencies: - rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: - rollup: + "@types/react": optional: true - checksum: 3cc5a6d91452a6eabbfd1ae79b4dd1f1e809d2eecda6e175deb784e75b0911f47e9ecce73f8dd315d6a8b3f362582c91d3c0f66908b6ced69345b3cbe28f8ce8 + "@types/react-dom": + optional: true + checksum: c75505c2858cffff7c742e888b635879f9a6d95e08bf5ae939be33f97e1171379bc6b5354ec0cd3d12624bdbe5a830ee6aa0fb1f46b1af160b488bc54e64d486 languageName: node linkType: hard -"@rollup/rollup-android-arm-eabi@npm:4.9.4": - version: 4.9.4 - resolution: "@rollup/rollup-android-arm-eabi@npm:4.9.4" - conditions: os=android & cpu=arm +"@radix-ui/react-avatar@npm:^1.0.4": + version: 1.1.2 + resolution: "@radix-ui/react-avatar@npm:1.1.2" + dependencies: + "@radix-ui/react-context": 1.1.1 + "@radix-ui/react-primitive": 2.0.1 + "@radix-ui/react-use-callback-ref": 1.1.0 + "@radix-ui/react-use-layout-effect": 1.1.0 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: fa2c95c3f9393cee30b5416147f9701edd335c27f1132782627db4dbd2545dbc5fdcea0a6e66945e3072ea91c80ec45183a07159a882c974ea5fb6bfc140fbb4 languageName: node linkType: hard -"@rollup/rollup-android-arm64@npm:4.9.4": - version: 4.9.4 - resolution: "@rollup/rollup-android-arm64@npm:4.9.4" - conditions: os=android & cpu=arm64 +"@radix-ui/react-checkbox@npm:^1.0.4": + version: 1.1.3 + resolution: "@radix-ui/react-checkbox@npm:1.1.3" + dependencies: + "@radix-ui/primitive": 1.1.1 + "@radix-ui/react-compose-refs": 1.1.1 + "@radix-ui/react-context": 1.1.1 + "@radix-ui/react-presence": 1.1.2 + "@radix-ui/react-primitive": 2.0.1 + "@radix-ui/react-use-controllable-state": 1.1.0 + "@radix-ui/react-use-previous": 1.1.0 + "@radix-ui/react-use-size": 1.1.0 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: ff2e62ba5f4bfdd6ef0cbbdf38a9f2249793d3a95b8fa55f95506198276e01c849b09bf963ec435c3f3ed43b18e27ddcb3087be4f6558e8d4f4dab8a27c49399 languageName: node linkType: hard -"@rollup/rollup-darwin-arm64@npm:4.9.4": - version: 4.9.4 - resolution: "@rollup/rollup-darwin-arm64@npm:4.9.4" - conditions: os=darwin & cpu=arm64 +"@radix-ui/react-collapsible@npm:^1.0.3": + version: 1.1.2 + resolution: "@radix-ui/react-collapsible@npm:1.1.2" + dependencies: + "@radix-ui/primitive": 1.1.1 + "@radix-ui/react-compose-refs": 1.1.1 + "@radix-ui/react-context": 1.1.1 + "@radix-ui/react-id": 1.1.0 + "@radix-ui/react-presence": 1.1.2 + "@radix-ui/react-primitive": 2.0.1 + "@radix-ui/react-use-controllable-state": 1.1.0 + "@radix-ui/react-use-layout-effect": 1.1.0 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: c605d2eeb21417e243b3e7bf975af21959c56d3d77677ae320eac7f38227806ca1f620833000c077a649e3b5e93652eaeb129756265c1423cff9f791af0b715d languageName: node linkType: hard -"@rollup/rollup-darwin-x64@npm:4.9.4": - version: 4.9.4 - resolution: "@rollup/rollup-darwin-x64@npm:4.9.4" - conditions: os=darwin & cpu=x64 +"@radix-ui/react-collection@npm:1.1.1": + version: 1.1.1 + resolution: "@radix-ui/react-collection@npm:1.1.1" + dependencies: + "@radix-ui/react-compose-refs": 1.1.1 + "@radix-ui/react-context": 1.1.1 + "@radix-ui/react-primitive": 2.0.1 + "@radix-ui/react-slot": 1.1.1 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: 413fbcb542ae5efefab05053a9cc3380586e7bc3d36427845a62749d90284986333e609066b952cc6ccd0a8ef49e8de773ea1cd294aecff3cdb1eb4047e97c2f languageName: node linkType: hard -"@rollup/rollup-linux-arm-gnueabihf@npm:4.9.4": - version: 4.9.4 +"@radix-ui/react-compose-refs@npm:1.0.1": + version: 1.0.1 + resolution: "@radix-ui/react-compose-refs@npm:1.0.1" + dependencies: + "@babel/runtime": ^7.13.10 + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 2b9a613b6db5bff8865588b6bf4065f73021b3d16c0a90b2d4c23deceeb63612f1f15de188227ebdc5f88222cab031be617a9dd025874c0487b303be3e5cc2a8 + languageName: node + linkType: hard + +"@radix-ui/react-compose-refs@npm:1.1.1": + version: 1.1.1 + resolution: "@radix-ui/react-compose-refs@npm:1.1.1" + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 1be82f9f7fab96cc10f167a2e4f976e0135a63d473334f664c06f02af13bc5ea1994cb0505f89ed190d756cb65d57506721c030908af07e49b9e3cfd36044f33 + languageName: node + linkType: hard + +"@radix-ui/react-context@npm:1.0.1": + version: 1.0.1 + resolution: "@radix-ui/react-context@npm:1.0.1" + dependencies: + "@babel/runtime": ^7.13.10 + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 60e9b81d364f40c91a6213ec953f7c64fcd9d75721205a494a5815b3e5ae0719193429b62ee6c7002cd6aaf70f8c0e2f08bdbaba9ffcc233044d32b56d2127d1 + languageName: node + linkType: hard + +"@radix-ui/react-context@npm:1.1.1": + version: 1.1.1 + resolution: "@radix-ui/react-context@npm:1.1.1" + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 9a04db236685dacc2f5ab2bdcfc4c82b974998e712ab97d79b11d5b4ef073d24aa9392398c876ef6cb3c59f40299285ceee3646187ad818cdad4fe1c74469d3f + languageName: node + linkType: hard + +"@radix-ui/react-dialog@npm:1.0.5": + version: 1.0.5 + resolution: "@radix-ui/react-dialog@npm:1.0.5" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/primitive": 1.0.1 + "@radix-ui/react-compose-refs": 1.0.1 + "@radix-ui/react-context": 1.0.1 + "@radix-ui/react-dismissable-layer": 1.0.5 + "@radix-ui/react-focus-guards": 1.0.1 + "@radix-ui/react-focus-scope": 1.0.4 + "@radix-ui/react-id": 1.0.1 + "@radix-ui/react-portal": 1.0.4 + "@radix-ui/react-presence": 1.0.1 + "@radix-ui/react-primitive": 1.0.3 + "@radix-ui/react-slot": 1.0.2 + "@radix-ui/react-use-controllable-state": 1.0.1 + aria-hidden: ^1.1.1 + react-remove-scroll: 2.5.5 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: 3d11ca31afb794a6dd286005ab7894cb0ce7bc2de5481de98900470b11d495256401306763de030f5e35aa545ff90d34632ffd54a1b29bf55afba813be4bb84a + languageName: node + linkType: hard + +"@radix-ui/react-dialog@npm:^1.0.5": + version: 1.1.4 + resolution: "@radix-ui/react-dialog@npm:1.1.4" + dependencies: + "@radix-ui/primitive": 1.1.1 + "@radix-ui/react-compose-refs": 1.1.1 + "@radix-ui/react-context": 1.1.1 + "@radix-ui/react-dismissable-layer": 1.1.3 + "@radix-ui/react-focus-guards": 1.1.1 + "@radix-ui/react-focus-scope": 1.1.1 + "@radix-ui/react-id": 1.1.0 + "@radix-ui/react-portal": 1.1.3 + "@radix-ui/react-presence": 1.1.2 + "@radix-ui/react-primitive": 2.0.1 + "@radix-ui/react-slot": 1.1.1 + "@radix-ui/react-use-controllable-state": 1.1.0 + aria-hidden: ^1.1.1 + react-remove-scroll: ^2.6.1 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: 695b35c7283adfe2be4ad88d30f0ad08be099a55dfd54e49ede61074846255b426eb57734509b5fc6f349439a051b48e44e58b542c2605383aa721a1b2bd7861 + languageName: node + linkType: hard + +"@radix-ui/react-direction@npm:1.1.0": + version: 1.1.0 + resolution: "@radix-ui/react-direction@npm:1.1.0" + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 25ad0d1d65ad08c93cebfbefdff9ef2602e53f4573a66b37d2c366ede9485e75ec6fc8e7dd7d2939b34ea5504ca0fe6ac4a3acc2f6ee9b62d131d65486eafd49 + languageName: node + linkType: hard + +"@radix-ui/react-dismissable-layer@npm:1.0.5": + version: 1.0.5 + resolution: "@radix-ui/react-dismissable-layer@npm:1.0.5" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/primitive": 1.0.1 + "@radix-ui/react-compose-refs": 1.0.1 + "@radix-ui/react-primitive": 1.0.3 + "@radix-ui/react-use-callback-ref": 1.0.1 + "@radix-ui/react-use-escape-keydown": 1.0.3 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: e73cf4bd3763f4d55b1bea7486a9700384d7d94dc00b1d5a75e222b2f1e4f32bc667a206ca4ed3baaaf7424dce7a239afd0ba59a6f0d89c3462c4e6e8d029a04 + languageName: node + linkType: hard + +"@radix-ui/react-dismissable-layer@npm:1.1.3": + version: 1.1.3 + resolution: "@radix-ui/react-dismissable-layer@npm:1.1.3" + dependencies: + "@radix-ui/primitive": 1.1.1 + "@radix-ui/react-compose-refs": 1.1.1 + "@radix-ui/react-primitive": 2.0.1 + "@radix-ui/react-use-callback-ref": 1.1.0 + "@radix-ui/react-use-escape-keydown": 1.1.0 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: 26d15726bdb274aeb8d801fd163051c270707fb19e9bac4e0e90b368e79063a5347a0b15dc3aadc0bbafa157674e9e796d785d720bd5132c059ac5294ac73a81 + languageName: node + linkType: hard + +"@radix-ui/react-dropdown-menu@npm:^2.0.6": + version: 2.1.4 + resolution: "@radix-ui/react-dropdown-menu@npm:2.1.4" + dependencies: + "@radix-ui/primitive": 1.1.1 + "@radix-ui/react-compose-refs": 1.1.1 + "@radix-ui/react-context": 1.1.1 + "@radix-ui/react-id": 1.1.0 + "@radix-ui/react-menu": 2.1.4 + "@radix-ui/react-primitive": 2.0.1 + "@radix-ui/react-use-controllable-state": 1.1.0 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: 51014b38daab21d32164813d228b182e5dbf90c77115e5c32b8bbd37696a303485fe30fa2e0a097e1ca3f474cad0dd15efcecc3f567b5edf0a79b5092fe3b4e0 + languageName: node + linkType: hard + +"@radix-ui/react-focus-guards@npm:1.0.1": + version: 1.0.1 + resolution: "@radix-ui/react-focus-guards@npm:1.0.1" + dependencies: + "@babel/runtime": ^7.13.10 + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 1f8ca8f83b884b3612788d0742f3f054e327856d90a39841a47897dbed95e114ee512362ae314177de226d05310047cabbf66b686ae86ad1b65b6b295be24ef7 + languageName: node + linkType: hard + +"@radix-ui/react-focus-guards@npm:1.1.1": + version: 1.1.1 + resolution: "@radix-ui/react-focus-guards@npm:1.1.1" + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + checksum: ac8dd31f48fa0500bafd9368f2f06c5a06918dccefa89fa5dc77ca218dc931a094a81ca57f6b181138029822f7acdd5280dceccf5ba4d9263c754fb8f7961879 + languageName: node + linkType: hard + +"@radix-ui/react-focus-scope@npm:1.0.4": + version: 1.0.4 + resolution: "@radix-ui/react-focus-scope@npm:1.0.4" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/react-compose-refs": 1.0.1 + "@radix-ui/react-primitive": 1.0.3 + "@radix-ui/react-use-callback-ref": 1.0.1 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: 3481db1a641513a572734f0bcb0e47fefeba7bccd6ec8dde19f520719c783ef0b05a55ef0d5292078ed051cc5eda46b698d5d768da02e26e836022f46b376fd1 + languageName: node + linkType: hard + +"@radix-ui/react-focus-scope@npm:1.1.1": + version: 1.1.1 + resolution: "@radix-ui/react-focus-scope@npm:1.1.1" + dependencies: + "@radix-ui/react-compose-refs": 1.1.1 + "@radix-ui/react-primitive": 2.0.1 + "@radix-ui/react-use-callback-ref": 1.1.0 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: 8716fe9b029a66f81b37e4e22457dd0fc7b4dba573d712454e18ead850f256d84cd994eeebcc31dd7780cf1028b6410d9ebe152fff4478d3b4ce2700690a38f4 + languageName: node + linkType: hard + +"@radix-ui/react-hover-card@npm:^1.0.7": + version: 1.1.4 + resolution: "@radix-ui/react-hover-card@npm:1.1.4" + dependencies: + "@radix-ui/primitive": 1.1.1 + "@radix-ui/react-compose-refs": 1.1.1 + "@radix-ui/react-context": 1.1.1 + "@radix-ui/react-dismissable-layer": 1.1.3 + "@radix-ui/react-popper": 1.2.1 + "@radix-ui/react-portal": 1.1.3 + "@radix-ui/react-presence": 1.1.2 + "@radix-ui/react-primitive": 2.0.1 + "@radix-ui/react-use-controllable-state": 1.1.0 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: 1c3e0d8edc01f714c1ca389fb28f4ed843726e0cb56b21dc7d79e9680f2c19138ce494eb3c333ef7ad524a94209494321b4ca70a44f56fdb01c6a90d30c02058 + languageName: node + linkType: hard + +"@radix-ui/react-icons@npm:^1.3.0": + version: 1.3.2 + resolution: "@radix-ui/react-icons@npm:1.3.2" + peerDependencies: + react: ^16.x || ^17.x || ^18.x || ^19.0.0 || ^19.0.0-rc + checksum: 4dbd60d9ca3481d3f01d99862ecfc2a06ac731603a54d45fe3027bbdb986c60a3c080c06b689b1fcc1628a935b07f765e8088a21902df55c2366ec61acbe9b30 + languageName: node + linkType: hard + +"@radix-ui/react-id@npm:1.0.1": + version: 1.0.1 + resolution: "@radix-ui/react-id@npm:1.0.1" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/react-use-layout-effect": 1.0.1 + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 446a453d799cc790dd2a1583ff8328da88271bff64530b5a17c102fa7fb35eece3cf8985359d416f65e330cd81aa7b8fe984ea125fc4f4eaf4b3801d698e49fe + languageName: node + linkType: hard + +"@radix-ui/react-id@npm:1.1.0, @radix-ui/react-id@npm:^1.0.1": + version: 1.1.0 + resolution: "@radix-ui/react-id@npm:1.1.0" + dependencies: + "@radix-ui/react-use-layout-effect": 1.1.0 + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 6fbc9d1739b3b082412da10359e63967b4f3a60383ebda4c9e56b07a722d29bee53b203b3b1418f88854a29315a7715867133bb149e6e22a027a048cdd20d970 + languageName: node + linkType: hard + +"@radix-ui/react-label@npm:^2.0.2": + version: 2.1.1 + resolution: "@radix-ui/react-label@npm:2.1.1" + dependencies: + "@radix-ui/react-primitive": 2.0.1 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: 40525096bf6fb5a1cfbf5c87aa46fd13d1329155fa62a42e35b4b55db5559d42081b97e77bf5f7ac45064fdae947ee6f78abb1f55b18821760a5712492243e6d + languageName: node + linkType: hard + +"@radix-ui/react-menu@npm:2.1.4": + version: 2.1.4 + resolution: "@radix-ui/react-menu@npm:2.1.4" + dependencies: + "@radix-ui/primitive": 1.1.1 + "@radix-ui/react-collection": 1.1.1 + "@radix-ui/react-compose-refs": 1.1.1 + "@radix-ui/react-context": 1.1.1 + "@radix-ui/react-direction": 1.1.0 + "@radix-ui/react-dismissable-layer": 1.1.3 + "@radix-ui/react-focus-guards": 1.1.1 + "@radix-ui/react-focus-scope": 1.1.1 + "@radix-ui/react-id": 1.1.0 + "@radix-ui/react-popper": 1.2.1 + "@radix-ui/react-portal": 1.1.3 + "@radix-ui/react-presence": 1.1.2 + "@radix-ui/react-primitive": 2.0.1 + "@radix-ui/react-roving-focus": 1.1.1 + "@radix-ui/react-slot": 1.1.1 + "@radix-ui/react-use-callback-ref": 1.1.0 + aria-hidden: ^1.1.1 + react-remove-scroll: ^2.6.1 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: 2a20db7c017075d3ceb07b076dfdbdc3c4d26825642e2f52d4bda9947078725db129c2b41a93a6f1393526eca3bf586f54dc47a9a66e1eef621c94a55a216aa4 + languageName: node + linkType: hard + +"@radix-ui/react-popover@npm:^1.0.7": + version: 1.1.4 + resolution: "@radix-ui/react-popover@npm:1.1.4" + dependencies: + "@radix-ui/primitive": 1.1.1 + "@radix-ui/react-compose-refs": 1.1.1 + "@radix-ui/react-context": 1.1.1 + "@radix-ui/react-dismissable-layer": 1.1.3 + "@radix-ui/react-focus-guards": 1.1.1 + "@radix-ui/react-focus-scope": 1.1.1 + "@radix-ui/react-id": 1.1.0 + "@radix-ui/react-popper": 1.2.1 + "@radix-ui/react-portal": 1.1.3 + "@radix-ui/react-presence": 1.1.2 + "@radix-ui/react-primitive": 2.0.1 + "@radix-ui/react-slot": 1.1.1 + "@radix-ui/react-use-controllable-state": 1.1.0 + aria-hidden: ^1.1.1 + react-remove-scroll: ^2.6.1 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: f4525ac9a2f5957ad709749daddb78e58d8b1471dfd8683ca713d1ade9aac202b30c7179b798471e90ab13a01f01a70a3bc4002a872c279ee383bd3ad8ad49e6 + languageName: node + linkType: hard + +"@radix-ui/react-popper@npm:1.2.1": + version: 1.2.1 + resolution: "@radix-ui/react-popper@npm:1.2.1" + dependencies: + "@floating-ui/react-dom": ^2.0.0 + "@radix-ui/react-arrow": 1.1.1 + "@radix-ui/react-compose-refs": 1.1.1 + "@radix-ui/react-context": 1.1.1 + "@radix-ui/react-primitive": 2.0.1 + "@radix-ui/react-use-callback-ref": 1.1.0 + "@radix-ui/react-use-layout-effect": 1.1.0 + "@radix-ui/react-use-rect": 1.1.0 + "@radix-ui/react-use-size": 1.1.0 + "@radix-ui/rect": 1.1.0 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: 1416acda53d06d497d40a587e02ef821dcb955f2eee86bad3a9acacfd7fda8601e5d36a9cbe5e47d200052169ccd2d840b685c51e7192afdf3fc7fa072274ee0 + languageName: node + linkType: hard + +"@radix-ui/react-portal@npm:1.0.4": + version: 1.0.4 + resolution: "@radix-ui/react-portal@npm:1.0.4" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/react-primitive": 1.0.3 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: c4cf35e2f26a89703189d0eef3ceeeb706ae0832e98e558730a5e929ca7c72c7cb510413a24eca94c7732f8d659a1e81942bec7b90540cb73ce9e4885d040b64 + languageName: node + linkType: hard + +"@radix-ui/react-portal@npm:1.1.3": + version: 1.1.3 + resolution: "@radix-ui/react-portal@npm:1.1.3" + dependencies: + "@radix-ui/react-primitive": 2.0.1 + "@radix-ui/react-use-layout-effect": 1.1.0 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: 470fb50c940772d05cc268e219b3d15848909dcd0a2dc1952965d0af905992f0ccab99e99c490dea6564c441397eba720b8425ba9f4582c94bef40ebe27ac0d0 + languageName: node + linkType: hard + +"@radix-ui/react-presence@npm:1.0.1": + version: 1.0.1 + resolution: "@radix-ui/react-presence@npm:1.0.1" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/react-compose-refs": 1.0.1 + "@radix-ui/react-use-layout-effect": 1.0.1 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: ed2ff9faf9e4257a4065034d3771459e5a91c2d840b2fcec94661761704dbcb65bcdd927d28177a2a129b3dab5664eb90a9b88309afe0257a9f8ba99338c0d95 + languageName: node + linkType: hard + +"@radix-ui/react-presence@npm:1.1.2": + version: 1.1.2 + resolution: "@radix-ui/react-presence@npm:1.1.2" + dependencies: + "@radix-ui/react-compose-refs": 1.1.1 + "@radix-ui/react-use-layout-effect": 1.1.0 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: 0345bc8d3e1ddcbf4b864025833c71f3d76e4801ce16ad126a98aed816be6e819c4fe01097c6c1320771b947f5a14929cc610d18e7a1438cfb5573289fa4d4a6 + languageName: node + linkType: hard + +"@radix-ui/react-primitive@npm:1.0.3": + version: 1.0.3 + resolution: "@radix-ui/react-primitive@npm:1.0.3" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/react-slot": 1.0.2 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: 9402bc22923c8e5c479051974a721c301535c36521c0237b83e5fa213d013174e77f3ad7905e6d60ef07e14f88ec7f4ea69891dc7a2b39047f8d3640e8f8d713 + languageName: node + linkType: hard + +"@radix-ui/react-primitive@npm:2.0.1": + version: 2.0.1 + resolution: "@radix-ui/react-primitive@npm:2.0.1" + dependencies: + "@radix-ui/react-slot": 1.1.1 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: d75882209101155f20babcff9475b887929db6473cd8e5b56d0c24d24d0042202e0fa785e6d6c6b322a96d9777cd0ef7610def9e11ea69839c6b204f1c99cf16 + languageName: node + linkType: hard + +"@radix-ui/react-roving-focus@npm:1.1.1": + version: 1.1.1 + resolution: "@radix-ui/react-roving-focus@npm:1.1.1" + dependencies: + "@radix-ui/primitive": 1.1.1 + "@radix-ui/react-collection": 1.1.1 + "@radix-ui/react-compose-refs": 1.1.1 + "@radix-ui/react-context": 1.1.1 + "@radix-ui/react-direction": 1.1.0 + "@radix-ui/react-id": 1.1.0 + "@radix-ui/react-primitive": 2.0.1 + "@radix-ui/react-use-callback-ref": 1.1.0 + "@radix-ui/react-use-controllable-state": 1.1.0 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: f3a78bd15cca322b384758c938106414b4cbe99aa58ca3eb12ec9d66552c7fa137128e846f413a6b7027f4bed04b2233c9aad8adb1ba07e14ed7a697f972d4f6 + languageName: node + linkType: hard + +"@radix-ui/react-scroll-area@npm:^1.0.5": + version: 1.2.2 + resolution: "@radix-ui/react-scroll-area@npm:1.2.2" + dependencies: + "@radix-ui/number": 1.1.0 + "@radix-ui/primitive": 1.1.1 + "@radix-ui/react-compose-refs": 1.1.1 + "@radix-ui/react-context": 1.1.1 + "@radix-ui/react-direction": 1.1.0 + "@radix-ui/react-presence": 1.1.2 + "@radix-ui/react-primitive": 2.0.1 + "@radix-ui/react-use-callback-ref": 1.1.0 + "@radix-ui/react-use-layout-effect": 1.1.0 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: 2cd63d159442d55951c8e9c447a513f9e3c435a7c9555165b26dceda6d4047432a25acded3f6c18f51c9abbae2ccf6b38d81cc4767d78133fb89d3de73ddaca8 + languageName: node + linkType: hard + +"@radix-ui/react-select@npm:^2.0.0": + version: 2.1.4 + resolution: "@radix-ui/react-select@npm:2.1.4" + dependencies: + "@radix-ui/number": 1.1.0 + "@radix-ui/primitive": 1.1.1 + "@radix-ui/react-collection": 1.1.1 + "@radix-ui/react-compose-refs": 1.1.1 + "@radix-ui/react-context": 1.1.1 + "@radix-ui/react-direction": 1.1.0 + "@radix-ui/react-dismissable-layer": 1.1.3 + "@radix-ui/react-focus-guards": 1.1.1 + "@radix-ui/react-focus-scope": 1.1.1 + "@radix-ui/react-id": 1.1.0 + "@radix-ui/react-popper": 1.2.1 + "@radix-ui/react-portal": 1.1.3 + "@radix-ui/react-primitive": 2.0.1 + "@radix-ui/react-slot": 1.1.1 + "@radix-ui/react-use-callback-ref": 1.1.0 + "@radix-ui/react-use-controllable-state": 1.1.0 + "@radix-ui/react-use-layout-effect": 1.1.0 + "@radix-ui/react-use-previous": 1.1.0 + "@radix-ui/react-visually-hidden": 1.1.1 + aria-hidden: ^1.1.1 + react-remove-scroll: ^2.6.1 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: 68571da6bebc83f0b685b2b49d804227e787adc72e759523d2d5ce9ee63b51d61de2a9ea92455f33d386eced626d4b2bf73639f4c08d2dbb595f5ec3af47533c + languageName: node + linkType: hard + +"@radix-ui/react-separator@npm:^1.0.3": + version: 1.1.1 + resolution: "@radix-ui/react-separator@npm:1.1.1" + dependencies: + "@radix-ui/react-primitive": 2.0.1 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: 85de387e6b94548d777b5ebafe3f170eb0bab31ec1850ee8bc58f7fc48b725d6cf33c4fb2c92be3196ca843a14819fa32108dd78d5675994e31adb5500484e01 + languageName: node + linkType: hard + +"@radix-ui/react-slider@npm:^1.1.2": + version: 1.2.2 + resolution: "@radix-ui/react-slider@npm:1.2.2" + dependencies: + "@radix-ui/number": 1.1.0 + "@radix-ui/primitive": 1.1.1 + "@radix-ui/react-collection": 1.1.1 + "@radix-ui/react-compose-refs": 1.1.1 + "@radix-ui/react-context": 1.1.1 + "@radix-ui/react-direction": 1.1.0 + "@radix-ui/react-primitive": 2.0.1 + "@radix-ui/react-use-controllable-state": 1.1.0 + "@radix-ui/react-use-layout-effect": 1.1.0 + "@radix-ui/react-use-previous": 1.1.0 + "@radix-ui/react-use-size": 1.1.0 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: 43acd22d5b0840092463ecd60a1c00db01d38330255b7e27d4ccd754379955ce7e6a305572a2691d34ea77df8904aa89030c5cb2596b0ddd54ceac5a62f916e5 + languageName: node + linkType: hard + +"@radix-ui/react-slot@npm:1.0.2": + version: 1.0.2 + resolution: "@radix-ui/react-slot@npm:1.0.2" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/react-compose-refs": 1.0.1 + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: edf5edf435ff594bea7e198bf16d46caf81b6fb559493acad4fa8c308218896136acb16f9b7238c788fd13e94a904f2fd0b6d834e530e4cae94522cdb8f77ce9 + languageName: node + linkType: hard + +"@radix-ui/react-slot@npm:1.1.1, @radix-ui/react-slot@npm:^1.0.2": + version: 1.1.1 + resolution: "@radix-ui/react-slot@npm:1.1.1" + dependencies: + "@radix-ui/react-compose-refs": 1.1.1 + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + checksum: ac391b921dcde1a71db8307247b36cd6908e0886d7a7b0babeb25158292bc29b61ccfb3f83279bfad11fe1f0f90e3e2f3de93b1174f36d107d77b073fe1a652a + languageName: node + linkType: hard + +"@radix-ui/react-tabs@npm:^1.0.4": + version: 1.1.2 + resolution: "@radix-ui/react-tabs@npm:1.1.2" + dependencies: + "@radix-ui/primitive": 1.1.1 + "@radix-ui/react-context": 1.1.1 + "@radix-ui/react-direction": 1.1.0 + "@radix-ui/react-id": 1.1.0 + "@radix-ui/react-presence": 1.1.2 + "@radix-ui/react-primitive": 2.0.1 + "@radix-ui/react-roving-focus": 1.1.1 + "@radix-ui/react-use-controllable-state": 1.1.0 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: 21c9ed2e65538beebbde6c2d5d47c8e7f8279cacc87e34c7581109f9524a11860375958b65fe72e70cfb0ade9be71bbc27dd4d94b90465966c7875ca6d704116 + languageName: node + linkType: hard + +"@radix-ui/react-toast@npm:^1.1.5": + version: 1.2.4 + resolution: "@radix-ui/react-toast@npm:1.2.4" + dependencies: + "@radix-ui/primitive": 1.1.1 + "@radix-ui/react-collection": 1.1.1 + "@radix-ui/react-compose-refs": 1.1.1 + "@radix-ui/react-context": 1.1.1 + "@radix-ui/react-dismissable-layer": 1.1.3 + "@radix-ui/react-portal": 1.1.3 + "@radix-ui/react-presence": 1.1.2 + "@radix-ui/react-primitive": 2.0.1 + "@radix-ui/react-use-callback-ref": 1.1.0 + "@radix-ui/react-use-controllable-state": 1.1.0 + "@radix-ui/react-use-layout-effect": 1.1.0 + "@radix-ui/react-visually-hidden": 1.1.1 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: 6479af12ec9ae4f3cdbb8ca66d2926c8323e60fdca57fc3de6af1f41f64bf4cb9e0ea70bdac4e9aac568f1abc2222ebfb67e048d77f36ec10bb4971ae4870b73 + languageName: node + linkType: hard + +"@radix-ui/react-tooltip@npm:^1.0.7": + version: 1.1.6 + resolution: "@radix-ui/react-tooltip@npm:1.1.6" + dependencies: + "@radix-ui/primitive": 1.1.1 + "@radix-ui/react-compose-refs": 1.1.1 + "@radix-ui/react-context": 1.1.1 + "@radix-ui/react-dismissable-layer": 1.1.3 + "@radix-ui/react-id": 1.1.0 + "@radix-ui/react-popper": 1.2.1 + "@radix-ui/react-portal": 1.1.3 + "@radix-ui/react-presence": 1.1.2 + "@radix-ui/react-primitive": 2.0.1 + "@radix-ui/react-slot": 1.1.1 + "@radix-ui/react-use-controllable-state": 1.1.0 + "@radix-ui/react-visually-hidden": 1.1.1 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: aabbb2c3a7592419fcf41d306582c57307e9518ee29d80e0a8f811bb29ade72144ee35a4f4a120e5143dee46813017fe4e087f351503929553a94e3af986305f + languageName: node + linkType: hard + +"@radix-ui/react-use-callback-ref@npm:1.0.1": + version: 1.0.1 + resolution: "@radix-ui/react-use-callback-ref@npm:1.0.1" + dependencies: + "@babel/runtime": ^7.13.10 + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: b9fd39911c3644bbda14a84e4fca080682bef84212b8d8931fcaa2d2814465de242c4cfd8d7afb3020646bead9c5e539d478cea0a7031bee8a8a3bb164f3bc4c + languageName: node + linkType: hard + +"@radix-ui/react-use-callback-ref@npm:1.1.0": + version: 1.1.0 + resolution: "@radix-ui/react-use-callback-ref@npm:1.1.0" + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 2ec7903c67e3034b646005556f44fd975dc5204db6885fc58403e3584f27d95f0b573bc161de3d14fab9fda25150bf3b91f718d299fdfc701c736bd0bd2281fa + languageName: node + linkType: hard + +"@radix-ui/react-use-controllable-state@npm:1.0.1": + version: 1.0.1 + resolution: "@radix-ui/react-use-controllable-state@npm:1.0.1" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/react-use-callback-ref": 1.0.1 + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: dee2be1937d293c3a492cb6d279fc11495a8f19dc595cdbfe24b434e917302f9ac91db24e8cc5af9a065f3f209c3423115b5442e65a5be9fd1e9091338972be9 + languageName: node + linkType: hard + +"@radix-ui/react-use-controllable-state@npm:1.1.0": + version: 1.1.0 + resolution: "@radix-ui/react-use-controllable-state@npm:1.1.0" + dependencies: + "@radix-ui/react-use-callback-ref": 1.1.0 + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + checksum: a6c167cf8eb0744effbeab1f92ea6c0ad71838b222670c0488599f28eecd941d87ac1eed4b5d3b10df6dc7b7b2edb88a54e99d92c2942ce3b21f81d5c188f32d + languageName: node + linkType: hard + +"@radix-ui/react-use-escape-keydown@npm:1.0.3": + version: 1.0.3 + resolution: "@radix-ui/react-use-escape-keydown@npm:1.0.3" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/react-use-callback-ref": 1.0.1 + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: c6ed0d9ce780f67f924980eb305af1f6cce2a8acbaf043a58abe0aa3cc551d9aa76ccee14531df89bbee302ead7ecc7fce330886f82d4672c5eda52f357ef9b8 + languageName: node + linkType: hard + +"@radix-ui/react-use-escape-keydown@npm:1.1.0": + version: 1.1.0 + resolution: "@radix-ui/react-use-escape-keydown@npm:1.1.0" + dependencies: + "@radix-ui/react-use-callback-ref": 1.1.0 + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 9bf88ea272b32ea0f292afd336780a59c5646f795036b7e6105df2d224d73c54399ee5265f61d571eb545d28382491a8b02dc436e3088de8dae415d58b959b71 + languageName: node + linkType: hard + +"@radix-ui/react-use-layout-effect@npm:1.0.1": + version: 1.0.1 + resolution: "@radix-ui/react-use-layout-effect@npm:1.0.1" + dependencies: + "@babel/runtime": ^7.13.10 + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: bed9c7e8de243a5ec3b93bb6a5860950b0dba359b6680c84d57c7a655e123dec9b5891c5dfe81ab970652e7779fe2ad102a23177c7896dde95f7340817d47ae5 + languageName: node + linkType: hard + +"@radix-ui/react-use-layout-effect@npm:1.1.0": + version: 1.1.0 + resolution: "@radix-ui/react-use-layout-effect@npm:1.1.0" + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 271ea0bf1cd74718895a68414a6e95537737f36e02ad08eeb61a82b229d6abda9cff3135a479e134e1f0ce2c3ff97bb85babbdce751985fb755a39b231d7ccf2 + languageName: node + linkType: hard + +"@radix-ui/react-use-previous@npm:1.1.0": + version: 1.1.0 + resolution: "@radix-ui/react-use-previous@npm:1.1.0" + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 8a2407e3db6248ab52bf425f5f4161355d09f1a228038094959250ae53552e73543532b3bb80e452f6ad624621e2e1c6aebb8c702f2dfaa5e89f07ec629d9304 + languageName: node + linkType: hard + +"@radix-ui/react-use-rect@npm:1.1.0": + version: 1.1.0 + resolution: "@radix-ui/react-use-rect@npm:1.1.0" + dependencies: + "@radix-ui/rect": 1.1.0 + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + checksum: facc9528af43df3b01952dbb915ff751b5924db2c31d41f053ddea19a7cc5cac5b096c4d7a2059e8f564a3f0d4a95bcd909df8faed52fa01709af27337628e2c + languageName: node + linkType: hard + +"@radix-ui/react-use-size@npm:1.1.0": + version: 1.1.0 + resolution: "@radix-ui/react-use-size@npm:1.1.0" + dependencies: + "@radix-ui/react-use-layout-effect": 1.1.0 + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 01a11d4c07fc620b8a081e53d7ec8495b19a11e02688f3d9f47cf41a5fe0428d1e52ed60b2bf88dfd447dc2502797b9dad2841097389126dd108530913c4d90d + languageName: node + linkType: hard + +"@radix-ui/react-visually-hidden@npm:1.1.1": + version: 1.1.1 + resolution: "@radix-ui/react-visually-hidden@npm:1.1.1" + dependencies: + "@radix-ui/react-primitive": 2.0.1 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: ccbdf29811283fb257f0b0f8604923e6fe349a264986463f6d6a20946fc51e243527985e69f0af27659f78fd7a4199dacbba5bfc7af3667aa409cd23a0ae3283 + languageName: node + linkType: hard + +"@radix-ui/rect@npm:1.1.0": + version: 1.1.0 + resolution: "@radix-ui/rect@npm:1.1.0" + checksum: 1ad93efbc9fc3b878bae5e8bb26ffa1005235d8b5b9fca8339eb5dbcf7bf53abc9ccd2a8ce128557820168c8600521e48e0ea4dda96aa5f116381f66f46aeda3 + languageName: node + linkType: hard + +"@rollup/plugin-node-resolve@npm:^15.0.1": + version: 15.2.3 + resolution: "@rollup/plugin-node-resolve@npm:15.2.3" + dependencies: + "@rollup/pluginutils": ^5.0.1 + "@types/resolve": 1.20.2 + deepmerge: ^4.2.2 + is-builtin-module: ^3.2.1 + is-module: ^1.0.0 + resolve: ^1.22.1 + peerDependencies: + rollup: ^2.78.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + checksum: 730f32c2f8fdddff07cf0fca86a5dac7c475605fb96930197a868c066e62eb6388c557545e4f7d99b7a283411754c9fbf98944ab086b6074e04fc1292e234aa8 + languageName: node + linkType: hard + +"@rollup/pluginutils@npm:^5.0.1": + version: 5.1.0 + resolution: "@rollup/pluginutils@npm:5.1.0" + dependencies: + "@types/estree": ^1.0.0 + estree-walker: ^2.0.2 + picomatch: ^2.3.1 + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + checksum: 3cc5a6d91452a6eabbfd1ae79b4dd1f1e809d2eecda6e175deb784e75b0911f47e9ecce73f8dd315d6a8b3f362582c91d3c0f66908b6ced69345b3cbe28f8ce8 + languageName: node + linkType: hard + +"@rollup/rollup-android-arm-eabi@npm:4.9.4": + version: 4.9.4 + resolution: "@rollup/rollup-android-arm-eabi@npm:4.9.4" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@rollup/rollup-android-arm64@npm:4.9.4": + version: 4.9.4 + resolution: "@rollup/rollup-android-arm64@npm:4.9.4" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-darwin-arm64@npm:4.9.4": + version: 4.9.4 + resolution: "@rollup/rollup-darwin-arm64@npm:4.9.4" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-darwin-x64@npm:4.9.4": + version: 4.9.4 + resolution: "@rollup/rollup-darwin-x64@npm:4.9.4" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm-gnueabihf@npm:4.9.4": + version: 4.9.4 resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.9.4" conditions: os=linux & cpu=arm languageName: node @@ -5959,6 +7556,13 @@ __metadata: languageName: node linkType: hard +"@scure/base@npm:~1.1.2": + version: 1.1.9 + resolution: "@scure/base@npm:1.1.9" + checksum: 120820a37dfe9dfe4cab2b7b7460552d08e67dee8057ed5354eb68d8e3440890ae983ce3bee957d2b45684950b454a2b6d71d5ee77c1fd3fddc022e2a510337f + languageName: node + linkType: hard + "@scure/bip32@npm:1.1.5": version: 1.1.5 resolution: "@scure/bip32@npm:1.1.5" @@ -5970,13 +7574,34 @@ __metadata: languageName: node linkType: hard +"@scure/bip32@npm:1.3.2": + version: 1.3.2 + resolution: "@scure/bip32@npm:1.3.2" + dependencies: + "@noble/curves": ~1.2.0 + "@noble/hashes": ~1.3.2 + "@scure/base": ~1.1.2 + checksum: c5ae84fae43490853693b481531132b89e056d45c945fc8b92b9d032577f753dfd79c5a7bbcbf0a7f035951006ff0311b6cf7a389e26c9ec6335e42b20c53157 + languageName: node + linkType: hard + "@scure/bip39@npm:1.1.1": version: 1.1.1 resolution: "@scure/bip39@npm:1.1.1" dependencies: - "@noble/hashes": ~1.2.0 + "@noble/hashes": ~1.2.0 + "@scure/base": ~1.1.0 + checksum: fbb594c50696fa9c14e891d872f382e50a3f919b6c96c55ef2fb10c7102c546dafb8f099a62bd114c12a00525b595dcf7381846f383f0ddcedeaa6e210747d2f + languageName: node + linkType: hard + +"@scure/bip39@npm:1.2.1": + version: 1.2.1 + resolution: "@scure/bip39@npm:1.2.1" + dependencies: + "@noble/hashes": ~1.3.0 "@scure/base": ~1.1.0 - checksum: fbb594c50696fa9c14e891d872f382e50a3f919b6c96c55ef2fb10c7102c546dafb8f099a62bd114c12a00525b595dcf7381846f383f0ddcedeaa6e210747d2f + checksum: c5bd6f1328fdbeae2dcdd891825b1610225310e5e62a4942714db51066866e4f7bef242c7b06a1b9dcc8043a4a13412cf5c5df76d3b10aa9e36b82e9b6e3eeaa languageName: node linkType: hard @@ -6062,6 +7687,90 @@ __metadata: languageName: node linkType: hard +"@shikijs/core@npm:1.26.2": + version: 1.26.2 + resolution: "@shikijs/core@npm:1.26.2" + dependencies: + "@shikijs/engine-javascript": 1.26.2 + "@shikijs/engine-oniguruma": 1.26.2 + "@shikijs/types": 1.26.2 + "@shikijs/vscode-textmate": ^10.0.1 + "@types/hast": ^3.0.4 + hast-util-to-html: ^9.0.4 + checksum: b7bad4c281102bdd74f0974aa780efca06117208419c205005f172b247221d42685608d96dba97bd215eb6af99463d914517bad77fdc507e3254527f08f95975 + languageName: node + linkType: hard + +"@shikijs/engine-javascript@npm:1.26.2": + version: 1.26.2 + resolution: "@shikijs/engine-javascript@npm:1.26.2" + dependencies: + "@shikijs/types": 1.26.2 + "@shikijs/vscode-textmate": ^10.0.1 + oniguruma-to-es: ^1.0.0 + checksum: 8df3d284033b17f50625d07e0cdcd26d24ad8e821bb8e58cc91aebffd28befe7cf80169aadbc5bcc038433d68a2e18148b5098ef4e243f18c9cb634ba20ea034 + languageName: node + linkType: hard + +"@shikijs/engine-oniguruma@npm:1.26.2": + version: 1.26.2 + resolution: "@shikijs/engine-oniguruma@npm:1.26.2" + dependencies: + "@shikijs/types": 1.26.2 + "@shikijs/vscode-textmate": ^10.0.1 + checksum: d2d4978be0b4e8b3b26fd01ea0480568ac2264135704442e54ee14fb3b05f564492f95cae521378d6114641f249e892a76dc01fb0e4ae64522e56b3b353ce08d + languageName: node + linkType: hard + +"@shikijs/langs@npm:1.26.2": + version: 1.26.2 + resolution: "@shikijs/langs@npm:1.26.2" + dependencies: + "@shikijs/types": 1.26.2 + checksum: c3f1882401f19bc50cbf5fb3d62b287c2fc07bdbc98b281d1d63b51dac8602d00b8446918aae73d8e76e455b205542cc9a73526b64fe3c1b020952af8ebe26c5 + languageName: node + linkType: hard + +"@shikijs/rehype@npm:^1.12.1": + version: 1.26.2 + resolution: "@shikijs/rehype@npm:1.26.2" + dependencies: + "@shikijs/types": 1.26.2 + "@types/hast": ^3.0.4 + hast-util-to-string: ^3.0.1 + shiki: 1.26.2 + unified: ^11.0.5 + unist-util-visit: ^5.0.0 + checksum: b1ed82152275ebf8deadd97308b1ead12b91ce451409fce70307b6d6953c9a9c60d18dd1c3d6317039b30df14f33e9c01c42c7f0e149662f94221b110b3a569c + languageName: node + linkType: hard + +"@shikijs/themes@npm:1.26.2": + version: 1.26.2 + resolution: "@shikijs/themes@npm:1.26.2" + dependencies: + "@shikijs/types": 1.26.2 + checksum: 1e11093ae6e4fbf3573ca626dea19e78f07e1eb33a720ef56503e830743e7789e2203203738162f9b6f1bb273fe23b51b545ea3acdd5818b39fb7052475993fb + languageName: node + linkType: hard + +"@shikijs/types@npm:1.26.2": + version: 1.26.2 + resolution: "@shikijs/types@npm:1.26.2" + dependencies: + "@shikijs/vscode-textmate": ^10.0.1 + "@types/hast": ^3.0.4 + checksum: 77e0823a60dce4f37b85b2648ae75f00750fc897b6efaf7f8d765b3e57849456c5bfbe6ad9f1faa269c2ccddf7e4d7ab6dc5b43ab69c70ee09b4bde37f360cc4 + languageName: node + linkType: hard + +"@shikijs/vscode-textmate@npm:^10.0.1": + version: 10.0.1 + resolution: "@shikijs/vscode-textmate@npm:10.0.1" + checksum: c5a8490417b9439b055844c6c09c3435fc435b1fc3923eb28f05ee346fd68e69df2d93cdaab319a51193970558ff1bf49c5ab047c9ed4fd86c3f9d062457a565 + languageName: node + linkType: hard + "@sideway/address@npm:^4.1.3": version: 4.1.4 resolution: "@sideway/address@npm:4.1.4" @@ -6196,6 +7905,34 @@ __metadata: languageName: node linkType: hard +"@statsig/client-core@npm:3.9.0": + version: 3.9.0 + resolution: "@statsig/client-core@npm:3.9.0" + checksum: 2c8b8a99ac4ce1d24fe4e9263f55af37d4861455450dc348787bfa3cf2cf523a12e75a3aaa4fb676a853493174481f7e29d99196bcfd1ce30ffcb48137ff6453 + languageName: node + linkType: hard + +"@statsig/js-client@npm:3.9.0, @statsig/js-client@npm:^3.1.0": + version: 3.9.0 + resolution: "@statsig/js-client@npm:3.9.0" + dependencies: + "@statsig/client-core": 3.9.0 + checksum: 0f715c5043fb529baeadd256f8487f63aa05502cbe51d1f2053bcedf306638dc831eec05b0ad31522c05d28edabd854f9da4a4844e48b9fc27109e089679fb47 + languageName: node + linkType: hard + +"@statsig/react-bindings@npm:^3.1.0": + version: 3.9.0 + resolution: "@statsig/react-bindings@npm:3.9.0" + dependencies: + "@statsig/client-core": 3.9.0 + "@statsig/js-client": 3.9.0 + peerDependencies: + react: ^16.6.3 || ^17.0.0 || ^18.0.0 || ^19.0.0 + checksum: e6b2cb68e3ca720c1714c5b8bc1f1b67fbb2fff7eab07b7395b4c32e64957877233158e29f38129a404ba3ede670490db665b22b8d4c1ce3fda71a95866008e7 + languageName: node + linkType: hard + "@svgr/babel-plugin-add-jsx-attribute@npm:8.0.0": version: 8.0.0 resolution: "@svgr/babel-plugin-add-jsx-attribute@npm:8.0.0" @@ -6508,6 +8245,53 @@ __metadata: languageName: node linkType: hard +"@tailwindcss/line-clamp@npm:^0.4.4": + version: 0.4.4 + resolution: "@tailwindcss/line-clamp@npm:0.4.4" + peerDependencies: + tailwindcss: ">=2.0.0 || >=3.0.0 || >=3.0.0-alpha.1" + checksum: 3d2ad992aa9263fe9b5cdb23bcfca521a6ab00f65e0f7167be35d2cb46b1635af72889ff9f6d5b2febf5aa5a36e3128eaad8ed43e43af4512c74c74f1058c4c0 + languageName: node + linkType: hard + +"@tanstack/react-table@npm:^8.11.7": + version: 8.20.6 + resolution: "@tanstack/react-table@npm:8.20.6" + dependencies: + "@tanstack/table-core": 8.20.5 + peerDependencies: + react: ">=16.8" + react-dom: ">=16.8" + checksum: 709f7216cf31af65abe10121e92233c0d9f04b9167769a0997052200cdbc35af525cbf1879d456f27c6df70399a14d006d117b06c5db665d9c486038efc2f4b1 + languageName: node + linkType: hard + +"@tanstack/react-virtual@npm:^3.0.0-beta.60": + version: 3.11.2 + resolution: "@tanstack/react-virtual@npm:3.11.2" + dependencies: + "@tanstack/virtual-core": 3.11.2 + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + checksum: a1136da0ec4c2ecbd4f996d8b84f228f0b8d851b15806e01049a160ad1d9b2eef0e0a491035fe017c6f84a0e125334f69ea23b32c180df23614ea4a8eeb7490c + languageName: node + linkType: hard + +"@tanstack/table-core@npm:8.20.5": + version: 8.20.5 + resolution: "@tanstack/table-core@npm:8.20.5" + checksum: f8b175f11eb9ee1e029bb5e91c1038527714382de4bd14750377f43c25e69b687b21bfb181ee07131637d3432618964a4b7a898608fc8411ca50da1e7e8ed4c5 + languageName: node + linkType: hard + +"@tanstack/virtual-core@npm:3.11.2": + version: 3.11.2 + resolution: "@tanstack/virtual-core@npm:3.11.2" + checksum: b5c91662461e3edd1cba0efbaa89e1d061c8bb605bb78d1e87e2a687335c740a731c96a81798b05491df4882ff2fbd27b312f5e7440e4f9d553a81fb2283156a + languageName: node + linkType: hard + "@tootallnate/quickjs-emscripten@npm:^0.23.0": version: 0.23.0 resolution: "@tootallnate/quickjs-emscripten@npm:0.23.0" @@ -6699,6 +8483,75 @@ __metadata: languageName: node linkType: hard +"@types/d3-array@npm:^3.0.3": + version: 3.2.1 + resolution: "@types/d3-array@npm:3.2.1" + checksum: 8a41cee0969e53bab3f56cc15c4e6c9d76868d6daecb2b7d8c9ce71e0ececccc5a8239697cc52dadf5c665f287426de5c8ef31a49e7ad0f36e8846889a383df4 + languageName: node + linkType: hard + +"@types/d3-color@npm:*": + version: 3.1.3 + resolution: "@types/d3-color@npm:3.1.3" + checksum: 8a0e79a709929502ec4effcee2c786465b9aec51b653ba0b5d05dbfec3e84f418270dd603002d94021885061ff592f614979193bd7a02ad76317f5608560e357 + languageName: node + linkType: hard + +"@types/d3-ease@npm:^3.0.0": + version: 3.0.2 + resolution: "@types/d3-ease@npm:3.0.2" + checksum: 0885219966294bfc99548f37297e1c75e75da812a5f3ec941977ebb57dcab0a25acec5b2bbd82d09a49d387daafca08521ca269b7e4c27ddca7768189e987b54 + languageName: node + linkType: hard + +"@types/d3-interpolate@npm:^3.0.1": + version: 3.0.4 + resolution: "@types/d3-interpolate@npm:3.0.4" + dependencies: + "@types/d3-color": "*" + checksum: efd2770e174e84fc7316fdafe03cf3688451f767dde1fa6211610137f495be7f3923db7e1723a6961a0e0e9ae0ed969f4f47c038189fa0beb1d556b447922622 + languageName: node + linkType: hard + +"@types/d3-path@npm:*": + version: 3.1.0 + resolution: "@types/d3-path@npm:3.1.0" + checksum: 1e81b56ed33ba1ac954a8c42c78c3fcf2716927fe5d01b2003591193ad3b639572a3dfcedd9bf78b6b73215a5cfb01cede8f25c936e95ac18fbe3858f9b62f5c + languageName: node + linkType: hard + +"@types/d3-scale@npm:^4.0.2": + version: 4.0.8 + resolution: "@types/d3-scale@npm:4.0.8" + dependencies: + "@types/d3-time": "*" + checksum: 3b1906da895564f73bb3d0415033d9a8aefe7c4f516f970176d5b2ff7a417bd27ae98486e9a9aa0472001dc9885a9204279a1973a985553bdb3ee9bbc1b94018 + languageName: node + linkType: hard + +"@types/d3-shape@npm:^3.1.0": + version: 3.1.7 + resolution: "@types/d3-shape@npm:3.1.7" + dependencies: + "@types/d3-path": "*" + checksum: 776b982e2c4fc04763782af5100993c02bca338632ff2c76d2423ace398300ba7c48cd745f95b5f51edefabbfd026c45829a146c411f8facde09ef92580b20ce + languageName: node + linkType: hard + +"@types/d3-time@npm:*, @types/d3-time@npm:^3.0.0": + version: 3.0.4 + resolution: "@types/d3-time@npm:3.0.4" + checksum: 0c296884571ce70c4bbd4ea9cd1c93c0c8aee602c6c806b056187dd4ee49daf70c2f41da94b25ba0d796edf8ca83cbb87fe6d1cdda7ca669ab800170ece1c12b + languageName: node + linkType: hard + +"@types/d3-timer@npm:^3.0.0": + version: 3.0.2 + resolution: "@types/d3-timer@npm:3.0.2" + checksum: 1643eebfa5f4ae3eb00b556bbc509444d88078208ec2589ddd8e4a24f230dd4cf2301e9365947e70b1bee33f63aaefab84cd907822aae812b9bc4871b98ab0e1 + languageName: node + linkType: hard + "@types/debounce@npm:^1.2.0": version: 1.2.4 resolution: "@types/debounce@npm:1.2.4" @@ -6800,6 +8653,15 @@ __metadata: languageName: node linkType: hard +"@types/hast@npm:^3.0.4": + version: 3.0.4 + resolution: "@types/hast@npm:3.0.4" + dependencies: + "@types/unist": "*" + checksum: 7a973e8d16fcdf3936090fa2280f408fb2b6a4f13b42edeb5fbd614efe042b82eac68e298e556d50f6b4ad585a3a93c353e9c826feccdc77af59de8dd400d044 + languageName: node + linkType: hard + "@types/history@npm:^4.7.11": version: 4.7.11 resolution: "@types/history@npm:4.7.11" @@ -7285,6 +9147,13 @@ __metadata: languageName: node linkType: hard +"@types/stylis@npm:4.2.5": + version: 4.2.5 + resolution: "@types/stylis@npm:4.2.5" + checksum: 24f91719db5569979e9e2f197e050ef82e1fd72474e8dc45bca38d48ee56481eae0f0d4a7ac172540d7774b45a2a78d901a4c6d07bba77a33dbccff464ea3edf + languageName: node + linkType: hard + "@types/unist@npm:*, @types/unist@npm:^3.0.0": version: 3.0.2 resolution: "@types/unist@npm:3.0.2" @@ -7471,6 +9340,13 @@ __metadata: languageName: node linkType: hard +"@vercel/edge@npm:^1.1.1": + version: 1.2.1 + resolution: "@vercel/edge@npm:1.2.1" + checksum: 3cddadf0bd9d733400cab5bdd73bf5c0fcba02edcfa5e8cc483491e9d5b31791667601ffa9b566b375d463f05d1e0408c39fd6a03044f289736569fde065c99b + languageName: node + linkType: hard + "@wasm-tool/wasm-pack-plugin@npm:^1.7.0": version: 1.7.0 resolution: "@wasm-tool/wasm-pack-plugin@npm:1.7.0" @@ -7966,6 +9842,21 @@ __metadata: languageName: node linkType: hard +"abitype@npm:1.0.0": + version: 1.0.0 + resolution: "abitype@npm:1.0.0" + peerDependencies: + typescript: ">=5.0.4" + zod: ^3 >=3.22.0 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + checksum: ea2c0548c3ba58c37a6de7483d63389074da498e63d803b742bbe94eb4eaa1f51a35d000c424058b2583aef56698cf07c696eb3bc4dd0303bc20c6f0826a241a + languageName: node + linkType: hard + "abort-controller@npm:^3.0.0": version: 3.0.0 resolution: "abort-controller@npm:3.0.0" @@ -8267,6 +10158,13 @@ __metadata: languageName: node linkType: hard +"ansi-styles@npm:^5.0.0": + version: 5.2.0 + resolution: "ansi-styles@npm:5.2.0" + checksum: d7f4e97ce0623aea6bc0d90dcd28881ee04cba06c570b97fd3391bd7a268eedfd9d5e2dd4fdcbdd82b8105df5faf6f24aaedc08eaf3da898e702db5948f63469 + languageName: node + linkType: hard + "ansi-styles@npm:^6.1.0": version: 6.2.1 resolution: "ansi-styles@npm:6.2.1" @@ -8321,6 +10219,15 @@ __metadata: languageName: node linkType: hard +"aria-hidden@npm:^1.1.1": + version: 1.2.4 + resolution: "aria-hidden@npm:1.2.4" + dependencies: + tslib: ^2.0.0 + checksum: 2ac90b70d29c6349d86d90e022cf01f4885f9be193932d943a14127cf28560dd0baf068a6625f084163437a4be0578f513cf7892f4cc63bfe91aa41dce27c6b2 + languageName: node + linkType: hard + "array-back@npm:^3.0.1, array-back@npm:^3.1.0": version: 3.1.0 resolution: "array-back@npm:3.1.0" @@ -8733,6 +10640,17 @@ __metadata: languageName: node linkType: hard +"bcp-47@npm:^2.1.0": + version: 2.1.0 + resolution: "bcp-47@npm:2.1.0" + dependencies: + is-alphabetical: ^2.0.0 + is-alphanumerical: ^2.0.0 + is-decimal: ^2.0.0 + checksum: 2ae12b551f0ef4da3684617d12941430091efa1114a89028f6ee05eba3df06e314cca2988cde43e7d66dc9d5799eac1201556b0c3a5df99efe514928144bab1e + languageName: node + linkType: hard + "big.js@npm:^5.2.2": version: 5.2.2 resolution: "big.js@npm:5.2.2" @@ -9192,6 +11110,62 @@ __metadata: languageName: node linkType: hard +"camelize@npm:^1.0.0": + version: 1.0.1 + resolution: "camelize@npm:1.0.1" + checksum: 91d8611d09af725e422a23993890d22b2b72b4cabf7239651856950c76b4bf53fe0d0da7c5e4db05180e898e4e647220e78c9fbc976113bd96d603d1fcbfcb99 + languageName: node + linkType: hard + +"can-dom-mutate@npm:^2.0.9": + version: 2.0.9 + resolution: "can-dom-mutate@npm:2.0.9" + dependencies: + can-globals: ^1.0.0 + can-namespace: 1.0.0 + can-reflect: ^1.17.6 + can-symbol: ^1.6.4 + checksum: 60165c5671335bcea1a1f432865ff5ee0e4e1b4980f1ff06eacea405a597d6605e106da7920d2d9618b18da0d0238a75163b81c6f9f92427c99474bfd16c689c + languageName: node + linkType: hard + +"can-globals@npm:^1.0.0": + version: 1.2.2 + resolution: "can-globals@npm:1.2.2" + dependencies: + can-namespace: ^1.0.0 + can-reflect: ^1.2.6 + can-symbol: ^1.0.0 + checksum: f7cdb19227e4118923ea09ab24d20e3be23a72973ca2f181c4d9da2949b23cfea14566b91f865cda2c50520bd774060eeca0b1ea360df2131b3f55d9ca346865 + languageName: node + linkType: hard + +"can-namespace@npm:1.0.0, can-namespace@npm:^1.0.0": + version: 1.0.0 + resolution: "can-namespace@npm:1.0.0" + checksum: 93a27be2c89bde52941bc18b606399c0cbbed596ac525f611a6fc66b7f684e678f4675ac9a53d0e25457159762e656505d23b55fdb1d116c30a7d78d18723577 + languageName: node + linkType: hard + +"can-reflect@npm:^1.17.6, can-reflect@npm:^1.2.6": + version: 1.19.2 + resolution: "can-reflect@npm:1.19.2" + dependencies: + can-namespace: ^1.0.0 + can-symbol: ^1.7.0 + checksum: 7022a5dc8b182beda745a7e3c33b67d9d7fd81bd75277e8db19714c54cfc485bce0899c5e2f879f993d484e7dba03494ff8d5494dd69ef28fb70462846050a5e + languageName: node + linkType: hard + +"can-symbol@npm:^1.0.0, can-symbol@npm:^1.6.4, can-symbol@npm:^1.7.0": + version: 1.7.0 + resolution: "can-symbol@npm:1.7.0" + dependencies: + can-namespace: ^1.0.0 + checksum: b51dc2abfb8cfc75cf1057fcef32317a278904860018e2cdec7852d23ad310cacb5d5ebf216030bf0ed1a6eae65eb12bca83f9e78ea81f33eb195d5e38cd8cf1 + languageName: node + linkType: hard + "caniuse-api@npm:^3.0.0": version: 3.0.0 resolution: "caniuse-api@npm:3.0.0" @@ -9569,6 +11543,13 @@ __metadata: languageName: node linkType: hard +"client-only@npm:^0.0.1": + version: 0.0.1 + resolution: "client-only@npm:0.0.1" + checksum: 0c16bf660dadb90610553c1d8946a7fdfb81d624adea073b8440b7d795d5b5b08beb3c950c6a2cf16279365a3265158a236876d92bce16423c485c322d7dfaf8 + languageName: node + linkType: hard + "clipboardy@npm:3.0.0": version: 3.0.0 resolution: "clipboardy@npm:3.0.0" @@ -9650,6 +11631,28 @@ __metadata: languageName: node linkType: hard +"clsx@npm:^2.1.0": + version: 2.1.1 + resolution: "clsx@npm:2.1.1" + checksum: acd3e1ab9d8a433ecb3cc2f6a05ab95fe50b4a3cfc5ba47abb6cbf3754585fcb87b84e90c822a1f256c4198e3b41c7f6c391577ffc8678ad587fc0976b24fd57 + languageName: node + linkType: hard + +"cmdk-react17@npm:^1.0.0": + version: 1.0.0 + resolution: "cmdk-react17@npm:1.0.0" + dependencies: + "@radix-ui/react-dialog": 1.0.5 + "@radix-ui/react-id": ^1.0.1 + "@radix-ui/react-primitive": 1.0.3 + use-sync-external-store: ^1.2.0 + peerDependencies: + react: ^17.0.0 + react-dom: ^17.0.0 + checksum: d10812fef42f0d9bd34aa66dbe7b294148ae7b2404e934e89f1fbe9de0b0f28b8f30bf38a79f6928e8eed37340aa7660cf45a4527b68d040b8c44c268de5dc24 + languageName: node + linkType: hard + "co-body@npm:^6.1.0": version: 6.1.0 resolution: "co-body@npm:6.1.0" @@ -9715,6 +11718,13 @@ __metadata: languageName: node linkType: hard +"color2k@npm:^2.0.3": + version: 2.0.3 + resolution: "color2k@npm:2.0.3" + checksum: 0748e16e43c1740045af61f44de5d181f3f7a2a9cb0d5cccdccee23d04e3f107f02aaafebb7ca3335cca1d11849c6321aba702eb2f893e993a77f65761de7661 + languageName: node + linkType: hard + "colord@npm:^2.9.1, colord@npm:^2.9.3": version: 2.9.3 resolution: "colord@npm:2.9.3" @@ -10111,6 +12121,13 @@ __metadata: languageName: node linkType: hard +"core-js@npm:^3.38.1": + version: 3.40.0 + resolution: "core-js@npm:3.40.0" + checksum: fc962b93470fd4a129555c765b630c1741fc38706bca68779879f0feaef3b6eec11a33904e3111b2b0e8ba206e8cfbc2a70193271227cfa2f2d13a986f78e557 + languageName: node + linkType: hard + "core-util-is@npm:^1.0.3, core-util-is@npm:~1.0.0": version: 1.0.3 resolution: "core-util-is@npm:1.0.3" @@ -10144,7 +12161,7 @@ __metadata: languageName: node linkType: hard -"cosmiconfig@npm:^8.1.3, cosmiconfig@npm:^8.2.0": +"cosmiconfig@npm:^8.0.0, cosmiconfig@npm:^8.1.3, cosmiconfig@npm:^8.2.0": version: 8.3.6 resolution: "cosmiconfig@npm:8.3.6" dependencies: @@ -10366,6 +12383,13 @@ __metadata: languageName: node linkType: hard +"css-color-keywords@npm:^1.0.0": + version: 1.0.0 + resolution: "css-color-keywords@npm:1.0.0" + checksum: 8f125e3ad477bd03c77b533044bd9e8a6f7c0da52d49bbc0bbe38327b3829d6ba04d368ca49dd9ff3b667d2fc8f1698d891c198bbf8feade1a5501bf5a296408 + languageName: node + linkType: hard + "css-declaration-sorter@npm:^6.3.1": version: 6.4.1 resolution: "css-declaration-sorter@npm:6.4.1" @@ -10486,6 +12510,17 @@ __metadata: languageName: node linkType: hard +"css-to-react-native@npm:3.2.0": + version: 3.2.0 + resolution: "css-to-react-native@npm:3.2.0" + dependencies: + camelize: ^1.0.0 + css-color-keywords: ^1.0.0 + postcss-value-parser: ^4.0.2 + checksum: 263be65e805aef02c3f20c064665c998a8c35293e1505dbe6e3054fb186b01a9897ac6cf121f9840e5a9dfe3fb3994f6fcd0af84a865f1df78ba5bf89e77adce + languageName: node + linkType: hard + "css-tree@npm:^1.1.2, css-tree@npm:^1.1.3": version: 1.1.3 resolution: "css-tree@npm:1.1.3" @@ -10700,15 +12735,108 @@ __metadata: version: 5.0.5 resolution: "csso@npm:5.0.5" dependencies: - css-tree: ~2.2.0 - checksum: 0ad858d36bf5012ed243e9ec69962a867509061986d2ee07cc040a4b26e4d062c00d4c07e5ba8d430706ceb02dd87edd30a52b5937fd45b1b6f2119c4993d59a + css-tree: ~2.2.0 + checksum: 0ad858d36bf5012ed243e9ec69962a867509061986d2ee07cc040a4b26e4d062c00d4c07e5ba8d430706ceb02dd87edd30a52b5937fd45b1b6f2119c4993d59a + languageName: node + linkType: hard + +"csstype@npm:3.1.3, csstype@npm:^3.0.2": + version: 3.1.3 + resolution: "csstype@npm:3.1.3" + checksum: 8db785cc92d259102725b3c694ec0c823f5619a84741b5c7991b8ad135dfaa66093038a1cc63e03361a6cd28d122be48f2106ae72334e067dd619a51f49eddf7 + languageName: node + linkType: hard + +"d3-array@npm:2 - 3, d3-array@npm:2.10.0 - 3, d3-array@npm:^3.1.6": + version: 3.2.4 + resolution: "d3-array@npm:3.2.4" + dependencies: + internmap: 1 - 2 + checksum: a5976a6d6205f69208478bb44920dd7ce3e788c9dceb86b304dbe401a4bfb42ecc8b04c20facde486e9adcb488b5d1800d49393a3f81a23902b68158e12cddd0 + languageName: node + linkType: hard + +"d3-color@npm:1 - 3": + version: 3.1.0 + resolution: "d3-color@npm:3.1.0" + checksum: 4931fbfda5d7c4b5cfa283a13c91a954f86e3b69d75ce588d06cde6c3628cebfc3af2069ccf225e982e8987c612aa7948b3932163ce15eb3c11cd7c003f3ee3b + languageName: node + linkType: hard + +"d3-ease@npm:^3.0.1": + version: 3.0.1 + resolution: "d3-ease@npm:3.0.1" + checksum: 06e2ee5326d1e3545eab4e2c0f84046a123dcd3b612e68858219aa034da1160333d9ce3da20a1d3486d98cb5c2a06f7d233eee1bc19ce42d1533458bd85dedcd + languageName: node + linkType: hard + +"d3-format@npm:1 - 3": + version: 3.1.0 + resolution: "d3-format@npm:3.1.0" + checksum: f345ec3b8ad3cab19bff5dead395bd9f5590628eb97a389b1dd89f0b204c7c4fc1d9520f13231c2c7cf14b7c9a8cf10f8ef15bde2befbab41454a569bd706ca2 + languageName: node + linkType: hard + +"d3-interpolate@npm:1.2.0 - 3, d3-interpolate@npm:^3.0.1": + version: 3.0.1 + resolution: "d3-interpolate@npm:3.0.1" + dependencies: + d3-color: 1 - 3 + checksum: a42ba314e295e95e5365eff0f604834e67e4a3b3c7102458781c477bd67e9b24b6bb9d8e41ff5521050a3f2c7c0c4bbbb6e187fd586daa3980943095b267e78b + languageName: node + linkType: hard + +"d3-path@npm:^3.1.0": + version: 3.1.0 + resolution: "d3-path@npm:3.1.0" + checksum: 2306f1bd9191e1eac895ec13e3064f732a85f243d6e627d242a313f9777756838a2215ea11562f0c7630c7c3b16a19ec1fe0948b1c82f3317fac55882f6ee5d8 + languageName: node + linkType: hard + +"d3-scale@npm:^4.0.2": + version: 4.0.2 + resolution: "d3-scale@npm:4.0.2" + dependencies: + d3-array: 2.10.0 - 3 + d3-format: 1 - 3 + d3-interpolate: 1.2.0 - 3 + d3-time: 2.1.1 - 3 + d3-time-format: 2 - 4 + checksum: a9c770d283162c3bd11477c3d9d485d07f8db2071665f1a4ad23eec3e515e2cefbd369059ec677c9ac849877d1a765494e90e92051d4f21111aa56791c98729e + languageName: node + linkType: hard + +"d3-shape@npm:^3.1.0": + version: 3.2.0 + resolution: "d3-shape@npm:3.2.0" + dependencies: + d3-path: ^3.1.0 + checksum: de2af5fc9a93036a7b68581ca0bfc4aca2d5a328aa7ba7064c11aedd44d24f310c20c40157cb654359d4c15c3ef369f95ee53d71221017276e34172c7b719cfa + languageName: node + linkType: hard + +"d3-time-format@npm:2 - 4": + version: 4.1.0 + resolution: "d3-time-format@npm:4.1.0" + dependencies: + d3-time: 1 - 3 + checksum: 7342bce28355378152bbd4db4e275405439cabba082d9cd01946d40581140481c8328456d91740b0fe513c51ec4a467f4471ffa390c7e0e30ea30e9ec98fcdf4 + languageName: node + linkType: hard + +"d3-time@npm:1 - 3, d3-time@npm:2.1.1 - 3, d3-time@npm:^3.0.0": + version: 3.1.0 + resolution: "d3-time@npm:3.1.0" + dependencies: + d3-array: 2 - 3 + checksum: 613b435352a78d9f31b7f68540788186d8c331b63feca60ad21c88e9db1989fe888f97f242322ebd6365e45ec3fb206a4324cd4ca0dfffa1d9b5feb856ba00a7 languageName: node linkType: hard -"csstype@npm:^3.0.2": - version: 3.1.3 - resolution: "csstype@npm:3.1.3" - checksum: 8db785cc92d259102725b3c694ec0c823f5619a84741b5c7991b8ad135dfaa66093038a1cc63e03361a6cd28d122be48f2106ae72334e067dd619a51f49eddf7 +"d3-timer@npm:^3.0.1": + version: 3.0.1 + resolution: "d3-timer@npm:3.0.1" + checksum: 1cfddf86d7bca22f73f2c427f52dfa35c49f50d64e187eb788dcad6e927625c636aa18ae4edd44d084eb9d1f81d8ca4ec305dae7f733c15846a824575b789d73 languageName: node linkType: hard @@ -10763,6 +12891,13 @@ __metadata: languageName: node linkType: hard +"decimal.js-light@npm:^2.4.1": + version: 2.5.1 + resolution: "decimal.js-light@npm:2.5.1" + checksum: f5a2c7eac1c4541c8ab8a5c8abea64fc1761cefc7794bd5f8afd57a8a78d1b51785e0c4e4f85f4895a043eaa90ddca1edc3981d1263eb6ddce60f32bf5fe66c9 + languageName: node + linkType: hard + "decode-named-character-reference@npm:^1.0.0": version: 1.0.2 resolution: "decode-named-character-reference@npm:1.0.2" @@ -10974,7 +13109,7 @@ __metadata: languageName: node linkType: hard -"dequal@npm:^2.0.0": +"dequal@npm:^2.0.0, dequal@npm:^2.0.3": version: 2.0.3 resolution: "dequal@npm:2.0.3" checksum: 8679b850e1a3d0ebbc46ee780d5df7b478c23f335887464023a631d1b9af051ad4a6595a44220f9ff8ff95a8ddccf019b5ad778a976fd7bbf77383d36f412f90 @@ -10997,6 +13132,13 @@ __metadata: languageName: node linkType: hard +"detect-node-es@npm:^1.1.0": + version: 1.1.0 + resolution: "detect-node-es@npm:1.1.0" + checksum: e46307d7264644975b71c104b9f028ed1d3d34b83a15b8a22373640ce5ea630e5640b1078b8ea15f202b54641da71e4aa7597093bd4b91f113db520a26a37449 + languageName: node + linkType: hard + "detect-node@npm:^2.0.4": version: 2.1.0 resolution: "detect-node@npm:2.1.0" @@ -11096,6 +13238,7 @@ __metadata: version: 0.0.0-use.local resolution: "docs@workspace:docs" dependencies: + "@cookbookdev/docsbot": ^4.24.9 "@docusaurus/core": ^3.5.2 "@docusaurus/module-type-aliases": ^3.5.2 "@docusaurus/preset-classic": ^3.5.2 @@ -11158,6 +13301,16 @@ __metadata: languageName: node linkType: hard +"dom-helpers@npm:^5.0.1": + version: 5.2.1 + resolution: "dom-helpers@npm:5.2.1" + dependencies: + "@babel/runtime": ^7.8.7 + csstype: ^3.0.2 + checksum: 863ba9e086f7093df3376b43e74ce4422571d404fc9828bf2c56140963d5edf0e56160f9b2f3bb61b282c07f8fc8134f023c98fd684bddcb12daf7b0f14d951c + languageName: node + linkType: hard + "dom-serializer@npm:^1.0.1": version: 1.4.1 resolution: "dom-serializer@npm:1.4.1" @@ -11312,6 +13465,13 @@ __metadata: languageName: node linkType: hard +"emoji-regex-xs@npm:^1.0.0": + version: 1.0.0 + resolution: "emoji-regex-xs@npm:1.0.0" + checksum: c33be159da769836f83281f2802d90169093ebf3c2c1643d6801d891c53beac5ef785fd8279f9b02fa6dc6c47c367818e076949f1e13bd1b3f921b416de4cbea + languageName: node + linkType: hard + "emoji-regex@npm:^8.0.0": version: 8.0.0 resolution: "emoji-regex@npm:8.0.0" @@ -12044,7 +14204,7 @@ __metadata: languageName: node linkType: hard -"eventemitter3@npm:^4.0.0": +"eventemitter3@npm:^4.0.0, eventemitter3@npm:^4.0.1": version: 4.0.7 resolution: "eventemitter3@npm:4.0.7" checksum: 1875311c42fcfe9c707b2712c32664a245629b42bb0a5a84439762dd0fd637fc54d078155ea83c2af9e0323c9ac13687e03cfba79b03af9f40c89b4960099374 @@ -12281,6 +14441,13 @@ __metadata: languageName: node linkType: hard +"fflate@npm:^0.4.8": + version: 0.4.8 + resolution: "fflate@npm:0.4.8" + checksum: 29d8cbe44d5e7f53e7f5a160ac7f9cc025480c7b3bfd85c5f898cbe20dfa2dad4732daa534982664bf30b35896a90af44ea33ede5d94c5ffd1b8b0c0a0a56ca2 + languageName: node + linkType: hard + "fflate@npm:^0.8.0": version: 0.8.1 resolution: "fflate@npm:0.8.1" @@ -12288,6 +14455,13 @@ __metadata: languageName: node linkType: hard +"fflate@npm:^0.8.2": + version: 0.8.2 + resolution: "fflate@npm:0.8.2" + checksum: 29470337b85d3831826758e78f370e15cda3169c5cd4477c9b5eea2402261a74b2975bae816afabe1c15d21d98591e0d30a574f7103aa117bff60756fa3035d4 + languageName: node + linkType: hard + "file-entry-cache@npm:^6.0.1": version: 6.0.1 resolution: "file-entry-cache@npm:6.0.1" @@ -12318,6 +14492,13 @@ __metadata: languageName: node linkType: hard +"file-saver@npm:^2.0.5": + version: 2.0.5 + resolution: "file-saver@npm:2.0.5" + checksum: c62d96e5cebc58b4bdf3ae8a60d5cf9607ad82f75f798c33a4ee63435ac2203002584d5256a2a780eda7feb5e19dc3b6351c2212e58b3f529e63d265a7cc79f7 + languageName: node + linkType: hard + "filesize@npm:^8.0.6": version: 8.0.7 resolution: "filesize@npm:8.0.7" @@ -12592,6 +14773,36 @@ __metadata: languageName: node linkType: hard +"framer-motion@npm:^6.5.1": + version: 6.5.1 + resolution: "framer-motion@npm:6.5.1" + dependencies: + "@emotion/is-prop-valid": ^0.8.2 + "@motionone/dom": 10.12.0 + framesync: 6.0.1 + hey-listen: ^1.0.8 + popmotion: 11.0.3 + style-value-types: 5.0.0 + tslib: ^2.1.0 + peerDependencies: + react: ">=16.8 || ^17.0.0 || ^18.0.0" + react-dom: ">=16.8 || ^17.0.0 || ^18.0.0" + dependenciesMeta: + "@emotion/is-prop-valid": + optional: true + checksum: 737959063137b4ccafe01e0ac0c9e5a9531bf3f729f62c34ca7a5d7955e6664f70affd22b044f7db51df41acb21d120a4f71a860e17a80c4db766ad66f2153a1 + languageName: node + linkType: hard + +"framesync@npm:6.0.1": + version: 6.0.1 + resolution: "framesync@npm:6.0.1" + dependencies: + tslib: ^2.1.0 + checksum: a23ebe8f7e20a32c0b99c2f8175b6f07af3ec6316aad52a2316316a6d011d717af8d2175dcc2827031c59fabb30232ed3e19a720a373caba7f070e1eae436325 + languageName: node + linkType: hard + "fresh@npm:0.5.2, fresh@npm:~0.5.2": version: 0.5.2 resolution: "fresh@npm:0.5.2" @@ -12781,6 +14992,13 @@ __metadata: languageName: node linkType: hard +"get-nonce@npm:^1.0.0": + version: 1.0.1 + resolution: "get-nonce@npm:1.0.1" + checksum: e2614e43b4694c78277bb61b0f04583d45786881289285c73770b07ded246a98be7e1f78b940c80cbe6f2b07f55f0b724e6db6fd6f1bcbd1e8bdac16521074ed + languageName: node + linkType: hard + "get-own-enumerable-property-symbols@npm:^3.0.0": version: 3.0.2 resolution: "get-own-enumerable-property-symbols@npm:3.0.2" @@ -13449,6 +15667,25 @@ __metadata: languageName: node linkType: hard +"hast-util-to-html@npm:^9.0.4": + version: 9.0.4 + resolution: "hast-util-to-html@npm:9.0.4" + dependencies: + "@types/hast": ^3.0.0 + "@types/unist": ^3.0.0 + ccount: ^2.0.0 + comma-separated-tokens: ^2.0.0 + hast-util-whitespace: ^3.0.0 + html-void-elements: ^3.0.0 + mdast-util-to-hast: ^13.0.0 + property-information: ^6.0.0 + space-separated-tokens: ^2.0.0 + stringify-entities: ^4.0.0 + zwitch: ^2.0.4 + checksum: 6b97f641bca4c1de66bd74dd5a965bc5fd5c4b8e09328448c4952226ebd691c107cc990ce4e29ccb1e6bfff0278d8956fc8159533456c167f94ae067b4b42b11 + languageName: node + linkType: hard + "hast-util-to-jsx-runtime@npm:^2.0.0": version: 2.3.0 resolution: "hast-util-to-jsx-runtime@npm:2.3.0" @@ -13500,6 +15737,15 @@ __metadata: languageName: node linkType: hard +"hast-util-to-string@npm:^3.0.1": + version: 3.0.1 + resolution: "hast-util-to-string@npm:3.0.1" + dependencies: + "@types/hast": ^3.0.0 + checksum: 556f3cb118fc09e3a6cd149ee4b4056a49028a3858a7d37617e4c6d2c9c5e2336d5fb87eb5f41211b1977a964c705aa70e419464c12debc1959ed03fdad5bed6 + languageName: node + linkType: hard + "hast-util-to-text@npm:^4.0.0": version: 4.0.0 resolution: "hast-util-to-text@npm:4.0.0" @@ -13556,6 +15802,13 @@ __metadata: languageName: node linkType: hard +"hey-listen@npm:^1.0.8": + version: 1.0.8 + resolution: "hey-listen@npm:1.0.8" + checksum: 6bad60b367688f5348e25e7ca3276a74b59ac5a09b0455e6ff8ab7d4a9e38cd2116c708a7dcd8a954d27253ce1d8717ec891d175723ea739885b828cf44e4072 + languageName: node + linkType: hard + "history@npm:^4.9.0": version: 4.10.1 resolution: "history@npm:4.10.1" @@ -13937,6 +16190,13 @@ __metadata: languageName: node linkType: hard +"idb@npm:^8.0.0": + version: 8.0.1 + resolution: "idb@npm:8.0.1" + checksum: 1ba2a1896920bcbfa5b7f826e2d152b0af46415711b74f32649322885c32b94a93fe6b0b78187edda5e77ca77790b74d0e7324a7b14893e0bd4842efcc9bedc2 + languageName: node + linkType: hard + "ieee754@npm:^1.1.13, ieee754@npm:^1.2.1": version: 1.2.1 resolution: "ieee754@npm:1.2.1" @@ -13969,6 +16229,13 @@ __metadata: languageName: node linkType: hard +"immediate@npm:~3.0.5": + version: 3.0.6 + resolution: "immediate@npm:3.0.6" + checksum: f9b3486477555997657f70318cc8d3416159f208bec4cca3ff3442fd266bc23f50f0c9bd8547e1371a6b5e82b821ec9a7044a4f7b944798b25aa3cc6d5e63e62 + languageName: node + linkType: hard + "immer@npm:^9.0.7": version: 9.0.21 resolution: "immer@npm:9.0.21" @@ -14144,6 +16411,13 @@ __metadata: languageName: unknown linkType: soft +"internmap@npm:1 - 2": + version: 2.0.3 + resolution: "internmap@npm:2.0.3" + checksum: 7ca41ec6aba8f0072fc32fa8a023450a9f44503e2d8e403583c55714b25efd6390c38a87161ec456bf42d7bc83aab62eb28f5aef34876b1ac4e60693d5e1d241 + languageName: node + linkType: hard + "interpret@npm:^1.0.0": version: 1.4.0 resolution: "interpret@npm:1.4.0" @@ -14710,6 +16984,13 @@ __metadata: languageName: node linkType: hard +"iso-639-1@npm:^3.1.3": + version: 3.1.3 + resolution: "iso-639-1@npm:3.1.3" + checksum: 9a4cf417a91f638af247328e2b92ca135ec82eedbab139246cfd0a53d2dee052a8abc1639ca997e84fbebb0cd536cb7fb88910433c922122bcb7250a6a16d8e9 + languageName: node + linkType: hard + "isobject@npm:^3.0.1": version: 3.0.1 resolution: "isobject@npm:3.0.1" @@ -14717,6 +16998,15 @@ __metadata: languageName: node linkType: hard +"isows@npm:1.0.3": + version: 1.0.3 + resolution: "isows@npm:1.0.3" + peerDependencies: + ws: "*" + checksum: 9cacd5cf59f67deb51e825580cd445ab1725ecb05a67c704050383fb772856f3cd5e7da8ad08f5a3bd2823680d77d099459d0c6a7037972a74d6429af61af440 + languageName: node + linkType: hard + "istanbul-lib-coverage@npm:^3.0.0": version: 3.2.2 resolution: "istanbul-lib-coverage@npm:3.2.2" @@ -14758,6 +17048,13 @@ __metadata: languageName: node linkType: hard +"jest-get-type@npm:^29.6.3": + version: 29.6.3 + resolution: "jest-get-type@npm:29.6.3" + checksum: 88ac9102d4679d768accae29f1e75f592b760b44277df288ad76ce5bf038c3f5ce3719dea8aa0f035dac30e9eb034b848ce716b9183ad7cc222d029f03e92205 + languageName: node + linkType: hard + "jest-util@npm:^29.7.0": version: 29.7.0 resolution: "jest-util@npm:29.7.0" @@ -14772,6 +17069,20 @@ __metadata: languageName: node linkType: hard +"jest-validate@npm:^29.4.3": + version: 29.7.0 + resolution: "jest-validate@npm:29.7.0" + dependencies: + "@jest/types": ^29.6.3 + camelcase: ^6.2.0 + chalk: ^4.0.0 + jest-get-type: ^29.6.3 + leven: ^3.1.0 + pretty-format: ^29.7.0 + checksum: 191fcdc980f8a0de4dbdd879fa276435d00eb157a48683af7b3b1b98b0f7d9de7ffe12689b617779097ff1ed77601b9f7126b0871bba4f776e222c40f62e9dae + languageName: node + linkType: hard + "jest-worker@npm:^27.4.5": version: 27.5.1 resolution: "jest-worker@npm:27.5.1" @@ -14795,6 +17106,15 @@ __metadata: languageName: node linkType: hard +"jiti@npm:^1.17.1": + version: 1.21.7 + resolution: "jiti@npm:1.21.7" + bin: + jiti: bin/jiti.js + checksum: 9cd20dabf82e3a4cceecb746a69381da7acda93d34eed0cdb9c9bdff3bce07e4f2f4a016ca89924392c935297d9aedc58ff9f7d3281bc5293319ad244926e0b7 + languageName: node + linkType: hard + "jiti@npm:^1.18.2, jiti@npm:^1.20.0": version: 1.21.0 resolution: "jiti@npm:1.21.0" @@ -14817,6 +17137,13 @@ __metadata: languageName: node linkType: hard +"js-sha256@npm:^0.10.1": + version: 0.10.1 + resolution: "js-sha256@npm:0.10.1" + checksum: 6eb5c9f95aa902cec1930f036deb3bc664024b75fede456c0ac74b855797776c18620f47efec36787077a56ba2f3b8d6aacc7733ff8a2b5bb9ce6b655a35c5e6 + languageName: node + linkType: hard + "js-sha3@npm:0.8.0": version: 0.8.0 resolution: "js-sha3@npm:0.8.0" @@ -14863,6 +17190,15 @@ __metadata: languageName: node linkType: hard +"jsesc@npm:^3.0.2": + version: 3.1.0 + resolution: "jsesc@npm:3.1.0" + bin: + jsesc: bin/jsesc + checksum: 19c94095ea026725540c0d29da33ab03144f6bcf2d4159e4833d534976e99e0c09c38cefa9a575279a51fc36b31166f8d6d05c9fe2645d5f15851d690b41f17f + languageName: node + linkType: hard + "jsesc@npm:~0.5.0": version: 0.5.0 resolution: "jsesc@npm:0.5.0" @@ -14937,7 +17273,7 @@ __metadata: languageName: node linkType: hard -"json5@npm:^2.1.2, json5@npm:^2.2.3": +"json5@npm:^2.1.2, json5@npm:^2.2.0, json5@npm:^2.2.3": version: 2.2.3 resolution: "json5@npm:2.2.3" bin: @@ -14978,6 +17314,18 @@ __metadata: languageName: node linkType: hard +"jszip@npm:^3.10.1": + version: 3.10.1 + resolution: "jszip@npm:3.10.1" + dependencies: + lie: ~3.3.0 + pako: ~1.0.2 + readable-stream: ~2.3.6 + setimmediate: ^1.0.5 + checksum: abc77bfbe33e691d4d1ac9c74c8851b5761fba6a6986630864f98d876f3fcc2d36817dfc183779f32c00157b5d53a016796677298272a714ae096dfe6b1c8b60 + languageName: node + linkType: hard + "just-extend@npm:^4.0.2": version: 4.2.1 resolution: "just-extend@npm:4.2.1" @@ -15284,6 +17632,15 @@ __metadata: languageName: node linkType: hard +"lie@npm:~3.3.0": + version: 3.3.0 + resolution: "lie@npm:3.3.0" + dependencies: + immediate: ~3.0.5 + checksum: 33102302cf19766f97919a6a98d481e01393288b17a6aa1f030a3542031df42736edde8dab29ffdbf90bebeffc48c761eb1d064dc77592ca3ba3556f9fe6d2a8 + languageName: node + linkType: hard + "lighthouse-logger@npm:^1.0.0": version: 1.4.2 resolution: "lighthouse-logger@npm:1.4.2" @@ -15741,6 +18098,19 @@ __metadata: languageName: node linkType: hard +"mdast-util-from-markdown@npm:^0.8.0": + version: 0.8.5 + resolution: "mdast-util-from-markdown@npm:0.8.5" + dependencies: + "@types/mdast": ^3.0.0 + mdast-util-to-string: ^2.0.0 + micromark: ~2.11.0 + parse-entities: ^2.0.0 + unist-util-stringify-position: ^2.0.0 + checksum: 5a9d0d753a42db763761e874c22365d0c7c9934a5a18b5ff76a0643610108a208a041ffdb2f3d3dd1863d3d915225a4020a0aade282af0facfd0df110601eee6 + languageName: node + linkType: hard + "mdast-util-from-markdown@npm:^2.0.0": version: 2.0.0 resolution: "mdast-util-from-markdown@npm:2.0.0" @@ -15955,6 +18325,22 @@ __metadata: languageName: node linkType: hard +"mdast-util-to-hast@npm:^10.2.0": + version: 10.2.0 + resolution: "mdast-util-to-hast@npm:10.2.0" + dependencies: + "@types/mdast": ^3.0.0 + "@types/unist": ^2.0.0 + mdast-util-definitions: ^4.0.0 + mdurl: ^1.0.0 + unist-builder: ^2.0.0 + unist-util-generated: ^1.0.0 + unist-util-position: ^3.0.0 + unist-util-visit: ^2.0.0 + checksum: 72df2dd9bfa2d07b4750a333444f82e0f3752dae75b6e300cf0a716407a185eb75095a54ecad90cbd6f6d133b20dea8844ff76c1ea78612550de170b43d4fa85 + languageName: node + linkType: hard + "mdast-util-to-hast@npm:^13.0.0": version: 13.0.2 resolution: "mdast-util-to-hast@npm:13.0.2" @@ -16087,6 +18473,13 @@ __metadata: languageName: node linkType: hard +"messageformat-parser@npm:^4.1.3": + version: 4.1.3 + resolution: "messageformat-parser@npm:4.1.3" + checksum: d5a72581116b1813460241672ffb6ff3e2f05c311d48c7d81daf368f5254447dda095756e53830664383c51c707dbf387c21b35cd341b7edd4f2945656ac476f + languageName: node + linkType: hard + "methods@npm:~1.1.2": version: 1.1.2 resolution: "methods@npm:1.1.2" @@ -16607,6 +19000,16 @@ __metadata: languageName: node linkType: hard +"micromark@npm:~2.11.0": + version: 2.11.4 + resolution: "micromark@npm:2.11.4" + dependencies: + debug: ^4.0.0 + parse-entities: ^2.0.0 + checksum: f8a5477d394908a5d770227aea71657a76423d420227c67ea0699e659a5f62eb39d504c1f7d69ec525a6af5aaeb6a7bffcdba95614968c03d41d3851edecb0d6 + languageName: node + linkType: hard + "micromatch@npm:^4.0.0, micromatch@npm:^4.0.2, micromatch@npm:^4.0.4, micromatch@npm:^4.0.5": version: 4.0.5 resolution: "micromatch@npm:4.0.5" @@ -16906,6 +19309,13 @@ __metadata: languageName: node linkType: hard +"moo@npm:^0.5.1": + version: 0.5.2 + resolution: "moo@npm:0.5.2" + checksum: 5a41ddf1059fd0feb674d917c4774e41c877f1ca980253be4d3aae1a37f4bc513f88815041243f36f5cf67a62fb39324f3f997cf7fb17b6cb00767c165e7c499 + languageName: node + linkType: hard + "mrmime@npm:^1.0.0": version: 1.0.1 resolution: "mrmime@npm:1.0.1" @@ -16962,7 +19372,7 @@ __metadata: languageName: node linkType: hard -"nanoid@npm:^3.1.25, nanoid@npm:^3.3.7": +"nanoid@npm:3.3.7, nanoid@npm:^3.1.25, nanoid@npm:^3.3.7": version: 3.3.7 resolution: "nanoid@npm:3.3.7" bin: @@ -17305,6 +19715,17 @@ __metadata: languageName: node linkType: hard +"oniguruma-to-es@npm:^1.0.0": + version: 1.0.0 + resolution: "oniguruma-to-es@npm:1.0.0" + dependencies: + emoji-regex-xs: ^1.0.0 + regex: ^5.1.1 + regex-recursion: ^5.1.1 + checksum: 2d88b3f0c670b1b7c87bf5c4caefea12771748c5970f691f04284604f3dce745107f7558573395c9103bea56154062b421d0a2a7005ada93968a7071316f5d1e + languageName: node + linkType: hard + "only@npm:~0.0.2": version: 0.0.2 resolution: "only@npm:0.0.2" @@ -17578,6 +19999,13 @@ __metadata: languageName: node linkType: hard +"pako@npm:~1.0.2": + version: 1.0.11 + resolution: "pako@npm:1.0.11" + checksum: 1be2bfa1f807608c7538afa15d6f25baa523c30ec870a3228a89579e474a4d992f4293859524e46d5d87fd30fa17c5edf34dbef0671251d9749820b488660b16 + languageName: node + linkType: hard + "param-case@npm:^3.0.4": version: 3.0.4 resolution: "param-case@npm:3.0.4" @@ -17911,6 +20339,18 @@ __metadata: languageName: node linkType: hard +"popmotion@npm:11.0.3": + version: 11.0.3 + resolution: "popmotion@npm:11.0.3" + dependencies: + framesync: 6.0.1 + hey-listen: ^1.0.8 + style-value-types: 5.0.0 + tslib: ^2.1.0 + checksum: 9fe7d03b4ec0e85bfb9dadc23b745147bfe42e16f466ba06e6327197d0e38b72015afc2f918a8051dedc3680310417f346ffdc463be6518e2e92e98f48e30268 + languageName: node + linkType: hard + "portfinder@npm:^1.0.32": version: 1.0.32 resolution: "portfinder@npm:1.0.32" @@ -18699,7 +21139,7 @@ __metadata: languageName: node linkType: hard -"postcss-value-parser@npm:^4.1.0, postcss-value-parser@npm:^4.2.0": +"postcss-value-parser@npm:^4.0.2, postcss-value-parser@npm:^4.1.0, postcss-value-parser@npm:^4.2.0": version: 4.2.0 resolution: "postcss-value-parser@npm:4.2.0" checksum: 819ffab0c9d51cf0acbabf8996dffbfafbafa57afc0e4c98db88b67f2094cb44488758f06e5da95d7036f19556a4a732525e84289a425f4f6fd8e412a9d7442f @@ -18724,6 +21164,17 @@ __metadata: languageName: node linkType: hard +"postcss@npm:8.4.38": + version: 8.4.38 + resolution: "postcss@npm:8.4.38" + dependencies: + nanoid: ^3.3.7 + picocolors: ^1.0.0 + source-map-js: ^1.2.0 + checksum: 649f9e60a763ca4b5a7bbec446a069edf07f057f6d780a5a0070576b841538d1ecf7dd888f2fbfd1f76200e26c969e405aeeae66332e6927dbdc8bdcb90b9451 + languageName: node + linkType: hard + "postcss@npm:^8.4.14, postcss@npm:^8.4.17, postcss@npm:^8.4.21, postcss@npm:^8.4.26": version: 8.4.32 resolution: "postcss@npm:8.4.32" @@ -18746,6 +21197,25 @@ __metadata: languageName: node linkType: hard +"posthog-js@npm:^1.136.8": + version: 1.205.1 + resolution: "posthog-js@npm:1.205.1" + dependencies: + core-js: ^3.38.1 + fflate: ^0.4.8 + preact: ^10.19.3 + web-vitals: ^4.2.0 + checksum: d91ce45d2d3d5784b6db8d2f4408619db6512bd3d5fae8022f6fc91864b3b2d2c14c0081b670aefa1a8b63d7a498b2a9e3e58e58583e419893b54276832f1635 + languageName: node + linkType: hard + +"preact@npm:^10.19.3": + version: 10.25.4 + resolution: "preact@npm:10.25.4" + checksum: 309f3128267c5bcac828c70a7a97fba0fdfed7b9ef2ece32a50bf94d257b5c825975df0648d9d5c79f90201d3a295cb2c9f511640aca37cb6879e509688b885a + languageName: node + linkType: hard + "prelude-ls@npm:^1.2.1": version: 1.2.1 resolution: "prelude-ls@npm:1.2.1" @@ -18797,6 +21267,17 @@ __metadata: languageName: node linkType: hard +"pretty-format@npm:^29.7.0": + version: 29.7.0 + resolution: "pretty-format@npm:29.7.0" + dependencies: + "@jest/schemas": ^29.6.3 + ansi-styles: ^5.0.0 + react-is: ^18.0.0 + checksum: 032c1602383e71e9c0c02a01bbd25d6759d60e9c7cf21937dde8357aa753da348fcec5def5d1002c9678a8524d5fe099ad98861286550ef44de8808cc61e43b6 + languageName: node + linkType: hard + "pretty-time@npm:^1.1.0": version: 1.1.0 resolution: "pretty-time@npm:1.1.0" @@ -18871,7 +21352,7 @@ __metadata: languageName: node linkType: hard -"prop-types@npm:^15.6.2, prop-types@npm:^15.7.2": +"prop-types@npm:^15.6.2, prop-types@npm:^15.7.2, prop-types@npm:^15.8.1": version: 15.8.1 resolution: "prop-types@npm:15.8.1" dependencies: @@ -19056,6 +21537,13 @@ __metadata: languageName: node linkType: hard +"ramda@npm:^0.27.1": + version: 0.27.2 + resolution: "ramda@npm:0.27.2" + checksum: 28d6735dd1eea1a796c56cf6111f3673c6105bbd736e521cdd7826c46a18eeff337c2dba4668f6eed990d539b9961fd6db19aa46ccc1530ba67a396c0a9f580d + languageName: node + linkType: hard + "randombytes@npm:^2.1.0": version: 2.1.0 resolution: "randombytes@npm:2.1.0" @@ -19117,6 +21605,15 @@ __metadata: languageName: node linkType: hard +"react-complex-tree@npm:^2.3.7": + version: 2.4.6 + resolution: "react-complex-tree@npm:2.4.6" + peerDependencies: + react: ">=16.0.0" + checksum: 1e0b9ff514fb265e5b95ebda752d20dd994c3ec044e4dc1dffc292afc763e2c02834fb4ebbead6f9a2bd607628590b11547325b017060a05ceb9ddaa71a7af8a + languageName: node + linkType: hard + "react-dev-utils@npm:^12.0.1": version: 12.0.1 resolution: "react-dev-utils@npm:12.0.1" @@ -19212,6 +21709,13 @@ __metadata: languageName: node linkType: hard +"react-is@npm:^18.0.0, react-is@npm:^18.3.1": + version: 18.3.1 + resolution: "react-is@npm:18.3.1" + checksum: e20fe84c86ff172fc8d898251b7cc2c43645d108bf96d0b8edf39b98f9a2cae97b40520ee7ed8ee0085ccc94736c4886294456033304151c3f94978cec03df21 + languageName: node + linkType: hard + "react-json-view-lite@npm:^1.2.0": version: 1.2.1 resolution: "react-json-view-lite@npm:1.2.1" @@ -19221,26 +21725,104 @@ __metadata: languageName: node linkType: hard -"react-loadable-ssr-addon-v5-slorber@npm:^1.0.1": - version: 1.0.1 - resolution: "react-loadable-ssr-addon-v5-slorber@npm:1.0.1" +"react-loadable-ssr-addon-v5-slorber@npm:^1.0.1": + version: 1.0.1 + resolution: "react-loadable-ssr-addon-v5-slorber@npm:1.0.1" + dependencies: + "@babel/runtime": ^7.10.3 + peerDependencies: + react-loadable: "*" + webpack: ">=4.41.1 || 5.x" + checksum: 1cf7ceb488d329a5be15f891dae16727fb7ade08ef57826addd21e2c3d485e2440259ef8be94f4d54e9afb4bcbd2fcc22c3c5bad92160c9c06ae6ba7b5562497 + languageName: node + linkType: hard + +"react-loadable@npm:@docusaurus/react-loadable@6.0.0": + version: 6.0.0 + resolution: "@docusaurus/react-loadable@npm:6.0.0" + dependencies: + "@types/react": "*" + peerDependencies: + react: "*" + checksum: 4c32061b2fc10689d5d8ba11ead71b69e4c8a55fcfeafb551a6747b1a7b496c4f2d8dbb5d023f5cafc2a9aea9d14582bdb324d11e6f9b8c3049d45b74439203f + languageName: node + linkType: hard + +"react-remark@npm:^2.1.0": + version: 2.1.0 + resolution: "react-remark@npm:2.1.0" + dependencies: + rehype-react: ^6.0.0 + remark-parse: ^9.0.0 + remark-rehype: ^8.0.0 + unified: ^9.0.0 + peerDependencies: + react: ">=16.8" + checksum: 96d7e221f15e26b9ea25e496a4490b92488331aa68678dcecc9e0621e07b1dfefc9a22a7d99c99bb81791397a3910aa532c53aff8d0a52892f0e79d84cf874e8 + languageName: node + linkType: hard + +"react-remove-scroll-bar@npm:^2.3.3, react-remove-scroll-bar@npm:^2.3.7": + version: 2.3.8 + resolution: "react-remove-scroll-bar@npm:2.3.8" + dependencies: + react-style-singleton: ^2.2.2 + tslib: ^2.0.0 + peerDependencies: + "@types/react": "*" + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: c4663247f689dbe51c370836edf735487f6d8796acb7f15b09e8a1c14e84c7997360e8e3d54de2bc9c0e782fed2b2c4127d15b4053e4d2cf26839e809e57605f + languageName: node + linkType: hard + +"react-remove-scroll@npm:2.5.5": + version: 2.5.5 + resolution: "react-remove-scroll@npm:2.5.5" dependencies: - "@babel/runtime": ^7.10.3 + react-remove-scroll-bar: ^2.3.3 + react-style-singleton: ^2.2.1 + tslib: ^2.1.0 + use-callback-ref: ^1.3.0 + use-sidecar: ^1.1.2 peerDependencies: - react-loadable: "*" - webpack: ">=4.41.1 || 5.x" - checksum: 1cf7ceb488d329a5be15f891dae16727fb7ade08ef57826addd21e2c3d485e2440259ef8be94f4d54e9afb4bcbd2fcc22c3c5bad92160c9c06ae6ba7b5562497 + "@types/react": ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 2c7fe9cbd766f5e54beb4bec2e2efb2de3583037b23fef8fa511ab426ed7f1ae992382db5acd8ab5bfb030a4b93a06a2ebca41377d6eeaf0e6791bb0a59616a4 languageName: node linkType: hard -"react-loadable@npm:@docusaurus/react-loadable@6.0.0": - version: 6.0.0 - resolution: "@docusaurus/react-loadable@npm:6.0.0" +"react-remove-scroll@npm:^2.6.1": + version: 2.6.2 + resolution: "react-remove-scroll@npm:2.6.2" dependencies: + react-remove-scroll-bar: ^2.3.7 + react-style-singleton: ^2.2.1 + tslib: ^2.1.0 + use-callback-ref: ^1.3.3 + use-sidecar: ^1.1.2 + peerDependencies: "@types/react": "*" + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 310e6e6d2f28226a1751dc5084a2dce49167f0b69e3d78d6510f329f423ee313d4f6477f5e1adccb68baef40a7af75541e980a8c398cb82ea0d3573e514e8124 + languageName: node + linkType: hard + +"react-resizable-panels@npm:2.0.19": + version: 2.0.19 + resolution: "react-resizable-panels@npm:2.0.19" peerDependencies: - react: "*" - checksum: 4c32061b2fc10689d5d8ba11ead71b69e4c8a55fcfeafb551a6747b1a7b496c4f2d8dbb5d023f5cafc2a9aea9d14582bdb324d11e6f9b8c3049d45b74439203f + react: ^16.14.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.14.0 || ^17.0.0 || ^18.0.0 + checksum: d09e07bf2514fb8bb60b6150708943bb784ca1827c4c45dfe8354fb33e4edde795292789b56383886e974d381503333112194679d606808fbc9bab7ec232430e languageName: node linkType: hard @@ -19292,6 +21874,20 @@ __metadata: languageName: node linkType: hard +"react-smooth@npm:^4.0.0": + version: 4.0.4 + resolution: "react-smooth@npm:4.0.4" + dependencies: + fast-equals: ^5.0.1 + prop-types: ^15.8.1 + react-transition-group: ^4.4.5 + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + checksum: 909305d40bae79a011ff21a10c4bc7ddadc87ac5ff093b4a5f827f730f093ec4e044c4330688d29b3ad2db83aab8997c3bb1bae550a9c66de74521b8ed52cc53 + languageName: node + linkType: hard + "react-spinners@npm:^0.13.8": version: 0.13.8 resolution: "react-spinners@npm:0.13.8" @@ -19302,6 +21898,37 @@ __metadata: languageName: node linkType: hard +"react-style-singleton@npm:^2.2.1, react-style-singleton@npm:^2.2.2": + version: 2.2.3 + resolution: "react-style-singleton@npm:2.2.3" + dependencies: + get-nonce: ^1.0.0 + tslib: ^2.0.0 + peerDependencies: + "@types/react": "*" + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + checksum: a7b0bf493c9231065ebafa84c4237aed997c746c561196121b7de82fe155a5355b372db5070a3ac9fe980cf7f60dc0f1e8cf6402a2aa5b2957392932ccf76e76 + languageName: node + linkType: hard + +"react-transition-group@npm:^4.4.5": + version: 4.4.5 + resolution: "react-transition-group@npm:4.4.5" + dependencies: + "@babel/runtime": ^7.5.5 + dom-helpers: ^5.0.1 + loose-envify: ^1.4.0 + prop-types: ^15.6.2 + peerDependencies: + react: ">=16.6.0" + react-dom: ">=16.6.0" + checksum: 75602840106aa9c6545149d6d7ae1502fb7b7abadcce70a6954c4b64a438ff1cd16fc77a0a1e5197cdd72da398f39eb929ea06f9005c45b132ed34e056ebdeb1 + languageName: node + linkType: hard + "react@npm:^18.2.0": version: 18.2.0 resolution: "react@npm:18.2.0" @@ -19323,7 +21950,7 @@ __metadata: languageName: node linkType: hard -"readable-stream@npm:^2.0.1, readable-stream@npm:^2.2.2": +"readable-stream@npm:^2.0.1, readable-stream@npm:^2.2.2, readable-stream@npm:~2.3.6": version: 2.3.8 resolution: "readable-stream@npm:2.3.8" dependencies: @@ -19390,6 +22017,34 @@ __metadata: languageName: node linkType: hard +"recharts-scale@npm:^0.4.4": + version: 0.4.5 + resolution: "recharts-scale@npm:0.4.5" + dependencies: + decimal.js-light: ^2.4.1 + checksum: e970377190a610e684a32c7461c7684ac3603c2e0ac0020bbba1eea9d099b38138143a8e80bf769bb49c0b7cecf22a2f5c6854885efed2d56f4540d4aa7052bd + languageName: node + linkType: hard + +"recharts@npm:^2.12.4": + version: 2.15.0 + resolution: "recharts@npm:2.15.0" + dependencies: + clsx: ^2.0.0 + eventemitter3: ^4.0.1 + lodash: ^4.17.21 + react-is: ^18.3.1 + react-smooth: ^4.0.0 + recharts-scale: ^0.4.4 + tiny-invariant: ^1.3.1 + victory-vendor: ^36.6.8 + peerDependencies: + react: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + checksum: b49337c1df995f60c302101e44e638660ca4ea5370467ffbc61c0ee829770ffae0337564c0d1dc8b46786c91d9201f6bcad9e0155173c4c6fca44a5f3a6decc3 + languageName: node + linkType: hard + "rechoir@npm:^0.6.2": version: 0.6.2 resolution: "rechoir@npm:0.6.2" @@ -19456,6 +22111,32 @@ __metadata: languageName: node linkType: hard +"regex-recursion@npm:^5.1.1": + version: 5.1.1 + resolution: "regex-recursion@npm:5.1.1" + dependencies: + regex: ^5.1.1 + regex-utilities: ^2.3.0 + checksum: 4f203ae8f4a2ebf9004f4e4119df5106ba07b39bd3778d7040a83b17f3a82fe22c202661adc3f5586e4eb782fece77e8a01eba8b7033f92147ad7a1e7e1531d7 + languageName: node + linkType: hard + +"regex-utilities@npm:^2.3.0": + version: 2.3.0 + resolution: "regex-utilities@npm:2.3.0" + checksum: 41408777df45cefe1b276281030213235aa1143809c4c10eb5573d2cc27ff2c4aa746c6f4d4c235e3d2f4830eff76b28906ce82fbe72895beca8e15204c2da51 + languageName: node + linkType: hard + +"regex@npm:^5.1.1": + version: 5.1.1 + resolution: "regex@npm:5.1.1" + dependencies: + regex-utilities: ^2.3.0 + checksum: bff664d0c001bf2929c2a5c92399419f719ef5ac9e7198bce653695d37628a3bd21595cea571f93ee13b55c5bbeff7fbab307a9ef569e36b149caf09601d4a31 + languageName: node + linkType: hard + "regexpu-core@npm:^5.3.1": version: 5.3.2 resolution: "regexpu-core@npm:5.3.2" @@ -19562,6 +22243,27 @@ __metadata: languageName: node linkType: hard +"rehype-react@npm:^6.0.0": + version: 6.2.1 + resolution: "rehype-react@npm:6.2.1" + dependencies: + "@mapbox/hast-util-table-cell-style": ^0.2.0 + hast-to-hyperscript: ^9.0.0 + checksum: 7f16a2cfc38d1fe0d40944f8f27309fc16440d69d3bb53f8e0ca6d6825572f7d1cac4212193a4f4de456b2342fea66ea0919648ad09c0663cfb1e4ca12bed647 + languageName: node + linkType: hard + +"rehype-react@npm:^8.0.0": + version: 8.0.0 + resolution: "rehype-react@npm:8.0.0" + dependencies: + "@types/hast": ^3.0.0 + hast-util-to-jsx-runtime: ^2.0.0 + unified: ^11.0.0 + checksum: dc5dfbaff4ba029a466e01729c63431cab4efa8937b811c9a2d25889d6e91839cff08592a11edff622ace2c08bfb0a564210ae5e27e21e22049cbbed77c02822 + languageName: node + linkType: hard + "relateurl@npm:^0.2.7": version: 0.2.7 resolution: "relateurl@npm:0.2.7" @@ -19712,6 +22414,15 @@ __metadata: languageName: node linkType: hard +"remark-parse@npm:^9.0.0": + version: 9.0.0 + resolution: "remark-parse@npm:9.0.0" + dependencies: + mdast-util-from-markdown: ^0.8.0 + checksum: 50104880549639b7dd7ae6f1e23c214915fe9c054f02f3328abdaee3f6de6d7282bf4357c3c5b106958fe75e644a3c248c2197755df34f9955e8e028fc74868f + languageName: node + linkType: hard + "remark-rehype@npm:^11.0.0": version: 11.0.0 resolution: "remark-rehype@npm:11.0.0" @@ -19725,6 +22436,28 @@ __metadata: languageName: node linkType: hard +"remark-rehype@npm:^11.1.0": + version: 11.1.1 + resolution: "remark-rehype@npm:11.1.1" + dependencies: + "@types/hast": ^3.0.0 + "@types/mdast": ^4.0.0 + mdast-util-to-hast: ^13.0.0 + unified: ^11.0.0 + vfile: ^6.0.0 + checksum: e199dff098ae6a560e13dd1778dec9c61440f979cc931c4ca4dcde0d58e51c0723243a901c1842379b189083cf4d74f224f57833738095ffa9535179d7cf3f01 + languageName: node + linkType: hard + +"remark-rehype@npm:^8.0.0": + version: 8.1.0 + resolution: "remark-rehype@npm:8.1.0" + dependencies: + mdast-util-to-hast: ^10.2.0 + checksum: e1152464cfa83c14b570b1cb85eb9b3667795b5bed2f6b16d1c6e96c369816b07945a3c04eb0e1fd57a19cc1837969527d0056d5b6d179f1290688db2a7e2c5f + languageName: node + linkType: hard + "remark-squeeze-paragraphs@npm:4.0.0": version: 4.0.0 resolution: "remark-squeeze-paragraphs@npm:4.0.0" @@ -20444,7 +23177,7 @@ __metadata: languageName: node linkType: hard -"shallowequal@npm:^1.1.0": +"shallowequal@npm:1.1.0, shallowequal@npm:^1.1.0": version: 1.1.0 resolution: "shallowequal@npm:1.1.0" checksum: f4c1de0837f106d2dbbfd5d0720a5d059d1c66b42b580965c8f06bb1db684be8783538b684092648c981294bf817869f743a066538771dbecb293df78f765e00 @@ -20487,6 +23220,22 @@ __metadata: languageName: node linkType: hard +"shiki@npm:1.26.2": + version: 1.26.2 + resolution: "shiki@npm:1.26.2" + dependencies: + "@shikijs/core": 1.26.2 + "@shikijs/engine-javascript": 1.26.2 + "@shikijs/engine-oniguruma": 1.26.2 + "@shikijs/langs": 1.26.2 + "@shikijs/themes": 1.26.2 + "@shikijs/types": 1.26.2 + "@shikijs/vscode-textmate": ^10.0.1 + "@types/hast": ^3.0.4 + checksum: 0cc7af769eb57de4bc1423ab60a62e8c68071914456b0bd94c6188051d770f65b8e76d94341f552274bb76221c734c0ccc802106fa2c969b701df5dffaf26a14 + languageName: node + linkType: hard + "shiki@npm:^0.14.1": version: 0.14.6 resolution: "shiki@npm:0.14.6" @@ -20684,6 +23433,23 @@ __metadata: languageName: node linkType: hard +"solc@npm:^0.8.25": + version: 0.8.28 + resolution: "solc@npm:0.8.28" + dependencies: + command-exists: ^1.2.8 + commander: ^8.1.0 + follow-redirects: ^1.12.1 + js-sha3: 0.8.0 + memorystream: ^0.3.1 + semver: ^5.5.0 + tmp: 0.0.33 + bin: + solcjs: solc.js + checksum: c22ec95c23505409e83fd7e2c1af47a2b2092d1886c1cf2256b9bc28958963d5bfc9944dc34d0d2e0cbc00975878068b8d87ab7407cec0f9ca5ac57951ee789a + languageName: node + linkType: hard + "sort-css-media-queries@npm:2.1.0": version: 2.1.0 resolution: "sort-css-media-queries@npm:2.1.0" @@ -20698,7 +23464,7 @@ __metadata: languageName: node linkType: hard -"source-map-js@npm:^1.0.1, source-map-js@npm:^1.2.1": +"source-map-js@npm:^1.0.1, source-map-js@npm:^1.2.0, source-map-js@npm:^1.2.1": version: 1.2.1 resolution: "source-map-js@npm:1.2.1" checksum: 4eb0cd997cdf228bc253bcaff9340afeb706176e64868ecd20efbe6efea931465f43955612346d6b7318789e5265bdc419bc7669c1cebe3db0eb255f57efa76b @@ -20830,6 +23596,13 @@ __metadata: languageName: node linkType: hard +"state-local@npm:^1.0.6": + version: 1.0.7 + resolution: "state-local@npm:1.0.7" + checksum: d1afcf1429e7e6eb08685b3a94be8797db847369316d4776fd51f3962b15b984dacc7f8e401ad20968e5798c9565b4b377afedf4e4c4d60fe7495e1cbe14a251 + languageName: node + linkType: hard + "state-toggle@npm:^1.0.0": version: 1.0.3 resolution: "state-toggle@npm:1.0.3" @@ -21062,6 +23835,36 @@ __metadata: languageName: node linkType: hard +"style-value-types@npm:5.0.0": + version: 5.0.0 + resolution: "style-value-types@npm:5.0.0" + dependencies: + hey-listen: ^1.0.8 + tslib: ^2.1.0 + checksum: 16d198302cd102edf9dba94e7752a2364c93b1eaa5cc7c32b42b28eef4af4ccb5149a3f16bc2a256adc02616a2404f4612bd15f3081c1e8ca06132cae78be6c0 + languageName: node + linkType: hard + +"styled-components@npm:^6.1.8": + version: 6.1.14 + resolution: "styled-components@npm:6.1.14" + dependencies: + "@emotion/is-prop-valid": 1.2.2 + "@emotion/unitless": 0.8.1 + "@types/stylis": 4.2.5 + css-to-react-native: 3.2.0 + csstype: 3.1.3 + postcss: 8.4.38 + shallowequal: 1.1.0 + stylis: 4.3.2 + tslib: 2.6.2 + peerDependencies: + react: ">= 16.8.0" + react-dom: ">= 16.8.0" + checksum: 88c0ce99f9c5bf593eedd0e287cc4f66dc6e64e49f36ad89a632ea36ee514e9b155d8927d06ec53af68ccce4c525208bd4f65d2019d373af4c60be6ebb53df38 + languageName: node + linkType: hard + "stylehacks@npm:^5.1.1": version: 5.1.1 resolution: "stylehacks@npm:5.1.1" @@ -21086,6 +23889,13 @@ __metadata: languageName: node linkType: hard +"stylis@npm:4.3.2": + version: 4.3.2 + resolution: "stylis@npm:4.3.2" + checksum: 0faa8a97ff38369f47354376cd9f0def9bf12846da54c28c5987f64aaf67dcb6f00dce88a8632013bfb823b2c4d1d62a44f4ac20363a3505a7ab4e21b70179fc + languageName: node + linkType: hard + "superstruct@npm:^1.0.3": version: 1.0.3 resolution: "superstruct@npm:1.0.3" @@ -21168,6 +23978,18 @@ __metadata: languageName: node linkType: hard +"swr@npm:^2.2.5": + version: 2.3.0 + resolution: "swr@npm:2.3.0" + dependencies: + dequal: ^2.0.3 + use-sync-external-store: ^1.4.0 + peerDependencies: + react: ^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + checksum: ea5503b9a34763e8af0681ee1570c5f8d301da6f46343512614799a07cadadf454cb2f832f8d49d2607da9147c9a8b8130994c4f4546c9da5d680bd9e90a8bcc + languageName: node + linkType: hard + "synckit@npm:^0.8.6": version: 0.8.8 resolution: "synckit@npm:0.8.8" @@ -21207,6 +24029,22 @@ __metadata: languageName: node linkType: hard +"tailwind-merge@npm:^2.2.1": + version: 2.6.0 + resolution: "tailwind-merge@npm:2.6.0" + checksum: 18976c4096920bc6125f1dc837479805de996d86bcc636f98436f65c297003bde89ffe51dfd325b7c97fc71b1dbba8505459dd96010e7b181badd29aea996440 + languageName: node + linkType: hard + +"tailwindcss-animate@npm:^1.0.7": + version: 1.0.7 + resolution: "tailwindcss-animate@npm:1.0.7" + peerDependencies: + tailwindcss: "*" + checksum: c1760983eb3fec0c8421e95082bf308e6845df43e2f90862386366e82545c801b26b4d189c4cd23d6915252b76d18005c8e5f591f8b119944c7fb8650d0f8bce + languageName: node + linkType: hard + "tapable@npm:^1.0.0": version: 1.1.3 resolution: "tapable@npm:1.1.3" @@ -21372,6 +24210,13 @@ __metadata: languageName: node linkType: hard +"tiny-invariant@npm:^1.3.1": + version: 1.3.3 + resolution: "tiny-invariant@npm:1.3.3" + checksum: 5e185c8cc2266967984ce3b352a4e57cb89dad5a8abb0dea21468a6ecaa67cd5bb47a3b7a85d08041008644af4f667fb8b6575ba38ba5fb00b3b5068306e59fe + languageName: node + linkType: hard + "tiny-warning@npm:^1.0.0": version: 1.0.3 resolution: "tiny-warning@npm:1.0.3" @@ -21589,6 +24434,13 @@ __metadata: languageName: node linkType: hard +"tslib@npm:2.6.2, tslib@npm:^2.0.1, tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.4.0, tslib@npm:^2.5.0, tslib@npm:^2.6.0, tslib@npm:^2.6.2": + version: 2.6.2 + resolution: "tslib@npm:2.6.2" + checksum: 329ea56123005922f39642318e3d1f0f8265d1e7fcb92c633e0809521da75eeaca28d2cf96d7248229deb40e5c19adf408259f4b9640afd20d13aecc1430f3ad + languageName: node + linkType: hard + "tslib@npm:^1.9.3": version: 1.14.1 resolution: "tslib@npm:1.14.1" @@ -21596,10 +24448,10 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^2.0.1, tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.4.0, tslib@npm:^2.5.0, tslib@npm:^2.6.0, tslib@npm:^2.6.2": - version: 2.6.2 - resolution: "tslib@npm:2.6.2" - checksum: 329ea56123005922f39642318e3d1f0f8265d1e7fcb92c633e0809521da75eeaca28d2cf96d7248229deb40e5c19adf408259f4b9640afd20d13aecc1430f3ad +"tslib@npm:^2.0.0, tslib@npm:^2.3.1": + version: 2.8.1 + resolution: "tslib@npm:2.8.1" + checksum: e4aba30e632b8c8902b47587fd13345e2827fa639e7c3121074d5ee0880723282411a8838f830b55100cbe4517672f84a2472667d355b81e8af165a55dc6203a languageName: node linkType: hard @@ -21939,7 +24791,22 @@ __metadata: languageName: node linkType: hard -"unified@npm:^9.2.2": +"unified@npm:^11.0.5": + version: 11.0.5 + resolution: "unified@npm:11.0.5" + dependencies: + "@types/unist": ^3.0.0 + bail: ^2.0.0 + devlop: ^1.0.0 + extend: ^3.0.0 + is-plain-obj: ^4.0.0 + trough: ^2.0.0 + vfile: ^6.0.0 + checksum: b3bf7fd6f568cc261e074dae21188483b0f2a8ab858d62e6e85b75b96cc655f59532906ae3c64d56a9b257408722d71f1d4135292b3d7ee02907c8b592fb3cf0 + languageName: node + linkType: hard + +"unified@npm:^9.0.0, unified@npm:^9.2.2": version: 9.2.2 resolution: "unified@npm:9.2.2" dependencies: @@ -22013,6 +24880,13 @@ __metadata: languageName: node linkType: hard +"unist-util-is@npm:^3.0.0": + version: 3.0.0 + resolution: "unist-util-is@npm:3.0.0" + checksum: d24a5dd80c670f763b2ae608651cf062317456aa81be51f66f45cbd7d440a2ab18356e4f48aeac6b5e3d391c69d3c3452ade5fe5aa9574bec4a2de0b10122ed5 + languageName: node + linkType: hard + "unist-util-is@npm:^4.0.0": version: 4.1.0 resolution: "unist-util-is@npm:4.1.0" @@ -22100,6 +24974,15 @@ __metadata: languageName: node linkType: hard +"unist-util-visit-parents@npm:^2.0.0": + version: 2.1.2 + resolution: "unist-util-visit-parents@npm:2.1.2" + dependencies: + unist-util-is: ^3.0.0 + checksum: 048edbb590a8c4bc0043eec9f50d3fe76faa58f1ac663a7e6dee5e895ddd0ce8bc52f2cfe2e633849fa93671e8de021070667acb1518e3d40220768c7f70a3d3 + languageName: node + linkType: hard + "unist-util-visit-parents@npm:^3.0.0": version: 3.1.1 resolution: "unist-util-visit-parents@npm:3.1.1" @@ -22131,6 +25014,15 @@ __metadata: languageName: node linkType: hard +"unist-util-visit@npm:^1.4.1": + version: 1.4.1 + resolution: "unist-util-visit@npm:1.4.1" + dependencies: + unist-util-visit-parents: ^2.0.0 + checksum: e9395205b6908c8d0fe71bc44e65d89d4781d1bb2d453a33cb67ed4124bad0b89d6b1d526ebaecb82a7c48e211bdf6f24351449b8cc115327b345f4617c18728 + languageName: node + linkType: hard + "unist-util-visit@npm:^5.0.0": version: 5.0.0 resolution: "unist-util-visit@npm:5.0.0" @@ -22163,6 +25055,13 @@ __metadata: languageName: node linkType: hard +"unraw@npm:^3.0.0": + version: 3.0.0 + resolution: "unraw@npm:3.0.0" + checksum: 19eee0bc500ce197d262b79723a2c8c81c1d716baaa2a62c48a4d0d6b9e1fd9d350c5df86262e51343d591ab9c8a47ed150317d0b867b2b65795cdc17ef69873 + languageName: node + linkType: hard + "unzipit@npm:^1.4.3": version: 1.4.3 resolution: "unzipit@npm:1.4.3" @@ -22306,6 +25205,46 @@ __metadata: languageName: node linkType: hard +"use-callback-ref@npm:^1.3.0, use-callback-ref@npm:^1.3.3": + version: 1.3.3 + resolution: "use-callback-ref@npm:1.3.3" + dependencies: + tslib: ^2.0.0 + peerDependencies: + "@types/react": "*" + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 4da1c82d7a2409cee6c882748a40f4a083decf238308bf12c3d0166f0e338f8d512f37b8d11987eb5a421f14b9b5b991edf3e11ed25c3bb7a6559081f8359b44 + languageName: node + linkType: hard + +"use-sidecar@npm:^1.1.2": + version: 1.1.3 + resolution: "use-sidecar@npm:1.1.3" + dependencies: + detect-node-es: ^1.1.0 + tslib: ^2.0.0 + peerDependencies: + "@types/react": "*" + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 88664c6b2c5b6e53e4d5d987694c9053cea806da43130248c74ca058945c8caa6ccb7b1787205a9eb5b9d124633e42153848904002828acabccdc48cda026622 + languageName: node + linkType: hard + +"use-sync-external-store@npm:^1.2.0, use-sync-external-store@npm:^1.4.0": + version: 1.4.0 + resolution: "use-sync-external-store@npm:1.4.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + checksum: dc3843a1b59ac8bd01417bd79498d4c688d5df8bf4801be50008ef4bfaacb349058c0b1605b5b43c828e0a2d62722d7e861573b3f31cea77a7f23e8b0fc2f7e3 + languageName: node + linkType: hard + "util-deprecate@npm:^1.0.1, util-deprecate@npm:^1.0.2, util-deprecate@npm:~1.0.1": version: 1.0.2 resolution: "util-deprecate@npm:1.0.2" @@ -22455,6 +25394,49 @@ __metadata: languageName: node linkType: hard +"victory-vendor@npm:^36.6.8": + version: 36.9.2 + resolution: "victory-vendor@npm:36.9.2" + dependencies: + "@types/d3-array": ^3.0.3 + "@types/d3-ease": ^3.0.0 + "@types/d3-interpolate": ^3.0.1 + "@types/d3-scale": ^4.0.2 + "@types/d3-shape": ^3.1.0 + "@types/d3-time": ^3.0.0 + "@types/d3-timer": ^3.0.0 + d3-array: ^3.1.6 + d3-ease: ^3.0.1 + d3-interpolate: ^3.0.1 + d3-scale: ^4.0.2 + d3-shape: ^3.1.0 + d3-time: ^3.0.0 + d3-timer: ^3.0.1 + checksum: a755110e287b700202d08ac81982093ab100edaa9d61beef1476d59e9705605bd8299a3aa41fa04b933a12bd66737f4c8f7d18448dd6488c69d4f72480023a2e + languageName: node + linkType: hard + +"viem@npm:2.9.16": + version: 2.9.16 + resolution: "viem@npm:2.9.16" + dependencies: + "@adraffy/ens-normalize": 1.10.0 + "@noble/curves": 1.2.0 + "@noble/hashes": 1.3.2 + "@scure/bip32": 1.3.2 + "@scure/bip39": 1.2.1 + abitype: 1.0.0 + isows: 1.0.3 + ws: 8.13.0 + peerDependencies: + typescript: ">=5.0.4" + peerDependenciesMeta: + typescript: + optional: true + checksum: aef2e4b4c906fbeed31b4fdf273cfccfc297c4f62c639334f3ba7c642c2374ac1f91531f19f6bbb7051af14dc5420d54f7b26990bbda079b485c9ab66ca09826 + languageName: node + linkType: hard + "vscode-languageserver-textdocument@npm:^1.0.11": version: 1.0.11 resolution: "vscode-languageserver-textdocument@npm:1.0.11" @@ -22531,6 +25513,13 @@ __metadata: languageName: node linkType: hard +"web-vitals@npm:^4.2.0": + version: 4.2.4 + resolution: "web-vitals@npm:4.2.4" + checksum: 5b3ffe1db33f23aebf8cc8560ac574401a95939baafde5841835c1bb1c01f9a2478442f319f77aa0d7914739fc2f6b020c5d5b128c16c5c77ca6be2f9dfbbde6 + languageName: node + linkType: hard + "webidl-conversions@npm:^3.0.0": version: 3.0.1 resolution: "webidl-conversions@npm:3.0.1" @@ -23018,6 +26007,21 @@ __metadata: languageName: node linkType: hard +"ws@npm:8.13.0": + version: 8.13.0 + resolution: "ws@npm:8.13.0" + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ">=5.0.2" + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + checksum: 53e991bbf928faf5dc6efac9b8eb9ab6497c69feeb94f963d648b7a3530a720b19ec2e0ec037344257e05a4f35bd9ad04d9de6f289615ffb133282031b18c61c + languageName: node + linkType: hard + "ws@npm:8.16.0, ws@npm:^8.16.0": version: 8.16.0 resolution: "ws@npm:8.16.0" @@ -23286,7 +26290,7 @@ __metadata: languageName: node linkType: hard -"zwitch@npm:^2.0.0": +"zwitch@npm:^2.0.0, zwitch@npm:^2.0.4": version: 2.0.4 resolution: "zwitch@npm:2.0.4" checksum: f22ec5fc2d5f02c423c93d35cdfa83573a3a3bd98c66b927c368ea4d0e7252a500df2a90a6b45522be536a96a73404393c958e945fdba95e6832c200791702b6