diff --git a/.circleci/config.yml b/.circleci/config.yml index cf0b8686481e..b036b70684a8 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -125,6 +125,17 @@ jobs: name: "Build" command: cond_spot_run_build noir-compile-acir-tests 32 + avm-transpiler: + docker: + - image: aztecprotocol/alpine-build-image + resource_class: small + steps: + - *checkout + - *setup_env + - run: + name: "Build" + command: cond_spot_run_build avm-transpiler 32 + # Barretenberg barretenberg-wasm-linux-clang: docker: @@ -610,7 +621,7 @@ jobs: name: "Test" command: cond_spot_run_compose end-to-end 4 ./scripts/docker-compose.yml TEST=e2e_sandbox_example.test.ts - e2e-singleton: + e2e-state-vars: docker: - image: aztecprotocol/alpine-build-image resource_class: small @@ -619,7 +630,7 @@ jobs: - *setup_env - run: name: "Test" - command: cond_spot_run_compose end-to-end 4 ./scripts/docker-compose.yml TEST=e2e_singleton.test.ts + command: cond_spot_run_compose end-to-end 4 ./scripts/docker-compose.yml TEST=e2e_state_vars.test.ts e2e-block-building: docker: @@ -1137,6 +1148,9 @@ workflows: - noir-ecr-manifest <<: *defaults + # Transpiler + - avm-transpiler: *defaults + # Barretenberg - barretenberg-x86_64-linux-gcc: *defaults - barretenberg-x86_64-linux-clang: *defaults @@ -1185,6 +1199,7 @@ workflows: # Yarn Project - yarn-project-base: requires: + - avm-transpiler - l1-contracts - bb-js - noir-ecr-manifest @@ -1240,7 +1255,7 @@ workflows: # TODO(3458): Investigate intermittent failure # - e2e-slow-tree: *e2e_test - e2e-sandbox-example: *e2e_test - - e2e-singleton: *e2e_test + - e2e-state-vars: *e2e_test - e2e-block-building: *e2e_test - e2e-nested-contract: *e2e_test - e2e-non-contract-account: *e2e_test @@ -1278,7 +1293,7 @@ workflows: - e2e-token-contract - e2e-blacklist-token-contract - e2e-sandbox-example - - e2e-singleton + - e2e-state-vars - e2e-block-building - e2e-nested-contract - e2e-non-contract-account diff --git a/.github/workflows/publish-bb.yml b/.github/workflows/publish-bb.yml index 27c345b34e67..6793f999745c 100644 --- a/.github/workflows/publish-bb.yml +++ b/.github/workflows/publish-bb.yml @@ -139,12 +139,9 @@ jobs: ./barretenberg/cpp/build-wasm/bin/barretenberg.wasm.tar.gz ./barretenberg/cpp/build-wasm/bin/acvm_backend.wasm.tar.gz - build-mac: - name: Build on Mac (${{ matrix.target }}) + build-mac-intel: + name: Build on Mac x86_64-apple-darwin runs-on: macos-13 - strategy: - matrix: - target: [x86_64-apple-darwin, aarch64-apple-darwin] steps: - name: Checkout uses: actions/checkout@v3 @@ -154,18 +151,42 @@ jobs: - name: Create Mac Build Environment run: brew install cmake ninja - - name: Compile Barretenberg (x86_64) - if: matrix.target == 'x86_64-apple-darwin' + - name: Compile Barretenberg working-directory: barretenberg/cpp run: | cmake --preset default -DCMAKE_BUILD_TYPE=RelWithAssert cmake --build --preset default --target bb - - name: Compile Barretenberg (ARM) - if: matrix.target == 'aarch64-apple-darwin' + - name: Package barretenberg artifact + working-directory: barretenberg/cpp/build/bin + run: | + mkdir dist + cp ./bb ./dist/bb + 7z a -ttar -so -an ./dist/* | 7z a -si ./barretenberg-x86_64-apple-darwin.tar.gz + + - name: Upload artifact + uses: actions/upload-artifact@v3 + with: + name: barretenberg-x86_64-apple-darwin + path: ./barretenberg/cpp/build/bin/barretenberg-x86_64-apple-darwin.tar.gz + retention-days: 3 + + build-mac-m1: + name: Build on Mac aarch64-apple-darwin + runs-on: macos-14 + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + ref: ${{ inputs.tag || env.GITHUB_REF }} + + - name: Create Mac Build Environment + run: brew install cmake ninja + + - name: Compile Barretenberg working-directory: barretenberg/cpp run: | - cmake --toolchain ./cmake/toolchains/aarch64-darwin.cmake --preset default -DCMAKE_BUILD_TYPE=RelWithAssert + cmake --preset default -DCMAKE_BUILD_TYPE=RelWithAssert cmake --build --preset default --target bb - name: Package barretenberg artifact @@ -173,18 +194,18 @@ jobs: run: | mkdir dist cp ./bb ./dist/bb - 7z a -ttar -so -an ./dist/* | 7z a -si ./barretenberg-${{ matrix.target }}.tar.gz + 7z a -ttar -so -an ./dist/* | 7z a -si ./barretenberg-aarch64-apple-darwin.tar.gz - name: Upload artifact uses: actions/upload-artifact@v3 with: - name: barretenberg-${{ matrix.target }} - path: ./barretenberg/cpp/build/bin/barretenberg-${{ matrix.target }}.tar.gz + name: barretenberg-aarch64-apple-darwin + path: ./barretenberg/cpp/build/bin/barretenberg-aarch64-apple-darwin.tar.gz retention-days: 3 release: name: Publish - needs: [build-x86_64-linux-gnu, build-mac, build-wasm-ts] + needs: [build-x86_64-linux-gnu, build-mac-intel, build-mac-m1,build-wasm-ts] runs-on: ubuntu-latest steps: - name: Download files from Linux Runner diff --git a/avm-transpiler/.gitignore b/avm-transpiler/.gitignore new file mode 100644 index 000000000000..212de442f4e5 --- /dev/null +++ b/avm-transpiler/.gitignore @@ -0,0 +1,2 @@ +/target +.DS_Store \ No newline at end of file diff --git a/avm-transpiler/Cargo.lock b/avm-transpiler/Cargo.lock new file mode 100644 index 000000000000..718f61220a59 --- /dev/null +++ b/avm-transpiler/Cargo.lock @@ -0,0 +1,2019 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "acir" +version = "0.39.0" +dependencies = [ + "acir_field", + "base64 0.21.7", + "bincode", + "brillig", + "flate2", + "serde", + "thiserror", +] + +[[package]] +name = "acir_field" +version = "0.39.0" +dependencies = [ + "ark-bn254", + "ark-ff", + "cfg-if", + "hex", + "num-bigint", + "num-traits", + "serde", +] + +[[package]] +name = "acvm" +version = "0.39.0" +dependencies = [ + "acir", + "acvm_blackbox_solver", + "brillig_vm", + "indexmap 1.9.3", + "num-bigint", + "thiserror", + "tracing", +] + +[[package]] +name = "acvm_blackbox_solver" +version = "0.39.0" +dependencies = [ + "acir", + "blake2", + "blake3", + "k256", + "keccak", + "p256", + "sha2", + "sha3", + "thiserror", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "ahash" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anstream" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2faccea4cc4ab4a667ce676a30e8ec13922a692c99bb8f5b11f1502c72e04220" + +[[package]] +name = "anstyle-parse" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "arena" +version = "0.23.0" +dependencies = [ + "generational-arena", +] + +[[package]] +name = "ark-bn254" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a22f4561524cd949590d78d7d4c5df8f592430d221f7f3c9497bbafd8972120f" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-std", +] + +[[package]] +name = "ark-ec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" +dependencies = [ + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", + "itertools", + "num-traits", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm", + "ark-ff-macros", + "ark-serialize", + "ark-std", + "derivative", + "digest", + "itertools", + "num-bigint", + "num-traits", + "paste", + "rustc_version", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-poly" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" +dependencies = [ + "ark-ff", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-serialize-derive", + "ark-std", + "digest", + "num-bigint", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "avm-transpiler" +version = "0.1.0" +dependencies = [ + "acvm", + "base64 0.21.7", + "env_logger", + "log", + "noirc_driver", + "regex", + "serde", + "serde_json", +] + +[[package]] +name = "aztec_macros" +version = "0.23.0" +dependencies = [ + "iter-extended", + "noirc_frontend", +] + +[[package]] +name = "base16ct" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitmaps" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2" +dependencies = [ + "typenum", +] + +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest", +] + +[[package]] +name = "blake3" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0231f06152bf547e9c2b5194f247cd97aacf6dcd8b15d8e5ec0663f64580da87" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "brillig" +version = "0.39.0" +dependencies = [ + "acir_field", + "serde", +] + +[[package]] +name = "brillig_vm" +version = "0.39.0" +dependencies = [ + "acir", + "acvm_blackbox_solver", + "num-bigint", + "num-traits", +] + +[[package]] +name = "build-data" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aed3884e2cab7c973c8fd2d150314b6a932df7fdc830edcaf1e8e7c4ae9db3c0" +dependencies = [ + "chrono", + "safe-lock", + "safe-regex", +] + +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f13690e35a5e4ace198e7beea2895d29f3a9cc55015fcebe6336bd2010af9eb" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "serde", + "windows-targets", +] + +[[package]] +name = "chumsky" +version = "0.8.0" +source = "git+https://github.com/jfecher/chumsky?rev=ad9d312#ad9d312d9ffbc66c14514fa2b5752f4127b44f1e" +dependencies = [ + "hashbrown 0.11.2", +] + +[[package]] +name = "clap" +version = "4.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "clap_lex" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" + +[[package]] +name = "codespan" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3362992a0d9f1dd7c3d0e89e0ab2bb540b7a95fea8cd798090e758fda2899b5e" +dependencies = [ + "codespan-reporting", + "serde", +] + +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "serde", + "termcolor", + "unicode-width", +] + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "constant_time_eq" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crypto-bigint" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +dependencies = [ + "generic-array", + "rand_core", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "darling" +version = "0.20.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc5d6b04b3fd0ba9926f945895de7d806260a2d7431ba82e7edaecb043c4c6b8" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04e48a959bcd5c761246f5d090ebc2fbf7b9cd527a492b07a67510c108f1e7e3" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.48", +] + +[[package]] +name = "darling_macro" +version = "0.20.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1545d67a2149e1d93b7e5c7752dce5a7426eb5d1357ddcfd89336b94444f77" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "der" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", + "serde", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "ecdsa" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" +dependencies = [ + "der", + "elliptic-curve", + "rfc6979", + "signature", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "elliptic-curve" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" +dependencies = [ + "base16ct", + "crypto-bigint", + "der", + "digest", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "env_filter" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05e7cf40684ae96ade6232ed84582f40ce0a66efcd43a5117aef610534f8e0b8" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "humantime", + "log", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "ff" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +dependencies = [ + "rand_core", + "subtle", +] + +[[package]] +name = "flate2" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fm" +version = "0.23.0" +dependencies = [ + "codespan-reporting", + "serde", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "generational-arena" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877e94aff08e743b651baaea359664321055749b398adff8740a7399af7796e7" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "group" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +dependencies = [ + "ahash 0.7.7", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash 0.8.7", +] + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "iana-time-zone" +version = "0.1.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "im" +version = "15.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0acd33ff0285af998aaf9b57342af478078f53492322fafc47450e09397e0e9" +dependencies = [ + "bitmaps", + "rand_core", + "rand_xoshiro", + "serde", + "sized-chunks", + "typenum", + "version_check", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "433de089bd45971eecf4668ee0ee8f4cec17db4f8bd8f7bc3197a6ce37aa7d9b" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", + "serde", +] + +[[package]] +name = "iter-extended" +version = "0.23.0" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "js-sys" +version = "0.3.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "jsonrpc" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34efde8d2422fb79ed56db1d3aea8fa5b583351d15a26770cdee2f88813dd702" +dependencies = [ + "base64 0.13.1", + "minreq", + "serde", + "serde_json", +] + +[[package]] +name = "k256" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "sha2", +] + +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "minreq" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb3371dfc7b772c540da1380123674a8e20583aca99907087d990ca58cf44203" +dependencies = [ + "log", + "serde", + "serde_json", +] + +[[package]] +name = "noirc_abi" +version = "0.23.0" +dependencies = [ + "acvm", + "iter-extended", + "noirc_frontend", + "num-bigint", + "num-traits", + "serde", + "serde_json", + "thiserror", + "toml", +] + +[[package]] +name = "noirc_driver" +version = "0.23.0" +dependencies = [ + "acvm", + "aztec_macros", + "build-data", + "clap", + "fm", + "fxhash", + "iter-extended", + "noirc_abi", + "noirc_errors", + "noirc_evaluator", + "noirc_frontend", + "rust-embed", + "serde", + "tracing", +] + +[[package]] +name = "noirc_errors" +version = "0.23.0" +dependencies = [ + "acvm", + "base64 0.21.7", + "chumsky", + "codespan", + "codespan-reporting", + "flate2", + "fm", + "serde", + "serde_json", + "serde_with", + "tracing", +] + +[[package]] +name = "noirc_evaluator" +version = "0.23.0" +dependencies = [ + "acvm", + "fxhash", + "im", + "iter-extended", + "noirc_errors", + "noirc_frontend", + "num-bigint", + "serde", + "thiserror", + "tracing", +] + +[[package]] +name = "noirc_frontend" +version = "0.23.0" +dependencies = [ + "acvm", + "arena", + "chumsky", + "fm", + "iter-extended", + "noirc_errors", + "noirc_printable_type", + "regex", + "rustc-hash", + "serde", + "serde_json", + "small-ord-set", + "smol_str", + "thiserror", + "tracing", +] + +[[package]] +name = "noirc_printable_type" +version = "0.23.0" +dependencies = [ + "acvm", + "iter-extended", + "jsonrpc", + "regex", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "num-bigint" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "p256" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594" +dependencies = [ + "ecdsa", + "elliptic-curve", + "sha2", +] + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pkcs8" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_xoshiro" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" +dependencies = [ + "rand_core", +] + +[[package]] +name = "regex" +version = "1.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "rfc6979" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" +dependencies = [ + "crypto-bigint", + "hmac", + "zeroize", +] + +[[package]] +name = "rust-embed" +version = "6.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a36224c3276f8c4ebc8c20f158eca7ca4359c8db89991c4925132aaaf6702661" +dependencies = [ + "rust-embed-impl", + "rust-embed-utils", + "walkdir", +] + +[[package]] +name = "rust-embed-impl" +version = "6.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49b94b81e5b2c284684141a2fb9e2a31be90638caf040bf9afbc5a0416afe1ac" +dependencies = [ + "proc-macro2", + "quote", + "rust-embed-utils", + "syn 2.0.48", + "walkdir", +] + +[[package]] +name = "rust-embed-utils" +version = "7.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d38ff6bf570dc3bb7100fce9f7b60c33fa71d80e88da3f2580df4ff2bdded74" +dependencies = [ + "sha2", + "walkdir", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "ryu" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" + +[[package]] +name = "safe-lock" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "077d73db7973cccf63eb4aff1e5a34dc2459baa867512088269ea5f2f4253c90" + +[[package]] +name = "safe-proc-macro2" +version = "1.0.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fd85be67db87168aa3c13fd0da99f48f2ab005dccad5af5626138dc1df20eb6" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "safe-quote" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77e530f7831f3feafcd5f1aae406ac205dd998436b4007c8e80f03eca78a88f7" +dependencies = [ + "safe-proc-macro2", +] + +[[package]] +name = "safe-regex" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15289bf322e0673d52756a18194167f2378ec1a15fe884af6e2d2cb934822b0" +dependencies = [ + "safe-regex-macro", +] + +[[package]] +name = "safe-regex-compiler" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fba76fae590a2aa665279deb1f57b5098cbace01a0c5e60e262fcf55f7c51542" +dependencies = [ + "safe-proc-macro2", + "safe-quote", +] + +[[package]] +name = "safe-regex-macro" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c2e96b5c03f158d1b16ba79af515137795f4ad4e8de3f790518aae91f1d127" +dependencies = [ + "safe-proc-macro2", + "safe-regex-compiler", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "sec1" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "semver" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" + +[[package]] +name = "serde" +version = "1.0.196" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.196" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "serde_json" +version = "1.0.113" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_with" +version = "3.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b0ed1662c5a68664f45b76d18deb0e234aff37207086803165c961eb695e981" +dependencies = [ + "base64 0.21.7", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.2.1", + "serde", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "568577ff0ef47b879f736cd66740e022f3672788cdf002a05a4e609ea5a6fb15" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +dependencies = [ + "digest", + "rand_core", +] + +[[package]] +name = "sized-chunks" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16d69225bde7a69b235da73377861095455d298f2b970996eec25ddbb42b3d1e" +dependencies = [ + "bitmaps", + "typenum", +] + +[[package]] +name = "small-ord-set" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf7035a2b2268a5be8c1395738565b06beda836097e12021cdefc06b127a0e7e" +dependencies = [ + "smallvec", +] + +[[package]] +name = "smallvec" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" + +[[package]] +name = "smol_str" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fad6c857cbab2627dcf01ec85a623ca4e7dcb5691cbaa3d7fb7653671f0d09c9" +dependencies = [ + "serde", +] + +[[package]] +name = "spki" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "time" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e" +dependencies = [ + "deranged", + "itoa", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f" +dependencies = [ + "time-core", +] + +[[package]] +name = "toml" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.2.1", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-width" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "walkdir" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.48", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + +[[package]] +name = "winnow" +version = "0.5.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "818ce546a11a9986bc24f93d0cdf38a8a1a400f1473ea8c82e59f6e0ffab9249" +dependencies = [ + "memchr", +] + +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] diff --git a/avm-transpiler/Cargo.toml b/avm-transpiler/Cargo.toml new file mode 100644 index 000000000000..9d4f0a001ea9 --- /dev/null +++ b/avm-transpiler/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "avm-transpiler" +version = "0.1.0" +authors = ["The Aztec Team "] +edition = "2021" +license = "MIT OR Apache-2.0" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +# local +acvm = { path = "../noir/acvm-repo/acvm" } +noirc_driver = { path = "../noir/compiler/noirc_driver" } + +# external +base64 = "0.21" +regex = "1.10" +env_logger = "0.11" +log = "0.4" +serde_json = "1.0" +serde = { version = "1.0.136", features = ["derive"]} \ No newline at end of file diff --git a/avm-transpiler/Dockerfile b/avm-transpiler/Dockerfile new file mode 100644 index 000000000000..1f55f8ba40da --- /dev/null +++ b/avm-transpiler/Dockerfile @@ -0,0 +1,13 @@ +FROM rust:bookworm + +WORKDIR /usr/src +COPY ./avm-transpiler ./avm-transpiler +COPY ./noir ./noir + +WORKDIR /usr/src/avm-transpiler +RUN apt-get update && apt-get install -y git +RUN ./scripts/bootstrap_native.sh + +FROM ubuntu:lunar +COPY --from=0 /usr/src/avm-transpiler/target/release/avm-transpiler /usr/src/avm-transpiler/target/release/avm-transpiler +ENTRYPOINT ["sh", "-c"] \ No newline at end of file diff --git a/avm-transpiler/Dockerfile.dockerignore b/avm-transpiler/Dockerfile.dockerignore new file mode 100644 index 000000000000..8edf9d12d625 --- /dev/null +++ b/avm-transpiler/Dockerfile.dockerignore @@ -0,0 +1,7 @@ +** + +!avm-transpiler/ +!noir/ +**/target/ +**/node_modules/ +**/packages/ \ No newline at end of file diff --git a/avm-transpiler/README.md b/avm-transpiler/README.md new file mode 100644 index 000000000000..2cd932c24518 --- /dev/null +++ b/avm-transpiler/README.md @@ -0,0 +1,15 @@ +# AVM Transpiler + +This component transpiles Aztec public contracts code from Noir's Brillig bytecode to AVM (Aztec Virtual Machine) bytecode. + +## Build + +``` +./boostrap.sh +``` + +## Run + +``` +cargo run +``` diff --git a/avm-transpiler/bootstrap.sh b/avm-transpiler/bootstrap.sh new file mode 100755 index 000000000000..916d8107876e --- /dev/null +++ b/avm-transpiler/bootstrap.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +set -eu + +cd $(dirname "$0") + +CMD=${1:-} + +if [ -n "$CMD" ]; then + if [ "$CMD" = "clean" ]; then + cargo clean + git clean -fdx + exit 0 + else + echo "Unknown command: $CMD" + exit 1 + fi +fi + +./scripts/bootstrap_native.sh \ No newline at end of file diff --git a/avm-transpiler/rust-toolchain.toml b/avm-transpiler/rust-toolchain.toml new file mode 100644 index 000000000000..6ed42dc02c29 --- /dev/null +++ b/avm-transpiler/rust-toolchain.toml @@ -0,0 +1,5 @@ +[toolchain] +channel = "1.71.1" +components = [ "rust-src" ] +targets = [] +profile = "default" diff --git a/avm-transpiler/scripts/bootstrap_native.sh b/avm-transpiler/scripts/bootstrap_native.sh new file mode 100755 index 000000000000..3e0e2ed853ac --- /dev/null +++ b/avm-transpiler/scripts/bootstrap_native.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +set -eu + +cd $(dirname "$0")/.. + +# If this project has been subrepod into another project, set build data manually. +export SOURCE_DATE_EPOCH=$(date +%s) +export GIT_DIRTY=false +if [ -f ".gitrepo" ]; then + export GIT_COMMIT=$(awk '/commit =/ {print $3}' .gitrepo) +else + export GIT_COMMIT=$(git rev-parse --verify HEAD) +fi + +# Build native. +if [ -n "${DEBUG:-}" ]; then + cargo build +else + cargo build --release +fi diff --git a/avm-transpiler/src/instructions.rs b/avm-transpiler/src/instructions.rs new file mode 100644 index 000000000000..efc195cc5b0f --- /dev/null +++ b/avm-transpiler/src/instructions.rs @@ -0,0 +1,115 @@ +use crate::opcodes::AvmOpcode; + +/// Common values of the indirect instruction flag +pub const ZEROTH_OPERAND_INDIRECT: u8 = 0b00000001; +pub const FIRST_OPERAND_INDIRECT: u8 = 0b00000010; +pub const ZEROTH_FIRST_OPERANDS_INDIRECT: u8 = 0b00000011; + +/// A simple representation of an AVM instruction for the purpose +/// of generating an AVM bytecode from Brillig. +/// Note: this does structure not impose rules like "ADD instruction must have 3 operands" +/// That job is left to the instruction decoder, not this thin transpiler. +pub struct AvmInstruction { + pub opcode: AvmOpcode, + + /// Any instructions with memory offset operands have the indirect flag + /// Each bit is a boolean: 0:direct, 1:indirect + /// The 0th bit corresponds to an instruction's 0th offset arg, 1st to 1st, etc... + pub indirect: Option, + + /// Some instructions have a destination or input tag + // TODO(4271): add in_tag alongside its support in TS + //pub in_tag: Option, + pub dst_tag: Option, + + /// Different instructions have different numbers of operands + pub operands: Vec, +} +impl AvmInstruction { + /// String representation for printing AVM programs + pub fn to_string(&self) -> String { + let mut out_str = format!("opcode {}", self.opcode.name()); + if let Some(indirect) = self.indirect { + out_str += format!(", indirect: {}", indirect).as_str(); + } + // TODO(4271): add in_tag alongside its support in TS + if let Some(dst_tag) = self.dst_tag { + out_str += format!(", dst_tag: {}", dst_tag as u8).as_str(); + } + if self.operands.len() > 0 { + out_str += ", operands: ["; + for operand in &self.operands { + out_str += format!("{}, ", operand.to_string()).as_str(); + } + out_str += "]"; + } + out_str + } + /// Bytes representation for generating AVM bytecode + pub fn to_bytes(&self) -> Vec { + let mut bytes = Vec::new(); + bytes.push(self.opcode as u8); + if let Some(indirect) = self.indirect { + bytes.push(indirect); + } + // TODO(4271): add in_tag alongside its support in TS + if let Some(dst_tag) = self.dst_tag { + // TODO(4271): make 8 bits when TS supports deserialization of 8 bit flags + //bytes.push(dst_tag as u8); + bytes.extend_from_slice(&(dst_tag as u32).to_be_bytes()); + } + for operand in &self.operands { + bytes.extend_from_slice(&operand.to_be_bytes()); + } + bytes + } +} +impl Default for AvmInstruction { + fn default() -> Self { + AvmInstruction { + opcode: AvmOpcode::ADD, + // TODO(4266): default to Some(0), since all instructions have indirect flag except jumps + indirect: None, + dst_tag: None, + operands: vec![], + } + } +} + +/// AVM instructions may include a type tag +#[derive(Copy, Clone)] +pub enum AvmTypeTag { + UNINITIALIZED, + UINT8, + UINT16, + UINT32, + UINT64, + UINT128, + FIELD, + INVALID, +} + +/// Operands are usually 32 bits (offsets or jump destinations) +/// Constants (as used by the SET instruction) can have size +/// different from 32 bits +pub enum AvmOperand { + U32 { value: u32 }, + // TODO(4267): Support operands of size other than 32 bits (for SET) + //U128 { value: u128 }, +} +impl AvmOperand { + pub fn to_string(&self) -> String { + match self { + AvmOperand::U32 { value } => format!(" U32:{}", value), + // TODO(4267): Support operands of size other than 32 bits (for SET) + //AvmOperand::U128 { value } => format!("U128:{}", value), + } + } + pub fn to_be_bytes(&self) -> Vec { + match self { + AvmOperand::U32 { value } => value.to_be_bytes().to_vec(), + // TODO(4267): Support operands of size other than 32 bits (for SET) + //AvmOperand::U128 { value } => value.to_be_bytes().to_vec(), + } + } +} diff --git a/avm-transpiler/src/main.rs b/avm-transpiler/src/main.rs new file mode 100644 index 000000000000..064bf343bbf7 --- /dev/null +++ b/avm-transpiler/src/main.rs @@ -0,0 +1,46 @@ +use log::warn; +use std::env; +use std::fs; +use std::path::Path; + +mod instructions; +mod opcodes; +mod transpile; +mod transpile_contract; +mod utils; + +use transpile_contract::{CompiledAcirContract, TranspiledContract}; + +fn main() { + env_logger::init(); + + let args: Vec = env::args().collect(); + let in_contract_artifact_path = &args[1]; + let out_transpiled_artifact_path = &args[2]; + + // Parse original (pre-transpile) contract + let contract_json = + fs::read_to_string(Path::new(in_contract_artifact_path)).expect("Unable to read file"); + let raw_json_obj: serde_json::Value = + serde_json::from_str(&contract_json).expect("Unable to parse json"); + + // Skip if contract has "transpiled: true" flag! + if let Some(transpiled) = raw_json_obj.get("transpiled") { + match transpiled { + serde_json::Value::Bool(true) => { + warn!("Contract already transpiled. Skipping."); + return; // nothing to transpile + } + _ => (), + } + } + // Parse json into contract object + let contract: CompiledAcirContract = + serde_json::from_str(&contract_json).expect("Unable to parse json"); + + // Transpile contract to AVM bytecode + let transpiled_contract = TranspiledContract::from(contract); + let transpiled_json = + serde_json::to_string(&transpiled_contract).expect("Unable to serialize json"); + fs::write(out_transpiled_artifact_path, transpiled_json).expect("Unable to write file"); +} diff --git a/avm-transpiler/src/opcodes.rs b/avm-transpiler/src/opcodes.rs new file mode 100644 index 000000000000..cc3c828d2ecd --- /dev/null +++ b/avm-transpiler/src/opcodes.rs @@ -0,0 +1,168 @@ +/// All AVM opcodes +/// Keep updated with TS and yellow paper! +#[derive(Copy, Clone)] +pub enum AvmOpcode { + // Compute + // Compute - Arithmetic + ADD, + SUB, + MUL, + DIV, + // Compute - Comparators + EQ, + LT, + LTE, + // Compute - Bitwise + AND, + OR, + XOR, + NOT, + SHL, + SHR, + // Compute - Type Conversions + CAST, + + // Execution Environment + ADDRESS, + STORAGEADDRESS, + ORIGIN, + SENDER, + PORTAL, + FEEPERL1GAS, + FEEPERL2GAS, + FEEPERDAGAS, + CONTRACTCALLDEPTH, + // Execution Environment - Globals + CHAINID, + VERSION, + BLOCKNUMBER, + TIMESTAMP, + COINBASE, + BLOCKL1GASLIMIT, + BLOCKL2GASLIMIT, + BLOCKDAGASLIMIT, + // Execution Environment - Calldata + CALLDATACOPY, + + // Machine State + // Machine State - Gas + L1GASLEFT, + L2GASLEFT, + DAGASLEFT, + // Machine State - Internal Control Flow + JUMP, + JUMPI, + INTERNALCALL, + INTERNALRETURN, + // Machine State - Memory + SET, + MOV, + CMOV, + + // World State + BLOCKHEADERBYNUMBER, + SLOAD, // Public Storage + SSTORE, // Public Storage + READL1TOL2MSG, // Messages + SENDL2TOL1MSG, // Messages + EMITNOTEHASH, // Notes & Nullifiers + EMITNULLIFIER, // Notes & Nullifiers + + // Accrued Substate + EMITUNENCRYPTEDLOG, + + // Control Flow - Contract Calls + CALL, + STATICCALL, + RETURN, + REVERT, + + // Gadgets + KECCAK, + POSEIDON, +} + +impl AvmOpcode { + pub fn name(&self) -> &'static str { + match self { + // Compute + // Compute - Arithmetic + AvmOpcode::ADD => "ADD", + AvmOpcode::SUB => "SUB", + AvmOpcode::MUL => "MUL", + AvmOpcode::DIV => "DIV", + // Compute - Comparators + AvmOpcode::EQ => "EQ", + AvmOpcode::LT => "LT", + AvmOpcode::LTE => "LTE", + // Compute - Bitwise + AvmOpcode::AND => "AND", + AvmOpcode::OR => "OR", + AvmOpcode::XOR => "XOR", + AvmOpcode::NOT => "NOT", + AvmOpcode::SHL => "SHL", + AvmOpcode::SHR => "SHR", + // Compute - Type Conversions + AvmOpcode::CAST => "CAST", + + // Execution Environment + AvmOpcode::ADDRESS => "ADDRESS", + AvmOpcode::STORAGEADDRESS => "STORAGEADDRESS", + AvmOpcode::ORIGIN => "ORIGIN", + AvmOpcode::SENDER => "SENDER", + AvmOpcode::PORTAL => "PORTAL", + AvmOpcode::FEEPERL1GAS => "FEEPERL1GAS", + AvmOpcode::FEEPERL2GAS => "FEEPERL2GAS", + AvmOpcode::FEEPERDAGAS => "FEEPERDAGAS", + AvmOpcode::CONTRACTCALLDEPTH => "CONTRACTCALLDEPTH", + // Execution Environment - Globals + AvmOpcode::CHAINID => "CHAINID", + AvmOpcode::VERSION => "VERSION", + AvmOpcode::BLOCKNUMBER => "BLOCKNUMBER", + AvmOpcode::TIMESTAMP => "TIMESTAMP", + AvmOpcode::COINBASE => "COINBASE", + AvmOpcode::BLOCKL1GASLIMIT => "BLOCKL1GASLIMIT", + AvmOpcode::BLOCKL2GASLIMIT => "BLOCKL2GASLIMIT", + AvmOpcode::BLOCKDAGASLIMIT => "BLOCKDAGASLIMIT", + // Execution Environment - Calldata + AvmOpcode::CALLDATACOPY => "CALLDATACOPY", + + // Machine State + // Machine State - Gas + AvmOpcode::L1GASLEFT => "L1GASLEFT", + AvmOpcode::L2GASLEFT => "L2GASLEFT", + AvmOpcode::DAGASLEFT => "DAGASLEFT", + // Machine State - Internal Control Flow + AvmOpcode::JUMP => "JUMP", + AvmOpcode::JUMPI => "JUMPI", + AvmOpcode::INTERNALCALL => "INTERNALCALL", + AvmOpcode::INTERNALRETURN => "INTERNALRETURN", + // Machine State - Memory + AvmOpcode::SET => "SET", + AvmOpcode::MOV => "MOV", + AvmOpcode::CMOV => "CMOV", + + // World State + AvmOpcode::BLOCKHEADERBYNUMBER => "BLOCKHEADERBYNUMBER", + AvmOpcode::SLOAD => "SLOAD", // Public Storage + AvmOpcode::SSTORE => "SSTORE", // Public Storage + AvmOpcode::READL1TOL2MSG => "READL1TOL2MSG", // Messages + AvmOpcode::SENDL2TOL1MSG => "SENDL2TOL1MSG", // Messages + AvmOpcode::EMITNOTEHASH => "EMITNOTEHASH", // Notes & Nullifiers + AvmOpcode::EMITNULLIFIER => "EMITNULLIFIER", // Notes & Nullifiers + + // Accrued Substate + AvmOpcode::EMITUNENCRYPTEDLOG => "EMITUNENCRYPTEDLOG", + + // Control Flow - Contract Calls + AvmOpcode::CALL => "CALL", + AvmOpcode::STATICCALL => "STATICCALL", + AvmOpcode::RETURN => "RETURN", + AvmOpcode::REVERT => "REVERT", + + // Gadgets + AvmOpcode::KECCAK => "KECCAK", + AvmOpcode::POSEIDON => "POSEIDON", + } + } +} diff --git a/avm-transpiler/src/transpile.rs b/avm-transpiler/src/transpile.rs new file mode 100644 index 000000000000..9785e41c7a1b --- /dev/null +++ b/avm-transpiler/src/transpile.rs @@ -0,0 +1,289 @@ +use acvm::acir::brillig::Opcode as BrilligOpcode; +use acvm::acir::circuit::brillig::Brillig; + +use acvm::brillig_vm::brillig::{BinaryFieldOp, BinaryIntOp}; + +use crate::instructions::{ + AvmInstruction, AvmOperand, AvmTypeTag, FIRST_OPERAND_INDIRECT, ZEROTH_OPERAND_INDIRECT, +}; +use crate::opcodes::AvmOpcode; +use crate::utils::{dbg_print_avm_program, dbg_print_brillig_program}; + +/// Transpile a Brillig program to AVM bytecode +pub fn brillig_to_avm(brillig: &Brillig) -> Vec { + dbg_print_brillig_program(&brillig); + + let mut avm_instrs: Vec = Vec::new(); + + // Map Brillig pcs to AVM pcs + // (some Brillig instructions map to >1 AVM instruction) + let brillig_pcs_to_avm_pcs = map_brillig_pcs_to_avm_pcs(avm_instrs.len(), brillig); + + // Transpile a Brillig instruction to one or more AVM instructions + for brillig_instr in &brillig.bytecode { + match brillig_instr { + BrilligOpcode::BinaryFieldOp { + destination, + op, + lhs, + rhs, + } => { + let avm_opcode = match op { + BinaryFieldOp::Add => AvmOpcode::ADD, + BinaryFieldOp::Sub => AvmOpcode::SUB, + BinaryFieldOp::Mul => AvmOpcode::MUL, + BinaryFieldOp::Div => AvmOpcode::DIV, + BinaryFieldOp::Equals => AvmOpcode::EQ, + }; + // TODO(4268): set in_tag to `field` + avm_instrs.push(AvmInstruction { + opcode: avm_opcode, + operands: vec![ + AvmOperand::U32 { + value: lhs.to_usize() as u32, + }, + AvmOperand::U32 { + value: rhs.to_usize() as u32, + }, + AvmOperand::U32 { + value: destination.to_usize() as u32, + }, + ], + ..Default::default() + }); + } + BrilligOpcode::BinaryIntOp { + destination, + op, + bit_size: _, // TODO(4268): support u8..u128 and use in_tag + lhs, + rhs, + } => { + let avm_opcode = match op { + BinaryIntOp::Add => AvmOpcode::ADD, + BinaryIntOp::Sub => AvmOpcode::SUB, + BinaryIntOp::Mul => AvmOpcode::MUL, + BinaryIntOp::UnsignedDiv => AvmOpcode::DIV, + BinaryIntOp::Equals => AvmOpcode::EQ, + BinaryIntOp::LessThan => AvmOpcode::LT, + BinaryIntOp::LessThanEquals => AvmOpcode::LTE, + BinaryIntOp::And => AvmOpcode::AND, + BinaryIntOp::Or => AvmOpcode::OR, + BinaryIntOp::Xor => AvmOpcode::XOR, + BinaryIntOp::Shl => AvmOpcode::SHL, + BinaryIntOp::Shr => AvmOpcode::SHR, + _ => panic!( + "Transpiler doesn't know how to process BinaryIntOp {:?}", + brillig_instr + ), + }; + // TODO(4268): support u8..u128 and use in_tag + avm_instrs.push(AvmInstruction { + opcode: avm_opcode, + operands: vec![ + AvmOperand::U32 { + value: lhs.to_usize() as u32, + }, + AvmOperand::U32 { + value: rhs.to_usize() as u32, + }, + AvmOperand::U32 { + value: destination.to_usize() as u32, + }, + ], + ..Default::default() + }); + } + BrilligOpcode::CalldataCopy { destination_address, size, offset } => { + avm_instrs.push(AvmInstruction { + opcode: AvmOpcode::CALLDATACOPY, + operands: vec![ + AvmOperand::U32 { + value: *offset as u32, // cdOffset (calldata offset) + }, AvmOperand::U32 { + value: *size as u32, + }, AvmOperand::U32 { + value: destination_address.to_usize() as u32, // dstOffset + }], + ..Default::default() + }); + } + BrilligOpcode::Jump { location } => { + let avm_loc = brillig_pcs_to_avm_pcs[*location]; + avm_instrs.push(AvmInstruction { + opcode: AvmOpcode::JUMP, + operands: vec![AvmOperand::U32 { + value: avm_loc as u32, + }], + ..Default::default() + }); + } + BrilligOpcode::JumpIf { + condition, + location, + } => { + let avm_loc = brillig_pcs_to_avm_pcs[*location]; + avm_instrs.push(AvmInstruction { + opcode: AvmOpcode::JUMPI, + operands: vec![ + AvmOperand::U32 { + value: avm_loc as u32, + }, + AvmOperand::U32 { + value: condition.to_usize() as u32, + }, + ], + ..Default::default() + }); + } + BrilligOpcode::Const { destination, value } => { + avm_instrs.push(AvmInstruction { + opcode: AvmOpcode::SET, + dst_tag: Some(AvmTypeTag::UINT32), + operands: vec![ + // TODO(4267): support u8..u128 and use dst_tag + AvmOperand::U32 { + value: value.to_usize() as u32, + }, + AvmOperand::U32 { + value: destination.to_usize() as u32, + }, + ], + ..Default::default() + }); + } + BrilligOpcode::Mov { + destination, + source, + } => { + avm_instrs.push(AvmInstruction { + opcode: AvmOpcode::MOV, + operands: vec![ + AvmOperand::U32 { + value: source.to_usize() as u32, + }, + AvmOperand::U32 { + value: destination.to_usize() as u32, + }, + ], + ..Default::default() + }); + } + BrilligOpcode::Load { + destination, + source_pointer, + } => { + avm_instrs.push(AvmInstruction { + opcode: AvmOpcode::MOV, + indirect: Some(ZEROTH_OPERAND_INDIRECT), // indirect srcOffset operand + operands: vec![ + AvmOperand::U32 { + value: source_pointer.to_usize() as u32, + }, + AvmOperand::U32 { + value: destination.to_usize() as u32, + }, + ], + ..Default::default() + }); + } + BrilligOpcode::Store { + destination_pointer, + source, + } => { + // INDIRECT dstOffset operand (bit 1 set high) + avm_instrs.push(AvmInstruction { + opcode: AvmOpcode::MOV, + indirect: Some(FIRST_OPERAND_INDIRECT), // indirect dstOffset operand + operands: vec![ + AvmOperand::U32 { + value: source.to_usize() as u32, + }, + AvmOperand::U32 { + value: destination_pointer.to_usize() as u32, + }, + ], + ..Default::default() + }); + } + BrilligOpcode::Call { location } => { + let avm_loc = brillig_pcs_to_avm_pcs[*location]; + avm_instrs.push(AvmInstruction { + opcode: AvmOpcode::INTERNALCALL, + operands: vec![AvmOperand::U32 { + value: avm_loc as u32, + }], + ..Default::default() + }); + } + BrilligOpcode::Return {} => avm_instrs.push(AvmInstruction { + opcode: AvmOpcode::INTERNALRETURN, + ..Default::default() + }), + BrilligOpcode::Stop { return_data_offset, return_data_size } => { + avm_instrs.push(AvmInstruction { + opcode: AvmOpcode::RETURN, + operands: vec![ + AvmOperand::U32 { value: *return_data_offset as u32}, + AvmOperand::U32 { value: *return_data_size as u32}, + ], + ..Default::default() + }); + } + BrilligOpcode::Trap { /*return_data_offset, return_data_size*/ } => { + // TODO(https://github.com/noir-lang/noir/issues/3113): Trap should support return data + avm_instrs.push(AvmInstruction { + opcode: AvmOpcode::REVERT, + operands: vec![ + //AvmOperand::U32 { value: *return_data_offset as u32}, + //AvmOperand::U32 { value: *return_data_size as u32}, + AvmOperand::U32 { value: 0}, + AvmOperand::U32 { value: 0}, + ], + ..Default::default() + }); + } + _ => panic!( + "Transpiler doesn't know how to process {:?} brillig instruction", + brillig_instr + ), + } + } + + dbg_print_avm_program(&avm_instrs); + + // Constructing bytecode from instructions + let mut bytecode = Vec::new(); + for i in 0..avm_instrs.len() { + let instr_bytes = avm_instrs[i].to_bytes(); + bytecode.extend_from_slice(&instr_bytes); + } + bytecode +} + +/// Compute an array that maps each Brillig pc to an AVM pc. +/// This must be done before transpiling to properly transpile jump destinations. +/// This is necessary for two reasons: +/// 1. The transpiler injects `initial_offset` instructions at the beginning of the program. +/// 2. Some brillig instructions (_e.g._ Stop, or certain ForeignCalls) map to multiple AVM instructions +/// args: +/// initial_offset: how many AVM instructions were inserted at the start of the program +/// brillig: the Brillig program +/// returns: an array where each index is a Brillig pc, +/// and each value is the corresponding AVM pc. +fn map_brillig_pcs_to_avm_pcs(initial_offset: usize, brillig: &Brillig) -> Vec { + let mut pc_map = Vec::with_capacity(brillig.bytecode.len()); + pc_map.resize(brillig.bytecode.len(), 0); + pc_map[0] = initial_offset; + for i in 0..brillig.bytecode.len() - 1 { + let num_avm_instrs_for_this_brillig_instr = match &brillig.bytecode[i] { + BrilligOpcode::Load { .. } => 2, + BrilligOpcode::Store { .. } => 2, + _ => 1, + }; + // next Brillig pc will map to an AVM pc offset by the + // number of AVM instructions generated for this Brillig one + pc_map[i + 1] = pc_map[i] + num_avm_instrs_for_this_brillig_instr; + } + pc_map +} diff --git a/avm-transpiler/src/transpile_contract.rs b/avm-transpiler/src/transpile_contract.rs new file mode 100644 index 000000000000..2a8b762139ff --- /dev/null +++ b/avm-transpiler/src/transpile_contract.rs @@ -0,0 +1,120 @@ +use log::info; +use regex::Regex; +use serde::{Deserialize, Serialize}; + +use acvm::acir::circuit::Circuit; +use noirc_driver::ContractFunctionType; + +use crate::transpile::brillig_to_avm; +use crate::utils::extract_brillig_from_acir; + +/// Representation of a contract with some transpiled functions +#[derive(Debug, Serialize, Deserialize)] +pub struct TranspiledContract { + pub transpiled: bool, + pub noir_version: String, + pub name: String, + // Functions can be ACIR or AVM + pub functions: Vec, + pub events: serde_json::Value, + pub file_map: serde_json::Value, + //pub warnings: serde_json::Value, +} + +/// A regular contract with ACIR+Brillig functions +/// but with fields irrelevant to transpilation +/// represented as catch-all serde Values +#[derive(Debug, Serialize, Deserialize)] +pub struct CompiledAcirContract { + pub noir_version: String, + pub name: String, + pub functions: Vec, + pub events: serde_json::Value, + pub file_map: serde_json::Value, + //pub warnings: serde_json::Value, +} + +/// Representation of a contract function +/// with AVM bytecode as a base64 string +#[derive(Debug, Serialize, Deserialize)] +pub struct AvmContractFunction { + pub name: String, + pub function_type: ContractFunctionType, + pub is_internal: bool, + pub abi: serde_json::Value, + pub bytecode: String, // base64 + pub debug_symbols: serde_json::Value, +} + +/// Representation of an ACIR contract function but with +/// catch-all serde Values for fields irrelevant to transpilation +#[derive(Debug, Serialize, Deserialize)] +pub struct AcirContractFunction { + pub name: String, + pub function_type: ContractFunctionType, + pub is_internal: bool, + pub abi: serde_json::Value, + #[serde( + serialize_with = "Circuit::serialize_circuit_base64", + deserialize_with = "Circuit::deserialize_circuit_base64" + )] + pub bytecode: Circuit, + pub debug_symbols: serde_json::Value, +} + +/// An enum that allows the TranspiledContract struct to contain +/// functions with either ACIR or AVM bytecode +#[derive(Debug, Serialize, Deserialize)] +#[serde(untagged)] // omit Acir/Avm tag for these objects in json +pub enum AvmOrAcirContractFunction { + Acir(AcirContractFunction), + Avm(AvmContractFunction), +} + +/// Transpilation is performed when a TranspiledContract +/// is constructed from a CompiledAcirContract +impl From for TranspiledContract { + fn from(contract: CompiledAcirContract) -> Self { + let mut functions = Vec::new(); + for function in contract.functions { + // TODO(4269): once functions are tagged for transpilation to AVM, check tag + let re = Regex::new(r"avm_.*$").unwrap(); + if function.function_type == ContractFunctionType::Unconstrained + && re.is_match(function.name.as_str()) + { + info!( + "Transpiling AVM function {} on contract {}", + function.name, contract.name + ); + // Extract Brillig Opcodes from acir + let acir_circuit = function.bytecode.clone(); + let brillig = extract_brillig_from_acir(&acir_circuit.opcodes); + + // Transpile to AVM + let avm_bytecode = brillig_to_avm(&brillig); + + // Push modified function entry to ABI + functions.push(AvmOrAcirContractFunction::Avm(AvmContractFunction { + name: function.name, + function_type: function.function_type, + is_internal: function.is_internal, + abi: function.abi, + bytecode: base64::encode(avm_bytecode), + debug_symbols: function.debug_symbols, + })); + } else { + // This function is not flagged for transpilation. Push original entry. + functions.push(AvmOrAcirContractFunction::Acir(function)); + } + } + TranspiledContract { + transpiled: true, + noir_version: contract.noir_version, + name: contract.name, + functions, // some acir, some transpiled avm functions + events: contract.events, + file_map: contract.file_map, + //warnings: contract.warnings, + } + } +} diff --git a/avm-transpiler/src/utils.rs b/avm-transpiler/src/utils.rs new file mode 100644 index 000000000000..2f31c900450b --- /dev/null +++ b/avm-transpiler/src/utils.rs @@ -0,0 +1,41 @@ +use log::debug; + +use acvm::acir::circuit::brillig::Brillig; +use acvm::acir::circuit::Opcode; + +use crate::instructions::AvmInstruction; + +/// Extract the Brillig program from its ACIR wrapper instruction. +/// An Noir unconstrained function compiles to one ACIR instruction +/// wrapping a Brillig program. This function just extracts that Brillig +/// assuming the 0th ACIR opcode is the wrapper. +pub fn extract_brillig_from_acir(opcodes: &Vec) -> &Brillig { + if opcodes.len() != 1 { + panic!("There should only be one brillig opcode"); + } + let opcode = &opcodes[0]; + let brillig = match opcode { + Opcode::Brillig(brillig) => brillig, + _ => panic!("Tried to extract a Brillig program from its ACIR wrapper opcode, but the opcode doesn't contain Brillig!"), + }; + brillig +} + +/// Print inputs, outputs, and instructions in a Brillig program +pub fn dbg_print_brillig_program(brillig: &Brillig) { + debug!("Printing Brillig program..."); + debug!("\tInputs: {:?}", brillig.inputs); + for i in 0..brillig.bytecode.len() { + let instr = &brillig.bytecode[i]; + debug!("\tPC:{0} {1:?}", i, instr); + } + debug!("\tOutputs: {:?}", brillig.outputs); +} + +/// Print each instruction in an AVM program +pub fn dbg_print_avm_program(avm_program: &Vec) { + debug!("Printing AVM program..."); + for i in 0..avm_program.len() { + debug!("\tPC:{0}: {1}", i, &avm_program[i].to_string()); + } +} diff --git a/barretenberg/cpp/src/barretenberg/ecc/fields/field_conversion.hpp b/barretenberg/cpp/src/barretenberg/ecc/fields/field_conversion.hpp index c22982365120..cf5b12d1def9 100644 --- a/barretenberg/cpp/src/barretenberg/ecc/fields/field_conversion.hpp +++ b/barretenberg/cpp/src/barretenberg/ecc/fields/field_conversion.hpp @@ -208,4 +208,4 @@ template std::vector inline convert_to_bn254_frs(co return fr_vec; } -} // namespace bb::field_conversion \ No newline at end of file +} // namespace bb::field_conversion diff --git a/barretenberg/cpp/src/barretenberg/ecc/fields/field_conversion.test.cpp b/barretenberg/cpp/src/barretenberg/ecc/fields/field_conversion.test.cpp index d54ca02ba4b6..0a024f82545d 100644 --- a/barretenberg/cpp/src/barretenberg/ecc/fields/field_conversion.test.cpp +++ b/barretenberg/cpp/src/barretenberg/ecc/fields/field_conversion.test.cpp @@ -15,15 +15,6 @@ class FieldConversionTest : public ::testing::Test { } }; -/** - * @brief Field conversion test for size_t - */ -TEST_F(FieldConversionTest, FieldConversionSizeT) -{ - size_t x = 210849; - check_conversion(x); -} - /** * @brief Field conversion test for uint32_t */ @@ -140,4 +131,4 @@ TEST_F(FieldConversionTest, FieldConversionUnivariateGrumpkinFr) check_conversion(x1); } -} // namespace bb::field_conversion_tests \ No newline at end of file +} // namespace bb::field_conversion_tests diff --git a/bootstrap.sh b/bootstrap.sh index 8e8b31e9b0de..502bdc199567 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -54,6 +54,7 @@ PROJECTS=( barretenberg noir l1-contracts + avm-transpiler yarn-project ) diff --git a/boxes/token/src/contracts/src/types/token_note.nr b/boxes/token/src/contracts/src/types/token_note.nr index 2f27cf4cb6dc..a0283d4a95a1 100644 --- a/boxes/token/src/contracts/src/types/token_note.nr +++ b/boxes/token/src/contracts/src/types/token_note.nr @@ -8,7 +8,7 @@ use dep::aztec::{ note::{ note_header::NoteHeader, note_interface::NoteInterface, - utils::compute_note_hash_for_read_or_nullify, + utils::compute_note_hash_for_consumption, }, context::PrivateContext, state_vars::set::Set, @@ -58,18 +58,14 @@ impl Deserialize for TokenNote { } impl NoteInterface for TokenNote { - fn compute_note_hash(self) -> Field { + fn compute_note_content_hash(self) -> Field { // TODO(#1205) Should use a non-zero generator index. - pedersen_hash([ - self.amount.value as Field, - self.owner.to_field(), - self.randomness, - ],0) + pedersen_hash(self.serialize(), 0) } // docs:start:nullifier fn compute_nullifier(self, context: &mut PrivateContext) -> Field { - let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(self); + let note_hash_for_nullify = compute_note_hash_for_consumption(self); let secret = context.request_nullifier_secret_key(self.owner); // TODO(#1205) Should use a non-zero generator index. pedersen_hash([ @@ -81,7 +77,7 @@ impl NoteInterface for TokenNote { // docs:end:nullifier fn compute_nullifier_without_context(self) -> Field { - let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(self); + let note_hash_for_nullify = compute_note_hash_for_consumption(self); let secret = get_nullifier_secret_key(self.owner); // TODO(#1205) Should use a non-zero generator index. pedersen_hash([ diff --git a/boxes/token/src/contracts/src/types/transparent_note.nr b/boxes/token/src/contracts/src/types/transparent_note.nr index 4408e20dee42..b8d6812330d7 100644 --- a/boxes/token/src/contracts/src/types/transparent_note.nr +++ b/boxes/token/src/contracts/src/types/transparent_note.nr @@ -3,7 +3,7 @@ use dep::aztec::{ note::{ note_header::NoteHeader, note_interface::NoteInterface, - utils::compute_note_hash_for_read_or_nullify, + utils::compute_note_hash_for_consumption, }, hash::{compute_secret_hash, pedersen_hash}, context::PrivateContext, @@ -42,12 +42,9 @@ impl Deserialize for TransparentNote { impl NoteInterface for TransparentNote { - fn compute_note_hash(self) -> Field { + fn compute_note_content_hash(self) -> Field { // TODO(#1205) Should use a non-zero generator index. - pedersen_hash([ - self.amount, - self.secret_hash, - ],0) + pedersen_hash(self.serialize(), 0) } fn compute_nullifier(self, _context: &mut PrivateContext) -> Field { @@ -55,7 +52,7 @@ impl NoteInterface for TransparentNote { } fn compute_nullifier_without_context(self) -> Field { - let siloed_note_hash = compute_note_hash_for_read_or_nullify(self); + let siloed_note_hash = compute_note_hash_for_consumption(self); // TODO(#1205) Should use a non-zero generator index. pedersen_hash([self.secret, siloed_note_hash],0) } diff --git a/build_manifest.yml b/build_manifest.yml index 861b632af04b..00af54931b98 100644 --- a/build_manifest.yml +++ b/build_manifest.yml @@ -133,6 +133,7 @@ yarn-project-base: - noir - noir-packages - boxes-files + - avm-transpiler yarn-project: buildDir: yarn-project @@ -209,3 +210,9 @@ yellow-paper: buildDir: yellow-paper rebuildPatterns: - ^yellow-paper/ + +avm-transpiler: + buildDir: . + dockerfile: avm-transpiler/Dockerfile + rebuildPatterns: + - ^avm-transpiler/ \ No newline at end of file diff --git a/docs/docs/developers/contracts/syntax/storage/private_state.md b/docs/docs/developers/contracts/syntax/storage/private_state.md index 35429d74020f..d636459720a8 100644 --- a/docs/docs/developers/contracts/syntax/storage/private_state.md +++ b/docs/docs/developers/contracts/syntax/storage/private_state.md @@ -246,69 +246,77 @@ The key distinction is that this method is unconstrained. It does not perform a This function requires a `NoteViewerOptions`. The `NoteViewerOptions` is essentially similar to the [`NoteGetterOptions`](#notegetteroptions), except that it doesn't take a custom filter. -### NoteGetterOptions +## `NoteGetterOptions` `NoteGetterOptions` encapsulates a set of configurable options for filtering and retrieving a selection of notes from a [data oracle](../functions/oracles.md). Developers can design instances of `NoteGetterOptions`, to determine how notes should be filtered and returned to the functions of their smart contracts. You can view the implementation [here](https://github.com/AztecProtocol/aztec-packages/blob/#include_aztec_version/yarn-project/aztec-nr/aztec/src/note/note_getter_options.nr). -#### `selects: BoundedVec, N>` +### `selects: BoundedVec, N>` `selects` is a collection of filtering criteria, specified by `Select { field_index: u8, value: Field, comparator: u3 }` structs. It instructs the data oracle to find notes whose (`field_index`)th field matches the provided `value`, according to the `comparator`. -#### `sorts: BoundedVec, N>` +### `sorts: BoundedVec, N>` `sorts` is a set of sorting instructions defined by `Sort { field_index: u8, order: u2 }` structs. This directs the data oracle to sort the matching notes based on the value of the specified field index and in the indicated order. The value of order is **1** for _DESCENDING_ and **2** for _ASCENDING_. -#### `limit: u32` +### `limit: u32` When the `limit` is set to a non-zero value, the data oracle will return a maximum of `limit` notes. -#### `offset: u32` +### `offset: u32` This setting enables us to skip the first `offset` notes. It's particularly useful for pagination. -#### `filter: fn ([Option; MAX_READ_REQUESTS_PER_CALL], FILTER_ARGS) -> [Option; MAX_READ_REQUESTS_PER_CALL]` +### `filter: fn ([Option; MAX_READ_REQUESTS_PER_CALL], FILTER_ARGS) -> [Option; MAX_READ_REQUESTS_PER_CALL]` Developers have the option to provide a custom filter. This allows specific logic to be applied to notes that meet the criteria outlined above. The filter takes the notes returned from the oracle and `filter_args` as its parameters. It's important to note that the process of applying the custom filter to get the final notes is not constrained. It's crucial to verify the returned notes even if certain assumptions are made in the custom filter. -#### `filter_args: FILTER_ARGS` +### `filter_args: FILTER_ARGS` `filter_args` provides a means to furnish additional data or context to the custom filter. -#### Methods +### `status: u2` + +`status` allows the caller to retrieve notes that have been nullified, which can be useful to prove historical data. Note that when querying for both active and nullified notes the caller cannot know if each note retrieved has or has not been nullified. + +### Methods Several methods are available on `NoteGetterOptions` to construct the options in a more readable manner: -#### `fn new() -> NoteGetterOptions` +### `fn new() -> NoteGetterOptions` This function initializes a `NoteGetterOptions` that simply returns the maximum number of notes allowed in a call. -#### `fn with_filter(filter, filter_args) -> NoteGetterOptions` +### `fn with_filter(filter, filter_args) -> NoteGetterOptions` This function initializes a `NoteGetterOptions` with a [`filter`](#filter-fn-optionnote-max_read_requests_per_call-filter_args---optionnote-max_read_requests_per_call) and [`filter_args`](#filter_args-filter_args). -#### `.select` +### `.select` This method adds a [`Select`](#selects-boundedvecoptionselect-n) criterion to the options. -#### `.sort` +### `.sort` This method adds a [`Sort`](#sorts-boundedvecoptionsort-n) criterion to the options. -#### `.set_limit` +### `.set_limit` This method lets you set a limit for the maximum number of notes to be retrieved. -#### `.set_offset` +### `.set_offset` This method sets the offset value, which determines where to start retrieving notes. -#### Examples +### `.set_status` + +This method sets the status of notes to retrieve (active or nullified). -##### Example 1 +### Examples + +#### Example 1 The following code snippet creates an instance of `NoteGetterOptions`, which has been configured to find the cards that belong to `account_address`. The returned cards are sorted by their points in descending order, and the first `offset` cards with the highest points are skipped. @@ -342,7 +350,7 @@ The limit is `MAX_READ_REQUESTS_PER_CALL` by default. But we can set it to any v #include_code state_vars-NoteGetterOptionsPickOne /yarn-project/noir-contracts/contracts/docs_example_contract/src/options.nr rust -##### Example 2 +#### Example 2 An example of how we can use a Comparator to select notes when calling a Noir contract from aztec.js is below. @@ -351,4 +359,3 @@ An example of how we can use a Comparator to select notes when calling a Noir co In this example, we use the above typescript code to invoke a call to our Noir contract below. This Noir contract function takes an input to match with, and a comparator to use when fetching and selecting notes from storage. #include_code state_vars-NoteGetterOptionsComparatorExampleNoir /yarn-project/noir-contracts/contracts/docs_example_contract/src/main.nr rust - diff --git a/docs/docs/developers/contracts/syntax/storage/public_state.md b/docs/docs/developers/contracts/syntax/storage/public_state.md index 3218d9a7bab1..1b53e9dbaf9e 100644 --- a/docs/docs/developers/contracts/syntax/storage/public_state.md +++ b/docs/docs/developers/contracts/syntax/storage/public_state.md @@ -90,3 +90,45 @@ We have a `write` method on the `PublicState` struct that takes the value to wri #### Writing to our `minters` example #include_code write_minter /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust + +--- + +## Stable Public State + +`StablePublicState` is a special type of `PublicState` that can be read from both public and private! + +Since private execution is based on historical data, the user can pick ANY of its prior values to read from. This is why it `MUST` not be updated after the contract is deployed. The variable should be initialized at the constructor and then never changed. + +This makes the stable public variables useful for stuff that you would usually have in `immutable` values in solidity. For example this can be the name of a token or its number of decimals. + +Just like the `PublicState` it is generic over the variable type `T`. The type `MUST` implement Serialize and Deserialize traits. + +You can find the details of `StablePublicState` in the implementation [here](https://github.com/AztecProtocol/aztec-packages/blob/#include_aztec_version/yarn-project/aztec-nr/aztec/src/state_vars/stable_public_state.nr). + +### `new` +Is done exactly like the `PublicState` struct, but with the `StablePublicState` struct. + +#include_code storage_decimals /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust + +#include_code storage_decimals_init /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust + +### `initialize` + +#include_code initialize_decimals /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust + +:::warning Should only be called as part of the deployment. +If this is called outside the deployment transaction multiple values could be used down the line, potentially breaking the contract. + +Currently this is not constrained as we are in the middle of changing deployments. +::: + +### `read_public` + +Reading the value is like `PublicState`, simply with `read_public` instead of `read`. +#include_code read_decimals_public /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust + + +### `read_private` +Reading the value is like `PublicState`, simply with `read_private` instead of `read`. This part can only be executed in private. + +#include_code read_decimals_private /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust diff --git a/docs/docs/developers/contracts/syntax/storage/storage_slots.md b/docs/docs/developers/contracts/syntax/storage/storage_slots.md index af14fdeebfa0..62386330e7ff 100644 --- a/docs/docs/developers/contracts/syntax/storage/storage_slots.md +++ b/docs/docs/developers/contracts/syntax/storage/storage_slots.md @@ -31,7 +31,7 @@ sequenceDiagram Set->>LifeCycle: create_note(derived_slot, note) LifeCycle->>LifeCycle: note.header = NoteHeader { contract_address,
storage_slot: derived_slot, nonce: 0, is_transient: true } LifeCycle->>Utils: compute_inner_note_hash(note) - Utils->>TokenNote: compute_note_hash(note) + Utils->>TokenNote: note.compute_note_content_hash() TokenNote->>Utils: note_hash = H(amount, to, randomness) Utils->>NoteHash: compute_inner_hash(derived_slot, note_hash) NoteHash->>LifeCycle: inner_note_hash = H(derived_slot, note_hash) diff --git a/docs/docs/developers/getting_started/quickstart.md b/docs/docs/developers/getting_started/quickstart.md index 822cf85f5a73..a7f1f894916e 100644 --- a/docs/docs/developers/getting_started/quickstart.md +++ b/docs/docs/developers/getting_started/quickstart.md @@ -53,7 +53,7 @@ Once these have been installed, to start the sandbox, run: aztec-sandbox ``` -This will attempt to run the Sandbox on ` localhost:8080`, so you will have to make sure nothing else is running on that port or change the port defined in `./.aztec/docker-compose.yml`. Running the command again will overwrite any changes made to the `docker-compose.yml`. +This will attempt to run the Sandbox on ` localhost:8080`, so you will have to make sure nothing else is running on that port or change the port defined in `./.aztec/docker-compose.yml`. Running the installation again will overwrite any changes made to the `docker-compose.yml`. This command will also install the CLI if a node package version of the CLI isn't found locally. @@ -71,6 +71,10 @@ Start by deploying a token contract. After it is deployed, we check that the dep #include_code deploy yarn-project/end-to-end/src/guides/up_quick_start.sh bash +:::note +If you're not using the default port for the Sandbox, make sure to pass the `--rpc-url` parameter, e.g.: `--rpc-url http://localhost:8000`. +::: + Note that the deployed contract address is exported, so we can use it as `$CONTRACT` later on. ## Call a contract with the CLI diff --git a/docs/docs/learn/concepts/hybrid_state/main.md b/docs/docs/learn/concepts/hybrid_state/main.md index ac2565ee39ff..2c540f2a0777 100644 --- a/docs/docs/learn/concepts/hybrid_state/main.md +++ b/docs/docs/learn/concepts/hybrid_state/main.md @@ -12,18 +12,6 @@ Internal to the Aztec network, public state is stored and updated by the sequenc ## Private State -Every smart contract needs a way to track information over time - that's what state is. In order to have both private and public transactions and storage in Aztec, we have to have public and private state. - -On this page, you’ll learn - -- Aztec's unique interpretation of private state -- Representation of private state in an append-only database -- Concept of 'deleting' private state variables using nullifiers -- How to modify private state -- How Aztec abstracts the UTXO model from developers - -## Private State on Aztec - Private state must be treated differently from public state and this must be expressed in the semantics of Aztec.nr. Private state is encrypted and therefore is "owned" by a user or a set of users (via shared secrets) that are able to decrypt the state. @@ -32,7 +20,7 @@ Private state is represented in an append-only database since updating a record The act of "deleting" a private state variable can be represented by adding an associated nullifier to a nullifier set. The nullifier is generated such that, without knowing the decryption key of the owner, an observer cannot link a state record with a nullifier. -Modification of state variables can be emulated by nullifying the a state record and creating a new record to represent the variable. Private state has an intrinsic UTXO structure and this must be represented in the language semantics of manipulating private state. +Modification of state variables can be emulated by nullifying the state record and creating a new record to represent the variable. Private state has an intrinsic UTXO structure and this must be represented in the language semantics of manipulating private state. ### Abstracting UTXO's from App's / Users diff --git a/docs/docs/misc/migration_notes.md b/docs/docs/misc/migration_notes.md index bea84d5c980a..62168759c5bc 100644 --- a/docs/docs/misc/migration_notes.md +++ b/docs/docs/misc/migration_notes.md @@ -6,6 +6,39 @@ keywords: [sandbox, cli, aztec, notes, migration, updating, upgrading] Aztec is in full-speed development. Literally every version breaks compatibility with the previous ones. This page attempts to target errors and difficulties you might encounter when upgrading, and how to resolve them. +## TBD + +### `Note::compute_note_hash` renamed to `Note::compute_note_content_hash` +The `compute_note_hash` function in of the `Note` trait has been renamed to `compute_note_content_hash` to avoid being confused with the actual note hash. + +Before: +```rust +impl NoteInterface for CardNote { + fn compute_note_hash(self) -> Field { + pedersen_hash([ + self.owner.to_field(), + ], 0) + } +``` + +Now: +```rust +impl NoteInterface for CardNote { + fn compute_note_content_hash(self) -> Field { + pedersen_hash([ + self.owner.to_field(), + ], 0) + } +``` + +### Introduce `compute_note_hash_for_consumption` and `compute_note_hash_for_insertion` + +Makes a split in logic for note hash computation for consumption and insertion. This is to avoid confusion between the two, and to make it clear that the note hash for consumption is different from the note hash for insertion (sometimes). + +`compute_note_hash_for_consumption` replaces `compute_note_hash_for_read_or_nullify`. +`compute_note_hash_for_insertion` is new, and mainly used in `lifecycle.nr`` + + ## 0.22.0 ### [Aztec.nr] `Serialize`, `Deserialize`, `NoteInterface` as Traits, removal of SerializationMethods and SERIALIZED_LEN diff --git a/l1-contracts/src/periphery/ContractDeploymentEmitter.sol b/l1-contracts/src/periphery/ContractDeploymentEmitter.sol index 15a72458d406..efa55fc0f357 100644 --- a/l1-contracts/src/periphery/ContractDeploymentEmitter.sol +++ b/l1-contracts/src/periphery/ContractDeploymentEmitter.sol @@ -23,7 +23,7 @@ contract ContractDeploymentEmitter is IContractDeploymentEmitter { * @param _saltedInitializationHash - Salted init hash * @param _publicKeyHash - Public key hash * @param _acir - The acir bytecode of the L2 contract - * @dev See the link bellow for more info on partial address and public key: + * @dev See the link below for more info on partial address and public key: * https://github.com/AztecProtocol/aztec-packages/blob/master/docs/docs/concepts/foundation/accounts/keys.md#addresses-partial-addresses-and-public-keys * TODO: replace the link above with the link to deployed docs */ diff --git a/l1-contracts/src/periphery/interfaces/IContractDeploymentEmitter.sol b/l1-contracts/src/periphery/interfaces/IContractDeploymentEmitter.sol index bd45c93cd68c..9debce8240fd 100644 --- a/l1-contracts/src/periphery/interfaces/IContractDeploymentEmitter.sol +++ b/l1-contracts/src/periphery/interfaces/IContractDeploymentEmitter.sol @@ -19,7 +19,7 @@ interface IContractDeploymentEmitter { * @param saltedInitializationHash - Salted init hash * @param publicKeyHash - Public key hash * @param acir - The acir bytecode of the L2 contract - * @dev See the link bellow for more info on partial address and public key: + * @dev See the link below for more info on partial address and public key: * https://github.com/AztecProtocol/aztec-packages/blob/master/docs/docs/concepts/foundation/accounts/keys.md#addresses-partial-addresses-and-public-keys * TODO: replace the link above with the link to deployed docs */ diff --git a/noir/.github/workflows/release.yml b/noir/.github/workflows/release.yml index 7dfc844d18f7..71a0ab6d8945 100644 --- a/noir/.github/workflows/release.yml +++ b/noir/.github/workflows/release.yml @@ -87,7 +87,7 @@ jobs: - name: Cut a new version working-directory: ./docs - run: yarn docusaurus docs:version ${{ steps.noir-version.outputs.semver }} + run: yarn version ${{ steps.noir-version.outputs.semver }} - name: Configure git run: | diff --git a/noir/.gitrepo b/noir/.gitrepo index 940fb7a0483a..a565286408ab 100644 --- a/noir/.gitrepo +++ b/noir/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = https://github.com/noir-lang/noir branch = aztec-packages - commit = ea6aebcc4e190d9dbadaf1dd0f70950651eed615 - parent = 3b25737324e45bdfb49233f73065569301282cc0 + commit = 9944bb170691d4e7905793a978019ba9504b1139 + parent = aeb4cf0d9cec6127cac947c4f0de8e853b2f34e0 method = merge cmdver = 0.4.6 diff --git a/noir/acvm-repo/acir/acir_docs.md b/noir/acvm-repo/acir/README.md similarity index 100% rename from noir/acvm-repo/acir/acir_docs.md rename to noir/acvm-repo/acir/README.md diff --git a/noir/acvm-repo/acvm/src/compiler/optimizers/redundant_range.rs b/noir/acvm-repo/acvm/src/compiler/optimizers/redundant_range.rs index 5d19f9629ba2..ecabd98b3b1f 100644 --- a/noir/acvm-repo/acvm/src/compiler/optimizers/redundant_range.rs +++ b/noir/acvm-repo/acvm/src/compiler/optimizers/redundant_range.rs @@ -3,8 +3,7 @@ use acir::{ opcodes::{BlackBoxFuncCall, FunctionInput}, Circuit, Opcode, }, - native_types::{Expression, Witness}, - FieldElement, + native_types::Witness, }; use std::collections::{BTreeMap, HashSet}; @@ -105,9 +104,11 @@ impl RangeOptimizer { let mut new_order_list = Vec::with_capacity(order_list.len()); let mut optimized_opcodes = Vec::with_capacity(self.circuit.opcodes.len()); for (idx, opcode) in self.circuit.opcodes.into_iter().enumerate() { - let (witness, num_bits) = match extract_range_opcode(&opcode) { - Some(range_opcode) => range_opcode, - None => { + let (witness, num_bits) = match &opcode { + Opcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE { input }) => { + (input.witness, input.num_bits) + } + _ => { // If its not the range opcode, add it to the opcode // list and continue; optimized_opcodes.push(opcode); @@ -131,7 +132,7 @@ impl RangeOptimizer { if is_lowest_bit_size { already_seen_witness.insert(witness); new_order_list.push(order_list[idx]); - optimized_opcodes.push(optimized_range_opcode(witness, num_bits)); + optimized_opcodes.push(opcode); } } @@ -139,36 +140,11 @@ impl RangeOptimizer { } } -/// Extract the range opcode from the `Opcode` enum -/// Returns None, if `Opcode` is not the range opcode. -fn extract_range_opcode(opcode: &Opcode) -> Option<(Witness, u32)> { - match opcode { - Opcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE { input }) => { - Some((input.witness, input.num_bits)) - } - _ => None, - } -} - -fn optimized_range_opcode(witness: Witness, num_bits: u32) -> Opcode { - if num_bits == 1 { - Opcode::AssertZero(Expression { - mul_terms: vec![(FieldElement::one(), witness, witness)], - linear_combinations: vec![(-FieldElement::one(), witness)], - q_c: FieldElement::zero(), - }) - } else { - Opcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE { - input: FunctionInput { witness, num_bits }, - }) - } -} - #[cfg(test)] mod tests { use std::collections::BTreeSet; - use crate::compiler::optimizers::redundant_range::{extract_range_opcode, RangeOptimizer}; + use crate::compiler::optimizers::redundant_range::RangeOptimizer; use acir::{ circuit::{ opcodes::{BlackBoxFuncCall, FunctionInput}, @@ -218,11 +194,12 @@ mod tests { let (optimized_circuit, _) = optimizer.replace_redundant_ranges(acir_opcode_positions); assert_eq!(optimized_circuit.opcodes.len(), 1); - let (witness, num_bits) = - extract_range_opcode(&optimized_circuit.opcodes[0]).expect("expected one range opcode"); - - assert_eq!(witness, Witness(1)); - assert_eq!(num_bits, 16); + assert_eq!( + optimized_circuit.opcodes[0], + Opcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE { + input: FunctionInput { witness: Witness(1), num_bits: 16 } + }) + ); } #[test] @@ -240,15 +217,18 @@ mod tests { let (optimized_circuit, _) = optimizer.replace_redundant_ranges(acir_opcode_positions); assert_eq!(optimized_circuit.opcodes.len(), 2); - let (witness_a, num_bits_a) = - extract_range_opcode(&optimized_circuit.opcodes[0]).expect("expected two range opcode"); - let (witness_b, num_bits_b) = - extract_range_opcode(&optimized_circuit.opcodes[1]).expect("expected two range opcode"); - - assert_eq!(witness_a, Witness(1)); - assert_eq!(witness_b, Witness(2)); - assert_eq!(num_bits_a, 16); - assert_eq!(num_bits_b, 23); + assert_eq!( + optimized_circuit.opcodes[0], + Opcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE { + input: FunctionInput { witness: Witness(1), num_bits: 16 } + }) + ); + assert_eq!( + optimized_circuit.opcodes[1], + Opcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE { + input: FunctionInput { witness: Witness(2), num_bits: 23 } + }) + ); } #[test] diff --git a/noir/acvm-repo/acvm/src/compiler/transformers/mod.rs b/noir/acvm-repo/acvm/src/compiler/transformers/mod.rs index 970eb9390bb2..e184401c5d4a 100644 --- a/noir/acvm-repo/acvm/src/compiler/transformers/mod.rs +++ b/noir/acvm-repo/acvm/src/compiler/transformers/mod.rs @@ -99,61 +99,8 @@ pub(super) fn transform_internal( } } Opcode::BlackBoxFuncCall(ref func) => { - match func { - acir::circuit::opcodes::BlackBoxFuncCall::AND { output, .. } - | acir::circuit::opcodes::BlackBoxFuncCall::XOR { output, .. } => { - transformer.mark_solvable(*output); - } - acir::circuit::opcodes::BlackBoxFuncCall::RANGE { .. } - | acir::circuit::opcodes::BlackBoxFuncCall::RecursiveAggregation { .. } - | acir::circuit::opcodes::BlackBoxFuncCall::BigIntFromLeBytes { .. } - | acir::circuit::opcodes::BlackBoxFuncCall::BigIntAdd { .. } - | acir::circuit::opcodes::BlackBoxFuncCall::BigIntNeg { .. } - | acir::circuit::opcodes::BlackBoxFuncCall::BigIntMul { .. } - | acir::circuit::opcodes::BlackBoxFuncCall::BigIntDiv { .. } => (), - acir::circuit::opcodes::BlackBoxFuncCall::SHA256 { outputs, .. } - | acir::circuit::opcodes::BlackBoxFuncCall::Keccak256 { outputs, .. } - | acir::circuit::opcodes::BlackBoxFuncCall::Keccak256VariableLength { - outputs, - .. - } - | acir::circuit::opcodes::BlackBoxFuncCall::Keccakf1600 { outputs, .. } - | acir::circuit::opcodes::BlackBoxFuncCall::Blake2s { outputs, .. } - | acir::circuit::opcodes::BlackBoxFuncCall::Blake3 { outputs, .. } - | acir::circuit::opcodes::BlackBoxFuncCall::BigIntToLeBytes { - outputs, .. - } - | acir::circuit::opcodes::BlackBoxFuncCall::Poseidon2Permutation { - outputs, - .. - } - | acir::circuit::opcodes::BlackBoxFuncCall::Sha256Compression { - outputs, .. - } => { - for witness in outputs { - transformer.mark_solvable(*witness); - } - } - acir::circuit::opcodes::BlackBoxFuncCall::FixedBaseScalarMul { - outputs, - .. - } - | acir::circuit::opcodes::BlackBoxFuncCall::EmbeddedCurveAdd { - outputs, .. - } - | acir::circuit::opcodes::BlackBoxFuncCall::PedersenCommitment { - outputs, - .. - } => { - transformer.mark_solvable(outputs.0); - transformer.mark_solvable(outputs.1); - } - acir::circuit::opcodes::BlackBoxFuncCall::EcdsaSecp256k1 { output, .. } - | acir::circuit::opcodes::BlackBoxFuncCall::EcdsaSecp256r1 { output, .. } - | acir::circuit::opcodes::BlackBoxFuncCall::SchnorrVerify { output, .. } - | acir::circuit::opcodes::BlackBoxFuncCall::PedersenHash { output, .. } => { - transformer.mark_solvable(*output); - } + for witness in func.get_outputs_vec() { + transformer.mark_solvable(witness); } new_acir_opcode_positions.push(acir_opcode_positions[index]); diff --git a/noir/acvm-repo/acvm_js/src/logging.rs b/noir/acvm-repo/acvm_js/src/logging.rs index f5d71fae067b..483c23ab624e 100644 --- a/noir/acvm-repo/acvm_js/src/logging.rs +++ b/noir/acvm-repo/acvm_js/src/logging.rs @@ -1,3 +1,4 @@ +use js_sys::{Error, JsString}; use tracing_subscriber::prelude::*; use tracing_subscriber::EnvFilter; use tracing_web::MakeWebConsoleWriter; @@ -7,12 +8,15 @@ use wasm_bindgen::prelude::*; /// /// @param {LogLevel} level - The maximum level of logging to be emitted. #[wasm_bindgen(js_name = initLogLevel, skip_jsdoc)] -pub fn init_log_level(filter: String) { +pub fn init_log_level(filter: String) -> Result<(), JsLogInitError> { // Set the static variable from Rust use std::sync::Once; - let filter: EnvFilter = - filter.parse().expect("Could not parse log filter while initializing logger"); + let filter: EnvFilter = filter.parse().map_err(|err| { + JsLogInitError::constructor( + format!("Could not parse log filter while initializing logger: {err}").into(), + ) + })?; static SET_HOOK: Once = Once::new(); SET_HOOK.call_once(|| { @@ -23,4 +27,19 @@ pub fn init_log_level(filter: String) { tracing_subscriber::registry().with(fmt_layer.with_filter(filter)).init(); }); + + Ok(()) +} + +/// `LogInitError` is a raw js error. +/// It'd be ideal that `LogInitError` was a subclass of Error, but for that we'd need to use JS snippets or a js module. +/// Currently JS snippets don't work with a nodejs target. And a module would be too much for just a custom error type. +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(extends = Error, js_name = "LogInitError", typescript_type = "LogInitError")] + #[derive(Clone, Debug, PartialEq, Eq)] + pub type JsLogInitError; + + #[wasm_bindgen(constructor, js_class = "Error")] + fn constructor(message: JsString) -> JsLogInitError; } diff --git a/noir/aztec_macros/src/lib.rs b/noir/aztec_macros/src/lib.rs index e2c232e32334..1dbe76313883 100644 --- a/noir/aztec_macros/src/lib.rs +++ b/noir/aztec_macros/src/lib.rs @@ -26,8 +26,12 @@ impl MacroProcessor for AztecMacro { transform(ast, crate_id, context) } - fn process_typed_ast(&self, crate_id: &CrateId, context: &mut HirContext) { - transform_hir(crate_id, context) + fn process_typed_ast( + &self, + crate_id: &CrateId, + context: &mut HirContext, + ) -> Result<(), (MacroError, FileId)> { + transform_hir(crate_id, context).map_err(|(err, file_id)| (err.into(), file_id)) } } @@ -41,6 +45,7 @@ pub enum AztecMacroError { ContractHasTooManyFunctions { span: Span }, ContractConstructorMissing { span: Span }, UnsupportedFunctionArgumentType { span: Span, typ: UnresolvedTypeData }, + EventError { span: Span, message: String }, } impl From for MacroError { @@ -71,6 +76,11 @@ impl From for MacroError { secondary_message: None, span: Some(span), }, + AztecMacroError::EventError { span, message } => MacroError { + primary_message: message, + secondary_message: None, + span: Some(span), + }, } } } @@ -237,8 +247,11 @@ fn transform( // /// Completes the Hir with data gathered from type resolution -fn transform_hir(crate_id: &CrateId, context: &mut HirContext) { - transform_events(crate_id, context); +fn transform_hir( + crate_id: &CrateId, + context: &mut HirContext, +) -> Result<(), (AztecMacroError, FileId)> { + transform_events(crate_id, context) } /// Includes an import to the aztec library if it has not been included yet @@ -472,19 +485,30 @@ fn collect_crate_structs(crate_id: &CrateId, context: &HirContext) -> Vec Result<(), (AztecMacroError, FileId)> { let struct_type = interner.get_struct(struct_id); let selector_id = interner - .lookup_method(&Type::Struct(struct_type, vec![]), struct_id, "selector", false) - .expect("Selector method not found"); + .lookup_method(&Type::Struct(struct_type.clone(), vec![]), struct_id, "selector", false) + .ok_or_else(|| { + let error = AztecMacroError::EventError { + span: struct_type.borrow().location.span, + message: "Selector method not found".to_owned(), + }; + (error, struct_type.borrow().location.file) + })?; let selector_function = interner.function(&selector_id); let compute_selector_statement = interner.statement( - selector_function - .block(interner) - .statements() - .first() - .expect("Compute selector statement not found"), + selector_function.block(interner).statements().first().ok_or_else(|| { + let error = AztecMacroError::EventError { + span: struct_type.borrow().location.span, + message: "Compute selector statement not found".to_owned(), + }; + (error, struct_type.borrow().location.file) + })?, ); let compute_selector_expression = match compute_selector_statement { @@ -494,12 +518,21 @@ fn transform_event(struct_id: StructId, interner: &mut NodeInterner) { }, _ => None, } - .expect("Compute selector statement is not a call expression"); - - let first_arg_id = compute_selector_expression - .arguments - .first() - .expect("Missing argument for compute selector"); + .ok_or_else(|| { + let error = AztecMacroError::EventError { + span: struct_type.borrow().location.span, + message: "Compute selector statement is not a call expression".to_owned(), + }; + (error, struct_type.borrow().location.file) + })?; + + let first_arg_id = compute_selector_expression.arguments.first().ok_or_else(|| { + let error = AztecMacroError::EventError { + span: struct_type.borrow().location.span, + message: "Compute selector statement is not a call expression".to_owned(), + }; + (error, struct_type.borrow().location.file) + })?; match interner.expression(first_arg_id) { HirExpression::Literal(HirLiteral::Str(signature)) @@ -518,18 +551,29 @@ fn transform_event(struct_id: StructId, interner: &mut NodeInterner) { selector_literal_id, Type::String(Box::new(Type::Constant(signature.len() as u64))), ); + Ok(()) } - _ => unreachable!("Signature placeholder literal does not match"), + _ => Err(( + AztecMacroError::EventError { + span: struct_type.borrow().location.span, + message: "Signature placeholder literal does not match".to_owned(), + }, + struct_type.borrow().location.file, + )), } } -fn transform_events(crate_id: &CrateId, context: &mut HirContext) { +fn transform_events( + crate_id: &CrateId, + context: &mut HirContext, +) -> Result<(), (AztecMacroError, FileId)> { for struct_id in collect_crate_structs(crate_id, context) { let attributes = context.def_interner.struct_attributes(&struct_id); if attributes.iter().any(|attr| matches!(attr, SecondaryAttribute::Event)) { - transform_event(struct_id, &mut context.def_interner); + transform_event(struct_id, &mut context.def_interner)?; } } + Ok(()) } const SIGNATURE_PLACEHOLDER: &str = "SIGNATURE_PLACEHOLDER"; @@ -846,11 +890,11 @@ fn make_return_push(push_value: Expression) -> Statement { /// Make Return push array /// /// Translates to: -/// `context.return_values.push_array({push_value})` -fn make_return_push_array(push_value: Expression) -> Statement { +/// `context.return_values.extend_from_array({push_value})` +fn make_return_extend_from_array(push_value: Expression) -> Statement { make_statement(StatementKind::Semi(method_call( context_return_values(), - "push_array", + "extend_from_array", vec![push_value], ))) } @@ -859,14 +903,14 @@ fn make_return_push_array(push_value: Expression) -> Statement { /// /// Translates to: /// ```noir -/// `context.return_values.push_array({push_value}.serialize())` +/// `context.return_values.extend_from_array({push_value}.serialize())` fn make_struct_return_type(expression: Expression) -> Statement { let serialized_call = method_call( expression, // variable "serialize", // method name vec![], // args ); - make_return_push_array(serialized_call) + make_return_extend_from_array(serialized_call) } /// Make array return type diff --git a/noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs b/noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs index 217ea3f75847..f1a639de2112 100644 --- a/noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs +++ b/noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs @@ -400,8 +400,8 @@ impl AcirContext { // Operands are booleans. // // a ^ b == a + b - 2*a*b - let sum = self.add_var(lhs, rhs)?; let prod = self.mul_var(lhs, rhs)?; + let sum = self.add_var(lhs, rhs)?; self.add_mul_var(sum, -FieldElement::from(2_i128), prod) } else { let inputs = vec![AcirValue::Var(lhs, typ.clone()), AcirValue::Var(rhs, typ)]; @@ -461,8 +461,8 @@ impl AcirContext { if bit_size == 1 { // Operands are booleans // a + b - ab - let sum = self.add_var(lhs, rhs)?; let mul = self.mul_var(lhs, rhs)?; + let sum = self.add_var(lhs, rhs)?; self.sub_var(sum, mul) } else { // Implement OR in terms of AND @@ -1441,20 +1441,22 @@ impl AcirContext { inputs: Vec, outputs: Vec, attempt_execution: bool, - ) -> Result, InternalError> { - let b_inputs = try_vecmap(inputs, |i| match i { - AcirValue::Var(var, _) => Ok(BrilligInputs::Single(self.var_to_expression(var)?)), - AcirValue::Array(vars) => { - let mut var_expressions: Vec = Vec::new(); - for var in vars { - self.brillig_array_input(&mut var_expressions, var)?; + ) -> Result, RuntimeError> { + let b_inputs = try_vecmap(inputs, |i| -> Result<_, InternalError> { + match i { + AcirValue::Var(var, _) => Ok(BrilligInputs::Single(self.var_to_expression(var)?)), + AcirValue::Array(vars) => { + let mut var_expressions: Vec = Vec::new(); + for var in vars { + self.brillig_array_input(&mut var_expressions, var)?; + } + Ok(BrilligInputs::Array(var_expressions)) + } + AcirValue::DynamicArray(_) => { + let mut var_expressions = Vec::new(); + self.brillig_array_input(&mut var_expressions, i)?; + Ok(BrilligInputs::Array(var_expressions)) } - Ok(BrilligInputs::Array(var_expressions)) - } - AcirValue::DynamicArray(_) => { - let mut var_expressions = Vec::new(); - self.brillig_array_input(&mut var_expressions, i)?; - Ok(BrilligInputs::Array(var_expressions)) } })?; @@ -1489,6 +1491,34 @@ impl AcirContext { let predicate = self.var_to_expression(predicate)?; self.acir_ir.brillig(Some(predicate), generated_brillig, b_inputs, b_outputs); + fn range_constraint_value( + context: &mut AcirContext, + value: &AcirValue, + ) -> Result<(), RuntimeError> { + match value { + AcirValue::Var(var, typ) => { + let numeric_type = match typ { + AcirType::NumericType(numeric_type) => numeric_type, + _ => unreachable!("`AcirValue::Var` may only hold primitive values"), + }; + context.range_constrain_var(*var, numeric_type, None)?; + } + AcirValue::Array(values) => { + for value in values { + range_constraint_value(context, value)?; + } + } + AcirValue::DynamicArray(_) => { + unreachable!("Brillig opcodes cannot return dynamic arrays") + } + } + Ok(()) + } + + for output_var in &outputs_var { + range_constraint_value(self, output_var)?; + } + Ok(outputs_var) } diff --git a/noir/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs b/noir/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs index a6ab6b1d825b..f7441750fc8a 100644 --- a/noir/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs +++ b/noir/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs @@ -14,7 +14,7 @@ use crate::hir::resolution::{ use crate::hir::type_check::{type_check_func, TypeCheckError, TypeChecker}; use crate::hir::Context; -use crate::macros_api::MacroProcessor; +use crate::macros_api::{MacroError, MacroProcessor}; use crate::node_interner::{FuncId, NodeInterner, StmtId, StructId, TraitId, TypeAliasId}; use crate::parser::{ParserError, SortedModule}; @@ -155,6 +155,12 @@ impl From for CustomDiagnostic { } } +impl From for CompilationError { + fn from(value: MacroError) -> Self { + CompilationError::DefinitionError(DefCollectorErrorKind::MacroError(value)) + } +} + impl From for CompilationError { fn from(value: ParserError) -> Self { CompilationError::ParseError(value) @@ -359,7 +365,11 @@ impl DefCollector { errors.extend(resolved_globals.errors); for macro_processor in macro_processors { - macro_processor.process_typed_ast(&crate_id, context); + macro_processor.process_typed_ast(&crate_id, context).unwrap_or_else( + |(macro_err, file_id)| { + errors.push((macro_err.into(), file_id)); + }, + ); } errors.extend(type_check_globals(&mut context.def_interner, resolved_globals.globals)); diff --git a/noir/compiler/noirc_frontend/src/hir/resolution/errors.rs b/noir/compiler/noirc_frontend/src/hir/resolution/errors.rs index 390807afd173..7bd4de77e842 100644 --- a/noir/compiler/noirc_frontend/src/hir/resolution/errors.rs +++ b/noir/compiler/noirc_frontend/src/hir/resolution/errors.rs @@ -84,6 +84,8 @@ pub enum ResolverError { InvalidTypeForEntryPoint { span: Span }, #[error("Nested slices are not supported")] NestedSlices { span: Span }, + #[error("Usage of the `#[foreign]` or `#[builtin]` function attributes are not allowed outside of the Noir standard library")] + LowLevelFunctionOutsideOfStdlib { ident: Ident }, } impl ResolverError { @@ -311,6 +313,11 @@ impl From for Diagnostic { "Try to use a constant sized array instead".into(), span, ), + ResolverError::LowLevelFunctionOutsideOfStdlib { ident } => Diagnostic::simple_error( + "Definition of low-level function outside of standard library".into(), + "Usage of the `#[foreign]` or `#[builtin]` function attributes are not allowed outside of the Noir standard library".into(), + ident.span(), + ), } } } diff --git a/noir/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/noir/compiler/noirc_frontend/src/hir/resolution/resolver.rs index df533f6a4ae9..8243b684c8a2 100644 --- a/noir/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/noir/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -191,10 +191,18 @@ impl<'a> Resolver<'a> { self.add_generics(&func.def.generics); self.trait_bounds = func.def.where_clause.clone(); + let is_low_level_or_oracle = func + .attributes() + .function + .as_ref() + .map_or(false, |func| func.is_low_level() || func.is_oracle()); let (hir_func, func_meta) = self.intern_function(func, func_id); let func_scope_tree = self.scopes.end_function(); - self.check_for_unused_variables_in_scope_tree(func_scope_tree); + // The arguments to low-level and oracle functions are always unused so we do not produce warnings for them. + if !is_low_level_or_oracle { + self.check_for_unused_variables_in_scope_tree(func_scope_tree); + } self.trait_bounds.clear(); (hir_func, func_meta, self.errors) @@ -900,6 +908,13 @@ impl<'a> Resolver<'a> { position: PubPosition::ReturnType, }); } + let is_low_level_function = + func.attributes().function.as_ref().map_or(false, |func| func.is_low_level()); + if !self.path_resolver.module_id().krate.is_stdlib() && is_low_level_function { + let error = + ResolverError::LowLevelFunctionOutsideOfStdlib { ident: func.name_ident().clone() }; + self.push_err(error); + } // 'pub' is required on return types for entry point functions if self.is_entry_point_function(func) diff --git a/noir/compiler/noirc_frontend/src/lexer/token.rs b/noir/compiler/noirc_frontend/src/lexer/token.rs index ab131ccd8803..835a0baae3f4 100644 --- a/noir/compiler/noirc_frontend/src/lexer/token.rs +++ b/noir/compiler/noirc_frontend/src/lexer/token.rs @@ -562,6 +562,10 @@ impl FunctionAttribute { matches!(self, FunctionAttribute::Foreign(_)) } + pub fn is_oracle(&self) -> bool { + matches!(self, FunctionAttribute::Oracle(_)) + } + pub fn is_low_level(&self) -> bool { matches!(self, FunctionAttribute::Foreign(_) | FunctionAttribute::Builtin(_)) } diff --git a/noir/compiler/noirc_frontend/src/lib.rs b/noir/compiler/noirc_frontend/src/lib.rs index 9582b80dcba4..b6d4c5683345 100644 --- a/noir/compiler/noirc_frontend/src/lib.rs +++ b/noir/compiler/noirc_frontend/src/lib.rs @@ -75,6 +75,10 @@ pub mod macros_api { ) -> Result; /// Function to manipulate the AST after type checking has been completed. /// The AST after type checking has been done is called the HIR. - fn process_typed_ast(&self, crate_id: &CrateId, context: &mut HirContext); + fn process_typed_ast( + &self, + crate_id: &CrateId, + context: &mut HirContext, + ) -> Result<(), (MacroError, FileId)>; } } diff --git a/noir/compiler/noirc_frontend/src/tests.rs b/noir/compiler/noirc_frontend/src/tests.rs index 9ccbddab9eca..a4246a9fe7d8 100644 --- a/noir/compiler/noirc_frontend/src/tests.rs +++ b/noir/compiler/noirc_frontend/src/tests.rs @@ -52,10 +52,12 @@ mod test { ) -> (ParsedModule, Context, Vec<(CompilationError, FileId)>) { let root = std::path::Path::new("/"); let fm = FileManager::new(root); + let mut context = Context::new(fm, Default::default()); context.def_interner.populate_dummy_operator_traits(); let root_file_id = FileId::dummy(); let root_crate_id = context.crate_graph.add_crate_root(root_file_id); + let (program, parser_errors) = parse_program(src); let mut errors = vecmap(parser_errors, |e| (e.into(), root_file_id)); remove_experimental_warnings(&mut errors); diff --git a/noir/docs/docs/explainers/explainer-recursion.md b/noir/docs/docs/explainers/explainer-recursion.md index 8f992ec29fdc..18846176ca74 100644 --- a/noir/docs/docs/explainers/explainer-recursion.md +++ b/noir/docs/docs/explainers/explainer-recursion.md @@ -16,12 +16,13 @@ keywords: "Optimizing Computational Resources", "Improving Efficiency", "Verification Key", - "Aggregation Objects", + "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: @@ -64,7 +65,7 @@ So, they use zero-knowledge proofs. Alice tries to guess Bob's number, and Bob w 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?". +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. @@ -116,25 +117,19 @@ As you can see in the [recursion reference](noir/standard_library/recursion.md), - 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 -- The input aggregation object -It also returns the `output aggregation object`. These aggregation objects can be confusing at times, so let's dive in a little bit. - -### Aggregation objects +:::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. -In the case of PLONK the recursive aggregation object is two G1 points (expressed as 16 witness values). The final verifier (in our case this is most often the smart contract verifier) has to be aware of this aggregation object to execute a pairing and check the validity of these points. - 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 is verifying a proof, so it needs to output an `aggregation object`: he is generating a recursive proof! -- Alice verifies Bob's *recursive proof*, and uses Bob's `output aggregation object` as the `input aggregation object` in her proof... Which in turn, generates another `output aggregation object`. +- 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. -One should notice that when Bob generates his first proof, he has no input aggregation object. Because he is not verifying an recursive proof, he has no `input aggregation object`. In this case, he may use zeros instead. +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. -We can imagine the `aggregation object` as the baton in 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 @@ -175,3 +170,7 @@ In this example, a regulator could verify that taxes were paid for a specific pu 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/noir/docs/docs/how_to/how-to-recursion.md b/noir/docs/docs/how_to/how-to-recursion.md index 39db23f1f3a7..f34647a99d5e 100644 --- a/noir/docs/docs/how_to/how-to-recursion.md +++ b/noir/docs/docs/how_to/how-to-recursion.md @@ -108,11 +108,7 @@ This call takes the public inputs and the proof, but also the public inputs coun :::info -The `proofAsFields` has a constant size `[Field; 93]`. However, currently the backend doesn't remove the public inputs from the proof when converting it. - -This means that if your `main` circuit has two public inputs, then you should also modify the recursive circuit to accept a proof with the public inputs appended. This means that in our example, since `y` is a public input, our `proofAsFields` is of type `[Field; 94]`. - -Verification keys in Barretenberg are always of size 114. +The `proofAsFields` has a constant size `[Field; 93]` and verification keys in Barretenberg are always `[Field; 114]`. ::: @@ -136,7 +132,6 @@ const recursiveInputs = { 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, - input_aggregation_object: Array(16).fill(0) // this circuit is verifying a non-recursive proof, so there's no input aggregation object: just use zero } const { witness, returnValue } = noir.execute(recursiveInputs) // we're executing the recursive circuit now! @@ -144,7 +139,7 @@ const { proof, publicInputs } = backend.generateFinalProof(witness) const verified = backend.verifyFinalProof({ 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! In that case, you should keep in mind the `returnValue`, as it will contain the `input_aggregation_object` for the next proof. +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 @@ -152,16 +147,16 @@ Managing circuits and "who does what" can be confusing. To make sure your naming ```js const circuits = { -main: mainJSON, -recursive: recursiveJSON + main: mainJSON, + recursive: recursiveJSON } const backends = { -main: new BarretenbergBackend(circuits.main), -recursive: new BarretenbergBackend(circuits.recursive) + main: new BarretenbergBackend(circuits.main), + recursive: new BarretenbergBackend(circuits.recursive) } const noir_programs = { -main: new Noir(circuits.main, backends.main), -recursive: new Noir(circuits.recursive, backends.recursive) + main: new Noir(circuits.main, backends.main), + recursive: new Noir(circuits.recursive, backends.recursive) } ``` diff --git a/noir/docs/docs/noir/concepts/data_types/fields.md b/noir/docs/docs/noir/concepts/data_types/fields.md index a1c67945d666..7870c98c858f 100644 --- a/noir/docs/docs/noir/concepts/data_types/fields.md +++ b/noir/docs/docs/noir/concepts/data_types/fields.md @@ -157,6 +157,23 @@ fn main() { } ``` +### 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. diff --git a/noir/docs/docs/noir/standard_library/recursion.md b/noir/docs/docs/noir/standard_library/recursion.md index 67962082a8f1..f252150c8b54 100644 --- a/noir/docs/docs/noir/standard_library/recursion.md +++ b/noir/docs/docs/noir/standard_library/recursion.md @@ -1,16 +1,16 @@ --- title: Recursive Proofs description: Learn about how to write recursive proofs in Noir. -keywords: [recursion, recursive proofs, verification_key, aggregation object, verify_proof] +keywords: [recursion, recursive proofs, verification_key, verify_proof] --- Noir supports recursively verifying proofs, meaning you verify the proof of a Noir program in another Noir program. This enables creating proofs of arbitrary size by doing step-wise verification of smaller components of a large proof. -The `verify_proof` function takes a verification key, proof and public inputs for a zk program, as well as a key hash and an input aggregation object. The key hash is used to check the validity of the verification key and the input aggregation object is required by some proving systems. The `verify_proof` function returns an output aggregation object that can then be fed into future iterations of the proof verification if required. +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) ```rust #[foreign(verify_proof)] -fn verify_proof(_verification_key : [Field], _proof : [Field], _public_input : Field, _key_hash : Field, _input_aggregation_object : [Field]) -> [Field] {} +fn verify_proof(_verification_key : [Field], _proof : [Field], _public_input : Field, _key_hash : Field) {} ``` :::info @@ -26,36 +26,29 @@ use dep::std; fn main( verification_key : [Field; 114], - proof : [Field; 94], + proof : [Field; 93], public_inputs : [Field; 1], key_hash : Field, - input_aggregation_object : [Field; 16], - proof_b : [Field; 94], -) -> pub [Field; 16] { - let output_aggregation_object_a = std::verify_proof( + proof_b : [Field; 93], +) { + std::verify_proof( verification_key.as_slice(), proof.as_slice(), public_inputs.as_slice(), - key_hash, - input_aggregation_object + key_hash ); - let output_aggregation_object = std::verify_proof( + std::verify_proof( verification_key.as_slice(), proof_b.as_slice(), public_inputs.as_slice(), - key_hash, - output_aggregation_object_a + key_hash ); - - let mut output = [0; 16]; - for i in 0..16 { - output[i] = output_aggregation_object[i]; - } - output } ``` +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` @@ -68,23 +61,8 @@ The proof for the zk program that is being verified. ### `public_inputs` -These represent the public inputs of the proof we are verifying. They should be checked against in the circuit after construction of a new aggregation state. +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. - -### `input_aggregation_object` - -An aggregation object is blob of data that the top-level verifier must run some proof system specific algorithm on to complete verification. The size is proof system specific and will be set by the backend integrating this opcode. The input aggregation object is only not `None` when we are verifying a previous recursive aggregation in the current circuit. If this is the first recursive aggregation there is no input aggregation object. It is left to the backend to determine how to handle when there is no input aggregation object. - -## Return value - -### `output_aggregation_object` - -This is the result of a recursive aggregation and is what will be fed into the next verifier. -The next verifier can either perform a final verification (returning true or false) or perform another recursive aggregation where this output aggregation object will be the input aggregation object of the next recursive aggregation. - -## Example - -You can see an example of how to do recursive proofs in [this example recursion demo repo](https://github.com/noir-lang/noir-examples/tree/master/recursion). diff --git a/noir/docs/docs/reference/NoirJS/backend_barretenberg/.nojekyll b/noir/docs/docs/reference/NoirJS/backend_barretenberg/.nojekyll deleted file mode 100644 index e2ac6616addc..000000000000 --- a/noir/docs/docs/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/noir/docs/docs/reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend.md b/noir/docs/docs/reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend.md deleted file mode 100644 index af4a7558cdf6..000000000000 --- a/noir/docs/docs/reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend.md +++ /dev/null @@ -1,202 +0,0 @@ -# BarretenbergBackend - -## Implements - -- [`Backend`](../interfaces/Backend.md) - -## Constructors - -### new BarretenbergBackend(acirCircuit, options) - -```ts -new BarretenbergBackend(acirCircuit, options): BarretenbergBackend -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `acirCircuit` | [`CompiledCircuit`](../type-aliases/CompiledCircuit.md) | -| `options` | [`BackendOptions`](../type-aliases/BackendOptions.md) | - -#### Returns - -[`BarretenbergBackend`](BarretenbergBackend.md) - -## Methods - -### destroy() - -```ts -destroy(): Promise -``` - -#### Returns - -`Promise`\<`void`\> - -#### Implementation of - -[`Backend`](../interfaces/Backend.md).[`destroy`](../interfaces/Backend.md#destroy) - -#### Description - -Destroys the backend - -*** - -### generateFinalProof() - -```ts -generateFinalProof(decompressedWitness): Promise -``` - -Generate a final proof. This is the proof for the circuit which will verify -intermediate proofs and or can be seen as the proof created for regular circuits. - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `decompressedWitness` | `Uint8Array` | - -#### Returns - -`Promise`\<[`ProofData`](../type-aliases/ProofData.md)\> - -#### Implementation of - -[`Backend`](../interfaces/Backend.md).[`generateFinalProof`](../interfaces/Backend.md#generatefinalproof) - -*** - -### generateIntermediateProof() - -```ts -generateIntermediateProof(witness): Promise -``` - -Generates an intermediate proof. This is the proof that can be verified -in another circuit. - -This is sometimes referred to as a recursive proof. -We avoid this terminology as the only property of this proof -that matters is the fact that it is easy to verify in another circuit. -We _could_ choose to verify this proof outside of a circuit just as easily. - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `witness` | `Uint8Array` | - -#### Returns - -`Promise`\<[`ProofData`](../type-aliases/ProofData.md)\> - -#### Implementation of - -[`Backend`](../interfaces/Backend.md).[`generateIntermediateProof`](../interfaces/Backend.md#generateintermediateproof) - -#### Example - -```typescript -const intermediateProof = await backend.generateIntermediateProof(witness); -``` - -*** - -### generateIntermediateProofArtifacts() - -```ts -generateIntermediateProofArtifacts(proofData, numOfPublicInputs): Promise -``` - -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 the `generateIntermediateProof` -method. - -The number of public inputs denotes how many public inputs are in the inner proof. - -#### Parameters - -| Parameter | Type | Default value | -| :------ | :------ | :------ | -| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | `undefined` | -| `numOfPublicInputs` | `number` | `0` | - -#### Returns - -`Promise`\<`object`\> - -#### Implementation of - -[`Backend`](../interfaces/Backend.md).[`generateIntermediateProofArtifacts`](../interfaces/Backend.md#generateintermediateproofartifacts) - -#### Example - -```typescript -const artifacts = await backend.generateIntermediateProofArtifacts(proof, numOfPublicInputs); -``` - -*** - -### verifyFinalProof() - -```ts -verifyFinalProof(proofData): Promise -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | - -#### Returns - -`Promise`\<`boolean`\> - -#### Implementation of - -[`Backend`](../interfaces/Backend.md).[`verifyFinalProof`](../interfaces/Backend.md#verifyfinalproof) - -#### Description - -Verifies a final proof - -*** - -### verifyIntermediateProof() - -```ts -verifyIntermediateProof(proofData): Promise -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | - -#### Returns - -`Promise`\<`boolean`\> - -#### Implementation of - -[`Backend`](../interfaces/Backend.md).[`verifyIntermediateProof`](../interfaces/Backend.md#verifyintermediateproof) - -#### Example - -```typescript -const isValidIntermediate = await backend.verifyIntermediateProof(proof); -``` - -*** - -Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/docs/reference/NoirJS/backend_barretenberg/index.md b/noir/docs/docs/reference/NoirJS/backend_barretenberg/index.md deleted file mode 100644 index e32501acb719..000000000000 --- a/noir/docs/docs/reference/NoirJS/backend_barretenberg/index.md +++ /dev/null @@ -1,46 +0,0 @@ -# backend_barretenberg - -## Exports - -### Classes - -| Class | Description | -| :------ | :------ | -| [BarretenbergBackend](classes/BarretenbergBackend.md) | - | - -### Interfaces - -| Interface | Description | -| :------ | :------ | -| [Backend](interfaces/Backend.md) | - | - -### Type Aliases - -| Type alias | Description | -| :------ | :------ | -| [BackendOptions](type-aliases/BackendOptions.md) | - | -| [CompiledCircuit](type-aliases/CompiledCircuit.md) | - | -| [ProofData](type-aliases/ProofData.md) | - | - -## Functions - -### publicInputsToWitnessMap() - -```ts -publicInputsToWitnessMap(publicInputs, abi): WitnessMap -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `publicInputs` | `string`[] | -| `abi` | `Abi` | - -#### Returns - -`WitnessMap` - -*** - -Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/docs/reference/NoirJS/backend_barretenberg/interfaces/Backend.md b/noir/docs/docs/reference/NoirJS/backend_barretenberg/interfaces/Backend.md deleted file mode 100644 index 3eb9645c8d27..000000000000 --- a/noir/docs/docs/reference/NoirJS/backend_barretenberg/interfaces/Backend.md +++ /dev/null @@ -1,132 +0,0 @@ -# Backend - -## Methods - -### destroy() - -```ts -destroy(): Promise -``` - -#### Returns - -`Promise`\<`void`\> - -#### Description - -Destroys the backend - -*** - -### generateFinalProof() - -```ts -generateFinalProof(decompressedWitness): Promise -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `decompressedWitness` | `Uint8Array` | - -#### Returns - -`Promise`\<[`ProofData`](../type-aliases/ProofData.md)\> - -#### Description - -Generates a final proof (not meant to be verified in another circuit) - -*** - -### generateIntermediateProof() - -```ts -generateIntermediateProof(decompressedWitness): Promise -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `decompressedWitness` | `Uint8Array` | - -#### Returns - -`Promise`\<[`ProofData`](../type-aliases/ProofData.md)\> - -#### Description - -Generates an intermediate proof (meant to be verified in another circuit) - -*** - -### generateIntermediateProofArtifacts() - -```ts -generateIntermediateProofArtifacts(proofData, numOfPublicInputs): Promise -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | -| `numOfPublicInputs` | `number` | - -#### Returns - -`Promise`\<`object`\> - -#### Description - -Retrieves the artifacts from a proof in the Field format - -*** - -### verifyFinalProof() - -```ts -verifyFinalProof(proofData): Promise -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | - -#### Returns - -`Promise`\<`boolean`\> - -#### Description - -Verifies a final proof - -*** - -### verifyIntermediateProof() - -```ts -verifyIntermediateProof(proofData): Promise -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | - -#### Returns - -`Promise`\<`boolean`\> - -#### Description - -Verifies an intermediate proof - -*** - -Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/docs/reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions.md b/noir/docs/docs/reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions.md deleted file mode 100644 index 266ade75d170..000000000000 --- a/noir/docs/docs/reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions.md +++ /dev/null @@ -1,19 +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 | -| :------ | :------ | :------ | -| `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/noir/docs/docs/reference/NoirJS/backend_barretenberg/type-aliases/CompiledCircuit.md b/noir/docs/docs/reference/NoirJS/backend_barretenberg/type-aliases/CompiledCircuit.md deleted file mode 100644 index 34e0dd042050..000000000000 --- a/noir/docs/docs/reference/NoirJS/backend_barretenberg/type-aliases/CompiledCircuit.md +++ /dev/null @@ -1,20 +0,0 @@ -# CompiledCircuit - -```ts -type CompiledCircuit: object; -``` - -## Description - -The representation of a compiled circuit - -## Type declaration - -| Member | Type | Description | -| :------ | :------ | :------ | -| `abi` | `Abi` | **Description**

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

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

An byte array representing the proof | -| `publicInputs` | `string`[] | **Description**

Public inputs of a proof | - -*** - -Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/docs/reference/NoirJS/backend_barretenberg/typedoc-sidebar.cjs b/noir/docs/docs/reference/NoirJS/backend_barretenberg/typedoc-sidebar.cjs deleted file mode 100644 index 2aaa55bccf67..000000000000 --- a/noir/docs/docs/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":"category","label":"Interfaces","items":[{"type":"doc","id":"reference/NoirJS/backend_barretenberg/interfaces/Backend","label":"Backend"}]},{"type":"category","label":"Type Aliases","items":[{"type":"doc","id":"reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions","label":"BackendOptions"},{"type":"doc","id":"reference/NoirJS/backend_barretenberg/type-aliases/CompiledCircuit","label":"CompiledCircuit"},{"type":"doc","id":"reference/NoirJS/backend_barretenberg/type-aliases/ProofData","label":"ProofData"}]}]}; -module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/noir/docs/docs/reference/NoirJS/noir_js/.nojekyll b/noir/docs/docs/reference/NoirJS/noir_js/.nojekyll deleted file mode 100644 index e2ac6616addc..000000000000 --- a/noir/docs/docs/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/noir/docs/docs/reference/NoirJS/noir_js/classes/Noir.md b/noir/docs/docs/reference/NoirJS/noir_js/classes/Noir.md deleted file mode 100644 index 34e20d996849..000000000000 --- a/noir/docs/docs/reference/NoirJS/noir_js/classes/Noir.md +++ /dev/null @@ -1,132 +0,0 @@ -# Noir - -## Constructors - -### new Noir(circuit, backend) - -```ts -new Noir(circuit, backend?): Noir -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `circuit` | [`CompiledCircuit`](../type-aliases/CompiledCircuit.md) | -| `backend`? | `Backend` | - -#### Returns - -[`Noir`](Noir.md) - -## Methods - -### destroy() - -```ts -destroy(): Promise -``` - -#### Returns - -`Promise`\<`void`\> - -#### Description - -Destroys the underlying backend instance. - -#### Example - -```typescript -await noir.destroy(); -``` - -*** - -### execute() - -```ts -execute(inputs, foreignCallHandler?): Promise -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `inputs` | [`InputMap`](../type-aliases/InputMap.md) | -| `foreignCallHandler`? | [`ForeignCallHandler`](../type-aliases/ForeignCallHandler.md) | - -#### Returns - -`Promise`\<`object`\> - -#### Description - -Allows to execute a circuit to get its witness and return value. - -#### Example - -```typescript -async execute(inputs) -``` - -*** - -### generateFinalProof() - -```ts -generateFinalProof(inputs, foreignCallHandler?): Promise -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `inputs` | [`InputMap`](../type-aliases/InputMap.md) | -| `foreignCallHandler`? | [`ForeignCallHandler`](../type-aliases/ForeignCallHandler.md) | - -#### Returns - -`Promise`\<[`ProofData`](../type-aliases/ProofData.md)\> - -#### Description - -Generates a witness and a proof given an object as input. - -#### Example - -```typescript -async generateFinalProof(input) -``` - -*** - -### verifyFinalProof() - -```ts -verifyFinalProof(proofData): Promise -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | - -#### Returns - -`Promise`\<`boolean`\> - -#### Description - -Instantiates the verification key and verifies a proof. - -#### Example - -```typescript -async verifyFinalProof(proof) -``` - -*** - -Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/docs/reference/NoirJS/noir_js/functions/and.md b/noir/docs/docs/reference/NoirJS/noir_js/functions/and.md deleted file mode 100644 index c783283e3965..000000000000 --- a/noir/docs/docs/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/noir/docs/docs/reference/NoirJS/noir_js/functions/blake2s256.md b/noir/docs/docs/reference/NoirJS/noir_js/functions/blake2s256.md deleted file mode 100644 index 7882d0da8d50..000000000000 --- a/noir/docs/docs/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/noir/docs/docs/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md b/noir/docs/docs/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md deleted file mode 100644 index 5e3cd53e9d36..000000000000 --- a/noir/docs/docs/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/noir/docs/docs/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md b/noir/docs/docs/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md deleted file mode 100644 index 0b20ff689575..000000000000 --- a/noir/docs/docs/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/noir/docs/docs/reference/NoirJS/noir_js/functions/keccak256.md b/noir/docs/docs/reference/NoirJS/noir_js/functions/keccak256.md deleted file mode 100644 index d10f155ce86f..000000000000 --- a/noir/docs/docs/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/noir/docs/docs/reference/NoirJS/noir_js/functions/sha256.md b/noir/docs/docs/reference/NoirJS/noir_js/functions/sha256.md deleted file mode 100644 index 6ba4ecac0229..000000000000 --- a/noir/docs/docs/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/noir/docs/docs/reference/NoirJS/noir_js/functions/xor.md b/noir/docs/docs/reference/NoirJS/noir_js/functions/xor.md deleted file mode 100644 index 8d762b895d30..000000000000 --- a/noir/docs/docs/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/noir/docs/docs/reference/NoirJS/noir_js/index.md b/noir/docs/docs/reference/NoirJS/noir_js/index.md deleted file mode 100644 index d600e21b2993..000000000000 --- a/noir/docs/docs/reference/NoirJS/noir_js/index.md +++ /dev/null @@ -1,37 +0,0 @@ -# noir_js - -## Exports - -### Classes - -| Class | Description | -| :------ | :------ | -| [Noir](classes/Noir.md) | - | - -### Type Aliases - -| Type alias | Description | -| :------ | :------ | -| [CompiledCircuit](type-aliases/CompiledCircuit.md) | - | -| [ForeignCallHandler](type-aliases/ForeignCallHandler.md) | A callback which performs an foreign call and returns the response. | -| [ForeignCallInput](type-aliases/ForeignCallInput.md) | - | -| [ForeignCallOutput](type-aliases/ForeignCallOutput.md) | - | -| [InputMap](type-aliases/InputMap.md) | - | -| [ProofData](type-aliases/ProofData.md) | - | -| [WitnessMap](type-aliases/WitnessMap.md) | - | - -### Functions - -| Function | Description | -| :------ | :------ | -| [and](functions/and.md) | Performs a bitwise AND operation between `lhs` and `rhs` | -| [blake2s256](functions/blake2s256.md) | Calculates the Blake2s256 hash of the input bytes | -| [ecdsa\_secp256k1\_verify](functions/ecdsa_secp256k1_verify.md) | 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` | - -*** - -Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/docs/reference/NoirJS/noir_js/type-aliases/CompiledCircuit.md b/noir/docs/docs/reference/NoirJS/noir_js/type-aliases/CompiledCircuit.md deleted file mode 100644 index 34e0dd042050..000000000000 --- a/noir/docs/docs/reference/NoirJS/noir_js/type-aliases/CompiledCircuit.md +++ /dev/null @@ -1,20 +0,0 @@ -# CompiledCircuit - -```ts -type CompiledCircuit: object; -``` - -## Description - -The representation of a compiled circuit - -## Type declaration - -| Member | Type | Description | -| :------ | :------ | :------ | -| `abi` | `Abi` | **Description**

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

The bytecode of the circuit | - -*** - -Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/docs/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md b/noir/docs/docs/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md deleted file mode 100644 index 812b8b164818..000000000000 --- a/noir/docs/docs/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/noir/docs/docs/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md b/noir/docs/docs/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md deleted file mode 100644 index dd95809186a2..000000000000 --- a/noir/docs/docs/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/noir/docs/docs/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md b/noir/docs/docs/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md deleted file mode 100644 index b71fb78a9469..000000000000 --- a/noir/docs/docs/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/noir/docs/docs/reference/NoirJS/noir_js/type-aliases/InputMap.md b/noir/docs/docs/reference/NoirJS/noir_js/type-aliases/InputMap.md deleted file mode 100644 index c714e999d937..000000000000 --- a/noir/docs/docs/reference/NoirJS/noir_js/type-aliases/InputMap.md +++ /dev/null @@ -1,13 +0,0 @@ -# InputMap - -```ts -type InputMap: object; -``` - -## Index signature - - \[`key`: `string`\]: `InputValue` - -*** - -Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/docs/reference/NoirJS/noir_js/type-aliases/ProofData.md b/noir/docs/docs/reference/NoirJS/noir_js/type-aliases/ProofData.md deleted file mode 100644 index 05cebbc4e948..000000000000 --- a/noir/docs/docs/reference/NoirJS/noir_js/type-aliases/ProofData.md +++ /dev/null @@ -1,20 +0,0 @@ -# ProofData - -```ts -type ProofData: object; -``` - -## Description - -The representation of a proof - -## Type declaration - -| Member | Type | Description | -| :------ | :------ | :------ | -| `proof` | `Uint8Array` | **Description**

An byte array representing the proof | -| `publicInputs` | `string`[] | **Description**

Public inputs of a proof | - -*** - -Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/docs/reference/NoirJS/noir_js/type-aliases/WitnessMap.md b/noir/docs/docs/reference/NoirJS/noir_js/type-aliases/WitnessMap.md deleted file mode 100644 index 258c46f9d0c9..000000000000 --- a/noir/docs/docs/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/noir/docs/docs/reference/NoirJS/noir_js/typedoc-sidebar.cjs b/noir/docs/docs/reference/NoirJS/noir_js/typedoc-sidebar.cjs deleted file mode 100644 index fe2629ddc9ff..000000000000 --- a/noir/docs/docs/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/CompiledCircuit","label":"CompiledCircuit"},{"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/InputMap","label":"InputMap"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ProofData","label":"ProofData"},{"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/noir/docs/docusaurus.config.ts b/noir/docs/docusaurus.config.ts index 4e0d053f61e6..d1d344ba6353 100644 --- a/noir/docs/docusaurus.config.ts +++ b/noir/docs/docusaurus.config.ts @@ -155,7 +155,7 @@ export default { entryPoints: ['../tooling/noir_js/src/index.ts'], tsconfig: '../tooling/noir_js/tsconfig.json', entryPointStrategy: 'resolve', - out: 'docs/reference/NoirJS/noir_js', + out: 'processed-docs/reference/NoirJS/noir_js', plugin: ['typedoc-plugin-markdown'], name: 'noir_js', disableSources: true, @@ -186,7 +186,7 @@ export default { entryPoints: ['../tooling/noir_js_backend_barretenberg/src/index.ts'], tsconfig: '../tooling/noir_js_backend_barretenberg/tsconfig.json', entryPointStrategy: 'resolve', - out: 'docs/reference/NoirJS/backend_barretenberg', + out: 'processed-docs/reference/NoirJS/backend_barretenberg', plugin: ['typedoc-plugin-markdown'], name: 'backend_barretenberg', disableSources: true, diff --git a/noir/docs/package.json b/noir/docs/package.json index 6c706e4f514e..71b624ff565d 100644 --- a/noir/docs/package.json +++ b/noir/docs/package.json @@ -7,7 +7,8 @@ "start": "yarn preprocess && docusaurus start", "build": "yarn preprocess && yarn version::stables && docusaurus build", "version::stables": "ts-node ./scripts/setStable.ts", - "serve": "serve build" + "serve": "serve build", + "version": "yarn preprocess && docusaurus docs:version" }, "dependencies": { "@docusaurus/core": "^3.0.1", diff --git a/noir/docs/versioned_docs/version-v0.23.0/explainers/explainer-recursion.md b/noir/docs/versioned_docs/version-v0.23.0/explainers/explainer-recursion.md index 8f992ec29fdc..18846176ca74 100644 --- a/noir/docs/versioned_docs/version-v0.23.0/explainers/explainer-recursion.md +++ b/noir/docs/versioned_docs/version-v0.23.0/explainers/explainer-recursion.md @@ -16,12 +16,13 @@ keywords: "Optimizing Computational Resources", "Improving Efficiency", "Verification Key", - "Aggregation Objects", + "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: @@ -64,7 +65,7 @@ So, they use zero-knowledge proofs. Alice tries to guess Bob's number, and Bob w 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?". +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. @@ -116,25 +117,19 @@ As you can see in the [recursion reference](noir/standard_library/recursion.md), - 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 -- The input aggregation object -It also returns the `output aggregation object`. These aggregation objects can be confusing at times, so let's dive in a little bit. - -### Aggregation objects +:::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. -In the case of PLONK the recursive aggregation object is two G1 points (expressed as 16 witness values). The final verifier (in our case this is most often the smart contract verifier) has to be aware of this aggregation object to execute a pairing and check the validity of these points. - 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 is verifying a proof, so it needs to output an `aggregation object`: he is generating a recursive proof! -- Alice verifies Bob's *recursive proof*, and uses Bob's `output aggregation object` as the `input aggregation object` in her proof... Which in turn, generates another `output aggregation object`. +- 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. -One should notice that when Bob generates his first proof, he has no input aggregation object. Because he is not verifying an recursive proof, he has no `input aggregation object`. In this case, he may use zeros instead. +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. -We can imagine the `aggregation object` as the baton in 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 @@ -175,3 +170,7 @@ In this example, a regulator could verify that taxes were paid for a specific pu 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/noir/docs/versioned_docs/version-v0.23.0/how_to/how-to-recursion.md b/noir/docs/versioned_docs/version-v0.23.0/how_to/how-to-recursion.md index 39db23f1f3a7..f34647a99d5e 100644 --- a/noir/docs/versioned_docs/version-v0.23.0/how_to/how-to-recursion.md +++ b/noir/docs/versioned_docs/version-v0.23.0/how_to/how-to-recursion.md @@ -108,11 +108,7 @@ This call takes the public inputs and the proof, but also the public inputs coun :::info -The `proofAsFields` has a constant size `[Field; 93]`. However, currently the backend doesn't remove the public inputs from the proof when converting it. - -This means that if your `main` circuit has two public inputs, then you should also modify the recursive circuit to accept a proof with the public inputs appended. This means that in our example, since `y` is a public input, our `proofAsFields` is of type `[Field; 94]`. - -Verification keys in Barretenberg are always of size 114. +The `proofAsFields` has a constant size `[Field; 93]` and verification keys in Barretenberg are always `[Field; 114]`. ::: @@ -136,7 +132,6 @@ const recursiveInputs = { 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, - input_aggregation_object: Array(16).fill(0) // this circuit is verifying a non-recursive proof, so there's no input aggregation object: just use zero } const { witness, returnValue } = noir.execute(recursiveInputs) // we're executing the recursive circuit now! @@ -144,7 +139,7 @@ const { proof, publicInputs } = backend.generateFinalProof(witness) const verified = backend.verifyFinalProof({ 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! In that case, you should keep in mind the `returnValue`, as it will contain the `input_aggregation_object` for the next proof. +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 @@ -152,16 +147,16 @@ Managing circuits and "who does what" can be confusing. To make sure your naming ```js const circuits = { -main: mainJSON, -recursive: recursiveJSON + main: mainJSON, + recursive: recursiveJSON } const backends = { -main: new BarretenbergBackend(circuits.main), -recursive: new BarretenbergBackend(circuits.recursive) + main: new BarretenbergBackend(circuits.main), + recursive: new BarretenbergBackend(circuits.recursive) } const noir_programs = { -main: new Noir(circuits.main, backends.main), -recursive: new Noir(circuits.recursive, backends.recursive) + main: new Noir(circuits.main, backends.main), + recursive: new Noir(circuits.recursive, backends.recursive) } ``` diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/recursion.md b/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/recursion.md index 67962082a8f1..f252150c8b54 100644 --- a/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/recursion.md +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/recursion.md @@ -1,16 +1,16 @@ --- title: Recursive Proofs description: Learn about how to write recursive proofs in Noir. -keywords: [recursion, recursive proofs, verification_key, aggregation object, verify_proof] +keywords: [recursion, recursive proofs, verification_key, verify_proof] --- Noir supports recursively verifying proofs, meaning you verify the proof of a Noir program in another Noir program. This enables creating proofs of arbitrary size by doing step-wise verification of smaller components of a large proof. -The `verify_proof` function takes a verification key, proof and public inputs for a zk program, as well as a key hash and an input aggregation object. The key hash is used to check the validity of the verification key and the input aggregation object is required by some proving systems. The `verify_proof` function returns an output aggregation object that can then be fed into future iterations of the proof verification if required. +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) ```rust #[foreign(verify_proof)] -fn verify_proof(_verification_key : [Field], _proof : [Field], _public_input : Field, _key_hash : Field, _input_aggregation_object : [Field]) -> [Field] {} +fn verify_proof(_verification_key : [Field], _proof : [Field], _public_input : Field, _key_hash : Field) {} ``` :::info @@ -26,36 +26,29 @@ use dep::std; fn main( verification_key : [Field; 114], - proof : [Field; 94], + proof : [Field; 93], public_inputs : [Field; 1], key_hash : Field, - input_aggregation_object : [Field; 16], - proof_b : [Field; 94], -) -> pub [Field; 16] { - let output_aggregation_object_a = std::verify_proof( + proof_b : [Field; 93], +) { + std::verify_proof( verification_key.as_slice(), proof.as_slice(), public_inputs.as_slice(), - key_hash, - input_aggregation_object + key_hash ); - let output_aggregation_object = std::verify_proof( + std::verify_proof( verification_key.as_slice(), proof_b.as_slice(), public_inputs.as_slice(), - key_hash, - output_aggregation_object_a + key_hash ); - - let mut output = [0; 16]; - for i in 0..16 { - output[i] = output_aggregation_object[i]; - } - output } ``` +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` @@ -68,23 +61,8 @@ The proof for the zk program that is being verified. ### `public_inputs` -These represent the public inputs of the proof we are verifying. They should be checked against in the circuit after construction of a new aggregation state. +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. - -### `input_aggregation_object` - -An aggregation object is blob of data that the top-level verifier must run some proof system specific algorithm on to complete verification. The size is proof system specific and will be set by the backend integrating this opcode. The input aggregation object is only not `None` when we are verifying a previous recursive aggregation in the current circuit. If this is the first recursive aggregation there is no input aggregation object. It is left to the backend to determine how to handle when there is no input aggregation object. - -## Return value - -### `output_aggregation_object` - -This is the result of a recursive aggregation and is what will be fed into the next verifier. -The next verifier can either perform a final verification (returning true or false) or perform another recursive aggregation where this output aggregation object will be the input aggregation object of the next recursive aggregation. - -## Example - -You can see an example of how to do recursive proofs in [this example recursion demo repo](https://github.com/noir-lang/noir-examples/tree/master/recursion). diff --git a/noir/noir_stdlib/src/array.nr b/noir/noir_stdlib/src/array.nr index bcdf56dd7aa5..87cf4167dac6 100644 --- a/noir/noir_stdlib/src/array.nr +++ b/noir/noir_stdlib/src/array.nr @@ -3,10 +3,10 @@ // by the methods in the `slice` module impl [T; N] { #[builtin(array_len)] - pub fn len(_self: Self) -> Field {} + pub fn len(self) -> Field {} #[builtin(arraysort)] - pub fn sort(_self: Self) -> Self {} + pub fn sort(self) -> Self {} // Sort with a custom sorting function. pub fn sort_via(mut a: Self, ordering: fn[Env](T, T) -> bool) -> Self { diff --git a/noir/noir_stdlib/src/bigint.nr b/noir/noir_stdlib/src/bigint.nr index f3ecfba8343a..9edd59359c1f 100644 --- a/noir/noir_stdlib/src/bigint.nr +++ b/noir/noir_stdlib/src/bigint.nr @@ -1,4 +1,3 @@ - use crate::ops::{Add, Sub, Mul, Div, Rem,}; struct BigInt { diff --git a/noir/noir_stdlib/src/collections.nr b/noir/noir_stdlib/src/collections.nr index e06c662e658d..177ca96816f3 100644 --- a/noir/noir_stdlib/src/collections.nr +++ b/noir/noir_stdlib/src/collections.nr @@ -1 +1,2 @@ mod vec; +mod bounded_vec; diff --git a/noir/noir_stdlib/src/collections/bounded_vec.nr b/noir/noir_stdlib/src/collections/bounded_vec.nr new file mode 100644 index 000000000000..332fefa63f9f --- /dev/null +++ b/noir/noir_stdlib/src/collections/bounded_vec.nr @@ -0,0 +1,88 @@ +struct BoundedVec { + storage: [T; MaxLen], + // TODO: change this to return a u64 as Noir now + // uses u64 for indexing + len: Field, + empty_value: T, +} + +impl BoundedVec { + pub fn new(initial_value: T) -> Self { + BoundedVec { storage: [initial_value; MaxLen], len: 0, empty_value: initial_value } + } + + pub fn get(mut self: Self, index: Field) -> T { + assert(index as u64 < self.len as u64); + self.storage[index] + } + + pub fn get_unchecked(mut self: Self, index: Field) -> T { + self.storage[index] + } + + pub fn push(&mut self, elem: T) { + assert(self.len as u64 < MaxLen as u64, "push out of bounds"); + + self.storage[self.len] = elem; + self.len += 1; + } + + pub fn len(self) -> Field { + self.len + } + + pub fn max_len(_self: BoundedVec) -> Field { + MaxLen + } + + // This is a intermediate method, while we don't have an + // .extend method + pub fn storage(self) -> [T; MaxLen] { + self.storage + } + + pub fn extend_from_array(&mut self, array: [T; Len]) { + let new_len = self.len + array.len(); + assert(new_len as u64 <= MaxLen as u64, "extend_from_array out of bounds"); + for i in 0..array.len() { + self.storage[self.len + i] = array[i]; + } + self.len = new_len; + } + + pub fn extend_from_bounded_vec(&mut self, vec: BoundedVec) { + let append_len = vec.len(); + let new_len = self.len + append_len; + assert(new_len as u64 <= MaxLen as u64, "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 { + self.storage[self.len + (i as Field)] = vec.get_unchecked(i as Field); + } + } + self.len = new_len; + } + + pub fn pop(&mut self) -> T { + assert(self.len as u64 > 0); + self.len -= 1; + + let elem = self.storage[self.len]; + self.storage[self.len] = self.empty_value; + elem + } + + 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) { + ret |= predicate(self.storage[i]); + } + } + ret + } +} \ No newline at end of file diff --git a/noir/noir_stdlib/src/ecdsa_secp256k1.nr b/noir/noir_stdlib/src/ecdsa_secp256k1.nr index 290ccba27e58..e8d9af2230f7 100644 --- a/noir/noir_stdlib/src/ecdsa_secp256k1.nr +++ b/noir/noir_stdlib/src/ecdsa_secp256k1.nr @@ -1,10 +1,10 @@ #[foreign(ecdsa_secp256k1)] // docs:start:ecdsa_secp256k1 pub fn verify_signature( - _public_key_x: [u8; 32], - _public_key_y: [u8; 32], - _signature: [u8; 64], - _message_hash: [u8; N] + public_key_x: [u8; 32], + public_key_y: [u8; 32], + signature: [u8; 64], + message_hash: [u8; N] ) -> bool // docs:end:ecdsa_secp256k1 -{} +{} \ No newline at end of file diff --git a/noir/noir_stdlib/src/ecdsa_secp256r1.nr b/noir/noir_stdlib/src/ecdsa_secp256r1.nr index 390f8ed39d2c..9fe932a2f3df 100644 --- a/noir/noir_stdlib/src/ecdsa_secp256r1.nr +++ b/noir/noir_stdlib/src/ecdsa_secp256r1.nr @@ -1,10 +1,10 @@ #[foreign(ecdsa_secp256r1)] // docs:start:ecdsa_secp256r1 pub fn verify_signature( - _public_key_x: [u8; 32], - _public_key_y: [u8; 32], - _signature: [u8; 64], - _message_hash: [u8; N] + public_key_x: [u8; 32], + public_key_y: [u8; 32], + signature: [u8; 64], + message_hash: [u8; N] ) -> bool // docs:end:ecdsa_secp256r1 -{} +{} \ No newline at end of file diff --git a/noir/noir_stdlib/src/field.nr b/noir/noir_stdlib/src/field.nr index fbd76a1e8a29..66fb50119f9a 100644 --- a/noir/noir_stdlib/src/field.nr +++ b/noir/noir_stdlib/src/field.nr @@ -13,13 +13,13 @@ impl Field { } #[builtin(to_le_bits)] - fn __to_le_bits(_self: Self, _bit_size: u32) -> [u1] {} + fn __to_le_bits(self, _bit_size: u32) -> [u1] {} #[builtin(to_be_bits)] - fn __to_be_bits(_self: Self, _bit_size: u32) -> [u1] {} + fn __to_be_bits(self, bit_size: u32) -> [u1] {} #[builtin(apply_range_constraint)] - fn __assert_max_bit_size(_self: Self, _bit_size: u32) {} + fn __assert_max_bit_size(self, bit_size: u32) {} pub fn assert_max_bit_size(self: Self, bit_size: u32) { crate::assert_constant(bit_size); @@ -53,10 +53,10 @@ impl Field { // decompose `_self` into a `_result_len` vector over the `_radix` basis // `_radix` must be less than 256 #[builtin(to_le_radix)] - fn __to_le_radix(_self: Self, _radix: u32, _result_len: u32) -> [u8] {} + fn __to_le_radix(self, radix: u32, result_len: u32) -> [u8] {} #[builtin(to_be_radix)] - fn __to_be_radix(_self: Self, _radix: u32, _result_len: u32) -> [u8] {} + fn __to_be_radix(self, radix: u32, result_len: u32) -> [u8] {} // Returns self to the power of the given exponent value. diff --git a/noir/noir_stdlib/src/hash.nr b/noir/noir_stdlib/src/hash.nr index 4033e2a53654..cc864039a90a 100644 --- a/noir/noir_stdlib/src/hash.nr +++ b/noir/noir_stdlib/src/hash.nr @@ -3,19 +3,19 @@ mod mimc; #[foreign(sha256)] // docs:start:sha256 -pub fn sha256(_input: [u8; N]) -> [u8; 32] +pub fn sha256(input: [u8; N]) -> [u8; 32] // docs:end:sha256 {} #[foreign(blake2s)] // docs:start:blake2s -pub fn blake2s(_input: [u8; N]) -> [u8; 32] +pub fn blake2s(input: [u8; N]) -> [u8; 32] // docs:end:blake2s {} #[foreign(blake3)] // docs:start:blake3 -pub fn blake3(_input: [u8; N]) -> [u8; 32] +pub fn blake3(input: [u8; N]) -> [u8; 32] // docs:end:blake3 {} @@ -32,7 +32,7 @@ pub fn pedersen_commitment(input: [Field; N]) -> PedersenPoint } #[foreign(pedersen_commitment)] -pub fn __pedersen_commitment_with_separator(_input: [Field; N], _separator: u32) -> [Field; 2] {} +pub fn __pedersen_commitment_with_separator(input: [Field; N], separator: u32) -> [Field; 2] {} pub fn pedersen_commitment_with_separator(input: [Field; N], separator: u32) -> PedersenPoint { let values = __pedersen_commitment_with_separator(input, separator); @@ -47,13 +47,13 @@ pub fn pedersen_hash(input: [Field; N]) -> Field } #[foreign(pedersen_hash)] -pub fn pedersen_hash_with_separator(_input: [Field; N], _separator: u32) -> Field {} +pub fn pedersen_hash_with_separator(input: [Field; N], separator: u32) -> Field {} -pub fn hash_to_field(_input: [Field; N]) -> Field { +pub fn hash_to_field(input: [Field; N]) -> Field { let mut inputs_as_bytes = []; for i in 0..N { - let input_bytes = _input[i].to_le_bytes(32); + let input_bytes = input[i].to_le_bytes(32); for i in 0..32 { inputs_as_bytes = inputs_as_bytes.push_back(input_bytes[i]); } @@ -65,7 +65,7 @@ pub fn hash_to_field(_input: [Field; N]) -> Field { #[foreign(keccak256)] // docs:start:keccak256 -pub fn keccak256(_input: [u8; N], _message_size: u32) -> [u8; 32] +pub fn keccak256(input: [u8; N], message_size: u32) -> [u8; 32] // docs:end:keccak256 {} diff --git a/noir/noir_stdlib/src/lib.nr b/noir/noir_stdlib/src/lib.nr index b7c7833277a6..5165d1ee07b1 100644 --- a/noir/noir_stdlib/src/lib.nr +++ b/noir/noir_stdlib/src/lib.nr @@ -25,12 +25,12 @@ mod ops; mod default; mod prelude; mod uint128; -mod bigint; +// mod bigint; // Oracle calls are required to be wrapped in an unconstrained function // Thus, the only argument to the `println` oracle is expected to always be an ident #[oracle(print)] -unconstrained fn print_oracle(_with_newline: bool, _input: T) {} +unconstrained fn print_oracle(with_newline: bool, input: T) {} unconstrained pub fn print(input: T) { print_oracle(false, input); @@ -41,20 +41,20 @@ unconstrained pub fn println(input: T) { } #[foreign(recursive_aggregation)] -pub fn verify_proof(_verification_key: [Field], _proof: [Field], _public_inputs: [Field], _key_hash: Field) {} +pub fn verify_proof(verification_key: [Field], proof: [Field], public_inputs: [Field], key_hash: Field) {} // Asserts that the given value is known at compile-time. // Useful for debugging for-loop bounds. #[builtin(assert_constant)] -pub fn assert_constant(_x: T) {} +pub fn assert_constant(x: T) {} // from_field and as_field are private since they are not valid for every type. // `as` should be the default for users to cast between primitive types, and in the future // traits can be used to work with generic types. #[builtin(from_field)] -fn from_field(_x: Field) -> T {} +fn from_field(x: Field) -> T {} #[builtin(as_field)] -fn as_field(_x: T) -> Field {} +fn as_field(x: T) -> Field {} pub fn wrapping_add(x: T, y: T) -> T { crate::from_field(crate::as_field(x) + crate::as_field(y)) diff --git a/noir/noir_stdlib/src/prelude.nr b/noir/noir_stdlib/src/prelude.nr index 26c6a805d542..3244329aa4b4 100644 --- a/noir/noir_stdlib/src/prelude.nr +++ b/noir/noir_stdlib/src/prelude.nr @@ -1,4 +1,5 @@ use crate::collections::vec::Vec; +use crate::collections::bounded_vec::BoundedVec; use crate::option::Option; use crate::{print, println, assert_constant}; use crate::uint128::U128; diff --git a/noir/noir_stdlib/src/scalar_mul.nr b/noir/noir_stdlib/src/scalar_mul.nr index fef2398222f1..26378e4839ae 100644 --- a/noir/noir_stdlib/src/scalar_mul.nr +++ b/noir/noir_stdlib/src/scalar_mul.nr @@ -1,8 +1,22 @@ +use crate::ops::Add; + struct EmbeddedCurvePoint { x: Field, y: Field, } +impl EmbeddedCurvePoint { + fn double(self) -> EmbeddedCurvePoint { + embedded_curve_add(self, self) + } +} + +impl Add for EmbeddedCurvePoint { + fn add(self, other: EmbeddedCurvePoint) -> EmbeddedCurvePoint { + embedded_curve_add(self, other) + } +} + // Computes a fixed base scalar multiplication over the embedded curve. // For bn254, We have Grumpkin and Baby JubJub. // For bls12-381, we have JubJub and Bandersnatch. @@ -11,12 +25,12 @@ struct EmbeddedCurvePoint { // underlying proof system. #[foreign(fixed_base_scalar_mul)] // docs:start:fixed_base_embedded_curve -pub fn fixed_base_embedded_curve(_low: Field, _high: Field) -> [Field; 2] {} +pub fn fixed_base_embedded_curve( + low: Field, + high: Field +) -> [Field; 2] // docs:end:fixed_base_embedded_curve +{} #[foreign(embedded_curve_add)] -pub fn embedded_curve_add(_point1: EmbeddedCurvePoint, _point2: EmbeddedCurvePoint) -> EmbeddedCurvePoint {} - -pub fn embedded_curve_double(point: EmbeddedCurvePoint) -> EmbeddedCurvePoint { - embedded_curve_add(point, point) -} +fn embedded_curve_add(_point1: EmbeddedCurvePoint, _point2: EmbeddedCurvePoint) -> EmbeddedCurvePoint {} diff --git a/noir/noir_stdlib/src/schnorr.nr b/noir/noir_stdlib/src/schnorr.nr index 025c3a0f9215..33656254550e 100644 --- a/noir/noir_stdlib/src/schnorr.nr +++ b/noir/noir_stdlib/src/schnorr.nr @@ -1,10 +1,10 @@ #[foreign(schnorr_verify)] // docs:start:schnorr_verify pub fn verify_signature( - _public_key_x: Field, - _public_key_y: Field, - _signature: [u8; 64], - _message: [u8; N] + public_key_x: Field, + public_key_y: Field, + signature: [u8; 64], + message: [u8; N] ) -> bool // docs:end:schnorr_verify -{} +{} \ No newline at end of file diff --git a/noir/noir_stdlib/src/slice.nr b/noir/noir_stdlib/src/slice.nr index a5a9a38ed539..aa4b73edc1a3 100644 --- a/noir/noir_stdlib/src/slice.nr +++ b/noir/noir_stdlib/src/slice.nr @@ -3,34 +3,34 @@ impl [T] { /// new slice with a length one greater than the /// original unmodified slice. #[builtin(slice_push_back)] - pub fn push_back(_self: Self, _elem: T) -> Self { } + pub fn push_back(self, elem: T) -> Self { } /// Push a new element to the front of the slice, returning a /// new slice with a length one greater than the /// original unmodified slice. #[builtin(slice_push_front)] - pub fn push_front(_self: Self, _elem: T) -> Self { } + pub fn push_front(self, elem: T) -> Self { } /// Remove the last element of the slice, returning the /// popped slice and the element in a tuple #[builtin(slice_pop_back)] - pub fn pop_back(_self: Self) -> (Self, T) { } + pub fn pop_back(self) -> (Self, T) { } /// Remove the first element of the slice, returning the /// element and the popped slice in a tuple #[builtin(slice_pop_front)] - pub fn pop_front(_self: Self) -> (T, Self) { } + pub fn pop_front(self) -> (T, Self) { } /// Insert an element at a specified index, shifting all elements /// after it to the right #[builtin(slice_insert)] - pub fn insert(_self: Self, _index: Field, _elem: T) -> Self { } + pub fn insert(self, index: Field, elem: T) -> Self { } /// Remove an element at a specified index, shifting all elements /// after it to the left, returning the altered slice and /// the removed element #[builtin(slice_remove)] - pub fn remove(_self: Self, _index: Field) -> (Self, T) { } + pub fn remove(self, index: Field) -> (Self, T) { } // Append each element of the `other` slice to the end of `self`. // This returns a new slice and leaves both input slices unchanged. diff --git a/noir/noir_stdlib/src/string.nr b/noir/noir_stdlib/src/string.nr index e402abf9ab61..ad6fd19e2dec 100644 --- a/noir/noir_stdlib/src/string.nr +++ b/noir/noir_stdlib/src/string.nr @@ -2,7 +2,7 @@ use crate::collections::vec::Vec; impl str { /// Converts the given string into a byte array #[builtin(str_as_bytes)] - pub fn as_bytes(_self: Self) -> [u8; N] { } + pub fn as_bytes(self) -> [u8; N] { } /// return a byte vector of the str content pub fn as_bytes_vec(self: Self) -> Vec { diff --git a/noir/noir_stdlib/src/test.nr b/noir/noir_stdlib/src/test.nr index 47b31f4acea0..560cfde741c2 100644 --- a/noir/noir_stdlib/src/test.nr +++ b/noir/noir_stdlib/src/test.nr @@ -1,17 +1,17 @@ #[oracle(create_mock)] -unconstrained fn create_mock_oracle(_name: str) -> Field {} +unconstrained fn create_mock_oracle(name: str) -> Field {} #[oracle(set_mock_params)] -unconstrained fn set_mock_params_oracle

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

(id: Field, params: P) {} #[oracle(set_mock_returns)] -unconstrained fn set_mock_returns_oracle(_id: Field, _returns: R) {} +unconstrained fn set_mock_returns_oracle(id: Field, returns: R) {} #[oracle(set_mock_times)] -unconstrained fn set_mock_times_oracle(_id: Field, _times: u64) {} +unconstrained fn set_mock_times_oracle(id: Field, times: u64) {} #[oracle(clear_mock)] -unconstrained fn clear_mock_oracle(_id: Field) {} +unconstrained fn clear_mock_oracle(id: Field) {} struct OracleMock { id: Field, diff --git a/noir/scripts/bootstrap_native.sh b/noir/scripts/bootstrap_native.sh index 3e0e2ed853ac..974f0edcfec0 100755 --- a/noir/scripts/bootstrap_native.sh +++ b/noir/scripts/bootstrap_native.sh @@ -12,6 +12,12 @@ else export GIT_COMMIT=$(git rev-parse --verify HEAD) fi +# Check if the 'cargo' command is available in the system +if ! command -v cargo > /dev/null; then + echo "Cargo is not installed. Please install Cargo and the Rust toolchain." + exit 1 +fi + # Build native. if [ -n "${DEBUG:-}" ]; then cargo build diff --git a/noir/test_programs/compile_failure/builtin_function_declaration/Nargo.toml b/noir/test_programs/compile_failure/builtin_function_declaration/Nargo.toml new file mode 100644 index 000000000000..3835292a6ba8 --- /dev/null +++ b/noir/test_programs/compile_failure/builtin_function_declaration/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "builtin_function_declaration" +type = "bin" +authors = [""] +compiler_version = ">=0.23.0" + +[dependencies] diff --git a/noir/test_programs/compile_failure/builtin_function_declaration/src/main.nr b/noir/test_programs/compile_failure/builtin_function_declaration/src/main.nr new file mode 100644 index 000000000000..ed376557371e --- /dev/null +++ b/noir/test_programs/compile_failure/builtin_function_declaration/src/main.nr @@ -0,0 +1,10 @@ +// This test prevents users from trying to create their own builtin functions as these should only exist in the stdlib. + +// This would otherwise be a perfectly valid declaration of the `to_le_bits` builtin function +#[builtin(to_le_bits)] +fn to_le_bits(_x: Field, _bit_size: u32) -> [u1] {} + +fn main(x: Field) -> pub u1 { + let bits = to_le_bits(x, 100); + bits[0] +} diff --git a/noir/test_programs/compile_failure/foreign_function_declaration/Nargo.toml b/noir/test_programs/compile_failure/foreign_function_declaration/Nargo.toml new file mode 100644 index 000000000000..951658d7fb89 --- /dev/null +++ b/noir/test_programs/compile_failure/foreign_function_declaration/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "foreign_function_declaration" +type = "bin" +authors = [""] +compiler_version = ">=0.23.0" + +[dependencies] diff --git a/noir/test_programs/compile_failure/foreign_function_declaration/src/main.nr b/noir/test_programs/compile_failure/foreign_function_declaration/src/main.nr new file mode 100644 index 000000000000..6273067f6a72 --- /dev/null +++ b/noir/test_programs/compile_failure/foreign_function_declaration/src/main.nr @@ -0,0 +1,10 @@ +// This test prevents users from trying to create their own blackbox functions as these should only exist in the stdlib. + +// This would otherwise be a perfectly valid definition of the `pedersen_hash` black box function, +// however executing the circuit results in an unhelpful ICE. +#[foreign(pedersen_hash)] +fn my_pedersen_hash(_input: [Field; N]) -> Field {} + +fn main() -> pub Field { + my_pedersen_hash([1]) +} diff --git a/noir/test_programs/execution_success/scalar_mul/src/main.nr b/noir/test_programs/execution_success/scalar_mul/src/main.nr index a88754da5d39..e20f47907db7 100644 --- a/noir/test_programs/execution_success/scalar_mul/src/main.nr +++ b/noir/test_programs/execution_success/scalar_mul/src/main.nr @@ -20,18 +20,12 @@ fn main( let res = std::scalar_mul::fixed_base_embedded_curve(priv_key, 0); assert(res[0] == pub_x); assert(res[1] == pub_y); - let pub_point= std::scalar_mul::EmbeddedCurvePoint { - x: pub_x, - y: pub_y - }; + let pub_point= std::scalar_mul::EmbeddedCurvePoint { x: pub_x, y: pub_y }; let g1_y = 17631683881184975370165255887551781615748388533673675138860; - let g1= std::scalar_mul::EmbeddedCurvePoint { - x: 1, - y: g1_y - }; + let g1= std::scalar_mul::EmbeddedCurvePoint { x: 1, y: g1_y }; - let res = std::scalar_mul::embedded_curve_double(pub_point); - let double = std::scalar_mul::embedded_curve_add(g1,g1 ); + let res = pub_point.double(); + let double = g1.add(g1); assert(double.x == res.x); } diff --git a/noir/test_programs/noir_test_success/bounded_vec/Nargo.toml b/noir/test_programs/noir_test_success/bounded_vec/Nargo.toml new file mode 100644 index 000000000000..0d58f5872ef6 --- /dev/null +++ b/noir/test_programs/noir_test_success/bounded_vec/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "bounded_vec" +type = "bin" +authors = [""] +compiler_version = ">=0.23.0" + +[dependencies] \ No newline at end of file diff --git a/noir/test_programs/noir_test_success/bounded_vec/Prover.toml b/noir/test_programs/noir_test_success/bounded_vec/Prover.toml new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/noir/test_programs/noir_test_success/bounded_vec/src/main.nr b/noir/test_programs/noir_test_success/bounded_vec/src/main.nr new file mode 100644 index 000000000000..d51d2cc3685c --- /dev/null +++ b/noir/test_programs/noir_test_success/bounded_vec/src/main.nr @@ -0,0 +1,105 @@ +#[test] +fn test_vec_push_pop() { + let mut vec: BoundedVec = BoundedVec::new(0); + assert(vec.len == 0); + vec.push(2); + assert(vec.len == 1); + vec.push(4); + assert(vec.len == 2); + vec.push(6); + assert(vec.len == 3); + let x = vec.pop(); + assert(x == 6); + assert(vec.len == 2); + assert(vec.get(0) == 2); + assert(vec.get(1) == 4); +} + +#[test] +fn test_vec_extend_from_array() { + let mut vec: BoundedVec = BoundedVec::new(0); + vec.extend_from_array([2, 4]); + assert(vec.len == 2); + assert(vec.get(0) == 2); + assert(vec.get(1) == 4); +} + +#[test(should_fail_with="extend_from_array out of bounds")] +fn test_vec_extend_from_array_out_of_bound() { + let mut vec: BoundedVec = BoundedVec::new(0); + vec.extend_from_array([2, 4, 6]); +} + +#[test(should_fail_with="extend_from_array out of bounds")] +fn test_vec_extend_from_array_twice_out_of_bound() { + let mut vec: BoundedVec = BoundedVec::new(0); + vec.extend_from_array([2]); + assert(vec.len == 1); + vec.extend_from_array([4, 6]); +} + +#[test(should_fail)] +fn test_vec_get_out_of_bound() { + let mut vec: BoundedVec = BoundedVec::new(0); + vec.extend_from_array([2, 4]); + let _x = vec.get(2); +} + +#[test(should_fail)] +fn test_vec_get_not_declared() { + let mut vec: BoundedVec = BoundedVec::new(0); + vec.extend_from_array([2]); + let _x = vec.get(1); +} + +#[test(should_fail)] +fn test_vec_get_uninitialized() { + let mut vec: BoundedVec = BoundedVec::new(0); + let _x = vec.get(0); +} + +#[test(should_fail_with="push out of bounds")] +fn test_vec_push_out_of_bound() { + let mut vec: BoundedVec = BoundedVec::new(0); + vec.push(1); + vec.push(2); +} + +#[test(should_fail_with="extend_from_bounded_vec out of bounds")] +fn test_vec_extend_from_bounded_vec_out_of_bound() { + let mut vec: BoundedVec = BoundedVec::new(0); + + let mut another_vec: BoundedVec = BoundedVec::new(0); + another_vec.extend_from_array([1, 2, 3]); + + vec.extend_from_bounded_vec(another_vec); +} + +#[test(should_fail_with="extend_from_bounded_vec out of bounds")] +fn test_vec_extend_from_bounded_vec_twice_out_of_bound() { + let mut vec: BoundedVec = BoundedVec::new(0); + vec.extend_from_array([1, 2]); + + let mut another_vec: BoundedVec = BoundedVec::new(0); + another_vec.push(3); + + vec.extend_from_bounded_vec(another_vec); +} + +#[test] +fn test_vec_any() { + let mut vec: BoundedVec = BoundedVec::new(0); + vec.extend_from_array([2, 4, 6]); + assert(vec.any(|v| v == 2) == true); + assert(vec.any(|v| v == 4) == true); + assert(vec.any(|v| v == 6) == true); + assert(vec.any(|v| v == 3) == false); +} + +#[test] +fn test_vec_any_not_default() { + let default_value = 1; + let mut vec: BoundedVec = BoundedVec::new(default_value); + vec.extend_from_array([2, 4]); + assert(vec.any(|v| v == default_value) == false); +} \ No newline at end of file diff --git a/noir/tooling/bb_abstraction_leaks/build.rs b/noir/tooling/bb_abstraction_leaks/build.rs index 18413f87793a..6197f52cb4b5 100644 --- a/noir/tooling/bb_abstraction_leaks/build.rs +++ b/noir/tooling/bb_abstraction_leaks/build.rs @@ -10,7 +10,7 @@ use const_format::formatcp; const USERNAME: &str = "AztecProtocol"; const REPO: &str = "aztec-packages"; -const VERSION: &str = "0.19.0"; +const VERSION: &str = "0.21.0"; const TAG: &str = formatcp!("aztec-packages-v{}", VERSION); const API_URL: &str = diff --git a/noir/tooling/noir_js_backend_barretenberg/package.json b/noir/tooling/noir_js_backend_barretenberg/package.json index cd2a6354ac4b..a0123883efd2 100644 --- a/noir/tooling/noir_js_backend_barretenberg/package.json +++ b/noir/tooling/noir_js_backend_barretenberg/package.json @@ -42,7 +42,7 @@ "lint": "NODE_NO_WARNINGS=1 eslint . --ext .ts --ignore-path ./.eslintignore --max-warnings 0" }, "dependencies": { - "@aztec/bb.js": "0.19.0", + "@aztec/bb.js": "0.21.0", "@noir-lang/types": "workspace:*", "fflate": "^0.8.0" }, diff --git a/noir/yarn.lock b/noir/yarn.lock index db3f493bc62d..743068f19071 100644 --- a/noir/yarn.lock +++ b/noir/yarn.lock @@ -235,9 +235,9 @@ __metadata: languageName: node linkType: hard -"@aztec/bb.js@npm:0.19.0": - version: 0.19.0 - resolution: "@aztec/bb.js@npm:0.19.0" +"@aztec/bb.js@npm:0.21.0": + version: 0.21.0 + resolution: "@aztec/bb.js@npm:0.21.0" dependencies: comlink: ^4.4.1 commander: ^10.0.1 @@ -245,7 +245,7 @@ __metadata: tslib: ^2.4.0 bin: bb.js: dest/node/main.js - checksum: c78c22c3b8c43e0010a43145f973489aa7da9fcf0b8527884107f1f34ac3ca5f5d4ab087d74ce50cb75d6dbaef88bfc693e23745282faa30b81dc78481c65874 + checksum: a0fb97476f52025f3c31b7a5e890966ac375ed47c5cfd3434f5c3e4265af3c7566a162f37d6c56f394f44bfe4ba67e5002b7c5998ecc4f6abe70e04f5b8abe34 languageName: node linkType: hard @@ -4435,7 +4435,7 @@ __metadata: version: 0.0.0-use.local resolution: "@noir-lang/backend_barretenberg@workspace:tooling/noir_js_backend_barretenberg" dependencies: - "@aztec/bb.js": 0.19.0 + "@aztec/bb.js": 0.21.0 "@noir-lang/types": "workspace:*" "@types/node": ^20.6.2 "@types/prettier": ^3 diff --git a/yarn-project/acir-simulator/src/avm/avm_memory_types.ts b/yarn-project/acir-simulator/src/avm/avm_memory_types.ts index 8963276813b6..40c02b65463f 100644 --- a/yarn-project/acir-simulator/src/avm/avm_memory_types.ts +++ b/yarn-project/acir-simulator/src/avm/avm_memory_types.ts @@ -220,11 +220,13 @@ export enum TypeTag { // TODO: Consider automatic conversion when getting undefined values. export class TaggedMemory { - static readonly MAX_MEMORY_SIZE = 1n << 32n; + // FIXME: memory should be 2^32, but TS doesn't allow for arrays that big. + static readonly MAX_MEMORY_SIZE = Number(1n << 31n); // 1n << 32n private _mem: MemoryValue[]; constructor() { - this._mem = []; + // Initialize memory size, but leave all entries undefined. + this._mem = new Array(TaggedMemory.MAX_MEMORY_SIZE); } public get(offset: number): MemoryValue { diff --git a/yarn-project/acir-simulator/src/avm/index.test.ts b/yarn-project/acir-simulator/src/avm/index.test.ts index 8fb3a49dab07..f085bc0bc008 100644 --- a/yarn-project/acir-simulator/src/avm/index.test.ts +++ b/yarn-project/acir-simulator/src/avm/index.test.ts @@ -1,4 +1,5 @@ import { Fr } from '@aztec/foundation/fields'; +import { AvmTestContractArtifact } from '@aztec/noir-contracts'; import { mock } from 'jest-mock-extended'; @@ -11,7 +12,7 @@ import { encodeToBytecode } from './opcodes/encode_to_bytecode.js'; import { Opcode } from './opcodes/opcodes.js'; describe('avm', () => { - it('Should execute bytecode', async () => { + it('Should execute bytecode that performs basic addition', async () => { const calldata: Fr[] = [new Fr(1), new Fr(2)]; const journal = mock(); @@ -38,4 +39,27 @@ describe('avm', () => { expect(returnData.length).toBe(1); expect(returnData).toEqual([new Fr(3)]); }); + + describe('testing transpiled Noir contracts', () => { + it('Should execute contract function that performs addition', async () => { + const calldata: Fr[] = [new Fr(1), new Fr(2)]; + const journal = mock(); + + // Get contract function artifact + const addArtifact = AvmTestContractArtifact.functions.find(f => f.name === 'avm_addArgsReturn')!; + + // Decode bytecode into instructions + const instructions = decodeBytecode(Buffer.from(addArtifact.bytecode, 'base64')); + + // Execute instructions + const context = new AvmMachineState(initExecutionEnvironment({ calldata })); + const avmReturnData = await executeAvm(context, journal, instructions); + + expect(avmReturnData.reverted).toBe(false); + + const returnData = avmReturnData.output; + expect(returnData.length).toBe(1); + expect(returnData).toEqual([new Fr(3)]); + }); + }); }); diff --git a/yarn-project/acir-simulator/src/client/client_execution_context.ts b/yarn-project/acir-simulator/src/client/client_execution_context.ts index 73f9d3467928..f3d684f4ef02 100644 --- a/yarn-project/acir-simulator/src/client/client_execution_context.ts +++ b/yarn-project/acir-simulator/src/client/client_execution_context.ts @@ -10,7 +10,7 @@ import { SideEffect, TxContext, } from '@aztec/circuits.js'; -import { computeUniqueCommitment, siloCommitment } from '@aztec/circuits.js/abis'; +import { computePublicDataTreeLeafSlot, computeUniqueCommitment, siloCommitment } from '@aztec/circuits.js/abis'; import { Grumpkin } from '@aztec/circuits.js/barretenberg'; import { FunctionAbi, FunctionArtifact, countArgumentsSize } from '@aztec/foundation/abi'; import { AztecAddress } from '@aztec/foundation/aztec-address'; @@ -436,4 +436,29 @@ export class ClientExecutionContext extends ViewDataOracle { startSideEffectCounter, ); } + + /** + * Read the public storage data. + * @param startStorageSlot - The starting storage slot. + * @param numberOfElements - Number of elements to read from the starting storage slot. + */ + public async storageRead(startStorageSlot: Fr, numberOfElements: number): Promise { + // TODO(#4320): This is a hack to work around not having directly access to the public data tree but + // still having access to the witnesses + const bn = await this.db.getBlockNumber(); + + const values = []; + for (let i = 0n; i < numberOfElements; i++) { + const storageSlot = new Fr(startStorageSlot.value + i); + const leafSlot = computePublicDataTreeLeafSlot(this.contractAddress, storageSlot); + const witness = await this.db.getPublicDataTreeWitness(bn, leafSlot); + if (!witness) { + throw new Error(`No witness for slot ${storageSlot.toString()}`); + } + const value = witness.leafPreimage.value; + this.log(`Oracle storage read: slot=${storageSlot.toString()} value=${value}`); + values.push(value); + } + return values; + } } diff --git a/yarn-project/archiver/src/archiver/archiver.ts b/yarn-project/archiver/src/archiver/archiver.ts index e350e2168236..4856a608bdff 100644 --- a/yarn-project/archiver/src/archiver/archiver.ts +++ b/yarn-project/archiver/src/archiver/archiver.ts @@ -297,11 +297,7 @@ export class Archiver implements ArchiveSource { retrievedBlocks.retrievedData.map(block => { // Ensure we pad the L1 to L2 message array to the full size before storing. block.newL1ToL2Messages = padArrayEnd(block.newL1ToL2Messages, Fr.ZERO, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP); - return L2Block.fromFields( - omit(block, ['newEncryptedLogs', 'newUnencryptedLogs']), - block.getBlockHash(), - block.getL1BlockNumber(), - ); + return L2Block.fromFields(omit(block, ['newEncryptedLogs', 'newUnencryptedLogs']), block.getL1BlockNumber()); }), ); } diff --git a/yarn-project/archiver/src/archiver/kv_archiver_store/block_store.ts b/yarn-project/archiver/src/archiver/kv_archiver_store/block_store.ts index db38f09866a0..ef637162c4e0 100644 --- a/yarn-project/archiver/src/archiver/kv_archiver_store/block_store.ts +++ b/yarn-project/archiver/src/archiver/kv_archiver_store/block_store.ts @@ -9,7 +9,6 @@ type BlockContext = { blockNumber: number; l1BlockNumber: bigint; block: Buffer; - blockHash: Buffer; }; /** @@ -46,7 +45,6 @@ export class BlockStore { blockNumber: block.number, block: block.toBuffer(), l1BlockNumber: block.getL1BlockNumber(), - blockHash: block.getBlockHash(), }); for (const [i, tx] of block.getTxs().entries()) { @@ -77,7 +75,7 @@ export class BlockStore { */ *getBlocks(start: number, limit: number): IterableIterator { for (const blockCtx of this.#blocks.values(this.#computeBlockRange(start, limit))) { - yield L2Block.fromBuffer(blockCtx.block, blockCtx.blockHash); + yield L2Block.fromBuffer(blockCtx.block); } } @@ -92,9 +90,7 @@ export class BlockStore { return undefined; } - const block = L2Block.fromBuffer(blockCtx.block, blockCtx.blockHash); - - return block; + return L2Block.fromBuffer(blockCtx.block); } /** diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index e770dbcf8402..2ccd40baa009 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -26,7 +26,6 @@ import { ARCHIVE_HEIGHT, CONTRACT_TREE_HEIGHT, Fr, - GlobalVariables, Header, L1_TO_L2_MSG_TREE_HEIGHT, NOTE_HASH_TREE_HEIGHT, @@ -35,7 +34,7 @@ import { PUBLIC_DATA_TREE_HEIGHT, PublicDataTreeLeafPreimage, } from '@aztec/circuits.js'; -import { computeGlobalsHash, computePublicDataTreeLeafSlot } from '@aztec/circuits.js/abis'; +import { computePublicDataTreeLeafSlot } from '@aztec/circuits.js/abis'; import { L1ContractAddresses, createEthereumChain } from '@aztec/ethereum'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { createDebugLogger } from '@aztec/foundation/log'; @@ -45,7 +44,6 @@ import { GlobalVariableBuilder, PublicProcessorFactory, SequencerClient, - buildInitialHeader, getGlobalVariableBuilder, } from '@aztec/sequencer-client'; import { SiblingPath } from '@aztec/types/membership'; @@ -537,7 +535,7 @@ export class AztecNodeService implements AztecNode { // No block was not found so we build the initial header. const committedDb = await this.#getWorldState('latest'); - return await buildInitialHeader(committedDb); + return await committedDb.buildInitialHeader(); } /** @@ -549,16 +547,11 @@ export class AztecNodeService implements AztecNode { const blockNumber = (await this.blockSource.getBlockNumber()) + 1; const newGlobalVariables = await this.globalVariableBuilder.buildGlobalVariables(new Fr(blockNumber)); const prevHeader = (await this.blockSource.getBlock(-1))?.header; - const prevGlobalVariables = prevHeader?.globalVariables ?? GlobalVariables.empty(); // Instantiate merkle trees so uncommitted updates by this simulation are local to it. // TODO we should be able to remove this after https://github.com/AztecProtocol/aztec-packages/issues/1869 // So simulation of public functions doesn't affect the merkle trees. - const merkleTrees = new MerkleTrees(this.merkleTreesDb, this.log); - const globalVariablesHash = computeGlobalsHash(prevGlobalVariables); - await merkleTrees.init({ - globalVariablesHash, - }); + const merkleTrees = await MerkleTrees.new(this.merkleTreesDb, this.log); const publicProcessorFactory = new PublicProcessorFactory( merkleTrees.asLatest(), diff --git a/yarn-project/aztec-nr/address-note/src/address_note.nr b/yarn-project/aztec-nr/address-note/src/address_note.nr index 3dc945793f2e..6a960c505d1c 100644 --- a/yarn-project/aztec-nr/address-note/src/address_note.nr +++ b/yarn-project/aztec-nr/address-note/src/address_note.nr @@ -9,7 +9,7 @@ use dep::aztec::{ note::{ note_header::NoteHeader, note_interface::NoteInterface, - utils::compute_note_hash_for_read_or_nullify, + utils::compute_note_hash_for_consumption, }, oracle::{ rand::rand, @@ -49,13 +49,13 @@ impl Deserialize for AddressNote { } impl NoteInterface for AddressNote { - fn compute_note_hash(self) -> Field { + fn compute_note_content_hash(self) -> Field { // TODO(#1205) Should use a non-zero generator index. pedersen_hash(self.serialize(), 0) } fn compute_nullifier(self, context: &mut PrivateContext) -> Field { - let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(self); + let note_hash_for_nullify = compute_note_hash_for_consumption(self); let secret = context.request_nullifier_secret_key(self.owner); // TODO(#1205) Should use a non-zero generator index. pedersen_hash([ @@ -66,7 +66,7 @@ impl NoteInterface for AddressNote { } fn compute_nullifier_without_context(self) -> Field { - let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(self); + let note_hash_for_nullify = compute_note_hash_for_consumption(self); let secret = get_nullifier_secret_key(self.owner); // TODO(#1205) Should use a non-zero generator index. pedersen_hash([ diff --git a/yarn-project/aztec-nr/authwit/src/entrypoint.nr b/yarn-project/aztec-nr/authwit/src/entrypoint.nr index 05ca66240f01..98987d49e362 100644 --- a/yarn-project/aztec-nr/authwit/src/entrypoint.nr +++ b/yarn-project/aztec-nr/authwit/src/entrypoint.nr @@ -1,5 +1,4 @@ use dep::aztec::abi; -use dep::aztec::types::vec::BoundedVec; use dep::aztec::context::PrivateContext; use dep::aztec::protocol_types::{ abis::{ @@ -66,7 +65,7 @@ impl Serialize for EntrypointPayload { fn serialize(self) -> [Field; ENTRYPOINT_PAYLOAD_SIZE] { let mut fields: BoundedVec = BoundedVec::new(0); for call in self.function_calls { - fields.push_array(call.serialize()); + fields.extend_from_array(call.serialize()); } fields.push(self.nonce); fields.storage diff --git a/yarn-project/aztec-nr/aztec/src/context.nr b/yarn-project/aztec-nr/aztec/src/context.nr index a0bafae62b54..c1e8bd5404c6 100644 --- a/yarn-project/aztec-nr/aztec/src/context.nr +++ b/yarn-project/aztec-nr/aztec/src/context.nr @@ -14,7 +14,6 @@ use crate::{ header::get_header_at, nullifier_key::{get_nullifier_key_pair, NullifierKeyPair}, }, - types::vec::BoundedVec, utils::Reader, }; use dep::protocol_types::{ diff --git a/yarn-project/aztec-nr/aztec/src/history/note_inclusion.nr b/yarn-project/aztec-nr/aztec/src/history/note_inclusion.nr index 15a2acc44edf..8ef9a2b5804c 100644 --- a/yarn-project/aztec-nr/aztec/src/history/note_inclusion.nr +++ b/yarn-project/aztec-nr/aztec/src/history/note_inclusion.nr @@ -3,7 +3,7 @@ use dep::std::merkle::compute_merkle_root; use crate::{ context::PrivateContext, note::{ - utils::compute_note_hash_for_read_or_nullify, + utils::compute_note_hash_for_consumption, note_header::NoteHeader, note_interface::NoteInterface, }, @@ -34,7 +34,7 @@ pub fn prove_note_inclusion( block_number: u32, // The block at which we'll prove that the note exists context: PrivateContext ) where Note: NoteInterface { - let note_commitment = compute_note_hash_for_read_or_nullify(note_with_header); + let note_commitment = compute_note_hash_for_consumption(note_with_header); prove_note_commitment_inclusion(note_commitment, block_number, context); } diff --git a/yarn-project/aztec-nr/aztec/src/history/public_value_inclusion.nr b/yarn-project/aztec-nr/aztec/src/history/public_value_inclusion.nr index 62c65bfd8581..ad14fb77cb38 100644 --- a/yarn-project/aztec-nr/aztec/src/history/public_value_inclusion.nr +++ b/yarn-project/aztec-nr/aztec/src/history/public_value_inclusion.nr @@ -35,12 +35,20 @@ pub fn prove_public_value_inclusion( // 4) Check that the witness matches the corresponding public_value let preimage = witness.leaf_preimage; - if preimage.slot == public_value_leaf_slot { - assert_eq(preimage.value, value, "Public value does not match value in witness"); + + // Here we have two cases. Code based on same checks in `validate_public_data_reads` in `base_rollup_inputs` + // 1. The value is the same as the one in the witness + // 2. The value was never initialized and is zero + let is_less_than_slot = full_field_less_than(preimage.slot, public_value_leaf_slot); + let is_next_greater_than = full_field_less_than(public_value_leaf_slot, preimage.next_slot); + let is_max = ((preimage.next_index == 0) & (preimage.next_slot == 0)); + let is_in_range = is_less_than_slot & (is_next_greater_than | is_max); + + if is_in_range { + assert_eq(value, 0, "Non-existant public data leaf value is non-zero"); } else { - assert_eq(value, 0, "Got non-zero public value for non-existing slot"); - assert(full_field_less_than(preimage.slot, public_value_leaf_slot), "Invalid witness range"); - assert(full_field_less_than(public_value_leaf_slot, preimage.next_slot), "Invalid witness range"); + assert_eq(preimage.slot, public_value_leaf_slot, "Public data slot don't match witness"); + assert_eq(preimage.value, value, "Public value does not match the witness"); } // 5) Prove that the leaf we validated is in the public data tree diff --git a/yarn-project/aztec-nr/aztec/src/lib.nr b/yarn-project/aztec-nr/aztec/src/lib.nr index 9bf7bf42c8f5..7a8531601c4f 100644 --- a/yarn-project/aztec-nr/aztec/src/lib.nr +++ b/yarn-project/aztec-nr/aztec/src/lib.nr @@ -8,6 +8,5 @@ mod messaging; mod note; mod oracle; mod state_vars; -mod types; mod utils; use dep::protocol_types; diff --git a/yarn-project/aztec-nr/aztec/src/note/lifecycle.nr b/yarn-project/aztec-nr/aztec/src/note/lifecycle.nr index 75d7bfcf3632..b42e9f86acc9 100644 --- a/yarn-project/aztec-nr/aztec/src/note/lifecycle.nr +++ b/yarn-project/aztec-nr/aztec/src/note/lifecycle.nr @@ -6,7 +6,7 @@ use crate::context::{ use crate::note::{ note_header::NoteHeader, note_interface::NoteInterface, - utils::compute_note_hash_for_read_or_nullify, + utils::{compute_note_hash_for_insertion, compute_note_hash_for_consumption}, }; use crate::oracle::notes::{notify_created_note, notify_nullified_note}; use dep::protocol_types::traits::{Serialize, Deserialize}; @@ -23,7 +23,7 @@ pub fn create_note( // TODO: change this to note.setHeader(header) once https://github.com/noir-lang/noir/issues/4095 is fixed Note::set_header(note, header); // As `is_transient` is true, this will compute the inner note hsah - let inner_note_hash = compute_note_hash_for_read_or_nullify(*note); + let inner_note_hash = compute_note_hash_for_insertion(*note); // TODO: Strong typing required because of https://github.com/noir-lang/noir/issues/4088 let serialized_note: [Field; N] = Note::serialize(*note); @@ -42,28 +42,28 @@ pub fn create_note_hash_from_public(context: &mut PublicContext, storage_s let header = NoteHeader { contract_address, storage_slot, nonce: 0, is_transient: true }; // TODO: change this to note.setHeader(header) once https://github.com/noir-lang/noir/issues/4095 is fixed Note::set_header(note, header); - let inner_note_hash = compute_note_hash_for_read_or_nullify(*note); + let inner_note_hash = compute_note_hash_for_insertion(*note); context.push_new_note_hash(inner_note_hash); } pub fn destroy_note(context: &mut PrivateContext, note: Note) where Note: NoteInterface { let mut nullifier = 0; - let mut nullified_commitment: Field = 0; + let mut consumed_note_hash: Field = 0; nullifier = note.compute_nullifier(context); - // We also need the note commitment corresponding to the "nullifier" + // We also need the note hash corresponding to the "nullifier" let header = note.get_header(); - // `nullified_commitment` is used to inform the kernel which pending commitment + // `consumed_note_hash` is used to inform the kernel which pending note hash // the nullifier corresponds to so they can be matched and both squashed/deleted. // nonzero nonce implies "persistable" nullifier (nullifies a persistent/in-tree - // commitment) in which case `nullified_commitment` is not used since the kernel + // note hash) in which case `consumed_note_hash` is not used since the kernel // just siloes and forwards the nullifier to its output. if (header.is_transient) { - // TODO(1718): Can we reuse the note commitment computed in `compute_nullifier`? - nullified_commitment = compute_note_hash_for_read_or_nullify(note); + // TODO(1718): Can we reuse the note hash computed in `compute_nullifier`? + consumed_note_hash = compute_note_hash_for_consumption(note); } - assert(notify_nullified_note(nullifier, nullified_commitment) == 0); + assert(notify_nullified_note(nullifier, consumed_note_hash) == 0); - context.push_new_nullifier(nullifier, nullified_commitment) + context.push_new_nullifier(nullifier, consumed_note_hash) } diff --git a/yarn-project/aztec-nr/aztec/src/note/note_getter.nr b/yarn-project/aztec-nr/aztec/src/note/note_getter.nr index a442aad9f83e..f34c89cfb8e4 100644 --- a/yarn-project/aztec-nr/aztec/src/note/note_getter.nr +++ b/yarn-project/aztec-nr/aztec/src/note/note_getter.nr @@ -14,10 +14,9 @@ use crate::note::{ note_getter_options::{NoteGetterOptions, Select, Sort, SortOrder, Comparator, NoteStatus}, note_interface::NoteInterface, note_viewer_options::NoteViewerOptions, - utils::compute_note_hash_for_read_or_nullify, + utils::compute_note_hash_for_consumption, }; use crate::oracle; -use crate::types::vec::BoundedVec; fn check_note_header( context: PrivateContext, @@ -79,7 +78,7 @@ pub fn get_note( check_note_header(*context, storage_slot, note); - let note_hash_for_read_request = compute_note_hash_for_read_or_nullify(note); + let note_hash_for_read_request = compute_note_hash_for_consumption(note); context.push_read_request(note_hash_for_read_request); note @@ -105,7 +104,7 @@ pub fn get_notes( } prev_fields = fields; - let note_hash_for_read_request = compute_note_hash_for_read_or_nullify(note); + let note_hash_for_read_request = compute_note_hash_for_consumption(note); // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1410): test to ensure // failure if malicious oracle injects 0 nonce here for a "pre-existing" note. context.push_read_request(note_hash_for_read_request); diff --git a/yarn-project/aztec-nr/aztec/src/note/note_getter_options.nr b/yarn-project/aztec-nr/aztec/src/note/note_getter_options.nr index 611ac239a34e..bec2aa7f0010 100644 --- a/yarn-project/aztec-nr/aztec/src/note/note_getter_options.nr +++ b/yarn-project/aztec-nr/aztec/src/note/note_getter_options.nr @@ -1,5 +1,4 @@ use dep::std::option::Option; -use crate::types::vec::BoundedVec; use dep::protocol_types::{ constants::MAX_READ_REQUESTS_PER_CALL, traits::Deserialize, diff --git a/yarn-project/aztec-nr/aztec/src/note/note_interface.nr b/yarn-project/aztec-nr/aztec/src/note/note_interface.nr index 48bd234e035a..63313e17bd58 100644 --- a/yarn-project/aztec-nr/aztec/src/note/note_interface.nr +++ b/yarn-project/aztec-nr/aztec/src/note/note_interface.nr @@ -3,7 +3,7 @@ use crate::note::note_header::NoteHeader; // docs:start:note_interface trait NoteInterface { - fn compute_note_hash(self) -> Field; + fn compute_note_content_hash(self) -> Field; fn get_header(self) -> NoteHeader; diff --git a/yarn-project/aztec-nr/aztec/src/note/note_viewer_options.nr b/yarn-project/aztec-nr/aztec/src/note/note_viewer_options.nr index 5677b33f429d..a935f63f7915 100644 --- a/yarn-project/aztec-nr/aztec/src/note/note_viewer_options.nr +++ b/yarn-project/aztec-nr/aztec/src/note/note_viewer_options.nr @@ -1,6 +1,5 @@ use dep::std::option::Option; use crate::note::note_getter_options::{Select, Sort, Comparator, NoteStatus}; -use crate::types::vec::BoundedVec; use dep::protocol_types::{ constants::MAX_NOTES_PER_PAGE, traits::Deserialize, diff --git a/yarn-project/aztec-nr/aztec/src/note/utils.nr b/yarn-project/aztec-nr/aztec/src/note/utils.nr index 148657752bb9..70591907cacc 100644 --- a/yarn-project/aztec-nr/aztec/src/note/utils.nr +++ b/yarn-project/aztec-nr/aztec/src/note/utils.nr @@ -18,11 +18,6 @@ use dep::protocol_types::{ traits::{Deserialize, Serialize}, }; -fn compute_inner_hash(storage_slot: Field, note_hash: Field) -> Field { - // TODO(#1205) Do we need a generator index here? - pedersen_hash([storage_slot, note_hash], 0) -} - fn compute_siloed_hash(contract_address: AztecAddress, inner_note_hash: Field) -> Field { let inputs = [contract_address.to_field(), inner_note_hash]; pedersen_hash(inputs, GENERATOR_INDEX__SILOED_COMMITMENT) @@ -35,9 +30,10 @@ fn compute_unique_hash(nonce: Field, siloed_note_hash: Field) -> Field { fn compute_inner_note_hash(note: Note) -> Field where Note: NoteInterface { let header = note.get_header(); - let note_hash = note.compute_note_hash(); + let note_hash = note.compute_note_content_hash(); - compute_inner_hash(header.storage_slot, note_hash) + // TODO(#1205) Do we need a generator index here? + pedersen_hash([header.storage_slot, note_hash], 0) } fn compute_siloed_note_hash(note_with_header: Note) -> Field where Note: NoteInterface { @@ -67,10 +63,17 @@ pub fn compute_siloed_nullifier( pedersen_hash(input, GENERATOR_INDEX__OUTER_NULLIFIER) } -pub fn compute_note_hash_for_read_or_nullify(note: Note) -> Field where Note: NoteInterface { +pub fn compute_note_hash_for_insertion(note: Note) -> Field where Note: NoteInterface { + compute_inner_note_hash(note) +} + +pub fn compute_note_hash_for_consumption(note: Note) -> Field where Note: NoteInterface { let header = note.get_header(); + // There are 3 cases for reading a note intended for consumption: + // 1. The note was inserted in this transaction, and is transient. + // 2. The note was inserted in a previous transaction, and was inserted in public + // 3. The note was inserted in a previous transaction, and was inserted in private - // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1386) if (header.is_transient) { // If a note is transient, we just read the inner_note_hash (kernel will silo by contract address). compute_inner_note_hash(note) @@ -95,8 +98,7 @@ pub fn compute_note_hash_and_nullifier( // TODO: change this to note.setHeader(header) once https://github.com/noir-lang/noir/issues/4095 is fixed T::set_header((&mut note), note_header); - let note_hash = note.compute_note_hash(); - let inner_note_hash = compute_inner_hash(note_header.storage_slot, note_hash); + let inner_note_hash = compute_inner_note_hash(note); let siloed_note_hash = compute_siloed_hash(note_header.contract_address, inner_note_hash); diff --git a/yarn-project/aztec-nr/aztec/src/oracle/header.nr b/yarn-project/aztec-nr/aztec/src/oracle/header.nr index 33718bf206bb..bcb7027d2d63 100644 --- a/yarn-project/aztec-nr/aztec/src/oracle/header.nr +++ b/yarn-project/aztec-nr/aztec/src/oracle/header.nr @@ -40,7 +40,7 @@ pub fn get_header_at(block_number: u32, context: PrivateContext) -> Header { let header = get_header_at_internal(block_number); // 4) Compute the block hash from the block header - let block_hash = header.block_hash(); + let block_hash = header.hash(); // 5) Get the membership witness of the block in the archive let witness = get_archive_membership_witness(last_archive_block_number, block_hash); diff --git a/yarn-project/aztec-nr/aztec/src/state_vars.nr b/yarn-project/aztec-nr/aztec/src/state_vars.nr index e1e813891ffb..d8213bb1ef04 100644 --- a/yarn-project/aztec-nr/aztec/src/state_vars.nr +++ b/yarn-project/aztec-nr/aztec/src/state_vars.nr @@ -3,3 +3,4 @@ mod map; mod public_state; mod set; mod singleton; +mod stable_public_state; diff --git a/yarn-project/aztec-nr/aztec/src/state_vars/set.nr b/yarn-project/aztec-nr/aztec/src/state_vars/set.nr index 489ccfad838d..03e53b33d7a2 100644 --- a/yarn-project/aztec-nr/aztec/src/state_vars/set.nr +++ b/yarn-project/aztec-nr/aztec/src/state_vars/set.nr @@ -13,7 +13,7 @@ use crate::note::{ note_header::NoteHeader, note_interface::NoteInterface, note_viewer_options::NoteViewerOptions, - utils::compute_note_hash_for_read_or_nullify, + utils::compute_note_hash_for_consumption, }; // docs:start:struct @@ -74,7 +74,7 @@ impl Set { // docs:start:remove pub fn remove(self, note: Note) where Note: NoteInterface { let context = self.context.private.unwrap(); - let note_hash = compute_note_hash_for_read_or_nullify(note); + let note_hash = compute_note_hash_for_consumption(note); let has_been_read = context.read_requests.any(|r: SideEffect| r.value == note_hash); assert(has_been_read, "Can only remove a note that has been read from the set."); diff --git a/yarn-project/aztec-nr/aztec/src/state_vars/stable_public_state.nr b/yarn-project/aztec-nr/aztec/src/state_vars/stable_public_state.nr new file mode 100644 index 000000000000..013f059aef08 --- /dev/null +++ b/yarn-project/aztec-nr/aztec/src/state_vars/stable_public_state.nr @@ -0,0 +1,69 @@ +use crate::context::{Context}; +use crate::oracle::{ + storage::{storage_read, storage_write}, +}; +use crate::history::public_value_inclusion::prove_public_value_inclusion; +use dep::std::option::Option; +use dep::protocol_types::traits::{Deserialize, Serialize}; + +struct StablePublicState{ + context: Context, + storage_slot: Field, +} + +impl StablePublicState { + pub fn new( + // Note: Passing the contexts to new(...) just to have an interface compatible with a Map. + context: Context, + storage_slot: Field + ) -> Self { + assert(storage_slot != 0, "Storage slot 0 not allowed. Storage slots must start from 1."); + Self { + context, + storage_slot, + } + } + + // Intended to be only called once. + pub fn initialize(self, value: T) where T: Serialize { + assert(self.context.private.is_none(), "Public state wrties only supported in public functions"); + // TODO: Must throw if the storage slot is not empty -> cannot allow overwriting + // This is currently impractical, as public functions are never marked `is_contract_deployment` + // in the `call_context`, only private functions will have this flag set. + let fields = T::serialize(value); + storage_write(self.storage_slot, fields); + } + + pub fn read_public(self) -> T where T: Deserialize { + assert(self.context.private.is_none(), "Public read only supported in public functions"); + let fields = storage_read(self.storage_slot); + T::deserialize(fields) + } + + pub fn read_private(self) -> T where T: Deserialize { + assert(self.context.public.is_none(), "Private read only supported in private functions"); + let private_context = self.context.private.unwrap(); + + // Read the value from storage (using the public tree) + let fields = storage_read(self.storage_slot); + + // TODO: The block_number here can be removed when using the current header in the membership proof. + let block_number = private_context.get_header().global_variables.block_number; + + // Loop over the fields and prove their inclusion in the public tree + for i in 0..fields.len() { + // TODO: Update membership proofs to use current header (Requires #4179) + // Currently executing unnecessary computation: + // - a membership proof of the header(block_number) in the history + // - a membership proof of the value in the public tree of the header + prove_public_value_inclusion( + fields[i], + self.storage_slot + i, + block_number as u32, + (*private_context), + ) + } + T::deserialize(fields) + } + +} diff --git a/yarn-project/aztec-nr/aztec/src/types.nr b/yarn-project/aztec-nr/aztec/src/types.nr deleted file mode 100644 index ae46c9e0c88e..000000000000 --- a/yarn-project/aztec-nr/aztec/src/types.nr +++ /dev/null @@ -1 +0,0 @@ -mod vec; // This can/should be moved out into an official noir library diff --git a/yarn-project/aztec-nr/aztec/src/types/vec.nr b/yarn-project/aztec-nr/aztec/src/types/vec.nr deleted file mode 100644 index 135bbbd53c33..000000000000 --- a/yarn-project/aztec-nr/aztec/src/types/vec.nr +++ /dev/null @@ -1,126 +0,0 @@ -struct BoundedVec { - storage: [T; MaxLen], - len: Field, -} - -impl BoundedVec { - pub fn new(initial_value: T) -> Self { - BoundedVec { storage: [initial_value; MaxLen], len: 0 } - } - - pub fn get(mut self: Self, index: Field) -> T { - assert(index as u64 < self.len as u64); - self.storage[index] - } - - pub fn get_unchecked(mut self: Self, index: Field) -> T { - self.storage[index] - } - - pub fn push(&mut self, elem: T) { - assert(self.len as u64 < MaxLen as u64); - - self.storage[self.len] = elem; - self.len += 1; - } - - pub fn push_array(&mut self, array: [T; Len]) { - let newLen = self.len + array.len(); - assert(newLen as u64 <= MaxLen as u64); - for i in 0..array.len() { - self.storage[self.len + i] = array[i]; - } - self.len = newLen; - } - - pub fn pop(&mut self) -> T { - assert(self.len as u64 > 0); - - let elem = self.storage[self.len - 1]; - self.len -= 1; - elem - } - - 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) { - ret |= predicate(self.storage[i]); - } - } - ret - } -} - -#[test] -fn test_vec_push_pop() { - let mut vec: BoundedVec = BoundedVec::new(0); - assert(vec.len == 0); - vec.push(2); - assert(vec.len == 1); - vec.push(4); - assert(vec.len == 2); - vec.push(6); - assert(vec.len == 3); - let x = vec.pop(); - assert(x == 6); - assert(vec.len == 2); - assert(vec.get(0) == 2); - assert(vec.get(1) == 4); -} - -#[test] -fn test_vec_push_array() { - let mut vec: BoundedVec = BoundedVec::new(0); - vec.push_array([2, 4]); - assert(vec.len == 2); - assert(vec.get(0) == 2); - assert(vec.get(1) == 4); -} - -#[test(should_fail)] -fn test_vec_get_out_of_bound() { - let mut vec: BoundedVec = BoundedVec::new(0); - vec.push_array([2, 4]); - let _x = vec.get(2); -} - -#[test(should_fail)] -fn test_vec_get_not_declared() { - let mut vec: BoundedVec = BoundedVec::new(0); - vec.push_array([2]); - let _x = vec.get(1); -} - -#[test(should_fail)] -fn test_vec_get_uninitialized() { - let mut vec: BoundedVec = BoundedVec::new(0); - let _x = vec.get(0); -} - -#[test(should_fail)] -fn test_vec_push_overflow() { - let mut vec: BoundedVec = BoundedVec::new(0); - vec.push(1); - vec.push(2); -} - -#[test] -fn test_vec_any() { - let mut vec: BoundedVec = BoundedVec::new(0); - vec.push_array([2, 4, 6]); - assert(vec.any(|v| v == 2) == true); - assert(vec.any(|v| v == 4) == true); - assert(vec.any(|v| v == 6) == true); - assert(vec.any(|v| v == 3) == false); -} - -#[test] -fn test_vec_any_not_default() { - let default_value = 1; - let mut vec: BoundedVec = BoundedVec::new(default_value); - vec.push_array([2, 4]); - assert(vec.any(|v| v == default_value) == false); -} diff --git a/yarn-project/aztec-nr/field-note/src/field_note.nr b/yarn-project/aztec-nr/field-note/src/field_note.nr index 45b2ba612084..7bf97b662ac5 100644 --- a/yarn-project/aztec-nr/field-note/src/field_note.nr +++ b/yarn-project/aztec-nr/field-note/src/field_note.nr @@ -34,7 +34,7 @@ impl Deserialize for FieldNote { } impl NoteInterface for FieldNote { - fn compute_note_hash(self) -> Field { + fn compute_note_content_hash(self) -> Field { // TODO(#1205) Should use a non-zero generator index. pedersen_hash(self.serialize(), 0) } diff --git a/yarn-project/aztec-nr/value-note/src/value_note.nr b/yarn-project/aztec-nr/value-note/src/value_note.nr index db0516f8306d..9b2c4b9bc6f1 100644 --- a/yarn-project/aztec-nr/value-note/src/value_note.nr +++ b/yarn-project/aztec-nr/value-note/src/value_note.nr @@ -6,7 +6,7 @@ use dep::aztec::{ note::{ note_header::NoteHeader, note_interface::NoteInterface, - utils::compute_note_hash_for_read_or_nullify, + utils::compute_note_hash_for_consumption, }, oracle::{ rand::rand, @@ -48,7 +48,7 @@ impl Deserialize for ValueNote { impl NoteInterface for ValueNote { - fn compute_note_hash(self) -> Field { + fn compute_note_content_hash(self) -> Field { // TODO(#1205) Should use a non-zero generator index. pedersen_hash(self.serialize(),0) } @@ -56,7 +56,7 @@ impl NoteInterface for ValueNote { // docs:start:nullifier fn compute_nullifier(self, context: &mut PrivateContext) -> Field { - let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(self); + let note_hash_for_nullify = compute_note_hash_for_consumption(self); let secret = context.request_nullifier_secret_key(self.owner); // TODO(#1205) Should use a non-zero generator index. pedersen_hash([ @@ -69,7 +69,7 @@ impl NoteInterface for ValueNote { // docs:end:nullifier fn compute_nullifier_without_context(self) -> Field { - let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(self); + let note_hash_for_nullify = compute_note_hash_for_consumption(self); let secret = get_nullifier_secret_key(self.owner); // TODO(#1205) Should use a non-zero generator index. pedersen_hash([ diff --git a/yarn-project/aztec/src/cli/cli.ts b/yarn-project/aztec/src/cli/cli.ts index da6b97a454be..b4620842cbd7 100644 --- a/yarn-project/aztec/src/cli/cli.ts +++ b/yarn-project/aztec/src/cli/cli.ts @@ -51,10 +51,10 @@ export function getProgram(userLog: LogFn, debugLogger: DebugLogger): Command { services = await startPXE(options, signalHandlers, userLog); } else if (options.archiver) { const { startArchiver } = await import('./cmds/start_archiver.js'); - await startArchiver(options, signalHandlers); + services = await startArchiver(options, signalHandlers); } else if (options.p2pBootstrap) { const { startP2PBootstrap } = await import('./cmds/start_p2p_bootstrap.js'); - await startP2PBootstrap(options, signalHandlers, debugLogger); + await startP2PBootstrap(options, signalHandlers, userLog, debugLogger); } if (services.length) { const rpcServer = createNamespacedJsonRpcServer(services, debugLogger); diff --git a/yarn-project/aztec/src/cli/cmds/start_p2p_bootstrap.ts b/yarn-project/aztec/src/cli/cmds/start_p2p_bootstrap.ts index 3ed48cf12cf4..2fedcfd56456 100644 --- a/yarn-project/aztec/src/cli/cmds/start_p2p_bootstrap.ts +++ b/yarn-project/aztec/src/cli/cmds/start_p2p_bootstrap.ts @@ -1,4 +1,5 @@ import { DebugLogger } from '@aztec/aztec.js'; +import { LogFn } from '@aztec/foundation/log'; import { BootstrapNode, P2PConfig, getP2PConfigEnvVars } from '@aztec/p2p'; import { mergeEnvVarsAndCliOptions, parseModuleOptions } from '../util.js'; @@ -6,6 +7,7 @@ import { mergeEnvVarsAndCliOptions, parseModuleOptions } from '../util.js'; export const startP2PBootstrap = async ( options: any, signalHandlers: (() => Promise)[], + userLog: LogFn, debugLogger: DebugLogger, ) => { // Start a P2P bootstrap node. @@ -14,5 +16,6 @@ export const startP2PBootstrap = async ( const bootstrapNode = new BootstrapNode(debugLogger); const config = mergeEnvVarsAndCliOptions(envVars, cliOptions); await bootstrapNode.start(config); + userLog(`P2P bootstrap node started on ${config.tcpListenIp}:${config.tcpListenPort}`); signalHandlers.push(bootstrapNode.stop); }; diff --git a/yarn-project/circuit-types/src/l2_block.ts b/yarn-project/circuit-types/src/l2_block.ts index 0f7dc8844e84..4210e720eaf1 100644 --- a/yarn-project/circuit-types/src/l2_block.ts +++ b/yarn-project/circuit-types/src/l2_block.ts @@ -11,7 +11,7 @@ import { } from '@aztec/circuits.js'; import { makeAppendOnlyTreeSnapshot, makeHeader } from '@aztec/circuits.js/factories'; import { times } from '@aztec/foundation/collection'; -import { keccak, sha256 } from '@aztec/foundation/crypto'; +import { sha256 } from '@aztec/foundation/crypto'; import { Fr } from '@aztec/foundation/fields'; import { createDebugLogger } from '@aztec/foundation/log'; import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; @@ -91,7 +91,6 @@ export class L2Block { public newL1ToL2Messages: Fr[] = [], newEncryptedLogs?: L2BlockL2Logs, newUnencryptedLogs?: L2BlockL2Logs, - private blockHash?: Buffer, l1BlockNumber?: bigint, ) { if (newCommitments.length % MAX_NEW_COMMITMENTS_PER_TX !== 0) { @@ -173,7 +172,6 @@ export class L2Block { newEncryptedLogs, newUnencryptedLogs, }, - undefined, // just for testing purposes, each random L2 block got emitted in the equivalent L1 block BigInt(l2BlockNum), ); @@ -229,7 +227,6 @@ export class L2Block { */ newUnencryptedLogs?: L2BlockL2Logs; }, - blockHash?: Buffer, l1BlockNumber?: bigint, ) { return new this( @@ -244,7 +241,6 @@ export class L2Block { fields.newL1ToL2Messages, fields.newEncryptedLogs, fields.newUnencryptedLogs, - blockHash, l1BlockNumber, ); } @@ -329,10 +325,9 @@ export class L2Block { /** * Deserializes L2 block without logs from a buffer. * @param buf - A serialized L2 block. - * @param blockHash - The hash of the block. * @returns Deserialized L2 block. */ - static fromBuffer(buf: Buffer | BufferReader, blockHash?: Buffer) { + static fromBuffer(buf: Buffer | BufferReader) { const reader = BufferReader.asReader(buf); const header = reader.readObject(Header); const archive = reader.readObject(AppendOnlyTreeSnapshot); @@ -345,20 +340,17 @@ export class L2Block { // TODO(sean): could an optimization of this be that it is encoded such that zeros are assumed const newL1ToL2Messages = reader.readVector(Fr); - return L2Block.fromFields( - { - archive, - header, - newCommitments, - newNullifiers, - newPublicDataWrites, - newL2ToL1Msgs, - newContracts, - newContractData, - newL1ToL2Messages, - }, - blockHash, - ); + return L2Block.fromFields({ + archive, + header, + newCommitments, + newNullifiers, + newPublicDataWrites, + newL2ToL1Msgs, + newContracts, + newContractData, + newL1ToL2Messages, + }); } /** @@ -441,14 +433,11 @@ export class L2Block { } /** - * Returns the block's hash. + * Returns the block's hash (hash of block header). * @returns The block's hash. */ - public getBlockHash(): Buffer { - if (!this.blockHash) { - this.blockHash = keccak(this.toBufferWithLogs()); - } - return this.blockHash; + public hash(): Fr { + return this.header.hash(); } /** @@ -460,11 +449,11 @@ export class L2Block { const buf = serializeToBuffer( this.header.globalVariables, // TODO(#3868) - AppendOnlyTreeSnapshot.empty(), // this.startNoteHashTreeSnapshot, - AppendOnlyTreeSnapshot.empty(), // this.startNullifierTreeSnapshot, - AppendOnlyTreeSnapshot.empty(), // this.startContractTreeSnapshot, - AppendOnlyTreeSnapshot.empty(), // this.startPublicDataTreeSnapshot, - AppendOnlyTreeSnapshot.empty(), // this.startL1ToL2MessageTreeSnapshot, + AppendOnlyTreeSnapshot.zero(), // this.startNoteHashTreeSnapshot, + AppendOnlyTreeSnapshot.zero(), // this.startNullifierTreeSnapshot, + AppendOnlyTreeSnapshot.zero(), // this.startContractTreeSnapshot, + AppendOnlyTreeSnapshot.zero(), // this.startPublicDataTreeSnapshot, + AppendOnlyTreeSnapshot.zero(), // this.startL1ToL2MessageTreeSnapshot, this.header.lastArchive, this.header.state.partial.noteHashTree, this.header.state.partial.nullifierTree, @@ -487,11 +476,11 @@ export class L2Block { const inputValue = serializeToBuffer( new Fr(Number(this.header.globalVariables.blockNumber.toBigInt()) - 1), // TODO(#3868) - AppendOnlyTreeSnapshot.empty(), // this.startNoteHashTreeSnapshot, - AppendOnlyTreeSnapshot.empty(), // this.startNullifierTreeSnapshot, - AppendOnlyTreeSnapshot.empty(), // this.startContractTreeSnapshot, - AppendOnlyTreeSnapshot.empty(), // this.startPublicDataTreeSnapshot, - AppendOnlyTreeSnapshot.empty(), // this.startL1ToL2MessageTreeSnapshot, + AppendOnlyTreeSnapshot.zero(), // this.startNoteHashTreeSnapshot, + AppendOnlyTreeSnapshot.zero(), // this.startNullifierTreeSnapshot, + AppendOnlyTreeSnapshot.zero(), // this.startContractTreeSnapshot, + AppendOnlyTreeSnapshot.zero(), // this.startPublicDataTreeSnapshot, + AppendOnlyTreeSnapshot.zero(), // this.startL1ToL2MessageTreeSnapshot, this.header.lastArchive, ); return sha256(inputValue); @@ -639,7 +628,7 @@ export class L2Block { newL2ToL1Msgs, newContracts, newContractData, - this.getBlockHash(), + this.hash(), Number(this.header.globalVariables.blockNumber.toBigInt()), ); } diff --git a/yarn-project/circuit-types/src/l2_tx.ts b/yarn-project/circuit-types/src/l2_tx.ts index 65588476418d..593f85d7b816 100644 --- a/yarn-project/circuit-types/src/l2_tx.ts +++ b/yarn-project/circuit-types/src/l2_tx.ts @@ -57,7 +57,7 @@ export class L2Tx { /** * The unique identifier of the block containing the transaction. */ - public blockHash: Buffer, + public blockHash: Fr, /** * The block number in which the transaction was included. */ @@ -80,7 +80,7 @@ export class L2Tx { reader.readVector(Fr), reader.readVector(Fr), reader.readVector(ContractData), - reader.readBytes(Fr.SIZE_IN_BYTES), + Fr.fromBuffer(reader), reader.readNumber(), ); } @@ -106,7 +106,7 @@ export class L2Tx { new Vector(this.newL2ToL1Msgs).toBuffer(), new Vector(this.newContracts).toBuffer(), new Vector(this.newContractData).toBuffer(), - this.blockHash, + this.blockHash.toBuffer(), numToUInt32BE(this.blockNumber), ]); } @@ -127,7 +127,7 @@ export class L2Tx { times(rand(0, MAX_NEW_L2_TO_L1_MSGS_PER_TX), Fr.random), times(rand(0, MAX_NEW_CONTRACTS_PER_TX), Fr.random), times(rand(0, MAX_NEW_CONTRACTS_PER_TX), ContractData.random), - Fr.random().toBuffer(), + Fr.random(), 123, ); } diff --git a/yarn-project/circuits.js/src/abis/__snapshots__/abis.test.ts.snap b/yarn-project/circuits.js/src/abis/__snapshots__/abis.test.ts.snap index 8491e4c1e370..a04642f359b3 100644 --- a/yarn-project/circuits.js/src/abis/__snapshots__/abis.test.ts.snap +++ b/yarn-project/circuits.js/src/abis/__snapshots__/abis.test.ts.snap @@ -10,49 +10,6 @@ exports[`abis Computes an empty public inputs hash 1`] = `"0x2e2b79cee62cb99e91 exports[`abis Computes an empty sideeffect hash 1`] = `"0x27b1d0839a5b23baf12a8d195b18ac288fcf401afb2f70b8a4b529ede5fa9fed"`; -exports[`abis compute globals hash 1`] = ` -Fr { - "asBigInt": 19996198784166720428914107076917074510032365849254400404611644441094528984289n, - "asBuffer": { - "data": [ - 44, - 53, - 114, - 139, - 52, - 188, - 28, - 197, - 40, - 157, - 117, - 130, - 246, - 163, - 68, - 239, - 11, - 227, - 228, - 184, - 250, - 189, - 231, - 11, - 41, - 32, - 78, - 168, - 174, - 60, - 100, - 225, - ], - "type": "Buffer", - }, -} -`; - exports[`abis compute private call stack item hash 1`] = ` Fr { "asBigInt": 12187345511405217717040217531423286257305914329376428594135414078733109256018n, @@ -280,49 +237,6 @@ exports[`abis computes a function selector 1`] = ` } `; -exports[`abis computes block hash with globals 1`] = ` -Fr { - "asBigInt": 7177915431153102916601456081755280584460785548870192083974540199919775120827n, - "asBuffer": { - "data": [ - 15, - 222, - 142, - 96, - 169, - 217, - 84, - 90, - 55, - 110, - 52, - 172, - 167, - 236, - 97, - 56, - 185, - 98, - 133, - 50, - 113, - 88, - 184, - 54, - 59, - 75, - 252, - 181, - 66, - 59, - 253, - 187, - ], - "type": "Buffer", - }, -} -`; - exports[`abis computes commitment nonce 1`] = ` Fr { "asBigInt": 7653394882992289714855533169019502055399179742531912347686813547951736946253n, diff --git a/yarn-project/circuits.js/src/abis/abis.test.ts b/yarn-project/circuits.js/src/abis/abis.test.ts index 30ed11ab2629..99b718e52a93 100644 --- a/yarn-project/circuits.js/src/abis/abis.test.ts +++ b/yarn-project/circuits.js/src/abis/abis.test.ts @@ -6,7 +6,6 @@ import { FunctionData, FunctionLeafPreimage, FunctionSelector, - GlobalVariables, NewContractData, PublicCallStackItem, PublicCircuitPublicInputs, @@ -22,14 +21,12 @@ import { makeVerificationKey, } from '../tests/factories.js'; import { - computeBlockHashWithGlobals, computeCommitmentNonce, computeCommitmentsHash, computeContractLeaf, computeFunctionLeaf, computeFunctionSelector, computeFunctionTreeRoot, - computeGlobalsHash, computeNullifierHash, computePrivateCallStackItemHash, computePublicCallStackItemHash, @@ -113,40 +110,6 @@ describe('abis', () => { expect(res).toMatchSnapshot(); }); - it('computes block hash with globals', () => { - const globals = GlobalVariables.from({ - chainId: new Fr(1n), - version: new Fr(2n), - blockNumber: new Fr(3n), - timestamp: new Fr(4n), - }); - const noteHashTreeRoot = new Fr(5n); - const nullifierTreeRoot = new Fr(6n); - const contractTreeRoot = new Fr(7n); - const l1ToL2DataTreeRoot = new Fr(8n); - const publicDataTreeRoot = new Fr(9n); - const res = computeBlockHashWithGlobals( - globals, - noteHashTreeRoot, - nullifierTreeRoot, - contractTreeRoot, - l1ToL2DataTreeRoot, - publicDataTreeRoot, - ); - expect(res).toMatchSnapshot(); - }); - - it('compute globals hash', () => { - const globals = GlobalVariables.from({ - chainId: new Fr(1n), - version: new Fr(2n), - blockNumber: new Fr(3n), - timestamp: new Fr(4n), - }); - const res = computeGlobalsHash(globals); - expect(res).toMatchSnapshot(); - }); - it('computes public data tree value', () => { const value = new Fr(3n); const res = computePublicDataTreeValue(value); diff --git a/yarn-project/circuits.js/src/abis/abis.ts b/yarn-project/circuits.js/src/abis/abis.ts index 999115a4839e..8f346fbd79e8 100644 --- a/yarn-project/circuits.js/src/abis/abis.ts +++ b/yarn-project/circuits.js/src/abis/abis.ts @@ -22,7 +22,6 @@ import { ContractStorageUpdateRequest, FunctionData, FunctionLeafPreimage, - GlobalVariables, NewContractData, PrivateCallStackItem, PrivateCircuitPublicInputs, @@ -212,90 +211,6 @@ export function siloNullifier(contract: AztecAddress, innerNullifier: Fr): Fr { return Fr.fromBuffer(pedersenHash([contract.toBuffer(), innerNullifier.toBuffer()], GeneratorIndex.OUTER_NULLIFIER)); } -/** - * Computes the block hash given the blocks globals and roots. - * @param globals - The global variables to put into the block hash. - * @param noteHashTree - The root of the note hash tree. - * @param nullifierTreeRoot - The root of the nullifier tree. - * @param contractTreeRoot - The root of the contract tree. - * @param l1ToL2DataTreeRoot - The root of the l1 to l2 data tree. - * @param publicDataTreeRoot - The root of the public data tree. - * @returns The block hash. - */ -// TODO(#3941) -export function computeBlockHashWithGlobals( - globals: GlobalVariables, - noteHashTreeRoot: Fr, - nullifierTreeRoot: Fr, - contractTreeRoot: Fr, - l1ToL2DataTreeRoot: Fr, - publicDataTreeRoot: Fr, -): Fr { - return computeBlockHash( - computeGlobalsHash(globals), - noteHashTreeRoot, - nullifierTreeRoot, - contractTreeRoot, - l1ToL2DataTreeRoot, - publicDataTreeRoot, - ); -} - -/** - * Computes the block hash given the blocks globals and roots. - * @param globalsHash - The global variables hash to put into the block hash. - * @param noteHashTree - The root of the note hash tree. - * @param nullifierTreeRoot - The root of the nullifier tree. - * @param contractTreeRoot - The root of the contract tree. - * @param l1ToL2DataTreeRoot - The root of the l1 to l2 data tree. - * @param publicDataTreeRoot - The root of the public data tree. - * @returns The block hash. - */ -// TODO(#3941): nuke this and replace with `Header.hash()` -export function computeBlockHash( - globalsHash: Fr, - noteHashTreeRoot: Fr, - nullifierTreeRoot: Fr, - contractTreeRoot: Fr, - l1ToL2DataTreeRoot: Fr, - publicDataTreeRoot: Fr, -): Fr { - return Fr.fromBuffer( - pedersenHash( - [ - globalsHash.toBuffer(), - noteHashTreeRoot.toBuffer(), - nullifierTreeRoot.toBuffer(), - contractTreeRoot.toBuffer(), - l1ToL2DataTreeRoot.toBuffer(), - publicDataTreeRoot.toBuffer(), - ], - GeneratorIndex.BLOCK_HASH, - ), - ); -} - -/** - * Computes the globals hash given the globals. - * @param globals - The global variables to put into the block hash. - * @returns The globals hash. - * TODO: move this to GlobalVariables? - * TODO(#3941) Investigate whether to nuke this once #3941 is done. - */ -export function computeGlobalsHash(globals: GlobalVariables): Fr { - return Fr.fromBuffer( - pedersenHash( - [ - globals.chainId.toBuffer(), - globals.version.toBuffer(), - globals.blockNumber.toBuffer(), - globals.timestamp.toBuffer(), - ], - GeneratorIndex.GLOBAL_VARIABLES, - ), - ); -} - /** * Computes a public data tree value ready for insertion. * @param value - Raw public data tree value to hash into a tree-insertion-ready value. diff --git a/yarn-project/circuits.js/src/structs/__snapshots__/header.test.ts.snap b/yarn-project/circuits.js/src/structs/__snapshots__/header.test.ts.snap new file mode 100644 index 000000000000..8a15a59ee847 --- /dev/null +++ b/yarn-project/circuits.js/src/structs/__snapshots__/header.test.ts.snap @@ -0,0 +1,44 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Header computes hash 1`] = ` +Fr { + "asBigInt": 17991638921121681345555824161757346486368776085978982802127357651656088857262n, + "asBuffer": { + "data": [ + 39, + 198, + 232, + 33, + 120, + 194, + 121, + 42, + 23, + 66, + 111, + 251, + 166, + 131, + 251, + 128, + 16, + 46, + 122, + 209, + 193, + 24, + 177, + 67, + 172, + 91, + 198, + 153, + 236, + 93, + 170, + 174, + ], + "type": "Buffer", + }, +} +`; diff --git a/yarn-project/circuits.js/src/structs/complete_address.ts b/yarn-project/circuits.js/src/structs/complete_address.ts index 7708ee176078..cf34dfa24265 100644 --- a/yarn-project/circuits.js/src/structs/complete_address.ts +++ b/yarn-project/circuits.js/src/structs/complete_address.ts @@ -16,7 +16,7 @@ import { * * @remarks We have introduced this type because it is common that these 3 values are used together. They are commonly * used together because it is the information needed to send user a note. - * @remarks See the link bellow for details about how address is computed: + * @remarks See the link below for details about how address is computed: * https://github.com/AztecProtocol/aztec-packages/blob/master/docs/docs/concepts/foundation/accounts/keys.md#addresses-partial-addresses-and-public-keys */ export class CompleteAddress { diff --git a/yarn-project/circuits.js/src/structs/header.test.ts b/yarn-project/circuits.js/src/structs/header.test.ts index 5581a1b482a3..72d49ae9bf27 100644 --- a/yarn-project/circuits.js/src/structs/header.test.ts +++ b/yarn-project/circuits.js/src/structs/header.test.ts @@ -18,4 +18,11 @@ describe('Header', () => { const res = Header.fromFieldArray(fieldArray); expect(res).toEqual(expected); }); + + it('computes hash', () => { + const seed = 9870243; + const header = makeHeader(seed, undefined); + const hash = header.hash(); + expect(hash).toMatchSnapshot(); + }); }); diff --git a/yarn-project/circuits.js/src/structs/header.ts b/yarn-project/circuits.js/src/structs/header.ts index 9f78ac3a1a02..016f9e9f98e7 100644 --- a/yarn-project/circuits.js/src/structs/header.ts +++ b/yarn-project/circuits.js/src/structs/header.ts @@ -1,7 +1,8 @@ +import { pedersenHash } from '@aztec/foundation/crypto'; import { Fr } from '@aztec/foundation/fields'; import { BufferReader, from2Fields, serializeToBuffer, to2Fields } from '@aztec/foundation/serialize'; -import { HEADER_LENGTH } from '../constants.gen.js'; +import { GeneratorIndex, HEADER_LENGTH } from '../constants.gen.js'; import { GlobalVariables } from './global_variables.js'; import { PartialStateReference } from './partial_state_reference.js'; import { AppendOnlyTreeSnapshot } from './rollup/append_only_tree_snapshot.js'; @@ -79,7 +80,7 @@ export class Header { static empty(): Header { return new Header( - AppendOnlyTreeSnapshot.empty(), + AppendOnlyTreeSnapshot.zero(), Buffer.alloc(NUM_BYTES_PER_SHA256), StateReference.empty(), GlobalVariables.empty(), @@ -88,7 +89,7 @@ export class Header { isEmpty(): boolean { return ( - this.lastArchive.isEmpty() && + this.lastArchive.isZero() && this.bodyHash.equals(Buffer.alloc(NUM_BYTES_PER_SHA256)) && this.state.isEmpty() && this.globalVariables.isEmpty() @@ -107,4 +108,13 @@ export class Header { const buffer = Buffer.from(str.replace(/^0x/i, ''), 'hex'); return Header.fromBuffer(buffer); } + + hash(): Fr { + return Fr.fromBuffer( + pedersenHash( + this.toFieldArray().map(f => f.toBuffer()), + GeneratorIndex.BLOCK_HASH, + ), + ); + } } diff --git a/yarn-project/circuits.js/src/structs/partial_state_reference.ts b/yarn-project/circuits.js/src/structs/partial_state_reference.ts index ed2ce8857921..7e375eeb12a8 100644 --- a/yarn-project/circuits.js/src/structs/partial_state_reference.ts +++ b/yarn-project/circuits.js/src/structs/partial_state_reference.ts @@ -29,10 +29,10 @@ export class PartialStateReference { static empty(): PartialStateReference { return new PartialStateReference( - AppendOnlyTreeSnapshot.empty(), - AppendOnlyTreeSnapshot.empty(), - AppendOnlyTreeSnapshot.empty(), - AppendOnlyTreeSnapshot.empty(), + AppendOnlyTreeSnapshot.zero(), + AppendOnlyTreeSnapshot.zero(), + AppendOnlyTreeSnapshot.zero(), + AppendOnlyTreeSnapshot.zero(), ); } @@ -51,10 +51,10 @@ export class PartialStateReference { isEmpty(): boolean { return ( - this.noteHashTree.isEmpty() && - this.nullifierTree.isEmpty() && - this.contractTree.isEmpty() && - this.publicDataTree.isEmpty() + this.noteHashTree.isZero() && + this.nullifierTree.isZero() && + this.contractTree.isZero() && + this.publicDataTree.isZero() ); } } diff --git a/yarn-project/circuits.js/src/structs/rollup/append_only_tree_snapshot.ts b/yarn-project/circuits.js/src/structs/rollup/append_only_tree_snapshot.ts index 4d8914029c1a..09216a8a0a17 100644 --- a/yarn-project/circuits.js/src/structs/rollup/append_only_tree_snapshot.ts +++ b/yarn-project/circuits.js/src/structs/rollup/append_only_tree_snapshot.ts @@ -47,11 +47,11 @@ export class AppendOnlyTreeSnapshot { return AppendOnlyTreeSnapshot.fromBuffer(Buffer.from(str, STRING_ENCODING)); } - static empty() { + static zero() { return new AppendOnlyTreeSnapshot(Fr.ZERO, 0); } - isEmpty(): boolean { + isZero(): boolean { return this.root.isZero() && this.nextAvailableLeafIndex === 0; } } diff --git a/yarn-project/circuits.js/src/structs/state_reference.ts b/yarn-project/circuits.js/src/structs/state_reference.ts index 7079c4511f48..284d988e07fb 100644 --- a/yarn-project/circuits.js/src/structs/state_reference.ts +++ b/yarn-project/circuits.js/src/structs/state_reference.ts @@ -30,10 +30,10 @@ export class StateReference { } static empty(): StateReference { - return new StateReference(AppendOnlyTreeSnapshot.empty(), PartialStateReference.empty()); + return new StateReference(AppendOnlyTreeSnapshot.zero(), PartialStateReference.empty()); } isEmpty(): boolean { - return this.l1ToL2MessageTree.isEmpty() && this.partial.isEmpty(); + return this.l1ToL2MessageTree.isZero() && this.partial.isEmpty(); } } diff --git a/yarn-project/circuits.js/src/types/partial_address.ts b/yarn-project/circuits.js/src/types/partial_address.ts index e99d3786536a..21877f72e889 100644 --- a/yarn-project/circuits.js/src/types/partial_address.ts +++ b/yarn-project/circuits.js/src/types/partial_address.ts @@ -1,7 +1,7 @@ import { Fr } from '@aztec/foundation/fields'; /** - * A type which along with public key forms a preimage of a contract address. See the link bellow for more details + * A type which along with public key forms a preimage of a contract address. See the link below for more details * https://github.com/AztecProtocol/aztec-packages/blob/master/docs/docs/concepts/foundation/accounts/keys.md#addresses-partial-addresses-and-public-keys */ export type PartialAddress = Fr; diff --git a/yarn-project/cli/src/cmds/example_contracts.ts b/yarn-project/cli/src/cmds/example_contracts.ts index 5b40c5c60c31..c7ee019ecccf 100644 --- a/yarn-project/cli/src/cmds/example_contracts.ts +++ b/yarn-project/cli/src/cmds/example_contracts.ts @@ -4,6 +4,6 @@ import { getExampleContractArtifacts } from '../utils.js'; export async function exampleContracts(log: LogFn) { const abisList = await getExampleContractArtifacts(); - const names = Object.keys(abisList); + const names = Object.keys(abisList).filter(name => name !== 'AvmTestContractArtifact'); names.forEach(name => log(name)); } diff --git a/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts b/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts index 287d3fc27f1e..87821e95d11c 100644 --- a/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts @@ -157,11 +157,15 @@ describe('e2e_inclusion_proofs_contract', () => { it('public value existence failure case', async () => { // Choose random block number between first block and current block number to test archival node const blockNumber = await getRandomBlockNumber(); - const randomPublicValue = Fr.random(); await expect( contract.methods.test_public_value_inclusion_proof(randomPublicValue, blockNumber).send().wait(), - ).rejects.toThrow(/Public value does not match value in witness/); + ).rejects.toThrow('Public value does not match the witness'); + }); + + it('proves existence of uninitialized public value', async () => { + const blockNumber = await getRandomBlockNumber(); + await contract.methods.test_public_unused_value_inclusion_proof(blockNumber).send().wait(); }); }); diff --git a/yarn-project/end-to-end/src/e2e_singleton.test.ts b/yarn-project/end-to-end/src/e2e_state_vars.test.ts similarity index 89% rename from yarn-project/end-to-end/src/e2e_singleton.test.ts rename to yarn-project/end-to-end/src/e2e_state_vars.test.ts index 6c1407ef053d..0ad3a75a04e6 100644 --- a/yarn-project/end-to-end/src/e2e_singleton.test.ts +++ b/yarn-project/end-to-end/src/e2e_state_vars.test.ts @@ -3,7 +3,7 @@ import { DocsExampleContract } from '@aztec/noir-contracts'; import { setup } from './fixtures/utils.js'; -describe('e2e_singleton', () => { +describe('e2e_state_vars', () => { let wallet: Wallet; let teardown: () => Promise; @@ -19,6 +19,24 @@ describe('e2e_singleton', () => { afterAll(() => teardown()); + describe('Stable Public State', () => { + it('private read of uninitialized stable', async () => { + const s = await contract.methods.get_stable().view(); + + const receipt2 = await contract.methods.match_stable(s.account, s.points).send().wait(); + expect(receipt2.status).toEqual(TxStatus.MINED); + }); + + it('private read of initialized stable', async () => { + const receipt = await contract.methods.initialize_stable(1).send().wait(); + expect(receipt.status).toEqual(TxStatus.MINED); + const s = await contract.methods.get_stable().view(); + + const receipt2 = await contract.methods.match_stable(s.account, s.points).send().wait(); + expect(receipt2.status).toEqual(TxStatus.MINED); + }, 200_000); + }); + describe('Singleton', () => { it('fail to read uninitialized singleton', async () => { expect(await contract.methods.is_legendary_initialized().view()).toEqual(false); diff --git a/yarn-project/end-to-end/src/e2e_token_contract.test.ts b/yarn-project/end-to-end/src/e2e_token_contract.test.ts index 33ecf0da53a6..bd40ce3faacb 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract.test.ts @@ -87,43 +87,87 @@ describe('e2e_token_contract', () => { reader = await ReaderContract.deploy(wallets[0]).send().deployed(); }); - it('name', async () => { - const t = toString(await asset.methods.un_get_name().view()); - expect(t).toBe(TOKEN_NAME); + describe('name', () => { + it('private', async () => { + const t = toString(await asset.methods.un_get_name().view()); + expect(t).toBe(TOKEN_NAME); - const tx = reader.methods.check_name(asset.address, TOKEN_NAME).send(); - const receipt = await tx.wait(); - expect(receipt.status).toBe(TxStatus.MINED); + const tx = reader.methods.check_name_private(asset.address, TOKEN_NAME).send(); + const receipt = await tx.wait(); + expect(receipt.status).toBe(TxStatus.MINED); + + await expect(reader.methods.check_name_private(asset.address, 'WRONG_NAME').simulate()).rejects.toThrowError( + "Cannot satisfy constraint 'name.is_eq(_what)'", + ); + }); + + it('public', async () => { + const t = toString(await asset.methods.un_get_name().view()); + expect(t).toBe(TOKEN_NAME); - await expect(reader.methods.check_name(asset.address, 'WRONG_NAME').simulate()).rejects.toThrowError( - "Failed to solve brillig function, reason: explicit trap hit in brillig 'name.is_eq(_what)'", - ); + const tx = reader.methods.check_name_public(asset.address, TOKEN_NAME).send(); + const receipt = await tx.wait(); + expect(receipt.status).toBe(TxStatus.MINED); + + await expect(reader.methods.check_name_public(asset.address, 'WRONG_NAME').simulate()).rejects.toThrowError( + "Failed to solve brillig function, reason: explicit trap hit in brillig 'name.is_eq(_what)'", + ); + }); }); - it('symbol', async () => { - const t = toString(await asset.methods.un_get_symbol().view()); - expect(t).toBe(TOKEN_SYMBOL); + describe('symbol', () => { + it('private', async () => { + const t = toString(await asset.methods.un_get_symbol().view()); + expect(t).toBe(TOKEN_SYMBOL); - const tx = reader.methods.check_symbol(asset.address, TOKEN_SYMBOL).send(); - const receipt = await tx.wait(); - expect(receipt.status).toBe(TxStatus.MINED); + const tx = reader.methods.check_symbol_private(asset.address, TOKEN_SYMBOL).send(); + const receipt = await tx.wait(); + expect(receipt.status).toBe(TxStatus.MINED); + + await expect( + reader.methods.check_symbol_private(asset.address, 'WRONG_SYMBOL').simulate(), + ).rejects.toThrowError("Cannot satisfy constraint 'symbol.is_eq(_what)'"); + }); + it('public', async () => { + const t = toString(await asset.methods.un_get_symbol().view()); + expect(t).toBe(TOKEN_SYMBOL); - await expect(reader.methods.check_symbol(asset.address, 'WRONG_SYMBOL').simulate()).rejects.toThrowError( - "Failed to solve brillig function, reason: explicit trap hit in brillig 'symbol.is_eq(_what)'", - ); + const tx = reader.methods.check_symbol_public(asset.address, TOKEN_SYMBOL).send(); + const receipt = await tx.wait(); + expect(receipt.status).toBe(TxStatus.MINED); + + await expect(reader.methods.check_symbol_public(asset.address, 'WRONG_SYMBOL').simulate()).rejects.toThrowError( + "Failed to solve brillig function, reason: explicit trap hit in brillig 'symbol.is_eq(_what)'", + ); + }); }); - it('decimals', async () => { - const t = await asset.methods.un_get_decimals().view(); - expect(t).toBe(TOKEN_DECIMALS); + describe('decimals', () => { + it('private', async () => { + const t = await asset.methods.un_get_decimals().view(); + expect(t).toBe(TOKEN_DECIMALS); - const tx = reader.methods.check_decimals(asset.address, TOKEN_DECIMALS).send(); - const receipt = await tx.wait(); - expect(receipt.status).toBe(TxStatus.MINED); + const tx = reader.methods.check_decimals_private(asset.address, TOKEN_DECIMALS).send(); + const receipt = await tx.wait(); + expect(receipt.status).toBe(TxStatus.MINED); - await expect(reader.methods.check_decimals(asset.address, 99).simulate()).rejects.toThrowError( - "Failed to solve brillig function, reason: explicit trap hit in brillig 'ret[0] as u8 == what'", - ); + await expect(reader.methods.check_decimals_private(asset.address, 99).simulate()).rejects.toThrowError( + "Cannot satisfy constraint 'ret[0] as u8 == what'", + ); + }); + + it('public', async () => { + const t = await asset.methods.un_get_decimals().view(); + expect(t).toBe(TOKEN_DECIMALS); + + const tx = reader.methods.check_decimals_public(asset.address, TOKEN_DECIMALS).send(); + const receipt = await tx.wait(); + expect(receipt.status).toBe(TxStatus.MINED); + + await expect(reader.methods.check_decimals_public(asset.address, 99).simulate()).rejects.toThrowError( + "Failed to solve brillig function, reason: explicit trap hit in brillig 'ret[0] as u8 == what'", + ); + }); }); }); diff --git a/yarn-project/end-to-end/src/integration_l1_publisher.test.ts b/yarn-project/end-to-end/src/integration_l1_publisher.test.ts index 4b6c4da2429e..f147fccfd4b2 100644 --- a/yarn-project/end-to-end/src/integration_l1_publisher.test.ts +++ b/yarn-project/end-to-end/src/integration_l1_publisher.test.ts @@ -36,7 +36,6 @@ import { L1Publisher, RealRollupCircuitSimulator, SoloBlockBuilder, - buildInitialHeader, getL1Publisher, getVerificationKeys, makeEmptyProcessedTx as makeEmptyProcessedTxFromHistoricalTreeRoots, @@ -150,7 +149,7 @@ describe('L1Publisher integration', () => { l1BlockPublishRetryIntervalMS: 100, }); - prevHeader = await buildInitialHeader(builderDb); + prevHeader = await builderDb.buildInitialHeader(); }, 100_000); const makeEmptyProcessedTx = async () => { @@ -256,7 +255,7 @@ describe('L1Publisher integration', () => { l2ToL1Messages: block.newL2ToL1Msgs.map(m => `0x${m.toBuffer().toString('hex').padStart(64, '0')}`), }, block: { - // The json formatting in forge is a bit brittle, so we convert Fr to a number in the few values bellow. + // The json formatting in forge is a bit brittle, so we convert Fr to a number in the few values below. // This should not be a problem for testing as long as the values are not larger than u32. archive: `0x${block.archive.root.toBuffer().toString('hex').padStart(64, '0')}`, body: `0x${block.bodyToBuffer().toString('hex')}`, diff --git a/yarn-project/foundation/src/abi/encoder.ts b/yarn-project/foundation/src/abi/encoder.ts index 5d757e080620..7aef767cca3b 100644 --- a/yarn-project/foundation/src/abi/encoder.ts +++ b/yarn-project/foundation/src/abi/encoder.ts @@ -96,7 +96,7 @@ class ArgumentEncoder { break; } for (const field of abiType.fields) { - // The ugly check bellow is here because of a `CompleteAddress`. Since it has `address` property but in ABI + // The ugly check below is here because of a `CompleteAddress`. Since it has `address` property but in ABI // it's called inner we set `field.name` here to `address` instead of using `field.name`. I know it's hacky // but using address.address in Noir looks stupid and renaming `address` param of `CompleteAddress` // to `inner` doesn't make sense. diff --git a/yarn-project/merkle-tree/src/index.ts b/yarn-project/merkle-tree/src/index.ts index 68826f44e42c..45ae1771bbf8 100644 --- a/yarn-project/merkle-tree/src/index.ts +++ b/yarn-project/merkle-tree/src/index.ts @@ -6,7 +6,7 @@ export * from './pedersen.js'; export * from './sparse_tree/sparse_tree.js'; export { StandardIndexedTree } from './standard_indexed_tree/standard_indexed_tree.js'; export * from './standard_tree/standard_tree.js'; -export { INITIAL_LEAF } from './tree_base.js'; +export { INITIAL_LEAF, getTreeMeta } from './tree_base.js'; export { newTree } from './new_tree.js'; export { loadTree } from './load_tree.js'; export * from './snapshots/snapshot_builder.js'; diff --git a/yarn-project/noir-contracts/Nargo.toml b/yarn-project/noir-contracts/Nargo.toml index 1b980b3dad3a..4c919e77911d 100644 --- a/yarn-project/noir-contracts/Nargo.toml +++ b/yarn-project/noir-contracts/Nargo.toml @@ -1,8 +1,9 @@ [workspace] members = [ - "contracts/benchmarking_contract", - "contracts/card_game_contract", - "contracts/child_contract", + "contracts/avm_test_contract", + "contracts/benchmarking_contract", + "contracts/card_game_contract", + "contracts/child_contract", "contracts/counter_contract", "contracts/docs_example_contract", "contracts/easy_private_token_contract", diff --git a/yarn-project/noir-contracts/contracts/avm_test_contract/Nargo.toml b/yarn-project/noir-contracts/contracts/avm_test_contract/Nargo.toml new file mode 100644 index 000000000000..f3d8583d4335 --- /dev/null +++ b/yarn-project/noir-contracts/contracts/avm_test_contract/Nargo.toml @@ -0,0 +1,8 @@ +[package] +name = "avm_test_contract" +authors = [""] +compiler_version = ">=0.18.0" +type = "contract" + +[dependencies] +aztec = { path = "../../../aztec-nr/aztec" } diff --git a/yarn-project/noir-contracts/contracts/avm_test_contract/src/main.nr b/yarn-project/noir-contracts/contracts/avm_test_contract/src/main.nr new file mode 100644 index 000000000000..00e35dfe6ba1 --- /dev/null +++ b/yarn-project/noir-contracts/contracts/avm_test_contract/src/main.nr @@ -0,0 +1,26 @@ +// + +contract AvmTest { + // Libs + use dep::aztec::protocol_types::{ + address::AztecAddress, + }; + + #[aztec(private)] + fn constructor() {} + + // Function name prefix "avm_" flags it for transpilation + unconstrained fn avm_addArgsReturn(argA: Field, argB: Field) -> pub Field { + argA + argB + } + + // Function required for all contracts + unconstrained fn compute_note_hash_and_nullifier( + _contract_address: AztecAddress, + _nonce: Field, + _storage_slot: Field, + _serialized_note: [Field; 1] + ) -> pub [Field; 4] { + [0, 0, 0, 0] + } +} diff --git a/yarn-project/noir-contracts/contracts/docs_example_contract/src/main.nr b/yarn-project/noir-contracts/contracts/docs_example_contract/src/main.nr index 9300ef77ecdb..ee28961aefdb 100644 --- a/yarn-project/noir-contracts/contracts/docs_example_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/docs_example_contract/src/main.nr @@ -18,9 +18,6 @@ contract DocsExample { address::AztecAddress, }; use dep::aztec::{ - oracle::{ - debug_log::debug_log_format, - }, note::{ note_header::NoteHeader, note_getter_options::{NoteGetterOptions, Comparator}, @@ -28,7 +25,7 @@ contract DocsExample { utils as note_utils, }, context::{PrivateContext, PublicContext, Context}, - state_vars::{map::Map, public_state::PublicState,singleton::Singleton, immutable_singleton::ImmutableSingleton, set::Set}, + state_vars::{map::Map, public_state::PublicState,singleton::Singleton, immutable_singleton::ImmutableSingleton, set::Set, stable_public_state::StablePublicState}, }; // how to import methods from other files/folders within your workspace use crate::options::create_account_card_getter_options; @@ -49,6 +46,9 @@ contract DocsExample { // docs:end:storage-map-singleton-declaration test: Set, imm_singleton: ImmutableSingleton, + // docs:start:start_vars_stable + stable_value: StablePublicState, + // docs:end:start_vars_stable } impl Storage { @@ -59,20 +59,21 @@ contract DocsExample { 1 ), // docs:start:start_vars_singleton - legendary_card: Singleton::new(context, 2), + legendary_card: Singleton::new(context, 3), // docs:end:start_vars_singleton // just used for docs example (not for game play): // docs:start:state_vars-MapSingleton profiles: Map::new( context, - 3, + 4, |context, slot| { Singleton::new(context, slot) }, ), // docs:end:state_vars-MapSingleton - test: Set::new(context, 4), - imm_singleton: ImmutableSingleton::new(context, 4), + test: Set::new(context, 5), + imm_singleton: ImmutableSingleton::new(context, 6), + stable_value: StablePublicState::new(context, 7), } } } @@ -80,6 +81,25 @@ contract DocsExample { #[aztec(private)] fn constructor() {} + #[aztec(public)] + fn initialize_stable(points: u8) { + let mut new_leader = Leader { account: context.msg_sender(), points }; + storage.stable_value.initialize(new_leader); + } + + #[aztec(private)] + fn match_stable(account: AztecAddress, points: u8) { + let expected = Leader { account, points }; + let read = storage.stable_value.read_private(); + + assert(read.account == expected.account, "Invalid account"); + assert(read.points == expected.points, "Invalid points"); + } + + unconstrained fn get_stable() -> pub Leader { + storage.stable_value.read_public() + } + #[aztec(private)] fn initialize_immutable_singleton(randomness: Field, points: u8) { let mut new_card = CardNote::new(points, randomness, context.msg_sender()); diff --git a/yarn-project/noir-contracts/contracts/docs_example_contract/src/types/card_note.nr b/yarn-project/noir-contracts/contracts/docs_example_contract/src/types/card_note.nr index 0f38b9bc34a0..912bb81e5682 100644 --- a/yarn-project/noir-contracts/contracts/docs_example_contract/src/types/card_note.nr +++ b/yarn-project/noir-contracts/contracts/docs_example_contract/src/types/card_note.nr @@ -2,7 +2,7 @@ use dep::aztec::{ note::{ note_header::NoteHeader, note_interface::NoteInterface, - utils::compute_note_hash_for_read_or_nullify, + utils::compute_note_hash_for_consumption, }, oracle::{ nullifier_key::get_nullifier_secret_key, @@ -59,16 +59,12 @@ impl Deserialize for CardNote { } impl NoteInterface for CardNote { - fn compute_note_hash(self) -> Field { - pedersen_hash([ - self.points as Field, - self.randomness, - self.owner.to_field(), - ],0) + fn compute_note_content_hash(self) -> Field { + pedersen_hash(self.serialize(), 0) } fn compute_nullifier(self, context: &mut PrivateContext) -> Field { - let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(self); + let note_hash_for_nullify = compute_note_hash_for_consumption(self); let secret = context.request_nullifier_secret_key(self.owner); pedersen_hash([ note_hash_for_nullify, @@ -78,7 +74,7 @@ impl NoteInterface for CardNote { } fn compute_nullifier_without_context(self) -> Field { - let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(self); + let note_hash_for_nullify = compute_note_hash_for_consumption(self); let secret = get_nullifier_secret_key(self.owner); pedersen_hash([ note_hash_for_nullify, diff --git a/yarn-project/noir-contracts/contracts/ecdsa_account_contract/src/ecdsa_public_key_note.nr b/yarn-project/noir-contracts/contracts/ecdsa_account_contract/src/ecdsa_public_key_note.nr index 28d7c84fe795..5370456c7a97 100644 --- a/yarn-project/noir-contracts/contracts/ecdsa_account_contract/src/ecdsa_public_key_note.nr +++ b/yarn-project/noir-contracts/contracts/ecdsa_account_contract/src/ecdsa_public_key_note.nr @@ -3,7 +3,7 @@ use dep::aztec::{ note::{ note_header::NoteHeader, note_interface::NoteInterface, - utils::compute_note_hash_for_read_or_nullify, + utils::compute_note_hash_for_consumption, }, oracle::{ nullifier_key::get_nullifier_secret_key, @@ -75,13 +75,13 @@ impl Deserialize for EcdsaPublicKeyNote { } impl NoteInterface for EcdsaPublicKeyNote { - fn compute_note_hash(note: EcdsaPublicKeyNote) -> Field { + fn compute_note_content_hash(note: EcdsaPublicKeyNote) -> Field { // TODO(#1205) Should use a non-zero generator index. pedersen_hash(note.serialize(), 0) } fn compute_nullifier(self, context: &mut PrivateContext) -> Field { - let unique_siloed_note_hash = compute_note_hash_for_read_or_nullify(self); + let unique_siloed_note_hash = compute_note_hash_for_consumption(self); let secret = context.request_nullifier_secret_key(self.owner); // TODO(#1205) Should use a non-zero generator index. pedersen_hash([ @@ -92,7 +92,7 @@ impl NoteInterface for EcdsaPublicKeyNote { } fn compute_nullifier_without_context(self) -> Field { - let unique_siloed_note_hash = compute_note_hash_for_read_or_nullify(self); + let unique_siloed_note_hash = compute_note_hash_for_consumption(self); let secret = get_nullifier_secret_key(self.owner); // TODO(#1205) Should use a non-zero generator index. pedersen_hash([ diff --git a/yarn-project/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr b/yarn-project/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr index f2db94a6a2dc..305176fd3df6 100644 --- a/yarn-project/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr @@ -52,6 +52,7 @@ contract InclusionProofs { struct Storage { private_values: Map>, public_value: PublicState, + public_unused_value: PublicState, } impl Storage { @@ -68,6 +69,10 @@ contract InclusionProofs { context, 2, // Storage slot ), + public_unused_value: PublicState::new( + context, + 3, // Storage slot + ), } } } @@ -98,7 +103,7 @@ contract InclusionProofs { fn test_note_inclusion_proof( owner: AztecAddress, block_number: u32, // The block at which we'll prove that the note exists - // Value bellow is only used when the note is not found --> used to test the note inclusion failure case (it + // Value below is only used when the note is not found --> used to test the note inclusion failure case (it // allows me to pass in random value of note nullifier - I cannot add and fetch a random note from PXE because // PXE performs note commitment inclusion check when you add a new note). spare_commitment: Field @@ -127,7 +132,7 @@ contract InclusionProofs { fn test_nullifier_non_inclusion_proof( owner: AztecAddress, block_number: u32, // The block at which we'll prove that the nullifier does not exists - // Value bellow is only used when the note is not found --> used to test the nullifier non-inclusion failure + // Value below is only used when the note is not found --> used to test the nullifier non-inclusion failure // case (it allows me to pass in random value of note nullifier - I cannot add and fetch a random note from PXE // because PXE performs note commitment inclusion check when you add a new note). spare_nullifier: Field @@ -193,6 +198,17 @@ contract InclusionProofs { // docs:end:prove_nullifier_inclusion } + #[aztec(private)] + fn test_public_unused_value_inclusion_proof(block_number: u32 // The block at which we'll prove that the public value exists + ) { + prove_public_value_inclusion( + 0, + storage.public_unused_value.storage_slot, + block_number, + context + ); + } + #[aztec(private)] fn test_public_value_inclusion_proof( public_value: Field, diff --git a/yarn-project/noir-contracts/contracts/reader_contract/src/main.nr b/yarn-project/noir-contracts/contracts/reader_contract/src/main.nr index b8c65f95c67c..928d80b908b0 100644 --- a/yarn-project/noir-contracts/contracts/reader_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/reader_contract/src/main.nr @@ -11,7 +11,7 @@ contract Reader { fn constructor() {} #[aztec(public)] - fn check_name(who: AztecAddress, what: str<31>) { + fn check_name_public(who: AztecAddress, what: str<31>) { let selector = FunctionSelector::from_signature("public_get_name()"); let ret = context.call_public_function_no_args(who, selector); let name = FieldCompressedString::from_field(ret[0]); @@ -19,8 +19,17 @@ contract Reader { assert(name.is_eq(_what)); } + #[aztec(private)] + fn check_name_private(who: AztecAddress, what: str<31>) { + let selector = FunctionSelector::from_signature("private_get_name()"); + let ret = context.call_private_function_no_args(who, selector); + let name = FieldCompressedString::from_field(ret[0]); + let _what = FieldCompressedString::from_string(what); + assert(name.is_eq(_what)); + } + #[aztec(public)] - fn check_symbol(who: AztecAddress, what: str<31>) { + fn check_symbol_public(who: AztecAddress, what: str<31>) { let selector = FunctionSelector::from_signature("public_get_symbol()"); let ret = context.call_public_function_no_args(who, selector); let symbol = FieldCompressedString::from_field(ret[0]); @@ -28,10 +37,26 @@ contract Reader { assert(symbol.is_eq(_what)); } + #[aztec(private)] + fn check_symbol_private(who: AztecAddress, what: str<31>) { + let selector = FunctionSelector::from_signature("private_get_symbol()"); + let ret = context.call_private_function_no_args(who, selector); + let symbol = FieldCompressedString::from_field(ret[0]); + let _what = FieldCompressedString::from_string(what); + assert(symbol.is_eq(_what)); + } + #[aztec(public)] - fn check_decimals(who: AztecAddress, what: u8) { + fn check_decimals_public(who: AztecAddress, what: u8) { let selector = FunctionSelector::from_signature("public_get_decimals()"); let ret = context.call_public_function_no_args(who, selector); assert(ret[0] as u8 == what); } + + #[aztec(private)] + fn check_decimals_private(who: AztecAddress, what: u8) { + let selector = FunctionSelector::from_signature("private_get_decimals()"); + let ret = context.call_private_function_no_args(who, selector); + assert(ret[0] as u8 == what); + } } diff --git a/yarn-project/noir-contracts/contracts/schnorr_account_contract/src/public_key_note.nr b/yarn-project/noir-contracts/contracts/schnorr_account_contract/src/public_key_note.nr index ff9f1b78be0c..6bfc37f4c90e 100644 --- a/yarn-project/noir-contracts/contracts/schnorr_account_contract/src/public_key_note.nr +++ b/yarn-project/noir-contracts/contracts/schnorr_account_contract/src/public_key_note.nr @@ -2,7 +2,7 @@ use dep::aztec::{ note::{ note_header::NoteHeader, note_interface::NoteInterface, - utils::compute_note_hash_for_read_or_nullify, + utils::compute_note_hash_for_consumption, }, hash::pedersen_hash, oracle::{ @@ -48,7 +48,7 @@ impl Deserialize for PublicKeyNote { impl NoteInterface for PublicKeyNote { fn compute_nullifier(self, context: &mut PrivateContext) -> Field { - let unique_siloed_note_hash = compute_note_hash_for_read_or_nullify(self); + let unique_siloed_note_hash = compute_note_hash_for_consumption(self); let secret = context.request_nullifier_secret_key(self.owner); // TODO(#1205) Should use a non-zero generator index. pedersen_hash([ @@ -59,7 +59,7 @@ impl NoteInterface for PublicKeyNote { } fn compute_nullifier_without_context(self) -> Field { - let unique_siloed_note_hash = compute_note_hash_for_read_or_nullify(self); + let unique_siloed_note_hash = compute_note_hash_for_consumption(self); let secret = get_nullifier_secret_key(self.owner); // TODO(#1205) Should use a non-zero generator index. pedersen_hash([ @@ -69,7 +69,7 @@ impl NoteInterface for PublicKeyNote { ],0) } - fn compute_note_hash(note: PublicKeyNote) -> Field { + fn compute_note_content_hash(note: PublicKeyNote) -> Field { // TODO(#1205) Should use a non-zero generator index. pedersen_hash(note.serialize(), 0) } diff --git a/yarn-project/noir-contracts/contracts/schnorr_hardcoded_account_contract/src/main.nr b/yarn-project/noir-contracts/contracts/schnorr_hardcoded_account_contract/src/main.nr index faef2015a90e..6226024e39f4 100644 --- a/yarn-project/noir-contracts/contracts/schnorr_hardcoded_account_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/schnorr_hardcoded_account_contract/src/main.nr @@ -5,7 +5,6 @@ contract SchnorrHardcodedAccount { use dep::aztec::{ abi::{ PrivateCircuitPublicInputs, PrivateContextInputs, Hasher }, context::PrivateContext, - types::vec::BoundedVec, }; use dep::authwit:: { diff --git a/yarn-project/noir-contracts/contracts/test_contract/src/main.nr b/yarn-project/noir-contracts/contracts/test_contract/src/main.nr index 14d7a7f6ee62..791016b7a8d1 100644 --- a/yarn-project/noir-contracts/contracts/test_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/test_contract/src/main.nr @@ -41,7 +41,6 @@ contract Test { }, state_vars::immutable_singleton::ImmutableSingleton, log::emit_unencrypted_log_from_private, - types::vec::BoundedVec, }; use dep::token_portal_content_hash_lib::{get_mint_private_content_hash, get_mint_public_content_hash}; use dep::field_note::field_note::FieldNote; @@ -193,7 +192,7 @@ contract Test { args.push(a_field); args.push(a_bool as Field); args.push(a_number as Field); - args.push_array(an_array); + args.extend_from_array(an_array); args.push(a_struct.amount); args.push(a_struct.secret_hash); args.push(a_deep_struct.a_field); diff --git a/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types/balance_set.nr b/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types/balance_set.nr index ae6536ff4a23..a6952f87c1d2 100644 --- a/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types/balance_set.nr +++ b/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types/balance_set.nr @@ -14,7 +14,6 @@ use dep::aztec::note::{ use dep::aztec::note::{ note_header::NoteHeader, note_interface::NoteInterface, - utils::compute_note_hash_for_read_or_nullify, }; use crate::types::token_note::TokenNote; diff --git a/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr b/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr index 235c121637af..e405a600131b 100644 --- a/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr +++ b/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr @@ -4,7 +4,7 @@ use dep::aztec::{ note::{ note_header::NoteHeader, note_interface::NoteInterface, - utils::compute_note_hash_for_read_or_nullify, + utils::compute_note_hash_for_consumption, }, context::PrivateContext, state_vars::set::Set, @@ -54,18 +54,14 @@ impl Deserialize for TokenNote { } impl NoteInterface for TokenNote { - fn compute_note_hash(self) -> Field { + fn compute_note_content_hash(self) -> Field { // TODO(#1205) Should use a non-zero generator index. - pedersen_hash([ - self.amount.value as Field, - self.owner.to_field(), - self.randomness, - ],0) + pedersen_hash(self.serialize(), 0) } // docs:start:nullifier fn compute_nullifier(self, context: &mut PrivateContext) -> Field { - let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(self); + let note_hash_for_nullify = compute_note_hash_for_consumption(self); let secret = context.request_nullifier_secret_key(self.owner); // TODO(#1205) Should use a non-zero generator index. pedersen_hash([ @@ -77,7 +73,7 @@ impl NoteInterface for TokenNote { // docs:end:nullifier fn compute_nullifier_without_context(self) -> Field { - let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(self); + let note_hash_for_nullify = compute_note_hash_for_consumption(self); let secret = get_nullifier_secret_key(self.owner); // TODO(#1205) Should use a non-zero generator index. pedersen_hash([ diff --git a/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types/transparent_note.nr b/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types/transparent_note.nr index 0d5922db6954..a008ddb6b3ce 100644 --- a/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types/transparent_note.nr +++ b/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types/transparent_note.nr @@ -3,7 +3,7 @@ use dep::aztec::{ note::{ note_header::NoteHeader, note_interface::NoteInterface, - utils::compute_note_hash_for_read_or_nullify, + utils::compute_note_hash_for_consumption, }, hash::{compute_secret_hash, pedersen_hash}, context::PrivateContext, @@ -42,12 +42,9 @@ impl Deserialize for TransparentNote { impl NoteInterface for TransparentNote { - fn compute_note_hash(self) -> Field { + fn compute_note_content_hash(self) -> Field { // TODO(#1205) Should use a non-zero generator index. - pedersen_hash([ - self.amount, - self.secret_hash, - ],0) + pedersen_hash(self.serialize(), 0) } fn compute_nullifier(self, _context: &mut PrivateContext) -> Field { @@ -55,8 +52,7 @@ impl NoteInterface for TransparentNote { } fn compute_nullifier_without_context(self) -> Field { - // TODO(#1386): should use `compute_note_hash_for_read_or_nullify` once public functions inject nonce! - let siloed_note_hash = compute_note_hash_for_read_or_nullify(self); + let siloed_note_hash = compute_note_hash_for_consumption(self); // TODO(#1205) Should use a non-zero generator index. pedersen_hash([self.secret, siloed_note_hash],0) } diff --git a/yarn-project/noir-contracts/contracts/token_contract/src/main.nr b/yarn-project/noir-contracts/contracts/token_contract/src/main.nr index 06c09e335e07..9815719c26f9 100644 --- a/yarn-project/noir-contracts/contracts/token_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/token_contract/src/main.nr @@ -24,7 +24,7 @@ contract Token { }, context::{PrivateContext, PublicContext, Context}, hash::{compute_secret_hash}, - state_vars::{map::Map, public_state::PublicState, set::Set}, + state_vars::{map::Map, public_state::PublicState, stable_public_state::StablePublicState, set::Set}, protocol_types::{ type_serialization::{ FIELD_SERIALIZED_LEN, @@ -69,9 +69,11 @@ contract Token { pending_shields: Set, // docs:end:storage_pending_shields public_balances: Map>, - symbol: PublicState, - name: PublicState, - decimals: PublicState, + symbol: StablePublicState, + name: StablePublicState, + // docs:start:storage_decimals + decimals: StablePublicState, + // docs:end:storage_decimals } // docs:end:storage_struct @@ -117,18 +119,20 @@ contract Token { ) }, ), - symbol: PublicState::new( + symbol: StablePublicState::new( context, 7, ), - name: PublicState::new( + name: StablePublicState::new( context, 8, ), - decimals: PublicState::new( + // docs:start:storage_decimals_init + decimals: StablePublicState::new( context, 9, ), + // docs:end:storage_decimals_init } } } @@ -160,29 +164,48 @@ contract Token { #[aztec(public)] fn public_get_name() -> pub FieldCompressedString { - storage.name.read() + storage.name.read_public() + } + + #[aztec(private)] + fn private_get_name() -> pub FieldCompressedString { + storage.name.read_private() } unconstrained fn un_get_name() -> pub [u8; 31] { - storage.name.read().to_bytes() + storage.name.read_public().to_bytes() } #[aztec(public)] fn public_get_symbol() -> pub FieldCompressedString { - storage.symbol.read() + storage.symbol.read_public() + } + + #[aztec(private)] + fn private_get_symbol() -> pub FieldCompressedString { + storage.symbol.read_private() } unconstrained fn un_get_symbol() -> pub [u8; 31] { - storage.symbol.read().to_bytes() + storage.symbol.read_public().to_bytes() } #[aztec(public)] fn public_get_decimals() -> pub u8 { - storage.decimals.read() + // docs:start:read_decimals_public + storage.decimals.read_public() + // docs:end:read_decimals_public + } + + #[aztec(private)] + fn private_get_decimals() -> pub u8 { + // docs:start:read_decimals_private + storage.decimals.read_private() + // docs:end:read_decimals_private } unconstrained fn un_get_decimals() -> pub u8 { - storage.decimals.read() + storage.decimals.read_public() } // docs:start:set_minter @@ -366,9 +389,11 @@ contract Token { assert(!new_admin.is_zero(), "invalid admin"); storage.admin.write(new_admin); storage.minters.at(new_admin).write(true); - storage.name.write(name); - storage.symbol.write(symbol); - storage.decimals.write(decimals); + storage.name.initialize(name); + storage.symbol.initialize(symbol); + // docs:start:initialize_decimals + storage.decimals.initialize(decimals); + // docs:end:initialize_decimals } // docs:end:initialize diff --git a/yarn-project/noir-contracts/contracts/token_contract/src/types/balance_set.nr b/yarn-project/noir-contracts/contracts/token_contract/src/types/balance_set.nr index 707c73461af7..e84b4cfd1109 100644 --- a/yarn-project/noir-contracts/contracts/token_contract/src/types/balance_set.nr +++ b/yarn-project/noir-contracts/contracts/token_contract/src/types/balance_set.nr @@ -15,8 +15,7 @@ use dep::aztec::note::{ }; use dep::aztec::note::{ note_header::NoteHeader, - note_interface::NoteInterface, - utils::compute_note_hash_for_read_or_nullify, + note_interface::NoteInterface }; use crate::types::token_note::TokenNote; diff --git a/yarn-project/noir-contracts/contracts/token_contract/src/types/token_note.nr b/yarn-project/noir-contracts/contracts/token_contract/src/types/token_note.nr index ad0567bf28ef..17e77155a282 100644 --- a/yarn-project/noir-contracts/contracts/token_contract/src/types/token_note.nr +++ b/yarn-project/noir-contracts/contracts/token_contract/src/types/token_note.nr @@ -8,7 +8,7 @@ use dep::aztec::{ note::{ note_header::NoteHeader, note_interface::NoteInterface, - utils::compute_note_hash_for_read_or_nullify, + utils::compute_note_hash_for_consumption, }, context::PrivateContext, state_vars::set::Set, @@ -61,18 +61,14 @@ impl Deserialize for TokenNote { } impl NoteInterface for TokenNote { - fn compute_note_hash(self) -> Field { + fn compute_note_content_hash(self) -> Field { // TODO(#1205) Should use a non-zero generator index. - pedersen_hash([ - self.amount.value as Field, - self.owner.to_field(), - self.randomness, - ],0) + pedersen_hash(self.serialize(), 0) } // docs:start:nullifier fn compute_nullifier(self, context: &mut PrivateContext) -> Field { - let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(self); + let note_hash_for_nullify = compute_note_hash_for_consumption(self); let secret = context.request_nullifier_secret_key(self.owner); // TODO(#1205) Should use a non-zero generator index. pedersen_hash([ @@ -84,7 +80,7 @@ impl NoteInterface for TokenNote { // docs:end:nullifier fn compute_nullifier_without_context(self) -> Field { - let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(self); + let note_hash_for_nullify = compute_note_hash_for_consumption(self); let secret = get_nullifier_secret_key(self.owner); // TODO(#1205) Should use a non-zero generator index. pedersen_hash([ diff --git a/yarn-project/noir-contracts/contracts/token_contract/src/types/transparent_note.nr b/yarn-project/noir-contracts/contracts/token_contract/src/types/transparent_note.nr index 0c5ea2a724e0..4ee641794c4e 100644 --- a/yarn-project/noir-contracts/contracts/token_contract/src/types/transparent_note.nr +++ b/yarn-project/noir-contracts/contracts/token_contract/src/types/transparent_note.nr @@ -3,7 +3,7 @@ use dep::aztec::{ note::{ note_header::NoteHeader, note_interface::NoteInterface, - utils::compute_note_hash_for_read_or_nullify, + utils::compute_note_hash_for_consumption, }, hash::{compute_secret_hash, pedersen_hash}, context::PrivateContext, @@ -44,17 +44,13 @@ impl Empty for TransparentNote { fn empty() -> Self { TransparentNote::new(0, 0) } - } impl NoteInterface for TransparentNote { - fn compute_note_hash(self) -> Field { + fn compute_note_content_hash(self) -> Field { // TODO(#1205) Should use a non-zero generator index. - pedersen_hash([ - self.amount, - self.secret_hash, - ],0) + pedersen_hash(self.serialize(), 0) } fn compute_nullifier(self, _context: &mut PrivateContext) -> Field { @@ -62,8 +58,7 @@ impl NoteInterface for TransparentNote { } fn compute_nullifier_without_context(self) -> Field { - // TODO(#1386): should use `compute_note_hash_for_read_or_nullify` once public functions inject nonce! - let siloed_note_hash = compute_note_hash_for_read_or_nullify(self); + let siloed_note_hash = compute_note_hash_for_consumption(self); // TODO(#1205) Should use a non-zero generator index. pedersen_hash([self.secret, siloed_note_hash],0) } diff --git a/yarn-project/noir-contracts/package.json b/yarn-project/noir-contracts/package.json index 6845b1f90ff7..38bf26e463ba 100644 --- a/yarn-project/noir-contracts/package.json +++ b/yarn-project/noir-contracts/package.json @@ -14,7 +14,7 @@ "formatting": "run -T prettier --check ./src && run -T eslint ./src", "formatting:fix": "run -T eslint --fix ./src && run -T prettier -w ./src", "test": "NODE_NO_WARNINGS=1 node --experimental-vm-modules $(yarn bin jest) --passWithNoTests", - "build:contracts": "./scripts/compile.sh && ./scripts/generate-types.sh" + "build:contracts": "./scripts/compile.sh && ./scripts/transpile.sh && ./scripts/generate-types.sh" }, "inherits": [ "../package.common.json", diff --git a/yarn-project/noir-contracts/package.local.json b/yarn-project/noir-contracts/package.local.json index 6fb912b0da85..9ea766d3f113 100644 --- a/yarn-project/noir-contracts/package.local.json +++ b/yarn-project/noir-contracts/package.local.json @@ -1,7 +1,7 @@ { "scripts": { "build": "yarn clean && yarn build:contracts && tsc -b", - "build:contracts": "./scripts/compile.sh && ./scripts/generate-types.sh", + "build:contracts": "./scripts/compile.sh && ./scripts/transpile.sh && ./scripts/generate-types.sh", "clean": "rm -rf ./dest .tsbuildinfo ./src ./target" } } diff --git a/yarn-project/noir-contracts/scripts/transpile.sh b/yarn-project/noir-contracts/scripts/transpile.sh new file mode 100755 index 000000000000..c330ee0f6546 --- /dev/null +++ b/yarn-project/noir-contracts/scripts/transpile.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -euo pipefail + +echo "Transpiling contracts..." +for contract_json in target/avm_test_*.json; do + echo Transpiling $contract_json... + ../../avm-transpiler/target/release/avm-transpiler $contract_json $contract_json +done \ No newline at end of file diff --git a/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/common.nr b/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/common.nr index bdc0c0231dfe..e2338ab01040 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/common.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/common.nr @@ -45,7 +45,6 @@ use dep::types::{ array_to_bounded_vec, validate_array, }, - bounded_vec::BoundedVec, }, traits::{is_empty, is_empty_array}, }; @@ -53,7 +52,7 @@ use dep::types::{ pub fn validate_arrays(app_public_inputs: PrivateCircuitPublicInputs) { // Each of the following arrays is expected to be zero-padded. // In addition, some of the following arrays (new_commitments, etc...) are passed - // to push_array_to_array() routines which rely on the passed arrays to be well-formed. + // to extend_from_array_to_array() routines which rely on the passed arrays to be well-formed. validate_array(app_public_inputs.return_values); validate_array(app_public_inputs.read_requests); @@ -200,7 +199,7 @@ pub fn update_end_values(private_call: PrivateCallData, public_inputs: &mut Kern ) } } - public_inputs.end.read_requests.push_vec(siloed_read_requests); + public_inputs.end.read_requests.extend_from_bounded_vec(siloed_read_requests); // Nullifier key validation requests. for i in 0..MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL { @@ -231,7 +230,7 @@ pub fn update_end_values(private_call: PrivateCallData, public_inputs: &mut Kern ); } } - public_inputs.end.new_nullifiers.push_vec(siloed_new_nullifiers); + public_inputs.end.new_nullifiers.extend_from_bounded_vec(siloed_new_nullifiers); // commitments let mut siloed_new_commitments: BoundedVec = BoundedVec::new(SideEffect::empty()); @@ -243,7 +242,7 @@ pub fn update_end_values(private_call: PrivateCallData, public_inputs: &mut Kern ); } } - public_inputs.end.new_commitments.push_vec(siloed_new_commitments); + public_inputs.end.new_commitments.extend_from_bounded_vec(siloed_new_commitments); // Call stacks // Private call stack. @@ -253,7 +252,7 @@ pub fn update_end_values(private_call: PrivateCallData, public_inputs: &mut Kern private_call_public_inputs.private_call_stack_hashes, private_call ); - public_inputs.end.private_call_stack.push_vec(private_call_stack); + public_inputs.end.private_call_stack.extend_from_bounded_vec(private_call_stack); // Public call stack. let public_call_stack = array_to_bounded_vec(private_call.public_call_stack); validate_call_requests( @@ -261,7 +260,7 @@ pub fn update_end_values(private_call: PrivateCallData, public_inputs: &mut Kern private_call_public_inputs.public_call_stack_hashes, private_call ); - public_inputs.end.public_call_stack.push_vec(public_call_stack); + public_inputs.end.public_call_stack.extend_from_bounded_vec(public_call_stack); // new l2 to l1 messages let portal_contract_address = private_call.portal_contract_address; @@ -280,7 +279,7 @@ pub fn update_end_values(private_call: PrivateCallData, public_inputs: &mut Kern new_l2_to_l1_msgs_to_insert.push(new_l2_to_l1_msgs) } } - public_inputs.end.new_l2_to_l1_msgs.push_vec(new_l2_to_l1_msgs_to_insert); + public_inputs.end.new_l2_to_l1_msgs.extend_from_bounded_vec(new_l2_to_l1_msgs_to_insert); // logs hashes // See the following thread if not clear: diff --git a/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/private_kernel_init.nr b/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/private_kernel_init.nr index 6f7fe47d49f6..04d5d60c0e73 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/private_kernel_init.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/private_kernel_init.nr @@ -264,7 +264,7 @@ mod tests { fn input_validation_malformed_arrays_return_values() { let mut builder = PrivateKernelInitInputsBuilder::new_constructor(); - builder.private_call.public_inputs.return_values.push_array([0, 9123]); + builder.private_call.public_inputs.return_values.extend_from_array([0, 9123]); builder.failed(); } @@ -273,7 +273,7 @@ mod tests { fn input_validation_malformed_arrays_read_requests() { let mut builder = PrivateKernelInitInputsBuilder::new_constructor(); - builder.private_call.public_inputs.read_requests.push_array( + builder.private_call.public_inputs.read_requests.extend_from_array( [ SideEffect { value: 0, counter: 0 }, SideEffect { value: 9123, counter: 1 } @@ -287,7 +287,7 @@ mod tests { fn input_validation_malformed_arrays_commitments() { let mut builder = PrivateKernelInitInputsBuilder::new_constructor(); - builder.private_call.public_inputs.new_commitments.push_array( + builder.private_call.public_inputs.new_commitments.extend_from_array( [ SideEffect { value: 0, counter: 0 }, SideEffect { value: 9123, counter: 1 } @@ -301,7 +301,7 @@ mod tests { fn input_validation_malformed_arrays_nullifiers() { let mut builder = PrivateKernelInitInputsBuilder::new_constructor(); - builder.private_call.public_inputs.new_nullifiers.push_array( + builder.private_call.public_inputs.new_nullifiers.extend_from_array( [ SideEffectLinkedToNoteHash { value: 0, note_hash: 0, counter: 0 }, SideEffectLinkedToNoteHash { value: 9123, note_hash: 0, counter: 1 } @@ -315,7 +315,7 @@ mod tests { fn input_validation_malformed_arrays_private_call_stack() { let mut builder = PrivateKernelInitInputsBuilder::new_constructor(); - builder.private_call.public_inputs.private_call_stack_hashes.push_array([0, 9123]); + builder.private_call.public_inputs.private_call_stack_hashes.extend_from_array([0, 9123]); builder.failed(); } @@ -324,7 +324,7 @@ mod tests { fn input_validation_malformed_arrays_public_call_stack() { let mut builder = PrivateKernelInitInputsBuilder::new_constructor(); - builder.private_call.public_inputs.public_call_stack_hashes.push_array([0, 9123]); + builder.private_call.public_inputs.public_call_stack_hashes.extend_from_array([0, 9123]); builder.failed(); } @@ -333,7 +333,7 @@ mod tests { fn input_validation_malformed_arrays_new_l2_to_l1_msgs() { let mut builder = PrivateKernelInitInputsBuilder::new_constructor(); - builder.private_call.public_inputs.new_l2_to_l1_msgs.push_array([0, 9123]); + builder.private_call.public_inputs.new_l2_to_l1_msgs.extend_from_array([0, 9123]); builder.failed(); } diff --git a/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/private_kernel_inner.nr b/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/private_kernel_inner.nr index 06d07b677f9a..47e378306c34 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/private_kernel_inner.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/private_kernel_inner.nr @@ -97,7 +97,6 @@ mod tests { hash::compute_logs_hash, utils::{ arrays::array_length, - bounded_vec::BoundedVec, }, }; @@ -441,7 +440,7 @@ mod tests { fn input_validation_malformed_arrays_return_values() { let mut builder = PrivateKernelInnerInputsBuilder::new(); - builder.private_call.public_inputs.return_values.push_array([0, 553]); + builder.private_call.public_inputs.return_values.extend_from_array([0, 553]); builder.failed(); } @@ -450,7 +449,7 @@ mod tests { fn input_validation_malformed_arrays_read_requests() { let mut builder = PrivateKernelInnerInputsBuilder::new(); - builder.private_call.public_inputs.read_requests.push_array( + builder.private_call.public_inputs.read_requests.extend_from_array( [ SideEffect { value: 0, counter: 0 }, SideEffect { value: 9123, counter: 1 } @@ -464,7 +463,7 @@ mod tests { fn input_validation_malformed_arrays_commitments() { let mut builder = PrivateKernelInnerInputsBuilder::new(); - builder.private_call.public_inputs.new_commitments.push_array( + builder.private_call.public_inputs.new_commitments.extend_from_array( [ SideEffect { value: 0, counter: 0 }, SideEffect { value: 9123, counter: 1 } @@ -478,7 +477,7 @@ mod tests { fn input_validation_malformed_arrays_nullifiers() { let mut builder = PrivateKernelInnerInputsBuilder::new(); - builder.private_call.public_inputs.new_nullifiers.push_array( + builder.private_call.public_inputs.new_nullifiers.extend_from_array( [ SideEffectLinkedToNoteHash { value: 0, note_hash: 0, counter: 0 }, SideEffectLinkedToNoteHash { value: 12, note_hash: 0, counter: 1 } @@ -492,7 +491,7 @@ mod tests { fn input_validation_malformed_arrays_private_call_stack() { let mut builder = PrivateKernelInnerInputsBuilder::new(); - builder.private_call.public_inputs.private_call_stack_hashes.push_array([0, 888]); + builder.private_call.public_inputs.private_call_stack_hashes.extend_from_array([0, 888]); builder.failed(); } @@ -501,7 +500,7 @@ mod tests { fn input_validation_malformed_arrays_public_call_stack() { let mut builder = PrivateKernelInnerInputsBuilder::new(); - builder.private_call.public_inputs.public_call_stack_hashes.push_array([0, 888]); + builder.private_call.public_inputs.public_call_stack_hashes.extend_from_array([0, 888]); builder.failed(); } @@ -510,12 +509,12 @@ mod tests { fn input_validation_malformed_arrays_new_l2_to_l1_msgs() { let mut builder = PrivateKernelInnerInputsBuilder::new(); - builder.private_call.public_inputs.new_l2_to_l1_msgs.push_array([0, 888]); + builder.private_call.public_inputs.new_l2_to_l1_msgs.extend_from_array([0, 888]); builder.failed(); } - #[test(should_fail_with = "push_vec out of bounds")] + #[test(should_fail_with = "extend_from_bounded_vec out of bounds")] fn private_kernel_should_fail_if_aggregating_too_many_commitments() { let mut builder = PrivateKernelInnerInputsBuilder::new(); @@ -530,7 +529,7 @@ mod tests { counter: i as u32, }; } - builder.previous_kernel.end.new_commitments.push_array(full_new_commitments); + builder.previous_kernel.end.new_commitments.extend_from_array(full_new_commitments); builder.failed(); } diff --git a/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/private_kernel_ordering.nr b/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/private_kernel_ordering.nr index c937d43d8320..6c6496b7ad51 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/private_kernel_ordering.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/private_kernel_ordering.nr @@ -28,7 +28,6 @@ use dep::types::{ }, utils::{ arrays::{array_length, array_eq}, - bounded_vec::BoundedVec, }, traits::{Empty, is_empty} }; @@ -236,7 +235,6 @@ mod tests { tests::previous_kernel_data_builder::PreviousKernelDataBuilder, utils::{ arrays::{array_eq, array_length}, - bounded_vec::BoundedVec, }, traits::{Empty, is_empty, is_empty_array} }; diff --git a/yarn-project/noir-protocol-circuits/src/crates/public-kernel-lib/src/common.nr b/yarn-project/noir-protocol-circuits/src/crates/public-kernel-lib/src/common.nr index f197513228ff..879295866260 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/public-kernel-lib/src/common.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/public-kernel-lib/src/common.nr @@ -28,7 +28,6 @@ use dep::types::{ hash::{silo_commitment, silo_nullifier, compute_l2_to_l1_hash, accumulate_sha256}, utils::{ arrays::{array_length, array_to_bounded_vec}, - bounded_vec::BoundedVec, }, traits::is_empty_array }; @@ -138,7 +137,7 @@ pub fn update_public_end_values(public_call: PublicCallData, circuit_outputs: &m let public_call_requests = array_to_bounded_vec(public_call.public_call_stack); let hashes = public_call.call_stack_item.public_inputs.public_call_stack_hashes; validate_call_requests(public_call_requests, hashes, public_call); - circuit_outputs.end.public_call_stack.push_vec(public_call_requests); + circuit_outputs.end.public_call_stack.extend_from_bounded_vec(public_call_requests); propagate_new_nullifiers(public_call, circuit_outputs); propagate_new_commitments(public_call, circuit_outputs); @@ -171,7 +170,7 @@ fn propagate_valid_public_data_update_requests( public_data_update_requests.push(public_data_update_request); } } - circuit_outputs.end.public_data_update_requests.push_vec(public_data_update_requests); + circuit_outputs.end.public_data_update_requests.extend_from_bounded_vec(public_data_update_requests); } fn propagate_valid_public_data_reads(public_call: PublicCallData, circuit_outputs: &mut KernelCircuitPublicInputsBuilder) { @@ -190,7 +189,7 @@ fn propagate_valid_public_data_reads(public_call: PublicCallData, circuit_output public_data_reads.push(public_data_read); } } - circuit_outputs.end.public_data_reads.push_vec(public_data_reads); + circuit_outputs.end.public_data_reads.extend_from_bounded_vec(public_data_reads); } fn propagate_new_commitments(public_call: PublicCallData, circuit_outputs: &mut KernelCircuitPublicInputsBuilder) { @@ -207,7 +206,7 @@ fn propagate_new_commitments(public_call: PublicCallData, circuit_outputs: &mut siloed_new_commitments.push(SideEffect { value: siloed_new_commitment, counter: new_commitments[i].counter }); } } - circuit_outputs.end.new_commitments.push_vec(siloed_new_commitments); + circuit_outputs.end.new_commitments.extend_from_bounded_vec(siloed_new_commitments); } fn propagate_new_nullifiers(public_call: PublicCallData, circuit_outputs: &mut KernelCircuitPublicInputsBuilder) { @@ -230,7 +229,7 @@ fn propagate_new_nullifiers(public_call: PublicCallData, circuit_outputs: &mut K } } - circuit_outputs.end.new_nullifiers.push_vec(siloed_new_nullifiers); + circuit_outputs.end.new_nullifiers.extend_from_bounded_vec(siloed_new_nullifiers); } fn propagate_new_l2_to_l1_messages(public_call: PublicCallData, public_inputs: &mut KernelCircuitPublicInputsBuilder) { @@ -254,7 +253,7 @@ fn propagate_new_l2_to_l1_messages(public_call: PublicCallData, public_inputs: & new_l2_to_l1_msgs_to_insert.push(new_l2_to_l1_msgs) } } - public_inputs.end.new_l2_to_l1_msgs.push_vec(new_l2_to_l1_msgs_to_insert); + public_inputs.end.new_l2_to_l1_msgs.extend_from_bounded_vec(new_l2_to_l1_msgs_to_insert); } /** diff --git a/yarn-project/noir-protocol-circuits/src/crates/public-kernel-lib/src/public_kernel_private_previous.nr b/yarn-project/noir-protocol-circuits/src/crates/public-kernel-lib/src/public_kernel_private_previous.nr index ce68efbc17cd..48a51a786db1 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/public-kernel-lib/src/public_kernel_private_previous.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/public-kernel-lib/src/public_kernel_private_previous.nr @@ -86,7 +86,6 @@ mod tests { array_eq, array_length, }, - bounded_vec::BoundedVec, }, }; use dep::types::constants::{ @@ -407,7 +406,7 @@ mod tests { contract_class_id: ContractClassId::from_field(78) } ]; - builder.previous_kernel.end.new_contracts.push_array(new_contracts); + builder.previous_kernel.end.new_contracts.extend_from_array(new_contracts); builder.public_call.append_public_call_requests_for_regular_calls(2); let storage = builder.public_call.public_call_stack.storage; diff --git a/yarn-project/noir-protocol-circuits/src/crates/public-kernel-lib/src/public_kernel_public_previous.nr b/yarn-project/noir-protocol-circuits/src/crates/public-kernel-lib/src/public_kernel_public_previous.nr index 985690d46b99..5f28c5d8dd76 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/public-kernel-lib/src/public_kernel_public_previous.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/public-kernel-lib/src/public_kernel_public_previous.nr @@ -180,7 +180,7 @@ mod tests { SideEffect { value: previous[1].value + 1, counter: 3 }, SideEffect { value: previous[1].value + 2, counter: 4 } ]; - builder.public_call.public_inputs.new_commitments.push_array(current); + builder.public_call.public_inputs.new_commitments.extend_from_array(current); let siloed = current.map(|c: SideEffect| silo_commitment(contract_address, c.value)); let new_commitments = [ previous[0], previous[1], SideEffect { value: siloed[0], counter: 3 }, SideEffect { value: siloed[1], counter: 4 } @@ -247,7 +247,7 @@ mod tests { SideEffectLinkedToNoteHash { value: silo_nullifier(contract_address, current.value), note_hash: current.note_hash, counter: current.counter } ); - builder.public_call.public_inputs.new_nullifiers.push_array(current); + builder.public_call.public_inputs.new_nullifiers.extend_from_array(current); // There are 3 nullifiers in the previous kernel. The first one is the tx nullifier. let new_nullifiers = [previous[0], previous[1], previous[2], siloed[0], siloed[1]]; @@ -265,10 +265,10 @@ mod tests { // Setup 1 new l2 to l1 message on the previous kernel. let previous = [12345]; - builder.previous_kernel.end.new_l2_to_l1_msgs.push_array(previous); + builder.previous_kernel.end.new_l2_to_l1_msgs.extend_from_array(previous); // Setup 1 new l2 to l1 message on the current public inputs. let current = [67890]; - builder.public_call.public_inputs.new_l2_to_l1_msgs.push_array(current); + builder.public_call.public_inputs.new_l2_to_l1_msgs.extend_from_array(current); let tx_context = builder.previous_kernel.tx_context; let version = tx_context.version; let chain_id = tx_context.chain_id; diff --git a/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/base/base_rollup_inputs.nr b/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/base/base_rollup_inputs.nr index c5cf1ee88d4d..0fb57a764527 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/base/base_rollup_inputs.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/base/base_rollup_inputs.nr @@ -339,7 +339,7 @@ impl BaseRollupInputs { // Rebuild the block hash let header = self.kernel_data.public_inputs.constants.historical_header; - let previous_block_hash = header.block_hash(); + let previous_block_hash = header.hash(); let previous_block_hash_witness = self.archive_root_membership_witness; @@ -739,7 +739,7 @@ mod tests { let _nullifier = builder.end.new_nullifiers.pop(); inputs.kernel_data = builder.is_public(); - inputs.pre_existing_blocks[0] = inputs.kernel_data.historical_header.block_hash(); + inputs.pre_existing_blocks[0] = inputs.kernel_data.historical_header.hash(); inputs } diff --git a/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/hash.nr b/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/hash.nr deleted file mode 100644 index 6f314de2f249..000000000000 --- a/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/hash.nr +++ /dev/null @@ -1,13 +0,0 @@ -use dep::types::{ - abis::global_variables::GlobalVariables, - constants::GENERATOR_INDEX__BLOCK_HASH, - state_reference::StateReference, -}; - -pub fn compute_block_hash_with_globals(globals: GlobalVariables, state: StateReference) -> Field { - let inputs = [ - globals.hash(), state.partial.note_hash_tree.root, state.partial.nullifier_tree.root, state.partial.contract_tree.root, state.l1_to_l2_message_tree.root, state.partial.public_data_tree.root - ]; - - dep::std::hash::pedersen_hash_with_separator(inputs, GENERATOR_INDEX__BLOCK_HASH) -} diff --git a/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/lib.nr b/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/lib.nr index 1d0df93cc9df..08534d6cb250 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/lib.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/lib.nr @@ -11,8 +11,6 @@ mod root; mod components; -mod hash; - mod merkle_tree; mod tests; diff --git a/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/root.nr b/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/root.nr index 1fca5912f628..cfd8ad62d4e9 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/root.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/root.nr @@ -13,7 +13,7 @@ use dep::types::{ state_reference::StateReference, utils::uint256::U256, }; -use crate::{components, hash::compute_block_hash_with_globals}; +use crate::components; use crate::merkle_tree::{calculate_subtree, calculate_empty_tree_root}; impl RootRollupInputs { @@ -49,12 +49,15 @@ impl RootRollupInputs { partial: right.end, }; - // Build the block hash for this iteration from the tree roots and global variables - // Then insert the block into the archive tree - let block_hash = compute_block_hash_with_globals( - left.constants.global_variables, + let header = Header { + last_archive: left.constants.last_archive, + body_hash: components::compute_calldata_hash(self.previous_rollup_data), state, - ); + global_variables : left.constants.global_variables + }; + + // Build the block hash for this by hashing the header and then insert the new leaf to archive tree. + let block_hash = header.hash(); // Update the archive let archive = components::insert_subtree_to_snapshot_tree( @@ -65,13 +68,6 @@ impl RootRollupInputs { 0 ); - let header = Header { - last_archive: left.constants.last_archive, - body_hash: components::compute_calldata_hash(self.previous_rollup_data), - state, - global_variables : left.constants.global_variables - }; - RootRollupPublicInputs{ aggregation_object, archive, diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/combined_accumulated_data.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/combined_accumulated_data.nr index 7ada51b7a52d..9e74c0ae1034 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/combined_accumulated_data.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/combined_accumulated_data.nr @@ -8,8 +8,7 @@ use crate::{ public_data_update_request::PublicDataUpdateRequest, side_effect::{SideEffect, SideEffectLinkedToNoteHash}, }, - mocked::AggregationObject, - utils::bounded_vec::BoundedVec + mocked::AggregationObject }; use crate::constants::{ MAX_READ_REQUESTS_PER_TX, diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/global_variables.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/global_variables.nr index 23a43a027034..d5f2857151b9 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/global_variables.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/global_variables.nr @@ -45,20 +45,6 @@ impl Eq for GlobalVariables { } } -// TODO(#3941) This is only used in Header.block_hash() --> Nuke it once that func is updated -impl Hash for GlobalVariables { - fn hash(self) -> Field { - dep::std::hash::pedersen_hash_with_separator([ - self.chain_id, - self.version, - self.block_number, - self.timestamp - ], - GENERATOR_INDEX__GLOBAL_VARIABLES, - ) - } -} - impl Empty for GlobalVariables { fn empty() -> Self { Self { diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/private_circuit_public_inputs.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/private_circuit_public_inputs.nr index 466420cd4164..0df8915d1a4c 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/private_circuit_public_inputs.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/private_circuit_public_inputs.nr @@ -9,7 +9,6 @@ use crate::{ pedersen_hash, }, header::Header, - utils::bounded_vec::BoundedVec, }; use crate::constants::{ MAX_READ_REQUESTS_PER_CALL, @@ -69,26 +68,26 @@ impl Hash for PrivateCircuitPublicInputs { let mut fields: BoundedVec = BoundedVec::new(0); fields.push(self.call_context.hash()); fields.push(self.args_hash); - fields.push_array(self.return_values); + fields.extend_from_array(self.return_values); for i in 0..MAX_READ_REQUESTS_PER_CALL{ - fields.push_array(self.read_requests[i].serialize()); + fields.extend_from_array(self.read_requests[i].serialize()); } for i in 0..MAX_NEW_COMMITMENTS_PER_CALL{ - fields.push_array(self.new_commitments[i].serialize()); + fields.extend_from_array(self.new_commitments[i].serialize()); } for i in 0..MAX_NEW_NULLIFIERS_PER_CALL{ - fields.push_array(self.new_nullifiers[i].serialize()); + fields.extend_from_array(self.new_nullifiers[i].serialize()); } - fields.push_array(self.private_call_stack_hashes); - fields.push_array(self.public_call_stack_hashes); - fields.push_array(self.new_l2_to_l1_msgs); + fields.extend_from_array(self.private_call_stack_hashes); + fields.extend_from_array(self.public_call_stack_hashes); + fields.extend_from_array(self.new_l2_to_l1_msgs); fields.push(self.end_side_effect_counter as Field); - fields.push_array(self.encrypted_logs_hash); - fields.push_array(self.unencrypted_logs_hash); + fields.extend_from_array(self.encrypted_logs_hash); + fields.extend_from_array(self.unencrypted_logs_hash); fields.push(self.encrypted_log_preimages_length); fields.push(self.unencrypted_log_preimages_length); - fields.push_array(self.historical_header.serialize()); + fields.extend_from_array(self.historical_header.serialize()); fields.push(self.contract_deployment_data.hash()); fields.push(self.chain_id); fields.push(self.version); @@ -102,29 +101,29 @@ impl Hash for PrivateCircuitPublicInputs { impl Serialize for PrivateCircuitPublicInputs { fn serialize(self) -> [Field; PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH] { let mut fields: BoundedVec = BoundedVec::new(0); - fields.push_array(self.call_context.serialize()); + fields.extend_from_array(self.call_context.serialize()); fields.push(self.args_hash); - fields.push_array(self.return_values); + fields.extend_from_array(self.return_values); for i in 0..MAX_READ_REQUESTS_PER_CALL{ - fields.push_array(self.read_requests[i].serialize()); + fields.extend_from_array(self.read_requests[i].serialize()); } for i in 0..MAX_NEW_COMMITMENTS_PER_CALL{ - fields.push_array(self.new_commitments[i].serialize()); + fields.extend_from_array(self.new_commitments[i].serialize()); } for i in 0..MAX_NEW_NULLIFIERS_PER_CALL{ - fields.push_array(self.new_nullifiers[i].serialize()); + fields.extend_from_array(self.new_nullifiers[i].serialize()); } - fields.push_array(self.private_call_stack_hashes); - fields.push_array(self.public_call_stack_hashes); - fields.push_array(self.new_l2_to_l1_msgs); + fields.extend_from_array(self.private_call_stack_hashes); + fields.extend_from_array(self.public_call_stack_hashes); + fields.extend_from_array(self.new_l2_to_l1_msgs); fields.push(self.end_side_effect_counter as Field); - fields.push_array(self.encrypted_logs_hash); - fields.push_array(self.unencrypted_logs_hash); + fields.extend_from_array(self.encrypted_logs_hash); + fields.extend_from_array(self.unencrypted_logs_hash); fields.push(self.encrypted_log_preimages_length); fields.push(self.unencrypted_log_preimages_length); - fields.push_array(self.historical_header.serialize()); - fields.push_array(self.contract_deployment_data.serialize()); + fields.extend_from_array(self.historical_header.serialize()); + fields.extend_from_array(self.contract_deployment_data.serialize()); fields.push(self.chain_id); fields.push(self.version); diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/public_circuit_public_inputs.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/public_circuit_public_inputs.nr index 5da7a1de2ffc..6c130e5e65db 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/public_circuit_public_inputs.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/public_circuit_public_inputs.nr @@ -22,7 +22,6 @@ use crate::{ storage_update_request::StorageUpdateRequest, }, header::Header, - utils::bounded_vec::BoundedVec, }; struct PublicCircuitPublicInputs{ @@ -59,14 +58,14 @@ impl PublicCircuitPublicInputs{ let mut inputs: BoundedVec = BoundedVec::new(0); inputs.push(self.call_context.hash()); inputs.push(self.args_hash); - inputs.push_array(self.return_values); + inputs.extend_from_array(self.return_values); for i in 0..MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL { inputs.push(self.contract_storage_update_requests[i].hash()); } for i in 0..MAX_PUBLIC_DATA_READS_PER_CALL { inputs.push(self.contract_storage_reads[i].hash()); } - inputs.push_array(self.public_call_stack_hashes); + inputs.extend_from_array(self.public_call_stack_hashes); for i in 0..MAX_NEW_COMMITMENTS_PER_CALL{ inputs.push(self.new_commitments[i].hash()); @@ -74,10 +73,10 @@ impl PublicCircuitPublicInputs{ for i in 0..MAX_NEW_NULLIFIERS_PER_CALL{ inputs.push(self.new_nullifiers[i].hash()); } - inputs.push_array(self.new_l2_to_l1_msgs); - inputs.push_array(self.unencrypted_logs_hash); + inputs.extend_from_array(self.new_l2_to_l1_msgs); + inputs.extend_from_array(self.unencrypted_logs_hash); inputs.push(self.unencrypted_log_preimages_length); - inputs.push_array(self.historical_header.serialize()); + inputs.extend_from_array(self.historical_header.serialize()); inputs.push(self.prover_address.to_field()); assert_eq(inputs.len(), PUBLIC_CIRCUIT_PUBLIC_INPUTS_HASH_INPUT_LENGTH, "Incorrect number of input fields when hashing PublicCircuitPublicInputs"); @@ -87,27 +86,27 @@ impl PublicCircuitPublicInputs{ pub fn serialize(self) -> [Field; PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH] { let mut fields: BoundedVec = BoundedVec::new(0); - fields.push_array(self.call_context.serialize()); + fields.extend_from_array(self.call_context.serialize()); fields.push(self.args_hash); - fields.push_array(self.return_values); + fields.extend_from_array(self.return_values); for i in 0..MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL { - fields.push_array(self.contract_storage_update_requests[i].serialize()); + fields.extend_from_array(self.contract_storage_update_requests[i].serialize()); } for i in 0..MAX_PUBLIC_DATA_READS_PER_CALL { - fields.push_array(self.contract_storage_reads[i].serialize()); + fields.extend_from_array(self.contract_storage_reads[i].serialize()); } - fields.push_array(self.public_call_stack_hashes); + fields.extend_from_array(self.public_call_stack_hashes); for i in 0..MAX_NEW_COMMITMENTS_PER_CALL{ - fields.push_array(self.new_commitments[i].serialize()); + fields.extend_from_array(self.new_commitments[i].serialize()); } for i in 0..MAX_NEW_NULLIFIERS_PER_CALL{ - fields.push_array(self.new_nullifiers[i].serialize()); + fields.extend_from_array(self.new_nullifiers[i].serialize()); } - fields.push_array(self.new_l2_to_l1_msgs); - fields.push_array(self.unencrypted_logs_hash); + fields.extend_from_array(self.new_l2_to_l1_msgs); + fields.extend_from_array(self.unencrypted_logs_hash); fields.push(self.unencrypted_log_preimages_length); - fields.push_array(self.historical_header.serialize()); + fields.extend_from_array(self.historical_header.serialize()); fields.push(self.prover_address.to_field()); fields.storage } diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/constants.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/constants.nr index 8a65c1d284c4..baeddd68ea5b 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/constants.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/constants.nr @@ -85,7 +85,7 @@ global ARGS_HASH_CHUNK_COUNT: u32 = 16; // NOIR CONSTANTS - constants used only in yarn-packages/noir-contracts // Some are defined here because Noir doesn't yet support globals referencing other globals yet. -// Move these constants to a noir file once the issue bellow is resolved: +// Move these constants to a noir file once the issue below is resolved: // https://github.com/noir-lang/noir/issues/1734 global L1_TO_L2_MESSAGE_LENGTH: Field = 8; global L1_TO_L2_MESSAGE_ORACLE_CALL_LENGTH: Field = 25; diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/hash.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/hash.nr index 4f0172faddec..efcb331b9546 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/hash.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/hash.nr @@ -7,7 +7,6 @@ use crate::abis::new_contract_data::NewContractData as ContractLeafPreimage; use crate::abis::function_data::FunctionData; use crate::abis::side_effect::{SideEffect}; use crate::utils::uint256::U256; -use crate::utils::bounded_vec::BoundedVec; use crate::constants::{ ARGS_HASH_CHUNK_COUNT, ARGS_HASH_CHUNK_LENGTH, diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/header.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/header.nr index b838c4534808..30e01dce3b1d 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/header.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/header.nr @@ -19,10 +19,12 @@ use crate::{ StateReference, STATE_REFERENCE_LENGTH, }, - traits::Empty, + traits::{ + Empty, + Hash, + }, utils::{ arr_copy_slice, - bounded_vec::BoundedVec, }, }; @@ -40,10 +42,10 @@ impl Header { pub fn serialize(self) -> [Field; HEADER_LENGTH] { let mut fields: BoundedVec = BoundedVec::new(0); - fields.push_array(self.last_archive.serialize()); - fields.push_array(self.body_hash); - fields.push_array(self.state.serialize()); - fields.push_array(self.global_variables.serialize()); + fields.extend_from_array(self.last_archive.serialize()); + fields.extend_from_array(self.body_hash); + fields.extend_from_array(self.state.serialize()); + fields.extend_from_array(self.global_variables.serialize()); fields.storage } @@ -69,19 +71,6 @@ impl Header { global_variables: GlobalVariables::deserialize(global_variables_fields), } } - - // TODO(#3941) Rename this as hash() and make it hash the whole header - pub fn block_hash(self) -> Field { - // TODO(https://github.com/AztecProtocol/aztec-packages/issues/3595) - pedersen_hash([ - self.global_variables.hash(), - self.state.partial.note_hash_tree.root, - self.state.partial.nullifier_tree.root, - self.state.partial.contract_tree.root, - self.state.l1_to_l2_message_tree.root, - self.state.partial.public_data_tree.root, - ], GENERATOR_INDEX__BLOCK_HASH) - } } impl Empty for Header { @@ -94,3 +83,9 @@ impl Empty for Header { } } } + +impl Hash for Header { + fn hash(self) -> Field { + pedersen_hash(self.serialize(), GENERATOR_INDEX__BLOCK_HASH) + } +} diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/state_reference.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/state_reference.nr index ea16db7d0326..bb3c517551eb 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/state_reference.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/state_reference.nr @@ -10,7 +10,6 @@ use crate::{ traits::Empty, utils::{ arr_copy_slice, - bounded_vec::BoundedVec, }, }; @@ -30,8 +29,8 @@ impl StateReference { fn serialize(self) -> [Field; STATE_REFERENCE_LENGTH] { let mut fields: BoundedVec = BoundedVec::new(0); - fields.push_array(self.l1_to_l2_message_tree.serialize()); - fields.push_array(self.partial.serialize()); + fields.extend_from_array(self.l1_to_l2_message_tree.serialize()); + fields.extend_from_array(self.partial.serialize()); fields.storage } diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/fixtures/read_requests.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/fixtures/read_requests.nr index dacb83e27075..a77ddd9b45f4 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/fixtures/read_requests.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/fixtures/read_requests.nr @@ -3,7 +3,6 @@ use crate::abis::{ side_effect::SideEffect, }; use crate::tests::fixtures; -use crate::utils::bounded_vec::BoundedVec; use crate::constants::{ MAX_READ_REQUESTS_PER_CALL, }; diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/private_call_data_builder.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/private_call_data_builder.nr index 290ea36dd60f..4bfa514397a3 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/private_call_data_builder.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/private_call_data_builder.nr @@ -21,9 +21,6 @@ use crate::{ transaction::{ request::TxRequest, }, - utils::{ - bounded_vec::BoundedVec, - }, }; use crate::constants::{ MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, @@ -120,14 +117,14 @@ impl PrivateCallDataBuilder { pub fn append_private_call_requests(&mut self, num_requests: Field, is_delegate_call: bool) { let (hashes, call_requests) = self.generate_call_requests(self.private_call_stack, num_requests, is_delegate_call); - self.public_inputs.private_call_stack_hashes.push_vec(hashes); - self.private_call_stack.push_vec(call_requests); + self.public_inputs.private_call_stack_hashes.extend_from_bounded_vec(hashes); + self.private_call_stack.extend_from_bounded_vec(call_requests); } pub fn append_public_call_requests(&mut self, num_requests: Field, is_delegate_call: bool) { let (hashes, call_requests) = self.generate_call_requests(self.public_call_stack, num_requests, is_delegate_call); - self.public_inputs.public_call_stack_hashes.push_vec(hashes); - self.public_call_stack.push_vec(call_requests); + self.public_inputs.public_call_stack_hashes.extend_from_bounded_vec(hashes); + self.public_call_stack.extend_from_bounded_vec(call_requests); } fn generate_call_requests( @@ -168,14 +165,14 @@ impl PrivateCallDataBuilder { pub fn append_read_requests(&mut self, num_read_requests: Field) { let (read_requests, read_request_membership_witnesses) = fixtures::read_requests::generate_read_requests(num_read_requests); - self.public_inputs.read_requests.push_vec(read_requests); - self.read_request_membership_witnesses.push_vec(read_request_membership_witnesses); + self.public_inputs.read_requests.extend_from_bounded_vec(read_requests); + self.read_request_membership_witnesses.extend_from_bounded_vec(read_request_membership_witnesses); } pub fn append_transient_read_requests(&mut self, num_read_requests: Field) { let (read_requests, read_request_membership_witnesses) = fixtures::read_requests::generate_transient_read_requests(num_read_requests); - self.public_inputs.read_requests.push_vec(read_requests); - self.read_request_membership_witnesses.push_vec(read_request_membership_witnesses); + self.public_inputs.read_requests.extend_from_bounded_vec(read_requests); + self.read_request_membership_witnesses.extend_from_bounded_vec(read_request_membership_witnesses); } pub fn set_encrypted_logs(&mut self, hash: [Field; NUM_FIELDS_PER_SHA256], preimages_length: Field) { diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/private_circuit_public_inputs_builder.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/private_circuit_public_inputs_builder.nr index 9ea565e91664..46196b43b4a1 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/private_circuit_public_inputs_builder.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/private_circuit_public_inputs_builder.nr @@ -13,7 +13,6 @@ use crate::{ fixtures, testing_harness::build_contract_deployment_data, }, - utils::bounded_vec::BoundedVec, }; use crate::constants::{ MAX_READ_REQUESTS_PER_CALL, diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/public_call_data_builder.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/public_call_data_builder.nr index a9f85427c666..48dd0bb62e10 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/public_call_data_builder.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/public_call_data_builder.nr @@ -17,7 +17,6 @@ use crate::{ fixtures, public_circuit_public_inputs_builder::PublicCircuitPublicInputsBuilder, }, - utils::bounded_vec::BoundedVec, }; use crate::constants::{ MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/public_circuit_public_inputs_builder.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/public_circuit_public_inputs_builder.nr index e266a8e34ee1..41f2102a0518 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/public_circuit_public_inputs_builder.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/public_circuit_public_inputs_builder.nr @@ -11,7 +11,6 @@ use crate::{ }, header::Header, tests::fixtures, - utils::bounded_vec::BoundedVec, }; use crate::constants::{ MAX_NEW_COMMITMENTS_PER_CALL, diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/utils.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/utils.nr index 068a027f7a70..f4c2b212cc2b 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/utils.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/utils.nr @@ -3,7 +3,6 @@ // Reducing the size of this package would be welcome. mod arrays; -mod bounded_vec; mod field; mod uint256; diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/utils/arrays.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/utils/arrays.nr index 239867b05e02..609071c4d847 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/utils/arrays.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/utils/arrays.nr @@ -1,6 +1,5 @@ use dep::std::array; use dep::std::cmp::Eq; -use crate::utils::bounded_vec::BoundedVec; use crate::traits::{Empty, is_empty}; pub fn array_to_bounded_vec(array: [T; N]) -> BoundedVec where T: Empty + Eq { diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/utils/bounded_vec.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/utils/bounded_vec.nr deleted file mode 100644 index e9aa91c26de4..000000000000 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/utils/bounded_vec.nr +++ /dev/null @@ -1,194 +0,0 @@ -struct BoundedVec { - storage: [T; MaxLen], - // TODO: change this to return a u64 as Noir now - // uses u64 for indexing - len: Field, - empty_value: T, -} - -impl BoundedVec { - pub fn new(initial_value: T) -> Self { - BoundedVec { storage: [initial_value; MaxLen], len: 0, empty_value: initial_value } - } - - pub fn get(mut self: Self, index: Field) -> T { - assert(index as u64 < self.len as u64); - self.storage[index] - } - - pub fn get_unchecked(mut self: Self, index: Field) -> T { - self.storage[index] - } - - pub fn push(&mut self, elem: T) { - assert(self.len as u64 < MaxLen as u64, "push out of bounds"); - - self.storage[self.len] = elem; - self.len += 1; - } - - pub fn len(self) -> Field { - self.len - } - - pub fn max_len(_self: BoundedVec) -> Field { - MaxLen - } - - // This is a intermediate method, while we don't have an - // .extend method - pub fn storage(self) -> [T; MaxLen] { - self.storage - } - - pub fn push_array(&mut self, array: [T; Len]) { - let new_len = self.len + array.len(); - assert(new_len as u64 <= MaxLen as u64, "push_array out of bounds"); - for i in 0..array.len() { - self.storage[self.len + i] = array[i]; - } - self.len = new_len; - } - - pub fn push_vec(&mut self, vec: BoundedVec) { - let append_len = vec.len(); - let new_len = self.len + append_len; - assert(new_len as u64 <= MaxLen as u64, "push_vec out of bounds"); - - let mut exceeded_len = false; - for i in 0..Len { - exceeded_len |= i == append_len; - if !exceeded_len { - self.storage[self.len + (i as Field)] = vec.get_unchecked(i as Field); - } - } - self.len = new_len; - } - - pub fn pop(&mut self) -> T { - assert(self.len as u64 > 0); - self.len -= 1; - - let elem = self.storage[self.len]; - self.storage[self.len] = self.empty_value; - elem - } - - 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) { - ret |= predicate(self.storage[i]); - } - } - ret - } -} - -#[test] -fn test_vec_push_pop() { - let mut vec: BoundedVec = BoundedVec::new(0); - assert(vec.len == 0); - vec.push(2); - assert(vec.len == 1); - vec.push(4); - assert(vec.len == 2); - vec.push(6); - assert(vec.len == 3); - let x = vec.pop(); - assert(x == 6); - assert(vec.len == 2); - assert(vec.get(0) == 2); - assert(vec.get(1) == 4); -} - -#[test] -fn test_vec_push_array() { - let mut vec: BoundedVec = BoundedVec::new(0); - vec.push_array([2, 4]); - assert(vec.len == 2); - assert(vec.get(0) == 2); - assert(vec.get(1) == 4); -} - -#[test(should_fail_with="push_array out of bounds")] -fn test_vec_push_array_out_of_bound() { - let mut vec: BoundedVec = BoundedVec::new(0); - vec.push_array([2, 4, 6]); -} - -#[test(should_fail_with="push_array out of bounds")] -fn test_vec_push_array_twice_out_of_bound() { - let mut vec: BoundedVec = BoundedVec::new(0); - vec.push_array([2]); - assert(vec.len == 1); - vec.push_array([4, 6]); -} - -#[test(should_fail)] -fn test_vec_get_out_of_bound() { - let mut vec: BoundedVec = BoundedVec::new(0); - vec.push_array([2, 4]); - let _x = vec.get(2); -} - -#[test(should_fail)] -fn test_vec_get_not_declared() { - let mut vec: BoundedVec = BoundedVec::new(0); - vec.push_array([2]); - let _x = vec.get(1); -} - -#[test(should_fail)] -fn test_vec_get_uninitialized() { - let mut vec: BoundedVec = BoundedVec::new(0); - let _x = vec.get(0); -} - -#[test(should_fail_with="push out of bounds")] -fn test_vec_push_out_of_bound() { - let mut vec: BoundedVec = BoundedVec::new(0); - vec.push(1); - vec.push(2); -} - -#[test(should_fail_with="push_vec out of bounds")] -fn test_vec_push_vec_out_of_bound() { - let mut vec: BoundedVec = BoundedVec::new(0); - - let mut another_vec: BoundedVec = BoundedVec::new(0); - another_vec.push_array([1, 2, 3]); - - vec.push_vec(another_vec); -} - -#[test(should_fail_with="push_vec out of bounds")] -fn test_vec_push_vec_twice_out_of_bound() { - let mut vec: BoundedVec = BoundedVec::new(0); - vec.push_array([1, 2]); - - let mut another_vec: BoundedVec = BoundedVec::new(0); - another_vec.push(3); - - vec.push_vec(another_vec); -} - -#[test] -fn test_vec_any() { - let mut vec: BoundedVec = BoundedVec::new(0); - vec.push_array([2, 4, 6]); - assert(vec.any(|v| v == 2) == true); - assert(vec.any(|v| v == 4) == true); - assert(vec.any(|v| v == 6) == true); - assert(vec.any(|v| v == 3) == false); -} - -#[test] -fn test_vec_any_not_default() { - let default_value = 1; - let mut vec: BoundedVec = BoundedVec::new(default_value); - vec.push_array([2, 4]); - assert(vec.any(|v| v == default_value) == false); -} diff --git a/yarn-project/p2p-bootstrap/terraform/main.tf b/yarn-project/p2p-bootstrap/terraform/main.tf index 376d59c134bb..57070dcfa762 100644 --- a/yarn-project/p2p-bootstrap/terraform/main.tf +++ b/yarn-project/p2p-bootstrap/terraform/main.tf @@ -123,10 +123,6 @@ resource "aws_ecs_task_definition" "p2p-bootstrap" { "name": "NODE_ENV", "value": "production" }, - { - "name": "MODE", - "value": "p2p-bootstrap" - }, { "name": "P2P_TCP_LISTEN_PORT", "value": "${var.BOOTNODE_LISTEN_PORT + count.index}" diff --git a/yarn-project/pxe/src/pxe_service/pxe_service.ts b/yarn-project/pxe/src/pxe_service/pxe_service.ts index b3831f976a45..09df2e57ebd3 100644 --- a/yarn-project/pxe/src/pxe_service/pxe_service.ts +++ b/yarn-project/pxe/src/pxe_service/pxe_service.ts @@ -445,7 +445,7 @@ export class PXEService implements PXE { txHash, TxStatus.MINED, '', - settledTx.blockHash, + settledTx.blockHash.toBuffer(), settledTx.blockNumber, deployedContractAddress, ); diff --git a/yarn-project/sequencer-client/src/block_builder/solo_block_builder.test.ts b/yarn-project/sequencer-client/src/block_builder/solo_block_builder.test.ts index 3a005017e91c..470ce36f9cc3 100644 --- a/yarn-project/sequencer-client/src/block_builder/solo_block_builder.test.ts +++ b/yarn-project/sequencer-client/src/block_builder/solo_block_builder.test.ts @@ -15,6 +15,7 @@ import { BaseOrMergeRollupPublicInputs, Fr, GlobalVariables, + Header, KernelCircuitPublicInputs, MAX_NEW_COMMITMENTS_PER_TX, MAX_NEW_L2_TO_L1_MSGS_PER_TX, @@ -33,7 +34,7 @@ import { SideEffectLinkedToNoteHash, StateReference, } from '@aztec/circuits.js'; -import { computeBlockHashWithGlobals, computeContractLeaf } from '@aztec/circuits.js/abis'; +import { computeContractLeaf } from '@aztec/circuits.js/abis'; import { fr, makeBaseOrMergeRollupPublicInputs, @@ -63,7 +64,6 @@ import { makeEmptyProcessedTx as makeEmptyProcessedTxFromHistoricalTreeRoots, makeProcessedTx, } from '../sequencer/processed_tx.js'; -import { buildInitialHeader } from '../sequencer/utils.js'; import { RollupSimulator } from '../simulator/index.js'; import { RealRollupCircuitSimulator } from '../simulator/rollup.js'; import { SoloBlockBuilder } from './solo_block_builder.js'; @@ -122,7 +122,7 @@ describe('sequencer/solo_block_builder', () => { }, 20_000); const makeEmptyProcessedTx = async () => { - const header = await buildInitialHeader(builderDb); + const header = await builderDb.buildInitialHeader(); return makeEmptyProcessedTxFromHistoricalTreeRoots(header, chainId, version); }; @@ -157,14 +157,7 @@ describe('sequencer/solo_block_builder', () => { }; const updateArchive = async () => { - const blockHash = computeBlockHashWithGlobals( - globalVariables, - rootRollupOutput.header.state.partial.noteHashTree.root, - rootRollupOutput.header.state.partial.nullifierTree.root, - rootRollupOutput.header.state.partial.contractTree.root, - rootRollupOutput.header.state.l1ToL2MessageTree.root, - rootRollupOutput.header.state.partial.publicDataTree.root, - ); + const blockHash = rootRollupOutput.header.hash(); await expectsDb.appendLeaves(MerkleTreeId.ARCHIVE, [blockHash.toBuffer()]); }; @@ -191,7 +184,7 @@ describe('sequencer/solo_block_builder', () => { const buildMockSimulatorInputs = async () => { const kernelOutput = makePrivateKernelPublicInputsFinal(); - kernelOutput.constants.historicalHeader = await buildInitialHeader(expectsDb); + kernelOutput.constants.historicalHeader = await expectsDb.buildInitialHeader(); const tx = await makeProcessedTx( new Tx( @@ -214,15 +207,8 @@ describe('sequencer/solo_block_builder', () => { await updateExpectedTreesFromTxs([txs[1]]); baseRollupOutputRight.end = await getPartialStateReference(); - // Update l1 to l2 data tree - // And update the root trees now to create proper output to the root rollup circuit + // Update l1 to l2 message tree await updateL1ToL2MessageTree(mockL1ToL2Messages); - rootRollupOutput.header.state = await getStateReference(); - - // Calculate block hash - rootRollupOutput.header.globalVariables = globalVariables; - await updateArchive(); - rootRollupOutput.archive = await getTreeSnapshot(MerkleTreeId.ARCHIVE); const newNullifiers = txs.flatMap(tx => tx.data.end.newNullifiers); const newCommitments = txs.flatMap(tx => tx.data.end.newCommitments); @@ -237,9 +223,11 @@ describe('sequencer/solo_block_builder', () => { const newEncryptedLogs = new L2BlockL2Logs(txs.map(tx => tx.encryptedLogs || new TxL2Logs([]))); const newUnencryptedLogs = new L2BlockL2Logs(txs.map(tx => tx.unencryptedLogs || new TxL2Logs([]))); + // We are constructing the block here just to get body hash/calldata hash so we can pass in an empty archive and header const l2Block = L2Block.fromFields({ - archive: rootRollupOutput.archive, - header: rootRollupOutput.header, + archive: AppendOnlyTreeSnapshot.zero(), + header: Header.empty(), + // Only the values below go to body hash/calldata hash newCommitments: newCommitments.map((sideEffect: SideEffect) => sideEffect.value), newNullifiers: newNullifiers.map((sideEffect: SideEffectLinkedToNoteHash) => sideEffect.value), newContracts, @@ -251,7 +239,13 @@ describe('sequencer/solo_block_builder', () => { newUnencryptedLogs, }); + // Now we update can make the final header, compute the block hash and update archive + rootRollupOutput.header.globalVariables = globalVariables; rootRollupOutput.header.bodyHash = l2Block.getCalldataHash(); + rootRollupOutput.header.state = await getStateReference(); + + await updateArchive(); + rootRollupOutput.archive = await getTreeSnapshot(MerkleTreeId.ARCHIVE); return txs; }; @@ -297,7 +291,7 @@ describe('sequencer/solo_block_builder', () => { const makeBloatedProcessedTx = async (seed = 0x1) => { const tx = mockTx(seed); const kernelOutput = KernelCircuitPublicInputs.empty(); - kernelOutput.constants.historicalHeader = await buildInitialHeader(builderDb); + kernelOutput.constants.historicalHeader = await builderDb.buildInitialHeader(); kernelOutput.end.publicDataUpdateRequests = makeTuple( MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, i => new PublicDataUpdateRequest(fr(i), fr(0), fr(i + 10)), diff --git a/yarn-project/sequencer-client/src/block_builder/solo_block_builder.ts b/yarn-project/sequencer-client/src/block_builder/solo_block_builder.ts index 49318dcaa672..488b71914564 100644 --- a/yarn-project/sequencer-client/src/block_builder/solo_block_builder.ts +++ b/yarn-project/sequencer-client/src/block_builder/solo_block_builder.ts @@ -42,12 +42,7 @@ import { VK_TREE_HEIGHT, VerificationKey, } from '@aztec/circuits.js'; -import { - computeBlockHash, - computeBlockHashWithGlobals, - computeContractLeaf, - computeGlobalsHash, -} from '@aztec/circuits.js/abis'; +import { computeContractLeaf } from '@aztec/circuits.js/abis'; import { makeTuple } from '@aztec/foundation/array'; import { toBigIntBE } from '@aztec/foundation/bigint-buffer'; import { padArrayEnd } from '@aztec/foundation/collection'; @@ -158,19 +153,19 @@ export class SoloBlockBuilder implements BlockBuilder { protected validateTxs(txs: ProcessedTx[]) { for (const tx of txs) { const txHeader = tx.data.constants.historicalHeader; - if (txHeader.state.l1ToL2MessageTree.isEmpty()) { + if (txHeader.state.l1ToL2MessageTree.isZero()) { throw new Error(`Empty L1 to L2 messages tree in tx: ${toFriendlyJSON(tx)}`); } - if (txHeader.state.partial.noteHashTree.isEmpty()) { + if (txHeader.state.partial.noteHashTree.isZero()) { throw new Error(`Empty note hash tree in tx: ${toFriendlyJSON(tx)}`); } - if (txHeader.state.partial.nullifierTree.isEmpty()) { + if (txHeader.state.partial.nullifierTree.isZero()) { throw new Error(`Empty nullifier tree in tx: ${toFriendlyJSON(tx)}`); } - if (txHeader.state.partial.contractTree.isEmpty()) { + if (txHeader.state.partial.contractTree.isZero()) { throw new Error(`Empty contract tree in tx: ${toFriendlyJSON(tx)}`); } - if (txHeader.state.partial.publicDataTree.isEmpty()) { + if (txHeader.state.partial.publicDataTree.isZero()) { throw new Error(`Empty public data tree in tx: ${toFriendlyJSON(tx)}`); } } @@ -276,48 +271,15 @@ export class SoloBlockBuilder implements BlockBuilder { const rootProof = await this.prover.getRootRollupProof(rootInput, rootOutput); - // Update the root trees with the latest data and contract tree roots, - // and validate them against the output of the root circuit simulation + // Update the archive with the latest block header this.debug(`Updating and validating root trees`); - const globalVariablesHash = computeGlobalsHash(left[0].constants.globalVariables); - await this.db.updateLatestGlobalVariablesHash(globalVariablesHash); - await this.db.updateArchive(globalVariablesHash); + await this.db.updateArchive(rootOutput.header); await this.validateRootOutput(rootOutput); return [rootOutput, rootProof]; } - async updateArchive(globalVariables: GlobalVariables) { - // Calculate the block hash and add it to the historical block hashes tree - const blockHash = await this.calculateBlockHash(globalVariables); - await this.db.appendLeaves(MerkleTreeId.ARCHIVE, [blockHash.toBuffer()]); - } - - protected async calculateBlockHash(globals: GlobalVariables) { - const [noteHashTreeRoot, nullifierTreeRoot, contractTreeRoot, publicDataTreeRoot, l1ToL2MessageTreeRoot] = ( - await Promise.all( - [ - MerkleTreeId.NOTE_HASH_TREE, - MerkleTreeId.NULLIFIER_TREE, - MerkleTreeId.CONTRACT_TREE, - MerkleTreeId.PUBLIC_DATA_TREE, - MerkleTreeId.L1_TO_L2_MESSAGE_TREE, - ].map(tree => this.getTreeSnapshot(tree)), - ) - ).map(r => r.root); - - const blockHash = computeBlockHashWithGlobals( - globals, - noteHashTreeRoot, - nullifierTreeRoot, - contractTreeRoot, - l1ToL2MessageTreeRoot, - publicDataTreeRoot, - ); - return blockHash; - } - protected async validatePartialState(partialState: PartialStateReference) { await Promise.all([ this.validateSimulatedTree( @@ -491,20 +453,6 @@ export class SoloBlockBuilder implements BlockBuilder { return new MembershipWitness(height, index, assertLength(path.toFieldArray(), height)); } - protected getHistoricalTreesMembershipWitnessFor(tx: ProcessedTx) { - const header = tx.data.constants.historicalHeader; - // TODO(#3941) - const blockHash = computeBlockHash( - computeGlobalsHash(header.globalVariables), - header.state.partial.noteHashTree.root, - header.state.partial.nullifierTree.root, - header.state.partial.contractTree.root, - header.state.l1ToL2MessageTree.root, - header.state.partial.publicDataTree.root, - ); - return this.getMembershipWitnessFor(blockHash, MerkleTreeId.ARCHIVE, ARCHIVE_HEIGHT); - } - protected async getConstantRollupData(globalVariables: GlobalVariables): Promise { return ConstantRollupData.from({ baseRollupVkHash: DELETE_FR, @@ -734,6 +682,13 @@ export class SoloBlockBuilder implements BlockBuilder { publicDataSiblingPath, }); + const blockHash = tx.data.constants.historicalHeader.hash(); + const archiveRootMembershipWitness = await this.getMembershipWitnessFor( + blockHash, + MerkleTreeId.ARCHIVE, + ARCHIVE_HEIGHT, + ); + return BaseRollupInputs.from({ kernelData: this.getKernelDataFor(tx), start, @@ -746,7 +701,7 @@ export class SoloBlockBuilder implements BlockBuilder { publicDataReadsPreimages: txPublicDataReadsInfo.newPublicDataReadsPreimages, publicDataReadsMembershipWitnesses: txPublicDataReadsInfo.newPublicDataReadsWitnesses, - archiveRootMembershipWitness: await this.getHistoricalTreesMembershipWitnessFor(tx), + archiveRootMembershipWitness, constants, }); diff --git a/yarn-project/sequencer-client/src/sequencer/index.ts b/yarn-project/sequencer-client/src/sequencer/index.ts index 43a83d121a16..4e1a69cbc9b2 100644 --- a/yarn-project/sequencer-client/src/sequencer/index.ts +++ b/yarn-project/sequencer-client/src/sequencer/index.ts @@ -1,3 +1,2 @@ export * from './sequencer.js'; export * from './config.js'; -export * from './utils.js'; diff --git a/yarn-project/sequencer-client/src/sequencer/public_processor.ts b/yarn-project/sequencer-client/src/sequencer/public_processor.ts index 51e1ad011d82..ebc2c87aa938 100644 --- a/yarn-project/sequencer-client/src/sequencer/public_processor.ts +++ b/yarn-project/sequencer-client/src/sequencer/public_processor.ts @@ -56,7 +56,6 @@ import { PublicKernelCircuitSimulator } from '../simulator/index.js'; import { ContractsDataSourcePublicDB, WorldStateDB, WorldStatePublicDB } from '../simulator/public_executor.js'; import { RealPublicKernelCircuitSimulator } from '../simulator/public_kernel.js'; import { FailedTx, ProcessedTx, makeEmptyProcessedTx, makeProcessedTx } from './processed_tx.js'; -import { buildInitialHeader } from './utils.js'; /** * Creates new instances of PublicProcessor given the provided merkle tree db and contract data source. @@ -79,7 +78,7 @@ export class PublicProcessorFactory { historicalHeader: Header | undefined, globalVariables: GlobalVariables, ): Promise { - historicalHeader = historicalHeader ?? (await buildInitialHeader(this.merkleTree)); + historicalHeader = historicalHeader ?? (await this.merkleTree.buildInitialHeader()); const publicContractsDB = new ContractsDataSourcePublicDB(this.contractDataSource); const worldStatePublicDB = new WorldStatePublicDB(this.merkleTree); diff --git a/yarn-project/sequencer-client/src/sequencer/utils.ts b/yarn-project/sequencer-client/src/sequencer/utils.ts deleted file mode 100644 index eebff558fea8..000000000000 --- a/yarn-project/sequencer-client/src/sequencer/utils.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { AppendOnlyTreeSnapshot, GlobalVariables, Header } from '@aztec/circuits.js'; -import { MerkleTreeOperations } from '@aztec/world-state'; - -/** - * Builds the initial header by reading the roots from the database. - * - * TODO(#4148) Proper genesis state. If the state is empty, we allow anything for now. - */ -export async function buildInitialHeader(db: MerkleTreeOperations) { - const state = await db.getStateReference(); - return new Header(AppendOnlyTreeSnapshot.empty(), Buffer.alloc(32, 0), state, GlobalVariables.empty()); -} diff --git a/yarn-project/world-state/src/world-state-db/merkle_tree_operations.ts b/yarn-project/world-state/src/world-state-db/merkle_tree_operations.ts index 3d4d5db15092..8a59832253af 100644 --- a/yarn-project/world-state/src/world-state-db/merkle_tree_operations.ts +++ b/yarn-project/world-state/src/world-state-db/merkle_tree_operations.ts @@ -1,6 +1,5 @@ import { L2Block, MerkleTreeId } from '@aztec/circuit-types'; -import { NullifierLeafPreimage, StateReference } from '@aztec/circuits.js'; -import { Fr } from '@aztec/foundation/fields'; +import { Header, NullifierLeafPreimage, StateReference } from '@aztec/circuits.js'; import { createDebugLogger } from '@aztec/foundation/log'; import { IndexedTreeLeafPreimage } from '@aztec/foundation/trees'; import { BatchInsertionResult } from '@aztec/merkle-tree'; @@ -56,6 +55,11 @@ export interface MerkleTreeOperations { */ getStateReference(): Promise; + /** + * Builds the initial header. + */ + buildInitialHeader(): Promise

; + /** * Gets sibling path for a leaf. * @param treeId - The tree to be queried for a sibling path. @@ -115,22 +119,11 @@ export interface MerkleTreeOperations { getLeafValue(treeId: MerkleTreeId, index: bigint): Promise; /** - * Inserts the new block hash into the archive. + * Inserts the block hash into the archive. * This includes all of the current roots of all of the data trees and the current blocks global vars. - * @param globalVariablesHash - The global variables hash to insert into the block hash. - */ - updateArchive(globalVariablesHash: Fr): Promise; - - /** - * Updates the latest global variables hash - * @param globalVariablesHash - The latest global variables hash - */ - updateLatestGlobalVariablesHash(globalVariablesHash: Fr): Promise; - - /** - * Gets the global variables hash from the previous block + * @param header - The header to insert into the archive. */ - getLatestGlobalVariablesHash(): Promise; + updateArchive(header: Header): Promise; /** * Batch insert multiple leaves into the tree. diff --git a/yarn-project/world-state/src/world-state-db/merkle_tree_operations_facade.ts b/yarn-project/world-state/src/world-state-db/merkle_tree_operations_facade.ts index 2669172c8133..f36513f6e784 100644 --- a/yarn-project/world-state/src/world-state-db/merkle_tree_operations_facade.ts +++ b/yarn-project/world-state/src/world-state-db/merkle_tree_operations_facade.ts @@ -1,6 +1,5 @@ import { L2Block, MerkleTreeId } from '@aztec/circuit-types'; -import { NullifierLeafPreimage, StateReference } from '@aztec/circuits.js'; -import { Fr } from '@aztec/foundation/fields'; +import { Header, NullifierLeafPreimage, StateReference } from '@aztec/circuits.js'; import { IndexedTreeLeafPreimage } from '@aztec/foundation/trees'; import { BatchInsertionResult } from '@aztec/merkle-tree'; import { SiblingPath } from '@aztec/types/membership'; @@ -32,6 +31,14 @@ export class MerkleTreeOperationsFacade implements MerkleTreeOperations { return this.trees.getStateReference(this.includeUncommitted); } + /** + * Builds the initial header. + * @returns The initial header. + */ + buildInitialHeader(): Promise
{ + return this.trees.buildInitialHeader(this.includeUncommitted); + } + /** * Appends a set of leaf values to the tree. * @param treeId - Id of the tree to append leaves to. @@ -128,25 +135,10 @@ export class MerkleTreeOperationsFacade implements MerkleTreeOperations { /** * Inserts the new block hash into the archive. * This includes all of the current roots of all of the data trees and the current blocks global vars. - * @param globalVariablesHash - The global variables hash to insert into the block hash. - */ - public updateArchive(globalVariablesHash: Fr): Promise { - return this.trees.updateArchive(globalVariablesHash, this.includeUncommitted); - } - - /** - * Updates the latest global variables hash - * @param globalVariablesHash - The latest global variables hash - */ - public updateLatestGlobalVariablesHash(globalVariablesHash: Fr): Promise { - return this.trees.updateLatestGlobalVariablesHash(globalVariablesHash, this.includeUncommitted); - } - - /** - * Gets the global variables hash from the previous block + * @param header - The header to insert into the archive. */ - public getLatestGlobalVariablesHash(): Promise { - return this.trees.getLatestGlobalVariablesHash(this.includeUncommitted); + public updateArchive(header: Header): Promise { + return this.trees.updateArchive(header, this.includeUncommitted); } /** diff --git a/yarn-project/world-state/src/world-state-db/merkle_tree_snapshot_operations_facade.ts b/yarn-project/world-state/src/world-state-db/merkle_tree_snapshot_operations_facade.ts index a0f94f8a14d0..79e7a01d16fa 100644 --- a/yarn-project/world-state/src/world-state-db/merkle_tree_snapshot_operations_facade.ts +++ b/yarn-project/world-state/src/world-state-db/merkle_tree_snapshot_operations_facade.ts @@ -1,5 +1,5 @@ import { MerkleTreeId } from '@aztec/circuit-types'; -import { AppendOnlyTreeSnapshot, Fr, PartialStateReference, StateReference } from '@aztec/circuits.js'; +import { AppendOnlyTreeSnapshot, Fr, Header, PartialStateReference, StateReference } from '@aztec/circuits.js'; import { IndexedTreeLeafPreimage } from '@aztec/foundation/trees'; import { BatchInsertionResult, IndexedTreeSnapshot, TreeSnapshot } from '@aztec/merkle-tree'; import { SiblingPath } from '@aztec/types/membership'; @@ -34,10 +34,6 @@ export class MerkleTreeSnapshotOperationsFacade implements MerkleTreeOperations return tree.findLeafIndex(value); } - getLatestGlobalVariablesHash(): Promise { - return Promise.reject(new Error('not implemented')); - } - async getLeafPreimage( treeId: MerkleTreeId.NULLIFIER_TREE, index: bigint, @@ -152,11 +148,11 @@ export class MerkleTreeSnapshotOperationsFacade implements MerkleTreeOperations return Promise.reject(new Error('Tree snapshot operations are read-only')); } - updateLatestGlobalVariablesHash(): Promise { + updateLeaf(): Promise { return Promise.reject(new Error('Tree snapshot operations are read-only')); } - updateLeaf(): Promise { - return Promise.reject(new Error('Tree snapshot operations are read-only')); + buildInitialHeader(): Promise
{ + throw new Error('Building initial header not supported on snapshot.'); } } diff --git a/yarn-project/world-state/src/world-state-db/merkle_trees.ts b/yarn-project/world-state/src/world-state-db/merkle_trees.ts index 21d4e4532365..3a55636c2730 100644 --- a/yarn-project/world-state/src/world-state-db/merkle_trees.ts +++ b/yarn-project/world-state/src/world-state-db/merkle_trees.ts @@ -5,6 +5,7 @@ import { CONTRACT_TREE_HEIGHT, Fr, GlobalVariables, + Header, L1_TO_L2_MSG_TREE_HEIGHT, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, NOTE_HASH_TREE_HEIGHT, @@ -19,12 +20,10 @@ import { PublicDataTreeLeafPreimage, StateReference, } from '@aztec/circuits.js'; -import { computeBlockHash, computeGlobalsHash } from '@aztec/circuits.js/abis'; -import { Committable } from '@aztec/foundation/committable'; import { SerialQueue } from '@aztec/foundation/fifo'; -import { createDebugLogger } from '@aztec/foundation/log'; +import { DebugLogger, createDebugLogger } from '@aztec/foundation/log'; import { IndexedTreeLeafPreimage } from '@aztec/foundation/trees'; -import { AztecKVStore, AztecSingleton } from '@aztec/kv-store'; +import { AztecKVStore } from '@aztec/kv-store'; import { AppendOnlyTree, BatchInsertionResult, @@ -33,6 +32,7 @@ import { StandardIndexedTree, StandardTree, UpdateOnlyTree, + getTreeMeta, loadTree, newTree, } from '@aztec/merkle-tree'; @@ -43,18 +43,6 @@ import { INITIAL_NULLIFIER_TREE_SIZE, INITIAL_PUBLIC_DATA_TREE_SIZE, MerkleTreeD import { HandleL2BlockResult, IndexedTreeId, MerkleTreeOperations, TreeInfo } from './merkle_tree_operations.js'; import { MerkleTreeOperationsFacade } from './merkle_tree_operations_facade.js'; -/** - * Data necessary to reinitialize the merkle trees from Db. - */ -interface FromDbOptions { - /** - * The global variables hash from the last block. - */ - globalVariablesHash: Fr; -} - -const LAST_GLOBAL_VARS_HASH = 'lastGlobalVarsHash'; - /** * The nullifier tree is an indexed tree. */ @@ -78,22 +66,26 @@ class PublicDataTree extends StandardIndexedTree { */ export class MerkleTrees implements MerkleTreeDb { private trees: (AppendOnlyTree | UpdateOnlyTree)[] = []; - private latestGlobalVariablesHash: Committable; private jobQueue = new SerialQueue(); - #globalVariablesHash: AztecSingleton; + private constructor(private store: AztecKVStore, private log: DebugLogger) {} - constructor(private store: AztecKVStore, private log = createDebugLogger('aztec:merkle_trees')) { - this.latestGlobalVariablesHash = new Committable(Fr.ZERO); - this.#globalVariablesHash = store.openSingleton(LAST_GLOBAL_VARS_HASH); + /** + * Method to asynchronously create and initialize a MerkleTrees instance. + * @param store - The db instance to use for data persistance. + * @returns - A fully initialized MerkleTrees instance. + */ + public static async new(store: AztecKVStore, log = createDebugLogger('aztec:merkle_trees')) { + const merkleTrees = new MerkleTrees(store, log); + await merkleTrees.#init(); + return merkleTrees; } /** - * initializes the collection of Merkle Trees. - * @param fromDbOptions - Options to initialize the trees from the database. + * Initializes the collection of Merkle Trees. */ - public async init(fromDbOptions?: FromDbOptions) { - const fromDb = fromDbOptions !== undefined; + async #init() { + const fromDb = this.#isDbPopulated(); const initializeTree = fromDb ? loadTree : newTree; const hasher = new Pedersen(); @@ -145,30 +137,19 @@ export class MerkleTrees implements MerkleTreeDb { this.jobQueue.start(); - // The first leaf in the blocks tree contains the empty roots of the other trees and empty global variables. if (!fromDb) { - const initialGlobalVariablesHash = computeGlobalsHash(GlobalVariables.empty()); - await this._updateLatestGlobalVariablesHash(initialGlobalVariablesHash); - await this._updateArchive(initialGlobalVariablesHash, true); - await this._commit(); - } else { - await this._updateLatestGlobalVariablesHash(fromDbOptions.globalVariablesHash); - // make the restored global variables hash and tree roots current - await this._commit(); + // We are not initializing from db so we need to populate the first leaf of the archive tree which is a hash of + // the initial header. + const initialHeder = await this.buildInitialHeader(true); + await this.#updateArchive(initialHeder, true); } + + await this.#commit(); } - /** - * Method to asynchronously create and initialize a MerkleTrees instance. - * @param store - The db instance to use for data persistance. - * @returns - A fully initialized MerkleTrees instance. - */ - public static async new(store: AztecKVStore) { - const merkleTrees = new MerkleTrees(store); - const globalVariablesHash = store.openSingleton(LAST_GLOBAL_VARS_HASH); - const val = globalVariablesHash.get(); - await merkleTrees.init(val ? { globalVariablesHash: Fr.fromBuffer(val) } : undefined); - return merkleTrees; + public async buildInitialHeader(includeUncommitted: boolean): Promise
{ + const state = await this.getStateReference(includeUncommitted); + return new Header(AppendOnlyTreeSnapshot.zero(), Buffer.alloc(32, 0), state, GlobalVariables.empty()); } /** @@ -195,29 +176,12 @@ export class MerkleTrees implements MerkleTreeDb { } /** - * Inserts into the roots trees (CONTRACT_TREE_ROOTS_TREE, NOTE_HASH_TREE_ROOTS_TREE, L1_TO_L2_MESSAGE_TREE_ROOTS_TREE) - * the current roots of the corresponding trees (CONTRACT_TREE, NOTE_HASH_TREE, L1_TO_L2_MESSAGE_TREE). - * @param globalsHash - The current global variables hash. + * Updates the archive with the new block/header hash. + * @param header - The header whose hash to insert into the archive. * @param includeUncommitted - Indicates whether to include uncommitted data. */ - public async updateArchive(globalsHash: Fr, includeUncommitted: boolean) { - await this.synchronize(() => this._updateArchive(globalsHash, includeUncommitted)); - } - - /** - * Updates the latest global variables hash - * @param globalVariablesHash - The latest global variables hash - */ - public async updateLatestGlobalVariablesHash(globalVariablesHash: Fr) { - return await this.synchronize(() => this._updateLatestGlobalVariablesHash(globalVariablesHash)); - } - - /** - * Gets the global variables hash from the previous block - * @param includeUncommitted - Indicates whether to include uncommitted data. - */ - public async getLatestGlobalVariablesHash(includeUncommitted: boolean): Promise { - return await this.synchronize(() => this._getGlobalVariablesHash(includeUncommitted)); + public async updateArchive(header: Header, includeUncommitted: boolean) { + await this.synchronize(() => this.#updateArchive(header, includeUncommitted)); } /** @@ -227,7 +191,7 @@ export class MerkleTrees implements MerkleTreeDb { * @returns The tree info for the specified tree. */ public async getTreeInfo(treeId: MerkleTreeId, includeUncommitted: boolean): Promise { - return await this.synchronize(() => this._getTreeInfo(treeId, includeUncommitted)); + return await this.synchronize(() => this.#getTreeInfo(treeId, includeUncommitted)); } /** @@ -256,19 +220,6 @@ export class MerkleTrees implements MerkleTreeDb { return Promise.resolve(state); } - // TODO(#3941) - private async _getCurrentBlockHash(globalsHash: Fr, includeUncommitted: boolean): Promise { - const state = await this.getStateReference(includeUncommitted); - return computeBlockHash( - globalsHash, - state.partial.noteHashTree.root, - state.partial.nullifierTree.root, - state.partial.contractTree.root, - state.l1ToL2MessageTree.root, - state.partial.publicDataTree.root, - ); - } - /** * Gets the value at the given index. * @param treeId - The ID of the tree to get the leaf value from. @@ -296,7 +247,7 @@ export class MerkleTrees implements MerkleTreeDb { index: bigint, includeUncommitted: boolean, ): Promise> { - return await this.synchronize(() => this._getSiblingPath(treeId, index, includeUncommitted)); + return await this.synchronize(() => this.trees[treeId].getSiblingPath(index, includeUncommitted)); } /** @@ -306,7 +257,7 @@ export class MerkleTrees implements MerkleTreeDb { * @returns Empty promise. */ public async appendLeaves(treeId: MerkleTreeId, leaves: Buffer[]): Promise { - return await this.synchronize(() => this._appendLeaves(treeId, leaves)); + return await this.synchronize(() => this.#appendLeaves(treeId, leaves)); } /** @@ -314,7 +265,7 @@ export class MerkleTrees implements MerkleTreeDb { * @returns Empty promise. */ public async commit(): Promise { - return await this.synchronize(() => this._commit()); + return await this.synchronize(() => this.#commit()); } /** @@ -322,7 +273,7 @@ export class MerkleTrees implements MerkleTreeDb { * @returns Empty promise. */ public async rollback(): Promise { - return await this.synchronize(() => this._rollback()); + return await this.synchronize(() => this.#rollback()); } /** @@ -350,7 +301,7 @@ export class MerkleTrees implements MerkleTreeDb { | undefined > { return await this.synchronize(() => - Promise.resolve(this._getIndexedTree(treeId).findIndexOfPreviousKey(value, includeUncommitted)), + Promise.resolve(this.#getIndexedTree(treeId).findIndexOfPreviousKey(value, includeUncommitted)), ); } @@ -367,7 +318,7 @@ export class MerkleTrees implements MerkleTreeDb { includeUncommitted: boolean, ): Promise { return await this.synchronize(() => - Promise.resolve(this._getIndexedTree(treeId).getLatestLeafPreimageCopy(index, includeUncommitted)), + Promise.resolve(this.#getIndexedTree(treeId).getLatestLeafPreimageCopy(index, includeUncommitted)), ); } @@ -397,7 +348,7 @@ export class MerkleTrees implements MerkleTreeDb { * @returns Empty promise. */ public async updateLeaf(treeId: IndexedTreeId, leaf: Buffer, index: bigint): Promise { - return await this.synchronize(() => this._updateLeaf(treeId, leaf, index)); + return await this.synchronize(() => this.#updateLeaf(treeId, leaf, index)); } /** @@ -406,7 +357,7 @@ export class MerkleTrees implements MerkleTreeDb { * @returns Whether the block handled was produced by this same node. */ public async handleL2Block(block: L2Block): Promise { - return await this.synchronize(() => this._handleL2Block(block)); + return await this.synchronize(() => this.#handleL2Block(block)); } /** @@ -441,18 +392,17 @@ export class MerkleTrees implements MerkleTreeDb { return await this.jobQueue.put(fn); } - private _updateLatestGlobalVariablesHash(globalVariablesHash: Fr): Promise { - this.latestGlobalVariablesHash.set(globalVariablesHash); - return Promise.resolve(); - } + async #updateArchive(header: Header, includeUncommitted: boolean) { + const state = await this.getStateReference(includeUncommitted); - private _getGlobalVariablesHash(includeUncommitted: boolean): Promise { - return Promise.resolve(this.latestGlobalVariablesHash.get(includeUncommitted)); - } + // This method should be called only when the block builder already updated the state so we sanity check that it's + // the case here. + if (!state.toBuffer().equals(header.state.toBuffer())) { + throw new Error('State in header does not match current state'); + } - private async _updateArchive(globalsHash: Fr, includeUncommitted: boolean) { - const blockHash = await this._getCurrentBlockHash(globalsHash, includeUncommitted); - await this._appendLeaves(MerkleTreeId.ARCHIVE, [blockHash.toBuffer()]); + const blockHash = header.hash(); + await this.#appendLeaves(MerkleTreeId.ARCHIVE, [blockHash.toBuffer()]); } /** @@ -461,7 +411,7 @@ export class MerkleTrees implements MerkleTreeDb { * @param includeUncommitted - Indicates whether to include uncommitted data. * @returns The tree info for the specified tree. */ - private _getTreeInfo(treeId: MerkleTreeId, includeUncommitted: boolean): Promise { + #getTreeInfo(treeId: MerkleTreeId, includeUncommitted: boolean): Promise { const treeInfo = { treeId, root: this.trees[treeId].getRoot(includeUncommitted), @@ -476,32 +426,17 @@ export class MerkleTrees implements MerkleTreeDb { * @param treeId - Id of the tree to get an instance of. * @returns The indexed tree for the specified tree id. */ - private _getIndexedTree(treeId: IndexedTreeId): IndexedTree { + #getIndexedTree(treeId: IndexedTreeId): IndexedTree { return this.trees[treeId] as IndexedTree; } - /** - * Returns the sibling path for a leaf in a tree. - * @param treeId - Id of the tree to get the sibling path from. - * @param index - Index of the leaf to get the sibling path for. - * @param includeUncommitted - Indicates whether to include uncommitted updates in the sibling path. - * @returns Promise containing the sibling path for the leaf. - */ - private _getSiblingPath( - treeId: MerkleTreeId, - index: bigint, - includeUncommitted: boolean, - ): Promise> { - return Promise.resolve(this.trees[treeId].getSiblingPath(index, includeUncommitted)); - } - /** * Appends leaves to a tree. * @param treeId - Id of the tree to append leaves to. * @param leaves - Leaves to append. * @returns Empty promise. */ - private async _appendLeaves(treeId: MerkleTreeId, leaves: Buffer[]): Promise { + async #appendLeaves(treeId: MerkleTreeId, leaves: Buffer[]): Promise { const tree = this.trees[treeId]; if (!('appendLeaves' in tree)) { throw new Error('Tree does not support `appendLeaves` method'); @@ -509,7 +444,7 @@ export class MerkleTrees implements MerkleTreeDb { return await tree.appendLeaves(leaves); } - private async _updateLeaf(treeId: IndexedTreeId, leaf: Buffer, index: bigint): Promise { + async #updateLeaf(treeId: IndexedTreeId, leaf: Buffer, index: bigint): Promise { const tree = this.trees[treeId]; if (!('updateLeaf' in tree)) { throw new Error('Tree does not support `updateLeaf` method'); @@ -521,30 +456,27 @@ export class MerkleTrees implements MerkleTreeDb { * Commits all pending updates. * @returns Empty promise. */ - private async _commit(): Promise { + async #commit(): Promise { for (const tree of this.trees) { await tree.commit(); } - this.latestGlobalVariablesHash.commit(); - await this.#globalVariablesHash.set(this.latestGlobalVariablesHash.get().toBuffer()); } /** * Rolls back all pending updates. * @returns Empty promise. */ - private async _rollback(): Promise { + async #rollback(): Promise { for (const tree of this.trees) { await tree.rollback(); } - this.latestGlobalVariablesHash.rollback(); } public getSnapshot(blockNumber: number) { return Promise.all(this.trees.map(tree => tree.getSnapshot(blockNumber))); } - private async _snapshot(blockNumber: number): Promise { + async #snapshot(blockNumber: number): Promise { for (const tree of this.trees) { await tree.snapshot(blockNumber); } @@ -554,7 +486,7 @@ export class MerkleTrees implements MerkleTreeDb { * Handles a single L2 block (i.e. Inserts the new commitments into the merkle tree). * @param l2Block - The L2 block to handle. */ - private async _handleL2Block(l2Block: L2Block): Promise { + async #handleL2Block(l2Block: L2Block): Promise { const treeRootWithIdPairs = [ [l2Block.header.state.partial.contractTree.root, MerkleTreeId.CONTRACT_TREE], [l2Block.header.state.partial.nullifierTree.root, MerkleTreeId.NULLIFIER_TREE], @@ -570,10 +502,10 @@ export class MerkleTrees implements MerkleTreeDb { const ourBlock = treeRootWithIdPairs.every(([root, id]) => compareRoot(root, id)); if (ourBlock) { this.log(`Block ${l2Block.number} is ours, committing world state`); - await this._commit(); + await this.#commit(); } else { this.log(`Block ${l2Block.number} is not ours, rolling back world state and committing state from chain`); - await this._rollback(); + await this.#rollback(); // Sync the append only trees for (const [tree, leaves] of [ @@ -581,7 +513,7 @@ export class MerkleTrees implements MerkleTreeDb { [MerkleTreeId.NOTE_HASH_TREE, l2Block.newCommitments], [MerkleTreeId.L1_TO_L2_MESSAGE_TREE, l2Block.newL1ToL2Messages], ] as const) { - await this._appendLeaves( + await this.#appendLeaves( tree, leaves.map(fr => fr.toBuffer()), ); @@ -605,20 +537,15 @@ export class MerkleTrees implements MerkleTreeDb { ); } - // Sync and add the block to the blocks tree - const globalVariablesHash = computeGlobalsHash(l2Block.header.globalVariables); - await this._updateLatestGlobalVariablesHash(globalVariablesHash); - this.log(`Synced global variables with hash ${globalVariablesHash}`); - - const blockHash = await this._getCurrentBlockHash(globalVariablesHash, true); - await this._appendLeaves(MerkleTreeId.ARCHIVE, [blockHash.toBuffer()]); + // The last thing remaining is to update the archive + await this.#updateArchive(l2Block.header, true); - await this._commit(); + await this.#commit(); } for (const [root, treeId] of treeRootWithIdPairs) { const treeName = MerkleTreeId[treeId]; - const info = await this._getTreeInfo(treeId, false); + const info = await this.#getTreeInfo(treeId, false); const syncedStr = '0x' + info.root.toString('hex'); const rootStr = root.toString(); // Sanity check that the rebuilt trees match the roots published by the L2 block @@ -630,8 +557,19 @@ export class MerkleTrees implements MerkleTreeDb { this.log(`Tree ${treeName} synched with size ${info.size} root ${rootStr}`); } } - await this._snapshot(l2Block.number); + await this.#snapshot(l2Block.number); return { isBlockOurs: ourBlock }; } + + #isDbPopulated(): boolean { + try { + getTreeMeta(this.store, MerkleTreeId[MerkleTreeId.NULLIFIER_TREE]); + // Tree meta was found --> db is populated + return true; + } catch (e) { + // Tree meta was not found --> db is not populated + return false; + } + } } diff --git a/yarn-project/yarn-project-base/Dockerfile b/yarn-project/yarn-project-base/Dockerfile index b373d429178d..fb2b49237e65 100644 --- a/yarn-project/yarn-project-base/Dockerfile +++ b/yarn-project/yarn-project-base/Dockerfile @@ -47,6 +47,7 @@ FROM 278380418400.dkr.ecr.eu-west-2.amazonaws.com/bb.js as bb.js FROM 278380418400.dkr.ecr.eu-west-2.amazonaws.com/noir as noir FROM 278380418400.dkr.ecr.eu-west-2.amazonaws.com/noir-packages as noir-packages FROM 278380418400.dkr.ecr.eu-west-2.amazonaws.com/boxes-files as boxes-files +FROM 278380418400.dkr.ecr.eu-west-2.amazonaws.com/avm-transpiler as transpiler FROM node:18.19.0 RUN apt update && apt install -y jq curl perl && rm -rf /var/lib/apt/lists/* && apt-get clean @@ -61,6 +62,8 @@ COPY --from=noir /usr/src/noir/target/release/nargo /usr/src/noir/target/release COPY --from=noir-packages /usr/src/noir/packages /usr/src/noir/packages # Copy in boxes COPY --from=boxes-files /usr/src/boxes /usr/src/boxes +# Copy in transpiler +COPY --from=transpiler /usr/src/avm-transpiler/target/release/avm-transpiler /usr/src/avm-transpiler/target/release/avm-transpiler # We install a symlink to yarn-project's node_modules at a location that all portalled packages can find as they # walk up the tree as part of module resolution. The supposedly idiomatic way of supporting module resolution diff --git a/yarn-project/yarn.lock b/yarn-project/yarn.lock index 06235a30e200..472b7df6b997 100644 --- a/yarn-project/yarn.lock +++ b/yarn-project/yarn.lock @@ -2761,7 +2761,7 @@ __metadata: version: 0.0.0-use.local resolution: "@noir-lang/backend_barretenberg@portal:../noir/packages/backend_barretenberg::locator=%40aztec%2Faztec3-packages%40workspace%3A." dependencies: - "@aztec/bb.js": 0.19.0 + "@aztec/bb.js": 0.21.0 "@noir-lang/types": 0.23.0 fflate: ^0.8.0 languageName: node diff --git a/yellow-paper/docs/public-vm/avm-circuit.md b/yellow-paper/docs/public-vm/avm-circuit.md index c5e91882b673..65234632f13c 100644 --- a/yellow-paper/docs/public-vm/avm-circuit.md +++ b/yellow-paper/docs/public-vm/avm-circuit.md @@ -108,7 +108,32 @@ The VM circuit's **Memory Controller** processes loads and stores between interm When decoded, instructions that operate on memory map to some Memory Controller sub-operations. A memory read maps to a `LOAD` sub-operation which loads a word from memory into an intermediate register. The memory offset for this sub-operation is generally specified by an instruction argument. Similarly, a memory write maps to a `STORE` sub-operation which stores a word from an intermediate register to memory. ### User Memory -**TODO** + +This table tracks all memory `Read` or `Write` operations. As introduced in the ["Memory State Model"](./state-model.md), a memory cell is indexed by a 32-bit unsigned integer (`u32`), i.e., the memory capacity is of $2^{32}$ words. Each word is associated with a tag defining its type (`uninitialized`, `u8`, `u16`, `u32`, `u64`, `u128`, `field`). At the beginning of a new call, each memory cell is of type `uninitialized` and has value 0. + +The main property enforcement of this table concerns read/write consistency of every memory cell. This must ensure: + +- Each initial read on a memory cell must have value 0 (`uninitialized` is compatible with any other type). +- Each read on a memory cell must have the same value and the same tag as those set by the last write on this memory cell. + +In addition, this table ensures that the instruction tag corresponding to a memory operation is the same as the memory cell tag. The instruction tag is passed to the memory controller and added to the pertaining row(s) of this table. Note that this is common for an instruction to generate several memory operations and thus several rows in this table. + +The user memory table essentially consists of the following colums: + +- `CALL_PTR`: call pointer uniquely identifying the contract call +- `CLK`: clock value of the memory operation +- `ADDR`: address (type `u32`) pertaining to the memory operation +- `VAL`: value which is read (resp. written) from (resp. to) the memory address +- `TAG`: tag associated to this memory address +- `IN_TAG`: tag of the pertaining instruction +- `RW`: boolean indicating whether memory operation is read or write +- `TAG_ERR`: boolean set to true if there is a mismatch between `TAG` and `IN_TAG` + +To facilitate consistency check, the rows are sorted by `CALL_PTR` then by `ADDR` and then by `CLK` in ascending (arrow of time) order. Any (non-initial) read operation row is constrained to have the same `VAL` and `TAG` than the previous row. A write operation does not need to be constrained. + +The tag consistency check can be performed within every row (order of rows does not matter). + +Note that `CLK` also plays the role of a foreign key to point to the corresponding sub-operation. This is crucial to enforce consistency of copied values between the sub-operations and memory table. ### Calldata **TODO**